0%

AVFoundation-媒体的组合和编辑

AVFoundation 能支持非线性、无损的编辑工具,并且可以在原始媒体资源不破坏的情况下无约束地编辑。

1. 基础

AVFoundation 有关资源组合的功能均需要用到 AVComposition 类,这个类将其他几种媒体资源组合成一个临时的排列,这个临时排列可以像 AVAsset 一样使用。一个 AVComposition 中的轨道都是 AVCompositionTrack,而 AVCompositionTrack 又由多个 AVCompositionTrackSegment 组成,代表这个组合中的实际媒体区域。AVComposition 及其相关类没有遵循 NSCoding 协议,因此不能将其简单归档到存储磁盘里,需要自定义数据模型来保存。

1.1 时间的处理

为了避免浮点数的不精确性导致的误差,AVFoundation 普遍使用 CMTime 数据类型来表达时间格式。

1
2
3
4
5
6
{
CMTimeValue value;
CMTimeScale timescale;
CMTimeFlags flags;// 表示时间状态,如是否有效、是否有舍入值
CMTimeEpoch epoch;
} CMTime;

另外使用 CMTimeRange 来表达时间范围

1
2
3
4
{
CMTime start;
CMTime duration;
} CMTimeRange;

下面是一些常用方法

  • CMTimeMake 创造一个 CMTime
  • CMTimeShow 打印 CMTime 值
  • CMTimeAdd 两个 CMTime 相加
  • CMTimeSubtract 两个 CMTime 相减
  • CMTimeRangeMake 创造一个 CMTimeRange
  • CMTimeRangeFromTimeToTime 以两个时间点创造 CMTimeRange
  • CMTimeRangeGetIntersection 获得两个 CMTimeRange 的交集
  • CMTimeRangeGetUnion 获得两个 CMTImeRange 的并集

2. 组合媒体

组合媒体资源时要注意对一个 AVAsset 的 videoTrack 和 audioTrack 分别进行组合。首先需要初始化一个 AVMutableComposition,还需要初始化两个 AVMutableCompositionTrack,一个用于附加 video,一个用于附加 audio。

1
2
3
4
AVMutableComposition *composition = [AVMutableComposition composition];
__block CMTime cursor = kCMTimeZero;// 标识当前附加资源的时间轴位置
AVMutableCompositionTrack *videoCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];// kCMPersistentTrackID_Invalid 表示 AVFoundation 应该自动生成一个正确的轨道 ID
AVMutableCompositionTrack *audioCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];

对于每一个 AVAsset,需要取出其中的 videoTrack 和 audioTrack,分别加入到对应的 AVMutableCompositionTrack 中

1
2
3
4
5
6
AVAssetTrack *videoTrack = [[targetAsset tracksWithMediaType:AVMediaTypeVideo] firstObject];
[videoCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, targetAsset.duration) ofTrack:videoTrack atTime:cursor error:nil];

AVAssetTrack *audioTracck = [[targetAsset tracksWithMediaType:AVMediaTypeAudio] firstObject];
[audioCompositionTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, targetAsset.duration) ofTrack:audioTracck atTime:cursor error:nil];
cursor = CMTimeAdd(cursor, targetAsset.duration); // 移动当前媒体资源附加位置

这样就完成了一个临时排列 AVMutableComposition,可以将其直接用于播放。

3. 导出媒体

AVMutableComposition 可以在内存中进行使用,但是如果想持久化到磁盘中,就需要进行导出,导出后的媒体资源是一个完整的独立资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
NSString *preset = AVAssetExportPresetHighestQuality;
AVAssetExportSession *export = [AVAssetExportSession exportSessionWithAsset:[composition copy] presetName:preset];
export.outputURL = [self outputUrl];
export.outputFileType = AVFileTypeMPEG4;
[export exportAsynchronouslyWithCompletionHandler:^{
AVAssetExportSessionStatus status = export.status;
if (status == AVAssetExportSessionStatusCompleted) {
[self saveVideo:export.outputURL];
}
}];
}

- (NSURL *)outputUrl
{
NSString *filePath = nil;
NSUInteger count = 0;
do {
filePath = NSTemporaryDirectory();
NSString *numberString = count > 0 ?
[NSString stringWithFormat:@"-%li", (unsigned long) count] : @"";
NSString *fileNameString =
[NSString stringWithFormat:@"Masterpiece-%@.m4v", numberString];
filePath = [filePath stringByAppendingPathComponent:fileNameString];
count++;
} while ([[NSFileManager defaultManager] fileExistsAtPath:filePath]);

return [NSURL fileURLWithPath:filePath];
}

最终在导出结束后,通过导出 URL 可以将目标视频保存到系统相册里。

1
2
3
4
5
6
7
8
9
10
11
12
13
__block NSString *imageIdentifier;
@weakify(self)
[[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
PHAssetChangeRequest *changeRequest = [PHAssetChangeRequest creationRequestForAssetFromVideoAtFileURL:videoURL];
imageIdentifier = changeRequest.placeholderForCreatedAsset.localIdentifier;
} completionHandler:^( BOOL success, NSError * _Nullable error ) {
dispatch_async(dispatch_get_main_queue(), ^{
[MTBProgressHUD dismiss];
if (!success) {
} else {
}
});
}];