2017-05-07 5 views
0

J'utilise Desktop Duplication à partir de Windows API. Voici le code pour accéder à l'image suivante et obtenir le rectangle des pixels qui ont changé de l'image précédente.Extrait récupère des données de tampon de pixel RVB DirectX

// 
// Get next frame and write it into Data 
// 
_Success_(*Timeout == false && return == DUPL_RETURN_SUCCESS) 
DUPL_RETURN DUPLICATIONMANAGER::GetFrame(_Out_ FRAME_DATA* Data, _Out_ bool* Timeout) 
{ 
IDXGIResource* DesktopResource = nullptr; 
DXGI_OUTDUPL_FRAME_INFO FrameInfo; 

// Get new frame 
HRESULT hr = m_DeskDupl->AcquireNextFrame(10000, &FrameInfo, &DesktopResource); 
if (hr == DXGI_ERROR_WAIT_TIMEOUT) 
{ 
    *Timeout = true; 
    return DUPL_RETURN_SUCCESS; 
} 
*Timeout = false; 

if (FAILED(hr)) 
{ 
    return ProcessFailure(m_Device, L"Failed to acquire next frame in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); 
} 

// If still holding old frame, destroy it 
if (m_AcquiredDesktopImage) 
{ 
    m_AcquiredDesktopImage->Release(); 
    m_AcquiredDesktopImage = nullptr; 
} 

// QI for IDXGIResource 
hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&m_AcquiredDesktopImage)); 
DesktopResource->Release(); 
DesktopResource = nullptr; 
if (FAILED(hr)) 
{ 
    return ProcessFailure(nullptr, L"Failed to QI for ID3D11Texture2D from acquired IDXGIResource in DUPLICATIONMANAGER", L"Error", hr); 
} 

// Get metadata 
if (FrameInfo.TotalMetadataBufferSize) 
{ 
    // Old buffer too small 
    if (FrameInfo.TotalMetadataBufferSize > m_MetaDataSize) 
    { 
     if (m_MetaDataBuffer) 
     { 
      delete [] m_MetaDataBuffer; 
      m_MetaDataBuffer = nullptr; 
     } 
     m_MetaDataBuffer = new (std::nothrow) BYTE[FrameInfo.TotalMetadataBufferSize]; 
     if (!m_MetaDataBuffer) 
     { 
      m_MetaDataSize = 0; 
      Data->MoveCount = 0; 
      Data->DirtyCount = 0; 
      return ProcessFailure(nullptr, L"Failed to allocate memory for metadata in DUPLICATIONMANAGER", L"Error", E_OUTOFMEMORY); 
     } 
     m_MetaDataSize = FrameInfo.TotalMetadataBufferSize; 
    } 

    UINT BufSize = FrameInfo.TotalMetadataBufferSize; 

    // Get move rectangles 
    hr = m_DeskDupl->GetFrameMoveRects(BufSize, reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*>(m_MetaDataBuffer), &BufSize); 
    if (FAILED(hr)) 
    { 
     Data->MoveCount = 0; 
     Data->DirtyCount = 0; 
     return ProcessFailure(nullptr, L"Failed to get frame move rects in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); 
    } 
    Data->MoveCount = BufSize/sizeof(DXGI_OUTDUPL_MOVE_RECT); 

    BYTE* DirtyRects = m_MetaDataBuffer + BufSize; 
    BufSize = FrameInfo.TotalMetadataBufferSize - BufSize; 

    // Get dirty rectangles 
    hr = m_DeskDupl->GetFrameDirtyRects(BufSize, reinterpret_cast<RECT*>(DirtyRects), &BufSize); 
    if (FAILED(hr)) 
    { 
     Data->MoveCount = 0; 
     Data->DirtyCount = 0; 
     return ProcessFailure(nullptr, L"Failed to get frame dirty rects in DUPLICATIONMANAGER", L"Error", hr, FrameInfoExpectedErrors); 
    } 
    Data->DirtyCount = BufSize/sizeof(RECT); 

    Data->MetaData = m_MetaDataBuffer; 
} 

Data->Frame = m_AcquiredDesktopImage; 
Data->FrameInfo = FrameInfo; 

//Here I would like to access pixel data from Data->Frame. A buffer of RGBA pixel 

return DUPL_RETURN_SUCCESS; 
} 

Voici Frame_Data Structure

