从任何元素捕获视频流

François Beaufort
François Beaufort

借助 Screen Capture API,您可以截取当前整个标签页。借助 Element Capture API,您可以捕获并记录特定的 HTML 元素。它会将整个标签页的屏幕截图转换为特定 DOM 子树的屏幕截图,只捕获目标元素的直接后代。换句话说,它会剪裁并移除遮挡和被遮挡的内容。

为何使用元素捕获功能?

考虑视频会议应用的要求有助于您了解元素捕获功能的用途。如果您使用的视频会议应用允许您在 iframe 中嵌入第三方应用,那么您有时可能需要将该 iframe 截取为视频,并将其传输给远程参与者。

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

调用 getDisplayMedia() 并让用户选择当前标签页会传输整个当前标签页。这样很可能会将人们自己的视频传输回他们。您可以使用区域截图功能将其剪裁掉。

但是,如果演示者与视频会议应用互动,而某些内容(例如下拉列表)恰好在要截取的内容上方绘制,该怎么办?

一个下拉列表的屏幕截图,其中列出了要拍摄的内容。
系统会在要截取的内容上方显示一个下拉列表。

“区域捕获”对您没有帮助。下拉菜单的部分内容可能会显示在远程参与者的屏幕上。

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

由于区域截取功能会以这种方式捕获元素的部分内容(称为遮挡内容),因此会导致多种问题:

  • 遮挡内容可能会妨碍用户查看用户原本打算分享的内容。
  • 遮挡的内容可能是私密内容(例如聊天通知)。
  • 遮挡内容可能会让用户感到困惑。(例如,应用重新布局可能会短暂地将远程参与者的视频显示在所捕获的目标上。)

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

目标元素的屏幕截图,其中没有显示下拉列表。
François 看不到 Elad 的下拉列表。

如何使用 Element Capture?

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 中启用该 API。

此功能还将从 Chrome 121 开始在桌面设备上进入来源试用阶段,届时开发者可以为其网站的访问者启用此功能,以便从真实用户那里收集数据。如需详细了解来源试用,请参阅开始使用来源试用

安全和隐私设置

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

Chrome 浏览器会在捕获的标签页的边缘绘制蓝色边框。

演示

您可以在 Glitch 上运行演示版,试用 Element Capture。请务必查看源代码

反馈

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

请说明设计

区域截取功能是否未按预期运行?或者,您是否缺少实现想法所需的方法或属性?对安全模型有疑问或意见?

  • GitHub 代码库中提交规范问题,或者添加您对现有问题的看法。

实现方面存在问题?

您在 Chrome 的实现过程中是否发现了错误?或者,实现是否与规范不同?

  • 请前往 https://new.crbug.com 提交错误。请务必提供尽可能多的细节信息,并提供关于重现错误的简单说明。故障非常适合分享快速简便的重现步骤。

致谢

照片由 Paul SkorupskasUnsplash 上发布