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 frameStream) { playVideoFrameStream(frameStream, 0); } - public void playVideoFrameStream(Stream frameStream, int width) { + public void playVideoFrameStream(Stream 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 frameStream) { SimpleVideoPlayer player = new SimpleVideoPlayer(256); player.show(); player.playVideoFrameStream(frameStream); } - public static void showVideoFrameStream(Stream frameStream, int width) { + public static void showVideoFrameStream(Stream 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