媒体源扩展™

W3C 工作草案

关于本文档的更多信息
此版本:
https://www.w3.org/TR/2026/WD-media-source-2-20260807/
最新发布版本:
https://www.w3.org/TR/media-source-2/
最新编辑草案:
https://w3c.github.io/media-source/
历史记录:
https://www.w3.org/standards/history/media-source-2/
提交历史
最新推荐标准:
https://www.w3.org/TR/2016/REC-media-source-20161117/
编辑:
(Apple公司)
Mark Watson (Netflix公司)
前编辑:
(W3C 特邀专家) - 截止至
Jerry Smith (微软公司) - 截止至
Aaron Colwell (谷歌公司) - 截止至
Adrian Bateman (微软公司) - 截止至
反馈:
GitHub w3c/media-source (拉取请求, 新建议, 开放议题)
public-media-wg@w3.org 邮件主题行请使用 [media-source-2] … 消息主题 … (邮件存档)
浏览器支持:
caniuse.com

摘要

本规范扩展了 HTMLMediaElement [HTML], 允许 JavaScript 生成用于播放的媒体流。JavaScript 生成流可满足多种使用场景,如自适应流媒体和直播时移等。

本文档状态

本节描述本文档在发布时的状态。当前 W3C 发布文档及本技术报告最新修订版可在 W3C 标准与草案索引中查阅。

除编辑性更新外,自 W3C 推荐标准 2016年11月发布以来的实质性变化包括:

完整变更列表请参见 提交历史

工作组维护着编辑尚未处理的所有问题报告列表

实现者需注意本规范并不稳定。未参与相关讨论的实现者可能会发现规范发生不兼容变化。有兴趣在本规范进入候选推荐阶段前进行实现的厂商应关注 GitHub 仓库并参与讨论。

本文档由 媒体工作组推荐标准流程发布为工作草案。

作为工作草案发布,并不意味着 W3C 及其成员的认可。

本文档为草稿,可能随时被更新、替换或废止。除“进行中的工作”外,不适宜引用本文件。

本文档由遵循 W3C 专利政策 的小组制作。 W3C 维护着 本小组相关专利公开的公共列表; 该页面还包含专利披露说明。个人如获知某专利涉及 必要权利要求 ,须遵循 W3C 专利政策第6节进行披露。

本文档适用 2025年8月18日 W3C 流程文件

1. 引言

本节为非规范内容。

本规范允许 JavaScript 动态构建 <audio> 和 <video> 的媒体流。它定义了一个 MediaSource 对象,可作为 HTMLMediaElement 的媒体数据源。MediaSource 对象包含一个或多个 SourceBuffer 对象。应用可以把数据分段追加到 SourceBuffer 对象,并能根据系统性能等因素调整追加数据的质量。SourceBuffer 对象中的数据会作为音频、视频和文本轨道缓冲区进行管理,解码后播放。与这些扩展配套使用的字节流规范可在字节流格式注册表 [MSE-REGISTRY] 中查阅。

媒体源管道模型图
1 媒体源管道模型图

1.1 目标

本规范设计时考虑了以下目标:

本规范定义了:

2. 定义

活动轨道 缓冲区

轨道缓冲区,它们为 编码帧提供给 enabledaudioTracksselectedvideoTracks,以及 处于 "showing""hidden" 状态的 textTracks。 所有这些轨道 都与 SourceBuffer 对象相关联,这些对象位于 activeSourceBuffers 列表中。

追加窗口

在追加时用于过滤掉 呈现时间戳范围之外的编码帧。 追加窗口表示具有单一起始时间 和结束时间的单个连续时间范围。呈现时间戳位于此 范围内的编码帧 可以追加到 SourceBuffer,而此 范围外的编码帧 会被过滤掉。追加窗口的起始时间和结束时间分别由 appendWindowStartappendWindowEnd 属性 控制。

编码帧

一种媒体数据单元,它具有 呈现时间戳解码时间戳以及 编码帧持续时间

编码帧 持续时间

一个编码帧的持续时间。对于视频和文本,该持续时间表示 视频帧或文本显示多长 时间。对于音频,该持续时间 表示编码帧中包含的所有采样之和。例如,如果一个音频帧 包含 441 个采样 @44100Hz,则该帧的持续时间为 10 毫秒。

编码 帧结束时间戳

一个编码帧呈现时间戳与其编码帧持续时间之和。它表示紧接在该 编码帧之后的呈现时间戳

编码帧 组

一组彼此相邻且编码帧,其 解码时间戳无间隙地单调递增。由 编码帧处理 算法检测到的不连续以及 abort() 调用都会触发新 编码帧组的开始。

解码时间戳

解码时间戳表示该帧最迟需要完成解码的时间, 假定该帧及任何依赖帧的解码和渲染都是瞬时完成的(这 等于在呈现顺序中最早依赖于该帧的帧的呈现时间戳)。如果 帧能够脱离 呈现顺序进行解码,则解码时间戳必须存在于字节流中或能够从 字节流中推导出来。如果不是 这种情况,用户代理必须运行追加错误算法。如果帧无法脱离呈现 顺序进行解码,并且字节流中不存在解码 时间戳,则解码时间戳等于 呈现时间戳

初始化段

一个字节序列,其中包含解码一系列媒体段所需的全部 初始化信息。这包括编解码器 初始化数据、复用段的 轨道 ID 映射以及时间戳 偏移量(例如编辑 列表)。

字节流格式注册表中的字节流格式规范 [MSE-REGISTRY] 包含特定于格式的示例。

媒体段

一个字节序列,其中包含媒体时间线某一部分的 分组化且带时间戳的媒体数据。 媒体段始终与最近 追加的初始化段相关联。

字节流格式注册表中的字节流格式规范 [MSE-REGISTRY] 包含特定于格式的示例。

MediaSource 对象 URL

MediaSource 对象 URL 是由 createObjectURL() 创建的唯一blob URL。 它用于将 MediaSource 对象附加到 HTMLMediaElement。

这些 URL 与blob URL相同,不同之处在于,该 特性的定义中任何引用 FileBlob 对象的内容,在此也扩展 为同样 适用于 MediaSource 对象。

MediaSource 对象 URL 的是调用 createObjectURL() 期间 this相关 设置对象

例如,MediaSource 对象 URL 的会影响 媒体元素被 canvas 使用的方式。

父级 Media Source

SourceBuffer 对象的父媒体源是 创建该对象的 MediaSource 对象。

呈现 开始时间

呈现开始时间是呈现中最早的时间点,并 指定初始播放 位置最早可能 位置。使用本规范创建的所有呈现,其呈现 开始时间均为 0。

为确定 HTMLMediaElementbuffered 是否包含一个包含当前播放位置的TimeRanges, 实现 可以选择允许位于呈现开始时间或之后、 且位于第一个 TimeRanges 之前的当前播放位置播放第一个 TimeRanges, 前提是该 TimeRanges呈现开始时间之后相当短的时间内开始,例如 1 秒。此许可 是为了适应复用流 通常不会让所有轨道恰好在呈现开始时间开始这一实际情况。 无论此许可如何,实现必须报告实际的缓冲范围。

呈现 区间

一个编码帧的呈现区间,是从其 呈现时间戳到该呈现时间戳加上该编码帧的持续时间之间的时间区间。例如, 如果一个编码帧的呈现 时间戳为 10 秒,且编码帧持续时间为 100 毫秒,则 呈现区间将为 [10-10.1)。请注意,该范围的起点 包含在内,而终点不包含在内。

呈现 顺序

编码帧在呈现中被渲染的顺序。呈现 顺序通过按 编码帧呈现时间戳单调递增排序来实现。

呈现时间戳

对呈现中特定时间的引用。一个 编码帧中的呈现时间戳指示该帧何时被渲染。

随机访问点

媒体段中的一个位置,可以从该位置开始解码和连续播放, 而无需依赖该段中任何先前的数据。对于视频,这通常是 I 帧所在的位置。对于音频,大多数音频帧都可以视为 随机访问点。由于视频轨道中的随机访问点往往分布得更加稀疏, 因而这些点的位置通常被视为复用流的随机 访问点。

SourceBuffer 字节流格式规范

描述 SourceBuffer 实例所接受字节 流格式的特定字节流格式规范。 对于一个 SourceBuffer 对象,其字节流格式规范最初 根据创建该对象时传递给 addSourceBuffer() 调用的 type 进行选择,并且可以通过对 该对象调用 changeType() 来更新。

SourceBuffer 配置

由单个 MediaSource 实例拥有, 并分布在一个或多个 SourceBuffer 对象中的一组特定轨道。

实现必须至少支持 1 个具有以下 配置的 MediaSource 对象:

  • 单个 SourceBuffer,具有 1 条音频轨道和/或 1 条视频轨道。
  • 两个 SourceBuffer,其中一个处理单条音频轨道,另一个处理 单条视频轨道。

MediaSource 对象必须支持上述每种配置,但它们 只需 一次支持一种配置。同时支持多种配置或 其他配置属于实现质量问题。

轨道描述

一种特定于字节流格式的结构,它提供单条轨道的轨道 ID、编解码器 配置以及其他元数据。单个 初始化段中的每个轨道描述都有唯一的轨道 ID。如果 轨道 ID初始化段内不唯一,则用户代理必须运行 追加错误算法。

轨道 ID

轨道 ID 是一种特定于字节流格式的标识符,用于将字节 流的各部分标记为属于特定轨道。轨道 描述中的轨道 ID 标识媒体段的哪些部分属于该轨道。

3. MediaSource 接口

MediaSource 接口表示用于 HTMLMediaElement 的媒体数据源。它会记录此源的 readyState 状态,以及可以用于向演示添加媒体数据的 SourceBuffer 对象列表。MediaSource 对象由 Web 应用创建,然后附加到 HTMLMediaElement。应用通过 SourceBuffer 对象与 sourceBuffers 向该源添加媒体数据。HTMLMediaElement 在播放时会从 MediaSource 对象获取媒体数据。

每个 MediaSource 对象都拥有一个 [[live seekable range]] 内部槽,用于存储一个 标准化的 TimeRanges 对象。该对象在 MediaSource 创建时初始化为空的 TimeRanges 对象,通过 setLiveSeekableRange()clearLiveSeekableRange() 维护,并在 10. HTMLMediaElement 扩展 中用于修改 HTMLMediaElementseekable 行为。

每个 MediaSource 对象都有一个 [[是否曾被 附加]]内部槽,用于存储一个 boolean。当 MediaSource 对象被创建时,它被初始化为 false,并在扩展的 HTMLMediaElement资源获取 算法中,按照附加到媒体元素算法中的描述被设置为 true。扩展的 资源获取 算法使用此内部 槽,以有条件地使通过设置在 HTMLMediaElementsrcObject 属性上的 MediaSourceHandle 附加 MediaSource 的操作失败。

WebIDLenum ReadyState {
  "closed",
  "open",
  "ended",
};
closed
表示该源当前未绑定到媒体元素。
open
该源已被媒体元素打开,可以向 SourceBuffer 对象(在 MediaSourcesourceBuffers 属性中)追加数据。
ended
源仍然附加到媒体元素,但 MediaSourceendOfStream() 已被 调用。
Issue 276: MSE-in-Workers: 建议增加 "closing" readyState 以解释关闭过程中出现的新 `InvalidStateError` 异常 mse-in-workers
WebIDLenum EndOfStreamError {
  "network",
  "decode",
};
network

终止播放并表示发生了网络错误。

JavaScript 应用 应当 使用该状态码以网络错误终止播放。例如,获取媒体数据时发生网络错误。

decode

终止播放并表示发生了解码错误。

JavaScript 应用 应当 使用该状态码以解码错误终止播放。例如,处理带外媒体数据时发生解析错误。

WebIDL[Exposed=(Window,DedicatedWorker)]
interface MediaSource : EventTarget {
    constructor();

    [SameObject, Exposed=DedicatedWorker]
    readonly  attribute MediaSourceHandle handle;
    readonly  attribute SourceBufferList sourceBuffers;
    readonly  attribute SourceBufferList activeSourceBuffers;
    readonly  attribute ReadyState readyState;

    attribute unrestricted double duration;
    attribute EventHandler onsourceopen;
    attribute EventHandler onsourceended;
    attribute EventHandler onsourceclose;

    static readonly attribute boolean canConstructInDedicatedWorker;

    SourceBuffer addSourceBuffer(DOMString type);
    undefined removeSourceBuffer(SourceBuffer sourceBuffer);
    undefined endOfStream(optional EndOfStreamError error);
    undefined setLiveSeekableRange(double start, double end);
    undefined clearLiveSeekableRange();
    static boolean isTypeSupported(DOMString type);
};

3.1 handle 属性

包含一个用于将专用 worker MediaSource 对象通过 srcObject 附加到 HTMLMediaElement 的句柄。此句柄在本 MediaSource 对象多次访问时保持为同一个对象,但每个 MediaSource 对象各自独立。

本规范最终可能会允许在主 Window 上下文中的 MediaSource 对象上公开此属性。如果这样做,则需要在规范中谨慎处理,以防止 潜在的向后不兼容更改,例如访问此属性时抛出异常可能会导致这种情况。

获取时,按以下步骤执行:

  1. 如果本 MediaSource 对象的句柄尚未创建,则执行以下步骤:
    1. created handle 为新建 MediaSourceHandle 对象及相关资源的结果,并与本 MediaSource 内部关联。
    2. 将属性值更新为 created handle
  2. 返回当前属性值的 MediaSourceHandle 对象。

3.2 sourceBuffers 属性

包含与本 SourceBuffer 关联的 MediaSource 对象列表。当 MediaSourcereadyState 等于 "closed" 时,此列表为空。当 readyState 状态变为 "open" 时,可以使用 addSourceBuffer() 向该列表添加 SourceBuffer 对象。

3.3 activeSourceBuffers 属性

包含 sourceBuffers 的子集,这些缓冲区为 selected 视频轨道、 enabled 音频轨道,以及 "showing""hidden" 文本轨道提供数据。

本列表中的 SourceBuffer 对象 必须sourceBuffers 属性中的顺序一致;例如,若仅 sourceBuffers[0] 和 sourceBuffers[3] 在 activeSourceBuffers 中,则 activeSourceBuffers[0] 必须等于 sourceBuffers[0],activeSourceBuffers[1] 必须等于 sourceBuffers[3]。

注意

3.15.5 选中/启用轨道状态变更 一节描述了此属性的更新机制。

3.4 readyState 属性

表示 MediaSource 对象当前状态。创建 MediaSource 时,readyState 必须被设置为 "closed"。

3.5 duration 属性

允许 Web 应用设置演示时长。创建 MediaSource 对象时,时长初始为 NaN。

获取时,按以下步骤执行:

  1. 如果 readyState 属性为 "closed",则返回 NaN 并终止步骤。
  2. 返回当前属性值。

设置时,按以下步骤执行:

  1. 如设置值为负数或 NaN,则抛出 TypeError 异常并终止步骤。
  2. 如果 readyState 属性不是 "open",则抛出 InvalidStateError 异常并终止步骤。
  3. 如果 updating 属性在任意 SourceBuffer(在 sourceBuffers 中)上为 true,则抛出 InvalidStateError 异常并终止步骤。
  4. 使用赋值给该属性的值作为 new duration,运行 时长变更算法。
    注意

    时长变更算法会在当前已缓冲编码帧结束时间更大时自动上调 new duration

    注意

    appendBuffer()endOfStream() 在某些情况下也会更新时长。

3.6 canConstructInDedicatedWorker 属性

返回 true。

注意

此属性用于在主线程和专用 worker 中检测是否支持在专用 worker 创建和使用 MediaSource 对象,减少需要高延迟检测 polyfill(如尝试在专用 worker 创建 MediaSource 对象,尤其在不支持该特性时)的需求。

3.7 addSourceBuffer() 方法

将新的 SourceBuffer 添加到 sourceBuffers

  1. 如果 type 是空字符串,则抛出一个 TypeError 异常并中止 这些步骤。
  2. 如果 type 包含不受支持的 MIME 类型,或者包含一个 与 SourceBuffer 中为其他对象指定的类型 一起使用时不受支持的 MIME 类型,而这些对象位于 sourceBuffers 中,则抛出一个 NotSupportedError 异常并中止这些 步骤。
  3. 如果用户代理无法处理更多 SourceBuffer 对象,或者基于 type 创建 SourceBuffer 会导致不受支持的SourceBuffer 配置, 则抛出一个 QuotaExceededError 异常并中止这些步骤。

    例如,如果媒体 元素已达到 HAVE_METADATA readyState,用户代理可以抛出一个 QuotaExceededError 异常。如果用户代理的媒体引擎不支持在播放期间添加更多轨道, 就可能发生这种情况。

  4. 如果 readyState 属性不处于 "open" 状态, 则抛出一个 InvalidStateError 异常并中止这些步骤。
  5. 如果 thisManagedMediaSource, 则令 buffer 为一个新的 ManagedSourceBuffer 实例; 否则,为一个 SourceBuffer 实例,并带有它们各自关联的 资源。
  6. buffer[[generate timestamps flag]] 设置为 与 type 关联的 媒体源扩展™字节 流格式注册表条目中 “生成时间戳标志”列里的值。
  7. 如果 buffer[[generate timestamps flag]] 为 true,则将 buffermode 设置为 "sequence"。否则,将 buffermode 设置为 "segments"。
  8. buffer 追加thissourceBuffers
  9. 排入一个任务,以在 thissourceBuffers触发一个事件, 其名称为 addsourcebuffer
  10. 返回 buffer

3.8 removeSourceBuffer() 方法

