大家好,我是來自RingCentral鈴盛的阮景雄(Bear)。

今天分享的主題是《高性能且靈活的iOS視頻剪輯與特效開源框架VideoLab》。

今天的分享主要包含以上六個方面,首先會對個人及公司做個簡介,其次會介紹VideoLab是什么,第三點會介紹VideoLab的技術(shù)選型,第四點介紹AVFoundation框架,接著會介紹VideoLab的設(shè)計與實現(xiàn),最后會介紹VideoLab后續(xù)的計劃。
1. 個人及公司簡介
首先,先介紹下個人及公司。

我2020年7月份入職了RingCentral,職位是移動端架構(gòu)師。之前的任職工作期間,在視頻處理、視頻采集、視頻播放、直播、社區(qū)等幾個領(lǐng)域都有涉獵。在RingCentral,負責的業(yè)務(wù)主要是移動端的架構(gòu)和iOS平臺模塊化的演進。

接著簡單介紹下RingCentral鈴盛,從右邊的圖可以看出我們公司主要包含 Message/Video/Phone三塊業(yè)務(wù)。Message是最右下角的IM聊天,Video是視頻會議,Phone是最早的打電話功能。我們公司連續(xù)七年獲得統(tǒng)一通信及服務(wù)的領(lǐng)導(dǎo)者,總部位于美國硅谷,鈴盛中國成立于 2011,目前在杭州,廈門,香港均有辦公室。
2. VideoLab 是什么?
接著讓我們來聊下VideoLab是什么?

首先,來看一些關(guān)鍵字:高性能,靈活,視頻剪輯,特效,開源框架,iOS,Swift,Metal,AVFoundation。這些關(guān)鍵字組合成一塊就解釋了VideoLab是什么。
讓我們來看下當前已有的一些Feature,當前已經(jīng)支持了高性能實時剪輯與導(dǎo)出,高自由度組合視頻、圖片、音頻,支持音頻音高設(shè)置、音量調(diào)節(jié),支持CALayer矢量動畫及復(fù)雜的文字動畫,支持關(guān)鍵幀動畫,支持類似于After Effect的預(yù)合成,支持轉(zhuǎn)場,支持自定義各類特效。

讓我們來看下一些Gif的示例,第一張圖片是多圖層的一個示例,組合了三個視頻,一張圖片。第二張是一個拆字的文字動畫。第三張是漸隱漸顯加Transform的關(guān)鍵幀動畫。第四張是類似AE里預(yù)合成的動畫。第五張是zoom blur轉(zhuǎn)場的示例。
3. 框架技術(shù)選型
接著讓我們來談?wù)効蚣艿募夹g(shù)選型。

說到框架選型,難免提到一些友商。Videoleap是業(yè)界的標桿了,它是國外的一家公司,它的母公司出了非常多剪輯和圖片處理的APP。剪映是頭條出品,目前在國內(nèi)做的也是非常好。VN 也是國內(nèi)的,整體體驗還不錯。
在體驗完競品之后,對它們做了個逆向,Videoleap使用的是AVFoundation + OpenGL,剪映主要是AVFoundation + GPUImage,VN 是AVFoundation + CoreImage。再看我之前工作的美拍,它最早是用的AVFoundation + GPUImage,因為那會時間比較早,所以都是直接用AVAssetReader + AVAssetWriter,后面轉(zhuǎn)成了FFmpeg + OpenGL。
在有了這些備選方案之后,我在想要做一個沉淀,要寫一個框架,能不能有一些其他選擇?然后我就選了AVFoundation + Metal,Metal是蘋果自家的渲染引擎,也是蘋果這幾年力推的主力之一,每年的WWDC都可以看到蘋果關(guān)于Metal的topic。
4. AVFoundation框架
接著我們來介紹下AVFoundation視頻剪輯的框架。

