From a18fdc33e7cd836c28f7d0e3e2e3a8090e85df0d Mon Sep 17 00:00:00 2001 From: Jeff Wallace Date: Wed, 31 Jul 2013 15:59:02 -0400 Subject: [PATCH] Updated vlc video component use and fixed bugs --- .../DataContentViewerMedia.java | 515 +++++++++--------- .../corecomponents/MediaViewVideoPanel.java | 228 +++++--- 2 files changed, 399 insertions(+), 344 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java index 1368598dee..1ec9eaeb8b 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java @@ -1,256 +1,259 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2011-2013 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.corecomponents; - -import java.awt.CardLayout; -import java.awt.Component; -import java.awt.Dimension; -import java.util.Arrays; -import java.util.logging.Level; -import javax.imageio.ImageIO; -import org.sleuthkit.autopsy.coreutils.Logger; -import javax.swing.SwingUtilities; -import org.openide.nodes.Node; -import org.openide.util.Exceptions; -import org.openide.util.lookup.ServiceProvider; -import org.openide.util.lookup.ServiceProviders; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; -import org.sleuthkit.datamodel.AbstractFile; -import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; - -/** - * Media content viewer for videos, sounds and images. - */ -@ServiceProviders(value = { - @ServiceProvider(service = DataContentViewer.class, position = 5) -}) -public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer { - - 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[] AUDIOS = new String[]{".mp3", ".wav", ".wma"}; - - private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName()); - - private AbstractFile lastFile; - //UI - private final MediaViewVideoPanel videoPanel; - private final MediaViewImagePanel imagePanel; - private boolean videoPanelInited; - private boolean imagePanelInited; - - private static final String IMAGE_VIEWER_LAYER = "IMAGE"; - private static final String VIDEO_VIEWER_LAYER = "VIDEO"; - - /** - * Creates new form DataContentViewerVideo - */ - public DataContentViewerMedia() { - - initComponents(); - - videoPanel = new MediaViewVideoPanel(); - imagePanel = new MediaViewImagePanel(); - videoPanelInited = videoPanel.isInited(); - imagePanelInited = imagePanel.isInited(); - - customizeComponents(); - logger.log(Level.INFO, "Created MediaView instance: " + this); - } - - private void customizeComponents() { - logger.log(Level.INFO, "Supported image formats by javafx image viewer: "); - //initialize supported image types - //TODO use mime-types instead once we have support - String[] fxSupportedImagesSuffixes = ImageIO.getReaderFileSuffixes(); - IMAGES = new String[fxSupportedImagesSuffixes.length]; - for (int i = 0; i < fxSupportedImagesSuffixes.length; ++i) { - String suffix = fxSupportedImagesSuffixes[i]; - logger.log(Level.INFO, "suffix: " + suffix); - IMAGES[i] = "." + suffix; - } - - add(imagePanel, IMAGE_VIEWER_LAYER); - add(videoPanel, VIDEO_VIEWER_LAYER); - - switchPanels(false); - - } - - /** - * This method is called from within the constructor to initialize the form. - * WARNING: Do NOT modify this code. The content of this method is always - * regenerated by the Form Editor. - */ - @SuppressWarnings("unchecked") - // //GEN-BEGIN:initComponents - private void initComponents() { - - setLayout(new java.awt.CardLayout()); - getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(DataContentViewerMedia.class, "DataContentViewerMedia.AccessibleContext.accessibleDescription")); // NOI18N - }// //GEN-END:initComponents - // Variables declaration - do not modify//GEN-BEGIN:variables - // End of variables declaration//GEN-END:variables - - @Override - public void setNode(Node selectedNode) { - - if (selectedNode == null) { - videoPanel.reset(); - return; - } - - AbstractFile file = selectedNode.getLookup().lookup(AbstractFile.class); - if (file == null) { - return; - } - - if (file.equals(lastFile)) { - return; //prevent from loading twice if setNode() called mult. times - } else { - lastFile = file; - } - - videoPanel.reset(); - - final Dimension dims = DataContentViewerMedia.this.getSize(); - - if (imagePanelInited && containsExt(file.getName(), IMAGES)) { - imagePanel.showImageFx(file, dims); - this.switchPanels(false); - } else if (videoPanelInited - && (containsExt(file.getName(), VIDEOS) || containsExt(file.getName(), AUDIOS))) { - videoPanel.setupVideo(file, dims); - switchPanels(true); - } - } - - /** - * switch to visible video or image panel - * - * @param showVideo true if video panel, false if image panel - */ - private void switchPanels(boolean showVideo) { - CardLayout layout = (CardLayout)this.getLayout(); - 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 getToolTip() { - return "Displays supported multimedia files (images, videos, audio)"; - } - - @Override - public DataContentViewer createInstance() { - return new DataContentViewerMedia(); - } - - @Override - public Component getComponent() { - return this; - } - - @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) { - return false; - } - - AbstractFile file = node.getLookup().lookup(AbstractFile.class); - if (file == null) { - return false; - } - - - if (file.getSize() == 0) { - return false; - } - - String name = file.getName().toLowerCase(); - - if (imagePanelInited && containsExt(name, IMAGES)) { - return true; - } //for gstreamer formats, check if initialized first, then - //support audio formats, and video formats - else if (videoPanelInited && videoPanel.isInited() - && (containsExt(name, AUDIOS) - || (containsExt(name, VIDEOS)))) { - return true; - } - - return false; - } - - @Override - public int isPreferred(Node node, boolean isSupported) { - if (isSupported) { - //special case, check if deleted video, then do not make it preferred - AbstractFile file = node.getLookup().lookup(AbstractFile.class); - if (file == null) { - return 0; - } - String name = file.getName().toLowerCase(); - - boolean deleted = file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC); - if (containsExt(name, VIDEOS) && deleted) { - return 0; - } else { - return 7; - } - } else { - return 0; - } - } - - private static boolean containsExt(String name, String[] exts) { - int extStart = name.lastIndexOf("."); - String ext = ""; - if (extStart != -1) { - ext = name.substring(extStart, name.length()).toLowerCase(); - } - return Arrays.asList(exts).contains(ext); - } -} - - - - - - - - - +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2013 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.corecomponents; + +import java.awt.CardLayout; +import java.awt.Component; +import java.awt.Dimension; +import java.util.Arrays; +import java.util.logging.Level; +import javax.imageio.ImageIO; +import org.sleuthkit.autopsy.coreutils.Logger; +import javax.swing.SwingUtilities; +import org.openide.nodes.Node; +import org.openide.util.Exceptions; +import org.openide.util.lookup.ServiceProvider; +import org.openide.util.lookup.ServiceProviders; +import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; + +/** + * Media content viewer for videos, sounds and images. + */ +@ServiceProviders(value = { + @ServiceProvider(service = DataContentViewer.class, position = 5) +}) +public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer { + + 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[] AUDIOS = new String[]{".mp3", ".wav", ".wma"}; + + private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName()); + + private AbstractFile lastFile; + //UI + private final MediaViewVideoPanel videoPanel; + private final MediaViewImagePanel imagePanel; + private boolean videoPanelInited; + private boolean imagePanelInited; + + private static final String IMAGE_VIEWER_LAYER = "IMAGE"; + private static final String VIDEO_VIEWER_LAYER = "VIDEO"; + + /** + * Creates new form DataContentViewerVideo + */ + public DataContentViewerMedia() { + + initComponents(); + + videoPanel = new MediaViewVideoPanel(); + imagePanel = new MediaViewImagePanel(); + videoPanelInited = videoPanel.isInited(); + imagePanelInited = imagePanel.isInited(); + + customizeComponents(); + logger.log(Level.INFO, "Created MediaView instance: " + this); + } + + private void customizeComponents() { + logger.log(Level.INFO, "Supported image formats by javafx image viewer: "); + //initialize supported image types + //TODO use mime-types instead once we have support + String[] fxSupportedImagesSuffixes = ImageIO.getReaderFileSuffixes(); + IMAGES = new String[fxSupportedImagesSuffixes.length]; + for (int i = 0; i < fxSupportedImagesSuffixes.length; ++i) { + String suffix = fxSupportedImagesSuffixes[i]; + logger.log(Level.INFO, "suffix: " + suffix); + IMAGES[i] = "." + suffix; + } + + add(imagePanel, IMAGE_VIEWER_LAYER); + add(videoPanel, VIDEO_VIEWER_LAYER); + + switchPanels(false); + + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + setLayout(new java.awt.CardLayout()); + getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(DataContentViewerMedia.class, "DataContentViewerMedia.AccessibleContext.accessibleDescription")); // NOI18N + }// //GEN-END:initComponents + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables + + @Override + public void setNode(Node selectedNode) { + try { + if (selectedNode == null) { + videoPanel.reset(); + return; + } + + AbstractFile file = selectedNode.getLookup().lookup(AbstractFile.class); + if (file == null) { + return; + } + + if (file.equals(lastFile)) { + return; //prevent from loading twice if setNode() called mult. times + } else { + lastFile = file; + } + + videoPanel.reset(); + + final Dimension dims = DataContentViewerMedia.this.getSize(); + + if (imagePanelInited && containsExt(file.getName(), IMAGES)) { + imagePanel.showImageFx(file, dims); + this.switchPanels(false); + } else if (videoPanelInited + && (containsExt(file.getName(), VIDEOS) || containsExt(file.getName(), AUDIOS))) { + videoPanel.setupVideo(file, dims); + 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 + */ + private void switchPanels(boolean showVideo) { + CardLayout layout = (CardLayout)this.getLayout(); + 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 getToolTip() { + return "Displays supported multimedia files (images, videos, audio)"; + } + + @Override + public DataContentViewer createInstance() { + return new DataContentViewerMedia(); + } + + @Override + public Component getComponent() { + return this; + } + + @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) { + return false; + } + + AbstractFile file = node.getLookup().lookup(AbstractFile.class); + if (file == null) { + return false; + } + + + if (file.getSize() == 0) { + return false; + } + + String name = file.getName().toLowerCase(); + + if (imagePanelInited && containsExt(name, IMAGES)) { + return true; + } //for gstreamer formats, check if initialized first, then + //support audio formats, and video formats + else if (videoPanelInited && videoPanel.isInited() + && (containsExt(name, AUDIOS) + || (containsExt(name, VIDEOS)))) { + return true; + } + + return false; + } + + @Override + public int isPreferred(Node node, boolean isSupported) { + if (isSupported) { + //special case, check if deleted video, then do not make it preferred + AbstractFile file = node.getLookup().lookup(AbstractFile.class); + if (file == null) { + return 0; + } + String name = file.getName().toLowerCase(); + + boolean deleted = file.isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM.UNALLOC); + if (containsExt(name, VIDEOS) && deleted) { + return 0; + } else { + return 7; + } + } else { + return 0; + } + } + + private static boolean containsExt(String name, String[] exts) { + int extStart = name.lastIndexOf("."); + String ext = ""; + if (extStart != -1) { + ext = name.substring(extStart, name.length()).toLowerCase(); + } + return Arrays.asList(exts).contains(ext); + } +} + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewVideoPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewVideoPanel.java index 0828249e7f..df41785c5e 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewVideoPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewVideoPanel.java @@ -38,8 +38,6 @@ import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.event.ChangeEvent; 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.ProgressHandleFactory; 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 { private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName()); - private boolean gstInited; private boolean vlcInited; private static final long MIN_FRAME_INTERVAL_MILLIS = 500; private static final long FRAME_CAPTURE_TIMEOUT_MILLIS = 1000; private static final String MEDIA_PLAYER_ERROR_STRING = "The media player cannot process this file."; + private static final int POS_FACTOR = 10000; //playback private long durationMillis = 0; private VideoProgressWorker videoProgressWorker; private int totalHours, totalMinutes, totalSeconds; - private volatile PlayBin2 gstPlaybin2; private MediaPlayer vlcMediaPlayer; - private VideoComponent gstVideoComponent; private EmbeddedMediaPlayerComponent vlcVideoComponent; 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 boolean isFileLoaded; + private java.io.File currentVideoFile; + private boolean replay; private Set badVideoFiles = Collections.synchronizedSet(new HashSet()); /** @@ -112,12 +108,10 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt } public EmbeddedMediaPlayerComponent getVideoComponent() { -// return gstVideoComponent; return vlcVideoComponent; } public boolean isInited() { -// return gstInited; return vlcInited; } @@ -137,14 +131,15 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt */ @Override public void stateChanged(ChangeEvent e) { - int time = progressSlider.getValue(); - if (vlcMediaPlayer != null && !autoTracking) { - boolean wasPlaying = !vlcMediaPlayer.isPlaying(); - vlcMediaPlayer.setPause(true); - vlcMediaPlayer.setTime(time); - vlcMediaPlayer.setPause(wasPlaying); + if (vlcMediaPlayer != null && !autoTracking) { + float positionValue = progressSlider.getValue() / (float) POS_FACTOR; + // Avoid end of file freeze-up + if (positionValue > 0.99f) { + positionValue = 0.99f; } + vlcMediaPlayer.setPosition(positionValue); } + } }); } @@ -161,7 +156,28 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt 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 true, if the library was loaded correctly. + * false, otherwise. + */ private boolean initVlc() { try { 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) { infoLabel.setText(""); currentFile = file; - isFileLoaded = false; + replay = false; final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC); if (deleted) { 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); pauseButton.setEnabled(true); progressSlider.setEnabled(true); - - java.io.File ioFile = getJFile(file); vlcVideoComponent = new EmbeddedMediaPlayerComponent(); vlcMediaPlayer = vlcVideoComponent.getMediaPlayer(); @@ -216,6 +230,9 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt videoPanel.add(vlcVideoComponent); videoPanel.setVisible(true); logger.log(Level.INFO, "Created media player."); + + ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile)); + em.execute(); } void reset() { @@ -230,7 +247,13 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt if (!isInited()) { return; } - + + // get rid of any existing videoProgressWorker thread + if (videoProgressWorker != null) { + videoProgressWorker.cancel(true); + videoProgressWorker = null; + } + if (vlcMediaPlayer != null) { if (vlcMediaPlayer.isPlaying()) { vlcMediaPlayer.stop(); @@ -245,13 +268,8 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt logger.log(Level.INFO, "Released video component"); } - // get rid of any existing videoProgressWorker thread - if (videoProgressWorker != null) { - videoProgressWorker.cancel(true); - videoProgressWorker = null; - } - currentFile = null; + currentVideoFile = null; } 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); 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 @@ -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(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 }// //GEN-END:initComponents private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed - if (!isFileLoaded) { - // First time play button is pressed - logger.log(Level.INFO, "Loading media file"); - ExtractMedia em = new ExtractMedia(currentFile, getJFile(currentFile)); - em.execute(); - em.getExtractedBytes(); + if (replay) { + // File has completed playing. Play button now replays + logger.log(Level.INFO, "Replaying video."); + replay = false; + playMedia(); } else if (vlcMediaPlayer.isPlaying()) { + logger.log(Level.INFO, "Pausing."); this.pause(); } else { + logger.log(Level.INFO, "Playing"); this.unPause(); } }//GEN-LAST:event_pauseButtonActionPerformed @@ -428,10 +488,23 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt private javax.swing.JPanel videoPanel; // 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 { private String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; 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; @@ -441,28 +514,23 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt } private void resetVideo() throws Exception { - logger.log(Level.INFO, "Resetting video."); - if (vlcMediaPlayer != null) { - vlcMediaPlayer.stop(); - } pauseButton.setText("►"); - progressSlider.setValue(0); String durationStr = String.format(durationFormat, 0, 0, 0, totalHours, totalMinutes, totalSeconds); progressLabel.setText(durationStr); } - /** - * @return true while millisElapsed is greater than END_TIME_MARGIN_MS - * from durationMillis. This is used to indicate when the video has - * ended because for some videos the time elapsed never becomes equal to - * the reported duration of the video. - */ - private boolean hasNotEnded() { - boolean ended = (durationMillis - millisElapsed) > END_TIME_MARGIN_MS; - return ended; - } +// /** +// * @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() { +// boolean ended = (durationMillis - millisElapsed) > END_TIME_MARGIN_MS; +// return ended; +// } @Override protected Object doInBackground() throws Exception { @@ -471,10 +539,11 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt progressSlider.setEnabled(true); int elapsedHours = -1, elapsedMinutes = -1, elapsedSeconds = -1; - while (hasNotEnded() && isMediaPlayerReady() && !isCancelled()) { + while (isMediaPlayerReady() && !isCancelled()) { millisElapsed = vlcMediaPlayer.getTime(); - + currentPosition = vlcMediaPlayer.getPosition(); + // pick out the elapsed hours, minutes, seconds long secondsElapsed = millisElapsed / 1000; elapsedHours = (int) secondsElapsed / 3600; @@ -489,7 +558,7 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt progressLabel.setText(durationStr); autoTracking = true; - progressSlider.setValue((int) millisElapsed); + progressSlider.setValue((int) (currentPosition * POS_FACTOR)); autoTracking = false; try { @@ -562,58 +631,41 @@ public class MediaViewVideoPanel extends javax.swing.JPanel implements FrameCapt logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); } finally { progress.finish(); - isFileLoaded = true; if (!this.isCancelled()) { logger.log(Level.INFO, "Loaded media file"); + currentVideoFile = jFile; 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 { + @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.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); + } } }