gpt4 book ai didi

visual-c++ - 桌面复制 (DirectX) 屏幕捕获无法提供屏幕更新

转载 作者:行者123 更新时间:2023-12-03 13:38:06 25 4
gpt4 key购买 nike

我正在开发一个应用程序,该应用程序将通过桌面复制 API(使用 DirectX 11)捕获屏幕(仅与上一个屏幕更新的差异)并将其呈现在另一个窗口上(查看器可能在通过 LAN 连接的另一台机器上运行) .该代码是 sample 的改进版本在 MSDN 中提供。一切正常,除了设备没有提供任何屏幕更新,虽然中间有几次,在某些机器上大约有 10% 的时间发生这种情况(主要在 windows 8/8.1 机器上,很少在 windows 10 机器上)。我尝试了所有可能的方法来解决这个问题。减少了设备重置的次数,这为我提供了一些可靠的输出,但并不总是能 100% 正常工作。
设备有时无法提供初始屏幕(全屏)(在所有支持桌面复制的 Windows 操作系统上,这种情况发生率为 60%),我想出了一个解决方法,重试从设备,直到它提供一个但也导致了多个问题,设备甚至可能永远不会给出初始屏幕。
我已经投入了数周的时间来解决这个问题,但没有找到合适的解决方案,而且我知道没有讨论这类问题的论坛。任何帮助,将不胜感激。
下面是我的代码,用于获取与前一个屏幕差异、初始化设备、填充适配器和监视器。
请耐心等待很长的代码片段,在此先感谢。
获取屏幕更新:

