Android/NDK环境下FFmpeg及AAC,MP3,X264的编译

本篇介绍在Android/Ndk环境下FFmpeg的编译及使用, FFmpeg自带了H264、AAC、MP3的解码器,但却没有(或没有好的)相应的编码器。相应的编码器需要使用第三方库。推荐使用的第三方库为x264(H264编码) 、FDK_AAC(AAC编码),lame(MP3编码)。

在顺序上,应该先编译好第三方库,最后再编译FFmpeg库。

【本书说明:本文作者:邵发,本文选自《FFmpeg视音频编程指南》。有关本书的详细信息请访问官网:http://www.afanihao.cn 】

【权利声明:作者保留本文的全部权利。作者授权任何人都可以自由转载本文,但转载时必须遵守以下限制:①转载时必须全文转载,不得有任何修改,包括“权利声明”和“本书说明”部分 ② 仅限于网络转载,即最终结果公布于网络上。凡是不遵守以上两条的转载行为视为侵权行为。除非本人允许,任何人不得将本文内容用于任何的其他用途。】

1.1        NDK环境的准备

本篇的演示示例使用的是android-ndk-r8e版本,但理论上也适用于更新的NDK版本。为了让你的编译过程极其顺利,应该对NDK作以下改动:

进入目录 platforms/android-14/arch-arm/usr/lib

应该可以看到一些文件,例如 crtbegin_dynamic.o ,crtbegin_static.o  crtend_so.o , crtbegin_so.o ,crtend_android.o,等等。

 

把这个目录下的所有文件拷贝到

\toolchains\arm-linux-androideabi-4.6\prebuilt\linux-x86\lib\gcc\arm-linux-androideabi\4.6

 

否则在链接的时候ld会提示“找不到crtbegin_so.o和crtend_so.o”的错误。

 

应该把NDK的相关工具都把加PATH环境变量中,例如,

#!/bin/sh

export PATH=$PATH:/opt/ndk/android-ndk-r8e/

export PATH=$PATH: /opt/ndk/android-ndk-r8e/toolchains/arm-linux-androideabi-4.6/prebuilt/windows/bin/

 

1.2        FDK_AAC库的编译

如果你不打算使用AAC编码的功能,则可以略过本节。目前(2015年3月),fdk_aac的官方发布地址是在source_forge上,也许以后会变,作者可以自行搜索。目前地址为:

http://sourceforge.net/projects/opencore-amr/files/fdk-aac/

本文使用的版本是fdk-aac-0.1.3,为减少不必要的麻烦,读者也请下载这个包。

 

1.2.1         修改configure脚本

修改脚本的目的是去除目标库的版本号,以适应Android/NDK的对库的加载要求。简单地讲,这是因为在Android/NDK项目的java代码中,使用System.loadLibrary()函数来加载so文件时,动态库是不能加版本号的。

在fdk_aac的configure脚本里找到如下的行:

# This must be glibc/ELF.

linux* | k*bsd*-gnu | kopensolaris*-gnu | gnu*)

  version_type=linux # correct to gnu/linux during the next big refactor

  need_lib_prefix=no

  need_version=no

  library_names_spec=‘${libname}${shared_ext}   ${libname}${shared_ext}   $libname${shared_ext}‘

  soname_spec=‘${libname}${shared_ext}‘

  finish_cmds=‘PATH="\$PATH:/sbin" ldconfig -n $libdir‘

  shlibpath_var=LD_LIBRARY_PATH

  shlibpath_overrides_runpath=no

 

把粗体字部分替换为:

library_names_spec=‘$libname${shared_ext} $libname.a‘

 

1.2.2         新建配置脚本

在源码的根目录下建议一个myconfig.sh脚本。这是一个SHELL脚本,要求读者自己稍微了解一些SHELL脚本的写法。在这里只强调一点:SHELL脚本是以\n结尾的,如果你是在windows下面编辑的,那么应该用dos2unix来把文本的换行符转成unix格式。

为了避免不必要的麻烦,把NDK解压缩在/opt/ndk/android-ndk-r8e目录,并将FDK的输出目录--prefix设置在/opt/ndk/openlib目录下。读者第一遍操作时,不要擅自修改目录,必须在熟练掌握之后再自行修改目录。

myconfig.sh里面内容为:

#!/bin/sh

 

export CC=arm-linux-androideabi-gcc

export CXX=arm-linux-androideabi-g++

export AR=arm-linux-androideabi-ar

export LD=arm-linux-androideabi-ld

