Mac下为Android编译FFMPEG和x264 (一)

May 25, 2016

前言

ffmpeg是一个超级牛逼的音视频软件,它几乎可以处理市面上常见的所有音视频格式,许多著名的软件都是基于ffmpeg的,比如kmplayer之类的播放器,格式工厂之类的格式转换软件等。并且ffmpeg可以运行在多种平台上,其中就包括了我们的Android平台。

1. 下载ffmpeg和x264

下载自然不必多说,到这个链接http://ffmpeg.org/download.html,在右下角的Get the Sources中,有Download Snapshotgit snapshot,如果你想通过git来更新代码,那就点击git snapshot,如果不需要则选择Download Snapshot。当然你也可以选择直接git clone的方式下载代码,带git的代码库有100多M,还是比较大的。

然后到这里http://www.videolan.org/developers/x264.html下载最新的x264代码,同样可以下载压缩包或者直接git clone。

2. 配置

首先,确保你已经安装了ndk,如果没有安装,到这里下载最新版:https://developer.android.com/ndk/downloads/index.html。下载完后解压到一个任意目录下。

由于ffmpeg默认的编译产物so文件后面会加上版本号,我们可以首先把编译产物命名修改成libxxxxx-.so以及libxxxxx.a

打开ffmpeg目录下的configure文件,找到如下一段:

SLIBNAME_WITH_VERSION='$(SLIBNAME).$(LIBVERSION)'
SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'

改成如下(注意我注释掉的行):

SLIBNAME_WITH_VERSION='$(SLIBNAME).$(LIBVERSION)'
# SLIBNAME_WITH_MAJOR='$(SLIBNAME).$(LIBMAJOR)'
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'  
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
# SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
# SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'

接下来就是配置x264和ffmpeg了,这里我把x264放入了ffmpeg的目录,目录结构如下

├── ffmpeg
    ├── x264
    └── others....

在ffmpeg下新建一个文件 build_x264.sh,内容如下:

cd x264
export PLATFORM_VERSION=android-24
export ANDROID_NDK=/Users/junyuecao/Work/adt-bundle-mac-x86_64-20140702/sdk/ndk-bundle #ndk 目录根据你的安装目录
export TOOLCHAIN=../../fftoolchain #toolchain 安装目录
export SYSROOT=$TOOLCHAIN/sysroot/
export PLATFORM=$ANDROID_NDK/platforms/$PLATFORM_VERSION/arch-arm
export PREFIX=../android-lib #编译结果的目录

#生成toolchain目录,这一段可以在第一次运行后注释掉
$ANDROID_NDK/build/tools/make-standalone-toolchain.sh \
    --toolchain=arm-linux-androideabi-4.9 \
    --platform=$PLATFORM_VERSION --install-dir=$TOOLCHAIN 

#
./configure \
    --prefix=$PREFIX \
    --enable-static \
    --enable-shared \
    --enable-pic \
    --disable-asm \
    --disable-cli \
    --host=arm-linux \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --sysroot=$PLATFORM

make -j8
make install

cd ..

在ffmpeg下新建一个文件 build_ffmpeg_with_x264.sh,内容如下:

cd x264
export WD=/Users/junyuecao/Work/Misc/ffmpeg
export PLATFORM_VERSION=android-24
export ANDROID_NDK=/Users/junyuecao/Work/adt-bundle-mac-x86_64-20140702/sdk/ndk-bundle #ndk 目录根据你的安装目录
export TOOLCHAIN=$WD/../fftoolchain #toolchain 安装目录
export SYSROOT=$TOOLCHAIN/sysroot/
export PLATFORM=$ANDROID_NDK/platforms/$PLATFORM_VERSION/arch-arm
export PREFIX=$WD/android-lib #编译结果的目录
export OUT_PREFIX=$WD/../264fflib

#生成toolchain目录,这一段可以在第一次运行后注释掉
$ANDROID_NDK/build/tools/make-standalone-toolchain.sh \
    --toolchain=arm-linux-androideabi-4.9 \
    --platform=$PLATFORM_VERSION --install-dir=$TOOLCHAIN 

#
./configure \
    --prefix=$PREFIX \
    --enable-static \
    --enable-shared \
    --enable-pic \
    --disable-asm \
    --disable-cli \
    --host=arm-linux \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --sysroot=$PLATFORM

make -j8
make install

cd ..

# 加入x264编译库
EXTRA_CFLAGS="-I./android-lib/include" 
EXTRA_LDFLAGS="-L./android-lib/lib"


