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

应用背景

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

dd6ab4736639237f06ed370292acac88.png

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(&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(&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(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;

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值