【JAVA】JavaCV实现简单录屏功能

最近接触的Java桌面程序需要一个录屏功能,然后就翻到了JavaCV,跟着教程大概写了一个,其中用上了之前用来截屏的Robot类

JavaCV就是一款开源的视觉处理库,封装了FFmpeg、OpenCV、videoInput和ARToolKitPlus等等计算机视觉的接口。

首先需要声明并初始化各种属性


    private ScheduledThreadPoolExecutor screenTimer;
    private Rectangle rectangle;
    private FFmpegFrameRecorder recorder;
    private Robot robot;
    private OpenCVFrameConverter.ToIplImage conveter;
    private BufferedImage screenCapture;
    private ScheduledThreadPoolExecutor exec;
    private TargetDataLine line;
    private AudioFormat audioFormat;
    private DataLine.Info dataLineInfo;
    private boolean isHaveDevice;
    private String fileName;
    private long startTime = 0;
    private long videoTS = 0;
    private long pauseTime = 0;
    private double frameRate = 5;
    
    //构造器
    public VideoRecorder(String fileName, boolean isHaveDevice) {
        init(fileName, isHaveDevice);
    }

void init(String fileName, boolean isHaveDevice) {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        rectangle = new Rectangle(screenSize.width, screenSize.height);
        recorder = new FFmpegFrameRecorder(fileName + ".mp4", screenSize.width, screenSize.height);
        recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264);
        logger.info("设置视频格式:h264");
        recorder.setFormat("mp4");
        recorder.setSampleRate(44100);
        recorder.setFrameRate(frameRate);

        recorder.setVideoQuality(0);
        recorder.setVideoOption("crf", "23");
        recorder.setVideoBitrate(1000000);
        recorder.setVideoOption("preset", "slow");
        recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
        recorder.setAudioChannels(2);
        recorder.setAudioOption("crf", "0");
        recorder.setAudioQuality(0);
//        recorder.setAudioCodec(avcodec.AV_CODEC_ID_IMC);
        try {
            robot = new Robot();
        } catch (AWTException e) {
            logger.error("AWTException : ", e);
        }
        try {
            recorder.start();
        } catch (Exception e) {
            logger.error(" 录制视频初始化异常 : ", e);
        } catch (Error error) {
            logger.error("录制视频初始化错误 : ", error);
        }
        conveter = new OpenCVFrameConverter.ToIplImage();
        this.isHaveDevice = isHaveDevice;
        this.fileName = fileName;
    }

开始录制

    public void start() {

        logger.info("录屏开始!start");
        if (startTime == 0) {
            startTime = System.currentTimeMillis();
        }
        if (pauseTime == 0) {
            pauseTime = System.currentTimeMillis();
        }

        if (isHaveDevice) {
            new Thread(() -> caputre()).start();
        }
        screenTimer = new ScheduledThreadPoolExecutor(1);
        screenTimer.scheduleAtFixedRate(() -> {
            try {
                screenCapture = robot.createScreenCapture(rectangle);
                String name = fileName + ".JPEG";
                File f = new File(name);
                try {
                    ImageIO.write(screenCapture, "JPEG", f);
                } catch (IOException e) {
                    logger.error("写文件时发生io异常 : ", e);
                }
                IplImage image = cvLoadImage(name);
                videoTS = 1000 * (System.currentTimeMillis() - startTime - (System.currentTimeMillis() - pauseTime));
                if (videoTS > recorder.getTimestamp()) {
                    recorder.setTimestamp(videoTS);
                }
                recorder.record(conveter.convert(image));
                f.delete();
                System.gc();
            } catch (Exception ex) {
                logger.error("录屏线程异常 : ", ex);
                MessageResponseHandler.uploadErrorInfo("录屏线程异常",ex);
            }
        }, (int) (1000 / frameRate), (int) (1000 / frameRate), TimeUnit.MILLISECONDS);
    }

