Autopsy  4.5.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
GstVideoPanel.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-15 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.corecomponents;
20 
21 import com.google.common.io.Files;
22 import java.awt.Dimension;
23 import java.awt.Image;
24 import java.awt.image.BufferedImage;
25 import java.io.File;
26 import java.io.IOException;
27 import java.nio.IntBuffer;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.HashSet;
32 import java.util.List;
33 import java.util.Set;
34 import java.util.concurrent.CancellationException;
35 import java.util.concurrent.ExecutionException;
36 import java.util.concurrent.TimeUnit;
37 import java.util.logging.Level;
38 import javax.swing.BoxLayout;
39 import javax.swing.JButton;
40 import javax.swing.JLabel;
41 import javax.swing.JPanel;
42 import javax.swing.JSlider;
43 import javax.swing.SwingUtilities;
44 import javax.swing.SwingWorker;
45 import javax.swing.event.ChangeEvent;
46 import org.gstreamer.ClockTime;
47 import org.gstreamer.Gst;
48 import org.gstreamer.GstException;
49 import org.gstreamer.State;
50 import org.gstreamer.StateChangeReturn;
51 import org.gstreamer.elements.PlayBin2;
52 import org.gstreamer.elements.RGBDataSink;
53 import org.gstreamer.swing.VideoComponent;
54 import org.netbeans.api.progress.ProgressHandle;
55 import org.openide.util.NbBundle;
56 import org.openide.util.lookup.ServiceProvider;
57 import org.openide.util.lookup.ServiceProviders;
62 import org.sleuthkit.datamodel.AbstractFile;
63 import org.sleuthkit.datamodel.TskCoreException;
64 import org.sleuthkit.datamodel.TskData;
65 
66 @ServiceProviders(value = {
67  @ServiceProvider(service = FrameCapture.class)
68 })
69 public class GstVideoPanel extends MediaViewVideoPanel {
70 
71  private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS
72  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
73 
74  private static final Logger logger = Logger.getLogger(GstVideoPanel.class.getName());
75  private boolean gstInited;
76  private static final long MIN_FRAME_INTERVAL_MILLIS = 500;
77  private static final long FRAME_CAPTURE_TIMEOUT_MILLIS = 1000;
78  private static final String MEDIA_PLAYER_ERROR_STRING = NbBundle.getMessage(GstVideoPanel.class, "GstVideoPanel.cannotProcFile.err");
79  //playback
80  private long durationMillis = 0;
82  private int totalHours, totalMinutes, totalSeconds;
83  private volatile PlayBin2 gstPlaybin2;
84  private VideoComponent gstVideoComponent;
85  private boolean autoTracking = false; // true if the slider is moving automatically
86  private final Object playbinLock = new Object(); // lock for synchronization of gstPlaybin2 player
87  private AbstractFile currentFile;
88  private final Set<String> badVideoFiles = Collections.synchronizedSet(new HashSet<String>());
89 
93  public GstVideoPanel() {
94  initComponents();
95  customizeComponents();
96  }
97 
98  public JButton getPauseButton() {
99  return pauseButton;
100  }
101 
102  public JLabel getProgressLabel() {
103  return progressLabel;
104  }
105 
106  public JSlider getProgressSlider() {
107  return progressSlider;
108  }
109 
110  public JPanel getVideoPanel() {
111  return videoPanel;
112  }
113 
114  public VideoComponent getVideoComponent() {
115  return gstVideoComponent;
116  }
117 
118  @Override
119  public boolean isInited() {
120  return gstInited;
121  }
122 
123  private void customizeComponents() {
124  if (!initGst()) {
125  return;
126  }
127 
128  progressSlider.setEnabled(false); // disable slider; enable after user plays vid
129  progressSlider.setValue(0);
130 
131  progressSlider.addChangeListener((ChangeEvent e) -> {
137  int time = progressSlider.getValue();
138  synchronized (playbinLock) {
139  if (gstPlaybin2 != null && !autoTracking) {
140  State orig = gstPlaybin2.getState();
141  if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
142  logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed."); //NON-NLS
143  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
144  return;
145  }
146  if (gstPlaybin2.seek(ClockTime.fromMillis(time)) == false) {
147  logger.log(Level.WARNING, "Attempt to call PlayBin2.seek() failed."); //NON-NLS
148  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
149  return;
150  }
151  gstPlaybin2.setState(orig);
152  }
153  }
154  });
155  }
156 
157  private boolean initGst() {
158  try {
159  logger.log(Level.INFO, "Initializing gstreamer for video/audio viewing"); //NON-NLS
160  Gst.init();
161  gstInited = true;
162  } catch (GstException e) {
163  gstInited = false;
164  logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and frame extraction capabilities", e); //NON-NLS
166  NbBundle.getMessage(this.getClass(), "GstVideoPanel.initGst.gstException.msg"),
167  e.getMessage());
168  return false;
169  } catch (UnsatisfiedLinkError | NoClassDefFoundError | Exception e) {
170  gstInited = false;
171  logger.log(Level.SEVERE, "Error initializing gstreamer for audio/video viewing and extraction capabilities", e); //NON-NLS
173  NbBundle.getMessage(this.getClass(), "GstVideoPanel.initGst.otherException.msg"),
174  e.getMessage());
175  return false;
176  }
177 
178  return true;
179  }
180 
181  @Override
182  void setupVideo(final AbstractFile file, final Dimension dims) {
183  reset();
184  infoLabel.setText("");
185  currentFile = file;
186  final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
187  if (deleted) {
188  infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.setupVideo.infoLabel.text"));
189  videoPanel.removeAll();
190  pauseButton.setEnabled(false);
191  progressSlider.setEnabled(false);
192  return;
193  }
194 
195  String path = "";
196  try {
197  path = file.getUniquePath();
198  } catch (TskCoreException ex) {
199  logger.log(Level.SEVERE, "Cannot get unique path of video file"); //NON-NLS
200  }
201  infoLabel.setText(path);
202  infoLabel.setToolTipText(path);
203  pauseButton.setEnabled(true);
204  progressSlider.setEnabled(true);
205 
206  java.io.File ioFile = VideoUtils.getTempVideoFile(file);
207 
208  gstVideoComponent = new VideoComponent();
209  synchronized (playbinLock) {
210  if (gstPlaybin2 != null) {
211  gstPlaybin2.dispose();
212  }
213  gstPlaybin2 = new PlayBin2("VideoPlayer"); //NON-NLS
214  gstPlaybin2.setVideoSink(gstVideoComponent.getElement());
215 
216  videoPanel.removeAll();
217 
218  videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
219  videoPanel.add(gstVideoComponent);
220 
221  videoPanel.setVisible(true);
222 
223  gstPlaybin2.setInputFile(ioFile);
224 
225  if (gstPlaybin2.setState(State.READY) == StateChangeReturn.FAILURE) {
226  logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.READY) failed."); //NON-NLS
227  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
228  }
229  }
230 
231  }
232 
233  @Override
234  void reset() {
235 
236  // reset the progress label text on the event dispatch thread
237  SwingUtilities.invokeLater(() -> {
238  progressLabel.setText("");
239  });
240 
241  if (!isInited()) {
242  return;
243  }
244 
245  synchronized (playbinLock) {
246  if (gstPlaybin2 != null) {
247  if (gstPlaybin2.isPlaying()) {
248  if (gstPlaybin2.stop() == StateChangeReturn.FAILURE) {
249  logger.log(Level.WARNING, "Attempt to call PlayBin2.stop() failed."); //NON-NLS
250  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
251  return;
252  }
253  }
254  if (gstPlaybin2.setState(State.NULL) == StateChangeReturn.FAILURE) {
255  logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.NULL) failed."); //NON-NLS
256  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
257  return;
258  }
259  if (gstPlaybin2.getState().equals(State.NULL)) {
260  gstPlaybin2.dispose();
261  }
262  gstPlaybin2 = null;
263  }
264  gstVideoComponent = null;
265  }
266 
267  // get rid of any existing videoProgressWorker thread
268  if (videoProgressWorker != null) {
269  videoProgressWorker.cancel(true);
270  videoProgressWorker = null;
271  }
272 
273  currentFile = null;
274  }
275 
286  @Override
287  public List<VideoFrame> captureFrames(java.io.File file, int numFrames) throws Exception {
288 
289  List<VideoFrame> frames = new ArrayList<>();
290 
291  Object lock = new Object();
292  FrameCaptureRGBListener rgbListener = new FrameCaptureRGBListener(lock);
293 
294  if (!isInited()) {
295  return frames;
296  }
297 
298  // throw exception if this file is known to be problematic
299  if (badVideoFiles.contains(file.getName())) {
300  throw new Exception(
301  NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemFile.msg", file.getName()));
302  }
303 
304  // set up a PlayBin2 object
305  RGBDataSink videoSink = new RGBDataSink("rgb", rgbListener); //NON-NLS
306  PlayBin2 playbin = new PlayBin2("VideoFrameCapture"); //NON-NLS
307  playbin.setInputFile(file);
308  playbin.setVideoSink(videoSink);
309 
310  // this is necessary to get a valid duration value
311  StateChangeReturn ret = playbin.play();
312  if (ret == StateChangeReturn.FAILURE) {
313  // add this file to the set of known bad ones
314  badVideoFiles.add(file.getName());
315  throw new Exception(NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemPlay.msg"));
316  }
317  ret = playbin.pause();
318  if (ret == StateChangeReturn.FAILURE) {
319  // add this file to the set of known bad ones
320  badVideoFiles.add(file.getName());
321  throw new Exception(NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemPause.msg"));
322  }
323  playbin.getState();
324 
325  // get the duration of the video
326  TimeUnit unit = TimeUnit.MILLISECONDS;
327  long myDurationMillis = playbin.queryDuration(unit);
328  if (myDurationMillis <= 0) {
329  return frames;
330  }
331 
332  // calculate the number of frames to capture
333  int numFramesToGet = numFrames;
334  long frameInterval = myDurationMillis / numFrames;
335  if (frameInterval < MIN_FRAME_INTERVAL_MILLIS) {
336  numFramesToGet = 1;
337  }
338 
339  // for each timeStamp, grap a frame
340  for (int i = 0; i < numFramesToGet; ++i) {
341  long timeStamp = i * frameInterval;
342 
343  ret = playbin.pause();
344  if (ret == StateChangeReturn.FAILURE) {
345  // add this file to the set of known bad ones
346  badVideoFiles.add(file.getName());
347  throw new Exception(
348  NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemPauseCaptFrame.msg"));
349  }
350  playbin.getState();
351 
352  if (!playbin.seek(timeStamp, unit)) {
353  logger.log(Level.INFO, "There was a problem seeking to " + timeStamp + " " + unit.name().toLowerCase()); //NON-NLS
354  }
355 
356  ret = playbin.play();
357  if (ret == StateChangeReturn.FAILURE) {
358  // add this file to the set of known bad ones
359  badVideoFiles.add(file.getName());
360  throw new Exception(
361  NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemPlayCaptFrame.msg"));
362  }
363 
364  // wait for FrameCaptureRGBListener to finish
365  synchronized (lock) {
366  try {
367  lock.wait(FRAME_CAPTURE_TIMEOUT_MILLIS);
368  } catch (InterruptedException e) {
369  logger.log(Level.INFO, "InterruptedException occurred while waiting for frame capture.", e); //NON-NLS
370  }
371  }
372  Image image = rgbListener.getImage();
373 
374  ret = playbin.stop();
375  if (ret == StateChangeReturn.FAILURE) {
376  // add this file to the set of known bad ones
377  badVideoFiles.add(file.getName());
378  throw new Exception(
379  NbBundle.getMessage(this.getClass(), "GstVideoPanel.exception.problemStopCaptFrame.msg"));
380  }
381 
382  if (image == null) {
383  logger.log(Level.WARNING, "There was a problem while trying to capture a frame from file " + file.getName()); //NON-NLS
384  badVideoFiles.add(file.getName());
385  break;
386  }
387 
388  frames.add(new VideoFrame(image, timeStamp));
389  }
390 
391  return frames;
392  }
393 
394  private class FrameCaptureRGBListener implements RGBDataSink.Listener {
395 
396  public FrameCaptureRGBListener(Object waiter) {
397  this.waiter = waiter;
398  }
399 
400  private BufferedImage bi;
401  private final Object waiter;
402 
403  @Override
404  public void rgbFrame(boolean bln, int w, int h, IntBuffer rgbPixels) {
405  synchronized (waiter) {
406  bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
407  bi.setRGB(0, 0, w, h, rgbPixels.array(), 0, w);
408  waiter.notify();
409  }
410  }
411 
412  public Image getImage() {
413  synchronized (waiter) {
414  Image image = bi;
415  bi = null;
416  return image;
417  }
418  }
419 
420  }
421 
427  @SuppressWarnings("unchecked")
428  // <editor-fold defaultstate="collapsed" desc="Generated Code">
429  private void initComponents() {
430 
431  videoPanel = new javax.swing.JPanel();
432  controlPanel = new javax.swing.JPanel();
433  pauseButton = new javax.swing.JButton();
434  progressSlider = new javax.swing.JSlider();
435  progressLabel = new javax.swing.JLabel();
436  infoLabel = new javax.swing.JLabel();
437 
438  javax.swing.GroupLayout videoPanelLayout = new javax.swing.GroupLayout(videoPanel);
439  videoPanel.setLayout(videoPanelLayout);
440  videoPanelLayout.setHorizontalGroup(
441  videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
442  .addGap(0, 0, Short.MAX_VALUE)
443  );
444  videoPanelLayout.setVerticalGroup(
445  videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
446  .addGap(0, 231, Short.MAX_VALUE)
447  );
448 
449  org.openide.awt.Mnemonics.setLocalizedText(pauseButton, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.pauseButton.text")); // NOI18N
450  pauseButton.addActionListener(new java.awt.event.ActionListener() {
451  public void actionPerformed(java.awt.event.ActionEvent evt) {
452  pauseButtonActionPerformed(evt);
453  }
454  });
455 
456  org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.progressLabel.text")); // NOI18N
457 
458  org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(GstVideoPanel.class, "MediaViewVideoPanel.infoLabel.text")); // NOI18N
459 
460  javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel);
461  controlPanel.setLayout(controlPanelLayout);
462  controlPanelLayout.setHorizontalGroup(
463  controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
464  .addGroup(controlPanelLayout.createSequentialGroup()
465  .addContainerGap()
466  .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
467  .addGroup(controlPanelLayout.createSequentialGroup()
468  .addGap(6, 6, 6)
469  .addComponent(infoLabel)
470  .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
471  .addGroup(controlPanelLayout.createSequentialGroup()
472  .addComponent(pauseButton)
473  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
474  .addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 265, Short.MAX_VALUE)
475  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
476  .addComponent(progressLabel)
477  .addContainerGap())))
478  );
479  controlPanelLayout.setVerticalGroup(
480  controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
481  .addGroup(controlPanelLayout.createSequentialGroup()
482  .addContainerGap()
483  .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
484  .addComponent(progressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
485  .addComponent(pauseButton)
486  .addComponent(progressLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 29, javax.swing.GroupLayout.PREFERRED_SIZE))
487  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
488  .addComponent(infoLabel)
489  .addContainerGap())
490  );
491 
492  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
493  this.setLayout(layout);
494  layout.setHorizontalGroup(
495  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
496  .addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
497  .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
498  );
499  layout.setVerticalGroup(
500  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
501  .addGroup(layout.createSequentialGroup()
502  .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
503  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
504  .addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
505  );
506  }// </editor-fold>
507 
508  private void pauseButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pauseButtonActionPerformed
509  synchronized (playbinLock) {
510  State state = gstPlaybin2.getState();
511  if (state.equals(State.PLAYING)) {
512  if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
513  logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed."); //NON-NLS
514  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
515  return;
516  }
517  pauseButton.setText("►");
518  // Is this call necessary considering we just called gstPlaybin2.pause()?
519  if (gstPlaybin2.setState(State.PAUSED) == StateChangeReturn.FAILURE) {
520  logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.PAUSED) failed."); //NON-NLS
521  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
522  return;
523  }
524  } else if (state.equals(State.PAUSED)) {
525  if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
526  logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed."); //NON-NLS
527  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
528  return;
529  }
530  pauseButton.setText("||");
531  // Is this call necessary considering we just called gstPlaybin2.play()?
532  if (gstPlaybin2.setState(State.PLAYING) == StateChangeReturn.FAILURE) {
533  logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.PLAYING) failed."); //NON-NLS
534  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
535  return;
536  }
537  } else if (state.equals(State.READY)) {
538  final File tempVideoFile = VideoUtils.getTempVideoFile(currentFile);
539 
540  new ExtractMedia(currentFile, tempVideoFile).execute();
541 
542  }
543  }
544  }//GEN-LAST:event_pauseButtonActionPerformed
545 
546  // Variables declaration - do not modify//GEN-BEGIN:variables
547  private javax.swing.JPanel controlPanel;
548  private javax.swing.JLabel infoLabel;
549  private javax.swing.JButton pauseButton;
550  private javax.swing.JLabel progressLabel;
551  private javax.swing.JSlider progressSlider;
552  private javax.swing.JPanel videoPanel;
553  // End of variables declaration//GEN-END:variables
554 
555  private class VideoProgressWorker extends SwingWorker<Object, Object> {
556 
557  private final String durationFormat = "%02d:%02d:%02d/%02d:%02d:%02d "; //NON-NLS
558  private long millisElapsed = 0;
559  private final long INTER_FRAME_PERIOD_MS = 20;
560  private final long END_TIME_MARGIN_MS = 50;
561 
562  private boolean isPlayBinReady() {
563  synchronized (playbinLock) {
564  return gstPlaybin2 != null && !gstPlaybin2.getState().equals(State.NULL);
565  }
566  }
567 
568  private void resetVideo() throws Exception {
569  synchronized (playbinLock) {
570  if (gstPlaybin2 != null) {
571  if (gstPlaybin2.stop() == StateChangeReturn.FAILURE) {
572  logger.log(Level.WARNING, "Attempt to call PlayBin2.stop() failed."); //NON-NLS
573  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
574  }
575  // ready to be played again
576  if (gstPlaybin2.setState(State.READY) == StateChangeReturn.FAILURE) {
577  logger.log(Level.WARNING, "Attempt to call PlayBin2.setState(State.READY) failed."); //NON-NLS
578  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
579  }
580  gstPlaybin2.getState(); //NEW
581  }
582  }
583  pauseButton.setText("►");
584  progressSlider.setValue(0);
585 
586  String durationStr = String.format(durationFormat, 0, 0, 0,
587  totalHours, totalMinutes, totalSeconds);
588  progressLabel.setText(durationStr);
589  }
590 
597  private boolean hasNotEnded() {
598  return (durationMillis - millisElapsed) > END_TIME_MARGIN_MS;
599  }
600 
601  @Override
602  protected Object doInBackground() throws Exception {
603 
604  // enable the slider
605  progressSlider.setEnabled(true);
606 
607  ClockTime pos;
608  while (hasNotEnded() && isPlayBinReady() && !isCancelled()) {
609 
610  synchronized (playbinLock) {
611  pos = gstPlaybin2.queryPosition();
612  }
613  millisElapsed = pos.toMillis();
614 
615  // pick out the elapsed hours, minutes, seconds
616  long secondsElapsed = millisElapsed / 1000;
617  int elapsedHours = (int) secondsElapsed / 3600;
618  secondsElapsed -= elapsedHours * 3600;
619  int elapsedMinutes = (int) secondsElapsed / 60;
620  secondsElapsed -= elapsedMinutes * 60;
621  int elapsedSeconds = (int) secondsElapsed;
622 
623  String durationStr = String.format(durationFormat,
624  elapsedHours, elapsedMinutes, elapsedSeconds,
625  totalHours, totalMinutes, totalSeconds);
626 
627  progressLabel.setText(durationStr);
628  autoTracking = true;
629  progressSlider.setValue((int) millisElapsed);
630  autoTracking = false;
631 
632  try {
633  Thread.sleep(INTER_FRAME_PERIOD_MS);
634  } catch (InterruptedException ex) {
635  break;
636  }
637  }
638 
639  // disable the slider
640  progressSlider.setEnabled(false);
641 
642  resetVideo();
643 
644  return null;
645  }
646 
647  @Override
648  protected void done() {
649  // see if any exceptions were thrown
650  try {
651  get();
652  } catch (InterruptedException | ExecutionException ex) {
653  logger.log(Level.WARNING, "Error updating video progress: " + ex.getMessage()); //NON-NLS
654  infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.infoLabel.updateErr",
655  ex.getMessage()));
656  } // catch and ignore if we were cancelled
657  catch (java.util.concurrent.CancellationException ex) {
658  }
659  }
660  } //end class progress worker
661 
662  /*
663  * Thread that extracts and plays a file
664  */
665  private class ExtractMedia extends SwingWorker<Long, Void> {
666 
667  private ProgressHandle progress;
668  private final AbstractFile sourceFile;
669  private final java.io.File tempFile;
670 
671  ExtractMedia(AbstractFile sFile, java.io.File jFile) {
672  this.sourceFile = sFile;
673  this.tempFile = jFile;
674  }
675 
676  @Override
677  protected Long doInBackground() throws Exception {
678  if (tempFile.exists() == false || tempFile.length() < sourceFile.getSize()) {
679  progress = ProgressHandle.createHandle(NbBundle.getMessage(GstVideoPanel.class, "GstVideoPanel.ExtractMedia.progress.buffering", sourceFile.getName()), () -> ExtractMedia.this.cancel(true));
680  progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.buffering"));
681  progress.start(100);
682  try {
683  Files.createParentDirs(tempFile);
684  return ContentUtils.writeToFile(sourceFile, tempFile, progress, this, true);
685  } catch (IOException ex) {
686  logger.log(Level.WARNING, "Error buffering file", ex); //NON-NLS
687  return 0L;
688  }
689  }
690  return 0L;
691  }
692 
693  /*
694  * clean up or start the worker threads
695  */
696  @Override
697  protected void done() {
698  try {
699  super.get(); //block and get all exceptions thrown while doInBackground()
700  } catch (CancellationException ex) {
701  logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS
702  } catch (InterruptedException ex) {
703  logger.log(Level.INFO, "Media buffering was interrupted."); //NON-NLS
704  } catch (Exception ex) {
705  logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); //NON-NLS
706  } finally {
707  if (progress != null) {
708  progress.finish();
709  }
710  if (!this.isCancelled()) {
711  playMedia();
712  }
713  }
714  }
715 
716  void playMedia() {
717  if (tempFile == null || !tempFile.exists()) {
718  progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progressLabel.bufferingErr"));
719  return;
720  }
721  ClockTime dur;
722  synchronized (playbinLock) {
723  // must play, then pause and get state to get duration.
724  if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
725  logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed."); //NON-NLS
726  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
727  return;
728  }
729  if (gstPlaybin2.pause() == StateChangeReturn.FAILURE) {
730  logger.log(Level.WARNING, "Attempt to call PlayBin2.pause() failed."); //NON-NLS
731  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
732  return;
733  }
734  gstPlaybin2.getState();
735  dur = gstPlaybin2.queryDuration();
736  }
737  durationMillis = dur.toMillis();
738 
739  // pick out the total hours, minutes, seconds
740  long durationSeconds = (int) durationMillis / 1000;
741  totalHours = (int) durationSeconds / 3600;
742  durationSeconds -= totalHours * 3600;
743  totalMinutes = (int) durationSeconds / 60;
744  durationSeconds -= totalMinutes * 60;
745  totalSeconds = (int) durationSeconds;
746 
747  SwingUtilities.invokeLater(() -> {
748  progressSlider.setMaximum((int) durationMillis);
749  progressSlider.setMinimum(0);
750 
751  synchronized (playbinLock) {
752  if (gstPlaybin2.play() == StateChangeReturn.FAILURE) {
753  logger.log(Level.WARNING, "Attempt to call PlayBin2.play() failed."); //NON-NLS
754  infoLabel.setText(MEDIA_PLAYER_ERROR_STRING);
755  }
756  }
757  pauseButton.setText("||");
758  videoProgressWorker = new VideoProgressWorker();
759  videoProgressWorker.execute();
760  });
761  }
762  }
763 
764  @Override
765  public String[] getExtensions() {
766  return EXTENSIONS.clone();
767  }
768 
769  @Override
770  public List<String> getMimeTypes() {
771  return MIMETYPES;
772  }
773 
774 }
List< VideoFrame > captureFrames(java.io.File file, int numFrames)
void rgbFrame(boolean bln, int w, int h, IntBuffer rgbPixels)
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
void pauseButtonActionPerformed(java.awt.event.ActionEvent evt)
static void error(String title, String message)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static File getTempVideoFile(AbstractFile file)
Definition: VideoUtils.java:93

Copyright © 2012-2016 Basis Technology. Generated on: Tue Feb 20 2018
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.