[英]Capturing from the screen and saving to disk multithreaded
以下问题应该是观看屏幕,记录事件(测量文本框变为绿色)并记录导致它的所有事件,从而产生导致它的事件的“电影”。 不幸的是,需要记录整个屏幕。 到目前为止,我已经完成了识别参与的部分。 但是我几乎没有每秒两帧。 我想有大约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();
}
}
}
我试图稍微编辑你的代码,而不是创建png
文件我尝试创建bmp
文件,这消除了数据压缩的开销时间,但是以磁盘空间为代价。
结果:我不知道如何计算fps,但解决方案比你的更快。 :-)
您应该测量robot.createScreenCapture()
花费的时间。 有可能它需要超过40毫秒,这意味着没有办法用纯Java实现你想要的。 根据我的经验,电话会很慢。
我确实设法通过一个技巧大大缩短了时间,但它仅适用于Unix:启动VNC服务器(= RAM中的桌面)。 我修补了TightVNC的源代码,使用NIO使用内存映射文件将映像写入磁盘。 这给了我大约10-20 fps。
以下是使用NIO编写图像的代码:
private File pixelFile = new File("tmp", "pixels.nio").getAbsoluteFile();
private IntBuffer intBuffer;
private FileChannel rwChannel;
private MappedByteBuffer byteBuffer;
private int[] pixels;
private void createMemoryMappedFile() {
File dir = pixelFile.getParentFile();
if(!dir.exists()) {
dir.mkdirs();
}
try {
rwChannel = new RandomAccessFile(pixelFile, "rw").getChannel();
int width = ...;
int height = ...;
pixels = new int[width*height];
byteBuffer = rwChannel.map(MapMode.READ_WRITE, 0, width * height * 4);
intBuffer = byteBuffer.asIntBuffer();
} catch(Exception e) {
throw new RuntimeException("Error creating NIO file " + pixelFile, e);
}
}
public void saveImage() {
buffer.position(0);
buffer.put(image.getRaster().getPixels(0,0,width,height,pixels));
flushPixels();
}
private void flushPixels() {
byteBuffer.force();
}
您最大的问题是您只能获得一个线程来实际创建图像。 ThreadPoolExecutor不会按照您期望的方式创建线程。
来自javadoc
因此,除非队列已满,否则它将只使用一个线程。 此时,您在内存中有100个屏幕截图,这是为GC添加工作。 如果我将核心线程设置为4(我的笔记本电脑上有4个核心)并将内存增加到1 GB,我设法捕获20 FPS左右。
如果您的磁盘输出是限制性的,您可以将最后400个写入的图像作为字节数组存储在队列中,并在按钮变为“绿色”时将它们写入磁盘。 但是在我的测试中,这些图像需要超过100MB的RAM,所以再次确保你有足够的内存。
由于使用执行程序的各种原因,重写到nio和类似的东西都无济于事。
这里有一堆你应该考虑的事情:
总而言之:java糟透了你想做的事,非常糟糕。
尽管如此,我已经编写了我自己的这个工具版本。 http://pastebin.com/5h285fQw
它做的一件事是允许在鼠标后面记录一个较小的矩形。 在500x500,它让我很容易达到25fps,图像在后台写入(图像压缩+写入对我来说需要5-10ms,因此写入比记录快得多)
(*)您不会谈论如何分析图像,但这似乎是您性能问题的主要来源。 一些想法:
(**)在macosx上你可以使用内置的quicktime x来非常有效地记录到hdd; 在Windows上,我听说playclaw(http://www.playclaw.com/)非常好,也许值得花钱(想想你在浪费的时候浪费了优化java野兽:))
关闭主题,但看看Xuggler 。 如果您想使用Java创建视频,那将非常有用。
编辑:此外,如果您不将每张图片转储到磁盘,但可以将它们附加到字节数组,并将它们很少转储到磁盘上,您可以优化图像使用者代码。
编辑2:有“无安装”版本的库和maven依赖项(带有预编译平台特定库的jar): blog.xuggle.com/2012/04/03/death-to-installers和xuggle。 COM / xuggler /下载
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.