购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

1.4
FFmpeg的开发框架

本节介绍FFmpeg开发框架基本的使用说明,包括FFmpeg提供的三个可执行程序及其用法、FFmpeg提供的8个动态链接库及其用法、如何基于FFmpeg平台编写第一个FFmpeg程序等。

1.4.1 可执行程序

外界对于FFmpeg主要有两种使用途径:一种是在命令行运行FFmpeg的可执行程序,该方式适合没什么特殊要求的普通场景;另一种是通过代码调用FFmpeg的动态链接库,由于开发者可以在C代码中编排个性化的逻辑,因此该方式适合厂商专用的特制场景。

开源的FFmpeg框架提供了三个可执行程序,分别是ffmpeg、ffplay和ffprobe,它们的源码均位于fftools目录。下面对这三个程序分别展开详细介绍。

1.ffmpeg程序

ffmpeg程序主要有两个用途:一个是查询FFmpeg的支持信息,另一个是处理音视频的转换操作。关于音视频的转换命令,会在后面的章节中逐一介绍,这里只说明该程序能够查到哪些FFmpeg支持信息。前面在搭建FFmpeg开发环境的时候,提到可以用以下命令查看FFmpeg的版本信息:

    ffmpeg -version

除此之外,ffmpeg程序还能查询它所支持的文件格式,比如以下命令可以查看FFmpeg支持的文件格式:

    ffmpeg -formats

执行上面的命令,控制台回显长长的一串文件格式支持列表,列表开头如下:

    File formats:
    D. = Demuxing supported
    .E = Muxing supported
    --
    D  3dostr          3DO STR
     E 3g2             3GP2 (3GPP2 file format)
     E 3gp             3GP (3GPP file format)
    D  4xm             4X Technologies
     E a64             a64 - video for Commodore 64
    D  aa              Audible AA format files
    D  aac             raw ADTS AAC (Advanced Audio Coding)

可见FFmpeg支持的文件格式分为两种类型:一种被标记为D,表示支持该类型文件的解析;另一种被标记为E,表示支持该类型文件的封装。继续下拉这一长串文件格式列表,既能找到古老的VCD格式,也能找到风靡一时的RM和FLV格式,还能找到MP3和MP4等常见格式,看来FFmpeg真的将常见的音视频格式一网打尽了。

ffmpeg程序还能够查看更多信息,详见表1-3。由于相关概念比较专业,因此这里不再一一展开,等到后续涉及时再来讲解。

表1-3 ffmpeg程序的一级命令用途说明

2.ffplay程序

ffplay程序相当于一个播放器,用来播放音视频文件。在播放音频时,ffplay不仅会让扬声器放出声音,还会在屏幕上展示该音频的波形画面。在播放视频时,ffplay会在屏幕上展示连续的视频画面,就像看电影、看电视那样。如果视频文件携带了音频数据,那么ffplay会让扬声器同时播放声音。

注意,在服务器上不能通过ffplay播放音视频文件,云服务执行ffplay命令会报错Could not initialize SDL - dsp: No such audio device,这是因为服务器只提供运算功能,没接入显示器和扬声器。只有把FFmpeg安装到个人计算机上,才能正常使用ffplay播放音视频。

以播放视频为例,前提条件完成了1.3节讲述的各项安装步骤,再在Windows系统的MSYS2窗口执行以下命令,使用ffplay程序播放名叫fuzhous.mp4的视频文件。

    ffplay fuzhous.mp4

运行上面的命令之后,控制台一边弹出视频播放器窗口,如图1-15所示。

图1-15 ffplay的视频播放界面

一边回显以下文件日志信息:

    filename=fuzhous.mp4, flags=1q=   0KB vq=  0KB sq=  0B f=0/0
    proto_str=file
       Last message repeated 1 times
        Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'fuzhous.mp4':
         Metadata:
           major_brand     : isom
           minor_version   : 512
           compatible_brands: isomiso2avc1mp41
           encoder         : Lavf59.34.102
         Duration: 00:00:19.52, start: 0.000000, bitrate: 288 kb/s
         Stream #0:0[0x1](und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(progressive),
    480x270 [SAR 1:1 DAR 16:9], 285 kb/s, 25 fps, 25 tbr, 12800 tbn (default)

