网络流媒体(六)———PTMP

0 简介

        RTMP — Real Time Messaging Protocol(实时消息传输协议),是Adobe公司为Flash播放器和服务器之间开发的音视频数据传输的开放协议,用来解决多媒体数据传输流的多路复用(Multiplexing)和分包(packetizing)的问题,一般传输flv或f4v格式的媒体流。
        RTMP协议是应用层协议,是要靠底层可靠的传输层协议(通常是TCP)来保证信息传输的可靠性的。在基于传输层协议的链接建立完成后,RTMP协议也要客户端和服务器通过“握手”来建立基于传输层链接之上的RTMP Connection链接,在Connection链接上会传输一些控制信息,如SetChunkSize,SetACKWindowSize。其中CreateStream命令会创建一个Stream链接,用于传输具体的音视频数据和控制这些信息传输的命令信息。RTMP协议传输时会对数据做自己的格式化,这种格式的消息我们称之为RTMP Message,而实际传输的时候为了更好地实现多路复用、分包和信息的公平性,发送端会把Message划分为带有Message ID的Chunk,每个Chunk可能是一个单独的Message,也可能是Message的一部分,接收端会根据chunk中包含的data的长度,message id和message的长度把chunk还原成完整的Message,从而实现信息的收发。
        RTMP是工作在TCP之上的明文协议,使用端口1935,能够保持长连接,并为用户提供低延时通信。由于基于flash,因此RTMP无法在iOS浏览器上播放,但其实时性较HLS好。RTMP是一个协议族,包括RTMP基本协议及RTMPT / RTMPS / RTMPE等多种变种:
      ① RTMPT封装在HTTP请求之中,可穿透防火墙;
      ② RTMPS类似于RTMPT,但使用的是HTTPS连接,增加了TLS/SSL安全功能;
      ③ RTMPE在RTMP的基础上增加了加密功能;
      ④ RTMFP(Real-Time Media Flow Protocol)使用UDP替代TCP传输RTMP数据,能够实现终端之间的直连和通信,适合P2P业务。
      RTMP定义了几种收发packets的虚拟通道,每个通道的操作都独立于其他通道,例如处理RPC请求和响应的通道,视频数据通道,音频数据通道,以及带外控制信息(例如实现分段大小的协商)通道,等等。在一个典型的RTMP会话中,多个通道可能在给定的任何时间同时激活。当编码生成RTMP数据时,会有一个packet header随之产生,packet header指定通道的ID、包生成的时间戳,以及packet payload的大小。而为了最大限度并且流畅地传输媒体数据,RTMP会对这些packets进行分段。Packet header之后紧跟按约定大小分段的payload内容,packet header不参与分段,并且其大小也不计入该包的第一个分段大小,也就是说,只有实际的packet payload(也就是媒体数据)才参与分段。
       在RTMP中,收发的packet被称为Message,相应的分段称为Chunk。RTMP传输时会对数据做相应的格式化,这种格式的消息称为RTMP Message,而在实际传输中,为了更好地多路复用、分包以及不同媒体流的Message收发的公平性,发送端会把Message切分成一个一个的Chunk,每个Chunk可能是一个单独的Message,也可能是某个Message的一部分。在接收端,根据Chunk中包含的Message Length、Message ID和Payload size等把Chunk还原成完整的Message。在发送过程中,为了实现来自不同Stream的Message能够按照时间戳先后有序传输,不同的Chunk是交织在一起发送的,每一个Chunk都有一个与所在媒体流相关的Chunk Stream ID。

1 Message Format

        这里的Message是指满足RTMP协议格式的,并可以切分成Chunk发送的消息。Message包含两个部分:message header和message body。
        Message header包含的字段如下:


        @Message Type:1字节的消息类型,指明当前Message是音频、视频还是控制消息等,如8代表音频数据,9代表视频数据,message type 1~6保留为协议控制消息使用;
        @Payload length:3字节的Message Payload长度,即消息所携带的音视频数据等信息的长度。该字段为大端序;
        @TimeStamp:4字节的时间戳,大端序;
        @Message Stream ID:3字节的消息流ID,大端序。
        Message Header后面紧跟的部分为Message Payload,也就是Message中携带的实际有效数据,例如,音频采样数据或压缩视频数据,其格式不在RTMP规定的格式范围内。

