## 一篇文章搞清楚直播协议RTMP
说起RTMP协议,相信很多人都比较陌生,这个协议相对HTTP、HTTPS、TCP等我们常见的协议而言,我们在工作中确实较少接触它,但是对现在如火如荼的直播行业,RTMP是一个重要的协议,它在实时音视频场景中使用非常广泛,而且目前市占率很高。
本文的主要内容是分析RTMP的协议,当然不是纯理论分析,这样没多大意思,还是结合实践抓包文件来具体分析,这样才能较好地理解RTMP的内涵。具体如何抓包见本文末尾的“**Android抓包**”模块。希望你阅读完本章之后,自己也能简单地动手操作一下,这样理解深刻一下。
原版的协议内容太冗长了,感兴趣可以看一下https://www.adobe.com/devnet/rtmp.html
## **RTMP基础介绍**
RTMP协议的主要特点:
* RTMP协议是应用层协议,是要靠底层可靠的传输层(TCP)
* 协议(通常是TCP)来保证信息传输的可靠性的。在基于传输层协议的链接建立完成后,RTMP协议也要客户端和服务器通过“握手”来建立基于传输层链接之上的RTMP Connection链接。播放一个RTMP协议的流媒体需要经过以下几个步骤:握手,建立网络连接,建立网络流,播放。服务器和客户端之间只能建立一个网络连接,但是基于该连接可以创建很多网络流。
![](https://img.kancloud.cn/00/d7/00d70718fe8b42bcd358499ee656eb44_656x273.png)
**这儿埋下一个小疑问?为什么传输层已经建立了TCP连接,RTMP还需要再次建立一个连接,有这个必要吗?**
* RTMP协议传输时会对数据做自己的格式化,这种格式的消息我们称之为RTMP Message,而实际传输的时候为了更好地实现多路复用、分包和信息的公平性,发送端会把Message划分为带有Message ID的Chunk,每个Chunk可能是一个单独的Message,也可能是Message的一部分,在接受端会根据chunk中包含的data的长度,message id和message的长度把chunk还原成完整的Message,从而实现信息的收发。
## **RTMP握手**
RTMP基于TCP,已知TCP需要3次握手才可以建立连接,在TCP3次握手成功之后,应用层的RTMP也是需要握手的,就是认证过程。具体的认证过程如下:
![](https://img.kancloud.cn/06/bb/06bbb542043ad5a06a267a37d318f528_534x341.png)
* 客户端发送 C0、C1、 C2,服务器发送 S0、 S1、 S2。
* 首先,客户端发送 C0 表示自己的版本号,不必等对方的回复,然后发送 C1 表示自己的时间戳。
* 服务器只有在收到 C0 的时候,才能返回 S0,表明自己的版本号,如果版本不匹配,可以断开连接。
* 服务器发送完 S0 后,也不用等什么,就直接发送自己的时间戳 S1。客户端收到 S1 的时候,发一个知道了对方时间戳的 ACK C2。同理服务器收到 C1 的时候,发一个知道了对方时间戳的 ACK S2。
* 握手建立完成。
现在回答上面提出的问题,为什么RTMP还需要单独建立一个连接?
**因为它们需要商量一些事情,保证以后的传输能正常进行。主要就是两个事情,一个是版本号,如果客户端、服务器的版本号不一致,则不能工作。另一个就是时间戳,视频播放中,时间是很重要的,后面的数据流互通的时候,经常要带上时间戳的差值,因而一开始双方就要知道对方的时间戳。**
光讲纯理论,没意思,还是抓包看一下具体的流程吧。
### 1.首先TCP 3次握手
![](https://img.kancloud.cn/5e/0a/5e0ab00883c4cbcbc7803593a22a8eb6_1080x171.png)
### 2.RTMP握手过程
![](https://img.kancloud.cn/3a/5d/3a5d88a3255fb775becd7ef3f658c5c2_1080x253.png)
我们发现真实发包是C0+C1一起发;S0、S1、S2一起发。但是发送的时候还是会严格按照时序来控制的,这样才能真正校验好版本号等字段。
![](https://img.kancloud.cn/86/2a/862a0510601060118643184009d2fae9_800x100.png)
*****
## **拉流**
RTMP拉流的核心流程如下:
![](https://img.kancloud.cn/a1/f9/a1f9ba68238533faf438a172599410ab_604x886.png)
### 1.建立网络连接
![](https://img.kancloud.cn/c3/ea/c3eac4debe527cb98c17ee33668f9af4_1080x480.png)
客户端发送命令消息中的“连接”(connect)到服务器,请求与一个服务应用实例建立连接。
**StreamID** : 是每个消息的唯一标识,划分成Chunk和还原Chunk为Message的时候都是根据这个ID来辨识是否是同一个消息的Chunk的,这里面为0说明这个消息是初始的0消息。
**Chunk stream ID**:一个RTMP message会拆分成多个chunk,同一个Chunk Stream ID必然属于同一个Message。这样在传送过程中发过来的chunk就是通过chunk stream ID最终组装成功我一个完成的message数据的。
**message type id** (消息的类型id):表示实际发送的数据的类型,如8代表音频数据、9代表视频数据。如下面的两张图,这样看上去是不是好理解一点了。
![](https://img.kancloud.cn/2d/a2/2da279e559ff3b4b50508d94b56ce081_734x256.png)
**Format:**指的是chunk type。共有4种不同的格式,其中第一种格式字段为0,可以表示其他三种表示的所有数据,但由于其他三种格式是基于对之前chunk的差量化的表示,因此可以更简洁地表示相同的数据,实际使用的时候还是应该采用尽量少的字节表示相同意义的数据。因为type 0是表示不同数据,其他是差量,所以可以想象如果搜不到type 0的包说明这个流肯定有问题。可以通过“rtmp.header.format == 0”过滤。
### 2.建立一个网络流
网络流代表了发送多媒体数据的通道。服务器和客户端之间只能建立一个网络连接,且多个网络流可以复用这一个网络连接。这个在上面已经反复说过。
客户端向服务器请求创建流:
![](https://img.kancloud.cn/dc/5c/dc5c7bb15d3fffa334b720362f552a39_1080x508.png)
服务器收到请求后向客户端发送\_result(),对创建流的消息进行响应。此时NetStream创建完成。
![](https://img.kancloud.cn/17/a2/17a2b6037e472f1f69865085aaa187b6_1080x477.png)
### 3.Play 播放
客户端发送命令消息中的“播放”(play)命令到服务器。
![](https://img.kancloud.cn/d0/78/d07897f600ee26070c02a0e31d435cfd_1080x529.png)
接收到播放命令后,服务器发送设置块大小(ChunkSize)协议消息。
服务器发送用户控制消息中的“streambegin”,告知客户端流ID。
![](https://img.kancloud.cn/65/11/651123cc720588eecf5f9455306ded1f_1080x315.png)
播放命令成功的话,服务器发送命令消息中的“响应状态” NetStream.Play.Start,告知客户端“播放”命令执行成功。
![](https://img.kancloud.cn/52/55/5255f2e008623dde79d018f65bd5fcc4_1080x204.png)
我们发现执行了3个动作,分别如下:
![](https://img.kancloud.cn/28/a7/28a79e2fe674ca7ad65ba49e26b6915e_887x488.png)
![](https://img.kancloud.cn/90/a5/90a5d64d8357cc52fe8e886003fe8826_643x390.png)
![](https://img.kancloud.cn/1b/a1/1ba10da166f7aab68b82a22d2cd6e1a8_938x603.png)
共用一个Stream ID,并且在可以播放消息回来之后,已经解析出视频的基本属性。
*****
## 推流
分析完拉流的所有操作,其实推流也是类似的,区别在Play ---> Publishing了。
![](https://img.kancloud.cn/08/b5/08b50d97235395a30002f36246e40b99_657x890.png)
**Android抓包**
* 进入网站:https://www.androidtcpdump.com/android-tcpdump/downloads,下载最新版本的android tcpdump工具,现在最新版本是4.9.3
* 找一个root的手机,将下载好的tcpdump文件先push到/sdcard/ 下面,adb push tcpdump /sdcard/tcpdump
* adb shell进入手机adb 模式下,cp -rf /sdcard/tcpdump /data/local/,将tcpdump拷贝到/data/local/目录下
* chmod 777 /data/tcpdump,赋予tcpdump完全的执行权限
* ./data/local/tcpdump -i any -p -s 0 -w /sdcard/capture.pcap
* 然后开始访问rtmp的请求,访问完成后,会在/sdcard/目录下生成capture.pcap文件
* adb pull /sdcard/capture.pcap ,本地使用wireshare分析capture.pcap文件
* rtmp的测试源提供一个:rtmp://58.200.131.2:1935/livetv/hunantv
回复**RTMP抓包**我把本次的capture.pcap发给你。
- 序言
- 编解码
- H264
- HEVC码流解析
- H264编码原理
- 多媒体封装
- MP4
- 学好 MP4,让直播更给力
- AAC
- FLV
- 流媒体协议
- RTSP
- RTCP
- RTP
- H265 RTP封包笔记
- SDP
- RTMP
- RTMP URL
- rtmp url基础
- webrtc
- 编译
- 最简单的编译webrtc方案
- Webrtc音视频会议之Webrtc“不求甚解”
- Webrtc音视频会议之Mesh/MCU/SFU三种架构
- 音频传输之Jitter Buffer设计与实现
- Janus
- Webrtc音视频会议之Janus编译
- Webrtc音视频会议之Janus源码架构设计
- webrtc服务器-janus房间管理
- 源码分析
- WebRTC视频JitterBuffer详解
- 走读Webrtc 中的视频JitterBuffer(一)
- 走读webrtc 中的视频JitterBuffer(二)
- webrtc视频帧率控制算法机制
- 目标码率丢帧-1
- 目标帧率丢帧-2
- 29 如何使用Medooze 实现多方视频会议
- FFmpeg
- FFmpeg编译
- Window10下编译最新版FFmpeg的方法步骤
- FFMPEG静态库编译
- ffmpeg实现画中画
- FFmpeg推流器
- ffmpeg-aac
- OpenCV
- OpenCV学习笔记——视频的边缘检测
- 图像特征点匹配(视频质量诊断、画面抖动检测)
- 图像质量诊断