基本介绍
音视频编辑主要依靠AVFoundation框架,首先要有一个AVMutableComposition对象composition,一个composition可以简单的认为是一组轨道(tracks)的集合,这些轨道可以是来自不同媒体资源AVAsset,AVMutableComposition提供了接口来插入或者删除轨道,也可以调整这些轨道的顺序。
下面这张图反映了一个新的 mixComposition 是怎么从已有的 AVAsset 中获取对应的 track 并进行拼接形成新的 AVAsset
如下图所示,我们还可以使用 AVMutableVideoComposition
来直接处理 composition 中的视频轨道。处理一个单独的 video composition 时,你可以指定它的渲染尺寸、缩放比例、帧率等参数并输出最终的视频文件。通过一些针对 video composition 的指令(AVMutableVideoCompositionInstruction 等),我们可以修改视频的背景颜色、应用 layer instructions。这些 layer instructions(AVMutableVideoCompositionLayerInstruction 等)可以用来对 composition 中的视频轨道实施图形变换、添加图形渐变、透明度变换、增加透明度渐变。此外,你还能通过设置 video composition 的 animationTool
属性来应用 Core Animation Framework 框架中的动画效果。
如下图所示,你可以使用 AVAssetExportSession
相关的接口来合并你的 composition 中的 audio mix 和 video composition。你只需要初始化一个 AVAssetExportSession
对象,然后将其 audioMix
和 videoComposition
属性分别设置为你的 audio mix 和 video composition 即可。
创建Composition对象
当使用 AVMutableComposition
创建自己的 composition 时,最典型的,我们可以使用 AVMutableCompositionTrack
来向 composition 中添加一个或多个 composition tracks,比如下面这个简单的例子便是向一个 composition 中添加一个音频轨道和一个视频轨道:
//音频视频合成体
AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init];
//创建音频通道容器
AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
//创建视频通道容器
AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
当为 composition 添加一个新的 track 的时候,需要设置其媒体类型(media type)和 track ID,主要的媒体类型包括:音频、视频、字幕、文本等等。
这里需要注意的是,每个 track 都需要一个唯一的 track ID,比较方便的做法是:设置 track ID 为 kCMPersistentTrackID_Invalid 来为对应的 track 获得一个自动生成的唯一 ID。
向 Composition 添加音视频数据
要将音视频数据添加到一个 composition track 中需要访问媒体数据所在的 AVAsset
,可以使用 AVMutableCompositionTrack
的接口将具有相同媒体类型的多个 track 添加到同一个 composition track 中。下面的例子便是从多个 AVAsset
中各取出一份 video asset track,再添加到一个新的 composition track 中去
CMTime totalDuration = kCMTimeZero;
for (int i = 0; i < videosPathArray.count; i++) {
// AVURLAsset *asset = [AVURLAsset assetWithURL:[NSURL URLWithString:videosPathArray[i]]];
//如果创建AVURLAsset时传入的AVURLAssetPreferPreciseDurationAndTimingKey值为NO(不传默认为NO),duration会取一个估计值,计算量比较小。反之如果为YES,duration需要返回一个精确值,计算量会比较大,耗时比较长
NSDictionary* options = @{AVURLAssetPreferPreciseDurationAndTimingKey:@YES};
AVAsset* asset = [AVURLAsset URLAssetWithURL:videosPathArray[i] options:options];
NSError *erroraudio = nil;
//获取AVAsset中的音频 或者视频
AVAssetTrack *assetAudioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
//向通道内加入音频或者视频
BOOL ba = [audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
ofTrack:assetAudioTrack
atTime:totalDuration
error:&erroraudio];
NSLog(@"erroraudio:%@%d",erroraudio,ba);
NSError *errorVideo = nil;
AVAssetTrack *assetVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo]firstObject];
BOOL bl = [videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
ofTrack:assetVideoTrack
atTime:totalDuration
error:&errorVideo];
NSLog(@"errorVideo:%@%d",errorVideo,bl);
totalDuration = CMTimeAdd(totalDuration, asset.duration);
}
这里需要注意:如果创建AVURLAsset时传入的AVURLAssetPreferPreciseDurationAndTimingKey值为NO(不传默认为NO),duration会取一个估计值,计算量比较小。反之如果为YES,duration需要返回一个精确值,计算量会比较大,耗时比较长,一般我们对短视频的音视频编辑时都是将这个属性设置为YES,来获取准确的duration。
视频水印处理
处理音频是我们使用 AVMutableAudioMix
,那么处理视频时,我们就使用 AVMutableVideoComposition
,只需要一个 AVMutableVideoComposition
实例就可以为 composition 中所有的 video track 做处理,比如设置渲染尺寸、缩放、播放帧率等等。所有的 video composition 也必然对应一组 AVVideoCompositionInstruction
实例,每个 AVVideoCompositionInstruction
中至少包含一条 video composition instruction。我们可以使用 AVMutableVideoCompositionInstruction
来创建我们自己的 video composition instruction,通过这些指令,我们可以修改 composition 的背景颜色、后处理、layer instruction 等等。
我们也可以用 video composition instructions 来应用 video composition layer instructions。AVMutableVideoCompositionLayerInstruction
可以用来设置 video track 的图形变换、图形渐变、透明度、透明度渐变等等。一个 video composition instruction 的 layerInstructions
属性中所存储的 layer instructions 的顺序决定了 tracks 中的视频帧是如何被放置和组合的。
我们还能通过设置 video composition 的 animationTool
属性来使用 Core Animation Framework 框架的强大能力。比如:设置视频水印、视频标题、动画浮层等。
下面的代码展示了直接使用 Core Animation Layer 在视频帧中渲染动画效果的一个例子,在视频右上角添加水印:
CGSize videoSize = [videoTrack naturalSize];
CALayer* aLayer = [CALayer layer];
aLayer.contents = (id)waterImg.CGImage;
aLayer.frame = CGRectMake(videoSize.width - waterImg.size.width - 30, videoSize.height - waterImg.size.height*3, waterImg.size.width, waterImg.size.height);
aLayer.opacity = 0.9;
CALayer *parentLayer = [CALayer layer];
CALayer *videoLayer = [CALayer layer];
parentLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
videoLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height);
[parentLayer addSublayer:videoLayer];
[parentLayer addSublayer:aLayer];
AVMutableVideoComposition* videoComp = [AVMutableVideoComposition videoComposition];
videoComp.renderSize = videoSize;
//表示 30 帧每秒
videoComp.frameDuration = CMTimeMake(1, 30);
//应用 Core Animation Framework 框架中的动画效果。
videoComp.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
AVMutableVideoCompositionInstruction* instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [mixComposition duration]);
AVAssetTrack* mixVideoTrack = [[mixComposition tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVMutableVideoCompositionLayerInstruction* layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:mixVideoTrack];
//layerInstructions 属性中所存储的 layer instructions 的顺序决定了 tracks 中的视频帧是如何被放置和组合的。
instruction.layerInstructions = [NSArray arrayWithObject:layerInstruction];
videoComp.instructions = [NSArray arrayWithObject: instruction];
音视频导出
主要是通过AVAssetExportSession这个类来导出音视频,它可以用来合并你添加了水印等的videoComposition和audioMix。
下面是使用AVAssetExportSession导出音视频的例子:
NSURL *mergeFileURL = [NSURL fileURLWithPath:outpath];
//视频导出工具
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition
presetName:AVAssetExportPreset1280x720];
exporter.videoComposition = videoComp;
/*
exporter.progress
导出进度
This property is not key-value observable.
不支持kvo 监听
只能用定时器监听了 NStimer
*/
exporter.outputURL = mergeFileURL;
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
[videoCamera stopCameraCapture];
EditVideoViewController* view = [[EditVideoViewController alloc]init];
view.width = _width;
view.hight = _hight;
view.bit = _bit;
view.frameRate = _frameRate;
view.videoURL = [NSURL fileURLWithPath:outpath];;
[[NSNotificationCenter defaultCenter] removeObserver:self];
// [[AppDelegate sharedAppDelegate] pushViewController:view animated:YES];
if (self.delegate&&[self.delegate respondsToSelector:@selector(pushCor:)]) {
[self.delegate pushCor:view];
}
[self removeFromSuperview];
});
}];