How to record timelapse video from iPhone using AVCaptureDeviceFormat?
Asked Answered
P

1

1

I am trying to record a timelapse video via iphone. I have already got it working for slow motion (by capturing maximum frames possible), with 120 fps.

Now I am trying to reverse the logic to capture least frames possible, to achieve timelapse functionality.

Code flow is like this:

  1. Query all the supported frame range from the available device formats of the AVCaptureDevice.

  2. Check if the frame rate is below or equal to the desired 20 fps and dimensions are equal to or greater than 1920*1080.

All works fine except, when I press record, I get "AVCaptureMovieFileOutput - no active/enabled connections" exception. I am only using one of the provided frame rate and range, why am I getting this exception?

All works well for 30 fps.

 #define kTimelapseRequiredFPS = 20

#define kMinRequiredDimensions (1920*1080)

        - (void)configureCameraForSlowMoRecording:(AVCaptureDevice *)currentCaptureDevice
        {
            [_captureSession beginConfiguration];

            AVCaptureDeviceFormat *bestFormat = nil;

            AVFrameRateRange *bestFrameRateRange = nil;

            for ( AVCaptureDeviceFormat *format in [currentCaptureDevice formats] )
            {
                //NSLog(@"Format: %@", format);

                CMFormatDescriptionRef videoInfo = [format formatDescription];

                double videoWidth = CMVideoFormatDescriptionGetDimensions(videoInfo).width;

                double videoHeight = CMVideoFormatDescriptionGetDimensions(videoInfo).height;

                double dimensions = videoWidth * videoHeight;

                for ( AVFrameRateRange *range in format.videoSupportedFrameRateRanges )
                {
                    //NSLog(@"Range: %@", range);

                    if ((range.maxFrameRate <= kTimelapseRequiredFPS) && (dimensions >= kMinRequiredDimensions))
                    {
                        bestFormat = format;

                        bestFrameRateRange = range;
                    }
                }
            }

            if ( bestFormat )
            {
                NSLog(@"Final format: %@, Final range %@", bestFormat, bestFrameRateRange);

                if ( [currentCaptureDevice lockForConfiguration:NULL] == YES )
                {
                    currentCaptureDevice.activeFormat = bestFormat;

                    currentCaptureDevice.activeVideoMinFrameDuration = bestFrameRateRange.minFrameDuration;

                    currentCaptureDevice.activeVideoMaxFrameDuration = bestFrameRateRange.minFrameDuration;

                    [currentCaptureDevice unlockForConfiguration];
                }
            }

            [_captureSession commitConfiguration];
        }

Here is the log of frame rate and range with 20 fps:

Format: <AVCaptureDeviceFormat: 0x1765f7a0 'vide'/'420v' 2592x1936, { 1- 20 fps}, fov:56.700, max zoom:153.00 (upscales @1.26), AF System:1, ISO:46.0-736.0, SS:0.000018-1.000000>
Range: <AVFrameRateRange: 0x176556d0 1 - 20>

Format: <AVCaptureDeviceFormat: 0x1765f750 'vide'/'420f' 2592x1936, { 1- 20 fps}, fov:56.700, max zoom:153.00 (upscales @1.26), AF System:1, ISO:46.0-736.0, SS:0.000018-1.000000>
Range: <AVFrameRateRange: 0x1750db10 1 - 20>

Format: <AVCaptureDeviceFormat: 0x1765f740 'vide'/'420v' 3264x2448, { 1- 20 fps}, fov:56.700, max zoom:153.00 (upscales @1.00), AF System:1, ISO:46.0-736.0, SS:0.000018-1.000000>
Range: <AVFrameRateRange: 0x1750dc80 1 - 20>

Format: <AVCaptureDeviceFormat: 0x1765f6f0 'vide'/'420f' 3264x2448, { 1- 20 fps}, fov:56.700, max zoom:153.00 (upscales @1.00), AF System:1, ISO:46.0-736.0, SS:0.000018-1.000000>
Range: <AVFrameRateRange: 0x1751a260 1 - 20>
Polybius answered 1/7, 2017 at 12:22 Comment(0)
E
0

Not sure if this is solved, but I was struggling in making slow motion to work, and kept getting "AVCaptureMovieFileOutput - no active/enabled connections" too (so I really wonder how you get it to work :D ). But anyways I solved it, and here are some tricky stuff that I think might as well solve your problem:

  1. Make sure that you set some preset before setting activeFormat. Turns out iOS won't just handle other stuff automatically. What I did is set AVCaptureSession.Preset.hd1280x720 before setting the activeFormat to the one with 120fps.
  2. After I did the above, I started to get Error Domain=AVFoundationErrorDomain Code=-11803 "Cannot Record" UserInfo=0x152e60 {NSLocalizedRecoverySuggestion=Try recording again., AVErrorRecordingSuccessfullyFinishedKey=false, NSLocalizedDescription=Cannot Record}. I couldn't find any possible cause - the connection was fine, the session was running. So I pressed the record button again, and it starts to record (Wow, really? Just literally "try again"?) I guess iOS's just got some temper and has to think a little bit longer to record. So I added a recursive function to give movieFileOutput some time to record. (Sorry it's in Swift)

    fileprivate func startRecording(to moviePath: URL) {
        sessionQueue.async {
            self.movieFileOutput.startRecording(to: moviePath, recordingDelegate: self)
    
            if self.movieFileOutput.isRecording {
                self.sessionQueue.asyncAfter(deadline: .now() + self.duration) {
                    self.stopRecording()
                }
            } else {
                self.startRecording(to: moviePath)
            }
        }
    }
    

Hope it'll help (or maybe others who just stumble on this thread just like I did.)

Evelinevelina answered 11/6, 2018 at 13:10 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.