• 集采集,编码,组播,推流和流媒体RTSP服务于一身大屏展示投屏互联网直播同屏组件EasyScreenlive播放器使用DXGI采集桌面方法介绍


    应用背景

    投屏和显示技术的升级,使视频远程控制的功能日益更新,EasyScreenlive广泛应用于大屏显示投屏,无纸化会议同屏演示,课堂同屏等,可以配合全屏显示,反向模拟触控实现远程控制功能(Android控制Windows,Windows控制Android,Windows控制Windows等)

    EasyScreenlive支持DXGI采集桌面

    之前文章中我们已经提到了通过GDI和D3D来采集windows桌面,通过D3D采集效率已经提高了不少,那么有没有更加高效的方法呢?答案是有的,那就是我们现在要讲的DXGI桌面采集技术。

    该技术是基于DirectX技术,所以你需要简单熟悉DirectX技术才能使用DXGI,他通过一系列DX设备的创建和查询各种接口,最终获取到 IDXGIOutputDuplication 接口。

    截屏的时候,使用这个接口的AcquireNextFrame 函数获取当前桌面图像, 当然这个接口还提供 GetFrameDirtyRects等函数获取发生了变化的矩形区域。 这应该算是一个比较好的截屏方法,CPU占用也极低,可惜只支持windows 8 以上的平台,而win7,windows xp等系统不支持。

    下面我们通过代码简单讲解下采集实现流程,首先,通过DXGI接口获取桌面设备:

        // Get desktop
        DUPL_RETURN Ret;
        HDESK CurrentDesktop = nullptr;
        CurrentDesktop = OpenInputDesktop(0, FALSE, GENERIC_ALL);
        if (!CurrentDesktop)
        {
            // We do not have access to the desktop so request a retry
            SetEvent(TData->ExpectedErrorEvent);
            Ret = DUPL_RETURN_ERROR_EXPECTED;
            goto Exit;
        }
    
        // Attach desktop to this thread
        bool DesktopAttached = SetThreadDesktop(CurrentDesktop) != 0;
        CloseDesktop(CurrentDesktop);
        CurrentDesktop = nullptr;
        if (!DesktopAttached)
        {
            // We do not have access to the desktop so request a retry
            Ret = DUPL_RETURN_ERROR_EXPECTED;
            goto Exit;
        }
    }
        // New display manager
        DispMgr.InitD3D(&TData->DxRes);
    
        // Obtain handle to sync shared Surface
        HRESULT hr = TData->DxRes.Device->OpenSharedResource(TData->TexSharedHandle, __uuidof(ID3D11Texture2D), reinterpret_cast<void**>(&SharedSurf));
        if (FAILED (hr))
        {
            Ret = ProcessFailure(TData->DxRes.Device, L"Opening shared texture failed", L"Error", hr, SystemTransitionsExpectedErrors);
            goto Exit;
        }
    
        hr = SharedSurf->QueryInterface(__uuidof(IDXGIKeyedMutex), reinterpret_cast<void**>(&KeyMutex));
        if (FAILED(hr))
        {
            Ret = ProcessFailure(nullptr, L"Failed to get keyed mutex interface in spawned thread", L"Error", hr);
            goto Exit;
        }
    
        // Make duplication manager
        Ret = DuplMgr.InitDupl(TData->DxRes.Device, TData->Output);
        if (Ret != DUPL_RETURN_SUCCESS)
        {
            goto Exit;
        }
    
        // Get output description
        DXGI_OUTPUT_DESC DesktopDesc;
        RtlZeroMemory(&DesktopDesc, sizeof(DXGI_OUTPUT_DESC));
        DuplMgr.GetOutputDesc(&DesktopDesc);
    

    然后,通过大循环GetFrame采集桌面图像数据源,实现代码如下:

        while ((WaitForSingleObjectEx(TData->TerminateThreadsEvent, 0, FALSE) == WAIT_TIMEOUT))
        {
            if (!WaitToProcessCurrentFrame)
            {
                // Get new frame from desktop duplication
                bool TimeOut;
                Ret = DuplMgr.GetFrame(&CurrentData, &TimeOut);
                if (Ret != DUPL_RETURN_SUCCESS)
                {
                    // An error occurred getting the next frame drop out of loop which
                    // will check if it was expected or not
                    break;
                }
    
                // Check for timeout
                if (TimeOut)
                {
                    // No new frame at the moment
                    continue;
                }
            }
    
            // We have a new frame so try and process it
            // Try to acquire keyed mutex in order to access shared surface
            hr = KeyMutex->AcquireSync(0, 1000);
            if (hr == static_cast<HRESULT>(WAIT_TIMEOUT))
            {
                // Can't use shared surface right now, try again later
                WaitToProcessCurrentFrame = true;
                continue;
            }
            else if (FAILED(hr))
            {
                // Generic unknown failure
                Ret = ProcessFailure(TData->DxRes.Device, L"Unexpected error acquiring KeyMutex", L"Error", hr, SystemTransitionsExpectedErrors);
                DuplMgr.DoneWithFrame();
                break;
            }
    
            // We can now process the current frame
            WaitToProcessCurrentFrame = false;
    
            // Get mouse info
            Ret = DuplMgr.GetMouse(TData->PtrInfo, &(CurrentData.FrameInfo), TData->OffsetX, TData->OffsetY);
            if (Ret != DUPL_RETURN_SUCCESS)
            {
                DuplMgr.DoneWithFrame();
                KeyMutex->ReleaseSync(1);
                break;
            }
    
            // Process new frame
            Ret = DispMgr.ProcessFrame(&CurrentData, SharedSurf, TData->OffsetX, TData->OffsetY, &DesktopDesc);
            if (Ret != DUPL_RETURN_SUCCESS)
            {
                DuplMgr.DoneWithFrame();
                KeyMutex->ReleaseSync(1);
                break;
            }
    
            // Release acquired keyed mutex
            hr = KeyMutex->ReleaseSync(1);
            if (FAILED(hr))
            {
                Ret = ProcessFailure(TData->DxRes.Device, L"Unexpected error releasing the keyed mutex", L"Error", hr, SystemTransitionsExpectedErrors);
                DuplMgr.DoneWithFrame();
                break;
            }
    
            // Release frame back to desktop duplication
            Ret = DuplMgr.DoneWithFrame();
            if (Ret != DUPL_RETURN_SUCCESS)
            {
                break;
            }
      }
    

     

  • 相关阅读:
    APP测试的那些坑
    最全的测试工具以及测试需要掌握的工具
    接口测试的必要性
    jmeter-察看结果树-响应数据,中文显示乱码问题处理
    JMeter学习(一)工具简单介绍
    BZOJ1972: [Sdoi2010]猪国杀
    luoguP1311 选择客栈 题解(NOIP2011)
    luoguP1003 铺地毯 题解(NOIP2011)
    luoguP1081 开车旅行 题解(NOIP2012)
    luoguP3391[模板]文艺平衡树(Splay) 题解
  • 原文地址:https://www.cnblogs.com/TSINGSEE/p/11792403.html
Copyright © 2020-2023  润新知