From 344700f0c4a51495c49e2ec8f9ca3243b77b4a09 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Wed, 26 Aug 2026 15:35:17 +0200 Subject: [PATCH] fall back to the next usable camera instead of failing the qr scan on one reporting no resolutions --- .../sparrow/control/QRScanDialog.java | 13 +++--- .../sparrow/control/WebcamService.java | 41 +++++++++++-------- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/sparrowwallet/sparrow/control/QRScanDialog.java b/src/main/java/com/sparrowwallet/sparrow/control/QRScanDialog.java index 9e06c748..d43211b9 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/QRScanDialog.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/QRScanDialog.java @@ -133,7 +133,6 @@ public class QRScanDialog extends Dialog { List newDevices = new ArrayList<>(webcamService.getAvailableDevices()); newDevices.removeAll(foundDevices); foundDevices.addAll(newDevices); - foundDevices.removeIf(device -> !webcamService.getDevices().contains(device)); if(webcamService.getDevice() != null) { for(CaptureDevice device : foundDevices) { @@ -143,6 +142,8 @@ public class QRScanDialog extends Dialog { } } + foundDevices.removeIf(device -> !webcamService.getAvailableDevices().contains(device)); + updateList(availableResolutions, webcamService.getResolutions()); webcamResolutionProperty.set(webcamService.getResolution()); } finally { @@ -189,10 +190,12 @@ public class QRScanDialog extends Dialog { } }); webcamDeviceProperty.addListener((_, _, newValue) -> { - Config.get().setWebcamDevice(newValue.getName()); - Config.get().setWebcamDeviceId(newValue.getUniqueId()); - if(!Objects.equals(webcamService.getDevice(), newValue)) { - webcamService.cancel(); + if(newValue != null) { + Config.get().setWebcamDevice(newValue.getName()); + Config.get().setWebcamDeviceId(newValue.getUniqueId()); + if(!Objects.equals(webcamService.getDevice(), newValue)) { + webcamService.cancel(); + } } }); diff --git a/src/main/java/com/sparrowwallet/sparrow/control/WebcamService.java b/src/main/java/com/sparrowwallet/sparrow/control/WebcamService.java index 1c332a07..0aea0648 100644 --- a/src/main/java/com/sparrowwallet/sparrow/control/WebcamService.java +++ b/src/main/java/com/sparrowwallet/sparrow/control/WebcamService.java @@ -133,15 +133,24 @@ public class WebcamService extends ScheduledService { try { if(devices == null) { devices = capture.getDevices(); - availableDevices = new ArrayList<>(devices); + availableDevices = devices.stream().filter(d -> !d.getFormats().isEmpty()).collect(Collectors.toCollection(ArrayList::new)); + + List unsupportedDevices = devices.stream().filter(d -> d.getFormats().isEmpty()).map(CaptureDevice::getName).toList(); + if(!unsupportedDevices.isEmpty()) { + log.warn("Ignoring cameras with no supported resolutions: " + String.join(", ", unsupportedDevices)); + } if(devices.isEmpty()) { throw new UnsupportedOperationException("No cameras available"); } + + if(availableDevices.isEmpty()) { + throw new UnsupportedOperationException("No resolutions supported by cameras " + String.join(", ", unsupportedDevices)); + } } while(stream == null && !availableDevices.isEmpty()) { - CaptureDevice selectedDevice = availableDevices.stream().filter(d -> !d.getFormats().isEmpty()).findFirst().orElse(availableDevices.getFirst()); + CaptureDevice selectedDevice = availableDevices.getFirst(); if(device != null) { for(CaptureDevice webcam : availableDevices) { @@ -165,10 +174,6 @@ public class WebcamService extends ScheduledService { device = selectedDevice; - if(device.getFormats().isEmpty()) { - throw new UnsupportedOperationException("No resolutions supported by camera " + device.getName()); - } - List deviceFormats = new ArrayList<>(device.getFormats()); //On *nix prioritise supported camera pixel formats, preferring RGB3, then YUYV, then MJPG @@ -206,23 +211,27 @@ public class WebcamService extends ScheduledService { log.debug("Opening capture stream on " + device + " with format " + format.formatInfo().width() + "x" + format.formatInfo().height() + " (" + WebcamPixelFormat.fourCCToString(format.formatInfo().fourcc()) + ")"); } - opening.set(true); - stream = device.openStream(format); - opening.set(false); - try { - zoomLimits = stream.getPropertyLimits(CaptureProperty.ZOOM); - } catch(Throwable e) { - log.debug("Error getting zoom limits on " + device + ", assuming no zoom function"); + opening.set(true); + stream = device.openStream(format); + } catch(Exception e) { + log.warn("Error opening capture stream on " + device.getName() + ", trying next available camera", e); + availableDevices.remove(device); + } finally { + opening.set(false); } - if(stream == null) { - availableDevices.remove(device); + if(stream != null) { + try { + zoomLimits = stream.getPropertyLimits(CaptureProperty.ZOOM); + } catch(Throwable e) { + log.debug("Error getting zoom limits on " + device + ", assuming no zoom function"); + } } } if(stream == null) { - throw new UnsupportedOperationException("No usable cameras available, tried " + devices); + throw new UnsupportedOperationException("No usable cameras available, tried " + devices.stream().map(CaptureDevice::getName).collect(Collectors.joining(", "))); } opened.set(true);