1. 程式人生 > >RTMP H5 直播流技術解析

RTMP H5 直播流技術解析

RTMP 是什麼

RTMP 全稱即是 Real-Time Messaging Protocol。顧名思義就是用來作為實時通訊的一種協議。該協議是 Adobe 搞出來的。主要是用來傳遞音視訊流的。它通過一種自定義的協議,來完成對指定直播流的播放和相關的操作。和現行的直播流相比,RTMP 主要的特點就是高效,這裡,我就不多費口舌了。我們先來了解一下 RTMP 是如何進行握手的。

RTMP 握手

RTMP 是基於 TCP 三次握手之後的,所以,RTMP 不是和 TCP 一個 level 的。它本身是基於 TCP 的可靠性連線。RTMP 握手的方式如圖:

image.png-50.3kB

(C 代表 Client,S 代表 Server)

它主要是通過兩端的欄位內容協商,來完成可信度認證的。基本過程如下:

  • client: 客戶端需要發 3 個包。C0,C1,C2
  • server: 服務端也需要發同樣 3 個包。 S0,S1,S2。

整個過程如上圖所述,但實際上有些細節需要注意。

握手開始:

【1】 客戶端傳送 C0,C1 包

此時,客戶端處於等待狀態。客戶端有兩個限制:

  • 客戶端在未接受到 S1 之前不能傳送 C2 包
  • 客戶端在未接收到 S2 之前不能傳送任何實際資料包

【2】 服務端在接受到 C0,傳送 S0,S1 包。也可以等到接受到 C1 之後再一起傳送,C1 包的等待不是必須的。

此時,服務端處於等待狀態。服務端有兩個限制:

  • 服務端在未接受到 C1 之前不能傳送 S2.
  • 服務端在未接收到 C2 之前不能傳送任何實際資料包

【3】客戶端接受到 S1/S0 包後,傳送 C2 包。

【4】服務端接受到 C2 包後,返回 S2 包,並且此時握手已經完成。

不過,在實際應用中,並不是嚴格按照上面的來。因為 RTMP 並不是強安全性的協議,所以,S2/C2 包只需要 C1/S1 中的內容,就可以完成內容的拼接。

實際握手

這麼多限制,說白了,其實就是一種通用模式:

  • C0+C1
  • S0+S1+S2
  • C2

接下來,我們來具體看看 C/S 012 包分別代表什麼。

C0 && S0

C0 和 S0 其實區別不大,我這裡主要講解一下 C0,就差不多了。首先,C0 的長度為 1B。它的主要工作是確定 RTMP 的版本號。

  • C0:客戶端傳送其所支援的 RTMP 版本號:3~31。一般都是寫 3。
  • S1:服務端返回其所支援的版本號。如果沒有客戶端的版本號,預設返回 3。

C1 && S1

C1/S1 長度為 1536B。主要目的是確保握手的唯一性。格式為:

image.png-107kB

  • time: 傳送時間戳,這個其實不是很重要,不過需要記住,不要超出 4B 的範圍即可。
  • zero: 保留值 0.
  • random: 該欄位長尾 1528B。主要內容就是隨機值,不管你用什麼產生都可以。它主要是為了保證此次握手的唯一性,和確定握手的物件。

C2 && S2

C2/S2 的長度也是 1536B。相當於就是 S1/C1 的響應值。上圖也簡單說明了就是,對應 C1/S1 的 Copy 值,不過第二個欄位有區別。基本格式為:

image.png-105.1kB

  • time: 時間戳,同上,也不是很重要
  • time2: C1/S1 傳送的時間戳。
  • random: S1/C1 傳送的隨機數。長度為 1528B。

這裡需要提及的是,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: },
    ()=>{
        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: 
}, () => {
	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);
    }
})

詳細程式碼可以參考 gist

RTMP 基本架構

RTMP 整個內容,除了握手,其實剩下的就是一些列圍繞 type id 的 message。為了讓大家更清楚的看到整個架構,這裡簡單陳列了一份框架:

image.png-94.9kB

在 Message 下的 3 個一級子 Item 就是我們現在將要大致講解的內容。

可以看到上面所有的 item 都有一個共同的父 Item–Message。它的基本結構為:

  • Header: header 部分用來標識不同的 typeID,告訴客戶端相應的 Message 型別。另外,還有個功效就是多路分發。
  • Body: Body 內容就是相應傳送的資料。這個根據不同的 typeID 來說,格式就完全不一樣了。

下面,我們先了解一下 Header 和不同 typeID 的內容:

