1. 程式人生 > >(轉)RTMP協議從入門到放棄

(轉)RTMP協議從入門到放棄

ole 解決 數量 cal 列表 忽略 string類 發的 播放列表

轉載自: http://blog.csdn.net/shangmingyang/article/details/50837852

RTMP協議是Real Time Message Protocol(實時信息傳輸協議)的縮寫,它是由Adobe公司提出的一種應用層的協議,用來解決多媒體數據傳輸流的多路復用(Multiplexing)和分包(packetizing)的問題。隨著VR技術的發展,視頻直播等領域逐漸活躍起來,RTMP作為業內廣泛使用的協議也重新被相關開發者重視起來。正好最近在從事這方面的工作,在此記錄下自己對RTMP的理解,文章內容多翻譯自英文版RTMP文檔,按照本人的理解重新整理,希望可以幫助想要了解RTMP協議的朋友,也方面自己日後查閱。



1. 總體介紹:
RTMP協議是應用層協議,是要靠底層可靠的傳輸層協議(通常是TCP)來保證信息傳輸的可靠性的。在基於傳輸層協議的鏈接建立完成後,RTMP協議也要客戶端和服務器通過“握手”來建立基於傳輸層鏈接之上的RTMP Connection鏈接,在Connection鏈接上會傳輸一些控制信息,如SetChunkSize,SetACKWindowSize。其中CreateStream命令會創建一個Stream鏈接,用於傳輸具體的音視頻數據和控制這些信息傳輸的命令信息。RTMP協議傳輸時會對數據做自己的格式化,這種格式的消息我們稱之為RTMP Message,而實際傳輸的時候為了更好地實現多路復用、分包和信息的公平性,發送端會把Message劃分為帶有Message ID的Chunk,每個Chunk可能是一個單獨的Message,也可能是Message的一部分,在接受端會根據chunk中包含的data的長度,message id和message的長度把chunk還原成完整的Message,從而實現信息的收發。
2. 握手
要建立一個有效的RTMP Connection鏈接,首先要“握手”:客戶端要向服務器發送C0,C1,C2(按序)三個chunk,服務器向客戶端發送S0,S1,S2(按序)三個chunk,然後才能進行有效的信息傳輸。RTMP協議本身並沒有規定這6個Message的具體傳輸順序,但RTMP協議的實現者需要保證這幾點:

  • 客戶端要等收到S1之後才能發送C2
  • 客戶端要等收到S2之後才能發送其他信息(控制信息和真實音視頻等數據)
  • 服務端要等到收到C0之後發送S1
  • 服務端必須等到收到C1之後才能發送S2
  • 服務端必須等到收到C2之後才能發送其他信息(控制信息和真實音視頻等數據)
    如果每次發送一個握手chunk的話握手順序會是這樣:

    技術分享圖片

    理論上來講只要滿足以上條件,如何安排6個Message的順序都是可以的,但實際實現中為了在保證握手的身份驗證功能的基礎上盡量減少通信的次數,一般的發送順序是這樣的,這一點可以通過wireshark抓ffmpeg推流包進行驗證:
    |client|Server |
    |---C0+C1—->|
    |<--S0+S1+S2– |
    |---C2----> |

    3. RTMP Chunk Stream

    Chunk Stream是對傳輸RTMP Chunk的流的邏輯上的抽象,客戶端和服務器之間有關RTMP的信息都在這個流上通信。這個流上的操作也是我們關註RTMP協議的重點。

    3.1 Message(消息)

    這裏的Message是指滿足該協議格式的、可以切分成Chunk發送的消息,消息包含的字段如下:

    • Timestamp(時間戳):消息的時間戳(但不一定是當前時間,後面會介紹),4個字節
    • Length(長度):是指Message Payload(消息負載)即音視頻等信息的數據的長度,3個字節
    • TypeId(類型Id):消息的類型Id,1個字節
    • Message Stream ID(消息的流ID):每個消息的唯一標識,劃分成Chunk和還原Chunk為Message的時候都是根據這個ID來辨識是否是同一個消息的Chunk的,4個字節,並且以小端格式存儲

    3.2 Chunking(Message分塊)

    RTMP在收發數據的時候並不是以Message為單位的,而是把Message拆分成Chunk發送,而且必須在一個Chunk發送完成之後才能開始發送下一個Chunk。每個Chunk中帶有MessageID代表屬於哪個Message,接受端也會按照這個id來將chunk組裝成Message。
    為什麽RTMP要將Message拆分成不同的Chunk呢?通過拆分,數據量較大的Message可以被拆分成較小的“Message”,這樣就可以避免優先級低的消息持續發送阻塞優先級高的數據,比如在視頻的傳輸過程中,會包括視頻幀,音頻幀和RTMP控制信息,如果持續發送音頻數據或者控制數據的話可能就會造成視頻幀的阻塞,然後就會造成看視頻時最煩人的卡頓現象。同時對於數據量較小的Message,可以通過對Chunk Header的字段來壓縮信息,從而減少信息的傳輸量。(具體的壓縮方式會在後面介紹)
    Chunk的默認大小是128字節,在傳輸過程中,通過一個叫做Set Chunk Size的控制信息可以設置Chunk數據量的最大值,在發送端和接受端會各自維護一個Chunk Size,可以分別設置這個值來改變自己這一方發送的Chunk的最大大小。大一點的Chunk減少了計算每個chunk的時間從而減少了CPU的占用率,但是它會占用更多的時間在發送上,尤其是在低帶寬的網絡情況下,很可能會阻塞後面更重要信息的傳輸。小一點的Chunk可以減少這種阻塞問題,但小的Chunk會引入過多額外的信息(Chunk中的Header),少量多次的傳輸也可能會造成發送的間斷導致不能充分利用高帶寬的優勢,因此並不適合在高比特率的流中傳輸。在實際發送時應對要發送的數據用不同的Chunk Size去嘗試,通過抓包分析等手段得出合適的Chunk大小,並且在傳輸過程中可以根據當前的帶寬信息和實際信息的大小動態調整Chunk的大小,從而盡量提高CPU的利用率並減少信息的阻塞機率。
    3.3 Chunk Format(塊格式)
    技術分享圖片
    3.3.1 Basic Header(基本的頭信息):
    包含了chunk stream ID(流通道Id)和chunk type(chunk的類型),chunk stream id一般被簡寫為CSID,用來唯一標識一個特定的流通道,chunk type決定了後面Message Header的格式。Basic Header的長度可能是1,2,或3個字節,其中chunk type的長度是固定的(占2位,註意單位是位,bit),Basic Header的長度取決於CSID的大小,在足夠存儲這兩個字段的前提下最好用盡量少的字節從而減少由於引入Header增加的數據量。
    RTMP協議支持用戶自定義[3,65599]之間的CSID,0,1,2由協議保留表示特殊信息。0代表Basic Header總共要占用2個字節,CSID在[64,319]之間,1代表占用3個字節,CSID在[64,65599]之間,2代表該chunk是控制信息和一些命令信息,後面會有詳細的介紹。
    chunk type的長度固定為2位,因此CSID的長度是(6=8-2)、(14=16-2)、(22=24-2)中的一個。
    當Basic Header為1個字節時,CSID占6位,6位最多可以表示64個數,因此這種情況下CSID在[0,63]之間,其中用戶可自定義的範圍為[3,63]。
    技術分享圖片
    當Basic Header為2個字節時,CSID占14位,此時協議將與chunk type所在字節的其他位都置為0,剩下的一個字節來表示CSID-64,這樣共有8個字節來存儲CSID,8位可以表示[0,255]共256個數,因此這種情況下CSID在[64,319],其中319=255+64。
    技術分享圖片
    當Basic Header為3個字節時,CSID占22位,此時協議將[2,8]字節置為1,余下的16個字節表示CSID-64,這樣共有16個位來存儲CSID,16位可以表示[0,65535]共65536個數,因此這種情況下CSID在[64,65599],其中65599=65535+64,需要註意的是,Basic Header是采用小端存儲的方式,越往後的字節數量級越高,因此通過這3個字節每一位的值來計算CSID時,應該是:<第三個字節的值>x256+<第二個字節的值>+64
    技術分享圖片
    可以看到2個字節和3個字節的Basic Header所能表示的CSID是有交集的[64,319],但實際實現時還是應該秉著最少字節的原則使用2個字節的表示方式來表示[64,319]的CSID。

    3.3.2 Message Header(消息的頭信息):
    包含了要發送的實際信息(可能是完整的,也可能是一部分)的描述信息。Message Header的格式和長度取決於Basic Header的chunk type,共有4種不同的格式,由上面所提到的Basic Header中的fmt字段控制。其中第一種格式可以表示其他三種表示的所有數據,但由於其他三種格式是基於對之前chunk的差量化的表示,因此可以更簡潔地表示相同的數據,實際使用的時候還是應該采用盡量少的字節表示相同意義的數據。以下按照字節數從多到少的順序分別介紹這4種格式的Message Header。
    Type=0:
    技術分享圖片
    type=0時Message Header占用11個字節,其他三種能表示的數據它都能表示,但在chunk stream的開始的第一個chunk和頭信息中的時間戳後退(即值與上一個chunk相比減小,通常在回退播放的時候會出現這種情況)的時候必須采用這種格式。

    • timestamp(時間戳):占用3個字節,因此它最多能表示到16777215=0xFFFFFF=2
      24-1, 當它的值超過這個最大值時,這三個字節都置為1,這樣實際的timestamp會轉存到Extended Timestamp字段中,接受端在判斷timestamp字段24個位都為1時就會去Extended timestamp中解析實際的時間戳。
    • message length(消息數據的長度):占用3個字節,表示實際發送的消息的數據如音頻幀、視頻幀等數據的長度,單位是字節。註意這裏是Message的長度,也就是chunk屬於的Message的總數據長度,而不是chunk本身Data的數據的長度。
    • message type id(消息的類型id):占用1個字節,表示實際發送的數據的類型,如8代表音頻數據、9代表視頻數據。
    • msg stream id(消息的流id):占用4個字節,表示該chunk所在的流的ID,和Basic Header的CSID一樣,它采用小端存儲的方式,
      Type = 1:
      技術分享圖片
      type=1時Message Header占用7個字節,省去了表示msg stream id的4個字節,表示此chunk和上一次發的chunk所在的流相同,如果在發送端只和對端有一個流鏈接的時候可以盡量去采取這種格式。
      timestamp delta:占用3個字節,註意這裏和type=0時不同,存儲的是和上一個chunk的時間差。類似上面提到的timestamp,當它的值超過3個字節所能表示的最大值時,三個字節都置為1,實際的時間戳差值就會轉存到Extended Timestamp字段中,接受端在判斷timestamp delta字段24個位都為1時就會去Extended timestamp中解析時機的與上次時間戳的差值。
      Type = 2:
      技術分享圖片
      type=2時Message Header占用3個字節,相對於type=1格式又省去了表示消息長度的3個字節和表示消息類型的1個字節,表示此chunk和上一次發送的chunk所在的流、消息的長度和消息的類型都相同。余下的這三個字節表示timestamp delta,使用同type=1
      Type = 3
      0字節!!!好吧,它表示這個chunk的Message Header和上一個是完全相同的,自然就不用再傳輸一遍了。當它跟在Type=0的chunk後面時,表示和前一個chunk的時間戳都是相同的。什麽時候連時間戳都相同呢?就是一個Message拆分成了多個chunk,這個chunk和上一個chunk同屬於一個Message。而當它跟在Type=1或者Type=2的chunk後面時,表示和前一個chunk的時間戳的差是相同的。比如第一個chunk的Type=0,timestamp=100,第二個chunk的Type=2,timestamp delta=20,表示時間戳為100+20=120,第三個chunk的Type=3,表示timestamp delta=20,時間戳為120+20=140
      3.3.3 Extended Timestamp(擴展時間戳):
      上面我們提到在chunk中會有時間戳timestamp和時間戳差timestamp delta,並且它們不會同時存在,只有這兩者之一大於3個字節能表示的最大數值0xFFFFFF=16777215時,才會用這個字段來表示真正的時間戳,否則這個字段為0。擴展時間戳占4個字節,能表示的最大數值就是0xFFFFFFFF=4294967295。當擴展時間戳啟用時,timestamp字段或者timestamp delta要全置為1,表示應該去擴展時間戳字段來提取真正的時間戳或者時間戳差。註意擴展時間戳存儲的是完整值,而不是減去時間戳或者時間戳差的值。
      3.3.4 Chunk Data(塊數據):
      用戶層面上真正想要發送的與協議無關的數據,長度在(0,chunkSize]之間。
      3.3.5 chunk表示例1
      技術分享圖片
      首先包含第一個Message的chunk的Chunk Type為0,因為它沒有前面可參考的chunk,timestamp為1000,表示時間戳。type為0的header占用11個字節,假定chunkstreamId為3<127,因此Basic Header占用1個字節,再加上Data的32個字節,因此第一個chunk共44=11+1+32個字節。
      第二個chunk和第一個chunk的CSID,TypeId,Data的長度都相同,因此采用Chunk Type=2,timestamp delta=1020-1000=20,因此第二個chunk占用36=3+1+32個字節。
      第三個chunk和第二個chunk的CSID,TypeId,Data的長度和時間戳差都相同,因此采用Chunk Type=3省去全部Message Header的信息,占用33=1+32個字節。
      第四個chunk和第三個chunk情況相同,也占用33=1+32個字節。
      最後實際發送的chunk如下:
      技術分享圖片

    3.3.6 chunk表示例2
    技術分享圖片
    註意到Data的Length=307>128,因此這個Message要切分成幾個chunk發送,第一個chunk的Type=0,Timestamp=1000,承擔128個字節的Data,因此共占用140=11+1+128個字節。
    第二個chunk也要發送128個字節,其他字段也同第一個chunk,因此采用Chunk Type=3,此時時間戳也為1000,共占用129=1+128個字節。
    第三個chunk要發送的Data的長度為307-128-128=51個字節,還是采用Type=3,共占用1+51=52個字節。
    最後實際發送的chunk如下:
    技術分享圖片
    3.4 協議控制消息(Protocol Control Message)
    在RTMP的chunk流會用一些特殊的值來代表協議的控制消息,它們的Message Stream ID必須為0(代表控制流信息),CSID必須為2,Message Type ID可以為1,2,3,5,6,具體代表的消息會在下面依次說明。控制消息的接受端會忽略掉chunk中的時間戳,收到後立即生效。

    • Set Chunk Size(Message Type ID=1):設置chunk中Data字段所能承載的最大字節數,默認為128B,通信過程中可以通過發送該消息來設置chunk Size的大小(不得小於128B),而且通信雙方會各自維護一個chunkSize,兩端的chunkSize是獨立的。比如當A想向B發送一個200B的Message,但默認的chunkSize是128B,因此就要將該消息拆分為Data分別為128B和72B的兩個chunk發送,如果此時先發送一個設置chunkSize為256B的消息,再發送Data為200B的chunk,本地不再劃分Message,B接受到Set Chunk Size的協議控制消息時會調整的接受的chunk的Data的大小,也不用再將兩個chunk組成為一個Message。
      以下為代表Set Chunk Size消息的chunk的Data:
      技術分享圖片
      其中第一位必須為0,chunk Size占31個位,最大可代表2147483647=0x7FFFFFFF=231-1,但實際上所有大於16777215=0xFFFFFF的值都用不上,因為chunk size不能大於Message的長度,表示Message的長度字段是用3個字節表示的,最大只能為0xFFFFFF。
    • Abort Message(Message Type ID=2):當一個Message被切分為多個chunk,接受端只接收到了部分chunk時,發送該控制消息表示發送端不再傳輸同Message的chunk,接受端接收到這個消息後要丟棄這些不完整的chunk。Data數據中只需要一個CSID,表示丟棄該CSID的所有已接收到的chunk。
      技術分享圖片
    • Acknowledgement(Message Type ID=3):當收到對端的消息大小等於窗口大小(Window Size)時接受端要回饋一個ACK給發送端告知對方可以繼續發送數據。窗口大小就是指收到接受端返回的ACK前最多可以發送的字節數量,返回的ACK中會帶有從發送上一個ACK後接收到的字節數。
      技術分享圖片
    • Window Acknowledgement Size(Message Type ID=5):發送端在接收到接受端返回的兩個ACK間最多可以發送的字節數。
      技術分享圖片
    • Set Peer Bandwidth(Message Type ID=6):限制對端的輸出帶寬。接受端接收到該消息後會通過設置消息中的Window ACK Size來限制已發送但未接受到反饋的消息的大小來限制發送端的發送帶寬。如果消息中的Window ACK Size與上一次發送給發送端的size不同的話要回饋一個Window Acknowledgement Size的控制消息。
      技術分享圖片
      • Hard(Limit Type=0):接受端應該將Window Ack Size設置為消息中的值
      • Soft(Limit Type=1):接受端可以講Window Ack Size設為消息中的值,也可以保存原來的值(前提是原來的Size小與該控制消息中的Window Ack Size)
      • Dynamic(Limit Type=2):如果上次的Set Peer Bandwidth消息中的Limit Type為0,本次也按Hard處理,否則忽略本消息,不去設置Window Ack Size。

