媒体源扩展™ (Media Source Extensions™)

W3C 工作草案

关于此文档的更多细节
此版本
https://w3org.cn/TR/2025/WD-media-source-2-20251104/
最新发布版本
https://w3org.cn/TR/media-source-2/
最新编辑草案
https://w3c.github.io/media-source/
历史
https://w3org.cn/standards/history/media-source-2/
提交历史
最新推荐标准
https://w3org.cn/TR/2016/REC-media-source-20161117/
编辑
(Apple Inc.)
Mark Watson (Netflix Inc.)
前任编辑
(W3C 特邀专家) - 截止至
Jerry Smith (Microsoft Corporation) - 截止至
Aaron Colwell (Google Inc.) - 截止至
Adrian Bateman (Microsoft Corporation) - 截止至
反馈
GitHub w3c/media-source (pull 请求, 新建 issue, 打开的 issues)
public-media-wg@w3.org,邮件主题为 [media-source-2] … 消息主题 … (存档)
浏览器支持
caniuse.com

摘要

本规范扩展了 HTMLMediaElement [HTML],允许 JavaScript 生成用于播放的媒体流。允许 JavaScript 生成流,为自适应流媒体和直播流时移等多种用例提供了便利。

本文档状态

本节描述了本文件发布时的状态。当前的 W3C 出版物列表和本技术报告的最新版本可以在 W3C 标准和草案索引中找到。

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

有关自上一版本以来的全部变更列表,请参阅 提交记录 (commits)

工作组维护着一份 编辑尚未尝试解决的所有错误报告列表

实现者应注意,本规范尚不稳定。不参与讨论的实现者可能会发现规范在他们不知情的情况下发生不兼容的改变。 有意在最终达到“候选推荐标准”阶段之前实施本规范的供应商,应跟踪 GitHub 仓库并参与讨论。

本文档由 媒体工作组 (Media Working Group) 作为工作草案发布,使用 推荐标准轨道 (Recommendation track)

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

这是一份草案文件,可能随时被其他文件更新、替换或废弃。将其作为进展中的工作以外的引用是不恰当的。

本文档由在一个受 W3C 专利政策约束下运作的组织制作。W3C 维护着一份 与该组交付成果相关的公开专利披露列表;该页面还包含了披露专利的说明。任何拥有实际知识并认为其持有的专利包含 必要权利要求 (Essential Claim(s)) 的个人,必须根据 W3C 专利政策第 6 节披露相关信息。

本文件受 2025 年 8 月 18 日 W3C 流程文档约束。

1. 简介

本节是非规范性的。

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

Media Source Pipeline Model Diagram
1 媒体源流水线模型图

1.1 目标

本规范的设计旨在实现以下目标:

本规范定义了:

2. 定义

活动轨道缓冲区 (Active Track Buffers)

提供 编码帧轨道缓冲区,用于 enabledaudioTracksselectedvideoTracks,以及 "showing""hidden"textTracks。所有这些轨道都关联到 SourceBuffer 对象在 activeSourceBuffers 列表中。

追加窗口 (Append Window)

一种用于在追加时过滤 编码帧表示时间戳范围。追加窗口表示一个具有单一开始时间和结束时间的连续时间范围。在此范围内的 表示时间戳的编码帧被允许追加到 SourceBuffer 中,而超出此范围的编码帧则被过滤掉。追加窗口的开始和结束时间分别由 appendWindowStartappendWindowEnd 属性控制。

编码帧 (Coded Frame)

一种媒体数据单元,具有 表示时间戳解码时间戳编码帧持续时间

编码帧持续时间 (Coded Frame Duration)

编码帧的持续时间。对于视频和文本,持续时间指示视频帧或文本 应当显示的时长。对于音频,持续时间表示编码帧内包含的所有采样的总和。例如,如果一个音频帧包含 441 个采样,采样率为 44100Hz,则帧持续时间为 10 毫秒。

编码帧结束时间戳 (Coded Frame End Timestamp)

编码帧 表示时间戳与其 编码帧持续时间之和。它表示紧随该编码帧之后的 表示时间戳

编码帧组 (Coded Frame Group)

一组相邻且具有单调递增 解码时间戳且中间没有任何间隙的 编码帧编码帧处理算法和 abort() 调用检测到的不连续性会触发新的编码帧组的开始。

解码时间戳 (Decode Timestamp)

解码时间戳指示该帧需要被解码的最晚时间(假设该帧及任何依赖帧的瞬时解码和渲染,这等于 表示顺序 中依赖于该帧的最早帧的 表示时间戳)。如果帧可以按非 表示顺序 解码,则解码时间戳 必须存在于或可从字节流中导出。如果不是这种情况,用户代理 必须运行 追加错误算法。如果帧不能按非 表示顺序 解码且字节流中不存在解码时间戳,则解码时间戳等于 表示时间戳

初始化段 (Initialization Segment)

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

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

媒体段 (Media Segment)

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

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

MediaSource 对象 URL

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

这些 URL 与 blob URL 相同,只是该功能定义中引用 FileBlob 对象的任何内容,现特此扩展为也适用于 MediaSource 对象。

MediaSource 对象 URL 的 源 (origin) 是在调用 createObjectURL()此对象相关设置对象

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

父媒体源 (Parent Media Source)

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

表示开始时间 (Presentation Start Time)

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

为了确定 HTMLMediaElementbuffered 是否包含一个涵盖当前播放位置的 TimeRanges,实现 可以选择允许在 表示开始时间 之后和第一个 TimeRanges 之前的当前播放位置,如果该 TimeRanges表示开始时间 后经过很短的时间(例如 1 秒)内开始,则可以播放该第一个 TimeRanges。此允许照顾了多路复用流通常不会在 表示开始时间 处精确开始所有轨道的现实。无论此项允许如何,实现 必须报告实际的缓冲范围。

表示区间 (Presentation Interval)

编码帧 的表示区间是从其 表示时间戳表示时间戳 加上 编码帧持续时间 的时间区间。例如,如果一个编码帧的表示时间戳为 10 秒,编码帧持续时间为 100 毫秒,则表示区间为 [10-10.1)。注意,区间的开始是包含的,但结尾是排他的。

表示顺序 (Presentation Order)

编码帧 在呈现中渲染的顺序。表示顺序是通过按 表示时间戳 单调递增的顺序对 编码帧 进行排序来实现的。

表示时间戳 (Presentation Timestamp)

对呈现中特定时间的引用。编码帧 中的表示时间戳指示帧应当被渲染的时间。

随机访问点 (Random Access Point)

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

SourceBuffer 字节流格式规范

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

SourceBuffer 配置

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

实现 必须支持至少 1 个 MediaSource 对象,并具备以下配置:

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

MediaSource 对象 必须支持上述每种配置,但它们仅被要求一次支持一种配置。同时支持多种配置或额外配置属于实现质量问题。

轨道描述 (Track Description)

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

轨道 ID (Track ID)

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

3. MediaSource 接口

MediaSource 接口代表 HTMLMediaElement 的媒体数据源。它跟踪该源的 readyState,以及可用于向呈现中添加媒体数据的 SourceBuffer 对象列表。MediaSource 对象由 Web 应用程序创建,然后附加到 HTMLMediaElement。应用程序使用 sourceBuffers 中的 SourceBuffer 对象向此源添加媒体数据。HTMLMediaElement 在播放期间需要时会从 MediaSource 对象获取此媒体数据。

每个 MediaSource 对象都有一个存储 标准化 TimeRanges 对象[[实时可跳转范围]] 内部槽。它在创建 MediaSource 对象时被初始化为一个空的 TimeRanges 对象,由 setLiveSeekableRange()clearLiveSeekableRange() 维护,并用于 10. HTMLMediaElement 扩展以修改 HTMLMediaElementseekable 行为。

每个 MediaSource 对象都有一个存储 boolean[[是否曾被附加]] 内部槽。它在创建 MediaSource 对象时被初始化为 false,并如 附加到媒体元素 算法中所述,在扩展的 HTMLMediaElement资源获取算法 中被设置为 true。扩展的 资源获取算法 使用此内部槽有条件地拒绝附加使用 HTMLMediaElementsrcObject 属性上设置的 MediaSourceHandleMediaSource

WebIDLenum ReadyState {
  "closed",
  "open",
  "ended",
};
closed
指示该源当前未附加到媒体元素。
开启
该源已被媒体元素打开,并准备好将数据追加到 MediaSourcesourceBuffers 中的 SourceBuffer 对象。
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. 创建的句柄 为创建新的 MediaSourceHandle 对象和关联资源的结果,并在内部链接到此 MediaSource
    2. 更新该属性为 创建的句柄
  2. 返回作为此属性值的 MediaSourceHandle 对象。

3.2 sourceBuffers 属性

包含与此 MediaSource 关联的 SourceBuffer 对象列表。当 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. 如果 sourceBuffers 中的任何 SourceBufferupdating 属性等于 true,则抛出 InvalidStateError 异常并中止这些步骤。
  4. 运行 时长变更 算法,将 新时长 设置为分配给此属性的值。

    时长变更 算法将调整 新时长 更高,如果存在任何当前缓冲的、具有更高结束时间的编码帧。

    appendBuffer()endOfStream() 在特定情况下可以更新时长。

3.6 canConstructInDedicatedWorker 属性

返回 true。

此属性支持主线程和专用 worker 对在专用 worker 中创建和使用 MediaSource 对象的支持情况进行特征检测,并缓解了对高延迟检测 polyfill 的需求(例如尝试从专用 worker 创建 MediaSource 对象,特别是如果不支持该功能时)。

