一 FFmpeg的主体结构
image.png
默认的编译会生成4
个可执行文件和8
个静态库。可执行文件包括用于转码
、推流
、Dump媒体文件的ffmpeg
、用于播放媒体文件的ffplay
、
用于获取媒体文件信息的ffprobe
,以及作为简单流媒体服务器的ffserver
。
8个静态库其实就是FFmpeg的8个模块,具体包括如下内容。
AVUtil
核心工具库,该模块是最基础
的模块之一,下面的许多undefined其他模块都会依赖该库做一些基本的音视频处理操作。AVFormat
文件格式和协议库,该模块是最重要的模块之一,封 装了Protocol层和Demuxer、Muxer层,使得协议和格式对于开发者来说是透明的。AVCodec
编解码库,该模块也是最重要的模块之一,封装了 Codec层,但是有一些Codec是具备自己的License的,FFmpeg是不会默认添加像libx264、FDK-AAC、lame等库的,但是FFmpeg就像一个平台 一样,可以将其他的第三方的Codec以插件的方式添加进来,然后为开 发者提供统一的接口。AVFilter
音视频滤镜库,该模块提供了包括音频特效和视频特效的处理,在使用FFmpeg的API进行编解码的过程中,直接使用该模块为音视频数据做特效处理是非常方便同时也非常高效的一种方式。AVDevice
输入输出设备库,比如,需要编译出播放声音或者视频的工具ffplay
,就需要确保该模块是打开的,同时也需要libSDL的预先编译,因为该设备模块播放声音与播放视频使用的都是libSDL库。SwrRessample
该模块可用于音频重采样
,可以对数字音频进行声道数、数据格式、采样率等多种基本信息的转换。SWScale
该模块是将图像进行格式转换
的模块,比如,可以将 YUV的数据转换为RGB的数据。PostProc
该模块可用于进行后期处理,当我们使用AVFilter的时候需要打开该模块的开关,因为Filter中会使用到该模块的一些基础函数。
比如AAC编码,常见的有两种封装格式
- 一种是ADTS格式的流,是AAC定义在MPEG2里面的格式
- 另外一种是封装在MPEG4里面的格式,这种格式会在每一帧前面拼接一个用声道、采样率等信息组成的头。
AAC
的bit stream filter
常常应用在编码
的过程中。
与音频的AAC编码格式相对应的是视频中的H264编码
,它也有两种封装格式
- 一种是 MP4封装的格式
- 一种是裸的H264格式(一般称为annexb封装格式)
FFmpeg中也提供了对应的`bit stream
filter,称
H264_mp4toannexb`,可以将MP4封装格式的H264数据包转换为annexb封装格式的H264数据
(其实就是裸的H264的数据)包。
H264
的bit stream filter
常常应用于视频解码过程中。
二 FFmpeg命令行工具的使用
ffmpeg
是进行媒体文件转码的命令行工具
ffprobe
是用于查看媒体 文件头信息的工具
ffplay
则是用于播放媒体文件的工具
2.1 ffprobe
1.首先用ffprobe查看一个音频的文件
代码语言:txt复制ffprobe ~/Desktop/32037.mp3
2.输出格式信息format_name、时间长度duration、文件 大小size、比特率bit_rate、流的数目nb_streams等。
代码语言:txt复制ffprobe -show_format 32037.mp4
3.以JSON格式的形式输出具体每一个流最详细
的信息
ffprobe -print_format json -show_streams 32037.mp4
4.显示帧信息的命令如下:
代码语言:txt复制ffprobe -show_frames sample.mp4
5.查看包信息的命令如下:
代码语言:txt复制ffprobe -show_packets sample.mp4
2.2 ffplay
ffplay是以FFmpeg框架为基础,外加渲染音视频 的库libSDL来构建的媒体文件播放器。
业界内开源的ijkPlayer
其实就是基于ffplay
进行改造的播放器,当然其做了硬件解码以及很多兼容性的工作。
音视频同步
在 ffplay中音画同步的实现方式其实有三种。分别是
- 以
音频
为主时间轴 作为同步源 - 以
视频
为主时间轴作为同步源 - 以
外部时钟
为主时间轴作为同步源
并且在ffplay
中默认的对齐方式也是以音频
为基准进行对齐的。
首先要声明的是,播放器接收到的视频帧或者音频帧,内部都会有时间戳(PTS时钟)
来标识它实际应该在什么时刻进行展示。
实际的对齐策略如下:比较视频当前的播放时间和音频当前的播放时间
- 如果视频播放
过快
,则通过加大延迟或者重复播放来降低视频播放速度; - 如果视频播
放慢
了,则通过减小延迟或者丢帧来追赶音频播放的时间点。
关键就在于音视频时间的比较以及延迟的计算,当然在比较的过程中会设 置一个阈值(Threshold)
,若超过预设的阈值就应该做调整(丢帧渲染
或者重复渲染),这就是整个对齐策略。
2.3 ffmpeg
ffmpeg
就是强大的媒体文件转换工具。它可以转换任何格式的媒体文件,并且还可以用自己的AudioFilter
以及VideoFilter
进行处理和编辑。
- 从MP4文件中抽取视频流导出为裸H264数据
ffmpeg -i output.mp4 -an -vcodec copy -bsf:v h264_mp4toannexb output.h264
- 使用AAC音频数据和H264的视频生成MP4文件
ffmpeg -i test.aac -i test.h264 -acodec copy -bsf:a aac_adtstoasc -vcodec copy -f mp4 output.mp4
- 从WAV音频文件中导出PCM裸数据
ffmpeg -i input.wav -acodec pcm_s16le -f s16le output.pcm
- 将两路声音进行合并,比如要给一段声音加上背景音乐
ffmpeg -i vocal.wav -i accompany.wav -filter_complex
代码语言:txt复制 amix=inputs=2:duration=shortest output.wav
- 为视频增加水印效果
ffmpeg -i input.mp4 -i changba_icon.png -filter_complex
代码语言:txt复制 '[0:v][1:v]overlay=main_w-overlay_w-10:10:1[out]' -map '[out]' output.mp4
- 将一个YUV格式表示的数据转换为JPEG格式的图片
ffmpeg -f rawvideo -pix_fmt yuv420p -s 480*480 -i texture.yuv -f image2-vcodec mjpeg output.jpg
三 FFmpeg API的介绍与使用
3.1 术语
容器/文件(Conainer/File)
即特定格式的多媒体文件,比如MP4
、flv
、mov
等。媒体流(Stream)
表示时间轴上的一段连续数据,如一段声音数 据、一段视频数据或一段字幕数据,可以是压缩的,也可以是非压缩的,压缩的数据需要关联特定的编解码器
。数据帧/数据包(Frame/Packet)
通常,一个媒体流是由大量的数据帧组成的,对于压缩数据,帧
对应着编解码器
的最小处理单元,分属于不同媒体流的数据帧交错存储于容器之中。编解码器
编解码器是以帧
为单位实现压缩数据
和原始数据
之间的相互转换的。
3.2 名词介绍
AVFormatContext
就是对容器或者说媒体文件层次的一个抽象。AVStream
对流的抽象AVCodecContext
与AVCodec
对编解码格式以及编解码器的抽象AVPacket
与AVFrame
对于编码器或者解码器的输入输出部分,也就是压缩数据以及原始数据的抽象。AVFilter
对于音视频的处理肯定是针对于原始数据的处理,也就是针对于AVFrame
的处理。
3.3 实例
接下来介绍一个解码的实例,该实例实现的功能非常单一,就是把一个视频文件解码成单独的音频PCM文件和视频YUV文件。
- 引用头文件
- 注册协议、格式与编解码器
avformat_network_init();
代码语言:txt复制av_register_all();
- 打开媒体文件源,并设置超时回调
- 寻找各个流,并且打开对应的解码器
- 初始化解码后数据的结构体undefined分配出解码之后的数据所存放的内存空间,以及进行格式转换需要用到的对象
- 读取流内容并且解码undefined打开了解码器之后,就可以读取一部分流中的数据(
压缩数据
),然后将压缩数据作为解码器
的输入,解码器将其解码为原始数据
(裸数据
),之后就可以将原始数据写入文件了。 - 处理解码后的裸数据undefined解码之后会得到裸数据,音频就是
PCM
数据,视频就是YUV
数据 - 关闭所有资源
四 FFmpeg源码结构
4.1 libavformat
image.png
AVFormatContext是API层直接接触到的结构体,它会进行格式的封 装与解封装。
4.2 libavcodec
image.png
该结构体包含的就是与实际的编解码
有关的部分。
3.3 FFmpeg通用API分析
3.3.1 av_register_all
所以该函数的内部实现会先调用avcodec_register_all
来注册所有config.h里面开放的编解码器,然后会注册所有的Muxer
和Demuxer
(也就是封装格式),最后注册所有的Protocol
(即协议层的东西)。
3.3.2 av_find_codec
这里面其实包含了两部分的内容:一部分是寻找解码器
,一部分是寻找编码器
。
3.3.3 avcodec_open2
该函数是打开编解码器(Codec)的函数,无论是编码过程还是解码过程,都会用到该函数。
3.4 调用FFmpeg解码时用到的函数分析
avformat_open_input
根据所提供的文件路径判断文件的格 式,其实就是通过这一步来决定使用的到底是哪一个Demuxer
。
avformat_find_stream_info
该方法的作用就是把所有Stream
的MetaData
信息填充好。
av_read_frame
使用该方法读取出来的数据是AVPacket
。
对于音频流
,一个AVPacket
可能包含多
个AVFrame
,但是对于视频流
,一个AVPacket
只包含一
个AVFrame
,该函数最终只会返回一个AVPacket
结构体。
avcodec_decode
该方法包含了两部分内容:一部分是解码视频
,一部分是解码音频
,解码
是会委托给对应的解码器来实施的。
avformat_close_input
该函数负责释放对应的资源。
3.5 调用FFmpeg编码时用到的函数分析
avformat_alloc_output_context2
该函数内部需要调用方法avformat_alloc_context来分配一个 AVFormatContext
结构体。
avio_open2
编码的阶段了,开发者需要将手动封装好的AVFrame
结构体,作为avcodec_encode_video
方法的输入,将其编码成为AVPacket
,然后调用av_write_frame
方法输出到媒体文件中。
本文参考音视频开发进阶指南
[项目源码地址 -
FFmpegDecoder](https://links.jianshu.com/go?to=https://github.com/zhanxiaokai/iOS-
FFmpegDecoder)