流媒体传输协议之 RTMP

RTMP是一种实时传输协议,用于音频、视频和数据的流媒体。本文深入解析了RTMP的握手过程、消息格式、块流、分块、时间戳处理以及协议控制消息,展示了如何在TCP基础上实现可靠的双向消息复用。RTMP块流允许消息按时间戳顺序传输,支持优先级控制,适用于一对一或一对多直播、视频会议等应用场景。

作者:逸殊
审核:泰一

简介

RTMP 在可靠流式传输(TCP)的基础上提供了双向的消息多路复用服务,在通讯双方之间传输与时间相关的并行流数据,如音频,视频和数据消息。协议实现方通常为不同的消息类型指定不同的优先级,这样在网络带宽受限时能改变底层传输顺序。

定义

  • 负载:包中所承载的数据。例如音频或视频数据。
  • 包:一个数据包由固定头部和所承载的数据组成。一些底层协议可能需要定义数据包的封装格式。
  • 端口:在一个计算机中用于区分不同目标的抽象定义。在 TCP/IP 协议中用一个小的正整数来表示端口。OSI 传输层的传输选择器就相当于端口。
  • 传输地址:标识一个传输终端的网络地址和端口的组合,例如 IP 地址和 TCP 端口的组合。
  • 消息流:允许消息传播的逻辑通道。
  • 消息流 ID:每个消息都会有一个对应的 ID,用于标识其所在的消息流。
  • 块:消息的一个片段。消息在传输之前会被分割成更小的片段,因为每一块都很小,以至于可以给不同的块指定各自的优先级,通过这种方式保证多个流中数据可以按照时间戳的顺序传输。
  • 块流:块向某一确定方向传播的逻辑通道。可以是客户端到服务端,也可以是服务端到客户端。
  • 块流 ID:每个块都会有一个对应的 ID,用于标识其所在的块流。
  • 复用:将独立的音频 / 视频数据整合为统一的音视频流,可以使多个音视频流同步传输。
  • 复用分离:复用的逆向过程。将合并的音视频数据分离为原始的音频和视频数据。
  • 远程过程调用:客户端或服务端调用另一端的功能。
  • 元数据:媒体数据的描述信息。
  • 应用实例:服务器上可以和 Client 建立连接的应用。
  • 动作消息格式:一个可用于序列化 ActionScript 对象图的紧凑的二进制格式。
  • 字节序:字节的顺序,即多字节类型的数据在内存中的存放顺序。TCP/IP 各层协议将字节序定义为大端字节序,因此 TCP/IP 协议中使用的字节序通常称之为网络字节序。
  • 大字节序:高位字节排放在内存的低地址,低位字节排放在内存的高地址。
  • 小字节序:低位字节排放在内存的低地址,高位字节排放在内存的高地址。

字节序,校准,时间格式

所有整数都是以网络字节序来表示的。除非另行说明,本文中的所有数字都是十进制数。
在没有特殊说明的情况下,RTMP 中的数据都是字节对齐的。如果有填充的话,填充字节应该用 0。
RTMP 中的时间戳是用一个整数来表示的,代表相对于一个起始时间的毫秒数。通常每个流的时间戳都从 0 开始,但这不是必须的,只要通讯双方使用统一的起始时间就可以了。要注意的是,跨流的时间同步(不同主机之间)需要额外的机制来实现。
由于时间戳的长度只有 32 位,所以只能在 50 天内循环(49 天 17 小时 2 分钟 47.296 秒)。而流是可以不断运行的,可能多年才会结束。所以 RTMP 应用在处理时间戳是应该使用连续的数字算法,并且应该支持回环处理。例如:一个应用可以假设所有相邻的时间戳间隔不超过 2^31-1 毫秒,在此基础上,10000 在 4000000000 之后,3000000000 在 4000000000 之前。
时间戳增量也是以毫秒为单位的无符号整数。时间戳增量可能会是 24 位长度也可能是 32 位长度。

RTMP 块流

块流为上层流媒体协议提供复用和分包的功能。RTMP 块流是为配合 RTMP 协议而设计,但它可以使用在任何发送消息流的协议中。每个消息包含时间戳和负载类型信息。RTMP 块流和 RTMP 协议组合可以适用于多种音视频应用,从一对一或一对多直播到视频会议都能很好的满足。
当使用可靠传输协议(如 TCP)时,RTMP 块流为所有消息提供了可靠的跨流端对端按时间戳顺序发送的机制。RTMP 块流不提供优先级控制,但是可以由上层协议提供这样的优先级。例如:当某个客户端网络比较慢时,可能会选择抛弃一些视频消息来保证声音消息能够及时接收。
RTMP 块流除自身内置的协议控制消息外,还为上层协议提供了用户控制消息的机制。