3.7 addSourceBuffer() 方法

将新的 SourceBuffer 添加到 sourceBuffers

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

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

  4. 如果 readyState 属性不处于 "open" 状态,则抛出 InvalidStateError 异常并中止这些步骤。
  5. 如果 此对象 是一个 ManagedMediaSource,则令 bufferManagedSourceBuffer 的新实例,否则为 SourceBuffer 的新实例,并带有它们各自关联的资源。
  6. buffer[[生成时间戳标志]] 设置为与 type 关联的 Media Source Extensions™ 字节流格式注册表 条目中“生成时间戳标志”列中的值。
  7. 如果 buffer[[生成时间戳标志]] 为 true,则将 buffermode 设置为 "sequence"。否则,将 buffermode 设置为 "segments"。
  8. buffer 追加此对象sourceBuffers 中。
  9. 排队一个任务,在 此对象sourceBuffers触发一个名为 addsourcebuffer 的事件。
  10. 返回 buffer

3.8 removeSourceBuffer() 方法

sourceBuffers 中移除 SourceBuffer

  1. 如果 sourceBuffer 指定的对象不在 sourceBuffers 中,则抛出 NotFoundError 异常并中止这些步骤。
  2. 如果 sourceBuffer.updating 属性等于 true,则运行以下步骤:
    1. 如果正在运行,则中止 缓冲区追加 算法。
    2. sourceBuffer.updating 属性设置为 false。
    3. 排队一个任务,在 sourceBuffer触发一个名为 abort 的事件。
    4. 排队一个任务,在 sourceBuffer触发一个名为 updateend 的事件。
  3. SourceBuffer 音频轨道列表 等于 sourceBuffer.audioTracks 返回的 AudioTrackList 对象。
  4. 如果 SourceBuffer 音频轨道列表 不为空,则运行以下步骤:
    1. 对于 SourceBuffer 音频轨道列表 中的每个 AudioTrack 对象,运行以下步骤:
      1. AudioTrack 对象上的 sourceBuffer 属性设置为 null。
      2. SourceBuffer 音频轨道列表 中移除 AudioTrack 对象。

        这应当触发 AudioTrackList [HTML] 逻辑来 排队一个任务,在 SourceBuffer 音频轨道列表触发一个名为 removetrack 的事件,使用 TrackEvent,其 track 属性初始化为该 AudioTrack 对象。如果 AudioTrack 对象上的 enabled 属性在此移除步骤开始时为 true,则这也应当触发 AudioTrackList [HTML] 逻辑来 排队一个任务,在 SourceBuffer 音频轨道列表触发一个名为 change 的事件。

      3. 使用 必要时镜像 算法在 Window 中运行以下步骤,以从媒体元素中移除 AudioTrack 对象(或者如果 MediaSource 对象是在 DedicatedWorkerGlobalScope 中构建的,则改为其 Window 镜像)。
        1. HTMLMediaElement 音频轨道列表 等于 HTMLMediaElement 上 audioTracks 属性返回的 AudioTrackList 对象。
        2. HTMLMediaElement 音频轨道列表 中移除该 AudioTrack 对象。

          这应当触发 AudioTrackList [HTML] 逻辑来 排队一个任务,在 HTMLMediaElement 音频轨道列表触发一个名为 removetrack 的事件,使用 TrackEvent,其 track 属性初始化为该 AudioTrack 对象。如果 AudioTrack 对象上的 enabled 属性在此移除步骤开始时为 true,则这也应当触发 AudioTrackList [HTML] 逻辑来 排队一个任务,在 HTMLMediaElement 音频轨道列表触发一个名为 change 的事件。

  5. SourceBuffer 视频轨道列表 等于 sourceBuffer.videoTracks 返回的 VideoTrackList 对象。
  6. 如果 SourceBuffer 视频轨道列表 不为空,则运行以下步骤:
    1. 对于 SourceBuffer 视频轨道列表 中的每个 VideoTrack 对象,运行以下步骤:
      1. VideoTrack 对象上的 sourceBuffer 属性设置为 null。
      2. SourceBuffer 视频轨道列表 中移除 VideoTrack 对象。

        这应当触发 VideoTrackList [HTML] 逻辑来 排队一个任务,在 SourceBuffer 视频轨道列表触发一个名为 removetrack 的事件,使用 TrackEvent,其 track 属性初始化为该 VideoTrack 对象。如果 VideoTrack 对象上的 selected 属性在此移除步骤开始时为 true,则这也应当触发 VideoTrackList [HTML] 逻辑来 排队一个任务,在 SourceBuffer 视频轨道列表触发一个名为 change 的事件。

      3. 使用 必要时镜像 算法在 Window 中运行以下步骤,以从媒体元素中移除 VideoTrack 对象(或者如果 MediaSource 对象是在 DedicatedWorkerGlobalScope 中构建的,则改为其 Window 镜像)。
        1. HTMLMediaElement 视频轨道列表 等于 HTMLMediaElement 上 videoTracks 属性返回的 VideoTrackList 对象。
        2. HTMLMediaElement 视频轨道列表 中移除该 VideoTrack 对象。

          这应当触发 VideoTrackList [HTML] 逻辑来 排队一个任务,在 HTMLMediaElement 视频轨道列表触发一个名为 removetrack 的事件,使用 TrackEvent,其 track 属性初始化为该 VideoTrack 对象。如果 VideoTrack 对象上的 selected 属性在此移除步骤开始时为 true,则这也应当触发 VideoTrackList [HTML] 逻辑来 排队一个任务,在 HTMLMediaElement 视频轨道列表触发一个名为 change 的事件。

  7. SourceBuffer 文本轨道列表 等于 sourceBuffer.textTracks 返回的 TextTrackList 对象。
  8. 如果 SourceBuffer 文本轨道列表 不为空,则运行以下步骤:
    1. 对于 SourceBuffer 文本轨道列表 中的每个 TextTrack 对象,运行以下步骤:
      1. TextTrack 对象上的 sourceBuffer 属性设置为 null。
      2. SourceBuffer 文本轨道列表 中移除 TextTrack 对象。

        这应触发 TextTrackList [HTML] 逻辑,以 排队一个任务,在 SourceBuffer textTracks 列表触发一个事件,该事件名为 removetrack,使用 TrackEvent,并将 track 属性初始化为 TextTrack 对象。如果在移除步骤开始时 TextTrack 对象上的 mode 属性为 "showing""hidden",则这还应触发 TextTrackList [HTML] 逻辑,以 排队一个任务,在 SourceBuffer textTracks 列表触发一个事件,该事件名为 change

      3. 使用 必要时镜像 (mirror if necessary) 算法在 Window 中运行以下步骤,以从媒体元素中移除 TextTrack 对象(或者,如果 MediaSource 对象是在 DedicatedWorkerGlobalScope 中构造的,则移除其 Window 镜像)。
        1. HTMLMediaElement textTracks 列表 等于 HTMLMediaElement 上 textTracks 属性返回的 TextTrackList 对象。
        2. HTMLMediaElement textTracks 列表 中移除 TextTrack 对象。

          这应触发 TextTrackList [HTML] 逻辑,以 排队一个任务,在 HTMLMediaElement textTracks 列表触发一个事件,该事件名为 removetrack,使用 TrackEvent,并将 track 属性初始化为 TextTrack 对象。如果在移除步骤开始时 TextTrack 对象上的 mode 属性为 "showing""hidden",则这还应触发 TextTrackList [HTML] 逻辑,以 排队一个任务,在 HTMLMediaElement textTracks 列表触发一个事件,该事件名为 change

  9. 如果 sourceBufferactiveSourceBuffers 中,则从 activeSourceBuffers 中移除 sourceBuffer,并 排队一个任务,在 SourceBufferList(由 activeSourceBuffers 返回)上 触发一个事件,该事件名为 removesourcebuffer
  10. sourceBuffers 中移除 sourceBuffer,并 排队一个任务,在 SourceBufferList(由 sourceBuffers 返回)上 触发一个事件,该事件名为 removesourcebuffer
  11. 销毁 sourceBuffer 的所有资源。

3.9 endOfStream() 方法

表示流的结束。

  1. 如果 readyState 属性不在 "open" 状态,则抛出一个 InvalidStateError 异常并中止这些步骤。
  2. 如果 sourceBuffers 中的任何 SourceBuffer 上的 updating 属性等于 true,则抛出一个 InvalidStateError 异常并中止这些步骤。
  3. 运行 流结束 (end of stream) 算法,并将 error 参数设置为 error

3.10 setLiveSeekableRange() 方法

更新用于第 10. HTMLMediaElement 扩展 章节的 [[live seekable range]],以修改 HTMLMediaElementseekable 行为。

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

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

3.11 clearLiveSeekableRange() 方法

更新用于第 10. HTMLMediaElement 扩展 章节的 [[live seekable range]],以修改 HTMLMediaElementseekable 行为。

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

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

3.12 isTypeSupported() 方法

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

如果此方法返回 true,则仅表示 MediaSource 实现有能力为指定的 MIME 类型创建 SourceBuffer 对象。如果可用资源不足以支持添加新的 SourceBufferaddSourceBuffer() 调用 仍应失败。

此方法返回 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 事件摘要

事件名称 Interface 调度时间:当……时
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 时,每个上下文都有依赖于对方信息的算法。

