欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

使用javaFX 做一个屏幕录像工具

程序员文章站 2024-02-06 22:46:46
...

基于https://blog.csdn.net/qq_36270700/article/details/109334232 上一篇,用javaFX做一个屏幕选择框后,借助ffmpeg,实现屏幕录制

整体思路为

1、选取屏幕区域,通过屏幕选择框,可以得到选取得坐标

2、根据帧率 , 设置间隔时间,再通过Robot截取指定区域屏幕

3、通过ffmpeg将截图合成视频

4、通过ffmpeg将视频转换为gif

重要的是 通过视频合成的gif体积较小!

实现效果:

 

使用javaFX 做一个屏幕录像工具

代码就两个类,跑Main的main函数即可

这边默认帧率为100,实际上会加速,因为通过计算,每秒期望截10张图,但是执行过程中会有短暂的延迟,导致和期待帧率有偏差,这个可以自己调整

 

比较简单使用javaFX 做一个屏幕录像工具总共两个类加一个ffmpeg.exe

 

ScreenUtil

package screen;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;

import javax.imageio.ImageIO;


public class ScreenUtil {
    static String ffmpeg = ScreenUtil.class.getResource("ffmpeg.exe").getFile();

    int bigNum = 1000000000;

    private String PATH = null;

    private String fileTypeName = "jpg";
    private String fileType = "." + fileTypeName;

    private String fieldName = "test";

    public ScreenUtil(String fieldName) {
        this.fieldName = fieldName;
    }

    public String getPATH() {
        return PATH;
    }

    public void setPATH(String PATH) {
        File file = new File(PATH + "/" + this.fieldName);
        while(file.exists()){
            this.fieldName = this.fieldName + "1";
            file = new File(PATH + "/" + this.fieldName);
        }
        this.PATH = PATH + "/";
    }

    public String getFileNamePatten(){
        return PATH + fieldName + "/1%09d" + fileType ;
    }

    //画鼠标
    private void buildMousePic(BufferedImage image,Double x,Double y) {
        Point p= MouseInfo.getPointerInfo().getLocation();
        image.createGraphics().drawOval(p.x-x.intValue() -20, p.y-y.intValue() -20,20,20);
    }

    public void printScreen(Double x,Double y,Double w ,Double h,int num) throws AWTException, IOException {
        //转视屏的时候需要32的倍数和2的倍数
        if (w % 32 != 0)
        {
            w = w.intValue() / 32 * 32D;
        }

        if (h % 2 != 0) {
            h = h.intValue() / 2 * 2D ;
        }
        // 截取屏幕
        BufferedImage image = new Robot().createScreenCapture(new Rectangle(x.intValue(),y.intValue(),w.intValue(),h.intValue()));
        buildMousePic(image,x,y);
        // 创建一个用于保存图片的文件夹
        File screenCaptureDirectory = new File(PATH + fieldName);
        if (!screenCaptureDirectory.exists()) {
            screenCaptureDirectory.mkdirs();
            System.out.println("The directory " + screenCaptureDirectory.getName() + " is created.");
        }
        // 指定路径,并以特定的日期格式作为图片的名称
        File imageFile = new File(screenCaptureDirectory, (bigNum +num + fileType));
        // 以指定的图片格式将截取的图片写到指定的文件
        ImageIO.write(image, fileTypeName, imageFile);
    }

    public void deleteField(){
        System.out.println(PATH + fieldName);
        File file = new File(PATH + fieldName);
        deleteFile(file);
    }

    /**
     * 删除文件
     * @param file
     */
    private void deleteFile(File file){
        if(file.isDirectory() && file.listFiles().length > 0){
            for(File f : file.listFiles()){
                deleteFile(f);
            }
            file.delete();
        }else{
            file.delete();
        }
    }

    //默认帧率
    int framerate = 60;

