大家使用播放器播放MP4文件时,可以看到画面,也可以听到声音,这里就涉及到几个知识点:
- MP4文件是什么?
- 画面怎么来的?
- 声音怎么来的?
先来讲讲画面是什么。我们最容易理解的画面就是一张图片,一张图片是静止的,如果把一张张图片拼接起来,就会形成动态效果。微信上的动态表情就是由一张张图片拼接起来形成的gif动图。
MP4文件中的画面就是由一帧帧的画面组成的,每一帧就类似于一张图片。但是有个问题,每张图片所占用的空间比较大,如果直接把它们组合在一起,会形成一个非常大的文件,那就不方便文件的存储和传播了。所以我们需要一些方式来把这些图片进行压缩,类似于把一个1M大小的txt文本文件一压缩就可能不到1K大小了,压缩了1000倍。这个技术在视频领域就叫“编码”,对应的解压过程就叫“解码”。视频编解码目前最常用的就是H264技术。声音也类似画面,其中的编解码一般用的是AAC。
有了这些连续的画面和声音,得需要有个东西或者说容器把这些东西装起来,这个容器就是MP4。我们还会看到FLV,MKV文件,与MP4一样也是容器,只是他们的规则有些不同而已。类似word可以把文字和图片装在一个文件里面,pdf也可以做相同的事情。
现在大部分视频都采用H264协议,这个协议最主要的好处是高的视频压缩比和良好的网络亲和性。协议很复杂,我们只说说它的几个关键知识:I、P、B、SPS、PPS帧,码率,分辨率。
我们回想一下看视频的场景,一般情况下,一段场景内变化的图像都不会太多,比如直播视频中,一般都只有主播的嘴在动,背景啊,服饰什么的都基本不会变化。参照一段时间内图像的统计结果表明,在相邻几幅图像画面中,一般有差别的像素只有10%以内的点,亮度差值变化不超过2%,而色度差值的变化只有1%以内。所以对于一段变化不大图像画面,我们可以先编码出一个完整的图像帧A,随后的B帧就不编码全部图像,只写入与A帧的差别,这样B帧的大小就只有完整帧的1/10或更小!B帧之后的C帧如果变化不大,我们可以继续以参考B的方式编码C帧,这样循环下去。这段图像我们称为一个序列(序列就是有相同特点的一段数据),当某个图像与之前的图像变化很大,无法参考前面的帧来生成,那我们就结束上一个序列,开始下一段序列,也就是对这个图像生成一个完整帧A1,随后的图像就参考A1生成,只写入与A1的差别内容。如此循环......
接下来来一段比较专业化的描述:在H264中图像以序列为单位进行组织,一个序列是一段图像编码后的数据流,以I帧开始,到下一个I帧结束。一个序列的第一个图像叫做 IDR 图像(立即刷新图像),IDR 图像都是 I 帧图像。H.264 引入 IDR 图像是为了解码的重同步,当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样,如果前一个序列出现重大错误,在这里可以获得重新同步的机会。IDR图像之后的图像永远不会使用IDR之前的图像的数据来解码。一个序列就是一段内容差异不太大的图像编码后生成的一串数据流。当运动变化比较少时,一个序列可以很长,因为运动变化少就代表图像画面的内容变动很小,所以就可以编一个I帧,然后一直P帧、B帧了。当运动变化多时,可能一个序列就比较短了,比如就包含一个I帧和3、4个P帧。
I帧是关键帧,你可以理解为这一帧画面的完整保留;P帧是前向预测编码帧,表示的是这一帧跟之前的一个关键帧(或P帧)的差别;B帧是双向预测内插编码帧,也就是B帧记录的是本帧与前后帧的差别。一般直播场景下都会把B帧丢掉,以节省带宽。
另外还有SPS(序列参数集)和PPS(图像参数集)两种类型的帧,其包含了初始化H.264解码器所需要的信息参数,包括编码所用的profile,level,图像的宽和高,deblock滤波器等,这两类帧一种出现在H264文件前面;在直播场景下,一般会在I帧之前都发送这两类帧,以便接收端能从中途判定流媒体的参数。