本文聚焦 RTMP 協議的最精華的內容,接進行實際操做 Buffer 的練習和協議的學習。
RTMP 是什麼
RTMP 全稱便是 Real-Time Messaging Protocol。顧名思義就是用來做爲實時通訊的一種協議。該協議是 Adobe 搞出來的。主要是用來傳遞音視頻流的。它經過一種自定義的協議,來完成對指定直播流的播放和相關的操做。和現行的直播流相比,RTMP 主要的特色就是高效,這裏,我就很少費口舌了。咱們先來了解一下 RTMP 是如何進行握手的。
RTMP 握手
RTMP 是基於 TCP 三次握手以後的,因此,RTMP 不是和 TCP 一個 level 的。它自己是基於 TCP 的可靠性鏈接。RTMP 握手的方式如圖:
(C 表明 Client,S 表明 Server)
它主要是經過兩端的字段內容協商,來完成可信度認證的。基本過程以下:
-
client: 客戶端須要發 3 個包。C0,C1,C2
-
server: 服務端也須要發一樣 3 個包。 S0,S1,S2。
【2】 服務端在接受到 C0,發送 S0,S1 包。也能夠等到接受到 C1 以後再一塊兒發送,C1 包的等待不是必須的。
【3】客戶端接受到 S1/S0 包後,發送 C2 包。
【4】服務端接受到 C2 包後,返回 S2 包,而且此時握手已經完成。
不過,在實際應用中,並非嚴格按照上面的來。由於 RTMP 並非強安全性的協議,因此,S2/C2 包只須要 C1/S1 中的內容,就能夠完成內容的拼接。
接下來,咱們來具體看看 C/S 012 包分別表明什麼。
C0 && S0
C0 和 S0 其實區別不大,我這裏主要講解一下 C0,就差很少了。首先,C0 的長度爲 1B。它的主要工做是肯定 RTMP 的版本號。
C1 && S1
C1/S1 長度爲 1536B。主要目的是確保握手的惟一性。格式爲:
C2 && S2
C2/S2 的長度也是 1536B。至關於就是 S1/C1 的響應值。上圖也簡單說明了就是,對應 C1/S1 的 Copy 值,不過第二個字段有區別。基本格式爲:
這裏須要說起的是,RTMP 默認都是使用 Big-Endian 進行寫入和讀取,除非強調對某個字段使用 Little-Endian 字節序。
上面握手協議的順序也是根據其中相關的字段來進行制定的。這樣,看起來很容易啊哈,可是,咱們並不只僅停留在瞭解,而是要真正的瞭解,接下來,咱們來實現一下,若是經過 Buffer 來進行 3 次握手。這裏,咱們做爲 Client 端來進行請求的發起,假設 Server 端是按照標準進行發送便可。
Buffer 實操握手
咱們使用 Buffer 實操主要涉及兩塊,一個塊是 request server 的搭建,還有一塊是 Buffer 的拼接。
Request Server 搭建
這裏的 Server 是直接使用底層的 TCP 鏈接。
const client = new net.Socket();
client.connect({
port: 1935,
host: "6721.myqcloud.com"},
()=>{
console.log("connected");
});
client.on('data',(data)=>{
client.write('hello');
});複製代碼
不過,爲了更好的進行實際演練,咱們經過 EventEmitter 的方式,來作一個篩選器。這裏,咱們使用
mitt 模塊來作代理。
const Emitter = require('mitt')();複製代碼
而後,咱們只要分析的就是將要接受到的 S0/1/2 包。根據上面的字節包圖,能夠清楚的知道包裏面的詳細內容。這裏,爲了簡單起見,咱們排除其餘協議的包頭,只是針對 RTMP 裏面的包。並且,咱們針對的只有 3 種包,S0/1/2。爲了達到這種目的,咱們須要在 data 時間中,加上相應的鉤子才行。
這裏,咱們借用 Now 直播的 RTMP 流來進行相關的 RTMP 直播講解。
Buffer 操做
Server 的搭建其實上網搜一搜,應該均可以搜索出來。關鍵點在於,如何針對 RTMP 的實操握手進行 encode/decode。因此,這裏,咱們針對上述操做,來主要講解一下。
咱們主要的工做量在於如何構造出 C0/1/2。根據上面格式的描述,你們應該能夠清楚的知道 C0/1/2 裏面的格式分別有啥。
好比,C1 中的 time 和 random,其實並非必須字段,因此,爲了簡單起見,咱們能夠默認設爲 0。具體代碼以下:
class C {
constructor() {
this.time;
this.random;
}
C0() {
let buf = Buffer.alloc(1);
buf[0] = 3;
return buf;
}
C1() {
let buf = Buffer.alloc(1536);
return buf;
}
/**
* write C2 package
* @param {Number} time the 4B Number of time
* @param {Buffer} random 1528 byte
*/
produceC2(){
let buf = Buffer.alloc(1536);
// leave empty value as origin time
buf.writeUInt32BE(this.time, 4);
this.random.copy(buf,8,0,1528);
return buf;
}
get getC01(){
return Buffer.concat([this.C0(),this.C1()]);
}
get C2(){
return this.produceC2();
}
}複製代碼
接下來,咱們來看一下,結合 server 完成的 RTMP 客戶端服務。
const Client = new net.Socket();
const RTMP_C = new C();
Client.connect({
port: 1935,
host: "6721.liveplay.myqcloud.com"
}, () => {
console.log('connected')
Client.write(RTMP_C.getC01);
});
Client.on('data',res=>{
if(!res){
console.warn('received empty Buffer ' + res);
return;
}
// start to decode res package
if(!RTMP_C.S0 && res.length>0){
RTMP_C.S0 = res.readUInt8(0);
res = res.slice(1);
}
if(!RTMP_C.S1 && res.length>=1536){
RTMP_C.time = res.readUInt32BE(0);
RTMP_C.random = res.slice(8,1536);
RTMP_C.S1 = true;
res = res.slice(1536);
console.log('send C2');
Client.write(RTMP_C.C2);
}
if(!RTMP_C.S2 && res.length >= 1536){
RTMP_C.S2 = true;
res = res.slice(1536);
}
})複製代碼
RTMP 基本架構
RTMP 整個內容,除了握手,其實剩下的就是一些列圍繞 type id 的 message。爲了讓你們更清楚的看到整個架構,這裏簡單陳列了一份框架:
在 Message 下的 3 個一級子 Item 就是咱們如今將要大體講解的內容。
能夠看到上面全部的 item 都有一個共同的父 Item–Message。它的基本結構爲:
下面,咱們先了解一下 Header 和不一樣 typeID 的內容:
Header
RTMP 中的 Header 分爲 Basic Header 和 Message Header。須要注意,他們二者並非獨立的,而是相互聯繫。Message Header 的結構由 Basic Header 的內容來決定。
Basic Header
BH(基礎頭部)主要是定義了該 chunk stream ID 和 chunk type。須要注意的是,BH 是變長度的,即,它的長度範圍是 1-3B。怎麼講呢?就是根據不一樣的 chunk stream ID 來決定具體的長度。CS ID(Chunk Stream ID)自己的支持的範圍爲 <= 65597 ,差很少爲 22bit。固然,爲了節省這 3B 的內容。 Adobe 搞了一個比較繞的理論,即,經過以下格式中的 CS ID 來肯定:
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|fmt| cs id |
+-+-+-+-+-+-+-+-+複製代碼
即,經過 2-7 bit 位來肯定整個 BH 的長度。怎麼肯定呢?
RTMP 規定,CS ID 的 0,1,2 爲保留字,你在設置 CS ID 的時候只能從 3 開始。
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt| 0 | cs id - 64 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+複製代碼
注意上面的 cs id - 64。這個表明的就是,你經過切割第二個 byte 時,是將獲得的值加上 64。即:2th byte + 64 = CS ID
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|fmt| 1 | cs id - 64 |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+複製代碼
固然,後面 CS ID 的計算方法也是最後的結果加上 64。
0 1 2 3 4 5 6 7
+-+-+-+-+-+-+-+-+
|fmt| cs id |
+-+-+-+-+-+-+-+-+複製代碼
最後強調一下,由於 RTMP 規定,CS ID 的 0,1,2 爲保留字,因此,0,1,2 不做爲 CS ID。綜上所述,CS ID 的起始位爲 3(並不表明它是 3 個 Stream)。
上面我並無提到 fmt 字段,這實際上是用來定義 Message Header 的。
Message Header
根據前面 BH 中 fmt 字段的定義,能夠分爲 4 種 MH(Message Header)。或者說,就是一種 MH 格式會存在從繁到簡 4 種:
當 fmt 爲 0 時,MH 的長度爲 11B。該類型的 MH 必需要流的開頭部分,這包括當進行快退或者點播時從新獲取的流。該結構的總體格式以下:
也就是說,當 fmt 爲 0 時,其格式是一個完整的 MH。
-
timestamp 是絕對時間戳。用來表明當前流編碼。
-
message length: 3B, 發送 message 的長度。
-
type id: 1B
-
stream id: 4B, 發送 message stream id 的值。是 little-endian 寫入格式!
當 fmt 爲 1 時,MH 的長度爲 7B。該類型的 MH 不帶 msg stream id。msg stream id 由前面一個 package 決定。該數值主要由前一個 fmt 爲 0 的 MH 決定。該類型的 MH 一般放在 fmt 爲 0 以後。
當 fmt 爲 2 時,MH 的長度爲 3B。該類型的 MH 只包括一個 timestamp delta 字段。其它的信息都是依照前面一個其餘類型 MH 決定的。
當 fmt 爲 3時,這其實 RTMP 裏面就沒有了 MH。官方定義,該類型主要所有都是 payload 的 chunk,其 Header 信息和第一個非 type:3 的頭一致。由於這主要用於 chunk 中,這些 chunk 都是從一個包裏面切割出來的,因此除了第一個 chunk 外,其它的 chunk 均可以採用這種格式。當 fmt 爲 3時,計算它的 timestamp 須要注意幾點,若是前面一個 chunk 裏面存在 timestrameDelta,那麼計算 fmt 爲 3 的 chunk 時,就直接相加,若是沒有,則是使用前一個 chunk 的 timestamp 來進行相加,用代碼表示爲:
prevChunk.timeStamp += prevChunk.timeStampDelta || prevChunk.timeStamp;複製代碼
不過,當 fmt: 3 的狀況通常很難遇到。由於,他要求前面幾個包必須存在 fmt 爲 0/1/2 的狀況。
Message Body
上面說的主要是 Message Header 的公用部分,可是,對於具體的 RTMP Message 來講,裏面的 type 會針對不一樣的業務場景有不一樣的格式。Message 所有內容如上圖所示:
這裏,咱們根據流程圖的一級子 item 來展開講解。
PCM
PCM 全稱爲:Protocol Control Messages(協議控制消息)。主要使用來溝通 RTMP 初始狀態的相關鏈接信息,好比,windows size,chunk size 等。
PCM 中一共有 5 種不一樣的 Message 類型,是根據 Header 中的 type ID 決定的,範圍是 1~6 (不包括 4)。另外,PCM 在構造的時候須要注意,它 Heaer 中的 message stream id 和 chunk stream id 須要設置爲固定值:
-
message stream ID 爲 0
-
chunk stream ID 爲 2
Set Chunk Size(1)
看名字你們應該都能猜到這類信息是用來幹啥的。該類型的 PCM 就是用來設置 server 和 client 之間正式傳輸信息的 chunk 的大小,type ID 爲 1。那這有啥用呢?
SCS(Set Chunk Size) 是針對正式發送數據而進行數據大小的發送限制。通常默認爲 128B。不過,若是 server 以爲過小了,想發送更大的包給你,好比 132B,那麼 server 就須要給你發送一個 SCS,告知你,接下來「我發送給你的數據大小是 132B」。
Abort Message(2)
該類 PCM 是用來告訴 client,丟棄指定的 stream 中,已經加載到一半或者還未加載完成的 Chunk Message。它須要指定一個 chunk stream ID。
Acknowledgement(3)
該協議信息其實就是一個 ACK 包,在實際使用是並無用到,它主要是用來做爲一個 ACK 包,來表示兩次 ACK 間,接收端所能接收的最大字節數。
Window Acknowledgement Size(5)
這是用來協商發送包的大小的。這個和上面的 chunk size 不一樣,這裏主要針對的是客戶端可接受的最大數據包的值,而 chunk size 是指每次發送的包的大小。也能夠叫作 window size。通常電腦設置的大小都是 500000B。
Set Peer Bandwidth(6)
這是 PCM 中,最後一個包。他作的工做主要是根據網速來改變發送包的大小。它的格式和 WAS 相似,不事後面帶上了一個 Type 用來標明當前帶寬限制算法。當一方接收到該信息後,若是設置的 window size 和前面的 WAS 不一致,須要返回一個 WAS 來進行顯示改變。
-
0: Hard,表示當前帶寬須要和當前設置的 window size 匹配
-
1: Soft,將當前寬帶設置爲該信息定義的 window size,或者已經生效的 window size。主要取決於誰的 window size 更小
-
2: Dynamic,若是前一個 Limit Type 爲 Hard 那麼,繼續使用 Hard 爲基準,不然忽略該次協議信息。
UCM
全稱爲:User Control Message(用戶控制信息)。它的 Type ID 只能爲 4。它主要是發送一些對視頻的控制信息。其發送的條件也有必定的限制:
-
msg stream ID 爲 0
-
chunk stream ID 爲 2
UCM 根據 Event Type 的不一樣,對流進行不一樣的設置。它的 Event Type 一共有 6 種格式 Stream Begin(0),Stream EOF(1),StreamDry(2),SetBuffer Length(3),StreamIs Recorded(4),PingRequest(6),PingResponse(7)。
這裏,根據重要性劃分,只介紹 Begin,EOF,SetBuffer Length 這 3 種。
-
Stream EOF: Event Type 爲 1。它經常出如今,當音視頻流已經所有傳輸完時。 Event Data 爲 4B,用來表示已經發送完音視頻流的 Stream ID(實際沒啥用)。
-
Set Buffer Length: Event Type 爲 3。它主要是爲了通知服務端,每毫秒用來接收流中 Buffer 的大小。Event Data 的前 4B 表示 stream ID,後面 4B 表示每毫秒 Buffer 的大小。一般爲 3000ms
OK 剩下就是 Command Msg 裏面的內容了。
Command Msg
Command Msg 裏面的內容,其 type id 涵蓋了 8~22 之間的值。具體內容,能夠參考下表:
須要注意,爲何有些選項裏面有兩個 id,這主要和 AMF 版本選擇有關。第一個 ID 表示 AMF0 的編解碼方式,第二個 ID 表示 AMF3 的編解碼方式。 其中比較重要的是 command Msg,video,audio 這 3 個 Msg。爲了讓你們更好的理解 RTMP 流的解析,這裏,先講解一下 video 和 audio 兩個 Msg。
Video Msg
由於 RTMP 是 Adobe 開發的。理所固然,內部的使用格式確定是 FLV 格式。不過,這和沒說同樣。由於,FLV 格式內部有不少的 tag 和相關的描述信息。那麼,RTMP 是怎麼解決的呢?是直接傳一整個 FLV 文件,還自定義協議來分段傳輸 FLV Tag 呢?
這個其實很好回答,由於 RTMP 協議是一個長鏈接,若是是傳整個 FLV 文件,根本不必用到這個,並且,RTMP 最經常使用在直播當中。直播中的視頻都是分段播放的。綜上所述,RTMP 是根據本身的自定義協議來分段傳輸 FLV Tag 的。那具體的協議是啥呢?
這個在 RTMP 官方文檔中其實也沒有給出。它只是告訴咱們 Video Msg 的 type ID 是 9 而已。
由於,RTMP 只是一個傳輸工具,裏面傳什麼仍是由具體的流生成框架來決定的。因此,這裏,我選擇了一個很是具備表明性的 RTMP 直播流來進行講解。
經過 wireshark 抓包,能夠捕獲到如下的 RTMP 包數據:
這裏須要說起一點,由於 RTMP 是主動將 Video 和 Audio 分開傳輸,因此,它須要交叉發佈 Video 和 Audio,以保證音視頻的同步。那麼具體每一個 Video Data 裏面的數據都是同樣的嗎?
若是看 Tag 的話,他們傳輸的都是 VideoData Tag。先看一下 FLV VideoData Tag 的內容:
這是 FLV Video 的協議格式。但,遇到第一個字段 FrameType 的時候,咱們就可能懵逼了,這 TM 有 5 種狀況,難道 RTMP 會給你 5 種不一樣的包嗎?
答案是,有可能,可是,很大狀況下,咱們只須要支持 1/2 便可。由於,視頻中最重要的是 I 幀,它對應的 FrameType 就是 1。而 B/P 則是剩下的 2。咱們只要針對 1/2 進行軟解,便可實現視頻全部信息的獲取。
因此,在 RTMP 中,也主要(或者大部分)都是傳輸上面兩種 FrameType。咱們經過實際抓包來說解一下。
這是 KeyFrame 的包,注意 Buffer 開頭的 17 數字。你們能夠找到上面的 FrameType 對應找一找,看結果是否是一致的:
這是 Inter-frame 的包。同上,你們也能夠對比一下:
Audio Tag
Aduio Tag 也是和 Video Tag 同樣的蜜汁數據。經過觀察 FLV Audio Tag 的內容:
上面這些字段全是相關的配置值,換句話說,你必須實現知道這些值才行。這裏,RTMP 發送 Audio Tag 和 Video Tag 有點不一樣。由於 Audio Tag 已經不可能再細分爲 Config Tag,因此,RTMP 會直接傳遞 上面的 audio Tag 內容。詳細能夠參考抓包內容:
由於 Audio 和 Video 是分開發送的。因此,在後期進行拼接的時候,須要注意二者的同步。說道這裏,順便補充一下,音視頻同步的相關知識點。
音視頻同步
主要過程變量參考就是 timeStamp 和 duration。由於,這裏主要是作直播的,推薦你們採用第二種方法,以 Video 爲準。由於,在實際開發中,會遇到 MP4 文件生成時,必需要求第一幀爲 keyframe,這就形成了,以 Audio 爲參考的,會遇到兩個變量的問題。一個是 timeStamp 一個是 keyframe。固然,解決辦法也是有的,就是檢查最後一個拼接的 Buffer 是否是 Keyframe,而後判斷是否移到下一次同步處理。
這裏,我簡單的說一下,以 Video 爲準的同步方法。以 Video 同步,不須要管第一幀是否是 keyframe,也不須要關心 Audio 裏面的數據,由於,Audio 數據是很是簡單的 AAC 數據。下面咱們經過僞代碼來講明一下:
// known condition
video.timeStamp && video.perDuration && video.wholeDuration
audio.timeStamp && audio.perDuration
// start
refDuration = video.timeStamp + video.wholeDuration
delta = refDuration - audio.timeStamp
audioCount = Math.round(delta/audio.perDuration);
audDemuxArr = this._tmpArr.splice(0,audioCount);
// begin to demux
this._remuxVideo(vidDemuxArr);
this._remuxAudio(audDemuxArr);複製代碼
上面算法能夠避免判斷 Aduio 和 Video timeStamp 的比較,保證 Video 一直在 Audio 前面並相差不遠。下面,咱們回到 RTMP 內容。來看看 Command Msg 裏面的內容。
Command Msg
Command Msg 是 RTMP 裏面的一個主要信息傳遞工具。經常用在 RTMP 前期和後期處理。Command Msg 是經過 AMF 的格式進行傳輸的(其實就是相似 JSON 的二進制編碼規則)。Command Msg 主要分爲 net connect 和 net stream 兩大塊。它的交流方式是雙向的,即,你發送一次 net connect 或者 stream 以後,另一端都必須返回一個 _result 或者 _error 以表示收到信息。詳細結構能夠參考下圖:
裏面的 _result 和 _error 會穿插在每一個包中進行講解。
NetConnection
netConnection 能夠分爲 4 種 Msg,connect,call,createStream,close。
connect 是客戶端向 Server 端發送播放請求的。裏面的字段內容有:
-
Command Name[String]: 默認爲 connect。表示信息名稱
-
Transaction ID[Number]: 默認爲 1。
-
Command Object: 鍵值對的形式存放相關信息。
-
Optional: 可選值。通常沒有
那,Command Object 裏面又能夠存放些什麼內容呢?
-
app[String]: 服務端鏈接應用的名字。這個主要根據你的 RTMP 服務器設定來設置。好比:live。
-
flashver[String]: Flash Player 的版本號。通常根據本身設備上的型號來肯定便可。也能夠設置爲默認值:LNX 9,0,124,2。
-
tcUrl[String]: 服務端的 URL 地址。簡單來講,就是 protocol://host/path。好比:rtmp://6521.liveplay.myqcloud.com/live。
-
fpad[Boolean]: 表示是否使用代理。通常爲 false。
-
audioCodecs[Number]: 客戶端支持的音頻解碼。後續會介紹。默承認以設置爲 4071
-
videoCodecs[Number]: 客戶端支持的視頻解碼。有自定義的標準。默承認以設置爲 252
-
videoFunction[Number]: 代表在服務端上調用那種特別的視頻函數。默承認以設置爲 1
簡單來講,Command Object 就是起到 RTMP Route 的做用。用來請求特定的資源路徑。實際數據,能夠參考抓包結果:
上面具體的取值主要是根據 rtmp 官方文檔來決定。若是懶得查,能夠直接使用上面的取值。上面的內容是兼容性比較高的值。當該包成功發送時,另一端須要獲得一個返回包來響應,具體格式爲:
-
Command Name[String]: 爲 _result 或者 _error。
-
Transaction ID[Number]: 默認爲 1。
-
Command Object: 鍵值對的形式存放相關信息。
-
Information[Object]: 鍵值對的形式,來描述相關的 response 信息。裏面存在的字段有:level,code,description
connect 包發送的位置,主要是在 RTMP 握手結束以後。以下:
call 包主要做用是用來遠程執行接收端的程序(RPC, remote procedure calls)。不過,在我解 RTMP 的過程當中,並無實際用到過。這裏簡單介紹一下格式。它的內容和 connect 相似:
-
Procedure Name[String]: 調用處理程序的名字。
-
Transaction ID[Number]: 若是想要有返回,則咱們須要制定一個 id。不然爲 0。
-
Command Object: 鍵值對的形式存放相關信息。AMF0/3
-
Optional: 可選值。通常沒有
Command Object 裏面的內容主要是針對程序,設置相關的調用參數。由於內容不固定,這裏就不介紹了。
call 通常是須要有 response 來代表,遠端程序是否執行,以及是否執行成功等。返回的格式爲:
-
Command Name[String]: 根據 call 中 Command Object 參數來決定的。
-
Transaction ID[Number]: 若是想要有返回,則咱們須要制定一個 id。不然爲 0。
-
Command Object: 鍵值對的形式存放相關信息。AMF0/3
-
Response[Object]: 響應的結果值
createStream 包只是用來告訴服務端,咱們如今要建立一個 channel 開始進行流的交流了。格式和內容都不復雜:
-
Procedure Name[String]: 調用處理程序的名字。
-
Transaction ID[Number]: 本身制定一個。通常能夠設爲 2
-
Command Object: 鍵值對的形式存放相關信息。AMF0/3
當成功後,服務端會返回一個 _result 或者 _error 包來講明接收成功,詳細內容爲:
-
Command Name[String]: 根據 call 中 Command Object 參數來決定的。
-
Transaction ID[Number]: 若是想要有返回,則咱們須要制定一個 id。不然爲 0。
-
Command Object: 鍵值對的形式存放相關信息。AMF0/3。通常爲 Null
-
Stream ID: 返回的 stream ID 值。
下面,咱們來看一下 RTMP 中第二個比較重要的 command msg – netStream msg。
NetStream Msg
NetStream 裏面的 Msg 有不少,但在直播流中,比較重要的只有 play 包。因此,這裏咱們着重介紹一下 play 包。
play 包主要是用來告訴 Server 正式播放音視頻流。並且,因爲 RTMP 自然是作多流分發的。若是遇到網絡出現相應的波動,客戶端能夠根據網絡條件屢次調用 play 命令,來切換不一樣模式的流。
-
Command Name[String]: 根據 call 中 Command Object 參數來決定的。
-
Transaction ID[Number]: 默認爲 0。也能夠設置爲其餘值
-
Command Object: 不須要該字段,在該命令中,默認設爲 Null
-
Stream Name[String]: 用來指定播放的視頻流文件。由於,RTMP 天生是支持 FLV 的,因此針對 FLV 文件來講,並不須要加額外的標識,只須要寫明文件名便可。好比:
StreamName: '6721_75994f92ce868a0cd3cc84600a97f75c'複製代碼
-
不過,若是想要支持其它的文件,那麼則須要額外的表示。固然,音頻和視頻須要不一樣的支持:
-
若是是播放音頻文件,好比 mp3,那麼則須要額外的前綴標識符-mp3。例如:mp3:6721_75994f9。
-
若是涉及到視頻文件的話,不只須要前綴,還須要後綴。好比播放的是 MP4 文件,則標識爲:mp4:6721_75994f9.mp4。
-
start[Number][seconds]: 這個字段其實有點意思。它能夠分爲 3 類來說解:-2,-1,>=0。
-
-2: 若是是該標識符,服務端會首先尋找是否有對應的 liveStream。沒有的話,就找 record_stream。若是尚未的,此次請求會暫時掛起,直到獲取到下一次 live_stream。
-
-1: 只有 live_stream 纔會播放。
-
=0: 至關於就是 seek video。它會直接找到 record_stream,而且根據該字段的值來肯定播放開始時間。若是沒有的話,則播放 list 中的下一個 video。
-
duration[Number][seconds]: 用來設置播放時長的。它裏面也有幾個參數須要講解一下,-1,0,>0。
-
-1: 會一直播放到 live_stream 或者 record_stream 結束。
-
0: 會播放一段一段的 frame。通常不用。
-
0: 會直接播放指定 duration 以內的流。若是超出,則會播放指定時間段內容的 record_stream。
整個 play 包內容就已經介紹完了。咱們能夠看看實際的 play 抓包結果:
那 play 包是在那個環節發送,發送完以後需不須要對應的 _result 包呢?
play 包比較特殊,它是不須要 _result 回包的。由於,一旦 play 包成功接收後。server 端會直接開始進行 streamBegin 的操做。
到這裏,後續就能夠開始正式接收 video 和 audio 的 stream。
瞭解
網易雲信,來自網易核心架構的通訊與視頻雲服務。
網易雲信(NeteaseYunXin)是集網易18年IM以及音視頻技術打造的PaaS服務產品,來自網易核心技術架構的通訊與視頻雲服務,穩定易用且功能全面,致力於提供全球領先的技術能力和場景化解決方案。開發者經過集成客戶端SDK和雲端OPEN API,便可快速實現包含IM、音視頻通話、直播、點播、互動白板、短信等功能。