diff --git a/CHANGELOG.md b/CHANGELOG.md index e02c8815..320fbcda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,5 @@ + * Add `FrameGrabber.grabAtFrameRate()` to simulate a device or stream when reading from files ([pull #1659](https://github.com/bytedeco/javacv/pull/1659)) * Update `FFmpegFrameGrabber` and `FFmpegFrameRecorder` with new `avcodec` API ([issue #1498](https://github.com/bytedeco/javacv/issues/1498)) * Add new `Similarity` sample with PSNR and MSSIM ([pull #1622](https://github.com/bytedeco/javacv/pull/1622)) * Avoid crash in `FFmpegFrameRecorder.stop()` by moving `av_write_trailer()` out of `flush()` ([issue #1616](https://github.com/bytedeco/javacv/issues/1616)) diff --git a/platform/src/test/java/org/bytedeco/javacv/FrameGrabberTest.java b/platform/src/test/java/org/bytedeco/javacv/FrameGrabberTest.java index 700c07b9..5d24322d 100644 --- a/platform/src/test/java/org/bytedeco/javacv/FrameGrabberTest.java +++ b/platform/src/test/java/org/bytedeco/javacv/FrameGrabberTest.java @@ -93,7 +93,8 @@ public void testFFmpegFrameGrabber() { int n = 0, m = 0; Frame frame2; - while ((frame2 = grabber.grab()) != null) { + long startTime = System.nanoTime(); + while ((frame2 = grabber.grabAtFrameRate()) != null) { Frame clone2 = frame2.clone(); if (frame2.image != null) { Frame frame = frames[n++]; @@ -127,6 +128,8 @@ public void testFFmpegFrameGrabber() { } clone2.close(); } + long stopTime = System.nanoTime(); + assertEquals(n, (stopTime - startTime) * grabber.getFrameRate() / 1_000_000_000, 10.0); assertEquals(frames.length, n); assertEquals(null, grabber.grab()); grabber.restart(); diff --git a/src/main/java/org/bytedeco/javacv/FrameGrabber.java b/src/main/java/org/bytedeco/javacv/FrameGrabber.java index b15f34fb..1c77ea60 100644 --- a/src/main/java/org/bytedeco/javacv/FrameGrabber.java +++ b/src/main/java/org/bytedeco/javacv/FrameGrabber.java @@ -45,7 +45,7 @@ public abstract class FrameGrabber implements Closeable { public static final List list = new LinkedList(Arrays.asList(new String[] { - "DC1394", "FlyCapture", "FlyCapture2", "OpenKinect", "OpenKinect2", "RealSense", "RealSense2", "PS3Eye", "VideoInput", "OpenCV", "FFmpeg", "IPCamera" })); + "DC1394", "FlyCapture", "FlyCapture2", "OpenKinect", "OpenKinect2", "RealSense", "RealSense2", "PS3Eye", "VideoInput", "OpenCV", "FFmpeg", "IPCamera" })); public static void init() { for (String name : list) { try { @@ -201,6 +201,7 @@ public static enum SampleMode { protected int frameNumber = 0; protected long timestamp = 0; protected int maxDelay = -1; + protected long startTime = 0; public int getVideoStream() { return videoStream; @@ -724,4 +725,27 @@ public void release() throws Exception { public Array createArray(FrameGrabber[] frameGrabbers) { return new Array(frameGrabbers); } + + /** Returns {@code frame = grab()} after {@code waitForTimestamp(frame)}. */ + public Frame grabAtFrameRate() throws Exception, InterruptedException { + Frame frame = grab(); + if (frame != null) { + waitForTimestamp(frame); + } + return frame; + } + + /** Returns true if {@code Thread.sleep()} had to be called. */ + public boolean waitForTimestamp(Frame frame) throws InterruptedException { + if (startTime == 0) { + startTime = System.nanoTime() / 1000 - frame.timestamp; + } else { + long delay = frame.timestamp - (System.nanoTime() / 1000 - startTime); + if (delay > 0) { + Thread.sleep(delay / 1000, (int)(delay % 1000) * 1000); + return true; + } + } + return false; + } }