[host] initial rewrite of DXGI capture

This commit is contained in:
Geoffrey McRae 2017-11-03 22:20:48 +11:00
parent dba6720724
commit f90c258c34
2 changed files with 230 additions and 29 deletions

View File

@ -23,7 +23,11 @@ using namespace Capture;
DXGI::DXGI() : DXGI::DXGI() :
m_initialized(false), m_initialized(false),
m_manager(NULL) m_dxgiFactory(NULL),
m_device(NULL),
m_deviceContext(NULL),
m_dup(NULL),
m_texture(NULL)
{ {
} }
@ -37,19 +41,132 @@ bool DXGI::Initialize()
if (m_initialized) if (m_initialized)
DeInitialize(); DeInitialize();
if (FAILED(CoInitializeEx(NULL, COINIT_MULTITHREADED))) HRESULT status;
status = CreateDXGIFactory1(__uuidof(IDXGIFactory1), (void **)(&m_dxgiFactory));
if (FAILED(status))
{ {
DEBUG_ERROR("CoInitializeEx failed"); DEBUG_ERROR("Failed to create DXGIFactory: %08x", status);
return false; return false;
} }
m_manager = new DXGIManager(); bool done = false;
m_manager->SetCaptureSource(CSDesktop); CComPtr<IDXGIAdapter1> adapter;
for (int i = 0; m_dxgiFactory->EnumAdapters1(i, &adapter) != DXGI_ERROR_NOT_FOUND; i++)
{
CComPtr<IDXGIOutput> output;
for (int i = 0; adapter->EnumOutputs(i, &output) != DXGI_ERROR_NOT_FOUND; i++)
{
DXGI_OUTPUT_DESC outputDesc;
output->GetDesc(&outputDesc);
if (!outputDesc.AttachedToDesktop)
{
output.Release();
continue;
}
RECT rect; m_output = output;
m_manager->GetOutputRect(rect); if (!m_output)
m_width = rect.right - rect.left; {
m_height = rect.bottom - rect.top; output.Release();
adapter.Release();
DEBUG_ERROR("Failed to get IDXGIOutput1");
DeInitialize();
return false;
}
m_width = outputDesc.DesktopCoordinates.right - outputDesc.DesktopCoordinates.left;
m_height = outputDesc.DesktopCoordinates.bottom - outputDesc.DesktopCoordinates.top;
output.Release();
done = true;
break;
}
if (done)
break;
adapter.Release();
}
if (!done)
{
DEBUG_ERROR("Failed to locate a valid output device");
DeInitialize();
return false;
}
static const D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
D3D_FEATURE_LEVEL_9_2,
D3D_FEATURE_LEVEL_9_1
};
status = D3D11CreateDevice(
adapter,
D3D_DRIVER_TYPE_UNKNOWN,
NULL,
D3D11_CREATE_DEVICE_DEBUG,
featureLevels, ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&m_device,
&m_featureLevel,
&m_deviceContext
);
adapter.Release();
if (FAILED(status))
{
DEBUG_ERROR("Failed to create D3D11 device");
DeInitialize();
return false;
}
CComQIPtr<IDXGIDevice1> device1 = m_device;
if (!device1)
{
DEBUG_ERROR("Failed to get IDXGIDevice1");
DeInitialize();
return false;
}
status = m_output->DuplicateOutput(m_device, &m_dup);
if (FAILED(status))
{
DEBUG_ERROR("DuplicateOutput Failed: %08x", status);
DeInitialize();
return false;
}
D3D11_TEXTURE2D_DESC texDesc;
ZeroMemory(&texDesc, sizeof(texDesc));
texDesc.Width = m_width;
texDesc.Height = m_height;
texDesc.MipLevels = 1;
texDesc.ArraySize = 1;
texDesc.SampleDesc.Count = 1;
texDesc.SampleDesc.Quality = 0;
texDesc.Usage = D3D11_USAGE_STAGING;
texDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
texDesc.BindFlags = 0;
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
texDesc.MiscFlags = 0;
status = m_device->CreateTexture2D(&texDesc, NULL, &m_texture);
if (FAILED(status))
{
DEBUG_ERROR("Failed to create texture: %08x", status);
DeInitialize();
return false;
}
if (!m_memcpy.Initialize())
{
DEBUG_ERROR("Failed to initialize memcpy");
return false;
}
m_initialized = true; m_initialized = true;
return true; return true;
@ -57,11 +174,25 @@ bool DXGI::Initialize()
void DXGI::DeInitialize() void DXGI::DeInitialize()
{ {
if (m_manager) m_memcpy.DeInitialize();
{
delete m_manager; if (m_texture)
m_manager = NULL; m_texture.Release();
}
if (m_dup)
m_dup.Release();
if (m_output)
m_output.Release();
if (m_deviceContext)
m_deviceContext.Release();
if (m_device)
m_device.Release();
if (m_dxgiFactory)
m_dxgiFactory.Release();
m_initialized = false; m_initialized = false;
} }
@ -84,7 +215,7 @@ FrameComp DXGI::GetFrameCompression()
size_t DXGI::GetMaxFrameSize() size_t DXGI::GetMaxFrameSize()
{ {
if (!m_initialized); if (!m_initialized)
return 0; return 0;
return m_width * m_height * 4; return m_width * m_height * 4;
@ -92,26 +223,85 @@ size_t DXGI::GetMaxFrameSize()
bool DXGI::GrabFrame(FrameInfo & frame) bool DXGI::GrabFrame(FrameInfo & frame)
{ {
RECT rect; DXGI_OUTDUPL_FRAME_INFO frameInfo;
m_manager->GetOutputRect(rect); CComPtr<IDXGIResource> res;
m_width = rect.right - rect.left;
m_height = rect.bottom - rect.top;
HRESULT result; HRESULT status;
for(int i = 0; i < 2; ++i) for(int i = 0; i < 2; ++i)
{ {
result = m_manager->GetOutputBits((BYTE*)frame.buffer, rect); status = m_dup->AcquireNextFrame(INFINITE, &frameInfo, &res);
if (SUCCEEDED(result)) if (SUCCEEDED(status))
break; break;
// desktop switch, mode change or switch DWM on or off
if (status == DXGI_ERROR_ACCESS_LOST)
{
DeInitialize();
if (!Initialize())
{
DEBUG_ERROR("Failed to re-initialize after access was lost");
return false;
}
continue;
} }
if (FAILED(result)) // unknown failure
DEBUG_INFO("AcquireNextFrame failed: %08x", status);
return false; return false;
}
// retry count exceeded
if (FAILED(status))
{
DEBUG_ERROR("Failed to acquire next frame");
return false;
}
CComQIPtr<ID3D11Texture2D> src = res;
if (!src)
{
DEBUG_ERROR("Failed to get src ID3D11Texture2D");
return false;
}
D3D11_TEXTURE2D_DESC desc;
src->GetDesc(&desc);
m_deviceContext->CopyResource(m_texture, src);
m_dup->ReleaseFrame();
res.Release();
src.Release();
CComQIPtr<IDXGISurface1> surface = m_texture;
if (!surface)
{
DEBUG_ERROR("Failed to get IDXGISurface1");
return false;
}
DXGI_MAPPED_RECT rect;
status = surface->Map(&rect, DXGI_MAP_READ);
if (FAILED(status))
{
DEBUG_ERROR("Failed to map surface: %08x", status);
return false;
}
m_width = desc.Width;
m_height = desc.Height;
frame.width = desc.Width;
frame.height = desc.Height;
frame.stride = rect.Pitch / 4;
frame.outSize = min(frame.bufferSize, m_height * rect.Pitch);
m_memcpy.Copy(frame.buffer, rect.pBits, frame.outSize);
status = surface->Unmap();
if (FAILED(status))
{
DEBUG_ERROR("Failed to unmap surface: %08x", status);
return false;
}
frame.width = m_width;
frame.height = m_height;
frame.stride = m_width;
frame.outSize = m_width * m_height * 4;
return true; return true;
} }

View File

@ -19,10 +19,13 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#pragma once #pragma once
#include "ICapture.h" #include "ICapture.h"
#include <vendor\DXGICapture\DXGIManager.h> #include "MTMemcpy.h"
#define W32_LEAN_AND_MEAN #define W32_LEAN_AND_MEAN
#include <windows.h> #include <windows.h>
#include <dxgi1_2.h>
#include <d3d11.h>
#include <atlbase.h>
namespace Capture namespace Capture
{ {
@ -40,8 +43,16 @@ namespace Capture
private: private:
bool m_initialized; bool m_initialized;
DXGIManager * m_manager; MTMemcpy m_memcpy;
unsigned int m_width; unsigned int m_width;
unsigned int m_height; unsigned int m_height;
CComPtr<IDXGIFactory1> m_dxgiFactory;
CComPtr<ID3D11Device> m_device;
D3D_FEATURE_LEVEL m_featureLevel;
CComPtr<ID3D11DeviceContext> m_deviceContext;
CComQIPtr<IDXGIOutput1> m_output;
CComPtr<IDXGIOutputDuplication> m_dup;
CComPtr<ID3D11Texture2D> m_texture;
}; };
}; };