从任何元素捕获视频流

弗朗索瓦·博福
François Beaufort
伊拉德·阿隆
Elad Alon

借助 Screen Capture API,您可以截取整个当前标签页。您可以使用 Element Capture API 捕获和录制特定的 HTML 元素。它将整个标签页的捕获转换为特定 DOM 子树的捕获,仅捕获目标元素的直接后代。换言之,此功能会剪裁并移除被遮挡和被遮挡的内容。

为何使用元素捕获?

考虑视频会议应用的要求有助于您了解元素捕获在哪些情况下非常有用。如果您有一款视频会议应用允许您在 iframe 中嵌入第三方应用,您有时可能需要将该 iframe 捕获为视频,并将其传输给远程参与者。

Chrome 中视频会议通话的屏幕截图。
Elad 在与 François 进行视频会议时使用了一个第三方应用。

调用 getDisplayMedia() 并让用户选择当前标签页会传输整个当前标签页。这可能会将人们自己的视频传回他们。您可以使用区域捕获将此信息裁剪掉。

但是,如果演示者与视频会议应用互动,而有些内容(如下拉列表)在要拍摄的内容之上绘制,该怎么办?

下拉列表的屏幕截图,其中包含用于拍摄的内容。
要拍摄的内容顶部会显示一个下拉列表。

这时区域拍摄功能无法为您提供帮助。下拉列表中的部分内容可能会显示在远程参与者的屏幕上。

已截取下拉列表的屏幕截图。
Elad 的下拉列表会显示在 François 收到的内容顶部。

区域拍摄就是以这种方式捕获部分元素(称为遮挡内容)这一事实,会造成多个问题:

  • 遮挡的内容可能会妨碍用户查看用户想要分享的内容。
  • 遮挡的内容可能是私密内容(可以考虑使用聊天通知)。
  • 遮挡的内容可能会让人感到困惑。(例如,重新布局应用后,远程参与者自己的视频可能会短暂地超出捕获的目标)。

借助 Element Capture API,您可以定位要分享的元素,从而解决了所有这些问题。

视图中没有下拉列表的目标元素的屏幕截图。
François 没有看到 Elad 中的下拉列表。

如何使用元素捕获?

captureTarget 是网页上的一个元素,其中包含用户希望捕获的内容。你希望视频会议 Web 应用截取captureTarget并与远程参与者分享。因此,您可以从 captureTarget 派生 RestrictionTarget。使用此 RestrictionTarget 限制视频轨道后,该视频轨道上的帧将只包含属于 captureTarget 及其直接 DOM 后代的像素。

如果 captureTarget 更改了大小、形状或位置,视频轨道会正常播放,无需来自任一 Web 应用的任何额外输入。遮挡的内容出现、消失或移动,同样无需特殊处理。

请再次查看这些步骤:

首先,允许用户截取当前标签页。

// Ask the user for permission to start capturing the current tab.
const stream = await navigator.mediaDevices.getDisplayMedia({
 preferCurrentTab: true,
});
const [track] = stream.getVideoTracks();

使用您选择的元素作为输入调用 RestrictionTarget.fromElement(),以定义 RestrictionTarget

// Associate captureTarget with a new RestrictionTarget
const captureTarget = document.querySelector("#captureTarget");
const restrictionTarget = await RestrictionTarget.fromElement(captureTarget);

然后,使用 RestrictionTarget 作为输入,对视频轨道调用 restrictTo()。当最后一个 promise 解析后,所有后续帧都将受到限制。

// Start restricting the self-capture video track using the RestrictionTarget.
await track.restrictTo(restrictionTarget);

// Enjoy! Transmit remotely.

深入解析

功能检测

如需检查 RestrictionTarget.fromElement() 是否受支持,请使用以下命令:

if ("RestrictionTarget" in self && "fromElement" in RestrictionTarget) {
  // Deriving a restriction target is supported.
}

派生 RestrictionTarget

将焦点置于名为 captureTarget元素上。如需从中派生 RestrictionTarget,请调用 RestrictionTarget.fromElement(captureTarget)。如果成功,返回的 Promise 将使用新的 RestrictionTarget 对象解析。否则,如果您创建的 RestrictionTarget 对象数量过多,则会被拒绝。

const captureTarget = document.querySelector("#captureTarget");
const restrictionTarget = await RestrictionTarget.fromElement(captureTarget);

与 Element 不同,RestrictionTarget 对象是可序列化的。例如,您可以使用 Window.postMessage() 将其传递给另一个文档。

限制

在捕获标签页时,视频轨道会公开 restrictTo()。捕获当前标签页时,可以使用 null 或派生自当前标签页中元素的任何 RestrictionTarget 来调用 restrictTo()

