Add support to update image only when there is a new frame

This commit is contained in:
Eduardo Ramos 2022-12-21 13:26:13 +01:00
parent 0704fc70a3
commit ee165b50ff
6 changed files with 95 additions and 44 deletions

View File

@ -53,30 +53,39 @@ public class TestDriver extends Application {
root.getChildren().add(imageView); root.getChildren().add(imageView);
Webcam.getWebcams().stream() Webcam.getWebcams().stream()
.findFirst() .findFirst()
.ifPresent((final Webcam camera) -> { .ifPresent((final Webcam camera) -> {
final WebcamDevice device = camera.getDevice(); final WebcamDevice device = camera.getDevice();
LOG.info("Found camera: {}, device = {}", camera, device); LOG.info("Found camera: {}, device = {}", camera, device);
final int width = device.getResolution().width; final int width = device.getResolution().width;
final int height = device.getResolution().height; final int height = device.getResolution().height;
final WritableImage fxImage = new WritableImage(width, height); final WritableImage fxImage = new WritableImage(width, height);
Platform.runLater(() -> { Platform.runLater(() -> {
imageView.setImage(fxImage); imageView.setImage(fxImage);
stage.setWidth(width); stage.setWidth(width);
stage.setHeight(height); stage.setHeight(height);
stage.centerOnScreen(); 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 -> { stage.setOnCloseRequest(t -> {
Platform.exit(); Platform.exit();
System.exit(0); System.exit(0);
@ -89,7 +98,6 @@ public class TestDriver extends Application {
stage.show(); stage.show();
} }
} }
``` ```
# Future work # Future work

View File

@ -54,8 +54,17 @@ public class TestDriver extends Application {
camera.getLock().disable(); camera.getLock().disable();
camera.open(); camera.open();
if (device instanceof WebcamDeviceWithBufferOperations) { if (device instanceof WebcamDeviceWithBufferOperations) {
EXECUTOR.scheduleAtFixedRate(() -> { final WebcamDeviceWithBufferOperations dev = ((WebcamDeviceWithBufferOperations) device);
((WebcamDeviceWithBufferOperations) device).updateFXIMage(fxImage); EXECUTOR.scheduleAtFixedRate(new Runnable() {
private long lastFrameTimestamp = -1;
@Override
public void run() {
if (dev.updateFXIMage(fxImage, lastFrameTimestamp)) {
lastFrameTimestamp = dev.getLastFrameTimestamp();
}
}
}, 0, 16, TimeUnit.MILLISECONDS); }, 0, 16, TimeUnit.MILLISECONDS);
} }
}); });

View File

@ -7,9 +7,11 @@ import javafx.scene.image.WritableImage;
public interface WebcamDeviceWithBufferOperations extends WebcamDevice { 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();
} }

View File

@ -36,6 +36,7 @@ public class AVFVideoDevice implements WebcamDevice, WebcamDevice.FPSSource, Web
private ByteBuffer imgBuffer = null; private ByteBuffer imgBuffer = null;
private byte[] arrayByteBuffer = null; private byte[] arrayByteBuffer = null;
private BufferedImage bufferedImage = null; private BufferedImage bufferedImage = null;
private long lastFrameTimestamp = -1;
public AVFVideoDevice(final int deviceIndex, final String id, final String name, final Collection<Dimension> resolutions) { public AVFVideoDevice(final int deviceIndex, final String id, final String name, final Collection<Dimension> resolutions) {
this.deviceIndex = deviceIndex; this.deviceIndex = deviceIndex;
@ -159,6 +160,11 @@ public class AVFVideoDevice implements WebcamDevice, WebcamDevice.FPSSource, Web
return open; return open;
} }
@Override
public long getLastFrameTimestamp() {
return lastFrameTimestamp;
}
public static final int MAX_FPS = 30; public static final int MAX_FPS = 30;
@Override @Override
@ -217,18 +223,23 @@ public class AVFVideoDevice implements WebcamDevice, WebcamDevice.FPSSource, Web
} }
@Override @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()) { if (!isOpen()) {
return false; return false;
} }
updateBuffer(); updateBuffer();
return updateFXIMage(writableImage, imgBuffer); if (this.lastFrameTimestamp <= lastFrameTimestamp) {
}
public boolean updateFXIMage(final WritableImage writableImage, final ByteBuffer byteBuffer) {
if (!isOpen()) {
return false; return false;
} }
@ -249,9 +260,11 @@ public class AVFVideoDevice implements WebcamDevice, WebcamDevice.FPSSource, Web
private void updateBuffer() { private void updateBuffer() {
if (LibVideoCapture.INSTANCE.vcavf_has_new_frame(deviceIndex)) { if (LibVideoCapture.INSTANCE.vcavf_has_new_frame(deviceIndex)) {
LibVideoCapture.INSTANCE.vcavf_grab_frame( if (LibVideoCapture.INSTANCE.vcavf_grab_frame(
deviceIndex, deviceIndex,
Native.getDirectBufferPointer(imgBuffer), imgBuffer.capacity()); Native.getDirectBufferPointer(imgBuffer), imgBuffer.capacity())) {
lastFrameTimestamp = System.currentTimeMillis();
};
} }
} }

View File

@ -37,6 +37,7 @@ public class CaptureManagerFrameGrabberSession {
private int videoWidth = -1; private int videoWidth = -1;
private int videoHeight = -1; private int videoHeight = -1;
private int bufferSizeBytes = -1; private int bufferSizeBytes = -1;
private long lastFrameTimestamp = -1;
public boolean init( public boolean init(
final CaptureManagerSource source, final CaptureManagerSource source,
@ -223,6 +224,10 @@ public class CaptureManagerFrameGrabberSession {
session.closeSession(); session.closeSession();
} }
public long getLastFrameTimestamp() {
return lastFrameTimestamp;
}
public synchronized BufferedImage toBufferedImage() { public synchronized BufferedImage toBufferedImage() {
if (!isOpen()) { if (!isOpen()) {
return null; return null;
@ -249,7 +254,7 @@ public class CaptureManagerFrameGrabberSession {
return bufferedImage; return bufferedImage;
} }
public synchronized boolean updateFXIMage(final WritableImage writableImage) { public synchronized boolean updateFXIMage(final WritableImage writableImage, long lastFrameTimestamp) {
if (!isOpen()) { if (!isOpen()) {
return false; return false;
} }
@ -260,6 +265,10 @@ public class CaptureManagerFrameGrabberSession {
return false; return false;
} }
if (this.lastFrameTimestamp <= lastFrameTimestamp) {
return false;
}
updateFXIMage(writableImage, directBuffer); updateFXIMage(writableImage, directBuffer);
return true; return true;
@ -285,7 +294,12 @@ public class CaptureManagerFrameGrabberSession {
public synchronized int updateDirectBuffer() { 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() { public boolean isOpen() {

View File

@ -144,6 +144,15 @@ public class CaptureManagerVideoDevice implements WebcamDevice, WebcamDevice.FPS
return session != null && session.isOpen(); return session != null && session.isOpen();
} }
@Override
public long getLastFrameTimestamp() {
if (isOpen()) {
return session.getLastFrameTimestamp();
}
return -1;
}
private static Pair<CaptureManagerStreamDescriptor, CaptureManagerMediaType> findBestMediaTypeInStreams( private static Pair<CaptureManagerStreamDescriptor, CaptureManagerMediaType> findBestMediaTypeInStreams(
final Collection<CaptureManagerStreamDescriptor> videoStreams, final Collection<CaptureManagerStreamDescriptor> videoStreams,
final Dimension resolution final Dimension resolution
@ -231,9 +240,9 @@ public class CaptureManagerVideoDevice implements WebcamDevice, WebcamDevice.FPS
} }
@Override @Override
public boolean updateFXIMage(WritableImage writableImage, ByteBuffer byteBuffer) { public boolean updateFXIMage(final WritableImage writableImage, final long lastFrameTimestamp) {
if (isOpen()) { if (isOpen()) {
session.updateFXIMage(writableImage, byteBuffer); session.updateFXIMage(writableImage, lastFrameTimestamp);
return true; return true;
} }
@ -242,11 +251,7 @@ public class CaptureManagerVideoDevice implements WebcamDevice, WebcamDevice.FPS
@Override @Override
public boolean updateFXIMage(WritableImage writableImage) { public boolean updateFXIMage(WritableImage writableImage) {
if (isOpen()) { return updateFXIMage(writableImage, -1);
return session.updateFXIMage(writableImage);
}
return false;
} }
@Override @Override