将 FFmpeg 移植到 Android
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时遇到了问题,欢迎在评论中提问,进行交流讨论~