Spec中文版首页投稿(暂停使用,暂停投稿)iOS音视频直播技术栈

RTMP Spec中文版

2016-10-20  本文已影响552人  SniperPan

个人翻译,转载请注明出处,谢谢!

Adobe's Real Time Messaging Protocol

摘要

本文档描述了Adobe公司的实时消息协议(RTMP),RTMP是设计用于multiplexing和分组多媒体传输流的建立在合适传输协议之上(如TCP)的应用层协议。

1. 简介

RTMP协议基于可靠的流传输(如TCP)层提供了一项双向消息复用服务,目的是可以在两节点间并行传输带有时间信息的音频、视频和数据消息。

RTMP的实现中通常会给不同类别消息分配不同优先级,从而达到在传输容量被压缩时影响进入底层传输流中消息的目的。

本文档描述了RTMP的相关语法和操作。

1.1 术语

文档中使用的关键词如“MUST”,"MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" 以及 “OPTIONAL”可在[RFC2119]中找到相应的说明。

2. 定义

3. 字节序、字节对齐和时间格式

所有的整型字段都是使用网络字节序(big-endian)进行传输的,该部分在[RFC0791]中有详细描述。另外,除特殊说明,文档中所有常数均为十进制格式。

同样,如无特殊说明,RTMP中所有数据都是字节对齐(byte-aligned)的;举个例子,一个16位的变量可能会处于奇数偏移位置。当需要补齐时,对应字节应当用用0填充。

RTMP中时间戳是一个整型值,单位为毫秒,它是一个相对值,相对未规定的某一时间点。通常,每个流会从时间戳0开始,但也不是必须的,只要流两端能够协商某一起始点即可。

注意,这说明任何需要跨流(尤其是不同主机间)同步必须依赖RTMP以外的其他机制。

由于时间戳32bits长度限制,它会在每49天17时2分47.296秒循环一次。但流通常是会持续地传输,甚至长达数年之久,因此RTMP应用需要在处理时间戳时使用serial number arithmetic[RFC1982],而且应当支持回滚处理。

时间戳增量同样规定为一个无符号整形数,单位毫秒,但相对于时间为上一时间戳。时间戳增量可使用24或32bits长度。

4. RTMP块流

本章节制定了RTMP块流细节,它为更高层流媒体协议提供了复用和分组服务。

虽然RTMP块流设计用于协同RTMP协议工作,但它同样适用于其他发送消息流的协议。每条消息由时间戳和负载类型标识构成。RTMP块流和RTMP广泛适用于各种音视频应用,既支持“一对一”或“一对多”实时广播,也支持为会议交互系统提供视频点播(video-on-demand)服务。

当配合可靠传输协议如TCP时,RTMP块流提供了有保证的时间戳序列的对端跨流消息传输。RTMP块流并未提供任何优先级或相关形式的控制,但可被高层协议用于提供类似功能。例如,一个实时视频服务器可能会参考每条消息发送和响应的时间,来决定是否要丢弃部分视频消息以满足较慢客户端能够流畅地接收音频数据。

RTMP块流集成了带内(in-band)协议控制消息,而且为高层协议提供了嵌入自定义控制消息的机制。

4.1 消息格式

消息格式可划分为块来支持不同高层协议进行复用。但无论如何,下列字段都是创建块时所必须的:

4.2 握手

RTMP连接始于握手,与其他协议握手不一样的是,它一共包含了3次固定大小的块,而不是类似的带有头信息的可变大小块。

客户端和服务器都会发送同样的三个块,具体而言,客户端发送的三个块分别为C0, C1和C2;而服务器会发送对应的S0, S1和S2。

4.2.1 握手次序

握手次序是以客户端发送C0和C1块开始的。

客户端必须成功接收到S1后才发送C2,且必须成功接收S2后才可以发送其他数据。

服务器必须成功接收C0后(也可以选择C1后才发送)才可以发送S0和S1。对S2而言,服务器必须成功接收C1后才可以,最后服务器必须成功接收到C2才可以开始发送其他数据。

