一個視頻文件中包含了很多軌道(Track),比如一個或多個音頻軌道,一個或多個視頻軌道。我們可以對這些軌道中的數(shù)據(jù)進行各種操作(裁剪、拼接、旋轉(zhuǎn)等)。
本次我用到的視頻打點裁剪相關(guān)功能用到的類如下:
AVAsset:素材,比如出相冊中取出的數(shù)據(jù)
AVAssetTrack:素材的軌道
AVMutableComposition:視頻的工程文件(是AVAsset的子類,可以用來直接播放)
AVMutableCompositionTrack:工程文件對應(yīng)的軌道
這里我的需求是要裁剪調(diào)一個視頻素材的頭尾,第一步需要獲取到素材的視頻軌道和音頻軌道
AVAssetTrack *videoAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0];
AVAssetTrack *audioAssetTrack = [[videoAsset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
第二步,創(chuàng)建工程文件,將素材的軌道數(shù)據(jù)導入到工程文件的軌道中
AVMutableComposition *mutableComposition = [AVMutableComposition composition];
AVMutableCompositionTrack *audioTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *videoTrack = [mutableComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
NSError *errorAudio;
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:audioAssetTrack atTime:kCMTimeZero error:&errorAudio];
NSError *errorVideo;
[videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:videoAssetTrack atTime:kCMTimeZero error:&errorVideo];
第三步,計算需要裁剪的頭和尾的range,然后將音頻軌道和視頻軌道都進行裁剪
CMTime start = CMTimeMakeWithSeconds(self.startTime, videoAsset.duration.timescale);
CMTime end = CMTimeMakeWithSeconds(self.stopTime - self.startTime, videoAsset.duration.timescale);
CMTime duration = CMTimeMakeWithSeconds(videoAsset.duration.value - self.stopTime, videoAsset.duration.timescale);
CMTimeRange range = CMTimeRangeMake(kCMTimeZero, start);
CMTimeRange range2 = CMTimeRangeMake(end, duration);
[videoTrack removeTimeRange: range];
[audioTrack removeTimeRange: range];
[videoTrack removeTimeRange: range2];
[audioTrack removeTimeRange: range2];
到這里,我們已經(jīng)將工程文件中的視頻進行了裁剪,我們可以直接使用AVPlayer進行播放看裁剪成功沒有
AVPlayerViewController *playerController = [[AVPlayerViewController alloc] init];
AVPlayerItem *playerItem = [[AVPlayerItem alloc] initWithAsset:mutableComposition];
AVPlayer *player = [[AVPlayer alloc] initWithPlayerItem:playerItem];
playerController.player = player;
[self presentViewController:playerController animated:true completion:nil];
當然,更多的是需要使用AVAssetExportSession將工程文件轉(zhuǎn)碼導出,代碼如下:
AVAssetExportSession* exportSession = [[AVAssetExportSession alloc] initWithAsset:mutableComposition presetName:AVAssetExportPresetPassthrough];
NSURL *fileUrl = [NSURL fileURLWithPath:self.tempPath];
exportSession.outputURL = fileUrl;
exportSession.outputFileType = AVFileTypeQuickTimeMovie;
[exportSession exportAsynchronouslyWithCompletionHandler:
^(void ) {
switch ([exportSession status]) {
case AVAssetExportSessionStatusFailed:
NSLog(@"failed: %@", [[exportSession error] localizedDescription]);
break;
case AVAssetExportSessionStatusCancelled:
NSLog(@"canceled");
break;
default:
NSLog(@"success");
break;
}
}
];
至此,整個裁剪功能的主要代碼已經(jīng)完成。
遇到的坑
最初我參考別人的demo發(fā)現(xiàn)可以使用AVAssetExportSession的timeRange來實現(xiàn)裁剪。
CMTimeRange range = CMTimeRangeMake(start, duration);
self.exportSession.timeRange = range;
但是當裁剪的視頻時長比較長時,這種方法在從頭開始裁剪和裁剪視頻后半部分在耗時上差別巨大。
比如我測試了30分鐘的視頻,我裁剪0-10分鐘大概耗時8秒,但是裁剪20-30分鐘耗時達到了驚人的60秒。我不清楚這個是因為底層來轉(zhuǎn)碼時選擇轉(zhuǎn)碼范圍的算法導致了這一現(xiàn)象還是其他什么原因。
最終我選擇了使用上文中提到的切割軌道數(shù)據(jù)的方式基本做到了在30分鐘的視頻中切割10分鐘,無論切割哪一段視頻,都可以保持8秒完成切割和轉(zhuǎn)碼導出。
如有問題,請指正。