2 Chunk Format

        前面我们提到,Message在网络上传送之前,会被拆分成一个一个的chunks,并将来自不同流的chunks交织在一起发送,这样的机制允许将大的Message分成多个较小的Messages,可以防止低优先级的大Message(例如Video数据)阻塞高优先级的较小Message(例如audio和control数据),亦可减小一些较小message的发送开销,因为chunk header可以选择压缩的方式来表示。
chunk size是可配置的,默认是128 bytes,通过Set Chunk Size命令消息进行配置。较大的chunk size可以降低CPU的使用,但是由于发送时间较长,在带宽较低时会增加其他内容的延迟;而较小的chunksize则不太适合高码率的码流。每个传输方向独立维护自己的chunk size。
        Chunk格式如下:


         每个chunk包含chunk header和chunk data,chunk header又包含三个部分,分别是Basic Header,Message Header和Extended Timestamp。

2.1 Basic Header

        Basic Header占1~3 bytes,该域存储的是chunk type (fmt,2 bits)和chunk stream ID (CSID)。chunk type决定message header的格式,共有4种格式,放在后面讲。Basic Header域可能有1,2,3 bytes,取决于CSID,在能完整表示CSID的前提下,使用尽量少的字节以减少Header的开销。
        由于fmt域占2位,因此在Basic Header分别为1,2,3 bytes时,对应的CSID长度分别是6 bits、14 bits或22 bits。RTMP协议支持的CSID范围是3~65599,0~2为协议保留用作特殊信息,0表示Basic Header占2个字节,CSID范围是[64~319],即最大为(2^8 - 1) + 64 = 319;1表示Basic Header占3个字节,CSID范围是[64~65599],最大值为 (2^16 - 1) + 64 = 65599;而第一个字节的6 bits的值在[3~63]之间时,本身就表示了完整的CSID,此时Basic Header占1个字节;2表示一些保留的low-level协议控制信息和命令。
        下面分别是chunk basic header分别占1 byte、2 bytes、3 bytes时的格式:


2.2 Message Header

        下面我们来看chunk header的第二部分:Chunk Message Header。
        该域的可能长度为0,3,7或11 bytes,取决于Basic Header中chunk type (fmt域)的值。

2.2.1 type 0

        Type 0 Chunk Message Header占11 bytes。在一个Chunk Stream开始时,以及Timestamp回退(例如回退播放)时必须采用这种形式的Message Header。格式如下,其内容基本就是前面第一部分Message Format中讲到的未经chunk的message header,不同的是,这里的timestamp是用3 bytes表示的,而不是原来的4 bytes。

        @timestamp:3 bytes,能表示的最大时间戳为2^24 – 1 = 0xFFFFFF,若时间戳的值超过该最大值时,这三个字节都置1,这样实际的timestamp会转存到32位的Extended Timestamp字段中。接收端在检测到该域的24位全为1时,就会去Extended Timestamp字段中解析实际的时间戳;否则,从该域解析出实际的时间戳。

2.2.2 type 1        

         Type 1 Chunk Message Header占7个字节。不包含stream ID,其stream ID与上一个Chunk的stream ID相同,在收发两端只有一个流连接时,尽量采取这种格式。

        @timestamp delta:3 bytes,与type 0 不同,这里存储的是当前chunk与上一个chunk的时间戳的差值。在存储与计算方式上与type 0相同,当存储的值超过0xFFFFFF时,该域的24位都被置1,实际的时间戳差值存入32位的Extended Timestamp字段中。

2.2.3 type 2

      Type 2的chunk header只有3个字节,在type 1的基础上进一步省略了stream ID和message length,表示当前chunk与上一chunk有相同的stream ID和message length。具有相同message size的流(例如一些audio和数据格式)可用这种格式。该chunk header中的timestamp delta与type 1中的存储和计算方式相同。

2.2.4 type 3 

       0字节,即类型3的快没有消息头。表示当前chunk的Chunk Message Header与上一个chunk完全相同。当type 3的chunk跟在type 0 chunk后面时,表示和前一个chunk的时间戳相同,一般在一个Message被拆分成多个chunk时会存在这种情况,比如一个较大的video message,可能会被拆分成多个chunk,而每个chunk的时间戳都相同。当type 3的chunk跟在type 1或type2的chunk后面时,表示与前一个chunk的时间戳的差值相同。