4.2.2 C0和S0格式

C0和S0分组都是一个八位数(octet)字段:


各字段含义如下所述:

本文档多对应的版本值为3,0 ~ 2是早期产品值,现已不推荐使用。4 ~ 31预留用于将来的实现版本使用;32 ~ 255则为禁止使用值(以便区分RTMP和基于文本的协议,它们总是会以可打印字符开始)。

4.2.3 C1和S1格式

C1和S1分组均长1536个字节,由如下字段组成:

4.2.4 C2和S2格式

C2和S2同样是1536字节,相当于是S1和C1的回应,均包含如下字段:

4.2.5 握手图例


下面将主要说明握手图例中提到的不同状态:

4.3 分块

握手成功后,连接将会复用一至多个块流。每个块流携带源自一个消息流中的一类消息。每个块创建时赋予独特ID,称为块流ID(chunk stream ID)。这些块经由网络传输,在传输时,每个块必须依次逐个传递,接收端收到块后,会基于块流ID将块重组成消息。

分块机制允许高层协议中的长消息分解为短消息,这样可以防止长的短优先级消息(如视频)阻塞高优先级消息(如音频或控制操作)。

分块机制还通过将本应包含在消息中的信息压缩至块头中以减小发送短消息的开销。

块的大小是可配置的,它还支持通过一系列“块大小设置消息(Chunk Size control message, section 4.4.1)”进行设置。设置块长度较大时可以降低CPU使用率,但同样会由于大量数据写入而造成低带宽环境下其他内容的延时。设置块长度较小时不利于高比特率流传输。

块的大小由两端单独控制。

4.3.1 块格式

每个块均由头(header)和数据(data)构成,头本身又包括下面三部分:

4.3.1.1 块基础头

块基础头编码了块流ID和块类型(如下图的fmt字段所示)。块类型决定了编码后消息头的格式,块基础头长度根据块流ID可为1,2,或3字节。

块基础头中的0 ~ 5 bit (least significant)代表块流ID。

实现时应该使用最短的长度来表示ID。

协议最多支持65597个流,流ID分别为3 ~ 65599. ID 0、1和2暂时预留。

值为 3 ~ 63的表示完整流ID。

块流ID为2 ~ 63时,可编码入1字节版本:

块流ID为 64 ~ 319时,可编码入2字节版本,ID计算式为(第二字节+64):

块流ID为 64 ~ 65599时,可编码入3字节版本,ID计算式为(第三字节*256 + 第二字节 + 64):

对应字段说明如下:

块流ID值为 64 ~ 319可选择使用2字节或3字节格式进行表示

4.3.1.2 块消息头

块消息头共有四种格式,由块基础头中的“fmt”字段进行选择。
实现时应该使用最紧凑的方式来表示每一个块消息头。

4.3.1.2.1 类型0

类型0 块头共11字节长,该类型必须使用在块流的开头和任何流时间戳回滚(如向后seek)的时候。

4.3.1.2.2 类型1

类型1 块头长7字节,消息流ID未包含在内,该块使用和前面块一致的块流ID。可变长消息的流(如许多视频格式)应当使用本格式作为每条新消息的第二块:

4.3.1.2.3 类型2

类型2 块头长3字节,消息流ID和长度均未包含在内;该块使用和前面块一致的块流ID和长度值。具有固定长度消息的流(如许多音频和数据格式)可使用本格式作为每条消息的第二块:

4.3.1.2.4 类型3

类型3 块没有消息头,流ID、消息长度以及时间戳增量字段都不会呈现;本类型的块是钱前面块的流ID。当一条消息分解成块后,除第一块外,所有其他块都该使用此类型(参考示例2,Section 4.3.2.2)。由完全相同大小、流ID和时间间隔的消息流可在开头的类型2块后的所有块中使用类型3块(参考示例1,Section 4.3.2.1)。如果第二条消息和第一条消息的时间增量与第一条消息的时间戳相同,那么可以在类型0块后直接跟随类型3块,因为没有必要使用类型2块说明时间增量。也就是说,如果类型3块紧跟类型0块,那么该类型3块的时间增量是和类型0块一致的。

