嘿stackoverflow ers,
我需要使用FFmpeg将视频和一些照片组合起来制作视频.
我已经设法在我的系统上编译FFmpeg并静态链接它.
现在我正在寻找利用ffmpeg来完成Android任务的包装/图书馆.
我尝试过的:
> Guardian项目代码,Awesome api,简单而且很好,但是当运行createSlideshowFromImagesAndAudio时,我得到了一个很好的返回代码,但是我的设备上永远不会创建该文件(如果您有使用此代码的经验,我得到的返回代码是11).
> JCodec,慢慢地说.
> FMJ,不支持我要求的功能.
所以问题仍然存在,Android的FFmpeg包装有什么好处?
最佳答案
这是FFMPEG的Wrapper完美运行:你必须按照这些步骤为android制作FFMPEG Wrapper.你需要创建几个classess,如下所示:
ShellUtils.java
package com.example.processvideo;
public class ShellUtils {
//varIoUs console cmds
public final static String SHELL_CMD_CHMOD = "chmod";
public final static String SHELL_CMD_KILL = "kill -9";
public final static String SHELL_CMD_RM = "rm";
public final static String SHELL_CMD_PS = "ps";
public final static String SHELL_CMD_PIDOF = "pidof";
public final static String CHMOD_EXE_VALUE = "700";
public static boolean isRootPossible()
{
StringBuilder log = new StringBuilder();
try {
// Check if Superuser.apk exists
File fileSU = new File("/system/app/Superuser.apk");
if (fileSU.exists())
return true;
fileSU = new File("/system/bin/su");
if (fileSU.exists())
return true;
//Check for 'su' binary
String[] cmd = {"which su"};
int exitCode = ShellUtils.doShellCommand(cmd,new ShellCallback ()
{
@Override
public void shellOut(char[] msg) {
//System.out.print(msg);
}
},false,true);
if (exitCode == 0) {
logMessage("Can acquire root permissions");
return true;
}
} catch (IOException e) {
//this means that there is no root to be had (normally) so we won't log anything
logException("Error checking for root access",e);
}
catch (Exception e) {
logException("Error checking for root access",e);
//this means that there is no root to be had (normally)
}
logMessage("Could not acquire root permissions");
return false;
}
public static int findProcessId(String command)
{
int procId = -1;
try
{
procId = findProcessIdWithPidOf(command);
if (procId == -1)
procId = findProcessIdWithPS(command);
}
catch (Exception e)
{
try
{
procId = findProcessIdWithPS(command);
}
catch (Exception e2)
{
logException("Unable to get proc id for: " + command,e2);
}
}
return procId;
}
//use 'pidof' command
public static int findProcessIdWithPidOf(String command) throws Exception
{
int procId = -1;
Runtime r = Runtime.getRuntime();
Process procPs = null;
String baseName = new File(command).getName();
//fix contributed my mikos on 2010.12.10
procPs = r.exec(new String[] {SHELL_CMD_PIDOF,baseName});
//procPs = r.exec(SHELL_CMD_PIDOF);
BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream()));
String line = null;
while ((line = reader.readLine())!=null)
{
try
{
//this line should just be the process id
procId = Integer.parseInt(line.trim());
break;
}
catch (NumberFormatException e)
{
logException("unable to parse process pid: " + line,e);
}
}
return procId;
}
//use 'ps' command
public static int findProcessIdWithPS(String command) throws Exception
{
int procId = -1;
Runtime r = Runtime.getRuntime();
Process procPs = null;
procPs = r.exec(SHELL_CMD_PS);
BufferedReader reader = new BufferedReader(new InputStreamReader(procPs.getInputStream()));
String line = null;
while ((line = reader.readLine())!=null)
{
if (line.indexOf(' ' + command)!=-1)
{
StringTokenizer st = new StringTokenizer(line," ");
st.nextToken(); //proc owner
procId = Integer.parseInt(st.nextToken().trim());
break;
}
}
return procId;
}
public static int doShellCommand(String[] cmds,ShellCallback sc,boolean runAsRoot,boolean waitFor) throws Exception
{
Process proc = null;
int exitCode = -1;
if (runAsRoot)
proc = Runtime.getRuntime().exec("su");
else
proc = Runtime.getRuntime().exec("sh");
OutputStreamWriter out = new OutputStreamWriter(proc.getOutputStream());
for (int i = 0; i < cmds.length; i++)
{
logMessage("executing shell cmd: " + cmds[i] + "; runAsRoot=" + runAsRoot + ";waitFor=" + waitFor);
out.write(cmds[i]);
out.write("\n");
}
out.flush();
out.write("exit\n");
out.flush();
if (waitFor)
{
final char buf[] = new char[20];
// Consume the "stdout"
InputStreamReader reader = new InputStreamReader(proc.getInputStream());
int read=0;
while ((read=reader.read(buf)) != -1) {
if (sc != null) sc.shellOut(buf);
}
// Consume the "stderr"
reader = new InputStreamReader(proc.getErrorStream());
read=0;
while ((read=reader.read(buf)) != -1) {
if (sc != null) sc.shellOut(buf);
}
exitCode = proc.waitFor();
}
return exitCode;
}
public static void logMessage (String msg)
{
}
public static void logException (String msg,Exception e)
{
}
public interface ShellCallback
{
public void shellOut (char[] msg);
}
}
RegionTrail.java
公共类RegionTrail {
private HashMap
ObscureRegion.java
public class ObscureRegion {
/*
* Thinking about whether or not a region should contain multiple start/end times
* realizing that doing this would make editing a real pita
* Of course,it would make displaying be a 1000x better though.
class PositionTime {
int sx = 0;
int sy = 0;
int ex = 0;
int ey = 0;
int startTime = 0;
int endTime = 0;
PositionTime(int _sx,int _sy,int _ex,int _ey,int _startTime,int _endTime) {
}
}
*/
public static final float DEFAULT_X_SIZE = 150;
public static final float DEFAULT_Y_SIZE = 150;
public float sx = 0;
public float sy = 0;
public float ex = 0;
public float ey = 0;
public int timeStamp = 0;
public RegionTrail regionTrail;
private RectF rectF;
public ObscureRegion(int _timeStamp,float _sx,float _sy,float _ex,float _ey) {
timeStamp = _timeStamp;
sx = _sx;
sy = _sy;
ex = _ex;
ey = _ey;
if (sx < 0) {
sx = 0;
} else if (sy < 0) {
sy = 0;
}
}
public ObscureRegion(int _startTime,float _sy) {
this(_startTime,_sx - DEFAULT_X_SIZE/2,_sy - DEFAULT_Y_SIZE/2,_sx + DEFAULT_X_SIZE/2,_sy + DEFAULT_Y_SIZE/2);
}
public void moveRegion(float _sx,float _sy) {
moveRegion(_sx - DEFAULT_X_SIZE/2,_sy + DEFAULT_Y_SIZE/2);
}
public void moveRegion(float _sx,float _ey) {
sx = _sx;
sy = _sy;
ex = _ex;
ey = _ey;
rectF = null;
}
public RectF getRectF() {
if (rectF == null)
rectF = new RectF(sx,ey);
return rectF;
}
public RectF getBounds() {
return getRectF();
}
public String getStringData(float widthMod,float heightMod,int startTime,int duration,String currentMode) {
//left,right,top,bottom
return "" + (float)startTime/(float)1000 + ',' + (float)(startTime+duration)/(float)1000 + ',' + (int)(sx*widthMod) + ',' + (int)(ex*widthMod) + ',' + (int)(sy*heightMod) + ',' + (int)(ey*heightMod) + ',' + currentMode;
}
public RegionTrail getRegionTrail() {
return regionTrail;
}
public void setRegionTrail(RegionTrail regionTrail) {
this.regionTrail = regionTrail;
}
}
FFMPEGWrapper.java
public class FFMPEGWrapper {
String[] libraryAssets = {"ffmpeg"};
public File fileBinDir;
Context context;
private final static String FFMPEG_BINARY_VERSION = "0.10.4.1";
private final static String FFMPEG_VERSION_KEY = "ffmpegkey";
public FFMPEGWrapper(Context _context) throws FileNotFoundException,IOException {
context = _context;
fileBinDir = context.getDir("bin",0);
checkBinary();
}
private void checkBinary () throws FileNotFoundException,IOException
{
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
String currTorBinary = prefs.getString(FFMPEG_VERSION_KEY,null);
if ((currTorBinary == null || (!currTorBinary.equals(FFMPEG_BINARY_VERSION)))
|| !new File(fileBinDir,libraryAssets[0]).exists())
{
BinaryInstaller bi = new BinaryInstaller(context,fileBinDir);
bi.installFromRaw();
}
}
public void execProcess( String[] cmds,ShellCallback sc) throws Exception {
ProcessBuilder pb = new ProcessBuilder(cmds);
pb.redirectErrorStream(true);
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
/*switch(command_call_type)
{
case Keys.KEY_COMMANDEXE_TYPE_MERGEFRAME:
// refincereference.updateLoadingbar(30);
break;
}*/
while ((line = reader.readLine()) != null)
{
if (sc != null)
{
sc.shellOut(line.tocharArray());
Log.d("FFMPEG",line.tocharArray()+"");
}
}
/*switch(command_call_type)
{
case Keys.KEY_COMMANDEXE_TYPE_MERGEFRAME:
//refincereference.updateLoadingbar(40);
break;
case Keys.KEY_COMMANDEXE_TYPE_CLIPMP3:
//refincereference.updateLoadingbar(60);
break;
case Keys.KEY_COMMANDEXE_TYPE_MP3TOM4A:
//refincereference.updateLoadingbar(80);
break;
}*/
/*
if (process != null) {
process.destroy();
}*/
}
public class FFMPEGArg
{
String key;
String value;
public static final String ARG_VIDEOCODEC = "vcodec";
public static final String ARG_VERBOSITY = "v";
public static final String ARG_FILE_INPUT = "i";
public static final String ARG_SIZE = "-s";
public static final String ARG_FRAMERATE = "-r";
public static final String ARG_FORMAT = "-f";
}
public void processVideo(File redactSettingsFile,ArrayListBox.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=" + Utils.getAvailiableStorageLocation() + "/" + 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",recordingFile.getPath(),"libx264","3000k","-vpre","baseline","720x480","30",//"-vf","drawBox=10:20:200:60:red@0.5","\"movie="+ overlayImage.getPath() +" [logo];[in][logo] overlay=0:0 [out]\"","copy","mp4",savePath.getPath()+"/output.mp4"};
*/
// execProcess(ffmpegCommand,sc);
}
private void writeRedactData(File redactSettingsFile,ArrayList
BinaryInstaller.java
public class BinaryInstaller {
File installFolder;
Context context;
private static int isARMv6 = -1;
private static String CHMOD_EXEC = "700";
private final static int FILE_WRITE_BUFFER_SIZE = 32256;
public BinaryInstaller (Context context,File installFolder)
{
this.installFolder = installFolder;
this.context = context;
}
//
/*
* Extract the Tor binary from the APK file using ZIP
*/
public boolean installFromRaw () throws IOException,FileNotFoundException
{
InputStream is;
File outFile;
is = context.getResources().openRawResource(R.raw.ffmpeg);
outFile = new File(installFolder,"ffmpeg");
streamToFile(is,outFile,"700");
return true;
}
private static void copyAssetFile(Context ctx,String asset,File file) throws IOException,InterruptedException
{
DataOutputStream out = new DataOutputStream(new FileOutputStream(file));
InputStream is = new GZIPInputStream(ctx.getAssets().open(asset));
byte buf[] = new byte[8172];
int len;
while ((len = is.read(buf)) > 0) {
out.write(buf,len);
}
out.close();
is.close();
}
/*
* Write the inputstream contents to the file
*/
private static boolean streamToFile(InputStream stm,File outFile,boolean append,boolean zip,String mode) throws IOException
{
byte[] buffer = new byte[FILE_WRITE_BUFFER_SIZE];
int bytecount;
OutputStream stmOut = new FileOutputStream(outFile,append);
if (zip)
{
ZipInputStream zis = new ZipInputStream(stm);
ZipEntry ze = zis.getNextEntry();
stm = zis;
}
while ((bytecount = stm.read(buffer)) > 0)
{
stmOut.write(buffer,bytecount);
}
stmOut.close();
stm.close();
Runtime.getRuntime().exec("chmod "+mode+" "+outFile.getAbsolutePath());
return true;
}
//copy the file from inputstream to File output - alternative impl
public void copyFile (InputStream is,File outputFile)
{
try {
outputFile.createNewFile();
DataOutputStream out = new DataOutputStream(new FileOutputStream(outputFile));
DataInputStream in = new DataInputStream(is);
int b = -1;
byte[] data = new byte[1024];
while ((b = in.read(data)) != -1) {
out.write(data);
}
if (b == -1); //rejoice
//
out.flush();
out.close();
in.close();
// chmod?
} catch (IOException ex) {
Log.e("SLIDEAGRAM","error copying binary",ex);
}
}
/**
* Check if this is an ARMv6 device
* @return true if this is ARMv6
*/
private static boolean isARMv6() {
if (isARMv6 == -1) {
BufferedReader r = null;
try {
isARMv6 = 0;
r = new BufferedReader(new FileReader("/proc/cpuinfo"));
for (String line = r.readLine(); line != null; line = r.readLine()) {
if (line.startsWith("Processor") && line.contains("ARMv6")) {
isARMv6 = 1;
break;
} else if (line.startsWith("cpu architecture") && (line.contains("6TE") || line.contains("5TE"))) {
isARMv6 = 1;
break;
}
}
} catch (Exception ex) {
} finally {
if (r != null) try {r.close();} catch (Exception ex) {}
}
}
return (isARMv6 == 1);
}
private static void copyRawFile(Context ctx,int resid,File file,String mode,boolean isZipd) throws IOException,InterruptedException
{
final String abspath = file.getAbsolutePath();
// Write the iptables binary
final FileOutputStream out = new FileOutputStream(file);
InputStream is = ctx.getResources().openRawResource(resid);
if (isZipd)
{
ZipInputStream zis = new ZipInputStream(is);
ZipEntry ze = zis.getNextEntry();
is = zis;
}
byte buf[] = new byte[1024];
int len;
while ((len = is.read(buf)) > 0) {
out.write(buf,len);
}
out.close();
is.close();
// Change the permissions
Runtime.getRuntime().exec("chmod "+mode+" "+abspath).waitFor();
}
}
FFMpegVideoProcess.java
public class FFMpegVideoProcess
{
public static void mergeFramesIntoVideo(Activity context,String duration_per_frame,String input_frame_path,String out_video_path)throws Exception
{
//Looper.prepare();
String ffmpegBin;
FFMPEGWrapper ffmpeg = null;
ShellUtils.ShellCallback sc;
if (ffmpeg == null)
ffmpeg = new FFMPEGWrapper(context);
sc = new ShellUtils.ShellCallback ()
{
int total = 0;
int current = 0;
@Override
public void shellOut(char[] shellout) {
String line = new String(shellout);
int idx1;
String newStatus = null;
int progress = 0;
if ((idx1 = line.indexOf("Duration:"))!=-1)
{
int idx2 = line.indexOf(",",idx1);
String time = line.substring(idx1+10,idx2);
int hour = Integer.parseInt(time.substring(0,2));
int min = Integer.parseInt(time.substring(3,5));
int sec = Integer.parseInt(time.substring(6,8));
total = (hour * 60 * 60) + (min * 60) + sec;
newStatus = line;
progress = 0;
}
else if ((idx1 = line.indexOf("time="))!=-1)
{
int idx2 = line.indexOf(" ",idx1);
String time = line.substring(idx1+5,idx2);
newStatus = line;
int hour = Integer.parseInt(time.substring(0,8));
current = (hour * 60 * 60) + (min * 60) + sec;
progress = (int)( ((float)current) / ((float)total) *100f );
}
if (newStatus != null)
{
// Message msg = mHandler.obtainMessage(1);
// msg.getData().putInt("progress",progress);
// msg.getData().putString("status",newStatus);
// mHandler.sendMessage(msg);
}
}
};
ffmpegBin = new File(ffmpeg.fileBinDir,"ffmpeg").getAbsolutePath();
Runtime.getRuntime().exec("chmod 700 " +ffmpegBin);
// refincereference.updateLoadingbar(20);
ffmpeg.execProcess(new String[]{
ffmpegBin,"image2",duration_per_frame,input_frame_path,"640x640",//"640x388",out_video_path
},sc);
//Looper.loop();
}
}
将所有文件放在一起后,如果要将帧合并到视频中,请调用此函数,如下所示
FFMpegVideoProcess.createFramesinFolder(this,DIR_IN_WHICH_YOU_WANT_TO_KEEP_FRAMES,"frame_%03d.jpg");
FFMpegVideoProcess.mergeFramesIntoVideo(args1,DIR_IN_WHICH_YOU_WANT_TO_KEEP_FRAMES/frame_%3d.jpg",argN...);
如果您有任何查询,请随时提出问题.谢谢