一、RTMP 介绍
RTMP 是 Real Time Messaging Protocol( 实时消息传输协议) 的首字母缩写。该协议基于 TCP,是一个协议族,包括 RTMP 基本协议及 RTMPT/RTMPS/RTMPE 等多种变种。
RTMP 是一种设计用来进行实时数据通信的网络协议,主要用来在 Flash/AIR 平台和支持 RTMP 协议的流媒体/交互服务器之间进行音视频和数据通信。支持该协议的软件包括 Adobe Media Server/Ultrant Media Server/red5 等。RTMP 与 HTTP 一样, 都属于 TCP/IP 四层模型的应用层。
RTMP(Real Time Messaging Protocol)实时消息传送协议是 Adobe Systems 公司为 Flash 播放器和服务器之间音频、视频和数据传输开发的开放协议。
RTMP 协议传输时会对数据格式化,而实际传输的时候为了更好地实现多路复用、分包和信息的公平性,发送端会把 Message 划分为带有 Message ID 的 Chunk,每个 Chunk 可能是一个单独的 Message,也可能是 Message 的一部分,在接收端会根据 Chunk 中包含的 data 的长度,message id 和 message 的长度把 chunk 还原成完整的 Message,从而实现信息的收发。 (Message, Chunk)
2、变种
它有多种变种:
RTMP 工作在 TCP 之上,默认使用端口 1935;
RTMPE 在 RTMP 的基础上增加了加密功能;encrypt
RTMPT 封装在 HTTP 请求之上,可穿透防火墙;http–rtmp
RTMPS 类似 RTMPT,增加了 TLS/SSL 的安全功能;
二、wireshark 抓 RTMP 报文
RTMP 服务器:Nginx+rtmp(windows)
推流:ffmpeg
播放器:VLC(虚拟机 linux)
抓包:wireshark
1、搭建 RTMP 服务器
RTMP 服务器:Nginx+rtmp(windows)的环境搭建如有需要可自取:
链接:https://pan.baidu.com/s/1AcIVERWUPbJL1zu8yCcAzw
提取码:mtdf
2、运行 RTMP 服务器
双击 nginx8080.exe
在任务管理器可以看到目前 nginx 已开始工作
3、打开 wireshark
虚拟机用的 VMware Network Adapter VMnet8 虚拟网卡(NAT 虚拟网络)与主机(Windows)进行通信,因此这里我们直接就抓 VMware Network Adapter VMnet8 网卡即可。
4、ffmpeg 推流
推流命令:
ffmpeg -re -i SampleVideo_1280x720_20mb.mp4 -vcodec libx264 -acodec aac -r 30 -g 150 -f flv -y rtmp://192.168.36.176:1935/live/test1
这个命令使用 FFmpeg 工具来将输入视频文件 SampleVideo_1280x720_20mb.mp4 转换为 FLV 格式并通过 RTMP 协议流式传输到指定的 URL 地址 rtmp://192.168.36.176:1935/live/test1;
-re:以实时模式(real-time)读取输入文件,模拟实时流传输的速度。
-i SampleVideo_1280x720_20mb.mp4:指定输入文件名为 SampleVideo_1280x720_20mb.mp4。
-vcodec libx264:选择 H.264 编码器作为视频编码器;
-acodec aac:选择 AAC 编码器作为音频编码器;
-r 30:设置输出视频的帧率为 30 帧每秒;
-g 150:设置关键帧间隔为 150 帧。关键帧是视频解码的起点,较短的关键帧间隔可以提高视频的快进/快退性能;
-f flv:指定输出格式为FLV(Flash Video);
-y:自动覆盖输出文件,如果存在同名文件则会被替换;
rtmp://192.168.36.176:1935/live/test1:指定输出的 URL 地址,以 RTMP 协议传输到 192.168.36.176 服务器的 1935 端口的 live 应用程序中的 test1 流
其中:
rtmp://ip:port/application/channelname
ip:本机 ip 地址
port:RTMP 工作在 TCP 之上,默认使用端口 1935
Application:参考 nginx.conf,这里是 live
channelname:自定义
推流过程:
5、VLC 拉流
①、打开虚拟机端 VLC 客户端,媒体 -> 打开网络串流,输入 rtmp://192.168.36.176:1935/live/test1
②、点击播放,可以看到拉流成功
③、查看 windows 端 wireshark,可以看到我们要的 RTMP 报文
其中 192.168.36.176 为主机 ip 地址,192.168.137.128 为虚拟机端 ip 地址,我们仅看 RTMP 报文即可。
我抓到的 RTMP 报文,这里存一下方便后面用到时直接拿来分析:RTMP报文
三、RTMP 协议详解
我们根据上面我们通过 wireshark 抓到的报文对 RTMP 协议进行学习。
1、前言
直播流:Video 和 Audio 编码器编码后是交织在一起
Timestamp:时间戳
Video(视频流) : ———————————–(每一个 - 代表一个视频包)
Audio(音频流): ++++++++++++++++++++(每一个 + 代表一个音频包)
Mp4 格式 : ###—-++——+++——,一个文件,只有一个头信息(在网络上传输如果文件头丢失那么中间的音视频流都无法解码)
Ts 格式:[#—-+] [#—-+] [#—-+],每一个包都有头信息(适合在网络上传输)
RTMP:Message:[$—-+] [$—-+] [$—-+];Chunk 块(消息太长,分成Chunk 块)。(RTMP 采用的传输格式是 flv)
Flv: [$—-+] [$—-+] [$—-+];
2、总体介绍
RTMP 协议是应用层协议,是要靠底层可靠的传输层协议(通常是 TCP)来保证信息传输的可靠性的。在基于传输层协议的链接建立完成后,RTMP 协议也要客户端和服务器通过 “RTMP 握手” 来建立基于传输层链接之上的 RTMP Connection 链接,在 Connection 链接上会传输一些控制信息,如 SetChunkSize,SetACKWindowSize。
其中 CreateStream 命令会创建一个 Stream 链接,用于传输具体的音视频数据和控制这些信息传输
的命令信息。
RTMP 协议传输时会对数据(直播流,推本地视频文件)做自己的格式化(Message/Chunk),这
种格式的消息我们称之为 RTMP Message,而实际传输的时候为了更好地实现多路复用、分包和信息的公平性,发送端会把 Message 划分为带有 Message ID 的 Chunk,每个 Chunk 可能是一个单独的
Message,也可能是 Message 的一部分,在接收端会根据 chunk 中包含的 data 的长度,message id
和 message 的长度把 chunk 还原成完整的 Message,从而实现信息的收发。
3、握手
要建立一个有效的 RTMP Connection 链接,首先要 “RTMP 握手”
客户端要向服务器发送 C0,C1,C2(按序)三个 chunk
服务器向客户端发送 S0,S1,S2(按序)三个 chunk,然后才能进行有效的信息传输
RTMP 协议本身并没有规定这 6 个 Message 的具体传输顺序,但 RTMP 协议的实现者需要保证这
几点:
客户端要等收到 S1 之后才能发送 C2
客户端要等收到 S2 之后才能发送其他信息(控制信息和真实音视频等数据)
服务端要等到收到 C0 之后发送 S1
服务端必须等到收到 C1 之后才能发送 S2
服务端必须等到收到 C2 之后才能发送其他信息(控制信息和真实音视频等数据)
理论上来讲只要满足以上条件,如何安排 6 个 Message 的顺序都是可以的,但实际实现中为了在保
证握手的身份验证功能的基础上尽量减少通信的次数,一般的发送顺序是这样的,这一点可以通过
wireshark 抓 ffmpeg 推流包进行验证。
4、RTMP Chunk Stream
Chunk Stream 是对传输 RTMP Chunk 的流的逻辑上的抽象,客户端和服务器之间有关 RTMP 的
信息都在这个流上通信。这个流上的操作也是我们关注 RTMP 协议的重点。
[控制信息,音视频流信息]
①、message(消息)
这里的 Message 是指满足该协议格式的、可以切分成 Chunk 发送的消息,消息包含的字段如下:
Timestamp(时间戳):消息的时间戳(但不一定是当前时间),4 个字节;
Length(长度):是指 Message Payload(消息负载)即音视频等信息的数据的长度,3 个字节;
TypeId(类型 Id):消息的类型 Id,1 个字节;
Message Stream ID(消息的流 ID):每个消息的唯一标识,划分成 Chunk 和还原 Chunk 为 Message 的时候都是根据这个 ID 来辨识是否是同一个消息的 Chunk 的,4 个字节,并且以小端格式(little-endian)存储。
②、Chunking(message 分块)
RTMP 在收发数据的时候并不是以 Message 为单位的,而是把 Message 拆分成 Chunk 发送,而
且必须在一个 Chunk 发送完成之后才能开始发送下一个 Chunk。每个 Chunk 中带有 MessageID 代表属于哪个 Message,接受端也会按照这个 id 来将 chunk 组装成 Message。
问:为什么 RTMP 要将 Message 拆分成不同的 Chunk 呢?
答:通过拆分,数据量较大的 Message 可以被拆分成较小的 “Message”,这样就可以避免优先级低的消息持续发送阻塞优先级高的数据,比如在视频的传输过程中,会包括视频帧,音频帧和 RTMP 控制信息,如果持续发送音频数据或者控制数据的话可能就会造成视频帧的阻塞,然后就会造成看视频时最烦人的卡顿现象。同时对于数据量较小的 Message,可以通过对 Chunk Header 的字段来压缩信息,从而减少信息的传输量
RTMP message
不同类型的 RTMP message
Command Message(命令消息,Message Type ID=17 或 20):表示在客户端和服务器间传递的在对端执行某些操作的命令消息(注:简单来说就是通知对方要开始干啥的命令),如 connect 表示连接对端,对端如果同意连接的话会记录发送端信息并返回连接成功消息,publish 表示开始向对方推流,接受端接到命令后准备好接受对端发送的流信息,后面会对比较常见的 Command Message 具体介绍。当信息使用 AMF0 编码时,Message Type ID=20,AMF3 编码时 Message Type ID=17。
Data Message(数据消息,Message Type ID=15 或 18):传递一些元数据(MetaData,比如视频名,分辨率等等)或者用户自定义的一些消息。当信息使用 AMF0 编码时,Message Type ID=18,AMF3 编码时 Message Type ID=15。
Shared Object Message(共享消息,Message Type ID=16 或 19):表示一个 Flash 类型的对象,由键值对的集合组成,用于多客户端,多实例时使用。当信息使用 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 Type ID=22):多个 RTMP 子消息的集合
User Control Message Events(用户控制消息,Message Type ID=4):告知对方执行该信息中包含的用户控制事件,比如 Stream Begin 事件告知对方流信息开始传输。和前面提到的协议控制信息(Protocol Control Message)不同,这是在 RTMP 协议层的,而不是在 RTMP chunk 流协议层的,这个很容易弄混。该信息在 chunk 流中发送时,Message Stream ID=0,Chunk Stream Id=2,Message TypeId=4。
①、Command Message(命令消息,Message Type ID=17 或 20)
发送端发送时会带有命令的名字,如 connect,TransactionID 表示此次命令的标识,Command Object 表示相关参数。接受端收到命令后,会返回以下三种消息中的一种:_result 消息表示接受该命令,对端可以继续往下执行流程,_error 消息代表拒绝该命令要执行的操作,method name 消息代表要在之前命令的发送端执行的函数名称。这三种回应的消息都要带有收到的命令消息中的 TransactionId 来表示本次的回应作用于哪个命令。
可以认为发送命令消息的对象有两种,一种是 NetConnection,表示双端的上层连接,一种是 NetStream,表示流信息的传输通道,控制流信息的状态,如 Play 播放流,Pause 暂停。
- NetConnection Commands(连接层的命令)
用来管理双端之间的连接状态,同时也提供了异步远程方法调用(RPC)在对端执行某方法,以下是常见的连接层的命令。
connect:用于客户端向服务器发送连接请求,消息的结构如下
消息的回应有两种,_result 表示接受连接,_error 表示连接失败。
Call:用于在对端执行某函数,即常说的 RPC:远程进程调用,消息的结构如下:
如果消息中的 TransactionID 不为 0 的话,对端需要对该命令做出响应,响应的消息结构如下:
Create Stream:创建传递具体信息的通道,从而可以在这个流中传递具体信息,传输信息单元为 Chunk
- NetStream Commands(流连接上的命令)
Netstream 建立在 NetConnection 之上,通过 NetConnection 的 createStream 命令创建,用于传输具体的音频、视频等信息。在传输层协议之上只能连接一个 NetConnection,但一个 NetConnection 可以建立多个 NetStream 来建立不同的流通道传输数据。以下会列出一些常用的 NetStream Commands,服务端收到命令后会通过 onStatus 的命令来响应客户端,表示当前 NetStream 的状态。
onStatus 命令的消息结构如下:
play(播放): 由客户端向服务器发起请求从服务器端接受数据(如果传输的信息是视频的话就是请求开始播流),可以多次调用,这样本地就会形成一组数据流的接收者。注意其中有一个 reset 字段,表示是覆盖之前的播流(设为 true)还是重新开始一路播放(设为 false)。
play2(播放): 和上面的 play 命令不同的是,play2 命令可以将当前正在播放的流切换到同样数据但不同比特率的流上,服务器端会维护多种比特率的文件来供客户端使用 play2 命令来切换。
deleteStream(删除流):用于客户端告知服务器端本地的某个流对象已被删除,不需要再传输此路流。
receiveAudio(接收音频):通知服务器端该客户端是否要发送音频,receiveAudio 命令结构如下:
receiveVideo(接收视频):通知服务器端该客户端是否要发送视频,receiveVideo 命令结构如下:
publish(推送数据):由客户端向服务器发起请求推流到服务器。publish 命令结构如下:
seek(定位流的位置): 定位到视频或音频的某个位置,以毫秒为单位。seek 命令的结构如下:
pause(暂停):客户端告知服务端停止或恢复播放。pause 命令的结构如下:
如果 Pause 为 true 即表示客户端请求暂停的话,服务端暂停对应的流会返回 NetStream.Pause.Notify 的 onStatus 命令来告知客户端当前流处于暂停的状态,当 Pause 为 false 时,服务端会返回 NetStream.Unpause.Notify 的命令来告知客户端当前流恢复。如果服务端对该命令响应失败,返回_error 信息。
四、RTMP 流媒体播放过程
1、简介
下面分析打开一个 RTMP 流媒体到视音频数据开始播放的全过程。
RTMP 协议规定, 播放一个流媒体有两个前提步骤:
第一步, 建立一个网络连接( NetConnection);
第二步, 建立一个网络流( NetStream)。
其中,网络连接代表服务器端应用程序和客户端之间基础的连通关系。网络流代表了发送多媒体数据的通道。服务器和客户端之间只能建立一个网络连接,但是基于该连接可以创建很多网络流。他们的关系如图所示:
播放一个 RTMP 协议的流媒体需要经过以下几个步骤:握手,建立连接,建立流,播放。RTMP 连接都是以握手作为开始的。建立连接阶段用于建立客户端与服务器之间的“ 网络连接” ;建立流阶段用于建立客户端与服务器之间的“ 网络流”;播放阶段用于传输视音频数据。
我们分析上面 wireshark 抓到的报文。
2、握手(HandShake)
rtmp 连接从握手开始。它包含三个固定大小的块。客户端发送的三个块命名为 C0,C1,C2;服务端发送的三个块命名为 S0,S1,S2。
握手序列如下:
握手开始于客户端发送 C0、 C1 块。 服务器收到 C0 或 C1 后发送 S0 和 S1。
当客户端收齐 S0 和 S1 后, 开始发送 C2。 当服务器收齐 C0 和 C1 后, 开始发送 S2。
当客户端和服务器分别收到 S2 和 C2 后, 握手完成。
握手示意图如下图所示:
结合上面理论对比我们抓到的报文:
①、客户端向服务器发送握手 C0+C1
②、服务器向客户端回应握手 S0+S1+S2
③、客户端向服务器发送握手 C2
抓包总结:
第一步:客户端发送 C0+C1
第二步:服务端发送 S0+S1+S2
第三步:客户端发送 C2
疑问: C2 和 S2, 到底哪个先发送? ? ? 【协议没有具体规定】
握手完成的意义
连接建立:C2 发送后,RTMP 握手结束,双方进入 业务阶段(如后续的 connect 命令,声明流名称、参数;createStream 建立流通道等)
安全保障:通过三次握手的随机数交互,确保连接双方是合法端点(中间人无法伪造匹配的随机数响应)
该报文是 RTMP 握手的收官步骤:客户端通过 C2 回应服务端的挑战,完成双向认证。
3、建立网络连接(NetConnection)
包括以下步骤:
①、客户端发起连接请求
②、服务器设置客户端的应答窗口大小
③、服务器设置客户端的发送带宽大小
④、服务器设置客户端的接收块大小
⑤、服务器响应连接结果
⑥、客户端设置服务器的接收块大小
①、客户端发起连接请求
客户端发送命令消息中的“连接” (connect)到服务器, 请求与一个服务应用实例建立连接。
②、服务器设置客户端的应答窗口大小、发送带宽大小、接收块大小
服务器接收到连接命令消息后,发送确认窗口大小、发送带宽大小、接收块大小(Window Acknowledgement Size)协议消息到客户端,同时连接到连接命令中提到的应用程序。
③、服务器响应创建流结果
服务器发送命令消息中的 “结果” (_result), 通知客户端连接的状态
④、客户端设置服务器的接收块大小
客户端发送确认窗口大小(Window Acknowledgement Size)协议消息到服务器端。
4、建立网络流(NetStream)
包括以下步骤:
客户端发送命令消息中的“创建流” (createStream) 命令到服务器端。
服务器端接收到“创建流” 命令后, 发送命令消息中的“结果” (_result), 通知客户端流的状态
客户端向服务器获取指定流的长度
①、客户端发起创建流请求
②、服务器响应创建流结果
③、客户端向服务器获取指定流的长度
5、播放(Play)
客户端发送命令消息中的“播放” (play) 命令到服务器。
服务器发送用户控制消息中的 “stream begin” ,告知客户端流 ID
服务器发送客户端要播放的音频和视频数据
①、客户端发送播放请求
②、服务器发送 stream begin
③、服务器发送音视频数据给客户端