iOS--相册视频MOV转MP4

相册视频MOV转MP4

最新做的一个功能涉及到了视频的录制、压缩及上传。

根据网上诸多大神的经验,终于算是调通了,但也发现了一些问题,所以把我的经验分享一下。

首先,肯定是调用一下系统的相机或相册,

那么导入哪几个库 就不用我说了吧

打开相册获取视频PHAsset

TZImagePickerController 这个库是个好东西

1
2
3
4
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:20 delegate:self];
imagePickerVc.allowPickingVideo = YES;
imagePickerVc.allowPickingImage = NO;
[self presentViewController:imagePickerVc animated:YES completion:nil];

从相册获取到视频PHAsset之后

这时的 (PHAsset*)asset其实就是 AVURLAsset类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
-(void)imagePickerController:(TZImagePickerController *)picker
didFinishPickingVideo:(UIImage *)coverImage
sourceAssets:(PHAsset *)asset{
/// 包含该视频的基础信息
PHAssetResource * resource = [[PHAssetResource assetResourcesForAsset: asset] firstObject];
NSString *string1 = [resource.description stringByReplacingOccurrencesOfString:@"{" withString:@""];
NSString *string2 = [string1 stringByReplacingOccurrencesOfString:@"}" withString:@""];
NSString *string3 = [string2 stringByReplacingOccurrencesOfString:@", " withString:@","];
NSMutableArray *resourceArray = [NSMutableArray arrayWithArray:[string3 componentsSeparatedByString:@" "]];
[resourceArray removeObjectAtIndex:0];
[resourceArray removeObjectAtIndex:0];
for (NSInteger index = 0; index<resourceArray.count; index++) {
NSString *string = resourceArray[index];
NSString *ret = [string stringByReplacingOccurrencesOfString:@" " withString:@""];
resourceArray[index] = ret;
}
NSMutableDictionary *videoInfo = [[NSMutableDictionary alloc] init];
for (NSString *string in resourceArray) {
NSArray *array = [string componentsSeparatedByString:@"="];
videoInfo[array[0]] = array[1];
}
NSLog(@"%@",videoInfo);
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHImageRequestOptionsVersionCurrent;
options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
WS(weakSelf);
PHImageManager *manager = [PHImageManager defaultManager];
[manager requestAVAssetForVideo:asset
options:options
resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
StrongSelf(strongSelf);
NSString *sizeString = videoInfo[@"size"];
NSArray *array = [sizeString componentsSeparatedByString:@","];
CGSize size = CGSizeMake([array[0] floatValue], [array[1] floatValue]);
[strongSelf choseVedioCompeletWithVedioAsset:(AVURLAsset *)asset
andAVAudioMix:audioMix
andVedioInfo:info
andImageSize:size];
}];
}

为了避免循环引用,这里另写方法调用转码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (void)choseVedioCompeletWithVedioAsset:(AVURLAsset *)urlAsset
andAVAudioMix:(AVAudioMix *)audioMix
andVedioInfo:(NSDictionary *)vedioInfo
andImageSize:(CGSize)size{
//[self showLoadingView:@"处理视频数据"];
WS(weakSelf);
[RecordTools convertMovToMp4FromAVURLAsset:urlAsset
andCompeleteHandler:^(NSURL * _Nonnull fileUrl) {
StrongSelf(strongSelf);
[strongSelf hideLoadingView];
[strongSelf addVideoToTableCompeletWithVedioAsset:urlAsset
andAVAudioMix:audioMix
andVedioInfo:vedioInfo
andImageSize:size
andMP4FileUrl:fileUrl];
}];
}

检查视频文件的合规性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//30*1024*1024 B 字节
#define VideoSizeMax 31457280
- (void)addVideoToTableCompeletWithVedioAsset:(AVURLAsset *)urlAsset
andAVAudioMix:(AVAudioMix *)audioMix
andVedioInfo:(NSDictionary *)vedioInfo
andImageSize:(CGSize)size
andMP4FileUrl:(NSURL *)MP4FileUrl{
if (MP4FileUrl == nil || MP4FileUrl.path == nil) {
showMsg(@"视频获取失败");
return;
}
AVAssetImageGenerator *generator = [AVAssetImageGenerator assetImageGeneratorWithAsset:urlAsset];
generator.appliesPreferredTrackTransform = YES;
generator.maximumSize = CGSizeMake(size.width, size.height);
NSError *error = nil;
CGImageRef img = [generator copyCGImageAtTime:CMTimeMake(0, 10) actualTime:NULL error:&error];
UIImage *image = [UIImage imageWithCGImage:img];
NSError *mp4Rrror = nil;
// 检查文件属性 查看文件大小 是否超标
NSDictionary *infoDict = [[NSFileManager defaultManager]attributesOfItemAtPath:MP4FileUrl.path error:&mp4Rrror];
NSString *fileSizeString = infoDict[@"NSFileSize"];
if (fileSizeString && !error) {
NSInteger fileSize = fileSizeString.integerValue;
if (fileSize>VideoSizeMax) {
showMsg(@"视频最大不能超过30M");
[[NSFileManager defaultManager] removeItemAtPath:MP4FileUrl.path error:&error];
return;
}
}
else{
showMsg(@"视频获取失败");
[[NSFileManager defaultManager] removeItemAtPath:MP4FileUrl.path error:&error];
return;
}
// 你们不需要
// if (self.lastSelectPath) {
// [self.dataArray replaceObjectAtIndex:self.lastSelectPath.row
// withObject:@{@"type":@"vedio",
// @"value":MP4FileUrl,
// @"image":image}];
// }
// else{
// [self.dataArray addObject:@{@"type":@"vedio",
// @"value":MP4FileUrl,
// @"image":image}];
// }
//
// dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
//
// dispatch_async(dispatch_get_main_queue(), ^{
//
// [self.mainTableView reloadData];
// });
//
// });
}

MOV转码MP4

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
+ (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:@"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:@"yyyy_MM_dd_HH_mm_ss"]; //每次启动后都保存一个新的日志文件中
NSString *dateStr = [formatter stringFromDate:[NSDate date]];
NSString *resultPath = [path stringByAppendingFormat:@"/%@.mp4",dateStr];
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 exportAsynchronouslyWithCompletionHandler:^(void)
{
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;
}
}];
}
}

请注意

上段代码请不要在模拟器上执行,哪怕你模拟器相册导入了视频也不要,因为

1
[AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];

没反应!!!!

这句话是其他帖子没写过的,可能他们没有在模拟器执行过,模拟器是可以导入视频和图片的,直接拖进去就OK。

相关链接