首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >如何在Windows (ffmpeg等)中使用Java快速拍摄桌面屏幕截图?

如何在Windows (ffmpeg等)中使用Java快速拍摄桌面屏幕截图?
EN

Stack Overflow用户
提问于 2014-07-10 04:16:44
回答 4查看 14.5K关注 0票数 11

我想使用java来截图我的机器,使用FFMPEG或其他解决方案。我知道linux可以在没有JNI的情况下使用ffmpeg,但是在Windows中运行它并不有效,而且可能需要(JNI?)有没有一些简单Java类的示例(以及其他必要的)来捕获windows环境中可运行的屏幕截图?有什么替代FFMPEG的方法吗?我想以比Java机器人API更快的速度拍摄屏幕截图,但速度比我想要的要慢。

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

代码语言:javascript
运行
复制
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来实现它的实际工作,而不必更改上面的大部分代码。

目标是快速抓取屏幕截图,但在截图后停止,这是“不同的”,又名。屏幕因某些事件而改变,如窗口关闭等。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-07-14 13:43:17

与其他库相比,使用内置的机器人类要容易得多,而且可能满足您的需要。

如果您需要一个带有>= 30 the的平滑视频(每秒超过30个屏幕截图),您应该首先尝试机器人方法,并使用异步存储屏幕截图来提高性能。

如果它不适合您,尝试使用JNA,这是(尽管它更复杂)几乎可以保证顺利的屏幕捕捉。

机器人进场

机器人类确实能够做您想做的事情,大多数使用机器人的屏幕捕获方法都存在的问题是截图的保存。一种方法可能是这样的:遍历captureScreen()方法,将屏幕抓取到BufferedImage中,将其转换为字节数组,并在将图像的未来引用添加到ArrayList之后,使用异步文件编写器将其保存到目标文件,以便能够在存储图像数据的同时继续运行。

代码语言:javascript
运行
复制
// 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
}

与JNA的接触

原始问题:如何在Java中快速抓取截图?

由于链接是一种糟糕的做法,我将在这里发布这个示例:

代码语言:javascript
运行
复制
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();
}

更多的信息和方法

也见

票数 33
EN

Stack Overflow用户

发布于 2015-03-04 21:41:09

根据官方的ffmpeg文件,如果您将file参数传递给FFmpegFrameGrabber (实际上是一个input参数,作为ffmpeg的-i选项被传递下来),您应该能够保持它非常跨平台。

ie:

Windows:dshow期望-i video="screen-capture-recorder"

对于OSX:avfoundation期望-i "<screen device index>":

对于Linux:x11grab期望-i :<display id>+<x>,<y>

因此,只要将这些值(参数传递给-i)传递给构造函数并设置格式(通过setFormat),就可以做到这一点。

票数 2
EN

Stack Overflow用户

发布于 2014-07-11 00:15:59

您需要使用JNI或JNA调用CreateCompatibleBitmap、XGetImage、DirectX或OpenGL的某些组合,以获取屏幕快照,然后将一些原始位图数据复制回Java。我的分析显示,在X11上访问原始位图数据时,机器人类的速度比机器人类快约400%。我目前还没有测试过其他平台。有些非常早期的代码是可用的这里,但我最近没有太多时间来处理它。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24668407

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档