4.3.1.2.5 常用消息头变量

下面列出块消息头中的每个变量说明:

4.3.1.3 扩展时间戳

扩展时间戳用于编码大于16777215(十六进制0xFFFFFF)的时间戳或时间戳增量,即用于类型0,1,2块中不适合24位变量的时间戳或时间戳增量。该变量编码了完整的32位时间戳或时间戳增量。该变量的出现意味着块0中时间戳或块1/2中时间戳增量值为16777215(0xFFFFFF)。该变量会在当最近的具有相同块流ID类型0/1/2块中出现扩展时间戳变量时,出现在紧跟其后的类型3块中。

4.3.2 示例

4.3.2.1 示例1

下面是一个简单的音频消息流示例,例子中示范了消息的冗余。


下表中显示流中对应产生的块,从消息3开始,数据传输做了最优化,每条消息前仅添加了一个字节:

4.3.2.2 示例2

下面举例说明了当消息太长而无法放入一个128字节的块时,分解成多个块进行传输。


下面是对应产生的块:

块1消息头中指明了消息总长307字节。

注意:
在上面两个示例中,类型3块可被用于两种用途。

  • 标识为一个消息的连续
  • 标识一个新消息的开始,该块沿用已存在的头信息

4.4 协议控制消息

RTMP块流可使用消息类型ID为1,2,3,5和6来发送协议控制消息,这些消息包含了RTMP块流协议所必需的信息。

这些协议控制消息必须使用消息流ID 0(又称为控制流)且在块流ID 2中发送。协议控制消息收到时立即生效,他们的时间戳会被直接忽略。

4.4.1 设置块大小(1)

协议控制消息1是设置块大小,用于通知对端更新最大块大小。

最大块大小默认为128字节,但是客户端或服务器都可以修改该值,然后通过消息通知对端更新。例如,假设一个客户端希望发送131字节的音频数据,而此时块大小为128字节,这时候客户端就可以发送消息1修改块大小为131字节,然后客户端就可以在单个块中发送音频数据了。

最大块大小应当至少为128字节,极端情况下,必须大于1字节。最大块字节可由任意方向单独维护。

0: 该位必须为0
块大小(31位):该处保存最大块大小值,单位为字节,将用于之后所有发送的块直到有新的通知位置。有效大小是1到2147483647(0x7FFFFFFF),事实上,所有大于16777215(0xFFFFFF)的值都是等价的,因为没有块可以大于消息长度。

4.4.2 取消消息(2)

协议控制消息2是取消消息,用于通知对端如在等待某一消息的剩余块,请忽略该消息中所有已收到块。对端接收作为协议消息负载的块流ID值。应用可能在关闭时发送该消息来告知对端该消息的无需进行后续处理。

块流ID(32位):该变量保存块流ID,标识那条消息将被丢弃

4.4.3 确认(3)

客户端或服务器必须在接收窗口大小(window size)的数据后发送确认消息,窗口大小是发送端在收到接收端确认前可发送的最大数据字节数。该消息指明了序列数值,标识当前所收到字节数。

序列数值(32位):该变量保存目前收到所有字节数

4.4.4 窗口确认大小(5)

客户端或服务器通过发送该消息来通知对端发送确认消息的间隔窗口大小。发送端会在发送窗口大小的字节数后等待确认消息,而接收端必须在继最近一次发送确认消息后收到指定数值的字节后再次发送确认消息。

4.4.5 设置对端带宽(6)

客户端或服务器发送该消息来限制对端输出带宽,对端接收到该消息后会通过限制窗口大小来实现限制带宽。如对端窗口大小与带宽值不同,则需响应一条窗口确认大小消息。

