RTMP 協議精講
前言
2018 年 12 月,接到了一個調查原有直播系統播放端與編碼端時差對不上的任務,於是首先開始學習 RTMP 協議,之後閱讀 nginx_rtmp_module 程式碼,除錯程式,調查原因,經過一個多月的努力,幸不辱命,終於查清並解決了該問題,成功為公司的實時直播系統優化掉 10 多毫秒的延時;於是公司安排我講解 RTMP 協議 和 nginx_rtmp_module 程式碼結構與流程,遂生成了此篇文章。雖然很多內容來自於網路,但我詳細整理和補充了其中的內容,修正了很多網上的錯誤,望能幫助到各位做流媒體開發的朋友,也請大家能尊重我的辛苦付出,轉載請註明出處,謝謝!
作者:楊玉奇
2019 年 1 月 22 日
轉發請保留
目錄
RTMP 協議
- 簡介
- 握手包
- 塊格式
- 協議控制訊息
- AMF 訊息
互動過程分析
- 握手及通用命令
- Publish 過程分析
- Play 過程分析
RTMP 協議 – 簡介
RTMP 是 Real Time Messaging Protocol(實時訊息傳輸協議)的首字母縮寫。是Adobe Systems 公司為 Flash 播放器和伺服器之間音訊、視訊和資料傳輸 開發的開放協議。該協議基於 TCP,是一個協議族,包括 RTMP 基本協議及 RTMPT/RTMPS/RTMPE等多種變種。
- RTMP 工作在 TCP 之上,預設使用埠 1935;
- RTMPE 在 RTMP 的基礎上增加了加密功能;
- RTMPT 封裝在 HTTP 請求之上,可穿透防火牆;
- RTMPS 類似 RTMPT,增加了 TLS/SSL 的安全功能;
RTMP 協議中資料都是大端

image.png
RTMP 協議 – 握手包
- C0 / S0
- C1 / S1
- C2 / S2
- 簡單握手 / 複雜握手
C0 / S0
C0 和 S0 包由一個位元組組成(目前的值應該為 3),下面是 C0/S0 包內的欄位:

image.png
在 C0 包內,這個欄位代表客戶端請求的 RTMP 版本號。在 S0 包內,這個欄位代表服務端選擇的 RTMP 版本號。此文件使用的版本是 3。版本 0-2 用在早期的產品中,現在已經被棄用;版本 4-31 被預留用於後續產品;版本 32-255(為了區分 RTMP 協議和文字協議,文字協議通常以可列印字元開始)不允許使用。如果伺服器無法識別客戶端的版本號,應該回復版本 3。客戶端可以選擇降低到版本 3,或者中止握手過程。
C1 / S1
C1 和 S1 包長度為 1536 位元組,包含以下欄位:
包內的欄位:

image.png
time: 毫秒值的時間戳,這個值可以是 0,或者一些任意值。用於本終端傳送的所有後續塊的時間起點。
zero: 必須全 0
Random data: 本欄位可以包含任意資料。由於握手的雙方需要區分另一端,此欄位填充的資料必須足夠隨機(以防止與其他握手端混淆)。不過沒必要為此使用加密資料或動態資料。
服務端必須接收到 C0 訊息,才能傳送 S0 和 S1 訊息。
C2 / S2
客戶端必須接收到 S1 訊息,然後傳送 C2 訊息。客戶端必須接收到 S2 訊息,然後傳送其他資料。
服務端必須接收到 C1 訊息,然後傳送 S2 訊息。服務端必須接收到 C2 訊息,然後傳送其他資料。

