简体   繁体   English

窗口8/10技术中的完全实时屏幕捕获,没有延迟

[英]fully real-time screen capture in window 8/10 technology without delay

I'm gonna create a very fast and real-time remote service with C# or C++ , doesn't matter , but currently I'm using c# . 我将用C#或C ++创建一个非常快速和实时的远程服务 ,无所谓,但目前我正在使用c#。

okay , What I'm looking for and asking for is a way to capture windows screen at real time in fastest shape . 好吧,我正在寻找和要求的是一种以最快的形状实时捕捉Windows屏幕的方法。

I know the ways already is on internet but the problem is the delay ... 我知道互联网已有的方法,但问题是延迟 ......

  • I used C# copyfromscreen and the delay was 92ms for 1920x1080 [ 5 frame in video ] 我使用C# copyfromscreen ,1920x1080的延迟为92ms [视频中的5帧]
  • I used C# copyfromscreen with jpg encoder and delay was 36ms for 1920x1080 我使用带有jpg编码器的C# copyfromscreen ,对于1920x1080,延迟为36ms
  • I used Unity3D screenCapture with jpg encoder and delay was 38ms for 1920x1080 我使用带有jpg编码器的Unity3D screenCapture ,对于1920x1080,延迟为38ms
  • I used C# Windows Desktop Duplication API and delay was 19ms for 1920x1080 [ 3 frame in video ] 我使用C#Windows Desktop Duplication API ,1920x1080的延迟为19ms [视频中的3帧]
  • I used C# Windows Desktop Duplication API with jpg encoder and delay was 12ms for 1920x1080 [ 2 frame in video ] 我使用带有jpg编码器的C#Windows Desktop Duplication API ,1920x1080的延迟为12ms [视频中的2帧]
  • I used C++ Windows Desktop Duplication API with jpg encoder and delay was 9ms for 1920x1080 [ 2 frame in video ] 我使用带有jpg编码器的C ++ Windows Desktop Duplication API ,1920x1080的延迟为9ms [视频中的2帧]
  • I used C# Windows DirectXCapture with jpg encoder and delay was 16ms for 1920x1080 [ 2 frame in video ] 我使用带有jpg编码器的C#Windows DirectXCapture ,1920x1080的延迟为16ms [视频中的2帧]

I thought the progress is normal at this way till I checked out Windows 10/8 Task Bar Live Preview Thumbnails and It's fully-real time with out 1ms delay that means frame to frame ! 我认为这样的进展是正常的,直到我检查了Windows 10/8任务栏实时预览缩略图,这是完全实时的,延迟1ms,这意味着帧到帧!

I tried to resize 1920 x 1080 from all methods to Task Bar Preview size but it didn't made any change ! 我试图将1920 x 1080从所有方法调整为任务栏预览大小,但它没有做任何改变!

Note : This program is not processing over internet it's gonna work on local network. 注意:此程序不通过互联网进行处理,它将在本地网络上运行。

I'm guessing the delay is because of bitmap processing or something but I don't know yet! 我猜测延迟是因为位图处理或其他东西,但我还不知道! [Times Added] Here's the way I process picture on server : [已添加时间]以下是我在服务器上处理图片的方式:

private static byte[] capture()
{
    Bitmap bmp = screenshot_DDAPI(); // Avarage Time : 29.6566ms
    MemoryStream ms = new MemoryStream();
    bmp.Save(ms, ImageFormat.Jpeg); // Avarage Time : 1.7101 ms
    return ms.ToArray(); // Avarage Time : 3.032 ms
}

Anybody knows which technology and method windows uses to process a screenshot to task bar live thumbnails ? 有谁知道窗口使用哪种技术和方法来处理屏幕截图到任务栏实时缩略图?

Note : if this question is not related to stack-overflow or it's off-topic please tell me which stack category I can ask my question ? 注意:如果这个问题与堆栈溢出无关,或者它是偏离主题请告诉我哪个堆栈类别我可以问我的问题?

Thanks 谢谢

Desktop duplication API is the fastest one. 桌面复制API是最快的。 The capture delay is zero. 捕获延迟为零。

