厌倦了复杂的编译?一键集成 AeroFFmpeg,让Android音视频开发更简单!
前言
作为 Android 开发者,你是否在项目中需要处理音视频功能,却被 FFmpeg 复杂的编译过程劝退?为了在 Android 上使用 FFmpeg,你需要:
- 下载 FFmpeg 源码,可能还有各种依赖库。
- 配置复杂的交叉编译环境,如 NDK、编译器等。
- 编写冗长的编译脚本,处理不同 CPU 架构和系统版本的兼容性问题。
- 每一次 FFmpeg 版本更新,都要重新来一遍。
- 这个过程就像一场噩梦,耗时耗力,稍有不慎就可能出错。!
本文介绍FFmpeg Android SDK开源项目AeroFFmpeg。它将 FFmpeg 编译成一个简单易用的 AAR (Android Archive) 文件,为Android 开发者提供一个开箱即用的解决方案。Android 开发者只需要几行代码,就可以将强大的 FFmpeg 功能集成到你的 Android 项目中。
极简集成AeroFFmpeg
当前的AeroFFmpeg的minSdk=24,targetSdk = 34,基于FFmpeg4.2.9编译而成,同时支持MP3、H.264、H.265。当前的AeroFFmpeg只支持arm64-v8a和x86两种架构,暂时不支持16KB page sizes。将AeroFFmpeg集成到你的项目中后便可以立即使用ffmpeg命令行。下面介绍如何集成和使用AeroFFmpeg。
引入AeroFFmpeg的AAR文件
由于还没有在Maven 仓库上发布AeroFFmpeg,所以需要直接导入AAR文件的原始方法集成AAR文件。AAR文件的地址如下:
github.com/Ilovecat194...
1.将AAR文件复制到 Android 项目的 app 模块下的 libs 文件夹中。如果 libs 文件夹不存在,请手动创建一个。

2.配置 build.gradle 文件
打开 app 模块的 build.gradle 文件。你需要配置两个部分:仓库(repositories) 和 依赖(dependencies)。
scss
repositories {
flatDir {
dirs("libs")
}
}
implementation(files("libs/AeroFFmpeglib-release.aar"))
settings.gradle.kts文件中repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)声明会导致无法本地添加模块,如果有的话请将这个声明替换成repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS)。

