45

I have implemented an overlay view when calling camera view before recording the video.

pickerController.cameraOverlayView =myOverlay;

Video recording and saving the video into Album after recording the video and sharing via email etc. all works fine.

If i use video quality as "High quality", then the recorded video has become huge size. For example, if i record video for 30 seconds with high quality, recorded video has become around 30 - 40 mb.

pickerController.videoQuality = UIImagePickerControllerQualityTypeHigh;

How do i program to compress the high quality recorded video before sharing it, like how Apple does with built-in Video recorder?

Please guide me to resolve this.

Thanks!

UPDATED:

This is what i'm trying recently, but still no success: I want to compress the recorded video taken which comes to didFinishPickingMediaWithInfo and store in same photo album actual video path itself, not anywhere else. I tested the same video is compressed to very small size when i pick from photo library, but the same video taken from camera and came via didFinishPickingMediaWithInfo is not compressed, though i used the AVAssetExportSession code below.

- (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];
});
}
Chintan Patel
  • 3,175
  • 3
  • 30
  • 36
Getsy
  • 4,887
  • 16
  • 78
  • 139
  • 1
    What would be the difference between lowering the videoQuality and compressing it? Or do you mean compressing like zipping? – Koh Jing Yu Apr 19 '11 at 12:36

7 Answers7

68

If you want to compress the video for remote sharing and keep the original quality for local storage on the iPhone, you should look into AVAssetExportSession or AVAssetWriter.

Also read up on how iOS manages 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");

         }
     }];

}
Adam Matan
  • 128,757
  • 147
  • 397
  • 562
tidwall
  • 6,881
  • 2
  • 36
  • 47
  • Hi Jojaba, I'm struggling on this still. I use UIImagePickerController to take video/choose from photo library, taking video from photo library is compressing the video(showing message also as compressing..) nicely when click on "Choose", but if i allow to take video from camera and then click on "Use" is not compressing the taken video. Is it possible to compress video when click on "Use"? How it is not working by default when i use the UIImagePickerController API's? – Getsy May 24 '11 at 21:32
  • Hi, Thanks. I tried samething already, but no success. please check my updated code above and let me know what am i doing wrong. Please help on this. – Getsy May 25 '11 at 16:29
  • 2
    Hi, I found that if we reduce to lower quality, then the actual HD recorded video(720p) has become lower quality which is not the actual HD video. My requirement is, how to keep the video with the same dimension(720p) but reduce the size of the video before uploading(sending) to my server. – Getsy May 26 '11 at 10:32
  • Look into the `exportSession.videoComposition` property. Though keep in mind that a low quailty 720p is going to look the same and be larger in filesize than a low quality 480p video. You may be better off using a low quality+resolution and scale the video larger during playback. – tidwall May 26 '11 at 13:42
  • 1
    Have you used the Photos app? If you use the "Share on Youtube" button, Apple seems to be able to compress 720p video with the resulting file retaining relatively high quality and the file size being smaller. How do they do it? – zakdances Aug 04 '12 at 22:25
  • Can someone post an example using AVAssetWriter to lower the framerate and bitrate of the video? – zakdances Aug 04 '12 at 22:36
  • this apple sample code might help. https://developer.apple.com/LIBRARY/IOS/samplecode/AVMovieExporter/Introduction/Intro.html – Underdog Oct 13 '13 at 20:13
  • If i change the output URL to point at "Document directory", it fails..Why so ? – Shailesh Aug 14 '14 at 17:35
  • What is AVAssetWriter used for? – etayluz Jul 29 '15 at 21:12
  • 1
    @etayluz From my experience, AVAssetWriter is for specialized muxing of video and audio. For example, if you have video in h264 and audio in raw 16bit-LE PCM, you can compress the audio AAC and then use AVAssetWriter to combine h264+AAC into an MPEG4 or Quicktime compatible file. AVAssetExportSession on the other hand is helpful for taking an already formatted MPEG4 / Quicktime file and recompressing for lower a bitrate. – tidwall Jul 29 '15 at 22:27
  • its giving error : Snapshotting a view that has not been rendered results in an empty snapshot. Ensure your view has been rendered at least once before snapshotting or snapshot after screen updates. error – coreDeviOS Dec 14 '15 at 05:53
  • This code did not work for me. I am using UIPickerViewController to get videos from gallery and capture videos as well. In both cases video data size did not change. And after calling the convertVideoToLowQuailtyWithInputURL method, I am not able to playback the video as well. I am using MPMoviePlayerController to play the video. – crypt Oct 28 '16 at 13:57
14

I guess the video is already compressed by the h264 codec. But you can try to use AVFoundation to capture the video files from camera. But I suspect you'll end up with the same file sizes.

Here is some statistics for the 10 seconds video file recorded on the iPhone 4 with different quality pressets.

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
Alexey Kozhanov
  • 405
  • 3
  • 5
  • Thank you for this info. Right here you can see the problem...14MB is waaaay to big for a 10 second video. and the resulting drop to 480p reduces the quality signifigantly – zakdances Aug 04 '12 at 22:27
  • 2
    Note: This does not hold true for newer iPhone models-- i.e. iPhone 5,5s, etc. – Alex Spencer Jun 26 '14 at 03:28
13
pickerController.videoQuality = UIImagePickerControllerQualityTypeMedium;

These are all the values you can pick from.

UIImagePickerControllerQualityTypeHigh    = 0,
UIImagePickerControllerQualityType640x480 = 3,
UIImagePickerControllerQualityTypeMedium  = 1,  // default value
UIImagePickerControllerQualityTypeLow     = 2
visakh7
  • 26,380
  • 8
  • 55
  • 69
Randall
  • 14,691
  • 7
  • 40
  • 60
  • 2
    Not sure if anyone else has this issue but when I change picker quality (shown above) it does not change my file size =\ – Ibdakine Sep 06 '15 at 21:22
6

Programmatically compressing video with using swift

And don't forgot to add - 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
    Cool. Why use a random filename though? That looks suspicious. If you have this invoked multiple times and want to avoid name clashes, using the random filename won't always work and lead to problems. If you only invoke this one at a time, you don't need a random filename. I recommend using a more deterministic naming scheme (counters etc) – n13 Jul 30 '16 at 12:01
  • This line is main where the video compression size is handle ***let assetExport: AVAssetExportSession = AVAssetExportSession(asset: sourceAsset, presetName: AVAssetExportPresetMediumQuality)!*** – Amjad Khan May 18 '20 at 12:00
4

Try this few lines :

    [[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);

    }];
Rajesh Loganathan
  • 11,129
  • 4
  • 78
  • 90
2

I found an excellent custom class(SDAVAssetExportSession) to do the video compression. You can download it from this link.

After downloading add SDAVAssetExportSession.h and SDAVAssetExportSession.m files into your project, Then the below code will help to do the compression. In below code you can compress video by specifying resolution and bitrate

#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");

         }
     }];

}

To Cancel Compression Use Below Line Of code

 [compressionEncoder cancelExport]; //Video compression cancel
arunjos007
  • 4,105
  • 1
  • 28
  • 43
  • I get error as -[AVAssetWriter startSessionAtSourceTime:] invalid parameter not satisfying: CMTIME_IS_NUMERIC(startTime) when using sdavexportsession –  Mar 08 '18 at 05:51
0

I cracked this.

Use exportSession.fileLengthLimit = 1048576 * 10 //10 MB

10MB is hard coded number. Use according to your required bitrate.

Kumar
  • 1,882
  • 2
  • 27
  • 44