/* * Autopsy Forensic Browser * * Copyright 2013-15 Basis Technology Corp. * Contact: carrier <at> sleuthkit <dot> org * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.sleuthkit.autopsy.corecomponents; import com.google.common.io.Files; import java.awt.Dimension; import java.awt.Image; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.SwingUtilities; import javax.swing.SwingWorker; import javax.swing.event.ChangeEvent; 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.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProviders; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.VideoUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; @ServiceProviders(value = { @ServiceProvider(service = FrameCapture.class) }) public class GstVideoPanel extends MediaViewVideoPanel { private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS private static final List<String> MIMETYPES = Arrays.asList("video/quicktime", "audio/mpeg", "audio/x-mpeg", "video/mpeg", "video/x-mpeg", "audio/mpeg3", "audio/x-mpeg-3", "video/x-flv", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS private static final Logger logger = Logger.getLogger(GstVideoPanel.class.getName()); private boolean gstInited; 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 = NbBundle.getMessage(GstVideoPanel.class, "GstVideoPanel.cannotProcFile.err"); //playback private long durationMillis = 0; private VideoProgressWorker videoProgressWorker; private int totalHours, totalMinutes, totalSeconds; 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 final Set<String> badVideoFiles = Collections.synchronizedSet(new HashSet<String>()); /** * Creates new form MediaViewVideoPanel */ public GstVideoPanel() { initComponents(); customizeComponents(); } public JButton getPauseButton() { return pauseButton; } public JLabel getProgressLabel() { return progressLabel; } public JSlider getProgressSlider() { return progressSlider; } public JPanel getVideoPanel() { return videoPanel; } public VideoComponent getVideoComponent() { return gstVideoComponent; } @Override public boolean isInited() { return gstInited; } private void customizeComponents() { if (!initGst()) { return; } progressSlider.setEnabled(false); // disable slider; enable after user plays vid progressSlider.setValue(0); progressSlider.addChangeListener((ChangeEvent e) -> { /** * Should always try to synchronize any call to * progressSlider.setValue() to avoid a different thread changing * playbin while stateChanged() is processing */ int time = progressSlider.getValue(); synchronized (playbinLock) { if (gstPlaybin2 != null && !autoTracking) { State orig = gstPlaybin2.getState(); if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) { logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed."); //NON-NLS infoLabel.setText(MEDIA_PLAYER_ERROR_STRING); return; } if (gstPlaybin2.seek(ClockTime.fromMillis(time)) == false) { logger.log(Level.WARNING, "Attempt to call PlayBin2.seek() failed."); //NON-NLS infoLabel.setText(MEDIA_PLAYER_ERROR_STRING); return; } gstPlaybin2.setState(orig); } } }); } private boolean initGst() { try { logger.log(Level.INFO, "Initializing gstreamer for video/audio viewing"); //NON-NLS Gst.init(); gstInited = true; } catch (GstException e) { gstInited = false; logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and frame extraction capabilities", e); //NON-NLS MessageNotifyUtil.Notify.error( NbBundle.getMessage(this.getClass(), "GstVideoPanel.initGst.gstException.msg"), 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); //NON-NLS MessageNotifyUtil.Notify.error( NbBundle.getMessage(this.getClass(), "GstVideoPanel.initGst.otherException.msg"), e.getMessage()); return false; } return true; } @Override void setupVideo(final AbstractFile file, final Dimension dims) { reset(); infoLabel.setText(""); currentFile = file; final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC); if (deleted) { infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.setupVideo.infoLabel.text")); videoPanel.removeAll(); pauseButton.setEnabled(false); progressSlider.setEnabled(false); return; } String path = ""; try { path = file.getUniquePath(); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Cannot get unique path of video file"); //NON-NLS } infoLabel.setText(path); infoLabel.setToolTipText(path); pauseButton.setEnabled(true); progressSlider.setEnabled(true); java.io.File ioFile = VideoUtils.getTempVideoFile(file); gstVideoComponent = new VideoComponent(); synchronized (playbinLock) { if (gstPlaybin2 != null) { gstPlaybin2.dispose(); } gstPlaybin2 = new PlayBin2("VideoPlayer"); //NON-NLS gstPlaybin2.setVideoSink(gstVideoComponent.getElement()); videoPanel.removeAll(); videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS)); videoPanel.add(gstVideoComponent); 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."); //NON-NLS infoLabel.setText(MEDIA_PLAYER_ERROR_STRING); } } } @Override void reset() { // reset the progress label text on the event dispatch thread SwingUtilities.invokeLater(() -> { progressLabel.setText(""); }); if (!isInited()) { return; } synchronized (playbinLock) { if (gstPlaybin2 != null) { if (gstPlaybin2.isPlaying()) { if (gstPlaybin2.stop() == StateChangeReturn.FAILURE) { logger.log(Level.WARNING, "Attempt to call PlayBin2.stop() failed."); //NON-NLS 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."); //NON-NLS 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 if (videoProgressWorker != null) { videoProgressWorker.cancel(true); videoProgressWorker = null; } currentFile = null; } /** * @param file a video file from which to capture frames * @param numFrames the number of frames to capture. These frames will be * captured at successive intervals given by * durationOfVideo/numFrames. If this frame interval is * less than MIN_FRAME_INTERVAL_MILLIS, then only one frame * will be captured and returned. * * @return a List of VideoFrames representing the captured frames. */ @Override public List<VideoFrame> captureFrames(java.io.File file, int numFrames) throws Exception { List<VideoFrame> frames = new ArrayList<>(); Object lock = new Object(); FrameCaptureRGBListener rgbListener = new FrameCaptureRGBListener(lock); if (!isInited()) { return frames; } // throw exception if this file is known to be problematic if (badVideoFiles.contains(file.getName())) { throw new Exception( NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemFile.msg", file.getName())); } // set up a PlayBin2 object RGBDataSink videoSink = new RGBDataSink("rgb", rgbListener); //NON-NLS PlayBin2 playbin = new PlayBin2("VideoFrameCapture"); //NON-NLS 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(NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemPlay.msg")); } ret = playbin.pause(); if (ret == StateChangeReturn.FAILURE) { // add this file to the set of known bad ones badVideoFiles.add(file.getName()); throw new Exception(NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemPause.msg")); } playbin.getState(); // get the duration of the video TimeUnit unit = TimeUnit.MILLISECONDS; long myDurationMillis = playbin.queryDuration(unit); if (myDurationMillis <= 0) { return frames; } // calculate the number of frames to capture int numFramesToGet = numFrames; long frameInterval = myDurationMillis / numFrames; if (frameInterval < MIN_FRAME_INTERVAL_MILLIS) { numFramesToGet = 1; } // for each timeStamp, grap a frame for (int i = 0; i < numFramesToGet; ++i) { long timeStamp = i * frameInterval; ret = playbin.pause(); if (ret == StateChangeReturn.FAILURE) { // add this file to the set of known bad ones badVideoFiles.add(file.getName()); throw new Exception( NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemPauseCaptFrame.msg")); } playbin.getState(); if (!playbin.seek(timeStamp, unit)) { logger.log(Level.INFO, "There was a problem seeking to " + timeStamp + " " + unit.name().toLowerCase()); //NON-NLS } ret = playbin.play(); if (ret == StateChangeReturn.FAILURE) { // add this file to the set of known bad ones badVideoFiles.add(file.getName()); throw new Exception( NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemPlayCaptFrame.msg")); } // 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); //NON-NLS } } 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( NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemStopCaptFrame.msg")); } if (image == null) { logger.log(Level.WARNING, "There was a problem while trying to capture a frame from file " + file.getName()); //NON-NLS badVideoFiles.add(file.getName()); break; } frames.add(new VideoFrame(image, timeStamp)); } 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. * WARNING: Do NOT modify this code. The content of this method is always * regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // <editor-fold defaultstate="collapsed" desc="Generated Code"> private void initComponents() { videoPanel = new javax.swing.JPanel(); controlPanel = new javax.swing.JPanel(); pauseButton = new javax.swing.JButton(); progressSlider = new javax.swing.JSlider(); progressLabel = new javax.swing.JLabel(); infoLabel = new javax.swing.JLabel(); javax.swing.GroupLayout videoPanelLayout = new javax.swing.GroupLayout(videoPanel); videoPanel.setLayout(videoPanelLayout); videoPanelLayout.setHorizontalGroup( videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 0, Short.MAX_VALUE) ); videoPanelLayout.setVerticalGroup( videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGap(0, 231, Short.MAX_VALUE) ); org.openide.awt.Mnemonics.setLocalizedText(pauseButton, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.pauseButton.text")); // NOI18N pauseButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { pauseButtonActionPerformed(evt); } }); org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.progressLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.infoLabel.text")); // NOI18N javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel); controlPanel.setLayout(controlPanelLayout); controlPanelLayout.setHorizontalGroup( controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(controlPanelLayout.createSequentialGroup() .addContainerGap() .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(controlPanelLayout.createSequentialGroup() .addGap(6, 6, 6) .addComponent(infoLabel) .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addGroup(controlPanelLayout.createSequentialGroup() .addComponent(pauseButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 265, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(progressLabel) .addContainerGap()))) ); controlPanelLayout.setVerticalGroup( controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(controlPanelLayout.createSequentialGroup() .addContainerGap() .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(progressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(pauseButton) .addComponent(progressLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE)) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(infoLabel) .addContainerGap()) ); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) ); }// </editor-fold> private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed synchronized (playbinLock) { State state = gstPlaybin2.getState(); if (state.equals(State.PLAYING)) { if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) { logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed."); //NON-NLS infoLabel.setText(MEDIA_PLAYER_ERROR_STRING); return; } 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."); //NON-NLS 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."); //NON-NLS 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."); //NON-NLS infoLabel.setText(MEDIA_PLAYER_ERROR_STRING); return; } } else if (state.equals(State.READY)) { final File tempVideoFile = VideoUtils.getTempVideoFile(currentFile); new ExtractMedia(currentFile, tempVideoFile).execute(); } } }//GEN-LAST:event_pauseButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JPanel controlPanel; private javax.swing.JLabel infoLabel; private javax.swing.JButton pauseButton; private javax.swing.JLabel progressLabel; private javax.swing.JSlider progressSlider; private javax.swing.JPanel videoPanel; // End of variables declaration//GEN-END:variables private class VideoProgressWorker extends SwingWorker<Object, Object> { private final String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; //NON-NLS private long millisElapsed = 0; private final long INTER_FRAME_PERIOD_MS = 20; private final long END_TIME_MARGIN_MS = 50; private boolean isPlayBinReady() { synchronized (playbinLock) { return gstPlaybin2 != null && !gstPlaybin2.getState().equals(State.NULL); } } 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."); //NON-NLS 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."); //NON-NLS infoLabel.setText(MEDIA_PLAYER_ERROR_STRING); } gstPlaybin2.getState(); //NEW } } 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() { return (durationMillis - millisElapsed) > END_TIME_MARGIN_MS; } @Override protected Object doInBackground() throws Exception { // enable the slider progressSlider.setEnabled(true); ClockTime pos; while (hasNotEnded() && isPlayBinReady() && !isCancelled()) { synchronized (playbinLock) { pos = gstPlaybin2.queryPosition(); } millisElapsed = pos.toMillis(); // pick out the elapsed hours, minutes, seconds long secondsElapsed = millisElapsed / 1000; int elapsedHours = (int) secondsElapsed / 3600; secondsElapsed -= elapsedHours * 3600; int elapsedMinutes = (int) secondsElapsed / 60; secondsElapsed -= elapsedMinutes * 60; int elapsedSeconds = (int) secondsElapsed; String durationStr = String.format(durationFormat, elapsedHours, elapsedMinutes, elapsedSeconds, totalHours, totalMinutes, totalSeconds); progressLabel.setText(durationStr); autoTracking = true; progressSlider.setValue((int) millisElapsed); autoTracking = false; try { Thread.sleep(INTER_FRAME_PERIOD_MS); } catch (InterruptedException ex) { break; } } // disable the slider progressSlider.setEnabled(false); resetVideo(); return null; } @Override protected void done() { // see if any exceptions were thrown try { get(); } catch (InterruptedException | ExecutionException ex) { logger.log(Level.WARNING, "Error updating video progress: " + ex.getMessage()); //NON-NLS infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.infoLabel.updateErr", ex.getMessage())); } // catch and ignore if we were cancelled catch (java.util.concurrent.CancellationException ex) { } } } //end class progress worker /* * Thread that extracts and plays a file */ private class ExtractMedia extends SwingWorker<Long, Void> { private ProgressHandle progress; private final AbstractFile sourceFile; private final java.io.File tempFile; ExtractMedia(AbstractFile sFile, java.io.File jFile) { this.sourceFile = sFile; this.tempFile = jFile; } @Override protected Long doInBackground() throws Exception { if (tempFile.exists() == false || tempFile.length() < sourceFile.getSize()) { progress = ProgressHandle.createHandle(NbBundle.getMessage(GstVideoPanel.class, "GstVideoPanel.ExtractMedia.progress.buffering", sourceFile.getName()), () -> ExtractMedia.this.cancel(true)); progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.buffering")); progress.start(100); try { Files.createParentDirs(tempFile); return ContentUtils.writeToFile(sourceFile, tempFile, progress, this, true); } catch (IOException ex) { logger.log(Level.WARNING, "Error buffering file", ex); //NON-NLS return 0L; } } return 0L; } /* * clean up or start the worker threads */ @Override protected void done() { try { super.get(); //block and get all exceptions thrown while doInBackground() } catch (CancellationException ex) { logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS } catch (InterruptedException ex) { logger.log(Level.INFO, "Media buffering was interrupted."); //NON-NLS } catch (Exception ex) { logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); //NON-NLS } finally { if (progress != null) { progress.finish(); } if (!this.isCancelled()) { playMedia(); } } } void playMedia() { if (tempFile == null || !tempFile.exists()) { progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progressLabel.bufferingErr")); return; } ClockTime dur; 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."); //NON-NLS infoLabel.setText(MEDIA_PLAYER_ERROR_STRING); return; } if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) { logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed."); //NON-NLS infoLabel.setText(MEDIA_PLAYER_ERROR_STRING); return; } gstPlaybin2.getState(); dur = gstPlaybin2.queryDuration(); } durationMillis = dur.toMillis(); // 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(() -> { progressSlider.setMaximum((int) durationMillis); progressSlider.setMinimum(0); synchronized (playbinLock) { if (gstPlaybin2.play() == StateChangeReturn.FAILURE) { logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed."); //NON-NLS infoLabel.setText(MEDIA_PLAYER_ERROR_STRING); } } pauseButton.setText("||"); videoProgressWorker = new VideoProgressWorker(); videoProgressWorker.execute(); }); } } @Override public String[] getExtensions() { return EXTENSIONS.clone(); } @Override public List<String> getMimeTypes() { return MIMETYPES; } }