在 WebRTC 应用中,通过 Capture Handle 识别共享标签页

我一直不太赞同在演示的同时共享屏幕。大家进入视频会议应用程序,跟大家打招呼,然后开始分享自己在另一个标签或窗口的ppt。那你要看什么呢?ppt吗?但我更想面对观众并与其互动。确实,有些工具可以让你预览所分享的内容,但你仍然需要来回滑动每张ppt。理应采用一个更好的办法,那就是capture handle。

Capture Handle是origin trial中一个用于屏幕共享(getDisplayMedia)的新的API。它可以让屏幕共享app识别,与用户选择的标签相协调。

Elad Alon是一位对接webrtc.org团队的谷歌员工。他负责整理capture handle规范,并推动其通过W3C的标准化过程。

在这篇文章中,Elad向我们介绍了capture handle,并列举了一些关于防止“镜室 (hall of mirrors)效应”的例子。另外他也回答了我在文章开头所说,屏幕共享时切换幻灯片的问题。capture handle也有很多其他用途。下面会对此进行详述。

在过去的一年里,屏幕共享逐渐成为大家生活中必不可少的一部分。其原因自然不言而喻。对于基于Web的产品,该操作通常是通过调用getDisplayMedia来实现的。一旦getDisplayMedia被调用,浏览器就会向用户展示他们自己的媒体选择器。

不同浏览器中,getDisplayMedia示例页面上的屏幕共享选择器

目前,该API允许捕获三种不同的界面展示选项——屏幕、窗口或标签。用户拥有该选择权。(这种选择可能受到用户选择的浏览器的限制。比如Firefox还不支持标签捕捉,而Safari只提供全屏捕捉。)

getDisplayMedia返回一个Promise。当用户做出选择后,Promise被解决,应用程序可以开始使用返回的MediaStream,该stream有一个视频轨道,可能还有一个音频轨道。但是,应用程序还能较容易地发现捕获界面的哪些内容呢?为什么需要应用程序这样做呢?让我们来解决这两个问题。

识别捕获的显示界面

以前,应用程序只能区分屏幕/窗口/标签捕获。

  • 对于Safari来说确实是这样,因为它只支持屏幕捕捉。
  • Chrome允许通过MediaTrackSettings.displaySurface查询捕获街面。
  • Firefox以MediaStreamTrack.label的形式公开了捕获窗口的标题。屏幕捕捉会指定一个特殊的字符串,比如“Primary Monitor”。

所以,并没有标准的方法来真正识别捕获的web应用。被捕获的标签是一个YouTube视频?还是一个演示文稿?如果是演示文稿的话,它又来自哪个应用呢?如果捕获的应用程序和被捕获的应用程序甚至不能识别对方,那么它们如何进行协作?是否应该通过嵌入二维码的方式进行隐写

输入“Capture Handle”。让我们看几个示例来说明Capture Handle如何工作。

检测自我抓取

我们可以从最简单的Capture Handle示例讲起——一个单一标签(应用程序里预期是两个)。设想现在有一个将捕获的视频呈现给本地用户的应用程序。自我捕获会导致镜厅效应。

显示被捕获屏幕时出现的镜室效应

如果web应用能够检测到这一点,它就可以避免将视频回放给本地用户,从而避免令人不适的这种效果。

const TOTALLY_RANDOM_ID = “42”;  // Or actually randomize...
const ownId = TOTALLY_RANDOM_ID;

// Set the capture handle of the web application.
navigator.mediaDevices.setCaptureHandleConfig(
  { handle: ownId, permittedOrigins: ["*"] }
);
// ...
const stream = await navigator.mediaDevices.getDisplayMedia();
const [track] = stream.getVideoTracks();

// Check to see if the captured tab has the same handle.
const isSelfCapture =
    track.getCaptureHandle &&
    track.getCaptureHandle() &&
    track.getCaptureHandle().handle == ownId;
// Then mitigate the hall-of-mirrors as you see fit,
// e.g. hide the element using CSS.

在这种情况下,Web应用程序可以很轻易地在当前标签上设置一个ID。然后这个captureHandle().handle可以用来检查用户是否在屏幕共享选择器中选择了他们自己的标签。

点击此处,你可以看到这个概念的完整演示。

