从任何元素捕获视频流

François Beaufort
François Beaufort

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

为何使用 Element Capture?

考虑视频会议应用的要求有助于您了解 Element Capture 的用途。如果您使用的视频会议应用允许您在 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 属性。应用应使用合理的捕获目标,并避免意外更改其属性。如果目标元素不符合条件,轨道上将不会发出新的帧,直到目标元素再次符合限制条件为止。

启用元素捕获

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

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

安全和隐私设置

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

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

演示

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

反馈

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

请说明设计

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

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

实现方面存在问题?

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

  • 请访问 https://new.crbug.com 提交 bug。请务必提供尽可能详细的信息,并附上简单的重现说明。故障非常适合分享快速简便的重现步骤。

致谢

照片由 Paul Skorupskas 拍摄,选自 Unsplash