Shaka Player 升级指南:从 v2.1 到 v2.4 的完整迁移手册
【免费下载链接】shaka-playerJavaScript player library / DASH & HLS client / MSE-EME player项目地址: https://gitcode.com/GitHub_Trending/sh/shaka-player
本篇指南以 Shaka Player 官方升级文档 docs/upgrades/upgrade-v2.1-to-v2.4.md 为主体,系统梳理从 v2.1 升级到 v2.4 过程中涉及的全部 API 变更、配置项迁移与新能力引入,并结合当前仓库的 lib 目录源码与 externs 声明文件进行源码级佐证。读完本文,你将掌握文本渲染、ABR 码率自适应、流失败重试、离线存储、网络请求与插件体系等模块在 v2.4 中的新写法,并能在不踩坑的前提下完成应用代码迁移。
一、v2.4 带来了什么
升级文档开篇即列举了 v2.1 到 v2.4 之间的核心改进,按主题可归纳为以下几类:
- 文本与字幕:允许应用自绘文本轨道;支持 CEA 字幕(TS 内容);支持 TTML 与 VTT 的 region 布局;字幕在显示前不再被提前流式下载。
- 码率自适应(ABR):默认 ABR 管理器更加可配置;vaiant 轨道上新增声道数与带宽信息;使用 NetworkInformation API 获取初始带宽估算。
- 流式传输与网络:Fetch 优先于 XHR(可用时);网络请求变为可中止(abortable);直播可在距离直播边缘负偏移处开始播放。
- 格式与清单:支持 HLS 直播流;支持开始时间不为 t=0 的 HLS VOD 流;MPEG-2 TS 内容可被转封装(transmux)为 MP4 以在所有浏览器播放;DASH 支持 Xlink。
- Player 生命周期:构造
Player时不再强制要求传入 video 元素,新增 attach() 与detach()方法管理 video 元素挂载。 - 其他:离线内容支持"无持久化许可"(即无 license persistence)方案;EME 证书配置运行时类型检查更严格;示例应用(demo)升级为可离线使用的 PWA。
需要说明的是,本文聚焦 v2.4 相对 v2.1 的 API 变化。当前仓库的 lib/player.js 等实现已远超出 v2.4 的能力范围,但文档中描述的接口契约(如TrackChoice、AbrManager等)在 externs 中仍可找到一一对应的声明,可以作为迁移目标的权威依据。
二、新的shaka.text命名空间
在 v2.1 中,TextEngine隶属于shaka.media命名空间;从 v2.2 起,它被迁移到新的shaka.text命名空间。文本解析插件现在应通过shaka.text.TextEngine.registerParser注册。
当前仓库中 lib/text/text_engine.js 即为该迁移后的实现:
static registerParser(mimeType, plugin) { shaka.text.TextEngine.parserMap_.set(mimeType, plugin); }同时它还提供了unregisterParser(mimeType)与findParser(mimeType)两个配套静态方法(见 lib/text/text_engine.js)。TextEngine的构造函数签名为new TextEngine(displayer, manifestType),其中displayer是shaka.extern.TextDisplayer实例——这正是下一节自定义字幕显示的挂载点。
三、自定义字幕显示(Customizing subtitle display)
v2.1 允许应用接入自定义文本解析器,但字幕的最终显示完全由浏览器负责。v2.2 起 Shaka 将"显示"环节也开放给应用:默认情况下渲染工作由shaka.text.SimpleTextDisplayer类完成,应用可通过player.configure()指定自定义的文本显示工厂:
player.configure({ textDisplayFactory: customTextDisplayerClass });自定义显示类需要实现shaka.extern.TextDisplayer接口(声明见 externs/shaka/player.js),核心职责包括append(cues)、remove(startTime, endTime)、destroy()等。从 lib/player.js 的默认配置可以看到,Player 在构造默认配置时便通过工厂函数懒加载 displayer——工厂接收 player 实例作为参数,应用也可以基于此实现"运行时根据场景选择 displayer"的逻辑。
在 lib/text/text_engine.js 中可以看到渲染链路:解析出的 cues 会先经过可选的modifyCueCallback修改(可用于样式或时间戳校正),再按 append window 过滤后交给displayer_.append(cuesToAppend)。
四、文本解析器 API 变更(破坏性)
v2.4 对文本解析插件 API 做了不向后兼容的修改,所有应用自带的解析插件必须更新:
- 返回值:插件现在返回
shaka.text.Cue对象数组,而非 v2.1 的VTTCue/TextTrackCue。 - 入参:
parseMedia的 data 参数由ArrayBuffer改为Uint8Array。 - 时间上下文:
timeContext.segmentStart变为可空(nullable)。当该信息不可用(例如 HLS 场景)时,插件会收到null。
对照代码迁移示例:
// v2.1 MyTextParser.prototype.parseMedia = function(data, timeContext) { var cues = []; var parserState = new MyInternalParser(data); // ArrayBuffer while (parserState.more()) { cues.push(new VTTCue(...)); } return cues; }; // v2.4 MyTextParser.prototype.parseMedia = function(data, timeContext) { var cues = []; var parserState = new MyInternalParser(data); // Uint8Array while (parserState.more()) { cues.push(new shaka.text.Cue(...)); } return cues; };在仓库中,externs/shaka/text.js 定义了shaka.extern.TextParser接口,parseMedia(data, timeContext, uri, images)返回!Array<!shaka.text.Cue>;externs/shaka/text.js 定义了TimeContext,包含periodStart、segmentStart、segmentEnd、vttOffset、isMpegTs五个字段。TextEngine.appendBuffer内部正是构造该time对象并传给解析器(见 lib/text/text_engine.js)。
shaka.text.Cue类包含与VTTCue相同的 cue 信息,并额外携带文本样式(样式相关字段与textDisplayFactory配合可实现完全自定义的渲染效果)。
注意:v2.4不提供该变更的向后兼容层,升级时必须同步更新全部文本解析插件。
五、ABR 管理器的设置与配置
5.1 配置入口变更
v2.1 中自定义 ABR 管理器通过abr.manager配置项注入,v2.4 改为顶层abrFactory:
// v2.1 player.configure({ abr.manager: customAbrManager }); // v2.4 player.configure({ abrFactory: customAbrManager });5.2 AbrManager 接口变更
v2.1 中,默认带宽估算与码率限制通过setDefaultEstimate()和setRestrictions()两个独立方法设置;v2.4 统一收敛为configure()方法,接受一个shaka.extern.AbrConfiguration结构(声明位于 externs/shaka/player.js):
// v2.1: abrManager.setDefaultEstimate(defaultBandwidthEstimate); abrManager.setRestrictions(restrictions); // v2.4: abrManager.configure(abrConfigurations);新方法更通用,除默认带宽与限制外,还允许配置带宽升/降级目标(bandwidthUpgradeTarget/bandwidthDowngradeTarget)。在默认实现 lib/abr/simple_abr_manager.js 中,configure(config)将配置缓存到this.config_并同步给内部的EwmaBandwidthEstimator。
5.3 流选择 API:chooseStreams()→chooseVariant()
v2.1 中 Player 通过chooseStreams()请求流选择,AbrManager 通过switch()回调把"主动建议的变更"回传,参数是 audio/video 流的映射表;v2.4 中chooseStreams()被chooseVariant()取代,switch()回调直接接收一个 variant:
// v2.1: var map = abrManager.chooseStreams(['audio', 'video']); console.log(map['video'], map['audio']); MyAbrManager.prototype.makeDecision_ = function() { var video = this.computeBestVideo_(this.bandwidth_); var audio = this.computeBestAudio_(this.bandwidth_); var map = { 'audio': audio, 'video': video }; this.switch_(map); }; // v2.4: var variant = abrManager.chooseVariant(); console.log(variant, variant.video, variant.audio); MyAbrManager.prototype.makeDecision_ = function() { var variant = this.computeBestVariant_(this.bandwidth_); this.switch_(variant); };当前仓库的shaka.extern.AbrManager接口(externs/shaka/abr_manager.js)完整反映了这一演进:setVariants(variants, isLowLatency)、chooseVariant(preferFastSwitching)、enable()/disable()、configure(config)等。switchCallback的语义也已明确——第一个参数是要切换到的 variant,第二、三个可选参数控制是否清空缓冲(clearBufferSwitch)以及清缓冲时保留的安全余量秒数(safeMarginSwitch),后者可用于实现无卡顿的快速切换。
v2.1 的旧接口在 v2.2 被标记废弃、v2.3 被移除,所有自定义 AbrManager 插件必须更新到 v2.4 接口。
六、切换历史(Switch History)的变化
v2.1 中shakaExtern.Stats.switchHistory使用shakaExtern.StreamChoice结构,type字段为 'audio' / 'video' / 'text'。v2.2 起改名为shakaExtern.TrackChoice,语义从"流"细化到"轨道":
// v2.1: shakaExtern.StreamChoice; // id: 流 id;type: 'audio'/'video'/'text' // v2.4: shakaExtern.TrackChoice; // id: 轨道 id;type: 'variant'/'text';新增 bandwidth仓库 externs/shaka/player.js 中的shaka.extern.TrackChoice定义与升级文档完全一致:{ timestamp, id, type: 'variant'|'text', fromAdaptation, bandwidth: ?number },其中fromAdaptation表示该切换是 ABR 自适应(true)还是应用主动调用选择接口(false),bandwidth对文本轨为null。
lib/util/switch_history.js 提供了SwitchHistory实现:updateCurrentVariant(newVariant, fromAdaptation)与updateCurrentText(newText, fromAdaptation)会去重记录冗余切换,getCopy()返回历史副本;文本轨的 bandwidth 固定记录为null(lib/util/switch_history.js),与 typedef 约定一致。开发者通过player.getStats().switchHistory即可拿到该数组用于分析码率切换行为。
七、流失败后的自定义重试逻辑
这是 v2.2 引入、v2.4 完整成型的核心能力。v2.0 时代,网络错误且重试耗尽后流式传输会无限继续重试请求,唯一的终止方式是unload()或destroy();v2.1.3 增加了streaming.infiniteRetriesForLiveStreams配置来单独控制直播重试;v2.2 则替换为更灵活的streaming.failureCallback回调机制,覆盖所有流类型:
// v2.1 player.configure({ streaming: { infiniteRetriesForLiveStreams: true // 默认值 } }); // v2.4 player.configure({ streaming: { failureCallback: function(error) { // 直播流总是重试: if (player.isLive()) player.retryStreaming(); } } });关闭重试的对应写法:
// v2.1 player.configure({ streaming: { infiniteRetriesForLiveStreams: false // 不重试直播 } }); // v2.4 player.configure({ streaming: { failureCallback: function(error) { // 什么都不做,即停止尝试流式传输该内容 } } });streaming.infiniteRetriesForLiveStreams在 v2.2 被废弃、v2.3 被移除。新机制下,决策依据可以是player.isLive()、error.code或任何其他信息;由于player.retryStreaming()可在任意时刻调用,你完全可以推迟决策——比如等用户反馈、等浏览器重新联网。
文档给出的几种典型回调策略:
function neverRetryCallback(error) {} function alwaysRetryCallback(error) { player.retryStreaming(); } function retryLiveOnFailureCallback(error) { if (player.isLive()) { player.retryStreaming(); } } function retryOnSpecificHttpErrorsCallback(error) { if (error.code == shaka.util.Error.Code.BAD_HTTP_STATUS) { var statusCode = error.data[1]; var retryCodes = [ 502, 503, 504, 520 ]; if (retryCodes.indexOf(statusCode) >= 0) { player.retryStreaming(); } } }如果你更习惯通过error事件处理,也可以用event.preventDefault()完全跳过 failureCallback:
player.addEventListener('error', function(event) { // 自定义 error 事件逻辑 if (player.isLive() && event.error.code == shaka.util.Error.Code.BAD_HTTP_STATUS) { player.retryStreaming(); } // 该事件不再触发 failureCallback event.preventDefault(); });当前仓库中 lib/player.js 的retryStreaming(retryDelaySeconds = 0.1)仅对 MEDIA_SOURCE 加载模式生效(loadMode_ == shaka.Player.LoadMode.MEDIA_SOURCE时调用streamingEngine_.retry()),且可传入以秒为单位的重试延迟。Player 的默认 failureCallback(lib/player.js)演示了 v2.4 之后的演进方向:对动态流(isDynamic())遇到BAD_HTTP_STATUS/HTTP_ERROR默认延迟 1 秒重试(低延迟模式下 0.1 秒)、TIMEOUT错误延迟 0.1 秒重试;而 VOD 流的流式失败视为致命错误不自动重试。另外 Player 内部还会监听浏览器的online事件,恢复联网后自动调用retryStreaming()恢复播放(lib/player.js)。
八、HLS 起始时间配置的移除
对于开始时间不为 t=0 的 HLS VOD 内容,v2.1 提供了manifest.hls.defaultTimeOffset配置来告知正确的起始时间。该配置在 v2.4 中已被移除——HLS 内容的起始时间现在可以从分段(segment)本身自动提取,无需任何配置。
这意味着升级到 v2.4 后,应用代码中针对manifest.hls.defaultTimeOffset的配置可以直接删除,由解析器自动完成起始时间的推导。
九、离线存储 API 变更
v2.1 中shaka.offline.Storage.remove()接收一个StoredContent实例;v2.4 改为接收StoredContent上的offlineUri字段(字符串):
// v2.1: storage.list().then(function(storedContentList) { var someContent = storedContentList[someIndex]; storage.remove(someContent); }); // v2.4: storage.list().then(function(storedContentList) { var someContent = storedContentList[someIndex]; storage.remove(someContent.offlineUri); });旧参数形式在 v2.3 被废弃、v2.4 移除,所有使用离线存储的应用必须更新。仓库 lib/offline/storage.js 中remove(contentUri)的签名验证了这一点:它接收字符串 URI,内部通过shaka.offline.OfflineUri.parse解析并以isManifest()校验,非法 URI 会抛出MALFORMED_OFFLINE_URI错误。
十、语言与角色(Language and Role)选择
在 v2.1 语言选择方法的基础上,v2.4 新增了针对角色的方法:getAudioLanguagesAndRoles()与getTextLanguagesAndRoles()。它们返回"语言/角色"组合对象数组,且语言选择方法支持用可选的第二个参数指定角色:
// v2.4: var languagesAndRoles = player.getAudioLanguagesAndRoles(); for (var i = 0; i < languagesAndRoles.length; ++i) { var combo = languagesAndRoles[i]; if (someSelector(combo)) { player.selectAudioLanguage(combo.language, combo.role); break; } }这一能力与当前仓库的轨道模型一脉相承:shaka.extern.Track结构中的language、roles字段(externs/shaka/player.js)正是语言/角色组合的数据来源,而 Player 的轨道查询/选择接口(getAudioTracks()、selectAudioTrack()、getTextTracks()、selectTextTrack()等,见 lib/player.js)继续承载"按用户偏好切换轨道"的职责。
十一、NetworkingEngine API 变更
v2.1 中shaka.net.NetworkingEngine.request()直接返回 Promise;v2.4 返回shakaExtern.IAbortableOperation实例,其中包含一个 Promise:
// v2.1: player.getNetworkingEngine().request(type, request).then((response) => { // ... }); // v2.4: let operation = player.getNetworkingEngine().request(type, request); // 用 operation.promise 获取响应 operation.promise.then((response) => { // ... }); // 也可在满足某个条件时中止操作 onSomeOtherCondition(() => { operation.abort(); });v2.4 通过给request()的返回值附加.then与.catch方法提供了向后兼容层,但该兼容层计划在 v2.5 移除,应用级请求建议尽快迁移到新 API。
从源码看,lib/net/networking_engine.js 的request(type, request, context)返回shaka.net.NetworkingEngine.PendingRequest(继承AbortableOperation),内部先执行带重试的请求,再通过.chain()串联 response filter 等阶段;当请求尚未开始就调用 abort 时,会以OPERATION_ABORTED拒绝。
十二、网络 scheme 插件 API 变更
v2.4 同时变更了网络 scheme 插件(自定义 URI 协议的请求插件)的 API:
- 插件返回
shakaExtern.IAbortableOperation实例,官方建议使用shaka.util.AbortableOperation工具类。 - 新增第三个参数
requestType用于标识请求类型(MANIFEST、SEGMENT、LICENSE 等),应用可据此实现差异化处理。
// v2.1 function fooPlugin(uri, request) { return new Promise((resolve, reject) => { // ... }); } shaka.net.NetworkingEngine.registerScheme('foo', fooPlugin); // v2.4 function fooPlugin(uri, request, requestType) { let rejectCallback = null; const promise = new Promise((resolve, reject) => { rejectCallback = reject; // 需要时使用 requestType,否则忽略 if (requestType == shaka.net.NetworkingEngine.RequestType.MANIFEST) { // ... } else { // ... } }); const abort = () => { // 中止底层操作 // ... // 拒绝 Promise rejectCallback(new shaka.util.Error( shaka.util.Error.Severity.RECOVERABLE, shaka.util.Error.Category.NETWORK, shaka.util.Error.Code.OPERATION_ABORTED)); }; return new shaka.util.AbortableOperation(promise, abort); } shaka.net.NetworkingEngine.registerScheme('foo', fooPlugin);shaka.util.AbortableOperation的完整实现位于 lib/util/abortable_operation.js,除了构造函数(promise, onAbort)外,还提供若干静态工厂:failed(error)、aborted()、completed(value)、notAbortable(promise)、all(operations),以及用于串联异步阶段的实例方法chain(onSuccess, onError)和finally(onFinal)。aborted属性可查询操作是否已被中止(lib/util/abortable_operation.js)。
注意:旧的 Promise 风格 scheme 插件同样只在 v2.4 中保留兼容层,计划在 v2.5 移除。
十三、Manifest 解析器插件 API 变更
shaka.media.PresentationTimeline的接口发生两处重命名/签名变化,使用这些方法的ManifestParser插件必须同步更新:
setAvailabilityStart()更名为setUserSeekStart()。notifySegments()现在接收一个引用数组(reference array)和一个名为isFirstPeriod的布尔值,取代原来的 period 起始时间 + 引用数组两个参数。
这两处变更直接影响 DASH、HLS 等清单解析插件的实现,应用若自行编写 ManifestParser 插件,升级时务必检查对PresentationTimeline的调用是否命中上述方法。
十四、升级清单速查
以下为从 v2.1 迁移到 v2.4 时需要逐一核对的应用代码变更点:
| 变更领域 | v2.1 写法 | v2.4 写法 | 迁移强度 |
|---|---|---|---|
| 文本命名空间 | shaka.media.TextEngine | shaka.text.TextEngine.registerParser(...) | 必须 |
| 文本解析插件 | 返回VTTCue,入参ArrayBuffer | 返回shaka.text.Cue,入参Uint8Array | 必须(无兼容层) |
| 字幕显示 | 仅浏览器渲染 | textDisplayFactory自定义渲染 | 可选 |
| ABR 配置 | abr.manager+setDefaultEstimate/setRestrictions | abrFactory+configure(AbrConfiguration) | 必须 |
| ABR 接口 | chooseStreams()/switch(streamMap) | chooseVariant()/switch(variant) | 必须 |
| 切换历史 | shakaExtern.StreamChoice | shakaExtern.TrackChoice(新增 bandwidth) | 读取方需适配 |
| 失败重试 | streaming.infiniteRetriesForLiveStreams | streaming.failureCallback+player.retryStreaming() | 必须 |
| HLS 起始时间 | manifest.hls.defaultTimeOffset | 自动从分段提取,配置移除 | 删除配置 |
| 离线删除 | storage.remove(storedContent) | storage.remove(storedContent.offlineUri) | 必须 |
| 语言/角色 | 仅语言选择方法 | 新增getAudioLanguagesAndRoles()等 | 新增能力 |
| 网络请求 | request()返回 Promise | 返回IAbortableOperation(含.promise/.abort()) | 强烈建议 |
| 网络插件 | 返回 Promise,(uri, request) | 返回AbortableOperation,(uri, request, requestType) | 强烈建议 |
| 清单插件 | setAvailabilityStart()、旧notifySegments() | setUserSeekStart()、新notifySegments(refs, isFirstPeriod) | 必须(若使用) |
十五、延伸阅读
- 升级文档系列:upgrade-v2.2-to-v2.4.md、upgrade-v2.3-to-v2.4.md,以及 v2.4 之后的 upgrade-v2.4-to-v2.5.md 与 upgrade-v2.5-to-v3.0.md。
- ABR 默认实现:lib/abr/simple_abr_manager.js 与带宽估算器 lib/abr/ewma_bandwidth_estimator.js。
- 文本引擎与 Cue:lib/text/text_engine.js、lib/text/cue.js。
- 网络层:lib/net/networking_engine.js 与可中止操作工具 lib/util/abortable_operation.js。
- 离线存储:lib/offline/storage.js。
- 基础使用与配置:docs/tutorials/basic-usage.md、docs/tutorials/config.md。
【免费下载链接】shaka-playerJavaScript player library / DASH & HLS client / MSE-EME player项目地址: https://gitcode.com/GitHub_Trending/sh/shaka-player
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考