RTMP 中的 Header 分為 Basic Header 和 Message Header。需要注意,他們兩者並不是獨立的,而是相互聯絡。Message Header 的結構由 Basic Header 的內容來決定。

image.png-41kB

接下來,先分開來講解:

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 開始。

  • CS ID: 0 ==> 整個 BH 長為 2B,其中可以表示的 Stream ID 數量為 64-319。例如:
  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

  • CS ID: 1 ==> 整個 BH 長為 3B。可以儲存的 Stream ID 數量為 64-65599。例如:
  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。

  • CS ID >2 ==> 整個 BH 長為 1B。可以儲存的 Stream ID 數量為 3-63。例如:
  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

當 fmt 為 0 時,MH 的長度為 11B。該型別的 MH 必須要流的開頭部分,這包括當進行快退或者點播時重新獲取的流。該結構的整體格式如下:

image.png-58.8kB

也就是說,當 fmt 為 0 時,其格式是一個完整的 MH。

  • timestamp 是絕對時間戳。用來代表當前流編碼。
  • message length: 3B, 傳送 message 的長度。
  • type id: 1B
  • stream id: 4B, 傳送 message stream id 的值。是 little-endian 寫入格式!

fmt: 1

當 fmt 為 1 時,MH 的長度為 7B。該型別的 MH 不帶 msg stream id。msg stream id 由前面一個 package 決定。該數值主要由前一個 fmt 為 0 的 MH 決定。該型別的 MH 通常放在 fmt 為 0 之後。

image.png-16.7kB

fmt: 2

當 fmt 為 2 時,MH 的長度為 3B。該型別的 MH 只包括一個 timestamp delta 欄位。其它的資訊都是依照前面一個其他型別 MH 決定的。

fmt: 3

當 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 Body

上面說的主要是 Message Header 的公用部分,但是,對於具體的 RTMP Message 來說,裡面的 type 會針對不同的業務場景有不同的格式。Message 全部內容如上圖所示:

image.png-94.9kB

這裡,我們根據流程圖的一級子 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

如圖所示:

此處輸入圖片的描述

OK,我們接下來一個一個來介紹一下:

Set Chunk Size(1)

看名字大家應該都能猜到這類資訊是用來幹啥的。該型別的 PCM 就是用來設定 server 和 client 之間正式傳輸資訊的 chunk 的大小,type ID 為 1。那這有啥用呢?

SCS(Set Chunk Size) 是針對正式傳送資料而進行資料大小的傳送限制。一般預設為 128B。不過,如果 server 覺得太小了,想傳送更大的包給你,比如 132B,那麼 server 就需要給你傳送一個 SCS,告知你,接下來“我傳送給你的資料大小是 132B”。

此處輸入圖片的描述

  • 0: 只能設為 0 ,用來表示當前的 PCM 的型別。
  • chunk size: 用來表示後面傳送正式資料時的大小。範圍為 1-16777215。

如下,提供過 wireshark 抓包的結果:

image.png-45.4kB

Abort Message(2)

該類 PCM 是用來告訴 client,丟棄指定的 stream 中,已經載入到一半或者還未載入完成的 Chunk Message。它需要指定一個 chunk stream ID。

基本格式為:

此處輸入圖片的描述

  • chunk stream id: 指定丟棄 chunk message 的 stream
Acknowledgement(3)

該協議資訊其實就是一個 ACK 包,在實際使用是並沒有用到,它主要是用來作為一個 ACK 包,來表示兩次 ACK 間,接收端所能接收的最大位元組數。

它基本格式為:

image.png-52.1kB

  • sequence number[4B]: 大小為 4B

不過,該包在實際應用中,沒有多高的出現頻率。

Window Acknowledgement Size(5)

這是用來協商傳送包的大小的。這個和上面的 chunk size 不同,這裡主要針對的是客戶端可接受的最大資料包的值,而 chunk size 是指每次傳送的包的大小。也可以叫做 window size。一般電腦設定的大小都是 500000B。

詳細格式為:

此處輸入圖片的描述

通過,wireshark 抓包的結果為:

image.png-56.2kB

Set Peer Bandwidth(6)

這是 PCM 中,最後一個包。他做的工作主要是根據網速來改變傳送包的大小。它的格式和 WAS 類似,不過後面帶上了一個 Type 用來標明當前頻寬限制演算法。當一方接收到該資訊後,如果設定的 window size 和前面的 WAS 不一致,需要返回一個 WAS 來進行顯示改變。

