Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change the bitrate of recordings made with AVFoundation on Mac

I'm doing some screen recording on the OSX 10.8 with AVFoundation. I'm using the sessionPreset "AVCaptureSessionPresetPhoto" to record the entire screen. One thing I'd like to change is the quality of the movie file that is created.

AVCaptureSessionPresetPhoto seems to be required to actually capture the full screen without clipping.

According to the documentation here: http://developer.apple.com/library/mac/#documentation/AVFoundation/Reference/AVCaptureSession_Class/Reference/Reference.html

"You use this property to customize the quality level or bitrate of the output. For possible values of sessionPreset, see “Video Input Presets.” The default value is AVCaptureSessionPresetHigh."

However, for Video Input Presets, the only options are these constants: http://developer.apple.com/library/ios/#documentation/AVFoundation/Reference/AVCaptureSession_Class/Reference/Reference.html#//apple_ref/occ/cl/AVCaptureSession

NSString *const AVCaptureSessionPresetPhoto;

NSString *const AVCaptureSessionPresetHigh;

NSString *const AVCaptureSessionPresetMedium;

NSString *const AVCaptureSessionPresetLow;

NSString *const AVCaptureSessionPreset320x240;

NSString *const AVCaptureSessionPreset352x288;

NSString *const AVCaptureSessionPreset640x480;

NSString *const AVCaptureSessionPreset960x540;

NSString *const AVCaptureSessionPreset1280x720;

AVCaptureSessionPresetPhoto does the work of capturing the full screen without clipping, but the final quality is somewhat underwhelming. There are visible artifacts due to the lower bitrate that is used by default.

How can I go about increasing the bitrate of the final recording?

Below is a sample of my current code.

    mSession = [[AVCaptureSession alloc] init];

    mSession.sessionPreset = AVCaptureSessionPresetPhoto;

    CGDirectDisplayID displayId = kCGDirectMainDisplay;

    AVCaptureScreenInput *input = [[AVCaptureScreenInput alloc] initWithDisplayID:displayId];
    if (!input) {
        mSession = nil;
        return;
    }        

    if ([mSession canAddInput:input]){
        [mSession addInput:input];
    }

    mMovieFileOutput = [[AVCaptureMovieFileOutput alloc] init];
    if ([mSession canAddOutput:mMovieFileOutput])
        [mSession addOutput:mMovieFileOutput];

    [mSession startRunning];

    if ([[NSFileManager defaultManager] fileExistsAtPath:[destPath path]])
    {
        NSError *err;
        if (![[NSFileManager defaultManager] removeItemAtPath:[destPath path] error:&err])
        {
            NSLog(@"Error deleting existing movie %@",[err localizedDescription]);
        }
    }

    [mMovieFileOutput startRecordingToOutputFileURL:destPath recordingDelegate:self];

    mTimer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(finishRecord:) userInfo:nil repeats:NO];
like image 931
Geuis Avatar asked Aug 23 '12 01:08

Geuis


2 Answers

If you need fine control over the bitrate of the MP4/MOV H.264 bit stream then you will need to use AVFoundation's AVAssetWriter. For the video input you would setup the properties like in this post. Take a look at the AVVideoAverageBitRateKey. Also note the AVVideoMaxKeyFrameIntervalKey key. If you plan on doing post production on the video then I recommend using 1 for the key frame interval. For an example of using AVAssetWriter check out the RosyWriter or AVCamDemo sample applications from Apple. Let me know if you would like more explanation.

like image 119
Steve McFarlin Avatar answered Oct 18 '22 23:10

Steve McFarlin


This can actually be done without using an AVAssetWriter. Here is some sample code.

for connection in movieFileOutput.connections {
     let settings = movieFileOutput.outputSettings(for: connection)
     var newSettings = settings
     var sub = newSettings[AVVideoCompressionPropertiesKey] as? [String: Any]
     sub![AVVideoAverageBitRateKey] = NSNumber(integerLiteral: 4000000)
     newSettings[AVVideoCompressionPropertiesKey] = sub!
     movieFileOutput.setOutputSettings(newSettings, for: connection)
  }

Of course you still need to determine that your AVCaptureConnection is your video connection. (You should also safely unwrap the optionals unlike my sample code)

like image 26
nitro805 Avatar answered Oct 19 '22 00:10

nitro805