19 package org.sleuthkit.autopsy.corecomponents;
21 import java.awt.Dimension;
22 import java.io.IOException;
23 import java.nio.file.Paths;
24 import java.util.Arrays;
25 import java.util.List;
26 import java.util.concurrent.CancellationException;
27 import java.util.logging.Level;
28 import javafx.application.Platform;
29 import javafx.beans.InvalidationListener;
30 import javafx.beans.Observable;
31 import javafx.beans.value.ChangeListener;
32 import javafx.beans.value.ObservableValue;
33 import javafx.embed.swing.JFXPanel;
34 import javafx.event.ActionEvent;
35 import javafx.event.EventHandler;
36 import javafx.geometry.Insets;
37 import javafx.geometry.Pos;
38 import javafx.scene.Scene;
39 import javafx.scene.control.Button;
40 import javafx.scene.control.Label;
41 import javafx.scene.control.Slider;
42 import javafx.scene.control.Tooltip;
43 import javafx.scene.layout.BorderPane;
44 import javafx.scene.layout.HBox;
45 import javafx.scene.layout.Priority;
46 import javafx.scene.layout.VBox;
47 import javafx.scene.media.Media;
48 import javafx.scene.media.MediaException;
49 import javafx.scene.media.MediaPlayer;
50 import javafx.scene.media.MediaPlayer.Status;
51 import static javafx.scene.media.MediaPlayer.Status.PAUSED;
52 import static javafx.scene.media.MediaPlayer.Status.PLAYING;
53 import static javafx.scene.media.MediaPlayer.Status.READY;
54 import static javafx.scene.media.MediaPlayer.Status.STOPPED;
55 import javafx.scene.media.MediaView;
56 import javafx.util.Duration;
57 import javax.swing.JPanel;
58 import javax.swing.SwingUtilities;
59 import javax.swing.SwingWorker;
60 import org.netbeans.api.progress.ProgressHandle;
61 import org.netbeans.api.progress.ProgressHandleFactory;
62 import org.openide.util.Cancellable;
63 import org.openide.util.NbBundle;
64 import org.openide.util.lookup.ServiceProvider;
65 import org.openide.util.lookup.ServiceProviders;
77 @ServiceProviders(value = {
78 @ServiceProvider(service = FrameCapture.class)
84 private static final String[] EXTENSIONS =
new String[]{
".m4v",
".fxm",
".flv",
".m3u8",
".mp4",
".aif",
".aiff",
".mp3",
"m4a",
".wav"};
85 private static final List<String> MIMETYPES = Arrays.asList(
"audio/x-aiff",
"video/x-javafx",
"video/x-flv",
"application/vnd.apple.mpegurl",
" audio/mpegurl",
"audio/mpeg",
"video/mp4",
"audio/x-m4a",
"video/x-m4v",
"audio/x-wav");
88 private boolean fxInited =
false;
115 Platform.runLater(
new Runnable() {
118 videoComponent =
new JFXPanel();
120 Scene fxScene =
new Scene(mediaPane);
121 videoComponent.setScene(fxScene);
123 SwingUtilities.invokeLater(
new Runnable() {
134 void setupVideo(
final AbstractFile file,
final Dimension dims) {
135 if (file.
equals(currentFile)) {
138 if (!Case.isCaseOpen()) {
145 final boolean deleted = file.
isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
147 mediaPane.
setInfoLabelText(NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.mediaPane.infoLabel"));
155 }
catch (TskCoreException ex) {
156 logger.log(Level.SEVERE,
"Cannot get unique path of video file");
161 ExtractMedia em =
new ExtractMedia(currentFile, getJFile(currentFile));
169 Platform.runLater(
new Runnable() {
172 if (mediaPane != null) {
184 int extStart = name.lastIndexOf(
".");
186 if (extStart != -1) {
187 ext = name.substring(extStart, name.length()).toLowerCase();
189 tempPath = tempPath + java.io.File.separator + file.
getId() + ext;
191 java.io.File tempFile =
new java.io.File(tempPath);
200 @SuppressWarnings(
"unchecked")
202 private
void initComponents() {
204 setBackground(
new java.awt.Color(0, 0, 0));
205 setLayout(
new javax.swing.BoxLayout(
this, javax.swing.BoxLayout.LINE_AXIS));
223 boolean success =
false;
237 return extractedBytes;
246 return Paths.get(jFile.getAbsolutePath()).toUri().toString();
252 progress = ProgressHandleFactory.createHandle(
253 NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.progress.bufferingFile", sFile.
getName()),
256 public boolean cancel() {
260 mediaPane.
setProgressLabelText(NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.progressLabel.buffering"));
262 progress.switchToDeterminate(100);
265 }
catch (IOException ex) {
266 logger.log(Level.WARNING,
"Error buffering file", ex);
268 logger.log(Level.INFO,
"Done buffering: " + jFile.getName());
279 }
catch (CancellationException ex) {
280 logger.log(Level.INFO,
"Media buffering was canceled.");
282 NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.progress.bufferingCancelled"));
283 }
catch (InterruptedException ex) {
284 logger.log(Level.INFO,
"Media buffering was interrupted.");
286 NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.progress.bufferingInterrupted"));
287 }
catch (Exception ex) {
288 logger.log(Level.SEVERE,
"Fatal error during media buffering.", ex);
290 NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.progress.errorWritingVideoToDisk"));
293 if (!this.isCancelled()) {
294 logger.log(Level.INFO,
"ExtractMedia in done: " + jFile.getName());
296 Platform.runLater(
new Runnable() {
302 }
catch (MediaException e) {
303 logger.log(Level.WARNING,
"something went wrong with javafx", e);
350 private String durationFormat =
"%02d:%02d:%02d/%02d:%02d:%02d ";
367 private static final String PLAY_TEXT =
"â–º";
369 private static final String PAUSE_TEXT =
"||";
371 private static final String STOP_TEXT =
"X";
375 mediaViewPane =
new HBox();
376 mediaViewPane.setStyle(
"-fx-background-color: black");
377 mediaViewPane.setAlignment(Pos.CENTER);
378 mediaView =
new MediaView();
379 mediaViewPane.getChildren().add(mediaView);
380 setCenter(mediaViewPane);
383 controlPanel =
new VBox();
384 mediaTools =
new HBox();
385 mediaTools.setAlignment(Pos.CENTER);
386 mediaTools.setPadding(
new Insets(5, 10, 5, 10));
388 pauseButton =
new Button(PLAY_TEXT);
389 stopButton =
new Button(STOP_TEXT);
390 mediaTools.getChildren().add(pauseButton);
391 mediaTools.getChildren().add(
new Label(
" "));
392 mediaTools.getChildren().add(stopButton);
393 mediaTools.getChildren().add(
new Label(
" "));
394 progressSlider =
new Slider();
395 HBox.setHgrow(progressSlider, Priority.ALWAYS);
396 progressSlider.setMinWidth(50);
397 progressSlider.setMaxWidth(Double.MAX_VALUE);
398 mediaTools.getChildren().add(progressSlider);
399 progressLabel =
new Label();
400 progressLabel.setPrefWidth(135);
401 progressLabel.setMinWidth(135);
402 mediaTools.getChildren().add(progressLabel);
404 controlPanel.getChildren().add(mediaTools);
405 controlPanel.setStyle(
"-fx-background-color: white");
406 infoLabel =
new Label(
"");
407 controlPanel.getChildren().add(infoLabel);
408 setBottom(controlPanel);
409 setProgressActionListeners();
420 mediaPlayer = createMediaPlayer(mediaUri);
421 mediaView.setMediaPlayer(mediaPlayer);
422 }
catch (MediaException ex) {
423 this.setProgressLabelText(
"");
424 this.setInfoLabelText(NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.media.unsupportedFormat"));
433 if (mediaPlayer != null) {
434 setInfoLabelText(
"");
435 if (mediaPlayer.getStatus() == Status.PLAYING) {
439 mediaView.setMediaPlayer(null);
450 logger.log(Level.INFO,
"Setting Info Label Text: " + text);
451 Platform.runLater(
new Runnable() {
454 infoLabel.setText(text);
464 public void setFit(
final Dimension dims) {
465 Platform.runLater(
new Runnable() {
468 setPrefSize(dims.getWidth(), dims.getHeight());
471 mediaView.setFitHeight(dims.getHeight() - controlPanel.getHeight());
480 pauseButton.setOnAction(
new EventHandler<ActionEvent>() {
482 public void handle(ActionEvent e) {
483 if (mediaPlayer == null) {
487 Status status = mediaPlayer.getStatus();
501 logger.log(Level.INFO,
"MediaPlayer in unexpected state: " + status.toString());
504 setInfoLabelText(NbBundle.getMessage(
this.getClass(),
505 "FXVideoPanel.pauseButton.infoLabel.playbackErr"));
511 stopButton.setOnAction(
new EventHandler<ActionEvent>() {
513 public void handle(ActionEvent e) {
514 if (mediaPlayer == null) {
522 progressSlider.valueProperty().addListener(
new InvalidationListener() {
524 public void invalidated(Observable o) {
525 if (mediaPlayer == null) {
529 if (progressSlider.isValueChanging()) {
530 mediaPlayer.seek(duration.multiply(progressSlider.getValue() / 100.0));
543 progressSlider.setValue(0.0);
544 updateTime(Duration.ZERO);
557 Media media =
new Media(mediaUri);
559 MediaPlayer player =
new MediaPlayer(media);
560 player.setOnReady(READY_LISTENER);
561 player.setOnPaused(NOT_PLAY_LISTENER);
562 player.setOnStopped(NOT_PLAY_LISTENER);
563 player.setOnPlaying(PLAY_LISTENER);
564 player.setOnEndOfMedia(END_LISTENER);
566 player.currentTimeProperty().addListener(TIME_LISTENER);
576 if (mediaPlayer == null) {
579 Duration currentTime = mediaPlayer.getCurrentTime();
580 updateSlider(currentTime);
581 updateTime(currentTime);
590 if (progressSlider != null) {
591 progressSlider.setDisable(currentTime.isUnknown());
592 if (!progressSlider.isDisabled() && duration.greaterThan(Duration.ZERO)
593 && !progressSlider.isValueChanging()) {
594 progressSlider.setValue(currentTime.divide(duration.toMillis()).toMillis() * 100.0);
605 long millisElapsed = (long) currentTime.toMillis();
607 long elapsedHours, elapsedMinutes, elapsedSeconds;
609 long secondsElapsed = millisElapsed / 1000;
610 elapsedHours = (int) secondsElapsed / 3600;
611 secondsElapsed -= elapsedHours * 3600;
612 elapsedMinutes = (int) secondsElapsed / 60;
613 secondsElapsed -= elapsedMinutes * 60;
614 elapsedSeconds = (int) secondsElapsed;
616 String durationStr = String.format(durationFormat,
617 elapsedHours, elapsedMinutes, elapsedSeconds,
618 totalHours, totalMinutes, totalSeconds);
619 setProgressLabelText(durationStr);
628 Platform.runLater(
new Runnable() {
631 progressLabel.setText(text);
637 Platform.runLater(
new Runnable() {
640 infoLabel.setTooltip(
new Tooltip(text));
654 if (mediaPlayer == null) {
658 duration = mediaPlayer.getMedia().getDuration();
659 long durationInMillis = (long) mediaPlayer.getMedia().getDuration().toMillis();
662 long durationSeconds = (int) durationInMillis / 1000;
663 totalHours = (int) durationSeconds / 3600;
664 durationSeconds -= totalHours * 3600;
665 totalMinutes = (int) durationSeconds / 60;
666 durationSeconds -= totalMinutes * 60;
667 totalSeconds = (int) durationSeconds;
681 if (mediaPlayer == null) {
685 Duration beginning = mediaPlayer.getStartTime();
688 pauseButton.setText(PLAY_TEXT);
689 updateSlider(beginning);
690 updateTime(beginning);
702 public void changed(ObservableValue<? extends Duration> observable, Duration oldValue, Duration newValue) {
703 updateSlider(newValue);
704 updateTime(newValue);
715 pauseButton.setText(PLAY_TEXT);
726 pauseButton.setText(PAUSE_TEXT);
741 public List<VideoFrame>
captureFrames(java.io.File file,
int numFrames)
throws Exception {
static boolean isJavaFxInited()
static< T, V > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, SwingWorker< T, V > worker, boolean source)
boolean isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM flag)
String getTempDirectory()
List< String > getMimeTypes()
synchronized String getUniquePath()
List< VideoFrame > captureFrames(java.io.File file, int numFrames)
boolean equals(Object obj)
java.io.File getJFile(AbstractFile file)
static Case getCurrentCase()
static Logger getLogger(String name)