UIViewController+YOUPAIVideo.m 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. //
  2. // UIViewController+YOUPAIVideo.m
  3. // MSYOUPAI
  4. //
  5. // Created by admin on 2022/3/5.
  6. // Copyright © 2022 MS. All rights reserved.
  7. //
  8. #import "UIViewController+YOUPAIVideo.h"
  9. #import "NSDate+formatter.h"
  10. typedef void(^MovEncodeToMpegToolResultBlock)(NSURL *mp4FileUrl, NSData *mp4Data, NSError *error);
  11. typedef enum : NSUInteger {
  12. ExportPresetLowQuality, //低质量 可以通过移动网络分享
  13. ExportPresetMediumQuality, //中等质量 可以通过WIFI网络分享
  14. ExportPresetHighestQuality, //高等质量
  15. ExportPreset640x480,
  16. ExportPreset960x540,
  17. ExportPreset1280x720, //720pHD
  18. ExportPreset1920x1080, //1080pHD
  19. ExportPreset3840x2160,
  20. } ExportPresetQuality;
  21. @implementation UIViewController (YOUPAIVideo)
  22. /**
  23. 选择视频
  24. */
  25. -(void)youpaifTZSelectVideo:(YOUPAIVideoWillSelectBlock)willSelect
  26. didSelect:(YOUPAIVideoDidSelectBlock)didSelect{
  27. TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:9 columnNumber:4 delegate:nil];
  28. if (willSelect){
  29. willSelect(imagePickerVc);
  30. }
  31. else{
  32. [LCTZImageConfigHelper setDefaultTZImageConfig:imagePickerVc];
  33. imagePickerVc.allowTakePicture = YES; // 在内部显示拍照按钮
  34. imagePickerVc.allowPickingVideo = YES;//是否允许选择视频
  35. imagePickerVc.allowPickingImage = NO;//是否允许选择照片
  36. imagePickerVc.allowPickingOriginalPhoto = NO;//是否选择原图
  37. imagePickerVc.showSelectedIndex = YES; //显示图片序号
  38. imagePickerVc.sortAscendingByModificationDate = YES;//按时间倒序排列图片
  39. }
  40. [imagePickerVc setDidFinishPickingVideoHandle:^(UIImage *coverImage, PHAsset *asset) {
  41. PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
  42. options.version = PHVideoRequestOptionsVersionOriginal;
  43. options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
  44. options.networkAccessAllowed = YES;
  45. [ZCHUDHelper showWithStatus:@"读取中..."];
  46. [[PHImageManager defaultManager] requestAVAssetForVideo:asset options:nil resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
  47. if (asset == nil) {
  48. NSError *error = [NSError errorWithDomain:@"无效视频" code:-1 userInfo:nil];
  49. if (didSelect){
  50. didSelect(nil,nil,error);
  51. }
  52. [ZCHUDHelper dismiss];
  53. return;
  54. }else{
  55. AVURLAsset *urlAsser = ( AVURLAsset *)asset;
  56. [UIViewController convertMovToMp4FromAVURLAsset:urlAsser
  57. andAVAssetExportPresetQuality:ExportPresetHighestQuality andMovEncodeToMpegToolResultBlock:^(NSURL *mp4FileUrl, NSData *mp4Data, NSError *error) {
  58. [ZCHUDHelper dismiss];
  59. if (error == nil){
  60. if (didSelect){
  61. AVURLAsset *mp4Asser = [AVURLAsset assetWithURL:mp4FileUrl];
  62. didSelect(coverImage,mp4Asser,nil);
  63. }
  64. }else{
  65. if (didSelect){
  66. didSelect(nil,nil,error);
  67. }
  68. }
  69. }];
  70. }
  71. }];
  72. }];
  73. [self presentViewController:imagePickerVc animated:YES completion:nil];
  74. }
  75. /**
  76. avplayer 播放视频
  77. */
  78. -(void)youpaifAVPlayer:(NSURL*)path {
  79. __block NSString *playurl = [path absoluteString];
  80. if ([NSThread isMainThread]) {
  81. if([playurl hasPrefix:@"://"])
  82. {
  83. playurl = [NSString stringWithFormat:@"file://%@",path];
  84. }
  85. NSURL * url = [NSURL URLWithString:playurl];
  86. AVPlayerViewController *moviePlayer=[[AVPlayerViewController alloc]init];
  87. moviePlayer.player = [AVPlayer playerWithURL:url];
  88. [moviePlayer.player play];
  89. [self presentViewController:moviePlayer animated:YES completion:^{
  90. }];
  91. } else {
  92. dispatch_sync(dispatch_get_main_queue(), ^{
  93. if([playurl hasPrefix:@"://"])
  94. {
  95. playurl = [NSString stringWithFormat:@"file://%@",path];
  96. }
  97. NSURL * url = [NSURL URLWithString:playurl];
  98. AVPlayerViewController *moviePlayer=[[AVPlayerViewController alloc]init];
  99. moviePlayer.player = [AVPlayer playerWithURL:url];
  100. [moviePlayer.player play];
  101. [self presentViewController:moviePlayer animated:YES completion:^{
  102. }];
  103. });
  104. }
  105. }
  106. /**
  107. 裁剪视频
  108. */
  109. -(void)youpaifCutVideo:(AVURLAsset *)asset
  110. start:(CGFloat)startTime
  111. end:(CGFloat)endTime
  112. completion:(YOUPAIVideoDidCutBlock)completion{
  113. NSString *docDirPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES) objectAtIndex:0];
  114. NSString *outputFilePath = [NSString stringWithFormat:@"%@/MediaTool%@.mp4", docDirPath , [UIViewController getCurrentTime]];
  115. NSURL *outputFileUrl = [NSURL fileURLWithPath:outputFilePath];
  116. NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:asset];
  117. if ([compatiblePresets containsObject:AVAssetExportPresetMediumQuality])
  118. {
  119. AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]
  120. initWithAsset:asset presetName:AVAssetExportPresetPassthrough];
  121. NSURL *outputURL = outputFileUrl;
  122. exportSession.outputURL = outputURL;
  123. exportSession.outputFileType = AVFileTypeMPEG4;
  124. exportSession.shouldOptimizeForNetworkUse = YES;
  125. CMTime start = CMTimeMakeWithSeconds(startTime, asset.duration.timescale);
  126. CMTime duration = CMTimeMakeWithSeconds(endTime,asset.duration.timescale);
  127. CMTimeRange range = CMTimeRangeMake(start, duration);
  128. exportSession.timeRange = range;
  129. [exportSession exportAsynchronouslyWithCompletionHandler:^{
  130. switch ([exportSession status]) {
  131. case AVAssetExportSessionStatusFailed:
  132. {
  133. completion(nil, 0, [exportSession error]);
  134. }
  135. break;
  136. case AVAssetExportSessionStatusCancelled:
  137. {
  138. NSError *error = [NSError errorWithDomain:@"已取消" code:-1 userInfo:nil];
  139. completion(nil, 0, error);
  140. }
  141. break;
  142. case AVAssetExportSessionStatusCompleted:
  143. {
  144. completion(outputURL, endTime, nil);
  145. }
  146. break;
  147. default:
  148. {
  149. NSError *error = [NSError errorWithDomain:@"裁剪失败" code:-1 userInfo:nil];
  150. completion(outputURL, 0, error);
  151. } break;
  152. }
  153. }];
  154. }
  155. }
  156. /**
  157. 获取上传信息
  158. */
  159. -(void)youpaifVideoSts:(NSString*)type
  160. sts:(YOUPAIVideoDidStsBlock)completion{
  161. [LCHttpHelper requestWithURLString:STS parameters:@{@"type":type} needToken:YES type:HttpRequestTypePost success:^(id responseObject) {
  162. NSDictionary* dict = (NSDictionary*)responseObject;
  163. NSInteger code = [[dict objectForKey:@"code"] integerValue];
  164. if (code==0) {
  165. YOUPAILZStsModel *model = [YOUPAILZStsModel mj_objectWithKeyValues:[dict objectForKey:@"data"]];
  166. if (completion) {
  167. completion(model,nil);
  168. }
  169. }
  170. } failure:^(NSError *error) {
  171. if (completion) {
  172. completion(nil,error);
  173. }
  174. }];
  175. }
  176. //获取当地时间
  177. + (NSString *)getCurrentTime {
  178. NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
  179. [formatter setDateFormat:@"HH:mm:ss"];
  180. NSString *dateTime = [formatter stringFromDate:[NSDate date]];
  181. return dateTime;
  182. }
  183. #pragma mark ### MOV转码MP4
  184. + (void)convertMovToMp4FromAVURLAsset:(AVURLAsset*)urlAsset
  185. andAVAssetExportPresetQuality:(ExportPresetQuality)exportQuality
  186. andMovEncodeToMpegToolResultBlock:(MovEncodeToMpegToolResultBlock)movEncodeToMpegToolResultBlock {
  187. AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:urlAsset.URL options:nil];
  188. NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
  189. // 查询是否有匹配的预设
  190. if ([compatiblePresets containsObject:[self getAVAssetExportPresetQuality:exportQuality]]) {
  191. // 在Documents目录下创建一个名为FileData的文件夹
  192. NSString *path = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)lastObject] stringByAppendingPathComponent:@"Cache/VideoData"];
  193. NSFileManager *fileManager = [NSFileManager defaultManager];
  194. BOOL isDir = FALSE;
  195. BOOL isDirExist = [fileManager fileExistsAtPath:path isDirectory:&isDir];
  196. if(!(isDirExist && isDir)) {
  197. BOOL bCreateDir = [fileManager createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
  198. if(!bCreateDir){
  199. NSLog(@"创建文件夹失败!%@",path);
  200. }
  201. NSLog(@"创建文件夹成功,文件路径%@",path);
  202. }
  203. NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
  204. [formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];
  205. [formatter setDateFormat:@"yyyyMMddHHmmss"]; //每次启动后都保存一个新的文件中
  206. NSString *dateStr = [formatter stringFromDate:[NSDate date]];
  207. NSString *resultPath = [path stringByAppendingFormat:@"/%@.mp4",dateStr];
  208. NSLog(@"resultFileName = %@",dateStr);
  209. AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:avAsset
  210. presetName:[self getAVAssetExportPresetQuality:exportQuality]];
  211. exportSession.outputURL = [NSURL fileURLWithPath:resultPath];
  212. exportSession.outputFileType = AVFileTypeMPEG4;
  213. exportSession.shouldOptimizeForNetworkUse = YES;
  214. [exportSession exportAsynchronouslyWithCompletionHandler:^(void) {
  215. switch (exportSession.status) {
  216. case AVAssetExportSessionStatusUnknown: {
  217. NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain"
  218. code:10001
  219. userInfo:@{NSLocalizedDescriptionKey:@"AVAssetExportSessionStatusUnknown"}];
  220. movEncodeToMpegToolResultBlock(nil, nil , error);
  221. }
  222. break;
  223. case AVAssetExportSessionStatusWaiting: {
  224. NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain"
  225. code:10002
  226. userInfo:@{NSLocalizedDescriptionKey:@"AVAssetExportSessionStatusWaiting"}];
  227. movEncodeToMpegToolResultBlock(nil, nil , error);
  228. }
  229. break;
  230. case AVAssetExportSessionStatusExporting: {
  231. NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain"
  232. code:10003
  233. userInfo:@{NSLocalizedDescriptionKey:@"AVAssetExportSessionStatusExporting"}];
  234. movEncodeToMpegToolResultBlock(nil, nil , error);
  235. }
  236. break;
  237. case AVAssetExportSessionStatusCompleted: {
  238. NSData *mp4Data = [NSData dataWithContentsOfURL:exportSession.outputURL];
  239. movEncodeToMpegToolResultBlock(exportSession.outputURL, mp4Data , nil);
  240. }
  241. break;
  242. case AVAssetExportSessionStatusFailed: {
  243. NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain"
  244. code:10005
  245. userInfo:@{NSLocalizedDescriptionKey:@"AVAssetExportSessionStatusFailed"}];
  246. movEncodeToMpegToolResultBlock(nil, nil , error);
  247. }
  248. break;
  249. case AVAssetExportSessionStatusCancelled: {
  250. NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain"
  251. code:10006
  252. userInfo:@{NSLocalizedDescriptionKey:@"AVAssetExportSessionStatusCancelled"}];
  253. movEncodeToMpegToolResultBlock(nil, nil , error);
  254. }
  255. break;
  256. }
  257. }];
  258. }
  259. else{
  260. NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain"
  261. code:10007
  262. userInfo:@{NSLocalizedDescriptionKey:@"AVAssetExportSessionStatusNoPreset"}];
  263. movEncodeToMpegToolResultBlock(nil, nil , error);
  264. }
  265. }
  266. /*
  267. AVAssetExportPresetLowQuality 低质量 可以通过移动网络分享
  268. AVAssetExportPresetMediumQuality 中等质量 可以通过WIFI网络分享
  269. AVAssetExportPresetHighestQuality 高等质量
  270. AVAssetExportPreset640x480
  271. AVAssetExportPreset960x540
  272. AVAssetExportPreset1280x720 720pHD
  273. AVAssetExportPreset1920x1080 1080pHD
  274. AVAssetExportPreset3840x2160
  275. */
  276. + (NSString *const )getAVAssetExportPresetQuality:(ExportPresetQuality)exportPreset {
  277. switch (exportPreset) {
  278. case ExportPresetLowQuality:
  279. return AVAssetExportPresetLowQuality;
  280. case ExportPresetMediumQuality:
  281. return AVAssetExportPresetMediumQuality;
  282. case ExportPresetHighestQuality:
  283. return AVAssetExportPresetHighestQuality;
  284. case ExportPreset640x480:
  285. return AVAssetExportPreset640x480;
  286. case ExportPreset960x540:
  287. return AVAssetExportPreset960x540;
  288. case ExportPreset1280x720:
  289. return AVAssetExportPreset1280x720;
  290. case ExportPreset1920x1080:
  291. return AVAssetExportPreset1920x1080;
  292. case ExportPreset3840x2160:
  293. return AVAssetExportPreset3840x2160;
  294. }
  295. }
  296. @end