Skip to content

Commit 727db46

Browse files
committed
Ability to specify ImageProvider in WebcamPanel, refs #599
1 parent c43160e commit 727db46

File tree

2 files changed

+108
-44
lines changed

2 files changed

+108
-44
lines changed

webcam-capture/src/main/java/com/github/sarxos/webcam/Webcam.java

Lines changed: 59 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -237,10 +237,9 @@ public boolean open() {
237237
}
238238

239239
/**
240-
* Open the webcam in either blocking (synchronous) or non-blocking
241-
* (asynchronous) mode. If the non-blocking mode is enabled the
242-
* DefaultDelayCalculator is used for calculating delay between two image
243-
* fetching.
240+
* Open the webcam in either blocking (synchronous) or non-blocking (asynchronous) mode. If the
241+
* non-blocking mode is enabled the DefaultDelayCalculator is used for calculating delay between
242+
* two image fetching.
244243
*
245244
* @param async true for non-blocking mode, false for blocking
246245
* @return True if webcam has been open, false otherwise
@@ -250,36 +249,30 @@ public boolean open() {
250249
public boolean open(boolean async) {
251250
return open(async, new DefaultDelayCalculator());
252251
}
253-
252+
254253
/**
255-
* Open the webcam in either blocking (synchronous) or non-blocking
256-
* (asynchronous) mode.The difference between those two modes lies in the
257-
* image acquisition mechanism.<br>
254+
* Open the webcam in either blocking (synchronous) or non-blocking (asynchronous) mode.The
255+
* difference between those two modes lies in the image acquisition mechanism.<br>
258256
* <br>
259-
* In blocking mode, when user calls {@link #getImage()} method, device is
260-
* being queried for new image buffer and user have to wait for it to be
261-
* available.<br>
257+
* In blocking mode, when user calls {@link #getImage()} method, device is being queried for new
258+
* image buffer and user have to wait for it to be available.<br>
262259
* <br>
263-
* In non-blocking mode, there is a special thread running in the background
264-
* which constantly fetch new images and cache them internally for further
265-
* use. This cached instance is returned every time when user request new
266-
* image. Because of that it can be used when timeing is very important,
267-
* because all users calls for new image do not have to wait on device
268-
* response. By using this mode user should be aware of the fact that in
269-
* some cases, when two consecutive calls to get new image are executed more
270-
* often than webcam device can serve them, the same image instance will be
271-
* returned. User should use {@link #isImageNew()} method to distinguish if
272-
* returned image is not the same as the previous one. <br>
273-
* The background thread uses implementation of DelayCalculator interface to
274-
* calculate delay between two image fetching. Custom implementation may be
275-
* specified as parameter of this method. If the non-blocking mode is
276-
* enabled and no DelayCalculator is specified, DefaultDelayCalculator will
277-
* be used.
260+
* In non-blocking mode, there is a special thread running in the background which constantly
261+
* fetch new images and cache them internally for further use. This cached instance is returned
262+
* every time when user request new image. Because of that it can be used when timeing is very
263+
* important, because all users calls for new image do not have to wait on device response. By
264+
* using this mode user should be aware of the fact that in some cases, when two consecutive
265+
* calls to get new image are executed more often than webcam device can serve them, the same
266+
* image instance will be returned. User should use {@link #isImageNew()} method to distinguish
267+
* if returned image is not the same as the previous one. <br>
268+
* The background thread uses implementation of DelayCalculator interface to calculate delay
269+
* between two image fetching. Custom implementation may be specified as parameter of this
270+
* method. If the non-blocking mode is enabled and no DelayCalculator is specified,
271+
* DefaultDelayCalculator will be used.
278272
*
279273
* @param async true for non-blocking mode, false for blocking
280-
* @param delayCalculator responsible for calculating delay between two
281-
* image fetching in non-blocking mode; It's ignored in blocking
282-
* mode.
274+
* @param delayCalculator responsible for calculating delay between two image fetching in
275+
* non-blocking mode; It's ignored in blocking mode.
283276
* @return True if webcam has been open
284277
* @throws WebcamException when something went wrong
285278
*/
@@ -724,12 +717,25 @@ public ByteBuffer getImageBytes() {
724717
assert driver != null;
725718
assert device != null;
726719

720+
long t1 = 0;
721+
long t2 = 0;
722+
727723
// some devices can support direct image buffers, and for those call
728724
// processor task, and for those which does not support direct image
729725
// buffers, just convert image to RGB byte array
730726

731727
if (device instanceof BufferAccess) {
732-
return new WebcamGetBufferTask(driver, device).getBuffer();
728+
t1 = System.currentTimeMillis();
729+
try {
730+
return new WebcamGetBufferTask(driver, device).getBuffer();
731+
} finally {
732+
t2 = System.currentTimeMillis();
733+
if (device instanceof WebcamDevice.FPSSource) {
734+
fps = ((WebcamDevice.FPSSource) device).getFPS();
735+
} else {
736+
fps = (4 * fps + 1000 / (t2 - t1 + 1)) / 5;
737+
}
738+
}
733739
} else {
734740
throw new IllegalStateException(String.format("Driver %s does not support buffer access", driver.getClass().getName()));
735741
}
@@ -755,21 +761,34 @@ public void getImageBytes(ByteBuffer target) {
755761
assert driver != null;
756762
assert device != null;
757763

764+
long t1 = 0;
765+
long t2 = 0;
766+
758767
// some devices can support direct image buffers, and for those call
759768
// processor task, and for those which does not support direct image
760769
// buffers, just convert image to RGB byte array
761770

762771
if (device instanceof BufferAccess) {
763-
new WebcamReadBufferTask(driver, device, target).readBuffer();
772+
t1 = System.currentTimeMillis();
773+
try {
774+
new WebcamReadBufferTask(driver, device, target).readBuffer();
775+
} finally {
776+
t2 = System.currentTimeMillis();
777+
if (device instanceof WebcamDevice.FPSSource) {
778+
fps = ((WebcamDevice.FPSSource) device).getFPS();
779+
} else {
780+
fps = (4 * fps + 1000 / (t2 - t1 + 1)) / 5;
781+
}
782+
}
764783
} else {
765784
throw new IllegalStateException(String.format("Driver %s does not support buffer access", driver.getClass().getName()));
766785
}
767786
}
768787

769788
/**
770-
* If the underlying device implements Configurable interface, specified
771-
* parameters are passed to it. May be called before the open method or
772-
* later in dependence of the device implementation.
789+
* If the underlying device implements Configurable interface, specified parameters are passed
790+
* to it. May be called before the open method or later in dependence of the device
791+
* implementation.
773792
*
774793
* @param parameters - Map of parameters changing device defaults
775794
* @see Configurable
@@ -782,7 +801,7 @@ public void setParameters(Map<String, ?> parameters) {
782801
LOG.debug("Webcam device {} is not configurable", device);
783802
}
784803
}
785-
804+
786805
/**
787806
* Is webcam ready to be read.
788807
*
@@ -1253,9 +1272,8 @@ public WebcamLock getLock() {
12531272
}
12541273

12551274
/**
1256-
* Shutdown webcam framework. This method should be used <b>ONLY</b> when you
1257-
* are exiting JVM, but please <b>do not invoke it</b> if you really don't
1258-
* need to.
1275+
* Shutdown webcam framework. This method should be used <b>ONLY</b> when you are exiting JVM,
1276+
* but please <b>do not invoke it</b> if you really don't need to.
12591277
*/
12601278
protected static void shutdown() {
12611279

@@ -1270,9 +1288,9 @@ protected static void shutdown() {
12701288
}
12711289

12721290
/**
1273-
* Return webcam with given name or null if no device with given name has
1274-
* been found. Please note that specific webcam name may depend on the order
1275-
* it was connected to the USB port (e.g. /dev/video0 vs /dev/video1).
1291+
* Return webcam with given name or null if no device with given name has been found. Please
1292+
* note that specific webcam name may depend on the order it was connected to the USB port (e.g.
1293+
* /dev/video0 vs /dev/video1).
12761294
*
12771295
* @param name the webcam name
12781296
* @return Webcam with given name or null if not found

webcam-capture/src/main/java/com/github/sarxos/webcam/WebcamPanel.java

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,39 @@ public enum DrawMode {
7474
FIT,
7575
}
7676

77+
/**
78+
* This interface can be used to supply {@link BufferedImage} to {@link WebcamPanel}.
79+
*
80+
* @author Bartosz Firyn (sarxos)
81+
*/
82+
public interface ImageSupplier {
83+
84+
/**
85+
* @return {@link BufferedImage} to be displayed in {@link WebcamPanel}
86+
*/
87+
public BufferedImage get();
88+
}
89+
90+
/**
91+
* Default implementation of {@link ImageSupplier} used in {@link WebcamPanel}. It invokes
92+
* {@link Webcam#getImage()} and return {@link BufferedImage}.
93+
*
94+
* @author Bartosz Firyn (sarxos)
95+
*/
96+
private static class DefaultImageSupplier implements ImageSupplier {
97+
98+
private final Webcam webcam;
99+
100+
public DefaultImageSupplier(Webcam webcam) {
101+
this.webcam = webcam;
102+
}
103+
104+
@Override
105+
public BufferedImage get() {
106+
return webcam.getImage();
107+
}
108+
}
109+
77110
/**
78111
* Interface of the painter used to draw image in panel.
79112
*
@@ -156,9 +189,8 @@ public void paintPanel(WebcamPanel owner, Graphics2D g2) {
156189
g2.drawLine(0, 0, getWidth(), getHeight());
157190
g2.drawLine(0, getHeight(), getWidth(), 0);
158191

159-
160192
String str;
161-
193+
162194
final String strInitDevice = rb.getString("INITIALIZING_DEVICE");
163195
final String strNoImage = rb.getString("NO_IMAGE");
164196
final String strDeviceError = rb.getString("DEVICE_ERROR");
@@ -562,7 +594,7 @@ private void update() {
562594

563595
// get new image from webcam
564596

565-
BufferedImage tmp = webcam.getImage();
597+
BufferedImage tmp = supplier.get();
566598
boolean repaint = true;
567599

568600
if (tmp != null) {
@@ -626,6 +658,8 @@ private void update() {
626658
*/
627659
private final Webcam webcam;
628660

661+
private final ImageSupplier supplier;
662+
629663
/**
630664
* Repainter is used to fetch images from camera and force panel repaint when image is ready.
631665
*/
@@ -715,6 +749,10 @@ public WebcamPanel(Webcam webcam, boolean start) {
715749
* @see WebcamPanel#setFillArea(boolean)
716750
*/
717751
public WebcamPanel(Webcam webcam, Dimension size, boolean start) {
752+
this(webcam, size, start, new DefaultImageSupplier(webcam));
753+
}
754+
755+
public WebcamPanel(Webcam webcam, Dimension size, boolean start, ImageSupplier supplier) {
718756

719757
if (webcam == null) {
720758
throw new IllegalArgumentException(String.format("Webcam argument in %s constructor cannot be null!", getClass().getSimpleName()));
@@ -723,6 +761,7 @@ public WebcamPanel(Webcam webcam, Dimension size, boolean start) {
723761
this.defaultSize = size;
724762
this.webcam = webcam;
725763
this.updater = new ImageUpdater();
764+
this.supplier = supplier;
726765
this.rb = WebcamUtils.loadRB(WebcamPanel.class, getLocale());
727766

728767
setDoubleBuffered(true);
@@ -1178,4 +1217,11 @@ public void setMirrored(boolean mirrored) {
11781217
public Webcam getWebcam() {
11791218
return webcam;
11801219
}
1220+
1221+
/**
1222+
* @return {@link BufferedImage} displayed on {@link WebcamPanel}
1223+
*/
1224+
public BufferedImage getImage() {
1225+
return image;
1226+
}
11811227
}

0 commit comments

Comments
 (0)