me
me copied to clipboard
学习 rtmp 开发 (Part 5: rtmp protocol)
学习rtmp协议应以adobe spec为主,网上文章和代码只能作为cookie,不要太当回事情,包括这篇,在读pdf的过程中辅以wireshark,事半功倍。
- H. Parmar, M. Thornburgh rtmp_specification_1.0 December 21, 2012
- Adobe Video File Format Specification Version 10
- amf0-file-format-specification.pdf
RTMP全称Real Time Messaging Protocol, 我们分别理解Messaging和Real Time。
Messaging
Spec的chapter 6 RTMP Message Formats (p21) 中说到RTMP Message设计就是用来广泛支持audio-video applications, 包括one-to-one, one-to-mang以及video-on-demand等各种应用服务。所以说Message在这里是一个高度抽象的概念,适合各种情况。
- RTMP message 有两个部分: header和payload。
- header 采用固定的11个字节: Message Type (1 byte)+Payload length (3 bytes) + Timestamp (4 bytes) + Stream ID (3 bytes)
- Message Type 采用1个字节可以支持到255个type,RTMP给出的类型主要包括3大类:
- Protocol Control Messages
- Set Chunk Size (1)
- Abort Message (2)
- Acknowledgement (3)
- User Control Message (4)
- Stream Begin (0)
- Stream EOF (1)
- StreamDry (2)
- SetBufferLength (3)
- StreamIsRecorded (4)
- PingRequest (6)
- PingResponse (7)
- Window Acknowledgment Size (5)
- Set Peer Bandwidth (6)
- RTMP Media Message
- Audio Message (8)
- Video Message (9)
- RTMP Command Message
- Command Message (20, 17): RPC, 20采用AMF0, 17采用AMF3
- NetConnection commands: connect, call, close, createStream
- NetStream commands: play, play2, deleteStream, closeStream, receiveAudio, receiveVideo, pulish, seek, pause
- Data Message (18, 15): Metadata or any user data to the peer. 18采用AMF0, 15采用AMF3
- Shared Object Message (19, 16): A shared object is a Flash object (a collection of name value pairs) , 各端同步数据
- Aggregate Message (22)
- Command Message (20, 17): RPC, 20采用AMF0, 17采用AMF3
- Protocol Control Messages
- Timestamp: Time in milliseconds at which the data in this message applied.
- Stream ID
- Protocol Control Messages MUST stream ID =0,且 chunk stream ID 2
- Media Messages走的Stream ID为client通过createStream创建后得到Stream ID
- Command Message中NetConnection采用默认的stream ID 0, 后面stream的play, publish中通过stream name告诉server, 但同时也会设置对应的stream id
Real Time
所以Real Time就是对应chunk,将Message拆分成多个chunk进行传输,wireshark抓到的一个个RTMP packet就是chunk, chunk通过拆分message后可以根据消息的timestamp和优先级排序进行发送,比如Protocol Control Message就会优先在Media Message chunk之前发送。
- Chunk 有2个部分:Chunk Header 和 Chunk Data
- Chunk Header 有3部分组成:
- Basic Header (1 to 3 bytes)
- Message Header (0, 3, 7, or 11 bytes)
- Extended Timestamp (0 or 4 bytes)
- Chunk Data (variable size): The payload of this chunk, up to the configured maximum chunk size.
几点说明:
-
Basic Header的开头2个bit表示fmt, 决定了Chunk Message Header的format
- 0x00 对应 Type 0, 即 Chunk Message Header (0 byte)
- 0x01 对应 Type 1, 即 Chunk Message Header (3 bytes)
- 0x10 对应 Type 2, 即 Chunk Message Header (7 bytes)
- 0x11 对应 Type 3, 即 Chunk Message Header (11 bytes)
-
具体含义和sample在Spec都有说明,无非就是为了重复的audio,video时尽可能节省byte
-
Basic Header到底长度由csid (chunk stream id) 的大小决定,spec说协议保留0,1,2这3个csid,实现时可以用到3-65599共65597个csid,
An implementation SHOULD use the smallest representation that can hold the ID.
如果csid控制在63以内则只需要一个字节(fmt+csid),如果64-319则采用2个字节,更大的话就用3个字节,实践中basic header都是1个字节。 -
csid的唯一原则就是一个相同的message在拆分成多个chunk时采用唯一的csid,否则后续无法还原成原始message,协议并没有规定如何使用csid,你自己看着办,反正我支持65599个,我能想到的是如果一个server同时支持10000个rtmp流并发,且每个video message都大于max chunk size,则确实需要10000个csid来对应,否则关联不上。
Media Message Payload
RTMP Spec中没有描述audio和video的message payload,这个就需要参考 Adobe Video File Format Specification Version 10
FLV的Tag Header完美对应Message Header:
- TagType (UI8): 对应 Message Type (1 byte)
- DateSize (UI24): 对应 Length (3 bytes)
- Timestamp (UI24), Timestamp Extended (UI8): 对应 Timestamp (4 bytes)
- StreamID (UI24): Always 0. 表示flv只关联一个stream,保留这个字段就是为了struct reuse
所以FLV录制可以理解为如下2个步骤:
-
构建一个FLV Header
-
然后将RTMP audio message (8), video message (9), data message (18, AMF0)3个类型的消息直接append到flv文件,然后跟一个PreviousTagSize
-
最后就形成 FLV Header - PreviousTagSize(0) - data message - PreviousTagSize(1) - audio - PreviousTagSize(2) - video - PreviousTagSize(3) 一路下去
-
RTMP video payload对应FLV中的VIDEODATA, audio payload对应AUDIODATA,根据spec解析即可 参考 #271
扩展
很多时候我们都被Implementation所限制住,其实RTMP本身只是messaging protocol,并没有任何限制,比如说你完全可以通过多个stream传送多股流,或者通过command message实现让client按需推流,然后再给implementation加一个http api, 通过curl就可以直接控制推流,所以有很多想象空间。