基本格式為:

此處輸入圖片的描述

其中 Limit Type 有 3 個取值:

  • 0: Hard,表示當前頻寬需要和當前設定的 window size 匹配
  • 1: Soft,將當前寬頻設定為該資訊定義的 window size,或者已經生效的 window size。主要取決於誰的 window size 更小
  • 2: Dynamic,如果前一個 Limit Type 為 Hard 那麼,繼續使用 Hard 為基準,否則忽略該次協議資訊。

實際抓包情況可以參考:

image.png-56.8kB

UCM

全稱為:User Control Message(使用者控制資訊)。它的 Type ID 只能為 4。它主要是傳送一些對視訊的控制資訊。其傳送的條件也有一定的限制:

  • msg stream ID 為 0
  • chunk stream ID 為 2

它的 Body 部分的基本格式為:

UCM

UCM 根據 Event Type 的不同,對流進行不同的設定。它的 Event Type 一共有 6 種格式 Stream Begin(0)Stream EOF(1)StreamDry(2)SetBuffer Length(3)StreamIs Recorded(4)PingRequest(6)PingResponse(7)

相關推薦

RTMP H5 直播技術解析

RTMP 是什麼 RTMP 全稱即是 Real-Time Messaging Protocol。顧名思義就是用來作為實時通訊的一種協議。該協議是 Adobe 搞出來的。主要是用來傳遞音視訊流的。它通過一種自定義的協議,來完成對指定直播流的播放和相關的操作。和現行的直播

Ubuntu下搭建Nginx伺服器+整合RTMP視訊直播處理(邊做邊做更新)

簡介: 要求:做一個網頁獲取攝像頭的視訊流,然後將視訊流經過流伺服器推送到後臺視訊處理伺服器,再由後臺視訊處理伺服器推送到流伺服器,最終推送到頁面。如圖: 經過查閱資料,目前有red5以及nginx+nginx-rtmp-module實現,選擇第二種方式來實現。 準備工作:

視訊監控安防平臺-國標GB28181轉RTSP和RTMP進行H5(RTMP/HLS)直播(支援GB28181-2016版本、支援公網碼傳輸)

          視訊監控安防平臺-國標GB28181轉RTSP、RTMP和HLS管理平臺(支援GB28181-2016版本、支援公網碼流傳輸)       最近抽了點時間把國標GB28181轉RTSP、RTMP和HLS管理平臺做了簡單的整理,把相應的Demo也整理好了,

obs nginx-rtmp-module搭建媒體服務器實現直播 ding

video 接下來 監聽 comm 地址 什麽 ip地址 automake text 接下來我就簡單跟大家介紹一下利用nginx來搭建流媒體服務器。 我選擇的是騰訊雲服務器 1、下載nginx-rtmp-module: nginx-rtmp-module的官方gith

極速搭建RTMP直播伺服器+webapp (vue) 簡單實現直播效果

  在嘗試使用webRTC實現webapp直播失敗後,轉移思路開始另外尋找可行的解決方案。在網頁上嘗試使用webRTC實現視訊的直播與看直播,在谷歌瀏覽器以及safari瀏覽器上測試是可行的。但是基於基座打包為webapp後不行,所以直播的話建議還是原生的好。HBuilder自帶的H5+有提供了原生

實現直接輸出h264直播rtmp伺服器

RTMP(Real Time Messaging Protocol)是常見的流媒體協議,用來傳輸音視訊資料,結合flash,廣泛用於直播、點播、聊天等應用,以及pc、移動、嵌入式等平臺,是做流媒體開發經常會接觸到的協議。我之前曾經寫過一篇文章“RTMP協議傳送H.264編碼及AAC編碼的音視

基於SRS搭建RTMP直播媒體伺服器

軟體定位 SRS 定位是運營級的網際網路直播伺服器叢集,追求更好的概念完整性和最簡單實現的程式碼。 運營級:商業運營追求極高的穩定性、良好的系統對接、錯誤排查和處理機制。譬如日誌檔案格式、reload、系統 HTTP 介面、提供 init.d 指令碼、轉發、轉碼和邊緣回多源站

利用nginx與nginx-rtmp-module搭建媒體伺服器實現直播

轉自:https://www.cnblogs.com/suiyuewuxin/p/7256972.html 使用環境是centos 7.0+nginx;可以實現簡單的流媒體服務。 先下載nginx-rtmp-module拓展: nginx-rtmp-module的官方github地址:h

Windows10環境下 Nginx+ffmpeg自搭伺服器製作RTMP直播