However, after the capture, you're downloading raw texture data into system RAM for CPU-based jpeg encoder. 但是,在捕获之后,您将原始纹理数据下载到基于CPU的jpeg编码器的系统RAM中。 This is what taking the time. 这是花时间的。 Live thumbnails don't need to do that, they scale window texture in GPU and render it with GPU, too, both are very cheap when the source data is already in VRAM. 实时缩略图不需要这样做,它们在GPU中缩放窗口纹理并使用GPU渲染它们,当源数据已经在VRAM中时,两者都非常便宜。

If you really want to minimize latency, look for GPU-based JPEG encoder that can take source data from B8G8R8A8_UNORM D3D11 texture. 如果您真的想要最小化延迟,请寻找可以从B8G8R8A8_UNORM D3D11纹理获取源数据的基于GPU的JPEG编码器。 JPEGs take much less space (and therefore bandwidth) than RGBA, ie you'll probably get the encoded result sooner. JPEG比RGBA占用更少的空间(因此带宽),即您可能会更快地获得编码结果。

Probably the fastest way to do this is to use DirectX. 可能最快的方法是使用DirectX。 Every DirectX application contains a buffer, or a surface to hold the contents of the video memory related to that application. 每个DirectX应用程序都包含一个缓冲区或一个表面来保存与该应用程序相关的视频内存的内容。 This is called the back buffer of the application. 这称为应用程序的后台缓冲区。 Some applications might have more than one back buffer. 某些应用程序可能有多个后备缓冲区。 And there is another buffer that every application can access by default - the front buffer. 每个应用程序都可以默认访问另一个缓冲区 - 前端缓冲区。 This one, the front buffer, holds the video memory related to the desktop contents, and so essentially is the screen image. 这个前缓冲区保存与桌面内容相关的视频内存,因此基本上是屏幕图像。 By accessing the front buffer from your application, you can capture the contents of the screen at that moment. 通过从应用程序访问前缓冲区,您可以捕获当时屏幕的内容。

Accessing the front buffer from your application is pretty easy and straightforward. 从应用程序访问前端缓冲区非常简单明了。 The interface IDirect3DDevice9 provides the GetFrontBufferData() method that takes a IDirect3DSurface9 object pointer and copies the contents of the front buffer onto that surface. 接口IDirect3DDevice9提供GetFrontBufferData()方法,该方法接收IDirect3DSurface9对象指针并将前缓冲区的内容复制到该表面上。 The IDirect3DSurfce9 object can be generated by using the method IDirect3DDevice8::CreateOffscreenPlainSurface() . 可以使用方法IDirect3DDevice8::CreateOffscreenPlainSurface()生成IDirect3DSurfce9对象。 Once the screen is captured onto the surface, you can use the function D3DXSaveSurfaceToFile() to save the surface directly to the disk in bitmap format. 将屏幕捕获到曲面后,可以使用D3DXSaveSurfaceToFile()函数将曲面直接以位图格式保存到磁盘。 Thus, the code to capture the screen looks as follows: 因此,捕获屏幕的代码如下所示:

extern IDirect3DDevice9* g_pd3dDevice;
Void CaptureScreen()
{
    IDirect3DSurface9* pSurface;

    // g_pd3dDevice is an IDirect3DDevice9 object, and has been assumed to be properly initialized
    g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
        D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurface, NULL);
    g_pd3dDevice->GetFrontBufferData(0, pSurface);
    D3DXSaveSurfaceToFile("Desktop.bmp",D3DXIFF_BMP,pSurface,NULL,NULL);
    pSurface->Release(); 
}

If you want a buffer to the actual bits, instead of saving it directly to disk, you can use code like this: 如果你想要一个缓冲区到实际位,而不是直接将它保存到磁盘,你可以使用这样的代码:

extern void* pBits;
extern IDirect3DDevice9* g_pd3dDevice;
IDirect3DSurface9* pSurface;

// g_pd3dDevice is an IDirect3DDevice9 object, and has been assumed to be properly initialized
g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
                                          D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, 
                                          &pSurface, NULL);
g_pd3dDevice->GetFrontBufferData(0, pSurface);
D3DLOCKED_RECT lockedRect;
pSurface->LockRect(&lockedRect,NULL,
                   D3DLOCK_NO_DIRTY_UPDATE|
                   D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY)));
for( int i=0 ; i < ScreenHeight ; i++)
{
    memcpy( (BYTE*) pBits + i * ScreenWidth * BITSPERPIXEL / 8 , 
        (BYTE*) lockedRect.pBits + i* lockedRect.Pitch , 
        ScreenWidth * BITSPERPIXEL / 8);
}
g_pSurface->UnlockRect();
pSurface->Release();

