中快速使用Java的桌面屏幕截图

中快速使用Java的桌面屏幕截图

本文介绍了如何在Windows(ffmpeg等)中快速使用Java的桌面屏幕截图?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想使用java来使用FFMPEG或其他解决方案来拍摄我的机器的屏幕截图。我知道linux与没有JNI的ffmpeg一起工作,但在Windows中运行它不起作用,可能需要(JNI?)有一些简单的Java类(还有其他必要的)的示例来捕获在Windows环境中运行的截图吗?有没有替代FFMPEG?我想以比Java Robot API快的速度拍摄截图,我发现它在截图上工作,但比我想要的要慢。



我知道在Linux中,这个工作非常快:

  import com.googlecode.javacv。*; 

public class ScreenGrabber {
public static void main(String [] args)throws Exception {
int x = 0,y = 0,w = 1024,h = 768;
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(:0.0++ x +,+ y);
grabber.setFormat(x11grab);
grabber.setImageWidth(w);
grabber.setImageHeight(h);
grabber.start();

CanvasFrame frame = new CanvasFrame(Screen Capture);
while(frame.isVisible()){
frame.showImage(grabber.grab());
}
frame.dispose();
grabber.stop();
}

这在Windows环境中不起作用。我不知道是否有某种方式我可以使用相同的代码,但是使用javacpp实际上可以使其工作,而无需更改上述代码。



目标是屏幕快照屏幕截图,然后停止后,屏幕截图是不同的,也是。屏幕因为某些事件而改变,窗口关闭等等。

解决方案

一般来说,实时屏幕捕获( > = 30fps)与使用内置机器人的Java不如您使用其他库可以获得的那样快,但它更容易,应该可以满足您的需求。另一方面,如果这不适合你,我建议你使用JNA,因为它具有比Robots类更好的性能,这样的任务。



使用机器人的方法



机器人类确实有能力做你想要的,机器人大多数屏幕捕获方法的问题是保存的截图。一种方法可能如下所示:循环使用captureScreen()方法,将屏幕抓取到BufferedImage,将其添加到字节数组,并将其添加到目标文件中,并将其添加到图像的未来引用到ArrayList能够在存储图像数据的同时继续进行。

  //伪代码


while(capture)
{
从屏幕
抓取buffersImage(screenCapture)将bufferImage转换为字节数组
启动异步文件通道以写入输出文件并添加未来引用(返回值)到ArrayList
}

strong>



原始问题:



因为这是不好的做法,只是链接,我将在这里发布示例: / p>

