FFmpeg Android平台编译与使用

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关键配置文件

  1. config.mak:此配置文件是执行configure时自动生成的,此配置文件定义了编译平台、目标平台、编译工具链、编译器的配置信息、链接器的配置信息、安装目录、库的前缀和后缀名、关键库的版本号及依赖关系、指定需要编译的库和功能(例如要不要把h263编译进去)。此配置文件是根据configure的参数来自动生成的,也就是动态的。
  2. arch.mak:根据CPU架构使用与架构相关的特性、也就是针对特定平台做的优化(一般来说就是汇编优化,arm的neon等)。
  3. common.mak:此配置文件是规定了中间文件、库的编译规则。
  4. 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;
 }

关键变量

  1. APP_OUT:定义安装的主目录,最终的安装目录还会根据具体的架构而定。
  2. APP_ABI:定义生成的架构。
  3. APP_DEBUG:定义是否生成debug版本的库。
#------------------------------------------------------------------------
# File Name: build_libstagefright
# Author: liuliang
# Mail: [email protected]
# 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
  1. bin目录里面是ffmpeg的demo(ffmpeg ffprobe ffserver)
  2. include里面按照相应的库导出的相应的头文件
  3. lib里面是一个个独立的静态库
  4. 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的思想。

你可能感兴趣的:(媒体)