Make sure that we have allocated enough memory before copying into pBits . 在复制到pBits之前,请确保我们已经分配了足够的内存。 A typical value for BITSPERPIXEL is 32 bits per pixel. BITSPERPIXEL的典型值是每像素32位。 However, it may vary depending on your current monitor settings. 但是,它可能会因您当前的显示器设置而异。

EDIT 编辑

Here is a C++ program I put together to time it. 这是一个C ++程序,我把它放在一起计时。 The function Direct3D9TakeScreenshots takes a parameter that determines how many times to take a screen shot. Direct3D9TakeScreenshots函数采用一个参数来确定拍摄屏幕截图的次数。 I have it set to 10 right now, but you can change it. 我现在把它设置为10,但你可以改变它。 Running on my machine, in release mode, I get the following results: 在我的机器上运行,在发布模式下,我得到以下结果:

18:33:23.189
18:33:23.579

So that's 390 milliseconds to take 10 screen shots and copy them to a buffer. 所以这需要390毫秒来拍摄10个屏幕截图并将它们复制到缓冲区。 That averages to about 39 milliseconds per screen shot and buffer copy. 每个屏幕截图和缓冲区复制的平均值约为39毫秒。

#include "pch.h"
#include <iostream>

#include <d3d9.h>                 // DirectX 9 header
#pragma comment(lib, "d3d9.lib")  // link to DirectX 9 library

#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)
#define HRCHECK(__expr) {hr=(__expr);if(FAILED(hr)){wprintf(L"FAILURE 0x%08X (%i)\n\tline: %u file: '%s'\n\texpr: '" WIDEN(#__expr) L"'\n",hr, hr, __LINE__,__WFILE__);goto cleanup;}}
#define RELEASE(__p) {if(__p!=nullptr){__p->Release();__p=nullptr;}}

HRESULT Direct3D9TakeScreenshots(UINT adapter, UINT count)
{
    HRESULT hr = S_OK;
    IDirect3D9 *d3d = nullptr;
    IDirect3DDevice9 *device = nullptr;
    IDirect3DSurface9 *surface = nullptr;
    D3DPRESENT_PARAMETERS parameters = { 0 };
    D3DDISPLAYMODE mode;
    D3DLOCKED_RECT rc;
    UINT pitch;
    SYSTEMTIME st;
    LPBYTE *shots = nullptr;

    // init D3D and get screen size
    d3d = Direct3DCreate9(D3D_SDK_VERSION);
    HRCHECK(d3d->GetAdapterDisplayMode(adapter, &mode));

    parameters.Windowed = TRUE;
    parameters.BackBufferCount = 1;
    parameters.BackBufferHeight = mode.Height;
    parameters.BackBufferWidth = mode.Width;
    parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
    parameters.hDeviceWindow = nullptr;

    // create device & capture surface
    HRCHECK(d3d->CreateDevice(adapter, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &parameters, &device));
    HRCHECK(device->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, nullptr));

    // compute the required buffer size
    HRCHECK(surface->LockRect(&rc, NULL, 0));
    pitch = rc.Pitch;
    HRCHECK(surface->UnlockRect());

    // allocate screenshots buffers
    shots = new LPBYTE[count];
    for (UINT i = 0; i < count; i++)
    {
        shots[i] = new BYTE[pitch * mode.Height];
    }

    GetSystemTime(&st); // measure the time we spend doing <count> captures
    wprintf(L"%i:%i:%i.%i\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
    for (UINT i = 0; i < count; i++)
    {
        // get the data
        HRCHECK(device->GetFrontBufferData(0, surface));

        // copy it into our buffers
        HRCHECK(surface->LockRect(&rc, NULL, 0));
        CopyMemory(shots[i], rc.pBits, rc.Pitch * mode.Height);
        HRCHECK(surface->UnlockRect());
    }
    GetSystemTime(&st);
    wprintf(L"%i:%i:%i.%i\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);


cleanup:
    if (shots != nullptr)
    {
        for (UINT i = 0; i < count; i++)
        {
            delete shots[i];
        }
        delete[] shots;
    }
    RELEASE(surface);
    RELEASE(device);
    RELEASE(d3d);
    return hr;
}

int main()
{
    Direct3D9TakeScreenshots(D3DADAPTER_DEFAULT, 10);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM