最近项目遇到Windows和Linux多显示器录制需求, 需采集每个接入的显示器然后输出RTMP/RTSP流,或让用户选择要采集的显示器。相对于Linux(X-Window), Windows 2000开始就提供了多显示器的相关API,现在跑的基本都是WIN10及以上系统, 实现起来不用考虑系统API兼容等问题,比Linux简单些。
枚举显示器接口:
// 1. EnumDisplayDevices:
BOOL EnumDisplayDevicesW(
[in] LPCWSTR lpDevice,
[in] DWORD iDevNum,
[out] PDISPLAY_DEVICEW lpDisplayDevice,
[in] DWORD dwFlags
);
// 2. EnumDisplayMonitors:
BOOL EnumDisplayMonitors(
[in] HDC hdc,
[in] LPCRECT lprcClip,
[in] MONITORENUMPROC pfnEnum,
[in] LPARAM dwData
);
获取显示器大小、主显示器信息等接口:
// 1.EnumDisplaySettingsEx:
BOOL EnumDisplaySettingsExW(
[in] LPCWSTR lpszDeviceName,
[in] DWORD iModeNum,
[out] DEVMODEW *lpDevMode,
[in] DWORD dwFlags
);
// 2.GetMonitorInfo:
BOOL GetMonitorInfoW(
[in] HMONITOR hMonitor,
[out] LPMONITORINFO lpmi
);
C#更用方便, 直接用:
System.Windows.Forms.Screen
获取到显示器数量、名称、坐标、大小和主辅设备等信息后,接下来就是采集屏幕图像,从Win8开始,不再支持32位以下bpp显示设置, 实现中可以假设采集到的都是32位RGB图像,再把RGB转YUV, 编码输出RTMP/RTSP就好了,实现细节较多,考虑到实现代码无法一一映射到文字描述,这里就不再详细说明了。
为方便集成调用,直接提供屏幕选择的和屏幕层叠加的接口:
/*
* Copyright (C) 1130758427@qq.com. All rights reserved.
* 问题沟通微信:ldxevt
*/
typedef struct _NT_PB_ScreenLayerConfigV3
{
NT_PB_LayerBaseConfig base_;
NT_PB_RectRegion clip_region_; // 如果是全屏幕的话,请全部填充0
NT_PVOID reserve1_; // 保留字段1
#define NT_PB_SCREEN_LAYER_FLAG_NONE (0)
#define NT_PB_SCREEN_LAYER_FLAG_DISPLAY_OPTION_DEVICE_NAME (1u<<0)
NT_UINT32 flags_;
NT_INT32 scale_filter_mode_; // 缩放质量, 0的话SDK将使用默认值, 目前可设置范围为[1, 3], 值越大缩放质量越好,但越耗性能
#define NT_PB_SCREEN_LAYER_DISPLAY_OPTION_BUFFER_SIZE (256)
NT_CHAR display_option_[NT_PB_SCREEN_LAYER_DISPLAY_OPTION_BUFFER_SIZE];
} NT_PB_ScreenLayerConfigV3;
typedef layer_conf_wrapper<NT_PB_ScreenLayerConfigV3, NT_PB_E_LAYER_TYPE_SCREEN> ScreenLayerConfigWrapperV3;
NT_UINT32 NT_API NT_PB_SetCaptureDisplayDeviceName(NT_HANDLE handle, NT_PCSTR name_utf8);
测试demo界面:
当前接口支持单独输出rtmp流和tsp流; 也可同时输出rtmp/rtsp流+本地录像。如果设备接入的显示器超过三个,考虑到性能和网络带宽等限制因素,也提供了图像采集后再缩放的接口。