[趣谈网络协议学习] 16 流媒体协议:如何在直播里看到美女帅哥?

三个名词系列

  • 名词系列一: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为什么需要建立一个单独的连接?因为需要商量一些事情,保证之后传输的正常进行。主要商量版本号和时间戳。

RTMP中的握手
客户端:

  1. 客户端发送 C0 表示自己的版本号
  2. 不必等对方的回复,然后发送 C1 表示自己的时间戳
  3. 客户端收到 S1 的时候,发一个知道了对方时间戳的 ACK C2。

服务器

  1. 服务器只有在收到 C0 的时候,才能返回 S0,表明自己的版本号,如果版本不匹配,可以断开连接。
  2. 服务器发送完 S0 后,也不用等什么,就直接发送自己的时间戳 S1。
  3. 服务器收到 C1 的时候,发一个知道了对方时间戳的 ACK S2。

握手之后,双方需要互相传递一些控制信息,例如 Chunk 块的大小、窗口大小等。

真正传输数据的时候,还是需要创建一个流 Stream,然后通过这个 Stream 来推流 publish。
推流的过程,就是将NALU放在Message里传送,称RTMP packet包

Message的格式如下图所示:

Message的格式
发送时,去掉NALU的起始标识符。将SPS和PPS参数集封装称一个RTMP包发送,然后发送一个个片的NALU。

RTMP 在收发数据的时候并不是以 Message 为单位的,而是把 Message 拆分成 Chunk 发送,而且必须在一个 Chunk 发送完成之后,才能开始发送下一个 Chunk。

每个 Chunk 中都带有 Message ID,表示属于哪个 Message,接收端也会按照这个 ID 将 Chunk 组装成 Message。

例子:
假设一个视频的消息长度为 307,但是 Chunk 大小约定为 128,于是会拆分为三个 Chunk。

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拉流过程

客户端通过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
欢迎大家来一起交流学习

发布了170 篇原创文章 · 获赞 16 · 访问量 2836

猜你喜欢

转载自blog.csdn.net/leacock1991/article/details/100752271
今日推荐