限制类型是下列值之一:

  • 0 - 硬限制:对端必须限制输出带宽为所述窗口大小
  • 1 - 软限制:对端必须限制输出带宽为所述窗口大小或已生效窗口大小中的更小值
  • 2 - 动态:除非上一条消息中限制类型为应限制,则需要把该消息视为硬限制,否则可直接忽略

5. RTMP消息格式

本章节指明通过底层传输层(如RTMP块流)在网络端点间传输的RTMP消息格式。

虽然RTMP设计用于RTMP块流,但它也可使用其他传输协议发送消息。RTMP块流和RTMP广泛适用于音视频应用,如一对一、一对多的实时广播、点播以及交互会以应用。

5.1 RTMP消息格式

客户端和服务器通过在网络中发送RTMP消息来进行交互,这些消息可包含音频、视频、数据或其他消息。

RTMP消息分为两部分,分别为头和负载。

5.1.1 消息头

消息头包含如下部分:

5.1.2 消息负载

消息的另一部分,是消息的真正数据。如可为音频样本或压缩后视频数据。负载格式和解释不在本文档讨论范围内。

5.2 用户控制消息(4)

RTMP中息类型ID 4为用户控制消息,这些消息包含了RTMP流传输层使用的信息,协议消息ID为1/2/3/5/6则用于RTMP块流协议(Section 4.4)。

用户控制消息应该使用消息流ID 0(又称为控制流),且通过RTMP块流传输时使用块流ID 2。用户控制消息收到时立即生效,可直接忽略其时间戳。

客户端或服务器发送该消息来通知对端用户控制事件,该消息将携带事件类型和事件内容。

消息内容中的前两个字节表示事件类型,事件内容紧跟其后。事件内容长度是可变的,因此,为了让这些消息顺利通过RTMP块流层进行传输,最大块大小(Section 4.4.1)应当足够大以使得该消息可以单块传输。

事件类型和事件内容格式将在Section 6.1.7中列出。

6. RTMP命令消息

本章节讨论了服务器和客户端通过网络进行通讯中使用的不同类型的消息和命令。

不同类型的消息被服务器和客户端进行交换,主要有用于发送音频数据的音频消息,发送视频数据的视频消息,发送任何用户数据的数据消息,共享对象消息,以及命令消息。共享对象消息提供了在多个客户端和服务器间管理分布式数据的通用方法。命令消息在客户端和服务器间携带AMF编码命令。一个客户端或服务器可通过在流中发送命令消息向对端申请RPC(Remote Procedure Calls)。

6.1 消息类型

服务器和客户端通过网络发送消息进行通讯,消息类型可以使音频消息、视频消息、命令消息、共享对象消息、数据消息和用户控制消息的任意一种。

6.1.1 命令消息(20, 17)

命令消息在客户端和服务期间携带AMF编码命令,这些消息中AMF0编码已被指定类型为20,AMF3编码已被指定消息类型为17。这些消息发送用于在对端执行一些操作如连接、创建流、发布publish、播放、暂停。命令消息如onstatus, result等用于通知发送端所请求命令的状态。一个命令消息包含命令名称、办理transaction ID,以及包含相关参数的命令对象。客户端或服务器可以通过发送命令消息至对端以请求RPC。

6.1.2 数据消息(18,15)

客户端或服务器通过该消息发送任何元数据Metadata或其他用户数据到对端。元数据包含数据(音视频等)细节如创建时间、时长、主题等等。这些消息中AFM0编码的被指定类型值为18,AMF3编码的指定类型为15。

6.1.3 共享对象消息(19, 16)

共享对象是一个Flash对象(一系列名字-值对),它用于多客户端、实例间同步。预留给共享对象事件中AMF0编码的为类型19,AMF3编码的为类型16。每条消息可包含多个事件。


目前支持如下事件类型:

6.1.4 音频消息(8)

客户端或服务器通过发送该消息来发送音频数据到对端,音频消息对应消息类型为8。

6.1.5 视频消息(9)

客户端或服务器通过发送该消息来发送视频数据到对端,视频消息对应消息类型为9。

6.1.6 聚合消息(22)

