本系列文章居于FFMpeg源码4.1版本,因此,有些流程和老版本会有稍微差别(源码我做了详细注释,有需要请在下方评论留言,写明邮箱,我会统一发给你们)。
FFMpeg的使用网上介绍的很多了,这里主要采用结构化思维的方式来对FFMpeg的使用进行介绍,让大家有一个整体到部分概念,能够比较清楚知道使用FFMpeg的流程以及使用中需要注意的东西。
这一节先从整体介绍我们使用FFMpeg主要完成的功能,以及在这一个过程中有那些很关键的点需要注意和单独进行说明的。
我们知道FFMpeg主要就是处理音视频的,那么音视频不外乎就是播放和编码,那么我们就从这两个方面进行说明,然后依次分析出播放和编码中主要的关键步骤。
1. 视频播放
下图描述FFMpeg播放视频文件的标准流程:
上图中红框里的流程对于视频和音频调用是一样的,不同是传入的解码器不一样,从这里看出,FFMpeg对音视频的封装还是很到位的,对音视频的处理比较统一,这也不难看出FFMpeg其实就是一个上层的音视频处理框架,具体的交由对应封装格式处理模块以及音视频编解码模块处理。
从上图看到有两个关键功能点:解码后过滤器对数据的过滤处理;音视频播放时同步策略。
1.1 FFMpeg过滤器
在FFMpeg中正确对音视频解码后,获取的数据存放在AVFrame中,视频对应的裸数据格式为YUV,音频对应的裸数据格式为PCM。
对于视频和音频数据在送入播放设备播放之前,可能需要对裸数据进行效果叠加处理,比如视频需要叠加水印,音频需要变声处理,对于这些需求,FFMpeg提供完整的插件化支持,这就是FFMpeg中的过滤器功能,具体我们会利用几节来详细说下FFMpeg中的过滤器。
1.2 FFMpeg音视频播放同步
音视频同步是视频播放中很核心的功能,其同步的原理就是选定一个时钟基准,然后其它的流的时钟同步到这个选定的时钟基准,播放慢的加快,播放快的减慢,这样音视频的播放就会一直,不会造成声音说了视频还没播放的现象。
目前音视频同步主要有三种:同步到音频、同步到视频、同步到外部时钟。
在FFMpeg提供的默认播放器里三种都实现了,但是默认使用的是同步到音频,因为人对声音的敏感度比对图像的敏感度要求要高,因此,绝大多数视频播放器都选择同步到音频,因为声音是连续的,对其要求很高。
同步的细节会单独说明,这里简单提几个同步相关的概念:I帧、B帧、P帧以及PTS、DTS。
I帧
I帧又称帧内编码帧,是一种带有图像全部信息的独立帧,因此,I帧无需参考其它帧便可独立进行解码,这里可以简单理解为一张静态画面。视频序列中的第一个帧始终都是I帧,因为它是关键帧。
P帧
P帧又叫做帧间预测编码帧,需要参考前面的I帧或P帧才能进行编码。表示的是当前帧画面与前一帧(前一帧可能是I帧也可能是P帧)的差别。解码时需要用之前缓存的画面叠加上本帧定义的差别,生成最终画面。与I帧相比,P帧通常占用更少的数据位,但不足是,由于P帧对前面的P和I参考帧有着复杂的依耐性,因此容错性比较低。
B帧
B帧又称双向预测编码帧,也就是B帧记录的是本帧与前后帧的差别。也就是说要解码B帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的与本帧数据的叠加取得最终的画面。B帧压缩率高,但是对解码性能要求较高。
DTS(Decoding Time Stamp):即解码时间戳,播放器利用该值计算在什么时候解码这一帧的数据。
PTS(Presentation Time Stamp):即显示时间戳,播放器利用该值计算在什么时候显示这一帧的数据。
对于音频来说,DTS和PTS是相同的,但是对于视频流来说,不存在B帧,其DTS和PTS也是相同,如果存在B帧,则DTS和PTS就会不一样。
2. 视频编码
视频编码什么情况会用到呢?
比如我们把一种视频格式转换到另外一种有可能会用到,对音视频的录制保存也会对音视频先进行编码,然后存储到文件或发送到服务器,其实,在FFMpeg中,音视频的编码和音视频的播放,在流程上比较类似,下面先看看音视频编码的整体流程图。
上图红框里的流程视频编码和音频编码都是一样的,循环处理直到把所有需要编码的数据处理完,然后退出,接着调用av_write_trailer写入尾部文件信息。
上面调用的函数比较简单,我们主要说下视频和音频编码参数的配置,这个比较关键,因为我们具体要把数据编码成什么格式,涉及到编码后数据的大小和解码。
其中对于音视频流都必须要初始化AVCodexContext结构,这个结构可以从流AVStream中的codecpar里复制过来,因此,我们只说结构AVCodexContext的参数配置。
codec_type:AVMEDIA_TYPE_AUDIO;
codec_id:如果是AAC编码,填充AV_CODEC_ID_H264,其它参考AVCodecID定义;
codec_tag:填充为0;
bit_rate:比特率(每秒传输的bit数)64000表示64KB;
bits_per_coded_sample:表示每个采样点可编码使用多少位存储,一般是0,对于ADPCM可以是4或8,这里填充0;
bits_per_raw_sample:原始每个采样点使用多少位存储,对于音频来说,可以是8、16、24、32等,这个值和sample_fmt有关;
format:图像像素格式,由AVSampleFormat定义的,这里填充AV_SAMPLE_FMT_S16 ;
channel_layout:声道通道数排列方式
channels:声道通道数,其实由channel_layout可以计算出其通道数的,这里填充2;
frame_size:每帧有多少个采样点
initial_padding:在开始处的一段填充数据,可以设置为0,可以设置为frame_size大小;
codec_type:AVMEDIA_TYPE_VIDEO;
codec_id:如果是h264编码,填充AV_CODEC_ID_H264,其它参考AVCodecID定义;
codec_tag:填充为0;
bit_rate:比特率(每秒传输的bit数)250600表示250KB;
bits_per_coded_sample:表示每个采样点编码使用多少位存储,对于视频流来说,比如RGB24,这个值是24,因为每个采样点包括三个分量R、G、B;
bits_per_raw_sample:原始每个采样点位深度,对于视频流来说就是每个颜色分量占用的位数,对于RGB24,就是8,因此这个值和format有关,由具体编码器根据format计算,也可以这样计算pixdesc = av_pix_fmt_desc_get(format); bits_per_raw_sample = pixdesc.comp[0].depth;
format:图像像素格式,由AVPixelFormat定义的,这里填充AV_PIX_FMT_YUV420P
width:图像宽度
height:图像高度