【问题标题】:Increasing screen capture speed when using Java and awt.Robot使用 Java 和 awt.Robot 时提高屏幕捕获速度
【发布时间】:2013-11-19 12:26:03
【问题描述】:

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

各位开发者您好,

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

试验#1

时长:15 秒 捕获的图像:255

试验#2

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

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

我现在有两个类,一个主类和一个“监视器”类。 Monitor 类包含捕获屏幕的方法。我的 Main 类有一个基于时间的循环,它调用 Monitor 类并将它返回的 BufferedImage 存储到 BufferedImages 的 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);

}
}

【问题讨论】:

  • 你不断创造新的矩形和新的机器人。尝试为实例制作最终版本,并仅使用该字段而不是制作新字段。这应该会有所帮助。
  • @Obicere 我做到了,我仍然获得类似的表现。但是你知道或认为多线程的想法值得追求吗?
  • 我不太了解您在做什么,但似乎如果您创建了一个机器人/核心,您将能够遍历每个机器人并在不同的核心上截取屏幕截图而主核心正忙。
  • 如果你做多线程也许你可以使用一个队列并在你去添加图像时同步它。您可以在以这种方式输入图像时避免对图像进行排序。或者,您可以在所有图像完成后进行排序。如果您关心的是 FPS,您不想在添加图像时执行任何排序计算。
  • 不是awt.Robot,而是更快stackoverflow.com/a/4843247/1018903

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


【解决方案1】:

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

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

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

正如@Obicere 所说,让 Robot 实例保持活动状态是个好主意。

【讨论】:

  • 是的。从我一直在做的所有阅读来看,似乎需要做的是捕捉初始图像。然后获取该图像和所有后续图像的 byte[] 数据。对于所有后续图像,将它们的 byte[] 数据转换为第一个 byte[] 的增量编码版本,从而大大降低了空间需求。然后,如果您需要写入磁盘,它也会更快。
【解决方案2】:

重用屏幕矩形和机器人类实例将为您节省一点开销。真正的瓶颈是将所有 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 转换为包含 JPEG 数据的字节数组
    • 为输出文件创建一个异步通道
    • 开始写入并将立即返回值(未来)添加到您的 ArrayList
  • 等待循环
    • 检查您的 ArrayList of Futures 并确保它们全部完成

【讨论】:

  • 好答案。我认为需要对捕获循环进行的一个修改是将字节数组转换为捕获的第一张图像的增量编码字节数组。那么你应该有一个稀疏的数据数组,可以更容易地压缩和存储。
  • @EthanWillis 你知道你会推荐的用于 Java 的增量编码字节数组实现吗?
猜你喜欢
  • 2016-12-31
  • 1970-01-01
  • 1970-01-01
  • 2015-04-06
  • 1970-01-01
  • 2012-04-19
  • 2017-03-15
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多