根据以上日志信息,可知该视频持续时间为19.52秒,视频编码器采用h264,视频分辨率为480×270,fps帧率为每秒25帧。

再来看看播放音频,以下命令表示使用ffplay程序播放名为ship.mp3的音频文件。

    ffplay ship.mp3

执行上面的命令,控制台一边弹出音频波形窗口,如图1-16所示。

图1-16 ffplay的音频播放界面

一边回显以下文件日志信息:

    filename=ship.mp3, flags=1 aq=    0KB vq=    0KB sq=    0B f=0/0
    proto_str=file
    Input #0, mp3, from 'ships.mp3':   0KB vq=    0KB sq=    0B f=0/0
     Metadata:
       title           : 渔舟唱晚
       artist          : 中国十大古典名曲
       genre           : Other
       encoder         : Lavf59.27.100
     Duration: 00:03:37.91, start: 0.025057, bitrate: 128 kb/s
     Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 128 kb/s

根据以上日志信息,可知晓该音频的标题和演唱者,以及音频的持续时间为3分37.91秒,音频编码器采用MP3,采样频率为44100Hz。

ffplay程序的更多命令行参数可通过以下命令查看,这里不再一一展开了。

    ffplay -help
3.ffprobe程序

ffprobe程序是一个音视频分析工具,它既能分析音视频的文件参数、容器参数等信息,也能分析音视频文件中每个数据包的大小、类型、编解码器等信息。

以查看文件参数为例,以下命令表示使用ffprobe查看视频文件2018.mp4的格式信息。

    ffprobe -show_format 2018.mp4

执行上面的命令,控制台回显如下的文件格式信息,斜杆后面是额外添加的说明注释。

    [FORMAT]
    filename=2018.mp4                    // 文件名
    nb_streams=2                         // 流的数量。为2表示包含视频流和音频流
    nb_programs=0
    format_name=mov,mp4,m4a,3gp,3g2,mj2  // 格式名称
    format_long_name=QuickTime / MOV     // 完整的格式名称
    start_time=0.000000                  // 开始时间,单位为秒
    duration=253.332993                  // 结束时间,单位为秒
    size=42853286                        // 文件大小,单位为字节
    bit_rate=1353263                     // 比特率,即每秒传输的比特数量(1字节有8比特)
    probe_score=100
    TAG:major_brand=isom
    TAG:minor_version=512
    TAG:compatible_brands=isomiso2avc1mp41
    TAG:encoder=Lavf57.71.100
    [/FORMAT]

因为ffprobe程序返回的文件信息直接显示在控制台,密密麻麻的,令人看得眼花缭乱,所以实际上很少使用ffprobe分析音视频,而是采用第三方专业的桌面软件加以分析,后面讲到相关格式时再介绍这些软件。

1.4.2 动态链接库

FFmpeg不仅提供了ffmpeg、ffplay和ffprobe三个可执行程序,还提供了8个工具库,方便开发者调用库中的函数,从而实现更精准的定制化需求。这8个库通常采用动态链接的方式,在Linux系统上动态库表现为SO文件,在Windows系统上动态库表现为DLL文件。这8个库的名字是avcodec、avdevice、avfilter、avformat、avutil、postproc、swresample、swscale,库名开头的a表示audio,也就是音频,库名开头的v表示video,也就是视频。下面分别对这些库展开介绍。

1.avcodec

avcodec是FFmpeg的音视频编解码库,它的源码位于libavcodec目录,在Linux系统的文件名形如libavcodec.so,在Windows系统的文件名形如avcodec-**.dll。

avcodec包含各种音频的编码库和解码库,以及各种视频的编码库和解码库。通过avcodec可以将原始的音视频数据压缩为符合某种码流规则的数据压缩包,也可以将数据压缩包按照指定的码流规则解压为原始的音视频数据。尽管avcodec内置了大部分的音视频编解码库,可是有些码流需要集成第三方的编解码库,比如视频格式H.264要求集成第三方的x264,视频格式H.265要求集成第三方的x265,音频格式MP3要求集成第三方的mp3lame等,libavcodec目录下的诸多lib***.c代码就是用来集成第三方编解码库的。

