版权声明 © 2025 万维网联盟 (World Wide Web Consortium)。适用 W3C® 责任限制、商标及宽松文档许可规则。
HTMLMediaElement [HTML],允许 JavaScript 生成用于播放的媒体流。允许 JavaScript 生成流,为自适应流媒体和直播流时移等多种用例提供了便利。本节描述了本文件发布时的状态。当前的 W3C 出版物列表和本技术报告的最新版本可以在 W3C 标准和草案索引中找到。
除了编辑更新之外,自 2016 年 11 月作为 W3C 推荐标准发布以来的实质性变更包括:
changeType() 方法,用于在编解码器或字节流之间进行切换MediaSource 对象createObjectURL() 对 URL 对象的扩展,此前它已被集成到文件 API (File API) 中 [FILEAPI]ManagedMediaSource、ManagedSourceBuffer 和 BufferedChangeEvent 接口,支持节能流媒体传输和用户代理主动进行的缓冲区媒体清理有关自上一版本以来的全部变更列表,请参阅 提交记录 (commits)。
工作组维护着一份 编辑尚未尝试解决的所有错误报告列表。
实现者应注意,本规范尚不稳定。不参与讨论的实现者可能会发现规范在他们不知情的情况下发生不兼容的改变。 有意在最终达到“候选推荐标准”阶段之前实施本规范的供应商,应跟踪 GitHub 仓库并参与讨论。
本文档由 媒体工作组 (Media Working Group) 作为工作草案发布,使用 推荐标准轨道 (Recommendation track)。
发布为工作草案并不意味着 W3C 及其成员的认可。
这是一份草案文件,可能随时被其他文件更新、替换或废弃。将其作为进展中的工作以外的引用是不恰当的。
本文档由在一个受 W3C 专利政策约束下运作的组织制作。W3C 维护着一份 与该组交付成果相关的公开专利披露列表;该页面还包含了披露专利的说明。任何拥有实际知识并认为其持有的专利包含 必要权利要求 (Essential Claim(s)) 的个人,必须根据 W3C 专利政策第 6 节披露相关信息。
本文件受 2025 年 8 月 18 日 W3C 流程文档约束。
本节是非规范性的。
本规范允许 JavaScript 为 <audio> 和 <video> 动态构建媒体流。它定义了一个 MediaSource 对象,该对象可以作为 HTMLMediaElement 的媒体数据源。MediaSource 对象具有一个或多个 SourceBuffer 对象。应用程序将数据段追加到 SourceBuffer 对象,并可根据系统性能和其他因素调整追加数据的质量。来自 SourceBuffer 对象的数据作为音频、视频和文本数据的轨道缓冲区进行管理,这些数据随后被解码和播放。与这些扩展一起使用的字节流规范可在字节流格式注册表 [MSE-REGISTRY] 中找到。
本规范的设计旨在实现以下目标:
本规范定义了:
提供 编码帧 的 轨道缓冲区,用于 enabled 的 audioTracks、selected 的 videoTracks,以及 "showing" 或 "hidden" 的 textTracks。所有这些轨道都关联到 SourceBuffer 对象在 activeSourceBuffers 列表中。
一种用于在追加时过滤 编码帧 的 表示时间戳范围。追加窗口表示一个具有单一开始时间和结束时间的连续时间范围。在此范围内的 表示时间戳的编码帧被允许追加到 SourceBuffer 中,而超出此范围的编码帧则被过滤掉。追加窗口的开始和结束时间分别由 appendWindowStart 和 appendWindowEnd 属性控制。
编码帧的持续时间。对于视频和文本,持续时间指示视频帧或文本 应当显示的时长。对于音频,持续时间表示编码帧内包含的所有采样的总和。例如,如果一个音频帧包含 441 个采样,采样率为 44100Hz,则帧持续时间为 10 毫秒。
一组相邻且具有单调递增 解码时间戳且中间没有任何间隙的 编码帧。编码帧处理算法和 abort() 调用检测到的不连续性会触发新的编码帧组的开始。
解码时间戳指示该帧需要被解码的最晚时间(假设该帧及任何依赖帧的瞬时解码和渲染,这等于 表示顺序 中依赖于该帧的最早帧的 表示时间戳)。如果帧可以按非 表示顺序 解码,则解码时间戳 必须存在于或可从字节流中导出。如果不是这种情况,用户代理 必须运行 追加错误算法。如果帧不能按非 表示顺序 解码且字节流中不存在解码时间戳,则解码时间戳等于 表示时间戳。
包含解码一系列 媒体段 所需的所有初始化信息的字节序列。这包括编解码器初始化数据、多路复用段的 轨道 ID 映射以及时间戳偏移(例如编辑列表)。
字节流格式注册表 [MSE-REGISTRY] 中的 字节流格式规范 包含格式特定的示例。
包含 媒体时间轴 一部分的分组且带时间戳的媒体数据的字节序列。媒体段始终与最近追加的 初始化段 相关联。
字节流格式注册表 [MSE-REGISTRY] 中的 字节流格式规范 包含格式特定的示例。
MediaSource 对象 URL 是由 createObjectURL() 创建的唯一 blob URL。它用于将 MediaSource 对象附加到 HTMLMediaElement。
这些 URL 与 blob URL 相同,只是该功能定义中引用 File 和 Blob 对象的任何内容,现特此扩展为也适用于 MediaSource 对象。
MediaSource 对象 URL 的 源 (origin) 是在调用 createObjectURL() 时 此对象 的 相关设置对象。
例如,MediaSource 对象 URL 的 源 (origin) 会影响媒体元素 被 canvas 使用 的方式。
SourceBuffer 对象的父媒体源是创建它的 MediaSource 对象。
表示开始时间是呈现中的最早时间点,并指定初始 播放位置 和 最早可能位置。使用本规范创建的所有呈现的表示开始时间均为 0。
为了确定 HTMLMediaElement 的 buffered 是否包含一个涵盖当前播放位置的 TimeRanges,实现 可以选择允许在 表示开始时间 之后和第一个 TimeRanges 之前的当前播放位置,如果该 TimeRanges 在 表示开始时间 后经过很短的时间(例如 1 秒)内开始,则可以播放该第一个 TimeRanges。此允许照顾了多路复用流通常不会在 表示开始时间 处精确开始所有轨道的现实。无论此项允许如何,实现 必须报告实际的缓冲范围。
编码帧 的表示区间是从其 表示时间戳 到 表示时间戳 加上 编码帧持续时间 的时间区间。例如,如果一个编码帧的表示时间戳为 10 秒,编码帧持续时间为 100 毫秒,则表示区间为 [10-10.1)。注意,区间的开始是包含的,但结尾是排他的。
对呈现中特定时间的引用。编码帧 中的表示时间戳指示帧应当被渲染的时间。
媒体段 中的一个位置,在该位置可以开始解码和连续播放,而无需依赖段中的任何先前数据。对于视频,这通常是 I 帧的位置。对于音频,大多数音频帧都可以视为随机访问点。由于视频轨道倾向于具有更稀疏的随机访问点分布,这些点的位置通常被视为多路复用流的随机访问点。
描述 SourceBuffer 实例所接受的字节流格式的特定 字节流格式规范。SourceBuffer 对象的 字节流格式规范 最初是根据创建该对象的 addSourceBuffer() 调用中传入的 type 选择的,并且可以通过该对象上的 changeType() 调用进行更新。
SourceBuffer 配置
分布在单个 MediaSource 实例拥有的一个或多个 SourceBuffer 对象中的特定轨道集。
实现 必须支持至少 1 个 MediaSource 对象,并具备以下配置:
MediaSource 对象 必须支持上述每种配置,但它们仅被要求一次支持一种配置。同时支持多种配置或额外配置属于实现质量问题。
一种字节流格式特定的结构,为单个轨道提供 轨道 ID、编解码器配置和其他元数据。单个 初始化段 内的每个轨道描述都有一个唯一的 轨道 ID。如果 轨道 ID 在 初始化段 内不唯一,则用户代理 必须运行 追加错误算法。
轨道 ID 是一种字节流格式特定的标识符,将字节流的部分标记为特定轨道的一部分。轨道描述 中的轨道 ID 标识 媒体段 的哪些部分属于该轨道。
MediaSource 接口代表 HTMLMediaElement 的媒体数据源。它跟踪该源的 readyState,以及可用于向呈现中添加媒体数据的 SourceBuffer 对象列表。MediaSource 对象由 Web 应用程序创建,然后附加到 HTMLMediaElement。应用程序使用 sourceBuffers 中的 SourceBuffer 对象向此源添加媒体数据。HTMLMediaElement 在播放期间需要时会从 MediaSource 对象获取此媒体数据。
每个 MediaSource 对象都有一个存储 标准化 TimeRanges 对象 的 [[实时可跳转范围]] 内部槽。它在创建 MediaSource 对象时被初始化为一个空的 TimeRanges 对象,由 setLiveSeekableRange() 和 clearLiveSeekableRange() 维护,并用于 10. HTMLMediaElement 扩展以修改 HTMLMediaElement 的 seekable 行为。
每个 MediaSource 对象都有一个存储 boolean 的 [[是否曾被附加]] 内部槽。它在创建 MediaSource 对象时被初始化为 false,并如 附加到媒体元素 算法中所述,在扩展的 HTMLMediaElement 的 资源获取算法 中被设置为 true。扩展的 资源获取算法 使用此内部槽有条件地拒绝附加使用 HTMLMediaElement 的 srcObject 属性上设置的 MediaSourceHandle 的 MediaSource。
WebIDLenum ReadyState {
"closed",
"open",
"ended",
};
closed
开启
MediaSource 的 sourceBuffers 中的 SourceBuffer 对象。ended
MediaSource 的 endOfStream()。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);
};
包含一个句柄,用于将专用 worker 中的 MediaSource 对象通过 srcObject 附加到 HTMLMediaElement。对于此 MediaSource 对象,句柄在多次访问此属性时保持为同一个对象,但对于每个 MediaSource 对象,它是唯一的。
本规范最终可能会启用主 Window 上下文中 MediaSource 对象对此属性的可视性。如果这样,需要规范上的谨慎处理,以防止潜在的向后不兼容变更,例如如果访问此属性时抛出异常可能发生的情况。
获取时,运行以下步骤:
MediaSource 对象创建句柄,则运行以下步骤:MediaSourceHandle 对象和关联资源的结果,并在内部链接到此 MediaSource。MediaSourceHandle 对象。包含与此 MediaSource 关联的 SourceBuffer 对象列表。当 MediaSource 的 readyState 等于 "closed" 时,此列表将为空。一旦 readyState 过渡到 "open",就可以使用 addSourceBuffer() 将 SourceBuffer 对象添加到此列表中。
包含 sourceBuffers 的子集,这些缓冲区提供了 selected 视频轨道、enabled 的音频轨道以及 "showing" 或 "hidden" 的文本轨道。
此列表中的 SourceBuffer 对象 必须以它们在 sourceBuffers 属性中出现的相同顺序出现;例如,如果只有 sourceBuffers[0] 和 sourceBuffers[3] 在 activeSourceBuffers 中,则 activeSourceBuffers[0] 必须等于 sourceBuffers[0],并且 activeSourceBuffers[1] 必须等于 sourceBuffers[3]。
3.15.5 选定/启用轨道状态的变更 章节描述了如何更新此属性。
指示 MediaSource 对象的当前状态。当创建 MediaSource 时,readyState 必须被设置为 "closed"。
允许 Web 应用程序设置呈现时长。当创建 MediaSource 对象时,时长最初设置为 NaN。
获取时,运行以下步骤:
readyState 属性为 "closed",则返回 NaN 并中止这些步骤。设置时,执行以下步骤:
TypeError 异常并中止这些步骤。readyState 属性不为 "open",则抛出 InvalidStateError 异常并中止这些步骤。sourceBuffers 中的任何 SourceBuffer 的 updating 属性等于 true,则抛出 InvalidStateError 异常并中止这些步骤。时长变更 算法将调整 新时长 更高,如果存在任何当前缓冲的、具有更高结束时间的编码帧。
appendBuffer() 和 endOfStream() 在特定情况下可以更新时长。
返回 true。
此属性支持主线程和专用 worker 对在专用 worker 中创建和使用 MediaSource 对象的支持情况进行特征检测,并缓解了对高延迟检测 polyfill 的需求(例如尝试从专用 worker 创建 MediaSource 对象,特别是如果不支持该功能时)。
将新的 SourceBuffer 添加到 sourceBuffers。
TypeError 异常并中止这些步骤。sourceBuffers 中其他 SourceBuffer 对象指定的类型不兼容的 MIME 类型,则抛出 NotSupportedError 异常并中止这些步骤。QuotaExceededError 异常并中止这些步骤。例如,如果媒体元素已达到 HAVE_METADATA readyState,用户代理 MAY 抛出 QuotaExceededError 异常。如果用户代理的媒体引擎不支持在播放期间添加更多轨道,则可能会发生这种情况。
readyState 属性不处于 "open" 状态,则抛出 InvalidStateError 异常并中止这些步骤。ManagedMediaSource,则令 buffer 为 ManagedSourceBuffer 的新实例,否则为 SourceBuffer 的新实例,并带有它们各自关联的资源。[[生成时间戳标志]] 设置为与 type 关联的 Media Source Extensions™ 字节流格式注册表 条目中“生成时间戳标志”列中的值。[[生成时间戳标志]] 为 true,则将 buffer 的 mode 设置为 "sequence"。否则,将 buffer 的 mode 设置为 "segments"。sourceBuffers 中。sourceBuffers 上 触发一个名为 addsourcebuffer 的事件。从 sourceBuffers 中移除 SourceBuffer。
sourceBuffers 中,则抛出 NotFoundError 异常并中止这些步骤。updating 属性等于 true,则运行以下步骤:
audioTracks 返回的 AudioTrackList 对象。AudioTrack 对象,运行以下步骤:AudioTrack 对象上的 sourceBuffer 属性设置为 null。AudioTrack 对象。这应当触发 AudioTrackList [HTML] 逻辑来 排队一个任务,在 SourceBuffer 音频轨道列表 上 触发一个名为 removetrack 的事件,使用 TrackEvent,其 track 属性初始化为该 AudioTrack 对象。如果 AudioTrack 对象上的 enabled 属性在此移除步骤开始时为 true,则这也应当触发 AudioTrackList [HTML] 逻辑来 排队一个任务,在 SourceBuffer 音频轨道列表 上 触发一个名为 change 的事件。
Window 中运行以下步骤,以从媒体元素中移除 AudioTrack 对象(或者如果 MediaSource 对象是在 DedicatedWorkerGlobalScope 中构建的,则改为其 Window 镜像)。audioTracks 属性返回的 AudioTrackList 对象。AudioTrack 对象。这应当触发 AudioTrackList [HTML] 逻辑来 排队一个任务,在 HTMLMediaElement 音频轨道列表 上 触发一个名为 removetrack 的事件,使用 TrackEvent,其 track 属性初始化为该 AudioTrack 对象。如果 AudioTrack 对象上的 enabled 属性在此移除步骤开始时为 true,则这也应当触发 AudioTrackList [HTML] 逻辑来 排队一个任务,在 HTMLMediaElement 音频轨道列表 上 触发一个名为 change 的事件。
videoTracks 返回的 VideoTrackList 对象。VideoTrack 对象,运行以下步骤:VideoTrack 对象上的 sourceBuffer 属性设置为 null。VideoTrack 对象。这应当触发 VideoTrackList [HTML] 逻辑来 排队一个任务,在 SourceBuffer 视频轨道列表 上 触发一个名为 removetrack 的事件,使用 TrackEvent,其 track 属性初始化为该 VideoTrack 对象。如果 VideoTrack 对象上的 selected 属性在此移除步骤开始时为 true,则这也应当触发 VideoTrackList [HTML] 逻辑来 排队一个任务,在 SourceBuffer 视频轨道列表 上 触发一个名为 change 的事件。
Window 中运行以下步骤,以从媒体元素中移除 VideoTrack 对象(或者如果 MediaSource 对象是在 DedicatedWorkerGlobalScope 中构建的,则改为其 Window 镜像)。videoTracks 属性返回的 VideoTrackList 对象。VideoTrack 对象。这应当触发 VideoTrackList [HTML] 逻辑来 排队一个任务,在 HTMLMediaElement 视频轨道列表 上 触发一个名为 removetrack 的事件,使用 TrackEvent,其 track 属性初始化为该 VideoTrack 对象。如果 VideoTrack 对象上的 selected 属性在此移除步骤开始时为 true,则这也应当触发 VideoTrackList [HTML] 逻辑来 排队一个任务,在 HTMLMediaElement 视频轨道列表 上 触发一个名为 change 的事件。
textTracks 返回的 TextTrackList 对象。TextTrack 对象,运行以下步骤:TextTrack 对象上的 sourceBuffer 属性设置为 null。TextTrack 对象。这应触发 TextTrackList [HTML] 逻辑,以 排队一个任务,在 SourceBuffer textTracks 列表 上 触发一个事件,该事件名为 removetrack,使用 TrackEvent,并将 track 属性初始化为 TextTrack 对象。如果在移除步骤开始时 TextTrack 对象上的 mode 属性为 "showing" 或 "hidden",则这还应触发 TextTrackList [HTML] 逻辑,以 排队一个任务,在 SourceBuffer textTracks 列表 上 触发一个事件,该事件名为 change。
Window 中运行以下步骤,以从媒体元素中移除 TextTrack 对象(或者,如果 MediaSource 对象是在 DedicatedWorkerGlobalScope 中构造的,则移除其 Window 镜像)。textTracks 属性返回的 TextTrackList 对象。TextTrack 对象。这应触发 TextTrackList [HTML] 逻辑,以 排队一个任务,在 HTMLMediaElement textTracks 列表 上 触发一个事件,该事件名为 removetrack,使用 TrackEvent,并将 track 属性初始化为 TextTrack 对象。如果在移除步骤开始时 TextTrack 对象上的 mode 属性为 "showing" 或 "hidden",则这还应触发 TextTrackList [HTML] 逻辑,以 排队一个任务,在 HTMLMediaElement textTracks 列表 上 触发一个事件,该事件名为 change。
activeSourceBuffers 中,则从 activeSourceBuffers 中移除 sourceBuffer,并 排队一个任务,在 SourceBufferList(由 activeSourceBuffers 返回)上 触发一个事件,该事件名为 removesourcebuffer。sourceBuffers 中移除 sourceBuffer,并 排队一个任务,在 SourceBufferList(由 sourceBuffers 返回)上 触发一个事件,该事件名为 removesourcebuffer。表示流的结束。
readyState 属性不在 "open" 状态,则抛出一个 InvalidStateError 异常并中止这些步骤。sourceBuffers 中的任何 SourceBuffer 上的 updating 属性等于 true,则抛出一个 InvalidStateError 异常并中止这些步骤。更新用于第 10. HTMLMediaElement 扩展 章节的 [[live seekable range]],以修改 HTMLMediaElement 的 seekable 行为。
调用此方法时,用户代理必须执行以下步骤
readyState 属性不是 "open",则抛出一个 InvalidStateError 异常并中止这些步骤。TypeError 异常并中止这些步骤。[[live seekable range]] 设置为一个新的 归一化 TimeRanges 对象,其中包含一个单一范围,其开始位置为 start,结束位置为 end。更新用于第 10. HTMLMediaElement 扩展 章节的 [[live seekable range]],以修改 HTMLMediaElement 的 seekable 行为。
调用此方法时,用户代理必须执行以下步骤
readyState 属性不是 "open",则抛出一个 InvalidStateError 异常并中止这些步骤。[[live seekable range]] 包含一个范围,则将 [[live seekable range]] 设置为一个新的空 TimeRanges 对象。检查 MediaSource 是否能够为指定的 MIME 类型创建 SourceBuffer 对象。
如果此方法返回 true,则仅表示 MediaSource 实现有能力为指定的 MIME 类型创建 SourceBuffer 对象。如果可用资源不足以支持添加新的 SourceBuffer,addSourceBuffer() 调用 仍应失败。
此方法返回 true 暗示 HTMLMediaElement 的 canPlayType() 将返回 "maybe" 或 "probably",因为 MediaSource 支持 HTMLMediaElement 已知无法播放的类型是没有意义的。
调用此方法时,用户代理必须执行以下步骤
| 事件名称 | Interface | 调度时间:当……时 |
|---|---|---|
| sourceopen |
Event
|
MediaSource 的 readyState 从 "closed" 转换为 "open",或从 "ended" 转换为 "open"。 |
| sourceended |
Event
|
MediaSource 的 readyState 从 "open" 转换为 "ended"。 |
| sourceclose |
Event
|
MediaSource 的 readyState 从 "open" 转换为 "closed",或从 "ended" 转换为 "closed"。 |
当 Window HTMLMediaElement 附加到 DedicatedWorkerGlobalScope MediaSource 时,每个上下文都有依赖于对方信息的算法。
HTMLMediaElement 仅向 Window 上下文公开,但本规范中定义的 MediaSource 及相关对象在 Window 和 DedicatedWorkerGlobalScope 上下文中均被公开。这允许应用程序在上述任一上下文中构造 MediaSource 对象,并使用 MediaSource 对象 URL 或 MediaSourceHandle(如 附加到媒体元素 算法中所述)将其附加到 Window 上下文中的 HTMLMediaElement 对象。 MediaSource 对象不是 Transferable 的;它仅在创建它的上下文中可见。
本节的其余部分描述了一个用于限制 Window 媒体元素与 DedicatedWorkerGlobalScope MediaSource 附件之间的信息延迟的模型。虽然该模型描述了使用消息传递进行通信,但实现 可以选择以可能更快的方式(例如使用共享内存和锁)进行通信。附加到 Window MediaSource 的附件无需跨上下文通信即可同步拥有信息。
在 DedicatedWorkerGlobalScope 中构造的 MediaSource 具有一个 [[port to main]] 内部槽位,该槽位在附加期间存储一个 MessagePort,并在分离时将其置空。 Window 的 [[port to main]] 始终为 null。
由本规范扩展并附加到 DedicatedWorkerGlobalScope MediaSource 的 HTMLMediaElement 类似地具有一个 [[port to worker]] 内部槽位,该槽位存储一个 MessagePort,以及一个 [[channel with worker]] 内部槽位,该槽位存储一个 MessageChannel,两者均在附加期间设置,并在分离时置空。除非附加到 DedicatedWorkerGlobalScope MediaSource,否则 [[port to worker]] 和 [[channel with worker]] 均为 null。
本规范中需要将信息从 Window HTMLMediaElement 通信到附加的 DedicatedWorkerGlobalScope MediaSource 的算法(反之亦然)将隐式使用这些内部端口向对应方发布消息,消息的隐式处理程序将运行算法中所述的步骤。
根据 MediaSource 对象是在 Window 还是在 DedicatedWorkerGlobalScope 中构造,将 MediaSource 附加到媒体元素有不同的机制。
将构造于 Window 中的 MediaSource 附加到媒体元素,可以通过将该 MediaSource 的 MediaSource 对象 URL 分配给媒体元素的 src 属性或媒体元素内 <source> 的 src 属性来完成。 MediaSource 对象 URL 是通过将 MediaSource 对象传递给 createObjectURL() 来创建的。
尽管实现 可能允许在 DedicatedWorkerGlobalScope 中为该 Worker 中构造的 MediaSource 创建 MediaSource 对象 URL,但尝试通过 src 属性或媒体元素内 <source> 的 src 属性,使用该 MediaSource 对象 URL 附加到媒体元素,必须在媒体元素的 资源获取算法(如下扩展)中失败。
将对象 URL 附加机制扩展到 Worker MediaSource 对象 URL 将进一步推广这种相比于使用 srcObject 而言不太受欢迎的习惯用法,并将不必要地增加用户代理的互操作性风险和实现复杂度。
DedicatedWorkerGlobalScope 中的 MediaSource 附加到媒体元素,只能通过使用 handle 从中获取句柄,将该 MediaSourceHandle 传输到 Window 上下文,并将其分配给媒体元素的 srcObject 属性来完成。为了使本规范与 HTMLMediaElement 资源加载和获取算法保持一致,底层的 DedicatedWorkerGlobalScope MediaSource 是此处提到的 MediaSource 对象,而 MediaSourceHandle 对象是媒体提供者对象。如果 资源获取算法 是使用 MediaSource 对象、MediaSourceHandle 对象或对象为 MediaSource 对象的 URL 记录调用的,则令 mode 为 local,跳过 资源获取算法 中的第一步(该步骤可能会将 mode 设置为 remote),并继续执行 资源获取算法。
资源获取算法 的第一步预期最终将与为对象是媒体提供者对象的 URL 记录选择 local 模式保持一致。其目的是,如果 HTMLMediaElement 的 src 属性或选定的子 source 的 src 属性在各自的 src 属性上次更改时,是一个匹配 MediaSource 对象 URL 的 blob: URL,则该 MediaSource 对象即被用作 资源获取算法 中 local 模式逻辑下的媒体提供者对象和当前媒体资源。这也意味着当附加了 MediaSource 对象时,包含对任何 preload 属性观察的 remote 模式逻辑将被跳过。即使在对 [HTML] 进行了最终更改之后,当当前媒体资源是 MediaSource 对象时,仍需要在 local 模式逻辑的开头执行以下步骤。
在 资源获取算法 的“否则 (mode 为 local)”部分的开头,执行下述附加步骤。
相对于触发媒体元素资源选择算法的操作而言,这些步骤是异步的。在允许调用资源选择算法的任务继续并达到稳定状态后,运行 资源获取算法。实现 可以延迟下述“Otherwise”子句中的步骤,直到 MediaSource 对象准备好使用为止。
MediaSource 对象、MediaSourceHandle 对象或对象为 MediaSource 对象的 URL 记录调用的,则DedicatedWorkerGlobalScope 中的 MediaSource,例如试图使用来自 DedicatedWorkerGlobalScope MediaSource 的 MediaSource 对象 URL 时发生的情况MediaSource 的 handle 从 DedicatedWorker 传输到 Window 上下文并将其分配给媒体元素的 srcObject 属性,是附加此类 MediaSource 的唯一方法。MediaSourceHandle,且其 [[Detached]] 内部槽位为 trueMediaSourceHandle,且其底层 MediaSource 的 [[has ever been attached]] 内部槽位为 trueMediaSourceHandle 多次加载底层的 MediaSource,即使该 MediaSource 是在 Window 上构造的,并且之前已使用 MediaSource 对象 URL 加载过。但这并不妨碍后续成功使用 Window MediaSource 的 MediaSource 对象 URL。readyState 未设置为 "closed"MediaSource 的 [[has ever been attached]] 内部槽位设置为 true。MediaSource 是在 DedicatedWorkerGlobalScope 中构造的,则设置 Worker 附件通信并打开 MediaSource[[channel with worker]] 设置为一个新的 MessageChannel。[[port to worker]] 设置为 [[channel with worker]] 的 port1 值。[[channel with worker]] 的 port2 作为值和 transferList 的唯一成员,令结果为 serialized port2。MediaSource 的 DedicatedWorkerGlobalScope 上,该任务将DedicatedWorkerGlobalScope 的 领域 (realm),并将 [[port to main]] 设置为传输后的 [[channel with worker]] 的 port2 值的反序列化克隆。readyState 属性设置为 "open"。MediaSource 上 触发一个事件,该事件名为 sourceopen。MediaSource 是在 Window 中构造的[[channel with worker]] 设置为 null。[[port to worker]] 设置为 null。[[port to main]] 设置为 null。readyState 属性设置为 "open"。MediaSource 上 触发一个事件,该事件名为 sourceopen。appendBuffer() 传入的数据。MediaSource 时,HTMLMediaElement 不通过 HTTP 获取媒体数据。在任何媒体元素将要转换为 NETWORK_EMPTY 并 排队一个任务 在媒体元素上 触发一个事件(名为 emptied)的情况下,均运行以下步骤。这些步骤 应在转换之前立即运行。
MediaSource 是在 DedicatedWorkerGlobalScope 中构造的[[port to worker]] 的内部 detach 消息通知 MediaSource。[[port to worker]] 设置为 null。[[channel with worker]] 设置为 null。detach 通知的隐式消息处理程序在 DedicatedWorkerGlobalScope MediaSource 中运行这些步骤的其余部分。MediaSource 是在 Window 中构造的Window MediaSource 上继续运行这些步骤的其余部分。[[port to main]] 设置为 null。readyState 属性设置为 "closed"。ManagedMediaSource,则将 streaming 属性设置为 false。duration 更新为 NaN。activeSourceBuffers 中移除所有 SourceBuffer 对象。activeSourceBuffers 上 触发一个事件,该事件名为 removesourcebuffer。sourceBuffers 中移除所有 SourceBuffer 对象。sourceBuffers 上 触发一个事件,该事件名为 removesourcebuffer。MediaSource 上 触发一个事件,该事件名为 sourceclose。今后,此算法旨在被外部调用,并在任何需要将附加的 MediaSource(如有)从媒体元素分离的情况下运行。除了或替代媒体元素转换为 NETWORK_EMPTY 的情况外,它 可以在 HTMLMediaElement [HTML] 操作(如 load())和 资源获取算法 失败时调用。资源获取算法失败是指那些中止资源获取算法或资源选择算法的失败,但“最终步骤” [HTML] 不被视为触发分离的失败。
作为 搜寻算法 (seek algorithm) 的“等待直到用户代理确定新播放位置的媒体数据是否可用,如果可用,则等待直到它解码出足够的数据来播放该位置”步骤的一部分,运行以下步骤
媒体元素在 activeSourceBuffers 中的每个 SourceBuffer 对象中查找包含 新播放位置 的 媒体段 (media segments)。 HTMLMediaElement 的 buffered 属性当前值中 TimeRanges 内的任何位置都已缓存了该位置所需的所有媒体段。
HTMLMediaElement 的 buffered 的任何 TimeRanges 中HTMLMediaElement 的 readyState 属性大于 HAVE_METADATA,则将 HTMLMediaElement 的 readyState 属性设置为 HAVE_METADATA。根据 HTMLMediaElement 就绪状态 (ready states) [HTML] 逻辑,HTMLMediaElement 的 readyState 更改可能会触发 HTMLMediaElement 上的事件。
appendBuffer() 调用导致 编码帧处理 (coded frame processing) 算法将 HTMLMediaElement 的 readyState 属性设置为大于 HAVE_METADATA 的值。Web 应用程序可以使用 buffered 和 HTMLMediaElement 的 buffered 来确定媒体元素需要什么来恢复播放。
如果 readyState 属性为 "ended",且 新播放位置 在 HTMLMediaElement 的 buffered 中的某个 TimeRanges 内,则搜寻操作必须在此处继续完成,即使一个或多个当前选定或启用的轨道缓冲区的最大范围结束时间戳小于 新播放位置。这种情况应该仅由于 buffered 中的逻辑而在 readyState 为 "ended" 时发生。
在播放期间定期运行以下步骤,以确保 activeSourceBuffers 中的所有 SourceBuffer 对象都有 确保不中断播放的足够数据 (enough data to ensure uninterrupted playback)。对 activeSourceBuffers 的更改也会导致这些步骤运行,因为它们会影响触发状态转换的条件。
拥有 确保不中断播放的足够数据 是一个特定于实现的条件,用户代理在该条件下确定它当前有足够的数据来播放演示文稿,而不会在有意义的时间段内停顿。该条件不断被评估,以确定何时将媒体元素切换进出 HAVE_ENOUGH_DATA 就绪状态。这些转换分别表示用户代理认为它已缓存足够的数据,或者需要更多数据。
实现 可以选择使用缓存字节数、缓存时间、追加速率或任何其他其认为合适的指标来确定何时拥有足够的数据。所使用的指标在播放期间 可能会发生变化,因此 Web 应用程序 应仅依赖 HTMLMediaElement 的 readyState 的值来确定是否需要更多数据。
当媒体元素需要更多数据时,用户代理 应在足够早的时间将其从 HAVE_ENOUGH_DATA 切换到 HAVE_FUTURE_DATA,以便 Web 应用程序能够做出响应而不会导致播放中断。例如,当当前播放位置在缓存数据结束前 500ms 时进行切换,给应用程序大约 500ms 的时间来在播放停顿前追加更多数据。
HTMLMediaElement 的 readyState 属性等于 HAVE_NOTHINGHTMLMediaElement 的 buffered 不包含针对当前播放位置的 TimeRangesHTMLMediaElement 的 readyState 属性设置为 HAVE_METADATA。根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElement 的 readyState 更改可能会触发 HTMLMediaElement 上的事件。
HTMLMediaElement 的 buffered 包含一个包含当前播放位置的 TimeRanges 并且有 确保不中断播放的足够数据HTMLMediaElement 的 readyState 属性设置为 HAVE_ENOUGH_DATA。根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElement 的 readyState 更改可能会触发 HTMLMediaElement 上的事件。
HAVE_CURRENT_DATA 而被暂停,则播放可能会在此处恢复。HTMLMediaElement 的 buffered 包含一个包含当前播放位置以及当前播放位置之后一段时间的 TimeRanges,则运行以下步骤HTMLMediaElement 的 readyState 属性设置为 HAVE_FUTURE_DATA。根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElement 的 readyState 更改可能会触发 HTMLMediaElement 上的事件。
HAVE_CURRENT_DATA 而被暂停,则播放可能会在此处恢复。HTMLMediaElement 的 buffered 包含一个在当前播放位置结束且没有覆盖当前位置之后紧接时间的范围的 TimeRangesHTMLMediaElement 的 readyState 属性设置为 HAVE_CURRENT_DATA。根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElement 的 readyState 更改可能会触发 HTMLMediaElement 上的事件。
在播放期间,如果 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 镜像。
SourceBuffer 未与任何其他已启用的轨道关联,则运行以下步骤activeSourceBuffers 中移除 SourceBuffer。activeSourceBuffers 上 触发一个事件,该事件名为 removesourcebufferSourceBuffer 尚未在 activeSourceBuffers 中,则运行以下步骤SourceBuffer 添加到 activeSourceBuffers。activeSourceBuffers 上 触发一个事件,该事件名为 addsourcebufferSourceBuffer 未与任何其他已启用或选定的轨道关联,则运行以下步骤activeSourceBuffers 中移除与音频轨道关联的 SourceBufferactiveSourceBuffers 上 触发一个事件,该事件名为 removesourcebufferSourceBuffer 尚未在 activeSourceBuffers 中,则运行以下步骤SourceBuffer 添加到 activeSourceBuffersactiveSourceBuffers 上 触发一个事件,该事件名为 addsourcebuffermode 变为 "disabled",且与此轨道关联的 SourceBuffer 未与任何其他已启用或选定的轨道关联,则运行以下步骤activeSourceBuffers 中移除与文本轨道关联的 SourceBufferactiveSourceBuffers 上触发一个名为 removesourcebuffer 的事件。mode 变为 "showing" 或 "hidden",且与该轨道关联的 SourceBuffer 尚未处于 activeSourceBuffers 中,则执行以下步骤SourceBuffer 添加到 activeSourceBuffers 中activeSourceBuffers 上触发一个名为 addsourcebuffer 的事件。当 duration 需要变更为新时长时,执行这些步骤。
duration 的当前值等于新时长,则返回。sourceBuffers 中任何 SourceBuffer 对象的所有已缓冲 编码帧的最高 呈现时间戳,则抛出 InvalidStateError 异常并终止这些步骤。
sourceBuffers 中所有 SourceBuffer 对象中,所有 轨道缓冲区的 轨道缓冲区范围结束时间中的最大值。这种情况的发生是因为 编码帧移除算法会保留在移除范围开始之前开始的编码帧。
duration 更新为新时长。Window 中执行以下步骤以更新媒体元素的时长duration 更新为新时长。当应用程序通过 endOfStream() 调用发出流结束信号,或者某个算法需要发出解码错误信号时,会调用此算法。此算法接收一个 error 参数,用于指示是否将发出错误信号。
readyState 属性值更改为 "ended"。MediaSource 上触发一个名为 sourceended 的事件。sourceBuffers 中所有 SourceBuffer 对象中,所有 轨道缓冲区的 轨道缓冲区范围结束时间中的最大值。这允许时长准确反映所添加媒体片段的结尾。例如,如果时长明确设置为 10 秒,但在调用 endOfStream() 之前仅添加了 0 到 5 秒的媒体片段,则时长将更新为 5 秒。
network"Window 中执行以下步骤HTMLMediaElement 的 readyState 属性等于 HAVE_NOTHINGHTMLMediaElement 的 readyState 属性大于 HAVE_NOTHINGdecode"Window 中执行以下步骤HTMLMediaElement 的 readyState 属性等于 HAVE_NOTHINGHTMLMediaElement 的 readyState 属性大于 HAVE_NOTHING此算法用于从附加在相同 Window 或 DedicatedWorkerGlobalScope 上的 MediaSource,在 Window 上执行步骤,通常用于更新所附加的 HTMLMediaElement 的状态。此算法接收一个 steps 参数,列出了要在 Window 上运行的步骤。
MediaSource 是在 DedicatedWorkerGlobalScope 中构造的[[port to main]] 发送一个内部的 mirror on window 消息,其在 Window 中的隐式处理程序将运行 steps。在不等待该处理程序接收消息的情况下将控制权返回给调用者。Window 上异步运行,而不是在其他 Window 任务执行过程中中断运行,并且DedicatedWorkerGlobalScope 上的同步执行和返回。MediaSourceHandle 接口表示 MediaSource 对象的代理,它可用于将 DedicatedWorkerGlobalScope 中的 MediaSource,通过使用 srcObject(如附加到媒体元素算法中所述),附加到 Window 的 HTMLMediaElement 上。
这种独特的对象对于将跨上下文的 MediaSource 附加到媒体元素是必要的,因为 MediaSource 对象本身是事件目标,不可传输。
每个 MediaSourceHandle 对象都有一个 [[has ever been assigned as srcobject]] 内部槽,用于存储一个 boolean 值。它在 MediaSourceHandle 对象创建时初始化为 false,在扩展的 HTMLMediaElement 的 srcObject 设置器中(如10. HTMLMediaElement 扩展章节所述)被设置为 true。如果为 true,则会阻止该 MediaSourceHandle 成功进行传输(如4.1 传输章节所述)。
MediaSourceHandle 对象是 Transferable 的,每个对象都有一个 [[Detached]] 内部槽,用于确保一旦句柄对象实例被传输,该实例就不能再次被传输。
WebIDL[Transferable, Exposed=(Window,DedicatedWorker)]
interface MediaSourceHandle {};
MediaSourceHandle 的 传输步骤和传输接收步骤要求实现维护一个隐式内部槽,引用底层的 MediaSource,以启用使用 srcObject 的附加到媒体元素操作,并随后设置附件的跨上下文通信模型。
实现者应注意,Transferable 所暗示的“移动”语义并不总是事实。例如,使用广播的 postMessage 的扩展或内部实现可能会导致传输后的 MediaSourceHandle 出现非预期的多个接收者。因此,建议实现不要解析传输后的 MediaSourceHandle 的哪个潜在克隆仍然有效,直到且仅当底层 MediaSource 对象的任何句柄在媒体元素的资源选择算法的异步部分中使用时。这类似于通过 MediaSource 对象 URL 进行附加的现有行为,该 URL 可以轻松克隆,但此类 URL 最多仅对一个附加操作(在其所有潜在的众多克隆中)有效。
实现 必须 支持每个 MediaSourceHandle 底层的 MediaSource 对象,通过 srcObject 进行最多一次的附件(加载),无论由于 Transferable 的不同实现导致 MediaSourceHandle 潜在的克隆如何。
参见附加到媒体元素,了解如何在媒体元素资源选择算法的异步部分中强制执行此操作。
MediaSourceHandle 仅在 Window 和 DedicatedWorkerGlobalScope 上下文中公开,无法在不同的 代理集群 之间成功传输 [ECMASCRIPT]。MediaSourceHandle 对象的传输仅能在同一个 代理集群 内成功。
例如,将 MediaSourceHandle 对象从 Window 或 DedicatedWorkerGlobalScope 传输到 SharedWorker 或 ServiceWorker 将不会成功。开发者应注意这种与 MediaSource 对象 URL 的区别,后者是可以通过多种方式通信的 DOMString。即便如此,使用 MediaSource 对象 URL 进行附加到媒体元素的操作,也只能对于在 Window 上下文中构造的 MediaSource 成功。另请参见 Web 应用程序 API 的 代理 和 代理集群 形式集成的定义 [HTML],其中定义了诸如 专用工作线程代理 等相关概念。
MediaSourceHandle 对象的 传输步骤 必须 包含以下步骤
MediaSourceHandle 的 [[has ever been assigned as srcobject]] 内部槽为 true,则 传输步骤 必须通过抛出 DataCloneError 异常而失败。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);
};
mode,类型为 AppendMode控制如何处理 媒体片段 序列。此属性最初由 addSourceBuffer() 在对象创建后设置,并可通过 changeType() 或设置此属性来更新。
获取时,返回初始值或最后成功设置的值。
设置时,执行以下步骤:
sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并终止这些步骤。updating 属性等于 true,则抛出 InvalidStateError 异常并终止这些步骤。[[generate timestamps flag]] 等于 true 且 新模式 等于 "segments",则抛出 TypeError 异常并终止这些步骤。如果 父级媒体源 的 readyState 属性处于 "ended" 状态,则运行以下步骤
readyState 属性设置为 "open"sourceopen 的事件。[[append state]] 等于 PARSING_MEDIA_SEGMENT,则抛出 InvalidStateError 并终止这些步骤。sequence",则将 [[group start timestamp]] 设置为 [[group end timestamp]]。updating,类型为 boolean,只读指示 appendBuffer() 或 remove() 操作的异步延续是否仍在处理中。对象创建时,此属性最初设置为 false。
buffered,类型为 TimeRanges,只读指示 SourceBuffer 中缓冲了哪些 TimeRanges。对象创建时,此属性最初设置为一个空的 TimeRanges 对象。
读取该属性时,必须 发生以下步骤
sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并终止这些步骤。SourceBuffer 对象管理的所有 轨道缓冲区的 轨道缓冲区范围结束时间中的最大值。TimeRanges 对象。SourceBuffer 管理的每个音频和视频 轨道缓冲区,执行以下步骤文本 轨道缓冲区 包含在上面计算最高结束时间的过程中,但此处排除了缓冲范围计算。它们不一定是连续的,且其中任何不连续性也不应在其他媒体轨道在同一时间范围内连续时触发播放停顿。
readyState 为 "ended",则将 轨道范围 中最后一个范围的结束时间设置为最高结束时间。timestampOffset,类型为 double控制应用于后续媒体片段中的时间戳的偏移量,这些媒体片段会被添加(append)到此 SourceBuffer 中。timestampOffset 最初设置为 0,表示没有应用偏移量。
获取时,返回初始值或最后成功设置的值。
设置时,执行以下步骤:
sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并终止这些步骤。updating 属性等于 true,则抛出 InvalidStateError 异常并终止这些步骤。如果 父级媒体源 的 readyState 属性处于 "ended" 状态,则运行以下步骤
readyState 属性设置为 "open"sourceopen 的事件。[[append state]] 等于 PARSING_MEDIA_SEGMENT,则抛出 InvalidStateError 并终止这些步骤。mode 属性等于 "sequence",则将 [[group start timestamp]] 设置为新时间戳偏移量。audioTracks,类型为 AudioTrackList,只读AudioTrack 对象列表。videoTracks,类型为 VideoTrackList,只读VideoTrack 对象列表。textTracks,类型为 TextTrackList,只读TextTrack 对象列表。appendWindowStart,类型为 double附加窗口开始处的 呈现时间戳。此属性最初设置为 呈现开始时间。
获取时,返回初始值或最后成功设置的值。
设置时,执行以下步骤:
sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并终止这些步骤。updating 属性等于 true,则抛出 InvalidStateError 异常并终止这些步骤。appendWindowEnd,则抛出 TypeError 异常并终止这些步骤。appendWindowEnd,类型为 unrestricted double附加窗口结束处的 呈现时间戳。此属性最初设置为正无穷大 (Infinity)。
获取时,返回初始值或最后成功设置的值。
设置时,执行以下步骤:
sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并终止这些步骤。updating 属性等于 true,则抛出 InvalidStateError 异常并终止这些步骤。TypeError 并终止这些步骤。appendWindowStart,则抛出 TypeError 异常并终止这些步骤。onupdatestart,类型为 EventHandlerupdatestart 事件的事件处理程序。
onupdate,类型为 EventHandlerupdate 事件的事件处理程序。
onupdateend,类型为 EventHandlerupdateend 事件的事件处理程序。
onerror,类型为 EventHandlererror 事件的事件处理程序。
onabort,类型为 EventHandlerabort 事件的事件处理程序。
appendBuffer
将 BufferSource [WEBIDL] 中的片段数据添加到 SourceBuffer。
调用此方法时,用户代理必须执行以下步骤
[[input buffer]] 的末尾。updating 属性设置为 true。SourceBuffer 对象上触发一个名为 updatestart 的事件。abort (中止)
中止当前片段并重置片段解析器。
调用此方法时,用户代理必须执行以下步骤
sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并终止这些步骤。readyState 属性不处于 "open" 状态,则抛出 InvalidStateError 异常并终止这些步骤。InvalidStateError 异常并终止这些步骤。updating 属性等于 true,则运行以下步骤updating 属性设置为 false。SourceBuffer 对象上触发一个名为 abort 的事件。SourceBuffer 对象上触发一个名为 updateend 的事件。appendWindowStart 设置为 呈现开始时间。appendWindowEnd 设置为正无穷大。changeType
更改与此对象关联的 MIME 类型。随后的 appendBuffer() 调用将期望新添加的字节符合新类型。
调用此方法时,用户代理必须执行以下步骤
TypeError 异常并中止这些步骤。sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并终止这些步骤。updating 属性等于 true,则抛出 InvalidStateError 异常并终止这些步骤。sourceBuffers 属性中 SourceBuffer 对象指定的(当前或之前)类型不兼容的 MIME 类型,则抛出 NotSupportedError 异常并终止这些步骤。如果 父级媒体源 的 readyState 属性处于 "ended" 状态,则运行以下步骤
readyState 属性设置为 "open"。sourceopen 的事件。SourceBuffer 对象上的 [[generate timestamps flag]] 更新为与 type 相关联的字节流格式注册表 [MSE-REGISTRY] 条目中,“生成时间戳标志”列中的值。[[generate timestamps flag]] 等于 trueSourceBuffer 对象上的 mode 属性设置为 "sequence",包括运行该属性设置的相关联步骤。SourceBuffer 对象上的 mode 属性的先前值,而不运行该属性设置的任何相关联步骤。SourceBuffer 对象上的 [[pending initialization segment for changeType flag]] 设置为 true。remove
移除特定时间范围内的媒体。移除范围的 start(以秒为单位,从 呈现开始时间 测量)。移除范围的 end(以秒为单位,从 呈现开始时间 测量)。
调用此方法时,用户代理必须执行以下步骤
sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并终止这些步骤。updating 属性等于 true,则抛出 InvalidStateError 异常并终止这些步骤。duration 等于 NaN,则抛出 TypeError 异常并终止这些步骤。duration,则抛出 TypeError 异常并终止这些步骤。TypeError 异常并终止这些步骤。如果 父级媒体源 的 readyState 属性处于 "ended" 状态,则运行以下步骤
readyState 属性设置为 "open"sourceopen 的事件。一个 轨道缓冲区 存储单个轨道的 轨道描述 和 编码帧。随着 初始化片段 和 媒体片段 添加到 SourceBuffer 中,轨道缓冲区会随之更新。
每个 轨道缓冲区 都有一个 最后解码时间戳 (last decode timestamp) 变量,该变量存储在当前 编码帧组 中添加的最后一个 编码帧 的解码时间戳。该变量最初处于未设置状态,表示尚未添加任何 编码帧。
每个 轨道缓冲区 都有一个 最后帧时长 (last frame duration) 变量,该变量存储在当前 编码帧组 中添加的最后一个 编码帧 的 编码帧时长。该变量最初处于未设置状态,表示尚未添加任何 编码帧。
每个 轨道缓冲区(track buffer) 都有一个 最高结束时间戳 变量,用于存储当前 编码帧组 中所有已追加到此轨道缓冲区的 编码帧 的最大 编码帧结束时间戳。该变量初始为未设置状态,以表明尚未追加任何 编码帧。
每个 轨道缓冲区 都有一个 需要随机访问点标记 变量,用于跟踪轨道缓冲区是否正在等待一个 随机访问点 编码帧。该变量初始设为 true,以表明在向 轨道缓冲区 添加任何内容之前,需要一个 随机访问点 编码帧。
每个 轨道缓冲区 都有一个 轨道缓冲区范围 变量,用于表示当前存储在轨道缓冲区中的 编码帧 所占用的呈现时间范围。
对于轨道缓冲区范围,这些呈现时间范围基于 呈现时间戳、帧持续时间,以及在多路复用的 SourceBuffer 中跨轨道缓冲区的编码帧组起始时间。
出于规范目的,此信息被视为存储在 归一化 TimeRanges 对象 中。交叉的 轨道缓冲区范围 用于报告 HTMLMediaElement 的 buffered 属性,因此 必须 支持 HTMLMediaElement 的 buffered 属性的每个范围内实现不间断播放。
| 事件名称 | Interface | 调度时间:当……时 |
|---|---|---|
| updatestart |
Event
|
SourceBuffer 的 updating 属性从 false 转换为 true。 |
| update |
Event
|
SourceBuffer 的追加或移除操作成功完成。SourceBuffer 的 updating 属性从 true 转换为 false。 |
| updateend |
Event
|
SourceBuffer 的追加或移除操作已结束。 |
| error |
Event
|
向 SourceBuffer 追加数据期间发生错误。updating 属性从 true 转换为 false。 |
| abort (中止) |
Event
|
SourceBuffer 的追加操作已被 abort() 调用终止。updating 属性从 true 转换为 false。 |
每个 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() 更新。
当调用分段解析器循环算法时,执行以下步骤
[[输入缓冲区]] 为空,则跳转到下方的 需要更多数据 步骤。[[输入缓冲区]] 包含违反 SourceBuffer 字节流格式规范 的字节,则运行 追加错误 算法并中止此算法。[[输入缓冲区]] 的开头移除所有 字节流格式规范 指明 必须 忽略的字节。如果 [[追加状态]] 等于 WAITING_FOR_SEGMENT,则执行以下步骤
[[输入缓冲区]] 的开头指示 初始化分段 的开始,则将 [[追加状态]] 设置为 PARSING_INIT_SEGMENT。[[输入缓冲区]] 的开头指示 媒体分段 的开始,则将 [[追加状态]] 设置为 PARSING_MEDIA_SEGMENT。如果 [[追加状态]] 等于 PARSING_INIT_SEGMENT,则执行以下步骤
如果 [[追加状态]] 等于 PARSING_MEDIA_SEGMENT,则执行以下步骤
[[首次初始化分段已接收标记]] 为 false,或者 [[changeType 挂起的初始化分段标记]] 为 true,则运行 追加错误 算法并中止此算法。[[输入缓冲区]] 包含一个或多个完整的 编码帧,则运行 编码帧处理 算法。运行编码帧处理算法的频率取决于实现。当输入缓冲区包含完整的媒体分段时,可以调用编码帧处理算法,或者当完整的编码帧被添加到输入缓冲区时,也可以多次调用它。
SourceBuffer 已满且无法接受更多媒体数据,则将 [[缓冲区已满标记]] 设置为 true。[[输入缓冲区]] 不包含完整的 媒体分段,则跳转到下方的 需要更多数据 步骤。[[输入缓冲区]] 的开头移除 媒体分段 字节。[[追加状态]] 设置为 WAITING_FOR_SEGMENT。当需要重置解析器状态时,执行以下步骤
[[追加状态]] 等于 PARSING_MEDIA_SEGMENT 且 [[输入缓冲区]] 包含一些完整的 编码帧,则运行 编码帧处理 算法,直到所有这些完整的 编码帧 都已处理完毕。mode 属性等于 "sequence",则将 [[组起始时间戳]] 设置为 [[组结束时间戳]]。[[输入缓冲区]] 中的所有字节。[[追加状态]] 设置为 WAITING_FOR_SEGMENT。当追加期间发生错误时,将调用此算法。
当追加操作开始时,运行以下步骤来验证并准备 SourceBuffer。
SourceBuffer 已从 父级媒体源 的 sourceBuffers 属性中移除,则抛出 InvalidStateError 异常并中止这些步骤。updating 属性等于 true,则抛出 InvalidStateError 异常并中止这些步骤。MediaSource 是在 Window 中构造的HTMLMediaElement 的 error 属性不为 null,则令 最近元素错误 为 true。如果该属性为 null,则令 最近元素错误 为 false。Window 情况下的步骤结果,但在 Window 的 HTMLMediaElement 上运行,并在其 error 属性发生任何变化时,通过使用 [[port to worker]] 隐式消息进行通信。如果尚未收到此类消息,则令 最近元素错误 为 false。InvalidStateError 异常并中止这些步骤。如果 父级媒体源 的 readyState 属性处于 "ended" 状态,则执行以下步骤
readyState 属性设置为 "open"sourceopen。如果 [[缓冲区已满标记]] 等于 true,则抛出 QuotaExceededError 异常并中止这些步骤。
这是实现无法驱逐足够数据以容纳此次追加,或者追加内容过大的信号。Web 应用程序 应当 使用 remove() 显式释放空间和/或减小追加大小。
当调用 appendBuffer() 时,运行以下步骤来处理追加的数据。
当调用者需要发起一个 JavaScript 可见的范围移除操作,且该操作会阻塞其他 SourceBuffer 更新时,请遵循以下步骤
updating 属性设置为 true。SourceBuffer 对象上 触发一个事件,命名为 updatestart。updating 属性设置为 false。SourceBuffer 对象上 触发一个事件,命名为 update。SourceBuffer 对象上 触发一个事件,命名为 updateend。当 分段解析器循环 成功解析出完整的 初始化分段 时,运行以下步骤
每个 SourceBuffer 对象都有一个 [[首次初始化分段已接收标记]] 内部槽位,用于跟踪第一个 初始化分段 是否已被追加并由此算法接收。当 SourceBuffer 创建时,此标记被设为 false,并由下述算法更新。
每个 SourceBuffer 对象都有一个 [[changeType 挂起的初始化分段标记]] 内部槽位,用于跟踪自最近一次 changeType() 调用以来,是否需要 初始化分段。此标记在 SourceBuffer 创建时设为 false,由 changeType() 设置为 true,并由下述算法重置为 false。
duration 属性当前等于 NaN,则更新它。
[[首次初始化分段已接收标记]] 为 true,则执行以下步骤用户代理 可以 在此处将原本支持的编解码器视为“不支持”,如果这些编解码器未指定在传递给 (a) 此 SourceBuffer 对象上最近一次成功的 changeType() 的 type 参数中,或者 (b) 如果此对象上尚未发生成功的 changeType(),则为创建此 SourceBuffer 对象的 addSourceBuffer()。例如,如果最近一次成功的 changeType() 是使用 'video/webm' 或 'video/webm; codecs="vp8"' 调用的,而初始化分段中出现了包含 vp9 的视频轨道,则用户代理 可以 使用此步骤触发解码错误,即使上述其他两个属性的检查通过。鼓励实现在此类情况下仅当编解码器确实不受支持或上述其他两个属性检查失败时才触发错误。鼓励 Web 作者使用带有精确编解码器参数的 changeType()、addSourceBuffer() 和 isTypeSupported(),以便更主动地检测用户代理支持。如果 SourceBuffer 对象的字节流格式发生变化,则需要 changeType()。
如果 [[首次初始化分段已接收标记]] 为 false,则执行以下步骤
用户代理 可以 在此处将原本支持的编解码器视为“不支持”,如果这些编解码器未指定在传递给 (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()。
对于 初始化分段 中的每个音频轨道,运行以下步骤
AudioTrack 对象。id 属性。language 属性。label 属性。kind 属性。如果此 SourceBuffer 对象 audioTracks 的 length 等于 0,则运行以下步骤
enabled 属性设置为 true。SourceBuffer 对象上的 audioTracks 属性。这应该触发 AudioTrackList [HTML] 逻辑来 排队一个任务,以触发名为 addtrack 的事件,使用 TrackEvent,并将 track 属性初始化为 新音频轨道,于此 SourceBuffer 对象上的 audioTracks 属性所引用的 AudioTrackList 对象上。
DedicatedWorkerGlobalScope 中构造的[[port to main]] 发送一条内部的 create track mirror 消息,其在 Window 中的隐式处理程序运行以下步骤AudioTrack 对象。audioTracks 属性。audioTracks 属性。这应该触发 AudioTrackList [HTML] 逻辑来 排队一个任务,以触发名为 addtrack 的事件,使用 TrackEvent,并将 track 属性初始化为 镜像音频轨道 或 新音频轨道,于 HTMLMediaElement 上 audioTracks 属性所引用的 AudioTrackList 对象上。
对于 初始化分段 中的每个视频轨道,运行以下步骤
VideoTrack 对象。id 属性。language 属性。label 属性。kind 属性。如果此 SourceBuffer 对象 videoTracks 的 length 等于 0,则运行以下步骤
selected 属性设置为 true。SourceBuffer 对象上的 videoTracks 属性。这应该触发 VideoTrackList [HTML] 逻辑来 排队一个任务,以触发名为 addtrack 的事件,使用 TrackEvent,并将 track 属性初始化为 新视频轨道,于此 SourceBuffer 对象上的 videoTracks 属性所引用的 VideoTrackList 对象上。
DedicatedWorkerGlobalScope 中构造的[[port to main]] 发送一条内部的 create track mirror 消息,其在 Window 中的隐式处理程序运行以下步骤VideoTrack 对象。videoTracks 属性。videoTracks 属性。这应该触发 VideoTrackList [HTML] 逻辑来 排队一个任务,以触发名为 addtrack 的事件,使用 TrackEvent,并将 track 属性初始化为 镜像视频轨道 或 新视频轨道,于 HTMLMediaElement 上 videoTracks 属性所引用的 VideoTrackList 对象上。
对于 初始化分段 中的每个文本轨道,运行以下步骤
TextTrack 对象。id 属性。language 属性。label 属性。kind 属性。mode 属性等于 "showing" 或 "hidden",则将 active track flag 设为 true。SourceBuffer 对象的 textTracks 属性中。这应触发 TextTrackList [HTML] 逻辑以 排队一个任务,使用 TrackEvent 触发一个事件,该事件名为 addtrack,并将 track 属性初始化为 new text track,目标对象为该 SourceBuffer 对象上 textTracks 属性所引用的 TextTrackList 对象。
DedicatedWorkerGlobalScope 中[[port to main]] 发送一个内部的 create track mirror 消息,其在 Window 中的隐式处理器将运行以下步骤:TextTrack 对象。textTracks 属性中。textTracks 属性中。这应触发 TextTrackList [HTML] 逻辑以 排队一个任务,使用 TrackEvent 触发一个事件,该事件名为 addtrack,并将 track 属性初始化为 mirrored text track 或 new text track,目标对象为 HTMLMediaElement 上 textTracks 属性所引用的 TextTrackList 对象。
SourceBuffer 添加到 activeSourceBuffers 中。activeSourceBuffers 上 触发一个事件,该事件名为 addsourcebuffer。[[first initialization segment received flag]] 设为 true。[[pending initialization segment for changeType flag]] 设为 false。Window 中运行以下步骤:HTMLMediaElement 的 readyState 属性大于 HAVE_CURRENT_DATA,则将 HTMLMediaElement 的 readyState 属性设为 HAVE_METADATA。根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElement 的 readyState 变化可能会在 HTMLMediaElement 上触发事件。
sourceBuffers 中的每个对象都具有 [[first initialization segment received flag]] 等于 true,则使用 父媒体源 的 必要时镜像 算法在 Window 中运行以下步骤:HTMLMediaElement 的 readyState 属性为 HAVE_NOTHING,则将 HTMLMediaElement 的 readyState 属性设为 HAVE_METADATA。根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElement 的 readyState 变化可能会在 HTMLMediaElement 上触发事件。如果发生了从 HAVE_NOTHING 到 HAVE_METADATA 的转换,它应触发 HTMLMediaElement 逻辑以 排队一个任务,在媒体元素上 触发一个事件,名为 loadedmetadata。
当完整的 编码帧 被 段解析器循环 解析完成后,运行以下步骤:
[[generate timestamps flag]] 等于 true:对于定时文本帧,可能需要特殊处理来确定显示和解码时间戳,因为此信息在底层格式中可能并未明确呈现,或者可能取决于帧的顺序。一些元数据轨道(如 MPEG2-TS PSI 数据)可能仅具有隐含的时间戳。针对这些情况的格式特定规则 应该 包含在 字节流格式规范 或单独的扩展规范中。
实现无需在内部以双精度浮点表示形式存储时间戳。在此处使用该表示形式是因为它是 HTML 规范中时间戳的表示形式。这样做的目的是在不向算法增加不必要复杂性的情况下明确行为,以应对增加 timestampOffset 可能会导致底层字节流格式所使用的时间戳表示出现溢出的情况。实现可以使用它们想要的任何内部时间戳表示,但添加 timestampOffset 的行为 应该 与使用双精度浮点表示时发生的情况类似。
mode 等于 "sequence" 且 [[group start timestamp]] 已设置,则运行以下步骤:timestampOffset 设为等于 [[group start timestamp]] 减去 presentation timestamp。[[group end timestamp]] 设为等于 [[group start timestamp]]。[[group start timestamp]]。如果 timestampOffset 不为 0,则运行以下步骤:
timestampOffset 添加到 presentation timestamp 中。timestampOffset 添加到 decode timestamp 中。appendWindowStart,则将 需要随机访问点标志 设为 true,丢弃该编码帧,并跳转至循环开头以开始处理下一个编码帧。一些实现 可能 选择收集一些 presentation timestamp 小于 appendWindowStart 的编码帧,并在第一个 显示时间戳 大于或等于 appendWindowStart 的编码帧处生成拼接,即使该帧不是 随机访问点。支持此功能需要多个解码器或高于实时速度的解码,因此目前此行为将不会作为规范要求。
appendWindowEnd,则将 需要随机访问点标志 设为 true,丢弃该编码帧,并跳转至循环开头以开始处理下一个编码帧。一些实现 可能 选择收集 presentation timestamp 小于 appendWindowEnd 且 frame end timestamp 大于 appendWindowEnd 的编码帧,并使用它们在收集时拼接窗口内的部分已收集编码帧,以及仅部分重叠已收集编码帧末尾的后续处理帧的起始部分。支持此功能需要多个解码器或高于实时速度的解码,因此目前此行为将不会作为规范要求。结合收集跨越 appendWindowStart 的编码帧,实现 可能 从而支持无缝音频拼接。
这是为了补偿在双精度浮点数和有理数之间来回转换时可能出现的帧时间戳计算中的细微误差。这种容差允许一帧替换现有帧,只要它在现有帧开始时间的 1 微秒内即可。略早于现有帧的帧由下面的移除步骤处理。
需要进行大于检查是因为编码帧之间的双向预测可能导致 presentation timestamp 不单调递增,即使解码时间戳是单调递增的。
[[group end timestamp]],则将 [[group end timestamp]] 设为等于 frame end timestamp。[[generate timestamps flag]] 等于 true,则将 timestampOffset 设为等于 frame end timestamp。如果 HTMLMediaElement 的 readyState 属性为 HAVE_METADATA,且新的 编码帧 导致 HTMLMediaElement 的 buffered 具有一个针对当前播放位置的 TimeRanges,则将 HTMLMediaElement 的 readyState 属性设为 HAVE_CURRENT_DATA。
根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElement 的 readyState 变化可能会在 HTMLMediaElement 上触发事件。
如果 HTMLMediaElement 的 readyState 属性为 HAVE_CURRENT_DATA,且新的 编码帧 导致 HTMLMediaElement 的 buffered 具有一个包含当前播放位置以及当前播放位置之后一段时间的 TimeRanges,则将 HTMLMediaElement 的 readyState 属性设为 HAVE_FUTURE_DATA。
根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElement 的 readyState 变化可能会在 HTMLMediaElement 上触发事件。
如果 HTMLMediaElement 的 readyState 属性为 HAVE_FUTURE_DATA,且新的 编码帧 导致 HTMLMediaElement 的 buffered 具有一个包含当前播放位置及 足够确保不间断播放的数据 的 TimeRanges,则将 HTMLMediaElement 的 readyState 属性设为 HAVE_ENOUGH_DATA。
根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElement 的 readyState 变化可能会在 HTMLMediaElement 上触发事件。
duration 的数据,则运行 时长变更 算法,并将 新时长 设为当前时长与 [[group end timestamp]] 中的最大值。当需要从 SourceBuffer 中移除特定时间范围的 编码帧 时,遵循以下步骤:
对于此 SourceBuffer 中的每个 轨道缓冲区,运行以下步骤:
duration 的当前值。如果此 轨道缓冲区 具有一个大于或等于 end 的 随机访问点 时间戳,则将 remove end timestamp 更新为该随机访问点时间戳。
随机访问点时间戳在不同轨道之间可能不同,因为轨道内 编码帧 之间的依赖关系通常不同于另一轨道中的依赖关系。
如果此对象位于 activeSourceBuffers 中,当前播放位置 大于或等于 start 且小于 remove end timestamp,且 HTMLMediaElement 的 readyState 大于 HAVE_METADATA,则将 HTMLMediaElement 的 readyState 属性设为 HAVE_METADATA 并暂停播放。
根据 HTMLMediaElement 就绪状态 [HTML] 逻辑,HTMLMediaElement 的 readyState 变化可能会在 HTMLMediaElement 上触发事件。
发生此转换是因为当前位置的媒体数据已被移除。在追加当前播放位置的媒体或 3.15.5 选定/启用轨道状态的变化 之前,播放无法继续。
[[buffer full flag]] 等于 true 且此对象准备好接收更多字节,则将 [[buffer full flag]] 设为 false。当追加新数据时,运行此算法以释放此 SourceBuffer 中的空间。
此处需要识别一个步骤:如果实现预测处理 new data 以及 [[input buffer]] 中任何现有字节会超过 SourceBuffer 的容量,则实现 可能 选择在此处将 [[buffer full flag]] 设为 true。此步骤使实现能够在接受可能溢出资源的 new data 之前,进行更主动的预先反馈。实际上,至少有一个实现已经这样做了。
[[buffer full flag]] 等于 false,则中止这些步骤。实现 可能 使用不同的方法来选择 removal ranges,因此 Web 应用程序 不应该 依赖于特定行为。Web 应用程序可以使用 buffered 属性来观察缓冲数据的一部分是否已被驱逐。
当 编码帧处理 算法需要为两个重叠的音频 编码帧 生成拼接帧时,遵循以下步骤:
floor(x * sample_rate + 0.5) / sample_rate)。例如,给定以下值:
presentation timestamp 和 decode timestamp 更新为 10.0125,因为 10.01255 更接近 10 + 100/8000 (10.0125) 而非 10 + 101/8000 (10.012625)。
一些实现 可能 会对插入的静音两侧的编码帧应用淡入/淡出到静音的效果,以使过渡不那么突兀。
这旨在允许将 new coded frame 添加到 track buffer,就像 overlapped frame 从一开始就没有在 track buffer 中一样。
如果 new coded frame 的持续时间小于 5 毫秒,则需要 new coded frame 之后追加的编码帧来正确渲染拼接。
有关如何渲染此拼接帧的详细信息,请参阅 音频拼接渲染 算法。
当 音频拼接帧 算法生成的拼接帧需要由媒体元素渲染时,运行以下步骤:
以下是该算法的图形表示。
当 编码帧处理 算法需要为两个重叠的定时文本 编码帧 生成拼接帧时,遵循以下步骤:
这旨在允许将 new coded frame 添加到 track buffer,就像它从一开始就没有与 track buffer 中的任何帧重叠一样。
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);
};
length,类型为 unsigned long,只读指示列表中 SourceBuffer 对象的数量。
onaddsourcebuffer,类型为 EventHandleraddsourcebuffer 事件的事件处理器。
onremovesourcebuffer,类型为 EventHandlerremovesourcebuffer 事件的事件处理器。
允许使用数组运算符(即 [])访问列表中的 SourceBuffer 对象。
调用此方法时,用户代理必须执行以下步骤
length 属性,则返回 undefined 并中止这些步骤。SourceBuffer 对象。| 事件名称 | Interface | 调度时间:当……时 |
|---|---|---|
| addsourcebuffer |
Event
|
当 SourceBuffer 被添加到列表时。 |
| removesourcebuffer |
Event
|
当 SourceBuffer 从列表中移除时。 |
一个 ManagedMediaSource 是一个主动管理其内存内容的 MediaSource。与 MediaSource 不同,用户代理 可以出于任何原因通过 内存清理 算法从其 sourceBuffers(填充有 ManagedSourceBuffer)中驱逐内容。
WebIDL[Exposed=(Window,DedicatedWorker)]
interface ManagedMediaSource : MediaSource {
constructor();
readonly attribute boolean streaming;
attribute EventHandler onstartstreaming;
attribute EventHandler onendstreaming;
};
streaming
获取时
| 事件名称 | Interface | 调度时间:当……时 |
|---|---|---|
| startstreaming |
Event
|
一个 ManagedMediaSource 的 streaming 属性从 false 变为 true。 |
| endstreaming |
Event
|
一个 ManagedMediaSource 的 streaming 属性从 true 变为 false。 |
以下步骤会定期运行,只要安排了 SourceBuffer 监控 算法运行。
拥有 足够确保不间断播放的受管数据 是一个由实现定义的条件,用户代理在其中确定它目前拥有足够的数据来播放演示,而不会在有意义的时间段内卡顿。此条件会被不断评估,以确定何时转换 streaming 的值。这些转换指示用户代理何时认为其已缓冲足够的数据,或者何时需要更多数据。
能够 以有效方式检索和缓冲数据 是一个由实现定义的条件,用户代理在其中确定它可以在实现所需内存使用量的同时,以能源效率高的方式获取新数据。
MediaSource 的 SourceBuffer 监控 算法。buffered 属性包含的 TimeRanges 覆盖了当前播放位置,且包含 确保不间断播放所需的足够受管数据,并 能够以高效的方式检索和缓冲数据,则该标志为真。streaming,则在 媒体元素 上 排入一个元素任务,该任务执行以下步骤:streaming 属性设置为 can play uninterrupted and efficiently 的值。ManagedMediaSource 上 触发一个名为 startstreaming 的事件。ManagedMediaSource 上 触发一个名为 endstreaming 的事件。sourceBuffers 中的每一个 buffer: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;
};
addedRanges
updatestart 和 updateend 事件(该事件应在上次运行 编码帧处理 算法期间发生)之间添加的时间范围。removedRanges
updatestart 和 updateend 事件之间移除的时间范围(该事件应在上次运行 编码帧移除 或 编码帧回收 算法期间发生,或者如果用户代理为了响应 内存清理 而回收了内容)。WebIDL[Exposed=(Window,DedicatedWorker)]
interface ManagedSourceBuffer : SourceBuffer {
attribute EventHandler onbufferedchange;
};
onbufferedchange
一个 事件处理器 IDL 属性,其 事件处理器事件类型 为 bufferedchange。
| 事件名称 | Interface | 调度时间:当……时 |
|---|---|---|
| bufferedchange |
BufferedChangeEvent
|
在调用 appendBuffer()、remove()、endOfStream() 之后,或者作为用户代理运行 内存清理 算法的结果,ManagedSourceBuffer 的缓冲范围发生了变化。 |
在完成对 ManagedSourceBuffer buffer 的所有操作(这些操作会导致 buffer 的 buffered 属性发生变化)后,执行以下步骤。即一旦 appendBuffer()、remove() 或 内存清理 算法完成。
buffered 属性。buffered TimeRanges。BufferedChangeEventInit 字典,其 addedRanges 初始化为 added,removedRanges 初始化为 removed。BufferedChangeEvent 接口并以 eventInitDict 初始化,在 buffer 上 触发一个名为 bufferedchange 的事件。ManagedMediaSource 父级的 activeSourceBuffers 中:currentTime 开始的不间断播放,直到此类演示能够再次被检索为止。实现可以使用不同的策略来选择 removal ranges,因此 Web 应用程序不应依赖于特定的行为。Web 应用程序应监听 bufferedchange 事件,以观察缓冲数据的部分是否已被回收。
本节规定了当 MediaSource 附加到元素时,现有的 HTMLMediaElement 的 seekable 和 buffered 属性 必须 返回什么,以及当 HTMLMediaElement 的 srcObject 属性被设置为 MediaSourceHandle 对象时,它 必须 执行的操作。
HTMLMediaElement 的 seekable该 HTMLMediaElement 的 seekable 属性根据以下步骤返回一个新的静态 归一化 TimeRanges 对象:
MediaSource 是在已终止或正在关闭的 DedicatedWorkerGlobalScope 中构建的,则返回一个空的 TimeRanges 对象并中止这些步骤。此情况旨在处理某些实现,这些实现可能不再维护在已被 terminate() 或用户代理执行 终止工作线程(例如作为 close() 执行的最终结果)的 DedicatedWorkerGlobalScope 中构建的 MediaSource 内关于已缓冲或可寻道媒体的任何先前信息。
在附加的工作线程 MediaSource 的上下文被销毁的情况下,是否应该有某种(最终的)媒体元素错误转换?Chromium 中对 Worker MSE 的实验性实现只是保持元素的 readyState、networkState 和 error 不变,与上下文销毁前一样,尽管 seekable 和 buffered 属性各自报告为空的 TimeRange。
duration 和 [[live seekable range]] 的最新值,确定如下:MediaSource 是在 Window 中构建的:duration,并将 recent live seekable range 设置为 [[live seekable range]]。duration 和 [[live seekable range]] 最近的值,通过处理 MediaSource 在 duration 或 [[live seekable range]] 每次更改时发送至其 [[port to main]] 的隐式消息来进行更新。TimeRanges 对象。HTMLMediaElement 的 buffered 属性的并集。HTMLMediaElement 的 buffered 属性返回一个空的 TimeRanges 对象,则返回一个空的 TimeRanges 对象并中止这些步骤。HTMLMediaElement 的 buffered 属性所报告的最高结束时间。HTMLMediaElement 的 buffered该 HTMLMediaElement 的 buffered 属性根据以下步骤返回一个静态 归一化 TimeRanges 对象。
MediaSource 是在已终止或正在关闭的 DedicatedWorkerGlobalScope 中构建的,则返回一个空的 TimeRanges 对象并中止这些步骤。此情况旨在处理某些实现,这些实现可能不再维护在已被 terminate() 或用户代理执行 终止工作线程(例如作为 close() 执行的最终结果)的 DedicatedWorkerGlobalScope 中构建的 MediaSource 内关于已缓冲或可寻道媒体的任何先前信息。
在附加的工作线程 MediaSource 的上下文被销毁的情况下,是否应该有某种(最终的)媒体元素错误转换?Chromium 中对 Worker MSE 的实验性实现只是保持元素的 readyState、networkState 和 error 不变,与上下文销毁前一样,尽管 seekable 和 buffered 属性各自报告为空的 TimeRange。
MediaSource 是在 Window 中构建的:TimeRanges 对象。activeSourceBuffers.length 不等于 0,则运行以下步骤:activeSourceBuffers 中每个 SourceBuffer 对象通过 buffered 返回的范围。TimeRanges 对象。activeSourceBuffers 中的每个 SourceBuffer 对象,运行以下步骤:SourceBuffer 上 buffered 属性返回的范围。readyState 为 "ended",则将 source ranges 中最后一个范围的结束时间设置为 highest end time。Window 情况的步骤所产生的 TimeRanges,但运行时使用 MediaSource 及其 SourceBuffer 对象在其 DedicatedWorkerGlobalScope 中,并通过使用 [[port to main]] 隐式消息进行通信,这些消息在每次 activeSourceBuffers、readyState 或任何缓冲状态(该状态会改变 activeSourceBuffers 中每个 buffered 属性的任何值)更新时触发。如此频繁地重新计算和通信 recent intersection ranges 的开销是允许实现灵活性的原因之一,即使用其他机制(如共享内存和锁)按需查询此信息,如 跨上下文通信模型 中所述。
HTMLMediaElement 的 srcObject如果 HTMLMediaElement 的 srcObject 属性被赋予一个 MediaSourceHandle,则作为扩展的 HTMLMediaElement 的 srcObject 设置器(发生在调用该元素的加载算法之前)的同步步骤的一部分,将该 MediaSourceHandle 的 [[has ever been assigned as srcobject]] 设置为 true。
这可以防止再次转移该 MediaSourceHandle 对象,从而在尝试这样做时实现清晰的同步异常。
MediaSourceHandle 需要添加到 HTMLMediaElement 的 MediaProvider IDL 类型定义及相关涉及媒体提供者对象的文本中。
本节指定了对 [HTML] AudioTrack 定义的扩展。
WebIDL[Exposed=(Window,DedicatedWorker)]
partial interface AudioTrack {
readonly attribute SourceBuffer? sourceBuffer;
};
AudioTrack 需要 Window+DedicatedWorker 暴露。sourceBuffer 类型为 SourceBuffer,只读,可为空。获取时,运行以下步骤:
SourceBuffer 所创建,并且该 SourceBuffer 尚未从其 父媒体源 的 sourceBuffers 属性中移除:SourceBuffer。DedicatedWorkerGlobalScope SourceBuffer 通知其内部的 create track mirror 处理器在 Window 中创建此轨道,则该轨道的 Window 副本对此属性将返回 null。本节指定了对 [HTML] VideoTrack 定义的扩展。
WebIDL[Exposed=(Window,DedicatedWorker)]
partial interface VideoTrack {
readonly attribute SourceBuffer? sourceBuffer;
};
VideoTrack 需要 Window+DedicatedWorker 暴露。sourceBuffer 类型为 SourceBuffer,只读,可为空。获取时,运行以下步骤:
SourceBuffer 所创建,并且该 SourceBuffer 尚未从其 父媒体源 的 sourceBuffers 属性中移除:SourceBuffer。DedicatedWorkerGlobalScope SourceBuffer 通知其内部的 create track mirror 处理器在 Window 中创建此轨道,则该轨道的 Window 副本对此属性将返回 null。本节指定了对 [HTML] TextTrack 定义的扩展。
WebIDL[Exposed=(Window,DedicatedWorker)]
partial interface TextTrack {
readonly attribute SourceBuffer? sourceBuffer;
};
sourceBuffer 类型为 SourceBuffer,只读,可为空。获取时,运行以下步骤:
SourceBuffer 所创建,并且该 SourceBuffer 尚未从其 父媒体源 的 sourceBuffers 属性中移除:SourceBuffer。DedicatedWorkerGlobalScope SourceBuffer 通知其内部的 create track mirror 处理器在 Window 中创建此轨道,则该轨道的 Window 副本对此属性将返回 null。通过 appendBuffer() 为 SourceBuffer 提供的字节构成一个逻辑字节流。这些字节流的格式和语义在 字节流格式规范 中定义。字节流格式注册表 [MSE-REGISTRY] 提供了传递给 addSourceBuffer()、isTypeSupported() 或 changeType() 的 MIME 类型与 SourceBuffer 使用该 MIME 类型解析新追加数据时所期望的字节流格式之间的映射。鼓励实现方为其支持的字节流格式注册映射,以促进互操作性。字节流格式注册表 [MSE-REGISTRY] 是这些映射的权威来源。如果实现声称支持注册表中列出的 MIME 类型,则其 SourceBuffer 实现 必须 符合注册表中列出的 字节流格式规范。
注册表中的字节流格式规范无意定义新的存储格式。它们只是概述了现有存储格式结构中本规范的实现方将接受的子集。
字节流格式解析和验证是在 段解析器循环 算法中实现的。
本节提供了所有字节流格式规范的一般要求。
AudioTrack、VideoTrack 和 TextTrack 属性值的参考。如果字节流格式涵盖了一种类似于带内轨道规范 [INBANDTRACKS] 所涵盖的格式,那么它 应当 尝试使用相同的属性映射,以便媒体源扩展回放和非媒体源扩展回放提供相同的轨道信息。
轨道的数量和类型不一致。
在 初始化段 之间发生不支持的编解码器更改。
有关详细信息和示例,请参阅 初始化段接收 算法、addSourceBuffer() 和 changeType()。
例如,如果 I1 与 M1、M2、M3 相关联,则上述条件 必须 对所有组合 I1+M1、I1+M2、I1+M1+M2、I1+M2+M3 等成立。
字节流规范 必须 至少定义约束,以确保上述要求成立。可以定义附加约束 可以,例如为了简化实现。
除了标记为非规范性的章节外,本规范中的所有创作指南、图表、示例和注释均为非规范性内容。本规范中的其他所有内容均为规范性内容。
本文件中的关键字 MAY(可以)、MUST(必须)、MUST NOT(不得)、SHOULD(应该)和 SHOULD NOT(不应)在且仅在全部大写时,按 BCP 14 [RFC2119] [RFC8174] 中的描述进行解释,如本处所示。
<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>
<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>
编辑们衷心感谢 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 对本规范的贡献。
本节是非规范性的。
本规范先前版本中描述的视频回放质量指标(例如 候选推荐标准 的第 5 节和第 10 节)现在正作为 [MEDIA-PLAYBACK-QUALITY] 的一部分进行开发。一些实现可能已经实现了先前版本中描述的早期草案 VideoPlaybackQuality 对象和 HTMLVideoElement 扩展方法 getVideoPlaybackQuality()。
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自
引用自