AV Foundation框架提供了很多类来方便对视频和音频的资源处理。AV Foundation框架的核心API是合成。一个合成仅仅是对一种或多种不同媒体资源track的组合。AVMutableComposition
类提供了一个插入,移除以及管理track临时顺序的接口。你可以使用一个mutable composition来将一个新的asset和已经存在的asset集合组合在一起。如果你仅仅需要将很多的asset按序组装到一起,这些就够了。如果你想在合成的过程中执行任何定制的视频,音频处理,你需要分别加入音频混合(audio mix)和视频合成。
使用AVMutableAudioMix
类,你可以在合成的过程中定制音频。同时,你可以指定一个音轨(audio track)的最大音量,或者设定一个音量坡度(volume ramp)。
对于视频合成中的video track如果要编辑的话,你可以使用AVMutableVideoComposition
类来进行处理。对于单一的视频合成来说,你可以对输出的视频文件指明渲染的大小,比例,以及时长。通过一个视频的合成指令(AVMutableVideoCompositionInstruction
类提供),你可以改变视频的背景色并且使用layer指令。在合成过程中,你可以使用这些layer指令(AVMutableVideoCompositionLayerInstruction
类)来对video track实现旋转,旋转坡度(transform ramps),透明度,透明度坡度(opacity ramp)。视频合成类也可以让你使用核心动画来产生响应的效果(使用animationTool
属性)。
使用AVAssetExportSession
,你可以将音频混合(audio mix)和视频合成同时组合到你的合成中去。用你的composition初始化这个export session,然后将音频混合和视频混合分别赋值给audioMix
和videoComposition
属性即可。
创建一个Composition
使用AVMutableComposition
类来创建你自己的composition。为了添加媒体数据到你的composition,你必须通过AVMutableCompositionTrack
类来添加一个或者多个composition tracks。最简单的情况就是用video track和audio track来创建一个mutable composition。
1 | AVMutableComposition *mutableComposition = [AVMutableComposition composition]; |
所有的AVAssetTrack
对象都有一个preferredTransform
属性,该属性中包含了这个asset track对象的方向信息。只要这个asset track在屏幕上展示,那么这个transform
都将会被使用。在上面的代码中,这个layer instruction transform
被设置成了这个asset track
的transform,以便于这个视频在新的合成中一旦调整渲染尺寸,仍然可以正常展示。
设置渲染尺寸和帧时长
为了将视频的方向固定,你必须响应地调整renderSize
属性。你也应该对frameDuration
属性挑选一个合适的值。比如每秒钟30次(或者30帧每秒)。默认情况下,renderScale属性被设置为1.0,这在本次合成过程中是合适的。
CGSize naturalSizeFirst, naturalSizeSecond;
// If the first video asset was shot in portrait mode, then so was the second one
if we made it here.
if (isFirstVideoAssetPortrait) {
// Invert the width and height for the video tracks to ensure that they display
properly.
naturalSizeFirst = CGSizeMake(firstVideoAssetTrack.naturalSize.height,
firstVideoAssetTrack.naturalSize.width);
naturalSizeSecond = CGSizeMake(secondVideoAssetTrack.naturalSize.height,
secondVideoAssetTrack.naturalSize.width);
} else {
// If the videos weren't shot in portrait mode, we can just use their natural
sizes.
naturalSizeFirst = firstVideoAssetTrack.naturalSize;
naturalSizeSecond = secondVideoAssetTrack.naturalSize;
}
float renderWidth, renderHeight;
// Set the renderWidth and renderHeight to the max of the two videos widths and
heights.
if (naturalSizeFirst.width > naturalSizeSecond.width) {
renderWidth = naturalSizeFirst.width;
} else {
renderWidth = naturalSizeSecond.width;
}
if (naturalSizeFirst.height > naturalSizeSecond.height) {
renderHeight = naturalSizeFirst.height;
} else {
renderHeight = naturalSizeSecond.height;
}
mutableVideoComposition.renderSize = CGSizeMake(renderWidth, renderHeight);
// Set the frame duration to an appropriate value (i.e. 30 frames per second for
video).
mutableVideoComposition.frameDuration = CMTimeMake(1,30);
导出合成,并且将它保存到Camera Roll中
在该过程的最后一个步骤设计到将整个合成保存到一个单独的视频文件,并且将视频存储到Camera Roll中。你可以使用AVAssetExportSession
对象来创建这个新的video文件,然后你将它传递到输出文件期望的URL
中。然后使用ALAssetLibrary
对象来保存这个结果视频文件到Camera Roll中。
// Create a static date formatter so we only have to initialize it once.
static NSDateFormatter *kDateFormatter;
if (!kDateFormatter) {
kDateFormatter = [[NSDateFormatter alloc] init];
kDateFormatter.dateStyle = NSDateFormatterMediumStyle;
kDateFormatter.timeStyle = NSDateFormatterShortStyle;
}
// Create the export session with the composition and set the preset to the highest quality.
AVAssetExportSession *exporter = [[AVAssetExportSession alloc]
initWithAsset:mutableComposition presetName:AVAssetExportPresetHighestQuality];
// Set the desired output URL for the file created by the export process.
exporter.outputURL = [[[[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil
create:@YES error:nil] URLByAppendingPathComponent:[kDateFormatter stringFromDate:[NSDate date]]] URLByAppendingPathExtension:CFBridgingRelease(UTTypeCopyPreferredTagWithClass((CFStringRef)AVFileTypeQuickTimeMovie,
kUTTagClassFilenameExtension))];
// Set the output file type to be a QuickTime movie.
exporter.outputFileType = AVFileTypeQuickTimeMovie;
exporter.shouldOptimizeForNetworkUse = YES;
exporter.videoComposition = mutableVideoComposition;
// Asynchronously export the composition to a video file and save this file to the camera roll once export completes.
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
if (exporter.status == AVAssetExportSessionStatusCompleted) {
ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
if ([assetsLibrary
videoAtPathIsCompatibleWithSavedPhotosAlbum:exporter.outputURL]) {
[assetsLibrary writeVideoAtPathToSavedPhotosAlbum:exporter.outputURL completionBlock:NULL];
}
}
});
}];