INT getChangedRegions(int timeout, rectangles &dirtyRects, std::vector <MOVE_RECT> &moveRects, UINT &rect_count, RECT ScreenRect)
{
UINT diffArea = 0;
FRAME_DATA currentFrameData;

bool isTimeOut = false;

TRY
{

m_LastErrorCode = m_DuplicationManager.GetFrame(&currentFrameData, timeout, &isTimeOut);

if(SUCCEEDED(m_LastErrorCode) && (!isTimeOut))
{
if(currentFrameData.FrameInfo.TotalMetadataBufferSize)
{

m_CurrentFrameTexture = currentFrameData.Frame;

if(currentFrameData.MoveCount)
{
DXGI_OUTDUPL_MOVE_RECT* moveRectArray = reinterpret_cast<DXGI_OUTDUPL_MOVE_RECT*> (currentFrameData.MetaData);

if (moveRectArray)
{
for(UINT index = 0; index < currentFrameData.MoveCount; index++)
{
//WebRTC
// DirectX capturer API may randomly return unmoved move_rects, which should
// be skipped to avoid unnecessary wasting of differing and encoding
// resources.
// By using testing application it2me_standalone_host_main, this check
// reduces average capture time by 0.375% (4.07 -> 4.055), and average
// encode time by 0.313% (8.042 -> 8.016) without other impacts.

if (moveRectArray[index].SourcePoint.x != moveRectArray[index].DestinationRect.left || moveRectArray[index].SourcePoint.y != moveRectArray[index].DestinationRect.top)
{

if(m_UseD3D11BitmapConversion)
{
MOVE_RECT moveRect;

moveRect.SourcePoint.x = moveRectArray[index].SourcePoint.x * m_ImageScalingFactor;
moveRect.SourcePoint.y = moveRectArray[index].SourcePoint.y * m_ImageScalingFactor;

moveRect.DestinationRect.left = moveRectArray[index].DestinationRect.left * m_ImageScalingFactor;
moveRect.DestinationRect.top = moveRectArray[index].DestinationRect.top * m_ImageScalingFactor;
moveRect.DestinationRect.bottom = moveRectArray[index].DestinationRect.bottom * m_ImageScalingFactor;
moveRect.DestinationRect.right = moveRectArray[index].DestinationRect.right * m_ImageScalingFactor;

moveRects.push_back(moveRect);
diffArea += abs((moveRect.DestinationRect.right - moveRect.DestinationRect.left) *
(moveRect.DestinationRect.bottom - moveRect.DestinationRect.top));
}
else
{
moveRects.push_back(moveRectArray[index]);
diffArea += abs((moveRectArray[index].DestinationRect.right - moveRectArray[index].DestinationRect.left) *
(moveRectArray[index].DestinationRect.bottom - moveRectArray[index].DestinationRect.top));
}
}
}
}
else
{
return -1;
}
}

if(currentFrameData.DirtyCount)
{
RECT* dirtyRectArray = reinterpret_cast<RECT*> (currentFrameData.MetaData + (currentFrameData.MoveCount * sizeof(DXGI_OUTDUPL_MOVE_RECT)));

if (!dirtyRectArray)
{
return -1;
}

rect_count = currentFrameData.DirtyCount;

for(UINT index = 0; index < rect_count; index ++)
{

if(m_UseD3D11BitmapConversion)
{
RECT dirtyRect;

dirtyRect.bottom = dirtyRectArray[index].bottom * m_ImageScalingFactor;
dirtyRect.top = dirtyRectArray[index].top * m_ImageScalingFactor;
dirtyRect.left = dirtyRectArray[index].left * m_ImageScalingFactor;
dirtyRect.right = dirtyRectArray[index].right * m_ImageScalingFactor;

diffArea += abs((dirtyRect.right - dirtyRect.left) *
(dirtyRect.bottom - dirtyRect.top));

dirtyRects.push_back(dirtyRect);
}
else
{
diffArea += abs((dirtyRectArray[index].right - dirtyRectArray[index].left) *
(dirtyRectArray[index].bottom - dirtyRectArray[index].top));

dirtyRects.push_back(dirtyRectArray[index]);
}
}

}

}

return diffArea;

}

CATCH_ALL(e)
{
LOG(CRITICAL) << _T("Exception in getChangedRegions");
}
END_CATCH_ALL

return -1;
}
这是初始化设备的代码
       //
// Initialize duplication interfaces
//
HRESULT cDuplicationManager::InitDupl(_In_ ID3D11Device* Device, _In_ IDXGIAdapter *_pAdapter, _In_ IDXGIOutput *_pOutput, _In_ UINT Output)
{
HRESULT hr = E_FAIL;

if(!_pOutput || !_pAdapter || !Device)
{
return hr;
}

m_OutputNumber = Output;

// Take a reference on the device
m_Device = Device;
m_Device->AddRef();

/*
// Get DXGI device
IDXGIDevice* DxgiDevice = nullptr;
HRESULT hr = m_Device->QueryInterface(__uuidof(IDXGIDevice), reinterpret_cast<void**>(&DxgiDevice));
if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for DXGI Device"), _T("Error"), hr);
}

// Get DXGI adapter
IDXGIAdapter* DxgiAdapter = nullptr;
hr = DxgiDevice->GetParent(__uuidof(IDXGIAdapter), reinterpret_cast<void**>(&DxgiAdapter));
DxgiDevice->Release();
DxgiDevice = nullptr;
if (FAILED(hr))
{
return ProcessFailure(m_Device, _T("Failed to get parent DXGI Adapter"), _T("Error"), hr);//, SystemTransitionsExpectedErrors);
}

// Get output
IDXGIOutput* DxgiOutput = nullptr;
hr = DxgiAdapter->EnumOutputs(Output, &DxgiOutput);
DxgiAdapter->Release();
DxgiAdapter = nullptr;
if (FAILED(hr))
{
return ProcessFailure(m_Device, _T("Failed to get specified output in DUPLICATIONMANAGER"), _T("Error"), hr);//, EnumOutputsExpectedErrors);
}

DxgiOutput->GetDesc(&m_OutputDesc);

IDXGIOutput1* DxgiOutput1 = nullptr;
hr = DxgiOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1));

*/

_pOutput->GetDesc(&m_OutputDesc);
// QI for Output 1
IDXGIOutput1* DxgiOutput1 = nullptr;
hr = _pOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1));

