iPhone:编程压缩录制的视频以便分享?

45
我在调用相机视图录制视频前,实现了一个覆盖视图。
pickerController.cameraOverlayView =myOverlay;

在录制视频并通过电子邮件等方式分享后,将视频录制并保存到相册中都正常工作。

如果我使用“高质量”作为视频质量,则录制的视频文件大小会变得非常大。例如,如果我以高质量录制30秒的视频,则录制的视频文件大小约为30-40 MB。

pickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;

如何像苹果内置视频录制一样,在分享前对高质量录制的视频进行压缩编程?

请指导我解决这个问题。

谢谢!

更新:

这是我最近尝试的方法,但仍然没有成功:我想要压缩通过didFinishPickingMediaWithInfo获取的已记录视频,并将其存储在同一个照片相册的实际视频路径中,而不是任何其他地方。当我从照片库中选择相同的视频时,测试发现该视频已被压缩到非常小的大小,但是通过didFinishPickingMediaWithInfo拍摄并传输的相同视频并没有被压缩,尽管我使用了下面的AVAssetExportSession代码。

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info{

    NSString *mediaType = [info objectForKey:UIImagePickerControllerMediaType];


if ([mediaType isEqualToString:(NSString *)kUTTypeMovie])
{

    NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
    NSString *urlPath = [videoURL path];

    if ([[urlPath lastPathComponent] isEqualToString:@"capturedvideo.MOV"])
    {
        if (UIVideoAtPathIsCompatibleWithSavedPhotosAlbum (urlPath))
        {
            [self copyTempVideoToMediaLibrary :urlPath];


        }
        else
        {
            NSLog(@"Video Capture Error: Captured video cannot be saved...didFinishPickingMediaWithInfo()");                
        }
    }       
    else
    {
        NSLog(@"Processing soon to saved photos album...else loop of lastPathComponent..didFinishPickingMediaWithInfo()");
    }
}    
[self dismissModalViewControllerAnimated:YES];
}

- (void)copyTempVideoToMediaLibrary :(NSString *)videoURL {

        dispatch_queue_t mainQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

dispatch_async(mainQueue, ^{

    ALAssetsLibrary *library = [[[ALAssetsLibrary alloc] init] autorelease];

    ALAssetsLibraryWriteVideoCompletionBlock completionBlock = ^(NSURL *assetURL, NSError *error) {
        NSLog(@"Saved URL: %@", assetURL);
        NSLog(@"Error: %@", error);

        if (assetURL != nil) {

            AVURLAsset *theAsset = [AVURLAsset URLAssetWithURL:[NSURL URLWithString:videoURL] options:nil];

            NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:theAsset];

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

            [exportSession setOutputURL:[NSURL URLWithString:videoURL]];
            [exportSession setOutputFileType:AVFileTypeQuickTimeMovie];

            [exportSession exportAsynchronouslyWithCompletionHandler:^ {
                switch ([exportSession status]) {
                    case AVAssetExportSessionStatusFailed:
                        NSLog(@"Export session faied with error: %@", [exportSession error]);
                        break;
                    default:
                        //[self mediaIsReady];
                        break;
                }
            }];
        }
    };

    [library writeVideoAtPathToSavedPhotosAlbum:[NSURL URLWithString:videoURL] completionBlock:completionBlock];
});
}

1
降低视频质量和压缩视频有什么区别?还是说你指的是像压缩文件一样的压缩? - Koh Jing Yu
7个回答

68
如果你想压缩视频以便远程共享并在iPhone上保留原始质量进行本地存储,你应该研究AVAssetExportSessionAVAssetWriter。此外,了解iOS如何管理Assets也很重要。
- (void)convertVideoToLowQuailtyWithInputURL:(NSURL*)inputURL 
                                   outputURL:(NSURL*)outputURL 
                                     handler:(void (^)(AVAssetExportSession*))handler
{
    [[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
    AVURLAsset *asset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc] initWithAsset:asset presetName:AVAssetExportPresetLowQuality];
    exportSession.outputURL = outputURL;
    exportSession.outputFileType = AVFileTypeQuickTimeMovie;
    [exportSession exportAsynchronouslyWithCompletionHandler:^(void) 
    {
        handler(exportSession);
        [exportSession release];
    }];
}

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info

