Снимок экрана в режиме реального времени в технологии Windows 8/10 без задержки - PullRequest
0 голосов
/ 22 октября 2018

Я собираюсь создать очень быструю и удаленную службу в реальном времени с C # или C ++, не имеет значения, но в настоящее время я использую c #.

хорошо, чтоЯ ищу и спрашиваю способ захвата экрана Windows в режиме реального времени в самой быстрой форме.

Я знаю, что способы уже есть в Интернете, но проблема в задержка ...

  • Я использовал C # copyfromscreen, а задержка составляла 92 мс для 1920x1080 [5 кадров в видео]
  • Я использовал C # copyfromscreenс jpg-кодером и задержкой 36 мс для 1920x1080
  • Я использовал Unity3D screenCapture с jpg-кодером и задержкой 38 мс для 1920x1080
  • Я использовал C # Windows Desktop Duplication API и задержкой 19 мс для 1920x1080 [3 кадра в видео]
  • Я использовал C # Windows Desktop Duplication API с кодировщиком JPG, и задержка составляла 12 мс для 1920x1080 [2 кадра в видео]
  • Я использовал C ++ Windows Desktop Duplication API с jpg-кодировщиком, а задержка составляла 9 мс для 1920x1080 [2 кадра в видео]
  • Я использовал C # Windows DirectXCapture с jpg-кодировщиком и задержкой 16 мс для 1920x1080 [2 кадра в видео]

Я думал, что прогресс в этом случае нормальный, пока я не проверилМиниатюры и предварительный просмотр панели задач Windows 10/8 в реальном времени без задержки в 1 мс, что означает кадр за кадром!

Я пытался изменить размер 1920 x 1080 для всех методов до размера панели задач, но это не помогло.Внесение каких-либо изменений!

Примечание: Эта программа не обрабатывает через Интернет, она будет работать в локальной сети.

Я предполагаю, что задержка из-заобработки растрового изображения или чего-то еще, но я еще не знаю! [Times Added] Вот как я обрабатываю изображение на сервере:

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
}

Кто-нибудь знает, какая технология и методWindows использует для обработки снимка экрана с живыми миниатюрами панели задач?

Примечание: , если этот вопрос не связан с переполнением стека или не по теме, скажите, пожалуйста, какую категорию стека я могу задатьмой вопрос ?

Спасибо

Ответы [ 2 ]

0 голосов
/ 22 октября 2018

API дублирования рабочего стола - самый быстрый.Задержка захвата равна нулю.

Однако после захвата вы загружаете необработанные текстурные данные в системную память для CPU-jpeg-кодировщика.Это то, что занимает время.Живые миниатюры не нуждаются в этом, они масштабируют текстуру окна в графическом процессоре и также визуализируют ее с помощью графического процессора, оба они очень дешевы, когда исходные данные уже находятся в VRAM.

Если вы действительно хотите минимизировать задержкуищите кодировщик JPEG на основе графического процессора, который может получать исходные данные из текстуры B8G8R8A8_UNORM D3D11.JPEG-файлы занимают намного меньше места (и, следовательно, полосы пропускания), чем RGBA, т.е. вы, вероятно, получите закодированный результат раньше.

0 голосов
/ 22 октября 2018

Вероятно, самый быстрый способ сделать это - использовать DirectX.Каждое приложение DirectX содержит буфер или поверхность для хранения содержимого видеопамяти, связанной с этим приложением.Это называется обратным буфером приложения.Некоторые приложения могут иметь более одного заднего буфера.И есть еще один буфер, к которому каждое приложение может получить доступ по умолчанию, - передний буфер.Этот, передний буфер, содержит видеопамять, относящуюся к содержимому рабочего стола, и, по сути, это изображение экрана.Получая доступ к переднему буферу из вашего приложения, вы можете захватить содержимое экрана в этот момент.

Доступ к переднему буферу из вашего приложения довольно прост и понятен.Интерфейс IDirect3DDevice9 предоставляет метод GetFrontBufferData(), который принимает указатель объекта IDirect3DSurface9 и копирует содержимое переднего буфера на эту поверхность.Объект IDirect3DSurfce9 может быть создан с использованием метода IDirect3DDevice8::CreateOffscreenPlainSurface().После захвата экрана на поверхность вы можете использовать функцию D3DXSaveSurfaceToFile(), чтобы сохранить поверхность непосредственно на диск в растровом формате.Таким образом, код для захвата экрана выглядит следующим образом:

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

Если вам нужен буфер с реальными битами, вместо того, чтобы сохранять его непосредственно на диск, вы можете использовать код, подобный этому:

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

Убедитесь, что мы выделили достаточно памяти перед копированием в pBits.Типичное значение для BITSPERPIXEL составляет 32 бит на пиксель.Однако, это может варьироваться в зависимости от текущих настроек монитора.

РЕДАКТИРОВАТЬ

Вот программа на C ++, которую я собрал, чтобы рассчитать время.Функция Direct3D9TakeScreenshots принимает параметр, который определяет, сколько раз сделать снимок экрана.У меня сейчас установлено значение 10, но вы можете изменить его.Работая на моей машине в режиме выпуска, я получаю следующие результаты:

18:33:23.189
18:33:23.579

Так что 390 миллисекунд, чтобы сделать 10 снимков экрана и скопировать их в буфер.Это в среднем составляет около 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...