On iOS 26, the first ProRes RAW recording after launching the app consistently stalls for the entire take: audio records normally, but video contains only a few frames (for example, an 11.8-second clip at approximately 0.25 fps instead of 30 fps). Every subsequent recording works correctly. Before startRecording, the Bayer format is active, ProRes RAW is available, white balance is locked, and frame duration is pinned to 1/30. Reordering setOutputSettings and reasserting configuration have not resolved it; only discarding the first recording acts as a reliable warm-up. Is this a known one-time ProRes RAW encoder initialisation issue, and is there a supported way to prepare the encoder before the first user takes?
I have a full working sample code if anyone needs but here is the setup function:
import CoreMedia
import CoreVideo
@available(iOS 26.0, *)
final class ProResRAWRecorder: NSObject,
AVCaptureFileOutputRecordingDelegate {
let session = AVCaptureSession()
private let movieOutput = AVCaptureMovieFileOutput()
private var camera: AVCaptureDevice!
// Call on a serial capture queue.
func configure() throws {
session.beginConfiguration()
defer { session.commitConfiguration() }
session.sessionPreset = .inputPriority
session.automaticallyConfiguresCaptureDeviceForWideColor = false
guard let device = AVCaptureDevice.default(
.builtInWideAngleCamera,
for: .video,
position: .back
) else {
throw SampleError.noCamera
}
let cameraInput = try AVCaptureDeviceInput(device: device)
guard session.canAddInput(cameraInput) else {
throw SampleError.cannotAddInput
}
session.addInput(cameraInput)
camera = device
// Optional, but reproduces the real topology where audio remains healthy.
if let microphone = AVCaptureDevice.default(for: .audio),
let microphoneInput = try? AVCaptureDeviceInput(device: microphone),
session.canAddInput(microphoneInput) {
session.addInput(microphoneInput)
}
// Find a 12-bit packed Bayer format supporting 30 fps.
guard let rawFormat = device.formats
.filter({
CMFormatDescriptionGetMediaSubType($0.formatDescription)
== kCVPixelFormatType_96VersatileBayerPacked12
&& $0.videoSupportedFrameRateRanges.contains {
$0.minFrameRate <= 30 && $0.maxFrameRate >= 30
}
})
.max(by: {
let a = CMVideoFormatDescriptionGetDimensions($0.formatDescription)
let b = CMVideoFormatDescriptionGetDimensions($1.formatDescription)
return Int(a.width) * Int(a.height)
< Int(b.width) * Int(b.height)
}) else {
throw SampleError.noRAWFormat
}
try device.lockForConfiguration()
device.activeFormat = rawFormat
if rawFormat.supportedColorSpaces.contains(.appleLog) {
device.activeColorSpace = .appleLog
}
let frameDuration = CMTime(value: 1, timescale: 30)
device.activeVideoMinFrameDuration = frameDuration
device.activeVideoMaxFrameDuration = frameDuration
if device.isWhiteBalanceModeSupported(.locked) {
device.whiteBalanceMode = .locked
}
device.unlockForConfiguration()
guard session.canAddOutput(movieOutput) else {
throw SampleError.cannotAddOutput
}
session.addOutput(movieOutput)
session.startRunning()
}
// Call on the same serial capture queue after startRunning() returns.
func record(to url: URL) throws {
guard let connection = movieOutput.connection(with: .video) else {
throw SampleError.noVideoConnection
}
// Reassert the required device state at the take boundary.
try camera.lockForConfiguration()
if camera.isWhiteBalanceModeSupported(.locked) {
camera.whiteBalanceMode = .locked
}
let frameDuration = CMTime(value: 1, timescale: 30)
camera.activeVideoMinFrameDuration = frameDuration
camera.activeVideoMaxFrameDuration = frameDuration
camera.unlockForConfiguration()
guard movieOutput.availableVideoCodecTypes.contains(.proResRAW) else {
throw SampleError.rawCodecUnavailable
}
movieOutput.setOutputSettings(
[AVVideoCodecKey: AVVideoCodecType.proResRAW],
for: connection
)
movieOutput.startRecording(to: url, recordingDelegate: self)
}
func stop() {
if movieOutput.isRecording {
movieOutput.stopRecording()
}
}
func fileOutput(
_ output: AVCaptureFileOutput,
didFinishRecordingTo outputFileURL: URL,
from connections: [AVCaptureConnection],
error: Error?
) {
print("Finished:", outputFileURL, "error:", error as Any)
}
enum SampleError: Error {
case noCamera
case cannotAddInput
case noRAWFormat
case cannotAddOutput
case noVideoConnection
case rawCodecUnavailable
}
}