将 FFmpeg 移植到 Android

Home

https://blog.csdn.net/lzw_java/article/details/21564091


缘起

对于那些尚未了解ffmpeg的同学,请移步了解FFmpeg常用基本命令,仔细研究一番,看看它的功能是否能够为你的应用带来更多乐趣和强大功能。

它具备合成音视频、播放各种编码的音视频、截取特定片段、将多张图片合成视频并与音频合并、格式转换等诸多功能。

在类似配音秀的应用中,配音是一种非常有趣的体验,我们也有意开发一个配音模块。因此,我们找到了ffmpeg这个工具,试图将其移植到Android平台。大概花了我3到4天的时间,经历了多个版本的尝试,网上很多的文章都没有帮助,直到我找到了那篇名为《android 使用ffmpeg 并调用接口》的文章,才最终获得了成功。我尝试了很多版本,只有ffmpeg 1.2和ndkr9的组合才能成功移植。

思路

ffmpeg是一个用C语言编写的程序,其中包含有一个main函数。我们首先在Android NDK下编译出libffmpeg.so文件,然后利用这个库来编译ffmpeg.c文件。我们将ffmpeg.c文件中的main函数修改为video_merge函数,即video_merge(int argc,char **argv)。接下来,就可以像下面这样合成视频:

这个操作类似在命令行下调用:ffmpeg -i src1 -i src2 -y output

环境

我们使用的操作系统是Ubuntu 12.04。

在开始之前,先查看一下相关的教程,如果遇到问题再来参考这篇文章,介绍在那篇教程下可能会遇到的问题。

修改接口调用

在修改接口调用的时候,那篇文章使用了这个Android.mk文件:

然而,我一直无法成功运行这个文件。

这个文件用于链接库,也就是为当前模块进行编译。它会在一个目录中寻找所需的函数等内容,然后链接到libffmpeg.so文件上。这里的-l表示链接的意思,而ffmpeg表示库的名称。至于lib.so这两部分是系统自动加上的。

因此,我采用了这个方法:

请注意,这里首先编译了一个共享库myffmpeg,然后通过链接到最终需要的ffmpeg-jni

在这个过程中,需要将libffmpeg.so文件放置在jni目录下,像这样:

调试ffmpeg

移植ffmpeg后,我们也使用了JNI来调用函数,但可能会遇到许多问题。那么如何进行调试呢?

如果我们能像在命令行下一样获得调试信息就好了:

这个日志可以帮助我们找到相应的位置,在附近添加一个Log.i语句即可。

在Eclipse下,你可以通过按住Ctrl键并点击类似于av_log的位置,从原始的main函数入口处跳转到函数位置,逐步跟踪。它位于ffmpeg/libavutil/log.c文件中的av_log_default_callback函数内,就像这样:

请注意到了LOGD吗,它是:

这里的...表示可变长参数。可变长参数的最简单例子就是printf函数,有时候我们可以这样写printf("%d", a),也可以这样写printf("%d %d", a, a),前者有两个参数,后者有三个参数。使用类似的语法实现。

__android_log_print则是Android提供的用于打印到Logcat中的函数。

通过这种方式,你就可以查看输出日志,帮助解决无法合成、不支持mp3或amr、或者视频问题等等。

有时候可能会突然出现异常。

如何知道错误发生的位置呢?

在命令行中输入:adb shell logcat ndk-stack -sym obj/local/armeabi

你会发现合成虽然成功了,但应用程序仍会崩溃退出,因为ffmpeg原始的main函数最后有一个exit语句,只需注释掉即可。

成功合成后,你可能会发现再次调用时会出现”INVALID HEAP ADDRESS IN dlfree ffmpeg”的错误。这是由于ffmpeg内存泄漏,一些动态分配的空间没有被释放。因此,我们可以将ffmpeg的合成放在一个service中,合成完成后再杀掉该service。

AnroidMainfest.xml:

在你的程序中注册一个receiver:

这样,你就可以第一次调用ffmpeg生成一个没有声音的视频文件,播放它,让用户进行配音,然后再次调用,将用户的声音与视频合成。

在我的测试中,我发现,如果使用AAC编码器录制,虽然可以合成视频,但无法使用MediaPlayer播放AAC文件(这可能是小米2s独有的问题?)。而如果使用ARMNB,则无法合成,还需要重新生成libffmpeg.so,并在configure中启用ARMNB。然而,在启用了ARMNB后,运行./config.sh编译ARMNB时会出错,提示找不到相应的文件。找到了文件后又提示找不到所包含的文件。同样地,启用libmp3lame也没有成功。

之所以希望能够进行播放,是因为合成一个10秒的1280*720视频和10秒的录音大约需要1分钟的时间。希望用户在决定是否合成之前可以先听听配音的效果。

观察配音秀的dubbing文件夹,你会发现配音秀录制了用户的声音,得到了tmp.amr和tmp.pcm文件,然后上传到服务器进行合成,并将合成的文件下载下来。

在配音时,原始视频、字幕和已经去除了需要配音片段的音频已经下载好了。因此,在配音时,只需播放这个音频,其中需要配音的部分是静音的。

在Eclipse中使用ndk-build

实际上,我们不必在命令行中进行ndk build。只需选择项目,右键选择”Android tools”,然后选择”Add native Support”即可。这样,每次在项目中运行时,都会自动进行ndk build

一键生成JNI函数头文件

每次手动编写这些头文件都很麻烦,其实我们可以通过一键生成来简化这个过程:

配置一个外部工具:

使用javah命令生成,并将其集成到Eclipse中。

在Eclipse中点击标题栏后,你就可以在jni目录中看到类似于com_lzw_iword_video_Myffmpeg.h的文件,就像这样:

然后,将这个头文件包含到你的C文件中,复制粘贴函数头即可。

如果你也在将ffmpeg移植到Android平台,或者在使用ffmpeg时遇到了问题,欢迎在评论中提问,进行交流讨论~


Back