UNPKG

p2p-media-loader-shaka

Version:

P2P Media Loader Shaka Player integration

377 lines (376 loc) 14.5 kB
var L = Object.defineProperty; var T = (a, e, t) => e in a ? L(a, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : a[e] = t; var g = (a, e, t) => T(a, typeof e != "symbol" ? e + "" : e, t); import { debug as C, CoreRequestError as H, Core as q } from "p2p-media-loader-core"; class x { constructor(e, t) { g(this, "debug", C("p2pml-shaka:manifest-parser")); g(this, "isHls"); g(this, "segmentManager"); g(this, "player"); this.shaka = e, this.originalManifestParser = t, this.isHls = this.originalManifestParser instanceof e.hls.HlsParser; } configure(e) { return this.originalManifestParser.configure(e); } banLocation(e) { return this.originalManifestParser.banLocation(e); } onInitialVariantChosen(e) { return this.originalManifestParser.onInitialVariantChosen(e); } setP2PMediaLoaderData(e) { e && (this.segmentManager = e.segmentManager, this.player = e.player, e.streamInfo.protocol = this.isHls ? "hls" : "dash"); } async start(e, t) { const { p2pml: n } = t.networkingEngine; this.setP2PMediaLoaderData(n); const r = await this.originalManifestParser.start(e, t); return n && (this.isHls && this.hookHlsStreamMediaSequenceTimeMaps(r.variants), this.processStreams(r.variants)), r; } stop() { return this.originalManifestParser.stop(); } update() { return this.originalManifestParser.update(); } setMediaElement(e) { return this.originalManifestParser.setMediaElement(e); } onExpirationUpdated(e, t) { return this.originalManifestParser.onExpirationUpdated(e, t); } processStreams(e) { const { segmentManager: t } = this; if (!t) return; const n = /* @__PURE__ */ new Set(), r = (h, d, s) => (this.hookSegmentIndex(h), t.setStream(h, d, s), n.add(h.id), !0); let i = 0, c = 0; for (const h of e) { const { video: d, audio: s } = h; d && !n.has(d.id) && r(d, "main", i++), s && !n.has(s.id) && r(s, d ? "secondary" : "main", c++); } } hookSegmentIndex(e) { const { segmentManager: t } = this; if (!t) return; const n = (i, c = !1) => { let h, d, s = null; const o = i.get, l = (u) => { var v, P; const m = o.call(i, u); if (m === s || !((v = this.player) != null && v.isLive()) && e.isSegmentIndexAlreadyRead) return m; s = m, i.get = o; try { const f = b(i, this.shaka); if (!f) throw new Error("Segment references not found"); const E = f[0], k = f[f.length - 1]; if (E === h && k === d) return m; h = E, d = k, t.updateStreamSegments(e, f), e.isSegmentIndexAlreadyRead = !0, this.debug(`Stream ${e.id} is updated`); } catch { } finally { e.isSegmentIndexAlreadyRead && !((P = this.player) != null && P.isLive()) && c || (i.get = l); } return m; }; i.get = l; }; if (e.segmentIndex) return void n(e.segmentIndex); const r = e.createSegmentIndex; e.createSegmentIndex = async () => { const i = await r.call(e); return e.segmentIndex && n(e.segmentIndex, !0), i; }; } hookHlsStreamMediaSequenceTimeMaps(e) { var d; const t = I(this.originalManifestParser); let n, r; const i = ["video", "audio", "text", "image"]; for (const s of t) i.every((o) => s.has(o)) && (n = s.get("video"), r = s.get("audio")); if (n && r) { for (const s of e) { const { video: o, audio: l } = s; o && (o.mediaSequenceTimeMap = n), l && (l.mediaSequenceTimeMap = n); } return; } const c = t.find((s) => { var l; const o = s.values().next().value; return typeof o == "object" && ((l = o == null ? void 0 : o.streams) == null ? void 0 : l.createSegmentIndex); }); if (!c) return; const h = [...c.values()]; for (const s of h) { if ((d = s == null ? void 0 : s.stream) != null && d.mediaSequenceTimeMap) continue; const o = I(s).find((l) => { const [u, m] = l.entries().next().value ?? []; return typeof u == "number" && typeof m == "number"; }); o && (s.stream.mediaSequenceTimeMap = o); } } } class U extends x { constructor(e) { super(e, new e.hls.HlsParser()); } } class A extends x { constructor(e) { super(e, new e.dash.DashParser()); } } function b(a, e) { for (const t in a) { if (Array.isArray(a[t]) && a[t].length > 0 && a[t][0] instanceof e.media.SegmentReference) return a[t]; if (typeof a[t] == "object") { const n = b(a[t], e); if (n) return n; } } return null; } function I(a) { return Object.values(a).filter((e) => e instanceof Map); } function y({ segmentReference: a, externalId: e, runtimeId: t }) { const { byteRange: n, url: r, startTime: i, endTime: c } = R(a); return { runtimeId: t ?? M(r, n), externalId: e, byteRange: n, url: r, startTime: i, endTime: c }; } function w(a) { const { url: e, byteRange: t } = R(a); return M(e, t); } function M(a, e) { if (!e) return a; const t = typeof e == "string" ? function(n) { if (!(n != null && n.includes("bytes="))) return; const r = n.split("=")[1].split("-").map((h) => parseInt(h)), [i, c] = r; return { start: i, end: c }; }(e) : e; return t ? `${a}|${t.start}-${t.end}` : a; } function R(a) { const e = a.getUris(), t = e[1] ?? e[0], n = a.getStartByte(), r = a.getEndByte() ?? void 0; return { byteRange: r !== void 0 ? { start: n, end: r } : void 0, url: t, startTime: a.getStartTime(), endTime: a.getEndTime() }; } class N { constructor(e, t) { g(this, "core"); g(this, "streamInfo"); this.core = t, this.streamInfo = e; } setStream(e, t, n = -1) { this.core.addStreamIfNoneExists({ runtimeId: e.id.toString(), type: t, index: n, shakaStream: e }), e.segmentIndex && this.updateStreamSegments(e); } updateStreamSegments(e, t) { const n = this.core.getStream(e.id.toString()); if (!n) return; const { segmentIndex: r } = n.shakaStream; if (!t && r) try { t = [...r]; } catch { return; } t && (this.streamInfo.protocol === "hls" ? this.processHlsSegmentReferences(n, t) : this.processDashSegmentReferences(n, t)); } processDashSegmentReferences(e, t) { const n = new Set(e.segments.keys()), r = []; for (const i of t) { const c = Math.trunc(i.getStartTime() / 0.5), h = w(i); if (!e.segments.has(h)) { const d = y({ segmentReference: i, externalId: c, runtimeId: h }); r.push(d); } n.delete(h); } (r.length || n.size) && this.core.updateStream(e.runtimeId, r, n.values()); } processHlsSegmentReferences(e, t) { const { segments: n } = e, r = function(s) { const { shakaStream: o } = s, l = o.mediaSequenceTimeMap; if (!l) return; const u = l.keys().next().value; return u !== void 0 ? u + l.size - 1 : void 0; }(e), i = []; if (n.size === 0) { const s = r === void 0 ? 0 : r - t.length + 1; for (const [o, l] of t.entries()) { const u = y({ segmentReference: l, externalId: s + o }); i.push(u); } return void this.core.updateStream(e.runtimeId, i); } if (!r) return; let c = r; for (const s of function* (o) { for (let l = o.length - 1; l >= 0; l--) yield o[l]; }(t)) { const o = w(s); if (n.has(o)) break; const l = y({ runtimeId: o, segmentReference: s, externalId: c }); i.push(l), c--; } i.reverse(); const h = [], d = i.length; for (const s of function* (o, l) { let u = 0; for (const m of o.values()) { if (u >= l) break; yield m, u++; } }(n, d)) h.push(s.runtimeId); (i.length || h.length) && this.core.updateStream(e.runtimeId, i, h); } } class B { constructor(e, t, n) { g(this, "loadArgs"); this.shaka = e, this.core = t, this.streamInfo = n; } defaultLoad() { return this.shaka.net.HttpFetchPlugin.parse(...this.loadArgs); } load(...e) { this.loadArgs = e; const { RequestType: t } = this.shaka.net.NetworkingEngine, [n, r, i] = e; if (i === t.SEGMENT) return this.loadSegment(n, r); const c = this.defaultLoad(); return i === t.MANIFEST && this.handleManifestLoading(c.promise), c; } async handleManifestLoading(e) { if (!this.streamInfo.manifestResponseUrl) { const t = await e; this.setManifestResponseUrl(t.uri); } } loadSegment(e, t) { const n = t.headers.Range, r = M(e, n), i = this.core.isSegmentLoadable(r); return !this.core.hasSegment(r) || !i ? this.defaultLoad() : new this.shaka.util.AbortableOperation((async () => { const { request: c, callbacks: h } = function() { let d, s; return { request: new Promise((l, u) => { d = l, s = u; }), callbacks: { onSuccess: d, onError: s } }; }(); this.core.loadSegment(r, h); try { const { data: d, bandwidth: s } = await c; return { data: d, headers: {}, originalRequest: t, uri: e, originalUri: e, timeMs: D(s, d.byteLength) }; } catch (d) { if (d instanceof H) { const { Error: s } = this.shaka.util; if (d.type === "aborted") throw new s(s.Severity.RECOVERABLE, s.Category.NETWORK, this.shaka.util.Error.Code.OPERATION_ABORTED); } throw d; } })(), () => (this.core.abortSegmentLoading(r), Promise.resolve())); } setManifestResponseUrl(e) { this.streamInfo.manifestResponseUrl = e, this.core.setManifestResponseUrl(e); } } function D(a, e) { const t = 8 * e; return 1e3 * Math.round(t / a); } class p { constructor(e, t = window.shaka) { g(this, "player"); g(this, "shaka"); g(this, "streamInfo", {}); g(this, "core"); g(this, "segmentManager"); g(this, "requestFilter"); g(this, "updatePlayerEventHandlers", (e) => { const { player: t } = this; if (!t) return; const n = t.getNetworkingEngine(); if (n) if (e === "register") { const i = { player: t, shaka: this.shaka, core: this.core, streamInfo: this.streamInfo, segmentManager: this.segmentManager }; this.requestFilter = (c, h) => { h.p2pml = i; }, n.p2pml = i, n.registerRequestFilter(this.requestFilter); } else n.p2pml = void 0, this.requestFilter && n.unregisterRequestFilter(this.requestFilter); const r = e === "register" ? "addEventListener" : "removeEventListener"; t[r]("loaded", this.handlePlayerLoaded), t[r]("loading", this.destroyCurrentStreamContext), t[r]("unloading", this.handlePlayerUnloading), t[r]("adaptation", this.onVariantChanged), t[r]("variantchanged", this.onVariantChanged); }); g(this, "onVariantChanged", () => { if (!this.player) return; const e = this.player.getVariantTracks().find((t) => t.active); e && this.core.setActiveLevelBitrate(e.bandwidth); }); g(this, "handlePlayerLoaded", () => { this.player && (this.core.setIsLive(this.player.isLive()), this.updateMediaElementEventHandlers("register")); }); g(this, "handlePlayerUnloading", () => { this.destroyCurrentStreamContext(), this.updateMediaElementEventHandlers("unregister"); }); g(this, "destroyCurrentStreamContext", () => { this.streamInfo.protocol = void 0, this.streamInfo.manifestResponseUrl = void 0, this.core.destroy(); }); g(this, "updateMediaElementEventHandlers", (e) => { var r; const t = (r = this.player) == null ? void 0 : r.getMediaElement(); if (!t) return; const n = e === "register" ? "addEventListener" : "removeEventListener"; t[n]("timeupdate", this.handlePlaybackUpdate), t[n]("ratechange", this.handlePlaybackUpdate), t[n]("seeking", this.handlePlaybackUpdate); }); g(this, "handlePlaybackUpdate", (e) => { const t = e.target; this.core.updatePlayback(t.currentTime, t.playbackRate); }); S(t), this.shaka = t, this.core = new q(e == null ? void 0 : e.core), this.segmentManager = new N(this.streamInfo, this.core); } bindShakaPlayer(e) { this.player !== e && (this.player && this.destroy(), this.player = e, this.player.configure("manifest.defaultPresentationDelay", 25), this.player.configure("manifest.dash.ignoreSuggestedPresentationDelay", !0), this.player.configure("streaming.useNativeHlsOnSafari", !1), this.updatePlayerEventHandlers("register")); } applyDynamicConfig(e) { e.core && this.core.applyDynamicConfig(e.core); } getConfig() { return { core: this.core.getConfig() }; } addEventListener(e, t) { this.core.addEventListener(e, t); } removeEventListener(e, t) { this.core.removeEventListener(e, t); } destroy() { this.destroyCurrentStreamContext(), this.updatePlayerEventHandlers("unregister"), this.updateMediaElementEventHandlers("unregister"), this.player = void 0; } static registerManifestParsers(e) { const t = () => new U(e), n = e.media.ManifestParser; n.registerParserByMime("application/dash+xml", () => new A(e)), n.registerParserByMime("application/x-mpegurl", t), n.registerParserByMime("application/vnd.apple.mpegurl", t); } static unregisterManifestParsers(e) { const t = e.media.ManifestParser; t.unregisterParserByMime("mpd"), t.unregisterParserByMime("application/dash+xml"), t.unregisterParserByMime("m3u8"), t.unregisterParserByMime("application/x-mpegurl"), t.unregisterParserByMime("application/vnd.apple.mpegurl"); } static registerNetworkingEngineSchemes(e) { const { NetworkingEngine: t } = e.net, n = (...r) => { const i = r[1], { p2pml: c } = i; return c ? new B(c.shaka, c.core, c.streamInfo).load(...r) : e.net.HttpFetchPlugin.parse(...r); }; t.registerScheme("http", n), t.registerScheme("https", n), t.registerScheme("data", n); } static unregisterNetworkingEngineSchemes(e) { const { NetworkingEngine: t } = e.net; t.unregisterScheme("http"), t.unregisterScheme("https"), t.unregisterScheme("data"); } static registerPlugins(e = window.shaka) { S(e), p.registerManifestParsers(e), p.registerNetworkingEngineSchemes(e); } static unregisterPlugins(e = window.shaka) { S(e), p.unregisterManifestParsers(e), p.unregisterNetworkingEngineSchemes(e); } } function S(a) { if (!a) throw new Error("shaka namespace is not defined in global scope and not passed as an argument to Shaka P2P engine constructor"); } export { p as ShakaP2PEngine }; //# sourceMappingURL=p2p-media-loader-shaka.es.min.js.map