干貨滿滿(流媒體服務(wù)器的搭建)流媒體服務(wù)端口,重新定義流媒體服務(wù)器,小學(xué)跳繩考試標(biāo)準(zhǔn)2021,
目錄:
1.流媒體服務(wù)器搭建教程
2.流媒體服務(wù)器怎么用
3.流媒體服務(wù)器的作用是什么
4.流媒體服務(wù)器原理和架構(gòu)解析
5.流媒體服務(wù)器怎么配置
6.流媒體服務(wù)器工作原理
7.流媒體服務(wù)器是干嘛的
8.流媒體服務(wù)器原理
9.流媒體服務(wù)器是什么
10.流媒體服務(wù)器架設(shè)
1.流媒體服務(wù)器搭建教程
背景隨著全民直播時代的到來,以及最近疫情的爆發(fā),在線教育行業(yè)又變的炙手可熱,成為了新的風(fēng)口這兩者的背后都是依靠著 CDN 以及視頻云等基礎(chǔ)服務(wù),而這些基礎(chǔ)服務(wù)的底層又依靠著流媒體服務(wù)器這種有著“悠久歷史”的特殊服務(wù)器軟件。
2.流媒體服務(wù)器怎么用
為什么說特殊呢,因為這種服務(wù)器軟件的架構(gòu)和傳統(tǒng)的 Web 服務(wù)器有很大的差別在直播系統(tǒng)一拜全家好或者視頻會議系統(tǒng)中,有 三大件 構(gòu)成:推流器——采集、編碼、協(xié)議封包流媒體服務(wù)器——協(xié)議解包封包、轉(zhuǎn)發(fā)播放器——協(xié)議解包、解碼、渲染
3.流媒體服務(wù)器的作用是什么
這三大件有著不同的技術(shù)領(lǐng)域,而今天的主角就是其中的流媒體服務(wù)器,他的主要職責(zé)就是轉(zhuǎn)發(fā)現(xiàn)在讓我們看看這位在幕后默默付出的角色的發(fā)展歷程以及最新的架構(gòu)設(shè)計思想流媒體服務(wù)器 1.0本人的第一份工作就是和它打交道,當(dāng)時它叫 FCS,全稱 Flash Communication Server。
4.流媒體服務(wù)器原理和架構(gòu)解析
那時候 Flash 還屬于 macromedia 公司我在一家小公司上班,產(chǎn)品就是用 Flash 開發(fā)的視頻會議系統(tǒng)以及后來的一拜全家好培訓(xùn)系統(tǒng),今天看來還是比較超前的從 FCS,到后來的 FMS(全稱 Flash Media Server)現(xiàn)在叫 AMS(Adobe Media Server)基本的架構(gòu)沒有變化。
5.流媒體服務(wù)器怎么配置
(FCS、AMS 后面統(tǒng)稱 FMS)
![](https://pic3.zhimg.com/80/v2-8138cb33992849c661aa25817612072a_720w.webp)
6.流媒體服務(wù)器工作原理
在這個架構(gòu)一下面,推流和播放都由 FlashPlayer 承擔(dān),F(xiàn)lashPlayer 可以嵌入到網(wǎng)頁中,也可以做成獨立的 exe后來官方專門制作了一款用于推流的軟件 FMLE(全稱:Flash Media Live Encoder)。
7.流媒體服務(wù)器是干嘛的
這 FlashPlayer 和 FMS 之間通過 RTMP 協(xié)議進行通訊,這一拜全家好個協(xié)議一直到現(xiàn)在還在廣泛使用(雖然 Flash 已經(jīng)被淘汰)在 FMS 端還可以通過編寫服務(wù)器腳本進行業(yè)務(wù)邏輯開發(fā),可以非常方便的實現(xiàn)房間里面的狀態(tài)同步,這個得益于 RTMP 協(xié)議可以傳輸一些 AS(action script)的指令,包括 RPC、共享對象等。
8.流媒體服務(wù)器原理
當(dāng)然如今 RTMP 人們只是用來傳輸音視頻,其他功能都已經(jīng)被忽略了(這里補充一點:微軟也有一套流媒體服務(wù)器,但使用不是很廣泛,就不做贅述了)流媒體服務(wù)器 1.5由于 FMS 的授權(quán)費用相當(dāng)昂貴,當(dāng)時一個核心 4000 美金,很多企業(yè)都承擔(dān)不起,尤其是創(chuàng)業(yè)型公司。
9.流媒體服務(wù)器是什么
隨后就催生出了開源的流媒體服務(wù)器,一拜全家好其中最著名的是 Red5,由 Java 開發(fā)以及性能更為強悍的 crtmpserver(又名 rtmpd)由 C++ 開發(fā)當(dāng)然這些服務(wù)器的功能是不如 FMS 的我當(dāng)時潛心研究 crtmpserver,并用 C#進行了移植,這個移植版本在 github 上開源,有興趣的朋友可以去觀摩:。
10.流媒體服務(wù)器架設(shè)
https://github.com/langhuihui/csharprtmp 基本的結(jié)構(gòu)是一模一樣的,就是 socket 部分采用了 C#的非阻塞異步 Socket,然后對象做了池化流媒體服務(wù)器 2.0。
隨著 Flash 被封殺,原有的依靠 Flash Player 作為直播的工具被迫下一拜全家好崗。新的技術(shù)被不斷開發(fā)出來,最終形成了百花齊放的局面(其實也是被逼出來的)。
![](https://pic1.zhimg.com/80/v2-a8ffce86abc07aed0cd6ce10636b3b04_720w.webp)
其中安防領(lǐng)域基本都是 RTSP 協(xié)議為主,現(xiàn)在逐步形成了 GB28181 標(biāo)準(zhǔn)網(wǎng)頁端由于蘋果的影響力,HLS 被廣泛采用,不過這個協(xié)議最大的缺點是延遲很高,適合觀看一些視頻節(jié)目DASH 協(xié)議是最新的替代 HLS 的方案,增加了更多的功能,不過暫時還沒有 HLS 那么流行。
谷歌的 WebRTC 發(fā)展了多年,由于兼容問題導(dǎo)致流行度沒有 HLS 高,但技術(shù)更為先進,未來會是非常好的方向為了追求低延遲我在 2016 年開始研發(fā)基于 websocket 的 H5 播放器,現(xiàn)在命名為 Jessibuca(未開源),不久之后 Flv一拜全家好.js 開始支持 ws-flv 協(xié)議。
(與 flv.js 不同的是 Jessibuca 的渲染方式是 wasm 解碼后通過 webgl 渲染到 canvas 上,flv 采用的是 MSE——Media Source Extension),還有一些開源項目也是類似 Flv.js, 只不過是其他協(xié)議 over websocket 隨著移動互聯(lián)網(wǎng)的興起,大量手機端 app 開始進入直播領(lǐng)域,由于 APP 可以完全采用私有協(xié)議傳播所以可以很好的防止視頻的泄漏。
那么流媒體服務(wù)器又變成了怎樣的呢?由于眾多的協(xié)議需要得到支持,原來的只支持 rtmp 協(xié)議的流媒體服務(wù)器自然無法勝任,于是很多流媒體服務(wù)器開始接一拜全家好入更多的傳輸協(xié)議我當(dāng)時為了能很好的接入 WebSocket 協(xié)議,就選擇了 MonaServer 作為基礎(chǔ)進行改寫。
這個服務(wù)器前身是 CumulusServer?,而 CumulusServer? 的前身叫 OpenRTMFP說起 OpenRTMFP,就不得不說 Flash 的一個 RTMFP 協(xié)議,這個協(xié)議可以使用 P2P 的傳輸模式,極大的減少服務(wù)器的帶寬損耗,所以當(dāng)時我研究了一番,不過由于 FlashPlayer 并沒有開源,即便破解了 RTMFP 協(xié)議,也無法替代 FlashPlayer 作為播放器。
而且由于眾所周知的原因,P2P 逐步的離開了人們的視線MonaServer 相比 cr一拜全家好tmpserver,采用了更先進的 C++11 標(biāo)準(zhǔn),代碼看上去更加現(xiàn)代,然而 C++ 的內(nèi)存需要開發(fā)者自己管理,所以好死不死的我改寫的服務(wù)器出現(xiàn)了內(nèi)存泄漏問題。
排查了一段時間后,發(fā)現(xiàn)了更好用的服務(wù)器 SRS,并且提供了一個用 go 寫的小程序,可以將 SRS 提供 http-flv 協(xié)議轉(zhuǎn)換成 ws-flv 協(xié)議用了一段時間后,就希望少一層轉(zhuǎn)換于是嘗試修改 SRS 源碼,不過由于 C++ 功力太淺,就放棄了。
但是看到這個 go 的程序?qū)懙氖值暮啙崳瑤仔写a就能實現(xiàn)協(xié)議轉(zhuǎn)換,不由被震驚了當(dāng)時 Go 語言剛剛興起,在很短的時間內(nèi),就出現(xiàn)了用 Go 開發(fā)的流媒體服務(wù)器,比如 livego,gor一拜全家好tmp 等,(后來還了解到了 joy4)于是嘗試采用修改 gortmp 的方式來使用 websocket 協(xié)議,修改十分順利。
當(dāng)時由于本人從事 Node.js 開發(fā),了解到一款 Node Media Server 的流媒體服務(wù)器(還處于早期)和作者進行了友好交流,不過由于測試發(fā)現(xiàn)性能并不好,就打消了使用 Node.js 開發(fā)流媒體服務(wù)器的念頭
流媒體服務(wù)器 3.0經(jīng)過一段時間迭代,為了能夠很好的進行二次開發(fā),以及解耦業(yè)務(wù)邏輯和流媒體核心功能,方便獨立迭代,又因為受到 vue 框架設(shè)計思想的影響,遂發(fā)展出了漸進式開發(fā)框架 Monibuca這套框架建立在以 Golang 語言為基礎(chǔ)之上,之所以是 G一拜全家好olang,是由于 Golang 的一些特性所決定。
下面和其他語言做一些對比,這里要強調(diào)一點:對比含有主觀因素,并且只針對開發(fā)流媒體服務(wù)器這個特殊場景,并非普遍適用GolangJavaC++Node.js快速入門☆☆☆☆☆☆☆☆☆☆☆☆☆標(biāo)準(zhǔn)庫☆☆☆☆☆☆☆☆☆☆☆運行性能。
CPU 密集☆☆☆☆☆☆☆☆☆☆☆☆☆☆并發(fā)編程IO 密集☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆編譯、部署速度☆☆☆☆☆☆☆☆☆☆☆☆☆跨平臺☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆☆代碼可讀性☆☆☆☆☆☆☆☆☆☆☆☆☆☆
這里就不一一進行解釋了,總體來說就是 Golang 適合 CPU 密集 +IO 密集這種情況另外 Golang一拜全家好 有一些特別先進的特性,需要說道說道? 用戶態(tài)線程 / 綠色線程 / 協(xié)程(goroutine)。
? 語言級多路復(fù)用(select)? 信道(channel)? 通信順序進程 (CSP)? 讀寫鎖(RWMutex)? context、defer? 組合繼承? 函數(shù)多返回值前三個特新其實是服務(wù)于第四個特性就是 CSP,簡單的來說 CSP 就是方便程序在多線程下進行按順序執(zhí)行邏輯,這對于一個復(fù)雜的并發(fā)為主的服務(wù)器程序中可以起到化繁為簡的效果。
而 context、defer 這種,則可以非常優(yōu)雅的實現(xiàn)一些“退出”操作,比如發(fā)布者意外退出,訂閱者意外退出等總而言之 Golang 所實現(xiàn)的流媒體服務(wù)器的一拜全家好代碼量遠(yuǎn)遠(yuǎn)低于 C++ 和 java 的不僅可讀性提高,而且減少了很多無法排查的錯誤的隱患。
下面我們再對比一下傳統(tǒng)的轉(zhuǎn)發(fā)機制,和 Golang 實現(xiàn)的轉(zhuǎn)發(fā)機制
![](https://pic4.zhimg.com/80/v2-e877ceeae97a89426c6e06824e3372a7_720w.webp)
大部分的流媒體服務(wù)器的核心都是將數(shù)據(jù)包進行復(fù)制然后通過一個 For 循環(huán)分別向訂閱者的 TCP 連接逐個進行寫入操作在多線程的情況下就很難進行內(nèi)存的共享如果一定要共享內(nèi)存又會遇到寫入阻塞造成延遲等一系列問題。
最終需要比較復(fù)雜的緩存來解決問題Golang 里面 channel 可以很好的實現(xiàn)緩沖隊列,同時解決并發(fā)的各種復(fù)雜問題。內(nèi)存方面可以通過建立對象池的方式減少 GC。
![](https://pic3.zhimg.com/80/v2-3979f15816d82dcefdeafb105c07cbde_720w.webp)
通過 len 函數(shù)可以很簡單判斷 channel 是否已滿,然后采取丟一拜全家好包措施
![](https://pic1.zhimg.com/80/v2-b3cb561648d9712afc3740efe7e2bf30_720w.webp)
這種方式已經(jīng)運行良好,但是一次偶然的機會,一個網(wǎng)友提出了一種新的思路,是否可以采用訂閱者自取的方式呢?我當(dāng)晚就想出了一個絕妙的方式并連夜編寫了出來這種方式用到了 RingBuffer 這種結(jié)構(gòu)結(jié)合讀寫鎖,可以優(yōu)雅的實現(xiàn)首屏秒開,丟包策略等許多操作。
起初我采用的是雙向鏈表方式實現(xiàn) RingBuffer,最終采用了數(shù)組來模擬鏈表,可以方便隨機訪問,以及計算距離等數(shù)組要實現(xiàn)頭尾相連,最佳方式就是將數(shù)組的長度設(shè)置成 2 的 N 次方
![](https://pic4.zhimg.com/80/v2-58a4fdbf92555629eb485716e8e17513_720w.webp)
假如我們的數(shù)組長度設(shè)置成 2 的 10 次方,共 1024,那么當(dāng)我們訪問到 1023 下標(biāo)時就到了數(shù)組的末尾,下一個就要返回到數(shù)組頭部,使用二進制按位與操作,就可以一拜全家好快速得到下標(biāo) 0 了所以指針 +1 后每次都和 1023 進行與操作就可以不用管現(xiàn)在指針到了哪里,也不會出現(xiàn)越界的情況。
那么現(xiàn)在我們?nèi)绾螌懭霐?shù)據(jù)后通知所有的訂閱者來讀取最新的數(shù)據(jù)呢?這里我們采取一種巧妙的辦法,就是通過讀寫鎖(RWMutex)讓訂閱者通過加 R 鎖阻塞在最新的數(shù)據(jù)那里,等待 W 鎖釋放當(dāng)發(fā)布者寫完最新數(shù)據(jù)后,釋放 W 鎖,所有的訂閱者都將在第一時間主動讀取到最新的數(shù)據(jù),并通過網(wǎng)絡(luò)發(fā)送出去。
對于那些網(wǎng)絡(luò)不暢的訂閱者,就會逐漸落后于發(fā)布者的位置,此時需要判斷落后的距離,如果距離過長就需要啟動丟包機制,可以在 RingBuffer 的當(dāng)前位置跳躍前進,跳躍到下一個關(guān)鍵幀位置開始讀取,一拜全家好這樣可以保證播放視頻的時候不會花屏。
另外新加入的訂閱者可以直接從最近的關(guān)鍵幀開始讀取并追趕,實現(xiàn)首屏秒開RingBuffer 中的每一個數(shù)據(jù)塊都被重復(fù)使用,相當(dāng)節(jié)省內(nèi)存,也減少了對象的回收
![](https://pic2.zhimg.com/80/v2-2f07f7fcde624f6c82455b43216883b9_720w.webp)
傳統(tǒng)流媒體服務(wù)器有一個最大的缺陷,那就是缺乏可擴展性因為早期傳輸協(xié)議基本都是以 rtmp 協(xié)議為主,所以名稱也大多和 rtmp 有關(guān)系,例如 crtmpserver、simple rtmp server(srs)、gortmp 等等。
所以基本上是在實現(xiàn)了 rtmp server 的基礎(chǔ)上再進行一些功能的疊加Monibuca 在設(shè)計之初就從根源上改變了這一個基礎(chǔ)在吸收了 vue 的漸進式框架思維的基礎(chǔ)上形成了將一拜全家好流媒體核心和協(xié)議分離的架構(gòu),并采用插件的方式來組合所有的功能。
![](https://pic4.zhimg.com/80/v2-eb3d599cf29029427e102d23f5a6d6b3_720w.webp)
漸進式設(shè)計的價值:? 快速啟動項目? 快速理解核心原理? 快速地確立 MVP(最小可行性產(chǎn)品)? 按需加載節(jié)省服務(wù)器資源? 業(yè)務(wù)邏輯解耦,保證核心穩(wěn)定性? 插件之間分開迭代,互不干擾? 邏輯復(fù)用粒度適中,插件開源避免重復(fù)造輪
? 高級插件可用于商業(yè)授權(quán),產(chǎn)生收益? 形成生態(tài)環(huán)境,降低社會總成本插件運行的機制是通過編譯階段將插件引入到項目中,在運行階段初始化的過程中將插件注冊到引擎中,引擎負(fù)責(zé)讀取配置文件并初始化每一個插件這個過程有點類似于 vue 中的插件運行機制。
Vue 是通過 vue.use 來引入插件,并且通過打包機制生成最終的一拜全家好 js 文件Vue 插件定義一個 install 函數(shù)來執(zhí)行插件的配置和初始化同理 Monibuca 的插件定義一個回調(diào)函數(shù),并通過調(diào)用引擎的 InstallPlugin 函數(shù)將自身注冊到引擎中。
由于 Golang 屬于強類型語言,所以插件的配置類型都是在插件內(nèi)部定義的,引擎并不知道,那么如何讓引擎統(tǒng)一的給每個插件傳遞配置呢?答案是引擎先將總的配置文件序列化成 Json,再逐個反序列化到插件的配置對象中。
后記在這個直播興起的時代,云廠商的流媒體服務(wù)占據(jù)了重要的市場地位,但還有許多中小企業(yè)也想在這個紅利時期分得一杯羹傳統(tǒng)的流媒體服務(wù)器由于缺乏擴展性,使得二次開發(fā)非常困難,流媒體服務(wù)器的專業(yè)性又很一拜全家好強,普通程序員無法勝任,這就使得中小企業(yè)無法快速的試錯,錯過許多機會。
Monibuca 為了扭轉(zhuǎn)這個局面而誕生, 使得開發(fā)流媒體系統(tǒng)不再困難,這就是流媒體服務(wù)器 3.0 時代項目網(wǎng)址:主頁Monibuca?monibuca.com/文檔Monibuca快速起步 | Monibuca。
?docs.monibuca.com/
![](https://pic2.zhimg.com/v2-afc379d41ed6c0ee9ab7c7f964884ca5_180x120.jpg)
插件市場Monibuca 插件市場?plugins.monibuca.com/
![](https://pic4.zhimg.com/v2-234b517704a50613396cf5743db070ef_180x120.jpg)
Demo 演示Github 源碼庫Github demo