在安卓中使用 FFmpegKit 剪切视频并添加文字水印
在安卓中用到的三方库:https://github.com/arthenica/ffmpeg-kit
这个库很强大,支持很多平台,每个平台都有各自的分支代码,用了一段时间,稳定性挺好的,
找到安卓下的分支:FFmpegKit for Android FFmpegKit Android 版
引入项目:
repositories { mavenCentral()}dependencies { implementation \'com.arthenica:ffmpeg-kit-full:6.0-2\'}
每个平台下,又分为多个库,每个库包含不同的功能,因为功能越丰富,导入到项目中编译的包体积越大,尽量选择适合自己功能的库进行使用。
📦 FFmpeg 编译配置选项(库依赖分类)
min
min-gpl
vid.stab
, x264
, x265
, xvidcore
https
gmp
, gnutls
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
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
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
即可:
implementation \'com.arthenica:ffmpeg-kit-min-gpl:6.0-2\'
如果你想要更多,比如添加水印,就涉及到 FFmpeg 滤镜相关功能,就需要引入full-gpl
:
implementation \'com.arthenica:ffmpeg-kit-full-gpl:6.0-2\'
具体需要什么功能,可以进去看说明,说明没有涉及到的,并且你不想用全功能库,你也可以一个一个试试,也许就能满足你。
正文开始
由说明可知,使用方式为:
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()));}
或者异步调用:
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 一条路领到黑,
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
的作用是 https://github.com/arthenica/ffmpeg-kit/wiki/Tips 参考第四条:ffmpeg 需要有效的 fontconfig 配置才能在使用 drawtext filter 时渲染文本。
这里指定一个存在的字体路径即可 比如:/system/fonts/NotoSansCJK-Regular.ttc
注意:有的字体不支持中文,写入中文水印的时候会乱码!
视频处理性能测试(A133,Android 10)
测试环境
- 设备平台:A133
- 操作系统:Android 10
- 测试内容:视频加水印 vs 无水印处理
- 加水印帧率:15fps
- 视频格式:H.264 (宽高:720x576)(位率:512Kbps)
性能对比数据
在 A133 平台上,加水印操作是性能瓶颈,视频重编码操作对cpu要求比较高。
补充
在低端设备使用FFmpeg处理视频显然是不推荐的,更是不明智的,除非无可选择,可以选择的替代方式为在手机或者在服务器进行视频处理操作,这样速度和体验感更好。
在手机处理发现 不设置 FFmpegKitConfig.setFontconfigConfigurationPath
也可以正常添加水印,这个操作应该是可有可无的!或者在特殊地方才会使用到它,有待发现。
开发中发现,只有full-gpl
库,才能进行加水印重编码操作,其他库均不行,没办法,只有一个加水印操作也要引入全功能,apk包体积大概增加13M左右,而且这只是在应用只支持arm64-v8a
的情况下,多一个就翻一倍!
而且full-gpl:6.0-2
最低要求sdk 24,如果你的应用之前最小sdk 小于23,然后改为24时你会发现,包体积大小剧增,这是因为minSdkVersion >= 23
默认不压缩so 大小 ,导致apk体积会变大,大于23 就在清单文件的application
标签下设置 android:extractNativeLibs=\"true\"
压缩so ,就正常了。