厌倦了复杂的编译?一键集成 AeroFFmpeg,让Android音视频开发更简单!

厌倦了复杂的编译?一键集成 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...

参考

1.创建 Android 库谷歌教程

2.Android后台工作官方指南

3.ffmpeg命令行官方文档

相关推荐
杨杨杨大侠5 小时前
Atlas Mapper 案例 03:企业级订单实体设计文档
java·开源·github
杨杨杨大侠6 小时前
手把手教你写 httpclient 框架(二)- 核心注解系统设计与实现
java·开源·github
FIT2CLOUD飞致云1 天前
操作教程|使用Cursor工具连接JumpServer资产
运维·开源
算家计算1 天前
PDF解析神器——MinerU本地部署教程,一键去除页眉页脚,精准提取公式表格,支持84种语言,让文档转换更简单!
人工智能·开源
杨杨杨大侠1 天前
案例03-附件B-映射器实现
java·开源·github
杨杨杨大侠1 天前
案例03-附件A-订单实体设计
java·开源·github
杨杨杨大侠1 天前
案例03-附件C-性能优化
java·开源·github
杨杨杨大侠1 天前
案例03-附件D-监控系统
java·开源·github
少女续续念1 天前
国产 DevOps 崛起!Gitee 领衔构建合规、高效的企业协作工具链
git·开源