【屏幕分享】WebRTC中的实现

前言

WebRTC中桌面采集Desktop Capture总体上分为两类:屏幕和窗口,分别对应下面两个类型

  • 屏幕捕获 - webrtc::DesktopCapturer::CreateScreenCapturer (静态方法)
  • 窗口捕获 - webrtc::DesktopCapturer::CreateWindowCapturer(静态方法)

Windows平台

屏幕采集

Windows上使用CreateRawScreenCapturer创建采集对象,具体实现在screen_capturer_win.cc中。

屏幕捕获根据创建时设置选项的不同,可以有多种层级的对象嵌套,嵌套是为了尽可能地使用一些高级特性,比如硬件加速,窗口过滤,黑屏检测,以及鼠标捕捉。

根据创建时设置的参数,最多可能创建的层级如下:

  1. ScreenCapturerWinGdi 屏幕/桌面采集一定会创建GDI的这个类,这个是最后兜底的方案,GDI是Windows平台上兼容性最好的,当然并不是可以处理所以场景的。
  2. ScreenCapturerWinDirectx 如果选项中允许开启DX(set_allow_directx_capturer(true)),就创建。
  3. BlankDetectorDesktopCapturerWrapper 与DX的方案配套的,用来检测采集到的是否为黑屏数据
  4. ScreenCapturerWinMagnifier 使用放大镜的方案(set_allow_use_magnification_api(true)),默认不开启。这个可以排除某些窗口来完成采集屏幕/桌面。
  5. DesktopAndCursorComposer 控制是否要采集鼠标光标
  6. DesktopCapturerDifferWrapper 控制是否要开启逐帧对比,通过对比相邻两帧,找出二者之间的变化区域。(内部使用了SSE指令做数据块的对比,在文件differ_vector_sse2.h/.cc中)

上面6类对象,通过一个工具类 FallbackDesktopCapturerWrapper 来做包装,完成失败回退的动作。

比如说 使用FallbackDesktopCapturerWrapper(main_capturer, secondary_capturer)包装的两个capturer,如果main_capturer失败的话,就是用secondary_capturer来完成。在设置参数的时候也是二者都会被设置到,获取值的时候,按照优先级依次获取。

而创建采集器的选项 DesktopCaptureOptions 支持的参数有:

  • set_allow_directx_capturer
  • set_allow_use_magnification_api
  • set_allow_capture_mousecursor
  • set_detect_updated_region

创建好上述对象之后,接下来就是指定SourceId进行采集了,以及做好采集回调工作了。这个在DesktopCapturer类中对应:

  • Start(CallBack* callback); 设置回调对象
  • CaptureFrame(); 采集的主函数
  • SelectSource(SourceId id); 选择采集屏幕/虚拟桌面/窗口
  • Callback::OnCaptureResult 回调接口
// 具体抓一帧数据的实现,注意内存分配的方式
void ScreenCapturerWinGdi::CaptureFrame() {
  TRACE_EVENT0("webrtc", "ScreenCapturerWinGdi::CaptureFrame");
  int64_t capture_start_time_nanos = rtc::TimeNanos();

  queue_.MoveToNextFrame();
  RTC_DCHECK(!queue_.current_frame() || !queue_.current_frame()->IsShared());

  // Make sure the GDI capture resources are up-to-date.
  PrepareCaptureResources();

  if (!CaptureImage()) {
    RTC_LOG(WARNING) << "Failed to capture screen by GDI.";
    callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr);
    return;
  }

  // Emit the current frame.
  std::unique_ptr<DesktopFrame> frame = queue_.current_frame()->Share();
  frame->set_dpi(DesktopVector(GetDeviceCaps(desktop_dc_, LOGPIXELSX),
                               GetDeviceCaps(desktop_dc_, LOGPIXELSY)));
  frame->mutable_updated_region()->SetRect(
      DesktopRect::MakeSize(frame->size()));
  frame->set_capture_time_ms((rtc::TimeNanos() - capture_start_time_nanos) /
                             rtc::kNumNanosecsPerMillisec);
  frame->set_capturer_id(DesktopCapturerId::kScreenCapturerWinGdi);
  callback_->OnCaptureResult(Result::SUCCESS, std::move(frame));
}

应用采集

Windows上应用采集使用的是CreateRawWindowCapturer创建采集对象,具体实现在desktop_capturer.cc中。

根据选项,最多有下面几层:

  1. WindowCapturerWin
  2. DesktopAndCursorComposer
  3. DesktopCapturerDifferWrapper

Mac平台

Mac上的取屏比Windows上的简单很多。

屏幕采集

创建屏幕采集对象的接口与Windows相同,只不过是在.mm文件中实现的。

接口DesktopCapturer::CreateRawScreenCapturer在screen_capturer_darwin.mm中实现:

std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateScreenCapturer(
    const DesktopCaptureOptions& options) {
  std::unique_ptr<DesktopCapturer> capturer = CreateRawScreenCapturer(options);
  if (capturer) {
      if (options.allow_capture_mousecursor()) {
          capturer.reset(new DesktopAndCursorComposer(std::move(capturer),options));
      }
      if (options.detect_updated_region()) {
          capturer.reset(new DesktopCapturerDifferWrapper(std::move(capturer)));
      }
  }

  return capturer;
}

所谓的raw screen capturer就是一个ScreenCapturerMac对象。抓屏的方法:ScreenCapturerMac::CaptureFrame在文件screen_capturer_mac.mm中实现。

取屏的几个核心的方法是Mac上CG库提供的:

  1. CGWindowListCreateImageFromArray 从一组Windows id中生成一张图片
  2. CGDisplayCreateImage 获取指定显示器的图片

应用采集

DesktopCapturer::CreateRawWindowCapturer接口在window_capturer_mac.mm文件中实现。code也很简单:

std::unique_ptr<DesktopCapturer> DesktopCapturer::CreateRawWindowCapturer(
    const DesktopCaptureOptions& options) {
  return std::unique_ptr<DesktopCapturer>(
      new WindowCapturerMac(options.full_screen_chrome_window_detector(),
                            options.configuration_monitor()));
}

WindowCapturerMac

核心方法是 CGWindowListCreateImage,为指定的一组窗口生成图片。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值