RTMP(3):Protocol Control Message

当 message type id 是 1,2,3,5,6 时,表示该 message 是一条协议控制消息(Protocol Control Message)。

这些类型的消息用来控制 chunk 层级的一些行为,具体类型名称如下:

  • 1:Set Chunk Size
  • 2:Abort Message
  • 3:Acknowledgement
  • 5:Windon Acknowledgement Size
  • 6:Set Peer Bandwidth

Protocol control message 有几个重要的规定:

  • Chunk stream id 必须是 2,message stream id 必须是 0。
  • 接收端在收到 protocol control message 后必须立即处理,处理之后才能接收处理后续的 chunk。
  • timestamp 字段会被忽略。

开始之前,先约定下几个定义:

  • client:发起 handshake 的一方。
  • server:client 的对端。
  • 发送端:发送 Protocol Control Message 的一方。
  • 接收端:接收 Protocol Control Message 的一方。

client/server,发送端/接收端是两组概念。client/server 是链接层面的,链接建立后就不会改变了。发送端/接收端是消息层面的,分别是指一条消息的发送者和接受者。client 或 server 均可是发送端或接收端。

Set Chunk Size (1)

前文提到过,maximum chunk size 是单个 chunk 的 payload size 的上限,默认值是 128,单位 byte。发送端可通过 Set Chunk Size 类型的消息调整 maximum chunk size 的大小。


Set Chunk Size 消息携带一个 32-bit 的整数—— chunk size,该字段的取值范围为[1, 0x7FFFFFFF]。实际上,chunk size 的值不应该超过 0xFFFFFF 。因为 message length 字段只有 24-bit,其值不可能超过 0xFFFFFF

注意,这里是发送端提醒接收端,我发送该消息之后,后续 chunk 的 payload size 上限就已经调整啦,但是你发给我的 chunk 的 payload size 上限不会变。

举个例子,假设要发送如下两个 message:

Message Stream IDMessage Type IDTimestampLength
MSG # 11081000280
MSG # 21081020150

在 maximum chunk size 是 128 时,会封装为 5 个 chunk,如下表示:

Chunk Stream IDChunk FormatTimestamp(Delta)Message LengthMessage Type IDMessage Stream IDChunk Data Size
CHK # 1301000280810128
CHK # 233\\\\128
CHK # 333\\\\24
CHK # 43120150810128
CHK # 533\\\\22

如果,发送端在 CHK#1 后加入一条 Set Chunk Size,通知对端上限调整为 152,则上述 chunk 变为:

Chunk Stream IDChunk FormatTimestamp(Delta)Message LengthMessage Type IDMessage Stream IDChunk Data Size
CHK # 1301000280810128
CHK # S2004104 (值依次为 0,0,0,152)
CHK # 233\\\\152
CHK # 43120150810150

显然,接收端在收到 CHK#S 后,就要按照 maximum chunk size 为 152 读取后续 chunk 啦。

Abort Message (2)


该消息携带一个 32-bit 的整数—— chunk stream id,用于通知接收端丢弃该 chunk stream 上尚未接收完的 message。

不过该消息好像被废弃了,文档里面也画上了删除线。
在这里插入图片描述
FFmpeg 也未支持该消息类型,在收到该类型的消息后会返回错误码 AVERROR_UNKNOWN

// libavformat/rtmpproto.c
 434         } else if (pkt.type == RTMP_CTRL_ABORT_MESSAGE) {
 435             av_log(s, AV_LOG_ERROR, "received abort message\n");
 436             ff_rtmp_packet_destroy(&pkt);
 437             return AVERROR_UNKNOWN;
 438         } else if (pkt.type == RTMP_PT_BYTES_READ) {

Acknowledgement (3)


该消息携带一个 32-bit 的整数—— sequence number,用于通知接收端我(发送端)已经收到的字节数。

几个小细节:

  • 统计的是在所有 chunk stream 上读取的字节数。
  • chunk header 的 size 也会统计在内。
  • 每收到 w 个字节,就要发送一次。w 由对端通过 Window Acknowledgement Size 指定。关于 w 的默认值,文档中并未提及,FFmpeg 将 2 20 2^{20} 220 作为默认值。

Window Acknowledgement Size (5)

在这里插入图片描述
该消息携带一个 32-bit 的整数 —— window size,用于设置接收端发送 Acknowledgement 消息的频率。接收端每收到 window size 个字节,至少发送一条 Acknowledgement 消息。

发送-等待机制:该消息的发送端期望每发送 window size 个字节就收到一条 Acknowledgement 消息,若未收到则会停止发送数据,直到 Acknowledgement 到来。

在 FFmpeg 的实现中,每收到 w i n d o w   s i z e 2 \frac{window\ size}{2} 2window size 个字节,就发送一次 Acknowledgement 消息,具体代码如下,如此实现的原因也容易理解,可参见 1596 -1598 行的注释。

// libavformat/rtmpproto.c
1578 static int handle_window_ack_size(URLContext *s, RTMPPacket *pkt)
1579 {
1580     RTMPContext *rt = s->priv_data;
1581
1582     if (pkt->size < 4) {
1583         av_log(s, AV_LOG_ERROR,
1584                "Too short window acknowledgement size packet (%d)\n",
1585                pkt->size);
1586         return AVERROR_INVALIDDATA;
1587     }
1588
1589     rt->receive_report_size = AV_RB32(pkt->data);
1590     if (rt->receive_report_size <= 0) {
1591         av_log(s, AV_LOG_ERROR, "Incorrect window acknowledgement size %d\n",
1592                rt->receive_report_size);
1593         return AVERROR_INVALIDDATA;
1594     }
1595     av_log(s, AV_LOG_DEBUG, "Window acknowledgement size = %d\n", rt->receive_report_size);
1596     // Send an Acknowledgement packet after receiving half the maximum
1597     // size, to make sure the peer can keep on sending without waiting
1598     // for acknowledgements.
1599     rt->receive_report_size >>= 1;
1600
1601     return 0;
1602 }

Set Peer Bandwidth (6)


该消息包含两个字段:

  • Window size:32-bit 的整数。
  • Limit type:8-bit 整数。

Limit type 的取值有如下三种:

  • 0 - Hard:接收端应使用 window size 作为控制带宽的参数。
  • 1 - Sort:使用 window size 和正在生效的 window size 中的最小值。
  • 2 - Dynamic:如果前一个 Limit Type 是 Hard,则该消息按照 Hard 规则生效,反之该消息被丢弃。

Set Peer Bandwidth 需配合 Acknowledgement 与 Window Acknowledgement Size 一起工作。设有两个握手成功的端点 A 和 B,其工作流程如下:

  • A 向 B 发送 Set Peer Bandwidth,window size = 1024。
  • B 收到后,向 A 发送 Window Acknowledgement Size,window size 需相同,亦为 1024。
  • A 收到 Window Acknowledgement Size 后,每收到 1024 个字节,至少发送一次 Acknowledgement。
  • B 在发送 Window Acknowledgement Size 后,会触发 发送-等待机制 以达到控制带宽的效果。

3,5,6 的例子

设有两个端点 A 和 B 已经握手成功,现在 A 要通过 Set Peer Bandwidth 控制 B 的发送速率,过程如下:

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值