mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-17 10:17:41 +00:00
cleanup GstVideoPanel to bring into closer alignment with FXVideoPanel in preparation for further abstraction
check if file exists before exporting in GstVideoPanel move getTempVideoFile and generateVideoThumbnail to new VideoUtils class
This commit is contained in:
parent
eae987ba5b
commit
c8ac16f713
@ -63,6 +63,7 @@ import org.openide.util.lookup.ServiceProviders;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.core.Installer;
|
||||
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;
|
||||
@ -142,11 +143,10 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
||||
}
|
||||
mediaPane.setInfoLabelText(path);
|
||||
mediaPane.setInfoLabelToolTipText(path);
|
||||
final File tempFile = getTempFile(currentFile);
|
||||
final File tempFile = VideoUtils.getTempVideoFile(currentFile);
|
||||
|
||||
if (tempFile.exists() == false || tempFile.length() < file.getSize()) {
|
||||
ExtractMedia em = new ExtractMedia(currentFile, tempFile);
|
||||
em.execute();
|
||||
new ExtractMedia(currentFile, tempFile).execute();
|
||||
}
|
||||
|
||||
mediaPane.setFit(dims);
|
||||
@ -162,18 +162,6 @@ public class FXVideoPanel extends MediaViewVideoPanel {
|
||||
currentFile = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* a file in the Case's temp folder to write the video content to for
|
||||
* playback.
|
||||
*
|
||||
* @param file
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private java.io.File getTempFile(AbstractFile file) {
|
||||
return Paths.get(Case.getCurrentCase().getTempDirectory(), "videos", file.getId() + "." + file.getNameExtension()).toFile();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013 Basis Technology Corp.
|
||||
* Copyright 2013-15 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.corecomponents;
|
||||
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;
|
||||
@ -41,7 +42,6 @@ import javax.swing.JSlider;
|
||||
import javax.swing.SwingUtilities;
|
||||
import javax.swing.SwingWorker;
|
||||
import javax.swing.event.ChangeEvent;
|
||||
import javax.swing.event.ChangeListener;
|
||||
import org.gstreamer.ClockTime;
|
||||
import org.gstreamer.Gst;
|
||||
import org.gstreamer.GstException;
|
||||
@ -52,23 +52,22 @@ import org.gstreamer.elements.RGBDataSink;
|
||||
import org.gstreamer.swing.VideoComponent;
|
||||
import org.netbeans.api.progress.ProgressHandle;
|
||||
import org.netbeans.api.progress.ProgressHandleFactory;
|
||||
import org.openide.util.Cancellable;
|
||||
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.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)
|
||||
})
|
||||
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
|
||||
|
||||
@ -129,30 +128,27 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
progressSlider.setEnabled(false); // disable slider; enable after user plays vid
|
||||
progressSlider.setValue(0);
|
||||
|
||||
progressSlider.addChangeListener(new ChangeListener() {
|
||||
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
|
||||
*/
|
||||
@Override
|
||||
public void stateChanged(ChangeEvent e) {
|
||||
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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -207,7 +203,7 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
pauseButton.setEnabled(true);
|
||||
progressSlider.setEnabled(true);
|
||||
|
||||
java.io.File ioFile = getJFile(file);
|
||||
java.io.File ioFile = VideoUtils.getTempVideoFile(file);
|
||||
|
||||
gstVideoComponent = new VideoComponent();
|
||||
synchronized (playbinLock) {
|
||||
@ -219,7 +215,6 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
|
||||
videoPanel.removeAll();
|
||||
|
||||
|
||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||
videoPanel.add(gstVideoComponent);
|
||||
|
||||
@ -239,11 +234,8 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
void reset() {
|
||||
|
||||
// reset the progress label text on the event dispatch thread
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
progressLabel.setText("");
|
||||
}
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
progressLabel.setText("");
|
||||
});
|
||||
|
||||
if (!isInited()) {
|
||||
@ -281,27 +273,13 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
currentFile = null;
|
||||
}
|
||||
|
||||
private java.io.File getJFile(AbstractFile file) {
|
||||
// Get the temp folder path of the case
|
||||
String tempPath = Case.getCurrentCase().getTempDirectory();
|
||||
String name = file.getName();
|
||||
int extStart = name.lastIndexOf(".");
|
||||
String ext = "";
|
||||
if (extStart != -1) {
|
||||
ext = name.substring(extStart, name.length()).toLowerCase();
|
||||
}
|
||||
tempPath = tempPath + java.io.File.separator + file.getId() + ext;
|
||||
|
||||
java.io.File tempFile = new java.io.File(tempPath);
|
||||
return tempFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file a video file from which to capture frames
|
||||
* @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.
|
||||
* 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
|
||||
@ -384,7 +362,7 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
}
|
||||
|
||||
// wait for FrameCaptureRGBListener to finish
|
||||
synchronized(lock) {
|
||||
synchronized (lock) {
|
||||
try {
|
||||
lock.wait(FRAME_CAPTURE_TIMEOUT_MILLIS);
|
||||
} catch (InterruptedException e) {
|
||||
@ -460,12 +438,12 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
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.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)
|
||||
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
|
||||
@ -482,48 +460,48 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
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.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())
|
||||
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.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))
|
||||
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>
|
||||
|
||||
@ -557,9 +535,10 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
return;
|
||||
}
|
||||
} else if (state.equals(State.READY)) {
|
||||
ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile));
|
||||
em.execute();
|
||||
em.getExtractedBytes();
|
||||
final File tempVideoFile = VideoUtils.getTempVideoFile(currentFile);
|
||||
if (tempVideoFile.exists() == false || tempVideoFile.length() < currentFile.getSize()) {
|
||||
new ExtractMedia(currentFile, tempVideoFile).execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
}//GEN-LAST:event_pauseButtonActionPerformed
|
||||
@ -575,11 +554,10 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
|
||||
private class VideoProgressWorker extends SwingWorker<Object, Object> {
|
||||
|
||||
private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; //NON-NLS
|
||||
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 hadError = false;
|
||||
|
||||
private boolean isPlayBinReady() {
|
||||
synchronized (playbinLock) {
|
||||
@ -612,9 +590,9 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* 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;
|
||||
@ -626,8 +604,7 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
// enable the slider
|
||||
progressSlider.setEnabled(true);
|
||||
|
||||
int elapsedHours = -1, elapsedMinutes = -1, elapsedSeconds = -1;
|
||||
ClockTime pos = null;
|
||||
ClockTime pos;
|
||||
while (hasNotEnded() && isPlayBinReady() && !isCancelled()) {
|
||||
|
||||
synchronized (playbinLock) {
|
||||
@ -637,11 +614,11 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
|
||||
// pick out the elapsed hours, minutes, seconds
|
||||
long secondsElapsed = millisElapsed / 1000;
|
||||
elapsedHours = (int) secondsElapsed / 3600;
|
||||
int elapsedHours = (int) secondsElapsed / 3600;
|
||||
secondsElapsed -= elapsedHours * 3600;
|
||||
elapsedMinutes = (int) secondsElapsed / 60;
|
||||
int elapsedMinutes = (int) secondsElapsed / 60;
|
||||
secondsElapsed -= elapsedMinutes * 60;
|
||||
elapsedSeconds = (int) secondsElapsed;
|
||||
int elapsedSeconds = (int) secondsElapsed;
|
||||
|
||||
String durationStr = String.format(durationFormat,
|
||||
elapsedHours, elapsedMinutes, elapsedSeconds,
|
||||
@ -667,7 +644,6 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
// see if any exceptions were thrown
|
||||
@ -676,54 +652,38 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
} catch (InterruptedException | ExecutionException ex) {
|
||||
logger.log(Level.WARNING, "Error updating video progress: " + ex.getMessage()); //NON-NLS
|
||||
infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.infoLabel.updateErr",
|
||||
ex.getMessage()));
|
||||
ex.getMessage()));
|
||||
} // catch and ignore if we were cancelled
|
||||
catch (java.util.concurrent.CancellationException ex) {
|
||||
}
|
||||
// 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<Object, Void> {
|
||||
private class ExtractMedia extends SwingWorker<Long, Void> {
|
||||
|
||||
private ProgressHandle progress;
|
||||
boolean success = false;
|
||||
private AbstractFile sFile;
|
||||
private java.io.File jFile;
|
||||
private String duration;
|
||||
private String position;
|
||||
private long extractedBytes;
|
||||
private final AbstractFile sourceFile;
|
||||
private final java.io.File tempFile;
|
||||
|
||||
ExtractMedia(org.sleuthkit.datamodel.AbstractFile sFile, java.io.File jFile) {
|
||||
this.sFile = sFile;
|
||||
this.jFile = jFile;
|
||||
}
|
||||
|
||||
public long getExtractedBytes() {
|
||||
return extractedBytes;
|
||||
ExtractMedia(AbstractFile sFile, java.io.File jFile) {
|
||||
this.sourceFile = sFile;
|
||||
this.tempFile = jFile;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object doInBackground() throws Exception {
|
||||
success = false;
|
||||
progress = ProgressHandleFactory.createHandle(
|
||||
NbBundle.getMessage(GstVideoPanel.class, "GstVideoPanel.ExtractMedia.progress.buffering", sFile.getName()),
|
||||
new Cancellable() {
|
||||
@Override
|
||||
public boolean cancel() {
|
||||
return ExtractMedia.this.cancel(true);
|
||||
}
|
||||
});
|
||||
protected Long doInBackground() throws Exception {
|
||||
|
||||
progress = ProgressHandleFactory.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();
|
||||
progress.switchToDeterminate(100);
|
||||
progress.start(100);
|
||||
try {
|
||||
extractedBytes = ContentUtils.writeToFile(sFile, jFile, progress, this, true);
|
||||
return ContentUtils.writeToFile(sourceFile, tempFile, progress, this, true);
|
||||
} catch (IOException ex) {
|
||||
logger.log(Level.WARNING, "Error buffering file", ex); //NON-NLS
|
||||
return 0L;
|
||||
}
|
||||
success = true;
|
||||
return null;
|
||||
|
||||
}
|
||||
|
||||
/* clean up or start the worker threads */
|
||||
@ -746,11 +706,11 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
}
|
||||
|
||||
void playMedia() {
|
||||
if (jFile == null || !jFile.exists()) {
|
||||
if (tempFile == null || !tempFile.exists()) {
|
||||
progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progressLabel.bufferingErr"));
|
||||
return;
|
||||
}
|
||||
ClockTime dur = null;
|
||||
ClockTime dur;
|
||||
synchronized (playbinLock) {
|
||||
// must play, then pause and get state to get duration.
|
||||
if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
|
||||
@ -766,7 +726,6 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
gstPlaybin2.getState();
|
||||
dur = gstPlaybin2.queryDuration();
|
||||
}
|
||||
duration = dur.toString();
|
||||
durationMillis = dur.toMillis();
|
||||
|
||||
// pick out the total hours, minutes, seconds
|
||||
@ -777,29 +736,26 @@ public class GstVideoPanel extends MediaViewVideoPanel {
|
||||
durationSeconds -= totalMinutes * 60;
|
||||
totalSeconds = (int) durationSeconds;
|
||||
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
progressSlider.setMaximum((int) durationMillis);
|
||||
progressSlider.setMinimum(0);
|
||||
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);
|
||||
}
|
||||
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();
|
||||
}
|
||||
pauseButton.setText("||");
|
||||
videoProgressWorker = new VideoProgressWorker();
|
||||
videoProgressWorker.execute();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] getExtensions() {
|
||||
return EXTENSIONS;
|
||||
return EXTENSIONS.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -37,8 +37,6 @@ import javax.imageio.ImageIO;
|
||||
import org.netbeans.api.progress.ProgressHandle;
|
||||
import org.netbeans.api.progress.ProgressHandleFactory;
|
||||
import org.opencv.core.Core;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.highgui.VideoCapture;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
|
||||
import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
||||
@ -314,80 +312,6 @@ public class ImageUtils {
|
||||
&& ((fileHeaderBuffer[5] & 0xff) == 0x0A) && ((fileHeaderBuffer[6] & 0xff) == 0x1A)
|
||||
&& ((fileHeaderBuffer[7] & 0xff) == 0x0A));
|
||||
}
|
||||
private final static int THUMB_COLUMNS = 3;
|
||||
private final static int THUMB_ROWS = 3;
|
||||
|
||||
private static BufferedImage generateVideoThumbnail(AbstractFile file, int iconSize) {
|
||||
|
||||
final String extension = file.getNameExtension();
|
||||
|
||||
java.io.File tempFile = Paths.get(Case.getCurrentCase().getTempDirectory(), "videos", file.getId() + "." + extension).toFile();
|
||||
|
||||
try {
|
||||
if (tempFile.exists() == false || tempFile.length() < file.getSize()) {
|
||||
copyFileUsingStream(file, tempFile);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
VideoCapture videoFile = new VideoCapture(); // will contain the video
|
||||
|
||||
if (!videoFile.open(tempFile.toString())) {
|
||||
return null;
|
||||
}
|
||||
double fps = videoFile.get(CV_CAP_PROP_FPS); // gets frame per second
|
||||
double totalFrames = videoFile.get(CV_CAP_PROP_FRAME_COUNT); // gets total frames
|
||||
if (fps <= 0 || totalFrames <= 0) {
|
||||
return null;
|
||||
}
|
||||
double milliseconds = 1000 * (totalFrames / fps); //total milliseconds
|
||||
|
||||
double timestamp = Math.min(milliseconds, 500); //default time to check for is 500ms, unless the files is extremely small
|
||||
|
||||
int framkeskip = Double.valueOf(Math.floor((milliseconds - timestamp) / (THUMB_COLUMNS * THUMB_ROWS))).intValue();
|
||||
|
||||
Mat imageMatrix = new Mat();
|
||||
BufferedImage bufferedImage = null;
|
||||
|
||||
for (int x = 0; x < THUMB_COLUMNS; x++) {
|
||||
for (int y = 0; y < THUMB_ROWS; y++) {
|
||||
if (!videoFile.set(CV_CAP_PROP_POS_MSEC, timestamp + x * framkeskip + y * framkeskip * THUMB_COLUMNS)) {
|
||||
break;
|
||||
}
|
||||
//read the frame into the image/matrix
|
||||
if (!videoFile.read(imageMatrix)) {
|
||||
break; //if the image for some reason is bad, return default icon
|
||||
}
|
||||
|
||||
if (bufferedImage == null) {
|
||||
bufferedImage = new BufferedImage(imageMatrix.cols() * THUMB_COLUMNS, imageMatrix.rows() * THUMB_ROWS, BufferedImage.TYPE_3BYTE_BGR);
|
||||
}
|
||||
|
||||
byte[] data = new byte[imageMatrix.rows() * imageMatrix.cols() * (int) (imageMatrix.elemSize())];
|
||||
imageMatrix.get(0, 0, data); //copy the image to data
|
||||
|
||||
//todo: this looks like we are swapping the first and third channels. so we can use BufferedImage.TYPE_3BYTE_BGR
|
||||
if (imageMatrix.channels() == 3) {
|
||||
for (int k = 0; k < data.length; k += 3) {
|
||||
byte temp = data[k];
|
||||
data[k] = data[k + 2];
|
||||
data[k + 2] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
bufferedImage.getRaster().setDataElements(imageMatrix.cols() * x, imageMatrix.rows() * y, imageMatrix.cols(), imageMatrix.rows(), data);
|
||||
}
|
||||
}
|
||||
|
||||
videoFile.release(); // close the file
|
||||
|
||||
return ScalrWrapper.resizeFast(bufferedImage, iconSize);
|
||||
}
|
||||
|
||||
private static final int CV_CAP_PROP_POS_MSEC = 0;
|
||||
private static final int CV_CAP_PROP_FRAME_COUNT = 7;
|
||||
private static final int CV_CAP_PROP_FPS = 5;
|
||||
|
||||
/**
|
||||
* Generate an icon and save it to specified location.
|
||||
@ -405,7 +329,7 @@ public class ImageUtils {
|
||||
try {
|
||||
if (SUPP_VIDEO_EXTENSIONS.contains(extension)) {
|
||||
if (openCVLoaded) {
|
||||
icon = generateVideoThumbnail((AbstractFile) content, iconSize);
|
||||
icon = VideoUtils.generateVideoThumbnail((AbstractFile) content, iconSize);
|
||||
} else {
|
||||
return DEFAULT_ICON;
|
||||
}
|
||||
@ -488,4 +412,7 @@ public class ImageUtils {
|
||||
}
|
||||
progress.finish();
|
||||
}
|
||||
|
||||
private ImageUtils() {
|
||||
}
|
||||
}
|
||||
|
114
Core/src/org/sleuthkit/autopsy/coreutils/VideoUtils.java
Normal file
114
Core/src/org/sleuthkit/autopsy/coreutils/VideoUtils.java
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2015 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.coreutils;
|
||||
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Paths;
|
||||
import org.opencv.core.Mat;
|
||||
import org.opencv.highgui.VideoCapture;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
|
||||
import static org.sleuthkit.autopsy.coreutils.ImageUtils.copyFileUsingStream;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
public class VideoUtils {
|
||||
|
||||
private final static int THUMB_COLUMNS = 3;
|
||||
private final static int THUMB_ROWS = 3;
|
||||
private static final int CV_CAP_PROP_POS_MSEC = 0;
|
||||
private static final int CV_CAP_PROP_FRAME_COUNT = 7;
|
||||
private static final int CV_CAP_PROP_FPS = 5;
|
||||
|
||||
public static File getTempVideoFile(AbstractFile file) {
|
||||
return Paths.get(Case.getCurrentCase().getTempDirectory(), "videos", file.getId() + "." + file.getNameExtension()).toFile();
|
||||
}
|
||||
|
||||
static BufferedImage generateVideoThumbnail(AbstractFile file, int iconSize) {
|
||||
java.io.File tempFile = getTempVideoFile(file);
|
||||
|
||||
try {
|
||||
if (tempFile.exists() == false || tempFile.length() < file.getSize()) {
|
||||
copyFileUsingStream(file, tempFile);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
VideoCapture videoFile = new VideoCapture(); // will contain the video
|
||||
|
||||
if (!videoFile.open(tempFile.toString())) {
|
||||
return null;
|
||||
}
|
||||
double fps = videoFile.get(CV_CAP_PROP_FPS); // gets frame per second
|
||||
double totalFrames = videoFile.get(CV_CAP_PROP_FRAME_COUNT); // gets total frames
|
||||
if (fps <= 0 || totalFrames <= 0) {
|
||||
return null;
|
||||
}
|
||||
double milliseconds = 1000 * (totalFrames / fps); //total milliseconds
|
||||
|
||||
double timestamp = Math.min(milliseconds, 500); //default time to check for is 500ms, unless the files is extremely small
|
||||
|
||||
int framkeskip = Double.valueOf(Math.floor((milliseconds - timestamp) / (THUMB_COLUMNS * THUMB_ROWS))).intValue();
|
||||
|
||||
Mat imageMatrix = new Mat();
|
||||
BufferedImage bufferedImage = null;
|
||||
|
||||
for (int x = 0; x < THUMB_COLUMNS; x++) {
|
||||
for (int y = 0; y < THUMB_ROWS; y++) {
|
||||
if (!videoFile.set(CV_CAP_PROP_POS_MSEC, timestamp + x * framkeskip + y * framkeskip * THUMB_COLUMNS)) {
|
||||
break;
|
||||
}
|
||||
//read the frame into the image/matrix
|
||||
if (!videoFile.read(imageMatrix)) {
|
||||
break; //if the image for some reason is bad, return default icon
|
||||
}
|
||||
|
||||
if (bufferedImage == null) {
|
||||
bufferedImage = new BufferedImage(imageMatrix.cols() * THUMB_COLUMNS, imageMatrix.rows() * THUMB_ROWS, BufferedImage.TYPE_3BYTE_BGR);
|
||||
}
|
||||
|
||||
byte[] data = new byte[imageMatrix.rows() * imageMatrix.cols() * (int) (imageMatrix.elemSize())];
|
||||
imageMatrix.get(0, 0, data); //copy the image to data
|
||||
|
||||
//todo: this looks like we are swapping the first and third channels. so we can use BufferedImage.TYPE_3BYTE_BGR
|
||||
if (imageMatrix.channels() == 3) {
|
||||
for (int k = 0; k < data.length; k += 3) {
|
||||
byte temp = data[k];
|
||||
data[k] = data[k + 2];
|
||||
data[k + 2] = temp;
|
||||
}
|
||||
}
|
||||
|
||||
bufferedImage.getRaster().setDataElements(imageMatrix.cols() * x, imageMatrix.rows() * y, imageMatrix.cols(), imageMatrix.rows(), data);
|
||||
}
|
||||
}
|
||||
|
||||
videoFile.release(); // close the file
|
||||
|
||||
return ScalrWrapper.resizeFast(bufferedImage, iconSize);
|
||||
}
|
||||
|
||||
private VideoUtils() {
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user