[音视频] 让 FFmpeg 支持 AV1 编解码

1,454 阅读5分钟

[音视频] 让 FFmpeg 支持 AV1 编解码

基于 FFmpeg 7.0.1 版本

前言

刚开始时我以为 FFmpeg 默认就支持 AV1 软解,直到我遇到问题。
当我使用不支持 AV1 的解码 FFmpeg 时,AVCodecContext 的初始化一路正常,当我通过 avcodec_send_packet() 方法将 Packet 发送给 AVCodecContext 时就会返回 -38,刚开始时我以为是我自己哪儿的代码写错了,通过网上查找资料才知道,FFmpeg 还需要通过其他的软解库才能够支持,目前 FFmpeg 支持的 AV1 软解库有:libaomlibdav1dlibsvtav1,我选择的是 libaom

如果想要使用硬解来解码 AV1 时,使用 FFmpeg 就相对要简单一点,Andorid 环境下,只需要开启 av1_mediacodec 解码器,除了这个硬件解码器,还有以下的几种 Android 支持的硬件解码器:h264_mediacodechevc_mediacodecvp8_mediacodecvp9_mediacodec,分别表示 H.264H.265VP8VP9 的硬件解码器,目前绝大多数的手机芯片都支持 H.264H.265 的硬件解码,只有一些新出来的手机芯片支持 AV1 的硬件解码(遥遥领先家的芯片目前没有一款支持 AV1 编码的)。我们可以通过这个网站 去查询芯片支持的硬件编码格式。
我以 高通 8 gen3 为例子:

8gen3.png

我们发现它对上述的硬件编码格式全部支持。
当我在使用不支持 AV1 硬件编码的手机时,在调用 avcodec_open2() 打开解码器时会返回错误。
这里我要说明下:我使用 Google G1 处理器的 AV1 硬件解码器来解码时发现它的性能比软件解码的性能还要差,目前我也不知道是什么原因导致的这个问题,可能是我自己的某些问题或者是 FFmpeg 目前对 av1_mediacodec 的支持不是很好,也可能是对 G1 芯片支持不好,但是我目前也没有其他的支持 AV1 硬件解码的手机。

NDK 交叉编译 libaom

首先去下载 libaom 的源码:

git clone https://aomedia.googlesource.com/aom

下载成功后在 aom 项目的目录下创建一个 aom_build 的目录,当作 cmake 的编译目录,然后进入到 aom_build 目录下执行以下命令来配置 aom 的打包:

cmake -DCMAKE_TOOLCHAIN_FILE=/Users/pengcheng.tan/Library/Android/sdk/ndk/25.1.8937393/build/cmake/android.toolchain.cmake -DANDROID_ABI="x86" -DANDROID_PLATFORM=android-21 -DBUILD_SHARED_LIBS=1 ..

-DCMAKE_TOOLCHAIN_FILE 配置 NDK 交叉编译工具链的 cmake 文件,你们记得替换成你们自己电脑上的目录。

-DANDROID_ABI 指定编译结果的 CPU 架构。

-DANDROID_PLATFORM 指定最低的 Android 版本。

-DBUILD_SHARED_LIBS 生成动态链接库,这个一定要打开,不然不生成 so 库。

如果你还需要特殊的配置,去 libaom 中去查询下。

配置的命令执行完成后就直接通过 make 命令来执行编译了。

如果不出意外的话编译成功后你就能在 aom_build 目录下面看到 libaom.so 这个文件了,最好是通过 file 命令查看一下是不是我们要的 CPU 架构的动态链接库。

NDK 交叉编译 FFmpeg

相对于编译 libaomFFmpeg 可要麻烦许多,也容易出错。
不管编译啥,第一步都是去下源码,去FFmpeg 官方网站 下载你需要的版本的源码。

下载好源码后将以下的编译脚本添加到 FFmpeg 的根目录下:

#!/bin/bash

#配置NDK路径
NDK=/Users/pengcheng.tan/Library/Android/sdk/ndk-bundle
#配置toolchain路径
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/darwin-x86_64
#配置交叉编译环境的根路径
SYSROOT=$TOOLCHAIN/sysroot

#arm64-v8a
API=30
ARCH=arm64
CPU=armv8-a
CROSS_PREFIX="$TOOLCHAIN/bin/aarch64-linux-android"
CROSS_PREFIX_CLANG="$TOOLCHAIN/bin/aarch64-linux-android$API"

