Windows下最快的屏幕截图方法

183

我想为Windows平台编写一个屏幕录制程序,但不确定如何捕捉屏幕。我所知道的唯一方法是使用GDI,但我想知道是否有其他方法可以达到同样的效果,并且如果有的话,哪种方法的开销最小?速度是首要考虑因素。

这个屏幕录制程序将用于记录游戏画面,但如果这会限制选项,我仍然对任何超出此范围的建议持开放态度。毕竟,没有坏处的知识。

编辑:我看到了这篇文章:各种捕获屏幕的方法。它介绍了使用Windows Media API和DirectX的方法。在结论中提到禁用硬件加速可以大大提高录制应用程序的性能。我想知道为什么会这样。有人可以为我填补这些空白吗?

编辑:我了解到像Camtasia这样的屏幕录制程序使用自己的捕获驱动程序。有人可以详细解释一下它是如何工作的以及为什么更快吗?我可能还需要指导如何实现类似的东西,但我相信已经存在相关的文档。

此外,我现在知道FRAPS如何录制屏幕。它挂钩底层图形API以从后台缓冲区读取。据我所知,这比从前台缓冲区读取要快,因为您正在从系统RAM而不是视频RAM中进行读取。您可以在此处阅读该文章。


2
你不需要hook任何东西。你只需要编写输入事件,以便它们不直接控制游戏,而是调用其他函数。例如,如果玩家按下左键,您不应该简单地减少玩家的x位置。相反,您需要调用一个函数,比如MovePlayerLeft()。同时,您还需要记录按键和其他输入的时间和持续时间。然后,当您处于回放模式时,您只需忽略输入,而是读取记录的数据。如果在数据中看到左键按下,则调用MovePlayerLeft() - Benjamin Lindley
2
@PigBen 这将是一个通用的应用程序,用于记录游戏画面。它不是针对特定游戏的。我知道,有人按左键可能意味着向右移动。此外,您还没有考虑到不受用户影响的事件。“那渲染呢?” - someguy
1
@someguy 好的,我猜你正在做更加复杂的事情。我使用上述方法添加了一个例程,在游戏中以约30fps保存回放AVI文件而没有任何问题。我使用Windows API制作了一个多监视器屏幕录像机,以进行“工作力量优化”,但即使以4fps为目标,性能也很差。 - AJG85
1
在UltraVNC的代码库网站上有一个Windows开源镜像驱动程序,位于此处http://ultravnc.svn.sourceforge.net/viewvc/ultravnc/UltraVNC%20Project%20Root/UltraVNC/winvnc/winvnc/。 - Beached
1
@anddero: 正确。我没有机会对任何方法进行基准测试。我怀疑我很快就不会尝试了,因为我放弃了这个项目。如果你能自己尝试并报告一些发现,那将是很棒的。 - someguy
显示剩余14条评论
16个回答

5
屏幕录制可以使用 VLC APIC# 中完成。我已经编写了一个示例程序来演示此功能。它使用 LibVLCSharpVideoLAN.LibVLC.Windows 库。您可以使用这个跨平台 API 实现更多与视频渲染相关的功能。
有关 API 文档,请参见:LibVLCSharp API Github
using System;
using System.IO;
using System.Reflection;
using System.Threading;
using LibVLCSharp.Shared;

namespace ScreenRecorderNetApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Core.Initialize();

            using (var libVlc = new LibVLC())
            using (var mediaPlayer = new MediaPlayer(libVlc))
            {
                var media = new Media(libVlc, "screen://", FromType.FromLocation);
                media.AddOption(":screen-fps=24");
                media.AddOption(":sout=#transcode{vcodec=h264,vb=0,scale=0,acodec=mp4a,ab=128,channels=2,samplerate=44100}:file{dst=testvlc.mp4}");
                media.AddOption(":sout-keep");

                mediaPlayer.Play(media);
                Thread.Sleep(10*1000);
                mediaPlayer.Stop();
            }
        }
    }
}

1
敬启者:请注意,libvlc是在GPL下发布的。如果您不打算在GPL下发布您的代码,请勿使用libvlc。 - Sebastian Cabot
2
这并不完全准确,LibVLC是LGPL许可证 - 它在2011年重新许可。VLC应用程序本身仍然是GPL:https://www.videolan.org/press/lgpl-libvlc.html - Andrew

3

这可能不是最快的方法,但它非常轻量级且易于使用。图像以整数数组返回,包含RGB颜色。

#define WIN32_LEAN_AND_MEAN
#define VC_EXTRALEAN
#include <Windows.h>
int* screenshot(int& width, int& height) {
    HDC hdc = GetDC(NULL); // get the desktop device context
    HDC cdc = CreateCompatibleDC(hdc); // create a device context to use yourself
    height = (int)GetSystemMetrics(SM_CYVIRTUALSCREEN); // get the width and height of the screen
    width  = 16*height/9; // only capture left monitor for dual screen setups, for both screens use (int)GetSystemMetrics(SM_CXVIRTUALSCREEN);
    HBITMAP hbitmap = CreateCompatibleBitmap(hdc, width, height); // create a bitmap
    SelectObject(cdc, hbitmap); // use the previously created device context with the bitmap
    BITMAPINFOHEADER bmi = { 0 };
    bmi.biSize = sizeof(BITMAPINFOHEADER);
    bmi.biPlanes = 1;
    bmi.biBitCount = 32;
    bmi.biWidth = width;
    bmi.biHeight = -height; // flip image upright
    bmi.biCompression = BI_RGB;
    bmi.biSizeImage = 3*width*height;
    BitBlt(cdc, 0, 0, width, height, hdc, 0, 0, SRCCOPY); // copy from desktop device context to bitmap device context
    ReleaseDC(NULL, hdc);
    int* image = new int[width*height];
    GetDIBits(cdc, hbitmap, 0, height, image, (BITMAPINFO*)&bmi, DIB_RGB_COLORS);
    DeleteObject(hbitmap);
    DeleteDC(cdc);
    return image;
}

以上代码结合了这个答案这个答案

如何使用的示例:

int main() {
    int width=0, height=0;
    int* image = screenshot(width, height);

    // access pixel colors for position (x|y)
    const int x=0, y=0;
    const int color = image[x+y*width];
    const int red   = (color>>16)&255;
    const int green = (color>> 8)&255;
    const int blue  =  color     &255;

    delete[] image;
}

2

我自己使用DirectX,认为它的速度足够快。我没有一个快速的代码示例,但我找到了这篇文章,应该会有所帮助。DirectX11版本应该不会有太大区别,DirectX9可能会有一些不同,但这是正确的方法。


2

DXGI桌面捕获

这个项目使用DXGI复制技术来捕获桌面图像,并将捕获的图像保存为不同的图像格式(*.bmp; *.jpg; *.tif)。

这个示例是用C++编写的。你需要一些DirectX(D3D11、D2D1)的经验。

应用程序可以做什么

  • 如果你有多个桌面监视器,你可以进行选择。
  • 调整捕获的桌面图像的大小。
  • 选择不同的缩放模式。
  • 在输出图像中显示或隐藏鼠标图标。
  • 你可以旋转图像以便输出图片,或者保持默认状态。

1

0

我知道以下建议并不能回答你的问题,但是我发现捕捉快速变化的DirectX视图最简单的方法是将一个视频摄像机插入视频卡的S-video端口,并将图像记录为电影。然后将视频从摄像机传输回计算机上的MPG、WMV、AVI等文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接