Updated vlc video component use and fixed bugs

This commit is contained in:
Jeff Wallace 2013-07-31 15:59:02 -04:00
parent fce1446bbc
commit a18fdc33e7
2 changed files with 399 additions and 344 deletions

View File

@ -1,256 +1,259 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2011-2013 Basis Technology Corp. * Copyright 2011-2013 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.sleuthkit.autopsy.corecomponents; package org.sleuthkit.autopsy.corecomponents;
import java.awt.CardLayout; import java.awt.CardLayout;
import java.awt.Component; import java.awt.Component;
import java.awt.Dimension; import java.awt.Dimension;
import java.util.Arrays; import java.util.Arrays;
import java.util.logging.Level; import java.util.logging.Level;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.Exceptions; import org.openide.util.Exceptions;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders; import org.openide.util.lookup.ServiceProviders;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
/** /**
* Media content viewer for videos, sounds and images. * Media content viewer for videos, sounds and images.
*/ */
@ServiceProviders(value = { @ServiceProviders(value = {
@ServiceProvider(service = DataContentViewer.class, position = 5) @ServiceProvider(service = DataContentViewer.class, position = 5)
}) })
public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer { public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer {
private String[] IMAGES; // use javafx supported private String[] IMAGES; // use javafx supported
private static final String[] VIDEOS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; private static final String[] VIDEOS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"};
private static final String[] AUDIOS = new String[]{".mp3", ".wav", ".wma"}; private static final String[] AUDIOS = new String[]{".mp3", ".wav", ".wma"};
private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName()); private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName());
private AbstractFile lastFile; private AbstractFile lastFile;
//UI //UI
private final MediaViewVideoPanel videoPanel; private final MediaViewVideoPanel videoPanel;
private final MediaViewImagePanel imagePanel; private final MediaViewImagePanel imagePanel;
private boolean videoPanelInited; private boolean videoPanelInited;
private boolean imagePanelInited; private boolean imagePanelInited;
private static final String IMAGE_VIEWER_LAYER = "IMAGE"; private static final String IMAGE_VIEWER_LAYER = "IMAGE";
private static final String VIDEO_VIEWER_LAYER = "VIDEO"; private static final String VIDEO_VIEWER_LAYER = "VIDEO";
/** /**
* Creates new form DataContentViewerVideo * Creates new form DataContentViewerVideo
*/ */
public DataContentViewerMedia() { public DataContentViewerMedia() {
initComponents(); initComponents();
videoPanel = new MediaViewVideoPanel(); videoPanel = new MediaViewVideoPanel();
imagePanel = new MediaViewImagePanel(); imagePanel = new MediaViewImagePanel();
videoPanelInited = videoPanel.isInited(); videoPanelInited = videoPanel.isInited();
imagePanelInited = imagePanel.isInited(); imagePanelInited = imagePanel.isInited();
customizeComponents(); customizeComponents();
logger.log(Level.INFO, "Created MediaView instance: " + this); logger.log(Level.INFO, "Created MediaView instance: " + this);
} }
private void customizeComponents() { private void customizeComponents() {
logger.log(Level.INFO, "Supported image formats by javafx image viewer: "); logger.log(Level.INFO, "Supported image formats by javafx image viewer: ");
//initialize supported image types //initialize supported image types
//TODO use mime-types instead once we have support //TODO use mime-types instead once we have support
String[] fxSupportedImagesSuffixes = ImageIO.getReaderFileSuffixes(); String[] fxSupportedImagesSuffixes = ImageIO.getReaderFileSuffixes();
IMAGES = new String[fxSupportedImagesSuffixes.length]; IMAGES = new String[fxSupportedImagesSuffixes.length];
for (int i = 0; i < fxSupportedImagesSuffixes.length; ++i) { for (int i = 0; i < fxSupportedImagesSuffixes.length; ++i) {
String suffix = fxSupportedImagesSuffixes[i]; String suffix = fxSupportedImagesSuffixes[i];
logger.log(Level.INFO, "suffix: " + suffix); logger.log(Level.INFO, "suffix: " + suffix);
IMAGES[i] = "." + suffix; IMAGES[i] = "." + suffix;
} }
add(imagePanel, IMAGE_VIEWER_LAYER); add(imagePanel, IMAGE_VIEWER_LAYER);
add(videoPanel, VIDEO_VIEWER_LAYER); add(videoPanel, VIDEO_VIEWER_LAYER);
switchPanels(false); switchPanels(false);
} }
/** /**
* This method is called from within the constructor to initialize the form. * 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 * WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor. * regenerated by the Form Editor.
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() { private void initComponents() {
setLayout(new java.awt.CardLayout()); setLayout(new java.awt.CardLayout());
getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(DataContentViewerMedia.class, "DataContentViewerMedia.AccessibleContext.accessibleDescription")); // NOI18N getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(DataContentViewerMedia.class, "DataContentViewerMedia.AccessibleContext.accessibleDescription")); // NOI18N
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables // End of variables declaration//GEN-END:variables
@Override @Override
public void setNode(Node selectedNode) { public void setNode(Node selectedNode) {
try {
if (selectedNode == null) { if (selectedNode == null) {
videoPanel.reset(); videoPanel.reset();
return; return;
} }
AbstractFile file = selectedNode.getLookup().lookup(AbstractFile.class); AbstractFile file = selectedNode.getLookup().lookup(AbstractFile.class);
if (file == null) { if (file == null) {
return; return;
} }
if (file.equals(lastFile)) { if (file.equals(lastFile)) {
return; //prevent from loading twice if setNode() called mult. times return; //prevent from loading twice if setNode() called mult. times
} else { } else {
lastFile = file; lastFile = file;
} }
videoPanel.reset(); videoPanel.reset();
final Dimension dims = DataContentViewerMedia.this.getSize(); final Dimension dims = DataContentViewerMedia.this.getSize();
if (imagePanelInited && containsExt(file.getName(), IMAGES)) { if (imagePanelInited && containsExt(file.getName(), IMAGES)) {
imagePanel.showImageFx(file, dims); imagePanel.showImageFx(file, dims);
this.switchPanels(false); this.switchPanels(false);
} else if (videoPanelInited } else if (videoPanelInited
&& (containsExt(file.getName(), VIDEOS) || containsExt(file.getName(), AUDIOS))) { && (containsExt(file.getName(), VIDEOS) || containsExt(file.getName(), AUDIOS))) {
videoPanel.setupVideo(file, dims); videoPanel.setupVideo(file, dims);
switchPanels(true); switchPanels(true);
} }
} } catch (Exception e) {
logger.log(Level.SEVERE, "Exception while setting node", e);
/** }
* switch to visible video or image panel }
*
* @param showVideo true if video panel, false if image panel /**
*/ * switch to visible video or image panel
private void switchPanels(boolean showVideo) { *
CardLayout layout = (CardLayout)this.getLayout(); * @param showVideo true if video panel, false if image panel
if (showVideo) { */
layout.show(this, VIDEO_VIEWER_LAYER); private void switchPanels(boolean showVideo) {
} else { CardLayout layout = (CardLayout)this.getLayout();
layout.show(this, IMAGE_VIEWER_LAYER); if (showVideo) {
} layout.show(this, VIDEO_VIEWER_LAYER);
} } else {
layout.show(this, IMAGE_VIEWER_LAYER);
@Override }
public String getTitle() { }
return "Media View";
} @Override
public String getTitle() {
@Override return "Media View";
public String getToolTip() { }
return "Displays supported multimedia files (images, videos, audio)";
} @Override
public String getToolTip() {
@Override return "Displays supported multimedia files (images, videos, audio)";
public DataContentViewer createInstance() { }
return new DataContentViewerMedia();
} @Override
public DataContentViewer createInstance() {
@Override return new DataContentViewerMedia();
public Component getComponent() { }
return this;
} @Override
public Component getComponent() {
@Override return this;
public void resetComponent() { }
// we don't want this to do anything
// because we already reset on each selected node @Override
} public void resetComponent() {
// we don't want this to do anything
// because we already reset on each selected node
}
@Override
public boolean isSupported(Node node) {
if (node == null) { @Override
return false; public boolean isSupported(Node node) {
}
if (node == null) {
AbstractFile file = node.getLookup().lookup(AbstractFile.class); return false;
if (file == null) { }
return false;
} AbstractFile file = node.getLookup().lookup(AbstractFile.class);
if (file == null) {
return false;
if (file.getSize() == 0) { }
return false;
}
if (file.getSize() == 0) {
String name = file.getName().toLowerCase(); return false;
}
if (imagePanelInited && containsExt(name, IMAGES)) {
return true; String name = file.getName().toLowerCase();
} //for gstreamer formats, check if initialized first, then
//support audio formats, and video formats if (imagePanelInited && containsExt(name, IMAGES)) {
else if (videoPanelInited && videoPanel.isInited() return true;
&& (containsExt(name, AUDIOS) } //for gstreamer formats, check if initialized first, then
|| (containsExt(name, VIDEOS)))) { //support audio formats, and video formats
return true; else if (videoPanelInited && videoPanel.isInited()
} && (containsExt(name, AUDIOS)
|| (containsExt(name, VIDEOS)))) {
return false; return true;
} }
@Override return false;
public int isPreferred(Node node, boolean isSupported) { }
if (isSupported) {
//special case, check if deleted video, then do not make it preferred @Override
AbstractFile file = node.getLookup().lookup(AbstractFile.class); public int isPreferred(Node node, boolean isSupported) {
if (file == null) { if (isSupported) {
return 0; //special case, check if deleted video, then do not make it preferred
} AbstractFile file = node.getLookup().lookup(AbstractFile.class);
String name = file.getName().toLowerCase(); if (file == null) {
return 0;
boolean deleted = file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC); }
if (containsExt(name, VIDEOS) && deleted) { String name = file.getName().toLowerCase();
return 0;
} else { boolean deleted = file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC);
return 7; if (containsExt(name, VIDEOS) && deleted) {
} return 0;
} else { } else {
return 0; return 7;
} }
} } else {
return 0;
private static boolean containsExt(String name, String[] exts) { }
int extStart = name.lastIndexOf("."); }
String ext = "";
if (extStart != -1) { private static boolean containsExt(String name, String[] exts) {
ext = name.substring(extStart, name.length()).toLowerCase(); int extStart = name.lastIndexOf(".");
} String ext = "";
return Arrays.asList(exts).contains(ext); if (extStart != -1) {
} ext = name.substring(extStart, name.length()).toLowerCase();
} }
return Arrays.asList(exts).contains(ext);
}
}