HTMLMediaElement 仅向 Window 上下文公开,但本规范中定义的 MediaSource 及相关对象在 WindowDedicatedWorkerGlobalScope 上下文中均被公开。这允许应用程序在上述任一上下文中构造 MediaSource 对象,并使用 MediaSource 对象 URLMediaSourceHandle(如 附加到媒体元素 算法中所述)将其附加到 Window 上下文中的 HTMLMediaElement 对象。 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,两者均在附加期间设置,并在分离时置空。除非附加到 DedicatedWorkerGlobalScope MediaSource,否则 [[port to worker]][[channel with worker]] 均为 null。

本规范中需要将信息从 Window HTMLMediaElement 通信到附加的 DedicatedWorkerGlobalScope MediaSource 的算法(反之亦然)将隐式使用这些内部端口向对应方发布消息,消息的隐式处理程序将运行算法中所述的步骤。

3.15 算法

3.15.1 附加到媒体元素

根据 MediaSource 对象是在 Window 还是在 DedicatedWorkerGlobalScope 中构造,将 MediaSource 附加到媒体元素有不同的机制。

如果 资源获取算法 是使用 MediaSource 对象、MediaSourceHandle 对象或对象为 MediaSource 对象的 URL 记录调用的,则令 mode 为 local,跳过 资源获取算法 中的第一步(该步骤可能会将 mode 设置为 remote),并继续执行 资源获取算法

资源获取算法 的第一步预期最终将与为对象是媒体提供者对象的 URL 记录选择 local 模式保持一致。其目的是,如果 HTMLMediaElementsrc 属性或选定的子 sourcesrc 属性在各自的 src 属性上次更改时,是一个匹配 MediaSource 对象 URLblob: URL,则该 MediaSource 对象即被用作 资源获取算法 中 local 模式逻辑下的媒体提供者对象和当前媒体资源。这也意味着当附加了 MediaSource 对象时,包含对任何 preload 属性观察的 remote 模式逻辑将被跳过。即使在对 [HTML] 进行了最终更改之后,当当前媒体资源是 MediaSource 对象时,仍需要在 local 模式逻辑的开头执行以下步骤。

资源获取算法 的“否则 (mode 为 local)”部分的开头,执行下述附加步骤。

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

  1. 如果 资源获取算法 是使用 MediaSource 对象、MediaSourceHandle 对象或对象为 MediaSource 对象的 URL 记录调用的,则
    如果媒体提供者对象是一个 URL 记录,其对象是构造于 DedicatedWorkerGlobalScope 中的 MediaSource,例如试图使用来自 DedicatedWorkerGlobalScope MediaSourceMediaSource 对象 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 MediaSourceMediaSource 对象 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]] 设置为 [[channel with worker]]port1 值。
      3. 执行 StructuredSerializeWithTransfer,并将 [[channel with worker]]port2 作为值和 transferList 的唯一成员,令结果为 serialized port2
      4. 排队一个任务MediaSourceDedicatedWorkerGlobalScope 上,该任务将
        1. 执行 StructuredDeserializeWithTransfer,使用 serialized port2DedicatedWorkerGlobalScope领域 (realm),并将 [[port to main]] 设置为传输后的 [[channel with worker]]port2 值的反序列化克隆。
        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. 排队一个任务,在 MediaSource触发一个事件,该事件名为 sourceopen
    4. 通过运行剩余的 Otherwise (mode is local) 步骤继续执行 资源获取算法,并满足以下要求
      1. 资源获取算法媒体数据处理步骤列表 中提及“下载”、“已接收字节”或“每当当前媒体资源有新数据可用时”的文本,是指通过 appendBuffer() 传入的数据。
      2. 资源获取算法媒体数据处理步骤列表 中对 HTTP 的引用不适用,因为当附加了 MediaSource 时,HTMLMediaElement 不通过 HTTP 获取媒体数据。

附加的 MediaSource 不使用 资源获取算法 中的 remote 模式步骤,因此媒体元素不会触发 "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 中构造的
    Window MediaSource 上继续运行这些步骤的其余部分。
  2. [[port to main]] 设置为 null。
  3. readyState 属性设置为 "closed"。
  4. 如果 thisManagedMediaSource,则将 streaming 属性设置为 false
  5. duration 更新为 NaN。
  6. activeSourceBuffers 中移除所有 SourceBuffer 对象。
  7. 排队一个任务,在 activeSourceBuffers触发一个事件,该事件名为 removesourcebuffer
  8. sourceBuffers 中移除所有 SourceBuffer 对象。
  9. 排队一个任务,在 sourceBuffers触发一个事件,该事件名为 removesourcebuffer
  10. 排队一个任务,在 MediaSource触发一个事件,该事件名为 sourceclose

今后,此算法旨在被外部调用,并在任何需要将附加的 MediaSource(如有)从媒体元素分离的情况下运行。除了或替代媒体元素转换为 NETWORK_EMPTY 的情况外,它 可以在 HTMLMediaElement [HTML] 操作(如 load())和 资源获取算法 失败时调用。资源获取算法失败是指那些中止资源获取算法或资源选择算法的失败,但“最终步骤” [HTML] 不被视为触发分离的失败。

3.15.3 搜寻 (Seeking)

作为 搜寻算法 (seek algorithm) 的“等待直到用户代理确定新播放位置的媒体数据是否可用,如果可用,则等待直到它解码出足够的数据来播放该位置”步骤的一部分,运行以下步骤

  1. 媒体元素在 activeSourceBuffers 中的每个 SourceBuffer 对象中查找包含 新播放位置媒体段 (media segments)HTMLMediaElementbuffered 属性当前值中 TimeRanges 内的任何位置都已缓存了该位置所需的所有媒体段。

    如果 新播放位置 不在 HTMLMediaElementbuffered 的任何 TimeRanges
    1. 如果 HTMLMediaElementreadyState 属性大于 HAVE_METADATA,则将 HTMLMediaElementreadyState 属性设置为 HAVE_METADATA

      根据 HTMLMediaElement 就绪状态 (ready states) [HTML] 逻辑,HTMLMediaElementreadyState 更改可能会触发 HTMLMediaElement 上的事件。

    2. 媒体元素等待直到 appendBuffer() 调用导致 编码帧处理 (coded frame processing) 算法将 HTMLMediaElementreadyState 属性设置为大于 HAVE_METADATA 的值。

      Web 应用程序可以使用 bufferedHTMLMediaElementbuffered 来确定媒体元素需要什么来恢复播放。

    否则
    继续

    如果 readyState 属性为 "ended",且 新播放位置HTMLMediaElementbuffered 中的某个 TimeRanges 内,则搜寻操作必须在此处继续完成,即使一个或多个当前选定或启用的轨道缓冲区的最大范围结束时间戳小于 新播放位置。这种情况应该仅由于 buffered 中的逻辑而在 readyState 为 "ended" 时发生。

  2. 媒体元素重置所有解码器,并使用来自适当 初始化段 (initialization segment) 的数据初始化每个解码器。
  3. 媒体元素将来自 活跃轨道缓冲区 (active track buffers)编码帧 (coded frames) 输入解码器,起始于 新播放位置 之前的最近的 随机访问点 (random access point)
  4. 在“Await a stable state”步骤处恢复 搜寻算法

3.15.4 SourceBuffer 监控

在播放期间定期运行以下步骤,以确保 activeSourceBuffers 中的所有 SourceBuffer 对象都有 确保不中断播放的足够数据 (enough data to ensure uninterrupted playback)。对 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 就绪状态 [HTML] 逻辑,HTMLMediaElementreadyState 更改可能会触发 HTMLMediaElement 上的事件。

  2. 中止这些步骤。
如果 HTMLMediaElementbuffered 包含一个包含当前播放位置的 TimeRanges 并且有 确保不中断播放的足够数据
  1. HTMLMediaElementreadyState 属性设置为 HAVE_ENOUGH_DATA

    根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElementreadyState 更改可能会触发 HTMLMediaElement 上的事件。

  2. 如果播放之前因转换为 HAVE_CURRENT_DATA 而被暂停,则播放可能会在此处恢复。
  3. 中止这些步骤。
如果 HTMLMediaElementbuffered 包含一个包含当前播放位置以及当前播放位置之后一段时间的 TimeRanges,则运行以下步骤
  1. HTMLMediaElementreadyState 属性设置为 HAVE_FUTURE_DATA

    根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElementreadyState 更改可能会触发 HTMLMediaElement 上的事件。

  2. 如果播放之前因转换为 HAVE_CURRENT_DATA 而被暂停,则播放可能会在此处恢复。
  3. 中止这些步骤。
如果 HTMLMediaElementbuffered 包含一个在当前播放位置结束且没有覆盖当前位置之后紧接时间的范围的 TimeRanges
  1. HTMLMediaElementreadyState 属性设置为 HAVE_CURRENT_DATA

    根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElementreadyState 更改可能会触发 HTMLMediaElement 上的事件。

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

3.15.5 对已选定/启用轨道状态的更改

在播放期间,如果 selected 视频轨道、enabled 音频轨道或文本轨道 mode 发生变化,则需要更新 activeSourceBuffers。当这些更改中的一个或多个发生时,需要遵循以下步骤。此外,当 MediaSource 是在 DedicatedWorkerGlobalScope 中构造时,对先前由内部 create track mirror 消息的隐式处理程序创建的轨道的 Window 镜像所发生的每次更改,必须通过发布到 [[port to worker]] 的内部 update track state 消息(其隐式处理程序进行更改并运行以下步骤)同样应用到相应的 DedicatedWorkerGlobalScope 轨道。同样,对 DedicatedWorkerGlobalScope 轨道发生的每次更改,必须通过发布到 [[port to main]] 的内部 update track state 消息(其隐式处理程序对镜像进行更改)同样应用到相应的轨道 Window 镜像。

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

