Autopsy  4.13.0
Graphical digital forensics platform for The Sleuth Kit and other tools.
MediaPlayerPanel.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-2019 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.contentviewers;
20 
21 import com.google.common.io.Files;
22 import java.awt.Color;
23 import java.awt.Dimension;
24 import java.awt.Graphics;
25 import java.awt.Graphics2D;
26 import java.awt.Point;
27 import java.awt.Rectangle;
28 import java.awt.RenderingHints;
29 import java.awt.event.ActionEvent;
30 import java.awt.event.ActionListener;
31 import java.awt.event.MouseEvent;
32 import java.io.File;
33 import java.io.IOException;
34 import java.util.Arrays;
35 import java.util.EnumSet;
36 import java.util.List;
37 import java.util.SortedSet;
38 import java.util.TreeSet;
39 import java.util.concurrent.CancellationException;
40 import java.util.concurrent.ExecutionException;
41 import java.util.concurrent.Semaphore;
42 import java.util.concurrent.TimeUnit;
43 import java.util.logging.Level;
44 import javax.swing.BoxLayout;
45 import javax.swing.JPanel;
46 import javax.swing.SwingWorker;
47 import javax.swing.Timer;
48 import javax.swing.event.ChangeEvent;
49 import org.freedesktop.gstreamer.Bus;
50 import org.freedesktop.gstreamer.Gst;
51 import org.freedesktop.gstreamer.GstObject;
52 import org.freedesktop.gstreamer.State;
53 import org.freedesktop.gstreamer.elements.PlayBin;
54 import org.netbeans.api.progress.ProgressHandle;
55 import org.openide.util.NbBundle;
61 import org.sleuthkit.datamodel.AbstractFile;
62 import org.sleuthkit.datamodel.TskData;
63 import javafx.embed.swing.JFXPanel;
64 import javax.swing.JComponent;
65 import javax.swing.JSlider;
66 import javax.swing.SwingUtilities;
67 import javax.swing.event.ChangeListener;
68 import javax.swing.plaf.basic.BasicSliderUI;
69 import javax.swing.plaf.basic.BasicSliderUI.TrackListener;
70 import org.freedesktop.gstreamer.ClockTime;
71 import org.freedesktop.gstreamer.Format;
72 import org.freedesktop.gstreamer.GstException;
73 import org.freedesktop.gstreamer.event.SeekFlags;
74 import org.freedesktop.gstreamer.event.SeekType;
75 
80 @SuppressWarnings("PMD.SingularField") // UI widgets cause lots of false positives
81 public class MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaViewPanel {
82 
83  //Enumerate the accepted file extensions and mimetypes
84  private static final String[] FILE_EXTENSIONS = new String[]{
85  ".3g2",
86  ".3gp",
87  ".3gpp",
88  ".aac",
89  ".aif",
90  ".aiff",
91  ".amr",
92  ".asf",
93  ".au",
94  ".avi",
95  ".flac",
96  ".flv",
97  ".m4a",
98  ".m4v",
99  ".mka",
100  ".mkv",
101  ".mov",
102  ".mp2",
103  ".mp3",
104  ".mp4",
105  ".mpeg",
106  ".mpg",
107  ".mxf",
108  ".ogg",
109  ".wav",
110  ".webm",
111  ".wma",
112  ".wmv",}; //NON-NLS
113  private static final List<String> MIME_TYPES = Arrays.asList(
114  "video/3gpp",
115  "video/3gpp2",
116  "audio/aiff",
117  "audio/amr-wb",
118  "audio/basic",
119  "audio/mp4",
120  "video/mp4",
121  "audio/mpeg",
122  "video/mpeg",
123  "audio/mpeg3",
124  "application/mxf",
125  "application/ogg",
126  "video/quicktime",
127  "audio/vorbis",
128  "audio/vnd.wave",
129  "video/webm",
130  "video/x-3ivx",
131  "audio/x-aac",
132  "audio/x-adpcm",
133  "audio/x-alaw",
134  "audio/x-cinepak",
135  "video/x-divx",
136  "audio/x-dv",
137  "video/x-dv",
138  "video/x-ffv",
139  "audio/x-flac",
140  "video/x-flv",
141  "audio/x-gsm",
142  "video/x-h263",
143  "video/x-h264",
144  "video/x-huffyuv",
145  "video/x-indeo",
146  "video/x-intel-h263",
147  "audio/x-ircam",
148  "video/x-jpeg",
149  "audio/x-m4a",
150  "video/x-m4v",
151  "audio/x-mace",
152  "audio/x-matroska",
153  "video/x-matroska",
154  "audio/x-mpeg",
155  "video/x-mpeg",
156  "audio/x-mpeg-3",
157  "video/x-ms-asf",
158  "audio/x-ms-wma",
159  "video/x-ms-wmv",
160  "video/x-msmpeg",
161  "video/x-msvideo",
162  "video/x-msvideocodec",
163  "audio/x-mulaw",
164  "audio/x-nist",
165  "audio/x-oggflac",
166  "audio/x-paris",
167  "audio/x-qdm2",
168  "audio/x-raw",
169  "video/x-raw",
170  "video/x-rle",
171  "audio/x-speex",
172  "video/x-svq",
173  "audio/x-svx",
174  "video/x-tarkin",
175  "video/x-theora",
176  "audio/x-voc",
177  "audio/x-vorbis",
178  "video/x-vp3",
179  "audio/x-w64",
180  "audio/x-wav",
181  "audio/x-wma",
182  "video/x-wmv",
183  "video/x-xvid"
184  ); //NON-NLS
185 
186  private static final Logger logger = Logger.getLogger(MediaPlayerPanel.class.getName());
187  private static final String MEDIA_PLAYER_ERROR_STRING = NbBundle.getMessage(MediaPlayerPanel.class,
188  "GstVideoPanel.cannotProcFile.err");
189 
190  //Video playback components
191  private volatile PlayBin gstPlayBin;
192  private JavaFxAppSink fxAppSink;
193  private Bus.ERROR errorListener;
194  private Bus.STATE_CHANGED stateChangeListener;
195  private Bus.EOS endOfStreamListener;
196 
197  //Update progress bar and time label during video playback
198  //Updating every 16 MS = 62.5 FPS.
199  private final Timer timer = new Timer(16, new VideoPanelUpdater());
200  private static final int PROGRESS_SLIDER_SIZE = 2000;
201  private static final int SKIP_IN_SECONDS = 30;
202 
204 
205  //Serialize setting the value of the Video progress slider.
206  //The slider is a shared resource between the VideoPanelUpdater
207  //and the TrackListener of the JSliderUI.
208  private final Semaphore sliderLock;
209 
213  public MediaPlayerPanel() throws GstException, UnsatisfiedLinkError {
214  initComponents();
215  customizeComponents();
216  //True for fairness. In other words,
217  //acquire() calls are processed in order of invocation.
218  sliderLock = new Semaphore(1, true);
219  }
220 
221  private void customizeComponents() {
222  progressSlider.setEnabled(false); // disable slider; enable after user plays vid
223  progressSlider.setMinimum(0);
224  progressSlider.setMaximum(PROGRESS_SLIDER_SIZE);
225  progressSlider.setValue(0);
226  //Manage the gstreamer video position when a user is dragging the slider in the panel.
227  progressSlider.addChangeListener(new ChangeListener() {
228  @Override
229  public void stateChanged(ChangeEvent e) {
230  if (progressSlider.getValueIsAdjusting()) {
231  long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
232  double relativePosition = progressSlider.getValue() * 1.0 / PROGRESS_SLIDER_SIZE;
233  long newStartTime = (long) (relativePosition * duration);
234  double playBackRate = getPlayBackRate();
235  gstPlayBin.seek(playBackRate,
236  Format.TIME,
237  //FLUSH - flushes the pipeline
238  //ACCURATE - video will seek exactly to the position requested
239  EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
240  //Set the start position to newTime
241  SeekType.SET, newStartTime,
242  //Do nothing for the end position
243  SeekType.NONE, -1);
244  //Keep constantly updating the time label so users have a sense of
245  //where the slider they are dragging is in relation to the video time
246  updateTimeLabel(newStartTime, duration);
247  }
248  }
249  });
250  //Manage the audio level when the user is adjusting the volumn slider
251  audioSlider.addChangeListener((ChangeEvent event) -> {
252  if (audioSlider.getValueIsAdjusting()) {
253  double audioPercent = (audioSlider.getValue() * 2.0) / 100.0;
254  gstPlayBin.setVolume(audioPercent);
255  }
256  });
257  errorListener = new Bus.ERROR() {
258  @Override
259  public void errorMessage(GstObject go, int i, String string) {
260  SwingUtilities.invokeLater(() -> {
261  enableComponents(false);
262  infoLabel.setText(String.format(
263  "<html><font color='red'>%s</font></html>",
264  MEDIA_PLAYER_ERROR_STRING));
265  });
266  timer.stop();
267  }
268  };
269  stateChangeListener = new Bus.STATE_CHANGED() {
270  @Override
271  public void stateChanged(GstObject go, State oldState, State currentState, State pendingState) {
272  if (State.PLAYING.equals(currentState)) {
273  SwingUtilities.invokeLater(() -> {
274  playButton.setText("||");
275  });
276  } else {
277  SwingUtilities.invokeLater(() -> {
278  playButton.setText("►");
279  });
280  }
281  }
282  };
283  endOfStreamListener = new Bus.EOS() {
284  @Override
285  public void endOfStream(GstObject go) {
286  gstPlayBin.seek(ClockTime.ZERO);
290  Gst.getExecutor().submit(() -> gstPlayBin.pause());
291  }
292  };
293  }
294 
301  @NbBundle.Messages({"GstVideoPanel.noOpenCase.errMsg=No open case available."})
302  void loadFile(final AbstractFile file) {
303  //Ensure everything is back in the initial state
304  infoLabel.setText("");
305  if (file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC)) {
306  infoLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.setupVideo.infoLabel.text"));
307  return;
308  }
309 
310  try {
311  //Pushing off initialization to the background
312  extractMediaWorker = new ExtractMedia(file, VideoUtils.getVideoFileInTempDir(file));
313  extractMediaWorker.execute();
314  } catch (NoCurrentCaseException ex) {
315  logger.log(Level.SEVERE, "Exception while getting open case.", ex); //NON-NLS
316  infoLabel.setText(String.format("<html><font color='red'>%s</font></html>", Bundle.GstVideoPanel_noOpenCase_errMsg()));
317  enableComponents(false);
318  }
319  }
320 
325  @NbBundle.Messages({
326  "MediaPlayerPanel.noSupport=File not supported."
327  })
328  void resetComponents() {
329  progressLabel.setText(String.format("%s/%s", Bundle.MediaPlayerPanel_unknownTime(),
330  Bundle.MediaPlayerPanel_unknownTime()));
331  infoLabel.setText(Bundle.MediaPlayerPanel_noSupport());
332  progressSlider.setValue(0);
333  }
334 
338  void reset() {
339  if (extractMediaWorker != null) {
340  extractMediaWorker.cancel(true);
341  }
342  timer.stop();
343  if (gstPlayBin != null) {
344  gstPlayBin.stop();
345  gstPlayBin.getBus().disconnect(endOfStreamListener);
346  gstPlayBin.getBus().disconnect(stateChangeListener);
347  gstPlayBin.getBus().disconnect(errorListener);
348  gstPlayBin.dispose();
349  fxAppSink.clear();
350  gstPlayBin = null;
351  }
352  videoPanel.removeAll();
353  resetComponents();
354  enableComponents(false);
355  }
356 
357  private void enableComponents(boolean isEnabled) {
358  playButton.setEnabled(isEnabled);
359  progressSlider.setEnabled(isEnabled);
360  videoPanel.setEnabled(isEnabled);
361  audioSlider.setEnabled(isEnabled);
362  rewindButton.setEnabled(isEnabled);
363  fastForwardButton.setEnabled(isEnabled);
364  playBackSpeedComboBox.setEnabled(isEnabled);
365  }
366 
367  @Override
368  public List<String> getSupportedExtensions() {
369  return Arrays.asList(FILE_EXTENSIONS.clone());
370  }
371 
372  @Override
373  public List<String> getSupportedMimeTypes() {
374  return MIME_TYPES;
375  }
376 
377  @Override
378  public boolean isSupported(AbstractFile file) {
379  String extension = file.getNameExtension();
396  if (getSupportedExtensions().contains("." + extension)) {
397  SortedSet<String> mimeTypes = new TreeSet<>(getSupportedMimeTypes());
398  try {
399  String mimeType = new FileTypeDetector().getMIMEType(file);
400  return mimeTypes.contains(mimeType);
402  logger.log(Level.WARNING, "Failed to look up mimetype for " + file.getName() + " using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
403  if (!mimeTypes.isEmpty() && file.isMimeType(mimeTypes) == AbstractFile.MimeMatchEnum.TRUE) {
404  return true;
405  }
406  }
407 
408  return getSupportedExtensions().contains("." + extension);
409  }
410  return false;
411  }
412 
420  private void updateTimeLabel(long start, long total) {
421  progressLabel.setText(formatTime(start) + "/" + formatTime(total));
422  }
423 
429  private double getPlayBackRate() {
430  int selectIndex = playBackSpeedComboBox.getSelectedIndex();
431  String selectText = playBackSpeedComboBox.getItemAt(selectIndex);
432  return Double.valueOf(selectText.substring(0, selectText.length() - 1));
433  }
434 
438  @NbBundle.Messages({
439  "MediaPlayerPanel.unknownTime=Unknown",
440  "MediaPlayerPanel.timeFormat=%02d:%02d:%02d"
441  })
442  private String formatTime(long ns) {
443  if (ns == -1) {
444  return Bundle.MediaPlayerPanel_unknownTime();
445  }
446 
447  long seconds = TimeUnit.SECONDS.convert(ns, TimeUnit.NANOSECONDS);
448  long hours = TimeUnit.HOURS.convert(seconds, TimeUnit.SECONDS);
449  seconds -= TimeUnit.SECONDS.convert(hours, TimeUnit.HOURS);
450  long minutes = TimeUnit.MINUTES.convert(seconds, TimeUnit.SECONDS);
451  seconds -= TimeUnit.SECONDS.convert(minutes, TimeUnit.MINUTES);
452 
453  return String.format(Bundle.MediaPlayerPanel_timeFormat(), hours, minutes, seconds);
454  }
455 
460  private class ExtractMedia extends SwingWorker<Void, Void> {
461 
462  private ProgressHandle progress;
463  private final AbstractFile sourceFile;
464  private final java.io.File tempFile;
465 
466  ExtractMedia(AbstractFile sFile, File jFile) {
467  this.sourceFile = sFile;
468  this.tempFile = jFile;
469  }
470 
471  @Override
472  protected Void doInBackground() throws Exception {
473  if (!tempFile.exists() || tempFile.length() < sourceFile.getSize()) {
474  progress = ProgressHandle.createHandle(NbBundle.getMessage(MediaPlayerPanel.class, "GstVideoPanel.ExtractMedia.progress.buffering", sourceFile.getName()), () -> this.cancel(true));
475 
476  SwingUtilities.invokeLater(() -> {
477  progressLabel.setText(NbBundle.getMessage(this.getClass(), "GstVideoPanel.progress.buffering"));
478  });
479 
480  progress.start(100);
481  try {
482  Files.createParentDirs(tempFile);
483  ContentUtils.writeToFile(sourceFile, tempFile, progress, this, true);
484  } catch (IOException ex) {
485  logger.log(Level.WARNING, "Error creating parent directory for copying video/audio in temp directory", ex); //NON-NLS
486  } finally {
487  progress.finish();
488  }
489  }
490  return null;
491  }
492 
493  /*
494  * Initialize the playback components if the extraction was successful.
495  */
496  @Override
497  protected void done() {
498  try {
499  super.get();
500 
501  if (this.isCancelled()) {
502  return;
503  }
504 
505  // Initialize Gstreamer. It is safe to call this for every file.
506  // It was moved here from the constructor because having it happen
507  // earlier resulted in conflicts on Linux.
508  Gst.init();
509 
510  //Video is ready for playback. Create new components
511  gstPlayBin = new PlayBin("VideoPlayer", tempFile.toURI());
512  //Configure event handling
513  Bus playBinBus = gstPlayBin.getBus();
514  playBinBus.connect(endOfStreamListener);
515  playBinBus.connect(stateChangeListener);
516  playBinBus.connect(errorListener);
517 
518  if (this.isCancelled()) {
519  return;
520  }
521 
522  JFXPanel fxPanel = new JFXPanel();
523  videoPanel.removeAll();
524  videoPanel.setLayout(new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
525  videoPanel.add(fxPanel);
526  fxAppSink = new JavaFxAppSink("JavaFxAppSink", fxPanel);
527  gstPlayBin.setVideoSink(fxAppSink);
528 
529  if (this.isCancelled()) {
530  return;
531  }
532 
533  gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0);
534  gstPlayBin.pause();
535 
536  timer.start();
537  enableComponents(true);
538  } catch (CancellationException ex) {
539  logger.log(Level.INFO, "Media buffering was canceled."); //NON-NLS
540  } catch (InterruptedException ex) {
541  logger.log(Level.INFO, "Media buffering was interrupted."); //NON-NLS
542  } catch (ExecutionException ex) {
543  logger.log(Level.SEVERE, "Fatal error during media buffering.", ex); //NON-NLS
544  }
545  }
546  }
547 
551  private class VideoPanelUpdater implements ActionListener {
552 
553  @Override
554  public void actionPerformed(ActionEvent e) {
555  if (!progressSlider.getValueIsAdjusting()) {
556  sliderLock.acquireUninterruptibly();
557  long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
558  long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
564  if (duration >= 0 && position >= 0) {
565  double relativePosition = (double) position / duration;
566  progressSlider.setValue((int) (relativePosition * PROGRESS_SLIDER_SIZE));
567  }
568 
569  SwingUtilities.invokeLater(() -> {
570  updateTimeLabel(position, duration);
571  });
572  sliderLock.release();
573  }
574  }
575  }
576 
581 
582  //Thumb configurations
583  private final Color thumbColor;
584  private final Dimension thumbDimension;
585 
586  //Track configurations
587  //Progress bar can be bisected into a seen group
588  //and an unseen group.
589  private final Color unseen;
590  private final Color seen;
591 
600  public CircularJSliderConfiguration(Dimension thumbDimension) {
601  Color lightBlue = new Color(0, 130, 255);
602 
603  seen = lightBlue;
604  unseen = Color.LIGHT_GRAY;
605 
606  thumbColor = lightBlue;
607 
608  this.thumbDimension = new Dimension(thumbDimension);
609  }
610 
611  public Color getThumbColor() {
612  return thumbColor;
613  }
614 
615  public Color getUnseenTrackColor() {
616  return unseen;
617  }
618 
619  public Color getSeenTrackColor() {
620  return seen;
621  }
622 
623  public Dimension getThumbDimension() {
624  return new Dimension(thumbDimension);
625  }
626  }
627 
631  private class CircularJSliderUI extends BasicSliderUI {
632 
634 
644  public CircularJSliderUI(JSlider slider, CircularJSliderConfiguration config) {
645  super(slider);
646  this.config = config;
647  }
648 
649  @Override
650  protected Dimension getThumbSize() {
651  return config.getThumbDimension();
652  }
653 
657  @Override
658  public void paintThumb(Graphics graphic) {
659  Rectangle thumb = this.thumbRect;
660 
661  Color original = graphic.getColor();
662 
663  //Change the thumb view from the rectangle
664  //controller to an oval.
665  graphic.setColor(config.getThumbColor());
666  Dimension thumbDimension = config.getThumbDimension();
667  graphic.fillOval(thumb.x, thumb.y, thumbDimension.width, thumbDimension.height);
668 
669  //Preserve the graphics original color
670  graphic.setColor(original);
671  }
672 
673  @Override
674  public void paintTrack(Graphics graphic) {
675  //This rectangle is the bounding box for the progress bar
676  //portion of the slider. The track is painted in the middle
677  //of this rectangle and the thumb laid overtop.
678  Rectangle track = this.trackRect;
679 
680  //Get the location of the thumb, this point splits the
681  //progress bar into 2 line segments, seen and unseen.
682  Rectangle thumb = this.thumbRect;
683  int thumbX = thumb.x;
684  int thumbY = thumb.y;
685 
686  Color original = graphic.getColor();
687 
688  //Paint the seen side
689  graphic.setColor(config.getSeenTrackColor());
690  graphic.drawLine(track.x, track.y + track.height / 2,
691  thumbX, thumbY + track.height / 2);
692 
693  //Paint the unseen side
694  graphic.setColor(config.getUnseenTrackColor());
695  graphic.drawLine(thumbX, thumbY + track.height / 2,
696  track.x + track.width, track.y + track.height / 2);
697 
698  //Preserve the graphics color.
699  graphic.setColor(original);
700  }
701 
702  @Override
703  protected TrackListener createTrackListener(JSlider slider) {
704  return new CustomTrackListener();
705  }
706 
707  @Override
708  protected void scrollDueToClickInTrack(int direction) {
709  //Set the thumb position to the mouse press location, as opposed
710  //to the closest "block" which is the default behavior.
711  Point mousePosition = slider.getMousePosition();
712  if (mousePosition == null) {
713  return;
714  }
715  int value = this.valueForXPosition(mousePosition.x);
716 
717  //Lock the slider down, which is a shared resource.
718  //The VideoPanelUpdater (dedicated thread) keeps the
719  //slider in sync with the video position, so without
720  //proper locking our change could be overwritten.
721  sliderLock.acquireUninterruptibly();
722  slider.setValueIsAdjusting(true);
723  slider.setValue(value);
724  slider.setValueIsAdjusting(false);
725  sliderLock.release();
726  }
727 
731  @Override
732  public void update(Graphics graphic, JComponent component) {
733  if (graphic instanceof Graphics2D) {
734  Graphics2D graphic2 = (Graphics2D) graphic;
735  graphic2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
736  RenderingHints.VALUE_ANTIALIAS_ON);
737  }
738 
739  super.update(graphic, component);
740  }
741 
749  private class CustomTrackListener extends CircularJSliderUI.TrackListener {
750 
751  @Override
752  public void mousePressed(MouseEvent e) {
753  if (!slider.isEnabled() || !SwingUtilities.isLeftMouseButton(e)) {
754  return;
755  }
756  //Snap the thumb to position of the mouse
757  scrollDueToClickInTrack(0);
758 
759  //Pause the video for convenience
760  gstPlayBin.pause();
761 
762  //Handle the event as normal.
763  super.mousePressed(e);
764  }
765 
766  @Override
767  public void mouseReleased(MouseEvent e) {
768  if (!slider.isEnabled() || !SwingUtilities.isLeftMouseButton(e)) {
769  return;
770  }
771 
772  super.mouseReleased(e);
773 
774  //Unpause once the mouse has been released.
775  gstPlayBin.play();
776  }
777  }
778  }
779 
785  @SuppressWarnings("unchecked")
786  // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
787  private void initComponents() {
788  java.awt.GridBagConstraints gridBagConstraints;
789 
790  videoPanel = new javax.swing.JPanel();
791  controlPanel = new javax.swing.JPanel();
792  progressSlider = new javax.swing.JSlider();
793  progressLabel = new javax.swing.JLabel();
794  buttonPanel = new javax.swing.JPanel();
795  playButton = new javax.swing.JButton();
796  fastForwardButton = new javax.swing.JButton();
797  rewindButton = new javax.swing.JButton();
798  VolumeIcon = new javax.swing.JLabel();
799  audioSlider = new javax.swing.JSlider();
800  infoLabel = new javax.swing.JLabel();
801  playBackPanel = new javax.swing.JPanel();
802  playBackSpeedComboBox = new javax.swing.JComboBox<>();
803  playBackSpeedLabel = new javax.swing.JLabel();
804 
805  javax.swing.GroupLayout videoPanelLayout = new javax.swing.GroupLayout(videoPanel);
806  videoPanel.setLayout(videoPanelLayout);
807  videoPanelLayout.setHorizontalGroup(
808  videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
809  .addGap(0, 0, Short.MAX_VALUE)
810  );
811  videoPanelLayout.setVerticalGroup(
812  videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
813  .addGap(0, 131, Short.MAX_VALUE)
814  );
815 
816  progressSlider.setValue(0);
817  progressSlider.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
818  progressSlider.setDoubleBuffered(true);
819  progressSlider.setMinimumSize(new java.awt.Dimension(36, 21));
820  progressSlider.setPreferredSize(new java.awt.Dimension(200, 21));
821  progressSlider.setUI(new CircularJSliderUI(progressSlider, new CircularJSliderConfiguration(new Dimension(18,18))));
822 
823  org.openide.awt.Mnemonics.setLocalizedText(progressLabel, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.progressLabel.text")); // NOI18N
824 
825  buttonPanel.setLayout(new java.awt.GridBagLayout());
826 
827  org.openide.awt.Mnemonics.setLocalizedText(playButton, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.playButton.text")); // NOI18N
828  playButton.addActionListener(new java.awt.event.ActionListener() {
829  public void actionPerformed(java.awt.event.ActionEvent evt) {
830  playButtonActionPerformed(evt);
831  }
832  });
833  gridBagConstraints = new java.awt.GridBagConstraints();
834  gridBagConstraints.gridx = 1;
835  gridBagConstraints.gridy = 0;
836  gridBagConstraints.ipadx = 21;
837  gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
838  gridBagConstraints.insets = new java.awt.Insets(5, 6, 0, 0);
839  buttonPanel.add(playButton, gridBagConstraints);
840 
841  org.openide.awt.Mnemonics.setLocalizedText(fastForwardButton, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.fastForwardButton.text")); // NOI18N
842  fastForwardButton.addActionListener(new java.awt.event.ActionListener() {
843  public void actionPerformed(java.awt.event.ActionEvent evt) {
844  fastForwardButtonActionPerformed(evt);
845  }
846  });
847  gridBagConstraints = new java.awt.GridBagConstraints();
848  gridBagConstraints.gridx = 2;
849  gridBagConstraints.gridy = 0;
850  gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
851  gridBagConstraints.insets = new java.awt.Insets(5, 6, 0, 0);
852  buttonPanel.add(fastForwardButton, gridBagConstraints);
853 
854  org.openide.awt.Mnemonics.setLocalizedText(rewindButton, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.rewindButton.text")); // NOI18N
855  rewindButton.addActionListener(new java.awt.event.ActionListener() {
856  public void actionPerformed(java.awt.event.ActionEvent evt) {
857  rewindButtonActionPerformed(evt);
858  }
859  });
860  gridBagConstraints = new java.awt.GridBagConstraints();
861  gridBagConstraints.gridx = 0;
862  gridBagConstraints.gridy = 0;
863  gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
864  gridBagConstraints.insets = new java.awt.Insets(5, 0, 1, 0);
865  buttonPanel.add(rewindButton, gridBagConstraints);
866 
867  org.openide.awt.Mnemonics.setLocalizedText(VolumeIcon, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.VolumeIcon.text")); // NOI18N
868  VolumeIcon.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
869  gridBagConstraints = new java.awt.GridBagConstraints();
870  gridBagConstraints.gridx = 3;
871  gridBagConstraints.gridy = 0;
872  gridBagConstraints.ipadx = 8;
873  gridBagConstraints.ipady = 7;
874  gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
875  gridBagConstraints.insets = new java.awt.Insets(6, 14, 0, 0);
876  buttonPanel.add(VolumeIcon, gridBagConstraints);
877 
878  audioSlider.setMajorTickSpacing(10);
879  audioSlider.setMaximum(50);
880  audioSlider.setMinorTickSpacing(5);
881  audioSlider.setToolTipText(org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.audioSlider.toolTipText")); // NOI18N
882  audioSlider.setValue(25);
883  audioSlider.setMinimumSize(new java.awt.Dimension(200, 21));
884  audioSlider.setPreferredSize(new java.awt.Dimension(200, 21));
885  audioSlider.setUI(new CircularJSliderUI(audioSlider, new CircularJSliderConfiguration(new Dimension(15,15))));
886  gridBagConstraints = new java.awt.GridBagConstraints();
887  gridBagConstraints.gridx = 4;
888  gridBagConstraints.gridy = 0;
889  gridBagConstraints.ipadx = -116;
890  gridBagConstraints.ipady = 7;
891  gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
892  gridBagConstraints.insets = new java.awt.Insets(3, 1, 0, 10);
893  buttonPanel.add(audioSlider, gridBagConstraints);
894 
895  infoLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
896  org.openide.awt.Mnemonics.setLocalizedText(infoLabel, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.infoLabel.text")); // NOI18N
897  infoLabel.setCursor(new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
898 
899  playBackSpeedComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "0.25x", "0.50x", "0.75x", "1x", "1.25x", "1.50x", "1.75x", "2x" }));
900  playBackSpeedComboBox.setSelectedIndex(3);
901  playBackSpeedComboBox.setMaximumSize(new java.awt.Dimension(53, 23));
902  playBackSpeedComboBox.setMinimumSize(new java.awt.Dimension(53, 23));
903  playBackSpeedComboBox.setPreferredSize(new java.awt.Dimension(53, 23));
904  playBackSpeedComboBox.addActionListener(new java.awt.event.ActionListener() {
905  public void actionPerformed(java.awt.event.ActionEvent evt) {
906  playBackSpeedComboBoxActionPerformed(evt);
907  }
908  });
909 
910  org.openide.awt.Mnemonics.setLocalizedText(playBackSpeedLabel, org.openide.util.NbBundle.getMessage(MediaPlayerPanel.class, "MediaPlayerPanel.playBackSpeedLabel.text")); // NOI18N
911 
912  javax.swing.GroupLayout playBackPanelLayout = new javax.swing.GroupLayout(playBackPanel);
913  playBackPanel.setLayout(playBackPanelLayout);
914  playBackPanelLayout.setHorizontalGroup(
915  playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
916  .addGroup(playBackPanelLayout.createSequentialGroup()
917  .addComponent(playBackSpeedLabel)
918  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
919  .addComponent(playBackSpeedComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
920  .addGap(13, 13, 13))
921  );
922  playBackPanelLayout.setVerticalGroup(
923  playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
924  .addGroup(playBackPanelLayout.createSequentialGroup()
925  .addGap(6, 6, 6)
926  .addGroup(playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
927  .addComponent(playBackSpeedComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
928  .addComponent(playBackSpeedLabel))
929  .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
930  );
931 
932  javax.swing.GroupLayout controlPanelLayout = new javax.swing.GroupLayout(controlPanel);
933  controlPanel.setLayout(controlPanelLayout);
934  controlPanelLayout.setHorizontalGroup(
935  controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
936  .addGroup(controlPanelLayout.createSequentialGroup()
937  .addContainerGap()
938  .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
939  .addComponent(infoLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
940  .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, controlPanelLayout.createSequentialGroup()
941  .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
942  .addComponent(buttonPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
943  .addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 623, Short.MAX_VALUE))
944  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
945  .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
946  .addComponent(progressLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
947  .addComponent(playBackPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
948  .addGap(10, 10, 10)))
949  .addGap(0, 0, 0))
950  );
951  controlPanelLayout.setVerticalGroup(
952  controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
953  .addGroup(controlPanelLayout.createSequentialGroup()
954  .addGap(0, 0, 0)
955  .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
956  .addComponent(progressLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
957  .addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
958  .addGap(5, 5, 5)
959  .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
960  .addComponent(buttonPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
961  .addComponent(playBackPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
962  .addGap(14, 14, 14)
963  .addComponent(infoLabel))
964  );
965 
966  javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
967  this.setLayout(layout);
968  layout.setHorizontalGroup(
969  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
970  .addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
971  .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
972  );
973  layout.setVerticalGroup(
974  layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
975  .addGroup(layout.createSequentialGroup()
976  .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
977  .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
978  .addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
979  );
980  }// </editor-fold>//GEN-END:initComponents
981 
982  private void rewindButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rewindButtonActionPerformed
983  long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
984  //Skip 30 seconds.
985  long rewindDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
986  //Ensure new video position is within bounds
987  long newTime = Math.max(currentTime - rewindDelta, 0);
988  double playBackRate = getPlayBackRate();
989  gstPlayBin.seek(playBackRate,
990  Format.TIME,
991  //FLUSH - flushes the pipeline
992  //ACCURATE - video will seek exactly to the position requested
993  EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
994  //Set the start position to newTime
995  SeekType.SET, newTime,
996  //Do nothing for the end position
997  SeekType.NONE, -1);
998  }//GEN-LAST:event_rewindButtonActionPerformed
999 
1000  private void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fastForwardButtonActionPerformed
1001  long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
1002  long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
1003  //Skip 30 seconds.
1004  long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
1005 
1006  //Ignore fast forward requests if there are less than 30 seconds left.
1007  if (currentTime + fastForwardDelta >= duration) {
1008  return;
1009  }
1010 
1011  long newTime = currentTime + fastForwardDelta;
1012  double playBackRate = getPlayBackRate();
1013  gstPlayBin.seek(playBackRate,
1014  Format.TIME,
1015  //FLUSH - flushes the pipeline
1016  //ACCURATE - video will seek exactly to the position requested
1017  EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
1018  //Set the start position to newTime
1019  SeekType.SET, newTime,
1020  //Do nothing for the end position
1021  SeekType.NONE, -1);
1022  }//GEN-LAST:event_fastForwardButtonActionPerformed
1023 
1024  private void playButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playButtonActionPerformed
1025  if (gstPlayBin.isPlaying()) {
1026  gstPlayBin.pause();
1027  } else {
1028  double playBackRate = getPlayBackRate();
1029  long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
1030  //Set playback rate before play.
1031  gstPlayBin.seek(playBackRate,
1032  Format.TIME,
1033  //FLUSH - flushes the pipeline
1034  //ACCURATE - video will seek exactly to the position requested
1035  EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
1036  //Set the start position to newTime
1037  SeekType.SET, currentTime,
1038  //Do nothing for the end position
1039  SeekType.NONE, -1);
1040  gstPlayBin.play();
1041  }
1042  }//GEN-LAST:event_playButtonActionPerformed
1043 
1044  private void playBackSpeedComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_playBackSpeedComboBoxActionPerformed
1045  double playBackRate = getPlayBackRate();
1046  long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
1047  gstPlayBin.seek(playBackRate,
1048  Format.TIME,
1049  //FLUSH - flushes the pipeline
1050  //ACCURATE - video will seek exactly to the position requested
1051  EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
1052  //Set the position to the currentTime, we are only adjusting the
1053  //playback rate.
1054  SeekType.SET, currentTime,
1055  SeekType.NONE, 0);
1056  }//GEN-LAST:event_playBackSpeedComboBoxActionPerformed
1057 
1058  // Variables declaration - do not modify//GEN-BEGIN:variables
1059  private javax.swing.JLabel VolumeIcon;
1060  private javax.swing.JSlider audioSlider;
1061  private javax.swing.JPanel buttonPanel;
1062  private javax.swing.JPanel controlPanel;
1063  private javax.swing.JButton fastForwardButton;
1064  private javax.swing.JLabel infoLabel;
1065  private javax.swing.JPanel playBackPanel;
1066  private javax.swing.JComboBox<String> playBackSpeedComboBox;
1067  private javax.swing.JLabel playBackSpeedLabel;
1068  private javax.swing.JButton playButton;
1069  private javax.swing.JLabel progressLabel;
1070  private javax.swing.JSlider progressSlider;
1071  private javax.swing.JButton rewindButton;
1072  private javax.swing.JPanel videoPanel;
1073  // End of variables declaration//GEN-END:variables
1074 }
void playButtonActionPerformed(java.awt.event.ActionEvent evt)
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
void playBackSpeedComboBoxActionPerformed(java.awt.event.ActionEvent evt)
CircularJSliderUI(JSlider slider, CircularJSliderConfiguration config)
void rewindButtonActionPerformed(java.awt.event.ActionEvent evt)
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt)

Copyright © 2012-2019 Basis Technology. Generated on: Tue Jan 7 2020
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.