java - MediaView 没有返回不同的快照

标签 java javafx javafx-11

使用以下方法播放时,我能够获取视频快照;

mediaView.snapshot(null, null);

现在我想拍摄具有一定持续时间间隙的快照,如下所示:

public class Main extends Application {
    
    Media media = null;
    MediaPlayer mediaPlayer = null;
    MediaView mediaView = null;

    public static void main(String[] args) {
        launch(args);
    }

    public void start(final Stage primaryStage) {
        primaryStage.setTitle("Video Snapshot");
        
        media = new Media(new File("filePathTo.mp4").toURI().toString());
        mediaPlayer = new MediaPlayer(media);
        mediaView = new MediaView(mediaPlayer);
        Pane pane = new Pane();
        
        mediaPlayer.setOnReady(new Runnable() {
          public void run() {
            writeToFile(0.0);
            writeToFile(1000.0);
            writeToFile(10000.0);;
            writeToFile(50000.0);
          }
      });
        
        Scene scene = new Scene(pane, 640, 480, Color.BLACK);
        primaryStage.setScene(scene);
        
        mediaPlayer.play();
        primaryStage.show();
    }
    
    
    private void writeToFile(Double double1) {
        
        mediaView.getMediaPlayer().seek( Duration.millis(double1) );
        
        WritableImage writableImage = mediaView.snapshot(null, null);
        ByteArrayOutputStream  byteOutput = new ByteArrayOutputStream();

        try {
            ImageIO.write( SwingFXUtils.fromFXImage( writableImage, new BufferedImage(200,200,BufferedImage.TYPE_INT_RGB) ), "png", byteOutput );
            byteOutput.writeTo(new FileOutputStream( Paths.get(double1.toString()).toFile() + ".png" ));
        } catch (IOException e) {
            e.printStackTrace();
        }
        
    }
}

但是现在我得到了相同的图像,而不是不同的图像,尽管返回的 writableImage 对象不同。检查文件 0.0.png、1000.0.png、10000.0.png、50000.0.png 文件。一切看起来都一样。我怎样才能获得不同的快照?

最佳答案

问题很可能是在您拍摄快照时尚未渲染新像素。您需要给 JavaFX 时间来渲染视频。以下示例对我有用:

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.concurrent.CompletableFuture;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;
import javafx.stage.Stage;
import javafx.util.Duration;
import javax.imageio.ImageIO;

public class Main extends Application {

    public void start(Stage primaryStage) {
        var player = new MediaPlayer(new Media(Path.of("video.mp4").toUri().toString()));
        player.setAutoPlay(true);

        var view = new MediaView(player);
        player.setOnPlaying(() -> {
            snapshotVideo(view, 0)
                    .thenCompose(unused -> snapshotVideo(view, 1000))
                    .thenCompose(unused -> snapshotVideo(view, 2000))
                    .thenCompose(unused -> snapshotVideo(view, 3000));
        });

        primaryStage.setScene(new Scene(new Pane(view), 1000, 650));
        primaryStage.setTitle("Video Snapshot");
        primaryStage.show();
    }

    private CompletableFuture<Void> snapshotVideo(MediaView view, double seekMillis) {
        var future = new CompletableFuture<Void>();

        view.getMediaPlayer().seek(Duration.millis(seekMillis));
        Platform.runLater(() -> {
            var fxImage = view.snapshot(null, null);
            var bufImage = SwingFXUtils.fromFXImage(fxImage, null);

            var filename = String.format("snapshot-%.0f.png", seekMillis);
            try (var out = Files.newOutputStream(Path.of(filename))) {
                ImageIO.write(bufImage, "png", out);
                future.complete(null);
            } catch (IOException ex) {
                ex.printStackTrace();
                future.completeExceptionally(ex);
            }
        });

        return future;
    }
}

它使用 Platform#runLater(Runnable)CompletableFuture 的组合。

  • 它会寻找所需的时间,然后在 runLater 调用中运行快照+保存代码。这是为了给 JavaFX 时间来渲染新的目标帧。我不知道这是否保证有效。如果我没记错的话,JavaFX 媒体实现有一个或多个后台线程来处理音频/视频。但我不知道这些线程是否会“落后”。此外,提前寻找足够远的位置可能会导致缓冲。因此,您可能必须通过监听 MediaPlayerstatus 属性来处理该问题。

  • 它使用CompletableFuture来确保在拍摄并保存前一个快照之前不会拍摄下一个快照。将图像保存代码移至后台线程可能是个好主意。我认为重要的是,在尝试拍摄下一张快照之前,快照已完全拍摄完毕。

我也会在玩家时开始拍摄这些快照,而不仅仅是准备。不确定这是否重要。我什至不确定玩家是否需要玩游戏才能发挥作用。

最后,JavaFX Media API 可能不是用于这项工作的最佳工具。如果我想要 MP4 视频中不同时间的帧,我会尝试找到一个可以直接从文件中抓取这些帧的库。

关于java - MediaView 没有返回不同的快照,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73960311/

相关文章:

javafx - 重启和暂停和恢复剪辑会挂起音乐播放器的gui,同时按下暂停和播放会从停止点恢复播放

css - fxml中的旋转图像动画

java - IntelliJ IDEA - 错误 : JavaFX runtime components are missing,,需要运行此应用程序

javafx - 带有 JavaFx 11 和 JDK 11 的 Netbeans 9.0

java - 沿方向行驶时减速

IntelliJ IDEA 中的 java Maven 项目,如何找到不需要的依赖项?

java - 在 for 循环中更新 ProgressBar 值

gradle - 如何在 Raspberry Pi 上运行 JavaFX 11 应用程序?

java - LibGDX标度法的使用

java - 单击另一个选项卡的按钮后如何执行包含在一个选项卡中的try-catch-Blackberry