- 第一步,創(chuàng)建一個或多個AVAsset。AVAsset是有時間的,模擬音視頻實體的對象。
- 第二步,創(chuàng)建AVComposition、AVVideoComposition以及 AVAudioMix。其中AVComposition指定了音視頻軌道的時間對齊,AVVideoComposition 指定了視頻軌道在任何給定時間點的幾何變換與混合,AVAudioMix管理音頻軌道的混合參數(shù)。這三個對象是視頻剪輯最主要的三個類,可以把第一個類的作用理解為擺放音視頻軌道,第二個類處理視頻混合,第三個類處理音頻混合。
- 第三步,我們可以使用這三個對象來創(chuàng)建AVPlayerItem,并從中創(chuàng)建一個AVPlayer來播放編輯效果。
- 此外,我們也可以使用這三個對象來創(chuàng)建AVAssetExportSession,用來將編輯結(jié)果寫入文件。

接下來,讓我們看下AVComposition,AVComposition是一個或多個AVCompositionTrack音視頻軌道的集合。其中AVCompositionTrack 又可以包含來自多個 AVAsset 的AVAssetTrack。右圖的例子,將兩個AVAsset中的音視頻 AVAssetTrack 組合到AVComposition的音視頻AVCompositionTrack中。

設(shè)想圖中所示的場景,AVComposition包含兩個 AVCompositionTrack。我們在T1 時間點需要混合兩個 AVCompositionTrack的圖像。為了達到這個目的,我們需要使用 AVVideoComposition。

AVVideoComposition可以用來指定渲染大小、渲染縮放以及幀率。圖中紫色的部分包含了一組指令,這些指令存儲了混合的參數(shù)。有了這些混合的參數(shù)之后,可以通過自定義的 Compositor 來混合對應(yīng)的圖像幀。
整體工作流如圖所示,接受指令,把原視頻幀通過合成器,生成合成后的幀,輸出給播放器或者導(dǎo)出器。讓我們聚焦到合成器,我們有多個原始幀,需要處理并輸出新的一幀。
流程可分解為:
- AVAsynchronousVideoCompositionRequest綁定了當前時間的一系列原始幀,以及當前時間所在的 Instruction。
- 收到startVideoCompositionRequest: 回調(diào),并接收到這個 Request。
- 根據(jù)原始幀及Instruction 相關(guān)混合參數(shù),渲染得到合成的幀。
- 調(diào)用finishWithComposedVideoFrame,交付渲染后的幀。

AVAudioMix在AVComposition的音頻軌道上處理音頻。包含一組AVAudioMixInputParameters,每個Parameters對應(yīng)一個音頻的 AVCompositionTrack。右邊的圖片是一個示例,可以看到AVCompositionTrack和AVAudioMixInputParameters是一一對應(yīng)的。
5. VideoLab 設(shè)計與實現(xiàn)
前面我們介紹了AVFoundation視頻剪輯流程,接下來我們介紹下VideoLab框架的設(shè)計與實現(xiàn)。

先簡要介紹下AE(Adobe After Effect),AE是特效設(shè)計師常用的動態(tài)圖形和視覺效果軟件。AE 通過“層”控制視頻、音頻及靜態(tài)圖片的合成,每個媒體(視頻、音頻及靜態(tài)圖片)對象都有自己獨立的軌道。
圖片是在 AE 中合成兩個視頻的示例。在左上角Project區(qū)域內(nèi),有名為Comp1類型為Composition 的一個合成。在 AE 中合成可以認為是一個作品,可以播放導(dǎo)出,也可以設(shè)置寬高值、幀率、背景色等參數(shù)。在下面Timeline Control 區(qū)域內(nèi),包含了兩個圖層,源分別為video1.MOV與video2.MOV。我們可以自由的設(shè)置圖層參數(shù),如Transform,Audio,也可以在右邊區(qū)域自由的移動圖層,達到靈活的組合效果。針對每個圖層,AE里還可以添加一組特效。
讓我們提取一些關(guān)鍵字:Composition合成,Layer圖層,Transform變換,Audio音頻和Source來源。