{   
    NSURL *videoURL = [info objectForKey:UIImagePickerControllerMediaURL];
    NSURL *outputURL = [NSURL fileURLWithPath:@"/Users/josh/Desktop/output.mov"];
    [self convertVideoToLowQuailtyWithInputURL:videoURL outputURL:outputURL handler:^(AVAssetExportSession *exportSession)
     {
         if (exportSession.status == AVAssetExportSessionStatusCompleted)
         {
             printf("completed\n");
         }
         else
         {
             printf("error\n");

         }
     }];

}

2
嗨,我发现如果我们降低视频质量,那么实际的高清录制视频(720p)的质量也会降低,这不是真正的高清视频。我的要求是,在上传(发送)到服务器之前如何保持视频的相同尺寸(720p),但减小视频的大小。 - Getsy
1
你用过照片应用吗?如果你使用“分享到YouTube”按钮,苹果似乎能够压缩720p视频,并且生成的文件保持相对高质量,文件大小更小。他们是如何做到的呢? - zakdances
这个苹果示例代码可能会有所帮助。https://developer.apple.com/LIBRARY/IOS/samplecode/AVMovieExporter/Introduction/Intro.html - Underdog
AVAssetWriter 用于什么? - etayluz
1
从我的经验来看,AVAssetWriter是用于专门的视频和音频混合。例如,如果你有H264格式的视频和原始的16位LE PCM格式的音频,你可以压缩音频AAC,然后使用AVAssetWriter将h264+AAC组合成MPEG4或兼容Quicktime的文件。另一方面,AVAssetExportSession对于将已经格式化为MPEG4/Quicktime文件并重新压缩为更低比特率非常有帮助。 - tidwall
显示剩余7条评论

14

我猜这个视频已经通过h264编解码器进行了压缩。但你可以尝试使用AVFoundation从相机中捕获视频文件。但我怀疑你最终会得到相同的文件大小。

以下是使用不同质量预设在iPhone 4上录制10秒视频文件的一些统计数据。

high (1280х720) = ~14MB = ~11Mbit/s
640 (640х480) = ~4MB = ~3.2Mbit/s
medium (360х480) = ~1MB = ~820Kbit/s
low (144х192) = ~208KB = ~170Kbit/s

谢谢您提供这些信息。在这里,您可以看到问题所在......14MB对于10秒的视频来说太大了,导致降至480p会显著降低质量。 - zakdances
2
注意:这不适用于新的iPhone型号,即iPhone 5、5s等。 - Alex Spencer

13
pickerController.videoQuality = UIImagePickerControllerQualityTypeMedium;

以下是您可以选择的所有值。

UIImagePickerControllerQualityTypeHigh    = 0,
UIImagePickerControllerQualityType640x480 = 3,
UIImagePickerControllerQualityTypeMedium  = 1,  // default value
UIImagePickerControllerQualityTypeLow     = 2

2
不确定其他人是否有这个问题,但当我更改选择器质量(如上所示),它并没有改变我的文件大小 =\ - Ibdakine

6

使用Swift编程压缩视频

不要忘记添加 - import AssetsLibrary

func convertVideoWithMediumQuality(inputURL : NSURL){

            let VideoFilePath = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent("mergeVideo\(arc4random()%1000)d").URLByAppendingPathExtension("mp4").absoluteString
            if NSFileManager.defaultManager().fileExistsAtPath(VideoFilePath) {
                do {
                    try NSFileManager.defaultManager().removeItemAtPath(VideoFilePath)
                } catch { }
            } 

            let savePathUrl =  NSURL(string: VideoFilePath)!

            let sourceAsset = AVURLAsset(URL: inputURL, options: nil)

            let assetExport: AVAssetExportSession = AVAssetExportSession(asset: sourceAsset, presetName: AVAssetExportPresetMediumQuality)!
            assetExport.outputFileType = AVFileTypeQuickTimeMovie
            assetExport.outputURL = savePathUrl
            assetExport.exportAsynchronouslyWithCompletionHandler { () -> Void in

                switch assetExport.status {
                case AVAssetExportSessionStatus.Completed:
                    dispatch_async(dispatch_get_main_queue(), {
                        do {
                            let videoData = try NSData(contentsOfURL: savePathUrl, options: NSDataReadingOptions())
                            print("MB - \(videoData.length / (1024 * 1024))")
                        } catch {
                            print(error)
                        }

                    })
                case  AVAssetExportSessionStatus.Failed:
                    self.hideActivityIndicator(self.view)
                    print("failed \(assetExport.error)")
                case AVAssetExportSessionStatus.Cancelled:
                    self.hideActivityIndicator(self.view)
                    print("cancelled \(assetExport.error)")
                default:
                    self.hideActivityIndicator(self.view)
                    print("complete")
                }
            }

        }

2
很好。但是为什么要使用随机文件名呢?这看起来很可疑。如果您多次调用此函数并希望避免名称冲突,则使用随机文件名不总是有效的,会导致问题。如果您一次只调用此函数,则不需要随机文件名。我建议使用更确定性的命名方案(例如计数器等)。 - n13
这行代码是主要的,用于处理视频压缩大小 ***let assetExport: AVAssetExportSession = AVAssetExportSession(asset: sourceAsset, presetName: AVAssetExportPresetMediumQuality)!***。 - Amjad Khan

4
尝试一下以下几行代码:
    [[NSFileManager defaultManager] removeItemAtURL:outputURL error:nil];
    AVURLAsset *urlAsset = [AVURLAsset URLAssetWithURL:inputURL options:nil];
    AVAssetExportSession *session = [[AVAssetExportSession alloc] initWithAsset: urlAsset presetName:AVAssetExportPresetLowQuality];
    session.outputURL = outputURL;
    session.outputFileType = AVFileTypeQuickTimeMovie;
    [session exportAsynchronouslyWithCompletionHandler:^(void) 
    {
        handler(session);

    }];

2
我发现了一个非常好的自定义类(SDAVAssetExportSession)来进行视频压缩。您可以从此链接下载它。
下载后,将SDAVAssetExportSession.h和SDAVAssetExportSession.m文件添加到您的项目中,然后使用下面的代码进行压缩。 在下面的代码中,您可以通过指定分辨率和比特率来压缩视频
#import "SDAVAssetExportSession.h"


- (void)compressVideoWithInputVideoUrl:(NSURL *) inputVideoUrl
{
    /* Create Output File Url */

    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSString *finalVideoURLString = [documentsDirectory stringByAppendingPathComponent:@"compressedVideo.mp4"];
    NSURL *outputVideoUrl = ([[NSURL URLWithString:finalVideoURLString] isFileURL] == 1)?([NSURL URLWithString:finalVideoURLString]):([NSURL fileURLWithPath:finalVideoURLString]); // Url Should be a file Url, so here we check and convert it into a file Url


    SDAVAssetExportSession *compressionEncoder = [SDAVAssetExportSession.alloc initWithAsset:[AVAsset assetWithURL:inputVideoUrl]]; // provide inputVideo Url Here
    compressionEncoder.outputFileType = AVFileTypeMPEG4;
    compressionEncoder.outputURL = outputVideoUrl; //Provide output video Url here
    compressionEncoder.videoSettings = @
    {
    AVVideoCodecKey: AVVideoCodecH264,
    AVVideoWidthKey: @800,   //Set your resolution width here
    AVVideoHeightKey: @600,  //set your resolution height here
    AVVideoCompressionPropertiesKey: @
        {
        AVVideoAverageBitRateKey: @45000, // Give your bitrate here for lower size give low values
        AVVideoProfileLevelKey: AVVideoProfileLevelH264High40,
        },
    };
    compressionEncoder.audioSettings = @
    {
    AVFormatIDKey: @(kAudioFormatMPEG4AAC),
    AVNumberOfChannelsKey: @2,
    AVSampleRateKey: @44100,
    AVEncoderBitRateKey: @128000,
    };

    [compressionEncoder exportAsynchronouslyWithCompletionHandler:^
     {
         if (compressionEncoder.status == AVAssetExportSessionStatusCompleted)
         {
            NSLog(@"Compression Export Completed Successfully");
         }
         else if (compressionEncoder.status == AVAssetExportSessionStatusCancelled)
         {
             NSLog(@"Compression Export Canceled");
         }
         else
         {
              NSLog(@"Compression Failed");

         }
     }];

}

取消压缩,请使用以下代码行
 [compressionEncoder cancelExport]; //Video compression cancel

当使用sdavexportsession时,我得到了以下错误 -[AVAssetWriter startSessionAtSourceTime:] invalid parameter not satisfying: CMTIME_IS_NUMERIC(startTime)。 - user8674636

0

我破解了这个。

使用 exportSession.fileLengthLimit = 1048576 * 10 //10 MB

10MB 是硬编码数字。根据您所需的比特率使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接