美文网首页
直播技术--RTMP协议篇

直播技术--RTMP协议篇

作者: d8893ea8ba05 | 来源:发表于2019-10-10 16:19 被阅读0次

协议的位置

RTMP与HTTP属于同一个层级,都位于TCP/IP协议簇中的应用层

  • 应用层:HTTP、RTMP
  • 传输层:TCP、UDP
  • 网络层:IP、ICMP
  • 数据链路层:ARP、RARP

握手

握手过程涉及到客户端发送的三个包c0、c1、c2和服务端发送的三个包s0、s1、s2,且满足如下要求

  • 从客户端发送c0和c1开始
  • 服务端需要接收到c0之后才能开发发送s0和s1;必须接收到c1后才能发送s2
  • 客户端需要接收到s1之后才能发送c2
  • 客户端和服务端分别接收到s2和c2之后才能开发播放视频
c0和s0版本确定

c0和s0都是八个字节,只包含版本号信息,c0发送自己的版本号,s0返回服务端支持的版本号,如果服务端不支持客户端的版本号,那么默认返回3,客户端根据服务端的版本号来确定是否降级或放弃握手

c1和s1
  • timestamp:4个字节,发送的时间戳
  • zero:4个字节,全部为0
  • random:内容随机
c2和s2
  • timestamp:4个字节,对c2来说是s1中的timestamp,对s2来说是c1中的timestamp
  • timestamp2:4个字节,之前c1或c2中的timestamp
  • random:对c2来说是s1中的random,对s2来说是c1中的random
    当客户端接收到s2的时候,可以根据c1的timestamp和s2的timestamp来确定出网络耗时;可以根据c1的timestamp来确定是否为本次握手的s2数据
实际握手过程
  • 客户端发送c0和c1
  • 服务端发送s0、s1和s2
  • 客户端发送c2

数据传输

在RTMP中数据传输的逻辑单元被成为消息,然后在实际传输的过程中,每条消息被切割成为消息块在网络上传输,这样可以很好地避免内容较大的消息阻塞了后面内容较小但是优先级更高的消息,如音频或控制数据


数据结构
消息格式
  • MessageType:1个字节
  • PlayloadLength:3个字节,消息负载内容的长度
  • Timestamp:4个字节,时间戳
  • MessageStreamID:消息流id;被复合到同一个块流的消息流,根据消息流id进行分离;这个字段在块头信息中占用4个字节
消息块格式

握手完成后,连接复用一个或多个块流,每个块流承载来自同一个消息流的同一类消息。创建的每个块都有一个唯一的块流ID,这些块通过网络进行传输。 在传输过程中,必须一个块发送完毕之后再发送下一个块。在接收端,将所有块根据块中的块流ID组装成消息。
分块将上层协议的大消息分割成小的消息,保证大的低优先级消息(比如视频)不阻塞小的高优先级消息(比如音频或控制消息)。
分块还能降低消息发送的开销,它在块头中包含了压缩的原本需要在消息中所包含的信息。
块大小是可配置的,这个可以通过一个设置块大小控制消息进行设定修改。越大的块CPU使用率越低,但是在低带宽的情况下,大的写入会阻塞其他内容的写入;而小一些的块不适合高比特率的流。块大小在每个方向上保持独立

  • 基本头(1-3字节): 该部分编码块流ID和块类型,块类型决定了消息头的编码格式。该部分的长度取决于块流ID,块流ID是一个变长字段。
  • 块消息头(0,3,7或11字节): 该部分编码所发送消息的描述信息(无论是整个消息还是一部分)。该部分的长度取决于基本头中指定的块类型。
  • 扩展时间戳(0或4字节): 该部分只有在某些特殊情况下才会使用,是否使用取决与块消息头中的时间戳或时间戳增量。
  • 块数据(变长): 块承载的有效数据,长度最大为配置的块大小。
基本头

