public MediaDesc convertToMPEG(MediaDesc mediaIn, String outPath, ShellCallback sc)
      throws Exception {
    ArrayList<String> cmd = new ArrayList<String>();

    cmd.add(ffmpegBin);
    cmd.add("-y");
    cmd.add("-i");
    cmd.add(mediaIn.path);

    if (mediaIn.startTime != null) {
      cmd.add("-ss");
      cmd.add(mediaIn.startTime);
    }

    if (mediaIn.duration != null) {
      cmd.add("-t");
      cmd.add(mediaIn.duration);
    }

    // cmd.add("-strict");
    // cmd.add("experimental");

    // everything to mpeg
    cmd.add("-f");
    cmd.add("mpeg");

    MediaDesc mediaOut = mediaIn.clone();
    mediaOut.path = outPath + ".mpg";

    cmd.add(mediaOut.path);

    execFFMPEG(cmd, sc);

    return mediaOut;
  }
  // based on this gist: https://gist.github.com/3757344
  // ffmpeg -i input1.mp4 -vcodec copy -vbsf h264_mp4toannexb -acodec copy part1.ts
  public MediaDesc convertToMP4Stream(MediaDesc mediaIn, String outPath, ShellCallback sc)
      throws Exception {
    ArrayList<String> cmd = new ArrayList<String>();

    MediaDesc mediaOut = mediaIn.clone();

    String mediaPath = mediaIn.path;

    cmd = new ArrayList<String>();

    cmd.add(ffmpegBin);
    cmd.add("-y");
    cmd.add("-i");
    cmd.add(mediaPath);

    if (mediaIn.startTime != null) {
      cmd.add(FFMPEGArg.ARG_STARTTIME);
      cmd.add(mediaIn.startTime);
    }

    if (mediaIn.duration != null) {
      cmd.add(FFMPEGArg.ARG_DURATION);
      cmd.add(mediaIn.duration);
    }

    if (mediaIn.videoFilter == null) {
      cmd.add(FFMPEGArg.ARG_VIDEOCODEC);
      cmd.add("copy");
    } else {
      cmd.add(FFMPEGArg.ARG_VIDEOCODEC);
      cmd.add("libx264");

      cmd.add("-vf");
      cmd.add(mediaIn.videoFilter);

      if (mediaIn.videoBitrate != -1) {
        cmd.add(FFMPEGArg.ARG_BITRATE_VIDEO);
        cmd.add(mediaIn.videoBitrate + "k");
      }
    }

    cmd.add(FFMPEGArg.ARG_VIDEOBITSTREAMFILTER);
    cmd.add("h264_mp4toannexb");

    cmd.add(FFMPEGArg.ARG_AUDIOCODEC);
    cmd.add("copy");

    mediaOut.path = outPath + ".ts";

    cmd.add(mediaOut.path);

    execFFMPEG(cmd, sc);

    return mediaOut;
  }
  public MediaDesc convertImageToMP4(
      MediaDesc mediaIn, int duration, String outPath, ShellCallback sc) throws Exception {
    MediaDesc result = new MediaDesc();
    ArrayList<String> cmd = new ArrayList<String>();

    // ffmpeg -loop 1 -i IMG_1338.jpg -t 10 -r 29.97 -s 640x480 -qscale 5 test.mp4

    cmd = new ArrayList<String>();

    // convert images to MP4
    cmd.add(ffmpegBin);
    cmd.add("-y");

    cmd.add("-loop");
    cmd.add("1");

    cmd.add("-i");
    cmd.add(mediaIn.path);

    cmd.add(FFMPEGArg.ARG_FRAMERATE);
    cmd.add(mediaIn.videoFps);

    cmd.add("-t");
    cmd.add(duration + "");

    cmd.add("-qscale");
    cmd.add("5"); // a good value 1 is best 30 is worst

    if (mediaIn.width != -1) {
      cmd.add(FFMPEGArg.ARG_SIZE);
      cmd.add(mediaIn.width + "x" + mediaIn.height);
      //	cmd.add("-vf");
      //	cmd.add("\"scale=-1:" + mediaIn.width + "\"");
    }

    if (mediaIn.videoBitrate != -1) {
      cmd.add(FFMPEGArg.ARG_BITRATE_VIDEO);
      cmd.add(mediaIn.videoBitrate + "");
    }

    //	-ar 44100 -acodec pcm_s16le -f s16le -ac 2 -i /dev/zero -acodec aac -ab 128k \
    //	-map 0:0 -map 1:0

    result.path = outPath;
    result.videoBitrate = mediaIn.videoBitrate;
    result.videoFps = mediaIn.videoFps;
    result.mimeType = "video/mp4";

    cmd.add(result.path);

    execFFMPEG(cmd, sc);

    return result;
  }
  public void concatAndTrimFilesMP4Stream(
      ArrayList<MediaDesc> videos, MediaDesc out, boolean mediaNeedsConversion, ShellCallback sc)
      throws Exception {

    StringBuffer cmdRun = new StringBuffer();

    cmdRun.append("cat ");

    for (MediaDesc vdesc : videos) {
      if (vdesc.path == null) continue;

      if (!vdesc.path.endsWith(".ts")) {
        /*
        MediaDesc mdOutMp4 = new MediaDesc ();
        mdOutMp4.path = vdesc.path + ".mp4";
        processVideo(vdesc, mdOutMp4, true, sc);
        */
        MediaDesc mdOut = convertToMP4Stream(vdesc, vdesc.path, sc);
        cmdRun.append(mdOut.path).append(' ');
      } else cmdRun.append(vdesc.path).append(' ');
    }

    String mCatPath = out.path + ".full.ts";

    cmdRun.append("> ");
    cmdRun.append(mCatPath);

    Log.d(TAG, "cat cmd: " + cmdRun.toString());

    String[] cmds = {"sh", "-c", cmdRun.toString()};
    Runtime.getRuntime().exec(cmds).waitFor();

    MediaDesc mInCat = new MediaDesc();
    mInCat.path = mCatPath;

    if (out.videoFilter == null) {
      out.videoCodec = "copy";
    }

    out.audioCodec = "copy";
    out.audioBitStreamFilter = "aac_adtstoasc";

    // ffmpeg -y -i parts.ts -acodec copy -absf aac_adtstoasc parts.mp4

    processVideo(mInCat, out, false, sc);

    // out.path = mCatPath;
  }
  /*
  	 * ffmpeg -y -loop 0 -f image2 -r 0.5 -i image-%03d.jpg -s:v 1280x720 -b:v 1M \
     -i soundtrack.mp3 -t 01:05:00 -map 0:0 -map 1:0 out.avi

     -loop_input – loops the images. Disable this if you want to stop the encoding when all images are used or the soundtrack is finished.

  -r 0.5 – sets the framerate to 0.5, which means that each image will be shown for 2 seconds. Just take the inverse, for example if you want each image to last for 3 seconds, set it to 0.33.

  -i image-%03d.jpg – use these input files. %03d means that there will be three digit numbers for the images.

  -s 1280x720 – sets the output frame size.

  -b 1M – sets the bitrate. You want 500MB for one hour, which equals to 4000MBit in 3600 seconds, thus a bitrate of approximately 1MBit/s should be sufficient.

  -i soundtrack.mp3 – use this soundtrack file. Can be any format.

  -t 01:05:00 – set the output length in hh:mm:ss format.

  out.avi – create this output file. Change it as you like, for example using another container like MP4.
  	 */
  public MediaDesc combineAudioAndVideo(
      MediaDesc videoIn, MediaDesc audioIn, String outPath, ShellCallback sc) throws Exception {
    MediaDesc result = new MediaDesc();
    ArrayList<String> cmd = new ArrayList<String>();

    cmd.add(ffmpegBin);
    cmd.add("-y");

    cmd.add("-i");
    cmd.add(audioIn.path);

    cmd.add("-i");
    cmd.add(videoIn.path);

    cmd.add(FFMPEGArg.ARG_AUDIOCODEC);
    if (audioIn.audioCodec != null) cmd.add(audioIn.audioCodec);
    else cmd.add("copy");

    cmd.add(FFMPEGArg.ARG_VIDEOCODEC);
    if (videoIn.videoCodec != null) cmd.add(videoIn.videoCodec);
    else cmd.add("copy");

    // cmd.add(FFMPEGArg.ARG_VIDEOBITSTREAMFILTER);
    // cmd.add("h264_mp4toannexb");

    if (videoIn.videoBitrate != -1) {
      cmd.add(FFMPEGArg.ARG_BITRATE_VIDEO);
      cmd.add(videoIn.videoBitrate + "k");
    }

    if (audioIn.audioBitrate != -1) {
      cmd.add(FFMPEGArg.ARG_BITRATE_AUDIO);
      cmd.add(audioIn.audioBitrate + "k");
    }

    cmd.add("-strict");
    cmd.add("-2"); // experimental

    result.path = outPath;
    cmd.add(result.path);

    execFFMPEG(cmd, sc);

    // ffmpeg -i audio.wav -i video.mp4 -acodec copy -vcodec copy output.mp4

    return result;
  }
  public void concatAndTrimFilesMPEG(
      ArrayList<MediaDesc> videos, MediaDesc out, boolean preConvert, ShellCallback sc)
      throws Exception {

    int idx = 0;

    if (preConvert) {
      for (MediaDesc mdesc : videos) {
        if (mdesc.path == null) continue;

        // extract MPG video
        ArrayList<String> cmd = new ArrayList<String>();

        cmd.add(ffmpegBin);
        cmd.add("-y");
        cmd.add("-i");
        cmd.add(mdesc.path);

        if (mdesc.startTime != null) {
          cmd.add("-ss");
          cmd.add(mdesc.startTime);
        }

        if (mdesc.duration != null) {
          cmd.add("-t");
          cmd.add(mdesc.duration);
        }

        /*
        cmd.add ("-acodec");
        cmd.add("pcm_s16le");

        cmd.add ("-vcodec");
        cmd.add("mpeg2video");
        */
        // cmd.add("-an"); //no audio

        // cmd.add("-strict");
        // cmd.add("experimental");

        // everything to mpeg
        cmd.add("-f");
        cmd.add("mpeg");
        cmd.add(out.path + '.' + idx + ".mpg");

        execFFMPEG(cmd, sc);

        idx++;
      }
    }

    StringBuffer cmdRun = new StringBuffer();

    cmdRun.append("cat ");

    idx = 0;

    for (MediaDesc vdesc : videos) {
      if (vdesc.path == null) continue;

      if (preConvert)
        cmdRun
            .append(out.path)
            .append('.')
            .append(idx++)
            .append(".mpg")
            .append(' '); // leave a space at the end!
      else cmdRun.append(vdesc.path).append(' ');
    }

    String mCatPath = out.path + ".full.mpg";

    cmdRun.append("> ");
    cmdRun.append(mCatPath);

    Log.d(TAG, "cat cmd: " + cmdRun.toString());

    String[] cmds = {"sh", "-c", cmdRun.toString()};
    Runtime.getRuntime().exec(cmds).waitFor();

    MediaDesc mInCat = new MediaDesc();
    mInCat.path = mCatPath;

    //	mInCat.format = "mpeg";
    //	mInCat.audioCodec = "mp2";
    //	mInCat.videoCodec = "mpeg1video";

    processVideo(mInCat, out, false, sc);

    out.path = mCatPath;
  }
  public MediaDesc createSlideshowFromImagesAndAudio(
      ArrayList<MediaDesc> images,
      MediaDesc audio,
      int width,
      int height,
      int durationPerSlide,
      String bitrate,
      String outPath,
      ShellCallback sc)
      throws Exception {

    final String imageBasePath =
        new File(new File(outPath).getParent(), "image-").getAbsolutePath();
    final String imageBaseVariablePath = imageBasePath + "%03d.jpg";

    MediaDesc result = new MediaDesc();

    result.path = outPath;

    ArrayList<String> cmd = new ArrayList<String>();

    // first we need to same size all the input images

    int imageCounter = 1;

    for (MediaDesc image : images) {
      cmd = new ArrayList<String>();
      cmd.add(ffmpegBin);
      cmd.add("-y");

      cmd.add("-i");
      cmd.add(image.path);

      cmd.add("-s");
      cmd.add(width + "x" + height);

      String newImagePath = imageBasePath + String.format("%03d", imageCounter) + ".jpg";
      cmd.add(newImagePath);

      execFFMPEG(cmd, sc);

      imageCounter++;
    }

    // then combine them with the audio track
    cmd = new ArrayList<String>();
    String audioPath = audio.path;

    cmd.add(ffmpegBin);
    cmd.add("-y");

    cmd.add("-loop");
    cmd.add("0");

    cmd.add("-f");
    cmd.add("image2");

    cmd.add("-r");
    cmd.add("1/" + durationPerSlide);

    cmd.add("-i");
    cmd.add(imageBaseVariablePath);

    cmd.add("-b:v");
    cmd.add(bitrate);

    if (audioPath != null) {
      cmd.add("-i");
      cmd.add(audioPath);

      cmd.add("-map");
      cmd.add("0:0");

      cmd.add("-map");
      cmd.add("1:0");
    }

    cmd.add("-strict");
    cmd.add("-2"); // experimental

    cmd.add("-vcodec");
    cmd.add("libx264");

    cmd.add(result.path);

    execFFMPEG(cmd, sc);

    return result;
  }