diff --git a/webcam-capture/src/example/java/com/github/sarxos/webcam/ConcurrentThreadsExample.java b/webcam-capture/src/example/java/com/github/sarxos/webcam/ConcurrentThreadsExample.java index 4a9df16d..23323362 100644 --- a/webcam-capture/src/example/java/com/github/sarxos/webcam/ConcurrentThreadsExample.java +++ b/webcam-capture/src/example/java/com/github/sarxos/webcam/ConcurrentThreadsExample.java @@ -1,7 +1,10 @@ package com.github.sarxos.webcam; +import java.awt.image.BufferedImage; import java.util.concurrent.atomic.AtomicInteger; +import com.github.sarxos.webcam.log.WebcamLogConfigurator; + public class ConcurrentThreadsExample { @@ -17,17 +20,32 @@ public Capture() { @Override public void run() { + + Webcam webcam = Webcam.getDefault(); + webcam.open(); + while (true) { - Webcam.getDefault().getImage(); - int value = counter.incrementAndGet(); - if (value != 0 && value % 10 == 0) { - System.out.println(Thread.currentThread().getName() + ": Frames captured: " + value); + + if (!webcam.isOpen()) { + break; + } + + BufferedImage image = webcam.getImage(); + if (image == null) { + break; + } + + int n = counter.incrementAndGet(); + if (n != 0 && n % 10 == 0) { + System.out.println(Thread.currentThread().getName() + ": Frames captured: " + n); } } } } - public static void main(String[] args) { + public static void main(String[] args) throws Throwable { + + WebcamLogConfigurator.configure("src/example/resources/logback.xml"); /** * This example will start several concurrent threads which use single @@ -39,5 +57,9 @@ public static void main(String[] args) { System.out.println("Thread: " + i); new Capture().start(); } + + Thread.sleep(10000); + + System.exit(1); } } diff --git a/webcam-capture/src/example/java/com/github/sarxos/webcam/CustomResolutionExample.java b/webcam-capture/src/example/java/com/github/sarxos/webcam/CustomResolutionExample.java new file mode 100644 index 00000000..4d66e310 --- /dev/null +++ b/webcam-capture/src/example/java/com/github/sarxos/webcam/CustomResolutionExample.java @@ -0,0 +1,27 @@ +package com.github.sarxos.webcam; + +import java.awt.Dimension; +import java.awt.image.BufferedImage; + + +public class CustomResolutionExample { + + public static void main(String[] args) { + + Dimension[] nonStandardResolutions = new Dimension[] { + WebcamResolution.PAL.getSize(), + WebcamResolution.HD720.getSize(), + new Dimension(2000, 1000), + new Dimension(1000, 500), + }; + + Webcam webcam = Webcam.getDefault(); + webcam.setCustomViewSizes(nonStandardResolutions); + webcam.open(); + + BufferedImage image = webcam.getImage(); + + System.out.println(image.getWidth() + "x" + image.getHeight()); + } + +} diff --git a/webcam-capture/src/example/java/com/github/sarxos/webcam/TakePictureDifferentSizeExample.java b/webcam-capture/src/example/java/com/github/sarxos/webcam/TakePictureDifferentSizeExample.java index 9c4d1977..2cad5c07 100644 --- a/webcam-capture/src/example/java/com/github/sarxos/webcam/TakePictureDifferentSizeExample.java +++ b/webcam-capture/src/example/java/com/github/sarxos/webcam/TakePictureDifferentSizeExample.java @@ -17,8 +17,8 @@ public class TakePictureDifferentSizeExample { public static void main(String[] args) throws IOException { Webcam webcam = Webcam.getDefault(); - webcam.setViewSize(new Dimension(176, 144)); webcam.open(); + webcam.setViewSize(new Dimension(1024, 768)); ImageIO.write(webcam.getImage(), "PNG", new File("test.png")); webcam.close(); } diff --git a/webcam-capture/src/example/resources/logback.xml b/webcam-capture/src/example/resources/logback.xml index c26128c3..445d1112 100644 --- a/webcam-capture/src/example/resources/logback.xml +++ b/webcam-capture/src/example/resources/logback.xml @@ -1,12 +1,10 @@ - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - - + diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/Webcam.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/Webcam.java index aa956945..87c67318 100644 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/Webcam.java +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/Webcam.java @@ -52,21 +52,22 @@ public class Webcam { */ private static final class ShutdownHook extends Thread { + private static int number = 0; + /** * Webcam instance to be disposed / closed. */ private Webcam webcam = null; public ShutdownHook(Webcam webcam) { + super("shutdown-hook-" + (++number)); this.webcam = webcam; } @Override public void run() { LOG.info("Automatic {} deallocation", webcam.getName()); - super.run(); webcam.dispose(); - webcam.close0(); } } @@ -134,6 +135,7 @@ protected Webcam(WebcamDevice device) { private void ensureSize() { Dimension size = device.getSize(); + if (size == null) { Dimension[] sizes = device.getSizes(); @@ -155,12 +157,11 @@ private void ensureSize() { public synchronized void open() { if (open) { + LOG.debug("Webcam has already been open {}", getName()); return; } - if (LOG.isInfoEnabled()) { - LOG.info("Opening webcam " + getName()); - } + LOG.info("Opening webcam {}", getName()); ensureSize(); @@ -226,14 +227,14 @@ private void close0() { * * @return true if open, false otherwise */ - public synchronized boolean isOpen() { + public boolean isOpen() { return open; } /** * @return Webcam view size (picture size) in pixels. */ - public Dimension getViewSize() { + public synchronized Dimension getViewSize() { return device.getSize(); } @@ -243,7 +244,7 @@ public Dimension getViewSize() { * * @return */ - public Dimension[] getViewSizes() { + public synchronized Dimension[] getViewSizes() { return device.getSizes(); } @@ -273,7 +274,7 @@ public Dimension[] getCustomViewSizes() { * @see Webcam#setCustomViewSizes(Dimension[]) * @see Webcam#getViewSizes() */ - public void setViewSize(Dimension size) { + public synchronized void setViewSize(Dimension size) { if (size == null) { throw new IllegalArgumentException("View size cannot be null!"); @@ -313,9 +314,7 @@ public void setViewSize(Dimension size) { throw new IllegalArgumentException(sb.toString()); } - if (LOG.isDebugEnabled()) { - LOG.debug("Setting new view size {} x {}", size.width, size.height); - } + LOG.debug("Setting new view size {} x {}", size.width, size.height); device.setSize(size); } @@ -325,17 +324,15 @@ public void setViewSize(Dimension size) { * * @return Captured image */ - public BufferedImage getImage() { + public synchronized BufferedImage getImage() { if (disposed) { + LOG.warn("Cannot get image - webcam has been already disposed"); return null; } if (!open) { - LOG.debug("Try to get image on closed webcam, opening it automatically"); - synchronized (this) { - open(); - } + open(); } return device.getImage(); @@ -644,8 +641,11 @@ protected void dispose() { } } - open = false; + // make sure to dispose first !! disposed = true; + open = false; + + LOG.trace("Disposed flag set, open flag disabled"); WebcamEvent we = new WebcamEvent(this); for (WebcamListener l : listeners) { @@ -658,9 +658,10 @@ protected void dispose() { } synchronized (this) { - device.close(); device.dispose(); } + + LOG.debug("Webcam disposed {}", getName()); } /** diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamDefaultDevice.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamDefaultDevice.java index 46f1bfa7..4f6811ea 100644 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamDefaultDevice.java +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamDefaultDevice.java @@ -99,7 +99,6 @@ public class WebcamDefaultDevice implements WebcamDevice { private boolean failOnSizeMismatch = false; private volatile boolean open = false; - private volatile boolean opening = false; private volatile boolean disposed = false; private String name = null; @@ -145,8 +144,12 @@ public BufferedImage getImage() { return null; } + LOG.trace("Webcam device get image (next frame)"); + frameTask.nextFrame(); + LOG.trace("Webcam device get image (transfer buffer)"); + byte[] bytes = imageTask.getImage(size); byte[][] data = new byte[][] { bytes }; @@ -167,88 +170,68 @@ public BufferedImage getImage() { @Override public void open() { + LOG.debug("Opening webcam device {}", getName()); + if (disposed) { throw new WebcamException("Cannot open webcam when device it's already disposed"); } - synchronized (device) { - - if (opening) { - try { - device.wait(); - } catch (InterruptedException e) { - throw new WebcamException("Opening wait interrupted"); - } finally { - opening = false; - } - } - - if (open) { - return; - } else { - opening = true; - } - } - if (size == null) { size = getSizes()[0]; } - try { + LOG.debug("Webcam device starting session, size {}", size); - boolean started = sessionTask.startSession(size, device); - if (!started) { - throw new WebcamException("Cannot start video data grabber!"); - } + boolean started = sessionTask.startSession(size, device); + if (!started) { + throw new WebcamException("Cannot start video data grabber!"); + } - Dimension size2 = sizeTask.getSize(); + LOG.debug("Webcam device session started"); - int w1 = size.width; - int w2 = size2.width; - int h1 = size.height; - int h2 = size2.height; + Dimension size2 = sizeTask.getSize(); - if (w1 != w2 || h1 != h2) { + int w1 = size.width; + int w2 = size2.width; + int h1 = size.height; + int h2 = size2.height; - if (failOnSizeMismatch) { - throw new WebcamException(String.format("Different size obtained vs requested - [%dx%d] vs [%dx%d]", w1, h1, w2, h2)); - } + if (w1 != w2 || h1 != h2) { - LOG.warn("Different size obtained vs requested - [{}x{}] vs [{}x{}]. Setting correct one. New size is [{}x{}]", new Object[] { w1, h1, w2, h2, w2, h2 }); - size = new Dimension(w2, h2); + if (failOnSizeMismatch) { + throw new WebcamException(String.format("Different size obtained vs requested - [%dx%d] vs [%dx%d]", w1, h1, w2, h2)); } - sampleModel = new ComponentSampleModel(DATA_TYPE, size.width, size.height, 3, size.width * 3, BAND_OFFSETS); - colorModel = new ComponentColorModel(COLOR_SPACE, BITS, false, false, Transparency.OPAQUE, DATA_TYPE); - - int i = 0; - do { + LOG.warn("Different size obtained vs requested - [{}x{}] vs [{}x{}]. Setting correct one. New size is [{}x{}]", new Object[] { w1, h1, w2, h2, w2, h2 }); + size = new Dimension(w2, h2); + } - frameTask.nextFrame(); - imageTask.getImage(size); + sampleModel = new ComponentSampleModel(DATA_TYPE, size.width, size.height, 3, size.width * 3, BAND_OFFSETS); + colorModel = new ComponentColorModel(COLOR_SPACE, BITS, false, false, Transparency.OPAQUE, DATA_TYPE); - if (disposed) { - opening = false; - return; - } + LOG.debug("Initialize buffer"); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - LOG.error("Nasty interrupted exception", e); - } - } while (i++ < 3); + int i = 0; + do { - open = true; - opening = false; + frameTask.nextFrame(); + imageTask.getImage(size); - } finally { + if (disposed) { + return; + } - // notify all threads which are also waiting for device to be open - synchronized (device) { - device.notifyAll(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + LOG.error("Nasty interrupted exception", e); } - } + + } while (i++ < 3); + + LOG.debug("Webcam device is now open"); + + open = true; } @Override @@ -258,19 +241,23 @@ public void close() { return; } - synchronized (device) { - closeTask.closeSession(); - open = false; - } + LOG.debug("Closing webcam device"); + + open = false; + closeTask.closeSession(); } @Override public void dispose() { + if (disposed) { return; } - close(); + + LOG.debug("Disposing webcam device {}", getName()); + disposed = true; + close(); } /** diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/StartSessionTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/StartSessionTask.java index 44bbde36..0958753b 100644 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/StartSessionTask.java +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/StartSessionTask.java @@ -22,9 +22,12 @@ public StartSessionTask(WebcamGrabberProcessor processor) { } public boolean startSession(Dimension size, Device device) { + this.size = size; this.device = device; + process(processor); + return started; }