本地已轉碼時,直接上傳;本地無轉碼時,先轉碼再上傳
/// 視頻處理通過本地沙盒文件路徑判斷此視頻文件是否已經轉碼 /// @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; }
