在互动直播或视频通话场景下,主播或视频通话用户可以使用屏幕共享功能,将自己的屏幕内容分享给观众或其他用户观看,以提高沟通效率。
屏幕共享在如下场景中应用广泛:
要在 Windows 系统实现屏幕共享,需要 Windows 原生 API 和 Agora API 共同实现。目前支持以下两种屏幕共享方式:
API 的调用时序有两种方案,分别如下图所示。本文中的实现方法以第一种调用时序为例。
在实现屏幕共享前,请确保已在你的项目中实现基本的实时音视频功能。详见开始音视频通话或开始互动直播。
本节介绍如何实现屏幕共享。
获取显示器屏幕信息列表。
// 获取显示器屏幕信息列表
void CAgoraScreenCapture::InitMonitorInfos()
{
// m_monitors 是 CMonitors 类的实例。CMonitors 类的具体实现参考示例项目。
// m_monitors 调用 EnumMonitor 获取显示器屏幕信息列表。EnumMonitor 方法的具体实现参考示例项目。
m_monitors.EnumMonitor();
// 定义 infos 向量,用于存储显示器屏幕信息列表。
std::vector<CMonitors::MonitorInformation> infos = m_monitors.GetMonitors();
CString str = _T("");
// 获取每个显示器屏幕在虚拟屏幕上的 Rectangle 坐标。
for (size_t i = 0; i < infos.size(); i++) {
RECT rcMonitor = infos[i].monitorInfo.rcMonitor;
CString strInfo;
strInfo.Format(_T("Screen%d: rect = {%d, %d, %d, %d} ")
, i + 1, rcMonitor.left, rcMonitor.top, rcMonitor.right, rcMonitor.bottom);
}
str += strInfo;
m_cmbScreenRegion.InsertString(i, utf82cs(infos[i].monitorName));
}
m_cmbScreenRegion.InsertString(infos.size(), _T("Select Window Hwnd Rect Area"));
m_staScreenInfo.SetWindowText(str);
m_cmbScreenRegion.SetCurSel(0);
}
通过待共享的显示器屏幕在虚拟屏幕上的 Rectangle
坐标以及待共享区域在显示器屏幕上的 Rectangle
坐标实现屏幕共享。
void CAgoraScreenCapture::OnBnClickedButtonStartShareScreen()
{
m_screenShare = !m_screenShare;
if (m_screenShare) {
// 获取选择的显示器屏幕。
int sel = m_cmbScreenRegion.GetCurSel();
agora::rtc::Rectangle regionRect = { 0,0,0,0 }, screenRegion = {0,0,0,0};
// 获取指定的待共享区域在整个显示器屏幕中的 Rectangle 坐标。
// GetMonitorRectangle 方法的具体实现详见示例项目。
regionRect = m_monitors.GetMonitorRectangle(sel);
// 获取显示器屏幕在虚拟屏幕中的 Rectangle 坐标。
screenRegion = m_monitors.GetScreenRect();
m_monitors.GetScreenRect();
// 屏幕共享的编码参数配置。
ScreenCaptureParameters capParam;
// 开始屏幕共享。
m_rtcEngine->startScreenCaptureByScreenRect(screenRegion, regionRect, capParam);
m_btnShareScreen.SetWindowText(screenShareCtrlStopShare);
// 开启视频预览。
m_rtcEngine->startPreview();
m_btnStartCap.EnableWindow(FALSE);
m_screenShare = true;
}
else {
// 停止屏幕共享。
m_rtcEngine->stopScreenCapture();
m_btnShareScreen.SetWindowText(screenShareCtrlShareSCreen);
m_btnStartCap.EnableWindow(TRUE);
m_screenShare = false;
}
}
加入频道,并发布屏幕共享流。
如果仅需发布屏幕共享流,参考如下代码:
ChannelMediaOptions option;
option.publishScreenTrack = true;
option.publishCameraTrack = false;
if (0 == m_rtcEngine->joinChannel(APP_TOKEN, szChannelId.c_str(), 0, option)) {
strInfo.Format(_T("join channel %s, use ChannelMediaOption"), getCurrentTime());
m_btnJoinChannel.EnableWindow(FALSE);
}
如需发布屏幕共享流和本地摄像头采集的视频流,参考如下代码:
// 加入频道并发布本地摄像头采集的视频流
ChannelMediaOptions option;
option.publishScreenTrack = false;
option.publishCameraTrack = true;
if (0 == m_rtcEngine->joinChannel(APP_TOKEN, szChannelId.c_str(), 0, option)) {
strInfo.Format(_T("join channel %s, use ChannelMediaOption"), getCurrentTime());
m_btnJoinChannel.EnableWindow(FALSE);
}
// 加入相同频道并发布屏幕共享流
ChannelMediaOptions options;
options.publishScreenTrack = true;
options.autoSubscribeAudio = false;
options.autoSubscribeVideo = false;
options.publishCameraTrack = false;
CAgoraMultiVideoSourceEventHandler * p = new CAgoraMultiVideoSourceEventHandler;
p->SetChannelId(m_vecVidoeSourceEventHandler.size());
p->SetMsgReceiver(GetSafeHwnd());
m_vecVidoeSourceEventHandler.push_back(p);
if (0 == m_rtcEngine->joinChannelEx(APP_TOKEN,connection, options, p))
{
m_btnJoinChannel.EnableWindow(FALSE);
}
Windows 系统为每个窗口分配一个 Window ID,数据类型为 HWND。该 ID 对应唯一的 Windows 窗口。为了兼容 x86 和 x64 系统,API 参数使用 view_t
类型。
调用 Windows 原生 API EnumWindows
获取 Window ID 列表。
// 获取所有最新顶层窗口的 HWND 并储存在 m_listWnd 中。
int CAgoraScreenCapture::RefreshWndInfo()
{
m_listWnd.RemoveAll();
::EnumWindows(&CAgoraScreenCapture::WndEnumProc, (LPARAM)&m_listWnd);
return static_cast<int>(m_listWnd.GetCount());
}
通过指定窗口的 Window ID 共享窗口。
void CAgoraScreenCapture::OnBnClickedButtonStartShare()
{
if (!m_rtcEngine || !m_initialize)
return;
HWND hWnd = NULL;
// 需要共享的 Window ID。
hWnd = m_listWnd.GetAt(m_listWnd.FindIndex(m_cmbScreenCap.GetCurSel()));
int ret = 0;
m_windowShare = !m_windowShare;
if (m_windowShare)
{
// 设置 captureParameters。
ScreenCaptureParameters capParam;
GetCaptureParameterFromCtrl(capParam);
// 设置 regionRect。
CRect rcWnd = { 0 };
::GetClientRect(hWnd, &rcWnd);
agora::rtc::Rectangle rcCapWnd = { rcWnd.left, rcWnd.top, rcWnd.right - rcWnd.left, rcWnd.bottom - rcWnd.top };
// 开始屏幕共享。
ret = m_rtcEngine->startScreenCaptureByWindowId(hWnd, rcCapWnd, capParam);
// 开启视频预览。
m_rtcEngine->startPreview();
m_lstInfo.InsertString(m_lstInfo.GetCount(), _T("startPreview"));
if (ret == 0)
m_lstInfo.InsertString(m_lstInfo.GetCount(), _T("Succees!"));
else
m_lstInfo.InsertString(m_lstInfo.GetCount(), _T("Failed!"));
m_btnStartCap.SetWindowText(screenShareCtrlEndCap);
m_btnShareScreen.EnableWindow(FALSE);
}
else {
// 停止屏幕共享。
ret = m_rtcEngine->stopScreenCapture();
if (ret == 0)
m_lstInfo.InsertString(m_lstInfo.GetCount(), _T("Success!"));
else
m_lstInfo.InsertString(m_lstInfo.GetCount(), _T("Failed!"));
m_btnStartCap.SetWindowText(screenShareCtrlStartCap);
m_btnShareScreen.EnableWindow(TRUE);
}
}
加入频道,并发布屏幕共享流。
如果仅需发布屏幕共享流,参考如下代码:
ChannelMediaOptions option;
option.publishScreenTrack = true;
option.publishCameraTrack = false;
if (0 == m_rtcEngine->joinChannel(APP_TOKEN, szChannelId.c_str(), 0, option)) {
strInfo.Format(_T("join channel %s, use ChannelMediaOption"), getCurrentTime());
m_btnJoinChannel.EnableWindow(FALSE);
}
如需发布屏幕共享流和本地摄像头采集的视频流,参考如下代码:
// 加入频道并发布本地摄像头采集的视频流
ChannelMediaOptions option;
option.publishScreenTrack = false;
option.publishCameraTrack = true;
if (0 == m_rtcEngine->joinChannel(APP_TOKEN, szChannelId.c_str(), 0, option)) {
strInfo.Format(_T("join channel %s, use ChannelMediaOption"), getCurrentTime());
m_btnJoinChannel.EnableWindow(FALSE);
}
// 加入相同频道并发布屏幕共享流
ChannelMediaOptions options;
options.publishScreenTrack = true;
options.autoSubscribeAudio = false;
options.autoSubscribeVideo = false;
options.publishCameraTrack = false;
CAgoraMultiVideoSourceEventHandler * p = new CAgoraMultiVideoSourceEventHandler;
p->SetChannelId(m_vecVidoeSourceEventHandler.size());
p->SetMsgReceiver(GetSafeHwnd());
m_vecVidoeSourceEventHandler.push_back(p);
if (0 == m_rtcEngine->joinChannelEx(APP_TOKEN,connection, options, p))
{
m_btnJoinChannel.EnableWindow(FALSE);
}
介绍更多相关信息和参考页面的链接。
ScreenCaptureParameters
类中各参数的设置可能会影响计费。如果你将 dimensions
参数设为默认值,会按照 1920 × 1080 进行计费。我们在 GitHub 提供了一个开源的示例项目。你可以前往下载体验。
Windows 原生 API
Agora API