聚合消息时指一条消息中包含了一系列使用Section5.1中描述格式RTMP子消息,对应消息类型为22。


聚合消息的消息流ID覆盖了内部子消息的消息流ID。

聚合消息和第一条子消息在时间戳上的区别在于用于归一化至流时间stream timescale的偏移量offset。偏移量会被加至每条子消息的时间戳来达成归一化,第一条子消息的时间戳应该和聚合消息时间戳是一致的,所以偏移量应当是0。

后置指针处包含了前一条消息(包括头)的大小,主要用于支持FLV的逆向seek操作。

使用聚合消息有如下好处:

6.1.7 用户控制消息事件

客户端和服务器发送该消息来通知对端用户控制事件。关于该消息格式可参照Section 5.2。

目前支持如下用户控制事件类型:

6.2 命令类型

客户端和服务器交换AMF编码的命令。发送端发送一条命令消息,其中包含了命令名称、处理ID、以及含有相关参数的命令对象。例如,连接命令消息包含了’app'参数,以告知服务器客户端希望连接的目标程序。接收端处理这条命令并回复含有同样处理ID的响应。回复的字符串可能为_result、_error或方法名。如verifyClient或contactExternalServer.

_result或_error的命令字符创代表一条响应,处理ID则表明回复是针对哪条命令的,这在IMAP或其他协议中是完全相同的。命令字符串中的方法名表明发送端希望运行接收端上的一个方法。

下列类对象通常用于发送各种不同的命令:

6.2.1 NetConnection命令

NetConection管理着一个客户端程序和服务器之间的双向连接,除此之外,它还提供了对异步远程方法调用的支持。

下列命令可通过NetConnection进行发送:

6.2.1.1 connect

客户端发送connect命令至服务器端以请求连接至某一服务器程序实例。
该由客户端发送至服务器的命令结构如下:


下面是connect命令中命令对象所使用到的名-值对:

audioCodecs属性有如下标志值:

videoCodecs属性有如下标志值:

videoFunction属性有如下标志值:

object Encoding属性有如下标志值:

由服务器发送至客户端的命令结构如下:


命令执行期间消息流动如下:
  1. 客户端发送connect命令至服务器以请求连接至服务器端程序实例
  2. 在收到连接命令后,服务器端发送协议消息'Window Acknowledgement Size'给客户端。同时,服务器端还会连接收到connect命令中提到的应用
  3. 服务器端发送协议消息‘Set Peer Bandwidth’至客户端
  4. 客户端成功处理‘Set Peer Bandwidth’后发送协议消息‘Window Acknowledgement Size'给服务器端
  5. 服务器端发送另一类型为用户控制消息(StreamBegin)协议消息给客户端
  6. 服务器端发送命令消息以通知客户端连接状态(success/fail)。该命令中含有处理ID(与1中收到相同),该消息同时还制定了部分属性,如Flash Media Server版本(string)。除此之外,它还指定了连接响应相关的信息如level(string),code(string),description(string),objectencoding(number),等

6.2.1.2 Call

NetConnection对象的call方法用于远程调用接收端上的程序。需要远程调用的程序名称通过一个参数传递给call命令。

发送端至接收端的命令结构如下:


响应的命令结构如下:

6.2.1.3 createStream

客户端发送该命令至服务器端以创建一条逻辑通道用于传递消息,从而可以利用已创建的流通道发布音频、视频和元数据。

NetConnection是默认的通讯通道,流ID为0。协议和一些命令消息,包括createStream,使用默认通讯通道。

从客户端至服务器的命令结构如下:


从服务器至客户端的命令结构如下:

6.2.2 NetStream命令

NetStream定义了一条基于NetConnection的客户端至服务器间连接的,可以传递音频流、视频流以及消息流的通道。NetConnection对象支持多个NetStreams以传输多个数据流。

客户端可在NetStream中发送下列命令至服务器:

服务器端通过“onStatus"命令发送NetStream的状态更新至客户端:

