單兵雙機作戰(zhàn)也能完成的純OBS游戲比賽多視角導(dǎo)播臺(以Apex Legends為例)

讓我們思考一下曾經(jīng)看過國內(nèi)外各種大大小小的賽事,從ALGS到星空杯,甚至是沒有解說的VeryApex Open訓(xùn)練賽,為了確保觀眾能夠看到精彩的畫面,總會安排多個OB視角,在主臺進行包裝和匯總,在發(fā)生交火時立即切換到捕捉到了畫面的視角:

這篇文章是一個手記心得,記錄了我如何在沒有團隊協(xié)作的情況下完成這種多時間的導(dǎo)播式直播,帶給觀眾酣暢淋漓的觀賽體驗(指比ALGS導(dǎo)播切得好、切得快)。
多視角導(dǎo)播體系的問題在于,大型賽事直播通常OB機位都在同一個局域網(wǎng)內(nèi),他們有太多太多方法,無論是基于采集卡還是基于NDI,都能夠做到無延遲、高質(zhì)量的視頻流聚合,但大家伙現(xiàn)在天各一方,中間相隔的網(wǎng)絡(luò)太過于復(fù)雜了,如何跨越各種礙事的NAT把所有機位的視角聚集起來成為執(zhí)行的難點。

條件梳理
我需要先在這里列舉一下這個體系想要完成的需求,以及擁有的條件:
需求
有一個導(dǎo)播臺,將最多5個OB位聚合到一個屏幕上
解說需要根據(jù)導(dǎo)播臺輸出的Mainstage畫面進行解說
將Mainstage畫面和解說語音送到直播平臺供觀眾觀看
OB與總臺異地,受復(fù)雜NAT和上行帶寬限制
OB的計算機基礎(chǔ)參差不齊,需要讓他們都能弄懂方案
OB位有時候會兼職解說
導(dǎo)播臺自己甚至要輸出一路Apex視角
導(dǎo)播臺自己甚至有可能也要充當(dāng)一下解說
導(dǎo)播臺在推流的同時可能還要順便錄像
整套系統(tǒng)要在免費的非商用軟件完成
條件
有一臺性能足夠強大的電腦,筆者是5900X + 64GB RAM + RTX3090
有能夠使用的多路音頻輸出通道,筆者是Motu M4聲卡 + Monster Audio,但是也可以通過Voice Meter等軟件純虛擬跳線實現(xiàn)
有2個或者以上的編碼加速單元,筆者有NVENC + 一臺核顯筆記本的QuickSync,否則運行這套系統(tǒng)編碼器可能會過載,推薦Intel Non F CPU + 4070Ti以上的N卡,或者雙N卡(沒有測試過,理論上可行)
一臺服務(wù)器,然而帶寬和計算能力都不高,也許你的服務(wù)器平常是用來托管網(wǎng)頁之類的
NAT比較優(yōu)秀的家寬,或者有IPV6可用,上行帶寬至少50Mbps
運營一個基于P2P打洞的內(nèi)網(wǎng)穿透方案,比如Zerotier或者Tailscale/Headscale
快速穩(wěn)定的LAN環(huán)境,比如1000Mbps有線或者WiFi6

系統(tǒng)架構(gòu)
這個系統(tǒng)的核心架構(gòu),可以用“找補”來形容。一開始只聚合多機位,后來再考慮解說視角,處理完后又注意到太子流的音頻回響問題,通過基礎(chǔ)架構(gòu)之上不斷地patch以完成最終目的。

這套系統(tǒng)絕大多數(shù)消耗算力和需要進行工程處理的部分都在本機完成,甚至連OSSRS都搭建在本機。
具體來說,
各個OB視角通過自己安裝OBS客戶端、添加游戲源和程序聲音捕捉來完成自己視角的采集,并通過IPV6直連或者Zerotier內(nèi)網(wǎng)將RTMP流送到本機的SRS上
OBS通過向localhost抓取OB源,和自己機器上的Apex視角整合到一起,做一些排版和切換動畫工作,輸出聚合太子流
解說通過Zerotier或者IPV6直連,非Apple平臺使用SRS控制臺,Apple平臺使用VLC,以完成最低延遲低獲取到太子流并進行解說
把解說Kook頻道和本機的麥克風(fēng)音頻通過NDI發(fā)送到筆記本,把太子流RTMP也發(fā)送到筆記本,進行QSV二壓,并送到直播平臺

