package io.swtc.proccessing; import com.github.sarxos.webcam.Webcam; import com.github.sarxos.webcam.WebcamException; import com.github.sarxos.webcam.WebcamResolution; import java.awt.Dimension; import java.awt.image.BufferedImage; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.LockSupport; import java.util.function.Consumer; import javax.swing.*; import io.swtc.proccessing.ui.ShowError; public class WebcamCaptureLoop { private final Webcam webcam; private final Consumer onFrameCaptured; private volatile boolean running = false; private final AtomicBoolean cleanedUp = new AtomicBoolean(false); private final int targetframes = 20; public WebcamCaptureLoop(Webcam webcam, Consumer onFrameCaptured) { this.webcam = webcam; this.onFrameCaptured = onFrameCaptured; Dimension vga = WebcamResolution.VGA.getSize(); if (isSizeSupported(webcam, vga)) { // we dont do stupid shit anymore! cause we are smarter than before... webcam.setViewSize(vga); } else { Dimension[] dimensions = webcam.getViewSizes(); webcam.setViewSize(dimensions[dimensions.length - 1]); } } private boolean isSizeSupported(Webcam webcam, Dimension vga) { for (Dimension d: webcam.getViewSizes()) { if ( d.width == vga.width && d.height == vga.height ) return true; // this should return 640x480 :) } return false; } public void start() { if (running) return; running = true; Thread captureThread = new Thread(() -> { try { if (!webcam.isOpen()) webcam.open(); // open if not long frameDurationLimitns = 1_000_000_000L / targetframes; // we use NanoSeconds cause its more accurate while (running) { long startTime = System.nanoTime(); BufferedImage img = webcam.getImage(); if (img != null) // there is no need for a invoke later swing wise! onFrameCaptured.accept(img); long timespent = System.nanoTime() - startTime; long sleeptime = frameDurationLimitns - timespent; if (sleeptime > 0) { LockSupport.parkNanos(sleeptime); } else { // do a yield to prevent ui freezes Thread.yield(); } } } catch (Exception e) { ShowError.warning( null, "Exception" + e, "Exception" ); } finally { cleanup(); } }, "cam_cap_thread"); captureThread.start(); } /** * Safely release webcam */ private synchronized void cleanup() { if (!cleanedUp.compareAndSet(false, true)) { return; } boolean success = false; try { if (webcam.isOpen()) webcam.close(); success = true; } catch (WebcamException exception) { SwingUtilities.invokeLater(() -> JOptionPane.showMessageDialog( null, "Cleanup failed \nWebCamException@"+exception.getMessage(), "WebCamException", JOptionPane.WARNING_MESSAGE // changed to warning, its better tbh )); } finally { if (!success) cleanedUp.set(false); } } /** * Arguments: null (or just none) * What does this method do? : Sets CameraThreads running flag to false and calls cleanup(); */ public void stop() { running = false; } }