SourceBuffersourceBuffers 移除一个 SourceBuffer。

  1. 如果 sourceBuffer 指定的对象不在 sourceBuffers 中,则抛出一个 NotFoundError 异常 并中止这些 步骤。
  2. 如果 sourceBuffer.updating 属性等于 true,则 执行以下步骤:
    1. 如果 buffer append 算法正在运行,则中止它。
    2. sourceBuffer.updating 属性设为 false。
    3. 排队一个 任务,在 sourceBuffer触发事件,事件名为 abort
    4. 排队一个 任务,在 sourceBuffer触发事件,事件名为 updateend
  3. 执行以下列表中的适当步骤:
    如果 MediaSourceWindow 中构造:
    1. SourceBuffer audioTracks list 等于 sourceBuffer.audioTracks 返回的 AudioTrackList 对象。
    2. media elementsourceBuffer父 media source 所附加到的媒体元素。
    3. HTMLMediaElement audioTracks list 等于 media element 上的 audioTracks 属性返回的 AudioTrackList 对象。
    4. 对于 audio track 中的每一项, 即 SourceBuffer audioTracks list 中的每个音轨,执行以下步骤:
      1. audio track 上的 sourceBuffer 属性设为 null。
      2. audio trackSourceBuffer audioTracks list 中移除。
        Note

        这应当触发 AudioTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 removetrack ,使用 TrackEvent 并将其 track 属性初始化为 audio track,在 SourceBuffer audioTracks list 上触发。 如果 audio trackenabled 属性在该移除步骤开始时为 true, 那么这还应触发 AudioTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 change ,在 SourceBuffer audioTracks list 上触发。

      3. audio trackHTMLMediaElement audioTracks list 中移除。
        Note

        这应当触发 AudioTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 removetrack ,使用 TrackEvent 并将 track 属性初始化为 audio track,在 HTMLMediaElement audioTracks list 上触发。如果 audio trackenabled 属性在该移除步骤开始时为 true, 那么这还应触发 AudioTrackList [HTML] 逻辑, 以排队一个任务触发事件 ,事件名为 change ,在 HTMLMediaElement audioTracks list 上触发。

    否则:
    [[port to main]] 发送一个内部 remove track 消息,其在 Window 中的隐式处理器执行以下步骤:
    1. media elementsourceBuffer父 media source 所附加到的媒体元素。
    2. HTMLMediaElement audioTracks list 等于 AudioTrackList 对象,其由 media element 上的 audioTracks 属性返回。
    3. 对于 HTMLMediaElement audioTracks list 中由 sourceBuffer 创建的每个 audio track,执行以下步骤:
      1. audio trackHTMLMediaElement audioTracks list 中移除。
        Note

        这应当触发 AudioTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 removetrack ,使用 TrackEvent 并将 track 属性初始化为 audio track,在 HTMLMediaElement audioTracks list 上触发。如果 audio trackenabled 属性在该移除步骤开始时为 true, 那么这还应触发 AudioTrackList [HTML] 逻辑, 以排队一个任务触发事件 ,事件名为 change ,在 HTMLMediaElement audioTracks list 上触发。

  4. 执行以下列表中的适当步骤:
    如果 MediaSourceWindow 中构造:
    1. SourceBuffer videoTracks list 等于 VideoTrackList 对象,其由 sourceBuffer.videoTracks 返回。
    2. media elementsourceBuffer父 media source 所附加到的媒体元素。
    3. HTMLMediaElement videoTracks list 等于 VideoTrackList 对象,其由 media element 上的 videoTracks 属性返回。
    4. 对于 video track 中的每一项, 即 SourceBuffer videoTracks list 中的每个视频轨道,执行以下步骤:
      1. video track 上的 sourceBuffer 属性设为 null。
      2. video trackSourceBuffer videoTracks list 中移除。
        Note

        这应当触发 VideoTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 removetrack ,使用 TrackEvent 并将其 track 属性初始化为 video track,在 SourceBuffer videoTracks list 上触发。 如果 video trackselected 属性在该移除步骤开始时为 true, 那么这还应触发 VideoTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 change ,在 SourceBuffer videoTracks list 上触发。

      3. video trackHTMLMediaElement videoTracks list 中移除。
        Note

        这应当触发 VideoTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 removetrack ,使用 TrackEvent 并将 track 属性初始化为 video track,在 HTMLMediaElement videoTracks list 上触发。如果 video trackselected 属性在该移除步骤开始时为 true, 那么这还应触发 VideoTrackList [HTML] 逻辑, 以排队一个任务触发事件 ,事件名为 change ,在 HTMLMediaElement videoTracks list 上触发。

    否则:
    [[port to main]] 发送一个内部 remove track 消息,其在 Window 中的隐式处理器执行以下步骤:
    1. media elementsourceBuffer父 media source 所附加到的媒体元素。
    2. HTMLMediaElement videoTracks list 等于 VideoTrackList 对象,其由 media element 上的 videoTracks 属性返回。
    3. 对于 HTMLMediaElement videoTracks list 中由 sourceBuffer 创建的每个 video track,执行以下步骤:
      1. video trackHTMLMediaElement videoTracks list 中移除。
        Note

        这应当触发 VideoTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 removetrack ,使用 TrackEvent 并将 track 属性初始化为 video track,在 HTMLMediaElement videoTracks list 上触发。如果 video trackselected 属性在该移除步骤开始时为 true, 那么这还应触发 VideoTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 change ,在 HTMLMediaElement videoTracks list 上触发。

  5. 执行以下列表中的适当步骤:
    如果 MediaSourceWindow 中构造:
    1. SourceBuffer textTracks list 等于 TextTrackList 对象,其由 sourceBuffer.textTracks 返回。
    2. media elementsourceBuffer父 media source 所附加到的媒体元素。
    3. HTMLMediaElement textTracks list 等于 TextTrackList 对象,其由 media element 上的 textTracks 属性返回。
    4. 对于 text track 中的每一项, 即 SourceBuffer textTracks list 中的每个文本轨道, 执行以下步骤:
      1. text track 上的 sourceBuffer 属性设为 null。
      2. text trackSourceBuffer textTracks list 中移除。
        Note

        这应当触发 TextTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 removetrack ,使用 TrackEvent 并将其 track 属性初始化为 text track,在 SourceBuffer textTracks list 上触发。如果 mode 属性在 text track 上为 "showing""hidden" 在该移除步骤开始时为真,那么这还应当触发 TextTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 change ,在 SourceBuffer textTracks list 上触发。

      3. text trackHTMLMediaElement textTracks list 中移除。
        Note

        这应当触发 TextTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 removetrack ,使用 TrackEvent 并将其 track 属性初始化为 text track,在 HTMLMediaElement textTracks list 上触发。如果 mode 属性在 text track 上为 "showing""hidden" 在该移除步骤开始时为真,那么这还应当触发 TextTrackList [HTML] 逻辑, 以排队一个任务触发事件 ,事件名为 change ,在 HTMLMediaElement textTracks list 上触发。

    否则:
    [[port to main]] 发送一个内部 remove track 消息,其在 Window 中的隐式处理器执行以下步骤:
    1. media elementsourceBuffer父 media source 所附加到的媒体元素。
    2. HTMLMediaElement textTracks list 等于 TextTrackList 对象,其由 media element 上的 textTracks 属性返回。
    3. 对于 HTMLMediaElement textTracks list 中由 sourceBuffer 创建的每个 text track,执行以下步骤:
      1. text trackHTMLMediaElement textTracks list 中移除。
        Note

        这应当触发 TextTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 removetrack ,使用 TrackEvent 并将其 track 属性初始化为 text track, 在 HTMLMediaElement textTracks list 上触发。如果 text trackmode 属性在 "showing""hidden" 在该移除步骤开始时为真,那么这还应当触发 TextTrackList [HTML] 逻辑, 以排队一个任务触发事件,事件名为 change ,在 HTMLMediaElement textTracks list 上触发。

  6. 如果 sourceBufferactiveSourceBuffers 中,则 将 sourceBufferactiveSourceBuffers 中移除,并排队一个任务 ,在返回的 SourceBufferList触发事件, 事件名为 removesourcebuffer, 该列表由 activeSourceBuffers 返回。
  7. sourceBuffersourceBuffers 中移除,并排队一个任务 ,在返回的 SourceBufferList触发事件, 事件名为 removesourcebuffer。 该列表由 sourceBuffers 返回。
  8. 销毁 sourceBuffer 的所有资源。

3.9 endOfStream() 方法

标志流结束。

  1. 如果 readyState 属性不是 "open" 状态,则抛出 InvalidStateError 异常并终止这些步骤。
  2. 如果 updating 属性在任意 SourceBuffer(在 sourceBuffers 中)上为 true,则抛出 InvalidStateError 异常并终止这些步骤。
  3. 以参数 error 调用 流结束算法。

3.10 setLiveSeekableRange() 方法

更新 [[live seekable range]],该属性用于 10. HTMLMediaElement 扩展,以修改 HTMLMediaElementseekable 行为。

调用该方法时,用户代理必须执行以下步骤:

  1. 如果 readyState 属性不是 "open",则抛出 InvalidStateError 异常并终止这些步骤。
  2. 如果 start 为负数或大于 end,则抛出 TypeError 异常并终止这些步骤。
  3. [[live seekable range]] 设为包含一个范围(起始为 start,结束为 end)的新的 标准化 TimeRanges 对象

3.11 clearLiveSeekableRange() 方法

更新 [[live seekable range]],该属性用于 10. HTMLMediaElement 扩展,以修改 HTMLMediaElementseekable 行为。

调用该方法时,用户代理必须执行以下步骤:

  1. 如果 readyState 属性不是 "open",则抛出 InvalidStateError 异常并终止这些步骤。
  2. 如果 [[live seekable range]] 包含范围,则将 [[live seekable range]] 设为新的空 TimeRanges 对象。

3.12 isTypeSupported() 方法

检查 MediaSource 是否能够为指定的 MIME 类型创建 SourceBuffer 对象。

Note

如果此方法返回 true,这仅表示该 MediaSource 实现能够为指定的 MIME 类型创建 SourceBuffer 对象。 即使如此,如果没有足够的资源来支持添加新的 SourceBufferaddSourceBuffer() 调用仍然应当失败。

Note

此方法返回 true 意味着 HTMLMediaElementcanPlayType() 将返回 "maybe" 或 "probably",因为 MediaSource 支持一种 HTMLMediaElement 明知自己无法播放的类型是没有意义的。

当调用此方法时,用户代理必须执行以下步骤:

  1. 如果 type 为空字符串,则返回 false。
  2. 如果 type 不包含有效的 MIME 类型字符串,则返回 false。
  3. 如果 type 包含 MediaSource 不支持的媒体类型或媒体子类型, 则返回 false。
  4. 如果 type 包含 MediaSource 不支持的编解码器, 则返回 false。
  5. 如果 MediaSource 不支持指定的媒体类型、媒体 子类型和编解码器组合,则返回 false。
  6. 返回 true。

3.13 事件概览

事件名称 接口 触发条件
sourceopen Event MediaSourcereadyState 状态从 "closed" 变为 "open" 或从 "ended" 变为 "open" 时触发。
sourceended Event MediaSourcereadyState 状态从 "open" 变为 "ended" 时触发。
sourceclose Event MediaSourcereadyState 状态从 "open" 变为 "closed" 或 "ended" 变为 "closed" 时触发。

3.14 跨上下文通信模型

当一个 Window HTMLMediaElement 与一个 DedicatedWorkerGlobalScope MediaSource 关联时, 每个上下文都有依赖于另一个上下文信息的算法。

Note

HTMLMediaElement 只暴露给 Window 上下文,但本规范定义的 MediaSource 及相关对象 会在 WindowDedicatedWorkerGlobalScope 上下文中暴露。这使得应用可以在这两种上下文中的任意一种里构造一个 MediaSource 对象, 并使用一个 MediaSource 对象 URL 或一个 MediaSourceHandle 将其附加到 HTMLMediaElement 对象上,且该对象位于 Window 上下文中,如附加到媒体元素 算法所述。MediaSource 对象不是 Transferable; 它只在其被创建的上下文中可见。

本节其余部分描述了一个用于限制 Window 媒体元素与一个 DedicatedWorkerGlobalScope MediaSource 关联时信息延迟的模型。 尽管该模型描述了使用消息传递进行通信, 实现可以选择以可能更快的方式通信,例如使用共享内存和锁。 连接到一个 Window MediaSource 的附加是同步的, 因而无需跨上下文通信就已经拥有这些信息。

一个在 DedicatedWorkerGlobalScope 中构造的 MediaSource 具有一个 [[port to main]] 内部槽, 该槽存储一个在附加时设置、在分离时置空的 MessagePort。 一个 Window [[port to main]] 始终为 null。

由本规范扩展并附加到一个 DedicatedWorkerGlobalScope MediaSourceHTMLMediaElement 也同样具有一个 [[port to worker]] 内部槽, 该槽存储一个 MessagePort, 以及一个 [[channel with worker]] 内部槽, 该槽存储一个 MessageChannel, 两者都在附加时设置、在分离时置空。[[port to worker]][[channel with worker]] 只有在附加到一个 DedicatedWorkerGlobalScope MediaSource 时才非空。

本规范中需要将信息从一个 Window HTMLMediaElement 传送到一个已附加的 DedicatedWorkerGlobalScope MediaSource, 或反过来传送的算法, 会隐式使用这些内部端口向其对应端发送消息,而消息的隐式处理器将按这些算法所描述的步骤运行。

3.15 算法

3.15.1 绑定到媒体元素

MediaSource 附加到媒体元素有不同方式,取决于该 MediaSource 对象是在哪构造的:在 Window 中,还是在 DedicatedWorkerGlobalScope 中:

如果 资源获取算法 的调用中,媒体提供者对象是一个 MediaSource 对象、一个 MediaSourceHandle 对象,或其对象为 MediaSource 的 URL 记录,则令模式为本地(local),跳过 资源获取算法 的第一步(否则可能将模式设为远程),并继续执行该 资源获取算法

注意

预期 资源获取算法 的第一步最终会与“当 URL 记录的对象是媒体提供者对象时选择本地模式”的行为一致。其意图是:如果 HTMLMediaElementsrc 属性或选中的子 sourcesrc 属性在最近一次变更时是与某个 MediaSource 对象 URL 匹配的 blob: URL,那么该 MediaSource 对象会在 资源获取算法 的本地模式逻辑中被用作媒体提供者对象和当前媒体资源。这也意味着当 MediaSource 对象被附加时,包含对 preload 属性考量的远程模式逻辑会被跳过。即便未来在 [HTML] 中做出上述改变,在当前媒体资源是 MediaSource 对象时,仍需要在本地模式逻辑开头执行下列步骤。

资源获取算法 “否则(模式为本地)”部分的开头,执行以下附加步骤。

注意

相对于触发媒体元素资源选择算法的动作,这些步骤是异步的。资源获取算法 会在调用资源选择算法的任务被允许继续并达到稳定状态后运行。实现可以延迟“否则”子句中的步骤,直到 MediaSource 对象已准备好使用。

  1. 如果 资源获取算法 的调用中,媒体提供者对象是一个 MediaSource 对象、一个 MediaSourceHandle 对象,或其对象为 MediaSource 的 URL 记录,则:
    如果媒体提供者对象是一个 URL 记录,其对象为构造于 DedicatedWorkerGlobalScopeMediaSource(例如尝试使用来自 DedicatedWorkerGlobalScopeMediaSourceMediaSource 对象 URL 时)
    运行 资源获取算法媒体数据处理步骤列表如果由于网络错误无法获取任何媒体数据,导致用户代理放弃获取资源 的步骤。
    注意
    这会阻止使用 MediaSource 对象 URL 来进行 DedicatedWorker 的 MediaSource 附加。将 MediaSourcehandle 从 DedicatedWorker 传到 Window 上下文,并赋给媒体元素的 srcObject 属性,是附加此类 MediaSource 的唯一方式。
    如果媒体提供者对象是一个 MediaSourceHandle,其 [[Detached]] 内部槽为 true
    运行 资源获取算法媒体数据处理步骤列表如果由于网络错误无法获取任何媒体数据,导致用户代理放弃获取资源 的步骤。
    如果媒体提供者对象是一个 MediaSourceHandle,且其底层 MediaSource[[has ever been attached]] 内部槽为 true
    运行 资源获取算法媒体数据处理步骤列表如果由于网络错误无法获取任何媒体数据,导致用户代理放弃获取资源 的步骤。
    注意
    这可防止通过 MediaSourceHandle 对同一底层 MediaSource 进行多次加载,即便该 MediaSource 是在 Window 中构造并且此前曾通过 MediaSource 对象 URL 加载。这并不妨碍随后对某个 Window 中的 MediaSource 使用 MediaSource 对象 URL 并成功。
    如果 readyState 不是 "closed"
    运行 资源获取算法媒体数据处理步骤列表如果由于网络错误无法获取任何媒体数据,导致用户代理放弃获取资源 的步骤。
    否则
    1. MediaSource[[has ever been attached]] 内部槽设为 true。
    2. 将媒体元素的 delaying-the-load-event-flag 设为 false。
    3. 如果该 MediaSource 构造于 DedicatedWorkerGlobalScope, 则建立 worker 附加通信并打开该 MediaSource
      1. [[channel with worker]] 设置为一个新的 MessageChannel
      2. [[port to worker]] 设置为 port1 的值,该值来自 [[channel with worker]]
      3. 执行 StructuredSerializeWithTransfer, 将 port2 (来自 [[channel with worker]]) 同时作为值和 transferList 的唯一成员, 并令结果为 serialized port2
      4. MediaSourceDedicatedWorkerGlobalScope排入 一个任务,该任务将
        1. 执行 StructuredDeserializeWithTransfer, 使用 serialized port2DedicatedWorkerGlobalScope领域, 并将 [[port to main]] 设置为 已传输的 port2 值的反序列化克隆,该值来自 [[channel with worker]]
        2. readyState 属性设置为 "open"。
        3. 排入 一个任务,以在 MediaSource触发 一个事件,其名称为 sourceopen
      否则,该 MediaSource 构造于 Window
      1. [[channel with worker]] 设为 null。
      2. [[port to worker]] 设为 null。
      3. [[port to main]] 设为 null。
      4. readyState 属性设为 "open"。
      5. 排队一个任务触发事件,事件名为 sourceopen, 目标为 MediaSource
    4. 继续运行 资源获取算法 的剩余 否则(模式为本地) 步骤,并遵循以下要求:
      1. 资源获取算法媒体数据处理步骤列表 中提到的“下载”“接收的字节数”或“每当当前媒体资源有新数据可用”之类表述,均指通过 appendBuffer() 传入的数据。
      2. 资源获取算法媒体数据处理步骤列表 中与 HTTP 相关的内容不适用,因为当 MediaSource 已附加时,HTMLMediaElement 不会通过 HTTP 获取媒体数据。