./configure \
    --target-os=linux \
    --prefix=$OUT_PREFIX \
    --enable-cross-compile \
    --enable-runtime-cpudetect \
    --disable-asm \
    --disable-doc \
    --arch=arm \
    --cc=$TOOLCHAIN/bin/arm-linux-androideabi-gcc \
    --cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
    --disable-stripping \
    --nm=$TOOLCHAIN/bin/arm-linux-androideabi-nm \
    --sysroot=$PLATFORM \
    --enable-gpl \
    --enable-static \
    --disable-shared \
    --enable-version3 \
    --enable-small \
    --disable-vda \
    --disable-iconv \
    --disable-encoders \
    --enable-libx264 \
    --enable-encoder=libx264 \
    --disable-muxers \
    --enable-muxer=mov \
    --enable-muxer=ipod \
    --enable-muxer=psp \
    --enable-muxer=mp4 \
    --enable-muxer=avi \
    --disable-decoders \
    --enable-decoder=aac \
    --enable-decoder=aac_latm \
    --enable-decoder=h264 \
    --enable-decoder=mpeg4 \
    --disable-demuxers \
    --enable-demuxer=h264 \
    --enable-demuxer=mov \
    --disable-parsers \
    --enable-parser=aac \
    --enable-parser=ac3 \
    --enable-parser=h264 \
    --disable-protocols \
    --enable-protocol=file \
    --enable-protocol=rtmp \
    --disable-bsfs \
    --enable-bsf=aac_adtstoasc \
    --enable-bsf=h264_mp4toannexb \
    --disable-indevs \
    --enable-zlib \
    --disable-outdevs \
    --disable-ffprobe \
    --disable-ffplay \
    --disable-ffmpeg \
    --disable-ffserver \
    --disable-debug \
    --extra-cflags=$EXTRA_CFLAGS \
    --extra-ldflags=$EXTRA_LDFLAGS


make clean 
make -j8
make install

# 这段解释见后文
$TOOLCHAIN/bin/arm-linux-androideabi-ld -rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -L$OUT_PREFIX/lib -soname libffmpeg.so -shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o $OUT_PREFIX/libffmpeg.so \
    android-lib/lib/libx264.a \
    libavcodec/libavcodec.a \
    libavfilter/libavfilter.a \
    libswresample/libswresample.a \
    libavformat/libavformat.a \
    libavutil/libavutil.a \
    libswscale/libswscale.a \
    libpostproc/libpostproc.a \
    libavdevice/libavdevice.a \
    -lc -lm -lz -ldl -llog --dynamic-linker=/system/bin/linker $TOOLCHAIN/lib/gcc/arm-linux-androideabi/4.9.x/libgcc.a   

这里有两种选择:

  1. 打包出一个ffmpeg.so文件,上面的配置就是这样的结果
  2. 每个模块打包出单独的so,那么需要对上面的配置文件做两件事:1.去掉最后一段配置(make install 后面的),2.将configure选项–enable-static –disable-shared 反过来–enable-shared –disable-static

另外在这段配置中我去掉了很多编码解码组件,只保留了我需要的组件,大家可以视情况而定,增减配置。

编译结果

最终编译结果在ffmpeg的编译脚本里的OUT_PREFIX=../264fflib所定义的路径,对于上面说的两种方式产生的编译产物分别如下。 第一种:

.
├── include
│   ├── libavcodec
│   ├── libavdevice
│   ├── libavfilter
│   ├── libavformat
│   ├── libavutil
│   ├── libpostproc
│   ├── libswresample
│   └── libswscale
├── lib
│   ├── libavcodec.a
│   ├── libavdevice.a
│   ├── libavfilter.a
│   ├── libavformat.a
│   ├── libavutil.a
│   ├── libpostproc.a
│   ├── libswresample.a
│   ├── libswscale.a
│   └── pkgconfig
└── libffmpeg.so

只需要在Android中引入libffmpeg.so就可以使用了。

第二种:

.
├── include
│   ├── libavcodec
│   ├── libavdevice
│   ├── libavfilter
│   ├── libavformat
│   ├── libavutil
│   ├── libpostproc
│   ├── libswresample
│   └── libswscale
└── lib
    ├── libavcodec-57.so
    ├── libavcodec.so -> libavcodec-57.so
    ├── libavdevice-57.so
    ├── libavdevice.so -> libavdevice-57.so
    ├── libavfilter-6.so
    ├── libavfilter.so -> libavfilter-6.so
    ├── libavformat-57.so
    ├── libavformat.so -> libavformat-57.so
    ├── libavutil-55.so
    ├── libavutil.so -> libavutil-55.so
    ├── libpostproc-54.so
    ├── libpostproc.so -> libpostproc-54.so
    ├── libswresample-2.so
    ├── libswresample.so -> libswresample-2.so
    ├── libswscale-4.so
    ├── libswscale.so -> libswscale-4.so
    └── pkgconfig

这个方式出来的话,就需要把每一个so文件都引用一遍了。

备注

我在编译单个so过程中遇到一个出线undefined reference to 'x264_picture_init'的错误,是因为上面配置文件最后一段没有加入android-lib/lib/libx264.a 静态文件的引用导致找不到x264库的函数,这还是由于我对C语言的编译链接过程以及动态库和静态库之间的区别理解太浅。这里将静态库合并成一个so文件的过程中需要保证每一个函数都能够找到,不像动态链接库,可以在需要时再去加载库文件。

接下来

后一篇文章