Java案例如何实现录屏?

wen python案例 2

本文目录导读:

Java案例如何实现录屏?

  1. 方案一:使用Robot类 + JavaCV(最常用)
  2. 方案二:使用JNativeHook实现热键控制
  3. 方案三:带GUI的控制界面
  4. 方案四:高性能录屏(使用grabber)
  5. 注意事项

在Java中实现录屏功能,主要有几种不同的技术方案,下面我会详细介绍几种常见的实现方式,并提供完整的代码示例。

使用Robot类 + JavaCV(最常用)

这是最经典的方式,使用java.awt.Robot截取屏幕,配合JavaCV生成视频文件。

Maven依赖

<dependency>
    <groupId>org.bytedeco</groupId>
    <artifactId>javacv-platform</artifactId>
    <version>1.5.9</version>
</dependency>

完整实现代码

import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.Java2DFrameConverter;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ScreenRecorder {
    private FFmpegFrameRecorder recorder;
    private volatile boolean recording = false;
    private Rectangle screenBounds;
    private Robot robot;
    private Thread recordingThread;
    private String outputPath;
    public ScreenRecorder(String outputPath) {
        this.outputPath = outputPath;
        this.screenBounds = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
        try {
            this.robot = new Robot();
        } catch (AWTException e) {
            e.printStackTrace();
        }
    }
    public void startRecording() {
        if (recording) return;
        recording = true;
        recordingThread = new Thread(() -> {
            try {
                // 初始化录制器
                recorder = new FFmpegFrameRecorder(outputPath, 
                    screenBounds.width, screenBounds.height);
                recorder.setVideoCodec(org.bytedeco.javacv.FrameRecorder.VideoCodec.H264);
                recorder.setFormat("mp4");
                recorder.setFrameRate(30);
                recorder.setPixelFormat(org.bytedeco.javacv.FrameRecorder.PixelFormat.YUV420P);
                recorder.start();
                Java2DFrameConverter converter = new Java2DFrameConverter();
                while (recording) {
                    // 截图
                    BufferedImage screenCapture = robot.createScreenCapture(screenBounds);
                    BufferedImage image = convertToType(screenCapture, BufferedImage.TYPE_3BYTE_BGR);
                    // 转换为Frame并录制
                    Frame frame = converter.convert(image);
                    recorder.record(frame);
                    // 控制帧率
                    Thread.sleep(33); // 约30fps
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (recorder != null) {
                        recorder.stop();
                        recorder.release();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
        recordingThread.start();
        System.out.println("录屏已开始,输出文件:" + outputPath);
    }
    public void stopRecording() {
        recording = false;
        if (recordingThread != null) {
            try {
                recordingThread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("录屏已停止");
    }
    private BufferedImage convertToType(BufferedImage sourceImage, int targetType) {
        BufferedImage image = new BufferedImage(
            sourceImage.getWidth(), 
            sourceImage.getHeight(), 
            targetType
        );
        Graphics2D g = image.createGraphics();
        g.drawImage(sourceImage, 0, 0, null);
        g.dispose();
        return image;
    }
    // 使用示例
    public static void main(String[] args) {
        String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String outputPath = "screen_recording_" + timestamp + ".mp4";
        ScreenRecorder recorder = new ScreenRecorder(outputPath);
        recorder.startRecording();
        // 录制10秒
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        recorder.stopRecording();
    }
}

使用JNativeHook实现热键控制

添加全局热键支持,方便开始/停止录制:

<dependency>
    <groupId>com.github.kwhat</groupId>
    <artifactId>jnativehook</artifactId>
    <version>2.2.2</version>
</dependency>
import com.github.kwhat.jnativehook.GlobalScreen;
import com.github.kwhat.jnativehook.keyboard.NativeKeyEvent;
import com.github.kwhat.jnativehook.keyboard.NativeKeyListener;
public class HotkeyRecorder extends ScreenRecorder implements NativeKeyListener {
    public HotkeyRecorder(String outputPath) {
        super(outputPath);
    }
    @Override
    public void nativeKeyPressed(NativeKeyEvent e) {
        // F6 开始录制,F7 停止录制
        if (e.getKeyCode() == NativeKeyEvent.VC_F6) {
            startRecording();
        } else if (e.getKeyCode() == NativeKeyEvent.VC_F7) {
            stopRecording();
        }
    }
    @Override
    public void nativeKeyReleased(NativeKeyEvent e) {}
    @Override
    public void nativeKeyTyped(NativeKeyEvent e) {}
    public void registerHotkeys() {
        try {
            GlobalScreen.registerNativeHook();
            GlobalScreen.addNativeKeyListener(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void unregisterHotkeys() {
        try {
            GlobalScreen.unregisterNativeHook();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String[] args) {
        String outputPath = "screen_recording_hotkey.mp4";
        HotkeyRecorder recorder = new HotkeyRecorder(outputPath);
        recorder.registerHotkeys();
        System.out.println("按 F6 开始录制,按 F7 停止录制");
        // 防止程序退出
        try {
            Thread.sleep(Long.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

带GUI的控制界面

提供一个简单的Swing界面控制录制:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.SimpleDateFormat;
import java.util.Date;
public class ScreenRecorderGUI extends JFrame {
    private ScreenRecorder recorder;
    private JButton startButton;
    private JButton stopButton;
    private JLabel statusLabel;
    private JTextField fpsField;
    public ScreenRecorderGUI() {
        setTitle("屏幕录制器");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLayout(new BorderLayout());
        // 控制面板
        JPanel controlPanel = new JPanel();
        startButton = new JButton("开始录制");
        stopButton = new JButton("停止录制");
        stopButton.setEnabled(false);
        statusLabel = new JLabel("就绪");
        // FPS设置
        controlPanel.add(new JLabel("FPS:"));
        fpsField = new JTextField("30", 5);
        controlPanel.add(fpsField);
        controlPanel.add(startButton);
        controlPanel.add(stopButton);
        add(controlPanel, BorderLayout.NORTH);
        add(statusLabel, BorderLayout.CENTER);
        // 事件监听
        startButton.addActionListener(e -> startRecording());
        stopButton.addActionListener(e -> stopRecording());
        pack();
        setLocationRelativeTo(null);
    }
    private void startRecording() {
        String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String outputPath = "recording_" + timestamp + ".mp4";
        recorder = new ScreenRecorder(outputPath);
        recorder.startRecording();
        startButton.setEnabled(false);
        stopButton.setEnabled(true);
        statusLabel.setText("正在录制... " + outputPath);
    }
    private void stopRecording() {
        if (recorder != null) {
            recorder.stopRecording();
        }
        startButton.setEnabled(true);
        stopButton.setEnabled(false);
        statusLabel.setText("录制完成");
    }
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            new ScreenRecorderGUI().setVisible(true);
        });
    }
}

高性能录屏(使用grabber)

对于更高性能要求的场景,可以使用JavaCV的FrameGrabber:

import org.bytedeco.javacv.*;
import org.bytedeco.javacpp.*;
public class HighPerformanceRecorder {
    public static void startRecording(String outputPath, int duration) {
        Rectangle screenBounds = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
        try {
            // 创建截屏抓取器
            FFmpegFrameGrabber grabber = new FFmpegFrameGrabber("desktop");
            grabber.setFormat("gdigrab"); // Windows下使用gdigrab
            grabber.setFrameRate(30);
            grabber.setImageWidth(screenBounds.width);
            grabber.setImageHeight(screenBounds.height);
            grabber.start();
            // 创建录制器
            FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputPath, 
                screenBounds.width, screenBounds.height);
            recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
            recorder.setFormat("mp4");
            recorder.setFrameRate(30);
            recorder.start();
            long startTime = System.currentTimeMillis();
            Frame frame;
            while ((frame = grabber.grab()) != null) {
                recorder.record(frame);
                // 检查是否达到指定时长
                if (System.currentTimeMillis() - startTime > duration * 1000) {
                    break;
                }
            }
            recorder.stop();
            grabber.stop();
            System.out.println("录制完成");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

注意事项

  1. 性能优化:实际的屏幕录制对性能要求较高,特别是高分辨率下,建议:

    • 适当降低帧率(15-30fps)
    • 可以降低录制分辨率
    • 使用硬件编码加速
  2. 跨平台问题

    • Windows:使用gdigrab或Robot
    • macOS:使用AVFoundation
    • Linux:使用X11或KMS
  3. 权限问题:某些系统可能需要屏幕录制权限

  4. 内存管理:长时间录屏要注意内存使用,及时清理

  5. 音频录制:如需同时录制音频,需要额外添加音频捕获功能

方案中,方案一(Robot + JavaCV)是最通用和稳定的选择,你可以根据实际需求选择合适的实现方式,并在此基础上进行功能扩展。

抱歉,评论功能暂时关闭!