4. 不同類型的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 Type Id=4。
———下面對以上7種信息具體介紹———-
4.1 Command Message(命令消息,Message Type ID=17或20)
發送端發送時會帶有命令的名字,如connect,TransactionID表示此次命令的標識,Command Object表示相關參數。接受端收到命令後,會返回以下三種消息中的一種:_result 消息表示接受該命令,對端可以繼續往下執行流程,_error消息代表拒絕該命令要執行的操作,method name消息代表要在之前命令的發送端執行的函數名稱。這三種回應的消息都要帶有收到的命令消息中的TransactionId來表示本次的回應作用於哪個命令。
可以認為發送命令消息的對象有兩種,一種是NetConnection,表示雙端的上層連接,一種是NetStream,表示流信息的傳輸通道,控制流信息的狀態,如Play播放流,Pause暫停。
4.1.1 NetConnection Commands(連接層的命令)
用來管理雙端之間的連接狀態,同時也提供了異步遠程方法調用(RPC)在對端執行某方法,以下是常見的連接層的命令:
4.1.1.1 connect:用於客戶端向服務器發送連接請求,消息的結構如下:

字段類型說明
Command Name(命令名字) String 命令的名字,如”connect”
Transaction ID(事務ID) Number 恒為1
Command Object(命令包含的參數對象) Object 鍵值對集合表示的命令參數
Optional User Arguments(額外的用戶參數) Object 用戶自定義的額外信息

