Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AVFileCaptureOutput: Not recording at 240 fps

I seem to be having a problem where I am setting the camera to record at 240 FPS, but for some reason the output file is only 30 FPS.

Here is my code for setting up the camera (this is instantiated first):

class HFRCamera {
public:
    HFRCamera();

    AVCaptureDeviceInput *camera;
    AVCaptureDeviceInput *microphone;
    AVCaptureDevice *videoCam;
    AVCaptureDevice *audioInput;
    AVCaptureSession *capSession;

    void start();
    void config();
    void stop();

};

 HFRCamera::HFRCamera() {

     // Set up capture session and add video camera and microphone
    this->capSession = [[AVCaptureSession alloc] init];
    this->videoCam = [AVCaptureDevice    defaultDeviceWithMediaType:AVMediaTypeVideo];
     this->config();


}

void HFRCamera::start() {
    [this->capSession startRunning];
}

void HFRCamera::stop() {
    [this->capSession stopRunning];
}

void HFRCamera::config() {

const CGFloat desiredFPS = 240;
AVCaptureDeviceFormat *selectedFormat = nil;
AVFrameRateRange *frameRateRange = nil;
int32_t maxWidth = 0;

for (AVCaptureDeviceFormat *format in [this->videoCam formats]) {

    for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {

        CMFormatDescriptionRef desc = format.formatDescription;
        CMVideoDimensions dimensions = CMVideoFormatDescriptionGetDimensions(desc);
        int32_t width = dimensions.width;

        if (range.minFrameRate <= desiredFPS && desiredFPS <= range.maxFrameRate && width >= maxWidth) {

            selectedFormat = format;
            frameRateRange = range;
            maxWidth = width;
        }
    }
}

if ([videoCam lockForConfiguration:nil]) {
    std::cout << "HERE\n";
    NSLog(@"selected format:%@", selectedFormat);
    this->videoCam.activeFormat = selectedFormat;
    this->videoCam.activeVideoMinFrameDuration = CMTimeMake(1, (int32_t)desiredFPS);
    this->videoCam.activeVideoMaxFrameDuration = CMTimeMake(1, (int32_t)desiredFPS);
    [this->videoCam unlockForConfiguration];

    NSLog(@"%s AVCaptureDevice: %@", __PRETTY_FUNCTION__, selectedFormat);
}



if (this->videoCam) {
    NSError *err;
    this->camera = [AVCaptureDeviceInput deviceInputWithDevice:this->videoCam error:&err];

    if (!err) {
        if ([this->capSession canAddInput:this->camera])
            [this->capSession addInput:this->camera];
        else
            NSLog(@"Could not add video input.");
    } else
        NSLog(@"Could not create video input");
} else {
    NSLog(@"Could not create video capture device.");
}

this->audioInput = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
NSError *err = nil;
this->microphone = [AVCaptureDeviceInput deviceInputWithDevice:this->audioInput error:&err];
if (this->microphone)
    [this->capSession addInput:this->microphone];

// Configure camera
[this->capSession setSessionPreset:AVCaptureSessionPresetHigh];

}

And finally, setting up the AVCameraFileOutput in ViewController:

// Configure the movie file output
self.movieFile = [[AVCaptureMovieFileOutput alloc] init];
self.movieFile.minFreeDiskSpaceLimit = 1024 * 1024;

CMTime maxDuration = CMTimeMakeWithSeconds(60*60, 240); // 1 hour at 240 fps should be more than enough
self.movieFile.maxRecordedDuration = maxDuration;
if ([self.camera.capSession canAddOutput:self.movieFile])
    [self.camera.capSession addOutput:self.movieFile];

Where am I going wrong?

EDIT: This is the output from ffprobe on the resulting file.

Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p(tv, bt709), 1920x1080, 15166 kb/s, 30 fps, 30 tbr, 600 tbn, 1200 tbc (default)

But yet, this is what the activeFormat supposedly is:

AVCaptureDevice: <AVCaptureDeviceFormat: 0x17401f780 'vide'/'420f' 1280x 720, { 6-240 fps}, fov:59.680, binned, supports vis, max zoom:65.50 (upscales @1.45), AF System:1, ISO:22.0-704.0, SS:0.000002-0.166667, supports wide color>

like image 919
NOP Avatar asked Dec 12 '16 20:12

NOP


2 Answers

There are two, largely incompatible, ways to configure a capture session: session presets and device formats. Presets are presets, formats are formats, and never the twain shall meet.

When you setActiveFormat on your capture device, that (and any further customizations like min/max frame duration) overrides any settings that came from whatever session preset was previously set. In this situation, the session preset changes to AVCaptureSessionPresetInputPriority to indicate that the session's settings are no longer controlling everything.

If, after setting an active format on the device (and further customizing device settings), you call setSessionPreset on the capture session, the new preset overrides/undoes the device format settings. That's what you appear to be doing. Since you've already set and configured a device format, you shouldn't need to use a session preset at all — just omit the setSessionPreset call at the end of your config function.


For more details: the best overview I've seen on session presets vs device formats is this video from WWDC13, when the device format API was introduced. (Even though that video predates a lot of the features, like high-FPS/slow-mo recording, that require configuring via activeFormat instead of sessionPreset.)

(Wow, two questions I can answer with the same link within such a short time that it's still on my clipboard.)


Also, be aware that your loop for finding a desired format might not always get the one you want, since it's choosing the first in the AVCaptureDevice.formats array that matches your desired fps and width. For example, depending on whether/how you're processing sample buffers you might care whether you're getting the 420f or 420v format, and depending on what you're doing with output files you might care about whether wide-color-capable devices (like iPhone 7 and iPad Pro 9.7-inch) are capturing in sRGB or P3. Take a look at the full list of camera features to make sure you're getting what you want across multiple devices.

like image 120
rickster Avatar answered Oct 18 '22 09:10

rickster


I ended up answering my own question.

In addition to using a preset, I discovered that in order to set the camera configuration, I need to add the camera to the capture session, configure it and then start the capture session immediately. Whereas, I was adding the camera to the capture session before I had configured it, which doesn't seem to cause the configuration to be committed.

Relevant iOS documentation: https://developer.apple.com/reference/avfoundation/avcapturedevice/1387810-lockforconfiguration?language=objc

like image 1
NOP Avatar answered Oct 18 '22 10:10

NOP