三个名词系列
- 名词系列一:AVI、MPEG、RMVB、MP4、MOV、FLV、WebM、WMV、ASF、MKV。例如 RMVB 和 MP4,看着是不是很熟悉?
- 名词系列二:H.261、 H.262、H.263、H.264、H.265。这个是不是就没怎么听过了?别着急,你先记住,要重点关注 H.264。
- 名词系列三:MPEG-1、MPEG-2、MPEG-4、MPEG-7。MPEG 好像听说过,但是后面的数字是怎么回事?是不是又熟悉又陌生?
视频是什么?其实就是快速播放连串连续的图片
视频和图片的压缩过程有什么特点?
之所以能够对视频流中的图片进行压缩,因为视频和图片有这样一些特点。
1、空间冗余:图像的相邻像素之间有较强的相关性,一张图片相邻像素往往是渐变的,不是突变的,没必要每个像素都完整地保存,可以隔几个保存一个,中间的用算法计算出来。
2、时间冗余:视频序列的相邻图像之间内容相似。一个视频中连续出现的图片也不是突变的,可以根据已有的图片进行预测和推断。
3、视觉冗余:人的视觉系统对某些细节不敏感,因此不会每一个细节都注意到,可以允许丢失一些数据。
4、编码冗余:不同像素值出现的概率不同,概率高的用的字节少,概率低的用的字节多,类似霍夫曼编码(Huffman Coding)的思路。
编码过程:
视频编码的两大流派
ITU(International Telecommunications Union)的VCEG(Video Codding Experts Group),称为国际电联下的 VCEG。侧重传输。名词系列二,就是这个组织制定的标准。
ISO(International Standards Organization)的MPEG(Moving Picture Experts Group),这个是ISO 旗下的 MPEG。侧重存储。名词系列三,就是这个组织制定的标准。
后来,ITU-T(ITU Telecommunication Standardization Sector)与MPEG联合制定了H.264/MPEG-4 AVC。
经过编码后 ,图像便成为二进制,这个二进制可以放在一个文件里面,按照一定的格式保存起来,这就是名词系列一。
如何在直播里看到帅哥美女?
直播过程
- 编码、推流与接流:网络协议将编码好的视频流,从主播端推送到服务器,在服务器上有个运行了同样协议的服务端来接收这些网络包,从而得到里面的视频流,这个过程称为接流。
- 流处理、转码:服务端接到视频流之后,可以对视频流进行一定的处理,例如转码,从一个编码格式,转成另一种格式,以满足不同客户端的需求。
- 分发、拉流:流处理完毕之后,就可以等待观众的客户端来请求这些视频流。观众的客户端请求的过程称为拉流。
- 为降低大量客户端拉流产生的服务器压力,需要一个视频分发网络,将视频预先加载到就近的边缘节点。
- 解码、播放:当观众的客户端将视频流拉下来之后,就需要进行解码,将一串串看不懂的二进制,再转变成一帧帧生动的图片
编码:如何将丰富多彩的图片变成二进制流?
####将视频序列分成三种帧:
- I帧,也称关键帧。里面是完整的图片,只需要本帧数据,就可以完成解码。
- P帧,前向预测编码帧。记录此帧与前一帧的差别,解码需要之前缓存的画面,叠加上和本帧定义的差别,生成最终画面。
- B帧,双向预测内插编码帧。记录本帧与前后两帧的差别。要解码 B 帧,不仅要取得之前的缓存画面,还要解码之后的画面,通过前后画面的数据与本帧数据的叠加,取得最终的画面。
I 帧最完整,B 帧压缩率最高,而压缩后帧的序列,应该是在 IBBP 的间隔出现的。这就是通过时序进行编码。
帧的组成:
一个帧分成多个片,片分成多个宏块,宏分成多个子块,方便进行空间上的编码。
编码后,需压缩成流,是一个个的网络提取层单元(NALU,Network Abstraction Layer Unit),方便网络上的传输,因为网络传输默认是包。
ALU起始标识符标识NALU之间的间隔;NALU头里配置了NALU 类型;Payload里是承载的数据。
NALU头里,主要的内容是类型NAL Type。
- 0x07 表示 SPS,序列参数集,包括图像序列的所有信息,如图像尺寸、视频格式等。
- 0x08 表示 PPS,图像参数集,包括图像的所有分片的相关信息,如图像类型、序列号等。
在传输视频流之前,必须要传输这两类参数,不然无法解码。为了保证容错性,每一个 I 帧前面,都会传一遍这两个参数集合。
如果NALU Header里表示的类型是SPS或PPS,则Payload里就是真正参数集的内容。
如果类型是帧,则Payload里是真正的视频数据。一帧的内容还是挺多的,因而每一个 NALU 里面保存的是一片。对于每一片,到底是 I 帧,还是 P 帧,还是 B 帧,在片结构里面也有个 Header,这里面有个类型,然后是片的内容。
一个视频,可以拆分成一系列的帧,每一帧拆分成一系列的片,每一片都放在一个 NALU 里面,NALU 之间都是通过特殊的起始标识符分隔,在每一个 I 帧的第一片前面,要插入单独保存 SPS 和 PPS 的 NALU,最终形成一个长长的 NALU 序列。
推流:如何把数据流打包传输到对端?
可参看 : RTMP协议分析及H.264打包原理
使用RTMP协议,将这个二进制的流打包成网络包进行发送。RTMP基于TCP,双方需要建立一个TCP的连接。在有 TCP 的连接的基础上,还需要建立一个 RTMP 的连接。
RTMP为什么需要建立一个单独的连接?因为需要商量一些事情,保证之后传输的正常进行。主要商量版本号和时间戳。
客户端:
- 客户端发送 C0 表示自己的版本号
- 不必等对方的回复,然后发送 C1 表示自己的时间戳
- 客户端收到 S1 的时候,发一个知道了对方时间戳的 ACK C2。
服务器
- 服务器只有在收到 C0 的时候,才能返回 S0,表明自己的版本号,如果版本不匹配,可以断开连接。
- 服务器发送完 S0 后,也不用等什么,就直接发送自己的时间戳 S1。
- 服务器收到 C1 的时候,发一个知道了对方时间戳的 ACK S2。
握手之后,双方需要互相传递一些控制信息,例如 Chunk 块的大小、窗口大小等。
真正传输数据的时候,还是需要创建一个流 Stream,然后通过这个 Stream 来推流 publish。
推流的过程,就是将NALU放在Message里传送,称RTMP packet包。
Message的格式如下图所示:
发送时,去掉NALU的起始标识符。将SPS和PPS参数集封装称一个RTMP包发送,然后发送一个个片的NALU。
RTMP 在收发数据的时候并不是以 Message 为单位的,而是把 Message 拆分成 Chunk 发送,而且必须在一个 Chunk 发送完成之后,才能开始发送下一个 Chunk。
每个 Chunk 中都带有 Message ID,表示属于哪个 Message,接收端也会按照这个 ID 将 Chunk 组装成 Message。
例子:
假设一个视频的消息长度为 307,但是 Chunk 大小约定为 128,于是会拆分为三个 Chunk。
-
第一个 Chunk 的 Type=0,表示 Chunk 头是完整的;头里面 Timestamp 为 1000,总长度 Length 为 307,类型为 9,是个视频,Stream ID 为 12346,正文部分承担 128 个字节的 Data。
-
第二个 Chunk 也要发送 128 个字节,Chunk 头由于和第一个 Chunk 一样,因此采用 Chunk Type=3,表示头一样就不再发送了。
-
第三个 Chunk 要发送的 Data 的长度为 307-128-128=51 个字节,还是采用 Type=3。
推流过程:
为降低服务器压力,需要有分发网络。
分发网络分为中心和边缘两层。边缘层服务器部署在全国各地及横跨各大运营商里,和用户距离很近。中心层是流媒体服务集群,负责内容的转发。
智能负载均衡系统,根据用户的地理位置信息,就近选择边缘服务器,为用户提供推 / 拉流服务。中心层也负责转码服务。
分发网络:
拉流:观众的客户端如何看到视频?
客户端通过RTMP拉流过程
先读到的是 H.264 的解码参数,例如 SPS 和 PPS,然后对收到的 NALU 组成的一个个帧,进行解码,交给播发器播放,一个绚丽多彩的视频画面就出来了。
小结:
视频名词比较多,编码两大流派达成了一致,都是通过时间、空间的各种算法来压缩数据;
压缩好的数据,为了传输组成一系列 NALU,按照帧和片依次排列;
排列好的 NALU,在网络传输的时候,要按照 RTMP 包的格式进行包装,RTMP 的包会拆分成 Chunk 进行传输;
推送到流媒体集群的视频流经过转码和分发,可以被客户端通过 RTMP 协议拉取,然后组合为 NALU,解码成视频格式进行播放。
参考资料:
趣谈网络协议(极客时间)链接:
http://gk.link/a/106nW
RTMP协议分析及H.264打包原理:
https://blog.csdn.net/caoshangpa/article/details/52872146
GitHub链接:
https://github.com/lichangke/LeetCode
知乎个人首页:
https://www.zhihu.com/people/lichangke/
CSDN首页:
https://me.csdn.net/leacock1991
欢迎大家来一起交流学习