这里我将详细记录我在Win7-32位平台下用Cygwin+NDK编译出跑在android系统上的FFmpeg+x264全过程。FFmpeg默认情况下只支持h264的解码,并不支持h264的编码,为了让FFmpeg支持对h264的编码功能,需要编译的时候加入x264这个扩展库的支持。本文的目的就是最终编译出一个能在android下运行,同时支持h264编码和解码的的FFmpeg。
在正式开始之前,先简单说一下几个重要的知识点:
1. 一个标准的Linux软件源码包的编译过程,当我们拿到某个Linux软件源码包的时候,一般是首先运行 configure ,这个文件其实是一个shell脚本,执行这个脚本最主要的目的是会输出另外一个名称为config.mak的文件,这个config.mak文件其实就是最为关键的一个文件,它被Makefile内部所引用,当你接下来利用make命令进行编译的时候,make命令会解析Makefile文件,而最终会解析到config.mak文件中,利用这个文件里面的各种配置信息进行编译工作。由此可见config.mak文件才是重点中的重点。
2. Cygwin为了在Windows下模拟Linux环境,有它自己的一套路径表示系统,比如说Windows下的路径C:\Users\,用Cygwin的表示方法是/cygdrive/c/Users/,这样看似非常完美,现实却很骨感,因为对于Windows版本的NDK来说它只认得Windows方式的路径,而不认识Cygwin方式的路径,不过想来也正常嘛,咱用的是Windows版本的NDK,又不是Cygwin版本的NDK。所以你如果在Cygwin下传入一个Cygwin方式的路径给NDK,那一定是坑爹的结果。
3. Linux符号链接文件在Linux系统中很常见,Cygwin当然也能很好支持,但是要注意的是Windows版本的NDK是不支持的,在Cygwin下当传给NDK的是一个Linux符号文件时,它并不会像Linux版本的NDK那样去找符号链接所指向的那个真实文件,而是会直接把符号链接文件当真实文件处理,结果可想而知,直接报错。谁怪咱们是Windows版本的NDK呢,当然不认识你Linux系统里面的符号链接文件啦。
4. Windows下的文本编辑工具一般都是遵从的DOS格式,意思就是说按回车后是0x0D0A,而Linux下的文本文件一般是遵从UNIX格式,按下回车是用0x0A表示的。Cygwin是模拟Linux环境,在它下面如果使用的是DOS格式的文本文件,比如shell脚本如果是DOS格式,就有可能发生一些奇怪错误,这个也是一定非常要注意的地方,还好有个小工具可以解决这个问题,就是dos2unix,当你用Windows下的编辑工具编辑过某个文本文件后,比较保险的办法就是在Cygwin下用dos2unix命令转换一下,确保万无一失。
有了上面的几个知识点后,下面开始正式讲解编译过程了,首先下载FFmpeg和x264的源码。
FFmpeg:
http://ffmpeg.org/
x264:
x264:
http://www.videolan.org/developers/x264.html
我这里下载的是ffmpeg-3.0.2版本,将它们都解压到同一个目录下。如图:
先编译x264,在x264的目录下建立一个shell文件,名称为build_android.sh,内容如下:
#!/bin/bash
NDK="E:\disk\D\android-ndk-r10e"
PLATFORM="E:\disk\D\android-ndk-r10e\platforms\android-9\arch-arm"
PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt
PREFIX=$(pwd)/android-lib
./configure --prefix=$PREFIX \
--enable-static \
--enable-pic \
--disable-asm \
--disable-cli \
--host=arm-linux \
--cross-prefix=$PREBUILT/windows/bin/arm-linux-androideabi- \
--sysroot=$PLATFORM
NDK="E:\disk\D\android-ndk-r10e"
PLATFORM="E:\disk\D\android-ndk-r10e\platforms\android-9\arch-arm"
PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt
PREFIX=$(pwd)/android-lib
./configure --prefix=$PREFIX \
--enable-static \
--enable-pic \
--disable-asm \
--disable-cli \
--host=arm-linux \
--cross-prefix=$PREBUILT/windows/bin/arm-linux-androideabi- \
--sysroot=$PLATFORM
里面的几个文件路径要根据实际情况改为实际路径。保存后最好用dos2unix转换下,然后在Cygwin下切换到这个目录下运行这个文件,顺利的话应该一切OK。如图:
这个时候 config.mak 已经在当前目录生成好了,这个时候可以运行make了,当然结果肯定出错了,如图:
明显是路径不对,我们可以打开config.mak 看看,里面的路径很混乱,有Cygwin风格,也有Windows风格,也有两种路径拼接在一起的风格,所以这里我们得耐心一点,把所有不对的路径都修改正确,具体怎么改呢,这里有一种实际测试很好用的改法,就是先把所有路径都先改成Windows风格的路径,然后把\都改成/,同时两边都用双引号包围起来,如:"C:/aaa/bbb/ccc" 这样。改好后,就能顺利编译通过了。最后make install 就能在android-lib得到最后想要的头文件和库文件。如图:
到此x264库已经编译好了,接下来编译FFmpeg。首先在FFmpeg目录下建立一个shell文件,名称为build_android.sh,内容如下:
#!/bin/bash
export NDK="E:\disk\D\android-ndk-r10e"
export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt
export PLATFORM="E:\disk\D\android-ndk-r10e\platforms\android-9\arch-arm"
export PREFIX=$(pwd)/fflib264
build_one(){
./configure --target-os=linux --prefix=$PREFIX \
--enable-cross-compile \
--enable-runtime-cpudetect \
--disable-asm \
--arch=arm \
--cc=$PREBUILT/windows/bin/arm-linux-androideabi-gcc \
--cross-prefix=$PREBUILT/windows/bin/arm-linux-androideabi- \
--disable-stripping \
--nm=$PREBUILT/windows/bin/arm-linux-androideabi-nm \
--sysroot=$PLATFORM \
--enable-gpl --enable-shared --disable-static --enable-nonfree --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=h264 --enable-decoder=mpeg4 \
--disable-demuxers --enable-demuxer=h264 --enable-demuxer=avi --enable-demuxer=mpc --enable-demuxer=mov \
--disable-parsers --enable-parser=h264 \
--disable-protocols --enable-protocol=file \
--disable-bsfs --enable-bsf=h264_mp4toannexb \
--disable-indevs --enable-zlib \
--disable-outdevs --disable-ffprobe --disable-ffplay --disable-ffmpeg --disable-ffserver --disable-debug \
--extra-cflags="-I ../x264-snapshot-20160521-2245/android-lib/include -fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a" \
--extra-ldflags="-L ../x264-snapshot-20160521-2245/android-lib/lib"
}
build_one
export NDK="E:\disk\D\android-ndk-r10e"
export PREBUILT=$NDK/toolchains/arm-linux-androideabi-4.8/prebuilt
export PLATFORM="E:\disk\D\android-ndk-r10e\platforms\android-9\arch-arm"
export PREFIX=$(pwd)/fflib264
build_one(){
./configure --target-os=linux --prefix=$PREFIX \
--enable-cross-compile \
--enable-runtime-cpudetect \
--disable-asm \
--arch=arm \
--cc=$PREBUILT/windows/bin/arm-linux-androideabi-gcc \
--cross-prefix=$PREBUILT/windows/bin/arm-linux-androideabi- \
--disable-stripping \
--nm=$PREBUILT/windows/bin/arm-linux-androideabi-nm \
--sysroot=$PLATFORM \
--enable-gpl --enable-shared --disable-static --enable-nonfree --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=h264 --enable-decoder=mpeg4 \
--disable-demuxers --enable-demuxer=h264 --enable-demuxer=avi --enable-demuxer=mpc --enable-demuxer=mov \
--disable-parsers --enable-parser=h264 \
--disable-protocols --enable-protocol=file \
--disable-bsfs --enable-bsf=h264_mp4toannexb \
--disable-indevs --enable-zlib \
--disable-outdevs --disable-ffprobe --disable-ffplay --disable-ffmpeg --disable-ffserver --disable-debug \
--extra-cflags="-I ../x264-snapshot-20160521-2245/android-lib/include -fPIC -DANDROID -D__thumb__ -mthumb -Wfatal-errors -Wno-deprecated -mfloat-abi=softfp -marm -march=armv7-a" \
--extra-ldflags="-L ../x264-snapshot-20160521-2245/android-lib/lib"
}
build_one
保存后运行,结果出错。如图:
从这里看不出具体的错误,具体的错误记录在config.log文件中,打开config.log文件,查看错误日志,发现又是路径导致的错误。如图:
既然知道错误原因,马上修改,打开configure这个脚本文件,将临时目录直接改成当前目录,问题搞定,按如下方式改:
# set temporary file name
#: ${TMPDIR:=$TEMPDIR}
#: ${TMPDIR:=$TMP}
#: ${TMPDIR:=/tmp}
: ${TMPDIR:=.}
#: ${TMPDIR:=$TEMPDIR}
#: ${TMPDIR:=$TMP}
#: ${TMPDIR:=/tmp}
: ${TMPDIR:=.}
至此configure脚本已经能顺利运行了,不过在运行之前还要修改一处,如下:
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_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_VERSION)'
SLIB_INSTALL_LINKS='$(SLIBNAME_WITH_MAJOR) $(SLIBNAME)'
替换为:
SLIBNAME_WITH_MAJOR='$(SLIBPREF)$(FULLNAME)-$(LIBMAJOR)$(SLIBSUF)'
LIB_INSTALL_EXTRA_CMD='$$(RANLIB)"$(LIBDIR)/$(LIBNAME)"'
SLIB_INSTALL_NAME='$(SLIBNAME_WITH_MAJOR)'
SLIB_INSTALL_LINKS='$(SLIBNAME)'
原因是android平台不认 libavcodec.so.5.100.1这样的命名方式。
现在可以运行build_android.sh了,等一段时间后, config.mak就顺利生成了。如图:
既然config.mak已经生成好了,那根据之前的经验接下来就是一个个修改config.mak中的各种路径了,此时需要的就是耐心和细心。当路径都修改好后,就可以运行make开始编译了。
一开始会一切正常,但是到快结束的时候还是会碰到错误,截图如下:
这个错误其实就是前面说的,Windows版本的NDK不认识Linux的符号链接文件引起的。我们可以进入到libavutil目录下面看看,其中的libavutil.so其实只是个符号链接文件,指向真实的文件libavutil-55.so。如图所示:
解决这个问题很简单,就是把这个符号链接文件删除,把真实的文件拷贝一份出来,改成这个符号文件的名字。这样就可以顺利继续了。然后等到处理另外一个so的时候,又会报同样的错误,这个时候就再用同样的办法解决,会生成几个so文件,就会出现几次错误,每次都得手动修改,比较麻烦,但终归能编译完成。暂时还没想到有什么一劳永逸的办法。
make完成以后运行make install,最后就会在fflib264目录下生成最终的头文件和库文件。如图:
终于顺利搞定了,在这个过程中,碰到的最主要的几个问题就是文章一开始说的那几个,Cygwin和NDK它们对路径的认知不同,然后就是Windows版本的NDK对很多Linux特性不认识,比如Linux符号链接等等。
最后可以正确编译的代码已经上传到了github上,另外还加入了一个android的演示demo,支持h264编码和解码。