从任何元素捕获视频流

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);

与元素不同,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 的源试用,开发者可借助此功能让自己网站的访问者从真实用户那里收集数据。如需详细了解源试用,请参阅开始试用

安全和隐私设置

如需了解安全权衡,请参阅 Element Capture 规范的隐私和安全注意事项部分。

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

演示

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

反馈

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

请说明设计

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

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

实现方面存在问题?

您是否发现了 Chrome 实现中的 bug?或者,实现是否与规范不同?

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

致谢

照片由 Paul Skorupskas 拍摄于 Unsplash 网站