桌面重复(DirectX)屏幕捕获无法提供屏幕更新



我正在处理一个应用程序,该应用程序可以通过桌面重复API捕获屏幕(使用DirectX 11)(仅差异到上一个屏幕更新),然后在另一个窗口上渲染它(查看器可能正在通过LAN连接的另一台计算机上运行)。该代码是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;
}

更新:

在设备悬挂时,在重复manager中未能获取下一个帧(在播放屏幕的中间,例如:连续捕获视频并将其发送到另一端)

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

这是详细错误信息:

ID3D11DUPLICATICMANAGER :: ProcessFailure-错误:在重复manager中未能获取下一帧,详细信息:键入的Mutex被放弃。

更新2:每当设备无法永远给屏幕更新时,我都会获得错误代码,而这是相同的

ID3D11DUPLICATICMANAGER :: ProcessFailure-错误:无法在重复manager中获得重复输出,详细信息:访问被拒绝。

错误代码是e_accessdened。

我不明白为什么我已经在系统模式下运行时会遇到此错误,并且SetThreadDesktop已经执行了两次(一个在INIT期间,另一个在检测到失败后)

这是MSDN上错误的解释:E_AccessDened如果应用程序对当前桌面映像没有访问权限。例如,只有在local_system上运行的应用程序才能访问安全桌面。

还有其他会导致这种问题的其他东西吗?

检查返回代码总是很好,并立即返回到GDI或其他任何可用的屏幕捕获方法,以防发生不可恢复的错误。重试对于某些硬件错误,例如达到的最大限制,删除设备等,我以一种艰难的方式学习了它。此外,DirectX设备在极少数情况下产生初始框架之前需要进行一些迭代。重试超过10次没有用,您可以安全退缩或尝试重新启动设备以在退回之前再检查一次。

这里有一些基本检查要做:

处理DXGI_ERROR_NOT_CURRYTY_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_REMAVE)或设备重置(DXGI_ERROR_DEVICE_RESET)&amp;不在内存(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_accessden。

还有其他情况,您可能会遇到此错误,例如,桌面开关案例,放弃了键入的静音。在这种情况下,您可以尝试重新定位设备。

我也在这里上传了我的示例项目。

相关内容

  • 没有找到相关文章

最新更新