[host] Added experimental H264 compression to DXGI (disabled by default)

This is not yet working, the client is yet to be updated to support
decompressing this stream.
This commit is contained in:
Geoffrey McRae 2017-12-29 20:53:52 +11:00
parent 3d9230ac93
commit 03622f61b0
9 changed files with 687 additions and 115 deletions

View File

@ -27,7 +27,7 @@ typedef enum FrameType
{
FRAME_TYPE_INVALID ,
FRAME_TYPE_ARGB , // ABGR interleaved: A,R,G,B 32bpp
FRAME_TYPE_RGB , // RGB interleaved : R,G,B 24bpp
FRAME_TYPE_H264 , // H264 compressed
FRAME_TYPE_MAX , // sentinel value
}
FrameType;

View File

@ -59,3 +59,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#else
#define DEBUG_PROTO(fmt, ...) do {} while(0)
#endif
#ifdef _WIN32
#include "Util.h"
#define DEBUG_WINERROR(x, y) Util::DebugWinError(STRIPPATH(__FILE__), __LINE__, __FUNCTION__, x, y)
#endif

View File

@ -23,7 +23,24 @@ using namespace Capture;
#include "common/debug.h"
#include "TraceUtil.h"
#include <mfapi.h>
#include <wmcodecdsp.h>
#include <codecapi.h>
#include <mferror.h>
#include <evr.h>
#include <mfreadwrite.h>
template <class T> void SafeRelease(T **ppT)
{
if (*ppT)
{
(*ppT)->Release();
*ppT = NULL;
}
}
DXGI::DXGI() :
m_cRef(1),
m_options(NULL),
m_initialized(false),
m_dxgiFactory(),
@ -33,7 +50,7 @@ DXGI::DXGI() :
m_texture(),
m_pointer(NULL)
{
MFStartup(MF_VERSION);
}
DXGI::~DXGI()
@ -66,15 +83,15 @@ bool DXGI::Initialize(CaptureOptions * options)
output->GetDesc(&outputDesc);
if (!outputDesc.AttachedToDesktop)
{
output.Release();
SafeRelease(&output);
continue;
}
m_output = output;
if (!m_output)
{
output.Release();
adapter.Release();
SafeRelease(&output);
SafeRelease(&adapter);
DEBUG_ERROR("Failed to get IDXGIOutput1");
DeInitialize();
return false;
@ -83,7 +100,7 @@ bool DXGI::Initialize(CaptureOptions * options)
m_width = outputDesc.DesktopCoordinates.right - outputDesc.DesktopCoordinates.left;
m_height = outputDesc.DesktopCoordinates.bottom - outputDesc.DesktopCoordinates.top;
output.Release();
SafeRelease(&output);
done = true;
break;
}
@ -91,7 +108,7 @@ bool DXGI::Initialize(CaptureOptions * options)
if (done)
break;
adapter.Release();
SafeRelease(&adapter);
}
if (!done)
@ -102,6 +119,8 @@ bool DXGI::Initialize(CaptureOptions * options)
}
static const D3D_FEATURE_LEVEL featureLevels[] = {
D3D_FEATURE_LEVEL_11_1,
D3D_FEATURE_LEVEL_11_0,
D3D_FEATURE_LEVEL_10_1,
D3D_FEATURE_LEVEL_10_0,
D3D_FEATURE_LEVEL_9_3,
@ -119,29 +138,54 @@ bool DXGI::Initialize(CaptureOptions * options)
adapter,
D3D_DRIVER_TYPE_UNKNOWN,
NULL,
CREATE_FLAGS,
CREATE_FLAGS | D3D11_CREATE_DEVICE_VIDEO_SUPPORT,
featureLevels, ARRAYSIZE(featureLevels),
D3D11_SDK_VERSION,
&m_device,
&m_featureLevel,
&m_deviceContext
);
#undef CREATE_FLAGS
adapter.Release();
SafeRelease(&adapter);
#undef CREATE_FLAGS
if (FAILED(status))
{
DEBUG_ERROR("Failed to create D3D11 device");
DEBUG_WINERROR("Failed to create D3D11 device", status);
DeInitialize();
return false;
}
IDXGIDevicePtr dxgi;
if (FAILED(m_device->QueryInterface(__uuidof(IDXGIDevice), (void **)&dxgi)))
bool h264 = false;
for(CaptureOptions::const_iterator it = m_options->cbegin(); it != m_options->cend(); ++it)
{
DEBUG_ERROR("Failed to obtain the IDXGIDevice interface from the D3D11 device");
if (_stricmp(*it, "h264") == 0) h264 = true;
}
if (h264)
{
DEBUG_WARN("Enabling experimental H.264 compression");
m_frameType = FRAME_TYPE_H264;
if (!InitH264Capture())
{
DeInitialize();
return false;
}
}
else
{
m_frameType = FRAME_TYPE_ARGB;
if (!InitRawCapture())
{
DeInitialize();
return false;
}
}
IDXGIDevicePtr dxgi;
status = m_device->QueryInterface(__uuidof(IDXGIDevice), (void **)&dxgi);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to obtain the IDXGIDevice interface from the D3D11 device", status);
DeInitialize();
return false;
}
@ -160,11 +204,17 @@ bool DXGI::Initialize(CaptureOptions * options)
if (FAILED(status))
{
DEBUG_ERROR("DuplicateOutput Failed: %08x", (int)status);
DEBUG_WINERROR("DuplicateOutput Failed", status);
DeInitialize();
return false;
}
m_initialized = true;
return true;
}
bool DXGI::InitRawCapture()
{
D3D11_TEXTURE2D_DESC texDesc;
ZeroMemory(&texDesc, sizeof(texDesc));
texDesc.Width = m_width;
@ -179,15 +229,180 @@ bool DXGI::Initialize(CaptureOptions * options)
texDesc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
texDesc.MiscFlags = 0;
status = m_device->CreateTexture2D(&texDesc, NULL, &m_texture);
HRESULT status = m_device->CreateTexture2D(&texDesc, NULL, &m_texture);
if (FAILED(status))
{
DEBUG_ERROR("Failed to create texture: %08x", (int)status);
DeInitialize();
DEBUG_WINERROR("Failed to create texture", status);
return false;
}
m_initialized = true;
return true;
}
bool DXGI::InitH264Capture()
{
HRESULT status;
MFT_REGISTER_TYPE_INFO typeInfo;
IMFActivate **activationPointers;
UINT32 activationPointerCount;
ID3D10MultithreadPtr mt(m_device);
mt->SetMultithreadProtected(TRUE);
m_encodeEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
InitializeCriticalSection(&m_encodeCS);
typeInfo.guidMajorType = MFMediaType_Video;
typeInfo.guidSubtype = MFVideoFormat_H264;
status = MFTEnumEx(
MFT_CATEGORY_VIDEO_ENCODER,
MFT_ENUM_FLAG_HARDWARE,
NULL,
&typeInfo,
&activationPointers,
&activationPointerCount
);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to enumerate encoder MFTs", status);
return false;
}
if (activationPointerCount == 0)
{
DEBUG_WINERROR("Hardware H264 MFT not available", status);
return false;
}
{
UINT32 nameLen = 0;
activationPointers[0]->GetStringLength(MFT_FRIENDLY_NAME_Attribute, &nameLen);
wchar_t * name = new wchar_t[nameLen+1];
activationPointers[0]->GetString(MFT_FRIENDLY_NAME_Attribute, name, nameLen + 1, NULL);
DEBUG_INFO("Using Encoder: %S", name);
delete[] name;
}
status = activationPointers[0]->ActivateObject(IID_PPV_ARGS(&m_mfTransform));
if (FAILED(status))
{
CoTaskMemFree(activationPointers);
DEBUG_WINERROR("Failed to create H264 encoder MFT", status);
return false;
}
CoTaskMemFree(activationPointers);
IMFAttributesPtr attribs;
m_mfTransform->GetAttributes(&attribs);
attribs->SetUINT32 (MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS , TRUE);
attribs->SetUINT32 (MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING , TRUE);
attribs->SetUINT32 (MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING, TRUE);
attribs->SetUINT32 (MF_LOW_LATENCY , TRUE);
UINT32 d3d11Aware = 0;
UINT32 async = 0;
attribs->GetUINT32(MF_TRANSFORM_ASYNC, &async);
attribs->GetUINT32(MF_SA_D3D11_AWARE, &d3d11Aware);
if (async)
attribs->SetUINT32(MF_TRANSFORM_ASYNC_UNLOCK, TRUE);
SafeRelease(&attribs);
status = m_mfTransform.QueryInterface(IID_PPV_ARGS(&m_mediaEventGen));
if (FAILED(status))
{
DEBUG_WINERROR("Failed to obtain th emedia event generator interface", status);
return false;
}
status = m_mediaEventGen->BeginGetEvent(this, NULL);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to set the begin get event", status);
return false;
}
if (d3d11Aware)
{
MFCreateDXGIDeviceManager(&m_resetToken, &m_mfDeviceManager);
status = m_mfDeviceManager->ResetDevice(m_device, m_resetToken);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to call reset device", status);
return false;
}
status = m_mfTransform->ProcessMessage(MFT_MESSAGE_SET_D3D_MANAGER, ULONG_PTR(m_mfDeviceManager.GetInterfacePtr()));
if (FAILED(status))
{
DEBUG_WINERROR("Failed to set the D3D manager", status);
return false;
}
}
IMFMediaTypePtr outType;
MFCreateMediaType(&outType);
outType->SetGUID (MF_MT_MAJOR_TYPE , MFMediaType_Video);
outType->SetGUID (MF_MT_SUBTYPE , MFVideoFormat_H264);
outType->SetUINT32(MF_MT_AVG_BITRATE , 240000);
outType->SetUINT32(MF_MT_INTERLACE_MODE , MFVideoInterlace_Progressive);
outType->SetUINT32(MF_MT_MPEG2_PROFILE , eAVEncH264VProfile_444);
outType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
MFSetAttributeSize (outType, MF_MT_FRAME_SIZE , m_width, m_height);
MFSetAttributeRatio(outType, MF_MT_FRAME_RATE , 30, 1);
MFSetAttributeRatio(outType, MF_MT_PIXEL_ASPECT_RATIO, 1, 1);
status = m_mfTransform->SetOutputType(0, outType, 0);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to set the output media type on the H264 encoder MFT", status);
return false;
}
IMFMediaTypePtr inType;
MFCreateMediaType(&inType);
inType->SetGUID (MF_MT_MAJOR_TYPE , MFMediaType_Video );
inType->SetGUID (MF_MT_SUBTYPE , MFVideoFormat_NV12);
inType->SetUINT32(MF_MT_INTERLACE_MODE , MFVideoInterlace_Progressive);
inType->SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, TRUE);
MFSetAttributeSize (inType, MF_MT_FRAME_SIZE , m_width, m_height);
MFSetAttributeRatio(inType, MF_MT_FRAME_RATE , 30, 1);
MFSetAttributeRatio(inType, MF_MT_PIXEL_ASPECT_RATIO, 1 , 1);
status = m_mfTransform->SetInputType(0, inType, 0);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to set the input media type on the H264 encoder MFT", status);
return false;
}
m_mfTransform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH , NULL);
m_mfTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_BEGIN_STREAMING, NULL);
m_mfTransform->ProcessMessage(MFT_MESSAGE_NOTIFY_START_OF_STREAM, NULL);
#if 0
status = MFTRegisterLocalByCLSID(
__uuidof(CColorConvertDMO),
MFT_CATEGORY_VIDEO_PROCESSOR,
L"",
MFT_ENUM_FLAG_SYNCMFT,
0,
NULL,
0,
NULL
);
if (FAILED(status))
{
DEBUG_ERROR("Failed to register color converter DSP");
return false;
}
#endif
return true;
}
@ -212,23 +427,22 @@ void DXGI::DeInitialize()
m_surfaceMapped = false;
}
if (m_texture)
m_texture.Release();
SafeRelease(&m_texture);
SafeRelease(&m_dup);
SafeRelease(&m_output);
SafeRelease(&m_deviceContext);
SafeRelease(&m_device);
SafeRelease(&m_dxgiFactory);
SafeRelease(&m_mfTransform);
SafeRelease(&m_mfDeviceManager);
SafeRelease(&m_mediaEventGen);
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();
if (m_encodeEvent)
{
CloseHandle(m_encodeEvent);
m_encodeEvent = NULL;
DeleteCriticalSection(&m_encodeCS);
}
m_initialized = false;
}
@ -237,8 +451,7 @@ FrameType DXGI::GetFrameType()
{
if (!m_initialized)
return FRAME_TYPE_INVALID;
return FRAME_TYPE_ARGB;
return m_frameType;
}
size_t DXGI::GetMaxFrameSize()
@ -249,6 +462,89 @@ size_t DXGI::GetMaxFrameSize()
return (m_width * m_height * 4);
}
STDMETHODIMP Capture::DXGI::Invoke(IMFAsyncResult * pAsyncResult)
{
HRESULT status, evtStatus;
MediaEventType meType = MEUnknown;
IMFMediaEvent *pEvent = NULL;
status = m_mediaEventGen->EndGetEvent(pAsyncResult, &pEvent);
if (FAILED(status))
{
DEBUG_WINERROR("EndGetEvent", status);
return status;
}
status = pEvent->GetStatus(&evtStatus);
if (FAILED(status))
{
SafeRelease(&pEvent);
DEBUG_WINERROR("GetStatus", status);
return status;
}
if (FAILED(evtStatus))
{
SafeRelease(&pEvent);
DEBUG_WINERROR("evtStatus", evtStatus);
return evtStatus;
}
status = pEvent->GetType(&meType);
if (FAILED(status))
{
SafeRelease(&pEvent);
DEBUG_WINERROR("GetType", status);
return status;
}
SafeRelease(&pEvent);
switch (meType)
{
case METransformNeedInput:
EnterCriticalSection(&m_encodeCS);
m_encodeNeedsData = true;
SetEvent(m_encodeEvent);
LeaveCriticalSection(&m_encodeCS);
break;
case METransformHaveOutput:
EnterCriticalSection(&m_encodeCS);
m_encodeHasData = true;
SetEvent(m_encodeEvent);
LeaveCriticalSection(&m_encodeCS);
break;
case METransformDrainComplete:
{
status = m_mfTransform->ProcessMessage(MFT_MESSAGE_COMMAND_FLUSH, 0);
if (FAILED(status))
{
DEBUG_WINERROR("MFT_MESSAGE_COMMAND_FLUSH", status);
return status;
}
break;
}
case MEError:
DEBUG_INFO("err");
break;
default:
DEBUG_INFO("unk");
break;
}
status = m_mediaEventGen->BeginGetEvent(this, NULL);
if (FAILED(status))
{
DEBUG_WINERROR("BeginGetEvent", status);
return status;
}
return status;
}
void DXGI::WaitForDesktop()
{
HDESK desktop;
@ -263,7 +559,7 @@ void DXGI::WaitForDesktop()
CloseDesktop(desktop);
}
GrabStatus DXGI::GrabFrame(FrameInfo & frame)
GrabStatus Capture::DXGI::GrabFrameTexture(FrameInfo & frame, ID3D11Texture2DPtr & texture)
{
if (!m_initialized)
return GRAB_STATUS_ERROR;
@ -273,9 +569,9 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
HRESULT status;
bool cursorUpdate = false;
for(int i = 0; i < 2; ++i)
for (int i = 0; i < 2; ++i)
{
while(true)
while (true)
{
if (m_releaseFrame)
{
@ -284,16 +580,16 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
switch (status)
{
case S_OK:
break;
case S_OK:
break;
case DXGI_ERROR_INVALID_CALL:
DEBUG_ERROR("Frame was already released");
return GRAB_STATUS_ERROR;
case DXGI_ERROR_INVALID_CALL:
DEBUG_ERROR("Frame was already released");
return GRAB_STATUS_ERROR;
case DXGI_ERROR_ACCESS_LOST:
WaitForDesktop();
return GRAB_STATUS_REINIT;
case DXGI_ERROR_ACCESS_LOST:
WaitForDesktop();
return GRAB_STATUS_REINIT;
}
}
@ -305,9 +601,9 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
m_memcpy.Wake();
// send the last frame again if we timeout to prevent the client stalling on restart
frame.width = m_width;
frame.width = m_width;
frame.height = m_height;
frame.pitch = m_mapping.RowPitch;
frame.pitch = m_mapping.RowPitch;
frame.stride = m_mapping.RowPitch / 4;
unsigned int size = m_height * m_mapping.RowPitch;
@ -326,18 +622,18 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
if (
m_lastMousePos.x != frameInfo.PointerPosition.Position.x ||
m_lastMousePos.y != frameInfo.PointerPosition.Position.y
) {
cursorUpdate = true;
frame.cursor.hasPos = true;
frame.cursor.x = frameInfo.PointerPosition.Position.x;
frame.cursor.y = frameInfo.PointerPosition.Position.y;
m_lastMousePos.x = frameInfo.PointerPosition.Position.x;
m_lastMousePos.y = frameInfo.PointerPosition.Position.y;
) {
cursorUpdate = true;
frame.cursor.hasPos = true;
frame.cursor.x = frameInfo.PointerPosition.Position.x;
frame.cursor.y = frameInfo.PointerPosition.Position.y;
m_lastMousePos.x = frameInfo.PointerPosition.Position.x;
m_lastMousePos.y = frameInfo.PointerPosition.Position.y;
}
if (m_lastMouseVis != frameInfo.PointerPosition.Visible)
{
cursorUpdate = true;
cursorUpdate = true;
m_lastMouseVis = frameInfo.PointerPosition.Visible;
}
@ -352,7 +648,7 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
{
if (m_pointer)
delete[] m_pointer;
m_pointer = new BYTE[frameInfo.PointerShapeBufferSize];
m_pointer = new BYTE[frameInfo.PointerShapeBufferSize];
m_pointerBufSize = frameInfo.PointerShapeBufferSize;
}
@ -360,32 +656,32 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
status = m_dup->GetFramePointerShape(m_pointerBufSize, m_pointer, &m_pointerSize, &shapeInfo);
if (!SUCCEEDED(status))
{
DEBUG_ERROR("Failed to get the new pointer shape: %08x", (int)status);
DEBUG_WINERROR("Failed to get the new pointer shape", status);
return GRAB_STATUS_ERROR;
}
switch (shapeInfo.Type)
{
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR : frame.cursor.type = CURSOR_TYPE_COLOR; break;
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: frame.cursor.type = CURSOR_TYPE_MASKED_COLOR; break;
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME : frame.cursor.type = CURSOR_TYPE_MONOCHROME; break;
default:
DEBUG_ERROR("Invalid cursor type");
return GRAB_STATUS_ERROR;
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR: frame.cursor.type = CURSOR_TYPE_COLOR; break;
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR: frame.cursor.type = CURSOR_TYPE_MASKED_COLOR; break;
case DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME: frame.cursor.type = CURSOR_TYPE_MONOCHROME; break;
default:
DEBUG_ERROR("Invalid cursor type");
return GRAB_STATUS_ERROR;
}
frame.cursor.hasShape = true;
frame.cursor.shape = m_pointer;
frame.cursor.w = shapeInfo.Width;
frame.cursor.h = shapeInfo.Height;
frame.cursor.pitch = shapeInfo.Pitch;
frame.cursor.shape = m_pointer;
frame.cursor.w = shapeInfo.Width;
frame.cursor.h = shapeInfo.Height;
frame.cursor.pitch = shapeInfo.Pitch;
frame.cursor.dataSize = m_pointerSize;
}
if (frameInfo.LastPresentTime.QuadPart != 0)
break;
res.Release();
SafeRelease(&res);
if (cursorUpdate)
return GRAB_STATUS_CURSOR;
@ -397,35 +693,47 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
switch (status)
{
// desktop switch, mode change, switch DWM on or off or Secure Desktop
case DXGI_ERROR_ACCESS_LOST:
case WAIT_ABANDONED:
WaitForDesktop();
return GRAB_STATUS_REINIT;
case DXGI_ERROR_ACCESS_LOST:
case WAIT_ABANDONED:
WaitForDesktop();
return GRAB_STATUS_REINIT;
default:
// unknown failure
DEBUG_INFO("AcquireNextFrame failed: %08x", (int)status);
return GRAB_STATUS_ERROR;
default:
// unknown failure
DEBUG_WINERROR("AcquireNextFrame failed", status);
return GRAB_STATUS_ERROR;
}
}
// retry count exceeded
if (FAILED(status))
{
DEBUG_ERROR("Failed to acquire next frame");
DEBUG_WINERROR("Failed to acquire next frame", status);
return GRAB_STATUS_ERROR;
}
ID3D11Texture2DPtr src(res);
res.Release();
if (!src)
res.QueryInterface(IID_PPV_ARGS(&texture));
SafeRelease(&res);
if (!texture)
{
DEBUG_ERROR("Failed to get src ID3D11Texture2D");
return GRAB_STATUS_ERROR;
}
return GRAB_STATUS_OK;
}
GrabStatus Capture::DXGI::GrabFrameRaw(FrameInfo & frame)
{
GrabStatus result;
ID3D11Texture2DPtr src;
result = GrabFrameTexture(frame, src);
if (result != GRAB_STATUS_OK)
return result;
m_deviceContext->CopyResource(m_texture, src);
src.Release();
SafeRelease(&src);
if (m_surfaceMapped)
{
@ -433,10 +741,11 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
m_surfaceMapped = false;
}
HRESULT status;
status = m_deviceContext->Map(m_texture, 0, D3D11_MAP_READ, 0, &m_mapping);
if (FAILED(status))
{
DEBUG_ERROR("Failed to map the texture: %08x", (int)status);
DEBUG_WINERROR("Failed to map the texture", status);
DeInitialize();
return GRAB_STATUS_ERROR;
}
@ -446,10 +755,8 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
// wake up the copy threads
m_memcpy.Wake();
frame.width = m_width;
frame.height = m_height;
frame.pitch = m_mapping.RowPitch;
frame.stride = m_mapping.RowPitch >> 2;
frame.pitch = m_mapping.RowPitch;
frame.stride = m_mapping.RowPitch >> 2;
const unsigned int size = m_height * m_mapping.RowPitch;
m_memcpy.Copy(frame.buffer, m_mapping.pData, size < frame.bufferSize ? size : frame.bufferSize);
@ -457,3 +764,144 @@ GrabStatus DXGI::GrabFrame(FrameInfo & frame)
return GRAB_STATUS_OK;
}
GrabStatus Capture::DXGI::GrabFrameH264(FrameInfo & frame)
{
while(true)
{
// only reset the event if there isn't work pending
EnterCriticalSection(&m_encodeCS);
if (!m_encodeHasData && !m_encodeNeedsData)
ResetEvent(m_encodeEvent);
LeaveCriticalSection(&m_encodeCS);
switch (WaitForSingleObject(m_encodeEvent, 1000))
{
case WAIT_FAILED:
DEBUG_WINERROR("Wait for encode event failed", GetLastError());
return GRAB_STATUS_ERROR;
case WAIT_ABANDONED:
DEBUG_ERROR("Wait abandoned");
return GRAB_STATUS_ERROR;
case WAIT_TIMEOUT:
continue;
case WAIT_OBJECT_0:
break;
}
EnterCriticalSection(&m_encodeCS);
HRESULT status;
if (m_encodeNeedsData)
{
LeaveCriticalSection(&m_encodeCS);
GrabStatus result;
ID3D11Texture2DPtr src;
result = GrabFrameTexture(frame, src);
if (result != GRAB_STATUS_OK)
{
return result;
}
// cursor data may be returned, only turn off the flag if we have a frame
EnterCriticalSection(&m_encodeCS);
m_encodeNeedsData = false;
LeaveCriticalSection(&m_encodeCS);
IMFMediaBufferPtr buffer;
status = MFCreateDXGISurfaceBuffer(__uuidof(ID3D11Texture2D), src, 0, FALSE, &buffer);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to create DXGI surface buffer from texture", status);
return GRAB_STATUS_ERROR;
}
IMF2DBufferPtr imfBuffer(buffer);
DWORD length;
imfBuffer->GetContiguousLength(&length);
buffer->SetCurrentLength(length);
SafeRelease(&imfBuffer);
IMFSamplePtr sample;
MFCreateSample(&sample);
sample->AddBuffer(buffer);
status = m_mfTransform->ProcessInput(0, sample, 0);
if (FAILED(status))
{
DEBUG_WINERROR("Failed to process the input", status);
return GRAB_STATUS_ERROR;
}
SafeRelease(&src );
SafeRelease(&sample);
SafeRelease(&buffer);
EnterCriticalSection(&m_encodeCS);
}
if (m_encodeHasData)
{
m_encodeHasData = false;
LeaveCriticalSection(&m_encodeCS);
// wake up the copy threads
TRACE_START("copy");
m_memcpy.Wake();
MFT_OUTPUT_STREAM_INFO streamInfo;
status = m_mfTransform->GetOutputStreamInfo(0, &streamInfo);
if (FAILED(status))
{
DEBUG_WINERROR("GetOutputStreamInfo", status);
return GRAB_STATUS_ERROR;
}
DWORD outStatus;
MFT_OUTPUT_DATA_BUFFER outDataBuffer;
outDataBuffer.dwStreamID = 0;
outDataBuffer.dwStatus = 0;
outDataBuffer.pEvents = NULL;
outDataBuffer.pSample = NULL;
status = m_mfTransform->ProcessOutput(0, 1, &outDataBuffer, &outStatus);
if (FAILED(status))
{
DEBUG_WINERROR("ProcessOutput", status);
return GRAB_STATUS_ERROR;
}
IMFMediaBufferPtr buffer;
MFCreateAlignedMemoryBuffer((DWORD)frame.bufferSize, MF_128_BYTE_ALIGNMENT, &buffer);
outDataBuffer.pSample->CopyToBuffer(buffer);
SafeRelease(&outDataBuffer.pEvents);
SafeRelease(&outDataBuffer.pSample);
BYTE *pixels;
DWORD maxLen, curLen;
buffer->Lock(&pixels, &maxLen, &curLen);
m_memcpy.Copy(frame.buffer, pixels, curLen);
buffer->Unlock();
SafeRelease(&buffer);
TRACE_END;
return GRAB_STATUS_OK;
}
LeaveCriticalSection(&m_encodeCS);
}
}
GrabStatus DXGI::GrabFrame(FrameInfo & frame)
{
frame.width = m_width;
frame.height = m_height;
if (m_frameType == FRAME_TYPE_H264)
return GrabFrameH264(frame);
else
return GrabFrameRaw(frame);
}

