从屏幕捕获并保存到磁盘多线程

tar*_*sch 6 java multithreading video-capture

以下问题应该是观看屏幕,记录事件(测量文本框变为绿色)并记录导致它的所有事件,从而产生导致它的事件的"电影".不幸的是,需要记录整个屏幕.到目前为止,我已经完成了识别参与的部分.但是我几乎没有每秒两帧.我想有大约25到30帧/秒.

我的想法是用两个单独的线程进行写作和阅读.由于写入事件很少并且可以在后台运行,因此录制事件可能会占用更多时间并且运行得更快.不幸的是,整个事情似乎太慢了.我希望能够在事件发生 10到20秒磁盘上写入屏幕.

编辑:如果可能的话,我希望尽可能保持与平台无关.

编辑2:Xuggler似乎有一个独立于平台的jar文件.不幸的是,我并没有真正了解我将如何能够将它用于我的目的:记录20秒,直到触发isarecord.

这是我到目前为止所做的:

package fragrecord;

import java.awt.AWTException;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import javax.imageio.ImageIO;

public class Main {
    public static void main(String[] args) {
        //The numbers are just silly tune parameters. Refer to the API.
        //The important thing is, we are passing a bounded queue.
        ExecutorService consumer = new ThreadPoolExecutor(1,4,30,TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(100));
        System.out.println("starting");
        //No need to bound the queue for this executor.
        //Use utility method instead of the complicated Constructor.
        ExecutorService producer = Executors.newSingleThreadExecutor();

        Runnable produce = new Produce(consumer);
        producer.submit(produce);  
        try {
            producer.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        consumer.shutdown();
        try {
            consumer.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }
}

class Produce implements Runnable {
    private final ExecutorService consumer;

    public Produce(ExecutorService consumer) {
        this.consumer = consumer;
    }
    boolean isarecord(BufferedImage image){
        int x=10, y = 10;
        Color c = new Color(image.getRGB(x,y));
        int red = c.getRed();
        int green = c.getGreen();
        int blue = c.getBlue();
        // Determine whether to start recording
        return false;

    }


    @Override
    public void run() {

        Robot robot = null;
        try {
            robot = new Robot();
        } catch (AWTException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //
        // Capture screen from the top left to bottom right
        //
        int i = 0;
        while(true) {

            i++;
        BufferedImage bufferedImage = robot.createScreenCapture(
                new Rectangle(new Dimension(1024, 798)));

        Runnable consume = new Consume(bufferedImage,i);
        consumer.submit(consume);
        }

    }
}

class Consume implements Runnable {
    private final BufferedImage bufferedImage;
    private final Integer picnr;
    public Consume(BufferedImage bufferedImage, Integer picnr){
        this.bufferedImage = bufferedImage;
        this.picnr = picnr;
    }

    @Override
    public void run() {
        File imageFile = new File("screenshot"+picnr+".png");
        try {
            System.out.println("screenshot"+picnr+".png");
            ImageIO.write(bufferedImage, "png", imageFile);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

kri*_*tzi 2

由于各种原因,使用执行器、重写 nio 和类似的东西不会有帮助。

以下是您应该考虑的一些事项:

  1. java 中的图像捕获速度很慢,如果没有 JNI 依赖,你无能为力
  2. 使用jpeg代替png,速度更快
  3. 使用 ImageIO 进行任何图像压缩都很慢。您可以使用老式的专有 JpegEncoder 类(在 com.sun 包中)或 TurboJPEG java 库(还有 JNI),但恕我直言,这不是瓶颈
  4. 磁盘 i/o 绝对不是问题(您正在写入 <5mb/s,所以不用担心)
  5. 并行写入许多图像实际上会减慢您的应用程序的速度,而不是加速它(除非您有 SSD)
  6. 考虑并行化捕获/分析线程(例如,仅每 20 帧进行一次分析)(*)
  7. 我敢打赌,在您可以充分优化 java 应用程序使其以 25fps 运行(使用 100% cpu xD)之前,您会使用母语为每个平台编写此应用程序两次
  8. 认真考虑混合解决方案;例如,使用可用工具将所有内容记录到压缩电影中,然后稍后进行分析 (**)

一言以蔽之:java 很糟糕,非常糟糕。

尽管如此,我已经编写了这个工具的我自己的版本。 http://pastebin.com/5h285fQw

它所做的一件事是允许在鼠标之后记录一个较小的矩形。在 500x500 下,它可以轻松达到 25fps,图像在后台写入(图像压缩+写入对我来说需要 5-10 毫秒,因此写入速度比记录速度快得多)


(*) 您没有谈论如何分析图像,但这似乎是性能问题的主要根源。一些想法:

  • 只查看每第 N 帧
  • 仅捕获屏幕的一个子部分,并随着时间的推移移动该子部分(稍后重新组合图像;这将导致可怕的撕裂,但也许这对您的目的并不重要)
  • 捕获本身应该“不太慢”(全屏可能为 10-20fps);使用串行捕获但并行分析

(**) 在 macOS 上,您可以使用内置的 Quicktime X 非常高效地录制到硬盘;在 Windows 上,我听说 playclaw (http://www.playclaw.com/) 非常好,也许物有所值(想想你在优化 java 野兽上浪费的时间会赚到什么:))