How to record timelapse video from iPhone using AVCaptureDeviceFormat?

南笙酒味 提交于 2019-12-12 03:27:24

问题


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>

回答1:


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.)



来源:https://stackoverflow.com/questions/44860899/how-to-record-timelapse-video-from-iphone-using-avcapturedeviceformat

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!