From fac1e059123d51490f9606670ca9ecdaf1a808dc Mon Sep 17 00:00:00 2001 From: Bartosz Firyn Date: Thu, 22 Jan 2015 09:14:11 +0100 Subject: [PATCH] Use vlcj direct rendering instead of snapshot --- README.md | 26 +- webcam-capture-drivers/driver-vlcj/README.md | 16 +- webcam-capture-drivers/driver-vlcj/pom.xml | 5 + .../src/example/java/VlcjDirectTest.java | 2 +- .../src/example/java/WebcamPanelExample.java | 2 +- .../sarxos/webcam/ds/vlcj/VlcjDevice.java | 440 ++++++++++++++---- .../sarxos/webcam/ds/vlcj/VlcjDriver.java | 18 +- .../sarxos/webcam/WebcamDiscoverySupport.java | 25 +- .../ds/buildin/WebcamDefaultDriver.java | 11 +- 9 files changed, 394 insertions(+), 151 deletions(-) diff --git a/README.md b/README.md index 7b2bf5a2..92233e6f 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# Webcam Capture API for Java +# Webcam Capture API This library allows you to use your build-in or external webcam directly from Java. It's designed to abstract commonly used camera features and support multiple capturing farmeworks. [![Maven Central](https://maven-badges.herokuapp.com/maven-central/com.github.sarxos/webcam-capture/badge.svg)](http://search.maven.org/#artifactdetails|com.github.sarxos|webcam-capture|0.3.10|bundle) [![Build Status](https://img.shields.io/travis/sarxos/webcam-capture.svg?branch=master)](http://travis-ci.org/sarxos/webcam-capture) [![Coverage Status](https://img.shields.io/coveralls/sarxos/webcam-capture.svg?branch=master)](https://coveralls.io/r/sarxos/webcam-capture?branch=master) -[![Ohloh Stats](https://img.shields.io/badge/ohloh-%24%20243k%2019.7%20kNCSL-blue.svg)](https://www.ohloh.net/p/java-webcam-capture) +[![Ohloh Stats](https://img.shields.io/badge/ohloh-19.7kNCSL-blue.svg)](https://www.ohloh.net/p/java-webcam-capture) ## Features @@ -27,7 +27,7 @@ This library allows you to use your build-in or external webcam directly from Ja * [Freedom for Media in Java (FMJ)](http://fmj-sf.net/), * [OpenCV](http://opencv.org/) via [JavaCV](https://github.com/bytedeco/javacv), * [VLC](http://www.videolan.org/vlc/) via [vlcj](http://www.capricasoftware.co.uk/projects/vlcj/index.html), - * [GStreamer](http://gstreamer.freedesktop.org/) via [gstreamer-java](https://code.google.com/p/gstreamer-java/) + * [GStreamer](http://gstreamer.freedesktop.org/) (0.10.x only) via [gstreamer-java](https://code.google.com/p/gstreamer-java/) * MJPEG IP Cameras, ## Raspberry PI @@ -150,15 +150,15 @@ List of additional capture drivers includes: | Driver Name | Stable | Central | Description | |-----------------|--------|---------|-----------------------------------------| -| [ipcam][] | yes | yes | IP / network camera driver | -| [gstreamer][] | yes | no | Driver for [GStreamer][] framework | -| [openimaj][] | yes | no | Driver for [OpenIMAJ][] framework | -| [v4l4j][] | yes | no | Driver for [V4L4j][] project | -| [jmf][] | yes | yes | Driver for [JMF][] / [FMJ][] frameworks | -| [lti-civil][] | yes | yes | Driver for [LTI-CIVIL][] library | -| [javacv][] | no | no | Driver for [JavaCV][] library | -| [vlcj][] | no | no | Driver for [VLCj][] library | -| [ffmpeg-cli][] | exp | no | Driver for [FFmpeg][] [CLI][] tool | +| [ipcam][] | yes | yes | Driver for IP / network camera | +| [gstreamer][] | yes | no | Driver for [GStreamer][] framework | +| [openimaj][] | yes | no | Driver for [OpenIMAJ][] framework | +| [v4l4j][] | yes | no | Driver for [V4L4j][] library | +| [jmf][] | yes | yes | Driver for [JMF][] / [FMJ][] frameworks | +| [lti-civil][] | yes | yes | Driver for [LTI-CIVIL][] library | +| [javacv][] | no | no | Driver for [JavaCV][] library | +| [vlcj][] | yes | yes | Driver for [vlcj][] library | +| [ffmpeg-cli][] | exp | no | Driver for [FFmpeg][] [CLI][] tool | * Central = Maven Central Repository * _exp_ = experimental @@ -190,7 +190,7 @@ I initially started working on Webcam Capture as a simple proof-of-concept after ## License -Copyright (C) 2012 - 2015 Bartosz Firyn +Copyright (C) 2012 - 2015 Bartosz Firyn (https://github.com/sarxos) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/webcam-capture-drivers/driver-vlcj/README.md b/webcam-capture-drivers/driver-vlcj/README.md index 240f5b04..a79276ce 100644 --- a/webcam-capture-drivers/driver-vlcj/README.md +++ b/webcam-capture-drivers/driver-vlcj/README.md @@ -1,18 +1,8 @@ # webcam-capture-driver-vlcj -This is capture driver which uses [vlcj](http://www.capricasoftware.co.uk/projects/vlcj/index.html) -library from [Caprica Software Limited](http://www.capricasoftware.co.uk/) -to gain access to the camera device. +This is capture driver which uses [vlcj](http://www.capricasoftware.co.uk/projects/vlcj/index.html) library from [Caprica Software Limited](http://www.capricasoftware.co.uk/) to gain access to the camera device. -Because vlcj saves every frame to the persistent storage (temporary directory on your hard drive) -before it is returned by the API method call, -the image capture rate is pretty small, indicated by tests to be around ~12 FPS, but it can pe -possibly higher or lower depending on the hardware used (e.g. different on hard drive, SSD and -flash memory). - -**NOTE!** On Windows one needs to provide list of webcam devices manually because -```vlclib``` does not implement video devices discovery on this platform (please see [this example](https://github.com/sarxos/webcam-capture/blob/master/webcam-capture-drivers/driver-vlcj/src/example/java/WebcamPanelForWindows.java) -to find out how this should be done). +**NOTE!** On Windows one needs to provide list of webcam devices manually because ```vlclib``` does not implement video devices discovery on this platform (please see [this example](https://github.com/sarxos/webcam-capture/blob/master/webcam-capture-drivers/driver-vlcj/src/example/java/WebcamPanelForWindows.java) to find out how this should be done). The vlcj library is distributed according to the terms of the [GPL](http://www.gnu.org/licenses/gpl.html) license. @@ -22,7 +12,7 @@ Not yet available. ## Capture Driver License -Copyright (C) 2012 - 2014 Bartosz Firyn +Copyright (C) 2012 - 2015 Bartosz Firyn Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/webcam-capture-drivers/driver-vlcj/pom.xml b/webcam-capture-drivers/driver-vlcj/pom.xml index fa34b6ea..da9b8965 100644 --- a/webcam-capture-drivers/driver-vlcj/pom.xml +++ b/webcam-capture-drivers/driver-vlcj/pom.xml @@ -31,6 +31,11 @@ vlcj 2.4.1 + + ch.qos.logback + logback-classic + provided + junit junit diff --git a/webcam-capture-drivers/driver-vlcj/src/example/java/VlcjDirectTest.java b/webcam-capture-drivers/driver-vlcj/src/example/java/VlcjDirectTest.java index fae1d378..053df725 100644 --- a/webcam-capture-drivers/driver-vlcj/src/example/java/VlcjDirectTest.java +++ b/webcam-capture-drivers/driver-vlcj/src/example/java/VlcjDirectTest.java @@ -75,6 +75,7 @@ private static BufferedImage convert(Memory[] buffers, BufferFormat format) { ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); ComponentSampleModel sampleModel = new ComponentSampleModel(dataType, width, height, pixelStride, scanlineStride, bgrBandOffsets); ComponentColorModel colorModel = new ComponentColorModel(colorSpace, bits, false, false, transparency, dataType); + DataBufferByte dataBuffer = new DataBufferByte(data, bytes.length, offsets); WritableRaster raster = Raster.createWritableRaster(sampleModel, dataBuffer, null); BufferedImage image = new BufferedImage(colorModel, raster, false, null); @@ -119,7 +120,6 @@ public void display(DirectMediaPlayer player, Memory[] buffers, BufferFormat for ":v4l2-width=320", ":v4l2-height=240", ":v4l2-fps=30", - ":v4l2-quality=20", ":v4l2-adev=none", }; diff --git a/webcam-capture-drivers/driver-vlcj/src/example/java/WebcamPanelExample.java b/webcam-capture-drivers/driver-vlcj/src/example/java/WebcamPanelExample.java index 7341656e..1a617a00 100644 --- a/webcam-capture-drivers/driver-vlcj/src/example/java/WebcamPanelExample.java +++ b/webcam-capture-drivers/driver-vlcj/src/example/java/WebcamPanelExample.java @@ -19,10 +19,10 @@ public static void main(String[] args) throws InterruptedException { WebcamPanel panel = new WebcamPanel(webcam); panel.setFPSDisplayed(true); + panel.setImageSizeDisplayed(true); JFrame window = new JFrame("Webcam Panel"); window.add(panel); - window.setResizable(false); window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); window.pack(); window.setVisible(true); diff --git a/webcam-capture-drivers/driver-vlcj/src/main/java/com/github/sarxos/webcam/ds/vlcj/VlcjDevice.java b/webcam-capture-drivers/driver-vlcj/src/main/java/com/github/sarxos/webcam/ds/vlcj/VlcjDevice.java index 60045986..64a190dd 100644 --- a/webcam-capture-drivers/driver-vlcj/src/main/java/com/github/sarxos/webcam/ds/vlcj/VlcjDevice.java +++ b/webcam-capture-drivers/driver-vlcj/src/main/java/com/github/sarxos/webcam/ds/vlcj/VlcjDevice.java @@ -1,8 +1,19 @@ package com.github.sarxos.webcam.ds.vlcj; import java.awt.Dimension; +import java.awt.Transparency; +import java.awt.color.ColorSpace; import java.awt.image.BufferedImage; -import java.util.List; +import java.awt.image.ComponentColorModel; +import java.awt.image.ComponentSampleModel; +import java.awt.image.DataBuffer; +import java.awt.image.DataBufferByte; +import java.awt.image.Raster; +import java.awt.image.WritableRaster; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,24 +21,169 @@ import uk.co.caprica.vlcj.medialist.MediaListItem; import uk.co.caprica.vlcj.player.MediaPlayer; import uk.co.caprica.vlcj.player.MediaPlayerFactory; +import uk.co.caprica.vlcj.player.direct.BufferFormat; +import uk.co.caprica.vlcj.player.direct.BufferFormatCallback; +import uk.co.caprica.vlcj.player.direct.DirectMediaPlayer; +import uk.co.caprica.vlcj.player.direct.RenderCallback; +import uk.co.caprica.vlcj.player.direct.format.RV32BufferFormat; import com.github.sarxos.webcam.WebcamDevice; +import com.github.sarxos.webcam.WebcamDevice.FPSSource; import com.github.sarxos.webcam.WebcamException; import com.github.sarxos.webcam.WebcamResolution; import com.github.sarxos.webcam.util.OsUtils; +import com.sun.jna.Memory; /** - * Capture driver which use vlcj project API to fetch images from camera. It - * should not be used when you need performance since vlcj saves snapshot image - * to disk prior it is returned - this affects performance and drop FPS rate - * down. In my case (HP Elitebook 8460p, 4 cores, 4 GB RAM, fast SSD disk) it - * was about ~12 FPS, which is very low when you compare it to the other capture - * drivers. - * + * This is capture driver which uses vlcj library to + * access webcam hardware. + * * @author Bartosz Firyn (SarXos) */ -public class VlcjDevice implements WebcamDevice { +public class VlcjDevice implements WebcamDevice, BufferFormatCallback, RenderCallback, FPSSource { + + /** + * This class is to convert vlcj {@link Memory} to {@link BufferedImage}. + * + * @author Bartosz Firyn (sarxos) + */ + private static class Converter { + + /** + * Converters are cached and created on demand for every width-height tuple. + */ + private static final Map CONVERTERS = new HashMap(); + + /** + * The buffer data type (buffer consist of 4 byte values for every pixel). + */ + final int dataType = DataBuffer.TYPE_BYTE; + + /** + * Number of bytes per pixel. + */ + final int pixelStride = 4; + + /** + * Number of bytes per line. + */ + final int scanlineStride; + + /** + * Band offsets for BGR components (B = 2, G = 1, R = 0). + */ + final int[] bgrBandOffsets = new int[] { 2, 1, 0 }; + + /** + * Number of bits per component. + */ + final int[] bits = { 8, 8, 8 }; + + /** + * Offset between pixels. + */ + final int[] offsets = new int[] { 0 }; + + /** + * Transparency type (opaque since there is no transparency in the image). + */ + final int transparency = Transparency.OPAQUE; + + /** + * Color space, a standard default color space for the Internet - sRGB. + */ + final ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB); + + /** + * Image sample model. + */ + final ComponentSampleModel sampleModel; + + /** + * Image color model. + */ + final ComponentColorModel colorModel = new ComponentColorModel(colorSpace, bits, false, false, transparency, dataType); + + private Converter(int width, int height) { + this.scanlineStride = width * pixelStride; + this.sampleModel = new ComponentSampleModel(dataType, width, height, pixelStride, scanlineStride, bgrBandOffsets); + } + + /** + * Get memory converter for given width-height tuple. + * + * @param width the image width + * @param height the image height + * @return Converter + */ + public static Converter getConverter(int width, int height) { + String key = key(width, height); + Converter converter = CONVERTERS.get(key); + if (converter == null) { + converter = new Converter(width, height); + CONVERTERS.put(key, converter); + } + return converter; + } + + /** + * Use width and height to create map key. + * + * @param width the image width + * @param height the image height + * @return Map key + */ + private static String key(int width, int height) { + return width + "x" + height; + } + + /** + * Converts {@link Memory} into {@link BufferedImage}. + * + * @param buffers the {@link Memory} buffers + * @param format the image format + * @return {@link BufferedImage} created from {@link Memory} + */ + public BufferedImage convert(Memory[] buffers, BufferFormat format) { + + // sanity, check if buffers is not empty + + if (buffers.length == 0) { + throw new RuntimeException("No memory elements found!"); + } + + // sanity check if buffer is not null + + final Memory memory = buffers[0]; + if (memory == null) { + throw new RuntimeException("Null memory!"); + } + + // transfer bytes into array + + final byte[] bytes = new byte[scanlineStride * format.getHeight()]; + final byte[][] data = new byte[][] { bytes }; + + memory + .getByteBuffer(0, memory.size()) + .get(bytes); + + // create image + + DataBufferByte dataBuffer = new DataBufferByte(data, bytes.length, offsets); + WritableRaster raster = Raster.createWritableRaster(sampleModel, dataBuffer, null); + BufferedImage image = new BufferedImage(colorModel, raster, false, null); + + // flush reconstructable resources to free memory + + image.flush(); + + // return image + + return image; + } + } /** * Logger. @@ -35,58 +191,84 @@ public class VlcjDevice implements WebcamDevice { private static final Logger LOG = LoggerFactory.getLogger(VlcjDevice.class); /** - * Artificial view sizes. The vlcj is not able to detect resolutions - * supported by the webcam. If you would like to detect resolutions and have - * high-quality with good performance images streaming, you should rather - * use gstreamer or v4lvj capture drivers. + * Artificial view sizes. The vlcj is not able to detect resolutions supported by the webcam. If + * you would like to detect resolutions and have high-quality with good performance images + * streaming, you should rather use gstreamer or v4lvj capture drivers. */ - private final static Dimension[] DIMENSIONS = new Dimension[] { + private final static Dimension[] RESOLUTIONS = new Dimension[] { WebcamResolution.QQVGA.getSize(), WebcamResolution.QVGA.getSize(), WebcamResolution.VGA.getSize(), }; + /** + * VLC args by Andrew Davison:
+ * http://fivedots.coe.psu.ac.th/~ad/jg/nui025/snapsWithoutJMF.pdf + */ private final static String[] VLC_ARGS = { + "--intf", "dummy", // no interface + "--vout", "dummy", // no video output + "--no-audio", // no audio decoding + "--no-video-title-show", // do not display title + "--no-stats", // no stats + "--no-sub-autodetect-file", // no subtitles + "--no-snapshot-preview", // no snapshot previews + "--live-caching=50", // reduce capture lag/latency + "--quiet", // turn off warnings + }; - // VLC args by Andrew Davison: - // http://fivedots.coe.psu.ac.th/~ad/jg/nui025/snapsWithoutJMF.pdf - - // no interface - "--intf", "dummy", + /** + * Used to calculate FPS. + */ + private long t1 = -1; - // no video output - "--vout", "dummy", + /** + * Used to calculate FPS. + */ + private long t2 = -1; - // no audio decoding - "--no-audio", + /** + * Image exchange reference. + */ + private final AtomicReference imageRef = new AtomicReference(); - // do not display title - "--no-video-title-show", + /** + * Current FPS. + */ + private final AtomicReference fps = new AtomicReference((double) 0); - // no stats - "--no-stats", + private final MediaListItem item; + private final MediaListItem sub; - // no subtitles - "--no-sub-autodetect-file", + /** + * Factory for media player instances. + */ + private MediaPlayerFactory factory; - // no snapshot previews - "--no-snapshot-preview", + /** + * Specification for a media player that provides direct access to the video frame data. + */ + private DirectMediaPlayer player; - // reduce capture lag/latency - "--live-caching=50", + /** + * Is in opening phase? + */ + private final AtomicBoolean opening = new AtomicBoolean(); - // turn off warnings - "--quiet", - }; + /** + * Is open? + */ + private final AtomicBoolean open = new AtomicBoolean(); - private Dimension size = null; - private MediaListItem item = null; - private MediaListItem sub = null; - private MediaPlayerFactory factory = null; - private MediaPlayer player = null; + /** + * Is disposed? + */ + private final AtomicBoolean disposed = new AtomicBoolean(); - private volatile boolean open = false; - private volatile boolean disposed = false; + /** + * Current resolution. + */ + private Dimension resolution = null; protected VlcjDevice(MediaListItem item) { @@ -94,12 +276,23 @@ protected VlcjDevice(MediaListItem item) { throw new IllegalArgumentException("Media list item cannot be null!"); } - List subs = item.subItems(); - this.item = item; - this.sub = subs.isEmpty() ? item : subs.get(0); + this.sub = item.subItems().isEmpty() ? item : item.subItems().get(0); + + LOG.trace("New device created {}", this); } + /** + * Get capture device protocol. This will be: + *
    + *
  • dshow:// for Windows
  • + *
  • qtcapture:// for Mac
  • + *
  • v4l2:// for linux
  • + *