注意

已附加的 MediaSource 不会使用 资源获取算法 中的远程模式步骤,因此媒体元素不会触发 “suspend” 事件。尽管本规范的未来版本可能会从附加了 MediaSource 的媒体元素中移除 “progress” 与 “stalled” 事件,但遵循本版本规范的用户代理仍可能触发这两个事件,因为这些对 [HTML] 的引用在本规范实现稳定之后才发生了变更。

3.15.2 从媒体元素解绑

在媒体元素将要切换到 NETWORK_EMPTY排队一个任务触发事件 emptied 到媒体元素的任何情况下,运行以下步骤。这些步骤应当在切换之前立即运行。

  1. 如果 MediaSource 构造于 DedicatedWorkerGlobalScope
    1. 通过向 [[port to worker]] 发送内部 detach 消息,通知 MediaSource
    2. [[port to worker]] 设为 null。
    3. [[channel with worker]] 设为 null。
    4. detach 通知的隐式消息处理程序在 DedicatedWorkerGlobalScope 中的 MediaSource 上运行余下的步骤。
    否则,MediaSource 构造于 Window
    在该 WindowMediaSource 上继续余下的步骤。
  2. [[port to main]] 设为 null。
  3. readyState 属性设为 "closed"。
  4. 如果 thisManagedMediaSource, 则将 streaming 属性设为 false
  5. duration 更新为 NaN。
  6. activeSourceBuffers 中移除所有 SourceBuffer 对象。
  7. 排队一个任务触发事件 removesourcebufferactiveSourceBuffers
  8. sourceBuffers 中移除所有 SourceBuffer 对象。
  9. 排队一个任务触发事件 removesourcebuffersourceBuffers
  10. 排队一个任务触发事件 sourcecloseMediaSource
注意

展望未来,当已附加的(如果有)MediaSource 必须从媒体元素解绑时,预期外部调用并运行此算法。除了或替代媒体元素切换到 NETWORK_EMPTY 的情形外,它可以在 HTMLMediaElement [HTML] 的 load() 操作以及 资源获取算法 失败时被调用。资源获取算法失败是指中止资源获取算法或资源选择算法的那些情况,但“最终步骤”(Final step) [HTML] 不视为触发解绑的失败。

3.15.3 定位