View File

@ -38,8 +38,6 @@ 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.elements.PlayBin2;
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;
@ -68,23 +66,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 gstInited;
private boolean vlcInited; private boolean vlcInited;
private static final long MIN_FRAME_INTERVAL_MILLIS = 500; private static final long MIN_FRAME_INTERVAL_MILLIS = 500;
private static final long FRAME_CAPTURE_TIMEOUT_MILLIS = 1000; 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;
//playback //playback
private long durationMillis = 0; private long durationMillis = 0;
private VideoProgressWorker videoProgressWorker; private VideoProgressWorker videoProgressWorker;
private int totalHours, totalMinutes, totalSeconds; private int totalHours, totalMinutes, totalSeconds;
private volatile PlayBin2 gstPlaybin2;
private MediaPlayer vlcMediaPlayer; private MediaPlayer vlcMediaPlayer;
private VideoComponent gstVideoComponent;
private EmbeddedMediaPlayerComponent vlcVideoComponent; private EmbeddedMediaPlayerComponent vlcVideoComponent;
private boolean autoTracking = false; // true if the slider is moving automatically 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 boolean isFileLoaded; private java.io.File currentVideoFile;
private boolean replay;
private Set<String> badVideoFiles = Collections.synchronizedSet(new HashSet<String>()); private Set<String> badVideoFiles = Collections.synchronizedSet(new HashSet<String>());
/** /**
@ -112,12 +108,10 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
} }
public EmbeddedMediaPlayerComponent getVideoComponent() { public EmbeddedMediaPlayerComponent getVideoComponent() {
// return gstVideoComponent;
return vlcVideoComponent; return vlcVideoComponent;
} }
public boolean isInited() { public boolean isInited() {
// return gstInited;
return vlcInited; return vlcInited;
} }
@ -137,14 +131,15 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
*/ */
@Override @Override
public void stateChanged(ChangeEvent e) { public void stateChanged(ChangeEvent e) {
int time = progressSlider.getValue(); if (vlcMediaPlayer != null && !autoTracking) {
if (vlcMediaPlayer != null && !autoTracking) { float positionValue = progressSlider.getValue() / (float) POS_FACTOR;
boolean wasPlaying = !vlcMediaPlayer.isPlaying(); // Avoid end of file freeze-up
vlcMediaPlayer.setPause(true); if (positionValue > 0.99f) {
vlcMediaPlayer.setTime(time); positionValue = 0.99f;
vlcMediaPlayer.setPause(wasPlaying);
} }
vlcMediaPlayer.setPosition(positionValue);
} }
}
}); });
} }
@ -161,7 +156,28 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
pauseButton.setText("||"); 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;
}
logger.log(Level.INFO, "Resetting media");
replay = true;
}
/**
* Load the VLC library dll using JNA.
*
* @return <code>true</code>, if the library was loaded correctly.
* <code>false</code>, otherwise.
*/
private boolean initVlc() { private boolean initVlc() {
try { try {
Native.loadLibrary(RuntimeUtil.getLibVlcLibraryName(), LibVlc.class); Native.loadLibrary(RuntimeUtil.getLibVlcLibraryName(), LibVlc.class);
@ -184,7 +200,7 @@ 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;
isFileLoaded = false; 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.");
@ -204,8 +220,6 @@ 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);
java.io.File ioFile = getJFile(file);
vlcVideoComponent = new EmbeddedMediaPlayerComponent(); vlcVideoComponent = new EmbeddedMediaPlayerComponent();
vlcMediaPlayer = vlcVideoComponent.getMediaPlayer(); vlcMediaPlayer = vlcVideoComponent.getMediaPlayer();
@ -216,6 +230,9 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
videoPanel.add(vlcVideoComponent); videoPanel.add(vlcVideoComponent);
videoPanel.setVisible(true); videoPanel.setVisible(true);
logger.log(Level.INFO, "Created media player."); logger.log(Level.INFO, "Created media player.");
ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile));
em.execute();
} }
void reset() { void reset() {
@ -230,7 +247,13 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
if (!isInited()) { if (!isInited()) {
return; return;
} }
// get rid of any existing videoProgressWorker thread
if (videoProgressWorker != null) {
videoProgressWorker.cancel(true);
videoProgressWorker = null;
}
if (vlcMediaPlayer != null) { if (vlcMediaPlayer != null) {
if (vlcMediaPlayer.isPlaying()) { if (vlcMediaPlayer.isPlaying()) {
vlcMediaPlayer.stop(); vlcMediaPlayer.stop();
@ -245,13 +268,8 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
logger.log(Level.INFO, "Released video component"); logger.log(Level.INFO, "Released video component");
} }
// get rid of any existing videoProgressWorker thread
if (videoProgressWorker != null) {
videoProgressWorker.cancel(true);
videoProgressWorker = null;
}
currentFile = null; currentFile = null;
currentVideoFile = null;
} }
private java.io.File getJFile(AbstractFile file) { private java.io.File getJFile(AbstractFile file) {
@ -268,6 +286,45 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
java.io.File tempFile = new java.io.File(tempPath); java.io.File tempFile = new java.io.File(tempPath);
return tempFile; return tempFile;
} }
void playMedia() {
logger.log(Level.INFO, "In play media");
if (currentVideoFile == null || !currentVideoFile.exists()) {
progressLabel.setText("Error buffering file");
return;
}
boolean mediaPrepared = vlcMediaPlayer.prepareMedia(currentVideoFile.getAbsolutePath());
if (mediaPrepared) {
vlcMediaPlayer.parseMedia();
durationMillis = vlcMediaPlayer.getMediaMeta().getLength();
logger.log(Level.INFO, "Media loaded correctly");
} else {
progressLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
// 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(new Runnable() {
@Override
public void run() {
progressSlider.setMaximum(POS_FACTOR);
progressSlider.setMinimum(0);
logger.log(Level.INFO, "Starting the media...");
vlcMediaPlayer.start();
pauseButton.setText("||");
videoProgressWorker = new VideoProgressWorker();
videoProgressWorker.execute();
}
});
}
/** /**
* @param file a video file from which to capture frames * @param file a video file from which to capture frames
@ -359,6 +416,8 @@ 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
@ -407,15 +466,16 @@ 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 (!isFileLoaded) { if (replay) {
// First time play button is pressed // File has completed playing. Play button now replays
logger.log(Level.INFO, "Loading media file"); logger.log(Level.INFO, "Replaying video.");
ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile)); replay = false;
em.execute(); playMedia();
em.getExtractedBytes();
} else if (vlcMediaPlayer.isPlaying()) { } else if (vlcMediaPlayer.isPlaying()) {
logger.log(Level.INFO, "Pausing.");
this.pause(); this.pause();
} else { } else {
logger.log(Level.INFO, "Playing");
this.unPause(); this.unPause();
} }
}//GEN-LAST:event_pauseButtonActionPerformed }//GEN-LAST:event_pauseButtonActionPerformed
@ -428,10 +488,23 @@ 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
void releaseVlcComponents() {
if (vlcMediaPlayer != null) {
vlcMediaPlayer.release();
vlcMediaPlayer = null;
}
if (vlcVideoComponent != null) {
vlcVideoComponent.release();
vlcVideoComponent = null;
}
}
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 INTER_FRAME_PERIOD_MS = 20;
private final long END_TIME_MARGIN_MS = 50; private final long END_TIME_MARGIN_MS = 50;
private boolean hadError = false; private boolean hadError = false;
@ -441,28 +514,23 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
} }
private void resetVideo() throws Exception { private void resetVideo() throws Exception {
logger.log(Level.INFO, "Resetting video.");
if (vlcMediaPlayer != null) {
vlcMediaPlayer.stop();
}
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 // * @return true while millisElapsed is greater than END_TIME_MARGIN_MS
* from durationMillis. This is used to indicate when the video has // * from durationMillis. This is used to indicate when the video has
* ended because for some videos the time elapsed never becomes equal to // * ended because for some videos the time elapsed never becomes equal to
* the reported duration of the video. // * the reported duration of the video.
*/ // */
private boolean hasNotEnded() { // private boolean hasNotEnded() {
boolean ended = (durationMillis - millisElapsed) > END_TIME_MARGIN_MS; // boolean ended = (durationMillis - millisElapsed) > END_TIME_MARGIN_MS;
return ended; // return ended;
} // }
@Override @Override
protected Object doInBackground() throws Exception { protected Object doInBackground() throws Exception {
@ -471,10 +539,11 @@ 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 (hasNotEnded() && isMediaPlayerReady() && !isCancelled()) { while (isMediaPlayerReady() && !isCancelled()) {
millisElapsed = vlcMediaPlayer.getTime(); millisElapsed = vlcMediaPlayer.getTime();
currentPosition = vlcMediaPlayer.getPosition();
// 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;
@ -489,7 +558,7 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
progressLabel.setText(durationStr); progressLabel.setText(durationStr);
autoTracking = true; autoTracking = true;
progressSlider.setValue((int) millisElapsed); progressSlider.setValue((int) (currentPosition * POS_FACTOR));
autoTracking = false; autoTracking = false;
try { try {
@ -562,58 +631,41 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt
logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); logger.log(Level.SEVERE, "Fatal error during media buffering.", ex);
} finally { } finally {
progress.finish(); progress.finish();
isFileLoaded = true;
if (!this.isCancelled()) { if (!this.isCancelled()) {
logger.log(Level.INFO, "Loaded media file"); logger.log(Level.INFO, "Loaded media file");
currentVideoFile = jFile;
playMedia(); playMedia();
} }
} }
} }
void playMedia() {
if (jFile == null || !jFile.exists()) {
progressLabel.setText("Error buffering file");
return;
}
boolean mediaPrepared = vlcMediaPlayer.prepareMedia(jFile.getAbsolutePath());
if (mediaPrepared) {
vlcMediaPlayer.parseMedia();
durationMillis = vlcMediaPlayer.getMediaMeta().getLength();
logger.log(Level.INFO, "Media loaded correctly");
} else {
progressLabel.setText(MEDIA_PLAYER_ERROR_STRING);
}
// 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(new Runnable() {
@Override
public void run() {
progressSlider.setMaximum((int) durationMillis);
progressSlider.setMinimum(0);
logger.log(Level.INFO, "Starting the media...");
vlcMediaPlayer.start();
pauseButton.setText("||");
videoProgressWorker = new VideoProgressWorker();
videoProgressWorker.execute();
}
});
}
} }
private class VlcMediaPlayerEventListener extends MediaPlayerEventAdapter { private class VlcMediaPlayerEventListener extends MediaPlayerEventAdapter {
@Override
public void finished(MediaPlayer mediaPlayer) {
logger.log(Level.INFO, "Media Player finished playing the media");
finished();
}
@Override @Override
public void error(MediaPlayer mediaPlayer) { public void error(MediaPlayer mediaPlayer) {
logger.log(Level.INFO, "an Error occured."); logger.log(Level.INFO, "an Error occured.");
} }
@Override
public void mediaDurationChanged(MediaPlayer mediaPlayer, long newDuration) {
logger.log(Level.INFO, "DURATION CHANGED: " + newDuration);
}
@Override
public void mediaFreed(MediaPlayer mediaPlayer) {
logger.log(Level.INFO, "Media was freed");
}
@Override
public void lengthChanged(MediaPlayer mediaPlayer, long newLength) {
logger.log(Level.INFO, "LENGTH CHANGED: " + newLength);
}
} }
} }