+ * + * @return Capture device protocol + * @throws WebcamException in case when there is no support for given operating system + */ public String getCaptureDevice() { switch (OsUtils.getOS()) { case WIN: @@ -109,7 +302,7 @@ public String getCaptureDevice() { case NIX: return "v4l2://"; default: - throw new RuntimeException("Capture device not supported on " + OsUtils.getOS()); + throw new WebcamException("Capture device not supported on " + OsUtils.getOS()); } } @@ -141,43 +334,61 @@ public String toString() { @Override public Dimension[] getResolutions() { - return DIMENSIONS; + return RESOLUTIONS; } @Override public Dimension getResolution() { - return size; + return resolution; } @Override - public void setResolution(Dimension size) { - this.size = size; + public void setResolution(Dimension resolution) { + this.resolution = resolution; } @Override public BufferedImage getImage() { - if (!open) { + + if (!open.get()) { throw new WebcamException("Cannot get image, webcam device is not open"); } - return player.getSnapshot(size.width, size.height); + + BufferedImage image = null; + + // wait for image + + while ((image = imageRef.getAndSet(null)) == null) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + return null; + } + } + + return image; } @Override public synchronized void open() { - if (disposed) { - LOG.warn("Cannot open device because it has been disposed"); + if (disposed.get()) { + LOG.warn("Cannot open device because it has been already disposed"); return; } - if (open) { + if (open.get()) { return; } - LOG.info("Opening webcam device"); + if (!opening.compareAndSet(false, true)) { + return; + } + + factory = new MediaPlayerFactory(VLC_ARGS); + player = factory.newDirectMediaPlayer(this, this); - factory = getFactory(); - player = factory.newHeadlessMediaPlayer(); + LOG.info("Opening webcam device"); String[] options = null; @@ -186,27 +397,26 @@ public synchronized void open() { LOG.debug("Open VLC device {}", getName()); options = new String[] { ":dshow-vdev=" + getName(), - ":dshow-size=" + size.width + "x" + size.height, + ":dshow-size=" + resolution.width + "x" + resolution.height, ":dshow-adev=none", // no audio device }; break; case NIX: LOG.debug("Open VLC device {}", getVDevice()); options = new String[] { - ":v4l-vdev=" + getVDevice(), - ":v4l-width=" + size.width, - ":v4l-height=" + size.height, - ":v4l-fps=30", - ":v4l-quality=20", - ":v4l-adev=none", // no audio device + ":v4l2-vdev=" + getVDevice(), + ":v4l2-width=" + resolution.width, + ":v4l2-height=" + resolution.height, + ":v4l2-fps=30", + ":v4l2-adev=none", // no audio device }; break; case OSX: LOG.debug("Open VLC device {}", getVDevice()); options = new String[] { ":qtcapture-vdev=" + getVDevice(), - ":qtcapture-width=" + size.width, - ":qtcapture-height=" + size.height, + ":qtcapture-width=" + resolution.width, + ":qtcapture-height=" + resolution.height, ":qtcapture-adev=none", // no audio device }; break; @@ -214,68 +424,96 @@ public synchronized void open() { player.startMedia(getMRL(), options); - // wait for images + // wait for the first image - int max = 0; - do { + long wait = 100; // ms + int max = 100; + int count = 0; - BufferedImage im = player.getSnapshot(size.width, size.height); - if (im != null && im.getWidth() > 0) { - open = true; - LOG.info("Webcam device is now open: " + getName()); - return; - } + while (imageRef.get() == null) { try { - Thread.sleep(1000); + Thread.sleep(wait); } catch (InterruptedException e) { return; } - } while (max++ < 10); + if (count++ > max) { + + LOG.error("Unable to open in {} ms", wait * max); + opening.set(false); + + return; + } + } - open = false; + open.set(true); + opening.set(false); } @Override public synchronized void close() { - if (!open) { - return; - } - - LOG.info("Closing"); + LOG.info("Closing device {}", this); - if (player != null) { - LOG.debug("Releasing player"); - player.release(); + if (open.compareAndSet(true, false)) { + player.stop(); } - if (factory != null) { - LOG.debug("Releasing player"); - factory.release(); - } - - open = false; } @Override public synchronized void dispose() { - disposed = true; + + if (!disposed.compareAndSet(false, true)) { + return; + } + + LOG.debug("Release resources (player={}, factory={})", player, factory); + + player.release(); + factory.release(); } @Override public boolean isOpen() { - return open; + return open.get(); } public MediaPlayer getPlayer() { return player; } - public MediaPlayerFactory getFactory() { - if (factory == null) { - factory = new MediaPlayerFactory(VLC_ARGS); + @Override + public BufferFormat getBufferFormat(int width, int height) { + return new RV32BufferFormat(width, height); + } + + @Override + public void display(DirectMediaPlayer player, Memory[] buffers, BufferFormat format) { + + LOG.trace("Direct media player display invoked with format {}", format); + + // convert memory to image + + Converter converter = Converter.getConverter(format.getWidth(), format.getHeight()); + BufferedImage image = converter.convert(buffers, format); + imageRef.set(image); + + // calculate fps + + if (t1 == -1 || t2 == -1) { + t1 = System.currentTimeMillis(); + t2 = System.currentTimeMillis(); } - return factory; + + t1 = t2; + t2 = System.currentTimeMillis(); + + fps.set((4 * fps.get() + 1000 / (t2 - t1 + 1)) / 5); + } + + @Override + public double getFPS() { + return fps.get(); } -} +} \ No newline at end of file diff --git a/webcam-capture-drivers/driver-vlcj/src/main/java/com/github/sarxos/webcam/ds/vlcj/VlcjDriver.java b/webcam-capture-drivers/driver-vlcj/src/main/java/com/github/sarxos/webcam/ds/vlcj/VlcjDriver.java index fe8a6b95..8502f7dc 100644 --- a/webcam-capture-drivers/driver-vlcj/src/main/java/com/github/sarxos/webcam/ds/vlcj/VlcjDriver.java +++ b/webcam-capture-drivers/driver-vlcj/src/main/java/com/github/sarxos/webcam/ds/vlcj/VlcjDriver.java @@ -4,6 +4,9 @@ import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import uk.co.caprica.vlcj.discovery.NativeDiscovery; import uk.co.caprica.vlcj.medialist.MediaList; import uk.co.caprica.vlcj.medialist.MediaListItem; @@ -26,17 +29,17 @@ */ public class VlcjDriver implements WebcamDriver, WebcamDiscoverySupport { + /** + * I'm the logger. + */ + private static final Logger LOG = LoggerFactory.getLogger(VlcjDriver.class); + static { if ("true".equals(System.getProperty("webcam.debug"))) { System.setProperty("vlcj.log", "DEBUG"); } } - /** - * Default webcam discovery scan interval in milliseconds. - */ - public static final long DEFAULT_SCAN_INTERVAL = 3000; - /** * Are natives initialized. */ @@ -92,6 +95,8 @@ protected static void initialize(boolean load) { @Override public List getDevices() { + LOG.debug("Searching devices"); + if (OsUtils.getOS() == OsUtils.WIN) { System.err.println("WARNING: VLCj does not support webcam devices discovery on Windows platform"); } @@ -112,6 +117,9 @@ public List getDevices() { List videoDevices = videoDeviceList.items(); for (MediaListItem item : videoDevices) { + + LOG.debug("Found item {}", item); + devices.add(mediaListItemToDevice(item)); } diff --git a/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamDiscoverySupport.java b/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamDiscoverySupport.java index afd0158a..77185730 100644 --- a/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamDiscoverySupport.java +++ b/webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamDiscoverySupport.java @@ -1,28 +1,31 @@ package com.github.sarxos.webcam; /** - * This interface should be implemented by all webcam drivers which would like - * to support webcam devices discovery mechanism. - * + * This interface should be implemented by all webcam drivers which would like to support webcam + * devices discovery mechanism. + * * @author Bartosz Firyn (SarXos) */ public interface WebcamDiscoverySupport { /** - * Get interval between next discovery scans. Time interval is given in - * milliseconds. - * + * Default webcam discovery scan interval in milliseconds. + */ + public static final long DEFAULT_SCAN_INTERVAL = 3000; + + /** + * Get interval between next discovery scans. Time interval is given in milliseconds. + * * @return Time interval between next scans */ long getScanInterval(); /** - * Check if scan is possible. In some cases, even if driver support devices - * discovery, there can be a situation when due to various factors, scan - * cannot be executed (e.g. devices are busy, network is unavailable, - * devices registry not responding, etc). In general this method should + * Check if scan is possible. In some cases, even if driver support devices discovery, there can + * be a situation when due to various factors, scan cannot be executed (e.g. devices are busy, + * network is unavailable, devices registry not responding, etc). In general this method should * return true. - * + * * @return True if scan possible, false otherwise */ boolean isScanPossible(); 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 ebe52704..cde59c1c 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 @@ -19,10 +19,9 @@ /** - * Default build-in webcam driver based on natives from OpenIMAJ framework. It - * can be widely used on various systems - Mac OS, Linux (x86, x64, ARM), - * Windows (win32, win64). - * + * Default build-in webcam driver based on natives from OpenIMAJ framework. It can be widely used on + * various systems - Mac OS, Linux (x86, x64, ARM), Windows (win32, win64). + * * @author Bartosz Firyn (SarXos) */ public class WebcamDefaultDriver implements WebcamDriver, WebcamDiscoverySupport { @@ -68,7 +67,7 @@ public GetDevicesTask(WebcamDriver driver) { /** * Return camera devices. - * + * * @param grabber the native grabber to use for search * @return Camera devices. */ @@ -135,7 +134,7 @@ public List getDevices() { @Override public long getScanInterval() { - return 3000; + return DEFAULT_SCAN_INTERVAL; } @Override