文本依据ndk-r21b版本进行,截至2021年8月15日,该版本为NDK最新的LTS版版本。
ptlzc/lighthouse-android-ffmpeg-builder: build libffmpeg.so for android (github.com)
前置知识
1. ffmpeg-configure文件是什么
configure的本质是一个shell脚本,也就是可以在shell中直接运行,它是ffmpeg为开发人员提供的一个构建脚本,用于生成编译所需要的makefile文件。
2. NDK是什么
NDK是谷歌公司为安卓开发人员提供的本地开发套件,套件中包含以下工具
- 交叉编译器 (用于编译目标主机代码)
- [binutils工具集(ar、nm、ld等)] (什么是binutils工具集 - 简书)
- 目标主机使用的的头文件、库文件
3. 交叉编译需要准备什么
交叉编译即本机编译目标机程序,例如x86架构的cpu编译出arm架构的二进制文件。
- 交叉编译器
- 目标主机的c库,头文件
上面两个NDK都已经为我们准备好了。
4. ARM的认识
在配置configura文件、选择ndk编译器的时候,会涉及到arch、cpu等分类,下面做个总结
从arm-cpu位数上看,arm-cpu分为以下两类
32位
armv1-armv7都属于32位cpu
64位
armv8(第一款64位cpu)
64位
NDK的交叉编译器也是根据这样进行分类的,在NDK中,64位CPU对应的交叉编译器前缀为aarch64 (这是因为新版的ndk使用clang作为编译器,clang使用架构名进行分类)
所以我们要想通过ndk编译64位arm-cpu的手机,就需要使用ndk_path/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-$version-clang,这里有以下几个信息:
-
aarch64上面说了,代表目标机器cpu为aarch64架构的交叉编译器。
-
linux 为当前主机系统
-
android 并不是说目标主机是android系统,而是指代arm64-v8a这个ABI。(注意这几是AArch64不是aarch64,前者是指令集,后者是CPU架构)
-
verison是安卓对应的版本号,向上兼容(例如这里version==28,编译后的文件只能在 android5.0和之后的版本使用,低版本不能用,高版本能用)
-
clang 没啥好说的
所以可以得出ndk中交叉编译器的命名规则为 CPU架构-当前系统-ABI安卓abi版本-clang
猜测:为什么谷歌使用andorid代替AArch64指令集ABI,可能是因为谷歌想全力推进AArch64指令集的使用,放弃armeabi等指令集。
代号、标记和 Build 号 | Android 开源项目 | Android Open Source Project
32位
上面说完arm64位cpu目前只有一个AArch64的指令集,下面要说32位,32-CPU下的指令集有很多个。所以导致选择交叉编译器的时候比较混乱
先看看安卓官方对其的分类 Android ABI | Android NDK | Android Developers
这里出现的新的名词,ABI、arm64-v8a和armeabi-v7a,解释一下
ABI:application binary interface,应用程序二进制接口,可以简单理解为指令集的集合
arm64-v8a:目前仅支持 AArch64指令集的ABI
armebi-v7a:支持三种指令集,看图。
所以按照上面说的命名规则,如果我的工作站环境是Linux,需要编译ABI_VERSION=28程序,在选择32位交叉编译器的时候,我就需要选择 armv7a-linux-androideabi28-clang作为我的C文件编译器。(armv7a是目前主流的32位架构cpu,低于armv7a架构没必要考虑去兼容,所以NDK只提供了armv7a的32位编译器)
编写编译脚本
Configure编译参数
在使用configure配置编译makefile文件的时候,有以下几个比较重要的参数
1. --arch 代表运行ffmpeg主机的CPU架构,arm上有armv7a和aarch64等两个架构
2. --cross-prefix binutils工具的前缀名称,包含路径。
3. --cc c文件交叉的交叉编译器
4. --cxx c++文件的交叉编译器
5. --prefix make install的路径,即编译后的库文件存放路径
通用配置
# 指定NDK路径
export NDK=/android/android-ndk-r21d
# ffmpeg组件配置
export COMMON_SET="
--enable-cross-compile \
--target-os=android \
--enable-small \
--sysroot=$TOOLCHAIN/sysroot \
--extra-cflags="-fpic" \
--disable-shared \
--enable-static \
--disable-symver \
--disable-doc \
--disable-htmlpages \
--disable-manpages \
--disable-podpages \
--disable-txtpages \
--disable-ffplay \
--disable-ffmpeg \
--disable-ffprobe \
--disable-avdevice \
--disable-bsfs \
--disable-devices \
--disable-protocols \
--enable-protocol=file \
--disable-protocol=rtmp* \
--disable-protocol=rtmp \
--disable-protocol=rtmpt \
--disable-protocol=rtp \
--disable-protocol=sctp \
--disable-protocol=srtp \
--disable-parsers \
--disable-demuxers \
--enable-demuxer=mov \
--enable-demuxer=mp3 \
--enable-demuxer=image2 \
--enable-demuxer=gif \
--enable-demuxer=wav \
--enable-demuxer=flv \
--enable-demuxer=live_flv \
--enable-demuxer=data \
--enable-demuxer=mpegps \
--enable-demuxer=mpegts \
--enable-demuxer=hls \
--disable-decoders \
--enable-decoder=aac \
--enable-decoder=png \
--enable-decoder=h264 \
--enable-decoder=mp3 \
--enable-decoder=mjpeg \
--enable-decoder=mpeg4 \
--enable-decoder=gif \
--enable-decoder=pcm_s16le \
--enable-decoder=hevc \
--disable-muxers \
--disable-encoders \
--enable-swscale \
--disable-filters \
--enable-filter=crop \
--enable-filter=scale \
--enable-filter=afade \
--enable-filter=atempo \
--enable-filter=copy \
--enable-filter=aformat \
--enable-filter=overlay \
--enable-filter=vflip \
--enable-filter=hflip \
--enable-filter=transpose \
--enable-filter=volume \
--enable-filter=rotate \
--enable-filter=apad \
--enable-filter=amerge \
--enable-filter=aresample \
--enable-filter=setpts \
--enable-filter=fps \
--enable-filter=palettegen \
--enable-filter=paletteuse \
--enable-filter=trim \
--enable-filter=null \
--enable-filter=overlay \
--enable-filter=format \
--enable-filter=atrim \
--enable-filter=split \
--enable-filter=amix \
--enable-filter=anull \
--enable-filter=adelay \
--enable-gpl \
--enable-zlib \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-decoder=mpeg4_mediacodec \
--enable-decoder=vp9_mediacodec \
--enable-decoder=vp8_mediacodec \
--enable-decoder=hevc_mediacodec \
--enable-hwaccels \
--enable-asm \
--enable-version3 "
64位脚本
ARCH=aarch64
ARCH_FLAGS="\
--arch=$ARCH \
--cross-prefix=$TOOLCHAIN/bin/aarch64-linux-android- \
--cc=$TOOLCHAIN/bin/aarch64-linux-android$API_VERSION-clang \
--prefix=$OUTPUT/aarch64 \
"
ARCH_OUTPUT="$OUTPUT/$ARCH/lib"
rm -rf "$ARCH_OUTPUT"
mkdir -p "$ARCH_OUTPUT"
build() {
./configure \
${COMMON_SET} \
${ARCH_FLAGS}
make clean all
make -j$(nproc)
make install
}
build
32位脚本
# 上面说道,目前主流的arm32位cpu架构为armv7a
ARCH=armv7a
ARCH_FLAGS="\
--arch=$ARCH \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--cc=$TOOLCHAIN/bin/armv7a-linux-androideabi$API_VERSION-clang \
--cxx=$TOOLCHAIN/bin/armv7a-linux-androideabi$API_VERSION-clang++ \
--prefix=$OUTPUT/$ARCH \
"
ARCH_OUTPUT="$OUTPUT/$ARCH/lib"
rm -rf "$ARCH_OUTPUT"
mkdir -p "$ARCH_OUTPUT"
build() {
./configure \
${COMMON_SET} \
${ARCH_FLAGS}
make clean all
make -j$(nproc)
make install
}
build
编译单文件libffmpeg.so库
64位
package_library() {
GCC_L=$NDK/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/lib/gcc/aarch64-linux-android/4.9.x
SYSROOT_L=$TOOLCHAIN/sysroot/usr/lib/aarch64-linux-android
$TOOLCHAIN/bin/$ARCH-linux-android-ld -L$ARCH_OUTPUT -L$GCC_L \
-rpath-link=$SYSROOT_L/$API_VERSION -L$SYSROOT_L/$API_VERSION -soname libffmpeg.so \
-shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o $ARCH_OUTPUT/libffmpeg.so \
-lavcodec -lpostproc -lavfilter -lswresample -lavformat -lavutil -lswscale -lgcc \
-lc -ldl -lm -lz -llog \
# 设置动态链接器,不同平台的不同,android 使用的是/system/bin/linker
--dynamic-linker=/system/bin/linker
}
32位
package_library() {
GCC_L=$NDK/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/armv7-a/thumb
SYSROOT_L=$TOOLCHAIN/sysroot/usr/lib/arm-linux-androideabi
$TOOLCHAIN/bin/arm-linux-androideabi-ld -L$ARCH_OUTPUT -L$GCC_L \
-rpath-link=$SYSROOT_L/$API_VERSION -L$SYSROOT_L/$API_VERSION -soname libffmpeg.so \
-shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o $ARCH_OUTPUT/libffmpeg.so \
-lavcodec -lpostproc -lavfilter -lswresample -lavformat -lavutil -lswscale -lgcc \
-lc -ldl -lm -lz -llog \
--dynamic-linker=/system/bin/linker
# 设置动态链接器,不同平台的不同,android 使用的是/system/bin/linker
}
单文件编译32位和64位ffmpeg
#!/bin/sh
# 配置NDK路径,根据本机环境自己指定
ANDROID_NDK_ROOT=/android/android-ndk-r21d
# ABI版本,自己指定
ABI_VERSION=28
if [ ! -d "./ffmpeg" ]; then
git clone git@github.com:FFmpeg/FFmpeg.git ffmpeg
git checkout git checkout release/4.4
fi
# 设置编译文件后输出文件夹
OUTPUT=$(pwd)/build
# 清空输出文件夹
rm -rf "$OUTPUT"
# 新建输出文件夹
mkdir -p "$OUTPUT"
cd ffmpeg
# 交叉工具链的路径
TOOLCHAIN=$ANDROID_NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64
# ffmpeg配置
COMMON_SET="
--enable-cross-compile \
--target-os=android \
--enable-small \
--sysroot=$TOOLCHAIN/sysroot \
--extra-cflags="-fpic" \
--disable-shared \
--enable-static \
--disable-symver \
--disable-doc \
--disable-htmlpages \
--disable-manpages \
--disable-podpages \
--disable-txtpages \
--disable-ffplay \
--disable-ffmpeg \
--disable-ffprobe \
--disable-avdevice \
--disable-bsfs \
--disable-devices \
--disable-protocols \
--enable-protocol=file \
--disable-protocol=rtmp* \
--disable-protocol=rtmp \
--disable-protocol=rtmpt \
--disable-protocol=rtp \
--disable-protocol=sctp \
--disable-protocol=srtp \
--disable-parsers \
--disable-demuxers \
--enable-demuxer=mov \
--enable-demuxer=mp3 \
--enable-demuxer=image2 \
--enable-demuxer=gif \
--enable-demuxer=wav \
--enable-demuxer=flv \
--enable-demuxer=live_flv \
--enable-demuxer=data \
--enable-demuxer=mpegps \
--enable-demuxer=mpegts \
--enable-demuxer=hls \
--disable-decoders \
--enable-decoder=aac \
--enable-decoder=png \
--enable-decoder=h264 \
--enable-decoder=mp3 \
--enable-decoder=mjpeg \
--enable-decoder=mpeg4 \
--enable-decoder=gif \
--enable-decoder=pcm_s16le \
--enable-decoder=hevc \
--disable-muxers \
--disable-encoders \
--enable-swscale \
--disable-filters \
--enable-filter=crop \
--enable-filter=scale \
--enable-filter=afade \
--enable-filter=atempo \
--enable-filter=copy \
--enable-filter=aformat \
--enable-filter=overlay \
--enable-filter=vflip \
--enable-filter=hflip \
--enable-filter=transpose \
--enable-filter=volume \
--enable-filter=rotate \
--enable-filter=apad \
--enable-filter=amerge \
--enable-filter=aresample \
--enable-filter=setpts \
--enable-filter=fps \
--enable-filter=palettegen \
--enable-filter=paletteuse \
--enable-filter=trim \
--enable-filter=null \
--enable-filter=overlay \
--enable-filter=format \
--enable-filter=atrim \
--enable-filter=split \
--enable-filter=amix \
--enable-filter=anull \
--enable-filter=adelay \
--enable-gpl \
--enable-zlib \
--enable-jni \
--enable-mediacodec \
--enable-decoder=h264_mediacodec \
--enable-decoder=mpeg4_mediacodec \
--enable-decoder=vp9_mediacodec \
--enable-decoder=vp8_mediacodec \
--enable-decoder=hevc_mediacodec \
--enable-hwaccels \
--enable-asm \
--enable-version3 "
# 配置configure并编译
build64(){
ARCH=aarch64
ARCH_FLAGS="\
--arch=$ARCH \
--cross-prefix=$TOOLCHAIN/bin/aarch64-linux-android- \
--cc=$TOOLCHAIN/bin/aarch64-linux-android$ABI_VERSION-clang \
--prefix=$OUTPUT/aarch64 \
"
ARCH_OUTPUT="$OUTPUT/$ARCH/lib"
rm -rf "$ARCH_OUTPUT"
mkdir -p "$ARCH_OUTPUT"
./configure \
${COMMON_SET} \
${ARCH_FLAGS}
make clean all
make -j$(nproc)
make install
}
# 打包为单文件
package64(){
ARCH=aarch64
ARCH_OUTPUT="$OUTPUT/$ARCH/lib"
GCC_L=$ANDROID_NDK_ROOT/toolchains/$ARCH-linux-android-4.9/prebuilt/linux-x86_64/lib/gcc/$ARCH-linux-android/4.9.x
SYSROOT_L=$TOOLCHAIN/sysroot/usr/lib/$ARCH-linux-android
$TOOLCHAIN/bin/$ARCH-linux-android-ld -L$ARCH_OUTPUT -L$GCC_L \
-rpath-link=$SYSROOT_L/$ABI_VERSION -L$SYSROOT_L/$ABI_VERSION -soname libffmpeg.so \
-shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o $ARCH_OUTPUT/libffmpeg.so \
-lavcodec -lpostproc -lavfilter -lswresample -lavformat -lavutil -lswscale -lgcc \
-lc -ldl -lm -lz -llog \
--dynamic-linker=/system/bin/linker
}
# 编译32位
build32() {
ARCH=armv7a
ARCH_FLAGS="\
--arch=$ARCH \
--cross-prefix=$TOOLCHAIN/bin/arm-linux-androideabi- \
--cc=$TOOLCHAIN/bin/armv7a-linux-androideabi$ABI_VERSION-clang \
--cxx=$TOOLCHAIN/bin/armv7a-linux-androideabi$ABI_VERSION-clang++ \
--prefix=$OUTPUT/$ARCH \
"
ARCH_OUTPUT="$OUTPUT/$ARCH/lib"
rm -rf "$ARCH_OUTPUT"
mkdir -p "$ARCH_OUTPUT"
./configure \
${COMMON_SET} \
${ARCH_FLAGS}
make clean all
make -j$(nproc)
make install
}
# 打包32位单文件
package32() {
ARCH=armv7a
ARCH_OUTPUT="$OUTPUT/$ARCH/lib"
GCC_L=$ANDROID_NDK_ROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/lib/gcc/arm-linux-androideabi/4.9.x/armv7-a/thumb
SYSROOT_L=$TOOLCHAIN/sysroot/usr/lib/arm-linux-androideabi
$TOOLCHAIN/bin/arm-linux-androideabi-ld -L$ARCH_OUTPUT -L$GCC_L \
-rpath-link=$SYSROOT_L/$ABI_VERSION -L$SYSROOT_L/$ABI_VERSION -soname libffmpeg.so \
-shared -nostdlib -Bsymbolic --whole-archive --no-undefined -o $ARCH_OUTPUT/libffmpeg.so \
-lavcodec -lpostproc -lavfilter -lswresample -lavformat -lavutil -lswscale -lgcc \
-lc -ldl -lm -lz -llog \
--dynamic-linker=/system/bin/linker
}
build64
package64
build32
package32