mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-06 21:00:22 +00:00
Revert "Revert "4589 gst video panel""
This commit is contained in:
parent
3866041bc8
commit
e75b502da6
14
BUILDING.txt
14
BUILDING.txt
@ -1,4 +1,4 @@
|
|||||||
Last Updated: 30 October 2015
|
Last Updated: 1 February 2019
|
||||||
|
|
||||||
This file outlines what it takes to build Autopsy from source.
|
This file outlines what it takes to build Autopsy from source.
|
||||||
|
|
||||||
@ -55,11 +55,13 @@ from the TSK root directory to install the libraries and such in
|
|||||||
the needed places (i.e. '/usr/local').
|
the needed places (i.e. '/usr/local').
|
||||||
|
|
||||||
|
|
||||||
3) For 32-bit targets, get GStreamer Setup. GStreamer is used to view video files.
|
3) For Windows builds, GStreamer must be setup. GStreamer is used to view video
|
||||||
You can either download it and install it or manually by unzipping the
|
files. You can either download it and install it, or you can copy it from the
|
||||||
version that is included in the 'thirdparty/gstreamer' folder. You
|
'thirdparty/gstreamer' folder. Both 32-bit (x86) and 64-bit (x86_64) versions
|
||||||
will need the 'bin' and 'lib/gstreamer-0.10' folders to be in your
|
are included, so feel free to exclude what you want. You will need to unzip the
|
||||||
Windows PATH environment variable.
|
'lib/gstreamer-1.0/libgstlibav.zip' file into it's residing
|
||||||
|
folder. You will also need the 'bin' and 'lib/gstreamer-1.0' paths included in
|
||||||
|
your Windows PATH environment variable.
|
||||||
|
|
||||||
NOTE: This has not been fully tested in non-Windows environments
|
NOTE: This has not been fully tested in non-Windows environments
|
||||||
yet, so we don't have instructions for that yet.
|
yet, so we don't have instructions for that yet.
|
||||||
|
@ -76,6 +76,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.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.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.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.source=1.8
|
||||||
javac.compilerargs=-Xlint -Xlint:-serial
|
javac.compilerargs=-Xlint -Xlint:-serial
|
||||||
license.file=../LICENSE-2.0.txt
|
license.file=../LICENSE-2.0.txt
|
||||||
|
@ -9,7 +9,6 @@ FXVideoPanel.progressLabel.buffering=Buffering...
|
|||||||
FXVideoPanel.media.unsupportedFormat=Unsupported Format.
|
FXVideoPanel.media.unsupportedFormat=Unsupported Format.
|
||||||
GstVideoPanel.cannotProcFile.err=The media player cannot process this file.
|
GstVideoPanel.cannotProcFile.err=The media player cannot process this file.
|
||||||
GstVideoPanel.initGst.gstException.msg=Error initializing gstreamer for audio/video viewing and frame extraction capabilities. Video and audio viewing will be disabled.
|
GstVideoPanel.initGst.gstException.msg=Error initializing gstreamer for audio/video viewing and frame extraction capabilities. Video and audio viewing will be disabled.
|
||||||
GstVideoPanel.initGst.otherException.msg=Error initializing gstreamer for audio/video viewing frame extraction capabilities. Video and audio viewing will be disabled.
|
|
||||||
GstVideoPanel.setupVideo.infoLabel.text=Playback of deleted videos is not supported, use an external player.
|
GstVideoPanel.setupVideo.infoLabel.text=Playback of deleted videos is not supported, use an external player.
|
||||||
GstVideoPanel.exception.problemFile.msg=Cannot capture frames from this file ({0}).
|
GstVideoPanel.exception.problemFile.msg=Cannot capture frames from this file ({0}).
|
||||||
GstVideoPanel.exception.problemPlay.msg=Problem with video file; problem when attempting to play while obtaining duration.
|
GstVideoPanel.exception.problemPlay.msg=Problem with video file; problem when attempting to play while obtaining duration.
|
||||||
|
@ -9,7 +9,6 @@ FXVideoPanel.progressLabel.buffering=\u30d0\u30c3\u30d5\u30a1\u30ea\u30f3\u30b0\
|
|||||||
FXVideoPanel.media.unsupportedFormat=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u3059\u3002
|
FXVideoPanel.media.unsupportedFormat=\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u306a\u3044\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u3067\u3059\u3002
|
||||||
GstVideoPanel.cannotProcFile.err=\u30e1\u30c7\u30a4\u30a2\u30d7\u30ec\u30fc\u30e4\u30fc\u3067\u306f\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u51e6\u7406\u3067\u304d\u307e\u305b\u3093\u3002
|
GstVideoPanel.cannotProcFile.err=\u30e1\u30c7\u30a4\u30a2\u30d7\u30ec\u30fc\u30e4\u30fc\u3067\u306f\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u3092\u51e6\u7406\u3067\u304d\u307e\u305b\u3093\u3002
|
||||||
GstVideoPanel.initGst.gstException.msg=\u30aa\u30fc\u30c7\u30a3\u30aa\uff0f\u30d3\u30c7\u30aa\u306e\u518d\u751f\u304a\u3088\u3073\u30d5\u30ec\u30fc\u30e0\u306e\u62bd\u51fa\u306b\u4f7f\u7528\u3059\u308bGStreamer\u306e\u521d\u671f\u5316\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30d3\u30c7\u30aa\u304a\u3088\u3073\u30aa\u30fc\u30c7\u30a3\u30aa\u518d\u751f\u304c\u7121\u52b9\u5316\u3055\u308c\u307e\u3059\u3002
|
GstVideoPanel.initGst.gstException.msg=\u30aa\u30fc\u30c7\u30a3\u30aa\uff0f\u30d3\u30c7\u30aa\u306e\u518d\u751f\u304a\u3088\u3073\u30d5\u30ec\u30fc\u30e0\u306e\u62bd\u51fa\u306b\u4f7f\u7528\u3059\u308bGStreamer\u306e\u521d\u671f\u5316\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30d3\u30c7\u30aa\u304a\u3088\u3073\u30aa\u30fc\u30c7\u30a3\u30aa\u518d\u751f\u304c\u7121\u52b9\u5316\u3055\u308c\u307e\u3059\u3002
|
||||||
GstVideoPanel.initGst.otherException.msg=\u30aa\u30fc\u30c7\u30a3\u30aa\uff0f\u30d3\u30c7\u30aa\u306e\u518d\u751f\u304a\u3088\u3073\u30d5\u30ec\u30fc\u30e0\u306e\u62bd\u51fa\u306b\u4f7f\u7528\u3059\u308bGStreamer\u306e\u521d\u671f\u5316\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u30d3\u30c7\u30aa\u304a\u3088\u3073\u30aa\u30fc\u30c7\u30a3\u30aa\u518d\u751f\u304c\u7121\u52b9\u5316\u3055\u308c\u307e\u3059\u3002
|
|
||||||
GstVideoPanel.setupVideo.infoLabel.text=\u524a\u9664\u3055\u308c\u305f\u30d3\u30c7\u30aa\u306e\u518d\u751f\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u5916\u90e8\u30d7\u30ec\u30fc\u30e4\u30fc\u3092\u4f7f\u7528\u3057\u3066\u4e0b\u3055\u3044\u3002
|
GstVideoPanel.setupVideo.infoLabel.text=\u524a\u9664\u3055\u308c\u305f\u30d3\u30c7\u30aa\u306e\u518d\u751f\u306f\u30b5\u30dd\u30fc\u30c8\u3055\u308c\u3066\u3044\u307e\u305b\u3093\u3002\u5916\u90e8\u30d7\u30ec\u30fc\u30e4\u30fc\u3092\u4f7f\u7528\u3057\u3066\u4e0b\u3055\u3044\u3002
|
||||||
GstVideoPanel.exception.problemFile.msg=\u30d5\u30a1\u30a4\u30eb({0})\u304b\u3089\u30d5\u30ec\u30fc\u30e0\u3092\u62bd\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
|
GstVideoPanel.exception.problemFile.msg=\u30d5\u30a1\u30a4\u30eb({0})\u304b\u3089\u30d5\u30ec\u30fc\u30e0\u3092\u62bd\u51fa\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002
|
||||||
GstVideoPanel.exception.problemPlay.msg=\u30d3\u30c7\u30aa\u30d5\u30a1\u30a4\u30eb\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u9577\u3055\u3092\u78ba\u8a8d\u4e2d\u306b\u518d\u751f\u3092\u3057\u3088\u3046\u3068\u3057\u305f\u969b\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
|
GstVideoPanel.exception.problemPlay.msg=\u30d3\u30c7\u30aa\u30d5\u30a1\u30a4\u30eb\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u9577\u3055\u3092\u78ba\u8a8d\u4e2d\u306b\u518d\u751f\u3092\u3057\u3088\u3046\u3068\u3057\u305f\u969b\u306b\u554f\u984c\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
|
|
||||||
<Form version="1.4" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
|
||||||
<Properties>
|
|
||||||
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
|
|
||||||
<Color blue="0" green="0" red="0" type="rgb"/>
|
|
||||||
</Property>
|
|
||||||
</Properties>
|
|
||||||
<AuxValues>
|
|
||||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
|
||||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
|
||||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
|
||||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
|
||||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
|
||||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
|
||||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
|
||||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
|
||||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
|
||||||
</AuxValues>
|
|
||||||
|
|
||||||
<Layout>
|
|
||||||
<DimensionLayout dim="0">
|
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
|
||||||
<Component id="jFXPanel" max="32767" attributes="0"/>
|
|
||||||
</Group>
|
|
||||||
</DimensionLayout>
|
|
||||||
<DimensionLayout dim="1">
|
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
|
||||||
<Component id="jFXPanel" max="32767" attributes="0"/>
|
|
||||||
</Group>
|
|
||||||
</DimensionLayout>
|
|
||||||
</Layout>
|
|
||||||
<SubComponents>
|
|
||||||
<Container class="javafx.embed.swing.JFXPanel" name="jFXPanel">
|
|
||||||
|
|
||||||
<Layout>
|
|
||||||
<DimensionLayout dim="0">
|
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
|
||||||
<EmptySpace min="0" pref="400" max="32767" attributes="0"/>
|
|
||||||
</Group>
|
|
||||||
</DimensionLayout>
|
|
||||||
<DimensionLayout dim="1">
|
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
|
||||||
<EmptySpace min="0" pref="300" max="32767" attributes="0"/>
|
|
||||||
</Group>
|
|
||||||
</DimensionLayout>
|
|
||||||
</Layout>
|
|
||||||
</Container>
|
|
||||||
</SubComponents>
|
|
||||||
</Form>
|
|
@ -1,669 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2013-2018 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> 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 com.google.common.io.Files;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CancellationException;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import javafx.application.Platform;
|
|
||||||
import javafx.beans.Observable;
|
|
||||||
import javafx.concurrent.Task;
|
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.geometry.Insets;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.Slider;
|
|
||||||
import javafx.scene.control.Tooltip;
|
|
||||||
import javafx.scene.layout.BorderPane;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.Priority;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.media.Media;
|
|
||||||
import javafx.scene.media.MediaException;
|
|
||||||
import javafx.scene.media.MediaPlayer;
|
|
||||||
import javafx.scene.media.MediaPlayer.Status;
|
|
||||||
import static javafx.scene.media.MediaPlayer.Status.PAUSED;
|
|
||||||
import static javafx.scene.media.MediaPlayer.Status.PLAYING;
|
|
||||||
import static javafx.scene.media.MediaPlayer.Status.READY;
|
|
||||||
import static javafx.scene.media.MediaPlayer.Status.STOPPED;
|
|
||||||
import javafx.scene.media.MediaView;
|
|
||||||
import javafx.util.Duration;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import org.netbeans.api.progress.ProgressHandle;
|
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
|
||||||
import org.openide.util.lookup.ServiceProviders;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
|
||||||
import org.sleuthkit.autopsy.core.Installer;
|
|
||||||
import org.sleuthkit.autopsy.corecomponents.FrameCapture;
|
|
||||||
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.datamodel.AbstractFile;
|
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
|
||||||
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 {
|
|
||||||
|
|
||||||
// Refer to https://docs.oracle.com/javafx/2/api/javafx/scene/media/package-summary.html
|
|
||||||
// for Javafx supported formats
|
|
||||||
private static final String[] EXTENSIONS = new String[]{".m4v", ".fxm", ".flv", ".m3u8", ".mp4", ".aif", ".aiff", ".mp3", "m4a", ".wav"}; //NON-NLS
|
|
||||||
private static final List<String> MIMETYPES = Arrays.asList("audio/x-aiff", "video/x-javafx", "video/x-flv", "application/vnd.apple.mpegurl", " audio/mpegurl", "audio/mpeg", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
|
|
||||||
private static final Logger logger = Logger.getLogger(FXVideoPanel.class.getName());
|
|
||||||
|
|
||||||
private boolean fxInited = false;
|
|
||||||
|
|
||||||
private MediaPane mediaPane;
|
|
||||||
|
|
||||||
private AbstractFile currentFile;
|
|
||||||
|
|
||||||
public FXVideoPanel() {
|
|
||||||
fxInited = Installer.isJavaFxInited();
|
|
||||||
initComponents();
|
|
||||||
if (fxInited) {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
|
|
||||||
mediaPane = new MediaPane();
|
|
||||||
Scene fxScene = new Scene(mediaPane);
|
|
||||||
jFXPanel.setScene(fxScene);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated
|
|
||||||
public JPanel getVideoPanel() {
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void setupVideo(final AbstractFile file, final Dimension dims) {
|
|
||||||
if (file.equals(currentFile)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!Case.isCaseOpen()) {
|
|
||||||
//handle in-between condition when case is being closed
|
|
||||||
//and an image was previously selected
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
reset();
|
|
||||||
currentFile = file;
|
|
||||||
final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
|
|
||||||
if (deleted) {
|
|
||||||
mediaPane.setInfoLabelText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.mediaPane.infoLabel"));
|
|
||||||
removeAll();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mediaPane.setFit(dims);
|
|
||||||
|
|
||||||
String path = "";
|
|
||||||
try {
|
|
||||||
path = file.getUniquePath();
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Cannot get unique path of video file", ex); //NON-NLS
|
|
||||||
}
|
|
||||||
mediaPane.setInfoLabelText(path);
|
|
||||||
mediaPane.setInfoLabelToolTipText(path);
|
|
||||||
|
|
||||||
final File tempFile;
|
|
||||||
try {
|
|
||||||
tempFile = VideoUtils.getVideoFileInTempDir(currentFile);
|
|
||||||
} catch (NoCurrentCaseException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
new Thread(mediaPane.new ExtractMedia(currentFile, tempFile)).start();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void reset() {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
if (mediaPane != null) {
|
|
||||||
mediaPane.reset();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
currentFile = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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
|
|
||||||
* regenerated by the Form Editor.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
|
||||||
private void initComponents() {
|
|
||||||
|
|
||||||
jFXPanel = new javafx.embed.swing.JFXPanel();
|
|
||||||
|
|
||||||
setBackground(new java.awt.Color(0, 0, 0));
|
|
||||||
|
|
||||||
javax.swing.GroupLayout jFXPanelLayout = new javax.swing.GroupLayout(jFXPanel);
|
|
||||||
jFXPanel.setLayout(jFXPanelLayout);
|
|
||||||
jFXPanelLayout.setHorizontalGroup(
|
|
||||||
jFXPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGap(0, 400, Short.MAX_VALUE)
|
|
||||||
);
|
|
||||||
jFXPanelLayout.setVerticalGroup(
|
|
||||||
jFXPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGap(0, 300, Short.MAX_VALUE)
|
|
||||||
);
|
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
|
||||||
this.setLayout(layout);
|
|
||||||
layout.setHorizontalGroup(
|
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addComponent(jFXPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
|
||||||
);
|
|
||||||
layout.setVerticalGroup(
|
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addComponent(jFXPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
|
||||||
);
|
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
|
||||||
private javafx.embed.swing.JFXPanel jFXPanel;
|
|
||||||
// End of variables declaration//GEN-END:variables
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInited() {
|
|
||||||
return fxInited;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MediaPane extends BorderPane {
|
|
||||||
|
|
||||||
private MediaPlayer mediaPlayer;
|
|
||||||
|
|
||||||
private final MediaView mediaView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Duration of the media. *
|
|
||||||
*/
|
|
||||||
private Duration duration;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The container for the media controls. *
|
|
||||||
*/
|
|
||||||
private final HBox mediaTools;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The container for the media video output. *
|
|
||||||
*/
|
|
||||||
private final HBox mediaViewPane;
|
|
||||||
|
|
||||||
private final VBox controlPanel;
|
|
||||||
|
|
||||||
private final Slider progressSlider;
|
|
||||||
|
|
||||||
private final Button pauseButton;
|
|
||||||
|
|
||||||
private final Button stopButton;
|
|
||||||
|
|
||||||
private final Label progressLabel;
|
|
||||||
|
|
||||||
private final Label infoLabel;
|
|
||||||
|
|
||||||
private int totalHours;
|
|
||||||
|
|
||||||
private int totalMinutes;
|
|
||||||
|
|
||||||
private int totalSeconds;
|
|
||||||
|
|
||||||
private final String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; //NON-NLS
|
|
||||||
|
|
||||||
private static final String PLAY_TEXT = "►";
|
|
||||||
|
|
||||||
private static final String PAUSE_TEXT = "||";
|
|
||||||
|
|
||||||
private static final String STOP_TEXT = "X"; //NON-NLS
|
|
||||||
|
|
||||||
public MediaPane() {
|
|
||||||
// Video Display
|
|
||||||
mediaViewPane = new HBox();
|
|
||||||
mediaViewPane.setStyle("-fx-background-color: black"); //NON-NLS
|
|
||||||
mediaViewPane.setAlignment(Pos.CENTER);
|
|
||||||
mediaView = new MediaView();
|
|
||||||
mediaViewPane.getChildren().add(mediaView);
|
|
||||||
setCenter(mediaViewPane);
|
|
||||||
|
|
||||||
// Media Controls
|
|
||||||
controlPanel = new VBox();
|
|
||||||
mediaTools = new HBox();
|
|
||||||
mediaTools.setAlignment(Pos.CENTER);
|
|
||||||
mediaTools.setPadding(new Insets(5, 10, 5, 10));
|
|
||||||
|
|
||||||
pauseButton = new Button(PLAY_TEXT);
|
|
||||||
stopButton = new Button(STOP_TEXT);
|
|
||||||
mediaTools.getChildren().add(pauseButton);
|
|
||||||
mediaTools.getChildren().add(new Label(" "));
|
|
||||||
mediaTools.getChildren().add(stopButton);
|
|
||||||
mediaTools.getChildren().add(new Label(" "));
|
|
||||||
progressSlider = new Slider();
|
|
||||||
HBox.setHgrow(progressSlider, Priority.ALWAYS);
|
|
||||||
progressSlider.setMinWidth(50);
|
|
||||||
progressSlider.setMaxWidth(Double.MAX_VALUE);
|
|
||||||
mediaTools.getChildren().add(progressSlider);
|
|
||||||
progressLabel = new Label();
|
|
||||||
progressLabel.setPrefWidth(135);
|
|
||||||
progressLabel.setMinWidth(135);
|
|
||||||
mediaTools.getChildren().add(progressLabel);
|
|
||||||
|
|
||||||
controlPanel.getChildren().add(mediaTools);
|
|
||||||
controlPanel.setStyle("-fx-background-color: white"); //NON-NLS
|
|
||||||
infoLabel = new Label("");
|
|
||||||
controlPanel.getChildren().add(infoLabel);
|
|
||||||
setBottom(controlPanel);
|
|
||||||
setProgressActionListeners();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset this MediaPane.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void reset() {
|
|
||||||
if (mediaPlayer != null) {
|
|
||||||
setInfoLabelText("");
|
|
||||||
if (mediaPlayer.getStatus() == Status.PLAYING) {
|
|
||||||
mediaPlayer.stop();
|
|
||||||
}
|
|
||||||
mediaPlayer = null;
|
|
||||||
mediaView.setMediaPlayer(null);
|
|
||||||
}
|
|
||||||
resetProgress();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the Information Label of this MediaPane.
|
|
||||||
*
|
|
||||||
* @param text
|
|
||||||
*/
|
|
||||||
public void setInfoLabelText(final String text) {
|
|
||||||
logger.log(Level.INFO, "Setting Info Label Text: {0}", text); //NON-NLS
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
infoLabel.setText(text);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the size of the MediaPane and it's components.
|
|
||||||
*
|
|
||||||
* @param dims the current dimensions of the DataContentViewer
|
|
||||||
*/
|
|
||||||
public void setFit(final Dimension dims) {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
setPrefSize(dims.getWidth(), dims.getHeight());
|
|
||||||
// Set the Video output to fit the size allocated for it. give an
|
|
||||||
// extra few px to ensure the info label will be shown
|
|
||||||
mediaView.setFitHeight(dims.getHeight() - controlPanel.getHeight());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the action listeners for the pause button and progress slider.
|
|
||||||
*/
|
|
||||||
private void setProgressActionListeners() {
|
|
||||||
pauseButton.setOnAction(new EventHandler<ActionEvent>() {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent e) {
|
|
||||||
if (mediaPlayer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status status = mediaPlayer.getStatus();
|
|
||||||
|
|
||||||
switch (status) {
|
|
||||||
// If playing, pause
|
|
||||||
case PLAYING:
|
|
||||||
mediaPlayer.pause();
|
|
||||||
break;
|
|
||||||
// If ready, paused or stopped, continue playing
|
|
||||||
case READY:
|
|
||||||
case PAUSED:
|
|
||||||
case STOPPED:
|
|
||||||
mediaPlayer.play();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
logger.log(Level.INFO, "MediaPlayer in unexpected state: {0}", status.toString()); //NON-NLS
|
|
||||||
// If the MediaPlayer is in an unexpected state, stop playback.
|
|
||||||
mediaPlayer.stop();
|
|
||||||
setInfoLabelText(NbBundle.getMessage(this.getClass(),
|
|
||||||
"FXVideoPanel.pauseButton.infoLabel.playbackErr"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
stopButton.setOnAction((ActionEvent e) -> {
|
|
||||||
if (mediaPlayer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mediaPlayer.stop();
|
|
||||||
});
|
|
||||||
|
|
||||||
progressSlider.valueProperty().addListener((Observable o) -> {
|
|
||||||
if (mediaPlayer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progressSlider.isValueChanging()) {
|
|
||||||
mediaPlayer.seek(duration.multiply(progressSlider.getValue() / 100.0));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the progress label and slider to zero.
|
|
||||||
*/
|
|
||||||
private void resetProgress() {
|
|
||||||
totalHours = 0;
|
|
||||||
totalMinutes = 0;
|
|
||||||
totalSeconds = 0;
|
|
||||||
progressSlider.setValue(0.0);
|
|
||||||
updateTime(Duration.ZERO);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a MediaPlayer from the given Media URI.
|
|
||||||
*
|
|
||||||
* Also adds the necessary listeners to MediaPlayer events.
|
|
||||||
*
|
|
||||||
* @param mediaUri the location of the media.
|
|
||||||
*
|
|
||||||
* @return a MediaPlayer
|
|
||||||
*/
|
|
||||||
private MediaPlayer createMediaPlayer(String mediaUri) {
|
|
||||||
Media media = new Media(mediaUri);
|
|
||||||
|
|
||||||
MediaPlayer player = new MediaPlayer(media);
|
|
||||||
player.setOnReady(new ReadyListener());
|
|
||||||
final Runnable pauseListener = () -> {
|
|
||||||
pauseButton.setText(PLAY_TEXT);
|
|
||||||
};
|
|
||||||
player.setOnPaused(pauseListener);
|
|
||||||
player.setOnStopped(pauseListener);
|
|
||||||
player.setOnPlaying(() -> {
|
|
||||||
pauseButton.setText(PAUSE_TEXT);
|
|
||||||
});
|
|
||||||
player.setOnEndOfMedia(new EndOfMediaListener());
|
|
||||||
|
|
||||||
player.currentTimeProperty().addListener((observable, oldTime, newTime) -> {
|
|
||||||
updateSlider(newTime);
|
|
||||||
updateTime(newTime);
|
|
||||||
});
|
|
||||||
|
|
||||||
return player;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the progress slider and label with the current time of the
|
|
||||||
* media.
|
|
||||||
*/
|
|
||||||
private void updateProgress() {
|
|
||||||
if (mediaPlayer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Duration currentTime = mediaPlayer.getCurrentTime();
|
|
||||||
updateSlider(currentTime);
|
|
||||||
updateTime(currentTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the slider with the current time.
|
|
||||||
*
|
|
||||||
* @param currentTime
|
|
||||||
*/
|
|
||||||
private void updateSlider(Duration currentTime) {
|
|
||||||
if (progressSlider != null) {
|
|
||||||
progressSlider.setDisable(currentTime.isUnknown());
|
|
||||||
if (!progressSlider.isDisabled() && duration.greaterThan(Duration.ZERO)
|
|
||||||
&& !progressSlider.isValueChanging()) {
|
|
||||||
progressSlider.setValue(currentTime.divide(duration.toMillis()).toMillis() * 100.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the progress label with the current time.
|
|
||||||
*
|
|
||||||
* @param currentTime
|
|
||||||
*/
|
|
||||||
private void updateTime(Duration currentTime) {
|
|
||||||
long millisElapsed = (long) currentTime.toMillis();
|
|
||||||
|
|
||||||
long elapsedHours, elapsedMinutes, elapsedSeconds;
|
|
||||||
// pick out the elapsed hours, minutes, seconds
|
|
||||||
long secondsElapsed = millisElapsed / 1000;
|
|
||||||
elapsedHours = (int) secondsElapsed / 3600;
|
|
||||||
secondsElapsed -= elapsedHours * 3600;
|
|
||||||
elapsedMinutes = (int) secondsElapsed / 60;
|
|
||||||
secondsElapsed -= elapsedMinutes * 60;
|
|
||||||
elapsedSeconds = (int) secondsElapsed;
|
|
||||||
|
|
||||||
String durationStr = String.format(durationFormat,
|
|
||||||
elapsedHours, elapsedMinutes, elapsedSeconds,
|
|
||||||
totalHours, totalMinutes, totalSeconds);
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
progressLabel.setText(durationStr);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setInfoLabelToolTipText(final String text) {
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
infoLabel.setTooltip(new Tooltip(text));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responds to MediaPlayer onReady events.
|
|
||||||
*
|
|
||||||
* Updates the progress label with the duration of the media.
|
|
||||||
*/
|
|
||||||
private class ReadyListener implements Runnable {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (mediaPlayer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
duration = mediaPlayer.getMedia().getDuration();
|
|
||||||
long durationInMillis = (long) mediaPlayer.getMedia().getDuration().toMillis();
|
|
||||||
|
|
||||||
// pick out the total hours, minutes, seconds
|
|
||||||
long durationSeconds = (int) durationInMillis / 1000;
|
|
||||||
totalHours = (int) durationSeconds / 3600;
|
|
||||||
durationSeconds -= totalHours * 3600;
|
|
||||||
totalMinutes = (int) durationSeconds / 60;
|
|
||||||
durationSeconds -= totalMinutes * 60;
|
|
||||||
totalSeconds = (int) durationSeconds;
|
|
||||||
updateProgress();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Responds to MediaPlayer onEndOfMediaEvents.
|
|
||||||
*
|
|
||||||
* Prepares the media to be replayed.
|
|
||||||
*/
|
|
||||||
private class EndOfMediaListener implements Runnable {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (mediaPlayer == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Duration beginning = mediaPlayer.getStartTime();
|
|
||||||
mediaPlayer.stop();
|
|
||||||
mediaPlayer.pause();
|
|
||||||
pauseButton.setText(PLAY_TEXT);
|
|
||||||
updateSlider(beginning);
|
|
||||||
updateTime(beginning);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thread that extracts Media from a Sleuthkit file representation to a
|
|
||||||
* Java file representation that the Media Player can take as input.
|
|
||||||
*/
|
|
||||||
private class ExtractMedia extends Task<Long> {
|
|
||||||
|
|
||||||
private ProgressHandle progress;
|
|
||||||
|
|
||||||
private final AbstractFile sourceFile;
|
|
||||||
|
|
||||||
private final java.io.File tempFile;
|
|
||||||
|
|
||||||
ExtractMedia(AbstractFile sFile, java.io.File jFile) {
|
|
||||||
this.sourceFile = sFile;
|
|
||||||
this.tempFile = jFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the URI of the media file.
|
|
||||||
*
|
|
||||||
* @return the URI of the media file.
|
|
||||||
*/
|
|
||||||
public String getMediaUri() {
|
|
||||||
return Paths.get(tempFile.getAbsolutePath()).toUri().toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Long call() throws Exception {
|
|
||||||
if (tempFile.exists() == false || tempFile.length() < sourceFile.getSize()) {
|
|
||||||
progress = ProgressHandle.createHandle(
|
|
||||||
NbBundle.getMessage(this.getClass(),
|
|
||||||
"FXVideoPanel.progress.bufferingFile",
|
|
||||||
sourceFile.getName()
|
|
||||||
),
|
|
||||||
() -> ExtractMedia.this.cancel(true));
|
|
||||||
|
|
||||||
Platform.runLater(() -> {
|
|
||||||
progressLabel.setText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.progressLabel.buffering"));
|
|
||||||
});
|
|
||||||
|
|
||||||
progress.start(100);
|
|
||||||
try {
|
|
||||||
Files.createParentDirs(tempFile);
|
|
||||||
return ContentUtils.writeToFile(sourceFile, tempFile, progress, this, true);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.WARNING, "Error buffering file", ex); //NON-NLS
|
|
||||||
return 0L;
|
|
||||||
} finally {
|
|
||||||
logger.log(Level.INFO, "Done buffering: {0}", tempFile.getName()); //NON-NLS
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0L;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void failed() {
|
|
||||||
super.failed();
|
|
||||||
onDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void succeeded() {
|
|
||||||
super.succeeded();
|
|
||||||
onDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void cancelled() {
|
|
||||||
super.cancelled();
|
|
||||||
onDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onDone() {
|
|
||||||
progressLabel.setText("");
|
|
||||||
try {
|
|
||||||
super.get(); //block and get all exceptions thrown while doInBackground()
|
|
||||||
} catch (CancellationException ex) {
|
|
||||||
logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS
|
|
||||||
progressLabel.setText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.progress.bufferingCancelled"));
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
logger.log(Level.INFO, "Media buffering was interrupted."); //NON-NLS
|
|
||||||
progressLabel.setText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.progress.bufferingInterrupted"));
|
|
||||||
} catch (Exception ex) {
|
|
||||||
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); //NON-NLS
|
|
||||||
progressLabel.setText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.progress.errorWritingVideoToDisk"));
|
|
||||||
} finally {
|
|
||||||
if (null != progress) {
|
|
||||||
progress.finish();
|
|
||||||
}
|
|
||||||
if (!this.isCancelled()) {
|
|
||||||
logger.log(Level.INFO, "ExtractMedia is done: {0}", tempFile.getName()); //NON-NLS
|
|
||||||
try {
|
|
||||||
mediaPane.mediaPlayer = mediaPane.createMediaPlayer(getMediaUri());
|
|
||||||
mediaView.setMediaPlayer(mediaPane.mediaPlayer);
|
|
||||||
} catch (MediaException ex) {
|
|
||||||
progressLabel.setText("");
|
|
||||||
mediaPane.setInfoLabelText(NbBundle.getMessage(this.getClass(), "FXVideoPanel.media.unsupportedFormat"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @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<VideoFrame> captureFrames(java.io.File file, int numFrames) throws Exception {
|
|
||||||
//What is/was the point of this method /interface.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getExtensions() {
|
|
||||||
return EXTENSIONS.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getMimeTypes() {
|
|
||||||
return MIMETYPES;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,797 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2013-2018 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> 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 com.google.common.io.Files;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.awt.Image;
|
|
||||||
import java.awt.image.BufferedImage;
|
|
||||||
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.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;
|
|
||||||
import javax.swing.JLabel;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
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.netbeans.api.progress.ProgressHandle;
|
|
||||||
import org.openide.util.NbBundle;
|
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
|
||||||
import org.openide.util.lookup.ServiceProviders;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
|
||||||
import org.sleuthkit.autopsy.corecomponents.FrameCapture;
|
|
||||||
import org.sleuthkit.autopsy.corecomponents.VideoFrame;
|
|
||||||
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.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 {
|
|
||||||
|
|
||||||
private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS
|
|
||||||
private static final List<String> 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 Logger logger = Logger.getLogger(GstVideoPanel.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");
|
|
||||||
//playback
|
|
||||||
private long durationMillis = 0;
|
|
||||||
private VideoProgressWorker videoProgressWorker;
|
|
||||||
private int totalHours, totalMinutes, totalSeconds;
|
|
||||||
private volatile PlayBin2 gstPlaybin2;
|
|
||||||
private VideoComponent gstVideoComponent;
|
|
||||||
private boolean autoTracking = false; // true if the slider is moving automatically
|
|
||||||
private final Object playbinLock = new Object(); // lock for synchronization of gstPlaybin2 player
|
|
||||||
private AbstractFile currentFile;
|
|
||||||
private final Set<String> badVideoFiles = Collections.synchronizedSet(new HashSet<>());
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new form MediaViewVideoPanel
|
|
||||||
*/
|
|
||||||
public GstVideoPanel() {
|
|
||||||
initComponents();
|
|
||||||
customizeComponents();
|
|
||||||
}
|
|
||||||
|
|
||||||
public JButton getPauseButton() {
|
|
||||||
return pauseButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JLabel getProgressLabel() {
|
|
||||||
return progressLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JSlider getProgressSlider() {
|
|
||||||
return progressSlider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public JPanel getVideoPanel() {
|
|
||||||
return videoPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VideoComponent getVideoComponent() {
|
|
||||||
return gstVideoComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isInited() {
|
|
||||||
return gstInited;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void customizeComponents() {
|
|
||||||
if (!initGst()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
progressSlider.setEnabled(false); // disable slider; enable after user plays vid
|
|
||||||
progressSlider.setValue(0);
|
|
||||||
|
|
||||||
progressSlider.addChangeListener((ChangeEvent e) -> {
|
|
||||||
/**
|
|
||||||
* Should always try to synchronize any call to
|
|
||||||
* progressSlider.setValue() to avoid a different thread changing
|
|
||||||
* playbin while stateChanged() is processing
|
|
||||||
*/
|
|
||||||
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
|
|
||||||
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
|
|
||||||
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
gstPlaybin2.setState(orig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean initGst() {
|
|
||||||
try {
|
|
||||||
logger.log(Level.INFO, "Initializing gstreamer for video/audio viewing"); //NON-NLS
|
|
||||||
Gst.init();
|
|
||||||
gstInited = true;
|
|
||||||
} catch (GstException e) {
|
|
||||||
gstInited = false;
|
|
||||||
logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and frame extraction capabilities", e); //NON-NLS
|
|
||||||
MessageNotifyUtil.Notify.error(
|
|
||||||
NbBundle.getMessage(this.getClass(), "GstVideoPanel.initGst.gstException.msg"),
|
|
||||||
e.getMessage());
|
|
||||||
return false;
|
|
||||||
} catch (UnsatisfiedLinkError | NoClassDefFoundError | Exception e) {
|
|
||||||
gstInited = false;
|
|
||||||
logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and extraction capabilities", e); //NON-NLS
|
|
||||||
MessageNotifyUtil.Notify.error(
|
|
||||||
NbBundle.getMessage(this.getClass(), "GstVideoPanel.initGst.otherException.msg"),
|
|
||||||
e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@NbBundle.Messages ({"GstVideoPanel.noOpenCase.errMsg=No open case available."})
|
|
||||||
void setupVideo(final AbstractFile file, final Dimension dims) {
|
|
||||||
reset();
|
|
||||||
infoLabel.setText("");
|
|
||||||
currentFile = file;
|
|
||||||
final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
|
|
||||||
if (deleted) {
|
|
||||||
infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.setupVideo.infoLabel.text"));
|
|
||||||
videoPanel.removeAll();
|
|
||||||
pauseButton.setEnabled(false);
|
|
||||||
progressSlider.setEnabled(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
java.io.File ioFile;
|
|
||||||
try {
|
|
||||||
ioFile = VideoUtils.getVideoFileInTempDir(file);
|
|
||||||
} catch (NoCurrentCaseException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
|
||||||
infoLabel.setText(Bundle.GstVideoPanel_noOpenCase_errMsg());
|
|
||||||
pauseButton.setEnabled(false);
|
|
||||||
progressSlider.setEnabled(false);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String path = "";
|
|
||||||
try {
|
|
||||||
path = file.getUniquePath();
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Cannot get unique path of video file"); //NON-NLS
|
|
||||||
}
|
|
||||||
infoLabel.setText(path);
|
|
||||||
infoLabel.setToolTipText(path);
|
|
||||||
pauseButton.setEnabled(true);
|
|
||||||
progressSlider.setEnabled(true);
|
|
||||||
|
|
||||||
|
|
||||||
gstVideoComponent = new VideoComponent();
|
|
||||||
synchronized (playbinLock) {
|
|
||||||
if (gstPlaybin2 != null) {
|
|
||||||
gstPlaybin2.dispose();
|
|
||||||
}
|
|
||||||
gstPlaybin2 = new PlayBin2("VideoPlayer"); //NON-NLS
|
|
||||||
gstPlaybin2.setVideoSink(gstVideoComponent.getElement());
|
|
||||||
|
|
||||||
videoPanel.removeAll();
|
|
||||||
|
|
||||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
|
||||||
videoPanel.add(gstVideoComponent);
|
|
||||||
|
|
||||||
videoPanel.setVisible(true);
|
|
||||||
|
|
||||||
gstPlaybin2.setInputFile(ioFile);
|
|
||||||
|
|
||||||
if (gstPlaybin2.setState(State.READY) == StateChangeReturn.FAILURE) {
|
|
||||||
logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.READY) failed."); //NON-NLS
|
|
||||||
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void reset() {
|
|
||||||
|
|
||||||
// reset the progress label text on the event dispatch thread
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
progressLabel.setText("");
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!isInited()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
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
|
|
||||||
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (gstPlaybin2.getState().equals(State.NULL)) {
|
|
||||||
gstPlaybin2.dispose();
|
|
||||||
}
|
|
||||||
gstPlaybin2 = null;
|
|
||||||
}
|
|
||||||
gstVideoComponent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// get rid of any existing videoProgressWorker thread
|
|
||||||
if (videoProgressWorker != null) {
|
|
||||||
videoProgressWorker.cancel(true);
|
|
||||||
videoProgressWorker = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
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<VideoFrame> captureFrames(java.io.File file, int numFrames) throws Exception {
|
|
||||||
|
|
||||||
List<VideoFrame> 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
|
|
||||||
* regenerated by the Form Editor.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">
|
|
||||||
private void initComponents() {
|
|
||||||
|
|
||||||
videoPanel = new javax.swing.JPanel();
|
|
||||||
controlPanel = new javax.swing.JPanel();
|
|
||||||
pauseButton = new javax.swing.JButton();
|
|
||||||
progressSlider = new javax.swing.JSlider();
|
|
||||||
progressLabel = new javax.swing.JLabel();
|
|
||||||
infoLabel = new javax.swing.JLabel();
|
|
||||||
|
|
||||||
javax.swing.GroupLayout videoPanelLayout = new javax.swing.GroupLayout(videoPanel);
|
|
||||||
videoPanel.setLayout(videoPanelLayout);
|
|
||||||
videoPanelLayout.setHorizontalGroup(
|
|
||||||
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGap(0, 0, Short.MAX_VALUE)
|
|
||||||
);
|
|
||||||
videoPanelLayout.setVerticalGroup(
|
|
||||||
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGap(0, 231, Short.MAX_VALUE)
|
|
||||||
);
|
|
||||||
|
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(pauseButton, org.openide.util.NbBundle.getMessage(GstVideoPanel.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(infoLabel, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.infoLabel.text")); // NOI18N
|
|
||||||
|
|
||||||
javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel);
|
|
||||||
controlPanel.setLayout(controlPanelLayout);
|
|
||||||
controlPanelLayout.setHorizontalGroup(
|
|
||||||
controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGroup(controlPanelLayout.createSequentialGroup()
|
|
||||||
.addContainerGap()
|
|
||||||
.addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGroup(controlPanelLayout.createSequentialGroup()
|
|
||||||
.addGap(6, 6, 6)
|
|
||||||
.addComponent(infoLabel)
|
|
||||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
|
||||||
.addGroup(controlPanelLayout.createSequentialGroup()
|
|
||||||
.addComponent(pauseButton)
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
|
||||||
.addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 265, Short.MAX_VALUE)
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
|
||||||
.addComponent(progressLabel)
|
|
||||||
.addContainerGap())))
|
|
||||||
);
|
|
||||||
controlPanelLayout.setVerticalGroup(
|
|
||||||
controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGroup(controlPanelLayout.createSequentialGroup()
|
|
||||||
.addContainerGap()
|
|
||||||
.addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addComponent(progressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
|
||||||
.addComponent(pauseButton)
|
|
||||||
.addComponent(progressLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE))
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
|
||||||
.addComponent(infoLabel)
|
|
||||||
.addContainerGap())
|
|
||||||
);
|
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
|
||||||
this.setLayout(layout);
|
|
||||||
layout.setHorizontalGroup(
|
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
|
||||||
.addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
|
||||||
);
|
|
||||||
layout.setVerticalGroup(
|
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGroup(layout.createSequentialGroup()
|
|
||||||
.addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
|
||||||
.addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
|
||||||
);
|
|
||||||
}// </editor-fold>
|
|
||||||
|
|
||||||
private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed
|
|
||||||
synchronized (playbinLock) {
|
|
||||||
State state = gstPlaybin2.getState();
|
|
||||||
if (state.equals(State.PLAYING)) {
|
|
||||||
if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
|
|
||||||
logger.log(Level.WARNING, "Attempt to call PlayBin2.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
|
|
||||||
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
|
|
||||||
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
|
|
||||||
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} else if (state.equals(State.READY)) {
|
|
||||||
final File tempVideoFile;
|
|
||||||
try {
|
|
||||||
tempVideoFile = VideoUtils.getVideoFileInTempDir(currentFile);
|
|
||||||
} catch (NoCurrentCaseException ex) {
|
|
||||||
logger.log(Level.WARNING, "Exception while getting open case."); //NON-NLS
|
|
||||||
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
new ExtractMedia(currentFile, tempVideoFile).execute();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}//GEN-LAST:event_pauseButtonActionPerformed
|
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
|
||||||
private javax.swing.JPanel controlPanel;
|
|
||||||
private javax.swing.JLabel infoLabel;
|
|
||||||
private javax.swing.JButton pauseButton;
|
|
||||||
private javax.swing.JLabel progressLabel;
|
|
||||||
private javax.swing.JSlider progressSlider;
|
|
||||||
private javax.swing.JPanel videoPanel;
|
|
||||||
// End of variables declaration//GEN-END:variables
|
|
||||||
|
|
||||||
private class VideoProgressWorker extends SwingWorker<Object, Object> {
|
|
||||||
|
|
||||||
private final String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; //NON-NLS
|
|
||||||
private long millisElapsed = 0;
|
|
||||||
private final long INTER_FRAME_PERIOD_MS = 20;
|
|
||||||
private final long END_TIME_MARGIN_MS = 50;
|
|
||||||
|
|
||||||
private boolean isPlayBinReady() {
|
|
||||||
synchronized (playbinLock) {
|
|
||||||
return gstPlaybin2 != null && !gstPlaybin2.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
|
|
||||||
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
|
|
||||||
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
|
||||||
}
|
|
||||||
gstPlaybin2.getState(); //NEW
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pauseButton.setText("►");
|
|
||||||
progressSlider.setValue(0);
|
|
||||||
|
|
||||||
String durationStr = String.format(durationFormat, 0, 0, 0,
|
|
||||||
totalHours, totalMinutes, totalSeconds);
|
|
||||||
progressLabel.setText(durationStr);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true while millisElapsed is greater than END_TIME_MARGIN_MS
|
|
||||||
* from durationMillis. This is used to indicate when the video
|
|
||||||
* has ended because for some videos the time elapsed never
|
|
||||||
* becomes equal to the reported duration of the video.
|
|
||||||
*/
|
|
||||||
private boolean hasNotEnded() {
|
|
||||||
return (durationMillis - millisElapsed) > END_TIME_MARGIN_MS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Object doInBackground() throws Exception {
|
|
||||||
|
|
||||||
// enable the slider
|
|
||||||
progressSlider.setEnabled(true);
|
|
||||||
|
|
||||||
ClockTime pos;
|
|
||||||
while (hasNotEnded() && isPlayBinReady() && !isCancelled()) {
|
|
||||||
|
|
||||||
synchronized (playbinLock) {
|
|
||||||
pos = gstPlaybin2.queryPosition();
|
|
||||||
}
|
|
||||||
millisElapsed = pos.toMillis();
|
|
||||||
|
|
||||||
// pick out the elapsed hours, minutes, seconds
|
|
||||||
long secondsElapsed = millisElapsed / 1000;
|
|
||||||
int elapsedHours = (int) secondsElapsed / 3600;
|
|
||||||
secondsElapsed -= elapsedHours * 3600;
|
|
||||||
int elapsedMinutes = (int) secondsElapsed / 60;
|
|
||||||
secondsElapsed -= elapsedMinutes * 60;
|
|
||||||
int elapsedSeconds = (int) secondsElapsed;
|
|
||||||
|
|
||||||
String durationStr = String.format(durationFormat,
|
|
||||||
elapsedHours, elapsedMinutes, elapsedSeconds,
|
|
||||||
totalHours, totalMinutes, totalSeconds);
|
|
||||||
|
|
||||||
progressLabel.setText(durationStr);
|
|
||||||
autoTracking = true;
|
|
||||||
progressSlider.setValue((int) millisElapsed);
|
|
||||||
autoTracking = false;
|
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(INTER_FRAME_PERIOD_MS);
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// disable the slider
|
|
||||||
progressSlider.setEnabled(false);
|
|
||||||
|
|
||||||
resetVideo();
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void done() {
|
|
||||||
// see if any exceptions were thrown
|
|
||||||
try {
|
|
||||||
get();
|
|
||||||
} catch (InterruptedException | ExecutionException ex) {
|
|
||||||
logger.log(Level.WARNING, "Error updating video progress: {0}", ex.getMessage()); //NON-NLS
|
|
||||||
infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.infoLabel.updateErr",
|
|
||||||
ex.getMessage()));
|
|
||||||
} // catch and ignore if we were cancelled
|
|
||||||
catch (java.util.concurrent.CancellationException ex) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} //end class progress worker
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Thread that extracts and plays a file
|
|
||||||
*/
|
|
||||||
private class ExtractMedia extends SwingWorker<Long, Void> {
|
|
||||||
|
|
||||||
private ProgressHandle progress;
|
|
||||||
private final AbstractFile sourceFile;
|
|
||||||
private final java.io.File tempFile;
|
|
||||||
|
|
||||||
ExtractMedia(AbstractFile sFile, java.io.File jFile) {
|
|
||||||
this.sourceFile = sFile;
|
|
||||||
this.tempFile = jFile;
|
|
||||||
}
|
|
||||||
|
|
||||||
@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));
|
|
||||||
progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.buffering"));
|
|
||||||
progress.start(100);
|
|
||||||
try {
|
|
||||||
Files.createParentDirs(tempFile);
|
|
||||||
return ContentUtils.writeToFile(sourceFile, tempFile, progress, this, true);
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.WARNING, "Error buffering file", ex); //NON-NLS
|
|
||||||
return 0L;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0L;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* clean up or start the worker threads
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void done() {
|
|
||||||
try {
|
|
||||||
super.get(); //block and get all exceptions thrown while doInBackground()
|
|
||||||
} catch (CancellationException ex) {
|
|
||||||
logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
logger.log(Level.INFO, "Media buffering was interrupted."); //NON-NLS
|
|
||||||
} catch (Exception ex) {
|
|
||||||
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); //NON-NLS
|
|
||||||
} finally {
|
|
||||||
if (progress != null) {
|
|
||||||
progress.finish();
|
|
||||||
}
|
|
||||||
if (!this.isCancelled()) {
|
|
||||||
playMedia();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void playMedia() {
|
|
||||||
if (tempFile == null || !tempFile.exists()) {
|
|
||||||
progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progressLabel.bufferingErr"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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
|
|
||||||
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
|
|
||||||
logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed."); //NON-NLS
|
|
||||||
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
gstPlaybin2.getState();
|
|
||||||
dur = gstPlaybin2.queryDuration();
|
|
||||||
}
|
|
||||||
durationMillis = dur.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;
|
|
||||||
|
|
||||||
SwingUtilities.invokeLater(() -> {
|
|
||||||
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
|
|
||||||
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pauseButton.setText("||");
|
|
||||||
videoProgressWorker = new VideoProgressWorker();
|
|
||||||
videoProgressWorker.execute();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getExtensions() {
|
|
||||||
return EXTENSIONS.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> getMimeTypes() {
|
|
||||||
return MIMETYPES;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
151
Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoRendererPanel.java
Executable file
151
Core/src/org/sleuthkit/autopsy/contentviewers/GstVideoRendererPanel.java
Executable file
@ -0,0 +1,151 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2019 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> 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.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.
|
||||||
|
*/
|
||||||
|
final 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 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 image = new WritableImage(width, height);
|
||||||
|
PixelWriter pixelWriter = image.getPixelWriter();
|
||||||
|
pixelWriter.setPixels(0, 0, width, height, PixelFormat.getByteBgraInstance(), pixels, 0, width * 4);
|
||||||
|
return image;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -36,24 +36,24 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
|||||||
private static final Logger LOGGER = Logger.getLogger(MediaFileViewer.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(MediaFileViewer.class.getName());
|
||||||
private AbstractFile lastFile;
|
private AbstractFile lastFile;
|
||||||
//UI
|
//UI
|
||||||
private final MediaViewVideoPanel videoPanel;
|
private final MediaPlayerPanel mediaPlayerPanel;
|
||||||
private final boolean videoPanelInited;
|
private final boolean mediaPlayerPanelInited;
|
||||||
private final MediaViewImagePanel imagePanel;
|
private final MediaViewImagePanel imagePanel;
|
||||||
private final boolean imagePanelInited;
|
private final boolean imagePanelInited;
|
||||||
|
|
||||||
private static final String IMAGE_VIEWER_LAYER = "IMAGE"; //NON-NLS
|
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() {
|
public MediaFileViewer() {
|
||||||
|
|
||||||
initComponents();
|
initComponents();
|
||||||
|
|
||||||
// get the right panel for our platform
|
// get the right panel for our platform
|
||||||
videoPanel = MediaViewVideoPanel.createVideoPanel();
|
mediaPlayerPanel = new MediaPlayerPanel();
|
||||||
videoPanelInited = videoPanel.isInited();
|
mediaPlayerPanelInited = mediaPlayerPanel.isInited();
|
||||||
|
|
||||||
imagePanel = new MediaViewImagePanel();
|
imagePanel = new MediaViewImagePanel();
|
||||||
imagePanelInited = imagePanel.isInited();
|
imagePanelInited = imagePanel.isInited();
|
||||||
@ -64,9 +64,9 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
|||||||
|
|
||||||
private void customizeComponents() {
|
private void customizeComponents() {
|
||||||
add(imagePanel, IMAGE_VIEWER_LAYER);
|
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<String> mimeTypes = new ArrayList<>();
|
List<String> mimeTypes = new ArrayList<>();
|
||||||
|
|
||||||
mimeTypes.addAll(this.imagePanel.getMimeTypes());
|
mimeTypes.addAll(this.imagePanel.getSupportedMimeTypes());
|
||||||
mimeTypes.addAll(this.videoPanel.getMimeTypes());
|
mimeTypes.addAll(this.mediaPlayerPanel.getSupportedMimeTypes());
|
||||||
|
|
||||||
return mimeTypes;
|
return mimeTypes;
|
||||||
}
|
}
|
||||||
@ -123,12 +123,12 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
|||||||
|
|
||||||
final Dimension dims = MediaFileViewer.this.getSize();
|
final Dimension dims = MediaFileViewer.this.getSize();
|
||||||
//logger.info("setting node on media viewer"); //NON-NLS
|
//logger.info("setting node on media viewer"); //NON-NLS
|
||||||
if (videoPanelInited && videoPanel.isSupported(file)) {
|
if (mediaPlayerPanelInited && mediaPlayerPanel.isSupported(file)) {
|
||||||
videoPanel.setupVideo(file, dims);
|
mediaPlayerPanel.loadFile(file, dims);
|
||||||
this.showVideoPanel(true);
|
this.showVideoPanel();
|
||||||
} else if (imagePanelInited && imagePanel.isSupported(file)) {
|
} else if (imagePanelInited && imagePanel.isSupported(file)) {
|
||||||
imagePanel.showImageFx(file, dims);
|
imagePanel.showImageFx(file, dims);
|
||||||
this.showVideoPanel(false);
|
this.showImagePanel();
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOGGER.log(Level.SEVERE, "Exception while setting node", e); //NON-NLS
|
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
|
* Show the media player panel.
|
||||||
*
|
|
||||||
* @param showVideo true if video panel, false if image panel
|
|
||||||
*/
|
*/
|
||||||
private void showVideoPanel(boolean showVideo) {
|
private void showVideoPanel() {
|
||||||
CardLayout layout = (CardLayout) this.getLayout();
|
CardLayout layout = (CardLayout) this.getLayout();
|
||||||
if (showVideo) {
|
layout.show(this, MEDIA_PLAYER_LAYER);
|
||||||
layout.show(this, VIDEO_VIEWER_LAYER);
|
}
|
||||||
} else {
|
|
||||||
layout.show(this, IMAGE_VIEWER_LAYER);
|
/**
|
||||||
}
|
* Show the image panel.
|
||||||
|
*/
|
||||||
|
private void showImagePanel() {
|
||||||
|
CardLayout layout = (CardLayout) this.getLayout();
|
||||||
|
layout.show(this, IMAGE_VIEWER_LAYER);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -156,24 +158,27 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetComponent() {
|
public void resetComponent() {
|
||||||
videoPanel.reset();
|
mediaPlayerPanel.reset();
|
||||||
imagePanel.reset();
|
imagePanel.reset();
|
||||||
lastFile = null;
|
lastFile = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MediaViewPanel {
|
/**
|
||||||
|
* Panel used to display media content.
|
||||||
|
*/
|
||||||
|
protected interface MediaViewPanel {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return supported mime types
|
* @return supported mime types
|
||||||
*/
|
*/
|
||||||
List<String> getMimeTypes();
|
List<String> getSupportedMimeTypes();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* returns supported extensions (each starting with .)
|
* returns supported extensions (each starting with .)
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
List<String> getExtensionsList();
|
List<String> getSupportedExtensions();
|
||||||
|
|
||||||
boolean isSupported(AbstractFile file);
|
boolean isSupported(AbstractFile file);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,677 @@
|
|||||||
|
/*
|
||||||
|
* Autopsy Forensic Browser
|
||||||
|
*
|
||||||
|
* Copyright 2013-2019 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> 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 com.google.common.io.Files;
|
||||||
|
import java.awt.Dimension;
|
||||||
|
import java.awt.EventQueue;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
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;
|
||||||
|
import javax.swing.JLabel;
|
||||||
|
import javax.swing.JPanel;
|
||||||
|
import javax.swing.JSlider;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.SwingWorker;
|
||||||
|
import javax.swing.Timer;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import org.freedesktop.gstreamer.ClockTime;
|
||||||
|
import org.freedesktop.gstreamer.Gst;
|
||||||
|
import org.freedesktop.gstreamer.GstException;
|
||||||
|
import org.freedesktop.gstreamer.State;
|
||||||
|
import org.freedesktop.gstreamer.StateChangeReturn;
|
||||||
|
import org.freedesktop.gstreamer.elements.PlayBin;
|
||||||
|
import org.netbeans.api.progress.ProgressHandle;
|
||||||
|
import org.openide.util.NbBundle;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
|
||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a video player that is part of the Media View layered pane. It uses
|
||||||
|
* GStreamer to process the video and JavaFX to display it.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
|
||||||
|
public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaViewPanel {
|
||||||
|
|
||||||
|
private static final String[] FILE_EXTENSIONS = new String[] {
|
||||||
|
".3g2",
|
||||||
|
".3gp",
|
||||||
|
".3gpp",
|
||||||
|
".aac",
|
||||||
|
".aif",
|
||||||
|
".aiff",
|
||||||
|
".amr",
|
||||||
|
".asf",
|
||||||
|
".au",
|
||||||
|
".avi",
|
||||||
|
".flac",
|
||||||
|
".flv",
|
||||||
|
".m4a",
|
||||||
|
".m4v",
|
||||||
|
".mka",
|
||||||
|
".mkv",
|
||||||
|
".mov",
|
||||||
|
".mp2",
|
||||||
|
".mp3",
|
||||||
|
".mp4",
|
||||||
|
".mpeg",
|
||||||
|
".mpg",
|
||||||
|
".mxf",
|
||||||
|
".ogg",
|
||||||
|
".wav",
|
||||||
|
".webm",
|
||||||
|
".wma",
|
||||||
|
".wmv",
|
||||||
|
}; //NON-NLS
|
||||||
|
private static final List<String> MIME_TYPES = Arrays.asList(
|
||||||
|
"video/3gpp",
|
||||||
|
"video/3gpp2",
|
||||||
|
"audio/aiff",
|
||||||
|
"audio/amr-wb",
|
||||||
|
"audio/basic",
|
||||||
|
"audio/mp4",
|
||||||
|
"video/mp4",
|
||||||
|
"audio/mpeg",
|
||||||
|
"video/mpeg",
|
||||||
|
"audio/mpeg3",
|
||||||
|
"application/mxf",
|
||||||
|
"application/ogg",
|
||||||
|
"video/quicktime",
|
||||||
|
"audio/vorbis",
|
||||||
|
"audio/vnd.wave",
|
||||||
|
"video/webm",
|
||||||
|
"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",
|
||||||
|
"video/x-flv",
|
||||||
|
"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",
|
||||||
|
"audio/x-mace",
|
||||||
|
"audio/x-matroska",
|
||||||
|
"video/x-matroska",
|
||||||
|
"audio/x-mpeg",
|
||||||
|
"video/x-mpeg",
|
||||||
|
"audio/x-mpeg-3",
|
||||||
|
"video/x-ms-asf",
|
||||||
|
"audio/x-ms-wma",
|
||||||
|
"video/x-ms-wmv",
|
||||||
|
"video/x-msmpeg",
|
||||||
|
"video/x-msvideo",
|
||||||
|
"video/x-msvideocodec",
|
||||||
|
"audio/x-mulaw",
|
||||||
|
"audio/x-nist",
|
||||||
|
"audio/x-oggflac",
|
||||||
|
"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(MediaPlayerPanel.class.getName());
|
||||||
|
private boolean gstInited;
|
||||||
|
private static final String MEDIA_PLAYER_ERROR_STRING = NbBundle.getMessage(MediaPlayerPanel.class, "GstVideoPanel.cannotProcFile.err");
|
||||||
|
//playback
|
||||||
|
private long durationMillis = 0;
|
||||||
|
private int totalHours, totalMinutes, totalSeconds;
|
||||||
|
private volatile PlayBin gstPlayBin;
|
||||||
|
private GstVideoRendererPanel gstVideoRenderer;
|
||||||
|
private final Object playbinLock = new Object(); // lock for synchronization of gstPlayBin player
|
||||||
|
private AbstractFile currentFile;
|
||||||
|
|
||||||
|
private Timer timer;
|
||||||
|
private ExtractMedia extractMediaWorker;
|
||||||
|
|
||||||
|
private static final long END_TIME_MARGIN_NS = 50000000;
|
||||||
|
private static final int PLAYER_STATUS_UPDATE_INTERVAL_MS = 50;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new form MediaViewVideoPanel
|
||||||
|
*/
|
||||||
|
public MediaPlayerPanel() {
|
||||||
|
initComponents();
|
||||||
|
customizeComponents();
|
||||||
|
}
|
||||||
|
|
||||||
|
public JButton getPauseButton() {
|
||||||
|
return pauseButton;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JLabel getProgressLabel() {
|
||||||
|
return progressLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JSlider getProgressSlider() {
|
||||||
|
return progressSlider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public JPanel getVideoPanel() {
|
||||||
|
return videoPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Has this MediaPlayerPanel been initialized correctly?
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isInited() {
|
||||||
|
return gstInited;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void customizeComponents() {
|
||||||
|
if (!initGst()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
progressSlider.setEnabled(false); // disable slider; enable after user plays vid
|
||||||
|
progressSlider.setMinimum(0);
|
||||||
|
progressSlider.setMaximum(2000);
|
||||||
|
progressSlider.setValue(0);
|
||||||
|
progressSlider.addChangeListener(new ChangeListener() {
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent event) {
|
||||||
|
if (gstPlayBin == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (progressSlider.getValueIsAdjusting()) {
|
||||||
|
synchronized (playbinLock) {
|
||||||
|
long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
|
||||||
|
long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||||
|
if (duration > 0) {
|
||||||
|
double relativePosition = progressSlider.getValue() / 2000.0;
|
||||||
|
gstPlayBin.seek((long) (relativePosition * duration), TimeUnit.NANOSECONDS);
|
||||||
|
} else if (position > 0 || progressSlider.getValue() > 0) {
|
||||||
|
gstPlayBin.seek(ClockTime.ZERO);
|
||||||
|
progressSlider.setValue(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean initGst() {
|
||||||
|
try {
|
||||||
|
logger.log(Level.INFO, "Initializing gstreamer for video/audio viewing"); //NON-NLS
|
||||||
|
Gst.init();
|
||||||
|
gstInited = true;
|
||||||
|
} catch (GstException ex) {
|
||||||
|
gstInited = false;
|
||||||
|
logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and frame extraction capabilities", ex); //NON-NLS
|
||||||
|
MessageNotifyUtil.Notify.error(
|
||||||
|
NbBundle.getMessage(this.getClass(), "GstVideoPanel.initGst.gstException.msg"),
|
||||||
|
ex.getMessage());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 loadFile(final AbstractFile file, final Dimension dims) {
|
||||||
|
EventQueue.invokeLater(() -> {
|
||||||
|
reset();
|
||||||
|
infoLabel.setText("");
|
||||||
|
currentFile = file;
|
||||||
|
final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
|
||||||
|
if (deleted) {
|
||||||
|
infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.setupVideo.infoLabel.text"));
|
||||||
|
videoPanel.removeAll();
|
||||||
|
pauseButton.setEnabled(false);
|
||||||
|
progressSlider.setEnabled(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
java.io.File ioFile;
|
||||||
|
try {
|
||||||
|
ioFile = VideoUtils.getVideoFileInTempDir(file);
|
||||||
|
} catch (NoCurrentCaseException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
|
||||||
|
infoLabel.setText(Bundle.GstVideoPanel_noOpenCase_errMsg());
|
||||||
|
pauseButton.setEnabled(false);
|
||||||
|
progressSlider.setEnabled(false);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String path = "";
|
||||||
|
try {
|
||||||
|
path = file.getUniquePath();
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Cannot get unique path of video file.", ex); //NON-NLS
|
||||||
|
}
|
||||||
|
infoLabel.setText(path);
|
||||||
|
infoLabel.setToolTipText(path);
|
||||||
|
pauseButton.setEnabled(true);
|
||||||
|
progressSlider.setEnabled(true);
|
||||||
|
timer = new Timer(PLAYER_STATUS_UPDATE_INTERVAL_MS, event -> {
|
||||||
|
if (!progressSlider.getValueIsAdjusting()) {
|
||||||
|
long duration;
|
||||||
|
long position;
|
||||||
|
synchronized (playbinLock) {
|
||||||
|
duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
|
||||||
|
position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
|
||||||
|
if (duration > 0) {
|
||||||
|
long positionDelta = duration - position;
|
||||||
|
if (positionDelta <= END_TIME_MARGIN_NS && gstPlayBin.isPlaying()) {
|
||||||
|
gstPlayBin.pause();
|
||||||
|
if (gstPlayBin.seek(ClockTime.ZERO) == false) {
|
||||||
|
logger.log(Level.WARNING, "Attempt to call PlayBin.seek() failed."); //NON-NLS
|
||||||
|
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
progressSlider.setValue(0);
|
||||||
|
pauseButton.setText("►");
|
||||||
|
} else {
|
||||||
|
double relativePosition = (double) position / duration;
|
||||||
|
progressSlider.setValue((int) (relativePosition * 2000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
durationMillis = duration / 1000000;
|
||||||
|
// 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 millisElapsed = position / 1000000;
|
||||||
|
// pick out the elapsed hours, minutes, seconds
|
||||||
|
long secondsElapsed = millisElapsed / 1000;
|
||||||
|
int elapsedHours = (int) secondsElapsed / 3600;
|
||||||
|
secondsElapsed -= elapsedHours * 3600;
|
||||||
|
int elapsedMinutes = (int) secondsElapsed / 60;
|
||||||
|
secondsElapsed -= elapsedMinutes * 60;
|
||||||
|
int elapsedSeconds = (int) secondsElapsed;
|
||||||
|
|
||||||
|
String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; //NON-NLS
|
||||||
|
String durationStr = String.format(durationFormat,
|
||||||
|
elapsedHours, elapsedMinutes, elapsedSeconds,
|
||||||
|
totalHours, totalMinutes, totalSeconds);
|
||||||
|
progressLabel.setText(durationStr);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
timer.start();
|
||||||
|
|
||||||
|
gstVideoRenderer = new GstVideoRendererPanel();
|
||||||
|
synchronized (playbinLock) {
|
||||||
|
if (gstPlayBin != null) {
|
||||||
|
gstPlayBin.dispose();
|
||||||
|
}
|
||||||
|
gstPlayBin = new PlayBin("VideoPlayer"); //NON-NLS
|
||||||
|
gstPlayBin.setVideoSink(gstVideoRenderer.getVideoSink());
|
||||||
|
|
||||||
|
videoPanel.removeAll();
|
||||||
|
|
||||||
|
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||||
|
|
||||||
|
videoPanel.add(gstVideoRenderer);//add jfx ui to JPanel
|
||||||
|
|
||||||
|
videoPanel.setVisible(true);
|
||||||
|
|
||||||
|
gstPlayBin.setInputFile(ioFile);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare this MediaViewVideoPanel to accept a different media file.
|
||||||
|
*/
|
||||||
|
void reset() {
|
||||||
|
if (timer != null) {
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
// reset the progress label text on the event dispatch thread
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
progressLabel.setText("");
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!isInited()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (playbinLock) {
|
||||||
|
if (gstPlayBin != null) {
|
||||||
|
if (gstPlayBin.isPlaying() && gstPlayBin.stop() == StateChangeReturn.FAILURE) {
|
||||||
|
logger.log(Level.WARNING, "Attempt to call PlayBin.stop() failed."); //NON-NLS
|
||||||
|
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gstPlayBin.dispose();
|
||||||
|
gstPlayBin = null;
|
||||||
|
}
|
||||||
|
gstVideoRenderer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
progressSlider.setValue(0);
|
||||||
|
pauseButton.setText("►");
|
||||||
|
|
||||||
|
currentFile = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
* regenerated by the Form Editor.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="Generated Code">
|
||||||
|
private void initComponents() {
|
||||||
|
|
||||||
|
videoPanel = new javax.swing.JPanel();
|
||||||
|
controlPanel = new javax.swing.JPanel();
|
||||||
|
pauseButton = new javax.swing.JButton();
|
||||||
|
progressSlider = new javax.swing.JSlider();
|
||||||
|
progressLabel = new javax.swing.JLabel();
|
||||||
|
infoLabel = new javax.swing.JLabel();
|
||||||
|
|
||||||
|
javax.swing.GroupLayout videoPanelLayout = new javax.swing.GroupLayout(videoPanel);
|
||||||
|
videoPanel.setLayout(videoPanelLayout);
|
||||||
|
videoPanelLayout.setHorizontalGroup(
|
||||||
|
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGap(0, 0, Short.MAX_VALUE)
|
||||||
|
);
|
||||||
|
videoPanelLayout.setVerticalGroup(
|
||||||
|
videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGap(0, 231, Short.MAX_VALUE)
|
||||||
|
);
|
||||||
|
|
||||||
|
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(MediaPlayerPanel.class, "MediaViewVideoPanel.progressLabel.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);
|
||||||
|
controlPanelLayout.setHorizontalGroup(
|
||||||
|
controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(controlPanelLayout.createSequentialGroup()
|
||||||
|
.addContainerGap()
|
||||||
|
.addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(controlPanelLayout.createSequentialGroup()
|
||||||
|
.addGap(6, 6, 6)
|
||||||
|
.addComponent(infoLabel)
|
||||||
|
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||||
|
.addGroup(controlPanelLayout.createSequentialGroup()
|
||||||
|
.addComponent(pauseButton)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 265, Short.MAX_VALUE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(progressLabel)
|
||||||
|
.addContainerGap())))
|
||||||
|
);
|
||||||
|
controlPanelLayout.setVerticalGroup(
|
||||||
|
controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(controlPanelLayout.createSequentialGroup()
|
||||||
|
.addContainerGap()
|
||||||
|
.addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(progressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addComponent(pauseButton)
|
||||||
|
.addComponent(progressLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
|
.addComponent(infoLabel)
|
||||||
|
.addContainerGap())
|
||||||
|
);
|
||||||
|
|
||||||
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
|
this.setLayout(layout);
|
||||||
|
layout.setHorizontalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
|
.addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
|
);
|
||||||
|
layout.setVerticalGroup(
|
||||||
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
|
.addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||||
|
);
|
||||||
|
}// </editor-fold>
|
||||||
|
|
||||||
|
private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed
|
||||||
|
synchronized (playbinLock) {
|
||||||
|
if (gstPlayBin == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
State state = gstPlayBin.getState();
|
||||||
|
if (state.equals(State.PLAYING)) {
|
||||||
|
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("►");
|
||||||
|
} else if (state.equals(State.PAUSED)) {
|
||||||
|
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("||");
|
||||||
|
} else if (state.equals(State.READY) || state.equals(State.NULL)) {
|
||||||
|
final File tempVideoFile;
|
||||||
|
try {
|
||||||
|
tempVideoFile = VideoUtils.getVideoFileInTempDir(currentFile);
|
||||||
|
} catch (NoCurrentCaseException ex) {
|
||||||
|
logger.log(Level.WARNING, "Exception while getting open case."); //NON-NLS
|
||||||
|
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (extractMediaWorker != null) {
|
||||||
|
extractMediaWorker.cancel(true);
|
||||||
|
extractMediaWorker = null;
|
||||||
|
}
|
||||||
|
extractMediaWorker = new ExtractMedia(currentFile, tempVideoFile);
|
||||||
|
extractMediaWorker.execute();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}//GEN-LAST:event_pauseButtonActionPerformed
|
||||||
|
|
||||||
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
|
private javax.swing.JPanel controlPanel;
|
||||||
|
private javax.swing.JLabel infoLabel;
|
||||||
|
private javax.swing.JButton pauseButton;
|
||||||
|
private javax.swing.JLabel progressLabel;
|
||||||
|
private javax.swing.JSlider progressSlider;
|
||||||
|
private javax.swing.JPanel videoPanel;
|
||||||
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thread that extracts and plays a file
|
||||||
|
*/
|
||||||
|
private class ExtractMedia extends SwingWorker<Long, Void> {
|
||||||
|
|
||||||
|
private ProgressHandle progress;
|
||||||
|
private final AbstractFile sourceFile;
|
||||||
|
private final java.io.File tempFile;
|
||||||
|
|
||||||
|
ExtractMedia(AbstractFile sFile, java.io.File jFile) {
|
||||||
|
this.sourceFile = sFile;
|
||||||
|
this.tempFile = jFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Long doInBackground() throws Exception {
|
||||||
|
if (tempFile.exists() == false || tempFile.length() < sourceFile.getSize()) {
|
||||||
|
progress = ProgressHandle.createHandle(NbBundle.getMessage(MediaPlayerPanel.class, "GstVideoPanel.ExtractMedia.progress.buffering", sourceFile.getName()), () -> this.cancel(true));
|
||||||
|
progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.buffering"));
|
||||||
|
progress.start(100);
|
||||||
|
try {
|
||||||
|
Files.createParentDirs(tempFile);
|
||||||
|
return ContentUtils.writeToFile(sourceFile, tempFile, progress, this, true);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.WARNING, "Error buffering file", ex); //NON-NLS
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0L;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* clean up or start the worker threads
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected void done() {
|
||||||
|
try {
|
||||||
|
super.get(); //block and get all exceptions thrown while doInBackground()
|
||||||
|
} catch (CancellationException ex) {
|
||||||
|
logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
logger.log(Level.INFO, "Media buffering was interrupted."); //NON-NLS
|
||||||
|
} catch (ExecutionException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); //NON-NLS
|
||||||
|
} finally {
|
||||||
|
if (progress != null) {
|
||||||
|
progress.finish();
|
||||||
|
}
|
||||||
|
if (!this.isCancelled()) {
|
||||||
|
playMedia();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void playMedia() {
|
||||||
|
if (tempFile == null || !tempFile.exists()) {
|
||||||
|
progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progressLabel.bufferingErr"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
synchronized (playbinLock) {
|
||||||
|
gstPlayBin.seek(ClockTime.ZERO);
|
||||||
|
// must play, then pause and get state to get duration.
|
||||||
|
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("||");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getSupportedExtensions() {
|
||||||
|
return Arrays.asList(FILE_EXTENSIONS.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getSupportedMimeTypes() {
|
||||||
|
return MIME_TYPES;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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<String> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -245,7 +245,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
|||||||
* @return supported mime types
|
* @return supported mime types
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<String> getMimeTypes() {
|
public List<String> getSupportedMimeTypes() {
|
||||||
return Collections.unmodifiableList(Lists.newArrayList(supportedMimes));
|
return Collections.unmodifiableList(Lists.newArrayList(supportedMimes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,7 +255,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public List<String> getExtensionsList() {
|
public List<String> getSupportedExtensions() {
|
||||||
return getExtensions();
|
return getExtensions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,168 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2013-2018 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> 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<String> 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<String> getMimeTypes();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> 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<String> 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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2013 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> 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;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface used to capture frames from a video file.
|
|
||||||
*/
|
|
||||||
public interface FrameCapture {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param file the video file to use
|
|
||||||
* @param numFrames the number of frames to capture. Note that the actual
|
|
||||||
* number of frames returned may be less than this number.
|
|
||||||
* Specifically, this may happen if the video is very
|
|
||||||
* short.
|
|
||||||
*
|
|
||||||
* @return a list of VideoFrames representing the captured frames
|
|
||||||
*/
|
|
||||||
List<VideoFrame> captureFrames(File file, int numFrames) throws Exception;
|
|
||||||
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* Autopsy Forensic Browser
|
|
||||||
*
|
|
||||||
* Copyright 2018 Basis Technology Corp.
|
|
||||||
* Contact: carrier <at> sleuthkit <dot> 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 {
|
|
||||||
|
|
||||||
}
|
|
@ -12,7 +12,9 @@
|
|||||||
<dependency org="com.google.code.gson" name="gson" rev="2.8.1"/>
|
<dependency org="com.google.code.gson" name="gson" rev="2.8.1"/>
|
||||||
|
|
||||||
<!-- for viewers -->
|
<!-- for viewers -->
|
||||||
<dependency conf="autopsy_core->*" org="com.googlecode.gstreamer-java" name="gstreamer-java" rev="1.5"/>
|
<dependency conf="autopsy_core->*" org="org.freedesktop.gstreamer" name="gst1-java-core" rev="0.9.3"/>
|
||||||
|
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="jna" rev="3.4.0"/>
|
||||||
|
<dependency conf="autopsy_core->*" org="net.java.dev.jna" name="platform" rev="3.4.0"/>
|
||||||
|
|
||||||
<!-- for file search -->
|
<!-- for file search -->
|
||||||
<dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/>
|
<dependency conf="autopsy_core->*" org="com.github.lgooddatepicker" name="LGoodDatePicker" rev="10.3.1"/>
|
||||||
@ -67,5 +69,9 @@
|
|||||||
<dependency conf="autopsy_core->default" org="com.twelvemonkeys.imageio" name="imageio-thumbsdb" rev="3.2" />
|
<dependency conf="autopsy_core->default" org="com.twelvemonkeys.imageio" name="imageio-thumbsdb" rev="3.2" />
|
||||||
<dependency conf="autopsy_core->default" org="com.twelvemonkeys.imageio" name="imageio-core" rev="3.2" />
|
<dependency conf="autopsy_core->default" org="com.twelvemonkeys.imageio" name="imageio-core" rev="3.2" />
|
||||||
<dependency conf="autopsy_core->default" org="com.twelvemonkeys.imageio" name="imageio-metadata" rev="3.2" />
|
<dependency conf="autopsy_core->default" org="com.twelvemonkeys.imageio" name="imageio-metadata" rev="3.2" />
|
||||||
|
|
||||||
|
<!-- conflict resolutions for multiple JAR versions -->
|
||||||
|
<conflict org="net.java.dev.jna" module="jna" rev="3.4.0"/>
|
||||||
|
<conflict org="net.java.dev.jna" module="platform" rev="3.4.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</ivy-module>
|
</ivy-module>
|
||||||
|
@ -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.geronimo-jms_1.1_spec-1.0.jar=release/modules/ext/geronimo-jms_1.1_spec-1.0.jar
|
||||||
file.reference.gson-2.8.1.jar=release/modules/ext/gson-2.8.1.jar
|
file.reference.gson-2.8.1.jar=release/modules/ext/gson-2.8.1.jar
|
||||||
file.reference.gstreamer-java-1.5.jar=release/modules/ext/gstreamer-java-1.5.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.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-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
|
file.reference.imageio-core-3.2.jar=release/modules/ext/imageio-core-3.2.jar
|
||||||
|
@ -587,24 +587,24 @@
|
|||||||
<package>org.dom4j.util</package>
|
<package>org.dom4j.util</package>
|
||||||
<package>org.dom4j.xpath</package>
|
<package>org.dom4j.xpath</package>
|
||||||
<package>org.dom4j.xpp</package>
|
<package>org.dom4j.xpp</package>
|
||||||
<package>org.gstreamer</package>
|
<package>org.freedesktop.gstreamer</package>
|
||||||
<package>org.gstreamer.controller</package>
|
<package>org.freedesktop.gstreamer.controller</package>
|
||||||
<package>org.gstreamer.elements</package>
|
<package>org.freedesktop.gstreamer.elements</package>
|
||||||
<package>org.gstreamer.elements.good</package>
|
<package>org.freedesktop.gstreamer.elements.good</package>
|
||||||
<package>org.gstreamer.event</package>
|
<package>org.freedesktop.gstreamer.event</package>
|
||||||
<package>org.gstreamer.example</package>
|
<package>org.freedesktop.gstreamer.example</package>
|
||||||
<package>org.gstreamer.glib</package>
|
<package>org.freedesktop.gstreamer.glib</package>
|
||||||
<package>org.gstreamer.interfaces</package>
|
<package>org.freedesktop.gstreamer.interfaces</package>
|
||||||
<package>org.gstreamer.io</package>
|
<package>org.freedesktop.gstreamer.io</package>
|
||||||
<package>org.gstreamer.lowlevel</package>
|
<package>org.freedesktop.gstreamer.lowlevel</package>
|
||||||
<package>org.gstreamer.lowlevel.annotations</package>
|
<package>org.freedesktop.gstreamer.lowlevel.annotations</package>
|
||||||
<package>org.gstreamer.media</package>
|
<package>org.freedesktop.gstreamer.media</package>
|
||||||
<package>org.gstreamer.media.event</package>
|
<package>org.freedesktop.gstreamer.media.event</package>
|
||||||
<package>org.gstreamer.message</package>
|
<package>org.freedesktop.gstreamer.message</package>
|
||||||
<package>org.gstreamer.query</package>
|
<package>org.freedesktop.gstreamer.query</package>
|
||||||
<package>org.gstreamer.swing</package>
|
<package>org.freedesktop.gstreamer.swing</package>
|
||||||
<package>org.gstreamer.swt</package>
|
<package>org.freedesktop.gstreamer.swt</package>
|
||||||
<package>org.gstreamer.swt.overlay</package>
|
<package>org.freedesktop.gstreamer.swt.overlay</package>
|
||||||
<package>org.hyperic.jni</package>
|
<package>org.hyperic.jni</package>
|
||||||
<package>org.hyperic.sigar</package>
|
<package>org.hyperic.sigar</package>
|
||||||
<package>org.hyperic.sigar.cmd</package>
|
<package>org.hyperic.sigar.cmd</package>
|
||||||
@ -967,8 +967,8 @@
|
|||||||
<binary-origin>release/modules/ext/common-lang-3.2.jar</binary-origin>
|
<binary-origin>release/modules/ext/common-lang-3.2.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/gstreamer-java-1.5.jar</runtime-relative-path>
|
<runtime-relative-path>ext/gst1-java-core-0.9.3.jar</runtime-relative-path>
|
||||||
<binary-origin>release/modules/ext/gstreamer-java-1.5.jar</binary-origin>
|
<binary-origin>release/modules/ext/gst1-java-core-0.9.3.jar</binary-origin>
|
||||||
</class-path-extension>
|
</class-path-extension>
|
||||||
<class-path-extension>
|
<class-path-extension>
|
||||||
<runtime-relative-path>ext/dom4j-1.6.1.jar</runtime-relative-path>
|
<runtime-relative-path>ext/dom4j-1.6.1.jar</runtime-relative-path>
|
||||||
|
@ -21,10 +21,10 @@
|
|||||||
<if>
|
<if>
|
||||||
<isset property="jre.home.32" />
|
<isset property="jre.home.32" />
|
||||||
<then>
|
<then>
|
||||||
<echo message="32-bit JRE found, 32 bit installer will be built."/>
|
<echo message="32-bit JRE found, 32-bit installer will be built."/>
|
||||||
</then>
|
</then>
|
||||||
<else>
|
<else>
|
||||||
<echo message="32-bit JRE not found. No 32 bit installer will be build. Set the JRE_HOME_32 environment variable to generate a 32-bit installer."/>
|
<echo message="32-bit JRE not found. No 32-bit installer will be built. Set the JRE_HOME_32 environment variable to generate a 32-bit installer."/>
|
||||||
</else>
|
</else>
|
||||||
</if>
|
</if>
|
||||||
|
|
||||||
@ -34,10 +34,10 @@
|
|||||||
<if>
|
<if>
|
||||||
<isset property="jre.home.64" />
|
<isset property="jre.home.64" />
|
||||||
<then>
|
<then>
|
||||||
<echo message="64-bit JRE found, 64 bit installer will be built."/>
|
<echo message="64-bit JRE found, 64-bit installer will be built."/>
|
||||||
</then>
|
</then>
|
||||||
<else>
|
<else>
|
||||||
<echo message="64-bit JRE not found. No 64 bit installer will be build. Set the JRE_HOME_64 environment variable to generate a 64-bit installer."/>
|
<echo message="64-bit JRE not found. No 64-bit installer will be built. Set the JRE_HOME_64 environment variable to generate a 64-bit installer."/>
|
||||||
</else>
|
</else>
|
||||||
</if>
|
</if>
|
||||||
</target>
|
</target>
|
||||||
@ -118,19 +118,15 @@
|
|||||||
<arg line="/edit ${aip-path-base} /SetAppdir -buildname DefaultBuild -path [ProgramFilesFolder][ProductName]-${app.version}"/>
|
<arg line="/edit ${aip-path-base} /SetAppdir -buildname DefaultBuild -path [ProgramFilesFolder][ProductName]-${app.version}"/>
|
||||||
</exec>
|
</exec>
|
||||||
|
|
||||||
<!-- gstreamer needs special path info to be set -->
|
<var name="gstreamer-relative-path" value="gstreamer/1.0/x86"/>
|
||||||
<exec executable="${ai-exe-path}">
|
<antcall target="install-gstreamer" inheritAll="true" />
|
||||||
<arg line="/edit ${aip-path} /NewEnvironment -name GSTREAMER_PATH -value [APPDIR]gstreamer\bin -install_operation CreateUpdate -behavior Append -system_variable"/>
|
|
||||||
</exec>
|
|
||||||
<exec executable="${ai-exe-path}">
|
|
||||||
<arg line="/edit ${aip-path} /NewEnvironment -name GSTREAMER_PATH -value [APPDIR]gstreamer\lib\gstreamer-0.10 -install_operation CreateUpdate -behavior Append -system_variable"/>
|
|
||||||
</exec>
|
|
||||||
<exec executable="${ai-exe-path}">
|
|
||||||
<arg line="/edit ${aip-path} /NewEnvironment -name PATH -value %GSTREAMER_PATH% -install_operation CreateUpdate -behavior Append -system_variable"/>
|
|
||||||
</exec>
|
|
||||||
|
|
||||||
<antcall target="ai-build" inheritAll="true" />
|
<antcall target="ai-build" inheritAll="true" />
|
||||||
|
|
||||||
|
<delete includeEmptyDirs="true">
|
||||||
|
<fileset dir="${inst-path}/${app.name}/${gstreamer-relative-path}"/>
|
||||||
|
</delete>
|
||||||
|
|
||||||
<delete dir="${nbdist.dir}/installer_${app.name}_32-cache"/>
|
<delete dir="${nbdist.dir}/installer_${app.name}_32-cache"/>
|
||||||
<move file="${nbdist.dir}/installer_${app.name}_32-SetupFiles/installer_${app.name}_32.msi" tofile="${nbdist.dir}/${app.name}-${app.version}-32bit.msi" />
|
<move file="${nbdist.dir}/installer_${app.name}_32-SetupFiles/installer_${app.name}_32.msi" tofile="${nbdist.dir}/${app.name}-${app.version}-32bit.msi" />
|
||||||
</target>
|
</target>
|
||||||
@ -158,11 +154,45 @@
|
|||||||
<arg line="/edit ${aip-path} /SetPackageType x64"/>
|
<arg line="/edit ${aip-path} /SetPackageType x64"/>
|
||||||
</exec>
|
</exec>
|
||||||
|
|
||||||
|
<var name="gstreamer-relative-path" value="gstreamer/1.0/x86_64"/>
|
||||||
|
<antcall target="install-gstreamer" inheritAll="true" />
|
||||||
|
|
||||||
<antcall target="ai-build" inheritAll="true" />
|
<antcall target="ai-build" inheritAll="true" />
|
||||||
|
|
||||||
|
<delete includeEmptyDirs="true">
|
||||||
|
<fileset dir="${inst-path}/${app.name}/${gstreamer-relative-path}"/>
|
||||||
|
</delete>
|
||||||
|
|
||||||
<delete dir="${nbdist.dir}/installer_${app.name}_64-cache"/>
|
<delete dir="${nbdist.dir}/installer_${app.name}_64-cache"/>
|
||||||
<move file="${nbdist.dir}/installer_${app.name}_64-SetupFiles/installer_${app.name}_64.msi" tofile="${nbdist.dir}/${app.name}-${app.version}-64bit.msi" />
|
<move file="${nbdist.dir}/installer_${app.name}_64-SetupFiles/installer_${app.name}_64.msi" tofile="${nbdist.dir}/${app.name}-${app.version}-64bit.msi" />
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
|
||||||
|
<target name="install-gstreamer" description="Install a copy of GStreamer.">
|
||||||
|
<!-- Make GStreamer available to the installer -->
|
||||||
|
<mkdir dir="${inst-path}/${app.name}/${gstreamer-relative-path}"/>
|
||||||
|
<copy todir="${inst-path}/${app.name}/${gstreamer-relative-path}" >
|
||||||
|
<fileset dir="${thirdparty.dir}/${gstreamer-relative-path}"/>
|
||||||
|
</copy>
|
||||||
|
|
||||||
|
<!-- The 'libgstlibav.dll' file is too big to store on GitHub, so we
|
||||||
|
have it stored in a ZIP file. We'll extract it in place and remove
|
||||||
|
the ZIP file afterward. -->
|
||||||
|
<unzip src="${inst-path}/${app.name}/${gstreamer-relative-path}/lib/gstreamer-1.0/libgstlibav.zip"
|
||||||
|
dest="${inst-path}/${app.name}/${gstreamer-relative-path}/lib/gstreamer-1.0/"/>
|
||||||
|
<delete file="${inst-path}/${app.name}/${gstreamer-relative-path}/lib/gstreamer-1.0/libgstlibav.zip" />
|
||||||
|
|
||||||
|
<!-- GStreamer needs special path info to be set -->
|
||||||
|
<exec executable="${ai-exe-path}">
|
||||||
|
<arg line="/edit ${aip-path} /NewEnvironment -name GSTREAMER_PATH -value [APPDIR]${app.name}\${gstreamer-relative-path}\bin -install_operation CreateUpdate -behavior Append -system_variable"/>
|
||||||
|
</exec>
|
||||||
|
<exec executable="${ai-exe-path}">
|
||||||
|
<arg line="/edit ${aip-path} /NewEnvironment -name GSTREAMER_PATH -value [APPDIR]${app.name}\${gstreamer-relative-path}\lib\gstreamer-1.0 -install_operation CreateUpdate -behavior Append -system_variable"/>
|
||||||
|
</exec>
|
||||||
|
<exec executable="${ai-exe-path}">
|
||||||
|
<arg line="/edit ${aip-path} /NewEnvironment -name PATH -value %GSTREAMER_PATH% -install_operation CreateUpdate -behavior Append -system_variable"/>
|
||||||
|
</exec>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
|
||||||
<!-- 32/64 specific since config settings are different -->
|
<!-- 32/64 specific since config settings are different -->
|
||||||
@ -194,7 +224,7 @@
|
|||||||
</path>
|
</path>
|
||||||
</foreach>
|
</foreach>
|
||||||
|
|
||||||
<echo message="Removing extra executable..."/>
|
<echo message="Removing extra Autopsy executable..."/>
|
||||||
<exec executable="${ai-exe-path}">
|
<exec executable="${ai-exe-path}">
|
||||||
<arg line="/edit ${aip-path} /DelFile APPDIR\bin\${aut-bin-name-todelete}"/>
|
<arg line="/edit ${aip-path} /DelFile APPDIR\bin\${aut-bin-name-todelete}"/>
|
||||||
</exec>
|
</exec>
|
||||||
|
@ -102,7 +102,6 @@
|
|||||||
<copy file="${basedir}/NEWS.txt" tofile="${zip-tmp}/${app.name}/NEWS.txt"/>
|
<copy file="${basedir}/NEWS.txt" tofile="${zip-tmp}/${app.name}/NEWS.txt"/>
|
||||||
<copy file="${basedir}/Running_Linux_OSX.txt" tofile="${zip-tmp}/${app.name}/Running_Linux_OSX.txt"/>
|
<copy file="${basedir}/Running_Linux_OSX.txt" tofile="${zip-tmp}/${app.name}/Running_Linux_OSX.txt"/>
|
||||||
<copy file="${basedir}/unix_setup.sh" tofile="${zip-tmp}/${app.name}/unix_setup.sh"/>
|
<copy file="${basedir}/unix_setup.sh" tofile="${zip-tmp}/${app.name}/unix_setup.sh"/>
|
||||||
<unzip src="${thirdparty.dir}/gstreamer/windows/i386/0.10.7/gstreamer.zip" dest="${zip-tmp}/${app.name}/gstreamer"/>
|
|
||||||
|
|
||||||
|
|
||||||
<copy file="${basedir}/icons/icon.ico" tofile="${zip-tmp}/${app.name}/icon.ico" overwrite="true"/>
|
<copy file="${basedir}/icons/icon.ico" tofile="${zip-tmp}/${app.name}/icon.ico" overwrite="true"/>
|
||||||
|
BIN
thirdparty/gstreamer/1.0/x86/bin/gdbus.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gdbus.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/gdk-pixbuf-csource.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gdk-pixbuf-csource.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/gdk-pixbuf-query-loaders.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gdk-pixbuf-query-loaders.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/ges-launch-1.0.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/ges-launch-1.0.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/gio-querymodules.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gio-querymodules.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/glib-compile-schemas.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/glib-compile-schemas.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/gsettings.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gsettings.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-device-monitor-1.0.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-device-monitor-1.0.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-discoverer-1.0.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-discoverer-1.0.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-inspect-1.0.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-inspect-1.0.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-launch-1.0.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-launch-1.0.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-play-1.0.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-play-1.0.exe
vendored
Executable file
Binary file not shown.
24
thirdparty/gstreamer/1.0/x86/bin/gst-shell
vendored
Executable file
24
thirdparty/gstreamer/1.0/x86/bin/gst-shell
vendored
Executable file
@ -0,0 +1,24 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
export GSTREAMER_ROOT="c:/gstreamer/1.0/x86"
|
||||||
|
export CPPFLAGS="-I${GSTREAMER_ROOT}/include ${CPPFLAGS}"
|
||||||
|
export GST_REGISTRY_1_0="${HOME}/.cache/gstreamer-1.0/gstreamer-cerbero-registry"
|
||||||
|
export XDG_CONFIG_DIRS="${GSTREAMER_ROOT}/etc/xdg${XDG_CONFIG_DIRS:+:$XDG_CONFIG_DIRS}:/etc/xdg"
|
||||||
|
export LDFLAGS="-L${GSTREAMER_ROOT}/lib ${LDFLAGS}"
|
||||||
|
export XDG_DATA_DIRS="${GSTREAMER_ROOT}/share${XDG_DATA_DIRS:+:$XDG_DATA_DIRS}:/usr/local/share:/usr/share"
|
||||||
|
export GST_PLUGIN_SYSTEM_PATH_1_0="${GSTREAMER_ROOT}/lib/gstreamer-1.0"
|
||||||
|
export GIO_EXTRA_MODULES="${GSTREAMER_ROOT}/lib/gio/modules"
|
||||||
|
export GST_PLUGIN_SYSTEM_PATH="${GSTREAMER_ROOT}/lib/gstreamer-0.10"
|
||||||
|
export GST_PLUGIN_SCANNER="${GSTREAMER_ROOT}/libexec/gstreamer-0.10/gst-plugin-scanner"
|
||||||
|
export GST_PLUGIN_SCANNER_1_0="${GSTREAMER_ROOT}/libexec/gstreamer-1.0/gst-plugin-scanner"
|
||||||
|
export CFLAGS="-I${GSTREAMER_ROOT}/include ${CFLAGS}"
|
||||||
|
export PYTHONPATH="${GSTREAMER_ROOT}/lib/python2.7/site-packages${PYTHONPATH:+:$PYTHONPATH}"
|
||||||
|
export PKG_CONFIG_PATH="${GSTREAMER_ROOT}/lib/pkgconfig:${GSTREAMER_ROOT}/share/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
|
||||||
|
export PATH="${GSTREAMER_ROOT}/bin${PATH:+:$PATH}:/usr/local/bin:/usr/bin:/bin"
|
||||||
|
export GST_REGISTRY="${HOME}/.gstreamer-0.10/gstreamer-cerbero-registry"
|
||||||
|
export LD_LIBRARY_PATH="${GSTREAMER_ROOT}/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
||||||
|
export CXXFLAGS="-I${GSTREAMER_ROOT}/include ${CXXFLAGS}"
|
||||||
|
export GI_TYPELIB_PATH="${GSTREAMER_ROOT}/lib/girepository-1.0"
|
||||||
|
|
||||||
|
|
||||||
|
$SHELL "$@"
|
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-typefind-1.0.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-typefind-1.0.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-validate-1.0.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-validate-1.0.exe
vendored
Executable file
Binary file not shown.
69
thirdparty/gstreamer/1.0/x86/bin/gst-validate-launcher
vendored
Executable file
69
thirdparty/gstreamer/1.0/x86/bin/gst-validate-launcher
vendored
Executable file
@ -0,0 +1,69 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
#
|
||||||
|
# Copyright (c) 2014,Thibault Saunier <thibault.saunier@collabora.com>
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU Lesser General Public
|
||||||
|
# License as published by the Free Software Foundation; either
|
||||||
|
# version 2.1 of the License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program 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 for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public
|
||||||
|
# License along with this program; if not, write to the
|
||||||
|
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||||
|
# Boston, MA 02110-1301, USA.
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
|
||||||
|
LIBDIR = r'/c/gstreamer/1.0/x86/lib'
|
||||||
|
BUILDDIR = r'@BUILDDIR@'
|
||||||
|
SRCDIR = r'@SRCDIR@'
|
||||||
|
GIT_FIRST_HASH = 'da962d096af9460502843e41b7d25fdece7ff1c2'
|
||||||
|
|
||||||
|
|
||||||
|
def _get_git_first_hash(path):
|
||||||
|
cdir = os.path.abspath(os.curdir)
|
||||||
|
try:
|
||||||
|
os.chdir(path)
|
||||||
|
res = subprocess.check_output(['git', 'rev-list', '--max-parents=0', 'HEAD']).decode().rstrip('\n')
|
||||||
|
except (subprocess.CalledProcessError, OSError):
|
||||||
|
res = ''
|
||||||
|
finally:
|
||||||
|
os.chdir(cdir)
|
||||||
|
|
||||||
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
def _in_devel():
|
||||||
|
root_dir = os.path.abspath(os.path.dirname(os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||||
|
"..", "..", "..")))
|
||||||
|
return _get_git_first_hash(root_dir) == GIT_FIRST_HASH
|
||||||
|
|
||||||
|
|
||||||
|
def _add_gst_launcher_path():
|
||||||
|
f = os.path.abspath(__file__)
|
||||||
|
if _in_devel():
|
||||||
|
print("Running with development path")
|
||||||
|
dir_ = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
root = os.path.split(dir_)[0]
|
||||||
|
elif f.startswith(BUILDDIR):
|
||||||
|
# Make sure to have the configured config.py in the python path
|
||||||
|
sys.path.insert(0, os.path.abspath(os.path.join(BUILDDIR, "..")))
|
||||||
|
root = os.path.abspath(os.path.join(SRCDIR, "../"))
|
||||||
|
else:
|
||||||
|
root = os.path.join(LIBDIR, 'gst-validate-launcher', 'python')
|
||||||
|
|
||||||
|
sys.path.insert(0, root)
|
||||||
|
return os.path.join(root, "launcher")
|
||||||
|
|
||||||
|
|
||||||
|
if "__main__" == __name__:
|
||||||
|
libsdir = _add_gst_launcher_path()
|
||||||
|
from launcher.main import main
|
||||||
|
exit(main(libsdir))
|
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-validate-media-check-1.0.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-validate-media-check-1.0.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-validate-rtsp-server-1.0.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-validate-rtsp-server-1.0.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-validate-transcoding-1.0.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/gst-validate-transcoding-1.0.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/json-glib-format.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/json-glib-format.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/json-glib-validate.exe
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/json-glib-validate.exe
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libFLAC-8.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libFLAC-8.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libSoundTouch-1.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libSoundTouch-1.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/liba52-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/liba52-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libass-9.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libass-9.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libbz2.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libbz2.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libcairo-2.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libcairo-2.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libcairo-gobject-2.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libcairo-gobject-2.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libcairo-script-interpreter-2.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libcairo-script-interpreter-2.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libcharset-1.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libcharset-1.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libcroco-0.6-3.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libcroco-0.6-3.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libcrypto-1_1.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libcrypto-1_1.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libdca-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libdca-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libdv-4.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libdv-4.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libdvdnav-4.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libdvdnav-4.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libdvdread-4.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libdvdread-4.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libexpat-1.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libexpat-1.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libfaad-2.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libfaad-2.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libffi-7.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libffi-7.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libfontconfig-1.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libfontconfig-1.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libfreetype-6.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libfreetype-6.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libfribidi-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libfribidi-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgcc_s_sjlj-1.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgcc_s_sjlj-1.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgdk_pixbuf-2.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgdk_pixbuf-2.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libges-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libges-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgio-2.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgio-2.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libglib-2.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libglib-2.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgmodule-2.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgmodule-2.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgmp-10.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgmp-10.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgnutls-30.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgnutls-30.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgnutlsxx-28.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgnutlsxx-28.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgobject-2.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgobject-2.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgomp-1.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgomp-1.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgraphene-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgraphene-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstadaptivedemux-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstadaptivedemux-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstallocators-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstallocators-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstapp-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstapp-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstaudio-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstaudio-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstbadaudio-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstbadaudio-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstbadvideo-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstbadvideo-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstbase-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstbase-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstbasecamerabinsrc-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstbasecamerabinsrc-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstcodecparsers-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstcodecparsers-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstcontroller-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstcontroller-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstfft-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstfft-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstgl-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstgl-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstinsertbin-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstinsertbin-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstisoff-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstisoff-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstmpegts-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstmpegts-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstnet-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstnet-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstpbutils-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstpbutils-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstphotography-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstphotography-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstplayer-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstplayer-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstreamer-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstreamer-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstriff-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstriff-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstrtp-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstrtp-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstrtsp-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstrtsp-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstrtspserver-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstrtspserver-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstsdp-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgstsdp-1.0-0.dll
vendored
Executable file
Binary file not shown.
BIN
thirdparty/gstreamer/1.0/x86/bin/libgsttag-1.0-0.dll
vendored
Executable file
BIN
thirdparty/gstreamer/1.0/x86/bin/libgsttag-1.0-0.dll
vendored
Executable file
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user