给Android 开发者的 FFmpeg 教程 (一) FFmpeg的介绍与编译

3,849 阅读8分钟

在Android音视频开发体系中,FFmpeg库是最为关键的一个开源库。但网路上关于Android开发工程师如何使用FFmpeg的教程都比较零散且内容陈旧,故想特开此系列文章来共同探讨学习。本系列的文章是基于FFmpeg 4.X的版本来分析,由于笔者的个人知识的局限性,其中有任何错误之处,还望各位读者及时指出,望不吝赐教。

在学习FFmpeg之前,读者最好能掌握一些必要的相关联的知识:C/C++语言、JNI的语法、linux编程技术。如需要相关的参考资料的话,在此推荐以下几本书籍:《C++ Primer Plus》《JNI编程指南》《Linux编程技术详解》


我们先来认识一下FFmpeg:

FFmpeg是一套可以用来记录、转换数字音频、视频,并能将其转化为流的开源计算机程序。它提供了录制、转换以及流化音视频的完整解决方案。

FFmpeg主要包含以下几个模块:

  1. libavutil

libavutil是一个实用库,用于辅助多媒体编程。此库包含安全的可移植字符串函数、随机数生成器、数据结构、附加数学函数、加密和多媒体相关功能(如像素和样本格式的枚举)。libavcodec和libavformat并不依赖此库。

  1. libswscale

libswscale库用于执行高度优化的图像缩放、颜色空间和像素格式转换的操作。具体而言,此库执行以下转换:

  • 重缩放:一种更改视频大小的操作。有几种重缩放选项和算法可用。这通常是一个有损的操作。
  • 像素格式转换:是将图像格式和色域转换为图像的操作,例如从平面YUV420P转换为RGB24压缩。它还处理打包转换,即从打包布局(属于在同一缓冲区中交错的不同平面的所有像素)转换为平面布局(属于存储在专用缓冲区或“平面”中的同一平面的所有样本)。

如果源和目标色域不同,这通常是一个有损过程。

  1. libswresample

libswresample库用于执行高度优化的音频重采样、重矩阵化和采样格式转换操作。具体而言,此库执行以下转换:

  • 重采样:是更改音频速率的操作,例如从44100Hz的高采样率更改为8000Hz。从高采样率到低采样率的音频转换是一个有损过程。有几种重采样选项和算法可用。

  • 格式转换:是将样本类型转换的操作,例如从16位有符号样本转换为无符号8位或浮点样本。当从打包布局(属于在同一缓冲区中交错的不同通道的所有样本)传递到平面布局(属于存储在专用缓冲区或“平面”中的同一通道的所有样本)时,它还处理打包转换。

  • 重新调制:是改变频道布局(Channel Layout)的操作,例如从立体声到单声道。当输入通道无法映射到输出流时,该过程是有损的,因为它涉及不同的增益因子和混合.

  1. libavcodec

libavcodec库提供通用编码/解码框架,包含用于音频、视频和字幕流的多个解码器和编码器,以及多个位流过滤器。

  1. libavformat

libavformat库为多路复用和解复用音频、视频和字幕流提供了通用框架。它包含用于多媒体容器格式的多个多路复用器和解复用器。

它还支持多种输入和输出协议来访问媒体资源。

  1. libavdevice

libavdevice库提供了一个通用框架,用于从许多常见的多媒体输入/输出设备进行抓取和渲染,并支持多种输入和输出设备,包括Video4Linux2、VfW、DShow和ALSA。

  1. libavfilter

libavfilter库是一个通用的音频/视频的滤镜框架,其中包含多个滤镜、源(sources)和汇聚点(sinks)。

在为了更方便理解,在此解释一下上述内容包含的几个较为陌生的概念:

多路复用

数据通信系统或计算机网络系统中,传输媒体的带宽或容量往往会大于传输单一信号的需求,为了有效地利用通信线路,希望一个信道同时传输多路信号,这就是所谓的多路复用技术(Multiplexing)。采用多路复用技术能把多个信号组合起来在一条物理信道上进行传输,在远距离传输时可大大节省电缆的安装和维护费用。


源(sources)和汇聚点(sinks)

libavfilter可以存在多个滤镜组合处理音视频,每个滤镜都具有input及output端口。一个滤镜的output可以和另一个滤镜的input 连接起来,其中第一个滤镜称为源(source),它只有output端口,最后一个滤镜称为汇聚点(sink),它只有input端口。

对于FFmpeg的初步了解我们先暂时到此打住,下面我们将迈出FFmpeg在Android平台上开发的第一步————FFmpeg的交叉编译。


  1. 编译环境

操作系统:Ubuntu 20.04.1

NDK : NDK_r23b

以上只是参考配置,只要不是太过于古老的版本,其他的版本的操作系统和NDK版本也可以使用。

  1. 下载FFmpeg源码

