1. 程式人生 > >短視訊架構設計與實現

短視訊架構設計與實現

視訊錄製模組

視訊錄製由視訊預覽、伴奏播放、視訊錄製/暫停、倍速/半速錄製四部分組成。

(1)視訊預覽

在Android&IOS平臺我們可以使用自己的Camera+OpenGL ES進行視訊的預覽,這裡的預覽過程一般就是從相機獲取紋理ID並進行諸如美顏、貼紙等特效的處理,之後再繪製到相應的View上。

(2)伴奏播放

伴奏播放是指將一個伴奏檔案以m4a或mp3形式下載到本地,隨後對檔案進行解碼。最常見的解碼方案是FFmpeg。由於經過解碼獲取的PCM或WAV檔案可被直接讀取,伴奏播放的同時我們可將解碼後的伴奏PCM檔案寫入磁碟中以方便後續編輯階段更高效率的使用。

(3)視訊錄製暫停

開始錄製之後系統將預覽的視訊幀編碼並寫入檔案中,這相當於為錄製開闢一條旁路。當得到一張影象後系統會將其送入編碼器,此時這裡的紋理ID保持不變並可再讓其繪製到我們的View上。如果使用者點選暫停則停止編碼,當用戶繼續則將視訊幀進行再次編碼。暫停時也可以當作一段視訊的錄製結束,後期再做拼接處理。

(4)倍速/半速錄製

對短視訊APP來說這一步驟十分重要,常用的方法是抽幀或者插幀操作。例如使用者錄製一段打籃球的視訊並希望在扣籃或上籃時放慢動作,首先需要以正常速度錄製而後再拉長視訊以實現慢放效果。倍速與半速錄製的關鍵是背景聲處理,這裡需要對背景聲進行變速不變調處理。在FFmpeg的AVfilter模組中的Audiofilter裡面有一個被稱為Tempo的濾波器,而在SOX中也有tempo.c可實現類似功能,當然SoundTouch也可以實現。

視訊錄製器分為三部分:輸入、處理和輸出。 輸入就是通過攝像頭和麥克風這類採集裝置去做音訊和畫面的採集。處理則是針對採集到的畫面和聲音進行處理,比如大家熟知的美顏、混響等等。最終輸出其實會分為幾部分,首先是預覽,比如用手機錄製視訊的時候,在手機螢幕上會有預覽畫面;第二部分是編碼,在安卓平臺是採用硬體編碼+軟體編碼的處理,而iOS平臺的相容性較好,所以只採用硬體編碼就可以達到要求;最後是將音視訊資料封裝成一個容器——FLV或MP4,再進行IO輸出, IO輸出有可能是磁碟——錄播場景,也有可能向流媒體伺服器推流——直播場景。

音訊架構設計: 在這裡插入圖片描述 對於安卓平臺,要有一個MP3的Decoder,它可以通過MAD、Lame或者FFmpeg等開源庫來實現,最終通過AudioTrack 的API或者OpenSL ES的API來播放,同時我們把播放PCM資料放到PCM佇列中。而在採集過程,我們一般使用Audio Recoder或OpenSL ES來採集人聲,採集到的人聲也會放在一個PCM佇列中。在一般架構設計中,佇列一般承擔生產者和消費者中間解耦的角色,因此可以看到Input和Output就是上面兩個佇列的生產者,而Consumer執行緒中的Encoder就是消費者——從佇列中取出PCM資料進行編碼。

對於iOS平臺同樣,我們使用的AUGraph,它底層使用的是AudioUnit,其中RemoteIO型別的AudioUnit可以採集人聲,AudioFilePlayer型別的AudioUnit可以播放伴奏。然後通過Mixer型別的AudioUnit將人聲和伴奏混合之後入隊,後面Consumer執行緒中的Encoder從佇列中取出PCM資料進行編碼。

視訊架構設計: 在這裡插入圖片描述

安卓平臺通過Camera採集視訊,在Output中首先是通過EGL Display來回顯預覽介面,其次編碼則是採用MediaCodec硬體編碼和Libx264軟體編碼相結合的實現方式(由於安卓平臺硬體編碼有可能出現相容性問題)。而在iOS平臺,直接使用Camera採集,然後通過GLImageView來進行渲染,GLImageView的實現方式是繼承自UIView,然後在LayerClass中返回CAEAGLLayer,然後構造出OpenGL環境用來渲染紋理,最終再用VideoToolbox進行編碼輸出。編碼後的資料會放到H.264佇列中,那麼這裡的生產者就編碼器,消費者實際上是Consumer模組,它把H.264佇列中資料Mux後再進行IO操作(輸出到磁碟成為mp4檔案或者輸出到流媒體伺服器)。

視訊編輯模組

在這裡插入圖片描述

在視訊編輯模組中,我們需要一個整合時間軸的可正常預覽視訊的標準視訊播放器從而讓使用者更精確地控制每一幀使用什麼特效;視訊特效方面可以新增貼紙或者實現靈魂出竅,井格,九宮格等特效;音訊特效方面我們可以新增背景音樂、視訊靜音、新增聲音特效等;也可進行快放慢放處理。