3.同步项目 点击 Sync Now。Gradle 会自动从指定的 Maven 仓库中下载 .aar 文件及其所有依赖,然后添加到你的项目中。 通过以上步骤你就将AeroFFmpeg集成到你的项目中了。
AeroFFmpeg的用法和情况
AeroFFmpeg采用单例模式,支持ffmpeg命令行,命令行是在子线程中异步执行。需要注意的是AeroFFmpeg并不支持多线程并发执行ffmpeg命令行,在前一个命令行任务结束前无法开启新的命令行任务。如果需要支持并发执行音视频任务可以考虑通过Android多进程编程调用不同的AeroFFmpeg实例执行。
kotlin
//开启一个ffmpeg命令行任务
//返回结果 0成功,-1 失败
val result=AeroFFmpeg.start("ffmpeg -i input.mp4 output.mp4")
//获取当前任务状态 0正在执行任务或一次任务都未执行 1结束任务且没有报错 -1结束任务有报错
AeroFFmpeg.getTaskState()
//取消任务
AeroFFmpeg.cancelTask()
//获取任务当前的时间进度,返回结果类型是Double,单位是毫秒
AeroFFmpeg.getProgressTime()
//通过日志回调函数处理日志信息
AeroFFmpeg.setLogListener (
object : OnLogListener {
override fun onLog(level: Int, message: String) {
Log.d("AeroFFmpeg.setLogListener","$level,$message")
}
}
)
基于AeroFFmpeg和WorkManager完成后台音视频任务
接下来,我们基于AeroFFmpeg和WorkManager来开发一个后台执行音视频任务的应用。WorkManager作为官方推荐的后台任务API,具有可靠性强、持久执行、高效节能等优势,关于WorkManager的具体用法可以看看我之前发的文章。
首先定义一个执行ffmpeg命令行的Worker类。
kotlin
class FFmpegTaskWorker(
appContext: Context,
workerParams: WorkerParameters
) : CoroutineWorker(appContext, workerParams) {
// 重写 getForegroundInfo() 以提供前台服务通知信息
override suspend fun getForegroundInfo(): ForegroundInfo {
return ForegroundInfo(
FFMPEG_CMD_ID,
notificationBuilder.setContentText("开始执行任务...").build()
)
}
override suspend fun doWork(): Result {
setForeground(getForegroundInfo())
val cmd = inputData.getString(FFMPEG_CMD)
return withContext(Dispatchers.IO) {
try {
try {
if(AeroFFmpeg.start(cmd!!)<0){
return@withContext Result.failure()
}
AeroFFmpeg.start(cmd)
while(AeroFFmpeg.getTaskState()==0){
notificationBuilder.setContentText("任务进度:${formatTimeFromMillis(AeroFFmpeg.getProgressTime())}")
setProgress(AeroFFmpeg.getProgressTime())
if (ActivityCompat.checkSelfPermission(
applicationContext,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
notificationManager.notify(FFMPEG_CMD_ID, notificationBuilder.build())
}
delay(100)
}
if(AeroFFmpeg.getTaskState()==1){
notificationBuilder.setContentText("执行成功")
if (ActivityCompat.checkSelfPermission(
applicationContext,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
notificationManager.notify(FFMPEG_CMD_ID, notificationBuilder.build())
}
return@withContext Result.success()
}
else{
notificationBuilder.setContentText("执行失败")
if (ActivityCompat.checkSelfPermission(
applicationContext,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
notificationManager.notify(FFMPEG_CMD_ID, notificationBuilder.build())
}
return@withContext Result.failure()
}
} catch (e: Exception) {
e.printStackTrace()
// Task failed, update notification
notificationBuilder.setContentText("执行失败").setProgress(0, 0, false)
return@withContext Result.failure()
} finally {
// Ensure notification is cancelled
if (ActivityCompat.checkSelfPermission(
applicationContext,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED
) {
notificationManager.cancel(FFMPEG_CMD_ID)
}
}
}
}
}
定义个启动任务的方法,将提交任务给WorkManager的流程给封装进去。
scss
fun startFFmpegTask(context: Context, cmd: String) {
val data = workDataOf(FFMPEG_CMD to cmd)
// Set constraints to only download when connected to a network
val constraints = Constraints.Builder()
//.setRequiredNetworkType(NetworkType.CONNECTED)
.build()
// Create a OneTimeWorkRequest for the download
val ffmpegTaskRequest = OneTimeWorkRequestBuilder<FFmpegTaskWorker>()
.setConstraints(constraints)
.setInputData(data)
.addTag(FFMPEG_TASK_TAG)
.build()
// Enqueue the work request
WorkManager.getInstance(context).enqueue(ffmpegTaskRequest)
}
通过LiveData类,实时观察并显示任务的状态和进度,实现后台任务信息与UI展示信息的同步。
scss
val context = LocalContext.current
val workManager = WorkManager.getInstance(context)
var cmd by remember { mutableStateOf("") }
val workInfos: LiveData<List<WorkInfo>> = remember {
workManager.getWorkInfosByTagLiveData(FFMPEG_TASK_TAG)
}
val ffmpegTasks by workInfos.observeAsState(initial = emptyList())
...
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
items(ffmpegTasks) { workInfo ->
FFmpegTaskItem(workInfo)
}
}
最后我们得到的应用如下。

这样,通过AeroFFmpeg我们很轻松在Android应用上实现音视频功能。
总结
FFmpeg 是一个功能强大的开源的音视频处理工具,支持转码、剪辑、录制、流处理、滤镜等功能。通过AeroFFmpeg,无需复杂的NDK配置、编写冗长的编译脚本就可以让你的Android应用获得FFmpeg的音视频能力。后面我会持续更新和完善AeroFFmpeg。
AeroFFmpeg的仓库地址如下:
github.com/Ilovecat194... AeroFFmpeg+WorkManager的示例代码在下面仓库的WorkDownloader项目里:
github.com/Ilovecat194...