/** * Create converted ffmpeg payload * * @param object DigitalObject to store the payload * @param file File to be stored as payload * @return Payload new payload * @throws StorageException if there is a problem trying to store * @throws FileNotFoundException if the file provided does not exist */ public Payload createFfmpegPayload(DigitalObject object, File file) throws StorageException, FileNotFoundException { String name = file.getName(); Payload payload = StorageUtils.createOrUpdatePayload(object, name, new FileInputStream(file)); payload.setContentType(MimeTypeUtil.getMimeType(name)); payload.setLabel(name); return payload; }
/** * Create ffmpeg error payload * * @param object : DigitalObject to store the payload * @return Payload the error payload * @throws FileNotFoundException if the file provided does not exist * @throws UnsupportedEncodingException for encoding errors in the message */ public Payload createFfmpegErrorPayload(DigitalObject object) throws StorageException, FileNotFoundException, UnsupportedEncodingException { // Compile our error data JsonConfigHelper content = new JsonConfigHelper(); content.setJsonMap("/", errors); log.debug("\nErrors:\n{}", content.toString()); InputStream data = new ByteArrayInputStream(content.toString().getBytes("UTF-8")); // Write to the object Payload payload = StorageUtils.createOrUpdatePayload(object, ERROR_PAYLOAD, data); payload.setType(PayloadType.Error); payload.setContentType("application/json"); payload.setLabel("FFMPEG conversion errors"); return payload; }
/** * Check the object for a multi-segment source and merge them. Such sources must come from a * harvester specifically designed to match this transformer. As such we can make certain * assumptions, and if they are not met we just fail silently with a log entry. * * @param object: The digital object to modify */ private void mergeSegments(DigitalObject object) { try { // Retrieve (optional) segment information from metadata Properties props = object.getMetadata(); String segs = props.getProperty("mediaSegments"); if (segs == null) { return; } int segments = Integer.parseInt(segs); if (segments <= 1) { return; } // We need to do some merging, lets validate IDs first log.info("Found {} source segments! Merging...", segments); List<String> segmentIds = new ArrayList(); Set<String> payloadIds = object.getPayloadIdList(); // The first segment String sourceId = object.getSourceId(); if (sourceId == null || !payloadIds.contains(sourceId)) { log.error("Cannot find source payload."); return; } segmentIds.add(sourceId); // Find the other segments for (int i = 1; i < segments; i++) { // We won't know the extension though String segmentId = "segment" + i + "."; for (String pid : payloadIds) { if (pid.startsWith(segmentId)) { segmentIds.add(pid); } } } // Did we find every segment? if (segmentIds.size() != segments) { log.error("Unable to find all segments in payload list."); return; } // Transcode all the files to neutral MPEGs first Map<String, File> files = new HashMap(); for (String segment : segmentIds) { try { File file = basicMpeg(object, segment); if (file != null) { files.put(segment, file); } } catch (Exception ex) { log.error("Error transcoding segment to MPEG: ", ex); // Cleanup for (File f : files.values()) { if (f.exists()) { f.delete(); } } return; } } // Did every transcoding succeed? if (files.size() != segments) { log.error("At least one segment transcoding failed."); // Cleanup for (File f : files.values()) { if (f.exists()) { f.delete(); } } return; } // Now to try merging all the segments. In MPEG format // they can just be concatenated. try { // Create our output file String filename = "temp_" + MERGED_PAYLOAD + "mpg"; File merged = new File(outputDir, filename); if (merged.exists()) { merged.delete(); } FileOutputStream out = new FileOutputStream(merged); // Merge each segment in order for (String sId : segmentIds) { try { mergeSegment(out, files.get(sId)); } catch (IOException ex) { log.error("Failed to stream to merged file: ", ex); out.close(); // Cleanup for (File f : files.values()) { if (f.exists()) { f.delete(); } } merged.delete(); return; } } out.close(); // Final step, run the output file through a transcoding to // write the correct metadata (eg. duration) filename = MERGED_PAYLOAD + finalFormat; File transcoded = new File(outputDir, filename); if (transcoded.exists()) { transcoded.delete(); } // Render String stderr = mergeRender(merged, transcoded); log.debug("=====\n{}", stderr); if (transcoded.exists()) { // Now we need to 'fix' the object, add the new source FileInputStream fis = new FileInputStream(transcoded); String pid = transcoded.getName(); Payload p = StorageUtils.createOrUpdatePayload(object, pid, fis); fis.close(); p.setType(PayloadType.Source); object.setSourceId(pid); // Remove all the old segments for (String sId : segmentIds) { object.removePayload(sId); } props.remove("mediaSegments"); object.close(); // Cleanup segments for (File f : files.values()) { if (f.exists()) { f.delete(); } } merged.delete(); transcoded.delete(); } } catch (IOException ex) { log.error("Error merging segments: ", ex); } } catch (StorageException ex) { log.error("Error accessing object metadata: ", ex); } }