gpt4 book ai didi

java - 有没有办法将 JavaFX 动画导出到图像帧?

转载 作者:行者123 更新时间:2023-12-04 23:06:52 25 4
gpt4 key购买 nike

我想要完成的事情:
制作最终会在 video.mp4(或任何其他扩展名)中协调的 JavaFX 动画
一种方法:
在动画播放期间,将 30fps(或 60fps)导出为静止图像(即 png 图像)。之后使用一些工具处理图像并创建视频。
那么如何创建 JavaFX 窗口的框架呢?

最佳答案

免责声明
以下解决方案仅作为概念证明提供,不附带解释,不支持评论,不保证它会为您工作,不保证它会没有错误(它可能有一些小错误),并且没有 promise 它将适用于任何目的。
解决方案策略

  • 创建动画。
  • 将其捕获到 mjpeg 文件中。
  • Play back the mjpeg file .

  • Capture 使用 James 和 mipa 评论中建议的技术:

    Create a Scene which is not displayed in a window containing your animation. Don't play() the animation, but repeatedly call jumpTo(...) on the animation to create each frame, snapshot the result, and write to a file.


    JPEG 是一种有损编码方法,此示例的输出有点模糊(可能是由于可以调整的默认 ImageIO 设置)。
    如果需要,可以将每一帧以无损格式(如 png)输出到单独的文件,而不是 mjpeg,然后通过第三方处理软件运行以创建另一种视频格式,如 mp4。
    MjpegCaptureAndPlayApp.java
    package com.example.mjpeg;

    import javafx.animation.Animation;
    import javafx.animation.TranslateTransition;
    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.scene.layout.Pane;
    import javafx.scene.layout.StackPane;
    import javafx.scene.layout.VBox;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Circle;
    import javafx.stage.Stage;
    import javafx.util.Duration;

    import java.nio.file.Files;

    public class MjpegCaptureAndPlayApp extends Application {
    final private double W = 100, H = 100;

    private MjpegPlayer player;

    @Override
    public void start(Stage stage) throws Exception {
    String movieFile = Files.createTempFile("mjpeg-capture", "mjpeg").toString();

    CaptureAnimation captureAnimation = createCaptureAnimation();
    MjpegCapture mjpegCapture = new MjpegCapture(
    movieFile,
    captureAnimation.root(),
    captureAnimation.animation()
    );
    mjpegCapture.capture();

    player = new MjpegPlayer(movieFile);
    StackPane viewer = new StackPane(player.getViewer());
    viewer.setPrefSize(W, H);

    VBox layout = new VBox(20);
    layout.setStyle("-fx-background-color: cornsilk;");
    layout.setPadding(new Insets(10));
    layout.setAlignment(Pos.CENTER);

    layout.getChildren().setAll(
    viewer,
    player.getControls()
    );

    stage.setScene(new Scene(layout));
    stage.show();

    player.getTimeline().playFromStart();
    }

    @Override
    public void stop() throws Exception {
    if (player != null) {
    player.dispose();
    }
    }

    record CaptureAnimation(Parent root, Animation animation) {}

    private CaptureAnimation createCaptureAnimation() {
    Pane root = new Pane();
    root.setMinSize(Pane.USE_PREF_SIZE, Pane.USE_PREF_SIZE);
    root.setPrefSize(W, H);
    root.setMaxSize(Pane.USE_PREF_SIZE, Pane.USE_PREF_SIZE);

    Circle circle = new Circle(W / 10, Color.PURPLE);
    root.getChildren().add(circle);

    TranslateTransition translateTransition = new TranslateTransition(
    Duration.seconds(5),
    circle
    );
    translateTransition.setFromX(0);
    translateTransition.setToX(W);
    translateTransition.setFromY(H/2);
    translateTransition.setToY(H/2);
    translateTransition.setAutoReverse(true);
    translateTransition.setCycleCount(2);

    // move to start pos.
    circle.setTranslateX(0);
    circle.setTranslateY(H/2);

    return new CaptureAnimation(root, translateTransition);
    }

    public static void main(String[] args) {
    launch(args);
    }
    }
    MjpegCapture.java
    package com.example.mjpeg;

    import javafx.animation.Animation;
    import javafx.embed.swing.SwingFXUtils;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.scene.SnapshotParameters;
    import javafx.scene.image.WritableImage;
    import javafx.util.Duration;

    import javax.imageio.ImageIO;
    import java.awt.*;
    import java.awt.image.BufferedImage;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;

    public class MjpegCapture {

    private final Duration SECS_PER_FRAME = Duration.seconds(1.0 / 24);

    private final String videoFilename;
    private final Parent root;
    private final Animation animation;

    public MjpegCapture(String videoFilename, Parent root, Animation animation) {
    this.videoFilename = videoFilename;
    this.root = root;
    this.animation = animation;
    }

    public void capture() throws IOException {
    VideoStreamOutput videoStreamOutput = new VideoStreamOutput(videoFilename);

    animation.playFromStart();
    Duration curPos = Duration.ZERO;

    SnapshotParameters snapshotParameters = new SnapshotParameters();
    // transparent fill not supported by jpeg I believe so not enabled.
    //snapshotParameters.setFill(Color.TRANSPARENT);

    Scene scene = new Scene(root);

    // force a layout pass so that we can measure the height and width of the root node.
    scene.snapshot(null);
    int w = (int) scene.getWidth();
    int h = (int) scene.getHeight();
    WritableImage fxImage = new WritableImage(w, h);

    boolean complete;
    ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
    do {
    animation.jumpTo(curPos);
    root.snapshot(snapshotParameters, fxImage);

    // Get buffered image:
    // uses ugly, inefficient workaround from:
    // https://stackoverflow.com/a/19605733/1155209
    BufferedImage image = SwingFXUtils.fromFXImage(fxImage, null);

    // Remove alpha-channel from buffered image:
    BufferedImage imageRGB = new BufferedImage(
    image.getWidth(),
    image.getHeight(),
    BufferedImage.OPAQUE);

    Graphics2D graphics = imageRGB.createGraphics();
    graphics.drawImage(image, 0, 0, null);
    ImageIO.write(imageRGB, "jpg", outputBuffer);

    videoStreamOutput.writeNextFrame(outputBuffer.toByteArray());
    outputBuffer.reset();

    complete = curPos.greaterThanOrEqualTo(animation.getTotalDuration());

    if (curPos.add(SECS_PER_FRAME).greaterThan(animation.getTotalDuration())) {
    curPos = animation.getTotalDuration();
    } else {
    curPos = curPos.add(SECS_PER_FRAME);
    }
    } while(!complete);

    videoStreamOutput.close();
    }
    }
    MjpegPlayer.java
    package com.example.mjpeg;

    import javafx.animation.KeyFrame;
    import javafx.animation.Timeline;
    import javafx.geometry.Pos;
    import javafx.scene.control.Button;
    import javafx.scene.image.Image;
    import javafx.scene.image.ImageView;
    import javafx.scene.layout.HBox;
    import javafx.util.Duration;

    import java.io.ByteArrayInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.util.Arrays;

    public class MjpegPlayer {

    private final String videoFilename;
    private final Timeline timeline;
    private final ImageView viewer = new ImageView();
    private final HBox controls;
    private VideoStream videoStream;

    public MjpegPlayer(String filename) throws FileNotFoundException {
    videoFilename = filename;
    videoStream = new VideoStream(filename);
    timeline = createTimeline(viewer);
    controls = createControls(timeline);
    }

    private Timeline createTimeline(ImageView viewer) {
    final Timeline timeline = new Timeline();
    final byte[] buf = new byte[15000];

    timeline.getKeyFrames().setAll(
    new KeyFrame(Duration.ZERO, event -> {
    try {
    int len = videoStream.readNextFrame(buf);
    if (len == -1) {
    timeline.stop();
    return;
    }
    viewer.setImage(
    new Image(
    new ByteArrayInputStream(
    Arrays.copyOf(buf, len)
    )
    )
    );
    } catch (Exception e) {
    e.printStackTrace();
    }
    }),
    new KeyFrame(Duration.seconds(1.0 / 24))
    );
    timeline.setCycleCount(Timeline.INDEFINITE);

    return timeline;
    }

    private HBox createControls(final Timeline timeline) {
    Button play = new Button("Play");
    play.setOnAction(event -> timeline.play());

    Button pause = new Button("Pause");
    pause.setOnAction(event -> timeline.pause());

    Button restart = new Button("Restart");
    restart.setOnAction(event -> {
    try {
    timeline.stop();
    videoStream = new VideoStream(videoFilename);
    timeline.playFromStart();
    } catch (Exception e) {
    e.printStackTrace();
    }
    });

    HBox controls = new HBox(10);
    controls.setAlignment(Pos.CENTER);
    controls.getChildren().setAll(
    play,
    pause,
    restart
    );

    return controls;
    }

    public void dispose() throws IOException {
    videoStream.close();
    }

    public String getVideoFilename() {
    return videoFilename;
    }

    public Timeline getTimeline() {
    return timeline;
    }

    public ImageView getViewer() {
    return viewer;
    }

    public HBox getControls() {
    return controls;
    }
    }
    视频流.java
    package com.example.mjpeg;

    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.IOException;

    class VideoStream {

    private final FileInputStream fis; //video file

    VideoStream(String filename) throws FileNotFoundException {
    fis = new FileInputStream(filename);
    }

    int readNextFrame(byte[] frame) throws Exception {
    int length;
    String lengthAsString;
    byte[] lengthAsBytes = new byte[5];

    //read current frame length
    fis.read(lengthAsBytes, 0, 5);

    //transform lengthAsBytes to integer
    lengthAsString = new String(lengthAsBytes);
    try {
    length = Integer.parseInt(lengthAsString);
    } catch (Exception e) {
    return -1;
    }

    return (fis.read(frame, 0, length));
    }

    void close() throws IOException {
    fis.close();
    }
    }
    视频流输出.java
    package com.example.mjpeg;

    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.nio.charset.StandardCharsets;

    class VideoStreamOutput {
    private FileOutputStream fos; //video file
    private int frameNum; //current frame nb

    public VideoStreamOutput(String filename) throws FileNotFoundException {
    fos = new FileOutputStream(filename);
    frameNum = 0;
    }

    public void writeNextFrame(byte[] frame) throws IOException {
    frameNum++;

    String lengthAsString = String.format("%05d", frame.length);
    byte[] lengthAsBytes = lengthAsString.getBytes(StandardCharsets.US_ASCII);

    fos.write(lengthAsBytes);
    fos.write(frame);

    System.out.println(frameNum + ": " + lengthAsString);
    }

    public void close() throws IOException {
    fos.flush();
    fos.close();
    }
    }
    模块信息.java
    module com.example.mjpeg {
    requires javafx.controls;
    requires javafx.swing;
    requires java.desktop;

    exports com.example.mjpeg;
    }

    关于java - 有没有办法将 JavaFX 动画导出到图像帧?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70152779/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com