大家好,我是来自RingCentral铃盛的阮景雄(Bear)。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117021933476.jpg)
今天分享的主题是《高性能且灵活的iOS视频剪辑与特效开源框架VideoLab》。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117021933838.jpg)
今天的分享主要包含以上六个方面,首先会对个人及公司做个简介,其次会介绍VideoLab是什么,第三点会介绍VideoLab的技术选型,第四点介绍AVFoundation框架,接着会介绍VideoLab的设计与实现,最后会介绍VideoLab后续的计划。
1. 个人及公司简介
首先,先介绍下个人及公司。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022008406.jpg)
我2020年7月份入职了RingCentral,职位是移动端架构师。之前的任职工作期间,在视频处理、视频采集、视频播放、直播、社区等几个领域都有涉猎。在RingCentral,负责的业务主要是移动端的架构和iOS平台模块化的演进。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022008408.jpg)
接着简单介绍下RingCentral铃盛,从右边的图可以看出我们公司主要包含 Message/Video/Phone三块业务。Message是最右下角的IM聊天,Video是视频会议,Phone是最早的打电话功能。我们公司连续七年获得统一通信及服务的领导者,总部位于美国硅谷,铃盛中国成立于 2011,目前在杭州,厦门,香港均有办公室。
2. VideoLab 是什么?
接着让我们来聊下VideoLab是什么?
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022104551.jpg)
首先,来看一些关键字:高性能,灵活,视频剪辑,特效,开源框架,iOS,Swift,Metal,AVFoundation。这些关键字组合成一块就解释了VideoLab是什么。
让我们来看下当前已有的一些Feature,当前已经支持了高性能实时剪辑与导出,高自由度组合视频、图片、音频,支持音频音高设置、音量调节,支持CALayer矢量动画及复杂的文字动画,支持关键帧动画,支持类似于After Effect的预合成,支持转场,支持自定义各类特效。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022104766.jpg)
让我们来看下一些Gif的示例,第一张图片是多图层的一个示例,组合了三个视频,一张图片。第二张是一个拆字的文字动画。第三张是渐隐渐显加Transform的关键帧动画。第四张是类似AE里预合成的动画。第五张是zoom blur转场的示例。
3. 框架技术选型
接着让我们来谈谈框架的技术选型。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022104948.jpg)
说到框架选型,难免提到一些友商。Videoleap是业界的标杆了,它是国外的一家公司,它的母公司出了非常多剪辑和图片处理的APP。剪映是头条出品,目前在国内做的也是非常好。VN 也是国内的,整体体验还不错。
在体验完竞品之后,对它们做了个逆向,Videoleap使用的是AVFoundation + OpenGL,剪映主要是AVFoundation + GPUImage,VN 是AVFoundation + CoreImage。再看我之前工作的美拍,它最早是用的AVFoundation + GPUImage,因为那会时间比较早,所以都是直接用AVAssetReader + AVAssetWriter,后面转成了FFmpeg + OpenGL。
在有了这些备选方案之后,我在想要做一个沉淀,要写一个框架,能不能有一些其他选择?然后我就选了AVFoundation + Metal,Metal是苹果自家的渲染引擎,也是苹果这几年力推的主力之一,每年的WWDC都可以看到苹果关于Metal的topic。
4. AVFoundation框架
接着我们来介绍下AVFoundation视频剪辑的框架。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022218825.jpg)
- 第一步,创建一个或多个AVAsset。AVAsset是有时间的,模拟音视频实体的对象。
- 第二步,创建AVComposition、AVVideoComposition以及 AVAudioMix。其中AVComposition指定了音视频轨道的时间对齐,AVVideoComposition 指定了视频轨道在任何给定时间点的几何变换与混合,AVAudioMix管理音频轨道的混合参数。这三个对象是视频剪辑最主要的三个类,可以把第一个类的作用理解为摆放音视频轨道,第二个类处理视频混合,第三个类处理音频混合。
- 第三步,我们可以使用这三个对象来创建AVPlayerItem,并从中创建一个AVPlayer来播放编辑效果。
- 此外,我们也可以使用这三个对象来创建AVAssetExportSession,用来将编辑结果写入文件。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022218469.jpg)
接下来,让我们看下AVComposition,AVComposition是一个或多个AVCompositionTrack音视频轨道的集合。其中AVCompositionTrack 又可以包含来自多个 AVAsset 的AVAssetTrack。右图的例子,将两个AVAsset中的音视频 AVAssetTrack 组合到AVComposition的音视频AVCompositionTrack中。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022219558.jpg)
设想图中所示的场景,AVComposition包含两个 AVCompositionTrack。我们在T1 时间点需要混合两个 AVCompositionTrack的图像。为了达到这个目的,我们需要使用 AVVideoComposition。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022219433.jpg)
AVVideoComposition可以用来指定渲染大小、渲染缩放以及帧率。图中紫色的部分包含了一组指令,这些指令存储了混合的参数。有了这些混合的参数之后,可以通过自定义的 Compositor 来混合对应的图像帧。
整体工作流如图所示,接受指令,把原视频帧通过合成器,生成合成后的帧,输出给播放器或者导出器。让我们聚焦到合成器,我们有多个原始帧,需要处理并输出新的一帧。
流程可分解为:
- AVAsynchronousVideoCompositionRequest绑定了当前时间的一系列原始帧,以及当前时间所在的 Instruction。
- 收到startVideoCompositionRequest: 回调,并接收到这个 Request。
- 根据原始帧及Instruction 相关混合参数,渲染得到合成的帧。
- 调用finishWithComposedVideoFrame,交付渲染后的帧。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022219233.jpg)
AVAudioMix在AVComposition的音频轨道上处理音频。包含一组AVAudioMixInputParameters,每个Parameters对应一个音频的 AVCompositionTrack。右边的图片是一个示例,可以看到AVCompositionTrack和AVAudioMixInputParameters是一一对应的。
5. VideoLab 设计与实现
前面我们介绍了AVFoundation视频剪辑流程,接下来我们介绍下VideoLab框架的设计与实现。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022437554.jpg)
先简要介绍下AE(Adobe After Effect),AE是特效设计师常用的动态图形和视觉效果软件。AE 通过“层”控制视频、音频及静态图片的合成,每个媒体(视频、音频及静态图片)对象都有自己独立的轨道。
图片是在 AE 中合成两个视频的示例。在左上角Project区域内,有名为Comp1类型为Composition 的一个合成。在 AE 中合成可以认为是一个作品,可以播放导出,也可以设置宽高值、帧率、背景色等参数。在下面Timeline Control 区域内,包含了两个图层,源分别为video1.MOV与video2.MOV。我们可以自由的设置图层参数,如Transform,Audio,也可以在右边区域自由的移动图层,达到灵活的组合效果。针对每个图层,AE里还可以添加一组特效。
让我们提取一些关键字:Composition合成,Layer图层,Transform变换,Audio音频和Source来源。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022437365.jpg)
基于前面对 AE 的分析,我们可以设计相似的描述方式:
- RenderComposition,对应AE中的合成。包含一组RenderLayer。此外,RenderComposition还包含BackgroundColor、FrameDuration、RenderSize,分别对应背景色、帧率及渲染大小等剪辑相关参数。
- RenderLayer,对应AE中的层。包含了Source、TimeRange、Transform、AudioConfiguration、Operations,分别对应素材来源、在时间轴的时间区间、变换(位置、旋转、缩放)、音频配置及特效操作组。
- RenderLayerGroup,对应 AE 的预合成。这个Group继承自RenderLayer,包含一组RenderLayer。可以理解成先把一组视频或图片处理完,再去做合成。
KeyframeAnimation,对应 AE 的关键帧动画。包含了KeyPath、Values、KeyTimes、缓动函数数组。
从上面的图示可以看到,我们可以灵活自由的放置这些区域。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022437832.jpg)
前面介绍了RenderComposition、RenderLayer、RenderLayerGroup 以及KeyframeAnimation。从前面的AVFoundation 介绍可知,我们需要生成AVPlayerItem与AVAssetExportSession 用于播放与导出。因此,我们需要有一个对象可以解析这几个描述对象,并用AVFoundation 的方法生成AVPlayerItem 与AVAssetExportSession。框架将这个对象命名为VideoLab,可以理解成这是一个实验室。
可以看到新的流程,把AVComposition/AVVideoComposition/AVAudioMix都封装在了VideoLab内。这样做极大的简化了开发对AVFoundation的认知,现在和AE比较相似,可以非常方便的组合这些图层。流程就转变为:
- 创建一个或多个RenderLayer。
- 创建RenderComposition,设置其BackgroundColor、FrameDuration、RenderSize,以及RenderLayer 数组。
- 使用创建的RenderComposition创建 VideoLab。
- 使用创建的VideoLab生成AVPlayerItem或AVAssetExportSession。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022438107.jpg)
那VideoLab是如何把这些描述对象转换为AVFoundation的三大对象的呢?
先来看下AVComposition,我们需要给AVComposition分别添加视频轨道与音频轨道。如图所示,这个RenderComposition包含6个RenderLayer,其中一个是RenderLayerGroup。
第一步是将RenderLayer转换VideoRenderLayer,VideoRenderLayer 是框架内部对象,包含一个RenderLayer,主要负责将RenderLayer的视频轨道添加到AVComposition中。可转换为VideoRenderLayer的 RenderLayer包含以下几类:1. Source包含视频轨道;2. Source为图片类型;3. 特效操作组不为空(Operations)。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117022438345.jpg)
转化为VideoRenderLayer后的第二步是将VideoRenderLayer视频轨道添加到AVComposition中。从上图中的例子可以看到,我们有3个视频轨道,还有一个Blank Video Track。 这里的空视频是指视频轨道是黑帧且不包含音频轨道的视频,为image或只有Operation的VideoRenderLayer服务。
从图中能看到VideoRenderLayer1和VideoRenderLayer5共用的一个视频轨道,这是因为苹果对视频轨道有限制,我们需要尽量的重用,每条视频轨道对应一个解码器,当解码器数量超出系统限制时,会出现无法解码的错误。框架视频轨道重用的原则是,如果要放入的 VideoRenderLayer 与之前视频轨道的VideoRenderLayer在时间上没有交集,则可以重用这个视频轨道,所有视频轨道都重用不了则新增一个视频轨道。当然这些其实都不重要,因为都封装在了VideoLab里面。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117023253808.jpg)
让我们接着聊下添加音频轨道,添加音频轨道第一步是将RenderLayer 转换为AudioRenderLayer,AudioRenderLayer是框架内部对象,包含一个RenderLayer,主要负责将RenderLayer的音频轨道添到AVComposition中。可转换为AudioRenderLayer的RenderLayer只需满足一个条件:Source包含音频轨道。转换AudioRenderLayer之后如右图所示。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117023254438.jpg)
添加音频轨道的第二步,将AudioRenderLayer视频轨道添加到AVComposition中,对于RenderLayer的Source包含音频轨道的AudioRenderLayer,从Source中获取音频AVAssetTrack,添加到AVComposition。
如右图所示,不同于视频轨道的重用,音频的每个AudioRenderLayer都对应一个音频轨道。这是由于一个AVAudioMixInputParameters与一个音频的轨道一一对应,而其音高设置(audioTimePitchAlgorithm)作用于整个音频轨道。如果重用的话,会存在一个音频轨道有多个AudioRenderLayer的情况,这样会导致所有的AudioRenderLayer都要配置同样的音高,这显然是不合理的。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117023254162.jpg)
接下来介绍一下关于渲染的实现。从前面的AVFoundation介绍可知,AVVideoComposition可以用来指定渲染大小和渲染缩放,以及帧率。此外,还有一组存储了混合参数的指令。有了这些指令后,AVVideoComposition可以通过自定义混合器来混合对应的图像帧。
第一步是创建指令,我们会在时间轴上标记每个VideoRenderLayer的起始时间点与结束时间点。然后为每个时间间隔创建一个Instruction,与时间间隔有交集的VideoRenderLayer,都作为Instruction的混合参数。
然后我们对前面的Compositor工作流程做一个更新,将混合参数更新为与Instruction有交集的VideoRenderLayer组。对于混合规则的话,是按层级渲染,从下往上。如当前层级有纹理则先处理自己的纹理,再混合进前面的纹理。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117023255760.jpg)
从前面的AVFoundation介绍可知,AVAudioMix用于处理音频。包含一组的AVAudioMixInputParameters,可以设置实时处理音频,指定音高算法。音频混合比较简单,只要为每个AudioRenderLayer创建了一个AVAudioMixInputParameters即可。
6. VideoLab后续计划
前面介绍了VideoLab的设计与实现。当然要做一个好的开源框架还需要不断的完善,接下来介绍一些VideoLab后续的计划。
![](http://www.ctiforum.com/uploadfile/2022/0117/20220117023255638.jpg)
首先是支持OpenGL,GL还是目前大多数公司选择渲染引擎的首选,VideoLab 的规划是能同时支持Metal + OpenGL,使用方决定渲染引擎使用Metal或Open GL。其次会持续完善特性,如变速、更便捷的转场使用方式。接下来会开始写有UI交互的Demo,这样可能会更直接一些。最后,当然期望VideoLab是可以跨平台,期望是上层能有统一的C++封装API,统一设计思路,底下用各自的平台优势,比如iOS用AVFoundation 做编解码,Android用 FFmpeg;iOS用Metal/GL,Android用Vulkan/GL。
最后也是期望能有更多的人参与维护,毕竟一个人的能力比较有限,大家一起维护能有更多的未来畅想。
这里附带上Github地址:https://github.com/ruanjx/VideoLab
以上是我的全部分享,谢谢大家。