diff --git a/README.md b/README.md
index f8d0eb6c2bad65357238a89eecb06dea0ea82e62..9e25cec6b7c177b7f4230df5e281c391388ee758 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-#由于工作中的需求,需要用java来实现当前电脑的录屏功能以及屏幕的截图截屏功能
+# 由于工作中的需求,需要用java来实现当前电脑的录屏功能以及屏幕的截图截屏功能
* 由于习惯了springboot 所以就写在springboot上了,并暴露了一个接口来触发录屏功能
* 核心代码在fxbase包下的VideoRecorder类中 但是代码写的有点乱,使用者可以不必过度关注这个类 而是看看RecorderUtil,我将这个类注入到了ioc容器,这个类的getVideoRecorderInstance可以获取录屏对象 调用这个对象的start()和stop()方法来完成录制和结束,具体过程很简单,可以看RecorderController的调用过程
@@ -19,3 +19,5 @@
* ####注意:主函数的启动类不是SpringApplication.run(DemoApplication.class, args);不然触发录屏会报错。而是SpringApplicationBuilder builder = new SpringApplicationBuilder(VideoRecordeApplication.class);
builder.headless(false).run(args);
+## 多屏录制测试demo
+- com.example.demo.VideoRecorderTest.recordTest
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 8183dd04f681d79dbde004a46b7394a9381b3575..1c95451f15661c0f2c00c28ffad7a969a605a247 100644
--- a/pom.xml
+++ b/pom.xml
@@ -31,10 +31,16 @@
+
+
+
+
+
+
org.bytedeco
- javacv
- 1.4.4
+ javacv-platform
+ 1.5.10
@@ -59,6 +65,10 @@
spring-boot-starter-test
test
+
+ junit
+ junit
+
diff --git a/src/main/java/com/ht/recode/Controller/RecorderController.java b/src/main/java/com/ht/recode/Controller/RecorderController.java
index 1ceb4d30e8263f83c501919fe902cd5fe936024e..7f07649946751d9121f1b2918df9fdd8bb50b56e 100644
--- a/src/main/java/com/ht/recode/Controller/RecorderController.java
+++ b/src/main/java/com/ht/recode/Controller/RecorderController.java
@@ -72,7 +72,7 @@ public class RecorderController {
Thread.sleep(reTime);
//截图 需要图片的用这个方法取图
- CutPicUtil.cutPic(name,imgPath,"png",videoRecorder.getRectangle());
+ // CutPicUtil.cutPic(name,imgPath,"png",videoRecorder.getRectangle());
log.info("截图保存在这个文件夹下====》:"+imgPath);
//结束============================================================================================
diff --git a/src/main/java/com/ht/recode/fxbase/RecorderUtil.java b/src/main/java/com/ht/recode/fxbase/RecorderUtil.java
index c6edcd0e2ac86da3070f2806818757cbf41b16e2..643487816f53f6ef9a080335899c3829ad7b602d 100644
--- a/src/main/java/com/ht/recode/fxbase/RecorderUtil.java
+++ b/src/main/java/com/ht/recode/fxbase/RecorderUtil.java
@@ -1,6 +1,5 @@
package com.ht.recode.fxbase;
-import com.ht.recode.fxbase.VideoRecorder;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@@ -37,8 +36,7 @@ public class RecorderUtil {
//文件夹+文件名
String fileHoldName = fileName + videoName;
//调用录屏API的对象 这个对象就可以开启或者关闭录屏
- VideoRecorder videoRecord = new VideoRecorder(fileHoldName, true,rectangle);
- return videoRecord;
+ return new VideoRecorder(fileHoldName, true, rectangle);
}
}
diff --git a/src/main/java/com/ht/recode/fxbase/VideoRecorder.java b/src/main/java/com/ht/recode/fxbase/VideoRecorder.java
index 9e7f67f67a28b7dfc6ec94c7c76e22ac9193455a..976d2292dc5642b9a282eea4bb9b835d62c5b05a 100644
--- a/src/main/java/com/ht/recode/fxbase/VideoRecorder.java
+++ b/src/main/java/com/ht/recode/fxbase/VideoRecorder.java
@@ -6,18 +6,24 @@ package com.ht.recode.fxbase;
* @date 2021/1/13 9:43
*/
import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.bytedeco.ffmpeg.global.avutil;
import org.bytedeco.javacpp.avcodec;
-import org.bytedeco.javacv.FFmpegFrameRecorder;
+import org.bytedeco.javacv.*;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameRecorder.Exception;
-import org.bytedeco.javacv.Java2DFrameConverter;
+import org.junit.Test;
+import javax.imageio.ImageIO;
import javax.sound.sampled.*;
import java.awt.*;
import java.awt.image.BufferedImage;
+import java.io.File;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.ShortBuffer;
+import java.util.Timer;
+import java.util.TimerTask;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@@ -26,18 +32,23 @@ import java.util.concurrent.TimeUnit;
*/
@Data
+@Slf4j
public class VideoRecorder {
//线程池 screenTimer
private ScheduledThreadPoolExecutor screenTimer;
+ private static boolean isStop = false;
//获取屏幕尺寸
- private static final int WIDTH = 900;
- private static final int HEIGHT = 600;
+ private static final int WIDTH = 2560;
+ private static final int HEIGHT = 1600;
private Rectangle rectangle;
//视频类 FFmpegFrameRecorder
private FFmpegFrameRecorder recorder;
private Robot robot;
//线程池 exec
private ScheduledThreadPoolExecutor exec;
+ private String fileName;
+ private OpenCVFrameConverter.ToIplImage conveter;
+ private BufferedImage screenCapture;
private TargetDataLine line;
private AudioFormat audioFormat;
private DataLine.Info dataLineInfo;
@@ -45,63 +56,50 @@ public class VideoRecorder {
private long startTime = 0;
private long videoTS = 0;
private long pauseTime = 0;
- private double frameRate = 24;
+ /*录制的频率*/
+ private int frameRate = 10;
- public VideoRecorder(String fileName, boolean isHaveDevice, Rectangle rectangle1) {
- rectangle = rectangle1;
- // TODO Auto-generated constructor stub
- System.out.println(rectangle1);
- //recorder = new FFmpegFrameRecorder(fileName + ".mp4", (int)rectangle1.getWidth(), (int)rectangle1.getHeight(),2);
- recorder = new FFmpegFrameRecorder(fileName + ".mp4", (int)rectangle1.getWidth(), (int)rectangle1.getHeight(),2);
- //recorder.setVideoCodec(avcodec.AV_CODEC_ID_H265); // 28
- // recorder.setVideoCodec(avcodec.AV_CODEC_ID_FLV1); // 28
- recorder.setVideoCodec(avcodec.AV_CODEC_ID_MPEG4); // 13
- recorder.setFormat("mp4");
- // recorder.setFormat("mov,mp4,m4a,3gp,3g2,mj2,h264,ogg,MPEG4");
- recorder.setSampleRate(44100);
- recorder.setFrameRate(frameRate);
-
- recorder.setVideoQuality(1);
- recorder.setVideoOption("crf", "23");
- // 2000 kb/s, 720P视频的合理比特率范围
- recorder.setVideoBitrate(1000000);
- /**
- * 权衡quality(视频质量)和encode speed(编码速度) values(值): ultrafast(终极快),superfast(超级快),
- * veryfast(非常快), faster(很快), fast(快), medium(中等), slow(慢), slower(很慢),
- * veryslow(非常慢)
- * ultrafast(终极快)提供最少的压缩(低编码器CPU)和最大的视频流大小;而veryslow(非常慢)提供最佳的压缩(高编码器CPU)的同时降低视频流的大小
- * 参考:https://trac.ffmpeg.org/wiki/Encode/H.264 官方原文参考:-preset ultrafast as the
- * name implies provides for the fastest possible encoding. If some tradeoff
- * between quality and encode speed, go for the speed. This might be needed if
- * you are going to be transcoding multiple streams on one machine.
- */
- recorder.setVideoOption("preset", "slow");
- recorder.setPixelFormat(0); // yuv420p = 0
- recorder.setAudioChannels(2);
- recorder.setAudioOption("crf", "0");
- // Highest quality
- recorder.setAudioQuality(0);
- recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC);
- try {
- robot = new Robot();
- } catch (AWTException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- try {
- recorder.start();
- } catch (Exception e) {
- // TODO Auto-generated catch block
- System.out.print("*******************************");
- }
- this.isHaveDevice = isHaveDevice;
+ public VideoRecorder(String fileName, boolean isHaveDevice, Rectangle rectangle) {
+ init(fileName, isHaveDevice, rectangle);
}
-
- /**
+
+ private void init(String fileName, boolean isHaveDevice, Rectangle rectangle) {
+ this.rectangle = rectangle;
+ System.out.println(this.rectangle);
+ recorder = new FFmpegFrameRecorder(fileName + ".mp4",
+ this.rectangle.width, this.rectangle.height,0);
+ recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 13
+ recorder.setFormat("mp4");
+ // recorder.setFormat("mov,mp4,m4a,3gp,3g2,mj2,h264,ogg,MPEG4");
+ recorder.setSampleRate(44100);
+ recorder.setFrameRate(frameRate);
+
+ recorder.setVideoQuality(1);
+ recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
+
+ recorder.setVideoOption("crf", "23");
+ // 2000 kb/s, 720P视频的合理比特率范围
+ recorder.setVideoBitrate(1000000);
+ recorder.setVideoOption("preset", "slow");
+ try {
+ robot = new Robot();
+ } catch (AWTException e) {
+ log.error("录屏功能出现异常:",e);
+ }
+ try {
+ recorder.start();
+ } catch (Exception e) {
+ log.error("录屏功能出现异常:",e);
+ }
+ this.isHaveDevice = isHaveDevice;
+ }
+
+ /**
* 开始录制
*/
public void start() {
-
+
+ isStop = false;
if (startTime == 0) {
startTime = System.currentTimeMillis();
}
@@ -109,76 +107,69 @@ public class VideoRecorder {
pauseTime = System.currentTimeMillis();
}
// 如果有录音设备则启动录音线程
- /* if (isHaveDevice) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- caputre();
- }
- }).start();
- }*/
-
- // 录屏
- screenTimer = new ScheduledThreadPoolExecutor(5);
- screenTimer.scheduleAtFixedRate(new Runnable() {
-
- @Override
- public void run() {
-
- // 将screenshot对象写入图像文件
- // try {
- // ImageIO.write(screenCapture, "JPEG", f);
- // videoGraphics.drawImage(screenCapture, 0, 0, null);
- // IplImage image = cvLoadImage(name); // 非常吃内存!!
- // // 创建一个 timestamp用来写入帧中
- // videoTS = 1000
- // * (System.currentTimeMillis() - startTime - (System.currentTimeMillis() -
- // pauseTime));
- // // 检查偏移量
- // if (videoTS > recorder.getTimestamp()) {
- // recorder.setTimestamp(videoTS);
- // }
-
- BufferedImage screenCapture = robot.createScreenCapture(rectangle); // 截屏
-
- BufferedImage videoImg = new BufferedImage((int)rectangle.getWidth(), (int)rectangle.getHeight(),
- BufferedImage.TYPE_3BYTE_BGR); // 声明一个BufferedImage用重绘截图
-
- Graphics2D videoGraphics = videoImg.createGraphics();// 创建videoImg的Graphics2D
-
- videoGraphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
- videoGraphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
- RenderingHints.VALUE_COLOR_RENDER_SPEED);
- videoGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
- videoGraphics.drawImage(screenCapture, 0, 0, null); // 重绘截图
-
- Java2DFrameConverter java2dConverter = new Java2DFrameConverter();
-
- Frame frame = java2dConverter.convert(videoImg);
- try {
- videoTS = 1000L
- * (System.currentTimeMillis() - startTime - (System.currentTimeMillis() - pauseTime));
- // 检查偏移量
- if (videoTS > recorder.getTimestamp()) {
- recorder.setTimestamp(videoTS);
- }
- recorder.record(frame); // 录制视频
- } catch (Exception e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- // 释放资源
- videoGraphics.dispose();
- videoGraphics = null;
- videoImg.flush();
- videoImg = null;
- java2dConverter = null;
- screenCapture.flush();
- screenCapture = null;
-
- }
- }, (int) (1000 / frameRate), (int) (1000 / frameRate), TimeUnit.MILLISECONDS);
-
+ screenTimer = new ScheduledThreadPoolExecutor(5);
+ screenTimer.scheduleAtFixedRate(() -> {
+ // 截屏
+ screenCapture = robot.createScreenCapture(rectangle);
+ // 声明一个BufferedImage用重绘截图
+ BufferedImage videoImg = new BufferedImage(rectangle.width, rectangle.height,
+ BufferedImage.TYPE_3BYTE_BGR);
+ // 创建videoImg的Graphics2D
+ Graphics2D videoGraphics = videoImg.createGraphics();
+
+ videoGraphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
+ videoGraphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
+ RenderingHints.VALUE_COLOR_RENDER_SPEED);
+ videoGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
+ // 重绘截图
+ videoGraphics.drawImage(screenCapture, 0, 0, null);
+ Java2DFrameConverter java2dConverter = new Java2DFrameConverter();
+ Frame frame = java2dConverter.convert(videoImg);
+ try {
+ // 检查偏移量
+ log.info("开始录制屏幕");
+ // Frame frame = grabber.grabFrame();
+ recorder.record(frame);
+ } catch (Exception e) {
+ log.error("录屏功能出现异常:",e);
+ }finally {
+ // 释放资源
+ videoGraphics.dispose();
+ videoGraphics = null;
+ videoImg.flush();
+ videoImg = null;
+ java2dConverter = null;
+ screenCapture.flush();
+ screenCapture = null;
+ }
+
+ if (isStop) {
+ if (null != screenTimer) {
+ screenTimer.shutdownNow();
+ }
+ try {
+ recorder.stop();
+ recorder.release();
+ recorder.close();
+ screenTimer = null;
+ screenCapture = null;
+ screenTimer.shutdown();
+ if (isHaveDevice) {
+ if (null != exec) {
+ exec.shutdownNow();
+ }
+ if (null != line) {
+ line.stop();
+ line.close();
+ }
+ dataLineInfo = null;
+ audioFormat = null;
+ }
+ } catch (Exception e) {
+ log.error("停止录屏功能出现异常:",e);
+ }
+ }
+ }, 0, 1000 / frameRate, TimeUnit.MILLISECONDS);
}
/**
@@ -224,7 +215,7 @@ public class VideoRecorder {
// recorder is instance of
// org.bytedeco.javacv.FFmpegFrameRecorder
recorder.recordSamples(sampleRate, numChannels, sBuff);
- // System.gc();
+ System.gc();
} catch (Exception e) {
e.printStackTrace();
}
@@ -236,30 +227,8 @@ public class VideoRecorder {
* 停止
*/
public void stop() {
- if (null != screenTimer) {
- screenTimer.shutdownNow();
- }
- try {
- recorder.stop();
- recorder.release();
- recorder.close();
- 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) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
+
+ isStop = true;
}
@@ -282,7 +251,111 @@ public class VideoRecorder {
}
pauseTime = System.currentTimeMillis();
}
-
-
-
+
+
+ public static void main(String[] args) throws AWTException {
+
+ }
+
+ @Test
+ public void record() throws AWTException, InterruptedException, FFmpegFrameRecorder.Exception {
+
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ GraphicsDevice[] screens = ge.getScreenDevices();
+
+ Robot robot = new Robot();
+ screenTimer = new ScheduledThreadPoolExecutor(5);
+ int i = 0;
+ for (GraphicsDevice screen : screens) {
+
+ Rectangle screenRect = screen.getDefaultConfiguration().getBounds();
+ FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(fileName + i++ + ".mp4",
+ screenRect.width, screenRect.height,0);
+ recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 13
+ recorder.setFormat("mp4");
+ // recorder.setFormat("mov,mp4,m4a,3gp,3g2,mj2,h264,ogg,MPEG4");
+ recorder.setSampleRate(44100);
+ recorder.setFrameRate(frameRate);
+
+ recorder.setVideoQuality(1);
+ recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
+
+ recorder.setVideoOption("crf", "23");
+ // 2000 kb/s, 720P视频的合理比特率范围
+ recorder.setVideoBitrate(1000000);
+ recorder.setVideoOption("preset", "slow");
+ recorder.start();
+
+
+ screenTimer.scheduleAtFixedRate(() -> {
+ // 截屏
+ // screenCapture = robot.createScreenCapture(rectangle);
+ BufferedImage videoImg = robot.createScreenCapture(screenRect);
+
+ // 声明一个BufferedImage用重绘截图
+ // BufferedImage videoImg = new BufferedImage(rectangle.width, rectangle.height,
+ // BufferedImage.TYPE_3BYTE_BGR);
+ // 创建videoImg的Graphics2D
+ // Graphics2D videoGraphics = videoImg.createGraphics();
+
+ // videoGraphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
+ // videoGraphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
+ // RenderingHints.VALUE_COLOR_RENDER_SPEED);
+ // videoGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
+ // 重绘截图
+ // videoGraphics.drawImage(screenCapture, 0, 0, null);
+ Java2DFrameConverter java2dConverter = new Java2DFrameConverter();
+ Frame frame = java2dConverter.convert(videoImg);
+ try {
+ // 检查偏移量
+ log.info("开始录制屏幕");
+ // Frame frame = grabber.grabFrame();
+ recorder.record(frame);
+ } catch (Exception e) {
+ log.error("录屏功能出现异常:",e);
+ }finally {
+ // 释放资源
+ // videoGraphics.dispose();
+ // videoGraphics = null;
+ videoImg.flush();
+ videoImg = null;
+ java2dConverter = null;
+ screenCapture.flush();
+ screenCapture = null;
+ }
+
+ if (isStop) {
+ if (null != screenTimer) {
+ screenTimer.shutdownNow();
+ }
+ try {
+ recorder.stop();
+ recorder.release();
+ recorder.close();
+ screenTimer = null;
+ screenCapture = null;
+ screenTimer.shutdown();
+ if (isHaveDevice) {
+ if (null != exec) {
+ exec.shutdownNow();
+ }
+ if (null != line) {
+ line.stop();
+ line.close();
+ }
+ dataLineInfo = null;
+ audioFormat = null;
+ }
+ } catch (Exception e) {
+ log.error("停止录屏功能出现异常:",e);
+ }
+ }
+ }, 0, 1000 / frameRate, TimeUnit.MILLISECONDS);
+ }
+ Thread.sleep(10000);
+ isStop = true;
+
+ }
+
+
}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index ac3599153fa52a9493475de9c6d0a00412c4db6b..5a64ebaaae2dce501c14558e7f3dd94512716cfb 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,7 +1,7 @@
video.recorder.startx=0
video.recorder.starty=0
-video.recorder.width=1920
-video.recorder.height=1080
+video.recorder.width=2560
+video.recorder.height=1600
video.recorder.file-name=D://screenRecoding/
video.recorder.recorderTime=10000
video.recorder.img-path=D://img/
diff --git a/src/test/java/com/example/demo/VideoRecorderTest.java b/src/test/java/com/example/demo/VideoRecorderTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1fafc72bcc1281d61fdd7fa67de98edc7e9abad1
--- /dev/null
+++ b/src/test/java/com/example/demo/VideoRecorderTest.java
@@ -0,0 +1,161 @@
+package com.example.demo;
+
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+import org.bytedeco.javacpp.avcodec;
+import org.bytedeco.javacv.FFmpegFrameRecorder;
+import org.bytedeco.javacv.Frame;
+import org.bytedeco.javacv.FrameRecorder.Exception;
+import org.bytedeco.javacv.Java2DFrameConverter;
+import org.bytedeco.javacv.OpenCVFrameConverter;
+import org.junit.jupiter.api.Test;
+
+import javax.sound.sampled.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.util.concurrent.*;
+
+/**
+ * @author fanyufeng
+ *
+ * 使用javacv进行录屏
+ */
+@Data
+@Slf4j
+public class VideoRecorderTest {
+ //线程池 screenTimer
+ private ScheduledThreadPoolExecutor screenTimer;
+ private static boolean isStop = false;
+ //获取屏幕尺寸
+ private static final int WIDTH = 2560;
+ private static final int HEIGHT = 1600;
+ private Rectangle rectangle;
+ //视频类 FFmpegFrameRecorder
+ private Robot robot;
+ //线程池 exec
+ private ScheduledThreadPoolExecutor exec;
+ private String fileName;
+ private OpenCVFrameConverter.ToIplImage conveter;
+ // private BufferedImage screenCapture;
+ private TargetDataLine line;
+ private AudioFormat audioFormat;
+ private DataLine.Info dataLineInfo;
+ private boolean isHaveDevice = true;
+ private long startTime = 0;
+ private long videoTS = 0;
+ private long pauseTime = 0;
+ /*录制的频率*/
+ private int frameRate = 10;
+
+ public VideoRecorderTest() {
+ }
+
+
+ /**
+ * windows 多屏录制demo
+ */
+ @Test
+ public void recordTest() throws AWTException, InterruptedException, FFmpegFrameRecorder.Exception {
+ // screenTimer = new ScheduledThreadPoolExecutor(5);
+ // 创建一个线程池
+ GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
+ GraphicsDevice[] screens = ge.getScreenDevices();
+ fileName = "D:\\screenRecoding\\zxczxc";
+ Robot robot = new Robot();
+ screenTimer = new ScheduledThreadPoolExecutor(5);
+ isStop = false;
+ int i = 0;
+ // for (GraphicsDevice gd : screens) {
+ // DisplayMode displayMode = gd.getDisplayMode();
+ // System.out.println("Screen Resolution: " + displayMode.getWidth() + "x" + displayMode.getHeight());
+ // }
+ for (GraphicsDevice screen : screens) {
+ DisplayMode displayMode = screen.getDisplayMode();
+ Rectangle screenRect = screen.getDefaultConfiguration().getBounds();
+ screenRect.height = displayMode.getHeight();
+ screenRect.width = displayMode.getWidth();
+
+ FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(fileName + i++ + ".mp4",
+ displayMode.getWidth(), displayMode.getHeight(), 0);
+ buildRecord(recorder);
+ screenTimer.scheduleAtFixedRate(() -> {
+ // 主动截屏屏幕偏红,需要重绘截图
+ BufferedImage screenCapture = robot.createScreenCapture(screenRect);
+ BufferedImage videoImg = new BufferedImage(displayMode.getWidth(), displayMode.getHeight(),
+ BufferedImage.TYPE_3BYTE_BGR);
+ // 声明一个BufferedImage用重绘截图
+ Graphics2D videoGraphics = videoImg.createGraphics();// 创建videoImg的Graphics2D
+ videoGraphics.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
+ videoGraphics.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING,
+ RenderingHints.VALUE_COLOR_RENDER_SPEED);
+ videoGraphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
+ videoGraphics.drawImage(screenCapture, 0, 0, null); // 重绘截图
+
+ Java2DFrameConverter java2dConverter = new Java2DFrameConverter();
+ Frame frame = java2dConverter.convert(videoImg);
+ try {
+ // 检查偏移量
+ log.info("开始录制屏幕");
+ recorder.record(frame);
+ } catch (Exception e) {
+ log.error("录屏功能出现异常:", e);
+ } finally {
+ // 释放资源
+ // videoGraphics.dispose();
+ // videoGraphics = null;
+ videoImg.flush();
+ videoImg = null;
+ java2dConverter = null;
+ // screenCapture.flush();
+ // screenCapture = null;
+ }
+ if (isStop) {
+ try {
+ log.info("录制线程池结束运行");
+ recorder.stop();
+ recorder.release();
+ recorder.close();
+ Thread.sleep(1000);
+ screenTimer.shutdown();
+ } catch (Exception e) {
+ log.error("停止录屏功能出现异常:", e);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }, 0, 1000 / frameRate, TimeUnit.MILLISECONDS);
+
+ }
+
+ // 线程睡眠5S 录制5S
+ Thread.sleep(1000 * 10);
+ log.info("执行完任务");
+ isStop = true;
+ Thread.sleep(1000 * 10);
+ // 等待任务执行完毕
+
+ }
+
+
+ private FFmpegFrameRecorder buildRecord(FFmpegFrameRecorder recorder) throws FFmpegFrameRecorder.Exception {
+
+ recorder.setSampleRate(44100);
+ recorder.setFrameRate(frameRate);
+ recorder.setVideoQuality(1);
+ recorder.setVideoOption("preset", "slow");
+ recorder.setVideoCodec(avcodec.AV_CODEC_ID_MPEG4); // 13
+ recorder.setFormat("mp4");
+ // recorder.setFormat("mov,mp4,m4a,3gp,3g2,mj2,h264,ogg,MPEG4");
+ recorder.setSampleRate(44100);
+ recorder.setFrameRate(frameRate);
+ recorder.setVideoQuality(1);
+ recorder.setVideoOption("crf", "23");
+ // 2000 kb/s, 720P视频的合理比特率范围
+ recorder.setVideoBitrate(1000000);
+ recorder.setPixelFormat(0); // yuv420p = 0
+ recorder.start();
+ return recorder;
+ }
+
+
+}