音频录制新线程

    private void caputre() {
        audioFormat = new AudioFormat(44100.0F, 16, 2, true, false);
        dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
        try {
            line = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
            line.open(audioFormat);
        } catch (LineUnavailableException e1) {
            logger.error("准备录制音频时发生异常 : ", e1);
            MessageResponseHandler.uploadErrorInfo("准备录制音频时发生异常",e1);
        }
        line.start();

        int sampleRate = (int) audioFormat.getSampleRate();
        int numChannels = audioFormat.getChannels();

        int audioBufferSize = sampleRate * numChannels;
        byte[] audioBytes = new byte[audioBufferSize];

        exec = new ScheduledThreadPoolExecutor(1);
        exec.scheduleAtFixedRate(() -> {
            try {
                int nBytesRead = line.read(audioBytes, 0, line.available());
                int nSamplesRead = nBytesRead / 2;
                short[] samples = new short[nSamplesRead];

                ByteBuffer.wrap(audioBytes).order(ByteOrder.LITTLE_ENDIAN).asShortBuffer().get(samples);
                ShortBuffer sBuff = ShortBuffer.wrap(samples, 0, nSamplesRead);

                recorder.recordSamples(sampleRate, numChannels, sBuff);
                System.gc();
            } catch (Exception e) {
                logger.error("录制音频时发生异常 : ", e);
                MessageResponseHandler.uploadErrorInfo("录制音频时发生异常",e);
            }
        }, (int) (1000 / frameRate), (int) (1000 / frameRate), TimeUnit.MILLISECONDS);
    }

释放资源

    public void stop() {
        if (null != screenTimer) {
            screenTimer.shutdownNow();
        }
        try {
            recorder.stop();
            recorder.release();
            recorder.close();
            System.out.println("录屏结束!");
            screenTimer = null;
            screenCapture = null;
            if (isHaveDevice) {
                if (null != exec) {
                    exec.shutdownNow();
                }
                if (null != line) {
                    line.stop();
                    line.close();
                }
                dataLineInfo = null;
                audioFormat = null;
            }
        } catch (Exception e) {
            logger.error("录屏结束时发生异常 : ", e);
            MessageResponseHandler.uploadErrorInfo("录屏结束时发生异常",e);
        }
    }
要使用JavaCv实现录屏功能,需要使用FFmpeg进行视频编码,以下是一个简单实现示例: 首先,需要引入JavaCv和FFmpeg的库文件,例如: ```xml <dependency> <groupId>org.bytedeco</groupId> <artifactId>javacv</artifactId> <version>1.5.3</version> </dependency> <dependency> <groupId>org.bytedeco</groupId> <artifactId>ffmpeg</artifactId> <version>4.4</version> </dependency> ``` 然后,创建一个FFmpegFrameRecorder对象,设置视频编码器、视频格式、视频帧率等参数: ```java FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("output.mp4", width, height); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); recorder.setFormat("mp4"); recorder.setFrameRate(fps); recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); recorder.start(); ``` 接着,使用Java.awt.Robot类进行屏幕捕获,获取屏幕上的图像: ```java Robot robot = new Robot(); Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); BufferedImage image = robot.createScreenCapture(screenRect); ``` 将图像转换为JavaCv的Frame对象,写入到视频文件中: ```java Frame frame = converter.convert(image); recorder.record(frame); ``` 最后,当录屏完成时,需要停止录制并释放资源: ```java recorder.stop(); recorder.release(); ``` 完整的代码示例如下: ```java import org.bytedeco.ffmpeg.global.avcodec; import org.bytedeco.ffmpeg.global.avutil; import org.bytedeco.javacv.FFmpegFrameRecorder; import org.bytedeco.javacv.Frame; import org.bytedeco.javacv.Java2DFrameConverter; import java.awt.*; import java.awt.image.BufferedImage; public class ScreenRecorder { public static void main(String[] args) throws Exception { int width = 1920; int height = 1080; int fps = 30; FFmpegFrameRecorder recorder = new FFmpegFrameRecorder("output.mp4", width, height); recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); recorder.setFormat("mp4"); recorder.setFrameRate(fps); recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P); recorder.start(); Java2DFrameConverter converter = new Java2DFrameConverter(); Robot robot = new Robot(); Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize()); long startTime = System.currentTimeMillis(); while (System.currentTimeMillis() - startTime < 60 * 1000) { BufferedImage image = robot.createScreenCapture(screenRect); Frame frame = converter.convert(image); recorder.record(frame); } recorder.stop(); recorder.release(); } } ``` 注意,由于录制屏幕需要操作系统的权限,所以需要以管理员身份运行程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值