适用于 MediaStreamTrack 的可插入流

MediaStreamTrack 的内容以流的形式公开,可用于操纵或生成新内容

背景

Media Capture and Streams API 的上下文中,MediaStreamTrack 接口表示流中的单个媒体轨道;通常,这些是音轨或视频轨道,但也可能存在其他轨道类型。MediaStream 对象包含零个或多个 MediaStreamTrack 对象,表示各种音轨或视频轨道。每个 MediaStreamTrack 可以有一个或多个频道。声道表示媒体流的最小单位,例如与给定扬声器(如立体声音轨中的左声道或右声道)关联的音频信号。

什么是 MediaStreamTrack 的可插入流?

MediaStreamTrack的可插入流背后的核心思想是将MediaStreamTrack的内容公开为一组(由 WHATWG Streams API 定义)。您可以操纵这些流来引入新组件。

授予开发者直接访问视频(或音频)流的权限后,他们便可以直接对该流进行修改。相比之下,如果使用传统方法实现相同的视频处理任务,开发者需要使用 <canvas> 元素等中介。(如需详细了解此类流程,请参阅视频 + Canvas = 神奇等文章。)

浏览器支持

从 Chrome 94 开始,支持 MediaStreamTrack 的可插入流。

使用场景

可插播的视频流适用于 MediaStreamTrack,其使用场景包括但不限于:

  • 视频会议小工具,例如“搞笑帽子”或虚拟背景。
  • 语音处理,例如软件声码器

如何为 MediaStreamTrack 使用可插播的视频流

功能检测

您可以按如下方式检测可插入的媒体流是否支持 MediaStreamTrack

if ('MediaStreamTrackProcessor' in window && 'MediaStreamTrackGenerator' in window) {
  // Insertable streams for `MediaStreamTrack` is supported.
}

核心概念

MediaStreamTrack 的可插入流基于之前由 WebCodecs 提出的概念,并在概念上将 MediaStreamTrack 拆分为两个组件:

  • MediaStreamTrackProcessor,它会使用 MediaStreamTrack 对象的来源并生成媒体帧(具体为 VideoFrameAudioFrame)对象流。您可以将此视为一种轨道接收器,能够将轨道中的未编码帧作为 ReadableStream 公开。
  • MediaStreamTrackGenerator,用于使用媒体帧流并公开 MediaStreamTrack 接口。它可以提供给任何接收器,就像 getUserMedia() 中的轨道一样。它以媒体帧作为输入。

MediaStreamTrackProcessor

MediaStreamTrackProcessor 对象公开了一个属性:readable。它允许从 MediaStreamTrack 读取帧。如果轨道是视频轨道,则从 readable 读取的块将是 VideoFrame 对象。如果轨道是音轨,则从 readable 读取的块将是 AudioFrame 对象。

MediaStreamTrackGenerator

MediaStreamTrackGenerator 对象同样公开了一个属性 writable,该属性是一个 WritableStream,可用于将媒体帧写入 MediaStreamTrackGenerator(本身是一个 MediaStreamTrack)。如果 kind 属性为 "audio",则该流接受 AudioFrame 对象,但会因任何其他类型而失败。如果 kind 为 "video",则该流接受 VideoFrame 对象,并会因任何其他类型而失败。当帧写入 writable 时,系统会自动调用帧的 close() 方法,以便无法再通过 JavaScript 访问其媒体资源。

MediaStreamTrackGenerator 是一种轨道,通过将媒体帧写入其 writable 字段,可以为该轨道实现自定义来源。

综合应用

核心思想是创建如下处理链:

平台轨道 → 处理器 → 转换器 → 生成器 → 平台接收器

以下示例展示了条形码扫描器应用的此链,该应用会在实时视频流中突出显示检测到的条形码。

const stream = await getUserMedia({ video: true });
const videoTrack = stream.getVideoTracks()[0];

const trackProcessor = new MediaStreamTrackProcessor({ track: videoTrack });
const trackGenerator = new MediaStreamTrackGenerator({ kind: 'video' });

const transformer = new TransformStream({
  async transform(videoFrame, controller) {
    const barcodes = await detectBarcodes(videoFrame);
    const newFrame = highlightBarcodes(videoFrame, barcodes);
    videoFrame.close();
    controller.enqueue(newFrame);
  },
});

trackProcessor.readable.pipeThrough(transformer).pipeTo(trackGenerator.writable);

const videoBefore = document.getElementById('video-before');
const videoAfter = document.getElementById('video-after');
videoBefore.srcObject = stream;
const streamAfter = new MediaStream([trackGenerator]);
videoAfter.srcObject = streamAfter;

演示

您可以在桌面版或移动版浏览器中观看上方部分中的 QR 码扫描器演示。将二维码放在摄像头前,应用会检测到并突出显示该二维码。您可以在 GitHub 上查看应用的源代码。

在桌面浏览器标签页中运行的二维码扫描器,显示用户拿着手机在笔记本电脑摄像头前检测到的突出显示的二维码。

安全性和隐私权注意事项

此 API 的安全性依赖于 Web 平台中的现有机制。由于数据是通过 VideoFrameAudioFrame 接口公开的,因此这些接口用于处理源污染数据的规则也适用。例如,由于目前对访问此类资源存在限制,因此无法访问来自跨源资源的数据(例如,无法访问跨源图片或视频元素的像素)。此外,对来自摄像头、麦克风或屏幕的媒体数据的访问也需要获得用户授权。此 API 公开的媒体数据已通过其他 API 提供。

反馈

Chromium 团队希望了解您在使用可插入流时遇到的问题(针对 MediaStreamTrack)。

介绍 API 设计

API 是否存在未按预期运行的情况?或者,是否有缺少的方法或属性需要您来实现自己的想法?您对安全模型有任何疑问或意见吗?在相应的 GitHub 代码库中提交规范问题,或在现有问题中添加您的想法。

报告实现方面的问题

您是否发现了 Chromium 的实现存在 bug?或者实现是否与规范不同? 请前往 new.crbug.com 提交 bug。请务必尽可能详细地说明问题,提供简单的重现说明,并在组件框中输入 Blink>MediaStream

显示对 API 的支持

您是否计划为 MediaStreamTrack 使用可插入的视频流?您的公开支持有助于 Chromium 团队确定功能优先级,并向其他浏览器供应商展示支持这些功能的重要性。

使用 #ChromiumDev 标签向 @ChromiumDev 发送推文,告诉我们您在何处以及如何使用它。#InsertableStreams

致谢

MediaStreamTrack 规范的可插入流由 Harald AlvestrandGuido Urdaneta 编写。本文由 Harald Alvestrand、Joe MedleyBen WagnerHuib KleinhoutFrançois Beaufort 审核。主打图片由 Unsplash 上的 Chris Montgomery 提供。