[音视频] 让 FFmpeg 支持 AV1 编解码
基于 FFmpeg 7.0.1 版本
前言
刚开始时我以为 FFmpeg
默认就支持 AV1
软解,直到我遇到问题。
当我使用不支持 AV1
的解码 FFmpeg
时,AVCodecContext
的初始化一路正常,当我通过 avcodec_send_packet()
方法将 Packet
发送给 AVCodecContext
时就会返回 -38
,刚开始时我以为是我自己哪儿的代码写错了,通过网上查找资料才知道,FFmpeg
还需要通过其他的软解库才能够支持,目前 FFmpeg
支持的 AV1
软解库有:libaom
、libdav1d
和 libsvtav1
,我选择的是 libaom
。
如果想要使用硬解来解码 AV1
时,使用 FFmpeg
就相对要简单一点,Andorid
环境下,只需要开启 av1_mediacodec
解码器,除了这个硬件解码器,还有以下的几种 Android
支持的硬件解码器:h264_mediacodec
,hevc_mediacodec
,vp8_mediacodec
和 vp9_mediacodec
,分别表示 H.264
,H.265
,VP8
和 VP9
的硬件解码器,目前绝大多数的手机芯片都支持 H.264
和 H.265
的硬件解码,只有一些新出来的手机芯片支持 AV1
的硬件解码(遥遥领先家的芯片目前没有一款支持 AV1
编码的)。我们可以通过这个网站 去查询芯片支持的硬件编码格式。
我以 高通 8 gen3
为例子:
我们发现它对上述的硬件编码格式全部支持。
当我在使用不支持 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
相对于编译 libaom
,FFmpeg
可要麻烦许多,也容易出错。
不管编译啥,第一步都是去下源码,去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
文件来支持我们的交叉编译。
修改 cc_default
和 cxx_default
这两个参数,注释掉的代码是以前的值。
如果你没有开启 --enable-libaom
那么你就可以开始执行脚本开始编译了,如果开启了 --enable-libaom
的话,那就还需要修改一个地方。
注释掉校验 libaom
库的代码,默认 FFmpeg
会通过 package_configure
文件来校验是否有 libaom
这个库,如果没有这个库那么就会关闭 libaom
的功能,然后会有一个 aom >= 1.0.0 not found
的报错信息,我们移除校验后就可以绕过去。
讲道理我们只需要将 libaom
的 package_configure
,headers
和 so
库移动到 NDK
中正确的位置就不会报错了,目前不知道怎么搞。
如果你的所有配置都没有问题,那么编译成功后你将在下面的目录中看到生成的库文件:
最后
如果没有意外的话将通过上面方法生成的 libaom
和 FFmpeg
生成的 so
文件添加至 Android
的项目中那么就可以支持 AV1
的硬件和软件解码了,当然你也可以直接复制我编译好的:tMediaPlayer