6.2.2.1 play

客户端发送该命令值服务器端以播放一个流。多次调用该命令也可创建一个播放清单。

如果你希望创建一个在不同live或recorded流间切换的动态播放清单,需要多次调用play并传递false以避免每次reset。相反地,如果你希望立即播放某一指定流,传递true以清除等待播放嘟列中的所有其他流。

客户端发送至服务器的命令结构如下:


命令执行期间消息流动如下:
  1. 客户端在接收到来自服务器createStream成功回复后发送play命令
  2. 收到play命令后,服务器发送一条协议消息来设置块大小
  3. 服务器发送另一条协议消息(用户控制)来指定事件“StreamIsRecord”和流ID。该消息在前2字节中携带事件类型,并在最后4字节中携带流ID
  4. 服务器发送另一条协议消息(用户控制)来指定事件“StreamBegin”来告知客户端流状态的开始
  5. 如客户端请求的play命令成功执行后,服务器发送一条onStatus命令消息NetStream.Play.Start和NetStream.Play.Reset。只有客户端发送的play命令中包含reset标志时服务器才会回复NetStream.Play.Reset。如未查找到客户端请求play的流,服务器将在onStatus消息中返回NetStream.Play.StreamNotFound.

6.2.2.2 Play2

与play命令不同的是,Play可以在不改变正在播放内容的时间线情况下切换至不同比特率流。实质上是服务器端维护了所支持的所有不同比特率的文件,这些文件都可被客户端通过play2进行请求。

从客户端到服务器的命令结构如下:


NetStreamPlayOptions对象的公共属性在ActionScript3 Language Reference[AS3]中有详细描述。

该命令的具体消息流如下:

6.2.2.3 deleteStream

NetStream对象销毁前会发出deleteStream命令。
客户端至服务器的命令结构如下:


服务器端不会进行任何回复。

6.2.2.4 receiveAudio

NetStream发送receiveAudioMessage以告知服务器是否需要发送音频至客户端。

由客户端发送至服务器的命令结构如下:


当receiveAudio命令中bool标志为false时,服务器不会进行任何响应;如该标志为true,服务器会响应以状态消息NetStream.Seek.Notify和NetStream.Play.Start

6.2.2.5 receiveVideo

NetStream发送receiveVideo消息来告知服务器是否需要发送视频至客户端。

由客户端发送至服务器端的命令结构如下:


如receiveVideo命令中bool标志为false时,服务器不进行任何响应;如该标志为true,服务器会响应以NetStream.Seek.Notify和NetStream.Play.Start

6.2.2.6 publish

客户端发送publish命令以发布一个命名流至服务器。通过这个名字,任何客户端可以播放该流并接收已发布的音频、视频及数据消息。

从客户端至服务器的命令结构如下:


服务器响应onStatus命令以标识发布的开始。

6.2.2.7 seek

客户端发送seek命令以在媒体文件或播放列表中查找指定偏移量(单位为毫秒)。

从客户端至服务器的命令结构如下:


当seek成功后,服务器响应以一条status消息NetStream.Seek.Notify。如失败则返回一条_error消息。

6.2.2.8 pause

客户端发送pause命令来告知服务器暂停或开始播放。
从客户端至服务器的命令结构如下:


操作成功后,如流为停止状态,服务器响应以一条状态消息NetStream.Pause.Notify;如流为未停止状态,则返回NetStream.UnPause.Notify。如操作失败,则返回_error消息。

6.3 消息交换示例

下面是一些使用RTMP进行消息交换的示例。

6.3.1 发布录制的视频

本示例演示了一个publisher如何发布一个流并传输视频至服务器,其他客户端可进行订阅至已发布流并播放视频。

6.3.2 广播一个共享对象消息

本示例演示了一个共享对象创建和改变期间的消息交换过程,它同样描述了共享对象消息广播的处理过程。

7.3.3 发布已录制流中元数据

本示例描述了发布元数据时消息交换。

上一篇 下一篇

猜你喜欢

热点阅读