  im端口java.awt.Rectangle; 
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;

import com.sun.jna.Native;
import com.sun.jna.platform.win32.W32API;
import com.sun.jna.win32.W32APIOptions;

public class JNAScreenShot
{

public static BufferedImage getScreenshot(Rectangle bounds)
{
W32API.HDC windowDC = GDI.GetDC USER.GetDesktopWindow());
W32API.HBITMAP outputBitmap = GDI.CreateCompatibleBitmap(windowDC,bounds.width,bounds.height);
try
{
W32API.HDC blitDC = GDI.CreateCompatibleDC(windowDC);
try
{
W32API.HANDLE oldBitmap = GDI.SelectObject(blitDC,outputBitmap);
try
{
GDI.BitBlt(blitDC,0,0,bounds.width,bounds.height,windowDC,bounds.x,bounds.y,GDI32.SRCCOPY);
}
finally
{
GDI.SelectObject(blitDC,oldBitmap);
}
GDI32.BITMAPINFO bi = new GDI32.BITMAPINFO(40);
bi.bmiHeader.biSize = 40;
boolean ok = GDI.GetDIBits(blitDC,outputBitmap,0,bounds.height,(byte [])null,bi,GDI32.DIB_RGB_COLORS);
if(ok)
{
GDI32.BITMAPINFOHEADER bih = bi.bmiHeader;
bih.biHeight = -Math.abs(bih.biHeight);
bi.bmiHeader.biCompression = 0;
return bufferedImageFromBitmap(blitDC,outputBitmap,bi);
}
else
{
返回null;
}
}
finally
{
GDI.DeleteObject(blitDC);
}
}
finally
{
GDI.DeleteObject(outputBitmap);
}
}

private static BufferedImage bufferedImageFromBitmap(GDI32.HDC blitDC,GDI32.HBITMAP outputBitmap,GDI32.BITMAPINFO bi)
{
GDI32.BITMAPINFOHEADER bih = bi.bmiHeader;
int height = Math.abs(bih.biHeight);
final ColorModel cm;
final DataBuffer buffer;
final WritableRaster raster;
int strideBits =(bih.biWidth * bih.biBitCount);
int strideBytesAligned =(((strideBits - 1)| 0x1F)+ 1)>> 3;
final int strideElementsAligned;
开关(bih.biBitCount)
{
案例16:
strideElementsAligned = strideBytesAligned / 2;
cm = new DirectColorModel(16,0x7C00,0x3E0,0x1F);
buffer = new DataBufferUShort(strideElementsAligned * height);
raster = Raster.createPackedRaster(buffer,bih.biWidth,height,strideElementsAligned,((DirectColorModel)cm).getMasks(),null);
break;
case 32:
strideElementsAligned = strideBytesAligned / 4;
cm = new DirectColorModel(32,0xFF0000,0xFF00,0xFF);
buffer = new DataBufferInt(strideElementsAligned * height);
raster = Raster.createPackedRaster(buffer,bih.biWidth,height,strideElementsAligned,((DirectColorModel)cm).getMasks(),null);
break;
default:
throw new IllegalArgumentException(不支持的位数:+ bih.biBitCount);
}
final boolean ok;
switch(buffer.getDataType())
{
case DataBuffer.TYPE_INT:
{
int [] pixels =((DataBufferInt)buffer).getData() ;
ok = GDI.GetDIBits(blitDC,outputBitmap,0,raster.getHeight(),pixels,bi,0);
}
break;
case DataBuffer.TYPE_USHORT:
{
short [] pixels =((DataBufferUShort)buffer).getData();
ok = GDI.GetDIBits(blitDC,outputBitmap,0,raster.getHeight(),pixels,bi,0);
}
break;
default:
throw new AssertionError(Unexpected buffer element type:+ buffer.getDataType());
}
if(ok)
{
返回新的BufferedImage(cm,raster,false,null);
}
else
{
返回null;
}
}

private static final User32 USER = User32.INSTANCE;

private static final GDI32 GDI = GDI32.INSTANCE;

}

接口GDI32扩展com.sun.jna.platform.win32.GDI32
{
GDI32 INSTANCE =(GDI32)Native.loadLibrary( GDI32.class);

boolean BitBlt(HDC hdcDest,int nXDest,int nYDest,int nWidth,int nHeight,HDC hdcSrc,int nXSrc,int nYSrc,int dwRop);

HDC GetDC(HWND hWnd);

boolean GetDIBits(HDC dc,HBITMAP bmp,int startScan,int scanLines,byte [] pixels,BITMAPINFO bi,int usage);

boolean GetDIBits(HDC dc,HBITMAP bmp,int startScan,int scanLines,short [] pixels,BITMAPINFO bi,int usage);

boolean GetDIBits(HDC dc,HBITMAP bmp,int startScan,int scanLines,int [] pixels,BITMAPINFO bi,int usage);

int SRCCOPY = 0xCC0020;
}

接口User32扩展com.sun.jna.platform.win32.User32
{
User32 INSTANCE =(User32)Native.loadLibrary(User32.class, W32APIOptions.UNICODE_OPTIONS);

HWND GetDesktopWindow();
}

更多信息&方法



我搜索了一段时间,收集一些可能有助于您帮助的其他有用信息,其中一些与SO中的类似问题有关。











更新



对自己有兴趣,我继续寻找更有效的解决方案,并发现了更多:



结论 / p>

如果您只需要这样一些截图,请使用机器人类。
如果您需要一个平滑的视频> = 30fps(每秒超过30个屏幕快照),您应该首先尝试如果使用机器人的方法(以及可能的解决方案)和使用异步存储屏幕截图的性能改进,您。如果没有,这是大多数帖子的这个话题建议,尝试使用JNA如上所述,即使它更复杂,几乎保证了平滑的屏幕捕获。



