共计 4883 个字符,预计需要花费 13 分钟才能阅读完成。
性能简介
屏幕共享包含屏幕采集和视频流推送两局部性能。与远程桌面不同,屏幕共享只是将本地桌面内容以视频流的形式分享到网络。本文的重点,是解说如何利用 webrtc 的屏幕采集性能。对于 webrtc 视频编码传输性能的利用,须要专门的文章进行解说,这里临时不做开展,而是把重点集中在屏幕采集上。webrtc 提供了多个平台的屏幕共享性能,这里以 windows 10 平台作为开发环境,讲述如何在 window 平台,利用 webrtc 的屏幕共享性能。
屏幕共享包含三种采集形式,有全屏采集,窗口采集,和指定区域采集。每种采集形式,都有本人特定的应用场景,这里次要关注的,是如何应用 webrtc 实现这三种采集形式。
webrtc 中屏幕采集的源码在 webrtc/src/modules/desktop_capture/ 目录下。在 desktop_capture 目录中的 desktop_capturer.h 中定义了 DesktopCapturer 类,DesktopCapturer 类形象了屏幕采集要用到的接口。windows 平台的屏幕采集实现,在 webrtc/src/modules/desktop_capture/win 目录下,其中有 ScreenCapturerWinGdi 类,ScreenCapturerWinMagnifier 类,DesktopAndCursorComposer 类,WindowCapturerWinGdi 类,WgcCapturerWin 类。这些实现类,别离实现了 Windows 平台的屏幕采集和窗口采集性能。
全屏采集
ScreenCapturerWinGdi 类只实现了单纯的屏幕采集性能,如果须要在全屏采集时过滤掉指定的窗口,则须要应用 ScreenCapturerWinMagnifier 类,通过 SetExcludedWindow 接口设置须要过滤的窗口。ScreenCapturerWinMagnifier 类只实现了过滤窗口的性能,如果须要在过滤窗口的同时还要显示鼠标地位,就必须应用 DesktopAndCursorComposer 类,DesktopAndCursorComposer 类实现了将鼠标地位与屏幕图像合并的性能。
窗口采集
WindowCapturerWinGdi 类最早实现了采集指定窗口的性能,然而对于启用了硬件加速的窗口,则无奈采集到窗口内的内容,只能采集到窗口的边框。在最新版本的 webrtc 中,提供了 WgcCapturerWin 类,WgcCapturerWin 实现了采集全屏和采集窗口性能,重要的是,WgcCapturerWin 能够采集开启了硬件加速的窗口,比方 chrome 浏览器。
采集区域
DesktopCapturer 类没有提供采集指定区域的接口,所以,须要在 DesktopCapturer 类中增加一个非纯虚函数,函数承受四个参数,别离是指定区域的左上角坐标 x 和 y,还有区域大小 width 和 height。而后再创立一个继承 ScreenCapturerWinGdi 的新类,而后重载 CaptureFrame 办法,能够拷贝 ScreenCapturerWinGdi 类中的 CaptureFrame 实现,而后把采集的区域指定为自定义的区域(把原来的全屏区域批改为自定义的区域)。这样就实现了采集指定区域。
<u> 趟过的坑:指定区域的 width 最好为 16 的整数倍,不能够为奇数。height 最好是 2 的整数倍。</u>
屏幕共享流程
1. 创立 DesktopCapturer 实例,能够依据需要创立不同的 DesktopCapturer 实现类,比方 ScreenCapturerWinMagnifier 类或者 DesktopAndCursorComposer 类或者 WgcCapturerWin 类。
2. 获取屏幕 ID 列表或窗口 ID 列表。
3. 指定要采集的屏幕 ID 或窗口 ID 列表。
4. 注册数据回调,开始采集。
5. 将回调中的屏幕图像编码传输。
<u> 趟过的坑:DesktopCapturer 实例肯定要在同一个线程内创立,初始化和销毁。</u>
代码示例
#ifndef __RCRTC_DESKTOP_CAPTURER__
#define __RCRTC_DESKTOP_CAPTURER__
#include "api/video/i420_buffer.h"
#include "libyuv/convert.h"
#include "libyuv/video_common.h"
#include "media/base/video_broadcaster.h"
#include "media/base/video_common.h"
#include "modules/desktop_capture/desktop_and_cursor_composer.h"
#include "rtc_base/thread.h"
#include "system_wrappers/include/sleep.h"
#include "webrtc_desktop_capturer.h"
typedef void (*desktop_capture_frame_callback)(int width,
int height,
int y_stride,
int u_stride,
int v_stride,
const uint8_t* y,
const uint8_t* u,
const uint8_t* v,
void* context);
class RcrtcDesktopCapturer : public webrtc::DesktopCapturer::Callback {
public:
RcrtcDesktopCapturer(const std::map<std::string, std::string>& opts) {}
inline ~RcrtcDesktopCapturer() override {}
// overide webrtc::DesktopCapturer::Callback
void OnCaptureResult(webrtc::DesktopCapturer::Result result,
std::unique_ptr<webrtc::DesktopFrame> frame) override {if (result == webrtc::DesktopCapturer::Result::SUCCESS) {int width = frame->size().width();
int height = frame->size().height();
rtc::scoped_refptr<webrtc::I420Buffer> I420buffer =
webrtc::I420Buffer::Create(width, height);
const int conversionResult = libyuv::ConvertToI420(frame->data(), 0, I420buffer->MutableDataY(), I420buffer->StrideY(),
I420buffer->MutableDataU(), I420buffer->StrideU(),
I420buffer->MutableDataV(), I420buffer->StrideV(), 0, 0,
// width, height,
I420buffer->width(), I420buffer->height(), I420buffer->width(),
I420buffer->height(), libyuv::kRotate0, ::libyuv::FOURCC_ARGB);
if (conversionResult >= 0) {
webrtc::VideoFrame videoFrame(I420buffer,
webrtc::VideoRotation::kVideoRotation_0,
rtc::TimeMicros());
rtc::scoped_refptr<webrtc::I420BufferInterface> buffer(videoFrame.video_frame_buffer()->ToI420());
_frameCallback(buffer->width(), buffer->height(), buffer->StrideY(),
buffer->StrideU(), buffer->StrideV(), buffer->DataY(),
buffer->DataU(), buffer->DataV(), this->_userContext);
}
}
}
void setExccludeWindow(webrtc::DesktopCapturer::Source windowId) {_excludeWindowList.push_back(windowId);
}
void CaptureThread() {
webrtc::DesktopCaptureOptions opts =
webrtc::DesktopCaptureOptions::CreateDefault();
opts.set_allow_use_magnification_api(true); // 设置过滤窗口选项
// 应用 DesktopAndCursorComposer 能够采集鼠标
std::unique_ptr<webrtc::DesktopCapturer> capturer =
std::unique_ptr<webrtc::DesktopCapturer>(
new webrtc::DesktopAndCursorComposer(webrtc::DesktopCapturer::CreateScreenCapturer(opts), opts));
// 设置开始采集状态
capturer->Start(this);
// 设置要过滤的窗口
for (auto source : _excludeWindowList) {capturer->SetExcludedWindow(source.id);
}
while (_isrunning) {webrtc::SleepMs(_msPerFrame);
// 采集桌面图像
capturer->CaptureFrame();}
}
bool Start() {
_isrunning = true;
_capture_thread = rtc::Thread::Create();
_capture_thread->Start();
_capture_thread->PostTask(RTC_FROM_HERE, [&] {CaptureThread(); });
}
void Stop() {if (_isrunning) {
_isrunning = false;
_capture_thread->Stop();
_capture_thread.reset();}
}
public:
int _msPerFrame = 100; // 100 毫秒采集一次,每秒钟采集 10 帧
webrtc::DesktopCapturer::SourceList _excludeWindowList; // 须要过滤的窗口列表
desktop_capture_frame_callback _frameCallback = nullptr; // 视频输入回调
void* _userContext = nullptr;
private:
std::unique_ptr<rtc::Thread> _capture_thread;
bool _isrunning = false;
};
#endif
总结
如果理清了 webrtc 屏幕共享的脉络,整个过程还不算太简单。然而,如果一开始没有对 webrtc 的 屏幕共享有一个概览,冒然逐渐趟坑,说不准就会卡在某处,无奈后退。心愿本文对正筹备利用 webrc 屏幕共享的人有所帮忙。
融云 RCRTC C++ SDK 曾经实现了 Windows 的屏幕共享性能,并且解决了很多产品需要层面的细节。有趣味的同学,能够关注咱们的 SDK,关注融云。
祝大家工作顺利!