音视频学习之路--编译FFmpeg

901 阅读8分钟

前言

看了前面的一些C/C++基础以及一些编译基础,那现在是时候实战一下,这里就编译顺便看一下FFmpeg的大体结构。

正文

这里就边操作,边记录吧,来看看大名鼎鼎的FFmpeg库。

FFmpeg定义

FFmpeg既是一款音视频编解码工具,同时也是一组音视频编解码开发套件,作为编解码开发套件,为开发者提供了丰富的音视频处理的调用接口。

FFmpeg提供了多种媒体格式的封装和解封装,包含多种音视频编解码、多种协议的流媒体、多种色彩格式转换、多种采样率转换、多种码率转换等等。

同时FFmpeg框架提供了多种丰富的插件模块,包含封装与解封装插件、编码与解码的插件等。

关于这里的一些具体概念,比如什么是封装和解封装,我们到后面学习音视频相关知识点就明白了,这里先大概了解。

FFmpeg模块介绍

关于下载FFmpeg代码很容易,从官网或者GitHub都可以,但是我尝试了在Linux上下载很慢,所以我直接在Windows上在GitHub下载了,拷贝到了Linux虚拟机中,下面就是完整的目录:

image.png

FFmpeg框架的目录还是蛮清晰的,其中包括了几个模块,这里先简单介绍一下:

  • libavcodec

编解码库,封装了Codec层,但是有一些codec是具备自己的License,FFmpeg不会默认添加,比如libx264、FDK-AAC、Lame等库,但是FFmpeg像是一个平台,可以降其他第三方codec以插件的方式添加进来,为开发者提供统一接口。

  • libavformat

文件格式和协议库,封装了Protocol层和Demuxer、Muxer层,使得协议和格式对于开发者来说是透明的。

  • libavfilter

音视频滤镜库,该模块包含了音视频特效的处理,在使用FFmpeg的API进行编解码的过程中,可以使用该模块高效的为音视频数据做特殊处理。

  • libavdevice

输入输出设备库,比如需要编译出播放声音或者视频的工具ffplay,就需要确保该模块是打开的,同时也需要libsdl的预先编译,该设备模块播放声音和视频又都是使用libsdl库。

  • libavutil

核心工具库,最基础模块之一,其他模块都会依赖该模块做一些基本的音视频处理操作。

  • libswresample

用于音视频采样,可以对音频进行声道数、数据格式、采样率等多种基本信息的转换。

  • libswscale

该模块用于图像格式转换,可以降YUV的数据转换为RGB的数据

  • libpostproc

该模块用于后期处理,当我们使用filter的时候,就需要打开这个模块,filter会用到这个模块的一些基础函数。

另外,库里还包含对H.264/MPEG-4 AVC视频编码的X264库,是非常常用的有损视频编码器,支持CBR、VBR模块,可以在编码的过程中直接改变码率的设置,在直播场景中非常适用,可以做码率自适应的功能。

一些最基本的介绍就是上面了,具体是啥意思,我们后面再细说。

编译FFmpeg

wget命令

在Linux中可以使用wget命令来下载文件,这个wget就相当于是一个下载器,但是呢最近发现很多网站使用wget下载特别慢,尤其是国外的一些网站,Windows挂了代理也还是不行,上网找了一些,推荐使用mwget。

mwget命令

mwget下载器是一个多线程下载应用,比wget下载更快,下面有个脚本可以直接安装:

#!/bin/bashwget http://jaist.dl.sourceforge.net/project/kmphpfm/mwget/0.1/mwget_0.1.0.orig.tar.bz2
yum install bzip2 gcc-c++ openssl-devel intltool -y
bzip2 -d mwget_0.1.0.orig.tar.bz2
tar -xvf mwget_0.1.0.orig.tar 
cd mwget_0.1.0.orig
./configure   #一般用来生成 Makefile,为下一步的编译做准备,你可以通过在 configure 后加上参数来对安装进行控制,比如代码:./configure –prefix=/usr 意思是将该软件安装在/usr下面
make       #编译,大多数的源代码包都经过这一步进行编译
make install  #开始安装echo "至此,安装完成"

