记一次NDK编译ffmpeg

702 阅读4分钟

文本依据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是谷歌公司为安卓开发人员提供的本地开发套件,套件中包含以下工具

  1. 交叉编译器 (用于编译目标主机代码)
  2. [binutils工具集(ar、nm、ld等)] (什么是binutils工具集 - 简书)
  3. 目标主机使用的的头文件、库文件

3. 交叉编译需要准备什么

交叉编译即本机编译目标机程序,例如x86架构的cpu编译出arm架构的二进制文件。

  1. 交叉编译器
  2. 目标主机的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,这里有以下几个信息:

  1. aarch64上面说了,代表目标机器cpu为aarch64架构的交叉编译器。

  2. linux 为当前主机系统

  3. android 并不是说目标主机是android系统,而是指代arm64-v8a这个ABI。(注意这几是AArch64不是aarch64,前者是指令集,后者是CPU架构)

  4. verison是安卓对应的版本号,向上兼容(例如这里version==28,编译后的文件只能在 android5.0和之后的版本使用,低版本不能用,高版本能用)

  5. 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

image.png

这里出现的新的名词,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