// // UIViewController+YOUPAIVideo.m // MSYOUPAI // // Created by admin on 2022/3/5. // Copyright © 2022 MS. All rights reserved. // #import "UIViewController+YOUPAIVideo.h" #import "NSDate+formatter.h" typedef void(^MovEncodeToMpegToolResultBlock)(NSURL *mp4FileUrl, NSData *mp4Data, NSError *error); typedef enum : NSUInteger { ExportPresetLowQuality, //低质量 可以通过移动网络分享 ExportPresetMediumQuality, //中等质量 可以通过WIFI网络分享 ExportPresetHighestQuality, //高等质量 ExportPreset640x480, ExportPreset960x540, ExportPreset1280x720, //720pHD ExportPreset1920x1080, //1080pHD ExportPreset3840x2160, } ExportPresetQuality; @implementation UIViewController (YOUPAIVideo) /** 选择视频 */ -(void)youpaifTZSelectVideo:(YOUPAIVideoWillSelectBlock)willSelect didSelect:(YOUPAIVideoDidSelectBlock)didSelect{ TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:9 columnNumber:4 delegate:nil]; if (willSelect){ willSelect(imagePickerVc); } else{ [LCTZImageConfigHelper setDefaultTZImageConfig:imagePickerVc]; imagePickerVc.allowTakePicture = YES; // 在内部显示拍照按钮 imagePickerVc.allowPickingVideo = YES;//是否允许选择视频 imagePickerVc.allowPickingImage = NO;//是否允许选择照片 imagePickerVc.allowPickingOriginalPhoto = NO;//是否选择原图 imagePickerVc.showSelectedIndex = YES; //显示图片序号 imagePickerVc.sortAscendingByModificationDate = YES;//按时间倒序排列图片 } [imagePickerVc setDidFinishPickingVideoHandle:^(UIImage *coverImage, PHAsset *asset) { PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init]; options.version = PHVideoRequestOptionsVersionOriginal; options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic; options.networkAccessAllowed = YES; [ZCHUDHelper showWithStatus:@"读取中..."]; [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:nil resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) { if (asset == nil) { NSError *error = [NSError errorWithDomain:@"无效视频" code:-1 userInfo:nil]; if (didSelect){ didSelect(nil,nil,error); } [ZCHUDHelper dismiss]; return; }else{ AVURLAsset *urlAsser = ( AVURLAsset *)asset; [UIViewController convertMovToMp4FromAVURLAsset:urlAsser andAVAssetExportPresetQuality:ExportPresetHighestQuality andMovEncodeToMpegToolResultBlock:^(NSURL *mp4FileUrl, NSData *mp4Data, NSError *error) { [ZCHUDHelper dismiss]; if (error == nil){ if (didSelect){ AVURLAsset *mp4Asser = [AVURLAsset assetWithURL:mp4FileUrl]; didSelect(coverImage,mp4Asser,nil); } }else{ if (didSelect){ didSelect(nil,nil,error); } } }]; } }]; }]; [self presentViewController:imagePickerVc animated:YES completion:nil]; } /** avplayer 播放视频 */ -(void)youpaifAVPlayer:(NSURL*)path { __block NSString *playurl = [path absoluteString]; if ([NSThread isMainThread]) { if([playurl hasPrefix:@"://"]) { playurl = [NSString stringWithFormat:@"file://%@",path]; } NSURL * url = [NSURL URLWithString:playurl]; AVPlayerViewController *moviePlayer=[[AVPlayerViewController alloc]init]; moviePlayer.player = [AVPlayer playerWithURL:url]; [moviePlayer.player play]; [self presentViewController:moviePlayer animated:YES completion:^{ }]; } else { dispatch_sync(dispatch_get_main_queue(), ^{ if([playurl hasPrefix:@"://"]) { playurl = [NSString stringWithFormat:@"file://%@",path]; } NSURL * url = [NSURL URLWithString:playurl]; AVPlayerViewController *moviePlayer=[[AVPlayerViewController alloc]init]; moviePlayer.player = [AVPlayer playerWithURL:url]; [moviePlayer.player play]; [self presentViewController:moviePlayer animated:YES completion:^{ }]; }); } } /** 裁剪视频 */ -(void)youpaifCutVideo:(AVURLAsset *)asset start:(CGFloat)startTime end:(CGFloat)endTime completion:(YOUPAIVideoDidCutBlock)completion{ NSString *docDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0]; NSString *outputFilePath = [NSString stringWithFormat:@"%@/MediaTool%@.mp4", docDirPath , [UIViewController getCurrentTime]]; NSURL *outputFileUrl = [NSURL fileURLWithPath:outputFilePath]; NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:asset]; if ([compatiblePresets containsObject:AVAssetExportPresetMediumQuality]) { AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetPassthrough]; NSURL *outputURL = outputFileUrl; exportSession.outputURL = outputURL; exportSession.outputFileType = AVFileTypeMPEG4; exportSession.shouldOptimizeForNetworkUse = YES; CMTime start = CMTimeMakeWithSeconds(startTime, asset.duration.timescale); CMTime duration = CMTimeMakeWithSeconds(endTime,asset.duration.timescale); CMTimeRange range = CMTimeRangeMake(start, duration); exportSession.timeRange = range; [exportSession exportAsynchronouslyWithCompletionHandler:^{ switch ([exportSession status]) { case AVAssetExportSessionStatusFailed: { completion(nil, 0, [exportSession error]); } break; case AVAssetExportSessionStatusCancelled: { NSError *error = [NSError errorWithDomain:@"已取消" code:-1 userInfo:nil]; completion(nil, 0, error); } break; case AVAssetExportSessionStatusCompleted: { completion(outputURL, endTime, nil); } break; default: { NSError *error = [NSError errorWithDomain:@"裁剪失败" code:-1 userInfo:nil]; completion(outputURL, 0, error); } break; } }]; } } /** 获取上传信息 */ -(void)youpaifVideoSts:(NSString*)type sts:(YOUPAIVideoDidStsBlock)completion{ [LCHttpHelper requestWithURLString:STS parameters:@{@"type":type} needToken:YES type:HttpRequestTypePost success:^(id responseObject) { NSDictionary* dict = (NSDictionary*)responseObject; NSInteger code = [[dict objectForKey:@"code"] integerValue]; if (code==0) { YOUPAILZStsModel *model = [YOUPAILZStsModel mj_objectWithKeyValues:[dict objectForKey:@"data"]]; if (completion) { completion(model,nil); } } } failure:^(NSError *error) { if (completion) { completion(nil,error); } }]; } //获取当地时间 + (NSString *)getCurrentTime { NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"HH:mm:ss"]; NSString *dateTime = [formatter stringFromDate:[NSDate date]]; return dateTime; } #pragma mark ### MOV转码MP4 + (void)convertMovToMp4FromAVURLAsset:(AVURLAsset*)urlAsset andAVAssetExportPresetQuality:(ExportPresetQuality)exportQuality andMovEncodeToMpegToolResultBlock:(MovEncodeToMpegToolResultBlock)movEncodeToMpegToolResultBlock { AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:urlAsset.URL options:nil]; NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset]; // 查询是否有匹配的预设 if ([compatiblePresets containsObject:[self getAVAssetExportPresetQuality:exportQuality]]) { // 在Documents目录下创建一个名为FileData的文件夹 NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"Cache/VideoData"]; 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); } NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]]; [formatter setDateFormat:@"yyyyMMddHHmmss"]; //每次启动后都保存一个新的文件中 NSString *dateStr = [formatter stringFromDate:[NSDate date]]; NSString *resultPath = [path stringByAppendingFormat:@"/%@.mp4",dateStr]; NSLog(@"resultFileName = %@",dateStr); AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset presetName:[self getAVAssetExportPresetQuality:exportQuality]]; exportSession.outputURL = [NSURL fileURLWithPath:resultPath]; exportSession.outputFileType = AVFileTypeMPEG4; exportSession.shouldOptimizeForNetworkUse = YES; [exportSession exportAsynchronouslyWithCompletionHandler:^(void) { switch (exportSession.status) { case AVAssetExportSessionStatusUnknown: { NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain" code:10001 userInfo:@{NSLocalizedDescriptionKey:@"AVAssetExportSessionStatusUnknown"}]; movEncodeToMpegToolResultBlock(nil, nil , error); } break; case AVAssetExportSessionStatusWaiting: { NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain" code:10002 userInfo:@{NSLocalizedDescriptionKey:@"AVAssetExportSessionStatusWaiting"}]; movEncodeToMpegToolResultBlock(nil, nil , error); } break; case AVAssetExportSessionStatusExporting: { NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain" code:10003 userInfo:@{NSLocalizedDescriptionKey:@"AVAssetExportSessionStatusExporting"}]; movEncodeToMpegToolResultBlock(nil, nil , error); } break; case AVAssetExportSessionStatusCompleted: { NSData *mp4Data = [NSData dataWithContentsOfURL:exportSession.outputURL]; movEncodeToMpegToolResultBlock(exportSession.outputURL, mp4Data , nil); } break; case AVAssetExportSessionStatusFailed: { NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain" code:10005 userInfo:@{NSLocalizedDescriptionKey:@"AVAssetExportSessionStatusFailed"}]; movEncodeToMpegToolResultBlock(nil, nil , error); } break; case AVAssetExportSessionStatusCancelled: { NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain" code:10006 userInfo:@{NSLocalizedDescriptionKey:@"AVAssetExportSessionStatusCancelled"}]; movEncodeToMpegToolResultBlock(nil, nil , error); } break; } }]; } else{ NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain" code:10007 userInfo:@{NSLocalizedDescriptionKey:@"AVAssetExportSessionStatusNoPreset"}]; movEncodeToMpegToolResultBlock(nil, nil , error); } } /* AVAssetExportPresetLowQuality 低质量 可以通过移动网络分享 AVAssetExportPresetMediumQuality 中等质量 可以通过WIFI网络分享 AVAssetExportPresetHighestQuality 高等质量 AVAssetExportPreset640x480 AVAssetExportPreset960x540 AVAssetExportPreset1280x720 720pHD AVAssetExportPreset1920x1080 1080pHD AVAssetExportPreset3840x2160 */ + (NSString *const )getAVAssetExportPresetQuality:(ExportPresetQuality)exportPreset { switch (exportPreset) { case ExportPresetLowQuality: return AVAssetExportPresetLowQuality; case ExportPresetMediumQuality: return AVAssetExportPresetMediumQuality; case ExportPresetHighestQuality: return AVAssetExportPresetHighestQuality; case ExportPreset640x480: return AVAssetExportPreset640x480; case ExportPreset960x540: return AVAssetExportPreset960x540; case ExportPreset1280x720: return AVAssetExportPreset1280x720; case ExportPreset1920x1080: return AVAssetExportPreset1920x1080; case ExportPreset3840x2160: return AVAssetExportPreset3840x2160; } } @end