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

Content


第一篇章 流媒体原理

1.1 流媒体概念

1.2 流式传输特点

1.3 流媒体系统构成

1.4 流媒体涉及技术

1.5 流媒体应用

1.6 国内外大型流媒体系统

1.7 总结

流媒体相关术语

第二篇章 流媒体系统

2.1 编码工具

2.2 流媒体服务器

2.3 CDN分发网络

2.4 网络协议

2.5 播放器

总结:从一个直播APP看流媒体系统的应用

第三篇章 流媒体协议

3.1 HTTP

3.2 RTMP

3.3 FLV

3.4 HLS

3.5 Websocket

3.6 URL

第三章 流媒体协议

(by 观止云@徐斌 观止云PM羌人彧)

认识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的格式如下:

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

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

□ key:随机数。

□ random1: 随机数。

▣ digest block

□ 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在前。客户端可以任选一种方式,而服务器要在使用一种方式解析失败时尝试另一种。

◎ 分 块

① 块格式

② 块类型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推流为例。

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

PLAY 流程

首先客户端和服务器进行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 亚太区产品官在直播协议问题上进行了交流,美国人也纠结着呢。

更多精彩内容,持续推送中,敬请关注!

本篇内容由观止云原创,未经许可,禁止转载。

更多干货,尽在“观止云”微信订阅号(微信号bravovcloud)

举报
评论 0