You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中使用FFmpeg合并音视频时崩溃问题求助

解决Android中FFmpeg合并音视频时"找不到程序"的崩溃问题

报错原因

崩溃的核心原因是Android系统默认不自带FFmpeg可执行文件,直接调用"ffmpeg"命令时,系统无法在PATH路径中找到对应的程序,抛出No such file or directory异常。同时原代码在主线程中调用process.waitFor()会阻塞UI线程,也会加剧崩溃风险。

解决方案

方案一:使用MobileFFmpeg封装库(最简便)

MobileFFmpeg是预编译好的FFmpeg库,集成后可以直接调用API执行命令,无需自行编译。

  1. 添加依赖
    在Module级别的build.gradle(或build.gradle.kts)中添加依赖:
dependencies {
    // 根据需求选择不同版本,full版本包含所有功能
    implementation 'com.arthenica:mobile-ffmpeg-full:4.4.LTS'
}
  1. 修正合并代码
    使用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的可执行文件:

  1. 编译FFmpeg
    针对Android的不同CPU架构(armeabi-v7a、arm64-v8a等)编译FFmpeg可执行文件,具体编译步骤可参考FFmpeg官方文档的Android编译指南。

  2. 将FFmpeg放入APK
    将编译好的FFmpeg文件放入src/main/jniLibs/[架构目录]下(比如src/main/jniLibs/arm64-v8a/ffmpeg)。

  3. 复制并调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 23:57:21