本地已转码时,直接上传;本地无转码时,先转码再上传
/// 视频处理通过本地沙盒文件路径判断此视频文件是否已经转码 /// @param asset 视频资源 /// @param callBack 回调 - (void)dealWithVidioAsset:(PHAsset *)asset callBack:(void (^)(NSString *))callBack { /// 转码操作 PHImageManager *manager = [PHImageManager defaultManager]; [manager requestAVAssetForVideo:asset options:nil resultHandler:^(AVAsset * _Nullable aVsset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) { ///判断此视频是否已经转码 NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingFormat:@"/VideoCacheData/%@.mp4",[((AVURLAsset*)aVsset).URL.path MD5String]]; NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL isDirExist = [fileManager fileExistsAtPath:path]; if (isDirExist) { // 已经转码成功 直接上传 [self uploadMpa4WithUrl:[NSURL URLWithString:path] callBack:^(NSString *str) { if (callBack) { callBack(str); } }]; return; } [self convertMovToMp4FromAVURLAsset:(AVURLAsset*)aVsset andCompeleteHandler:^(NSURL *fileUrl) { if (!fileUrl) { [HUDManager hideHUDView]; [self.view showMessageWithText:@"上传错误请重试!"]; self.navigationItem.rightBarButtonItem.enabled = YES; }else { [self uploadMpa4WithUrl:fileUrl callBack:^(NSString *str) { if (callBack) { callBack(str); } }]; } }]; }]; }
转码MP4
- (void)convertMovToMp4FromAVURLAsset:(AVURLAsset*)urlAsset andCompeleteHandler:(void(^)(NSURL *fileUrl))fileUrlHandler{ AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:urlAsset.URL options:nil]; NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset]; if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) { // 在Documents目录下创建一个名为FileData的文件夹 NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"VideoCacheData"]; NSFileManager *fileManager = [NSFileManager defaultManager]; BOOL isDir = FALSE; BOOL isDirExist = [fileManager fileExistsAtPath:path isDirectory:&isDir]; if(!(isDirExist && isDir)) { BOOL bCreateDir = [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil]; if(!bCreateDir){ NSLog(@"创建文件夹失败!%@",path); } NSLog(@"创建文件夹成功,文件路径%@",path); } /// 文件名为本地相册路径的Md5 NSString *resultPath = [path stringByAppendingFormat:@"/%@.mp4",[urlAsset.URL.path MD5String]]; NSLog(@"file path:%@",resultPath); NSLog(@"resultPath = %@",resultPath); AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:AVAssetExportPresetMediumQuality]; exportSession.outputURL = [NSURL fileURLWithPath:resultPath]; exportSession.outputFileType = AVFileTypeMPEG4; exportSession.shouldOptimizeForNetworkUse = YES; exportSession.videoComposition = [self getVideoComposition:avAsset]; //修正某些播放器不识别视频Rotation的问题 [exportSession exportAsynchronouslyWithCompletionHandler:^(void) { dispatch_async(dispatch_get_main_queue(), ^{ switch (exportSession.status) { case AVAssetExportSessionStatusUnknown: NSLog(@"AVAssetExportSessionStatusUnknown"); fileUrlHandler(nil); break; case AVAssetExportSessionStatusWaiting: NSLog(@"AVAssetExportSessionStatusWaiting"); fileUrlHandler(nil); break; case AVAssetExportSessionStatusExporting: NSLog(@"AVAssetExportSessionStatusExporting"); fileUrlHandler(nil); break; case AVAssetExportSessionStatusCompleted: NSLog(@"AVAssetExportSessionStatusCompleted"); fileUrlHandler(exportSession.outputURL); break; case AVAssetExportSessionStatusFailed: NSLog(@"AVAssetExportSessionStatusFailed"); fileUrlHandler(nil); break; case AVAssetExportSessionStatusCancelled: NSLog(@"AVAssetExportSessionStatusCancelled"); fileUrlHandler(nil); break; } }); }]; } }
修正视频位置
/// 修正位置信息 - (AVMutableVideoComposition *)getVideoComposition:(AVAsset *)asset { AVAssetTrack *videoTrack = [[asset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; AVMutableComposition *composition = [AVMutableComposition composition]; AVMutableVideoComposition *videoComposition = [AVMutableVideoComposition videoComposition]; CGSize videoSize = videoTrack.naturalSize; BOOL isPortrait_ = [self isVideoPortrait:asset]; if(isPortrait_) { videoSize = CGSizeMake(videoSize.height, videoSize.width); } composition.naturalSize = videoSize; videoComposition.renderSize = videoSize; videoComposition.frameDuration = CMTimeMakeWithSeconds( 1 / videoTrack.nominalFrameRate, 600); AVMutableCompositionTrack *compositionVideoTrack; compositionVideoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, asset.duration) ofTrack:videoTrack atTime:kCMTimeZero error:nil]; AVMutableVideoCompositionLayerInstruction *layerInst; layerInst = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack]; [layerInst setTransform:videoTrack.preferredTransform atTime:kCMTimeZero]; AVMutableVideoCompositionInstruction *inst = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; inst.timeRange = CMTimeRangeMake(kCMTimeZero, asset.duration); inst.layerInstructions = [NSArray arrayWithObject:layerInst]; videoComposition.instructions = [NSArray arrayWithObject:inst]; return videoComposition; } - (BOOL) isVideoPortrait:(AVAsset *)asset { BOOL isPortrait = NO; NSArray *tracks = [asset tracksWithMediaType:AVMediaTypeVideo]; if([tracks count] > 0) { AVAssetTrack *videoTrack = [tracks objectAtIndex:0]; CGAffineTransform t = videoTrack.preferredTransform; // Portrait if(t.a == 0 && t.b == 1.0 && t.c == -1.0 && t.d == 0) { isPortrait = YES; } // PortraitUpsideDown if(t.a == 0 && t.b == -1.0 && t.c == 1.0 && t.d == 0) { isPortrait = YES; } // LandscapeRight if(t.a == 1.0 && t.b == 0 && t.c == 0 && t.d == 1.0) { isPortrait = NO; } // LandscapeLeft if(t.a == -1.0 && t.b == 0 && t.c == 0 && t.d == -1.0) { isPortrait = NO; } } return isPortrait; }