#armeabi-v7a
#API=30
#ARCH=arm
#CPU=armv7-a
#CROSS_PREFIX="$TOOLCHAIN/bin/arm-linux-androideabi"
#CROSS_PREFIX_CLANG="$TOOLCHAIN/bin/armv7a-linux-androideabi$API"

#x86
#API=30
#ARCH=x86
#CPU=i686
#CROSS_PREFIX="$TOOLCHAIN/bin/i686-linux-android"
#CROSS_PREFIX_CLANG="$TOOLCHAIN/bin/i686-linux-android$API"

#x86_64
#API=30
#ARCH=x86_64
#CPU=x86-64
#CROSS_PREFIX="$TOOLCHAIN/bin/x86_64-linux-android"
#CROSS_PREFIX_CLANG="$TOOLCHAIN/bin/x86_64-linux-android$API"


OPTIMIZE_CFLAGS="-march=$CPU"
#配置so输出路径
OUTPUT=./android/$CPU

./configure \
    --prefix=$OUTPUT \
    --target-os=android \
    --arch=$ARCH \
    --cpu=$CPU \
    --enable-neon \
    --enable-cross-compile \
    --enable-shared \
    --enable-jni \
    --enable-mediacodec \
    --enable-decoder=h264 \
    --enable-decoder=h264_mediacodec \
    --enable-decoder=hevc \
    --enable-decoder=hevc_mediacodec \
    --enable-decoder=av1 \
    --enable-decoder=av1_mediacodec \
    --enable-decoder=vp8 \
    --enable-decoder=vp8_mediacodec \
    --enable-decoder=vp9 \
    --enable-decoder=vp9_mediacodec \
    --disable-static \
    --disable-asm \
    --disable-doc \
    --disable-ffplay \
    --disable-ffprobe \
    --disable-symver \
    --disable-ffmpeg \
    --disable-avdevice \
    --disable-debug \
    --disable-postproc \
    --sysroot=$SYSROOT \
    --cross-prefix=$CROSS_PREFIX- \
    --cross_prefix_clang=$CROSS_PREFIX_CLANG- \
    --enable-libaom \

    make clean all
    make -j8
    make install

NDK 的路径配置需要替换成你们自己的哈,我配置了四种 CPU 架构的配置,如果你要其中的一种就需要注释掉其他的三种,我默认选中的是 arm64-v8a 的架构。

我的配置中开启了以下的解码器:

    --enable-mediacodec \
    --enable-decoder=h264 \
    --enable-decoder=h264_mediacodec \
    --enable-decoder=hevc \
    --enable-decoder=hevc_mediacodec \
    --enable-decoder=av1 \
    --enable-decoder=av1_mediacodec \
    --enable-decoder=vp8 \
    --enable-decoder=vp8_mediacodec \
    --enable-decoder=vp9 \
    --enable-decoder=vp9_mediacodec \

如果你们有自己的特殊的配置需要自己修改就行了。

然后也开启了 libaom:

--enable-libaom 

然后我们还需要修改默认的 configure 文件来支持我们的交叉编译。

ffmpeg_configure.png

修改 cc_defaultcxx_default 这两个参数,注释掉的代码是以前的值。

如果你没有开启 --enable-libaom 那么你就可以开始执行脚本开始编译了,如果开启了 --enable-libaom 的话,那就还需要修改一个地方。

ffmpeg_configure_aom.png

注释掉校验 libaom 库的代码,默认 FFmpeg 会通过 package_configure 文件来校验是否有 libaom 这个库,如果没有这个库那么就会关闭 libaom 的功能,然后会有一个 aom >= 1.0.0 not found 的报错信息,我们移除校验后就可以绕过去。
讲道理我们只需要将 libaompackage_configureheadersso 库移动到 NDK 中正确的位置就不会报错了,目前不知道怎么搞。

如果你的所有配置都没有问题,那么编译成功后你将在下面的目录中看到生成的库文件:

ffmpeg_output.png

最后

如果没有意外的话将通过上面方法生成的 libaomFFmpeg 生成的 so 文件添加至 Android 的项目中那么就可以支持 AV1 的硬件和软件解码了,当然你也可以直接复制我编译好的:tMediaPlayer