比java.awt.Robot.createScreenCapture更快的替代方法是什么?

6

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

我不想使用任何第三方库,因为我尽量保持它尽可能小,但如果可以显著提高性能,我愿意考虑。我也试图保持平台无关,但再次,如果可以显著提高性能,我愿意将其限制在Windows上。

编辑:现在我已经尝试了两种不同的方式;使用在Oracle网站上找到的片段和在下面评论中指出的片段。三种方法都需要大约2.1-2.2百万纳秒的时间,效率非常低下。

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);
    }
}

2
听起来你正在尝试捕获屏幕视频。 - Jim Garrison
你可以看看这个链接,不过我没用过或进行过基准测试。 - MadProgrammer
@JimGarrison 就是这样的。 - user2585313
2
@MadProgrammer 我其实是那个论坛的活跃用户,这是我首先比较的东西哈哈。但可悲的是,我只得到了极小的性能提升,大约2-4毫秒。 - user2585313
1
@MadProgrammer 我进行了第二次基准测试,可以确认任何性能提升都是微不足道的。 - Alex Barker
3个回答

3

唯一的方法是通过JNI或可能是JNA。我对本机屏幕捕获API进行了一些基准测试,它能够保持约45 FPS,而机器人仅有8 FPS。不久将来,我可能会开始一个JNI项目来解决这个问题。如果项目进展顺利,我将在此帖子中更新项目URL。


有关此事的任何消息吗?我们是否有使用JNI/JNA进行屏幕捕获的替代方案? - Roberto Andrade
我也想看到这个。 - Doctor Parameter
是的,我在这个项目上有点懈怠了。我正在努力将原始库放入Maven中,然后我会重新开始开发工作。 - Alex Barker
1
听起来不错,我做了一些研究,找到了这个链接:http://stackoverflow.com/questions/13773870/how-to-get-over-30fps-using-java-in-a-screen-capture-program。 我还创建了一个简单的ThreadPoolExecutor实现,以帮助那些需要将图像写入文件的人。 如果您认为有帮助,请告诉我,我可以提供代码。 - Doctor Parameter
上述FPS数字是在以下分辨率下测试的:Linux @ 1920x1080,OSX @ 1440x900和Windows @ 1920x1017(虚拟机)。 - Alex Barker
显示剩余6条评论

0

我目前已经建立了一个使用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);
        }

    }

0

使用纯Java可以通过并行使用多个java.awt.Robot实现帧速率约为30FPS。您可以在GitHub上找到一个示例实现。


是的,这是几个线程 - 如果您的计算机具有多个 CPU(对于当前的计算机而言很好,但不总是保证)- 尽管如此,这是个好主意。但是,想法是使原始代码更快地工作 - 上面的这个人开始说一些关于 JNA 的东西,然后转而谈论 Objective-C 和其他无聊的话题 - 我想看看 JNA 方法!! - gpasch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接