音视频编辑引擎

移动端音视频领域自从抖音火爆之后开始成为一个备受开发者关注的行业。抖音主推音视频编辑工具“剪映”当之无愧为行业中的佼佼者,放眼整个移动APP音视频市场,算是功能最强大的音视频编辑工具。今天就来讲讲如何在iOS上实现一个类似的音视频编辑引擎。

工欲善其事,必先利其器。我们需要对苹果提供的标准API进行一些个性化定制抽象。

一、AVFoundation 视频编辑API

1、音视频资源组装

在 AVFoundation 中,视频和音频数据可以用 AVAsset 表示,AVAsset 里面包含了 AVAssetTrack 数据。

比如:一个视频文件里面包含了一个视频 track 和两个音频 track。

图:AVAsset 及其子类结构


通过上图发现可以使用 AVComposition 对 track 进行裁剪和变速等操作,也可以把多段 track 拼接到 AVComposition 里面。

例如将多段视频合并为一个视频文件

在处理完 track 的拼接和修改后,得到最终的 AVComposition,它是 AVAsset 的子类,也就是说可以把它传递到 AVPlayer、AVAssetImageGenerator、AVExportSession 和 AVAssetReader 里面作为数据源,把 AVComposition 当成是一个视频数据进行处理。

2、视频画面拼接/混音处理

AVFoundation 提供了 AVVideoComposition 对象和 AVVideoCompositing 协议用于处理视频的画面帧。

图:AVVideoComposition 结构图


2.1AVVideoComposition 支持特性:

设置帧率;

设置输出画面尺寸;

设置不同的 video track 应用何种编辑操作以及可以将视频画面嵌套在 CALayer 中;

设置video track 的编辑(通过设置 AVVideoComposition 里的 instructions 属性实现,它是一个 AVVideoCompositionInstruction 协议数组,AVVideoCompositionInstruction 内定义了处理的时间范围、需要处理的 track ID 有哪些等)

图:AVVideoCompositionInstruction 在时间轴中的组成


重点关注 timerange 20s~30s区间。

将视频画面嵌套在 CALayer 内可以通过设置 AVVideoCompositionCoreAnimationTool 给 AVVideoComposition。

AVVideoCompositionCoreAnimationTool 有两种使用场景:

一是可以添加一个 CALayer 做一个独立的 track 渲染到视频画面上;

二是可以设置一个 parentLayer,然后把视频 layer 放置在这个 parentLayer 上,并且还可以放入其它的 layer。

图:AVVideoCompositionCoreAnimationTool 设置的 layer 层级


通过上述的动画配置可以实现一些简单的画面融合及动画效果,如果想实现类似抖音中丰富的画面效果,我们可能就需要使用AVVideoCompositing了。

 AVVideoCompositing 这个协议可以接管视频画面的渲染。

实现了AVVideoCompositing 协议的类中,AVComposition 处理到某一时间点的视频时,会向AVVideoCompositing 发起请求,AVVideoCompositing 内根据请求包含的视频帧、时间信息、画布大小信息等,根据具体的业务逻辑进行处理,最后将处理后的视频数据返回。

例如 介绍AVVideoCompositionInstruction中timerange:20s~30s这个时间区间,AVVideoCompositing会同时接收到videoTrack1和videoTrack2的画面解码,然后对画面做一些复杂的图层效果处理(包括滤镜处理)。

2.2 AVFoundation 中的音频处理

AVFoundation 提供了 AVAudioMix 用于处理音频数据。 AVAudioMix 这个类很简单,只有一个 inputParameters 属性,它是一个 AVAudioMixInputParameters 数组。具体的音频处理都在 AVAudioMixInputParameters 里进行配置。

不同于 AVVideoComposition 的 instructions,AVVideoCompositionInstruction 可以传入多个 trackID 方便之后多个视频画面进行合成。AVAudioMixInputParameters 只能绑定单个 AVAssetTrack 的音频数据。

AVAudioMixInputParameters 内可以设置音量,支持分段设置音量,以及设置两个时间点的音量变化,比如 0 - 1 秒,音量大小从 0 - 1.0 线性递增。

AVAudioMixInputParameters 内还有个 audioTapProcessor 属性,他是一个 MTAudioProcessingTap 类。这个属性提供了接口用于实时处理音频数据。


总结一下 AVFoundation中常用的几个类,避免混淆

AVAsset 所有媒体资源的承载类。可以是音频,视频,图像。

AVMutableComposition 用于组合AVAsset的音视频轨道。

AVMutableCompositionTrack 分为视频轨道,音频轨道,它由AVAsset内部解码生成。

AVMutableVideoComposition 视频编辑指令管理类,它是整个视频编辑里动画指令组合。

AVMutableVideoCompositionInstruction 视频视图编辑指令,可以用它来设置视频的背景颜色等。它可以包含一组AVVideoCompositionLayerInstruction。

AVMutableVideoCompositionLayerInstruction 视频层编辑指令,可以用它设置视频的transform,opacity等。

二、音视频编辑框架

通过对AVFoundation常用API的了解,你一定也想到通过对AVVideoCompositing作为突破口,只要获取解码后实时视频数据,我们就可以很方便的对进行画面效果处理。但是这个API只支持视频的输出,那么当我们希望将多张图片/图片+视频合成为视频的时候该如何处理呢?

