17 April 2017
有一段时间没有更新博客了。 最近有人在我的博客中跟我说mp4Parse不能裁剪mov视频,问我有没有其他的解决方案,所以这次我更新了一下自己的项目:Android-Video-Trimmer,使用ffmpeg来实现视频的裁剪。
FFmpeg是为大家所熟悉的处理视频的一个跨平台的开源库,支持android、ios。我们可以自己手动编译不同平台的lib库,从何集成到我们的项目中去。但是FFmpeg是一个C/C++的库,对于熟悉Java开发的Android同学来说,更希望是Java层面的调用。
在我的项目中,集成了一个FFmpegAndroid开发库,这个库优点是我们可以直接用Java的API来执行ffmpge的命令,缺点是该库比较大(大概19M),没办法对库做瘦身缩减工作。下面是我集成这个库的几个步骤。
aar依赖:
dependencies {
compile 'com.writingminds:FFmpegAndroid:0.3.2'
}
源码依赖: 用git将ffmpeg-android-java clone到本地,然后把项目中FFmpegAndroid库的源码加入到自己的项目中。
public class ZApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
initFFmpegBinary(this);
}
private void initFFmpegBinary(Context context) {
try {
FFmpeg.getInstance(context).loadBinary(new LoadBinaryResponseHandler() {
@Override
public void onFailure() {
}
});
} catch (FFmpegNotSupportedException e) {
e.printStackTrace();
}
}
}
初始化的目的是根据Android手机的cpu架构,load对应架构的ffmpeg库。
上面的准备工作做完之后,剩下就是我们关心的问题了,如何执行ffmpeg命令行呢?在前面我已经说了,这个库是对ffmpeg的在Linux系统中命令行的一个封装,所以我们需要在该库中找到一个这样的API。 那我在FFmpegInterface.java类中找到了如下的PAI:
/**
* Executes a command
* @param environvenmentVars Environment variables
* @param cmd command to execute
* @param ffmpegExecuteResponseHandler {@link FFmpegExecuteResponseHandler}
* @throws FFmpegCommandAlreadyRunningException
*/
public void execute(Map<String, String> environvenmentVars, String[] cmd, FFmpegExecuteResponseHandler ffmpegExecuteResponseHandler) throws FFmpegCommandAlreadyRunningException;
/**
* Executes a command
* @param cmd command to execute
* @param ffmpegExecuteResponseHandler {@link FFmpegExecuteResponseHandler}
* @throws FFmpegCommandAlreadyRunningException
*/
public void execute(String[] cmd, FFmpegExecuteResponseHandler ffmpegExecuteResponseHandler) throws FFmpegCommandAlreadyRunningException;
小伙伴们可以看到参数String[] cmd,就是我所说的控制台的命令行参数,只要将我们的命令行传入,设置好回调,剩下的事情就是静静等着ffmpeg触发我们的回调了,是不是感觉很爽! 下面是我的调用实现:
public static void trimVideo(Context context, String inputFile, String outputFile, long startMs, long endMs, final OnTrimVideoListener callback) {
final String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(new Date());
final String outputName = "trimmedVideo_" + timeStamp + ".mp4";
String start = convertSecondsToTime(startMs/1000);
String duration = convertSecondsToTime((endMs - startMs)/1000);
/**ffmpeg -ss START -t DURATION -i INPUT -vcodec copy -acodec copy OUTPUT
-ss 开始时间,如: 00:00:20,表示从20秒开始;
-t 时长,如: 00:00:10,表示截取10秒长的视频;
-i 输入,后面是空格,紧跟着就是输入视频文件;
-vcodec copy 和 -acodec copy 表示所要使用的视频和音频的编码格式,这里指定为copy表示原样拷贝;
INPUT,输入视频文件;
OUTPUT,输出视频文件*/
String cmd = "-ss " + start + " -t " + duration + " -i " + inputFile + " -vcodec copy -acodec copy " + outputFile + "/" + outputName;
String[] command = cmd.split(" ");
try {
FFmpeg.getInstance(context).execute(command, new ExecuteBinaryResponseHandler() {
@Override
public void onFailure(String s) {
}
@Override
public void onSuccess(String s) {
callback.onFinishTrim(null);
}
@Override
public void onStart() {
callback.onStartTrim();
}
@Override
public void onFinish() {
}
});
} catch (FFmpegCommandAlreadyRunningException e) {
// do nothing for now
}
}
上面主要是拼出ffmpeg视频裁剪的命令行参数。
视频裁剪的命令如下: ffmpeg -ss START -t DURATION -i INPUT -vcodec copy -acodec copy OUTPUT 需要主意的是: DURATION 是表示视频的持续时间而不是结束时间 INPUT 表示输入的包含文件名的文件路径 e.g: sdcard/xxx/xx/input.mp4 OUTPUT 表示输出的包含文件名的文件路径 e.g: sdcard/xxx/xx/output.mov
其他命令也基本也差不多,需要实现什么功能之后可以去查询具体的命令行,对着实现就行。
Thanks for reading. To help others please click ❤ to recommend this article if you found it helpful.
You can also follow me at GitHub
— J.Chou