大小端模式
理解字节序
Little-Endian就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端;Big-Endian就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。
举一个例子,比如数字0x12 34 56 78在内存中的表示形式为:
RTMP都是大端模式,所以发送数据,包头,交互消息都要填写大端模式的,但是只有streamID是小端模式
RTMP协议,Real Time Messaging Protocol(实时消息传输协议),是应用层协议,要靠底层可靠的传输层协议(TCP、UDP)来保证信息传输的可靠性的。
RTMP在传输层建立成功后,还要进行握手、建立链接RTMP Connection、建立流CreateStream、播放/发送四个步骤。
建立链接阶段去建立客户端和服务器之间的“网络链接”。
建立流阶段用于建立客户端和服务器之间的“网络流”。
播放/发送阶段用于传输音视频数据。
官方PDF
客户端要向服务器发送C0,C1,C2(按序)三个chunk,服务器向客户端发送S0,S1,S2(按序)三个chunk,然后才能进行有效的信息传输。RTMP协议本身并没有规定这6个Message的具体传输顺序,但RTMP协议的实现者需要保证这几点:
客户端要等收到S1之后才能发送C2
客户端要等收到S2之后才能发送其他信息(控制信息和真实音视频等数据)
服务端要等到收到C0之后发送S1
服务端必须等到收到C1之后才能发送S2
服务端必须等到收到C2之后才能发送其他信息(控制信息和真实音视频等数据)
如果每次发送一个握手chunk的话握手顺序会是这样:
实际实现中为了在保证握手的身份验证功能的基础上尽量减少通信的次数,一般的发送顺序是这样的,这一点可以通过wireshark抓ffmpeg推流包进行验证:
|client|Server |
|---C0+C1---->|
|<--S0+S1+S2-- |
|---C2----> |
Payload:
The data contained in a packet, for example audio samples or compressed video data. The payload format and interpretation are beyond the scope of this document.
Packet:
A data packet consists of fixed header and payload data. Some underlying protocols may require an encapsulation of the packet to be defined.
Message stream:
A logical channel of communication in which messages flow.
Chunk:
A fragment of a message. The messages are broken into smaller parts and interleaved before they are sent over the network. The chunks ensure timestamp-ordered end-to-end delivery of all messages, across multiple streams.
Chunk stream:
A logical channel of communication that allows flow of chunks in a particular direction. The chunk stream can travel from the client to the server and reverse.
Multiplexing:
Process of making separate audio/video data into one coherent audio/video stream, making it possible to transmit several video and audio simultaneously.
Action Message Format (AMF):
A compact binary format that is used to serialize ActionScript object graphs. AMF has two versions: AMF 0 [AMF0] and AMF 3 [AMF3].
In C0, this field identifies the RTMP version requested by the client. In S0, this field identifies the RTMP version selected by the server. The version defined by this specification is 3.
占一个字节,表达version,一般是3。
C1、S1长度为1536个字节,4个字节时间、4个字节的0,1528个字节Random data任意数据。
C2、S2长度为1536个字节,4个字节时间Time、4个字节时间Time2(S1、C1读取的时间),1528字节的Random echo,包含上面的Random data。
RTMP协议传输时会对数据做自己的格式化,这种格式的消息我们称之为RTMP Message
实际传输的时候为了更好地实现多路复用、分包和信息的公平性,发送端会把Message划分为带有Message ID的Chunk,每个Chunk可能是一个单独的Message,也可能是Message的一部分,在接受端会根据chunk中包含的data的长度,messageID和message的长度把chunk还原成完整的Message,从而实现信息的收发。
通过拆分,数据量较大的Message可以被拆分成较小的“Message”,这样就可以避免优先级低的消息持续发送阻塞优先级高的数据,比如在视频的传输过程中,会包括视频帧,音频帧和RTMP控制信息,如果持续发送音频数据或者控制数据的话可能就会造成视频帧的阻塞,然后就会造成看视频时最烦人的卡顿现象。同时对于数据量较小的Message,可以通过对Chunk Header的字段来压缩信息,从而减少信息的传输量。
Chunk的默认大小是128字节。
在传输过程中,通过一个叫做Set Chunk Size的控制信息可以设置Chunk数据量的最大值,在发送端和接受端会各自维护一个Chunk Size,可以分别设置这个值来改变自己这一方发送的Chunk的最大大小。
大一点的Chunk减少了计算每个chunk的时间从而减少了CPU的占用率,但是它会占用更多的时间在发送上,尤其是在低带宽的网络情况下,很可能会阻塞后面更重要信息的传输。
小一点的Chunk可以减少这种阻塞问题,但小的Chunk会引入过多额外的信息(Chunk中的Header),少量多次的传输也可能会造成发送的间断导致不能充分利用高带宽的优势,因此并不适合在高比特率的流中传输。
在实际发送时应对要发送的数据用不同的Chunk Size去尝试,通过抓包分析等手段得出合适的Chunk大小,并且在传输过程中可以根据当前的带宽信息和实际信息的大小动态调整Chunk的大小,从而尽量提高CPU的利用率并减少信息的阻塞机率。
每个Chunk由Chunk Header + Chunk Data组成
ChunkHeader又由Basic Header + Message Header + Extended Timestamp组成;
如下图所示:
This field encodes the chunk stream ID and the chunk type.
Chunk type determines the format of the encoded message header.
The length depends entirely on the chunk stream ID, which is a variable-length field.
根据chunk stream ID来决定Chunk Basic Header长度,1字节长到3字节长
The protocol supports up to 65597 streams with IDs 3-65599. The IDs 0, 1, and 2 are reserved. Chunk Stream ID with value 2 is reserved for low-level protocol control messages and commands.
RTMP协议只支持3-65599这么多的chunkID,共65597种流。 0、1、2保留,为其它协议的控制命令。
Value 0 indicates the 2 byte form and an ID in the range of 64-319 (the second byte + 64).
Value 1 indicates the 3 byte form and an ID in the range of 64-65599 ((the third byte)*256 + the second byte + 64).
Values in the range of 3-63 represent the complete stream ID.
0表示2字节、1表示3字节、其它表示1字节。
Chunk stream IDs 2-63 can be encoded in the 1-byte version of this field.
Chunk stream IDs 64-319 can be encoded in the 2-byte form of the header. ID is computed as (the second byte + 64,即2^8 + 64)
Chunk stream IDs 64-65599 can be encoded in the 3-byte version of this field. ID is computed as ((the third byte)*256 + (the second byte) + 64,即2^16 +64).
This field encodes information about the message being sent (whether in whole or in part).
The length can be determined by the “fmt” field in the chunk basic header.
fmt = 0,11个字节
This type MUST be used at the start of a chunk stream,
chunk stream的起始点。
fmt = 1,7字节
The message stream ID is not included; this chunk takes the same stream ID as the preceding chunk。
没有streamID的chunk Data,和前一个chunk的streamID相同。
fmt =2,3个字节
Neither the stream ID nor the message length is included; this chunk has the same stream ID and message length as the preceding chunk.
和前一个的streamID、length均相同。
fmt = 3,0个字节
When a single message is split into chunks, all chunks of a message except the first one SHOULD use this type.
当一个消息被分成多个chunks,除了第一个以外,都使用该类型。
注意: message type id 发送音视频数据的时候
The Extended Timestamp field is used to encode timestamps or timestamp deltas that are greater than 16777215 (0xFFFFFF);
超过了两个字节长度,则使用该头部。
The payload of this chunk, up to the configured maximum chunk size.
该chunk真实的数据,是协议商定的值。
是通过 Set Chunk Size协议控制消息设置的值,默认值为128字节。
均参考:深入理解rtmp(四)之协议实现分析
MessageType的范围1~7:
1~2 用于chunk协议
3~6 用于rtmp协议本身,协议控制消息必须要求
Message Stream ID=0 和 Chunk Stream ID=2
MessageType=8, Audio message:
客户端或服务端发送本消息用于发送音频数据。消息类型 8 ,保留为音频消息
MessageType=9, Video message:
客户端或服务端使用本消息向对方发送视频数据。消息类型值 9 ,保留为视频消息。
MessageType=15或18, Data message:
客户端或服务端通过本消息向对方发送元数据和用户数据。
元数据包括数据的创建时间、时长、主题等细节。
消息类型为 18 的用 AMF0 编码;消息类型为 15 的用AMF3 编码。
MessageType=16或19, Shared object message:
共享对象是跨多个客户端,实例同步的 FLASH 对象(名值对的集合)。
MessageType=17或20, Command message:
命令消息都是用AMF编码的,AMF有两种,为AMF0和AMF3。
命令消息有命令名,传输ID,和命名对象组成。而命名对象是由一系列参数组成的。
命令消息的类型有:
代表服务端和客户端之间连接的更高层的对象。包含4个命令类型:
RTMP握手之后先发送一个connect 命令消息,命令里面包含什么东西,协议中没有具体规定,实际通信中要指定一些编解码的信息,并以AMF格式发送。
服务器返回的是一个_result命令类型消息,这个消息的payload length一般不会大于128字节,但是在最新的nginx-rtmp中返回的消息长度会大于128字节。
消息的transactionID是用来标识command类型的消息的,服务器返回的_result消息可以通过 transactionID来区分是对哪个命令的回应,connect 命令发完之后还要发送其他命令消息,要保证他们的transactionID不相同。
Netstream建立在NetConnection之上,通过NetConnection的createStream命令创建,用于传输具体的音频、视频等信息。
在传输层协议之上只能连接一个NetConnection,但一个NetConnection可以建立多个NetStream来建立不同的流通道传输数据。
以下会列出一些常用的NetStream Commands,服务端收到命令后会通过onStatus的命令来响应客户端,表示当前NetStream的状态。
onStatus命令的消息结构如下:
客户端发送命令消息中releaseStream命令到服务器端
客户端发送命令消息中FCPublish命令到服务器端
客户端发送命令消息中的“创建流”(createStream)命令到服务器端。
服务器端接收到“创建流”命令后,发送命令消息中的“结果”(_result),通知客户端流的状态。
解析服务器返回的消息会得到一个stream ID, 这个ID也就是以后和服务器通信的 message stream ID, 一般返回的是1,不固定。
MessageType=22, Aggregate message,:
聚合消息是含有一个消息列表的一种消息。消息类型值 22 ,保留用于聚合消息。
通过块消息携带的数据,拼接成消息内容,通过AMF解读消息内容
本文梳理了RTMP协议相关格式,方便了对代码的理解。
参考:
吃透RTMP
RTMP推流及协议学习
深入理解rtmp(四)之协议实现分析
RTMP规范简单分析
官方文档
srs-librtmp