到GitHub上使用git clone FFmpeg的源码:github.com/FFmpeg/FFmp…

  1. 编写交叉编译的脚本

在clone下来的FFmpeg源码根目录下打开命令终端,输入以下命令行,创建一个编译脚本文件:

touch build_script.sh
sudo chmod 751 build_script.sh

对于不了解linux命令行的读者简单解释一下上述命令行吧。

第一行touch build_script.sh,表示在当前目录创建一个文件名为 build_script.sh的文件,当然touch命令本质的用法并不是用于创建文件的,它是用于修改文件的属性的,只有当文件不存在时,才会创建一个新的文件。

第二行sudo chmod 741 build_script.sh,将 build_script.sh文件的权限修改为751,表示当前用户对文件拥有全部权限,当前用户组对文件只具有读和执行的权限,其他的只具有执行的权限。

使用文本编辑器,打开build_script.sh文件,贴入以下代码:

TOOLS_DIR=/home/jie/sda3/android-ndk-r23b/toolchains/llvm/prebuilt/linux-x86_64/bin
./configure --enable-shared \
--disable-static \
--disable-x86asm \
--enable-cross-compile \
--cc=$TOOLS_DIR/aarch64-linux-android30-clang \
--cxx=$TOOLS_DIR/aarch64-linux-android30-clang++ \
--strip=$TOOLS_DIR/llvm-strip \
--extra-cflags="-I$(pwd)/include" \
--extra-ldflags="-L$(pwd)/libs" \
--arch=arm64 \
--prefix=$(pwd)/install/aarch64 \
--target-os=android \

以上这段代码,是交叉编译cpu架构为aarch64的so库的脚本,如需编译其他cpu架构平台的so库,只需要对其中--cc--cxx中的内容进行替换即可。

下面我将详细介绍一下上面代码每行的含义及其中需要注意的事项。


TOOLS_DIR=/home/jie/sda3/android-ndk-r23b/toolchains/llvm/prebuilt/linux-x86_64/bin

定义NDK工具的目录,其中加粗部分的文字请各位读者根据自己NDK安装的实际路径自行替换。


./configure

运行当前目录下的名为configure的可执行程序。


--enable-shared

生成动态库,在此需要注意行尾的反斜杠前有个空格,这个空格一定不要漏掉了。


--disable-x86asm

系统上没有安装yasm汇编器,编译时会报错,故加上此行禁用汇编。


--enable-cross-compile

启用交叉编译


--cc=$TOOLS_DIR/aarch64-linux-android30-clang

指定编译时所采用的C编译器路径,这里指定的编译器需要与目标cpu相对应,如须编译armv7a、x86等架构的cpu,请自行替换编译器名称。


--cxx=$TOOLS_DIR/aarch64-linux-android30-clang++

指定编译时所采用的C++编译器路径,同样编译器需要与目标cpu相对应。


--strip=$TOOLS_DIR/llvm-strip

指定编译时采用的strip工具,用于对动态库进行优化。


--extra-cflags="-I$(pwd)/include"

为编译指定第三方头文件的存放路径,$(pwd)表示当前目录,上述的写法需要在当前目录下新建一个名为include的文件夹。当不存在第三方库需要整合打包到ffmpeg中时,可以不需要此代码行。


--extra-ldflags="-L$(pwd)/libs"

为编译指定第三方lib文件的存放路径,$(pwd)表示当前目录,上述的写法需要在当前目录下新建一个名为libs的文件夹。当不存在第三方库需要整合打包到ffmpeg中时,可以不需要此代码行。


--arch=arm64

指定交叉编译目标cpu的架构,读者根据实际情况自行修改。


--prefix=$(pwd)/install/aarch64

执行make install指令时,编译生成的文件的安装路径。


--target-os=android

指定目标OS平台为Android。

  1. 修改版本号

FFmpeg默认的版本号命名规则不太适用于Android平台,在这一步,我们还需要对FFmpeg的版本号命名规则进行修改。

让我们打开configure文件,然后搜索major这个关键字,找到以下代码片段:

image.png

将上述代码的内容替换为如下所示内容:

image.png

  1. 开始交叉编译

在终端中输入以下命令行,开始编译。

./build_install.sh
make -j4
make install

如果在执行./build_install.sh时发生了错误,可以打开ffbuild/config.log文件,检查编写脚本的错误。

一切顺利的话,我们最终可以在/install/aarch64这个目录下找到生成的头文件以及so文件。

image.png

至于如何将FFmpeg集成到Android工程中,可能在后续的文章会涉及部分内容,但严格意义上来说并不属于FFmpeg的知识点,故在此不再做专门的介绍,还请有需要的读者自行探索。

下一篇:

给Android 开发者的 FFmpeg 教程 (二) 初识FFmpeg命令行