作为 seek 算法 中“等待用户代理确定新播放位置的媒体数据是否可用;若可用,等待其已解码足够数据以播放该位置” 步骤的一部分,运行以下步骤:

  1. 注意

    媒体元素会在 SourceBuffer 对象(位于 activeSourceBuffers 中)中查找包含 媒体分段new playback position。在当前 HTMLMediaElementbuffered 属性值中的任何 TimeRanges 范围内的位置,都已为该位置缓冲了所有必要的媒体分段。

    如果 new playback position 不在 TimeRanges 中(来自 HTMLMediaElementbuffered
    1. 如果 HTMLMediaElementreadyState 属性大于 HAVE_METADATA, 则将其 readyState 设为 HAVE_METADATA
      注意

      按照 HTMLMediaElement ready states [HTML] 的逻辑, HTMLMediaElementreadyState 变化可能触发该元素上的事件。

    2. 媒体元素等待一次 appendBuffer() 调用,使 编码帧处理 算法将 HTMLMediaElementreadyState 设为大于 HAVE_METADATA 的值。
      注意

      Web 应用可以使用 buffered 以及 HTMLMediaElementbuffered 来判定媒体元素恢复播放所需的数据。

    否则
    继续
    注意

    如果 readyState 属性为 "ended", 且 new playback position 位于当前 HTMLMediaElementbuffered 所含的某个 TimeRanges 中,那么即使一个或多个当前选中或启用的轨道缓冲区的最大范围结束时间戳小于 new playback position, 也必须在此继续完成 seek 操作。该情况应仅因 bufferedreadyState 为 "ended" 时的逻辑而出现。

  2. 媒体元素重置所有解码器,并使用相应 初始化分段 的数据对每个解码器进行初始化。
  3. 媒体元素从 活动轨道缓冲区 向解码器馈送 编码帧, 起始于位于 new playback position 之前最近的 随机访问点
  4. 在“等待稳定状态”步骤恢复 seek 算法

3.15.4 SourceBuffer 监控

在播放过程中会定期运行以下步骤,以确保 SourceBuffer 对象(位于 activeSourceBuffers 中)具有 足够数据以确保不间断播放。 对 activeSourceBuffers 的更改也会触发这些步骤,因为它们影响触发状态转换的条件。

具有 足够数据以确保不间断播放 是实现相关的条件,用户代理判定其当前拥有足够数据,可在有意义的一段时间内不发生停顿地播放。该条件被持续评估,以确定何时将媒体元素在 HAVE_ENOUGH_DATA 就绪状态之间进行转换。这些转换指示用户代理何时认为已缓冲足够数据,或分别需要更多数据。

注意

实现可以选择使用已缓冲字节数、已缓冲时间、追加速率或任何其他认为合适的度量来判断何时拥有足够数据。所用度量可以 在播放期间变化,因此 Web 应用应当仅依赖 HTMLMediaElementreadyState 的值来判定是否需要更多数据。

注意

当媒体元素需要更多数据时,用户代理应当及时将其从 HAVE_ENOUGH_DATA 切换到 HAVE_FUTURE_DATA, 以便 Web 应用能够在不造成播放中断的情况下响应。例如,当当前播放位置距离已缓冲数据的末尾还有 500ms 时进行切换, 可为应用留出约 500ms 的时间追加更多数据,以避免播放停顿。

如果 HTMLMediaElementreadyState 属性等于 HAVE_NOTHING
  1. 终止这些步骤。
如果 HTMLMediaElementbuffered 不包含覆盖当前播放位置的 TimeRanges
  1. HTMLMediaElementreadyState 属性设为 HAVE_METADATA
    注意

    按照 HTMLMediaElement ready states [HTML] 的逻辑, HTMLMediaElementreadyState 变化可能触发该元素上的事件。

  2. 终止这些步骤。
如果 HTMLMediaElementbuffered 包含覆盖当前播放位置的 TimeRanges 且具有 足够数据以确保不间断播放
  1. HTMLMediaElementreadyState 属性设为 HAVE_ENOUGH_DATA
    注意

    按照 HTMLMediaElement ready states [HTML] 的逻辑, HTMLMediaElementreadyState 变化可能触发该元素上的事件。

  2. 如果此前因切换到 HAVE_CURRENT_DATA 而暂停播放,则此时可能恢复播放。
  3. 终止这些步骤。
如果 HTMLMediaElementbuffered 包含覆盖当前播放位置并向后延伸一段时间的 TimeRanges, 则运行以下步骤:
  1. HTMLMediaElementreadyState 属性设为 HAVE_FUTURE_DATA
    注意

    按照 HTMLMediaElement ready states [HTML] 的逻辑, HTMLMediaElementreadyState 变化可能触发该元素上的事件。

  2. 如果此前因切换到 HAVE_CURRENT_DATA 而暂停播放,则此时可能恢复播放。
  3. 终止这些步骤。
如果 HTMLMediaElementbuffered 包含的 TimeRanges 正好在当前播放位置结束,且在当前位置之后没有覆盖的范围:
  1. HTMLMediaElementreadyState 属性设为 HAVE_CURRENT_DATA
    注意

    按照 HTMLMediaElement ready states [HTML] 的逻辑, HTMLMediaElementreadyState 变化可能触发该元素上的事件。

  2. 此时播放被暂停,因为媒体元素没有足够数据推进 媒体时间线
  3. 终止这些步骤。

3.15.5 选中/启用轨道状态变更

在播放过程中,如果 activeSourceBuffers 需要更新, 这是因为 selected 视频轨道、enabled 音频轨道,或 文本轨道的 mode 发生了变化。当这些变化中的一个或多个发生时, 需要遵循以下步骤。由于 AudioTrackVideoTrack 以及 TextTrack 对象及其 AudioTrackListVideoTrackListTextTrackList 只在 Window HTMLMediaElement 上暴露, 因此这些变化 总是发生在 Window 上下文中。当 MediaSourceDedicatedWorkerGlobalScope 中构造时, 则每一次此类变化必须通过发送到 [[port to worker]] 的内部 update track state 消息传达给 DedicatedWorkerGlobalScope, 其隐式处理器将针对创建受影响轨道的 SourceBuffer 执行以下步骤。当 MediaSourceWindow 中构造时, 以下步骤直接执行。

如果选中的视频轨道发生变化,则执行以下步骤:
  1. 如果与先前选中的视频轨道关联的 SourceBuffer 不与任何其他已启用轨道关联,则执行以下步骤:
    1. 将该 SourceBufferactiveSourceBuffers 中移除。
    2. 排队一个任务,在 activeSourceBuffers触发事件,事件名为 removesourcebuffer
  2. 如果与新选中的视频轨道关联的 SourceBuffer 尚未在 activeSourceBuffers 中,则执行以下步骤:
    1. 将该 SourceBuffer 添加到 activeSourceBuffers 中。
    2. 排队一个任务,在 activeSourceBuffers触发事件,事件名为 addsourcebuffer
如果某个音频轨道变为禁用,并且与该轨道关联的 SourceBuffer 不与任何其他已启用或已选中轨道关联,则执行以下步骤:
  1. 将与该音频轨道关联的 SourceBufferactiveSourceBuffers 中移除
  2. 排队一个任务,在 activeSourceBuffers触发事件,事件名为 removesourcebuffer
如果某个音频轨道变为启用,并且与该轨道关联的 SourceBuffer 尚未在 activeSourceBuffers 中, 则执行以下步骤:
  1. 将与该音频轨道关联的 SourceBuffer 添加到 activeSourceBuffers
  2. 排队一个任务,在 activeSourceBuffers触发事件,事件名为 addsourcebuffer
如果某个文本轨道的 mode 变为 "disabled" 并且与该轨道关联的 SourceBuffer 不与任何其他 已启用或已选中轨道关联,则执行以下步骤:
  1. 将与该文本轨道关联的 SourceBufferactiveSourceBuffers 中移除
  2. 排队一个任务,在 activeSourceBuffers触发事件,事件名为 removesourcebuffer
如果某个文本轨道的 mode 变为 "showing""hidden" 并且与该轨道关联的 SourceBuffer 尚未在 activeSourceBuffers 中, 则执行以下步骤:
  1. 将与该文本轨道关联的 SourceBuffer 添加到 activeSourceBuffers
  2. 排队一个任务,在 activeSourceBuffers触发事件,事件名为 addsourcebuffer

3.15.6 时长变更

duration 需要变为一个 新的 时长 时,按以下步骤执行。

  1. 如果当前 duration 的值 等于 新的时长,则 返回。
  2. 如果 新的时长 小于所有位于 SourceBuffer 对象的 sourceBuffers 中所有已缓冲 编码帧 的最高 呈现时间戳,则抛出一个 InvalidStateError 异常并中止 这些步骤。
    Note

    会截断当前已缓冲媒体的时长缩减是不允许的。 当需要截断时,请先使用 remove() 减少缓冲 范围,然后再更新 duration

  3. 最高结束时间 为所有 轨道缓冲范围 的结束时间中最大的那个,范围遍及 所有 轨道缓冲区,而这些轨道缓冲区又分布在所有 SourceBuffer 对象的 sourceBuffers 中。
  4. 如果 新的时长 小于 最高结束时间,则
    Note

    之所以会出现此条件,是因为 编码帧移除 算法会保留那些在移除范围起始之前开始的编码帧。

    1. 新的时长 更新为等于 最高结束时间
  5. duration 更新为 新的时长
  6. 使用 如有必要则镜像 算法,在 Window 中执行以下步骤,以更新媒体元素的时长:
    1. 将媒体元素的 duration 更新为 新的时长
    2. 运行 HTMLMediaElement 时长变化算法

3.15.7 流结束

当应用通过调用 endOfStream() 表示流结束,或者某个算法需要表示解码错误时,就会调用此算法。该 算法接受一个 error 参数,用于指示是否 将要发出错误信号。

  1. readyState 属性值更改为 "ended"。
  2. 排队一个任务 ,在 MediaSource触发一个 事件,事件名为 sourceended
  3. 如果 error 未设置
    1. 运行 时长变更 算法,并将 新的时长 设为所有 轨道缓冲范围 的最大结束时间, 该范围遍及所有 轨道缓冲区,而这些轨道缓冲区属于所有 SourceBuffer 对象, 这些对象位于 sourceBuffers 中。
      Note

      这可以使时长正确反映已附加媒体片段的末尾。例如,如果时长被显式设置为 10 秒, 而在调用 endOfStream() 之前只附加了 0 到 5 秒的媒体片段,那么时长将被更新为 5 秒。

    2. 通知媒体元素它现在拥有全部媒体数据。
    如果 error 被设置为 "network"
    使用 如有必要则镜像 算法,在 Window 中运行以下步骤:
    如果 HTMLMediaElementreadyState 属性 等于 HAVE_NOTHING
    运行 资源 获取算法媒体 数据处理步骤列表中的“If the media data cannot be fetched at all, due to network errors, causing the user agent to give up trying to fetch the resource”步骤。
    如果 HTMLMediaElementreadyState 属性大于 HAVE_NOTHING
    运行 媒体 数据处理步骤列表中的 “If the connection is interrupted after some media data has been received, causing the user agent to give up trying to fetch the resource”步骤。
    如果 error 被设置为 "decode"
    使用 如有必要则镜像 算法,在 Window 中运行以下步骤:
    如果 HTMLMediaElementreadyState 属性 等于 HAVE_NOTHING
    运行 资源 获取算法媒体 数据处理步骤列表中的 “If the media data can be fetched but is found by inspection to be in an unsupported format, or can otherwise not be rendered at all”步骤。
    如果 HTMLMediaElementreadyState 属性大于 HAVE_NOTHING
    运行 媒体 数据已损坏 步骤,作为 资源 获取算法媒体 数据处理步骤列表的一部分。

3.15.8 必要时镜像

此算法用于从附加自 同一 WindowDedicatedWorkerGlobalScopeMediaSource 中,在 Window 上运行步骤,通常用于更新 所附加 HTMLMediaElement 的状态。 此算法接受一个 steps 参数,其中列出了要在 Window 上运行的步骤。

如果 MediaSource 是在 DedicatedWorkerGlobalScope 中构造的:
[[port to main]] 发送一个内部 mirror on window 消息,其在 Window 中的隐式处理器将运行 steps。在该处理器收到消息之前, 将控制权返回给调用者,而不等待它。
Note
mirror 消息机制的目的是确保:
  1. steps 作为 Window 上的独立任务异步运行,而不是让这些 steps 以某种方式发生在其他 Window 任务执行的中间,并且
  2. stepsDedicatedWorkerGlobalScope 上 的同步执行和返回不会被阻塞。
否则:
运行 steps

4. MediaSourceHandle 接口

MediaSourceHandle 接口表示一个代理, 该代理对应一个 MediaSource 对象, 它可用于通过 srcObject 将一个来自 DedicatedWorkerGlobalScopeMediaSource 附加到一个位于 Window 中的 HTMLMediaElement, 如 附加到媒体元素 算法所述。

Note

之所以需要这个独立对象,是因为要将跨上下文的 MediaSource 附加到媒体 元素上;因为 MediaSource 对象本身 不是可传输的,原因是它们是事件目标。

每个 MediaSourceHandle 对象有一个 [[has ever been assigned as srcobject]] 内部槽,存储一个 boolean。该槽在 MediaSourceHandle 对象创建时初始化为 false,在 扩展的 HTMLMediaElementsrcObject setter(见 10. HTMLMediaElement 扩展)中设置为 true, 若为 true,则会阻止该 MediaSourceHandle 的成功传递,具体见 4.1 传递

MediaSourceHandle 对象是 Transferable, 每个对象有一个 [[Detached]] 内部槽,用于保证该实例在被传递后不可再次传递。

WebIDL[Transferable, Exposed=(Window,DedicatedWorker)]
interface MediaSourceHandle {};

4.1 传递

MediaSourceHandle传输步骤传输接收 步骤 要求 实现维护一个隐式内部槽,该槽引用底层的 MediaSource,以便通过 srcObject 实现 附加到媒体元素 ,并随后为该附加建立 跨上下文通信模型

实现者应当注意, Transferable 所暗示的“移动”语义并不总是符合实际情况。例如,扩展或内部 使用广播实现 postMessage 的方式可能会导致已传输的 MediaSourceHandle 意外出现多个接收者。 因此,建议实现在底层 MediaSource 对象的任一句柄 被用于媒体元素资源选择算法的异步部分之前,不要 确定已传输的 MediaSourceHandle 的哪个潜在克隆仍然有效 并可用于附加。 这类似于现有通过MediaSource 对象 URL 进行附加的行为,这类 URL 可以轻易克隆,而这样的 URL 最多只对一次附加开始有效 (跨其所有可能存在的多个克隆)。

实现必须支持:对于作为 MediaSourceHandle 底层对象的 MediaSource 对象, 通过 srcObject 进行的附加(加载)在任何情况下最多只能成功一次, 不受由于 Transferable 的不同实现而导致的 MediaSourceHandle 潜在克隆影响。

Note

关于这一点如何在媒体元素资源选择算法的异步部分中强制执行,请参见 附加到媒体元素

MediaSourceHandle 仅在 WindowDedicatedWorkerGlobalScope 上下文中暴露,并且不能在不同的 agent clusters [ECMASCRIPT] 之间成功传输。 MediaSourceHandle 对象的传输只能在同一个 agent cluster 内成功。

Note

例如,从 WindowDedicatedWorkerGlobalScope 向 SharedWorker 或 ServiceWorker 传输一个 MediaSourceHandle 对象将不会成功。 开发者应当注意,这与 MediaSource 对象 URL 的区别在于, 后者是可以通过多种方式通信的 DOMString。 即便如此,使用 附加到媒体元素 并通过 MediaSource 对象 URL 进行附加, 也只能对一个在 Window 上下文中构造的 MediaSource 成功。 另请参见 Web Application APIs 中 agent 与 agent cluster 形式化的集成 [HTML],其中定义了诸如 专用 worker agent 等相关概念。

传输步骤 对于一个 MediaSourceHandle 对象 必须包括以下步骤:

  1. 如果该 MediaSourceHandle[[has ever been assigned as srcobject]] 内部槽为 true,则 传输步骤 必须通过抛出一个 DataCloneError 异常而失败。

5. SourceBuffer 接口

WebIDLenum AppendMode {
  "segments",
  "sequence",
};
segments
媒体分段中的时间戳决定了编码帧 在演示中的位置。媒体分段可以以任意顺序追加。
sequence
媒体分段将被视为时间上相邻,和分段中的时间戳无关。新媒体分段中的编码帧会紧接在前一个媒体分段的编码帧之后。若需要使新媒体分段和前一个分段相邻,会更新 timestampOffset 属性。设置 "sequence" 模式下的 timestampOffset 属性,可以让媒体分段放到时间线上的指定位置,而无需了解分段内时间戳。
WebIDL[Exposed=(Window,DedicatedWorker)]
interface SourceBuffer : EventTarget {
  attribute AppendMode mode;
  readonly  attribute boolean updating;
  readonly  attribute TimeRanges buffered;
  attribute double timestampOffset;
  [Exposed=Window] readonly  attribute AudioTrackList audioTracks;
  [Exposed=Window] readonly  attribute VideoTrackList videoTracks;
  [Exposed=Window] readonly  attribute TextTrackList textTracks;
  attribute double appendWindowStart;
  attribute unrestricted double appendWindowEnd;

  attribute EventHandler onupdatestart;
  attribute EventHandler onupdate;
  attribute EventHandler onupdateend;
  attribute EventHandler onerror;
  attribute EventHandler onabort;

  undefined appendBuffer(BufferSource data);
  undefined abort();
  undefined changeType(DOMString type);
  undefined remove(double start, unrestricted double end);
};
Note

audioTracksvideoTrackstextTracks 属性只对 Window 上下文暴露。当一个 SourceBufferDedicatedWorkerGlobalScope 使用时,其轨道则会在它所附加到的 Window HTMLMediaElement 上创建并归其所有,如 初始化段 已接收 算法所述。

5.1 属性

mode 的 类型为 AppendMode

控制一系列 媒体段 的处理方式。该属性 在对象创建后由 addSourceBuffer() 初始设置,并且可以通过 changeType() 或 设置此属性来更新。

在获取时,返回初始值或最近一次成功设置的值。

在设置时,运行以下步骤:

  1. 如果此对象已从 sourceBuffers 属性所指向的 父 media source 中移除,则抛出一个 InvalidStateError 异常并中止这些步骤。
  2. 如果 updating 属性等于 true,则抛出一个 InvalidStateError 异常并中止这些步骤。
  3. 新模式 等于为该属性赋的新值。
  4. 如果 [[generate timestamps flag]] 等于 true 且 新模式 等于 "segments",则抛出一个 TypeError 异常并中止 这些步骤。
  5. 如果 readyState 属性 属于 父 media source,且处于 "ended" 状态,则运行以下步骤:

    1. readyState 属性 设为 父 media source 的 "open"。
    2. 排队一个任务,在 事件触发 时, 将事件名为 sourceopen 的事件触发到 父 media source 上。
  6. 如果 [[append state]] 等于 PARSING_MEDIA_SEGMENT,则 抛出一个 InvalidStateError 并中止这些步骤。
  7. 如果 新模式 等于 "sequence",则将 [[group start timestamp]] 设为 [[group end timestamp]]
  8. 将该属性更新为 新模式
updating 的类型为 boolean, 只读

指示 appendBuffer()remove() 操作的异步后续处理是否仍在进行中。该属性在对象创建时初始设为 false。

buffered 的类型为 TimeRanges, 只读

指示该 SourceBuffer 中缓冲了哪些 TimeRanges。 该属性在对象创建时初始设为空的 TimeRanges 对象。

读取该属性时,必须执行以下步骤:

  1. 如果此对象已从 sourceBuffers 属性所指向的 父 media source 中移除,则抛出一个 InvalidStateError 异常并中止这些步骤。
  2. 最高结束时间 为所有由此 SourceBuffer 对象管理的 轨道缓冲范围 的最大结束时间, 其遍及所有 轨道缓冲区
  3. 交集范围 等于一个包含从 0 到 最高结束时间 的单个范围的 TimeRanges 对象。
  4. 对于此 SourceBuffer 所管理的每个音频和视频 轨道缓冲区,执行以下步骤:
    Note

    文本 轨道缓冲区 也包含在上方 最高结束时间 的计算中, 但在这里的缓冲范围计算中被排除。它们不一定是连续的,也不应因其中的任何不连续而在其他媒体轨道在相同时间范围内连续时触发播放停顿。

    1. track ranges 等于当前轨道缓冲区轨道缓冲区范围
    2. 如果 readyState 是 "ended",则将 track ranges 中最后一个范围的结束 时间设置为 highest end time
    3. new intersection ranges 等于 intersection rangestrack ranges 之间的交集。
    4. new intersection ranges 替换 intersection ranges 中的范围。
  5. 如果 交集范围 不包含与该属性当前值完全相同的范围信息, 则将该属性当前值更新为 交集范围
  6. 返回该属性的当前值。
timestampOffset 的类型为 double

控制随后追加到该 SourceBuffer 的后续 媒体段 中时间戳所应用的偏移量。timestampOffset 初始设为 0,表示不应用偏移。

在获取时,返回初始值或最近一次成功设置的值。

在设置时,运行以下步骤:

  1. 新 timestamp offset 等于为该属性赋的新值。
  2. 如果此对象已从 sourceBuffers 属性所指向的 父 media source 中移除,则抛出一个 InvalidStateError 异常并中止这些步骤。
  3. 如果 updating 属性等于 true,则抛出一个 InvalidStateError 异常并中止这些步骤。
  4. 如果 readyState 属性 所属的 父 media source 处于 "ended" 状态,则运行以下步骤:

    1. readyState 属性 设为 父 media source 的 "open"。
    2. 排队一个任务,在 事件触发 时, 将名为 sourceopen 的事件触发到 父 media source 上。
  5. 如果 [[append state]] 等于 PARSING_MEDIA_SEGMENT,则 抛出一个 InvalidStateError 并中止这些步骤。
  6. 如果 mode 属性等于 "sequence",则将 [[group start timestamp]] 设为 新 timestamp offset
  7. 将该属性更新为 新 timestamp offset
audioTracks 的类型为 AudioTrackList, 只读
由此对象创建的 AudioTrack 对象列表。该属性只对 Window 上下文暴露;当 SourceBufferDedicatedWorkerGlobalScope 中创建时不可用。
videoTracks 的类型为 VideoTrackList, 只读
由此对象创建的 VideoTrack 对象列表。该属性只对 Window 上下文暴露;当 SourceBufferDedicatedWorkerGlobalScope 中创建时不可用。
textTracks 的类型为 TextTrackList, 只读
由此对象创建的 TextTrack 对象列表。该属性只对 Window 上下文暴露;当 SourceBufferDedicatedWorkerGlobalScope 中创建时不可用。
appendWindowStart 的类型为 double

呈现时间戳,表示 追加窗口 的开始。该属性 初始设为 呈现开始时间

在获取时,返回初始值或最近一次成功设置的值。

在设置时,运行以下步骤:

  1. 如果此对象已从 sourceBuffers 属性所指向的 父 media source 中移除,则抛出一个 InvalidStateError 异常并中止这些步骤。
  2. 如果 updating 属性等于 true,则抛出一个 InvalidStateError 异常并中止这些步骤。
  3. 如果新值小于 0 或大于等于 appendWindowEnd,则 抛出一个 TypeError 异常并中止这些 步骤。
  4. 将该属性更新为新值。
appendWindowEnd 的类型为 unrestricted double

呈现时间戳,表示 追加窗口 的结束。该属性 初始设为正无穷大。

在获取时,返回初始值或最近一次成功设置的值。

在设置时,运行以下步骤:

  1. 如果此对象已从 sourceBuffers 属性所指向的 父 media source 中移除,则抛出一个 InvalidStateError 异常并中止这些步骤。
  2. 如果 updating 属性等于 true,则抛出一个 InvalidStateError 异常并中止这些步骤。
  3. 如果新值等于 NaN,则抛出一个 TypeError 并中止这些步骤。
  4. 如果新值小于或等于 appendWindowStart,则 抛出一个 TypeError 异常并中止这些步骤。
  5. 将该属性更新为新值。
onupdatestart 的类型为 EventHandler

updatestart 事件的事件处理器。

onupdate 的类型为 EventHandler

update 事件的事件处理器。

onupdateend 的类型为 EventHandler

updateend 事件的事件处理器。

onerror 的类型为 EventHandler

error 事件的事件处理器。

onabort 的类型为 EventHandler

abort 事件的事件处理器。

5.2 方法

appendBuffer

BufferSource[WEBIDL] 中的段数据追加到 SourceBuffer

调用此方法时,用户代理必须运行以下步骤:

  1. 运行准备追加算法。
  2. data 添加到[[input buffer]]的末尾。
  3. updating属性设置为 true。
  4. 排入一个 任务,以在此 SourceBuffer 对象上触发一个事件,其名称为updatestart
  5. 异步运行缓冲区追加算法。
abort

中止当前段并重置段解析器。

调用此方法时,用户代理必须运行以下步骤:

  1. 如果此对象已从父媒体源sourceBuffers属性 中移除,则抛出一个InvalidStateError 异常并 中止这些步骤。
  2. 如果父媒体源readyState属性 不处于 "open" 状态,则抛出一个InvalidStateError 异常 并中止这些步骤。
  3. 如果范围移除算法正在运行,则抛出一个 InvalidStateError 异常并中止这些步骤。
  4. 如果updating属性等于 true,则运行以下 步骤:
    1. 如果缓冲区追加算法 正在运行,则中止它。
    2. updating属性设置为 false。
    3. 排入 一个任务,以在此 SourceBuffer 对象上触发一个事件,其名称为 abort
    4. 排入 一个任务,以在此 SourceBuffer 对象上触发一个事件,其名称为 updateend
  5. 运行重置解析器状态 算法。
  6. appendWindowStart设置为 呈现开始时间
  7. appendWindowEnd设置为 正无穷大。
changeType

更改与此对象关联的 MIME 类型。后续 appendBuffer() 调用将预期新追加的字节符合 新类型。

调用此方法时,用户代理必须运行以下步骤:

  1. 如果 type 是空字符串,则抛出一个TypeError 异常并 中止这些步骤。
  2. 如果此对象已从父媒体源sourceBuffers属性 中移除,则抛出一个InvalidStateError 异常并 中止这些步骤。
  3. 如果updating属性等于 true,则抛出一个 InvalidStateError 异常并中止这些步骤。
  4. 如果 type 包含不受支持的 MIME 类型,或者 包含一个与父媒体源sourceBuffers属性中 SourceBuffer 对象当前或之前指定的类型 一起使用时不受支持的 MIME 类型,则抛出一个NotSupportedError 异常并中止这些 步骤。
  5. 如果父媒体源readyState属性 处于 "ended" 状态,则运行以下步骤:

    1. 父媒体源readyState属性 设置为 "open"。
    2. 排入 一个任务,以在父媒体源触发一个事件,其名称为 sourceopen
  6. 运行重置解析器状态 算法。
  7. 将此 SourceBuffer 对象上的[[generate timestamps flag]] 更新为字节流 格式注册表 [MSE-REGISTRY] 中与 type 关联的条目里的“生成时间戳标志”列中的值。
  8. 如果[[generate timestamps flag]] 等于 true:
    将此 SourceBuffer 对象上的mode属性设置为 "sequence",包括 运行设置该 属性所关联的步骤。
    否则:
    保留此 SourceBuffer 对象上的mode 属性的先前值,而不 运行设置该 属性所关联的任何步骤。
  9. 将此 SourceBuffer 对象上的[[pending initialization segment for changeType flag]] 设置为 true。
remove

移除特定时间范围内的媒体。移除范围的 start,以从呈现开始时间起测量的秒数 表示。移除范围的 end,以从呈现开始时间起测量的秒数 表示。

调用此方法时,用户代理必须运行以下步骤:

  1. 如果此对象已从父媒体源sourceBuffers属性 中移除,则抛出一个InvalidStateError 异常并 中止这些步骤。
  2. 如果updating属性等于 true,则抛出一个 InvalidStateError 异常并中止这些步骤。
  3. 如果duration等于 NaN,则抛出 一个TypeError 异常并 中止这些步骤。
  4. 如果 start 为负数或大于duration,则 抛出一个TypeError 异常并中止这些步骤。
  5. 如果 end 小于或等于 start,或者 end 等于 NaN,则抛出一个TypeError 异常并中止这些步骤。
  6. 如果父媒体源readyState属性 处于 "ended" 状态,则运行以下步骤:

    1. 父媒体源readyState属性 设置为 "open"
    2. 排入 一个任务,以在父媒体源触发一个事件,其名称为 sourceopen
  7. startend 作为移除范围的 起点和终点,运行范围移除算法。

5.3 轨道缓冲区

轨道缓冲区存储单个轨道的轨道 描述编码帧。当初始化段媒体段被追加到SourceBuffer时,轨道缓冲区会 随之更新。

每个轨道缓冲区都有一个最后解码时间戳 变量,用于存储当前编码帧组中最后追加的编码帧的解码时间戳,该 当前编码帧组。该变量最初为未设置状态,以 表示尚未追加任何 编码帧

每个轨道缓冲区都有一个最后帧持续时间 变量,用于存储当前编码帧持续时间组中最后一个被追加的编码帧的编码帧持续时间,该组为 当前编码帧组。该变量最初为未设置状态,以 表示尚未追加任何 编码帧

每个轨道缓冲区都有一个最高结束时间戳 变量,用于存储当前编码帧结束时间戳组中所有编码帧里最高的编码帧结束时间戳,这些编码帧位于当前编码帧 组中,并已追加到此轨道缓冲区。 该变量最初为未设置状态,以表示尚未追加任何编码帧

每个轨道缓冲区都有一个需要随机访问点标志 变量,用于跟踪轨道缓冲区是否正在等待一个随机访问点编码帧。该 变量最初设置为 true,以表示在任何内容可以添加到轨道缓冲区之前,需要一个随机访问 点编码帧

每个轨道缓冲区都有一个轨道缓冲区范围 变量,用于表示当前存储在轨道缓冲区中的编码帧 所占据的呈现时间范围。

对于轨道缓冲区范围,这些呈现时间范围基于呈现时间戳、帧持续时间,以及 复用SourceBuffer中跨轨道缓冲区的编码 帧组可能具有的编码帧组开始时间。

出于规范目的,此信息被视为如同存储在 规范化 TimeRanges 对象中。相交后的轨道缓冲区范围用于 报告HTMLMediaElementbuffered, 因此必须 支持在HTMLMediaElementbuffered的每个范围内不间断播放。

这些编码帧组开始时间与编码帧处理算法中提到的时间略有不同,因为它们是 不连续之后所有轨道缓冲区中最早的呈现时间戳 。不连续可以发生在 编码帧处理算法中,也可以由 编码帧移除 算法产生,而与mode无关。用于确定轨道缓冲区范围是否不相交的阈值由实现决定。例如,为了 减少意外的播放停顿,实现可以通过合并相邻范围来近似编码帧处理算法的不连续 检测逻辑,这些相邻范围之间的间隙小于此轨道缓冲区中到目前为止已缓冲的最大帧持续时间的 2 倍。实现也可以 在复用SourceBuffer中,使用跨轨道缓冲区的编码帧组开始时间作为 范围开始时间,以进一步减少 意外的播放停顿。

5.4 事件概要

事件名称 接口 在……时触发
updatestart Event SourceBufferupdating 从 false 变为 true。
update Event SourceBuffer 的 追加或移除成功完成。SourceBufferupdating 从 true 变为 false。
updateend Event SourceBuffer 的追加或移除结束。
error Event 在向 SourceBuffer 追加期间发生错误。 updating 从 true 变为 false。
abort Event SourceBuffer 的 追加被一个 abort() 调用中止。 updating 从 true 变为 false。

5.5 算法

5.5.1 分段解析循环

每个 SourceBuffer 对象都有一个 [[append state]] 内部槽,用于跟踪高层级的分段解析状态。 它在初始时设为 WAITING_FOR_SEGMENT, 并且随着数据追加可以转换到下列 状态。

追加状态名称 描述
WAITING_FOR_SEGMENT 等待要追加的 初始化段媒体段 的开始。
PARSING_INIT_SEGMENT 正在解析一个 初始化段
PARSING_MEDIA_SEGMENT 正在解析一个 媒体段

每个 SourceBuffer 对象都有一个 [[input buffer]] 内部槽,它是一个字节缓冲区,用于在多次 appendBuffer() 调用之间保存未解析的字节。 该缓冲区在 SourceBuffer 对象创建时为空。

每个 SourceBuffer 对象都有一个 [[buffer full flag]] 内部槽,用于跟踪 appendBuffer() 是否允许接受更多字节。它在 SourceBuffer 对象创建时设为 false, 并在数据追加和移除时更新。

每个 SourceBuffer 对象都有一个 [[audio track added flag]] 内部槽,用于跟踪是否已经为该 AudioTrack 为该 SourceBuffer 对象创建过一个轨道。它在 SourceBuffer 对象创建时设为 false,并由 初始化段 已接收 算法用于确定新创建的 AudioTrack 是否默认处于 enabled 状态。

每个 SourceBuffer 对象都有一个 [[video track added flag]] 内部槽,用于跟踪是否已经为该 VideoTrack 为该 SourceBuffer 对象创建过一个轨道。它在 SourceBuffer 对象创建时设为 false,并由 初始化段 已接收 算法用于确定新创建的 VideoTrack 是否默认处于 selected 状态。

每个 SourceBuffer 对象都有一个 [[group start timestamp]] 内部槽,用于跟踪在 "sequence" 模式下一个新的 编码帧组 的起始时间戳。该槽在 SourceBuffer 对象创建时未设置,并会在 mode 属性等于 "sequence" 且 timestampOffset 属性被设置时,或者在 编码帧处理 算法运行时更新。

每个 SourceBuffer 对象都有一个 [[group end timestamp]] 内部槽,用于存储当前 编码帧组 中所有 编码帧 的最高 编码帧结束时间戳。 它在 SourceBuffer 对象创建时设为 0,并由 编码帧处理 算法更新。

[[group end timestamp]] 存储 SourceBuffer 中所有轨道缓冲区里的最高编码帧结束 时间戳。因此,在追加各轨道间时间戳未对齐的复用 段时,应谨慎设置mode 属性。

每个 SourceBuffer 对象都有一个 [[generate timestamps flag]] 内部槽,它是一个布尔值,用于跟踪是否需要为传递给 编码帧处理 算法的 编码帧 生成时间戳。该标志在 addSourceBuffer() 创建 SourceBuffer 对象时设置,并由 changeType() 更新。

当调用 segment parser loop 算法时,运行以下步骤:

  1. 循环顶部: 如果 [[input buffer]] 为空,则跳转到下面的 需要更多数据 步骤。
  2. 如果 [[input buffer]] 包含违反 SourceBuffer 字节流格式规范 的字节,则运行 append error 算法并中止此算法。
  3. [[input buffer]] 的开头移除任何字节, 这些字节是 字节流格式规范 指定必须忽略的。
  4. 如果 [[append state]] 等于 WAITING_FOR_SEGMENT,则运行以下步骤:

    1. 如果 [[input buffer]] 的开头指示 一个 初始化段 的开始,则将 [[append state]] 设为 PARSING_INIT_SEGMENT
    2. 如果 [[input buffer]] 的开头指示 一个 媒体段 的开始,则将 [[append state]] 设为 PARSING_MEDIA_SEGMENT
    3. 跳转回上面的 循环顶部 步骤。
  5. 如果 [[append state]] 等于 PARSING_INIT_SEGMENT,则运行以下步骤:

    1. 如果 [[input buffer]] 还不包含完整的 初始化段, 则跳转到下面的 需要更多数据 步骤。
    2. 运行 初始化段已接收 算法。
    3. [[input buffer]] 的开头移除 初始化段 字节。
    4. [[append state]] 设为 WAITING_FOR_SEGMENT
    5. 跳转回上面的 循环顶部 步骤。
  6. 如果 [[append state]] 等于 PARSING_MEDIA_SEGMENT,则运行以下步骤:

    1. 如果 [[first initialization segment received flag]] 为 false,或者 [[pending initialization segment for changeType flag]] 为 true,则运行 append error 算法并 中止此算法。
    2. 如果 [[input buffer]] 包含一个或多个完整的 编码帧,则运行 编码帧处理 算法。
      Note

      编码帧处理算法运行的频率由实现决定。编码帧处理算法可以在输入缓冲区包含完整媒体段时调用, 也可以在完整编码帧加入输入缓冲区时多次调用。

    3. 如果此 SourceBuffer 已满且无法再接受更多媒体数据,则将 [[buffer full flag]] 设为 true。
    4. 如果 [[input buffer]] 还不包含完整的 媒体段,则跳转到下面的 需要更多数据 步骤。
    5. [[input buffer]] 的开头移除 媒体段 字节。
    6. [[append state]] 设为 WAITING_FOR_SEGMENT
    7. 跳转回上面的 循环顶部 步骤。
  7. 需要更多数据: 将控制权返回给调用算法。

5.5.2 重置解析器状态

当需要重置解析器状态时,执行以下步骤:

  1. 如果 [[append state]] 等于 PARSING_MEDIA_SEGMENT 并且 [[input buffer]] 包含一些完整的 编码帧,则运行 编码帧处理算法,直到所有这些完整的 编码帧 都被处理完毕。
  2. 取消所有 轨道缓冲区上的 最后解码时间戳
  3. 取消所有 轨道缓冲区上的 最后帧持续时间
  4. 取消所有 轨道缓冲区上的 最高结束时间戳
  5. 将所有 轨道缓冲区上的 需要随机访问点标志 设置为 true。
  6. 如果 mode 属性等于 "sequence",则将 [[group start timestamp]] 设置为 [[group end timestamp]]
  7. 移除 [[input buffer]] 中的所有字节。
  8. [[append state]] 设置为 WAITING_FOR_SEGMENT

5.5.3 追加错误

当追加过程中发生错误时调用此算法。

  1. 运行 reset parser state 算法。
  2. updating 属性设为 false。
  3. 排队一个任务 ,在此 SourceBuffer 对象上触发一个 事件,事件名为 error
  4. 排队一个任务 ,在此 触发一个 事件,事件名为 updateend,触发于此 SourceBuffer 对象上。
  5. 运行 end of stream 算法,并将 error 参数设 为 "decode"。

5.5.4 准备追加

当追加操作开始时,将运行以下步骤来验证并准备 SourceBuffer

  1. 如果 SourceBuffer 已从 sourceBuffers 属性所对应的 父 media source 中移除,则抛出 一个 InvalidStateError 异常 并中止这些步骤。
  2. 如果 updating 属性等于 true, 则抛出一个 InvalidStateError 异常并中止这些步骤。
  3. 按如下方式确定 recent element error
    如果 MediaSource 是在 Window
    HTMLMediaElementerror 属性不为 null,则令 recent element error 为 true。若该属性为 null,则令 recent element error 为 false。
    否则
    recent element error 为针对 Window 情况下的步骤所得到的结果,但要在 Window HTMLMediaElementerror 属性发生任何变化时, 并通过 [[port to worker]] 隐式消息进行通信。 如果尚未收到此类消息,则令 recent element error 为 false。
  4. 如果 recent element error 为 true,则抛出一个 InvalidStateError 异常 并中止这些步骤。
  5. 如果 readyState 属性属于 父 media source,且处于 "ended" 状态,则运行以下步骤:

    1. readyState 属性设为 父 media source 的 "open"
    2. 排队一个任务,在 触发事件 时, 将名为 sourceopen 的事件触发到 父 media source 上。
  6. 运行 coded frame eviction 算法。
  7. 如果 [[buffer full flag]] 等于 true, 则抛出一个 QuotaExceededError 异常并中止这些步骤。

    Note

    这是实现无法清理出足够数据以容纳此次追加,或者追加内容太大的信号。 Web 应用应当使用 remove() 来显式释放空间和/或减小追加内容的大小。

5.5.5 缓冲区追加

当调用 appendBuffer() 时,执行以下步骤处理追加的数据。

  1. 运行 segment parser loop 算法。
  2. 如果上一步中的 segment parser loop 算法已被中止,则 中止此算法。
  3. updating 属性设为 false。
  4. 排队一个任务 ,在此 SourceBuffer 对象上触发一个 事件,事件名为 update
  5. 排队一个任务 ,在此 触发一个 事件,事件名为 updateend,触发于此 SourceBuffer 对象上。

5.5.6 范围移除

当调用者需要发起一个 JavaScript 可见的范围移除操作(该操作会阻塞其它 SourceBuffer 更新)时,按照以下步骤进行:

  1. start 等于移除 范围的起始 呈现时间戳,单位为从 呈现开始时间 起算的秒数。
  2. end 等于移除 范围的结束 呈现时间戳,单位为从 呈现开始时间 起算的秒数。
  3. updating 属性设为 true。
  4. 排队一个任务 ,在此 SourceBuffer 对象上触发一个 事件,事件名为 updatestart
  5. 将剩余步骤返回给调用者,并异步运行其余步骤。
  6. 使用 startend 作为 移除范围的起点和 终点,运行 coded frame removal 算法。
  7. updating 属性设为 false。
  8. 排队一个任务 ,在此 触发一个 事件,事件名为 update,触发于此 SourceBuffer 对象上。
  9. 排队一个任务 ,在此 触发一个 事件,事件名为 updateend,触发于此 SourceBuffer 对象上。

5.5.7 接收到初始化分段

分段解析循环 成功解析出一个完整的 初始化分段 时,运行以下步骤:

每个 SourceBuffer 对象都有一个用于跟踪是否已经由该算法追加并接收到首个 初始化分段 的内部插槽 [[first initialization segment received flag]]。该标志在创建 SourceBuffer 时被设为 false,并由下述算法进行更新。

每个 SourceBuffer 对象都有一个用于跟踪自最近一次 changeType() 起是否需要 初始化分段 的内部插槽 [[pending initialization segment for changeType flag]]。该标志在创建 SourceBuffer 时设为 false,由 changeType() 设为 true,并由下述算法重置为 false。

  1. 如果 duration 属性当前 等于 NaN,则更新该属性:
    如果初始化段包含时长:
    运行 时长变更算法, 并将 new duration 设置 为初始化段中的时长。
    否则:
    运行 时长变更算法, 并将 new duration 设置为正 Infinity。
  2. 如果初始化段 不包含音频、视频或文本轨道,则运行 追加错误算法,并中止这些步骤。
  3. 如果 [[first initialization segment received flag]] 为 true, 则运行以下步骤:
    1. 验证以下属性。如果任一检查失败,则运行 追加错误算法,并中止这些步骤。
      • 音频、视频和文本轨道的数量与第一个 初始化段中的数量相匹配。
      • 如果存在同一类型的多个轨道(例如 2 个音频 轨道),则 轨道 ID 与 第一个初始化段中的轨道 ID 相匹配。
      • 用户代理支持每个轨道的编解码器。

        如果原本受支持的编解码器未在传递给以下调用的 type 参数中指定,则用户代理可以在此将这些编解码器视为“不 受支持”:(a) 此 SourceBuffer 对象上最近一次成功的 changeType(), 或 (b) 如果此对象上尚未发生过 成功的 changeType(), 则为创建此 SourceBuffer 对象的 addSourceBuffer()。 例如,如果最近一次成功的 changeType() 是以 'video/webm''video/webm; codecs="vp8"' 调用的,而初始化段中出现了包含 vp9 的视频轨道,则即使上述另外两个属性检查 均通过,用户代理也可以使用此步骤 触发解码错误。建议实现仅在编解码器确实不受支持或另外两个属性 检查 失败时才在这些情况下触发错误。建议 Web 作者使用 changeType()addSourceBuffer()isTypeSupported() 并使用精确的编解码器参数,以更主动地检测用户代理 支持情况。如果 SourceBuffer 对象的字节流格式正在发生变化,则必须调用 changeType()

    2. 将此初始化 段中的适当轨道描述 添加到各个轨道缓冲区
    3. 将所有轨道缓冲区上的 需要随机访问点标志 设置为 true。
  4. active track flag 等于 false。
  5. 如果 [[first initialization segment received flag]] 为 false, 则运行以下步骤:

    1. 如果初始化 段包含使用用户代理 不支持的编解码器的轨道,则运行 追加 错误算法,并中止这些步骤。

      如果原本受支持的编解码器未在传递给以下调用的 type 参数 中指定,则用户代理可以在此将这些编解码器视为“不 受支持”:(a) 此 SourceBuffer 对象上最近一次成功的 changeType(), 或 (b) 如果此对象上尚未发生过成功的 changeType(),则为 创建此 SourceBuffer 对象的 addSourceBuffer()。 例如, MediaSource.isTypeSupported('video/webm;codecs="vp8,vorbis"') 可能返回 true,但如果调用 addSourceBuffer() 时使用了 'video/webm;codecs="vp8"',而初始化段中出现了 Vorbis 轨道,则 用户代理可以使用此步骤触发解码 错误。 建议实现仅在该编解码器确实不受支持时才在这种情况下触发错误。建议 Web 作者使用 changeType()addSourceBuffer()isTypeSupported() 并使用精确的编解码器参数,以更 主动地检测用户代理支持情况。如果 SourceBuffer 对象的 字节流格式正在发生变化,则必须调用 changeType()

    2. 初始化段中的每个音频轨道,运行 以下步骤:

      1. audio byte stream track ID 为当前正在处理的轨道的 轨道 ID
      2. audio language 为一个 BCP 47 语言标签,表示 初始化段中为 此轨道指定的语言;如果不存在语言信息, 则为空字符串。
      3. 如果 audio language 等于 BCP 47 的 'und' 值,则将空 字符串赋给 audio language
      4. audio label初始化段中为 此轨道指定的标签;如果不存在标签信息,则为空字符串。
      5. audio kinds初始化段中为 此轨道指定的 kind 字符串序列;如果未提供 kind 信息,则为 仅包含一个空字符串元素的序列。
      6. 对于 audio kinds 中的每个值,运行 以下步骤:
        1. current audio kind 等于 本次循环迭代中来自 audio kinds 的值。
        2. audio track default enabled flag 为 false。
        3. 如果此 SourceBuffer 对象的 [[audio track added flag]] 内部槽为 false,则运行以下步骤:
          1. audio track default enabled flag 设置为 true。
          2. active track flag 设置为 true。
        4. 将此 SourceBuffer 对象的 [[audio track added flag]] 内部槽设置为 true。
        5. Window HTMLMediaElement 上创建 AudioTrack, 并在 Window 中运行 以下步骤:

          如果父媒体源 是在 Window 中构造的:
          1. new audio track 为 一个新的 AudioTrack 对象。
          2. 生成一个唯一 ID,并将其赋给 new audio track 上的 id 属性。
          3. audio languageaudio labelcurrent audio kind 分别赋给 new audio track 上的 languagelabelkind 属性。
          4. 如果 audio track default enabled flag 为 true, 则将 new audio track 上的 enabled 属性设置为 true。
          5. new audio track 添加到 此 SourceBuffer 对象上的 audioTracks 属性。

            这应当触发 AudioTrackList [HTML] 逻辑,以排入 一个任务,在此 audioTracks 属性所引用的 AudioTrackList 对象上,使用 TrackEvent 触发 一个事件,其名称为 addtrack, 并将 track 属性初始化为 new audio track。该 audioTracks 属性位于此 SourceBuffer 对象上。

          6. media element 为此 SourceBuffer 对象的父媒体 源所附加到的媒体元素。
          7. new audio track 添加到 media element 上的 audioTracks 属性。

            这应当触发 AudioTrackList [HTML] 逻辑,以排入 一个任务,在 media element 上的 audioTracks 属性所引用的 AudioTrackList 对象上,使用 TrackEvent 触发 一个事件,其名称为 addtrack, 并将 track 属性初始化为 new audio track

          否则:
          [[port to main]] 发送一条内部 create track 消息,其在 Window 中的隐式处理程序运行以下步骤:
          1. new audio track 为 一个新的 AudioTrack 对象。
          2. 生成一个唯一 ID,并将其赋给 new audio track 上的 id 属性。
          3. audio languageaudio labelcurrent audio kind 分别赋给 new audio track 上的 languagelabelkind 属性。
          4. 如果 audio track default enabled flag 为 true, 则将 new audio track 上的 enabled 属性设置为 true。
          5. media element 为此 SourceBuffer 对象的父媒体 源所附加到的媒体元素。
          6. new audio track 添加到 media element 上的 audioTracks 属性。

            这应当触发 AudioTrackList [HTML] 逻辑,以排入 一个任务,在 media element 上的 audioTracks 属性所引用的 AudioTrackList 对象上,使用 TrackEvent 触发 一个事件,其名称为 addtrack, 并将 track 属性初始化为 new audio track

      7. 创建一个新的轨道缓冲区,用于存储 此轨道的编码帧
      8. 将此轨道的轨道 描述添加到轨道 缓冲区
    3. 初始化段中的每个视频轨道,运行 以下步骤:

      1. video byte stream track ID 为当前正在处理的轨道的 轨道 ID
      2. video language 为一个 BCP 47 语言标签,表示 初始化段中为 此轨道指定的语言;如果不存在语言信息, 则为空字符串。
      3. 如果 video language 等于 BCP 47 的 'und' 值,则将空 字符串赋给 video language
      4. video label初始化段中为 此轨道指定的标签;如果不存在标签信息,则为空字符串。
      5. video kinds初始化段中为 此轨道指定的 kind 字符串序列;如果未提供 kind 信息,则为 仅包含一个空字符串元素的序列。
      6. 对于 video kinds 中的每个值,运行 以下步骤:
        1. current video kind 等于 本次循环迭代中来自 video kinds 的值。
        2. video track default selected flag 为 false。
        3. 如果此 SourceBuffer 对象的 [[video track added flag]] 内部槽为 false,则运行以下步骤:
          1. video track default selected flag 设置为 true。
          2. active track flag 设置为 true。
        4. 将此 SourceBuffer 对象的 [[video track added flag]] 内部槽设置为 true。
        5. Window HTMLMediaElement 上创建 VideoTrack, 并在 Window 中运行 以下步骤:

          如果父媒体源 是在 Window 中构造的:
          1. new video track 为 一个新的 VideoTrack 对象。
          2. 生成一个唯一 ID,并将其赋给 new video track 上的 id 属性。
          3. video languagevideo labelcurrent video kind 分别赋给 new video track 上的 languagelabelkind 属性。
          4. 如果 video track default selected flag 为 true, 则将 new video track 上的 selected 属性设置为 true。
          5. new video track 添加到 此 SourceBuffer 对象上的 videoTracks 属性。

            这应当触发 VideoTrackList [HTML] 逻辑,以排入 一个任务,在此 videoTracks 属性所引用的 VideoTrackList 对象上,使用 TrackEvent 触发 一个事件,其名称为 addtrack, 并将 track 属性初始化为 new video track。该 videoTracks 属性位于此 SourceBuffer 对象上。

          6. media element 为此 SourceBuffer 对象的父媒体 源所附加到的媒体元素。
          7. new video track 添加到 media element 上的 videoTracks 属性。

            这应当触发 VideoTrackList [HTML] 逻辑,以排入 一个任务,在 media element 上的 videoTracks 属性所引用的 VideoTrackList 对象上,使用 TrackEvent 触发 一个事件,其名称为 addtrack, 并将 track 属性初始化为 new video track

          否则:
          [[port to main]] 发送一条内部 create track 消息,其在 Window 中的隐式处理程序运行以下步骤:
          1. new video track 为 一个新的 VideoTrack 对象。
          2. 生成一个唯一 ID,并将其赋给 new video track 上的 id 属性。
          3. video languagevideo labelcurrent video kind 分别赋给 new video track 上的 languagelabelkind 属性。
          4. 如果 video track default selected flag 为 true, 则将 new video track 上的 selected 属性设置为 true。
          5. media element 为此 SourceBuffer 对象的父媒体 源所附加到的媒体元素。
          6. new video track 添加到 media element 上的 videoTracks 属性。

            这应当触发 VideoTrackList [HTML] 逻辑,以排入 一个任务,在 media element 上的 videoTracks 属性所引用的 VideoTrackList 对象上,使用 TrackEvent 触发 一个事件,其名称为 addtrack, 并将 track 属性初始化为 new video track

      7. 创建一个新的轨道缓冲区,用于存储 此轨道的编码帧
      8. 将此轨道的轨道 描述添加到轨道 缓冲区
    4. 初始化段中的每个文本轨道,运行 以下步骤:

      1. text byte stream track ID 为当前正在处理的轨道的 轨道 ID
      2. text language 为一个 BCP 47 语言标签,表示 初始化段中为 此轨道指定的语言;如果不存在语言信息, 则为空字符串。
      3. 如果 text language 等于 BCP 47 的 'und' 值, 则将空 字符串赋给 text language
      4. text label初始化段中为 此轨道指定的标签;如果不存在标签信息,则为空字符串。
      5. text kinds初始化段中为 此轨道指定的 kind 字符串序列;如果未提供 kind 信息,则为仅包含一个 空字符串元素的序列。
      6. 对于 text kinds 中的每个值,运行 以下步骤:
        1. current text kind 等于 本次循环迭代中来自 text kinds 的值。
        2. text track mode 为根据 此轨道的初始化 段确定的 mode 值。
        3. 如果 text track mode 等于 "showing""hidden", 则将 active track flag 设置为 true。
        4. Window HTMLMediaElement 上创建 TextTrack, 并在 Window 中运行 以下步骤:

          如果父媒体源 是在 Window 中构造的:
          1. new text track 为一个 新的 TextTrack 对象。
          2. 生成一个唯一 ID,并将其赋给 new text track 上的 id 属性。
          3. text languagetext labelcurrent text kind 分别赋给 new text track 上的 languagelabelkind 属性。
          4. new text track 上的 mode 属性设置为 text track mode,并使用从 此轨道的初始化 段中确定的适当信息填充 new text track 上的其余属性。
          5. new text track 添加到 此 SourceBuffer 对象上的 textTracks 属性。

            这应当触发 TextTrackList [HTML] 逻辑,以排入 一个任务,在此 textTracks 属性所引用的 TextTrackList 对象上,使用 TrackEvent 触发 一个事件,其名称为 addtrack, 并将 track 属性初始化为 new text track。 该 textTracks 属性位于此 SourceBuffer 对象上。

          6. media element 为此 SourceBuffer 对象的父媒体 源所附加到的媒体元素。
          7. new text track 添加到 media element 上的 textTracks 属性。

            这应当触发 TextTrackList [HTML] 逻辑,以排入 一个任务,在 media element 上的 textTracks 属性所引用的 TextTrackList 对象上,使用 TrackEvent 触发 一个事件,其名称为 addtrack, 并将 track 属性初始化为 new text track

          否则:
          [[port to main]] 发送一条内部 create track 消息,其在 Window 中的隐式处理程序运行以下步骤:
          1. new text track 为一个 新的 TextTrack 对象。
          2. 生成一个唯一 ID,并将其赋给 new text track 上的 id 属性。
          3. text languagetext labelcurrent text kind 分别赋给 new text track 上的 languagelabelkind 属性。
          4. new text track 上的 mode 属性设置为 text track mode,并使用从 此轨道的初始化 段中确定的适当信息填充 new text track 上的其余属性。
          5. media element 为此 SourceBuffer 对象的父媒体 源所附加到的媒体元素。
          6. new text track 添加到 media element 上的 textTracks 属性。

            这应当触发 TextTrackList [HTML] 逻辑,以排入 一个任务,在 media element 上的 textTracks 属性所引用的 TextTrackList 对象上,使用 TrackEvent 触发 一个事件,其名称为 addtrack, 并将 track 属性初始化为 new text track

      7. 创建一个新的轨道缓冲区,用于存储 此轨道的编码帧
      8. 将此轨道的轨道 描述添加到轨道 缓冲区
    5. 如果 active track flag 等于 true,则运行以下 步骤:
      1. 将此 SourceBuffer 添加到 activeSourceBuffers
      2. 排入 一个任务,以在 activeSourceBuffers触发一个事件, 其名称为 addsourcebuffer
    6. [[first initialization segment received flag]] 设置为 true。
  6. [[pending initialization segment for changeType flag]] 设置为 false。
  7. 如果 active track flag 等于 true,则运行以下 步骤:
  8. 使用父媒体源必要时镜像算法,在 Window 中运行以下步骤:
    1. 如果 HTMLMediaElementreadyState 属性 大于 HAVE_CURRENT_DATA, 则将 HTMLMediaElementreadyState 属性设置为 HAVE_METADATA

      根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElementreadyState 发生变化时可能 在 HTMLMediaElement 上触发事件。

  9. 如果父媒体源sourceBuffers 中的每个对象, 其 [[first initialization segment received flag]] 均等于 true,则使用 父媒体源必要时镜像算法,在 Window 中运行以下 步骤:
    1. 如果 HTMLMediaElementreadyState 属性为 HAVE_NOTHING, 则将 HTMLMediaElementreadyState 属性设置为 HAVE_METADATA

      根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElementreadyState 发生变化时可能 在 HTMLMediaElement 上触发事件。如果发生从 HAVE_NOTHINGHAVE_METADATA 的转换,则它应当触发 HTMLMediaElement 逻辑,以排入 一个任务,在媒体元素上触发一个事件, 其名称为 loadedmetadata

5.5.8 编码帧处理

编码帧 已被 分段解析循环 完整解析后,执行以下步骤:

  1. 媒体段中的每个编码帧运行以下步骤:

    1. 循环顶部:
      如果 [[generate timestamps flag]] 等于 true:
      1. presentation timestamp 等于 0。
      2. decode timestamp 等于 0。
      否则:
      1. presentation timestamp 为编码帧的呈现时间戳以秒为单位的 双精度浮点表示。

        由于定时文本帧的呈现 和 解码时间戳信息可能未 显式存在于底层格式中,或者可能 取决于帧的顺序,因此确定这些时间戳时可能需要特殊处理。某些元数据文本轨道,例如 MPEG2-TS PSI 数据,可能只有隐含的时间戳。针对这些 情况的格式特定规则应该包含在字节流格式 规范或 单独的扩展规范中。

      2. decode timestamp 为编码帧的解码时间戳以秒为单位的 双精度浮点 表示。

        实现不必在内部以 双精度 浮点表示存储时间戳。此表示 在这里使用,是因为它是 HTML 规范中时间戳所使用的表示。 这里的意图是在不向算法添加 不必要复杂性的情况下明确行为,以处理 添加 timestampOffset 可能导致 字节流格式所使用的底层时间戳表示发生时间戳回绕这一事实。 实现可以使用其 希望的任何内部时间戳表示,但 timestampOffset 的加法应该以类似于 使用双精度浮点 表示时所发生情况的方式表现。

    2. frame duration编码帧持续时间以秒为单位的双精度浮点 表示。
    3. 如果 mode 等于 "sequence",并且 [[group start timestamp]] 已设置,则运行以下步骤:
      1. timestampOffset 设置为 [[group start timestamp]] 减去 presentation timestamp
      2. [[group end timestamp]] 设置为 [[group start timestamp]]
      3. 将所有轨道缓冲区上的需要随机访问点 标志设置为 true。
      4. 取消设置 [[group start timestamp]]
    4. 如果 timestampOffset 不为 0,则运行以下步骤:

      1. timestampOffset 加到 presentation timestamp 上。
      2. timestampOffset 加到 decode timestamp 上。
    5. track buffer 等于编码帧将被 添加到的轨道 缓冲区
    6. 如果 track buffer最后解码 时间戳已设置,并且 decode timestamp 小于最后解码 时间戳
      如果 track buffer最后解码 时间戳已设置,并且 decode timestamp最后解码 时间戳之间的差值大于 最后帧 持续时间的 2 倍:
      1. 如果 mode 等于 "segments":
        [[group end timestamp]] 设置为 presentation timestamp
        如果 mode 等于 "sequence":
        [[group start timestamp]] 设置为 [[group end timestamp]]
      2. 取消设置所有轨道缓冲区上的最后解码时间戳
      3. 取消设置所有轨道缓冲区上的最后帧持续时间
      4. 取消设置所有轨道缓冲区上的最高结束时间戳
      5. 将所有轨道缓冲区上的需要随机访问 点标志设置为 true。
      6. 跳转到上面的循环顶部步骤,以重新开始处理当前 编码帧
      否则:
      继续。
    7. frame end timestamp 等于 presentation timestampframe duration 之和。
    8. 如果 presentation timestamp 小于 appendWindowStart, 则将需要随机访问点标志 设置为 true,丢弃该编码帧,并 跳转到循环顶部以开始处理下一个编码帧。

      某些实现可以选择收集其中一些 presentation timestamp 小于 appendWindowStart 的编码帧,并使用 它们在第一个呈现 时间戳大于或等于 appendWindowStart 的编码帧处生成拼接,即使 该帧不是随机访问点。支持此行为 需要多个 解码器或快于实时的解码,因此目前此行为不会成为 规范性要求。

    9. 如果 frame end timestamp 大于 appendWindowEnd, 则 将需要随机访问点标志设置为 true,丢弃 该编码帧,并跳转 到循环顶部以开始处理下一个编码帧。

      某些实现可以选择收集 presentation timestamp 小于 appendWindowEndframe end timestamp 大于 appendWindowEnd 的编码帧,并使用它们在收集时 对所收集编码帧中位于追加窗口内的部分,以及之后处理的、仅 与所收集编码帧末尾部分重叠的帧的开头部分生成拼接。支持此行为 需要多个解码器或快于实时的解码,因此目前此 行为不会成为规范性要求。结合收集 跨越 appendWindowStart 的编码帧,实现可以 由此支持无缝音频拼接。

    10. 如果 track buffer 上的需要随机访问点标志等于 true,则 运行以下步骤:
      1. 如果该编码帧不是随机访问点,则丢弃该编码 帧并跳转到循环顶部以开始处理下一个编码帧。
      2. track buffer 上的需要随机访问点 标志设置为 false。
    11. spliced audio frame 为一个未设置的变量,用于保存音频拼接 信息
    12. spliced timed text frame 为一个未设置的变量,用于保存定时文本 拼接信息
    13. 如果 track buffer最后解码时间戳未设置,并且 presentation timestamp 落在 track buffer 中某个编码帧呈现区间内,则运行以下步骤:
      1. overlapped frametrack buffer 中满足上述条件的编码帧
      2. 如果 track buffer 包含音频编码 帧
        运行音频拼接帧 算法,如果返回了拼接帧, 则将其赋给 spliced audio frame
        如果 track buffer 包含视频编码 帧
        1. remove window timestamp 等于 overlapped frame呈现 时间戳加 1 微秒。
        2. 如果 presentation timestamp 小于 remove window timestamp,则从 track buffer 中移除 overlapped frame

          这是为了补偿帧 时间戳 计算中的微小误差,这些误差可能在 双精度浮点数与有理数之间 来回转换时出现。此 容差允许一个帧替换现有帧,只要其 与现有帧的开始 时间相差不超过 1 微秒。略微 早于现有帧的帧由 下面的移除步骤处理。

        如果 track buffer 包含定时文本编码 帧
        运行文本拼接帧算法, 如果返回了拼接帧, 则将其赋给 spliced timed text frame
    14. 移除 track buffer 中现有的编码帧:
      如果 track buffer最高结束 时间戳未设置:
      track buffer 中移除所有编码帧, 这些帧的呈现时间戳大于 或等于 presentation timestamp 且小于 frame end timestamp
      如果 track buffer最高结束 时间戳已设置,并且小于或等于 presentation timestamp
      track buffer 中移除所有编码帧, 这些帧的呈现时间戳大于 或等于最高结束时间戳且小于 frame end timestamp
    15. 通过从 track buffer 中移除 上两个步骤中移除的那些帧与其后下一个随机访问点 之间的所有编码 帧,移除所有可能对上两个步骤中移除的编码 帧的解码依赖。

      移除所有编码帧直到 下一个随机访问 点,是对 解码依赖关系的保守估计,因为它假定被移除帧与 下一个随机访问点之间的所有帧都依赖于 被移除的帧。

    16. 如果 spliced audio frame 已设置:
      spliced audio frame 添加到 track buffer
      如果 spliced timed text frame 已设置:
      spliced timed text frame 添加到 track buffer
      否则:
      将具有 presentation timestampdecode timestampframe duration编码帧添加到 track buffer
    17. track buffer最后解码时间戳 设置为 decode timestamp
    18. track buffer最后帧持续时间设置为 frame duration
    19. 如果 track buffer最高结束时间戳未设置,或者 frame end timestamp 大于最高结束时间戳,则将 track buffer最高结束时间戳设置为 frame end timestamp

      之所以需要大于检查,是因为编码帧之间的双向预测 可能导致 presentation timestamp 不会 单调递增, 即使解码时间戳是单调递增的。

    20. 如果 frame end timestamp 大于 [[group end timestamp]], 则将 [[group end timestamp]] 设置为 frame end timestamp
    21. 如果 [[generate timestamps flag]] 等于 true,则将 timestampOffset 设置为 frame end timestamp
  2. 如果 HTMLMediaElementreadyState 属性为 HAVE_METADATA, 并且新的编码帧使 HTMLMediaElementbuffered 具有一个覆盖 当前播放位置的 TimeRanges, 则将 HTMLMediaElementreadyState 属性设置为 HAVE_CURRENT_DATA

    根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElementreadyState 发生变化时可能触发 HTMLMediaElement 上的事件。

  3. 如果 HTMLMediaElementreadyState 属性为 HAVE_CURRENT_DATA, 并且新的编码帧使 HTMLMediaElementbuffered 具有一个 包含当前播放位置以及当前播放 位置之后一段时间的 TimeRanges,则将 HTMLMediaElementreadyState 属性设置为 HAVE_FUTURE_DATA

    根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElementreadyState 发生变化时可能触发 HTMLMediaElement 上的事件。

  4. 如果 HTMLMediaElementreadyState 属性为 HAVE_FUTURE_DATA, 并且新的编码帧使 HTMLMediaElementbuffered 具有一个 包含当前播放位置以及足以确保 不间断播放的数据TimeRanges,则将 HTMLMediaElementreadyState 属性设置为 HAVE_ENOUGH_DATA

    根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElementreadyState 发生变化时可能触发 HTMLMediaElement 上的事件。

  5. 如果媒体段包含超出当前duration的数据, 则运行时长变更算法,并将 new duration 设置 为当前时长与 [[group end timestamp]] 中的最大值。

5.5.9 编码帧移除

当需要从 SourceBuffer 移除特定时间范围的 编码帧 时,请按照以下步骤操作:

  1. start 为移除 范围的起始呈现时间戳
  2. end 为移除 范围的结束呈现时间戳
  3. 对此 SourceBuffer 中的每个轨道缓冲区,运行以下 步骤:

    1. remove end timestampduration 的当前值
    2. 如果此轨道缓冲区有一个 大于或等于 end随机访问点时间戳,则将 remove end timestamp 更新为该 随机访问点时间戳。

      不同轨道的随机访问点时间戳可能不同,因为 一个轨道内编码帧之间的依赖关系通常不同于 另一个轨道中的依赖关系。

    3. 从此轨道 缓冲区中移除所有起始 时间戳大于或等于 start 且小于 remove end timestamp 的媒体数据。
      1. 对每个被移除的帧,如果该帧的解码 时间戳等于 该帧所在轨道的最后解码 时间戳,则运行以下 步骤:

        如果 mode 等于 "segments":
        [[group end timestamp]] 设置为呈现时间戳
        如果 mode 等于 "sequence":
        [[group start timestamp]] 设置为 [[group end timestamp]]
      2. 取消设置所有轨道 缓冲区上的最后解码 时间戳
      3. 取消设置所有轨道缓冲区上的最后帧 持续时间
      4. 取消设置所有轨道 缓冲区上的最高结束 时间戳
      5. 将所有轨道缓冲区上的需要随机访问点 标志设置为 true。
    4. 通过从此轨道缓冲区 中移除上一步中被移除的那些帧与其后下一个随机访问点之间的所有编码 帧,移除所有可能对上一步中被移除的编码 帧的解码依赖。

      移除所有编码帧直到 下一个随机访问 点,是对 解码依赖关系的保守估计,因为它假定被移除帧与下一个随机访问点 之间的所有帧都依赖于 被移除的帧。

    5. 如果此对象位于 activeSourceBuffers 中, 当前 播放位置大于或等于 start 且小于 remove end timestamp,并且 HTMLMediaElementreadyState 大于 HAVE_METADATA, 则将 HTMLMediaElementreadyState 属性设置为 HAVE_METADATA 并暂停播放。

      根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElementreadyState 发生变化时可能 在 HTMLMediaElement 上触发事件。

      此转换发生是因为当前播放位置的媒体数据已被 移除。直到当前 播放位置的媒体被追加,或者发生 3.15.5 所选/已启用轨道状态的更改之前,播放都无法继续。

  4. 如果 [[buffer full flag]] 等于 true,并且 此对象已准备好 接受更多字节,则将 [[buffer full flag]] 设置为 false。

5.5.10 编码帧清除

此算法在向此 SourceBuffer 追加新数据时运行,以释放 空间。

  1. new data 等于即将追加到此 SourceBuffer 的数据。
    议题 289:编辑性问题? 编码帧驱逐算法需要说明,"buffer full flag" 可以立即根据 |new data| 更新

    这里需要增加一个步骤,说明如果实现预测到,在 [[input buffer]] 中任何现有字节的基础上再处理 new data 会超过 SourceBuffer 的容量,则实现可以决定 在此将 [[buffer full flag]] 设置为 true。 例如,这样的步骤能够让实现 在接受会导致资源溢出的 new data 之前,更 主动地施加反压。实际上,至少已有一个实现 这样做。

  2. 如果 [[buffer full flag]] 等于 false,则 中止这些步骤。
  3. removal ranges 等于一个呈现 时间范围列表, 这些范围可以从呈现中驱逐,以便为 new data 腾出空间。

    实现可以使用不同的方法选择 removal ranges,因此 Web 应用程序不应该依赖特定行为。Web 应用程序可以使用 buffered 属性来 观察已缓冲数据中的部分内容是否已被 驱逐。

  4. 对于 removal ranges 中的每个范围,运行编码帧移除算法,并将 startend 分别设置为 移除范围的起始和结束 时间戳。

5.5.11 音频拼接帧

编码帧处理算法需要为两个重叠的音频 编码帧 生成拼接帧时,按照以下步骤操作:

  1. track buffer 为将包含 拼接的轨道缓冲区
  2. new coded frame 为正在添加到 track buffer 中、并触发了拼接需求的新的编码帧
  3. presentation timestampnew coded frame呈现时间戳
  4. decode timestampnew coded frame 的解码时间戳。
  5. frame durationnew coded frame编码帧持续时间
  6. overlapped frametrack buffer编码帧, 其呈现区间包含 presentation timestamp
  7. 根据 overlapped frame 中音频的采样率,将 presentation timestampdecode timestamp 更新为最近的音频采样 时间戳。如果一个时间戳与两个音频采样时间戳的距离相等,则使用较大的时间戳(例如, floor(x * sample_rate + 0.5) / sample_rate)。

    例如,给定以下值:

    • overlapped frame呈现 时间戳等于 10。
    • overlapped frame 的采样率等于 8000 Hz
    • presentation timestamp 等于 10.01255
    • decode timestamp 等于 10.01255

    presentation timestampdecode timestamp 被更新为 10.0125,因为 10.01255 更接近 10 + 100/8000 (10.0125),而不是 10 + 101/8000 (10.012625)

  8. 如果用户代理不支持交叉淡化,则运行以下步骤:
    1. track buffer 中移除 overlapped frame
    2. track buffer 添加一个具有以下属性的静音帧:

      某些实现可以对插入的静音两侧的 编码帧应用淡入/淡出静音效果,以使过渡不那么突兀。

    3. 返回调用方,而不提供拼接帧。

      这是为了允许将 new coded frame 添加到 track buffer 中,就如同 overlapped frame 一开始就不存在于 track buffer 中一样。

  9. frame end timestamp 等于 presentation timestampframe duration 之和。
  10. splice end timestamp 等于 presentation timestamp 与 5 毫秒拼接持续时间之和。
  11. fade out coded frames 等于 overlapped frame,以及 track buffer 中所有 呈现时间戳大于 presentation timestamp 且小于 splice end timestamp 的其他帧。
  12. track buffer 中移除 fade out coded frames 中包含的所有帧。
  13. 返回一个具有以下属性的拼接帧:
    • 呈现 时间戳设置为 overlapped frame呈现时间戳
    • 解码时间戳设置为 overlapped frame解码时间戳
    • 编码帧持续时间 设置为 frame end timestampoverlapped frame呈现时间戳之间的差值。
    • 淡出编码帧等于 fade out coded frames
    • 淡入编码帧等于 new coded frame

      如果 new coded frame 的持续时间小于 5 毫秒,则 需要在 new coded frame 之后追加的编码 帧才能正确 渲染该拼接。

    • 拼接时间戳等于 presentation timestamp

    有关如何渲染此拼接帧的详细信息,请参阅音频拼接 渲染算法。

5.5.12 音频拼接渲染

当由 音频拼接帧算法生成的拼接帧需要由媒体元素进行渲染时,运行以下步骤:

  1. fade out coded frames 为在 拼接期间淡出的编码帧
  2. fade in coded frames 为在 拼接期间淡入的编码帧
  3. presentation timestampfade out coded frames 中第一个 编码帧的呈现时间戳
  4. end timestampfade in coded frames 中最后一个帧的呈现时间戳编码帧持续时间之和。
  5. splice timestamp 为拼接 开始处的呈现时间戳。这对应于 fade in coded frames 中第一个帧的呈现时间戳
  6. splice end timestamp 等于 splice timestamp 加上五毫秒。
  7. fade out samples 为通过解码 fade out coded frames 生成的采样。
  8. 裁剪 fade out samples,使其仅包含 presentation timestampsplice end timestamp 之间的采样。
  9. fade in samples 为通过解码 fade in coded frames 生成的采样。
  10. 如果 fade out samplesfade in samples 不具有相同的采样率 和 声道布局,则将 fade out samplesfade in samples 转换为 相同的 采样率和声道布局。
  11. output samples 为用于保存输出采样的缓冲区。
  12. fade out samplessplice timestampsplice end timestamp 之间的采样应用起始增益为 1、结束增益为 0 的线性增益淡出。
  13. fade in samplessplice timestampsplice end timestamp 之间的采样应用起始增益为 0、结束增益为 1 的线性增益淡入。
  14. fade out samplespresentation timestampsplice timestamp 之间的采样复制到 output samples 中。
  15. 对于 splice timestampsplice end timestamp 之间的每个采样,计算 fade out samples 中的一个采样与 fade in samples 中对应采样的和,并将结果存储到 output samples 中。
  16. fade in samplessplice end timestampend timestamp 之间的采样复制到 output samples 中。
  17. 渲染 output samples
注意

以下是该算法的图示。

音频拼接示意图

5.5.13 文本拼接帧

编码帧处理算法需要为两个重叠的定时文本 编码帧 生成拼接帧时,运行以下步骤:

  1. track buffer 为将包含该拼接的轨道缓冲区
  2. new coded frame 为正在添加到 track buffer 中、并触发了进行拼接需求的新编码帧
  3. presentation timestampnew coded frame呈现时间戳
  4. decode timestampnew coded frame 的解码时间戳。
  5. frame durationnew coded frame编码帧持续时间
  6. frame end timestamp 等于 presentation timestampframe duration 之和。
  7. first overlapped frametrack buffer 中,其 呈现区间包含 presentation timestamp编码帧
  8. overlapped presentation timestampfirst overlapped frame呈现时间戳
  9. overlapped frames 等于 first overlapped frame,以及 track buffer 中任何 其他 呈现时间戳大于 presentation timestamp 且小于 frame end timestamp 的帧。
  10. track buffer 中移除 overlapped frames 所包含的所有帧。
  11. first overlapped frame编码帧持续时间更新为 presentation timestamp 减去 overlapped presentation timestamp
  12. first overlapped frame 添加到 track buffer
  13. 返回调用方,而不提供拼接帧。

    这是为了允许将 new coded frame 添加到 track buffer 中,就好像它 一开始就没有与 track buffer 中的任何帧重叠一样。

6. SourceBufferList 接口

SourceBufferList 是用于 SourceBuffer 对象的简单容器对象。它提供只读的数组访问方式,并在列表被修改时触发事件。

WebIDL[Exposed=(Window,DedicatedWorker)]
interface SourceBufferList : EventTarget {
  readonly attribute unsigned long length;

  attribute EventHandler onaddsourcebuffer;
  attribute EventHandler onremovesourcebuffer;

  getter SourceBuffer (unsigned long index);
};

6.1 属性

length 类型为 unsigned long, 只读

表示列表中 SourceBuffer 对象的数量。

onaddsourcebuffer 类型为 EventHandler

处理 addsourcebuffer 事件的事件处理器。

onremovesourcebuffer 类型为 EventHandler

处理 removesourcebuffer 事件的事件处理器。

6.2 方法

getter

允许通过数组操作符(即 [])访问列表中的 SourceBuffer 对象。

调用此方法时,用户代理必须运行以下步骤:

  1. 如果 index 大于等于 length 属性,则返回 undefined 并中止这些步骤。
  2. 返回列表中第 indexSourceBuffer 对象。

6.3 事件摘要

事件名 接口 何时分发...
addsourcebuffer Event SourceBuffer 被添加到列表时。
removesourcebuffer Event SourceBuffer 被从列表中移除时。

7. ManagedMediaSource 接口

ManagedMediaSource 是一种会主动管理其内存内容的 MediaSource。 与 MediaSource 不同,用户代理可以出于任何 原因,通过 内存清理算法,从其 sourceBuffers (其中填充的是 ManagedSourceBuffer)中驱逐 内容。

:驱逐 原因
WebIDL[Exposed=(Window,DedicatedWorker)]
interface ManagedMediaSource : MediaSource {
  constructor();
  readonly attribute boolean streaming;
  attribute EventHandler onstartstreaming;
  attribute EventHandler onendstreaming;
};

7.1 属性

streaming

获取时:

  1. 返回该属性的当前值。

7.2 事件摘要

事件名 接口 何时分发...
startstreaming Event ManagedMediaSourcestreaming 属性从 false 变为 true 时。
endstreaming Event ManagedMediaSourcestreaming 属性从 true 变为 false 时。

7.3 算法

7.3.1 ManagedSourceBuffer 监控

以下步骤会周期性运行,每当 SourceBuffer 监控算法被调度执行时。

拥有 足够的受管数据以确保不间断播放,是一个由实现定义的条件,用户代理判断当前拥有足够数据以在展示过程中避免长时间卡顿。该条件会被持续评估,以决定何时切换 streaming 的值。这些切换表示用户代理认为数据缓冲充足或仍需更多数据。

能以高效方式检索和缓冲数据,是一个由实现定义的条件,用户代理判断其能以节能的方式抓取新数据,同时实现所需内存使用。

  1. 运行 MediaSourceSourceBuffer 监控算法。
  2. can play uninterrupted and efficiently 为一个标志,当下列条件同时满足时为 true: buffered 属性包含一个 TimeRanges,覆盖当前播放位置且拥有 足够的受管数据以确保不间断播放,并且 能以高效方式检索和缓冲数据
    如果 can play uninterrupted and efficiently 不等于 streaming在媒体元素上排队一个元素任务,其运行以下步骤:
    1. thisstreaming 属性设为 can play uninterrupted and efficiently
    2. 如果 can play uninterrupted and efficiently 为 false,触发事件 名为 startstreaming,于 ManagedMediaSource 处触发。
    3. 否则,触发事件 名为 endstreaming,于 ManagedMediaSource 处触发。

7.3.2 内存清理

  1. thissourceBuffers 中的每个 buffer
    1. 运行 buffer内存清理算法。

8. BufferedChangeEvent 接口

WebIDL[Exposed=(Window,DedicatedWorker)]
interface BufferedChangeEvent : Event {
  constructor(DOMString type, optional BufferedChangeEventInit eventInitDict = {});

  [SameObject] readonly attribute TimeRanges addedRanges;
  [SameObject] readonly attribute TimeRanges removedRanges;
};

dictionary BufferedChangeEventInit : EventInit {
  TimeRanges addedRanges;
  TimeRanges removedRanges;
};

8.1 属性

addedRanges
在上一次 updatestartupdateend 事件之间新增的时间范围(这通常发生在上一次运行 编码帧处理 算法期间)。
removedRanges
在上一次 updatestartupdateend 事件之间被移除的时间范围(这通常发生在上一次运行 编码帧移除编码帧清除 算法期间,或当用户代理因 内存清理 而驱逐内容时)。

9. ManagedSourceBuffer 接口

WebIDL[Exposed=(Window,DedicatedWorker)]
interface ManagedSourceBuffer : SourceBuffer {
  attribute EventHandler onbufferedchange;
};

9.1 属性

onbufferedchange

一个 事件处理程序 IDL 属性, 其 事件处理程序事件类型bufferedchange

9.2 事件摘要

事件名 接口 何时分发…
bufferedchange BufferedChangeEvent 在调用 appendBuffer()remove()endOfStream() 之后,或由于用户代理运行 内存清理 算法而导致 ManagedSourceBuffer 的缓冲范围发生变化时分发。

9.3 算法

9.3.1 缓冲变更

当对 ManagedSourceBuffer buffer 的所有会导致该 bufferbuffered 发生变化的操作完成后,运行以下步骤。即,一旦 appendBuffer()remove()内存清理 算法完成时。

  1. previous buffered ranges 等于变更发生之前 buffered 属性的值。
  2. new buffered ranges 等于新的 buffered TimeRanges
  3. added 等于用 new buffered ranges 减去 previous buffered ranges 的结果。
  4. removed 等于用 previous buffered ranges 减去 new buffered ranges 的结果。
  5. eventInitDict 为一个新的 BufferedChangeEventInit 字典,并用 added 初始化其 addedRanges,用 removed 初始化其 removedRanges
  6. 排队一个任务, 以 触发一个事件,事件名为 bufferedchange, 在 buffer 上使用 BufferedChangeEvent 接口, 并用 eventInitDict 初始化。

9.3.2 内存清理

  1. 如果 this 不在其 ManagedMediaSource 父对象的 activeSourceBuffers 中:
    1. 运行 编码帧移除 算法, 将 start 设为 0,end 设为正无穷大,然后中止这些步骤。
  2. removal ranges 为一组可从展示中被驱逐的展示时间范围, 以确保从 currentTime 起直到能够再次获取该展示内容期间的连续播放。
    注意

    不同实现可以采用不同策略选择 removal ranges, 因此 Web 应用不应依赖特定行为。Web 应用应监听 bufferedchange 事件以观察缓冲数据的部分是否被驱逐。

  3. 对于 removal ranges 中的每个范围,运行 编码帧移除 算法,其中 startend 分别等于该移除范围的起始与结束时间戳。

10. HTMLMediaElement 扩展

本节规定当一个 MediaSource 附加到元素时,现有 HTMLMediaElementseekableHTMLMediaElementbuffered 属性 必须 返回的内容,以及当其 srcObject 属性被设为 MediaSourceHandle 对象时, 现有 HTMLMediaElementsrcObject 属性 必须 执行的操作。

HTMLMediaElementseekable 属性返回一个新的静态 规范化 TimeRanges 对象,按以下步骤创建:

  1. 如果 MediaSource 是在一个 已终止或正在关闭的 DedicatedWorkerGlobalScope 中构造的,则返回一个空的 TimeRanges 对象,并中止这些 步骤。

    此情况旨在处理这样的实现:对于在 已通过 terminate() 终止,或由用户代理为 MediaSource 的 DedicatedWorkerGlobalScope 执行终止 worker(例如最终由 close() 执行所导致)的 DedicatedWorkerGlobalScope 中构造的 MediaSource,实现可能不再保留任何 先前有关已缓冲或可寻址媒体的信息。

    议题 277:Worker 中的 MSE:考虑(最终) 在 MediaSource 的 worker 终止时将已附加元素转换为错误状态/媒体 元素应该怎么做? mse-in-workers

    如果已附加的 worker MediaSource 的上下文被销毁,是否应该发生某种(最终的) 媒体元素错误状态转换?Chromium 的实验性 worker MSE 实现在该上下文销毁后仍保持元素的 readyState、networkState 和 error 与销毁前相同,不过 seekable 和 buffered 属性都会报告一个空的 TimeRange。

  2. recent durationrecent live seekable range 分别为 duration[[live seekable range]] 的最近值,按 如下方式确定:
    如果 MediaSource 是 在 Window 中构造的
    recent duration 设置为 duration,并将 recent live seekable range 设置为 [[live seekable range]]
    否则:
    recent durationrecent live seekable range 分别设置为 duration[[live seekable range]] 最近的值, 这些值通过处理 MediaSource 在每次 duration[[live seekable range]] 发生变化时发送到其 [[port to main]] 的隐式消息来更新。
  3. 如果 recent duration 等于 NaN:
    返回一个空的 TimeRanges 对象。
    如果 recent duration 等于正 Infinity:
    1. 如果 recent live seekable range 不为空:
      1. union rangesrecent live seekable rangeHTMLMediaElementbuffered 属性的并集。
      2. 返回一个单一范围,其起始时间等于 union ranges 中最早的起始时间,结束时间 等于 union ranges 中最大的结束时间,并中止这些步骤。
    2. 如果 HTMLMediaElementbuffered 属性返回 一个空的 TimeRanges 对象,则返回一个空的 TimeRanges 对象,并 中止这些步骤。
    3. 返回一个单一范围,其起始时间为 0,结束时间等于 HTMLMediaElementbuffered 属性所报告的最大结束时间。
    否则:
    返回一个单一范围,其起始时间为 0,结束时间等于 recent duration

HTMLMediaElementbuffered 属性返回一个静态的 规范化 TimeRanges 对象,按以下步骤计算。

  1. 如果 MediaSource 是在一个 已终止或正在关闭的 DedicatedWorkerGlobalScope 中构造的,则返回一个空的 TimeRanges 对象,并中止这些 步骤。

    此情况旨在处理这样的实现:对于在 已通过 terminate() 终止,或由用户代理为 MediaSource 的 DedicatedWorkerGlobalScope 执行终止 worker(例如最终由 close() 执行所导致)的 DedicatedWorkerGlobalScope 中构造的 MediaSource,实现可能不再保留任何 先前有关已缓冲或可寻址媒体的信息。

    议题 277:Worker 中的 MSE:考虑(最终) 在 MediaSource 的 worker 终止时将已附加元素转换为错误状态/媒体 元素应该怎么做? mse-in-workers

    如果已附加的 worker MediaSource 的上下文被销毁,是否应该发生某种(最终的) 媒体元素错误状态转换?Chromium 的实验性 worker MSE 实现在该上下文销毁后仍保持元素的 readyState、networkState 和 error 与销毁前相同,不过 seekable 和 buffered 属性都会报告一个空的 TimeRange。

  2. recent intersection ranges 按 以下方式确定:
    如果 MediaSource 是 在 Window 中构造的
    1. recent intersection ranges 等于一个 空的 TimeRanges 对象。
    2. 如果 activeSourceBuffers.length 不等于 0,则运行 以下步骤:
      1. active rangesactiveSourceBuffers 中每个 SourceBuffer 对象的 buffered 所返回的范围。
      2. highest end timeactive ranges 中最大的范围结束时间。
      3. recent intersection ranges 等于一个 TimeRanges 对象,其中包含从 0 到 highest end time 的单个范围。
      4. 对于 activeSourceBuffers 中的每个 SourceBuffer 对象, 运行以下步骤:
        1. source ranges 等于 当前 SourceBuffer 上的 buffered 属性所返回的范围。
        2. 如果 readyState 为 "ended", 则将 source ranges 中最后一个范围的结束时间设置为 highest end time
        3. new intersection ranges 等于 recent intersection rangessource ranges 的交集。
        4. recent intersection ranges 中的范围替换为 new intersection ranges
    否则:
    recent intersection rangesWindow 情况下的步骤所产生的 TimeRanges, 但这些步骤使用位于其 DedicatedWorkerGlobalScope 中的 MediaSource 及其 SourceBuffer 对象运行,并在每次 activeSourceBuffersreadyState 或任何会改变 activeSourceBuffers 中各个 buffered 属性值的 缓冲状态发生更新时,通过 [[port to main]] 隐式消息进行通信。

    如此频繁地重新计算和传递 recent intersection ranges 所产生的开销, 是允许实现灵活地使用其他机制(例如共享内存和锁)按需查询此 信息的原因之一,如跨上下文通信 模型中所述。

  3. 如果此属性的当前值尚未由此算法设置,或者 recent intersection ranges 不包含与此属性当前 值完全相同的范围信息,则将此属性的当前值更新为 recent intersection ranges
  4. 返回此属性的当前值。

如果某个 HTMLMediaElementsrcObject 属性被赋值为一个 MediaSourceHandle,则在调用元素的加载算法之前, 作为扩展的 HTMLMediaElementsrcObject setter 的同步步骤的一部分,将该 MediaSourceHandle[[has ever been assigned as srcobject]] 设为 true。

注意

这可防止再次传输该 MediaSourceHandle 对象, 并在尝试时提供明确的同步异常。

Issue

需要将 MediaSourceHandle 添加到 HTMLMediaElement 的 MediaProvider IDL typedef 及其与媒体提供者对象相关的文本中。

11. AudioTrack 扩展

本节对 [HTML] 中 AudioTrack 的定义进行扩展。

WebIDL[Exposed=Window]
partial interface AudioTrack {
  readonly attribute SourceBuffer? sourceBuffer;
};

属性

sourceBuffer,类型为 SourceBuffer, 只读,可为空

获取时,运行以下步骤:

如果此轨道是由一个 SourceBuffer 创建的,并且该 SourceBuffer 与 此轨道创建于同一 领域 中,而且该 SourceBuffer 尚未从其 父媒体源sourceBuffers 属性中移除:
返回创建此轨道的 SourceBuffer
否则:
返回 null。
例如,当 MediaSource 是在 DedicatedWorkerGlobalScope 中构造的时,此轨道由内部 create track 处理程序在 Window HTMLMediaElement 上创建,而创建它的 SourceBuffer 则存在于 DedicatedWorkerGlobalScope 中。 因为它们不位于同一 领域中, 所以此属性返回 null。

12. VideoTrack 扩展

本节对 [HTML] 中 VideoTrack 的定义进行扩展。

WebIDL[Exposed=Window]
partial interface VideoTrack {
  readonly attribute SourceBuffer? sourceBuffer;
};

属性

sourceBuffer,类型为 SourceBuffer, 只读,可为空

获取时,运行以下步骤:

如果此轨道是由一个 SourceBuffer 创建的,并且该 SourceBuffer 与 此轨道创建于同一 领域 中,而且该 SourceBuffer 尚未 从其 父媒体源sourceBuffers 属性中移除:
返回创建此轨道的 SourceBuffer
否则:
返回 null。
例如,当 MediaSource 是在 DedicatedWorkerGlobalScope 中构造的时,此轨道由内部 create track 处理程序在 Window HTMLMediaElement 上创建,而创建它的 SourceBuffer 则存在于 DedicatedWorkerGlobalScope 中。 因为它们不位于同一 领域中, 所以此属性返回 null。

13. TextTrack 扩展

本节对 [HTML] 中 TextTrack 的定义进行扩展。

WebIDL[Exposed=Window]
partial interface TextTrack {
  readonly attribute SourceBuffer? sourceBuffer;
};

属性

sourceBuffer,类型为 SourceBuffer, 只读,可为空

获取时,运行以下步骤:

如果此轨道是由一个 SourceBuffer 创建的,并且该 SourceBuffer 与 此轨道创建于同一 领域 中,而且该 SourceBuffer 尚未 从其 父媒体源sourceBuffers 属性中移除:
返回创建此轨道的 SourceBuffer
否则:
返回 null。
例如,当 MediaSource 是在 DedicatedWorkerGlobalScope 中构造的时,此轨道由内部 create track 处理程序在 Window HTMLMediaElement 上创建,而创建它的 SourceBuffer 则存在于 DedicatedWorkerGlobalScope 中。 因为它们不位于同一 领域中, 所以此属性返回 null。

14. 字节流格式

通过 appendBuffer()SourceBuffer 提供的字节构成一个 逻辑字节流。这些字节流的格式和语义在 字节流格式规范中定义。字节流 格式 注册表 [MSE-REGISTRY] 提供了 可传递给 addSourceBuffer()isTypeSupported()changeType() 的 MIME 类型与 使用该 MIME 类型解析新追加数据的 SourceBuffer 所预期的字节流格式之间的映射。鼓励实现 为其支持的字节流格式注册映射,以促进互操作性。 字节流格式注册表 [MSE-REGISTRY] 是这些 映射的权威来源。如果某个实现声称支持注册表中列出的 MIME 类型,则其 SourceBuffer 实现必须符合注册表条目中列出的字节流 格式规范

注册表中的字节流格式规范并非旨在定义新的 存储格式。它们只是概述本规范的实现将接受的现有存储格式结构 的子集。

字节流格式解析和验证在段 解析器循环 算法中实现。

本节提供适用于所有字节流格式规范的一般要求:

字节流规范必须至少定义确保上述 要求成立的约束。可以定义额外约束,例如用于简化 实现。

15. 一致性

除标记为非规范性的章节外,本规范中的所有编写指南、图示、示例与注记均为非规范性内容。除此之外的内容均为规范性内容。

本文中的关键词 MAYMUSTMUST NOTSHOULDSHOULD NOT 按照 BCP 14 [RFC2119] 与 [RFC8174] 的描述进行解释,且仅当这些词以大写形式出现时(如本段所示)方适用。

16. 示例

16.1 使用 Media Source Extensions

<video id="v" autoplay></video>
<script>
const video = document.getElementById("v");
const mediaSource = new MediaSource();
mediaSource.addEventListener("sourceopen", onSourceOpen);
video.src = window.URL.createObjectURL(mediaSource);

async function onSourceOpen(e) {
  const mediaSource = e.target;

  if (mediaSource.sourceBuffers.length > 0) return;

  const sourceBuffer = mediaSource.addSourceBuffer(
    'video/webm; codecs="vorbis,vp8"',
  );

  video.addEventListener("seeking", (e) => onSeeking(mediaSource, e.target));
  video.addEventListener("progress", () =>
    appendNextMediaSegment(mediaSource),
  );

  try {
    const initSegment = await getInitializationSegment();

    if (initSegment == null) {
      // 获取初始化分段出错。使用错误结束流。
      mediaSource.endOfStream("network");
      return;
    }

    // 追加初始化分段。
    sourceBuffer.addEventListener("updateend", function firstAppendHandler() {
      sourceBuffer.removeEventListener("updateend", firstAppendHandler);

      // 追加一些初始媒体数据。
      appendNextMediaSegment(mediaSource);
    });

    sourceBuffer.appendBuffer(initSegment);
  } catch (error) {
    // 处理在获取初始化分段期间可能发生的错误。
    console.error("获取初始化分段出错:", error);
    mediaSource.endOfStream("network");
  }
}

async function appendNextMediaSegment(mediaSource) {
  if (
    mediaSource.readyState === "closed" ||
    mediaSource.sourceBuffers[0].updating
  )
    return;

  // 若流数据已用尽,则结束流。
  if (!haveMoreMediaSegments()) {
    mediaSource.endOfStream();
    return;
  }

  try {
    const mediaSegment = await getNextMediaSegment();

    // 注意: 若 mediaSource.readyState == "ended",此 appendBuffer() 调用将
    // 使其状态转为 "open"。Web 应用应准备处理多个 "sourceopen" 事件。
    mediaSource.sourceBuffers[0].appendBuffer(mediaSegment);
  }
  catch (error) {
    // 处理在获取媒体分段期间可能发生的错误。
    console.error("获取媒体分段出错:", error);
    mediaSource.endOfStream("network");
  }
}

function onSeeking(mediaSource, video) {
  if (mediaSource.readyState === "open") {
    // 中止当前分段追加。
    mediaSource.sourceBuffers[0].abort();
  }

  // 通知媒体分段加载逻辑从新的播放位置开始抓取数据。
  seekToMediaSegmentAt(video.currentTime);

  // 从新的播放位置追加一个媒体分段。
  appendNextMediaSegment(mediaSource);
}

function onProgress(mediaSource, e) {
  appendNextMediaSegment(mediaSource);
}

// 获取初始化分段的异步函数示例
async function getInitializationSegment() {
  // 在此实现获取初始化分段的逻辑
  // 这里只是占位函数
}

// 检查是否还有更多媒体分段的示例函数
function haveMoreMediaSegments() {
  // 在此实现是否仍有更多媒体分段的判断逻辑
  // 这里只是占位函数
}

// 获取下一个媒体分段的示例函数
async function getNextMediaSegment() {
  // 在此实现获取下一个媒体分段的逻辑
  // 这里只是占位函数
}

// 跳转到特定媒体分段的示例函数
function seekToMediaSegmentAt(currentTime) {
  // 在此实现跳转逻辑
  // 这里只是占位函数
}
</script>

16.2 使用受管媒体源(Managed Media Source)

<script>
async function setUpVideoStream() {
  // 指定的视频格式与编解码器
  const mediaType = 'video/mp4; codecs="mp4a.40.2,avc1.4d4015"';

  // 检查该视频格式/编解码器是否受支持。
  if (!window.ManagedMediaSource?.isTypeSupported(mediaType)) {
    return; // 不受支持,采取其他方案。
  }

  // 设置视频与其受管源。
  const video = document.createElement("video");
  const source = new ManagedMediaSource();

  video.controls = true;

  await new Promise((resolve) => {
    video.src = URL.createObjectURL(source);
    source.addEventListener("sourceopen", resolve, { once: true });
    document.body.appendChild(video);
  });

  const sourceBuffer = source.addSourceBuffer(mediaType);

  // 设置事件处理程序
  sourceBuffer.onbufferedchange = (e) => {
    console.log("触发 onbufferedchange 事件。");
    console.log(`新增范围: ${timeRangesToString(e.addedRanges)}`);
    console.log(`移除范围: ${timeRangesToString(e.removedRanges)}`);
  };

  source.onstartstreaming = async () => {
    const response = await fetch("./videos/bipbop.mp4");
    const buffer = await response.arrayBuffer();
    await new Promise((resolve) => {
      sourceBuffer.addEventListener("updateend", resolve, { once: true });
      sourceBuffer.appendBuffer(buffer);
    });
  };

  source.onendstreaming = async () => {
    // 在此停止抓取新分段
  };
}

// 辅助函数...
function timeRangesToString(timeRanges) {
  const ranges = [];
  for (let i = 0; i < timeRanges.length; i++) {
    ranges.push([timeRanges.start(i), timeRanges.end(i)]);
  }
  return "[" + ranges.map(([start, end]) => `[${start}, ${end})` ) + "]";     
}
</script>
<body onload="setUpVideoStream()"></body>

17. 致谢

编辑们感谢 Alex Giladi、Bob Lund、Chris Needham、Chris Poole、Chris Wilson、Cyril Concolato、Dale Curtis、David Dorwin、David Singer、Duncan Rowden、François Daoust、Frank Galligan、Glenn Adams、Jer Noble、Joe Steele、John Simmons、Kagami Sascha Rosylight、Kevin Streeter、Marcos Cáceres、Mark Vickers、Matt Ward、Matthew Gregan、 Michael(tm) Smith、Michael Thornburgh、Mounir Lamouri、Paul Adenot、Philip Jägenstedt、 Philippe Le Hegaret、Pierre Lemieux、Ralph Giles、Steven Robertson 与 Tatsuya Igarashi 对本规范所作的贡献。

A. VideoPlaybackQuality

本节为非规范性内容。

在本规范先前修订版中描述的视频播放质量指标(例如 候选推荐标准 的第 5 与第 10 节) 现已作为 [MEDIA-PLAYBACK-QUALITY] 的一部分进行制定。某些实现可能已支持较早草案中的 VideoPlaybackQuality 对象及 HTMLVideoElement 扩展方法 getVideoPlaybackQuality(), 这些均见于之前的修订版中。

B. 问题摘要

C. 参考文献

C.1 规范性引用

[dom]
DOM Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://dom.spec.whatwg.org/
[ECMASCRIPT]
ECMAScript Language Specification. Ecma International. URL: https://tc39.es/ecma262/multipage/
[FILEAPI]
File API. Marijn Kruisselbrink. W3C. 4 June 2026. W3C Working Draft. URL: https://www.w3.org/TR/FileAPI/
[HTML]
HTML Standard. Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. Living Standard. URL: https://html.spec.whatwg.org/multipage/
[infra]
Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
[MSE-REGISTRY]
Media Source Extensions™ Byte Stream Format Registry. Matthew Wolenetz; Jerry Smith; Aaron Colwell. W3C. URL: https://www.w3.org/TR/mse-byte-stream-format-registry/
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/info/rfc2119/
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/info/rfc8174/
[WEBIDL]
Web IDL Standard. Edgar Chen; Timothy Gu. WHATWG. Living Standard. URL: https://webidl.spec.whatwg.org/

C.2 非规范性引用

[INBANDTRACKS]
在 HTML 中从媒体容器为内嵌媒体资源轨道提供来源. Silvia Pfeiffer; Bob Lund. W3C. 26 April 2015. Unofficial Draft. URL: https://dev.w3.org/html5/html-sourcing-inband-tracks/
[MEDIA-PLAYBACK-QUALITY]
Media Playback Quality。 Mounir Lamouri; Chris Cunningham. W3C. W3C Editor's Draft. URL: https://w3c.github.io/media-playback-quality/
[url]
URL Standard. Anne van Kesteren. WHATWG. Living Standard. URL: https://url.spec.whatwg.org/