(1)特效時間Model

當我們在視訊編輯階段點選儲存按紐時,特效時間Model就已建立了,視訊編輯階段會把視訊特效作用的起始時間和終止時間記錄到Model中,在視訊儲存階段的對應時間段內進行特效應用。

(2)特效處理

無論音訊特效還是視訊特效都會按照特效時間Model進行對應的特效處理,處理後的音訊PCM或視訊幀會被送至編碼器(這裡的編碼器一般為H.264或AAC),一般我們會選擇硬體編碼器從而實現高效編解碼處理過程。

(3)編解碼器

解碼器解碼而成的原始資料可被解析為紋理ID和PCM,隨後這些資料會被交給Processor進行處理, 最終交給編碼器並編碼為H264與AAC資料。

(4)封裝格式IO

最終我們會將H264與AAC封裝成MP4並存儲到本地檔案中,整體就是視訊儲存模組。

視訊處理系統:

跨平臺的視訊處理系統實際可以說是跨平臺的複雜濾鏡系統,它所應用的場景主要會有實現美顏、實現瘦臉這種單幀圖片的處理,也有如雨天、老照片等主題效果,以及貼紙效果這幾種。為了達到效果,我們通過OpenGL ES來實現,如果用軟體(CPU中計算)做視訊處理是非常消耗效能的,尤其在移動端是無法接受的。因此它的輸入是紋理ID和時間戳,時間戳主要用於主題和貼紙場景的處理,而輸出則是處理完畢的紋理ID。 在這裡插入圖片描述 這裡特別介紹下GPUImage框架(以iOS平臺作為講解),它的整個流程分為Input、Processor和Output。首先通過GPUImageVideoCamera採集畫面;然後轉化為紋理ID就可以通過模糊、混合、邊緣檢測、飽和度等一系列處理進行優化;最終Output中使用GPUImageView把處理完的視訊幀渲染到螢幕上,而對於錄製則提供了GPUImageMovieWriter,它可以將紋理ID硬體編碼到本地檔案。除了視訊錄製過程,它對視訊播放器和離線處理場景提供了GPUImageMovie作為Input的實現。

對於搭建跨平臺的視訊處理系統,我們需要搭建兩個客戶端的OpenGL環境,安卓平臺使用EGL來提供上下文環境與視窗管理,iOS使用EAGL來提供上下文環境與視窗管理,然後我們抽象出統一介面服務於兩個平臺。

在這裡插入圖片描述

這是結構圖,左邊是根據平臺搭建的環境——Platform OpenGL Environment,右邊是視訊處理系統—VideoEffectProcessor。整個過程為:首先通過Camera或者Decoder採集或者解碼出視訊幀紋理,將紋理ID交給VideoEffectProcessor完成視訊處理功能,而這裡面可能需要很多支援,比如整合一些第三方庫解析XML、解析Json、libpng等等,同時我們也要暴露一些可以動態新增和刪除Filter的功能。當處理完成後會輸出一個Output TexId做渲染,最終呈現到介面上,或者給到Encoder做離線儲存。

OpenGL視訊處理演示:

映象: 在這裡插入圖片描述 映象,先生成一個16:9的螢幕比例的畫布,將它分割為四部分,每部分畫一個相同的視訊幀,因為螢幕被分割為4部分,我們的物體座標在渲染時就不能設定為全屏的。在OpenGL中物體座標,左下角為(-1,-1),右上角為(1,1),這樣我們就可以分別計算出4部分的物體座標。

確認好物體座標後,我們接下來就要確認畫什麼?也就是將視訊幀以什麼樣的方式畫在物體座標上,這時就需要控制紋理座標,我們可以看到OpenGL的紋理座標定義:從左下角(0,0)到右上角(1,1),實際畫的時候左上角是我們完整的紋理,右上角我們需要做映象處理,左下角需要做橫向翻轉,右下角則是針對右上角視訊幀做橫向翻轉,這樣就可以實現簡單的映象效果。

靈魂出竅: 在這裡插入圖片描述

這個特效就是人影有一個向外擴散的效果,同樣它的節奏也是非常重要的。那麼它的實現過程如下:首先我們每隔15幀拷貝一幀作為“靈魂”並且按照比例放大,這裡特別需要提到的是SRT(Scale/Rotate/Translate),基於這三個的組合我們可以寫一個TransformEffect,它可以利用通用的SRT矩陣變化紋理。

在得到放大後的“靈魂”(拷貝幀),我們就需要考慮把“靈魂”和“肉體”(原本視訊幀)混合起來,這裡需要用到GLES的一個內嵌Mix函式將兩個紋理進行mix即可。那麼同理,我們還可以實現眩暈、影隨的效果:眩暈是將每一幀向兩側做位移再與本幀進行mix,而影隨則是將之前的幀快取下來,以一定的間隔和當前幀做mix。