原理及步骤:
1、新建一个视频的框架
2、往框架里面添加一条音频轨道和视频轨道
3、把需要拼接的视频片段循环读取出视频和音频部分,并依次插入对应轨道
4、导出视频
代码:
- (void)finalMergeAndExportVideos:(NSArray*)videosPathArray withOutPath:(NSString*)outpath success:(void (^)(void))successBlock{
if (videosPathArray.count == 0) {
return;
}
AVMutableComposition *mixComposition = [[AVMutableComposition alloc] init];
AVMutableCompositionTrack *audioTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeAudio
preferredTrackID:kCMPersistentTrackID_Invalid];
AVMutableCompositionTrack *videoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo
preferredTrackID:kCMPersistentTrackID_Invalid];
CMTime totalDuration = kCMTimeZero;
//用来计算视频大小(不需要可以删掉)
CGFloat length = 0;
for (int i = 0; i < videosPathArray.count; i++) {
AVURLAsset *asset = [AVURLAsset assetWithURL:videosPathArray[i]];
NSError *erroraudio = nil;
AVAssetTrack *assetAudioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] firstObject];
[audioTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
ofTrack:assetAudioTrack
atTime:totalDuration
error:&erroraudio];
NSError *errorVideo = nil;
AVAssetTrack *assetVideoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo]firstObject];
[videoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration)
ofTrack:assetVideoTrack
atTime:totalDuration
error:&errorVideo];
//合成的视频方向设置成第一个视频的方向 (不需要可以直接删掉)
if (i==0 ) {
[videoTrack setPreferredTransform:[MediaManager ssfixedCompositionWithAsset:asset]];
}
totalDuration = CMTimeAdd(totalDuration, asset.duration);
length += [NSData dataWithContentsOfFile:self.marr_movies[i]].length/1000000.0;
NSLog(@"lenght ============= %f", length);
}
NSString * name = AVAssetExportPresetPassthrough;
//视频如果大于5M就压缩(不需要可以删掉)
if (length > 5) {
name = AVAssetExportPresetMediumQuality;
}
NSURL *mergeFileURL = [NSURL fileURLWithPath:outpath];
AVAssetExportSession *exporter = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:name];
exporter.outputURL = mergeFileURL;
exporter.outputFileType = AVFileTypeMPEG4;
exporter.shouldOptimizeForNetworkUse = YES;
[exporter exportAsynchronouslyWithCompletionHandler:^{
if( exporter.status == AVAssetExportSessionStatusCompleted ){
// UISaveVideoAtPathToSavedPhotosAlbum(outpath, nil, nil, nil);
successBlock();
}else if( exporter.status == AVAssetExportSessionStatusFailed )
{
}
}];
}
PS:
1、videosPathArray为本地视频片段url的数组:[NSURL fileURLWithPath:path],注意别写成了URLWithString
2、里面有视频方向设置,以后有时间再单独写出来 (方法如下)
// 获取优化后的视频转向信息
+ (CGAffineTransform)ssfixedCompositionWithAsset:(AVAsset *)videoAsset {
// 视频转向
int degrees = [self degressFromVideoFileWithAsset:videoAsset];
CGAffineTransform translateToCenter;
CGAffineTransform mixedTransform;
NSArray *tracks = [videoAsset tracksWithMediaType:AVMediaTypeVideo];
AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
if (degrees == 90) {
// 顺时针旋转90°
translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.height, 0.0);
mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2);
} else if(degrees == 180){
// 顺时针旋转180°
translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.width, videoTrack.naturalSize.height);
mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI);
} else if(degrees == 270){
// 顺时针旋转270°
translateToCenter = CGAffineTransformMakeTranslation(0.0, videoTrack.naturalSize.width);
mixedTransform = CGAffineTransformRotate(translateToCenter,M_PI_2*3.0);
}else{
translateToCenter = CGAffineTransformMakeTranslation(videoTrack.naturalSize.height, 0.0);
mixedTransform = CGAffineTransformRotate(translateToCenter,0);
}
return mixedTransform;
}
// 获取视频角度
+ (int)degressFromVideoFileWithAsset:(AVAsset *)asset {
int degress = 0;
NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo];
if([tracks count] > 0) {
AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
CGAffineTransform t = videoTrack.preferredTransform;
if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0){
// Portrait
degress = 90;
} else if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0){
// PortraitUpsideDown
degress = 270;
} else if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0){
// LandscapeRight
degress = 0;
} else if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0){
// LandscapeLeft
degress = 180;
}
}
return degress;
}