当前位置: 首页 > backend >正文

在安卓中使用 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最小化构建并启用 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即可:

 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 {// FAILURELog.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() {@Overridepublic void apply(FFmpegSession session) {SessionState state = session.getState();ReturnCode returnCode = session.getReturnCode();// CALLED WHEN SESSION IS EXECUTEDLog.d(TAG, String.format("FFmpeg process exited with state %s and rc %s.%s", state, returnCode, session.getFailStackTrace()));}
}, new LogCallback() {@Overridepublic void apply(com.arthenica.ffmpegkit.Log log) {// CALLED WHEN SESSION PRINTS LOGS}
}, new StatisticsCallback() {@Overridepublic 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:SScommandList.add(timeRange.startTime);commandList.add("-i");                               // 输入文件路径commandList.add(file.filePath);commandList.add("-t");                               // 指定持续时间,格式:HH:MM:SScommandList.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");                        // 将负时间戳调整为0commandList.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)

性能对比数据

视频时长处理方式耗时(ms)耗时(秒)输出文件大小
30s加水印52145.211.5M
30s加水印(15帧)38133.811.5M
30s不加水印1680.170.9M
60s加水印1201312.012.5M
60s加水印(15帧)73927.392.5M
60s不加水印2080.211.5M
120s加水印2200622.015.03M
120s加水印(15帧)1840418.405.03M
120s不加水印2770.282.8M
240s加水印5054750.5510.57M
240s加水印(15帧)3785737.8610.57M
240s不加水印4480.455.24M

在 A133 平台上,加水印操作是性能瓶颈,视频重编码操作对cpu要求比较高。

http://www.xdnf.cn/news/16939.html

相关文章:

  • 05.Redis 图形工具RDM
  • io_getevents 和 io_pgetevents 系统调用及示例
  • 论文阅读:DMD蒸馏 | One-step Diffusion with Distribution Matching Distillation
  • SaProt 模型部署与运行教程
  • 【AI】文档理解
  • ZStack Cloud 5.3.40正式发布
  • 大模型能力测评(提示词请帮我把这个项目改写成为python项目)
  • 人工智能之数学基础:几何型(连续型)随机事件概率
  • 深度学习周报(7.28~8.3)
  • 2025年6月电子学会青少年软件编程(C语言)等级考试试卷(七级)
  • 抖音全新推荐大模型RankMixer
  • p2p打洞
  • 后端研发转型爬虫实战:Scrapy 二开爬虫框架的避坑指南
  • [2025ICCV-目标检测方向]DuET:通过无示例任务算术进行双增量对象检测
  • 记一次v-if和key错误使用,导致vue2的内存爆炸修复!
  • 十八、Javaweb-day18-前端实战-登录
  • 11.消息队列
  • 2. 字符设备驱动
  • Docker环境离线安装指南
  • 计算机网络:如何将一个C类网络划分成两个子网
  • 35.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--数据缓存
  • Docker-03.快速入门-部署MySQL
  • 【大模型实战】向量数据库实战 - Chroma Milvus
  • Android 之 蓝牙通信(4.0 BLE)
  • CASAtomic 原子操作详解
  • 1.内核模块
  • 攻防世界-web-csaw-mfw
  • IO流-字节流-FileOutputStream
  • 复现YOLOV5+训练指定数据集
  • 关于Web前端安全防御之安全头配置