From 98b510dcd2d76d9e7ac6c556fb1c12798289718d Mon Sep 17 00:00:00 2001 From: don4325 Date: Fri, 16 Oct 2026 12:11:45 -0600 Subject: [PATCH] fix: Encode with a compressed codec when `targetBitRate` is set on iOS `HybridCameraVideoOutput.setBitRate` read the connection's current codec and applied the bit-rate to it. On formats whose default movie codec is ProRes (the 10-bit 4:2:2 formats of Pro iPhones) the connection does not list `AVVideoCompressionPropertiesKey`, so the guard bailed, the bit-rate was dropped silently and the recorder kept writing ProRes. Observed on an iPhone 17 Pro Max (iOS 27.0.1): the rear camera wrote 33.1 MB in 1.03 s (~257 Mbps) until `maxFileSize` stopped the take. Resolve the codec first: keep the current codec when it is already a compressed one the output offers, otherwise move to HEVC, else H.264. Apply that codec on its own before reading `supportedOutputSettingsKeys`, because the supported keys are reported for the connection's current codec. Co-Authored-By: Claude Fable 5.1 --- .../Outputs/HybridCameraVideoOutput.swift | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/packages/react-native-vision-camera/ios/Hybrid Objects/Outputs/HybridCameraVideoOutput.swift b/packages/react-native-vision-camera/ios/Hybrid Objects/Outputs/HybridCameraVideoOutput.swift index 03534524fa..8e46ec6fd2 100644 --- a/packages/react-native-vision-camera/ios/Hybrid Objects/Outputs/HybridCameraVideoOutput.swift +++ b/packages/react-native-vision-camera/ios/Hybrid Objects/Outputs/HybridCameraVideoOutput.swift @@ -141,10 +141,37 @@ final class HybridCameraVideoOutput: HybridCameraVideoOutputSpec, NativeCameraOu let currentSettings = output.outputSettings(for: connection) let currentCodec = currentSettings[AVVideoCodecKey] as? AVVideoCodecType ?? AVVideoCodecType.hevc - setOutputSettings(bitRate: bitRate, codec: currentCodec, for: connection) + // A `targetBitRate` is a request for a compressed codec. On a format whose + // default movie codec is ProRes (the 10-bit 4:2:2 formats of Pro iPhones) + // the connection reports that codec and does not list compression + // properties, so applying the bit-rate to the current codec silently keeps + // ProRes and the recorder writes ~250 Mbps. Keep HEVC or H.264 when the + // connection already holds one; otherwise move to the first the output + // offers (HEVC, else H.264). With neither, leave the connection untouched: + // setting a codec the output does not offer raises. + guard let codec = compressedCodec(preferring: currentCodec) else { + logger.error("No compressed video codec is available - cannot use custom bit-rate!") + return + } + setOutputSettings(bitRate: bitRate, codec: codec, for: connection) + } + + private func compressedCodec(preferring currentCodec: AVVideoCodecType) -> AVVideoCodecType? { + // An allow-list, not a ProRes deny-list: any other codec (one the converter + // does not know, or JPEG) would drop the bit-rate the same way ProRes did. + let compressed: [AVVideoCodecType] = [.hevc, .h264] + let available = output.availableVideoCodecTypes + if compressed.contains(currentCodec) && available.contains(currentCodec) { + return currentCodec + } + return compressed.first { available.contains($0) } } private func setOutputSettings(bitRate: Int, codec: AVVideoCodecType, for connection: AVCaptureConnection) { + // Apply the codec on its own first: the supported keys are reported for the + // connection's current codec, and a ProRes connection does not list + // compression properties until it has been moved to a compressed codec. + setOutputSettings(codec: codec, for: connection) let supportedKeys = output.supportedOutputSettingsKeys(for: connection) guard supportedKeys.contains(AVVideoCompressionPropertiesKey) else { logger.error(