调用 restrictTo(restrictionTarget) 会使视频轨道变为 captureTarget 的捕获,就像它是单独绘制的,与 DOM 的其余部分无关。系统也会捕获 captureTarget 的所有后代;captureTarget 的同级将从捕获中移除。这会导致轨道上传送的所有帧看起来就像剪裁成了 captureTarget 的轮廓一样,并且任何被遮挡和被遮挡的内容都会被移除。

// Start restricting the self-capture video track using the RestrictionTarget.
await track.restrictTo(restrictionTarget);

调用 restrictTo(null) 可将轨道还原为原始状态。

// Stop restricting.
await track.restrictTo(null);

如果对 restrictTo() 的调用成功,那么当可以保证所有后续视频帧都限制为 captureTarget 时,返回的 Promise 会得到解析。

如果执行失败,promise 会被拒绝。如果 restrictTo() 调用失败,可能的原因如下:

  • restrictionTarget 是在当前捕获的标签页中创建的。(请注意,使用“改为分享此标签页”按钮,用户可以随时更改要截取的标签页。)
  • 如果 restrictionTarget 派生自不存在的元素。
  • 曲目是否有克隆。(请参阅问题 1509418。)
  • 当前曲目不是自拍视频轨道。
  • 如果派生 restrictionTarget 的元素不符合限制条件。

有关自拍的注意事项

当应用调用 getDisplayMedia(),并且用户选择截取应用自己的标签页时,我们将这种行为称为“自拍”。

restrictTo() 方法不仅适用于自行捕获,适用于所有标签页捕获视频轨道。不过,“元素捕获”目前只针对自拍启用。因此,建议您先检查用户是否选择了当前标签页,然后再尝试限制轨道。这可以使用捕获句柄来实现。您也可以使用 preferCurrentTab 请求浏览器促使用户自行拍照。

透明度

应用通过 getDisplayMedia() 获取的视频帧不包含 Alpha 通道。如果应用设置了部分透明的捕获目标,则剥离 Alpha 通道可能会造成一些后果:

  • 颜色可能会有变化。移除 Alpha 通道后,在浅色背景上绘制的部分透明目标元素可能会显得更暗,在深色背景上绘制的目标元素可能会显得更浅。
  • 当 Alpha 通道设为最大值时,用户看不到或察觉不到的颜色,会在删除 alpha 通道后显示。例如,如果透明部分具有 RGBA 代码 rgba(0, 0, 0, 0),这可能会导致捕获的帧中出现意外的黑色区域。
非矩形透明拍摄目标结果的屏幕截图。
非矩形透明拍摄目标视频流(右侧)是一个包含不透明蓝色圆圈的黑色背景矩形。

捕获目标不符合条件

您可以随时开始将航迹限制为任何有效的捕获目标。不过,在某些条件下(例如,当元素或祖先实体为 display:none 时),系统将无法生成帧。一般而言,限制仅适用于所含单个连贯二维矩形区域的元素,其像素可独立于任何父元素或同级元素在逻辑上确定。

确保元素符合限制条件的一个重要考虑因素是,它必须形成自己的堆叠上下文。为确保这一点,您可以指定 isolation CSS 属性,并将其设置为 isolate

<div id="captureTarget" style="isolation: isolate;"></iframe>

请注意,目标元素可以随时在符合限制和不符合限制条件之间切换,例如,当应用更改其 CSS 属性时。应用应负责使用合理的捕获目标,并避免意外更改其属性。如果目标元素不符合条件,则系统根本不会在轨道中发出新帧,直到目标元素再次符合限制条件。

启用元素捕获

在桌面版 Chrome 中,Element Capture API 位于 Element Capture 标志后面,并可通过 chrome://flags/#element-capture 启用。

此外,此功能也开始进入桌面版 Chrome 121 的源试用,以便开发者能够为网站的访问者启用该功能,以便从真实用户那里收集数据。如需详细了解源试用,请参阅开始试用源试用

安全和隐私设置

如需了解安全性方面的权衡取舍,请参阅元素捕获规范的隐私权和安全注意事项部分。

Chrome 浏览器会在已截取的标签页边缘绘制蓝色边框。

演示

你可以在 Glitch 上运行演示,以便体验“元素捕获”功能。请务必查看源代码

反馈

Chrome 团队和网络标准社区希望了解您使用 Element Capture 的体验。

请告诉我们设计情况

是否存在区域拍摄功能无法按预期运行的功能?或者是否缺少一些您需要的方法或属性来实现您的想法?对安全模型有疑问或意见?

  • GitHub 代码库上提交规范问题,或将您的想法添加到现有问题中。

实施时遇到了问题?

您是否发现了 Chrome 实现方面的错误?或者,实现方式是否不同于规范?

  • 请访问 https://new.crbug.com 提交 bug。请务必提供尽可能详细的信息,和简单的重现说明。Glitch 非常适合用于快速轻松地重现问题。

确认

照片由 Paul Skorupskas 提供,由 Unsplash 提供