程夢(mèng)龍 姜忠鼎
(復(fù)旦大學(xué)上海市數(shù)據(jù)科學(xué)重點(diǎn)實(shí)驗(yàn)室 上海 201203) (復(fù)旦大學(xué)軟件學(xué)院 上海 201203)
近年來(lái),隨著科技水平的提高以及圖形技術(shù)的進(jìn)步,虛擬現(xiàn)實(shí)得到了快速的發(fā)展。虛擬現(xiàn)實(shí)使用計(jì)算機(jī)模擬生成可交互式的虛擬三維場(chǎng)景,向用戶提供沉浸式的環(huán)境,給人身臨其境的感覺(jué)[1]。相比于頭戴式的虛擬現(xiàn)實(shí)頭盔,使用多投影或多顯示器拼接而成的環(huán)幕系統(tǒng)能夠支持多人同時(shí)交互,并且往往具有更大尺寸、更高分辨率的顯示表面,在教育、娛樂(lè)和虛擬仿真等領(lǐng)域具有重要的應(yīng)用價(jià)值[2]。
傳統(tǒng)環(huán)幕系統(tǒng)的畫(huà)面源與顯示環(huán)境耦合度較高,外部應(yīng)用需要修改自身代碼才能實(shí)現(xiàn)在環(huán)幕的顯示,環(huán)幕系統(tǒng)不具有通用性。本文提出一種基于多通道視頻同步采集的多投影環(huán)幕顯示系統(tǒng),系統(tǒng)使用視頻采集卡對(duì)畫(huà)面源內(nèi)容進(jìn)行采集,通過(guò)網(wǎng)絡(luò)傳輸畫(huà)面內(nèi)容到多投影環(huán)幕呈現(xiàn)。渲染與顯示的分離降低了畫(huà)面源與環(huán)幕的耦合度,提高系統(tǒng)的通用性。
關(guān)于使用渲染顯示分離的方法降低系統(tǒng)耦合度的思想,目前已有許多相關(guān)的研究與應(yīng)用。Lamberti等[3]在2007年提出基于視頻流實(shí)現(xiàn)復(fù)雜3D圖形在移動(dòng)設(shè)備中呈現(xiàn)的方案,使用集群主機(jī)渲染復(fù)雜3D模型,將渲染畫(huà)面以MPEG視頻流的形式發(fā)送給呈現(xiàn)客戶端,解決移動(dòng)端圖形處理能力不足的問(wèn)題。文獻(xiàn)[4-5]提出使用遠(yuǎn)程渲染的方式來(lái)實(shí)現(xiàn)3D視頻在移動(dòng)設(shè)備的呈現(xiàn),提出基于代理的框架,3D視頻流在代理中渲染,然后將渲染畫(huà)面通過(guò)無(wú)線網(wǎng)絡(luò)發(fā)送給移動(dòng)設(shè)備顯示。文獻(xiàn)[6]提出使用遠(yuǎn)程渲染的方式來(lái)保護(hù)畫(huà)面源不被篡改。文獻(xiàn)[18]提出使用渲染顯示分離的方法實(shí)現(xiàn)3D地圖在移動(dòng)設(shè)備的呈現(xiàn)。云技術(shù)的發(fā)展也使得云游戲逐漸變?yōu)楝F(xiàn)實(shí)[7],國(guó)內(nèi)外也已有一些云游戲系統(tǒng),如GamingAnywhere[8]、UBCGaming[9]和文獻(xiàn)[10]提出的游戲云等。云游戲的基本思想為:在云端遠(yuǎn)程運(yùn)行游戲應(yīng)用,將渲染畫(huà)面壓縮,以像素流的形式傳輸給客戶端,如手機(jī)等;客戶端對(duì)畫(huà)面數(shù)據(jù)進(jìn)行解碼呈現(xiàn),客戶端的交互指令通過(guò)網(wǎng)絡(luò)發(fā)送給云端主機(jī),從而模擬本地場(chǎng)景交互。云游戲能夠大大降低對(duì)客戶端的設(shè)備性能要求,使得玩家能夠隨時(shí)隨地享受高質(zhì)量的游戲體驗(yàn)[11]。文獻(xiàn)[19]提出一種使用分布式渲染的方式來(lái)降低系統(tǒng)交互延遲的方案,通過(guò)服務(wù)器提供的背景圖和客戶端GPU加速技術(shù)對(duì)畫(huà)面進(jìn)行變形與插值,降低交互延遲,提高云游戲的用戶體驗(yàn)。隨著5G時(shí)代的來(lái)臨,遠(yuǎn)端渲染傳輸?shù)姆绞綍?huì)得到更一步的發(fā)展與推廣。
在本文系統(tǒng)中,畫(huà)面源以像素流的形式傳輸至環(huán)幕呈現(xiàn),內(nèi)容類型與呈現(xiàn)流程無(wú)關(guān),因而支持多種類型的畫(huà)面源,例如視頻播放應(yīng)用、游戲、可視化和VR仿真等。畫(huà)面源只需提供符合環(huán)幕顯示規(guī)范的輸出畫(huà)面,即可實(shí)現(xiàn)在多投影環(huán)幕顯示系統(tǒng)中的呈現(xiàn)。
本文系統(tǒng)的設(shè)計(jì)與實(shí)現(xiàn)可分為兩個(gè)模塊:多通道視頻采集模塊與集群顯示模塊。多通道視頻采集模塊負(fù)責(zé)畫(huà)面采集、壓縮與傳輸;集群顯示模塊負(fù)責(zé)畫(huà)面接收、解碼與顯示。將一款使用Unity游戲引擎開(kāi)發(fā)的第一人稱射擊游戲使用本系統(tǒng)進(jìn)行實(shí)驗(yàn),結(jié)果表明本文系統(tǒng)具有可行性與實(shí)用性,并且畫(huà)面延遲較低。
多通道視頻采集包含多通道視頻同步采集、視頻幀壓縮與重組、視頻幀傳輸三個(gè)部分。
為了充分利用多核主機(jī)的計(jì)算性能,降低畫(huà)面延遲,提高系統(tǒng)的運(yùn)行效率和數(shù)據(jù)吞吐量,采集服務(wù)器使用多線程來(lái)完成多通道視頻采集工作。相比于單線程方法,多線程的流水線架構(gòu)能夠提高并發(fā)性,降低視頻畫(huà)面從采集到傳輸?shù)目偤臅r(shí),從而降低整個(gè)系統(tǒng)的畫(huà)面延遲。
系統(tǒng)啟動(dòng)后,采集模塊創(chuàng)建采集線程、壓縮線程、重組線程、網(wǎng)絡(luò)傳輸線程四種類型的線程,并初始化采集緩沖區(qū)、壓縮緩沖區(qū)、發(fā)送緩沖區(qū)三種類型的數(shù)據(jù)緩沖區(qū)。多線程之間使用數(shù)據(jù)緩沖區(qū)進(jìn)行數(shù)據(jù)的共享,數(shù)據(jù)緩沖區(qū)通過(guò)加鎖的方式保證多線程訪問(wèn)的互斥性。多線程流程圖如圖1所示。
圖1 多線程流程圖
多通道視頻同步采集包含兩個(gè)關(guān)鍵步驟:通道視頻幀數(shù)據(jù)的正確讀取,多通道視頻幀的采集同步。本文系統(tǒng)使用Magewell Pro Capture Quad HDMI Video Capture Card視頻采集卡,每個(gè)采集卡支持4通道高清視頻采集[12],以及Magewell提供的采集卡SDK(MWCaptureSDK 3.3.1.1004)來(lái)完成視頻幀的讀取與同步。為了減少視頻幀大小,提高視頻幀壓縮速度,本文系統(tǒng)使用I420顏色格式采集視頻幀。
采集模塊啟動(dòng)后創(chuàng)建一個(gè)采集線程,采集線程額外創(chuàng)建信號(hào)監(jiān)聽(tīng)線程與視頻幀讀取線程兩個(gè)線程,分別負(fù)責(zé)監(jiān)聽(tīng)通道狀態(tài)和從通道讀取視頻幀數(shù)據(jù)。
信號(hào)監(jiān)聽(tīng)線程包含以下三個(gè)基本步驟:
步驟1使用MWRegisterNotify函數(shù)為每個(gè)通道注冊(cè)通知事件hNotifyEvent。當(dāng)通道狀態(tài)發(fā)生改變時(shí),hNotifyEvent變?yōu)镾ignal狀態(tài)。
步驟2在線程主循環(huán)內(nèi)使用WaitForMultipleObjects函數(shù)等待所有通道的通知事件hNotifyEvent。當(dāng)某個(gè)通道狀態(tài)發(fā)生變化時(shí),使用MWGetNotifyStatus函數(shù)獲取通知事件的具體類型。
步驟3當(dāng)通知事件類型包含MWCAP_NOTIFY_VIDEO_FRAME_BUFFERED,即有新的視頻幀數(shù)據(jù)時(shí),設(shè)置本通道的待讀取狀態(tài)為T(mén)rue。當(dāng)所有通道的待讀取狀態(tài)為T(mén)rue時(shí),設(shè)置m_dwCapMask為T(mén)rue,表示所有通道數(shù)據(jù)已準(zhǔn)備好。
視頻幀讀取線程包含以下三個(gè)基本步驟:
步驟1使用MWRegisterTimer函數(shù)注冊(cè)定時(shí)事件hTimerEvent。每隔一段時(shí)間,hTimerEvent變?yōu)镾ignal狀態(tài)。
步驟2在線程主循環(huán)內(nèi)使用WaitForMultipleObjects函數(shù)等待定時(shí)事件hTimerEvent。然后判斷m_dwCapMask是否為T(mén)rue,即是否所有通道的數(shù)據(jù)均已準(zhǔn)備完成。
步驟3當(dāng)m_dwCapMask為False時(shí),不執(zhí)行數(shù)據(jù)讀取等操作,直接等待下一個(gè)定時(shí)事件。當(dāng)m_dwCapMask為T(mén)rue時(shí),使用MWCaptureVideoFrameToVirtualAddress函數(shù)依次從所有通道讀取視頻幀數(shù)據(jù)至指定的內(nèi)存區(qū)域。視頻幀讀取完成后,為所有通道視頻幀賦予相同的幀號(hào),然后將所有通道采集到的視頻幀數(shù)據(jù)放入采集緩沖區(qū)等待壓縮,最后將m_dwCapMask設(shè)置為False。
采集的原始視頻幀數(shù)據(jù)量較大,直接傳輸會(huì)占用較多的帶寬。為了減少網(wǎng)絡(luò)帶寬占用,提高視頻幀傳輸效率,對(duì)采集的視頻幀進(jìn)行壓縮,減少視頻幀數(shù)據(jù)大小。本文系統(tǒng)使用LibJpegTurbo插件[13]對(duì)視頻幀進(jìn)行JPEG壓縮。
采集模塊啟動(dòng)后,創(chuàng)建壓縮線程池,壓縮線程池中包含與通道數(shù)量相同個(gè)數(shù)的壓縮線程。在壓縮線程主循環(huán)中,從采集緩沖區(qū)獲取一幀視頻幀,對(duì)視頻幀數(shù)據(jù)進(jìn)行壓縮得到壓縮視頻幀,將壓縮視頻幀數(shù)據(jù)放入壓縮緩沖區(qū)等待重組,然后進(jìn)入下一個(gè)獲取數(shù)據(jù)、壓縮數(shù)據(jù)、放入壓縮緩沖區(qū)循環(huán)。壓縮視頻幀幀號(hào)與壓縮前的視頻幀幀號(hào)相同。
為了保證多通道畫(huà)面顯示的同步性,在壓縮視頻幀傳輸前需要將多個(gè)通道的壓縮視頻幀重組。重組線程根據(jù)幀號(hào)將所有通道同一幀號(hào)的壓縮視頻幀打包為重組視頻幀,具體重組過(guò)程包含以下三個(gè)步驟:
步驟1根據(jù)采集線程記錄的幀號(hào)序列,獲取下一個(gè)等待重組的幀號(hào),稱為目標(biāo)幀號(hào)。
步驟2依次從壓縮緩沖區(qū)中獲取各個(gè)通道的壓縮視頻幀隊(duì)列中對(duì)應(yīng)目標(biāo)幀號(hào)的壓縮視頻幀。若某個(gè)通道的視頻幀還未壓縮完成,則等待直至收集齊所有通道目標(biāo)幀號(hào)的壓縮視頻幀。
步驟3將所有通道的壓縮視頻幀數(shù)據(jù)拷貝至重組視頻幀,重組視頻幀的幀號(hào)被賦值為目標(biāo)幀號(hào)。將重組視頻幀放入重組緩沖區(qū)中,等待網(wǎng)絡(luò)傳輸線程使用。
圖2給出三個(gè)通道時(shí)的壓縮視頻幀重組示例,采集線程記錄采集的視頻幀幀號(hào)序列為1、2、3、4等。重組線程首先重組1號(hào)幀:從壓縮緩沖區(qū)中的三個(gè)通道的壓縮視頻幀隊(duì)列取出幀號(hào)為1的壓縮視頻幀,重組為重組視頻幀,然后放入重組緩沖區(qū)。2號(hào)幀的重組過(guò)程與1號(hào)幀相同。在重組3號(hào)幀時(shí),通道1的壓縮視頻幀隊(duì)列沒(méi)有3號(hào)幀,說(shuō)明通道1的3號(hào)幀還未壓縮完成,等待直至通道1的3號(hào)幀壓縮完成,然后對(duì)3號(hào)幀進(jìn)行重組。
圖2 重組示例
若要實(shí)現(xiàn)畫(huà)面在環(huán)幕的顯示,須將重組視頻幀數(shù)據(jù)發(fā)送至集群顯示模塊,集群顯示模塊渲染顯示畫(huà)面。為了保證重組視頻幀數(shù)據(jù)的可靠到達(dá),防止丟包帶來(lái)的異常行為,采集模塊與集群顯示模塊使用TCP完成重組視頻幀數(shù)據(jù)的傳輸。
網(wǎng)絡(luò)傳輸線程負(fù)責(zé)將重組視頻幀發(fā)送至集群主節(jié)點(diǎn)。網(wǎng)絡(luò)線程與集群主節(jié)點(diǎn)建立TCP連接,在線程主循環(huán)內(nèi),每次從重組緩沖區(qū)取出一幀重組視頻幀,發(fā)送至集群主節(jié)點(diǎn);若重組緩沖區(qū)內(nèi)無(wú)重組視頻幀,則線程休眠5秒后再次嘗試。采集模塊與顯示模塊傳輸?shù)臄?shù)據(jù)格式如圖3所示。
圖3 數(shù)據(jù)包格式
數(shù)據(jù)包頭部數(shù)據(jù)包含幀號(hào)、寬度、高度和通道數(shù)量共四個(gè)字段,每個(gè)字段均為整數(shù)類型,占用4個(gè)字節(jié)長(zhǎng)度。接下來(lái)為通道數(shù)據(jù)列表,每個(gè)通道數(shù)據(jù)項(xiàng)包含通道ID、壓縮視頻幀數(shù)據(jù)長(zhǎng)度和壓縮視頻幀內(nèi)容三個(gè)字段。通道ID和壓縮視頻幀數(shù)據(jù)長(zhǎng)度為整數(shù)類型,占用4個(gè)字段。壓縮視頻幀內(nèi)容為字節(jié)數(shù)組,占用長(zhǎng)度不固定,接收方根據(jù)壓縮視頻幀數(shù)據(jù)長(zhǎng)度字段來(lái)確定字節(jié)數(shù)組的邊界。
為了降低線程之間的耦合度,提高數(shù)據(jù)處理的并發(fā)性,采集模塊使用生產(chǎn)者-消費(fèi)者模式[14],多線程之間使用數(shù)據(jù)緩沖區(qū)實(shí)現(xiàn)數(shù)據(jù)的傳遞。生產(chǎn)者-消費(fèi)者模式如圖4所示,生產(chǎn)者向數(shù)據(jù)緩沖區(qū)存入數(shù)據(jù),消費(fèi)者從數(shù)據(jù)緩沖區(qū)讀取數(shù)據(jù),數(shù)據(jù)的存入與讀取使用加鎖的方式保證同步性與互斥性。
圖4 生產(chǎn)者-消費(fèi)者模式
采集模塊中,數(shù)據(jù)緩沖區(qū)包含采集緩沖區(qū)、壓縮緩沖區(qū)和重組緩沖區(qū)三種。
對(duì)于采集緩沖區(qū),采集線程充當(dāng)生產(chǎn)者的角色,將所有通道的視頻幀放入同一視頻幀隊(duì)列。壓縮線程充當(dāng)消費(fèi)者的角色,從視頻幀隊(duì)列獲取視頻幀,對(duì)視頻幀進(jìn)行壓縮。
對(duì)于壓縮緩沖區(qū),壓縮線程充當(dāng)生產(chǎn)者的角色,對(duì)視頻幀進(jìn)行壓縮后,根據(jù)視頻幀的通道號(hào),將其放入對(duì)應(yīng)的壓縮視頻幀隊(duì)列。壓縮緩沖區(qū)為每個(gè)通道分配一個(gè)壓縮視頻幀隊(duì)列。重組線程充當(dāng)消費(fèi)者的角色,從壓縮緩沖區(qū)的壓縮視頻幀隊(duì)列獲取所有對(duì)應(yīng)序號(hào)的壓縮視頻幀進(jìn)行重組。
對(duì)于重組緩沖區(qū),重組線程充當(dāng)生產(chǎn)者角色,對(duì)壓縮視頻幀進(jìn)行重組后,將重組視頻幀數(shù)據(jù)放入重組視頻幀隊(duì)列。網(wǎng)絡(luò)傳輸線程充當(dāng)消費(fèi)者的角色,從重組視頻幀隊(duì)列獲取重組視頻幀,然后發(fā)送至集群主節(jié)點(diǎn)。
對(duì)于采集緩沖區(qū)和壓縮緩沖區(qū),本文系統(tǒng)能夠確保消費(fèi)者取數(shù)據(jù)的頻率大于等于生產(chǎn)者存數(shù)據(jù)的頻率,因此兩者均無(wú)容量限制。對(duì)于重組緩沖區(qū),其與集群主節(jié)點(diǎn)間的網(wǎng)絡(luò)狀況不確定,將其緩存隊(duì)列容量設(shè)為10。當(dāng)網(wǎng)絡(luò)傳輸速率低于重組速率時(shí),需要對(duì)部分重組視頻幀進(jìn)行主動(dòng)丟棄,防止隊(duì)列溢出。
緩沖區(qū)丟棄數(shù)據(jù)的策略有兩種。一是在放入隊(duì)列前進(jìn)行檢測(cè),若隊(duì)列已滿,則將隊(duì)列中的已有的數(shù)據(jù)全部丟棄,放入最新的數(shù)據(jù);二是根據(jù)消費(fèi)者反饋選擇性丟幀,如每三幀丟棄一幀。第一種方式畫(huà)面延遲較小,但是可能會(huì)出現(xiàn)畫(huà)面變化幅度較大,畫(huà)面偶爾不連貫的現(xiàn)象;第二種方式畫(huà)面看起來(lái)比較連貫,但是相對(duì)第一種畫(huà)面延遲會(huì)比較高。為了保證較低的畫(huà)面延遲以及多線程之間的低耦合度,本文選用第一種丟棄策略。
集群顯示模塊包包含視頻幀接收與轉(zhuǎn)發(fā)、解碼與顯示兩個(gè)部分。
由于單臺(tái)主機(jī)的輸出分辨率有限且不具有擴(kuò)展性,本文使用集群來(lái)搭建多投影環(huán)幕顯示系統(tǒng)。集群包含一個(gè)集群主節(jié)點(diǎn)與多個(gè)集群從節(jié)點(diǎn),主節(jié)點(diǎn)負(fù)責(zé)保證所有節(jié)點(diǎn)狀態(tài)的同步,每個(gè)從節(jié)點(diǎn)都是顯示節(jié)點(diǎn),負(fù)責(zé)渲染一塊顯示區(qū)域,所有節(jié)點(diǎn)畫(huà)面通過(guò)拼接組成完整的畫(huà)面。
視頻幀數(shù)據(jù)的接收由集群主節(jié)點(diǎn)負(fù)責(zé)。集群主節(jié)點(diǎn)創(chuàng)建單獨(dú)的網(wǎng)絡(luò)線程,網(wǎng)絡(luò)線程與采集模塊建立TCP網(wǎng)絡(luò)連接,從采集模塊接收重組視頻幀數(shù)據(jù),兩者之間的網(wǎng)絡(luò)狀況影響重組視頻幀的接收速率。
集群主節(jié)點(diǎn)使用生產(chǎn)者消費(fèi)者模式,網(wǎng)絡(luò)線程接收到重組視頻幀后,將其放入接收緩沖區(qū)。在每幀的Unity主線程的LateUpdate階段,從接收緩沖區(qū)取一幀重組視頻幀,然后發(fā)送至集群從節(jié)點(diǎn)。重組緩沖區(qū)的接收隊(duì)列容量為10,當(dāng)緩沖區(qū)將要溢出時(shí),使用直接丟棄緩沖區(qū)內(nèi)已有數(shù)據(jù)的丟棄策略,保證具有較低的畫(huà)面延遲。重組視頻幀在集群主節(jié)點(diǎn)內(nèi)部的流向如圖5所示。
圖5 集群主節(jié)點(diǎn)數(shù)據(jù)流向
集群主節(jié)點(diǎn)接收到重組視頻幀數(shù)據(jù)后,需要將數(shù)據(jù)通過(guò)網(wǎng)絡(luò)轉(zhuǎn)發(fā)給所有集群從節(jié)點(diǎn)。
TCP為點(diǎn)對(duì)點(diǎn)的可靠網(wǎng)絡(luò)傳輸協(xié)議,能夠保證數(shù)據(jù)的按序可靠到達(dá),TCP傳輸如圖6所示。在數(shù)據(jù)量為M,從節(jié)點(diǎn)數(shù)量為N的情況下,主節(jié)點(diǎn)需要傳輸?shù)臄?shù)據(jù)量為M×N。在節(jié)點(diǎn)較多時(shí),使用TCP不僅耗時(shí)較且多占用很高的網(wǎng)絡(luò)帶寬,系統(tǒng)運(yùn)行效率較低且不具有可擴(kuò)展性。
圖6 TCP點(diǎn)對(duì)點(diǎn)傳輸
本文使用復(fù)旦大學(xué)交互式圖形學(xué)實(shí)驗(yàn)室開(kāi)發(fā)的基于可靠多播的集群同步協(xié)議,UDP多播傳輸如圖7所示。在數(shù)據(jù)量為M,從節(jié)點(diǎn)為N的情況下,主節(jié)點(diǎn)只需傳輸M的數(shù)據(jù)量。相比于TCP的點(diǎn)對(duì)點(diǎn),使用集群同步協(xié)議能夠減少網(wǎng)絡(luò)帶寬占用,提高系統(tǒng)運(yùn)行效率與可擴(kuò)展性。
圖7 UDP多播傳輸
集群從節(jié)點(diǎn)應(yīng)用中包含與通道數(shù)量相同的紋理,將接收到的多個(gè)通道的畫(huà)面數(shù)據(jù)上傳至紋理顯示。
紋理上傳可使用Unity提供的Texture2D.LoadImage函數(shù)[15]或者Texture2D.LoadRawTextureData函數(shù)[16]將各個(gè)通道的畫(huà)面數(shù)據(jù)上傳至對(duì)應(yīng)的紋理,然而這種方法的紋理數(shù)據(jù)加載與上傳過(guò)程均在主線程執(zhí)行。在上傳紋理數(shù)量較多或者紋理數(shù)據(jù)較大時(shí),主線程耗時(shí)過(guò)多,嚴(yán)重影響系統(tǒng)運(yùn)行幀率。
本文系統(tǒng)使用Unity提供的多線程渲染技術(shù)[17],開(kāi)發(fā)渲染插件NativeRenderingPlugin.dll,在Unity渲染線程中實(shí)現(xiàn)紋理上傳,從而提高系統(tǒng)幀率。渲染線程中執(zhí)行紋理上傳的操作需要對(duì)各個(gè)通道的壓縮視頻幀進(jìn)行解碼操作,并且紋理上傳的起始指令只能在Unity主線程中調(diào)用。為了提高系統(tǒng)效率以及保證多通道畫(huà)面顯示的同步性,集群從節(jié)點(diǎn)額外創(chuàng)建解碼線程與紋理上傳線程,多線程的流程圖如圖8所示。
圖8 集群從節(jié)點(diǎn)多線程流程圖
集群從節(jié)點(diǎn)的基本行為可分為如下三部分:
1)Unity主線程在LateUpdate階段,從主節(jié)點(diǎn)接收重組視頻幀數(shù)據(jù),然后將數(shù)據(jù)放入接收緩沖區(qū)。接收緩沖區(qū)容量為10,使用前文提到的第一種丟棄策略:在隊(duì)列已滿時(shí)將隊(duì)列清空。
2)從節(jié)點(diǎn)在啟動(dòng)后,創(chuàng)建一個(gè)解碼管理線程和與通道數(shù)量相同個(gè)數(shù)的解碼線程,每個(gè)解碼線程負(fù)責(zé)一個(gè)通道的壓縮視頻幀解碼。主線程每次在LateUpdate階段接收到重組視頻幀數(shù)據(jù)后,使用信號(hào)通知解碼管理線程。解碼管理線程從接收緩沖區(qū)獲取一幀重組視頻幀,使用信號(hào)通知所有解碼線程開(kāi)始解碼。解碼線程使用LibJpegTurbo插件將其負(fù)責(zé)的通道壓縮視頻幀數(shù)據(jù)解碼為RGBA格式的原始視頻幀數(shù)據(jù),解碼完成后使用信號(hào)通知解碼管理線程。解碼管理線程接收到所有解碼線程的信號(hào)后,表示所有通道的視頻幀均已解碼完成,使用信號(hào)通知紋理上傳線程。
3)紋理上傳線程接收到信號(hào)后,在下一個(gè)LateUpdate階段調(diào)用GL.IssuePluginEvent函數(shù)開(kāi)始上傳紋理數(shù)據(jù)。每個(gè)通道的數(shù)據(jù)被用于解碼單獨(dú)的原始視頻幀數(shù)據(jù),執(zhí)行獨(dú)立的紋理上傳動(dòng)作。在Unity中,渲染線程的紋理上傳只能夠保證在下一幀的相機(jī)渲染之前完成,在本幀的相機(jī)渲染階段紋理上傳的完成狀態(tài)難以確保。在一個(gè)從節(jié)點(diǎn)上,多個(gè)通道的原始視頻幀同時(shí)開(kāi)始執(zhí)行紋理上傳動(dòng)作,但不同紋理上傳的速度可能不同:在本幀渲染階段之前上傳完成的紋理能夠在本幀顯示;在本幀渲染階段之后上傳完成的紋理只能夠在下一幀顯示。因此一個(gè)節(jié)點(diǎn)內(nèi)多通道的畫(huà)面顯示可能會(huì)有一幀的差異。對(duì)于多個(gè)從節(jié)點(diǎn),由于紋理上傳的開(kāi)始只發(fā)生在LateUpdate階段,因此在解碼能力足夠,即解碼速率快于接收速率時(shí),從節(jié)點(diǎn)之間的顯示畫(huà)面可能會(huì)有一至兩幀的差異。
經(jīng)過(guò)之前的解碼和紋理上傳步驟,集群從節(jié)點(diǎn)的多幅紋理能夠正確顯示多個(gè)通道的畫(huà)面。多幅紋理根據(jù)畫(huà)面顯示區(qū)域排列,根據(jù)當(dāng)前從節(jié)點(diǎn)的顯示區(qū)域配置,使用Unity正交相機(jī)獲取節(jié)點(diǎn)顯示畫(huà)面至RenderTexture,根據(jù)投影儀的矯正文件對(duì)該RenderTexture進(jìn)行矯正,最終通過(guò)多臺(tái)投影呈現(xiàn)在環(huán)幕表面。圖9給出六通道畫(huà)面顯示實(shí)例,六幅紋理排列成一排,呈現(xiàn)六個(gè)通道的畫(huà)面數(shù)據(jù)。在六個(gè)通道的畫(huà)面數(shù)據(jù)為360度場(chǎng)景且投影有融合區(qū)域的情況下,在第六幅紋理后面復(fù)制一幅第一幅紋理畫(huà)面,從而實(shí)現(xiàn)首尾的畫(huà)面相連。然后根據(jù)從配置文件讀取當(dāng)前節(jié)點(diǎn)的顯示區(qū)域信息,包含起始位置與顯示范圍,使用正交相機(jī)渲染顯示畫(huà)面至渲染紋理。最后對(duì)渲染紋理進(jìn)行校正,校正后的畫(huà)面如圖10所示。
圖9 可視區(qū)域畫(huà)面截取
圖10 畫(huà)面校正結(jié)果
本文基于上文描述實(shí)現(xiàn)系統(tǒng)并設(shè)計(jì)兩個(gè)實(shí)驗(yàn),以驗(yàn)證本文系統(tǒng)的可用性,兩個(gè)實(shí)驗(yàn)的畫(huà)面源主機(jī)與采集服務(wù)器配置相同。
畫(huà)面源主機(jī)使用兩臺(tái)NVIDIA Quadro P620顯卡,輸出六個(gè)通道的畫(huà)面。在畫(huà)面源主機(jī)全屏運(yùn)行FPS游戲《泰坦英雄》,在六個(gè)通道輸出360度的畫(huà)面,每個(gè)通道負(fù)責(zé)60度的畫(huà)面。畫(huà)面源主機(jī)多通道輸出的360度畫(huà)面如圖11所示。
圖11 畫(huà)面源多通道畫(huà)面
采集服務(wù)器使用兩臺(tái)Magewell Pro Capture Quad HDMI Video Capture Card視頻采集卡,通過(guò)HDMI連接線采集畫(huà)面源主機(jī)輸出的六個(gè)通道的畫(huà)面。每個(gè)通道的采集分辨率均為1 920×1 080,共11 520×1 080的分辨率,采集目標(biāo)幀率為60,采集畫(huà)面格式為I420格式。在對(duì)采集到的畫(huà)面進(jìn)行JPEG壓縮時(shí),壓縮質(zhì)量為85。
為了驗(yàn)證本文系統(tǒng)的有效性,實(shí)驗(yàn)在復(fù)旦大學(xué)交互式圖形學(xué)實(shí)驗(yàn)室的環(huán)幕環(huán)境中運(yùn)行,該環(huán)幕提供120度視角。集群顯示模塊使用三臺(tái)主機(jī),包括一臺(tái)主節(jié)點(diǎn)與兩臺(tái)集群從節(jié)點(diǎn),所有主機(jī)之間使用千兆網(wǎng)絡(luò)相連。每臺(tái)集群從節(jié)點(diǎn)連接三臺(tái)投影儀,顯示從30度至150度的輸出畫(huà)面,運(yùn)行效果如圖12所示。
圖12 環(huán)幕運(yùn)行效果
表1給出了實(shí)驗(yàn)運(yùn)行過(guò)程中各階段速率的統(tǒng)計(jì)數(shù)據(jù)。
表1 各階段速率統(tǒng)計(jì)表
可以看出,多通道同步采集每秒采集53幀的視頻幀數(shù)據(jù),壓縮、重組和發(fā)送速率都能夠達(dá)到采集速率,說(shuō)明在采集模塊不會(huì)發(fā)生數(shù)據(jù)包的丟棄,采集端以每秒53幀的速率向集群模塊發(fā)送多通道畫(huà)面數(shù)據(jù)。在集群模塊,集群主節(jié)點(diǎn)發(fā)送、集群從節(jié)點(diǎn)接收和解碼的速率與采集模塊的速率一致,說(shuō)明所有的畫(huà)面數(shù)據(jù)都被集群從節(jié)點(diǎn)接收并解碼顯示。圖13為系統(tǒng)運(yùn)行過(guò)程中的帶寬占用變化曲線,圖14為系統(tǒng)運(yùn)行過(guò)程中集群畫(huà)面刷新率變化曲線,系統(tǒng)平均帶寬占用為554 Mbit/s,平均畫(huà)面刷新率為53幀/s。
圖13 帶寬變化曲線
圖14 集群畫(huà)面刷新率變化曲線
除了畫(huà)面刷新速率外,畫(huà)面的延遲也是系統(tǒng)非常重要的評(píng)價(jià)指標(biāo)。本文系統(tǒng)中,各個(gè)階段的耗時(shí)統(tǒng)計(jì)如表2所示。
表2 各階段耗時(shí)統(tǒng)計(jì)表
可以看出,系統(tǒng)各個(gè)模塊的耗時(shí)都較短,三個(gè)階段耗時(shí)總計(jì)47.3 ms,加上采集模塊到集群主節(jié)點(diǎn)的網(wǎng)絡(luò)延遲和紋理從上傳到顯示的平均一幀的延遲,本實(shí)驗(yàn)畫(huà)面平均延遲在80 ms左右。
實(shí)驗(yàn)使用更多的集群主機(jī)數(shù)量來(lái)模擬本文系統(tǒng)在360度環(huán)幕下的運(yùn)行狀況。集群顯示模塊使用五臺(tái)主機(jī),包含一臺(tái)集群主節(jié)點(diǎn)與四臺(tái)集群從節(jié)點(diǎn),所有主機(jī)通過(guò)千兆網(wǎng)絡(luò)相連。每臺(tái)集群從節(jié)點(diǎn)連接四個(gè)高清顯示屏,負(fù)責(zé)180度的畫(huà)面渲染,四臺(tái)從節(jié)點(diǎn)共顯示兩個(gè)360度的畫(huà)面,模擬360度的被動(dòng)立體環(huán)幕。系統(tǒng)運(yùn)行效果如圖15所示。
圖15 模擬360度環(huán)幕運(yùn)行效果
表3給出了實(shí)驗(yàn)運(yùn)行過(guò)程中各階段速率的統(tǒng)計(jì)數(shù)據(jù)。
表3 各階段速率統(tǒng)計(jì)表
可以看出,多通道同步采集每秒采集57.4幀的視頻幀數(shù)據(jù),壓縮、合并和發(fā)送速率都能夠達(dá)到采集速率,說(shuō)明在采集模塊不會(huì)發(fā)生數(shù)據(jù)包的丟棄,采集端以每秒57.4幀的速率向集群模塊發(fā)送多通道畫(huà)面數(shù)據(jù)。在集群模塊,集群主節(jié)點(diǎn)發(fā)送、集群從節(jié)點(diǎn)接收和解碼的速率都只為每秒51.2幀,即在集群模塊每秒鐘轉(zhuǎn)發(fā)51.2幀畫(huà)面的數(shù)據(jù),這是由于集群節(jié)點(diǎn)間的網(wǎng)絡(luò)出現(xiàn)波動(dòng),導(dǎo)致集群主節(jié)點(diǎn)轉(zhuǎn)發(fā)重組視頻幀的速率低于網(wǎng)絡(luò)線程接收重組視頻幀的速率,從而在集群主節(jié)點(diǎn)的接收緩沖區(qū)中主動(dòng)丟棄部分重組視頻幀。圖16為系統(tǒng)運(yùn)行過(guò)程中的帶寬占用變化曲線,圖17為系統(tǒng)運(yùn)行過(guò)程中集群畫(huà)面刷新率變化曲線。系統(tǒng)平均帶寬占用為500 Mbit/s,平均畫(huà)面刷新率為51.2幀/s。
圖16 帶寬變化曲線
圖17 集群畫(huà)面刷新率變化曲線
除了畫(huà)面刷新速率外,畫(huà)面的延遲也是系統(tǒng)非常重要的評(píng)價(jià)指標(biāo)。本文系統(tǒng)中,各個(gè)階段的耗時(shí)統(tǒng)計(jì)如表4所示。
表4 各階段耗時(shí)統(tǒng)計(jì)表
可以看出,多通道同步采集到發(fā)送、集群從節(jié)點(diǎn)接收到上傳的耗時(shí)與實(shí)驗(yàn)一相近,耗時(shí)都較短,兩個(gè)階段共耗時(shí)總計(jì)45.1 ms。集群主節(jié)點(diǎn)接收到發(fā)送的耗時(shí)多的原因?yàn)榧褐鞴?jié)點(diǎn)轉(zhuǎn)發(fā)的速率低于網(wǎng)絡(luò)線程接收重組視頻幀的速率,部分視頻幀在接收緩沖區(qū)中等待了一段時(shí)間才被轉(zhuǎn)發(fā),從而導(dǎo)致從接收到發(fā)送的平均耗時(shí)變長(zhǎng)。三個(gè)階段的總耗時(shí)為127.0 ms,加上采集模塊到集群主節(jié)點(diǎn)的網(wǎng)絡(luò)延遲和紋理從上傳到顯示的平均1幀的延遲,本實(shí)驗(yàn)畫(huà)面平均延遲在160 ms左右。
本文提出一種基于多通道視頻同步采集的多投影環(huán)幕顯示系統(tǒng)。使用視頻采集卡多通道同步采集畫(huà)面源畫(huà)面,將畫(huà)面數(shù)據(jù)壓縮,通過(guò)網(wǎng)絡(luò)傳輸給集群顯示模塊。集群主節(jié)點(diǎn)接收到畫(huà)面數(shù)據(jù)后,使用可靠多播協(xié)議將畫(huà)面數(shù)據(jù)轉(zhuǎn)發(fā)至集群顯示節(jié)點(diǎn)。顯示節(jié)點(diǎn)對(duì)畫(huà)面數(shù)據(jù)進(jìn)行解碼,上傳至紋理顯示,然后根據(jù)顯示區(qū)域配置,對(duì)視角內(nèi)紋理畫(huà)面進(jìn)行變形與矯正,最終通過(guò)多臺(tái)投影機(jī)呈現(xiàn)在環(huán)幕顯示表面。本文系統(tǒng)通過(guò)多通道采集的方法降低了畫(huà)面源與顯示環(huán)境的耦合度,具有較好的通用性。實(shí)驗(yàn)結(jié)果表明,本文系統(tǒng)具有很好的通用性,并且系統(tǒng)畫(huà)面延遲較低。