export AS=arm-linux-androideabi-gcc

 

export CFLAGS=" --sysroot=/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 "

export CXXFLAGS=" --sysroot=/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 "

 

export LDFLAGS=" -L/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm/usr/lib  -march=armv7-a -Wl,--fix-cortex-a8 "

 

./configure --enable-static --prefix=/opt/ndk/openlib --host=arm-linux

 

1.2.3         编译

如果你严格按照以上的操作进行,那么编译过程会非常简单。Linux下的开源软件一般都是分为三步编译 configure , make,make install,而fdk_aac也不例外。

1)   ./myconfig.sh

执行上述配置脚本,开始配置

2)  make

3)  make  install

一切正常,将在/opt/ndk/openlib下存放输出库及头文件,注意这个库是去掉版本号的libfdk-aac。显然,Android/NDK的程序员会更喜欢不带版本号的库。

 

1.3        LAME-MP3的编译

如果你不打算在项目中使用mp3编码,则略过本节。

本节介绍lame的编译,但遗憾的是,其动态库没有办法去掉版本号。所以本方法生成的目标动态库是带版本号的。

1.3.1         新建配置脚本

新建配置文件myconfig.sh,内容如下,

#!/bin/sh

 

export CC=arm-linux-androideabi-gcc

export CXX=arm-linux-androideabi-g++

export AR=arm-linux-androideabi-ar

export LD=arm-linux-androideabi-ld

export CFLAGS=" --sysroot=/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm "

export LDFLAGS=" -L/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm/usr/lib  "

./configure --enable-static --prefix=/opt/ndk/openlib --host=arm-linux

 

1.3.2         编译

1)   ./myconfig.sh

执行上述配置脚本,开始配置

2)  make

3)  make  install

一切正常,将在/opt/ndk/openlib下存放输出库及头文件。

 

1.4        x264的编译

x264属于VLC的一部分,当前的源码位置为:

ftp://ftp.videolan.org/pub/videolan/x264/snapshots/

本篇使用的源码版本为x264-snapshot-20140723-2245-stable.tar.bz2

1.4.1         修改configure脚本

这一步的目的是使输出的库文件不带版本号。如果你坚持认为版本号不影响你的开发,则忽略本小节。

找到这一位置:

    else

        echo "SOSUFFIX=so" >> config.mak

        echo "SONAME=libx264.so.$API" >> config.mak

        echo "SOFLAGS=-shared -Wl,-soname,\$(SONAME) $SOFLAGS" >> config.mak

    fi

 

将粗体字修改为:(即去掉$API后缀)

echo "SONAME=libx264.so" >> config.mak

 

1.4.2         新建配置脚本

新建myconfig.sh配置脚本,

#!/bin/sh

export CC=arm-linux-androideabi-gcc

export CXX=arm-linux-androideabi-g++

export AR=arm-linux-androideabi-ar

export LD=arm-linux-androideabi-ld

export AS=arm-linux-androideabi-gcc

export CFLAGS=" --sysroot=/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16"

export LDFLAGS=" -L/opt/ndk/android-ndk-r8e/platforms/android-14/arch-arm/usr/lib -march=armv7-a -Wl,--fix-cortex-a8 "

./configure --enable-static --enable-shared --prefix=/opt/ndk/openlib --host=arm-linux 

 

1.4.3         编译

1)   ./myconfig.sh

执行上述配置脚本,开始配置

2)  make

3)  make  install

 

1.5        FFmpeg的编译

本示例使用的是FFmpeg2.1.3版本,可以从其官网ffmpeg.org上下载。如果不需要x264/aac/lame,可以直接进行本节的编译。

1.5.1         修改configure脚本

本节的目的是让输出的库不含有版本号。

先找到configure的如下位置:

SHFLAGS=‘-shared -Wl,-soname,$$(@F)‘

LIBPREF="lib"

LIBSUF=".a"

FULLNAME=‘$(NAME)$(BUILDSUF)‘

LIBNAME=‘$(LIBPREF)$(FULLNAME)$(LIBSUF)‘

SLIBPREF="lib"

SLIBSUF=".so"

SLIBNAME=‘$(SLIBPREF)$(FULLNAME)$(SLIBSUF)‘

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)‘

SLIBNAME_WITH_MAJOR=‘$(SLIBNAME)‘

LIB_INSTALL_EXTRA_CMD=‘$$(RANLIB) "$(LIBDIR)/$(LIBNAME)"‘

SLIB_INSTALL_NAME=‘$(SLIBNAME_WITH_VERSION)‘

