2015-05-04 17 views
11

so come utilizzare GDI per bloccare lo schermo, tuttavia è molto lento (si coglie appena 10 fps)cattura dello schermo usando DirectX

Ho letto che DirectX offre la migliore velocità. Ma prima di iniziare a imparare DirectX volevo testare un campione per vedere se è davvero così veloce.

Ho trovato questo question che offre un esempio di codice per farlo:

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

ho cercato di includere i file necessari. Tuttavia non tutti possono essere inclusi (ad esempio #include <D3dx9tex.h>).

Qualcuno può fornire un esempio funzionante che includa tutto il necessario o che indichi quali librerie dovrei installare.

Sto utilizzando Visual C++ 2010 Express su Windows 7 Ultimate (x64).


Edit:

Inoltre, questo codice non è completa, ad esempio, che cosa è l'identificatore Device ?!

+0

@yms Non riesco a trovare un collegamento per il download per DirectX SDK per Windows 7! Inoltre, perché sono in grado di includere questa libreria: '#include ', ho una versione precedente di DirectX SDK installata o qualcosa del genere? –

+0

Ho provato diversi metodi di cattura dello schermo incluso DirectX uno, mentre DirectX uno era più veloce di altri non ha fornito un significativo aumento della velocità (per coprire la complessità aggiuntiva e il tempo speso). Scusa se non ricordo alcun dettaglio o numero. –

+0

@Andy T Quanti screenshot sei riuscito a catturare in 1 secondo? –

risposta

13

Ecco alcuni esempi di codice per acquisire lo schermo con DirectX 9. Non è necessario installare alcun SDK (ad eccezione dei file standard forniti con Visual Studio, sebbene non sia stato testato VS 2010).

Basta creare una semplice applicazione console Win32, aggiungere il seguente nel file 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 

Qui è il campione di implementazione principale

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

Che cosa questo farà è di cattura 10 volte sullo schermo e salvare le immagini "cap% i.png" sul disco. Visualizzerà anche il tempo impiegato per questo (il salvataggio delle immagini non viene conteggiato in quel momento, ma solo le schermate). Sul mio (desktop windows 8 - Dell Precision M2800/i7-4810MQ-2.80GHz/Intel HD 4600 che è una macchina piuttosto schifosa ...), richiede 100 acquisizioni 1920x1080 in ~ 4sec, quindi intorno a 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; 
    } 

Nota questo codice collega implicitamente ad WIC (una libreria di immagini incluso in Windows per un po 'di tempo ormai) per salvare i file di immagine (in modo non è necessario il famoso D3DXSaveSurfaceToFile che richiedono vecchi SDK DirectX per essere installato):

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

E alcune macro che ho usato:

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

Nota: per i client Windows 8+, tutti questi (tranne WIC) dovrebbe essere eliminato in favore dello Desktop Duplication API.

+0

Ho provato a cambiare il tuo codice per catturare jpeg.Il risultato non è stato soddisfacente in quanto l'immagine avrà sovrapposizioni di linee verticali e elaborazione lenta.Ad ogni modo per velocizzarlo e migliorare la qualità dell'immagine per favore? –

+0

@FeiHapLee - fai un'altra domanda –

+0

@Simon, postata Grazie. http://stackoverflow.com/questions/33753912/directx-screen-capture-and-output-as-video –

1

Non sono stati specificati i requisiti per le versioni di Windows di destinazione. Se non è necessario supportare Windows 7, Windows 8 include una nuova interfaccia DXGI IDXGIOutputDuplication che consente di creare un oggetto COM che duplica l'output di una scheda video e fornisce l'accesso della CPU alla memoria video tramite IDXGIOutputDuplication::MapDesktopSurface. MSDN ha un bel sample che cattura il desktop attraverso questo e lo disegna all'interno di un modulo e funziona bene e senza intoppi. Quindi, se Windows 7 non è un must, ti suggerirei di dare un'occhiata a questo.

+0

Scusa, ho dimenticato di dirlo. In realtà, voglio anche il supporto per Windows XP :-) –

+0

@ user4592590 è solo la mia opinione soggettiva, ma dubito che sarai in grado di farlo su XP in qualsiasi altro modo rispetto a GDI che è lento :( –

1

È possibile ottenere l'SDK di DirectX da microsoft.com/en-ca/download/details.aspx?id=6812 (pubblicato da @yms). Questo SDK è compatibile con tutte le versioni di Windows, incluso XP. Fare riferimento alla relativa documentazione su come includere/collegare a D3D9.

Nell'esempio Device è un IDirect3DDevice9. Ogni applicazione D3D9 deve creare uno di questi. È molto facile trovare codice di esempio su come crearne uno (ad esempio https://msdn.microsoft.com/en-us/library/windows/desktop/bb204867%28v=vs.85%29.aspx).

Nel codice di esempio, vengono acquisiti solo i contenuti sottoposti a rendering in DirectX, che presumo non sia la vostra intenzione. Per acquisire l'intero schermo (che presumo sia l'obiettivo), invece di utilizzare IDirect3DDevice9::GetRenderTarget, è necessario utilizzare IDirect3DDevice9::GetFrontBufferData, come in questo tutorial (http://dim-i.net/2008/01/29/taking-screenshots-with-directx-and-dev-cpp/). Se stai cercando la velocità, non dovresti ricreare la superficie fuori dallo schermo ogni fotogramma, come nel tuo esempio e in questo tutorial. Il pool di memoria deve essere D3DPOOL_SYSTEMMEM in questo caso, non D3DPOOL_SCRATCH. A seconda delle dimensioni dello schermo, il probabile collo di bottiglia sarà la scrittura delle immagini sul disco.

Si noti inoltre che lo schermo catturato da questo sarà per l'adattatore utilizzato per creare il IDirect3DDevice9. Questo è il primo parametro per IDirect3D9::CreateDevice. Questo è solo un problema se catturare più monitor è una possibilità.