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) {
433 String extension = file.getNameExtension();
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);
457 if (!mimeTypes.isEmpty() && file.isMimeType(mimeTypes) == AbstractFile.MimeMatchEnum.TRUE) {
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
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