FFmpeg Android平台编译与使用
FFmepg使用Makefile来编译,在Linux可以直接使用make编译,在Windows可以使用Cygwin的make来编译,FFmpeg可编译出的平台有Linux、Mac OS X、Windows、Android和IOS,只需要替换掉编译工具链和设置与平台相关的属性即可。微软也自己弄了一个项目FFmpegInterop来让FFmpeg支持WindowsPhone平台。此文参考以下两篇文章:
FFmepg使用Makefile来编译,在Linux可以直接使用make编译,在Windows可以使用Cygwin的make来编译,FFmpeg可编译出的平台有Linux、Mac OS X、Windows、Android和IOS,只需要替换掉编译工具链和设置与平台相关的属性即可。微软也自己弄了一个项目FFmpegInterop来让FFmpeg支持WindowsPhone平台。
此文参考以下两篇文章
ffmpeg编译android 硬解码支持库 libstagefright
Building FFMPEG for Android on x86
FFmpeg需要额外编译的库
libfaac、libx264、libx265等库需要单独编译,FFmpeg支持他们,但是需要在链接的时候链接这些库。
最近才发现有一个开源项目AndroidFFmpeg
写得很好,并且添加了aac、x264,还给出了APP的demo,只需要稍作修改就能编译硬编解码的了。
FFmpeg关键配置文件
- config.mak:此配置文件是执行configure时自动生成的,此配置文件定义了编译平台、目标平台、编译工具链、编译器的配置信息、链接器的配置信息、安装目录、库的前缀和后缀名、关键库的版本号及依赖关系、指定需要编译的库和功能(例如要不要把h263编译进去)。此配置文件是根据configure的参数来自动生成的,也就是动态的。
- arch.mak:根据CPU架构使用与架构相关的特性、也就是针对特定平台做的优化(一般来说就是汇编优化,arm的neon等)。
- common.mak:此配置文件是规定了中间文件、库的编译规则。
- library.mak:此配置文件定义了库和相应头文件的安装和卸载规则。
编译工具链
基于android-ndk-r10e制作的GNU-4.8版本的arm、x86版本的交叉编译工具链。
关于如何制作独立的交叉编译工具链可以参考Google的这篇文件:Standalone Toolchain
FFmpeg版本
FFmpeg的版本是2.8,关键库的版本号如下:
libavcodec_VERSION=57.7.100
libavdevice_VERSION=57.0.100
libavfilter_VERSION=6.12.100
libavformat_VERSION=57.8.102
libavresample_VERSION=3.0.0
libavutil_VERSION=55.4.100
libpostproc_VERSION=54.0.100
libswresample_VERSION=2.0.100
libswscale_VERSION=4.0.100
FFmpeg关键库的依赖关系
avcodec_FFLIBS=swresample avutil
avdevice_FFLIBS=avformat avcodec swresample avutil
avfilter_FFLIBS=swscale avformat avcodec swresample avutil
avformat_FFLIBS=avcodec swresample avutil
avresample_FFLIBS= avutil
avutil_FFLIBS=
postproc_FFLIBS= avutil
swresample_FFLIBS= avutil
swscale_FFLIBS= avutil
以下脚本基于ffmpeg/tools/build_libstagefright改写而成。由于所依赖的库update-cm-7.0.3-N1-signed.zip的链接无效了,所以采用我自己弄的一套库。由于github的访问速度较慢,我就采用了csdn的git管理。由于对它里面依赖的头文件还没有全部理清,所以暂时还不敢动它的东西。目前仅仅支持Android的armeabi-v7a和x86,如果需要支持其他架构或者平台做类似的我的修改即可。
使用方式
把此脚本保存为文件、然后放入ffmpeg/tools中,然后添加执行权限(chmod +x fileName),在ffmpeg位置执行此脚本即可。如果不能同时编译两个ARCH,那么就一个个来吧。
必要修改
- configure中的修改,参考前辈做法,我至今还未知道为啥需要这样修改。
diff --git a/configure b/configure
index 1bbaf7f..6ad3f79 100755
--- a/configure
+++ b/configure
@@ -5369,7 +5369,7 @@ enabled libsnappy && require snappy snappy-c.h snappy_compress -lsnappy
enabled libsoxr && require libsoxr soxr.h soxr_create -lsoxr && LIBSOXR="-lsoxr"
enabled libssh && require_pkg_config libssh libssh/sftp.h sftp_init
enabled libspeex && require_pkg_config speex speex/speex.h speex_decoder_init -lspeex
-enabled libstagefright_h264 && require_cpp libstagefright_h264 "binder/ProcessState.h media/stagefright/MetaData.h
+enabled libstagefright && require_cpp libstagefright_h264 "binder/ProcessState.h media/stagefright/MetaData.h
- libstagefright.cpp中的修改,这个应该说是一个错误了,不应该发生才对的,meta是一个智能指针(sp)
diff --git a/libavcodec/libstagefright.cpp b/libavcodec/libstagefright.cpp
index 07cac33..3e5e41c 100644
--- a/libavcodec/libstagefright.cpp
+++ b/libavcodec/libstagefright.cpp
@@ -280,7 +280,7 @@ static av_cold int Stagefright_init(AVCodecContext *avctx)
memcpy(s->orig_extradata, avctx->extradata, avctx->extradata_size);
meta = new MetaData;
- if (!meta) {
+ if (!meta.get()) {
ret = AVERROR(ENOMEM);
goto fail;
}
关键变量
- APP_OUT:定义安装的主目录,最终的安装目录还会根据具体的架构而定。
- APP_ABI:定义生成的架构。
- APP_DEBUG:定义是否生成debug版本的库。
#------------------------------------------------------------------------
# File Name: build_libstagefright
# Author: liuliang
# Mail: momo0853@live.com
# Time: Mon 26 Oct 2015 11:41:34 AM CST
#------------------------------------------------------------------------
#!/bin/bash
APP_OUT=out
APP_ABI="armeabi-v7a" # 'armeabi-v7a' 'x86'
APP_DEBUG=debug # 'debug' or 'release'
ANDROID_SOURCE=Android/android_source
ANDROID_LIBS=Android/android_libs
echo "Fetching Android system headers"
# this framework platform is 9
git clone --depth=1 --branch gingerbread-release https://github.com/CyanogenMod/android_frameworks_base.git $ANDROID_SOURCE/frameworks/base
git clone --depth=1 --branch gingerbread-release https://github.com/CyanogenMod/android_system_core.git $ANDROID_SOURCE/system/core
echo "Fetching Android libraries for linking"
# Libraries from any froyo/gingerbread device/emulator should work
# fine, since the symbols used should be available on most of them.
if [ ! -d "$ANDROID_LIBS" ]; then
git clone git://code.csdn.net/momo0853/android-libs.git $ANDROID_LIBS
fi
# Create APP_OUT
if [ ! -d $APP_OUT ]; then
mkdir $APP_OUT
fi
MY_EXTRA_CFLAGS="-DANDROID"
if [ "debug" = "$APP_DEBUG" ]; then
MY_EXTRA_CFLAGS="$_EXTRA_CFLAGS -DNDEBUG"
fi
for abi in $APP_ABI; do
echo "configure $abi"
# Expand the prebuilt/* path into the correct one
if [ "$abi" = "x86" ]; then
TOOLCHAIN=echo /home/liuliang/soft/my/android_x86
FLAGS="--target-os=linux --cross-prefix=i686-linux-android- --arch=x86 --cpu=i686"
EXTRA_CFLAGS="-fpic -pipe -march=atom -msse3 -ffast-math -mfpmath=sse"
elif [ "$abi" = "armeabi-v7a" ]; then
TOOLCHAIN=echo /home/liuliang/soft/my/android_arm
FLAGS="--target-os=linux --cross-prefix=arm-linux-androideabi- --arch=arm --cpu=armv7-a"
EXTRA_CFLAGS="-march=armv7-a -mfloat-abi=softfp -mfpu=neon"
fi
export PATH=$TOOLCHAIN/bin:$PATH
ANDROID_LIBS="$ANDROID_LIBS/$abi"
FLAGS="$FLAGS --disable-avdevice --disable-decoder=h264 --disable-decoder=h264_vdpau --enable-libstagefright-h264"
DEST="$APP_OUT/$abi"
FLAGS="$FLAGS --prefix=$DEST"
mkdir -p $DEST
EXTRA_CFLAGS="$EXTRA_CFLAGS -I$ANDROID_SOURCE/frameworks/base/include -I$ANDROID_SOURCE/system/core/include"
EXTRA_CFLAGS="$EXTRA_CFLAGS -I$ANDROID_SOURCE/frameworks/base/media/libstagefright"
EXTRA_CFLAGS="$EXTRA_CFLAGS -I$ANDROID_SOURCE/frameworks/base/include/media/stagefright/openmax"
EXTRA_CFLAGS="$EXTRA_CFLAGS $MY_EXTRA_CFLAGS"
EXTRA_LDFLAGS="-Wl,--fix-cortex-a8 -L$ANDROID_LIBS -Wl,-rpath-link,$ANDROID_LIBS -lstagefright -lstagefright_foundation -lstdc++ -lutils -lbinder -lgnustl_shared"
EXTRA_CXXFLAGS="-Wno-multichar -fno-exceptions -fno-rtti"
echo $FLAGS --extra-cflags="$EXTRA_CFLAGS" --extra-ldflags="$EXTRA_LDFLAGS" --extra-cxxflags="$EXTRA_CXXFLAGS" > $DEST/info.txt
./configure $FLAGS --extra-cflags="$EXTRA_CFLAGS" --extra-ldflags="$EXTRA_LDFLAGS" --extra-cxxflags="$EXTRA_CXXFLAGS" | tee $DEST/configuration.txt
[ $PIPESTATUS == 0 ] || exit 1
make clean
make -j64 || exit 1
make install
make clean
done
FFmpeg在Android平台的使用
如果一切编译顺利,那么生成目录应该是这样的
out/
├── armeabi-v7a
│ ├── bin
│ ├── configuration.txt
│ ├── include
│ ├── info.txt
│ ├── lib
│ ├── out
│ └── share
└── x86
├── bin
├── configuration.txt
├── include
├── info.txt
├── lib
└── share
- bin目录里面是ffmpeg的demo(
ffmpeg
ffprobe
ffserver
) - include里面按照相应的库导出的相应的头文件
- lib里面是一个个独立的静态库
- share里面是man和ffmpeg的测试代码,可以利用这些测试demo来理一理这些库的使用方式
有些同学喜欢使用动态库,把这几个静态库编译为动态库即可。我个人比较喜欢静态库,好处有
1. 把用到的静态库全部打到我的动态库中,减少库的个数。
2. 只打包用到的库,精简库的大小,关于库的依赖关系往前看。
使用方式:
有了这些库和头文件以后我们就可以独立于ffmpeg开发了,以后要更新ffmpeg仅仅需要更新这些头文件和对应的库即可。
这里以armeabi-v7a为例,把Android.mk放在与lib目录同一路径,应该是这个样子的:
Android.mk Application.mk bin configuration.txt include info.txt lib out share
Android.mk的内容如下,然后在其他地方使用就直接使用库名来代替,添加在LOCAL_STATIC_LIBRARIES,已经把对应库的头文件export出来了,所以不需要单独添加对应库的头文件路径,这些都是Android NDK的基础知识了。
LOCAL_PATH := $(call my-dir)
LIB_STATIC_LIBS := $(wildcard $(LOCAL_PATH)/lib/*.a)
$(foreach lib, $(LIB_STATIC_LIBS), \
$(eval include $$(CLEAR_VARS)) \
$(eval LOCAL_MODULE := $(basename $(notdir $(lib)))) \
$(eval LOCAL_SRC_FILES := $(lib)) \
$(eval LOCAL_EXPORT_C_INCLUDE := include/$(basename $(lib))) \
$(eval include $$(PREBUILT_STATIC_LIBRARY)))
单独编译后应该是这个样子的:
[armeabi-v7a] Install : libavcodec.a => out/libs/armeabi-v7a/libavcodec.a
[armeabi-v7a] Install : libavfilter.a => out/libs/armeabi-v7a/libavfilter.a
[armeabi-v7a] Install : libavformat.a => out/libs/armeabi-v7a/libavformat.a
[armeabi-v7a] Install : libavutil.a => out/libs/armeabi-v7a/libavutil.a
[armeabi-v7a] Install : libswresample.a => out/libs/armeabi-v7a/libswresample.a
[armeabi-v7a] Install : libswscale.a => out/libs/armeabi-v7a/libswscale.a
目前遗留的疑问
由于我早先就基于libstagefright做过Android平台的硬编码,这个是与版本相关的才对,为啥它只需要一个版本的库和头文件呢(platform是10)?这点我很疑惑,vlc同样也用到了libavcodec,但是却有10~21的头文件和库,我早先的做法是参考vlc的思想。
更多推荐
所有评论(0)