diff --git a/.gitignore b/.gitignore
index d9bccd3..db58563 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,3 +3,4 @@
.settings
target
/out
+.vscode
diff --git a/CHANGELOG.md b/CHANGELOG.md
deleted file mode 100644
index 80fe982..0000000
--- a/CHANGELOG.md
+++ /dev/null
@@ -1,7 +0,0 @@
-## v1.1.0
-
-
-
-## v1.0.0
-
-* Initial release of Video4j
\ No newline at end of file
diff --git a/README.md b/README.md
index 29ea154..dd01a6b 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ Video4j is a highlevel library ontop of `org.openpnp:opencv` which provides APIs
io.metaloom.video
video4j
- 1.1.0
+ 1.2.0
```
@@ -30,7 +30,7 @@ try (Video video = Videos.open(BIG_BUCK_BUNNY2_PATH)) {
video.fps();
// Total frames of the video
- video.length()
+ video.length();
// Seek to specific frame
video.seekToFrame(1020);
@@ -48,7 +48,7 @@ try (Video video = Videos.open(BIG_BUCK_BUNNY2_PATH)) {
BufferedImage image = video.frameToImage();
// Read the frame and resize it to a width of 256 pixel.
- BufferdImage image2 = video.boxedFrameToImage(256);
+ BufferedImage image2 = video.boxedFrameToImage(256);
// Display the frame in a window
ImageUtils.show(image);
@@ -158,13 +158,25 @@ while (true) {
## Requirements / Limitations
-The library uses OpenCV via JNI. Thus the JNI library `libopencv4.5-jni` must be installed on the host system.
+The library uses OpenCV via JNI. Thus the JNI library `libopencv406-jni` must be installed on the host system.
Currently only Linux is supported.
The JNI libraries need to be manually be loaded once via
```Video4j.init()```. This method will try its best to locate the library itself.
-On Debian Linux the JNI library can be installed via the `libopencv4.5-jni` package. Version `4.5.1+dfsg-5` has been used for testing. Video4j expects the library to be locatable in the library path. Or via `/usr/lib/jni/libopencv_java451.so`.
-You can set `-Djava.library.path` for your application if the `libopencv_java451.so` file is located in a different directory.
+On Debian Linux the JNI library can be installed via the `libopencv406-jni` package. Version `4.6.0+dfsg-9+b1` has been used for testing. Video4j expects the library to be locatable in the library path. Or via `/usr/lib/jni/libopencv_java460.so`.
+You can set `-Djava.library.path` for your application if the `libopencv_java460.so` file is located in a different directory.
-The capabilities of the OpenCV code and thus this library is linked to the installed OpenCV library. If you are unable to open a specific video format this might be related to `libavcodec` library that was used to build the OpenCV library.
\ No newline at end of file
+The capabilities of the OpenCV code and thus this library is linked to the installed OpenCV library. If you are unable to open a specific video format this might be related to `libavcodec` library that was used to build the OpenCV library.
+
+## Releasing
+
+```bash
+# Run tests
+mvn clean package
+# Invoke release to maven central
+mvn clean deploy -Drelease
+# Publish release on github
+jreleaser config
+jreleaser full:release
+```
\ No newline at end of file
diff --git a/jreleaser.yml b/jreleaser.yml
index 790cdf1..9a334c4 100644
--- a/jreleaser.yml
+++ b/jreleaser.yml
@@ -1,6 +1,6 @@
project:
name: video4j
- version: 1.1.0
+ version: 1.2.0
versionPattern: SEMVER
description: Video4j Java Video Processing Library
@@ -10,8 +10,7 @@ project:
authors:
- Johannes Schüth
license: Apache-2.0
- extraProperties:
- inceptionYear: 2022
+ inceptionYear: 2022
release:
github:
diff --git a/pom.xml b/pom.xml
index e315622..2336412 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
io.metaloom.video
video4j
- 1.1.1-SNAPSHOT
+ 1.2.0
io.metaloom
@@ -30,7 +30,7 @@
org.openpnp
opencv
- 4.5.1-2
+ 4.6.0-0
org.imgscalr
@@ -86,7 +86,7 @@
org.apache.maven.plugins
maven-compiler-plugin
- 11
+ 17
diff --git a/src/main/java/io/metaloom/video4j/Video.java b/src/main/java/io/metaloom/video4j/Video.java
index 8cd3ad0..2cfa7af 100644
--- a/src/main/java/io/metaloom/video4j/Video.java
+++ b/src/main/java/io/metaloom/video4j/Video.java
@@ -102,7 +102,7 @@ static Video open(String path) {
/**
* Open the video.
*
- * @return Fluent APOI
+ * @return Fluent API
*/
Video open();
@@ -141,6 +141,22 @@ static Video open(String path) {
*/
String path();
+ /**
+ * Return metadata for the video.
+ *
+ * @param
+ * @return
+ */
+ T getMeta();
+
+ /**
+ * Set metadata for the video.
+ *
+ * @param
+ * @param meta
+ */
+ void setMeta(T meta);
+
/**
* Return a stream of {@link Mat} frames for this video.
*
diff --git a/src/main/java/io/metaloom/video4j/impl/VideoImpl.java b/src/main/java/io/metaloom/video4j/impl/VideoImpl.java
index 576d5fe..e4da5e5 100644
--- a/src/main/java/io/metaloom/video4j/impl/VideoImpl.java
+++ b/src/main/java/io/metaloom/video4j/impl/VideoImpl.java
@@ -18,6 +18,7 @@ public class VideoImpl implements Video {
private final String path;
private final ExtendedVideoCapture capture;
+ private Object meta;
public VideoImpl(String path, ExtendedVideoCapture capture) {
this.path = path;
@@ -25,7 +26,9 @@ public VideoImpl(String path, ExtendedVideoCapture capture) {
}
public Video open() {
- capture.open(path);
+ if (!capture.open(path)) {
+ throw new RuntimeException("Video " + path + " could not be opened.");
+ }
return this;
}
@@ -76,6 +79,17 @@ public int width() {
return capture.width();
}
+ @Override
+ @SuppressWarnings("unchecked")
+ public T getMeta() {
+ return (T) meta;
+ }
+
+ @Override
+ public void setMeta(T meta) {
+ this.meta = meta;
+ }
+
@Override
public void seekToFrame(long frame) {
assertOpen();
diff --git a/src/main/java/io/metaloom/video4j/opencv/CVUtils.java b/src/main/java/io/metaloom/video4j/opencv/CVUtils.java
index 50291ca..cf30c66 100644
--- a/src/main/java/io/metaloom/video4j/opencv/CVUtils.java
+++ b/src/main/java/io/metaloom/video4j/opencv/CVUtils.java
@@ -1,15 +1,22 @@
package io.metaloom.video4j.opencv;
+import java.awt.AlphaComposite;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
+import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
+import java.awt.image.DataBufferInt;
import java.util.Collection;
import java.util.Random;
import org.imgscalr.Scalr;
+import org.imgscalr.Scalr.Method;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfRect;
import org.opencv.core.Point;
+import org.opencv.core.Point3;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
@@ -22,27 +29,78 @@
import io.metaloom.video4j.VideoFrame;
import io.metaloom.video4j.impl.MatProvider;
-public class CVUtils {
+public final class CVUtils {
private static final double BLACK_FRAME_THRESHOLD = 10.0f;
- /**
- * Convert the {@link Mat} into a {@link BufferedImage}.
- *
- * @param m
- * @return
- */
- public static BufferedImage mat2BufferedImage(Mat m) {
- int type = BufferedImage.TYPE_BYTE_GRAY;
- if (m.channels() > 1) {
- type = BufferedImage.TYPE_3BYTE_BGR;
+ public static BufferedImage matToBufferedImage(Mat original) {
+ BufferedImage image = null;
+ int width = original.width(), height = original.height(), channels = original.channels();
+ byte[] sourcePixels = new byte[width * height * channels];
+ original.get(0, 0, sourcePixels);
+
+ if (original.channels() > 1) {
+ image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
+ } else {
+ image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
}
- int bufferSize = m.channels() * m.cols() * m.rows();
- byte[] b = new byte[bufferSize];
- m.get(0, 0, b);
- BufferedImage image = new BufferedImage(m.cols(), m.rows(), type);
final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
- System.arraycopy(b, 0, targetPixels, 0, b.length);
+ System.arraycopy(sourcePixels, 0, targetPixels, 0, sourcePixels.length);
+
+ return image;
+ }
+
+ public static void bufferedImageToMat(BufferedImage image, Mat dest) {
+
+ DataBuffer dataBuffer = image.getRaster().getDataBuffer();
+ byte[] imgPixels = null;
+
+ int width = image.getWidth();
+ int height = image.getHeight();
+
+ if (dataBuffer instanceof DataBufferByte) {
+ imgPixels = ((DataBufferByte) dataBuffer).getData();
+ }
+
+ if (dataBuffer instanceof DataBufferInt) {
+
+ int byteSize = width * height;
+ imgPixels = new byte[byteSize * 3];
+
+ int[] imgIntegerPixels = ((DataBufferInt) dataBuffer).getData();
+
+ for (int p = 0; p < byteSize; p++) {
+ imgPixels[p * 3 + 0] = (byte) ((imgIntegerPixels[p] & 0x00FF0000) >> 16);
+ imgPixels[p * 3 + 1] = (byte) ((imgIntegerPixels[p] & 0x0000FF00) >> 8);
+ imgPixels[p * 3 + 2] = (byte) (imgIntegerPixels[p] & 0x000000FF);
+ }
+ }
+
+ dest.put(0, 0, imgPixels);
+ }
+
+ public static BufferedImage toBufferedImageOfType(BufferedImage original, int type) {
+ if (original == null) {
+ throw new IllegalArgumentException("original == null");
+ }
+
+ // Don't convert if it already has correct type
+ if (original.getType() == type) {
+ return original;
+ }
+
+ // Create a buffered image
+ BufferedImage image = new BufferedImage(original.getWidth(), original.getHeight(), type);
+
+ // Draw the image onto the new buffer
+ Graphics2D g = image.createGraphics();
+ try {
+ g.setComposite(AlphaComposite.Src);
+ g.drawImage(original, 0, 0, null);
+ } finally {
+ g.dispose();
+ }
+
return image;
}
@@ -91,6 +149,83 @@ public static void resize(Mat step1, Mat step2, int x, int y) {
Imgproc.resize(step1, step2, new Size(x, y), 0, 0, Imgproc.INTER_LANCZOS4);
}
+ public static boolean boxFrame2(VideoFrame vframe, int resX) {
+ // return boxFrame2(frame.mat(), resX);
+
+ Mat frame = vframe.mat();
+ int width = frame.width();
+ int height = frame.height();
+
+ double ratio = (double) width / (double) height;
+
+ // Check for vertical video syndrome
+ boolean vvs = ratio < 1;
+ int resY = vvs ? (int) (((double) resX) * ratio) : (int) (((double) resX) / ratio);
+
+ int spaceY = (resX - resY) / 2;
+ Mat target = null;
+ if (vvs) {
+ target = new Mat(resX, resY, frame.type());
+ resize2(frame, target, resY, resX);
+ free(frame);
+ } else {
+ target = new Mat(resY, resX, frame.type());
+ resize2(frame, target, resX, resY);
+ free(frame);
+
+ }
+ Core.copyMakeBorder(target, target, spaceY, spaceY, 0, 0, Core.BORDER_CONSTANT);
+ vframe.setMat(target);
+
+ return false;
+ }
+
+ public static boolean boxFrame2(Mat frame, int resX) {
+ return boxFrame2(frame, resX, frame.width(), frame.height());
+ }
+
+ public static boolean boxFrame2(Mat frame, int resX, int width, int height) {
+ double ratio = (double) width / (double) height;
+
+ // Check for vertical video syndrome
+ boolean vvs = ratio < 1;
+ int resY = vvs ? (int) (((double) resX) * ratio) : (int) (((double) resX) / ratio);
+
+ int spaceY = (resX - resY) / 2;
+ Mat target = null;
+ if (vvs) {
+ target = new Mat(resY, resX, frame.type());
+ resize2(frame, target, resY, resX);
+ } else {
+ target = new Mat(resX, resY, frame.type());
+ resize2(frame, target, resX, resY);
+ }
+ // Core.copyMakeBorder(target, frame, spaceY, spaceY, 0, 0, Core.BORDER_CONSTANT);
+ free(target);
+
+ return false;
+
+ }
+
+ /**
+ * Resizes the source image using non-opencv methods.
+ *
+ * @param sourceMat
+ * @param destMat
+ * @param x
+ * @param y
+ */
+ public static void resize2(Mat sourceMat, Mat destMat, int x, int y) {
+ // 1. Convert the mat to a buffered image which can be processed
+ BufferedImage sourceImage = matToBufferedImage(sourceMat);
+ // 2. Resize the image
+ BufferedImage resizedImage = Scalr.resize(sourceImage, Method.SPEED, x, y);
+ // 3. Convert the type of the image so that conversion to mat can succeed
+ sourceImage = toBufferedImageOfType(resizedImage, sourceImage.getType());
+ // 4. Convert back to mat
+ CVUtils.bufferedImageToMat(sourceImage, destMat);
+ }
+
/**
* Run canny86 for edge detection.
*
@@ -191,7 +326,9 @@ public static Mat houghLinesP(Mat frame, double rho, double theta, int threshold
*
* @param image
* @param x
+ * new width of the image
* @param y
+ * new height of the image
* @return
*/
public static BufferedImage scale(BufferedImage image, int x, int y) {
@@ -200,6 +337,23 @@ public static BufferedImage scale(BufferedImage image, int x, int y) {
return resizedImage;
}
+ /**
+ * Crop the given frame area.
+ *
+ * @param frame
+ * @param start
+ * Start point of the crop
+ * @param dim
+ * Dimension of crop area
+ * @return
+ */
+ public static VideoFrame crop(VideoFrame frame, java.awt.Point start, Dimension dim) {
+ Mat mat = frame.mat();
+ Imgproc.getRectSubPix(mat, new Size(dim.getWidth(), dim.getHeight()),
+ new Point(start.x + (dim.getWidth() / 2), start.y + (dim.getHeight() / 2)), mat);
+ return frame;
+ }
+
public static VideoFrame faceDetectAndDisplay(VideoFrame frame) {
frame.setMat(faceDetectAndDisplay(frame.mat()));
return frame;
@@ -217,7 +371,7 @@ public static Mat faceDetectAndDisplay(Mat frame) {
CascadeClassifier faceCascade = new CascadeClassifier();
String profileXML = "src/main/resources/lbpcascade_profileface.xml";
if (!faceCascade.load(profileXML)) {
- throw new RuntimeException("Could not find " + profileXML);
+ throw new RuntimeException("Could not load " + profileXML);
}
// convert the frame in gray scale
Imgproc.cvtColor(frame, grayFrame, Imgproc.COLOR_BGR2GRAY);
@@ -246,6 +400,31 @@ public static Mat faceDetectAndDisplay(Mat frame) {
}
+ public static VideoFrame faceDetectAndDisplay2(VideoFrame frame) {
+ frame.setMat(faceDetectAndDisplay2(frame.mat()));
+ return frame;
+ }
+
+ public static Mat faceDetectAndDisplay2(Mat frame) {
+ CascadeClassifier faceDetector = new CascadeClassifier();
+ String profileXML = "src/main/resources/haarcascade_frontalface_alt.xml";
+ if (!faceDetector.load(profileXML)) {
+ throw new RuntimeException("Could not load " + profileXML);
+ }
+
+ // Detecting faces
+ MatOfRect faceDetections = new MatOfRect();
+ faceDetector.detectMultiScale(frame, faceDetections);
+
+ // Creating a rectangular box showing faces detected
+ for (Rect rect : faceDetections.toArray()) {
+ Imgproc.rectangle(frame, new Point(rect.x, rect.y), new Point(rect.width + rect.x,
+ rect.height + rect.y), new Scalar(0, 255, 0));
+ }
+ return frame;
+
+ }
+
/**
* Create a new mat which has the same dimensions as the source.
*
@@ -308,9 +487,9 @@ public static void normalize(Mat step1, Mat step2, int clamp, int max) {
* @param fontScale
* @param color
* @param thickness
- * @return
+ * @return Fluent API
*/
- public static VideoFrame drawText(VideoFrame frame, String text, Point pos, double fontScale, Scalar color, int thickness) {
+ public static T drawText(T frame, String text, Point pos, double fontScale, Scalar color, int thickness) {
Imgproc.putText(frame.mat(), text, pos, Imgproc.FONT_HERSHEY_PLAIN, fontScale, color, thickness);
return frame;
}
@@ -326,13 +505,23 @@ public static boolean boxFrame(Mat frame, int resX) {
public static boolean boxFrame(Mat frame, int resX, int width, int height) {
double ratio = (double) width / (double) height;
- int resY = (int) (((double) resX) / ratio);
- int spaceY = (resX - resY) / 2;
+ // Check for vertical video syndrome
+ boolean vvs = ratio < 1;
+ int resY = vvs ? (int) (((double) resX) * ratio) : (int) (((double) resX) / ratio);
- Mat target = frame.clone();
+ int spaceY = (resX - resY) / 2;
int method = Imgproc.INTER_LANCZOS4;
- Imgproc.resize(target, target, new Size(resX, resY), 0, 0, method);
+ Mat target = null;
+ if (vvs) {
+ target = new Mat(resY, resX, frame.type());
+ Imgproc.resize(frame, target, new Size(resY, resX), 0, 0, method);
+ } else {
+ target = new Mat(resX, resY, frame.type());
+ Imgproc.resize(frame, target, new Size(resX, resY), 0, 0, method);
+ }
Core.copyMakeBorder(target, frame, spaceY, spaceY, 0, 0, Core.BORDER_CONSTANT);
+ free(target);
+
return false;
}
@@ -365,4 +554,33 @@ public static void clear(Mat mat, double value) {
}
}
+ /**
+ * @deprecated Use {@link #matToBufferedImage(Mat)} instead
+ * @param mat
+ * @return
+ */
+ public static BufferedImage mat2BufferedImage(Mat mat) {
+ return matToBufferedImage(mat);
+ }
+
+ /**
+ * Convert a {@link Point} back into {@link org.opencv.core.Point}
+ *
+ * @param awtPoint
+ * @return
+ */
+ public static org.opencv.core.Point toCVPoint(java.awt.Point awtPoint) {
+ return new org.opencv.core.Point(awtPoint.getX(), awtPoint.getY());
+ }
+
+ /**
+ * Convert an OpenCV {@link Point} back to {@link java.awt.Point}n
+ *
+ * @param cvPoint
+ * @return
+ */
+ public static java.awt.Point toAWTPoint(Point cvPoint) {
+ return new java.awt.Point((int) cvPoint.x, (int) cvPoint.y);
+ }
+
}
diff --git a/src/main/java/io/metaloom/video4j/preview/PreviewGenerator.java b/src/main/java/io/metaloom/video4j/preview/PreviewGenerator.java
index d4c5481..604f27d 100644
--- a/src/main/java/io/metaloom/video4j/preview/PreviewGenerator.java
+++ b/src/main/java/io/metaloom/video4j/preview/PreviewGenerator.java
@@ -128,11 +128,11 @@ private BufferedImage generateTilemap(List frames, int cols, int rows) {
// Ignored
}
- BufferedImage image = CVUtils.mat2BufferedImage(target);
+ BufferedImage image = CVUtils.matToBufferedImage(target);
CVUtils.free(target);
return image;
} else {
- BufferedImage image = CVUtils.mat2BufferedImage(allRows.get(0));
+ BufferedImage image = CVUtils.matToBufferedImage(allRows.get(0));
CVUtils.free(allRows);
return image;
}
diff --git a/src/main/java/io/metaloom/video4j/ui/PreviewDebugUI.java b/src/main/java/io/metaloom/video4j/ui/PreviewDebugUI.java
index 1f28f78..af9b930 100644
--- a/src/main/java/io/metaloom/video4j/ui/PreviewDebugUI.java
+++ b/src/main/java/io/metaloom/video4j/ui/PreviewDebugUI.java
@@ -58,13 +58,13 @@ public void show() {
private Component createPlayButton() {
ImageIcon playButtonIcon = createImageIcon("/images/play.gif");
JButton playButton = new JButton("Play", playButtonIcon);
- try (Video video = Videos.open(path)) {
- playButton.addActionListener(event -> {
+ playButton.addActionListener(event -> {
+ try (Video video = Videos.open(path)) {
for (PreviewGenerator handler : handlers) {
handler.preview(video, image -> refresh(handler, image));
}
- });
- }
+ }
+ });
return playButton;
}
diff --git a/src/main/java/io/metaloom/video4j/utils/ImageUtils.java b/src/main/java/io/metaloom/video4j/utils/ImageUtils.java
index 76e4880..dde3ba2 100644
--- a/src/main/java/io/metaloom/video4j/utils/ImageUtils.java
+++ b/src/main/java/io/metaloom/video4j/utils/ImageUtils.java
@@ -1,10 +1,9 @@
package io.metaloom.video4j.utils;
+import java.awt.FlowLayout;
import java.awt.image.BufferedImage;
-import java.awt.image.DataBufferByte;
import java.io.File;
import java.io.IOException;
-import java.awt.FlowLayout;
import java.util.Iterator;
import javax.imageio.IIOImage;
@@ -22,6 +21,8 @@
import org.imgscalr.Scalr.Method;
import org.opencv.core.Mat;
+import io.metaloom.video4j.opencv.CVUtils;
+
/**
* Image Utils which allow the conversion of OpenCV image data to regular java image objects.
*/
@@ -32,11 +33,11 @@ private ImageUtils() {
}
public static void show(Mat mat) {
- show(matToBufferedImage(mat));
+ show(CVUtils.matToBufferedImage(mat));
}
public static void show(Mat mat, int width) {
- BufferedImage image = matToBufferedImage(mat);
+ BufferedImage image = CVUtils.matToBufferedImage(mat);
image = Scalr.resize(image, Method.SPEED, width);
show(image);
}
@@ -45,8 +46,7 @@ public static void show(Mat mat, int width) {
* Show a scaled version of the provided image.
*
* @param image
-^ * @param width
- * New width of the image to be shown
+ * ^ * @param width New width of the image to be shown
*/
public static void show(BufferedImage image, int width) {
image = Scalr.resize(image, Method.SPEED, width);
@@ -61,23 +61,6 @@ public static void show(BufferedImage image) {
frame.setVisible(true);
}
- public static BufferedImage matToBufferedImage(Mat original) {
- BufferedImage image = null;
- int width = original.width(), height = original.height(), channels = original.channels();
- byte[] sourcePixels = new byte[width * height * channels];
- original.get(0, 0, sourcePixels);
-
- if (original.channels() > 1) {
- image = new BufferedImage(width, height, BufferedImage.TYPE_3BYTE_BGR);
- } else {
- image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
- }
- final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
- System.arraycopy(sourcePixels, 0, targetPixels, 0, sourcePixels.length);
-
- return image;
- }
-
public static void save(File outputPath, BufferedImage image) throws IOException {
try (ImageOutputStream out = new FileImageOutputStream(outputPath)) {
ImageWriteParam params = getImageWriteparams();
@@ -100,4 +83,8 @@ private static ImageWriteParam getImageWriteparams() {
return params;
}
+ public static BufferedImage matToBufferedImage(Mat frame) {
+ return CVUtils.matToBufferedImage(frame);
+ }
+
}
diff --git a/src/main/java/io/metaloom/video4j/utils/SimpleVideoPlayer.java b/src/main/java/io/metaloom/video4j/utils/SimpleVideoPlayer.java
index b9f5c3b..477e4d9 100644
--- a/src/main/java/io/metaloom/video4j/utils/SimpleVideoPlayer.java
+++ b/src/main/java/io/metaloom/video4j/utils/SimpleVideoPlayer.java
@@ -102,11 +102,11 @@ public void play(Video video, boolean loop) {
}
}
- public void playVideoFrameStream(Stream frameStream) {
+ public void playVideoFrameStream(Stream extends VideoFrame> frameStream) {
playVideoFrameStream(frameStream, 0);
}
- public void playVideoFrameStream(Stream frameStream, int width) {
+ public void playVideoFrameStream(Stream extends VideoFrame> frameStream, int width) {
AtomicReference fps = new AtomicReference<>(null);
frameStream.forEach(frame -> {
if (fps.get() == null) {
@@ -120,7 +120,7 @@ public void playVideoFrameStream(Stream frameStream, int width) {
int fHeight = mat.height();
int fWidth = mat.width();
CVUtils.boxFrame(mat, width, fWidth, fHeight);
- BufferedImage image = CVUtils.mat2BufferedImage(mat);
+ BufferedImage image = CVUtils.matToBufferedImage(mat);
refresh(image);
}
applySyncDelay(start, fps.get());
diff --git a/src/main/java/io/metaloom/video4j/utils/VideoUtils.java b/src/main/java/io/metaloom/video4j/utils/VideoUtils.java
index 2f150ea..9cafb5a 100644
--- a/src/main/java/io/metaloom/video4j/utils/VideoUtils.java
+++ b/src/main/java/io/metaloom/video4j/utils/VideoUtils.java
@@ -76,13 +76,13 @@ public static void showMatStream(Stream frameStream) {
player.playMatStream(frameStream);
}
- public static void showVideoFrameStream(Stream frameStream) {
+ public static void showVideoFrameStream(Stream extends VideoFrame> frameStream) {
SimpleVideoPlayer player = new SimpleVideoPlayer(256);
player.show();
player.playVideoFrameStream(frameStream);
}
- public static void showVideoFrameStream(Stream frameStream, int width) {
+ public static void showVideoFrameStream(Stream extends VideoFrame> frameStream, int width) {
SimpleVideoPlayer player = new SimpleVideoPlayer(256);
player.show();
player.playVideoFrameStream(frameStream, width);
diff --git a/src/test/java/io/metaloom/video4j/AbstractVideoTest.java b/src/test/java/io/metaloom/video4j/AbstractVideoTest.java
index 7bb4b1a..9e26a9d 100644
--- a/src/test/java/io/metaloom/video4j/AbstractVideoTest.java
+++ b/src/test/java/io/metaloom/video4j/AbstractVideoTest.java
@@ -13,6 +13,8 @@ public static void setup() {
public static final String BIG_BUCK_BUNNY2_PATH = "src/test/resources/BigBuckBunny.mp4";
+ public static final String BIG_BUCK_BUNNY_VVS_PATH = "src/test/resources/BigBuckBunny_VVS.mp4";
+
protected void sleep(int timeMs) {
try {
Thread.sleep(timeMs);
diff --git a/src/test/java/io/metaloom/video4j/VideoAPITest.java b/src/test/java/io/metaloom/video4j/VideoAPITest.java
index 7eba8f3..b727ee8 100644
--- a/src/test/java/io/metaloom/video4j/VideoAPITest.java
+++ b/src/test/java/io/metaloom/video4j/VideoAPITest.java
@@ -13,12 +13,13 @@
public class VideoAPITest extends AbstractVideoTest {
@Test
+ @SuppressWarnings("resource")
public void testAPI() throws Exception {
Video v;
try (Video video = Videos.open(BIG_BUCK_BUNNY2_PATH)) {
- assertEquals(1280, video.width());
- assertEquals(720, video.height());
+ assertEquals(320, video.width());
+ assertEquals(240, video.height());
assertEquals(24, video.fps(), 0);
video.seekToFrame(1020);
BufferedImage image = video.frameToImage();
diff --git a/src/test/java/io/metaloom/video4j/preview/PreviewGeneratorTest.java b/src/test/java/io/metaloom/video4j/preview/PreviewGeneratorTest.java
index bf1cb3c..73373d8 100644
--- a/src/test/java/io/metaloom/video4j/preview/PreviewGeneratorTest.java
+++ b/src/test/java/io/metaloom/video4j/preview/PreviewGeneratorTest.java
@@ -40,4 +40,16 @@ public void testPreview() {
MatProvider.printLeaks();
assertFalse("There should not be any leaked mats", MatProvider.hasLeaks());
}
+
+ @Test
+ public void testVVSPreview() {
+ MatProvider.enableTracking();
+ PreviewGenerator gen = new PreviewGenerator(TILE_SIZE, 6, 3);
+ try (Video video = Videos.open(BIG_BUCK_BUNNY_VVS_PATH)) {
+ ImageUtils.show(gen.preview(video));
+ }
+ sleep(250);
+ MatProvider.printLeaks();
+ assertFalse("There should not be any leaked mats", MatProvider.hasLeaks());
+ }
}
diff --git a/src/test/resources/BigBuckBunny_VVS.mp4 b/src/test/resources/BigBuckBunny_VVS.mp4
new file mode 100644
index 0000000..9d3d1aa
Binary files /dev/null and b/src/test/resources/BigBuckBunny_VVS.mp4 differ