mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-19 11:07:43 +00:00
Reverted Video Panel back to vlc.
This commit is contained in:
parent
ed75e9258b
commit
fe8257e1db
@ -18,13 +18,18 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.corecomponents;
|
package org.sleuthkit.autopsy.corecomponents;
|
||||||
|
|
||||||
import com.sun.jna.Native;
|
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
|
import java.awt.Image;
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javax.swing.BoxLayout;
|
import javax.swing.BoxLayout;
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
@ -35,6 +40,14 @@ import javax.swing.SwingUtilities;
|
|||||||
import javax.swing.SwingWorker;
|
import javax.swing.SwingWorker;
|
||||||
import javax.swing.event.ChangeEvent;
|
import javax.swing.event.ChangeEvent;
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
|
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.netbeans.api.progress.ProgressHandle;
|
||||||
import org.netbeans.api.progress.ProgressHandleFactory;
|
import org.netbeans.api.progress.ProgressHandleFactory;
|
||||||
import org.openide.util.Cancellable;
|
import org.openide.util.Cancellable;
|
||||||
@ -47,15 +60,9 @@ import org.sleuthkit.autopsy.datamodel.ContentUtils;
|
|||||||
import org.sleuthkit.datamodel.AbstractFile;
|
import org.sleuthkit.datamodel.AbstractFile;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
import uk.co.caprica.vlcj.binding.LibVlc;
|
|
||||||
import uk.co.caprica.vlcj.component.EmbeddedMediaPlayerComponent;
|
|
||||||
import uk.co.caprica.vlcj.player.MediaPlayer;
|
|
||||||
import uk.co.caprica.vlcj.player.MediaPlayerEventAdapter;
|
|
||||||
import uk.co.caprica.vlcj.player.MediaPlayerFactory;
|
|
||||||
import uk.co.caprica.vlcj.runtime.RuntimeUtil;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Video viewer part of the Media View layered pane.
|
* Video viewer part of the Media View layered pane
|
||||||
*/
|
*/
|
||||||
@ServiceProviders(value = {
|
@ServiceProviders(value = {
|
||||||
@ServiceProvider(service = FrameCapture.class)
|
@ServiceProvider(service = FrameCapture.class)
|
||||||
@ -63,37 +70,21 @@ import uk.co.caprica.vlcj.runtime.RuntimeUtil;
|
|||||||
public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapture {
|
public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapture {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
|
private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
|
||||||
private boolean vlcInited; // Has the vlc library been initalized properly?
|
private boolean gstInited;
|
||||||
private static final long MIN_FRAME_INTERVAL_MILLIS = 500;
|
private static final long MIN_FRAME_INTERVAL_MILLIS = 500;
|
||||||
private static final long INTER_FRAME_PERIOD_MS = 20; // Time between frames.
|
private static final long FRAME_CAPTURE_TIMEOUT_MILLIS = 1000;
|
||||||
private static final String MEDIA_PLAYER_ERROR_STRING = "The media player cannot process this file.";
|
private static final String MEDIA_PLAYER_ERROR_STRING = "The media player cannot process this file.";
|
||||||
private static final int POS_FACTOR = 10000; // The number of ticks on the video progress bar
|
//playback
|
||||||
// Frame Capture Media Player Args
|
|
||||||
private static final String[] VLC_FRAME_CAPTURE_ARGS = {
|
|
||||||
"--intf", "dummy", // no interface
|
|
||||||
"--vout", "dummy", // we don't want video (output)
|
|
||||||
"--no-audio", // we don't want audio (decoding)
|
|
||||||
"--no-video-title-show", // nor the filename displayed
|
|
||||||
"--no-stats", // no stats
|
|
||||||
"--no-sub-autodetect-file", // we don't want subtitles
|
|
||||||
"--no-disable-screensaver", // we don't want interfaces
|
|
||||||
"--no-snapshot-preview" // no blending in dummy vout
|
|
||||||
};
|
|
||||||
// Playback state information
|
|
||||||
private long durationMillis = 0;
|
private long durationMillis = 0;
|
||||||
private int totalHours, totalMinutes, totalSeconds;
|
|
||||||
private boolean autoTracking = false; // true if the slider is moving automatically
|
|
||||||
private boolean replay;
|
|
||||||
// VLC Components
|
|
||||||
private MediaPlayer vlcMediaPlayer;
|
|
||||||
private EmbeddedMediaPlayerComponent vlcVideoComponent;
|
|
||||||
// Worker threads
|
|
||||||
private VideoProgressWorker videoProgressWorker;
|
private VideoProgressWorker videoProgressWorker;
|
||||||
private MediaPlayerThread mediaPlayerThread;
|
private int totalHours, totalMinutes, totalSeconds;
|
||||||
// Current media content representations
|
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 AbstractFile currentFile;
|
||||||
private java.io.File currentVideoFile;
|
private Set<String> badVideoFiles = Collections.synchronizedSet(new HashSet<String>());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates new form MediaViewVideoPanel
|
* Creates new form MediaViewVideoPanel
|
||||||
*/
|
*/
|
||||||
@ -118,82 +109,73 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
return videoPanel;
|
return videoPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public EmbeddedMediaPlayerComponent getVideoComponent() {
|
public VideoComponent getVideoComponent() {
|
||||||
return vlcVideoComponent;
|
return gstVideoComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isInited() {
|
public boolean isInited() {
|
||||||
return vlcInited;
|
return gstInited;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void customizeComponents() {
|
private void customizeComponents() {
|
||||||
if (!initVlc()) {
|
if (!initGst()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
progressSlider.setEnabled(false); // disable slider; enable after user plays vid
|
progressSlider.setEnabled(false); // disable slider; enable after user plays vid
|
||||||
progressSlider.setValue(0);
|
progressSlider.setValue(0);
|
||||||
|
|
||||||
progressSlider.addChangeListener(new ChangeListener() {
|
progressSlider.addChangeListener(new ChangeListener() {
|
||||||
@Override
|
/**
|
||||||
public void stateChanged(ChangeEvent e) {
|
* Should always try to synchronize any call to
|
||||||
if (vlcMediaPlayer != null && !autoTracking) {
|
* progressSlider.setValue() to avoid a different thread
|
||||||
float positionValue = progressSlider.getValue() / (float) POS_FACTOR;
|
* changing playbin while stateChanged() is processing
|
||||||
// Avoid end of file freeze-up
|
*/
|
||||||
if (positionValue > 0.99f) {
|
@Override
|
||||||
positionValue = 0.99f;
|
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.");
|
||||||
|
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gstPlaybin2.seek(ClockTime.fromMillis(time)) == false) {
|
||||||
|
logger.log(Level.WARNING, "Attempt to call PlayBin2.seek() failed.");
|
||||||
|
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gstPlaybin2.setState(orig);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
vlcMediaPlayer.setPosition(positionValue);
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void pause() {
|
|
||||||
if (vlcMediaPlayer != null) {
|
|
||||||
vlcMediaPlayer.setPause(true);
|
|
||||||
pauseButton.setText("►");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void unPause() {
|
|
||||||
if (vlcMediaPlayer != null) {
|
|
||||||
vlcMediaPlayer.setPause(false);
|
|
||||||
pauseButton.setText("||");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the video for replaying the current file.
|
|
||||||
*
|
|
||||||
* Called when finished Event is fired from MediaPlayer.
|
|
||||||
*/
|
|
||||||
private void finished() {
|
|
||||||
if (videoProgressWorker != null) {
|
|
||||||
videoProgressWorker.cancel(true);
|
|
||||||
videoProgressWorker = null;
|
|
||||||
}
|
|
||||||
mediaPlayerThread = new MediaPlayerThread();
|
|
||||||
replay = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private boolean initGst() {
|
||||||
* Load the VLC library dll using JNA.
|
|
||||||
*
|
|
||||||
* @return <code>true</code>, if the library was loaded correctly.
|
|
||||||
* <code>false</code>, otherwise.
|
|
||||||
*/
|
|
||||||
private boolean initVlc() {
|
|
||||||
try {
|
try {
|
||||||
Native.loadLibrary(RuntimeUtil.getLibVlcLibraryName(), LibVlc.class);
|
logger.log(Level.INFO, "Initializing gstreamer for video/audio viewing");
|
||||||
vlcInited = true;
|
Gst.init();
|
||||||
} catch (UnsatisfiedLinkError e) {
|
gstInited = true;
|
||||||
logger.log(Level.SEVERE, "Error initalizing vlc for audio/video viewing and extraction capabilities", e);
|
} catch (GstException e) {
|
||||||
MessageNotifyUtil.Notify.error("Error initializing vlc for audio/video viewing and frame extraction capabilities. "
|
gstInited = false;
|
||||||
+ " Video and audio viewing will be disabled. ", e.getMessage());
|
logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and frame extraction capabilities", e);
|
||||||
vlcInited = false;
|
MessageNotifyUtil.Notify.error("Error initializing gstreamer for audio/video viewing and frame extraction capabilities. "
|
||||||
|
+ " Video and audio viewing will be disabled. ",
|
||||||
|
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);
|
||||||
|
MessageNotifyUtil.Notify.error("Error initializing gstreamer for audio/video viewing frame extraction capabilities. "
|
||||||
|
+ " Video and audio viewing will be disabled. ",
|
||||||
|
e.getMessage());
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return vlcInited;
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -205,7 +187,6 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
void setupVideo(final AbstractFile file, final Dimension dims) {
|
void setupVideo(final AbstractFile file, final Dimension dims) {
|
||||||
infoLabel.setText("");
|
infoLabel.setText("");
|
||||||
currentFile = file;
|
currentFile = file;
|
||||||
replay = false;
|
|
||||||
final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
|
final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
|
||||||
if (deleted) {
|
if (deleted) {
|
||||||
infoLabel.setText("Playback of deleted videos is not supported, use an external player.");
|
infoLabel.setText("Playback of deleted videos is not supported, use an external player.");
|
||||||
@ -214,7 +195,7 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
progressSlider.setEnabled(false);
|
progressSlider.setEnabled(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
String path = "";
|
String path = "";
|
||||||
try {
|
try {
|
||||||
path = file.getUniquePath();
|
path = file.getUniquePath();
|
||||||
@ -225,29 +206,37 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
infoLabel.setToolTipText(path);
|
infoLabel.setToolTipText(path);
|
||||||
pauseButton.setEnabled(true);
|
pauseButton.setEnabled(true);
|
||||||
progressSlider.setEnabled(true);
|
progressSlider.setEnabled(true);
|
||||||
|
|
||||||
// Create and Configure MediaPlayer
|
java.io.File ioFile = getJFile(file);
|
||||||
vlcVideoComponent = new EmbeddedMediaPlayerComponent();
|
|
||||||
vlcMediaPlayer = vlcVideoComponent.getMediaPlayer();
|
gstVideoComponent = new VideoComponent();
|
||||||
vlcMediaPlayer.setPlaySubItems(true);
|
synchronized (playbinLock) {
|
||||||
vlcMediaPlayer.addMediaPlayerEventListener(new VlcMediaPlayerEventListener());
|
if (gstPlaybin2 != null) {
|
||||||
|
gstPlaybin2.dispose();
|
||||||
// Configure VideoPanel
|
}
|
||||||
videoPanel.removeAll();
|
gstPlaybin2 = new PlayBin2("VideoPlayer");
|
||||||
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
gstPlaybin2.setVideoSink(gstVideoComponent.getElement());
|
||||||
videoPanel.add(vlcVideoComponent);
|
|
||||||
videoPanel.setVisible(true);
|
videoPanel.removeAll();
|
||||||
|
|
||||||
// Create Extraction and Playback Threads
|
|
||||||
mediaPlayerThread = new MediaPlayerThread();
|
videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
|
||||||
ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile));
|
videoPanel.add(gstVideoComponent);
|
||||||
em.execute();
|
|
||||||
|
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.");
|
||||||
|
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleanup all threads and VLC components and reset UI.
|
|
||||||
*/
|
|
||||||
void reset() {
|
void reset() {
|
||||||
|
|
||||||
// reset the progress label text on the event dispatch thread
|
// reset the progress label text on the event dispatch thread
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@ -259,10 +248,27 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
if (!isInited()) {
|
if (!isInited()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mediaPlayerThread != null) {
|
synchronized (playbinLock) {
|
||||||
mediaPlayerThread.cancel();
|
if (gstPlaybin2 != null) {
|
||||||
mediaPlayerThread = null;
|
if (gstPlaybin2.isPlaying()) {
|
||||||
|
if (gstPlaybin2.stop() == StateChangeReturn.FAILURE) {
|
||||||
|
logger.log(Level.WARNING, "Attempt to call PlayBin2.stop() failed.");
|
||||||
|
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.");
|
||||||
|
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
|
// get rid of any existing videoProgressWorker thread
|
||||||
@ -271,27 +277,7 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
videoProgressWorker = null;
|
videoProgressWorker = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vlcMediaPlayer != null) {
|
|
||||||
if (vlcMediaPlayer.isPlaying()) {
|
|
||||||
vlcMediaPlayer.stop();
|
|
||||||
}
|
|
||||||
vlcMediaPlayer.release();
|
|
||||||
vlcMediaPlayer = null;
|
|
||||||
}
|
|
||||||
if (vlcVideoComponent != null) {
|
|
||||||
vlcVideoComponent.release(true);
|
|
||||||
vlcVideoComponent = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentFile = null;
|
currentFile = null;
|
||||||
currentVideoFile = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start the media player.
|
|
||||||
*/
|
|
||||||
void playMedia() {
|
|
||||||
SwingUtilities.invokeLater(mediaPlayerThread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private java.io.File getJFile(AbstractFile file) {
|
private java.io.File getJFile(AbstractFile file) {
|
||||||
@ -321,59 +307,134 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
public List<VideoFrame> captureFrames(java.io.File file, int numFrames) throws Exception {
|
public List<VideoFrame> captureFrames(java.io.File file, int numFrames) throws Exception {
|
||||||
|
|
||||||
List<VideoFrame> frames = new ArrayList<>();
|
List<VideoFrame> frames = new ArrayList<>();
|
||||||
|
|
||||||
|
Object lock = new Object();
|
||||||
|
FrameCaptureRGBListener rgbListener = new FrameCaptureRGBListener(lock);
|
||||||
|
|
||||||
if (!isInited()) {
|
if (!isInited()) {
|
||||||
return frames;
|
return frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create Media Player with no video output
|
|
||||||
MediaPlayerFactory mediaPlayerFactory = new MediaPlayerFactory(VLC_FRAME_CAPTURE_ARGS);
|
|
||||||
MediaPlayer mediaPlayer = mediaPlayerFactory.newHeadlessMediaPlayer();
|
|
||||||
boolean mediaPrepared = mediaPlayer.prepareMedia(file.getAbsolutePath());
|
|
||||||
if (!mediaPrepared) {
|
|
||||||
logger.log(Level.WARNING, "Video file was not loaded properly.");
|
|
||||||
return frames;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
// throw exception if this file is known to be problematic
|
||||||
|
if (badVideoFiles.contains(file.getName())) {
|
||||||
|
throw new Exception("Cannot capture frames from this file (" + file.getName() + ").");
|
||||||
|
}
|
||||||
|
|
||||||
|
// set up a PlayBin2 object
|
||||||
|
RGBDataSink videoSink = new RGBDataSink("rgb", rgbListener);
|
||||||
|
PlayBin2 playbin = new PlayBin2("VideoFrameCapture");
|
||||||
|
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("Problem with video file; problem when attempting to play while obtaining duration.");
|
||||||
|
}
|
||||||
|
ret = playbin.pause();
|
||||||
|
if (ret == StateChangeReturn.FAILURE) {
|
||||||
|
// add this file to the set of known bad ones
|
||||||
|
badVideoFiles.add(file.getName());
|
||||||
|
throw new Exception("Problem with video file; problem when attempting to pause while obtaining duration.");
|
||||||
|
}
|
||||||
|
playbin.getState();
|
||||||
|
|
||||||
// get the duration of the video
|
// get the duration of the video
|
||||||
mediaPlayer.parseMedia();
|
TimeUnit unit = TimeUnit.MILLISECONDS;
|
||||||
long myDurationMillis = mediaPlayer.getMediaMeta().getLength();
|
long myDurationMillis = playbin.queryDuration(unit);
|
||||||
if (myDurationMillis <= 0) {
|
if (myDurationMillis <= 0) {
|
||||||
return frames;
|
return frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate the number of frames to capture
|
// calculate the number of frames to capture
|
||||||
int numFramesToGet = numFrames;
|
int numFramesToGet = numFrames;
|
||||||
long frameInterval = (myDurationMillis - INTER_FRAME_PERIOD_MS) / numFrames;
|
long frameInterval = myDurationMillis / numFrames;
|
||||||
if (frameInterval < MIN_FRAME_INTERVAL_MILLIS) {
|
if (frameInterval < MIN_FRAME_INTERVAL_MILLIS) {
|
||||||
numFramesToGet = 1;
|
numFramesToGet = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
mediaPlayer.start();
|
|
||||||
mediaPlayer.setPause(true);
|
|
||||||
|
|
||||||
BufferedImage snapShot;
|
|
||||||
// for each timeStamp, grap a frame
|
// for each timeStamp, grap a frame
|
||||||
for (int i = 0; i < numFramesToGet; ++i) {
|
for (int i = 0; i < numFramesToGet; ++i) {
|
||||||
logger.log(Level.INFO, "Grabbing a frame...");
|
long timeStamp = i * frameInterval;
|
||||||
long timeStamp = i * frameInterval + INTER_FRAME_PERIOD_MS;
|
|
||||||
|
|
||||||
mediaPlayer.setTime(timeStamp);
|
ret = playbin.pause();
|
||||||
mediaPlayer.setPause(true);
|
if (ret == StateChangeReturn.FAILURE) {
|
||||||
|
// add this file to the set of known bad ones
|
||||||
snapShot = mediaPlayer.getSnapshot();
|
badVideoFiles.add(file.getName());
|
||||||
|
throw new Exception("Problem with video file; problem when attempting to pause while capturing a frame.");
|
||||||
if (snapShot == null) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
frames.add(new VideoFrame(snapShot, timeStamp));
|
playbin.getState();
|
||||||
|
|
||||||
|
//System.out.println("Seeking to " + timeStamp + "milliseconds.");
|
||||||
|
if (!playbin.seek(timeStamp, unit)) {
|
||||||
|
logger.log(Level.INFO, "There was a problem seeking to " + timeStamp + " " + unit.name().toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = playbin.play();
|
||||||
|
if (ret == StateChangeReturn.FAILURE) {
|
||||||
|
// add this file to the set of known bad ones
|
||||||
|
badVideoFiles.add(file.getName());
|
||||||
|
throw new Exception("Problem with video file; problem when attempting to play while capturing a frame.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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("Problem with video file; problem when attempting to stop while capturing a frame.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (image == null) {
|
||||||
|
logger.log(Level.WARNING, "There was a problem while trying to capture a frame from file " + file.getName());
|
||||||
|
badVideoFiles.add(file.getName());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
frames.add(new VideoFrame(image, timeStamp));
|
||||||
}
|
}
|
||||||
|
|
||||||
// cleanup media player
|
|
||||||
mediaPlayer.release();
|
|
||||||
mediaPlayerFactory.release();
|
|
||||||
return frames;
|
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.
|
* This method is called from within the constructor to initialize the form.
|
||||||
@ -409,8 +470,6 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
progressSlider.setSnapToTicks(true);
|
|
||||||
|
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.progressLabel.text")); // NOI18N
|
org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.progressLabel.text")); // NOI18N
|
||||||
|
|
||||||
org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.infoLabel.text")); // NOI18N
|
org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(MediaViewVideoPanel.class, "MediaViewVideoPanel.infoLabel.text")); // NOI18N
|
||||||
@ -459,14 +518,39 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
}// </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 (replay) {
|
synchronized (playbinLock) {
|
||||||
// File has completed playing. Play button now replays media.
|
State state = gstPlaybin2.getState();
|
||||||
replay = false;
|
if (state.equals(State.PLAYING)) {
|
||||||
playMedia();
|
if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
|
||||||
} else if (vlcMediaPlayer.isPlaying()) {
|
logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed.");
|
||||||
this.pause();
|
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
||||||
} else {
|
return;
|
||||||
this.unPause();
|
}
|
||||||
|
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.");
|
||||||
|
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.");
|
||||||
|
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.");
|
||||||
|
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (state.equals(State.READY)) {
|
||||||
|
ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile));
|
||||||
|
em.execute();
|
||||||
|
em.getExtractedBytes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}//GEN-LAST:event_pauseButtonActionPerformed
|
}//GEN-LAST:event_pauseButtonActionPerformed
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
@ -478,29 +562,53 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
private javax.swing.JPanel videoPanel;
|
private javax.swing.JPanel videoPanel;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
|
|
||||||
/**
|
|
||||||
* Thread that updates the video progress bar and current time with information
|
|
||||||
* queried from the MediaPlayer.
|
|
||||||
*/
|
|
||||||
private class VideoProgressWorker extends SwingWorker<Object, Object> {
|
private class VideoProgressWorker extends SwingWorker<Object, Object> {
|
||||||
|
|
||||||
private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d ";
|
private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d ";
|
||||||
private long millisElapsed = 0;
|
private long millisElapsed = 0;
|
||||||
private float currentPosition = 0.0f;
|
private final long INTER_FRAME_PERIOD_MS = 20;
|
||||||
|
private final long END_TIME_MARGIN_MS = 50;
|
||||||
|
private boolean hadError = false;
|
||||||
|
|
||||||
private boolean isMediaPlayerReady() {
|
private boolean isPlayBinReady() {
|
||||||
return vlcMediaPlayer != null;
|
synchronized (playbinLock) {
|
||||||
|
return gstPlaybin2 != null && !gstPlaybin2.getState().equals(State.NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Could be moved to finished()
|
|
||||||
private void resetVideo() throws Exception {
|
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.");
|
||||||
|
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.");
|
||||||
|
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
||||||
|
}
|
||||||
|
gstPlaybin2.getState(); //NEW
|
||||||
|
}
|
||||||
|
}
|
||||||
pauseButton.setText("►");
|
pauseButton.setText("►");
|
||||||
|
progressSlider.setValue(0);
|
||||||
|
|
||||||
String durationStr = String.format(durationFormat, 0, 0, 0,
|
String durationStr = String.format(durationFormat, 0, 0, 0,
|
||||||
totalHours, totalMinutes, totalSeconds);
|
totalHours, totalMinutes, totalSeconds);
|
||||||
progressLabel.setText(durationStr);
|
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
|
@Override
|
||||||
protected Object doInBackground() throws Exception {
|
protected Object doInBackground() throws Exception {
|
||||||
|
|
||||||
@ -508,10 +616,14 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
progressSlider.setEnabled(true);
|
progressSlider.setEnabled(true);
|
||||||
|
|
||||||
int elapsedHours = -1, elapsedMinutes = -1, elapsedSeconds = -1;
|
int elapsedHours = -1, elapsedMinutes = -1, elapsedSeconds = -1;
|
||||||
while (isMediaPlayerReady() && !isCancelled()) {
|
ClockTime pos = null;
|
||||||
millisElapsed = vlcMediaPlayer.getTime();
|
while (hasNotEnded() && isPlayBinReady() && !isCancelled()) {
|
||||||
currentPosition = vlcMediaPlayer.getPosition();
|
|
||||||
|
synchronized (playbinLock) {
|
||||||
|
pos = gstPlaybin2.queryPosition();
|
||||||
|
}
|
||||||
|
millisElapsed = pos.toMillis();
|
||||||
|
|
||||||
// pick out the elapsed hours, minutes, seconds
|
// pick out the elapsed hours, minutes, seconds
|
||||||
long secondsElapsed = millisElapsed / 1000;
|
long secondsElapsed = millisElapsed / 1000;
|
||||||
elapsedHours = (int) secondsElapsed / 3600;
|
elapsedHours = (int) secondsElapsed / 3600;
|
||||||
@ -526,7 +638,7 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
|
|
||||||
progressLabel.setText(durationStr);
|
progressLabel.setText(durationStr);
|
||||||
autoTracking = true;
|
autoTracking = true;
|
||||||
progressSlider.setValue((int) (currentPosition * POS_FACTOR));
|
progressSlider.setValue((int) millisElapsed);
|
||||||
autoTracking = false;
|
autoTracking = false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -545,16 +657,15 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
}
|
}
|
||||||
} //end class progress worker
|
} //end class progress worker
|
||||||
|
|
||||||
/**
|
/* Thread that extracts and plays a file */
|
||||||
* 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 SwingWorker<Object, Void> {
|
private class ExtractMedia extends SwingWorker<Object, Void> {
|
||||||
|
|
||||||
private ProgressHandle progress;
|
private ProgressHandle progress;
|
||||||
boolean success = false;
|
boolean success = false;
|
||||||
private AbstractFile sFile;
|
private AbstractFile sFile;
|
||||||
private java.io.File jFile;
|
private java.io.File jFile;
|
||||||
|
private String duration;
|
||||||
|
private String position;
|
||||||
private long extractedBytes;
|
private long extractedBytes;
|
||||||
|
|
||||||
ExtractMedia(org.sleuthkit.datamodel.AbstractFile sFile, java.io.File jFile) {
|
ExtractMedia(org.sleuthkit.datamodel.AbstractFile sFile, java.io.File jFile) {
|
||||||
@ -583,7 +694,6 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.WARNING, "Error buffering file", ex);
|
logger.log(Level.WARNING, "Error buffering file", ex);
|
||||||
}
|
}
|
||||||
logger.log(Level.INFO, "Done buffering: " + jFile.getName());
|
|
||||||
success = true;
|
success = true;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -602,35 +712,34 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
} finally {
|
} finally {
|
||||||
progress.finish();
|
progress.finish();
|
||||||
if (!this.isCancelled()) {
|
if (!this.isCancelled()) {
|
||||||
logger.log(Level.INFO, "ExtractMedia in done: " + jFile.getName());
|
|
||||||
currentVideoFile = jFile;
|
|
||||||
playMedia();
|
playMedia();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thread that is responsible for running the Media Player.
|
|
||||||
*/
|
|
||||||
private class MediaPlayerThread implements Runnable, Cancellable {
|
|
||||||
|
|
||||||
private volatile boolean cancelled = false;
|
void playMedia() {
|
||||||
|
if (jFile == null || !jFile.exists()) {
|
||||||
/* Prepare and run the current media. */
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (currentVideoFile == null || !currentVideoFile.exists()) {
|
|
||||||
progressLabel.setText("Error buffering file");
|
progressLabel.setText("Error buffering file");
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
boolean mediaPrepared = vlcMediaPlayer.prepareMedia(currentVideoFile.getAbsolutePath());
|
|
||||||
if (mediaPrepared) {
|
|
||||||
vlcMediaPlayer.parseMedia();
|
|
||||||
durationMillis = vlcMediaPlayer.getMediaMeta().getLength();
|
|
||||||
} else {
|
|
||||||
progressLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
|
||||||
}
|
}
|
||||||
|
ClockTime dur = null;
|
||||||
|
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.");
|
||||||
|
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
|
||||||
|
logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed.");
|
||||||
|
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gstPlaybin2.getState();
|
||||||
|
dur = gstPlaybin2.queryDuration();
|
||||||
|
}
|
||||||
|
duration = dur.toString();
|
||||||
|
durationMillis = dur.toMillis();
|
||||||
|
|
||||||
// pick out the total hours, minutes, seconds
|
// pick out the total hours, minutes, seconds
|
||||||
long durationSeconds = (int) durationMillis / 1000;
|
long durationSeconds = (int) durationMillis / 1000;
|
||||||
@ -640,45 +749,23 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
|
|||||||
durationSeconds -= totalMinutes * 60;
|
durationSeconds -= totalMinutes * 60;
|
||||||
totalSeconds = (int) durationSeconds;
|
totalSeconds = (int) durationSeconds;
|
||||||
|
|
||||||
progressSlider.setMaximum(POS_FACTOR);
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
progressSlider.setMinimum(0);
|
@Override
|
||||||
if (!isCancelled()) {
|
public void run() {
|
||||||
vlcMediaPlayer.start();
|
progressSlider.setMaximum((int) durationMillis);
|
||||||
pauseButton.setText("||");
|
progressSlider.setMinimum(0);
|
||||||
videoProgressWorker = new VideoProgressWorker();
|
|
||||||
videoProgressWorker.execute();
|
|
||||||
} else {
|
|
||||||
logger.log(Level.INFO, "Media Playing cancelled.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
synchronized (playbinLock) {
|
||||||
public boolean cancel() {
|
if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
|
||||||
cancelled = true;
|
logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed.");
|
||||||
return cancelled;
|
infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
private boolean isCancelled() {
|
pauseButton.setText("||");
|
||||||
return cancelled;
|
videoProgressWorker = new VideoProgressWorker();
|
||||||
}
|
videoProgressWorker.execute();
|
||||||
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
/**
|
|
||||||
* An Object that listens and handles events thrown by the VLC Media Player.
|
|
||||||
*/
|
|
||||||
private class VlcMediaPlayerEventListener extends MediaPlayerEventAdapter {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void finished(MediaPlayer mediaPlayer) {
|
|
||||||
logger.log(Level.INFO, "Media Player finished playing the media");
|
|
||||||
finished();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void error(MediaPlayer mediaPlayer) {
|
|
||||||
logger.log(Level.WARNING, "A VLC error occured. Resetting the video panel.");
|
|
||||||
reset();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user