The simpler way to record a video on iOS is by setting a AVCaptureSession.sessionPreset
.
But that doesn't work for me since I want to control parameters like binning, stabilization (cinematic, standard, or none) and ISO.
I find the format I want and assign it to activeFormat
, but when I try to start recording, I get an error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason:
'*** -[AVCaptureMovieFileOutput startRecordingToOutputFileURL:recordingDelegate:] No active/enabled connections'
Here is my initialisation code:
let device = AVCaptureDevice.defaultDevice(
withDeviceType: .builtInWideAngleCamera,
mediaType: AVMediaTypeVideo,
position: .back)!
let session = AVCaptureSession()
session.addInput(try! AVCaptureDeviceInput(device: device))
output = AVCaptureMovieFileOutput()
session.addOutput(output)
device.setFormatWithHighestIso()
session.startRunning()
setFormatWithHighestIso()
is defined as:
extension AVCaptureDevice {
var goodVideoFormats: [AVCaptureDeviceFormat] {
return (formats as! [AVCaptureDeviceFormat])
.filter { CMFormatDescriptionGetMediaSubType($0.formatDescription) != 875704422 } // 420f
.filter { $0.autoFocusSystem == .phaseDetection }
}
func setFormatWithHighestIso() {
let format = goodVideoFormats
.filter { $0.maxISO > 1759 }
.filter { $0.height < 1937 }
.first!
try! lockForConfiguration()
defer { unlockForConfiguration() }
activeFormat = format
NSLog("\(format)")
}
}
The last log statement produces:
<AVCaptureDeviceFormat: 0x1702027d0 'vide'/'420f' 2592x1936, { 3- 30 fps}, HRSI:4032x3024, fov:58.986, max zoom:189.00 (upscales @1.56), AF System:2, ISO:22.0-1760.0, SS:0.000005-0.333333, supports wide color>
This is indeed the format I want, so setFormatWithHighestIso()
is working as expected. See the Apple reference.
Some other things I tried:
- Using 420v instead of 420f, by changing the == 875704422 to !=.
- Instead of starting the camera in photo mode, starting it in video mode, and then changing it to video mode by removing the AVCapturePhotoOutput and adding the AVCaptureMovieFileOutput.
- Verifying that the AVCaptureConnection is enabled, and it is.
Verifying that the connection is active, but it's not:
let conn = output.connection(withMediaType: AVMediaTypeVideo)! verify(conn.isActive)
I also tried using some other AVCaptureDeviceFormats
, and they work:
extension AVCaptureDevice {
func setFormatWithCinematicVS() {
let format = goodVideoFormats
.filter { $0.isVideoStabilizationModeSupported(.cinematic) }
.filter { $0.height == 720 }
.first!
try! lockForConfiguration()
defer { unlockForConfiguration() }
activeFormat = format
}
func setFormatWithStandardVS() {
let format = goodVideoFormats
.filter { $0.isVideoStabilizationModeSupported(.standard) }
.filter { $0.height == 540 }
.first!
try! lockForConfiguration()
defer { unlockForConfiguration() }
activeFormat = format
}
}
It's only the format with the highest ISO that doesn't work. What's special about this format?
Do I need to manually create an AVCaptureConnection? But there's already a connection; it's just not active.
This is on the iPhone 7 Plus running iOS 10.3.3. How do I record video in a specific format by setting the activeFormat without using a session?
If, instead of assigning to activeFormat, I use a sessionPreset, it does record a video successfully.
There are other questions talking of this error message, but this isn't a dupe of them since I specifically need to capture video without using a preset.
The solution turned out to be configuring the AVCaptureDevice before adding it to a session. Instead of:
You should do:
When the device is added to the session, an AVCaptureConnection is created and configured in a certain way. If you change the device's resolution later, the configuration no longer matches, so the connection gets deactivated, and the video won't record.