早期的FFmpeg对于音频格式AAC要求集成第三方的fdk-aac,不过最新的FFmpeg已经集成了自己的AAC编解码库,因此即使没集成fdk-aac也能正常进行AAC格式的编解码。还有一些媒体格式,虽然FFmpeg内置了该格式的编解码库,但因为依赖于特定库,所以编译时要把特定库链接进来,比如图像格式PNG的编解码就依赖于zlib库。

2.avdevice

avdevice是FFmpeg的音视频设备库,它的源码位于libavdevice目录,在Linux系统的文件名形如libavdevice.so,在Windows系统的文件名形如avdevice-**.dll。

avdevice包含音视频的各种输入输出设备库,其中输入设备指的是采集音视频信号的设备,输出设备指的是渲染音视频画面的设备。当然,FFmpeg不会直接操作设备硬件,而是通过第三方的软件包来实现,比如采集媒体信号用到了Windows平台的VFW(Video for Windows,捕捉器),以及VFW的升级版DirectShow捕捉器;渲染媒体画面用到了Windows平台的GDI(Graphics Device Interface,接收器),以及跨平台的SDL2(Simple DirectMedia Layer,媒体开发库)。当然,FFmpeg也支持音效处理库OpenAL(Open Audio Library)和图形处理库OpenGL(Open Graphics Library)。

3.avfilter

avfilter是FFmpeg的音视频滤镜库,它的源码位于libavfilter目录,在Linux系统的文件名形如libavfilter.so,在Windows系统的文件名形如avfilter-**.dll。

avfilter包含加工编辑音频和视频的各种滤镜包,其中音频滤镜的源码文件名形如af_***.c,视频滤镜的源码文件名形如vf_***.c。音频滤镜多用于调整参数、混合音频等处理,视频滤镜多用于变换视频、特效画面、添加部件等处理。

部分高级滤镜要求FFmpeg集成第三方支持库,例如水印滤镜drawtext需要集成FreeType库,字幕滤镜subtitles需要集成ASS库。

4.avformat

avformat是FFmpeg的音视频格式库,它的源码位于libavformat目录,在Linux系统的文件名形如libavformat.so,在Windows统的文件名形如avformat-**.dll。

avformat包含各类媒体文件格式库,以及各种网络通信协议库。其中格式库不仅包含视频格式MP4、AVI、MOV、3GP等,音频格式MP3、WAV、AAC、PCM等,还包含图像格式JPEG、GIF、PNG、YUV等。协议库不仅包含文件协议file,常规的通信协议HTTP、FTP、TCP、UDP等,还包含流媒体传输协议RTSP、RTMP、HLS、SRT等。

由于FFmpeg把协议层的传输操作和不同格式的解析操作都封装好了,因此它们对开发者而言是透明的,从而减轻了开发者适配不同协议和格式的负担。

5.avutil

avutil是FFmpeg的音视频工具库,它的源码位于libavutil目录,在Linux系统的文件名形如libavutil.so,在Windows系统的文件名形如avutil-**.dll。

avutil包含常见的通用工具和各类算法库,其中通用工具包括字典读写、日志记录、缓存交互、线程处理,以及加解密库AES、MD5、SHA、BASE64等;各类算法包括排队算法、排序算法、哈希表、二叉树等。除此之外,avutil也囊括色彩空间、音频采样等方面的公共函数。

6.postproc

postproc是FFmpeg的音视频后期效果处理库,它的源码位于libpostproc目录,在Linux系统的文件名形如libpostproc.so,在Windows系统的文件名形如postproc-**.dll。

postproc主要用于进行后期的效果处理,如果代码中使用了滤镜,编译时就要链接这个库,因为滤镜用到了postproc的一些基础函数。

7.swresample