2.3 Extended Timestamp

        该域长度为0或4 bytes。前面我们提到,当Message Header中的24 bits timestamp或timestamp delta域的值超过0xFFFFFF(16777215)时(该24 bits全部被置1),该域用来存储实际的32bits的timestamp或者timestamp delta。当type 0,1,2的Message Header中的timestamp或timestamp delta域的24 bits都被置1时,启用32 bits的Extended Timestamp域,否则Extended Timestamp域不存在。如果是type 3,该域存在的条件是,其前一个具有相同stream ID的type 0, 1,或2的chunk指示了extended timestamp域存在。

2.4 Chunk Data

        可变长度的payload,例如实际的音频数据、视频数据或控制信息,默认长度为128 bytes,长度可通过Set Chunk Size命令进行配置,最大长度不超过配置的最大chunk size。

2.5 Chunk Examples

2.5.1 audio chunk example

        下表中是一个audio message的序列,每个message有相同的message stream ID,message type ID,message length,以及增量相同的timestamp。

        将以上message序列封装成chunk序列后,每个chunk的格式如下

         可以看到,将以上4个audio message通过chunk封装后,形成1个type 0 chunk + 1个type 2 chunk + 2个type 3 chunk,由于其他message header信息都相同,第二个chunk相对于第一个chunk,只有20的timestamp delta,且CSID为3,只需1 Byte的Basic Header即可表示,因此第二个chunk的header只需1 byte的Basic Header + 3 bytes的timestamp delta,共4 bytes;第三和第四个chunk的chunk message header则与第二个chunk的完全相同,因此只需1 Byte的Basic Header即可。

2.5.2 video chunk example

         下表中表示一个video message,由于数据量较多,无法通过一个chunk(默认大小128 bytes)发送,因此需要切分成多个chunks。

        切分之后的chunks序列如下,由于对于video message来说,切分后的chunks具有完全相同的头信息(一般一个video message是同一帧数据,具有相同的timestamp),因此切分之后是一个type 0 chunk + 若干 type 3 chunks,除了最后一个chunk之外,其他chunk的size都是最大chunk size。

3 Message Types

         最后,我们来简单介绍一下RTMP的message类型,RTMP中有如下一些Message types:
        @ User Control Message (message typeID = 4):用户控制消息,告知对端执行该信息中包含的用户控制事件。
        @ Command Messages (message typeID = 20 / 17):命令消息,表示服务器和客户端之间传递的一些特定操作的命令消息,例如connect,publish,play等,AMF0编码时,message type ID = 20,AMF3编码时,message type ID = 17。
        @ Data Message (message type ID =18 / 15):传递一些元数据的消息,例如视频名、分辨率等,或某些用户自定义消息,AMF0编码时,message type ID = 18,AMF3编码时,message type ID = 15。
        @ Shared Object Message (message type ID = 19 /16):共享消息,当使用AMF0编码时,message type ID =19;使用AMF3编码时,message type ID = 16。
        @ Audio Message (message type ID= 8):音频数据。
        @ Video Message (message type ID= 9):视频数据。
        @ Aggregate Message (message typeID = 22):聚合消息,多个RTMP子消息的集合。

4 Protocol Control Messages

        RTMP Chunk Stream将message type ID 1,2,3,5和6用于协议控制消息,这些协议控制消息必须使用message stream ID 0(即控制流ID),并在chunk stream ID (CSID) 2上发送。协议控制消息在被接收到的瞬间生效,其时间戳被接收端忽略。

4.1 Set Chunk Size (message type ID = 1)

        Set Chunk Size消息使用协议控制消息ID 1,用于通知对端新的最大chunk size。最大chunk size默认为128字节,但客户端和服务器都可以修改该值,并通过Set Chunk Size命令通知对端更新。例如,假如客户端要发送131 bytes大小的音频数据至服务器端,而当前的chunk size为128 bytes,那么客户端就可以通过Set Chunk Size消息告知服务器端最新的chunk size是131 bytes,从而,客户端即可在1个chunk上发送131 bytes的音频数据了。最大chunk size至少为128 byte,内容最少为1byte,客户端和服务器端两方均可维护自己的最大chunk size。
        下面为Set Chunk Size的message payload:

        最高位必须为0。Chunk Size占31位,最大值为2147483647(0x7FFFFFFF),但实际上所有大于16777215(0xFFFFFF)的值都认为等于16777215,因为Message的最大长度为0xFFFFFF,而chunk size不可能大于Message的长度。

