裁剪视频是很久之前所做的项目中用到的,现在又看到了,整理一下。
与之相关的这些类有些抽象,所以我这里重点将它们具体化。用代码将视频合成其实与绘声绘影/vegas等软件将视频合成的过程类似,首先了解下这类软件一些相关知识:一个工程文件中有很多轨道,如音频轨道1,音频轨道2,音频轨道3,视频轨道1,视频轨道2等等,每个轨道里有许多素材,对于每个视频素材,它可以进行缩放、旋转等操作,素材库中的视频拖到轨道中会分为视频轨和音频轨两个轨道。这里用这些软件里的一些术语类来比这些类:
AVAsset:素材库里的素材;
AVAssetTrack:素材的轨道;
AVMutableComposition :一个用来合成视频的工程文件;
AVMutableCompositionTrack :工程文件中的轨道,有音频轨、视频轨等,里面可以插入各种对应的素材;
AVMutableVideoComposition:用来生成video的组合指令,包含多段instruction。可以决定最终视频的尺寸,裁剪需要在这里进行;
AVMutableVideoCompositionInstruction:一个指令,决定一个timeRange内每个轨道的状态,包含多个layerInstruction;
AVMutableVideoCompositionLayerInstruction:在一个指令的时间范围内,某个轨道的状态;
AVAssetExportSession:配置渲染参数并渲染。
接下来就用这种类比的方式裁剪一个视频:
1.将素材拖入到素材库中
<code class="hljs perl has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">AVAsset <span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">*asset</span> = [AVAsset assetWithURL:outputFileURL];AVAssetTrack <span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">*videoAssetTrack</span> = [[asset tracksWithMediaType:AVMediaTypeVideo]objectAtIndex:<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>];<span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">//</span>素材的视频轨
AVAssetTrack <span class="hljs-variable" style="color: rgb(102, 0, 102); box-sizing: border-box;">*audioAssertTrack</span> = [[asset tracksWithMediaType:AVMediaTypeAudio]objectAtIndex:<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>];<span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">//</span>素材的音频轨</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li></ul>
2.将素材的视频插入视频轨,音频插入音频轨
<code class="hljs ruby has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-constant" style="box-sizing: border-box;">AVMutableComposition</span> *composition = [<span class="hljs-constant" style="box-sizing: border-box;">AVMutableComposition</span> composition];<span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">//</span>这是工程文件<span class="hljs-constant" style="box-sizing: border-box;">AVMutableCompositionTrack</span> *videoCompositionTrack = [composition <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">addMutableTrackWithMediaType:</span><span class="hljs-constant" style="box-sizing: border-box;">AVMediaTypeVideo</span> <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">preferredTrackID:</span>kCMPersistentTrackID_Invalid];<span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">//</span>视频轨道
[videoCompositionTrack <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">insertTimeRange:</span><span class="hljs-constant" style="box-sizing: border-box;">CMTimeRangeMake</span>(kCMTimeZero, videoAssetTrack.timeRange.duration) <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">ofTrack:</span>videoAssetTrack <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">atTime:</span>kCMTimeZero <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">error:</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nil</span>];<span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">//</span>在视频轨道插入一个时间段的视频
<span class="hljs-constant" style="box-sizing: border-box;">AVMutableCompositionTrack</span> *audioCompositionTrack = [composition <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">addMutableTrackWithMediaType:</span><span class="hljs-constant" style="box-sizing: border-box;">AVMediaTypeAudio</span> <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">preferredTrackID:</span>kCMPersistentTrackID_Invalid];<span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">//</span>音频轨道
[audioCompositionTrack <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">insertTimeRange:</span> <span class="hljs-constant" style="box-sizing: border-box;">CMTimeRangeMake</span>(kCMTimeZero, videoAssetTrack.timeRange.duration) <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">ofTrack:</span>audioAssertTrack <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">atTime:</span>kCMTimeZero <span class="hljs-symbol" style="color: rgb(0, 102, 102); box-sizing: border-box;">error:</span><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">nil</span>];<span class="hljs-regexp" style="color: rgb(0, 136, 0); box-sizing: border-box;">//</span>插入音频数据,否则没有声音 </code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li></ul>
3.裁剪视频,就是要将所有视频轨进行裁剪,就需要得到所有的视频轨,而得到一个视频轨就需要得到它上面所有的视频素材
<code class="hljs r has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">AVMutableVideoCompositionLayerInstruction *videoCompositionLayerIns = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoAssetTrack];[videoCompositionLayerIns setTransform:videoAssetTrack.preferredTransform atTime:kCMTimeZero];//得到视频素材(这个例子中只有一个视频)
AVMutableVideoCompositionInstruction *videoCompositionIns = [AVMutableVideoCompositionInstruction videoCompositionInstruction];
[videoCompositionIns setTimeRange:CMTimeRangeMake(kCMTimeZero, videoAssetTrack.timeRange.duration)];//得到视频轨道(这个例子中只有一个轨道)
AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition];
videoComposition.instructions = @[videoCompositionIns];
videoComposition.renderSize = CGSizeMake(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>);//裁剪出对应的大小
videoComposition.frameDuration = CMTimeMake(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span>, <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">30</span>);</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li></ul>
4.导出
<code class="hljs r has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;">AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:composition presetName:AVAssetExportPresetMediumQuality];exporter.videoComposition = videoComposition;
exporter.outputURL = [NSURL fileURLWithPath:_outputFilePath isDirectory:YES];
exporter.outputFileType = AVFileTypeMPEG4;
exporter.shouldOptimizeForNetworkUse = YES;
[exporter exportAsynchronouslyWithCompletionHandler:^{
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (exporter.error) {
//<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>
}<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>{
//<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">...</span>
}
}];</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li></ul>
总结:学习这些类和方法最大的难度在于其抽象性。我的理解未必完全正确,但希望这种类比的方式能够帮助学习