另请参阅














I would like to use java to take a screenshot of my machine using FFMPEG or some other solution. I know linux works with ffmpeg without JNI, but running it in Windows does not work and may require (JNI?) is there any sample of some simple Java class (and anything else necessary) to capture a screenshot runnable in a windows environment? Is there some alternative to FFMPEG? I want to take screenshot at a rate faster than the Java Robot API, which I have found to work at taking screenshots, but is slower than I would like.

I know in Linux this works very fast:

import com.googlecode.javacv.*;

public class ScreenGrabber {
    public static void main(String[] args) throws Exception {
        int x = 0, y = 0, w = 1024, h = 768;
        FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(":0.0+" + x + "," + y);
        grabber.setFormat("x11grab");
        grabber.setImageWidth(w);
        grabber.setImageHeight(h);
        grabber.start();

        CanvasFrame frame = new CanvasFrame("Screen Capture");
        while (frame.isVisible()) {
            frame.showImage(grabber.grab());
        }
        frame.dispose();
        grabber.stop();
    }

This does not work in windows environment. Am not sure if there is some way I could use this same code, but use javacpp to actually get it working without having to change much of the above code.

Goal is to take screenshots of screen fast, but then stop after it takes a screenshot that is "different", aka. screen changed because of some event like, a window is window closed, etc.

解决方案

Generally speaking, real-time screen capturing (>=30fps) with Java using the built-in Robots is not nearly as fast as what you can get using other libraries, but it is way easier and should probably fit your needs. On the other hand, if that doesnt work out for you, I suggest you use JNA as it has way better performance than the Robots class for tasks like this.

Approach with Robots

The robots class is indeed capable of doing what you want, the problem most screen capturing approaches with Robots have is the saving of the screenshots. An approach could look like that: Looping over the captureScreen() method, grabbing the screen into a BufferedImage, convert it to a byte array and save it with an asynchronous file writer to a target file after adding the future reference of your image to the ArrayList to be able to keep going while storing the image data.

//Pseudo code


while (capturing)
{
    grab bufferedImage (screenCapture) from screen
    convert bufferImage to byte array
    start asynchronous file channel to write to the output file and add the future reference (return value) to the ArrayList
}

Approach with JNA

Original Question:How to take screenshots fast in Java?

As it is bad practice to just link, I will post the example here:

import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DataBufferUShort;
import java.awt.image.DirectColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;

import com.sun.jna.Native;
import com.sun.jna.platform.win32.W32API;
import com.sun.jna.win32.W32APIOptions;

public class JNAScreenShot
{

    public static BufferedImage getScreenshot(Rectangle bounds)
    {
        W32API.HDC windowDC = GDI.GetDC(USER.GetDesktopWindow());
        W32API.HBITMAP outputBitmap = GDI.CreateCompatibleBitmap(windowDC, bounds.width, bounds.height);
        try
        {
            W32API.HDC blitDC = GDI.CreateCompatibleDC(windowDC);
            try
            {
                W32API.HANDLE oldBitmap = GDI.SelectObject(blitDC, outputBitmap);
                try
                {
                    GDI.BitBlt(blitDC, 0, 0, bounds.width, bounds.height, windowDC, bounds.x, bounds.y, GDI32.SRCCOPY);
                }
                finally
                {
                    GDI.SelectObject(blitDC, oldBitmap);
                }
                GDI32.BITMAPINFO bi = new GDI32.BITMAPINFO(40);
                bi.bmiHeader.biSize = 40;
                boolean ok = GDI.GetDIBits(blitDC, outputBitmap, 0, bounds.height, (byte[]) null, bi, GDI32.DIB_RGB_COLORS);
                if (ok)
                {
                    GDI32.BITMAPINFOHEADER bih = bi.bmiHeader;
                    bih.biHeight = -Math.abs(bih.biHeight);
                    bi.bmiHeader.biCompression = 0;
                    return bufferedImageFromBitmap(blitDC, outputBitmap, bi);
                }
                else
                {
                    return null;
                }
            }
            finally
            {
                GDI.DeleteObject(blitDC);
            }
        }
        finally
        {
            GDI.DeleteObject(outputBitmap);
        }
    }

