转自:http://blog.sina.com.cn/s/blog_4868f98601016o4e.html
最近搞的视频监控项目,需要在android移动端做视频显示。用的是android ndk的开发方式,利用ffmpeg作为解码库。640*480 分辨率下, 10帧每秒尚可正常播放,但是15fps就比较吃力了,20fps就几乎不能看了。通过打日志的方式发现:每显示一帧需要133毫秒,解码的时间占据了64毫秒,很显然, 解码已经成为了最大的瓶颈。
我们在网上搜了一圈之后,发现用arm扩展的neon指令集可以很大的优化ffmpeg的解码功能。但是在编译ffmpeg库的时候要把这部分功能加进去。但是这个编译真的把我们头都要搞大了。网上的帖子试来试去没一个能用的。经过痛苦而艰难的摸索,今天下午终于编出可以用的库了!
。 亢奋之余,必须把心得总结一下。
总的来说,编译方法分为四步:
a)创建android工程,在工程目录下手动创建jni目录,下载ffmpeg(可以用最新的,我用的是ffmpeg0.11),解压到刚才的jni目录下
b) 编写针对ffmpeg configure的config.sh 使能neon功能
c) 利用ffmpeg自己的make 编译
静态库文件libavcodec.a, libavformat.a,libavutil.a
...
(注意,一定不要编译动态库,不然还要移到机器上面很麻烦),将生成的.a文件移到之前的jni目录下
d) 编写jni目录下的 Android.mk和ffmpeg_jni.c(名字你自取,就是给java调用的文件) ,然后ndk-build 生成so文件, 编译完成
以上最重要的就是:
千万别用av.mk配合ndk-build
之类的方法编译ffmpeg静态库,比如这篇文章提到的方式:http://blog.csdn.net/perfectpdl/article/details/6932696(当然,我本人对此文作者毫无攻击的意思,相反借鉴了很多,如果不用到neon功能的话,此文方法应该是可以编译的)
之所以不提倡用ndk方式编译静态库是因为比之ndk-build,ffmpeg本身自带的make功能要强大的多。而且ndk-build需要把所有的.c.S文件都要获取一遍再生成依赖关系,有点脱裤放啥的感觉。再者,利用ndk-build编译静态库要自己写一堆Android。mk文件,繁琐不说,很多关门末节的东西弄得人要崩溃。
下面逐渐来介绍一下:
创建android工程什么的就不多说了。既然用到neon你们之前就应该已经编译出过版本了,目录结构如下:
1)首先贴一下我们用的config文件:(红色的是需要注意的地方,使用的时候注意删除#后面的话)
#!/bin/sh
export TMPDIR="/tmp/"
export NDKROOT="/opt/android-ndk-r8c" #这个是你ndk安装的目录,自己改
PREBUILT=$NDKROOT/toolchains/arm-linux-androideabi-4.6/prebuilt/linux-x86
./configure --target-os=linux \
--arch=arm \
--cpu=armv7-a \
#指定处理器
--enable-cross-compile \ #使能交叉编译
--cc=$PREBUILT/bin/arm-linux-androideabi-gcc \
--cross-prefix=$PREBUILT/bin/arm-linux-androideabi-\
--nm=$PREBUILT/bin/arm-linux-androideabi-nm \
--extra-cflags="-fPIC -DANDROID-mfpu=neon -mfloat-abi=softfp-I$NDKROOT/platforms/android-9/arch-arm/usr/include" \
--enable-asm \
--disable-yasm \
--enable-static \
--disable-shared \
--enable-small \
--enable-gpl \
--enable-version3 \
--enable-nonfree \
--enable-neon \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffserver \
--disable-ffprobe \
--prefix=/home/ffmpeg-android-bin \
--extra-ldflags="-Wl,-T,$PREBUILT/arm-linux-androideabi/lib/ldscripts/armelf_linux_eabi.x-Wl,-rpath-link=$NDKROOT/platforms/android-9/arch-arm/usr/lib-L$NDKROOT/platforms/android-9/arch-arm/usr/lib
-nostdlib$PREBUILT/lib/gcc/arm-linux-androideabi/4.6/crtbegin.o$PREBUILT/lib/gcc/arm-linux-androideabi/4.6/crtend.o -lc -lm-ldl"
2)编译静态库: 这个就很简单了, 到ffmpeg目录下面make即可。将生成出来的几个.a文件移到jni目录下
3)编写jni目录下mk文件:
include $(CLEAR_VARS)
LOCAL_MODULE :=avfilter
LOCAL_SRC_FILES := libavfilter.a #这几个prebuild是为了把库预编译一下,ndk会将它移动到libs目录下面去
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE :=avutil
LOCAL_SRC_FILES := libavutil.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE :=avcodec
LOCAL_SRC_FILES := libavcodec.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE :=avdevice
LOCAL_SRC_FILES := libavdevice.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE :=swscale
LOCAL_SRC_FILES := libswscale.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE :=swresample
LOCAL_SRC_FILES := libswresample.a
include $(PREBUILT_STATIC_LIBRARY)
include $(CLEAR_VARS)
LOCAL_CFLAGS := -D__STDC_CONSTANT_MACROS-Wno-sign-compare -Wno-switch -Wno-pointer-sign -DHAVE_NEON=1\
-mfpu=neon -mfloat-abi=softfp -fPIC -DANDROID #这里的Cflag是照抄之前的config.sh里面的,实际可能用不到这么多
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/ffmpeg\
LOCAL_SRC_FILES := \
hello-jni.c #这个写你自己的jni文件名
LOCAL_LDLIBS :=
-L$(NDK_PLATFORMS_ROOT)/$(TARGET_PLATFORM)/arch-arm/usr/lib\
-L$(LOCAL_PATH) -lavformat -lavcodec -lavdevice -lavfilter-lavutil \
-lswscale -lswresample -llog-ljnigraphics -lz -ldl -lgcc
#这两个-L很重要, 保证程序能找到库
LOCAL_MODULE := libffmpeg_jni
include $(BUILD_SHARED_LIBRARY)
4) 之后在工程根目录下ndk-build 就能生成带neon的so库拉! 大功告成!
经过实际测试,解码时间缩短为15ms。 提升了4倍!!!!! 还犹豫什么,赶快敲动键盘优化吧!!
最后提醒: 本博文中的选项仅供参考, 具体还要根据实际情况修改。编译ffmpeg本身就是一件很麻烦的事情,大家要有耐心啊