    public void toGif() throws IOException, InterruptedException {
        //转视频
        String outPutMp4 = PATH + fieldName + "/temp.mp4";
        ProcessBuilder processBuilder = new ProcessBuilder(ffmpeg, "-f", "image2","-framerate" , framerate+"",
            "-i",getFileNamePatten(),
            "-vcodec" , "libx264" , "-r" ,framerate + "",
            outPutMp4);
        doProcessBuilder(processBuilder);
        //mp4 转 gif
        String outPutGif =  PATH + fieldName + ".gif";
        processBuilder = new ProcessBuilder(ffmpeg, "-i", outPutMp4, "-r" ,framerate + "",outPutGif);
        doProcessBuilder(processBuilder);
        //执行完毕删除截图文件,仅保留gif文件
        deleteField();
        System.out.println("over ");
        try {
            Desktop.getDesktop().open(new File( PATH));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void doProcessBuilder(ProcessBuilder processBuilder) throws InterruptedException, IOException {
        Process process = processBuilder.start();
        java.io.BufferedReader readerInput = new java.io.BufferedReader(
            new InputStreamReader(process.getErrorStream()));
        String inputLine = "";
        while ((inputLine = readerInput.readLine()) != null) {
            System.out.println(inputLine);
        }
        process.waitFor();
        process.destroy();
    }
}

Main:

package screen;

import java.awt.*;
import java.io.File;
import java.util.concurrent.ExecutorService;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.Background;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.DirectoryChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

import static java.util.concurrent.Executors.newFixedThreadPool;

public class Main extends Application {
    /**
     * 屏幕尺寸
     */
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    private int width = (int)screenSize.getWidth();
    private int height = (int)screenSize.getHeight();

    /**
     * 屏幕中心开始,画一个默认的正方形状
     */
    private int defaultWidth = 100;

    /**
     * 默认线条宽度
     */
    private int defaultLineWidth = 10;


    /**
     * 当点击的时候,给个误差值,确保能达到拖拽的目的
     */
    private int defaultFixWidth = 10;
    /**
     * 定义
     * 左上角坐标 (x,y)
     * 右下角坐标(w,h)
     */
    double x,y,w,h = 0.0;

    /**
     *     a
     *  d ▢ b
     *    c
     * 定义矩形的四个边,被点击后为true
     */
    boolean a,b,c,d = false;

    /**
     *
     * @param primaryStage
     */
    boolean stop = true;
    private Stage stage;
    private ExecutorService executorService = newFixedThreadPool(1);
    ScreenUtil screenUtil = new ScreenUtil("random");

    @Override
    public void start(Stage primaryStage) {
        stage = primaryStage;
        StackPane root = new StackPane();
        Canvas canvas = new Canvas(width, height);
        Button startBtn = new Button("start");
        Button exitBtn = new Button("exit");
        GraphicsContext gc = canvas.getGraphicsContext2D();
        gc.setStroke(Color.RED);
        gc.setLineWidth(defaultLineWidth);
        x = width/2 - defaultWidth;
        y = height/2 - defaultWidth;
        w = width/2 + defaultWidth;
        h = height/2 + defaultWidth;
        drawRect(gc,x,y,w,h);
        //拖拽事件
        canvas.setOnMouseDragged(event ->{
            //清空一下
            gc.clearRect(0, 0, width, height);
            if(a || c){
                //上边被点击
                if(d){
                    //左边被点击(=左上角被点击)
                    x = event.getX();
                }else if(b){
                    //右边被点击(=右上角被点击)
                    w = event.getX();
                }
                if(a){
                    y = event.getY();
                }else{
                    h = event.getY();
                }
            }else if(b || d){
                //右边被点击
                if(a){
                    //右上角
                    y = event.getY();
                }else if(c){
                    //右下角
                    h = event.getY();
                }
                if(b){
                    w = event.getX();
                }else{
                    x = event.getX();
                }
            }
            drawRect(gc,x,y,w,h);
            startBtn.setTranslateX(w-width/2+startBtn.getWidth()/2);
            startBtn.setTranslateY(y-height/2+startBtn.getHeight()/2);
            exitBtn.setTranslateX(w-width/2+startBtn.getWidth()/2);
            exitBtn.setTranslateY(y-height/2+startBtn.getHeight()/2 + 35);
        });
        //当点击的时候,取得点击的abcd哪些边
        canvas.setOnMousePressed(event ->{
            a=b=c=d=false;
            //点击上边界
            if(event.getY() < (y + defaultFixWidth)) {
                a = true;
            }
            //下边界
            if(event.getY() > (h - defaultFixWidth)){
                c = true;
            }
            //点击左边界
            if(event.getX() < (x + defaultFixWidth)) {
                d = true;
            }
            //右边界
            if(event.getX() > (w - defaultFixWidth)){
                b = true;
            }
        });

        root.getChildren().add(canvas);

        //开始按钮
        root.getChildren().add(startBtn(startBtn));
        //退出按钮
        root.getChildren().add(exitBtn(exitBtn));

        //透明底
        root.setBackground(Background.EMPTY);
        primaryStage.initStyle(StageStyle.TRANSPARENT);
        Scene scene = new Scene(root, width, height);
        scene.setFill(null);
        primaryStage.setScene(scene);
        //常驻顶层
        primaryStage.setAlwaysOnTop(true);
        primaryStage.show();

        if(screenUtil.getPATH() == null){
            primaryStage.close();
            DirectoryChooser directoryChooser = new DirectoryChooser();
            directoryChooser.setTitle("请先选择保存路径");
            File directory = directoryChooser.showDialog(new Stage());
            if (directory != null) {
                screenUtil.setPATH(directory.getAbsolutePath());
                primaryStage.show();
            }

        }
    }

    private Button startBtn(Button startBtn){
        startBtn.setMinWidth(50);
        startBtn.setMinHeight(30);
        startBtn.setTranslateX(w-width/2+30);
        startBtn.setTranslateY(y-height/2+15);
        startBtn.setOnAction(event -> {
            stop = !stop;
            System.out.println(stop);
            if(stop){
                startBtn.setText("start");
                dialog();
            }else{
                startBtn.setText("stop");
                printScreenGif();
            }

        });
        return startBtn;
    }

    private Button exitBtn(Button exitBtn){
        exitBtn.setMinWidth(50);
        exitBtn.setMinHeight(30);
        exitBtn.setTranslateX(w-width/2+30);
        exitBtn.setTranslateY(y-height/2+15+35);
        exitBtn.setOnAction(event -> System.exit(0));
        return exitBtn;
    }

    private void drawRect(GraphicsContext gc, double x, double y, double w,double h){
        gc.strokeLine(x,y,w,y);
        gc.strokeLine(x,y,x,h);
        gc.strokeLine(w,h,w,y);
        gc.strokeLine(w,h,x,h);
    }

    private void printScreenGif(){
        executorService.execute(() ->{
            try {
                for(int i = 0 ; i < 99999999 ; i++){
                    screenUtil.printScreen(x+defaultLineWidth,y,w-x-defaultLineWidth-defaultLineWidth,h-y-defaultLineWidth-defaultLineWidth,i);
                    Thread.sleep(1000/screenUtil.framerate);
                    if(stop){
                        break;
                    }
                }
                screenUtil.toGif();
                endJob();
            } catch (Exception e) {
                e.printStackTrace();
            }
        });

    }
    private Stage dialogStage;
    private void dialog() {

        dialogStage = new Stage();
        ProgressIndicator progressIndicator = new ProgressIndicator();
        // 窗口父子关系
        dialogStage.initOwner(stage);
        dialogStage.initStyle(StageStyle.UNDECORATED);
        dialogStage.initStyle(StageStyle.TRANSPARENT);
        dialogStage.initModality(Modality.APPLICATION_MODAL);
        // progress bar
        javafx.scene.control.Label label = new Label("操作中....想取消,请杀进程");
        label.setTextFill(Color.RED);
        progressIndicator.setProgress(-1F);
        VBox vBox = new VBox();
        vBox.setSpacing(10);
        vBox.setBackground(Background.EMPTY);
        vBox.getChildren().addAll(progressIndicator, label);
        Scene scene = new Scene(vBox);
        scene.setFill(null);
        dialogStage.setScene(scene);
        dialogStage.show();
    }

    /**
     * 收尾工作
     */
    private void endJob() {
        Platform.runLater(() -> {
            dialogStage.close();
        });
    }
    public static void main(String[] args) {
        launch(args);
    }
}

ffmpeg则要自己去下载,这边提供免费下载链接 https://download.csdn.net/download/qq_36270700/13086883

相关标签: JAVAFX javafx

上一篇: 用C开发PHP扩张初探

下一篇: