19 package org.sleuthkit.autopsy.corecomponents;
 
   21 import com.google.common.io.Files;
 
   22 import java.awt.Dimension;
 
   24 import java.io.IOException;
 
   25 import java.nio.file.Paths;
 
   26 import java.util.Arrays;
 
   27 import java.util.List;
 
   28 import java.util.concurrent.CancellationException;
 
   29 import java.util.logging.Level;
 
   30 import javafx.application.Platform;
 
   31 import javafx.beans.Observable;
 
   32 import javafx.concurrent.Task;
 
   33 import javafx.event.ActionEvent;
 
   34 import javafx.event.EventHandler;
 
   35 import javafx.geometry.Insets;
 
   36 import javafx.geometry.Pos;
 
   37 import javafx.scene.Scene;
 
   38 import javafx.scene.control.Button;
 
   39 import javafx.scene.control.Label;
 
   40 import javafx.scene.control.Slider;
 
   41 import javafx.scene.control.Tooltip;
 
   42 import javafx.scene.layout.BorderPane;
 
   43 import javafx.scene.layout.HBox;
 
   44 import javafx.scene.layout.Priority;
 
   45 import javafx.scene.layout.VBox;
 
   46 import javafx.scene.media.Media;
 
   47 import javafx.scene.media.MediaException;
 
   48 import javafx.scene.media.MediaPlayer;
 
   49 import javafx.scene.media.MediaPlayer.Status;
 
   50 import static javafx.scene.media.MediaPlayer.Status.PAUSED;
 
   51 import static javafx.scene.media.MediaPlayer.Status.PLAYING;
 
   52 import static javafx.scene.media.MediaPlayer.Status.READY;
 
   53 import static javafx.scene.media.MediaPlayer.Status.STOPPED;
 
   54 import javafx.scene.media.MediaView;
 
   55 import javafx.util.Duration;
 
   56 import javax.swing.JPanel;
 
   57 import org.netbeans.api.progress.ProgressHandle;
 
   58 import org.openide.util.NbBundle;
 
   59 import org.openide.util.lookup.ServiceProvider;
 
   60 import org.openide.util.lookup.ServiceProviders;
 
   73 @ServiceProviders(value = {
 
   74     @ServiceProvider(service = FrameCapture.class)
 
   80     private static final String[] EXTENSIONS = 
new String[]{
".m4v", 
".fxm", 
".flv", 
".m3u8", 
".mp4", 
".aif", 
".aiff", 
".mp3", 
"m4a", 
".wav"}; 
 
   81     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"); 
 
   84     private boolean fxInited = 
false;
 
   94             Platform.runLater(() -> {
 
   97                 Scene fxScene = 
new Scene(mediaPane);
 
   98                 jFXPanel.setScene(fxScene);
 
  109     void setupVideo(
final AbstractFile file, 
final Dimension dims) {
 
  110         if (file.equals(currentFile)) {
 
  113         if (!Case.isCaseOpen()) {
 
  120         final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
 
  122             mediaPane.
setInfoLabelText(NbBundle.getMessage(
this.getClass(), 
"FXVideoPanel.mediaPane.infoLabel"));
 
  130             path = file.getUniquePath();
 
  131         } 
catch (TskCoreException ex) {
 
  132             logger.log(Level.SEVERE, 
"Cannot get unique path of video file", ex); 
 
  137         final File tempFile = VideoUtils.getTempVideoFile(currentFile);
 
  139         new Thread(mediaPane.new ExtractMedia(currentFile, tempFile)).start();
 
  145         Platform.runLater(() -> {
 
  146             if (mediaPane != null) {
 
  158     @SuppressWarnings(
"unchecked")
 
  160     private 
void initComponents() {
 
  162         jFXPanel = 
new javafx.embed.swing.JFXPanel();
 
  164         setBackground(
new java.awt.Color(0, 0, 0));
 
  166         javax.swing.GroupLayout jFXPanelLayout = 
new javax.swing.GroupLayout(jFXPanel);
 
  167         jFXPanel.setLayout(jFXPanelLayout);
 
  168         jFXPanelLayout.setHorizontalGroup(
 
  169             jFXPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  170             .addGap(0, 400, Short.MAX_VALUE)
 
  172         jFXPanelLayout.setVerticalGroup(
 
  173             jFXPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  174             .addGap(0, 300, Short.MAX_VALUE)
 
  177         javax.swing.GroupLayout layout = 
new javax.swing.GroupLayout(
this);
 
  178         this.setLayout(layout);
 
  179         layout.setHorizontalGroup(
 
  180             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  181             .addComponent(jFXPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
  183         layout.setVerticalGroup(
 
  184             layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
 
  185             .addComponent(jFXPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
 
  236         private final String durationFormat = 
"%02d:%02d:%02d/%02d:%02d:%02d  "; 
 
  238         private static final String PLAY_TEXT = 
"►";
 
  240         private static final String PAUSE_TEXT = 
"||";
 
  242         private static final String STOP_TEXT = 
"X"; 
 
  246             mediaViewPane = 
new HBox();
 
  247             mediaViewPane.setStyle(
"-fx-background-color: black"); 
 
  248             mediaViewPane.setAlignment(Pos.CENTER);
 
  249             mediaView = 
new MediaView();
 
  250             mediaViewPane.getChildren().add(mediaView);
 
  251             setCenter(mediaViewPane);
 
  254             controlPanel = 
new VBox();
 
  255             mediaTools = 
new HBox();
 
  256             mediaTools.setAlignment(Pos.CENTER);
 
  257             mediaTools.setPadding(
new Insets(5, 10, 5, 10));
 
  259             pauseButton = 
new Button(PLAY_TEXT);
 
  260             stopButton = 
new Button(STOP_TEXT);
 
  261             mediaTools.getChildren().add(pauseButton);
 
  262             mediaTools.getChildren().add(
new Label(
"  "));
 
  263             mediaTools.getChildren().add(stopButton);
 
  264             mediaTools.getChildren().add(
new Label(
"  "));
 
  265             progressSlider = 
new Slider();
 
  266             HBox.setHgrow(progressSlider, Priority.ALWAYS);
 
  267             progressSlider.setMinWidth(50);
 
  268             progressSlider.setMaxWidth(Double.MAX_VALUE);
 
  269             mediaTools.getChildren().add(progressSlider);
 
  270             progressLabel = 
new Label();
 
  271             progressLabel.setPrefWidth(135);
 
  272             progressLabel.setMinWidth(135);
 
  273             mediaTools.getChildren().add(progressLabel);
 
  275             controlPanel.getChildren().add(mediaTools);
 
  276             controlPanel.setStyle(
"-fx-background-color: white"); 
 
  277             infoLabel = 
new Label(
"");
 
  278             controlPanel.getChildren().add(infoLabel);
 
  279             setBottom(controlPanel);
 
  280             setProgressActionListeners();
 
  288             if (mediaPlayer != null) {
 
  289                 setInfoLabelText(
"");
 
  290                 if (mediaPlayer.getStatus() == Status.PLAYING) {
 
  294                 mediaView.setMediaPlayer(null);
 
  305             logger.log(Level.INFO, 
"Setting Info Label Text: {0}", text); 
 
  306             Platform.runLater(() -> {
 
  307                 infoLabel.setText(text);
 
  316         public void setFit(
final Dimension dims) {
 
  317             Platform.runLater(() -> {
 
  318                 setPrefSize(dims.getWidth(), dims.getHeight());
 
  321                 mediaView.setFitHeight(dims.getHeight() - controlPanel.getHeight());
 
  329             pauseButton.setOnAction(
new EventHandler<ActionEvent>() {
 
  331                 public void handle(ActionEvent e) {
 
  332                     if (mediaPlayer == null) {
 
  336                     Status status = mediaPlayer.getStatus();
 
  350                             logger.log(Level.INFO, 
"MediaPlayer in unexpected state: {0}", status.toString()); 
 
  353                             setInfoLabelText(NbBundle.getMessage(
this.getClass(),
 
  354                                     "FXVideoPanel.pauseButton.infoLabel.playbackErr"));
 
  360             stopButton.setOnAction((ActionEvent e) -> {
 
  361                 if (mediaPlayer == null) {
 
  368             progressSlider.valueProperty().addListener((Observable o) -> {
 
  369                 if (mediaPlayer == null) {
 
  373                 if (progressSlider.isValueChanging()) {
 
  374                     mediaPlayer.seek(duration.multiply(progressSlider.getValue() / 100.0));
 
  386             progressSlider.setValue(0.0);
 
  387             updateTime(Duration.ZERO);
 
  400             Media media = 
new Media(mediaUri);
 
  402             MediaPlayer player = 
new MediaPlayer(media);
 
  404             final Runnable pauseListener = () -> {
 
  405                 pauseButton.setText(PLAY_TEXT);
 
  407             player.setOnPaused(pauseListener);
 
  408             player.setOnStopped(pauseListener);
 
  409             player.setOnPlaying(() -> {
 
  410                 pauseButton.setText(PAUSE_TEXT);
 
  414             player.currentTimeProperty().addListener((observable, oldTime, newTime) -> {
 
  415                 updateSlider(newTime);
 
  427             if (mediaPlayer == null) {
 
  430             Duration currentTime = mediaPlayer.getCurrentTime();
 
  431             updateSlider(currentTime);
 
  432             updateTime(currentTime);
 
  441             if (progressSlider != null) {
 
  442                 progressSlider.setDisable(currentTime.isUnknown());
 
  443                 if (!progressSlider.isDisabled() && duration.greaterThan(Duration.ZERO)
 
  444                         && !progressSlider.isValueChanging()) {
 
  445                     progressSlider.setValue(currentTime.divide(duration.toMillis()).toMillis() * 100.0);
 
  456             long millisElapsed = (long) currentTime.toMillis();
 
  458             long elapsedHours, elapsedMinutes, elapsedSeconds;
 
  460             long secondsElapsed = millisElapsed / 1000;
 
  461             elapsedHours = (int) secondsElapsed / 3600;
 
  462             secondsElapsed -= elapsedHours * 3600;
 
  463             elapsedMinutes = (int) secondsElapsed / 60;
 
  464             secondsElapsed -= elapsedMinutes * 60;
 
  465             elapsedSeconds = (int) secondsElapsed;
 
  467             String durationStr = String.format(durationFormat,
 
  468                     elapsedHours, elapsedMinutes, elapsedSeconds,
 
  469                     totalHours, totalMinutes, totalSeconds);
 
  470             Platform.runLater(() -> {
 
  471                 progressLabel.setText(durationStr);
 
  476             Platform.runLater(() -> {
 
  477                 infoLabel.setTooltip(
new Tooltip(text));
 
  490                 if (mediaPlayer == null) {
 
  494                 duration = mediaPlayer.getMedia().getDuration();
 
  495                 long durationInMillis = (long) mediaPlayer.getMedia().getDuration().toMillis();
 
  498                 long durationSeconds = (int) durationInMillis / 1000;
 
  499                 totalHours = (int) durationSeconds / 3600;
 
  500                 durationSeconds -= totalHours * 3600;
 
  501                 totalMinutes = (int) durationSeconds / 60;
 
  502                 durationSeconds -= totalMinutes * 60;
 
  503                 totalSeconds = (int) durationSeconds;
 
  517                 if (mediaPlayer == null) {
 
  521                 Duration beginning = mediaPlayer.getStartTime();
 
  524                 pauseButton.setText(PLAY_TEXT);
 
  525                 updateSlider(beginning);
 
  526                 updateTime(beginning);
 
  543                 this.sourceFile = sFile;
 
  544                 this.tempFile = jFile;
 
  553                 return Paths.get(tempFile.getAbsolutePath()).toUri().toString();
 
  557             protected Long 
call() throws Exception {
 
  558                 if (tempFile.exists() == 
false || tempFile.length() < sourceFile.getSize()) {
 
  559                     progress = ProgressHandle.createHandle(
 
  560                             NbBundle.getMessage(
this.getClass(),
 
  561                                     "FXVideoPanel.progress.bufferingFile",
 
  566                     Platform.runLater(() -> {
 
  567                         progressLabel.setText(NbBundle.getMessage(
this.getClass(), 
"FXVideoPanel.progressLabel.buffering"));
 
  572                         Files.createParentDirs(tempFile);
 
  574                     } 
catch (IOException ex) {
 
  575                         logger.log(Level.WARNING, 
"Error buffering file", ex); 
 
  578                         logger.log(Level.INFO, 
"Done buffering: {0}", tempFile.getName()); 
 
  603                 progressLabel.setText(
"");
 
  606                 } 
catch (CancellationException ex) {
 
  607                     logger.log(Level.INFO, 
"Media buffering was canceled."); 
 
  608                     progressLabel.setText(NbBundle.getMessage(
this.getClass(), 
"FXVideoPanel.progress.bufferingCancelled"));
 
  609                 } 
catch (InterruptedException ex) {
 
  610                     logger.log(Level.INFO, 
"Media buffering was interrupted."); 
 
  611                     progressLabel.setText(NbBundle.getMessage(
this.getClass(), 
"FXVideoPanel.progress.bufferingInterrupted"));
 
  612                 } 
catch (Exception ex) {
 
  613                     logger.log(Level.SEVERE, 
"Fatal error during media buffering.", ex); 
 
  614                     progressLabel.setText(NbBundle.getMessage(
this.getClass(), 
"FXVideoPanel.progress.errorWritingVideoToDisk"));
 
  616                     if (null != progress) {
 
  619                     if (!this.isCancelled()) {
 
  620                         logger.log(Level.INFO, 
"ExtractMedia is done: {0}", tempFile.getName()); 
 
  624                         } 
catch (MediaException ex) {
 
  625                             progressLabel.setText(
"");
 
  626                             mediaPane.
setInfoLabelText(NbBundle.getMessage(
this.getClass(), 
"FXVideoPanel.media.unsupportedFormat"));
 
  645     public List<VideoFrame> 
captureFrames(java.io.File file, 
int numFrames) 
throws Exception {
 
  652         return EXTENSIONS.clone();
 
javafx.embed.swing.JFXPanel jFXPanel
static boolean isJavaFxInited()
List< String > getMimeTypes()
static< T > long writeToFile(Content content, java.io.File outputFile, ProgressHandle progress, Future< T > worker, boolean source)
List< VideoFrame > captureFrames(java.io.File file, int numFrames)
synchronized static Logger getLogger(String name)