diff --git a/webcam-capture-drivers/webcam-capture-driver-civil/src/main/java/com/github/sarxos/webcam/ds/civil/LtiCivilDevice.java b/webcam-capture-drivers/webcam-capture-driver-civil/src/main/java/com/github/sarxos/webcam/ds/civil/LtiCivilDevice.java index e6b586e1..1dc58059 100644 --- a/webcam-capture-drivers/webcam-capture-driver-civil/src/main/java/com/github/sarxos/webcam/ds/civil/LtiCivilDevice.java +++ b/webcam-capture-drivers/webcam-capture-driver-civil/src/main/java/com/github/sarxos/webcam/ds/civil/LtiCivilDevice.java @@ -52,7 +52,7 @@ public String getName() { } @Override - public Dimension[] getSizes() { + public Dimension[] getResolutions() { if (dimensions == null) { dimensions = new ArrayList(); @@ -180,12 +180,12 @@ public void close() { } @Override - public Dimension getSize() { + public Dimension getResolution() { return size; } @Override - public void setSize(Dimension d) { + public void setResolution(Dimension d) { this.size = d; } @@ -193,4 +193,9 @@ public void setSize(Dimension d) { public void dispose() { disposed = true; } + + @Override + public boolean isOpen() { + return open; + } } diff --git a/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/IpCamDevice.java b/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/IpCamDevice.java index 6000d172..f98ca046 100644 --- a/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/IpCamDevice.java +++ b/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/IpCamDevice.java @@ -235,7 +235,7 @@ public String getName() { } @Override - public Dimension[] getSizes() { + public Dimension[] getResolutions() { if (sizes != null) { return sizes; @@ -268,15 +268,15 @@ protected void setSizes(Dimension[] sizes) { } @Override - public Dimension getSize() { + public Dimension getResolution() { if (size == null) { - size = getSizes()[0]; + size = getResolutions()[0]; } return size; } @Override - public void setSize(Dimension size) { + public void setResolution(Dimension size) { this.size = size; } @@ -441,4 +441,9 @@ public void setFailOnError(boolean failOnError) { public void dispose() { disposed = true; } + + @Override + public boolean isOpen() { + return open; + } } diff --git a/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/device/xvision/X104S.java b/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/device/xvision/X104S.java index 9e5bfd43..de789693 100644 --- a/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/device/xvision/X104S.java +++ b/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/device/xvision/X104S.java @@ -42,12 +42,12 @@ public X104S(String name, URL base) { } @Override - public Dimension[] getSizes() { + public Dimension[] getResolutions() { return SIZES; } @Override - public void setSize(Dimension size) { + public void setResolution(Dimension size) { int index = -1; for (int i = 0; i < SIZES.length; i++) { @@ -61,7 +61,7 @@ public void setSize(Dimension size) { throw new IllegalArgumentException(String.format("Incorrect size %s", size)); } - super.setSize(size); + super.setResolution(size); } @Override @@ -69,7 +69,7 @@ public URL getURL() { int index = -1; for (int i = 0; i < SIZES.length; i++) { - if (SIZES[i].equals(getSize())) { + if (SIZES[i].equals(getResolution())) { index = i; break; } diff --git a/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/device/zavio/B7210.java b/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/device/zavio/B7210.java index c3705d77..40752f89 100644 --- a/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/device/zavio/B7210.java +++ b/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/device/zavio/B7210.java @@ -40,12 +40,12 @@ public B7210(String name, URL base) { } @Override - public Dimension[] getSizes() { + public Dimension[] getResolutions() { return SIZES; } @Override - public void setSize(Dimension size) { + public void setResolution(Dimension size) { int index = -1; for (int i = 0; i < SIZES.length; i++) { @@ -59,7 +59,7 @@ public void setSize(Dimension size) { throw new IllegalArgumentException(String.format("Incorrect size %s", size)); } - super.setSize(size); + super.setResolution(size); } @Override @@ -67,7 +67,7 @@ public URL getURL() { int index = -1; for (int i = 0; i < SIZES.length; i++) { - if (SIZES[i].equals(getSize())) { + if (SIZES[i].equals(getResolution())) { index = i; break; } diff --git a/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/device/zavio/F3201.java b/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/device/zavio/F3201.java index da168769..8999e905 100644 --- a/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/device/zavio/F3201.java +++ b/webcam-capture-drivers/webcam-capture-driver-ipcam/src/main/java/com/github/sarxos/webcam/ds/ipcam/device/zavio/F3201.java @@ -40,12 +40,12 @@ public F3201(String name, URL base) { } @Override - public Dimension[] getSizes() { + public Dimension[] getResolutions() { return SIZES; } @Override - public void setSize(Dimension size) { + public void setResolution(Dimension size) { int index = -1; for (int i = 0; i < SIZES.length; i++) { @@ -59,7 +59,7 @@ public void setSize(Dimension size) { throw new IllegalArgumentException(String.format("Incorrect size %s", size)); } - super.setSize(size); + super.setResolution(size); } @Override @@ -67,7 +67,7 @@ public URL getURL() { int index = -1; for (int i = 0; i < SIZES.length; i++) { - if (SIZES[i].equals(getSize())) { + if (SIZES[i].equals(getResolution())) { index = i; break; } diff --git a/webcam-capture-drivers/webcam-capture-driver-javacv/src/main/java/com/github/sarxos/webcam/ds/javacv/JavaCvDevice.java b/webcam-capture-drivers/webcam-capture-driver-javacv/src/main/java/com/github/sarxos/webcam/ds/javacv/JavaCvDevice.java index 8bd74aa8..e0bacb3e 100644 --- a/webcam-capture-drivers/webcam-capture-driver-javacv/src/main/java/com/github/sarxos/webcam/ds/javacv/JavaCvDevice.java +++ b/webcam-capture-drivers/webcam-capture-driver-javacv/src/main/java/com/github/sarxos/webcam/ds/javacv/JavaCvDevice.java @@ -37,17 +37,17 @@ public String getName() { } @Override - public Dimension[] getSizes() { + public Dimension[] getResolutions() { throw new WebcamException("Not implemented"); } @Override - public Dimension getSize() { + public Dimension getResolution() { throw new WebcamException("Not implemented"); } @Override - public void setSize(Dimension size) { + public void setResolution(Dimension size) { throw new WebcamException("Not implemented"); } @@ -116,4 +116,9 @@ public void close() { public void dispose() { disposed = true; } + + @Override + public boolean isOpen() { + return open; + } } diff --git a/webcam-capture-drivers/webcam-capture-driver-jmf/src/main/java/com/github/sarxos/webcam/ds/jmf/JmfDevice.java b/webcam-capture-drivers/webcam-capture-driver-jmf/src/main/java/com/github/sarxos/webcam/ds/jmf/JmfDevice.java index 9bd1a475..aba34ba1 100644 --- a/webcam-capture-drivers/webcam-capture-driver-jmf/src/main/java/com/github/sarxos/webcam/ds/jmf/JmfDevice.java +++ b/webcam-capture-drivers/webcam-capture-driver-jmf/src/main/java/com/github/sarxos/webcam/ds/jmf/JmfDevice.java @@ -234,7 +234,7 @@ private VideoFormat getLargestVideoFormat() { } @Override - public Dimension[] getSizes() { + public Dimension[] getResolutions() { if (dimensions == null) { dimensions = new ArrayList(); @@ -267,12 +267,12 @@ public int compare(Dimension a, Dimension b) { } @Override - public Dimension getSize() { + public Dimension getResolution() { return dimension; } @Override - public void setSize(Dimension size) { + public void setResolution(Dimension size) { this.dimension = size; } @@ -387,4 +387,9 @@ public void dispose() { disposed = true; } + @Override + public boolean isOpen() { + return open; + } + } diff --git a/webcam-capture-drivers/webcam-capture-driver-openimaj/src/main/java/com/github/sarxos/webcam/ds/openimaj/OpenImajDevice.java b/webcam-capture-drivers/webcam-capture-driver-openimaj/src/main/java/com/github/sarxos/webcam/ds/openimaj/OpenImajDevice.java index 435b6ae8..31c38fcb 100644 --- a/webcam-capture-drivers/webcam-capture-driver-openimaj/src/main/java/com/github/sarxos/webcam/ds/openimaj/OpenImajDevice.java +++ b/webcam-capture-drivers/webcam-capture-driver-openimaj/src/main/java/com/github/sarxos/webcam/ds/openimaj/OpenImajDevice.java @@ -48,17 +48,17 @@ public String getName() { } @Override - public Dimension[] getSizes() { + public Dimension[] getResolutions() { return DIMENSIONS; } @Override - public Dimension getSize() { + public Dimension getResolution() { return size; } @Override - public void setSize(Dimension size) { + public void setResolution(Dimension size) { if (open) { throw new RuntimeException("Cannot set new size when device is open, please close it first"); } @@ -129,4 +129,9 @@ public void close() { public void dispose() { disposed = true; } + + @Override + public boolean isOpen() { + return open; + } } diff --git a/webcam-capture-drivers/webcam-capture-driver-vlcj/src/main/java/com/github/sarxos/webcam/ds/vlcj/VlcjDevice.java b/webcam-capture-drivers/webcam-capture-driver-vlcj/src/main/java/com/github/sarxos/webcam/ds/vlcj/VlcjDevice.java index 53421eb9..13d2fc38 100644 --- a/webcam-capture-drivers/webcam-capture-driver-vlcj/src/main/java/com/github/sarxos/webcam/ds/vlcj/VlcjDevice.java +++ b/webcam-capture-drivers/webcam-capture-driver-vlcj/src/main/java/com/github/sarxos/webcam/ds/vlcj/VlcjDevice.java @@ -106,17 +106,17 @@ public String getName() { } @Override - public Dimension[] getSizes() { + public Dimension[] getResolutions() { return DIMENSIONS; } @Override - public Dimension getSize() { + public Dimension getResolution() { return size; } @Override - public void setSize(Dimension size) { + public void setResolution(Dimension size) { this.size = size; } @@ -199,4 +199,9 @@ public synchronized void close() { public synchronized void dispose() { disposed = true; } + + @Override + public boolean isOpen() { + return open; + } } diff --git a/webcam-capture/src/example/java/com/github/sarxos/webcam/DetectMotionExample.java b/webcam-capture/src/example/java/com/github/sarxos/webcam/DetectMotionExample.java index 53da712a..ce59e5f4 100644 --- a/webcam-capture/src/example/java/com/github/sarxos/webcam/DetectMotionExample.java +++ b/webcam-capture/src/example/java/com/github/sarxos/webcam/DetectMotionExample.java @@ -2,11 +2,6 @@ import java.io.IOException; -import com.github.sarxos.webcam.Webcam; -import com.github.sarxos.webcam.WebcamMotionDetector; -import com.github.sarxos.webcam.WebcamMotionEvent; -import com.github.sarxos.webcam.WebcamMotionListener; - /** * Detect motion. diff --git a/webcam-capture/src/example/java/com/github/sarxos/webcam/TakePictureExample.java b/webcam-capture/src/example/java/com/github/sarxos/webcam/TakePictureExample.java index 068ec4c0..4c5a7410 100644 --- a/webcam-capture/src/example/java/com/github/sarxos/webcam/TakePictureExample.java +++ b/webcam-capture/src/example/java/com/github/sarxos/webcam/TakePictureExample.java @@ -15,7 +15,14 @@ public class TakePictureExample { public static void main(String[] args) throws IOException { + + // automatically open if webcam is closed + Webcam.setAutoOpenMode(true); + + // get image BufferedImage image = Webcam.getDefault().getImage(); + + // save image to PNG file ImageIO.write(image, "PNG", new File("test.png")); } } diff --git a/webcam-capture/src/example/resources/logback.xml b/webcam-capture/src/example/resources/logback.xml index 445d1112..baeded4b 100644 --- a/webcam-capture/src/example/resources/logback.xml +++ b/webcam-capture/src/example/resources/logback.xml @@ -4,7 +4,7 @@ %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 a014dd96..c27d6f24 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 @@ -8,12 +8,18 @@ import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import com.github.sarxos.webcam.ds.WebcamProcessor; import com.github.sarxos.webcam.ds.buildin.WebcamDefaultDevice; import com.github.sarxos.webcam.ds.buildin.WebcamDefaultDriver; +import com.github.sarxos.webcam.ds.cgt.WebcamCloseTask; +import com.github.sarxos.webcam.ds.cgt.WebcamDisposeTask; +import com.github.sarxos.webcam.ds.cgt.WebcamOpenTask; +import com.github.sarxos.webcam.ds.cgt.WebcamReadBufferTask; /** @@ -76,6 +82,9 @@ public void run() { */ private static volatile WebcamDriver driver = null; + // TODO: create only if !WebcamDriver::isThreadSafe() + private static final WebcamProcessor processor = new WebcamProcessor(); + /** * Webcam discovery service. */ @@ -84,7 +93,9 @@ public void run() { /** * Is automated deallocation on TERM signal enabled. */ - private static volatile boolean deallocOnTermSignal = false; + private static boolean deallocOnTermSignal = false; + + private static boolean autoOpen = false; /** * Webcam listeners. @@ -109,12 +120,12 @@ public void run() { /** * Is webcam open? */ - private volatile boolean open = false; + private AtomicBoolean open = new AtomicBoolean(false); /** * Is webcam already disposed? */ - private volatile boolean disposed = false; + private AtomicBoolean disposed = new AtomicBoolean(false); /** * Webcam class. @@ -130,96 +141,35 @@ protected Webcam(WebcamDevice device) { } /** - * Check if size is set up. - */ - private void ensureSize() { - - Dimension size = device.getSize(); - - if (size == null) { - - Dimension[] sizes = device.getSizes(); - - if (sizes == null) { - throw new WebcamException("Device error - sizes array from driver cannot be null!"); - } - if (sizes.length == 0) { - throw new WebcamException("Device error - sizes array from driver cannot be empty"); - } - - device.setSize(sizes[0]); - } - } - - /** - * Open webcam. - */ - public synchronized void open() { - - if (open) { - LOG.debug("Webcam has already been open {}", getName()); - return; - } - - LOG.info("Opening webcam {}", getName()); - - ensureSize(); - - device.open(); - open = true; - - hook = new ShutdownHook(this); - - Runtime.getRuntime().addShutdownHook(hook); - - WebcamEvent we = new WebcamEvent(this); - for (WebcamListener l : listeners) { - try { - l.webcamOpen(we); - } catch (Exception e) { - LOG.error(String.format("Notify webcam open, exception when calling listener %s", l.getClass()), e); - } - } - } - - /** - * Close webcam. + * Open the webcam. */ - public synchronized void close() { + public void open() { - if (!open) { + if (!open.compareAndSet(false, true)) { + LOG.debug("Webcam is already open {}", getName()); return; } - Runtime.getRuntime().removeShutdownHook(hook); + WebcamOpenTask task = new WebcamOpenTask(processor, device, true); + task.open(this); - close0(); + Runtime.getRuntime().addShutdownHook(hook = new ShutdownHook(this)); } /** - * Close webcam (internal impl). + * Close the webcam. */ - private void close0() { + public void close() { - if (!open) { + if (!open.compareAndSet(true, false)) { + LOG.debug("Webcam is already closed {}", getName()); return; } - if (LOG.isInfoEnabled()) { - LOG.info("Closing {}", getName()); - } - - device.close(); - open = false; + WebcamCloseTask task = new WebcamCloseTask(processor, device, true); + task.close(this); - WebcamEvent we = new WebcamEvent(this); - for (WebcamListener l : listeners) { - try { - l.webcamClosed(we); - } catch (Exception e) { - LOG.error(String.format("Notify webcam closed, exception when calling %s listener", l.getClass()), e); - } - } + Runtime.getRuntime().removeShutdownHook(hook); } /** @@ -228,7 +178,7 @@ private void close0() { * @return true if open, false otherwise */ public boolean isOpen() { - return open; + return open.get(); } /** @@ -236,8 +186,8 @@ public boolean isOpen() { * * @return Webcam resolution (picture size) in pixels. */ - public synchronized Dimension getViewSize() { - return device.getSize(); + public Dimension getViewSize() { + return device.getResolution(); } /** @@ -246,8 +196,8 @@ public synchronized Dimension getViewSize() { * * @return */ - public synchronized Dimension[] getViewSizes() { - return device.getSizes(); + public Dimension[] getViewSizes() { + return device.getResolutions(); } /** @@ -276,16 +226,24 @@ public Dimension[] getCustomViewSizes() { * @see Webcam#setCustomViewSizes(Dimension[]) * @see Webcam#getViewSizes() */ - public synchronized void setViewSize(Dimension size) { + public void setViewSize(Dimension size) { if (size == null) { throw new IllegalArgumentException("Resolution cannot be null!"); } - if (open) { + + if (open.get()) { throw new IllegalStateException("Cannot change resolution when webcam is open, please close it first"); } - // check if dimension is valid + // check if new resolution is the same as current one + + Dimension current = getViewSize(); + if (current != null && current.width == size.width && current.height == size.height) { + return; + } + + // check if new resolution is valid Dimension[] predefined = getViewSizes(); Dimension[] custom = getCustomViewSizes(); @@ -319,28 +277,45 @@ public synchronized void setViewSize(Dimension size) { throw new IllegalArgumentException(sb.toString()); } - LOG.debug("Setting new view size {} x {}", size.width, size.height); + LOG.debug("Setting new resolution {}x{}", size.width, size.height); - device.setSize(size); + device.setResolution(size); } /** - * Capture image from webcam. + * Capture image from webcam and return it. Will return image object or null + * if webcam is closed or has been already disposed by JVM.
+ *
+ * IMPORTANT NOTE!!!
+ *
+ * There are two possible behaviors of what webcam should do when you try to + * get image and webcam is actually closed. Normally it will return null, + * but there is a special flag which can be statically set to switch all + * webcams to auto open mode. In this mode, webcam will be automatically + * open, when you try to get image from closed webcam. Please be aware of + * some side effects! In case of multi-threaded applications, there is no + * guarantee that one thread will not try to open webcam even if it was + * manually closed in different thread. * - * @return Captured image + * @return Captured image or null if webcam is closed or disposed by JVM */ - public synchronized BufferedImage getImage() { + public BufferedImage getImage() { - if (disposed) { - LOG.warn("Cannot get image - webcam has been already disposed"); + if (disposed.get()) { + LOG.warn("Cannot get image, webcam has been already disposed"); return null; } - if (!open) { - open(); + if (!open.get()) { + if (autoOpen) { + open(); + } else { + return null; + } } - return device.getImage(); + WebcamReadBufferTask task = new WebcamReadBufferTask(processor, device, true); + return task.getImage(); } /** @@ -353,11 +328,13 @@ public synchronized BufferedImage getImage() { * @see Webcam#getWebcams(long, TimeUnit) */ public static List getWebcams() throws WebcamException { + + // timeout exception below will never be caught since user would have to + // wait around three hundreds billion years for it to occur + try { return getWebcams(Long.MAX_VALUE); } catch (TimeoutException e) { - // this should never happen since user would have to wait 300000000 - // years for it to occur throw new RuntimeException(e); } } @@ -585,7 +562,7 @@ public static void resetDriver() { driver = null; if (discovery != null) { - discovery.dispose(); + discovery.shutdown(); discovery = null; } } @@ -630,27 +607,20 @@ public WebcamDevice getDevice() { /** * Completely dispose capture device. After this operation webcam cannot be - * used any more and reinstantiation is required. + * used any more and full reinstantiation is required. */ protected void dispose() { - LOG.info("Disposing webcam {}", getName()); - - // hook can be null because there is a possibility that webcam has never - // been open and therefore hook was not created - if (hook != null) { - try { - Runtime.getRuntime().removeShutdownHook(hook); - } catch (IllegalStateException e) { - // ignore, it means that shutdown is in progress - } + if (!disposed.compareAndSet(false, true)) { + return; } - // make sure to dispose first !! - disposed = true; - open = false; + open.set(false); + + LOG.info("Disposing webcam {}", getName()); - LOG.trace("Disposed flag set, open flag disabled"); + WebcamDisposeTask task = new WebcamDisposeTask(processor, device, true); + task.dispose(); WebcamEvent we = new WebcamEvent(this); for (WebcamListener l : listeners) { @@ -662,8 +632,14 @@ protected void dispose() { } } - synchronized (this) { - device.dispose(); + // hook can be null because there is a possibility that webcam has never + // been open and therefore hook was not created + if (hook != null) { + try { + Runtime.getRuntime().removeShutdownHook(hook); + } catch (IllegalStateException e) { + // ignore, it means that shutdown is in progress + } } LOG.debug("Webcam disposed {}", getName()); @@ -696,6 +672,34 @@ public static boolean isHandleTermSignal() { return deallocOnTermSignal; } + /** + * Switch all webcams to auto open mode. In this mode, each webcam will be + * automatically open whenever user will try to get image from instance + * which has not yet been open. Please be aware of some side effects! In + * case of multi-threaded applications, there is no guarantee that one + * thread will not try to open webcam even if it was manually closed in + * different thread. + * + * @param on true to enable, false to disable + */ + public static void setAutoOpenMode(boolean on) { + autoOpen = on; + } + + /** + * Is auto open mode enabled. Auto open mode will will automatically open + * webcam whenever user will try to get image from instance which has not + * yet been open. Please be aware of some side effects! In case of + * multi-threaded applications, there is no guarantee that one thread will + * not try to open webcam even if it was manually closed in different + * thread. + * + * @return True if mode is enabled, false otherwise + */ + public static boolean isAutoOpenMode() { + return autoOpen; + } + /** * Add new webcam discovery listener. * diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamDevice.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamDevice.java index f1e39187..6c5f14be 100644 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamDevice.java +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamDevice.java @@ -23,21 +23,21 @@ public interface WebcamDevice { * * @return Possible resolutions */ - Dimension[] getSizes(); + Dimension[] getResolutions(); /** * Get currently set image size. * * @return The size which is currently set */ - Dimension getSize(); + Dimension getResolution(); /** * Set new expected image size. * * @param size the size to be set */ - void setSize(Dimension size); + void setResolution(Dimension size); /** * Fetch image from underlying camera. @@ -61,4 +61,11 @@ public interface WebcamDevice { */ void dispose(); + /** + * Is webcam device open? + * + * @return True if webcam device is open, false otherwise + */ + boolean isOpen(); + } diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamDiscoveryService.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamDiscoveryService.java index 58212f50..17e35010 100644 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamDiscoveryService.java +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamDiscoveryService.java @@ -321,7 +321,7 @@ public boolean isRunning() { /** * Cleanup. */ - protected synchronized void dispose() { + protected synchronized void shutdown() { stop(); diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamResolution.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamResolution.java index 53340789..72c87189 100644 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamResolution.java +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamResolution.java @@ -5,14 +5,34 @@ public enum WebcamResolution { + /** + * 176x144 + */ QQVGA(176, 144), + + /** + * 320x240 + */ QVGA(320, 240), + + /** + * 352x288 + */ CIF(352, 288), + HVGA(480, 400), VGA(640, 480), PAL(768, 576), SVGA(800, 600), + + /** + * 1024x768 + */ XGA(1024, 768), + + /** + * 1280x720 + */ HD720(1280, 720), WXGA(1280, 768), SXGA(1280, 1024), diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamUtils.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamUtils.java index d06d4c07..5f4e18a3 100644 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamUtils.java +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamUtils.java @@ -12,6 +12,9 @@ public class WebcamUtils { public static final void capture(Webcam webcam, File file) { + if (!webcam.isOpen()) { + webcam.open(); + } try { ImageIO.write(webcam.getImage(), ImageUtils.FORMAT_JPG, file); } catch (IOException e) { @@ -20,6 +23,9 @@ public static final void capture(Webcam webcam, File file) { } public static final void capture(Webcam webcam, File file, String format) { + if (!webcam.isOpen()) { + webcam.open(); + } try { ImageIO.write(webcam.getImage(), format, file); } catch (IOException e) { diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/WebcamProcessor.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/WebcamProcessor.java new file mode 100644 index 00000000..67fd6777 --- /dev/null +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/WebcamProcessor.java @@ -0,0 +1,102 @@ +package com.github.sarxos.webcam.ds; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicBoolean; + +import com.github.sarxos.webcam.WebcamException; + + +public class WebcamProcessor { + + /** + * Thread factory for processor. + * + * @author Bartosz Firyn (SarXos) + */ + private static final class ProcessorThreadFactory implements ThreadFactory { + + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r, "atomic-processor"); + t.setDaemon(true); + return t; + } + } + + /** + * Heart of overall processing system. This class process all native calls + * wrapped in tasks, by doing this all tasks executions are + * super-synchronized. + * + * @author Bartosz Firyn (SarXos) + */ + private static final class AtomicProcessor implements Runnable { + + private SynchronousQueue inbound = new SynchronousQueue(true); + private SynchronousQueue outbound = new SynchronousQueue(true); + + /** + * Process task. + * + * @param task the task to be processed + * @return Processed task + * @throws InterruptedException when thread has been interrupted + */ + public WebcamTask process(WebcamTask task) throws InterruptedException { + inbound.put(task); + return outbound.take(); + } + + @Override + public void run() { + while (true) { + WebcamTask t = null; + try { + (t = inbound.take()).handle(); + } catch (InterruptedException e) { + return; + } finally { + try { + if (t != null) { + outbound.put(t); + } + } catch (InterruptedException e) { + return; + } + } + } + } + } + + /** + * Is processor started? + */ + private static final AtomicBoolean started = new AtomicBoolean(false); + + /** + * Execution service. + */ + private static final Executor runner = Executors.newSingleThreadExecutor(new ProcessorThreadFactory()); + + /** + * Static processor. + */ + private static final AtomicProcessor processor = new AtomicProcessor(); + + public WebcamProcessor() { + if (started.compareAndSet(false, true)) { + runner.execute(processor); + } + } + + public void process(WebcamTask task) { + try { + processor.process(task); + } catch (InterruptedException e) { + throw new WebcamException("Processing interrupted", e); + } + } +} diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/WebcamTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/WebcamTask.java new file mode 100644 index 00000000..c8c68e05 --- /dev/null +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/WebcamTask.java @@ -0,0 +1,54 @@ +package com.github.sarxos.webcam.ds; + +import com.github.sarxos.webcam.WebcamDevice; +import com.github.sarxos.webcam.WebcamException; + + +public abstract class WebcamTask { + + private boolean sync = true; + private WebcamProcessor processor = null; + private WebcamDevice device = null; + private WebcamException exception = null; + + public WebcamTask(WebcamProcessor processor, WebcamDevice device, boolean sync) { + + if (processor == null) { + throw new IllegalArgumentException("Processor argument cannot be null!"); + } + if (device == null) { + throw new IllegalArgumentException("Device argument cannot be null!"); + } + + this.processor = processor; + this.device = device; + this.sync = sync; + } + + public WebcamDevice getDevice() { + return device; + } + + /** + * Process task by processor thread. + * + * @param processor the processor to be used to process this task + */ + public void process() { + if (sync) { + processor.process(this); + } else { + handle(); + } + } + + public WebcamException getException() { + return exception; + } + + public void setException(WebcamException exception) { + this.exception = exception; + } + + protected abstract void handle(); +} 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 cfc26fe1..f53ac9a8 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 @@ -11,19 +11,17 @@ import java.awt.image.DataBufferByte; import java.awt.image.Raster; import java.awt.image.WritableRaster; +import java.util.concurrent.atomic.AtomicBoolean; +import org.bridj.Pointer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.github.sarxos.webcam.WebcamDevice; import com.github.sarxos.webcam.WebcamException; import com.github.sarxos.webcam.WebcamResolution; -import com.github.sarxos.webcam.ds.buildin.cgt.CloseSessionTask; -import com.github.sarxos.webcam.ds.buildin.cgt.GetImageTask; -import com.github.sarxos.webcam.ds.buildin.cgt.GetSizeTask; -import com.github.sarxos.webcam.ds.buildin.cgt.NextFrameTask; -import com.github.sarxos.webcam.ds.buildin.cgt.StartSessionTask; import com.github.sarxos.webcam.ds.buildin.natives.Device; +import com.github.sarxos.webcam.ds.buildin.natives.OpenIMAJGrabber; public class WebcamDefaultDevice implements WebcamDevice { @@ -79,27 +77,15 @@ public class WebcamDefaultDevice implements WebcamDevice { */ private static final ColorSpace COLOR_SPACE = ColorSpace.getInstance(ColorSpace.CS_sRGB); - /** - * Synchronous webcam video grabber processor. - */ - private final WebcamGrabberProcessor processor = new WebcamGrabberProcessor(); - - // synchronous grabber tasks - - private final NextFrameTask frameTask = new NextFrameTask(processor); - private final GetImageTask imageTask = new GetImageTask(processor); - private final StartSessionTask sessionTask = new StartSessionTask(processor); - private final GetSizeTask sizeTask = new GetSizeTask(processor); - private final CloseSessionTask closeTask = new CloseSessionTask(processor); - + private OpenIMAJGrabber grabber = new OpenIMAJGrabber(); private Device device = null; private Dimension size = null; private ComponentSampleModel sampleModel = null; private ColorModel colorModel = null; private boolean failOnSizeMismatch = false; - private volatile boolean open = false; - private volatile boolean disposed = false; + private AtomicBoolean disposed = new AtomicBoolean(false); + private AtomicBoolean open = new AtomicBoolean(false); private String name = null; private String id = null; @@ -118,18 +104,18 @@ public String getName() { } @Override - public Dimension[] getSizes() { + public Dimension[] getResolutions() { return DIMENSIONS; } @Override - public Dimension getSize() { + public Dimension getResolution() { return size; } @Override - public void setSize(Dimension size) { - if (open) { + public void setResolution(Dimension size) { + if (open.get()) { throw new IllegalStateException("Cannot change resolution when webcam is open, please close it first"); } this.size = size; @@ -138,22 +124,31 @@ public void setSize(Dimension size) { @Override public BufferedImage getImage() { - if (disposed) { - throw new WebcamException("Cannot get image since device is already disposed"); + if (disposed.get()) { + LOG.debug("Webcam is disposed, image will be null"); + return null; } - if (!open) { - LOG.error("Cannot get image when device is closed"); + if (!open.get()) { + LOG.debug("Webcam is closed, image will be null"); return null; } LOG.trace("Webcam device get image (next frame)"); - frameTask.nextFrame(); + grabber.nextFrame(); + + Pointer image = grabber.getImage(); + if (image == null) { + LOG.warn("Null array pointer found instead of image"); + return null; + } + + int length = size.width * size.height * 3; - LOG.trace("Webcam device get image (transfer buffer)"); + LOG.trace("Webcam device get image (transfer buffer) {} bytes", length); - byte[] bytes = imageTask.getImage(size); + byte[] bytes = image.getBytes(length); byte[][] data = new byte[][] { bytes }; if (bytes == null) { @@ -173,26 +168,26 @@ 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"); + if (disposed.get()) { + return; } + LOG.debug("Opening webcam device {}", getName()); + if (size == null) { - size = getSizes()[0]; + size = getResolutions()[0]; } LOG.debug("Webcam device starting session, size {}", size); - boolean started = sessionTask.startSession(size, device); + boolean started = grabber.startSession(size.width, size.height, 30, Pointer.pointerTo(device)); if (!started) { - throw new WebcamException("Cannot start video data grabber!"); + throw new WebcamException("Cannot start native grabber!"); } LOG.debug("Webcam device session started"); - Dimension size2 = sizeTask.getSize(); + Dimension size2 = new Dimension(grabber.getWidth(), grabber.getHeight()); int w1 = size.width; int w2 = size2.width; @@ -217,12 +212,7 @@ public void open() { int i = 0; do { - frameTask.nextFrame(); - imageTask.getImage(size); - - if (disposed) { - return; - } + grabber.nextFrame(); try { Thread.sleep(1000); @@ -230,36 +220,34 @@ public void open() { LOG.error("Nasty interrupted exception", e); } - } while (i++ < 3); + } while (++i < 3); LOG.debug("Webcam device is now open"); - open = true; + open.set(true); } @Override public void close() { - if (!open) { + if (!open.compareAndSet(true, false)) { return; } LOG.debug("Closing webcam device"); - open = false; - closeTask.closeSession(); + grabber.stopSession(); } @Override public void dispose() { - if (disposed) { + if (!disposed.compareAndSet(false, true)) { return; } LOG.debug("Disposing webcam device {}", getName()); - disposed = true; close(); } @@ -272,4 +260,9 @@ public void dispose() { public void setFailOnSizeMismatch(boolean fail) { this.failOnSizeMismatch = fail; } + + @Override + public boolean isOpen() { + return open.get(); + } } diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamDefaultDriver.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamDefaultDriver.java index fee8c77c..3a3e4c07 100644 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamDefaultDriver.java +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamDefaultDriver.java @@ -3,20 +3,22 @@ import java.util.ArrayList; import java.util.List; +import org.bridj.Pointer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.github.sarxos.webcam.WebcamDevice; import com.github.sarxos.webcam.WebcamDiscoverySupport; import com.github.sarxos.webcam.WebcamDriver; -import com.github.sarxos.webcam.ds.buildin.cgt.GetDevicesTask; import com.github.sarxos.webcam.ds.buildin.natives.Device; +import com.github.sarxos.webcam.ds.buildin.natives.DeviceList; +import com.github.sarxos.webcam.ds.buildin.natives.OpenIMAJGrabber; /** * Default build-in webcam driver based on natives from OpenIMAJ framework. It - * can be widely used on various systems - Mac OS X, Linux (x86, x64, 32-bit - * ARM), Windows (win32, win64). + * can be widely used on various systems - Mac OS, Linux (x86, x64, ARM), + * Windows (win32, win64). * * @author Bartosz Firyn (SarXos) */ @@ -28,14 +30,9 @@ public class WebcamDefaultDriver implements WebcamDriver, WebcamDiscoverySupport private static final Logger LOG = LoggerFactory.getLogger(WebcamDefaultDriver.class); /** - * Synchronous video grabber processor. + * Native grabber. */ - private static final WebcamGrabberProcessor processor = new WebcamGrabberProcessor(); - - /** - * Task to fetch images list from grabber. - */ - private static final GetDevicesTask DEVICES_TASK = new GetDevicesTask(processor); + private static final OpenIMAJGrabber GRABBER = new OpenIMAJGrabber(); @Override public List getDevices() { @@ -43,8 +40,10 @@ public List getDevices() { LOG.debug("Searching devices"); List devices = new ArrayList(); + Pointer pointer = GRABBER.getVideoDevices(); + DeviceList list = pointer.get(); - for (Device device : DEVICES_TASK.getDevices()) { + for (Device device : list.asArrayList()) { devices.add(new WebcamDefaultDevice(device)); } diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamGrabberProcessor.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamGrabberProcessor.java deleted file mode 100644 index 1a532655..00000000 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamGrabberProcessor.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.github.sarxos.webcam.ds.buildin; - -import java.util.concurrent.Executor; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import com.github.sarxos.webcam.WebcamException; -import com.github.sarxos.webcam.ds.buildin.cgt.NewGrabberTask; -import com.github.sarxos.webcam.ds.buildin.natives.OpenIMAJGrabber; - - -/** - * This class is to ensure that all native calls will be executed by a - * single, well synchronized thread. The problem with grabber which is being - * used to perform native calls is the fact it is completely not ready to be - * used in well-written concurrent application (such as Swing for example). - * - * @author Bartosz Firyn (SarXos) - */ -public final class WebcamGrabberProcessor { - - /** - * Thread factory for processor. - * - * @author Bartosz Firyn (SarXos) - */ - private static final class ProcessorThreadFactory implements ThreadFactory { - - private static int number = 0; - - @Override - public Thread newThread(Runnable r) { - - // should always be 1, but add some unique name for debugging - // purpose just in case if there is some bug in my understanding - - Thread t = new Thread(r, "processor-" + (++number)); - t.setDaemon(true); - - return t; - } - } - - /** - * Heart of overall processing system. This class process all native calls - * wrapped in tasks. - * - * @author Bartosz Firyn (SarXos) - */ - private static final class StaticProcessor implements Runnable { - - private class IO { - - public AtomicReference input = new AtomicReference(); - public AtomicReference output = new AtomicReference(); - - public IO(WebcamGrabberTask task) { - input.set(task); - } - } - - private final AtomicReference ioref = new AtomicReference(); - - /** - * Process task. - * - * @param task the task to be processed - * @return Processed task - * @throws InterruptedException when thread has been interrupted - */ - public WebcamGrabberTask process(WebcamGrabberTask task) throws InterruptedException { - - IO io = new IO(task); - - // submit task wrapped in IO pair - synchronized (ioref) { - while (!ioref.compareAndSet(null, io)) { - ioref.wait(); - } - } - - // obtain processed task - synchronized (io.output) { - while ((task = io.output.getAndSet(null)) == null) { - io.output.wait(); - } - } - - return task; - } - - @Override - public void run() { - - WebcamGrabberTask t = null; - IO io = null; - - while (true) { - - if ((io = ioref.getAndSet(null)) != null) { - - synchronized (ioref) { - ioref.notify(); - } - - (t = io.input.get()).handle(); - io.output.set(t); - - synchronized (io.output) { - io.output.notify(); - } - - } else { - try { - Thread.sleep(10); - } catch (InterruptedException e) { - throw new RuntimeException(e.getMessage(), e); - } - } - } - } - } - - /** - * Execution service. - */ - private static final Executor runner = Executors.newSingleThreadExecutor(new ProcessorThreadFactory()); - - /** - * Static processor. - */ - private static final StaticProcessor processor = new StaticProcessor(); - - /** - * Is processor started? - */ - private static final AtomicBoolean started = new AtomicBoolean(false); - - /** - * Native grabber. - */ - private OpenIMAJGrabber grabber = null; - - /** - * Protected access so user is not able to create it. - */ - protected WebcamGrabberProcessor() { - if (started.compareAndSet(false, true)) { - runner.execute(processor); - } - } - - /** - * Create native grabber. - * - * @return New grabber - */ - private OpenIMAJGrabber createGrabber() { - NewGrabberTask ngt = new NewGrabberTask(); - try { - return ((NewGrabberTask) processor.process(ngt)).getGrabber(); - } catch (InterruptedException e) { - throw new WebcamException("Grabber creation interrupted", e); - } - } - - /** - * Process task. - * - * @param task the task to be processed - */ - protected void process(WebcamGrabberTask task) { - - // construct grabber if not available yet - - if (grabber == null) { - grabber = createGrabber(); - } - - // process task and wait for it to be completed - - task.setGrabber(grabber); - - try { - processor.process(task); - } catch (InterruptedException e) { - throw new WebcamException("Processing interrupted", e); - } - } -} diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamGrabberTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamGrabberTask.java deleted file mode 100644 index 41188684..00000000 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/WebcamGrabberTask.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.github.sarxos.webcam.ds.buildin; - -import com.github.sarxos.webcam.ds.buildin.natives.OpenIMAJGrabber; - - -/** - * Abstract webcam processor class. - * - * @author Bartosz Firyn (SarXos) - */ -public abstract class WebcamGrabberTask { - - /** - * Native grabber. - */ - protected volatile OpenIMAJGrabber grabber = null; - - /** - * ne = true; Process task by processor thread. - * - * @param processor the processor to be used to process this task - */ - protected void process(WebcamGrabberProcessor processor) { - processor.process(this); - } - - /** - * Set grabber connected with specific device. - * - * @param grabber the grabber to be set - */ - public void setGrabber(OpenIMAJGrabber grabber) { - this.grabber = grabber; - } - - /** - * Method to be called from inside of the processor thread. - */ - protected abstract void handle(); -} diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/CloseSessionTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/CloseSessionTask.java deleted file mode 100644 index 209fd3c2..00000000 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/CloseSessionTask.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.github.sarxos.webcam.ds.buildin.cgt; - -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberProcessor; -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberTask; - - -/** - * This task is to close grabber session. - * - * @author Bartosz Firyn (SarXos) - */ -public class CloseSessionTask extends WebcamGrabberTask { - - /** - * Grabber processor. - */ - private WebcamGrabberProcessor processor = null; - - /** - * Create task closing session of grabber connected with specific device. - * - * @param processor - */ - public CloseSessionTask(WebcamGrabberProcessor processor) { - this.processor = processor; - } - - /** - * Method to be called from outside of the processor thread. - */ - public void closeSession() { - process(processor); - } - - @Override - protected void handle() { - grabber.stopSession(); - } -} diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/GetDevicesTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/GetDevicesTask.java deleted file mode 100644 index ef6ad4b7..00000000 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/GetDevicesTask.java +++ /dev/null @@ -1,50 +0,0 @@ -package com.github.sarxos.webcam.ds.buildin.cgt; - -import java.util.List; -import java.util.concurrent.atomic.AtomicReference; - -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberProcessor; -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberTask; -import com.github.sarxos.webcam.ds.buildin.natives.Device; - - -/** - * Processor task used to discover native webcam devices. - * - * @author Bartosz Firyn (SarXos) - */ -public class GetDevicesTask extends WebcamGrabberTask { - - /** - * Devices list. - */ - private AtomicReference> devices = new AtomicReference>(); - - /** - * Processor. - */ - private WebcamGrabberProcessor processor = null; - - /** - * Creates new task used to discover native webcam devices. - * - * @param processor - */ - public GetDevicesTask(WebcamGrabberProcessor processor) { - this.processor = processor; - } - - /** - * @return Discovered devices - */ - public List getDevices() { - process(processor); - return devices.get(); - } - - @Override - protected void handle() { - List devices = grabber.getVideoDevices().get().asArrayList(); - this.devices.set(devices); - } -} diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/GetImageTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/GetImageTask.java deleted file mode 100644 index abd485a0..00000000 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/GetImageTask.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.github.sarxos.webcam.ds.buildin.cgt; - -import java.awt.Dimension; -import java.util.concurrent.atomic.AtomicReference; - -import org.bridj.Pointer; - -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberProcessor; -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberTask; - - -public class GetImageTask extends WebcamGrabberTask { - - private AtomicReference dimension = new AtomicReference(); - private AtomicReference bytes = new AtomicReference(); - - private WebcamGrabberProcessor processor = null; - - public GetImageTask(WebcamGrabberProcessor processor) { - this.processor = processor; - } - - public byte[] getImage(Dimension size) { - dimension.set(size); - process(processor); - return bytes.get(); - } - - @Override - protected void handle() { - Pointer image = grabber.getImage(); - Dimension size = dimension.get(); - bytes.set(image == null ? null : image.getBytes(size.width * size.height * 3)); - } -} diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/GetSizeTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/GetSizeTask.java deleted file mode 100644 index 1136105e..00000000 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/GetSizeTask.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.github.sarxos.webcam.ds.buildin.cgt; - -import java.awt.Dimension; -import java.util.concurrent.atomic.AtomicReference; - -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberProcessor; -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberTask; - - -public class GetSizeTask extends WebcamGrabberTask { - - private AtomicReference dimension = new AtomicReference(); - private WebcamGrabberProcessor processor = null; - - public GetSizeTask(WebcamGrabberProcessor processor) { - this.processor = processor; - } - - public Dimension getSize() { - process(processor); - return dimension.get(); - } - - @Override - protected void handle() { - dimension.set(new Dimension(grabber.getWidth(), grabber.getHeight())); - } -} diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/NewGrabberTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/NewGrabberTask.java deleted file mode 100644 index d61e7343..00000000 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/NewGrabberTask.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.github.sarxos.webcam.ds.buildin.cgt; - -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberTask; -import com.github.sarxos.webcam.ds.buildin.natives.OpenIMAJGrabber; - - -/** - * Internal task used to create new grabber. Yeah, native grabber construction, - * same as all other methods invoked on its instance, also has to be - * super-synchronized. - * - * @author Bartosz Firyn (SarXos) - */ -public class NewGrabberTask extends WebcamGrabberTask { - - private volatile OpenIMAJGrabber grabber = null; - - public OpenIMAJGrabber getGrabber() { - return grabber; - } - - @Override - protected void handle() { - grabber = new OpenIMAJGrabber(); - } -} \ No newline at end of file diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/NextFrameTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/NextFrameTask.java deleted file mode 100644 index a76cf29a..00000000 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/NextFrameTask.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.github.sarxos.webcam.ds.buildin.cgt; - -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberProcessor; -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberTask; - - -public class NextFrameTask extends WebcamGrabberTask { - - private WebcamGrabberProcessor processor = null; - - public NextFrameTask(WebcamGrabberProcessor processor) { - this.processor = processor; - } - - public void nextFrame() { - process(processor); - } - - @Override - protected void handle() { - grabber.nextFrame(); - } -} 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 deleted file mode 100644 index 0958753b..00000000 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/buildin/cgt/StartSessionTask.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.github.sarxos.webcam.ds.buildin.cgt; - -import java.awt.Dimension; - -import org.bridj.Pointer; - -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberProcessor; -import com.github.sarxos.webcam.ds.buildin.WebcamGrabberTask; -import com.github.sarxos.webcam.ds.buildin.natives.Device; - - -public class StartSessionTask extends WebcamGrabberTask { - - private Dimension size = null; - private Device device = null; - private volatile boolean started = false; - - private WebcamGrabberProcessor processor = null; - - public StartSessionTask(WebcamGrabberProcessor processor) { - this.processor = processor; - } - - public boolean startSession(Dimension size, Device device) { - - this.size = size; - this.device = device; - - process(processor); - - return started; - } - - @Override - protected void handle() { - started = grabber.startSession(size.width, size.height, 50, Pointer.pointerTo(device)); - } -} diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/cgt/WebcamCloseTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/cgt/WebcamCloseTask.java new file mode 100644 index 00000000..de529f42 --- /dev/null +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/cgt/WebcamCloseTask.java @@ -0,0 +1,52 @@ +package com.github.sarxos.webcam.ds.cgt; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.sarxos.webcam.Webcam; +import com.github.sarxos.webcam.WebcamDevice; +import com.github.sarxos.webcam.WebcamEvent; +import com.github.sarxos.webcam.WebcamListener; +import com.github.sarxos.webcam.ds.WebcamProcessor; +import com.github.sarxos.webcam.ds.WebcamTask; + + +public class WebcamCloseTask extends WebcamTask { + + private static final Logger LOG = LoggerFactory.getLogger(WebcamCloseTask.class); + + private Webcam webcam = null; + + public WebcamCloseTask(WebcamProcessor processor, WebcamDevice device, boolean sync) { + super(processor, device, sync); + } + + public void close(Webcam webcam) { + this.webcam = webcam; + process(); + } + + @Override + protected void handle() { + + WebcamDevice device = getDevice(); + if (!device.isOpen()) { + return; + } + + LOG.info("Closing {}", device.getName()); + + device.close(); + + // TODO move back to Webcam + + WebcamEvent we = new WebcamEvent(webcam); + for (WebcamListener l : webcam.getWebcamListeners()) { + try { + l.webcamClosed(we); + } catch (Exception e) { + LOG.error(String.format("Notify webcam closed, exception when calling %s listener", l.getClass()), e); + } + } + } +} diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/cgt/WebcamDisposeTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/cgt/WebcamDisposeTask.java new file mode 100644 index 00000000..c0e5c346 --- /dev/null +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/cgt/WebcamDisposeTask.java @@ -0,0 +1,27 @@ +package com.github.sarxos.webcam.ds.cgt; + +import com.github.sarxos.webcam.WebcamDevice; +import com.github.sarxos.webcam.ds.WebcamProcessor; +import com.github.sarxos.webcam.ds.WebcamTask; + + +/** + * Dispose webcam device. + * + * @author Bartosz Firyn (sarxos) + */ +public class WebcamDisposeTask extends WebcamTask { + + public WebcamDisposeTask(WebcamProcessor processor, WebcamDevice device, boolean sync) { + super(processor, device, sync); + } + + public void dispose() { + process(); + } + + @Override + protected void handle() { + getDevice().dispose(); + } +} diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/cgt/WebcamOpenTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/cgt/WebcamOpenTask.java new file mode 100644 index 00000000..94f715fc --- /dev/null +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/cgt/WebcamOpenTask.java @@ -0,0 +1,57 @@ +package com.github.sarxos.webcam.ds.cgt; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.github.sarxos.webcam.Webcam; +import com.github.sarxos.webcam.WebcamDevice; +import com.github.sarxos.webcam.WebcamEvent; +import com.github.sarxos.webcam.WebcamListener; +import com.github.sarxos.webcam.ds.WebcamProcessor; +import com.github.sarxos.webcam.ds.WebcamTask; + + +public class WebcamOpenTask extends WebcamTask { + + private static final Logger LOG = LoggerFactory.getLogger(WebcamOpenTask.class); + + private Webcam webcam = null; + + public WebcamOpenTask(WebcamProcessor processor, WebcamDevice device, boolean sync) { + super(processor, device, sync); + } + + public void open(Webcam webcam) { + this.webcam = webcam; + process(); + } + + @Override + protected void handle() { + + WebcamDevice device = getDevice(); + + if (device.isOpen()) { + return; + } + + if (device.getResolution() == null) { + device.setResolution(device.getResolutions()[0]); + } + + LOG.info("Opening webcam {}", device.getName()); + + device.open(); + + // TODO move back to Webcam + + WebcamEvent we = new WebcamEvent(webcam); + for (WebcamListener l : webcam.getWebcamListeners()) { + try { + l.webcamOpen(we); + } catch (Exception e) { + LOG.error(String.format("Notify webcam open, exception when calling listener %s", l.getClass()), e); + } + } + } +} diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/cgt/WebcamReadBufferTask.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/cgt/WebcamReadBufferTask.java new file mode 100644 index 00000000..2b28225e --- /dev/null +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/ds/cgt/WebcamReadBufferTask.java @@ -0,0 +1,33 @@ +package com.github.sarxos.webcam.ds.cgt; + +import java.awt.image.BufferedImage; + +import com.github.sarxos.webcam.WebcamDevice; +import com.github.sarxos.webcam.ds.WebcamProcessor; +import com.github.sarxos.webcam.ds.WebcamTask; + + +public class WebcamReadBufferTask extends WebcamTask { + + private BufferedImage image = null; + + public WebcamReadBufferTask(WebcamProcessor processor, WebcamDevice device, boolean sync) { + super(processor, device, sync); + } + + public BufferedImage getImage() { + process(); + return image; + } + + @Override + protected void handle() { + + WebcamDevice device = getDevice(); + if (!device.isOpen()) { + return; + } + + image = device.getImage(); + } +} diff --git a/webcam-capture/src/test/java/com/github/sarxos/webcam/WebcamTest.java b/webcam-capture/src/test/java/com/github/sarxos/webcam/WebcamTest.java index b177d0d4..51311578 100644 --- a/webcam-capture/src/test/java/com/github/sarxos/webcam/WebcamTest.java +++ b/webcam-capture/src/test/java/com/github/sarxos/webcam/WebcamTest.java @@ -58,11 +58,6 @@ public void test_open() { Assert.assertTrue(webcam.isOpen()); webcam.open(); - - Assert.assertTrue(webcam.isOpen()); - webcam = Webcam.getWebcams().get(1); - webcam.getImage(); - Assert.assertTrue(webcam.isOpen()); } @@ -83,6 +78,7 @@ public void test_close() { public void test_getImage() { Webcam webcam = Webcam.getDefault(); + webcam.open(); Image image = webcam.getImage(); Assert.assertNotNull(image); diff --git a/webcam-capture/src/test/java/com/github/sarxos/webcam/ds/test/DummyDevice.java b/webcam-capture/src/test/java/com/github/sarxos/webcam/ds/test/DummyDevice.java index 22682727..58b8a203 100644 --- a/webcam-capture/src/test/java/com/github/sarxos/webcam/ds/test/DummyDevice.java +++ b/webcam-capture/src/test/java/com/github/sarxos/webcam/ds/test/DummyDevice.java @@ -28,17 +28,17 @@ public String getName() { } @Override - public Dimension[] getSizes() { + public Dimension[] getResolutions() { return DIMENSIONS; } @Override - public Dimension getSize() { + public Dimension getResolution() { return size; } @Override - public void setSize(Dimension size) { + public void setResolution(Dimension size) { this.size = size; }