iOS--相冊(cè)視頻MOV轉(zhuǎn)MP4

相冊(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

相關(guān)鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容