背景:需要做一个Android端的视频转码so库使用,需要用到FFmpeg开源库,并且按需求集成进X264和vo-aacenc两个静态库,随后就走上了不断的踩坑和爬坑之路,现在特地记录一下,以供将来脑子不好的时候有个回想的根据。
构建环境
系统:Ubuntu16.04_64位(是的,还是Mac上跑的虚拟机,网上有太多的Mac构建FFmpeg的过程,然后爬了个把星期都不能用,有哪位大神有靠谱点的教程欢迎分享);
NDK:android-ndk-r9d(不要问我为什么不用最新的,都是泪T T);
FFmpeg:2.0.7版本,根据官方的Release Note发现3.0以上版本已经不再支持vo-aacenc了,所以下了2.0.X的版本;
vo-aacenc:0.1.3版本,是的,最新版本还是2013-07-27发布的,就是说该库已经光荣退休了,有条件的推荐使用fdk-aac尝试编译一下;
X264:最新的就好,没啥要求。
过程
过程无非就是写编译脚步,调编译脚本的过程,刚学习写这玩意,过程时异常曲折。首先从各自网站上下载FFmpeg、X264和vo-aacenc,然后把压缩包解压后放到同一个目录下,不如说将x264以及vo-aacenc都放在FFmpeg解压出来的文件夹下。这样的好处是少写几个代码,整个编译过程可以放在同一个脚本中运行。顺序是按照x264\vo-aacenc\ffmpeg的先后编译,因为FFmpeg编译的时候需要用到前面两个已经生成好的静态库。主要的脚本编译放到后面去说。
坑
期间真的遇到了无数的坑,差点都要放弃了,(╯°Д°)╯︵ ┻━┻
主要摘了一些典型的记录一下:
1.脚本运行中出现No such file or directory VS no such command
别笑,还真是这种问题折磨的死去活来死去,因为脚本里面要配置的东西太多了,ToolChains的路径又要配置好几次,稍不留神写错一个路径,编译就无法成功,这个时候千万淡定,看看脚本命令行之间是不是都用\分隔着,路径有没有出错,最好打印一下,echo $PATH
之类的。
2.In file included from libavcodec/x86/mpegvideoenc.c:81:0: libavcodec/x86/mpegvideoenc_template.c: In function 'dct_quantize_SSSE3': libavcodec/x86/mpegvideoenc_template.c:144:9: error: 'asm' operand has impossible constraints __asm__ volatile( libavcodec/x86/mpegvideoenc_template.c:179:9: error: 'asm' operand has impossible constraints __asm__ volatile( CC libavcodec/x86/videodsp_init.o CC libpostproc/postprocess.o common.mak:48: recipe for target 'libavcodec/x86/mpegvideoenc.o' failed make: *** [libavcodec/x86/mpegvideoenc.o] Error 1 make: *** Waiting for unfinished jobs....
典型错误,去掉脚本中的--enable-asm
3.undefined reference to "__umodsi3"
undefined reference to "__udivdi3"
在脚本中没有指定连接的GCC库,添加-lgcc,多说一句,-lgcc代表链接器将连接GCC的支持库libgcc.a,-lm代表链接器将连接GCC的标准数学库libm.a,-lc代表链接器将连接GCC的标准C库libc.a。
4.诸如C compiler not found
之类的错误就是你的GCC路径没设对了。
构建脚本
最后,贴一下构建的脚本(这个虽然是现成的,但是思想和踩坑经过才是最重要的!)
再啰嗦一句,标题上写的是构建基于X86和armeabi的,那么为什么不加上arm64呢?对于ARM64架构的so库,目前无法编译,因为arm64的GCC版本于Android-21(5.0.1-2014年发布)才开始支持,而FFmpeg中的AAC编解码的开源模块于2013年就已停止维护,官方文档也说明该库不支持ARM64的架构。话又说回来,64位也是兼容32位的静态库的,只不过性能上就要牺牲一下了。MIPS的毕竟太少太少,就不折腾了。
贴一下X86的运行脚本,armeabi的稍微换一下就好了
#!/bin/bash
function setup_paths
{
export PLATFORM=$NDKROOT/platforms/$PLATFORM_VERSION/arch-$ARCH
if [ ! -d $PLATFORM ]; then
echo $PLATFORM does not exist
exit 1
fi
echo "Using platform: $PLATFORM"
export PATH=${PATH}:$PREBUILT/bin/
export CROSS_COMPILE=$PREBUILT/bin/$EABIARCH-
export CFLAGS=$OPTIMIZE_CFLAGS
export CPPFLAGS="$CFLAGS"
export CFLAGS="$CFLAGS"
export CXXFLAGS="$CFLAGS"
export CXX="${CROSS_COMPILE}g++ --sysroot=$PLATFORM -m32"
export AS="${CROSS_COMPILE}gcc --sysroot=$PLATFORM"
export CC="${CROSS_COMPILE}gcc --sysroot=$PLATFORM -m32"
export PKG_CONFIG="${CROSS_COMPILE}pkg-config"
export LD="${CROSS_COMPILE}ld"
export NM="${CROSS_COMPILE}nm"
export STRIP="${CROSS_COMPILE}strip"
export RANLIB="${CROSS_COMPILE}ranlib"
export AR="${CROSS_COMPILE}ar"
export LDFLAGS="-Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib -lm -lc -lgcc -ldl -llog"
export PKG_CONFIG_LIBDIR=$PREFIX/lib/pkgconfig/
export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig/
if [ ! -f "${CROSS_COMPILE}gcc" ]; then
echo "Gcc does not exists in path: ${CROSS_COMPILE}gcc"
exit 1;
fi
if [ ! -f "${PKG_CONFIG}" ]; then
echo "Pkg config does not exists in path: ${PKG_CONFIG} - Probably BUG in NDK but..."
set +e
SYS_PKG_CONFIG=$(which pkg-config)
if [ "$?" -ne 0 ]; then
echo "This system does not contain system pkg-config, so we can do anything"
exit 1
fi
set -e
cat > $PKG_CONFIG << EOF
#!/bin/bash
pkg-config \$*
EOF
chmod u+x $PKG_CONFIG
echo "Because we have local pkg-config we will create it in ${PKG_CONFIG} directory using ${SYS_PKG_CONFIG}"
fi
}
function build_x264_x86
{
echo "Starting build x264 for $ARCH"
cd x264
./configure \
--prefix=$PREFIX \
--host=$ARCH-linux \
--enable-static \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make -j4 install
make clean
cd ..
echo "finished x264 for $ARCH"
}
function build_aac_x86
{
echo "Starting build aac for $ARCH"
cd vo-aacenc-0.1.3
./configure \
--prefix=$PREFIX \
--host=$ARCH-linux \
--disable-dependency-tracking \
--disable-shared \
--enable-static \
--with-pic \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make -j4 install
make clean
cd ..
echo "Finished aac for $ARCH"
}
function build_ffmpeg_x86
{
echo "Starting build ffmpeg-x86 for $ARCH"
./configure --target-os=linux \
--prefix="$PREFIX" \
--enable-cross-compile \
--enable-runtime-cpudetect \
--arch=$ARCH \
--cc=$PREBUILT/bin/$EABIARCH-gcc \
--cross-prefix=$PREBUILT/bin/$EABIARCH- \
--disable-stripping \
--nm=$PREBUILT/bin/$EABIARCH-nm \
--sysroot=$PLATFORM \
--extra-cflags=" -O3 -fpic -DANDROID -DHAVE_SYS_UIO_H=1 -Dipv6mr_interface=ipv6mr_ifindex -fasm -Wno-psabi -fno-short-enums -fno-strict-aliasing -finline-limit=300 $OPTIMIZE_CFLAGS " \
--extra-ldflags="-Wl,-rpath-link=$PLATFORM/usr/lib -L$PLATFORM/usr/lib -nostdlib -lc -lm -ldl -llog -L$PREFIX/lib" \
--extra-cflags="-I$PREFIX/include" \
--extra-libs="-lgcc" \
--enable-static \
--disable-shared \
--disable-yasm \
--disable-amd3dnow \
--enable-version3 \
--enable-gpl \
--enable-libvo-aacenc \
--enable-libx264 \
--disable-everything \
--enable-parser=h263 \
--enable-parser=mpeg4video \
--enable-demuxer=mov \
--enable-decoder=amrnb \
--enable-decoder=amrwb \
--enable-decoder=h263 \
--enable-decoder=aac \
--enable-decoder=h264 \
--enable-decoder=mpeg4 \
--enable-encoder=mjpeg \
--enable-encoder=libvo_aacenc \
--enable-encoder=libx264 \
--enable-muxer=mp4 \
--enable-muxer=mov \
--enable-protocol=file \
--disable-ffmpeg \
--disable-ffplay \
--disable-ffprobe \
--disable-ffserver \
--disable-network \
--disable-swscale-alpha \
--disable-debug \
--disable-logging \
--enable-small \
$ADDITIONAL_CONFIGURE_FLAG
make clean
make -j4 install
make clean
echo "Finished ffmpeg_x86 for $ARCH"
}
NDKROOT=/home/young/Documents/android-ndk-r9d
EABIARCH=i686-linux-android
ARCH=x86
OPTIMIZE_CFLAGS="-m32 -march=i686"
PREBUILT=$NDKROOT/toolchains/$ARCH-4.8/prebuilt/linux-x86_64
PREFIX=/home/young/Documents/FFmpeg3.1.2/output_android
PLATFORM_VERSION=android-14
ADDITIONAL_CONFIGURE_FLAG=--disable-asm
setup_paths
build_x264_x86
build_aac_x86
build_ffmpeg_x86
脚本中用的都是绝对路径,现实中使用的话需要都改成当前电脑上的配置路径。PREFIX是静态库的输出文件夹,setup_paths方法用于配置脚本的各种设置,后面的几个方法就是依次构建的过程。具体的配置命令的意思可以通过在本地包含configure的目录下使用./configure --help
的命令查询。
总结一点就是胆大心细,多Google!