SLIB_INSTALL_LINKS=

 

1.5.2         修改配置脚本

新建myconfig.sh

export NDK_ROOT=/opt/ndk

export TMPDIR=/tmp

./configure  --prefix=$NDK_ROOT/ffmpeg --sysroot=$NDK_ROOT/android-ndk-r8e/platforms/android-14/arch-arm  --cross-prefix=arm-linux-androideabi-  --target-os=linux --arch=arm   --extra-cflags="-I$NDK_ROOT/openlib/include -fPIC  " --extra-ldflags=-L$NDK_ROOT/openlib/lib   --disable-programs --disable-doc --enable-shared --enable-cross-compile  --enable-gpl --enable-nonfree --enable-libx264 --enable-libmp3lame --enable-libfdk-aac

 

这个脚本中是把x264, fdk-aac, lame全部编译上了,你可以视自己的情况来修改。

1.5.3         编译

1)   ./myconfig.sh

执行上述配置脚本,开始配置

2)  make

3)  make  install

 

1.6        在Android.mk中引用FFmpeg库

1.6.1         使用静态库

推荐使用静态库。需要注意,FFmpeg库的静态库引用顺序是不能随便写的,为了避免不必要的麻烦,请照抄以下配置。

在你的Android.mk中添加以下几行(见黑体字部分)

LOCAL_LDLIBS    += -llog -lz

 

# ffmpeg

FFMPEG=/opt/ndk

LOCAL_CFLAGS += -I$(FFMPEG)/include

LOCAL_LDLIBS += -l$(FFMPEG)/lib/libavformat.a

LOCAL_LDLIBS += -l$(FFMPEG)/lib/libavcodec.a

LOCAL_LDLIBS += -l$(FFMPEG)/lib/libavdevice.a

LOCAL_LDLIBS += -l$(FFMPEG)/lib/libswresample.a

LOCAL_LDLIBS += -l$(FFMPEG)/lib/libavdevice.a

LOCAL_LDLIBS += -l$(FFMPEG)/lib/libswscale.a

LOCAL_LDLIBS += -l$(FFMPEG)/lib/libpostproc.a

LOCAL_LDLIBS += -l$(FFMPEG)/lib/libavutil.a

 

# fdk_aac

LOCAL_LDLIBS += -l/opt/ndk/lib/libfdk-aac.a

LOCAL_LDLIBS += -l/opt/ndk/lib/libmp3lame.a

LOCAL_LDLIBS += -l/opt/ndk/lib/ libx264.a

 

include $(BUILD_SHARED_LIBRARY)

 

1.6.2         使用动态库

不推荐使用动态库,此过程过于复杂。要注意的有两点:①编译的时候,必须去除库的版本号 ② 在Java代码中加载的时候,要注意加载的顺序。

以下可以加在Android.mk中,但是仅供参考,遇到自己问题需要自己调试

# ffmpeg

FFMPEG=f:/Ndk/ffmpeg

LOCAL_CFLAGS += -I$(FFMPEG)/include

LOCAL_LDLIBS += -L$(FFMPEG)/lib

LOCAL_LDLIBS += -lavformat -lavcodec  -lswresample -lswscale  -lavutil -lavfilter

 

include $(BUILD_SHARED_LIBRARY)

 

1.6.3         在C++中使用ffmpeg

ffmpeg的库是按C编译的,里面的符号都是C格式,因此,若在C++中使用,还是要使用extern "C"这种技术。相关语法请参考《C/C++语法指南》(作者:邵发,官网http://www.afanihao.cn)。

为了大家的方便,在/opt/ndk/ffmpeg/include下新建一个ffmpeg.h,内容为:

#ifndef _FFMPEG_H

#define _FFMPEG_H

 

#ifdef __cplusplus

extern "C" {

 

#define INT64_C(val) val##LL

#define UINT64_C(val) val##ULL

 

#endif

 

#include <libavcodec/avcodec.h>

#include <libavformat/avformat.h>

#include <libswscale/swscale.h>

#include <libavutil/avutil.h>

#include <libavutil/opt.h>

#include <libavutil/imgutils.h>

#include <libswresample/swresample.h>

 

#include <libavutil/error.h>

#include <libavfilter/avfiltergraph.h>

#include <libavfilter/avcodec.h>

#include <libavfilter/buffersink.h>

#include <libavfilter/buffersrc.h>

 

#ifdef __cplusplus

}

#endif

 

#endif

 

然后在C++代码中直接#include "ffmpeg.h"就可以了。

 

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。