View File

@ -24,14 +24,17 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#define W32_LEAN_AND_MEAN
#include <windows.h>
#include <shlwapi.h>
#include <dxgi1_2.h>
#include <d3d11.h>
#include <mftransform.h>
#include <stdio.h>
#include <comdef.h>
_COM_SMARTPTR_TYPEDEF(IDXGIFactory1 , __uuidof(IDXGIFactory1 ));
_COM_SMARTPTR_TYPEDEF(ID3D11Device , __uuidof(ID3D11Device ));
_COM_SMARTPTR_TYPEDEF(ID3D11DeviceContext , __uuidof(ID3D11DeviceContext ));
_COM_SMARTPTR_TYPEDEF(ID3D10Multithread , __uuidof(ID3D10Multithread ));
_COM_SMARTPTR_TYPEDEF(IDXGIDevice , __uuidof(IDXGIDevice ));
_COM_SMARTPTR_TYPEDEF(IDXGIOutput1 , __uuidof(IDXGIOutput1 ));
_COM_SMARTPTR_TYPEDEF(IDXGIOutput , __uuidof(IDXGIOutput ));
@ -40,17 +43,28 @@ _COM_SMARTPTR_TYPEDEF(IDXGIOutputDuplication, __uuidof(IDXGIOutputDuplication));
_COM_SMARTPTR_TYPEDEF(ID3D11Texture2D , __uuidof(ID3D11Texture2D ));
_COM_SMARTPTR_TYPEDEF(IDXGIResource , __uuidof(IDXGIResource ));
_COM_SMARTPTR_TYPEDEF(IMFActivate , __uuidof(IMFActivate ));
_COM_SMARTPTR_TYPEDEF(IMFAttributes , __uuidof(IMFAttributes ));
_COM_SMARTPTR_TYPEDEF(IMFDXGIDeviceManager , __uuidof(IMFDXGIDeviceManager ));
_COM_SMARTPTR_TYPEDEF(IMFTransform , __uuidof(IMFTransform ));
_COM_SMARTPTR_TYPEDEF(IMFMediaEventGenerator, __uuidof(IMFMediaEventGenerator));
_COM_SMARTPTR_TYPEDEF(IMFMediaType , __uuidof(IMFMediaType ));
_COM_SMARTPTR_TYPEDEF(IMFSample , __uuidof(IMFSample ));
_COM_SMARTPTR_TYPEDEF(IMFMediaBuffer , __uuidof(IMFMediaBuffer ));
_COM_SMARTPTR_TYPEDEF(IMF2DBuffer , __uuidof(IMF2DBuffer ));
namespace Capture
{
class DXGI : public ICapture
class DXGI : public ICapture, public IMFAsyncCallback
{
public:
DXGI();
~DXGI();
virtual ~DXGI();
const char * GetName() { return "DXGI"; }
bool Initialize(CaptureOptions * options);
void DeInitialize();
bool ReInitialize()
{
@ -67,14 +81,54 @@ namespace Capture
size_t GetMaxFrameSize();
enum GrabStatus GrabFrame(struct FrameInfo & frame);
/*
Junk needed for the horrid IMFAsyncCallback interface
*/
STDMETHODIMP QueryInterface(REFIID riid, void ** ppv)
{
static const QITAB qit[] =
{
QITABENT(DXGI, IMFAsyncCallback),
{ NULL }
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) AddRef()
{
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) Release()
{
long cRef = InterlockedDecrement(&m_cRef);
if (!cRef)
delete this;
return cRef;
}
STDMETHODIMP GetParameters(DWORD *pdwFlags, DWORD *pdwQueue) { return E_NOTIMPL; }
STDMETHODIMP Invoke(IMFAsyncResult *pAsyncResult);
private:
bool InitRawCapture();
bool InitH264Capture();
GrabStatus DXGI::GrabFrameTexture(FrameInfo & frame, ID3D11Texture2DPtr & texture);
GrabStatus DXGI::GrabFrameRaw (FrameInfo & frame);
GrabStatus DXGI::GrabFrameH264 (FrameInfo & frame);
void WaitForDesktop();
long m_cRef;
CaptureOptions * m_options;
bool m_initialized;
unsigned int m_width;
unsigned int m_height;
bool m_initialized;
unsigned int m_width;
unsigned int m_height;
enum FrameType m_frameType;
MultiMemcpy m_memcpy;
IDXGIFactory1Ptr m_dxgiFactory;
@ -87,6 +141,16 @@ namespace Capture
ID3D11Texture2DPtr m_texture;
D3D11_MAPPED_SUBRESOURCE m_mapping;
bool m_surfaceMapped;
HANDLE m_encodeEvent;
bool m_encodeNeedsData, m_encodeHasData;
CRITICAL_SECTION m_encodeCS;
UINT m_resetToken;
IMFDXGIDeviceManagerPtr m_mfDeviceManager;
IMFTransformPtr m_mfTransform;
IMFMediaEventGeneratorPtr m_mediaEventGen;
BYTE * m_pointer;
UINT m_pointerBufSize;
UINT m_pointerSize;

View File

@ -18,7 +18,6 @@ Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "MultiMemcpy.h"
#include "Util.h"
#include "common/memcpySSE.h"
MultiMemcpy::MultiMemcpy()
@ -27,6 +26,7 @@ MultiMemcpy::MultiMemcpy()
{
m_workers[i].id = (1 << i);
m_workers[i].running = &m_running;
m_workers[i].abort = false;
m_workers[i].start = CreateSemaphore(NULL, 0, 1, NULL);
m_workers[i].thread = CreateThread(0, 0, WorkerFunction, &m_workers[i], 0, NULL);
@ -44,15 +44,23 @@ MultiMemcpy::~MultiMemcpy()
void MultiMemcpy::Copy(void * dst, void * src, size_t size)
{
if (!m_awake)
Wake();
const size_t block = (size / MULTIMEMCPY_THREADS) & ~0x7F;
if (block == 0)
{
Abort();
memcpySSE(dst, src, size);
return;
}
const size_t block = size / MULTIMEMCPY_THREADS;
Wake();
for (int i = 0; i < MULTIMEMCPY_THREADS; ++i)
{
m_workers[i].dst = (uint8_t *)dst + i * block;
m_workers[i].src = (uint8_t *)src + i * block;
m_workers[i].size = (i + 1) * block - i * block;
if (i == MULTIMEMCPY_THREADS - 1)
m_workers[i].size = size - (block * i);
else
m_workers[i].size = block;
}
INTERLOCKED_OR8(&m_running, (1 << MULTIMEMCPY_THREADS) - 1);
@ -69,6 +77,12 @@ DWORD WINAPI MultiMemcpy::WorkerFunction(LPVOID param)
{
WaitForSingleObject(w->start, INFINITE);
while(!(*w->running & w->id)) {}
if (w->abort)
{
w->abort = false;
INTERLOCKED_AND8(w->running, ~w->id);
continue;
}
memcpySSE(w->dst, w->src, w->size);
INTERLOCKED_AND8(w->running, ~w->id);

View File

@ -22,6 +22,8 @@ Place, Suite 330, Boston, MA 02111-1307 USA
#include <windows.h>
#include <stdint.h>
#include "Util.h"
#pragma once
class MultiMemcpy
{
@ -41,12 +43,29 @@ public:
m_awake = true;
}
// abort a pre-empted copy
inline void Abort()
{
if (!m_awake)
return;
for (int i = 0; i < MULTIMEMCPY_THREADS; ++i)
m_workers[i].abort = true;
INTERLOCKED_OR8(&m_running, (1 << MULTIMEMCPY_THREADS) - 1);
while (m_running) {}
m_awake = false;
}
void Copy(void * dst, void * src, size_t size);
private:
struct Worker
{
unsigned int id;
unsigned int id;
volatile char *running;
bool abort;
HANDLE start;
HANDLE thread;

View File

@ -40,6 +40,27 @@ Place, Suite 330, Boston, MA 02111-1307 USA
class Util
{
public:
static void DebugWinError(const char * file, const unsigned int line, const char * function, const char * desc, HRESULT status)
{
char *buffer;
FormatMessageA(
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER,
NULL,
status,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(char*)&buffer,
1024,
NULL
);
for(size_t i = strlen(buffer) - 1; i > 0; --i)
if (buffer[i] == '\n' || buffer[i] == '\r')
buffer[i] = 0;
fprintf(stderr, "[E] %20s:%-4u | %-30s | %s: 0x%08x (%s)\n", file, line, function, desc, status, buffer);
LocalFree(buffer);
}
static std::string GetSystemRoot()
{
std::string defaultPath;

View File

@ -165,7 +165,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;wmcodecdspuuid.lib;mfplat.lib;mfuuid.lib;evr.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Manifest>
@ -185,7 +185,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;wmcodecdspuuid.lib;mfplat.lib;mfuuid.lib;evr.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Manifest>
@ -205,7 +205,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;wmcodecdspuuid.lib;mfplat.lib;mfuuid.lib;evr.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Manifest>
@ -225,7 +225,7 @@
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;wmcodecdspuuid.lib;mfplat.lib;mfuuid.lib;evr.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Manifest>
@ -249,7 +249,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;wmcodecdspuuid.lib;mfplat.lib;mfuuid.lib;evr.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Manifest>
@ -273,7 +273,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;wmcodecdspuuid.lib;mfplat.lib;mfuuid.lib;evr.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Manifest>
@ -297,7 +297,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;wmcodecdspuuid.lib;mfplat.lib;mfuuid.lib;evr.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Manifest>
@ -321,7 +321,7 @@
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>kernel32.lib;shlwapi.lib;dxgi.lib;d3d11.lib;setupapi.lib;uuid.lib;wmcodecdspuuid.lib;mfplat.lib;mfuuid.lib;evr.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
<Manifest>

View File

@ -52,6 +52,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdParam
{
CrashHandler::Initialize();
TraceUtil::Initialize();
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
SetPriorityClass(GetCurrentProcess(), REALTIME_PRIORITY_CLASS);