连续捕获屏幕区域

时间:2018-10-17 19:48:52

标签: c# bitmap screen-capture

我希望不断捕获屏幕区域。

我可以使用LockBitsBitBlt等来执行此操作,但是我的所有测量均显示,捕获单个帧平均需要30毫秒。似乎与VSynch非常相似...它将尝试将屏幕更新速率保持恒定为30ms ...

但是,我刚遇到following post

其中有人声称可以在95毫秒内获得 1000帧 ...(对我来说是闻所未闻的)。这是他发布的代码:

using System;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Diagnostics;
namespace WindowsFormsApplication1
{
  public partial class Form1 : Form
  {
    public Form1()
    {
      InitializeComponent();
    }
    private readonly Stopwatch sw = new Stopwatch();
    private static Bitmap CaptureImage(int x, int y)
    {
      Bitmap b = new Bitmap(100, 100);
      using (Graphics g = Graphics.FromImage(b))
      {
        g.CopyFromScreen(x, y, 0, 0, new Size(100, 100), CopyPixelOperation.SourceCopy);
        g.DrawLine(Pens.Black, new Point(0, 27), new Point(99, 27));
        g.DrawLine(Pens.Black, new Point(0, 73), new Point(99, 73));
        g.DrawLine(Pens.Black, new Point(52, 0), new Point(52, 99));
        g.DrawLine(Pens.Black, new Point(14, 0), new Point(14, 99));
        g.DrawLine(Pens.Black, new Point(85, 0), new Point(85, 99));
      }
      return b;
    }
    private void button1_Click(object sender, EventArgs e)
    {
      Bitmap bmp = null;
      sw.Restart();
      for (int i = 0; i < 1000; i++)
      {
         bmp = CaptureImage(390, 420);
      }
      sw.Stop();
      Console.WriteLine(sw.ElapsedMilliseconds);
    }
  }
}

我已经尝试过了,猜猜怎么着...。我打印到控制台的时间大约是3万毫秒。因此很明显,在我的系统上,这些完全相同的代码仍然只能以30ms /帧的速度捕获。他在帖子中说:

  

如果系统相同,则肯定存在严重错误   程序需要35毫秒才能捕获1帧。

这让我感到奇怪,因此我更新了显卡驱动程序。结果相同。然后我以为我的显卡可能已经旧了……好吧,是:AMD Radeon HD 5700系列。由于怀疑VSync,因此我安装了AMD Catalyst Software Suite,禁用了Vsyc并重新启动。再次运行测试,我得到了相同的结果:每1000帧大约3万毫秒。

然后,我在Amazon EC2 g2.2xlarge instance上安装了Windows 10和NVIDIA GRID K520驱动程序(最新)。结果相同:每帧30ms。

有更多经验的人可以向我解释一下这里发生了什么吗?

难道就无法做到作者声称的那样并在95ms内捕获1000帧吗?

我的系统出问题了吗?我不知道还能尝试什么。

1 个答案:

答案 0 :(得分:0)

使用当前技术基本上不可能达到100,000 fps。指向social.msdn.microsoft.com的链接包含有关实现此类结果的声明,但它们在从内存复制到内存与从屏幕复制到内存之间是错误的,或者可能是混淆的。

下面的示例使用核心WinAPI函数。在我的计算机上(使用便宜的板载图形卡),对于100 x 100像素的小区域,它每秒只能实现60帧。这将比所讨论的代码快一点,但是如果清理代码,则可能可以通过.net达到相同的结果。

无论如何,它只会导致计算机增加60 FPS或100 FPS(如果您尝试复制整个屏幕则更少)

请注意,从内存复制到内存的速度要快得多。我确实获得了超过100,000 FPS,但这不是屏幕上的实际帧。它只是从内存复制到内存。如果将区域增加到大于100x100像素,则速度会降低。

使用云计算机进行测试不可靠。我什至不知道将屏幕复制到云计算机上意味着什么。尝试在具有更好图形卡的台式机上进行测试。

class Program
{
    [DllImport("user32.dll")]
    static extern IntPtr GetDC(IntPtr ptr);

    [DllImport("user32.dll")]
    static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDC);

    [DllImport("Gdi32.dll")]
    static extern IntPtr CreateCompatibleDC(IntPtr hDC);

    [DllImport("Gdi32.dll")]
    static extern IntPtr CreateCompatibleBitmap(IntPtr hDC, int w, int h);

    [DllImport("Gdi32.dll")]
    static extern IntPtr BitBlt(IntPtr hDC, int x, int y, int w, int h, IntPtr hSrc, int sx, int sy, int mode);

    [DllImport("Gdi32.dll")]
    static extern IntPtr SelectObject(IntPtr hdc, IntPtr obj);

    [DllImport("Gdi32.dll")]
    static extern int DeleteObject(IntPtr hdc);

    [DllImport("Gdi32.dll")]
    static extern int DeleteDC(IntPtr hdc);

    static void Main(string[] args)
    {
        const int SRCCOPY = 0x00CC0020;
        int w = 100;
        int h = 100;

        //get screen dc:
        IntPtr hdc = GetDC((IntPtr)0);

        //create memory dc and bitmap, select bitmap in to memory:
        IntPtr memdc = CreateCompatibleDC(hdc);
        IntPtr hbitmap = CreateCompatibleBitmap(hdc, w, h);
        IntPtr holdbmp = SelectObject(memdc, hbitmap);

        //create another memory dc and bitmap:
        IntPtr memdc2 = CreateCompatibleDC(hdc);
        IntPtr hbitmap2 = CreateCompatibleBitmap(hdc, w, h);
        IntPtr holdbmp2 = SelectObject(memdc2, hbitmap2);

        Stopwatch sw = new Stopwatch();

        //copy the screen for 1 second
        sw.Restart();
        int fps = 0; //frames per second

        for (fps = 0; sw.ElapsedMilliseconds < 1000; fps++)
            BitBlt(memdc, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
        sw.Stop();
        Console.WriteLine($"screen capture, FPS: {fps}");

        //copy from memory to memory for 1 second
        sw.Restart();
        fps = 0;
        for (fps = 0; sw.ElapsedMilliseconds < 1000; fps++)
            BitBlt(memdc, 0, 0, w, h, memdc2, 0, 0, SRCCOPY);
        sw.Stop();
        Console.WriteLine($"memory to memory copy, FPS: {fps}");

        //cleanup:
        SelectObject(memdc, holdbmp);
        SelectObject(memdc2, holdbmp2);
        DeleteObject(hbitmap);
        DeleteObject(hbitmap2);
        DeleteDC(memdc);
        DeleteDC(memdc2);
        ReleaseDC((IntPtr)0, hdc);
    }
}