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

我想用java来使用FFMPEG或其他解决scheme来截取我的机器的截图。 我知道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实际上可以工作,而不必更改上面的代码。

目标是快速地截屏,但是在截取“不同”的截图之后停止。 屏幕因某些事件而改变,窗户被closures,等等

Solutions Collecting From Web of "如何在Windows中使用Java快速截取桌面(ffmpeg等)?"

一般来说,使用内置Robots的Java实时屏幕截图(> = 30fps)并不像使用其他库时那样快,但它更容易,而且可能适合您的需求。 另一方面,如果这不适合你,我建议你使用JNA,因为它比Robots类的性能要好。

与机器人的方法

机器人类确实能够做你想做的事情,大多数机器人的屏幕捕捉方法的问题是屏幕截图的保存 。 一种方法可能是这样的:将captureScreen()方法循环,​​将屏幕抓取到BufferedImage中,将其转换为字节数组,然后将异步文件编写器保存到目标文件中,将未来的图像引用添加到ArrayList在存储图像数据时能够继续前进。

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

与JNA的方法

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

由于这只是一个不好的做法链接,我会在这里发表的例子:

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

更多信息和方法

我搜索了一段时间收集一些其他有用的信息,可能会帮助你,其中一些在这里类似的问题。

更新

本人对这个话题感兴趣,我继续寻找更有效的解决方案,并发现更多:

结论

如果你只是需要这样一些截图使用机器人类。 如果您需要> = 30fps(每秒超过30张屏幕截图)的流畅视频,则应首先尝试使用Robots的方法(以及可能的解决方案),并使用异步存储屏幕截图的性能改进。 如果没有,这是这个主题上的大多数帖子建议,尝试使用JNA如上所述,这是(即使它更复杂)几乎保证工作顺利的屏幕捕捉。

也可以看看

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

你熟悉Xuggler吗? 它使用FFmpeg进行编码和解码。 几个月前,我不得不从视频中提取帧,并且工作顺利。

在官方网站上你可以找到一些例子,包括一个名为“CaptureScreenToFile.java”的例子。 欲了解更多信息请点击这些链接

http://www.xuggle.com/xuggler/

https://github.com/artclarke/xuggle-xuggler/tree/master/src/com/xuggle/xuggler/demos

根据官方的ffmpeg文档 ,如果将file参数传递给FFmpegFrameGrabber (这实际上是一个input参数,作为FFmpegFrameGrabber选项传递给ffmpeg),则它应该能够保持相当的跨平台, 每个device期望 。

即:

对于Windows: dshow预计-i video="screen-capture-recorder"

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

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

所以只需将这些值(参数-i )传递给构造函数,并相应地设置格式(通过setFormat )就可以实现。