一、握手:
1、C0:C—>S
2、S0: S—>C
名称:C0 S0
长度:1字节
对于版本号的定义:当前rtmp协议的版本号一致为“3”,0、1、2是旧版本号,已经弃用。4-31被保留为rtmp协议的未来实现版本使用;32-255不允许使用。如果服务器端或者客户端收到的C0字段解析出为非03,如果是0x06考虑使用openssl进行解密C1 C2 S1 S2,如果对端不支持加密字段可以选择以版本3来响应,也可以放弃握手。
简单握手:
作用:C0和S0一致,都是一个字节,都代表当前使用的rtmp协议的版本号。如果服务器端或者客户端收到的C0/S0字段解析出为非03,对端可以选择以版本3来响应,也可以放弃握手。
复杂握手:
作用:说明是明文还是密文。如果使用的是明文(0X03),同时代表当前使用的rtmp协议的版本号。如果是密文,该位为0x06
3、C1: C—>S
4、S1: S—>C
名称:C1 & S1
长度:1536字节
简单握手:
作用:
包结构:
time(4字节)+zero(4字节)+ random data(1528字节)。
Time(4字节):这个字段包含一个timestamp,用于本终端发送的所有后续块的时间起点。这个值可以是0,或者一些任意值。要同步多个块流,终端可以发送其他块流当前的timestamp的值,以此让当前流跟要同步的流保持时间上的同步。
Zero (4个字节):这个字段必须都是0。如果不是0,代表要使用complex handshack。
Random data (1528个字节):这个字段可以包含任意值。终端需要区分出响应来自它发起的握手还是对端发起的握手,这个数据应该发送一些足够随机的数。这个不需要对随机数进行加密保护,也不需要动态值。
复杂握手:
作用:用于验证服务器端或者client端的有效性。
包结构:
time(4字节)+version(4字节)+key(764字节)+digest(764字节)。总共1536字节
其中,key和digest可能会交换位置,也就如下图有两种格式:schemal0&schemal1。
客户端决定使用哪种schema方式,服务器端比较倒霉,需要将两种方式都尝试,一般是先按照schema0解析,失败则使用schema1解析。但是无论key和digest位置如何,它们的结构是不变的。
Time(4字节):这个字段包含一个timestamp,用于本终端发送的所有后续块的时间起点。这个值可以是0,或者一些任意值。要同步多个块流,终端可以发送其他块流当前的timestamp的值,以此让当前流跟要同步的流保持时间上的同步。
Version(4个字节):4bytes 为程序版本。C1一般是0x80000702。S1是0x04050001。貌似这个可以随意填写,但是要采用非0值跟simple handshack区分。
Key(764个字节):
random-data:长度由这个字段的最后4个byte决定,即761-764
key-data:128个字节。Key字段对应C1和S1有不同的算法,这个需要注意。后面会详细解释。发送端(C1)中的Key应该是随机的,接收端(S1)的key需要按照发送端的key去计算然后返回给发送端。
random-data:(764-offset-128-4)个字节
key_offset:4字节, 最后4字节定义了key的offset(相对于KeyBlock开头而言,相当于第一个random_data的长度)
Digest(764个字节):
offset:4字节, 开头4字节定义了digest的offset(相对于DigestBlock的第5字节而言,offset=3表示digestBlock[7~38]为digest,【4-6】即为第一个random_data)
random-data:长度由这个字段起始的4个byte决定
digest-data:32个字节。Digest字段对应C1和S1有不同的算法,这个需要注意。后面会详细解释。
random-data:(764-4-offset-32)个字节
算法:
C1的key为128bytes随机数。C1_32bytes_digest= HMACsha256(P1+P2, 1504, FPKey, 30) ,其中P1为digest之前的部分,P2为digest之后的部分,P1+P2是将这两部分拷贝到新的数组,共1536-32长度。S1的key根据 C1的key算出来。
S1的digest算法同C1。注意,必须先计算S1的key,因为key变化后digest也重新计算。
5、C2: c—>s
6、S2: s—>c
名称:C2 & S2
长度:1536字节。
简单握手:
作用:基本是C1&S1的副本
包结构:
time(4字节)+ Time2(4字节)+randomecho(1528字节)。
Time(4个字节):这个字段必须包含终端在S1 (给 C2) 或者 C1 (给 S2) 发的 timestamp。
Time2 (4个字节):这个字段必须包含终端先前发出数据包 (s1 或者 c1) timestamp。
Randomecho (1528个字节):这个字段必须包含终端发的 S1 (给 C2) 或者 S2 (给 C1) 的随机数。两端都可以一起使用 time 和 time2 字段再加当前 timestamp 以快速估算带宽和/或者连接延迟,但这不太可能是有多大用处。
复杂握手:
作用:主要是用来提供对C1 S1的验证
包结构:
randomdata(1504字节)+Digest-data(32字节)。
验证算法:
分别拿到C1 和S1的数据,按照上文定义的计算方法再将C1或S1的digest字段计算一遍,跟当前从C1和S1中拿到的Digest字段进行比较
以上,关于rtmp协议握手的地方已经全部阐述完成。下面将分应用模式对各包进行解释。
二、观看流程:
7、Connect(‘stream名’):属于命令消息类型(c—>s)
名称:connect
作用:客户端发送 connect 命令到服务器端来请求连接到一个服务器应用的实例
包结构:(结构跟publish中的connect一致)
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+body size(3byte)+Typeid(1byte)+stream id(4byte)
其中:Type id可能为0x14,代表包将采用AMF0方式进行编码。也可以为0x11,此时表示包将采用AMF3方式进行编码。
RTMP_body:
Command Name(字符串,命令的名字。设置给 "connect")+ Transaction ID(数字,总是设置为 1)+ Command Object(对象,具有名值对(名:值)的命令信息对象)+ Optional User Arguments(对象,可选)+End of objectMarker(0x00 0x00 0x09)
包体中包含了很多“对象(object)”信息。具体内容请参照rtmp官方给出的协议。上面有详细介绍。
8、Window Acknowledgement Size: 属于协议控制消息(C—>S)
名称:窗口确认大小。
作用:客户端或者服务器端发送这条消息来通知对端发送和应答之间的窗口大小。发送者在发送完窗口大小字节之后期待对端的确认。接收端在上次确认发送后接收到的指示数值后,或者会话建立之后尚未发送确认,必须发送一个确认。
包结构:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,type id是一定的为5
RTMP_body:
WindowAcknowledgement Size(4byte)
9、Set peer bandwidth: 属于协议控制消息(C—>S)(type id=6)
名称:设置对端带宽。
作用:客户端或者服务器端发送这一消息来限制其对端的输出带宽。对端接收到这一消息后,将通过限制这一消息中窗口大小指出的已发送但未被答复的数据的数量以限制其输出带宽。如果这个窗口大小不同于其发送给 (设置对端带宽) 发送者的最后一条消息,那么接收到这一消息的对端应该回复一个窗口确认大小消息。
包结构:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,type id是一定的为6
RTMP_body:
WindowAcknowledgement Size(4byte)+Limit type(1byte)
限制类型取以下值之一:
0 – Hard:对端应该限制其输出带宽到指示的窗口大小。
1 – Soft:对端应该限制其输出带宽到指示的窗口大小,或者已经有限制在其作用的话就取两者之间的较小值。
2 – Dynamic:如果先前的限制类型为Hard,处理这个消息就好像它被标记为 Hard,否则的话忽略这个消息。
10、Stream begin 0:属于用户控制消息.(C—>S)
名称:。
作用:服务器发送这个事件来通知客户端一个流已就绪并可以用来通信。默认情况下,这一事件在成功接收到客户端的应用连接命令之后以 ID 0 发送。这一事件数据为 4 字节,代表了已就绪流的流 ID。
包结构:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,type id是一定的为4.csid是一定的为2.stream id是一定的为0。并且timestamp将被忽略。
RTMP_body:
Eventtype:stream begin(0)(2byte)+4byte的数据
11、_result(netconnect.connect.success):属于命令消息类型(S–>C)
名称:服务器应答命令。
作用:服务器端向客户端发送的关于之前客户端向服务器端请求的命令的响应结果。
包结构:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,Type id可能为0x14,代表包将采用AMF0方式进行编码。也可以为0x11,此时表示包将采用AMF3方式进行编码。
RTMP_body:
字段名 |
类型 |
描述 |
Command Name |
字符串 |
_result 或者 _error;表明回复是一个结果还是错误。 |
Transaction ID |
数字 |
响应所属的命令的 ID。 |
Command Object |
对象 |
如果存在一些命令信息要设置这个对象,否则置空。 |
Stream ID |
数字 |
返回值要么是一个流 ID 要么是一个错误信息对象。 |
12、onBWDone():属于命令消息类型(S–>C)
名称:
作用:服务器端向客户端发送的关于之前客户端向服务器端请求的命令的响应结果。
包结构:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,Type id可能为0x14,代表包将采用AMF0方式进行编码。也可以为0x11,此时表示包将采用AMF3方式进行编码。
RTMP_body:
13、Window acknowledgement size: 属于协议控制消息(C—>S)具体参照上文。
14、creatStream(): 属于协议控制消息(C—>S)
名称:
作用:客户端发送这一命令到服务器端以为消息连接创建一个逻辑通道。音频、视频和元数据使用 createStream 命令创建的流通道传输。
包结构:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,Type id可能为0x14,代表包将采用AMF0方式进行编码。也可以为0x11,此时表示包将采用AMF3方式进行编码。
RTMP_body:
字段名 |
类型 |
描述 |
Command Name |
字符串 |
命令名。设置给 "createStream"。 |
Transaction ID |
数字 |
命令的事务 ID。 |
Command Object |
对象 |
如果存在一些命令信息要设置这个对象,否则置空。 |
15、set Buffer Length:属于用户控制消息.(C—>S)
名称:窗口确认大小。
作用:客户端发送这一事件来通知服务器端用于缓存流中任何数据的缓存大小 (以毫秒为单位)。这一事件在服务器端开始处理流之前就发送。这一事件数据的前 4 个字节代表了流 ID 后 4 个字节代表了以毫秒为单位的缓存的长度。
包结构:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,type id是一定的为4.csid是一定的为2.stream id是一定的为0。并且timestamp将被忽略。
RTMP_body:
Eventtype:Set Buffer Length(3)(2byte)+ 流ID(4byte) + 缓存的长度(以毫秒为单位,4byte)
16、_result(netconnect.connect.success):属于命令消息类型(S–>C)。具体内容参照上文
17、Play(‘app名’):属于命令消息类型(c—>s)
名称:play
作用:客户端发送这一命令到服务器端以播放流。也可以多次使用这一命令以创建一个播放列表。
如果你想要创建一个动态的播放列表这一可以在不同的直播流或者录制流之间进行切换播放的话,多次调用 play 方法,并在每次调用时设置Reset的值为 false。相反的,如果你想要立即播放指定流,需要将其他等待播放的流清空,并为将Reset设为true。
包结构:(结构跟publish中的connect一致)
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+body size(3byte)+Typeid(1byte)+stream id(4byte)
其中:Type id可能为0x14,代表包将采用AMF0方式进行编码。也可以为0x11,此时表示包将采用AMF3方式进行编码。
RTMP_body:
字段名 |
类型 |
描述 |
Command Name |
字符串 |
命令名。设为 "play"。 |
Transaction ID |
数字 |
事务 ID设为 0。 |
Command Object |
Null |
命令信息不存在。设为 null类型。 |
Stream Name |
字符串 |
要播放流的名字。要播放视频 (FLV)文件,使用没有文件扩展名的名字对流名进行定义 (例如,"sample")。要重播 MP3或者 ID3,你必须在流名前加上 mp3:例如,"mp3:sample"。要播放 H.264/AAC文件,你必须在流名前加上 mp4:并指定文件扩展名。例如,要播放 sample.m4v文件,定义 "mp4:sample.m4v"。 |
Start |
数字 |
一个可选的参数,直播流交互过程中没有此字段,可能是没有添加。该字段以秒为单位定义开始时间。默认值为 -2,表示用户首先尝试播放流名字段中定义的直播流。如果那个名字的直播流没有找到,它将播放同名的录制流。如果没有那个名字的录制流,客户端将等待一个新的那个名字的直播流,并当其有效时进行播放。如果你在 Start字段中传递 -1,那么就只播放流名中定义的那个名字的直播流。如果你在 Start字段中传递 0或一个整数,那么将从 Start 字段定义的时间开始播放流名中定义的那个录制流。如果没有找到录制流,那么将播放播放列表中的下一项。 |
Duration |
数字 |
一个可选的参数,以秒为单位定义了回放的持续时间。默认值为 -1。-1值意味着一个直播流会一直播放直到它不再可用或者一个录制流一直播放直到结束。如果你传递 0 值,它将只播放单一一帧,因为播放时间已经在录制流的开始的Start字段指定了。假定定义在 Start字段中的值大于或者等于 0。如果你传递一个正数,将播放 Duration字段定义的一段直播流。之后,变为可播放状态,或者播放Duration 字段定义的一段录制流。(如果流在 Duration字段定义的时间段内结束,那么流结束时回放结束)。如果你在 Duration字段中传递一个 -1以外的负数的话,它将把你给的值当做 -1处理。 |
Reset |
布尔 |
一个可选的布尔值或者数字定义了是否对以前的播放列表进行 flush。 |
18、set Buffer Length:属于用户控制消息.(C—>S),具体参照上文
19、Stream begin 1:属于用户控制消息.(C—>S),具体参照上文
20、onStatus(‘NetStream.Play.Reset’):属于命令消息类型(S–>C)
名称:
作用:服务器端使用"onStatus" 命令向客户端发送 NetStream 状态。
包结构:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,Type id可能为0x14,代表包将采用AMF0方式进行编码。也可以为0x11,此时表示包将采用AMF3方式进行编码。
RTMP_body:
字段名 |
类型 |
描述 |
Command Name |
字符串 |
命令名 "onStatus"。 |
Transaction ID |
数字 |
事务 ID 设置为 0。 |
Command Object |
Null |
onStatus 消息没有命令对象。 |
Info Object |
对象 |
一个 AMF 对象至少要有以下三个属性。"level" (字符串):这一消息的等级,"warning"、"status"、"error" 中的某个值;"code" (字符串):消息码,例如 "NetStream.Play.Start";"description" (字符串):关于这个消息人类可读描述。 |
21、onStatus(‘NetStream.Play.Start’):属于命令消息类型(S–>C),具体请参照上文
22、RtmpSampleAccess():属于数据消息类型
名称:
作用:客户端或者服务器端通过发送这些消息以发送元数据或者任何用户数据到对端。元数据包括数据 (音频,视频等等) 的详细信息,比如创建时间,时长,主题等等。
包结构:
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+bodysize(3byte)+Type id(1byte)+stream id(4byte)
其中,Type id可能为0x12,代表包将采用AMF0方式进行编码。也可以为0x0E,此时表示包将采用AMF3方式进行编码。stream id为1.
RTMP_body:
23、onMetaData():属于数据消息类型,同上文的RtmpSampleAccess
24、video data:type id=9=videodata
25、audio data:type id=8=audiodata
……
26、CloseStream():属于命令消息类型
名称:
作用:客户端发送 closeStream 命令到服务器端来请求关闭连接通道
包结构:(结构跟publish中的connect一致)
RTMP_header:
fmt(2bit)+csid(6bit-22bit)+timestamp(3byte)+body size(3byte)+Typeid(1byte)+stream id(4byte)
其中:Type id可能为0x14,代表包将采用AMF0方式进行编码。也可以为0x11,此时表示包将采用AMF3方式进行编码。
RTMP_body: