AVFoundation 媒体的组合和编辑

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

1. 基础

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

1.1 时间的处理

为了不浮点数的不精确性致使的偏差,AVFoundation 广泛使用 CMTime 数据类型来表达时间格式。工具

{
	CMTimeValue	value;
	CMTimeScale	timescale;
	CMTimeFlags	flags;// 表示时间状态,如是否有效、是否有舍入值
	CMTimeEpoch	epoch;
} CMTime;
复制代码

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

{
	CMTime			start;
	CMTime			duration;
} CMTimeRange;
复制代码

下面是一些经常使用方法spa

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

2. 组合媒体

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

AVMutableComposition *composition = [AVMutableComposition composition];
                    __block CMTime cursor = kCMTimeZero;// 标识当前附加资源的时间轴位置
                    AVMutableCompositionTrack *videoCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
                    AVMutableCompositionTrack *audioCompositionTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
复制代码

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

[mediaAssets enumerateObjectsUsingBlock:^(MTBKDBImerchantMediaAsset * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
                        AVAsset *targetAsset = obj.videoAsset;
                        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 能够在内存中进行使用,可是若是想持久化到磁盘中,就须要进行导出,导出后的媒体资源是一个完整的独立资源。内存

{
	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 能够将目标视频保存到系统相册里。资源

__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 {
            }
        });
    }];
复制代码
相关文章
相关标签/搜索