本文由百度智能云音視頻SDK產品技術負責人李明路在LiveVideoStack線上分享的演講內容整理而成,內容從音視頻數據角度出發,梳理了音視頻SDK的發展和技術演進。詳細分析數據在常見音視頻模塊上遇到的問題與挑戰,并提出相應的解決思路和技術實踐。
本次分享的主題是移動音視頻SDK開發工程實踐,內容主要分為以下五個部分:
音視頻SDK的技術演進
數據采集管線的設計與實現
特效模塊數據中間件設計和實現
連麥模塊數據中間件設計和實現
渲染模塊數據中間件設計和實現
01音視頻SDK的技術演進1.1 數字化進程
多媒體技術是一項傳統技術,但同時也是在不斷發展與進步的,我們可以形象的用細胞分裂來表示,即多媒體技術內部也在不斷發生分裂,而音視頻SDK則是其中的一個分系。
以下大致列出了音視頻技術演進的幾個重要進展。首先,在互聯網化前期多媒體技術就已經開始出現廣泛的應用,得益于通訊技術的發展,我們可以借助于廣播、有線電視、衛星等等,將數字信號傳輸給用戶。
解碼器其實在整個多媒體技術的發展當中也起到非常重要的作用,只不過當時解碼器的能力與現在相比還是有些不同,但同樣可以實現解碼和容器的分離。
此后,隨著互聯網的出現,我們可以實現基于一些IP網絡協議、光纖的廣泛使用,通過WiFi、蜂窩網絡,傳輸數據到設備終端(此時終端更多的還是指PC端)。因此產生了一些在線類的音視頻服務,例如點播、在線語音等典型的音視頻場景。在這些場景中,SDK更多的還是以服務端為主。
2010-2015年期間,隨著手機硬件的發展,終端的算力不斷提升,編解碼芯片也得以快速發展,再加上消費者使用習慣的變化,出現了更多碎片化的場景和產品,像直播、短視頻等。此時,隨著手機等移動設備的普及,移動端SDK也慢慢進入到消費級領域,逐漸發展成獨立技術棧。 近年來, 隨著5G、AI人工智能的發展,VR/AR、語音交互等技術也在發生著新的變化,應用場景變得更加廣泛,音視頻SDK已經不僅僅局限于移動端,未來會出現在各種設備屏幕、產品形態當中。 1.2 移動端音視頻框架
移動端的音視頻框架與其它移動端框架相比有很大不同。音視頻框架首先要立足移動端提供的系統能力,包括系統框架和硬件能力。如圖,系統框架層有iOS/Android的一些多媒體框架、硬件的編解碼、硬件的處理能力如CPU、GPU、NPU的處理模塊,以及一些開源圖像庫。
系統框架層之上是第三方框架,如FFmpeg、OpenSSL(加解密);在點播場景用的比較多的Ijkplayer/Exoplayer;用于底層通訊的技術:雙向的,低延時的WebRTC技術,用于單向點播的RTMP,目前比較火的SRT低延時方案;除此之外,還會有一些圖像處理方面的框架如GPUImage等。
在此之上,就是一些跟場景相結合比較多的模塊,這里大致列出了部分核心模塊。 數據從多媒體采集模塊出來,會經過一路或多路的混音混流(與實際場景相結合),然后過渡到多媒體編輯模塊:當下短視頻的一些能力都是通過多媒體編輯這個處理單元實現,再到后面的多媒體后處理模塊:例如AR特效,以及一些比較好玩的互動能力等; 內容生產完成后,我們會根據點播、直播或者短視頻等場景的不同,采用不同協議進行分發。再下面就是消費側,這里可以直接讀取數據,也可以間接讀取數據作為緩存加快二次讀取數據的載入。獲取數據后會根據不同的封裝格式,如FLV、TS或者是MP4等對數據進行解封裝,再進行解碼和渲染操作。 相比與其它移動端框架音視頻框架最特別的地方就是管線部分,因為音視頻SDK的產品與其它產品不太一樣,首先需要的是實時處理,數據流是不斷在各個模塊之間穿梭的,如何保證各個模塊間的高效傳輸,這里提出了一個管線的概念。 1.3 唯快不破
簡單介紹了音視頻SDK的框架,接下來介紹下目前框架遇到一些問題和挑戰。目前很多業務場景,其實都在追求更多的新玩法。這里可能會有一些追求更大的分辨率,還有一些更高的刷新率,甚至是更極致的體驗。但是我們知道音視頻SDK很大程度上會受制于平臺的能力,由于平臺具有更多的差異性,所以導致音視頻SDK在發展過程當中,其實遇到很多的問題。另外還涉及到模塊間的數據交互,因此性能對于移動端來說是一個最大的瓶頸。
其實在有些場景像點播或短視頻,720p的分辨率已經不能滿足場景的使用,甚至在一些運動場景,30fps的刷新率也已經沒辦法滿足場景的開發,當然這些其實更多的是業務挑戰。除此之外,剛才也說過整個多媒體技術的發展,編碼器一直起到了舉足輕重的作用,所以底層技術也在追求更高效的編碼效率。
對于這些問題,其實大家都能達成一個共識,就是說速度是音視頻SDK框架要解決的一個問題。但拋開現象看本質,速度其實還是來自于數據的傳輸,它是一個根本前提條件,因此如何讓數據更高效的傳遞和更高效的處理,才是移動端SDK要解決的最根本問題。
02數據采集管線的設計和實現
這里介紹一下目前一些開源產品當中數據鏈的設計和方案,以兩個產品為例,一個是GPUImage。相信做過移動端SDK特效的同學應該都是耳熟能詳了,因為它確實是比較好用,可以提供大量的Shader。拋開Shader的不說,我們看一下它在數據鏈條的設計方式。首先GPUImage提供了一個傳輸數據的類Output和一個實現接收數據的Input,然后通過生產模式,生產模塊像相機采集模塊,以及本地相冊模塊,其實都是實現了Output的類,生產模塊生產完畢后會交由Output進行渲染,這里它的強大之處在于Output可以與可編程的Shader進行強關聯,很大程度的驅動了OpenGL的運行效率。然后將渲染后RGB的數據轉換成FrameBuffer這個實體的對象,整個鏈條都是通過FrameBuffer進行接收,然后通過鏈條上的組件去實現了Input后,就能拿到Output傳出的FrameBuffer,以此類推將FrameBuffer進一步的傳遞到下個鏈條當中,整個鏈條其實是非常清晰和簡單。
我們再來看一下右邊的FFmpeg,除了提供很多的數據鏈條的處理機制、數據的分包,它還可以在不進行解碼的情況下進行轉包,也可以把數據進行解碼,做一些后處理等等,整個鏈條其實也是比較清晰的。
介紹了兩種多媒體框架,接下來看一下我們團隊是怎么去考慮這些問題。實際開發中,音視頻SDK框架中管線其實是不可控的,我們來看一下這兩種方案的一些優勢和問題。
首先GPUImage的優勢是顯而易見的,就是協議相對簡單清晰,另外通過可編程Shader驅動了OpenGL,這樣它的運行效率變的非常快,通過大量的Shader可以讓開發同學去進一步學習這門語言,是一個優秀的圖像處理 開源框架。但同樣存在一些問題,首先我們看到它提供的這些Shader其實大多數都是圖像處理,并且基本上都是同步處理的。所以可能導致在使用GPUImage跟一些框架進行結合的時候,當你的某個模塊涉及到耗時處理,這個時候有可能會出現某個對象被其它釋放掉或怎么樣,會造成整個線程的不安全。
FFmpeg的優勢特別多,簡單說一下它的函數指針,其實也是FFmpeg的一個最佳實踐,它通過指針的方式,把數據進行一層層的拆分跟傳遞。但是它也同樣存在一些問題,比如說FFmpeg是面向過程的,它的鏈路設計對于不熟悉FFmpeg的同學來說,可能會覺得鏈路非常復雜,使用起來可能并不是那么的得心應手。
所以我們經過多輪討論跟思考,就希望得到一個數據管線,它首先像GPUImage,協議比較簡單,用起來比較容易上手,對開發者來說是友好的;另外需要它是可控的,因為隨著SDK的模塊越來越多,我們發現在很多場景下,會出現不可控的問題;除此之外我們還希望它是支持同步或者是異步的,調度是安全的,鏈路配置是簡單可依賴的。
我們也總結了自己的一些方法論跟實踐:專注鏈式,這主要還是考慮到音視頻框架,鏈式是它的一個核心思想,所以我們遵從這種法則自研數據管線。
首先是制定數據協議,主要解決的是最基本的模塊間數據的高效、穩定的傳遞。這里可以看到左圖,它的方案跟剛才介紹的GPUImage有些相似,但是也有些不同。我們也提供了類似的AVOutput的模塊和AVInput的數據傳輸接收的協議。但是我們并沒有跟GPUImage的OpenGL進行綁定,只是單純的去記錄和管理這條鏈條上的各個的組件,我們叫target。然后通過Dispatcher的機制,分發從生產端上報的視頻幀,通過視頻幀不斷的傳遞到各個鏈路的target當中,而每個target實現AVInput其中的協議方法。例如frame跟type兩個函數,frame就是從raiseFrame Output傳遞下來的數據;type主要是做一些音視頻的音頻跟視頻的區分。
除此之外我們還支持一些二進制場景的分發,主要是為了配合像直播這種對數據有分發要求的場景做了一些協議的升級。在鏈條的最末端,可以看到我們也實現了AVControl,跟之前不太一樣是我們制定了一個控制協議,這個控制協議主要是為了控制數據的流入和流出。為什么要做這個事情呢?我們知道音視頻SDK最核心的就是數據在不斷的傳遞,如果說某個模塊出現異常,數據沒有一種機制能保護它,可能會導致整個SDK的運行出現不穩定的情況。比如在直播場景分發的時候,我們發現網絡有抖動,此時我們可以調整發送的速率、速度等。
這里簡單的畫了一下我們的數據流向,一個是推的方式,就是直接從相機這種模塊采集數據,直接著向后面的某個模塊去傳遞。一個是拉的方式,主要是指讀取本地文件或者說是間接的獲取數據,比如從網絡讀取數據首先需要將數據讀下來,然后再傳遞到各個模塊。這里其實相對于之前說了GPU在異步線程的一些處理,我們也做了一些兼容和保護,就是防止異步處理時對象被釋放的情況發生。所以說我們基本上也是沿用了GPUImage協議簡單的這種思想,然后在此基礎上又增加了一些像控制協議的實現機制,讓整個鏈路變得可控。
除此之外,我們也發現在日常過程當中,只是簡單的設計數據管線,其實并不能很好的對業務場景開發提供幫助,原因什么呢?
其實我們只是解決了各個模塊數據之間的傳遞問題,但是并不能對最終產品的場景開發落地解決所有的問題,因為場景跟模塊是有很大差異的。我們所了解的像點播、直播,甚至是特效等,它其實都是通用性的能力。但是實際場景就涉及到各個模塊之間的組合,所以數據的傳遞并不能說傳遞一個數據,就能把一個場景串聯起來。
這里也舉了幾個日常過程中典型的例子,比如對于點播的畫質優化,會發現類型轉換不是那么通暢和簡單。對于連麥場景,如何讓我們的SDK或者產品用起來更簡單,像人臉特效,比如說涉及到能力的多樣化,如何做到兼容性等,這些都不只是靠數據鏈路或模塊搭載就能解決的。因此我們提到了一個中間件的概念,就是把數據進行橋接實現資源的共享,在各個模塊或業務輸出或使用的時候,就能提高整體數據采集或處理的應用性。所以說應用性也是我們數據采集管線的另一個考量指標。
03特效模塊數據中間件設計和實現 接下來我們來看一下,在實際過程當中遇到的一些問題跟解決方案。
特效模塊通常是典型的PaaS結構,它上面存在多種模型,而且模型之間是可以進行插拔;它還有另外一個特點就是耗資源。那么如何在音視頻SDK中將這個模塊更好的運用起來,去對外提供能力呢?我們這里以人臉特效的高級美顏接口為例,高級美顏中涉及到的特征點非常多,像大眼、瘦臉、下巴等,而且這些特征點并不是通過一次迭代或是一個模型就能解決的,它可能會涉及到多次的迭代和多種模型的組合疊加。這樣就會帶來一個問題,在集成特效模塊的時候,如果這些能力都是不斷變化的,對于模塊的使用來說,就存在一個不安全不穩定的因素。那么我們該如何把這個問題解決,或者說屏蔽掉呢?
這里我們提供了一個概念:首先在調用能力的時候,不直接去調用,而是把這些能力做進行抽象、封裝,然后這些封裝的模型,用來關聯后面的一些不同的算法。因為別人在用SDK的時候,不一定按照你所有東西來集成,可能只使用其中部分能力,這樣就有可能會導致一些特效的SDK存在版本不一致。如果沒有這個代理層,當出現版本不一致的情況,對于上層來說可能就會出現大量接口的調整和修改,會比較耗時耗力。不過我們做代理的話,可能就給屏蔽上層接口的穩定性,然后通過我們的模型跟一些抽象對象,可以去驅動不同的AR模塊的版本。
通過數據管線我們可以看到,從錄制模塊把數據傳到effect接口,再把數據再給到AR SDK的時候,每個AR SDK都會有一個處理檢測能力,會定時的檢測主屏指標等,為什么要這么做?
我們知道現在的特效,場景、玩法是特別復雜的,處理效果沒辦法完全保證。所以為了保證每幀的處理和整體鏈路的穩定,我們需要不斷做一些性能指標的監測,這些指標監測需要不斷的反饋給上層的調用。比如當前數據傳輸的速度較快,或者傳遞幀太多處理不完,我們就可以通過數據管線進行回傳,進行控制。甚至可以調整錄制模塊采集的幀率等,這樣再把數據返回給錄制模塊,錄制模塊再將數據傳遞給其它模塊,像預覽進行渲染等。通過數據管線加代理的方案,我們就可以很好的整合不同AR版本、能力,對外保持接口的統一。
04連麥模塊數據中間件設計和實現
連麥模塊也是目前音視頻產品中用的比較多的一個能力,如在線教育、直播帶貨、PK、娛樂等等,并且已經慢慢成為一個標準化能力,但連麥模塊在使用過程當中還存在比較多的問題。
標準直播是單向通信,且信令相對簡單。融合連麥模塊后,則會變成雙向通信,信令會變得復雜,媒體流從單路流變成了多路流。如左下圖,一個標準的直播SDK加連麥SDK的整合結構,可以看到白色區域是標準的直播流程,從創建直播間到建鏈、編碼、封包,包括通過隊列進行分發等等。當融合了連麥能力之后,對整個模塊來說會增加更多的鏈路。首先會引入RTC的服務端,媒體服務端與信令服務端。另外可能還會引入一些類似于業務系統的消息機制,IM服務器等等。
用戶如果發起連麥,可以看到左圖紅色的箭頭。首先它會向RTC信令服務器發送連麥請求,同時也會向IM服務器發送一個請求,向IM服務器發送請求的原因,主要是為了做一些業務上的處理,比如說UI界面或者場景的一些流程處理。實際上信令服務器主要是為了傳遞加入房間的請求,請求到達主播直播間后,主播直播間會響應信令服務器,選擇同意或者拒絕。如果同意,則會通過信令服務器將信號返回給小主播/觀眾,小主播/觀眾這個時候就會把數據傳遞到RTMP的媒體服務器,主播也會把媒體流傳到RTMP服務器,兩路流匯聚到RTMP服務器后,通過旁路轉播等方式來進行對外的轉播,此時觀眾就會看到兩個主播或主播和觀眾合流的畫面。
這個實現流程其實比較復雜繁瑣,對于用戶(主播)來說可能理解起來非常吃力。首先他要關心的東西特別多,例如 IM服務器、旁路直播,旁路直播還會涉及一些模板的設置等等,他可能都要去關心,所以使用起來非常的麻煩。而對于消費側的觀眾來說,也同樣存在一些問題,比如說會出現相同地址,但是會出現不同編碼的兩路流,這種情況下可能對于一些播放器來說會有一些挑戰,可能會出現一些卡頓,因為它在不斷的重置一些編碼參數,另外這種方案通過旁路轉推的方式,可能原始的直播流會斷掉,就在頻繁切換直播流跟混合流的過程當中可能就會出現延遲,可能對于拉流側來說它就有很多的問題,也不太好。
針對這些問題其實我們也看到,就是用戶在使用過程當中其實有很多的不方便,所以說可以看到說簡單的數據傳遞,其實并不能讓這個場景做的特別的簡單和應用,后面我們就做了一些大量的端到端的一些數據鏈的整合,首先是我們考慮到了就是消費側的編碼播放器的一些問題,首先是切換編碼參數,這個方案可能并不是一個適用的方案,所以我們采用了本地混流的技術方案,其好處在于我的推流各方面的參數可以保持一致而不發生變化,而其實對于移動端的處理能力來說,本地的2~3路流的合流,其實對一些硬件來說,壓力并不是特別大。另外我們把IM服務器跟RTC信令服務器進行了整合,因為我們覺得讓用戶去關心這么多的信令其實是沒有必要的,而且用戶也可能會被這些問題所困擾,所以我們內部就通過服務端的方式進行了消息的整合,這樣子就讓用戶使用起來變得更加簡單。通過這種端到端的一些媒體和信令的整合,其實也是讓數據流變得簡單,提高了整體接入的應用性。
05渲染模塊數據中間件設計和實現
為什么說渲染是音視頻SDK關鍵技術之一?從整體技術鏈路的角度來說,渲染模塊實際上是用戶最能感知到的一個模塊。在一些復雜的場景下,渲染模塊也承載了一些數據的交互和處理。因此渲染模塊所包括的處理已經不再只是渲染,可能會涉及到某些場景的特殊需求,例如清屏等等:多人會議的時候,相當于一個關閉畫面的效果;如多路混流,前面提到的RTC混流方案等等,渲染模塊也可以作為其中的一個技術方案。也包括像小程序,尤其是一些小程序、安卓的同層設計方案,渲染方案等等。
每個模塊甚至每個處理的節點,都有可能存在渲染的需求,所以說我們要將渲染模塊進一步拆分。首先通過數據管線的一個能力,把各個模塊的數據匯聚到渲染模塊,然后再進行數據的處理。這里通常會將一些數據轉換成各個平臺的處理能力,比如CPU的Buffer,還有一些Surface等等。
數據加工,相對之前的生產流程這是新增的一個節點,它主要是為了應對一些復雜場景,如安卓的同層渲染、Surface的創建與繪制相分離,比如業務模塊持有了Surface,但是渲染模塊會間接引用并繪制。這里可能還會去做多路流混流的一些參數化配置,可以把每路流作為一個間值進行保存,也會做像圖層跟視頻幀的綁定,然后通過渲染線程同時繪制多路流。
我們都知道渲染必須要獨立于當前的線程,否則對CPU和整體的開銷影響還是比較大的。因此在創建完GL的環境之后,會按照GL隊列將數據進行遍歷拆分,來實現單路流,甚至是多路流的繪制。接下來就是一些標準的渲染流程,包括設置渲染的頂點,取渲染紋理,然后新增混合模式,通過綁定紋理來實現OpenGL整體的繪制。
-
數據采集
+關注
關注
38文章
5925瀏覽量
113539 -
音視頻
+關注
關注
4文章
466瀏覽量
29856 -
數字化
+關注
關注
8文章
8628瀏覽量
61648
原文標題:移動音視頻SDK工程實踐之數據采集和處理
文章出處:【微信號:livevideostack,微信公眾號:LiveVideoStack】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論