在Android中使用FFmpeg进阶1

在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-v7aarm64-v8ax86x86_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源码就可以完成配置和编译工作。

  1. 一个可以在linux或者windows环境下编译安卓二进制的交叉编译器;

    交叉编译指的是在一个平台上编译生成另一个平台上的可执行代码。

  2. 另外还需要一个链接器和其他一些工具(这个工具子集被称为binutils)来构建目标文件;

  3. 另外,还需要用到Android 操作系统相关的组件,你需要定义个sysroot目录用来代表安卓操作系统的根目录。

让我们逐行看一下这个具体的例子。

1
2
3
4
5
6
7
8
9
10
11
12
./configure \
--prefix=${BUILD_DIR}/${ABI} \
--enable-cross-compile \
--target-os=android \
--arch=${TARGET_TRIPLE_MACHINE_BINUTILS} \
--sysroot=${SYSROOT} \
--cross-prefix=${CROSS_PREFIX} \
--cc=${CC} \
--extra-cflags="-O3 -fPIC" \
--enable-shared \
--disable-static \
${EXTRA_BUILD_CONFIGURATION_FLAGS} \

prefix:编译目标结果的位置。

enable-cross-compile :允许使用交叉编译

target-os=android:标记编译的目标平台为安卓平台。

arch:根据ABI的不同,设置的值不同。下面是不同ABI对应的设置:

1
2
3
4
armeabi-v7a: arm
arm64-v8a: aarch64
x86: i686
x86_64: x86_64

sysroot:所需的sysroot目录现在直接位于工具链的目录中:$TOOLCHAIN_PATH/sysroot。顺便说一下,工具链的路径本身是$NDK_ROOT/toolchains/llvm/prebuilt/$HOST_TAGHOST_TAG取决于构建NDK时使用的操作系统。

cross-prefix:这是一种将所有binutils工具一次性告知FFmpeg的配置脚本的方式。例如,前缀将被ld追加,以获得链接器。下面是不同ABI对应的设置,注意armeabi-v7a对应值结尾为abi。

1
2
3
4
armeabi-v7a: $TOOLCHAIN_PATH/bin/arm-linux-androideabi-
arm64-v8a: $TOOLCHAIN_PATH/bin/aarch64-linux-android-
x86: $TOOLCHAIN_PATH/bin/i686-linux-android-
x86_64: $TOOLCHAIN_PATH/bin/x86_64-linux-android-

cc:C编译器设置。

1
2
3
4
armeabi-v7a: $TOOLCHAIN_PATH/bin/armv7a-linux-androideabi16-clang
arm64-v8a: $TOOLCHAIN_PATH/bin/aarch64-linux-android21-clang
x86: $TOOLCHAIN_PATH/bin/i686-linux-android16-clang
x86_64: $TOOLCHAIN_PATH/bin/x86_64-linux-android21-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
2
wget https://ffmpeg.org/releases/ffmpeg-4.2.3.tar.gz
tar -zxvf ffmpeg-4.2.3.tar.gz

下载NDK_r20版本

1
2
wget https://dl.google.com/android/repository/android-ndk-r20-linux-x86_64.zip
unzip 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
2
3
4
5
6
7
8
9
10
11
ubuntu@VM-4-9-ubuntu:~/ffmpegmaker$ tree -L 2
.
├── build_dir
│ └── ffmpeg_for_armeabi-v7a.sh
├── ffmpeg
│ ├── ffmpeg-4.2.3
│ └── ffmpeg-4.2.3.tar.gz
├── ndk_dir
│ ├── android-ndk-r20
│ └── android-ndk-r20-linux-x86_64.zip
└── out

执行source ffmpeg_maker.sh && make &&make install,可以看到编译结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
ubuntu@VM-4-9-ubuntu:~/ffmpegmaker/out$ tree -L 3
.
└── armeabi-v7a
├── bin
│   ├── ffmpeg
│   └── ffprobe
├── include
│   ├── libavcodec
│   ├── libavdevice
│   ├── libavfilter
│   ├── libavformat
│   ├── libavutil
│   ├── libswresample
│   └── libswscale
├── lib
│   ├── libavcodec.so
│   ├── libavdevice.so
│   ├── libavfilter.so
│   ├── libavformat.so
│   ├── libavutil.so
│   ├── libswresample.so
│   ├── libswscale.so
│   └── pkgconfig
└── share
├── ffmpeg
└── man

其他相关设置

前面提到的,FFmpeg是高度可配置的,你可以剪掉某些你不需要的部分,以达到你的目的。我选择用下面这些配置参数来配置脚本,使其具有最小的二进制文件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--disable-runtime-cpudetect \
--disable-programs \
--disable-muxers \
--disable-encoders \
--disable-avdevice \
--disable-postproc \
--disable-swresample \
--disable-avfilter \
--disable-doc \
--disable-debug \
--disable-pthreads \
--disable-network \
--disable-bsfs \
--disable-decoders \
${DECODERS_TO_ENABLE}

对于可能的标志的完整列表,只要看看配置脚本本身,看看哪些是可以禁用的,哪些是默认启用的。

完整编译方案

更多的信息可以查看FFmpeg编译方案



关注博客或微信搜索公众号多媒体与图形,获取更多内容,欢迎在公众号留言交流!
扫一扫关注公众号
作者

占航

发布于

2021-11-15

更新于

2023-10-04

许可协议

评论