From b1f7e6d6f2ef179e9bc28bd9f0db3ce050090030 Mon Sep 17 00:00:00 2001 From: Yuhao Date: Thu, 8 May 2025 10:57:03 +0800 Subject: [PATCH] [cscore] Resolve macOS camera freeze with specific devices (#7960) Addresses an issue where certain USB cameras, specifically the ArduCam OV9281, would freeze when attempting to stream on macOS. The previous logic started the AVCaptureSession (startRunning) before locking the device for configuration (lockForConfiguration). While this works for many cameras, it causes the OV9281 to become unresponsive. Further investigation revealed: - Moving startRunning to after unlockForConfiguration resulted in macOS overriding the custom format and frame rate settings applied within the lock. - The reliable solution, inspired by findings shared in the community (e.g., Stack Overflow), is to lock the device, apply the configuration, start the session, and then unlock the device. This commit reorders the operations within deviceStreamOn in UsbCameraImplObjc.mm to follow the sequence: lockForConfiguration -> apply settings -> startRunning -> unlockForConfiguration. This ensures the desired camera configuration is applied correctly without causing device freezes on problematic hardware like the OV9281. --- .../main/native/objcpp/UsbCameraImplObjc.mm | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/cscore/src/main/native/objcpp/UsbCameraImplObjc.mm b/cscore/src/main/native/objcpp/UsbCameraImplObjc.mm index 1db2ccbc30..58f5dde90c 100644 --- a/cscore/src/main/native/objcpp/UsbCameraImplObjc.mm +++ b/cscore/src/main/native/objcpp/UsbCameraImplObjc.mm @@ -905,24 +905,32 @@ static cs::VideoMode::PixelFormat FourCCToPixelFormat(FourCharCode fourcc) { if (!self.deviceValid) { return false; } - self.streaming = true; + if (![self.videoDevice lockForConfiguration:nil]) { + OBJCERROR("Failed to lock for configuration"); + return false; + } + + [self.session beginConfiguration]; + + if (self.currentFormat != nil) { + self.videoDevice.activeFormat = self.currentFormat; + } + + if (self.currentFPS != 0) { + CMTime frameDuration = [self findNearestFrameDuration:self.currentFPS]; + self.videoDevice.activeVideoMinFrameDuration = frameDuration; + self.videoDevice.activeVideoMaxFrameDuration = frameDuration; + } + + [self.session commitConfiguration]; + + self.streaming = true; + // Start the capture session before device unlock to ensure + // the session preset settings are preserved [self.session startRunning]; - if ([self.videoDevice lockForConfiguration:nil]) { - if (self.currentFormat != nil) { - self.videoDevice.activeFormat = self.currentFormat; - } - if (self.currentFPS != 0) { - self.videoDevice.activeVideoMinFrameDuration = - [self findNearestFrameDuration:self.currentFPS]; - self.videoDevice.activeVideoMaxFrameDuration = - [self findNearestFrameDuration:self.currentFPS]; - } - [self.videoDevice unlockForConfiguration]; - } else { - OBJCERROR("Failed to lock for configuration"); - } + [self.videoDevice unlockForConfiguration]; return true; }