RTMP(Real Time Messaging Protocol) 是由 Adobe 公司基于 Flash Player 播放器对应的音视频 flv 封装格式提出的一种,基于TCP 的数据传输协议。本身具有稳定、兼容性强、高穿透的特点。常被应用于流媒体直播、点播等场景。常用于推推流方(主播)的稳定传输需求。
0. 目录
2.3.1.1 设置块大小 (Set Chunk Size, Message Type ID = 1)
2.3.1.2 中断消息 (Abort Message, Message Type ID = 2)
2.3.1.3 应答信息 (Acknowledgement, Message Type ID = 3)
2.3.1.4 应答窗口大小 (Window Acknowledgement Size, Message Type ID = 5)
2.3.1.5 设置流带宽 (Set Peer Bandwidth, Message Type ID = 6)
2.3.2.1 网络连接命令 (NetConnection Commands)
1. 环境搭建
1.1 nginx-rtmp
- 下载nginx
nginx_1.7.11.3_Gryphon.zip 下载链接 Index of /download/
- 下载nginx的rtmp拓展包
下载链接 https://gitcode.net/mirrors/arut/nginx-rtmp-module?utm_source=csdn_github_accelerator
- 解压上述两个文件压缩包,将拓展包文件放入nginx目录下
- 按照下面内容修改nginx配置文件:nginx_1.7.11.3_Gryphon/conf/nginx.conf
- 在nginx目录下打开命令行界面:nginx.exe -c conf/nginx.conf
- 浏览器地址输入 localhost/stat ,查看服务器状态信息。
worker_processes 1;
events {
worker_connections 1024;
}
#RTMP服务
rtmp {
server {
listen 1935; #监听端口
chunk_size 4096; #数据传输块大小
application live{ #创建名为"live"的应用
live on;
}
}
}
#HTTP服务,可以通过浏览器访问http://localhost/stat 或者 http://localhost:80/stat 查看服务器状态
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name localhost;
location /stat {
rtmp_stat all;
rtmp_stat_stylesheet stat.xsl;
}
location /stat.xsl {
root ./nginx-rtmp-module-master/; #rtmp拓展包目录
}
location / {
root html;
index index.html index.htm;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}
1.2 推流
OBS :
官网下载地址:下载 | OBS
其他下载地址:http://www.pc6.com/down.asp?id=410276
推流使用方法:
- 找一个mp4格式的视频,拖到程序中
- 设置推流地址,串流秘钥
- 点击直播
VLC :
使用方法:
- 打开软件:点击媒体 -> 点击打开网络串流 -> 网络
- 输入网络url: rtmp://127.0.0.1:1935/live/stream
2. 协议解析
RTMP协议是要靠底层可靠的传输层协议(通常是TCP)来保证信息传输的可靠性的,默认使用端口1935。在基于传输层协议的链接建立完成后,RTMP协议也要客户端和服务器通过“握手”来建立基于传输层链接之上的RTMP Connection链接。RTMP Connection成功后会传输一些控制信息,如CreateStream命令会创建一个Stream链接,用于传输具体的音视频数据和控制这些信息传输的命令信息。
rtmp官方文档:https://rtmp.veriskope.com/pdf/rtmp_specification_1.0.pdf
2.1 握手 (Handshake)
一个 RTMP 连接以握手开始。先进行TCP握手后再进行RTMP握手。RTMP 握手由三个固定长度的块组成,有简单握手和复杂握手两种方式,两种握手方式信息流转的过程是相同的,只是消息中携带的信息不同。
无论推流(直播)还是拉流(观看),都是客户端向服务端发起握手请求。第一条握手消息是客户端发送的。
握手流程:
客户端向服务端按序发送C0,C1,C2(按序)3个chunk,服务端向客户端按序发送S0,S1,S2(按序)3个chunk,然后才能进行有效信息的传输。RTMP协议并没有规定这6个Message的具体传输顺序,
但需要保证以下几点:(简单握手和复杂握手均是如此)
(1)客户端要等收到S1之后才能发送C2
(2)客户端要等收到S2之后才能发送其他信息(控制信息和真实音视频等数据)
(3)服务端要等到收到C0之后发送S1
(4)服务端必须等到收到C1之后才能发送S2
(5)服务端必须等到收到C2之后才能发送其他信息(控制信息和真实音视频等数据)
2.1.1 简单握手
下面结合真实的抓包数据,分析一下简单握手的协议格式。
简单握手中S2是C1的复制,C2是S1的复制。
Client -> Server C0+C1
C0 和 S0都是一个8位字节,表示一个整数。
在 C0中,这一字段指示出客户端要求的 RTMP 版本号。
在 S0 中,这一字段指示出服务端选择的 RTMP 版本号。版本号基本都是3。
0、1、2 这三个值是由早期其他产品使用的,是废弃值。4 - 31 被保留为RTMP 协议的未来实现版本使用。
32 - 255 不允许使用 (以区分开 RTMP 和其他常以一个可打印字符开始的文本协议)。
C1 和 S1 数据包的长度都是 1536 字节。
0~4字节:这个字段包含一个 timestamp,用于本终端发送的所有后续块的时间起点。这个值可以是 0。
4~8字节:这个字段必须都是 0。如果不是0,代表要使用复杂握手。
其余字节:这个字段可以包含任意值。
Server -> Client S0+S1+S2
S0 表示服务端的版本号,格式和 C0 一致
S1 和 C1 的格式一致
S2 是 C1的复制
Client -> Server C2
C2 是 S1 的复制
2.1.2 复杂握手
暂不关心。
2.2 消息格式
2.2.1 Message介绍
消息(Message)是RTMP协议中基本的数据单元。由Message Header和Message Payload(可以理解成message body)组成。
Field | Type | Comment | |
Message Header | Length | 4 bytes | Message Payload(消息负载)的长度,不包含Message Header |
Timestamp | 3 bytes | 时间戳 | |
Message Type Id | 1 bytes | 消息类型,主要包括协议控制消息、音视频消息、命令消息等 | |
Message Stream Id | 3 bytes | 消息流ID可以是任意值。不同的message可以有相同的值 | |
Message Payload | n bytes | 是消息中包含的实际数据,消息类型不同payload大小也不同。例如,它可以是一些音频样本或压缩视频数据或Metadata等 |
2.2.2 Chunk介绍
RTMP以Message为基本单位,通过把Message拆分成Chunk来进行网络发送。chunk data默认是128字节。chunk是RTMP最小的传输单元。目的是:防止一个大的数据包传输时间过长,阻塞其它数据包的传输。chunk合成message:接收端将接收到chunk的chunk data的大小加和,如果等于message payload(通过chunk->message header->message length获取)的则认为是同一个message。
Chunk在传输时:同一个Message产生的多个Chunk只会串行发送。先发送的Chunk一定先到达。不同Message产生的Chunk可以并行发送。并行发送的Chunk复用了一条TCP链接。
Field | Type | Comment | |
Chunk Header | Basic Header | 1-3 bytes | 包含fmt(chunk type)和chunk stream id(csid),其中fmt决定了chunk的类型及message header的长度,占2 bit,而Basic header的长度取决于csid的数值大小,最少占1 byte。 |
Message Header | 0,3,7 or 11 bytes | 要发送的实际信息(可能是完整的,也可能是一部分)的描述信息。 长度取决于Basic Header中的chunk type,有Type 0,1,2,3类型的header | |
Extended Timestamp | 0 or 4 bytes | 扩展时间戳(0 bytes时表示此字段不存在) | |
Chunk Data | — | n bytes | 是消息中包含的实际数据,消息类型不同data大小也不同 |
2.2.2.1 Basic Header
Field | Type | Comment | |
fmt | 2 bits | 表示chunk type,取值[0, 3],即chunk共有4种类型 | |
Basic Header | csid (chunk stream id) | 6,14 or 22 bits | csid范围是365599,02为协议保留用作特殊信息;通常控制流csid为2,命令流为3,开发中发现音视频流csid可自定义,如音频流4,视频流6。 |
2.2.2.2 Message Header
Messages Header 的格式和长度取决于Basic Header 的 chunk type,即 fmt ,fmt取值 [0~3], 所以共有4种不同的chunk格式,目的是减少重复数据发送,提高 chunk data 的占比。同时也有4种不同的 Message Header。
chunk type = 0 (fmt = 0) :
Field | Type | Comment |
timestamp | 3 bytes | 时间戳,如果值大于等于16777215(0xFFFFFF),该字段必须等于16777215,然后转存到4字节的Extended Timstamp字段中。接收端判断是0xFFFFFF后会Extended Timstamp解析时间戳 |
message length | 3 bytes | 指的是message拆分之前message payload的长度(不包含header),而且如果被拆分成chunk,此字段填充拆分前message的body长度,而不是chunk的长度 |
message type id | 1 byte | 消息类型,如8代表audio数据,9代表video,其它值可参考后文的消息类型 |
message stream id | 4 bytes | 表示该Chunk所在的流的ID |
chunk type = 1 (fmt = 1) :
Field | Type | Comment |
timestamp | 3 bytes | 时间戳,如果值大于等于16777215(0xFFFFFF),该字段必须等于16777215,然后转存到4字节的Extended Timstamp字段中。接收端判断是0xFFFFFF后会Extended Timstamp解析时间戳 |
message length | 3 bytes | 指的是message拆分之前message payload的长度(不包含header),而且如果被拆分成chunk,此字段填充拆分前message的body长度,而不是chunk的长度 |
message type id | 1 byte | 消息类型,如8代表audio数据,9代表video,其它值可参考后文的消息类型 |
chunk type = 2 (fmt = 2) :
Field | Type | Comment |
timestamp | 3 bytes | 和上一个chunk的时间差。如果值大于等于16777215(0xFFFFFF),该字段必须等于16777215,然后转存到4字节的Extended Timstamp字段中。接收端判断是0xFFFFFF后会去Extended Timstamp解析时间戳 |
chunk type = 3 (fmt = 3) : Message Header共0字节,即此时chunk没有Message header。
2.3 消息类型
RTMP 协议中有多种消息, 用于数据传输和命令控制等操作, 所有的消息都是封装成 message,然后通过chunk来传输的。
2.3.1 协议控制消息
在RTMP的chunk流会用一些特殊的值来代表协议的控制消息,属于RTMP chunk流协议层的消息,它们的Message Stream ID必须为0(代表控制流信息),CSID必须为2,Message Type ID可以为1,2,3,5,6,具体代表的消息会在下面依次说明。控制消息的接受端会忽略掉chunk中的时间戳,收到后立即生效。
2.3.1.1 设置块大小 (Set Chunk Size, Message Type ID = 1)
RTMP消息需要以chunk size为单位封装成chunk包发送,因此接收端需要根据chunk size才能正确解包,所以双端都要记录对端的封包单位chunk size,默认128 bytes。
通信过程中可发送此消息通知对端更新其记录的本端的chunk size。比如client想发送131 bytes的音频数据(此时chunk size为128 bytes,不更新chunk size的话需要拆成两个chunk),此时client可通知对端,这边client的chunk size更新为131 bytes,之后发送一个data为131 bytes的chunk即可,server端收到Set Chunk Size之后更新chunk size即可正确解析之后到来的chunk。
双端的chunk size各自独立维护,可以不同。例如client可以发送131 bytes的chunk,server也按照131 bytes解析,server发送128 bytes的chunk,client也按照128 bytes解析。
RTMP Body:
占4个字节
第1个bit : 恒为0
chunk size (31bit) : 取值区间为[1, 0xFFFFFF]
2.3.1.2 中断消息 (Abort Message, Message Type ID = 2)
发送数据过程中,发送端可发送Abort消息通知接收端丢弃当前未接收完的Message及忽略之后的消息。先前已收到的chunk将被全部被抛弃,接收端根据Abort消息中的chunk stream id(csid)可丢弃对应chunk流中之后的所有数据。比如在发送端需要关闭时,发送此消息通知对端之后的数据可以不用处理了。
RTMP Body :
Field | Type | Comment | |
Message Payload | chunk stream id | 32 bits | 此字段保存要丢弃其当前消息的块流ID |
2.3.1.3 应答信息 (Acknowledgement, Message Type ID = 3)
当收到对端消息字节数等于接收窗口大小时,接收端要回复一个应答消息(相当于ack)告知对端可以继续发送数据,发送方在收到应答消息之前不会再继续发送消息。
RTMP Body :
Field | Type | Comment | |
Message Payload | chunk stream id | 32 bits | 截止目前接收到的数据总和,以字节为单位 |
2.3.1.4 应答窗口大小 (Window Acknowledgement Size, Message Type ID = 5)
规定接收端接收多少数据后需要发送一个应答消息。发送端可以发送此消息通知对端更新窗口大小,一般在音视频数据之前发送。并且双端的window size共同维护,保持相同。
RTMP Body :
Field | Type | Comment | |
Message Payload | acknowledgement window size | 32 bits | 接收端接收多少数据后需要发送一个应答消息 |
2.3.1.5 设置流带宽 (Set Peer Bandwidth, Message Type ID = 6)
客户端或服务器发送此消息以限制其对等端的输出带宽。
Field | Type | Comment | |
Message Payload | acknowledgement window size | 32 bits | 设置对端出口带宽 |
limit type | 8 bits | limit type = 0:硬限制,立即更新出口带宽大小 limit type = 1:软限制,可以更新出口带宽大小,也可以保留原值,但是原值一定要小于期望更新的大小 limit type = 2:动态限制,如果上一次为硬限制,此消息被视为硬限制,否则忽略此消息 |
2.3.2 命令消息
命令消息(Command Messages)是用于 C-S 进行直接交互应答的一类消息。一般情况下,命令消息的发送对端,是需要对端进行应答信号反馈的。需要AMF编码,AMF0编码时Message Type ID = 20,使用AMF3编码时Message Type ID = 17,CSID通常为3。
2.3.2.1 网络连接命令 (NetConnection Commands)
- 表示双端的上层连接,服务器和客户端之间进行网络连接的一种高级表示形式。
- 网络连接允许使用:连接(connect)、调用(call)、创建流(createStream),每一种消息都有应答消息。
- 应答消息中的“Transaction ID”表明对哪个请求的应答。
2.1. 连接 (connet)
客户端向服务器发送connect命令,以请求连接到服务器应用程序实例。
请求消息 :
Field | Type | Comment | |
Message Payload | Command Name(命令名字) | String | ”connect” |
Transaction ID(事务ID) | Number | 恒为1 | |
Command Object(命令包含的参数对象) | Object | 键值对集合表示的命令参数 | |
Optional User Arguments(额外的用户参数) | Object | 用户自定义的额外信息 |
Optional User Arguments(额外的用户参数) 包含如下内容:
Property | Type | Description | Example Value |
app | String | 客户端要连接的服务器应用程序名 | testapp |
flashver | String | Flash Player的版本号。它与ApplicationScript中getversion()方法返回的字符串是一样的。 | FMSc/1.0 |
swfUrl | String | 发起连接的SWF源文件的URL | file://C:/FlvPlayer.swf |
tcUrl | String | 服务器URL。格式:protocol://servername:port/appName /appInstance | rtmp://localhost:1935/testapp/instance1 |
fpad | Boolean | 如果使用代理,值为true | true或false |
audioCodecs | Number | 指示客户端支持的音频编码 | SUPPORT_SND_MP3 |
videoCodecs | Number | 指示客户端支持的视频编码 | SUPPORT_VID_SORENSON |
videoFunction | Number | 指示客户端支持的专用的视频方法 | SUPPORT_VID_CLIENT_SEEK |
pageUrl | String | SWF文件从哪里加载的网页URL | http://somehost/sqmple.html |
objectEncoding | Number | AMF编码方法 | AMF3 |
(2)应答消息
Field | Type | Comment | |
Message Payload | Command Name (命令名字) | String | ”_result”或"_err",表示响应是否为结果或者错误 |
Transaction ID(事务ID) | Number | 恒为1 | |
Properties (命令包含的参数对象) | Object | 键值对集合表示的命令参数(具体内容可参考官方文档) | |
Information(应答信息) | Object | 描述应答的名称-值对,如“code”,“level”,“description”之类的内容 |
2.2. 调用(call)
2.3. 创建流 (createStream)
客户端将此命令发送到服务器,以创建消息通信的逻辑通道。音频、视频和元数据的发布通过使用createStream命令创建的流通道执行。NetConnection是默认的通信信道,其流ID为0。协议和一些命令消息(包括createStream)使用默认的通信通道。
请求消息
Field | Type | Comment | |
Message Payload | Command Name(程序名称) | String | “createstream” |
Transaction ID(事务ID) | Number | 命令会话ID | |
Command Object(命令包含的参数对象) | Object | 如果存在任何命令信息,则设置此对象,否则设置为空类型。 |
应答消息
Field | Type | Comment | |
Message Payload | Command Name(命令名字) | String | ”_result”或"_err",表示响应是否为结果或者错误 |
Transaction ID(事务ID) | Number | 标识它属于哪个应答的 | |
Command Object | Object | 如果存在任何命令信息,则设置此对象,否则设置为空类型。 | |
Stream ID(应答信息) | Object | stream id或错误信息 |
2.3.2.2 网络流命令 (NetStream)
网络流命令是在数据信道建立完毕后(先有NetConnection,才有NetStream命令),用来对音视频数据流进行直接控制的命令类型。这些命令作用于当前信道对应数据的操控行为,都是客户端向服务端发送的命令,一个NetConnection对象可以有多个NetStream,进而支持多种数据。
命令 | 作用 |
play | 播放 |
play2 | 播放2 |
deleteStream | 删除流 |
closeStream | 关闭流 |
receiveAudio | 接收音频 |
receiveVideo | 接收视频 |
publish | 推流 |
seek | 定位 |
pause | 暂停 |
网络流命令的请求命令所对应的应答命令,被统一命名为 “onStatus” 以描述数据所处的状态发生变更,因此具有统一的格式。
Field | Type | Comment | |
Message Payload | Command Name(命令名字) | String | onStatus |
Transaction ID(事务ID) | Number | 恒为0 | |
Command Object(命令包含的参数对象) | Object | NULL | |
Info Object(应答信息) | Object | stream id或错误信息 |
2.3.2.2.1 播放(play)
客户端发送此命令让服务器播放流。也可以多次使用此命令创建一个播放列表。如果你想创建一个在不同直播或录制流之间切换的动态播放列表,多次调用play并将reset字段设置为false。相反,如果你想立即播放指定的流,将reset字段设置为true。
(1) 请求消息
Field | Type | Comment |
Command Name(命令名字) | String | “play” |
Transaction ID(事务ID) | Number | Transaction ID设置为0 |
Command Object(命令包含的参数对象) | Null | 不存在命令信息,设置为null类型 |
Stream Name(流名称) | String | 待播放流的名称和格式。 如果要播放视频(FLV)文件,指定不带文件扩展名(比如,“sample”)的流名称。 如果要播放H.264/AAC文件,你必须在流名称前加上mp4:的前缀,并且指定文件名后缀。比如,要播放文件sample.m4v,指定“mp4:sample.m4v” 如果要回放MP3或者ID3标签,你必须要在流名称前加上mp3:的前缀。比如“mp3:sample” |
Start(开始时间) | String | 一个可选的参数,指定开始播放的起开时间,单位是秒,默认值是-2。 Start = -2,代表选取对应该流名称的直播流,即当前正在推送的流开始播放,如果对应该名称的直播流不存在,就选取该名称的流的录播版本,如果这也没有,当前播流端要等待直到对端开始该名称的流的直播。 Start = -1,那么只会选取直播流进行播放,即使有录播流也不会播放;如果传值或者正数,就代表从该流的该时间点开始播放,如果流不存在的话就会自动播放播放列表中的下一个流 Duration >= 0,一个在流名称字段指定的录播流会被播放,起始时间是Start字段指定的时间。如果找不到该记录流,播放列表的下一个条目会被播放。 |
Duration(时长) | String | 一个可选的参数,指定回放时长,单位是秒,默认值是-1。 Duration = -1,直播流被播放直到它不可用,或者录播流被播放直到结束。(如果你传递一个不同于-1的负数,它会把该值解析为-1) Duration = 0,它会播放录播流中Start字段指定开始时间的一帧。 Duration > 0,如果你指定一个正数,它会播放Duration字段指定该段时间的直播流。之后,它能够播放Duration字段指定该段时间的录播流。(如果流在Duration字段指定的时间前结束,回放随着流的结束而结束) |
Reset(重置) | Boolean | 一个可选的布尔值,它指定是否清除之前的所有播放列表 |
2.3.2.2.2 播放2(play2)
play命令不同的是,play2命令可以将当前正在播放的流切换到同样数据但不同码率的流上,服务端会维护多种比特率的文件来供客户端使用play2命令来切换。
(1) 请求消息
Field | Type | Comment |
Command Name(命令名字) | String | “play2” |
Transaction ID(事务ID) | Number | Transaction ID设置为0 |
Command Object(命令包含的参数对象) | Null | 不存在命令信息,设置为null类型 |
Parameters(参数) | Object | AMF编码的Flash对象,包括了一些用于 描述flash.net.NetstreamPlayOptions ActionScript obejct的参数 |
2.3.2.2.3 删除流(deleteStream)
NetStream在NetStream对象被销毁时发送deleteStream命令,用于客户端告知服务端本地的某个流对象已被删除,不需要再传输此路流。
(1) 请求消息
Field | Type | Comment |
Command Name(命令名字) | String | “deleteStream” |
Transaction ID(事务ID) | Number | Transaction ID设置为0 |
Command Object(命令包含的参数对象) | Null | 不存在命令信息,设置为null类型 |
Stream ID(流ID) | Number | 本地已删除,不再需要服务器传输的流的ID |
(2) 应答消息
无应答消息
2.3.2.2.4 接收音频(receiveAudio)
客户端通知服务端是否要发送音频。
(1) 请求消息
Field | Type | Comment |
Command Name(命令名字) | String | “deleteStream” |
Transaction ID(事务ID) | Number | Transaction ID设置为0 |
Command Object(命令包含的参数对象) | Null | 不存在命令信息,设置为null类型 |
Bool Flag(标识) | Boolean | Bool Flag = true,表示发送音频,服务端就会准备接受音频数据,会向客户端回复NetStream.Seek.Notify和NetStream.Play.Start的Onstatus命令告知客户端当前流的状态 Bool Flag = false,服务端不做响应 |
(2) 应答消息
如果,Bool Flag = false 这个消息只用来通知服务器,没有统一应答返回。
如果,Bool Flag = true则应答消息’code’为 NetStream.Seek.Notify 或 NetStream.Play.Start
2.3.2.2.5 接收视频(receiveVideo)
客户端通知服务端是否要发送视频。
(1) 请求消息
Field | Type | Comment |
Command Name(命令名字) | String | “deleteStream” |
Transaction ID(事务ID) | Number | Transaction ID设置为0 |
Command Object(命令包含的参数对象) | Null | 不存在命令信息,设置为null类型 |
Bool Flag(标识) | Boolean | Bool Flag = true,表示发送音频,服务端就会准备接受音频数据,会向客户端回复NetStream.Seek.Notify和NetStream.Play.Start的Onstatus命令告知客户端当前流的状态 Bool Flag = false,服务端不做响应 |
(2) 应答消息
如果,Bool Flag = false 这个消息只用来通知服务器,没有统一应答返回。
如果,Bool Flag = true则应答消息’code’为 NetStream.Seek.Notify 或 NetStream.Play.Start
2.3.2.2.6 发布(publish)
客户端发送此消息,将命名流发布到服务器。其他客户端可以使用此流名来播放流,接收发布的音频,视频,以及其它数据消息。
(1) 请求消息
Field | Type | Comment |
Command Name(命令名字) | String | “publish“ |
Transaction ID(事务ID) | Number | Transaction ID设置为0 |
Command Object(命令包含的参数对象) | Null | 不存在命令信息,设置为null类型 |
PublishingName(发布的流名称) | String | 发布流的名称 |
PublishingType(发布类型) | String | 有三种发布的类型:live record 或 append PublishingType = record : 该流已被发布并且数据被记录到一个新的文件。该文件存储在服务器的一个包含服务器应用程序目录的子目录。如果文件已经存在,则它被覆盖 PublishingType = append:流已经被发布,并且该数据被追加到一个文件。如果找不到文件,则创建它 PublishingType = live:直播数据被发布,而没有记录到文件 |
(2) 应答消息
publish 的返回状态 NetStream.Publish.Start,这个消息不止由 onStatus 统一应答携带,也会由 onFCPublish 返回。
4.2.2.7 定位(seek)
定位到视频或音频的某个位置,以毫秒为单位。
(1) 请求消息
Field | Type | Comment |
Command Name(命令名字) | String | “seek“ |
Transaction ID(事务ID) | Number | Transaction ID设置为0 |
Command Object(命令包含的参数对象) | Null | 不存在命令信息,设置为null类型 |
milliSeconds(毫秒) | Number | 定位到该文件的xx毫秒处 |
(2) 应答消息(应答格式参考 "4.2.2 网络流命令(NetStream)--> 2"):
当定位成功,服务器发送NetStream.Seek.Notify的状态消息。失败的时候,它返回一个_error的消息
4.2.2.8 暂停(pause)
客户端发送pause命令以告诉服务器暂停或者开始播放。
(1) 请求信息:
Field | Type | Comment |
Command Name(命令名字) | String | “pause“ |
Transaction ID(事务ID) | Number | Transaction ID设置为0 |
Command Object(命令包含的参数对象) | Null | 不存在命令信息,设置为null类型 |
Pause/Unpause Flag(暂停/恢复播放) | Boolean | true,暂停 false,恢复播放 |
milliSeconds(毫秒) | Number | 定位到该文件的xx毫秒处 |
(2) 应答消息
pause 成功后,会有 ’code’ 为 NetStream.Pause.Notify 消息返回。
unpause 成功后,会有 ’code’ 为 NetStream.Unpause.Notify 消息返回。
2.3.3 数据信息
传递一些元数据(MetaData,比如视频名,分辨率等等)或者用户自定义的一些消息。当信息使用AMF0编码时,Message Type ID=18,AMF3编码时Message Type ID=15。
2.3.4 共享信息
共享对象是在多个客户端、实例等之间同步的Flash对象,Flash对象是由键值对组成的集合。每条消息可以包含多个事件。当信息使用AMF0编码时,Message Type ID=19,AMF3编码时Message Type ID=16。
消息结构:
Event Type | Comment |
Use(=1) | 客户端发送此事件通知服务器一个共享对象的创建 |
Release(=2) | 当在客户端删除共享对象时,客户端将此事件发送到服务器上 |
Request Change(=3) | 客户端发送此事件以请求更改与共享对象命名参数关联的值 |
Change(=4) | 服务器发送此事件通知除了正在发起请求之外的所有客户端命名参数值的变化 |
Success(=5) | 如果请求被受理,服务器发送此事件给正在请求的客户端,以响应RequestChange事件 |
SendMessage(=6) | 客户端将此事件发送到服务器以广播一个消息。在接收此事件时,服务器向所有客户端广播消息,包括该发送者 |
Status(=7) | 服务器发送此事件以通知客户端相关的错误状况 |
Clear(=8) | 服务器将此事件发送给客户端,以清除共享对象。服务器还发送此事件以响应客户端在连接时发送Use event |
Remove(=9) | 服务器发送此事件让客户端删除一个slot |
Request Remove(=10) | 客户端发送此事件让服务器删除一个slot |
Use Success(=11) | 成功连接时,服务器将此事件发送给客户端8. 音频信息(Audio Message) |
2.3.5 音/视频信息
每一个message就是一帧数据。对于flv的tag而言,就是对应rtmp每个message,一个tag就是一个message,是一一对应的关系;相当于每一个tag都封装成一个message。
音频:
RTMP 块流使用Message Type ID=8 作为音频数据,flv的tag header->tag type也用8来表示音频。通常音频流的csid是4(也可以自定义),音频流的每一个chunk的csid都是相同的。
视频:
RTMP 块流使用Message Type ID=9 作为视频数据,flv的tag header->tag type也用9来表示音频。通常视频流的csid是6(也可以自定义),视频流的每一个chunk的csid都是相同的。
2.3.6 聚合消息
- 聚合消息是包含一系列RTMP子消息的单个消息,Message Type ID=22。
- 聚合消息的消息流ID覆盖聚合内部的子消息的消息流程ID;聚合消息和第一个子消息的时间戳之间的差异是用于将子消息的时间戳记重新规范化为流时间尺度的偏移量。偏移量被添加到每个子消息的时间戳,以达到标准化的流时间。第一个子消息的时间戳应与聚合消息的时间戳记相同,因此偏移量应为零;返回指针包含前一条消息的大小,包括其标头,它被包括以匹配FLV文件的格式,并用于反向搜索,类似于flv的previous tag header。
- 使用聚合消息有几个性能优势:
(1)区块流最多可以在一个区块内发送一条完整的消息。因此,增加块大小并使用聚合消息可以减少发送的块数。
(2)子消息可以连续存储在内存中。当进行系统调用以在网络上发送数据时,效率更高。
消息格式:
2.3.7 用户控制消息
RTMP 流中的用户控制消息在接收时立即生效,消息中的时间戳被忽略。该信息在 chunk 流中发送时,它们的Message Stream ID必须为0(代表控制流信息),CSID必须为2,Message Type ID必须为4。和前面提到的协议控制信息(Protocol Control Message)不同,这是在RTMP协议层的,而不是在RTMP chunk流协议层的,这个很容易弄混。
消息格式:
前 2 个字节数据用来标识事件类型,事件类型后面是事件数据。事件数据字段的大小是不固定的。因为消息是通过RTMP chunk发送的,所以最大块大小应足够大,以允许这些消息在单个块中。
Event Type | Comment |
StreamBegin(=0) | 服务器发送此事件通知客户端流成功创建,并可用于通信。默认情况下,在从客户端成功收到应用程序连接命令后,此事件以ID 0来发送。Event Data大小是4字节,表示开始运行的流的stream ID |
StreamEOF(=1) | 服务器发送此事件通知客户端该流请求的数据回放结束。若不发出额外的命令,就没有更多的数据被发送了。客户端丢弃该流收到的消息。Event Data大小是4字节,代表播放已结束流的stream ID |
StreamDry(=2) | 服务器发送此事件通知客户端,在流上没有更多的数据。如果服务器在一段时间内没有检测到任何的消息,它可以通知订阅的客户端流是结束的。Event Data大小是4字节,代表已结束流的stream ID |
SetBufferLength(=3) | 客户端发送此事件通知服务器用于缓冲从流过来的任何数据的缓冲区大小(以毫秒为单位)。此事件在服务器开始处理流之前被发送。Event Data大小是8字节,前4字节是stream ID,后4字节是每毫秒缓冲区的长度 |
StreamIsRecorded (=4) | 服务器发送此事件来通知客户端,需要记录(录像)。Event Data大小是4字节,代表需要记录的流的stream ID |
PingRequest(=6) | 服务器发送此事件来测试客户端是否是可到达的 Event Data大小是4字节,内容是时间戳,表示当服务器发出命令时,本地服务器的时间。客户收到PingRequest时响应PingResponse |
PingRequest(=6) | 服务器发送此事件来测试客户端是否是可到达的 Event Data大小是4字节,内容是时间戳,表示当服务器发出命令时,本地服务器的时间。客户收到PingRequest时响应PingResponse |
Event Type = StreamBegin
Event Type = SetBufferLength
2.4 数据传输过程
2.5 总结
- message分成chunk的目的:防止一个大的数据包传输时间过长,阻塞其它数据包的传输;
- chunk分成4种形式的目的:减少重复数据发送,提高chunk data的占比;
- 消息优先级:总结起来就是协议先行,数据次之。
- 协议控制消息(Protocol Control Messages):中断消息只能是数据发送端给接收端(如果是推流就是客户端发给服务端,如果是拉流则是服务端发给客户端),其它消息都可以双端发送;
- 命令消息(Command Message)
- 先有网络连接命令再有网络流命令,也就是说先建立连接,才能传输数据;
- 所有命令消息都需要应答,网络连接中不同的命令应答格式不同,网络流命令的应答命令格式相同;
- 除了调用(call)命令可以双端发送外,其余命令消息都是客户端向服务端发送,服务端向客户端发送应答消息;