这个脚本在Ubuntu上肯定运行不起来,就比如yum这个命令在Ubuntu上就不行,这里就简单介绍一下apt和yum。

apt和yun

一般来说著名的Linux系统分为2大类,

  • RedHat系列:Redhat、Centos、Fedora等

  • Debian系列:Debian、Ubuntu等

这2个系统的包管理器用的是不一样的,有区分yum、dkpg和apt,下图是区别:

image.png

所以上面yum安装bzip2的命令就需要修改一下,改成

apt install bzip2 gcc-c++ openssl-devel intltool -y

但是呢,会发现这4个软件只有一个bzip2能安装成功,其他几个都找不到,这又是什么鬼呢,下面再来细说。

Ubuntu安装mwget

这里还是单独说一下在Ubuntu下安装这个mwget,因为网上很多都是centos系统,直接看一个链接:

# Ubuntu安装mwget

会发现想跑通mwget的shell脚本需要安装3个软件,然后把压缩包bz2文件先进行解压,然后运行./configure脚本,这个脚本跑完后需要执行make和make install,注意这又是坑,首先需要sudo权限,然后会发现很多cpp文件缺少头文件,这时需要挨个去修改添加头文件。

下载FFmpeg

假设上面mwget能成功安装,这时就需要下载和解压FFmpeg了,命令如下:

mwget https://ffmpeg.org/releases/ffmpeg-4.2.2.tar.bz2
tar -jxvf ffmpeg-4.2.2.tar.bz2

下载完之后,就是上面我们简单介绍的几个目录了,这时可以直接运行configure脚本,这个脚本是用来配置FFmpeg的,里面配置项巨多,我们可以直接运行:

image.png

这里发现居然执行不过,原因很简单,是因为没有yasm汇编编译器,FFmpeg为了提高效率使用了汇编指令,所以需要安装汇编编译器:

sudo apt-get install yasm

即可,然后configure脚本就可以执行成功。

编译FFmpeg

当然上面脚本编译时是编译很多东西,我们需要针对我们自己的需求,定制一个脚本,其实就是配置FFmpeg的configure中的各种选项,下面我们来看一下:

#!/bin/bash

echo ">>>>>>>>> 注意:该编译环境目前只在 NDK17c + ffmpeg4.2.2 测试过 <<<<<<<<"
echo ">>>>>>>>> 注意:该编译环境目前只在 NDK17c + ffmpeg4.2.2 测试过 <<<<<<<<"
echo ">>>>>>>>> 注意:该编译环境目前只在 NDK17c + ffmpeg4.2.2 测试过 <<<<<<<<"

#NDK_ROOT 变量指向 ndk 目录
NDK_ROOT=$NDK_HOME
#指定android api版本
ANDROID_API=21


#开始编译 在下面调用传入参数即可
function build_ffmpeg()
{
echo "开始编译 $PREFIX_CPU"
echo "开始编译 $PREFIX"
echo "开始编译 $TOOLCHAIN"

./configure \
--prefix=$PREFIX \
--enable-small \
--disable-programs \
--disable-avdevice \
--disable-encoders \
--disable-muxers \
--disable-filters \
--enable-cross-compile \
--cross-prefix=$CROSS_PREFIX \
--disable-shared \
--enable-static \
--sysroot=$NDK_ROOT/platforms/android-$ANDROID_API/arch-$ARCH \
--extra-cflags="$CFLAGES" \
--arch=$ARCH \
--target-os=android

#上面运行脚本生成makefile之后,使用make执行脚本
make clean
make
make install

echo "$PREFIX_CPU 编译完成"
echo "$PREFIX_CPU 编译完成"
echo "$PREFIX_CPU 编译完成"
}

#armeabi-v7a
PREFIX=./result/armeabi-v7a
TOOLCHAIN=$NDK_ROOT/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64
ARCH=arm
CROSS_PREFIX=$TOOLCHAIN/bin/arm-linux-androideabi-
CFLAGES="-isysroot $NDK_ROOT/sysroot -isystem $NDK_ROOT/sysroot/usr/include/arm-linux-androideabi -D__ANDROID_API__=$ANDROID_API -U_FILE_OFFSET_BITS  -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -march=armv7-a -mfloat-abi=softfp -mfpu=vfpv3-d16 -mthumb -Wa,--noexecstack -Wformat -Werror=format-security  -O0 -fPIC"

