1. 程式人生 > >iOS--相簿視訊MOV轉MP4

iOS--相簿視訊MOV轉MP4

相簿視訊MOV轉MP4

最新做的一個功能涉及到了視訊的錄製、壓縮及上傳。

根據網上諸多大神的經驗,終於算是調通了,但也發現了一些問題,所以把我的經驗分享一下。

首先,肯定是呼叫一下系統的相機或相簿,

那麼匯入哪幾個庫 就不用我說了吧

開啟相簿獲取視訊PHAsset

TZImagePickerController 這個庫是個好東西

    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型別

-(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];
                          
                      }];
}

為了避免迴圈引用,這裡另寫方法呼叫轉碼


- (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];
                           }];
    
    
}

檢查視訊檔案的合規性

//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

+ (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;
             }
         }];
    }
}

請注意

上段程式碼請不要在模擬器上執行,哪怕你模擬器相簿匯入了視訊也不要,因為

[AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];

沒反應!!!!

這句話是其他帖子沒寫過的,可能他們沒有在模擬器執行過,模擬器是可以匯入視訊和圖片的,直接拖進去就OK。

相關連結