3.15.6 时长变更 (Duration change)

duration 需要变更为新时长时,执行这些步骤。

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

    不允许会导致截断当前已缓冲媒体的时长缩减。当需要截断时,请在更新 duration 之前,使用 remove() 来减小缓冲范围。

  3. 最高结束时间sourceBuffers 中所有 SourceBuffer 对象中,所有 轨道缓冲区轨道缓冲区范围结束时间中的最大值。
  4. 如果新时长小于最高结束时间,则

    这种情况的发生是因为 编码帧移除算法会保留在移除范围开始之前开始的编码帧。

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

3.15.7 流结束 (End of stream)

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

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

      这允许时长准确反映所添加媒体片段的结尾。例如,如果时长明确设置为 10 秒,但在调用 endOfStream() 之前仅添加了 0 到 5 秒的媒体片段,则时长将更新为 5 秒。

    2. 通知媒体元素它现在拥有所有媒体数据。
    如果 error 设置为 "network"
    使用 必要时镜像算法在 Window 中执行以下步骤
    如果 HTMLMediaElementreadyState 属性等于 HAVE_NOTHING
    运行 资源获取算法媒体数据处理步骤列表 中的 如果由于网络错误而根本无法获取媒体数据,导致用户代理放弃尝试获取资源 步骤。
    如果 HTMLMediaElementreadyState 属性大于 HAVE_NOTHING
    运行 资源获取算法媒体数据处理步骤列表中,“如果连接在接收到部分媒体数据后中断,导致用户代理放弃获取资源的尝试”的步骤。
    如果 error 设置为 "decode"
    使用 必要时镜像算法在 Window 中执行以下步骤
    如果 HTMLMediaElementreadyState 属性等于 HAVE_NOTHING
    运行 资源获取算法媒体数据处理步骤列表中,“如果媒体数据可以获取,但经检查发现格式不受支持,或完全无法渲染”的步骤。
    如果 HTMLMediaElementreadyState 属性大于 HAVE_NOTHING
    运行 资源获取算法媒体数据处理步骤列表中,“媒体数据已损坏”的步骤。

3.15.8 必要时镜像 (Mirror if necessary)

此算法用于从附加在相同 WindowDedicatedWorkerGlobalScope 上的 MediaSource,在 Window 上执行步骤,通常用于更新所附加的 HTMLMediaElement 的状态。此算法接收一个 steps 参数,列出了要在 Window 上运行的步骤。

如果 MediaSource 是在 DedicatedWorkerGlobalScope 中构造的
[[port to main]] 发送一个内部的 mirror on window 消息,其在 Window 中的隐式处理程序将运行 steps。在不等待该处理程序接收消息的情况下将控制权返回给调用者。
镜像消息机制的目的是确保
  1. steps 作为其自身的任务在 Window 上异步运行,而不是在其他 Window 任务执行过程中中断运行,并且
  2. 运行 steps 时不会阻塞此算法在 DedicatedWorkerGlobalScope 上的同步执行和返回。
否则
运行 steps

4. MediaSourceHandle 接口

MediaSourceHandle 接口表示 MediaSource 对象的代理,它可用于将 DedicatedWorkerGlobalScope 中的 MediaSource,通过使用 srcObject(如附加到媒体元素算法中所述),附加到 WindowHTMLMediaElement 上。

这种独特的对象对于将跨上下文的 MediaSource 附加到媒体元素是必要的,因为 MediaSource 对象本身是事件目标,不可传输。

每个 MediaSourceHandle 对象都有一个 [[has ever been assigned as srcobject]] 内部槽,用于存储一个 boolean 值。它在 MediaSourceHandle 对象创建时初始化为 false,在扩展的 HTMLMediaElementsrcObject 设置器中(如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 出现非预期的多个接收者。因此,建议实现不要解析传输后的 MediaSourceHandle 的哪个潜在克隆仍然有效,直到且仅当底层 MediaSource 对象的任何句柄在媒体元素的资源选择算法的异步部分中使用时。这类似于通过 MediaSource 对象 URL 进行附加的现有行为,该 URL 可以轻松克隆,但此类 URL 最多仅对一个附加操作(在其所有潜在的众多克隆中)有效。

实现 必须 支持每个 MediaSourceHandle 底层的 MediaSource 对象,通过 srcObject 进行最多一次的附件(加载),无论由于 Transferable 的不同实现导致 MediaSourceHandle 潜在的克隆如何。

参见附加到媒体元素,了解如何在媒体元素资源选择算法的异步部分中强制执行此操作。

MediaSourceHandle 仅在 WindowDedicatedWorkerGlobalScope 上下文中公开,无法在不同的 代理集群 之间成功传输 [ECMASCRIPT]。MediaSourceHandle 对象的传输仅能在同一个 代理集群 内成功。

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

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;
  readonly  attribute AudioTrackList audioTracks;
  readonly  attribute VideoTrackList videoTracks;
  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);
};
Issue 280: MSE-in-Workers: {Audio,Video,Text}Track{,List} IDL in HTML need additional DedicatedWorker in Exposed mse-in-workers
[HTML] AudioTrackList, VideoTrackListTextTrackList 需要 Window+DedicatedWorker 公开。

5.1 属性

mode,类型为 AppendMode

控制如何处理 媒体片段 序列。此属性最初由 addSourceBuffer() 在对象创建后设置,并可通过 changeType() 或设置此属性来更新。

获取时,返回初始值或最后成功设置的值。

设置时,执行以下步骤:

  1. 如果此对象已从 父级媒体源sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并终止这些步骤。
  2. 如果 updating 属性等于 true,则抛出 InvalidStateError 异常并终止这些步骤。
  3. 新模式 等于正在分配给此属性的新值。
  4. 如果 [[generate timestamps flag]] 等于 true 且 新模式 等于 "segments",则抛出 TypeError 异常并终止这些步骤。
  5. 如果 父级媒体源readyState 属性处于 "ended" 状态,则运行以下步骤

    1. 父级媒体源readyState 属性设置为 "open"
    2. 排队一个任务,以在 父级媒体源触发一个名为 sourceopen 的事件。
  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 属性中移除,则抛出 InvalidStateError 异常并终止这些步骤。
  2. 最高结束时间 为此 SourceBuffer 对象管理的所有 轨道缓冲区轨道缓冲区范围结束时间中的最大值。
  3. 交集范围 等于一个包含从 0 到最高结束时间的单一范围的 TimeRanges 对象。
  4. 对于此 SourceBuffer 管理的每个音频和视频 轨道缓冲区,执行以下步骤

    文本 轨道缓冲区 包含在上面计算最高结束时间的过程中,但此处排除了缓冲范围计算。它们不一定是连续的,且其中任何不连续性也不应在其他媒体轨道在同一时间范围内连续时触发播放停顿。

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

控制应用于后续媒体片段中的时间戳的偏移量,这些媒体片段会被添加(append)到此 SourceBuffer 中。timestampOffset 最初设置为 0,表示没有应用偏移量。

获取时,返回初始值或最后成功设置的值。

设置时,执行以下步骤:

  1. 新时间戳偏移量 等于正在分配给此属性的新值。
  2. 如果此对象已从 父级媒体源sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并终止这些步骤。
  3. 如果 updating 属性等于 true,则抛出 InvalidStateError 异常并终止这些步骤。
  4. 如果 父级媒体源readyState 属性处于 "ended" 状态,则运行以下步骤

    1. 父级媒体源readyState 属性设置为 "open"
    2. 排队一个任务,以在 父级媒体源触发一个名为 sourceopen 的事件。
  5. 如果 [[append state]] 等于 PARSING_MEDIA_SEGMENT,则抛出 InvalidStateError 并终止这些步骤。
  6. 如果 mode 属性等于 "sequence",则将 [[group start timestamp]] 设置为新时间戳偏移量
  7. 将该属性更新为新时间戳偏移量
audioTracks,类型为 AudioTrackList,只读
由此对象创建的 AudioTrack 对象列表。
videoTracks,类型为 VideoTrackList,只读
由此对象创建的 VideoTrack 对象列表。
textTracks,类型为 TextTrackList,只读
由此对象创建的 TextTrack 对象列表。
appendWindowStart,类型为 double

附加窗口开始处的 呈现时间戳。此属性最初设置为 呈现开始时间

获取时,返回初始值或最后成功设置的值。

设置时,执行以下步骤:

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

附加窗口结束处的 呈现时间戳。此属性最初设置为正无穷大 (Infinity)。

获取时,返回初始值或最后成功设置的值。

设置时,执行以下步骤:

  1. 如果此对象已从 父级媒体源sourceBuffers 属性中移除,则抛出 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. 运行 准备添加 (prepare append) 算法。
  2. 数据 添加到 [[input buffer]] 的末尾。
  3. updating 属性设置为 true。
  4. 排队一个任务,以在此 SourceBuffer 对象上触发一个名为 updatestart 的事件。
  5. 异步运行 缓冲区添加 (buffer append) 算法。