4.2 Abort Message (message type ID = 2)

        当一个message被切分成多个chunks,接收端只接收到了部分chunks时,发送该控制消息通知对端不再传输该message的chunks,接收端在收到该消息后,会丢弃已接收到的不能组成完整message的chunks。该控制消息的payload中只有一个CSID,该CSID表示的当前消息的chunks都被丢弃。Abort Message的payload如下:

       32 bits的CSID:该CSID的当前message不再传输后续的chunks,已接收到的chunks将被丢弃。

4.3 Acknowledgement (message type ID = 3)

        当收到对端的消息大小等于窗口大小(window size)时,接收端必须返回一个ACK给发送端。窗口大小就是指收到接收端反馈的ACK前最多可以发送的字节数量。ACK中携带一个4字节的sequence number,表明目前已经接收到的字节总数。

4.4 Window Acknowledgement Size (message type ID = 5)

        客户端和服务器端发送该消息通知对端在发送acknowledgements之间使用的window size。发送端在发送等于window size大小的数据后,会期待对端有一个确认消息(上一节的acknowledgement)返回;而接收端自从发送完上一个acknowledgement(或从会话的开始)后,若接收到指定大小的数据,则必须向发送端反馈一个acknowledgement消息。

4.5 Set Peer Bandwidth (message type ID = 6)

        客户端或服务器通过发送该消息来限制对端的输出带宽。接收端在收到该消息后,会限制已发送但未收到ACK的数据大小至该消息指定的window size大小。接收端在收到该消息后,如果该消息指定的window size与上一次反馈给发送端的window size大小不一致,应该再次反馈一个Window Acknowledgement Size消息给发送端。

        该消息的Payload包含5个字节,其中4字节的Acknowledgement Window Size和1字节的Limit Type,Limit Type有如下取值:
       0 – Hard:对端应该限制其输出带宽至该消息指定带宽;
       1 – Soft:对端应将带宽限制在该消息指定的值以内,或者按照已生效的带宽限制,以两者中较小的值为准;
       2 – Dynamic:若上一个Limit Type为Hard,将当前message的Limit Type也标为Hard,否则忽略该消息。   

5 Handshake

        RTMP 连接的建立首先通过握手(handshake)开始。Handshake消息不同于协议的其他消息,并非由可变大小的chunk和chunk header组成,而是由固定大小的三个chunks组成。客户端和服务器依次发送这三个同样的chunks,为了方便表述,客户端发送的三个chunks记为C0,C1和C2,服务器端发送的三个chunks记为S0,S1和S2。

5.1 Handshake Sequence

        Handshake始于客户端发送C0和C1,RTMP协议并没有严格规定6个Handshake message发送顺序,但有如下几个限制:
        ① 客户端必须收到S1后,才能发送C2;
        ② 客户端必须收到S2后,才能发送其他数据;
        ③ 服务器端必须收到C0后,才能发送S0和S1;
        ④ 服务器端必须收到C1后,才能发送S2;
        ⑤ 服务器端必须收到C2后才能发送其他数据。
        Handshake sequence如下图所示,

          在实际使用中,一般的Handshake顺序是这样的:
| client | Server |
|---C0+C1—->|
|<--S0+S1+S2–|
|---C2---->|
         下面是通过PC播放香港电视台直播,并通过wireshark抓包得到的RTMP握手过程。

5.2 C0、S0 Format

        C0和S0为单字节chunk,格式如下:

        Version表示RTMP协议的版本号,目前使用的是版本3。例如,前面抓取的香港电视台直播的handshake message,将,C0和S0的包展开如下:

5.3 C0、S0 Format

        C1和S1 message长1536字节,包含如下组成:

       @ Time:4 bytes的时间戳;
       @ Zero:4 bytes的全零域;
       @ Random bytes:1528 bytes的随机数。

5.4 C2、S2 Format

        C2和S2 的包也是1536字节长,几乎分别是对S1和C1的回声,其组成如下:

       @ Time:对于C2来说,该域的值与S1的time域相同;对于S2来说,该域的值与1的time域相同。
       @ Time2:该域存储的是对端送来的上一个packet(S1或C1)被读取的时间。
       @ Random echo:该域的数据必须是S1(对于C2来说)和C1(对于S2来说)域中的数据。

