/**
  * 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;
 }
 @Before
 public void setup() throws Exception {
   ram = PluginManager.getStorage("ram");
   ram.init("{}");
   ffmpeg = new FfmpegMockImpl();
   sourceObject =
       StorageUtils.storeFile(ram, new File(getClass().getResource("/african_drum.aif").toURI()));
 }
  private String createDigitalObject(File file) throws HarvesterException, StorageException {
    String objectId;
    DigitalObject object;
    if (forceUpdate) {
      object = StorageUtils.storeFile(getStorage(), file, !forceLocalStorage);
    } else {
      String oid = StorageUtils.generateOid(file);
      String pid = StorageUtils.generatePid(file);
      object = getStorage().createObject(oid);
      if (forceLocalStorage) {
        try {
          object.createStoredPayload(pid, new FileInputStream(file));
        } catch (FileNotFoundException ex) {
          throw new HarvesterException(ex);
        }
      } else {
        object.createLinkedPayload(pid, file.getAbsolutePath());
      }
    }
    // update object metadata
    Properties props = object.getMetadata();
    props.setProperty("render-pending", "true");
    props.setProperty("file.path", FilenameUtils.separatorsToUnix(file.getAbsolutePath()));
    objectId = object.getId();

    // Store rendition information if we have it
    String ext = FilenameUtils.getExtension(file.getName());
    for (String chain : renderChains.keySet()) {
      Map<String, List<String>> details = renderChains.get(chain);
      if (details.get("fileTypes").contains(ext)) {
        storeList(props, details, "harvestQueue");
        storeList(props, details, "indexOnHarvest");
        storeList(props, details, "renderQueue");
      }
    }

    object.close();
    return objectId;
  }
 /**
  * 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);
    }
  }