以编程方式将 iPhone MOV 转换为 Mp4 视频

Posted

技术标签:

【中文标题】以编程方式将 iPhone MOV 转换为 Mp4 视频【英文标题】:MOV to Mp4 video conversion iPhone programmatically 【发布时间】:2012-01-18 11:00:45 【问题描述】:

我正在为 iPhone 中的 Play Station 3 开发媒体服务器。

我知道 PS3 不支持 .MOV 文件,所以我必须将其转换为 Mp4 或其他 PS3 支持的转码。

这是我所做的,但如果我设置的文件类型与其源文件不同,它会崩溃。

AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];

NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:avAsset];

if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality])

    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:avAsset presetName:AVAssetExportPresetLowQuality];

    exportSession.outputURL = [NSURL fileURLWithPath:videoPath];

    exportSession.outputFileType = AVFileTypeMPEG4;

    CMTime start = CMTimeMakeWithSeconds(1.0, 600);

    CMTime duration = CMTimeMakeWithSeconds(3.0, 600);

    CMTimeRange range = CMTimeRangeMake(start, duration);

    exportSession.timeRange = range;

    [exportSession exportAsynchronouslyWithCompletionHandler:^

        switch ([exportSession status]) 

            case AVAssetExportSessionStatusFailed:
                NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]);

                break;

            case AVAssetExportSessionStatusCancelled:

                NSLog(@"Export canceled");

                break;

            default:

                break;
        

        [exportSession release];
    ];

如果我在此处设置 AVFileTypeMPEG4,它会崩溃,并显示“文件类型无效”。所以我必须将它设置为 AVFileTypeQuickTimeMovie 并提供 MOV 文件。

是否可以在 ios 中通过 AVAssetExportSession 将视频从 MOV 转换为 Mp4...或者无需任何第三方库?

【问题讨论】:

你有没有为此找到解决方案? 你有这个转换的解决方案吗??? 您无法在 iOS 上读取任何随机的 .mov 编解码器,AVFoundation 只能读取编码为 .mov/.m4v 的 h.264 视频。 【参考方案1】:

这里是代码

    func encodeVideo(videoURL: NSURL)  
    let avAsset = AVURLAsset(URL: videoURL, options: nil)

    var startDate = NSDate()

    //Create Export session
    exportSession = AVAssetExportSession(asset: avAsset, presetName: AVAssetExportPresetPassthrough)

    // exportSession = AVAssetExportSession(asset: composition, presetName: mp4Quality)
    //Creating temp path to save the converted video


    let documentsDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
    let myDocumentPath = NSURL(fileURLWithPath: documentsDirectory).URLByAppendingPathComponent("temp.mp4").absoluteString
    let url = NSURL(fileURLWithPath: myDocumentPath)

    let documentsDirectory2 = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as NSURL

    let filePath = documentsDirectory2.URLByAppendingPathComponent("rendered-Video.mp4")
    deleteFile(filePath)

    //Check if the file already exists then remove the previous file
    if NSFileManager.defaultManager().fileExistsAtPath(myDocumentPath) 
        do 
            try NSFileManager.defaultManager().removeItemAtPath(myDocumentPath)
        
        catch let error 
            print(error)
        
    

     url

    exportSession!.outputURL = filePath
    exportSession!.outputFileType = AVFileTypeMPEG4
    exportSession!.shouldOptimizeForNetworkUse = true
    var start = CMTimeMakeWithSeconds(0.0, 0)
    var range = CMTimeRangeMake(start, avAsset.duration)
    exportSession.timeRange = range

    exportSession!.exportAsynchronouslyWithCompletionHandler(() -> Void in
        switch self.exportSession!.status 
        case .Failed:
            print("%@",self.exportSession?.error)
        case .Cancelled:
            print("Export canceled")
        case .Completed:
            //Video conversion finished
            var endDate = NSDate()

            var time = endDate.timeIntervalSinceDate(startDate)
            print(time)
            print("Successful!")
            print(self.exportSession.outputURL)

        default:
            break
        

    )




func deleteFile(filePath:NSURL) 
    guard NSFileManager.defaultManager().fileExistsAtPath(filePath.path!) else 
        return
    

    do 
        try NSFileManager.defaultManager().removeItemAtPath(filePath.path!)
    catch
        fatalError("Unable to delete file: \(error) : \(__FUNCTION__).")
    

【讨论】:

【参考方案2】:

您可以通过 AVAssets 将视频转换为 mp4。

AVURLAsset *avAsset = [AVURLAsset URLAssetWithURL:videoURL options:nil];
NSArray *compatiblePresets = [AVAssetExportSession     
exportPresetsCompatibleWithAsset:avAsset];
AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:avAsset presetName:AVAssetExportPresetLowQuality];

NSString* documentsDirectory=[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
  exportSession.outputURL = url;
 //set the output file format if you want to make it in other file format (ex .3gp)
 exportSession.outputFileType = AVFileTypeMPEG4;
 exportSession.shouldOptimizeForNetworkUse = YES;

 [exportSession exportAsynchronouslyWithCompletionHandler:^
 switch ([exportSession status])
 
      case AVAssetExportSessionStatusFailed:
           NSLog(@"Export session failed");
           break;
      case AVAssetExportSessionStatusCancelled:
           NSLog(@"Export canceled");
           break;
      case AVAssetExportSessionStatusCompleted:
      
           //Video conversion finished
           NSLog(@"Successful!");
      
           break;
      default:
           break;
  
 ];

要轻松地将视频转换为 mp4,请使用此 link

您还可以找到将视频转换为 mp4 的示例项目。

【讨论】:

输出文件是否可以是临时的NSData 对象而不是outputURL?我不明白我应该将转换后的文件保存在哪里......而且我不想保存它,我只想将它上传到服务器,所以我不需要@987654325 @,我只需要将它输出到一个对象。【参考方案3】:

您需要AVMutableComposition 来执行此操作。因为Asset在iOS 5.0下无法直接转码为MP4。

- (BOOL)encodeVideo:(NSURL *)videoURL

    AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoURL options:nil];

    // Create the composition and tracks
    AVMutableComposition *composition = [AVMutableComposition composition];
    AVMutableCompositionTrack *videoTrack = [composition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid];
    AVMutableCompositionTrack *audioTrack = [composition addMutableTrackWithMediaType:AVMediaTypeAudio preferredTrackID:kCMPersistentTrackID_Invalid];
    NSArray *assetVideoTracks = [asset tracksWithMediaType:AVMediaTypeVideo];
    if (assetVideoTracks.count <= 0)
    
            NSLog(@"Error reading the transformed video track");
            return NO;
    

    // Insert the tracks in the composition's tracks
    AVAssetTrack *assetVideoTrack = [assetVideoTracks firstObject];
    [videoTrack insertTimeRange:assetVideoTrack.timeRange ofTrack:assetVideoTrack atTime:CMTimeMake(0, 1) error:nil];
    [videoTrack setPreferredTransform:assetVideoTrack.preferredTransform];

    AVAssetTrack *assetAudioTrack = [[asset tracksWithMediaType:AVMediaTypeAudio] objectAtIndex:0];
    [audioTrack insertTimeRange:assetAudioTrack.timeRange ofTrack:assetAudioTrack atTime:CMTimeMake(0, 1) error:nil];

    // Export to mp4
    NSString *mp4Quality = [MGPublic isIOSAbove:@"6.0"] ? AVAssetExportPresetMediumQuality : AVAssetExportPresetPassthrough;
    NSString *exportPath = [NSString stringWithFormat:@"%@/%@.mp4",
                                     [NSHomeDirectory() stringByAppendingString:@"/tmp"],
                                     [BSCommon uuidString]];

    NSURL *exportUrl = [NSURL fileURLWithPath:exportPath];
    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:composition presetName:mp4Quality];
    exportSession.outputURL = exportUrl;
    CMTime start = CMTimeMakeWithSeconds(0.0, 0);
    CMTimeRange range = CMTimeRangeMake(start, [asset duration]);
    exportSession.timeRange = range;
    exportSession.outputFileType = AVFileTypeMPEG4;
    [exportSession exportAsynchronouslyWithCompletionHandler:^
            switch ([exportSession status])
            
                case AVAssetExportSessionStatusCompleted:
                       NSLog(@"MP4 Successful!");
                       break;
                case AVAssetExportSessionStatusFailed:
                       NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]);
                       break;
                case AVAssetExportSessionStatusCancelled:
                       NSLog(@"Export canceled");
                       break;
                default:
                       break;
            
    ];

    return YES;

