From ee165b50ff1acfa1658ad3785cdc6eba294897ef Mon Sep 17 00:00:00 2001 From: Eduardo Ramos Date: Wed, 21 Dec 2022 13:26:13 +0100 Subject: [PATCH] Add support to update image only when there is a new frame --- README.md | 52 +++++++++++-------- .../eduramiba/webcamcapture/TestDriver.java | 13 ++++- .../WebcamDeviceWithBufferOperations.java | 8 +-- .../avfoundation/driver/AVFVideoDevice.java | 29 ++++++++--- .../CaptureManagerFrameGrabberSession.java | 18 ++++++- .../CaptureManagerVideoDevice.java | 19 ++++--- 6 files changed, 95 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 4ed52d6..1ec56be 100644 --- a/README.md +++ b/README.md @@ -53,30 +53,39 @@ public class TestDriver extends Application { root.getChildren().add(imageView); Webcam.getWebcams().stream() - .findFirst() - .ifPresent((final Webcam camera) -> { - final WebcamDevice device = camera.getDevice(); - LOG.info("Found camera: {}, device = {}", camera, device); + .findFirst() + .ifPresent((final Webcam camera) -> { + final WebcamDevice device = camera.getDevice(); + LOG.info("Found camera: {}, device = {}", camera, device); - final int width = device.getResolution().width; - final int height = device.getResolution().height; - final WritableImage fxImage = new WritableImage(width, height); - Platform.runLater(() -> { - imageView.setImage(fxImage); - stage.setWidth(width); - stage.setHeight(height); - stage.centerOnScreen(); + final int width = device.getResolution().width; + final int height = device.getResolution().height; + final WritableImage fxImage = new WritableImage(width, height); + Platform.runLater(() -> { + imageView.setImage(fxImage); + stage.setWidth(width); + stage.setHeight(height); + stage.centerOnScreen(); + }); + + camera.getLock().disable(); + camera.open(); + if (device instanceof WebcamDeviceWithBufferOperations) { + final WebcamDeviceWithBufferOperations dev = ((WebcamDeviceWithBufferOperations) device); + EXECUTOR.scheduleAtFixedRate(new Runnable() { + private long lastFrameTimestamp = -1; + + @Override + public void run() { + if (dev.updateFXIMage(fxImage, lastFrameTimestamp)) { + lastFrameTimestamp = dev.getLastFrameTimestamp(); + } + + } + }, 0, 16, TimeUnit.MILLISECONDS); + } }); - camera.getLock().disable(); - camera.open(); - if (device instanceof WebcamDeviceWithBufferOperations) { - EXECUTOR.scheduleAtFixedRate(() -> { - ((WebcamDeviceWithBufferOperations) device).updateFXIMage(fxImage); - }, 0, 16, TimeUnit.MILLISECONDS); - } - }); - stage.setOnCloseRequest(t -> { Platform.exit(); System.exit(0); @@ -89,7 +98,6 @@ public class TestDriver extends Application { stage.show(); } } - ``` # Future work diff --git a/src/main/java/com/github/eduramiba/webcamcapture/TestDriver.java b/src/main/java/com/github/eduramiba/webcamcapture/TestDriver.java index 2811130..b99695e 100644 --- a/src/main/java/com/github/eduramiba/webcamcapture/TestDriver.java +++ b/src/main/java/com/github/eduramiba/webcamcapture/TestDriver.java @@ -54,8 +54,17 @@ public class TestDriver extends Application { camera.getLock().disable(); camera.open(); if (device instanceof WebcamDeviceWithBufferOperations) { - EXECUTOR.scheduleAtFixedRate(() -> { - ((WebcamDeviceWithBufferOperations) device).updateFXIMage(fxImage); + final WebcamDeviceWithBufferOperations dev = ((WebcamDeviceWithBufferOperations) device); + EXECUTOR.scheduleAtFixedRate(new Runnable() { + private long lastFrameTimestamp = -1; + + @Override + public void run() { + if (dev.updateFXIMage(fxImage, lastFrameTimestamp)) { + lastFrameTimestamp = dev.getLastFrameTimestamp(); + } + + } }, 0, 16, TimeUnit.MILLISECONDS); } }); diff --git a/src/main/java/com/github/eduramiba/webcamcapture/drivers/WebcamDeviceWithBufferOperations.java b/src/main/java/com/github/eduramiba/webcamcapture/drivers/WebcamDeviceWithBufferOperations.java index 10b06e5..2fa6f2d 100644 --- a/src/main/java/com/github/eduramiba/webcamcapture/drivers/WebcamDeviceWithBufferOperations.java +++ b/src/main/java/com/github/eduramiba/webcamcapture/drivers/WebcamDeviceWithBufferOperations.java @@ -7,9 +7,11 @@ import javafx.scene.image.WritableImage; public interface WebcamDeviceWithBufferOperations extends WebcamDevice { - BufferedImage getImage(final ByteBuffer byteBuffer); + BufferedImage getImage(ByteBuffer byteBuffer); - boolean updateFXIMage(final WritableImage writableImage, final ByteBuffer byteBuffer); + boolean updateFXIMage(WritableImage writableImage); - boolean updateFXIMage(final WritableImage writableImage); + boolean updateFXIMage(WritableImage writableImage, long lastFrameTimestamp); + + long getLastFrameTimestamp(); } diff --git a/src/main/java/com/github/eduramiba/webcamcapture/drivers/avfoundation/driver/AVFVideoDevice.java b/src/main/java/com/github/eduramiba/webcamcapture/drivers/avfoundation/driver/AVFVideoDevice.java index 5b9e39d..b334ca7 100644 --- a/src/main/java/com/github/eduramiba/webcamcapture/drivers/avfoundation/driver/AVFVideoDevice.java +++ b/src/main/java/com/github/eduramiba/webcamcapture/drivers/avfoundation/driver/AVFVideoDevice.java @@ -36,6 +36,7 @@ public class AVFVideoDevice implements WebcamDevice, WebcamDevice.FPSSource, Web private ByteBuffer imgBuffer = null; private byte[] arrayByteBuffer = null; private BufferedImage bufferedImage = null; + private long lastFrameTimestamp = -1; public AVFVideoDevice(final int deviceIndex, final String id, final String name, final Collection resolutions) { this.deviceIndex = deviceIndex; @@ -159,6 +160,11 @@ public class AVFVideoDevice implements WebcamDevice, WebcamDevice.FPSSource, Web return open; } + @Override + public long getLastFrameTimestamp() { + return lastFrameTimestamp; + } + public static final int MAX_FPS = 30; @Override @@ -217,18 +223,23 @@ public class AVFVideoDevice implements WebcamDevice, WebcamDevice.FPSSource, Web } @Override - public synchronized boolean updateFXIMage(WritableImage writableImage) { + public boolean updateFXIMage(WritableImage writableImage) { + return updateFXIMage(writableImage, -1); + } + + @Override + public synchronized boolean updateFXIMage(final WritableImage writableImage, final long lastFrameTimestamp) { + return updateFXIMage(writableImage, imgBuffer, lastFrameTimestamp); + } + + private boolean updateFXIMage(final WritableImage writableImage, final ByteBuffer byteBuffer, final long lastFrameTimestamp) { if (!isOpen()) { return false; } updateBuffer(); - return updateFXIMage(writableImage, imgBuffer); - } - - public boolean updateFXIMage(final WritableImage writableImage, final ByteBuffer byteBuffer) { - if (!isOpen()) { + if (this.lastFrameTimestamp <= lastFrameTimestamp) { return false; } @@ -249,9 +260,11 @@ public class AVFVideoDevice implements WebcamDevice, WebcamDevice.FPSSource, Web private void updateBuffer() { if (LibVideoCapture.INSTANCE.vcavf_has_new_frame(deviceIndex)) { - LibVideoCapture.INSTANCE.vcavf_grab_frame( + if (LibVideoCapture.INSTANCE.vcavf_grab_frame( deviceIndex, - Native.getDirectBufferPointer(imgBuffer), imgBuffer.capacity()); + Native.getDirectBufferPointer(imgBuffer), imgBuffer.capacity())) { + lastFrameTimestamp = System.currentTimeMillis(); + }; } } diff --git a/src/main/java/com/github/eduramiba/webcamcapture/drivers/capturemanager/CaptureManagerFrameGrabberSession.java b/src/main/java/com/github/eduramiba/webcamcapture/drivers/capturemanager/CaptureManagerFrameGrabberSession.java index 6cce04b..c22c511 100644 --- a/src/main/java/com/github/eduramiba/webcamcapture/drivers/capturemanager/CaptureManagerFrameGrabberSession.java +++ b/src/main/java/com/github/eduramiba/webcamcapture/drivers/capturemanager/CaptureManagerFrameGrabberSession.java @@ -37,6 +37,7 @@ public class CaptureManagerFrameGrabberSession { private int videoWidth = -1; private int videoHeight = -1; private int bufferSizeBytes = -1; + private long lastFrameTimestamp = -1; public boolean init( final CaptureManagerSource source, @@ -223,6 +224,10 @@ public class CaptureManagerFrameGrabberSession { session.closeSession(); } + public long getLastFrameTimestamp() { + return lastFrameTimestamp; + } + public synchronized BufferedImage toBufferedImage() { if (!isOpen()) { return null; @@ -249,7 +254,7 @@ public class CaptureManagerFrameGrabberSession { return bufferedImage; } - public synchronized boolean updateFXIMage(final WritableImage writableImage) { + public synchronized boolean updateFXIMage(final WritableImage writableImage, long lastFrameTimestamp) { if (!isOpen()) { return false; } @@ -260,6 +265,10 @@ public class CaptureManagerFrameGrabberSession { return false; } + if (this.lastFrameTimestamp <= lastFrameTimestamp) { + return false; + } + updateFXIMage(writableImage, directBuffer); return true; @@ -285,7 +294,12 @@ public class CaptureManagerFrameGrabberSession { public synchronized int updateDirectBuffer() { - return sampleGrabberCall.readData(directBuffer); + final int readSize = sampleGrabberCall.readData(directBuffer); + if (readSize > 0) { + lastFrameTimestamp = System.currentTimeMillis(); + } + + return readSize; } public boolean isOpen() { diff --git a/src/main/java/com/github/eduramiba/webcamcapture/drivers/capturemanager/CaptureManagerVideoDevice.java b/src/main/java/com/github/eduramiba/webcamcapture/drivers/capturemanager/CaptureManagerVideoDevice.java index d441783..1fddda4 100644 --- a/src/main/java/com/github/eduramiba/webcamcapture/drivers/capturemanager/CaptureManagerVideoDevice.java +++ b/src/main/java/com/github/eduramiba/webcamcapture/drivers/capturemanager/CaptureManagerVideoDevice.java @@ -144,6 +144,15 @@ public class CaptureManagerVideoDevice implements WebcamDevice, WebcamDevice.FPS return session != null && session.isOpen(); } + @Override + public long getLastFrameTimestamp() { + if (isOpen()) { + return session.getLastFrameTimestamp(); + } + + return -1; + } + private static Pair findBestMediaTypeInStreams( final Collection videoStreams, final Dimension resolution @@ -231,9 +240,9 @@ public class CaptureManagerVideoDevice implements WebcamDevice, WebcamDevice.FPS } @Override - public boolean updateFXIMage(WritableImage writableImage, ByteBuffer byteBuffer) { + public boolean updateFXIMage(final WritableImage writableImage, final long lastFrameTimestamp) { if (isOpen()) { - session.updateFXIMage(writableImage, byteBuffer); + session.updateFXIMage(writableImage, lastFrameTimestamp); return true; } @@ -242,11 +251,7 @@ public class CaptureManagerVideoDevice implements WebcamDevice, WebcamDevice.FPS @Override public boolean updateFXIMage(WritableImage writableImage) { - if (isOpen()) { - return session.updateFXIMage(writableImage); - } - - return false; + return updateFXIMage(writableImage, -1); } @Override