2015-05-04 14 views
11

wiem jak używać GDI do przechwytywania ekranu, jednak jest to bardzo powolny (ledwie oddaje 10 fps)przechwytywania ekranu przy użyciu DirectX

Czytałem, że DirectX oferuje najlepszą prędkość. Ale zanim zacznę się uczyć DirectX, chciałem przetestować próbkę, aby zobaczyć, czy jest naprawdę tak szybko.

Znalazłem question który oferuje przykładowy kod, aby to zrobić:

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

Próbowałem to wymagane pliki. Jednak nie wszystkie z nich można uwzględnić (na przykład #include <D3dx9tex.h>).

Czy każdy może podać przykład, który zawiera wszystkie wymagane elementy, lub wskazać, które biblioteki powinienem zainstalować.

Używam Visual C++ 2010 Express na Windows 7 Ultimate (x64).


Edit:

Również ten kod nie jest kompletny, na przykład, co jest identyfikatorem Device ?!

+0

@yms Nie mogę znaleźć łącza pobierania dla DirectX SDK dla Windows 7! Ponadto, dlaczego mogę dołączyć tę bibliotekę: '#include ', czy mam starszą wersję DirectX SDK zainstalowaną, czy coś? –

+0

Próbowałem różnych metod przechwytywania ekranu, w tym DirectX jeden, podczas gdy DirectX był szybszy niż inne, nie zapewniał znacznego przyspieszenia (w celu pokrycia dodatkowej złożoności i czasu spędzonego). Przepraszam, nie pamiętam żadnych szczegółów ani liczb. –

+0

@Andy T Ile zrzutów ekranu można było zrobić w ciągu 1 sekundy? –

Odpowiedz

13

Oto przykładowy kod do przechwytywania ekranu w DirectX 9. Nie powinieneś instalować żadnych SDK (oprócz standardowych plików dostarczanych z Visual Studio, chociaż nie testowałem VS 2010).

Wystarczy stworzyć prostą aplikację konsoli Win32, należy dodać następujące w pliku 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 

Oto przykładowy głównego realizacji

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

Co to zrobi jest przechwytywanie 10 razy ekranie i zapisać obrazy "cap% i.png" na dysku. Wyświetli również czas do tego (zapisywanie obrazów nie jest liczone w tym czasie, tylko zrzuty ekranu). Na moim (komputerowym Windows 8 - Dell Precision M2800/i7-4810MQ-2.80GHz/Intel HD 4600, który jest dość brzydką maszyną ...) maszyna wykonuje 100 przechwytywania 1920x1080 w ~ 4sec, czyli około 20/25 fps.

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

Uwaga Ten kod implicitely linki do WIC (biblioteką obrazowania dołączone do systemu Windows od dłuższego czasu), aby zapisać pliki obrazów (więc nie trzeba się słynny D3DXSaveSurfaceToFile które wymagają starych DirectX SDK do zainstalowania):

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

a niektóre makra użyłem:

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

Uwaga: dla klientów Windows 8+, wszystkie te (z wyjątkiem WIC) powinny zostać usunięte w korzyść z Desktop Duplication API.

+0

Próbowałem zmienić twój kod Aby uchwycić JPEG, wynik nie był zadowalający, ponieważ obraz będzie miał nałożoną pionową linię i powolne przetwarzanie, w każdym razie, aby przyspieszyć i poprawić jakość obrazu, proszę? –

+0

@FeiHapLee - zadaj inne pytanie –

+0

@Simon, wysłana Dziękuję. http://stackoverflow.com/questions/33753912/directx-screen-capture-and-output-as-video –

1

Nie określono wymagań dotyczących docelowych wersji systemu Windows. Jeśli nie potrzebujesz obsługi Windows 7, Windows 8 zawiera ładny nowy interfejs DXGI IDXGIOutputDuplication, który pozwala utworzyć obiekt COM, który powiela wyjście karty wideo i zapewnia dostęp CPU do pamięci wideo poprzez IDXGIOutputDuplication::MapDesktopSurface. MSDN ma całkiem niezłą sample, która przechwytuje pulpit przez to i rysuje go wewnątrz formy i działa ładnie i gładko. Więc jeśli Windows 7 nie jest koniecznością, sugeruję, żebyś na to spojrzał.

+0

Przepraszam, zapomniałem o tym wspomnieć. Właściwie nawet chcę obsługiwać Windows XP :-) –

+0

@ user4592590 to tylko moja subiektywna opinia, ale wątpię, czy będziesz w stanie zrobić to na XP w inny sposób niż GDI, który jest powolny :( –

1

Możesz pobrać DirectX SDK z witryny microsoft.com/en-ca/download/details.aspx?id=6812 (opublikowanej przez @yms). Ten pakiet SDK jest zgodny ze wszystkimi wersjami systemu Windows, w tym XP. Zapoznaj się z jego dokumentacją, jak dołączyć/połączyć się z D3D9.

W twoim przykładzie Device to IDirect3DDevice9. Każda aplikacja D3D9 musi utworzyć jedną z nich. Bardzo łatwo znaleźć przykładowy kod, jak go utworzyć (np. https://msdn.microsoft.com/en-us/library/windows/desktop/bb204867%28v=vs.85%29.aspx).

W twoim przykładowym kodzie przechwytywana jest tylko zawartość renderowana w DirectX, co, jak zakładam, nie jest twoją intencją. Aby przechwycić cały ekran (co zakładam jest celem), zamiast używać IDirect3DDevice9::GetRenderTarget, powinieneś użyć IDirect3DDevice9::GetFrontBufferData, tak jak w tym samouczku (http://dim-i.net/2008/01/29/taking-screenshots-with-directx-and-dev-cpp/). Jeśli szukasz prędkości, nie powinieneś odtwarzać powierzchni poza ekranem w każdej klatce, tak jak w przykładzie i w tym samouczku. Pula pamięci powinna być w tym przypadku D3DPOOL_SYSTEMMEM, a nie D3DPOOL_SCRATCH. W zależności od wielkości ekranu prawdopodobne wąskie gardło będzie zapisywanie obrazów na dysk.

Należy również pamiętać, że zrzut ekranu z tego będzie dotyczyć adaptera użytego do utworzenia IDirect3DDevice9. To jest pierwszy parametr do IDirect3D9::CreateDevice. Stanowi to problem, jeśli możliwe jest przechwytywanie wielu monitorów.

Powiązane problemy