6 RTMP Command Messages

       我们提到RTMP中的message类型,包括:audio message、video message、data message、shared object message、command message。Command message是客户端和服务器用来交换AMF编码的命令的消息。发送端发送的消息中包含:命令名称(command name)、事务ID(transaction ID)、以及包含相关参数的命令对象(command object)。接收端处理接收到的command message后,会回馈给发送端一个包含相同transaction ID的回复消息。回复的消息可能是:_result、_error或method name,其中_result表示接受该命令,对端可以继续往下执行相关流程;_error表示拒绝该命令;method name表示要在对端执行的函数或方法名称,比如verifyClient或contactExternalServer。
        Command消息主要分为两类:NetConnection、NetStream。NetConnection为服务器和客户端的高层表示,用于管理两端之间的连接状态;NetStream是信息流的传输通道,也会传输一些诸如play、pause等控制信息流的命令。

6.1 NetConnection Commands

        NetConnection管理服务器和客户端的双向连接,此外,它还提供异步远程方法调用(RPC)。NetConnection上可传输下列命令:connect、call、close、createStream。

6.1.1 connect

        客户端向服务器端发送connect请求来建立连接。
        ① 客户端发送到服务端
        connect命令结构为:

        以下是为connect命令中使用的名值对对象的描述。

 

        AudioCodecs属性的标识值:

        VideoCodecs属性的标识值:

       ② 服务发送到客户端
       Connect命令结构:

6.1.2 call

        Call方法用于接收端的远程过程调用(RPC),被调用的RPC名称作为参数在Call命令中传输。Call请求和反馈的消息结构如下:

6.1.3 createStream

        客户端向服务器端发送该消息以创建message通信的逻辑通道。音频、视频和元数据的发布都是通过该命令创建的流通道来传输的。该命令的请求和回复消息结构如下:

6.2 NetStream Commands

        NetStream定义了NetConnection连接的客户端和服务器之间用于传输音频、视频和数据的通道。一个netConnection可支持不同数据流的多个netStreams。netStream上可传输的从client到server方向的命令有:play,play2,deleteStream,closeStream,receiveAudio,receiveVideo,publish,seek,pause;在server到client方向,有”onStatus”命令,用于更新netStream的状态。
        onStatus命令消息结构如下:

6.2.1 play

       客户端向服务器发送该命令来实现一个流的播放。也可以通过多次执行该命令来形成一个播放列表。命令结构如下:

6.2.2 play2

        与play命令不同,play2可以将正在播放的流切换到相同内容的不同比特率的流上。服务器端维护了不同比特率的文件供客户端发送play2请求切换。命令结构如下:

6.2.3 deleteStream

        客户端发送该命令告知服务器本地的某个流被销毁,不需要再传输此路流;服务器不需要回复该命令。该命令的结构如下:

6.2.4 receiveAudio

       客户端发送该消息通知服务器是否接收Audio数据。

        如果Bool Flag设为false,表示客户端不接收audio数据,服务器端无需回复该消息;如果Bool Flag为true,表示客户端要接收audio数据,则服务器端会回复NetStream.Seek.Notify和NetStream.Play.Start的状态消息。

6.2.5 receiveVideo

       与receiveAudio类似,该消息用于通知服务器端是否接收视频数据,除了Command Name字段为“receiveVideo”,其他字段都与receiveAudio相同,不再赘述。

6.2.6 publish

       客户端通过该命令向服务器发布某个名称的流(推流),发布成功后,其他客户端都可通过该名称来访问服务器上已发布的音频、视频或数据流。服务器会向客户端回复一个表示发布开始的onStatus命令。

6.2.7 seek

        定位到视频或音频的某个位置,以毫秒为单位。若seek成功,服务器回复一个NetStream.Seek.Notify状态消息;若seek失败,回复_error消息。

6.2.8 pause

        暂停命令。若暂停,服务器回复NetStream.Pause.Notify状态;若取消暂停,回复NetStream.Unpause.Notify;若失败,返回_error消息。

7 Message Exchange Examples

7.1 Publish Recorded Video

7.2 Broadcast a Shared Object Message

7.3 Publish Metadata from Recorded Stream

8 参考文献

https://blog.csdn.net/deliapu/article/details/79229720

https://blog.csdn.net/DeliaPu/article/details/79273670

http://www.hangge.com/blog/cache/detail_1325.html

https://blog.csdn.net/qq_31186123/article/details/82907303

https://blog.csdn.net/leixiaohua1020/article/details/12952977

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值