ts获取服务器数据_基于Nginx的媒体服务器技术-线上公开课
文 / 朱建平
整理 / LiveVideoStack
直播回放https://www2.tutormeetplus.com/v2/render/playback?mode=playback&token=006643cdea15499d96f19ab676924e88
1. Nginx流媒體擴展:http-flv、http-ts、hls+
最初始的nginx-rtmp-module相關(guān)模型與包括SRS在內(nèi)的多數(shù)流媒體服務器實際上是一樣的(1個生產(chǎn)者,n個消費者)。Nginx存一個問題:它僅僅做了RTMP的消費模型,如果想擴展 http-flv或http-ts的形式會較為困難。由于rtmp-session僅供RTMP協(xié)議使用,如果想擴展http-flv,首先我們需要了解其基礎(chǔ)分發(fā)模型(如上圖所示):所有的生產(chǎn)者與消費者都會被掛載到同一個stream中,生產(chǎn)者負責從網(wǎng)絡端接收數(shù)據(jù),消費者從buffer中獲取數(shù)據(jù)對外發(fā)送。
如果是發(fā)送flv數(shù)據(jù),那么可以保留原有rtmp-session,當服務器收到一個HTTP請求時,創(chuàng)建一個rtmp-session,此session與網(wǎng)絡不相關(guān),僅僅是邏輯上的session。然后將這個session注入stream當中,如果是以消費者的角色注入進stream當中,則可以實現(xiàn)獲取數(shù)據(jù)并往外分發(fā)。假如此時服務器收到的是http-flv的請求,就可以創(chuàng)建一個邏輯上的session,并把它注入stream中,此時理論上我們可以獲得的是rtmp的數(shù)據(jù)。但我們需要的是flv的數(shù)據(jù),由于flv數(shù)據(jù)與rtmp數(shù)據(jù)相似,我們可以通過tag-header的方式非常簡單的將rtmp數(shù)據(jù)還原成flv數(shù)據(jù)。根據(jù)上述思路,在生產(chǎn)者和消費者模型中,消費者可以通過創(chuàng)建http-fake-session的形式來復用以前的分發(fā)流程并實現(xiàn)http-flv協(xié)議。我們對其進行擴展,創(chuàng)建一個http-fake-session作為生產(chǎn)者,并讓http-fake-session與一個http client進行關(guān)聯(lián),關(guān)聯(lián)之后http client負責從遠程服務器端下載數(shù)據(jù)傳遞給生產(chǎn)者,生產(chǎn)者就可以把這些數(shù)據(jù)通過分發(fā)模型分發(fā)給下面的rtmp-session。這樣也就間接實現(xiàn)了一個http回源的功能。通過上述思路我們就能夠快速地實現(xiàn)http-flv的播放與拉流。同樣,我們可以根據(jù)上述思路繼續(xù)擴展協(xié)議。假如我們在收到一個http請求之后,創(chuàng)建一個同樣的rtmp-fake-session(邏輯上的session,與網(wǎng)絡不相關(guān)),我們把它以消費者的角色插入到 stream當中。這樣就可以從stream當中獲取到需要向下分發(fā)的數(shù)據(jù)。需要注意的是:stream中最初保存的是rtmp數(shù)據(jù)而不是ts數(shù)據(jù),無法直接獲取ts數(shù)據(jù)。
1.1 http-flv在Nginx中的實現(xiàn)
基于Nginx實現(xiàn)http-flv需要注意以下幾點細節(jié):首先該實現(xiàn)復用了Nginx的分發(fā)模型以及http功能模塊。(Nginx對http協(xié)議棧的支持更加完善,包括http1.0、http1.1協(xié)議)在部分線上業(yè)務中,客戶可能需要在下載http-flv時添加后綴,按照以往的實踐邏輯我們會在代碼當中過濾后綴。如果遇見更為復雜,如修改是否需要開啟http chunked編碼的需求,我們就只能修改代碼。而如果是基于Nginx通過復用http的現(xiàn)有模塊來實現(xiàn)http-flv,我們就可以通過nginx-http-rewrite功能來實現(xiàn)這些操作。因此使用nginx-http的原生功能來開發(fā)http-flv可以帶來更多好處,如顯著降低代碼量。
在這里我曾經(jīng)看到過一種情況:即復用了http模塊,但沒有復用rtmp的分發(fā)流程。這樣就會導致我們需要將分發(fā)流程在http-flv中重新再做一遍,對業(yè)務的控制就會變得非常復雜。
舉個例子,假如此時有人請求播放,需要將消息通知給業(yè)務服務器。此時,如果rtmp與http-flv兩種協(xié)議的實現(xiàn)是分開的,那么意味著如果兩者都被觸發(fā),就需要分別向業(yè)務服務器進行匯報。于是我們就需要付出雙倍的代碼與邏輯維護工作,這無疑會顯著增加開發(fā)與維護成本。因此,最簡單的實現(xiàn)方案就是flv不做任何與業(yè)務相關(guān)的處理,僅在下發(fā)的時候進行格式轉(zhuǎn)換,相當于rtmp分發(fā)時只發(fā) rtmp格式的數(shù)據(jù),而flv分發(fā)時只需要將rtmp的數(shù)據(jù)打上flv的tag-header,然后再進行下發(fā),這樣就省去了業(yè)務層的開發(fā)。
http-flv播放實現(xiàn)
圖中展示的是rtmp的緩存對于rtmp和http-flv這兩個協(xié)議的支持。http-flv和rtmp二者共用一套緩存,其實rtmp本身傳輸?shù)木褪莊lv的數(shù)據(jù),只不過是把tag-header給拋掉了。
http-flv的下發(fā)與rtmp的下發(fā)唯一的區(qū)別點在于send函數(shù)不同:http-flv調(diào)用的是http的send函數(shù),rtmp下發(fā)時調(diào)用的是原生的send函數(shù),在下發(fā)前需要添加各自的協(xié)議頭。二者共用一塊內(nèi)存可以達到節(jié)省內(nèi)存的效果,并且實現(xiàn)業(yè)務統(tǒng)一,降低開發(fā)成本。
http-flv回源的實現(xiàn)
圖中展示的是http-flv回源在nginx中的實現(xiàn)。http-flv回源實現(xiàn)的思路與http-flv的播放實現(xiàn)思路類似:即在需要回源的時候創(chuàng)建一個http client,http client所做的事情就是把http數(shù)據(jù)下載到本地。在下載數(shù)據(jù)到本地之前http client需要先創(chuàng)建一個rtmp fake session并將其作為生產(chǎn)者注入stream當中。而后http client開始從網(wǎng)絡上下載數(shù)據(jù)并且將下載到的fIv數(shù)據(jù)拆成rtmp數(shù)據(jù)。為什么要拆成rtmp數(shù)據(jù)?這是因為rtmp的推流過來的緩存數(shù)據(jù)類型是rtmp,因此從網(wǎng)上下載到的flv數(shù)據(jù)需要做一次拆分,拆成rtmp的數(shù)據(jù),然后放入緩存。最終根據(jù)實際要求將數(shù)據(jù)轉(zhuǎn)成rtmp或flv的格式。這樣按照http-flv播放中rtmp fake session的邏輯,也就能夠快速的實現(xiàn)http-flv的回源操作。
1.2 http-ts在Nginx中的實現(xiàn)
圖中展示的是http-ts在Nginx中的實現(xiàn)。其實現(xiàn)思想與http-flv的實現(xiàn)基本一致,僅僅是在操作上有所不同,不同點在于http-ts需要一個獨立的buffer進行緩存。由于http-ts與http-flv的數(shù)據(jù)格式相差較大,對于flv數(shù)據(jù)到rtmp來說,只需要將數(shù)據(jù)拆成一個個小塊,并在前面添加一個header。即使flv數(shù)據(jù)的最一幀或者一個分塊缺少也不用補齊。
但是ts數(shù)據(jù)不同,它的要求比較嚴格,每一分塊必須為188字節(jié),其中包括ts header以及有效載荷部分。并且如果數(shù)據(jù)庫大小不足188個字節(jié),則需要補齊。而rtmp的數(shù)據(jù)塊沒有嚴格固定要求其長度大小。對于ts數(shù)據(jù)來說,要想將flv數(shù)據(jù)轉(zhuǎn)成ts數(shù)據(jù),這個過程是需要消耗一些計算量的。由于ts數(shù)據(jù)和flv的數(shù)據(jù)格式相差太大,因此在這里我們將ts的buffer與rtmp的buffer完全獨立開。但此操作并不是默認開啟的,需要在服務器中進行配置。
開啟配置后,才會將rtmp的buffer生成一份鏡像的ts數(shù)據(jù),這一部分的ts數(shù)據(jù)僅會供http-ts和hls兩個協(xié)議使用。服務器中還涉及到一個原生的hls服務,在這里我們沒有做任何的改動,而是加入了hls+的服務來使用這個buffer。無論是ts還是hls+,它們都注冊了自己的fake session,這樣做的目的是為了統(tǒng)一業(yè)務。例如在有播放請求進入時,我們需要讓業(yè)務服務器知道當前有請求產(chǎn)生。
類似這種網(wǎng)絡通知、事件通知的接口,在開發(fā)的過程中大家都希望只需要編寫一份業(yè)務數(shù)據(jù),而不是說做hls協(xié)議要針對hls播放寫一個通知,做ts協(xié)議還要針對ts再寫一份通知,這樣業(yè)務代碼會越來越龐大,最后導致服務幾乎就很難維護。
因此fakesession的作用是非常大的,其會把網(wǎng)絡層與業(yè)務層完全隔離開。即使服務器本身的下發(fā)協(xié)議不是rtmp,創(chuàng)建一個rtmp-session并掛載到業(yè)務服務器中即可。總的來說,http-ts與http-flv唯一實現(xiàn)區(qū)別就是獲取buffer的位置不同。http-flv需要從rtmp buffer獲取,http-ts則是從ts buffer中獲取。如果能理解http-flv的協(xié)議流程,那么也就不難理解http-ts的實現(xiàn)流程。
1.3 hls+在Nginx中的實現(xiàn)
圖中展示的是hls+在nginx中的實現(xiàn)。hls+與傳統(tǒng)hls不同,傳統(tǒng)hls在服務端沒有狀態(tài),服務端包含大量碎數(shù)據(jù),客戶端在不斷執(zhí)行下載,而hls+則會記錄每一個客戶端的狀態(tài)。對于如何記錄每個客戶端的狀態(tài),之前我曾嘗試通過對hls+的連接創(chuàng)建一個虛擬連接用來記錄狀態(tài)。但是發(fā)現(xiàn)業(yè)務會比較復雜,并且后期會存在很多問題,包括代碼量、bug以及維護成本等。于是更換另外一種思路,還是用fake session的方式來實現(xiàn)。利用fake session作為消費者放入,根據(jù)每次進入的http,連接,通過session ID進行綁定。
由于第1次發(fā)送hls請求時客戶端是不知道sessionID的,如果服務器獲取到一個沒有session ID的連接,則認為此客戶端為第1次進入。客戶端會接收到一個302的回復,302回復中會告訴客戶端一個新的地址,其中包含一個session ID。客戶端得到session ID之后,再次請求m3u8時,會加入session ID,服務器就可獲取相應session ID并對客戶端進行身份區(qū)分。這樣就能夠通過session ID記錄每一個客戶端的播放狀態(tài)。為什么要記錄這個狀態(tài)?這主要是因為服務器不是將數(shù)據(jù)直接寫入硬盤而是放進內(nèi)存,它需要知道每一個用戶、每一個客戶端的下載進度,并根據(jù)不同的進度從內(nèi)存中定位ts數(shù)據(jù)。
hls+和http-ts它們共用了一個 ts buffer,并且hls+是實時的從buffer中定位ts內(nèi)容。所以對于hls+來說,并沒有真正的ts數(shù)據(jù)產(chǎn)生,只是記錄每一個文件在內(nèi)存里面的偏移量。因此hls+不存在讀寫的問題,在做hls服務時,以前可能會遇到過一個問題——讀寫硬盤的瓶頸。機械硬盤的讀寫速度比較慢,普遍的解決思路就是掛載一個虛擬硬盤,將內(nèi)存映射到目錄中進行讀寫。
如果采用的是hls+的方案,就可以省去掛載的操作,對于內(nèi)存也并沒有太多的消耗。而且如果同時有hls+以及 http-ts的需求,此時對于內(nèi)存的利用率是非常高的。
2. 靜態(tài)推拉流
靜態(tài)推拉流主要是為了滿足集群化的需求。如果單臺服務器不足以支撐服務的高并發(fā)量,那么我們就需要考慮服務器的擴展性。除此之外如果用戶分散在全國各地,還需要進行服務器之間的打通。但是如果業(yè)務沒有那么復雜就可以選擇使用靜態(tài)推拉流。
靜態(tài)推拉流服務配置如上圖所示,首先看靜態(tài)拉流:首先存在一個目標源站,如果使用靜態(tài)回源,那么目標地址會被配置在配置文件當中,目標源站能隨意更改。
圖中展示的是一個簡單的靜態(tài)拉流模型:如果來自主播的數(shù)據(jù)被推流到源站A,那么我們需要保證服務器A的地址不會改變。
除此之外,如果想要構(gòu)建一套完善的流媒體系統(tǒng),則需要包含靜態(tài)拉流與靜態(tài)推流。假如有觀眾向服務器C請求播放,那么服務器C就會向服務器A拉流,無論服務器A是否存在視頻流,服務器C都會拉取。因此該模型只適用于較為簡單的業(yè)務場景。
3. 動態(tài)控制:動態(tài)回源、動態(tài)轉(zhuǎn)推、鑒權(quán)
相對于靜態(tài)推拉流的“無腦”推拉流,更適用于多數(shù)人需求的則是動態(tài)推拉流。
Nginx的RTMP服務針對每一項功能都做了不同的觸發(fā)階段。以oclp_play為例,當有人啟動播放時會觸發(fā)play消息,play消息會攜帶一項start參數(shù)。
在播放過程中,play消息依舊會被觸發(fā),只不過此時還會攜帶update參數(shù)。在play結(jié)束時也會觸發(fā)一個play消息,所攜帶的參數(shù)是down。借助這些參數(shù),我們可以實現(xiàn)向業(yè)務服務器通知請求播放以及播放的具體階段。
3.1 動態(tài)回源
推流過程也存在類似操作,推流中存在publish,同樣分為三個階段,play和publish主要應用在鑒權(quán)操作中。如果在start階段,業(yè)務服務器返回了一個404或者非200的結(jié)果,服務器就會中斷當前的play請求,publish亦是如此。除此之外, pull與push主要應用于動態(tài)拉流階段。當服務器接收到play請求,并且發(fā)現(xiàn)當前服務器里面沒有目標流,也就是說publish的流不存在,就會觸發(fā)pull的start階段。在發(fā)送start請求之后如果業(yè)務服務器返回結(jié)果為302,并且在location中又寫了一個新的rtmp地址或http-flv地址,這臺服務器就會向標記的那一臺目標服務器拉取rtmp流或fIv流,這個過程就被稱為動態(tài)拉流。
3.2 動態(tài)轉(zhuǎn)推
與動態(tài)拉流相對應的是動態(tài)推流,其理解方式與動態(tài)拉流大致相同。如果你向服務器推流,服務器會向配置好的目標地址發(fā)送start請求。如果在返回結(jié)果當中加入一個新的rtmp地址,這一臺媒體服務器就會向新的rtmp地址推流,這也就是動態(tài)推流的操作。這一切的前提是返回302的結(jié)果,如果不想將流推出,那么反饋給服務器400或其他非200,該流就會被中斷。
Oclp_stream用的比較少,僅僅在這路流創(chuàng)建與消失時被觸發(fā)。不管是play還是publish,如果只有play或publish存在,都會認為這路流的生命周期還沒有結(jié)束,只有當二者全部消失時才會被認定該路流生命周期已結(jié)束。
同樣的,如果一路流沒有被發(fā)布過而是僅僅第一次有人請求,此時也會觸發(fā)start并認為是該路流被創(chuàng)建,只不過沒有生產(chǎn)者而已。這種場景的應用比較少,只有對業(yè)務要求比較高的系統(tǒng)可能會用到這一條消息。
上圖展示了一個配置事例,主要包括查詢服務器的IP、查詢服務器play操作希望支持哪些階段等。
集群化部署依賴業(yè)務(調(diào)度)服務器,如果有回源需求則讓邊緣服務器B在oclp_hold階段向業(yè)務服務器查詢,此時業(yè)務服務器會告訴邊緣服務器B一個302地址,其中包含源地址。邊緣服務器B就會從標記出來的這一臺(媒體服務器A)拉流,從而實現(xiàn)動態(tài)回源。
動態(tài)轉(zhuǎn)推主要是為了把本地的流推出去。在CDN的服務中,不同集群負責不同的職能。例如有些集群負責錄制,有些則僅負責轉(zhuǎn)碼,此時我們希望核心機器能夠把這些需要轉(zhuǎn)碼或需要錄制的流按照需求轉(zhuǎn)接到相應集群。動態(tài)轉(zhuǎn)推非常重要,如果業(yè)務中包含這些不同的類型,就需要添加配置oclp_push去實現(xiàn)動態(tài)轉(zhuǎn)推。
3.3 鑒權(quán)
鑒權(quán)操作中,我們只會對publish或play進行鑒權(quán)。
如果play的時候反饋200就是允許播放,如果反饋403就是不允許播放,publish也是如此,通過業(yè)務服務器控制客戶某一次服務請求是否能被允許。
前端進行play或者是進行publish時,如何把鑒權(quán)的token帶過來?
主要通過變量:args=k=v&pargs=$pargs在向外發(fā)送play查詢時,如果加入args=k=v&pargs=$pargs ,發(fā)請求時會帶上這些參數(shù),這樣就可以將rtmp的全部自定義參數(shù)傳遞過來。
4. 多進程:進程間回源
多進程問題在原生的nginx rtmp中有很多bug,現(xiàn)在的做法是通過共享內(nèi)存記錄下每個進程上的stream列表。如果play的進程沒有流,則查詢stream列表,并通過unix socket向目標進程回源拉流。除此之外,進程間的回源不會觸發(fā)ocl_playoclp_publish oclp_pull消息。
5. 更多操作說明
PingOS:https://github.com/im-pingo/pingos
【線上分享預告】大規(guī)模互動直播與在線視頻用戶體驗優(yōu)化實踐
伴隨疫情突發(fā),在線教育、視頻會議、互動直播與在線視頻需求被推上風口,如何在大規(guī)模、高并發(fā)視頻需求下為用戶提供更流暢、更高清、低延遲的直播與視頻觀看體驗?如何為線上教育賦予電子白板功能優(yōu)化用戶學習體驗與教學效果?如何激發(fā)用戶在線視頻觀看興趣、提升用戶視頻交互體驗?
3月29日19:00,LiveVideoStack攜手UCloud RTC首席架構(gòu)師王立飛、學而思網(wǎng)校資深架構(gòu)師趙文杰、嗶哩嗶哩資深研發(fā)工程師唐君行,共同探索大規(guī)模互動直播與在線視頻背后的底層技術(shù)架構(gòu),分享用戶體驗優(yōu)化實踐經(jīng)驗。
- 《URTC萬人連麥直播的優(yōu)化實踐》 王立飛 UCloud RTC首席架構(gòu)師
- 《線上/線下教育中白板技術(shù)優(yōu)化》 趙文杰 學而思網(wǎng)校資深架構(gòu)師
- 《高能進度條:視頻交互體驗優(yōu)化》 唐君行 嗶哩嗶哩資深研發(fā)工程師
公開課預約入口:
大規(guī)模互動直播與在線視頻用戶體驗優(yōu)化實踐-LiveVideoStack&UCan公開課第21期?mudu.tv總結(jié)
以上是生活随笔為你收集整理的ts获取服务器数据_基于Nginx的媒体服务器技术-线上公开课的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: 氢燃料汽车不认输 日本投资15万亿日元豪
- 下一篇: python3.8.2中文手册chm_s
