me icon indicating copy to clipboard operation
me copied to clipboard

学习 rtmp 开发 (Part 5: rtmp protocol)

Open nonocast opened this issue 2 years ago • 0 comments

学习rtmp协议应以adobe spec为主,网上文章和代码只能作为cookie,不要太当回事情,包括这篇,在读pdf的过程中辅以wireshark,事半功倍。

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)
  • 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就可以直接控制推流,所以有很多想象空间。

参考文档

nonocast avatar May 18 '22 11:05 nonocast