在Android中使用FFmpeg进阶1
学习过多媒体开发的同学大概都听过[FFMPEG,FFmpeg为音视频处理提供了一套比较完美的解决方案,但是,想要在android系统上使用它并不简单,一般来说,开发人员需要编译android版本的可用库文件,要使用Android NDK技术才能调用其提供的能力。这个过程非常复杂,作为一个音视频开发的初学者,很可能被一步编译工作就给打败了,FFmpeg学习之路直接从入门到放弃。
在Android中使用FFmpeg进阶系列文章可以帮助开发人员学习如何将FFmpeg工具编译并集成到自己的工程中。
Libraries 文档
文档中介绍了以下工具。
libavutil库说明
libavutil库是一个帮助便携式多媒体编程的实用库。它包含安全的可移植字符串函数、随机数生成器、数据结构、额外的数学函数、密码学和多媒体相关功能(如像素和样本格式的枚举)。它不是libavcodec和libavformat都需要的代码库。libswscale库说明
libswscale库执行高度优化的图像缩放和色彩空间及像素格式转换操作。 具体来说,这个库可以执行以下转换。 重新缩放:是改变视频尺寸的过程。有几个重新缩放的选项和算法可用。这通常是一个有损失的过程。 像素格式转换:是转换图像格式和图像色彩空间的过程,例如从平面的YUV420P到RGB24包装。它还处理打包转换,即从打包布局(属于不同平面的所有像素在同一缓冲区内交错排列)转换为平面布局(属于同一平面的所有样本存储在一个专门的缓冲区或 "平面")。 如果源和目的色彩空间不同,这通常是一个有损失的过程。libswresample 库说明
libswresample库执行高度优化的音频重采样、重矩阵化和采样格式转换操作。 具体来说,这个库可以执行以下转换。 重采样:是改变音频速率的过程,例如从44100Hz的高采样率到8000Hz。从高采样率到低采样率的音频转换是一个有损失的过程。有几个重采样选项和算法可用。 格式转换:是转换样本类型的过程,例如从16位有符号样本转换为无符号8位或浮动样本。它还处理打包转换,当从打包布局(所有属于不同通道的样本交错在同一个缓冲区内)传递到平面布局(所有属于同一通道的样本存储在一个专门的缓冲区或 "平面")。 Rematrixing:是改变通道布局的过程,例如从立体声到单声道。当输入通道不能被映射到输出流时,这个过程是有损失的,因为它涉及到不同的增益系数和混合。 其他各种音频转换(如拉伸和填充)通过专用选项启用。libavcodec库说明
libavcodec库提供了一个通用的编码/解码框架,包含了多个用于音频、视频和字幕流的解码器和编码器,以及几个比特流过滤器。 共享架构提供了从比特流I/O到DSP优化的各种服务,并使其适合于实现强大而快速的编解码器以及实验。libavformat库说明
libavformat库为音频、视频和字幕流的复用和解复用(muxing and demuxing)提供了一个通用框架。它包含了多个多路复用器和解复用器的多媒体容器格式。 它还支持几个输入和输出协议来访问媒体资源。libavdevice库说明
libavdevice库提供了一个通用框架,用于从许多常见的多媒体输入/输出设备抓取和渲染,并支持几个输入和输出设备,包括Video4Linux2、VfW、DShow和ALSA。libavfilter库说明
libavfilter库提供了一个通用的音频/视频过滤框架,包含几个过滤器、源和汇。如果你需要在android工程中使用FFMpeg的这些能力,有两种途径:
第一种:使用已有的封装了FFMPEG能力的java库,例如:Vitamio。这让你避开了编译等繁琐的工作,但是任何事都有两面性,有时候这些Java库封装了一堆其实你用不上的能力,让你的工程变得很重。
第二种:自己编译,自己封装使用。想要学习好音视频的同学,应该学会走这条路。万事开头难,但是通过这种文章了解如何编译FFmpeg,我相信大家会有兴趣进一步学习它,掌握这个音视频学习的重要工具。
学习编译
要编译FFmpeg工具库,需要下载源码文件,然后进行编译配置,再执行编译过程。
源码和NDK
编译配置
在FFMPEG源码的根目录中有一个configure文件,它检查可用的编译工具,并准备好内部模块的编译工作。尽管它是一个shell脚本,它也可以在Windows上执行。
FFmpeg是高度可配置的,配置脚本可以接受很多不同的参数,这些参数会影响整个构建过程的输出。而要让FFmpeg在安卓上工作,最困难的部分就是要传递适当的参数。
安卓平台包含四种类型的ABI,我们编译对应的二进制文件,它们分别是 armeabi-v7a
, arm64-v8a
, x86
, x86_64
,安卓早期支持三种类型的CPU,它们分别是ARM,INTEL和MIPS。从NDP r17版本开始,Android NDK已经不再支持MIPS。
通过adb shell cat /proc/cpuinfo 可以查看手机设备的cpu信息。
比如华为P30 PRO,对应的ABI位 arm64-v8a
C:\Users\Administrator>adb shell cat /proc/cpuinfo
Processor : AArch64 Processor rev 0 (aarch64)
FFmpeg源码是基于C语言编程的,为了编译出安卓平台使用的二进制文件,我们需要有以下三样东西,这些工具都在NDK开发工具包中,所以我们只需要下载NDK工具包和FFmpeg源码就可以完成配置和编译工作。
一个可以在linux或者windows环境下编译安卓二进制的交叉编译器;
交叉编译指的是在一个平台上编译生成另一个平台上的可执行代码。
另外还需要一个链接器和其他一些工具(这个工具子集被称为
binutils
)来构建目标文件;另外,还需要用到Android 操作系统相关的组件,你需要定义个
sysroot
目录用来代表安卓操作系统的根目录。
让我们逐行看一下这个具体的例子。
1 | ./configure \ |
–prefix:编译目标结果的位置。
–enable-cross-compile :允许使用交叉编译
–target-os=android:标记编译的目标平台为安卓平台。
–arch:根据ABI的不同,设置的值不同。下面是不同ABI对应的设置:
1 | armeabi-v7a: arm |
–sysroot:所需的sysroot
目录现在直接位于工具链的目录中:$TOOLCHAIN_PATH/sysroot
。顺便说一下,工具链的路径本身是$NDK_ROOT/toolchains/llvm/prebuilt/$HOST_TAG
。HOST_TAG
取决于构建NDK时使用的操作系统。
–cross-prefix:这是一种将所有binutils工具一次性告知FFmpeg的配置脚本的方式。例如,前缀将被ld追加,以获得链接器。下面是不同ABI对应的设置,注意armeabi-v7a对应值结尾为abi。
1 | armeabi-v7a: $TOOLCHAIN_PATH/bin/arm-linux-androideabi- |
–cc:C编译器设置。
1 | armeabi-v7a: $TOOLCHAIN_PATH/bin/armv7a-linux-androideabi16-clang |
这里有几点需要注意:
- 这些编译器都以clang结尾。
- 这些不是二进制文件,而是 shell 脚本。它们只是将所有参数重定向到
$TOOLCHAIN_PATH/bin/clang
二进制文件,并增加了几个参数。只要看看这些文件中的一个就知道了。另外,32位和64位版本的额外参数集是不同的。 - 每个文件名中都有一个Android API级别。我们只需要根据我们应用程序的
minSdkV
版本来选择它。在NDK r19中,最小的此类版本是16。对于64位版本,它从21开始,因为Lollipop是第一个首先得到64位支持的Android版本。 - 请注意,对于armabi-v7a ABI,交叉前缀和cc名称的第一个字是不同的:arm和armv7a。
–extra-flags=”-O3 -fPIC” :这里是我们为C编译器传递额外参数的方法。-O3是优化级别。如果我们要为Android构建共享对象(*.so库),则需要-fPIC。
–enable-shared 和 –disable-static –在这里,我们实际上是告诉配置脚本,我们需要构建共享库,而不是静态库。
${EXTRA_BUILD_CONFIGURATION_FLAGS} - 针对ABI的额外标志。
x86_64 增加了 --x86asmexe=${TOOLCHAIN_PATH}/bin/yasm
来指定驻留在 toochain
目录中的汇编编译器。
x86不指定汇编编译器,而是用--disable-asm
完全禁用汇编优化。
执行编译命令
make &&make install
编译案例
下面以编译一种ABI版本为例。
下载FFmpeg
1 | wget https://ffmpeg.org/releases/ffmpeg-4.2.3.tar.gz |
下载NDK_r20版本
1 | wget https://dl.google.com/android/repository/android-ndk-r20-linux-x86_64.zip |
比如以编译armeabi-v7a
为例,假设我们编译的
目标路径/home/ubuntu/ffmpegmaker/out
,NDK路径为/home/ubuntu/ffmpegmaker/ndk_dir
,编译使用的操作系统为linux-x86_64
,编写ffmpeg_for_armeabi-v7a.sh脚本。将ffmpeg_for_armeabi-v7a.sh
脚本放置在build_dir文件夹中,
1 | ubuntu@VM-4-9-ubuntu:~/ffmpegmaker$ tree -L 2 |
执行source ffmpeg_maker.sh && make &&make install
,可以看到编译结果。
1 | ubuntu@VM-4-9-ubuntu:~/ffmpegmaker/out$ tree -L 3 |
其他相关设置
前面提到的,FFmpeg是高度可配置的,你可以剪掉某些你不需要的部分,以达到你的目的。我选择用下面这些配置参数来配置脚本,使其具有最小的二进制文件。
1 | --disable-runtime-cpudetect \ |
对于可能的标志的完整列表,只要看看配置脚本本身,看看哪些是可以禁用的,哪些是默认启用的。
完整编译方案
更多的信息可以查看FFmpeg编译方案
在Android中使用FFmpeg进阶1
https://hangliebe.com/blog/2021/11/15/2021-11-15-鍦ˋndroid涓?浣跨敤FFmpeg杩涢樁1/