mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-09 06:39:33 +00:00
TSK-463: Media viewer: add seek, set position, see length in UI
This commit is contained in:
parent
4117ac141b
commit
162dc647d1
@ -19,7 +19,11 @@
|
|||||||
<Component id="videoPanel" alignment="0" max="32767" attributes="0"/>
|
<Component id="videoPanel" alignment="0" max="32767" attributes="0"/>
|
||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<Component id="pauseButton" min="-2" max="-2" attributes="0"/>
|
<Component id="pauseButton" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace min="-2" pref="240" max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="progressSlider" pref="160" max="32767" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Component id="progressLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
@ -28,7 +32,11 @@
|
|||||||
<Group type="102" alignment="1" attributes="0">
|
<Group type="102" alignment="1" attributes="0">
|
||||||
<Component id="videoPanel" max="32767" attributes="0"/>
|
<Component id="videoPanel" max="32767" attributes="0"/>
|
||||||
<EmptySpace max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="pauseButton" min="-2" max="-2" attributes="0"/>
|
<Group type="103" groupAlignment="1" attributes="0">
|
||||||
|
<Component id="pauseButton" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="progressLabel" min="-2" max="-2" attributes="0"/>
|
||||||
|
<Component id="progressSlider" min="-2" max="-2" attributes="0"/>
|
||||||
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
@ -59,5 +67,14 @@
|
|||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
</Layout>
|
</Layout>
|
||||||
</Container>
|
</Container>
|
||||||
|
<Component class="javax.swing.JSlider" name="progressSlider">
|
||||||
|
</Component>
|
||||||
|
<Component class="javax.swing.JLabel" name="progressLabel">
|
||||||
|
<Properties>
|
||||||
|
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||||
|
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataContentViewerMedia.progressLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||||
|
</Property>
|
||||||
|
</Properties>
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -5,20 +5,23 @@
|
|||||||
package org.sleuthkit.autopsy.corecomponents;
|
package org.sleuthkit.autopsy.corecomponents;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.io.BufferedInputStream;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
import org.gstreamer.*;
|
import org.gstreamer.*;
|
||||||
import org.gstreamer.elements.DecodeBin2;
|
import org.gstreamer.elements.PlayBin2;
|
||||||
import org.gstreamer.io.InputStreamSrc;
|
|
||||||
import org.gstreamer.swing.VideoComponent;
|
import org.gstreamer.swing.VideoComponent;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.openide.util.lookup.ServiceProvider;
|
import org.openide.util.lookup.ServiceProvider;
|
||||||
|
import org.sleuthkit.autopsy.casemodule.Case;
|
||||||
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
|
||||||
|
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||||
import org.sleuthkit.datamodel.File;
|
import org.sleuthkit.datamodel.File;
|
||||||
import org.sleuthkit.datamodel.ReadContentInputStream;
|
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,10 +35,10 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
private static final String[] VIDEOS = new String[]{ ".mov", ".m4v", ".flv", ".mp4", ".3gp"};
|
private static final String[] VIDEOS = new String[]{ ".mov", ".m4v", ".flv", ".mp4", ".3gp"};
|
||||||
private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName());
|
private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName());
|
||||||
private VideoComponent videoComponent;
|
private VideoComponent videoComponent;
|
||||||
private Pipeline pipe;
|
private PlayBin2 playbin2;
|
||||||
private Element source = null;
|
private File currentFile;
|
||||||
private DecodeBin2 decodeBin;
|
private long durationMillis = 0;
|
||||||
private Bin audioBin;
|
private boolean autoTracking = false; // true if the slider is moving automatically
|
||||||
/**
|
/**
|
||||||
* Creates new form DataContentViewerVideo
|
* Creates new form DataContentViewerVideo
|
||||||
*/
|
*/
|
||||||
@ -47,6 +50,21 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
private void customizeComponents() {
|
private void customizeComponents() {
|
||||||
Gst.init();
|
Gst.init();
|
||||||
resetVideo();
|
resetVideo();
|
||||||
|
progressSlider.addChangeListener(new ChangeListener() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stateChanged(ChangeEvent e) {
|
||||||
|
int time = progressSlider.getValue();
|
||||||
|
if(playbin2 != null && !autoTracking) {
|
||||||
|
State orig = playbin2.getState();
|
||||||
|
playbin2.pause();
|
||||||
|
playbin2.getState();
|
||||||
|
playbin2.seek(ClockTime.fromMillis(time));
|
||||||
|
playbin2.setState(orig);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,6 +78,8 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
|
|
||||||
pauseButton = new javax.swing.JButton();
|
pauseButton = new javax.swing.JButton();
|
||||||
videoPanel = new javax.swing.JPanel();
|
videoPanel = new javax.swing.JPanel();
|
||||||
|
progressSlider = new javax.swing.JSlider();
|
||||||
|
progressLabel = new javax.swing.JLabel();
|
||||||
|
|
||||||
pauseButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerMedia.class, "DataContentViewerMedia.pauseButton.text")); // NOI18N
|
pauseButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerMedia.class, "DataContentViewerMedia.pauseButton.text")); // NOI18N
|
||||||
pauseButton.addActionListener(new java.awt.event.ActionListener() {
|
pauseButton.addActionListener(new java.awt.event.ActionListener() {
|
||||||
@ -79,6 +99,8 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
.addGap(0, 242, Short.MAX_VALUE)
|
.addGap(0, 242, Short.MAX_VALUE)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
progressLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerMedia.class, "DataContentViewerMedia.progressLabel.text")); // NOI18N
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||||
this.setLayout(layout);
|
this.setLayout(layout);
|
||||||
layout.setHorizontalGroup(
|
layout.setHorizontalGroup(
|
||||||
@ -86,29 +108,95 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
.addComponent(videoPanel, 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)
|
||||||
.addGroup(layout.createSequentialGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addComponent(pauseButton)
|
.addComponent(pauseButton)
|
||||||
.addGap(240, 240, 240))
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 160, Short.MAX_VALUE)
|
||||||
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
|
.addComponent(progressLabel)
|
||||||
|
.addContainerGap())
|
||||||
);
|
);
|
||||||
layout.setVerticalGroup(
|
layout.setVerticalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
|
||||||
.addComponent(videoPanel, 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)
|
||||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||||
.addComponent(pauseButton))
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
|
||||||
|
.addComponent(pauseButton)
|
||||||
|
.addComponent(progressLabel)
|
||||||
|
.addComponent(progressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
|
||||||
);
|
);
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
}// </editor-fold>//GEN-END:initComponents
|
||||||
|
|
||||||
private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed
|
private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed
|
||||||
if(pipe.isPlaying()){
|
if(playbin2.getState().equals(State.PLAYING)){
|
||||||
pipe.pause();
|
playbin2.pause();
|
||||||
pauseButton.setText("►");
|
pauseButton.setText("►");
|
||||||
} else {
|
} else if(playbin2.getState().equals(State.PAUSED)) {
|
||||||
pipe.play();
|
playbin2.play();
|
||||||
pauseButton.setText("||");
|
pauseButton.setText("||");
|
||||||
|
} else {
|
||||||
|
progressLabel.setText("Buffering...");
|
||||||
|
java.io.File ioFile = extractFile(currentFile);
|
||||||
|
if(ioFile == null || !ioFile.exists()) {
|
||||||
|
progressLabel.setText("Error buffering file");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
playbin2.setInputFile(ioFile);
|
||||||
|
playbin2.play(); // must play, then pause and get state to get duration.
|
||||||
|
playbin2.pause();
|
||||||
|
playbin2.getState();
|
||||||
|
String duration = playbin2.queryDuration().toString();
|
||||||
|
durationMillis = playbin2.queryDuration().toMillis();
|
||||||
|
progressSlider.setMaximum((int)durationMillis);
|
||||||
|
progressSlider.setMinimum(0);
|
||||||
|
final String finalDuration;
|
||||||
|
if(duration.length() == 8) {
|
||||||
|
finalDuration = duration.substring(3);
|
||||||
|
progressLabel.setText("00:00/" + duration);
|
||||||
|
} else {
|
||||||
|
finalDuration = duration;
|
||||||
|
progressLabel.setText("00:00:00/" + duration);
|
||||||
|
}
|
||||||
|
playbin2.play();
|
||||||
|
pauseButton.setText("||");
|
||||||
|
new Thread(new Runnable() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
long positionMillis = 0;
|
||||||
|
while (positionMillis < durationMillis
|
||||||
|
&& !playbin2.getState().equals(State.NULL)) {
|
||||||
|
String position = playbin2.queryPosition().toString();
|
||||||
|
positionMillis = playbin2.queryPosition().toMillis();
|
||||||
|
if (position.length() == 8) {
|
||||||
|
position = position.substring(3);
|
||||||
|
}
|
||||||
|
progressLabel.setText(position + "/" + finalDuration);
|
||||||
|
autoTracking = true;
|
||||||
|
progressSlider.setValue((int) positionMillis);
|
||||||
|
autoTracking = false;
|
||||||
|
try {
|
||||||
|
Thread.sleep(20);
|
||||||
|
} catch (InterruptedException ex) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (finalDuration.length() == 5) {
|
||||||
|
progressLabel.setText("00:00/" + finalDuration);
|
||||||
|
} else {
|
||||||
|
progressLabel.setText("00:00:00/" + finalDuration);
|
||||||
|
}
|
||||||
|
playbin2.stop();
|
||||||
|
playbin2.getState();
|
||||||
|
pauseButton.setText("►");
|
||||||
|
progressSlider.setValue(0);
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
}
|
}
|
||||||
}//GEN-LAST:event_pauseButtonActionPerformed
|
}//GEN-LAST:event_pauseButtonActionPerformed
|
||||||
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JButton pauseButton;
|
private javax.swing.JButton pauseButton;
|
||||||
|
private javax.swing.JLabel progressLabel;
|
||||||
|
private javax.swing.JSlider progressSlider;
|
||||||
private javax.swing.JPanel videoPanel;
|
private javax.swing.JPanel videoPanel;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
@ -124,39 +212,20 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
setDataView(file);
|
setDataView(file);
|
||||||
boolean isVid = isVid(file.getName());
|
boolean isVid = isVid(file.getName());
|
||||||
pauseButton.setVisible(isVid);
|
pauseButton.setVisible(isVid);
|
||||||
|
progressLabel.setVisible(isVid);
|
||||||
|
progressSlider.setVisible(isVid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDataView(File file) {
|
private void setDataView(File file) {
|
||||||
if(file == null)
|
if(file == null)
|
||||||
return;
|
return;
|
||||||
InputStream is = new ReadContentInputStream(file);
|
this.currentFile = file;
|
||||||
source = new InputStreamSrc(new BufferedInputStream(is), "input file");
|
|
||||||
pipe.add(source);
|
|
||||||
|
|
||||||
source.link(decodeBin);
|
if(!isVid(file.getName())) {
|
||||||
|
java.io.File ioFile = extractFile(file);
|
||||||
decodeBin.connect(new DecodeBin2.NEW_DECODED_PAD() {
|
playbin2.setInputFile(ioFile);
|
||||||
@Override
|
playbin2.play();
|
||||||
public void newDecodedPad(DecodeBin2 elem, Pad pad, boolean last) {
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* only link once
|
|
||||||
*/
|
|
||||||
if (pad.isLinked()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Caps caps = pad.getCaps();
|
|
||||||
Structure struct = caps.getStructure(0);
|
|
||||||
if (struct.getName().startsWith("audio/")) {
|
|
||||||
pad.link(audioBin.getStaticPad("sink"));
|
|
||||||
} else if (struct.getName().startsWith("video/")) {
|
|
||||||
pad.link(videoComponent.getElement().getStaticPad("sink"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if(!isVid(file.getName()))
|
|
||||||
pipe.play();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -184,28 +253,24 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void resetVideo() {
|
private void resetVideo() {
|
||||||
if(pipe != null) {
|
if(playbin2 != null) {
|
||||||
pipe.stop();
|
playbin2.stop();
|
||||||
pipe.dispose();
|
playbin2.getState();
|
||||||
|
} else {
|
||||||
|
playbin2 = new PlayBin2("VideoPlayer");
|
||||||
|
}
|
||||||
|
if(videoComponent != null && videoComponent.getElement() != null) {
|
||||||
|
videoComponent.getElement().stop();
|
||||||
|
videoComponent.getElement().getState();
|
||||||
|
} else {
|
||||||
|
videoComponent = new VideoComponent();
|
||||||
|
playbin2.setVideoSink(videoComponent.getElement());
|
||||||
}
|
}
|
||||||
pipe = new Pipeline("main pipeline");
|
|
||||||
videoComponent = new VideoComponent();
|
|
||||||
videoPanel.removeAll();
|
videoPanel.removeAll();
|
||||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||||
videoPanel.add(videoComponent);
|
videoPanel.add(videoComponent);
|
||||||
videoPanel.revalidate();
|
videoPanel.revalidate();
|
||||||
videoPanel.repaint();
|
videoPanel.repaint();
|
||||||
decodeBin = (DecodeBin2) ElementFactory.make("decodebin2", "Decode Bin");
|
|
||||||
pipe.add(decodeBin);
|
|
||||||
audioBin = new Bin("Audio Bin");
|
|
||||||
Element conv = ElementFactory.make("audioconvert", "Audio Convert");
|
|
||||||
Element resample = ElementFactory.make("audioresample", "Audio Resample");
|
|
||||||
Element sink = ElementFactory.make("autoaudiosink", "sink");
|
|
||||||
audioBin.addMany(conv, resample, sink);
|
|
||||||
Element.linkMany(conv, resample, sink);
|
|
||||||
audioBin.addPad(new GhostPad("sink", conv.getStaticPad("sink")));
|
|
||||||
pipe.add(audioBin);
|
|
||||||
pipe.add(videoComponent.getElement());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -259,4 +324,23 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
|
|||||||
}
|
}
|
||||||
return Arrays.asList(VIDEOS).contains(ext);
|
return Arrays.asList(VIDEOS).contains(ext);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private java.io.File extractFile(File file) {
|
||||||
|
// Get the temp folder path of the case
|
||||||
|
String tempPath = Case.getCurrentCase().getTempDirectory();
|
||||||
|
tempPath = tempPath + java.io.File.separator + file.getName();
|
||||||
|
|
||||||
|
// create the temporary file
|
||||||
|
java.io.File tempFile = new java.io.File(tempPath);
|
||||||
|
if (tempFile.exists()) {
|
||||||
|
tempFile.delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ContentUtils.writeToFile(file, tempFile);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.INFO, "Error buffering file", ex);
|
||||||
|
}
|
||||||
|
return tempFile;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user