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));
309 progressLabel.setText(
"");
314 stateChangeListener =
new Bus.STATE_CHANGED() {
316 public void stateChanged(GstObject go, State oldState, State currentState, State pendingState) {
317 if (State.PLAYING.equals(currentState)) {
318 SwingUtilities.invokeLater(() -> {
319 playButton.setIcon(pauseIcon);
322 SwingUtilities.invokeLater(() -> {
323 playButton.setIcon(playIcon);
328 endOfStreamListener =
new Bus.EOS() {
330 public void endOfStream(GstObject go) {
331 if (gstPlayBin != null) {
332 gstPlayBin.seek(ClockTime.ZERO);
336 Gst.getExecutor().submit(() -> gstPlayBin.pause());
348 @NbBundle.Messages({
"GstVideoPanel.noOpenCase.errMsg=No open case available."})
349 void loadFile(
final AbstractFile file) {
351 infoLabel.setText(
"");
352 if (file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC)) {
353 infoLabel.setText(NbBundle.getMessage(
this.getClass(),
"GstVideoPanel.setupVideo.infoLabel.text"));
359 extractMediaWorker =
new ExtractMedia(file, VideoUtils.getVideoFileInTempDir(file));
360 extractMediaWorker.execute();
361 }
catch (NoCurrentCaseException ex) {
362 logger.log(Level.SEVERE,
"Exception while getting open case.", ex);
363 infoLabel.setText(String.format(
"<html><font color='red'>%s</font></html>", Bundle.GstVideoPanel_noOpenCase_errMsg()));
364 enableComponents(
false);
373 "MediaPlayerPanel.noSupport=File not supported."
375 void resetComponents() {
376 progressLabel.setText(String.format(
"%s/%s", Bundle.MediaPlayerPanel_unknownTime(),
377 Bundle.MediaPlayerPanel_unknownTime()));
378 infoLabel.setText(Bundle.MediaPlayerPanel_noSupport());
379 progressSlider.setValue(0);
386 if (extractMediaWorker != null) {
387 extractMediaWorker.cancel(
true);
390 if (gstPlayBin != null) {
391 Gst.getExecutor().submit(() -> {
393 gstPlayBin.getBus().disconnect(endOfStreamListener);
394 gstPlayBin.getBus().disconnect(stateChangeListener);
395 gstPlayBin.getBus().disconnect(errorListener);
396 gstPlayBin.getBus().dispose();
397 gstPlayBin.dispose();
402 videoPanel.removeAll();
404 enableComponents(
false);
408 playButton.setEnabled(isEnabled);
409 progressSlider.setEnabled(isEnabled);
410 videoPanel.setEnabled(isEnabled);
411 audioSlider.setEnabled(isEnabled);
412 rewindButton.setEnabled(isEnabled);
413 fastForwardButton.setEnabled(isEnabled);
414 playBackSpeedComboBox.setEnabled(isEnabled);
419 return Arrays.asList(FILE_EXTENSIONS.clone());
429 if (!IS_GST_ENABLED) {
450 if (getSupportedExtensions().contains(
"." + extension)) {
451 SortedSet<String> mimeTypes =
new TreeSet<>(getSupportedMimeTypes());
454 return mimeTypes.contains(mimeType);
456 logger.log(Level.WARNING,
"Failed to look up mimetype for " + file.
getName() +
" using FileTypeDetector. Fallingback on AbstractFile.isMimeType", ex);
462 return getSupportedExtensions().contains(
"." + extension);
475 progressLabel.setText(formatTime(start) +
"/" + formatTime(total));
484 int selectIndex = playBackSpeedComboBox.getSelectedIndex();
485 String selectText = playBackSpeedComboBox.getItemAt(selectIndex);
486 return Double.valueOf(selectText.substring(0, selectText.length() - 1));
493 "MediaPlayerPanel.unknownTime=Unknown",
494 "MediaPlayerPanel.timeFormat=%02d:%02d:%02d"
498 return Bundle.MediaPlayerPanel_unknownTime();
501 long seconds = TimeUnit.SECONDS.convert(ns, TimeUnit.NANOSECONDS);
502 long hours = TimeUnit.HOURS.convert(seconds, TimeUnit.SECONDS);
503 seconds -= TimeUnit.SECONDS.convert(hours, TimeUnit.HOURS);
504 long minutes = TimeUnit.MINUTES.convert(seconds, TimeUnit.SECONDS);
505 seconds -= TimeUnit.SECONDS.convert(minutes, TimeUnit.MINUTES);
507 return String.format(Bundle.MediaPlayerPanel_timeFormat(), hours, minutes, seconds);
521 this.sourceFile = sFile;
522 this.tempFile = jFile;
527 if (this.isCancelled()) {
528 throw new InterruptedException(
"Thread has been interrupted");
536 if (this.isCancelled()) {
537 throw new InterruptedException(
"Thread has been interrupted");
540 if (!tempFile.exists() || tempFile.length() < sourceFile.
getSize()) {
541 progress = ProgressHandle.createHandle(NbBundle.getMessage(
MediaPlayerPanel.class,
"GstVideoPanel.ExtractMedia.progress.buffering", sourceFile.
getName()), () -> this.cancel(
true));
543 SwingUtilities.invokeLater(() -> {
544 progressLabel.setText(NbBundle.getMessage(
this.getClass(),
"GstVideoPanel.progress.buffering"));
549 Files.createParentDirs(tempFile);
551 }
catch (IOException ex) {
552 logger.log(Level.WARNING,
"Error creating parent directory for copying video/audio in temp directory", ex);
564 "MediaPlayerPanel.playbackDisabled=A problem was encountered with"
565 +
" the video and audio playback service. Video and audio "
566 +
"playback will be disabled for the remainder of the session."
571 if (this.isCancelled()) {
580 if (this.isCancelled()) {
584 Gst.getExecutor().submit(() -> {
586 gstPlayBin =
new PlayBin(
"VideoPlayer", tempFile.toURI());
588 Bus playBinBus = gstPlayBin.getBus();
589 playBinBus.connect(endOfStreamListener);
590 playBinBus.connect(stateChangeListener);
591 playBinBus.connect(errorListener);
593 if (this.isCancelled()) {
597 JFXPanel fxPanel =
new JFXPanel();
598 videoPanel.removeAll();
599 videoPanel.setLayout(
new BoxLayout(videoPanel, BoxLayout.Y_AXIS));
600 videoPanel.add(fxPanel);
601 fxAppSink =
new JavaFxAppSink(
"JavaFxAppSink", fxPanel);
602 if (gstPlayBin != null) {
603 gstPlayBin.setVideoSink(fxAppSink);
605 if (this.isCancelled()) {
608 if (gstPlayBin != null) {
609 gstPlayBin.setVolume((audioSlider.getValue() * 2.0) / 100.0);
614 SwingUtilities.invokeLater(() -> {
615 enableComponents(
true);
618 }
catch (CancellationException ex) {
619 logger.log(Level.INFO,
"Media buffering was canceled.");
620 }
catch (InterruptedException ex) {
621 logger.log(Level.INFO,
"Media buffering was interrupted.");
622 }
catch (ExecutionException ex) {
623 logger.log(Level.SEVERE,
"Fatal error during media buffering.", ex);
635 if (!progressSlider.getValueIsAdjusting() && gstPlayBin != null) {
636 Gst.getExecutor().submit(() -> {
638 sliderLock.acquireUninterruptibly();
639 long position = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
640 long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
647 if (duration >= 0 && position >= 0) {
648 double relativePosition = (double) position / duration;
649 progressSlider.setValue((
int) (relativePosition * PROGRESS_SLIDER_SIZE));
652 SwingUtilities.invokeLater(() -> {
653 updateTimeLabel(position, duration);
656 sliderLock.release();
683 this.thumbDimension = thumbDimension;
686 Color lightBlue =
new Color(0, 130, 255);
687 thumbColor = lightBlue;
688 trackSeen = lightBlue;
689 trackUnseen = Color.LIGHT_GRAY;
694 return new Dimension(thumbDimension);
703 Rectangle thumb = this.thumbRect;
705 Color original = graphic.getColor();
709 graphic.setColor(thumbColor);
710 graphic.fillOval(thumb.x, thumb.y, thumbDimension.width, thumbDimension.height);
713 graphic.setColor(original);
721 Rectangle track = this.trackRect;
725 Rectangle thumb = this.thumbRect;
726 int thumbX = thumb.x;
727 int thumbY = thumb.y;
729 Color original = graphic.getColor();
732 graphic.setColor(trackSeen);
733 graphic.drawLine(track.x, track.y + track.height / 2,
734 thumbX, thumbY + track.height / 2);
737 graphic.setColor(trackUnseen);
738 graphic.drawLine(thumbX, thumbY + track.height / 2,
739 track.x + track.width, track.y + track.height / 2);
742 graphic.setColor(original);
755 return new TrackListener() {
757 public void mousePressed(MouseEvent e) {
758 if (!slider.isEnabled() || !SwingUtilities.isLeftMouseButton(e)) {
762 scrollDueToClickInTrack(0);
765 super.mousePressed(e);
774 Point mousePosition = slider.getMousePosition();
775 if (mousePosition == null) {
778 int value = this.valueForXPosition(mousePosition.x);
784 sliderLock.acquireUninterruptibly();
785 slider.setValueIsAdjusting(
true);
786 slider.setValue(value);
787 slider.setValueIsAdjusting(
false);
788 sliderLock.release();
795 public void update(Graphics graphic, JComponent component) {
796 if (graphic instanceof Graphics2D) {
797 Graphics2D graphic2 = (Graphics2D) graphic;
798 graphic2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
799 RenderingHints.VALUE_ANTIALIAS_ON);
802 super.update(graphic, component);
811 @SuppressWarnings(
"unchecked")
813 private
void initComponents() {
814 java.awt.GridBagConstraints gridBagConstraints;
816 videoPanel =
new javax.swing.JPanel();
817 controlPanel =
new javax.swing.JPanel();
818 progressSlider =
new javax.swing.JSlider();
819 progressLabel =
new javax.swing.JLabel();
820 buttonPanel =
new javax.swing.JPanel();
821 playButton =
new javax.swing.JButton();
822 fastForwardButton =
new javax.swing.JButton();
823 rewindButton =
new javax.swing.JButton();
824 VolumeIcon =
new javax.swing.JLabel();
825 audioSlider =
new javax.swing.JSlider();
826 infoLabel =
new javax.swing.JLabel();
827 playBackPanel =
new javax.swing.JPanel();
828 playBackSpeedComboBox =
new javax.swing.JComboBox<>();
829 playBackSpeedLabel =
new javax.swing.JLabel();
831 javax.swing.GroupLayout videoPanelLayout =
new javax.swing.GroupLayout(videoPanel);
832 videoPanel.setLayout(videoPanelLayout);
833 videoPanelLayout.setHorizontalGroup(
834 videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
835 .addGap(0, 0, Short.MAX_VALUE)
837 videoPanelLayout.setVerticalGroup(
838 videoPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
839 .addGap(0, 117, Short.MAX_VALUE)
842 progressSlider.setValue(0);
843 progressSlider.setCursor(
new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
844 progressSlider.setDoubleBuffered(
true);
845 progressSlider.setMinimumSize(
new java.awt.Dimension(36, 21));
846 progressSlider.setPreferredSize(
new java.awt.Dimension(200, 21));
847 progressSlider.setUI(
new CircularJSliderUI(progressSlider,
new Dimension(18,18)));
849 org.openide.awt.Mnemonics.setLocalizedText(progressLabel,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.progressLabel.text"));
851 buttonPanel.setLayout(
new java.awt.GridBagLayout());
853 playButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/contentviewers/images/Play-arrow-01.png")));
854 org.openide.awt.Mnemonics.setLocalizedText(playButton,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.playButton.text"));
855 playButton.setMaximumSize(
new java.awt.Dimension(53, 29));
856 playButton.setMinimumSize(
new java.awt.Dimension(53, 29));
857 playButton.setPreferredSize(
new java.awt.Dimension(49, 29));
858 playButton.addActionListener(
new java.awt.event.ActionListener() {
859 public void actionPerformed(java.awt.event.ActionEvent evt) {
860 playButtonActionPerformed(evt);
863 gridBagConstraints =
new java.awt.GridBagConstraints();
864 gridBagConstraints.gridx = 1;
865 gridBagConstraints.gridy = 0;
866 gridBagConstraints.ipadx = 21;
867 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
868 gridBagConstraints.insets =
new java.awt.Insets(5, 6, 0, 0);
869 buttonPanel.add(playButton, gridBagConstraints);
871 fastForwardButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/contentviewers/images/Fast-forward-01.png")));
872 org.openide.awt.Mnemonics.setLocalizedText(fastForwardButton,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.fastForwardButton.text"));
873 fastForwardButton.addActionListener(
new java.awt.event.ActionListener() {
874 public void actionPerformed(java.awt.event.ActionEvent evt) {
875 fastForwardButtonActionPerformed(evt);
878 gridBagConstraints =
new java.awt.GridBagConstraints();
879 gridBagConstraints.gridx = 2;
880 gridBagConstraints.gridy = 0;
881 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
882 gridBagConstraints.insets =
new java.awt.Insets(5, 6, 0, 0);
883 buttonPanel.add(fastForwardButton, gridBagConstraints);
885 rewindButton.setIcon(
new javax.swing.ImageIcon(getClass().getResource(
"/org/sleuthkit/autopsy/contentviewers/images/Fast-rewind-01.png")));
886 org.openide.awt.Mnemonics.setLocalizedText(rewindButton,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.rewindButton.text"));
887 rewindButton.addActionListener(
new java.awt.event.ActionListener() {
888 public void actionPerformed(java.awt.event.ActionEvent evt) {
889 rewindButtonActionPerformed(evt);
892 gridBagConstraints =
new java.awt.GridBagConstraints();
893 gridBagConstraints.gridx = 0;
894 gridBagConstraints.gridy = 0;
895 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
896 gridBagConstraints.insets =
new java.awt.Insets(5, 0, 1, 0);
897 buttonPanel.add(rewindButton, gridBagConstraints);
899 org.openide.awt.Mnemonics.setLocalizedText(VolumeIcon,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.VolumeIcon.text"));
900 VolumeIcon.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
901 VolumeIcon.setMaximumSize(
new java.awt.Dimension(34, 29));
902 VolumeIcon.setMinimumSize(
new java.awt.Dimension(34, 29));
903 VolumeIcon.setPreferredSize(
new java.awt.Dimension(34, 19));
904 gridBagConstraints =
new java.awt.GridBagConstraints();
905 gridBagConstraints.gridx = 3;
906 gridBagConstraints.gridy = 0;
907 gridBagConstraints.ipadx = 8;
908 gridBagConstraints.ipady = 7;
909 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
910 gridBagConstraints.insets =
new java.awt.Insets(6, 14, 0, 0);
911 buttonPanel.add(VolumeIcon, gridBagConstraints);
913 audioSlider.setMajorTickSpacing(10);
914 audioSlider.setMaximum(50);
915 audioSlider.setMinorTickSpacing(5);
916 audioSlider.setToolTipText(
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.audioSlider.toolTipText"));
917 audioSlider.setValue(25);
918 audioSlider.setMaximumSize(
new java.awt.Dimension(32767, 19));
919 audioSlider.setMinimumSize(
new java.awt.Dimension(200, 19));
920 audioSlider.setPreferredSize(
new java.awt.Dimension(200, 30));
921 audioSlider.setRequestFocusEnabled(
false);
923 gridBagConstraints =
new java.awt.GridBagConstraints();
924 gridBagConstraints.gridx = 4;
925 gridBagConstraints.gridy = 0;
926 gridBagConstraints.ipadx = -116;
927 gridBagConstraints.ipady = 7;
928 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
929 gridBagConstraints.insets =
new java.awt.Insets(3, 1, 0, 10);
930 buttonPanel.add(audioSlider, gridBagConstraints);
932 infoLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
933 org.openide.awt.Mnemonics.setLocalizedText(infoLabel,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.infoLabel.text"));
934 infoLabel.setCursor(
new java.awt.Cursor(java.awt.Cursor.DEFAULT_CURSOR));
936 playBackSpeedComboBox.setModel(
new javax.swing.DefaultComboBoxModel<>(
new String[] {
"0.25x",
"0.50x",
"0.75x",
"1x",
"1.25x",
"1.50x",
"1.75x",
"2x" }));
937 playBackSpeedComboBox.setSelectedIndex(3);
938 playBackSpeedComboBox.setMaximumSize(
new java.awt.Dimension(53, 29));
939 playBackSpeedComboBox.setMinimumSize(
new java.awt.Dimension(53, 29));
940 playBackSpeedComboBox.setPreferredSize(
new java.awt.Dimension(53, 29));
941 playBackSpeedComboBox.setRequestFocusEnabled(
false);
942 playBackSpeedComboBox.addActionListener(
new java.awt.event.ActionListener() {
943 public void actionPerformed(java.awt.event.ActionEvent evt) {
944 playBackSpeedComboBoxActionPerformed(evt);
948 org.openide.awt.Mnemonics.setLocalizedText(playBackSpeedLabel,
org.openide.util.NbBundle.getMessage(
MediaPlayerPanel.class,
"MediaPlayerPanel.playBackSpeedLabel.text"));
949 playBackSpeedLabel.setMaximumSize(
new java.awt.Dimension(34, 19));
950 playBackSpeedLabel.setMinimumSize(
new java.awt.Dimension(34, 19));
951 playBackSpeedLabel.setPreferredSize(
new java.awt.Dimension(34, 19));
953 javax.swing.GroupLayout playBackPanelLayout =
new javax.swing.GroupLayout(playBackPanel);
954 playBackPanel.setLayout(playBackPanelLayout);
955 playBackPanelLayout.setHorizontalGroup(
956 playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
957 .addGroup(playBackPanelLayout.createSequentialGroup()
958 .addComponent(playBackSpeedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 34, javax.swing.GroupLayout.PREFERRED_SIZE)
959 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
960 .addComponent(playBackSpeedComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
963 playBackPanelLayout.setVerticalGroup(
964 playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
965 .addGroup(playBackPanelLayout.createSequentialGroup()
967 .addGroup(playBackPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
false)
968 .addGroup(playBackPanelLayout.createSequentialGroup()
970 .addComponent(playBackSpeedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
971 .addComponent(playBackSpeedComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
975 javax.swing.GroupLayout controlPanelLayout =
new javax.swing.GroupLayout(controlPanel);
976 controlPanel.setLayout(controlPanelLayout);
977 controlPanelLayout.setHorizontalGroup(
978 controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
979 .addGroup(controlPanelLayout.createSequentialGroup()
981 .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
982 .addComponent(infoLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
983 .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, controlPanelLayout.createSequentialGroup()
984 .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
985 .addComponent(buttonPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
986 .addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, 623, Short.MAX_VALUE))
987 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
988 .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
false)
989 .addComponent(progressLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
990 .addComponent(playBackPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
991 .addGap(10, 10, 10)))
994 controlPanelLayout.setVerticalGroup(
995 controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
996 .addGroup(controlPanelLayout.createSequentialGroup()
998 .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
false)
999 .addComponent(progressLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
1000 .addComponent(progressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
1002 .addGroup(controlPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING,
false)
1003 .addComponent(buttonPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
1004 .addComponent(playBackPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
1006 .addComponent(infoLabel))
1009 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
1010 this.setLayout(layout);
1011 layout.setHorizontalGroup(
1012 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
1013 .addComponent(controlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
1014 .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
1016 layout.setVerticalGroup(
1017 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
1018 .addGroup(layout.createSequentialGroup()
1019 .addComponent(videoPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
1020 .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
1021 .addComponent(controlPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
1026 Gst.getExecutor().submit(() -> {
1027 if (gstPlayBin != null) {
1028 long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
1030 long rewindDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
1032 long newTime = Math.max(currentTime - rewindDelta, 0);
1033 double playBackRate = getPlayBackRate();
1034 gstPlayBin.seek(playBackRate,
1038 EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
1040 SeekType.SET, newTime,
1048 Gst.getExecutor().submit(() -> {
1049 if (gstPlayBin != null) {
1050 long duration = gstPlayBin.queryDuration(TimeUnit.NANOSECONDS);
1051 long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
1053 long fastForwardDelta = TimeUnit.NANOSECONDS.convert(SKIP_IN_SECONDS, TimeUnit.SECONDS);
1056 long twoSecondsInNano = TimeUnit.NANOSECONDS.convert(2, TimeUnit.SECONDS);
1057 if ((duration - currentTime) <= twoSecondsInNano) {
1062 if (currentTime + fastForwardDelta >= duration) {
1064 newTime = currentTime + (duration - currentTime) / 2;
1066 newTime = currentTime + fastForwardDelta;
1069 double playBackRate = getPlayBackRate();
1070 gstPlayBin.seek(playBackRate,
1074 EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
1076 SeekType.SET, newTime,
1084 Gst.getExecutor().submit(() -> {
1085 if (gstPlayBin != null) {
1086 if (gstPlayBin.isPlaying()) {
1089 double playBackRate = getPlayBackRate();
1090 long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
1092 gstPlayBin.seek(playBackRate,
1096 EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
1098 SeekType.SET, currentTime,
1108 Gst.getExecutor().submit(() -> {
1109 if (gstPlayBin != null) {
1110 double playBackRate = getPlayBackRate();
1111 long currentTime = gstPlayBin.queryPosition(TimeUnit.NANOSECONDS);
1112 gstPlayBin.seek(playBackRate,
1116 EnumSet.of(SeekFlags.FLUSH, SeekFlags.ACCURATE),
1119 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
String getNameExtension()
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)
MimeMatchEnum isMimeType(SortedSet< String > mimeTypes)
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