第三個字段中的Command Object中會涉及到很多鍵值對,這裏不再一一列出,使用時可以參考協議的官方文檔。
消息的回應有兩種,_result表示接受連接,_error表示連接失敗

4.1.1.2 Call:用於在對端執行某函數,即常說的RPC:遠程進程調用,消息的結構如下:

字段類型說明
Procedure Name(進程名) String 要調用的進程名稱
Transaction ID Number|如果想要對端響應的話置為非0值,否則置為0
Command Object Object 命令參數
Optional Arguents Object 用戶自定義參數

如果消息中的TransactionID不為0的話,對端需要對該命令做出響應,響應的消息結構如下:

字段類型說明
Command Name(命令名) String 命令的名稱
TransactionID Number 上面接收到的命令消息中的TransactionID
Command Object Object 命令參數
Optional Arguments Object 用戶自定義參數

4.1.1.3 Create Stream:創建傳遞具體信息的通道,從而可以在這個流中傳遞具體信息,傳輸信息單元為Chunk。

字段類型說明
Command Name(命令名) String “createStream”
TransactionID Number 上面接收到的命令消息中的TransactionID
Command Object Object 命令參數
Optional Arguments Object 用戶自定義參數

4.1.2 NetStream Commands(流連接上的命令)

Netstream建立在NetConnection之上,通過NetConnection的createStream命令創建,用於傳輸具體的音頻、視頻等信息。在傳輸層協議之上只能連接一個NetConnection,但一個NetConnection可以建立多個NetStream來建立不同的流通道傳輸數據。
以下會列出一些常用的NetStream Commands,服務端收到命令後會通過onStatus的命令來響應客戶端,表示當前NetStream的狀態。
onStatus命令的消息結構如下:

字段類型說明
Command Name String “onStatus”
TransactionID Number 恒為0
Command Object NULL 對onSatus命令來說不需要這個字段
Info Object Object AMF類型的Object,至少包含以下三個屬性:1,“level”,String類型,可以為“warning”、”status”、”error”中的一種;2,”code”,String類型,代表具體狀態的關鍵字,比如”NetStream.Play.Start”表示開始播流;3,”description”,String類型,代表對當前狀態的描述,提供對當前狀態可讀性更好的解釋,除了這三種必要信息,用戶還可以自己增加自定義的鍵值對

4.1.2.1 play(播放):由客戶端向服務器發起請求從服務器端接受數據(如果傳輸的信息是視頻的話就是請求開始播流),可以多次調用,這樣本地就會形成一組數據流的接收者。註意其中有一個reset字段,表示是覆蓋之前的播流(設為true)還是重新開始一路播放(設為false)。
play命令的結構如下:

字段類型說明
命令名 String “play”
事務ID Number 恒為0
命令參數對象 Null 不需要此字段,設為空
流名稱 String 要播放的流的名稱
開始位置 Number 可選參數,表示從何時開始播流,以秒為單位。默認為-2,代表選取對應該流名稱的直播流,即當前正在推送的流開始播放,如果對應該名稱的直播流不存在,就選取該名稱的流的錄播版本,如果這也沒有,當前播流端要等待直到對端開始該名稱的流的直播。如果傳值-1,那麽只會選取直播流進行播放,即使有錄播流也不會播放;如果傳值或者正數,就代表從該流的該時間點開始播放,如果流不存在的話就會自動播放播放列表中的下一個流
周期 Number 可選參數,表示回退的最小間隔單位,以秒為單位計數。默認值為-1,代表直到直播流不再可用或者錄播流停止後才能回退播放;如果傳值為0,代表從當前幀開始播放
重置 Boolean 可選參數,true代表清除之前的流,重新開始一路播放,false代表保留原來的流,向本地的播放列表中再添加一條播放流

