视频经过编码之后,生动活泼的一帧帧图像就变成了一串串让人看不懂的二进制。这个二进制可以放在一个文件里,然后按照一定的格式保存起来,这里的保存格式,就是系列名词一。
编码后的二进制文件就可以通过某种网络协议进行封装,放在互联网上传输,这个时候就可以进行网络直播了。
网络协议将编码好的视频流,从主播端推送到服务器,在服务器上有个运行了同样协议的服务端来接收这些网络数据包,从而得到里面的视频流,这个过程称为接流。
服务端接到视频流之后,可以滴视频流进行一定的处理,比如转码,也就是从一个编码格式转成另一种格式,这样才能适应各个观众使用的客户端,保证他们都能看到直播。
流处理完毕后,就可以等待观众的客户端来请求这些视频流。观众的客户端请求视频流的过程称为拉流。
如果有非常多的观众同时看一个视频直播,都从一个服务器上拉流,压力就非常大,因此需要一个视频的分发网络,将视频预先加载到就近的边缘节点,这样大部分观众就能通过边缘节点拉取视频,降低服务器的压力。
当观众将视频流拉下来后,就需要进行解码,也就是通过上述过程的逆过程,将一串串看不懂的二进制转变成一帧帧生动的图片,在客户端播放出来。
整个直播过程,可以用下图来描述:
接下来,我们依次来看一下每个过程:
编码:将丰富多彩的图片变成二进制流虽然我们说视频是一张张图片的序列,但如果每张图片都完整,就太大了,因而会将视频序列分成三种帧:
I帧,也称关键帧。里面是完整的图片,只需要本帧数据,就可以完成解码。
P帧,前向预测编码帧。P 帧表示的是这一帧跟之前一个关键帧(或 P 帧)的差别,解码时需要用之前缓存的画面,叠加上和本帧定义的差别,生成最终画面。
B帧,双向预测内插编码帧。B 帧记录的是本帧与前后帧的差别。要解码 B 帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的数据与本帧数据的叠加,取得最终的画面。
可以看出,I 帧最完整,B 帧压缩率最高,而压缩后帧的序列,应该是 IBBP 间隔出现。这就是通过时序进行编码。
在一帧中,分成多个片,每个片中分成多个宏块,每个宏块分成多个子块,这样将一张大图分解成一个个小块,可以方便进行空间上的编码。如下图:
尽管时空非常立体的组成了一个序列,但总归还是要压缩成一个二进制流。这个流是有结构的,是一个个的网络提取层单元(NALU,Network Abstraction Layer Unit)。变成这种格式就是为了传输,因为网络上的传输,默认的是一个个的包,因而这里也就分成了一个个的单元。
如上图,每个 NALU 首先是一个起始标识符,用于标识 NALU 之间的间隔。然后是 NALU 的头,里面主要配置了 NALU 的类型。最后的 Payload 里面是 NALU 承载的数据。
在 NALU 头里面,主要的内容是类型 NAL Type,其中:
0x07 表示 SPS,是序列参数集,包括一个图像序列的所有信息,如图像尺寸、视频格式等。
0x08 表示 PPS,是图像参数集,包括一个图像的所有分片的所有相关信息,包括图像类型、序列号等。
在传输视频流之前,剥削要传输者两类参数,不然就无法解码。为了保证容错性,每一个 I 帧之前,都会传一遍这两个参数集合。
如果 NALU Header 里面的表示类型是 SPS 或 PPS,则 Payload 中就是真正的参数集的内容。
如果类型是帧,则 Payload 中是真正的视频数据。当然也是一帧帧保存的。前面说了,一帧的内容还是挺多的,因而每一个 NALU 里面保存的是一片。对于每一片,到底是 I 帧,还是 P 帧,亦或是 B 帧,在片结构里面也有 Header,这里面有个类型用来标识帧的类型,然后是片的内容。