Editing(编辑)
AVFoundation框架提供了一组功能丰富的类,以便于编辑视听资料。AVFoundation的编辑API的核心是组合。组合仅仅是来自一个或多个不同媒体asset的曲目的集合。的AVMutableComposition类提供用于插入和移除轨道,以及管理他们的时间排序的接口。图3-1显示了新组合如何从现有asset的组合中拼接成一个新的asset。如果您想要做的是将多个asset合并到一个文件中,那就是您需要的细节。如果您想在作曲中的轨道上执行任何自定义音频或视频处理,则需要分别包含音频混合或视频构图。
使用AVMutableAudioMix该类,您可以对构图中的音轨执行自定义音频处理,如图3-2所示。目前,您可以为音轨指定最大音量或设置音量斜坡。
您可以使用AVMutableVideoComposition课程直接与组合中的视频轨道一起工作,以进行编辑,如图3-3所示。使用单个视频构图,您可以为输出视频指定所需的渲染大小和缩放以及帧持续时间。通过视频作品的指示(由AVMutableVideoCompositionInstruction类表示),您可以修改视频的背景颜色并应用图层说明。这些层指令(由AVMutableVideoCompositionLayerInstruction类表示)可用于将转换,变换斜坡,不透明度和不透明度斜坡应用于组合中的视频轨道。视频构图类还使您能够使用该animationTool属性将核心动画框架的效果引入到您的视频中。
要将您的作品与音频混合和视频合成相结合,可以使用AVAssetExportSession对象,如图3-4所示。您可以使用您的作品初始化导出会话,然后分别将音频混合和视频组合分配给该属性audioMix和videoComposition属性。
Creating a Composition
要创建自己的构图,您可以使用AVMutableComposition该类。要将媒体数据添加到组合中,您必须添加一个或多个由AVMutableCompositionTrack类表示的组合曲目。最简单的情况是创建一个具有一个视频轨道和一个音轨的可变组合:
AVMutableComposition * mutableComposition = [AVMutableComposition composition];
//创建视频构图轨道。
AVMutableCompositionTrack * mutableCompositionVideoTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
//创建音频合成音轨。
AVMutableCompositionTrack * mutableCompositionAudioTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
Options for Initializing a Composition Track
添加新曲目时,您必须同时提供媒体类型和曲目ID。虽然音频和视频是最常用的媒体类型,但您也可以指定其他媒体类型,例如AVMediaTypeSubtitle或AVMediaTypeText。
与某些视听数据相关联的每个轨道具有称为轨迹ID的唯一标识符。如果您指定kCMPersistentTrackID_Invalid为首选轨道ID,则会为您自动生成并与轨道相关联的唯一标识符。
Adding Audiovisual Data to a Composition(将视听数据添加到组合)
一旦您有一个或多个曲目的作品,您可以开始将媒体数据添加到相应的曲目。要将媒体数据添加到合成曲目,您需要访问AVAsset媒体数据所在的对象。您可以使用可变成分跟踪界面将同一底层媒体类型的多个轨道放在同一轨道上。以下示例说明如何依次添加两个不同的视频资源轨道到同一个组合轨道:
// You can retrieve AVAssets from a number of places, like the camera roll for example.
AVAsset *videoAsset = <#AVAsset with at least one video track#>;
AVAsset *anotherVideoAsset = <#another AVAsset with at least one video track#>;
// Get the first video track from each asset.
AVAssetTrack *videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVAssetTrack *anotherVideoAssetTrack = [[anotherVideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
// Add them both to the composition.
[mutableCompositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,videoAssetTrack.timeRange.duration) ofTrack:videoAssetTrack atTime:kCMTimeZero error:nil];
[mutableCompositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,anotherVideoAssetTrack.timeRange.duration) ofTrack:anotherVideoAssetTrack atTime:videoAssetTrack.timeRange.duration error:nil];
Retrieving Compatible Composition Tracks(检索兼容的构图轨迹)
在可能的情况下,每个媒体类型应该只有一个组合轨。兼容的asset轨道的统一导致最少的资源使用量。当连续呈现媒体数据时,您应该将相同类型的任何媒体数据放在同一作曲轨道上。您可以查询可变组合来确定是否有与所需asset轨道兼容的组合曲目:
AVMutableCompositionTrack * compatibleCompositionTrack = [mutableComposition mutableTrackCompatibleWithTrack:<#the AVAssetTrack you want to insert#>];
if(compatibleCompositionTrack){
//执行继续。
}
注意 将多个视频片段放置在同一作曲轨道上可能会导致在视频片段(特别是嵌入式设备)之间的转换时放弃播放帧。为视频片段选择合成曲目的数量完全取决于您的应用程序的设计及其预期平台。 |
---|
Generating a Volume Ramp(生成音量斜坡)
单个AVMutableAudioMix对象可以单独对组合中的所有音轨进行自定义音频处理。您可以使用audioMix类方法创建音频混合,并使用AVMutableAudioMixInputParameters该类的实例将音频混合与组合中的特定曲目相关联。可以使用音频混合来改变音轨的音量。以下示例显示如何在特定音轨上设置音量斜坡,以在组合的持续时间内缓慢淡出音频:
AVMutableAudioMix * mutableAudioMix = [AVMutableAudioMix audioMix];
//创建音频混合输入参数对象。
音频混合输入参数
//设置音量斜坡在组合的持续时间内慢慢淡出音频输出。
[mixParameters setVolumeRampFromStartVolume:1.f toEndVolume:0.f timeRange:CMTimeRangeMake(kCMTimeZero,mutableComposition.duration)];
//将输入参数附加到音频混合。
mutableAudioMix.inputParameters = @ [mixParameters];
Performing Custom Video Processing(执行自定义视频处理)
与音频组合一样,您只需要一个AVMutableVideoComposition
对象来对您的作品的视频轨道执行所有自定义视频处理。使用视频构图,您可以直接设置合成视频轨道的相应渲染大小,缩放比例和帧速率。有关为这些属性设置适当值的详细示例,请参阅设置渲染大小和帧持续时间。
Changing the Composition’s Background Color(改变作品的背景颜色)
所有视频构图也必须AVVideoCompositionInstruction包含至少包含一个视频构图指令的对象数组。您可以使用AVMutableVideoCompositionInstruction该类创建自己的视频构图说明。使用视频合成指令,您可以修改组合的背景颜色,指定是否需要后处理或应用图层指令。
以下示例说明如何创建将整个构图的背景颜色更改为红色的视频合成指令
AVMutableVideoCompositionInstruction * mutableVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
mutableVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,mutableComposition.duration);
mutableVideoCompositionInstruction.backgroundColor = [[UIColor redColor] CGColor];
Applying Opacity Ramps(应用不透明度斜坡)
视频合成指令也可用于应用视频合成图层指令。一个AVMutableVideoCompositionLayerInstruction对象可以应用变换,组合物内的变换坡道,不透明度和不透明度坡道到一定视频道。在视频合成指令的layerInstructions阵列中的层指令的顺序决定了在构图指令的持续时间内,来自源轨道的视频帧应如何分层和组合。以下代码片段显示了如何设置不透明度斜坡以在转换到第二个视频之前缓慢淡出组合中的第一个视频:
AVAsset * firstVideoAssetTrack = <#AVAssetTrack表示组合#中播放的第一个视频段;
AVAsset * secondVideoAssetTrack = <#AVAssetTrack表示组合#中播放的第二个视频段;
//创建第一个视频构图指令。
AVMutableVideoCompositionInstruction * firstVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
//设置其时间范围以跨越第一个视频轨道的持续时间。
firstVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,firstVideoAssetTrack.timeRange.duration);
//创建图层指令并将其与合成视频轨道相关联。
AVMutableVideoCompositionLayerInstruction * firstVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:mutableCompositionVideoTrack];
//创建不透明度斜坡以在整个持续时间内淡出第一个视频轨道。
[firstVideoLayerInstruction setOpacityRampFromStartOpacity:1.f toEndOpacity:0.f timeRange:CMTimeRangeMake(kCMTimeZero,firstVideoAssetTrack.timeRange.duration)];
//创建第二个视频合成指令,使第二个视频轨道不透明。
AVMutableVideoCompositionInstruction * secondVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
//设置其时间范围以跨越第二个视频轨道的持续时间。
secondVideoCompositionInstruction.timeRange = CMTimeRangeMake(firstVideoAssetTrack.timeRange.duration,CMTimeAdd(firstVideoAssetTrack.timeRange.duration,secondVideoAssetTrack.timeRange.duration));
//创建第二层指令并将其与合成视频轨道相关联。
AVMutableVideoCompositionLayerInstruction * secondVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:mutableCompositionVideoTrack];
//将第一层指令附加到第一个视频构图指令。
firstVideoCompositionInstruction.layerInstructions = @ [firstVideoLayerInstruction];
//将第二层指令附加到第二个视频构图指令。
secondVideoCompositionInstruction.layerInstructions = @ [secondVideoLayerInstruction];
//将两个视频构图指令附加到视频构图。
AVMutableVideoComposition * mutableVideoComposition = [AVMutableVideoComposition videoComposition];
mutableVideoComposition.instructions = @ [firstVideoCompositionInstruction,secondVideoCompositionInstruction];
Incorporating Core Animation Effects(结合核心动画效果)
视频构图可以通过animationTool属性添加Core Animation的功能。通过这个动画工具,您可以完成诸如水印视频和添加标题或动画叠加等任务。核心动画可以以两种不同的方式与视频作品一起使用:您可以添加一个核心动画图层作为自己的个人作品轨迹,或者您可以将核心动画效果(使用核心动画图层)直接渲染到构图中的视频帧中。以下代码通过向视频的中心添加水印来显示后一个选项:
CALayer * watermarkLayer = <#CALayer表示所需的水印图像#>;
CALayer * parentLayer = [CALayer layer];
CALayer * videoLayer = [CALayer layer];
parentLayer.frame = CGRectMake(0,0,mutableVideoComposition.renderSize.width,mutableVideoComposition.renderSize.height);
videoLayer.frame = CGRectMake(0,0,mutableVideoComposition.renderSize.width,mutableVideoComposition.renderSize.height);
[parentLayer addSublayer:videoLayer];
watermarkLayer.position = CGPointMake(mutableVideoComposition.renderSize.width / 2,mutableVideoComposition.renderSize.height / 4);
[parentLayer addSublayer:watermarkLayer];
mutableVideoComposition.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer];
Putting It All Together: Combining Multiple Assets and Saving the Result to the Camera Roll(放在一起:组合多个asset并将结果保存到相机卷)
这个简短的代码示例说明了如何组合两个视频asset轨道和音频asset轨道来创建单个视频文件。它显示如何:
- 创建一个AVMutableComposition对象并添加多个AVMutableCompositionTrack对象
- 将AVAssetTrack对象的时间范围添加到兼容的合成轨道
- 检查preferredTransform视频asset轨道的属性以确定视频的方向
- 使用AVMutableVideoCompositionLayerInstruction对象将变换应用于组合中的视频轨道
- 设置适当的值用于renderSize和frameDuration一个视频组合物的性质
- 导出到视频文件时,使用组合结合视频合成
- 将视频文件保存到相机胶卷
注意: 为了专注于最相关的代码,本示例省略了一个完整的应用程序的几个方面,如内存管理和错误处理。要使用AVFoundation,您将有足够的经验与可可推断丢失的部分。 |
---|
Creating the Composition(创建构图)
要使用单独的资源组合曲目,您可以使用AVMutableComposition对象。创建构图并添加一个音频和一个视频轨道。
AVMutableComposition * mutableComposition = [AVMutableComposition composition];
AVMutableCompositionTrack * videoCompositionTrack = [mutableComposition addMutableTrackWithMediaType:
AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack * audioCompositionTrack = [mutableComposition addMutableTrackWithMediaType:
AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
Adding the Assets(添加Asset)
一个空的作文你没有好处。将两个视频asset轨道和音频asset轨道添加到组合。
AVAssetTrack * firstVideoAssetTrack = [[firstVideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVAssetTrack * secondVideoAssetTrack = [[secondVideoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,firstVideoAssetTrack.timeRange.duration)ofTrack:firstVideoAssetTrack atTime:kCMTimeZero error:nil];
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,secondVideoAssetTrack.timeRange.duration)ofTrack:secondVideoAssetTrack atTime:firstVideoAssetTrack.timeRange.duration error:nil];
[audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero,CMTimeAdd(firstVideoAssetTrack.timeRange.duration,secondVideoAssetTrack.timeRange.duration))ofTrack:[[audioAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0] atTime:kCMTimeZero error:nil];
注意: 这假设您有两个asset每个至少包含一个视频轨道,第三个asset至少包含一个音轨。可以从相机胶卷中检索视频,并且可以从音乐库或视频本身检索音轨。 |
---|
Checking the Video Orientations(检查视频方向)
将视频和音轨添加到组合中后,您需要确保两个视频轨道的方向正确。默认情况下,所有视频轨道都假定为横向模式。如果您的视频轨道是以纵向模式拍摄的,导出时视频将不会正确定向。同样,如果您尝试将以纵向模式拍摄的视频与风景模式下的视频拍摄相结合,导出会话将无法完成。
BOOL isFirstVideoPortrait = NO;
CGAffineTransform firstTransform = firstVideoAssetTrack.preferredTransform;
// Check the first video track's preferred transform to determine if it was recorded in portrait mode.
if (firstTransform.a == 0 && firstTransform.d == 0 && (firstTransform.b == 1.0 || firstTransform.b == -1.0) && (firstTransform.c == 1.0 || firstTransform.c == -1.0)) {
isFirstVideoPortrait = YES;
}
BOOL isSecondVideoPortrait = NO;
CGAffineTransform secondTransform = secondVideoAssetTrack.preferredTransform;
// Check the second video track's preferred transform to determine if it was recorded in portrait mode.
if (secondTransform.a == 0 && secondTransform.d == 0 && (secondTransform.b == 1.0 || secondTransform.b == -1.0) && (secondTransform.c == 1.0 || secondTransform.c == -1.0)) {
isSecondVideoPortrait = YES;
}
if ((isFirstVideoAssetPortrait && !isSecondVideoAssetPortrait) || (!isFirstVideoAssetPortrait && isSecondVideoAssetPortrait)) {
UIAlertView *incompatibleVideoOrientationAlert = [[UIAlertView alloc] initWithTitle:@"Error!" message:@"Cannot combine a video shot in portrait mode with a video shot in landscape mode." delegate:self cancelButtonTitle:@"Dismiss" otherButtonTitles:nil];
[incompatibleVideoOrientationAlert show];
return;
}
Applying the Video Composition Layer Instructions(应用视频组合层说明)
一旦知道视频片段具有兼容的方向,您可以对每个视频片段应用必要的图层指令,并将这些图层指令添加到视频构图。
AVMutableVideoCompositionInstruction * firstVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
//设置第一个指令的时间范围,以跨越第一个视频轨道的持续时间。
firstVideoCompositionInstruction.timeRange = CMTimeRangeMake(kCMTimeZero,firstVideoAssetTrack.timeRange.duration);
AVMutableVideoCompositionInstruction * secondVideoCompositionInstruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
//设置第二条指令的时间范围,以跨越第二个视频轨道的持续时间。
secondVideoCompositionInstruction.timeRange = CMTimeRangeMake(firstVideoAssetTrack.timeRange.duration,CMTimeAdd(firstVideoAssetTrack.timeRange.duration,secondVideoAssetTrack.timeRange.duration));
AVMutableVideoCompositionLayerInstruction * firstVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];
//将第一层指令的变换设置为第一个视频轨道的首选变换。
[firstVideoLayerInstruction setTransform:firstTransform atTime:kCMTimeZero];
AVMutableVideoCompositionLayerInstruction * secondVideoLayerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoCompositionTrack];
//将第二层指令的变换设置为第二视频轨道的首选变换。
[secondVideoLayerInstruction setTransform:secondTransform atTime:
firstVideoAssetTrack.timeRange.duration];
firstVideoCompositionInstruction.layerInstructions = @ [firstVideoLayerInstruction];
secondVideoCompositionInstruction.layerInstructions = @ [secondVideoLayerInstruction];
AVMutableVideoComposition * mutableVideoComposition = [AVMutableVideoComposition videoComposition];
mutableVideoComposition.instructions = @ [firstVideoCompositionInstruction,secondVideoCompositionInstruction];
所有AVAssetTrack对象都具有preferredTransform包含该asset轨道的方向信息的属性。每当asset轨迹显示在屏幕上时,就会应用此转换。
在之前的代码中,层指令的变换设置为asset轨道的变换,以便在调整渲染大小后,新构图中的视频将正确显示。
Setting the Render Size and Frame Duration(设置渲染大小和帧持续时间)
要完成视频导向修复,您必须相应地调整renderSize属性。您还应该为该frameDuration属性选择合适的值,例如1/30秒(或30帧/秒)。默认情况下,renderScale属性设置为1.0,适用于此组合。
CGSize naturalSizeFirst,naturalSizeSecond;
//如果第一个视频asset以纵向模式拍摄,那么第二个视频资源如果我们在这里拍摄,
if(isFirstVideoAssetPortrait){
//反转视频轨道的宽度和高度,以确保它们正确显示。
naturalSizeFirst = CGSizeMake(firstVideoAssetTrack.naturalSize.height,firstVideoAssetTrack.naturalSize.width);
naturalSizeSecond = CGSizeMake(secondVideoAssetTrack.naturalSize.height,secondVideoAssetTrack.naturalSize.width);
}
else {
//如果视频没有以纵向模式拍摄,我们只能使用自然尺寸。
naturalSizeFirst = firstVideoAssetTrack.naturalSize;
naturalSizeSecond = secondVideoAssetTrack.naturalSize;
}
float renderWidth,renderHeight;
//将renderWidth和renderHeight设置为两个视频宽度和高度的最大值。
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);
//将帧持续时间设置为适当的值(即视频为30帧/秒)。
mutableVideoComposition.frameDuration = CMTimeMake(1,30);
Exporting the Composition and Saving it to the Camera Roll(导出构图并将其保存到相机胶卷)
此过程的最后一步是将整个组合导出到单个视频文件中,并将该视频保存到相机卷。您可以使用AVAssetExportSession对象创建新的视频文件,并将其传送给输出文件所需的URL。然后,您可以使用ALAssetsLibrary该类将生成的视频文件保存到相机胶卷。
// 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];
}
}
});
}];