abort (中止)

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

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

  1. 如果此对象已从 父级媒体源sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并终止这些步骤。
  2. 如果 父级媒体源readyState 属性不处于 "open" 状态,则抛出 InvalidStateError 异常并终止这些步骤。
  3. 如果 范围移除 (range removal) 算法正在运行,则抛出 InvalidStateError 异常并终止这些步骤。
  4. 如果 updating 属性等于 true,则运行以下步骤
    1. 如果 缓冲区添加算法正在运行,则中止该算法。
    2. updating 属性设置为 false。
    3. 排队一个任务,以在此 SourceBuffer 对象上触发一个名为 abort 的事件。
    4. 排队一个任务,以在此 SourceBuffer 对象上触发一个名为 updateend 的事件。
  5. 运行 重置解析器状态 (reset parser state) 算法。
  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]] 更新为与 type 相关联的字节流格式注册表 [MSE-REGISTRY] 条目中,“生成时间戳标志”列中的值。
  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 小于等于 startend 等于 NaN,则抛出 TypeError 异常并终止这些步骤。
  6. 如果 父级媒体源readyState 属性处于 "ended" 状态,则运行以下步骤

    1. 父级媒体源readyState 属性设置为 "open"
    2. 排队一个任务,以在 父级媒体源触发一个名为 sourceopen 的事件。
  7. 运行 范围移除算法,并以 startend 作为移除范围的开始和结束。

5.3 轨道缓冲区

一个 轨道缓冲区 存储单个轨道的 轨道描述编码帧。随着 初始化片段媒体片段 添加到 SourceBuffer 中,轨道缓冲区会随之更新。

每个 轨道缓冲区 都有一个 最后解码时间戳 (last decode timestamp) 变量,该变量存储在当前 编码帧组 中添加的最后一个 编码帧 的解码时间戳。该变量最初处于未设置状态,表示尚未添加任何 编码帧

每个 轨道缓冲区 都有一个 最后帧时长 (last frame duration) 变量,该变量存储在当前 编码帧组 中添加的最后一个 编码帧编码帧时长。该变量最初处于未设置状态,表示尚未添加任何 编码帧

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

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

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

对于轨道缓冲区范围,这些呈现时间范围基于 呈现时间戳、帧持续时间,以及在多路复用的 SourceBuffer 中跨轨道缓冲区的编码帧组起始时间。

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

这些编码帧组的起始时间与 编码帧处理 算法中提到的那些略有不同,因为它们是跨越不连续点后所有轨道缓冲区中最早的 呈现时间戳。无论 mode 如何,不连续点都可能出现在 编码帧处理 算法内,或由 编码帧移除 算法导致。确定 轨道缓冲区范围 不相交的阈值取决于具体的实现。例如,为了减少意外的播放停顿,实现 可以 通过合并被小于该 轨道缓冲区 中目前已缓冲的最大帧持续时间 2 倍的间隙分隔的相邻范围,来近似处理 编码帧处理 算法的不连续性检测逻辑。实现 还可以 使用多路复用 SourceBuffer 中跨 轨道缓冲区 的编码帧组起始时间作为范围起始时间,以进一步减少意外的播放停顿。

5.4 事件摘要

事件名称 Interface 调度时间:当……时
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 对象都有一个 [[追加状态]] 内部槽位,用于跟踪高层分段解析状态。它最初被设置为 WAITING_FOR_SEGMENT,并随着数据追加可以转换到以下状态。

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

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

每个 SourceBuffer 对象都有一个 [[缓冲区已满标记]] 内部槽位,用于跟踪 appendBuffer() 是否被允许接受更多字节。它在 SourceBuffer 对象创建时设置为 false,并随着数据的追加和移除进行更新。

每个 SourceBuffer 对象都有一个 [[组起始时间戳]] 内部槽位,用于跟踪 "sequence" 模式下新 编码帧组 的起始时间戳。当 SourceBuffer 对象创建时,它处于未设置状态,并在 mode 属性等于 "sequence" 且设置了 timestampOffset 属性时,或者执行 编码帧处理 算法时进行更新。

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

[[组结束时间戳]] 存储 SourceBuffer 中所有 轨道缓冲区 的最大 编码帧结束时间戳。因此,在追加时间戳跨轨道不对齐的多路复用分段时,设置 mode 属性时应格外小心。

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

当调用分段解析器循环算法时,执行以下步骤

  1. 循环顶部: 如果 [[输入缓冲区]] 为空,则跳转到下方的 需要更多数据 步骤。
  2. 如果 [[输入缓冲区]] 包含违反 SourceBuffer 字节流格式规范 的字节,则运行 追加错误 算法并中止此算法。
  3. [[输入缓冲区]] 的开头移除所有 字节流格式规范 指明 必须 忽略的字节。
  4. 如果 [[追加状态]] 等于 WAITING_FOR_SEGMENT,则执行以下步骤

    1. 如果 [[输入缓冲区]] 的开头指示 初始化分段 的开始,则将 [[追加状态]] 设置为 PARSING_INIT_SEGMENT
    2. 如果 [[输入缓冲区]] 的开头指示 媒体分段 的开始,则将 [[追加状态]] 设置为 PARSING_MEDIA_SEGMENT
    3. 跳转到上述 循环顶部 步骤。
  5. 如果 [[追加状态]] 等于 PARSING_INIT_SEGMENT,则执行以下步骤

    1. 如果 [[输入缓冲区]] 尚不包含完整的 初始化分段,则跳转到下方的 需要更多数据 步骤。
    2. 运行 初始化分段已接收 算法。
    3. [[输入缓冲区]] 的开头移除 初始化分段 字节。
    4. [[追加状态]] 设置为 WAITING_FOR_SEGMENT
    5. 跳转到上述 循环顶部 步骤。
  6. 如果 [[追加状态]] 等于 PARSING_MEDIA_SEGMENT,则执行以下步骤

    1. 如果 [[首次初始化分段已接收标记]] 为 false,或者 [[changeType 挂起的初始化分段标记]] 为 true,则运行 追加错误 算法并中止此算法。
    2. 如果 [[输入缓冲区]] 包含一个或多个完整的 编码帧,则运行 编码帧处理 算法。

      运行编码帧处理算法的频率取决于实现。当输入缓冲区包含完整的媒体分段时,可以调用编码帧处理算法,或者当完整的编码帧被添加到输入缓冲区时,也可以多次调用它。

    3. 如果此 SourceBuffer 已满且无法接受更多媒体数据,则将 [[缓冲区已满标记]] 设置为 true。
    4. 如果 [[输入缓冲区]] 不包含完整的 媒体分段,则跳转到下方的 需要更多数据 步骤。
    5. [[输入缓冲区]] 的开头移除 媒体分段 字节。
    6. [[追加状态]] 设置为 WAITING_FOR_SEGMENT
    7. 跳转到上述 循环顶部 步骤。
  7. 需要更多数据: 将控制权交还给调用算法。

5.5.2 重置解析器状态

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

  1. 如果 [[追加状态]] 等于 PARSING_MEDIA_SEGMENT[[输入缓冲区]] 包含一些完整的 编码帧,则运行 编码帧处理 算法,直到所有这些完整的 编码帧 都已处理完毕。
  2. 取消所有 轨道缓冲区 上的 最后解码时间戳
  3. 取消所有 轨道缓冲区 上的 最后一帧持续时间
  4. 取消所有 轨道缓冲区 上的 最高结束时间戳
  5. 将所有 轨道缓冲区 上的 需要随机访问点标记 设置为 true。
  6. 如果 mode 属性等于 "sequence",则将 [[组起始时间戳]] 设置为 [[组结束时间戳]]
  7. 移除 [[输入缓冲区]] 中的所有字节。
  8. [[追加状态]] 设置为 WAITING_FOR_SEGMENT

5.5.3 追加错误

当追加期间发生错误时,将调用此算法。

  1. 运行 重置解析器状态 算法。
  2. updating 属性设置为 false。
  3. 排队一个任务,在此 SourceBuffer 对象上 触发一个事件,命名为 error
  4. 排队一个任务,在此 SourceBuffer 对象上 触发一个事件,命名为 updateend
  5. 运行 流结束 算法,并将 error 参数设置为 "decode"。

5.5.4 准备追加

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

  1. 如果 SourceBuffer 已从 父级媒体源sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并中止这些步骤。
  2. 如果 updating 属性等于 true,则抛出 InvalidStateError 异常并中止这些步骤。
  3. 最近元素错误 确定如下
    如果 MediaSource 是在 Window 中构造的
    如果 HTMLMediaElementerror 属性不为 null,则令 最近元素错误 为 true。如果该属性为 null,则令 最近元素错误 为 false。
    否则
    最近元素错误Window 情况下的步骤结果,但在 WindowHTMLMediaElement 上运行,并在其 error 属性发生任何变化时,通过使用 [[port to worker]] 隐式消息进行通信。如果尚未收到此类消息,则令 最近元素错误 为 false。
  4. 如果 最近元素错误 为 true,则抛出 InvalidStateError 异常并中止这些步骤。
  5. 如果 父级媒体源readyState 属性处于 "ended" 状态,则执行以下步骤

    1. 父级媒体源readyState 属性设置为 "open"
    2. 排队一个任务,在 父级媒体源触发一个事件,命名为 sourceopen
  6. 运行 编码帧驱逐 算法。
  7. 如果 [[缓冲区已满标记]] 等于 true,则抛出 QuotaExceededError 异常并中止这些步骤。

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

5.5.5 缓冲区追加

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

  1. 运行 分段解析器循环 算法。
  2. 如果上一步中的 分段解析器循环 算法被中止,则中止此算法。
  3. updating 属性设置为 false。
  4. 排队一个任务,在此 SourceBuffer 对象上 触发一个事件,命名为 update
  5. 排队一个任务,在此 SourceBuffer 对象上 触发一个事件,命名为 updateend

5.5.6 范围移除