基于前面對 AE 的分析,我們可以設(shè)計相似的描述方式:
- RenderComposition,對應(yīng)AE中的合成。包含一組RenderLayer。此外,RenderComposition還包含BackgroundColor、FrameDuration、RenderSize,分別對應(yīng)背景色、幀率及渲染大小等剪輯相關(guān)參數(shù)。
- RenderLayer,對應(yīng)AE中的層。包含了Source、TimeRange、Transform、AudioConfiguration、Operations,分別對應(yīng)素材來源、在時間軸的時間區(qū)間、變換(位置、旋轉(zhuǎn)、縮放)、音頻配置及特效操作組。
- RenderLayerGroup,對應(yīng) AE 的預(yù)合成。這個Group繼承自RenderLayer,包含一組RenderLayer。可以理解成先把一組視頻或圖片處理完,再去做合成。
KeyframeAnimation,對應(yīng) AE 的關(guān)鍵幀動畫。包含了KeyPath、Values、KeyTimes、緩動函數(shù)數(shù)組。
從上面的圖示可以看到,我們可以靈活自由的放置這些區(qū)域。

前面介紹了RenderComposition、RenderLayer、RenderLayerGroup 以及KeyframeAnimation。從前面的AVFoundation 介紹可知,我們需要生成AVPlayerItem與AVAssetExportSession 用于播放與導(dǎo)出。因此,我們需要有一個對象可以解析這幾個描述對象,并用AVFoundation 的方法生成AVPlayerItem 與AVAssetExportSession。框架將這個對象命名為VideoLab,可以理解成這是一個實驗室。
可以看到新的流程,把AVComposition/AVVideoComposition/AVAudioMix都封裝在了VideoLab內(nèi)。這樣做極大的簡化了開發(fā)對AVFoundation的認知,現(xiàn)在和AE比較相似,可以非常方便的組合這些圖層。流程就轉(zhuǎn)變?yōu)椋?/div>
- 創(chuàng)建一個或多個RenderLayer。
- 創(chuàng)建RenderComposition,設(shè)置其BackgroundColor、FrameDuration、RenderSize,以及RenderLayer 數(shù)組。
- 使用創(chuàng)建的RenderComposition創(chuàng)建 VideoLab。
- 使用創(chuàng)建的VideoLab生成AVPlayerItem或AVAssetExportSession。

那VideoLab是如何把這些描述對象轉(zhuǎn)換為AVFoundation的三大對象的呢?
先來看下AVComposition,我們需要給AVComposition分別添加視頻軌道與音頻軌道。如圖所示,這個RenderComposition包含6個RenderLayer,其中一個是RenderLayerGroup。
第一步是將RenderLayer轉(zhuǎn)換VideoRenderLayer,VideoRenderLayer 是框架內(nèi)部對象,包含一個RenderLayer,主要負責將RenderLayer的視頻軌道添加到AVComposition中。可轉(zhuǎn)換為VideoRenderLayer的 RenderLayer包含以下幾類:1. Source包含視頻軌道;2. Source為圖片類型;3. 特效操作組不為空(Operations)。

轉(zhuǎn)化為VideoRenderLayer后的第二步是將VideoRenderLayer視頻軌道添加到AVComposition中。從上圖中的例子可以看到,我們有3個視頻軌道,還有一個Blank Video Track。 這里的空視頻是指視頻軌道是黑幀且不包含音頻軌道的視頻,為image或只有Operation的VideoRenderLayer服務(wù)。
從圖中能看到VideoRenderLayer1和VideoRenderLayer5共用的一個視頻軌道,這是因為蘋果對視頻軌道有限制,我們需要盡量的重用,每條視頻軌道對應(yīng)一個解碼器,當解碼器數(shù)量超出系統(tǒng)限制時,會出現(xiàn)無法解碼的錯誤。框架視頻軌道重用的原則是,如果要放入的 VideoRenderLayer 與之前視頻軌道的VideoRenderLayer在時間上沒有交集,則可以重用這個視頻軌道,所有視頻軌道都重用不了則新增一個視頻軌道。當然這些其實都不重要,因為都封裝在了VideoLab里面。

