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"));
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()
boolean isDirNameFlagSet(TSK_FS_NAME_FLAG_ENUM flag)
List< String > getMimeTypes()
synchronized String getUniquePath()
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)
boolean equals(Object obj)
synchronized static Logger getLogger(String name)