if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for DxgiOutput1 in DUPLICATIONMANAGER"), _T("Error"), hr);
}

// Create desktop duplication
hr = DxgiOutput1->DuplicateOutput(m_Device, &m_DeskDupl);

DxgiOutput1->Release();
DxgiOutput1 = nullptr;


if (FAILED(hr) || !m_DeskDupl)
{
if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
{
return ProcessFailure(nullptr, _T("Maximum number of applications using Desktop Duplication API"), _T("Error"), hr);
}
return ProcessFailure(m_Device, _T("Failed to get duplicate output in DUPLICATIONMANAGER"), _T("Error"), hr);//, CreateDuplicationExpectedErrors);
}

return S_OK;
}
最后得到当前帧和与前一帧的区别:
   //
// Get next frame and write it into Data
//
_Success_(*Timeout == false && return == DUPL_RETURN_SUCCESS)
HRESULT cDuplicationManager::GetFrame(_Out_ FRAME_DATA* Data, int timeout, _Out_ bool* Timeout)
{
IDXGIResource* DesktopResource = nullptr;
DXGI_OUTDUPL_FRAME_INFO FrameInfo;

try
{
// Get new frame
HRESULT hr = m_DeskDupl->AcquireNextFrame(timeout, &FrameInfo, &DesktopResource);

if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
*Timeout = true;
return S_OK;
}

*Timeout = false;

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

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

if (DesktopResource)
{
// QI for IDXGIResource
hr = DesktopResource->QueryInterface(__uuidof(ID3D11Texture2D), reinterpret_cast<void **>(&m_AcquiredDesktopImage));
DesktopResource->Release();
DesktopResource = nullptr;
}

if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for ID3D11Texture2D from acquired IDXGIResource in DUPLICATIONMANAGER"), _T("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, _T("Failed to allocate memory for metadata in DUPLICATIONMANAGER"), _T("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, _T("Failed to get frame dirty rects in DUPLICATIONMANAGER"), _T("Error"), hr);//, FrameInfoExpectedErrors);
}

Data->DirtyCount = BufSize / sizeof(RECT);

Data->MetaData = m_MetaDataBuffer;
}

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

}
catch (...)
{
return S_FALSE;
}

return S_OK;
}
更新:

Failed to acquire next frame in DUPLICATIONMANAGER is getting printed whenever the device has hung (That is in the mid of streaming the screens, Ex: Continuously capturing a video and sending it to the other end)

// Get new frame
HRESULT hr = m_DeskDupl->AcquireNextFrame(timeout, &FrameInfo, &DesktopResource);

if (hr == DXGI_ERROR_WAIT_TIMEOUT)
{
*Timeout = true;
return S_OK;
}

*Timeout = false;

if (FAILED(hr))
{
return ProcessFailure(m_Device, _T("Failed to acquire next frame in DUPLICATIONMANAGER"), _T("Error"), hr);//, FrameInfoExpectedErrors);
}
这是详细的错误信息:

Id3d11DuplicationManager::ProcessFailure - Error: Failed to acquire next frame in DUPLICATIONMANAGER, Detail: The keyed mutex was abandoned.


更新 2:
每当设备无法永远提供屏幕更新时,我都会收到错误代码,这里是一样的
Id3d11DuplicationManager::ProcessFailure - 错误:无法在 DUPLICATIONMANAGER 中获取重复输出,详细信息:访问被拒绝。
错误代码是 E_ACCESSDENIED。
我不明白为什么会出现此错误,因为我已经在 SYSTEM 模式下运行并且 SetThreadDesktop 已执行两次(一次在初始化期间,另一次在检测到故障后)

