視頻合成
視頻合成中依然用到了AVMutableComposition,思路依然很簡(jiǎn)單,取出視頻軌和音頻軌數(shù)據(jù)加入到可變軌道之中。不多說(shuō),還是貼代碼吧:
NSArray *videosPathArray =@[@"",
@""];
AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init];
AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
CMTime totalDuration = kCMTimeZero;
for (int i = 0; i < videosPathArray.count; i++) {
NSDictionary* options = @{AVURLAssetPreferPreciseDurationAndTimingKey:@YES
};
//網(wǎng)絡(luò)視頻 URLWithString: 本地視頻 fileURLWithPath:
AVURLAsset *asset =[AVURLAsset URLAssetWithURL:[NSURL URLWithString:videosPathArray[i]] options:options];
NSError *erroraudio = nil;
//獲取AVAsset中的音頻 或者視頻
AVAssetTrack *assetAudioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
//向通道內(nèi)加入音頻或者視頻
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);
}
LYAVPlayerView *playerView =[[LYAVPlayerView alloc]init];
// CGFloat scale =_composition.naturalSize.width/_composition.naturalSize.height;
playerView.frame =CGRectMake(0,100,ScreenWidth,ScreenHeight-160);
playerView.videoGravity =AVLayerVideoGravityResizeAspect;
[playerView setPlayerItem:[[AVPlayerItem alloc]initWithAsset:mixComposition]];
[self.view addSubview:playerView];
[playerView play];
注:創(chuàng)建AVURLAsset時(shí),如果是本地視頻則用fileURLWithPath:函數(shù),如果是網(wǎng)絡(luò)視頻,則調(diào)用URLWithString函數(shù)。多個(gè)網(wǎng)絡(luò)視頻合成后,效果就跟播放一個(gè)視頻一樣,用戶體驗(yàn)非常不錯(cuò)。CMTimeAdd的用法參考另一篇文章短視頻從無(wú)到有 (三)CMTime詳解。
視頻壓縮和轉(zhuǎn)換
視頻合成后即可導(dǎo)出到本地或者手機(jī)相冊(cè),思路依然很簡(jiǎn)單,使用AVAssetExportSession即可。代碼如下:
unlink([outPath UTF8String]);
NSURL *compressionFileURL = [NSURL fileURLWithPath:outPath];
if ([[NSFileManager defaultManager] fileExistsAtPath:outPath]) {
[[NSFileManager defaultManager] removeItemAtPath:outPath error:nil];
}
// 創(chuàng)建AVAsset對(duì)象
AVAsset* asset = [AVAsset assetWithURL:[NSURL fileURLWithPath:videoPath]];
// 創(chuàng)建AVAssetExportSession對(duì)象
AVAssetExportSession * exporter = [[AVAssetExportSession alloc] initWithAsset:asset presetName:presetName];
//優(yōu)化網(wǎng)絡(luò)
exporter.shouldOptimizeForNetworkUse = YES;
//設(shè)置輸出路徑
exporter.outputURL =compressionFileURL;
//設(shè)置輸出類型
exporter.outputFileType = outputFileType;
[exporter exportAsynchronouslyWithCompletionHandler:^{
dispatch_async(dispatch_get_main_queue(), ^{
switch (exporter.status) {
case AVAssetExportSessionStatusFailed:{
if (completeBlock) {
completeBlock(exporter.error,compressionFileURL);
}
break;
}
case AVAssetExportSessionStatusCancelled:{
NSLog(@"Export Status: Cancell");
break;
}
case AVAssetExportSessionStatusCompleted: {
if (completeBlock) {
completeBlock(nil,compressionFileURL);
}
break;
}
case AVAssetExportSessionStatusUnknown: {
NSLog(@"Export Status: Unknown");
break;
}
case AVAssetExportSessionStatusExporting : {
NSLog(@"Export Status: Exporting");
break;
}
case AVAssetExportSessionStatusWaiting: {
NSLog(@"Export Status: Wating");
break;
}
};
});
}];
注:導(dǎo)出過(guò)程中設(shè)置exporter的presetName(分辨率)和outputFileType(視頻封裝格式)即可完成視頻轉(zhuǎn)換和壓縮。當(dāng)然,更深層次的處理靠這些是遠(yuǎn)遠(yuǎn)不夠的,還要依靠更多的框架來(lái)處理,譬如ffmpeg等,這些以后再做討論,今兒就先到這。