Shaka Player v2.2 升级至 v2.4 完整指南:API 迁移、配置变更与重试机制详解
【免费下载链接】shaka-playerJavaScript player library / DASH & HLS client / MSE-EME player项目地址: https://gitcode.com/GitHub_Trending/sh/shaka-player
本文是面向应用开发者的 Shaka Player 从 v2.2 升级到 v2.4 的详细迁移指南,覆盖 HLS 起始时间配置移除、文本解析/显示插件 API 重写、离线存储删除接口变更、流失败重试回调机制、语言与角色选择、网络层可中止操作(IAbortableOperation)以及 Manifest 解析器相关 API 调整等全部破坏性变更。读完本文,你将掌握每个变更点的迁移步骤、可直接复制的升级代码示例,以及对应的源码实现依据,能够在升级后快速定位并修复自己的应用代码。
v2.4 带来了哪些新能力
相比 v2.2,Shaka Player v2.4 引入了一系列播放能力与工程体验上的改进,主要包括:
- HLS 直播流支持,并支持起始时间不为 t=0 的 HLS VOD 流;
- MPEG-2 TS 内容可被转封装(transmux)为 MP4,从而在所有浏览器上播放;
- 字幕仅在显示时才被拉取流式传输(Captions are not streamed until shown),减少带宽浪费;
- 使用NetworkInformation API获取初始带宽估计;
- 官方 Demo 应用升级为渐进式 Web 应用(PWA),可离线使用;
- 支持 TS 内容中的CEA 字幕;
- 支持TTML 与 VTT 区域(region)的精确表示;
- 构造
Player时不再强制要求传入 video 元素,新增attach()与detach()方法管理播放器与 video 元素的绑定关系; - Fetch 优先于 XHR发起网络请求;
- 网络请求支持中止(abort);
- 直播流可从偏离直播边缘的负偏移位置开始播放。
这些能力中的多项(如 TS 转封装、请求中止、字幕按需拉取)都依赖下文要讲的 API 变更,因此在升级时务必同步迁移应用代码。
HLS 起始时间配置移除:自动从分片提取
v2.2 中,对于起始时间不为 t=0 的 HLS VOD 内容,应用需要显式配置manifest.hls.defaultTimeOffset告知播放器正确的内容起始时间。该配置在 v2.4 中已被移除。
v2.4 会自动从分片(segments)本身提取 HLS 内容的起始时间,无需任何配置。如果你的应用仍在调用player.configure({manifest: {hls: {defaultTimeOffset: ...}}}),请在升级时删除这段配置。
这一能力背后的实现可参考 lib/media/presentation_timeline.js 中的setUserSeekStart(time)方法(由 lib/player.js 在解析到播放范围起始时间后调用),它设置用户可定义的 seek 范围起点,并仅用于 VOD 内容,与getSegmentAvailabilityStart()共同决定可用分片的起始边界。自动提取的起始时间最终会落到这一时间轴上。
文本解析插件(TextParser)API 变更
文本解析插件(TextParser)的 API 在 v2.4 中发生变化,所有应用自定义的文本解析插件都必须更新,v2.4 不提供向后兼容。
核心变化是:插件接收的数据类型由ArrayBuffer改为Uint8Array,这一改动让库内部得以优化并避免缓冲区拷贝。
// v2.2 /** * @param {!ArrayBuffer} data * @param {shakaExtern.TextParser.TimeContext} timeContext * @return {!Array.<!shaka.text.Cue>} */ MyTextParser.prototype.parseMedia = function(data, timeContext) {}; // v2.4 /** * @param {!Uint8Array} data * @param {shakaExtern.TextParser.TimeContext} timeContext * @return {!Array.<!shaka.text.Cue>} */ MyTextParser.prototype.parseMedia = function(data, timeContext) {};同时,timeContext中的segmentStart字段变为可空(nullable)。当该信息不可用时——例如在 HLS 场景下——你的插件会收到null作为segmentStart,需要做好空值处理:
MyTextParser.prototype.parseMedia = function(data, timeContext) { if (timeContext.segmentStart == null) { // HLS 场景下起始时间可能不可用,走兜底逻辑 } // ... };产生区域(region)信息的文本解析插件还需要改用新的shaka.text.CueRegion类。新结构能够更精确地同时表示 TTML 与 VTT 的区域信息。接口细节可参考 externs/shaka/text.js 中的shakaExtern.TextParser.prototype.parseInit与shakaExtern.TextParser.prototype.parseMedia定义。
文本显示插件(TextDisplayer)API 变更
文本显示插件(TextDisplayer)的 API 同样发生了变更。所有应用自定义的 TextDisplayer 插件都必须更新,v2.4 不提供向后兼容。
插件需要适配shakaExtern.CueRegion结构的调整——新的结构支持更精确地表示 TTML 与 VTT 区域。例如,区域单位目前支持shaka.text.CueRegion.units.LINES(行单位)与PERCENTAGE(百分比单位)两种取值,CEA-708 窗口定位就是通过把锚点相关值映射到CueRegion实现的(参见 lib/cea/cea708_window.js)。如果你的自定义显示插件直接操作旧的区域字段,需要按新结构重新实现。
区域结构的完整定义可查看shakaExtern.CueRegion(位于 externs/shaka/text.js)。
离线存储(Offline Storage)API 变更
v2.2 中,shaka.offline.Storage的remove()方法接收一个StoredContent实例作为参数;v2.4 改为接收StoredContent中的offlineUri字段。
// v2.2: 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 中正式移除。所有使用离线存储的应用都必须更新到新 API。
从源码看,新实现位于 lib/offline/storage.js:remove(contentUri)接收字符串形式的contentUri,随后通过shaka.offline.OfflineUri.parse(contentUri)解析并校验;若 URI 不合法或不是 manifest 类型,会抛出MALFORMED_OFFLINE_URI错误。删除内容时还会尝试释放对应的 DRM license。
流失败后的重试机制:从配置项到回调函数
v2.1.3 引入了streaming.infiniteRetriesForLiveStreams配置来控制直播流的重试行为;v2.2 又增加了更灵活的回调机制来为所有类型的流指定重试策略。到 v2.4,该配置已被移除(v2.2 标记废弃、v2.3 正式删除),取而代之的是streaming.failureCallback。
// v2.1 —— 直播流无限重试(默认行为) player.configure({ streaming: { infiniteRetriesForLiveStreams: true // the default } }); // v2.4 —— 等价写法:回调里判断直播并重试 player.configure({ streaming: { failureCallback: function(error) { // Always retry live streams: if (player.isLive()) player.retryStreaming(); } } }); // v2.1 —— 直播流不重试 player.configure({ streaming: { infiniteRetriesForLiveStreams: false // do not retry live } }); // v2.4 —— 等价写法:回调里什么都不做,停止尝试继续流式传输 player.configure({ streaming: { failureCallback: function(error) { // Do nothing, and we will stop trying to stream the content. } } });灵活决策:结合 isLive()、错误码与其他条件
player.retryStreaming()可在失败后随时调用以重试。你可以基于player.isLive()、error.code或任何其他条件决定是否重试。由于retryStreaming()可在任意时刻调用,你甚至可以把决策推迟到收到用户反馈、浏览器恢复联网等时机。
几个典型回调示例:
function neverRetryCallback(error) {} function alwaysRetryCallback(error) { 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事件而不是 failure 回调,可以通过event.preventDefault()完全绕过回调:
player.addEventListener('error', function(event) { // Custom logic for error events if (player.isLive() && event.error.code == shaka.util.Error.Code.BAD_HTTP_STATUS) { player.retryStreaming(); } // Do not invoke the failure callback for this event event.preventDefault(); });从源码看,retryStreaming()定义于 lib/player.js,其默认重试延迟为 0.1 秒,并且只有在当前加载模式为MEDIA_SOURCE时才真正生效(否则返回false)。failure 回调的实际触发点位于 lib/media/streaming_engine.js,即流媒体引擎在遭遇失败并经过退避等待(backoff)后,会调用配置中的failureCallback,并把错误对象传给它;回调自身抛出的异常会被捕获并记入日志,避免干扰播放器状态机。
语言与角色选择
在 v2.1 引入的语言选择方法基础上,v2.4 新增了面向角色(role)的方法: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; } }这种模式非常适合“根据某种偏好规则自动挑选一条带指定角色(如注释音轨、导演评论)的音轨或字幕轨”的场景,例如在 UI 中列出所有可选项供用户勾选后调用selectAudioLanguage(language, role)/selectTextLanguage(language, role)完成切换。
NetworkingEngine API 变更:request() 返回可中止操作
v2.2 中,shaka.net.NetworkingEngine的request()方法直接返回一个 Promise;v2.4 改为返回shakaExtern.IAbortableOperation实例,该实例内含一个 Promise。
所有通过NetworkingEngine发起应用级请求的代码 SHOULD 更新到新 API(旧 API 的支持将在 v2.5 中移除):
// v2.2: player.getNetworkingEngine().request(type, request).then((response) => { // ... }); // v2.4: let operation = player.getNetworkingEngine().request(type, request); // Use operation.promise to get the response. operation.promise.then((response) => { // ... }); // The operation can also be aborted on some condition. onSomeOtherCondition(() => { operation.abort(); });为平滑过渡,v2.4 的发布版本对request()的返回值额外提供了.then与.catch方法,实现向后兼容。
IAbortableOperation的推荐实现是工具类shaka.util.AbortableOperation(见 lib/util/abortable_operation.js)。它的构造函数接收底层操作的 Promise 与onAbort回调,并对外暴露promise、aborted与abort()。其中abort()并非“取消”(不撤销已完成的工作),只是停止后续工作;被中止的操作应以OPERATION_ABORTED错误码拒绝其 Promise。该工具类还提供了failed()、aborted()、completed()、notAbortable()等静态工厂方法与combine()、chain()等组合原语,方便网络插件与上层逻辑复用。
网络 scheme 插件 API 变更
v2.4 同样调整了网络 scheme 插件(负责特定 URI scheme 的请求)的 API。所有应用级网络 scheme 插件 SHOULD 更新到新 API(旧 API 支持将在 v2.5 中移除)。
变化有两处:
- 插件现在返回
shakaExtern.IAbortableOperation实例(推荐用shaka.util.AbortableOperation封装); - 新增一个参数用于标识请求类型(requestType)。
// v2.2 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; // Use this if you have a need for it. Ignore it otherwise. if (requestType == shaka.net.NetworkingEngine.RequestType.MANIFEST) { // ... } else { // ... } // ... }); const abort = () => { // Abort the operation. // ... // Reject the 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);requestType的枚举值定义于 lib/net/networking_engine.js 的shaka.net.NetworkingEngine.RequestType,覆盖MANIFEST、SEGMENT、LICENSE、TIMING等常用类型,另有更细粒度的AdvancedRequestType。registerScheme(scheme, plugin, priority, progressSupport)也支持通过priority调整同一 scheme 下多个插件的优先级,并在 v2.4 中增加了progressSupport参数以支持请求进度上报。
Manifest 解析器插件 API 变更
shaka.media.PresentationTimeline的 API 发生了变化,使用以下方法的 ManifestParser 插件必须更新:
setAvailabilityStart()更名为setUserSeekStart();notifySegments()的参数由“周期起始时间 + 引用数组”改为“引用数组 + 布尔值isFirstPeriod”。
其中setUserSeekStart(time)的当前实现位于 lib/media/presentation_timeline.js,仅用于 VOD 内容,其值会参与getSegmentAvailabilityStart()的计算(取userSeekStart_与动态可用窗口起点中较大者),直接影响播放器允许 seek 的最小时间点。
升级自检清单
对照下表逐项检查你的应用,可避免在 v2.4 升级后踩坑:
| 变更点 | 旧用法(v2.2) | 新用法(v2.4) | 是否破坏性 |
|---|---|---|---|
| HLS 起始时间 | manifest.hls.defaultTimeOffset配置 | 自动从分片提取,删除配置 | 是 |
| TextParser 插件 | parseMedia(data /* ArrayBuffer */, timeContext) | parseMedia(data /* Uint8Array */, timeContext),segmentStart可为null,区域改用CueRegion | 是,无兼容 |
| TextDisplayer 插件 | 旧CueRegion结构 | 新CueRegion结构(支持 TTML/VTT 区域) | 是,无兼容 |
| 离线存储删除 | storage.remove(storedContent) | storage.remove(storedContent.offlineUri) | 是 |
| 直播流重试 | streaming.infiniteRetriesForLiveStreams | streaming.failureCallback+player.retryStreaming() | 是(配置已移除) |
| 语言/角色选择 | selectAudioLanguage(language)等 | 新增getAudioLanguagesAndRoles()/getTextLanguagesAndRoles(),选择方法支持 role 参数 | 否(新增) |
| NetworkingEngine.request | 返回 Promise | 返回IAbortableOperation(含.promise,可abort()) | 建议更新(v2.5 移除旧支持) |
| 网络 scheme 插件 | (uri, request)返回 Promise | (uri, request, requestType)返回AbortableOperation | 建议更新(v2.5 移除旧支持) |
| Manifest 解析器 | setAvailabilityStart()、旧notifySegments() | setUserSeekStart()、notifySegments(refs, isFirstPeriod) | 是(解析器插件) |
其中标注“无兼容”的两处(TextParser 与 TextDisplayer 插件 API)破坏性最强,应用必须同步改写插件代码;标注“建议更新”的两处(NetworkingEngine 与网络 scheme 插件)在 v2.4 中仍带过渡兼容层,但需在 v2.5 之前完成迁移。按此清单逐项处理后,你的应用即可平稳运行在 Shaka Player v2.4 之上。
【免费下载链接】shaka-playerJavaScript player library / DASH & HLS client / MSE-EME player项目地址: https://gitcode.com/GitHub_Trending/sh/shaka-player
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考