流媒体|从入门到出家:流媒体协议—RTMP | WebRTC编风网
0

流媒体|从入门到出家:流媒体协议—RTMP

已有 316 人阅读此文 - - 观止云(微信ID:bravovcloud) - dora

认识RTMP

RTMP协议是由Adobe提出的一个应用层的协议,主要用来解决流媒体数据传输的问题,是目前低延时直播应用最广泛的协议。在我们实际工作中,我们对RTMP应该再熟悉不过,因它是几乎所有编码器标准输出协议,是PC机打开浏览器就能播放(一般浏览器默认有Flash),也是所有CDN支持的最好的直播分发协议。所以,即便RTMP协议较为复杂,也有不少缺陷,但较长的一段时间内,还不会出现另一个协议将之取代。

本文将对RTMP协议的握手、分块、发布、播放等进行详细介绍。

握手

握手开始于客户端发送C0,C1块。在发送C2之前客户端必须等待接收S1。在发送任何数据之前客户端必须等待接收S2。服务端在发送S0和S1之前必须等待接收C0,也可以等待接收C1。服务端在发送S2之前必须等待接收C1。服务端在发送任何数据之前必须等待接收C2。

一般,客户端会直接一起发送C0C1,服务器端在收到C0C1后直接一起发送S0S1S2,最后客户端发送C2。

C0块

1字节,表示客户端要求的RTMP版本,一般是0x03。

 C1块

1536字节,包括4字节时间戳,4字节0x00,1528字节随机数。

S0块

1字节,表示服务器选择的RTMP版本,一般是0x03。

 S1块

1536字节,包括4字节时间戳,4字节0x00,1528字节随机数。

S2块

1536字节,包括4字节c1的时间戳,4字节s1的时间戳,1528字节c1随机数。

C2块

1536字节,包括4字节s1的时间戳,4字节c1的时间戳,1528字节s1随机数。

备注:以上是RTMP协议的简单握手,当服务器和客户端的握手是按照简单握手进行时,是不支持h264/aac的,有数据,但是没有视频和声音。程序要同时支持复杂握手和简单握手。握手时先尝试复杂握手,复杂握手失败时尝试简单握手。

复杂握手

复杂握手的流程与简单握手一样。只是数据有更多的含义。

C0块

1字节,使用0x06。

C1块

1536字节,4byes time + 4bytes version + 764bytes key block + 764bytes digestblock。key block 和 digest block 位置可以互换。即可以是4byes time + 4bytes version + 764bytes digest block + 764bytes keyblock。

key block

key block的格式如下:

1

key_offset: key字段在整个key block中的偏移量。

random0: 随机数,长度n就是key_offset的值。

key:随机数。

random1: 随机数。

digest block

2

digest_offset: digest的偏移量,偏移量是相对第五字节来计算的。也就是random0的长度。

random0: 随机数。

digest: 根据公式计算digest =HMACsha256(random0+random1, FPKey, 30),random0+random1的意思就是把这两个拼接到一起。

random1: 随机数。

 S0块

1字节,使用0x06。

S1块

S1的key block结构与C1结构相同。只是S1.key不是随机数而是使用公式S1.key= DH_compute_key(C1.key)这个公式算出来的。

S1的digest block结构与C1结构相同,S1.digest使用公式S1.digest= HMACsha256(random0+random1, FMSKey, 36)。

S2块

1504bytes random + 32bytes digest

S2的digest 使用公式 TmpKey= HMACsha256(C1.digest, FMSKey, 68); S2.digest=HMACsha256(random, TmpKey, 32)。

C2块

1504bytesrandom + 32bytes digest

C2的digest使用公式TmpKey= HMACsha256(S1.digest, FPKey, 62); S2.digest=HMACsha256(random, TmpKey, 32)。

备注:复杂握手中key block和digest block的位置并不固定。可以key block在前,也可digest block在前。客户端可以任选一种方式,而服务器要在使用一种方式解析失败时尝试另一种。

分块

块格式

3

 块类型fmt

fmt为0,表示11字节messageheader包括timestamp、message length、message type id、message stream id;

对于0类型的块。timestamp是消息的绝对时间戳;

fmt为1,表示7字节message header包括timestamp、message length、message type id;

对于1类型的块,message stream id与先前的块相同。而timestamp字段表示先前块的时间戳与当前块的时间戳的差值;

fmt为2,表示3字节message header 包括timestamp。而timestamp字段表示先前块的时间戳与当前块的时间戳的差值;