OB視角傳輸方式的選擇
受制于上行帶寬的畫面?zhèn)鬏斒且粋€trade-off的藝術(shù),大概就是畫質(zhì)、延遲、帶寬組成的一個不可能三角:

在項目開始之前我本來打算嘗試RTMP串流、WebRTC串流以及NDI傳輸,發(fā)現(xiàn)最后可選的手段只有高度優(yōu)化的RTMP轉(zhuǎn)發(fā)服務(wù)。
NDI
NDI無疑是Ethernet載體最優(yōu)秀的視頻流傳輸方案之一,幾乎無延遲、畫質(zhì)損失肉眼不可感知,非常適合專業(yè)工作流,我本來都要選擇它了,直到我在我的PC和筆記本之間的視頻流傳輸測試發(fā)現(xiàn)1080P60會占用150Mbps以上的網(wǎng)絡(luò)帶寬,這種事情在外網(wǎng)是無法實現(xiàn)的。再加上NDI已經(jīng)和NVIDIA構(gòu)成合作,支持(并且默認(rèn)調(diào)用)N卡硬解NDI,本身直播推流NVENC就需要占用一部分顯卡資源,運行Apex更是消耗大戶,顯卡沒法承受這種強度的壓力。因此無論從性能還是帶寬來看,NDI方案都只能直接告吹。
WebRTC
WebRTC筆者之前接觸過開發(fā),它實在是太誘人了,使用Webcam對話的時候延遲甚至不可感知,如果能用WebRTC來完成OB位的畫面?zhèn)鬏斀^對無敵。
然而現(xiàn)實給了我狠狠的3棒子:
即使SRS支持該協(xié)議,WebRTC目前無法在OBS生態(tài)中推流,而且協(xié)議決定了推流強制需要HTTPS,而我們要上域名HTTPS必然會經(jīng)過備案環(huán)節(jié),浪費時間并且難以成功。
OBS目前沒有任何辦法將WebRTC流作為媒體源輸入
WebRTC本身的設(shè)計目標(biāo)并不是用于高保真直播,進而通過測試發(fā)現(xiàn)在高分辨率高碼率高幀率回放時,會出現(xiàn)畫面撕裂錯位/被抽幀成30fps等問題,高保真視頻流有點為難這個為視頻會議而生的協(xié)議了
還有第4點,成熟的WebRTC直播方案現(xiàn)在都被各大商業(yè)公司握在手里等著我們給他們砸錢。
打通直連網(wǎng)絡(luò)
為什么打通直連網(wǎng)絡(luò)這么重要?因為我把SRS部署在本機上。
為什么要把SRS部署在本機上?因為我手頭現(xiàn)有的服務(wù)器無論是地段、性能還是帶寬配置都不足以提供RTMP轉(zhuǎn)發(fā)服務(wù),唉,唉唉。
加上我的服務(wù)器本來就跑了一個Zerotier Moon,PC性能又過剩,那為什么不把東西架在我的PC上,然后讓他們調(diào)通內(nèi)網(wǎng)穿透,直接訪問我機器的服務(wù)呢?
事實上我也這么做了,招聘并確定了幾個OB后,我寫了一篇教程手把手地教他們怎么加入我的Zerotier網(wǎng)絡(luò),并且完成推流、解說、遠(yuǎn)程控制等等工作。照顧到可能有些朋友計算機基礎(chǔ)一般,我甚至在搭建moon的時候省略了cmd操作,直接把moon配置文件給他們,讓他們通過拷貝法部署:

事實上,當(dāng)真正部署好網(wǎng)絡(luò)之后,才發(fā)現(xiàn)NAT這個東西真的很復(fù)雜。
有的人在校園網(wǎng)里搭了一個路由器,結(jié)果古早年間的路由器不支持IPV6,即使Zerotier搭建好了能聯(lián)通也被迫走IPV4鏈路產(chǎn)生不必要的多跳;
有的人的寬帶網(wǎng)IPV4、IPV6齊全,但是Zerotier搭建好之后發(fā)現(xiàn)他無論通過什么方式都沒法訪問我的PC上的任何服務(wù),甚至ICMP Ping也超時(懷疑UDP出口被限制)。我遠(yuǎn)程協(xié)助摸索了半小時發(fā)現(xiàn)Zerotier網(wǎng)絡(luò)報廢,最后IPV6地址直連反而能跑得通;
有的人讀完了我的教程之后下載了moons.d,不選擇復(fù)制文檔里的路徑,毅然決然地手敲目錄,最后把moon放在了Program Files (x86)\Zerotier One里,發(fā)現(xiàn)沒有粘貼權(quán)限。然后問我“文檔里的Programdata文件夾我去C盤看沒有,我要不要手動創(chuàng)建一個?”
總之,歷經(jīng)千辛萬苦,所有OB位的網(wǎng)絡(luò)都已經(jīng)連通,所有OB都能夠訪問我的SRS控制臺,進而能夠進行后續(xù)的推流工作。

傳輸延遲的優(yōu)化工作
傳輸延遲很大一部分來自于RTMP協(xié)議本身的包裝和傳輸開銷,在未優(yōu)化的情況下默認(rèn)推流延遲可能高達十幾秒,我在兩年前曾經(jīng)頂著這樣的延遲為部門開過一個游戲合家歡直播互動會,單向傳播(主播玩游戲給觀眾看)體驗尚可,但那一晚我忘不了的是互動環(huán)節(jié),主播-轉(zhuǎn)發(fā)-觀眾帶來的十幾秒延遲以及部分主播掛著直播頻道產(chǎn)生的回聲卷積帶來的地獄級糟糕體驗。
那時候我的推流用的是阿里云的直播服務(wù)。所以這一次我吸取教訓(xùn),痛定思痛,決心盡我最大所能將延遲降到最低。
好在SRS考慮到了這個需求,官方就寫了一個非常詳細(xì)的文檔(doc/low-latency)來科普并且教開發(fā)者與RTMP延遲相關(guān)的參數(shù)以及如何構(gòu)建一個最低延遲直播配置。通過對Merged-Read、Merged-Write、GOP-Cache、Max Queue Length、TCP delay等參數(shù)的更改,使SRS能夠達成理想情況下(網(wǎng)絡(luò)傳輸時間和渲染時間都近似為0)RTMP單向推流-解碼協(xié)議開銷能夠降低到0.65s。

編碼延遲的優(yōu)化工作
編碼延遲的優(yōu)化體現(xiàn)在OBS編碼器的設(shè)置上,我寫了一篇詳盡的文章教他們?nèi)绾闻渲肙BS,包括搭建純凈流場景和優(yōu)化編碼器,在這里直接引用:
引用之前我先自裁,下面的編碼器教學(xué)教他們使用high profile,這會導(dǎo)致GOP的增加。相比使用main和baseline配置,high配置會讓延遲增加0.25s。
另外,將最大b幀從2調(diào)整到能夠帶來大約0.1s的提升。
確保你的畫布尺寸是1080P@60fps

??!接下來的編碼參數(shù)教程里,如果你在XX大學(xué)使用校園網(wǎng)推流,那么校園網(wǎng)的上行帶寬是低于10000Kbps的,你需要適當(dāng)降低碼率,否則會導(dǎo)致大量丟幀?。?/strong>
根據(jù)熱心校友的測試,暑假期間XX宿舍校園網(wǎng)支持的最高串流碼率在8000Kbps附近
我們推薦編碼器使用的順序是QuickSync >> NVENC >>>>>>?x264 >>>>>>>>>>>>>AMF
1. 假設(shè)你使用CPU軟編碼,那么我推薦使用CBR、10000碼率、CPU預(yù)設(shè)veryfast、配置high、微調(diào)zerolatency