build_ffmpeg

#arm64-v8a
PREFIX=./result/arm64-v8a
TOOLCHAIN=$NDK_ROOT/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64
ARCH=arm64
CROSS_PREFIX=$TOOLCHAIN/bin/aarch64-linux-android-
CFLAGES="-isysroot $NDK_ROOT/sysroot -isystem $NDK_ROOT/sysroot/usr/include/aarch64-linux-android -D__ANDROID_API__=$ANDROID_API -U_FILE_OFFSET_BITS  -DANDROID -ffunction-sections -funwind-tables -fstack-protector-strong -no-canonical-prefixes -Wa,--noexecstack -Wformat -Werror=format-security  -O0 -fPIC"

build_ffmpeg

#直接跳转到编译完成的路径
cd /result

上面就是编译的脚本,这里有几点需要说明一下,对于这种编译的脚本,还是要力求都看懂,不然出问题了根本无法查找。

  • 就是变量问题,比如这里的PREFIX、TOOLCHAIN、ARCH这3个变量就是有的是新建的目录、有的就是字符串。
  • 这里函数参数也要注意,比如工具链、CPU架构版本等等。
  • 这里可以在shell脚本中使用函数,其中和运行shell脚本一样可以直接在脚本名后面传递参数一样,这里可以直接定义build_ffmpeg()函数,然后在后面进行调用。
  • 其中在build_ffmpeg函数中,就是调用FFmpeg的configure脚本,并且传入一些列参数来配置这个脚本,关于这个脚本,后面还可以细说一下里面的常用配置。
  • 在运行完configure后,会生成一个makefile文件,然后通过make指令可以执行脚本。

configure常用参数

既然我们知道了这里的编译脚本就是配置configure中的一些配置,那常用的配置我们必须得懂,下面就罗列一些:

  • --prefix=PREFIX:设置动、静态库的位置,这个必须设置。
  • --enable-small:可以优化库的大小。
  • --disable-programs:我们不需要使用程序也就是不需要在Windows执行某个.exe程序,我们只在代码中使用。
  • --disable-avdevice:可以操控我们的摄像头,Android中是不支持。
  • --disable-encoders:编码是否打开,这里为了编译更快,如果用不到可以去掉。
  • --disable-muxers:混合封装是否可以打开,如果不用可以去掉。
  • --disable-filters:滤镜特效等是否开启,如果不用可以去掉。
  • --enable-cross-compile:开启交叉编译。
  • --cross-prefix=$CROSS_PREFIX:交叉编译的脚本,也就是前面设置的NDK里的编译脚本。
  • --disable-shared:是否开启编译成动态库。
  • --enable-static:是否开启编译成静态库。
  • --sysroot=NDKROOT/platforms/androidNDK_ROOT/platforms/android-ANDROID_API/arch-$ARCH:交叉编译的一些配置。
  • --extra-cflags="$CFLAGES":其他的一些配置。
  • --arch=$ARCH:期望的运行环境的CPU架构
  • --target-os=android:期望运行在Android平台。

编译问题

首先这个编译可不是一帆风顺的,由于代码太多,我的Ubuntu虚拟机大概要编译20分钟,其中有几个主要错误,下面来说一下。

  • C compiler test failed:c编译器错误,这个原因主要是FFmpeg 4.2.2版本开始默认使用clang进行编译,这需要找到configure中的代码进行修改默认配置:
//1. 修改 configure 文件
vim configure
//2. 把 默认的 clang 修改为 gcc
if test "$target_os" = android; then
   # cc_default="clang"
		 cc_default="gcc"
fi

  • yasm not found:这个之前也说过,无法找到汇编编译器,需要安装即可。

  • vim中如何查找关键字,这个是vim的一个操作,在命令模式下,输入 /xxx 即可查找xxx,并且可以高亮显示,想查看下一个按 n 键即next可以查看下一个xxx关键字,这个在查找长脚本文件中很关键。

总结

编译FFmpeg还是很复杂的,这也正常,毕竟FFmpeg有这么多功能,音视频学习之路还很长,边学边总结吧,加油。