This is what the explanation of the error on MSDN : E_ACCESSDENIED if the application does not have access privilege to the current desktop image. For example, only an application that runs at LOCAL_SYSTEM can access the secure desktop.


还有什么会导致这种问题的吗?

最佳答案

如果出现不可恢复的错误,检查返回代码并立即回退到 GDI 或任何其他可用的屏幕捕获方法总是好的。对于某些硬件错误,例如达到最大限制、内存不足、设备已移除等,重试在大多数情况下都不起作用,我很难学会。此外,DirectX 设备在极少数情况下会在生成初始帧之前进行几次迭代。重试 10 次以上没有用,您可以安全地回退或尝试重新初始化设备以在回退之前再检查一次。

以下是一些基本检查:

处理 DXGI_ERROR_NOT_CURRENTLY_AVAILABLE 错误:

_pOutput->GetDesc(&m_OutputDesc);
// QI for Output 1
IDXGIOutput1* DxgiOutput1 = nullptr;
hr = _pOutput->QueryInterface(__uuidof(DxgiOutput1), reinterpret_cast<void**>(&DxgiOutput1));

if (FAILED(hr))
{
return ProcessFailure(nullptr, _T("Failed to QI for DxgiOutput1 in DUPLICATIONMANAGER"), _T("Error"), hr);
}

// Create desktop duplication
hr = DxgiOutput1->DuplicateOutput(m_Device, &m_DeskDupl);

DxgiOutput1->Release();
DxgiOutput1 = nullptr;

if (FAILED(hr) || !m_DeskDupl)
{
if (hr == DXGI_ERROR_NOT_CURRENTLY_AVAILABLE)
{
return ProcessFailure(nullptr, _T("Maximum number of applications using Desktop Duplication API"), _T("Error"), hr);
}

return ProcessFailure(m_Device, _T("Failed to get duplicate output in DUPLICATIONMANAGER"), _T("Error"), hr);//, CreateDuplicationExpectedErrors);
}

检查设备已移除(DXGI_ERROR_DEVICE_REMOVED)或设备重置(DXGI_ERROR_DEVICE_RESET)和内存不足(E_OUTOFMEMORY)错误代码(我有时会收到 E_OUTOFMEMORY,尽管这并不常见):
 HRESULT ProcessFailure(_In_opt_ ID3D11Device* Device, _In_ LPCWSTR Str, _In_ LPCWSTR Title, HRESULT hr)//, _In_opt_z_ HRESULT* ExpectedErrors = NULL)
{
HRESULT TranslatedHr;

// On an error check if the DX device is lost
if (Device)
{
HRESULT DeviceRemovedReason = Device->GetDeviceRemovedReason();

switch (DeviceRemovedReason)
{
case DXGI_ERROR_DEVICE_REMOVED:
case DXGI_ERROR_DEVICE_RESET:
case static_cast<HRESULT>(E_OUTOFMEMORY) :
{
// Our device has been stopped due to an external event on the GPU so map them all to
// device removed and continue processing the condition
TranslatedHr = DXGI_ERROR_DEVICE_REMOVED;
break;
}

case S_OK:
{
// Device is not removed so use original error
TranslatedHr = hr;
break;
}

default:
{
// Device is removed but not a error we want to remap
TranslatedHr = DeviceRemovedReason;
}
}
}
else
{
TranslatedHr = hr;
}

_com_error err(TranslatedHr);
LPCTSTR errMsg = err.ErrorMessage();

return TranslatedHr;
}

此外,桌面复制需要真正的图形设备处于事件状态才能工作。否则你可能会得到 E_ACCESSDENIED 。

在其他情况下,您可能会收到此错误,例如桌面开关盒、废弃的键控互斥锁。在这种情况下,您可以尝试重新初始化设备。

我还上传了我的示例项目 here .

关于visual-c++ - 桌面复制 (DirectX) 屏幕捕获无法提供屏幕更新,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46145057/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com