2. 假設(shè)你是有核心顯卡的Intel系統(tǒng),那么我強烈推薦使用QuickSync H.264編碼器,CBR 10000Kbps碼率、目標(biāo)Speed、配置High、延遲ultra-low,其它看情況調(diào)整。

3. 假設(shè)你有一張GTX1660或者以上的顯卡(在臺式機平臺,筆記本我不好說,因為沒試過),那么推薦使用NVENC H.264編碼,CBR 10000Kbps碼率,預(yù)設(shè)P2(不用P1是因為Nvenc畫質(zhì)稍差),調(diào)節(jié)超低延遲,單次編碼,配置High

關(guān)于音頻參數(shù),我推薦碼率打到192Kbps,如果你是一個對音質(zhì)有追求的人,你也可以拉到320Kbps,但是不建議低于160Kbps。
如果你的電腦性能比較捉急,那么可以嘗試在推流時關(guān)閉預(yù)覽:在非工作室模式下對視頻窗口右鍵-將開啟預(yù)覽的勾勾去掉。
AMD顯卡的玩家可以準(zhǔn)備/re了,直播生態(tài)與你無緣,AMF做了十年一直是一坨,如果你是Intel non F CPU請選擇2方案,如果是Intel F或者Ryzen CPU請嘗試1方案,發(fā)現(xiàn)性能較差(推流掉幀/游戲無法滿足60fps)的話請升級CPU或者換一張N卡??
解碼延遲的優(yōu)化工作
解碼優(yōu)化的工作來源于最后的聯(lián)調(diào)。第一次真實環(huán)境的測試?yán)镂彝ㄟ^對時法發(fā)現(xiàn),無論如何【廣州—(RTMP)→深圳—(RTMP)→廣州】(即OB視角→PC聚合流→主視角送給解說)的推流信號都有高達8秒的延遲,這個結(jié)果對于Apex這種節(jié)奏非常快的游戲是絕對不可容忍的。既然編碼和傳輸?shù)难舆t已經(jīng)盡力了,那么唯一能夠調(diào)整的也只有解碼端了。
經(jīng)過測試我發(fā)現(xiàn),VLC雖然全平臺通用,但是為了回放質(zhì)量默認(rèn)情況下自帶一個playback buffer,使得接收端產(chǎn)生約0.5s的延遲。VLC目前這個buffer支持的最低值是100ms,即有0.1s延遲不可避免,但預(yù)設(shè)的ultra-low latency模式也僅把buffer縮小到了333ms,也許100ms確實不適合絕大多數(shù)情況下的網(wǎng)絡(luò):

值得一提的是,SRS唯一的低延遲回放方案只有VLC,因為iOS的瀏覽器內(nèi)核完全不支持Flash,因而任何基于flv.js的web端rtmp播放器都無法運行。
實際測試,無論是SRS控制臺預(yù)覽亦或是其它任何基于flv.js方案的rtmp播放器,都會固定比“最低延遲”設(shè)定的VLC快大約0.5秒。

可這才解決了0.5s,我和OB機器的TCP ping大約在5-20ms,雙向推流延遲(0.65協(xié)議+0.02網(wǎng)絡(luò))*2+單客戶端回放0.5=1.84s,那么我按照保守估計延遲大約2s,這離8秒中間還差了6秒去哪里了?
排除了所有的可能性后,可能性僅剩下OBS的媒體源了。當(dāng)我把目光轉(zhuǎn)移到OBS媒體源并進行測量后,驚奇地發(fā)現(xiàn)OBS媒體源的延遲大得就離譜,即使你設(shè)定了0MB的網(wǎng)絡(luò)緩存也有差不多4s的延遲:

經(jīng)過大量的嘗試以后,我發(fā)現(xiàn)在“媒體源”里的“FFMpeg選項”欄里無論填寫任何與ffmpeg降低回放延遲的參數(shù),比如
等,均無任何效果,并且我去GitHub轉(zhuǎn)了一圈obs的源碼,意味著我們必須要尋找其它的替代方案。
在網(wǎng)絡(luò)上搜索了一大圈,發(fā)現(xiàn)有一個obs-gstreamer插件也許有用,它借助gstreamer構(gòu)建管道通過參數(shù)優(yōu)化能夠完成非常非常低延遲的rtmp回放,據(jù)稱效果能夠逼近flv.js。

但是我經(jīng)過多次嘗試,更換搭配了各個gstreamer-mingw版本,在確保自己的PATH環(huán)境絕對沒有問題,能夠任意目錄運行g(shù)streamer組件的情況下,安裝obs-gstreamer組件仍舊報錯無法加載,也許是和obs29不太兼容。

那么我們可知的能夠播放rtmp的來源只剩下VLC了,死馬當(dāng)活馬醫(yī):

這下馬還真活了,VLC里有個網(wǎng)絡(luò)緩存的選項,與我們在APP里見到的如出一轍,它最低支持100ms,即便如此也只比flv.js慢了0.3s,相比原生媒體源的4s快了10倍多?。ㄉ厦鎸Ρ葓D的“優(yōu)化輸入時間”用的就是VLC源)
這就收回前面的伏筆了,為什么我要把SRS搭載本機上。因為我能夠100%保證在我機器執(zhí)行最多的回放行為是延遲最低并且絕對不會丟包的,任何流從localhost拉下來都能把buffer縮短到可允許的極限工況,比如在這里的100ms,我相信如果VLC允許,我能夠再縮短到50ms甚至更低。
如果把SRS架在LAN以外的區(qū)域,我還真不能夠保證100ms的buffer能夠穩(wěn)定回放。
至此,全鏈路優(yōu)化完成,從OB推流到解說觀看這一段對延遲要求非常高的鏈路通過編碼優(yōu)化、網(wǎng)絡(luò)優(yōu)化、回放優(yōu)化,將延遲從8秒縮短到能夠符合基本交互和解說需求的2秒。至于后面的聚合解說語音推流到平臺的延遲,再到平臺觀眾觀看的延遲隨它去吧。

