parent
							
								
									244eacd306
								
							
						
					
					
						commit
						b73f4ddfd3
					
				| @ -0,0 +1,154 @@ | ||||
| # iOS短视频篇:音视频编辑之音视频合成,添加水印及音视频导出 | ||||
| 
 | ||||
| ## 1.基本介绍 | ||||
| 
 | ||||
| 音视频编辑主要依靠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 即可。 | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ## 2.创建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。 | ||||
| 
 | ||||
| ## 3.向 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。 | ||||
| 
 | ||||
| ## 4.视频水印处理 | ||||
| 
 | ||||
| 处理音频是我们使用 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]; | ||||
| 
 | ||||
| ## 5.音视频导出 | ||||
| 
 | ||||
| 主要是通过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]; | ||||
|         }); | ||||
|          | ||||
| 
 | ||||
| 原文链接:https://blog.csdn.net/weixin_42433480/article/details/90109873 | ||||
					Loading…
					
					
				
		Reference in new issue