块基本头编码块流ID和块类型(用fmt字段表示),块类型决定了消息头的编码格式,块基本头长度可能是1,2或3字节,这取决于块流ID的长度。
RTMP最多支持65597个流,ID在3-65599范围内。ID0,1,2为保留值。
第一个字节的前两位fmt用来标识块消息头的类型,块流id称为csid,基本头编码的目的是用最小的长度来标识尽可能多的id,于是有了下面的规则:

  • 在3~63之间的csid,可以直接用2~7位来标识
  • 在64~319之间的csid,用第二个字节+64计算得来,第2~7位全部置0
  • 在64~65599之间的csid,用第三个字节值*255 + 第二个字节值 + 64计算得来,第2~7位全部置1
  • 64~319之间的csid既可以用两个字节来标识也可以用三个字节来表示
块消息头

块消息头共有4种不同的格式,根据块基本头中的"fmt"字段值来选择

  • 0类型的块消息头占11个字节长度,该类型必须用在一个块流的开头, 和每当块流时间戳后退的时候(例如向后搜索的操作)


    fmt=0
  • 1类型的块消息头占用7个字节长度,不包含消息流ID,该块沿用上一个消息的消息流ID。对于传输大小可变消息的流(如多数视频格式),在发送第一个消息之后的每个消息,第一个块都应该使用该类型格式


    fmt=1
  • 2类型的块消息头占用3个字节长度,不包含消息流ID和消息长度,沿用上一个块的消息流ID和消息长度。对于传输固定大小消息的流(如音频数据格式),在发送第一个消息之后的每一个消息,第一个块都应该使用该类型格式


    fmt=2
  • 3类型的块没有消息头,消息流ID、消息长度和时间戳增量都不指定,该类型的块都使用上一个块相同的块流ID。当一个消息被分割成块时,除了第一个块,其他块都应该使用该类型。由相同大小、消息流ID和时间间隔的消息组成的流,在类型2的块之后所有块都应该使用该类型格式。如果第一个消息和第二消息之间的时间增量与第一个消息的时间戳相同,则0类型的块之后可以马上发送3类型的块,而不必使用2类型的块来注册时间增量。如果类型3的块跟在类型0的块后面,那么3类型块的时间戳增量与0类型块的时间戳相同。
  • timestamp delta(3字节): 时间戳增量。类型1和类型2的块包含此字段,表示前一个块的timestamp字段和当前块timestamp间的差值。
  • message length(3字节): 消息长度,类型0和类型1的块包含此字段,表示消息的长度。要注意的是,通常该长度与块负载长度并不相同。块负载长度除了最后一个块,都与块最大长度相同。
  • message type id(3字节):消息类型id,类型0和类型1的块包含此字段,表示消息的类型。
  • message stream id(4字节):消息流ID,类型0的块包含此字段,表示消息流ID。消息流ID以小字节序存储。通常,相同块流中的消息属于用一个消息流。虽然,不同的消息流复用相同的块流会导致消息头无法有效压缩,但是当一个消息流已关闭,才打开另外一个消息流,就可以通过发送一个新的0类型块来实现复用。
扩展时间戳

扩展时间戳用来辅助编码超过16777215(0xFFFFFF)的时间戳或时间戳增量,也就说0,1或2
类型的块,无法用24位数字来表示时间戳或时间戳增量时,既0类型块的时间戳字段或1,2类
型的时间戳增量字段值为16777215(0xFFFFFF)时。当最近的属于相同块流ID的0类型块、1
类型块或2类型块有此字段时有此字段时,3类型块也应该有此字段。

消息分块范例
  • 一个简单的音频消息流切块


    消息
    消息块
  • 一个超过128字节长度的消息,消息被分割成了数个块


    消息
    消息块

协议控制消息

RTMP块流用消息类型ID 1,2,3,5和6来作为协议控制消息,这些消息包含RTMP块流协议所需要的信息。。这些协议控制消息必须用 0 作为消息流ID,并在ID为2的块流中发送。协议控制消息收到后立即生效,它们的时间戳信息是被忽略的。

  • MessageType-1:设置块大小,用于通知另一端新的最大块大小。
  • MessageType-2:终止消息,通知正在等待消息后续块的另一端,可以丢弃通过指定块流接
    收到的部分数据
  • MessageType-3:确认消息,客户端或服务端在接收到数量与窗口大小相等的字节后发送确认消息到对方;服务端在建立连接之后发送窗口大小
  • MessageType-5:视窗大小确认,客户端或服务端发送本消息来通知对方发送确认消息
  • MessageType-6:设置对等端带宽,客户端或服务端发送该消息来限制对端的输出带宽。接收端收到消息后,通过将已发送但尚未被确认的数据总数限制为该消息指定的视窗大小,来实现限制输出带宽的目的。如果视窗大小与上一个视窗大小不同,则该消息的接收端应该向该消息的发送端发送视窗大小确认消息