    private static BufferedImage bufferedImageFromBitmap(GDI32.HDC blitDC, GDI32.HBITMAP outputBitmap, GDI32.BITMAPINFO bi)
    {
        GDI32.BITMAPINFOHEADER bih = bi.bmiHeader;
        int height = Math.abs(bih.biHeight);
        final ColorModel cm;
        final DataBuffer buffer;
        final WritableRaster raster;
        int strideBits = (bih.biWidth * bih.biBitCount);
        int strideBytesAligned = (((strideBits - 1) | 0x1F) + 1) >> 3;
        final int strideElementsAligned;
        switch (bih.biBitCount)
        {
            case 16:
                strideElementsAligned = strideBytesAligned / 2;
                cm = new DirectColorModel(16, 0x7C00, 0x3E0, 0x1F);
                buffer = new DataBufferUShort(strideElementsAligned * height);
                raster = Raster.createPackedRaster(buffer, bih.biWidth, height, strideElementsAligned, ((DirectColorModel) cm).getMasks(), null);
                break;
            case 32:
                strideElementsAligned = strideBytesAligned / 4;
                cm = new DirectColorModel(32, 0xFF0000, 0xFF00, 0xFF);
                buffer = new DataBufferInt(strideElementsAligned * height);
                raster = Raster.createPackedRaster(buffer, bih.biWidth, height, strideElementsAligned, ((DirectColorModel) cm).getMasks(), null);
                break;
            default:
                throw new IllegalArgumentException("Unsupported bit count: " + bih.biBitCount);
        }
        final boolean ok;
        switch (buffer.getDataType())
        {
            case DataBuffer.TYPE_INT:
            {
                int[] pixels = ((DataBufferInt) buffer).getData();
                ok = GDI.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0);
            }
                break;
            case DataBuffer.TYPE_USHORT:
            {
                short[] pixels = ((DataBufferUShort) buffer).getData();
                ok = GDI.GetDIBits(blitDC, outputBitmap, 0, raster.getHeight(), pixels, bi, 0);
            }
                break;
            default:
                throw new AssertionError("Unexpected buffer element type: " + buffer.getDataType());
        }
        if (ok)
        {
            return new BufferedImage(cm, raster, false, null);
        }
        else
        {
            return null;
        }
    }

    private static final User32 USER = User32.INSTANCE;

    private static final GDI32 GDI = GDI32.INSTANCE;

}

interface GDI32 extends com.sun.jna.platform.win32.GDI32
{
    GDI32 INSTANCE = (GDI32) Native.loadLibrary(GDI32.class);

    boolean BitBlt(HDC hdcDest, int nXDest, int nYDest, int nWidth, int nHeight, HDC hdcSrc, int nXSrc, int nYSrc, int dwRop);

    HDC GetDC(HWND hWnd);

    boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines, byte[] pixels, BITMAPINFO bi, int usage);

    boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines, short[] pixels, BITMAPINFO bi, int usage);

    boolean GetDIBits(HDC dc, HBITMAP bmp, int startScan, int scanLines, int[] pixels, BITMAPINFO bi, int usage);

    int SRCCOPY = 0xCC0020;
}

interface User32 extends com.sun.jna.platform.win32.User32
{
    User32 INSTANCE = (User32) Native.loadLibrary(User32.class, W32APIOptions.UNICODE_OPTIONS);

    HWND GetDesktopWindow();
}

More Information & Approaches

I searched for a while to collect some additional useful information that might help you, some of them in similar questions here on SO.

Update

Interested in the topic myself, I continued searching for more effective solutions and found a lot more:

Conclusion

If you just need to so some screenshots use the Robots class.If you need a smooth video with >= 30fps (more than 30 screenshots per second), you should first try if the approach (and possible solution) with Robots and performance improvements there using asynchronous storing of the screenshots works out for you. If it doesnt, and this is what most posts on this topic suggest, try using JNA as stated above and that is (even though its more complex) almost guaranteed to work for smooth screen capturing.

See also

这篇关于如何在Windows(ffmpeg等)中快速使用Java的桌面屏幕截图?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-30 15:00