当调用者需要发起一个 JavaScript 可见的范围移除操作,且该操作会阻塞其他 SourceBuffer 更新时,请遵循以下步骤

  1. start 等于移除范围的起始 呈现时间戳,单位为秒,从 呈现开始时间 测量。
  2. end 等于移除范围的结束 呈现时间戳,单位为秒,从 呈现开始时间 测量。
  3. updating 属性设置为 true。
  4. 排队一个任务,在此 SourceBuffer 对象上 触发一个事件,命名为 updatestart
  5. 将控制权交还给调用者,并异步运行其余步骤。
  6. 运行 编码帧移除 算法,以 startend 作为移除范围的起始和结束。
  7. updating 属性设置为 false。
  8. 排队一个任务,在此 SourceBuffer 对象上 触发一个事件,命名为 update
  9. 排队一个任务,在此 SourceBuffer 对象上 触发一个事件,命名为 updateend

5.5.7 初始化分段已接收

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

每个 SourceBuffer 对象都有一个 [[首次初始化分段已接收标记]] 内部槽位,用于跟踪第一个 初始化分段 是否已被追加并由此算法接收。当 SourceBuffer 创建时,此标记被设为 false,并由下述算法更新。

每个 SourceBuffer 对象都有一个 [[changeType 挂起的初始化分段标记]] 内部槽位,用于跟踪自最近一次 changeType() 调用以来,是否需要 初始化分段。此标记在 SourceBuffer 创建时设为 false,由 changeType() 设置为 true,并由下述算法重置为 false。

  1. 如果 duration 属性当前等于 NaN,则更新它。
    如果初始化分段包含时长
    运行 时长变更 算法,将 新时长 设置为初始化分段中的时长。
    否则
    运行 时长变更 算法,将 新时长 设置为正无穷大。
  2. 如果 初始化分段 不包含任何音频、视频或文本轨道,则运行 追加错误 算法并中止这些步骤。
  3. 如果 [[首次初始化分段已接收标记]] 为 true,则执行以下步骤
    1. 验证以下属性。如果任何检查失败,则运行 追加错误 算法并中止这些步骤。
    2. 音频、视频和文本轨道的数量与第一个 初始化分段 中的内容一致。
    3. 如果存在单一类型的多个轨道(例如,2 个音频轨道),则 轨道 ID 与第一个 初始化分段 中的 ID 匹配。
    4. 用户代理支持每个轨道的编解码器。

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

    5. 将此 初始化分段 中的相应 轨道描述 添加到每个 轨道缓冲区
    6. 将所有轨道缓冲区上的 需要随机访问点标记 设置为 true。
  4. 活动轨道标记 等于 false。
  5. 如果 [[首次初始化分段已接收标记]] 为 false,则执行以下步骤

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

      用户代理 可以 在此处将原本支持的编解码器视为“不支持”,如果这些编解码器未指定在传递给 (a) 此 SourceBuffer 对象上最近一次成功的 changeType()type 参数中,或者 (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. 音频字节流轨道 ID 为当前正在处理的轨道的 轨道 ID
      2. 音频语言初始化分段 中为该轨道指定的语言的 BCP 47 语言标签,如果没有语言信息,则为空字符串。
      3. 如果 音频语言 等于 'und' BCP 47 值,则将空字符串赋值给 音频语言
      4. 音频标签初始化分段 中为该轨道指定的标签,如果没有标签信息,则为空字符串。
      5. 音频类型初始化分段 中为该轨道指定的类型字符串序列,如果没有提供类型信息,则为包含单个空字符串元素的序列。
      6. 对于 音频类型 中的每个值,运行以下步骤
      7. 当前音频类型 等于此循环迭代中来自 音频类型 的值。
      8. 新音频轨道 为一个新的 AudioTrack 对象。
      9. 生成一个唯一 ID 并将其赋值给 新音频轨道 上的 id 属性。
      10. 音频语言 赋值给 新音频轨道 上的 language 属性。
      11. 音频标签 赋值给 新音频轨道 上的 label 属性。
      12. 当前音频类型 赋值给 新音频轨道 上的 kind 属性。
      13. 如果此 SourceBuffer 对象 audioTrackslength 等于 0,则运行以下步骤

        1. 新音频轨道 上的 enabled 属性设置为 true。
        2. 活动轨道标记 设置为 true。
      14. 新音频轨道 添加到此 SourceBuffer 对象上的 audioTracks 属性。

        这应该触发 AudioTrackList [HTML] 逻辑来 排队一个任务,以触发名为 addtrack 的事件,使用 TrackEvent,并将 track 属性初始化为 新音频轨道,于此 SourceBuffer 对象上的 audioTracks 属性所引用的 AudioTrackList 对象上。

      15. 如果 父级媒体源 是在 DedicatedWorkerGlobalScope 中构造的
        [[port to main]] 发送一条内部的 create track mirror 消息,其在 Window 中的隐式处理程序运行以下步骤
      16. 镜像音频轨道 为一个新的 AudioTrack 对象。
      17. 将为 新音频轨道 确定的相同属性值赋值给 镜像音频轨道
      18. 镜像音频轨道 添加到 HTMLMediaElement 上的 audioTracks 属性。
      否则
      新音频轨道 添加到 HTMLMediaElement 上的 audioTracks 属性。

      这应该触发 AudioTrackList [HTML] 逻辑来 排队一个任务,以触发名为 addtrack 的事件,使用 TrackEvent,并将 track 属性初始化为 镜像音频轨道新音频轨道,于 HTMLMediaElement 上 audioTracks 属性所引用的 AudioTrackList 对象上。

  6. 创建一个新的 轨道缓冲区 来为该轨道存储 编码帧
  7. 将此轨道的 轨道描述 添加到 轨道缓冲区
  • 对于 初始化分段 中的每个视频轨道,运行以下步骤

    1. 视频字节流轨道 ID 为当前正在处理的轨道的 轨道 ID
    2. 视频语言初始化分段 中为该轨道指定的语言的 BCP 47 语言标签,如果没有语言信息,则为空字符串。
    3. 如果 视频语言 等于 'und' BCP 47 值,则将空字符串赋值给 视频语言
    4. 视频标签初始化分段 中为该轨道指定的标签,如果没有标签信息,则为空字符串。
    5. 视频类型初始化分段 中为该轨道指定的类型字符串序列,如果没有提供类型信息,则为包含单个空字符串元素的序列。
    6. 对于 视频类型 中的每个值,运行以下步骤
    7. 当前视频类型 等于此循环迭代中来自 视频类型 的值。
    8. 新视频轨道 为一个新的 VideoTrack 对象。
    9. 生成一个唯一 ID 并将其赋值给 新视频轨道 上的 id 属性。
    10. 视频语言 赋值给 新视频轨道 上的 language 属性。
    11. 视频标签 赋值给 新视频轨道 上的 label 属性。
    12. 当前视频类型 赋值给 新视频轨道 上的 kind 属性。
    13. 如果此 SourceBuffer 对象 videoTrackslength 等于 0,则运行以下步骤

      1. 新视频轨道 上的 selected 属性设置为 true。
      2. 活动轨道标记 设置为 true。
    14. 新视频轨道 添加到此 SourceBuffer 对象上的 videoTracks 属性。

      这应该触发 VideoTrackList [HTML] 逻辑来 排队一个任务,以触发名为 addtrack 的事件,使用 TrackEvent,并将 track 属性初始化为 新视频轨道,于此 SourceBuffer 对象上的 videoTracks 属性所引用的 VideoTrackList 对象上。

    15. 如果 父级媒体源 是在 DedicatedWorkerGlobalScope 中构造的
      [[port to main]] 发送一条内部的 create track mirror 消息,其在 Window 中的隐式处理程序运行以下步骤
    16. 镜像视频轨道 为一个新的 VideoTrack 对象。
    17. 将为 新视频轨道 确定的相同属性值赋值给 镜像视频轨道
    18. 镜像视频轨道 添加到 HTMLMediaElement 上的 videoTracks 属性。
    否则
    新视频轨道 添加到 HTMLMediaElement 上的 videoTracks 属性。

    这应该触发 VideoTrackList [HTML] 逻辑来 排队一个任务,以触发名为 addtrack 的事件,使用 TrackEvent,并将 track 属性初始化为 镜像视频轨道新视频轨道,于 HTMLMediaElement 上 videoTracks 属性所引用的 VideoTrackList 对象上。

  • 创建一个新的 轨道缓冲区 来为该轨道存储 编码帧
  • 将此轨道的 轨道描述 添加到 轨道缓冲区
  • 对于 初始化分段 中的每个文本轨道,运行以下步骤

    1. 文本字节流轨道 ID 为当前正在处理的轨道的 轨道 ID
    2. 文本语言初始化分段 中为该轨道指定的语言的 BCP 47 语言标签,如果没有语言信息,则为空字符串。
    3. 如果 文本语言 等于 'und' BCP 47 值,则将空字符串赋值给 文本语言
    4. 文本标签初始化分段 中为该轨道指定的标签,如果没有标签信息,则为空字符串。
    5. 文本类型初始化分段 中为该轨道指定的类型字符串序列,如果没有提供类型信息,则为包含单个空字符串元素的序列。
    6. 对于 text kinds 中的每个值,运行以下步骤:
      1. current text kind 等于本次循环迭代中来自 text kinds 的值。
      2. new text track 为一个新的 TextTrack 对象。
      3. 生成一个唯一 ID,并将其赋值给 new text trackid 属性。
      4. text language 赋值给 new text tracklanguage 属性。
      5. text label 赋值给 new text tracklabel 属性。
      6. current text kind 赋值给 new text trackkind 属性。
      7. 使用来自 初始化段 的适当信息填充 new text track 的剩余属性。
      8. 如果 new text trackmode 属性等于 "showing""hidden",则将 active track flag 设为 true。
      9. new text track 添加到此 SourceBuffer 对象的 textTracks 属性中。

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

      10. 如果 父媒体源 构建于 DedicatedWorkerGlobalScope
        [[port to main]] 发送一个内部的 create track mirror 消息,其在 Window 中的隐式处理器将运行以下步骤:
        1. mirrored text track 为一个新的 TextTrack 对象。
        2. mirrored text track 分配与为 new text track 所确定的相同的属性值。
        3. mirrored text track 添加到 HTMLMediaElement 的 textTracks 属性中。
        否则
        new text track 添加到 HTMLMediaElement 的 textTracks 属性中。

        这应触发 TextTrackList [HTML] 逻辑以 排队一个任务,使用 TrackEvent 触发一个事件,该事件名为 addtrack,并将 track 属性初始化为 mirrored text tracknew text track,目标对象为 HTMLMediaElement 上 textTracks 属性所引用的 TextTrackList 对象。

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

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

  • 如果 父媒体源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 小于 最后解码时间戳
        OR
        如果已设置 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,则运行以下步骤:
      11. 如果该编码帧不是 随机访问点,则丢弃该编码帧并跳转至循环开头以开始处理下一个编码帧。
      12. track buffer 上的 需要随机访问点标志 设为 false。
    2. spliced audio frame 为用于保存音频拼接信息的未设置变量。
    3. spliced timed text frame 为用于保存定时文本拼接信息的未设置变量。
    4. 如果 track buffer最后解码时间戳 未设置,且 presentation timestamp 落入 track buffer 中某个 编码帧显示区间 内,则运行以下步骤:
    5. overlapped frametrack buffer 中符合上述条件的 编码帧
    6. 如果 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
  • 移除 track buffer 中的现有编码帧:
    如果未设置 track buffer最高结束时间戳
    track buffer 中移除所有 编码帧,这些帧的 显示时间戳 大于或等于 presentation timestamp 且小于 frame end timestamp
    如果设置了 track buffer最高结束时间戳,且其小于或等于 presentation timestamp
    track buffer 中移除所有 编码帧,这些帧的 显示时间戳 大于或等于 最高结束时间戳 且小于 frame end timestamp
  • 通过从 track buffer 中移除前两个步骤中移除的那些帧与这些移除帧之后下一个 随机访问点 之间的所有 编码帧,来消除对前两个步骤中移除的 编码帧 的所有可能的解码依赖。

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

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

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

  • 如果 frame end timestamp 大于 [[group end timestamp]],则将 [[group end timestamp]] 设为等于 frame end timestamp
  • 如果 [[generate timestamps flag]] 等于 true,则将 timestampOffset 设为等于 frame end timestamp
  • 如果 HTMLMediaElementreadyState 属性为 HAVE_METADATA,且新的 编码帧 导致 HTMLMediaElementbuffered 具有一个针对当前播放位置的 TimeRanges,则将 HTMLMediaElementreadyState 属性设为 HAVE_CURRENT_DATA

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

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

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

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

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

  • 如果 媒体段 包含超出当前 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 的起始时间戳的所有媒体数据。
      4. 对于每个被移除的帧,如果该帧具有等于其轨道 最后解码时间戳解码时间戳,则运行以下步骤:

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

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

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

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

      发生此转换是因为当前位置的媒体数据已被移除。在追加当前播放位置的媒体或 3.15.5 选定/启用轨道状态的变化 之前,播放无法继续。

  • 如果 [[buffer full flag]] 等于 true 且此对象准备好接收更多字节,则将 [[buffer full flag]] 设为 false。
  • 5.5.10 编码帧驱逐

    当追加新数据时,运行此算法以释放此 SourceBuffer 中的空间。

    1. new data 等于即将追加到此 SourceBuffer 的数据。
      Issue 289: 编辑建议?编码帧驱逐算法需要说明“缓冲区满标志”可能会根据 |new data| 立即更新

      此处需要识别一个步骤:如果实现预测处理 new data 以及 [[input buffer]] 中任何现有字节会超过 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. 如果用户代理不支持交叉淡入淡出,则运行以下步骤:
    9. track buffer 中移除 overlapped frame
    10. track buffer 添加一个具有以下属性的静音帧:
    11. 显示时间戳 设置为 overlapped frame显示时间戳
    12. 解码时间戳 设置为 overlapped frame解码时间戳
    13. 编码帧持续时间 设置为 presentation timestampoverlapped frame 显示时间戳 之间的差值。

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

    14. 返回调用者而不提供拼接帧。

      这旨在允许将 new coded frame 添加到 track buffer,就像 overlapped frame 从一开始就没有在 track buffer 中一样。

  • frame end timestamp 等于 presentation timestampframe duration 之和。
  • splice end timestamp 等于 presentation timestamp 与 5 毫秒拼接持续时间之和。
  • fade out coded frames 等于 overlapped frame 以及 track buffer 中任何 显示时间戳 大于 presentation timestamp 且小于 splice end timestamp 的额外帧。
  • track buffer 中移除 fade out coded frames 中包含的所有帧。
  • 返回一个具有以下属性的拼接帧:
  • 显示时间戳 设置为 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 加上 5 毫秒。
    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. presentation timestampsplice timestamp 之间的样本从 fade out samples 复制到 output samples 中。
    15. 对于 splice timestampsplice end timestamp 之间的每个样本,计算 fade out samples 中的一个样本与 fade in samples 中对应样本的和,并将结果存储在 output samples 中。
    16. splice end timestampend timestamp 之间的样本从 fade in samples 复制到 output samples 中。
    17. 渲染 output samples

    以下是该算法的图形表示。Audio splice diagram

    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 事件摘要

    事件名称 Interface 调度时间:当……时
    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 事件摘要

    事件名称 Interface 调度时间:当……时
    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 为一个标志位。如果 buffered 属性包含的 TimeRanges 覆盖了当前播放位置,且包含 确保不间断播放所需的足够受管数据,并 能够以高效的方式检索和缓冲数据,则该标志为真。
      如果 can play uninterrupted and efficiently 的值不等于 streaming,则在 媒体元素排入一个元素任务,该任务执行以下步骤:
      1. 对象的 streaming 属性设置为 can play uninterrupted and efficiently 的值。
      2. 如果 can play uninterrupted and efficiently 为假,则在 ManagedMediaSource触发一个名为 startstreaming 的事件。
      3. 否则,在 ManagedMediaSource触发一个名为 endstreaming 的事件。

    7.3.2 内存清理

    1. 对于 对象 sourceBuffers 中的每一个 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 事件摘要

    事件名称 Interface 调度时间:当……时
    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 字典,其 addedRanges 初始化为 addedremovedRanges 初始化为 removed
    6. 排入一个任务,使用 BufferedChangeEvent 接口并以 eventInitDict 初始化,在 buffer触发一个名为 bufferedchange 的事件。

    9.3.2 内存清理

    1. 如果 对象不在其 ManagedMediaSource 父级的 activeSourceBuffers 中:
      1. 运行 编码帧移除 算法,将开始时间设置为 0,结束时间设置为正无穷大,并中止这些步骤。
    2. removal ranges 为一组可从演示中回收的呈现时间范围,以确保从 currentTime 开始的不间断播放,直到此类演示能够再次被检索为止。

      实现可以使用不同的策略来选择 removal ranges,因此 Web 应用程序不应依赖于特定的行为。Web 应用程序应监听 bufferedchange 事件,以观察缓冲数据的部分是否已被回收。

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

    10. HTMLMediaElement 扩展

    本节规定了当 MediaSource 附加到元素时,现有的 HTMLMediaElementseekablebuffered 属性 必须 返回什么,以及当 HTMLMediaElementsrcObject 属性被设置为 MediaSourceHandle 对象时,它 必须 执行的操作。

    HTMLMediaElementseekable 属性根据以下步骤返回一个新的静态 归一化 TimeRanges 对象

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

      此情况旨在处理某些实现,这些实现可能不再维护在已被 terminate() 或用户代理执行 终止工作线程(例如作为 close() 执行的最终结果)的 DedicatedWorkerGlobalScope 中构建的 MediaSource 内关于已缓冲或可寻道媒体的任何先前信息。

      问题 277: MSE-in-Workers:考虑(最终)在 MediaSource 工作线程终止时将附加元素转换为错误状态/媒体元素应该做什么? mse-in-workers

      在附加的工作线程 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]] 最近的值,通过处理 MediaSourceduration[[live seekable range]] 每次更改时发送至其 [[port to main]] 的隐式消息来进行更新。
    3. 如果 recent duration 等于 NaN:
      返回一个空的 TimeRanges 对象。
      如果 recent duration 等于正无穷大:
      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() 或用户代理执行 终止工作线程(例如作为 close() 执行的最终结果)的 DedicatedWorkerGlobalScope 中构建的 MediaSource 内关于已缓冲或可寻道媒体的任何先前信息。

      问题 277: MSE-in-Workers:考虑(最终)在 MediaSource 工作线程终止时将附加元素转换为错误状态/媒体元素应该做什么? mse-in-workers

      在附加的工作线程 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 等于一个包含从 0 到 highest end time 单一范围的 TimeRanges 对象。
        4. 对于 activeSourceBuffers 中的每个 SourceBuffer 对象,运行以下步骤:
          1. source ranges 等于当前 SourceBufferbuffered 属性返回的范围。
          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,但运行时使用 MediaSource 及其 SourceBuffer 对象在其 DedicatedWorkerGlobalScope 中,并通过使用 [[port to main]] 隐式消息进行通信,这些消息在每次 activeSourceBuffersreadyState 或任何缓冲状态(该状态会改变 activeSourceBuffers 中每个 buffered 属性的任何值)更新时触发。

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

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

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

    这可以防止再次转移该 MediaSourceHandle 对象,从而在尝试这样做时实现清晰的同步异常。

    议题

    MediaSourceHandle 需要添加到 HTMLMediaElement 的 MediaProvider IDL 类型定义及相关涉及媒体提供者对象的文本中。

    11. AudioTrack 扩展

    本节指定了对 [HTML] AudioTrack 定义的扩展。

    WebIDL[Exposed=(Window,DedicatedWorker)]
    partial interface AudioTrack {
      readonly attribute SourceBuffer? sourceBuffer;
    };
    Issue 280: MSE-in-Workers: {Audio,Video,Text}Track{,List} IDL in HTML need additional DedicatedWorker in Exposed mse-in-workers
    [HTML] AudioTrack 需要 Window+DedicatedWorker 暴露。

    属性

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

    获取时,运行以下步骤:

    如果此轨道是由与此轨道在相同 领域 (realm) 上创建的 SourceBuffer 所创建,并且该 SourceBuffer 尚未从其 父媒体源sourceBuffers 属性中移除:
    返回创建此轨道的 SourceBuffer
    否则
    返回 null。
    例如,如果一个 DedicatedWorkerGlobalScope SourceBuffer 通知其内部的 create track mirror 处理器在 Window 中创建此轨道,则该轨道的 Window 副本对此属性将返回 null。

    12. VideoTrack 扩展

    本节指定了对 [HTML] VideoTrack 定义的扩展。

    WebIDL[Exposed=(Window,DedicatedWorker)]
    partial interface VideoTrack {
      readonly attribute SourceBuffer? sourceBuffer;
    };
    Issue 280: MSE-in-Workers: {Audio,Video,Text}Track{,List} IDL in HTML need additional DedicatedWorker in Exposed mse-in-workers
    [HTML] VideoTrack 需要 Window+DedicatedWorker 暴露。

    属性

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

    获取时,运行以下步骤:

    如果此轨道是由与此轨道在相同 领域 (realm) 上创建的 SourceBuffer 所创建,并且该 SourceBuffer 尚未从其 父媒体源sourceBuffers 属性中移除:
    返回创建此轨道的 SourceBuffer
    否则
    返回 null。
    例如,如果一个 DedicatedWorkerGlobalScope SourceBuffer 通知其内部的 create track mirror 处理器在 Window 中创建此轨道,则该轨道的 Window 副本对此属性将返回 null。

    13. TextTrack 扩展

    本节指定了对 [HTML] TextTrack 定义的扩展。

    WebIDL[Exposed=(Window,DedicatedWorker)]
    partial interface TextTrack {
      readonly attribute SourceBuffer? sourceBuffer;
    };
    Issue 280: MSE-in-Workers: {Audio,Video,Text}Track{,List} IDL in HTML need additional DedicatedWorker in Exposed mse-in-workers
    [HTML] TextTrack 需要 Window+DedicatedWorker 暴露。

    属性

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

    获取时,运行以下步骤:

    如果此轨道是由与此轨道在相同 领域 (realm) 上创建的 SourceBuffer 所创建,并且该 SourceBuffer 尚未从其 父媒体源sourceBuffers 属性中移除:
    返回创建此轨道的 SourceBuffer
    否则
    返回 null。
    例如,如果一个 DedicatedWorkerGlobalScope SourceBuffer 通知其内部的 create track mirror 处理器在 Window 中创建此轨道,则该轨道的 Window 副本对此属性将返回 null。

    14. 字节流格式

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

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

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

    本节提供了所有字节流格式规范的一般要求。

  • 未提供计算 媒体段 序列中每个样本的全局 呈现时间戳 所需的信息。

    例如,如果 I1 与 M1、M2、M3 相关联,则上述条件 必须 对所有组合 I1+M1、I1+M2、I1+M1+M2、I1+M2+M3 等成立。

  • 字节流规范 必须 至少定义约束,以确保上述要求成立。可以定义附加约束 可以,例如为了简化实现。

    15. 一致性

    除了标记为非规范性的章节外,本规范中的所有创作指南、图表、示例和注释均为非规范性内容。本规范中的其他所有内容均为规范性内容。

    本文件中的关键字 MAY(可以)、MUST(必须)、MUST NOT(不得)、SHOULD(应该)和 SHOULD NOT(不应)在且仅在全部大写时,按 BCP 14 [RFC2119] [RFC8174] 中的描述进行解释,如本处所示。

    16. 示例

    16.1 使用媒体源扩展

    <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) {
          // Error fetching the initialization segment. Signal end of stream with an error.
          mediaSource.endOfStream("network");
          return;
        }
    
        // Append the initialization segment.
        sourceBuffer.addEventListener("updateend", function firstAppendHandler() {
          sourceBuffer.removeEventListener("updateend", firstAppendHandler);
    
          // Append some initial media data.
          appendNextMediaSegment(mediaSource);
        });
    
        sourceBuffer.appendBuffer(initSegment);
      } catch (error) {
        // Handle errors that might occur during initialization segment fetching.
        console.error("Error fetching initialization segment:", error);
        mediaSource.endOfStream("network");
      }
    }
    
    async function appendNextMediaSegment(mediaSource) {
      if (
        mediaSource.readyState === "closed" ||
        mediaSource.sourceBuffers[0].updating
      )
        return;
    
      // If we have run out of stream data, then signal end of stream.
      if (!haveMoreMediaSegments()) {
        mediaSource.endOfStream();
        return;
      }
    
      try {
        const mediaSegment = await getNextMediaSegment();
    
        // NOTE: If mediaSource.readyState == "ended", this appendBuffer() call will
        // cause mediaSource.readyState to transition to "open". The web application
        // should be prepared to handle multiple "sourceopen" events.
        mediaSource.sourceBuffers[0].appendBuffer(mediaSegment);
      }
      catch (error) {
        // Handle errors that might occur during media segment fetching.
        console.error("Error fetching media segment:", error);
        mediaSource.endOfStream("network");
      }
    }
    
    function onSeeking(mediaSource, video) {
      if (mediaSource.readyState === "open") {
        // Abort current segment append.
        mediaSource.sourceBuffers[0].abort();
      }
    
      // Notify the media segment loading code to start fetching data at the
      // new playback position.
      seekToMediaSegmentAt(video.currentTime);
    
      // Append a media segment from the new playback position.
      appendNextMediaSegment(mediaSource);
    }
    
    function onProgress(mediaSource, e) {
      appendNextMediaSegment(mediaSource);
    }
    
    // Example of async function for getting initialization segment
    async function getInitializationSegment() {
      // Implement fetching of the initialization segment
      // This is just a placeholder function
    }
    
    // Example function for checking if there are more media segments
    function haveMoreMediaSegments() {
      // Implement logic to determine if there are more media segments
      // This is just a placeholder function
    }
    
    // Example function for getting the next media segment
    async function getNextMediaSegment() {
      // Implement fetching of the next media segment
      // This is just a placeholder function
    }
    
    // Example function for seeking to a specific media segment
    function seekToMediaSegmentAt(currentTime) {
      // Implement seeking logic
      // This is just a placeholder function
    }
    </script>

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

    <script>
    async function setUpVideoStream() {
      // Specific video format and codec
      const mediaType = 'video/mp4; codecs="mp4a.40.2,avc1.4d4015"';
    
      // Check if the type of video format / codec is supported.
      if (!window.ManagedMediaSource?.isTypeSupported(mediaType)) {
        return; // Not supported, do something else.
      }
    
      // Set up video and its managed source.
      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);
    
      // Set up the event handlers
      sourceBuffer.onbufferedchange = (e) => {
        console.log("onbufferedchange event fired.");
        console.log(`Added Ranges: ${timeRangesToString(e.addedRanges)}`);
        console.log(`Removed Ranges: ${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 () => {
        // Stop fetching new segments here
      };
    }
    
    // Helper function...
    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 语言规范. Ecma International. URL: https://tc39.es/ecma262/multipage/
    [FILEAPI]
    文件 API。Marijn Kruisselbrink。W3C。2024 年 12 月 4 日。W3C 工作草案。网址:https://w3org.cn/TR/FileAPI/
    [HTML]
    HTML 标准. Anne van Kesteren; Domenic Denicola; Dominic Farolino; Ian Hickson; Philip Jägenstedt; Simon Pieters. WHATWG. 活标准. URL: https://html.whatwg.cn/multipage/
    [infra]
    Infra Standard. Anne van Kesteren; Domenic Denicola. WHATWG. Living Standard. URL: https://infra.spec.whatwg.org/
    [MSE-REGISTRY]
    Media Source Extensions™ 字节流格式注册表。Matthew Wolenetz; Jerry Smith; Aaron Colwell. W3C. URL: https://w3org.cn/TR/mse-byte-stream-format-registry/
    [RFC2119]
    Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. 1997年3月. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
    [RFC8174]
    RFC 2119 关键词中大小写的歧义. B. Leiba. IETF. 2017年5月. 最佳当前实践. URL: https://www.rfc-editor.org/rfc/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. 2015年4月26日. 非官方草案. URL: https://dev.w3.org/html5/html-sourcing-inband-tracks/
    [MEDIA-PLAYBACK-QUALITY]
    媒体回放质量。Mounir Lamouri; Chris Cunningham. W3C. W3C 编辑草案. URL: https://w3c.github.io/media-playback-quality/
    [url]
    URL 标准. Anne van Kesteren. WHATWG. 活标准. URL: https://url.spec.whatwg.org/