swresample是FFmpeg的音频重采样库,它的源码位于libswresample目录,在Linux系统的文件名形如libswresample.so,在Windows系统的文件名形如swresample-**.dll。

swresample主要用于音频重采样的相关功能,比如把音频从单声道变为多声道,变更音频的采样频率,转换音频的数据格式等。

8.swscale

swscale是FFmpeg的视频图像转换库,它的源码位于libswscale目录,在Linux系统的文件名形如libswscale.so,在Windows系统的文件名形如swscale-**.dll。

swscale主要用于图像缩放、色彩空间转换等功能,其中色彩空间转换有时也被称作像素格式转换,比如把视频帧从YUV格式转换为RGB格式。

1.4.3 第一个FFmpeg程序

在验证FFmpeg是否成功安装时,可通过命令ffmpeg -version查看FFmpeg版本号。如果能够正确回显FFmpeg的版本信息,就表示FFmpeg已经成功安装。不过,对于开发者来说,最佳的验证方式是通过编写C代码。特别是看到自己亲手编写的代码输出Hello World时,这标志着成功迈出FFmpeg开发的第一步。下面就来介绍如何编写第一个FFmpeg程序。

众所周知,C语言有个printf函数,可以把文字信息输出到控制台,比如下面的C代码调用printf函数打印Hello World(完整代码见chapter01/helloc.c):

    #include <stdio.h>
 
    int main(int argc, char *argv[]) {
       printf("Hello World\n");
       return 0;
    }

把上面的代码保存为helloc.c文件,接着运行以下GCC命令编译可执行程序,命令格式为“gcc源代码文件名称-o可执行程序名称”。

    gcc helloc.c -o helloc

编译通过后,执行下面的命令(“./”表示位于当前目录),即可在控制台看到程序输出了一行文字Hello World,表示C程序正常运行。

    ./hello

FFmpeg框架使用av_log函数替代printf函数,顾名思义,该函数用于打印日志,默认会把日志信息打印到控制台上,相当于printf函数的日志打印功能。av_log函数的用法很简单,只要包含头文件libavutil/avutil.h,然后调用av_log函数,指定日志等级和日志内容就行。编写带日志功能的代码的详细步骤说明如下。

01 创建名为helloffmpeg.c的C代码文件,填入下面的代码内容(完整代码见chapter01/helloffmpeg.c):

    #include <stdio.h>
    #include <libavutil/avutil.h>
 
    int main(int argc, char *argv[]) {
       av_log(NULL, AV_LOG_INFO, "Hello World\n");
       return 0;
    }

02 保存并退出该文件,执行以下命令编译helloffmpeg.c:

    gcc helloffmpeg.c -o helloffmpeg -I/usr/local/ffmpeg/include -L/usr/local/ffmpeg/lib
-lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswscale -lswresample -lpostproc -lm

编译命令中的-I指定了头文件的存放目录,-L指定了链接库的存放目录,-l指定了编译过程需要链接哪些库,比如上面的命令要求链接avformat、avdevice、avfilter、avcodec、avutil、swscale、swresample、postproc这8个FFmpeg动态库,另外还链接了系统自带的m库(表示math库,也就是数学函数库)。

03 运行编译好的helloffmpeg程序,也就是执行以下命令:

    ./helloffmpeg

发现控制台回显日志信息Hello World,这表明测试程序运行正常,说明FFmpeg开发环境已经成功搭建。

04 刚才的测试程序helloffmpeg.c采用C语言编写,并且使用GCC编译。若要采用C++编程,则需改成下面的helloffmpeg.cpp代码(完整代码见chapter01/helloffmpeg.cpp):

        #include <iostream>  // C++使用iostream代替stdio.h
 
        // 因为FFmpeg源码使用C语言编写,所以若是在C++代码中调用FFmpeg,则要通过标记extern "C"{…}把
    FFmpeg的头文件包含进来
        extern "C"
        {
        #include <libavutil/avutil.h>
        }
 
        int main(int argc, char** argv) {
           av_log(NULL, AV_LOG_INFO, "Hello World\n");
           return 0;
        }

