从屏幕截图生成视频源的替代方案

发布于 2024-12-29 16:54:33 字数 808 浏览 2 评论 0原文

我正在从事一个远程管理玩具项目。现在,我可以使用 Robot 类捕获屏幕截图并控制鼠标。屏幕截图是 BufferedImage 实例。

首先,我的要求: - 只有一个服务器和一个客户端。 - 性能很重要,因为客户端可能是 Android 应用程序。

我考虑过打开两个套接字连接,一个用于鼠标和系统命令,第二个用于视频源。

如何将屏幕截图转换为视频流?我应该将它们转换为已知的视频格式还是只发送一系列序列化图像就可以了?

压缩是另一个问题。根据我的初步测试,以全分辨率发送屏幕截图会导致帧速率较低。我认为我至少需要 24 fps 才能感知运动,因此我必须缩小规模并进行压缩。我可以将 BufferedImages 转换为 jpg 文件,然后设置压缩率,但我不想将文件存储在磁盘上,它们应该仅存在于 RAM 中。另一种可能性是将实例(表示未压缩的屏幕截图)序列化到 GZipOutputStream。正确的做法是什么?

总结一下:

  • 如果您推荐“图像系列”方法,您将如何将它们序列化到套接字OutputStream
  • 如果您的建议是转换为已知的视频格式,哪些类或库可用?

提前致谢。

更新:我的测试,客户端和服务器在同一台机器上
- 全屏序列化 BufferedImages(仅尺寸、类型和 int[]),无压缩:1.9 fps。
-通过 GZip 流的全屏图像:2.6 fps。
-缩小图像(640 宽度)和 GZip 流:6.56 fps。
-全屏图像和 RLE 编码:4.14 fps。
-缩小图像和 RLE 编码:7.29 fps。

I'm working in a remote administration toy project. For now, I'm able to capture screenshots and control the mouse using the Robot class. The screenshots are BufferedImage instances.

First of all, my requirements:
- Only a server and a client.
- Performance is important, since the client might be an Android app.

I've thought on opening two socket connections, one for mouse and system commands and the second one for the video feed.

How could I convert the screenshots to a video stream? Should I convert them to a known video format or would it be ok to just send a series of serialized images?

The compression is another problem. Sending the screen captures in full resolution would result in a low frame rate, according to my preliminary tests. I think I need at least 24 fps to perceive movement, so I've to both downscale and compress. I could convert the BufferedImages to jpg files and then set the compression rate, but I don't want to store the files on disk, they should live in RAM only. Another possibility would be to serialize instances (representing an uncompressed screenshot) to a GZipOutputStream. What is the correct approach for this?

To summarize:

  • In case you recommend the "series of images" approach, how would you serialize them to the socket OutputStream?
  • If your proposition is to convert to a know video format, which classes or libraries are available?

Thanks in advance.

UPDATE: my tests, client and server on same machine
-Full screen serialized BufferedImages (only dimension, type and int[]), without compression: 1.9 fps.
-full screen images through GZip streams: 2.6 fps.
-Downscaled images (640 width) and GZip streams: 6.56 fps.
-Full screen images and RLE encoding: 4.14 fps.
-Downscaled images and RLE encoding: 7.29 fps.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

小镇女孩 2025-01-05 16:54:33

如果它只是屏幕截图,我不会使用视频压缩方案来压缩它们,很可能您不想要有损压缩(小文本等中的模糊细节是最常见的缺陷)。
为了获得可行的“远程桌面”感觉,请记住之前发送的屏幕截图并仅发送差异以获取下一个屏幕截图。如果帧之间没有任何(或很少)变化,那么这是非常有效的。
然而,它在某些情况下效果不佳,例如播放视频、游戏或在文档中大量滚动。

压缩两个 BufferedImage 之间的差异可以通过或多或少复杂的方法来完成,一种非常简单但相当有效的方法是简单地将一个图像从另一个图像中减去(在它们相同的地方都为零)并使用简单的 RLE 压缩结果(游程长度编码)。

降低颜色精度可用于进一步减少数据量(根据使用情况,您可以省略每个颜色通道的最低有效 N 位,因为如果将颜色从 24 位减少到 15 位,大多数 GUI 应用程序看起来并没有太大不同位)。

If its just screen captures, I would not compress them using a Video compression scheme, most likely you don't want lossy compression (blurred details in small text etc are the most common defects).
For getting a workable "remote Desktop" feel, remember the previously sent Screenshot and send only the difference to get to the next one. If nothing (or very little) changes between frames this is very efficient.
It will however not work well in certain situations like playing a video, game or scrolling a lot in a document.

Compressing the difference between two BufferedImage can be done with more or less elaborate methods, a very simple, yet reasonably effective method is simply to subtract one image from the other (resulting in zeros everywhere they are identical) and compressing the result with simple RLE (run length encoding).

Reducing the color precision can be used to further reduce the amount of data (depending on the use case you could omit the least significant N bits of each color channel, for most GUI applications look not much different if you reduce colors from 24 bits to 15 bits).

没有伤那来痛 2025-01-05 16:54:33
  • 将屏幕分成网格方块(或条带)
  • 仅在与之前的网格方块不同时发送网格方块

// server start

sendScreenMetaToClient(); // width, height, how many grid squares
...

// server loop ImageBuffer[] prevScrnGrid while(isRunning) {

ImageBuffer scrn = captureScreen();
ImageBuffer[] scrnGrid = screenToGrid(scrn);
for(int i = 0; i < scrnGrid.length; i++) {
    if(isSameImage(scrnGrid[i], prevScrnGrid[i]) == false) {
        prevScrnGrid[i] = scrnGrid[i];
        sendGridSquareToClient(i, scrnGrid[i]); // send the client a message saying it will get grid square (i) then send the bytes for grid square (i)
    }
} }

不要发送序列化的 java 对象,仅发送图像数据。

ByteArrayOutputStream imgBytes = new ByteArrayOutputStream();
ImageIO.write( bufferedImage, "jpg", imgBytes );
imgBytes.flush();
  • Break the screen up into a grid squares (or strips)
  • Only send the grid square if it's different from the previous

// server start

sendScreenMetaToClient(); // width, height, how many grid squares
...

// server loop ImageBuffer[] prevScrnGrid while(isRunning) {

ImageBuffer scrn = captureScreen();
ImageBuffer[] scrnGrid = screenToGrid(scrn);
for(int i = 0; i < scrnGrid.length; i++) {
    if(isSameImage(scrnGrid[i], prevScrnGrid[i]) == false) {
        prevScrnGrid[i] = scrnGrid[i];
        sendGridSquareToClient(i, scrnGrid[i]); // send the client a message saying it will get grid square (i) then send the bytes for grid square (i)
    }
} }

Don't send serialized java objects just send the image data.

ByteArrayOutputStream imgBytes = new ByteArrayOutputStream();
ImageIO.write( bufferedImage, "jpg", imgBytes );
imgBytes.flush();
以可爱出名 2025-01-05 16:54:33

首先,我可能建议仅捕获屏幕的一小部分,而不是缩小尺寸并可能丢失信息,也许使用诸如滑动窗口之类的东西,可以通过用光标推动边缘来移动它。但这实际上只是一个小小的设计建议。

至于压缩,我认为一系列图像不会像体面的视频压缩方案那样单独压缩,特别是在这种情况下,帧可能在捕获之间保持一致。

一种选择是使用 Xuggle,它能够通过 Robot 以多种视频格式捕获桌面,但我不知道您是否可以使用它进行流式传输和解码。

要捕获 jpeg 并对其进行转换,您还可以使用 this。

不过,流式传输这些视频似乎有点复杂。

另外,似乎已废弃的 Java Media Framework 支持此功能。

老实说,我在这方面的知识并不出色,如果我浪费了您的时间,那么很抱歉,但看起来已经编译了一些关于使用 Xuggle 作为屏幕共享器的可行性的更有用的信息此处。这似乎也链接到他们自己对现有方法的注释。

如果它不需要是纯Java,我认为仅通过与本机屏幕捕获工具连接就可以更容易地使用...

毕竟,也许最简单的方法是将视频作为一系列jpeg 发送!如果你觉得有点疯狂,你总是可以实现你自己的压缩方案......

Firstly, I might suggest only capturing a small part of the screen, rather than downscaling and potentially losing information, perhaps with something like a sliding window which can be moved around by pushing the edges with a cursor. This is really just a small design suggestion though.

As for compression, I would think that a series of images would not compress separately as well as with a decent video compression scheme, especially as frames are likely to remain consistent between captures in this scenario.

One option would be to use Xuggle, which is capable of capturing the desktop via Robot in a number of video formats afaiu, but I can't tell if you can stream and decode with this.

For capturing jpegs and converting them, then you can also use this.

Streaming these videos seems to be a little more complicated, though.

Also, it seems that the abandoned Java Media Framework supports this functionality.

My knowledge in this area is not fantastic tbh, so sorry if I have wasted your time, but it looks like some more useful information on the feasibility of using Xuggle as a screensharer has been compiled here. This also appears to link to their own notes on existing approaches.

If it doesn't need to be pure Java I reckon this would all be much easier using just by interfacing with a native screen capture tool...

Maybe it would be easiest just to send video as a series of jpegs after all! You could always implement your own compression scheme if you were feeling a little crazy...

森林散布 2025-01-05 16:54:33

我认为您在问题中描述了一个很好的解决方案。将图像转换为 jpeg,但不要将它们作为文件写入磁盘。如果您希望它成为已知的视频格式,请使用 M-JPEG。 M-JPEG 是标准格式的 jpeg 帧流。许多数码相机,尤其是较旧的数码相机,都以这种格式保存视频。

您可以从此问题的答案中获取有关如何播放 M-JPEG 流的一些信息:Android 和 MJPEG

如果网络带宽是一个问题,那么您将需要使用帧间压缩系统,例如 MPEG-2、h.264 或类似系统。这比 M-JPEG 需要更多的处理,但效率更高。

I think you described a good solution in your question. Convert the images to jpeg, but don't write them as files to disk. If you want it to be a known video format, use M-JPEG. M-JPEG is a stream of jpeg frames in a standard format. Many digital cameras, especially older ones, save videos in this format.

You can get some information about how to play an M-JPEG stream from this question's answers: Android and MJPEG

If network bandwidth is a problem, then you'll want to use an inter-frame compression system such as MPEG-2, h.264, or similar. That requires a lot more processing than M-JPEG but is far more efficient.

雨的味道风的声音 2025-01-05 16:54:33

如果您想获取 24fps 视频,那么没有理由不使用现代视频编解码器。为什么要尝试重新创建那个轮子?

Xuggler 可以很好地编码 h264 视频,听起来它可以很好地满足您的需求。

If you're trying to get 24fps video then there's no reason not to use modern video codecs. Why try and recreate that wheel?

Xuggler works fine for encoding h264 video and sounds like it would serve your needs nicely.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文