typedef struct _FRAME_DATA 
{ 
ID3D11Texture2D* Frame; 
DXGI_OUTDUPL_FRAME_INFO FrameInfo; 
_Field_size_bytes_((MoveCount * sizeof(DXGI_OUTDUPL_MOVE_RECT)) + (DirtyCount * sizeof(RECT))) BYTE* MetaData; 
UINT DirtyCount; 
UINT MoveCount; 
} FRAME_DATA; 

Est-il possible d'accéder aux données de la mémoire tampon de pixels qui ont été modifiés de Data->Frame

Voici mon code pour accéder aux données:

BYTE* DISPLAYMANAGER::GetImageData(ID3D11Texture2D* texture2D, D3D11_TEXTURE2D_DESC Desc) 
{ 
if (texture2D != NULL) 
{ 
    D3D11_TEXTURE2D_DESC description; 
    texture2D->GetDesc(&description); 
    description.BindFlags = 0; 
    description.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE; 
    description.Usage = D3D11_USAGE_STAGING; 
    description.Format = DXGI_FORMAT_B8G8R8A8_UNORM; 

    ID3D11Texture2D* texTemp = NULL; 
    HRESULT hr = m_Device->CreateTexture2D(&description, NULL, &texTemp); 
    if (FAILED(hr)) 
    { 
     if (texTemp) 
     { 
      texTemp->Release(); 
      texTemp = NULL; 
     } 
     return NULL; 
    } 
    m_DeviceContext->CopyResource(texTemp, texture2D); 

    D3D11_MAPPED_SUBRESOURCE mapped; 
    unsigned int subresource = D3D11CalcSubresource(0, 0, 0); 
    hr = m_DeviceContext->Map(texTemp, subresource, D3D11_MAP_READ_WRITE, 0, &mapped); 
    if (FAILED(hr)) 
    { 
     texTemp->Release(); 
     texTemp = NULL; 
     return NULL; 
    } 

    unsigned char *captureData = new unsigned char[Desc.Width * Desc.Height * 4]; 
    RtlZeroMemory(captureData, Desc.Width * Desc.Height * 4); 
    const int pitch = mapped.RowPitch; 
    unsigned char *source = static_cast<unsigned char*>(mapped.pData); 
    unsigned char *dest = captureData; 
    for (int i = 0; i < Desc.Height; i++) { 
     memcpy(captureData, source, Desc.Width * 4); 
     source += pitch; 
     captureData += Desc.Width * 4; 
    } 
    for (int i = 0; i < Desc.Width * Desc.Height * 4; i++) { 
     //trace(L"Pixel[%d] = %x\n", i, dest[i]); 
    } 

    m_DeviceContext->Unmap(texTemp, 0); 
    return dest; 
} 
else 
    return NULL; 
} 

Merci pour votre aide!

Répondre

0

Les textures que vous obtenez via l'API de duplication ne sont pas nécessairement accessibles pour la CPU, pour un accès individuel aux pixels. Pour lire les données de texture, vous devrez peut-être créer une texture de mise en page mappable et y copier la texture obtenue. Ensuite, en effectuant le mappage, vous obtiendrez un pointeur vers les données réelles. Notez qu'il ne s'agit généralement pas d'une opération conviviale.

Vous trouverez des informations connexes dans d'autres réponses ainsi:

How to work with pixels using Direct2D:

Pour les moments où vous devez absolument faire la manipulation de pixels du processeur, mais veulent toujours un degré important d'accélération, vous pouvez gérez vos propres textures D3D11 cartographiables. Par exemple, vous pouvez utiliser des textures de transfert si vous souhaitez manipuler de manière asynchrone vos ressources de texture à partir du processeur.

Transferring textures across adapters in DirectX 11:

... copie dans une ressource de mise en scène (créée sur le même appareil) en utilisant ID3D11DeviceContext :: CopyResource. Je cartographie ensuite cette ressource de mise en scène avec Lire ...

+0

Bonjour Roman merci pour votre aide. J'ai édité mon post avec ce que j'ai fait mais j'ai obtenu '0' partout ... J'appelle "GetImageData" après la fonction "Process Frame" de cet exemple https://code.msdn.microsoft.com/windowsdesktop/Desktop-Duplication -Sample-da4c696a/code source? FileId = 42782 & pathId = 1384140008 – DevAndroid

+0

L'extrait de code mis à jour est à peu près correct. Je pensais que CopyResource pourrait échouer pour une raison quelconque, mais il n'est pas clair à partir du code pourquoi il ferait cela. –

+0

Oui ... C'est un problème. Je ne sais pas pourquoi cela échoue .. Même mapped.pData a des valeurs nulles partout. Peut-être devrais-je ajouter un drapeau sur la description? – DevAndroid