image.png
time1: 這個欄位必須是對端傳送過來的時間戳(C1 或 S1 內的)。
time2: 這個欄位值為本機接收到對端傳送過來的握手包的時刻。
random echo: 這個欄位必須是對端傳送過來的隨機資料。握手的雙方可以使用 time1 和 time2 欄位來估算網路連線的頻寬和/或延遲,但是不一定有用。
簡單握手 / 複雜握手
前面描述的握手協議是標準的 Adobe RTMP 文件中描述的內容,我們稱為簡單握手。
在 Flash 10.1 之後,Adobe 公司改了握手,簡單握手不能用了,但是 Adobe 公司並沒有修正文件。握手步驟沒有變,但內容完全不一樣,資料是加密的。
它的步驟是由三個固定大小的塊組成,而不是可變大小的塊加上頭。握手開始於客戶端傳送 C0 + C1 塊。在傳送 C2 之前客戶端必須等待接收 S1。在傳送任何資料之前客戶端必須等待接收 S2。服務端在傳送 S0 和 S1 之前必須等待接收 C0,也可以等待接收C1。服務端在傳送 S2 之前必須等待接收 C1。服務端在傳送任何資料之前必須等待接收 C2。
C1 / S1 和 C2 / S2 的 1536 位元組是:
- 4 位元組的當前時間:(u_int32_t)time(NULL)
- 4 位元組的程式版本:C1 一般是 0x80000702,S1 是 0x04050001
- 764 位元組的 KeyBlock 或者 DigestBlock
- 764 位元組的 KeyBlock 或者 DigestBlock
在不同的包裡,後兩個順序可能會顛倒
子結構 KeyBlock 定義: - 760 bytes: 包含 128 bytes 的 key 的資料。
- key_offset: 4 bytes,最後 4 位元組定義了 key的 offset(相對於 KeyBlock 開頭而言)
子結構 DigestBlock 定義: - digest_offset: 4bytes,開頭 4 位元組定義了 digest 的 offset(相對於 DigestBlock 的第 5 位元組而言,offset=3 表示 digestBlock[7~38] 為 digest
- 760bytes: 包含 32 bytes 的 digest 的資料。
RTMP 協議 – 塊格式
塊格式
握手之後,連線複用一個或多個塊流。建立的每個塊都有一個唯一 ID 對其進行關聯,這個 ID 叫做 chunk stream ID (塊流 ID),每一類 csid 都對應一種功能。傳遞時,每個塊必須被完全傳送才可以傳送下一塊。在接收端,這些塊被根據塊流 ID 被組裝成訊息。
組塊允許更高層協議中的大訊息分解成較小的訊息,例如防止大的、低優先順序的訊息(如視訊)阻塞較小但高優先順序的訊息,如:音訊(高優先順序)或控制(中優先順序)。塊大小是可配置的。

image.png
Basic Header (基本頭,1 到 3 個位元組):這個欄位對塊流 ID 和塊型別進行編碼。長度完全取決於塊流 ID,因為塊流 ID 是一個可變長度的欄位。
Message Header (訊息頭,0,3,7,或者 11 個位元組):這一欄位對正在傳送的訊息 (不管是整個訊息,還是隻是一小部分) 的資訊進行編碼。這一欄位的長度可以使用塊頭中定義的塊型別進行決定。
Extended Timestamp (擴充套件 timestamp,0 或 4 位元組):這一欄位是否出現取決於塊訊息頭中的 timestamp 或者 timestamp delta 欄位。它表示的是時間戳的增量,需要與 timestamp 或者 timestamp delta 相加獲得總時間戳。
Chunk Data (有效大小):當前塊的有效負載,相當於定義的最大塊大小。
Basic Header
RTMP 協議最多支援 65597 個流(chunk),CS ID 範圍為: 3 ~ 65599。ID 0、1、2 被保留。
0 值表示二位元組形式,並且 ID 範圍 64 ~ 319 (第二個位元組 + 64)。
1 值表示三位元組形式,並且 ID 範圍為 64 ~ 65599 ((第三個位元組) * 256 + 第二個位元組 + 64)。3 ~ 63 範圍內的值表示整個流 ID。
帶有 2 值的塊流 ID 被保留,用於下層協議控制訊息和命令。
塊基本頭中的 0 ~ 5 位 (最低有效) 代表塊流 ID,塊流 ID 2 ~ 63 可以編進這一欄位的一位元組版本中。

image.png
Basic Header 為 1 位元組的情況

image.png
Basic Header 為 2 位元組的情況 (第一個位元組後 6 位為 000000)

image.png
Basic Header 為 3 位元組的情況(第一個位元組後 6 位為 000001)

image.png
CSID 保留值
0、1、2 被保留, 3 ~ 8 基本都是固定用途,所以 9 ~ 65599 才用於自定義 csid,但一般我們用不到。
- 0 表示 Basic Header 總共要佔用 2 個位元組
- 1 表示 Basic Header 總共要佔用 3 個位元組
- 2 代表該 chunk 是控制資訊和一些命令資訊
- 3 代表該 chunk 是客戶端發出的 AMF0 命令以及服務端對該命令的應答
- 4 代表該 chunk 是客戶端發出的音訊資料,用於 publish
- 5 代表該 chunk 是服務端發出的 AMF0 命令和資料
- 6 代表該 chunk 是服務端發出的音訊資料,用於 play;或客戶端發出的視訊資料,用於 publish
- 7 代表該 chunk 是服務端發出的視訊資料,用於 play
- 8 代表該 chunk 是客戶端發出的 AMF0 命令,專用來發送: getStreamLength, play, publish
Message Header
Basic Header fmt 對 Message Header 長度的影響
- fmt == 0:Message Header 長度為 11
- fmt == 1:Message Header 長度為 7
- fmt == 2:Message Header 長度為 3
- fmt == 3:Message Header 長度為 0
Chunk Msg Header 可變的原因是為了壓縮傳輸的位元組數,把一些相同型別的 chunk 的 head 去掉一些位元組,換句話說就是四種類型的包頭都可以通過一定的規則還原成 11 個位元組,這個壓縮和還原在 RTMP 協議中稱之為複用/解複用。
fmt == 0
表示 message stream 是個新的流,即使 message stream id 複用了以前的 id。這會是 11 個位元組的完整包頭。

image.png
timestamp:對於 fmt == 0 的 chunk,絕對時間戳在這裡表示,如果時間戳值大於等於 0xffffff(16777215),該值必須是 0xffffff, 且時間戳擴充套件欄位 (Extended Timestamp) 必須傳送,其他情況沒有要求。
message length:Message 的長度,注意這裡的長度並不是跟隨 chunk head 其後的 chunk data(Payload)的長度,而是前文提到的一條信令或者一幀視訊資料或音訊資料的長度。前文提到過信令或者媒體資料都稱之為 Message,一條 Message 可以分為一條或者多條 chunk。
message type id:後面有詳述。
fmt == 1
長度是 7 bytes 的 chunk head,該型別不包含 stream ID,該 chunk 的 streamID 和前一個 chunk 的 stream ID 是相同的。變長的訊息,例如視訊流格式,在第一個新的 chunk 以後使用這種型別。注意其中時間戳部分是相對時間。

image.png
timestamp delta : 相對時間,是與上一個 相同 CSID 的 chunk 之間的差值。如果 timestamp delta 大於或等於 16777215(十六進位制 0xFFFFFF),這個欄位必須是 16777215,指示擴充套件時間戳欄位 (Extended Timestamp) 的存在。
fmt == 2
3 bytes 的 chunk head,從之前具有相同塊流 ID 的塊中取相應的值。該型別既不包含 stream ID 也不包含訊息長度,這種型別用於 stream ID 和前一個 chunk 相同,且有固定長度的資訊,例如音訊流格式,在第一個新的 chunk 以後使用該型別。

image.png
fmt == 3
0 bytes 的 message head,從之前具有相同塊流 ID 的塊中取相應的值。當一個單個訊息拆成多個 chunk 時,這些 chunk 除了第一個以外,其他的都應該使用這種型別。
Message Type ID
- 1 設定塊大小
- 2 中斷訊息,丟棄舊資料
- 3 確認
- 4 使用者控制訊息
- 5 設定確認視窗大小
- 6 設定流頻寬
- 7 音訊資料
- 9 視訊資料
- 15(0x0f). AMF3 資料
- 16(0x10) AMF3 共享物件事件
- 17(0x11) AMF3 命令
- 18(0x12) AMF0 資料
- 19(0x13) AMF0 共享物件事件
- 20(0x14) AMF0 命令,Invoke 方法呼叫
- 22(0x16) 聚合訊息, H.264, 類似 FLV 檔案儲存格式,每個音視訊包作為一個 Tag, 許多的 Tag 組成了這個 AMFType=0x16 的資料型別
RTMP 協議 – 協議控制訊息
格式
RTMP 塊流使用 Message Type ID 1 ~ 6 (見上一節)作為控制訊息,這些訊息包含了必要的 RTMP 塊流協議資訊。
這些協議控制訊息必須使用使用 2 作為塊流 ID(CSID),使用 0 作為訊息流 ID(message stream id) 。
協議控制訊息接收立即生效;解析時,時間戳欄位被忽略。
協議控制訊息的 RTMP 頭樣式:

image.png
設定塊大小 Message Type ID=1
chunk 的初始長度固定為 128 個位元組,但是這個值是可變的,在客戶端和服務端建立連線後,客戶端和服務端都可以通過傳送信令的方式來通知對端修改 chunk 的長度,理論上來說可以修改 chunk 的最長長度為 65536。這裡 chunk 的長度是指 chunk 的資料部分的長度,即 chunk data(payload)的長度。
設定塊大小的 RTMP 訊息樣式:

image.png
chunk size 的特殊規定:

image.png
其中第一位必須為 0,chunk Size 佔 31 位,最大可代表 2147483647=0x7FFFFFFF=231-1,但實際上所有大於 16777215=0xFFFFFF 的值都用不上,因為 chunk size 不能大於 Message 的長度,表示 Message 的長度欄位是用 3 個位元組表示的,最大隻能為 0xFFFFFF。
中斷訊息 Message Type ID=2
當一個 Message 被切分為多個 chunk,接受端只接收到了部分 chunk 時,傳送該控制訊息表示傳送端不再傳輸同 Message 的 chunk,接受端接收到這個訊息後要丟棄這些不完整的 chunk。Data 資料中只需要一個 CSID,表示丟棄該 CSID 的所有已接收到的 chunk。
中斷訊息的 RTMP 訊息樣式:

image.png
確認 Message Type ID=3
當收到對端的訊息大小等於視窗大小(Window Size)時接受端要回饋一個 ACK 給傳送端告知對方可以繼續傳送資料。視窗大小就是指收到接受端返回的 ACK 前最多可以傳送的位元組數量,返回的 ACK 中會帶有從傳送上一個 ACK 後接收到的位元組數。
確認的 RTMP 訊息樣式:

image.png
視窗確認大小 Message Type ID=5
傳送端在接收到接受端返回的兩個確認間最多可以傳送的位元組數。
視窗確認大小的 RTMP 訊息樣式:

image.png
設定頻寬 Message Type ID=6
限制對端的輸出頻寬。接受端接收到該訊息後會通過設定訊息中的 Window ACK Size 來限制已傳送但未接受到反饋的訊息的大小來限制傳送端的傳送頻寬。如果訊息中的 Window ACK Size 與上一次傳送給傳送端的 size 不同的話要回饋一個 Window Acknowledgement Size 的控制訊息數。
設定頻寬的 RTMP 訊息樣式:

image.png
Limit Type 為 0、1、2 中的一個:
- 0:Hard,接受端應該將 Window Ack Size 設定為訊息中的值
- 1:Soft,接受端可以將 Window Ack Size 設為訊息中的值,也可以儲存原來的值(前提是原來的Size 小於該控制訊息中的 Window Ack Size)
- 2:Dynamic,如果上次的設定頻寬訊息中的 Lim Type 為 0,本次也按 Hard 處理,否則忽略本訊息,不去設定 Window Ack Size
RTMP 協議 – AMF 訊息
AMF 命令
傳送端傳送時會帶有:
- 命令的名字,如 connect
- Transaction ID 表示此次命令的標識
- Command Object 表示相關引數
接受端收到命令後,會返回以下三種訊息中的一種:
- _result 訊息表示接受該命令,對端可以繼續往下執行流程
- _error 訊息代表拒絕該命令要執行的操作
- method name 訊息代表要在之前命令的傳送端執行的函式名稱。
這三種迴應的訊息都要帶有收到的命令訊息中的 Transaction ID 來表示本次的迴應作用於哪個命令。�
可以認為傳送命令訊息的物件有兩種: - 一種是 Net Connection,表示雙端的上層連線
- 一種是 Net Stream,表示流資訊的傳輸通道,控制流資訊的狀態,如 Play 播放,Pause 暫停等。
這些命令直接以 AMF 格式資料放置在 Chunk Data 部分。
AMF 連線層命令
Connect

image.png
Invoke / Call

image.png
_result
如果 Invoke 訊息中的 TransactionID 不為 0 的話,對端需要對該命令做出響應,響應的訊息結構如下:

image.png
Create Stream

image.png
AMF 流控制命令
建立在 Net Connection 之上,通過 Net Connection 的 Create Stream 命令建立,用於傳輸具體的音訊、視訊等資訊。在傳輸層協議之上只能連線一個 Net Connection,但一個 Net Connection 可以建立多個 Net Stream 來建立不同的流通道傳輸資料。
play
由客戶端向伺服器發起請求從伺服器端接受資料(如果傳輸的資訊是視訊的話就是請求開始播流),可以多次呼叫,這樣本地就會形成一組資料流的接收者。注意其中有一個 reset 欄位,表示是覆蓋之前的播流(設為 true)還是重新開始一路播放(設為 false)。

image.png
play2
可以將當前正在播放的流切換到同樣資料但不同位元率的流上,伺服器端會維護多種位元率的檔案來供客戶端使用 play2 命令來切換。

image.png
delete Stream
用於客戶端告知伺服器端本地的某個流物件已被刪除,不需要再傳輸此路流。

image.png
receiveAudio
通知伺服器端該客戶端是否要傳送音訊。

image.png
receiveVideo
通知伺服器端該客戶端是否要傳送視訊。

image.png
publish
由客戶端向伺服器發起請求推流到伺服器。

image.png
seek
定位到視訊或音訊的某個位置,以毫秒為單位。

image.png
Pause
客戶端告知服務端停止或恢復播放。

image.png
onStatus
客戶端向服務端傳送流控制命令後,服務端會通過 onStatus 的命令來響應客戶端,表示當前 Net Stream 的狀態。

image.png
onStatus 狀態值:
- "NetStream.Play.Reset" --- 播放列表重置
- "NetStream.Play.Start" --- 播放開始
- "NetStream.Buffer.Empty" --- 視訊正在緩衝
- "NetStream.Buffer.Full" --- 緩衝區已填滿
- "NetStream.Play.StreamNotFound" --- 找不到此視訊
- "NetStream.Play.Stop" --- 視訊播放完成
- "NetStream.Pause.Notify" --- 流已暫停
- "NetStream.Unpause.Notify" --- 流已恢復
- "NetStream.Seek.Failed" --- 搜尋失敗
- "NetStream.SeekStart.Notify" --- 搜尋開始
- "NetStream.Seek.Notify" --- 正在搜尋
- "NetStream.Seek.Complete" --- 搜尋完畢
- "NetStream.Publish.Start" --- 釋出開始
- "NetStream.Unpublish.Success" --- 停止釋出
- "NetStream.Record.Start" --- 開始錄製
- "NetStream.Record.Stop" --- 停止錄製
- "NetStream.Publish.BadName" --- 警告!試圖釋出已經被他人釋出的流
- "NetStream.Play.PublishNotify" --- 釋出開始,資訊已經發送到所有訂閱者
- "NetStream.Play.UnpublishNotify" --- 釋出停止,資訊已經發送到所有訂閱者
- "NetStream.Play.InsufficientBW" --- 警告!客戶端沒有足夠的頻寬,無法以正常速度播放資料"
AMF 共享物件訊息
共享物件是 Flash 物件,可以通過多客戶端,例項同步傳輸。在 AMF0 編碼中,型別為 19;在 AMF3 編碼中,型別為 16。每個訊息可以包含多個事件。
訊息格式:

image.png
共享物件訊息支援的事件型別:
- Use (=1):The client sends this event to inform the server about the creation of a named shared object.
- Release (=2):The client sends this event to the server when the shared object is deleted on the client side.
- Request Change (=3): The client sends this event to request that the change the value associated with a named parameter of the shared object.
- Change (=4): The server sends this event to notify all clients, except the client originating the request, of a change in the value of a named parameter.
- Succes s(=5): The server sends this event to the requesting client in response to RequestChange event if the request is accepted.
- SendMessage (=6): The client sends this event to the server to broadcast a message. On receiving this event, the server broadcasts a message to all the clients, including the sender.
- Status (=7): The server sends this event to notify clients about error conditions.
- Clear (=8): The server sends this event to the client to clear a shared object. The server also sends this event in response to Use event that the client sends on connect.
- Remove (=9): The server sends this event to have the client delete a slot
- Request Remove (=10): The client sends this event to have the client delete a slot.
- Use Success (=11): The server sends this event to the client on a successful connection.
AMF 音訊訊息
publish:
CSID = 04, Message Type ID = 08
play:
CSID = 06, Message Type ID = 08
RTMP HEADER 舉例:
- fmt == 0: 04 000000 000007 08 01000000
- fmt == 0: 04 000000 00001c 08 01000000
- fmt == 1: 44 000017 00000b 08
- fmt == 3: c4
- fmt == 2: 84 000018
- fmt == 2: 84 000017
- fmt == 3: c4
AMF 視訊訊息
publish:
CSID = 06, Message Type ID = 09
play:
CSID = 07, Message Type ID = 09
RTMP HEADER 舉例:
- fmt == 0: 07 000000 00002c 09 01000000
- fmt == 1: 47 000000 00cbf1 09
- fmt == 3: c7
- fmt == 3: c7
- fmt == 1: 47 000028 0014b7 09
- fmt == 3: c7
AMF 集合訊息
集合訊息是一個獨立訊息,包含了一系列的 RTMP 訊息。此訊息 Message Type ID = 22。集合訊息由訊息頭和訊息內容組成。訊息內容由子訊息組成,子訊息由訊息頭,訊息資料,回放指標組成。

image.png
集合訊息的訊息流 ID 覆蓋此訊息內的子訊息流的 ID。集合訊息和第一個子訊息的時間戳之間的偏移量,用來將子訊息的時間戳處理為流的時間刻度。每個子訊息的時間戳可以通過新增偏移量來處理為正常的流時間。第一個子訊息的時間戳應該和集合訊息的時間戳相同,因此偏移量應該為零。
反向指標包含了以前的訊息(包含頭資訊)的大小。集合訊息包含此欄位,一是為了適配 FLV 檔案格式,二是為了回放定位。
使用集合訊息有如下幾個優勢:
塊流在一個塊內至多可以攜帶一條完整的訊息。使用集合訊息之後,不僅可以增加塊大小,同時還減少了傳送的塊數量。
集合訊息的子訊息可以連續的儲存在記憶體中。當系統呼叫網路傳送資料時更高效。
AMF 使用者控制訊息
CSID = 2,Message Type ID = 4

image.png
CMD ID:
- Stream Begin = 0 ,流開始,4 位元組資料: stream ID
- Stream EOF = 1 ,流結束,4 位元組資料: stream ID
- StreamDry = 2 ,流中沒有更多資料,4 位元組資料: stream ID
- SetBuffer = 3,設定緩衝區長度(以毫秒為單位)8 位元組資料:stream ID + 長度
- StreamIs Recorded = 4,4 位元組資料:stream ID
- PingRequest = 6,4 位元組資料:時間戳
- PingResponse = 7,4 位元組資料:時間戳
AMF 資料
AMF0
- AMF_NUMBER = 0, 數字(double),8 位元組
- AMF_BOOLEAN = 1, 布林,1 位元組
- AMF_STRING = 2, 字串,後面跟 2 位元組長度 + 資料
- AMF_OBJECT = 3, 物件,包含多組屬性名(都是字串)與值的對應資料:2 位元組長度 + 屬性名 + AMF 資料,0x000009 表示巢狀結束
- AMF_MOVIECLIP = 4, 保留, 未使用
- AMF_NULL = 5, NULL,0 位元組
- AMF_UNDEFINED = 6, 未定義
- AMF_REFERENCE = 7, 引用
- AMF_ECMA_ARRAY = 8, 陣列
- AMF_OBJECT_END = 9, 物件結束
- AMF_STRICT_ARRAY = 0x0a, 嚴格的陣列
- AMF_DATE = 0x0b, 日期
- AMF_LONG_STRING = 0x0c, 長字串
- AMF_UNSUPPORTED = 0x0d, 未支援
- AMF_RECORDSET = 0x0e, 保留, 未使用
- AMF_XML_DOC = 0x0f, xml 文件
- AMF_TYPED_OBJECT = 0x10, 有型別的物件
- AMF_AVMPLUS = 0x11, 需要擴充套件到 AMF3
- AMF_INVALID = 0xff, 無效的
例項

image.png
互動過程分析
握手及通用命令
- 客戶端發:C0 + C1
- 服務端發:S0 + S1 + S2
- 客戶端發:C2
- 客戶端發:connect
- 服務端發:設定應答視窗大小
- 服務端發:設定流頻寬
- 服務端發:設定 chunk 塊大小
- 服務端發:_result('NetConnection.Connect.Success')
之後 publisher 和 player 的命令就不相同了
Publish 過程分析
- 客戶端發:設定 chunk 塊大小
- 客戶端發:releaseStream
- 客戶端發:FCPublish
- 客戶端發:createStream
- 服務端發:_result()
- 客戶端發:publish
- 服務端發: onStatus('NetStream.Publish.Start')
- 客戶端發:@setDataFrame
- 客戶端發:音/視訊資料
之後就是不斷的傳送音視訊資料了
Play 過程分析
- 客戶端發:設定應答視窗大小
- 客戶端發:createStream
- 服務端發:_result()
- 客戶端發:getStreamLength()
- 客戶端發:play
- 客戶端發:Set Buffer
- 服務端發:onStatus('NetStream.Play.Start')
- 服務端發:|RtmpSampleAccess()
- 服務端發:Stream Begin
- 服務端發:onMetaData()
- 服務端發:音/視訊資料
之後就是不斷的傳送音視訊資料了