使用Java和awt.Robot时提高屏幕捕获速度

时间:2013-11-07 17:49:43

标签: java java-2d javax.imageio awtrobot

编辑:如果有人提出任何其他提高屏幕捕获性能的建议,请随意分享,因为它可能完全解决我的问题!

Hello Fellow Developers,

我正在为自己制作一些基本的屏幕捕获软件。截至目前,我已经获得了一些概念/修补代码的证明,它使用java.awt.Robot将屏幕捕获为BufferedImage。然后我执行此捕获指定的时间,然后将所有图片转储到磁盘。从我的测试中我得到大约每秒17帧。

试验#1

长度:15秒 拍摄的图像:255

试验#2

长度:15秒 捕获的图像:229

显然,这对于真正的屏幕捕获应用来说还不够好。特别是因为这些捕获只是在我的IDE中选择一些文本而没有任何图形密集的文本。

我现在有两个班级是Main类和一个“Monitor”类。 Monitor类包含捕获屏幕的方法。我的Main类有一个基于时间的循环,它调用Monitor类并将它返回的BufferedImage存储到BufferedImages的ArrayList中。 如果我修改我的主类以产生几个执行该循环的线程,并且还收集有关捕获图像的系统时间的信息,我可以提高性能吗?我的想法是使用一个共享数据结构,它会在插入时基于捕获时间自动对帧进行排序,而不是将连续图像插入到arraylist中的单个循环。

代码:

监视器

public class Monitor {

/**
 * Returns a BufferedImage
 * @return
 */
public BufferedImage captureScreen() {
    Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
    BufferedImage capture = null;

    try {
        capture = new Robot().createScreenCapture(screenRect);
    } catch (AWTException e) {
        e.printStackTrace();
    }

    return capture;
}
}

主要

public class Main {


public static void main(String[] args) throws InterruptedException {
    String outputLocation = "C:\\Users\\ewillis\\Pictures\\screenstreamer\\";
    String namingScheme = "image";
    String mediaFormat = "jpeg";
    DiscreteOutput output = DiscreteOutputFactory.createOutputObject(outputLocation, namingScheme, mediaFormat);

    ArrayList<BufferedImage> images = new ArrayList<BufferedImage>();
    Monitor m1 = new Monitor();

    long startTimeMillis = System.currentTimeMillis();
    long recordTimeMillis = 15000;

    while( (System.currentTimeMillis() - startTimeMillis) <= recordTimeMillis ) {
        images.add( m1.captureScreen() );
    }

    output.saveImages(images);

}
}

2 个答案:

答案 0 :(得分:3)

重新使用屏幕矩形和机器人类实例将为您节省一些开销。真正的瓶颈是将所有BufferedImage存储到数组列表中。

我首先会对你的robot.createScreenCapture(screenRect)的速度进行基准测试;呼叫没有任何IO(不保存或存储缓冲的图像)。这将为您提供机器人类的理想吞吐量。

long frameCount = 0;
while( (System.currentTimeMillis() - startTimeMillis) <= recordTimeMillis ) {
    image = m1.captureScreen();
    if(image !== null) {
        frameCount++;
    }
    try {
        Thread.yield();
    } catch (Exception ex) {
    }
}

如果事实证明captureScreen可以达到你想要的FPS,那么不需要来实现多线程机器人实例。

我没有拥有缓冲图像的数组列表,而是从AsynchronousFileChannel.write获得了Futures的数组列表。

  • 捕获循环
    • 获取BufferedImage
    • 将BufferedImage转换为包含JPEG数据的字节数组
    • 创建输出文件的异步通道
    • 开始写入并将立即返回值(将来)添加到ArrayList
  • 等待循环
    • 浏览期货的ArrayList并确保它们全部完成

答案 1 :(得分:2)

我想强烈的内存使用是一个问题。您在测试中捕获了大约250个屏幕截图。根据屏幕分辨率,这是:

1280x800 : 250 * 1280*800  * 3/1024/1024 ==  732 MB data
1920x1080: 250 * 1920*1080 * 3/1024/1024 == 1483 MB data

尝试捕捉而不将所有这些图像保留在内存中。

正如@Obicere所说,让机器人实例保持活着是个好主意。