4.1.2.2 play2(播放):和上面的play命令不同的是,play2命令可以將當前正在播放的流切換到同樣數據但不同比特率的流上,服務器端會維護多種比特率的文件來供客戶端使用play2命令來切換。

字段類型說明
Command Name String “play2”
TransactionID Number 恒為0
Command Object NULL,對onSatus命令來說不需要這個字段
parameters Object AMF編碼的Flash對象,包括了一些用於描述flash.net.NetstreamPlayOptions ActionScript obejct的參數

4.1.2.3 deleteStream(刪除流):用於客戶端告知服務器端本地的某個流對象已被刪除,不需要再傳輸此路流。

字段類型說明
Command Name String “deleteStream”
TransactionID Number 恒為0
Command Object NULL,對onSatus命令來說不需要這個字段
Stream ID(流ID) Number 本地已刪除,不再需要服務器傳輸的流的ID

4.1.2.4 receiveAudio(接收音頻):通知服務器端該客戶端是否要發送音頻
receiveAudio命令結構如下:

字段類型說明
Command Name String “receiveAudio”
TransactionID Number 恒為0
Command Object NULL 對onSatus命令來說不需要這個字段
Bool Flag Boolean true表示發送音頻,如果該值為false,服務器端不做響應,如果為true的話,服務器端就會準備接受音頻數據,會向客戶端回復NetStream.Seek.Notify和NetStream.Play.Start的Onstatus命令告知客戶端當前流的狀態

