package org.ffmpeg.android;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import org.ffmpeg.android.ShellUtils.ShellCallback;
import android.content.Context;
import android.graphics.Bitmap;
import android.media.MediaMetadataRetriever;
import android.util.Log;
public class FfmpegController
{
private String mFfmpegBin;
private final static String TAG = "FFMPEG";
private File mFileTemp;
private String mCmdCat = "sh cat";
public FfmpegController(Context context, File fileTemp)
throws FileNotFoundException, IOException
{
mFileTemp = fileTemp;
installBinaries(context, false);
}
public void installBinaries(Context context, boolean overwrite)
{
mFfmpegBin = installBinary(context, R.raw.ffmpeg, "ffmpeg", overwrite);
}
public String getBinaryPath()
{
return mFfmpegBin;
}
private static String installBinary(Context ctx, int resId,
String filename, boolean upgrade)
{
try
{
File f = new File(ctx.getDir("bin", 0), filename);
if (f.exists())
{
f.delete();
}
copyRawFile(ctx, resId, f, "0755");
return f.getCanonicalPath();
} catch (Exception e)
{
Log.e(TAG, "installBinary failed: " + e.getLocalizedMessage());
return null;
}
}
/**
* Copies a raw resource file, given its ID to the given location
*
* @param ctx
* context
* @param resid
* resource id
* @param file
* destination file
* @param mode
* file permissions (E.g.: "755")
* @throws IOException
* on error
* @throws InterruptedException
* when interrupted
*/
private static void copyRawFile(Context ctx, int resid, File file,
String mode) throws IOException, InterruptedException
{
final String abspath = file.getAbsolutePath();
// Write the iptables binary
final FileOutputStream out = new FileOutputStream(file);
final InputStream is = ctx.getResources().openRawResource(resid);
byte buf[] = new byte[1024];
int len;
while ((len = is.read(buf)) > 0)
{
out.write(buf, 0, len);
}
out.close();
is.close();
// Change the permissions
Runtime.getRuntime().exec("chmod " + mode + " " + abspath).waitFor();
}
private void execFFMPEG(List<String> cmd, ShellCallback sc, File fileExec)
throws IOException, InterruptedException
{
enablePermissions();
execProcess(cmd, sc, fileExec);
}
private void enablePermissions() throws IOException
{
Runtime.getRuntime().exec("chmod 700 " + mFfmpegBin);
}
private void execFFMPEG(List<String> cmd, ShellCallback sc)
throws IOException, InterruptedException
{
execFFMPEG(cmd, sc, new File(mFfmpegBin).getParentFile());
}
private int execProcess(List<String> cmds, ShellCallback sc, File fileExec)
throws IOException, InterruptedException
{
// ensure that the arguments are in the correct Locale format
for (String cmd : cmds)
{
cmd = String.format(Locale.US, "%s", cmd);
}
ProcessBuilder pb = new ProcessBuilder(cmds);
pb.directory(fileExec);
StringBuffer cmdlog = new StringBuffer();
for (String cmd : cmds)
{
cmdlog.append(cmd);
cmdlog.append(' ');
}
sc.shellOut(cmdlog.toString());
// pb.redirectErrorStream(true);
Process process = pb.start();
// any error message?
StreamGobbler errorGobbler = new StreamGobbler( process.getErrorStream(), "ERROR", sc);
// any output?
StreamGobbler outputGobbler = new StreamGobbler(process.getInputStream(), "OUTPUT", sc);
errorGobbler.start();
outputGobbler.start();
int exitVal = process.waitFor();
sc.processComplete(exitVal);
return exitVal;
}
private int execProcess(String cmd, ShellCallback sc, File fileExec)
throws IOException, InterruptedException
{
// ensure that the argument is in the correct Locale format
cmd = String.format(Locale.US, "%s", cmd);
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.directory(fileExec);
// pb.redirectErrorStream(true);
Process process = pb.start();
// any error message?
StreamGobbler errorGobbler = new StreamGobbler(
process.getErrorStream(), "ERROR", sc);
// any output?
StreamGobbler outputGobbler = new StreamGobbler(
process.getInputStream(), "OUTPUT", sc);
// kick them off
errorGobbler.start();
outputGobbler.start();
int exitVal = process.waitFor();
sc.processComplete(exitVal);
return exitVal;
}
public class Argument
{
String key;
String value;
public static final String VIDEOCODEC = "-vcodec";
public static final String AUDIOCODEC = "-acodec";
public static final String VIDEOBITSTREAMFILTER = "-vbsf";
public static final String AUDIOBITSTREAMFILTER = "-absf";
public static final String VERBOSITY = "-v";
public static final String FILE_INPUT = "-i";
public static final String SIZE = "-s";
public static final String FRAMERATE = "-r";
public static final String FORMAT = "-f";
public static final String BITRATE_VIDEO = "-b:v";
public static final String BITRATE_AUDIO = "-b:a";
public static final String CHANNELS_AUDIO = "-ac";
public static final String FREQ_AUDIO = "-ar";
public static final String STARTTIME = "-ss";
public static final String DURATION = "-t";
}
public void getThumbnailForRecorder(ClipVideoRecorder inThumb,
ClipVideoRecorder outThumb, boolean enableExperimental,
ShellCallback sc) throws Exception
{
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
if (inThumb.format != null)
{
cmd.add(Argument.FORMAT);
cmd.add(inThumb.format);
}
if (inThumb.videoCodec != null)
{
cmd.add(Argument.VIDEOCODEC);
cmd.add(inThumb.videoCodec);
}
if (inThumb.audioCodec != null)
{
cmd.add(Argument.AUDIOCODEC);
cmd.add(inThumb.audioCodec);
}
cmd.add("-i");
cmd.add(new File(inThumb.path).getCanonicalPath());
if (outThumb.videoBitrate > 0)
{
cmd.add(Argument.BITRATE_VIDEO);
cmd.add(outThumb.videoBitrate + "k");
}
if (outThumb.width > 0)
{
cmd.add(Argument.SIZE);
cmd.add(outThumb.width + "x" + outThumb.height);
}
if (outThumb.videoFps != null)
{
cmd.add(Argument.FRAMERATE);
cmd.add(outThumb.videoFps);
}
if (outThumb.videoCodec != null)
{
cmd.add(Argument.VIDEOCODEC);
cmd.add(outThumb.videoCodec);
}
if (outThumb.videoBitStreamFilter != null)
{
cmd.add(Argument.VIDEOBITSTREAMFILTER);
cmd.add(outThumb.videoBitStreamFilter);
}
if (outThumb.videoFilter != null)
{
cmd.add("-vf");
cmd.add(outThumb.videoFilter);
}
if (outThumb.audioCodec != null)
{
cmd.add(Argument.AUDIOCODEC);
cmd.add(outThumb.audioCodec);
}
if (outThumb.audioBitStreamFilter != null)
{
cmd.add(Argument.AUDIOBITSTREAMFILTER);
cmd.add(outThumb.audioBitStreamFilter);
}
if (outThumb.audioChannels > 0)
{
cmd.add(Argument.CHANNELS_AUDIO);
cmd.add(outThumb.audioChannels + "");
}
if (outThumb.audioBitrate > 0)
{
cmd.add(Argument.BITRATE_AUDIO);
cmd.add(outThumb.audioBitrate + "k");
}
if (outThumb.format != null)
{
cmd.add("-f");
cmd.add(outThumb.format);
}
if (enableExperimental)
{
cmd.add("-strict");
cmd.add("-2");// experimental
}
if (outThumb.vframes > 0)
{
cmd.add("-vframes");
cmd.add(String.valueOf(outThumb.vframes));
}
if (outThumb.secondToTakeThumbFrom > 0)
{
cmd.add("-ss");
cmd.add(String.valueOf(outThumb.secondToTakeThumbFrom));
} else
{
// lets take thumb from 1st second
cmd.add("-ss");
cmd.add(String.valueOf(1));
}
cmd.add(new File(outThumb.path).getCanonicalPath());
execFFMPEG(cmd, sc);
}
public void processVideo
(ClipVideoRecorder in, ClipVideoRecorder out, boolean enableExperimental, ShellCallback sc) throws Exception
{
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
if (in.format != null)
{
cmd.add(Argument.FORMAT);
cmd.add(in.format);
}
if (in.videoCodec != null)
{
cmd.add(Argument.VIDEOCODEC);
cmd.add(in.videoCodec);
}
if (in.audioCodec != null)
{
cmd.add(Argument.AUDIOCODEC);
cmd.add(in.audioCodec);
}
cmd.add("-i");
cmd.add(new File(in.path).getCanonicalPath());
if (out.videoBitrate > 0)
{
cmd.add(Argument.BITRATE_VIDEO);
cmd.add(out.videoBitrate + "k");
}
if (out.width > 0)
{
cmd.add(Argument.SIZE);
cmd.add(out.width + "x" + out.height);
}
if (out.videoFps != null)
{
cmd.add(Argument.FRAMERATE);
cmd.add(out.videoFps);
}
if (out.videoCodec != null)
{
cmd.add(Argument.VIDEOCODEC);
cmd.add(out.videoCodec);
}
if (out.fast264)
{
cmd.add("-vpre");
cmd.add("lossless_ultrafast");
}
if (out.videoBitStreamFilter != null)
{
cmd.add(Argument.VIDEOBITSTREAMFILTER);
cmd.add(out.videoBitStreamFilter);
}
String croppingCommand = "";
// crops regularly to shorter edge
croppingCommand = croppingCommand.concat("crop=" + out.cropValue + ":"
+ out.cropValue + ":" + "0" + ":"
+ (out.cropValueBigger - out.cropValue) / 2);
if (out.videoFilter != null)
{
cmd.add("-vf");
cmd.add(out.videoFilter + "," + croppingCommand.toString()
+ ",scale=320:320");
}
if (out.audioCodec != null)
{
cmd.add(Argument.AUDIOCODEC);
cmd.add(out.audioCodec);
}
if (out.audioBitStreamFilter != null)
{
cmd.add(Argument.AUDIOBITSTREAMFILTER);
cmd.add(out.audioBitStreamFilter);
}
if (out.audioChannels > 0)
{
cmd.add(Argument.CHANNELS_AUDIO);
cmd.add(out.audioChannels + "");
}
if (out.audioBitrate > 0)
{
cmd.add(Argument.BITRATE_AUDIO);
cmd.add(out.audioBitrate + "k");
}
if (out.format != null)
{
cmd.add("-f");
cmd.add(out.format);
}
if (enableExperimental)
{
cmd.add("-strict");
cmd.add("-2");// experimental
}
cmd.add(new File(out.path).getCanonicalPath());
System.out.println("FFMPEG command to execute is: " + cmd.toString());
execFFMPEG(cmd, sc);
}
public void changeAspectOfAVideo
(ClipVideoRecorder in, ClipVideoRecorder out, boolean enableExperimental, ShellCallback sc)
throws Exception
{
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
if (in.format != null)
{
cmd.add(Argument.FORMAT);
cmd.add(in.format);
}
if (in.videoCodec != null)
{
cmd.add(Argument.VIDEOCODEC);
cmd.add(in.videoCodec);
}
if (in.audioCodec != null)
{
cmd.add(Argument.AUDIOCODEC);
cmd.add(in.audioCodec);
}
cmd.add("-i");
cmd.add(new File(in.path).getCanonicalPath());
if (out.videoBitrate > 0)
{
cmd.add(Argument.BITRATE_VIDEO);
cmd.add(out.videoBitrate + "k");
}
if (out.width > 0)
{
cmd.add(Argument.SIZE);
cmd.add(out.width + "x" + out.height);
}
if (out.videoFps != null)
{
cmd.add(Argument.FRAMERATE);
cmd.add(out.videoFps);
}
if (out.videoCodec != null)
{
cmd.add(Argument.VIDEOCODEC);
cmd.add(out.videoCodec);
}
if (out.fast264)
{
cmd.add("-vpre");
cmd.add("lossless_ultrafast");
}
if (out.videoBitStreamFilter != null)
{
cmd.add(Argument.VIDEOBITSTREAMFILTER);
cmd.add(out.videoBitStreamFilter);
}
if (out.videoFilter != null)
{
cmd.add("-vf");
cmd.add(out.videoFilter);
}
if (out.audioCodec != null)
{
cmd.add(Argument.AUDIOCODEC);
cmd.add(out.audioCodec);
}
if (out.audioBitStreamFilter != null)
{
cmd.add(Argument.AUDIOBITSTREAMFILTER);
cmd.add(out.audioBitStreamFilter);
}
if (out.audioChannels > 0)
{
cmd.add(Argument.CHANNELS_AUDIO);
cmd.add(out.audioChannels + "");
}
if (out.audioBitrate > 0)
{
cmd.add(Argument.BITRATE_AUDIO);
cmd.add(out.audioBitrate + "k");
}
if (out.format != null)
{
cmd.add("-f");
cmd.add(out.format);
}
if (enableExperimental)
{
cmd.add("-strict");
cmd.add("-2");// experimental
}
cmd.add(new File(out.path).getCanonicalPath());
System.out.println("FFMPEG aspect command to execute is: " + cmd.toString());
execFFMPEG(cmd, sc);
}
public void concatDemuxVideos(File inFile, Clip out, boolean enableExperimental, ShellCallback sc) throws Exception
{
// sample demux concat command
// ffmpeg -f concat -i mylist.txt -c copy output
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-f");
cmd.add("concat");
cmd.add("-i");
cmd.add(inFile.getCanonicalPath());
cmd.add("-strict");
cmd.add("-2");// experimental
cmd.add("-c");
cmd.add("copy");
// cmd.add(Argument.BITRATE_VIDEO);
// cmd.add(out.videoBitrate + "k");
// cmd.add("-acodec");
// cmd.add("aac");
// cmd.add("-ar");
// cmd.add("22050");
// cmd.add("-ac");
// cmd.add("2");
// cmd.add("-vf");
// cmd.add(croppingCommand.toString());
cmd.add(new File(out.path).getCanonicalPath());
System.out.println("FFMPEG CONCAT DEMUX command to execute is: " + cmd.toString());
execFFMPEG(cmd, sc);
}
public void cropAndScaleVideo(ClipVideoRecorder in, ClipVideoRecorder out, boolean enableExperimental, ShellCallback sc)
throws Exception
{
// sample command that crops and scales at the same time
// ffmpeg -i input.mpg -vf crop=in_w:in_w*9/16,scale=1280:720 -t 150 -s
// hd720 -b 8500k output.mpg
String croppingCommand = "";
// crops regularly to shorter edge
croppingCommand = croppingCommand.concat("crop=" + out.cropValue + ":"
+ out.cropValue + ":" + "0" + ":"
+ (out.cropValueBigger - out.cropValue) / 2);
// crops to size.height
// croppingCommand = croppingCommand.concat("crop=" + "320" + ":" +
// "320" + ":" + "0" + ":" + (out.cropValueBigger-out.cropValue)/2);
System.out.println("Cropping command = " + croppingCommand);
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
// cmd.add("-f");
// cmd.add("lavfi");
cmd.add("-i");
cmd.add(new File(in.path).getCanonicalPath());
cmd.add("-strict");
cmd.add("-2");// experimental
cmd.add(Argument.VIDEOCODEC);
cmd.add(out.videoCodec);
cmd.add(Argument.BITRATE_VIDEO);
cmd.add(out.videoBitrate + "k");
// cmd.add("-preset"); //only for libx264
// cmd.add("ultrafast");
cmd.add("-acodec");
cmd.add("aac");
cmd.add("-ar");
cmd.add("22050");
cmd.add("-ac");
cmd.add("2");
cmd.add("-vf");
// add scaling to fixed 320x320 so processing goes faster
cmd.add(croppingCommand.toString() + ",scale=320:320");
cmd.add(new File(out.path).getCanonicalPath());
System.out.println("FFMPEG CROP AND SCALE command to execute is: " + cmd.toString());
execFFMPEG(cmd, sc);
}
public void resizeVideo(ClipVideoRecorder in, ClipVideoRecorder out, boolean enableExperimental, ShellCallback sc) throws Exception
{
// resizing videos to 320x320
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
// cmd.add("-f");
// cmd.add("lavfi");
cmd.add("-i");
cmd.add(new File(in.path).getCanonicalPath());
cmd.add("-strict");
cmd.add("-2");// experimental
cmd.add(Argument.VIDEOCODEC);
cmd.add(out.videoCodec);
cmd.add(Argument.BITRATE_VIDEO);
cmd.add(out.videoBitrate + "k");
// cmd.add("-preset"); //only for libx264
// cmd.add("ultrafast");
cmd.add("-acodec");
cmd.add("aac");
cmd.add("-ar");
cmd.add("22050");
cmd.add("-ac");
cmd.add("2");
cmd.add("-vf");
cmd.add("scale=320:320");
cmd.add(new File(out.path).getCanonicalPath());
System.out.println("FFMPEG SCALE command to execute is: " + cmd.toString());
execFFMPEG(cmd, sc);
}
public Clip createSlideshowFromImagesAndAudio(ArrayList<Clip> images, Clip audio, Clip out, int durationPerSlide, ShellCallback sc)
throws Exception
{
final String imageBasePath = new File(mFileTemp, "image-").getCanonicalPath();
final String imageBaseVariablePath = imageBasePath + "%03d.jpg";
ArrayList<String> cmd = new ArrayList<String>();
String newImagePath = null;
int imageCounter = 0;
Clip imageCover = images.get(0); // add the first image twice
cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add(new File(imageCover.path).getCanonicalPath());
if (out.width != -1 && out.height != -1)
{
cmd.add("-s");
cmd.add(out.width + "x" + out.height);
}
newImagePath = imageBasePath
+ String.format(Locale.US, "%03d", imageCounter++) + ".jpg";
cmd.add(newImagePath);
execFFMPEG(cmd, sc);
for (Clip image : images)
{
cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add(new File(image.path).getCanonicalPath());
if (out.width != -1 && out.height != -1)
{
cmd.add("-s");
cmd.add(out.width + "x" + out.height);
}
newImagePath = imageBasePath
+ String.format(Locale.US, "%03d", imageCounter++) + ".jpg";
cmd.add(newImagePath);
execFFMPEG(cmd, sc);
}
// then combine them
cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
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("-strict");
cmd.add("-2");// experimental
String fileTempMpg = new File(mFileTemp, "tmp.mpg").getCanonicalPath();
cmd.add(fileTempMpg);
execFFMPEG(cmd, sc);
// now combine and encode
cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add(fileTempMpg);
if (audio != null && audio.path != null)
{
cmd.add("-i");
cmd.add(new File(audio.path).getCanonicalPath());
cmd.add("-map");
cmd.add("0:0");
cmd.add("-map");
cmd.add("1:0");
cmd.add(Argument.AUDIOCODEC);
cmd.add("aac");
cmd.add(Argument.BITRATE_AUDIO);
cmd.add("128k");
}
cmd.add("-strict");
cmd.add("-2");// experimental
cmd.add(Argument.VIDEOCODEC);
if (out.videoCodec != null)
cmd.add(out.videoCodec);
else
cmd.add("mpeg4");
if (out.videoBitrate != -1)
{
cmd.add(Argument.BITRATE_VIDEO);
cmd.add(out.videoBitrate + "k");
}
cmd.add(new File(out.path).getCanonicalPath());
execFFMPEG(cmd, sc);
return out;
}
/*
* 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 Clip combineAudioAndVideo(Clip videoIn, Clip audioIn, Clip out,
ShellCallback sc) throws Exception
{
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add(new File(videoIn.path).getCanonicalPath());
cmd.add("-i");
cmd.add(new File(audioIn.path).getCanonicalPath());
cmd.add("-strict");
cmd.add("-2");// experimental
cmd.add(Argument.AUDIOCODEC);
if (out.audioCodec != null)
cmd.add(out.audioCodec);
else
{
cmd.add("copy");
}
cmd.add(Argument.VIDEOCODEC);
if (out.videoCodec != null)
cmd.add(out.videoCodec);
else
{
cmd.add("copy");
}
if (out.videoBitrate != -1)
{
cmd.add(Argument.BITRATE_VIDEO);
cmd.add(out.videoBitrate + "k");
}
if (out.videoFps != null)
{
cmd.add(Argument.FRAMERATE);
cmd.add(out.videoFps);
}
if (out.audioBitrate != -1)
{
cmd.add(Argument.BITRATE_AUDIO);
cmd.add(out.audioBitrate + "k");
}
cmd.add("-y");
cmd.add("-cutoff");
cmd.add("15000");
if (out.width > 0)
{
cmd.add(Argument.SIZE);
cmd.add(out.width + "x" + out.height);
}
if (out.format != null)
{
cmd.add("-f");
cmd.add(out.format);
}
File fileOut = new File(out.path);
cmd.add(fileOut.getCanonicalPath());
execFFMPEG(cmd, sc);
return out;
}
public Clip convertImageToMP4(Clip mediaIn, int duration, String outPath,
ShellCallback sc) throws Exception
{
Clip result = new Clip();
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(mFfmpegBin);
cmd.add("-y");
cmd.add("-loop");
cmd.add("1");
cmd.add("-i");
cmd.add(new File(mediaIn.path).getCanonicalPath());
cmd.add(Argument.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(Argument.SIZE);
cmd.add(mediaIn.width + "x" + mediaIn.height);
// cmd.add("-vf");
// cmd.add("\"scale=-1:" + mediaIn.width + "\"");
}
if (mediaIn.videoBitrate != -1)
{
cmd.add(Argument.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(new File(result.path).getCanonicalPath());
execFFMPEG(cmd, sc);
return result;
}
// based on this gist: https://gist.github.com/3757344
// ffmpeg -i input1.mp4 -vcodec copy -vbsf h264_mp4toannexb -acodec copy
// part1.ts
// ffmpeg -i input2.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts
// intermediate2.ts
public Clip convertToMP4Stream(Clip mediaIn, String startTime,
double duration, String outPath, ShellCallback sc) throws Exception
{
ArrayList<String> cmd = new ArrayList<String>();
Clip mediaOut = new Clip();
mediaOut.path = outPath;
String mediaPath = new File(mediaIn.path).getCanonicalPath();
cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
if (startTime != null)
{
cmd.add(Argument.STARTTIME);
cmd.add(startTime);
}
if (duration != -1)
{
cmd.add(Argument.DURATION);
double dValue = mediaIn.duration;
int hours = (int) (dValue / 3600f);
dValue -= (hours * 3600);
cmd.add("0");
cmd.add(String.format(Locale.US, "%s", hours));
cmd.add(":");
int min = (int) (dValue / 60f);
dValue -= (min * 60);
cmd.add("0");
cmd.add(String.format(Locale.US, "%s", min));
cmd.add(":");
cmd.add(String.format(Locale.US, "%f", dValue));
// cmd.add("00:00:" +
// String.format(Locale.US,"%f",mediaIn.duration));
}
cmd.add("-i");
cmd.add(mediaPath);
cmd.add("-f");
cmd.add("mpegts");
cmd.add("-c");
cmd.add("copy");
cmd.add("-an");
// cmd.add(Argument.VIDEOBITSTREAMFILTER);
cmd.add("-bsf:v");
cmd.add("h264_mp4toannexb");
File fileOut = new File(mediaOut.path);
mediaOut.path = fileOut.getCanonicalPath();
cmd.add(mediaOut.path);
execFFMPEG(cmd, sc);
return mediaOut;
}
public Clip convertToWaveAudio(Clip mediaIn, String outPath,
int sampleRate, int channels, ShellCallback sc) throws Exception
{
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
if (mediaIn.startTime != null)
{
cmd.add("-ss");
cmd.add(mediaIn.startTime);
}
if (mediaIn.duration != -1)
{
cmd.add("-t");
cmd.add(String.format(Locale.US, "%f", mediaIn.duration));
}
cmd.add("-i");
cmd.add(new File(mediaIn.path).getCanonicalPath());
cmd.add("-ar");
cmd.add(sampleRate + "");
cmd.add("-ac");
cmd.add(channels + "");
cmd.add("-vn");
Clip mediaOut = new Clip();
File fileOut = new File(outPath);
mediaOut.path = fileOut.getCanonicalPath();
cmd.add(mediaOut.path);
execFFMPEG(cmd, sc);
return mediaOut;
}
public Clip convertTo3GPAudio(Clip mediaIn, Clip mediaOut, ShellCallback sc)
throws Exception
{
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add(new File(mediaIn.path).getCanonicalPath());
if (mediaIn.startTime != null)
{
cmd.add("-ss");
cmd.add(mediaIn.startTime);
}
if (mediaIn.duration != -1)
{
cmd.add("-t");
cmd.add(String.format(Locale.US, "%f", mediaIn.duration));
}
cmd.add("-vn");
if (mediaOut.audioCodec != null)
{
cmd.add("-acodec");
cmd.add(mediaOut.audioCodec);
}
if (mediaOut.audioBitrate != -1)
{
cmd.add("-ab");
cmd.add(mediaOut.audioBitrate + "k");
}
cmd.add("-strict");
cmd.add("-2");
File fileOut = new File(mediaOut.path);
cmd.add(fileOut.getCanonicalPath());
execFFMPEG(cmd, sc);
return mediaOut;
}
public Clip convert(Clip mediaIn, String outPath, ShellCallback sc)
throws Exception
{
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add(new File(mediaIn.path).getCanonicalPath());
if (mediaIn.startTime != null)
{
cmd.add("-ss");
cmd.add(mediaIn.startTime);
}
if (mediaIn.duration != -1)
{
cmd.add("-t");
cmd.add(String.format(Locale.US, "%f", mediaIn.duration));
}
Clip mediaOut = new Clip();
File fileOut = new File(outPath);
mediaOut.path = fileOut.getCanonicalPath();
cmd.add(mediaOut.path);
execFFMPEG(cmd, sc);
return mediaOut;
}
public Clip convertToMPEG(Clip mediaIn, String outPath, ShellCallback sc)
throws Exception
{
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add(new File(mediaIn.path).getCanonicalPath());
if (mediaIn.startTime != null)
{
cmd.add("-ss");
cmd.add(mediaIn.startTime);
}
if (mediaIn.duration != -1)
{
cmd.add("-t");
cmd.add(String.format(Locale.US, "%f", mediaIn.duration));
}
// cmd.add("-strict");
// cmd.add("experimental");
// everything to mpeg
cmd.add("-f");
cmd.add("mpeg");
Clip mediaOut = mediaIn.clone();
File fileOut = new File(outPath);
mediaOut.path = fileOut.getCanonicalPath();
cmd.add(mediaOut.path);
execFFMPEG(cmd, sc);
return mediaOut;
}
public void concatAndTrimFilesMPEG(ArrayList<ClipVideoRecorder> videos, ClipVideoRecorder out,
boolean preConvert, ShellCallback sc) throws Exception
{
int idx = 0;
if (preConvert)
{
for (ClipVideoRecorder mdesc : videos)
{
if (mdesc.path == null)
continue;
// extract MPG video
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add(mdesc.path);
if (mdesc.startTime != null)
{
cmd.add("-ss");
cmd.add(mdesc.startTime);
}
if (mdesc.duration != -1)
{
cmd.add("-t");
cmd.add(String.format(Locale.US, "%f", mdesc.duration));
}
/*
* cmd.add ("-acodec"); cmd.add("pcm_s16le");
*
* cmd.add ("-vcodec"); cmd.add("mpeg2video");
*/
if (out.audioCodec == null)
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(mCmdCat);
idx = 0;
for (ClipVideoRecorder 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);
String[] cmds =
{ "sh", "-c", cmdRun.toString() };
Runtime.getRuntime().exec(cmds).waitFor();
ClipVideoRecorder mInCat = new ClipVideoRecorder();
mInCat.path = mCatPath;
processVideo(mInCat, out, false, sc);
out.path = mCatPath;
}
public void extractAudio(Clip mdesc, String audioFormat, File audioOutPath,
ShellCallback sc) throws IOException, InterruptedException
{
// no just extract the audio
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add(new File(mdesc.path).getCanonicalPath());
cmd.add("-vn");
if (mdesc.startTime != null)
{
cmd.add("-ss");
cmd.add(mdesc.startTime);
}
if (mdesc.duration != -1)
{
cmd.add("-t");
cmd.add(String.format(Locale.US, "%f", mdesc.duration));
}
cmd.add("-f");
cmd.add(audioFormat); // wav
// everything to WAV!
cmd.add(audioOutPath.getCanonicalPath());
execFFMPEG(cmd, sc);
}
private class FileMover
{
InputStream inputStream;
File destination;
public FileMover(InputStream _inputStream, File _destination)
{
inputStream = _inputStream;
destination = _destination;
}
public void moveIt() throws IOException
{
OutputStream destinationOut = new BufferedOutputStream(
new FileOutputStream(destination));
int numRead;
byte[] buf = new byte[1024];
while ((numRead = inputStream.read(buf)) >= 0)
{
destinationOut.write(buf, 0, numRead);
}
destinationOut.flush();
destinationOut.close();
}
}
public int killVideoProcessor(boolean asRoot, boolean waitFor)
throws IOException
{
int killDelayMs = 300;
int result = -1;
int procId = -1;
while ((procId = ShellUtils.findProcessId(mFfmpegBin)) != -1)
{
// Log.d(TAG, "Found PID=" + procId + " - killing now...");
String[] cmd =
{ ShellUtils.SHELL_CMD_KILL + ' ' + procId + "" };
try
{
result = ShellUtils.doShellCommand(cmd, new ShellCallback()
{
@Override
public void shellOut(String msg)
{
}
@Override
public void processComplete(int exitValue)
{
}
}, asRoot, waitFor);
Thread.sleep(killDelayMs);
} catch (Exception e)
{
}
}
return result;
}
public Clip trim(Clip mediaIn, boolean withSound, String outPath,
ShellCallback sc) throws Exception
{
ArrayList<String> cmd = new ArrayList<String>();
Clip mediaOut = new Clip();
String mediaPath = mediaIn.path;
cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
if (mediaIn.startTime != null)
{
cmd.add(Argument.STARTTIME);
cmd.add(mediaIn.startTime);
}
if (mediaIn.duration != -1)
{
cmd.add("-t");
cmd.add(String.format(Locale.US, "%f", mediaIn.duration));
}
cmd.add("-i");
cmd.add(mediaPath);
if (!withSound)
cmd.add("-an");
cmd.add("-strict");
cmd.add("-2");// experimental
mediaOut.path = outPath;
cmd.add(mediaOut.path);
execFFMPEG(cmd, sc);
return mediaOut;
}
public void concatAndTrimFilesMP4Stream(ArrayList<Clip> videos, Clip out,
boolean preconvertClipsToMP4, boolean useCatCmd, ShellCallback sc)
throws Exception
{
File fileExportOut = new File(out.path);
StringBuffer sbCat = new StringBuffer();
int tmpIdx = 0;
for (Clip vdesc : videos)
{
Clip mdOut = null;
if (preconvertClipsToMP4)
{
File fileOut = new File(mFileTemp, tmpIdx + "-trim.mp4");
if (fileOut.exists())
fileOut.delete();
// boolean withSound = false;
boolean withSound = true;
mdOut = trim(vdesc, withSound, fileOut.getCanonicalPath(), sc);
fileOut = new File(mFileTemp, tmpIdx + ".ts");
if (fileOut.exists())
fileOut.delete();
mdOut = convertToMP4Stream(mdOut, null, -1,
fileOut.getCanonicalPath(), sc);
} else
{
File fileOut = new File(mFileTemp, tmpIdx + ".ts");
if (fileOut.exists())
fileOut.delete();
mdOut = convertToMP4Stream(vdesc, vdesc.startTime,
vdesc.duration, fileOut.getCanonicalPath(), sc);
}
if (mdOut != null)
{
if (sbCat.length() > 0)
sbCat.append("|");
sbCat.append(new File(mdOut.path).getCanonicalPath());
tmpIdx++;
}
}
File fileExportOutTs = new File(fileExportOut.getCanonicalPath()
+ ".ts");
if (useCatCmd)
{
// cat 0.ts 1.ts > foo.ts
StringBuffer cmdBuff = new StringBuffer();
cmdBuff.append(mCmdCat);
cmdBuff.append(" ");
StringTokenizer st = new StringTokenizer(sbCat.toString(), "|");
while (st.hasMoreTokens())
cmdBuff.append(st.nextToken()).append(" ");
cmdBuff.append("> ");
cmdBuff.append(fileExportOut.getCanonicalPath() + ".ts");
Runtime.getRuntime().exec(cmdBuff.toString());
ArrayList<String> cmd = new ArrayList<String>();
cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add(fileExportOut.getCanonicalPath() + ".ts");
cmd.add("-c");
cmd.add("copy");
cmd.add("-an");
cmd.add(fileExportOut.getCanonicalPath());
execFFMPEG(cmd, sc, null);
} else
{
// ffmpeg -i "concat:intermediate1.ts|intermediate2.ts" -c copy
// -bsf:a aac_adtstoasc output.mp4
ArrayList<String> cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add("concat:" + sbCat.toString());
cmd.add("-c");
cmd.add("copy");
cmd.add("-an");
cmd.add(fileExportOut.getCanonicalPath());
execFFMPEG(cmd, sc);
}
if ((!fileExportOut.exists()) || fileExportOut.length() == 0)
{
throw new Exception("There was a problem rendering the video: "
+ fileExportOut.getCanonicalPath());
}
}
public Clip getInfo(Clip in) throws IOException, InterruptedException
{
ArrayList<String> cmd = new ArrayList<String>();
cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add(new File(in.path).getCanonicalPath());
InfoParser ip = new InfoParser(in);
execFFMPEG(cmd, ip, null);
try
{
Thread.sleep(200);
} catch (Exception e)
{
}
return in;
}
public ClipVideoRecorder getInfoForAndroidAlternativeVideoRecorder(ClipVideoRecorder in)
throws IOException, InterruptedException
{
ArrayList<String> cmd = new ArrayList<String>();
cmd = new ArrayList<String>();
cmd.add(mFfmpegBin);
cmd.add("-y");
cmd.add("-i");
cmd.add(new File(in.path).getCanonicalPath());
InfoParserForAndroidAlternativeVideoRecorder ip = new InfoParserForAndroidAlternativeVideoRecorder(in);
execFFMPEG(cmd, ip, null);
try
{
Thread.sleep(200);
} catch (Exception e)
{
}
return in;
}
private class InfoParser implements ShellCallback
{
private Clip mMedia;
private int retValue;
public InfoParser(Clip media)
{
mMedia = media;
}
@Override
public void shellOut(String shellLine)
{
if (shellLine.contains("Duration:"))
{
// Duration: 00:01:01.75, start: 0.000000, bitrate: 8184 kb/s
String[] timecode = shellLine.split(",")[0].split(":");
double duration = 0;
duration = Double.parseDouble(timecode[1].trim()) * 60 * 60; // hours
duration += Double.parseDouble(timecode[2].trim()) * 60; // minutes
duration += Double.parseDouble(timecode[3].trim()); // seconds
mMedia.duration = duration;
}
// Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661),
// yuv420p, 1920x1080, 16939 kb/s, 30.02 fps, 30 tbr, 90k tbn, 180k
// tbc
else if (shellLine.contains(": Video:"))
{
String[] line = shellLine.split(":");
String[] videoInfo = line[3].split(",");
mMedia.videoCodec = videoInfo[0];
}
// Stream #0:1(eng): Audio: aac (mp4a / 0x6134706D), 48000 Hz,
// stereo, s16, 121 kb/s
else if (shellLine.contains(": Audio:"))
{
String[] line = shellLine.split(":");
String[] audioInfo = line[3].split(",");
mMedia.audioCodec = audioInfo[0];
}
//
// Stream #0.0(und): Video: h264 (Baseline), yuv420p, 1280x720, 8052
// kb/s, 29.97 fps, 90k tbr, 90k tbn, 180k tbc
// Stream #0.1(und): Audio: mp2, 22050 Hz, 2 channels, s16, 127 kb/s
}
@Override
public void processComplete(int exitValue)
{
retValue = exitValue;
}
}
private class InfoParserForAndroidAlternativeVideoRecorder implements ShellCallback
{
private ClipVideoRecorder mMedia;
private int retValue;
public InfoParserForAndroidAlternativeVideoRecorder(ClipVideoRecorder media)
{
mMedia = media;
}
@Override
public void shellOut(String shellLine)
{
if (shellLine.contains("Duration:"))
{
// Duration: 00:01:01.75, start: 0.000000, bitrate: 8184 kb/s
String[] timecode = shellLine.split(",")[0].split(":");
double duration = 0;
duration = Double.parseDouble(timecode[1].trim()) * 60 * 60; // hours
duration += Double.parseDouble(timecode[2].trim()) * 60; // minutes
duration += Double.parseDouble(timecode[3].trim()); // seconds
mMedia.duration = duration;
}
// Stream #0:0(eng): Video: h264 (High) (avc1 / 0x31637661),
// yuv420p, 1920x1080, 16939 kb/s, 30.02 fps, 30 tbr, 90k tbn, 180k
// tbc
else if (shellLine.contains(": Video:"))
{
String[] line = shellLine.split(":");
String[] videoInfo = line[3].split(",");
mMedia.videoCodec = videoInfo[0];
}
// Stream #0:1(eng): Audio: aac (mp4a / 0x6134706D), 48000 Hz,
// stereo, s16, 121 kb/s
else if (shellLine.contains(": Audio:"))
{
String[] line = shellLine.split(":");
String[] audioInfo = line[3].split(",");
mMedia.audioCodec = audioInfo[0];
}
//
// Stream #0.0(und): Video: h264 (Baseline), yuv420p, 1280x720, 8052
// kb/s, 29.97 fps, 90k tbr, 90k tbn, 180k tbc
// Stream #0.1(und): Audio: mp2, 22050 Hz, 2 channels, s16, 127 kb/s
}
@Override
public void processComplete(int exitValue)
{
retValue = exitValue;
}
}
private class StreamGobbler extends Thread
{
InputStream is;
String type;
ShellCallback sc;
StreamGobbler(InputStream is, String type, ShellCallback sc)
{
this.is = is;
this.type = type;
this.sc = sc;
}
public void run()
{
try
{
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line = null;
while ((line = br.readLine()) != null)
if (sc != null)
sc.shellOut(line);
} catch (IOException ioe)
{
// Log.e(TAG,"error reading shell slog",ioe);
ioe.printStackTrace();
}
}
}
public static Bitmap getVideoFrame(String videoPath, long frameTime)
throws Exception
{
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try
{
retriever.setDataSource(videoPath);
return retriever.getFrameAtTime(frameTime,
MediaMetadataRetriever.OPTION_CLOSEST);
} finally
{
try
{
retriever.release();
} catch (RuntimeException ex)
{
}
}
}
}
/*
* Main options: -L show license -h show help -? show help -help show help
* --help show help -version show version -formats show available formats
* -codecs show available codecs -bsfs show available bit stream filters
* -protocols show available protocols -filters show available filters -pix_fmts
* show available pixel formats -sample_fmts show available audio sample formats
* -loglevel loglevel set libav* logging level -v loglevel set libav* logging
* level -debug flags set debug flags -report generate a report -f fmt force
* format -i filename input file name -y overwrite output files -n do not
* overwrite output files -c codec codec name -codec codec codec name -pre
* preset preset name -t duration record or transcode "duration" seconds of
* audio/video -fs limit_size set the limit file size in bytes -ss time_off set
* the start time offset -itsoffset time_off set the input ts offset -itsscale
* scale set the input ts scale -timestamp time set the recording timestamp
* ('now' to set the current time) -metadata string=string add metadata -dframes
* number set the number of data frames to record -timelimit limit set max
* runtime in seconds -target type specify target file type ("vcd", "svcd",
* "dvd", "dv", "dv50", "pal-vcd", "ntsc-svcd", ...) -xerror exit on error
* -frames number set the number of frames to record -tag fourcc/tag force codec
* tag/fourcc -filter filter_list set stream filterchain -stats print progress
* report during encoding -attach filename add an attachment to the output file
* -dump_attachment filename extract an attachment into a file -bsf
* bitstream_filters A comma-separated list of bitstream filters -dcodec codec
* force data codec ('copy' to copy stream)
*
* Advanced options: -map file.stream[:syncfile.syncstream] set input stream
* mapping -map_channel file.stream.channel[:syncfile.syncstream] map an audio
* channel from one stream to another -map_meta_data
* outfile[,metadata]:infile[,metadata] DEPRECATED set meta data information of
* outfile from infile -map_metadata outfile[,metadata]:infile[,metadata] set
* metadata information of outfile from infile -map_chapters input_file_index
* set chapters mapping -benchmark add timings for benchmarking -dump dump each
* input packet -hex when dumping packets, also dump the payload -re read input
* at native frame rate -loop_input deprecated, use -loop -loop_output
* deprecated, use -loop -vsync video sync method -async audio sync method
* -adrift_threshold threshold audio drift threshold -copyts copy timestamps
* -copytb source copy input stream time base when stream copying -shortest
* finish encoding within shortest input -dts_delta_threshold threshold
* timestamp discontinuity delta threshold -copyinkf copy initial non-keyframes
* -q q use fixed quality scale (VBR) -qscale q use fixed quality scale (VBR)
* -streamid streamIndex:value set the value of an outfile streamid -muxdelay
* seconds set the maximum demux-decode delay -muxpreload seconds set the
* initial demux-decode delay -fpre filename set options from indicated preset
* file
*
* Video options: -vframes number set the number of video frames to record -r
* rate set frame rate (Hz value, fraction or abbreviation) -s size set frame
* size (WxH or abbreviation) -aspect aspect set aspect ratio (4:3, 16:9 or
* 1.3333, 1.7777) -bits_per_raw_sample number set the number of bits per raw
* sample -croptop size Removed, use the crop filter instead -cropbottom size
* Removed, use the crop filter instead -cropleft size Removed, use the crop
* filter instead -cropright size Removed, use the crop filter instead -padtop
* size Removed, use the pad filter instead -padbottom size Removed, use the pad
* filter instead -padleft size Removed, use the pad filter instead -padright
* size Removed, use the pad filter instead -padcolor color Removed, use the pad
* filter instead -vn disable video -vcodec codec force video codec ('copy' to
* copy stream) -sameq use same quantizer as source (implies VBR) -same_quant
* use same quantizer as source (implies VBR) -pass n select the pass number (1
* or 2) -passlogfile prefix select two pass log file name prefix -vf filter
* list video filters -b bitrate video bitrate (please use -b:v) -dn disable
* data
*
* Advanced Video options: -pix_fmt format set pixel format -intra use only
* intra frames -vdt n discard threshold -rc_override override rate control
* override for specific intervals -deinterlace deinterlace pictures -psnr
* calculate PSNR of compressed frames -vstats dump video coding statistics to
* file -vstats_file file dump video coding statistics to file -intra_matrix
* matrix specify intra matrix coeffs -inter_matrix matrix specify inter matrix
* coeffs -top top=1/bottom=0/auto=-1 field first -dc precision
* intra_dc_precision -vtag fourcc/tag force video tag/fourcc -qphist show QP
* histogram -force_fps force the selected framerate, disable the best supported
* framerate selection -force_key_frames timestamps force key frames at
* specified timestamps -vbsf video bitstream_filters deprecated -vpre preset
* set the video options to the indicated preset
*
* Audio options: -aframes number set the number of audio frames to record -aq
* quality set audio quality (codec-specific) -ar rate set audio sampling rate
* (in Hz) -ac channels set number of audio channels -an disable audio -acodec
* codec force audio codec ('copy' to copy stream) -vol volume change audio
* volume (256=normal) -rmvol volume rematrix volume (as factor)
*/
/*
* //./ffmpeg -y -i test.mp4 -vframes 999999 -vf 'redact=blurbox.txt [out] [d],
* [d]nullsink' -acodec copy outputa.mp4
*
* //ffmpeg -v 10 -y -i
* /sdcard/org.witness.sscvideoproto/videocapture1042744151.mp4 -vcodec libx264
* //-b 3000k -s 720x480 -r 30 -acodec copy -f mp4 -vf
* 'redact=/data/data/org.witness.sscvideoproto/redact_unsort.txt'
* ///sdcard/org.witness.sscvideoproto/new.mp4
*
* //"-vf" , "redact=" + Environment.getExternalStorageDirectory().getPath() +
* "/" + PACKAGENAME + "/redact_unsort.txt",
*
*
* // Need to make sure this will create a legitimate mp4 file //"-acodec",
* "ac3", "-ac", "1", "-ar", "16000", "-ab", "32k",
*
*
* String[] ffmpegCommand = {"/data/data/"+PACKAGENAME+"/ffmpeg", "-v", "10",
* "-y", "-i", recordingFile.getPath(), "-vcodec", "libx264", "-b", "3000k",
* "-vpre", "baseline", "-s", "720x480", "-r", "30", //"-vf",
* "drawbox=10:20:200:60:red@0.5", "-vf" , "\"movie="+ overlayImage.getPath()
* +" [logo];[in][logo] overlay=0:0 [out]\"", "-acodec", "copy", "-f", "mp4",
* savePath.getPath()+"/output.mp4"};
*
*
*
*
* //ffmpeg -i source-video.avi -s 480x320 -vcodec mpeg4 -acodec aac -ac 1 -ar
* 16000 -r 13 -ab 32000 -aspect 3:2 output-video.mp4/
*/
/*
* concat doesn't seem to work cmd.add("-i");
*
* StringBuffer concat = new StringBuffer();
*
* for (int i = 0; i < videos.size(); i++) { if (i > 0) concat.append("|");
*
* concat.append(out.path + '.' + i + ".wav");
*
* }
*
* cmd.add("concat:\"" + concat.toString() + "\"");
*/