2015-05-04 3 views
11

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

Я прочитал, что 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 C++ 2010 Express для Windows 7 Ultimate (x64).


Edit:

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

+0

@yms Я не могу найти ссылку для скачивания для DirectX SDK для Windows 7! Кроме того, почему я могу включить эту библиотеку: '#include ', у меня установлена ​​более старая версия DirectX SDK или что-то еще? –

+0

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

+0

@ Andy T Сколько скриншотов вы смогли захватить за 1 секунду? –

ответ

13

Вот пример кода для захвата экрана с помощью 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» на диске. Он также отобразит время, затраченное на это (сохранение изображений не засчитывается за это время, только снимки экрана). На моем (настольные окна 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, которые требуют старого DirectX SDK, для установки):

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.

+0

Я попытался изменить ваш код для захвата jpeg. Результат не был удовлетворительным, так как изображение будет иметь вертикальные линии и медленную обработку. В любом случае, чтобы ускорить его и улучшить качество изображения, пожалуйста? –

+0

@FeiHapLee - задайте другой вопрос –

+0

@Simon, отправленный. http://stackoverflow.com/questions/33753912/directx-screen-capture-and-output-as-video –

1

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

+0

Извините, я забыл упомянуть об этом. Я даже хочу, чтобы Windows XP поддерживала :-) –

+0

@ user4592590 это только мое субъективное мнение, но я сомневаюсь, что вы сможете сделать это на XP любым другим способом, чем GDI, который медленный :( –

1

Вы можете получить 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. Это всего лишь проблема, если возможность захвата нескольких мониторов возможна.

Смежные вопросы