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

Copyright © 2012-2015 Basis Technology. Generated on: Mon Oct 19 2015
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.