消息格式

消息格式由上层协议定义,消息可以被分成多个块以支持多路复用。消息应该包含分块功能所需的所有字段,具体内容如下:

  • 时间戳(4-byte):消息的时间戳。
  • 长度(3-byte):消息有效负载的长度,如果消息头不能被省略,则消息头的长度也应该包含在长度中。
  • 类型 ID(1-byte):消息类型 ID。一些类型 ID 是为协议控制消息保留的,这些消息所表示的信息同时供 RTMP 块流协议和上层协议使用。所有其他类型 ID 都用于上层协议,RTMP 块流对这些 ID 做不透明处理。实际上,RTMP 块流不需要用这些值来区分类型,所有消息都可以是相同的类型,应用也可以用本字段来区分同步轨道而不是区分类型。
  • 消息流 ID(4-byte):消息流 ID 可以是任意值。被复合到同一个块流的消息流,依据消息流 ID 进行分离。另外,就相关的块流而言,这个值是不透明的。这个字段使用小字节序。

握手

RTMP 连接以握手开始,它的握手过程可能和其他协议不同,这里的握手由 3 个固定大小的块组成,而不是可变大小的块加上固定大小的头。

握手流程

握手由客户端发送 C0 和 C1 块开始。
客户端必须等接收到 S1 之后才可以发送 C2。客户端必须等接收到 S2 之后才可以发送其他数据。
服务器必须等接收到 C0 之后才可以发送 S0 和 S1,也可能接收到 C1 之后发送。服务器必须等接收到 C1 之后才可以发送 S2。服务器必须等接收到 C2 之后才可以发送其他数据。

C0 和 S0 格式

C0 和 S0 是单独的一个字节,可以当做一个 8bit 的整数字段来对待。
1.png

以下是 C0 和 S0 包的字段解释:

  • 版本号(8 位): 在 C0 包中,该字段表示客户端请求的 RTMP 版本。在 S0 中,该字段表示服务器选择的 RTMP 版本。本规范所定义的版本是 3。可选值中,0-2 是早期版本所用的,已被丢弃,4-31 保留在未来使用,32-255 不允许使用(为了区分其他以某一可见字符开始的文本协议)。如果服务器不能识别客户端请求的版本,应该返回 3,客户端可能选择降级到版本 3,也可能放弃握手。
C1 和 S1 格式

C1 和 S1 包固定为 1536 字节,包含以下字段:
2.png

  • 时间戳(4 字节):该字段承载一个时间戳,该时间戳应该作为发送端点所有后续块的时间戳起始时间。可以是 0,也可以是其他的任意值。为了同步多个块流,端点可能会发送其他块流的当前时间戳。
  • 零值(4 字节):该字段所有值都必须为 0。
  • 随机数据(1528 字节):该字段可以是任意值。通过这个字段来区分自己和连接的另一方,所以此数据应该有充分的随机性,但是没必要使用加密安全的随机值或动态值。
C2 和 S2 格式

C2 和 S2 包的长度也为 1536 字节,基本上是 S1 和 C1 的回传,包含以下字段:
3.png

  • 时间戳(4 字节):该字段必须包含对端发来的时间戳(对 C2 来说是 S1, 对 S2 来说是 C1)。
  • 时间 2(4 字节):该字段必须包含先前发送的并被对端读取的包的时间戳。(对 C2 来说是 C1,对 S2 来说是 S1)。
  • 随机数据回显(1528 字节):该字段必须包含对端发送过来的随机数据字段值(对 C2 来说是 S1, 对 S2 来说是 C1)。任何一端都可以用时间戳和时间戳 2 两个字段值和当前时间戳来快速的估算带宽和延迟,但这样可能并不实用。
握手流程示意图

4.png

上图提到的状态的解释如下:

  • Uninitialized:未初始化状态。在该阶段发送协议版本。客户端在 C0 包中发送 RTMP 协议版本,如果服务器支持此版本,服务器将在响应中发送 S0 和 S1。如果不支持,服务器采用适当的行为作为响应,在 RTMP 规范中是终止连接。
  • Version Send:版本已发送状态。在未初始化状态之后客户端和服务端都进入版本已发送状态。客户端等待接收 S1 包,服务端等待接收 C1 包。收到所等待的包后,客户端发送 C2 包,服务端发送 S2 包。之后状态进入发送确认状态。
  • Ack Send:客户端和服务端等待接收 S2 和 C2 包,收到后进入握手完成状态。
  • Handshake Done:握手完成, 客户端和服务端开始交换消息。

分块

握手完成后,一个或多个块流可能会复用同一个连接,每个块流承载来自同一个消息流的同一类消息。每个块都有一个唯一的块流 ID,这

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值