Подтвердить что ты не робот

Захват экрана с помощью DirectX

Я знаю, как использовать GDI для захвата экрана, однако он очень медленный (он едва фиксирует 10 кадров в секунду)

Я читал, что DirectX предлагает лучшую скорость. Но прежде чем я начну изучать DirectX, я захотел проверить образец, чтобы увидеть, действительно ли это быстро.

Я нашел этот question, который предлагает пример кода для этого:

void dump_buffer()
{
   IDirect3DSurface9* pRenderTarget=NULL;
   IDirect3DSurface9* pDestTarget=NULL;
     const char file[] = "Pickture.bmp";
   // sanity checks.
   if (Device == NULL)
      return;

   // get the render target surface.
   HRESULT hr = Device->GetRenderTarget(0, &pRenderTarget);
   // get the current adapter display mode.
   //hr = pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddisplaymode);

   // create a destination surface.
   hr = Device->CreateOffscreenPlainSurface(DisplayMde.Width,
                         DisplayMde.Height,
                         DisplayMde.Format,
                         D3DPOOL_SYSTEMMEM,
                         &pDestTarget,
                         NULL);
   //copy the render target to the destination surface.
   hr = Device->GetRenderTargetData(pRenderTarget, pDestTarget);
   //save its contents to a bitmap file.
   hr = D3DXSaveSurfaceToFile(file,
                              D3DXIFF_BMP,
                              pDestTarget,
                              NULL,
                              NULL);

   // clean up.
   pRenderTarget->Release();
   pDestTarget->Release();
}

Я попытался включить нужные файлы. Однако не все из них могут быть включены (например, #include <D3dx9tex.h>).

Может ли кто-нибудь предоставить рабочий пример, в котором есть все необходимое, или указать мне, какие библиотеки я должен установить.

Я использую Visual С++ 2010 Express для Windows 7 Ultimate (x64).


Edit:

Кроме того, этот код не является полным, например, что такое идентификатор Device?!

4b9b3361

Ответ 1

Вот пример кода для захвата экрана с помощью DirectX 9. Вам не нужно устанавливать SDK (кроме стандартных файлов, поставляемых с Visual Studio, хотя я не тестировал VS 2010).

Просто создайте простое консольное приложение Win32, добавьте в файл stdafx.h следующее:

  #include <Wincodec.h>             // we use WIC for saving images
  #include <d3d9.h>                 // DirectX 9 header
  #pragma comment(lib, "d3d9.lib")  // link to DirectX 9 library

Вот пример основной реализации

  int _tmain(int argc, _TCHAR* argv[])
  {
    HRESULT hr = Direct3D9TakeScreenshots(D3DADAPTER_DEFAULT, 10);
    return 0;
  }

Что это будет делать, так это захват в 10 раз экрана и сохранение изображений "cap% i.png" на диске. Он также отобразит время, затраченное на это (сохранение изображений не засчитывается за это время, только снимки экрана). На моем (Windows 8 - Dell Precision M2800/i7-4810MQ-2.80GHz/Intel HD 4600, который является довольно дрянной машиной...), он занимает 100 кадров 1920x1080 в ~ 4 сек., Поэтому около 20/25 кадров в секунду.

  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 = NULL;

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

    // save all screenshots
    for (UINT i = 0; i < count; i++)
    {
      WCHAR file[100];
      wsprintf(file, L"cap%i.png", i);
      HRCHECK(SavePixelsToFile32bppPBGRA(mode.Width, mode.Height, pitch, shots[i], file, GUID_ContainerFormatPng));
    }

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

Обратите внимание, что этот код неявно ссылается на WIC (библиотека изображений, включенная в Windows довольно долгое время) для сохранения файлов изображений (так вам не нужен знаменитый D3DXSaveSurfaceToFile, который требует установки старых SDK DirectX):

  HRESULT SavePixelsToFile32bppPBGRA(UINT width, UINT height, UINT stride, LPBYTE pixels, LPWSTR filePath, const GUID &format)
  {
    if (!filePath || !pixels)
      return E_INVALIDARG;

    HRESULT hr = S_OK;
    IWICImagingFactory *factory = nullptr;
    IWICBitmapEncoder *encoder = nullptr;
    IWICBitmapFrameEncode *frame = nullptr;
    IWICStream *stream = nullptr;
    GUID pf = GUID_WICPixelFormat32bppPBGRA;
    BOOL coInit = CoInitialize(nullptr);

    HRCHECK(CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&factory)));
    HRCHECK(factory->CreateStream(&stream));
    HRCHECK(stream->InitializeFromFilename(filePath, GENERIC_WRITE));
    HRCHECK(factory->CreateEncoder(format, nullptr, &encoder));
    HRCHECK(encoder->Initialize(stream, WICBitmapEncoderNoCache));
    HRCHECK(encoder->CreateNewFrame(&frame, nullptr)); // we don't use options here
    HRCHECK(frame->Initialize(nullptr)); // we dont' use any options here
    HRCHECK(frame->SetSize(width, height));
    HRCHECK(frame->SetPixelFormat(&pf));
    HRCHECK(frame->WritePixels(height, stride, stride * height, pixels));
    HRCHECK(frame->Commit());
    HRCHECK(encoder->Commit());

  cleanup:
    RELEASE(stream);
    RELEASE(frame);
    RELEASE(encoder);
    RELEASE(factory);
    if (coInit) CoUninitialize();
    return hr;
  }

