19 package org.sleuthkit.autopsy.contentviewers;
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.awt.event.MouseListener;
34 import java.io.IOException;
35 import java.util.Arrays;
36 import java.util.EnumSet;
37 import java.util.List;
38 import java.util.SortedSet;
39 import java.util.TreeSet;
40 import java.util.concurrent.CancellationException;
41 import java.util.concurrent.ExecutionException;
42 import java.util.concurrent.Semaphore;
43 import java.util.concurrent.TimeUnit;
44 import java.util.logging.Level;
45 import javax.swing.BoxLayout;
46 import javax.swing.JPanel;
47 import javax.swing.SwingWorker;
48 import javax.swing.Timer;
49 import javax.swing.event.ChangeEvent;
50 import org.freedesktop.gstreamer.Bus;
51 import org.freedesktop.gstreamer.Gst;
52 import org.freedesktop.gstreamer.GstObject;
53 import org.freedesktop.gstreamer.State;
54 import org.freedesktop.gstreamer.elements.PlayBin;
55 import org.netbeans.api.progress.ProgressHandle;
56 import org.openide.util.NbBundle;
64 import javafx.embed.swing.JFXPanel;
65 import javax.swing.ImageIcon;
66 import javax.swing.JComponent;
67 import javax.swing.JSlider;
68 import javax.swing.SwingUtilities;
69 import javax.swing.event.ChangeListener;
70 import javax.swing.plaf.basic.BasicSliderUI;
71 import javax.swing.plaf.basic.BasicSliderUI.TrackListener;
72 import org.freedesktop.gstreamer.ClockTime;
73 import org.freedesktop.gstreamer.Format;
74 import org.freedesktop.gstreamer.GstException;
75 import org.freedesktop.gstreamer.event.SeekFlags;
76 import org.freedesktop.gstreamer.event.SeekType;
85 @SuppressWarnings(
"PMD.SingularField")
86 public class
MediaPlayerPanel extends JPanel implements MediaFileViewer.MediaViewPanel {
89 private static final String[] FILE_EXTENSIONS =
new String[]{
118 private static final List<String> MIME_TYPES = Arrays.asList(
151 "video/x-intel-h263",
167 "video/x-msvideocodec",
192 private static final String MEDIA_PLAYER_ERROR_STRING = NbBundle.getMessage(
MediaPlayerPanel.class,
193 "GstVideoPanel.cannotProcFile.err");
205 private static final int PROGRESS_SLIDER_SIZE = 2000;
206 private static final int SKIP_IN_SECONDS = 30;
208 private final ImageIcon playIcon =
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/contentviewers/images/Play-arrow-01.png"));
209 private final ImageIcon pauseIcon =
new ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/contentviewers/images/Pause-01.png"));
218 private static volatile boolean IS_GST_ENABLED =
true;
225 customizeComponents();
228 sliderLock =
new Semaphore(1,
true);
232 enableComponents(
false);
233 progressSlider.setMinimum(0);
234 progressSlider.setMaximum(PROGRESS_SLIDER_SIZE);
235 progressSlider.setValue(0);
237 progressSlider.addChangeListener(
new ChangeListener() {
239 public void stateChanged(ChangeEvent e) {
240 if (progressSlider.getValueIsAdjusting() && gstPlayBin != null) {
241 long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
242 double relativePosition = progressSlider.getValue() * 1.0 / PROGRESS_SLIDER_SIZE;
243 long newStartTime = (long) (relativePosition * duration);
244 double playBackRate = getPlayBackRate();
245 gstPlayBin.seek(playBackRate,
249 EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
251 SeekType.SET, newStartTime,
256 updateTimeLabel(newStartTime, duration);
261 progressSlider.addMouseListener(
new MouseListener() {
262 private State previousState = State.NULL;
265 public void mousePressed(MouseEvent e) {
266 if (gstPlayBin != null) {
267 previousState = gstPlayBin.getState();
273 public void mouseReleased(MouseEvent e) {
274 if (previousState.equals(State.PLAYING) && gstPlayBin != null) {
277 previousState = State.NULL;
281 public void mouseClicked(MouseEvent e) {
285 public void mouseEntered(MouseEvent e) {
289 public void mouseExited(MouseEvent e) {
294 audioSlider.addChangeListener((ChangeEvent event) -> {
295 if (audioSlider.getValueIsAdjusting() && gstPlayBin != null) {
296 double audioPercent = (audioSlider.getValue() * 2.0) / 100.0;
297 gstPlayBin.setVolume(audioPercent);
300 errorListener =
new Bus.ERROR() {
302 public void errorMessage(GstObject go,
int i, String
string) {
303 SwingUtilities.invokeLater(() -> {
304 enableComponents(
false);
305 infoLabel.setText(String.format(
306 "<html><font color='red'>%s</font></html>",
307 MEDIA_PLAYER_ERROR_STRING));
312 stateChangeListener =
new Bus.STATE_CHANGED() {
314 public void stateChanged(GstObject go, State oldState, State currentState, State pendingState) {
315 if (State.PLAYING.equals(currentState)) {
316 SwingUtilities.invokeLater(() -> {
317 playButton.setIcon(pauseIcon);
320 SwingUtilities.invokeLater(() -> {
321 playButton.setIcon(playIcon);
326 endOfStreamListener =
new Bus.EOS() {
328 public void endOfStream(GstObject go) {
329 if (gstPlayBin != null) {
330 gstPlayBin.seek(ClockTime.ZERO);
334 Gst.getExecutor().submit(() -> gstPlayBin.pause());
346 @NbBundle.Messages({
"GstVideoPanel.noOpenCase.errMsg=No open case available."})
347 void loadFile(
final AbstractFile file) {
349 infoLabel.setText(
"");
350 if (file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC)) {
351 infoLabel.setText(NbBundle.getMessage(
this.getClass(),
"GstVideoPanel.setupVideo.infoLabel.text"));
357 extractMediaWorker =
new ExtractMedia(file, VideoUtils.getVideoFileInTempDir(file));
358 extractMediaWorker.execute();
359 }
catch (NoCurrentCaseException ex) {
360 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
361 infoLabel.setText(String.format(
"<html><font color='red'>%s</font></html>", Bundle.GstVideoPanel_noOpenCase_errMsg()));
362 enableComponents(
false);
371 "MediaPlayerPanel.noSupport=File not supported."
373 void resetComponents() {
374 progressLabel.setText(String.format(
"%s/%s", Bundle.MediaPlayerPanel_unknownTime(),
375 Bundle.MediaPlayerPanel_unknownTime()));
376 infoLabel.setText(Bundle.MediaPlayerPanel_noSupport());
377 progressSlider.setValue(0);
384 if (extractMediaWorker != null) {
385 extractMediaWorker.cancel(
true);
388 if (gstPlayBin != null) {
389 Gst.getExecutor().submit(() -> {
391 gstPlayBin.getBus().disconnect(endOfStreamListener);
392 gstPlayBin.getBus().disconnect(stateChangeListener);
393 gstPlayBin.getBus().disconnect(errorListener);
394 gstPlayBin.getBus().dispose();
395 gstPlayBin.dispose();
400 videoPanel.removeAll();
402 enableComponents(
false);
406 playButton.setEnabled(isEnabled);
407 progressSlider.setEnabled(isEnabled);
408 videoPanel.setEnabled(isEnabled);
409 audioSlider.setEnabled(isEnabled);
410 rewindButton.setEnabled(isEnabled);
411 fastForwardButton.setEnabled(isEnabled);
412 playBackSpeedComboBox.setEnabled(isEnabled);
417 return Arrays.asList(FILE_EXTENSIONS.clone());
427 if (!IS_GST_ENABLED) {
431 String extension = file.getNameExtension();
448 if (getSupportedExtensions().contains(
"." + extension)) {
449 SortedSet<String> mimeTypes =
new TreeSet<>(getSupportedMimeTypes());
452 return mimeTypes.contains(mimeType);
454 logger.log(Level.WARNING,
"Failed to look up mimetype for " + file.getName() +
" using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
455 if (!mimeTypes.isEmpty() && file.isMimeType(mimeTypes) == AbstractFile.MimeMatchEnum.TRUE) {
460 return getSupportedExtensions().contains(
"." + extension);
473 progressLabel.setText(formatTime(start) +
"/" + formatTime(total));
482 int selectIndex = playBackSpeedComboBox.getSelectedIndex();
483 String selectText = playBackSpeedComboBox.getItemAt(selectIndex);
484 return Double.valueOf(selectText.substring(0, selectText.length() - 1));
491 "MediaPlayerPanel.unknownTime=Unknown",
492 "MediaPlayerPanel.timeFormat=%02d:%02d:%02d"
496 return Bundle.MediaPlayerPanel_unknownTime();
499 long seconds = TimeUnit.SECONDS.convert(ns, TimeUnit.NANOSECONDS);
500 long hours = TimeUnit.HOURS.convert(seconds, TimeUnit.SECONDS);
501 seconds -= TimeUnit.SECONDS.convert(hours, TimeUnit.HOURS);
502 long minutes = TimeUnit.MINUTES.convert(seconds, TimeUnit.SECONDS);
503 seconds -= TimeUnit.SECONDS.convert(minutes, TimeUnit.MINUTES);
505 return String.format(Bundle.MediaPlayerPanel_timeFormat(), hours, minutes, seconds);
519 this.sourceFile = sFile;
520 this.tempFile = jFile;
525 if (!tempFile.exists() || tempFile.length() < sourceFile.getSize()) {
526 progress = ProgressHandle.createHandle(NbBundle.getMessage(
MediaPlayerPanel.class,
"GstVideoPanel.ExtractMedia.progress.buffering", sourceFile.getName()), () -> this.cancel(
true));
528 SwingUtilities.invokeLater(() -> {
529 progressLabel.setText(NbBundle.getMessage(
this.getClass(),
"GstVideoPanel.progress.buffering"));
534 Files.createParentDirs(tempFile);
536 }
catch (IOException ex) {
537 logger.log(Level.WARNING,
"Error creating parent directory for copying video/audio in temp directory", ex);
549 "MediaPlayerPanel.playbackDisabled=A problem was encountered with"
550 +
" the video and audio playback service. Video and audio "
551 +
"playback will be disabled for the remainder of the session."
558 if (this.isCancelled()) {
567 IS_GST_ENABLED =
false;
571 Gst.getExecutor().submit(() -> {
573 gstPlayBin =
new PlayBin(
"VideoPlayer", tempFile.toURI());
575 Bus playBinBus = gstPlayBin.getBus();
576 playBinBus.connect(endOfStreamListener);
577 playBinBus.connect(stateChangeListener);
578 playBinBus.connect(errorListener);
580 if (this.isCancelled()) {
584 JFXPanel fxPanel =
new JFXPanel();
585 videoPanel.removeAll();
586 videoPanel.setLayout(
new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
587 videoPanel.add(fxPanel);
588 fxAppSink =
new JavaFxAppSink(
"JavaFxAppSink", fxPanel);
589 if (gstPlayBin != null) {
590 gstPlayBin.setVideoSink(fxAppSink);
592 if (this.isCancelled()) {
595 if (gstPlayBin != null) {
596 gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0);
601 SwingUtilities.invokeLater(() -> {
602 enableComponents(
true);
605 }
catch (CancellationException ex) {
606 logger.log(Level.INFO,
"Media buffering was canceled.");
607 }
catch (InterruptedException ex) {
608 logger.log(Level.INFO,
"Media buffering was interrupted.");
609 }
catch (ExecutionException ex) {
610 logger.log(Level.SEVERE,
"Fatal error during media buffering.", ex);
622 if (!progressSlider.getValueIsAdjusting() && gstPlayBin != null) {
623 Gst.getExecutor().submit(() -> {
625 sliderLock.acquireUninterruptibly();
626 long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
627 long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
634 if (duration >= 0 && position >= 0) {
635 double relativePosition = (double) position / duration;
636 progressSlider.setValue((
int) (relativePosition * PROGRESS_SLIDER_SIZE));
639 SwingUtilities.invokeLater(() -> {
640 updateTimeLabel(position, duration);
643 sliderLock.release();
670 this.thumbDimension = thumbDimension;
673 Color lightBlue =
new Color(0, 130, 255);
674 thumbColor = lightBlue;
675 trackSeen = lightBlue;
676 trackUnseen = Color.LIGHT_GRAY;
681 return new Dimension(thumbDimension);
690 Rectangle thumb = this.thumbRect;
692 Color original = graphic.getColor();
696 graphic.setColor(thumbColor);
697 graphic.fillOval(thumb.x, thumb.y, thumbDimension.width, thumbDimension.height);
700 graphic.setColor(original);
708 Rectangle track = this.trackRect;
712 Rectangle thumb = this.thumbRect;
713 int thumbX = thumb.x;
714 int thumbY = thumb.y;
716 Color original = graphic.getColor();
719 graphic.setColor(trackSeen);
720 graphic.drawLine(track.x, track.y + track.height / 2,
721 thumbX, thumbY + track.height / 2);
724 graphic.setColor(trackUnseen);
725 graphic.drawLine(thumbX, thumbY + track.height / 2,
726 track.x + track.width, track.y + track.height / 2);
729 graphic.setColor(original);
742 return new TrackListener() {
744 public void mousePressed(MouseEvent e) {
745 if (!slider.isEnabled() || !SwingUtilities.isLeftMouseButton(e)) {
749 scrollDueToClickInTrack(0);
752 super.mousePressed(e);
761 Point mousePosition = slider.getMousePosition();
762 if (mousePosition == null) {
765 int value = this.valueForXPosition(mousePosition.x);
771 sliderLock.acquireUninterruptibly();
772 slider.setValueIsAdjusting(
true);
773 slider.setValue(value);
774 slider.setValueIsAdjusting(
false);
775 sliderLock.release();
782 public void update(Graphics graphic, JComponent component) {
783 if (graphic instanceof Graphics2D) {
784 Graphics2D graphic2 = (Graphics2D) graphic;
785 graphic2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
786 RenderingHints.VALUE_ANTIALIAS_ON);
789 super.update(graphic, component);
798 @SuppressWarnings(
"unchecked")
800 private
void initComponents() {
801 java.awt.GridBagConstraints gridBagConstraints;
803 videoPanel =
new javax.swing.JPanel();
804 controlPanel =
new javax.swing.JPanel();
805 progressSlider =
new javax.swing.JSlider();
806 progressLabel =
new javax.swing.JLabel();
807 buttonPanel =
new javax.swing.JPanel();
808 playButton =
new javax.swing.JButton();
809 fastForwardButton =
new javax.swing.JButton();
810 rewindButton =
new javax.swing.JButton();
811 VolumeIcon =
new javax.swing.JLabel();
812 audioSlider =
new javax.swing.JSlider();
813 infoLabel =
new javax.swing.JLabel();
814 playBackPanel =
new javax.swing.JPanel();
815 playBackSpeedComboBox =
new javax.swing.JComboBox<>();
816 playBackSpeedLabel =
new javax.swing.JLabel();
818 javax.swing.GroupLayout videoPanelLayout =
new javax.swing.GroupLayout(videoPanel);
819 videoPanel.setLayout(videoPanelLayout);
820 videoPanelLayout.setHorizontalGroup(
821 videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
822 .addGap(0, 0, Short.MAX_VALUE)
824 videoPanelLayout.setVerticalGroup(
825 videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
826 .addGap(0, 117, Short.MAX_VALUE)
829 progressSlider.setValue(0);
830 progressSlider.setCursor(
new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
831 progressSlider.setDoubleBuffered(
true);
832 progressSlider.setMinimumSize(
new java.awt.Dimension(36, 21));
833 progressSlider.setPreferredSize(
new java.awt.Dimension(200, 21));
834 progressSlider.setUI(
new CircularJSliderUI(progressSlider,
new Dimension(18,18)));
836 org.openide.awt.Mnemonics.setLocalizedText(progressLabel,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.progressLabel.text"));
838 buttonPanel.setLayout(
new java.awt.GridBagLayout());
840 playButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/contentviewers/images/Play-arrow-01.png")));
841 org.openide.awt.Mnemonics.setLocalizedText(playButton,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.playButton.text"));
842 playButton.setMaximumSize(
new java.awt.Dimension(53, 29));
843 playButton.setMinimumSize(
new java.awt.Dimension(53, 29));
844 playButton.setPreferredSize(
new java.awt.Dimension(49, 29));
845 playButton.addActionListener(
new java.awt.event.ActionListener() {
846 public void actionPerformed(java.awt.event.ActionEvent evt) {
847 playButtonActionPerformed(evt);
850 gridBagConstraints =
new java.awt.GridBagConstraints();
851 gridBagConstraints.gridx = 1;
852 gridBagConstraints.gridy = 0;
853 gridBagConstraints.ipadx = 21;
854 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
855 gridBagConstraints.insets =
new java.awt.Insets(5, 6, 0, 0);
856 buttonPanel.add(playButton, gridBagConstraints);
858 fastForwardButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/contentviewers/images/Fast-forward-01.png")));
859 org.openide.awt.Mnemonics.setLocalizedText(fastForwardButton,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.fastForwardButton.text"));
860 fastForwardButton.addActionListener(
new java.awt.event.ActionListener() {
861 public void actionPerformed(java.awt.event.ActionEvent evt) {
862 fastForwardButtonActionPerformed(evt);
865 gridBagConstraints =
new java.awt.GridBagConstraints();
866 gridBagConstraints.gridx = 2;
867 gridBagConstraints.gridy = 0;
868 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
869 gridBagConstraints.insets =
new java.awt.Insets(5, 6, 0, 0);
870 buttonPanel.add(fastForwardButton, gridBagConstraints);
872 rewindButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/contentviewers/images/Fast-rewind-01.png")));
873 org.openide.awt.Mnemonics.setLocalizedText(rewindButton,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.rewindButton.text"));
874 rewindButton.addActionListener(
new java.awt.event.ActionListener() {
875 public void actionPerformed(java.awt.event.ActionEvent evt) {
876 rewindButtonActionPerformed(evt);
879 gridBagConstraints =
new java.awt.GridBagConstraints();
880 gridBagConstraints.gridx = 0;
881 gridBagConstraints.gridy = 0;
882 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
883 gridBagConstraints.insets =
new java.awt.Insets(5, 0, 1, 0);
884 buttonPanel.add(rewindButton, gridBagConstraints);
886 org.openide.awt.Mnemonics.setLocalizedText(VolumeIcon,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.VolumeIcon.text"));
887 VolumeIcon.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
888 VolumeIcon.setMaximumSize(
new java.awt.Dimension(34, 29));
889 VolumeIcon.setMinimumSize(
new java.awt.Dimension(34, 29));
890 VolumeIcon.setPreferredSize(
new java.awt.Dimension(34, 19));
891 gridBagConstraints =
new java.awt.GridBagConstraints();
892 gridBagConstraints.gridx = 3;
893 gridBagConstraints.gridy = 0;
894 gridBagConstraints.ipadx = 8;
895 gridBagConstraints.ipady = 7;
896 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
897 gridBagConstraints.insets =
new java.awt.Insets(6, 14, 0, 0);
898 buttonPanel.add(VolumeIcon, gridBagConstraints);
900 audioSlider.setMajorTickSpacing(10);
901 audioSlider.setMaximum(50);
902 audioSlider.setMinorTickSpacing(5);
903 audioSlider.setToolTipText(
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.audioSlider.toolTipText"));
904 audioSlider.setValue(25);
905 audioSlider.setMaximumSize(
new java.awt.Dimension(32767, 19));
906 audioSlider.setMinimumSize(
new java.awt.Dimension(200, 19));
907 audioSlider.setPreferredSize(
new java.awt.Dimension(200, 30));
908 audioSlider.setRequestFocusEnabled(
false);
910 gridBagConstraints =
new java.awt.GridBagConstraints();
911 gridBagConstraints.gridx = 4;
912 gridBagConstraints.gridy = 0;
913 gridBagConstraints.ipadx = -116;
914 gridBagConstraints.ipady = 7;
915 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
916 gridBagConstraints.insets =
new java.awt.Insets(3, 1, 0, 10);
917 buttonPanel.add(audioSlider, gridBagConstraints);
919 infoLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
920 org.openide.awt.Mnemonics.setLocalizedText(infoLabel,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.infoLabel.text"));
921 infoLabel.setCursor(
new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
923 playBackSpeedComboBox.setModel(
new javax.swing.DefaultComboBoxModel<>(
new String[] {
"0.25x",
"0.50x",
"0.75x",
"1x",
"1.25x",
"1.50x",
"1.75x",
"2x" }));
924 playBackSpeedComboBox.setSelectedIndex(3);
925 playBackSpeedComboBox.setMaximumSize(
new java.awt.Dimension(53, 29));
926 playBackSpeedComboBox.setMinimumSize(
new java.awt.Dimension(53, 29));
927 playBackSpeedComboBox.setPreferredSize(
new java.awt.Dimension(53, 29));
928 playBackSpeedComboBox.setRequestFocusEnabled(
false);
929 playBackSpeedComboBox.addActionListener(
new java.awt.event.ActionListener() {
930 public void actionPerformed(java.awt.event.ActionEvent evt) {
931 playBackSpeedComboBoxActionPerformed(evt);
935 org.openide.awt.Mnemonics.setLocalizedText(playBackSpeedLabel,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.playBackSpeedLabel.text"));
936 playBackSpeedLabel.setMaximumSize(
new java.awt.Dimension(34, 19));
937 playBackSpeedLabel.setMinimumSize(
new java.awt.Dimension(34, 19));
938 playBackSpeedLabel.setPreferredSize(
new java.awt.Dimension(34, 19));
940 javax.swing.GroupLayout playBackPanelLayout =
new javax.swing.GroupLayout(playBackPanel);
941 playBackPanel.setLayout(playBackPanelLayout);
942 playBackPanelLayout.setHorizontalGroup(
943 playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
944 .addGroup(playBackPanelLayout.createSequentialGroup()
945 .addComponent(playBackSpeedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE)
946 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
947 .addComponent(playBackSpeedComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
950 playBackPanelLayout.setVerticalGroup(
951 playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
952 .addGroup(playBackPanelLayout.createSequentialGroup()
954 .addGroup(playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
false)
955 .addGroup(playBackPanelLayout.createSequentialGroup()
957 .addComponent(playBackSpeedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
958 .addComponent(playBackSpeedComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
962 javax.swing.GroupLayout controlPanelLayout =
new javax.swing.GroupLayout(controlPanel);
963 controlPanel.setLayout(controlPanelLayout);
964 controlPanelLayout.setHorizontalGroup(
965 controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
966 .addGroup(controlPanelLayout.createSequentialGroup()
968 .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
969 .addComponent(infoLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
970 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, controlPanelLayout.createSequentialGroup()
971 .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
972 .addComponent(buttonPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
973 .addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 623, Short.MAX_VALUE))
974 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
975 .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
false)
976 .addComponent(progressLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
977 .addComponent(playBackPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
978 .addGap(10, 10, 10)))
981 controlPanelLayout.setVerticalGroup(
982 controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
983 .addGroup(controlPanelLayout.createSequentialGroup()
985 .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
false)
986 .addComponent(progressLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
987 .addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
989 .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
false)
990 .addComponent(buttonPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
991 .addComponent(playBackPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
993 .addComponent(infoLabel))
996 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
997 this.setLayout(layout);
998 layout.setHorizontalGroup(
999 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
1000 .addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
1001 .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
1003 layout.setVerticalGroup(
1004 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
1005 .addGroup(layout.createSequentialGroup()
1006 .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
1007 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
1008 .addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
1013 Gst.getExecutor().submit(() -> {
1014 if (gstPlayBin != null) {
1015 long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
1017 long rewindDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
1019 long newTime = Math.max(currentTime - rewindDelta, 0);
1020 double playBackRate = getPlayBackRate();
1021 gstPlayBin.seek(playBackRate,
1025 EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
1027 SeekType.SET, newTime,
1035 Gst.getExecutor().submit(() -> {
1036 if (gstPlayBin != null) {
1037 long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
1038 long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
1040 long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
1043 long twoSecondsInNano = TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);
1044 if ((duration - currentTime) <= twoSecondsInNano) {
1049 if (currentTime + fastForwardDelta >= duration) {
1051 newTime = currentTime + (duration - currentTime) / 2;
1053 newTime = currentTime + fastForwardDelta;
1056 double playBackRate = getPlayBackRate();
1057 gstPlayBin.seek(playBackRate,
1061 EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
1063 SeekType.SET, newTime,
1071 Gst.getExecutor().submit(() -> {
1072 if (gstPlayBin != null) {
1073 if (gstPlayBin.isPlaying()) {
1076 double playBackRate = getPlayBackRate();
1077 long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
1079 gstPlayBin.seek(playBackRate,
1083 EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
1085 SeekType.SET, currentTime,
1095 Gst.getExecutor().submit(() -> {
1096 if (gstPlayBin != null) {
1097 double playBackRate = getPlayBackRate();
1098 long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
1099 gstPlayBin.seek(playBackRate,
1103 EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
1106 SeekType.SET, currentTime,
void actionPerformed(ActionEvent e)
void playButtonActionPerformed(java.awt.event.ActionEvent evt)
javax.swing.JButton playButton
void updateTimeLabel(long start, long total)
boolean isSupported(AbstractFile file)
javax.swing.JSlider progressSlider
ExtractMedia extractMediaWorker
void scrollDueToClickInTrack(int direction)
javax.swing.JPanel buttonPanel
javax.swing.JButton rewindButton
javax.swing.JButton fastForwardButton
void paintTrack(Graphics graphic)
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
void playBackSpeedComboBoxActionPerformed(java.awt.event.ActionEvent evt)
String getMIMEType(AbstractFile file)
TrackListener createTrackListener(JSlider slider)
List< String > getSupportedExtensions()
javax.swing.JPanel videoPanel
List< String > getSupportedMimeTypes()
javax.swing.JLabel progressLabel
void paintThumb(Graphics graphic)
void enableComponents(boolean isEnabled)
final Semaphore sliderLock
CircularJSliderUI(JSlider slider, Dimension thumbDimension)
javax.swing.JComboBox< String > playBackSpeedComboBox
String formatTime(long ns)
Bus.EOS endOfStreamListener
void update(Graphics graphic, JComponent component)
javax.swing.JLabel playBackSpeedLabel
javax.swing.JPanel controlPanel
void customizeComponents()
javax.swing.JLabel VolumeIcon
Bus.STATE_CHANGED stateChangeListener
volatile PlayBin gstPlayBin
void rewindButtonActionPerformed(java.awt.event.ActionEvent evt)
synchronized static Logger getLogger(String name)
final Dimension thumbDimension
void fastForwardButtonActionPerformed(java.awt.event.ActionEvent evt)
javax.swing.JSlider audioSlider
static synchronized GstStatus tryLoad()
javax.swing.JLabel infoLabel
javax.swing.JPanel playBackPanel
static void error(String message)