用户控制消息

RTMP协议将消息类型 4 作为用户控制消息ID,用户控制消息应该使用ID为0的消息流(控制流),并且通过RTMP块流传输时使用ID为2的块流。用户控制消息收到后立即生效,它们的时间戳信息会被忽略。
客户端或服务端通过发送该消息告知对方用户控制事件。该消息携带事件类型和事件数据两部分。开头的2个字节用于指定事件类型,紧跟着是事件数据。事件数据字段长度可变,但是如果用RTMP块流传输,则消息总长度不能超过最大块大小,以使消息可以使用一个单独的块进行传输。

  • 流开始:0,服务端发送该事件,用来通知客户端一个流已经可以用来通讯了。默认情况下,该事件是在收到客户端连接指令并成功处理后发送的第一个事件。事件的数据使用4个字节来表示可用的流的ID
  • 流结束:1,服务端发送该事件,用来通知客户端其在流中请求的回放数据已经结束了。如果没有额外的指令,将不会再发送任何数据,而客户端会丢弃之后从该流接收到的消息。事件数据使用4个字节来表示回放完成的流的ID。
  • 流枯竭:2,服务端发送该事件,用来通知客户端流中已经没有更多的数据了。如果服务端在一定时间后没有探测到更多数据,它就可以通知所有订阅该流的客户端,流已经枯竭。事件数据用4个字节来表示枯竭的流的ID。
  • 设置缓冲区大小:3,客户端发送该事件,用来告知服务端用来缓存流中数据的缓冲区大小(单位
    毫秒)。该事件在服务端开始处理流数据之前发送。事件数据中,前4个字节用来表示流ID,之后的4个字节用来表示缓冲区大小(单位毫秒)。
  • 流已录制:4,服务端发送该事件,用来通知客户端指定流是一个录制流。事件数据用4个字节表示录制流的ID
  • ping请求:6,服务端发送该事件,用来探测客户端是否处于可达状态。事件数据是一个4字节的时间戳,表示服务端分发该事件时的服务器本地时间。客户端收到后用 ping响应 回复服务端。
  • ping响应:7,客户端用该事件回复服务端的 ping请求 ,事件数据为收到的 ping请求 中携带的4字节的时间戳。

其他消息类型

客户端和服务端通过在网络上发送消息来实现交互,消息可以是任意类型,包括但不限于音频消息、视频消息、指令消息、共享对象消息、数据消息和用户控制消息。

  • 类型17、20:指令消息在客户端和服务端之间传递AMF编码的指令,消息类型20代表AMF0编码,消息类型17代表AMF3编码
  • 类型15、18:客户端或服务端通过该消息来发送元数据或其他用户数据。元数据包括数据(音频、视频)的创建时间、时长、主题等详细信息。消息类型18代表AMF0编码,消息类型15代表AMF3编码
  • 类型16、19:共享对象是一个在多个客户端、示例之间进行同步的Flash对象(键值对集合)。消息类型19代表AMF0编码,消息类型16代表AMF3编码。每个消息都可以包含多个事件
  • 类型8:客户端或服务端通过发送此消息来发送音频数据给对方,消息类型8是为音频消息预留的。
  • 类型9:客户端或服务端通过发送此消息来发送视频数据给对方,消息类型9是为视频消息预留的
  • 类型22:组合消息,是一个消息包含多个子RTMP消息。消息类型22用于组合消息

交互过程

  • 推流


    推流
  • 拉流


    拉流

相关文章

网友评论

      本文标题:直播技术--RTMP协议篇

      本文链接:https://www.haomeiwen.com/subject/isropctx.html