在安卓中用到的三方库:github.com/arthenica/f...
这个库很强大,支持很多平台,每个平台都有各自的分支代码,用了一段时间,稳定性挺好的,
找到安卓下的分支:FFmpegKit for Android FFmpegKit Android 版
引入项目:
java
repositories {
mavenCentral()
}
dependencies {
implementation 'com.arthenica:ffmpeg-kit-full:6.0-2'
}
每个平台下,又分为多个库,每个库包含不同的功能,因为功能越丰富,导入到项目中编译的包体积越大,尽量选择适合自己功能的库进行使用。
📦 FFmpeg 编译配置选项(库依赖分类)
配置项 | 说明 | 包含的库 |
---|---|---|
min |
最小化构建 | - |
min-gpl |
最小化构建并启用 GPL 库 | vid.stab , x264 , x265 , xvidcore |
https |
启用 HTTPS 支持(使用非-GPL 依赖) | gmp , gnutls |
https-gpl |
启用 HTTPS 支持(使用 GPL 兼容依赖) | gmp , gnutls , vid.stab , x264 , x265 , xvidcore |
audio |
启用音频相关编码器/解码器 | lame , libilbc , libvorbis , opencore-amr , opus , shine , soxr , speex , twolame , vo-amrwbenc |
video |
启用视频相关编码器/解码器 | dav1d , fontconfig , freetype , fribidi , kvazaar , libass , libiconv , libtheora , libvpx , libwebp , snappy , zimg |
full |
启用所有非 GPL 第三方库 | dav1d , fontconfig , freetype , fribidi , gmp , gnutls , kvazaar , libass , libiconv , libilbc , libtheora , libvorbis , libvpx , libwebp , libxml2 , opencore-amr , opus , shine , snappy , soxr , speex , twolame , vo-amrwbenc , zimg |
full-gpl |
启用所有库(包括 GPL) | dav1d , fontconfig , freetype , fribidi , gmp , gnutls , kvazaar , lame , libass , libiconv , libilbc , libtheora , libvorbis , libvpx , libwebp , libxml2 , opencore-amr , opus , shine , snappy , soxr , speex , twolame , vid.stab , vo-amrwbenc , x264 , x265 , xvidcore , zimg |
比如你如果只需要保存 rtsp 视频流 和 推流的话,只需要导入 min-gpl
即可:
java
implementation 'com.arthenica:ffmpeg-kit-min-gpl:6.0-2'
如果你想要更多,比如添加水印,就涉及到 FFmpeg 滤镜相关功能,就需要引入full-gpl
:
java
implementation 'com.arthenica:ffmpeg-kit-full-gpl:6.0-2'
具体需要什么功能,可以进去看说明,说明没有涉及到的,并且你不想用全功能库,你也可以一个一个试试,也许就能满足你。
正文开始
由说明可知,使用方式为:
java
import com.arthenica.ffmpegkit.FFmpegKit;
FFmpegSession session = FFmpegKit.execute("-i file1.mp4 -c:v mpeg4 file2.mp4");
if (ReturnCode.isSuccess(session.getReturnCode())) {
// SUCCESS
} else if (ReturnCode.isCancel(session.getReturnCode())) {
// CANCEL
} else {
// FAILURE
Log.d(TAG, String.format("Command failed with state %s and rc %s.%s", session.getState(), session.getReturnCode(), session.getFailStackTrace()));
}
或者异步调用:
java
FFmpegKit.executeAsync("-i file1.mp4 -c:v mpeg4 file2.mp4", new FFmpegSessionCompleteCallback() {
@Override
public void apply(FFmpegSession session) {
SessionState state = session.getState();
ReturnCode returnCode = session.getReturnCode();
// CALLED WHEN SESSION IS EXECUTED
Log.d(TAG, String.format("FFmpeg process exited with state %s and rc %s.%s", state, returnCode, session.getFailStackTrace()));
}
}, new LogCallback() {
@Override
public void apply(com.arthenica.ffmpegkit.Log log) {
// CALLED WHEN SESSION PRINTS LOGS
}
}, new StatisticsCallback() {
@Override
public void apply(Statistics statistics) {
// CALLED WHEN SESSION GENERATES STATISTICS
}
});
与正常 ffmpeg 命令不同的是,在传入命令时,前面不需要加 "ffmpeg" 关键字,只需传入后面的具体命令即可,加上会报错哦!
关键代码:
搞了好久才凑齐的正确代码,这东西真不能听 AI 的一面之辞,不然就被 AI 一条路领到黑,
java
private static String buildWatermarkCommand(VideoFile file, VideoTimeRange timeRange, String outputPath, String fontPath) {
FFmpegKitConfig.setFontconfigConfigurationPath(fontPath);
String drawtextFilter = String.format(
"drawtext=text='%s':fontfile=%s:fontcolor=white:fontsize=20:x=0:y=30",
DEFAULT_WATERMARK, fontPath
);
List<String> commandList = new ArrayList<>();
commandList.add("-ss"); // 指定输入文件的开始时间,格式:HH:MM:SS
commandList.add(timeRange.startTime);
commandList.add("-i"); // 输入文件路径
commandList.add(file.filePath);
commandList.add("-t"); // 指定持续时间,格式:HH:MM:SS
commandList.add(timeRange.durationStr);
commandList.add("-vf"); // 视频滤镜,用于添加水印文字
commandList.add(drawtextFilter);
commandList.add("-c:v"); // 视频编码器设置
commandList.add(VIDEO_CODEC); // 使用H.264软件编码器
commandList.add("-preset"); // 编码速度预设
commandList.add(VIDEO_PRESET); // ultrafast:最快编码速度,文件稍大
commandList.add("-crf"); // 恒定质量因子(0-51,越小质量越好)
commandList.add(String.valueOf(VIDEO_CRF)); // 23:平衡质量和文件大小的推荐值
commandList.add("-c:a"); // 音频编码器设置
commandList.add("copy"); // 直接复制音频流,不重新编码
commandList.add("-r");
commandList.add("20"); // 每秒20帧
commandList.add("-avoid_negative_ts"); // 避免负时间戳问题
commandList.add("make_zero"); // 将负时间戳调整为0
commandList.add(outputPath); // 输出文件路径
return String.join(" ", commandList);
}
简单介绍下命令作用:对一段现有的视频文件进行剪辑,-ss
指定开始时间,比如要剪切的原视频时长为两分钟,所以开始时间到结束时间就是:00:00:00 - 00:02:00 , 假设要剪辑中间一分钟的视频,那么 -ss
指定的开始时间为:00:00:30 , -t
持续时间就是:00:01:00 , 截取的时间段为:00:00:30 - 00:01:30 ,-c:v
设置编码器,一般 H.264 就够了,如果设置其他的编码器,要看你的设备支持不支持了,-r
设置帧率,如果你想要剪切的视频大小小一点,一方面就可以通过降低帧率,另一方面就可以降低码率来实现(上述命令码率未指定默认按原视频码率)。
如果你指定的时间范围,超过原视频时长会报错,这很正常,只是报错内容可能看不懂,这是一个问题点!
设置 FFmpegKitConfig.setFontconfigConfigurationPath
的作用是 github.com/arthenica/f... 参考第四条:ffmpeg 需要有效的 fontconfig 配置才能在使用 drawtext filter 时渲染文本。
这里指定一个存在的字体路径即可 比如:/system/fonts/NotoSansCJK-Regular.ttc
注意:有的字体不支持中文,写入中文水印的时候会乱码!
视频处理性能测试(A133,Android 10)
测试环境
- 设备平台:A133
- 操作系统:Android 10
- 测试内容:视频加水印 vs 无水印处理
- 加水印帧率:15fps
- 视频格式:H.264 (宽高:720x576)(位率:512Kbps)
性能对比数据
视频时长 | 处理方式 | 耗时(ms) | 耗时(秒) | 输出文件大小 |
---|---|---|---|---|
30s | 加水印 | 5214 | 5.21 | 1.5M |
30s | 加水印(15帧) | 3813 | 3.81 | 1.5M |
30s | 不加水印 | 168 | 0.17 | 0.9M |
60s | 加水印 | 12013 | 12.01 | 2.5M |
60s | 加水印(15帧) | 7392 | 7.39 | 2.5M |
60s | 不加水印 | 208 | 0.21 | 1.5M |
120s | 加水印 | 22006 | 22.01 | 5.03M |
120s | 加水印(15帧) | 18404 | 18.40 | 5.03M |
120s | 不加水印 | 277 | 0.28 | 2.8M |
240s | 加水印 | 50547 | 50.55 | 10.57M |
240s | 加水印(15帧) | 37857 | 37.86 | 10.57M |
240s | 不加水印 | 448 | 0.45 | 5.24M |
在 A133 平台上,加水印操作是性能瓶颈,视频重编码操作对cpu要求比较高。