4.1.2.5 receiveVideo(接收視頻):通知服務器端該客戶端是否要發送視頻
receiveVideo命令結構如下:

字段類型說明
Command Name String “receiveVideo”
TransactionID Number 恒為0
Command Object NULL,對onSatus命令來說不需要這個字段
Bool Flag Boolean true表示發送視頻,如果該值為false,服務器端不做響應,如果為true的話,服務器端就會準備接受視頻數據,會向客戶端回復NetStream.Seek.Notify和NetStream.Play.Start的Onstatus命令告知客戶端當前流的狀態

4.1.2.6 publish(推送數據):由客戶端向服務器發起請求推流到服務器。
publish命令結構如下:

字段類型說明
Command Name String “publish”
TransactionID Number 恒為0
Command Object NULL,對onSatus命令來說不需要這個字段
Publishing Name(推流的名稱) String 流名稱|
Publishing Type(推流類型) String “live”、”record”、”append”中的一種。live表示該推流文件不會在服務器端存儲;record表示該推流的文件會在服務器應用程序下的子目錄下保存以便後續播放,如果文件已經存在的話刪除原來所有的內容重新寫入;append也會將推流數據保存在服務器端,如果文件不存在的話就會建立一個新文件寫入,如果對應該流的文件已經存在的話保存原來的數據,在文件末尾接著寫入

