Skip to content

fix(macos): select format and frame-rate range from the same AVCaptureDeviceFormat - #248

Open
kasper-cancilico wants to merge 1 commit into
l1npengtul:0.10from
kasper-cancilico:fix-avfoundation-format-range-mismatch
Open

fix(macos): select format and frame-rate range from the same AVCaptureDeviceFormat#248
kasper-cancilico wants to merge 1 commit into
l1npengtul:0.10from
kasper-cancilico:fix-avfoundation-format-range-mismatch

Conversation

@kasper-cancilico

Copy link
Copy Markdown

Fixes #247.

Problem

AVCaptureDevice::set_all overwrites selected_format for every format matching the requested resolution, while selected_range may come from an earlier format. Applying a frame-rate range that does not belong to the new activeFormat makes AVFoundation throw NSInvalidArgumentException, which cannot unwind through the objc catch and aborts the process instead of returning Err. Cameras exposing several formats at one resolution (e.g. Logitech UVC devices: 420v + MJPEG variants) hit this deterministically in Camera::new().

Change

  • Pick selected_format and selected_range together from the same AVCaptureDeviceFormat, breaking at the first pair that satisfies the requested fps (keeping the existing 0.999 tolerance for 29.97-style rates).
  • Fall back to the first resolution-matching format with its first supported range when no fps match exists, instead of leaving a null/mismatched pair.
  • lock(): set self.locked = true after a successful lockForConfiguration so unlock() (guarded by self.locked) actually calls unlockForConfiguration. Previously the flag was never set and the device stayed configuration-locked. This changes lock(&self) to lock(&mut self); the only in-tree caller (set_all) already takes &mut self.

Testing

  • cargo check on nokhwa-bindings-macos (macOS 26, Apple Silicon): compiles; no new warnings.
  • Hardware: Logitech USB webcam (VID 0x046D) that reproducibly aborted on Camera::new() with RequestedFormatType::AbsoluteHighestResolution now opens at 1280x960, streams frames continuously, and delivers full-resolution stills. A MacBook Air built-in camera and an iPhone Continuity camera still enumerate as before (open not re-tested on those).

🤖 Generated with Claude Code

…eDeviceFormat

AVCaptureDevice::set_all kept overwriting selected_format for every
format matching the requested resolution while selected_range could
come from an earlier format. Applying a frame-rate range that does not
belong to the active format makes AVFoundation throw
NSInvalidArgumentException, which cannot unwind through the objc catch
and aborts the process. Cameras exposing several formats at the same
resolution (e.g. Logitech UVC devices with 420v and MJPEG variants)
hit this deterministically on open.

Select the format and range from the same AVCaptureDeviceFormat,
stop at the first satisfying pair, and fall back consistently to the
first resolution-matching format with its first range. Also record the
acquired configuration lock in lock() so unlock() actually releases it
(self.locked was never set, so unlockForConfiguration was never
called).
@akx

akx commented Jul 29, 2026

Copy link
Copy Markdown

I implemented the same thing in 066c85a (#246) 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants