diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties
index 2406271416..ba4dc45c96 100644
--- a/Core/nbproject/project.properties
+++ b/Core/nbproject/project.properties
@@ -52,6 +52,7 @@ file.reference.sis-metadata-0.6.jar=release/modules/ext/sis-metadata-0.6.jar
file.reference.sis-netcdf-0.6.jar=release/modules/ext/sis-netcdf-0.6.jar
file.reference.sis-utility-0.6.jar=release/modules/ext/sis-utility-0.6.jar
file.reference.slf4j-api-1.7.24.jar=release/modules/ext/slf4j-api-1.7.24.jar
+file.reference.sqlite-jdbc-3.25.2.jar=release/modules/ext/sqlite-jdbc-3.25.2.jar
file.reference.sqlite-jdbc-3.8.11.jar=release/modules/ext/sqlite-jdbc-3.8.11.jar
file.reference.StixLib.jar=release/modules/ext/StixLib.jar
file.reference.jempbox-1.8.13.jar=release/modules/ext/jempbox-1.8.13.jar
@@ -77,6 +78,7 @@ file.reference.xz-1.6.jar=release/modules/ext/xz-1.6.jar
file.reference.zookeeper-3.4.6.jar=release/modules/ext/zookeeper-3.4.6.jar
file.reference.SparseBitSet-1.1.jar=release/modules/ext/SparseBitSet-1.1.jar
file.reference.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.jar
+file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar
javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial
license.file=../LICENSE-2.0.txt
diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml
index 37ea029fba..faf08934d4 100644
--- a/Core/nbproject/project.xml
+++ b/Core/nbproject/project.xml
@@ -415,6 +415,10 @@
ext/StixLib.jar
release/modules/ext/StixLib.jar
+
+ ext/jackson-core-2.9.7.jar
+ release/modules/ext/jackson-core-2.9.7.jar
+
ext/pdfbox-tools-2.0.8.jar
release/modules/ext/pdfbox-tools-2.0.8.jar
@@ -431,10 +435,6 @@
ext/tika-parsers-1.17.jar
release/modules/ext/tika-parsers-1.17.jar
-
- ext/sqlite-jdbc-3.25.2.jar
- release/modules/ext/sqlite-jdbc-3.25.2.jar
-
ext/json-simple-1.1.1.jar
release/modules/ext/json-simple-1.1.1.jar
@@ -447,6 +447,10 @@
ext/jhighlight-1.0.2.jar
release/modules/ext/jhighlight-1.0.2.jar
+
+ ext/sleuthkit-postgresql-4.6.5.jar
+ release/modules/ext/sleuthkit-postgresql-4.6.5.jar
+
ext/jempbox-1.8.13.jar
release/modules/ext/jempbox-1.8.13.jar
@@ -499,10 +503,6 @@
ext/isoparser-1.1.18.jar
release/modules/ext/isoparser-1.1.18.jar
-
- ext/sleuthkit-postgresql-4.6.5.jar
- release/modules/ext/sleuthkit-postgresql-4.6.5.jar
-
ext/vorbis-java-core-0.8.jar
release/modules/ext/vorbis-java-core-0.8.jar
@@ -608,8 +608,8 @@
release/modules/ext/curator-client-2.8.0.jar
- ext/jackson-core-2.9.7.jar
- release/modules/ext/jackson-core-2.9.7.jar
+ ext/sqlite-jdbc-3.25.2.jar
+ release/modules/ext/sqlite-jdbc-3.25.2.jar
ext/cxf-rt-frontend-jaxrs-3.0.16.jar
@@ -619,10 +619,6 @@
ext/grib-4.5.5.jar
release/modules/ext/grib-4.5.5.jar
-
- ext/jackson-core-2.9.2.jar
- release/modules/ext/jackson-core-2.9.2.jar
-
ext/activemq-all-5.11.1.jar
release/modules/ext/activemq-all-5.11.1.jar
diff --git a/Core/src/org/freedesktop/gstreamer/examples/SimpleVideoComponent.java b/Core/src/org/freedesktop/gstreamer/examples/SimpleVideoComponent.java
new file mode 100755
index 0000000000..667c46e3d9
--- /dev/null
+++ b/Core/src/org/freedesktop/gstreamer/examples/SimpleVideoComponent.java
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2018 Neil C Smith
+ * Copyright (c) 2007 Wayne Meissner
+ *
+ * This file is part of gstreamer-java.
+ *
+ * This code is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License version 3 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * version 3 for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with this work. If not, see .
+ */
+package org.freedesktop.gstreamer.examples;
+
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.GraphicsConfiguration;
+import java.awt.RenderingHints;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ComponentAdapter;
+import java.awt.event.ComponentEvent;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
+import java.awt.image.VolatileImage;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.nio.IntBuffer;
+import java.util.concurrent.locks.Lock;
+import java.util.concurrent.locks.ReentrantLock;
+
+import javax.swing.SwingUtilities;
+import javax.swing.Timer;
+
+import org.freedesktop.gstreamer.Element;
+import org.freedesktop.gstreamer.Structure;
+import org.freedesktop.gstreamer.elements.AppSink;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import org.freedesktop.gstreamer.Buffer;
+import org.freedesktop.gstreamer.Caps;
+import org.freedesktop.gstreamer.FlowReturn;
+import org.freedesktop.gstreamer.Sample;
+
+/**
+ *
+ */
+//DLG: Made public
+public class SimpleVideoComponent extends javax.swing.JComponent {
+
+ private BufferedImage currentImage = null;
+ private final Lock bufferLock = new ReentrantLock();
+ private final AppSink videosink;
+// private Pad videoPad;
+ private RenderComponent renderComponent = new RenderComponent();
+ private boolean keepAspect = true;
+ private Timer resourceTimer;
+ private VolatileImage volatileImage;
+ private boolean frameRendered = false;
+ private volatile boolean updatePending = false;
+ private final boolean useVolatile;
+
+ /**
+ * Creates a new instance of GstVideoComponent
+ */
+ public SimpleVideoComponent() {
+ this(new AppSink("GstVideoComponent"));
+ }
+
+ /**
+ * Creates a new instance of GstVideoComponent
+ */
+ public SimpleVideoComponent(AppSink appsink) {
+ this.videosink = appsink;
+ videosink.set("emit-signals", true);
+ AppSinkListener listener = new AppSinkListener();
+ videosink.connect((AppSink.NEW_SAMPLE) listener);
+ videosink.connect((AppSink.NEW_PREROLL) listener);
+ StringBuilder caps = new StringBuilder("video/x-raw,pixel-aspect-ratio=1/1,");
+ // JNA creates ByteBuffer using native byte order, set masks according to that.
+ if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) {
+ caps.append("format=BGRx");
+ } else {
+ caps.append("format=xRGB");
+ }
+ videosink.setCaps(new Caps(caps.toString()));
+
+ useVolatile = true;
+
+ // Kick off a timer to free up the volatile image if there have been no recent updates
+ // (e.g. the player is paused)
+ //
+ resourceTimer = new Timer(250, resourceReaper);
+
+ //
+ // Don't use a layout manager - the output component will positioned within this
+ // component according to the aspect ratio and scaling mode
+ //
+ setLayout(null);
+ add(renderComponent);
+
+ //
+ // Listen for the child changing its preferred size to the size of the
+ // video stream.
+ //
+ renderComponent.addPropertyChangeListener("preferredSize", new PropertyChangeListener() {
+
+ public void propertyChange(PropertyChangeEvent evt) {
+ setPreferredSize(renderComponent.getPreferredSize());
+ scaleVideoOutput();
+ }
+ });
+ //
+ // Scale the video output in response to this component being resized
+ //
+ addComponentListener(new ComponentAdapter() {
+
+ @Override
+ public void componentResized(ComponentEvent arg0) {
+ scaleVideoOutput();
+ }
+
+ });
+ renderComponent.setBounds(getBounds());
+ setOpaque(true);
+ setBackground(Color.BLACK);
+ }
+
+ /**
+ * Scales the video output component according to its aspect ratio
+ */
+ private void scaleVideoOutput() {
+ final Component child = renderComponent;
+ final Dimension childSize = child.getPreferredSize();
+ final int width = getWidth(), height = getHeight();
+ // Figure out the aspect ratio
+ double aspect = keepAspect ? (double) childSize.width / (double) childSize.height : 1.0f;
+
+ //
+ // Now scale and position the videoChild component to be in the correct position
+ // to keep the aspect ratio correct.
+ //
+ int scaledHeight = (int) ((double) width / aspect);
+ if (!keepAspect) {
+ //
+ // Just make the child match the parent
+ //
+ child.setBounds(0, 0, width, height);
+ } else if (scaledHeight < height) {
+ //
+ // Output window is taller than the image is when scaled, so move the
+ // video component to sit vertically in the centre of the VideoComponent.
+ //
+ final int y = (height - scaledHeight) / 2;
+ child.setBounds(0, y, width, scaledHeight);
+ } else {
+ final int scaledWidth = (int) ((double) height * aspect);
+ final int x = (width - scaledWidth) / 2;
+ child.setBounds(x, 0, scaledWidth, height);
+ }
+ }
+ private ActionListener resourceReaper = new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ if (!frameRendered) {
+ if (volatileImage != null) {
+ volatileImage.flush();
+ volatileImage = null;
+ }
+
+ // Stop the timer so we don't wakeup needlessly
+ resourceTimer.stop();
+ }
+ frameRendered = false;
+ }
+ };
+
+ public Element getElement() {
+ return videosink;
+ }
+
+ public void setKeepAspect(boolean keepAspect) {
+ this.keepAspect = keepAspect;
+ }
+
+ @Override
+ public boolean isLightweight() {
+ return true;
+ }
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ if (isOpaque()) {
+ Graphics2D g2d = (Graphics2D) g.create();
+ g2d.setColor(getBackground());
+ g2d.fillRect(0, 0, getWidth(), getHeight());
+ g2d.dispose();
+ }
+ }
+
+ private class RenderComponent extends javax.swing.JComponent {
+
+ private static final long serialVersionUID = -4736605073704494268L;
+
+ @Override
+ protected void paintComponent(Graphics g) {
+ int width = getWidth(), height = getHeight();
+ Graphics2D g2d = (Graphics2D) g.create();
+ g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+ RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+ if (currentImage != null) {
+ GraphicsConfiguration gc = getGraphicsConfiguration();
+ render(g2d, 0, 0, width, height);
+ } else {
+ g2d.setColor(getBackground());
+ g2d.fillRect(0, 0, width, height);
+ }
+ g2d.dispose();
+ }
+
+ @Override
+ public boolean isOpaque() {
+ return SimpleVideoComponent.this.isOpaque();
+ }
+
+ @Override
+ public boolean isLightweight() {
+ return true;
+ }
+ }
+
+ private void renderVolatileImage(BufferedImage bufferedImage) {
+ do {
+ int w = bufferedImage.getWidth(), h = bufferedImage.getHeight();
+ GraphicsConfiguration gc = getGraphicsConfiguration();
+ if (volatileImage == null || volatileImage.getWidth() != w
+ || volatileImage.getHeight() != h
+ || volatileImage.validate(gc) == VolatileImage.IMAGE_INCOMPATIBLE) {
+ if (volatileImage != null) {
+ volatileImage.flush();
+ }
+ volatileImage = gc.createCompatibleVolatileImage(w, h);
+ volatileImage.setAccelerationPriority(1.0f);
+ }
+ //
+ // Now paint the BufferedImage into the accelerated image
+ //
+ Graphics2D g = volatileImage.createGraphics();
+ g.drawImage(bufferedImage, 0, 0, null);
+ g.dispose();
+ } while (volatileImage.contentsLost());
+ }
+
+ /**
+ * Renders to a volatile image, and then paints that to the screen. This
+ * helps with scaling performance on accelerated surfaces (e.g. OpenGL)
+ *
+ * @param g the graphics to paint the image to
+ * @param x the left coordinate to start painting at.
+ * @param y the top coordinate to start painting at.
+ * @param w the width of the paint area
+ * @param h the height of the paint area
+ */
+ private void volatileRender(Graphics g, int x, int y, int w, int h) {
+ do {
+ if (updatePending || volatileImage == null
+ || volatileImage.validate(getGraphicsConfiguration()) != VolatileImage.IMAGE_OK) {
+ bufferLock.lock();
+ try {
+ updatePending = false;
+ renderVolatileImage(currentImage);
+ } finally {
+ bufferLock.unlock();
+ }
+ }
+ g.drawImage(volatileImage, x, y, w, h, null);
+ } while (volatileImage.contentsLost());
+ }
+
+ /**
+ * Renders directly to the given Graphics. This is only really
+ * useful on MacOS where swing graphics are unaccelerated so using a
+ * volatile just incurs an extra memcpy().
+ *
+ * @param g the graphics to paint the image to
+ * @param x the left coordinate to start painting at.
+ * @param y the top coordinate to start painting at.
+ * @param w the width of the paint area
+ * @param h the height of the paint area
+ */
+ private void heapRender(Graphics g, int x, int y, int w, int h) {
+ bufferLock.lock();
+ try {
+ updatePending = false;
+ g.drawImage(currentImage, x, y, w, h, null);
+ } finally {
+ bufferLock.unlock();
+ }
+ }
+
+ /**
+ * Renders the current frame to the given Graphics.
+ *
+ * @param g the graphics to paint the image to
+ * @param x the left coordinate to start painting at.
+ * @param y the top coordinate to start painting at.
+ * @param w the width of the paint area
+ * @param h the height of the paint area
+ */
+ private void render(Graphics g, int x, int y, int w, int h) {
+ if (useVolatile) {
+ volatileRender(g, x, y, w, h);
+ } else {
+ heapRender(g, x, y, w, h);
+ }
+ //
+ // Restart the resource reaper timer if neccessary
+ //
+ if (!frameRendered) {
+ frameRendered = true;
+ if (!resourceTimer.isRunning()) {
+ resourceTimer.restart();
+ }
+ }
+ }
+
+ private int imgWidth = 0, imgHeight = 0;
+
+ private final void update(final int width, final int height) {
+ SwingUtilities.invokeLater(new Runnable() {
+
+ public void run() {
+ //
+ // If the image changed size, resize the component to fit
+ //
+ if (width != imgWidth || height != imgHeight) {
+ renderComponent.setPreferredSize(new Dimension(width, height));
+ imgWidth = width;
+ imgHeight = height;
+ }
+
+ if (renderComponent.isVisible()) {
+ renderComponent.paintImmediately(0, 0,
+ renderComponent.getWidth(), renderComponent.getHeight());
+ }
+ }
+ });
+ }
+
+ private BufferedImage getBufferedImage(int width, int height) {
+ if (currentImage != null && currentImage.getWidth() == width
+ && currentImage.getHeight() == height) {
+ return currentImage;
+ }
+ if (currentImage != null) {
+ currentImage.flush();
+ }
+ currentImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+ currentImage.setAccelerationPriority(0.0f);
+ return currentImage;
+ }
+
+ private class AppSinkListener implements AppSink.NEW_SAMPLE, AppSink.NEW_PREROLL {
+
+ public void rgbFrame(boolean isPrerollFrame, int width, int height, IntBuffer rgb) {
+ // If the EDT is still copying data from the buffer, just drop this frame
+ //
+ if (!bufferLock.tryLock()) {
+ return;
+ }
+
+ //
+ // If there is already a swing update pending, also drop this frame.
+ //
+ if (updatePending && !isPrerollFrame) {
+ bufferLock.unlock();
+ return;
+ }
+ try {
+ final BufferedImage renderImage = getBufferedImage(width, height);
+ int[] pixels = ((DataBufferInt) renderImage.getRaster().getDataBuffer()).getData();
+ rgb.get(pixels, 0, width * height);
+ updatePending = true;
+ } finally {
+ bufferLock.unlock();
+ }
+
+// int scaledWidth = currentImage.getWidth();
+// if (keepAspect) {
+// // Scale width according to pixel aspect ratio.
+// Caps videoCaps = videoPad.getNegotiatedCaps();
+// Structure capsStruct = videoCaps.getStructure(0);
+// if (capsStruct.hasField("pixel-aspect-ratio")) {
+// Fraction pixelAspectRatio = capsStruct.getFraction("pixel-aspect-ratio");
+// scaledWidth = scaledWidth * pixelAspectRatio.getNumerator() / pixelAspectRatio.getDenominator();
+// }
+// }
+ // Tell swing to use the new buffer
+ update(currentImage.getWidth(), currentImage.getHeight());
+ }
+
+ @Override
+ public FlowReturn newSample(AppSink elem) {
+ Sample sample = elem.pullSample();
+ Structure capsStruct = sample.getCaps().getStructure(0);
+ int w = capsStruct.getInteger("width");
+ int h = capsStruct.getInteger("height");
+ Buffer buffer = sample.getBuffer();
+ ByteBuffer bb = buffer.map(false);
+ if (bb != null) {
+ rgbFrame(false, w, h, bb.asIntBuffer());
+ buffer.unmap();
+ }
+ sample.dispose();
+ return FlowReturn.OK;
+ }
+
+ @Override
+ public FlowReturn newPreroll(AppSink elem) {
+ Sample sample = elem.pullPreroll();
+ Structure capsStruct = sample.getCaps().getStructure(0);
+ int w = capsStruct.getInteger("width");
+ int h = capsStruct.getInteger("height");
+ Buffer buffer = sample.getBuffer();
+ ByteBuffer bb = buffer.map(false);
+ if (bb != null) {
+ rgbFrame(false, w, h, bb.asIntBuffer());
+ buffer.unmap();
+ }
+ sample.dispose();
+ return FlowReturn.OK;
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/FXVideoPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/FXVideoPanel.java
index 07736120c7..865123d4ab 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/FXVideoPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/FXVideoPanel.java
@@ -25,6 +25,8 @@ import java.io.IOException;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import javafx.application.Platform;
@@ -66,6 +68,7 @@ import org.sleuthkit.autopsy.corecomponents.VideoFrame;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.VideoUtils;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
+import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@@ -73,11 +76,8 @@ import org.sleuthkit.datamodel.TskData;
/**
* Video viewer part of the Media View layered pane.
*/
-@ServiceProviders(value = {
- @ServiceProvider(service = FrameCapture.class)
-})
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
-public class FXVideoPanel extends MediaViewVideoPanel {
+public class FXVideoPanel extends JPanel implements MediaFileViewer.MediaViewPanel {
// Refer to https://docs.oracle.com/javafx/2/api/javafx/scene/media/package-summary.html
// for Javafx supported formats
@@ -109,8 +109,7 @@ public class FXVideoPanel extends MediaViewVideoPanel {
return this;
}
- @Override
- void setupVideo(final AbstractFile file, final Dimension dims) {
+ void loadFile(final AbstractFile file, final Dimension dims) {
if (file.equals(currentFile)) {
return;
}
@@ -150,7 +149,6 @@ public class FXVideoPanel extends MediaViewVideoPanel {
}
- @Override
void reset() {
Platform.runLater(() -> {
if (mediaPane != null) {
@@ -199,7 +197,6 @@ public class FXVideoPanel extends MediaViewVideoPanel {
private javafx.embed.swing.JFXPanel jFXPanel;
// End of variables declaration//GEN-END:variables
- @Override
public boolean isInited() {
return fxInited;
}
@@ -641,29 +638,49 @@ public class FXVideoPanel extends MediaViewVideoPanel {
}
}
- /**
- * @param file a video file from which to capture frames
- * @param numFrames the number of frames to capture. These frames will be
- * captured at successive intervals given by
- * durationOfVideo/numFrames. If this frame interval is
- * less than MIN_FRAME_INTERVAL_MILLIS, then only one frame
- * will be captured and returned.
- *
- * @return a List of VideoFrames representing the captured frames.
- */
@Override
- public List captureFrames(java.io.File file, int numFrames) throws Exception {
- //What is/was the point of this method /interface.
- return null;
+ public List getSupportedExtensions() {
+ return Arrays.asList(EXTENSIONS.clone());
}
@Override
- public String[] getExtensions() {
- return EXTENSIONS.clone();
- }
-
- @Override
- public List getMimeTypes() {
+ public List getSupportedMimeTypes() {
return MIMETYPES;
}
+
+ @Override
+ public boolean isSupported(AbstractFile file) {
+ String extension = file.getNameExtension();
+ /**
+ * Although it seems too restrictive, requiring both a supported
+ * extension and a supported MIME type prevents two undesirable
+ * behaviors:
+ *
+ * 1) Until AUT-1766 and AUT-1801 are fixed, we incorrectly identify all
+ * iff files as audio/aiff. This means that if this panel went with the
+ * looser 'mime type OR extension' criteria we use for images, then this
+ * panel would attempt (and fail) to display all iff files, even non
+ * audio ones.
+ *
+ * 2) The looser criteria means we are less confident about the files we
+ * are potentialy sending to GStreamer on 32bit jvms. We are less
+ * comfortable with the error handling for GStreamer, and don't want to
+ * send it files which might cause it trouble.
+ */
+ if (getSupportedExtensions().contains("." + extension)) {
+ SortedSet mimeTypes = new TreeSet<>(getSupportedMimeTypes());
+ try {
+ String mimeType = new FileTypeDetector().getMIMEType(file);
+ return mimeTypes.contains(mimeType);
+ } catch (FileTypeDetector.FileTypeDetectorInitException ex) {
+ logger.log(Level.WARNING, "Failed to look up mimetype for " + file.getName() + " using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
+ if (!mimeTypes.isEmpty() && file.isMimeType(mimeTypes) == AbstractFile.MimeMatchEnum.TRUE) {
+ return true;
+ }
+ }
+
+ return getSupportedExtensions().contains("." + extension);
+ }
+ return false;
+ }
}
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoRendererPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoRendererPanel.java
new file mode 100755
index 0000000000..16f2e99854
--- /dev/null
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoRendererPanel.java
@@ -0,0 +1,156 @@
+/*
+ * Autopsy Forensic Browser
+ *
+ * Copyright 2019 Basis Technology Corp.
+ * Contact: carrier sleuthkit org
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.sleuthkit.autopsy.contentviewers;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import javafx.application.Platform;
+import javafx.beans.property.ObjectProperty;
+import javafx.beans.property.SimpleObjectProperty;
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.embed.swing.JFXPanel;
+import javafx.scene.Scene;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.image.PixelFormat;
+import javafx.scene.image.PixelWriter;
+import javafx.scene.image.WritableImage;
+import javafx.scene.layout.BorderPane;
+import org.freedesktop.gstreamer.Buffer;
+import org.freedesktop.gstreamer.Caps;
+import org.freedesktop.gstreamer.FlowReturn;
+import org.freedesktop.gstreamer.Sample;
+import org.freedesktop.gstreamer.Structure;
+import org.freedesktop.gstreamer.elements.AppSink;
+
+/**
+ * This is a video renderer for GStreamer.
+ */
+class GstVideoRendererPanel extends JFXPanel {
+
+ private static final String CAP_MIME_TYPE = "video/x-raw";
+ private static final String CAP_BYTE_ORDER = (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN ? "format=BGRx" : "format=xRGB");
+ private static final int PROP_MAX_BUFFERS = 5000;
+ private AppSink videoSink;
+ private ImageView fxImageView;
+ private BorderPane borderpane;
+
+ /**
+ * Create an instance.
+ */
+ GstVideoRendererPanel() {
+ initImageView();
+ initVideoSink();
+ }
+
+ /**
+ * Initialize the ImageView to show the current frame.
+ */
+ private void initImageView() {
+ fxImageView = new ImageView(); // Will hold the current video frame.
+ borderpane = new BorderPane(fxImageView); // Center and size ImageView.
+ Scene scene = new Scene(borderpane); // Root of the JavaFX tree.
+ setScene(scene);
+
+ // Bind size of image to that of scene, while keeping proportions
+ fxImageView.fitWidthProperty().bind(scene.widthProperty());
+ fxImageView.fitHeightProperty().bind(scene.heightProperty());
+ fxImageView.setPreserveRatio(true);
+ fxImageView.setSmooth(true);
+ fxImageView.setCache(true);
+ }
+
+ /**
+ * Initialize the video sink.
+ */
+ private void initVideoSink() {
+ videoSink = new AppSink("GstVideoComponent");
+ videoSink.set("emit-signals", true);
+ AppSinkListener gstListener = new AppSinkListener();
+ videoSink.connect(gstListener);
+ videoSink.setCaps(new Caps(
+ String.format("%s, %s", CAP_MIME_TYPE, CAP_BYTE_ORDER)));
+ videoSink.set("max-buffers", PROP_MAX_BUFFERS);
+ videoSink.set("drop", true);
+ }
+
+ /**
+ * Get the video sink.
+ *
+ * @return The video sink.
+ */
+ AppSink getVideoSink() {
+ return videoSink;
+ }
+
+ /**
+ * Listen for NEW_SAMPLE events to update the ImageView with the newest
+ * video frame.
+ */
+ class AppSinkListener implements AppSink.NEW_SAMPLE {
+
+ private Image videoFrame;
+ private int lastWidth = 0;
+ private int lastHeight = 0;
+ private byte[] byteArray;
+
+ @Override
+ public FlowReturn newSample(AppSink appSink) {
+ Sample sample = appSink.pullSample();
+ Buffer buffer = sample.getBuffer();
+ ByteBuffer byteBuffer = buffer.map(false);
+ if (byteBuffer != null) {
+ Structure capsStruct = sample.getCaps().getStructure(0);
+ int width = capsStruct.getInteger("width");
+ int height = capsStruct.getInteger("height");
+ if (width != lastWidth || height != lastHeight) {
+ lastWidth = width;
+ lastHeight = height;
+ byteArray = new byte[width * height * 4];
+ }
+ byteBuffer.get(byteArray);
+ videoFrame = convertBytesToImage(byteArray, width, height);
+ Platform.runLater(() -> {
+ fxImageView.setImage(videoFrame);
+ });
+ buffer.unmap();
+ }
+ sample.dispose();
+
+ return FlowReturn.OK;
+ }
+
+ /**
+ * Create an image from a byte array of pixels.
+ *
+ * @param pixels The byte array of pixels.
+ * @param width The width of the image.
+ * @param height The height of the image.
+ *
+ * @return The image.
+ */
+ private Image convertBytesToImage(byte[] pixels, int width, int height) {
+ WritableImage img = new WritableImage(width, height);
+ PixelWriter pw = img.getPixelWriter();
+ pw.setPixels(0, 0, width, height, PixelFormat.getByteBgraInstance(), pixels, 0, width * 4);
+ return img;
+ }
+ }
+}
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java
index 963e3f8b39..0085a2d4e8 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaFileViewer.java
@@ -36,24 +36,24 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
private static final Logger LOGGER = Logger.getLogger(MediaFileViewer.class.getName());
private AbstractFile lastFile;
//UI
- private final MediaViewVideoPanel videoPanel;
- private final boolean videoPanelInited;
+ private final MediaPlayerPanel mediaPlayerPanel;
+ private final boolean mediaPlayerPanelInited;
private final MediaViewImagePanel imagePanel;
private final boolean imagePanelInited;
private static final String IMAGE_VIEWER_LAYER = "IMAGE"; //NON-NLS
- private static final String VIDEO_VIEWER_LAYER = "VIDEO"; //NON-NLS
+ private static final String MEDIA_PLAYER_LAYER = "AUDIO_VIDEO"; //NON-NLS
/**
- * Creates new form DataContentViewerVideo
+ * Creates a new MediaFileViewer.
*/
public MediaFileViewer() {
initComponents();
// get the right panel for our platform
- videoPanel = MediaViewVideoPanel.createVideoPanel();
- videoPanelInited = videoPanel.isInited();
+ mediaPlayerPanel = new MediaPlayerPanel();
+ mediaPlayerPanelInited = mediaPlayerPanel.isInited();
imagePanel = new MediaViewImagePanel();
imagePanelInited = imagePanel.isInited();
@@ -64,9 +64,9 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
private void customizeComponents() {
add(imagePanel, IMAGE_VIEWER_LAYER);
- add(videoPanel, VIDEO_VIEWER_LAYER);
+ add(mediaPlayerPanel, MEDIA_PLAYER_LAYER);
- showVideoPanel(false);
+ showImagePanel();
}
/**
@@ -94,8 +94,8 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
List mimeTypes = new ArrayList<>();
- mimeTypes.addAll(this.imagePanel.getMimeTypes());
- mimeTypes.addAll(this.videoPanel.getMimeTypes());
+ mimeTypes.addAll(this.imagePanel.getSupportedMimeTypes());
+ mimeTypes.addAll(this.mediaPlayerPanel.getSupportedMimeTypes());
return mimeTypes;
}
@@ -123,12 +123,12 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
final Dimension dims = MediaFileViewer.this.getSize();
//logger.info("setting node on media viewer"); //NON-NLS
- if (videoPanelInited && videoPanel.isSupported(file)) {
- videoPanel.setupVideo(file, dims);
- this.showVideoPanel(true);
+ if (mediaPlayerPanelInited && mediaPlayerPanel.isSupported(file)) {
+ mediaPlayerPanel.loadFile(file, dims);
+ this.showVideoPanel();
} else if (imagePanelInited && imagePanel.isSupported(file)) {
imagePanel.showImageFx(file, dims);
- this.showVideoPanel(false);
+ this.showImagePanel();
}
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Exception while setting node", e); //NON-NLS
@@ -136,17 +136,19 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
}
/**
- * switch to visible video or image panel
- *
- * @param showVideo true if video panel, false if image panel
+ * Show the media player panel.
*/
- private void showVideoPanel(boolean showVideo) {
+ private void showVideoPanel() {
CardLayout layout = (CardLayout) this.getLayout();
- if (showVideo) {
- layout.show(this, VIDEO_VIEWER_LAYER);
- } else {
- layout.show(this, IMAGE_VIEWER_LAYER);
- }
+ layout.show(this, MEDIA_PLAYER_LAYER);
+ }
+
+ /**
+ * Show the image panel.
+ */
+ private void showImagePanel() {
+ CardLayout layout = (CardLayout) this.getLayout();
+ layout.show(this, IMAGE_VIEWER_LAYER);
}
@Override
@@ -156,24 +158,24 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
@Override
public void resetComponent() {
- videoPanel.reset();
+ mediaPlayerPanel.reset();
imagePanel.reset();
lastFile = null;
}
- interface MediaViewPanel {
+ protected interface MediaViewPanel {
/**
* @return supported mime types
*/
- List getMimeTypes();
+ List getSupportedMimeTypes();
/**
* returns supported extensions (each starting with .)
*
* @return
*/
- List getExtensionsList();
+ List getSupportedExtensions();
boolean isSupported(AbstractFile file);
}
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.form b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.form
similarity index 100%
rename from Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.form
rename to Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.form
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java
similarity index 65%
rename from Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.java
rename to Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java
index a1ca715a19..4522c406b4 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaPlayerPanel.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2013-2018 Basis Technology Corp.
+ * Copyright 2013-2019 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -20,20 +20,19 @@ package org.sleuthkit.autopsy.contentviewers;
import com.google.common.io.Files;
import java.awt.Dimension;
-import java.awt.Image;
-import java.awt.image.BufferedImage;
+import java.awt.EventQueue;
import java.io.File;
import java.io.IOException;
-import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import javax.swing.BoxLayout;
import javax.swing.JButton;
@@ -43,14 +42,18 @@ import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.event.ChangeEvent;
-import org.gstreamer.ClockTime;
-import org.gstreamer.Gst;
-import org.gstreamer.GstException;
-import org.gstreamer.State;
-import org.gstreamer.StateChangeReturn;
-import org.gstreamer.elements.PlayBin2;
-import org.gstreamer.elements.RGBDataSink;
-import org.gstreamer.swing.VideoComponent;
+import org.freedesktop.gstreamer.Bus;
+import org.freedesktop.gstreamer.ClockTime;
+import org.freedesktop.gstreamer.Format;
+import org.freedesktop.gstreamer.Gst;
+import org.freedesktop.gstreamer.GstException;
+import org.freedesktop.gstreamer.GstObject;
+import org.freedesktop.gstreamer.Message;
+import org.freedesktop.gstreamer.MessageType;
+import org.freedesktop.gstreamer.State;
+import org.freedesktop.gstreamer.StateChangeReturn;
+import org.freedesktop.gstreamer.Structure;
+import org.freedesktop.gstreamer.elements.PlayBin;
import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
@@ -62,39 +65,133 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.VideoUtils;
import org.sleuthkit.autopsy.datamodel.ContentUtils;
+import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
-@ServiceProviders(value = {
- @ServiceProvider(service = FrameCapture.class)
-})
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
-public class GstVideoPanel extends MediaViewVideoPanel {
+public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaViewPanel {
- private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS
- private static final List MIMETYPES = Arrays.asList("video/quicktime", "audio/mpeg", "audio/x-mpeg", "video/mpeg", "video/x-mpeg", "audio/mpeg3", "audio/x-mpeg-3", "video/x-flv", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
+ private static final String[] EXTENSIONS = new String[] {
+ ".3gp",
+ ".aac", //froze
+ ".aif",
+ ".aiff",
+ ".amr",
+ ".asf", //froze
+ ".au",
+ ".avi",
+ ".flac",
+ ".flv",
+ ".m4a",
+ ".m4v",
+ ".mka",
+ ".mkv",
+ ".mov",
+ ".mp2", //froze
+ ".mp3", //froze
+ ".mp4",
+ ".mpeg",
+ ".mpg",
+ ".mxf",
+ ".ogg",
+ ".ra", //froze
+ ".wav",
+ ".webm",
+ ".wma",
+ ".wmv",
+ }; //NON-NLS
+ private static final List MIMETYPES = Arrays.asList(
+ "video/3gpp", //tested
+ "audio/aiff", //tested
+ "audio/amr-wb",
+ "audio/basic",
+ "audio/mp4", //tested
+ "video/mp4", //tested
+ "audio/mpeg", //froze
+ "video/mpeg", //tested
+ "audio/mpeg3",
+ "application/mxf", //tested
+ "application/ogg",
+ "video/quicktime", //tested
+ "audio/vorbis", //tested
+ "application/vnd.rn-realmedia",
+ "audio/vnd.wave", //tested
+ "video/webm", //tested
+ "video/x-3ivx",
+ "audio/x-aac",
+ "audio/x-adpcm",
+ "audio/x-alaw",
+ "audio/x-cinepak",
+ "video/x-divx",
+ "audio/x-dv",
+ "video/x-dv",
+ "video/x-ffv",
+ "audio/x-flac", //tested
+ "video/x-flv", //tested
+ "audio/x-gsm",
+ "video/x-h263",
+ "video/x-h264",
+ "video/x-huffyuv",
+ "video/x-indeo",
+ "video/x-intel-h263",
+ "audio/x-ircam",
+ "video/x-jpeg",
+ "audio/x-m4a",
+ "video/x-m4v", //tested
+ "audio/x-mace",
+ "audio/x-matroska", //tested
+ "video/x-matroska", //tested
+ "audio/x-mpeg",
+ "video/x-mpeg",
+ "audio/x-mpeg-3",
+ "video/x-ms-asf",
+ "audio/x-ms-wma", //tested
+ "video/x-ms-wmv", //tested
+ "video/x-msmpeg",
+ "video/x-msvideo", //tested
+ "video/x-msvideocodec",
+ "audio/x-mulaw",
+ "audio/x-nist",
+ "audio/x-oggflac", //tested
+ "audio/x-paris",
+ "audio/x-qdm2",
+ "audio/x-raw",
+ "video/x-raw",
+ "video/x-rle",
+ "audio/x-speex",
+ "video/x-svq",
+ "audio/x-svx",
+ "video/x-tarkin",
+ "video/x-theora",
+ "audio/x-voc",
+ "audio/x-vorbis",
+ "video/x-vp3",
+ "audio/x-w64",
+ "audio/x-wav",
+ "audio/x-wma",
+ "video/x-wmv",
+ "video/x-xvid"
+ ); //NON-NLS
- private static final Logger logger = Logger.getLogger(GstVideoPanel.class.getName());
+ private static final Logger logger = Logger.getLogger(MediaPlayerPanel.class.getName());
private boolean gstInited;
- private static final long MIN_FRAME_INTERVAL_MILLIS = 500;
- private static final long FRAME_CAPTURE_TIMEOUT_MILLIS = 1000;
- private static final String MEDIA_PLAYER_ERROR_STRING = NbBundle.getMessage(GstVideoPanel.class, "GstVideoPanel.cannotProcFile.err");
+ private static final String MEDIA_PLAYER_ERROR_STRING = NbBundle.getMessage(MediaPlayerPanel.class, "GstVideoPanel.cannotProcFile.err");
//playback
private long durationMillis = 0;
private VideoProgressWorker videoProgressWorker;
private int totalHours, totalMinutes, totalSeconds;
- private volatile PlayBin2 gstPlaybin2;
- private VideoComponent gstVideoComponent;
+ private volatile PlayBin gstPlayBin;
+ private GstVideoRendererPanel gstVideoRenderer;
private boolean autoTracking = false; // true if the slider is moving automatically
- private final Object playbinLock = new Object(); // lock for synchronization of gstPlaybin2 player
+ private final Object playbinLock = new Object(); // lock for synchronization of gstPlayBin player
private AbstractFile currentFile;
- private final Set badVideoFiles = Collections.synchronizedSet(new HashSet<>());
/**
* Creates new form MediaViewVideoPanel
*/
- public GstVideoPanel() {
+ public MediaPlayerPanel() {
initComponents();
customizeComponents();
}
@@ -115,11 +212,11 @@ public class GstVideoPanel extends MediaViewVideoPanel {
return videoPanel;
}
- public VideoComponent getVideoComponent() {
- return gstVideoComponent;
- }
-
- @Override
+ /**
+ * Has this MediaPlayerPanel been initialized correctly?
+ *
+ * @return
+ */
public boolean isInited() {
return gstInited;
}
@@ -140,19 +237,19 @@ public class GstVideoPanel extends MediaViewVideoPanel {
*/
int time = progressSlider.getValue();
synchronized (playbinLock) {
- if (gstPlaybin2 != null && !autoTracking) {
- State orig = gstPlaybin2.getState();
- if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed."); //NON-NLS
+ if (gstPlayBin != null && !autoTracking) {
+ State orig = gstPlayBin.getState();
+ if (gstPlayBin.pause() == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.pause() failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
- if (gstPlaybin2.seek(ClockTime.fromMillis(time)) == false) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.seek() failed."); //NON-NLS
+ if (gstPlayBin.seek(ClockTime.fromMillis(time)) == false) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.seek() failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
- gstPlaybin2.setState(orig);
+ gstPlayBin.setState(orig);
}
}
});
@@ -182,9 +279,14 @@ public class GstVideoPanel extends MediaViewVideoPanel {
return true;
}
- @Override
+ /**
+ * Initialize all the necessary variables to play an audio/video file.
+ *
+ * @param file Media file to play.
+ * @param dims Dimension of the parent window.
+ */
@NbBundle.Messages ({"GstVideoPanel.noOpenCase.errMsg=No open case available."})
- void setupVideo(final AbstractFile file, final Dimension dims) {
+ void loadFile(final AbstractFile file, final Dimension dims) {
reset();
infoLabel.setText("");
currentFile = file;
@@ -221,32 +323,75 @@ public class GstVideoPanel extends MediaViewVideoPanel {
progressSlider.setEnabled(true);
- gstVideoComponent = new VideoComponent();
+ //gstVideoComponent = new SimpleVideoComponent();
+ gstVideoRenderer = new GstVideoRendererPanel();
synchronized (playbinLock) {
- if (gstPlaybin2 != null) {
- gstPlaybin2.dispose();
+ if (gstPlayBin != null) {
+ gstPlayBin.dispose();
}
- gstPlaybin2 = new PlayBin2("VideoPlayer"); //NON-NLS
- gstPlaybin2.setVideoSink(gstVideoComponent.getElement());
+ gstPlayBin = new PlayBin("VideoPlayer"); //NON-NLS
+ gstPlayBin.setVideoSink(gstVideoRenderer.getVideoSink());
videoPanel.removeAll();
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
- videoPanel.add(gstVideoComponent);
+ //videoPanel.add(gstVideoComponent);
+
+ EventQueue.invokeLater(() -> {
+ videoPanel.add(gstVideoRenderer);//add jfx ui to JPanel
+ });
videoPanel.setVisible(true);
- gstPlaybin2.setInputFile(ioFile);
+ gstPlayBin.setInputFile(ioFile);
- if (gstPlaybin2.setState(State.READY) == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.READY) failed."); //NON-NLS
+ if (gstPlayBin.setState(State.READY) == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.setState(State.READY) failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
+
+ gstPlayBin.getBus().connect(new Bus.EOS() {
+ @Override
+ public void endOfStream(GstObject source) {
+ long test = durationMillis;
+ System.out.println(test);
+ System.out.println();
+ }
+ });
+ gstPlayBin.getBus().connect(new Bus.ERROR() {
+ @Override
+ public void errorMessage(GstObject source, int code, String message) {
+ long test = durationMillis;
+ System.out.println(test);
+ System.out.println();
+ }
+ });
+ gstPlayBin.getBus().connect(new Bus.STATE_CHANGED() {
+ @Override
+ public void stateChanged(GstObject source, State old, State current, State pending) {
+ if (durationMillis == 0 && current.equals(State.PLAYING)) {
+ durationMillis = gstPlayBin.queryDuration().toMillis();
+
+ // pick out the total hours, minutes, seconds
+ long durationSeconds = (int) durationMillis / 1000;
+ totalHours = (int) durationSeconds / 3600;
+ durationSeconds -= totalHours * 3600;
+ totalMinutes = (int) durationSeconds / 60;
+ durationSeconds -= totalMinutes * 60;
+ totalSeconds = (int) durationSeconds;
+ }
+ long test = durationMillis;
+ System.out.println(test);
+ System.out.println();
+ }
+ });
}
}
- @Override
+ /**
+ * Prepare this MediaViewVideoPanel to accept a different media file.
+ */
void reset() {
// reset the progress label text on the event dispatch thread
@@ -259,25 +404,26 @@ public class GstVideoPanel extends MediaViewVideoPanel {
}
synchronized (playbinLock) {
- if (gstPlaybin2 != null) {
- if (gstPlaybin2.isPlaying()) {
- if (gstPlaybin2.stop() == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.stop() failed."); //NON-NLS
+ if (gstPlayBin != null) {
+ if (gstPlayBin.isPlaying()) {
+ if (gstPlayBin.stop() == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.stop() failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
}
- if (gstPlaybin2.setState(State.NULL) == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.NULL) failed."); //NON-NLS
+ if (gstPlayBin.setState(State.NULL) == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.setState(State.NULL) failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
- if (gstPlaybin2.getState().equals(State.NULL)) {
- gstPlaybin2.dispose();
+ if (gstPlayBin.getState().equals(State.NULL)) {
+ gstPlayBin.dispose();
}
- gstPlaybin2 = null;
+ gstPlayBin = null;
}
- gstVideoComponent = null;
+ //gstVideoComponent = null;
+ gstVideoRenderer = null;
}
// get rid of any existing videoProgressWorker thread
@@ -289,152 +435,6 @@ public class GstVideoPanel extends MediaViewVideoPanel {
currentFile = null;
}
- /**
- * @param file a video file from which to capture frames
- * @param numFrames the number of frames to capture. These frames will be
- * captured at successive intervals given by
- * durationOfVideo/numFrames. If this frame interval is
- * less than MIN_FRAME_INTERVAL_MILLIS, then only one frame
- * will be captured and returned.
- *
- * @return a List of VideoFrames representing the captured frames.
- */
- @Override
- public List captureFrames(java.io.File file, int numFrames) throws Exception {
-
- List frames = new ArrayList<>();
-
- Object lock = new Object();
- FrameCaptureRGBListener rgbListener = new FrameCaptureRGBListener(lock);
-
- if (!isInited()) {
- return frames;
- }
-
- // throw exception if this file is known to be problematic
- if (badVideoFiles.contains(file.getName())) {
- throw new Exception(
- NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemFile.msg", file.getName()));
- }
-
- // set up a PlayBin2 object
- RGBDataSink videoSink = new RGBDataSink("rgb", rgbListener); //NON-NLS
- PlayBin2 playbin = new PlayBin2("VideoFrameCapture"); //NON-NLS
- playbin.setInputFile(file);
- playbin.setVideoSink(videoSink);
-
- // this is necessary to get a valid duration value
- StateChangeReturn ret = playbin.play();
- if (ret == StateChangeReturn.FAILURE) {
- // add this file to the set of known bad ones
- badVideoFiles.add(file.getName());
- throw new Exception(NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemPlay.msg"));
- }
- ret = playbin.pause();
- if (ret == StateChangeReturn.FAILURE) {
- // add this file to the set of known bad ones
- badVideoFiles.add(file.getName());
- throw new Exception(NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemPause.msg"));
- }
- playbin.getState();
-
- // get the duration of the video
- TimeUnit unit = TimeUnit.MILLISECONDS;
- long myDurationMillis = playbin.queryDuration(unit);
- if (myDurationMillis <= 0) {
- return frames;
- }
-
- // calculate the number of frames to capture
- int numFramesToGet = numFrames;
- long frameInterval = myDurationMillis / numFrames;
- if (frameInterval < MIN_FRAME_INTERVAL_MILLIS) {
- numFramesToGet = 1;
- }
-
- // for each timeStamp, grap a frame
- for (int i = 0; i < numFramesToGet; ++i) {
- long timeStamp = i * frameInterval;
-
- ret = playbin.pause();
- if (ret == StateChangeReturn.FAILURE) {
- // add this file to the set of known bad ones
- badVideoFiles.add(file.getName());
- throw new Exception(
- NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemPauseCaptFrame.msg"));
- }
- playbin.getState();
-
- if (!playbin.seek(timeStamp, unit)) {
- logger.log(Level.INFO, "There was a problem seeking to {0} {1}", new Object[]{timeStamp, unit.name().toLowerCase()}); //NON-NLS
- }
-
- ret = playbin.play();
- if (ret == StateChangeReturn.FAILURE) {
- // add this file to the set of known bad ones
- badVideoFiles.add(file.getName());
- throw new Exception(
- NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemPlayCaptFrame.msg"));
- }
-
- // wait for FrameCaptureRGBListener to finish
- synchronized (lock) {
- try {
- lock.wait(FRAME_CAPTURE_TIMEOUT_MILLIS);
- } catch (InterruptedException e) {
- logger.log(Level.INFO, "InterruptedException occurred while waiting for frame capture.", e); //NON-NLS
- }
- }
- Image image = rgbListener.getImage();
-
- ret = playbin.stop();
- if (ret == StateChangeReturn.FAILURE) {
- // add this file to the set of known bad ones
- badVideoFiles.add(file.getName());
- throw new Exception(
- NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemStopCaptFrame.msg"));
- }
-
- if (image == null) {
- logger.log(Level.WARNING, "There was a problem while trying to capture a frame from file {0}", file.getName()); //NON-NLS
- badVideoFiles.add(file.getName());
- break;
- }
-
- frames.add(new VideoFrame(image, timeStamp));
- }
-
- return frames;
- }
-
- private class FrameCaptureRGBListener implements RGBDataSink.Listener {
-
- public FrameCaptureRGBListener(Object waiter) {
- this.waiter = waiter;
- }
-
- private BufferedImage bi;
- private final Object waiter;
-
- @Override
- public void rgbFrame(boolean bln, int w, int h, IntBuffer rgbPixels) {
- synchronized (waiter) {
- bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
- bi.setRGB(0, 0, w, h, rgbPixels.array(), 0, w);
- waiter.notify();
- }
- }
-
- public Image getImage() {
- synchronized (waiter) {
- Image image = bi;
- bi = null;
- return image;
- }
- }
-
- }
-
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
@@ -462,16 +462,16 @@ public class GstVideoPanel extends MediaViewVideoPanel {
.addGap(0, 231, Short.MAX_VALUE)
);
- org.openide.awt.Mnemonics.setLocalizedText(pauseButton, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.pauseButton.text")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(pauseButton, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaViewVideoPanel.pauseButton.text")); // NOI18N
pauseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
pauseButtonActionPerformed(evt);
}
});
- org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.progressLabel.text")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaViewVideoPanel.progressLabel.text")); // NOI18N
- org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.infoLabel.text")); // NOI18N
+ org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaViewVideoPanel.infoLabel.text")); // NOI18N
javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel);
controlPanel.setLayout(controlPanelLayout);
@@ -523,30 +523,33 @@ public class GstVideoPanel extends MediaViewVideoPanel {
private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed
synchronized (playbinLock) {
- State state = gstPlaybin2.getState();
+ if (gstPlayBin == null) {
+ return;
+ }
+ State state = gstPlayBin.getState();
if (state.equals(State.PLAYING)) {
- if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed."); //NON-NLS
+ if (gstPlayBin.pause() == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.pause() failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
pauseButton.setText("►");
- // Is this call necessary considering we just called gstPlaybin2.pause()?
- if (gstPlaybin2.setState(State.PAUSED) == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.PAUSED) failed."); //NON-NLS
+ // Is this call necessary considering we just called gstPlayBin.pause()?
+ if (gstPlayBin.setState(State.PAUSED) == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.setState(State.PAUSED) failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
} else if (state.equals(State.PAUSED)) {
- if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed."); //NON-NLS
+ if (gstPlayBin.play() == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.play() failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
pauseButton.setText("||");
- // Is this call necessary considering we just called gstPlaybin2.play()?
- if (gstPlaybin2.setState(State.PLAYING) == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.PLAYING) failed."); //NON-NLS
+ // Is this call necessary considering we just called gstPlayBin.play()?
+ if (gstPlayBin.setState(State.PLAYING) == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.setState(State.PLAYING) failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
@@ -584,23 +587,23 @@ public class GstVideoPanel extends MediaViewVideoPanel {
private boolean isPlayBinReady() {
synchronized (playbinLock) {
- return gstPlaybin2 != null && !gstPlaybin2.getState().equals(State.NULL);
+ return gstPlayBin != null && !gstPlayBin.getState().equals(State.NULL);
}
}
private void resetVideo() throws Exception {
synchronized (playbinLock) {
- if (gstPlaybin2 != null) {
- if (gstPlaybin2.stop() == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.stop() failed."); //NON-NLS
+ if (gstPlayBin != null) {
+ if (gstPlayBin.stop() == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.stop() failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
// ready to be played again
- if (gstPlaybin2.setState(State.READY) == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.READY) failed."); //NON-NLS
+ if (gstPlayBin.setState(State.READY) == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.setState(State.READY) failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
- gstPlaybin2.getState(); //NEW
+ gstPlayBin.getState(); //NEW
}
}
pauseButton.setText("►");
@@ -631,7 +634,7 @@ public class GstVideoPanel extends MediaViewVideoPanel {
while (hasNotEnded() && isPlayBinReady() && !isCancelled()) {
synchronized (playbinLock) {
- pos = gstPlaybin2.queryPosition();
+ pos = gstPlayBin.queryPosition();
}
millisElapsed = pos.toMillis();
@@ -699,7 +702,7 @@ public class GstVideoPanel extends MediaViewVideoPanel {
@Override
protected Long doInBackground() throws Exception {
if (tempFile.exists() == false || tempFile.length() < sourceFile.getSize()) {
- progress = ProgressHandle.createHandle(NbBundle.getMessage(GstVideoPanel.class, "GstVideoPanel.ExtractMedia.progress.buffering", sourceFile.getName()), () -> ExtractMedia.this.cancel(true));
+ progress = ProgressHandle.createHandle(NbBundle.getMessage(MediaPlayerPanel.class, "GstVideoPanel.ExtractMedia.progress.buffering", sourceFile.getName()), () -> ExtractMedia.this.cancel(true));
progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.buffering"));
progress.start(100);
try {
@@ -744,20 +747,26 @@ public class GstVideoPanel extends MediaViewVideoPanel {
ClockTime dur;
synchronized (playbinLock) {
// must play, then pause and get state to get duration.
- if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed."); //NON-NLS
+ if (gstPlayBin.play() == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.play() failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
}
- if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed."); //NON-NLS
+ /*gstPlayBin.getBus().connect(new Bus.DURATION() {
+ @Override
+ public void durationChanged(GstObject go, Format format, long l) {
+ System.out.println();
+ }
+ });*/
+ //DLG:
+ /*if (gstPlayBin.pause() == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.pause() failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
return;
- }
- gstPlaybin2.getState();
- dur = gstPlaybin2.queryDuration();
+ }*/
+ State state = gstPlayBin.getState();
+ dur = gstPlayBin.queryDuration();
}
- durationMillis = dur.toMillis();
// pick out the total hours, minutes, seconds
long durationSeconds = (int) durationMillis / 1000;
@@ -771,12 +780,12 @@ public class GstVideoPanel extends MediaViewVideoPanel {
progressSlider.setMaximum((int) durationMillis);
progressSlider.setMinimum(0);
- synchronized (playbinLock) {
- if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
- logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed."); //NON-NLS
+ /*synchronized (playbinLock) {
+ if (gstPlayBin.play() == StateChangeReturn.FAILURE) {
+ logger.log(Level.WARNING, "Attempt to call PlayBin.play() failed."); //NON-NLS
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
- }
+ }*/
pauseButton.setText("||");
videoProgressWorker = new VideoProgressWorker();
videoProgressWorker.execute();
@@ -785,13 +794,49 @@ public class GstVideoPanel extends MediaViewVideoPanel {
}
@Override
- public String[] getExtensions() {
- return EXTENSIONS.clone();
+ public List getSupportedExtensions() {
+ return Arrays.asList(EXTENSIONS.clone());
}
@Override
- public List getMimeTypes() {
+ public List getSupportedMimeTypes() {
return MIMETYPES;
}
+ @Override
+ public boolean isSupported(AbstractFile file) {
+ String extension = file.getNameExtension();
+ /**
+ * Although it seems too restrictive, requiring both a supported
+ * extension and a supported MIME type prevents two undesirable
+ * behaviors:
+ *
+ * 1) Until AUT-1766 and AUT-1801 are fixed, we incorrectly identify all
+ * iff files as audio/aiff. This means that if this panel went with the
+ * looser 'mime type OR extension' criteria we use for images, then this
+ * panel would attempt (and fail) to display all iff files, even non
+ * audio ones.
+ *
+ * 2) The looser criteria means we are less confident about the files we
+ * are potentialy sending to GStreamer on 32bit jvms. We are less
+ * comfortable with the error handling for GStreamer, and don't want to
+ * send it files which might cause it trouble.
+ */
+ if (getSupportedExtensions().contains("." + extension)) {
+ SortedSet mimeTypes = new TreeSet<>(getSupportedMimeTypes());
+ try {
+ String mimeType = new FileTypeDetector().getMIMEType(file);
+ return mimeTypes.contains(mimeType);
+ } catch (FileTypeDetector.FileTypeDetectorInitException ex) {
+ logger.log(Level.WARNING, "Failed to look up mimetype for " + file.getName() + " using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
+ if (!mimeTypes.isEmpty() && file.isMimeType(mimeTypes) == AbstractFile.MimeMatchEnum.TRUE) {
+ return true;
+ }
+ }
+
+ return getSupportedExtensions().contains("." + extension);
+ }
+ return false;
+ }
+
}
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java
index da526803a8..88ad22d1c1 100644
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java
+++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java
@@ -230,7 +230,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
* @return supported mime types
*/
@Override
- public List getMimeTypes() {
+ public List getSupportedMimeTypes() {
return Collections.unmodifiableList(Lists.newArrayList(supportedMimes));
}
@@ -240,7 +240,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
* @return
*/
@Override
- public List getExtensionsList() {
+ public List getSupportedExtensions() {
return getExtensions();
}
diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewVideoPanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewVideoPanel.java
deleted file mode 100644
index 0a203a0909..0000000000
--- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewVideoPanel.java
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Autopsy Forensic Browser
- *
- * Copyright 2013-2018 Basis Technology Corp.
- * Contact: carrier sleuthkit org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.sleuthkit.autopsy.contentviewers;
-
-import java.awt.Dimension;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Set;
-import java.util.SortedSet;
-import java.util.TreeSet;
-import java.util.logging.Level;
-import javax.swing.JPanel;
-import org.sleuthkit.autopsy.corecomponents.FrameCapture;
-import org.sleuthkit.autopsy.coreutils.Logger;
-import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
-import org.sleuthkit.datamodel.AbstractFile;
-
-/**
- * Video viewer part of the Media View layered pane. Uses different engines
- * depending on platform.
- */
-abstract class MediaViewVideoPanel extends JPanel implements FrameCapture, MediaFileViewer.MediaViewPanel {
-
- private static final Set AUDIO_EXTENSIONS = new TreeSet<>(Arrays.asList(".mp3", ".wav", ".wma")); //NON-NLS
-
- private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
-
- // 64 bit architectures
- private static final String[] ARCH64 = new String[]{"amd64", "x86_64"}; //NON-NLS NON-NLS
-
- // 32 bit architectures
- private static final String[] ARCH32 = new String[]{"x86"}; //NON-NLS
-
- /**
- * Factory Method to create a MediaViewVideoPanel.
- *
- * Implementation is dependent on the architecture of the JVM.
- *
- * @return a MediaViewVideoPanel instance.
- */
- public static MediaViewVideoPanel createVideoPanel() {
- if (is64BitJVM()) {
- logger.log(Level.INFO, "64 bit JVM detected. Creating JavaFX Video Player."); //NON-NLS
- return getFXImpl();
- } else {
- logger.log(Level.INFO, "32 bit JVM detected. Creating GStreamer Video Player."); //NON-NLS
- return getGstImpl();
- }
- }
-
- /**
- * Is the JVM architecture 64 bit?
- *
- * @return
- */
- private static boolean is64BitJVM() {
- String arch = System.getProperty("os.arch");
- return Arrays.asList(ARCH64).contains(arch);
- }
-
- /**
- * Get a GStreamer video player implementation.
- *
- * @return a GstVideoPanel
- */
- private static MediaViewVideoPanel getGstImpl() {
- return new GstVideoPanel();
- }
-
- /**
- * Get a JavaFX video player implementation.
- *
- * @return a FXVideoPanel
- */
- private static MediaViewVideoPanel getFXImpl() {
- return new FXVideoPanel();
- }
-
- /**
- * Has this MediaViewVideoPanel been initialized correctly?
- *
- * @return
- */
- public abstract boolean isInited();
-
- /**
- * Prepare this MediaViewVideoPanel to accept a different media file.
- */
- abstract void reset();
-
- /**
- * Initialize all the necessary vars to play a video/audio file.
- *
- * @param file video file to play
- * @param dims dimension of the parent window
- */
- abstract void setupVideo(final AbstractFile file, final Dimension dims);
-
- /**
- * Return the extensions supported by this video panel.
- *
- * @return
- */
- abstract public String[] getExtensions();
-
- /**
- * Return the MimeTypes supported by this video panel.
- */
- @Override
- abstract public List getMimeTypes();
-
- @Override
- public List getExtensionsList() {
- return Arrays.asList(getExtensions());
- }
-
- @Override
- public boolean isSupported(AbstractFile file) {
- String extension = file.getNameExtension();
- /**
- * Although it seems too restrictive, requiring both a supported
- * extension and a supported MIME type prevents two undesirable
- * behaviors:
- *
- * 1) Until AUT-1766 and AUT-1801 are fixed, we incorrectly identify all
- * iff files as audio/aiff. This means that if this panel went with the
- * looser 'mime type OR extension' criteria we use for images, then this
- * panel would attempt (and fail) to display all iff files, even non
- * audio ones.
- *
- * 2) The looser criteria means we are less confident about the files we
- * are potentialy sending to GStreamer on 32bit jvms. We are less
- * comfortable with the error handling for GStreamer, and don't want to
- * send it files which might cause it trouble.
- */
- if (AUDIO_EXTENSIONS.contains("." + extension) || getExtensionsList().contains("." + extension)) {
- SortedSet mimeTypes = new TreeSet<>(getMimeTypes());
- try {
- String mimeType = new FileTypeDetector().getMIMEType(file);
- return mimeTypes.contains(mimeType);
- } catch (FileTypeDetector.FileTypeDetectorInitException ex) {
- logger.log(Level.WARNING, "Failed to look up mimetype for " + file.getName() + " using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
- if (!mimeTypes.isEmpty() && file.isMimeType(mimeTypes) == AbstractFile.MimeMatchEnum.TRUE) {
- return true;
- }
- }
-
- return getExtensionsList().contains("." + extension);
- }
- return false;
- }
-}
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/GSTVideoPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/GSTVideoPanel.java
deleted file mode 100755
index 4c49490373..0000000000
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/GSTVideoPanel.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Autopsy Forensic Browser
- *
- * Copyright 2018 Basis Technology Corp.
- * Contact: carrier sleuthkit org
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.sleuthkit.autopsy.corecomponents;
-
-/**
- * This class exists to support backwards compatibility of an erroneous call to
- * Logger.getLogger(GSTVideoPanel.class.getName()) in OpenCVFrameCapture.java in
- * an older version of the Video Triage Net Beans Module (NBM). It should be
- * removed when we are ready to stop supporting older Video Triage NBMs. The
- * current Video Triage code has already been updated.
- */
-@Deprecated
-public class GSTVideoPanel {
-
-}
diff --git a/CoreLibs/ivy.xml b/CoreLibs/ivy.xml
index a5f7aab768..8ba1ccf7bc 100644
--- a/CoreLibs/ivy.xml
+++ b/CoreLibs/ivy.xml
@@ -11,6 +11,8 @@
+
+
@@ -66,5 +68,8 @@
+
+
+
diff --git a/CoreLibs/nbproject/project.properties b/CoreLibs/nbproject/project.properties
index 08ccc6fea1..768e8ce45c 100644
--- a/CoreLibs/nbproject/project.properties
+++ b/CoreLibs/nbproject/project.properties
@@ -24,6 +24,8 @@ file.reference.dom4j-1.6.1.jar=release/modules/ext/dom4j-1.6.1.jar
file.reference.geronimo-jms_1.1_spec-1.0.jar=release/modules/ext/geronimo-jms_1.1_spec-1.0.jar
file.reference.gson-1.4.jar=release/modules/ext/gson-1.4.jar
file.reference.gstreamer-java-1.5.jar=release/modules/ext/gstreamer-java-1.5.jar
+file.reference.gst1-java-core-0.9.3.jar=release/modules/ext/gst1-java-core-0.9.3.jar
+file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar
file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar
file.reference.imageio-bmp-3.2.jar=release/modules/ext/imageio-bmp-3.2.jar
file.reference.imageio-core-3.2.jar=release/modules/ext/imageio-core-3.2.jar
diff --git a/CoreLibs/nbproject/project.xml b/CoreLibs/nbproject/project.xml
index 38da548a38..1a48f31d58 100644
--- a/CoreLibs/nbproject/project.xml
+++ b/CoreLibs/nbproject/project.xml
@@ -583,6 +583,24 @@
org.dom4j.util
org.dom4j.xpath
org.dom4j.xpp
+ org.freedesktop.gstreamer
+ org.freedesktop.gstreamer.controller
+ org.freedesktop.gstreamer.elements
+ org.freedesktop.gstreamer.elements.good
+ org.freedesktop.gstreamer.event
+ org.freedesktop.gstreamer.example
+ org.freedesktop.gstreamer.glib
+ org.freedesktop.gstreamer.interfaces
+ org.freedesktop.gstreamer.io
+ org.freedesktop.gstreamer.lowlevel
+ org.freedesktop.gstreamer.lowlevel.annotations
+ org.freedesktop.gstreamer.media
+ org.freedesktop.gstreamer.media.event
+ org.freedesktop.gstreamer.message
+ org.freedesktop.gstreamer.query
+ org.freedesktop.gstreamer.swing
+ org.freedesktop.gstreamer.swt
+ org.freedesktop.gstreamer.swt.overlay
org.gstreamer
org.gstreamer.controller
org.gstreamer.elements
@@ -966,6 +984,10 @@
ext/gstreamer-java-1.5.jar
release/modules/ext/gstreamer-java-1.5.jar
+
+ ext/gst1-java-core-0.9.3.jar
+ release/modules/ext/gst1-java-core-0.9.3.jar
+
ext/dom4j-1.6.1.jar
release/modules/ext/dom4j-1.6.1.jar
diff --git a/build-windows-installer.xml b/build-windows-installer.xml
index b82e28ca24..ebd60138db 100644
--- a/build-windows-installer.xml
+++ b/build-windows-installer.xml
@@ -123,7 +123,7 @@
-
+
diff --git a/build.xml b/build.xml
index 5616cb2c66..d5d8532d6e 100644
--- a/build.xml
+++ b/build.xml
@@ -103,7 +103,7 @@
-
+