引导协作

如果一个应用程序可以识别它正在捕获的应用程序,它就可以与之协作。它们之间无论使用多传统的方法,都可以进行消息交流,并添加包括任何种类的语义。例如,一个VC应用可以远程控制幻灯片。

如需观看演示请点击此处。请注意,它是由一个C++开发人员制作的,并不是一个web开发人员。TL;DR是指,被捕获的应用程序设置一个handle,而捕获的应用程序从中提取会话ID。可以想象一个名为Slides 3000的幻灯片web应用程序:

////////////////////////////////////////
// Captured application (Slides 3000) //
////////////////////////////////////////

function getLoonySessionId() {
  ...  // Returns some ID which is meaningful using loonyAPI.
}

function onPageLoaded() {
  // Expose and add info as you like, including the origin.
  ...
  setCaptureHandleConfig({
    exposeOrigin: true,
    handle: JSON.stringify({
      description: "See slides-3000.com for our API.",
      protocol: "loonyAPI",
      version: "1.983",
      sessionId: getLoonySessionId(),
    }),
    permittedOrigins = ['*']
  });
  ...
}

和一个名为VC-MAX的视频通话web应用:

////////////////////////////////////
// Capturing application (VC-MAX) //
////////////////////////////////////

async function startCapture() {
  // ...
  const stream = await navigator.mediaDevices.getDisplayMedia();
  const [track] = stream.getVideoTracks();
  if (track.getCaptureHandle) {  // Feature detection.
    // Subscribe to notifications of the capture-handle changing.
    track.oncapturehandlechange = (event) => {
      onNewCaptureHandle(event.captureHandle());
    };
    // Read the current capture-handle.
    onNewCaptureHandle(track.getCaptureHandle());
  }
  ...
}

function onNewCaptureHandle(captureHandle) {
  if (captureHandle.origin == 'slides-3000.com') {
    const parsed = JSON.parse(captureHandle.handle);
    onNewSlides300Session(parsed.protocol, parsed.version, parsed.sessionId);
  }
}

function onNewSlides300Session(protocol, version, sessionId) {
  if (protocol != "loonyAPI" || version > "2.02") {
    return;
  }
  // Exposes prev/next buttons to the user. When clicked, these send
  // a message to some REST API, where |sessionId| indicates that the
  // message has to be relayed to the Slides 3000 session in question.
  ExposeSlides300Controls(sessionId);
}

选择你自己的信令

capture handle可以提供识别,但并不提供通信。它并不涉及两个标签之间如何交换消息。你可以设想是BroadcastChannel,或者共享云基础设施被启用,即信令服务器(在比通常使用WebRTC情景更广泛的情况里)。所有合理的通信手段都有一个共同特点——它们要求捕获者知道被捕获的应用程序的ID,而这正是capture handle所能提供的。

这为开发者实现其应用提供了方式上的灵活性。比如,如果用户打开多个幻灯片标签,多个标签是否都会被指示翻到下一张幻灯片呢?不可思议!

使用capture handle优化capture参数

假设有两个用户,一个用户正在和朋友分享一个1080p的预告片,提议大家一起看这部电影。另一个用户正在向同事们展示一个基本都是静态的文件。这两个用户都使用了同一种视频通话应用程序来分享另一个标签。那么在这两个capture场景中,是否应该使用相同的帧率和分辨率呢?

将来,可以直接提示内容类型的机制才是主流。但作为现在的一种权宜之计,Capture Handle能帮助用户检查标签的来源是否是已知的视频服务网站、已知的生产力套装等。然后,应用程序就可以设置帧速率、分辨率属性以及contentHint了。

Capture Handle何时能用?

从M92版开始,Chrome进行Capture Handle origin trial

M92版和M93版相比,API有细微差别:

除此之外,Chrome目前没有计划进行额外的修改。如果有,我们可以在变更日志中找到。然而,我们确实在计划进行一项扩展,将该API扩展到窗口级capture。

Chrome浏览器正在收集公共web开发人员的支持意见,以说服其他浏览器供应商了解这一功能的重要性。

文章地址:https://webrtchacks.com/capture-handle/

原文作者:Elad Alon

填写常用邮箱,接收社区更新

WebRTC 中文社区由

运营