对于2类型的块,message length、message stream id 和 message type id与先前的块相同;

fmt为3,表示没有message header。message length、message stream id 、message type id、timestamp与先前的块相同。

chunk id

id为0,表示ID的范围是64-319(第二个字节+64);

id为1,表示ID范围是64-65599(第三个字节*256+第二个字节+64);

id为2,表示低层协议消息;

id为3 – 63,表示完整的流ID。

timestamp

时间戳。如果增量大于等于1677215(16进制0x00ffffff),这个值必须是16777215 ,并且扩展时间戳必须出现。否则这个值就是整个的增量。

message length

消息长度

message type id

消息类型

message stream id

消息流ID

extend timestamp

扩展时间戳,见timestamp

chunkdata

负载数据。叫payload data更为贴切。而message header中的message length的长度指的是负载数据的长度。

备注:在解析chunk数据时要注意解交错。对端可能会在一个chunk负载没有全部发送完毕的情况下发送另一个chunk的负载。

负载

负载是rtmp的各种消息,消息可以分为以下类型:命令消息、数据消息、音频消息、视频消息、聚合消息。

命令消息

命令消息承载用AMF编码的客户端与服务端之间的命令。消息类型为20的用AMF0编码,消息类型为17的用AMF3编码。这些消息用于在远端实现连接,创建流,发布,播放和暂停等操作。状态,结果等命令消息用于通知发送者请求命令的状态。

数据消息

客户端或服务端通过此消息向对方发送元数据。元数据包括数据的创建时间、时长、主题等细节。消息类型为18的用AMF0编码,消息类型为15的用AMF3编码。

音频消息

客户端或服务端发送本消息用于发送音频数据。消息类型8。

视频消息

客户端或服务端使用本消息向对方发送视频数据。消息类型值9。

聚合消息

聚合消息是含有一个消息列表的一种消息。消息类型值22。

备注:rtmp中的AMF3编码,是嵌套在AMF0中的。当AMF0的marker为0x11时,表示之后的包体是AMF3格式。

publish 流程

不同客户端publish的流程不一定完全一样,以ffmpeg推流为例。

4

首先客户端和服务器进行rtmp握手。握手之后客户端发送connect命令,并等待服务器响应。当获取服务器响应之后,发送releaseStream、FCPublish、createStream三个命令,并等待服务器响应。之后发送publish命令,获取服务器响应后就可以发送媒体数据了。

PLAY 流程

5

首先客户端和服务器进行rtmp握手。握手成功后客户端发送connect命令,并等待服务器响应。当获取服务器响应之后,发送createStream命令,同样等待服务器响应。最后发送play命令,服务器会使用user control message和三个不同的on status包相应。之后服务器发送媒体数据。

RTMP 延迟分析

RTMP协议的延迟组成主要受协议本身建连耗时,网络抖动的丢包重传耗时,编码器关键帧距离设置、播放器缓冲区大小设置这几方面因素影响。

RTMP是基于 TCP 之上的应用层协议,TCP 每次握手过程,慢启动过程中的每一次往来,都会加上一次往返耗时 ( RTT ),这些交互过程会增加延迟。

如果网络环境不佳,抖动严重, TCP 会丢包重传,故网络抖动可能导致丢包重传,也会导致延迟加大。RTMP也正因为TCP这个不丢包要重传的原因,它有累计延时的缺陷,即随着直播时间越长,如果网络抖动多,累计延迟会越来越大。

GOP,也就是两个I帧间的时间距离,我们知道解码端只有拿到I帧才开始解码,所以如果我们在编码端设置关键帧间隔为10秒的话,那么至少要10秒才有一个关键帧,延时至少也就10秒以上了。所以为了追求低延时的直播效果,在编码端可将关键帧间隔参数设置的尽可能小,比如1秒。

播放器的缓冲区也是同样道理。我们在播放器原理一文中有说,播放器都是从缓冲区拿数据渲染呈现。如果播放器缓冲区设置成了10秒,那么延迟至少就是10秒了。

总结

直播到底选择哪个协议,归结起来其实就是跨平台和延时之间的平衡。HLS跨平台好,但无奈延迟大,RTMP延时小要又依赖于Flash。事实就是这样,目前还没有一个一劳永逸的协议来替代RTMP。小编上周和Akamai 亚太区产品官在直播协议问题上进行了交流,美国人也纠结着呢。

 

0
相关文章!