Windows10環境下 Nginx+ffmpeg自搭伺服器製作RTMP直播流 學習筆記 所需條件: nginx-rtmp-module(帶rtmp模組) ,連結:https://link.jianshu.com/?t=http%3A%2F%2Fnginx-win.ec

直播大火,HTML5直播技術你知道嗎?H5直播方案分析講解

2017年視訊直播可謂是大火,各種視訊直播平臺陸續登場,H5直播也逐漸成熟,今天就來講解H5視訊直播可行性方案的分析。 目前 WEB 上主流的視訊直播方案有 HLS 和 RTMP,移動 WEB 端目前以 HLS 為主(HLS存在延遲性問題,也可以藉助 video.

一對一直播原始碼如何選擇直播媒體技術(CDN)

快上西樓,怕天放、浮雲遮月。但喚取、玉纖橫笛,一聲吹裂。 誰做冰壺浮世界,最憐玉斧修時節。問常娥、孤冷有愁無。應華髮。 雲液滿,瓊杯滑。長袖起,清歌咽。嘆十常八九,欲磨還缺。 若得長圓如此夜,人情未必看承別。把從前、離恨總成歡,歸時說 ——中秋快樂,簡短寒暄後

一對一分析直播協議和推技術

近年來直播已成為網際網路行業的大熱話題,直播答題、遊戲直播、競賽直播等層出不窮,直播早已成為人們耳熟能詳的技術。事實上直播的興起不僅與新時代人們要求為自己代言的心理有關,同時也得益於頻寬的提速和CDN技術的發展。 伴隨著CDN技術的成熟,企業自己部署雲伺服器做直播也越來越簡單 。 本文作

Android中直播視訊技術探究之---採集攝像頭Camera視訊源資料進行推(採用金山雲SDK)

一、前言在之前已經詳細介紹了Android中的一種視訊資料來源:Camera,不瞭解的同學可以點選進入:Android中Camera使用詳解 ,在這篇文章中我們介紹瞭如何採集攝像頭的每一幀資料,然後進行

利用nginx的nginx-rtmp-module搭建媒體直播伺服器

Nginx除了做web伺服器之外在流媒體方面的支援也是有對應的模組,nginx-rtmp-module就是nginx的一個擴充套件模組,支援rtmp視訊推流,同時利用nginx作為web伺服器的有時可以很方便的實現直播拉流,專案官方地址是https://github.com/arut/nginx-r

通過nginx,nginx-rtmp-module實現媒體直播

1、 下載nginx http://nginx.org/en/download.html 下載nginx-rtmp-module: nginx-rtmp-module的官方github地址:https://github.com/arut/nginx-rtmp-module

利用Nginx搭建RTMP視訊直播,點播伺服器,ffmpeg推,回看

#下面的server是在http的一級配置標籤下的#上面的註釋對懂nginx的人是廢話,但是如果你不熟悉nginx,建議認真看看http{...# 這裡有一些其他的配置 server { listen 80;#埠 server_name rtmp-server;#設定http

各種RTMP直播播放許可權_音視訊_資料花屏_問題檢測與分析工具EasyRTMPClient

之前的一篇部落格《網路攝像機IPCamera RTSP直播播放網路/許可權/音視訊資料/花屏問題檢測與分析助手EasyRTSPClient》,我們介紹了RTSP流的檢測和分析工具EasyRTSPClient,可以說已經是深入了我的平時運維工作中了,當我們發現有任

搭建直播伺服器,使用nginx與nginx-rtmp-module搭建媒體伺服器;

現在,一起學習一下如何自己搭建一個流媒體伺服器吧! 本次搭建流媒體使用的環境是centos 7.0+nginx; 讓我們一起開始奇妙的流媒體之旅吧! 1、下載nginx-rtmp-module: 使用命令: git clone https://gi

直播答題技術方案解析

直播答題已經是風口,毋容置疑。對攻城獅們來說,2018 年春節是個坎,直播答題技術做細緻做到位了

搭建rtmp直播服務之3:java開發ffmpeg實現rtsp轉rtmp並實現ffmpeg命令的介面化管理架構設計及程式碼實現

這一篇將進一步深挖java對ffmepg命令的控制並最終實現服務介面化 通知:由於很多同學反映本章程式碼的命令封裝設計的不是很好,所以對本章程式碼重新進行了實現,新版本推翻了本章原有程式碼內部實現,介面設計更加利於注入自己的實現,並增加可執行原生ffmpeg命令功