From a69d096d15b47b520fde687051412354cf6784cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=27=C3=A9lectron=20rare?= <108685187+electron-rare@users.noreply.github.com> Date: Wed, 13 May 2026 12:01:14 +0200 Subject: [PATCH] fix(camera): strict built-in filter both sides Both the Python AVCapture path and the Swift CameraPreviewLayer now enforce the same two-step filter : 1. deviceType == .builtInWideAngleCamera 2. localizedName must NOT contain 'iPhone', 'GSM', 'Desk View', or 'Continuity' The second clause matters because under some macOS configurations (Continuity Camera promoted to widescreen, iPhone mirroring), externally tethered phone cameras can advertise themselves with the same primary deviceType. Swift side also logs via NSLog so the picked device shows up in Console.app even when stdout is buffered. --- data_only_viz/_av_capture.py | 17 +++++++++-- .../AVLiveBody/CameraPreviewLayer.swift | 29 ++++++++++++++++--- 2 files changed, 39 insertions(+), 7 deletions(-) diff --git a/data_only_viz/_av_capture.py b/data_only_viz/_av_capture.py index 7d34e5b..90c2662 100644 --- a/data_only_viz/_av_capture.py +++ b/data_only_viz/_av_capture.py @@ -69,11 +69,22 @@ def enumerate_devices() -> list[dict]: return out +_BANNED_NAME_TOKENS = ("iphone", "gsm", "desk view", "continuity") + + def find_builtin_device() -> Optional[dict]: - """Selectionne le BuiltInWideAngleCamera (webcam Mac integree).""" + """Selectionne la webcam Mac integree : + - deviceType doit etre BuiltInWideAngleCamera + - le nom ne doit contenir aucun de _BANNED_NAME_TOKENS + Evite les pieges Continuity/iPhone/Desk View qui peuvent matcher + BuiltInWideAngleCamera dans certaines configs.""" for info in enumerate_devices(): - if "BuiltInWideAngleCamera" in info["type"]: - return info + if "BuiltInWideAngleCamera" not in info["type"]: + continue + name_l = info["name"].lower() + if any(tok in name_l for tok in _BANNED_NAME_TOKENS): + continue + return info return None diff --git a/launcher/AV-Live-Body/Sources/AVLiveBody/CameraPreviewLayer.swift b/launcher/AV-Live-Body/Sources/AVLiveBody/CameraPreviewLayer.swift index 773e727..f77b50d 100644 --- a/launcher/AV-Live-Body/Sources/AVLiveBody/CameraPreviewLayer.swift +++ b/launcher/AV-Live-Body/Sources/AVLiveBody/CameraPreviewLayer.swift @@ -17,14 +17,36 @@ final class CameraPreviewLayer { } func start() -> Bool { + // On enumere TOUS les types puis on filtre durement : nom ne + // contient ni 'iPhone', ni 'GSM', ni 'Desk View', ni + // 'Continuity' (cas Continuity Camera 13+) et deviceType == + // builtInWideAngleCamera. Ca evite tous les pieges. let discovery = AVCaptureDevice.DiscoverySession( - deviceTypes: [.builtInWideAngleCamera], + deviceTypes: [.builtInWideAngleCamera, + .continuityCamera, + .external, + .deskViewCamera], mediaType: .video, position: .unspecified) - guard let device = discovery.devices.first else { - print("No built-in camera found") + NSLog("AV-Live-Body: %d cameras detected", discovery.devices.count) + for d in discovery.devices { + NSLog("AV-Live-Body: - %@ type=%@", + d.localizedName, d.deviceType.rawValue) + } + let banned = ["iPhone", "GSM", "Desk View", "Continuity"] + let device = discovery.devices.first { d in + guard d.deviceType == .builtInWideAngleCamera else { return false } + let name = d.localizedName + for b in banned where name.localizedCaseInsensitiveContains(b) { + return false + } + return true + } + guard let device = device else { + NSLog("AV-Live-Body: no Mac built-in camera found") return false } + NSLog("AV-Live-Body: picked '%@'", device.localizedName) do { let input = try AVCaptureDeviceInput(device: device) guard session.canAddInput(input) else { @@ -40,7 +62,6 @@ final class CameraPreviewLayer { DispatchQueue.global(qos: .userInitiated).async { [weak self] in self?.session.startRunning() } - print("CameraPreviewLayer running on '\(device.localizedName)'") return true }