И некоторые макросы, которые я использовал:

  #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;}}

Примечание. Для клиентов Windows 8+ все эти (кроме WIC) следует отбросить в пользу Desktop Duplication API.

Ответ 2

Вы не указали требования к целевым версиям Windows. Если вам не нужно поддерживать Windows 7, Windows 8 включает в себя новый новый интерфейс DXGI IDXGIOutputDuplication, который позволяет создать COM-объект, который дублирует вывод видеоадаптер и обеспечивает доступ ЦП к видеопамяти через IDXGIOutputDuplication:: MapDesktopSurface. MSDN имеет довольно приятный sample, который захватывает рабочий стол через это и рисует его внутри формы и работает красиво и плавно. Поэтому, если Windows 7 не обязательно, я бы предложил вам посмотреть на это.

Ответ 3

Вы можете получить DirectX SDK с microsoft.com/en-ca/download/details.aspx?id=6812 (опубликовано @yms). Этот SDK совместим со всеми версиями Windows, включая XP. Обратитесь к его документации о том, как включить/связать с D3D9.

В вашем примере Device есть IDirect3DDevice9. Каждое приложение D3D9 должно создать одно из них. Очень просто найти пример кода о том, как его создать (например, https://msdn.microsoft.com/en-us/library/windows/desktop/bb204867%28v=vs.85%29.aspx).

В вашем примере кода фиксируется только содержимое, отображаемое в DirectX, которое, как я полагаю, не является вашим намерением. Чтобы захватить весь экран (который я предполагаю, это цель), вместо использования IDirect3DDevice9::GetRenderTarget вы должны использовать IDirect3DDevice9::GetFrontBufferData, как в этом уроке (http://dim-i.net/2008/01/29/taking-screenshots-with-directx-and-dev-cpp/). Если вы ищете скорость, вы не должны воссоздавать внешнюю поверхность каждого кадра, как в вашем примере, так и в этом уроке. Пул памяти должен быть D3DPOOL_SYSTEMMEM в этом случае, а не D3DPOOL_SCRATCH. В зависимости от размера экрана вероятное узкое место будет записывать изображения на диск.

Также обратите внимание, что экран, захваченный из этого, будет для адаптера, используемого для создания IDirect3DDevice9. Это первый параметр IDirect3D9::CreateDevice. Это всего лишь проблема, если возможность захвата нескольких мониторов возможна.