## 一篇文章搞清楚直播協議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協議的流媒體需要經過以下幾個步驟:握手,建立網絡連接,建立網絡流,播放。服務器和客戶端之間只能建立一個網絡連接,但是基于該連接可以創建很多網絡流。

**這兒埋下一個小疑問?為什么傳輸層已經建立了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也是需要握手的,就是認證過程。具體的認證過程如下:

* 客戶端發送 C0、C1、 C2,服務器發送 S0、 S1、 S2。
* 首先,客戶端發送 C0 表示自己的版本號,不必等對方的回復,然后發送 C1 表示自己的時間戳。
* 服務器只有在收到 C0 的時候,才能返回 S0,表明自己的版本號,如果版本不匹配,可以斷開連接。
* 服務器發送完 S0 后,也不用等什么,就直接發送自己的時間戳 S1。客戶端收到 S1 的時候,發一個知道了對方時間戳的 ACK C2。同理服務器收到 C1 的時候,發一個知道了對方時間戳的 ACK S2。
* 握手建立完成。
現在回答上面提出的問題,為什么RTMP還需要單獨建立一個連接?
**因為它們需要商量一些事情,保證以后的傳輸能正常進行。主要就是兩個事情,一個是版本號,如果客戶端、服務器的版本號不一致,則不能工作。另一個就是時間戳,視頻播放中,時間是很重要的,后面的數據流互通的時候,經常要帶上時間戳的差值,因而一開始雙方就要知道對方的時間戳。**
光講純理論,沒意思,還是抓包看一下具體的流程吧。
### 1.首先TCP?3次握手

### 2.RTMP握手過程

我們發現真實發包是C0+C1一起發;S0、S1、S2一起發。但是發送的時候還是會嚴格按照時序來控制的,這樣才能真正校驗好版本號等字段。

*****
## **拉流**
RTMP拉流的核心流程如下:

### 1.建立網絡連接

客戶端發送命令消息中的“連接”(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代表視頻數據。如下面的兩張圖,這樣看上去是不是好理解一點了。

**Format:**指的是chunk type。共有4種不同的格式,其中第一種格式字段為0,可以表示其他三種表示的所有數據,但由于其他三種格式是基于對之前chunk的差量化的表示,因此可以更簡潔地表示相同的數據,實際使用的時候還是應該采用盡量少的字節表示相同意義的數據。因為type ??0是表示不同數據,其他是差量,所以可以想象如果搜不到type ?? 0的包說明這個流肯定有問題。可以通過“rtmp.header.format == 0”過濾。
### 2.建立一個網絡流
網絡流代表了發送多媒體數據的通道。服務器和客戶端之間只能建立一個網絡連接,且多個網絡流可以復用這一個網絡連接。這個在上面已經反復說過。
客戶端向服務器請求創建流:

服務器收到請求后向客戶端發送\_result(),對創建流的消息進行響應。此時NetStream創建完成。

### 3.Play 播放
?客戶端發送命令消息中的“播放”(play)命令到服務器。

接收到播放命令后,服務器發送設置塊大小(ChunkSize)協議消息。
服務器發送用戶控制消息中的“streambegin”,告知客戶端流ID。

播放命令成功的話,服務器發送命令消息中的“響應狀態” NetStream.Play.Start,告知客戶端“播放”命令執行成功。

我們發現執行了3個動作,分別如下:



共用一個Stream ID,并且在可以播放消息回來之后,已經解析出視頻的基本屬性。
*****
## 推流
分析完拉流的所有操作,其實推流也是類似的,區別在Play ---> Publishing了。

**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學習筆記——視頻的邊緣檢測
- 圖像特征點匹配(視頻質量診斷、畫面抖動檢測)
- 圖像質量診斷