ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Shaka Player DASH Manifest 时间轴全解析:Presentation Time、Period 与 Live Edge 计算指南

2026/9/16 16:30:56 拓冰建站 浏览量
Shaka Player DASH Manifest 时间轴全解析:Presentation Time、Period 与 Live Edge 计算指南 Shaka Player DASH Manifest 时间轴全解析Presentation Time、Period 与 Live Edge 计算指南【免费下载链接】shaka-playerJavaScript player library / DASH HLS client / MSE-EME player项目地址: https://gitcode.com/GitHub_Trending/sh/shaka-player导读DASHDynamic Adaptive Streaming over HTTP清单Manifest是流媒体播放的“地图”而其中最让开发者困惑的莫过于时间time、Period、presentationTimeOffsetPTO、live edge 等一堆相互关联又容易混淆的概念。本文以 Shaka Player 官方设计文档为基础结合 lib/dash/dash_parser.js 与 lib/media/presentation_timeline.js 的源码实现系统拆解 DASH Manifest 的时间轴模型从三类“时间”的定义到 presentation time 的精确计算公式再到 Live 流的 live edge、segment availability 与时钟同步机制。读完本文你将能读懂任何一份 DASH Manifest 的时间字段、独立推算每个片段的播放时间并具备排查“Live 流无法播放”类问题多为时钟同步所致的实战能力。认识 DASH Manifest 中的三类“时间”流媒体处理中充斥着各种时间概念VOD 与 Live 的取值规则又各不相同。文档强调必须为每种时间使用准确的词汇避免混淆。虽然各类时间之间可以互相换算但它们的值各不相同必须视为不同的量。Presentation time呈现时间Presentation time 是呈现节目内部的时间表示自节目开始以来经过的时间量对 VOD 而言它是自视频开头起算的时间对 Live 而言它是自直播流开始起算的时间。它是播放器逻辑层统一使用的时间基准详见下文“Presentation Timeline”。Media time媒体时间Media time 是编码在媒体分片media segment内部的时间戳。每个分片内部都带有“应该在什么时间播放”的信息。播放器不会用 media time 来决定呈现时间真正消费 media time 的是底层媒体播放器即浏览器它把分片放进video元素的对应时间位置。例如一个分片的 media time 是 30 秒浏览器就会把它放到video的 30 秒处。Manifest 可以通过属性调整 media timepresentationTimeOffset与Periodstart都会改变媒体时间从而使分片在video元素中出现于正确的位置。这一点是理解 PTO 的关键——见“计算 presentation times”一节。Wall-clock time墙钟时间Wall-clock time 就是“时钟上看到的时间”数值上通常用自 Unix epoch 起的秒数表示。它并不意味着某个分片“将在什么时刻被播放”——播放可能因用户暂停而推迟。墙钟时间主要用于 Live 流的 live edge 与 segment availability 计算以及availabilityStartTime、UTCTiming等字段。Presentation Timeline一切播放的基准DASH 中最大的核心概念是presentation timeline呈现时间轴。它是一条统一的、描述“该播放什么内容”的时间线可以理解为单个 VOD 片段的时间轴时间轴从 0 开始它规定了每个分片在什么时间播放如果某个媒体分片的 presentation time 是 30 秒那么它将在呈现开始 30 秒后播放。来自不同内容的媒体时间会被调整adjust到时间轴上的正确位置从而可以把多个片段拼接concatenate成连续的视频播放。同一时刻可能存在多条描述不同版本内容的流例如 SD 与 HD它们共享同一条时间轴供播放器切换。Shaka Player 的实现策略播放器只处理 presentation time并直接把它映射到video元素——也就是说video.currentTime就代表内容的 presentation time。因此当你 seek 到 90 秒时播放器会去查找 presentation time 为 90 秒的那个分片。从源码看这一“查找”由各 Segment Index 在解析时构建以 lib/dash/segment_template.js 为例解析器计算segmentStart segmentPeriodTime periodStart再结合段时长确定每个SegmentReference的startTime/endTime相对呈现的秒数从而把“period 内相对时间”换算为“呈现时间轴上的绝对时间”。Periods时间轴上的独立片段Presentation timeline 由一个或多个顺序排列的 Period组成每个 Period 是时间轴上一段独立的内容Period不能重叠Period 之间不应存在间隙虽然 Shaka Player 支持 gap jumping相关实现见 lib/media/gap_jumping_controller.js每个 Period 都有起始时间可以显式通过start属性指定也可以由上一个 Period 的duration属性隐式推导。内容被“裁剪”到其所属 Period 内出现在 Period 开始之前或结束之后的内容会被丢弃如果整个分片都落在 Period 之外播放器会直接忽略它。一个隐蔽的坑appendWindowStart 与段首不完整分片文档特别提醒Shaka Player 使用 MSE 的appendWindowStart实现内容删除这在 Period 开头存在不完整分片partial segment时可能引发问题当浏览器丢弃 Period 开始处的内容后它会一直丢帧直到下一个关键帧。也就是说如果你的第一个分片相对 Period 起始位置是 -0.1 秒例如PTO St而该分片内又没有更多关键帧那么整个分片都可能被丢掉。这一行为遵循 MSE 规范中的 coded frame processing 算法属于浏览器 MediaSource 实现层面的限制manifest 生成方在编排 Period 边界分段时应特别留意。计算 presentation times公式与实例文档给出了一份同时使用SegmentTemplate与SegmentTimeline的典型示例。注意文档说明换成SegmentBase或SegmentList等其他形式结果完全一样——无论如何最终都会得到一张“分片列表”其中每个分片都有起点与时长Period startPT30S durationPT10S AdaptationSet Representation SegmentTemplate timescale10 presentationTimeOffset100 medias$Number$.mp4 initializationinit.mp4 SegmentTimeline S t111 d40 / S d10 / S t170 d10 / /SegmentTimeline /SegmentTemplate /Representation /AdaptationSet /Period通用计算公式将任意分片起点换算为 presentation time 的通用公式如下start 111; // St presentationTime (start - presentationTimeOffset) / timescale periodStart;换算分三步每一步都有明确的语义用 PTO 校正分片起点。manifest 中St指定的时间必须与媒体时间media time一致而 PTO 会同步调整媒体时间。如果只改St而不改 PTO追加媒体后浏览器会把分片放到错误的时间位置——因为媒体时间没有被正确校正。按 timescale 归一化。St与 PTO 都以 timescale 为单位需要除以 timescale 换算成秒。加上 Period 起始时间。Manifest 中的时间都是相对所属 Period 的必须加上 Period 在时间轴上的位置才能得到真实的 presentation time。这让每个 Period 可以独立书写、不依赖其在时间轴中的绝对位置——对广告插入ad-insertion场景尤其有用。数值演算按上述公式对示例清单逐段计算第一段起点(111 - 100) / 10 3031.1 秒第一段时长为Sd / timescale40 / 10 4 秒因此第二段起点为35.1 秒第三段起点(170 - 100) / 10 3037 秒第二段与第三段之间存在 0.9 秒的间隙。该公式在源码中同样可见。例如 lib/dash/dash_parser.js 在解析EventStream事件时间时执行了完全一致的换算const presentationTimeOffset TXml.parseAttr(elem, presentationTimeOffset, parseNumber) || 0; // ... let startTime Math.max( (presentationTime - presentationTimeOffset) / timescale periodStart, periodStart);可见presentationTimeOffset的解析默认值为 0、timescale默认值为 1且解析出的时间会被钳制clamp到不小于periodStart——这与文档“内容被裁剪到 Period 内”的描述相互印证。如何为 presentationTimeOffsetPTO取值文档给出了三条非常实操的选值准则St应严格等于媒体时间。如果这样无法得到想要的 presentation time应当通过 PTO 或 Period 的start来调整而不是去改St。单一 Period 内容可以令Periodstart 0、只用 PTO 校正分段位置多 Period 内容则需要调整各 Period 内部的分段使其从 0 开始。所有流必须使用相同的 PTO 值。不要偷懒直接把第一个St当作 PTO——那样会导致不同流被不同量地校正造成音视频不同步A/V desync。正确做法是选定一个具体值让每条流都使用相同的 PTO注意考虑各自的 timescale。在两个候选值之间优先选较小的那个。若选了较大的值另一条流的 Period 相对时间会变成负数从而触发上文 appendWindowStart 相关的问题。从源码看PTO 在多个解析路径中都被读取SegmentBase通过继承解析lib/dash/segment_base.js、SegmentList由 lib/dash/mpd_utils.js 读取且 lib/dash/segment_list.js 明确注明使用duration时不使用 PTO因为“PTO 已在 timeline 创建时被考虑”。这从实现层面印证了“PTO 统一影响媒体时间”的语义。Live与 VOD 几乎相同的时间轴一个经常被误解的事实Live 的播放机制与 VOD 几乎完全一样——同样有统一的时间轴同样使用 presentation time。区别只在于Live 的 presentation time 0 表示直播流开始的那一刻对应 manifest 中的availabilityStartTimeAST开始播放 Live 流时播放器计算now与 AST 的差值从而得到“当前该播到哪个 presentation time”。重要不要试图把 presentation time 映射到墙钟时间。若某分片的 presentation time 是 5 分钟、AST 是 9:00绝不意味着它会在 9:05 被播放——应用可以暂停视频从而推迟其实际播放时刻这个时间只表示“它是什么时候被录制的”。另一个常见做法是Live 内容的媒体时间戳直接用 Unix 时间戳并把 AST 设为 epoch这样分片自然落在正确位置。文档指出这种做法合法但非必需——和 VOD 一样只要调整后的 presentation time 让分片落到正确位置媒体时间本身是多少并不重要。Live edge当前该播到哪播放 Live 内容时首先要确定“当前时间”。live edge就是直播播放的最新边界以 presentation time 表示随着播放进行live edge 会随当前墙钟时间一起向前推进。计算方式分两步用 AST 确定直播流开始的墙钟时刻用now - AST得到当前对应的 presentation time但服务器仍在录制最新分片、尚不可播放因此还要往回退一个分片时长maxSegmentSize。const ast 1518717600; // availabilityStartTime2018-02-15T18:00:00Z const now 1518718680; // (Date.now() / 1000) - 2018-02-15T18:18:00Z const maxSegmentSize 10; // seconds const liveEdge now - ast - maxSegmentSize; // 1070 video.currentTime liveEdge; // Start playing at live edge. // Well start playing segments with a *presentation time* around 1070.该公式与 Shaka Player 源码中的PresentationTimeline.getLiveEdge_()实现完全对应lib/media/presentation_timeline.jsconst now (Date.now() this.clockOffset_) / 1000.0; return Math.max( 0, now - this.maxSegmentDuration_ - this.presentationStartTime_);注意其中presentationStartTime_即 AST 对应的秒数maxSegmentDuration_即文档中的maxSegmentSize且结果被钳制为不小于 0。clockOffset_则来自时钟同步见下文“Clock sync”它把客户端时钟修正到服务端时钟后再参与计算。然而即使这样manifest 更新与网络延迟仍可能导致在 live edge 处播放时卡顿。因此播放器实际会再往后延迟一段距离再开始播放。这段延迟可以由 manifest 的MPDsuggestedPresentationDelay属性指定该属性即为 Live 流预留的平滑播放余量若未指定播放器使用合理的默认值。从源码看Shaka Player 的延迟取值有明确的三级优先级lib/dash/dash_parser.js若 manifest 提供了suggestedPresentationDelay优先使用并以timeShiftBufferDepth作为下限约束官方建议内容提供方尽可能提供该属性以优化直播体验否则使用开发者配置manifest.defaultPresentationDelay若大于 0作为回退否则默认取minBufferTime * 1.5与 segmentAvailabilityDuration 中的较小值属于较保守的选择。此外还有对应的运行时配置项dash.ignoreSuggestedPresentationDelay可让播放器忽略 manifest 中的该属性lib/dash/dash_parser.js。Segment availability哪些分片还能下载DASH 定义了分片可用性availability只要一个分片“可用”它就必须存在于服务器上可供下载。可用性窗口由MPDtimeShiftBufferDepthTSBD属性决定它指定了围绕 live edge 的一个移动窗口。例如 TSBD 为 10 分钟意味着可以回看最近 10 分钟的直播或者说可 seek 的 presentation time 范围是“live edge 往前 10 分钟”到 live edge 之间。Manifest 列出的分片数可以多于或少于可用性窗口列出更多时多余的分片在 manifest 下载时可能已经不可用列出更少时播放器可能无法填满整个可用性窗口。关键点播放器被允许记住不再列出的分片。即使更新后的 manifest 不再包含某些分片只要它们仍在可用性窗口内就仍可下载。Shaka Player 的实现印证了这一点——在每次 manifest 更新前DashParser会先按presentationTimeline.getSegmentAvailabilityStart()对既有 SegmentIndex 执行evictlib/dash/dash_parser.js同时PresentationTimeline内部通过maxSegmentEndTime_、maxSegmentDuration_等记忆已见到的分片边界lib/media/presentation_timeline.js。而 seek 范围的起止分别由getSafeSeekRangeStart与getSegmentAvailabilityEnd计算getSegmentAvailabilityEnd在动态内容上返回min(liveEdge availabilityTimeOffset, duration)lib/media/presentation_timeline.jsgetSafeSeekRangeStart再结合minSegmentStartTime_与用户 seek 起点做安全兜底lib/media/presentation_timeline.js。另外低延迟 DASHLL-DASH场景可通过setAvailabilityTimeOffset让分片“提前可下载”从而更贴近 live edgelib/media/presentation_timeline.js。Clock sync时钟同步是 Live 的生命线由于 live edge 与可用性窗口的计算都依赖当前墙钟时间服务端与客户端必须保持时钟同步。DASH 通过UTCTiming元素实现它指明如何获取服务端的精确时钟客户端据此修正本地时钟。Shaka Player 对UTCTiming的支持情况lib/dash/dash_parser.js整理如下schemeIdUri方式Shaka Player 支持情况urn:mpeg:dash:utc:http-head:2014/:2012HTTP HEAD 请求返回时间支持urn:mpeg:dash:utc:http-xsdate:2014/:2012HTTP GET 返回 XML Schema 日期支持urn:mpeg:dash:utc:http-iso:2014/:2012HTTP GET 返回 ISO 8601 日期支持urn:mpeg:dash:utc:direct:2014/:2012直接在属性中携带日期支持urn:mpeg:dash:utc:http-ntp:2014/:ntp:2014/:sntp:2014NTP/SNTP不支持记录警告其他—记录警告后跳过同时开发者可通过配置dash.clockSyncUri提供一个默认的时钟同步地址当 manifest 未给出任何UTCTiming时播放器会将其作为http-head:2014方案使用lib/dash/dash_parser.js。若最终一个可用方案都没有播放器会发出alwaysWarn级警告“Live manifests 中应始终包含 UTCTiming 元素此内容在时钟不准的客户端上可能无法播放”——这正是文档强调的观点DASH manifest 对漂移drift与时钟同步问题高度敏感。值得一提的是文档写作时提到 Shaka Player 计划改为“用分片列表计算 live edge”。从当前仓库源码看该能力已落地于PresentationTimeline其notifySegments/notifyTimeRange会利用显式的分片起止时间在启用autoCorrectDrift时反向推算presentationStartTime_now - maxSegmentEndTime_ - maxSegmentDuration_从而让 live edge 的估算对编码器漂移不敏感lib/media/presentation_timeline.js并通过usingPresentationStartTime()暴露当前是否仍依赖 AST 计算lib/media/presentation_timeline.js。但请务必注意DASH 规范本身仍要求准确的时钟——即使 Shaka Player 容忍了漂移你的 manifest 也可能在其他 DASH 播放器上无法播放。排错速查Live 流不播先查时钟综合文档与源码当遇到 Live manifest 无法播放时建议按以下顺序排查确认 manifest 包含UTCTiming元素且 scheme 属于上表“支持”的范围否则在时钟偏差大的客户端上必然出问题。确认客户端与服务端时钟偏差可控或已通过UTCTiming成功同步观察 Shaka Player 日志中的时钟同步警告。核对availabilityStartTime、timeShiftBufferDepth与suggestedPresentationDelay的组合可用性窗口、live edge 与播放起点均由这三者共同决定任何一项异常都会导致 seek 范围错乱或卡顿。核对各 Representation 的presentationTimeOffset是否一致注意 timescale避免音视频不同步。检查 Period 边界分段避免在 Period 开头放置无关键帧的短分片以免被 MSEappendWindowStart机制整段丢弃。延伸阅读本文配套时间轴示意图docs/design/current/dash-timeline.svgDASH 解析器主体实现lib/dash/dash_parser.jsAST / TSBD / SPD 解析见 L705-L809UTCTiming解析见 L3734-L3802呈现时间轴模型lib/media/presentation_timeline.jslive edge 见 L740-L747可用性窗口见 L621-L682分段模板生成lib/dash/segment_template.js相关单元测试test/dash/dash_parser_manifest_unit.js覆盖presentationTimeOffset、timeShiftBufferDepth、suggestedPresentationDelay等解析行为【免费下载链接】shaka-playerJavaScript player library / DASH HLS client / MSE-EME player项目地址: https://gitcode.com/GitHub_Trending/sh/shaka-player创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考