鉴于C++代码采用G++编译,那么编译helloffmpeg.cpp的编译命令如下所示:

    g++ helloffmpeg.cpp -o helloffmpeg -I/usr/local/ffmpeg/include -L/usr/local/ffmpeg/lib
-lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswscale -lswresample -lpostproc -lm

编译完毕后,同样生成名为helloffmpeg的可执行程序,如此就实现了C++代码集成FFmpeg函数的目标。

不过extern "C"标记只能在CPP代码中使用,如果在C代码中写入extern "C"并且使用GCC来编译的话,GCC会报错error: expected identifier or '(' before string constant,意思是它不认识这个标记。这种情况属于GCC和G++的编译差别,主要体现在下列几个方面:

(1)G++在编译C代码和CPP代码时,都采用C++的语法来编译;而GCC对于C代码采用C语言的语法编译,对于CPP代码采用C++的语法编译(GCC也能编译C++程序)。

(2)GCC不能自动链接C++库,而G++会自动链接C++库,所以通过GCC编译C++代码时,要记得链接stdc++库。

如果希望C代码既能通过GCC编译,也能通过G++编译,就要引入宏__cplusplus。一旦定义了这个宏,表示当前采用C++的语法编译,就得添加extern "C"标记;否则表示当前采用C语言的语法编译,无须添加extern "C"标记。可将helloffmpeg.c的代码补充完善,并将修改后的代码另存为hellofull.c,具体代码示例如下(完整代码见chapter01/hellofull.c):

    #include <stdio.h>
 
    //libavutil/common.h要求定义,否则会报错:error missing -D__STDC_CONSTANT_MACROS
    #define __STDC_CONSTANT_MACROS
 
    // 之所以增加__cplusplus的宏定义,是为了同时兼容GCC编译器和G++编译器
    #ifdef __cplusplus
    extern "C"
    {
    #endif
    #include <libavutil/avutil.h>
    #ifdef __cplusplus
    };
    #endif
 
    int main(int argc, char** argv) {
       av_log(NULL, AV_LOG_INFO, "Hello World\n");
       return 0;
    }

然后分别运行下面两行GCC和G++编译命令,发现均能编译通过,表示该代码同时兼容C语言的语法和C++的语法。

    gcc hellofull.c -o hellofull -I/usr/local/ffmpeg/include -L/usr/local/ffmpeg/lib
-lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswscale -lswresample -lpostproc -lm
    g++ hellofull.c -o hellofull -I/usr/local/ffmpeg/include -L/usr/local/ffmpeg/lib
-lavformat -lavdevice -lavfilter -lavcodec -lavutil -lswscale -lswresample -lpostproc -lm

注意,前面调用av_log函数时,第二个参数都填作AV_LOG_INFO,该参数值表示标准信息,用于标明当前日志的日志等级,详细的日志等级说明见表1-4。

表1-4 FFmpeg的日志等级说明

之所以要设置这些日志等级,是为了更好地管理输入日志,主要体现在以下两个方面:

(1)给不同等级的日志文字显示不同的颜色,有利于快速找到警告、错误等重要日志。

(2)能够通过av_log_set_level函数来设置打印的日志等级,默认只会打印AV_LOG_INFO和更高级别的日志。如果调用av_log_set_level函数设置了其他的日志级别,那么只会打印该级别以及更高级别的日志信息。

为了更好地观察日志的输出情况,打开helloffmpeg.c,在av_log之前增加调用av_log_set_level函数,修改后的代码如下:

    #include <stdio.h>
    #include <libavutil/avutil.h>
 
    int main(int argc, char** argv) {
       av_log_set_level(AV_LOG_TRACE);
       av_log(NULL, AV_LOG_INFO, "Hello World\n");
       return 0;
    }

然后更改av_log函数的日志等级参数,编译并运行程序,即可观察对应的日志输出情况。 kAjnMVErliRu5d79f1gOErNhrWMMzPWY74ekXn0WWpbZi6wn1FEmGUJtQyPpoQHU

点击中间区域
呼出菜单
上一章
目录
下一章
×