個人翻譯,轉載請注明出處,謝謝!
Adobe's Real Time Messaging Protocol
摘要
本文檔描述了Adobe公司的實時消息協議(RTMP),RTMP是設計用于multiplexing和分組多媒體傳輸流的建立在合適傳輸協議之上(如TCP)的應用層協議。
1. 簡介
RTMP協議基于可靠的流傳輸(如TCP)層提供了一項雙向消息復用服務,目的是可以在兩節點間并行傳輸帶有時間信息的音頻、視頻和數據消息。
RTMP的實現中通常會給不同類別消息分配不同優先級,從而達到在傳輸容量被壓縮時影響進入底層傳輸流中消息的目的。
本文檔描述了RTMP的相關語法和操作。
1.1 術語
文檔中使用的關鍵詞如“MUST”,"MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY" 以及 “OPTIONAL”可在[RFC2119]中找到相應的說明。
2. 定義
- 負載Payload:分組(packet)中包含的數據,如音頻采樣點或已壓縮視頻數據。
- 分組Packet:一個數據分組包含了固定頭(header)和payload數據。部分底層協議可能會需要定義分組的封裝。
- 端口Port: 在給定計算機上區隔多終端的抽象,TCP/IP協議簇使用小的正整數標識不同端口,OSI模型中傳輸層的選擇器和端口有相同的效果。
- 傳輸地址(Transport address):通過組合網絡地址和端口來標識傳輸層終端,格式如IP:TCP Port。分組總是從源傳輸地址到目的傳輸地址流動。
- 消息流(Message stream): 消息傳輸的邏輯通道
- 消息流ID(Message stream ID):ID用于標識消息傳輸的具體消息流
- 塊(Chunk):消息碎片。消息通過網絡發送前分解成小塊并交錯存取。塊保證了所有消息在點對點跨流時保持時間序列傳輸。
- 塊流(Chunk stream): 支持塊定向傳輸的邏輯通道,塊流可以從client到server,反向亦可支持
- 塊流ID(Chunk stream ID):每個塊都有自己的ID,用以標識將在哪個塊流傳輸
- 復用(Multiplexing):將獨立的音視頻數據整合至一個音視頻流中,從而使得可以同步傳輸多個音視頻數據
- 解復用(DeMultiplexing):復用的逆向處理,從流中分解出獨立的音視頻數據
- 遠程過程調用(Remote Procedure Call, RPC): 允許對端Client或Server遠程調用子程序或過程。
- 元數據(Metadata):數據說明,如一部電影的元數據包括電影名稱、時長、創建時間等
- 應用實例(Application Instance):位于server的應用實例,由client端發起連接請求是創建
- 活動消息格式(Action Message Format, AMF):一種緊湊二進制格式,用于格式化活動腳本對象圖(ActionScript object graph)。AMF目前有兩個版本:AMF0 和 AMF3
3. 字節序、字節對齊和時間格式
所有的整型字段都是使用網絡字節序(big-endian)進行傳輸的,該部分在[RFC0791]中有詳細描述。另外,除特殊說明,文檔中所有常數均為十進制格式。
同樣,如無特殊說明,RTMP中所有數據都是字節對齊(byte-aligned)的;舉個例子,一個16位的變量可能會處于奇數偏移位置。當需要補齊時,對應字節應當用用0填充。
RTMP中時間戳是一個整型值,單位為毫秒,它是一個相對值,相對未規定的某一時間點。通常,每個流會從時間戳0開始,但也不是必須的,只要流兩端能夠協商某一起始點即可。
注意,這說明任何需要跨流(尤其是不同主機間)同步必須依賴RTMP以外的其他機制。
由于時間戳32bits長度限制,它會在每49天17時2分47.296秒循環一次。但流通常是會持續地傳輸,甚至長達數年之久,因此RTMP應用需要在處理時間戳時使用serial number arithmetic[RFC1982],而且應當支持回滾處理。
時間戳增量同樣規定為一個無符號整形數,單位毫秒,但相對于時間為上一時間戳。時間戳增量可使用24或32bits長度。
4. RTMP塊流
本章節制定了RTMP塊流細節,它為更高層流媒體協議提供了復用和分組服務。
雖然RTMP塊流設計用于協同RTMP協議工作,但它同樣適用于其他發送消息流的協議。每條消息由時間戳和負載類型標識構成。RTMP塊流和RTMP廣泛適用于各種音視頻應用,既支持“一對一”或“一對多”實時廣播,也支持為會議交互系統提供視頻點播(video-on-demand)服務。
當配合可靠傳輸協議如TCP時,RTMP塊流提供了有保證的時間戳序列的對端跨流消息傳輸。RTMP塊流并未提供任何優先級或相關形式的控制,但可被高層協議用于提供類似功能。例如,一個實時視頻服務器可能會參考每條消息發送和響應的時間,來決定是否要丟棄部分視頻消息以滿足較慢客戶端能夠流暢地接收音頻數據。
RTMP塊流集成了帶內(in-band)協議控制消息,而且為高層協議提供了嵌入自定義控制消息的機制。
4.1 消息格式
消息格式可劃分為塊來支持不同高層協議進行復用。但無論如何,下列字段都是創建塊時所必須的:
- 時間戳Timestamp:消息時間戳,該字段為4個字節
- 長度Length: 消息負載長度,如果消息頭(header)不能被省略,其長度也應計入其中。該字段占用塊頭中的3個字節
- 類型Id:一些類型ID預留用于協議控制消息,這些傳遞信息的消息會同時被RTMP塊流協議和高層協議處理。所有其他的類型ID則由高層協議使用,RTMP塊流會直接忽略這些值。實際上,RTMP塊流并不需要類型ID,所有消息都可以是同一類型,有時候應用可以通過該字段來區隔同步軌。該字段占用塊頭中的1個字節
- 消息流ID:消息流ID可以是任意值,不同的消息流復用到同一塊流,然后再根據各自消息流ID進行解復用。除此之外,別無用處。該字段占用塊頭中的4字節,使用小端法(little-endian)格式。
4.2 握手
RTMP連接始于握手,與其他協議握手不一樣的是,它一共包含了3次固定大小的塊,而不是類似的帶有頭信息的可變大小塊。
客戶端和服務器都會發送同樣的三個塊,具體而言,客戶端發送的三個塊分別為C0, C1和C2;而服務器會發送對應的S0, S1和S2。
4.2.1 握手次序
握手次序是以客戶端發送C0和C1塊開始的。
客戶端必須成功接收到S1后才發送C2,且必須成功接收S2后才可以發送其他數據。
服務器必須成功接收C0后(也可以選擇C1后才發送)才可以發送S0和S1。對S2而言,服務器必須成功接收C1后才可以,最后服務器必須成功接收到C2才可以開始發送其他數據。
4.2.2 C0和S0格式
C0和S0分組都是一個八位數(octet)字段:
各字段含義如下所述:
- Version (8 bits): 在C0中,該字段標識客戶端請求使用的RTMP版本;在S0中,該字段標識服務器選擇使用的RTMP版本。如果服務器不能識別客戶端所請求的版本時,應該回復3。相應的,客戶端可以選擇使用版本3,也可以放棄握手。
本文檔多對應的版本值為3,0 ~ 2是早期產品值,現已不推薦使用。4 ~ 31預留用于將來的實現版本使用;32 ~ 255則為禁止使用值(以便區分RTMP和基于文本的協議,它們總是會以可打印字符開始)。
4.2.3 C1和S1格式
C1和S1分組均長1536個字節,由如下字段組成:
- Time (4 bytes): 該字段包含一個時間戳,它是所有之后發出塊的起始點。它既可以是0值,也可以是其他任意數字。為了同步多個塊流,斷點可能會希望發送其他塊流的當前時間戳。
- Zero (4 bytes):該字段必須為0s
- Random data (1528 bytes):該字段可以包含任意數值。由于每個終端需要區分自身發起的握手還是對端發起的,該字段應當用于發送一些充分隨機數以做標識。但也沒有必要因此而選擇一些加密等級的隨機數,甚至動態數值。
4.2.4 C2和S2格式
C2和S2同樣是1536字節,相當于是S1和C1的回應,均包含如下字段:
- Time (4 bytes): 該字段必須為對端先前發送的塊時間戳,C2中填寫S1的,S2中填寫C1的
- Time2 (4 bytes): 該字段必須包含讀取到前一塊(S1或C1)的時間戳
- Random echo (1528 bytes): 該字段必須為對端發送的隨機數。兩端均可通過time和time2以及當前時間來快速估計出當前連接的帶寬和延時,只有這個字段看起來毫無用處。
4.2.5 握手圖例
下面將主要說明握手圖例中提到的不同狀態:
- Unintialized: 未初始化,協議版本在該階段發送,此時客戶端和服務器均為“未初始化狀態”??蛻舳嗽贑0塊中發送協議版本,如果服務器支持該版本號,則返回S0和S1作為響應。如果不支持,則服務器采取合適的行為作為響應。在RTMP中,該行為通常是關閉連接。
- Version Sent: 版本號已發出,客戶端和服務器的“未初始化狀態”的下一階段都是“版本號已發出”階段。此時客戶端在等待S1塊,而服務器在等待C1塊。一旦成功接收后,客戶端回應C2塊,而服務器回應S2塊。之后便進入了“響應已發出”狀態。
- Ack Sent: 響應已發出,客戶端和服務器均在分別等待S2和C2塊。
- Handshake Done: 握手成功,客戶端和服務器交換消息。
4.3 分塊
握手成功后,連接將會復用一至多個塊流。每個塊流攜帶源自一個消息流中的一類消息。每個塊創建時賦予獨特ID,稱為塊流ID(chunk stream ID)。這些塊經由網絡傳輸,在傳輸時,每個塊必須依次逐個傳遞,接收端收到塊后,會基于塊流ID將塊重組成消息。
分塊機制允許高層協議中的長消息分解為短消息,這樣可以防止長的短優先級消息(如視頻)阻塞高優先級消息(如音頻或控制操作)。
分塊機制還通過將本應包含在消息中的信息壓縮至塊頭中以減小發送短消息的開銷。
塊的大小是可配置的,它還支持通過一系列“塊大小設置消息(Chunk Size control message, section 4.4.1)”進行設置。設置塊長度較大時可以降低CPU使用率,但同樣會由于大量數據寫入而造成低帶寬環境下其他內容的延時。設置塊長度較小時不利于高比特率流傳輸。
塊的大小由兩端單獨控制。
4.3.1 塊格式
每個塊均由頭(header)和數據(data)構成,頭本身又包括下面三部分:
- Basic Header(1 to 3 bytes):基礎頭,該字段編碼了塊流ID和塊類型。塊類型決定了編碼后的消息頭格式,而長度則完全由可變長塊流ID決定。
- Message Header(0, 3, 7 or 11 bytes):該字段編碼了要發送的消息的信息(無論整個發送還是切分后發送)。它的長度由塊頭中的塊類型決定
- Extended Timestamp (0 or 4 bytes):擴展時間戳,該字段是否出現由塊消息頭中的時間戳或時間戳增量字段決定,更多內容可閱讀 Section 4.3.1.3
- Chunk Data(variable size): 塊數據,是快的真正負載,長度最大可為已配置的塊長最大值
4.3.1.1 塊基礎頭
塊基礎頭編碼了塊流ID和塊類型(如下圖的fmt字段所示)。塊類型決定了編碼后消息頭的格式,塊基礎頭長度根據塊流ID可為1,2,或3字節。
塊基礎頭中的0 ~ 5 bit (least significant)代表塊流ID。
實現時應該使用最短的長度來表示ID。
協議最多支持65597個流,流ID分別為3 ~ 65599. ID 0、1和2暫時預留。
- 0值表示2字節格式,其ID值范圍為64 ~ 319 (第二字節+64)
- 1值表示3字節格式,其ID值范圍為64 ~ 65599 (第三字節*256 + 第二字節 + 64)。
- 2值預留給低層協議發送控制消息或命令
值為 3 ~ 63的表示完整流ID。
塊流ID為2 ~ 63時,可編碼入1字節版本:
塊流ID為 64 ~ 319時,可編碼入2字節版本,ID計算式為(第二字節+64):
塊流ID為 64 ~ 65599時,可編碼入3字節版本,ID計算式為(第三字節*256 + 第二字節 + 64):
對應字段說明如下:
- cs id(6 bits): 該字段包含了塊流ID,值為0或1用于標識使用2個還是3個字節來表示該字段
- fmt (2 bits): 該字段標識了塊消息頭中使用的四個格式之一,關于塊消息頭的內容會在下一章節中講到
- cs id - 64 (8 or 16 bits): 該字段包含了塊流ID減去64的值。例如,ID為365時,cs id值為1,且16bits中值為301
塊流ID值為 64 ~ 319可選擇使用2字節或3字節格式進行表示
4.3.1.2 塊消息頭
塊消息頭共有四種格式,由塊基礎頭中的“fmt”字段進行選擇。
實現時應該使用最緊湊的方式來表示每一個塊消息頭。
4.3.1.2.1 類型0
類型0 塊頭共11字節長,該類型必須使用在塊流的開頭和任何流時間戳回滾(如向后seek)的時候。
- timestamp(3 bytes): 對于類型0的塊,這里填寫發送消息時的絕對時間戳。如時間戳大于或等于 16777215(十六進制 0xFFFFFF)時,該字段值必須為16777215,且擴展時間戳必須出現以編碼完整的32位時間戳。除此之外,該字段必須為完整時間戳值。
4.3.1.2.2 類型1
類型1 塊頭長7字節,消息流ID未包含在內,該塊使用和前面塊一致的塊流ID??勺冮L消息的流(如許多視頻格式)應當使用本格式作為每條新消息的第二塊:
4.3.1.2.3 類型2
類型2 塊頭長3字節,消息流ID和長度均未包含在內;該塊使用和前面塊一致的塊流ID和長度值。具有固定長度消息的流(如許多音頻和數據格式)可使用本格式作為每條消息的第二塊:
4.3.1.2.4 類型3
類型3 塊沒有消息頭,流ID、消息長度以及時間戳增量字段都不會呈現;本類型的塊是錢前面塊的流ID。當一條消息分解成塊后,除第一塊外,所有其他塊都該使用此類型(參考示例2,Section 4.3.2.2)。由完全相同大小、流ID和時間間隔的消息流可在開頭的類型2塊后的所有塊中使用類型3塊(參考示例1,Section 4.3.2.1)。如果第二條消息和第一條消息的時間增量與第一條消息的時間戳相同,那么可以在類型0塊后直接跟隨類型3塊,因為沒有必要使用類型2塊說明時間增量。也就是說,如果類型3塊緊跟類型0塊,那么該類型3塊的時間增量是和類型0塊一致的。
4.3.1.2.5 常用消息頭變量
下面列出塊消息頭中的每個變量說明:
- 時間戳增量(3字節):用于類型1和類型2塊,標識與前一塊時間戳的差值。如果增量大于或等于16777215(十六進制 0xFFFFFF),該變量必須為16777215,以說明將同時使用到擴展時間戳以完成32位完整增量的編碼。除此之外,該變量應該等于實際差值。
- 消息長度(3字節):用于類型0和類型1塊,標識消息長度。注意該變量通常和塊負載長度不同。The chunk payload length is the maximum chunk size for all but the last chunk, and the remainder (which may be the entire length, for small messages) for the last chunk.
- 消息類型ID(1字節):用于類型0和類型1塊,標識當前發送消息的類型。
- 消息流ID(4字節):用于類型0塊,標識存儲的消息流ID。消息流ID使用little-endian格式存儲。通常,同一塊流中的所有消息消息流ID相同,但將多個消息流復用到同一塊流中也是可以的,不過這樣就會失去可使用塊頭壓縮而帶來的好處。不過當一個消息流關閉且另外一個緊接著打開時,也沒有理由不通過發送新的類型0塊對已經存在的塊流進行復用。
4.3.1.3 擴展時間戳
擴展時間戳用于編碼大于16777215(十六進制0xFFFFFF)的時間戳或時間戳增量,即用于類型0,1,2塊中不適合24位變量的時間戳或時間戳增量。該變量編碼了完整的32位時間戳或時間戳增量。該變量的出現意味著塊0中時間戳或塊1/2中時間戳增量值為16777215(0xFFFFFF)。該變量會在當最近的具有相同塊流ID類型0/1/2塊中出現擴展時間戳變量時,出現在緊跟其后的類型3塊中。
4.3.2 示例
4.3.2.1 示例1
下面是一個簡單的音頻消息流示例,例子中示范了消息的冗余。
下表中顯示流中對應產生的塊,從消息3開始,數據傳輸做了最優化,每條消息前僅添加了一個字節:
4.3.2.2 示例2
下面舉例說明了當消息太長而無法放入一個128字節的塊時,分解成多個塊進行傳輸。
下面是對應產生的塊:
塊1消息頭中指明了消息總長307字節。
注意:
在上面兩個示例中,類型3塊可被用于兩種用途。
- 標識為一個消息的連續
- 標識一個新消息的開始,該塊沿用已存在的頭信息
4.4 協議控制消息
RTMP塊流可使用消息類型ID為1,2,3,5和6來發送協議控制消息,這些消息包含了RTMP塊流協議所必需的信息。
這些協議控制消息必須使用消息流ID 0(又稱為控制流)且在塊流ID 2中發送。協議控制消息收到時立即生效,他們的時間戳會被直接忽略。
4.4.1 設置塊大?。?)
協議控制消息1是設置塊大小,用于通知對端更新最大塊大小。
最大塊大小默認為128字節,但是客戶端或服務器都可以修改該值,然后通過消息通知對端更新。例如,假設一個客戶端希望發送131字節的音頻數據,而此時塊大小為128字節,這時候客戶端就可以發送消息1修改塊大小為131字節,然后客戶端就可以在單個塊中發送音頻數據了。
最大塊大小應當至少為128字節,極端情況下,必須大于1字節。最大塊字節可由任意方向單獨維護。
0: 該位必須為0
塊大小(31位):該處保存最大塊大小值,單位為字節,將用于之后所有發送的塊直到有新的通知位置。有效大小是1到2147483647(0x7FFFFFFF),事實上,所有大于16777215(0xFFFFFF)的值都是等價的,因為沒有塊可以大于消息長度。
4.4.2 取消消息(2)
協議控制消息2是取消消息,用于通知對端如在等待某一消息的剩余塊,請忽略該消息中所有已收到塊。對端接收作為協議消息負載的塊流ID值。應用可能在關閉時發送該消息來告知對端該消息的無需進行后續處理。
塊流ID(32位):該變量保存塊流ID,標識那條消息將被丟棄
4.4.3 確認(3)
客戶端或服務器必須在接收窗口大小(window size)的數據后發送確認消息,窗口大小是發送端在收到接收端確認前可發送的最大數據字節數。該消息指明了序列數值,標識當前所收到字節數。
序列數值(32位):該變量保存目前收到所有字節數
4.4.4 窗口確認大?。?)
客戶端或服務器通過發送該消息來通知對端發送確認消息的間隔窗口大小。發送端會在發送窗口大小的字節數后等待確認消息,而接收端必須在繼最近一次發送確認消息后收到指定數值的字節后再次發送確認消息。
4.4.5 設置對端帶寬(6)
客戶端或服務器發送該消息來限制對端輸出帶寬,對端接收到該消息后會通過限制窗口大小來實現限制帶寬。如對端窗口大小與帶寬值不同,則需響應一條窗口確認大小消息。
限制類型是下列值之一:
- 0 - 硬限制:對端必須限制輸出帶寬為所述窗口大小
- 1 - 軟限制:對端必須限制輸出帶寬為所述窗口大小或已生效窗口大小中的更小值
- 2 - 動態:除非上一條消息中限制類型為應限制,則需要把該消息視為硬限制,否則可直接忽略
5. RTMP消息格式
本章節指明通過底層傳輸層(如RTMP塊流)在網絡端點間傳輸的RTMP消息格式。
雖然RTMP設計用于RTMP塊流,但它也可使用其他傳輸協議發送消息。RTMP塊流和RTMP廣泛適用于音視頻應用,如一對一、一對多的實時廣播、點播以及交互會以應用。
5.1 RTMP消息格式
客戶端和服務器通過在網絡中發送RTMP消息來進行交互,這些消息可包含音頻、視頻、數據或其他消息。
RTMP消息分為兩部分,分別為頭和負載。
5.1.1 消息頭
消息頭包含如下部分:
- 消息類型:一個字節變量,用于標識消息類型。消息類型1-6預留用于協議控制消息
- 長度:3字節變量,用于標識負載字節數。設置為big-endian格式
- 時間戳:4字節變量,包含消息時間戳。以big-endian格式打包
- 消息流ID:3字節變量,標識消息流。設置為big-endian格式
5.1.2 消息負載
消息的另一部分,是消息的真正數據。如可為音頻樣本或壓縮后視頻數據。負載格式和解釋不在本文檔討論范圍內。
5.2 用戶控制消息(4)
RTMP中息類型ID 4為用戶控制消息,這些消息包含了RTMP流傳輸層使用的信息,協議消息ID為1/2/3/5/6則用于RTMP塊流協議(Section 4.4)。
用戶控制消息應該使用消息流ID 0(又稱為控制流),且通過RTMP塊流傳輸時使用塊流ID 2。用戶控制消息收到時立即生效,可直接忽略其時間戳。
客戶端或服務器發送該消息來通知對端用戶控制事件,該消息將攜帶事件類型和事件內容。
消息內容中的前兩個字節表示事件類型,事件內容緊跟其后。事件內容長度是可變的,因此,為了讓這些消息順利通過RTMP塊流層進行傳輸,最大塊大?。⊿ection 4.4.1)應當足夠大以使得該消息可以單塊傳輸。
事件類型和事件內容格式將在Section 6.1.7中列出。
6. RTMP命令消息
本章節討論了服務器和客戶端通過網絡進行通訊中使用的不同類型的消息和命令。
不同類型的消息被服務器和客戶端進行交換,主要有用于發送音頻數據的音頻消息,發送視頻數據的視頻消息,發送任何用戶數據的數據消息,共享對象消息,以及命令消息。共享對象消息提供了在多個客戶端和服務器間管理分布式數據的通用方法。命令消息在客戶端和服務器間攜帶AMF編碼命令。一個客戶端或服務器可通過在流中發送命令消息向對端申請RPC(Remote Procedure Calls)。
6.1 消息類型
服務器和客戶端通過網絡發送消息進行通訊,消息類型可以使音頻消息、視頻消息、命令消息、共享對象消息、數據消息和用戶控制消息的任意一種。
6.1.1 命令消息(20, 17)
命令消息在客戶端和服務期間攜帶AMF編碼命令,這些消息中AMF0編碼已被指定類型為20,AMF3編碼已被指定消息類型為17。這些消息發送用于在對端執行一些操作如連接、創建流、發布publish、播放、暫停。命令消息如onstatus, result等用于通知發送端所請求命令的狀態。一個命令消息包含命令名稱、辦理transaction ID,以及包含相關參數的命令對象??蛻舳嘶蚍掌骺梢酝ㄟ^發送命令消息至對端以請求RPC。
6.1.2 數據消息(18,15)
客戶端或服務器通過該消息發送任何元數據Metadata或其他用戶數據到對端。元數據包含數據(音視頻等)細節如創建時間、時長、主題等等。這些消息中AFM0編碼的被指定類型值為18,AMF3編碼的指定類型為15。
6.1.3 共享對象消息(19, 16)
共享對象是一個Flash對象(一系列名字-值對),它用于多客戶端、實例間同步。預留給共享對象事件中AMF0編碼的為類型19,AMF3編碼的為類型16。每條消息可包含多個事件。
目前支持如下事件類型:
6.1.4 音頻消息(8)
客戶端或服務器通過發送該消息來發送音頻數據到對端,音頻消息對應消息類型為8。
6.1.5 視頻消息(9)
客戶端或服務器通過發送該消息來發送視頻數據到對端,視頻消息對應消息類型為9。
6.1.6 聚合消息(22)
聚合消息時指一條消息中包含了一系列使用Section5.1中描述格式RTMP子消息,對應消息類型為22。
聚合消息的消息流ID覆蓋了內部子消息的消息流ID。
聚合消息和第一條子消息在時間戳上的區別在于用于歸一化至流時間stream timescale的偏移量offset。偏移量會被加至每條子消息的時間戳來達成歸一化,第一條子消息的時間戳應該和聚合消息時間戳是一致的,所以偏移量應當是0。
后置指針處包含了前一條消息(包括頭)的大小,主要用于支持FLV的逆向seek操作。
使用聚合消息有如下好處:
- 塊流可在一塊中發送至少一條完整消息,這樣一來,增加塊大小并使用聚合消息就可減少需發送的塊數
- 子消息可在內存中連續存儲,這在系統調用通過網絡發送數據時會更加高效
6.1.7 用戶控制消息事件
客戶端和服務器發送該消息來通知對端用戶控制事件。關于該消息格式可參照Section 5.2。
目前支持如下用戶控制事件類型:
6.2 命令類型
客戶端和服務器交換AMF編碼的命令。發送端發送一條命令消息,其中包含了命令名稱、處理ID、以及含有相關參數的命令對象。例如,連接命令消息包含了’app'參數,以告知服務器客戶端希望連接的目標程序。接收端處理這條命令并回復含有同樣處理ID的響應?;貜偷淖址赡転開result、_error或方法名。如verifyClient或contactExternalServer.
_result或_error的命令字符創代表一條響應,處理ID則表明回復是針對哪條命令的,這在IMAP或其他協議中是完全相同的。命令字符串中的方法名表明發送端希望運行接收端上的一個方法。
下列類對象通常用于發送各種不同的命令:
- NetConnection 一個服務器和客戶端之間連接的高層表現對象
- NetStream 一個音頻流、視頻流及其他相關數據傳輸同感到的表現對象,我們也會發送如播放、暫停等命令來控制數據流動
6.2.1 NetConnection命令
NetConection管理著一個客戶端程序和服務器之間的雙向連接,除此之外,它還提供了對異步遠程方法調用的支持。
下列命令可通過NetConnection進行發送:
- connect
- call
- close
- createStream
6.2.1.1 connect
客戶端發送connect命令至服務器端以請求連接至某一服務器程序實例。
該由客戶端發送至服務器的命令結構如下:
下面是connect命令中命令對象所使用到的名-值對:
audioCodecs屬性有如下標志值:
videoCodecs屬性有如下標志值:
videoFunction屬性有如下標志值:
object Encoding屬性有如下標志值:
由服務器發送至客戶端的命令結構如下:
命令執行期間消息流動如下:
- 客戶端發送connect命令至服務器以請求連接至服務器端程序實例
- 在收到連接命令后,服務器端發送協議消息'Window Acknowledgement Size'給客戶端。同時,服務器端還會連接收到connect命令中提到的應用
- 服務器端發送協議消息‘Set Peer Bandwidth’至客戶端
- 客戶端成功處理‘Set Peer Bandwidth’后發送協議消息‘Window Acknowledgement Size'給服務器端
- 服務器端發送另一類型為用戶控制消息(StreamBegin)協議消息給客戶端
- 服務器端發送命令消息以通知客戶端連接狀態(success/fail)。該命令中含有處理ID(與1中收到相同),該消息同時還制定了部分屬性,如Flash Media Server版本(string)。除此之外,它還指定了連接響應相關的信息如level(string),code(string),description(string),objectencoding(number),等
6.2.1.2 Call
NetConnection對象的call方法用于遠程調用接收端上的程序。需要遠程調用的程序名稱通過一個參數傳遞給call命令。
發送端至接收端的命令結構如下:
響應的命令結構如下:
6.2.1.3 createStream
客戶端發送該命令至服務器端以創建一條邏輯通道用于傳遞消息,從而可以利用已創建的流通道發布音頻、視頻和元數據。
NetConnection是默認的通訊通道,流ID為0。協議和一些命令消息,包括createStream,使用默認通訊通道。
從客戶端至服務器的命令結構如下:
從服務器至客戶端的命令結構如下:
6.2.2 NetStream命令
NetStream定義了一條基于NetConnection的客戶端至服務器間連接的,可以傳遞音頻流、視頻流以及消息流的通道。NetConnection對象支持多個NetStreams以傳輸多個數據流。
客戶端可在NetStream中發送下列命令至服務器:
- play
- play2
- deleteStream
- closeStream
- receiveAudio
- receiveVideo
- publish
- seek
- pause
服務器端通過“onStatus"命令發送NetStream的狀態更新至客戶端:
6.2.2.1 play
客戶端發送該命令值服務器端以播放一個流。多次調用該命令也可創建一個播放清單。
如果你希望創建一個在不同live或recorded流間切換的動態播放清單,需要多次調用play并傳遞false以避免每次reset。相反地,如果你希望立即播放某一指定流,傳遞true以清除等待播放嘟列中的所有其他流。
客戶端發送至服務器的命令結構如下:
命令執行期間消息流動如下:
- 客戶端在接收到來自服務器createStream成功回復后發送play命令
- 收到play命令后,服務器發送一條協議消息來設置塊大小
- 服務器發送另一條協議消息(用戶控制)來指定事件“StreamIsRecord”和流ID。該消息在前2字節中攜帶事件類型,并在最后4字節中攜帶流ID
- 服務器發送另一條協議消息(用戶控制)來指定事件“StreamBegin”來告知客戶端流狀態的開始
- 如客戶端請求的play命令成功執行后,服務器發送一條onStatus命令消息NetStream.Play.Start和NetStream.Play.Reset。只有客戶端發送的play命令中包含reset標志時服務器才會回復NetStream.Play.Reset。如未查找到客戶端請求play的流,服務器將在onStatus消息中返回NetStream.Play.StreamNotFound.
6.2.2.2 Play2
與play命令不同的是,Play可以在不改變正在播放內容的時間線情況下切換至不同比特率流。實質上是服務器端維護了所支持的所有不同比特率的文件,這些文件都可被客戶端通過play2進行請求。
從客戶端到服務器的命令結構如下:
NetStreamPlayOptions對象的公共屬性在ActionScript3 Language Reference[AS3]中有詳細描述。
該命令的具體消息流如下:
6.2.2.3 deleteStream
NetStream對象銷毀前會發出deleteStream命令。
客戶端至服務器的命令結構如下:
服務器端不會進行任何回復。
6.2.2.4 receiveAudio
NetStream發送receiveAudioMessage以告知服務器是否需要發送音頻至客戶端。
由客戶端發送至服務器的命令結構如下:
當receiveAudio命令中bool標志為false時,服務器不會進行任何響應;如該標志為true,服務器會響應以狀態消息NetStream.Seek.Notify和NetStream.Play.Start
6.2.2.5 receiveVideo
NetStream發送receiveVideo消息來告知服務器是否需要發送視頻至客戶端。
由客戶端發送至服務器端的命令結構如下:
如receiveVideo命令中bool標志為false時,服務器不進行任何響應;如該標志為true,服務器會響應以NetStream.Seek.Notify和NetStream.Play.Start
6.2.2.6 publish
客戶端發送publish命令以發布一個命名流至服務器。通過這個名字,任何客戶端可以播放該流并接收已發布的音頻、視頻及數據消息。
從客戶端至服務器的命令結構如下:
服務器響應onStatus命令以標識發布的開始。
6.2.2.7 seek
客戶端發送seek命令以在媒體文件或播放列表中查找指定偏移量(單位為毫秒)。
從客戶端至服務器的命令結構如下:
當seek成功后,服務器響應以一條status消息NetStream.Seek.Notify。如失敗則返回一條_error消息。
6.2.2.8 pause
客戶端發送pause命令來告知服務器暫?;蜷_始播放。
從客戶端至服務器的命令結構如下:
操作成功后,如流為停止狀態,服務器響應以一條狀態消息NetStream.Pause.Notify;如流為未停止狀態,則返回NetStream.UnPause.Notify。如操作失敗,則返回_error消息。
6.3 消息交換示例
下面是一些使用RTMP進行消息交換的示例。
6.3.1 發布錄制的視頻
本示例演示了一個publisher如何發布一個流并傳輸視頻至服務器,其他客戶端可進行訂閱至已發布流并播放視頻。
6.3.2 廣播一個共享對象消息
本示例演示了一個共享對象創建和改變期間的消息交換過程,它同樣描述了共享對象消息廣播的處理過程。
7.3.3 發布已錄制流中元數據
本示例描述了發布元數據時消息交換。