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

Copyright © 2012-2015 Basis Technology. Generated on: Wed Apr 6 2016
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.