【问题标题】:Faster alternative to java.awt.Robot.createScreenCapture?java.awt.Robot.createScreenCapture 的更快替代方案?
【发布时间】:2013-07-13 23:36:46
【问题描述】:

我正在制作一个程序,该程序需要每秒至少捕获 24 个屏幕截图。目前使用下面的代码,我每 94 毫秒只能得到 1 个,所以每秒大约 10 个。

我不想使用任何 3rd 方库,因为我试图让它尽可能小,但如果我能获得显着的性能提升,我会愿意。我也在努力保持这个平台的独立性,但同样,如果这能带来真正显着的性能提升,我愿意将其限制在 Windows 上。

edit:我现在也尝试了两种不同的方法;使用在 oracles 网站上找到的 sn-p 以及在下面的 cmets 中指出的。这三个都花费了大约相同的时间,2.1-220 万纳秒,这非常低效。

public abstract class Benchmark {

    private final int iterations;

    public Benchmark(int iterations) {
        this.iterations = iterations;
    }

    public abstract void logic();

    public void start() {
        long start = System.nanoTime();
        for (int iteration = 0; iteration < iterations; iteration++) {
            long iterationStart = System.nanoTime();
            logic();
            System.out.println("iteration: " + iteration + " took: " + (System.nanoTime() - iterationStart) + " nanoseconds.");
        }
        long total = (System.nanoTime() - start);
        System.out.println(iterations + " iterations took: " + total + " nanoseconds.  Average iteration was: " + (total / iterations));
    }
}

_

import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;

public class RobotBenchmark extends Benchmark {

    private final Robot robot;
    private final Rectangle screen;

    public static void main(String[] args) {
        Benchmark benchmark;
        try {
            benchmark = new RobotBenchmark(24);
            benchmark.start();
        } catch (AWTException e) {
            e.printStackTrace();
        }
    }

    public RobotBenchmark(int iterations) throws AWTException {
        super(iterations);
        robot = new Robot();
        screen = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
    }

    @Override
    public void logic() {
        robot.createScreenCapture(screen);
    }

}

_

import java.awt.AWTException;
import java.awt.GraphicsDevice;
import java.awt.HeadlessException;
import java.awt.Rectangle;

public class DirectRobotBenchmark extends Benchmark {

    private final GraphicsDevice device;
    private final Rectangle screenRectangle;
    private final DirectRobot robot;

    private int[] screen;

    public static void main(String[] args) {
        Benchmark benchmark;
        try {
            benchmark = new DirectRobotBenchmark(24);
            benchmark.start();
        } catch (HeadlessException | AWTException e) {
            e.printStackTrace();
        }
    }

    public DirectRobotBenchmark(int iterations) throws HeadlessException, AWTException {
        super(iterations);
        device = DirectRobot.getDefaultScreenDevice();
        screenRectangle = new Rectangle(1920, 1080);
        robot = new DirectRobot(device);
        screen = new int[screenRectangle.width * screenRectangle.height];
    }

    @Override
    public void logic() {
        screen = robot.getRGBPixels(screenRectangle);
    }
}

_

import java.awt.AWTException;
import java.awt.GraphicsEnvironment;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Toolkit;
import java.awt.peer.RobotPeer;

import sun.awt.SunToolkit;

@SuppressWarnings("restriction")
public class RobotPeerBenchmark extends Benchmark {

    private final SunToolkit toolkit;
    private final RobotPeer peer;
    private final Rectangle screenRectangle;

    private int[] screen;

    public static void main(String[] args) {
        try {
            Benchmark robotPeerBenchmark = new RobotPeerBenchmark(24);
            robotPeerBenchmark.start();
        } catch (AWTException e) {
            e.printStackTrace();
        }
    }

    public RobotPeerBenchmark(int iterations) throws AWTException {
        super(iterations);
        toolkit = (SunToolkit) Toolkit.getDefaultToolkit();
        peer = toolkit.createRobot(new Robot(), GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice());
        screenRectangle = new Rectangle(toolkit.getScreenSize());
        screen = new int[screenRectangle.width * screenRectangle.height];
    }

    @Override
    public void logic() {
        screen = peer.getRGBPixels(screenRectangle);
    }
}

【问题讨论】:

  • 听起来您正在尝试捕获屏幕视频。
  • 你可以看看this,不过我没用过,也没有在benchmark过
  • @JimGarrison 类似的东西。
  • @MadProgrammer 我实际上是该论坛的活跃用户,这是我将其与哈哈进行比较的第一件事。但遗憾的是,我的性能提升非常小,比如 2-4 毫秒。
  • @MadProgrammer 我做了第二次基准测试,可以确认任何性能提升都可以忽略不计。

