Android中使用FFmpeg合并音视频时崩溃问题求助
解决Android中FFmpeg合并音视频时"找不到程序"的崩溃问题
报错原因
崩溃的核心原因是Android系统默认不自带FFmpeg可执行文件,直接调用"ffmpeg"命令时,系统无法在PATH路径中找到对应的程序,抛出No such file or directory异常。同时原代码在主线程中调用process.waitFor()会阻塞UI线程,也会加剧崩溃风险。
解决方案
方案一:使用MobileFFmpeg封装库(最简便)
MobileFFmpeg是预编译好的FFmpeg库,集成后可以直接调用API执行命令,无需自行编译。
- 添加依赖
在Module级别的build.gradle(或build.gradle.kts)中添加依赖:
dependencies { // 根据需求选择不同版本,full版本包含所有功能 implementation 'com.arthenica:mobile-ffmpeg-full:4.4.LTS' }
- 修正合并代码
使用MobileFFmpeg的API执行命令,同时通过协程将耗时操作放到子线程:
import com.arthenica.mobileffmpeg.FFmpeg import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch private fun mergeAudioAndVideo(videoFile: String, audioFile: String, outputFile: File) { // 将命令拼接成字符串数组 val command = arrayOf( "-i", videoFile, "-i", audioFile, "-c:v", "copy", "-c:a", "aac", "-strict", "experimental", "-map", "0:v:0", "-map", "1:a:0", "-shortest", outputFile.absolutePath ) // 在子线程执行FFmpeg命令 GlobalScope.launch(Dispatchers.IO) { val rc = FFmpeg.execute(command) if (rc == 0) { // 合并成功,切换到主线程更新UI launch(Dispatchers.Main) { // 比如显示合并完成提示 } } else { // 合并失败,处理错误 launch(Dispatchers.Main) { // 显示错误提示 } } } }
方案二:自行编译FFmpeg并打包进APK
如果需要自定义FFmpeg功能,可自行编译适合Android的可执行文件:
编译FFmpeg
针对Android的不同CPU架构(armeabi-v7a、arm64-v8a等)编译FFmpeg可执行文件,具体编译步骤可参考FFmpeg官方文档的Android编译指南。将FFmpeg放入APK
将编译好的FFmpeg文件放入src/main/jniLibs/[架构目录]下(比如src/main/jniLibs/arm64-v8a/ffmpeg)。复制并调用FFmpeg
在应用启动时,将FFmpeg复制到应用私有目录并赋予可执行权限,然后调用该路径的命令:
import android.content.Context import java.io.File import java.io.FileOutputStream import java.io.IOException private fun getFFmpegPath(context: Context): String { val ffmpegFile = File(context.filesDir, "ffmpeg") if (!ffmpegFile.exists()) { // 从jniLibs复制FFmpeg文件 try { context.assets.open("ffmpeg").use { inputStream -> FileOutputStream(ffmpegFile).use { outputStream -> inputStream.copyTo(outputStream) } // 赋予可执行权限 ffmpegFile.setExecutable(true) } } catch (e: IOException) { e.printStackTrace() } } return ffmpegFile.absolutePath } private fun mergeAudioAndVideo(context: Context, videoFile: String, audioFile: String, outputFile: File) { val ffmpegPath = getFFmpegPath(context) val command = arrayOf( ffmpegPath, "-i", videoFile, "-i", audioFile, "-c:v", "copy", "-c:a", "aac", "-strict", "experimental", "-map", "0:v:0", "-map", "1:a:0", "-shortest", outputFile.absolutePath ) // 在子线程执行命令 Thread { try { val process = Runtime.getRuntime().exec(command) val exitCode = process.waitFor() // 根据exitCode判断结果,0为成功 process.inputStream.close() process.errorStream.close() process.outputStream.close() } catch (e: IOException) { e.printStackTrace() } catch (e: InterruptedException) { e.printStackTrace() } }.start() }
注意事项
- 权限处理:如果操作外部存储文件,需要申请
READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE权限(Android 10及以上可使用Scoped Storage)。 - 主线程阻塞:所有FFmpeg操作必须放在子线程执行,避免阻塞UI导致ANR或崩溃。
内容的提问来源于stack exchange,提问作者Sachin Saxena
相关产品推荐
相关产品推荐