由于AVFoundation的图层特效能力有限、不支持图片等数据流进行视频合成,所以我们需要自己进行图片和视频的基于时间线进行排布并进行驱动的引擎框架,来实现音视频数据从解码到画面特效应用,最终写入文件或者进行预览整个流程。

2.1 音视频处理基本流程


工作流程:

对视频/图片资源进行时间线排布以及视频特效和音频特效描述 (资源出现顺序、转场信息、音频混合、音频声音大小控制,画面特效)

将视频或者图片进行解码得到相应的原始画面 

应用相应的画面Transform

应用相应的视频特效

得到最终画面和最终音频数据

写入文件或者预览输出音频播放

其中视频资源解码使用AVAssetReader ,文件写入使用AVAssetWriter, 中间画面特效部分可以使用OpenGL ES或者Metal API进行GPU处理 

2.2跨平台支持

考虑到框架可以很方便的应用到安卓/服务端/PC,做到跨平台。游戏平台有一个很不错的跨平台轻型引擎 cocos2d,整体处理流程和我们要做的事情有点相似。

Cocos2d采用场景树结构来管理游戏对象,把一个游戏划分为不同的场景,场景又可以分为不同的层,一个层又可以拥有任意个可见的游戏对象.游戏对象又可以执行Action来修改其属性.每一个时刻都有一个场景在独立运行,通过切换不同的场景来完成一个游戏流程.cocos2d还采用了引用计数的方式来管理内存,基本上所有的类都派生于拥有引用计数的机制的CCObject.

总结起来调用关系简化为CCDirector→CCScene→CCLayer→CCSprite→CCAction.

音视频引擎需要对不同的特效进行分层,每个特效都对应一个Action,每一个特效在时间线中是变化的,对应为一个Node及Node的参数列表和动画列表.

架构图如下

VdieoEngine:VE引擎核心,构建原始资源(图片/视频/音频)时间线排布,  根据特效描述文件Node创建相关的Action,及不同分层的LayerTree.

CompostionDirector:类似cocos2d中的CCDirector,负责VE中所有资源的管理及调度

Schedule:时间驱动器 更新视频帧及图片帧时间戳,并应用对应时间戳的特效

ActionBuilder:Action实例化工厂类

FilterLayer:不同特效分层

2.3 特效树

特效及资源节点总的来说可以描述为下面的树状结构

2.4 整体架构图

特效资源描述文件包:

字体包(用于特定字体显示)

mp3文件(用于背景音乐)

图片(用于特效混合效果)

视频(用于特效混合效果)

glsl文件(定制特效shader)

Splice.xml文件(描述拼接)

Fitler.xml文件(描述全局特效)

Transition.xml文件(描述转场)

资源动态下载后,通过资源管理模块进行资源解析,传入VideoEngine,进行画面预览及最终视频画面导出。

2.4示例代码

本项目部分代码已开源,欢迎下载体验,https://github.com/guolai/BBZVideoEngine (由于项目已用于shopee 音视频编辑项目,因此部分重要功能并未开源)

创建model

BBZVideoModel *videoModel = [[BBZVideoModel alloc] init];  

加入图片资源

NSString *path = [[NSBundle mainBundle] pathForResource:@"IMG_7305" ofType:@"HEIC" inDirectory:@"Resource"];

[videoModel addImageSource:path]; 

加入视频资源

NSString *path = [[NSBundle mainBundle] pathForResource:@"douyin3" ofType:@"mp4" inDirectory:@"Resource"];

[videoModel addVideoSource:path];

加入背景音乐

NSString *path = [[NSBundle mainBundle] pathForResource:@"jimoshazhouleng" ofType:@"mp3" inDirectory:@"Resource"];

[videoModel addAudioSource:path];

指定转场资源路径

NSString *path = [NSString stringWithFormat:@"%@/Resource/demo2", [[NSBundle mainBundle] bundlePath]];

[videoModel addTransitionGroup:path];

加入视频或者图集 支持加入背景图片,并对内容进行缩放和旋转

NSString *path = [[NSBundle mainBundle] pathForResource:@"IMG_7305" ofType:@"HEIC" inDirectory:@"Resource"];

NSData *data = [NSData dataWithContentsOfFile:path];

UIImage *bgImage = [UIImage imageWithData:data];

videoModel.bgImage = bgImage;

BBZTransformItem *transformItem = [[BBZTransformItem alloc] init];

transformItem.scale = 0.8;

transformItem.angle = 45.0;

videoModel.transform = transformItem;

视频水印支持动态图片序列帧

NSMutableArray *multiArray = [NSMutableArray array];

for (int i = 1; i < 10; i++) {

    NSString *strName = [NSString stringWithFormat:@"00%d@2x", i];

    NSString *icon = [[NSBundle mainBundle] pathForResource:strName ofType:@"png" inDirectory:@"Resource/icon"];

    NSData *data = [NSData dataWithContentsOfFile:icon];

    UIImage *image = [UIImage imageWithData:data];

    [multiArray addObject:image];

}

videoModel.maskImage = multiArray;

启动合成任务

BBZExportTask *task = [BBZExportTask taskWithModel:videoModel];

task.completeBlock= ^(BOOL sucess, NSError *error) {

};

task.progressBlock = ^(CGFloat progress) {

};

[task start];

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,667评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,361评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,700评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,027评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,988评论 5 361
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,230评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,705评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,366评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,496评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,405评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,453评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,126评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,725评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,803评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,015评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,514评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,111评论 2 341

推荐阅读更多精彩内容