讓我們接著聊下添加音頻軌道,添加音頻軌道第一步是將RenderLayer 轉(zhuǎn)換為AudioRenderLayer,AudioRenderLayer是框架內(nèi)部對象,包含一個RenderLayer,主要負責將RenderLayer的音頻軌道添到AVComposition中。可轉(zhuǎn)換為AudioRenderLayer的RenderLayer只需滿足一個條件:Source包含音頻軌道。轉(zhuǎn)換AudioRenderLayer之后如右圖所示。

添加音頻軌道的第二步,將AudioRenderLayer視頻軌道添加到AVComposition中,對于RenderLayer的Source包含音頻軌道的AudioRenderLayer,從Source中獲取音頻AVAssetTrack,添加到AVComposition。
如右圖所示,不同于視頻軌道的重用,音頻的每個AudioRenderLayer都對應(yīng)一個音頻軌道。這是由于一個AVAudioMixInputParameters與一個音頻的軌道一一對應(yīng),而其音高設(shè)置(audioTimePitchAlgorithm)作用于整個音頻軌道。如果重用的話,會存在一個音頻軌道有多個AudioRenderLayer的情況,這樣會導(dǎo)致所有的AudioRenderLayer都要配置同樣的音高,這顯然是不合理的。

接下來介紹一下關(guān)于渲染的實現(xiàn)。從前面的AVFoundation介紹可知,AVVideoComposition可以用來指定渲染大小和渲染縮放,以及幀率。此外,還有一組存儲了混合參數(shù)的指令。有了這些指令后,AVVideoComposition可以通過自定義混合器來混合對應(yīng)的圖像幀。
第一步是創(chuàng)建指令,我們會在時間軸上標記每個VideoRenderLayer的起始時間點與結(jié)束時間點。然后為每個時間間隔創(chuàng)建一個Instruction,與時間間隔有交集的VideoRenderLayer,都作為Instruction的混合參數(shù)。
然后我們對前面的Compositor工作流程做一個更新,將混合參數(shù)更新為與Instruction有交集的VideoRenderLayer組。對于混合規(guī)則的話,是按層級渲染,從下往上。如當前層級有紋理則先處理自己的紋理,再混合進前面的紋理。

從前面的AVFoundation介紹可知,AVAudioMix用于處理音頻。包含一組的AVAudioMixInputParameters,可以設(shè)置實時處理音頻,指定音高算法。音頻混合比較簡單,只要為每個AudioRenderLayer創(chuàng)建了一個AVAudioMixInputParameters即可。
6. VideoLab后續(xù)計劃
前面介紹了VideoLab的設(shè)計與實現(xiàn)。當然要做一個好的開源框架還需要不斷的完善,接下來介紹一些VideoLab后續(xù)的計劃。

首先是支持OpenGL,GL還是目前大多數(shù)公司選擇渲染引擎的首選,VideoLab 的規(guī)劃是能同時支持Metal + OpenGL,使用方?jīng)Q定渲染引擎使用Metal或Open GL。其次會持續(xù)完善特性,如變速、更便捷的轉(zhuǎn)場使用方式。接下來會開始寫有UI交互的Demo,這樣可能會更直接一些。最后,當然期望VideoLab是可以跨平臺,期望是上層能有統(tǒng)一的C++封裝API,統(tǒng)一設(shè)計思路,底下用各自的平臺優(yōu)勢,比如iOS用AVFoundation 做編解碼,Android用 FFmpeg;iOS用Metal/GL,Android用Vulkan/GL。
最后也是期望能有更多的人參與維護,畢竟一個人的能力比較有限,大家一起維護能有更多的未來暢想。
這里附帶上Github地址:https://github.com/ruanjx/VideoLab
以上是我的全部分享,謝謝大家。
【免責聲明】本文僅代表作者本人觀點,與CTI論壇無關(guān)。CTI論壇對文中陳述、觀點判斷保持中立,不對所包含內(nèi)容的準確性、可靠性或完整性提供任何明示或暗示的保證。請讀者僅作參考,并請自行承擔全部責任。
相關(guān)閱讀:
- ·專訪RingCentral鈴盛陳志豪:未來被重新定義,企業(yè)該如何應(yīng)對2021-12-15 14:26:01