Set a custom AVFrameRateRange for an AVCaptureSess

2019-04-10 06:29发布

I'm trying to take 5 pictures every second with AVCaptureSession and I'm not sure I understand what AVFrameRange means. Currently I have some code that sets up the device:

AVCaptureDevice *device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

and tries to set the activeVideoMinFrameDuration and activeVideoMaxFrameDuration to a custom value of CMTimeMake(1, 5). Apple tells me I can only use one of the AVFrameRanges that they've provided.

When I NSLogged them, I get (2, 30), (2,60), and (2,24). I first want to know what this means? Is this the frame rate at which the camera will run or an interval for capturing frames (i.e. what I'm trying to do)?

If it isn't, what can I do to save 5 frames every second on my sampleBufferDelegate method? Currently it gives me every single frame because the method is called every single time there is a frame, so I just need some pointer on how I can grab just 5 at each second.

2条回答
混吃等死
2楼-- · 2019-04-10 06:52

Here is working code we have used that sets the frame rate at 5 per second.

If you measure calls to CaptureOutput while using this code, you can see that the camera frames are called every 200 msecs (i.e. which is 5 frames per second.) (We just tested this to confirm.)

Change desiredFrameRate to get other camera frame rates.

- (void)attemptToConfigure5FPS
{
    NSError *error;
    if (![self lockForConfiguration:&error]) {
        NSLog(@"Could not lock device %@ for configuration: %@", self, error);
        return;
    }

    AVCaptureDeviceFormat *format = self.activeFormat;
    double epsilon = 0.00000001;

    int desiredFrameRate = 5;

    for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {

            if (range.minFrameRate <= (desiredFrameRate + epsilon) &&
            range.maxFrameRate >= (desiredFrameRate - epsilon)) {

            self.activeVideoMaxFrameDuration = (CMTime){
                .value = 1,
                .timescale = desiredFrameRate,
                .flags = kCMTimeFlags_Valid,
                .epoch = 0,
            };
            self.activeVideoMinFrameDuration = (CMTime){
                .value = 1,
                .timescale = desiredFrameRate,
                .flags = kCMTimeFlags_Valid,
                .epoch = 0,
            };           
            break;
        }
    }

    [self unlockForConfiguration];
}
查看更多
迷人小祖宗
3楼-- · 2019-04-10 06:59

Code for choosing custom frame rate is as below - Added checks to Apple RosyWriter to verify if current format supports FPS chosen

- (void)configureCamera:(AVCaptureDevice *)videoDevice withFrameRate:(int)desiredFrameRate
{
    BOOL isFPSSupported = NO;
    AVCaptureDeviceFormat *currentFormat = [videoDevice activeFormat];
    for ( AVFrameRateRange *range in currentFormat.videoSupportedFrameRateRanges ) {
        if ( range.maxFrameRate >= desiredFrameRate && range.minFrameRate <= desiredFrameRate )        {
            isFPSSupported = YES;
            break;
        }
    }

    if( isFPSSupported ) {
        if ( [videoDevice lockForConfiguration:NULL] ) {
            videoDevice.activeVideoMaxFrameDuration = CMTimeMake( 1, desiredFrameRate );
            videoDevice.activeVideoMinFrameDuration = CMTimeMake( 1, desiredFrameRate );
            [videoDevice unlockForConfiguration];
        }
    }
}

In case current format (activeFormat) didn't support your chosen FPS, use below code to change activeFormat and then chose FPS. Will need to get format dimension to match your needs though.

- (void)configureCamera:(AVCaptureDevice *)device withFrameRate:(int)desiredFrameRate
{
    AVCaptureDeviceFormat *desiredFormat = nil;
    for ( AVCaptureDeviceFormat *format in [device formats] ) {
        for ( AVFrameRateRange *range in format.videoSupportedFrameRateRanges ) {
            if ( range.maxFrameRate >= desiredFrameRate && range.minFrameRate <= desiredFrameRate ) {
                desiredFormat = format;
                goto desiredFormatFound;
            }
        }
    }

    desiredFormatFound:
    if ( desiredFormat ) {
        if ( [device lockForConfiguration:NULL] == YES ) {
            device.activeFormat = desiredFormat ;
            device.activeVideoMinFrameDuration = CMTimeMake ( 1, desiredFrameRate );
            device.activeVideoMaxFrameDuration = CMTimeMake ( 1, desiredFrameRate );
            [device unlockForConfiguration];
        }
    }
}

Note: Usage of AVCaptureConnection videoMinFrameDuration to set FPS is deprecated.

查看更多
登录 后发表回答