/** Shut down the transformer plugin */
 @Override
 public void shutdown() throws PluginException {
   // Shutdown the database
   if (stats != null) {
     try {
       stats.shutdown();
     } catch (Exception ex) {
       log.error("Error shutting down database: ", ex);
       throw new PluginException(ex);
     }
   }
 }
  /**
   * Convert audio/video to required output(s)
   *
   * @param sourceFile : The file to be converted
   * @param render : Configuration to use during the render
   * @param info : Parsed metadata about the source
   * @return File containing converted media
   * @throws TransformerException if the conversion failed
   */
  private File convert(File sourceFile, JsonConfigHelper render, FfmpegInfo info)
      throws TransformerException {

    // Statistics variables
    long startTime, timeSpent;
    String resolution;
    // One list for all settings, the other is a subset for statistics
    List<String> statParams = new ArrayList<String>();
    List<String> params = new ArrayList<String>();

    // Prepare the output location
    String outputName = render.get("name");
    if (outputName == null) {
      return null;
    }
    File outputFile = new File(outputDir, outputName);
    if (outputFile.exists()) {
      FileUtils.deleteQuietly(outputFile);
    }
    log.info("Converting '{}': '{}'", sourceFile.getName(), outputFile.getName());

    // Get metadata ready
    JsonConfigHelper renderMetadata = new JsonConfigHelper();
    String key = jsonKey(outputName);
    String formatString = render.get("formatMetadata");
    if (formatString != null) {
      renderMetadata.set("format", formatString);
    }
    String codecString = render.get("codecMetadata");
    if (codecString != null) {
      renderMetadata.set("codec", codecString);
    }

    try {
      // *************
      // 1) Input file
      // *************
      params.add("-i");
      params.add(sourceFile.getAbsolutePath());
      // Overwrite output file if it exists
      params.add("-y");

      // *************
      // 2) Configurable options
      // *************
      String optionStr = render.get("options", "");
      List<String> options = split(optionStr, " ");
      // Replace the offset placeholder now that we know the duration
      long start = 0;
      for (int i = 0; i < options.size(); i++) {
        String option = options.get(i);
        // For stats, use placeholder.. random data messes with hashing
        statParams.add(option);
        // If it even exists that is...
        if (option.equalsIgnoreCase("[[OFFSET]]")) {
          start = (long) (Math.random() * info.getDuration() * 0.25);
          option = Long.toString(start);
        }
        // Store the parameter for usage
        params.add(option);
      }

      // *************
      // 3) Video resolution / padding
      // *************
      String audioStr = render.get("audioOnly");
      boolean audio = Boolean.parseBoolean(audioStr);

      // Non-audio files need some resolution work
      if (!audio) {
        List<String> dimensions = getPaddedParams(render, info, renderMetadata, statParams);
        if (dimensions == null || dimensions.isEmpty()) {
          addError(key, "Error calculating dimensions");
          return null;
        }
        // Merge resultion parameters into standard parameters
        params.addAll(dimensions);
      }
      // Statistics
      String width = renderMetadata.get("width");
      String height = renderMetadata.get("height");
      if (width == null || height == null) {
        // Audio... or an error
        resolution = "0x0";
      } else {
        resolution = width + "x" + height;
      }

      // *************
      // 4) Output options
      // *************
      optionStr = render.get("output", "");
      options = split(optionStr, " ");
      // Merge option parameters into standard parameters
      if (!options.isEmpty()) {
        params.addAll(options);
        statParams.addAll(options);
      }
      params.add(outputFile.getAbsolutePath());

      // *************
      // 5) All done. Perform the transcoding
      // *************
      startTime = new Date().getTime();
      String stderr = ffmpeg.transform(params, outputDir);
      timeSpent = (new Date().getTime()) - startTime;

      renderMetadata.set("timeSpent", String.valueOf(timeSpent));
      renderMetadata.set("debugOutput", stderr);
      if (outputFile.exists()) {
        long fileSize = outputFile.length();
        if (fileSize == 0) {
          throw new TransformerException("File conversion failed!\n=====\n" + stderr);
        } else {
          renderMetadata.set("size", String.valueOf(fileSize));
        }
      } else {
        throw new TransformerException("File conversion failed!\n=====\n" + stderr);
      }

      // log.debug("FFMPEG Output:\n=====\n\\/\\/\\/\\/\n{}/\\/\\/\\/\\\n=====\n",
      // stderr);
    } catch (IOException ioe) {
      addError(key, "Failed to convert!", ioe);
      throw new TransformerException(ioe);
    }

    // On a multi-pass encoding we may be asked to
    // throw away the video from some passes.
    if (outputFile.getName().contains("nullFile")) {
      return null;
    } else {
      // For anything else, record metadata
      metadata.put(key, renderMetadata);
      // And statistics
      if (stats != null) {
        Map<String, String> data = new HashMap();
        data.put("oid", oid);
        data.put("datetime", String.valueOf(startTime));
        data.put("timespent", String.valueOf(timeSpent));
        data.put("renderString", StringUtils.join(statParams, " "));
        data.put("mediaduration", String.valueOf(info.getDuration()));
        data.put("inresolution", info.getWidth() + "x" + info.getHeight());
        data.put("outresolution", resolution);
        data.put("insize", String.valueOf(sourceFile.length()));
        data.put("outsize", String.valueOf(outputFile.length()));
        data.put("infile", sourceFile.getName());
        data.put("outfile", outputFile.getName());
        try {
          stats.storeTranscoding(data);
        } catch (Exception ex) {
          log.error("Error storing statistics: ", ex);
        }
      }
    }
    return outputFile;
  }