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.
|
||||
|
||||
@ -55,11 +55,13 @@ from the TSK root directory to install the libraries and such in
|
||||
the needed places (i.e. '/usr/local').
|
||||
|
||||
|
||||
3) For 32-bit targets, get GStreamer Setup. GStreamer is used to view video files.
|
||||
You can either download it and install it or manually by unzipping the
|
||||
version that is included in the 'thirdparty/gstreamer' folder. You
|
||||
will need the 'bin' and 'lib/gstreamer-0.10' folders to be in your
|
||||
Windows PATH environment variable.
|
||||
3) For Windows builds, GStreamer must be setup. GStreamer is used to view video
|
||||
files. You can either download it and install it, or you can copy it from the
|
||||
'thirdparty/gstreamer' folder. Both 32-bit (x86) and 64-bit (x86_64) versions
|
||||
are included, so feel free to exclude what you want. You will need to unzip the
|
||||
'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
|
||||
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.SparseBitSet-1.1.jar=release/modules/ext/SparseBitSet-1.1.jar
|
||||
file.reference.commons-validator-1.6.jar=release/modules/ext/commons-validator-1.6.jar
|
||||
file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar
|
||||
javac.source=1.8
|
||||
javac.compilerargs=-Xlint -Xlint:-serial
|
||||
license.file=../LICENSE-2.0.txt
|
||||
|
@ -9,7 +9,6 @@ FXVideoPanel.progressLabel.buffering=Buffering...
|
||||
FXVideoPanel.media.unsupportedFormat=Unsupported Format.
|
||||
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.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.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.
|
||||
|
@ -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
|
||||
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.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.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
|
||||
|
@ -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 AbstractFile lastFile;
|
||||
//UI
|
||||
private final MediaViewVideoPanel videoPanel;
|
||||
private final boolean videoPanelInited;
|
||||
private final MediaPlayerPanel mediaPlayerPanel;
|
||||
private final boolean mediaPlayerPanelInited;
|
||||
private final MediaViewImagePanel imagePanel;
|
||||
private final boolean imagePanelInited;
|
||||
|
||||
private static final String IMAGE_VIEWER_LAYER = "IMAGE"; //NON-NLS
|
||||
private static final String VIDEO_VIEWER_LAYER = "VIDEO"; //NON-NLS
|
||||
private static final String MEDIA_PLAYER_LAYER = "AUDIO_VIDEO"; //NON-NLS
|
||||
|
||||
/**
|
||||
* Creates new form DataContentViewerVideo
|
||||
* Creates a new MediaFileViewer.
|
||||
*/
|
||||
public MediaFileViewer() {
|
||||
|
||||
initComponents();
|
||||
|
||||
// get the right panel for our platform
|
||||
videoPanel = MediaViewVideoPanel.createVideoPanel();
|
||||
videoPanelInited = videoPanel.isInited();
|
||||
mediaPlayerPanel = new MediaPlayerPanel();
|
||||
mediaPlayerPanelInited = mediaPlayerPanel.isInited();
|
||||
|
||||
imagePanel = new MediaViewImagePanel();
|
||||
imagePanelInited = imagePanel.isInited();
|
||||
@ -64,9 +64,9 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
|
||||
private void customizeComponents() {
|
||||
add(imagePanel, IMAGE_VIEWER_LAYER);
|
||||
add(videoPanel, VIDEO_VIEWER_LAYER);
|
||||
add(mediaPlayerPanel, MEDIA_PLAYER_LAYER);
|
||||
|
||||
showVideoPanel(false);
|
||||
showImagePanel();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -94,8 +94,8 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
|
||||
List<String> mimeTypes = new ArrayList<>();
|
||||
|
||||
mimeTypes.addAll(this.imagePanel.getMimeTypes());
|
||||
mimeTypes.addAll(this.videoPanel.getMimeTypes());
|
||||
mimeTypes.addAll(this.imagePanel.getSupportedMimeTypes());
|
||||
mimeTypes.addAll(this.mediaPlayerPanel.getSupportedMimeTypes());
|
||||
|
||||
return mimeTypes;
|
||||
}
|
||||
@ -123,12 +123,12 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
|
||||
final Dimension dims = MediaFileViewer.this.getSize();
|
||||
//logger.info("setting node on media viewer"); //NON-NLS
|
||||
if (videoPanelInited && videoPanel.isSupported(file)) {
|
||||
videoPanel.setupVideo(file, dims);
|
||||
this.showVideoPanel(true);
|
||||
if (mediaPlayerPanelInited && mediaPlayerPanel.isSupported(file)) {
|
||||
mediaPlayerPanel.loadFile(file, dims);
|
||||
this.showVideoPanel();
|
||||
} else if (imagePanelInited && imagePanel.isSupported(file)) {
|
||||
imagePanel.showImageFx(file, dims);
|
||||
this.showVideoPanel(false);
|
||||
this.showImagePanel();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOGGER.log(Level.SEVERE, "Exception while setting node", e); //NON-NLS
|
||||
@ -136,17 +136,19 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
}
|
||||
|
||||
/**
|
||||
* switch to visible video or image panel
|
||||
*
|
||||
* @param showVideo true if video panel, false if image panel
|
||||
* Show the media player panel.
|
||||
*/
|
||||
private void showVideoPanel(boolean showVideo) {
|
||||
private void showVideoPanel() {
|
||||
CardLayout layout = (CardLayout) this.getLayout();
|
||||
if (showVideo) {
|
||||
layout.show(this, VIDEO_VIEWER_LAYER);
|
||||
} else {
|
||||
layout.show(this, IMAGE_VIEWER_LAYER);
|
||||
}
|
||||
layout.show(this, MEDIA_PLAYER_LAYER);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the image panel.
|
||||
*/
|
||||
private void showImagePanel() {
|
||||
CardLayout layout = (CardLayout) this.getLayout();
|
||||
layout.show(this, IMAGE_VIEWER_LAYER);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -156,24 +158,27 @@ class MediaFileViewer extends javax.swing.JPanel implements FileTypeViewer {
|
||||
|
||||
@Override
|
||||
public void resetComponent() {
|
||||
videoPanel.reset();
|
||||
mediaPlayerPanel.reset();
|
||||
imagePanel.reset();
|
||||
lastFile = null;
|
||||
}
|
||||
|
||||
interface MediaViewPanel {
|
||||
/**
|
||||
* Panel used to display media content.
|
||||
*/
|
||||
protected interface MediaViewPanel {
|
||||
|
||||
/**
|
||||
* @return supported mime types
|
||||
*/
|
||||
List<String> getMimeTypes();
|
||||
List<String> getSupportedMimeTypes();
|
||||
|
||||
/**
|
||||
* returns supported extensions (each starting with .)
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<String> getExtensionsList();
|
||||
List<String> getSupportedExtensions();
|
||||
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
public List<String> getMimeTypes() {
|
||||
public List<String> getSupportedMimeTypes() {
|
||||
return Collections.unmodifiableList(Lists.newArrayList(supportedMimes));
|
||||
}
|
||||
|
||||
@ -255,7 +255,7 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
public List<String> getExtensionsList() {
|
||||
public List<String> getSupportedExtensions() {
|
||||
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"/>
|
||||
|
||||
<!-- 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 -->
|
||||
<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-core" 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>
|
||||
</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.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.gst1-java-core-0.9.3.jar=release/modules/ext/gst1-java-core-0.9.3.jar
|
||||
file.reference.jna-3.4.0.jar=release/modules/ext/jna-3.4.0.jar
|
||||
file.reference.guava-19.0.jar=release/modules/ext/guava-19.0.jar
|
||||
file.reference.imageio-bmp-3.2.jar=release/modules/ext/imageio-bmp-3.2.jar
|
||||
file.reference.imageio-core-3.2.jar=release/modules/ext/imageio-core-3.2.jar
|
||||
|
@ -587,24 +587,24 @@
|
||||
<package>org.dom4j.util</package>
|
||||
<package>org.dom4j.xpath</package>
|
||||
<package>org.dom4j.xpp</package>
|
||||
<package>org.gstreamer</package>
|
||||
<package>org.gstreamer.controller</package>
|
||||
<package>org.gstreamer.elements</package>
|
||||
<package>org.gstreamer.elements.good</package>
|
||||
<package>org.gstreamer.event</package>
|
||||
<package>org.gstreamer.example</package>
|
||||
<package>org.gstreamer.glib</package>
|
||||
<package>org.gstreamer.interfaces</package>
|
||||
<package>org.gstreamer.io</package>
|
||||
<package>org.gstreamer.lowlevel</package>
|
||||
<package>org.gstreamer.lowlevel.annotations</package>
|
||||
<package>org.gstreamer.media</package>
|
||||
<package>org.gstreamer.media.event</package>
|
||||
<package>org.gstreamer.message</package>
|
||||
<package>org.gstreamer.query</package>
|
||||
<package>org.gstreamer.swing</package>
|
||||
<package>org.gstreamer.swt</package>
|
||||
<package>org.gstreamer.swt.overlay</package>
|
||||
<package>org.freedesktop.gstreamer</package>
|
||||
<package>org.freedesktop.gstreamer.controller</package>
|
||||
<package>org.freedesktop.gstreamer.elements</package>
|
||||
<package>org.freedesktop.gstreamer.elements.good</package>
|
||||
<package>org.freedesktop.gstreamer.event</package>
|
||||
<package>org.freedesktop.gstreamer.example</package>
|
||||
<package>org.freedesktop.gstreamer.glib</package>
|
||||
<package>org.freedesktop.gstreamer.interfaces</package>
|
||||
<package>org.freedesktop.gstreamer.io</package>
|
||||
<package>org.freedesktop.gstreamer.lowlevel</package>
|
||||
<package>org.freedesktop.gstreamer.lowlevel.annotations</package>
|
||||
<package>org.freedesktop.gstreamer.media</package>
|
||||
<package>org.freedesktop.gstreamer.media.event</package>
|
||||
<package>org.freedesktop.gstreamer.message</package>
|
||||
<package>org.freedesktop.gstreamer.query</package>
|
||||
<package>org.freedesktop.gstreamer.swing</package>
|
||||
<package>org.freedesktop.gstreamer.swt</package>
|
||||
<package>org.freedesktop.gstreamer.swt.overlay</package>
|
||||
<package>org.hyperic.jni</package>
|
||||
<package>org.hyperic.sigar</package>
|
||||
<package>org.hyperic.sigar.cmd</package>
|
||||
@ -967,8 +967,8 @@
|
||||
<binary-origin>release/modules/ext/common-lang-3.2.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/gstreamer-java-1.5.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/gstreamer-java-1.5.jar</binary-origin>
|
||||
<runtime-relative-path>ext/gst1-java-core-0.9.3.jar</runtime-relative-path>
|
||||
<binary-origin>release/modules/ext/gst1-java-core-0.9.3.jar</binary-origin>
|
||||
</class-path-extension>
|
||||
<class-path-extension>
|
||||
<runtime-relative-path>ext/dom4j-1.6.1.jar</runtime-relative-path>
|
||||
|
@ -21,10 +21,10 @@
|
||||
<if>
|
||||
<isset property="jre.home.32" />
|
||||
<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>
|
||||
<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>
|
||||
</if>
|
||||
|
||||
@ -34,10 +34,10 @@
|
||||
<if>
|
||||
<isset property="jre.home.64" />
|
||||
<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>
|
||||
<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>
|
||||
</if>
|
||||
</target>
|
||||
@ -118,19 +118,15 @@
|
||||
<arg line="/edit ${aip-path-base} /SetAppdir -buildname DefaultBuild -path [ProgramFilesFolder][ProductName]-${app.version}"/>
|
||||
</exec>
|
||||
|
||||
<!-- gstreamer needs special path info to be set -->
|
||||
<exec executable="${ai-exe-path}">
|
||||
<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>
|
||||
<var name="gstreamer-relative-path" value="gstreamer/1.0/x86"/>
|
||||
<antcall target="install-gstreamer" 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"/>
|
||||
<move file="${nbdist.dir}/installer_${app.name}_32-SetupFiles/installer_${app.name}_32.msi" tofile="${nbdist.dir}/${app.name}-${app.version}-32bit.msi" />
|
||||
</target>
|
||||
@ -158,13 +154,47 @@
|
||||
<arg line="/edit ${aip-path} /SetPackageType x64"/>
|
||||
</exec>
|
||||
|
||||
<var name="gstreamer-relative-path" value="gstreamer/1.0/x86_64"/>
|
||||
<antcall target="install-gstreamer" 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"/>
|
||||
<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 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 -->
|
||||
<target name="update-config" description="Updates configuration file with correct JVM args.">
|
||||
<!-- Update configuration file to include jre -->
|
||||
@ -194,7 +224,7 @@
|
||||
</path>
|
||||
</foreach>
|
||||
|
||||
<echo message="Removing extra executable..."/>
|
||||
<echo message="Removing extra Autopsy executable..."/>
|
||||
<exec executable="${ai-exe-path}">
|
||||
<arg line="/edit ${aip-path} /DelFile APPDIR\bin\${aut-bin-name-todelete}"/>
|
||||
</exec>
|
||||
|
@ -102,7 +102,6 @@
|
||||
<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}/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"/>
|
||||
|
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