当前位置: 首页 > 面试题库 >

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

姚兴安
2023-03-14
问题内容

我想使用Java使用FFMPEG或其他解决方案来截取我的机器的屏幕截图。我知道linux在没有JNI的情况下可以与ffmpeg一起使用,但是在Windows中运行它却无法正常工作,并且可能需要(JNI?)是否有一些简单的Java类的示例(以及其他必需的东西)来捕获可在Windows环境中运行的屏幕快照?FFMPEG可以替代吗?我想以比Java
Robot API更快的速度来截屏,我发现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使其真正起作用,而不必更改上述大部分代码。

目标是快速截取屏幕截图,但在截取“不同”(又称为“截屏”)之后停止。由于某些事件(例如窗口关闭)等原因导致屏幕发生了变化。


问题答案:

使用内置的 Robots类比 其他Java库更容易,并且可能满足您的需求。

如果您需要一个> = 30fps的流畅视频(每秒超过30个屏幕截图),则应首先尝试使用Robots方法以及其中 的屏幕截图异步存储功能来 改善性能。

如果它对您不起作用,请尝试使用 JNA ,它几乎可以保证(即使更复杂)也可以正常进行屏幕捕获。

机器人接近

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

// 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的方法

由于只是链接是一种不好的做法,因此我将在此处发布示例:

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

也可以看看

  • http://www.thepcwizard.in/2012/12/java-screen-capturing-tutorial.html

  • 如何将截屏开发为视频应用程序

  • http://www.javalobby.org/forums/thread.jspa?threadID=16400&tstart=0

  • http://hiddensciencex.blogspot.co.at/2014/01/fast-screen-capture-in-java-example.html

  • http://www.coderanch.com/t/340180/GUI/java/ficient-screenshot-Java

  • http://www.javaworld.com/article/2071755/learn-java/capture-the-screen.html



 类似资料:
  • 我想使用java使用FFMPEG或其他解决方案拍摄我的机器的屏幕截图。我知道linux可以在没有JNI的情况下使用ffmpeg,但是在Windows中运行它不起作用,可能需要(JNI?)是否有一些简单的Java类的示例(以及任何其他必要的)来捕获可在windows环境中运行的屏幕截图?有什么替代FFMPEG的方法吗?我想以比Java机器人应用编程接口更快的速度截屏,我发现它可以截屏,但比我希望的要

  • 可在游戏中等拍摄并保存任何场景。同时按下PS键和START键。 可从(照片)查看已保存的信息。 若要拍摄画面截图,需事先将PS Vita专用存储卡插入主机。 因画面或应用程序的状态,可能无法拍摄画面截图。

  • 我需要使用Sikuli的自动化,但我无法采取截图。问题是,当我点击“截屏”或“创建区域”时,IDE会被隐藏,但选择区域选项不会出现....

  • 问题内容: 我正在实现一个简单的眼动仪,它需要对屏幕上正在发生的事情进行快速屏幕截图,并同时从网络摄像头捕获视频。 顺便说一句,从网络摄像头检索视频的速度更快,并且返回字节数组,该字节数组的处理速度非常快。 有人知道更快的解决方案吗?可以链接到Java的C ++库也可能会有所帮助。 谢谢! 更新:决定切换到OpenCV,现在正在寻找使用它制作屏幕截图的方法:) 问题答案: 你绝对应该对OpenCV

  • 问题内容: 我知道以前是不可能的,但是现在有了以下更新: https://developers.google.com/web/updates/2017/04/devtools-release- notes#screenshots 使用Chrome开发工具似乎可以做到这一点。 现在可以在Java中使用Selenium吗? 问题答案: 要使用Java中的Selenium Webdriver进行此工作需

  • 嗨,我如何在Windows电脑上使用任何浏览器拍摄网页的完整屏幕截图? 每当我用PrintScreen截图时,它只截图显示页面,但我希望截图的高度为整页。我们能拍完整的截图吗,包括隐藏的八张? 抱歉我英语不好。 我有firefox和chrome。