為什么單機體系宣告失敗了
因為編碼器過載。
我使用rtmp-multiple-output插件,試圖將我的主視角流同時推送到SRS和直播平臺,然后發(fā)現(xiàn)這個插件是雙編碼方案,同時運行會導(dǎo)致強如3090的NVENC也嚴(yán)重過載,即使分一路編碼去x264也無法緩解,游戲幀數(shù)難以達到165、推流直播瘋狂掉幀,并且調(diào)試后發(fā)現(xiàn),將任意一路編碼停下來都能夠緩解直播和游戲的掉幀問題,就是沒辦法雙編碼輸出。
然后我在考慮轉(zhuǎn)發(fā)直播流:Nginx有內(nèi)置的純網(wǎng)絡(luò)協(xié)議方法,但在Windows上部署Nginx未免有些脫褲子放屁,而且跟SRS功能沖突并且又需要大量的時間寫config調(diào)試;SRS的forward方法與vhost綁定,純IP使用的時候因為只有一個_defaultVhost_似乎無法完成轉(zhuǎn)發(fā)(因為SRS的forward是面向edge或者集群的方案,與我們的工況需求并不一致)。
我們要看哪里解說呢?總不能在平臺吧,延遲太大了……
把我問懵了,對啊,解說總得看一個總臺吧,對著自己的視角解說那和以前的各自操作模式又有什么不一樣呢?
太子流在這個時候才誕生,才產(chǎn)生后續(xù)的8秒回放延遲問題,可伴隨而來出問題的不只有回放。
假設(shè)解說兼職OB只有1臺機器,那么解說需要調(diào)整Apex Legends使得:
切換到后臺時不掉幀
游戲聲音與自己的講話/解說頻道語音獨立開來采集
切換到后臺時不靜音
問題2能夠通過OBS29新增的應(yīng)用程序音頻采集(測試)來源做到單獨采集Apex的聲音,問題3能夠通過開啟Apex設(shè)置-聲音-背景聲音完成,但是問題1無解。
Apex出于對硬件友好的考量,切換到后臺之后會強制鎖在10fps以降低顯卡工作強度,目前不支持任何的設(shè)置/啟動項/CFG參數(shù)進行修改,導(dǎo)致了解說兼職OB的方案直接破產(chǎn)。這意味著提供OB的機器必須時刻保持Apex在前臺。如果想要繼續(xù)兼職,必須要掏出一個副屏或者副設(shè)備,或者直接放棄供給OB位,直接做純解說。
在此之后獨立出來了太子流供解說專用。但在第二次聯(lián)調(diào)測試的時候,有個解說兼OB一直在納悶他自己講話會有回聲被打斷,調(diào)了半天OBS以為鬧鬼了,最后我說聲音是我這里推出去的。
然后我想了一下,對啊,給解說看的太子流就應(yīng)當(dāng)是沒有解說音頻的!先拋開有解說使用外放導(dǎo)致回聲卷積的可能性不談,解說說著說著被自己的聲音打斷也確實非常影響體驗。所以我們要把解說語音剝離,只有推送給直播平臺的流加上語音。
于是在此處我發(fā)揮出了我的聲卡機架優(yōu)勢,把KOOK輸出到獨立的聲音通道中,并在OBS引入為全局音頻設(shè)備,把他們掛到軌道2用于錄像和后期。因為直播只能輸出1個音軌,因此語音軌道將不會被送往太子流。


遠(yuǎn)程控制權(quán)
OB到導(dǎo)播臺的畫面仍然存在0.75-1s的延遲,怎么樣才能彌補這個延遲,盡量不錯過第一時間的戰(zhàn)斗場面呢?畢竟RTMP的開銷0.65s,但機器之間的ping只有5ms,有辦法通過視頻以外的方法進行補救。
對的,就是websocket。我開啟了本機的obs-websocket端口,并且允許ob在語音和導(dǎo)播(我)溝通確認(rèn)并且自己先手通過websocket client切換場景:

在介紹混音與動效之前,應(yīng)該首先展示一下主視角的布局:

視頻畫幅2400*1080,精確地塞下了1個主畫面和4個副畫面,通過切換場景來放大右邊4個不同的機位,使用move-transition插件賦予動態(tài)縮放效果,以給觀眾連貫的變化感。
我喜歡用Circle運動曲線,在offset 2.00的情況下,500ms的動效觀感與我第一喜歡的Material Design動畫曲線非常類似,靈動且優(yōu)雅。
這種場景搭配方法讓每個場景所有媒體源都在播放,熟悉OBS的朋友都知道,這種情況下混音器的調(diào)整是與場景獨立的,我無法做到在原生情況下把某個畫面縮回去,它的聲音隨之變小,反之亦然。
但是OBS社區(qū)的朋友洞察到了這個需求,他們之中有個人做了一個Lua腳本,能夠記住每個場景的混音器音量設(shè)定,并且隨著場景自動切換:

這個腳本就是這套系統(tǒng)的點睛之筆,使最后一個混音難題成為了可能。
系統(tǒng)和賽事成果
這套系統(tǒng)我投入了大量的時間搭建和優(yōu)化,從一開始的天方夜譚經(jīng)過幾十個小時的優(yōu)化,達到真正能落地實戰(zhàn)的程度,已經(jīng)摩拳擦掌蓄勢待發(fā)。
就是比賽到現(xiàn)在還沒湊滿60個人,沒開起來,群里70來個人,每天發(fā)公告催他們填表報名打比賽,到現(xiàn)在就收到了30個選手的問卷??