公司动态

【Android FFMPEG 开发】告别权限困扰:使用 mobile-ffmpeg 在 Android 10+ 上安全执行 FFMPEG 指令

📅 2026/7/15 5:41:50
【Android FFMPEG 开发】告别权限困扰:使用 mobile-ffmpeg 在 Android 10+ 上安全执行 FFMPEG 指令
1. 为什么需要 mobile-ffmpeg在 Android 10 及更高版本中Google 引入了更严格的安全策略其中一项重要变化就是禁止应用直接执行外部可执行文件。这个改动直接影响了传统 FFmpeg 集成方案——那些需要将 ffmpeg 可执行文件放入 assets 目录运行时解压到应用私有目录再执行的方案全部失效。我去年在开发一个视频编辑应用时就遇到了这个问题。当时应用在 Android 9 上运行良好但在 Android 10 设备上总是报错java.io.IOException: Cannot run program /data/user/0/com.example/files/ffmpeg: error13, Permission denied经过排查发现这正是因为 Android 10 开始禁止应用执行私有目录中的可执行文件。这时候就需要 mobile-ffmpeg 这样的替代方案它通过 JNI 直接调用 FFmpeg 的函数库完全避开了执行外部程序的需求。2. mobile-ffmpeg 的核心原理2.1 与传统方案的对比传统 FFmpeg 集成方案如 FFmpegAndroid的工作流程是这样的将预编译的 ffmpeg 可执行文件打包到 assets应用启动时将其解压到/data/data/包名/files通过Runtime.getRuntime().exec()执行命令而 mobile-ffmpeg 采用了完全不同的思路将 FFmpeg 编译为动态库.so通过 JNI 直接调用ffmpeg.c中的main()函数所有处理都在进程内完成不涉及外部程序执行这种架构差异带来了几个关键优势完全兼容 Android 10不违反任何沙箱限制性能更好避免了进程间通信开销资源占用更低不需要维护可执行文件副本2.2 项目现状与替代方案需要注意的是mobile-ffmpeg 原作者已经停止维护该项目并在 GitHub 主页推荐迁移到ffmpeg-kit。ffmpeg-kit 可以看作是 mobile-ffmpeg 的增强版主要改进包括支持更多平台iOS/macOS/Flutter/React Native更频繁的更新更好的性能优化更完善的 API 设计不过由于 mobile-ffmpeg 仍然可以正常工作且迁移成本很低API 基本兼容本文仍以它为例讲解核心概念。3. 快速集成指南3.1 添加依赖在模块的 build.gradle 中添加依赖以完整版为例dependencies { implementation com.arthenica:mobile-ffmpeg-full:4.4 }这里有多个版本可选mobile-ffmpeg-min: 基础版约 6MBmobile-ffmpeg-min-gpl: 基础版GPL 协议代码mobile-ffmpeg-full: 完整版约 25MBmobile-ffmpeg-full-gpl: 完整版GPL 协议代码3.2 执行基础命令同步执行示例Javaint rc FFmpeg.execute(-i input.mp4 -c:v libx264 output.mp4); if (rc Config.RETURN_CODE_SUCCESS) { Log.i(TAG, 转码成功); } else { Log.e(TAG, 转码失败返回值 rc); Config.printLastCommandOutput(Log.ERROR); // 打印错误输出 }异步执行示例Kotlinval executionId FFmpeg.executeAsync(-i input.mp4 -c:v libx264 output.mp4) { id, returnCode - when (returnCode) { Config.RETURN_CODE_SUCCESS - Log.i(TAG, 后台转码完成) Config.RETURN_CODE_CANCEL - Log.w(TAG, 用户取消操作) else - Log.e(TAG, 转码出错) } } // 需要取消时可以调用 FFmpeg.cancel(executionId)4. 高级功能与最佳实践4.1 进度监控对于耗时操作如视频转码可以添加统计回调Config.enableStatisticsCallback(new StatisticsCallback() { Override public void apply(Statistics statistics) { int progress (int) (statistics.getTime() * 100 / 10000); // 假设总时长10秒 Log.d(TAG, 转码进度 progress %); } });4.2 常见问题排查Q命令执行失败但没输出A先检查是否添加了日志回调Config.enableLogCallback(new LogCallback() { Override public void apply(LogMessage message) { Log.d(TAG, message.getText()); } });Q如何确定支持哪些编解码器A执行FFmpeg.execute(-codecs)查看完整列表或者检查Config.getPackageName()返回的版本信息。Q出现 No such file or directory 错误AAndroid 11 加强了文件访问限制建议使用Context.getFilesDir()获取应用私有目录对于媒体文件使用 MediaStore API确保路径使用绝对路径而非相对路径4.3 性能优化建议线程管理默认会使用 4 个线程可以通过-threads参数调整内存控制大文件处理时添加-thread_queue_size限制队列大小硬件加速根据设备支持情况选择解码器如-c:v h264_mediacodec批量处理合并多个操作为一个命令如转码水印裁剪5. 迁移到 ffmpeg-kit虽然 mobile-ffmpeg 仍可使用但长期项目建议迁移到 ffmpeg-kit。迁移步骤非常简单修改依赖implementation com.arthenica:ffmpeg-kit-full:5.1导入包名改为import com.arthenica.ffmpegkit.*;API 调用方式几乎相同FFmpegKit.executeAsync(-i input.mp4 output.mp4, session - { ReturnCode returnCode session.getReturnCode(); // 处理结果 });ffmpeg-kit 还新增了这些实用功能会话管理Session自定义日志级别更完善的异步回调内置崩溃保护机制在实际项目中我发现 ffmpeg-kit 的内存管理更加优秀特别是在长时间执行复杂命令时稳定性更好。