标签: java image performance screenshot awtrobot


【解决方案1】:

这样做的唯一方法是通过 JNI 或可能的 JNA。我做了一些基准测试和原生屏幕捕获 API,它能够维持大约 45 FPS 与 Robots 8 FPS。我可能会在不久的将来开始一个 JNI 项目来解决这个问题。如果继续,我将使用项目 URL 更新此帖子。

【讨论】:

  • 有这方面的消息吗?我们是否有使用 JNI/JNA 进行屏幕捕获的替代方案?
  • 我也想看看这个。
  • 是的,我一直在偷懒。我正在努力将原始库放入 maven,然后我将开始开发。
  • 听起来不错,我做了一些研究并想出了这个链接:stackoverflow.com/questions/13773870/…。我还创建了一个简单的 ThreadPoolExecutor 实现,以帮助我们这些需要将图像写入文件的人进行图像写入。如果您认为对您的努力有帮助,我可以提供代码。
  • 所以我在过去的几天里做了一些编码,下面是事情是如何分解的。对于 X11 目标,我可以使用 XShm 扩展在大约 10-15 毫秒内获得单帧。使用 XImage,每帧大约 5 毫秒。在使用 Core Foundation 的 OSX 上,我能够每帧执行大约 42-55 毫秒。在我使用 BitBlt 的 Windows VM 上,我看到每帧大约 60-75 毫秒。 x11 数字在大约 60 FPS 时几乎无法通过。 OSX 数字相当于 20 FPS,而 Windows 则高达 15 FPS。我将发布有关本机 Windows 测试的更新以及是否可以提高性能。
【解决方案2】:

我目前设置了一个使用 VLCJ 的工作示例,然后使用 DirectMediaPlayer (https://github.com/caprica/vlcj/blob/master/src/test/java/uk/co/caprica/vlcj/test/direct/DirectTestPlayer.java) 获取 BufferedImage。

JFrame 不是正常工作所必需的。

我知道这是一个老问题,但今天仍然是个问题,所以我想分享一下。

VLCJ 是 LibVLC 的 Java 绑定。

示例代码:

    private BufferedImage image;
    private MediaPlayerFactory factory;
    private DirectMediaPlayer mediaPlayer;

    public void start() {

            image = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDefaultConfiguration().createCompatibleImage(width, height);
            image.setAccelerationPriority(1.0f);

            String mrl = "screen://";
            String[] options = {
                    ":screen-fps=30",
                    ":live-caching=0",
                    ":screen-width=1920",
                    ":screen-height=1080",
                    ":screen-left=0",
                    ":screen-top=0"
            };
            factory = new MediaPlayerFactory();
            mediaPlayer = factory.newDirectMediaPlayer(new TestBufferFormatCallback(), new TestRenderCallback());
            mediaPlayer.playMedia(mrl, options);
    }

    // Callbacks are required.
    private final class TestRenderCallback extends RenderCallbackAdapter {

        public TestRenderCallback() {
            super(((DataBufferInt) image.getRaster().getDataBuffer()).getData());
        }

        @Override
        public void onDisplay(DirectMediaPlayer mediaPlayer, int[] data) {
            // The image data could be manipulated here...

            /* RGB to GRAYScale conversion example */
//            for(int i=0; i < data.length; i++){
//                int argb = data[i];
//                int b = (argb & 0xFF);
//                int g = ((argb >> 8 ) & 0xFF);
//                int r = ((argb >> 16 ) & 0xFF);
//                int grey = (r + g + b + g) >> 2 ; //performance optimized - not real grey!
//                data[i] = (grey << 16) + (grey << 8) + grey;
//            }
//            imagePane.repaint();
        }
    }

    private final class TestBufferFormatCallback implements BufferFormatCallback {

        @Override
        public BufferFormat getBufferFormat(int sourceWidth, int sourceHeight) {
            return new RV32BufferFormat(width, height);
        }

    }

【讨论】:

    【解决方案3】:

    并行使用多个java.awt.Robot 可以实现纯Java 30FPS 左右的帧速率。在GitHub

    上可以找到一个示例实现

    【讨论】:

    • 是的,那是几个线程——如果你的机器有多个 CPU(对于当前的计算机来说还可以,但并不总是保证)——尽管如此,这是个好主意。但想法是让原始代码运行得更快——上面这个人开始谈论 JNA,然后改成 Objective-C 和其他废话——我想看看 JNA 方法!
    猜你喜欢
    • 2018-12-11
    • 2012-07-05
    • 2012-01-23
    • 2011-02-27
    • 2010-09-22
    • 2017-05-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多