【讨论】:

【参考方案4】:

使用下面的代码

    NSURL * mediaURL = [info objectForKey:UIImagePickerControllerMediaURL];
    AVAsset *video = [AVAsset assetWithURL:mediaURL];
    AVAssetExportSession *exportSession = [AVAssetExportSession exportSessionWithAsset:video presetName:AVAssetExportPresetMediumQuality];
    exportSession.shouldOptimizeForNetworkUse = YES;
    exportSession.outputFileType = AVFileTypeMPEG4;

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
    basePath = [basePath stringByAppendingPathComponent:@"videos"];
    if (![[NSFileManager defaultManager] fileExistsAtPath:basePath])
        [[NSFileManager defaultManager] createDirectoryAtPath:basePath withIntermediateDirectories:YES attributes:nil error:nil];

    compressedVideoUrl=nil;
    compressedVideoUrl = [NSURL fileURLWithPath:basePath];
    long CurrentTime = [[NSDate date] timeIntervalSince1970];
    NSString *strImageName = [NSString stringWithFormat:@"%ld",CurrentTime];
    compressedVideoUrl=[compressedVideoUrl URLByAppendingPathComponent:[NSString stringWithFormat:@"%@.mp4",strImageName]];

    exportSession.outputURL = compressedVideoUrl;

    [exportSession exportAsynchronouslyWithCompletionHandler:^

        NSLog(@"done processing video!");
        NSLog(@"%@",compressedVideoUrl);

        if(!dataMovie)
            dataMovie = [[NSMutableData alloc] init];
        dataMovie = [NSData dataWithContentsOfURL:compressedVideoUrl];

    ];

【讨论】:

【参考方案5】:

只是想说网址不能像

[NSURL URLWithString: [@"~/Documents/movie.mov" stringByExpandingTildeInPath]]

应该是这样的

[NSURL fileURLWithPath: [@"~/Documents/movie.mov" stringByExpandingTildeInPath]]

我花了一段时间才弄明白 :-)

【讨论】:

【参考方案6】:

presetName 使用“AVAssetExportPresetPassthrough”而不是“AVAssetExportPresetLowQuality”

 AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]initWithAsset:avAsset presetName:AVAssetExportPresetPassthrough];

【讨论】:

【参考方案7】:

MOV 与 MP4 非常相似,您也许可以只更改扩展名并让它工作,Windows Phone 无法播放 .MOVS 但可以播放 mp4,我所做的只是将扩展名更改为 .mov到.mp4,它工作正常,这是从iphone上拍摄的视频......如果有什么可以定义的尝试使用AVAssetExporter导出并尝试那里有一个MP4和M4A的文件类型,你可以从文件格式中看到尿路感染here

希望对你有帮助

【讨论】:

Daniel,下面是我所做的,但如果我设置的文件类型与其源文件不同,它会崩溃。 该解决方案不适用于所有地方,例如某些 android 设备无法重现这些视频 更改扩展名可能是 PS3 的一个 hack,但不是一个普遍适用的真正解决方案。 即使更改了扩展名,文件本身仍然是.mov.. mp4 和 mov 之间的差异是微妙的,并且会导致意想不到的问题(例如有时无法播放音频)。 MP4 是一种容器格式。最好做一个合适的 mp4 生成。

以上是关于以编程方式将 iPhone MOV 转换为 Mp4 视频的主要内容,如果未能解决你的问题,请参考以下文章

将带有 amr_nb 音频编解码器的 .mov 包装器转换为 aac/mp4a 编解码器

ffmpeg - 将 MOV 文件转换为 MP4 [关闭]

以 mp4 格式录制、保存和/或转换视频?

ffmpeg 没有将一些 mov 视频转换为 mp4

快速将MOV格式视频转换成MP4无损方法

快速将MOV格式视频转换成MP4无损方法