
視頻錄制由視頻預覽、伴奏播放、視頻錄制/暫停、倍速/半速錄制四部分組成。
(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。
發表評論 取消回復