4.1.2.7 seek(定位流的位置):定位到視頻或音頻的某個位置,以毫秒為單位。
seek命令的結構如下:

字段類型說明
Command Name String “seek”
TransactionID Number 恒為0
Command Object NULL,對onSatus命令來說不需要這個字段
milliSeconds Number 定位到該文件的xx毫秒處|

4.1.2.8 pause(暫停):客戶端告知服務端停止或恢復播放。
pause命令的結構如下:

字段類型說明
Command Name String “pause”
TransactionID Number 恒為0
Command Object NULL,對onSatus命令來說不需要這個字段
Pause/Unpause Flag Boolean true表示暫停,false表示恢復
milliSeconds Number 暫停或者恢復的時間,以毫秒為單位|

如果Pause為true即表示客戶端請求暫停的話,服務端暫停對應的流會返回NetStream.Pause.Notify的onStatus命令來告知客戶端當前流處於暫停的狀態,當Pause為false時,服務端會返回NetStream.Unpause.Notify的命令來告知客戶端當前流恢復。如果服務端對該命令響應失敗,返回_error信息。

5. 代表流程
5.1 推流流程
技術分享圖片

5.2 播流流程
技術分享圖片

6. 新手建議

如果讀者仔細讀完了上面講的RTMP協議,想必會覺得RTMP協議非常繁瑣,事實也確實是這樣,RTMP協議中充斥著很多冗余的字段,比如三次握手中的時間戳的校對,還有一些特殊的命令,如FCPublish、UnFCPublish等,但在實際實現中為了保證更大兼容性通常還是要處理這些看似多余的命令。加上Adobe對RTMP協議的實現細節有些並沒有按照協議來或者協議中沒有寫清楚自己搞了一套實現,其他應用開發時還要兼容Adobe錯誤的實現,從而使的RTMP也一直為開發者所詬病。但不管怎樣,RTMP確實提供了一種能夠全面並且實現簡單的協議來保證流信息的傳輸,這方面暫時還沒有一種更完善更簡潔的協議能夠取代它在視頻流開發中的地位。
新人一開始接觸RTMP的時候肯定會覺得頭大,這也是RTMP協議不簡潔的後果。建議讀者在學習時先過一遍協議理解大概的概念和流程,然後對照wireshark抓的包,和協議進行比對,這樣將理論和實踐結合,應該會理解的更快一點。

(轉)RTMP協議從入門到放棄