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.netbeans.api.progress.ProgressHandleFactory;
59 import org.openide.util.NbBundle;
60 import org.openide.util.lookup.ServiceProvider;
61 import org.openide.util.lookup.ServiceProviders;
74 @ServiceProviders(value = {
75 @ServiceProvider(service = FrameCapture.class)
81 private static final String[] EXTENSIONS =
new String[]{
".m4v",
".fxm",
".flv",
".m3u8",
".mp4",
".aif",
".aiff",
".mp3",
"m4a",
".wav"};
82 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");
85 private boolean fxInited =
false;
95 Platform.runLater(() -> {
98 Scene fxScene =
new Scene(mediaPane);
99 jFXPanel.setScene(fxScene);
110 void setupVideo(
final AbstractFile file,
final Dimension dims) {
111 if (file.equals(currentFile)) {
114 if (!Case.isCaseOpen()) {
121 final boolean deleted = file.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC);
123 mediaPane.
setInfoLabelText(NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.mediaPane.infoLabel"));
131 path = file.getUniquePath();
132 }
catch (TskCoreException ex) {
133 logger.log(Level.SEVERE,
"Cannot get unique path of video file", ex);
138 final File tempFile = VideoUtils.getTempVideoFile(currentFile);
140 new Thread(mediaPane.new ExtractMedia(currentFile, tempFile)).start();
146 Platform.runLater(() -> {
147 if (mediaPane != null) {
159 @SuppressWarnings(
"unchecked")
161 private
void initComponents() {
163 jFXPanel =
new javafx.embed.swing.JFXPanel();
165 setBackground(
new java.awt.Color(0, 0, 0));
167 javax.swing.GroupLayout jFXPanelLayout =
new javax.swing.GroupLayout(jFXPanel);
168 jFXPanel.setLayout(jFXPanelLayout);
169 jFXPanelLayout.setHorizontalGroup(
170 jFXPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
171 .addGap(0, 400, Short.MAX_VALUE)
173 jFXPanelLayout.setVerticalGroup(
174 jFXPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
175 .addGap(0, 300, Short.MAX_VALUE)
178 javax.swing.GroupLayout layout =
new javax.swing.GroupLayout(
this);
179 this.setLayout(layout);
180 layout.setHorizontalGroup(
181 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
182 .addComponent(jFXPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
184 layout.setVerticalGroup(
185 layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
186 .addComponent(jFXPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
237 private final String durationFormat =
"%02d:%02d:%02d/%02d:%02d:%02d ";
239 private static final String PLAY_TEXT =
"►";
241 private static final String PAUSE_TEXT =
"||";
243 private static final String STOP_TEXT =
"X";
247 mediaViewPane =
new HBox();
248 mediaViewPane.setStyle(
"-fx-background-color: black");
249 mediaViewPane.setAlignment(Pos.CENTER);
250 mediaView =
new MediaView();
251 mediaViewPane.getChildren().add(mediaView);
252 setCenter(mediaViewPane);
255 controlPanel =
new VBox();
256 mediaTools =
new HBox();
257 mediaTools.setAlignment(Pos.CENTER);
258 mediaTools.setPadding(
new Insets(5, 10, 5, 10));
260 pauseButton =
new Button(PLAY_TEXT);
261 stopButton =
new Button(STOP_TEXT);
262 mediaTools.getChildren().add(pauseButton);
263 mediaTools.getChildren().add(
new Label(
" "));
264 mediaTools.getChildren().add(stopButton);
265 mediaTools.getChildren().add(
new Label(
" "));
266 progressSlider =
new Slider();
267 HBox.setHgrow(progressSlider, Priority.ALWAYS);
268 progressSlider.setMinWidth(50);
269 progressSlider.setMaxWidth(Double.MAX_VALUE);
270 mediaTools.getChildren().add(progressSlider);
271 progressLabel =
new Label();
272 progressLabel.setPrefWidth(135);
273 progressLabel.setMinWidth(135);
274 mediaTools.getChildren().add(progressLabel);
276 controlPanel.getChildren().add(mediaTools);
277 controlPanel.setStyle(
"-fx-background-color: white");
278 infoLabel =
new Label(
"");
279 controlPanel.getChildren().add(infoLabel);
280 setBottom(controlPanel);
281 setProgressActionListeners();
289 if (mediaPlayer != null) {
290 setInfoLabelText(
"");
291 if (mediaPlayer.getStatus() == Status.PLAYING) {
295 mediaView.setMediaPlayer(null);
306 logger.log(Level.INFO,
"Setting Info Label Text: {0}", text);
307 Platform.runLater(() -> {
308 infoLabel.setText(text);
317 public void setFit(
final Dimension dims) {
318 Platform.runLater(() -> {
319 setPrefSize(dims.getWidth(), dims.getHeight());
322 mediaView.setFitHeight(dims.getHeight() - controlPanel.getHeight());
330 pauseButton.setOnAction(
new EventHandler<ActionEvent>() {
332 public void handle(ActionEvent e) {
333 if (mediaPlayer == null) {
337 Status status = mediaPlayer.getStatus();
351 logger.log(Level.INFO,
"MediaPlayer in unexpected state: {0}", status.toString());
354 setInfoLabelText(NbBundle.getMessage(
this.getClass(),
355 "FXVideoPanel.pauseButton.infoLabel.playbackErr"));
361 stopButton.setOnAction((ActionEvent e) -> {
362 if (mediaPlayer == null) {
369 progressSlider.valueProperty().addListener((Observable o) -> {
370 if (mediaPlayer == null) {
374 if (progressSlider.isValueChanging()) {
375 mediaPlayer.seek(duration.multiply(progressSlider.getValue() / 100.0));
387 progressSlider.setValue(0.0);
388 updateTime(Duration.ZERO);
401 Media media =
new Media(mediaUri);
403 MediaPlayer player =
new MediaPlayer(media);
405 final Runnable pauseListener = () -> {
406 pauseButton.setText(PLAY_TEXT);
408 player.setOnPaused(pauseListener);
409 player.setOnStopped(pauseListener);
410 player.setOnPlaying(() -> {
411 pauseButton.setText(PAUSE_TEXT);
415 player.currentTimeProperty().addListener((observable, oldTime, newTime) -> {
416 updateSlider(newTime);
428 if (mediaPlayer == null) {
431 Duration currentTime = mediaPlayer.getCurrentTime();
432 updateSlider(currentTime);
433 updateTime(currentTime);
442 if (progressSlider != null) {
443 progressSlider.setDisable(currentTime.isUnknown());
444 if (!progressSlider.isDisabled() && duration.greaterThan(Duration.ZERO)
445 && !progressSlider.isValueChanging()) {
446 progressSlider.setValue(currentTime.divide(duration.toMillis()).toMillis() * 100.0);
457 long millisElapsed = (long) currentTime.toMillis();
459 long elapsedHours, elapsedMinutes, elapsedSeconds;
461 long secondsElapsed = millisElapsed / 1000;
462 elapsedHours = (int) secondsElapsed / 3600;
463 secondsElapsed -= elapsedHours * 3600;
464 elapsedMinutes = (int) secondsElapsed / 60;
465 secondsElapsed -= elapsedMinutes * 60;
466 elapsedSeconds = (int) secondsElapsed;
468 String durationStr = String.format(durationFormat,
469 elapsedHours, elapsedMinutes, elapsedSeconds,
470 totalHours, totalMinutes, totalSeconds);
471 Platform.runLater(() -> {
472 progressLabel.setText(durationStr);
477 Platform.runLater(() -> {
478 infoLabel.setTooltip(
new Tooltip(text));
491 if (mediaPlayer == null) {
495 duration = mediaPlayer.getMedia().getDuration();
496 long durationInMillis = (long) mediaPlayer.getMedia().getDuration().toMillis();
499 long durationSeconds = (int) durationInMillis / 1000;
500 totalHours = (int) durationSeconds / 3600;
501 durationSeconds -= totalHours * 3600;
502 totalMinutes = (int) durationSeconds / 60;
503 durationSeconds -= totalMinutes * 60;
504 totalSeconds = (int) durationSeconds;
518 if (mediaPlayer == null) {
522 Duration beginning = mediaPlayer.getStartTime();
525 pauseButton.setText(PLAY_TEXT);
526 updateSlider(beginning);
527 updateTime(beginning);
544 this.sourceFile = sFile;
545 this.tempFile = jFile;
554 return Paths.get(tempFile.getAbsolutePath()).toUri().toString();
558 protected Long
call() throws Exception {
559 if (tempFile.exists() ==
false || tempFile.length() < sourceFile.getSize()) {
560 progress = ProgressHandleFactory.createHandle(
561 NbBundle.getMessage(
this.getClass(),
562 "FXVideoPanel.progress.bufferingFile",
567 Platform.runLater(() -> {
568 progressLabel.setText(NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.progressLabel.buffering"));
573 Files.createParentDirs(tempFile);
575 }
catch (IOException ex) {
576 logger.log(Level.WARNING,
"Error buffering file", ex);
579 logger.log(Level.INFO,
"Done buffering: {0}", tempFile.getName());
604 progressLabel.setText(
"");
607 }
catch (CancellationException ex) {
608 logger.log(Level.INFO,
"Media buffering was canceled.");
609 progressLabel.setText(NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.progress.bufferingCancelled"));
610 }
catch (InterruptedException ex) {
611 logger.log(Level.INFO,
"Media buffering was interrupted.");
612 progressLabel.setText(NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.progress.bufferingInterrupted"));
613 }
catch (Exception ex) {
614 logger.log(Level.SEVERE,
"Fatal error during media buffering.", ex);
615 progressLabel.setText(NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.progress.errorWritingVideoToDisk"));
617 if (null != progress) {
620 if (!this.isCancelled()) {
621 logger.log(Level.INFO,
"ExtractMedia is done: {0}", tempFile.getName());
625 }
catch (MediaException ex) {
626 progressLabel.setText(
"");
627 mediaPane.
setInfoLabelText(NbBundle.getMessage(
this.getClass(),
"FXVideoPanel.media.unsupportedFormat"));
646 public List<VideoFrame>
captureFrames(java.io.File file,
int numFrames)
throws Exception {
653 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)