相冊(cè)視頻MOV轉(zhuǎn)MP4
最新做的一個(gè)功能涉及到了視頻的錄制、壓縮及上傳。
根據(jù)網(wǎng)上諸多大神的經(jīng)驗(yàn),終于算是調(diào)通了,但也發(fā)現(xiàn)了一些問(wèn)題,所以把我的經(jīng)驗(yàn)分享一下。
首先,肯定是調(diào)用一下系統(tǒng)的相機(jī)或相冊(cè),
那么導(dǎo)入哪幾個(gè)庫(kù) 就不用我說(shuō)了吧
打開(kāi)相冊(cè)獲取視頻PHAsset
TZImagePickerController 這個(gè)庫(kù)是個(gè)好東西
TZImagePickerController *imagePickerVc = [[TZImagePickerController alloc] initWithMaxImagesCount:20 delegate:self];
imagePickerVc.allowPickingVideo = YES;
imagePickerVc.allowPickingImage = NO;
// 設(shè)置 模態(tài)彈出模式。 iOS 13默認(rèn)非全屏
imagePicker.modalPresentationStyle = UIModalPresentationFullScreen;
[self presentViewController:imagePickerVc animated:YES completion:nil];
從相冊(cè)獲取到視頻PHAsset之后
這時(shí)的 (PHAsset*)asset其實(shí)就是 AVURLAsset類(lèi)型
// 選擇視頻的回調(diào)
-(void)imagePickerController:(TZImagePickerController *)picker
didFinishPickingVideo:(UIImage *)coverImage
sourceAssets:(PHAsset *)asset{
NSLog(@"--------- ?視頻編碼 ----------- 開(kāi)始 ----------");
[MovEncodeToMpegTool convertMovToMp4FromPHAsset:asset
andAVAssetExportPresetQuality:ExportPresetMediumQuality
andMovEncodeToMpegToolResultBlock:^(NSURL *mp4FileUrl, NSData *mp4Data, NSError *error) {
NSLog(@"--------- 視頻編碼 ----------- 結(jié)束 ----------\n{\n %@,\n %ld,\n %@\n}",mp4FileUrl,mp4Data.length,error.localizedDescription);
}];
}
提取MOV文件的AVURLAsset
/// 轉(zhuǎn)碼 MOV--MP4
/// @param resourceAsset MOV資源
/// @param exportQuality 預(yù)設(shè)
/// @param movEncodeToMpegToolResultBlock 轉(zhuǎn)碼后的MP4文件鏈接
+ (void)convertMovToMp4FromPHAsset:(PHAsset*)resourceAsset
andAVAssetExportPresetQuality:(ExportPresetQuality)exportQuality
andMovEncodeToMpegToolResultBlock:(MovEncodeToMpegToolResultBlock)movEncodeToMpegToolResultBlock {
/*
iOS 13 以前
{
assetLocalIdentifier = "A99AA1C3-7D59-4E10-A8D3-BF4FAD7A1BC6/L0/001";
fileSize = 2212572;
filename = "IMG_0049.MOV";
size = "1080,1920";
type = video;
uti = "com.apple.quicktime-movie";
}
iOS 13
{
asset = "9B3F7172-14BB-462E-B003-1CDA5583B038/L0/001";
duration = "20.678";
filename = "IMG_0031.MOV";
size = "1080,1920";
type = video;
uti = "com.apple.quicktime-movie";
}
*/
NSDictionary *videoInfo = [self getVideoInfo:resourceAsset];
NSLog(@"\n%@", videoInfo);
PHVideoRequestOptions *options = [[PHVideoRequestOptions alloc] init];
options.version = PHImageRequestOptionsVersionCurrent;
options.deliveryMode = PHVideoRequestOptionsDeliveryModeAutomatic;
options.networkAccessAllowed = true;
PHImageManager *manager = [PHImageManager defaultManager];
[manager requestAVAssetForVideo:resourceAsset
options:options
resultHandler:^(AVAsset * _Nullable asset, AVAudioMix * _Nullable audioMix, NSDictionary * _Nullable info) {
if ([asset isKindOfClass:[AVURLAsset class]]) {
AVURLAsset *urlAsset = (AVURLAsset *)asset;
[MovEncodeToMpegTool convertMovToMp4FromAVURLAsset:urlAsset
andAVAssetExportPresetQuality:exportQuality
andMovEncodeToMpegToolResultBlock:movEncodeToMpegToolResultBlock];
}else{
NSError *error = [NSError errorWithDomain:@"ConvertMovToMp4ErrorDomain"
code:10008
userInfo:@{NSLocalizedDescriptionKey:@"resource type error"}];
movEncodeToMpegToolResultBlock(nil, nil , error);
}
}];;
}
MOV轉(zhuǎn)碼MP4
#pragma mark ### MOV轉(zhuǎn)碼MP4
+ (void)convertMovToMp4FromAVURLAsset:(AVURLAsset*)urlAsset
andAVAssetExportPresetQuality:(ExportPresetQuality)exportQuality
andMovEncodeToMpegToolResultBlock:(MovEncodeToMpegToolResultBlock)movEncodeToMpegToolResultBlock {
AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:urlAsset.URL options:nil];
NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
// 查詢是否有匹配的預(yù)設(shè)
if ([compatiblePresets containsObject:[self getAVAssetExportPresetQuality:exportQuality]]) {
// 在Documents目錄下創(chuàng)建一個(gè)名為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(@"創(chuàng)建文件夾失??!%@",path);
}
NSLog(@"創(chuàng)建文件夾成功,文件路徑%@",path);
}
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[formatter setLocale:[[NSLocale alloc] initWithLocaleIdentifier:@"zh_CN"]];
[formatter setDateFormat:@"yyyyMMddHHmmss"]; //每次啟動(dòng)后都保存一個(gè)新的文件中
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);
}
}
自定義轉(zhuǎn)碼預(yù)設(shè)枚舉類(lèi)型,用以映射系統(tǒng)預(yù)設(shè)
/*
AVAssetExportPresetLowQuality 低質(zhì)量 可以通過(guò)移動(dòng)網(wǎng)絡(luò)分享
AVAssetExportPresetMediumQuality 中等質(zhì)量 可以通過(guò)WIFI網(wǎng)絡(luò)分享
AVAssetExportPresetHighestQuality 高等質(zhì)量
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;
}
}
請(qǐng)注意
轉(zhuǎn)碼方法最好不要在模擬器上執(zhí)行,哪怕你模擬器相冊(cè)導(dǎo)入了視頻也不要,因?yàn)?/p>
[AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];
有概率 無(wú) 反 應(yīng) !?。?!
這句話是其他帖子沒(méi)寫(xiě)過(guò)的,可能他們沒(méi)有在模擬器執(zhí)行過(guò),模擬器是可以導(dǎo)入視頻和圖片的,直接拖進(jìn)去就OK。
建議真機(jī)運(yùn)行!
建議真機(jī)運(yùn)行!
建議真機(jī)運(yùn)行!
2020年兒童節(jié)更新。
目前本文Demo已經(jīng)遷移至《TZImagePickerControllerDemo》