代码之家  ›  专栏  ›  技术社区  ›  iamrameshkumar

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

  •  18
  • iamrameshkumar  · 技术社区  · 8 年前

    我正在开发一个应用程序,该应用程序将通过桌面复制API(使用DirectX 11)(仅与以前的屏幕更新不同)捕获屏幕,并在另一个窗口上呈现(查看器可能在通过LAN连接的另一台机器上运行)。该代码是 sample MSDN中提供。除了设备没有提供任何屏幕更新之外,一切都很好,尽管有时在中间会有一次,但在某些机器上(大多数在windows 8/8.1机器上,很少在windows 10机器上)大约有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;
    }
    

    无法在DUPLICATIONMANAGER中获取下一帧每当设备挂起时都会打印(即在流式处理屏幕的过程中,例如:连续捕获视频并将其发送到另一端)

    // 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-错误:未能在DUPLICATIONMANAGER中获取下一帧,详细信息:已放弃键控互斥体。

    更新2:

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

    错误代码为E_ACCESSDENIED。

    我不理解为什么我会出现这个错误,因为我已经在系统模式下运行,并且SetThreadDesktop已经执行了两次(一次在初始化期间,另一次在检测到故障后)

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

    1 回复  |  直到 6 年前
        1
  •  1
  •   iamrameshkumar    6 年前

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

    以下是一些需要进行的基本检查:

    处理DXGI\u ERROR\u NOT\u current\u AVAILABLE ERROR:

    _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\u ERROR\u device\u removed)或设备重置(DXGI\u ERROR\u device\u reset)&内存不足(E\u OUTOFMEMORY)错误代码(我有时会收到E\u 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;
    }
    

    还有其他可能出现此错误的情况,例如,桌面交换机情况,放弃键控互斥体。在这种情况下,您可以尝试重新初始化设备。

    我还上传了我的样本项目 here .