diff --git a/src/renderer/apple-hls-old.js b/src/renderer/apple-hls-old.js new file mode 100644 index 00000000..fbbc38fc --- /dev/null +++ b/src/renderer/apple-hls-old.js @@ -0,0 +1,25453 @@ +/*! For license information please see hls.js.LICENSE.txt */ ! function Xy(Yy) { + const Jy = this; + var e, t; + e = this, t = function() { + "use strict"; + var P, e = e => e && e.Math === Math && e, + l = e("object" == typeof globalThis && globalThis) || e("object" == typeof window && window) || e("object" == typeof Jy && Jy) || e("object" == typeof global && global) || Function("return this")(); + class d { + constructor() { + this.keySize = null, this.ksRows = null, this.keySchedule = null, this.invKeySchedule = null, this.rcon = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54], this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)], this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)], this.sBox = new Uint32Array(256), this.invSBox = new Uint32Array(256), this.key = new Uint32Array(0), this.initTable() + } + uint8ArrayToUint32Array_(e) { + const t = new DataView(e), + i = Math.floor(t.byteLength / 4), + r = new Uint32Array(i); + for (let e = 0; e < i; e++) r[e] = t.getUint32(4 * e); + return r + } + initTable() { + const e = this["sBox"], + t = this["invSBox"], + i = this["subMix"], + r = i[0], + n = i[1], + s = i[2], + a = i[3], + o = this["invSubMix"], + l = o[0], + d = o[1], + u = o[2], + c = o[3], + h = new Uint32Array(256); + let p = 0, + f = 0, + m = 0; + for (m = 0; m < 256; m++) h[m] = m < 128 ? m << 1 : m << 1 ^ 283; + for (m = 0; m < 256; m++) { + var g = (g = f ^ f << 1 ^ f << 2 ^ f << 3 ^ f << 4) >>> 8 ^ 255 & g ^ 99; + e[p] = g, t[g] = p; + const o = h[p], + m = h[o], + v = h[m]; + var y = 257 * h[g] ^ 16843008 * g; + r[p] = y << 24 | y >>> 8, n[p] = y << 16 | y >>> 16, s[p] = y << 8 | y >>> 24, a[p] = y, y = 16843009 * v ^ 65537 * m ^ 257 * o ^ 16843008 * p, l[g] = y << 24 | y >>> 8, d[g] = y << 16 | y >>> 16, u[g] = y << 8 | y >>> 24, c[g] = y, p ? (p = o ^ h[h[h[v ^ o]]], f ^= h[h[f]]) : p = f = 1 + } + } + expandKey(e) { + var n = this.uint8ArrayToUint32Array_(e); + let t = !0, + i = 0; + for (; i < n.length && t;) t = n[i] === this.key[i], i++; + if (!t) { + this.key = n; + var s = this.keySize = n.length; + if (4 !== s && 6 !== s && 8 !== s) throw new Error("Invalid aes key size=" + s); + var a = this.ksRows = 4 * (s + 6 + 1); + let e, t; + const o = this.keySchedule = new Uint32Array(a), + l = this.invKeySchedule = new Uint32Array(a), + d = this.sBox, + u = this["rcon"], + c = this["invSubMix"], + h = c[0], + p = c[1], + f = c[2], + m = c[3]; + let i, r; + for (e = 0; e < a; e++) e < s ? i = o[e] = n[e] : (r = i, e % s == 0 ? (r = r << 8 | r >>> 24, r = d[r >>> 24] << 24 | d[r >>> 16 & 255] << 16 | d[r >>> 8 & 255] << 8 | d[255 & r], r ^= u[e / s | 0] << 24) : 6 < s && e % s == 4 && (r = d[r >>> 24] << 24 | d[r >>> 16 & 255] << 16 | d[r >>> 8 & 255] << 8 | d[255 & r]), o[e] = i = (o[e - s] ^ r) >>> 0); + for (t = 0; t < a; t++) e = a - t, r = 3 & t ? o[e] : o[e - 4], l[t] = t < 4 || e <= 4 ? r : h[d[r >>> 24]] ^ p[d[r >>> 16 & 255]] ^ f[d[r >>> 8 & 255]] ^ m[d[255 & r]], l[t] = l[t] >>> 0 + } + } + networkToHostOrderSwap(e) { + return e << 24 | (65280 & e) << 8 | (16711680 & e) >> 8 | e >>> 24 + } + decrypt(e, t, i) { + var r = this.keySize + 6, + n = this["invKeySchedule"], + s = this.invSBox, + a = this["invSubMix"], + o = a[0], + l = a[1], + d = a[2], + u = a[3], + i = this.uint8ArrayToUint32Array_(i); + let c = i[0], + h = i[1], + p = i[2], + f = i[3]; + const m = new Int32Array(e), + g = new Int32Array(m.length); + let y, v, S, b, T, E, I, w, A, O, k, C, D, M; + const x = this.networkToHostOrderSwap; + for (; t < m.length;) { + for (A = x(m[t]), O = x(m[t + 1]), k = x(m[t + 2]), C = x(m[t + 3]), T = A ^ n[0], E = C ^ n[1], I = k ^ n[2], w = O ^ n[3], D = 4, M = 1; M < r; M++) y = o[T >>> 24] ^ l[E >> 16 & 255] ^ d[I >> 8 & 255] ^ u[255 & w] ^ n[D], v = o[E >>> 24] ^ l[I >> 16 & 255] ^ d[w >> 8 & 255] ^ u[255 & T] ^ n[D + 1], S = o[I >>> 24] ^ l[w >> 16 & 255] ^ d[T >> 8 & 255] ^ u[255 & E] ^ n[D + 2], b = o[w >>> 24] ^ l[T >> 16 & 255] ^ d[E >> 8 & 255] ^ u[255 & I] ^ n[D + 3], T = y, E = v, I = S, w = b, D += 4; + y = s[T >>> 24] << 24 ^ s[E >> 16 & 255] << 16 ^ s[I >> 8 & 255] << 8 ^ s[255 & w] ^ n[D], v = s[E >>> 24] << 24 ^ s[I >> 16 & 255] << 16 ^ s[w >> 8 & 255] << 8 ^ s[255 & T] ^ n[D + 1], S = s[I >>> 24] << 24 ^ s[w >> 16 & 255] << 16 ^ s[T >> 8 & 255] << 8 ^ s[255 & E] ^ n[D + 2], b = s[w >>> 24] << 24 ^ s[T >> 16 & 255] << 16 ^ s[E >> 8 & 255] << 8 ^ s[255 & I] ^ n[D + 3], D += 3, g[t] = x(y ^ c), g[t + 1] = x(b ^ h), g[t + 2] = x(S ^ p), g[t + 3] = x(v ^ f), c = A, h = O, p = k, f = C, t += 4 + } + return g.buffer + } + destroy() { + this.key = void 0, this.keySize = void 0, this.ksRows = void 0, this.sBox = void 0, this.invSBox = void 0, this.subMix = void 0, this.invSubMix = void 0, this.keySchedule = void 0, this.invKeySchedule = void 0, this.rcon = void 0 + } + } + class i { + constructor(e, t) { + this.rpc = e, this.logger = t, this.decrypt = (r, n, s, a, o) => t => { + const i = l.crypto; + if (null != o && o.useJSCrypto || null == i || !i.subtle) { + const s = new d; + var e; + s.expandKey(r); + const i = s.decrypt(a, 0, n); + e = o.plainTextLength ? i.slice(0, o.plainTextLength) : function(e) { + var t = new Uint8Array(e), + i = t[e.byteLength - 1], + r = e.byteLength - 1; + let n = 0; + if (1 <= i && i <= 16) + for (let e = r; e > r - i && t[e] === i; e--) n++; + return e = n === i ? e.slice(0, r - i + 1) : e + }(i), t(e, void 0, [e]) + } else i.subtle.importKey("raw", r, s, !1, ["decrypt"]).then(e => i.subtle.decrypt({ + name: s, + iv: n + }, e, a)).then(e => { + t(e, void 0, [e]) + }).catch(e => t(void 0, e)) + }, e.register("decrypt", this.decrypt) + } + }(vr = P = P || {}).MEDIA_ATTACHING = "hlsMediaAttaching", vr.MEDIA_ATTACHED = "hlsMediaAttached", vr.MEDIA_DETACHING = "hlsMediaDetaching", vr.MEDIA_DETACHED = "hlsMediaDetached", vr.BUFFER_CREATED = "hlsBufferCreated", vr.BUFFER_APPENDING = "hlsBufferAppending", vr.BUFFER_APPENDED = "hlsBufferAppended", vr.BUFFER_FLUSHED = "hlsBufferFlushed", vr.MANIFEST_LOADING = "hlsManifestLoading", vr.MANIFEST_LOADED = "hlsManifestLoaded", vr.MANIFEST_PARSED = "hlsManifestParsed", vr.LEVEL_SWITCHING = "hlsLevelSwitching", vr.LEVEL_SWITCHED = "hlsLevelSwitched", vr.LEVEL_LOADING = "hlsLevelLoading", vr.LEVEL_LOADED = "hlsLevelLoaded", vr.LEVEL_UPDATED = "hlsLevelUpdated", vr.LEVELS_CHANGED = "hlsLevelsChanged", vr.AUDIO_TRACKS_UPDATED = "hlsAudioTracksUpdated", vr.AUDIO_TRACK_SWITCH = "hlsAudioTrackSwitch", vr.AUDIO_TRACK_SWITCHED = "hlsAudioTrackSwitched", vr.AUDIO_TRACK_LOADED = "hlsAudioTrackLoaded", vr.SUBTITLE_TRACKS_UPDATED = "hlsSubtitleTracksUpdated", vr.SUBTITLE_TRACKS_CREATED = "hlsSubtitleTracksCreated", vr.SUBTITLE_TRACK_SWITCH = "hlsSubtitleTrackSwitch", vr.INLINE_STYLES_PARSED = "hlsInlineStylesParsed", vr.SESSION_DATA_COMPLETE = "hlsSessionDataComplete", vr.FRAG_LOADING = "hlsFragLoading", vr.FRAG_LOADED = "hlsFragLoaded", vr.FRAG_BUFFERED = "hlsFragBuffered", vr.FRAG_CHANGED = "hlsFragChanged", vr.INTERNAL_ERROR = "hlsInternalError", vr.ERROR = "hlsError", vr.DESTROYING = "hlsDestroying", vr.KEY_REQUEST_STARTED = "hlsKeyRequestStarted", vr.LICENSE_CHALLENGE_CREATED = "hlsLicenseChallengeCreated", vr.LICENSE_RELEASED = "hlsLicenseReleased", vr.KEY_LOADED = "hlsKeyLoaded", vr.UNRESOLVED_URI_LOADING = "hlsUnresolvedUriLoading", vr.DESIRED_RATE_CHANGED = "hlsDesiredRateChanged", vr.PLAYER_STATE_CHANGE = "hlsPlayerStateChange", vr.SEEKING = "hlsSeeking", vr.SEEKED = "hlsSeeked", vr.STALLED = "hlsStalled", vr.RESUME_FROM_STALL = "hlsResumeFromStall", vr.READY_FOR_NEXT_ITEM = "hlsReadyForNextItem", vr.ITEM_TRANSITIONED = "hlsItemTransitioned", vr.ITEM_EVICTED = "hlsItemEvicted", vr.DATERANGE_UPDATED = "hlsDaterangeUpdated"; + var v, x = P; + (dl = v = v || {}).FRAG_PARSING_INIT_SEGMENT = "hlsFragParsingInitSegment", dl.FRAG_PARSING_DATA = "hlsFragParsingData", dl.FRAG_PARSED = "hlsFragParsed", dl.INIT_PTS_FOUND = "hlsInitPtsFound"; + class p extends Error { + constructor(e, t, i, r, n) { + super(r), this.type = e, this.details = t, this.fatal = i, this.reason = r, this.response = n, this.handled = !1 + } + } + class R extends p { + constructor(e, t, i, r, n) { + super(e, t, i, r, n), this.response = n + } + } + const $ = { + PlaylistNotReceived: { + code: -12884, + text: "Playlist not received" + }, + CryptResponseReceivedSlowly: { + code: -16833, + text: "Crypt key received slowly" + }, + LivePlaylistUpdateError: { + code: -12888, + text: "Live playlist not updated" + }, + NoResponseFromMediaRequest: { + code: -12889, + text: "No response for fragment" + }, + IncompatibleAsset: { + code: -12927, + text: "IncompatibleAsset" + }, + CorruptStream: { + code: -16041, + text: "Corrupt fragment" + }, + InternalError: { + code: -12645, + text: "InternalException" + }, + CantSwitchInTime: { + code: -12644, + text: "CantSwitchInTime" + }, + VideoDecoderBadDataErr: { + code: -12909, + text: "Buffer error" + }, + InsufficientDataAvailable: { + code: -12928, + text: "Incomplete data" + }, + AllocationFailed: { + code: -12862, + text: "AllocationFailed" + }, + PlaylistErrorMissingEXTM3U: { + code: -12269, + text: "Response doesnt have #EXTM3U tag" + }, + PlaylistErrorInvalidEntry: { + code: -12264, + text: "Invalid entry" + }, + PlaylistErrorBadTargetDuration: { + code: -12271, + text: "Invalid targetduration" + }, + NoValidAlternates: { + code: -12925, + text: "No valid alternates" + }, + FormatError: { + code: -12642, + text: "Incorrect playlist format" + }, + UnsupportedKeySystemError: { + code: -6e4, + text: "Unsupported Key System" + }, + EmptyLoadSourceError: { + code: -60001, + text: "Empty loadSource url" + }, + UndefinedItemIdError: { + code: -60002, + text: "Undefined itemId" + }, + ManifestParseError: { + code: -60003, + text: "Manifest parse error" + }, + DemuxWorkerError: { + code: -60004, + text: "Demux worker error" + }, + DecryptWorkerError: { + code: -60005, + text: "Decrypt worker error" + }, + OutOfRangeSeekError: { + code: -60006, + text: "Seeked out of playable range" + }, + ExceptionInKeyLoadError: { + code: -60007, + text: "Exception in Key load" + }, + FragmentAbortError: { + code: -60008, + text: "Fragment abort error" + }, + ManifestTimeoutError: { + code: -60009, + text: "Manifest Timeout Error" + }, + PlaylistTimeoutError: { + code: -60010, + text: "Playlist Timeout Error" + }, + FragmentTimeoutError: { + code: -60011, + text: "Fragment Timeout Error" + }, + IncompleteSessionData: { + code: -60012, + text: "Session data not complete after loading all items" + }, + SessionDataLoadTimeout: { + code: -60013, + text: "Session data load timeout" + }, + FailedDemuxerSanityCheck: { + code: -60014, + text: "Failed demuxer sanity check" + }, + InvalidADTSSamplingIndex: { + code: -60015, + text: "Invalid ADTS sampling index" + }, + DemuxerNotFound: { + code: -60016, + text: "No demux matching with content found" + }, + InvalidAC3Magic: { + code: -60029, + text: "Invalid ac-3 magic" + }, + InvalidInitTimestamp: { + code: -60017, + text: "Invalid initPTS or initDTS" + }, + NoAVSamplesFound: { + code: -60018, + text: "no audio/video samples found" + }, + NoTSSyncByteFound: { + code: -60019, + text: "TS packet did not start with 0x47" + }, + PESDidNotStartWithADTS: { + code: -60020, + text: "AAC PES did not start with ADTS header" + }, + NoADTSHeaderInPES: { + code: -60021, + text: "No ADTS header found in AAC PES" + }, + InvalidDolbyAudioMagic: { + code: -60022, + text: "Invalid dolby audio magic" + }, + FailedToAllocateVideoMdat: { + code: -60023, + text: "Fail allocating video mdat" + }, + FailedToAllocateAudioMdat: { + code: -60024, + text: "Fail allocating audio mdat" + }, + InsufficientEC3Data: { + code: -60025, + text: "Error parsing ec-3, not enough data" + }, + InvalidEC3Magic: { + code: -60026, + text: "Invalid ec-3 magic" + }, + ReservedStreamType: { + code: -60027, + text: "Reserved stream type" + }, + InsufficientAC3Data: { + code: -60028, + text: "error parsing ac-3, not enough data" + }, + InvalidAC3SamplingRateCode: { + code: -60030, + text: "Invalid ac-3 samplingRateCode" + }, + PlaylistErrorInvalidEXTXDEFINE: { + code: -61e3, + text: "Encountered undefined/not imported EXT-X-DEFINE property" + }, + PlaylistErrorMissingImportReference: { + code: -61001, + text: "IMPORT references variable not in master playlist and/or NAME" + }, + PlaylistErrorInvalidSERVERURI: { + code: -61002, + text: "Encountered undefined/invalid SERVER-URI attribute for EXT-X-CONTENT-STEERING tag" + }, + PlaylistErrorInvalidPATHWAYID: { + code: -61003, + text: "Encountered invalid PATHWAY-ID attribute for EXT-X-CONTENT-STEERING tag" + }, + PlaylistErrorInvalidSCORE: { + code: -61004, + text: "Encountered negative/non-number SCORE property" + }, + KeySystemFailedToUpdateSession: { + code: -62e3, + text: "KeySystem: Promise Rejected while updating session" + }, + KeySystemFailedToGenerateLicenseRenewal: { + code: -62001, + text: "KeySystem: Failed to generate license renewal" + }, + KeySystemFailedToGenerateLicenseRequest: { + code: -62002, + text: "KeySystem: Failed to generate license request" + }, + KeySystemAbort: { + code: -62003, + text: "KeySystem: Aborted" + }, + KeySystemUnexpectedStateTransition: { + code: -62004, + text: "KeySystem: Unexpected state transition" + }, + KeySystemUnexpectedState: { + code: -62005, + text: "KeySystem: Unexpected state" + }, + KeySystemCDMUnknownError: { + code: -62006, + text: "KeySystem: Unknown error from CDM" + }, + KeySystemRequestTimedOut: { + code: -62007, + text: "Key request timed out" + }, + KeySystemUnexpectedMETHOD: { + code: -62008, + text: "Unexpected METHOD attribute" + }, + KeySystemUnmatchedString: { + code: -62009, + text: "KeySystem: string does not match" + }, + KeySystemInternalError: { + code: -62010, + text: "KeySystem: internal-error" + }, + KeySystemOutputRestricted: { + code: -62011, + text: "KeySystem: output-restricted" + }, + KeySystemSetupError: { + code: -62012, + text: "KeySystem: setup error" + }, + KeySystemFailedToInitialize: { + code: -62013, + text: "KeySystem: could not initialize" + }, + KeySystemFailedToCreateSession: { + code: -62014, + text: "KeySystem: could not create session" + }, + KeySystemUndefinedNavigator: { + code: -62015, + text: "KeySystem: navigator undefined" + }, + KeySystemNoKeySystemsToTry: { + code: -62016, + text: "KeySystem: no key systems to try" + }, + KeySystemNoConstructor: { + code: -62017, + text: "KeySystem: No constructor" + }, + KeySystemNoKeySystemAccess: { + code: -62018, + text: "KeySystem: No KeySystemAccess" + }, + KeySystemCertificateLoadError: { + code: -62019, + text: "KeySystem: Certificate Load Error" + } + }, + o = "networkError", + L = "mediaError", + s = "otherError", + _ = "manifestParsingError", + f = "manifestIncompatibleCodecsError", + N = "levelLoadError", + n = "bufferAppendError", + r = "internalException"; + class V extends p { + constructor(e, t, i) { + super(s, r, e, t, i) + } + } + class D extends p { + constructor(e, t, i) { + super(L, "fragParsingError", e, t, i) + } + } + class F extends p { + constructor(e, t, i, r) { + super("muxError", "remuxAllocError", e, t, i), this.bytes = r + } + } + + function S(e) { + return e.baseTime / e.timescale + } + + function B(e, t) { + return { + baseTime: Math.floor(e * t), + timescale: t + } + } + + function g(e, t) { + return S(e) < S(t) ? e : t + } + + function y(e, t) { + return S(e) > S(t) ? e : t + } + + function b(e, t) { + return S(e) - S(t) + } + var t = void 0 !== l.Buffer ? require("events").EventEmitter : class { + constructor() { + this.eventMap = {} + } + _on(e, t, i = !1) { + return null == this.eventMap[e] && (this.eventMap[e] = []), i ? this.eventMap[e].splice(0, 0, t) : this.eventMap[e].push(t), this + } + _off(e, t) { + return null != this.eventMap[e] && (this.eventMap[e] = this.eventMap[e].filter(e => e.listener !== t.listener), 0 === this.eventMap[e].length && delete this.eventMap[e]), this + } + on(e, t) { + return this._on(e, { + listener: t, + once: !1 + }) + } + off(e, t) { + return this._off(e, { + listener: t + }) + } + addListener(e, t) { + return this.on(e, t) + } + once(e, t) { + return this._on(e, { + listener: t, + once: !0 + }) + } + removeListener(e, t) { + return this.off(e, t) + } + removeAllListeners(e) { + return delete this.eventMap[e], this + } + setMaxListeners(e) { + return this + } + getMaxListeners() { + return 1 / 0 + } + listeners(e) { + return null == this.eventMap[e] ? [] : this.eventMap[e].map(e => e.listener) + } + rawListeners(e) { + return this.listeners(e) + } + emit(e, ...t) { + if (null == this.eventMap[e]) return !1; + let i = !1; + for (const r of this.eventMap[e]) { + try { + r.listener.apply(this, t) + } catch (e) {} + i = !0 + } + return i + } + listenerCount(e) { + return null == this.eventMap[e] ? 0 : this.eventMap[e].length + } + prependListener(e, t) { + return this._on(e, { + listener: t, + once: !1 + }, !0) + } + prependOnceListener(e, t) { + return this._on(e, { + listener: t, + once: !0 + }, !0) + } + eventNames() { + return Object.keys(this.eventMap) + } + }; + class a extends t { + trigger(e, t) { + this.emit(e, t) + } + } + + function E(e, t, i, r, n) { + let s, a, o, l; + const d = navigator.userAgent.toLowerCase(), + u = [96e3, 88200, 64e3, 48e3, 44100, 32e3, 24e3, 22050, 16e3, 12e3, 11025, 8e3, 7350]; + s = 1 + ((192 & t[i + 2]) >>> 6); + var c = (60 & t[i + 2]) >>> 2; + if (!(u.length - 1 < c)) return o = (1 & t[i + 2]) << 2, o |= (192 & t[i + 3]) >>> 6, /firefox/i.test(d) ? 6 <= c ? (s = 5, l = new Array(4), a = c - 3) : (s = 2, l = new Array(2)) : -1 !== d.indexOf("android") ? (s = 2, l = new Array(2)) : (s = 5, l = new Array(4), a = r && (-1 !== r.indexOf("mp4a.40.29") || -1 !== r.indexOf("mp4a.40.5")) || !r && 6 <= c ? c - 3 : ((r && -1 !== r.indexOf("mp4a.40.2") || !r && 1 == o) && (s = 2, l = new Array(2)), c)), l[0] = s << 3, l[0] |= (14 & c) >> 1, l[1] |= (1 & c) << 7, l[1] |= o << 3, 5 === s && (l[1] |= (14 & a) >> 1, l[2] = (1 & a) << 7, l[2] |= 8, l[3] = 0), { + esdsConfig: l, + samplerate: u[c], + channelCount: o, + segmentCodec: "aac", + codec: "mp4a.40." + s + }; { + const t = new D(!0, `invalid ADTS sampling index:${c}`, $.InvalidADTSSamplingIndex); + e.trigger(x.INTERNAL_ERROR, t) + } + } + class u { + constructor(e, t, i, r, n) { + this.observer = e, this.remuxer = t, this.config = i, this.typeSupported = r, this.logger = n + } + static probe(e, t) { + throw new Error("Method not implemented") + } + resetTimeStamp(e) {} + resetInitSegment(e, t, i, r) {} + destroy() {} + } + class c extends u { + constructor(e, t, i, r, n) { + super(e, t, i, r, n), this.observer = e, this.remuxer = t, this.config = i, this.typeSupported = r, this.logger = n, this.esRemuxer = t + } + } + class h { + constructor(e, t, i) { + this.observer = e, this.config = t, this.logger = i + } + resetInitSegment() {} + resetTimeStamp(e) {} + destroy() {} + } + let m, I; + var w = { + strToUtf8array: e => (m = m || new TextEncoder, m.encode(e)), + utf8arrayToStr: e => (I = I || new TextDecoder("utf-8"), I.decode(e)) + }, + A = { + strToUtf8array(e) { + e = l.Buffer.from(e, "utf-8"); + return new Uint8Array(e.buffer, e.byteOffset, e.byteLength) + }, + utf8arrayToStr: e => l.Buffer.from(e).toString("utf-8") + }; + let O = { + strToUtf8array(e) { + const t = unescape(encodeURIComponent(e)), + i = new Uint8Array(t.length); + for (let e = 0; e < t.length; e++) i[e] = t.charCodeAt(e); + return i + }, + utf8arrayToStr: e => String.fromCharCode.apply(null, Array.from(e)) + }; + "undefined" != typeof TextEncoder && "undefined" != typeof TextDecoder ? O = w : "function" == typeof(null === (fl = l.Buffer) || void 0 === fl ? void 0 : fl.from) && (O = A); + const k = { + name: "ID3" + }; + class C { + constructor(e, t) { + this.logger = t, this._hasTimeStamp = !1, this._audioType = null, this._length = 0, this._frames = []; + let i, r, n, s, a = 0; + for (;;) + if (n = C.readUTF(e, a, 3), a += 3, "ID3" === n) { + this._minor = e[a++], this._revision = e[a++]; + const t = e[a++]; + if (128 & t && (this._unsynchronized = !0, this.logger.error(k, "id3 tag is unsynchronized")), 64 & t && (this._hasExtendedHeader = !0, this.logger.warn(k, "id3 tag has extended header")), i = C.readSynchSafeUint32(e.subarray(a, a + 4)), a += 4, r = a + i, this._hasExtendedHeader) { + const t = C.readSynchSafeUint32(e.subarray(a, a + 4)); + this.logger.warn(k, `id3 tag has ${t}-byte extended header. usually 6 or 10 bytes`), a += t + } + 2 < this.minor ? this._parseID3Frames(e, a, r) : this.logger.error(k, "[id3] doesn't support older than v2.3 tags"), a = r + } else { + if ("3DI" !== n) return a -= 3, void((s = a) && (this.hasTimeStamp || this.logger.warn(k, "ID3 tag found, but no timestamp"), this._length = s, this._payload = e.slice(0, s))); + a += 7 + } + } + static isHeader(e, t) { + return 73 === e[t] && 68 === e[t + 1] && 51 === e[t + 2] && e[t + 3] < 255 && e[t + 4] < 255 && e[t + 6] < 128 && e[t + 7] < 128 && e[t + 8] < 128 && e[t + 9] < 128 + } + static readSynchSafeUint32(e) { + return 2097152 * (127 & e[0]) + 16384 * (127 & e[1]) + 128 * (127 & e[2]) + (127 & e[3]) + } + static readUTF(e, t, i) { + let r = "", + n = t; + for (var s = t + i; r += String.fromCharCode(e[n++]), n < s;); + return r + } + isID3Frame(e, t) { + return e[t + 4] < 128 && e[t + 5] < 128 && e[t + 6] < 128 && e[t + 7] < 128 + } + decodeID3Frame(e) { + return "TXXX" === e.type ? this.decodeTxxxFrame(e) : "WXXX" === e.type ? this.decodeWxxxFrame(e) : "PRIV" === e.type ? this.decodePrivFrame(e) : "T" === e.type[0] ? this.decodeTextFrame(e) : { + key: e.type, + data: e.data + } + } + decodeTxxxFrame(e) { + if (!(e.size < 2) && 3 === e.data[0]) { + var t = 1, + i = this.id3utf8ArrayToStr(e.data.subarray(1)); + return t += i.length + 1, { + key: "TXXX", + description: i, + data: this.id3utf8ArrayToStr(e.data.subarray(t)) + } + } + } + decodeWxxxFrame(e) { + if (!(e.size < 2) && 3 === e.data[0]) { + var t = 1, + i = this.id3utf8ArrayToStr(e.data.subarray(1)); + return t += i.length + 1, { + key: "WXXX", + description: i, + data: O.utf8arrayToStr(e.data.subarray(t)) + } + } + } + decodeTextFrame(e) { + if (!(e.size < 2) && 3 === e.data[0]) { + var t = e.data.subarray(1); + return { + key: e.type, + data: this.id3utf8ArrayToStr(t) + } + } + } + decodePrivFrame(e) { + if (!(e.size < 2)) { + var t = this.id3utf8ArrayToStr(e.data); + return { + key: "PRIV", + info: t, + data: e.data.slice(t.length + 1) + } + } + } + _extractID3Frame(e, t, i, r, n) { + var s = r + i; + let a; + return s <= n ? a = { + type: t, + data: e.slice(r, s) + } : this.logger.error(k, `id3 frame ${t} size ${i} exceeded ${n}`), a + } + _parseID3Frames(e, t, i) { + let r, n, s, a; + for (; t + 8 <= i;) { + if (!this.isID3Frame(e, t)) return void this.logger.error(k, `[id3] illegal id3 frame @ offset ${t}. skip this id3 tag`); + if (r = C.readUTF(e, t, 4), t += 4, "" === r) return; + if (0 === (n = C.readSynchSafeUint32(e.subarray(t, t + 4)))) return; + t += 4, e[t++], e[t++], s = t; + var o = this._extractID3Frame(e, r, n, s, i); + if (o) { + const e = this.decodeID3Frame(o); + this._frames.push(e) + } + if ("PRIV" === r) + if (53 === n && "com.apple.streaming.transportStreamTimestamp" === C.readUTF(e, t, 44)) { + t += 44, t += 4; + const i = 1 & e[t++]; + this._hasTimeStamp = !0, a = ((e[t++] << 23) + (e[t++] << 15) + (e[t++] << 7) + e[t++]) / 45, i && (a += 47721858.84), a = Math.round(a), this._timeStamp = a + } else 45 <= n && "com.apple.streaming.audioDescription" === C.readUTF(e, t, 36) ? (t += 37, this._audioType = C.readUTF(e, t, 4), t += 4, t += n - 41) : t += n; + else t += n + } + } + id3utf8ArrayToStr(e) { + let t, i, r = "", + n = 0; + const s = e.length; + for (; n < s;) { + const s = e[n++]; + switch (s >> 4) { + case 0: + return r; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + r += String.fromCharCode(s); + break; + case 12: + case 13: + t = e[n++], r += String.fromCharCode((31 & s) << 6 | 63 & t); + break; + case 14: + t = e[n++], i = e[n++], r += String.fromCharCode((15 & s) << 12 | (63 & t) << 6 | (63 & i) << 0) + } + } + } + get hasTimeStamp() { + return this._hasTimeStamp + } + get timeStamp() { + return this._timeStamp + } + get audioType() { + return this._audioType + } + get length() { + return this._length + } + get payload() { + return this._payload + } + get frames() { + return this._frames + } + get minor() { + return this._minor + } + get revision() { + return this._revision + } + } + var M = C; + const U = { + name: "AACDemuxer" + }; + class K extends c { + resetInitSegment(e, t) { + this.audioConfig = void 0, this.audioTrack = void 0, this.duration = t + } + static probe(e, t) { + let i, r; + for (i = new M(e, t).length, r = Math.min(e.length - 1, i + 100); i < r; i++) + if (255 === e[i] && 240 == (246 & e[i + 1])) return !0; + return !1 + } + append(e, t, i, r, n) { + var s = new M(e, this.logger), + a = s.hasTimeStamp ? 90 * s.timeStamp : 9e4 * t; + let o, l, d, u, c, h, p, f, m, g; + for (s.length && (g = s.payload, s.frames.length && (m = s.frames), f = { + id3Samples: [{ + pts: a, + dts: a, + data: g, + frames: m + }], + inputTimescale: 9e4 + }), d = s.length, h = e.length; d < h - 1 && (255 !== e[d] || 240 != (246 & e[d + 1])); d++); + if (!this.audioConfig && (this.audioConfig = E(this.observer, e, d, void 0, this.logger), !this.audioConfig)) throw "failed to parse adts config"; + if (!this.audioTrack) { + const e = { + id: 258, + inputTimescale: 9e4, + timescale: NaN, + duration: this.duration, + encrypted: !1, + keyTagInfo: n + }, + t = { + len: 0, + sequenceNumber: 0, + esSamples: [] + }; + this.audioTrack = { + info: e, + parsingData: t, + type: "audio", + config: this.audioConfig + } + } + "zaac" !== s.audioType && "zach" !== s.audioType && "zacp" !== s.audioType || (this.audioTrack.info.encrypted = !0), l = 0; + for (var y = 9216e4 / this.audioConfig.samplerate; d + 5 < h && (u = 1 & e[d + 1] ? 7 : 9, o = (3 & e[d + 3]) << 11 | e[d + 4] << 3 | (224 & e[d + 5]) >>> 5, o -= u, 0 < o && d + u + o <= h);) + for (c = a + l * y, p = { + unit: e.subarray(d + u, d + u + o), + pts: c, + dts: c, + keyTagInfo: n + }, this.audioTrack.parsingData.esSamples.push(p), this.audioTrack.parsingData.len += o, d += o + u, l++; d < h - 1; d++) { + if (M.isHeader(e, d)) { + const t = new M(e.subarray(d), this.logger); + if (0 < t.length) { + d += t.length; + const e = t.hasTimeStamp ? 90 * t.timeStamp : a; + f.id3Samples.push({ + pts: e, + dts: e, + data: t.payload, + frames: t.frames + }) + } else this.logger.error(U, `[id3] invalid length ${h}`) + } + if (255 === e[d] && 240 == (246 & e[d + 1])) break + } + this.esRemuxer.remuxEsTracks(this.audioTrack, void 0, f, void 0, t, i, r, n) + } + } + class q { + bsReadAndUpdate(e, t, i) { + e = this.readBits(e, t, i); + return this.updateOffset(t, i), e + } + bsWriteAndUpdate(e, t, i, r) { + r = this.writeBits(e, t, i, r); + return this.updateOffset(t, i), r + } + bsSkip(e, t) { + this.updateOffset(e, t) + } + readBits(i, r, n) { + if (i && r) { + let t = r.byteOffset; + const s = r["usedBits"]; + if (!(8 <= s || 32 < s + n)) { + let e; + const a = new Uint32Array(1), + o = new Uint32Array(1), + l = new Uint8Array(1); + if (!(8 <= s || 32 < n)) { + if (s) { + const r = 8 - s, + a = n < r ? r - n : 0; + o[0] = 4278190080 >>> 32 - r, e = (i[t] & o[0]) >>> a, t += 1, n -= r + } + for (; 0 < n;) { + l[0] = i[t]; + const r = Math.min(n, 8), + s = 8 - r; + o[0] = 4278190080 >>> 24 + s << s, a[0] = (l[0] & o[0]) >> s, e = e ? e << r | a[0] : a[0], t += 1, n -= r + } + return e + } + } + } + } + writeBits(t, i, r, n) { + if (t && i) { + let e = i.byteOffset; + var i = i["usedBits"]; + if (!(8 <= i || 32 < i + r)) { + const s = new Uint32Array(1), + a = new Uint32Array(1), + o = new Uint32Array(1), + l = new Uint8Array(1); + for (s[0] = n, i && (a[0] = s[0] << 32 - r, o[0] = 4278190080, l[0] = (a[0] & o[0]) >>> 24 + i, t[e] &= ~(o[0] >>> 24 + i), t[e] |= l[0], e += 1, r -= 8 - i); 0 < r;) { + a[0] = s[0] << 32 - r, o[0] = 4278190080, l[0] = (a[0] & o[0]) >>> 24; + const d = r < 0 ? 8 - r : 0; + t[e] &= ~(o[0] >>> 24 >>> d << d), t[e] |= l[0], r -= 8, e += 1 + } + return 0 + } + } + } + updateOffset(e, t) { + var i, r; + !e || !t || 32 < e.usedBits + t || (i = e.usedBits % 8, r = Math.floor((i + t) / 8), t = (i + t) % 8, e.byteOffset += r, e.usedBits = t) + } + } + + function H(e, t) { + return 1536 / e.samplerate * t + } + + function j(e, t, i, r) { + let n; + if (i + 8 > t.length) return n = new D(!0, "error parsing ac-3, not enough data", $.InsufficientAC3Data), void e.trigger(x.INTERNAL_ERROR, n); + if (11 !== t[i] || 119 !== t[i + 1]) return n = new D(!0, "invalid ac-3 magic", $.InvalidAC3Magic), void e.trigger(x.INTERNAL_ERROR, n); + var s = t[i + 4] >> 6; + if (3 <= s) return n = new D(!0, `invalid ac-3 samplingRateCode:${s}`, $.InvalidAC3SamplingRateCode), void e.trigger(x.INTERNAL_ERROR, n); + var a = 63 & t[i + 4], + o = t[i + 6] >> 5; + let l = 0; + 2 == o ? l += 2 : (1 & o && 1 != o && (l += 2), 4 & o && (l += 2)); + var d = (t[i + 6] << 8 | t[i + 7]) >> 12 - l & 1, + u = [2, 1, 2, 3, 3, 4, 4, 5][o] + d, + e = t[i + 5] >> 3, + i = 7 & t[i + 5]; + return { + samplerate: W[s], + channelCount: u, + segmentCodec: "ac3", + codec: "ac-3", + extraData: s << 22 | e << 17 | i << 14 | o << 11 | d << 10 | a >> 1 << 5 + } + } + + function Q(e, t, i) { + let r; + if (i + 8 > t.length) return r = new D(!0, "error parsing ac-3, not enough data", $.InsufficientAC3Data), void e.trigger(x.INTERNAL_ERROR, r); + if (11 !== t[i] || 119 !== t[i + 1]) return r = new D(!0, "invalid ac-3 magic", $.InvalidAC3Magic), void e.trigger(x.INTERNAL_ERROR, r); + var n = t[i + 4] >> 6; + return 3 <= n ? (r = new D(!0, `invalid ac-3 samplingRateCode:${n}`, $.InvalidAC3SamplingRateCode), void e.trigger(x.INTERNAL_ERROR, r)) : (i = 63 & t[i + 4], 2 * G[3 * i + n]) + } + const W = [48e3, 44100, 32e3], + G = [64, 69, 96, 64, 70, 96, 80, 87, 120, 80, 88, 120, 96, 104, 144, 96, 105, 144, 112, 121, 168, 112, 122, 168, 128, 139, 192, 128, 140, 192, 160, 174, 240, 160, 175, 240, 192, 208, 288, 192, 209, 288, 224, 243, 336, 224, 244, 336, 256, 278, 384, 256, 279, 384, 320, 348, 480, 320, 349, 480, 384, 417, 576, 384, 418, 576, 448, 487, 672, 448, 488, 672, 512, 557, 768, 512, 558, 768, 640, 696, 960, 640, 697, 960, 768, 835, 1152, 768, 836, 1152, 896, 975, 1344, 896, 976, 1344, 1024, 1114, 1536, 1024, 1115, 1536, 1152, 1253, 1728, 1152, 1254, 1728, 1280, 1393, 1920, 1280, 1394, 1920]; + class z extends c { + resetInitSegment(e, t) { + this.audioConfig = void 0, this.audioTrack = void 0, this.duration = t + } + static probe(e, t) { + var i = new M(e, t), + t = i.length; + return !!(i.hasTimeStamp && 11 === e[t] && 119 === e[t + 1] && (new q).bsReadAndUpdate(e, { + byteOffset: t + 5, + usedBits: 0 + }, 5) < 16) + } + append(e, t, i, r, n) { + var s = new M(e, this.logger), + a = 90 * s.timeStamp, + o = e.byteLength; + let l = 0, + d = s.length; + if (this.audioConfig || (this.audioConfig = j(this.observer, e, d, this.logger)), !this.audioConfig) throw "failed to parse ac3 config"; + if (!this.audioTrack) { + const e = { + id: 258, + inputTimescale: 9e4, + timescale: NaN, + duration: this.duration, + encrypted: !1, + keyTagInfo: n + }, + t = { + len: 0, + sequenceNumber: 0, + esSamples: [] + }; + this.audioTrack = { + info: e, + parsingData: t, + type: "audio", + config: this.audioConfig + } + } + var u = H(this.audioConfig, this.audioTrack.info.inputTimescale); + for ("zac3" === s.audioType && (this.audioTrack.info.encrypted = !0); d < o;) { + if (M.isHeader(e, d) && (d += new M(e.subarray(d), this.logger).length), 11 !== e[d] || 119 !== e[d + 1]) { + const e = new D(!0, "invalid ac-3 magic", $.InvalidAC3Magic); + return void this.observer.trigger(x.INTERNAL_ERROR, e) + } + const t = Q(this.observer, e, d), + i = a + l * u, + r = { + unit: e.subarray(d, d + t), + pts: i, + dts: i, + keyTagInfo: n + }; + this.audioTrack.parsingData.esSamples.push(r), this.audioTrack.parsingData.len += t, d += t, l++ + } + this.esRemuxer.remuxEsTracks(this.audioTrack, void 0, { + id3Samples: [{ + pts: a, + dts: a, + data: s.payload, + frames: s.frames + }], + inputTimescale: this.audioTrack.info.inputTimescale + }, void 0, t, i, r, n) + } + } + var X = function(t, i, r, n) { + const s = new q; + let a, o = !1, + l = 0; + for (; r < i.length;) { + if (r + 8 > i.length) return a = new D(!0, "error parsing ec-3, not enough data", $.InsufficientEC3Data), void t.trigger(x.INTERNAL_ERROR, a); + let e = 0; + if (M.isHeader(i, r) && (e = new M(i.subarray(r), n).length || 0, r += e), 11 !== i[r] || 119 !== i[r + 1]) return a = new D(!0, "invalid ec-3 magic", $.InvalidEC3Magic), void t.trigger(x.INTERNAL_ERROR, a); + var d = { + byteOffset: r + 2, + usedBits: 0 + }, + u = s.bsReadAndUpdate(i, d, 2), + c = s.bsReadAndUpdate(i, d, 3); + if (0 === u || 2 === u) + if (!0 === o) { + if (0 === c) break + } else o = !0; + else if (1 !== u) return a = new D(!0, "reserved stream type", $.ReservedStreamType), void t.trigger(x.INTERNAL_ERROR, a); + d = 2 * (s.bsReadAndUpdate(i, d, 11) + 1); + r += d, l += d + (e || 0) + } + return l + }, + Y = function(t, i, r, n) { + const s = { + frmsiz: 0, + fscod: 0, + numblkscod: 0, + acmod: 0, + lfeon: 0, + bsid: 0, + strmtyp: 0, + substreamid: 0, + chanmape: 0, + chanmap: 0, + mixdef: 0, + mixdeflen: 0, + bsmod: 0 + }, + a = { + fscod: 0, + acmod: 0, + lfeon: 0, + bsid: 0, + bsmod: 0, + chan_loc: 0, + data_rate: 0, + num_ind_sub: 0, + num_dep_sub: [], + complexity_index_type_a: 0 + }, + o = new q; + let l, d = !1, + u = 0; + for (; r < i.length;) { + if (r + 8 > i.length) return l = new D(!0, "error parsing ec-3, not enough data", $.InsufficientEC3Data), void t.trigger(x.INTERNAL_ERROR, l); + let e = 0; + if (M.isHeader(i, r) && (e = new M(i.subarray(r), n).length || 0, r += e), 11 !== i[r] || 119 !== i[r + 1]) return l = new D(!0, "invalid ec-3 magic", $.InvalidEC3Magic), void t.trigger(x.INTERNAL_ERROR, l); + const h = { + byteOffset: r + 2, + usedBits: 0 + }; + if (s.strmtyp = o.bsReadAndUpdate(i, h, 2), s.substreamid = o.bsReadAndUpdate(i, h, 3), 0 === s.strmtyp || 2 === s.strmtyp) { + if (!0 === d) { + if (0 === s.substreamid) break + } else d = !0; + a.num_ind_sub++, a.num_dep_sub.push(0) + } else { + if (1 !== s.strmtyp) return l = new D(!0, "reserved stream type", $.ReservedStreamType), void t.trigger(x.INTERNAL_ERROR, l); + a.num_dep_sub[a.num_ind_sub - 1]++ + } + if (s.frmsiz = o.bsReadAndUpdate(i, h, 11), s.fscod = o.bsReadAndUpdate(i, h, 2), 3 === s.fscod ? (o.bsSkip(h, 2), s.numblkscod = 3) : s.numblkscod = o.bsReadAndUpdate(i, h, 2), s.acmod = o.bsReadAndUpdate(i, h, 3), s.lfeon = o.bsReadAndUpdate(i, h, 1), s.bsid = o.bsReadAndUpdate(i, h, 5), o.bsSkip(h, 5), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 8), 0 === s.acmod && (o.bsSkip(h, 5), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 8)), 1 === s.strmtyp && (s.chanmape = o.bsReadAndUpdate(i, h, 1), s.chanmape && (s.chanmap = o.bsReadAndUpdate(i, h, 16))), o.bsReadAndUpdate(i, h, 1) && (2 < s.acmod && o.bsSkip(h, 2), 1 & s.acmod && 2 < s.acmod && o.bsSkip(h, 6), 4 & s.acmod && o.bsSkip(h, 6), s.lfeon && o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 5), 0 === s.strmtyp)) { + if (o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 6), 0 === s.acmod && o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 6), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 6), s.mixdef = o.bsReadAndUpdate(i, h, 2), 1 === s.mixdef) o.bsSkip(h, 5); + else if (2 === s.mixdef) o.bsSkip(h, 12); + else if (3 === s.mixdef) { + s.mixdeflen = o.bsReadAndUpdate(i, h, 5), o.bsReadAndUpdate(i, h, 1) && (o.bsSkip(h, 5), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && (o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4))), o.bsReadAndUpdate(i, h, 1) && (o.bsSkip(h, 5), o.bsReadAndUpdate(i, h, 1) && (o.bsSkip(h, 7), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 8))); + const t = s.mixdeflen + 2 + (h.usedBits ? 1 : 0); + h.byteOffset += t + } + if (s.acmod < 2 && (o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 14), 0 === s.acmod && o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 14)), o.bsReadAndUpdate(i, h, 1)) + if (0 === s.numblkscod) o.bsSkip(h, 5); + else + for (let e = 0; e < s.numblkscod; e++) o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 5) + } + if (s.bsmod = 0, o.bsReadAndUpdate(i, h, 1) && (s.bsmod = o.bsReadAndUpdate(i, h, 3), o.bsSkip(h, 2), 2 === s.acmod && o.bsSkip(h, 4), 6 <= s.acmod && o.bsSkip(h, 2), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 8), 0 === s.acmod && o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 8), s.fscod < 3 && o.bsSkip(h, 1)), 0 === s.strmtyp && 3 !== s.numblkscod && o.bsSkip(h, 1), 2 !== s.strmtyp || (3 === s.numblkscod ? 1 : o.bsReadAndUpdate(i, h, 1)) && o.bsReadAndUpdate(i, h, 6), o.bsReadAndUpdate(i, h, 1)) { + const t = o.bsReadAndUpdate(i, h, 6); + if (0 === s.strmtyp && 0 === s.substreamid && 1 === t) { + const t = o.bsReadAndUpdate(i, h, 7), + r = o.bsReadAndUpdate(i, h, 1), + n = o.bsReadAndUpdate(i, h, 8); + 0 === t && 1 === r && 1 <= n && n <= 16 && (a.complexity_index_type_a = n) + } + } + if (s.chanmape) a.chan_loc |= s.chanmap; + else { + const t = [40960, 16384, 40960, 57344, 41472, 57856, 47104, 63488]; + a.chan_loc |= t[s.acmod] + } + 0 === s.strmtyp && (a.fscod = s.fscod, a.bsid = s.bsid, a.bsmod = s.bsmod, a.acmod = s.acmod, a.lfeon = s.lfeon), a.chan_loc |= s.lfeon ? 1 : 0; + const p = 2 * (s.frmsiz + 1); + r += p, u += p + (e || 0) + } + let c = 0; + for (let e = 0; e < 16; e++) a.chan_loc & 1 << e && c++; + a.lfeon && c++; + let h = 10 + 3 * a.num_ind_sub; + const p = [48e3, 44100, 32e3][a.fscod]; + a.data_rate = p / 1536 * u * 8, h = 10 + 3 * a.num_ind_sub; + for (let e = 0; e < a.num_ind_sub; e++) 0 < a.num_dep_sub[e] && h++; + 0 < a.complexity_index_type_a && (h += 2); + var f = new Uint8Array(h), + m = { + byteOffset: 0, + usedBits: 0 + }; + o.bsWriteAndUpdate(f, m, 32, h), o.bsWriteAndUpdate(f, m, 32, 1684366131), o.bsWriteAndUpdate(f, m, 13, a.data_rate), o.bsWriteAndUpdate(f, m, 3, a.num_ind_sub); + for (let e = 0; e < a.num_ind_sub; e++) o.bsWriteAndUpdate(f, m, 2, a.fscod), o.bsWriteAndUpdate(f, m, 5, a.bsid), o.bsWriteAndUpdate(f, m, 1, 0), o.bsWriteAndUpdate(f, m, 1, 0 === e ? 0 : 1), o.bsWriteAndUpdate(f, m, 3, a.bsmod), o.bsWriteAndUpdate(f, m, 3, a.acmod), o.bsWriteAndUpdate(f, m, 1, a.lfeon), o.bsWriteAndUpdate(f, m, 3, 0), o.bsWriteAndUpdate(f, m, 4, a.num_dep_sub[e]), 0 < a.num_dep_sub[e] ? o.bsWriteAndUpdate(f, m, 9, a.chan_loc) : o.bsWriteAndUpdate(f, m, 1, 0); + return 0 < a.complexity_index_type_a && (o.bsWriteAndUpdate(f, m, 7, 0), o.bsWriteAndUpdate(f, m, 1, 1), o.bsWriteAndUpdate(f, m, 8, a.complexity_index_type_a)), { + samplerate: p, + channelCount: c, + segmentCodec: "ec3", + codec: "ec-3", + extraDataBytes: f + } + }; + class J extends c { + resetInitSegment(e, t) { + this.audioConfig = void 0, this.audioTrack = void 0, this.duration = t + } + static probe(e, t) { + var i = new M(e, t), + t = i.length; + return !(!i.hasTimeStamp || 11 !== e[t] || 119 !== e[t + 1] || 16 !== (new q).bsReadAndUpdate(e, { + byteOffset: t + 5, + usedBits: 0 + }, 5)) + } + append(e, t, i, r, n) { + var s = new M(e, this.logger), + a = 90 * s.timeStamp, + o = e.length; + let l = 0, + d = s.length; + if (this.audioConfig || (this.audioConfig = Y(this.observer, e, d, this.logger)), !this.audioConfig) throw "failed to parse ec-3 config"; + if (!this.audioTrack) { + const e = { + id: 258, + inputTimescale: 9e4, + timescale: NaN, + duration: this.duration, + encrypted: !1, + keyTagInfo: n + }, + t = { + len: 0, + sequenceNumber: 0, + esSamples: [] + }; + this.audioTrack = { + info: e, + parsingData: t, + type: "audio", + config: this.audioConfig + } + } + var u = H(this.audioConfig, this.audioTrack.info.inputTimescale); + for ("zec3" === s.audioType && (this.audioTrack.info.encrypted = !0); d < o;) { + const t = X(this.observer, e, d, this.logger), + i = a + l * u, + r = { + unit: e.subarray(d, d + t), + pts: i, + dts: i, + keyTagInfo: n + }; + this.audioTrack.parsingData.esSamples.push(r), this.audioTrack.parsingData.len += t, d += t, l++ + } + this.esRemuxer.remuxEsTracks(this.audioTrack, void 0, { + id3Samples: [{ + pts: a, + dts: a, + data: s.payload, + frames: s.frames + }], + inputTimescale: this.audioTrack.info.inputTimescale + }, void 0, t, i, r, n) + } + } + const Z = { + BitratesMap: [32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160], + SamplingRateMap: [44100, 48e3, 32e3, 22050, 24e3, 16e3, 11025, 12e3, 8e3], + SamplesCoefficients: [ + [0, 72, 144, 12], + [0, 0, 0, 0], + [0, 72, 144, 12], + [0, 144, 144, 12] + ], + BytesInSlot: [0, 1, 1, 4], + onFrame: function(e, t, i, r, n, s, a) { + r = a + s * (10368e4 / r); + e.esSamples.push({ + unit: t, + pts: r, + dts: r + }), e.len += t.length + }, + onNoise: function(e, t) { + t.warn("mpeg audio has noise: " + e.length + " bytes") + }, + parseFrames: function(e, t, i, r, n, s, a) { + if (r < i + 2) return -1; + if (255 === t[i] || 224 == (224 & t[i + 1])) { + if (r < i + 24) return -1; + const a = t[i + 1] >> 3 & 3, + c = t[i + 1] >> 1 & 3, + h = t[i + 2] >> 4 & 15, + p = t[i + 2] >> 2 & 3, + f = !!(2 & t[i + 2]); + if (1 != a && 0 != h && 15 != h && 3 != p) { + var o = 1e3 * [32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160][14 * (3 == a ? 3 - c : 3 == c ? 3 : 4) + h - 1], + l = [44100, 48e3, 32e3, 22050, 24e3, 16e3, 11025, 12e3, 8e3][3 * (3 == a ? 0 : 2 == a ? 1 : 2) + p], + d = f ? 1 : 0, + u = t[i + 3] >> 6 == 3 ? 1 : 2, + d = 3 == c ? (3 == a ? 12 : 6) * o / l + d << 2 : (3 == a ? 144 : 72) * o / l + d | 0; + return r < i + d ? -1 : (Z.onFrame(e, t.subarray(i, i + d), o, l, u, n, s), d) + } + } + let c = i + 2; + for (; c < r;) { + if (255 === t[c - 1] && 224 == (224 & t[c])) return Z.onNoise(t.subarray(i, c - 1), a), c - i - 1; + c++ + } + return -1 + }, + parse: function(e, t, i, r, n) { + var s = t.length; + let a, o = 0; + for (; i < s && 0 < (a = Z.parseFrames(e, t, i, s, o++, r, n));) i += a + }, + getAudioConfig: function(e, t) { + var i = e[t + 1] >> 3 & 3, + r = e[t + 1] >> 1 & 3, + n = e[t + 2] >> 4 & 15, + s = e[t + 2] >> 2 & 3, + a = e[t + 2] >> 1 & 1; + if (1 != i && 0 != n && 15 != n && 3 != s) { + var o = 3 == i ? 3 - r : 3 == r ? 3 : 4, + o = 1e3 * Z.BitratesMap[14 * o + n - 1], + n = 3 == i ? 0 : 2 == i ? 1 : 2, + s = Z.SamplingRateMap[3 * n + s], + t = e[t + 3] >> 6 == 3 ? 1 : 2, + i = Z.SamplesCoefficients[i][r], + r = Z.BytesInSlot[r]; + return { + segmentCodec: "mp3", + codec: "mp3", + samplerate: s, + channelCount: t, + frameLength: parseInt(i * o / s + a, 10) * r + } + } + }, + isHeaderPattern: function(e, t) { + return 255 === e[t] && 224 == (224 & e[t + 1]) && 0 != (6 & e[t + 1]) + }, + probe: function(t, i) { + if (i + 1 < t.length && Z.isHeaderPattern(t, i)) { + var r = Z.getAudioConfig(t, i); + let e = 4; + r && r.frameLength && (e = r.frameLength); + i = i + e; + if (i === t.length || i + 1 < t.length && Z.isHeaderPattern(t, i)) return !0 + } + return !1 + } + }; + var ee = Z; + const te = { + name: "MP3Demuxer" + }; + class ie extends c { + resetInitSegment(e, t) { + this.audioConfig = void 0, this.audioTrack = void 0, this.duration = t + } + static probe(e, t) { + var i = new M(e, t); + let r, n; + if (i.hasTimeStamp) + for (r = i.length, n = Math.min(e.length - 1, r + 100); r < n; r++) + if (ee.probe(e, r)) return t.warn(te, "MPEG Audio sync word found !"), !0; + return !1 + } + append(e, t, i, r, n) { + var s = new M(e, this.logger), + a = 90 * s.timeStamp; + if (this.audioConfig || (this.audioConfig = ee.getAudioConfig(e, s.length)), !this.audioConfig) throw "unable to parse mp3 header"; + if (!this.audioTrack) { + const e = { + id: 258, + inputTimescale: 9e4, + timescale: NaN, + duration: this.duration, + encrypted: !1, + keyTagInfo: n + }, + t = { + len: 0, + sequenceNumber: 0, + esSamples: [] + }; + this.audioTrack = { + info: e, + parsingData: t, + type: "audio", + config: this.audioConfig + } + } + ee.parse(this.audioTrack.parsingData, e, s.length, a, this.logger), this.esRemuxer.remuxEsTracks(this.audioTrack, void 0, { + id3Samples: [{ + pts: a, + dts: a, + data: s.payload, + frames: s.frames + }], + inputTimescale: 9e4 + }, void 0, t, i, r) + } + } + + function re(e, t) { + if ("mp4a.40.2" === e) { + if (1 === t) return new Uint8Array([0, 200, 0, 128, 35, 128]); + if (2 === t) return new Uint8Array([33, 0, 73, 144, 2, 25, 0, 35, 128]); + if (3 === t) return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 142]); + if (4 === t) return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 128, 44, 128, 8, 2, 56]); + if (5 === t) return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 130, 48, 4, 153, 0, 33, 144, 2, 56]); + if (6 === t) return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 130, 48, 4, 153, 0, 33, 144, 2, 0, 178, 0, 32, 8, 224]) + } else { + if (1 === t) return new Uint8Array([1, 64, 34, 128, 163, 78, 230, 128, 186, 8, 0, 0, 0, 28, 6, 241, 193, 10, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 94]); + if (2 === t) return new Uint8Array([1, 64, 34, 128, 163, 94, 230, 128, 186, 8, 0, 0, 0, 0, 149, 0, 6, 241, 161, 10, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 94]); + if (3 === t) return new Uint8Array([1, 64, 34, 128, 163, 94, 230, 128, 186, 8, 0, 0, 0, 0, 149, 0, 6, 241, 161, 10, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 94]) + } + return null + } + + function ne(e) { + return "number" == typeof e && isFinite(e) + } + + function se(e, t) { + return ne(e) ? e.toFixed(t) : `${e}` + } + + function ae(e, i = 3) { + return JSON.stringify(e, (e, t) => !isNaN(t) && null != t && t.toFixed ? Number(null == t ? void 0 : t.toFixed(i)) : t) + } + let oe = !0; + + function le(e) { + return oe ? "" : e + } + + function de(e) { + if (!e) return e; + if ("object" != typeof e) return e; { + if (Array.isArray(e)) return e.map(de); + const r = {}; + for (var [t, i] of Object.entries(e)) r[t] = de(i); + return r + } + } + + function ue(e) { + const t = [...e]; + for (let e = 0; e < t.length; e++) t[e] = Object.assign({}, t[e]), t[e].url = le(t[e].url), t[e].attrs && (t[e].attrs = Object.assign({}, t[e].attrs), t[e].attrs.URI = le(t[e].attrs.URI)); + return t + } + + function ce(e) { + const t = [...e]; + for (let e = 0; e < t.length; e++) t[e] = Object.assign({}, t[e]), t[e].url = le(t[e].url); + return t + } + const he = Math.pow(2, 32) - 1; + class pe { + static init() { + let e; + for (e in pe.types = { + avc1: [], + avcC: [], + btrt: [], + dinf: [], + dref: [], + esds: [], + free: [], + ftyp: [], + hdlr: [], + mdat: [], + mdhd: [], + mdia: [], + mfhd: [], + minf: [], + moof: [], + moov: [], + mp4a: [], + ".mp3": [], + dac3: [], + "ac-3": [], + dec3: [], + "ec-3": [], + mvex: [], + mvhd: [], + pasp: [], + sdtp: [], + stbl: [], + stco: [], + stsc: [], + stsd: [], + stsz: [], + stts: [], + tfdt: [], + tfhd: [], + traf: [], + trak: [], + trun: [], + trex: [], + tkhd: [], + vmhd: [], + smhd: [], + uuid: [], + encv: [], + enca: [], + frma: [], + schm: [], + schi: [], + senc: [], + saio: [], + saiz: [], + sinf: [], + tenc: [], + sbgp: [], + seig: [], + sgpd: [], + pssh: [] + }, pe.types) pe.types.hasOwnProperty(e) && (pe.types[e] = [e.charCodeAt(0), e.charCodeAt(1), e.charCodeAt(2), e.charCodeAt(3)]); + var t = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 118, 105, 100, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 105, 100, 101, 111, 72, 97, 110, 100, 108, 101, 114, 0]), + i = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 115, 111, 117, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 111, 117, 110, 100, 72, 97, 110, 100, 108, 101, 114, 0]); + pe.HDLR_TYPES = { + video: t, + audio: i + }; + var r = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 12, 117, 114, 108, 32, 0, 0, 0, 1]), + n = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); + pe.STTS = pe.STSC = pe.STCO = n, pe.STSZ = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), pe.VMHD = new Uint8Array([0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]), pe.SMHD = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]), pe.STSD = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1]); + t = new Uint8Array([105, 115, 111, 109]), i = new Uint8Array([97, 118, 99, 49]), n = new Uint8Array([0, 0, 0, 1]); + pe.FTYP = pe.box(pe.types.ftyp, t, n, t, i), pe.DINF = pe.box(pe.types.dinf, pe.box(pe.types.dref, r)) + } + static set16(e, t, i) { + return t[i] = e >> 8 & 255, t[i + 1] = 255 & e, i + 2 + } + static set32(e, t, i) { + return t[i] = e >> 24 & 255, t[i + 1] = e >> 16 & 255, t[i + 2] = e >> 8 & 255, t[i + 3] = 255 & e, i + 4 + } + static box(e) { + var t = Array.prototype.slice.call(arguments, 1); + let i = 8, + r = t.length; + for (var n = r; r--;) i += t[r].byteLength; + const s = new Uint8Array(i); + for (s[0] = i >> 24 & 255, s[1] = i >> 16 & 255, s[2] = i >> 8 & 255, s[3] = 255 & i, s.set(e, 4), r = 0, i = 8; r < n; r++) s.set(t[r], i), i += t[r].byteLength; + return s + } + static hdlr(e) { + return pe.box(pe.types.hdlr, pe.HDLR_TYPES[e]) + } + static mdat(e) { + return pe.box(pe.types.mdat, e) + } + static mdhd(e, t) { + t *= e; + var i = Math.floor(t / (1 + he)), + t = Math.floor(t % (1 + he)); + return pe.box(pe.types.mdhd, new Uint8Array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, e >> 24 & 255, e >> 16 & 255, e >> 8 & 255, 255 & e, i >> 24, i >> 16 & 255, i >> 8 & 255, 255 & i, t >> 24, t >> 16 & 255, t >> 8 & 255, 255 & t, 85, 196, 0, 0])) + } + static mdia(e) { + var t = pe.mdhd(e.info.timescale, e.info.duration), + i = pe.hdlr(e.type), + e = pe.minf(e); + return pe.box(pe.types.mdia, t, i, e) + } + static mfhd(e) { + return pe.box(pe.types.mfhd, new Uint8Array([0, 0, 0, 0, e >> 24, e >> 16 & 255, e >> 8 & 255, 255 & e])) + } + static minf(e) { + return "audio" === e.type ? pe.box(pe.types.minf, pe.box(pe.types.smhd, pe.SMHD), pe.DINF, pe.stbl(e)) : pe.box(pe.types.minf, pe.box(pe.types.vmhd, pe.VMHD), pe.DINF, pe.stbl(e)) + } + static moof(e, t) { + pe.types || pe.init(); + e = pe.traf(t, e); + return pe.box(pe.types.moof, pe.mfhd(t.sequenceNumber), e) + } + static moov(e) { + let t = e.length; + const i = []; + for (; t--;) i[t] = pe.trak(e[t]); + return pe.box.apply(null, [pe.types.moov, pe.mvhd(e[0].info.timescale, e[0].info.duration)].concat(i).concat(pe.mvex(e))) + } + static mvex(e) { + let t = e.length; + const i = []; + for (; t--;) i[t] = pe.trex(e[t]); + return pe.box(pe.types.mvex, ...i) + } + static mvhd(e, t) { + t *= e; + var i = Math.floor(t / (1 + he)), + t = Math.floor(t % (1 + he)), + t = new Uint8Array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, e >> 24 & 255, e >> 16 & 255, e >> 8 & 255, 255 & e, i >> 24, i >> 16 & 255, i >> 8 & 255, 255 & i, t >> 24, t >> 16 & 255, t >> 8 & 255, 255 & t, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255]); + return pe.box(pe.types.mvhd, t) + } + static sdtp(e) { + const t = e.samples || [], + i = new Uint8Array(4 + t.length); + let r, n; + for (n = 0; n < t.length; n++) r = t[n].flags, i[n + 4] = r.dependsOn << 4 | r.isDependedOn << 2 | r.hasRedundancy; + return pe.box(pe.types.sdtp, i) + } + static stbl(e) { + var t = pe.stsd(e), + i = pe.box(pe.types.stts, pe.STTS), + r = pe.box(pe.types.stsc, pe.STSC), + n = pe.box(pe.types.stsz, pe.STSZ), + e = pe.box(pe.types.stco, pe.STCO); + return pe.box(pe.types.stbl, t, i, r, n, e) + } + static avc1(e) { + let t, i, r, n = [], + s = []; + var a = e.info.encrypted ? pe.types.encv : pe.types.avc1; + for (t = 0; t < e.config.sps.length; t++) i = e.config.sps[t], r = i.byteLength, n.push(r >>> 8 & 255), n.push(255 & r), n = n.concat(Array.prototype.slice.call(i)); + for (t = 0; t < e.config.pps.length; t++) i = e.config.pps[t], r = i.byteLength, s.push(r >>> 8 & 255), s.push(255 & r), s = s.concat(Array.prototype.slice.call(i)); + var o = pe.box(pe.types.avcC, new Uint8Array([1, n[3], n[4], n[5], 255, 224 | e.config.sps.length].concat(n).concat([e.config.pps.length]).concat(s))), + l = e.config.width, + d = e.config.height, + u = e.config.pixelRatio[0], + c = e.config.pixelRatio[1], + h = e.info.encrypted && e.info.keyTagInfo ? pe.sinf(e.info.keyTagInfo, e.type, pe.types.avc1) : new Uint8Array; + return pe.box(a, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, l >> 8 & 255, 255 & l, d >> 8 & 255, 255 & d, 0, 72, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 1, 18, 100, 97, 105, 108, 121, 109, 111, 116, 105, 111, 110, 47, 104, 108, 115, 46, 106, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 17, 17]), o, h, pe.box(pe.types.btrt, new Uint8Array([0, 28, 156, 128, 0, 45, 198, 192, 0, 45, 198, 192])), pe.box(pe.types.pasp, new Uint8Array([u >> 24, u >> 16 & 255, u >> 8 & 255, 255 & u, c >> 24, c >> 16 & 255, c >> 8 & 255, 255 & c]))) + } + static esds(e) { + var t = e.esdsConfig.length; + return new Uint8Array([0, 0, 0, 0, 3, 23 + t, 0, 1, 0, 4, 15 + t, 64, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5].concat([t]).concat(e.esdsConfig).concat([6, 1, 2])) + } + static audioStsd(e) { + var t = e.samplerate; + return new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, e.channelCount, 0, 16, 0, 0, 0, 0, t >> 8 & 255, 255 & t, 0, 0]) + } + static dac3(e) { + e = e.extraData; + return new Uint8Array([e >> 16 & 255, e >> 8 & 255, 255 & e]) + } + static dec3(e) { + return e.extraDataBytes + } + static mp4a(e, t) { + let i = pe.types.mp4a, + r = null; + r = e.encrypted && e.keyTagInfo ? (i = pe.types.enca, pe.sinf(e.keyTagInfo, "audio", pe.types.mp4a)) : new Uint8Array; + e = pe.audioStsd(t), t = pe.box(pe.types.esds, pe.esds(t)); + return pe.box(i, e, t, r) + } + static mp3(e) { + return pe.box(pe.types[".mp3"], pe.audioStsd(e)) + } + static ac3(e, t) { + let i = pe.types["ac-3"], + r = null; + return r = e.encrypted && e.keyTagInfo ? (i = pe.types.enca, pe.sinf(e.keyTagInfo, "audio", pe.types["ac-3"])) : new Uint8Array, pe.box(i, pe.audioStsd(t), pe.box(pe.types.dac3, pe.dac3(t)), r) + } + static ec3(e, t) { + let i = pe.types["ec-3"], + r = null; + return r = e.encrypted && e.keyTagInfo ? (i = pe.types.enca, pe.sinf(e.keyTagInfo, "audio", pe.types["ec-3"])) : new Uint8Array, pe.box(i, pe.audioStsd(t), pe.box(pe.types.dec3, pe.dec3(t)), r) + } + static stsd(e) { + if ("audio" !== e.type) return pe.box(pe.types.stsd, pe.STSD, pe.avc1(e)); + if ("mp3" === e.config.segmentCodec && "mp3" === e.config.codec) return pe.box(pe.types.stsd, pe.STSD, pe.mp3(e.config)); + if ("ac3" === e.config.segmentCodec) return pe.box(pe.types.stsd, pe.STSD, pe.ac3(e.info, e.config)); + if ("ec3" === e.config.segmentCodec) return pe.box(pe.types.stsd, pe.STSD, pe.ec3(e.info, e.config)); + if ("aac" === e.config.segmentCodec) return pe.box(pe.types.stsd, pe.STSD, pe.mp4a(e.info, e.config)); + throw `unknown segmentCodec ${e.config.segmentCodec}` + } + static tkhd(e) { + var t = e.info.id, + i = e.info.duration * e.info.timescale, + r = Math.floor(i / (1 + he)), + i = Math.floor(i % (1 + he)); + let n = 0, + s = 0; + return "video" === e.type && (n = e.config.width, s = e.config.height), pe.box(pe.types.tkhd, new Uint8Array([1, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, t >> 24 & 255, t >> 16 & 255, t >> 8 & 255, 255 & t, 0, 0, 0, 0, r >> 24, r >> 16 & 255, r >> 8 & 255, 255 & r, i >> 24, i >> 16 & 255, i >> 8 & 255, 255 & i, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, n >> 8 & 255, 255 & n, 0, 0, s >> 8 & 255, 255 & s, 0, 0])) + } + static traf(e, t) { + var i = pe.senc(e), + r = pe.sdtp(e), + n = i.boxData, + s = n.length ? pe.saio(76) : new Uint8Array, + a = n.length ? pe.saiz(i.defaultSampleInfoSize, i.sampleInfoSizes) : new Uint8Array, + o = pe.sbgp(e), + l = pe.sgpd(e), + d = e.id, + i = Math.floor(t / (1 + he)), + t = Math.floor(t % (1 + he)); + return pe.box(pe.types.traf, pe.box(pe.types.tfhd, new Uint8Array([0, 2, 0, 0, d >> 24, d >> 16 & 255, d >> 8 & 255, 255 & d])), pe.box(pe.types.tfdt, new Uint8Array([1, 0, 0, 0, i >> 24, i >> 16 & 255, i >> 8 & 255, 255 & i, t >> 24, t >> 16 & 255, t >> 8 & 255, 255 & t])), n, s, a, o, l, pe.trun(e, r.length + n.length + o.length + l.length + s.length + a.length + 16 + 20 + 8 + 16 + 8 + 8), r) + } + static trak(e) { + if ("trakData" in e) return e.trakData; + e.info.duration = e.info.duration || 4294967295; + var t = pe.types.trak, + i = pe.tkhd(e), + e = pe.mdia(e); + return pe.box(t, i, e) + } + static trex(e) { + e = e.info.id; + return pe.box(pe.types.trex, new Uint8Array([0, 0, 0, 0, e >> 24, e >> 16 & 255, e >> 8 & 255, 255 & e, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1])) + } + static trun(e, t) { + const i = e.samples || [], + r = i.length, + n = 12 + 16 * r, + s = new Uint8Array(n); + let a, o, l, d, u, c; + for (t += 8 + n, s.set([0, 0, 15, 1, r >>> 24 & 255, r >>> 16 & 255, r >>> 8 & 255, 255 & r, t >>> 24 & 255, t >>> 16 & 255, t >>> 8 & 255, 255 & t], 0), a = 0; a < r; a++) l = (o = i[a]).duration, d = o.size, u = o.flags, c = o.cts, s.set([l >>> 24 & 255, l >>> 16 & 255, l >>> 8 & 255, 255 & l, d >>> 24 & 255, d >>> 16 & 255, d >>> 8 & 255, 255 & d, u.isLeading << 2 | u.dependsOn, u.isDependedOn << 6 | u.hasRedundancy << 4 | u.paddingValue << 1 | u.isNonSync, 61440 & u.degradPrio, 15 & u.degradPrio, c >>> 24 & 255, c >>> 16 & 255, c >>> 8 & 255, 255 & c], 12 + 16 * a); + return pe.box(pe.types.trun, s) + } + static initSegment(e) { + pe.types || pe.init(); + const t = pe.moov(e), + i = new Uint8Array(pe.FTYP.byteLength + t.byteLength); + return i.set(pe.FTYP), i.set(t, pe.FTYP.byteLength), i + } + static saio(e) { + e = e + 4 + 4; + return pe.box(pe.types.saio, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, e >> 24 & 255, e >> 16 & 255, e >> 8 & 255, 255 & e])) + } + static saiz(e, t) { + ne(e) || (e = 0); + var i = t.length, + t = 0 === e ? new Uint8Array(t) : new Uint8Array; + return pe.box(pe.types.saiz, new Uint8Array([0, 0, 0, 0, e, i >> 24 & 255, i >> 16 & 255, i >> 8 & 255, 255 & i]), t) + } + static senc(e) { + const t = e.samples || [], + i = t.length; + let r = 0, + n = NaN, + s = !0; + const a = []; + if (!e.encrypted || i <= 0) return { + boxData: new Uint8Array, + sampleInfoSizes: a, + defaultSampleInfoSize: 0 + }; + e = e.defaultPerSampleIVSize || 0; + for (const d of t) d.subsamples && (r += d.subsamples.length); + if (r <= 0) return { + boxData: new Uint8Array, + sampleInfoSizes: a, + defaultSampleInfoSize: 0 + }; + const o = new Uint8Array(2 * i + i * e + 6 * r + 4); + let l = this.set32(i, o, 0); + for (const d of t) { + const t = d.subsamples || []; + let e = 2; + d.iv && (o.set(d.iv, l), l += d.iv.byteLength, e += d.iv.byteLength), l = this.set16(t.length, o, l); + for (const d of t) l = this.set16(d[0], o, l), l = this.set32(d[1], o, l), e += 6; + a.push(e), ne(n) || (n = e), s = s && n === e, n = e + } + return { + boxData: pe.box(pe.types.senc, new Uint8Array([0, 0, 0, 2]), o), + defaultSampleInfoSize: s ? n : 0, + sampleInfoSizes: a + } + } + static sinf(e, t, i) { + return pe.box(pe.types.sinf, pe.frma(i), pe.schm(), pe.schi(e, t)) + } + static frma(e) { + return pe.box(pe.types.frma, new Uint8Array(e)) + } + static schm() { + return pe.box(pe.types.schm, new Uint8Array([0, 0, 0, 0, 99, 98, 99, 115, 0, 1, 0, 0])) + } + static schi(e, t) { + return pe.box(pe.types.schi, pe.tenc(e, t)) + } + static tenc(e, t) { + let i = 0; + "video" === t && (i = 25); + const r = new Uint8Array(17); + if (r[0] = 16, e.iv && 16 === e.iv.byteLength && r.set(e.iv, 1), !e.keyId) throw "tenc: no key id found in decryptdata"; + return pe.box(pe.types.tenc, new Uint8Array([1, 0, 0, 0, 0, i, 1, 0]), e.keyId, r) + } + static sbgp(e) { + if (!e.encrypted || 0 === e.samples.length || !e.samples[0].keyTagInfo) return new Uint8Array; + e = e.samples.length; + return pe.box(pe.types.sbgp, new Uint8Array([0, 0, 0, 0]), new Uint8Array(pe.types.seig), new Uint8Array([0, 0, 0, 1, e >> 24 & 255, e >> 16 & 255, e >> 8 & 255, 255 & e, 0, 1, 0, 1])) + } + static sgpd(e) { + if (!e.encrypted || 0 === e.samples.length || !e.samples[0].keyTagInfo) return new Uint8Array; + var t = e.samples[0].keyTagInfo; + let i = 0; + "video" === e.type && (i = 25); + const r = new Uint8Array(17); + if (r[0] = 16, t.iv && r.set(t.iv, 1), !t.keyId) throw "sgpd: no keyid in decryptdata"; + return pe.box(pe.types.sgpd, new Uint8Array([1, 0, 0, 0]), new Uint8Array(pe.types.seig), new Uint8Array([0, 0, 0, 37, 0, 0, 0, 1]), new Uint8Array([0, i, 1, 0]), t.keyId, r) + } + static pssh(e, t, i) { + if (pe.types || pe.init(), !e) throw new TypeError("Bad system id"); + if (16 !== e.byteLength) throw new RangeError("Invalid system id"); + let r, n, s; + if (t) { + r = 1, n = new Uint8Array(16 * t.length); + for (let e = 0; e < t.length; e++) { + const i = t[e]; + if (16 !== i.byteLength) throw new RangeError("Invalid key"); + n.set(i, 16 * e) + } + } else r = 0, n = new Uint8Array; + 0 < r ? (s = new Uint8Array(4), 0 < t.length && new DataView(s.buffer).setUint32(0, t.length, !1)) : s = new Uint8Array; + var a = new Uint8Array(4); + return i && 0 < i.byteLength && new DataView(a.buffer).setUint32(0, i.byteLength, !1), pe.box(pe.types.pssh, new Uint8Array([r, 0, 0, 0]), e, s, n, a, i || new Uint8Array) + } + } + var fe, me, ge = pe; + (vi = fe = fe || {})[vi.SDR = 0] = "SDR", vi[vi.HDR = 1] = "HDR", vi[vi.HDR10 = 2] = "HDR10", vi[vi.DolbyVision = 3] = "DolbyVision", vi[vi.HLG = 4] = "HLG", (bi = me = me || {})[bi.H264 = 16] = "H264", bi[bi.HEVC = 64] = "HEVC", bi[bi.VP09 = 65] = "VP09"; + const ye = new Set(["ac-3", "mp4a.a5", "mp4a.A5"]), + ve = new Set(["ec-3", "mp4a.a6", "mp4a.A6"]), + Se = { + aac: 1024, + mp3: 1024, + ac3: 1536, + ec3: 1536 + }, + be = { + isAC3: e => Boolean(e && ye.has(e)), + isEC3: e => Boolean(e && ve.has(e)), + isDolbyAtmos(e, t) { + const i = t.split("/"); + return Boolean(be.isEC3(e) && 1 < i.length && i[1].split(",").find(e => "JOC" === e)) + }, + isAAC(e) { + return Boolean(e && ("aac" === e || null !== (e = e.match(/^mp4a\.40\.(.*)/)) && "34" !== e[1])) + }, + isMP3(e) { + return Boolean(e && ("mp3" === e || null !== (e = e.match(/^mp4a\.40\.(.*)/)) && "34" === e[1])) + }, + isAVC: e => Boolean(e && e.match(/^avc[13]\.(.*)/)), + isXHEAAC: function(e) { + return Boolean("mp4a.40.42" === e) + }, + isALAC: function(e) { + return Boolean("alac" === e) + }, + isFLAC: function(e) { + return Boolean("fLaC" === e) + }, + isHEVC: e => Boolean(e && e.match(/^(hev|hvc)1\..*/)), + isDolby: e => Boolean(e && e.match(/^dv(h1|he|a1|av)\..*/)), + isVP09: e => Boolean(e && e.match(/^vp09\..*/)), + isCompatibleCodecString(e, t) { + const i = e.split(","), + r = t.split(","), + n = i.filter(e => be.isVideoCodec(e)), + s = r.filter(e => be.isVideoCodec(e)), + a = i.filter(e => be.isAudioCodec(e)), + o = r.filter(e => be.isAudioCodec(e)), + l = 0 === n.length && 0 === s.length || n.length === s.length && be.isCompatibleVideoCodec(n[0], s[0]), + d = 0 === a.length && 0 === o.length || a.length === o.length && be.isCompatibleAudioCodec(a[0], o[0]); + return l && d + }, + isVideoCodec: e => be.isAVC(e) || be.isDolby(e) || be.isHEVC(e) || be.isVP09(e), + isAudioCodec: e => be.isAC3(e) || be.isEC3(e) || be.isAAC(e) || be.isMP3(e), + isCompatibleVideoCodec: (e, t) => Boolean(e && t && (e === t || be.isDolby(e) && be.isDolby(t) || be.isHEVC(e) && be.isHEVC(t) || be.isAVC(e) && be.isAVC(t) || be.isVP09(e) && be.isVP09(t))), + isCompatibleAudioCodec: (e, t) => Boolean(e && t && (e === t || be.isAAC(e) && be.isAAC(t) || be.isAC3(e) && be.isAC3(t) || be.isEC3(e) && be.isEC3(t) || be.isMP3(e) && be.isMP3(t))), + getSegmentCodec(e) { + let t; + if (be.isAAC(e)) t = "aac"; + else if (be.isAC3(e)) t = "ac3"; + else if (be.isEC3(e)) t = "ec3"; + else { + if ("mp3" !== e) throw new Error(`invalid audio config, codec ${e}`); + t = "mp3" + } + return t + }, + getChannelCount(e) { + if (!e) return 0; + e = e.split("/"), e = parseInt(e[0]); + return ne(e) ? e : 0 + }, + avc1toavcoti(e) { + var t; + const i = e.split("."); + let r; + return 2 < i.length ? (r = i.shift() + ".", r += parseInt(null !== (t = i.shift()) && void 0 !== t ? t : "").toString(16), r += ("000" + parseInt(null !== (t = i.shift()) && void 0 !== t ? t : "").toString(16)).substr(-4)) : r = e, r + }, + getDynamicRangeType(e, t) { + let i = fe.SDR; + return "PQ" === e && be.isDolby(t) ? i = fe.DolbyVision : "PQ" === e && (be.isHEVC(t) || be.isVP09(t)) ? i = fe.HDR10 : "HLG" !== e || -1 === t.indexOf("hvc1") && !be.isVP09(t) || (i = fe.HLG), i + }, + getCompressionType(e) { + let t = me.H264; + return be.isHEVC(e) || be.isDolby(e) ? t = me.HEVC : be.isVP09(e) && (t = me.VP09), t + }, + isHigherCodecByFamily(e, t) { + if (!e) return !0; + const i = e.split("."), + r = t.split("."); + if (i[0] !== r[0]) throw new Error(`mismatch in codec family current/new: ${i[0]}/${r[0]}`); + switch (i[0]) { + case "avc1": + case "avc3": + return r[1] > i[1]; + case "vp09": + return e < t; + case "hvc1": + case "hev1": + var n = "H" === i[3].substring(0, 1) ? 1 : 0, + s = i[3].substring(1), + a = "H" === r[3].substring(0, 1) ? 1 : 0, + o = r[3].substring(1); + return r[1] > i[1] || r[2] > i[2] || n < a || s < o; + case "dvh1": + return r[1] > i[1] || r[2] > i[2] + } + } + }; + class Te { + static getTrack(e, t, i, r, n) { + let s; + switch (be.getSegmentCodec(i)) { + case "aac": + var a; + (a = E(e, 1 === r ? new Uint8Array([255, 241, 92, 64, 1, 127, 252]) : new Uint8Array([255, 241, 92, 128, 1, 191, 252]), 0, i)) && (s = { + type: "audio", + info: { + id: t, + timescale: a.samplerate, + duration: 0, + encrypted: !1, + keyTagInfo: void 0 + }, + config: a + }); + break; + case "ac3": + case "ec3": { + const i = j(e, new Uint8Array([11, 119, 69, 17, 128, 64, 47, 132]), 0); + i && (s = { + type: "audio", + info: { + id: t, + timescale: i.samplerate, + duration: 0, + encrypted: !1, + keyTagInfo: void 0 + }, + config: i + }) + } + } + return s + } + static getSample(e, t) { + let i; + switch (e) { + case "mp4a.40.2": + case "mp4a.40.5": + i = 1 === t ? new Uint8Array([0, 208, 0, 7]) : new Uint8Array([33, 0, 3, 64, 104, 28]); + break; + case "ac-3": + case "ec-3": + i = new Uint8Array([11, 119, 69, 17, 128, 64, 47, 132, 41, 3, 253, 214, 124, 253, 243, 215, 233, 95, 185, 123, 78, 20, 40, 106, 97, 190, 74, 253, 43, 218, 208, 140, 191, 176, 144, 120, 214, 181, 44, 124, 129, 251, 91, 109, 187, 109, 198, 225, 43, 172, 116, 140, 176, 123, 38, 144, 211, 247, 225, 64, 29, 53, 175, 96, 16, 57, 121, 87, 78, 203, 81, 37, 7, 72, 228, 132, 37, 169, 38, 231, 97, 229, 247, 194, 208, 8, 12, 83, 74, 139, 137, 17, 22, 26, 221, 203, 107, 113, 94, 93, 75, 33, 208, 247, 146, 105, 39, 143, 6, 36, 1, 227, 108, 70, 11, 180, 152, 218, 182, 218, 209, 59, 85, 104, 201, 70, 37, 82, 219, 68, 55, 225, 144, 99, 149, 0, 119, 26, 14, 69, 164, 241, 204, 222, 81, 177, 142, 80, 20, 100, 97, 143, 101, 221, 140, 113, 31, 208, 124, 25, 64, 29, 49, 77, 140, 30, 155, 74, 214, 204, 138, 229, 109, 172, 95, 130, 70, 230, 134, 88, 59, 179, 212, 155, 232, 0, 0, 0, 0, 0, 173, 234]) + } + return i + } + static getSegment(e, i, r, n) { + if (e) { + var s = e.info["timescale"], + a = e.config["segmentCodec"], + o = Te.getSample(e.config.codec, e.config.channelCount); + if (o) { + const l = [], + d = { + id: e.info.id, + sequenceNumber: i, + type: "audio", + encrypted: !1, + samples: l, + defaultPerSampleIVSize: 0 + }, + u = Se[a], + c = Math.ceil(n * s / u), + h = { + baseTime: Math.round(c * u + r), + timescale: s + }; + let t = 0; + const p = c * o.byteLength + 8, + f = new Uint8Array(p); + f[0] = p >> 24 & 255, f[1] = p >> 16 & 255, f[2] = p >> 8 & 255, f[3] = 255 & p, ge.types || ge.init(), f.set(ge.types.mdat, 4), t += 8; + for (let e = 0; e < c; e++) l.push({ + duration: u, + size: o.byteLength, + cts: 0, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: 1, + isNonSync: 0, + paddingValue: 0 + } + }), f.set(o, t), t += o.byteLength; + const m = ge.moof(r, d), + g = new Uint8Array(m.byteLength + f.byteLength); + return g.set(m), g.set(f, m.byteLength), { + silentFragData: g, + endTs: h + } + } + } + } + } + class Ee extends h { + constructor(e, t, i, r, n) { + super(e, t, n), this.typeSupported = i, this.isVideoContiguous = !1, this.logger = n.child({ + name: "EsRemuxer" + }); + const s = navigator.userAgent; + this.isSafari = r && -1 < r.indexOf("Apple") && s && !s.match("CriOS") + } + resetTimeStamp(e) { + this._initPTS = this._initDTS = e + } + resetInitSegment() { + this.currentInitTrack = void 0, this._silentAudioTrack = void 0 + } + remuxEsTracks(r, n, s, a, o, l, d, u, c, h) { + let p; + l || (this.isVideoContiguous = !1); + c = void 0 === c ? o : c; + if (!this.currentInitTrack) { + if (r && r.config.codec && (this._audioTrackInfo = { + id: r.info.id, + codec: r.config.codec, + channelCount: r.config.channelCount + }), n && h && this._audioTrackInfo) { + const r = Te.getTrack(this.observer, this._audioTrackInfo.id, this._audioTrackInfo.codec, this._audioTrackInfo.channelCount, this.logger); + if (r) { + this._silentAudioTrack = Object.assign(Object.assign({}, r), { + info: Object.assign(Object.assign({}, r.info), { + inputTimescale: 9e4 + }), + parsingData: { + len: 0, + sequenceNumber: 0, + esSamples: [] + } + }); + const s = this._initPTS + Math.round(c * this._silentAudioTrack.info.timescale); + p = Te.getSegment(this._silentAudioTrack, n.parsingData.sequenceNumber, s, h), n.parsingData.sequenceNumber++ + } + } else this._silentAudioTrack = void 0; + this.updateInitPTSDTS(n, r, o), this.generateIS(this._silentAudioTrack || r, n) + } + if (this.currentInitTrack) { + const f = n && n.parsingData.esSamples.length, + m = this.isVideoContiguous; + let t, i, e; + if (n && h && this._silentAudioTrack && !p) { + const r = this._initPTS + Math.round((c + this.config.audioPrimingDelay) * this._silentAudioTrack.info.timescale); + p = Te.getSegment(this._silentAudioTrack, n.parsingData.sequenceNumber, r, h) + } + if (r && r.parsingData.esSamples.length) { + if (ne(r.info.timescale) || (this.logger.warn("regenerate InitSegment as audio detected"), this.updateInitPTSDTS(n, r, o), this.generateIS(r, n)), t = this.remuxAudio(r, c, l, d, u), f) { + let e; + t && (e = S(t.endPTS) - S(t.startPTS)), ne(n.info.timescale) || (this.logger.warn("regenerate InitSegment as video detected"), this.updateInitPTSDTS(n, r, o), this.generateIS(r, n)), i = this.remuxVideo(n, c, m, e, h) + } + } else f && (i = this.remuxVideo(n, c, m, void 0, h)), i && r && r.config.codec && (t = this.remuxEmptyAudio(r, c, l, d, i, u)); + p ? e = { + data1: i.data1, + data2: p.silentFragData, + startDTS: i.startDTS, + startPTS: i.startPTS, + endDTS: y(i.endDTS, p.endTs), + endPTS: y(i.endPTS, p.endTs), + type: "audiovideo", + track: this.currentInitTrack + } : i && t ? e = { + data1: i.data1, + data2: t.data1, + startDTS: g(i.startDTS, t.startDTS), + startPTS: g(i.startPTS, t.startPTS), + endDTS: y(i.endDTS, t.endDTS), + endPTS: y(i.endPTS, t.endPTS), + type: "audiovideo", + track: this.currentInitTrack, + dropped: i.dropped, + framesWithoutIDR: i.framesWithoutIDR, + firstKeyframePts: i.firstKeyframePts + } : i ? e = { + data1: i.data1, + startDTS: i.startDTS, + startPTS: i.startPTS, + endDTS: i.endDTS, + endPTS: i.endPTS, + type: "video", + track: this.currentInitTrack, + dropped: i.dropped, + framesWithoutIDR: i.framesWithoutIDR, + firstKeyframePts: i.firstKeyframePts + } : t ? e = { + data1: t.data1, + startDTS: t.startDTS, + startPTS: t.startPTS, + endDTS: t.endDTS, + endPTS: t.endPTS, + type: "audio", + track: this.currentInitTrack + } : this.logger.error("Missing video and audio data"), a && a.captionSamples.length && this.remuxText(a, e), null !== (a = null == s ? void 0 : s.id3Samples) && void 0 !== a && a.length && this.remuxID3(s, e), this.observer.trigger(v.FRAG_PARSING_DATA, e) + } else this.logger.error("failed to generate IS"); + this.observer.trigger(v.FRAG_PARSED) + } + updateInitPTSDTS(e, t, i) { + let r = 1 / 0, + n = 1 / 0; + var s = e ? e.parsingData.esSamples : [], + a = t ? t.parsingData.esSamples : []; + if (!ne(this._initPTS)) { + if (t && a.length && (r = n = a[0].pts - t.info.inputTimescale * i), e && s.length) { + const t = e.info.inputTimescale; + e.info.timescale = t, r = Math.min(r, s.reduce((e, t) => { + var i = t.pts - e; + return i < -4294967296 ? Ie(e, t.pts) : 0 < i ? e : t.pts + }, s[0].pts) - t * i), n = Math.min(n, s[0].dts - t * i), this.observer.trigger(v.INIT_PTS_FOUND, { + initPTS: B(r, t) + }) + } + if (ne(r) && ne(n)) this._initPTS = r, this._initDTS = n; + else { + const e = new D(!1, "invalid initPTS or initDTS", $.InvalidInitTimestamp); + this.observer.trigger(x.INTERNAL_ERROR, e) + } + } + } + generateIS(e, t) { + const i = t ? t.parsingData.esSamples : [], + r = this.typeSupported; + let n, s = "audio/mp4"; + if (e && t && i.length) { + const i = t.info.inputTimescale; + t.info.timescale = i, e.info.timescale = e.config.samplerate; + const r = pe.initSegment([t, e]); + n = { + type: "audiovideo", + container: "video/mp4", + codec: `${t.config.codec},${e.config.codec}`, + initSegment: r + } + } else if (e) { + "mp3" === (e.info.timescale = e.config.samplerate, e.config.segmentCodec) && (r.mpeg ? (s = "audio/mpeg", e.config.codec = "") : r.mp3 && (e.config.codec = "mp3")); + const t = "mp3" === e.config.segmentCodec && r.mpeg ? new Uint8Array : pe.initSegment([e]); + n = { + type: "audio", + container: s, + codec: e.config.codec, + initSegment: t + } + } else if (t && i.length) { + const e = t.info.inputTimescale; + t.info.timescale = e; + const i = pe.initSegment([t]); + n = { + type: "video", + container: "video/mp4", + codec: t.config.codec, + initSegment: i + } + } + if (n) { + this.currentInitTrack = n; + const e = { + track: n + }; + this.observer.trigger(v.FRAG_PARSING_INIT_SEGMENT, e) + } else { + const e = new D(!1, "no audio/video samples found", $.NoAVSamplesFound); + this.observer.trigger(x.INTERNAL_ERROR, e) + } + } + remuxVideo(n, e, s, a, o) { + let l, d, u, c, h, t, p = 8, + i = n.parsingData.dropped; + const r = !s && this.config.forceKeyFrameOnDiscontinuity, + f = n.parsingData.esSamples, + m = n.info.inputTimescale, + g = [], + y = n.info.encrypted; + let v, S; + v = s ? this.nextAvcDts : Ie(f[0].dts, f[0].pts), f.forEach(function(e) { + e.pts = Ie(e.pts, v), e.dts = Ie(e.dts, v) + }), f.sort(function(e, t) { + var i = e.dts - t.dts, + r = e.pts - t.pts; + return i || r || e.id - t.id + }); + var b = f.findIndex(e => e.key); + f[b] && (S = f[b].pts), r && (0 < b ? (this.logger.warn(`Dropped ${b} out of ${f.length} video samples due to a missing keyframe`), f.splice(0, b), i += b) : -1 === b && (this.logger.warn(`No keyframe found out of ${f.length} video samples`), i += f.length)); + var T = f[0], + E = f[f.length - 1], + I = f.reduce((e, t) => Math.max(Math.min(e, t.pts - t.dts), -18e3), 0); + if (I < 0) { + this.logger.warn(`PTS < DTS detected in video samples, shifting DTS by ${Math.round(I/90)} ms to overcome this issue`); + for (let e = 0; e < f.length; e++) f[e].dts += I + } + var w = this.isSafari; + if (l = Math.round((E.dts - T.dts) / (f.length - 1)), c = Math.max(T.dts, 0), u = Math.max(T.pts, 0), ne(o) && (c = e * m, u = e * m), s) { + const n = c - v, + M = n > l, + s = n < -1; + (M || s) && (M ? this.logger.warn(`AVC: ${n}/90000 hole between fragments detected`) : this.logger.warn(`AVC: ${n}/90000 overlapping between fragments detected`)) + } + let A = 0, + O = 0; + var k = f.length; + for (let e = 0; e < k; e++) { + const M = f[e], + s = M.units, + a = s.length; + let t = 0; + for (let e = 0; e < a; e++) t += s[e].data.length; + O += t, A += a, M.length = t, M.dts = w ? c + e * l : Math.max(M.dts, c), M.pts = Math.max(M.pts, M.dts) + } + t = Math.max(E.dts, 0), h = Math.max(E.pts, 0, t), ne(o) && (t = e * m, h = e * m); + e = O + 4 * A + 8; + try { + d = new Uint8Array(e) + } catch (n) { + const M = new F(!1, `fail allocating video mdat ${e}`, $.FailedToAllocateVideoMdat, e); + return void this.observer.trigger(x.INTERNAL_ERROR, M) + } + const C = new DataView(d.buffer); + C.setUint32(0, e), d.set(pe.types.mdat, 4); + for (let t = 0; t < k; t++) { + const M = f[t], + s = M.units; + let e, i = 0; + const x = []; + let r = 0; + for (let e = 0, t = s.length; e < t; e++) { + const M = s[e], + a = M.data, + o = M.data.byteLength; + if (C.setUint32(p, o), p += 4, d.set(a, p), p += o, i += 4 + o, y) + if (o <= 48 || 1 !== M.type && 5 !== M.type) r += 4 + o; + else { + let e = o - 32; + e % 16 == 0 && (e -= 16), x.push([r + 36, e]), r = o - 32 - e + } + } + if (0 < r && x.push([r, 0]), w) e = Math.max(0, l * Math.round((M.pts - M.dts) / l)); + else { + if (t < k - 1) l = f[t + 1].dts - M.dts; + else { + const s = this.config, + o = M.dts - f[0 < t ? t - 1 : t].dts; + if (s.stretchShortVideoTrack) { + const n = s.maxBufferHole, + d = s.maxSeekHole, + c = Math.floor(Math.min(n, d) * m), + h = (a ? u + a * m : this.nextAudioPts) - M.pts; + h > c ? (l = h - o, l < 0 && (l = o)) : l = o + } else l = o + } + e = Math.round(M.pts - M.dts) + } + ne(o) && (e = 0, l = o * m), g.push({ + size: i, + duration: l, + cts: e, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: M.key ? 2 : 1, + isNonSync: M.key ? 0 : 1, + paddingValue: 0 + }, + keyTagInfo: M.keyTagInfo, + subsamples: x + }) + } + if (this.nextAvcDts = t + l, this.isVideoContiguous = !0, g.length && -1 < navigator.userAgent.toLowerCase().indexOf("chrome")) { + const n = g[0].flags; + n.dependsOn = 2, n.isNonSync = 0 + } + e = { + sequenceNumber: n.parsingData.sequenceNumber++, + id: n.info.id, + type: n.type, + encrypted: n.info.encrypted, + samples: g, + defaultPerSampleIVSize: 0 + }, e = pe.moof(c + this.config.audioPrimingDelay * m, e); + n.parsingData.esSamples = []; + const D = new Uint8Array(e.byteLength + d.byteLength); + return D.set(e), D.set(d, e.byteLength), { + data1: D, + startPTS: B(u / m, m), + endPTS: B((h + l) / m, m), + startDTS: B(c / m, m), + endDTS: B(this.nextAvcDts / m, m), + type: "video", + dropped: i, + framesWithoutIDR: b, + firstKeyframePts: B(S / m, m) + } + } + remuxAudio(r, i, n, e, s) { + const a = r.info.inputTimescale, + o = a / r.info.timescale, + l = ("aac" === r.config.segmentCodec ? 1024 : "mp3" === r.config.segmentCodec ? 1152 : 1536) * o, + d = "mp3" === r.config.segmentCodec && this.typeSupported.mpeg, + u = [], + c = r.info.encrypted, + t = this._initPTS + i * a; + let h, p, f, m, g, y, v, S, b, T, E, I, w, A, O = d ? 0 : 8; + const k = new q, + C = r.parsingData.esSamples; + if (A = this.nextAudioPts, (n = n || C.length && A && (e && Math.abs(t - A) < 9e3 || Math.abs(Ie(C[0].pts - A, t)) < 20 * l)) || (A = Ie(C[0].pts, this._initPTS)), C.forEach(function(e) { + e.pts = e.dts = Ie(e.pts, A) + }), e && "aac" === r.config.segmentCodec) + for (let t = 0, i = A; t < C.length;) { + const M = C[t]; + T = M.pts; + const o = T - i, + d = Math.abs(1e3 * o / a); + if (o <= -l) this.logger.warn(`Dropping 1 audio frame @ ${(i/a).toFixed(3)}s due to ${d} ms overlap.`), C.splice(t, 1), r.parsingData.len -= M.unit.length; + else if (o >= l && d < 1e4 && i) { + const d = Math.round(o / l); + this.logger.warn(`Injecting ${d} audio frame @ ${(i/a).toFixed(3)}s due to ${Math.round(1e3*o/a)} ms gap.`); + for (let e = 0; e < d; e++) w = Math.max(i, 0), I = re(r.config.codec, r.config.channelCount), I || (this.logger.warn("Unable to get silent frame for given audio codec; duplicating last frame instead."), I = M.unit.subarray(0)), C.splice(t, 0, { + unit: I, + pts: w, + dts: w, + keyTagInfo: s + }), r.parsingData.len += I.length, i += l, t += 1; + M.pts = M.dts = i, i += l, t += 1 + } else i += l, M.pts = M.dts = 0 === t ? A : C[t - 1].pts + l, t += 1 + } + for (let e = 0, t = C.length; e < t; e++) { + if (p = C[e], m = p.unit, T = p.pts, E = p.dts, void 0 !== b) f.duration = Math.round((E - b) / o); + else { + const i = Math.round(1e3 * (T - A) / a); + let t = 0; + if (n && "aac" === r.config.segmentCodec && i) { + if (0 < i && i < 1e4) t = Math.round((T - A) / l), 0 < t && (I = re(r.config.codec, r.config.channelCount), I = I || m.subarray(0), r.parsingData.len += t * I.length); + else if (i < -12) { + r.parsingData.len -= m.byteLength; + continue + } + T = E = A + } + if (v = Math.max(0, T), S = Math.max(0, E), !(0 < r.parsingData.len)) return; { + const i = d ? r.parsingData.len : r.parsingData.len + 8; + try { + g = new Uint8Array(i) + } catch (r) { + const n = new F(!1, `fail allocating audio mdat ${i}`, $.FailedToAllocateAudioMdat, i); + return void this.observer.trigger(x.INTERNAL_ERROR, n) + } + d || (h = new DataView(g.buffer), h.setUint32(0, i), g.set(pe.types.mdat, 4)) + } + for (let e = 0; e < t; e++) w = T - (t - e) * l, I = re(r.config.codec, r.config.channelCount), I || (this.logger.warn("Unable to get silent frame for given audio codec; duplicating this frame instead."), I = m.subarray(0)), g.set(I, O), O += I.byteLength, f = { + size: I.byteLength, + cts: 0, + duration: 1024, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: 1, + paddingValue: 0, + isNonSync: 0 + }, + keyTagInfo: p.keyTagInfo, + subsamples: c ? [ + [I.byteLength, 0] + ] : [] + }, u.push(f) + } + g.set(m, O); + const M = m.byteLength; + O += M; + const s = []; + if (c) + if ("ec3" === r.config.segmentCodec) { + let e = 0; + for (; e < m.byteLength;) { + const i = 2 * (k.bsReadAndUpdate(m, { + byteOffset: e + 2, + usedBits: 5 + }, 11) + 1); + e += i; + const n = Math.min(i, 16); + s.push([n, i - n]) + } + } else { + const r = Math.min(M, 16); + s.push([r, M - r]) + } f = { + size: M, + cts: 0, + duration: 0, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: 1, + paddingValue: 0, + isNonSync: 0 + }, + keyTagInfo: p.keyTagInfo, + subsamples: s + }, u.push(f), b = E + } + let D = 0; + e = u.length; + if (2 <= e && (D = u[e - 2].duration, f.duration = D), e) { + if (this.nextAudioPts = T + o * D, r.parsingData.len = 0, d) y = new Uint8Array; + else { + const i = { + sequenceNumber: r.parsingData.sequenceNumber++, + id: r.info.id, + type: r.type, + encrypted: r.info.encrypted, + samples: u, + defaultPerSampleIVSize: 0 + }; + y = pe.moof((S + this.config.audioPrimingDelay * a) / o, i) + } + const i = new Uint8Array(y.byteLength + g.byteLength); + return i.set(y), i.set(g, y.byteLength), r.parsingData.esSamples = [], { + data1: i, + startPTS: B(v / a, a), + endPTS: B(this.nextAudioPts / a, a), + startDTS: B(S / a, a), + endDTS: B((E + o * D) / a, a), + type: "audio" + } + } + return null + } + remuxEmptyAudio(t, e, i, r, n, s) { + var a = t.info.inputTimescale, + o = a / (t.config.samplerate || a), + l = this.nextAudioPts, + d = (void 0 !== l ? l : S(n.startDTS) * a) + this._initDTS, + a = S(n.endDTS) * a + this._initDTS, + u = 1024 * o, + c = Math.ceil((a - d) / u), + h = re(t.config.codec, t.config.channelCount); + if (this.logger.warn("remux empty Audio"), !h) return this.logger.error("Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec!"), null; + const p = []; + for (let e = 0; e < c; e++) { + const i = d + e * u; + p.push({ + unit: h, + pts: i, + dts: i, + keyTagInfo: s + }), t.parsingData.len += h.length + } + return t.parsingData.esSamples = p, this.remuxAudio(t, e, i, r, s) + } + remuxID3(t, e) { + var i = t.id3Samples.length; + let r; + var n = t.inputTimescale; + if (i) { + for (let e = 0; e < i; e++) r = t.id3Samples[e], r.pts = r.pts / n, r.dts = r.dts / n; + e.id3Samples = t.id3Samples + } + t.id3Samples = [] + } + remuxText(t, e) { + t.captionSamples.sort(function(e, t) { + return e.pts - t.pts + }); + var i = t.captionSamples.length; + let r; + var n = t.inputTimescale; + if (i) { + for (let e = 0; e < i; e++) r = t.captionSamples[e], r.pts = r.pts / n; + e.captionData || (e.captionData = {}), e.captionData.ts = t.captionSamples + } + t.captionSamples = [] + } + } + + function Ie(e, t) { + var i; + if (void 0 === t) return e; + for (i = t < e ? -8589934592 : 8589934592; 4294967296 < Math.abs(e - t);) e += i; + return e + } + e = "undefined" != typeof globalThis ? globalThis : "undefined" != typeof window ? window : "undefined" != typeof global ? global : void 0 !== Jy ? Jy : {}; + + function we(e) { + try { + return JSON.stringify(e) + } catch (e) { + return '"[Circular]"' + } + } + + function Ae(e, t, i) { + var r = i && i.stringify || we; + if ("object" == typeof e && null !== e) { + var n = t.length + 1; + if (1 === n) return e; + var s = new Array(n); + s[0] = r(e); + for (var a = 1; a < n; a++) s[a] = r(t[a]); + return s.join(" ") + } + if ("string" != typeof e) return e; + var o = t.length; + if (0 === o) return e; + for (var l = "", d = 0, u = -1, c = e && e.length || 0, h = 0; h < c;) { + if (37 === e.charCodeAt(h) && h + 1 < c) { + switch (u = -1 < u ? u : 0, e.charCodeAt(h + 1)) { + case 100: + if (o <= d) break; + if (u < h && (l += e.slice(u, h)), null == t[d]) break; + l += Number(t[d]), u = h += 2; + break; + case 79: + case 111: + case 106: + if (o <= d) break; + if (u < h && (l += e.slice(u, h)), void 0 === t[d]) break; + var p = typeof t[d]; + if ("string" == p) { + l += "'" + t[d] + "'", u = h + 2, h++; + break + } + if ("function" == p) { + l += t[d].name || "", u = h + 2, h++; + break + } + l += r(t[d]), u = h + 2, h++; + break; + case 115: + if (o <= d) break; + u < h && (l += e.slice(u, h)), l += String(t[d]), u = h + 2, h++; + break; + case 37: + u < h && (l += e.slice(u, h)), l += "%", u = h + 2, h++ + }++d + }++h + } + return -1 === u ? e : (u < c && (l += e.slice(u)), l) + } + var Oe = De; + const ke = function() { + function t(e) { + return void 0 !== e && e + } + try { + return "undefined" != typeof globalThis || Object.defineProperty(Object.prototype, "globalThis", { + get: function() { + return delete Object.prototype.globalThis, this.globalThis = this + }, + configurable: !0 + }), globalThis + } catch (e) { + return t(Jy) || t(window) || t(this) || {} + } + }().console || {}, + Ce = { + mapHttpRequest: Le, + mapHttpResponse: Le, + wrapRequestSerializer: _e, + wrapResponseSerializer: _e, + wrapErrorSerializer: _e, + req: Le, + res: Le, + err: function(e) { + const t = { + type: e.constructor.name, + msg: e.message, + stack: e.stack + }; + for (const i in e) void 0 === t[i] && (t[i] = e[i]); + return t + } + }; + + function De(s) { + (s = s || {}).browser = s.browser || {}; + const a = s.browser.transmit; + if (a && "function" != typeof a.send) throw Error("pino: transmit option must have a send function"); + const e = s.browser.write || ke; + s.browser.write && (s.browser.asObject = !0); + const o = s.serializers || {}, + l = (t = s.browser.serialize, i = o, Array.isArray(t) ? t.filter(function(e) { + return "!stdSerializers.err" !== e + }) : !0 === t && Object.keys(i)); + var t, i; + let r = s.browser.serialize; + Array.isArray(s.browser.serialize) && -1 < s.browser.serialize.indexOf("!stdSerializers.err") && (r = !1), "function" == typeof e && (e.error = e.fatal = e.warn = e.info = e.debug = e.trace = e), !1 === s.enabled && (s.level = "silent"); + const n = s.level || "info", + d = Object.create(e); + d.log || (d.log = Ne), Object.defineProperty(d, "levelVal", { + get: function() { + return "silent" === this.level ? 1 / 0 : this.levels.values[this.level] + } + }), Object.defineProperty(d, "level", { + get: function() { + return this._level + }, + set: function(e) { + if ("silent" !== e && !this.levels.values[e]) throw Error("unknown level " + e); + this._level = e, Me(u, d, "error", "log"), Me(u, d, "fatal", "error"), Me(u, d, "warn", "error"), Me(u, d, "info", "log"), Me(u, d, "debug", "log"), Me(u, d, "trace", "log") + } + }); + const u = { + transmit: a, + serialize: l, + asObject: s.browser.asObject, + levels: ["error", "fatal", "warn", "info", "debug", "trace"], + timestamp: "function" == typeof(i = s).timestamp ? i.timestamp : !1 === i.timestamp ? Fe : Be + }; + return d.levels = De.levels, d.level = n, d.setMaxListeners = d.getMaxListeners = d.emit = d.addListener = d.on = d.prependListener = d.once = d.prependOnceListener = d.removeListener = d.removeAllListeners = d.listeners = d.listenerCount = d.eventNames = d.write = d.flush = Ne, d.serializers = o, d._serialize = l, d._stdErrSerialize = r, d.child = function(t) { + if (!t) throw new Error("missing bindings for child Pino"); + var i, r, e = t.serializers; + + function n(e) { + this._childLevel = 1 + (0 | e._childLevel), this.error = Pe(e, t, "error"), this.fatal = Pe(e, t, "fatal"), this.warn = Pe(e, t, "warn"), this.info = Pe(e, t, "info"), this.debug = Pe(e, t, "debug"), this.trace = Pe(e, t, "trace"), i && (this.serializers = i, this._serialize = r), a && (this._logEvent = Re([].concat(e._logEvent.bindings, t))) + } + return l && e && (i = Object.assign({}, o, e), r = !0 === s.browser.serialize ? Object.keys(i) : l, delete t.serializers, xe([t], r, i, this._stdErrSerialize)), n.prototype = this, new n(this) + }, a && (d._logEvent = Re()), d + } + + function Me(e, t, i, r) { + var n, s, a, o, l = Object.getPrototypeOf(t); + t[i] = !(t.levelVal > t.levels.values[i]) && (l[i] || ke[i] || ke[r]) || Ne, s = t, a = i, !(n = e).transmit && s[a] === Ne || (s[a] = (o = s[a], function() { + const e = n.timestamp(), + t = new Array(arguments.length), + i = Object.getPrototypeOf && Object.getPrototypeOf(this) === ke ? ke : this; + for (var r = 0; r < t.length; r++) t[r] = arguments[r]; + if (n.serialize && !n.asObject && xe(t, this._serialize, this.serializers, this._stdErrSerialize), n.asObject ? o.call(i, function(e, t, i, r) { + e._serialize && xe(i, e._serialize, e.serializers, e._stdErrSerialize); + const n = i.slice(); + let s = n[0]; + const a = {}; + r && (a.time = r), a.level = De.levels.values[t]; + let o = 1 + (0 | e._childLevel); + if (o < 1 && (o = 1), null !== s && "object" == typeof s) { + for (; o-- && "object" == typeof n[0];) Object.assign(a, n.shift()); + s = n.length ? Ae(n.shift(), n) : void 0 + } else "string" == typeof s && (s = Ae(n.shift(), n)); + return void 0 !== s && (a.msg = s), a + }(this, a, t, e)) : o.apply(i, t), n.transmit) { + const o = n.transmit.level || s.level, + i = De.levels.values[o], + r = De.levels.values[a]; + r < i || function(e, t, i) { + const r = t.send, + n = t.ts, + s = t.methodLevel, + a = t.methodValue, + o = t.val, + l = e._logEvent.bindings; + xe(i, e._serialize || Object.keys(e.serializers), e.serializers, void 0 === e._stdErrSerialize || e._stdErrSerialize), e._logEvent.ts = n, e._logEvent.messages = i.filter(function(e) { + return -1 === l.indexOf(e) + }), e._logEvent.level.label = s, e._logEvent.level.value = a, r(s, e._logEvent, o), e._logEvent = Re(l) + }(this, { + ts: e, + methodLevel: a, + methodValue: r, + transmitLevel: o, + transmitValue: De.levels.values[n.transmit.level || s.level], + send: n.transmit.send, + val: s.levelVal + }, t) + } + })) + } + + function xe(e, t, i, r) { + for (const n in e) + if (r && e[n] instanceof Error) e[n] = De.stdSerializers.err(e[n]); + else if ("object" == typeof e[n] && !Array.isArray(e[n])) + for (const r in e[n]) t && -1 < t.indexOf(r) && r in i && (e[n][r] = i[r](e[n][r])) + } + + function Pe(i, r, n) { + return function() { + try{ const e = new Array(1 + arguments.length); + e[0] = r; + for (var t = 1; t < e.length; t++) + e[t] = arguments[t - 1]; + return i[n].apply(this, e) } catch (e){} + } + } + + function Re(e) { + return { + ts: 0, + messages: [], + bindings: e || [], + level: { + label: "", + value: 0 + } + } + } + + function Le() { + return {} + } + + function _e(e) { + return e + } + + function Ne() {} + + function Fe() { + return !1 + } + + function Be() { + return Date.now() + } + let Ue; + + function $e(e = {}) { + return Object.assign(Object.assign({}, e), { + customLevels: Object.assign(Object.assign({}, e.customLevels || {}), { + qe: 35 + }) + }) + } + De.levels = { + values: { + fatal: 60, + error: 50, + warn: 40, + info: 30, + debug: 20, + trace: 10 + }, + labels: { + 10: "trace", + 20: "debug", + 30: "info", + 40: "warn", + 50: "error", + 60: "fatal" + } + }, De.stdSerializers = Ce, De.stdTimeFunctions = Object.assign({}, { + nullTime: Fe, + epochTime: Be, + unixTime: function() { + return Math.round(Date.now() / 1e3) + }, + isoTime: function() { + return new Date(Date.now()).toISOString() + } + }); + const Ve = () => {}; + + function Ke(e, t) { + const i = (e = t in e ? e : console)[t] || e.log; + return i ? i.bind(e) : Ve + } + + function qe(r, n, e) { + var { + time: s, + sessionId: t, + critical: i, + name: a, + msg: o + } = e; + let l = ""; + if ("data" in e) try { + const r = [], + n = []; + l = JSON.stringify(e.data, (e, t) => { + if ("object" == typeof t && null !== t) { + var i = n.indexOf(t); + if (-1 !== i) return `[Circular object reference: '${r[i]}']`; + r.push(e), n.push(t) + } + return t + }) + } catch (r) { + l = `Log serialization error: "${r}"` + } + r(`${function(){const e=new Date(s),t=e.getTimezoneOffset(),i=He(Math.floor(Math.abs(t)/60)),r=He(Math.abs(t)%60);let n=t<=0?"UTC+"+i:"UTC-"+i;return n=r?n+":"+r:n,e.getFullYear()+"-"+He(e.getMonth()+1)+"-"+He(e.getDate())+" "+He(e.getHours())+":"+He(e.getMinutes())+":"+He(e.getSeconds())+"."+(e.getMilliseconds()/1e3).toFixed(3).slice(2,5)+" "+n}()}| [SessionID: ${t}] | [${n}] >${i?" [QE Critical]":""} [${a}] ${o||""} ${l}`) + } + + function He(e) { + return e < 10 ? "0" + e : e.toString() + } + + function je(e, t = 1 / 0) { + if (!e) return ""; + const i = new Uint8Array(e); + let r, n = ""; + for (r = 0; r < i.length && r < t; r++) { + let e = i[r].toString(16); + e.length < 2 && (e = "0" + e), n += e + } + return n + } + const Qe = () => (Ue || (Ue = Oe($e()).child({ + name: "hls" + }), Ue.qe = e => Ue.info(e), Ue.warn("getLogger called without hls object instantiated, returning a logger that is not configured")), Ue), + We = { + bin2str: e => String.fromCharCode.apply(null, Array.from(e)), + readUint16(e, t) { + t = e[t] << 8 | e[t + 1]; + return t < 0 ? 65536 + t : t + }, + readSint32: (e, t) => e[t] << 24 | e[t + 1] << 16 | e[t + 2] << 8 | e[t + 3], + readUint32(e, t) { + t = We.readSint32(e, t); + return t < 0 ? 4294967296 + t : t + }, + writeUint32(e, t, i) { + e[t] = i >> 24, e[t + 1] = i >> 16 & 255, e[t + 2] = i >> 8 & 255, e[t + 3] = 255 & i + }, + readUint64(e, t) { + var i = We.readUint32(e, t); + return i *= Math.pow(2, 32), i += We.readUint32(e, t + 4) + }, + writeUint64(e, t, i) { + var r = Math.pow(2, 32) - 1, + n = Math.floor(i / (1 + r)), + r = Math.floor(i % (1 + r)); + We.writeUint32(e, t, n), We.writeUint32(e, t + 4, r) + }, + findBox(e, t) { + let i, r, n, s, a, o = []; + if (!t.length) return []; + for (i = 0; i < e.byteLength;) r = We.readUint32(e, i), n = We.bin2str(e.subarray(i + 4, i + 8)), s = 1 < r ? i + r : e.byteLength, n === t[0] && (1 === t.length ? o.push(e.subarray(i + 8, s)) : (a = We.findBox(e.subarray(i + 8, s), t.slice(1))).length && (o = o.concat(a))), i = s; + return o + }, + findBoxWithOffset(e, t, i, r) { + let n, s, a, o, l, d = []; + if (!i.length) return []; + for (n = 0; n < e.byteLength;) s = We.readUint32(e, n), a = We.bin2str(e.subarray(n + 4, n + 8)), o = 1 < s ? n + s : e.byteLength, a === i[0] && (r && r.push({ + offset: n + t, + type: a, + size: s + }), 1 === i.length ? d.push({ + offset: n + t, + type: a, + data: e.subarray(n + 8, o), + boxSize: s, + walkedPath: r ? r.slice(0) : void 0 + }) : (l = We.findBoxWithOffset(e.subarray(n + 8, o), n + t + 8, i.slice(1), r ? r.slice(0) : void 0)).length && (d = d.concat(l), r = r ? r.slice(0, -1) : void 0)), n = o; + return d + } + }, + Ge = { + name: "MP4EncryptionRemuxer" + }; + class ze extends h { + constructor(e, t, i, r, n) { + super(e, t, n) + } + static _isCommonEncryptionInternal(e) { + return Boolean(e && !("NONE" === e || "AES-128" === e)) + } + static remuxInitSegment(c, h, p, f) { + if (!p) return c; + let e = c; + if (ze._isCommonEncryptionInternal(p.method)) { + const m = p.keyId; + let u = !1; + const g = []; + if (We.findBoxWithOffset(c, 0, ["moov", "trak"]).forEach(t => { + const o = t.data; + let l, i = 0; + const d = We.findBoxWithOffset(o, 0, ["mdia", "minf", "stbl", "stsd"], [])[0], + e = d.data.subarray(8); + let r = !0, + n = We.findBoxWithOffset(e, d.offset + 16, ["enca"]); + 0 === n.length && (r = !1, n = We.findBoxWithOffset(e, d.offset + 16, ["encv"])), n.forEach(s => { + let e = null, + a = null; + i = r ? (e = s.data.subarray(28), l = "audio", d.offset + 16 + 8 + 28) : (e = s.data.subarray(78), l = "video", d.offset + 16 + 8 + 78), e && We.findBoxWithOffset(e, i, ["sinf"]).forEach(e => { + const t = e.data, + i = We.findBox(t, ["frma"])[0], + r = We.findBox(t, ["schm"])[0]; + if (i) + if (r) { + var n = We.bin2str(r.subarray(4, 8)); + if ("aac " === We.bin2str(i.subarray(0, 4)) && (ge.types || ge.init(), i.set(ge.types.mp4a, 0)), "cbcs" === n || "cenc" === n) { + f && f.push(t); + const e = We.findBox(t, ["schi", "tenc"])[0]; + if (e) { + const h = 8; + e.subarray(8, 24), e.set(m, 8); + const f = e[6], + t = e[7]; + if (1 === f && 0 === t) { + const h = e[24]; + 0 < h && p.iv && h === p.iv.length && e.set(p.iv, 25) + } + } + } else if ("cbc2" === n) { + u = !0, ge.types || ge.init(); + const h = We.findBoxWithOffset(t, 0, ["frma"])[0], + f = ge.box(ge.types.schi, ge.tenc(p, l)), + m = ge.box(ge.types.sinf, t.subarray(h.offset, h.boxSize), ge.schm(), f); + a = ge.box(ge.types.trak, o.subarray(0, e.offset), m, o.subarray(e.offset + e.boxSize)); + const i = a.subarray(8), + r = m.byteLength - e.boxSize; + d.walkedPath && (d.walkedPath.push({ + type: "stsd", + offset: s.offset, + size: s.boxSize + }), d.walkedPath.forEach(e => { + We.writeUint32(i, e.offset, e.size + r) + })) + } + } else h.error(Ge, "missing schm box"); + else h.error(Ge, "missing frma box") + }), a = a || c.subarray(t.offset, t.offset + t.boxSize), g.push(a) + }); + var s = We.findBoxWithOffset(o, 0, ["edts"])[0]; + s && (ge.types || ge.init(), o.set(ge.types.free, s.offset + 4)) + }), u) { + const p = ze.remuxCbc2InitSegment(c, g, h); + e = p || c + } + } + return e + } + static remuxCbc2InitSegment(i, r, n) { + const s = We.findBoxWithOffset(i, 0, ["ftyp"])[0]; + if (s) { + const a = We.findBoxWithOffset(i, s.boxSize, ["moov"])[0]; + let e = [], + t = 0; + for (; t < a.data.byteLength;) { + const i = We.readUint32(a.data, t), + n = We.bin2str(a.data.subarray(t + 4, t + 8)), + s = 1 < i ? t + i : a.data.byteLength; + "trak" === n ? r && (e = e.concat(r), r = void 0) : e.push(a.data.subarray(t, s)), t = s + } + const o = ge.box(ge.types.moov, ...e), + l = new Uint8Array(s.boxSize + o.byteLength); + return l.set(i.subarray(0, s.boxSize)), l.set(o, s.boxSize), l + } + n.error(Ge, "no ftyp found") + } + static remuxOverflowSegment(i, e) { + ge.types || ge.init(); + const t = We.findBoxWithOffset(i, 0, ["moof", "traf", "tfdt"], []); + let r, n = i.byteLength; + if (t.forEach(e => { + 0 === e.data[0] && (n += 4) + }), n > i.byteLength) { + r = new Uint8Array(n); + let e = 0, + t = 0; + for (; t < i.byteLength;) { + const n = We.readUint32(i, t), + s = We.bin2str(i.subarray(t + 4, t + 8)), + a = 1 < n ? t + n : i.byteLength; + if ("moof" === s) { + const n = ze.remuxOverflowMoof(i.subarray(t + 8, a)); + r.set(n, e), e += n.byteLength + } else r.set(i.subarray(t, a), e), e += n; + t = a + } + } else e.warn(Ge, "no increase in size"); + return r || i + } + static remuxOverflowMoof(e) { + let t = 0; + const i = []; + for (; t < e.byteLength;) { + const r = We.readUint32(e, t); + if ("traf" === We.bin2str(e.subarray(t + 4, t + 8))) { + const s = ze.remuxOverflowTraf(e.subarray(t + 8, t + r)); + i.push(s) + } else i.push(e.subarray(t, t + r)); + t = 1 < r ? t + r : e.byteLength + } + const r = ge.box(ge.types.moof, ...i), + s = r.byteLength - e.byteLength - 8; + return We.findBoxWithOffset(r, 0, ["moof", "traf", "trun"], []).forEach(e => { + var t; + 0 != (1 & e.data[3]) && (t = We.readUint32(e.data, 8), We.writeUint32(e.data, 8, t + s)) + }), We.findBoxWithOffset(r, 0, ["moof", "traf", "saio"], []).forEach(t => { + const i = 1 & t.data[0]; + let r = 4; + 1 & t.data[3] && (r += 8); + var n = We.readUint32(t.data, r); + if (r += 4, i) + for (let e = 0; e < n; e++) { + const i = We.readUint64(t.data, r); + We.writeUint64(t.data, r, i + s), r += 8 + } else + for (let e = 0; e < n; e++) { + const i = We.readUint32(t.data, r); + We.writeUint32(t.data, r, i + s), r += 4 + } + }), r + } + static remuxOverflowTraf(e) { + let t = 0; + const i = []; + for (; t < e.byteLength;) { + var r, n = We.readUint32(e, t); + "tfdt" === We.bin2str(e.subarray(t + 4, t + 8)) && 0 === e[t + 8] ? (r = We.readUint32(e, t + 12), r = ge.box(ge.types.tfdt, new Uint8Array([1, 0, 0, 0, 0, 0, 0, 0, r >> 24, r >> 16 & 255, r >> 8 & 255, 255 & r])), i.push(r)) : i.push(e.subarray(t, t + n)), t = 1 < n ? t + n : e.byteLength + } + return ge.box(ge.types.traf, ...i) + } + remuxText(e, t) { + e.captionSamples.sort(function(e, t) { + return e.pts - t.pts + }), e.captionSamples.length && (t.captionData || (t.captionData = {}), t.captionData.mp4 = e.captionSamples), e.captionSamples = [] + } + remuxIFrame(e, t, i, r, n) { + if (!t.samples || !t.samples.length || !t.samples[0].data) return null; + let s; + const a = ge.moof(e * t.timescale, t), + o = new Uint8Array(a.byteLength + t.samples[0].data.byteLength + 8); + o.set(a), We.writeUint32(o, a.byteLength, t.samples[0].data.byteLength + 8), o.set(ge.types.mdat, a.byteLength + 4), o.set(t.samples[0].data, a.byteLength + 8), t.sequenceNumber++; + var l = t.timescale, + d = B(e + r, l), + l = B(e, l); + let u, c; + u = i ? (s = "audiovideo", i.sequenceNumber = t.sequenceNumber, t.sequenceNumber++, c = Te.getSegment(i, i.sequenceNumber, e * i.info.timescale, r), y(c.endTs, d)) : (s = "video", d); + n = { + data1: o, + data2: null == c ? void 0 : c.silentFragData, + startPTS: l, + startDTS: l, + endPTS: u, + endDTS: u, + type: s, + dropped: 0, + track: n + }; + this.observer.trigger(v.FRAG_PARSING_DATA, n), this.observer.trigger(v.FRAG_PARSED) + } + remuxEmsgAndRawData(e, t, i, r, n, s, a, o, l, d) { + let u; + t && r ? u = "audiovideo" : t ? u = "audio" : r && (u = "video"); + const c = Math.max(i, e), + h = { + data1: l, + track: d, + startPTS: B(a, o), + startDTS: B(a, o), + endPTS: void 0, + endDTS: void 0, + type: u, + dropped: 0 + }; + return c && (h.endPTS = B(a + c, o), h.endDTS = B(a + c, o)), n && n.captionSamples.length && this.remuxText(n, h), s && 0 < s.id3Samples.length && this.remuxID3(s, h), this.observer.trigger(v.FRAG_PARSING_DATA, h), this.observer.trigger(v.FRAG_PARSED), h + } + remuxID3(t, e) { + let i; + var r = t.id3Samples.length; + if (r) { + for (let e = 0; e < r; e++) i = t.id3Samples[e], i.pts = i.pts - 10, i.dts = i.dts - 10; + e.id3Samples = [...t.id3Samples] + } + t.id3Samples = [] + } + } + const Xe = Math.pow(2, 32) - 1, + Ye = Math.pow(2, 20) - 1, + Je = { + name: "MP4Demuxer" + }; + class Ze extends u { + constructor(e, t, i, r, n) { + super(e, t, i, {}, n), this.mp4Remuxer = t, this.audioPrimingDelay = i.audioPrimingDelay + } + resetTimeStamp(e) { + ne(e) ? this.initData.audio && !this.initData.video ? this.initPtsTs = { + baseTime: Math.round(e * this.initData.audio.timescale / 9e4), + timescale: this.initData.audio.timescale + } : this.initPtsTs = { + baseTime: e, + timescale: 9e4 + } : this.initPtsTs = void 0 + } + static isHEVCFlavor(e) { + if (!e) return !1; + var t = e.indexOf("."), + t = t < 0 ? e : e.substring(0, t); + return "hvc1" === t || "hev1" === t || "chvc" === t || "qhvc" === t || "qhev" === t || "muxa" === t || "dvh1" === t || "dvhe" === t || "cdh1" === t || "qdh1" === t || "qdhe" === t + } + resetInitSegment(t, i, r) { + if (this._silentAudioTrack = void 0, t && t.byteLength) { + var n = ze.remuxInitSegment(t, this.logger, r), + s = this.initData = Ze.parseInitSegment(n); + let e; + s.foundLargeTimescale && this.logger.warn(Je, "large timescale found, will check for 32 bit tfdts"); + var a = s.audioCodec, + r = s.videoCodec; + if (s.audio && s.video ? e = { + type: "audiovideo", + container: "video/mp4", + codec: a + "," + r, + initSegment: n + } : (s.audio && a && (e = { + type: "audio", + container: "audio/mp4", + codec: a, + initSegment: n + }), s.video && r && (e = { + type: "video", + container: "video/mp4", + codec: r, + initSegment: n + })), s.video) { + const o = s.video, + l = t.subarray(o.trakOffset, o.trakOffset + o.trakSize); + this._videoTrack = Object.assign(Object.assign({}, o), { + info: { + id: o.id, + timescale: o.timescale, + duration: i + }, + trakData: l, + sequenceNumber: 0, + samples: [] + }), this.trySEICaptions = !be.isVP09(r), this._captionTrack = Object.assign(Object.assign({}, s.caption), { + sequenceNumber: 0, + captionSamples: [] + }) + } + s.audio && a && (this._audioTrack = Object.assign({}, s.audio)), s.caption && (this.trySEICaptions = !1, this._captionTrack = Object.assign(Object.assign({}, s.caption), { + sequenceNumber: 0, + captionSamples: [] + })), this.remuxedInitDataTrack = e; + s = { + track: e + }; + this.observer.trigger(v.FRAG_PARSING_INIT_SEGMENT, s) + } + } + static probe(e, t) { + return 0 < We.findBox(e.subarray(0, Math.min(e.length, 512e3)), ["moof"]).length + } + static parseHvcC(e) { + let t; + var i, r; + return e ? 1 === (i = e[0]) ? (r = e[1], t = { + profileSpace: r >> 6, + tierFlag: (32 & r) >> 5 ? "H" : "L", + profileIDC: 31 & r, + profileCompat: We.readUint32(e, 2), + constraintIndicator: e.subarray(6, 12), + levelIDC: e[12] + }) : Qe().warn(Je, `Unhandled version ${i} in hvcC box`) : Qe().warn(Je, "No hvcC box"), t + } + static hvcCToCodecString(t, i) { + const r = t + "." + (i.profileSpace ? String.fromCharCode(i.profileSpace + "A" - 1) : "") + i.profileIDC + "." + i.profileCompat.toString(16).toUpperCase() + "." + i.tierFlag + i.levelIDC; + let n = ""; + for (let e = i.constraintIndicator.length - 1; 0 <= e; --e) { + const r = i.constraintIndicator[e]; + if (0 !== r || "" !== n) { + const t = r.toString(16).toUpperCase(); + n = "." + ("" === n ? t : t + n) + } + } + return r + n + } + static parseDvcC(e) { + let t; + return e ? t = { + versionMajor: e[0], + versionMinor: e[1], + profile: e[2] >> 1 & 127, + level: e[2] << 5 & 32 | e[3] >> 3 & 31 + } : Qe().warn(Je, "No dvcC box"), t + } + static dvcCToCodecString(e, t) { + return e + "." + Ze.checkAndAddLeadingZero(t.profile) + "." + Ze.checkAndAddLeadingZero(t.level) + } + static parseVpcC(e) { + let t; + return e ? t = { + profile: e[4], + level: e[5], + bitDepth: e[6] >> 4 & 15 + } : Qe().warn(Je, "No vpcC box"), t + } + static vpcCToCodecString(e, t) { + return e + "." + Ze.checkAndAddLeadingZero(t.profile) + "." + Ze.checkAndAddLeadingZero(t.level) + "." + Ze.checkAndAddLeadingZero(t.bitDepth) + } + static checkAndAddLeadingZero(e) { + return (e < 10 ? "0" : "") + e + } + static parseInitSegment(e) { + const c = { + foundLargeTimescale: !1, + tracksById: {} + }; + return We.findBoxWithOffset(e, 0, ["moov", "trak"]).forEach(t => { + const i = t.data, + r = We.findBox(i, ["tkhd"])[0]; + if (r) { + var n = 0 === (a = r[0]) ? 12 : 20, + s = We.readUint32(r, n), + e = We.findBox(i, ["mdia", "mdhd"])[0]; + if (e) { + var a, o = 0 === (a = e[0]) ? 12 : 20, + n = We.readUint32(e, o); + o += 4, 1e6 <= n && (c.foundLargeTimescale = !0); + const l = 0 === a ? We.readUint32(e, o) : 0, + d = We.findBox(i, ["mdia", "hdlr"])[0]; + if (d) { + const r = We.bin2str(d.subarray(8, 12)), + u = { + soun: "audio", + vide: "video", + clcp: "caption" + } [r] || r; + if (u) { + const r = We.findBox(i, ["mdia", "minf", "stbl", "stsd"]); + if (r.length) { + const i = r[0]; + We.bin2str(i.subarray(12, 16)); + o = Ze.parseStsd(i); + let e; + if ("caption" === u) { + const t = Object.assign({ + id: s, + type: u, + timescale: n, + duration: l, + isTimingTrack: !1, + sequenceNumber: 0, + captionSamples: [] + }, o); + c.caption = t, e = t + } else { + const i = Object.assign({ + id: s, + type: u, + timescale: n, + duration: l, + isTimingTrack: !0, + trakOffset: t.offset, + trakSize: t.boxSize, + sequenceNumber: 0, + samples: [], + fragmentDuration: 0 + }, o); + "video" === u ? (c.video = i, c.videoCodec = i.codec) : (c.audio = i, c.audioCodec = i.codec), e = i + } + c.tracksById[s] = e + } + } + } + } + } + }), We.findBoxWithOffset(e, 0, ["moov", "mvex", "trex"]).forEach(e => { + var t = e.data, + e = We.readUint32(t, 4), + t = We.readUint32(t, 16); + c.tracksById[e].defaultSampleSize = t + }), c + } + static parseStsd(e) { + let r, t; + const i = e.subarray(8); + let n = We.bin2str(i.subarray(4, 8)), + s = null, + a = null; + "enca" === n ? (s = We.findBox(i, ["enca"])[0], a = s.subarray(28)) : "encv" === n && (s = We.findBox(i, ["encv"])[0], a = s.subarray(78)); + e = !!a; + r = 0, a && We.findBox(a, ["sinf"]).forEach(e => { + const t = We.findBox(e, ["schm"])[0]; + if (t) { + var i = We.bin2str(t.subarray(4, 8)); + if ("cbcs" === i || "cenc" === i) { + const t = We.findBox(e, ["frma"])[0]; + t && (n = We.bin2str(t)); + e = We.findBox(e, ["schi", "tenc"])[0]; + e && (r = e[7]) + } + } + }); + let o; + var l = i.subarray(86); + switch (n) { + case "mp4a": + t = "mp4a.40.5"; + break; + case "ac-3": + case "ec-3": + case "alac": + case "fLaC": + t = n; + break; + case "avc1": + case "avc3": + t = n + ".640028"; + break; + case "hvc1": + case "hev1": + const d = We.findBox(l, ["hvcC"])[0]; + o = Ze.parseHvcC(d), t = o ? Ze.hvcCToCodecString(n, o) : n + ".2.4.H150.B0"; + break; + case "dvh1": + case "dvhe": + const r = We.findBox(l, ["dvcC"])[0]; + o = Ze.parseDvcC(r), t = o ? Ze.dvcCToCodecString(n, o) : n + ".05.01"; + break; + case "c608": + t = n; + break; + case "vp09": + const i = We.findBox(l, ["vpcC"])[0]; + o = Ze.parseVpcC(i), t = Ze.vpcCToCodecString(n, o); + break; + default: + t = n + } + return { + codec: t, + encrypted: e, + defaultPerSampleIVSize: r + } + } + static has32BitTfdts(e) { + const t = We.findBox(e, ["moof", "traf", "tfdt"]); + let i = !1; + return t.forEach(e => { + 0 === e[0] && (i = !0) + }), i + } + static getStartDtsTs(r, e) { + const t = We.findBox(e, ["moof", "traf"]); + let n, s = Number.MAX_SAFE_INTEGER; + return t.map(function(i) { + return We.findBox(i, ["tfhd"]).forEach(e => { + var t = We.readUint32(e, 4), + e = r.tracksById[t]; + if (e) { + if (!e.isTimingTrack) return 1 / 0; + t = e.timescale || 9e4, e = We.findBox(i, ["tfdt"]).map(function(e) { + let t; + var i = e[0]; + return t = We.readUint32(e, 4), 1 === i && (t > Ye && Qe().warn(Je, `Value larger than can be represented by float for upper 32 bits ${t}`), t *= Math.pow(2, 32), t += We.readUint32(e, 8)), t + }), e = 0 < e.length ? e[0] : 1 / 0; + isFinite(e) && e / t < s && (s = e / t, n = { + baseTime: e, + timescale: t + }) + } + }) + }), n + } + static offsetStartDTS(r, e, l, n) { + We.findBox(e, ["moof", "traf"]).map(function(i) { + return We.findBox(i, ["tfhd"]).map(function(e) { + const t = We.readUint32(e, 4), + s = r.tracksById[t]; + if (s) { + const a = s.timescale || 9e4, + o = "caption" === s.type ? 0 : n; + We.findBox(i, ["tfdt"]).map(function(t) { + const i = t[0], + r = s.type; + if (0 === i) { + let e = We.readUint32(t, 4) - Math.round(l.baseTime * a / l.timescale); + "video" === r && e < 0 && (Qe().warn(Je, `video tdft would have gone negative by ${e/a} seconds`), e = 0), e += Math.round(o * a), e = Math.max(e, 0), We.writeUint32(t, 4, e) + } else { + const i = We.readUint32(t, 4); + i > Ye && Qe().error(Je, `baseMediaDecodeTime larger than can be represented by float for upper 32 bits ${i}`); + let e = i; + e *= Math.pow(2, 32), e += We.readUint32(t, 8), e -= Math.round(l.baseTime * a / l.timescale), "video" === r && e < 0 && (Qe().warn(Je, `video tdft would have gone negative by ${e/a} seconds`), e = 0), e += Math.round(o * a), e = Math.max(e, 0); + const n = Math.floor(e / (1 + Xe)), + s = Math.floor(e % (1 + Xe)); + We.writeUint32(t, 4, n), We.writeUint32(t, 8, s) + } + }) + } + }) + }) + } + static writeStartDTS(i, e, s) { + We.findBox(e, ["moof", "traf"]).map(function(t) { + return We.findBox(t, ["tfhd"]).map(function(e) { + e = We.readUint32(e, 4), e = i.tracksById[e]; + if (e) { + const r = e.timescale || 9e4, + n = Math.round(s * r) / r; + .01 < Math.abs(n - s) && Qe().warn(Je, `[iframes] large rounding error when adjusting timestamps, startDTS: ${s}, roundedStartDTS: ${n}`), We.findBox(t, ["tfdt"]).map(function(e) { + var t, i; + 0 === e[0] ? We.writeUint32(e, 4, n * r) : (i = n * r, i = Math.max(i, 0), t = Math.floor(i / (1 + Xe)), i = Math.floor(i % (1 + Xe)), We.writeUint32(e, 4, t), We.writeUint32(e, 8, i)) + }) + } + }) + }) + } + static parseSAIO(e) { + let t = 0, + i = 0; + var r = e[0]; + i += 4, 0 != (1 & We.readUint32(e, 0)) && (i += 8); + var n = 16777215 & We.readUint32(e, i); + return 1 == n ? (i += 4, t = We.readUint32(e, i), 1 === r && (i += 4, t *= Math.pow(2, 32), t += We.readUint32(e, i))) : Qe().error(Je, `saio entry count error, count is: ${n}`), t + } + static parseSAIZ(e) { + let t = 0, + i = 0; + return i += 4, 0 != (1 & We.readUint32(e, 0)) && (i += 8), t = e[i], i++, i += 4, 0 === t && (t = e[i]), t + } + static parseSubsample(e, t) { + const i = { + subsamples: [] + }; + let r = 0; + for (e && (i.iv = t.subarray(0, e), r += e), r += 2; r + 6 <= t.byteLength;) { + const e = We.readUint16(t, r); + r += 2; + var n = We.readUint32(t, r); + r += 4, i.subsamples.push([e, n]) + } + return i + } + static isSEIMessage(e, t) { + return e ? 39 === t || 40 === t : 6 === t + } + static parseCLCPSample(e, t, i) { + let r = 0; + const n = []; + let s = 0; + for (; r < i;) { + var a = t + r, + o = We.readUint32(e, a); + a += 4; + var l = We.bin2str(e.subarray(a, a + 4)); + if (a += 4, "cdat" !== l) break; { + const t = o - 8, + d = e.subarray(a, a + t); + s += t, n.push(d), r += o + } + } + return { + cdatList: n, + cdatTotalSize: s + } + } + static parseSamples(e, S, b, T, E, I) { + const w = b.timescale, + d = b.id; + let A, O = e, + k = 0, + C = !1; + We.findBoxWithOffset(S, 0, ["moof"]).map(function(e) { + const t = e.data, + v = e.offset; + We.findBox(t, ["traf"]).map(function(l) { + var e = We.findBox(l, ["tfdt"]).map(function(e) { + let t; + var i = e[0]; + return t = We.readUint32(e, 4), 1 === i && (t *= Math.pow(2, 32), t += We.readUint32(e, 8)), t / w + })[0]; + return void 0 !== e && (O = e), We.findBox(l, ["tfhd"]).map(function(e) { + var t = We.readUint32(e, 4), + i = 16777215 & We.readUint32(e, 0), + r = 0 != (1 & i), + n = 0 != (2 & i), + s = 0 != (8 & i); + let g = 0; + var a = 0 != (16 & i); + let y = 0; + i = 0 != (32 & i); + let o = 8; + if (ne(b.defaultSampleSize) && (y = b.defaultSampleSize), t === d) { + if (r && (We.readUint32(e, o), o += 4, We.readUint32(e, o), o += 4), n && (We.readUint32(e, o), o += 4), s && (g = We.readUint32(e, o), o += 4), a && (y = We.readUint32(e, o), o += 4), i && (We.readUint32(e, o), o += 4), "video" === b.type) { + let t = 0, + i = 0; + We.findBox(l, ["saio"]).map(function(e) { + t = Ze.parseSAIO(e) + }), We.findBox(l, ["saiz"]).map(function(e) { + i = Ze.parseSAIZ(e) + }), t && i && (A = Ze.parseSubsample(b.defaultPerSampleIVSize, S.subarray(t, t + i))), C = Ze.isHEVCFlavor(b.codec) + } + We.findBox(l, ["trun"]).map(function(i) { + var t = i[0], + e = 16777215 & We.readUint32(i, 0), + r = 0 != (1 & e); + let n = 0; + var s = 0 != (4 & e), + a = 0 != (256 & e); + let o = 0; + var l = 0 != (512 & e); + let d = 0; + var u = 0 != (1024 & e), + c = 0 != (2048 & e); + let h = 0; + var p = We.readUint32(i, 4); + let f = 8; + r && (n = We.readUint32(i, f), f += 4), s && (f += 4); + let m = n + v; + for (let e = 0; e < p && (I < 0 || k < I); e++) { + if (a ? (o = We.readUint32(i, f), f += 4) : o = g, l ? (d = We.readUint32(i, f), f += 4) : d = y, u && (f += 4), c && (h = 0 === t ? We.readUint32(i, f) : We.readSint32(i, f), f += 4), "video" === b.type) { + if (ne(T)) b.samples.push({ + data: S.subarray(m, m + d), + size: d, + duration: T * w, + cts: 0, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: 2, + isNonSync: 0, + paddingValue: 0 + }, + subsamples: A ? A.subsamples : [], + iv: A ? A.iv : void 0 + }); + else if (b.fragmentDuration += o, E) { + let e = 0; + for (; e < d;) { + const T = We.readUint32(S, m); + m += 4; + const E = 31 & S[m]; + if (b.seiSamples || (b.seiSamples = []), Ze.isSEIMessage(C, E)) { + const i = S.subarray(m, m + T); + b.seiSamples.push({ + pts: O + h / w, + type: E, + data: i, + sampleOffset: m, + naluSize: T + }) + } + m += T, e += T + 4 + } + } + } else if ("audio" === b.type) b.fragmentDuration += o; + else if ("caption" === b.type) { + const { + cdatList: i, + cdatTotalSize: T + } = Ze.parseCLCPSample(S, m, d); + if (m += d, i.length) { + let t; + if (1 === i.length) t = new Uint8Array(i[0]); + else if (1 < i.length) { + let e = 0; + t = new Uint8Array(T); + for (const T of i) t.set(T, e), e += T.length + } + b.captionSamples.push({ + type: 3, + pts: O, + bytes: t + }) + } + } + k++, O += o / w + } + }) + } + }) + }) + }) + } + static parseEmsg(e) { + let t, i, r, n, s, a = "", + o = "", + l = 0; + if (0 === e[0]) { + for (; + "\0" !== We.bin2str(e.subarray(l, l + 1));) a += We.bin2str(e.subarray(l, l + 1)), l += 1; + for (a += We.bin2str(e.subarray(l, l + 1)), l += 1; + "\0" !== We.bin2str(e.subarray(l, l + 1));) o += We.bin2str(e.subarray(l, l + 1)), l += 1; + o += We.bin2str(e.subarray(l, l + 1)), l += 1, t = We.readUint32(e, 12), i = We.readUint32(e, 16), n = We.readUint32(e, 20), s = We.readUint32(e, 24), l = 28 + } else { + l += 4, t = We.readUint32(e, l), l += 4; + const i = We.readUint32(e, l); + l += 4; + var d = We.readUint32(e, l); + for (l += 4, r = Math.pow(2, 32) * i + d, Number.isSafeInteger(r) || (r = Number.MAX_SAFE_INTEGER, Qe().warn(Je, "Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box")), n = We.readUint32(e, l), l += 4, s = We.readUint32(e, l), l += 4; + "\0" !== We.bin2str(e.subarray(l, l + 1));) a += We.bin2str(e.subarray(l, l + 1)), l += 1; + for (a += We.bin2str(e.subarray(l, l + 1)), l += 1; + "\0" !== We.bin2str(e.subarray(l, l + 1));) o += We.bin2str(e.subarray(l, l + 1)), l += 1; + o += We.bin2str(e.subarray(l, l + 1)), l += 1 + } + return { + schemeIdUri: a, + value: o, + timeScale: t, + presentationTime: r, + presentationTimeDelta: i, + eventDuration: n, + id: s, + payload: e.subarray(l, e.byteLength) + } + } + static extractID3PayloadCreateID3Track(e, t, i, r) { + const n = new M(e.payload, r), + s = new Uint8Array(e.payload), + a = s.byteLength; + let o = 0, + l = 0; + var d = ne(e.presentationTime) ? e.presentationTime / e.timeScale : t + e.presentationTimeDelta / e.timeScale; + if (ne(d)) { + const c = e.eventDuration, + h = s.subarray(0, 10), + p = We.bin2str(h.subarray(l, l + 3)); + l += 3, "ID3" !== p && Qe().error(Je, "No ID3 tag found when extracting ID3 payload"), l += 2; + var t = s.subarray(l, l + 1), + e = 64 & t[0], + u = 16 & t[0]; + if (l += 1, M.readSynchSafeUint32(s.subarray(l, l + 4)), l += 4, e) { + const f = M.readSynchSafeUint32(s.subarray(l, l + 4)); + l += 4, l += f + } + for (; l + 2 < a;) { + We.bin2str(s.subarray(l, l + 4)), l += 4; + const m = M.readSynchSafeUint32(s.subarray(l, l + 4)); + l += 4; + const r = d + o * c, + a = { + data: s, + pts: r, + dts: r, + keyTagInfo: void 0, + frames: n.frames + }; + i.id3Samples.push(a), l += m, o++, u && ("DI3" !== We.bin2str(s.subarray(l, l + 3)) && Qe().error(Je, "End should be DI3 if footer present in extracting ID3 payload"), l += 3, l += 7) + } + l + 2 === a && 0 !== We.readUint16(s, l) && Qe().warn(Je, "Padding should be 0 when extracting ID3 payload") + } else Qe().error(Je, "No pts found in emsg info when extracting ID3 payload") + } + append(e, t, i, r, n, s, a) { + let o = this.initData, + l = 0, + d = 0, + u = !1, + c = !1; + void 0 === o && (this.resetInitSegment(e, 0), o = this.initData); + let h, p = this.initPtsTs; + p || (h = Ze.getStartDtsTs(o, e), this.initPtsTs = p = { + baseTime: h.baseTime - Math.round(t * h.timescale), + timescale: h.timescale + }, this.observer.trigger(v.INIT_PTS_FOUND, { + initPTS: p + })), o.foundLargeTimescale && Ze.has32BitTfdts(e) && !a && (e = ze.remuxOverflowSegment(e, this.logger)), h = Ze.getStartDtsTs(o, e); + const f = We.findBox(e, ["emsg"]); + if (o.video && o.video.encrypted && (We.findBox(e, ["moof", "traf"]).find(function(e) { + return Boolean(We.findBox(e, ["senc"])[0] || We.findBox(e, ["saiz"])[0] && We.findBox(e, ["saio"])[0]) + }) || this.logger.warn(Je, `Missing subsample information for encrypted content codec=${o.videoCodec}`)), a) { + const t = this._videoTrack.timescale; + a = Math.ceil(a * t) / t; + const i = (h.baseTime + this.audioPrimingDelay * h.timescale) / h.timescale; + if (this._videoTrack && this._audioTrack && !this._silentAudioTrack) { + const e = Te.getTrack(this.observer, this._audioTrack.id, this._audioTrack.codec, 2, this.logger); + if (!e) throw `unable to create silent audio track for codec ${this._audioTrack.codec}`; + this._silentAudioTrack = Object.assign(Object.assign({}, e), { + sequenceNumber: 0 + }); + const t = ge.initSegment([this._videoTrack, this._silentAudioTrack]); + this.remuxedInitDataTrack = { + type: "audiovideo", + container: "video/mp4", + codec: this._silentAudioTrack.config.codec + "," + this._videoTrack.codec, + initSegment: t + }; + const i = { + track: this.remuxedInitDataTrack + }; + this.observer.trigger(v.FRAG_PARSING_INIT_SEGMENT, i) + } + Ze.parseSamples(i, e, this._videoTrack, a, !1, 1), this.mp4Remuxer.remuxIFrame(i, this._videoTrack, this._silentAudioTrack, a, this.remuxedInitDataTrack), this._videoTrack.samples = [] + } else { + a = (h.baseTime - this.audioPrimingDelay * h.timescale) / h.timescale, a = Math.max(0, a); + if (f && 0 < f.length) { + const e = f.map(e => { + e = Ze.parseEmsg(e); + return "https://aomedia.org/emsg/ID3\0" !== e.schemeIdUri || this._id3Track || (this._id3Track = { + id3Samples: [], + inputTimescale: 9e4 + }), e + }); + this._id3Track && e.map(e => { + Ze.extractID3PayloadCreateID3Track(e, t, this._id3Track, this.logger) + }) + } + this._videoTrack && (Ze.parseSamples(a, e, this._videoTrack, void 0, this.trySEICaptions, -1), l = this._videoTrack.fragmentDuration / this._videoTrack.timescale, u = !0, this._videoTrack.fragmentDuration = 0, this.trySEICaptions ? (Ze.extractSEICaptionsFromNALu(this._videoTrack.seiSamples, this._captionTrack), this._videoTrack.seiSamples = []) : this._captionTrack && Ze.parseSamples(a, e, this._captionTrack, void 0, !1, Number.MAX_SAFE_INTEGER)), this._audioTrack && (Ze.parseSamples(a, e, this._audioTrack, void 0, !1, -1), d = this._audioTrack.fragmentDuration / this._audioTrack.timescale, c = !0, this._audioTrack.fragmentDuration = 0), this.mp4Remuxer.remuxEmsgAndRawData(d, c, l, u, this._captionTrack, this._id3Track, a, h.timescale, e, this.remuxedInitDataTrack), this._id3Track && (this._id3Track.id3Samples = []) + } + } + static extractSEICaptionsFromNALu(a, o) { + if (a) { + for (let s = 0; s < a.length; ++s) { + var l = a[s], + d = l.pts; + let t = 0; + t++; + let e = 0, + i = 0, + r = !1, + n = 0; + for (; !r && t < l.data.length;) { + for (e = 0; !(t >= l.data.length) && (n = l.data[t++], e += n, 255 === n);); + for (i = 0; !(t >= l.data.length) && (n = l.data[t++], i += n, 255 === n);); + const a = l.data.length - t; + if (4 === e && t < l.data.length) { + if (r = !0, 181 === l.data[t++]) { + const a = We.readUint16(l.data, t); + if (t += 2, 49 === a) { + const a = We.readUint32(l.data, t); + if (t += 4, 1195456820 === a && 3 === l.data[t++]) { + const a = l.data[t++]; + t++; + const u = 31 & a, + c = []; + if (64 & a) + for (let e = 0; e < u; e++) { + const a = l.data[t++]; + if (a === (252 & a)) { + const o = 3 & a; + if (0 == o || 1 == o) { + const a = l.data[t++], + o = l.data[t++]; + c.push(a), c.push(o) + } + } else t += 2 + } + 0 < c.length && o.captionSamples.push({ + type: 3, + pts: d, + bytes: c + }) + } + } + } + } else if (i < a) t += i; + else if (i > a) break + } + } + return o + } + } + } + const et = { + name: "ExpGolomb" + }; + class tt { + constructor(e, t) { + this.data = e, this.logger = t, this._bytesAvailable = e.byteLength, this.word = 0, this.bitsAvailable = 0 + } + get bytesAvailable() { + return this._bytesAvailable + } + loadWord() { + const e = this.data, + t = this._bytesAvailable, + i = e.byteLength - t, + r = new Uint8Array(4), + n = Math.min(4, t); + if (0 === n) throw new Error("no bytes available"); + r.set(e.subarray(i, i + n)), this.word = new DataView(r.buffer).getUint32(0), this.bitsAvailable = 8 * n, this._bytesAvailable -= n + } + skipBits(e) { + var t; + this.bitsAvailable > e || (t = (e -= this.bitsAvailable) >> 3, e -= t >> 3, this._bytesAvailable -= t, this.loadWord()), this.word <<= e, this.bitsAvailable -= e + } + readBits(e) { + let t = Math.min(this.bitsAvailable, e); + var i = this.word >>> 32 - t; + return 32 < e && this.logger.error(et, "Cannot read more than 32 bits at a time"), this.bitsAvailable -= t, 0 < this.bitsAvailable ? this.word <<= t : 0 < this._bytesAvailable && this.loadWord(), t = e - t, 0 < t && this.bitsAvailable ? i << t | this.readBits(t) : i + } + skipLZ() { + let e; + for (e = 0; e < this.bitsAvailable; ++e) + if (0 != (this.word & 2147483648 >>> e)) return this.word <<= e, this.bitsAvailable -= e, e; + return this.loadWord(), e + this.skipLZ() + } + skipUEG() { + this.skipBits(1 + this.skipLZ()) + } + skipEG() { + this.skipBits(1 + this.skipLZ()) + } + readUEG() { + var e = this.skipLZ(); + return this.readBits(e + 1) - 1 + } + readEG() { + var e = this.readUEG(); + return 1 & e ? 1 + e >>> 1 : -1 * (e >>> 1) + } + readBoolean() { + return 1 === this.readBits(1) + } + readUByte() { + return this.readBits(8) + } + readUShort() { + return this.readBits(16) + } + readUInt() { + return this.readBits(32) + } + skipScalingList(e) { + let t, i, r = 8, + n = 8; + for (t = 0; t < e; t++) 0 !== n && (i = this.readEG(), n = (r + i + 256) % 256), r = 0 === n ? r : n + } + readSPS() { + let e, t, i, r = 0, + n = 0, + s = 0, + a = 0; + const o = this.readUByte.bind(this), + l = this.readBits.bind(this), + d = this.readUEG.bind(this), + u = this.readBoolean.bind(this), + c = this.skipBits.bind(this), + h = this.skipEG.bind(this), + p = this.skipUEG.bind(this), + f = this.skipScalingList.bind(this); + o(); + var m = o(); + if (l(5), c(3), o(), p(), 100 === m || 110 === m || 122 === m || 244 === m || 44 === m || 83 === m || 86 === m || 118 === m || 128 === m) { + const e = d(); + if (3 === e && c(1), p(), p(), c(1), u()) + for (t = 3 !== e ? 8 : 12, i = 0; i < t; i++) u() && f(i < 6 ? 16 : 64) + } + p(); + var g = d(); + if (0 === g) d(); + else if (1 === g) + for (c(1), h(), h(), e = d(), i = 0; i < e; i++) h(); + p(), c(1); + var y = d(), + m = d(), + g = l(1); + 0 === g && c(1), c(1), u() && (r = d(), n = d(), s = d(), a = d()); + let v = [1, 1]; + if (u() && u()) switch (o()) { + case 1: + v = [1, 1]; + break; + case 2: + v = [12, 11]; + break; + case 3: + v = [10, 11]; + break; + case 4: + v = [16, 11]; + break; + case 5: + v = [40, 33]; + break; + case 6: + v = [24, 11]; + break; + case 7: + v = [20, 11]; + break; + case 8: + v = [32, 11]; + break; + case 9: + v = [80, 33]; + break; + case 10: + v = [18, 11]; + break; + case 11: + v = [15, 11]; + break; + case 12: + v = [64, 33]; + break; + case 13: + v = [160, 99]; + break; + case 14: + v = [4, 3]; + break; + case 15: + v = [3, 2]; + break; + case 16: + v = [2, 1]; + break; + case 255: + v = [o() << 8 | o(), o() << 8 | o()] + } + return { + width: Math.ceil(16 * (y + 1) - 2 * r - 2 * n), + height: (2 - g) * (m + 1) * 16 - (g ? 2 : 4) * (s + a), + pixelRatio: v + } + } + readSliceType() { + return this.readUByte(), this.readUEG(), this.readUEG() + } + } + const it = { + name: "TS Demuxer" + }; + var rt, nt = class extends c { + constructor(e, t, i, r, n) { + super(e, t, i, r, n) + } + static probe(e, t) { + return 564 <= e.length && 71 === e[0] && 71 === e[188] && 71 === e[376] + } + resetInitSegment(e, t, i) { + this.pmtParsed = !1; + var r = { + id: this._pmtId = -1, + inputTimescale: 9e4, + timescale: NaN, + duration: 0, + encrypted: i && i.isEncrypted, + keyTagInfo: i + }, + i = { + len: 0, + sequenceNumber: 0 + }; + this._avcContext = { + info: Object.assign({}, r), + parsingData: Object.assign(Object.assign({}, i), { + esSamples: new Array, + dropped: 0 + }), + config: {}, + container: "video/mp2t", + type: "video" + }, this._audioContext = { + info: Object.assign({}, r), + parsingData: Object.assign(Object.assign({}, i), { + esSamples: new Array + }), + container: "video/mp2t", + type: "audio" + }, this._id3Track = { + id: -1, + inputTimescale: 9e4, + id3Samples: [] + }, this._txtTrack = { + inputTimescale: 9e4, + captionSamples: [] + }, this._duration = t, this._initSegment = e + } + append(e, t, i, r, n, s, a) { + let o, l, d, u, c, h = !1; + this.contiguous = i; + const p = this._avcContext, + f = this._audioContext, + m = this._id3Track; + let g = this.pmtParsed, + y = p.info.id, + v = f.info.id, + S = m.id, + b = this._pmtId, + T = p.pesData, + E = f.pesData, + I = m.pesData; + if (this.iframeMode = void 0 !== a, this._initSegment && 0 < this._initSegment.byteLength) { + const t = new Uint8Array(this._initSegment.byteLength + e.byteLength); + t.set(this._initSegment), t.set(e, this._initSegment.byteLength), this._initSegment = void 0, 71 === t[0] && (e = t) + } + let w, A, O = e.length; + for (O -= O % 188, o = 0; o < O; o += 188) { + if (71 !== e[o]) { + const e = new D(!1, "TS packet did not start with 0x47", $.NoTSSyncByteFound); + return void this.observer.trigger(x.INTERNAL_ERROR, e) + } + if (l = !!(64 & e[o + 1]), d = ((31 & e[o + 1]) << 8) + e[o + 2], 1 < (48 & e[o + 3]) >> 4) { + if (u = o + 5 + e[o + 4], u === o + 188) continue + } else u = o + 4; + switch (d) { + case y: + l && (T && (c = this._parsePES(T)) && this._parseAVCPES(c, !1), T = { + data: [], + size: 0, + keyTagInfo: n + }), T && (T.data.push(e.subarray(u, o + 188)), T.size += o + 188 - u); + break; + case v: + if (l && !this.iframeMode) { + if (E && (c = this._parsePES(E))) switch (f.segmentCodec) { + case "aac": + this._parseAACPES(c); + break; + case "mp3": + this._parseMPEGPES(c); + break; + case "ac3": + case "ec3": + this._parseDolbyPES(c) + } + E = { + data: [], + size: 0, + keyTagInfo: n + } + } + E && (E.data.push(e.subarray(u, o + 188)), E.size += o + 188 - u); + break; + case S: + l && (I && (c = this._parsePES(I)) && m.id3Samples.push(c), I = { + data: [], + size: 0 + }), I && (I.data.push(e.subarray(u, o + 188)), I.size += o + 188 - u); + break; + case 0: + l && (u += e[u] + 1), b = this._pmtId = this._parsePAT(e, u); + break; + case b: + l && (u += e[u] + 1); + const t = this._parsePMT(e, u, this.typeSupported); + y = t.avcId, 0 < y && (p.info.id = y, p.info.encrypted = t.videoEncrypted), v = t.audioId, 0 < v && (f.info.id = v, f.segmentCodec = t.audioSegmentCodec, f.info.encrypted = t.audioEncrypted), S = t.id3Id, 0 < S && (m.id = S), h && !g && (h = !1, o = -188), g = this.pmtParsed = !0; + break; + case 17: + case 8191: + break; + default: + h = !0 + } + } + if (T && (c = this._parsePES(T)) ? (this._parseAVCPES(c, !0), p.pesData = void 0) : p.pesData = T, E && (c = this._parsePES(E))) { + switch (f.segmentCodec) { + case "aac": + this._parseAACPES(c); + break; + case "mp3": + this._parseMPEGPES(c); + break; + case "ac3": + case "ec3": + this._parseDolbyPES(c) + } + f.pesData = void 0 + } else E && E.size && this.logger.warn(it, "last AAC PES packet truncated,might overlap between fragments"), f.pesData = E; + I && (c = this._parsePES(I)) ? (m.id3Samples.push(c), m.pesData = void 0) : m.pesData = I, f.config && f.segmentCodec && (w = { + type: "audio", + info: f.info, + config: f.config, + parsingData: f.parsingData + }); + var k, C = p.config; + "string" == typeof(k = C).codec && Array.isArray(k.sps) && Array.isArray(k.pps) && "number" == typeof k.width && "number" == typeof k.height && Array.isArray(k.pixelRatio) && (A = { + type: "video", + info: p.info, + config: C, + parsingData: p.parsingData + }), this.esRemuxer.remuxEsTracks(w, A, m, this._txtTrack, t, i, r, n, s, a) + } + destroy() { + this._duration = 0 + } + _parsePAT(e, t) { + return (31 & e[t + 10]) << 8 | e[t + 11] + } + _parsePMT(e, t, i) { + var r; + const n = { + audioId: -1, + avcId: -1, + id3Id: -1, + audioEncrypted: !1, + videoEncrypted: !1 + }, + s = t + 3 + ((15 & e[t + 1]) << 8 | e[t + 2]) - 4; + for (t += 12 + ((15 & e[t + 10]) << 8 | e[t + 11]); t < s;) { + switch (r = (31 & e[t + 1]) << 8 | e[t + 2], e[t]) { + case 207: + n.audioEncrypted = !0; + case 15: + -1 === n.audioId && (n.audioId = r, n.audioSegmentCodec = "aac"); + break; + case 21: + -1 === n.id3Id && (n.id3Id = r); + break; + case 219: + n.videoEncrypted = !0; + case 27: + -1 === n.avcId && (n.avcId = r); + break; + case 3: + case 4: + !0 !== i.mpeg && !0 !== i.mp3 ? this.logger.warn(it, "MPEG audio found, not supported in this browser for now") : -1 === n.audioId && (n.audioId = r, n.audioSegmentCodec = "mp3"); + break; + case 193: + n.audioEncrypted = !0; + case 129: + !0 !== i.ac3 ? this.logger.warn(it, "AC-3 audio found, not supported in this browser for now") : -1 === n.audioId && (n.audioId = r, n.audioSegmentCodec = "ac3"); + break; + case 194: + n.audioEncrypted = !0; + case 135: + !0 !== i.ec3 ? this.logger.warn(it, "EC-3 audio found, not supported in this browser for now") : -1 === n.audioId && (n.audioId = r, n.audioSegmentCodec = "ec3"); + break; + case 36: + this.logger.warn(it, "HEVC stream type found, not supported for now"); + break; + default: + this.logger.warn(it, "unkown stream type:" + e[t]) + } + t += 5 + ((15 & e[t + 3]) << 8 | e[t + 4]) + } + return n + } + _parsePES(e) { + let i, t, r, n, s, a, o = 0; + const l = e.data, + d = e.keyTagInfo; + let u = NaN, + c = NaN; + if (e && 0 !== e.size) { + for (; l[0].length < 19 && 1 < l.length;) { + const e = new Uint8Array(l[0].length + l[1].length); + e.set(l[0]), e.set(l[1], l[0].length), l[0] = e, l.splice(1, 1) + } + if (i = l[0], 1 === (i[0] << 16) + (i[1] << 8) + i[2] && (r = (i[4] << 8) + i[5], !(r && r > e.size - 6))) { + 192 & (t = i[7]) && (u = 536870912 * (14 & i[9]) + 4194304 * (255 & i[10]) + 16384 * (254 & i[11]) + 128 * (255 & i[12]) + (254 & i[13]) / 2, 64 & t ? (c = 536870912 * (14 & i[14]) + 4194304 * (255 & i[15]) + 16384 * (254 & i[16]) + 128 * (255 & i[17]) + (254 & i[18]) / 2, 54e5 < u - c && (this.logger.warn(it, `${Math.round((u-c)/9e4)}s delta between PTS and DTS, align them`), u = c)) : c = u), n = i[8], a = n + 9, e.size -= a, s = new Uint8Array(e.size); + for (let t = 0, e = l.length; t < e; t++) { + i = l[t]; + let e = i.byteLength; + if (a) { + if (a > e) { + a -= e; + continue + } + i = i.subarray(a), e -= a, a = 0 + } + s.set(i, o), o += e + } + return r && (r -= n + 3), { + data: s, + pts: u, + dts: c, + len: r, + keyTagInfo: d + } + } + } + } + pushAccesUnit(e, t) { + const i = e.avcSample; + if (i && i.units.length && i.frame) { + const r = e.parsingData.esSamples, + n = r.length; + (!0 === i.key || e.config.sps && (n || this.contiguous)) && (i.id = n, i.keyTagInfo = t, e.info.encrypted) && i.units.forEach(e => { + if (48 < e.data.byteLength) switch (e.type) { + case 1: + case 5: + e.data = this.discardEPB(e.data) + } + }), n || isFinite(i.pts) ? r.push(i) : e.parsingData.dropped++ + } + } + _parseAVCPES(o, e) { + if (!o.data) throw "invalid pes data"; + const l = this._avcContext, + t = this._parseAVCNALu(o.data); + let d, u, c, h = l.avcSample; + const p = o.keyTagInfo; + o.data = void 0, t.forEach(n => { + switch (n.type) { + case 1: + if (h && !this.iframeMode) { + u = !0, h.frame = !0; + const o = n.data; + if (4 < o.length) { + const n = new tt(o, this.logger).readSliceType(); + 2 !== n && 4 !== n && 7 !== n && 9 !== n || (h.key = !0) + } + } + break; + case 5: + u = !0, h && (h = h || (l.avcSample = this._createAVCSample(!0, o.pts, o.dts, "")), h.key = !0, h.frame = !0); + break; + case 6: + u = !0, d = new tt(this.discardEPB(n.data), this.logger), d.readUByte(); + let e = 0, + t = 0, + i = !1, + r = 0; + for (; !i && 1 < d.bytesAvailable;) { + for (e = 0; r = d.readUByte(), e += r, 255 === r;); + for (t = 0; r = d.readUByte(), t += r, 255 === r;); + if (4 === e && 0 !== d.bytesAvailable) { + if (i = !0, 181 === d.readUByte() && 49 === d.readUShort() && 1195456820 === d.readUInt() && 3 === d.readUByte()) { + const n = d.readUByte(), + l = 31 & n, + s = [n, d.readUByte()]; + for (c = 0; c < l; c++) s.push(d.readUByte()), s.push(d.readUByte()), s.push(d.readUByte()); + this._insertSampleInOrder(this._txtTrack.captionSamples, { + type: 3, + pts: o.pts, + bytes: s + }) + } + } else if (t < d.bytesAvailable) + for (c = 0; c < t; c++) d.readUByte() + } + break; + case 7: + if (u = !0, !l.config.sps) { + d = new tt(n.data, this.logger); + const o = d.readSPS(); + l.config.width = o.width, l.config.height = o.height, l.config.pixelRatio = o.pixelRatio, l.config.sps = [n.data], l.info.duration = this._duration; + const a = n.data.subarray(1, 4); + let t = "avc1."; + for (c = 0; c < 3; c++) { + let e = a[c].toString(16); + e.length < 2 && (e = "0" + e), t += e + } + l.config.codec = t + } + break; + case 8: + u = !0, l.config.pps || (l.config.pps = [n.data]); + break; + case 9: + u = !1, h && this.pushAccesUnit(l, p), h = l.avcSample = this._createAVCSample(!1, o.pts, o.dts, ""); + break; + case 12: + u = !1; + break; + default: + u = !1, h && (h.debug += "unknown NAL " + n.type + " ") + } + h && u && h.units.push(n) + }), e && h && (this.pushAccesUnit(l, p), l.avcSample = void 0) + } + _createAVCSample(e, t, i, r) { + return { + id: NaN, + key: e, + pts: t, + dts: i, + units: new Array, + debug: r + } + } + _insertSampleInOrder(t, i) { + var r = t.length; + if (0 < r) { + if (i.pts >= t[r - 1].pts) t.push(i); + else + for (let e = r - 1; 0 <= e; e--) + if (i.pts < t[e].pts) { + t.splice(e, 0, i); + break + } + } else t.push(i) + } + _getLastNalUnit() { + const e = this._avcContext; + let t, i = e.avcSample; + if (!i || 0 === i.units.length) { + const t = e.parsingData.esSamples; + i = t[t.length - 1] + } + if (i) { + const e = i.units; + t = e[e.length - 1] + } + return t + } + _parseAVCNALu(e) { + const t = e.byteLength; + let i, r, n = 0; + const s = this._avcContext; + let a = s.naluState || 0; + const o = a, + l = []; + let d, u, c, h = -1; + for (-1 === a && (h = 0, c = 31 & e[0], a = 0, n = 1); n < t;) + if (i = e[n++], a) + if (1 !== a) + if (i) + if (1 === i) { + if (0 <= h) d = { + data: e.subarray(h, n - a - 1), + type: c + }, l.push(d); + else { + const t = this._getLastNalUnit(); + if (t && (o && n <= 4 - o && t.state && (t.data = t.data.subarray(0, t.data.byteLength - o)), r = n - a - 1, 0 < r)) { + const i = new Uint8Array(t.data.byteLength + r); + i.set(t.data, 0), i.set(e.subarray(0, r), t.data.byteLength), t.data = i, t.state = 0 + } + } + a = n < t ? (u = 31 & e[n], h = n, c = u, 0) : -1 + } else a = 0; + else a = 3; + else a = i ? 0 : 2; + else a = i ? 0 : 1; + if (0 <= h && 0 <= a && (d = { + data: e.subarray(h, t), + type: c, + state: a + }, l.push(d)), 0 === l.length) { + const t = this._getLastNalUnit(); + if (t) { + const i = new Uint8Array(t.data.byteLength + e.byteLength); + i.set(t.data, 0), i.set(e, t.data.byteLength), t.data = i + } + } + return s.naluState = a, l + } + discardEPB(e) { + const t = e.byteLength, + i = []; + let r = 1; + for (; r < t - 2;) 0 === e[r] && 0 === e[r + 1] && 3 === e[r + 2] ? (i.push(r + 2), r += 2) : r++; + if (0 === i.length) return e; + const n = t - i.length, + s = new Uint8Array(n); + let a = 0; + for (r = 0; r < n; a++, r++) a === i[0] && (a++, i.shift()), s[r] = e[a]; + return s + } + _parseAACPES(e) { + const t = this._audioContext, + i = t.audioLastPTS, + r = e.keyTagInfo; + let n, s, a, o, l, d, u, c = e.data, + h = e.pts, + p = t.audioOverFlow; + if (p) { + const e = new Uint8Array(p.byteLength + c.byteLength); + e.set(p, 0), e.set(c, p.byteLength), c = e + } + for (a = 0, d = c.length; a < d - 1 && (255 !== c[a] || 240 != (240 & c[a + 1])); a++); + if (a) { + let e, t, i; + i = a < d - 1 ? (e = `AAC PES did not start with ADTS header,offset:${a}`, t = !1, $.PESDidNotStartWithADTS) : (e = "no ADTS header found in AAC PES", t = !0, $.NoADTSHeaderInPES), this.logger.warn(it, `parsing error:${e}`); + const r = new D(t, e, i); + if (this.observer.trigger(x.INTERNAL_ERROR, r), t) return + } + if (!t.config) { + const e = E(this.observer, c, a, void 0, this.logger); + if (!e) throw "unable to parse adts header"; + t.config = e + } + s = 0; + var f = 9216e4 / t.config.samplerate; + if (p && i) { + const e = i + f; + 1 < Math.abs(e - h) && (h = e) + } + for (; a + 5 < d && (o = 1 & c[a + 1] ? 7 : 9, n = (3 & c[a + 3]) << 11 | c[a + 4] << 3 | (224 & c[a + 5]) >>> 5, n -= o, 0 < n && a + o + n <= d);) + for (l = h + s * f, u = { + unit: c.subarray(a + o, a + o + n), + pts: l, + dts: l, + keyTagInfo: r + }, t.parsingData.esSamples.push(u), t.parsingData.len += n, a += n + o, s++; a < d - 1 && (255 !== c[a] || 240 != (240 & c[a + 1])); a++); + p = a < d ? c.subarray(a, d) : void 0, t.audioOverFlow = p, t.audioLastPTS = l + } + _parseMPEGPES(e) { + "mp3" === this._audioContext.segmentCodec && ee.parse(this._audioContext.parsingData, e.data, 0, e.pts, this.logger) + } + _parseDolbyPES(e) { + const t = this._audioContext; + let i = e.data, + r = e.pts; + var n = e.keyTagInfo; + let s = 0, + a = 0, + o = t.audioOverFlow; + e = t.audioLastPTS; + if (!t.config) { + let e; + if ("ac3" === t.segmentCodec ? e = j(this.observer, i, a, this.logger) : "ec3" === t.segmentCodec && (e = Y(this.observer, i, a, this.logger)), !e) throw "unable to parse dolby header"; + t.config = e + } + if ("ac3" !== t.config.segmentCodec && "ec3" !== t.config.segmentCodec) throw "unexpected config type"; + var l = 1536 / t.config.samplerate * t.info.inputTimescale; + if (o) { + const c = new Uint8Array(o.byteLength + i.byteLength); + c.set(o, 0), c.set(i, o.byteLength), i = c + } + var d = i.length; + if (o && e) { + const c = e + l; + 1 < Math.abs(c - r) && (r = c) + } + let u = 0; + for (; a + u <= d;) { + if (11 !== i[a] || 119 !== i[a + 1]) { + const c = new D(!0, "invalid dolby audio magic", $.InvalidDolbyAudioMagic); + return void this.observer.trigger(x.INTERNAL_ERROR, c) + } + "ac3" === t.segmentCodec ? u = Q(this.observer, i, a) : "ec3" === t.segmentCodec && (u = X(this.observer, i, a, this.logger)); + const c = r + s * l; + t.audioLastPTS = c; + const o = { + unit: i.subarray(a, a + u), + pts: c, + dts: c, + keyTagInfo: n + }; + t.parsingData.esSamples.push(o), t.info.duration = this._duration, t.parsingData.len += u, a += u, s++ + } + o = a < d ? i.subarray(a, d) : void 0, t.audioOverFlow = o + } + }; + class st extends a { + constructor(e, t, i, r) { + super(), this.typeSupported = e, this.config = t, this.vendor = i, this.logger = r + } + destroy() { + this.removeAllListeners(); + const e = this.demuxer, + t = this.remuxer; + e && e.destroy(), t && t.destroy() + } + push(t, i, r, n, s, a, o, l, d, u, c, h) { + if (t) { + let e = this.demuxer; + var p = new Uint8Array(t); + if (!e || (s || a) && !this.probeFn(p, this.logger)) { + const { + typeSupported: t, + config: i + } = this, r = [{ + demux: Ze, + remux: ze + }, { + demux: nt, + remux: Ee + }, { + demux: J, + remux: Ee + }, { + demux: z, + remux: Ee + }, { + demux: K, + remux: Ee + }, { + demux: ie, + remux: Ee + }]; + for (const n of r) { + const r = n.demux["probe"]; + if (r(p, this.logger)) { + this.remuxer = new n.remux(this, i, t, this.vendor, this.logger), e = new n.demux(this, this.remuxer, i, t, this.logger), this.probeFn = r; + break + } + } + if (!e) { + const t = new D(!0, "no demux matching with content found", $.DemuxerNotFound); + return void this.trigger(x.INTERNAL_ERROR, t) + } + this.demuxer = e + } + const f = this.remuxer, + m = !this.lastKeyTagInfo || i && "NONE" !== i.method && this.lastKeyTagInfo.uri !== i.uri; + if (this.lastKeyTagInfo = i, (s || a || m) && (e.resetInitSegment(new Uint8Array(r), l, i, s), f.resetInitSegment()), s) { + const t = u ? S(u) : void 0; + e.resetTimeStamp(t), f.resetTimeStamp(t) + } + e.append(p, n, o, d, i, c, h) + } + } + } + + function at() { + let e = `${Date.now()}-${Math.random()}`; + return "undefined" != typeof performance && "function" == typeof performance.now && (e += `-${performance.now()}`), e + } + class ot { + constructor(e, t) { + this.rpc = e, this.logger = t, this.init = (t, n, s) => e => { + const i = at(), + r = this.demuxers[i] = new st(t, n, s, this.logger); + [v.INIT_PTS_FOUND, v.FRAG_PARSING_INIT_SEGMENT, v.FRAG_PARSING_DATA, v.FRAG_PARSED, x.INTERNAL_ERROR].forEach(t => { + r.on(t, e => this.rpc.invoke("demuxer.event", [i, t, e])(() => {})) + }), e(i) + }, this.push = (i, r, n, s, a, o, l, d, u, c, h, p, f) => e => { + const t = this.demuxers[i]; + t ? (t.push(r, n, s, a, o, l, d, u, c, h, p, f), e()) : e(void 0, `Demuxer with id "${i}" does not exist on push`) + }, this.destroy = i => e => { + const t = this.demuxers[i]; + t ? (t.destroy(), delete this.demuxers[i], e()) : this.logger.error(`Demuxer with id "${i}" does not exist on destroy`) + }, this.demuxers = {}, e.register("demuxer.init", this.init), e.register("demuxer.push", this.push), e.register("demuxer.destroy", this.destroy) + } + } + class lt { + constructor(e) { + this.worker = e, this.handlers = {}, this.deferers = {}, this._messageHandler = e => { + var { + type: t, + id: i, + command: r, + args: n, + result: e, + error: s + } = e.data; + if (t === rt.Invoke) try { + if (null == this.handlers[r]) throw new Error(`command ${r} not found`); + this.handlers[r](...n)(this._respond.bind(this, i, r)) + } catch (s) { + this._respond(i, r, null, new Error(`command ${r} not found`)) + } else t === rt.Result && null != this.deferers[i] && (this.deferers[i](e, s), delete this.deferers[i]) + }, e.addEventListener("message", this._messageHandler) + } + register(e, t) { + if (null != this.handlers[e]) return !1; + this.handlers[e] = t + } + unregister(e) { + if (null != this.handlers[e]) return !1; + delete this.handlers[e] + } + invoke(i, r, n) { + return (e = lt._fallbackCallback) => { + var t = at(); + this.deferers[t] = e; + t = { + type: rt.Invoke, + id: t, + command: i, + args: r + }; + this._send(t, n) + } + } + teardown(e) { + this.worker.removeEventListener("message", this._messageHandler), e() + } + _respond(e, t, i, r, n) { + r instanceof Error && (r = `[${r.name}] ${r.message}\n${r.stack}`); + r = { + type: rt.Result, + id: e, + command: t, + result: i, + error: r + }; + this._send(r, n) + } + _send(e, t = []) { + this.worker.postMessage(e, t.map(e => ArrayBuffer.isView(e) ? e.buffer : e).filter(e => void 0 !== e)) + } + } + lt._fallbackCallback = (e, t) => { + if (null != t) throw t + }, (vr = rt = rt || {})[vr.Invoke = 0] = "Invoke", vr[vr.Result = 1] = "Result", ArrayBuffer.isView || (ArrayBuffer.isView = function(e) { + return null !== e && "object" == typeof e && e.buffer instanceof ArrayBuffer + }), void 0 !== Yy && Yy && (Vr = new lt(l), iu = (n => { + const t = (i = []) => { + const r = {}; + return ["fatal", "error", "warn", "info", "debug", "trace", "qe"].forEach(e => { + return r[e] = (t = e, (...e) => { + n.invoke("logger.log", [i, t, ...e])((e, t) => { + if (null != t) throw t + }) + }); + var t + }), r.child = e => t([...i, e]), r + }; + return t() + })(Vr), new i(Vr, iu), new ot(Vr, iu)); + var dt = function(e, t) { + return (dt = Object.setPrototypeOf || { + __proto__: [] + } + instanceof Array && function(e, t) { + e.__proto__ = t + } || function(e, t) { + for (var i in t) Object.prototype.hasOwnProperty.call(t, i) && (e[i] = t[i]) + })(e, t) + }; + + function ut(e, t) { + if ("function" != typeof t && null !== t) throw new TypeError("Class extends value " + String(t) + " is not a constructor or null"); + + function i() { + this.constructor = e + } + dt(e, t), e.prototype = null === t ? Object.create(t) : (i.prototype = t.prototype, new i) + } + var ct = function() { + return (ct = Object.assign || function(e) { + for (var t, i = 1, r = arguments.length; i < r; i++) + for (var n in t = arguments[i]) Object.prototype.hasOwnProperty.call(t, n) && (e[n] = t[n]); + return e + }).apply(this, arguments) + }; + + function ht(e, t, i, r) { + var n, s = arguments.length, + a = s < 3 ? t : null === r ? r = Object.getOwnPropertyDescriptor(t, i) : r; + if ("object" == typeof Reflect && "function" == typeof Reflect.decorate) a = Reflect.decorate(e, t, i, r); + else + for (var o = e.length - 1; 0 <= o; o--)(n = e[o]) && (a = (s < 3 ? n(a) : 3 < s ? n(t, i, a) : n(t, i)) || a); + return 3 < s && a && Object.defineProperty(t, i, a), a + } + + function pt(e, t) { + if ("object" == typeof Reflect && "function" == typeof Reflect.metadata) return Reflect.metadata(e, t) + } + + function ft(e) { + var t = "function" == typeof Symbol && Symbol.iterator, + i = t && e[t], + r = 0; + if (i) return i.call(e); + if (e && "number" == typeof e.length) return { + next: function() { + return { + value: (e = e && r >= e.length ? void 0 : e) && e[r++], + done: !e + } + } + }; + throw new TypeError(t ? "Object is not iterable." : "Symbol.iterator is not defined.") + } + + function mt(e, t) { + var i = "function" == typeof Symbol && e[Symbol.iterator]; + if (!i) return e; + var r, n, s = i.call(e), + a = []; + try { + for (; + (void 0 === t || 0 < t--) && !(r = s.next()).done;) a.push(r.value) + } catch (e) { + n = { + error: e + } + } finally { + try { + r && !r.done && (i = s.return) && i.call(s) + } finally { + if (n) throw n.error + } + } + return a + } + + function gt() { + for (var e = [], t = 0; t < arguments.length; t++) e = e.concat(mt(arguments[t])); + return e + } + + function yt(e) { + return "function" == typeof e + } + var vt = !1, + St = { + Promise: void 0, + set useDeprecatedSynchronousErrorHandling(e) { + vt = e + }, + get useDeprecatedSynchronousErrorHandling() { + return vt + } + }; + + function bt(e) { + setTimeout(function() { + throw e + }, 0) + } + var Tt = { + closed: !0, + next: function(e) {}, + error: function(e) { + if (St.useDeprecatedSynchronousErrorHandling) throw e; + bt(e) + }, + complete: function() {} + }, + Et = Array.isArray || function(e) { + return e && "number" == typeof e.length + }; + + function It(e) { + return null !== e && "object" == typeof e + } + var wt = (kt.prototype = Object.create(Error.prototype), kt), + At = (Ot.prototype.unsubscribe = function() { + var t; + if (!this.closed) { + var e = this._parentOrParents, + i = this._ctorUnsubscribe, + r = this._unsubscribe, + n = this._subscriptions; + if (this.closed = !0, this._parentOrParents = null, this._subscriptions = null, e instanceof Ot) e.remove(this); + else if (null !== e) + for (var s = 0; s < e.length; ++s) e[s].remove(this); + if (yt(r)) { + i && (this._unsubscribe = void 0); + try { + r.call(this) + } catch (e) { + t = e instanceof wt ? Ct(e.errors) : [e] + } + } + if (Et(n)) + for (var s = -1, a = n.length; ++s < a;) { + var o = n[s]; + if (It(o)) try { + o.unsubscribe() + } catch (e) { + t = t || [], e instanceof wt ? t = t.concat(Ct(e.errors)) : t.push(e) + } + } + if (t) throw new wt(t) + } + }, Ot.prototype.add = function(e) { + var t, i = e; + if (!e) return Ot.EMPTY; + switch (typeof e) { + case "function": + i = new Ot(e); + case "object": + if (i === this || i.closed || "function" != typeof i.unsubscribe) return i; + if (this.closed) return i.unsubscribe(), i; + i instanceof Ot || (t = i, (i = new Ot)._subscriptions = [t]); + break; + default: + throw new Error("unrecognized teardown " + e + " added to Subscription.") + } + var r = i._parentOrParents; + if (null === r) i._parentOrParents = this; + else if (r instanceof Ot) { + if (r === this) return i; + i._parentOrParents = [r, this] + } else { + if (-1 !== r.indexOf(this)) return i; + r.push(this) + } + r = this._subscriptions; + return null === r ? this._subscriptions = [i] : r.push(i), i + }, Ot.prototype.remove = function(e) { + var t = this._subscriptions; + !t || -1 !== (e = t.indexOf(e)) && t.splice(e, 1) + }, Ot.EMPTY = ((dl = new Ot).closed = !0, dl), Ot); + + function Ot(e) { + this.closed = !1, this._parentOrParents = null, this._subscriptions = null, e && (this._ctorUnsubscribe = !0, this._unsubscribe = e) + } + + function kt(e) { + return Error.call(this), this.message = e ? e.length + " errors occurred during unsubscription:\n" + e.map(function(e, t) { + return t + 1 + ") " + e.toString() + }).join("\n ") : "", this.name = "UnsubscriptionError", this.errors = e, this + } + + function Ct(e) { + return e.reduce(function(e, t) { + return e.concat(t instanceof wt ? t.errors : t) + }, []) + } + var Dt, Mt, xt = "function" == typeof Symbol ? Symbol("rxSubscriber") : "@@rxSubscriber_" + Math.random(), + Pt = (ut(_t, Mt = At), _t.prototype[xt] = function() { + return this + }, _t.create = function(e, t, i) { + i = new _t(e, t, i); + return i.syncErrorThrowable = !1, i + }, _t.prototype.next = function(e) { + this.isStopped || this._next(e) + }, _t.prototype.error = function(e) { + this.isStopped || (this.isStopped = !0, this._error(e)) + }, _t.prototype.complete = function() { + this.isStopped || (this.isStopped = !0, this._complete()) + }, _t.prototype.unsubscribe = function() { + this.closed || (this.isStopped = !0, Mt.prototype.unsubscribe.call(this)) + }, _t.prototype._next = function(e) { + this.destination.next(e) + }, _t.prototype._error = function(e) { + this.destination.error(e), this.unsubscribe() + }, _t.prototype._complete = function() { + this.destination.complete(), this.unsubscribe() + }, _t.prototype._unsubscribeAndRecycle = function() { + var e = this._parentOrParents; + return this._parentOrParents = null, this.unsubscribe(), this.closed = !1, this.isStopped = !1, this._parentOrParents = e, this + }, _t), + Rt = (ut(Lt, Dt = Pt), Lt.prototype.next = function(e) { + var t; + !this.isStopped && this._next && (t = this._parentSubscriber, St.useDeprecatedSynchronousErrorHandling && t.syncErrorThrowable ? this.__tryOrSetError(t, this._next, e) && this.unsubscribe() : this.__tryOrUnsub(this._next, e)) + }, Lt.prototype.error = function(e) { + if (!this.isStopped) { + var t = this._parentSubscriber, + i = St.useDeprecatedSynchronousErrorHandling; + if (this._error) i && t.syncErrorThrowable ? this.__tryOrSetError(t, this._error, e) : this.__tryOrUnsub(this._error, e), this.unsubscribe(); + else if (t.syncErrorThrowable) i ? (t.syncErrorValue = e, t.syncErrorThrown = !0) : bt(e), this.unsubscribe(); + else { + if (this.unsubscribe(), i) throw e; + bt(e) + } + } + }, Lt.prototype.complete = function() { + var e, t, i = this; + this.isStopped || (e = this._parentSubscriber, this._complete && (t = function() { + return i._complete.call(i._context) + }, St.useDeprecatedSynchronousErrorHandling && e.syncErrorThrowable ? this.__tryOrSetError(e, t) : this.__tryOrUnsub(t)), this.unsubscribe()) + }, Lt.prototype.__tryOrUnsub = function(e, t) { + try { + e.call(this._context, t) + } catch (e) { + if (this.unsubscribe(), St.useDeprecatedSynchronousErrorHandling) throw e; + bt(e) + } + }, Lt.prototype.__tryOrSetError = function(e, t, i) { + if (!St.useDeprecatedSynchronousErrorHandling) throw new Error("bad call"); + try { + t.call(this._context, i) + } catch (t) { + return St.useDeprecatedSynchronousErrorHandling ? (e.syncErrorValue = t, e.syncErrorThrown = !0) : bt(t), !0 + } + return !1 + }, Lt.prototype._unsubscribe = function() { + var e = this._parentSubscriber; + this._context = null, this._parentSubscriber = null, e.unsubscribe() + }, Lt); + + function Lt(e, t, i, r) { + var n, s = Dt.call(this) || this; + s._parentSubscriber = e; + e = s; + return yt(t) ? n = t : t && (n = t.next, i = t.error, r = t.complete, t !== Tt && (yt((e = Object.create(t)).unsubscribe) && s.add(e.unsubscribe.bind(e)), e.unsubscribe = s.unsubscribe.bind(s))), s._context = e, s._next = n, s._error = i, s._complete = r, s + } + + function _t(e, t, i) { + var r = Mt.call(this) || this; + switch (r.syncErrorValue = null, r.syncErrorThrown = !1, r.syncErrorThrowable = !1, r.isStopped = !1, arguments.length) { + case 0: + r.destination = Tt; + break; + case 1: + if (!e) { + r.destination = Tt; + break + } + if ("object" == typeof e) { + e instanceof _t ? (r.syncErrorThrowable = e.syncErrorThrowable, (r.destination = e).add(r)) : (r.syncErrorThrowable = !0, r.destination = new Rt(r, e)); + break + } + default: + r.syncErrorThrowable = !0, r.destination = new Rt(r, e, t, i) + } + return r + } + var Nt = "function" == typeof Symbol && Symbol.observable || "@@observable"; + + function Ft(e) { + return e + } + + function Bt() { + for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; + return Ut(e) + } + + function Ut(t) { + return 0 === t.length ? Ft : 1 === t.length ? t[0] : function(e) { + return t.reduce(function(e, t) { + return t(e) + }, e) + } + } + var $t = (Vt.prototype.lift = function(e) { + var t = new Vt; + return t.source = this, t.operator = e, t + }, Vt.prototype.subscribe = function(e, t, i) { + var r = this.operator, + i = function(e, t, i) { + if (e) { + if (e instanceof Pt) return e; + if (e[xt]) return e[xt]() + } + return e || t || i ? new Pt(e, t, i) : new Pt(Tt) + }(e, t, i); + if (r ? i.add(r.call(i, this.source)) : i.add(this.source || St.useDeprecatedSynchronousErrorHandling && !i.syncErrorThrowable ? this._subscribe(i) : this._trySubscribe(i)), St.useDeprecatedSynchronousErrorHandling && i.syncErrorThrowable && (i.syncErrorThrowable = !1, i.syncErrorThrown)) throw i.syncErrorValue; + return i + }, Vt.prototype._trySubscribe = function(t) { + try { + return this._subscribe(t) + } catch (e) { + St.useDeprecatedSynchronousErrorHandling && (t.syncErrorThrown = !0, t.syncErrorValue = e), + function(e) { + for (; e;) { + var t = e, + i = t.closed, + r = t.destination, + t = t.isStopped; + if (i || t) return; + e = r && r instanceof Pt ? r : null + } + return 1 + }(t) ? t.error(e) : console.warn(e) + } + }, Vt.prototype.forEach = function(r, e) { + var n = this; + return new(e = Kt(e))(function(e, t) { + var i = n.subscribe(function(e) { + try { + r(e) + } catch (e) { + t(e), i && i.unsubscribe() + } + }, t, e) + }) + }, Vt.prototype._subscribe = function(e) { + var t = this.source; + return t && t.subscribe(e) + }, Vt.prototype[Nt] = function() { + return this + }, Vt.prototype.pipe = function() { + for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; + return 0 === e.length ? this : Ut(e)(this) + }, Vt.prototype.toPromise = function(e) { + var r = this; + return new(e = Kt(e))(function(e, t) { + var i; + r.subscribe(function(e) { + return i = e + }, function(e) { + return t(e) + }, function() { + return e(i) + }) + }) + }, Vt.create = function(e) { + return new Vt(e) + }, Vt); + + function Vt(e) { + this._isScalar = !1, e && (this._subscribe = e) + } + + function Kt(e) { + if (!(e = e || (St.Promise || Promise))) throw new Error("no Promise impl found"); + return e + } + var qt, Ht, jt, Qt, Wt = (ii.prototype = Object.create(Error.prototype), ii), + Gt = (ut(ti, Qt = At), ti.prototype.unsubscribe = function() { + var e, t; + this.closed || (this.closed = !0, e = (t = this.subject).observers, this.subject = null, !e || 0 === e.length || t.isStopped || t.closed || -1 !== (t = e.indexOf(this.subscriber)) && e.splice(t, 1)) + }, ti), + zt = (ut(ei, jt = Pt), ei), + Xt = (ut(Zt, Ht = $t), Zt.prototype[xt] = function() { + return new zt(this) + }, Zt.prototype.lift = function(e) { + var t = new Yt(this, this); + return t.operator = e, t + }, Zt.prototype.next = function(e) { + if (this.closed) throw new Wt; + if (!this.isStopped) + for (var t = this.observers, i = t.length, r = t.slice(), n = 0; n < i; n++) r[n].next(e) + }, Zt.prototype.error = function(e) { + if (this.closed) throw new Wt; + this.hasError = !0, this.thrownError = e, this.isStopped = !0; + for (var t = this.observers, i = t.length, r = t.slice(), n = 0; n < i; n++) r[n].error(e); + this.observers.length = 0 + }, Zt.prototype.complete = function() { + if (this.closed) throw new Wt; + this.isStopped = !0; + for (var e = this.observers, t = e.length, i = e.slice(), r = 0; r < t; r++) i[r].complete(); + this.observers.length = 0 + }, Zt.prototype.unsubscribe = function() { + this.isStopped = !0, this.closed = !0, this.observers = null + }, Zt.prototype._trySubscribe = function(e) { + if (this.closed) throw new Wt; + return Ht.prototype._trySubscribe.call(this, e) + }, Zt.prototype._subscribe = function(e) { + if (this.closed) throw new Wt; + return this.hasError ? (e.error(this.thrownError), At.EMPTY) : this.isStopped ? (e.complete(), At.EMPTY) : (this.observers.push(e), new Gt(this, e)) + }, Zt.prototype.asObservable = function() { + var e = new $t; + return e.source = this, e + }, Zt.create = function(e, t) { + return new Yt(e, t) + }, Zt), + Yt = (ut(Jt, qt = Xt), Jt.prototype.next = function(e) { + var t = this.destination; + t && t.next && t.next(e) + }, Jt.prototype.error = function(e) { + var t = this.destination; + t && t.error && this.destination.error(e) + }, Jt.prototype.complete = function() { + var e = this.destination; + e && e.complete && this.destination.complete() + }, Jt.prototype._subscribe = function(e) { + return this.source ? this.source.subscribe(e) : At.EMPTY + }, Jt); + + function Jt(e, t) { + var i = qt.call(this) || this; + return i.destination = e, i.source = t, i + } + + function Zt() { + var e = Ht.call(this) || this; + return e.observers = [], e.closed = !1, e.isStopped = !1, e.hasError = !1, e.thrownError = null, e + } + + function ei(e) { + var t = jt.call(this, e) || this; + return t.destination = e, t + } + + function ti(e, t) { + var i = Qt.call(this) || this; + return i.subject = e, i.subscriber = t, i.closed = !1, i + } + + function ii() { + return Error.call(this), this.message = "object unsubscribed", this.name = "ObjectUnsubscribedError", this + } + + function ri() { + return function(e) { + return e.lift(new pi(e)) + } + } + var ni, si, ai, oi, li, di, ui, ci, hi, pi = (Li.prototype.call = function(e, t) { + var i = this.connectable; + i._refCount++; + e = new fi(e, i), t = t.subscribe(e); + return e.closed || (e.connection = i.connect()), t + }, Li), + fi = (ut(Ri, hi = Pt), Ri.prototype._unsubscribe = function() { + var e, t = this.connectable; + t ? (this.connectable = null, (e = t._refCount) <= 0 ? this.connection = null : (t._refCount = e - 1, 1 < e ? this.connection = null : (e = this.connection, t = t._connection, this.connection = null, !t || e && t !== e || t.unsubscribe()))) : this.connection = null + }, Ri), + w = (ut(Pi, ci = $t), Pi.prototype._subscribe = function(e) { + return this.getSubject().subscribe(e) + }, Pi.prototype.getSubject = function() { + var e = this._subject; + return e && !e.isStopped || (this._subject = this.subjectFactory()), this._subject + }, Pi.prototype.connect = function() { + var e = this._connection; + return e || (this._isComplete = !1, (e = this._connection = new At).add(this.source.subscribe(new gi(this.getSubject(), this))), e.closed && (this._connection = null, e = At.EMPTY)), e + }, Pi.prototype.refCount = function() { + return ri()(this) + }, Pi), + mi = { + operator: { + value: null + }, + _refCount: { + value: 0, + writable: !0 + }, + _subject: { + value: null, + writable: !0 + }, + _connection: { + value: null, + writable: !0 + }, + _subscribe: { + value: (fl = w.prototype)._subscribe + }, + _isComplete: { + value: fl._isComplete, + writable: !0 + }, + getSubject: { + value: fl.getSubject + }, + connect: { + value: fl.connect + }, + refCount: { + value: fl.refCount + } + }, + gi = (ut(xi, ui = zt), xi.prototype._error = function(e) { + this._unsubscribe(), ui.prototype._error.call(this, e) + }, xi.prototype._complete = function() { + this.connectable._isComplete = !0, this._unsubscribe(), ui.prototype._complete.call(this) + }, xi.prototype._unsubscribe = function() { + var e, t = this.connectable; + t && (this.connectable = null, e = t._connection, t._refCount = 0, t._subject = null, t._connection = null, e && e.unsubscribe()) + }, xi), + yi = (ut(Mi, di = Xt), Object.defineProperty(Mi.prototype, "value", { + get: function() { + return this.getValue() + }, + enumerable: !0, + configurable: !0 + }), Mi.prototype._subscribe = function(e) { + var t = di.prototype._subscribe.call(this, e); + return t && !t.closed && e.next(this._value), t + }, Mi.prototype.getValue = function() { + if (this.hasError) throw this.thrownError; + if (this.closed) throw new Wt; + return this._value + }, Mi.prototype.next = function(e) { + di.prototype.next.call(this, this._value = e) + }, Mi), + A = (ut(Di, li = At), Di.prototype.schedule = function(e, t) { + return this + }, ut(Ci, oi = Di), Ci.prototype.schedule = function(e, t) { + if (void 0 === t && (t = 0), this.closed) return this; + this.state = e; + var i = this.id, + e = this.scheduler; + return null != i && (this.id = this.recycleAsyncId(e, i, t)), this.pending = !0, this.delay = t, this.id = this.id || this.requestAsyncId(e, this.id, t), this + }, Ci.prototype.requestAsyncId = function(e, t, i) { + return void 0 === i && (i = 0), setInterval(e.flush.bind(e, this), i) + }, Ci.prototype.recycleAsyncId = function(e, t, i) { + if (null !== (i = void 0 === i ? 0 : i) && this.delay === i && !1 === this.pending) return t; + clearInterval(t) + }, Ci.prototype.execute = function(e, t) { + if (this.closed) return new Error("executing a cancelled action"); + this.pending = !1; + t = this._execute(e, t); + if (t) return t; + !1 === this.pending && null != this.id && (this.id = this.recycleAsyncId(this.scheduler, this.id, null)) + }, Ci.prototype._execute = function(e, t) { + var i = !1, + r = void 0; + try { + this.work(e) + } catch (e) { + i = !0, r = !!e && e || new Error(e) + } + if (i) return this.unsubscribe(), r + }, Ci.prototype._unsubscribe = function() { + var e = this.id, + t = this.scheduler, + i = t.actions, + r = i.indexOf(this); + this.work = null, this.state = null, this.pending = !1, this.scheduler = null, -1 !== r && i.splice(r, 1), null != e && (this.id = this.recycleAsyncId(t, e, null)), this.delay = null + }, Ci), + vi = (ut(ki, ai = A), ki.prototype.schedule = function(e, t) { + return 0 < (t = void 0 === t ? 0 : t) ? ai.prototype.schedule.call(this, e, t) : (this.delay = t, this.state = e, this.scheduler.flush(this), this) + }, ki.prototype.execute = function(e, t) { + return 0 < t || this.closed ? ai.prototype.execute.call(this, e, t) : this._execute(e, t) + }, ki.prototype.requestAsyncId = function(e, t, i) { + return null !== (i = void 0 === i ? 0 : i) && 0 < i || null === i && 0 < this.delay ? ai.prototype.requestAsyncId.call(this, e, t, i) : e.flush(this) + }, ki), + Si = (Oi.prototype.schedule = function(e, t, i) { + return void 0 === t && (t = 0), new this.SchedulerAction(this, e).schedule(i, t) + }, Oi.now = function() { + return Date.now() + }, Oi), + bi = (ut(Ai, si = Si), Ai.prototype.schedule = function(e, t, i) { + return void 0 === t && (t = 0), Ai.delegate && Ai.delegate !== this ? Ai.delegate.schedule(e, t, i) : si.prototype.schedule.call(this, e, t, i) + }, Ai.prototype.flush = function(e) { + var t, i = this.actions; + if (this.active) i.push(e); + else { + this.active = !0; + do { + if (t = e.execute(e.state, e.delay)) break + } while (e = i.shift()); + if (this.active = !1, t) { + for (; e = i.shift();) e.unsubscribe(); + throw t + } + } + }, Ai), + Ti = (ut(wi, ni = bi), new wi(vi)), + Ei = Ti, + Ii = new $t(function(e) { + return e.complete() + }); + + function wi() { + return null !== ni && ni.apply(this, arguments) || this + } + + function Ai(e, t) { + void 0 === t && (t = Si.now); + var i = si.call(this, e, function() { + return Ai.delegate && Ai.delegate !== i ? Ai.delegate.now() : t() + }) || this; + return i.actions = [], i.active = !1, i.scheduled = void 0, i + } + + function Oi(e, t) { + void 0 === t && (t = Oi.now), this.SchedulerAction = e, this.now = t + } + + function ki(e, t) { + var i = ai.call(this, e, t) || this; + return i.scheduler = e, i.work = t, i + } + + function Ci(e, t) { + var i = oi.call(this, e, t) || this; + return i.scheduler = e, i.work = t, i.pending = !1, i + } + + function Di(e, t) { + return li.call(this) || this + } + + function Mi(e) { + var t = di.call(this) || this; + return t._value = e, t + } + + function xi(e, t) { + e = ui.call(this, e) || this; + return e.connectable = t, e + } + + function Pi(e, t) { + var i = ci.call(this) || this; + return i.source = e, i.subjectFactory = t, i._refCount = 0, i._isComplete = !1, i + } + + function Ri(e, t) { + e = hi.call(this, e) || this; + return e.connectable = t, e + } + + function Li(e) { + this.connectable = e + } + + function _i(e) { + return e ? (t = e, new $t(function(e) { + return t.schedule(function() { + return e.complete() + }) + })) : Ii; + var t + } + + function Ni(e) { + return e && "function" == typeof e.schedule + } + var Fi = function(r) { + return function(e) { + for (var t = 0, i = r.length; t < i && !e.closed; t++) e.next(r[t]); + e.complete() + } + }; + + function Bi(r, n) { + return new $t(function(e) { + var t = new At, + i = 0; + return t.add(n.schedule(function() { + i !== r.length ? (e.next(r[i++]), e.closed || t.add(this.schedule())) : e.complete() + })), t + }) + } + + function Ui(e, t) { + return t ? Bi(e, t) : new $t(Fi(e)) + } + + function $i() { + for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; + var i = e[e.length - 1]; + return Ni(i) ? (e.pop(), Bi(e, i)) : Ui(e) + } + + function Vi(t, i) { + return new $t(i ? function(e) { + return i.schedule(Ki, 0, { + error: t, + subscriber: e + }) + } : function(e) { + return e.error(t) + }) + } + + function Ki(e) { + var t = e.error; + e.subscriber.error(t) + } + var qi = (Hi.prototype.observe = function(e) { + switch (this.kind) { + case "N": + return e.next && e.next(this.value); + case "E": + return e.error && e.error(this.error); + case "C": + return e.complete && e.complete() + } + }, Hi.prototype.do = function(e, t, i) { + switch (this.kind) { + case "N": + return e && e(this.value); + case "E": + return t && t(this.error); + case "C": + return i && i() + } + }, Hi.prototype.accept = function(e, t, i) { + return e && "function" == typeof e.next ? this.observe(e) : this.do(e, t, i) + }, Hi.prototype.toObservable = function() { + switch (this.kind) { + case "N": + return $i(this.value); + case "E": + return Vi(this.error); + case "C": + return _i() + } + throw new Error("unexpected notification kind value") + }, Hi.createNext = function(e) { + return void 0 !== e ? new Hi("N", e) : Hi.undefinedValueNotification + }, Hi.createError = function(e) { + return new Hi("E", void 0, e) + }, Hi.createComplete = function() { + return Hi.completeNotification + }, Hi.completeNotification = new Hi("C"), Hi.undefinedValueNotification = new Hi("N", void 0), Hi); + + function Hi(e, t, i) { + this.kind = e, this.value = t, this.error = i, this.hasValue = "N" === e + } + + function ji(t, i) { + return void 0 === i && (i = 0), + function(e) { + return e.lift(new zi(t, i)) + } + } + var Qi, Wi, Gi, zi = (ar.prototype.call = function(e, t) { + return t.subscribe(new Xi(e, this.scheduler, this.delay)) + }, ar), + Xi = (ut(sr, Gi = Pt), sr.dispatch = function(e) { + var t = e.notification, + e = e.destination; + t.observe(e), this.unsubscribe() + }, sr.prototype.scheduleMessage = function(e) { + this.destination.add(this.scheduler.schedule(sr.dispatch, this.delay, new Yi(e, this.destination))) + }, sr.prototype._next = function(e) { + this.scheduleMessage(qi.createNext(e)) + }, sr.prototype._error = function(e) { + this.scheduleMessage(qi.createError(e)), this.unsubscribe() + }, sr.prototype._complete = function() { + this.scheduleMessage(qi.createComplete()), this.unsubscribe() + }, sr), + Yi = function(e, t) { + this.notification = e, this.destination = t + }, + Ji = (ut(nr, Wi = Xt), nr.prototype.nextInfiniteTimeWindow = function(e) { + var t; + this.isStopped || ((t = this._events).push(e), t.length > this._bufferSize && t.shift()), Wi.prototype.next.call(this, e) + }, nr.prototype.nextTimeWindow = function(e) { + this.isStopped || (this._events.push(new Zi(this._getNow(), e)), this._trimBufferThenGetEvents()), Wi.prototype.next.call(this, e) + }, nr.prototype._subscribe = function(e) { + var t, i = this._infiniteTimeWindow, + r = i ? this._events : this._trimBufferThenGetEvents(), + n = this.scheduler, + s = r.length; + if (this.closed) throw new Wt; + if (t = this.isStopped || this.hasError ? At.EMPTY : (this.observers.push(e), new Gt(this, e)), n && e.add(e = new Xi(e, n)), i) + for (var a = 0; a < s && !e.closed; a++) e.next(r[a]); + else + for (a = 0; a < s && !e.closed; a++) e.next(r[a].value); + return this.hasError ? e.error(this.thrownError) : this.isStopped && e.complete(), t + }, nr.prototype._getNow = function() { + return (this.scheduler || Ei).now() + }, nr.prototype._trimBufferThenGetEvents = function() { + for (var e = this._getNow(), t = this._bufferSize, i = this._windowTime, r = this._events, n = r.length, s = 0; s < n && !(e - r[s].time < i);) s++; + return 0 < (s = t < n ? Math.max(s, n - t) : s) && r.splice(0, s), r + }, nr), + Zi = function(e, t) { + this.time = e, this.value = t + }, + er = (ut(rr, Qi = Xt), rr.prototype._subscribe = function(e) { + return this.hasError ? (e.error(this.thrownError), At.EMPTY) : this.hasCompleted && this.hasNext ? (e.next(this.value), e.complete(), At.EMPTY) : Qi.prototype._subscribe.call(this, e) + }, rr.prototype.next = function(e) { + this.hasCompleted || (this.value = e, this.hasNext = !0) + }, rr.prototype.error = function(e) { + this.hasCompleted || Qi.prototype.error.call(this, e) + }, rr.prototype.complete = function() { + this.hasCompleted = !0, this.hasNext && Qi.prototype.next.call(this, this.value), Qi.prototype.complete.call(this) + }, rr), + tr = new bi(A), + ir = tr; + + function rr() { + var e = null !== Qi && Qi.apply(this, arguments) || this; + return e.value = null, e.hasNext = !1, e.hasCompleted = !1, e + } + + function nr(e, t, i) { + void 0 === e && (e = Number.POSITIVE_INFINITY), void 0 === t && (t = Number.POSITIVE_INFINITY); + var r = Wi.call(this) || this; + return r.scheduler = i, r._events = [], r._infiniteTimeWindow = !1, r._bufferSize = e < 1 ? 1 : e, r._windowTime = t < 1 ? 1 : t, t === Number.POSITIVE_INFINITY ? (r._infiniteTimeWindow = !0, r.next = r.nextInfiniteTimeWindow) : r.next = r.nextTimeWindow, r + } + + function sr(e, t, i) { + void 0 === i && (i = 0); + e = Gi.call(this, e) || this; + return e.scheduler = t, e.delay = i, e + } + + function ar(e, t) { + void 0 === t && (t = 0), this.scheduler = e, this.delay = t + } + + function or() {} + var lr = (cr.prototype = Object.create(Error.prototype), cr), + dr = (ur.prototype = Object.create(Error.prototype), ur); + + function ur() { + return Error.call(this), this.message = "Timeout has occurred", this.name = "TimeoutError", this + } + + function cr() { + return Error.call(this), this.message = "argument out of range", this.name = "ArgumentOutOfRangeError", this + } + + function hr(t, i) { + return function(e) { + if ("function" != typeof t) throw new TypeError("argument is not a function. Are you looking for `mapTo()`?"); + return e.lift(new gr(t, i)) + } + } + var pr, fr, mr, gr = (Ir.prototype.call = function(e, t) { + return t.subscribe(new yr(e, this.project, this.thisArg)) + }, Ir), + yr = (ut(Er, mr = Pt), Er.prototype._next = function(e) { + var t; + try { + t = this.project.call(this.thisArg, e, this.count++) + } catch (e) { + return void this.destination.error(e) + } + this.destination.next(t) + }, Er), + vr = (ut(Tr, fr = Pt), Tr.prototype.notifyNext = function(e, t, i, r, n) { + this.destination.next(t) + }, Tr.prototype.notifyError = function(e, t) { + this.destination.error(e) + }, Tr.prototype.notifyComplete = function(e) { + this.destination.complete() + }, Tr), + Sr = (ut(br, pr = Pt), br.prototype._next = function(e) { + this.parent.notifyNext(this.outerValue, e, this.outerIndex, this.index++, this) + }, br.prototype._error = function(e) { + this.parent.notifyError(e, this), this.unsubscribe() + }, br.prototype._complete = function() { + this.parent.notifyComplete(this), this.unsubscribe() + }, br); + + function br(e, t, i) { + var r = pr.call(this) || this; + return r.parent = e, r.outerValue = t, r.outerIndex = i, r.index = 0, r + } + + function Tr() { + return null !== fr && fr.apply(this, arguments) || this + } + + function Er(e, t, i) { + e = mr.call(this, e) || this; + return e.project = t, e.count = 0, e.thisArg = i || e, e + } + + function Ir(e, t) { + this.project = e, this.thisArg = t + } + var wr = "function" == typeof Symbol && Symbol.iterator ? Symbol.iterator : "@@iterator", + Ar = function(e) { + return e && "number" == typeof e.length && "function" != typeof e + }; + + function Or(e) { + return e && "function" != typeof e.subscribe && "function" == typeof e.then + } + var kr = function(e) { + if (e && "function" == typeof e[Nt]) return n = e, + function(e) { + var t = n[Nt](); + if ("function" != typeof t.subscribe) throw new TypeError("Provided object does not correctly implement Symbol.observable"); + return t.subscribe(e) + }; + if (Ar(e)) return Fi(e); + if (Or(e)) return i = e, + function(t) { + return i.then(function(e) { + t.closed || (t.next(e), t.complete()) + }, function(e) { + return t.error(e) + }).then(null, bt), t + }; + if (e && "function" == typeof e[wr]) return r = e, + function(t) { + for (var e = r[wr]();;) { + var i = void 0; + try { + i = e.next() + } catch (e) { + return t.error(e), t + } + if (i.done) { + t.complete(); + break + } + if (t.next(i.value), t.closed) break + } + return "function" == typeof e.return && t.add(function() { + e.return && e.return() + }), t + }; + var r, i, n, e = It(e) ? "an invalid object" : "'" + e + "'"; + throw new TypeError("You provided " + e + " where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.") + }; + + function Cr(e, t, i, r, n) { + if (!(n = void 0 === n ? new Sr(e, i, r) : n).closed) return t instanceof $t ? t.subscribe(n) : kr(t)(n) + } + var Dr = {}; + + function Mr() { + for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; + var i = void 0, + r = void 0; + return Ni(e[e.length - 1]) && (r = e.pop()), "function" == typeof e[e.length - 1] && (i = e.pop()), Ui(e = 1 === e.length && Et(e[0]) ? e[0] : e, r).lift(new Pr(i)) + } + var xr, Pr = (_r.prototype.call = function(e, t) { + return t.subscribe(new Rr(e, this.resultSelector)) + }, _r), + Rr = (ut(Lr, xr = vr), Lr.prototype._next = function(e) { + this.values.push(Dr), this.observables.push(e) + }, Lr.prototype._complete = function() { + var e = this.observables, + t = e.length; + if (0 === t) this.destination.complete(); + else { + this.active = t, this.toRespond = t; + for (var i = 0; i < t; i++) { + var r = e[i]; + this.add(Cr(this, r, void 0, i)) + } + } + }, Lr.prototype.notifyComplete = function(e) { + 0 == --this.active && this.destination.complete() + }, Lr.prototype.notifyNext = function(e, t, i) { + var r = this.values, + n = r[i], + n = this.toRespond ? n === Dr ? --this.toRespond : this.toRespond : 0; + r[i] = t, 0 === n && (this.resultSelector ? this._tryResultSelector(r) : this.destination.next(r.slice())) + }, Lr.prototype._tryResultSelector = function(e) { + var t; + try { + t = this.resultSelector.apply(this, e) + } catch (e) { + return void this.destination.error(e) + } + this.destination.next(t) + }, Lr); + + function Lr(e, t) { + e = xr.call(this, e) || this; + return e.resultSelector = t, e.active = 0, e.values = [], e.observables = [], e + } + + function _r(e) { + this.resultSelector = e + } + + function Nr(e, t) { + if (null != e) { + if (e && "function" == typeof e[Nt]) return s = e, a = t, new $t(function(t) { + var i = new At; + return i.add(a.schedule(function() { + var e = s[Nt](); + i.add(e.subscribe({ + next: function(e) { + i.add(a.schedule(function() { + return t.next(e) + })) + }, + error: function(e) { + i.add(a.schedule(function() { + return t.error(e) + })) + }, + complete: function() { + i.add(a.schedule(function() { + return t.complete() + })) + } + })) + })), i + }); + if (Or(e)) return r = e, n = t, new $t(function(t) { + var i = new At; + return i.add(n.schedule(function() { + return r.then(function(e) { + i.add(n.schedule(function() { + t.next(e), i.add(n.schedule(function() { + return t.complete() + })) + })) + }, function(e) { + i.add(n.schedule(function() { + return t.error(e) + })) + }) + })), i + }); + if (Ar(e)) return Bi(e, t); + if (e && "function" == typeof e[wr] || "string" == typeof e) return function(t, i) { + if (!t) throw new Error("Iterable cannot be null"); + return new $t(function(r) { + var n, e = new At; + return e.add(function() { + n && "function" == typeof n.return && n.return() + }), e.add(i.schedule(function() { + n = t[wr](), e.add(i.schedule(function() { + if (!r.closed) { + try { + var e = n.next(), + t = e.value, + i = e.done + } catch (t) { + return void r.error(t) + } + i ? r.complete() : (r.next(t), this.schedule()) + } + })) + })), e + }) + }(e, t) + } + var r, n, s, a; + throw new TypeError((null !== e && typeof e || e) + " is not observable") + } + + function Fr(e, t) { + return t ? Nr(e, t) : e instanceof $t ? e : new $t(kr(e)) + } + var Br, Ur, $r = (ut(qr, Ur = Pt), qr.prototype._next = function(e) { + this.parent.notifyNext(e) + }, qr.prototype._error = function(e) { + this.parent.notifyError(e), this.unsubscribe() + }, qr.prototype._complete = function() { + this.parent.notifyComplete(), this.unsubscribe() + }, qr), + Vr = (ut(Kr, Br = Pt), Kr.prototype.notifyNext = function(e) { + this.destination.next(e) + }, Kr.prototype.notifyError = function(e) { + this.destination.error(e) + }, Kr.prototype.notifyComplete = function() { + this.destination.complete() + }, Kr); + + function Kr() { + return null !== Br && Br.apply(this, arguments) || this + } + + function qr(e) { + var t = Ur.call(this) || this; + return t.parent = e, t + } + + function Hr(e, t) { + if (!t.closed) return e instanceof $t ? e.subscribe(t) : kr(e)(t) + } + + function jr(t, n, i) { + return void 0 === i && (i = Number.POSITIVE_INFINITY), "function" == typeof n ? function(e) { + return e.pipe(jr(function(i, r) { + return Fr(t(i, r)).pipe(hr(function(e, t) { + return n(i, e, r, t) + })) + }, i)) + } : ("number" == typeof n && (i = n), function(e) { + return e.lift(new Wr(t, i)) + }) + } + var Qr, Wr = (Xr.prototype.call = function(e, t) { + return t.subscribe(new Gr(e, this.project, this.concurrent)) + }, Xr), + Gr = (ut(zr, Qr = Vr), zr.prototype._next = function(e) { + this.active < this.concurrent ? this._tryNext(e) : this.buffer.push(e) + }, zr.prototype._tryNext = function(e) { + var t, i = this.index++; + try { + t = this.project(e, i) + } catch (e) { + return void this.destination.error(e) + } + this.active++, this._innerSub(t) + }, zr.prototype._innerSub = function(e) { + var t = new $r(this), + i = this.destination; + i.add(t); + e = Hr(e, t); + e !== t && i.add(e) + }, zr.prototype._complete = function() { + this.hasCompleted = !0, 0 === this.active && 0 === this.buffer.length && this.destination.complete(), this.unsubscribe() + }, zr.prototype.notifyNext = function(e) { + this.destination.next(e) + }, zr.prototype.notifyComplete = function() { + var e = this.buffer; + this.active--, 0 < e.length ? this._next(e.shift()) : 0 === this.active && this.hasCompleted && this.destination.complete() + }, zr); + + function zr(e, t, i) { + void 0 === i && (i = Number.POSITIVE_INFINITY); + e = Qr.call(this, e) || this; + return e.project = t, e.concurrent = i, e.hasCompleted = !1, e.buffer = [], e.active = 0, e.index = 0, e + } + + function Xr(e, t) { + void 0 === t && (t = Number.POSITIVE_INFINITY), this.project = e, this.concurrent = t + } + + function Yr(e) { + return jr(Ft, e = void 0 === e ? Number.POSITIVE_INFINITY : e) + } + + function Jr() { + for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; + return Yr(1)($i.apply(void 0, e)) + } + + function Zr(i) { + return new $t(function(t) { + var e; + try { + e = i() + } catch (e) { + return void t.error(e) + } + return (e ? Fr(e) : _i()).subscribe(t) + }) + } + + function en() { + for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; + if (1 === e.length) { + var i = e[0]; + if (Et(i)) return tn(i, null); + if (It(i) && Object.getPrototypeOf(i) === Object.prototype) { + var r = Object.keys(i); + return tn(r.map(function(e) { + return i[e] + }), r) + } + } + if ("function" != typeof e[e.length - 1]) return tn(e, null); + var n = e.pop(); + return tn(e = 1 === e.length && Et(e[0]) ? e[0] : e, null).pipe(hr(function(e) { + return n.apply(void 0, e) + })) + } + + function tn(l, d) { + return new $t(function(r) { + var n = l.length; + if (0 !== n) + for (var s = new Array(n), a = 0, o = 0, e = 0; e < n; e++) ! function(t) { + var e = Fr(l[t]), + i = !1; + r.add(e.subscribe({ + next: function(e) { + i || (i = !0, o++), s[t] = e + }, + error: function(e) { + return r.error(e) + }, + complete: function() { + ++a !== n && i || (o === n && r.next(d ? d.reduce(function(e, t, i) { + return e[t] = s[i], e + }, {}) : s), r.complete()) + } + })) + }(e); + else r.complete() + }) + } + + function rn(e, i, r, t) { + return yt(r) && (t = r, r = void 0), t ? rn(e, i, r).pipe(hr(function(e) { + return Et(e) ? t.apply(void 0, e) : t(e) + })) : new $t(function(t) { + ! function e(t, i, r, n, s) { + var a; + if (function(e) { + return e && "function" == typeof e.addEventListener && "function" == typeof e.removeEventListener + }(t)) { + var o = t; + t.addEventListener(i, r, s), a = function() { + return o.removeEventListener(i, r, s) + } + } else if (function(e) { + return e && "function" == typeof e.on && "function" == typeof e.off + }(t)) { + var l = t; + t.on(i, r), a = function() { + return l.off(i, r) + } + } else if (function(e) { + return e && "function" == typeof e.addListener && "function" == typeof e.removeListener + }(t)) { + var d = t; + t.addListener(i, r), a = function() { + return d.removeListener(i, r) + } + } else { + if (!t || !t.length) throw new TypeError("Invalid event target"); + for (var u = 0, c = t.length; u < c; u++) e(t[u], i, r, n, s) + } + n.add(a) + }(e, i, function(e) { + 1 < arguments.length ? t.next(Array.prototype.slice.call(arguments)) : t.next(e) + }, t, r) + }) + } + + function nn(e, t, i) { + return void 0 === t && (t = Ii), void 0 === i && (i = Ii), Zr(function() { + return e() ? t : i + }) + } + + function sn(e) { + return !Et(e) && 0 <= e - parseFloat(e) + 1 + } + + function an() { + for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; + var i = Number.POSITIVE_INFINITY, + r = null, + n = e[e.length - 1]; + return Ni(n) ? (r = e.pop(), 1 < e.length && "number" == typeof e[e.length - 1] && (i = e.pop())) : "number" == typeof n && (i = e.pop()), null === r && 1 === e.length && e[0] instanceof $t ? e[0] : Yr(i)(Ui(e, r)) + } + var on = new $t(or); + + function ln(t, i) { + return function(e) { + return e.lift(new un(t, i)) + } + } + var dn, un = (pn.prototype.call = function(e, t) { + return t.subscribe(new cn(e, this.predicate, this.thisArg)) + }, pn), + cn = (ut(hn, dn = Pt), hn.prototype._next = function(e) { + var t; + try { + t = this.predicate.call(this.thisArg, e, this.count++) + } catch (e) { + return void this.destination.error(e) + } + t && this.destination.next(e) + }, hn); + + function hn(e, t, i) { + e = dn.call(this, e) || this; + return e.predicate = t, e.thisArg = i, e.count = 0, e + } + + function pn(e, t) { + this.predicate = e, this.thisArg = t + } + + function fn() { + for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; + if (1 === e.length) { + if (!Et(e[0])) return e[0]; + e = e[0] + } + return Ui(e, void 0).lift(new gn) + } + var mn, gn = (Sn.prototype.call = function(e, t) { + return t.subscribe(new yn(e)) + }, Sn), + yn = (ut(vn, mn = vr), vn.prototype._next = function(e) { + this.observables.push(e) + }, vn.prototype._complete = function() { + var e = this.observables, + t = e.length; + if (0 === t) this.destination.complete(); + else { + for (var i = 0; i < t && !this.hasFirst; i++) { + var r = Cr(this, e[i], void 0, i); + this.subscriptions && this.subscriptions.push(r), this.add(r) + } + this.observables = null + } + }, vn.prototype.notifyNext = function(e, t, i) { + if (!this.hasFirst) { + this.hasFirst = !0; + for (var r, n = 0; n < this.subscriptions.length; n++) n !== i && ((r = this.subscriptions[n]).unsubscribe(), this.remove(r)); + this.subscriptions = null + } + this.destination.next(t) + }, vn); + + function vn(e) { + e = mn.call(this, e) || this; + return e.hasFirst = !1, e.observables = [], e.subscriptions = [], e + } + + function Sn() {} + + function bn(i, e, r) { + void 0 === i && (i = 0); + var n = -1; + return sn(e) ? n = Number(e) < 1 ? 1 : Number(e) : Ni(e) && (r = e), Ni(r) || (r = ir), new $t(function(e) { + var t = sn(i) ? i : +i - r.now(); + return r.schedule(Tn, t, { + index: 0, + period: n, + subscriber: e + }) + }) + } + + function Tn(e) { + var t = e.index, + i = e.period, + r = e.subscriber; + if (r.next(t), !r.closed) { + if (-1 === i) return r.complete(); + e.index = t + 1, this.schedule(e, i) + } + } + + function En() { + for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; + var i = e[e.length - 1]; + return "function" == typeof i && e.pop(), Ui(e, void 0).lift(new An(i)) + } + var In, wn, An = (Ln.prototype.call = function(e, t) { + return t.subscribe(new On(e, this.resultSelector)) + }, Ln), + On = (ut(Rn, wn = Pt), Rn.prototype._next = function(e) { + var t = this.iterators; + Et(e) ? t.push(new Cn(e)) : "function" == typeof e[wr] ? t.push(new kn(e[wr]())) : t.push(new Dn(this.destination, this, e)) + }, Rn.prototype._complete = function() { + var e = this.iterators, + t = e.length; + if (this.unsubscribe(), 0 !== t) { + this.active = t; + for (var i = 0; i < t; i++) { + var r = e[i]; + r.stillUnsubscribed ? this.destination.add(r.subscribe()) : this.active-- + } + } else this.destination.complete() + }, Rn.prototype.notifyInactive = function() { + this.active--, 0 === this.active && this.destination.complete() + }, Rn.prototype.checkIterators = function() { + for (var e = this.iterators, t = e.length, i = this.destination, r = 0; r < t; r++) + if ("function" == typeof(a = e[r]).hasValue && !a.hasValue()) return; + for (var n = !1, s = [], r = 0; r < t; r++) { + var a, o = (a = e[r]).next(); + if (a.hasCompleted() && (n = !0), o.done) return void i.complete(); + s.push(o.value) + } + this.resultSelector ? this._tryresultSelector(s) : i.next(s), n && i.complete() + }, Rn.prototype._tryresultSelector = function(e) { + var t; + try { + t = this.resultSelector.apply(this, e) + } catch (e) { + return void this.destination.error(e) + } + this.destination.next(t) + }, Rn), + kn = (Pn.prototype.hasValue = function() { + return !0 + }, Pn.prototype.next = function() { + var e = this.nextResult; + return this.nextResult = this.iterator.next(), e + }, Pn.prototype.hasCompleted = function() { + var e = this.nextResult; + return Boolean(e && e.done) + }, Pn), + Cn = (xn.prototype[wr] = function() { + return this + }, xn.prototype.next = function(e) { + var t = this.index++, + i = this.array; + return t < this.length ? { + value: i[t], + done: !1 + } : { + value: null, + done: !0 + } + }, xn.prototype.hasValue = function() { + return this.array.length > this.index + }, xn.prototype.hasCompleted = function() { + return this.array.length === this.index + }, xn), + Dn = (ut(Mn, In = Vr), Mn.prototype[wr] = function() { + return this + }, Mn.prototype.next = function() { + var e = this.buffer; + return 0 === e.length && this.isComplete ? { + value: null, + done: !0 + } : { + value: e.shift(), + done: !1 + } + }, Mn.prototype.hasValue = function() { + return 0 < this.buffer.length + }, Mn.prototype.hasCompleted = function() { + return 0 === this.buffer.length && this.isComplete + }, Mn.prototype.notifyComplete = function() { + 0 < this.buffer.length ? (this.isComplete = !0, this.parent.notifyInactive()) : this.destination.complete() + }, Mn.prototype.notifyNext = function(e) { + this.buffer.push(e), this.parent.checkIterators() + }, Mn.prototype.subscribe = function() { + return Hr(this.observable, new $r(this)) + }, Mn); + + function Mn(e, t, i) { + e = In.call(this, e) || this; + return e.parent = t, e.observable = i, e.stillUnsubscribed = !0, e.buffer = [], e.isComplete = !1, e + } + + function xn(e) { + this.array = e, this.index = 0, this.length = 0, this.length = e.length + } + + function Pn(e) { + this.iterator = e, this.nextResult = e.next() + } + + function Rn(e, t, i) { + e = wn.call(this, e) || this; + return e.resultSelector = t, e.iterators = [], e.active = 0, e.resultSelector = "function" == typeof t ? t : void 0, e + } + + function Ln(e) { + this.resultSelector = e + } + var _n, Nn = (Un.prototype.call = function(e, t) { + return t.subscribe(new Fn(e, this.durationSelector)) + }, Un), + Fn = (ut(Bn, _n = Vr), Bn.prototype._next = function(e) { + if (this.value = e, this.hasValue = !0, !this.throttled) { + var t = void 0; + try { + t = (0, this.durationSelector)(e) + } catch (e) { + return this.destination.error(e) + } + t = Hr(t, new $r(this)); + !t || t.closed ? this.clearThrottle() : this.add(this.throttled = t) + } + }, Bn.prototype.clearThrottle = function() { + var e = this.value, + t = this.hasValue, + i = this.throttled; + i && (this.remove(i), this.throttled = void 0, i.unsubscribe()), t && (this.value = void 0, this.hasValue = !1, this.destination.next(e)) + }, Bn.prototype.notifyNext = function() { + this.clearThrottle() + }, Bn.prototype.notifyComplete = function() { + this.clearThrottle() + }, Bn); + + function Bn(e, t) { + e = _n.call(this, e) || this; + return e.durationSelector = t, e.hasValue = !1, e + } + + function Un(e) { + this.durationSelector = e + } + + function $n(e, t) { + return void 0 === t && (t = ir), i = function() { + return bn(e, t) + }, + function(e) { + return e.lift(new Nn(i)) + }; + var i + } + + function Vn(i) { + return function(e) { + var t = new qn(i), + e = e.lift(t); + return t.caught = e + } + } + var Kn, qn = (Qn.prototype.call = function(e, t) { + return t.subscribe(new Hn(e, this.selector, this.caught)) + }, Qn), + Hn = (ut(jn, Kn = Vr), jn.prototype.error = function(e) { + if (!this.isStopped) { + var t = void 0; + try { + t = this.selector(e, this.caught) + } catch (e) { + return void Kn.prototype.error.call(this, e) + } + this._unsubscribeAndRecycle(); + var i = new $r(this); + this.add(i); + t = Hr(t, i); + t !== i && this.add(t) + } + }, jn); + + function jn(e, t, i) { + e = Kn.call(this, e) || this; + return e.selector = t, e.caught = i, e + } + + function Qn(e) { + this.selector = e + } + + function Wn(e, t) { + return jr(e, t, 1) + } + + function Gn(t, i) { + return void 0 === i && (i = ir), + function(e) { + return e.lift(new Xn(t, i)) + } + } + var zn, Xn = (Zn.prototype.call = function(e, t) { + return t.subscribe(new Yn(e, this.dueTime, this.scheduler)) + }, Zn), + Yn = (ut(Jn, zn = Pt), Jn.prototype._next = function(e) { + this.clearDebounce(), this.lastValue = e, this.hasValue = !0, this.add(this.debouncedSubscription = this.scheduler.schedule(es, this.dueTime, this)) + }, Jn.prototype._complete = function() { + this.debouncedNext(), this.destination.complete() + }, Jn.prototype.debouncedNext = function() { + var e; + this.clearDebounce(), this.hasValue && (e = this.lastValue, this.lastValue = null, this.hasValue = !1, this.destination.next(e)) + }, Jn.prototype.clearDebounce = function() { + var e = this.debouncedSubscription; + null !== e && (this.remove(e), e.unsubscribe(), this.debouncedSubscription = null) + }, Jn); + + function Jn(e, t, i) { + e = zn.call(this, e) || this; + return e.dueTime = t, e.scheduler = i, e.debouncedSubscription = null, e.lastValue = null, e.hasValue = !1, e + } + + function Zn(e, t) { + this.dueTime = e, this.scheduler = t + } + + function es(e) { + e.debouncedNext() + } + var ts, is = (ss.prototype.call = function(e, t) { + return t.subscribe(new rs(e, this.defaultValue)) + }, ss), + rs = (ut(ns, ts = Pt), ns.prototype._next = function(e) { + this.isEmpty = !1, this.destination.next(e) + }, ns.prototype._complete = function() { + this.isEmpty && this.destination.next(this.defaultValue), this.destination.complete() + }, ns); + + function ns(e, t) { + e = ts.call(this, e) || this; + return e.defaultValue = t, e.isEmpty = !0, e + } + + function ss(e) { + this.defaultValue = e + } + + function as(e) { + return e instanceof Date && !isNaN(+e) + } + var os, ls = (hs.prototype.call = function(e, t) { + return t.subscribe(new ds(e, this.delay, this.scheduler)) + }, hs), + ds = (ut(cs, os = Pt), cs.dispatch = function(e) { + for (var t, i = e.source, r = i.queue, n = e.scheduler, s = e.destination; 0 < r.length && r[0].time - n.now() <= 0;) r.shift().notification.observe(s); + 0 < r.length ? (t = Math.max(0, r[0].time - n.now()), this.schedule(e, t)) : (this.unsubscribe(), i.active = !1) + }, cs.prototype._schedule = function(e) { + this.active = !0, this.destination.add(e.schedule(cs.dispatch, this.delay, { + source: this, + destination: this.destination, + scheduler: e + })) + }, cs.prototype.scheduleNotification = function(e) { + var t; + !0 !== this.errored && (t = this.scheduler, e = new us(t.now() + this.delay, e), this.queue.push(e), !1 === this.active && this._schedule(t)) + }, cs.prototype._next = function(e) { + this.scheduleNotification(qi.createNext(e)) + }, cs.prototype._error = function(e) { + this.errored = !0, this.queue = [], this.destination.error(e), this.unsubscribe() + }, cs.prototype._complete = function() { + this.scheduleNotification(qi.createComplete()), this.unsubscribe() + }, cs), + us = function(e, t) { + this.time = e, this.notification = t + }; + + function cs(e, t, i) { + e = os.call(this, e) || this; + return e.delay = t, e.scheduler = i, e.queue = [], e.active = !1, e.errored = !1, e + } + + function hs(e, t) { + this.delay = e, this.scheduler = t + } + var ps, fs, ms, gs = (Es.prototype.call = function(e, t) { + return t.subscribe(new ys(e, this.delayDurationSelector)) + }, Es), + ys = (ut(Ts, ms = vr), Ts.prototype.notifyNext = function(e, t, i, r, n) { + this.destination.next(e), this.removeSubscription(n), this.tryComplete() + }, Ts.prototype.notifyError = function(e, t) { + this._error(e) + }, Ts.prototype.notifyComplete = function(e) { + e = this.removeSubscription(e); + e && this.destination.next(e), this.tryComplete() + }, Ts.prototype._next = function(e) { + var t = this.index++; + try { + var i = this.delayDurationSelector(e, t); + i && this.tryDelay(i, e) + } catch (e) { + this.destination.error(e) + } + }, Ts.prototype._complete = function() { + this.completed = !0, this.tryComplete(), this.unsubscribe() + }, Ts.prototype.removeSubscription = function(e) { + e.unsubscribe(); + var t = this.delayNotifierSubscriptions.indexOf(e); + return -1 !== t && this.delayNotifierSubscriptions.splice(t, 1), e.outerValue + }, Ts.prototype.tryDelay = function(e, t) { + t = Cr(this, e, t); + t && !t.closed && (this.destination.add(t), this.delayNotifierSubscriptions.push(t)) + }, Ts.prototype.tryComplete = function() { + this.completed && 0 === this.delayNotifierSubscriptions.length && this.destination.complete() + }, Ts), + vs = (ut(bs, fs = $t), bs.prototype._subscribe = function(e) { + this.subscriptionDelay.subscribe(new vs(e, this.source)) + }, ut(Ss, ps = Pt), Ss.prototype._next = function(e) { + this.subscribeToSource() + }, Ss.prototype._error = function(e) { + this.unsubscribe(), this.parent.error(e) + }, Ss.prototype._complete = function() { + this.unsubscribe(), this.subscribeToSource() + }, Ss.prototype.subscribeToSource = function() { + this.sourceSubscribed || (this.sourceSubscribed = !0, this.unsubscribe(), this.source.subscribe(this.parent)) + }, Ss); + + function Ss(e, t) { + var i = ps.call(this) || this; + return i.parent = e, i.source = t, i.sourceSubscribed = !1, i + } + + function bs(e, t) { + var i = fs.call(this) || this; + return i.source = e, i.subscriptionDelay = t, i + } + + function Ts(e, t) { + e = ms.call(this, e) || this; + return e.delayDurationSelector = t, e.completed = !1, e.delayNotifierSubscriptions = [], e.index = 0, e + } + + function Es(e) { + this.delayDurationSelector = e + } + + function Is(t, i) { + return function(e) { + return e.lift(new As(t, i)) + } + } + var ws, As = (Cs.prototype.call = function(e, t) { + return t.subscribe(new Os(e, this.compare, this.keySelector)) + }, Cs), + Os = (ut(ks, ws = Pt), ks.prototype.compare = function(e, t) { + return e === t + }, ks.prototype._next = function(e) { + try { + var t = this.keySelector, + i = t ? t(e) : e + } catch (e) { + return this.destination.error(e) + } + t = !1; + if (this.hasKey) try { + t = (0, this.compare)(this.key, i) + } catch (e) { + return this.destination.error(e) + } else this.hasKey = !0; + t || (this.key = i, this.destination.next(e)) + }, ks); + + function ks(e, t, i) { + e = ws.call(this, e) || this; + return e.keySelector = i, e.hasKey = !1, "function" == typeof t && (e.compare = t), e + } + + function Cs(e, t) { + this.compare = e, this.keySelector = t + } + + function Ds(t) { + return function(e) { + return 0 === t ? _i() : e.lift(new xs(t)) + } + } + var Ms, xs = (Ls.prototype.call = function(e, t) { + return t.subscribe(new Ps(e, this.total)) + }, Ls), + Ps = (ut(Rs, Ms = Pt), Rs.prototype._next = function(e) { + var t = this.total, + i = ++this.count; + i <= t && (this.destination.next(e), i === t && (this.destination.complete(), this.unsubscribe())) + }, Rs); + + function Rs(e, t) { + e = Ms.call(this, e) || this; + return e.total = t, e.count = 0, e + } + + function Ls(e) { + if (this.total = e, this.total < 0) throw new lr + } + + function _s(t, n) { + return n ? function(e) { + return e.pipe(_s(function(i, r) { + return Fr(t(i, r)).pipe(hr(function(e, t) { + return n(i, e, r, t) + })) + })) + } : function(e) { + return e.lift(new Fs(t)) + } + } + var Ns, Fs = ($s.prototype.call = function(e, t) { + return t.subscribe(new Bs(e, this.project)) + }, $s), + Bs = (ut(Us, Ns = Vr), Us.prototype._next = function(e) { + this.hasSubscription || this.tryNext(e) + }, Us.prototype.tryNext = function(e) { + var t, i = this.index++; + try { + t = this.project(e, i) + } catch (e) { + return void this.destination.error(e) + } + this.hasSubscription = !0, this._innerSub(t) + }, Us.prototype._innerSub = function(e) { + var t = new $r(this), + i = this.destination; + i.add(t); + e = Hr(e, t); + e !== t && i.add(e) + }, Us.prototype._complete = function() { + this.hasCompleted = !0, this.hasSubscription || this.destination.complete(), this.unsubscribe() + }, Us.prototype.notifyNext = function(e) { + this.destination.next(e) + }, Us.prototype.notifyError = function(e) { + this.destination.error(e) + }, Us.prototype.notifyComplete = function() { + this.hasSubscription = !1, this.hasCompleted && this.destination.complete() + }, Us); + + function Us(e, t) { + e = Ns.call(this, e) || this; + return e.project = t, e.hasSubscription = !1, e.hasCompleted = !1, e.index = 0, e + } + + function $s(e) { + this.project = e + } + + function Vs(t) { + return function(e) { + return e.lift(new qs(t)) + } + } + var Ks, qs = (Qs.prototype.call = function(e, t) { + return t.subscribe(new Hs(e, this.callback)) + }, Qs), + Hs = (ut(js, Ks = Pt), js); + + function js(e, t) { + e = Ks.call(this, e) || this; + return e.add(new At(t)), e + } + + function Qs(e) { + this.callback = e + } + + function Ws(t) { + return function(e) { + return 0 === t ? _i() : e.lift(new zs(t)) + } + } + var Gs, zs = (Js.prototype.call = function(e, t) { + return t.subscribe(new Xs(e, this.total)) + }, Js), + Xs = (ut(Ys, Gs = Pt), Ys.prototype._next = function(e) { + var t = this.ring, + i = this.total, + r = this.count++; + t.length < i ? t.push(e) : t[r % i] = e + }, Ys.prototype._complete = function() { + var e = this.destination, + t = this.count; + if (0 < t) + for (var i = this.count >= this.total ? this.total : this.count, r = this.ring, n = 0; n < i; n++) { + var s = t++ % i; + e.next(r[s]) + } + e.complete() + }, Ys); + + function Ys(e, t) { + e = Gs.call(this, e) || this; + return e.total = t, e.ring = new Array, e.count = 0, e + } + + function Js(e) { + if (this.total = e, this.total < 0) throw new lr + } + + function Zs(t) { + return function(e) { + return e.lift(new ta(t)) + } + } + var ea, ta = (na.prototype.call = function(e, t) { + return t.subscribe(new ia(e, this.value)) + }, na), + ia = (ut(ra, ea = Pt), ra.prototype._next = function(e) { + this.destination.next(this.value) + }, ra); + + function ra(e, t) { + e = ea.call(this, e) || this; + return e.value = t, e + } + + function na(e) { + this.value = e + } + + function sa(t, i) { + var r = 2 <= arguments.length ? !0 : !1; + return function(e) { + return e.lift(new oa(t, i, r)) + } + } + var aa, oa = (ua.prototype.call = function(e, t) { + return t.subscribe(new la(e, this.accumulator, this.seed, this.hasSeed)) + }, ua), + la = (ut(da, aa = Pt), Object.defineProperty(da.prototype, "seed", { + get: function() { + return this._seed + }, + set: function(e) { + this.hasSeed = !0, this._seed = e + }, + enumerable: !0, + configurable: !0 + }), da.prototype._next = function(e) { + if (this.hasSeed) return this._tryNext(e); + this.seed = e, this.destination.next(e) + }, da.prototype._tryNext = function(e) { + var t, i = this.index++; + try { + t = this.accumulator(this.seed, e, i) + } catch (e) { + this.destination.error(e) + } + this.seed = t, this.destination.next(t) + }, da); + + function da(e, t, i, r) { + e = aa.call(this, e) || this; + return e.accumulator = t, e._seed = i, e.hasSeed = r, e.index = 0, e + } + + function ua(e, t, i) { + void 0 === i && (i = !1), this.accumulator = e, this.seed = t, this.hasSeed = i + } + ca.prototype.call = function(e, t) { + var i = this.selector, + r = this.subjectFactory(), + e = i(r).subscribe(e); + return e.add(t.subscribe(r)), e + }; + + function ca(e, t) { + this.subjectFactory = e, this.selector = t + } + + function ha() { + return function(e) { + return e.lift(new fa) + } + } + var pa, fa = (ya.prototype.call = function(e, t) { + return t.subscribe(new ma(e)) + }, ya), + ma = (ut(ga, pa = Pt), ga.prototype._next = function(e) { + var t; + this.hasPrev ? t = [this.prev, e] : this.hasPrev = !0, this.prev = e, t && this.destination.next(t) + }, ga); + + function ga(e) { + e = pa.call(this, e) || this; + return e.hasPrev = !1, e + } + + function ya() {} + + function va(t) { + return function(e) { + return e.lift(new ba(t, e)) + } + } + var Sa, ba = (Ia.prototype.call = function(e, t) { + return t.subscribe(new Ta(e, this.notifier, this.source)) + }, Ia), + Ta = (ut(Ea, Sa = Vr), Ea.prototype.error = function(e) { + if (!this.isStopped) { + var t = this.errors, + i = this.retries, + r = this.retriesSubscription; + if (i) this.errors = void 0, this.retriesSubscription = void 0; + else { + t = new Xt; + try { + i = (0, this.notifier)(t) + } catch (e) { + return Sa.prototype.error.call(this, e) + } + r = Hr(i, new $r(this)) + } + this._unsubscribeAndRecycle(), this.errors = t, this.retries = i, this.retriesSubscription = r, t.next(e) + } + }, Ea.prototype._unsubscribe = function() { + var e = this.errors, + t = this.retriesSubscription; + e && (e.unsubscribe(), this.errors = void 0), t && (t.unsubscribe(), this.retriesSubscription = void 0), this.retries = void 0 + }, Ea.prototype.notifyNext = function() { + var e = this._unsubscribe; + this._unsubscribe = null, this._unsubscribeAndRecycle(), this._unsubscribe = e, this.source.subscribe(this) + }, Ea); + + function Ea(e, t, i) { + e = Sa.call(this, e) || this; + return e.notifier = t, e.source = i, e + } + + function Ia(e, t) { + this.notifier = e, this.source = t + } + + function wa() { + return new Xt + } + + function Aa() { + return function(e) { + return ri()((t = e, i = "function" == typeof(r = wa) ? r : function() { + return r + }, (e = Object.create(t, mi)).source = t, e.subjectFactory = i, e)); + var t, i, r + } + } + + function Oa(e, t, i) { + var c = e && "object" == typeof e ? e : { + bufferSize: e, + windowTime: t, + refCount: !1, + scheduler: i + }; + return function(e) { + return e.lift((e = c.bufferSize, n = void 0 === e ? Number.POSITIVE_INFINITY : e, e = c.windowTime, s = void 0 === e ? Number.POSITIVE_INFINITY : e, a = c.refCount, o = c.scheduler, l = 0, u = d = !1, function(e) { + var t; + l++, !i || d ? (d = !1, i = new Ji(n, s, o), t = i.subscribe(this), r = e.subscribe({ + next: function(e) { + i.next(e) + }, + error: function(e) { + d = !0, i.error(e) + }, + complete: function() { + u = !0, r = void 0, i.complete() + } + }), u && (r = void 0)) : t = i.subscribe(this), this.add(function() { + l--, t.unsubscribe(), t = void 0, r && !u && a && 0 === l && (r.unsubscribe(), i = r = void 0) + }) + })); + var i, r, n, s, a, o, l, d, u + } + } + + function ka(t) { + return function(e) { + return e.lift(new Da(t)) + } + } + var Ca, Da = (Pa.prototype.call = function(e, t) { + return t.subscribe(new Ma(e, this.total)) + }, Pa), + Ma = (ut(xa, Ca = Pt), xa.prototype._next = function(e) { + ++this.count > this.total && this.destination.next(e) + }, xa); + + function xa(e, t) { + e = Ca.call(this, e) || this; + return e.total = t, e.count = 0, e + } + + function Pa(e) { + this.total = e + } + + function Ra() { + for (var t = [], e = 0; e < arguments.length; e++) t[e] = arguments[e]; + var i = t[t.length - 1]; + return Ni(i) ? (t.pop(), function(e) { + return Jr(t, e, i) + }) : function(e) { + return Jr(t, e) + } + } + + function La(t, n) { + return "function" == typeof n ? function(e) { + return e.pipe(La(function(i, r) { + return Fr(t(i, r)).pipe(hr(function(e, t) { + return n(i, e, r, t) + })) + })) + } : function(e) { + return e.lift(new Na(t)) + } + } + var _a, Na = (Ua.prototype.call = function(e, t) { + return t.subscribe(new Fa(e, this.project)) + }, Ua), + Fa = (ut(Ba, _a = Vr), Ba.prototype._next = function(e) { + var t, i = this.index++; + try { + t = this.project(e, i) + } catch (e) { + return void this.destination.error(e) + } + this._innerSub(t) + }, Ba.prototype._innerSub = function(e) { + var t = this.innerSubscription; + t && t.unsubscribe(); + var i = new $r(this), + t = this.destination; + t.add(i), this.innerSubscription = Hr(e, i), this.innerSubscription !== i && t.add(this.innerSubscription) + }, Ba.prototype._complete = function() { + var e = this.innerSubscription; + e && !e.closed || _a.prototype._complete.call(this), this.unsubscribe() + }, Ba.prototype._unsubscribe = function() { + this.innerSubscription = void 0 + }, Ba.prototype.notifyComplete = function() { + this.innerSubscription = void 0, this.isStopped && _a.prototype._complete.call(this) + }, Ba.prototype.notifyNext = function(e) { + this.destination.next(e) + }, Ba); + + function Ba(e, t) { + e = _a.call(this, e) || this; + return e.project = t, e.index = 0, e + } + + function Ua(e) { + this.project = e + } + + function $a(e, t) { + return t ? La(function() { + return e + }, t) : La(function() { + return e + }) + } + + function Va(t) { + return function(e) { + return e.lift(new qa(t)) + } + } + var Ka, qa = (Qa.prototype.call = function(e, t) { + var i = new Ha(e), + e = Hr(this.notifier, new $r(i)); + return e && !i.seenValue ? (i.add(e), t.subscribe(i)) : i + }, Qa), + Ha = (ut(ja, Ka = Vr), ja.prototype.notifyNext = function() { + this.seenValue = !0, this.complete() + }, ja.prototype.notifyComplete = function() {}, ja); + + function ja(e) { + e = Ka.call(this, e) || this; + return e.seenValue = !1, e + } + + function Qa(e) { + this.notifier = e + } + + function Wa(t, i) { + return void 0 === i && (i = !1), + function(e) { + return e.lift(new za(t, i)) + } + } + var Ga, za = (Ja.prototype.call = function(e, t) { + return t.subscribe(new Xa(e, this.predicate, this.inclusive)) + }, Ja), + Xa = (ut(Ya, Ga = Pt), Ya.prototype._next = function(e) { + var t, i = this.destination; + try { + t = this.predicate(e, this.index++) + } catch (e) { + return void i.error(e) + } + this.nextOrComplete(e, t) + }, Ya.prototype.nextOrComplete = function(e, t) { + var i = this.destination; + Boolean(t) ? i.next(e) : (this.inclusive && i.next(e), i.complete()) + }, Ya); + + function Ya(e, t, i) { + e = Ga.call(this, e) || this; + return e.predicate = t, e.inclusive = i, e.index = 0, e + } + + function Ja(e, t) { + this.predicate = e, this.inclusive = t + } + + function Za(t, i, r) { + return function(e) { + return e.lift(new to(t, i, r)) + } + } + var eo, to = (so.prototype.call = function(e, t) { + return t.subscribe(new io(e, this.nextOrObserver, this.error, this.complete)) + }, so), + io = (ut(no, eo = Pt), no.prototype._next = function(e) { + try { + this._tapNext.call(this._context, e) + } catch (e) { + return void this.destination.error(e) + } + this.destination.next(e) + }, no.prototype._error = function(e) { + try { + this._tapError.call(this._context, e) + } catch (e) { + return void this.destination.error(e) + } + this.destination.error(e) + }, no.prototype._complete = function() { + try { + this._tapComplete.call(this._context) + } catch (e) { + return void this.destination.error(e) + } + return this.destination.complete() + }, no), + ro = { + leading: !0, + trailing: !1 + }; + + function no(e, t, i, r) { + e = eo.call(this, e) || this; + return e._tapNext = or, e._tapError = or, e._tapComplete = or, e._tapError = i || or, e._tapComplete = r || or, yt(t) ? (e._context = e)._tapNext = t : t && (e._context = t, e._tapNext = t.next || or, e._tapError = t.error || or, e._tapComplete = t.complete || or), e + } + + function so(e, t, i) { + this.nextOrObserver = e, this.error = t, this.complete = i + } + + function ao(t, i, r) { + return void 0 === i && (i = ir), void 0 === r && (r = ro), + function(e) { + return e.lift(new lo(t, i, r.leading, r.trailing)) + } + } + var oo, lo = (ho.prototype.call = function(e, t) { + return t.subscribe(new uo(e, this.duration, this.scheduler, this.leading, this.trailing)) + }, ho), + uo = (ut(co, oo = Pt), co.prototype._next = function(e) { + this.throttled ? this.trailing && (this._trailingValue = e, this._hasTrailingValue = !0) : (this.add(this.throttled = this.scheduler.schedule(po, this.duration, { + subscriber: this + })), this.leading ? this.destination.next(e) : this.trailing && (this._trailingValue = e, this._hasTrailingValue = !0)) + }, co.prototype._complete = function() { + this._hasTrailingValue && this.destination.next(this._trailingValue), this.destination.complete() + }, co.prototype.clearThrottle = function() { + var e = this.throttled; + e && (this.trailing && this._hasTrailingValue && (this.destination.next(this._trailingValue), this._trailingValue = null, this._hasTrailingValue = !1), e.unsubscribe(), this.remove(e), this.throttled = null) + }, co); + + function co(e, t, i, r, n) { + e = oo.call(this, e) || this; + return e.duration = t, e.scheduler = i, e.leading = r, e.trailing = n, e._hasTrailingValue = !1, e._trailingValue = null, e + } + + function ho(e, t, i, r) { + this.duration = e, this.scheduler = t, this.leading = i, this.trailing = r + } + + function po(e) { + e.subscriber.clearThrottle() + } + var fo, mo = (vo.prototype.call = function(e, t) { + return t.subscribe(new go(e, this.absoluteTimeout, this.waitFor, this.withObservable, this.scheduler)) + }, vo), + go = (ut(yo, fo = Vr), yo.dispatchTimeout = function(e) { + var t = e.withObservable; + e._unsubscribeAndRecycle(), e.add(Hr(t, new $r(e))) + }, yo.prototype.scheduleTimeout = function() { + var e = this.action; + e ? this.action = e.schedule(this, this.waitFor) : this.add(this.action = this.scheduler.schedule(yo.dispatchTimeout, this.waitFor, this)) + }, yo.prototype._next = function(e) { + this.absoluteTimeout || this.scheduleTimeout(), fo.prototype._next.call(this, e) + }, yo.prototype._unsubscribe = function() { + this.action = void 0, this.scheduler = null, this.withObservable = null + }, yo); + + function yo(e, t, i, r, n) { + e = fo.call(this, e) || this; + return e.absoluteTimeout = t, e.waitFor = i, e.withObservable = r, e.scheduler = n, e.scheduleTimeout(), e + } + + function vo(e, t, i, r) { + this.waitFor = e, this.absoluteTimeout = t, this.withObservable = i, this.scheduler = r + } + + function So(e, t) { + return void 0 === t && (t = ir), r = e, n = Vi(new dr), void 0 === (s = t) && (s = ir), + function(e) { + var t = as(r), + i = t ? +r - s.now() : Math.abs(r); + return e.lift(new mo(i, t, n, s)) + }; + var r, n, s + } + + function bo() { + for (var i = [], e = 0; e < arguments.length; e++) i[e] = arguments[e]; + return function(e) { + var t; + return "function" == typeof i[i.length - 1] && (t = i.pop()), e.lift(new Io(i, t)) + } + } + var To, Eo, Io = (Co.prototype.call = function(e, t) { + return t.subscribe(new wo(e, this.observables, this.project)) + }, Co), + wo = (ut(ko, To = vr), ko.prototype.notifyNext = function(e, t, i) { + this.values[i] = t; + t = this.toRespond; + 0 < t.length && (-1 !== (i = t.indexOf(i)) && t.splice(i, 1)) + }, ko.prototype.notifyComplete = function() {}, ko.prototype._next = function(e) { + 0 === this.toRespond.length && (e = [e].concat(this.values), this.project ? this._tryProject(e) : this.destination.next(e)) + }, ko.prototype._tryProject = function(e) { + var t; + try { + t = this.project.apply(this, e) + } catch (e) { + return void this.destination.error(e) + } + this.destination.next(t) + }, ko), + Ao = { + type: null, + entityIds: null, + skip: !1 + }, + Oo = !1; + + function ko(e, t, i) { + var r = To.call(this, e) || this; + r.observables = t, r.project = i, r.toRespond = []; + var n = t.length; + r.values = new Array(n); + for (var s = 0; s < n; s++) r.toRespond.push(s); + for (s = 0; s < n; s++) { + var a = t[s]; + r.add(Cr(r, a, void 0, s)) + } + return r + } + + function Co(e, t) { + this.observables = e, this.project = t + } + + function Do(e, t) { + Mo(e, t), Oo = !0 + } + + function Mo(e, t) { + !1 === Oo && (Ao.type = e, Ao.entityIds = t) + } + + function xo(e, t) { + return e.hasOwnProperty(t) + } + + function Po(e) { + return null == e + } + + function Ro(e) { + return Po(e) ? [] : Array.isArray(e) ? e : [e] + }(iu = Eo = Eo || {}).Set = "Set", iu.Add = "Add", iu.Update = "Update", iu.Remove = "Remove"; + var Lo = "undefined" != typeof window, + _o = !0; + + function No(e) { + var t = typeof e; + return null != e && ("object" == t || "function" == t) + } + + function Fo(e) { + return Array.isArray(e) + } + + function Bo(e) { + return !1 === Po(e) + } + + function Uo(e) { + return Fo(e) && 0 === e.length + } + + function $o(e) { + return "function" == typeof e + } + + function Vo(e) { + return void 0 === e + } + + function Ko(e) { + return e.hasOwnProperty("active") + } + + function qo(e) { + return Fo(e) + } + + function Ho(e) { + var t, i = e.active, + r = e.ids, + n = e.entities; + return qo(i) ? (t = r, (r = (e = i).filter(function(e) { + return -1 < t.indexOf(e) + })).length === e.length ? e : r) : !1 === xo(n, i) ? null : i + } + + function jo(e, t) { + var i, r, n = {}; + try { + for (var s = ft(Object.keys(e)), a = s.next(); !a.done; a = s.next()) { + var o = a.value; + n[o] = t(e[o]) + } + } catch (e) { + i = { + error: e + } + } finally { + try { + a && !a.done && (r = s.return) && r.call(s) + } finally { + if (i) throw i.error + } + } + return n + } + var Qo = { + resettable: !1, + ttl: null, + producerFn: void 0 + }; + + function Wo(t) { + Object.freeze(t); + var i = "function" == typeof t, + r = Object.prototype.hasOwnProperty; + return Object.getOwnPropertyNames(t).forEach(function(e) { + !r.call(t, e) || i && ("caller" === e || "callee" === e || "arguments" === e) || null === t[e] || "object" != typeof t[e] && "function" != typeof t[e] || Object.isFrozen(t[e]) || Wo(t[e]) + }), t + } + var Go, zo = new Xt, + Xo = new Ji(50, 5e3), + Yo = new Xt; + + function Jo(e) { + return null != e && "" + e != "false" + } + + function Zo(e) { + return Jo(e) && "Object" === e.constructor.name + } + ut(function(e) { + return Go.call(this, e) || this + }, Go = Error); + var el = {}, + tl = {}; + Lo && (window.$$stores = el, window.$$queries = tl); + var il = new Xt, + rl = new yi(!1), + nl = { + activeTransactions: 0, + batchTransaction: null + }; + + function sl() { + return 0 < nl.activeTransactions + } + + function al(e, t) { + void 0 === t && (t = void 0), sl() || (nl.batchTransaction = new Xt), nl.activeTransactions++, rl.next(!0); + try { + return e.apply(t) + } finally { + Do("@Transaction"), 0 == --nl.activeTransactions && (nl.batchTransaction.next(!0), nl.batchTransaction.complete(), rl.next(!1), il.next(!0)) + } + } + + function ol() { + return function(e, t, i) { + var r = i.value; + return i.value = function() { + for (var e = this, t = [], i = 0; i < arguments.length; i++) t[i] = arguments[i]; + return al(function() { + return r.apply(e, t) + }, this) + }, i + } + } + + function ll(t) { + return function(e) { + return e.pipe(Za(function(e) { + return al(function() { + return t(e) + }) + })) + } + } + var dl = (ul.prototype.setLoading = function(t) { + (t = void 0 === t ? !1 : t) !== this._value().loading && (_o && Mo("Set Loading"), this._setState(function(e) { + return ct({}, e, { + loading: t + }) + })) + }, ul.prototype.setHasCache = function(e, t) { + var i, r = this; + void 0 === t && (t = { + restartTTL: !1 + }), e !== this.cache.active.value && this.cache.active.next(e), t.restartTTL && (i = this.getCacheTTL()) && (null !== this.cache.ttl && clearTimeout(this.cache.ttl), this.cache.ttl = setTimeout(function() { + return r.setHasCache(!1) + }, i)) + }, ul.prototype.getValue = function() { + return this.storeValue + }, ul.prototype.setError = function(t) { + t !== this._value().error && (_o && Mo("Set Error"), this._setState(function(e) { + return ct({}, e, { + error: t + }) + })) + }, ul.prototype._select = function(t) { + return this.store.asObservable().pipe(hr(function(e) { + return t(e.state) + }), Is()) + }, ul.prototype._value = function() { + return this.storeValue + }, ul.prototype._cache = function() { + return this.cache.active + }, Object.defineProperty(ul.prototype, "config", { + get: function() { + return this.constructor.akitaConfig || {} + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(ul.prototype, "storeName", { + get: function() { + return this.config.storeName || this.options.storeName || this.options.name + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(ul.prototype, "deepFreeze", { + get: function() { + return this.config.deepFreezeFn || this.options.deepFreezeFn || Wo + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(ul.prototype, "cacheConfig", { + get: function() { + return this.config.cache || this.options.cache + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(ul.prototype, "_producerFn", { + get: function() { + return this.config.producerFn || this.options.producerFn || Qo.producerFn + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(ul.prototype, "resettable", { + get: function() { + return (Bo(this.config.resettable) ? this.config : this.options).resettable + }, + enumerable: !0, + configurable: !0 + }), ul.prototype._setState = function(e, t) { + var i, r = this; + if (void 0 === t && (t = !0), $o(e) ? (i = e(this._value()), this.storeValue = _o ? this.deepFreeze(i) : i) : this.storeValue = e, !this.store) return this.store = new yi({ + state: this.storeValue + }), void(_o && this.store.subscribe(function(e) { + var t = e.action; + t && (e = r.storeName, Yo.next({ + storeName: e, + action: t + })) + })); + sl() ? this.handleTransaction() : this.dispatch(this.storeValue, t) + }, ul.prototype.reset = function() { + var e = this; + this.isResettable() ? (_o && Mo("Reset"), this._setState(function() { + return Object.assign({}, e._initialState) + }), this.setHasCache(!1)) : _o && console.warn("You need to enable the reset functionality") + }, ul.prototype.update = function(e) { + _o && Mo("Update"); + var t = this._value(), + e = $o(e) ? $o(this._producerFn) ? this._producerFn(t, e) : e(t) : e, + e = this.akitaPreUpdate(t, ct({}, t, e)), + e = Zo(t) ? e : new t.constructor(e); + this._setState(e) + }, ul.prototype.updateStoreConfig = function(e) { + this.options = ct({}, this.options, e) + }, ul.prototype.akitaPreUpdate = function(e, t) { + return t + }, ul.prototype.ngOnDestroy = function() { + this.destroy() + }, ul.prototype.destroy = function() { + var e; + Lo && window.hmrEnabled || this !== el[this.storeName] || (delete el[this.storeName], e = this.storeName, zo.next(e), this.setHasCache(!1), this.cache.active.complete(), this.store.complete()) + }, ul.prototype.onInit = function(e) { + var t, i; + (el[this.storeName] = this)._setState(function() { + return e + }), i = this.storeName, Xo.next(i), this.isResettable() && (this._initialState = e), _o && (t = this.storeName, i = this.constructor.name, t || console.error("@StoreConfig({ name }) is missing in " + i)) + }, ul.prototype.dispatch = function(e, t) { + var i = void 0; + (t = void 0 === t ? !0 : t) && (i = Ao, Oo = !1), this.store.next({ + state: e, + action: i + }) + }, ul.prototype.watchTransaction = function() { + var e = this; + (nl.batchTransaction ? nl.batchTransaction.asObservable() : $i(!0)).subscribe(function() { + e.inTransaction = !1, e.dispatch(e._value()) + }) + }, ul.prototype.isResettable = function() { + return !1 !== this.resettable && (this.resettable || Qo.resettable) + }, ul.prototype.handleTransaction = function() { + this.inTransaction || (this.watchTransaction(), this.inTransaction = !0) + }, ul.prototype.getCacheTTL = function() { + return this.cacheConfig && this.cacheConfig.ttl || Qo.ttl + }, ul); + + function ul(e, t) { + this.options = t = void 0 === t ? {} : t, this.inTransaction = !1, this.cache = { + active: new yi(!1), + ttl: null + }, this.onInit(e) + } + var cl, hl, pl, fl = (ut(yl, pl = dl), Object.defineProperty(yl.prototype, "selectEntityAction$", { + get: function() { + return this.entityActions.asObservable() + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(yl.prototype, "selectEntityIdChanges$", { + get: function() { + return this.entityIdChanges.asObservable() + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(yl.prototype, "idKey", { + get: function() { + return this.config.idKey || this.options.idKey || "id" + }, + enumerable: !0, + configurable: !0 + }), yl.prototype.set = function(a, o) { + var l, d = this; + void 0 === o && (o = {}), Po(a) || (_o && Mo("Set Entity"), l = this.akitaPreAddEntity === yl.prototype.akitaPreAddEntity, this.setHasCache(!0, { + restartTTL: !0 + }), this._setState(function(e) { + var t, i, r, n, s, e = (t = { + state: e, + entities: a, + idKey: d.idKey, + preAddEntity: d.akitaPreAddEntity, + isNativePreAdd: l + }, r = t.state, n = t.entities, s = t.idKey, e = t.preAddEntity, t = t.isNativePreAdd, e = Fo(n) ? (i = (s = function(e, t, i) { + var r, n, s = { + entities: {}, + ids: [] + }; + try { + for (var a = ft(e), o = a.next(); !o.done; o = a.next()) { + var l = i(o.value); + s.entities[l[t]] = l, s.ids.push(l[t]) + } + } catch (e) { + r = { + error: e + } + } finally { + try { + o && !o.done && (n = a.return) && n.call(a) + } finally { + if (r) throw r.error + } + } + return s + }(n, s, e)).entities, s.ids) : n.entities && n.ids ? (i = t ? n.entities : jo(n.entities, e), n.ids) : (i = t ? n : jo(n, e), Object.keys(i).map(function(e) { + return isNaN(e) ? e : Number(e) + })), e = ct({}, r, { + entities: i, + ids: e, + loading: !1 + }), Ko(r) && (e.active = Ho(e)), e); + return !1 === Vo(o.activeId) && (e.active = o.activeId), e + }), this.hasInitialUIState() && this.handleUICreation(), this.entityActions.next({ + type: Eo.Set, + ids: this.ids + })) + }, yl.prototype.add = function(e, t) { + void 0 === t && (t = { + loading: !1 + }); + var i, e = Ro(e); + Uo(e) || (i = function(e) { + var t, i, r = e.state, + n = e.entities, + s = e.idKey, + a = e.options, + o = void 0 === a ? {} : a, + l = e.preAddEntity, + d = {}, + u = [], + c = !1; + try { + for (var h = ft(n), p = h.next(); !p.done; p = h.next()) { + var f, m, g = p.value; + !1 === xo(r.entities, g[s]) && (d[m = (f = l(g))[s]] = f, o.prepend ? u.unshift(m) : u.push(m), c = !0) + } + } catch (e) { + t = { + error: e + } + } finally { + try { + p && !p.done && (i = h.return) && i.call(h) + } finally { + if (t) throw t.error + } + } + return c ? { + newState: ct({}, r, { + entities: ct({}, r.entities, d), + ids: o.prepend ? gt(u, r.ids) : gt(r.ids, u) + }), + newIds: u + } : null + }({ + state: this._value(), + preAddEntity: this.akitaPreAddEntity, + entities: e, + idKey: this.idKey, + options: t + })) && (_o && Mo("Add Entity"), i.newState.loading = t.loading, this._setState(function() { + return i.newState + }), this.hasInitialUIState() && this.handleUICreation(!0), this.entityActions.next({ + type: Eo.Add, + ids: i.newIds + })) + }, yl.prototype.update = function(t, i) { + var r, n, s = this; + Vo(i) ? pl.prototype.update.call(this, t) : (n = [], Uo(n = $o(t) ? this.ids.filter(function(e) { + return t(s.entities[e]) + }) : Po(t) ? this.ids : Ro(t)) || (_o && Mo("Update Entity", n), this._setState(function(e) { + return function(e) { + var t = e.state, + i = e.ids, + r = e.idKey, + n = e.newStateOrFn, + s = e.preUpdateEntity, + a = e.producerFn, + o = e.onEntityIdChanges, + l = {}, + d = !1; + try { + for (var u = ft(i), c = u.next(); !c.done; c = u.next()) { + var h, p, f, m, g, y, v = c.value; + !1 !== xo(t.entities, v) && (h = t.entities[v], p = void 0, f = (p = $o(n) ? $o(a) ? a(h, n) : n(h) : n).hasOwnProperty(r) && p[r] !== h[r], y = void 0, m = v, f && (d = !0, m = p[r]), g = ct({}, h, p), y = Zo(h) ? g : new(Zo(p) ? h : p).constructor(g), l[m] = s(h, y)) + } + } catch (e) { + T = { + error: e + } + } finally { + try { + c && !c.done && (b = u.return) && b.call(u) + } finally { + if (T) throw T.error + } + } + var S, b = t.ids, + T = t.entities; + return d && (S = mt(i, 1)[0], T = function(e, t) { + var i = {}; + for (n in e) Object.prototype.hasOwnProperty.call(e, n) && t.indexOf(n) < 0 && (i[n] = e[n]); + if (null != e && "function" == typeof Object.getOwnPropertySymbols) + for (var r = 0, n = Object.getOwnPropertySymbols(e); r < n.length; r++) t.indexOf(n[r]) < 0 && Object.prototype.propertyIsEnumerable.call(e, n[r]) && (i[n[r]] = e[n[r]]); + return i + }(t.entities, ["symbol" == typeof S ? S : S + ""]), b = t.ids.map(function(e) { + return e === S ? m : e + }), o(S, m)), ct({}, t, { + entities: ct({}, T, l), + ids: b + }) + }({ + idKey: s.idKey, + ids: n, + preUpdateEntity: s.akitaPreUpdateEntity, + state: e, + newStateOrFn: i, + producerFn: s._producerFn, + onEntityIdChanges: function(e, t) { + r = { + oldId: e, + newId: t + }, s.entityIdChanges.next(ct({}, r, { + pending: !0 + })) + } + }) + }), r && this.entityIdChanges.next(ct({}, r, { + pending: !1 + })), this.entityActions.next({ + type: Eo.Update, + ids: n + }))) + }, yl.prototype.upsert = function(e, i, r, t) { + var n = this; + void 0 === t && (t = {}); + var s = Ro(e), + e = function(t) { + return function(e) { + return xo(n.entities, e) === t + } + }, + a = $o(r) ? t.baseClass : r ? r.baseClass : void 0, + o = $o(a), + t = s.filter(e(!0)), + e = s.filter(e(!1)).map(function(e) { + var t = "function" == typeof i ? i({}) : i, + t = $o(r) ? r(e, t) : t, + t = ct({}, t, ((t = {})[n.idKey] = e, t)); + return o ? new a(t) : t + }); + this.update(t, i), this.add(e), _o && Do("Upsert Entity") + }, yl.prototype.upsertMany = function(e, t) { + var i, r; + void 0 === t && (t = {}); + var n = [], + s = [], + a = {}; + try { + for (var o = ft(e), l = o.next(); !l.done; l = o.next()) { + var d, u, c, h, p, f, m = l.value, + g = this.akitaPreCheckEntity(m), + y = g[this.idKey]; + xo(this.entities, y) ? (d = this._value().entities[y], u = ct({}, this._value().entities[y], g), c = t.baseClass ? new t.baseClass(u) : u, f = (h = this.akitaPreUpdateEntity(d, c))[this.idKey], a[f] = h, s.push(f)) : (p = t.baseClass ? new t.baseClass(g) : g, f = (h = this.akitaPreAddEntity(p))[this.idKey], n.push(f), a[f] = h) + } + } catch (e) { + i = { + error: e + } + } finally { + try { + l && !l.done && (r = o.return) && r.call(o) + } finally { + if (i) throw i.error + } + } + _o && Do("Upsert Many"), this._setState(function(e) { + return ct({}, e, { + ids: n.length ? gt(e.ids, n) : e.ids, + entities: ct({}, e.entities, a), + loading: !!t.loading + }) + }), s.length && this.entityActions.next({ + type: Eo.Update, + ids: s + }), n.length && this.entityActions.next({ + type: Eo.Add, + ids: n + }), n.length && this.hasUIStore() && this.handleUICreation(!0) + }, yl.prototype.replace = function(e, t) { + var i, r, n = Ro(e); + if (!Uo(n)) { + var s = {}; + try { + for (var a = ft(n), o = a.next(); !o.done; o = a.next()) { + var l = o.value; + t[this.idKey] = l, s[l] = t + } + } catch (e) { + i = { + error: e + } + } finally { + try { + o && !o.done && (r = a.return) && r.call(a) + } finally { + if (i) throw i.error + } + } + _o && Mo("Replace Entity", e), this._setState(function(e) { + return ct({}, e, { + entities: ct({}, e.entities, s) + }) + }) + } + }, yl.prototype.move = function(e, t) { + var i = this.ids.slice(); + i.splice(t < 0 ? i.length + t : t, 0, i.splice(e, 1)[0]), _o && Mo("Move Entity"), this._setState(function(e) { + return ct({}, e, { + entities: ct({}, e.entities), + ids: i + }) + }) + }, yl.prototype.remove = function(t) { + var e, i, r = this; + Uo(this.ids) || (e = Bo(t), i = [], Uo(i = $o(t) ? this.ids.filter(function(e) { + return t(r.entities[e]) + }) : e ? Ro(t) : this.ids) || (_o && Mo("Remove Entity", i), this._setState(function(e) { + return function(e) { + var t, i = e.state, + r = e.ids; + if (Po(r)) return ct({}, i, { + entities: {}, + ids: [], + active: qo(i.active) ? [] : null + }); + var n = i.entities, + s = {}; + try { + for (var a = ft(i.ids), o = a.next(); !o.done; o = a.next()) { + var l = o.value; + !1 === r.includes(l) && (s[l] = n[l]) + } + } catch (e) { + d = { + error: e + } + } finally { + try { + o && !o.done && (t = a.return) && t.call(a) + } finally { + if (d) throw d.error + } + } + var d = ct({}, i, { + entities: s, + ids: i.ids.filter(function(e) { + return !1 === r.includes(e) + }) + }); + return Ko(i) && (d.active = Ho(d)), d + }({ + state: e, + ids: i + }) + }), e || this.setHasCache(!1), this.handleUIRemove(i), this.entityActions.next({ + type: Eo.Remove, + ids: i + }))) + }, yl.prototype.updateActive = function(e) { + var t = Ro(this.active); + _o && Mo("Update Active", t), this.update(t, e) + }, yl.prototype.setActive = function(e) { + e = function(e, t, i) { + var r; + if (Fo(e)) r = e; + else if (No(e)) { + if (Po(i)) return; + e = Object.assign({ + wrap: !0 + }, e); + var n = t.indexOf(i); + if (e.prev) { + var s = 0 === n; + if (s && !e.wrap) return; + r = s ? t[t.length - 1] : t[n - 1] + } else if (e.next) { + s = t.length === n + 1; + if (s && !e.wrap) return; + r = s ? t[0] : t[n + 1] + } + } else { + if (e === i) return; + r = e + } + return r + }(e, this.ids, this.active); + void 0 !== e && (_o && Mo("Set Active", e), this._setActive(e)) + }, yl.prototype.addActive = function(e) { + var t = this, + i = Ro(e); + Uo(i) || i.every(function(e) { + return -1 < t.active.indexOf(e) + }) || (_o && Mo("Add Active", e), this._setState(function(e) { + var t = Array.from(new Set(gt(e.active, i))); + return ct({}, e, { + active: t + }) + })) + }, yl.prototype.removeActive = function(e) { + var t = this, + i = Ro(e); + Uo(i) || i.some(function(e) { + return -1 < t.active.indexOf(e) + }) && (_o && Mo("Remove Active", e), this._setState(function(e) { + return ct({}, e, { + active: Array.isArray(e.active) ? e.active.filter(function(e) { + return -1 === i.indexOf(e) + }) : null + }) + })) + }, yl.prototype.toggleActive = function(e) { + var i = this, + t = Ro(e), + r = function(t) { + return function(e) { + return i.active.includes(e) === t + } + }, + e = t.filter(r(!0)), + r = t.filter(r(!1)); + this.removeActive(e), this.addActive(r), _o && Do("Toggle Active") + }, yl.prototype.createUIStore = function(e, t) { + var i = { + name: "UI/" + this.storeName, + idKey: this.idKey + }; + return this.ui = new ml(e = void 0 === e ? {} : e, ct({}, i, t = void 0 === t ? {} : t)), this.ui + }, yl.prototype.destroy = function() { + pl.prototype.destroy.call(this), this.ui instanceof yl && this.ui.destroy(), this.entityActions.complete() + }, yl.prototype.akitaPreUpdateEntity = function(e, t) { + return t + }, yl.prototype.akitaPreAddEntity = function(e) { + return e + }, yl.prototype.akitaPreCheckEntity = function(e) { + return e + }, Object.defineProperty(yl.prototype, "ids", { + get: function() { + return this._value().ids + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(yl.prototype, "entities", { + get: function() { + return this._value().entities + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(yl.prototype, "active", { + get: function() { + return this._value().active + }, + enumerable: !0, + configurable: !0 + }), yl.prototype._setActive = function(t) { + this._setState(function(e) { + return ct({}, e, { + active: t + }) + }) + }, yl.prototype.handleUICreation = function(e) { + var r = this, + t = this.ids, + n = $o(this.ui._akitaCreateEntityFn), + i = function(e) { + var t = r.entities[e], + i = n ? r.ui._akitaCreateEntityFn(t) : r.ui._akitaCreateEntityFn; + return ct(((e = {})[r.idKey] = t[r.idKey], e), i) + }, + i = ((e = void 0 === e ? !1 : e) ? this.ids.filter(function(e) { + return Vo(r.ui.entities[e]) + }) : t).map(i); + e ? this.ui.add(i) : this.ui.set(i) + }, yl.prototype.hasInitialUIState = function() { + return this.hasUIStore() && !1 === Vo(this.ui._akitaCreateEntityFn) + }, yl.prototype.handleUIRemove = function(e) { + this.hasUIStore() && this.ui.remove(e) + }, yl.prototype.hasUIStore = function() { + return this.ui instanceof ml + }, ht([ol(), pt("design:type", Function), pt("design:paramtypes", [Object, Object, Object, Object]), pt("design:returntype", void 0)], yl.prototype, "upsert", null), ht([ol(), pt("design:type", Function), pt("design:paramtypes", ["function" == typeof(w = "undefined" != typeof T && T) ? w : Object]), pt("design:returntype", void 0)], yl.prototype, "toggleActive", null), yl), + ml = (ut(gl, hl = fl), gl.prototype.setInitialEntityState = function(e) { + this._akitaCreateEntityFn = e + }, gl); + + function gl(e, t) { + return hl.call(this, e = void 0 === e ? {} : e, t = void 0 === t ? {} : t) || this + } + + function yl(e, t) { + void 0 === t && (t = {}); + e = pl.call(this, ct({}, { + entities: {}, + ids: [], + loading: !0, + error: null + }, e = void 0 === e ? {} : e), t) || this; + return e.options = t, e.entityActions = new Xt, e.entityIdChanges = new Xt, e + } + + function vl() { + return Is(function(e, t) { + return e === t || !1 !== Fo(e) && !1 !== Fo(t) && (!(!Uo(e) || !Uo(t)) || !Sl(t, e) && !1 === Sl(e, t)) + }) + } + + function Sl(e, t) { + return t.some(function(t) { + return void 0 === e.find(function(e) { + return e === t + }) + }) + } + + function bl(i, e) { + for (var r, n, s, a = [], o = i.ids, l = i.entities, d = e.filterBy, t = e.limitTo, u = e.sortBy, e = e.sortByOrder, c = 0; c < o.length; c++) ! function(t) { + var i = l[o[t]]; + if (!d) return a.push(i); + Ro(d).every(function(e) { + return e(i, t) + }) && a.push(i) + }(c); + u && (s = $o(u) ? u : (r = u, void 0 === (n = e) && (n = cl.ASC), function(e, t) { + if (!e.hasOwnProperty(r) || !t.hasOwnProperty(r)) return 0; + var i = "string" == typeof e[r] ? e[r].toUpperCase() : e[r], + e = "string" == typeof t[r] ? t[r].toUpperCase() : t[r], + t = 0; + return e < i ? t = 1 : i < e && (t = -1), n == cl.DESC ? -1 * t : t + }), a = a.sort(function(e, t) { + return s(e, t, i) + })); + t = Math.min(t || a.length, a.length); + return t === a.length ? a : a.slice(0, t) + } + + function Tl(e) { + return "string" == typeof e + } + + function El(t, i) { + return function(e) { + e = e[t]; + if (!Vo(e)) return i ? Tl(i) ? e[i] : i(e) : e + } + }(vi = cl = cl || {}).ASC = "asc", vi.DESC = "desc"; + Il.prototype.select = function(t) { + var e, n; + if ($o(t)) e = t; + else if (Tl(t)) e = function(e) { + return e[t] + }; + else { + if (Array.isArray(t)) return this.store._select(function(e) { + return e + }).pipe(Is((n = t, function(t, i) { + var r = $o(n[0]); + return !1 === n.some(function(e) { + return r ? e(t) !== e(i) : t[e] !== i[e] + }) + })), hr(function(i) { + return $o(t[0]) ? t.map(function(e) { + return e(i) + }) : t.reduce(function(e, t) { + return e[t] = i[t], e + }, {}) + })); + e = function(e) { + return e + } + } + return this.store._select(e) + }, Il.prototype.selectLoading = function() { + return this.select(function(e) { + return e.loading + }) + }, Il.prototype.selectError = function() { + return this.select(function(e) { + return e.error + }) + }, Il.prototype.getValue = function() { + return this.store._value() + }, Il.prototype.selectHasCache = function() { + return this.store._cache().asObservable() + }, Il.prototype.getHasCache = function() { + return this.store._cache().value + }, Object.defineProperty(Il.prototype, "config", { + get: function() { + return this.constructor.akitaQueryConfig + }, + enumerable: !0, + configurable: !0 + }), bi = Il; + + function Il(e) { + this.store = e, this.__store__ = e, _o && (tl[e.storeName] = this) + } + + function wl(e) { + return e.pipe(ln(function(e) { + return null != e + })) + } + var Al, Ol, kl = (ut(Ml, Ol = bi), Ml.prototype.selectAll = function(e) { + var t = this; + return void 0 === e && (e = { + asObject: !1 + }), this.select(function(e) { + return e.entities + }).pipe(hr(function() { + return t.getAll(e) + })) + }, Ml.prototype.getAll = function(e) { + return (e = void 0 === e ? { + asObject: !1, + filterBy: void 0, + limitTo: void 0 + } : e).asObject ? function(e, t) { + var r = {}, + n = t.filterBy, + s = t.limitTo, + a = e.ids, + o = e.entities; + if (!n && !s) return o; + e = !1 === Po(s); + if (n && e) + for (var l = 0, i = 0, d = a.length; i < d && "break" !== function(t) { + if (l === s) return "break"; + var e = a[t], + i = o[e]; + Ro(n).every(function(e) { + return e(i, t) + }) && (r[e] = i, l++) + }(i); i++); + else + for (var u = Math.min(s || a.length, a.length), i = 0; i < u; i++) ! function(t) { + var e = a[t], + i = o[e]; + if (!n) return r[e] = i; + Ro(n).every(function(e) { + return e(i, t) + }) && (r[e] = i) + }(i); + return r + }(this.getValue(), e) : (t = e, i = this.config || this.options, t.sortBy = t.sortBy || i && i.sortBy, t.sortByOrder = t.sortByOrder || i && i.sortByOrder, bl(this.getValue(), e)); + var t, i + }, Ml.prototype.selectMany = function(e, i) { + return e && e.length ? this.select(function(e) { + return e.entities + }).pipe(hr(function(t) { + return n = function(e) { + return El(e, i)(t) + }, e.reduce(function(e, t, i, r) { + t = n(t); + return void 0 !== t && e.push(t), e + }, []); + var n + }), vl()) : $i([]) + }, Ml.prototype.selectEntity = function(e, t) { + var i = e; + return $o(e) && (i = function(e, t) { + var i, r; + try { + for (var n = ft(Object.keys(t)), s = n.next(); !s.done; s = n.next()) { + var a = s.value; + if (!0 === e(t[a])) return a + } + } catch (e) { + i = { + error: e + } + } finally { + try { + s && !s.done && (r = n.return) && r.call(n) + } finally { + if (i) throw i.error + } + } + }(e, this.getValue().entities)), this.select(function(e) { + return e.entities + }).pipe(hr(El(i, t)), Is()) + }, Ml.prototype.getEntity = function(e) { + return this.getValue().entities[e] + }, Ml.prototype.selectActiveId = function() { + return this.select(function(e) { + return e.active + }) + }, Ml.prototype.getActiveId = function() { + return this.getValue().active + }, Ml.prototype.selectActive = function(t) { + var i = this; + return Fo(this.getActive()) ? this.selectActiveId().pipe(La(function(e) { + return i.selectMany(e, t) + })) : this.selectActiveId().pipe(La(function(e) { + return i.selectEntity(e, t) + })) + }, Ml.prototype.getActive = function() { + var t = this, + e = this.getActiveId(); + return Fo(e) ? e.map(function(e) { + return t.getValue().entities[e] + }) : Jo(e) ? this.getEntity(e) : void 0 + }, Ml.prototype.selectCount = function(e) { + var t = this; + return this.select(function(e) { + return e.entities + }).pipe(hr(function() { + return t.getCount(e) + })) + }, Ml.prototype.getCount = function(e) { + return ($o(e) ? this.getAll().filter(e) : this.getValue().ids).length + }, Ml.prototype.selectLast = function(e) { + return this.selectAt(function(e) { + return e[e.length - 1] + }, e) + }, Ml.prototype.selectFirst = function(e) { + return this.selectAt(function(e) { + return e[0] + }, e) + }, Ml.prototype.selectEntityAction = function(e) { + if (Po(e)) return this.store.selectEntityAction$; + var t = Fo(e) ? function(e) { + return e + } : function(e) { + return e.ids + }, + i = Ro(e); + return this.store.selectEntityAction$.pipe(ln(function(e) { + e = e.type; + return i.includes(e) + }), hr(function(e) { + return t(e) + })) + }, Ml.prototype.hasEntity = function(e) { + var t = this; + return Po(e) ? 0 < this.getValue().ids.length : $o(e) ? this.getAll().some(e) : Fo(e) ? e.every(function(e) { + return e in t.getValue().entities + }) : e in this.getValue().entities + }, Ml.prototype.hasActive = function(e) { + var t = this.getValue().active, + i = Bo(e); + return Array.isArray(t) ? i ? t.includes(e) : 0 < t.length : i ? t === e : Bo(t) + }, Ml.prototype.createUIQuery = function() { + this.ui = new Cl(this.__store__.ui) + }, Ml.prototype.selectAt = function(e, t) { + var i = this; + return this.select(function(e) { + return e.ids + }).pipe(hr(e), Is(), La(function(e) { + return i.selectEntity(e, t) + })) + }, Ml), + Cl = (ut(Dl, Al = kl), Dl); + + function Dl(e) { + return Al.call(this, e) || this + } + + function Ml(e, t) { + void 0 === t && (t = {}); + var i = Ol.call(this, e) || this; + return i.options = t, i.__store__ = e, i + } + + function xl(e, t) { + return 1 === t.split(".").length ? e : t.split(".").slice(1).join(".").split(".").reduce(function(e, t) { + return e && e[t] + }, e) + } + + function Pl(e, t, r) { + var i = t.split("."); + if (1 === i.length) return ct({}, e, r); + e = ct({}, e); + var n = i.length - 2; + return t.split(".").slice(1).reduce(function(e, t, i) { + return e[t] = i !== n ? ct({}, e[t]) : Array.isArray(e[t]) || !No(e[t]) ? r : ct({}, e[t], r), e && e[t] + }, e), e + } + new Ji(1); + var Rl, Ll, _l, Nl, A = (Bl.prototype.getQuery = function() { + return this.query + }, Bl.prototype.getStore = function() { + return this.getQuery().__store__ + }, Bl.prototype.isEntityBased = Jo, Bl.prototype.selectSource = function(e, t) { + var i = this; + return this.isEntityBased(e) ? this.getQuery().selectEntity(e).pipe(wl) : t ? this.getQuery().select(function(e) { + return xl(e, i.withStoreName(t)) + }) : this.getQuery().select() + }, Bl.prototype.getSource = function(e, t) { + if (this.isEntityBased(e)) return this.getQuery().getEntity(e); + e = this.getQuery().getValue(); + return t ? xl(e, this.withStoreName(t)) : e + }, Bl.prototype.withStoreName = function(e) { + return this.storeName + "." + e + }, Object.defineProperty(Bl.prototype, "storeName", { + get: function() { + return this.getStore().storeName + }, + enumerable: !0, + configurable: !0 + }), Bl.prototype.updateStore = function(t, e, i) { + var r = this; + this.isEntityBased(e) ? this.getStore().update(e, t) : i ? this.getStore()._setState(function(e) { + return Pl(e, r.withStoreName(i), t) + }) : this.getStore()._setState(function(e) { + return ct({}, e, t) + }) + }, Bl.prototype.onReset = function(i) { + var r = this, + n = this.getStore().reset; + this.getStore().reset = function() { + for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; + setTimeout(function() { + n.apply(r.getStore(), e), i() + }) + } + }, Bl), + Fl = { + pagesControls: !1, + range: !1, + startWith: 1, + cacheTimeout: void 0, + clearStoreWithCache: !0 + }; + + function Bl(e, t) { + this.query = e + } + + function Ul(e, t, i) { + void 0 === i && (i = {}); + var r = Rl.call(this, e) || this; + return r.query = e, r.factoryFnOrPath = t, r.params = i, r.params = ct({ + debounceTime: 300, + formKey: "akitaForm", + emitEvent: !1, + arrControlFactory: function(e) { + return r.builder.control(e) + } + }, i), r.isRootKeys = !1 === Jo(t), r.isKeyBased = Tl(t) || r.isRootKeys, r + } + + function $l(e, t) { + void 0 === t && (t = {}); + var i = Ll.call(this, e, { + resetFn: function() { + i.initial = !1, i.destroy({ + clearCache: !0, + currentPage: 1 + }) + } + }) || this; + i.query = e, i.config = t, i.metadata = new Map, i.pages = new Map, i.pagination = { + currentPage: 1, + perPage: 0, + total: 0, + lastPage: 0, + data: [] + }, i.initial = !0, i.isLoading$ = i.query.selectLoading().pipe(function(t) { + void 0 === t && (t = ir); + var i = as(0) ? 0 - t.now() : Math.abs(0); + return function(e) { + return e.lift(new ls(i, t)) + } + }()), i.config = Object.assign(Fl, t); + e = i.config, t = e.startWith, e = e.cacheTimeout; + return i.page = new yi(t), e && (e instanceof $t || "function" == typeof e.lift && "function" == typeof e.subscribe) && (i.clearCacheSubscription = e.subscribe(function() { + return i.clearCache() + })), i + } + ut($l, Ll = A), Object.defineProperty($l.prototype, "pageChanges", { + get: function() { + return this.page.asObservable() + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty($l.prototype, "currentPage", { + get: function() { + return this.pagination.currentPage + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty($l.prototype, "isFirst", { + get: function() { + return 1 === this.currentPage + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty($l.prototype, "isLast", { + get: function() { + return this.currentPage === this.pagination.lastPage + }, + enumerable: !0, + configurable: !0 + }), $l.prototype.withControls = function() { + return this.config.pagesControls = !0, this + }, $l.prototype.withRange = function() { + return this.config.range = !0, this + }, $l.prototype.setLoading = function(e) { + void 0 === e && (e = !0), this.getStore().setLoading(e) + }, $l.prototype.update = function(e) { + this.pagination = e, this.addPage(e.data) + }, $l.prototype.addPage = function(e) { + var t = this; + this.pages.set(this.currentPage, { + ids: e.map(function(e) { + return e[t.getStore().idKey] + }) + }), this.getStore().upsertMany(e) + }, $l.prototype.clearCache = function(e) { + void 0 === e && (e = {}), this.initial || (Do("@Pagination - Clear Cache"), !1 !== e.clearStore && (this.config.clearStoreWithCache || e.clearStore) && this.getStore().remove(), this.pages = new Map, this.metadata = new Map), this.initial = !1 + }, $l.prototype.clearPage = function(e) { + this.pages.delete(e) + }, $l.prototype.destroy = function(e) { + var t = void 0 === e ? {} : e, + e = t.clearCache, + t = t.currentPage; + this.clearCacheSubscription && this.clearCacheSubscription.unsubscribe(), e && this.clearCache(), Vo(t) || this.setPage(t), this.initial = !0 + }, $l.prototype.isPageActive = function(e) { + return this.currentPage === e + }, $l.prototype.setPage = function(e) { + e === this.currentPage && this.hasPage(e) || this.page.next(this.pagination.currentPage = e) + }, $l.prototype.nextPage = function() { + this.currentPage !== this.pagination.lastPage && this.setPage(this.pagination.currentPage + 1) + }, $l.prototype.prevPage = function() { + 1 < this.pagination.currentPage && this.setPage(this.pagination.currentPage - 1) + }, $l.prototype.setLastPage = function() { + this.setPage(this.pagination.lastPage) + }, $l.prototype.setFirstPage = function() { + this.setPage(1) + }, $l.prototype.hasPage = function(e) { + return this.pages.has(e) + }, $l.prototype.getPage = function(e) { + var t = this, + i = this.pagination.currentPage; + return this.hasPage(i) ? this.selectPage(i) : (this.setLoading(!0), Fr(e()).pipe(La(function(e) { + return i = e.currentPage, al(function() { + t.setLoading(!1), t.update(e) + }), t.selectPage(i) + }))) + }, $l.prototype.getQuery = function() { + return this.query + }, $l.prototype.refreshCurrentPage = function() { + !1 === Po(this.currentPage) && (this.clearPage(this.currentPage), this.setPage(this.currentPage)) + }, $l.prototype.getFrom = function() { + return this.isFirst ? 1 : (this.currentPage - 1) * this.pagination.perPage + 1 + }, $l.prototype.getTo = function() { + return this.isLast ? this.pagination.total : this.currentPage * this.pagination.perPage + }, $l.prototype.selectPage = function(n) { + var s = this; + return this.query.selectAll({ + asObject: !0 + }).pipe(Ds(1), hr(function(t) { + var e = ct({}, s.pagination, { + data: s.pages.get(n).ids.map(function(e) { + return t[e] + }) + }), + i = s.config, + r = i.range, + i = i.pagesControls; + return isNaN(s.pagination.total) && (1 === e.lastPage ? e.total = e.data ? e.data.length : 0 : e.total = e.perPage * e.lastPage, s.pagination.total = e.total), r && (e.from = s.getFrom(), e.to = s.getTo()), i && (e.pageControls = function(e, t) { + for (var i = Math.ceil(e / t), r = [], n = 0; n < i; n++) r.push(n + 1); + return r + }(s.pagination.total, s.pagination.perPage)), e + })) + }, ht([(_l = "@Pagination - New Page", function(e, t, i) { + var r = i.value; + return i.value = function() { + for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; + return Do(_l, Nl), r.apply(this, e) + }, i + }), pt("design:type", Function), pt("design:paramtypes", [Object]), pt("design:returntype", void 0)], $l.prototype, "update", null), ut(Ul, Rl = A), Ul.prototype.setForm = function(e, t) { + return this.form = e, this.builder = t, this.activate(), this + }, Ul.prototype.reset = function(e) { + var r = this, + e = e || (this.isKeyBased ? this.initialValue : this.factoryFnOrPath()); + this.isKeyBased && Object.keys(this.initialValue).forEach(function(i) { + var e, t = r.initialValue[i]; + Array.isArray(t) && r.builder && (e = r.form.controls[i], r.cleanArray(e), t.forEach(function(e, t) { + r.form.get(i).insert(t, r.params.arrControlFactory(e)) + })) + }), this.form.patchValue(e, { + emitEvent: this.params.emitEvent + }); + var t = this.isKeyBased ? Pl(this.getQuery().getValue(), this.getStore().storeName + "." + this.factoryFnOrPath, e) : ((t = {})[this.params.formKey] = e, t); + this.updateStore(t) + }, Ul.prototype.cleanArray = function(e) { + for (; 0 !== e.length;) e.removeAt(0) + }, Ul.prototype.resolveInitialValue = function(e, n) { + var s = this; + if (e) return Object.keys(e).reduce(function(e, i) { + var r, t = n[i]; + return Array.isArray(t) && s.builder && (r = s.params.arrControlFactory, s.cleanArray(s.form.get(i)), t.forEach(function(e, t) { + s.form.get(i).insert(t, r(e)) + })), e[i] = n[i], e + }, {}) + }, Ul.prototype.activate = function() { + var i, e, t, r = this; + this.isKeyBased ? (this.isRootKeys ? this.initialValue = this.resolveInitialValue(this.form.value, this.getQuery().getValue()) : (i = this.getStore().storeName + "." + this.factoryFnOrPath, e = xl(this.getQuery().getValue(), i), this.initialValue = this.resolveInitialValue(e, e)), this.form.patchValue(this.initialValue, { + emitEvent: this.params.emitEvent + })) : (this.getQuery().getValue()[this.params.formKey] || (Do("@PersistNgFormPlugin activate"), this.updateStore(((t = {})[this.params.formKey] = this.factoryFnOrPath(), t))), t = this.getQuery().getValue()[this.params.formKey], this.form.patchValue(t)), this.formChanges = this.form.valueChanges.pipe(Gn(this.params.debounceTime)).subscribe(function(t) { + var e; + Do("@PersistForm - Update"), e = r.isKeyBased ? r.isRootKeys ? function(e) { + return ct({}, e, t) + } : function(e) { + return Pl(e, i, t) + } : function() { + var e; + return (e = {})[r.params.formKey] = t, e + }, r.updateStore(e(r.getQuery().getValue())) + }) + }, Ul.prototype.destroy = function() { + this.formChanges && this.formChanges.unsubscribe(), this.form = null, this.builder = null + }; + var Vl, Kl, Vr = (jl.prototype.getEntity = function(e) { + return this.entities.get(e) + }, jl.prototype.hasEntity = function(e) { + return this.entities.has(e) + }, jl.prototype.removeEntity = function(e) { + return this.destroy(e), this.entities.delete(e) + }, jl.prototype.createEntity = function(e, t) { + return this.entities.set(e, t) + }, jl.prototype.getIds = function() { + return Vo(this.entityIds) ? this.query.getValue().ids : Ro(this.entityIds) + }, jl.prototype.resolvedIds = function(e) { + return Vo(e) ? this.getIds() : Ro(e) + }, jl.prototype.rebase = function(i, r) { + var n = this; + if (void 0 === r && (r = {}), Jo(i)) + if (Vo(this.entityIds)) { + for (var e = 0, t = i.length; e < t; e++) { + var s, a = i[e]; + !1 === this.hasEntity(a) && ($o(r.beforeAdd) && r.beforeAdd(a), s = this.instantiatePlugin(a), this.entities.set(a, s), $o(r.afterAdd) && r.afterAdd(s)) + } + this.entities.forEach(function(e, t) { + -1 === i.indexOf(t) && ($o(r.beforeRemove) && r.beforeRemove(e), n.removeEntity(t)) + }) + } else + for (var o = Ro(this.entityIds), e = 0, t = o.length; e < t; e++) a = o[e], -1 < i.indexOf(a) && !1 === this.hasEntity(a) ? ($o(r.beforeAdd) && r.beforeAdd(a), s = this.instantiatePlugin(a), this.entities.set(a, s), $o(r.afterAdd) && r.afterAdd(s)) : this.entities.forEach(function(e, t) { + -1 === i.indexOf(t) && !0 === n.hasEntity(t) && ($o(r.beforeRemove) && r.beforeRemove(e), n.removeEntity(t)) + }); + else this.getIds().forEach(function(e) { + n.hasEntity(e) || n.createEntity(e, n.instantiatePlugin(e)) + }) + }, jl.prototype.selectIds = function() { + return this.query.select(function(e) { + return e.ids + }) + }, jl.prototype.activate = function(e) { + this.rebase(e) + }, jl.prototype.forEachId = function(e, t) { + for (var i = this.resolvedIds(e), r = 0, n = i.length; r < n; r++) { + var s = i[r]; + this.hasEntity(s) && t(this.getEntity(s)) + } + }, jl), + ql = (ut(Hl, Vl = A), Object.defineProperty(Hl.prototype, "hasPast$", { + get: function() { + return this._hasPast$ + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(Hl.prototype, "hasFuture$", { + get: function() { + return this._hasFuture$ + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(Hl.prototype, "hasPast", { + get: function() { + return 0 < this.history.past.length + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(Hl.prototype, "hasFuture", { + get: function() { + return 0 < this.history.future.length + }, + enumerable: !0, + configurable: !0 + }), Object.defineProperty(Hl.prototype, "property", { + get: function() { + return this.params.watchProperty + }, + enumerable: !0, + configurable: !0 + }), Hl.prototype.updateHasHistory = function() { + this.hasFutureSubject.next(this.hasFuture), this.hasPastSubject.next(this.hasPast) + }, Hl.prototype.activate = function() { + var r = this; + this.hasPastSubject = new yi(!1), this._hasPast$ = this.hasPastSubject.asObservable().pipe(Is()), this.hasFutureSubject = new yi(!1), this._hasFuture$ = this.hasFutureSubject.asObservable().pipe(Is()), this.history.present = this.getSource(this._entityId, this.property), this.subscription = this.selectSource(this._entityId, this.property).pipe(ha()).subscribe(function(e) { + var t = mt(e, 2), + i = t[0], + e = t[1]; + r.skip ? r.skip = !1 : (t = r.params.comparator(i, e), !r.skipUpdate && t && (r.history.past.length === r.params.maxAge && (r.history.past = r.history.past.slice(1)), r.history.past = gt(r.history.past, [i]), r.history.present = e, r.updateHasHistory())) + }) + }, Hl.prototype.undo = function() { + var e, t, i; + 0 < this.history.past.length && (e = (i = this.history).past, t = i.present, i = e[e.length - 1], this.history.past = e.slice(0, e.length - 1), this.history.present = i, this.history.future = gt([t], this.history.future), this.update()) + }, Hl.prototype.redo = function() { + var e, t, i, r; + 0 < this.history.future.length && (e = (r = this.history).past, t = r.present, i = this.history.future[0], r = this.history.future.slice(1), this.history.past = gt(e, [t]), this.history.present = i, this.history.future = r, this.update("Redo")) + }, Hl.prototype.jumpToPast = function(e) { + var t, i, r, n; + e < 0 || e >= this.history.past.length || (t = (r = this.history).past, n = r.future, i = r.present, r = t.slice(0, e), n = gt(t.slice(e + 1), [i], n), e = t[e], this.history.past = r, this.history.present = e, this.history.future = n, this.update()) + }, Hl.prototype.jumpToFuture = function(e) { + var t, i, r; + e < 0 || e >= this.history.future.length || (i = (r = this.history).past, t = r.future, i = gt(i, [r.present], t.slice(0, e)), r = t[e], e = t.slice(e + 1), this.history.past = i, this.history.present = r, this.history.future = e, this.update("Redo")) + }, Hl.prototype.jump = function(e) { + return 0 < e ? this.jumpToFuture(e - 1) : e < 0 ? this.jumpToPast(this.history.past.length + e) : void 0 + }, Hl.prototype.clear = function(e) { + this.history = $o(e) ? e(this.history) : { + past: [], + present: null, + future: [] + }, this.updateHasHistory() + }, Hl.prototype.destroy = function(e) { + (e = void 0 === e ? !1 : e) && this.clear(), this.subscription.unsubscribe() + }, Hl.prototype.ignoreNext = function() { + this.skip = !0 + }, Hl.prototype.update = function(e) { + void 0 === e && (e = "Undo"), this.skipUpdate = !0, Do("@StateHistory - " + e), this.updateStore(this.history.present, this._entityId, this.property), this.updateHasHistory(), this.skipUpdate = !1 + }, Hl); + + function Hl(e, t, i) { + void 0 === t && (t = {}); + var r = Vl.call(this, e, { + resetFn: function() { + return r.clear() + } + }) || this; + return r.query = e, r.params = t, r._entityId = i, r.skip = !1, r.history = { + past: [], + present: null, + future: [] + }, r.skipUpdate = !1, t.maxAge = t.maxAge || 10, t.comparator = t.comparator || function() { + return !0 + }, r.activate(), r + } + + function jl(e, t) { + this.query = e, this.entityIds = t, this.entities = new Map + } + + function Ql(e, t) { + var i = Kl.call(this, e, (t = void 0 === t ? {} : t).entityIds) || this; + return i.query = e, (i.params = t).maxAge = Jo(t.maxAge) ? t.maxAge : 10, i.activate(), i.selectIds().pipe(ka(1)).subscribe(function(e) { + return i.activate(e) + }), i + } + ut(Ql, Kl = Vr), Ql.prototype.redo = function(e) { + this.forEachId(e, function(e) { + return e.redo() + }) + }, Ql.prototype.undo = function(e) { + this.forEachId(e, function(e) { + return e.undo() + }) + }, Ql.prototype.hasPast = function(e) { + if (this.hasEntity(e)) return this.getEntity(e).hasPast + }, Ql.prototype.hasFuture = function(e) { + if (this.hasEntity(e)) return this.getEntity(e).hasFuture + }, Ql.prototype.jumpToFuture = function(e, t) { + this.forEachId(e, function(e) { + return e.jumpToFuture(t) + }) + }, Ql.prototype.jumpToPast = function(e, t) { + this.forEachId(e, function(e) { + return e.jumpToPast(t) + }) + }, Ql.prototype.clear = function(e) { + this.forEachId(e, function(e) { + return e.clear() + }) + }, Ql.prototype.destroy = function(e, t) { + void 0 === t && (t = !1), this.forEachId(e, function(e) { + return e.destroy(t) + }) + }, Ql.prototype.ignoreNext = function(e) { + this.forEachId(e, function(e) { + return e.ignoreNext() + }) + }, Ql.prototype.instantiatePlugin = function(e) { + return new ql(this.query, this.params, e) + }; + var Wl = { + comparator: function(e, t) { + return JSON.stringify(e) !== JSON.stringify(t) + } + }; + + function Gl(e, t) { + return t.split(".").reduce(function(e, t) { + return e && "undefined" !== e[t] ? e[t] : void 0 + }, e) + } + var zl, Xl, Yl = (ut(Jl, zl = A), Jl.prototype.reset = function(e) { + var t = this.head; + $o((e = void 0 === e ? {} : e).updateFn) && (t = this.isEntityBased(this._entityId) ? e.updateFn(this.head, this.getQuery().getEntity(this._entityId)) : e.updateFn(this.head, this.getQuery().getValue())), Do("@DirtyCheck - Revert"), this.updateStore(t, this._entityId), this._reset.next() + }, Jl.prototype.setHead = function() { + return this.active ? this.head = this._getHead() : (this.activate(), this.active = !0), this.updateDirtiness(!1), this + }, Jl.prototype.isDirty = function() { + return !!this.dirty.value + }, Jl.prototype.hasHead = function() { + return !!this.getHead() + }, Jl.prototype.destroy = function() { + this.head = null, this.subscription && this.subscription.unsubscribe(), this._reset && this._reset.complete() + }, Jl.prototype.isPathDirty = function(e) { + var t = this.getHead(), + i = Gl(this.getQuery().getValue(), e), + e = Gl(t, e); + return this.params.comparator(i, e) + }, Jl.prototype.getHead = function() { + return this.head + }, Jl.prototype.activate = function() { + var i = this; + this.head = this._getHead(); + var e = this.params.watchProperty ? this.params.watchProperty.map(function(t) { + return i.query.select(function(e) { + return e[t] + }).pipe(hr(function(e) { + return { + val: e, + __akitaKey: t + } + })) + }) : [this.selectSource(this._entityId)]; + this.subscription = Mr.apply(void 0, gt(e)).pipe(ka(1)).subscribe(function(e) { + Vo(i.head) || (e = e.some(function(e) { + var t = e.__akitaKey ? i.head[e.__akitaKey] : i.head, + e = e.__akitaKey ? e.val : e; + return i.params.comparator(t, e) + }), i.updateDirtiness(e)) + }) + }, Jl.prototype.updateDirtiness = function(e) { + this.dirty.next(e) + }, Jl.prototype._getHead = function() { + var e = this.getSource(this._entityId); + return e = this.params.watchProperty ? this.getWatchedValues(e) : e + }, Jl.prototype.getWatchedValues = function(i) { + return this.params.watchProperty.reduce(function(e, t) { + return e[t] = i[t], e + }, {}) + }, Jl); + + function Jl(e, t, i) { + var r = zl.call(this, e) || this; + return r.query = e, r.params = t, r._entityId = i, r.dirty = new yi(!1), r.active = !1, r._reset = new Xt, r.isDirty$ = r.dirty.asObservable().pipe(Is()), r.reset$ = r._reset.asObservable(), r.params = ct({}, Wl, t), r.params.watchProperty && (t = Ro(r.params.watchProperty), e instanceof kl && t.includes("entities") && !t.includes("ids") && t.push("ids"), r.params.watchProperty = t), r + } + + function Zl() { + return Math.random().toString(36).slice(2) + } + + function ed(e) { + return Mr(e).pipe($n(0)) + } + + function td(e, t) { + var i = Xl.call(this, e, (t = void 0 === t ? {} : t).entityIds) || this; + return i.query = e, i.params = t, i._someDirty = new Xt, i.someDirty$ = an(i.query.select(function(e) { + return e.entities + }), i._someDirty.asObservable()).pipe($n(0), hr(function() { + return i.checkSomeDirty() + })), i.params = ct({}, Wl, t), i.activate(), i.selectIds().pipe(ka(1)).subscribe(function(e) { + Xl.prototype.rebase.call(i, e, { + afterAdd: function(e) { + return e.setHead() + } + }) + }), i + } + ut(td, Xl = Vr), td.prototype.setHead = function(e) { + if (this.params.entityIds && e) { + var t = Ro(e); + if (!1 === Ro(this.params.entityIds).some(function(e) { + return -1 < t.indexOf(e) + })) return this + } + return this.forEachId(e, function(e) { + return e.setHead() + }), this._someDirty.next(), this + }, td.prototype.hasHead = function(e) { + return !!this.entities.has(e) && this.getEntity(e).hasHead() + }, td.prototype.reset = function(e, t) { + void 0 === t && (t = {}), this.forEachId(e, function(e) { + return e.reset(t) + }) + }, td.prototype.isDirty = function(e, t) { + if (void 0 === t && (t = !0), this.entities.has(e)) { + e = this.getEntity(e); + return t ? e.isDirty$ : e.isDirty() + } + return !1 + }, td.prototype.someDirty = function() { + return this.checkSomeDirty() + }, td.prototype.isPathDirty = function(e, t) { + if (this.entities.has(e)) { + var i = this.getEntity(e).getHead(), + e = Gl(this.query.getEntity(e), t), + t = Gl(i, t); + return this.params.comparator(e, t) + } + return null + }, td.prototype.destroy = function(e) { + this.forEachId(e, function(e) { + return e.destroy() + }), e || this._someDirty.complete() + }, td.prototype.instantiatePlugin = function(e) { + return new Yl(this.query, this.params, e) + }, td.prototype.checkSomeDirty = function() { + var e, t, i = this.resolvedIds(); + try { + for (var r = ft(i), n = r.next(); !n.done; n = r.next()) { + var s = n.value; + if (this.getEntity(s).isDirty()) return !0 + } + } catch (t) { + e = { + error: t + } + } finally { + try { + n && !n.done && (t = r.return) && t.call(r) + } finally { + if (e) throw e.error + } + } + return !1 + }, (tu = tu || {}).Update = "UPDATE", {} [tu.Update] = "update", (vr = ru = ru || {}).Update = "UPDATE", vr.AddEntities = "ADD_ENTITIES", vr.SetEntities = "SET_ENTITIES", vr.UpdateEntities = "UPDATE_ENTITIES", vr.RemoveEntities = "REMOVE_ENTITIES", vr.UpsertEntities = "UPSERT_ENTITIES", vr.UpsertManyEntities = "UPSERT_MANY_ENTITIES", (iu = {})[ru.Update] = "update", iu[ru.AddEntities] = "add", iu[ru.SetEntities] = "set", iu[ru.UpdateEntities] = "update", iu[ru.RemoveEntities] = "remove", iu[ru.UpsertEntities] = "upsert", iu[ru.UpsertManyEntities] = "upsertMany"; + w = {}; + Object.defineProperty(w, "__esModule", { + value: !0 + }); + var vi = "undefined" != typeof Symbol && "symbol" == typeof Symbol("x"), + id = "undefined" != typeof Map, + rd = "undefined" != typeof Set, + nd = "undefined" != typeof Proxy && void 0 !== Proxy.revocable && "undefined" != typeof Reflect, + sd = vi ? Symbol.for("immer-nothing") : ((nu = {})["immer-nothing"] = !0, nu), + ad = vi ? Symbol.for("immer-draftable") : "__$immer_draftable", + od = vi ? Symbol.for("immer-state") : "__$immer_state", + ld = "undefined" != typeof Symbol && Symbol.iterator || "@@iterator", + dd = { + 0: "Illegal state", + 1: "Immer drafts cannot have computed properties", + 2: "This object has been frozen and should not be mutated", + 3: function(e) { + return "Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + e + }, + 4: "An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.", + 5: "Immer forbids circular references", + 6: "The first or second argument to `produce` must be a function", + 7: "The third argument to `produce` must be a function or undefined", + 8: "First argument to `createDraft` must be a plain object, an array, or an immerable object", + 9: "First argument to `finishDraft` must be a draft returned by `createDraft`", + 10: "The given draft is already finalized", + 11: "Object.defineProperty() cannot be used on an Immer draft", + 12: "Object.setPrototypeOf() cannot be used on an Immer draft", + 13: "Immer only supports deleting array indices", + 14: "Immer only supports setting array indices and the 'length' property", + 15: function(e) { + return "Cannot apply patch, path doesn't resolve: " + e + }, + 16: 'Sets cannot have "replace" patches.', + 17: function(e) { + return "Unsupported patch operation: " + e + }, + 18: function(e) { + return "The plugin for '" + e + "' has not been loaded into Immer. To enable the plugin, import and call `enable" + e + "()` when initializing your application." + }, + 20: "Cannot use proxies if Proxy, Proxy.revocable or Reflect are not available", + 21: function(e) { + return "produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '" + e + "'" + }, + 22: function(e) { + return "'current' expects a draft, got: " + e + }, + 23: function(e) { + return "'original' expects a draft, got: " + e + } + }; + + function ud(e) { + for (var t = arguments.length, i = new Array(1 < t ? t - 1 : 0), r = 1; r < t; r++) i[r - 1] = arguments[r]; + var n = dd[e], + e = n ? "function" == typeof n ? n.apply(null, i) : n : "unknown error nr: " + e; + throw new Error("[Immer] " + e) + } + + function cd(e) { + return !!e && !!e[od] + } + + function hd(t) { + return !!t && (function() { + if (!t || "object" != typeof t) return !1; + var e = Object.getPrototypeOf(t); + return !e || e === Object.prototype + }() || Array.isArray(t) || !!t[ad] || !!t.constructor[ad] || Td(t) || Ed(t)) + } + var pd = "undefined" != typeof Reflect && Reflect.ownKeys ? Reflect.ownKeys : void 0 !== Object.getOwnPropertySymbols ? function(e) { + return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e)) + } : Object.getOwnPropertyNames, + fd = Object.getOwnPropertyDescriptors || function(t) { + var i = {}; + return pd(t).forEach(function(e) { + i[e] = Object.getOwnPropertyDescriptor(t, e) + }), i + }; + + function md(i, r, t) { + void 0 === t && (t = !1), 0 === gd(i) ? (t ? Object.keys : pd)(i).forEach(function(e) { + t && "symbol" == typeof e || r(e, i[e], i) + }) : i.forEach(function(e, t) { + return r(t, e, i) + }) + } + + function gd(e) { + var t = e[od]; + return t ? 3 < t.type_ ? t.type_ - 4 : t.type_ : Array.isArray(e) ? 1 : Td(e) ? 2 : Ed(e) ? 3 : 0 + } + + function yd(e, t) { + return 2 === gd(e) ? e.has(t) : Object.prototype.hasOwnProperty.call(e, t) + } + + function vd(e, t) { + return 2 === gd(e) ? e.get(t) : e[t] + } + + function Sd(e, t, i) { + var r = gd(e); + 2 === r ? e.set(t, i) : 3 === r ? (e.delete(t), e.add(i)) : e[t] = i + } + + function bd(e, t) { + return e === t ? 0 !== e || 1 / e == 1 / t : e != e && t != t + } + + function Td(e) { + return id && e instanceof Map + } + + function Ed(e) { + return rd && e instanceof Set + } + + function Id(e) { + return e.copy_ || e.base_ + } + + function wd(e) { + if (Array.isArray(e)) return Array.prototype.slice.call(e); + var t = fd(e); + delete t[od]; + for (var i = pd(t), r = 0; r < i.length; r++) { + var n = i[r], + s = t[n]; + !1 === s.writable && (s.writable = !0, s.configurable = !0), (s.get || s.set) && (t[n] = { + configurable: !0, + writable: !0, + enumerable: s.enumerable, + value: e[n] + }) + } + return Object.create(Object.getPrototypeOf(e), t) + } + + function Ad(e, t) { + kd(e) || cd(e) || !hd(e) || (1 < gd(e) && (e.set = e.add = e.clear = e.delete = Od), Object.freeze(e), t && md(e, function(e, t) { + return Ad(t, !0) + }, !0)) + } + + function Od() { + ud(2) + } + + function kd(e) { + return null == e || "object" != typeof e || Object.isFrozen(e) + } + var Cd, Dd = {}; + + function Md(e) { + var t = Dd[e]; + return t || ud(18, e), t + } + + function xd(e, t) { + Dd[e] || (Dd[e] = t) + } + + function Pd() { + return Cd || ud(0), Cd + } + + function Rd(e, t) { + t && (Md("Patches"), e.patches_ = [], e.inversePatches_ = [], e.patchListener_ = t) + } + + function Ld(e) { + _d(e), e.drafts_.forEach(Fd), e.drafts_ = null + } + + function _d(e) { + e === Cd && (Cd = e.parent_) + } + + function Nd(e) { + return Cd = { + drafts_: [], + parent_: Cd, + immer_: e, + canAutoFreeze_: !0, + unfinalizedDrafts_: 0 + } + } + + function Fd(e) { + e = e[od]; + 0 === e.type_ || 1 === e.type_ ? e.revoke_() : e.revoked_ = !0 + } + + function Bd(e, t) { + t.unfinalizedDrafts_ = t.drafts_.length; + var i = t.drafts_[0], + r = void 0 !== e && e !== i; + return t.immer_.useProxies_ || Md("ES5").willFinalizeES5_(t, e, r), r ? (i[od].modified_ && (Ld(t), ud(4)), hd(e) && (e = Ud(t, e), t.parent_ || Vd(t, e)), t.patches_ && Md("Patches").generateReplacementPatches_(i[od], e, t.patches_, t.inversePatches_)) : e = Ud(t, i, []), Ld(t), t.patches_ && t.patchListener_(t.patches_, t.inversePatches_), e !== sd ? e : void 0 + } + + function Ud(i, r, n) { + if (kd(r)) return r; + var s, a = r[od]; + return a ? a.scope_ !== i ? r : a.modified_ ? (a.finalized_ || (a.finalized_ = !0, a.scope_.unfinalizedDrafts_--, s = 4 === a.type_ || 5 === a.type_ ? a.copy_ = wd(a.draft_) : a.copy_, md(3 === a.type_ ? new Set(s) : s, function(e, t) { + return $d(i, a, s, e, t, n) + }), Vd(i, s, !1), n && i.patches_ && Md("Patches").generatePatches_(a, n, i.patches_, i.inversePatches_)), a.copy_) : (Vd(i, a.base_, !0), a.base_) : (md(r, function(e, t) { + return $d(i, a, r, e, t, n) + }, !0), r) + } + + function $d(e, t, i, r, n, s) { + if (n === i && ud(5), cd(n)) { + s = Ud(e, n, s && t && 3 !== t.type_ && !yd(t.assigned_, r) ? s.concat(r) : void 0); + if (Sd(i, r, s), !cd(s)) return; + e.canAutoFreeze_ = !1 + } + hd(n) && !kd(n) && (!e.immer_.autoFreeze_ && e.unfinalizedDrafts_ < 1 || (Ud(e, n), t && t.scope_.parent_ || Vd(e, n))) + } + + function Vd(e, t, i) { + void 0 === i && (i = !1), e.immer_.autoFreeze_ && e.canAutoFreeze_ && Ad(t, i) + } + var Kd = { + get: function(e, t) { + if (t === od) return e; + var i, r, n = Id(e); + if (!yd(n, t)) return i = e, (r = jd(n, t)) ? "value" in r ? r.value : null === (r = r.get) || void 0 === r ? void 0 : r.call(i.draft_) : void 0; + n = n[t]; + return !e.finalized_ && hd(n) && n === Hd(e.base_, t) ? (Wd(e), e.copy_[t] = zd(e.scope_.immer_, n, e)) : n + }, + has: function(e, t) { + return t in Id(e) + }, + ownKeys: function(e) { + return Reflect.ownKeys(Id(e)) + }, + set: function(e, t, i) { + var r = jd(Id(e), t); + if (null != r && r.set) return r.set.call(e.draft_, i), !0; + if (!e.modified_) { + var n = Hd(Id(e), t), + r = null == n ? void 0 : n[od]; + if (r && r.base_ === i) return e.copy_[t] = i, !(e.assigned_[t] = !1); + if (bd(i, n) && (void 0 !== i || yd(e.base_, t))) return !0; + Wd(e), Qd(e) + } + return e.copy_[t] = i, e.assigned_[t] = !0 + }, + deleteProperty: function(e, t) { + return void 0 !== Hd(e.base_, t) || t in e.base_ ? (e.assigned_[t] = !1, Wd(e), Qd(e)) : delete e.assigned_[t], e.copy_ && delete e.copy_[t], !0 + }, + getOwnPropertyDescriptor: function(e, t) { + var i = Id(e), + r = Reflect.getOwnPropertyDescriptor(i, t); + return r && { + writable: !0, + configurable: 1 !== e.type_ || "length" !== t, + enumerable: r.enumerable, + value: i[t] + } + }, + defineProperty: function() { + ud(11) + }, + getPrototypeOf: function(e) { + return Object.getPrototypeOf(e.base_) + }, + setPrototypeOf: function() { + ud(12) + } + }, + qd = {}; + + function Hd(e, t) { + var i = e[od]; + return (i ? Id(i) : e)[t] + } + + function jd(e, t) { + if (t in e) + for (var i = Object.getPrototypeOf(e); i;) { + var r = Object.getOwnPropertyDescriptor(i, t); + if (r) return r; + i = Object.getPrototypeOf(i) + } + } + + function Qd(e) { + e.modified_ || (e.modified_ = !0, e.parent_ && Qd(e.parent_)) + } + + function Wd(e) { + e.copy_ || (e.copy_ = wd(e.base_)) + } + md(Kd, function(e, t) { + qd[e] = function() { + return arguments[0] = arguments[0][0], t.apply(this, arguments) + } + }), qd.deleteProperty = function(e, t) { + return isNaN(parseInt(t)) && ud(13), Kd.deleteProperty.call(this, e[0], t) + }, qd.set = function(e, t, i) { + return "length" !== t && isNaN(parseInt(t)) && ud(14), Kd.set.call(this, e[0], t, i, e[0]) + }; + (A = Gd.prototype).produce = function(e, s, t) { + if ("function" == typeof e && "function" != typeof s) { + var a = s; + s = e; + var o = this; + return function(e) { + var t = this; + void 0 === e && (e = a); + for (var i = arguments.length, r = new Array(1 < i ? i - 1 : 0), n = 1; n < i; n++) r[n - 1] = arguments[n]; + return o.produce(e, function(e) { + return s.call.apply(s, [t, e].concat(r)) + }) + } + } + var i; + if ("function" != typeof s && ud(6), void 0 !== t && "function" != typeof t && ud(7), hd(e)) { + var r = Nd(this), + n = zd(this, e, void 0), + l = !0; + try { + i = s(n), l = !1 + } finally { + (l ? Ld : _d)(r) + } + return "undefined" != typeof Promise && i instanceof Promise ? i.then(function(e) { + return Rd(r, t), Bd(e, r) + }, function(e) { + throw Ld(r), e + }) : (Rd(r, t), Bd(i, r)) + } + if (!e || "object" != typeof e) return (i = s(e)) === sd ? void 0 : (void 0 === i && (i = e), this.autoFreeze_ && Ad(i, !0), i); + ud(21, e) + }, A.produceWithPatches = function(n, e, t) { + var i, r, s = this; + return "function" == typeof n ? function(e) { + for (var t = arguments.length, i = new Array(1 < t ? t - 1 : 0), r = 1; r < t; r++) i[r - 1] = arguments[r]; + return s.produceWithPatches(e, function(e) { + return n.apply(void 0, [e].concat(i)) + }) + } : [this.produce(n, e, function(e, t) { + i = e, r = t + }), i, r] + }, A.createDraft = function(e) { + hd(e) || ud(8), cd(e) && (e = Xd(e)); + var t = Nd(this), + e = zd(this, e, void 0); + return e[od].isManual_ = !0, _d(t), e + }, A.finishDraft = function(e, t) { + e = e && e[od]; + e && e.isManual_ || ud(9), e.finalized_ && ud(10); + e = e.scope_; + return Rd(e, t), Bd(void 0, e) + }, A.setAutoFreeze = function(e) { + this.autoFreeze_ = e + }, A.setUseProxies = function(e) { + e && !nd && ud(20), this.useProxies_ = e + }, A.applyPatches = function(e, t) { + for (var i = t.length - 1; 0 <= i; i--) { + var r = t[i]; + if (0 === r.path.length && "replace" === r.op) { + e = r.value; + break + } + } + var n = Md("Patches").applyPatches_; + return cd(e) ? n(e, t) : this.produce(e, function(e) { + return n(e, t.slice(i + 1)) + }) + }, Vr = Gd; + + function Gd(e) { + this.useProxies_ = nd, this.autoFreeze_ = !0, "boolean" == typeof(null == e ? void 0 : e.useProxies) && this.setUseProxies(e.useProxies), "boolean" == typeof(null == e ? void 0 : e.autoFreeze) && this.setAutoFreeze(e.autoFreeze), this.produce = this.produce.bind(this), this.produceWithPatches = this.produceWithPatches.bind(this) + } + + function zd(e, t, i) { + t = Td(t) ? Md("MapSet").proxyMap_(t, i) : Ed(t) ? Md("MapSet").proxySet_(t, i) : e.useProxies_ ? function(e, t) { + var i = Array.isArray(e), + r = { + type_: i ? 1 : 0, + scope_: t ? t.scope_ : Pd(), + modified_: !1, + finalized_: !1, + assigned_: {}, + parent_: t, + base_: e, + draft_: null, + copy_: null, + revoke_: null, + isManual_: !1 + }, + t = r, + e = Kd; + i && (t = [r], e = qd); + t = Proxy.revocable(t, e), e = t.revoke, t = t.proxy; + return r.draft_ = t, r.revoke_ = e, t + }(t, i) : Md("ES5").createES5Proxy_(t, i); + return (i ? i.scope_ : Pd()).drafts_.push(t), t + } + + function Xd(e) { + return cd(e) || ud(22, e), + function i(e) { + if (!hd(e)) return e; + var r, n = e[od], + t = gd(e); + if (n) { + if (!n.modified_ && (n.type_ < 4 || !Md("ES5").hasChanges_(n))) return n.base_; + n.finalized_ = !0, r = Yd(e, t), n.finalized_ = !1 + } else r = Yd(e, t); + return md(r, function(e, t) { + n && vd(n.base_, e) === t || Sd(r, e, i(t)) + }), 3 === t ? new Set(r) : r + }(e) + } + + function Yd(e, t) { + switch (t) { + case 2: + return new Map(e); + case 3: + return Array.from(e) + } + return wd(e) + } + + function Jd() { + var r = {}; + + function l(i, e) { + var t = r[i]; + return t ? t.enumerable = e : r[i] = t = { + configurable: !0, + enumerable: e, + get: function() { + var e = this[od]; + return a(e), Kd.get(e, i) + }, + set: function(e) { + var t = this[od]; + a(t), Kd.set(t, i, e) + } + }, t + } + + function n(e) { + for (var t = e.length - 1; 0 <= t; t--) { + var i = e[t][od]; + if (!i.modified_) switch (i.type_) { + case 5: + u(i) && Qd(i); + break; + case 4: + s(i) && Qd(i) + } + } + } + + function s(e) { + for (var t = e.base_, i = e.draft_, r = pd(i), n = r.length - 1; 0 <= n; n--) { + var s = r[n]; + if (s !== od) { + var a = t[s]; + if (void 0 === a && !yd(t, s)) return !0; + var o = i[s], + s = o && o[od]; + if (s ? s.base_ !== a : !bd(o, a)) return !0 + } + } + e = !!t[od]; + return r.length !== pd(t).length + (e ? 0 : 1) + } + + function u(e) { + var t = e.draft_; + if (t.length !== e.base_.length) return !0; + t = Object.getOwnPropertyDescriptor(t, t.length - 1); + return !(!t || t.get) + } + + function a(e) { + e.revoked_ && ud(3, JSON.stringify(Id(e))) + } + xd("ES5", { + createES5Proxy_: function(e, t) { + var i = Array.isArray(e), + r = function(e, t) { + if (e) { + for (var i = new Array(t.length), r = 0; r < t.length; r++) Object.defineProperty(i, "" + r, l(r, !0)); + return i + } + var n = fd(t); + delete n[od]; + for (var s = pd(n), a = 0; a < s.length; a++) { + var o = s[a]; + n[o] = l(o, e || !!n[o].enumerable) + } + return Object.create(Object.getPrototypeOf(t), n) + }(i, e), + e = { + type_: i ? 5 : 4, + scope_: t ? t.scope_ : Pd(), + modified_: !1, + finalized_: !1, + assigned_: {}, + parent_: t, + base_: e, + draft_: r, + copy_: null, + revoked_: !1, + isManual_: !1 + }; + return Object.defineProperty(r, od, { + value: e, + writable: !0 + }), r + }, + willFinalizeES5_: function(e, t, i) { + i ? cd(t) && t[od].scope_ === e && n(e.drafts_) : (e.patches_ && function t(e) { + if (e && "object" == typeof e) { + var i = e[od]; + if (i) { + var r = i.base_, + n = i.draft_, + s = i.assigned_; + if (4 === (e = i.type_)) md(n, function(e) { + e !== od && (void 0 !== r[e] || yd(r, e) ? s[e] || t(n[e]) : (s[e] = !0, Qd(i))) + }), md(r, function(e) { + void 0 !== n[e] || yd(n, e) || (s[e] = !1, Qd(i)) + }); + else if (5 === e) { + if (u(i) && (Qd(i), s.length = !0), n.length < r.length) + for (var a = n.length; a < r.length; a++) s[a] = !1; + else + for (var o = r.length; o < n.length; o++) s[o] = !0; + for (var l = Math.min(n.length, r.length), d = 0; d < l; d++) void 0 === s[d] && t(n[d]) + } + } + } + }(e.drafts_[0]), n(e.drafts_)) + }, + hasChanges_: function(e) { + return (4 === e.type_ ? s : u)(e) + } + }) + } + + function Zd() { + var m = "replace", + g = "add", + y = "remove"; + + function d(e) { + if (!hd(e)) return e; + if (Array.isArray(e)) return e.map(d); + if (Td(e)) return new Map(Array.from(e.entries()).map(function(e) { + return [e[0], d(e[1])] + })); + if (Ed(e)) return new Set(Array.from(e).map(d)); + var t, i = Object.create(Object.getPrototypeOf(e)); + for (t in e) i[t] = d(e[t]); + return i + } + + function v(e) { + return cd(e) ? d(e) : e + } + xd("Patches", { + applyPatches_: function(l, e) { + return e.forEach(function(e) { + for (var t = e.path, i = e.op, r = l, n = 0; n < t.length - 1; n++) "object" != typeof(r = vd(r, t[n])) && ud(15, t.join("/")); + var s = gd(r), + a = d(e.value), + o = t[t.length - 1]; + switch (i) { + case m: + switch (s) { + case 2: + return r.set(o, a); + case 3: + ud(16); + default: + return r[o] = a + } + case g: + switch (s) { + case 1: + return r.splice(o, 0, a); + case 2: + return r.set(o, a); + case 3: + return r.add(a); + default: + return r[o] = a + } + case y: + switch (s) { + case 1: + return r.splice(o, 1); + case 2: + return r.delete(o); + case 3: + return r.delete(e.value); + default: + return delete r[o] + } + default: + ud(17, i) + } + }), l + }, + generatePatches_: function(c, e, t, i) { + switch (c.type_) { + case 0: + case 4: + case 2: + return d = e, u = t, h = i, p = c.base_, f = c.copy_, void md(c.assigned_, function(e, t) { + var i = vd(p, e), + r = vd(f, e), + t = t ? yd(p, e) ? m : g : y; + i === r && t == m || (e = d.concat(e), u.push(t == y ? { + op: t, + path: e + } : { + op: t, + path: e, + value: r + }), h.push(t == g ? { + op: y, + path: e + } : t == y ? { + op: g, + path: e, + value: v(i) + } : { + op: m, + path: e, + value: v(i) + })) + }); + case 5: + case 1: + return function(e, t, i) { + var r, n = c.base_, + s = c.assigned_, + a = c.copy_; + a.length < n.length && (n = (r = [a, n])[0], a = r[1], t = (r = [i, t])[0], i = r[1]); + for (var o, l = 0; l < n.length; l++) s[l] && a[l] !== n[l] && (o = e.concat([l]), t.push({ + op: m, + path: o, + value: v(a[l]) + }), i.push({ + op: m, + path: o, + value: v(n[l]) + })); + for (var d = n.length; d < a.length; d++) { + var u = e.concat([d]); + t.push({ + op: g, + path: u, + value: v(a[d]) + }) + } + n.length < a.length && i.push({ + op: m, + path: e.concat(["length"]), + value: n.length + }) + }(e, t, i); + case 3: + return r = e, n = t, s = i, a = c.base_, o = c.copy_, l = 0, a.forEach(function(e) { + var t; + o.has(e) || (t = r.concat([l]), n.push({ + op: y, + path: t, + value: e + }), s.unshift({ + op: g, + path: t, + value: e + })), l++ + }), l = 0, void o.forEach(function(e) { + var t; + a.has(e) || (t = r.concat([l]), n.push({ + op: g, + path: t, + value: e + }), s.unshift({ + op: y, + path: t, + value: e + })), l++ + }) + } + var r, n, s, a, o, l, d, u, h, p, f + }, + generateReplacementPatches_: function(e, t, i, r) { + i.push({ + op: m, + path: [], + value: t + }), r.push({ + op: m, + path: [], + value: e.base_ + }) + } + }) + } + + function eu() { + var r = function(e, t) { + return (r = Object.setPrototypeOf || { + __proto__: [] + } + instanceof Array && function(e, t) { + e.__proto__ = t + } || function(e, t) { + for (var i in t) t.hasOwnProperty(i) && (e[i] = t[i]) + })(e, t) + }; + + function i(e, t) { + function i() { + this.constructor = e + } + r(e, t), e.prototype = (i.prototype = t.prototype, new i) + } + var n = function() { + function e(e, t) { + return this[od] = { + type_: 2, + parent_: t, + scope_: t ? t.scope_ : Pd(), + modified_: !1, + finalized_: !1, + copy_: void 0, + assigned_: void 0, + base_: e, + draft_: this, + isManual_: !1, + revoked_: !1 + }, this + } + i(e, Map); + var t = e.prototype; + return Object.defineProperty(t, "size", { + get: function() { + return Id(this[od]).size + } + }), t.has = function(e) { + return Id(this[od]).has(e) + }, t.set = function(e, t) { + var i = this[od]; + return l(i), Id(i).has(e) && Id(i).get(e) === t || (s(i), Qd(i), i.assigned_.set(e, !0), i.copy_.set(e, t), i.assigned_.set(e, !0)), this + }, t.delete = function(e) { + if (!this.has(e)) return !1; + var t = this[od]; + return l(t), s(t), Qd(t), t.assigned_.set(e, !1), t.copy_.delete(e), !0 + }, t.clear = function() { + var t = this[od]; + l(t), Id(t).size && (s(t), Qd(t), t.assigned_ = new Map, md(t.base_, function(e) { + t.assigned_.set(e, !1) + }), t.copy_.clear()) + }, t.forEach = function(r, n) { + var s = this; + Id(this[od]).forEach(function(e, t, i) { + r.call(n, s.get(t), t, s) + }) + }, t.get = function(e) { + var t = this[od]; + l(t); + var i = Id(t).get(e); + if (t.finalized_ || !hd(i)) return i; + if (i !== t.base_.get(e)) return i; + i = zd(t.scope_.immer_, i, t); + return s(t), t.copy_.set(e, i), i + }, t.keys = function() { + return Id(this[od]).keys() + }, t.values = function() { + var e, t = this, + i = this.keys(); + return (e = {})[ld] = function() { + return t.values() + }, e.next = function() { + var e = i.next(); + return e.done ? e : { + done: !1, + value: t.get(e.value) + } + }, e + }, t.entries = function() { + var e, i = this, + r = this.keys(); + return (e = {})[ld] = function() { + return i.entries() + }, e.next = function() { + var e = r.next(); + if (e.done) return e; + var t = i.get(e.value); + return { + done: !1, + value: [e.value, t] + } + }, e + }, t[ld] = function() { + return this.entries() + }, e + }(); + + function s(e) { + e.copy_ || (e.assigned_ = new Map, e.copy_ = new Map(e.base_)) + } + var a = function() { + function e(e, t) { + return this[od] = { + type_: 3, + parent_: t, + scope_: t ? t.scope_ : Pd(), + modified_: !1, + finalized_: !1, + copy_: void 0, + base_: e, + draft_: this, + drafts_: new Map, + revoked_: !1, + isManual_: !1 + }, this + } + i(e, Set); + var t = e.prototype; + return Object.defineProperty(t, "size", { + get: function() { + return Id(this[od]).size + } + }), t.has = function(e) { + var t = this[od]; + return l(t), t.copy_ ? !!t.copy_.has(e) || !(!t.drafts_.has(e) || !t.copy_.has(t.drafts_.get(e))) : t.base_.has(e) + }, t.add = function(e) { + var t = this[od]; + return l(t), this.has(e) || (o(t), Qd(t), t.copy_.add(e)), this + }, t.delete = function(e) { + if (!this.has(e)) return !1; + var t = this[od]; + return l(t), o(t), Qd(t), t.copy_.delete(e) || !!t.drafts_.has(e) && t.copy_.delete(t.drafts_.get(e)) + }, t.clear = function() { + var e = this[od]; + l(e), Id(e).size && (o(e), Qd(e), e.copy_.clear()) + }, t.values = function() { + var e = this[od]; + return l(e), o(e), e.copy_.values() + }, t.entries = function() { + var e = this[od]; + return l(e), o(e), e.copy_.entries() + }, t.keys = function() { + return this.values() + }, t[ld] = function() { + return this.values() + }, t.forEach = function(e, t) { + for (var i = this.values(), r = i.next(); !r.done;) e.call(t, r.value, r.value, this), r = i.next() + }, e + }(); + + function o(i) { + i.copy_ || (i.copy_ = new Set, i.base_.forEach(function(e) { + var t; + hd(e) ? (t = zd(i.scope_.immer_, e, i), i.drafts_.set(e, t), i.copy_.add(t)) : i.copy_.add(e) + })) + } + + function l(e) { + e.revoked_ && ud(3, JSON.stringify(Id(e))) + } + xd("MapSet", { + proxyMap_: function(e, t) { + return new n(e, t) + }, + proxySet_: function(e, t) { + return new a(e, t) + } + }) + } + var tu = new Vr, + vr = tu.produce, + iu = tu.produceWithPatches.bind(tu), + ru = tu.setAutoFreeze.bind(tu), + nu = tu.setUseProxies.bind(tu), + vi = tu.applyPatches.bind(tu), + A = tu.createDraft.bind(tu), + tu = tu.finishDraft.bind(tu); + w.Immer = Vr, w.applyPatches = vi, w.castDraft = function(e) { + return e + }, w.castImmutable = function(e) { + return e + }, w.createDraft = A, w.current = Xd, w.default = vr, w.enableAllPlugins = function() { + Jd(), eu(), Zd() + }, w.enableES5 = Jd; + A = w.enableMapSet = eu; + w.enablePatches = Zd, w.finishDraft = tu, w.immerable = ad, w.isDraft = cd, w.isDraftable = hd, w.nothing = sd, w.original = function(e) { + return cd(e) || ud(23, e), e[od].base_ + }; + var su = w.produce = vr; + w.produceWithPatches = iu; + var au = w.setAutoFreeze = ru; + w.setUseProxies = nu; + const ou = /^((?:[^\/;?#]+:)?)(\/\/[^\/\;?#]*)?(.*?)??(;.*?)?(\?.*?)?(#.*?)?$/, + lu = /^([^\/;?#]*)(.*)$/, + du = /(?:\/|^)\.(?=\/)/g, + uu = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g, + cu = { + buildAbsoluteURL: function(e, t, i) { + if (i = i || {}, e = e.trim(), !(t = t.trim())) { + if (!i.alwaysNormalize) return e; + const t = cu.parseURL(e); + if (!t) throw new Error("Error trying to parse base URL."); + return t.path = cu.normalizePath(t.path), cu.buildURLFromParts(t) + } + const r = cu.parseURL(t); + if (!r) throw new Error("Error trying to parse relative URL."); + if (r.scheme) return i.alwaysNormalize ? (r.path = cu.normalizePath(r.path), cu.buildURLFromParts(r)) : t; + const n = cu.parseURL(e); + if (!n) throw new Error("Error trying to parse base URL."); + if (!n.netLoc && n.path && "/" !== n.path[0]) { + const e = lu.exec(n.path); + n.netLoc = e[1], n.path = e[2] + } + n.netLoc && !n.path && (n.path = "/"); + const s = { + scheme: n.scheme, + netLoc: r.netLoc, + path: null, + params: r.params, + query: r.query, + fragment: r.fragment + }; + if (!r.netLoc && (s.netLoc = n.netLoc, "/" !== r.path[0])) + if (r.path) { + i.inheritQuery && (r.query || (s.query = n.query)); + const e = n.path, + t = e.substring(0, e.lastIndexOf("/") + 1) + r.path; + s.path = cu.normalizePath(t) + } else s.path = n.path, r.params || (s.params = n.params, r.query || (s.query = n.query)); + return null === s.path && (s.path = i.alwaysNormalize ? cu.normalizePath(r.path) : r.path), cu.buildURLFromParts(s) + }, + parseURL: function(e) { + e = ou.exec(e); + return e ? { + scheme: e[1] || "", + netLoc: e[2] || "", + path: e[3] || "", + params: e[4] || "", + query: e[5] || "", + fragment: e[6] || "" + } : null + }, + normalizePath: function(e) { + for (e = e.split("").reverse().join("").replace(du, ""); e.length !== (e = e.replace(uu, "")).length;); + return e.split("").reverse().join("") + }, + buildURLFromParts: function(e) { + return e.scheme + e.netLoc + e.path + e.params + e.query + e.fragment + }, + getHostName: function(e) { + let t; + return e && (t = -1 < e.indexOf("://") ? e.split("/")[2] : e.split("/")[0], t = t.split(":")[0], t = t.split("?")[0]), t + } + }; + var hu, pu, fu, mu, gu, yu, vu, Su, bu = cu; + + function Tu(e) { + return null != e && !e.startsWith("http://") && !e.startsWith("https://") + } + + function Eu(e) { + return null == e || Tu(e) ? null : cu.getHostName(e) + } + const Iu = [".*.itunes.apple.com"], + wu = { + deviceName: "&xapdn=", + deviceModel: "&xapdm=", + language: "&xapdl=", + dsid: "&xapdsid=", + subs: "&xapsub=" + }; + + function Au(e, t) { + return !e || e === Eu(t) + }(iu = hu = hu || {})[iu.DOVI = 4] = "DOVI", iu[iu.HEVC = 3] = "HEVC", iu[iu.VP09 = 2] = "VP09", iu[iu.AVC = 1] = "AVC", iu[iu.UNKNOWN = 0] = "UNKNOWN", (ru = pu = pu || {})[ru.PQ = 3] = "PQ", ru[ru.HLG = 2] = "HLG", ru[ru.SDR = 1] = "SDR", ru[ru.UNKNOWN = 0] = "UNKNOWN", (w = fu = fu || {})[w.ALAC = 7] = "ALAC", w[w.FLAC = 6] = "FLAC", w[w.EC3 = 5] = "EC3", w[w.AC3 = 4] = "AC3", w[w.XHEAAC = 3] = "XHEAAC", w[w.AAC = 2] = "AAC", w[w.MP3 = 1] = "MP3", w[w.UNKNOWN = 0] = "UNKNOWN", (nu = mu = mu || {})[nu.VALID = 1] = "VALID", nu[nu.INVALID = 0] = "INVALID"; + const Ou = ["via", "x-apple-request-uuid"], + ku = { + maxNumRetry: 4, + retryDelayMs: 0, + maxRetryDelayMs: 0 + }, + Cu = { + maxNumRetry: 6, + retryDelayMs: 1e3, + maxRetryDelayMs: 8e3 + }, + Du = { + maxNumRetry: 0, + retryDelayMs: 0, + maxRetryDelayMs: 0 + }, + Mu = { + default: { + maxTimeToFirstByteMs: 5e3, + maxLoadTimeMs: 2e4, + autoRetry: !1, + timeoutRetry: Du, + errorRetry: Du + }, + customURL: { + maxTimeToFirstByteMs: 1e4, + maxLoadTimeMs: 2e4, + autoRetry: !1, + timeoutRetry: Du, + errorRetry: Du + } + }, + xu = { + maxNumRetry: 8, + retryDelayMs: 1e3, + maxRetryDelayMs: 2e4, + backoff: "linear" + }, + Pu = Object.assign(Object.assign({}, xu), { + maxNumRetry: 1 + }), + Ru = { + autoStartLoad: !0, + startPosition: NaN, + defaultAudioCodec: void 0, + defaultVideoCodec: void 0, + debug: !1, + debugLevel: "info", + buildType: void 0, + minFramesBeforeSwitchingLevel: 11, + minTargetDurations: 3, + maxBufferLength: 60, + maxBufferHole: .5, + maxSeekHole: 2, + nudgeFromEventSeek: !0, + jaggedSeekTolerance: 0, + discontinuitySeekTolerance: 2, + bufferedSegmentEjectionToleranceMs: .5, + almostDryBufferSec: .5, + maxTotalDurationTolerance: .1, + lowBufferThreshold: .5, + lowBufferWatchdogPeriod: .5, + highBufferWatchdogPeriod: 3, + seekWatchdogPeriod: 5, + nudgeOffset: .1, + nudgeMaxRetry: 3, + maxFragLookUpTolerance: .2, + initialLiveManifestSize: 1, + liveSyncDurationCount: 3, + liveMaxLatencyDurationCount: 1 / 0, + liveSyncDuration: void 0, + liveMaxLatencyDuration: void 0, + liveFlushExpiredFrags: !0, + liveMaxUnchangedPlaylistRefresh: 3, + liveEdgeForZeroStartPositon: !1, + livePlaylistUpdateStaleness: 2, + livePlaylistDurationNudge: .001, + allowFastSwitchUp: !1, + minMatchGroupDuration: 5, + desiredIframeFPS: 8, + initialIframeFPS: 6, + minRemainingTimeInMediaPipeline: 3, + leftMediaTimeToAutoPause: 10, + startTargetDurationFactor: .9, + minRequiredStartDuration: 4, + maxRequiredStartDuration: 15, + enableWorker: !0, + enableWebCrypto: !0, + keySystemPreference: void 0, + useMultipleKeySessions: !1, + enablePlayReadyKeySystem: !1, + useMediaKeySystemAccessFilter: !1, + playReadyMessageFormat: "utf16", + startLevel: void 0, + livePlaylistRefreshDelay: 2500, + liveMinPlayingBufferLen: 5, + enableIFramePreloading: !0, + useMediaCapabilities: !1, + enableID3Cues: !0, + certLoadPolicy: Mu, + keyLoadPolicy: { + default: { + maxTimeToFirstByteMs: 5e3, + maxLoadTimeMs: 2e4, + autoRetry: !1, + timeoutRetry: Pu, + errorRetry: xu + }, + customURL: { + maxTimeToFirstByteMs: 1e4, + maxLoadTimeMs: 2e4, + autoRetry: !1, + timeoutRetry: Pu, + errorRetry: xu + } + }, + manifestLoadPolicy: { + default: { + maxTimeToFirstByteMs: 1e4, + maxLoadTimeMs: 2e4, + autoRetry: !1, + timeoutRetry: { + maxNumRetry: 2, + retryDelayMs: 0, + maxRetryDelayMs: 0 + }, + errorRetry: { + maxNumRetry: 1, + retryDelayMs: 1e3, + maxRetryDelayMs: 8e3 + } + }, + customURL: { + maxTimeToFirstByteMs: 1e4, + maxLoadTimeMs: 1e4, + autoRetry: !1, + timeoutRetry: { + maxNumRetry: 2, + retryDelayMs: 0, + maxRetryDelayMs: 0 + }, + errorRetry: { + maxNumRetry: 1, + retryDelayMs: 1e3, + maxRetryDelayMs: 8e3 + } + } + }, + trickPlaybackConfig: { + enabled: !0, + minIframeDuration: 8 + }, + playlistLoadPolicy: { + default: { + maxTimeToFirstByteMs: 1e4, + maxLoadTimeMs: 2e4, + autoRetry: !1, + timeoutRetry: { + maxNumRetry: 2, + retryDelayMs: 0, + maxRetryDelayMs: 0 + }, + errorRetry: { + maxNumRetry: 2, + retryDelayMs: 1e3, + maxRetryDelayMs: 8e3 + } + }, + customURL: { + maxTimeToFirstByteMs: 1e4, + maxLoadTimeMs: 1e4, + autoRetry: !1, + timeoutRetry: { + maxNumRetry: 2, + retryDelayMs: 0, + maxRetryDelayMs: 0 + }, + errorRetry: { + maxNumRetry: 2, + retryDelayMs: 1e3, + maxRetryDelayMs: 8e3 + } + } + }, + fragLoadPolicy: { + default: { + maxTimeToFirstByteMs: 5e3, + maxLoadTimeMs: 2e4, + autoRetry: !1, + timeoutRetry: ku, + errorRetry: Cu, + forceContentLenCheckIfNoHeader: !1, + reportCDNServer: !0 + }, + customURL: { + maxTimeToFirstByteMs: 1e4, + maxLoadTimeMs: 2e4, + autoRetry: !1, + timeoutRetry: ku, + errorRetry: Cu, + reportCDNServer: !0 + } + }, + steeringManifestLoadPolicy: { + default: { + maxTimeToFirstByteMs: 1e4, + maxLoadTimeMs: 2e4, + autoRetry: !1, + timeoutRetry: { + maxNumRetry: 2, + retryDelayMs: 0, + maxRetryDelayMs: 0 + }, + errorRetry: { + maxNumRetry: 1, + retryDelayMs: 1e3, + maxRetryDelayMs: 8e3 + } + }, + customURL: { + maxTimeToFirstByteMs: 1e4, + maxLoadTimeMs: 1e4, + autoRetry: !1, + timeoutRetry: { + maxNumRetry: 2, + retryDelayMs: 0, + maxRetryDelayMs: 0 + }, + errorRetry: { + maxNumRetry: 1, + retryDelayMs: 1e3, + maxRetryDelayMs: 8e3 + } + } + }, + maxNumAddLevelToPenaltyBox: 4, + firstAudioMustOverlapVideoStart: !1, + keyMinHoldTimeBeforeCleanup: 15e3, + startFragPrefetch: !1, + appendErrorMaxRetry: 3, + alwaysResetOnNewCC: !1, + fLoader: void 0, + pLoader: void 0, + xhrSetup: void 0, + iframeMaxExitSeekDuration: 2e3, + iframeStallMaxRetry: 5, + audioPrimingDelay: 0, + enableCEA708Captions: !0, + customTextTrackCueRenderer: !1, + enableWebVTT: !0, + captionsTextTrack1Label: "English", + captionsTextTrack1LanguageCode: "en", + captionsTextTrack2Label: "Spanish", + captionsTextTrack2LanguageCode: "es", + enableDualTrackSelection: !1, + condenseSubtitleTrack: !1, + nativeTextTrackChangeHandling: !0, + earlyFragTolerance: 7, + vttConcurrentLoadCount: 1, + trottleCheckInterval: 2e3, + subtitleLeadTime: 30, + lateTolerance: 2, + stretchShortVideoTrack: !1, + forceKeyFrameOnDiscontinuity: !0, + useFirstLevelAtIncompatDiscontinuity: !0, + abrBandwidthEstimator: "bandwidth-history-controller", + abrEwmaDefaultEstimate: 5e5, + abrDefaultEstimate: 5e5, + abrBandWidthFactor: .95, + abrBandWidthUpFactor: .9, + abrMaxWithRealBitrate: !1, + maxStarvationDelay: 4, + maxLoadingDelay: 4, + minAutoBitrate: 0, + enableRtcReporting: !1, + rtcIntervalTimeout: 3e5, + rtcSender: "HLSJS", + rtcSessionTag: "none", + useHTTPPlaybackSessionId: !1, + warmupCdms: !1, + enablePerformanceLogging: !1, + overridePlaybackRate: !1, + nativeControlsEnabled: !1, + useCustomMediaFunctions: !0, + seekEventThrottleMs: 150, + enableAdaptiveStartup: !0, + bandwidthHistoryWindowSize: 12e4, + bandwidthHistoryTTL: 6e5, + bandwidthHistoryAggregationMethod: "quadratic-time-weighted", + bandwidthHistoryGetEstimateThrottleMs: 1e3, + defaultTargetDuration: 10, + targetStartupMs: 4e3, + adaptiveStartupMetricsOverride: { + maxValidHeight: 1080, + maxValidBitrate: 1 / 0, + maxPreferredBitrate: 1 / 0 + }, + bandwidthHistoryStorageKey: "AppleHLS-bandwidth-estimation", + storageKeyPrefix: "AppleHLS-", + storage: { + get: "undefined" == typeof localStorage ? void 0 : localStorage.getItem.bind(localStorage), + set: "undefined" == typeof localStorage ? void 0 : localStorage.setItem.bind(localStorage) + }, + minFragmentCount: 10, + minPlaylistCount: 5, + enableCDNFallback: !0, + enableQueryParamsForITunes: !1, + gapless: !1, + useViewportSizeForLevelCap: !1, + statDefaults: { + playlistLoadTimeMs: 500, + playlistParseTimeMs: 50, + fragParseTimeMs: 50, + fragBufferCreationDelayMs: 200, + dataFragAppendMs: 50, + initFragAppendMs: 50 + }, + disableVideoCodecList: new Set([]), + disableAudioCodecList: new Set([fu.ALAC, fu.FLAC, fu.XHEAAC]), + useHighestVideoCodecPrivate: !0, + sessionDataAutoLoad: { + "com.apple.hls.chapters": !0 + } + }, + Lu = Object.assign(Object.assign({}, { + itemId: "Nah", + mediaOptionId: "Nah" + }), { + mediaOptionType: void 0 + }), + _u = e => { + var { + itemId: t, + mediaOptionId: e + } = e; + return "Nah" !== t && "Nah" !== e + }; + (iu = gu = gu || {})[iu.Variant = 0] = "Variant", iu[iu.AltAudio = 1] = "AltAudio", iu[iu.Subtitle = 2] = "Subtitle"; + const Nu = ["variant", "altAudio", "subtitle"], + Fu = [gu.Variant, gu.AltAudio, gu.Subtitle], + Bu = [gu.Variant, gu.AltAudio]; + (ru = yu = yu || {})[ru.Variant = 0] = "Variant", ru[ru.AltAudio = 1] = "AltAudio"; + const Uu = ["variant", "altAudio"]; + + function $u(e) { + switch (e) { + case gu.Variant: + return yu.Variant; + case gu.AltAudio: + return yu.AltAudio; + default: + return null + } + } + + function Vu(e) { + return e === yu.Variant ? gu.Variant : gu.AltAudio + }(w = vu = vu || {})[w.NO = 0] = "NO", w[w.YES = 1] = "YES", (nu = Su = Su || {}).UNKNOWN = "unkn", nu.VIDEO = "vide", nu.AUDIO = "soun", nu.SUBTITLE = "sbtl", nu.CLOSEDCAPTION = "clcp"; + const Ku = { + search: function(e, t) { + let i, r, n = 0, + s = (null == e ? void 0 : e.length) - 1; + for (; n <= s;) { + var a = t(r = e[i = (n + s) / 2 | 0]); + if (0 < a) n = 1 + i; + else { + if (!(a < 0)) return r; + s = i - 1 + } + } + return null + } + }; + var qu = Ku, + Hu = { + findFragmentBySNAndBuffer: function(e, t, i = 0, r = 0, n = 0) { + let s; + e = e ? t[e.mediaSeqNum - t[0].mediaSeqNum + 1] : null; + return s = i < r ? (r - n < i && (n = 0), e && !this.fragmentWithinToleranceTest(i, n, e) ? e : Ku.search(t, this.fragmentWithinToleranceTest.bind(null, i, n))) : t[t.length - 1], s + }, + fragmentWithinToleranceTest: function(e, t, i) { + t = Math.min(t, i.duration); + return i.start + i.duration - t <= e ? 1 : i.start - t > e && i.start ? -1 : 0 + } + }; + const ju = { + startFragmentInCC: function(e, t, i) { + let r = t - i.discoSeqNum; + if (0 === r) { + const t = e[i.mediaSeqNum - e[0].mediaSeqNum - 1]; + r = t && t.discoSeqNum === i.discoSeqNum ? -1 : 0 + } + return r + }, + endFragmentInCC: function(e, t, i) { + let r = t - i.discoSeqNum; + if (0 === r) { + const t = e[i.mediaSeqNum - e[0].mediaSeqNum + 1]; + r = t && t.discoSeqNum === i.discoSeqNum ? 1 : 0 + } + return r + }, + findStartEndFragmentsInCC: function(e, t, i) { + let r, n; + return 0 < (null == e ? void 0 : e.length) && ne(t) && (r = Ku.search(e, ju.startFragmentInCC.bind(null, e, t)), n = Ku.search(e, ju.endFragmentInCC.bind(null, e, t))), r && !ne(r.discoSeqNum) && (r = void 0), n && !ne(n.discoSeqNum) && (n = void 0), { + startFrag: r, + endFrag: n + } + }, + getTimeRangeDictForCC: function(e, t, i) { + var { + startFrag: t, + endFrag: i + } = ju.findStartEndFragmentsInCC(e, t, i); + return { + start: t.start, + end: i.start + i.duration + } + }, + getTimeRangeForCC: function(e, t, i) { + var { + startFrag: t, + endFrag: i + } = ju.findStartEndFragmentsInCC(e, t, i); + let r = []; + return t && i && (r = [t.start, i.start + i.duration]), r + }, + snapToCCTimeRange: function(e, t, i, r) { + r = ju.getTimeRangeForCC(t, i, r); + return null != r && r.length ? Math.min(r[1], Math.max(r[0], e)) : e + }, + getMinTimeForCC(e, t, i, r) { + if (null == e || !e.length) return 0; + let n = 0; + if (ne(i) && e) { + e = ju.getTimeRangeForCC(e, i, r); + if (e) { + const s = ju.getTimeRangeForCC(t, i, r); + n = null != s && s.length ? Math.max(s[0], e[0]) : e[0] + } + } + return n + }, + discoSeqNumForTime(e, t, i = 0) { + i = Ku.search(e, Hu.fragmentWithinToleranceTest.bind(null, t, i)); + return null == i ? void 0 : i.discoSeqNum + } + }; + var Qu = ju; + const Wu = $i(void 0); + + function Gu(e, t, i = 1) { + return e.pipe(ln(t), Ds(i)) + } + + function zu(e, t) { + var i = e.timeline; + return { + seconds: (e.seconds - i.rootTimeSeconds) / i.rate * t.rate + t.rootTimeSeconds, + timeline: t + } + } + class Xu { + constructor() { + this._timeline = null + } + get forward() { + var e; + return 0 < (null === (e = this.timeline) || void 0 === e ? void 0 : e.rate) + } + get isStarted() { + return Boolean(this.hostClock) + } + start(e) { + this.stopTime = null, this._timeline = Object.assign({}, e); + const t = { + rootTimeSeconds: performance.now() / 1e3, + rate: 1 + }; + this.hostClock = { + timeline: t, + getCurrentTime: () => ({ + seconds: performance.now() / 1e3, + timeline: t + }) + } + } + pause() { + this.isStarted && (this.stopTime = this.getCurrentTime()) + } + stop() { + this.pause(), this.hostClock = null, this._timeline = null + } + get timeline() { + return this._timeline + } + getCurrentTime() { + return this.stopTime || zu(this.hostClock.getCurrentTime(), this.timeline) + } + } + class Yu { + constructor(e) { + this._timeline = e, this.mediaRootTime = this.lastTimeSeconds = e.rootTimeSeconds + } + get timeline() { + return Object.assign({}, this._timeline) + } + getCurrentTime() { + return { + seconds: this.lastTimeSeconds, + timeline: Object.assign({}, this._timeline) + } + } + } + const Ju = { + name: "ifm" + }; + class Zu { + constructor(e, t) { + this.config = e, this.logger = t, this.scaledFragments = [], this.iframeClock = new Xu, this.hasMore$ = new yi(!0), this.logger = t.child(Ju) + } + destroy() { + this.stop() + } + resetScaledSegments() { + this.scaledFragments = [] + } + get anchorFrag() { + return this.scaledFragments.length ? this.scaledFragments[0] : null + } + get lastFrag() { + return this.scaledFragments.length ? this.scaledFragments.slice(-1)[0] : null + } + get isStarted() { + return Boolean(this.iframeClock.isStarted) + } + get iframeRate() { + var e; + return null !== (e = null === (e = this.iframeClock.timeline) || void 0 === e ? void 0 : e.rate) && void 0 !== e ? e : 0 + } + get iframeClockTimeSeconds() { + return Math.max(0, this.iframeClock.getCurrentTime().seconds) + } + get mediaAppendClockTimeSeconds() { + var e; + return null !== (e = null === (e = this.mediaAppendClock) || void 0 === e ? void 0 : e.getCurrentTime().seconds) && void 0 !== e ? e : 0 + } + get mediaRootTime() { + var e; + return null === (e = this.mediaAppendClock) || void 0 === e ? void 0 : e.mediaRootTime + } + pause() { + this.iframeClock.pause() + } + stop() { + this.hasMore$.next(!0), this.iframeClock.stop(), this.mediaAppendClock = null, this.resetScaledSegments() + } + checkHasMore() { + this.hasMore$.next(!0) + } + startClocksAndGetFirstFragment(e, t, i, r, n) { + let s = Ku.search(e, Hu.fragmentWithinToleranceTest.bind(null, r, 1e-4)); + if (!s && r >= e[e.length - 1].start && (s = e[e.length - 1]), !s) return this.logger.error(`startClocksAndGetFirstFragment => no anchorFrag for time ${r}`), this.hasMore$.next(!1), null; + var a = ne(n) ? n : s.discoSeqNum, + a = Qu.getMinTimeForCC(e, t, a, this.logger), + r = ne(n) && 1 < i ? a : r, + a = 1 < i ? Math.ceil(r) : Math.ceil(a); + return this.iframeClock.start({ + rootTimeSeconds: r, + rate: i + }), this.mediaAppendClock = new Yu({ + rootTimeSeconds: a, + rate: 1 + }), { + frag: s, + newMediaRootTime: a + } + } + getNextFragment(e, t, i) { + const r = this.lastFrag, + { + iframeClock: n, + mediaAppendClock: s + } = this, + a = n.timeline; + s.lastTimeSeconds = t; + var o = n.forward ? 1 : -1; + let l, d = Math.max(0, Math.min(r.mediaSeqNum - e[0].mediaSeqNum + o, e.length - 1)); + if (r.mediaSeqNum !== e[d].mediaSeqNum) + do { + l = e[d]; + const i = n.forward ? l.start : l.start + l.duration; + if (zu({ + seconds: i, + timeline: a + }, s.timeline).seconds >= t && (n.forward && i >= this.iframeClockTimeSeconds || !n.forward && i <= this.iframeClockTimeSeconds)) break + } while (d += o, 0 < d && d < e.length); + return l || (this.logger.error(`getNextFragment(bufferEnd: ${t}) => no more frags, but we should have found an end fragment, setting hasMore to false`), this.hasMore$.next(!1)), { + frag: l + } + } + nextFragment(e, t, i, r) { + let n, s; + if (this.isStarted && i !== this.iframeRate && (s = this.iframeClockTimeSeconds, this.stop()), this.isStarted) { + if (n = this.getNextFragment(e, r, i), !n.frag) return; + if (n.frag.discoSeqNum !== this.anchorFrag.discoSeqNum) { + const a = this.iframeClockTimeSeconds; + this.stop(); + const s = this.startClocksAndGetFirstFragment(e, t, i, a, n.frag.discoSeqNum)["newMediaRootTime"]; + n.newMediaRootTime = s + } + } else if (n = this.startClocksAndGetFirstFragment(e, t, i, null != s ? s : r), !n) return; + var r = n["frag"], + r = this.handleNextFrag(e, r); + return Object.assign(Object.assign({}, n), { + frag: r + }) + } + handleNextFrag(e, t) { + const i = Object.assign(Object.assign({}, t), { + iframeMediaStart: NaN, + iframeMediaDuration: NaN + }), + { + mediaAppendClock: r, + iframeClock: n + } = this; + this.iframeClockBounds = Qu.getTimeRangeDictForCC(e, i.discoSeqNum, this.logger); + var s = r.getCurrentTime().seconds, + t = n.timeline.rate; + return ne(i.iframeOriginalStart) || (i.iframeOriginalStart = i.start), i.start = i.iframeMediaStart = s, i.iframeMediaDuration = Math.max(i.duration / Math.abs(t), 1 / this.config.minIframeDuration), this.scaledFragments.push(i), this.isEndFrag(e, i) && this.hasMore$.next(!1), i + } + get hasMore() { + return this.hasMore$.value + } + handleWaitForMore() { + return this.isStarted ? (this.iframeClock.getCurrentTime(), Gu(this.hasMore$, e => !0 === e).pipe(Za(() => {}))) : Wu + } + isEndFrag(e, t) { + var i = e[0], + r = e[e.length - 1], + e = this.iframeClock.forward; + return !e && (null == i ? void 0 : i.mediaSeqNum) === t.mediaSeqNum || e && (null == r ? void 0 : r.mediaSeqNum) === t.mediaSeqNum + } + } + + function ec(e, t) { + for (var i = 0; i < t.length; i++) { + var r = t[i]; + r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r) + } + } + + function tc(t) { + return function(e) { + return e.lift(new nc(t)) + } + } + var ic, rc, nc = (ec(sc.prototype, [{ + key: "call", + value: function(e, t) { + return t.subscribe(e) + } + }]), sc); + + function sc(e) { + ! function(e) { + if (!(e instanceof sc)) throw new TypeError("Cannot call a class as a function") + }(this), "tag" in this ? Object.defineProperty(this, "tag", { + value: void 0, + enumerable: !0, + configurable: !0, + writable: !0 + }) : this.tag = void 0, this.tag = e + } + + function ac(e) { + const { + method: t, + isEncrypted: i, + uri: r, + format: n, + formatversions: s + } = e, a = { + method: t, + isEncrypted: i, + uri: r, + format: n, + formatversions: s, + iv: e.ivBuf ? new Uint8Array(e.ivBuf) : null + }; + return e.keyIdBuf && (a.keyId = new Uint8Array(e.keyIdBuf)), e.keyBuf && (a.key = new Uint8Array(e.keyBuf)), e.psshBuf && (a.pssh = new Uint8Array(e.psshBuf)), a + }(iu = ic = ic || {}).MustRequestResponse = "MustRequestResponse", iu.WaitingForKeyResponse = "WaitingForKeyResponse", iu.GotKeyResponse = "GotKeyResponse"; + class oc extends Error { + constructor(e, t) { + super(e), this.code = t + } + } + class lc extends p { + constructor(e, t, i, r, n, s) { + super(o, e, t, i, n), this.code = r, this.isTimeout = s, this.response = n + } + } + class dc extends lc { + constructor(e, t, i, r, n) { + super(n ? "manifestLoadTimeOut" : "manifestLoadError", e, t, i, r, n) + } + } + class uc extends lc { + constructor(e, t, i, r, n, s, a, o) { + switch (super("", e, t, i, r, n), this.mediaOptionType = s, this.mediaOptionId = a, this.url = o, s) { + case gu.Variant: + this.details = n ? "levelLoadTimeOut" : N; + break; + case gu.AltAudio: + this.details = n ? "audioTrackLoadTimeOut" : "audioTrackLoadError"; + break; + case gu.Subtitle: + this.details = n ? "subtitleTrackLoadTimeout" : "subtitleTrackLoadError" + } + } + } + class cc extends lc { + constructor(e, t, i, r) { + super("sessionDataLoadError", e, t, i, r, !1) + } + } + class hc extends lc { + constructor(e, t, i, r, n, s, a) { + super(n ? "fragLoadTimeOut" : "fragLoadError", e, t, i, r, n), this.mediaOptionId = s.mediaOptionId, this.mediaOptionType = s.mediaOptionType, this.stats = a + } + } + class pc extends dr { + constructor(e, t, i) { + super(), this.message = e, this.code = t, this.stats = i + } + } + class fc extends lc { + constructor(e, t, i) { + super("fragAbortError", !1, "Fragment abort", 0, i, !1), this.candidateMediaOptionId = t, this.mediaOptionId = e.mediaOptionId, this.mediaOptionType = e.mediaOptionType + } + } + class mc extends lc { + constructor(e, t, i, r = []) { + super("keyLoadTimeOut", !1, e, i.code, i, !0), this.keyuri = t, this.response = i, this.mediaOptionIds = r + } + }(ru = rc = rc || {})[ru.InvalidState = 0] = "InvalidState", ru[ru.Abort = 1] = "Abort", ru[ru.OutputRestricted = 2] = "OutputRestricted", ru[ru.AlreadyFailedKey = 3] = "AlreadyFailedKey", ru[ru.HttpError = 4] = "HttpError", ru[ru.InternalError = 5] = "InternalError", ru[ru.LicenseServerError = 6] = "LicenseServerError", ru[ru.InsufficientCPC = 7] = "InsufficientCPC"; + class gc extends lc { + constructor(e, t, i, r, n, s, a = !1, o = []) { + super("keyLoadError", a, e, i, r, !1), this.keyuri = t, this.isOkToRetry = n, this.keyErrorReason = s, this.mediaOptionIds = o + } + } + class yc extends p { + constructor(e, t, i, r, n) { + super(s, "keySystemGenericError", !0, e, r), this.keyuri = t, this.code = i, this.response = r, this.keysystemstring = n + } + } + + function vc(e, t) { + return e instanceof gc ? new gc(e.message, e.keyuri, e.code, e.response, e.isOkToRetry, e.keyErrorReason, e.fatal, t) : e instanceof mc ? new mc(e.message, e.keyuri, $.CryptResponseReceivedSlowly, t) : e ? new V(e.fatal, e.reason, $.InternalError) : null + } + const Sc = { + id: "fairplaystreaming", + systemStringPrefix: "com.apple.fps", + keyFormatString: "com.apple.streamingkeydelivery", + securityLevels: { + AppleBaseline: 0, + AppleMain: 1, + Main: 1, + Baseline: 0 + } + }; + class bc extends kl { + constructor(e) { + super(e), this.store = e + } + get unresolvedUriLoading$() { + return this.selectEntityAction(Eo.Add).pipe(hr(e => e.map(e => this.getEntity(e)))) + } + } + class Tc { + constructor(e) { + this.store = e + } + createUnresolvedUriLoading(e, t, i) { + Do("loader.create.unresolvedUriLoading"), this.store.add({ + uri: e, + responseType: t, + userAgent: i + }) + } + removeUnresolvedUriLoading(e) { + Do("loader.remove.unresolvedUriLoading"), this.store.remove(e) + } + } + const Ec = new class extends fl { + constructor() { + super({}, { + name: "loader", + producerFn: su, + idKey: "uri" + }) + } + }; + let Ic = null; + class wc extends t { + trigger(e, t) { + try { + this.emit(e, e, t), "hlsFragLoadProgress" !== e.toString() && ("hlsInternalError" !== e && "hlsError" !== e || !t.length || JSON.stringify(t[0], ["fatal", "details", "reason"]), e.toString()) + } catch (t) { + Qe().warn(`error in event listener for ${e}: ${t.message}`) + } + } + } + class Ac { + constructor(e, t) { + this.target = e, this._this = t + } + eventWithOptions(e, t, i, r = this._this) { + let n = rn(this.target, e, t); + return this.target instanceof wc && (n = n.pipe(hr(([, e]) => e))), i && (r && (i = i.bind(r)), n = n.pipe(Za(i))), n + } + event(e, t, i = this._this) { + return this.eventWithOptions(e, void 0, t, i) + } + listen(e, t, i, r = this._this) { + return this.event(e, i, r).pipe(Va(t)).subscribe() + } + } + + function Oc(e, t) { + return new Ac(e, t) + } + + function kc(e, t, i) { + var r; + return { + currentTarget: null !== (r = null == i ? void 0 : i.currentTarget) && void 0 !== r ? r : e, + target: null !== (i = null == i ? void 0 : i.target) && void 0 !== i ? i : e, + type: t + } + } + + function Cc(y, v) { + return new $t(e => { + const { + maxTimeToFirstByteMs: t, + maxLoadTimeMs: i + } = v, r = new XMLHttpRequest, n = { + trequest: performance.now(), + tfirst: NaN, + tload: NaN, + loaded: 0, + total: NaN, + complete: !1 + }, s = Oc(r), a = s.event("progress").pipe(Aa(), ao(300, Ti, { + leading: !0, + trailing: !0 + }), hr(e => (isNaN(n.tfirst) && (n.tfirst = performance.now()), n.loaded = e.loaded, e.lengthComputable && (n.total = e.total), e.target)), ln(e => 3 <= e.readyState)), o = s.event("readystatechange").pipe(Aa(), hr(e => e.target), ln(e => 2 <= e.readyState), Za(e => { + isNaN(n.tfirst) && 3 <= e.readyState && (n.tfirst = performance.now()) + })); + let l = Ii; + isFinite(t) && 0 < t && (l = an(a, o).pipe(Ds(1), So(0 < y.extendMaxTTFB ? y.extendMaxTTFB : t), La(() => Ii))); + let d = Ii; + isFinite(i) && 0 < i && (d = o.pipe(ln(e => 4 <= e.readyState), Ds(1), So(i), La(() => Ii))); + let u = Ii; + y.onProgress && (u = an($i(r), a).pipe(hr(e => { + const { + getData: t, + cb: i + } = y.onProgress; + return i(y.url, e.status, n, t ? e.response : void 0) + }), Wa(e => !e, !0)).pipe($a(Ii))); + const c = an(a.pipe(La(() => Ii)), o, l, d, u).pipe(ln(e => 4 <= e.readyState), Ds(1), La(e => { + if (n.complete = !0, 200 <= e.status && e.status < 300) { + if (n.tload = performance.now(), n.contentType = e.getResponseHeader("Content-Type"), v.reportCDNServer && (n.cdnServer = e.getResponseHeader("CDN-Server")), n.contentLength = v.forceContentLenCheckIfNoHeader ? function(e) { + let t; + const i = e.getResponseHeader("Content-Encoding"), + r = e.getResponseHeader("Transfer-Encoding"), + n = !i || i && "identity" === i.toLowerCase(), + s = !r || r && "identity" === r.toLowerCase(); + return n && s && (t = function(e) { + e = /([0-9]+)\-([0-9]+)\/([0-9]+)/.exec(e); + return e ? parseInt(e[2]) - parseInt(e[1]) + 1 : void 0 + }(e.getResponseHeader("Content-Range")), ne(t) || (t = parseInt(e.getResponseHeader("Content-Length")))), t + }(e) : null, i = e, (r = y).collectServerInstanceInfo && (r.serverInstanceInfo = {}, r.collectServerInstanceInfo.forEach(e => { + var t = i.getResponseHeader(e); + t && (r.serverInstanceInfo[e] = t) + })), "arraybuffer" === y.responseType ? n.loaded = e.response.byteLength : n.loaded = e.responseText.length, n.total = n.loaded, y.checkContentLength && (0 === n.total || ne(n.contentLength) && n.total != n.contentLength)) throw new oc("Network error", e.status); + return Nr($i([e, n]), Ti) + } + var i, r; + throw new oc("Network error", e.status) + }), Vn(e => { + if (e instanceof dr) throw new pc(e.message, 0, n); + if (!(e instanceof oc)) throw new oc(e.message, 0); + throw e + })).subscribe(e), + { + url: h, + method: p, + byteRangeOffset: f, + responseType: m, + body: g + } = y; + y.mimeType && r.overrideMimeType(y.mimeType); + try { + const v = y.xhrSetup; + if (v) try { + v(r, h) + } catch (e) { + r.open(p, y.url, !0), v(r, y.url) + } + r.readyState || r.open(p, y.url, !0) + } catch (e) { + throw new oc(e.message, r.status) + } + if (r.responseType = m, f && ne(f.start) && ne(f.end) && 0 <= f.start && f.end > f.start) { + const { + start: y, + end: v + } = f; + r.setRequestHeader("Range", `bytes=${y}-${v-1}`) + } + if (y.headers) + for (const [v, e] of Object.entries(y.headers)) r.setRequestHeader(v, e); + return "POST" === p && g ? r.send(g) : r.send(), () => { + r.abort(), c.unsubscribe() + } + }) + } + const Dc = { + name: "CustomUrlLoader" + }; + class Mc { + constructor(e) { + this.loaderService = e, this.requestMap = {}, this.logger = Qe() + } + load(a, o) { + return new $t(e => { + const t = a.url, + i = o["maxTimeToFirstByteMs"], + r = { + trequest: performance.now(), + tfirst: NaN, + tload: NaN, + loaded: 0, + total: NaN, + complete: !1 + }, + n = (this.requestMap[t] = new er).pipe(So(0 < a.extendMaxTTFB ? a.extendMaxTTFB : i), La(e => (r.tfirst = performance.now(), this.handleExternalResponse(e, a, o, r))), Vn(e => { + if (e instanceof dr) throw new pc(e.message, 0, r); + throw e + }), Vs(() => { + this.requestMap[t] = void 0, this.loaderService.removeUnresolvedUriLoading(t) + })); + a.onProgress && a.onProgress.cb(t, 0, r, void 0); + const s = n.subscribe(e); + return this.loaderService.createUnresolvedUriLoading(t, a.responseType, navigator.userAgent), () => { + s.unsubscribe(), this.requestMap[t] = void 0 + } + }) + } + setCustomUrlResponse(e, t) { + const i = this.requestMap[e]; + i && (i.next(t), i.complete(), this.requestMap[e] = void 0) + } + handleExternalResponse(e, t, i, r) { + r.tload = performance.now(); + var n = e.response.status || 200; + return 200 <= n && n < 300 ? ("arraybuffer" === t.responseType && e.response.data instanceof ArrayBuffer ? r.loaded = e.response.data.byteLength : r.loaded = e.response.data.toString().length, r.total = r.loaded, r.complete = !0, Nr($i({ + status: n, + data: e, + stats: r + }), Ti)) : 300 === n || 302 === n || 303 === n || 305 === n ? this.redirectRequest(e.response.uri, t, i, r) : (this.logger.warn(Dc, `unable to load custom url > uri=${le(e.response.uri)}, status=${n}`), Vi(new oc("Unable to load custom url", n))) + } + redirectRequest(e, t, i, n) { + var { + maxLoadTimeMs: r, + maxTimeToFirstByteMs: s + } = i, r = r - (performance.now() - n.trequest), s = 0 < t.extendMaxTTFB ? t.extendMaxTTFB : s - (performance.now() - n.trequest), i = Object.assign(Object.assign({}, i), { + maxLoadTimeMs: r, + maxTimeToFirstByteMs: s + }), e = Object.assign(Object.assign({}, t), { + url: e + }); + return r <= 0 || s <= 0 ? Vi(new dr) : Cc(e, i).pipe(hr(([e, t]) => { + var { + responseURL: i, + status: r + } = e, i = i || "", i = { + uri: i, + response: { + status: r, + uri: i, + data: e.response + } + }; + return n.loaded = n.total = t.loaded, n.tload = performance.now(), n.complete = !0, { + status: e.status, + data: i, + stats: n + } + })) + } + } + let xc; + + function Pc(e) { + return xc || (e = e || (Ic = Ic || new Tc(Ec), Ic), xc = new Mc(e)), xc + } + + function Rc(e, t) { + const i = Object.assign(Object.assign({}, e), { + method: "GET", + responseType: "arraybuffer" + }), + r = Pc(); + return Tu(i.url) ? r.load(i, t).pipe(hr(e => [e.data.response.data, e.stats, i.serverInstanceInfo])) : Cc(i, t).pipe(hr(([e, t]) => [e.response, t, i.serverInstanceInfo])) + } + + function Lc(e, t) { + return !e.url || Tu(e.url) ? t.customURL : t.default + } + + function _c(e, t) { + return e instanceof lc ? e.isTimeout ? t.timeoutRetry : t.errorRetry : null + } + + function Nc(e) { + var t = e.type, + i = e.liveOrEvent; + let r = "VOD"; + "EVENT" === t && i ? r = "EVENT" : t && 0 !== t.length && "LIVE" !== t || !i || (r = "LIVE"), e.type !== r && (e.type = r) + } + const Fc = { + id: "playready", + systemStringPrefix: "com.microsoft.playready", + keyFormatString: "com.microsoft.playready", + securityLevels: { + SL2000: 0, + SL3000: 1 + } + }, + Bc = { + id: "widevine", + systemStringPrefix: "com.widevine.alpha", + keyFormatString: "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", + securityLevels: { + WIDEVINE_SOFTWARE: 0, + WIDEVINE_HARDWARE: 1 + } + }; + + function Uc(e) { + return e.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=+$/, "") + } + class $c { + static strToBase64Encode(e) { + return btoa(e) + } + static base64DecodeToStr(e) { + return atob(e) + } + static base64Encode(e) { + return btoa(String.fromCharCode(...e)) + } + static base64UrlEncode(e) { + return Uc($c.base64Encode(e)) + } + static base64Decode(e) { + return Uint8Array.from(atob(e), e => e.charCodeAt(0)) + } + } + class Vc { + static strToBase64Encode(e) { + return l.Buffer.from(e).toString("base64") + } + static base64DecodeToStr(e) { + return l.Buffer.from(e, "base64").toString() + } + static base64Encode(e) { + return l.Buffer.from(e).toString("base64") + } + static base64UrlEncode(e) { + return Uc(Vc.base64Encode(e)) + } + static base64Decode(e) { + e = l.Buffer.from(e, "base64"); + return new Uint8Array(e.buffer, e.byteOffset, e.byteLength) + } + } + var Kc, qc = void 0 !== l.Buffer ? Vc : $c; + const Hc = { + avc1: "video/mp4", + avc3: "video/mp4", + dvav: "video/mp4", + dva1: "video/mp4", + hev1: "video/mp4", + hvc1: "video/mp4", + dvh1: "video/mp4", + dvhe: "video/mp4" + }, + jc = { + mp4a: "audio/mp4", + "ac-3": "audio/mp4", + "ec-3": "audio/mp4" + }; + + function Qc(e) { + const t = O.strToUtf8array(e).subarray(0, 16), + i = new Uint8Array(16); + return i.set(t, 16 - t.length), i + } + const Wc = { + getCapabilities: function(e, t) { + const i = { + videoCapabilities: [], + audioCapabilities: [] + }; + return e && e.forEach(e => { + var t = e.split(".")[0].trim(); + t in Hc && i.videoCapabilities.push({ + contentType: Hc[t] + ";codecs=" + e, + robustness: "" + }) + }), t && t.forEach(e => { + var t = e.split(".")[0].trim(); + t in jc && i.audioCapabilities.push({ + contentType: jc[t] + ";codecs=" + e, + robustness: "" + }) + }), i + }, + changeEndianness: function(e) { + function t(e, t, i) { + var r = e[t]; + e[t] = e[i], e[i] = r + } + t(e, 0, 3), t(e, 1, 2), t(e, 4, 5), t(e, 6, 7) + }, + getKeyIdBytes: Qc, + convertDataUriToArrayBytes: function(e) { + const t = e.split(":"); + let i = null; + if ("data" === t[0] && 2 === t.length) { + const e = t[1].split(";"), + r = e[e.length - 1].split(","); + if (2 === r.length) { + const t = "base64" === r[0], + n = r[1]; + i = t ? (e.splice(-1, 1), qc.base64Decode(n)) : Qc(n) + } + } + return i + }, + makeKeyIdsInitData: function(e) { + e = { + kids: e.map(qc.base64UrlEncode) + }; + return O.strToUtf8array(JSON.stringify(e)) + }, + parsePSSHList: function(e) { + const i = new DataView(e); + let r = 0; + const n = {}; + for (; r < i.buffer.byteLength;) { + const e = r, + t = i.getUint32(r); + r += 4; + var s = e + t; + if (1886614376 === i.getUint32(r)) { + switch (r += 4, i.getUint8(r)) { + case 0: + case 1: + r += 1; + break; + default: + r = s + } + r += 3; + let t = ""; + for (let e = 0; e < 16; ++e, ++r) switch (t += i.getUint8(r).toString(16), e) { + case 4: + case 6: + case 8: + case 10: + t += "-" + } + r += 4, n[t] = i.buffer.slice(e, s) + } else r = s + } + return n + } + }; + let Gc = {}; + class zc { + constructor(e, t, i, r, n) { + if (this.method = e, this.uri = t, this.iv = i, this.format = r, this.formatversions = n, this.isEncrypted = this.method && "NONE" !== this.method, this.formatversions && 0 !== this.formatversions.length || (this.formatversions = [1]), this.key = void 0, this.keyId = void 0, this.isEncrypted) { + const a = Wc.convertDataUriToArrayBytes(this.uri); + if (a) switch (r) { + case Fc.keyFormatString: { + this.pssh = a; + const e = new Uint16Array(a.buffer, a.byteOffset, a.byteLength / 2), + t = String.fromCharCode.apply(null, Array.from(e)), + i = t.substring(t.indexOf("<"), t.length), + r = (new DOMParser).parseFromString(i, "text/xml").getElementsByTagName("KID")[0]; + if (r) { + var s = null; + if (s = r.childNodes[0] ? r.childNodes[0].nodeValue : r.getAttribute("VALUE")) { + const t = qc.base64Decode(s).subarray(0, 16); + Wc.changeEndianness(t), this.keyId = t + } + } + break + } + case Bc.keyFormatString: + this.pssh = a, 22 <= a.length && (this.keyId = a.subarray(a.length - 22, a.length - 6)); + break; + default: { + let e = a.subarray(0, 16); + if (16 !== e.length) { + const t = new Uint8Array(16); + t.set(e, 16 - e.length), e = t + } + this.keyId = e; + break + } + } + if (!this.keyId || 16 !== this.keyId.byteLength) { + let e = Gc[this.uri]; + if (!e) { + const t = Object.keys(Gc).length % Number.MAX_SAFE_INTEGER; + e = new Uint8Array(16), new DataView(e.buffer, 12, 4).setUint32(0, t), Gc[this.uri] = e + } + this.keyId = e + } + } + } + get keyTagInfo() { + var { + method: e, + isEncrypted: t, + uri: i, + iv: r, + keyId: n, + key: s, + format: a, + formatversions: o + } = this; + return { + method: e, + isEncrypted: t, + uri: i, + iv: r, + keyId: n, + key: s, + format: a, + formatversions: o + } + } + static clearKeyUriToKeyIdMap() { + Gc = {} + } + }(w = Kc = Kc || {}).NONE = "NONE", w.GET_REQUEST_INFO = "GET_REQUEST_INFO", w.GET_CHALLENGE = "GET_CHALLENGE", w.GET_KEY_RESPONSE = "GET_KEY_RESPONSE", w.PROCESS_LICENSE = "PROCESS_LICENSE"; + class Xc { + constructor(e, t, i, r) { + this.session = e, this.onkeystatuseschange = t, this.onkeymessage = i, this.logger = r, this.isClosing$ = new yi(!1), this.closed$ = new yi(!1); + const n = Oc(this.session); + n.listen("keystatuseschange", this.isClosing$.pipe(ln(e => !0 === e)), this.onkeystatuseschange), n.listen("message", this.closed$.pipe(ln(e => !0 === e)), this.onkeymessage) + } + get isClosing() { + return this.isClosing$.value + } + get isClosed() { + return this.closed$.value + } + destroy() { + this.isClosing$.next(!0); + const e = this.session; + return Fr(e.remove().catch(e => {}).then(() => e.close()).catch(e => {})).pipe(Za(() => { + this.isClosing$.next(!1), this.closed$.next(!0) + }), Vs(() => { + this.isClosing$.next(!1), this.closed$.next(!0) + })) + } + } + class Yc { + constructor(e, t = null) { + this.decryptdata = e, this._requestState$ = new yi(Kc.NONE), this.destroy$ = new Xt, this.currentObservable = null, this.session = null, this.oldSessions = [], this.session = t + } + get requestState() { + return this._requestState$.value + } + get onKeyRequestState$() { + return this._requestState$ + } + destroy() { + this.destroy$.next() + } + abort() { + var e; + this.requestState !== Kc.NONE && (e = new gc("Aborted", this.decryptdata.uri, 0, $.KeySystemAbort, !0, rc.Abort), this.error(e)) + } + setKeyRequestState(e) { + if (this.currentObservable) { + const t = new gc(`Unexpected state transition ${this.requestState}->${e}`, this.decryptdata.uri, 0, $.KeySystemUnexpectedStateTransition, !0, rc.InvalidState); + this.error(t) + } + this._requestState$.next(e); + const t = new er; + return e === Kc.NONE ? (t.complete(), this.currentObservable = null) : this.currentObservable = t, t + } + resolveState(e, t) { + if (this.currentObservable) + if (e === this.requestState) + if (t instanceof Error) this.error(t); + else { + const e = this.currentObservable; + this.currentObservable = null, e.next(t), e.complete() + } + else { + const t = new gc(`Unexpected state ${this.requestState} != ${e}`, this.decryptdata.uri, 0, $.KeySystemUnexpectedState, !0, rc.InvalidState); + this.error(t) + } + } + error(e) { + if (this.currentObservable) { + const t = this.currentObservable; + this.currentObservable = null, t.error(e) + } + this.setKeyRequestState(Kc.NONE) + } + } + + function Jc(e) { + return `uri=${le(e.uri)} keyId=${je(e.keyId)}` + } + class Zc { + constructor(e, t, i, r, n, s, a) { + this.mediaKeys = e, this.systemString = t, this.config = i, this.eventEmitter = r, this.useSingleKeySession = n, this.sessionHandler = s, this.logger = a, this.destroy$ = new Xt, this.setCert = !1, this.certificate$ = new yi(null), this._keyStatusChange$ = new Xt, this.shouldDestroyMediaKeys = !1, this.itemId = "", this.sessions = [], this.keyIdToKeyInfo = {}, this.keyUriToKeyInfo = {}, this.sessionIdToKeyUri = {}, this.onkeystatuseschange = this.handleKeyStatusesChange.bind(this), this.onkeymessage = this.handleKeyMessage.bind(this) + } + get keyStatusChange$() { + return this._keyStatusChange$ + } + destroy() { + this.isDestroying = !0, this.destroy$.next(); + for (const e of Object.values(this.keyIdToKeyInfo)) this._abortKeyRequest(e); + const e = this.sessions.map(e => e.destroy()), + t = nn(() => 0 === e.length, Wu, en(e)).pipe(Zs(void 0), Vs(() => { + this.mediaKeys = void 0, this.keyIdToKeyInfo = {}, this.keyUriToKeyInfo = {}, this.sessionIdToKeyUri = {} + })); + return zc.clearKeyUriToKeyIdMap(), t + } + setServerCertificate(e = null) { + return this.needsCert ? (e && this.certificate$.next(e), Gu(this.certificate$, e => null != e).pipe(So(1e4)).pipe(La(e => this.setCert ? $i(!0) : !this.setCertSubject || this.setCertSubject.isStopped ? (this.setCertSubject = new er, Fr(this.mediaKeys.setServerCertificate(e)).pipe(Za(e => { + e = void 0 === e || e; + this.setCert = e, this.setCertSubject.next(e), this.setCertSubject.complete() + }), Vn(e => (this.setCert = !1, this.setCertSubject.error(e), Wu)), La(() => this.setCertSubject))) : this.setCertSubject), Vn(e => { + throw e + }))) : $i(!0) + } + ensureKeyContext(e) { + var t = e.uri; + this.keyUriToKeyInfo[t] || (this.keyUriToKeyInfo[t] = new Yc(e)); + const i = this.keyUriToKeyInfo[t]; + if (i.session) return i; + if (this.useSingleKeySession && 0 < this.sessions.length) return i.session = this.sessions[0].session, i; + t = this.mediaKeys.createSession(); + return t && this.sessions.push(new Xc(t, this.onkeystatuseschange, this.onkeymessage, this.logger)), i.session = t, i + } + startKeyRequest(t) { + const i = t.uri, + r = this.ensureKeyContext(t); + if (null == r || !r.session) return Vi(new yc("Could not create key session", t.uri, 0, $.KeySystemFailedToCreateSession, this.systemString)); + var e = O.utf8arrayToStr(t.keyId); + return this.keyIdToKeyInfo[e] = r, en([this.getKeyRequestInfo(r), this.setServerCertificate()]).pipe(hr(e => e[0]), La(e => { + var t; + return null === (t = this.sessionHandler) || void 0 === t || t.licenseChallengeSubmitted({ + keyuri: i, + keyFormat: this.systemString + }), this.generateLicenseChallenge(r, e).pipe(Vn(e => { + var t; + throw null === (t = this.sessionHandler) || void 0 === t || t.licenseChallengeError({ + keyuri: i + }), e + })) + }), La(e => { + var t; + return null === (t = this.sessionHandler) || void 0 === t || t.licenseChallengeCreated({ + keyuri: i, + cdmVersion: this.cdmVersion + }), this.getKeyRequestResponse(r, e) + }), La(e => { + var t; + return null === (t = this.sessionHandler) || void 0 === t || t.licenseResponseSubmitted({ + keyuri: i + }), this.handleParsedKeyResponse(r, e).pipe(Vn(e => { + var t; + throw null === (t = this.sessionHandler) || void 0 === t || t.licenseResponseError({ + keyuri: i + }), e + })) + }), hr(() => { + var e; + return null === (e = this.sessionHandler) || void 0 === e || e.licenseResponseProcessed({ + keyuri: i + }), r.setKeyRequestState(Kc.NONE), this.removeSessions(r.decryptdata, !1).subscribe(), t + }), Vn(e => { + throw this.handleKeyExchangeError(r, e), e + }), Vs(() => { + this._abortKeyRequest(r) + })) + } + _abortKeyRequest(e) { + var t, i; + e && (i = e.decryptdata.uri, O.utf8arrayToStr(e.decryptdata.keyId), e.requestState !== Kc.NONE && (null === (t = this.sessionHandler) || void 0 === t || t.keyAborted({ + keyuri: i + })), e.abort()) + } + handleKeyExchangeError(e, t) { + e.error(t), O.utf8arrayToStr(e.decryptdata.keyId) + } + updateItemId(e) { + this.itemId = e + } + removeKey(e) { + return this.removeKeyInternal(e) + } + removeKeyInternal(e) { + const t = this.keyUriToKeyInfo[e.uri]; + if (t && t.session) { + var i = t.session; + t.abort(), t.destroy(); + var r = O.utf8arrayToStr(e.keyId); + return this.keyIdToKeyInfo[r] = void 0, this.keyUriToKeyInfo[e.uri] = void 0, this.removeSession(i) + } + } + removeSessions(e, t) { + const i = this.keyUriToKeyInfo[e.uri]; + if (i) { + const r = i.oldSessions.map(e => this.removeSession(e)); + return i.oldSessions = [], nn(() => 0 === r.length, Wu, en(r)).pipe(La(() => t ? this.removeKeyInternal(e) : Wu)) + } + return Wu + } + removeSession(t) { + const e = this.sessions.findIndex(e => e.session === t), + i = this.sessions[e]; + return -1 < e && this.sessions.splice(e, 1), this.sessionIdToKeyUri[t.sessionId] = void 0, i ? i.destroy() : Wu + } + getKeyRequestInfo(e) { + var t = e.decryptdata, + i = t.uri, + e = e.setKeyRequestState(Kc.GET_REQUEST_INFO); + return this.eventEmitter.trigger(x.KEY_REQUEST_STARTED, { + keyuri: i, + decryptdata: t, + timestamp: Date.now() + }), e + } + setKeyRequestInfo(e, t) { + const i = this.keyUriToKeyInfo[e]; + i && i.resolveState(Kc.GET_REQUEST_INFO, t) + } + sanitizeRequest(e) { + return e + } + generateLicenseChallengeInternal(t, e, i) { + const r = t.decryptdata, + n = t.session, + s = r.uri, + a = r.keyId; + let o; + var l = t.setKeyRequestState(Kc.GET_CHALLENGE); + if (n.generateRequestPromise) o = Nr(n.generateRequestPromise, Ti).pipe(La(() => this.generateRequestInitialized(t, "usable" === i))); + else { + const i = this.generateInitData(a, r, e); + n.generateRequestPromise = n.generateRequest(i.initDataType, i.initData), o = Nr(n.generateRequestPromise, Ti).pipe(Za(() => { + this.sessionIdToKeyUri[n.sessionId] = s + }), Vn(e => { + throw new yc(e.message, t.decryptdata.uri, 0, $.KeySystemFailedToGenerateLicenseRequest, this.systemString) + })) + } + return en([l, o]).pipe(hr(e => new Uint8Array(e[0]))) + } + generateLicenseChallenge(e, t) { + const i = e.decryptdata, + r = e.session, + n = i.uri, + s = i.keyId; + let a, o; + if (O.utf8arrayToStr(i.keyId), e.licenseChallenge && e.resolveState(Kc.GET_CHALLENGE, e.licenseChallenge), t = this.sanitizeRequest(t), e.requestInfo = t, this.sessionId = this.sessionId || t && t.sessionId || this.itemId, this.systemString === Fc.systemStringPrefix) { + const e = new Uint8Array(s); + Wc.changeEndianness(e), a = r.keyStatuses.get(e) + } else a = r.keyStatuses.get(s); + switch (a) { + case "status-pending": + case "usable": + case "expired": + case void 0: + o = this.generateLicenseChallengeInternal(e, t, a); + break; + default: + o = Vi(new yc(`Bad internal state state=${a}`, n, 0, $.KeySystemUnexpectedState, this.systemString)) + } + return o + } + setParsedResponse(e, t) { + const i = this.keyUriToKeyInfo[e]; + i && i.resolveState(Kc.GET_KEY_RESPONSE, t) + } + handleKeyStatusesChange(e) { + e.target.keyStatuses.forEach((e, t, i) => { + t = new Uint8Array(t); + this.systemString === Fc.systemStringPrefix && Wc.changeEndianness(t); + t = O.utf8arrayToStr(t), t = this.keyIdToKeyInfo[t]; + t && this.handleKeyStatusForKey(e, t) + }) + } + handleKeyStatusForKey(e, t) { + if (t) { + var i = t.decryptdata, + r = i.uri; + switch (e) { + case "internal-error": + this.logger.error(`${this.systemString} internal-error for key ${Jc(i)}`), this._signalError(t, new gc("Got internal error from key system", r, 0, $.KeySystemInternalError, !1, rc.InternalError)); + break; + case "usable": + t.requestState === Kc.PROCESS_LICENSE && t.resolveState(Kc.PROCESS_LICENSE, void 0); + break; + case "output-restricted": + this.logger.warn(`${this.systemString} output-restricted for key ${Jc(i)}`), t.session && this.removeSession(t.session).pipe(Va(this.destroy$)).subscribe(), this._signalError(t, new gc("output-restricted", r, 0, $.KeySystemOutputRestricted, !1, rc.OutputRestricted)); + break; + case "expired": + this.logger.warn(`${this.systemString} expired for key ${Jc(i)}. Attempting renewal`), this._signalRenewal(t) + } + } + } + _scheduleRenewal(e, t) { + bn(t).pipe(Za(() => this._signalRenewal(e)), Va(fn(e.destroy$, this.destroy$, Gu(e.onKeyRequestState$, e => e === Kc.GET_REQUEST_INFO)))).subscribe() + } + _signalRenewal(e) { + this._keyStatusChange$.next({ + decryptdata: e.decryptdata, + status: "needs-renewal" + }) + } + _signalError(e, t) { + this._keyStatusChange$.next({ + decryptdata: e.decryptdata, + status: "error", + error: t + }), e.error(t) + } + } + const eh = "org.w3.clearkey", + th = { + id: "clearkey", + systemStringPrefix: eh, + keyFormatString: eh, + securityLevels: { + NONE: 0 + } + }, + ih = ["clearkey", "fairplaystreaming", "playready", "widevine"], + rh = { + initDataTypes: ["keyids", "cenc"] + }; + var nh, nu = class extends Zc { + constructor(e, t, i, r, n, s) { + super(e, t, i, r, !1, n, s) + } + static get requestAccessConfig() { + return rh + } + get needsCert() { + return !1 + } + getKeyRequestResponse(e, t) { + return Rc({ + url: e.decryptdata.uri, + xhrSetup: this.config.xhrSetup + }, { + maxLoadTimeMs: 0, + maxTimeToFirstByteMs: 0, + autoRetry: !1, + timeoutRetry: null, + errorRetry: null + }).pipe(hr(([e]) => { + e = new Uint8Array(e); + return { + response: this.parseResponse(t, e) + } + })) + } + parseResponse(e, t) { + t = { + kty: "oct", + kid: qc.base64UrlEncode(e), + k: qc.base64UrlEncode(t) + }; + return O.strToUtf8array(JSON.stringify({ + keys: [t] + })) + } + handleParsedKeyResponse(i, e) { + e = e.response; + return en([i.setKeyRequestState(Kc.PROCESS_LICENSE), Fr(i.session.update(e)).pipe(Za(() => { + var e = i.decryptdata.keyId, + e = i.session.keyStatuses.get(e); + this.handleKeyStatusForKey(e, i) + }), Vn(e => { + var t = { + code: e.code, + text: "Failed to update with key response" + }; + throw new yc(e.message, i.decryptdata.uri, e.code, t, this.systemString) + }))]).pipe(Zs(void 0)) + } + generateInitData(e) { + return { + initData: Wc.makeKeyIdsInitData([e]), + initDataType: "keyids" + } + } + generateRequestInitialized() { + return Wu + } + sanitizeRequest(e) { + return e + } + handleKeyMessage(e) { + if (!this.isDestroying && "license-request" === e.messageType) { + e = new Uint8Array(e.message), e = JSON.parse(O.utf8arrayToStr(e).trim()); + if (1 === e.kids.length) { + const t = qc.base64Decode(e.kids[0]), + i = O.utf8arrayToStr(t), + r = this.keyIdToKeyInfo[i]; + r && r.resolveState(Kc.GET_CHALLENGE, t) + } + } + } + }; + const sh = { + initDataTypes: ["cenc"] + }, + ah = new Uint8Array([148, 206, 134, 251, 7, 255, 79, 67, 173, 184, 147, 210, 250, 150, 140, 162]); + (iu = nh = nh || {})[iu.CENC = 1667591779] = "CENC", iu[iu.CBCS = 1667392371] = "CBCS"; + class oh extends Zc { + constructor(e, t, i, r, n, s, a) { + super(e, t, i, r, n, s, a), this._hasSetRenewal = !1 + } + static get systemId() { + return ah + } + static get requestAccessConfig() { + return sh + } + get needsCert() { + return !0 + } + sanitizeRequest(e) { + return { + assetId: e && e.assetId ? new Uint8Array(e.assetId) : void 0, + ssc: e && e.ssc ? new Uint8Array(e.ssc) : void 0, + sessionId: e && e.sessionId ? e.sessionId : void 0 + } + } + _scheduleRenewal(e, t) { + this.useSingleKeySession ? this._hasSetRenewal || (this._hasSetRenewal = !0, bn(t).pipe(Za(() => { + var e = Object.values(this.keyUriToKeyInfo)[0]; + e && this._signalRenewal(e) + }), Vs(() => { + this._hasSetRenewal = !1 + }), Va(this.destroy$)).subscribe()) : super._scheduleRenewal(e, t) + } + handleParsedKeyResponse(t, e) { + var i = t.decryptdata.uri, + r = e.statusCode, + n = e.ckc && 0 !== e.ckc.byteLength ? e.ckc : e.license && 0 !== e.license.byteLength ? e.license : void 0; + if (0 === r && !n) return Vi(new gc("License request resulted in HTTP Error", i, r, { + code: r, + text: "HTTP Error" + }, !0, rc.HttpError)); + if (0 !== r) return Vi(new gc("License server responded with error", i, r, { + code: r, + text: "Server Error" + }, !1, rc.LicenseServerError)); + if (!n) return Vi(new gc("License server responded with invalid license", i, r, { + code: r, + text: "Invalid license" + }, !1, rc.LicenseServerError)); + const s = e.renewalDate, + a = new Date, + o = s > a ? s.getTime() - a.getTime() : 0; + 0 < o && this._scheduleRenewal(t, o); + const l = this.makeProcessLicenseRequestMessage(t, n, o), + d = t.session, + u = Fr(d.update(l)).pipe(Za(() => { + var e = t.decryptdata.keyId, + e = d.keyStatuses.get(e); + this.handleKeyStatusForKey(e, t) + }), Vn(e => { + throw new yc(e.message, t.decryptdata.uri, 0, $.KeySystemFailedToUpdateSession, this.systemString) + })); + return en([t.setKeyRequestState(Kc.PROCESS_LICENSE), u]).pipe(Zs(void 0)) + } + makeKeyRequests(e) { + const t = []; + for (const i of e) { + const e = i.decryptdata, + r = i.requestInfo, + n = e.keyId; + t.push({ + keyId: n, + assetId: r ? r.assetId : void 0, + ssc: r ? r.ssc : void 0, + versionList: e.formatversions + }) + } + return t + } + getSchemeAndFlags(e) { + return { + scheme: "ISO-23001-7" === e.method ? nh.CENC : nh.CBCS, + flags: 0 + } + } + generateInitData(e, t, i) { + var { + scheme: r, + flags: n + } = this.getSchemeAndFlags(t), i = this.makeKeyRequests([{ + decryptdata: t, + requestInfo: i + }]); + return { + initData: this.makeFpsKeySystemInitData(r, n, i), + initDataType: "cenc" + } + } + generateRequestInitialized(t, e) { + tc(`[Keys] challenge create start uri=${le(t.decryptdata.uri)} versions=${JSON.stringify(t.decryptdata.formatversions)}`); + e = this.makeKeyRequestMessage(t, e); + return e ? Fr(t.session.update(e)).pipe(Za(() => { + t.requestInfo = void 0 + }), Vn(e => { + throw tc(`[Keys] ${this.systemString} FAIL: generateRequestInitialized keyuri=${le(t.decryptdata.uri)} message=${e.message}`), new yc(e.message, t.decryptdata.uri, 0, $.KeySystemFailedToGenerateLicenseRenewal, this.systemString) + })) : Vi(new gc("Unable to generate request using existing keySession", t.decryptdata.uri, 0, $.KeySystemFailedToGenerateLicenseRequest, !0, rc.InvalidState)) + } + getKeyRequestResponse(e, t) { + var i = e.decryptdata.uri, + e = e.setKeyRequestState(Kc.GET_KEY_RESPONSE); + return this.eventEmitter.trigger(x.LICENSE_CHALLENGE_CREATED, { + keyuri: i, + licenseChallenge: t, + keysystem: this.systemString + }), e + } + resolveSPCPromise(e, t) { + e.resolveState(Kc.GET_CHALLENGE, t) + } + } + const lh = {}, + dh = 1919710053; + + function uh(e, t, i) { + if (!(i + 4 > e.byteLength)) { + t = t.getUint32(i); + if (!((i += 4) + t > e.byteLength)) { + e = e.slice(i, i + t); + return { + pos: i += t, + data: e + } + } + } + } + + function ch(t) { + const i = {}, + r = new DataView(t.buffer); + let n = 4; + const s = r.getUint32(n); + n += 4; + for (let e = 0; e < s && n < t.byteLength && !(n + 16 > t.byteLength); ++e) { + const s = t.slice(n, n + 16); + if (n += 16, n + 4 > t.byteLength) break; + var a = uh(t, r, n); + if (!a) break; + n = a.pos, i[O.utf8arrayToStr(s)] = a.data + } + return i + } + + function hh(e, t, i, r) { + var n = r ? r.byteLength : 0; + return t.setUint32(i, n), n && e.set(r, i + 4), i + (4 + n) + } + + function ph(e) { + let t = 4; + for (const i of e) t += 28 + (i.assetId ? i.assetId.byteLength : 0) + (i.ssc ? i.ssc.byteLength : 0) + (i.versionList ? 4 * i.versionList.length : 0); + return t + } + + function fh(e, t, i, r) { + t.setUint32(i, r.length), i += 4; + for (const n of r) + if (e.set(n.keyId, i), i = hh(e, t, i += 16, n.assetId), i = hh(e, t, i, n.ssc), t.setUint32(i, n.versionList ? n.versionList.length : 0), i += 4, n.versionList) + for (const e of n.versionList) t.setUint32(i, e), i += 4; + return i + } + class mh extends oh { + constructor(e, t, i, r, n, s) { + super(e, t, i, r, void 0 === i.useMultipleKeySessions || !i.useMultipleKeySessions, n, s) + } + makeProcessLicenseRequestMessage(e, t, i) { + t = new Uint8Array(t); + return function(e) { + let t = 0; + for (const i of e) t += 24 + i.ckc.byteLength; + let i = 0; + const r = new Uint8Array(8 + t), + n = new DataView(r.buffer); + n.setUint32(0, 1667982195), n.setUint32(4, e.length), i += 8; + for (const t of e) r.set(t.keyId, i), i += 16, n.setUint32(i, t.expirySec), i += 4, i = hh(r, n, i, t.ckc); + return r + }([{ + keyId: e.decryptdata.keyId, + expirySec: i / 1e3, + ckc: t + }]) + } + makeFpsKeySystemInitData(e, t, i) { + i = function(e, t, i) { + var r = ph(i); + const n = new Uint8Array(8 + r), + s = new DataView(n.buffer); + return s.setUint32(0, e), s.setUint32(4, 1 << 24 | 16777215 & t), fh(n, s, 8, i), n + }(e, t, i); + return ge.pssh(mh.systemId, [], i) + } + makeKeyRequestMessage(e) { + return function(e) { + var t = ph(e); + const i = new Uint8Array(4 + t), + r = new DataView(i.buffer); + return r.setUint32(0, 1668442994), fh(i, r, 4, e), i + }([{ + keyId: e.decryptdata.keyId, + assetId: e.requestInfo ? e.requestInfo.assetId : void 0, + ssc: e.requestInfo ? e.requestInfo.ssc : void 0, + versionList: e.decryptdata.formatversions + }]) + } + handleKeyMessage(e) { + if (e.message.byteLength < 4) this.logger.warn("Unexpected message"); + else { + const t = new Uint8Array(e.message), + i = new DataView(e.message), + r = i.getUint32(0); + if (this.isDestroying && r !== dh) this.logger.warn(`In the middle of destroying, ignore command: ${r.toString(16)}`); + else switch (r) { + case 1667592820: + this.logger.warn("Certificate not set!"); + break; + case 1919837559: { + const e = ch(t); + for (const t in e) + if (Object.prototype.hasOwnProperty.call(e, t)) { + const e = this.keyIdToKeyInfo[t]; + e && this._signalRenewal(e) + } break + } + case 1936745331: { + const e = ch(t); + for (const t in e) + if (Object.prototype.hasOwnProperty.call(e, t)) { + const i = this.keyIdToKeyInfo[t], + r = e[t]; + i && r && this.resolveSPCPromise(i, r) + } break + } + case dh: + this._handleLicenseRelease(t); + break; + case 1667525993: { + const e = uh(t, i, 4); + e && (this.cdmVersion = O.utf8arrayToStr(e.data)); + break + } + default: + this.logger.warn(`Unrecognized command:'0x${r.toString(16)}'`) + } + } + } + _handleLicenseRelease(e) { + const t = {}, + i = new DataView(e.buffer); + switch (i.getUint32(4)) { + case 1936946288: + lh, 0, t[lh.SessionId] = this.sessionId; + var r; + if (e.byteLength < 12) break; + t[lh.APIProvider] = i.getUint32(8) === nh.CENC ? "EC396D13-FB13-4993-9D0D-71518ACF3D6F" : "F19BF03B-7470-41A4-9655-86D078307D59", 0; + var n = uh(e, i, 12); + if (!n) break; + if (r = n.pos, t[lh.MovieID] = O.utf8arrayToStr(n.data), !(n = uh(e, i, r))) break; + if (r = n.pos, t[lh.SecureStopSPC] = n.data, !(n = uh(e, i, r))) break; + n.pos, t[lh.SessionLifespanSPC] = n.data + } + this.eventEmitter.trigger(x.LICENSE_RELEASED, { + keysystem: this.systemString, + itemId: this.itemId, + releaseRecord: t + }) + } + } + ru = mh; + const gh = { + fpsd: O.strToUtf8array("fpsd"), + fpsi: O.strToUtf8array("fpsi"), + fpsk: O.strToUtf8array("fpsk"), + fkri: O.strToUtf8array("fkri"), + fkai: O.strToUtf8array("fkai"), + fkcx: O.strToUtf8array("fkcx"), + fkvl: O.strToUtf8array("fkvl") + }; + t = class extends oh { + constructor(e, t, i, r, n, s) { + super(e, t, i, r, !1, n, s), this.sessions = [], this.keyIdToKeyInfo = {}, this.keyUriToKeyInfo = {}, this.sessionIdToKeyUri = {} + } + static get needsCert() { + return !0 + } + handleKeyExchangeError(e, t) { + this.removeKey(e.decryptdata).subscribe(), super.handleKeyExchangeError(e, t) + } + _abortKeyRequest(e) { + return !this.isDestroying && e && e.requestState !== Kc.NONE && this.removeKey(e.decryptdata).subscribe(), super._abortKeyRequest(e) + } + makeFpsKeySystemInitData(e, t, i) { + const r = [gh.fpsd, (n = e, e = t, t = new Uint8Array(4), ge.set32(n, t, 0), ge.box(gh.fpsi, new Uint8Array([0, e >> 16 & 255, e >> 8 & 255, 255 & e]), t))]; + var n; + for (const s of i) r.push(function(t, e, i, r) { + const n = [gh.fpsk], + s = ge.box(gh.fkri, new Uint8Array([0, 0, 0, 0]), t); + if (n.push(s), e && e.byteLength && n.push(ge.box(gh.fkai, e)), i && i.byteLength && n.push(ge.box(gh.fkcx, i)), r && r.length) { + const t = new Uint8Array(4 * r.length); + let e = 0; + for (const i of r) ge.set32(i, t, e), e += 4; + n.push(ge.box(gh.fkvl, t)) + } + return ge.box.apply(null, n) + }(s.keyId, s.assetId, s.ssc, s.versionList)); + i = ge.box.apply(null, r); + return ge.pssh(oh.systemId, null, i) + } + makeKeyRequestMessage(e, t) { + if (t) return O.strToUtf8array("renew") + } + makeProcessLicenseRequestMessage(e, t, i) { + t = JSON.stringify([{ + keyID: qc.base64Encode(e.decryptdata.keyId), + payload: qc.base64Encode(new Uint8Array(t)) + }]); + return O.strToUtf8array(t) + } + handleKeyMessage(e) { + const t = e.target, + i = t.sessionId, + r = e.messageType, + n = this.sessionIdToKeyUri[i]; + let s; + if (n) s = this.keyUriToKeyInfo[n]; + else + for (const e of Object.values(this.keyUriToKeyInfo)) e && e.session === t && (s = e); + if (s) switch (r) { + case "license-request": { + const t = new Uint8Array(e.message), + i = O.utf8arrayToStr(t); + try { + JSON.parse(i).forEach(e => { + var t = qc.base64DecodeToStr(e.keyID), + e = qc.base64Decode(e.payload), + t = this.keyIdToKeyInfo[t]; + t && this.resolveSPCPromise(t, e) + }) + } catch (e) { + this.logger.warn("[Keys] got unexpected license-request format"), this.resolveSPCPromise(s, t) + } + break + } + case "license-renewal": { + const t = new Uint8Array(e.message); + this.resolveSPCPromise(s, t); + break + } + case "license-release": + this._handleLicenseRelease(t); + break; + default: + this.logger.warn(`[Keys] Unexpected messageType ${r}`) + } else this.logger.warn("[Keys] No key associated with session") + } + _handleLicenseRelease(e) { + e.update(O.strToUtf8array("acknowledged")).catch(e => { + this.logger.error(`Promise error: ${e.message}`) + }) + } + }; + const yh = { + initDataTypes: ["cenc"] + }, + vh = new Uint8Array([154, 4, 240, 121, 152, 64, 66, 134, 171, 146, 230, 91, 224, 136, 95, 149]); + class Sh extends Zc { + constructor(e, t, i, r, n, s) { + super(e, t, i, r, !1, n, s), this.shouldDestroyMediaKeys = !0 + } + static get systemId() { + return vh + } + static get requestAccessConfig() { + return yh + } + get needsCert() { + return !1 + } + generateInitData(e, t) { + t = t.pssh; + return { + initData: ge.pssh(Sh.systemId, [], t), + initDataType: "cenc" + } + } + removeKey(e) { + return super.removeSessions(e, !0) + } + ensureKeyContext(e) { + const t = e.uri, + i = this.keyUriToKeyInfo[t]; + return null != i && i.session && (i.oldSessions.push(i.session), i.session = null), super.ensureKeyContext(e) + } + getKeyRequestResponse(e, t) { + var i = e.decryptdata.uri, + r = e.setKeyRequestState(Kc.GET_KEY_RESPONSE); + return this.eventEmitter.trigger(x.LICENSE_CHALLENGE_CREATED, { + keyuri: i, + licenseChallenge: t, + keysystem: this.systemString, + keyId: e.decryptdata.keyId + }), r + } + generateRequestInitialized(e) { + var t = e.licenseChallenge; + return e.requestInfo = void 0, e.resolveState(Kc.GET_CHALLENGE, t), Wu + } + handleParsedKeyResponse(t, e) { + const i = t.decryptdata.uri, + r = e.statusCode; + if (0 !== r) return Vi(new gc("License server responded with error", i, r, { + code: r, + text: "Server error" + }, !1, rc.LicenseServerError)); + if (!e.license || !e.license.byteLength) return Vi(new gc("License server responded with invalid license", i, r, { + code: r, + text: "Invalid license" + }, !1, rc.LicenseServerError)); + if (e.renewalDate) { + const i = e.renewalDate, + r = new Date, + n = i > r ? i.getTime() - r.getTime() : 0; + 0 < n && this._scheduleRenewal(t, n) + } + return en([t.setKeyRequestState(Kc.PROCESS_LICENSE), Fr(t.session.update(e.license)).pipe(Za(() => { + t.resolveState(Kc.PROCESS_LICENSE, void 0) + }), Vn(e => { + throw this.logger.error(`${this.systemString} FAIL: Failed to update with key response message=${e.message}`), new yc(e.message, t.decryptdata.uri, 0, $.KeySystemFailedToUpdateSession, this.systemString) + }))]).pipe(Zs(void 0)) + } + handleKeyMessage(e) { + if (this.isDestroying) this.logger.warn("In the middle of destroying, ignore key message"); + else { + const t = new DOMParser, + i = new("utf16" === this.config.playReadyMessageFormat ? Uint16Array : Uint8Array)(e.message.buffer || e.message), + r = String.fromCharCode.apply(null, Array.from(i)), + n = t.parseFromString(r, "application/xml").getElementsByTagName("PlayReadyKeyMessage")[0]; + if (n && "LicenseAcquisition" === n.getAttribute("type")) { + const s = t.parseFromString(r, "application/xml").getElementsByTagName("Challenge")[0]; + if (s && "base64encoded" === s.getAttribute("encoding") && 0 !== s.childNodes.length) { + const a = qc.base64Decode(s.childNodes[0].nodeValue), + o = O.utf8arrayToStr(a), + l = t.parseFromString(o, "application/xml").getElementsByTagName("KID")[0]; + e = null, e = l.childNodes[0] ? l.childNodes[0].nodeValue : l.getAttribute("VALUE"), e = qc.base64Decode(e).subarray(0, 16); + Wc.changeEndianness(e); + const d = this.keyIdToKeyInfo[O.utf8arrayToStr(e)]; + d && (d.licenseChallenge = a, d.resolveState(Kc.GET_CHALLENGE, a)) + } else this.logger.warn(`${this.systemString} wrong challenge format or empty challenge`) + } else this.logger.warn(`${this.systemString} unrecognized message ignore it`) + } + } + } + w = Sh; + const bh = { + initDataTypes: ["cenc", "keyids"] + }, + Th = new Uint8Array([237, 239, 139, 169, 121, 214, 74, 206, 163, 200, 39, 220, 213, 29, 33, 237]), + Eh = { + clearkey: th, + fairplaystreaming: Sc, + playready: Fc, + widevine: Bc + }, + Ih = { + clearkey: [ + ["org.w3.clearkey", nu] + ], + fairplaystreaming: [ + ["com.apple.fps.3_0", t], + ["com.apple.fps", ru] + ], + playready: [ + ["com.microsoft.playready.recommendation", w] + ], + widevine: [ + ["com.widevine.alpha", class extends Zc { + constructor(e, t, i, r, n, s) { + super(e, t, i, r, !1, n, s), this.shouldDestroyMediaKeys = !0 + } + static get systemId() { + return Th + } + static get requestAccessConfig() { + return bh + } + get needsCert() { + return !0 + } + removeKey(e) { + return super.removeSessions(e, !0) + } + ensureKeyContext(e) { + const t = e.uri, + i = this.keyUriToKeyInfo[t]; + return null != i && i.session && (i.oldSessions.push(i.session), i.session = null), super.ensureKeyContext(e) + } + getKeyRequestResponse(e, t) { + var i = e.decryptdata.uri, + r = e.setKeyRequestState(Kc.GET_KEY_RESPONSE); + return this.eventEmitter.trigger(x.LICENSE_CHALLENGE_CREATED, { + keyuri: i, + licenseChallenge: t, + keysystem: this.systemString, + keyId: e.decryptdata.keyId + }), r + } + handleParsedKeyResponse(t, e) { + t.licenseChallenge = void 0; + const i = t.decryptdata.uri, + r = e.statusCode; + if (0 !== r) return Vi(new gc("License server responded with error", i, r, { + code: r, + text: "Server error" + }, !1, rc.LicenseServerError)); + if (!e.license || !e.license.byteLength) return Vi(new gc("License server responded with invalid license", i, r, { + code: r, + text: "Invalid license" + }, !1, rc.LicenseServerError)); + if (e.renewalDate) { + const i = e.renewalDate, + r = new Date, + n = i > r ? i.getTime() - r.getTime() : 0; + 0 < n && this._scheduleRenewal(t, n) + } + return en([t.setKeyRequestState(Kc.PROCESS_LICENSE), Fr(t.session.update(e.license)).pipe(Za(() => { + var e = t.decryptdata.keyId, + e = t.session.keyStatuses.get(e); + this.handleKeyStatusForKey(e, t) + }), Vn(e => { + throw this.logger.error(`${this.systemString} FAIL: Failed to update with key response code=${e.code} message=${e.message}`), new yc(e.message, t.decryptdata.uri, e.code, { + code: e.code, + text: "Failed to update with key response" + }, this.systemString) + }))]).pipe(Zs(void 0)) + } + generateInitData(e, t) { + return { + initData: t.pssh, + initDataType: "cenc" + } + } + generateRequestInitialized(e) { + var t = e.licenseChallenge; + return e.requestInfo = void 0, e.resolveState(Kc.GET_CHALLENGE, t), Wu + } + handleKeyMessage(i) { + if (this.isDestroying) this.logger.warn("In the middle of destroying, ignore key message"); + else { + const r = i.target; + let e = null, + t = null; + if (r.sessionId in this.sessionIdToKeyUri) e = this.sessionIdToKeyUri[r.sessionId], t = this.keyUriToKeyInfo[e]; + else + for (const [i, n] of Object.entries(this.keyUriToKeyInfo)) + if (n.session === r) { + e = i, t = n; + break + } if (t) switch (i.messageType) { + case "license-request": { + const r = new Uint8Array(i.message); + t.resolveState(Kc.GET_CHALLENGE, r); + break + } + } else this.logger.warn(`${this.systemString} empty keyuri and keyInfo`) + } + } + }] + ] + }, + wh = th.id; + class Ah { + createMediaKeys(t, e, i, r, n) { + let s = Ah.idToMediaKeysInfoMap[t]; + if (!s) { + const a = Wc.getCapabilities(e, i), + o = new er; + Ah.requestKeySystemAccess(t, a, n, r).pipe(La(function(e) { + return Fr((Ah.idToMediaKeysInfoMap[t].keySystemAccess = e).createMediaKeys()) + }), Za(e => { + o.next(e), o.complete() + }), Vn(e => (o.error(new yc(`could not initialize key system: ${e.message}`, void 0, 0, $.KeySystemFailedToInitialize, t)), Ii)), Va(Ah.destroy$)).subscribe(), s = Ah.idToMediaKeysInfoMap[t] = { + mediaKeys$: o, + keySystemAccess: null + } + } + return s.mediaKeys$ + } + destroyMediaKeys() { + Ah.destroy$.next(), Ah.idToMediaKeysInfoMap = {} + } + static getKeySystemIdForDecryptData(e) { + let t; + if (e) { + t = wh; + var i = e.format; + for (const e of ih) { + var r = Eh[e]; + if ((null == r ? void 0 : r.keyFormatString) === i) { + t = e; + break + } + } + } + if (!t) throw Error("No matching key system"); + return t + } + static requestKeySystemAccess(e, t, i, r) { + if ("undefined" == typeof navigator || void 0 === navigator.requestMediaKeySystemAccess) return Vi(new yc("navigator undefined", void 0, 0, $.KeySystemUndefinedNavigator, e)); + const n = Ih[e]; + let s = Vi(new yc("no key systems to try", void 0, 0, $.KeySystemNoKeySystemsToTry, e)); + for (const e of n) { + const n = e[0], + o = e[1], + l = (a = i) && "object" == typeof a ? i : o.requestAccessConfig, + d = [Object.assign({}, l, t)]; + s = s.pipe(Vn(() => Ah.requestKeySystemInternal(n, d, r))) + } + var a; + return s + } + static requestKeySystemInternal(e, t, i) { + return Zr(() => Fr(navigator.requestMediaKeySystemAccess(e, t))) + } + make(e, t, i, r, n, s) { + var a = null === (l = Ah.idToMediaKeysInfoMap[e]) || void 0 === l ? void 0 : l.keySystemAccess; + if (!a) throw new yc(`No keySystemAccess for ${e}`, void 0, 0, $.KeySystemNoKeySystemAccess, e); + let o; + var l = Eh[e].systemStringPrefix; + for (const t of Ih[e]) + if (t[0] === a.keySystem) { + o = t[1]; + break + } if (!o) throw new yc(`No constructor associated with ${e}`, void 0, 0, $.KeySystemNoConstructor, l); + return new o(t, l, i, r, n, s) + } + static get availableKeySystems() { + return Object.keys(Eh) + } + static getKeySystemFormat(e) { + e = Eh[e]; + return e ? e.keyFormatString : "" + } + static getKeySystemSecurityLevel(e) { + e = Eh[e]; + return e ? e.securityLevels : void 0 + } + } + Ah.idToMediaKeysInfoMap = {}, Ah.destroy$ = new Xt; + const Oh = ["avc1.42E01E"], + kh = ["mp4a.40.2"]; + + function Ch(e, t, i) { + if (t) { + if (t instanceof dr) t = new mc("Key request timed out", e, $.KeySystemRequestTimedOut); + else if (t instanceof yc) { + const i = (null == t ? void 0 : t.response) || $.InternalError; + t = new gc(t.message, e, 0, i, !1, rc.InternalError, !0) + } + } else t = new gc("Unknown error from CDM", e, 0, $.KeySystemCDMUnknownError, !1, rc.InternalError); + return (t instanceof gc || t instanceof mc) && (t.mediaOptionIds = [...i]), t + } + class Dh { + constructor(e, t, i, r, n, s, a, o = new Ah) { + this.ksService = e, this.mediaSink = t, this.config = i, this.platformQuery = r, this.eventEmitter = n, this.sessionHandler = s, this.keySystemFactory = o, this.reset$ = new Xt, this.keyRequest$ = new Xt, this.abort$ = new Xt, this.keySystem$ = new yi(null), this._keyStatusChange$ = new Xt, this.protectionData = {}, this.keySystemId = null, this.keyUriToRequest = {}, this.ksQuery = e.getQuery(), this.logger = a.child({ + name: "eme" + }), this.config.warmupCdms && this.keySystemFactory.createMediaKeys(Sc.id, Oh, kh, this.logger, void 0).subscribe(), an(r.platformInfo$.pipe(Is((e, t) => e && t && e.requiresCDMAttachOnStart === t.requiresCDMAttachOnStart), La(e => null != e && e.requiresCDMAttachOnStart ? this.attachMediaKeys().pipe(Vn(e => (this.handleKeySystemError(e), Ii))) : Wu), $a(Ii)), this.keyRequest$.pipe(jr(e => e.pipe(Vn(() => Ii)))), this.keySystem$.pipe(La(e => e ? e.keyStatusChange$.pipe(Za(e => { + var t = e.decryptdata.uri, + i = this.ksQuery.getKeyInfo(t); + "needs-renewal" === e.status ? this.ksService.updateKeyRequestState(t, ic.MustRequestResponse, e => e === ic.GotKeyResponse) : (i = Ch(t, e.error, null !== (i = null == i ? void 0 : i.mediaOptionIds) && void 0 !== i ? i : []), this.ksService.setError(t, i)), this._keyStatusChange$.next(e) + })) : Ii)), this.isKeyCleanupSupported() ? this.mediaSink.mediaQuery.bufferedSegmentsTuple$.pipe(jr(e => { + const [t, i] = e, r = new Set; + return t.forEach(e => { + e = null === (e = null === (e = e.frag) || void 0 === e ? void 0 : e.keyTagInfo) || void 0 === e ? void 0 : e.uri; + e && r.add(e) + }), i.forEach(e => { + e = null === (e = null === (e = e.frag) || void 0 === e ? void 0 : e.keyTagInfo) || void 0 === e ? void 0 : e.uri; + e && r.add(e) + }), this.handleKeyCleanup(r) + })) : Wu).pipe(Va(this.reset$)).subscribe() + } + get keyStatusChange$() { + return this._keyStatusChange$ + } + get keySystem() { + return this.keySystem$.value + } + destroy() { + this.reset$.next(), this.ksService.removeAll(), this.keySystemId = null; + const e = this.keySystem; + let t = Wu; + return e && (this.keySystem$.next(null), e.shouldDestroyMediaKeys && this.keySystemFactory.destroyMediaKeys(), t = e.destroy()), Mr([t, this.mediaSink.clearMediaKeys()]).pipe(Zs(void 0)) + } + attachMediaKeys() { + if (this.keySystem) return Wu; + var e = this.config.keySystemPreference ? Ah.getKeySystemFormat(this.config.keySystemPreference) : Sc.keyFormatString; + return this.makeKeySystem(new zc("NONE", null, null, e, [1])).pipe(Zs(void 0)) + } + isKeyCleanupSupported() { + return !0 === this.config.useMultipleKeySessions || "widevine" === this.config.keySystemPreference || "playready" === this.config.keySystemPreference + } + handleKeyCleanup(i) { + if (this.ksQuery.getCount() < 6) return Wu; + const r = performance.now(), + e = this.ksQuery.getAll().map(e => { + var t = e.keyUri; + if (!i.has(t)) { + const i = ac(e.decryptdata); + if ("AES-128" !== i.method && r > e.minHoldTime) return this._removeKey(t, i) + } + return Wu + }); + return e.length ? en(e).pipe($a(Wu)) : Wu + } + _removeKey(e, t) { + return this.abort$.next(e), this.ksService.removeKey(e), this.keySystem.removeKey(t) + } + removeKeysForItems(i) { + const r = []; + return al(() => { + for (const e of i) { + this.ksService.removeAllKeysForItem(e); + const i = this.ksQuery.getAll({ + filterBy: e => 0 === e.itemIds.length + }); + for (const t of i) r.push(this._removeKey(t.keyUri, ac(t.decryptdata))) + } + }), r.length ? en(r).pipe(La(() => Wu)) : Wu + } + get availableKeySystems() { + return Ah.availableKeySystems + } + initialize(e) { + var t = this.protectionData; + this.protectionData = {}; + var i = this.config.keySystemPreference; + for (const a of Ah.availableKeySystems) { + var r = e[a]; + if (r) + if (i === a) { + var n, s = r.certificate, + r = r.serverCertUrl ? bu.buildAbsoluteURL(window.location.href, r.serverCertUrl) : void 0; + let e; + this.protectionData[a] = { + serverCertUrl: r, + certificate: s + }, s ? e = $i({ + keysystem: a, + certificate: s + }) : r && (null === (n = null == t ? void 0 : t[a]) || void 0 === n ? void 0 : n.serverCertUrl) !== r && (n = Tu(r) ? this.config.certLoadPolicy.customURL : this.config.certLoadPolicy.default, e = Rc({ + url: r, + xhrSetup: this.config.xhrSetup + }, n).pipe(hr(([e]) => ({ + keysystem: a, + certificate: new Uint8Array(e) + })))), e && e.pipe(La(e => this.onServerCertificateLoaded(e)), Vn(e => { + throw this.logger.error(`Error loading cert: ${e.message}`), this.eventEmitter.trigger(x.INTERNAL_ERROR, { + type: o, + details: "certificateLoadError", + fatal: !1, + handled: !0, + reason: "Error handling cert", + response: $.KeySystemCertificateLoadError, + message: e.message, + name: "certificateLoadError" + }), e + }), Va(this.reset$)).subscribe() + } else this.logger.warn(`Key system ${a} does not match preference ${i}, ignoring`) + } + } + generateRequest(e, t) { + this.keySystem && this.keySystem.setKeyRequestInfo(e, t) + } + setLicenseResponse(e, t) { + this.keySystem && this.keySystem.setParsedResponse(e, t) + } + getKeyFromDecryptData(e, t) { + if (!e || !e.isEncrypted) return $i(e); + let i; + return al(() => { + i = this._getKeyFromDecryptData(e, t) + }), i + } + _getKeyFromDecryptData(t, i) { + let r = null, + n = null; + i && (r = i.itemId, n = i.mediaOptionId); + const s = t.uri, + e = this.ksQuery.getKeyInfo(s); + if (e && null != i && this.ksService.addMediaOption(s, i), (null == e ? void 0 : e.error) instanceof gc && !1 === e.error.isOkToRetry) return Vi(e.error); + if (e && e.requestState !== ic.MustRequestResponse) return e.requestState === ic.GotKeyResponse ? $i(ac(e.decryptdata)) : this.keyUriToRequest[s]; { + const o = performance.now() + this.config.keyMinHoldTimeBeforeCleanup; + let e; + this.abort$.next(s), this.ksService.upsertKey({ + keyUri: s, + decryptdata: function(e) { + var { + method: t, + isEncrypted: i, + uri: r, + format: n, + formatversions: s + } = e; + return { + method: t, + isEncrypted: i, + uri: r, + format: n, + formatversions: s, + ivBuf: null !== (s = null === (s = e.iv) || void 0 === s ? void 0 : s.buffer) && void 0 !== s ? s : null, + keyIdBuf: null === (s = e.keyId) || void 0 === s ? void 0 : s.buffer, + keyBuf: null === (s = e.key) || void 0 === s ? void 0 : s.buffer, + psshBuf: null === (e = e.key) || void 0 === e ? void 0 : e.buffer + } + }(t), + minHoldTime: o, + mediaOptionIds: [n], + requestState: ic.WaitingForKeyResponse, + itemIds: [r] + }); + var a = t.method; + switch (a) { + case "SAMPLE-AES": + case "ISO-23001-7": + case "SAMPLE-AES-CTR": { + const o = this.config.keyLoadPolicy.customURL; + e = this.fetchKeyEME(t).pipe(So(o.maxLoadTimeMs)); + break + } + case "AES-128": + e = this.fetchKeyHTTP(t.uri, t, this.config.keyLoadPolicy); + break; + default: + return Vi(new V(!1, `Unexpected METHOD attribute ${a}`, $.KeySystemUnexpectedMETHOD)) + } + i = this.keyUriToRequest[s] = e.pipe(hr(e => { + var t = e.decryptdata; + return this.ksService.updateKeyValue(s, t.key), this.eventEmitter.trigger(x.KEY_LOADED, e), e.decryptdata + }), Vn(e => { + var t = this.ksQuery.getKeyInfo(s); + return e = Ch(s, e, null !== (t = null == t ? void 0 : t.mediaOptionIds) && void 0 !== t ? t : []), this.ksService.setError(s, e), Vi(e) + }), Vs(() => { + this.ksService.updateKeyRequestState(s, ic.MustRequestResponse, e => e === ic.WaitingForKeyResponse), this.keyUriToRequest[s] = null + }), Aa(), Va(fn(this.abort$.pipe(ln(e => e === s)), this.reset$).pipe(Za(e => this.logger.warn(e ? `aborted ${le(e)}` : "got reset"))))); + return this.keyRequest$.next(i), i + } + } + fetchKeyEME(e) { + return this.requestKey(e).pipe(hr(e => ({ + timestamp: performance.now(), + keyuri: e.uri, + decryptdata: e + }))) + } + fetchKeyHTTP(e, t, i) { + return Dh.fetchKeyHTTP(e, this.config, t, i) + } + static fetchKeyHTTP(t, e, i, r) { + e = { + url: t, + xhrSetup: e.xhrSetup + }; + return Rc(e, Lc(e, r)).pipe(hr(([e]) => (i.key = new Uint8Array(e), { + decryptdata: i, + keyuri: t, + timestamp: performance.now() + }))) + } + requestKey(t) { + return this.makeKeySystem(t).pipe(La(e => e.startKeyRequest(t))) + } + ensureKeySystem(t) { + return Zr(() => { + if (!this.keySystem && this.keySystemId) { + this.keySystem$.next(this.keySystemFactory.make(this.keySystemId, t, this.config, this.eventEmitter, this.sessionHandler, this.logger)); + var e = this.protectionData && this.protectionData[this.keySystemId]; + if (e) return this.keySystem.setServerCertificate(e.certificate).pipe(Zs(this.keySystem)) + } + return $i(this.keySystem) + }) + } + makeKeySystem(e) { + return this.ensureMediaKeys(e).pipe(La(e => this.mediaSink.setMediaKeys(e).pipe(La(() => this.ensureKeySystem(e))))) + } + ensureMediaKeys(e) { + var t = Ah.getKeySystemIdForDecryptData(e); + if (null == this.keySystemId) this.keySystemId = t; + else if (this.keySystemId !== t) return Vi(new gc(`New key system string does not match existing ${t} !== ${this.keySystemId}`, e.uri, 0, $.KeySystemUnmatchedString, !1, rc.InternalError)); + return this.keySystemFactory.createMediaKeys(this.keySystemId, Oh, kh, this.logger, null === (e = this.platformQuery.platformInfo) || void 0 === e ? void 0 : e.keySystemConfig) + } + onServerCertificateLoaded(e) { + var t = e.keysystem, + e = e.certificate; + return this.protectionData[t].certificate = e, this.keySystem && this.keySystemId === t ? this.keySystem.setServerCertificate(e).pipe(Zs(void 0)) : Wu + } + handleKeySystemError(e) { + e = new yc(e.message, void 0, void 0, $.KeySystemSetupError, void 0); + this.eventEmitter.trigger(x.INTERNAL_ERROR, e) + } + } + class Mh extends kl { + constructor(e) { + super(e) + } + getKeyInfo(e) { + e = this.getEntity(e); + return e ? Object.assign(Object.assign({}, e), { + error: vc(e.error, e.mediaOptionIds) + }) : null + } + getKeyInfo$(e) { + return this.selectEntity(e).pipe(hr(e => e ? Object.assign(Object.assign({}, e), { + error: vc(e.error, e.mediaOptionIds) + }) : null)) + } + getKeyRequestState$(e) { + return this.selectEntity(e, e => null == e ? void 0 : e.requestState) + } + getKeyStatus$(e) { + return this.selectEntity(e, e => null == e ? void 0 : e.status) + } + getKeyError$(e) { + return this.selectEntity(e, e => vc(null == e ? void 0 : e.error, null == e ? void 0 : e.mediaOptionIds)).pipe(wl) + } + } + class xh { + constructor(e) { + this.store = e + } + getQuery() { + return new Mh(this.store) + } + upsertKey(i) { + Do("keys.upsert", i.keyUri); + const r = new Set(i.itemIds.filter(e => null != e)), + n = new Set(i.mediaOptionIds.filter(e => null != e)); + this.store.upsert(i.keyUri, e => { + const t = Object.assign(Object.assign({}, e), i); + if ("itemIds" in e) + for (const i of e.itemIds) r.add(i); + if (t.itemIds = Array.from(r), "mediaOptionIds" in e) + for (const i of e.mediaOptionIds) n.add(i); + return t.mediaOptionIds = Array.from(n), t + }, () => Object.assign(Object.assign({}, i), { + itemIds: Array.from(r), + mediaOptionIds: Array.from(n) + })) + } + removeKey(e) { + Do("keys.removeKey", e), this.store.remove(e) + } + removeAllKeysForItem(i) { + Do(`keys.removeAllKeysForItem ${i}`), this.store.update(null, e => { + var t = e.itemIds.findIndex(e => e === i); + 0 <= t && e.itemIds.splice(t, 1) + }) + } + removeAll() { + Do("keys.remove"), this.store.remove() + } + updateKeyValue(e, t) { + Do("keys.updateKeyValue", e), this.store.update(e, e => { + null == e.decryptdata.keyBuf && null != t && (e.decryptdata.keyBuf = t.buffer), e.requestState = ic.GotKeyResponse + }) + } + updateKeyStatus(e, t) { + Do(`keys.updateKeyStatus ${t}`, e), this.store.update(e, e => { + e.status = t + }) + } + updateKeyRequestState(e, t, i) { + Do(`keys.updateKeyRequestState ${t}`, e), this.store.update(e, e => { + i && !i(e.requestState) || (e.requestState = t) + }) + } + addMediaOption(e, t) { + const { + itemId: i, + mediaOptionId: r + } = t; + Do(`keys.addMediaOption itemId: ${i}, mediaOptionId: ${r}`, e), this.store.update(e, e => { + null != r && e.mediaOptionIds.every(e => e !== r) && e.mediaOptionIds.push(r), null != i && e.itemIds.every(e => e !== i) && e.itemIds.push(i) + }) + } + setError(e, t) { + var i; + Do(`keys.setError ${null===(i=null==t?void 0:t.constructor)||void 0===i?void 0:i.name}`, e), this.store.update(e, e => { + e.error = vc(t), e.requestState = ic.MustRequestResponse + }) + } + } + const Ph = new class extends fl { + constructor() { + super({}, { + name: "key-system-store", + idKey: "keyUri", + producerFn: su + }) + } + }; + let Rh = null; + + function Lh(e) { + let t = e; + return Nh.hasOwnProperty(e) && (t = Nh[e]), String.fromCharCode(t) + } + + function _h(t) { + const i = []; + for (let e = 0; e < t.length; e++) i.push(t[e].toString(16)); + return i + } + const Nh = { + 42: 225, + 92: 233, + 94: 237, + 95: 243, + 96: 250, + 123: 231, + 124: 247, + 125: 209, + 126: 241, + 127: 9608, + 128: 174, + 129: 176, + 130: 189, + 131: 191, + 132: 8482, + 133: 162, + 134: 163, + 135: 9834, + 136: 224, + 137: 32, + 138: 232, + 139: 226, + 140: 234, + 141: 238, + 142: 244, + 143: 251, + 144: 193, + 145: 201, + 146: 211, + 147: 218, + 148: 220, + 149: 252, + 150: 8216, + 151: 161, + 152: 42, + 153: 8217, + 154: 9473, + 155: 169, + 156: 8480, + 157: 8226, + 158: 8220, + 159: 8221, + 160: 192, + 161: 194, + 162: 199, + 163: 200, + 164: 202, + 165: 203, + 166: 235, + 167: 206, + 168: 207, + 169: 239, + 170: 212, + 171: 217, + 172: 249, + 173: 219, + 174: 171, + 175: 187, + 176: 195, + 177: 227, + 178: 205, + 179: 204, + 180: 236, + 181: 210, + 182: 242, + 183: 213, + 184: 245, + 185: 123, + 186: 125, + 187: 92, + 188: 94, + 189: 95, + 190: 124, + 191: 8764, + 192: 196, + 193: 228, + 194: 214, + 195: 246, + 196: 223, + 197: 165, + 198: 164, + 199: 9475, + 200: 197, + 201: 229, + 202: 216, + 203: 248, + 204: 9487, + 205: 9491, + 206: 9495, + 207: 9499 + }, + Fh = 100, + Bh = { + 17: 1, + 18: 3, + 21: 5, + 22: 7, + 23: 9, + 16: 11, + 19: 12, + 20: 14 + }, + Uh = { + 17: 2, + 18: 4, + 21: 6, + 22: 8, + 23: 10, + 19: 13, + 20: 15 + }, + $h = { + 25: 1, + 26: 3, + 29: 5, + 30: 7, + 31: 9, + 24: 11, + 27: 12, + 28: 14 + }, + Vh = { + 25: 2, + 26: 4, + 29: 6, + 30: 8, + 31: 10, + 27: 13, + 28: 15 + }, + Kh = ["white", "green", "blue", "cyan", "red", "yellow", "magenta", "black", "transparent"], + qh = { + verboseFilter: { + DATA: 3, + DEBUG: 3, + INFO: 2, + WARNING: 2, + TEXT: 1, + ERROR: 0 + }, + time: null, + verboseLevel: 0, + setTime: function(e) { + this.time = e + }, + log: function(e, t) { + var i = this.verboseFilter[e]; + this.verboseLevel >= i && console.log(this.time + " [" + e + "] " + t) + } + }; + class Hh { + constructor(e, t, i, r, n) { + this.foreground = e || "white", this.underline = t || !1, this.italics = i || !1, this.background = r || "black", this.flash = n || !1 + } + reset() { + this.foreground = "white", this.underline = !1, this.italics = !1, this.background = "black", this.flash = !1 + } + setStyles(e) { + Object.assign(this, e) + } + isDefault() { + return "white" === this.foreground && !this.underline && !this.italics && "black" === this.background && !this.flash + } + equals(e) { + return this.foreground === e.foreground && this.underline === e.underline && this.italics === e.italics && this.background === e.background && this.flash === e.flash + } + copy(e) { + this.foreground = e.foreground, this.underline = e.underline, this.italics = e.italics, this.background = e.background, this.flash = e.flash + } + toString() { + return "color=" + this.foreground + ", underline=" + this.underline + ", italics=" + this.italics + ", background=" + this.background + ", flash=" + this.flash + } + } + class jh { + constructor(e, t, i, r, n, s) { + this.uchar = e || " ", this.penState = new Hh(t, i, r, n, s) + } + reset() { + this.uchar = " ", this.penState.reset() + } + setChar(e, t) { + this.uchar = e, this.penState.copy(t) + } + setPenState(e) { + this.penState.copy(e) + } + equals(e) { + return this.uchar === e.uchar && this.penState.equals(e.penState) + } + copy(e) { + this.uchar = e.uchar, this.penState.copy(e.penState) + } + isEmpty() { + return " " === this.uchar && this.penState.isDefault() + } + } + class Qh { + constructor() { + this.chars = []; + for (let e = 0; e < Fh; e++) this.chars.push(new jh); + this.pos = 0, this.currPenState = new Hh + } + equals(t) { + let i = !0; + for (let e = 0; e < Fh; e++) + if (!this.chars[e].equals(t.chars[e])) { + i = !1; + break + } return i + } + copy(t) { + for (let e = 0; e < Fh; e++) this.chars[e].copy(t.chars[e]) + } + isEmpty() { + let t = !0; + for (let e = 0; e < Fh; e++) + if (!this.chars[e].isEmpty()) { + t = !1; + break + } return t + } + setCursor(e) { + this.pos !== e && (this.pos = e), this.pos < 0 ? (qh.log("ERROR", "Negative cursor position " + this.pos), this.pos = 0) : this.pos > Fh && (qh.log("ERROR", "Too large cursor position " + this.pos), this.pos = Fh) + } + moveCursor(e) { + var t = this.pos + e; + if (1 < e) + for (let e = this.pos + 1; e < t + 1; e++) this.chars[e].setPenState(this.currPenState); + this.setCursor(t) + } + backSpace() { + this.moveCursor(-1), this.chars[this.pos].setChar(" ", this.currPenState) + } + insertChar(e) { + 144 <= e && this.backSpace(); + var t = Lh(e); + this.pos >= Fh ? qh.log("ERROR", "Cannot insert " + e.toString(16) + " (" + t + ") at position " + this.pos + ". Skipping it!") : (this.chars[this.pos].setChar(t, this.currPenState), this.moveCursor(1)) + } + clearFromPos(e) { + let t; + for (t = e; t < Fh; t++) this.chars[t].reset() + } + clear() { + this.clearFromPos(0), this.pos = 0, this.currPenState.reset() + } + clearToEndOfRow() { + this.clearFromPos(this.pos) + } + getTextString() { + const t = []; + let i = !0; + for (let e = 0; e < Fh; e++) { + var r = this.chars[e].uchar; + " " !== r && (i = !1), t.push(r) + } + return i ? "" : t.join("") + } + setPenStyles(e) { + this.currPenState.setStyles(e), this.chars[this.pos].setPenState(this.currPenState) + } + } + class Wh { + constructor() { + this.rows = []; + for (let e = 0; e < 15; e++) this.rows.push(new Qh); + this.currRow = 14, this.nrRollUpRows = null, this.reset() + } + reset() { + for (let e = 0; e < 15; e++) this.rows[e].clear(); + this.currRow = 14 + } + equals(t) { + let i = !0; + for (let e = 0; e < 15; e++) + if (!this.rows[e].equals(t.rows[e])) { + i = !1; + break + } return i + } + copy(t) { + for (let e = 0; e < 15; e++) this.rows[e].copy(t.rows[e]) + } + isEmpty() { + let t = !0; + for (let e = 0; e < 15; e++) + if (!this.rows[e].isEmpty()) { + t = !1; + break + } return t + } + backSpace() { + this.rows[this.currRow].backSpace() + } + clearToEndOfRow() { + this.rows[this.currRow].clearToEndOfRow() + } + insertChar(e) { + this.rows[this.currRow].insertChar(e) + } + setPen(e) { + this.rows[this.currRow].setPenStyles(e) + } + moveCursor(e) { + this.rows[this.currRow].moveCursor(e) + } + setCursor(e) { + qh.log("INFO", "setCursor: " + e), this.rows[this.currRow].setCursor(e) + } + setPAC(t) { + qh.log("INFO", "pacData = " + JSON.stringify(t)); + let i = t.row - 1; + if (this.nrRollUpRows && i < this.nrRollUpRows - 1 && (i = this.nrRollUpRows - 1), this.nrRollUpRows && this.currRow !== i) { + for (let e = 0; e < 15; e++) this.rows[e].clear(); + const t = this.currRow + 1 - this.nrRollUpRows, + r = this.lastOutputScreen; + if (r) { + const e = r.rows[t].cueStartTime; + if (e && e < qh.time) + for (let e = 0; e < this.nrRollUpRows; e++) this.rows[i - this.nrRollUpRows + e + 1].copy(r.rows[t + e]) + } + } + this.currRow = i; + const r = this.rows[this.currRow]; + if (null !== t.indent) { + const i = t.indent, + e = Math.max(i - 1, 0); + r.setCursor(t.indent), t.color = r.chars[e].penState.foreground + } + const e = { + foreground: t.color, + underline: t.underline, + italics: t.italics, + background: "black", + flash: !1 + }; + this.setPen(e) + } + setBkgData(e) { + qh.log("INFO", "bkgData = " + JSON.stringify(e)), this.backSpace(), this.setPen(e), this.insertChar(32) + } + setRollUpRows(e) { + this.nrRollUpRows = e + } + rollUp() { + if (null !== this.nrRollUpRows) { + qh.log("INFO", "TEXT " + this.getDisplayText()); + const e = this.currRow + 1 - this.nrRollUpRows, + t = this.rows.splice(e, 1)[0]; + t.clear(), this.rows.splice(this.currRow, 0, t), qh.log("INFO", "Rolling up") + } else qh.log("DEBUG", "roll_up but nrRollUpRows not set yet") + } + getDisplayText(t) { + t = t || !1; + const i = []; + let e = "", + r; + for (let e = 0; e < 15; e++) { + const n = this.rows[e].getTextString(); + n && (r = e + 1, t ? i.push("Row " + r + ": '" + n + "'") : i.push(n.trim())) + } + return 0 < i.length && (e = t ? "[" + i.join(" | ") + "]" : i.join("\n")), e + } + getTextAndFormat() { + return this.rows + } + } + class Gh { + constructor(e, t) { + this.chNr = e, this.outputFilter = t, this.mode = null, this.verbose = 0, this.displayedMemory = new Wh, this.nonDisplayedMemory = new Wh, this.lastOutputScreen = new Wh, this.currRollUpRow = this.displayedMemory.rows[14], this.writeScreen = this.displayedMemory, this.mode = null, this.cueStartTime = null + } + reset() { + this.mode = null, this.displayedMemory.reset(), this.nonDisplayedMemory.reset(), this.lastOutputScreen.reset(), this.currRollUpRow = this.displayedMemory.rows[14], this.writeScreen = this.displayedMemory, this.mode = null, this.cueStartTime = null, this.lastCueEndTime = null + } + getHandler() { + return this.outputFilter + } + setHandler(e) { + this.outputFilter = e + } + setPAC(e) { + this.writeScreen.setPAC(e) + } + setBkgData(e) { + this.writeScreen.setBkgData(e) + } + setMode(e) { + e !== this.mode && (this.mode = e, qh.log("INFO", "MODE=" + e), "MODE_POP-ON" === this.mode ? this.writeScreen = this.nonDisplayedMemory : (this.writeScreen = this.displayedMemory, this.writeScreen.reset()), "MODE_ROLL-UP" !== this.mode && (this.displayedMemory.nrRollUpRows = null, this.nonDisplayedMemory.nrRollUpRows = null), this.mode = e) + } + insertChars(t) { + for (let e = 0; e < t.length; e++) this.writeScreen.insertChar(t[e]); + var e = this.writeScreen === this.displayedMemory ? "DISP" : "NON_DISP"; + qh.log("INFO", e + ": " + this.writeScreen.getDisplayText(!0)), "MODE_PAINT-ON" !== this.mode && "MODE_ROLL-UP" !== this.mode || (qh.log("TEXT", "DISPLAYED: " + this.displayedMemory.getDisplayText(!0)), this.outputDataUpdate()) + } + ccRCL() { + qh.log("INFO", "RCL - Resume Caption Loading"), this.setMode("MODE_POP-ON") + } + ccBS() { + qh.log("INFO", "BS - BackSpace"), "MODE_TEXT" !== this.mode && (this.writeScreen.backSpace(), this.writeScreen === this.displayedMemory && this.outputDataUpdate()) + } + ccAOF() {} + ccAON() {} + ccDER() { + qh.log("INFO", "DER- Delete to End of Row"), this.writeScreen.clearToEndOfRow(), this.outputDataUpdate() + } + ccRU(e) { + qh.log("INFO", "RU(" + e + ") - Roll Up"), this.writeScreen = this.displayedMemory, this.setMode("MODE_ROLL-UP"), this.writeScreen.setRollUpRows(e) + } + ccFON() { + qh.log("INFO", "FON - Flash On"), this.writeScreen.setPen({ + flash: !0 + }) + } + ccRDC() { + qh.log("INFO", "RDC - Resume Direct Captioning"), this.setMode("MODE_PAINT-ON") + } + ccTR() { + qh.log("INFO", "TR"), this.setMode("MODE_TEXT") + } + ccRTD() { + qh.log("INFO", "RTD"), this.setMode("MODE_TEXT") + } + ccEDM() { + qh.log("INFO", "EDM - Erase Displayed Memory"), this.displayedMemory.reset(), this.outputDataUpdate(!0) + } + ccCR() { + qh.log("INFO", "CR - Carriage Return"), this.writeScreen.rollUp(), this.outputDataUpdate(!0) + } + ccENM() { + qh.log("INFO", "ENM - Erase Non-displayed Memory"), this.nonDisplayedMemory.reset() + } + ccEOC() { + var e; + qh.log("INFO", "EOC - End Of Caption"), "MODE_POP-ON" === this.mode && (e = this.displayedMemory, this.displayedMemory = this.nonDisplayedMemory, this.nonDisplayedMemory = e, this.writeScreen = this.nonDisplayedMemory, qh.log("TEXT", "DISP: " + this.displayedMemory.getDisplayText())), this.outputDataUpdate(!0) + } + ccTO(e) { + qh.log("INFO", "TO(" + e + ") - Tab Offset"), this.writeScreen.moveCursor(e) + } + ccMIDROW(e) { + const t = { + flash: !1, + underline: !1, + italics: !1 + }; + t.underline = e % 2 == 1, t.italics = 46 <= e, t.italics ? t.foreground = "white" : (e = Math.floor(e / 2) - 16, t.foreground = ["white", "green", "blue", "cyan", "red", "yellow", "magenta"][e]), qh.log("INFO", "MIDROW: " + JSON.stringify(t)), this.writeScreen.setPen(t) + } + outputDataUpdate(e = !1) { + var t = qh.time; + null !== t && this.outputFilter && (this.outputFilter.updateData && this.outputFilter.updateData(t, this.displayedMemory), null !== this.cueStartTime || this.displayedMemory.isEmpty() ? this.displayedMemory.equals(this.lastOutputScreen) || (this.outputFilter.newCue && (this.outputFilter.newCue(this.cueStartTime, t, this.lastOutputScreen), !0 === e && this.outputFilter.dispatchCue && this.outputFilter.dispatchCue()), this.cueStartTime = this.displayedMemory.isEmpty() ? null : t) : this.cueStartTime = t, this.lastOutputScreen.copy(this.displayedMemory)) + } + cueSplitAtTime(e) { + this.outputFilter && (this.displayedMemory.isEmpty() || (this.outputFilter.newCue && this.outputFilter.newCue(this.cueStartTime, e, this.displayedMemory), this.cueStartTime = e)) + } + } + var zh = class { + constructor(e = 1, t, i) { + this.field = e, this.currChNr = -1, this.lastCmdA = null, this.lastCmdB = null, this.channels = [new Gh(1, t), new Gh(2, i)], this.dataCounters = { + padding: 0, + char: 0, + cmd: 0, + other: 0 + } + } + getHandler(e) { + return this.channels[e].getHandler() + } + setHandler(e, t) { + this.channels[e].setHandler(t) + } + addData(e, t) { + let i, r, n, s = null; + qh.setTime(e); + for (let e = 0; e < t.length; e += 2) r = 127 & t[e], n = 127 & t[e + 1], 16 <= r && r <= 31 && r === this.lastCmdA && n === this.lastCmdB ? (this.lastCmdA = null, this.lastCmdB = null, qh.log("DEBUG", "Repeated command (" + _h([r, n]) + ") is dropped")) : 0 != r || 0 != n ? (qh.log("DATA", "[" + _h([t[e], t[e + 1]]) + "] -> (" + _h([r, n]) + ")"), i = this.parseCmd(r, n), i = i || this.parseMidrow(r, n), i = i || this.parsePAC(r, n), i = i || this.parseBackgroundAttributes(r, n), !i && (s = this.parseChars(r, n), s) && (this.currChNr && 0 <= this.currChNr ? this.channels[this.currChNr - 1].insertChars(s) : qh.log("WARNING", "No channel found yet. TEXT-MODE?")), i ? this.dataCounters.cmd += 2 : s ? this.dataCounters.char += 2 : (this.dataCounters.other += 2, qh.log("WARNING", "Couldn't parse cleaned data " + _h([r, n]) + " orig: " + _h([t[e], t[e + 1]])))) : this.dataCounters.padding += 2 + } + parseCmd(e, t) { + var i; + if (!((20 === e || 21 === e || 28 === e || 29 === e) && 32 <= t && t <= 47 || (23 === e || 31 === e) && 33 <= t && t <= 35)) return !1; + const r = this.channels[(i = 20 === e || 23 === e ? 1 : 2) - 1]; + return 20 === e || 21 === e || 28 === e || 29 === e ? 32 === t ? r.ccRCL() : 33 === t ? r.ccBS() : 34 === t ? r.ccAOF() : 35 === t ? r.ccAON() : 36 === t ? r.ccDER() : 37 === t ? r.ccRU(2) : 38 === t ? r.ccRU(3) : 39 === t ? r.ccRU(4) : 40 === t ? r.ccFON() : 41 === t ? r.ccRDC() : 42 === t ? r.ccTR() : 43 === t ? r.ccRTD() : 44 === t ? r.ccEDM() : 45 === t ? r.ccCR() : 46 === t ? r.ccENM() : 47 === t && r.ccEOC() : r.ccTO(t - 32), this.lastCmdA = e, this.lastCmdB = t, this.currChNr = i, !0 + } + parseMidrow(e, t) { + var i; + if ((17 === e || 25 === e) && 32 <= t && t <= 47) { + if ((i = 17 === e ? 1 : 2) !== this.currChNr) return qh.log("ERROR", "Mismatch channel in midrow parsing"), !1; + const r = this.channels[i - 1]; + return r.insertChars([32]), r.ccMIDROW(t), qh.log("DEBUG", "MIDROW (" + _h([e, t]) + ")"), this.lastCmdA = e, this.lastCmdB = t, !0 + } + return !1 + } + parsePAC(e, t) { + if (!((17 <= e && e <= 23 || 25 <= e && e <= 31) && 64 <= t && t <= 127 || (16 === e || 24 === e) && 64 <= t && t <= 95)) return !1; + var i = e <= 23 ? 1 : 2, + r = (64 <= t && t <= 95 ? 1 == i ? Bh : $h : 1 == i ? Uh : Vh)[e], + r = this.interpretPAC(r, t); + return this.channels[i - 1].setPAC(r), this.lastCmdA = e, this.lastCmdB = t, this.currChNr = i, !0 + } + interpretPAC(e, t) { + var i; + const r = { + color: null, + italics: !1, + indent: null, + underline: !1, + row: e + }; + return i = 95 < t ? t - 96 : t - 64, r.underline = 1 == (1 & i), i <= 13 ? r.color = ["white", "green", "blue", "cyan", "red", "yellow", "magenta", "white"][Math.floor(i / 2)] : i <= 15 ? (r.italics = !0, r.color = "white") : r.indent = 4 * Math.floor((i - 16) / 2), r + } + parseChars(e, t) { + let i = null, + r = null, + n = null; + var s; + if (n = 25 <= e ? (i = 2, e - 8) : (i = 1, e), 17 <= n && n <= 19 ? (s = t, s = 17 === n ? t + 80 : 18 === n ? t + 112 : t + 144, qh.log("INFO", "Special char '" + Lh(s) + "' in channel " + i), r = [s], this.lastCmdA = e, this.lastCmdB = t) : 32 <= e && e <= 127 && (r = 0 === t ? [e] : [e, t], this.lastCmdA = null, this.lastCmdB = null), r) { + const e = _h(r); + qh.log("DEBUG", `Char codes = ${e.join(",")}`) + } + return r + } + parseBackgroundAttributes(e, t) { + let i, r, n; + return ((16 === e || 24 === e) && 32 <= t && t <= 47 || (23 === e || 31 === e) && 45 <= t && t <= 47) && (i = { + underline: !1 + }, 16 === e || 24 === e ? (r = Math.floor((t - 32) / 2), i.background = Kh[r], t % 2 == 1 && (i.background = i.background + "_semi")) : 45 === t ? i.background = "transparent" : (i.foreground = "black", 47 === t && (i.underline = !0)), n = this.channels[(e < 24 ? 1 : 2) - 1], n.setBkgData(i), this.lastCmdA = null, !(this.lastCmdB = null)) + } + reset() { + for (let e = 0; e < this.channels.length; e++) this.channels[e] && this.channels[e].reset(); + this.lastCmdA = null, this.lastCmdB = null + } + cueSplitAtTime(t) { + for (let e = 0; e < this.channels.length; e++) this.channels[e] && this.channels[e].cueSplitAtTime(t) + } + }; + class Xh { + constructor(e, t) { + this.handler = e, this.track = t, this.startTime = null, this.endTime = null, this.screen = null + } + dispatchCue() { + null !== this.startTime && (this.handler.addCues("cc" + this.track, this.startTime, this.endTime, this.screen), this.startTime = null) + } + newCue(e, t, i) { + (null === this.startTime || this.startTime > e) && (this.startTime = e), this.endTime = t, this.screen = i, this.handler.createHTMLCaptionsTrack(this.track) + } + } + var Yh = {}, + iu = {}, + nu = {}, + t = {}; + Object.defineProperty(t, "__esModule", { + value: !0 + }), t.isValidPercentValue = function(e) { + return "number" == typeof e && 0 <= e && e <= 100 + }, t.isValidAlignSetting = function(e) { + return "string" == typeof e && ["start", "center", "end", "left", "right", "middle"].includes(e) + }, t.isValidDirectionSetting = function(e) { + return "string" == typeof e && ["", "rl", "lr"].includes(e) + }, t.isValidLineAndPositionSetting = function(e) { + return "number" == typeof e || "auto" === e + }, t.isValidLineAlignSetting = function(e) { + return "string" == typeof e && ["start", "center", "end"].includes(e) + }, t.isValidPositionAlignSetting = function(e) { + return "string" == typeof e && ["line-left", "center", "line-right", "auto", "left", "start", "middle", "end", "right"].includes(e) + }, t.isValidScrollSetting = function(e) { + return ["", "up"].includes(e) + }; + ru = {}; + Object.defineProperty(ru, "__esModule", { + value: !0 + }); + const Jh = { + "&": "&", + "<": "<", + ">": ">", + "‎": "‎", + "‏": "‏", + " ": " " + }, + Zh = { + c: "span", + i: "i", + b: "b", + u: "u", + ruby: "ruby", + rt: "rt", + v: "span", + lang: "span" + }, + ep = { + v: "title", + lang: "lang" + }, + tp = { + rt: "ruby" + }, + ip = { + "text-combine-upright": "-webkit-text-combine:horizontal; text-orientation: mixed;" + }; + class rp { + static parseTimeStamp(e) { + function t(e) { + var [t, i, r, e] = e.map(e => e ? parseInt("" + e) : 0); + return 3600 * t + 60 * i + r + e / 1e3 + } + const i = /^(\d+):(\d{2})(:\d{2})?\.(\d{3})/.exec(e); + return i ? i[3] ? t([i[1], i[2], i[3].substring(1), i[4]]) : 59 < parseInt(i[1]) ? t([i[1], i[2], null, i[4]]) : t([null, i[1], i[2], i[4]]) : null + } + static parseContent(s, t, a) { + let i = t.text; + + function e(e) { + return Jh[e] + } + const r = s.document.createElement("div"), + n = []; + let o, l, d = r; + for (; null !== (o = function() { + if (!i) return null; + var e = (e = /^([^<]*)(<[^>]+>?)?/.exec(i))[1] || e[2]; + return i = i.substr(e.length), e + }());) + if ("<" !== o[0]) d.appendChild(s.document.createTextNode(o.replace(/&(amp|lt|gt|lrm|rlm|nbsp);/g, e))); + else { + if ("/" === o[1]) { + n.length && n[n.length - 1] === o.substr(2).replace(">", "") && (n.pop(), d = d.parentNode); + continue + } + const t = rp.parseTimeStamp(o.substr(1, o.length - 2)); + let e; + if (t) { + e = s.document.createProcessingInstruction("timestamp", t.toString()), d.appendChild(e); + continue + } + if (!(l = /^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/.exec(o))) continue; + if (e = function(e, t, i) { + var r = Zh[e]; + if (!r) return null; + const n = s.document.createElement(r); + if (n.dataset.localName = r, (e = ep[e]) && i && (n[e] = i.trim()), t) + if (a[t]) { + const s = function(e) { + let t = ""; + for (const i in e) t += ip[i] || i + ":" + e[i] + ";"; + return t + }(a[t]); + n.setAttribute("style", s) + } else console.info(`WebVTT: parseContent: Style referenced, but no style defined for '${t}'!`); + return n + }(l[1], l[2], l[3]), !e) continue; + if (u = d, c = e, tp[c.dataset.localName] && tp[c.dataset.localName] !== u.dataset.localName) continue; + n.push(l[1]), d.appendChild(e), d = e + } var u, c; + return r + } + } + ru.default = rp; + w = e && e.__decorate || function(e, t, i, r) { + var n, s = arguments.length, + a = s < 3 ? t : null === r ? r = Object.getOwnPropertyDescriptor(t, i) : r; + if ("object" == typeof Reflect && "function" == typeof Reflect.decorate) a = Reflect.decorate(e, t, i, r); + else + for (var o = e.length - 1; 0 <= o; o--)(n = e[o]) && (a = (s < 3 ? n(a) : 3 < s ? n(t, i, a) : n(t, i)) || a); + return 3 < s && a && Object.defineProperty(t, i, a), a + }; + Object.defineProperty(nu, "__esModule", { + value: !0 + }); + const np = t, + sp = ru; + w = w([function(e) { + let t = e; + return "undefined" != typeof window && null != window.VTTCue && (t = window.VTTCue, t.create = e.create, t.fromJSON = e.fromJSON, t.prototype.toJSON = e.prototype.toJSON), t + }], w = class { + constructor(e, t, i) { + this._id = "", this._pauseOnExit = !1, this._region = null, this._vertical = "", this._snapToLines = !0, this._line = "auto", this._lineAlign = "start", this._position = "auto", this._positionAlign = "auto", this._size = 100, this._align = "center", this.hasBeenReset = !1, this._startTime = e, this._endTime = t, this._text = i + } + get id() { + return this._id + } + set id(e) { + this._id = "" + e + } + get pauseOnExit() { + return this._pauseOnExit + } + set pauseOnExit(e) { + this._pauseOnExit = !!e + } + get startTime() { + return this._startTime + } + set startTime(e) { + if ("number" != typeof e) throw new TypeError(`Start time must be set to a number: ${e}`); + this._startTime = e, this.hasBeenReset = !0 + } + get endTime() { + return this._endTime + } + set endTime(e) { + if ("number" != typeof e) throw new TypeError(`End time must be set to a number: ${e}`); + this._endTime = e, this.hasBeenReset = !0 + } + get text() { + return this._text + } + set text(e) { + this._text = "" + e, this.hasBeenReset = !0 + } + get region() { + return this._region + } + set region(e) { + this._region = e, this.hasBeenReset = !0 + } + get vertical() { + return this._vertical + } + set vertical(e) { + if (!np.isValidDirectionSetting(e)) throw new SyntaxError(`An invalid or illegal string was specified for vertical: ${e}`); + this._vertical = e, this.hasBeenReset = !0 + } + get snapToLines() { + return this._snapToLines + } + set snapToLines(e) { + this._snapToLines = !!e, this.hasBeenReset = !0 + } + get line() { + return this._line + } + set line(e) { + if (!np.isValidLineAndPositionSetting(e)) throw new SyntaxError(`An invalid number or illegal string was specified for line: ${e}`); + this._line = e, this.hasBeenReset = !0 + } + get lineAlign() { + return this._lineAlign + } + set lineAlign(e) { + if (!np.isValidLineAlignSetting(e)) throw new SyntaxError(`An invalid or illegal string was specified for lineAlign: ${e}`); + this._lineAlign = e, this.hasBeenReset = !0 + } + get position() { + return this._position + } + set position(e) { + if (!np.isValidLineAndPositionSetting(e)) throw new Error(`Position must be between 0 and 100 or auto: ${e}`); + this._position = e, this.hasBeenReset = !0 + } + get positionAlign() { + return this._positionAlign + } + set positionAlign(e) { + if (!np.isValidPositionAlignSetting(e)) throw new SyntaxError(`An invalid or illegal string was specified for positionAlign: ${e}`); + this._positionAlign = e, this.hasBeenReset = !0 + } + get size() { + return this._size + } + set size(e) { + if (e < 0 || 100 < e) throw new Error(`Size must be between 0 and 100: ${e}`); + this._size = e, this.hasBeenReset = !0 + } + get align() { + return this._align + } + set align(e) { + if (!np.isValidAlignSetting(e)) throw new SyntaxError(`An invalid or illegal string was specified for align: ${e}`); + this._align = e, this.hasBeenReset = !0 + } + getCueAsHTML() { + return sp.default.parseContent(window, this, {}) + } + static create(t) { + if (!t.hasOwnProperty("startTime") || !t.hasOwnProperty("endTime") || !t.hasOwnProperty("text")) throw new Error("You must at least have start time, end time, and text."); + const i = new this(t.startTime, t.endTime, t.text); + return Object.keys(t).forEach(e => { + i.hasOwnProperty(e) && (i[e] = t[e]) + }), i + } + static fromJSON(e) { + return this.create(JSON.parse(e)) + } + toJSON() { + const t = {}; + return Object.keys(this).forEach(e => { + this.hasOwnProperty(e) && "getCueAsHTML" !== e && "hasBeenReset" !== e && "displayState" !== e && (t[e] = this[e]) + }), t + } + }); + nu.VTTCue = w; + w = {}, e = e && e.__decorate || function(e, t, i, r) { + var n, s = arguments.length, + a = s < 3 ? t : null === r ? r = Object.getOwnPropertyDescriptor(t, i) : r; + if ("object" == typeof Reflect && "function" == typeof Reflect.decorate) a = Reflect.decorate(e, t, i, r); + else + for (var o = e.length - 1; 0 <= o; o--)(n = e[o]) && (a = (s < 3 ? n(a) : 3 < s ? n(t, i, a) : n(t, i)) || a); + return 3 < s && a && Object.defineProperty(t, i, a), a + }; + Object.defineProperty(w, "__esModule", { + value: !0 + }); + const ap = t; + e = e([function(e) { + let t = e; + return "undefined" != typeof window && null != window.VTTRegion && (t = window.VTTRegion, t.create = e.create, t.fromJSON = e.fromJSON, t.prototype.toJSON = e.prototype.toJSON), t + }], e = class { + constructor() { + this._id = "", this._lines = 3, this._regionAnchorX = 0, this._regionAnchorY = 100, this._scroll = "", this._viewportAnchorX = 0, this._viewportAnchorY = 100, this._width = 100 + } + get id() { + return this._id + } + set id(e) { + if ("string" != typeof e) throw new Error("ID must be a string."); + this._id = e + } + get lines() { + return this._lines + } + set lines(e) { + if ("number" != typeof e) throw new TypeError("Lines must be set to a number."); + this._lines = e + } + get regionAnchorX() { + return this._regionAnchorX + } + set regionAnchorX(e) { + if (!ap.isValidPercentValue(e)) throw new TypeError("RegionAnchorX must be between 0 and 100."); + this._regionAnchorX = e + } + get regionAnchorY() { + return this._regionAnchorY + } + set regionAnchorY(e) { + if (!ap.isValidPercentValue(e)) throw new TypeError("RegionAnchorY must be between 0 and 100."); + this._regionAnchorY = e + } + get scroll() { + return this._scroll + } + set scroll(e) { + if ("string" == typeof e) { + e = e.toLowerCase(); + if (ap.isValidScrollSetting(e)) return void(this._scroll = e) + } + throw new SyntaxError("An invalid or illegal string was specified.") + } + get viewportAnchorX() { + return this._viewportAnchorX + } + set viewportAnchorX(e) { + if (!ap.isValidPercentValue(e)) throw new TypeError("ViewportAnchorX must be between 0 and 100."); + this._viewportAnchorX = e + } + get viewportAnchorY() { + return this._viewportAnchorY + } + set viewportAnchorY(e) { + if (!ap.isValidPercentValue(e)) throw new TypeError("ViewportAnchorY must be between 0 and 100."); + this._viewportAnchorY = e + } + get width() { + return this._width + } + set width(e) { + if (!ap.isValidPercentValue(e)) throw new TypeError("Width must be between 0 and 100."); + this._lines = e + } + toJSON() { + const t = {}; + return Object.keys(this).forEach(e => { + this.hasOwnProperty(e) && (t[e] = this[e]) + }), t + } + static create(t) { + const i = new this; + return Object.keys(t).forEach(e => { + i.hasOwnProperty(e) && (i[e] = t[e]) + }), i + } + static fromJSON(e) { + return this.create(JSON.parse(e)) + } + }); + w.VTTRegion = e, Object.defineProperty(iu, "__esModule", { + value: !0 + }); + const op = nu; + iu.VTTCue = op.VTTCue; + const lp = w; + iu.VTTRegion = lp.VTTRegion; + const dp = ru; + class up extends Error { + constructor(e, t) { + super(), this.name = "ParsingError", this.code = "number" == typeof e ? e : e.code, t ? this.message = t : e instanceof up && (this.message = e.message) + } + }(iu.ParsingError = up).Errors = { + BadSignature: new up(0, "Malformed WebVTT signature."), + BadTimeStamp: new up(1, "Malformed time stamp.") + }; + class cp { + constructor() { + this.values = {} + } + set(e, t) { + this.get(e) || "" === t || (this.values[e] = t) + } + get(e, t, i) { + return "object" == typeof t && "string" == typeof i ? this.has(e) ? this.values[e] : t[i] : this.has(e) ? this.values[e] : t + } + has(e) { + return e in this.values + } + alt(t, i, r) { + for (let e = 0; e < r.length; ++e) + if (i === r[e]) { + this.set(t, i); + break + } + } + integer(e, t) { + /^-?\d+$/.test(t) && this.set(e, parseInt(t, 10)) + } + percent(e, t) { + if (t.match(/^([\d]{1,3})(\.[\d]*)?%$/)) try { + var i = parseFloat(t); + if (0 <= i && i <= 100) return this.set(e, i), !0 + } catch (e) { + return !1 + } + return !1 + } + } + class hp { + constructor(e, t, i) { + this.window = e, this.state = "INITIAL", this.styleCollector = "", this.buffer = "", this.decoder = t || new TextDecoder("utf8"), this.regionList = [], this.onStylesParsedCallback = i, this._styles = {} + } + static StringDecoder() { + return { + decode: e => { + if (!e) return ""; + if ("string" != typeof e) throw new Error("Error - expected string data."); + return decodeURIComponent(encodeURIComponent(e)) + } + } + } + reportOrThrowError(e) { + if (!(e instanceof up && "function" == typeof this.onparsingerror)) throw e; + this.onparsingerror(e) + } + parseOptions(e, t, i, r) { + e = r ? e.split(r) : [e]; + for (const n of e) + if ("string" == typeof n) { + const r = n.split(i); + 2 === r.length && t(r[0], r[1]) + } + } + parseCue(t, e, a) { + const i = t, + r = () => { + var e = dp.default.parseTimeStamp(t); + if (null === e) throw new up(up.Errors.BadTimeStamp, "Malformed timestamp: " + i); + return t = t.replace(/^[^\sa-zA-Z-]+/, ""), e + }, + n = () => { + t = t.replace(/^\s+/, "") + }; + if (n(), e.startTime = r(), n(), "--\x3e" !== t.substr(0, 3)) throw new up(up.Errors.BadTimeStamp, `Malformed time stamp (time stamps must be separated by '--\x3e'): ${i}`); + t = t.substr(3), n(), e.endTime = r(), n(), ((e, t) => { + const s = new cp; + this.parseOptions(e, (t, i) => { + let e, r; + switch (t) { + case "region": + for (let e = a.length - 1; 0 <= e; e--) + if (a[e].id === i) { + s.set(t, a[e].region); + break + } break; + case "vertical": + s.alt(t, i, ["rl", "lr"]); + break; + case "line": + e = i.split(","), r = e[0], s.integer(t, r), s.percent(t, r) && s.set("snapToLines", !1), s.alt(t, r, ["auto"]), 2 === e.length && s.alt("lineAlign", e[1], ["start", "center", "end"]); + break; + case "position": + e = i.split(","), s.percent(t, e[0]), 2 === e.length && (n = ["line-left", "line-right", "center", "auto", "left", "start", "middle", "end", "right"], s.alt("positionAlign", e[1], n)); + break; + case "size": + s.percent(t, i); + break; + case "align": + var n = ["start", "center", "end", "left", "right", "middle"]; + s.alt(t, i, n) + } + }, /:/, /\s/), t.region = s.get("region", null), t.vertical = s.get("vertical", ""), t.line = s.get("line", void 0 === t.line ? "auto" : t.line), t.lineAlign = s.get("lineAlign", "start"), t.snapToLines = s.get("snapToLines", !0), t.size = s.get("size", 100); + e = s.get("align", "center"); + t.align = "middle" === e ? "center" : e, t.position = s.get("position", "auto"); + e = s.get("positionAlign", { + start: "start", + left: "start", + center: "center", + right: "end", + end: "end" + }, t.align); + t.positionAlign = { + start: "start", + "line-left": "start", + left: "start", + center: "center", + middle: "center", + "line-right": "end", + right: "end", + end: "end" + } [e] + })(t, e) + } + parseRegion(e) { + const n = new cp; + if (this.parseOptions(e, (e, t) => { + switch (e) { + case "id": + n.set(e, t); + break; + case "width": + n.percent(e, t); + break; + case "lines": + n.integer(e, t); + break; + case "regionanchor": + case "viewportanchor": { + var i = t.split(","); + if (2 !== i.length) break; + const r = new cp; + if (r.percent("x", i[0]), r.percent("y", i[1]), !r.has("x") || !r.has("y")) break; + n.set(e + "X", r.get("x")), n.set(e + "Y", r.get("y")); + break + } + case "scroll": + n.alt(e, t, ["up"]) + } + }, /=/, /\s/), n.has("id")) { + const e = new lp.VTTRegion; + e.width = n.get("width", 100), e.lines = n.get("lines", 3), e.regionAnchorX = n.get("regionanchorX", 0), e.regionAnchorY = n.get("regionanchorY", 100), e.viewportAnchorX = n.get("viewportanchorX", 0), e.viewportAnchorY = n.get("viewportanchorY", 100), e.scroll = n.get("scroll", ""), this.onregion && this.onregion(e), this.regionList.push({ + id: n.get("id"), + region: e + }) + } + } + parseStyle(i) { + const e = i.split("}"); + e.pop(); + for (const i of e) { + let e = null, + t = null; + const r = i.split("{"); + r[0] && (e = r[0].trim()), r[1] && (t = (e => { + const t = {}, + i = e.split(";"); + for (let e = 0; e < i.length; e++) + if (i[e].includes(":")) { + const r = i[e].split(":", 2), + n = r[0].trim(), + s = r[1].trim(); + "" !== n && "" !== s && (t[n] = s) + } return t + })(r[1])), e && t && (this._styles[e] = t) + } + this.onStylesParsedCallback && this.onStylesParsedCallback(this._styles) + } + parseHeader(e) { + this.parseOptions(e, function(e, t) { + "Region" === e && this.parseRegion(t) + }, /:/) + } + parse(i) { + i && (this.buffer += this.decoder.decode(i, { + stream: !0 + })); + const r = () => { + const e = this.buffer; + let t = 0; + let i = { + start: e.length, + length: 0 + }; + for (; t < e.length;) { + const r = ((t, i) => { + const r = { + start: -1, + length: -1 + }; + if ("\r" === t[i]) r.start = i, r.length = 1; + else if ("\n" === t[i]) r.start = i, r.length = 1; + else if ("<" === t[i] && i + 1 < t.length && "b" === t[i + 1] && i + 2 < t.length && "r" === t[i + 2]) { + let e = i + 2; + for (; e < t.length && ">" !== t[e++];); + r.start = i, r.length = e - i + } + return r + })(e, t); + if (0 < r.length) { + i = r; + break + }++t + } + const r = e.substr(0, i.start); + return this.buffer = e.substr(i.start + i.length), r + }; + try { + let e; + if ("INITIAL" === this.state) { + if (!/\r\n|\n/.test(this.buffer)) return this; + e = r(); + var n = /^()?WEBVTT([ \t].*)?$/.exec(e); + if (!n || !n[0]) throw new up(up.Errors.BadSignature); + this.state = "HEADER" + } + let t = !1; + for (; this.buffer;) { + if (!/\r\n|\n/.test(this.buffer)) return this; + switch (t ? t = !1 : e = r(), this.state) { + case "HEADER": + e.includes(":") ? this.parseHeader(e) : e || (this.state = "ID"); + continue; + case "NOTE": + e || (this.state = "ID"); + continue; + case "STYLE": + e ? this.styleCollector += e : (this.parseStyle(this.styleCollector), this.state = "ID", this.styleCollector = ""); + continue; + case "ID": + if (/^NOTE($|[ \t])/.test(e)) { + this.state = "NOTE"; + break + } + if (/^STYLE($|[ \t])/.test(e)) { + this.state = "STYLE"; + break + } + if (!e) continue; + if (this.cue = new op.VTTCue(0, 0, ""), this.state = "CUE", !e.includes("--\x3e")) { + this.cue.id = e; + continue + } + case "CUE": + try { + this.parseCue(e, this.cue, this.regionList) + } catch (i) { + this.reportOrThrowError(i), this.cue = null, this.state = "BADCUE"; + continue + } + this.state = "CUETEXT"; + continue; + case "CUETEXT": { + const r = e.includes("--\x3e"); + if (!e || r) { + t = !0, this.oncue && this.oncue(this.cue), this.cue = null, this.state = "ID"; + continue + } + this.cue.text && (this.cue.text += "\n"), this.cue.text += e; + continue + } + case "BADCUE": + e || (this.state = "ID"); + continue + } + } + } catch (i) { + this.reportOrThrowError(i), "CUETEXT" === this.state && this.cue && this.oncue && this.oncue(this.cue), this.cue = null, this.state = "INITIAL" === this.state ? "BADWEBVTT" : "BADCUE" + } + return this + } + flush() { + try { + if (this.buffer += this.decoder.decode(), !this.cue && "HEADER" !== this.state || (this.buffer += "\n\n", this.parse()), "INITIAL" === this.state) throw new up(up.Errors.BadSignature) + } catch (e) { + this.reportOrThrowError(e) + } + return this.onflush && this.onflush(), this + } + styles() { + return this._styles + } + } + iu.default = hp, iu.WebVTTParser = hp; + var pp, w = {}; + Object.defineProperty(w, "__esModule", { + value: !0 + }); + const fp = nu; + w.VTTCue = fp.VTTCue; + const mp = ru, + gp = [/^(::cue\()(\..*)(\))/, /^(::cue\()(#.*)(\))/, /^(::cue\()(c|i|b|u|ruby|rt|v|lang)(\))/], + yp = [ + [1470, 1470], + [1472, 1472], + [1475, 1475], + [1478, 1478], + [1488, 1514], + [1520, 1524], + [1544, 1544], + [1547, 1547], + [1549, 1549], + [1563, 1563], + [1566, 1610], + [1645, 1647], + [1649, 1749], + [1765, 1766], + [1774, 1775], + [1786, 1805], + [1807, 1808], + [1810, 1839], + [1869, 1957], + [1969, 1969], + [1984, 2026], + [2036, 2037], + [2042, 2042], + [2048, 2069], + [2074, 2074], + [2084, 2084], + [2088, 2088], + [2096, 2110], + [2112, 2136], + [2142, 2142], + [2208, 2208], + [2210, 2220], + [8207, 8207], + [64285, 64285], + [64287, 64296], + [64298, 64310], + [64312, 64316], + [64318, 64318], + [64320, 64321], + [64323, 64324], + [64326, 64449], + [64467, 64829], + [64848, 64911], + [64914, 64967], + [65008, 65020], + [65136, 65140], + [65142, 65276], + [67584, 67589], + [67592, 67592], + [67594, 67637], + [67639, 67640], + [67644, 67644], + [67647, 67669], + [67671, 67679], + [67840, 67867], + [67872, 67897], + [67903, 67903], + [67968, 68023], + [68030, 68031], + [68096, 68096], + [68112, 68115], + [68117, 68119], + [68121, 68147], + [68160, 68167], + [68176, 68184], + [68192, 68223], + [68352, 68405], + [68416, 68437], + [68440, 68466], + [68472, 68479], + [68608, 68680], + [126464, 126467], + [126469, 126495], + [126497, 126498], + [126500, 126500], + [126503, 126503], + [126505, 126514], + [126516, 126519], + [126521, 126521], + [126523, 126523], + [126530, 126530], + [126535, 126535], + [126537, 126537], + [126539, 126539], + [126541, 126543], + [126545, 126546], + [126548, 126548], + [126551, 126551], + [126553, 126553], + [126555, 126555], + [126557, 126557], + [126559, 126559], + [126561, 126562], + [126564, 126564], + [126567, 126570], + [126572, 126578], + [126580, 126583], + [126585, 126588], + [126590, 126590], + [126592, 126601], + [126603, 126619], + [126625, 126627], + [126629, 126633], + [126635, 126651], + [1114109, 1114109] + ]; + class vp { + applyStyles(e, t) { + t = t || this.div; + for (const i in e) e.hasOwnProperty(i) && (t.style[i] = e[i]) + } + formatStyle(e, t) { + return 0 === e ? "0" : e + t + } + } + w.StyleBox = vp; + class Sp extends vp { + constructor(e, t, i, r, n) { + super(); + let s = { + textAlign: { + start: "left", + "line-left": "left", + left: "left", + center: "center", + middle: "center", + "line-right": "right", + right: "right", + end: "right" + } [(this.cue = t).positionAlign] || t.align, + whiteSpace: "pre-line", + position: "absolute" + }; + s.direction = this.determineBidi(this.cueDiv), s.writingMode = this.directionSettingToWritingMode(t.vertical), s.unicodeBidi = "plaintext", this.div = e.document.createElement("div"), this.applyStyles(s), s = { + backgroundColor: r.backgroundColor, + display: "inline-block" + }, this.parseOpacity(s.backgroundColor) && (s.padding = "5px", s.borderRadius = "5px"), this.backgroundDiv = e.document.createElement("div"), this.applyStyles(s, this.backgroundDiv), s = { + color: i.color, + backgroundColor: i.backgroundColor, + textShadow: i.textShadow, + fontSize: i.fontSize, + fontFamily: i.fontFamily, + position: "relative", + left: "0", + right: "0", + top: "0", + bottom: "0", + display: "inline-block", + textOrientation: "upright" + }, s.writingMode = this.directionSettingToWritingMode(t.vertical), s.unicodeBidi = "plaintext", this.cueDiv = mp.default.parseContent(e, t, n), this.applyStyles(s, this.cueDiv), this.backgroundDiv.appendChild(this.cueDiv), this.div.appendChild(this.backgroundDiv); + let a = 0; + if ("number" == typeof t.position) { + n = t.positionAlign || t.align; + if (n) switch (n) { + case "start": + case "left": + a = t.position; + break; + case "center": + case "middle": + a = t.position - t.size / 2; + break; + case "end": + case "right": + a = t.position - t.size + } + } + "" === t.vertical ? this.applyStyles({ + left: this.formatStyle(a, "%"), + width: this.formatStyle(t.size, "%") + }) : this.applyStyles({ + top: this.formatStyle(a, "%"), + height: this.formatStyle(t.size, "%") + }) + } + determineBidi(e) { + let t = [], + i = ""; + if (!e || !e.childNodes) return "ltr"; + + function n(t, i) { + for (let e = i.childNodes.length - 1; 0 <= e; e--) t.push(i.childNodes[e]) + } + for (n(t, e); i = function e(t) { + if (!t || !t.length) return null; + let i = t.pop(), + r = i.textContent || i.innerText; + if (r) { + const i = /^.*(\n|\r)/.exec(r); + return i ? i[t.length = 0] : r + } + return "ruby" === i.tagName ? e(t) : i.childNodes ? (n(t, i), e(t)) : void 0 + }(t);) + for (let e = 0; e < i.length; e++) + if (function(e, t) { + for (const i of t) + if (e >= i[0] && e <= i[1]) return 1 + }(i.charCodeAt(e), yp)) return "rtl"; + return "ltr" + } + parseOpacity(e) { + if (!e || "string" != typeof e) return null; + e = (e = e.replace(/ /g, "").replace("rgba(", "").replace(")", "")).split(","); + return e && 4 <= e.length ? e[3] : null + } + directionSettingToWritingMode(e) { + return "" === e ? "horizontal-tb" : "lr" === e ? "vertical-lr" : "vertical-rl" + } + move(e) { + this.applyStyles({ + top: this.formatStyle(e.top, "px"), + bottom: this.formatStyle(e.bottom, "px"), + left: this.formatStyle(e.left, "px"), + right: this.formatStyle(e.right, "px"), + height: this.formatStyle(e.height, "px"), + width: this.formatStyle(e.width, "px") + }) + } + } + w.CueStyleBox = Sp; + class bp { + constructor(e) { + var t; + let i, r, n, s, a, o; + if (e instanceof Sp && e.cue ? (t = e.cue) && "" !== t.vertical ? this.property = "width" : this.property = "height" : e instanceof bp && (this.property = e.property || "height"), e instanceof Sp && e.div) { + n = e.div.offsetHeight, s = e.div.offsetWidth, a = e.div.offsetTop; + const t = e.div.firstChild; + if (o = (t || e.div).getBoundingClientRect(), i = o && o[this.property] || null, t && t.firstChild) { + const e = t.firstChild; + e && "string" == typeof e.textContent && (r = i / this.calculateNewLines(e.textContent)) + } + } else e instanceof bp && (o = e); + this.left = o.left, this.right = o.right, this.top = o.top || a, this.height = o.height || n, this.bottom = o.bottom || a + (o.height || n), this.width = o.width || s, this.lineHeight = null !== i ? i : o.lineHeight, this.singleLineHeight = null !== r ? r : o.singleLineHeight, this.singleLineHeight || (this.singleLineHeight = 41) + } + calculateNewLines(t) { + let i = 1; + for (let e = 0; e < t.length; e++) "\n" === t[e] && i++; + return i + } + move(e, t) { + switch (t = void 0 !== t ? t : this.singleLineHeight, e) { + case "+x": + this.left += t, this.right += t; + break; + case "-x": + this.left -= t, this.right -= t; + break; + case "+y": + this.top += t, this.bottom += t; + break; + case "-y": + this.top -= t, this.bottom -= t + } + } + overlaps(e) { + return this.left < e.right && this.right > e.left && this.top < e.bottom && this.bottom > e.top + } + overlapsAny(e) { + for (const t of e) + if (this.overlaps(t)) return !0; + return !1 + } + within(e) { + return this.top >= e.top && this.bottom <= e.bottom && this.left >= e.left && this.right <= e.right + } + moveIfOutOfBounds(e, t) { + switch (t) { + case "+x": + this.left < e.left && (this.left = e.left, this.right = this.left + this.width); + break; + case "-x": + this.right > e.right && (this.right = e.right, this.left = this.right - this.width); + break; + case "+y": + this.top < e.top && (this.top = e.top, this.bottom = this.top + this.height); + break; + case "-y": + this.bottom > e.bottom && (this.bottom = e.bottom, this.top = this.bottom - this.height) + } + } + toCSSCompatValues(e) { + return { + top: this.top - e.top, + bottom: e.bottom - this.bottom, + left: this.left - e.left, + right: e.right - this.right, + height: this.height, + width: this.width + } + } + static getSimpleBoxPosition(e) { + let t = null; + e instanceof vp && e.div ? t = e.div : e instanceof HTMLElement && (t = e); + let i = t.offsetHeight || 0, + r = t.offsetWidth || 0, + n = t.offsetTop || 0, + s = n + i, + a = t.getBoundingClientRect(); + var { + left: o, + right: e + } = a; + return a.top && (n = a.top), a.height && (i = a.height), a.width && (r = a.width), a.bottom && (s = a.bottom), { + left: o, + right: e, + top: n, + height: i, + bottom: s, + width: r + } + } + static getBoxPosition(r, n) { + if (r && 0 < r.length) { + let t = 0, + i = r[0][n]; + for (let e = 0; e < r.length; e++) n in ["top", "right"] ? r[e][n] > i && (t = e, i = r[e][n]) : n in ["bottom", "left"] && r[e][n] < i && (t = e, i = r[e][n]); + return r[t] + } + return null + } + static moveToMinimumDistancePlacement(e, t, i) { + "height" === e.property ? "+y" === t ? (e.top = i.topMostBoxPosition.bottom + 0, e.bottom = e.top + e.height) : "-y" === t && (e.bottom = +i.bottomMostBoxPosition.top, e.top = e.bottom - e.height) : "width" === e.property && ("+x" === t ? (e.left = i.rightMostBoxPosition.right + 0, e.right = e.left + e.width) : "-x" === t && (e.right = +i.leftMostBoxPosition.left, e.left = e.right - e.width)) + } + static moveBoxToLinePosition(e, a, o) { + var n = e.cue; + let i, r = new bp(e), + s = function() { + if ("number" == typeof n.line && (n.snapToLines || 0 <= n.line && n.line <= 100)) return n.line; + if (!n.track || !n.track.textTrackList || !n.track.textTrackList.mediaElement) return -1; + let t = 0; + var i = n.track, + r = i.textTrackList; + for (let e = 0; e < r.length && r[e] !== i; e++) "showing" === r[e].mode && t++; + return -1 * ++t + }(), + l = []; + if (n.snapToLines) { + let t = 0; + switch (n.vertical) { + case "": + l = ["+y", "-y"], i = "height"; + break; + case "rl": + l = ["+x", "-x"], i = "width"; + break; + case "lr": + l = ["-x", "+x"], i = "width" + } + const o = r.lineHeight, + d = a[i] + o, + u = l[0]; + if (s < 0) { + let e = 0; + switch (n.vertical) { + case "": + e = a.height - o - .05 * a.height; + break; + case "rl": + case "lr": + e = -a.width + o + .05 * a.width + } + t = e, l = l.reverse() + } else { + switch (n.vertical) { + case "": + t = o * Math.round(s); + break; + case "rl": + t = a.width - o * Math.round(s); + break; + case "lr": + t = o * Math.round(s) + } + Math.abs(t) > d && (t = t < 0 ? -1 : 1, t *= Math.ceil(d / o) * o) + } + r.move(u, t) + } else { + const o = "" === n.vertical ? a.height : a.width, + i = r.lineHeight / o * 100; + switch (n.lineAlign) { + case "center": + s -= i / 2; + break; + case "end": + s -= i + } + switch (n.vertical) { + case "": + e.applyStyles({ + top: e.formatStyle(s, "%") + }); + break; + case "rl": + e.applyStyles({ + right: e.formatStyle(s, "%") + }); + break; + case "lr": + e.applyStyles({ + left: e.formatStyle(s, "%") + }) + } + l = ["+y", "-y", "+x", "-x"], "+y" === n.axis ? l = ["+y", "-y", "+x", "-x"] : "-y" === n.axis && (l = ["-y", "+y", "+x", "-x"]), r = new bp(e) + } + const d = function(r, n) { + let s; + for (let i = 0; i < n.length; i++) { + r.moveIfOutOfBounds(a, n[i]); + let e = 0, + t = !1; + for (; r.overlapsAny(o) && !(9 < e);) t ? r.move(n[i]) : (o && 0 < o.length && (s = s || { + topMostBoxPosition: bp.getBoxPosition(o, "top"), + bottomMostBoxPosition: bp.getBoxPosition(o, "bottom"), + leftMostBoxPosition: bp.getBoxPosition(o, "left"), + rightMostBoxPosition: bp.getBoxPosition(o, "right") + }, bp.moveToMinimumDistancePlacement(r, n[i], s)), t = !0), e++ + } + return r + }(r, l); + e.move(d.toCSSCompatValues(a)) + } + } + w.BoxPosition = bp; + class Tp { + constructor(e, t, i = !0) { + if (!e) return null; + this.window = e, this.overlay = t, this.loggingEnabled = i, this.foregroundStyleOptions = { + fontFamily: "Helvetica", + fontSize: "36px", + color: "rgba(255, 255, 255, 1)", + textShadow: "", + backgroundColor: "rgba(0, 0, 0, 0)" + }, this.backgroundStyleOptions = { + backgroundColor: "rgba(0, 0, 0, 0.5)" + }, this.globalStyleCollection = {}; + const r = e.document.createElement("div"); + r.style.position = "absolute", r.style.left = "0", r.style.right = "0", r.style.top = "0", r.style.bottom = "0", r.style.margin = "1.5%", this.paddedOverlay = r, t.appendChild(this.paddedOverlay), this.initSubtitleCSS() + } + initSubtitleCSS() { + var e = [new fp.VTTCue(0, 0, "String to init CSS - Won't be visible to user")]; + this.paddedOverlay.style.opacity = "0", this.processCues(e), this.processCues([]), this.paddedOverlay.style.opacity = "1" + } + convertCueToDOMTree(e) { + return e ? mp.default.parseContent(this.window, e, this.globalStyleCollection) : null + } + setStyles(i) { + function r(e, t, i) { + for (const r in t) t.hasOwnProperty(r) && (!0 === i && void 0 !== e[r] || !1 === i) && (e[r] = t[r]) + } + for (const a in i) { + let t = !1, + e = null; + "::cue" === a ? (e = this.foregroundStyleOptions, t = !0) : "::-webkit-media-text-track-display" === a && (e = this.backgroundStyleOptions, t = !0); + var n = i[a]; + if (!0 === t) r(e, n, t); + else + for (let e = 0; e < gp.length; e++) { + var s = gp[e].exec(a); + if (s && 4 === s.length) { + const i = s[2], + o = {}; + r(o, n, t), this.globalStyleCollection[i] = o + } + } + } + this.initSubtitleCSS(), this.loggingEnabled && (console.log("WebVTTRenderer setStyles foregroundStyleOptions: " + JSON.stringify(this.foregroundStyleOptions)), console.log("WebVTTRenderer setStyles backgroundStyleOptions: " + JSON.stringify(this.backgroundStyleOptions)), console.log("WebVTTRenderer setStyles globalStyleCollection: " + JSON.stringify(this.globalStyleCollection))) + } + processCues(r) { + if (r) { + for (; this.paddedOverlay.firstChild;) this.paddedOverlay.removeChild(this.paddedOverlay.firstChild); + if (function(t) { + for (let e = 0; e < t.length; e++) + if (t[e].hasBeenReset || !t[e].displayState) return 1 + }(r)) { + const n = [], + s = bp.getSimpleBoxPosition(this.paddedOverlay); + 1 < r.length && (r = function(t) { + const i = []; + let r = 0; + for (let e = 0; e < t.length; e++) { + var n = t[e]; + if ("number" != typeof n.line) return t; + r += n.line, i.push(n) + } + return r /= t.length, 50 < r ? (i.forEach(function(e) { + e.axis = "-y" + }), i.sort((e, t) => t.line - e.line)) : (i.forEach(function(e) { + e.axis = "+y" + }), i.sort((e, t) => e.line - t.line)), i + }(r)); + for (let i = 0; i < r.length; i++) { + let e = r[i], + t = new Sp(this.window, e, this.foregroundStyleOptions, this.backgroundStyleOptions, this.globalStyleCollection); + this.paddedOverlay.appendChild(t.div), bp.moveBoxToLinePosition(t, s, n), e.displayState = t.div, n.push(bp.getSimpleBoxPosition(t)) + } + } else + for (let e = 0; e < r.length; e++) this.paddedOverlay.appendChild(r[e].displayState) + } + } + setSize(e, t) { + e && (this.overlay.style.width = e + "px"), t && (this.overlay.style.height = t + "px") + } + getOverlay() { + return this.overlay + } + } + + function Ep(e) { + for (var t in e) pp.hasOwnProperty(t) || (pp[t] = e[t]) + } + w.default = Tp, w.WebVTTRenderer = Tp, pp = Yh, Object.defineProperty(pp, "__esModule", { + value: !0 + }), Ep(iu), Ep(w); + + function Ip(e, t, i) { + return e.substr(i || 0, t.length) === t + } + + function wp(e) { + let t = 5381, + i = e.length; + for (; i;) t = 33 * t ^ e.charCodeAt(--i); + return (t >>> 0).toString() + } + + function Ap(e) { + var t = Math.floor(e), + i = t + .5, + r = t + 1; + return i <= e ? r - e <= e - i ? r : i : i - e <= e - t ? i : t + } + + function Op(e, t = 0, i = 8589934592) { + if (!Number.isFinite(t)) return e; + var r = i / 2, + n = Math.abs(e - t) % i; + return t + (t < e ? -1 : 1) * (r < n ? i - n : -n) + } + var kp, Cp, Dp, Mp = function(e, t, i, r, n, s, a, o) { + const l = O.utf8arrayToStr(new Uint8Array(e)).trim().replace(/\r\n|\n\r|\n|\r/g, "\n").split("\n"), + d = { + baseTime: Math.floor(9e4 * t.baseTime / t.timescale), + timescale: 9e4 + }; + let u = 0, + c = 0; + const h = []; + let p = null, + f = !0; + const m = new Yh.WebVTTParser(window, Yh.WebVTTParser.StringDecoder(), a); + m.oncue = function(e) { + var t = S({ + baseTime: Op(Op(u) - d.baseTime, 9e4 * i), + timescale: 9e4 + }); + e.startTime = Op(e.startTime + t - c, 0, 95443.7176888889), e.endTime = Op(e.endTime + t - c, 0, 95443.7176888889), e.id = wp(Ap(e.startTime).toString()) + wp(Ap(e.endTime - e.startTime).toString()) + wp(e.text), e.text = decodeURIComponent(encodeURIComponent(e.text)), 0 < e.endTime && h.push(e) + }, m.onparsingerror = function(e) { + p = e + }, m.onflush = function() { + p && s ? s(p) : n(h) + }, l.forEach(a => f && Ip(a, "X-TIMESTAMP-MAP=") ? (f = !1, void a.substr(16).split(",").forEach(e => { + if (Ip(e, "LOCAL:")) { + let t; + try { + t = (i = e.substr(6), r = parseInt(i.substr(-3)), n = parseInt(i.substr(-6, 2)), s = parseInt(i.substr(-9, 2)), i = 9 < i.length ? parseInt(i.substr(0, i.indexOf(":"))) : 0, ne(r) && ne(n) && ne(s) && ne(i) ? (r += 1e3 * n, r += 6e4 * s, r += 36e5 * i) : -1) + } catch (e) { + t = -1 + } - 1 !== t ? c = t / 1e3 : p = new Error(`Malformed X-TIMESTAMP-MAP: ${a}`) + } else Ip(e, "MPEGTS:") && (u = parseInt(e.substr(7))); + var i, r, n, s + })) : void m.parse(a + "\n")), m.flush() + }, + xp = { + newCue: function(e, t, i, r, n) { + let s, a, o, l, d; + var u, c, h = { + foreground: !1, + background: !1, + italics: !1, + underline: !1, + flash: !1, + styleStack: [] + }; + for ([u, c] of r.rows.entries()) + if (a = !0, o = 0, l = "", !c.isEmpty()) { + for (let e = 0; e < c.chars.length; e++) c.chars[e].uchar.match(/\s/) && a ? o++ : (l += this.getFormattedChar(c.chars[e], h), a = !1); + (c.cueStartTime = t) === i && (i += 1e-4), l = l.trim().replace(//gi, "\n"), l += this.closeStyles(h), s = new Yh.VTTCue(t, i, l), 16 <= o ? o-- : o++, d = !navigator.userAgent.match(/Firefox\//) && 7 < u ? u : u + 1, s.snapToLines = !1, s.line = 10 + 5.33 * d, s.align = "left", s.position = this.getPosition(o, n), e.addCue(s) + } + }, + getPosition: function(e, t) { + let i = 1.3333333333333333; + t && t.offsetWidth && t.offsetHeight && 1.6 <= t.offsetWidth / t.offsetHeight && (i = 1.7777777777777777); + let r = 10 + e / 32 * 80, + n = 10, + s = 90; + return 1.7777777777777777 === i && (r = 12.5 + .75 * r, n = 20, s = 80), Math.max(n, Math.min(s, r + (navigator.userAgent.match(/Firefox\//) ? 50 : 0))) + }, + getRootStyleTag: function(e) { + var t = e[0]; + return "c" === t ? t : e + }, + closeStyles: function(t) { + let i = ""; + for (let e = t.styleStack.length - 1; 0 <= e; --e) i += "", t.styleStack.pop(); + return i + }, + beginStyleAndBalance: function(e, t) { + let i = ""; + return "c" === t[0] && (i += this.closeStyleAndBalance(e, "c")), i += "<" + t + ">", e.styleStack.push(t), i + }, + closeStyleAndBalance: function(t, i) { + var r = t.styleStack.length; + let n = 0, + s = ""; + for (let e = r - 1; 0 <= e; --e) { + var a = t.styleStack[e], + o = this.getRootStyleTag(a); + if (i[0] === a[0]) { + s += "", t.styleStack.splice(r - 1 - n); + break + } + "c" === o[0] ? (t.background = "", t.foreground = "", t.flash = !1, s += "") : "u" === o[0] ? (t.underline = !1, s += "") : "i" === o[0] && (t.italics = !1, s += ""), n++ + } + return s + }, + getFormattedChar: function(e, t) { + let i = "", + r = e.uchar, + n = ""; + var s = e.penState.foreground !== t.foreground, + a = e.penState.background !== t.background, + o = e.penState.flash !== t.flash; + return (s || a || o) && (n = "." + e.penState.foreground, n += ".bg_" + e.penState.background, e.penState.flash && o && (n += ".blink"), e.penState.foreground || e.penState.background || e.penState.blink ? i += this.beginStyleAndBalance(t, "c" + n) : i += this.closeStyleAndBalance(t, "c"), s && (t.foreground = e.penState.foreground), a && (t.background = e.penState.background), o && (t.flash = e.penState.flash)), e.penState.underline !== t.underline && (i += e.penState.underline ? this.beginStyleAndBalance(t, "u") : this.closeStyleAndBalance(t, "u"), t.underline = e.penState.underline), e.penState.italics !== t.italics && (i += e.penState.italics ? this.beginStyleAndBalance(t, "i") : this.closeStyleAndBalance(t, "i"), t.italics = e.penState.italics), i + r + } + }; + (w = kp = kp || {}).CloseEnough = "CloseEnough", w.TooFar = "TooFar", w.Unknown = "Unknown"; + const Pp = e => "cc1" === e || "cc2" === e; + + function Rp(t) { + const i = []; + for (let e = 0; e < t.length; e++) { + var r = t[e]; + ("captions" === r.kind || "subtitles" === r.kind || "metadata" === r.kind && r.customTextTrackCueRenderer) && i.push(r) + } + return i + } + + function Lp(e) { + if (e && e.cues) + for (; 0 < e.cues.length;) e.removeCue(e.cues[0]) + } + class _p extends $t { + constructor(e, t, i, r) { + super(e => { + const t = Oc(this.hls, this); + if (e.add(t.event(x.INLINE_STYLES_PARSED, this.onInlineStylesParsed).pipe(Vs(() => this.destroy())).subscribe()), e.add(bn(0, this.config.trottleCheckInterval).pipe(La(() => (this.checkReadyToLoadNextSubtitleFragment(), Wu))).subscribe()), this.config.nativeTextTrackChangeHandling) + if (this.mediaSink.textTracks && "onchange" in this.mediaSink.textTracks) { + const t = Oc(this.mediaSink.textTracks, this); + e.add(t.event("change", this._onTextTracksChanged).subscribe()) + } else e.add(bn(0, 500).pipe(La(() => (this._onTextTracksChanged(), Wu))).subscribe()) + }), this.config = t, this.hls = i, this.logger = r.child({ + name: "legible" + }), this.mediaSink = e, this.id3Track = e.id3TextTrack, this.enableCaption = !0, this.Cues = xp, this.tracks = [], this.cueRanges = [], this.channelToTrackMap = {}, this.htmlTextTrackMap = new Map, this.lastCueEndTime = 0, this.gotTracks = !1, this.tryAgain$ = new yi(!0), this.needNextSubtitle$ = new yi(!0) + } + destroy() { + Lp(this.textTrack1), Lp(this.textTrack2), this.mediaSink = void 0, this.nativeSubtitleTrackChange$ = void 0 + } + convertCuesIntoSubtitleFragInfo(t) { + const i = {}; + if (null != t && 0 < t.length) + for (let e = 0; e < t.length; e++) { + var r = t[e]; + if (ne(r.fragSN)) { + const n = i[r.fragSN]; + n ? (n.count++, n.startTime = Math.min(r.startTime, n.startTime), n.endTime = Math.max(r.endTime, n.endTime)) : i[r.fragSN] = { + count: 1, + startTime: r.startTime, + endTime: r.endTime + } + } + } + return i + } + checkReadyToLoadNextSubtitleFragment() { + let e = !1; + this.mediaSink.mediaQuery.currentTime >= this.lastCueEndTime - this.config.subtitleLeadTime && (e = !0), this.needNextSubtitle$.next(e) + } + checkReadyToLoadNextSubtitleFragment$(e, t) { + return e.mediaSeqNum === (null === (t = t[0]) || void 0 === t ? void 0 : t.mediaSeqNum) ? $i(!0) : (this.checkReadyToLoadNextSubtitleFragment(), this.needNextSubtitle$) + } + getNextFragment(e, t) { + t = t.mediaSeqNum + 1; + return t < e.fragments.length ? e.fragments[t - e.startSN] : null + } + calculateFragInfoMap(e, t, i, r) { + var n = this.convertCuesIntoSubtitleFragInfo(t); + let s = { + len: 0, + start: e, + end: e + }, + a = e, + o = e, + l = null, + d = null; + for (const t in n) + if (Object.prototype.hasOwnProperty.call(n, t)) { + var u = Number(t); + if (ne(u)) { + var c = n[u]; + if (this.isFragmentCompleteOrEmpty(u, c.count, i)) + if (u === r.startSN && e < c.startTime && (a = o = s.start = s.end = e = c.startTime), e >= c.startTime && (a === e || ne(l) && 1 < u - l) && (a = o = c.startTime), e >= c.startTime) o = c.endTime, l = u; + else { + if (!ne(l) || u - l != 1) { + d = u; + break + } + o = c.endTime, l = u + } + } else this.logger.warn(`$fragInfoMap has invalid key ${u}`) + } return s = { + len: o - a, + start: a, + end: o + }, { + fragInfoMap: n, + bufferInfo: s, + prevFragSN: l, + nextFragSN: d + } + } + findFrags$(t, i) { + return this.tryAgain$.pipe(ji(tr), La(() => { + var e = this.findFragmentsForPosition(this.mediaSink.mediaQuery.currentTime, i, t); + return e.foundFrags ? (this.lastCueEndTime = 0, this.needNextSubtitle$.next(!0), $i(e)) : Ii + })) + } + reviewParsedFrag(e, i, r) { + var n = e.frag, + s = e.cueRange, + t = i.subtitleBufferInfo, + a = i.subtitleParsedInfo, + o = this.mediaSink.mediaQuery.currentTime, + e = i.foundFrags; + let l = !0; + if (n.mediaSeqNum === e[0].mediaSeqNum) { + if (!i.timelineEstablished) return kp.TooFar; + if (!s) return this.logger.warn(`[subtitle] 1st frag sn ${n.mediaSeqNum} has no cue; details ${r.fragments.length} frags`), kp.Unknown; + if (s.startTime < o) { + const d = r.fragments, + i = n.mediaSeqNum - r.startSN; + let e = i, + t = s.startTime; + for (; e < d.length && (t += d[e].duration, !(t >= o)); ++e); + l = e - i + 1 <= this.config.earlyFragTolerance + } else if (s.startTime > o && n.mediaSeqNum !== r.startSN) { + const d = s.startTime - o, + i = t.prevFragSN; + l = n.mediaSeqNum === i + 1 && (null === (t = t.fragInfoMap[i]) || void 0 === t ? void 0 : t.count) === (null === (a = a[i]) || void 0 === a ? void 0 : a.count) || d <= this.config.lateTolerance + } + } + return l ? kp.CloseEnough : kp.TooFar + } + isFragmentEmpty(e) { + return e && !ne(e.startTime) && 0 === e.count + } + isFragmentCompleteOrEmpty(e, t, i) { + e = i ? i[e] : null; + return (null == e ? void 0 : e.count) === t || this.isFragmentEmpty(e) + } + getEarlierFragmentInSameDisco(e, t, i) { + var r = t.mediaSeqNum - e.startSN - 1; + if (r < 0 || r > e.fragments.length - 1) return this.logger.error(`[subtitle] getEarlierFragmentInSameDisco index ${r} out of range`), t; + r = e.fragments[r]; + return r && r.discoSeqNum === t.discoSeqNum && !i[t.mediaSeqNum] ? r : t + } + inferSubtitleFragmentForPosition(i, r, t, n, s) { + let a, o, e, l, d; + if (ne(n.prevFragSN) && (o = n.prevFragSN - s.startSN, e = t[n.prevFragSN]), ne(n.nextFragSN) && (l = n.nextFragSN - s.startSN, d = t[n.nextFragSN]), ne(o) && 0 <= o && o < s.fragments.length && e) { + let t = e.startTime; + const n = ne(l) ? l : s.fragments.length; + for (let e = o; e < n; ++e) { + const l = s.fragments[e]; + if (!ne(r) || l.discoSeqNum === r) { + if (e === n - 1) { + a = { + foundFrag: l, + timelineEstablished: !0 + }; + break + } + if (t + l.duration > i && e > o) { + a = { + foundFrag: l, + timelineEstablished: !0 + }; + break + } + t += l.duration + } + } + } else if (ne(l) && 0 <= l && l < s.fragments.length && d) { + let t = d.startTime; + for (let e = l - 1; 0 <= e; --e) { + const o = s.fragments[e]; + if (!ne(r) || o.discoSeqNum === r) { + if (t <= i) { + a = { + foundFrag: o, + timelineEstablished: !0 + }; + break + } + t -= o.duration + } + } + } else + for (let e = 0; e < s.fragments.length; ++e) { + const t = s.fragments[e]; + if (ne(r) && t.discoSeqNum === r) { + a = { + foundFrag: t, + timelineEstablished: !1 + }; + break + } + } + return a + } + generateFragmentBatch(t, i, e, r, n, s) { + var a; + const o = [], + l = null == e ? void 0 : e.foundFrag; + if (!l) return { + foundFrags: void 0, + subtitleParsedInfo: void 0, + subtitleBufferInfo: void 0, + timelineEstablished: null == e ? void 0 : e.timelineEstablished + }; + for (let e = l ? l.mediaSeqNum - s.startSN : s.fragments.length; e < s.fragments.length && o.length < t; ++e) { + const t = s.fragments[e]; + if (t.discoSeqNum === i) { + const l = null === (a = n.fragInfoMap[t.mediaSeqNum]) || void 0 === a ? void 0 : a.count; + this.isFragmentCompleteOrEmpty(t.mediaSeqNum, null != l ? l : 0, r) || o.push(t) + } + } + return { + foundFrags: o, + subtitleParsedInfo: r, + subtitleBufferInfo: n, + timelineEstablished: null == e ? void 0 : e.timelineEstablished + } + } + findFragmentsForPosition(e, t, i) { + var r = this.mediaSink.mediaQuery.getParsedSubtitleRecordsForMediaOption(this.selectedTrack.persistentID), + n = this.getCuesOfEnabledTrack(this.selectedMediaOption.mediaOptionId, !1), + s = this.calculateFragInfoMap(e, n, r, i), + n = s.bufferInfo, + n = Math.max(e, n.end), + n = this.inferSubtitleFragmentForPosition(n, t, r, s, i); + return this.generateFragmentBatch(1 / 0, t, n, r, s, i) + } + get selectedMediaOption() { + return this.selectedTrack || this._disabledMediaOption + } + set selectedMediaOption(e) { + this.selectedTrack = "groupId" in e ? e : void 0 + } + get selectedTrack() { + return this._selectedMediaOption + } + set selectedTrack(e) { + e !== this._selectedMediaOption && (this._selectedMediaOption = e, this.updateTextTrackState()) + } + getTrack(t) { + return this._availableMediaOptions.find(e => e.mediaOptionId === t) + } + updateTextTrackState() { + if (this.mediaSink.textTracks) { + const i = this.selectedTrack ? this.getExistingHTMLTextTrack(this.selectedTrack) : void 0, + r = Rp(this.mediaSink.textTracks); + for (let e = 0; e < r.length; e++) { + var t = r[e]; + t === i && "showing" !== r[e].mode ? r[e].mode = "showing" : t !== i && "hidden" !== r[e].mode && (r[e].mode = "hidden") + } + } + } + mapHTMLTextTrackIndexToMediaOptionId(e) { + const i = this.mediaSink.textTracks[e]; + let r; + return this.htmlTextTrackMap.forEach((e, t) => { + i === e && (r = t) + }), r + } + get mediaSelectionOptions() { + return this._availableMediaOptions + } + _makeDisableOption(e) { + return { + itemId: e.itemId, + mediaOptionType: e.mediaOptionType, + mediaOptionId: "Nah" + } + } + _onTextTracksChanged() { + if (this.mediaSink) { + let t, i = !1; + const r = Rp(this.mediaSink.textTracks); + for (let e = 0; e < r.length; e++) r[e].seen ? "showing" === r[e].mode && (t = r[e].persistentId) : (r[e].seen = !0, i = !0); + if (!i) { + const e = this.selectedTrack; + if ((null == e ? void 0 : e.persistentID) !== t) { + const e = this.mediaSelectionOptions.find(function(e) { + return e.persistentID === t + }); + this.nativeSubtitleTrackChange$.next(e || this._disabledMediaOption) + } + } + } + } + addCues(e, t, i, r) { + const n = this.cueRanges; + let s = !1; + for (let e = n.length; e--;) { + const r = n[e], + d = (a = r[0], o = r[1], l = t, Math.min(o, i) - Math.max(a, l)); + if (0 <= d && (r[0] = Math.min(r[0], t), r[1] = Math.max(r[1], i), s = !0, .5 < d / (i - t))) return + } + var a, o, l; + s || n.push([t, i]), this.Cues.newCue(this.channelToTrackMap[e], t, i, r, this.mediaSink) + } + getExistingHTMLTextTrackWithChannelNumber(t) { + var i = this.mediaSink; + if (i) + for (let e = 0; e < i.textTracks.length; e++) { + var r = i.textTracks[e], + n = "cc" + t; + if (Pp(n) && !0 === r[n]) return r + } + return null + } + sendAddTrackEvent(e, t) { + let i = null; + try { + i = new window.Event("addtrack") + } catch (e) { + i = document.createEvent("Event"), i.initEvent("addtrack", !1, !1) + } + i.track = e, t.dispatchEvent(i) + } + createHTMLCaptionsTrackGuts(e, t, i, r) { + var n = "cc" + e; + if (!this.channelToTrackMap[n]) { + e = this.getExistingHTMLTextTrackWithChannelNumber(e); + if (e) this.channelToTrackMap[n] = e, Lp(this.channelToTrackMap[n]), this.sendAddTrackEvent(this.channelToTrackMap[n], this.mediaSink); + else { + const s = this.createHTMLTextTrackGuts("captions", t, i, r); + s && Pp(n) && (s[n] = !0, this.channelToTrackMap[n] = s) + } + } + return this.channelToTrackMap[n] + } + createHTMLCaptionsTrack(e) { + return this.createHTMLCaptionsTrackGuts(e, this.config[1 === e ? "captionsTextTrack1Label" : "captionsTextTrack2Label"], this.config.captionsTextTrack1LanguageCode, !1) + } + getExistingHTMLTextTrack(e) { + return this.config.condenseSubtitleTrack ? this.htmlTextTrackMap.get(e.persistentID) : this.htmlTextTrackMap.get(e.id) + } + getExistingHTMLTextTrackWithSubtitleTrackId(t) { + var e = this._availableMediaOptions.find(e => e.id === t); + return e ? this.getExistingHTMLTextTrack(e) : void 0 + } + getExistingHTMLTextTrackIndex(e) { + var t = this.getExistingHTMLTextTrack(e), + i = this.mediaSink.textTracks; + let r = -1; + for (let e = 0; e < i.length; ++e) + if (i[e] === t) { + r = e; + break + } return r + } + setExistingHTMLTextTrack(e, t) { + return t.persistentId = e.persistentID, this.config.condenseSubtitleTrack ? this.htmlTextTrackMap.set(e.persistentID, t) : this.htmlTextTrackMap.set(e.id, t) + } + createHTMLTextTrack(t) { + let i = this.getExistingHTMLTextTrack(t); + if (i) this.tracksReused += 1; + else { + if ("sbtl" === t.mediaType) this.subtitleTracksCreated += 1, i = this.createHTMLTextTrackGuts("subtitles", t.name, t.lang, t.forced); + else { + let e = 1; + t.inStreamID && (e = Number(t.inStreamID.substring(2))), this.captionTracksCreated += 1, i = this.createHTMLCaptionsTrackGuts(e, t.name, t.lang, !1) + } + i ? this.setExistingHTMLTextTrack(t, i) : (this.logger.error(`failed to create HTML text track for track ${t.id}: persistent id ${t.persistentID} name ${t.name} lang ${t.lang} inStreamID ${t.inStreamID}`), this.tracksFailed += 1) + } + return i + } + createHTMLTextTrackGuts(t, i, r, e) { + const n = this.mediaSink; + if (n) { + let e = !1; + "metadata" !== t && this.config.customTextTrackCueRenderer && (e = !0, t = "metadata"); + const s = n.addTextTrack(t, i, r); + return e && (s.customTextTrackCueRenderer = !0), s + } + } + resetLoadSource() { + this.resetTracks() + } + resetTracks() { + this._cleanTracks(), this.cueRanges = [] + } + _cleanTracks() { + var e = this.mediaSink; + if (e) { + var t = e.textTracks; + if (t) + for (let e = 0; e < t.length; e++) Lp(t[e]) + } + } + getCuesOfEnabledTrack(e, t = !1) { + let i = []; + if (t) { + const t = this._getCuesOfEnabledTrack(e); + for (let e = 0; e < t.length; e++) { + var r = t[e]; + Boolean(r.webVTTCue) && i.push(r) + } + } else i = this._getCuesOfEnabledTrack(e); + return i + } + _getCuesOfEnabledTrack(e) { + e = this.getTrack(e), e = this.config.condenseSubtitleTrack ? null == e ? void 0 : e.persistentID : null == e ? void 0 : e.id, e = this.htmlTextTrackMap.get(e); + return e && e.cues ? Array.from(e.cues) : [] + } + attachSubtitleTracks() { + this.gotTracks && (this.subtitleTracksCreated = 0, this.captionTracksCreated = 0, this.tracksReused = 0, this.tracksFailed = 0, this.tracks.forEach(e => { + this.createHTMLTextTrack(e) + })) + } + setTracks(e, t, i) { + this._cleanTracks(), this.htmlTextTrackMap = new Map, this.cueRanges = [], this.config.enableWebVTT && (this.tracks = e || []), this.gotTracks = !0, this._availableMediaOptions = e, this._disabledMediaOption = i, this.attachSubtitleTracks(), this.selectedTrack = t, this.nativeSubtitleTrackChange$ = new Xt, this.mediaSink.textTracksCreated = !0 + } + onInlineStylesParsed(e) {} + processSubtitleFrag(e, t, i, r) { + var n = new Uint8Array(r), + e = this.getExistingHTMLTextTrackIndex(e); + if (t && r.byteLength) { + const s = this._parseVTTs(e, t, i, n); + return s && ne(s.startTime) && (this.lastCueEndTime = Math.max(this.lastCueEndTime, s.endTime)), s + } + } + _parseVTTs(r, n, e, t) { + let s; + return Mp(t, e, n.start, n.discoSeqNum, e => { + const t = this.mediaSink.textTracks[r], + i = { + count: 0, + startTime: Number.POSITIVE_INFINITY, + endTime: 0 + }; + e.map(e => { + !t || t.cues && t.cues.getCueById(e.id) || (e.fragSN = n.mediaSeqNum, e.webVTTCue = !0, t.addCue(e), i.count++), i.startTime = Math.min(e.startTime, i.startTime), i.endTime = Math.max(e.endTime, i.endTime) + }), s = i, this.mediaSink.archiveParsedSubtitleFragmentRecord(this.selectedTrack.persistentID, n.mediaSeqNum, i) + }, e => {}, e => { + this.hls.trigger(x.INLINE_STYLES_PARSED, { + styles: e + }) + }, this.logger), s + } + _ensureParser() { + var e, t; + this.cea608Parser || (e = new Xh(this, 1), t = new Xh(this, 2), this.cea608Parser = new zh(0, e, t)) + } + setupForFrag(e) { + e && e.mediaOptionType === gu.Variant && !e.iframe && ((e = e.mediaSeqNum) !== this.lastVariantSeqNum + 1 && this.resetClosedCaptionParser(), this.lastVariantSeqNum = e) + } + resetClosedCaptionParser() { + var e; + null === (e = this.cea608Parser) || void 0 === e || e.reset() + } + addLegibleSamples(e, t, i, r) { + t && this.addClosedCaptionSamples(e, t), i && 0 < i.length && this.addId3Samples(e, i, S(r)) + } + addClosedCaptionSamples(e, t) { + t.mp4 ? this.addMP4CaptionSamples(e, t.mp4) : t.ts && this.addTSCaptionSamples(e, t.ts) + } + addMP4CaptionSamples(e, i) { + if (this.enableCaption && this.config.enableCEA708Captions) { + var r = S(e); + this._ensureParser(); + for (let e = 0; e < i.length; e++) { + let t = i[e].pts - r; + var n = i[e].bytes; + for (let e = 0; e < n.length; e += 2) { + const i = []; + i.push(n[e]), e + 1 < n.length ? i.push(n[e + 1]) : i.push(80), this.cea608Parser.addData(t, i), t += .03336666666666667 + } + } + } + } + addTSCaptionSamples(e, t) { + if (this.enableCaption && this.config.enableCEA708Captions) { + var i = S(e); + this._ensureParser(); + for (let e = 0; e < t.length; e++) { + var r = t[e].pts - i, + n = _p.extractCea608Data(t[e].bytes); + this.cea608Parser.addData(r, n) + } + } + } + addId3Samples(e, t, r) { + if (this.config.enableID3Cues) { + const n = window.WebKitDataCue || window.VTTCue || window.TextTrackCue, + s = S(e); + for (let e = 0; e < t.length; e++) { + const a = t[e].pts - s; + let i = (e < t.length - 1 ? t[e + 1].pts : r) - s; + a === i && (i += 1e-4), t[e].frames && t[e].frames.forEach(e => { + if (e && !this.id3shouldIgnore(e)) { + const t = new n(a, i, ""); + t.value = e, this.id3Track.addCue(t) + } + }) + } + } + } + id3shouldIgnore(e) { + return "PRIV" === e.key && ("com.apple.streaming.transportStreamTimestamp" === e.info || "com.apple.streaming.audioDescription" === e.info) + } + static extractCea608Data(t) { + var i = 31 & t[0]; + let r, n, s, a = 2; + const o = []; + for (let e = 0; e < i; e++) r = t[a++], n = 127 & t[a++], s = 127 & t[a++], 0 == n && 0 == s || 0 != (4 & r) && 0 == (3 & r) && (o.push(n), o.push(s)); + return o + } + } + const Np = { + name: "plist" + }; + class Fp { + constructor(e, t, i, r) { + this.config = e, this.xhrLoader = t, this.customUrlLoader = i, this.sessionDataCheckForCompleteness = e => { + const t = this.config["sessionDataAutoLoad"], + i = Object.assign({}, e); + return e.complete || (e.itemList ? i.complete = e.itemList.every(e => t[e["DATA-ID"]] && !e.VALUE && !e._STATUS && e.URI ? (this.logger.warn(`Incomplete because ${e["DATA-ID"]} was autoloaded but no response yet`), !1) : (t[e["DATA-ID"]] && !e.URI && this.logger.warn(`id=${e["DATA-ID"]} missing uri`), !0)) : this.logger.warn("Uninitialized SessionData")), i + }, this.logger = r.child({ + name: "SessionDataLoader" + }) + } + loadSessionData(r) { + const n = this.config["sessionDataAutoLoad"], + t = r.itemList || []; + let s = $i(r); + return t.forEach(e => { + const i = e["DATA-ID"], + t = e.URI; + if (t && n[i]) { + const n = bu.buildAbsoluteURL(r.baseUrl, t, { + alwaysNormalize: !0 + }), + e = ""; + s = s.pipe(La(t => this.loadSessionDataItemWithUrl(n, i, "", this.config, t, this.xhrLoader, this.customUrlLoader).pipe(Vn(e => (this.logger.error(`Error loading SessionData > url=${n}, id=${i}, err=${e}`), $i(t)))))) + } + }), s.pipe(hr(e => { + if (t.length < 1) return e; + e = this.sessionDataCheckForCompleteness(e); + if (e.complete) return e; + throw new V(!1, "Session data not complete after loading all items", $.IncompleteSessionData) + }), Vs(() => {})) + } + loadSessionDataItemWithUrl(e, t, i, r, n, s, a) { + const o = Qe(), + l = { + url: e, + method: "GET", + responseType: i, + xhrSetup: r.xhrSetup, + mimeType: "application/xml" + }, + d = Lc({ + url: e + }, r.fragLoadPolicy); + let u; + return u = Tu(e) ? a(l, d).pipe(hr(e => this.onLoadSuccess(n, t, e.data.response.data.toString(), e.data.response.data))) : s(l, d).pipe(hr(([e]) => this.onLoadSuccess(n, t, e.response, e.responseXML))), u.pipe(Vn(e => (e instanceof dr ? e = new cc(!1, e.message, 0, $.SessionDataLoadTimeout) : e instanceof oc && (e = new cc(!1, e.message, e.code, { + code: e.code, + text: "Failed to load SessionData" + })), o.error(`Unable to load SessionData > err=${e}`), $i(this.onLoadError(n, t, e))))) + } + onLoadSuccess(e, t, i, r) { + let n = null, + s = e; + if (function(e) { + const t = /[\s]*<\?xml/i; + t.lastIndex = 0; + var i = t.exec(e); + return i || /[\s]*"), null) + } else if ("string" === i) { + const e = a[0]; + s = e ? e.nodeValue : null + } else if ("integer" === i) { + const e = a[0]; + s = e ? parseInt(e.nodeValue) : 0 + } else if ("float" === i) { + const e = a[0]; + s = e ? parseFloat(e.nodeValue) : 0 + } else if ("date" === i) { + const e = a[0]; + s = e ? new Date(e.nodeValue) : null + } else if ("data" === i) { + const e = a[0]; + s = e ? atob(e.nodeValue) : null + } else "true" === i ? s = !0 : "false" === i && (s = !1) + } else if (a.length < 1) t.warn(Np, `unknown node with unknown value > nodeType=${e.nodeType} tagName=${e.tagName} nodeName=${e.nodeName} value=${e.nodeValue}`); + else { + s = []; + for (let e = 0; e < a.length; ++e) { + const r = a[e]; + r.tagName && s.push(n(r)) + } + 1 === s.length && (s = s[0]) + } + return s + }(n); + s = this.setSessionData(e, t, "VALUE", i) + } else if (function(e) { + const t = /[\s]*[\{\[]/; + return t.lastIndex = 0, t.exec(e) + }(i)) try { + const r = JSON.parse(i); + s = this.setSessionData(e, t, "VALUE", r) + } catch (r) { + this.logger.error(`JSON parser error: ${r}`), s = this.setSessionData(e, t, "VALUE", i), s = this.setSessionData(s, t, "_STATUS", -1) + } else s = this.setSessionData(e, t, "VALUE", i); + return s + } + setSessionData(t, i, r, n) { + let s = t; + if (t.itemList) { + let e; + const a = [...t.itemList]; + for (e = 0; e < t.itemList.length; ++e) { + const t = Object.assign({}, a[e]); + if (t["DATA-ID"] === i) { + t[r] = n, a[e] = t; + break + } + } + e === t.itemList.length && this.logger.error(`Can't set ${r} of session data ${i}`), s = Object.assign(Object.assign({}, t), { + itemList: a + }) + } else this.logger.error(`Can't set ${r} on uninitialized session data`); + return s + } + onLoadError(e, t, i) { + return this.setSessionData(e, t, "_STATUS", null === (i = i.response) || void 0 === i ? void 0 : i.code) + } + } + + function Bp(e, t) { + let i, r = 0; + for (const a of e) + if (a.start <= t.endPTS && a.end > t.startPTS) { + const e = (n = t, s = a, Math.min(n.endPTS, s.end) - Math.max(n.startPTS, s.start)); + e > r && (i = a, r = e) + } else if (0 < r) break; + var n, s; + return i + } + + function Up(e) { + return null != e && "iframeMediaDuration" in e && "iframeMediaStart" in e + } + + function $p(e, t) { + return e === t || e && t && e.itemId === t.itemId && e.mediaOptionId === t.mediaOptionId && e.mediaSeqNum === t.mediaSeqNum && e.discoSeqNum === t.discoSeqNum + } + + function Vp(e) { + return JSON.stringify(e, ["mediaOptionId", "mediaSeqNum", "discoSeqNum", "start", "duration"]) + } + + function Kp() { + return e => e.pipe(ln(e => null != e), hr(e => e)) + } + + function qp(e) { + return be.isDolby(e) ? hu.DOVI : be.isHEVC(e) ? hu.HEVC : be.isVP09(e) ? hu.VP09 : be.isAVC(e) ? hu.AVC : hu.UNKNOWN + } + + function Hp(e) { + return null == e ? void 0 : e.split(".")[0] + } + + function jp(e) { + return be.isALAC(e) ? fu.ALAC : be.isFLAC(e) ? fu.FLAC : be.isEC3(e) ? fu.EC3 : be.isAC3(e) ? fu.AC3 : be.isXHEAAC(e) ? fu.XHEAAC : be.isAAC(e) ? fu.AAC : be.isMP3(e) ? fu.MP3 : fu.UNKNOWN + } + class Qp { + constructor(...e) { + this.identifier = e + } + ensureSameIdentifierLength(e) { + if (this.identifier.length !== e.identifier.length) throw new Error(`Identifiers have non-matching lengths! (${this.identifier.length} vs ${e.identifier.length})`) + } + isGreaterThan(t) { + this.ensureSameIdentifierLength(t); + for (let e = 0; e < this.identifier.length; ++e) { + if (this.identifier[e] < t.identifier[e]) return !1; + if (this.identifier[e] > t.identifier[e]) return !0 + } + return !1 + } + isEqualTo(i) { + return this.ensureSameIdentifierLength(i), this.identifier.every((e, t) => e === i.identifier[t]) + } + } + + function Wp(e) { + return ne(e) && 0 !== e && 1 !== e + } + class Gp extends Error {} + class zp { + constructor(e) { + this.value = e, this.waiters = [], this.wcounter = 0, this.rcounter = 0 + } + lock(e, t = !1) { + return this._lock(!0, e, t) + } + unlock() { + this._unlock(!0) + } + readLock(e, t = !1) { + return this._lock(!1, e, t) + } + readUnlock() { + this._unlock(!1) + } + _schedule() { + const t = []; + this.waiters = this.waiters.filter(e => !this._canLock(e.rw) || (e.rw ? ++this.wcounter : ++this.rcounter, t.push(e), !1)); + for (const e of t) e.observer.next(this.value), e.observer.complete() + } + _canLock(e) { + return e && 0 === this.wcounter && 0 === this.rcounter || !e && 0 === this.wcounter + } + _lock(i, e, r = !1) { + "boolean" == typeof e && ([r, e] = [e, void 0]); + const t = new $t(e => { + var t = this._canLock(i); + if (r && !t) throw new Gp; + t ? (i ? ++this.wcounter : ++this.rcounter, e.next(), e.complete()) : this.waiters.push({ + rw: i, + observer: e + }) + }); + return e ? t.pipe(jr(() => ((e, t, i, r) => { + if (!r) return $i(e); + let n, s; + try { + n = r(e, t) + } catch (e) { + s = Vi(() => e) + } + return s = s || (void 0 === n ? $i(e) : Fr(n)), s.pipe(Vs(i)) + })(this.value, e => { + this.value = e + }, () => this._unlock(i), e))) : t + } + _unlock(e) { + e ? this.wcounter = Math.max(this.wcounter - 1, 0) : this.rcounter = Math.max(this.rcounter - 1, 0), this._schedule() + } + } + class Xp extends $t { + constructor() { + super(e => this._count$.pipe(ln(e => 0 === e), Ds(1), Zs(void 0)).subscribe(e)), this._count$ = new yi(0) + } + wrap(e) { + return Zr(() => (this.add(), Fr(e))).pipe(Za({ + error: e => this._count$.error(e) + }), Vs(() => this.done())) + } + add(e = 1) { + this._count$.next(this._count$.value + e) + } + done(e = 1) { + this._count$.next(this._count$.value - e) + } + } + const Yp = { + isBuffered(t, i) { + for (let e = 0; t && e < t.length; e++) + if (i >= t.start(e) && i <= t.end(e)) return !0; + return !1 + }, + timeRangesToBufferedRange(t) { + const i = []; + for (let e = 0; t && e < t.length; e++) i.push({ + start: t.start(e), + end: t.end(e) + }); + return i + }, + subtitleBufferInfo(e, t, i) { + if (e) { + e = this.bufferedCues(e); + return this.getBufferedInfo(e, t, i) + } + return { + len: 0, + start: t, + end: t, + nextStart: void 0 + } + }, + fragmentsBufferedInfo(e, t, i) { + const r = []; + for (const t of e) r.push({ + start: t.start, + end: t.start + t.duration + }); + return this.getBufferedInfo(r, t, i) + }, + bufferedCues(t) { + const i = []; + if (t) + for (let e = 0; e < t.length; e++) i.push({ + start: t[e].startTime, + end: t[e].endTime + }); + return i + }, + bufferedInfoFromMedia: (e, t, i) => Yp.getBufferedInfo(Yp.timeRangesToBufferedRange(e.buffered), t, i), + getBufferedInfo(e, t, i) { + const r = []; + let n, s, a, o, l; + const d = e.map(({ + start: e, + end: t + }) => ({ + start: e, + end: t + })); + for (d.sort((e, t) => { + return e.start - t.start || t.end - e.end + }), l = 0; l < d.length; l++) { + const e = r.length; + if (e) { + const t = r[e - 1].end; + d[l].start - t < i ? d[l].end > t && (r[e - 1].end = d[l].end) : r.push(d[l]) + } else r.push(d[l]) + } + for (l = 0, n = 0, s = a = t; l < r.length; l++) { + const e = r[l]["start"], + d = r[l]["end"]; + if (t + i >= e && t < d) s = e, a = d, n = a - t; + else if (t + i < e) { + o = e; + break + } + } + return { + len: n, + start: s, + end: a, + nextStart: o + } + }, + toRangeString: e => `[${e.start.toFixed(3)},${e.end.toFixed(3)}]` + }; + (w = Cp = Cp || {}).Seek = "Seek", w.HighBuffer = "HighBuffer", w.LowBuffer = "LowBuffer", (w = Dp = Dp || {}).AlmostDry = "AlmostDry", w.LowWater = "LowWater", w.HighWater = "HighWater", w.AboveHighWater = "AboveHighWater"; + const Jp = { + [Dp.AlmostDry]: 0, + [Dp.LowWater]: 1, + [Dp.HighWater]: 2, + [Dp.AboveHighWater]: 3 + }; + + function Zp(t, e) { + return [{ + threshold: e.highWaterLevelSeconds, + level: Dp.HighWater + }, { + threshold: e.lowWaterLevelSeconds, + level: Dp.LowWater + }, { + threshold: e.almostDryWaterLevelSeconds, + level: Dp.AlmostDry + }].find(({ + threshold: e + }) => e < t) + } + + function ef(t, e) { + return [{ + threshold: e.almostDryWaterLevelSeconds, + level: Dp.AlmostDry + }, { + threshold: e.lowWaterLevelSeconds, + level: Dp.LowWater + }, { + threshold: e.highWaterLevelSeconds, + level: Dp.HighWater + }, { + threshold: 1 / 0, + level: Dp.AboveHighWater + }].find(({ + threshold: e + }) => t <= e) + } + + function tf(t, i) { + const e = ef(t.getCurrentWaterLevel(i), t.bufferMonitorInfo).level, + r = [null, null]; + return [yu.Variant, yu.AltAudio].forEach(e => { + null != t.sourceBufferEntityByType(e) && (r[e] = ef(t.getCurrentWaterLevelByType(e, i), t.bufferMonitorInfo).level) + }), { + combined: e, + sbTuple: r + } + } + + function rf(s, a) { + return ed([s.combinedBuffer$, s.gotPlaying$, s.seeking$, s.waterLevelChangedForType$(null), s.stallInfo$]).pipe(La(([e, t, i, r, n]) => 0 === e.length || !t || i || null == r || null != n ? Ii : function t(i, r, e) { + const n = i.getCurrentWaterLevel(r), + s = Zp(n, i.bufferMonitorInfo); + if (s) { + const e = s["threshold"]; + return bn(Math.ceil(1e3 * (n - e))).pipe(La(() => { + const e = Zp(i.getCurrentWaterLevel(r), i.bufferMonitorInfo); + return (null == e ? void 0 : e.level) === s.level ? t(i, r) : Wu + }), hr(() => tf(i, r))) + } + return Ii + }(s, a))) + } + class nf extends kl { + constructor(e, t) { + super(t), this.mediaElement = e + } + get mediaElementDuration$() { + return this.selectActive(({ + mediaElementDuration: e + }) => e) + } + get mediaElementDuration() { + var e; + return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.mediaElementDuration) && void 0 !== e ? e : 1 / 0 + } + get msDuration() { + var e; + return null !== (e = null === (e = this.mediaSourceEntity) || void 0 === e ? void 0 : e.duration) && void 0 !== e ? e : 1 / 0 + } + get minSBDuration() { + var e; + let i = Number.POSITIVE_INFINITY; + return null === (e = null === (e = this.mediaSourceEntity) || void 0 === e ? void 0 : e.sourceBufferEntities) || void 0 === e || e.forEach((e, t) => { + e && (i = ne(e.totalDuration) ? Math.min(i, e.totalDuration) : Number.NEGATIVE_INFINITY) + }), ne(i) ? i : 1 / 0 + } + get currentTime() { + return this.mediaElement.currentTime + } + get clientWidth() { + return this.mediaElement.clientWidth + } + get clientHeight() { + return this.mediaElement.clientHeight + } + getBufferedDuration(e = .5) { + var t = Yp.timeRangesToBufferedRange(this.mediaElement.buffered), + e = Yp.getBufferedInfo(t, this.currentTime, e); + return e.end - e.start + } + get mediaSourceEntity() { + var e; + return null === (e = this.getActive()) || void 0 === e ? void 0 : e.mediaSourceEntity + } + get msReadyState() { + var e; + return null === (e = this.mediaSourceEntity) || void 0 === e ? void 0 : e.readyState + } + get sourceBufferEntities() { + var e; + return null === (e = this.mediaSourceEntity) || void 0 === e ? void 0 : e.sourceBufferEntities + } + sourceBufferEntityByType(e) { + var t; + return null === (t = this.sourceBufferEntities) || void 0 === t ? void 0 : t[e] + } + initSegmentEntityByType(e) { + return null === (e = this.sourceBufferEntityByType(e)) || void 0 === e ? void 0 : e.initSegmentInfo + } + get maxBufferSize() { + var e = null === (e = this.sourceBufferEntities) || void 0 === e ? void 0 : e[yu.Variant]; + let t = 1 / 0; + return null != e && e.gotQuotaExceeded && (t = null !== (e = e.maxTotalBytes) && void 0 !== e ? e : 1 / 0), t + } + get postFlushSeek() { + var e; + return null === (e = this.getActive()) || void 0 === e ? void 0 : e.postFlushSeek + } + get seekable() { + return this.mediaElement.seekable + } + get desiredRate() { + var e; + return (null === (e = this.getActive()) || void 0 === e ? void 0 : e.desiredRate) || 0 + } + get desiredRate$() { + return this.selectActive(({ + desiredRate: e + }) => null != e ? e : 0) + } + get effectiveRate() { + return this.isIframeRate ? this.desiredRate : this.paused ? 0 : 1 + } + get playbackRate() { + return this.mediaElement.playbackRate + } + get isIframeRate() { + return Wp(this.desiredRate) + } + get isIframeRate$() { + return this.desiredRate$.pipe(hr(Wp)) + } + get msObjectUrl$() { + return this.selectActive(({ + mediaSourceEntity: e + }) => null == e ? void 0 : e.objectUrl).pipe(Is()) + } + get msReadyState$() { + return this.selectActive(({ + mediaSourceEntity: e + }) => { + return null !== (e = null == e ? void 0 : e.readyState) && void 0 !== e ? e : null + }) + } + get readyState() { + var e; + return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.readyState) && void 0 !== e ? e : 0 + } + get readyState$() { + return this.selectActive(({ + readyState: e + }) => null != e ? e : 0) + } + get mediaSourceEntity$() { + return this.selectActive(({ + mediaSourceEntity: e + }) => e) + } + get expectedSbCount$() { + return this.selectActive(({ + expectedSbCount: e + }) => e) + } + get expectedSbCount() { + var e; + return null === (e = this.getActive()) || void 0 === e ? void 0 : e.expectedSbCount + } + get paused$() { + return this.selectActive(({ + paused: e + }) => e) + } + get paused() { + var e; + return null === (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.paused) || void 0 === e || e + } + get playbackStarted() { + var e; + return ne(null === (e = this.getActive()) || void 0 === e ? void 0 : e.firstPlayTime) + } + get flushing$() { + return this.selectActive(({ + flushing: e + }) => e) + } + get flushing() { + var e; + return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.flushing) && void 0 !== e && e + } + get waitingForDisco$() { + return this.selectActive(({ + waitingForDisco: e + }) => e) + } + get waitingForDisco() { + var e; + return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.waitingForDisco) && void 0 !== e && e + } + get gotPlaying() { + var e; + return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.gotPlaying) && void 0 !== e && e + } + get gotPlaying$() { + return this.selectActive(({ + gotPlaying: e + }) => e) + } + get gotLoadStart$() { + return this.selectActive(({ + gotLoadStart: e + }) => e) + } + get seekTo() { + var e; + return null === (e = this.getActive()) || void 0 === e ? void 0 : e.seekTo + } + get seekTo$() { + return this.selectActive(({ + seekTo: e + }) => e) + } + get seeking() { + var e; + return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.seeking) && void 0 !== e && e + } + get seeking$() { + return this.selectActive(({ + seeking: e + }) => e) + } + get nudgeTarget$() { + return this.selectActive(({ + nudgeInfo: e + }) => null == e ? void 0 : e.nudgeTarget) + } + get nudgeCount() { + var e; + return null !== (e = null === (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.nudgeInfo) || void 0 === e ? void 0 : e.nudgeCount) && void 0 !== e ? e : 0 + } + get sourceBufferEntities$() { + return this.selectActive(({ + mediaSourceEntity: e + }) => null == e ? void 0 : e.sourceBufferEntities) + } + sourceBufferEntityByType$(t) { + return this.selectActive(({ + mediaSourceEntity: e + }) => { + return null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[t] + }) + } + bufferedSegmentsByType$(t) { + return this.selectActive(({ + mediaSourceEntity: e + }) => { + return null !== (e = null === (e = null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[t]) || void 0 === e ? void 0 : e.bufferedSegments) && void 0 !== e ? e : [] + }) + } + getBufferedSegmentsByType(e) { + return null !== (e = null === (e = this.sourceBufferEntityByType(e)) || void 0 === e ? void 0 : e.bufferedSegments) && void 0 !== e ? e : [] + } + get bufferedSegmentsTuple$() { + return ed([this.bufferedSegmentsByType$(yu.Variant), this.bufferedSegmentsByType$(yu.AltAudio)]).pipe(Gn(10)) + } + get timeupdate$() { + return Oc(this.mediaElement).event("timeupdate").pipe(ji(tr), Aa(), ao(125, void 0, { + leading: !0, + trailing: !0 + }), hr(e => this.currentTime), ln(e => ne(e))) + } + get playingEvent$() { + return Oc(this.mediaElement).event("playing").pipe(hr(() => {})) + } + get mediaElementEntity$() { + return this.selectActive(e => Boolean(e)) + } + get ended$() { + return this.selectActive(e => { + return null !== (e = null == e ? void 0 : e.ended) && void 0 !== e && e + }) + } + sbUpdating$(t) { + return this.selectActive(e => { + return null !== (e = null === (e = null === (e = null === (e = null == e ? void 0 : e.mediaSourceEntity) || void 0 === e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[t]) || void 0 === e ? void 0 : e.updating) && void 0 !== e && e + }) + } + sbUpdating(e) { + var t; + return null !== (e = null === (e = null === (t = null === (t = null === (t = this.getActive()) || void 0 === t ? void 0 : t.mediaSourceEntity) || void 0 === t ? void 0 : t.sourceBufferEntities) || void 0 === t ? void 0 : t[e]) || void 0 === e ? void 0 : e.updating) && void 0 !== e && e + } + sbError$(t) { + return this.selectActive(e => { + return null === (e = null === (e = null === (e = null == e ? void 0 : e.mediaSourceEntity) || void 0 === e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[t]) || void 0 === e ? void 0 : e.error + }) + } + get updating$() { + return ed([this.sbUpdating$(yu.Variant), this.sbUpdating$(yu.AltAudio)]).pipe(hr(e => e.some(e => e))) + } + get bufferedRangeTuple$() { + return ed([this.selectActive(e => { + return null !== (e = null === (e = null === (e = null === (e = null == e ? void 0 : e.mediaSourceEntity) || void 0 === e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[gu.Variant]) || void 0 === e ? void 0 : e.bufferedRanges) && void 0 !== e ? e : null + }), this.selectActive(e => { + return null !== (e = null === (e = null === (e = null === (e = null == e ? void 0 : e.mediaSourceEntity) || void 0 === e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[gu.AltAudio]) || void 0 === e ? void 0 : e.bufferedRanges) && void 0 !== e ? e : null + })]) + } + getBufferedRangeByType(e) { + return null !== (e = null === (e = this.sourceBufferEntities[e]) || void 0 === e ? void 0 : e.bufferedRanges) && void 0 !== e ? e : [] + } + get combinedBuffer$() { + return this.selectActive(e => { + return null !== (e = null == e ? void 0 : e.bufferedRanges) && void 0 !== e ? e : [] + }) + } + getBufferInfo(n, s) { + var e; + const t = null === (e = this.sourceBufferEntities) || void 0 === e ? void 0 : e.map(e => null == e ? void 0 : e.bufferedRanges), + i = { + buffered: { + start: n, + end: n, + len: 0 + }, + bufferedSegments: [] + }, + a = [i, i]; + return t && t.forEach((e, t) => { + if (e) { + const i = Yp.getBufferedInfo(e, n, s), + r = (null !== (e = this.sourceBufferEntities[t].bufferedSegments) && void 0 !== e ? e : []).filter(e => !(e.endPTS < i.start || e.startPTS > i.end)); + a[t] = { + buffered: i, + bufferedSegments: r + } + } + }), a + } + getCombinedBufferInfo(e, t) { + var i = this.getActive(); + return i ? Yp.getBufferedInfo(i.bufferedRanges, e, t) : null + } + get bufferMonitorInfo() { + var e; + return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.bufferMonitorInfo) && void 0 !== e ? e : null + } + get bufferMonitorThresholds$() { + return this.selectActive(e => { + var t = null == e ? void 0 : e.bufferMonitorInfo; + if (!t) return null; + var { + almostDryWaterLevelSeconds: i, + lowWaterLevelSeconds: r, + highWaterLevelSeconds: e, + maxBufferSeconds: t + } = t; + return { + almostDryWaterLevelSeconds: i, + lowWaterLevelSeconds: r, + highWaterLevelSeconds: e, + maxBufferSeconds: t + } + }).pipe(Is((e, t) => (null == e ? void 0 : e.lowWaterLevelSeconds) === (null == t ? void 0 : t.lowWaterLevelSeconds))) + } + get waterLevelType$() { + return this.selectActive(e => { + return null !== (e = null == e ? void 0 : e.bufferMonitorInfo.waterLevelType) && void 0 !== e ? e : null + }) + } + waterLevelForType(e) { + var t; + return null !== (e = null === (t = null === (t = this.getActive()) || void 0 === t ? void 0 : t.bufferMonitorInfo.waterLevelType) || void 0 === t ? void 0 : t.sbTuple[e]) && void 0 !== e ? e : null + } + waterLevelChangedForType$(t) { + return this.waterLevelType$.pipe(hr(e => null == e ? null : null == t ? e.combined : e.sbTuple[t])) + } + get fellBelowLowWater$() { + return this.waterLevelChangedForType$(yu.Variant).pipe(ha(), hr(([e, t]) => function(e, t) { + return Jp[e] > Jp[t] + }(e, t) && (t === Dp.LowWater || t === Dp.AlmostDry)), bo(this.seekTo$, this.waitingForDisco$), hr(([e, t, i]) => e && !ne(null == t ? void 0 : t.pos) && !i), Ra(!1)) + } + isBufferedToEnd$(s, a = !0) { + return ed([this.combinedBuffer$, this.selectActive(e => e.bufferMonitorInfo).pipe(Kp(), hr(e => a ? e.almostDryWaterLevelSeconds : Math.max(e.almostDryWaterLevelSeconds, e.lowWaterLevelSeconds / 2))), this.seeking$]).pipe(hr(([e, t]) => { + var i = this.minSBDuration; + if (!e || !ne(i) && a) return !1; + e = Yp.getBufferedInfo(e, this.currentTime, s).end; + let r, n; + return n = a ? (r = i, Math.abs(r - e) <= t) : (r = this.mediaElementDuration, r - e <= t), n + }), Is()) + } + needData$(e, n = !1) { + var t = !n; + return ed([this.msReadyState$, this.waterLevelChangedForType$(null), this.isBufferedToEnd$(e, t), this.bufferedRangeTuple$, this.seekTo$, this.mediaElementDuration$]).pipe(Gn(10), hr(([e, t, i, , r]) => { + if ("closed" === e) return !1; + if (n) return !0; + i = null == t || !i && t !== Dp.AboveHighWater, r = this.isIframeRate || !!r; + return i || t !== Dp.AboveHighWater && r + }), tc("needData")) + } + getSourceBufferInfoAction(e, t, i, r) { + var { + currentTime: n, + sourceBufferEntities: s, + msReadyState: a + } = this; + let o = [null, null]; + return !e && i.every(e => !(null != e && e.userInitiated)) ? null : "open" === a && s && null != s[0] ? (o = this.getBufferInfo(n, r), { + position: n, + discoSeqNum: null == t ? void 0 : t.discoSeqNum, + bufferInfoTuple: o, + switchContexts: i + }) : { + position: null == t ? void 0 : t.pos, + discoSeqNum: null == t ? void 0 : t.discoSeqNum, + bufferInfoTuple: o, + switchContexts: i + } + } + get haveEnough() { + var e; + return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.haveEnough) && void 0 !== e && e + } + get haveEnough$() { + return this.selectActive(({ + haveEnough: e + }) => e) + } + static likelyToKeepUp(e, t, i) { + return t && i >= e.HAVE_FUTURE_DATA + } + get playbackLikelyToKeepUp() { + return nf.likelyToKeepUp(this.mediaElement, this.haveEnough, this.readyState) + } + get playbackLikelyToKeepUp$() { + return ed([this.haveEnough$, this.readyState$]).pipe(hr(([e, t]) => nf.likelyToKeepUp(this.mediaElement, e, t))) + } + getCurrentWaterLevel(e) { + var t = this.currentTime, + i = null !== (i = null === (i = this.getActive()) || void 0 === i ? void 0 : i.bufferedRanges) && void 0 !== i ? i : []; + return Yp.getBufferedInfo(i, t, e).len + } + getCombinedMediaSourceBufferInfo(e) { + var t = this.currentTime, + [i, r] = null === (r = null === (i = this.getActive()) || void 0 === i ? void 0 : i.mediaSourceEntity) || void 0 === r ? void 0 : r.sourceBufferEntities; + return [Yp.getBufferedInfo(null !== (i = null == i ? void 0 : i.bufferedRanges) && void 0 !== i ? i : [], t, e), Yp.getBufferedInfo(null !== (r = null == r ? void 0 : r.bufferedRanges) && void 0 !== r ? r : [], t, e)] + } + getCurrentWaterLevelByType(e, t) { + var i = this.currentTime, + e = this.sourceBufferEntityByType(e), + e = null !== (e = null == e ? void 0 : e.bufferedRanges) && void 0 !== e ? e : []; + return Yp.getBufferedInfo(e, i, t).len + } + canContinuePlaybackWithoutGap(e, t, i, r) { + if ("LIVE" !== e.type) return !0; + if (!e.ptsKnown) return !1; + var n = this.currentTime, + i = performance.now() + i.avgPlaylistLoadTimeMs + 1e3 * e.targetduration, + t = e.fragments[0].start + (i - t) / 1e3; + let s = this.getCombinedBufferInfo(n, r).end; + return s >= e.fragments[0].start - r && s <= e.fragments[0].start + e.totalduration && (s = e.fragments[0].start + e.totalduration), t <= s + } + get stallInfo$() { + return this.selectActive(e => { + return null !== (e = null == e ? void 0 : e.stallInfo) && void 0 !== e ? e : null + }) + } + get textTracks() { + return this.mediaElement.textTracks + } + get textTracksCreated$() { + return this.selectActive(e => null == e ? void 0 : e.textTracksCreated) + } + get mediaOptionParsedSubtitleRecord() { + var e; + return null === (e = this.getActive()) || void 0 === e ? void 0 : e.mediaOptionParsedSubtitleRecord + } + getParsedSubtitleRecordsForMediaOption(e) { + return this.mediaOptionParsedSubtitleRecord ? this.mediaOptionParsedSubtitleRecord[e] || {} : null + } + } + class sf { + constructor(e, t, i, r) { + this.mediaSink = e, this.media = t, this.logger = r, this.useCustomMediaFunctions = i.useCustomMediaFunctions, this.overridePlaybackRate = i.overridePlaybackRate + } + install() { + const e = this.media; + e && (this.useCustomMediaFunctions && e && e.play && e.pause && (e.originalPlay || (e.originalPlay = e.play.bind(e)), e.originalPause || (e.originalPause = e.pause.bind(e)), e.play = () => (this.mediaSink.checkForReplay(), this.mediaSink.desiredRate = 1, 0 < e.currentTime && !e.paused && !e.ended && 2 < e.readyState ? Promise.resolve() : new Promise((e, t) => { + this.pendingPlayPromises || (this.pendingPlayPromises = []), this.pendingPlayPromises.push({ + resolve: e, + reject: t + }) + })), e.pause = () => { + this.mediaSink.desiredRate = 0 + }), "function" == typeof HTMLMediaElement && this.overridePlaybackRate && Object.defineProperty(e, "playbackRate", { + enumerable: !0, + configurable: !0, + get: function() { + return 1 + }, + set: function(e) { + Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, "playbackRate").set.call(this, e) + } + }), this.playPromise = null, this.expectPauseEvent = this.expectPlayEvent = !1) + } + uninstall() { + const e = this.media; + e && (e.originalPlay && (e.play = e.originalPlay, delete e.originalPlay), e.originalPause && (e.pause = e.originalPause, delete e.originalPause), this.overridePlaybackRate && (e.playbackRate = 1, delete e.playbackRate)), this.playPromise = null, this.expectPauseEvent = this.expectPlayEvent = !1 + } + play() { + var e; + this.media && (e = this.mediaSink.flushing, this.playPromise || e ? this.logger.warn(`Ignoring play command playPromise/flushing ${Boolean(this.playPromise)}/${e}`) : (this.expectPlayEvent = this.expectPlayEvent || this.media.paused, this.playPromise = this._mediaPlayInternal(), this.playPromise && this.playPromise.then(function() { + this.playPromise = null, this._handlePendingPlayPromises(null) + }.bind(this)).catch(function(e) { + this.playPromise = null, this.expectPlayEvent = !1, this._handlePendingPlayPromises(e || new Error("Play rejected for unknown reason")), "NotAllowedError" === (null == e ? void 0 : e.name) ? (this.logger.warn("play() not allowed, going back to rate 0"), this.mediaSink.desiredRate = 0) : this.logger.error(`play() error: ${null==e?void 0:e.message}`) + }.bind(this)))) + } + pause() { + this.media && (this.playPromise ? this.playPromise.then(() => { + var e = this.mediaSink.mediaQuery; + (0 === this.mediaSink.desiredRate || e.seeking && !e.playbackLikelyToKeepUp) && this._mediaPauseInternal() + }).catch(e => { + this.logger.error(`Promise error in pause(): ${e.message}`) + }) : this._mediaPauseInternal()) + } + _handlePendingPlayPromises(t) { + var e, i = null === (e = this.pendingPlayPromises) || void 0 === e ? void 0 : e.length; + if (t) + for (let e = 0; e < i; e++) this.pendingPlayPromises[e].reject(t); + else + for (let e = 0; e < i; e++) this.pendingPlayPromises[e].resolve(); + this.pendingPlayPromises = [] + } + _mediaPlayInternal() { + return (this.media.originalPlay || this.media.play.bind(this.media))() + } + _mediaPauseInternal() { + return this.expectPauseEvent = this.expectPauseEvent || !this.media.paused, (this.media.originalPause || this.media.pause.bind(this.media))() + } + } + class af extends Error {} + class of extends p { + constructor(e, t, i, r, n) { + super(L, e, t, i, r), this.sbType = n, this.response = r + } + } + class lf extends of { + constructor(e, t, i, r) { + super("bufferAddCodecError", !1, e, t, i), this.mediaOptionId = r, this.mediaOptionType = Vu(this.sbType) + } + } + class df extends of { + constructor(e, t, i, r, n, s) { + super(e, t, i, r, n), this.isTimeout = s, this.mediaOptionType = Vu(this.sbType) + } + } + class uf extends df { + constructor(e, t, i, r) { + super("bufferFullError", !1, e, t, i, !1), this.maxTotalBytes = r + } + } + class cf extends df { + constructor(e, t, i) { + super(n, !1, e, t, i, !0) + } + } + class hf extends df { + constructor(e, t, i, r) { + super(n, !1, e, t, i, !1), this.mediaOptionId = r, this.mediaOptionType = Vu(this.sbType) + } + } + class pf extends p { + constructor(e, t, i, r, n, s, a = NaN) { + super(L, e, t, i, r), this.stallType = n, this.bufferLen = s, this.nudgePosition = a, this.response = r + } + } + class ff extends $t { + constructor(n, e, s, a, o, l, d, u, c) { + super(e => { + const t = Oc(l), + i = u.child({ + sb: o + }); + n.setSourceBufferEntity(o, d), d.mimeType.includes("audio/mpeg") && (this.updateMp3Timestamps = !0); + const r = an(t.event("updatestart").pipe(Za(() => { + n.setSourceBufferUpdating(o) + })), t.event("updateend").pipe(ji(tr), Za(() => { + var e = Yp.timeRangesToBufferedRange(l.buffered), + t = Yp.timeRangesToBufferedRange(s.buffered); + n.setBufferedRangesUpdated(o, e, t, !1, c) + })), t.event("error").pipe(Za(() => { + n.setSourceBufferError(o, "Got source buffer error") + }))).pipe(La(() => Ii)).subscribe(e); + return () => { + r.unsubscribe(); + try { + "open" === a.readyState && l.abort(), a.removeSourceBuffer(l) + } catch (e) { + i.error(`Error aborting SourceBuffer on unsubscribe: ${e.message}`) + } + } + }), this.mediaElementStore = n, this.mediaElementQuery = e, this.mediaElement = s, this.type = o, this.sourceBuffer = l, this.config = c, this.updateMp3Timestamps = !1 + } + get buffered() { + return this.sourceBuffer.buffered + } + appendBuffer(e, t) { + return Zr(() => this.sourceBuffer.updating ? this._waitForUpdateEndOrError().pipe(La(() => this.appendBuffer(e, t))) : this._appendBufferAsync(e, t)) + } + _appendBufferAsync(e, t) { + let i = NaN, + r = null; + const n = ("startPTS" in t ? t.frag : t).mediaOptionId; + try { + "startPTS" in t && (r = { + startPTS: t.startPTS, + endPTS: t.endPTS, + bytes: t.bytes, + frag: Object.assign({}, t.frag) + }), this.mediaElementStore.setInflightSegment(this.type, r), i = performance.now(), this.sourceBuffer.appendBuffer(e) + } catch (e) { + return 22 !== e.code ? (this.mediaElementStore.setInflightSegment(this.type, null), this.mediaElement.error ? Vi(new hf(e.message, $.VideoDecoderBadDataErr, this.type, n)) : Vi(e)) : (this.mediaElementStore.setBufferedRangesUpdated(this.type, Yp.timeRangesToBufferedRange(this.sourceBuffer.buffered), Yp.timeRangesToBufferedRange(this.mediaElement.buffered), !0, this.config), Vi(new uf(e.message, $.AllocationFailed, this.type, this.maxTotalBytes))) + } + return this._waitForUpdateEndOrError().pipe(hr(() => ({ + startAppend: i, + endAppend: performance.now(), + bytesAppend: e.byteLength + })), So(1e4), Vn(e => { + throw e instanceof dr ? (this.sourceBuffer.abort(), e = new cf("Append took longer than 10000ms", $.InternalError, this.type)) : e instanceof of && (e = new hf("Decode error", $.VideoDecoderBadDataErr, this.type, n)), e + })) + } + remove(e, t) { + return this._waitForUpdateEndOrError().pipe(La(this._removeAsync.bind(this, e, t))) + } + _removeAsync(e, t) { + try { + this.sourceBuffer.remove(e, t) + } catch (e) { + return Vi(new of (r, !1, e.message, $.InternalError, this.type)) + } + return this._waitForUpdateEndOrError() + } + abort() { + try { + this.sourceBuffer.abort() + } catch (e) { + return Vi(new of (r, !1, e.message, $.InternalError, this.type)) + } + return this._waitForUpdateEndOrError() + } + get updating() { + return this.sourceBuffer.updating + } + get timestampOffset() { + return this.sourceBuffer.timestampOffset + } + set timestampOffset(e) { + this.sourceBuffer.timestampOffset = e + } + get gotQuotaExceeded() { + var e; + return null !== (e = null === (e = this.mediaElementQuery.sourceBufferEntityByType(this.type)) || void 0 === e ? void 0 : e.gotQuotaExceeded) && void 0 !== e && e + } + get bufferedSegments() { + var e; + return null !== (e = null === (e = this.mediaElementQuery.sourceBufferEntityByType(this.type)) || void 0 === e ? void 0 : e.bufferedSegments) && void 0 !== e ? e : [] + } + get totalBytes() { + var e; + return null !== (e = null === (e = this.mediaElementQuery.sourceBufferEntityByType(this.type)) || void 0 === e ? void 0 : e.totalBytes) && void 0 !== e ? e : 0 + } + get maxTotalBytes() { + var e = null !== (e = null === (e = this.mediaElementQuery.sourceBufferEntityByType(this.type)) || void 0 === e ? void 0 : e.maxTotalBytes) && void 0 !== e ? e : 1 / 0; + return this.gotQuotaExceeded ? e : 1 / 0 + } + _waitForUpdateEndOrError() { + return this.sourceBuffer.updating && this.mediaElementStore.setSourceBufferUpdating(this.type), this.mediaElementQuery.sbUpdating$(this.type).pipe(ln(e => !1 === e), bo(this.mediaElementQuery.sbError$(this.type)), hr(([, e]) => { + if (e) throw new of (r, !1, "Got error during sourceBuffer operation", $.InternalError, this.type) + }), Ds(1)) + } + } + class mf extends $t { + constructor(a, o, e, l, t) { + super(e => { + const t = Oc(l), + i = an(t.event("sourceopen"), t.event("sourceclose"), t.event("sourceended")).pipe(Za(e => { + e = (null !== (e = null == e ? void 0 : e.target) && void 0 !== e ? e : l).readyState; + o.msReadyState = e + })), + r = this.sourceBuffers$.pipe(La(e => e ? an(...e.filter(e => null != e)) : Ii)), + n = an(i, r).pipe(La(() => Ii)).subscribe(e), + s = URL.createObjectURL(l); + return a.src = s, o.setMediaSourceEntity(s, l.readyState), () => { + n.unsubscribe(), URL.revokeObjectURL(s), a.src === s && (a.removeAttribute("src"), a.load(), o.setMediaSourceEntity(null)), this.sourceBuffers$.next(null) + } + }), this.mediaElement = a, this.mediaElementStore = o, this.mediaElementQuery = e, this.mediaSource = l, this.logger = t, this.sourceBuffers$ = new yi(null) + } + get readyState() { + return this.mediaSource.readyState + } + set duration(e) { + this.mediaSource.duration = e + } + get duration() { + return this.mediaSource.duration + } + endOfStream(e) { + this.mediaSource.endOfStream(e) + } + createSourceBuffers(e, a) { + const o = this.mediaSource; + al(() => { + try { + const s = [null, null]; + e.forEach((t, i) => { + if (t) { + var { + mimeType: r, + mediaOptionId: n + } = t; + let e; + try { + e = o.addSourceBuffer(r) + } catch (t) { + throw new lf(t.message, $.IncompatibleAsset, i, n) + } + s[i] = new ff(this.mediaElementStore, this.mediaElementQuery, this.mediaElement, this.mediaSource, i, e, t, this.logger, a) + } + }), this.sourceBuffers$.next(s) + } catch (e) { + if (!(e instanceof p)) throw new af(`error initializing sourcebuffers ${e.message} readyState=${o.readyState}`); + throw e + } + }) + } + get needSourceBuffers() { + return null == this.sourceBuffers$.value || null == this.sourceBuffers$.value[0] + } + get sourceBuffers() { + return this.sourceBuffers$.value + } + getSourceBufferByType(e) { + var t = this.sourceBuffers$.value; + return t ? t[e] : null + } + updateLiveSeekableRange(e, t) { + const i = this.mediaSource; + null != i && i.setLiveSeekableRange && "open" === (null == i ? void 0 : i.readyState) && i.setLiveSeekableRange(e, t) + } + clearLiveSeekableRange() { + const e = this.mediaSource; + null != e && e.clearLiveSeekableRange && "open" === (null == e ? void 0 : e.readyState) && e.clearLiveSeekableRange() + } + } + + function gf(e, c, t) { + const { + lowBufferThreshold: h, + lowBufferWatchdogPeriod: p, + highBufferWatchdogPeriod: f, + seekWatchdogPeriod: m + } = t, i = ed([c.desiredRate$, c.ended$, c.combinedBuffer$, c.seekTo$]).pipe(hr(e => { + var t, [i, r, n, s] = e; + return t = c.currentTime, e = i, i = r, r = n, n = isFinite(null == s ? void 0 : s.pos), s = r.some(e => e.start <= t && e.end > t), !(1 !== e || i || 0 === r.length || n && !s) + }), Is()), r = c.combinedBuffer$.pipe(hr(() => c.getCurrentWaterLevel(0) <= t.lowBufferThreshold || !c.haveEnough ? Cp.LowBuffer : Cp.HighBuffer), Is()), n = ed([i, c.seekTo$, c.gotPlaying$, r]).pipe(La(e => { + var [t, i, r] = e; + if (!t) return $i(null); + var n, s, a, o, l, d, u, e = c.nudgeCount, + t = m + +e; + return isFinite(null == i ? void 0 : i.pos) || !r ? (n = c, s = performance.now(), t = t, a = h, bn(1e3 * t).pipe(hr(() => { + var e = n.currentTime, + t = n.getCombinedBufferInfo(e, 0); + return yf(Cp.Seek, e, s, t, a, n.haveEnough) + }))) : (o = c, l = p, d = f + +e, u = h, an($i(o.currentTime), o.timeupdate$).pipe(La(e => { + const t = performance.now(), + i = o.getCombinedBufferInfo(e, 0); + let r, n; + return r = i.len <= u || !o.haveEnough ? (n = l, Cp.LowBuffer) : (n = d, Cp.HighBuffer), bn(Math.max(100, 1e3 * n)).pipe(hr(() => e < o.currentTime ? null : yf(r, e, t, i, u, o.haveEnough))) + }))) + })), s = n.pipe(Kp(), bo(c.combinedBuffer$), La(([]) => ed([c.seeking$, c.paused$])), La(([e, t]) => e || t ? Ii : c.timeupdate$.pipe(ha(), ln(([e, t]) => ne(e) && ne(t) && e < t), Ds(1))), hr(() => null)); + return an(n, s) + } + + function yf(e, t, i, r, n, s) { + var a = performance.now() - i; + return { + type: e, + isLowBufferStall: r.len <= n || !s, + tstalled: i, + stallDurationMs: a, + currentTime: t + } + } + class vf extends $t { + constructor(w, A, e, t, i, r, n) { + super(e => { + const t = this.config, + i = A.startMediaSession(w, t.maxBufferLength, t.almostDryBufferSec, t.defaultTargetDuration), + r = Sf(w, A, this._mediaQuery, this, this.hlsGapless, t, this.logger, this.rtcService), + n = this.mediaSource$.pipe(La(e => e || Ii)), + s = this._mediaQuery.seekTo$.pipe((u = w, c = this._mediaQuery, p = (h = this).config, f = (f = this.logger).child({ + name: "seek" + }), e => e.pipe(Za(e => {}), ln(e => e && ne(e.pos)), La(e => c.readyState$.pipe(ln(e => e >= u.HAVE_METADATA), Ds(1), Zs(e), La(({ + pos: e, + fromEvent: t + }) => (h.checkForInconsistentStoreBufferRangesAndUpdate(), u.paused || h.pause(), t || (u.currentTime = e), Gu(c.haveEnough$, e => e).pipe(Zs({ + pos: e, + fromEvent: t + })))), La(({ + pos: e, + fromEvent: t + }) => { + var i = c.getCombinedBufferInfo(e, 0), + r = i.nextStart; + return (!t || p.nudgeFromEventSeek) && 0 === i.len && ne(r) && e < r && r - e <= p.maxSeekHole ? (h.seekTo = r, Ii) : $i(e) + }), bo(c.desiredRate$), hr(([, e]) => { + u.paused && 0 !== e && h.play() + }), $a(Ii), Vn(e => (f.error(`error during seek ${e.message}`), Ii))))))), + a = this._mediaQuery.desiredRate$.pipe((o = w, l = this._mediaQuery, d = this, e => e.pipe(bo(l.seekTo$), La(([e, t]) => ne(null == t ? void 0 : t.pos) ? Ii : 0 === e ? (o.paused || d.pause(), Ii) : Gu(l.haveEnough$, e => e).pipe(Za(() => { + o.paused && d.play() + }))), $a(Ii)))); + var o, l, d, u, c, h, p, f; + this.liveSeekableWindow = { + start: NaN, + end: NaN + }, this.mediaFunctions = this.mediaFunctions || new sf(this, w, t, this.logger), this.mediaFunctions.install(); + var m, g, y, v, S, b, T, E, I = an(gf(this.logger, this._mediaQuery, this.config).pipe(Za(e => { + A.setStallInfo(e) + })), this.mediaQuery.stallInfo$.pipe((g = A, y = (m = this).config, v = this.logger, e => e.pipe(_s(e => e ? function(i, e, r, n) { + const s = i.mediaQuery; + return an(ed([s.seekTo$, s.nudgeTarget$]).pipe(ln(([e, t]) => e && ne(e.pos) && ne(t) && (e.pos < t || e.pos - t > r.maxSeekHole)), Zs(null)), s.stallInfo$).pipe(bo(s.desiredRate$), ji(tr), hr(([c, e], t) => { + if (!c) return NaN; + var h = s.getCombinedBufferInfo(c.currentTime, 0), + e = Wp(e); + return function(e, t, i, r, n) { + var { + type: s, + isLowBufferStall: a, + currentTime: o + } = c, l = h.len, d = t.maxSeekHole; + let u = NaN; + if (a) { + const t = h.nextStart - o <= d ? h.nextStart : 1 / 0; + ne(t) ? u = t : e.mediaQuery.msDuration - o < .1 && (u = o + .1) + } else if (n < t.nudgeMaxRetry) u = o + t.nudgeOffset; + else { + if (!r) throw i.error(`still stuck in high buffer @${o} after ${t.nudgeMaxRetry}, raise fatal error`), new pf("bufferStalledError", !0, "got fatal buffer error", $.VideoDecoderBadDataErr, s, l); + i.error(`still stuck in high buffer @${o} after ${t.nudgeMaxRetry}, non fatal in iframeMode`) + } + return ne(u) && e.nudgeSeek(u, n + 1), u + }(i, r, n, e, t) + }), Wa(e => ne(e)), Vs(() => { + e.setNudgeInfo(null) + })) + }(m, g, y, v) : $i(NaN)))))); + an(r, n, s, a, I, (S = this.mediaQuery, b = A, I = t.maxBufferHole, an((E = I, ed([(T = S).bufferMonitorThresholds$, T.combinedBuffer$, T.seeking$]).pipe(hr(([e]) => null == e ? null : tf(T, E)))), rf(S, I)).pipe(ji(tr), Za(({ + combined: e, + sbTuple: t + }) => { + b.updateWaterLevels(e, t) + })))).pipe($a(Ii), Vs(() => { + A.remove(i), this.mediaFunctions.uninstall(), this.mediaFunctions = void 0 + })).subscribe(e) + }), this.mediaElement = w, this.mediaElementStore = A, this.config = e, this.hlsGapless = t, this.logger = i, this.teardownWG$ = r, this.rtcService = n, this.mediaSource$ = new yi(null), this.mediaKeysMutex = new zp, this._mediaQuery = new nf(w, A), this.logger = i.child({ + name: "mse" + }), this.createId3Track(w), this.mediaFunctions = new sf(this, w, e, this.logger) + } + get mediaSourceAdapter() { + return this.mediaSource$.value + } + get sourceBuffers() { + var e; + return null !== (e = null === (e = this.mediaSourceAdapter) || void 0 === e ? void 0 : e.sourceBuffers) && void 0 !== e ? e : [] + } + get needSourceBuffers() { + return !this.sourceBuffers[0] + } + get mediaQuery() { + return this._mediaQuery + } + sourceBuffersBufferedRangeByType(e) { + var t, e = null === (t = null === (t = this.mediaSourceAdapter) || void 0 === t ? void 0 : t.sourceBuffers) || void 0 === t ? void 0 : t[e]; + return e ? Yp.timeRangesToBufferedRange(e.sourceBuffer.buffered) : null + } + createId3Track(e) { + this.id3Track = e.addTextTrack("metadata", "id3"), this.id3Track.mode = "hidden" + } + checkForInconsistentStoreBufferRangesAndUpdate() { + var e = Yp.timeRangesToBufferedRange(this.mediaElement.buffered), + t = this.sourceBuffersBufferedRangeByType(yu.Variant), + i = this.sourceBuffersBufferedRangeByType(yu.AltAudio), + r = null !== (n = null === (r = this.mediaQuery.sourceBufferEntityByType(yu.Variant)) || void 0 === r ? void 0 : r.bufferedRanges) && void 0 !== n ? n : null, + n = null !== (n = null === (n = this.mediaQuery.sourceBufferEntityByType(yu.AltAudio)) || void 0 === n ? void 0 : n.bufferedRanges) && void 0 !== n ? n : null; + this.shouldUpdateStoreValues(t, r) && (this.logger.warn(`[${Uu[yu.Variant]}] SourceBuffer's loaded bufferedRanges ${JSON.stringify(t)} & mediaElementStore's bufferedRanges ${JSON.stringify(r)} are out of sync!`), this.updateMediaElementStoreBufferedRanges(e, yu.Variant)), this.shouldUpdateStoreValues(i, n) && (this.logger.warn(`[${Uu[yu.AltAudio]}] SourceBuffer's loaded bufferedRanges ${JSON.stringify(i)} & mediaElementStore's bufferedRanges ${JSON.stringify(n)} are out of sync!`), this.updateMediaElementStoreBufferedRanges(e, yu.AltAudio)) + } + shouldUpdateStoreValues(e, i) { + return !(null == e && null == i || (null == e ? void 0 : e.length) == (null == i ? void 0 : i.length) && !e.find(t => { + var e = Ku.search(i, e => t.start >= e.start && t.end <= e.end ? 0 : t.end < e.start ? -1 : 1); + return null == e || e.start != t.start || e.end != t.end || void 0 + })) + } + updateMediaElementStoreBufferedRanges(e, t) { + var i = this.sourceBuffersBufferedRangeByType(t); + i && !this.mediaQuery.sbUpdating(t) && this.mediaElementStore.setBufferedRangesUpdated(t, i, e, !1, this.config) + } + destroyMediaSource() { + this.mediaSource$.next(null) + } + makeMediaSource() { + return new MediaSource + } + openMediaSource(t) { + al(() => { + var e; + t ? (e = new mf(this.mediaElement, this.mediaElementStore, this.mediaQuery, t, this.logger), this.mediaSource$.next(e)) : this.mediaSource$.next(null) + }) + } + createSourceBuffers(e) { + const t = this.mediaSource$.value; + if (!t) throw new Error("createSourceBuffers empty mediaSource"); + t.createSourceBuffers(e, this.config) + } + _waitForMediaSourceOpen(i) { + const r = this.mediaQuery.mediaSourceEntity.objectUrl; + return ed([this.mediaQuery.msReadyState$, this.mediaQuery.msObjectUrl$]).pipe(La(([e, t]) => t !== r ? $i(null) : "open" === e || "ended" === e ? $i(i) : Ii)) + } + get appendOrder() { + return this.mediaQuery.isIframeRate ? [yu.Variant, yu.AltAudio] : [yu.AltAudio, yu.Variant] + } + clearFlush(e) { + e.forEach(e => { + e && (e.dataSeg.flushBeforeAppend = { + start: 0, + end: 0 + }) + }) + } + getSwitchPosition(e) { + return e.reduce((e, t) => { + t = t ? t.dataSeg.switchPosition : void 0; + return ne(t) ? ne(e) ? Math.min(e, t) : t : e + }, void 0) + } + checkForReplay() { + var e = this.mediaElement; + e.paused && !e.seeking && e.duration && e.currentTime && e.currentTime >= e.duration - this.config.maxTotalDurationTolerance && (this.seekTo = 0) + } + resetMediaSourceIfNeeded(r) { + const n = this["mediaQuery"], + e = n["sourceBufferEntities"], + t = n.getActive()["expectedSbCount"]; + if (!e || this.needSourceBuffers) return this._waitForMediaSourceOpen(r); + const s = function(e, s, t, i) { + const r = s.filter(e => Boolean(e)).length, + n = e.filter(e => Boolean(e)).length, + l = [null, null]; + e.forEach((e, t) => { + var i, r, n, s, a, o; + e && (s = e["offsetTimestamp"], a = S(s), i = e.initSeg.mimeType, { + audioCodec: r, + videoCodec: n + } = e.initSeg.initParsedData, o = e["dataSeg"], s = S(o.startPts) - a, a = S(o.endPts) - a, o = o.discoSeqNum, l[t] = { + audioCodec: r, + videoCodec: n, + mimeType: i, + startPTSSec: s, + endPTSSec: a, + discoSeqNum: o, + mediaOptionId: e.initSeg.mediaOptionId + }) + }); + let a = r === t, + o = n === t; + if (1 === n && n < t && 0 !== r) { + const t = e[gu.Variant] ? gu.Variant : gu.AltAudio, + h = 1 - t, + r = l[t], + n = l[h] = function(e, i, r, n, t, s) { + const a = null == e ? void 0 : e.bufferedSegments; + if (!a) return s.warn("getMatchingInfo trying to query null sbEntity"), null; + s = a.find(e => { + var t = e.frag.discoSeqNum === n, + e = Math.max(i, e.startPTS) < Math.min(r, e.endPTS); + return t && e + }); + if (null == s) return null; { + const { + audioCodec: i, + videoCodec: r, + mimeType: o + } = e; + return { + mimeType: o, + audioCodec: i, + videoCodec: r, + startPTSSec: s.startPTS, + endPTSSec: s.endPTS, + discoSeqNum: n, + mediaOptionId: t + } + } + }(s[h], r.startPTSSec, r.endPTSSec, r.discoSeqNum, r.mediaOptionId, i); + if (!n) + if (null !== (e = null === (e = e[gu.Variant]) || void 0 === e ? void 0 : e.dataSeg) && void 0 !== e && e.iframe && t === gu.Variant && l[t]) { + const p = s[t].videoCodec, + h = l[t].videoCodec; + if (a = a && (p === h || be.isCompatibleVideoCodec(p, h)), a) return { + compatible: a, + boundary: NaN, + allowance: NaN, + discoSeqNum: l[t].discoSeqNum + } + } else i.warn(`${Nu[t]} No matching frag found ${ae(r)} buffered=${ae(s[h].bufferedSegments.map(e=>{var{mediaSeqNum:t,discoSeqNum:i}=e.frag;return{mediaSeqNum:t,discoSeqNum:i,startPTS:e.startPTS,endPTS:e.endPTS}}))}`); + o = null != n + } + let d = NaN, + u = NaN, + c = NaN; + return o && l.forEach((e, t) => { + if (!e) return null; + ne(c) ? c !== e.discoSeqNum && (c = NaN) : c = e.discoSeqNum; + t = s[t]; + if (t) { + const s = t.audioCodec, + i = t.videoCodec, + { + audioCodec: r, + videoCodec: n + } = e; + a = a && (i === n || be.isCompatibleVideoCodec(i, n)), a = a && (s === r || be.isCompatibleAudioCodec(s, r)) + } else a = !1; + d = ne(d) ? (u = Math.abs(e.startPTSSec - d), Math.max(e.startPTSSec, d)) : (u = 0, e.startPTSSec) + }), { + compatible: a && o, + boundary: d, + allowance: u, + discoSeqNum: c + } + }(r, e, t, (this.config.maxBufferHole, this.logger)); + if (s.compatible) return this._waitForMediaSourceOpen(r); + let i = s.boundary; + const a = s.allowance, + o = this.getSwitchPosition(r); + if (ne(o) && (i = o), !ne(i)) return this.logger.warn("not enough info #disco"), $i(null); + const l = fn(Gu(an($i(n.currentTime), n.timeupdate$), e => e >= i), Gu(n.stallInfo$.pipe(hr(e => { + return null !== (e = null == e ? void 0 : e.currentTime) && void 0 !== e ? e : NaN + })), e => e >= i - a - this.config.discontinuitySeekTolerance)); + return this.mediaElementStore.waitingForDisco = !0, l.pipe(Zs(i), La(e => { + performance.now(); + const t = n.currentTime, + i = this.msDuration; + return this.resetMediaSource(Math.max(t, e), s.discoSeqNum), this._waitForMediaSourceOpen(r).pipe(Za(() => { + performance.now(), this.msDuration = i + })) + }), Vs(() => { + this.mediaElementStore.waitingForDisco = !1 + })) + } + resetMediaSource(e = NaN, t) { + var i; + ne(e) || (e = null !== (i = null === (i = this.mediaQuery.seekTo) || void 0 === i ? void 0 : i.pos) && void 0 !== i ? i : this.mediaQuery.currentTime), ne(t) || (t = null === (i = this.mediaQuery.seekTo) || void 0 === i ? void 0 : i.discoSeqNum), 0 < this.sourceBuffers.length && (this.openMediaSource(this.makeMediaSource()), this.setSeekToWithDiscontinuity(e, t)) + } + setExpectedSbCount(e) { + this.mediaElementStore.expectedSbCount = e + } + appendInitSegments(l, d) { + const { + mediaQuery: e, + mediaElementStore: u, + sourceBuffers: c + } = this, h = e["sourceBufferEntities"]; + if (!c) throw new Error("appendInitSegments: null sourceBuffers"); + if (!h) throw new Error("appendInitSegments: null sourceBufferEntities"); + var t = this.appendOrder.map(t => { + if (l[t]) { + const i = c[t], + r = l[t], + n = h[t], + s = r["initSeg"]; + if (!n) throw new Error(`appendInitSegments: sb[${Uu[t]}] null currentSbEntity`); + if (!i) throw new Error(`appendInitSegments: sb[${Uu[t]}] null source buffer`); + const a = n.initSegmentInfo, + o = function() { + var { + itemId: e, + mediaOptionId: t, + discoSeqNum: i, + keyTagInfo: r + } = s; + return { + itemId: e, + mediaOptionId: t, + discoSeqNum: i, + keyId: je(null == r ? void 0 : r.keyId) + } + }(); + if ((e = o) && a && e.itemId === a.itemId && e.mediaOptionId === a.mediaOptionId && e.discoSeqNum === a.discoSeqNum && e.keyId === a.keyId) return $i(null); + var e = Vu(t); + return i.appendBuffer(s.data, s).pipe(Za(e => { + u.setInitSegmentEntity(t, o) + }), d(i, e, s.mediaOptionId, this.config, this.mediaQuery)) + } + }).filter(e => Boolean(e)); + return 0 === t.length ? $i(null) : en(t) + } + appendDataSegments(m, g) { + var e = this.appendOrder.map(e => { + const t = m[e], + { + mediaQuery: i, + sourceBuffers: r + } = this, + n = i["sourceBufferEntities"]; + if (!r) throw new Error("appendDataSegments: null sourceBuffers"); + if (!n) throw new Error("appendDataSegments: null sourceBufferEntities"); + if (!t) return null; + const s = r[e], + a = m[e], + o = n[e]; + if (!o) throw new Error("appendDataSegments: null currentSbEntity"); + const l = o.initSegmentInfo, + d = a["dataSeg"]; + if (!l) throw new Error(`appendDataSegments: sb[${Uu[e]}] null currentInitSegmentInfo`); + if (!o) throw new Error(`appendDataSegments: sb[${Uu[e]}] null currentSbEntity`); + if (!s) throw new Error(`appendDataSegments: sb[${Uu[e]}] null source buffer`); + const u = s.timestampOffset, + c = { + startPTS: S(d.startPts) + u, + endPTS: S(d.endPts) + u, + firstKeyframePts: d.firstKeyframePts ? S(d.firstKeyframePts) + u : void 0, + bytes: d.data2 ? d.data1.byteLength + d.data2.byteLength : d.data1.byteLength, + frag: { + itemId: d.itemId, + mediaOptionId: d.mediaOptionId, + mediaSeqNum: d.mediaSeqNum, + discoSeqNum: d.discoSeqNum, + keyTagInfo: d.keyTagInfo, + isLastFragment: d.isLastFragment, + iframe: d.iframe, + framesWithoutIDR: d.framesWithoutIDR, + dropped: d.dropped + } + }, + h = Vu(e); + let p = Wu; + var f = t.dataSeg.flushBeforeAppend; + return f && f.start !== f.end && (p = this.flushData(e, f.start, f.end)), p.pipe(La(() => $i(d.data1, d.data2).pipe(Kp()).pipe(Wn(e => s.appendBuffer(e, c).pipe(g(s, h, d.mediaOptionId, this.config, this.mediaQuery))))), Za(() => { + i.getBufferedRangeByType(e) + })) + }).filter(e => Boolean(e)); + return 0 === e.length ? $i(null) : en(e) + } + setStoreSbTimeoffsets(s) { + const { + mediaElementStore: a, + sourceBuffers: e + } = this; + e.forEach((t, i) => { + if (t && s[i]) { + var { + offsetTimestamp: r, + dataSeg: n + } = s[i], n = S(n.startPts); + let e = -1 * S(r); + t.updateMp3Timestamps && .1 < Math.abs(t.timestampOffset - n) && (e = n + e), t.timestampOffset !== e && (t.timestampOffset = e, a.setTimestampOffset(i, t.timestampOffset)) + } + }) + } + adjustJaggedStart(e) { + const { + mediaQuery: t, + logger: n + } = this, { + sourceBufferEntities: i, + currentTime: r, + seekTo: s + } = t, a = e.reduce((e, t) => null != t && t.dataSeg.endPts ? Math.min(b(t.dataSeg.endPts, t.offsetTimestamp), e) : e, Number.POSITIVE_INFINITY); + if (!i) throw new Error("appendSourceBufferData null currentSbEntity"); + const o = (null == s ? void 0 : s.pos) || r; + let l = NaN; + i.forEach((e, t) => { + if (e) { + e = Yp.getBufferedInfo(e.bufferedRanges, o, 0); + if (0 === e.len) { + const i = e["nextStart"], + r = ne(this.config.jaggedSeekTolerance) ? this.config.jaggedSeekTolerance : 0; + n.warn(`sb[${Uu[t]}] jagged start: ${i} appendEndTime=${a} current=${l} tolerance=${r}`), ne(i) && (!ne(l) || i - l > r) && (l = i) + } + } + }), ne(l) && a > l && (n.warn(`[seek] jagged start, adjusting currentTime:${r.toFixed(3)} seekTo=${null===(e=null==s?void 0:s.pos)||void 0===e?void 0:e.toFixed(3)}->${l} appendEndTime=${a}`), this.seekTo = l) + } + addCues(e, t) { + const i = this.mediaElement.textTracks[e]; + i && t.forEach(e => { + i.addCue(e) + }) + } + _flushInternal(e, t, i) { + return Zr(() => e.remove(t, i)).pipe(Za(() => {})) + } + flushAll(i, r, n = !1) { + return 0 === this.sourceBuffers.length ? Wu : en(this.sourceBuffers.map((e, t) => e ? this.flushData(t, i, r, n) : Wu)).pipe(Zs(void 0)) + } + flushData(o, l, d, u = !1) { + const { + mediaQuery: t, + logger: c + } = this; + return Gu(t.updating$, e => !1 === e).pipe(La(() => { + var e = t["sourceBufferEntities"], + e = e[o]; + null != e && e.updating && this.logger.warn(`trying to flush while updating ${o}`); + const r = this.sourceBuffers[o]; + if (!r) return Wu; + let n, s, a = Wu; + e = -1 === navigator.userAgent.toLowerCase().indexOf("firefox"); + if (this.flushing = !0, e) return this._flushInternal(r, l, d); + for (let i = 0; i < r.buffered.length; i++) { + let e, t; + n = r.buffered.start(i), s = r.buffered.end(i), t = d === 1 / 0 ? (e = l, d) : (e = Math.max(n, l), Math.min(s, d)), Math.min(t, s) > e && (u || .5 < Math.min(t, s) - e) ? a = a.pipe($a(this._flushInternal(r, e, t))) : c.warn(`ignoring sb[${Uu[o]}] flush ${e},${t}`) + } + return a + }), Vs(() => { + this.flushing = !1 + })) + } + static convertInitSegToCompatInfo(e) { + return { + mimeType: e.mimeType, + audioCodec: e.initParsedData.audioCodec, + videoCodec: e.initParsedData.videoCodec, + startPTSSec: void 0, + endPTSSec: void 0, + discoSeqNum: e.discoSeqNum, + mediaOptionId: e.mediaOptionId + } + } + static combineAppendDataInfoWithCompatInfo(e, t, i, r = 0) { + const n = [...t]; + e.forEach((e, t) => null != e && e.initSeg ? n[t] = vf.convertInitSegToCompatInfo(e.initSeg) : null); + t = n[yu.Variant].videoCodec, e = Hp(t); + if (i && i.has(e)) { + const s = i.get(e); + n[yu.Variant].mimeType = n[yu.Variant].mimeType.replace(t, s), n[yu.Variant].videoCodec = s + } + return n + } + convertSourceBufferEntitiesToCompatInfo(e) { + const t = e.sourceBufferEntities, + i = [null, null]; + return t.forEach((e, t) => { + e && (i[t] = { + mimeType: e.mimeType, + audioCodec: e.audioCodec, + videoCodec: e.videoCodec, + startPTSSec: void 0, + endPTSSec: void 0, + discoSeqNum: void 0, + mediaOptionId: null === (e = e.initSegmentInfo) || void 0 === e ? void 0 : e.mediaOptionId + }) + }), i + } + appendData(e, i, r) { + const { + mediaQuery: t, + logger: n + } = this, o = this.convertSourceBufferEntitiesToCompatInfo(t); + return e.every(e => null == e) ? $i([]) : this.resetMediaSourceIfNeeded(e).pipe(La(e => e ? t.updating$.pipe(ln(e => !1 === e), Ds(1), Zs(e)) : $i(null)), La(t => { + if (!t) return $i([]); + let s = NaN, + a = NaN; + if (this.needSourceBuffers) { + s = performance.now(); + const i = vf.combineAppendDataInfoWithCompatInfo(t, o, r, n); + this.createSourceBuffers(i), a = performance.now(), this.clearFlush(t) + } + return t.forEach(e => { + e = null == e ? void 0 : e.dataSeg; + null != e && e.cues && ne(null == e ? void 0 : e.texttrackIdx) && this.addCues(e.texttrackIdx, e.cues) + }), this.setStoreSbTimeoffsets(t), Jr(Zr(() => this.appendInitSegments(t, i)), Zr(() => this.appendDataSegments(t, i))).pipe(function(r, i) { + return 2 <= arguments.length ? function(e) { + return Bt(sa(r, i), Ws(1), (void 0 === (t = i) && (t = null), function(e) { + return e.lift(new is(t)) + }))(e); + var t + } : function(e) { + return Bt(sa(function(e, t, i) { + return r(e, t) + }), Ws(1))(e) + } + }((e, t) => (e.push(t), e), new Array), hr(([i, r]) => { + const n = [null, null]; + return [yu.Variant, yu.AltAudio].forEach(e => { + var t; + null != (null == i ? void 0 : i[e]) && (t = { + fragmentType: Vu(e), + bufferCreationStart: s, + bufferCreationEnd: a, + startInitAppend: i[e].startAppend, + endInitAppend: i[e].endAppend, + initBytesAppend: i[e].bytesAppend, + startDataAppend: r[e].startAppend, + endDataAppend: r[e].endAppend, + dataBytesAppend: r[e].bytesAppend + }, n[e] = t) + }), n + }), Za(e => { + this.adjustJaggedStart(t) + })) + }), Ds(1)) + } + endStream() { + try { + this.mediaSourceAdapter.endOfStream() + } catch (e) { + this.logger.warn(`endOfStream failed: ${e.message}`) + } + } + setMediaKeys(e) { + return this.teardownWG$.wrap(this.mediaKeysMutex.lock(() => Fr(this.mediaElement.setMediaKeys(e))).pipe(Za(() => {}), va(e => e.pipe(jr((e, t) => { + if (t < 3) return bn(100 * t); + throw e + }))))) + } + clearMediaKeys() { + return Zr(() => { + if (!this.mediaElement) return Wu; + const e = -1 < navigator.userAgent.toLowerCase().indexOf("chrome"), + t = this.mediaElement.src; + return e && (this.mediaElement.src = ""), this.setMediaKeys(null).pipe(Za(() => this.mediaElement.src = t)) + }) + } + set postFlushSeek(e) { + this.mediaElementStore.postFlushSeek = e + } + schedulePostFlushSeek(e) { + al(() => { + this.mediaQuery.seekTo && (this.seekTo = null), this.postFlushSeek = e + }) + } + set seekTo(e) { + this.mediaElementStore.setSeekToPos(e, !1) + } + setSeekToWithDiscontinuity(e, t) { + this.mediaElementStore.setSeekToPos(e, !1, t) + } + nudgeSeek(e, t) { + al(() => { + this.mediaElementStore.setSeekToPos(e, !1), this.mediaElementStore.setNudgeInfo({ + nudgeTarget: e, + nudgeCount: t + }) + }) + } + set desiredRate(e) { + this.mediaElementStore.desiredRate = e + } + toggleTrickPlaybackMode(e) { + if (this.config.overridePlaybackRate) { + const t = e ? 2 : 1; + try { + this.mediaElement.playbackRate = t + } catch (e) { + this.logger.error({ + name: "iframes" + }, `Exception when setting playbackRate=${t}: ${e.message}`) + } + } + const t = this.muteValueOnTrickPlaybackToggle; + e && void 0 === t ? (this.muteValueOnTrickPlaybackToggle = this.mediaElement.muted, this.mediaElement.muted = e) : e || void 0 === t || (this.mediaElement.muted = t, this.muteValueOnTrickPlaybackToggle = void 0) + } + play() { + this.mediaFunctions.play() + } + pause() { + this.mediaFunctions.pause() + } + get expectPlayEvent() { + return this.mediaFunctions.expectPlayEvent + } + set expectPlayEvent(e) { + this.mediaFunctions.expectPlayEvent = e + } + get expectPauseEvent() { + return this.mediaFunctions.expectPauseEvent + } + set expectPauseEvent(e) { + this.mediaFunctions.expectPauseEvent = e + } + set textTracksCreated(e) { + const t = this["mediaElementStore"]; + t.textTracksCreated = e + } + get msDuration() { + return this._mediaQuery.msDuration + } + set msDuration(e) { + try { + const t = this["mediaElementStore"], + i = this.mediaSource$.value; + i.duration !== e && (i.duration = e, t.msDuration = e) + } catch (e) { + this.logger.warn(`Error setting duration ${e.message}`) + } + } + set haveEnough(e) { + this.mediaElementStore.haveEnough = e + } + set flushing(e) { + this.mediaElementStore.flushing = e + } + set bufferMonitorTargetDuration(e) { + this.mediaElementStore.bufferMonitorTargetDuration = e + } + get textTracks() { + return this.mediaElement.textTracks + } + get id3TextTrack() { + return this.id3Track + } + addTextTrack(e, t, i) { + return this.mediaElement.addTextTrack(e, t, i) + } + dispatchEvent(e) { + return this.mediaElement.dispatchEvent(e) + } + get offsetWidth() { + return this.mediaElement.offsetWidth + } + get offsetHeight() { + return this.mediaElement.offsetHeight + } + getliveSeekableWindow() { + return this.liveSeekableWindow + } + archiveParsedSubtitleFragmentRecord(e, t, i) { + return this.mediaElementStore.archiveParsedSubtitleFragmentRecord(e, t, i) + } + updateLiveSeekableRange(e) { + var t = e.fragments, + e = t.length; + if (1 < e) { + const i = Math.max(t[0].start, 0), + r = t[e - 1].start + t[e - 1].duration; + this.mediaSource$.value.updateLiveSeekableRange(i, r), this.liveSeekableWindow.start = i, this.liveSeekableWindow.end = r + } + } + clearLiveSeekableRange() { + this.mediaSource$.value.clearLiveSeekableRange(), this.liveSeekableWindow.start = NaN, this.liveSeekableWindow.end = NaN + } + } + const Sf = (t, r, n, s, a, o, l, i) => { + if (!t) return Ii; + const e = Oc(t); + return an(e.event("durationchange").pipe(hr(e => kc(t, "durationchange", e)), Za(e => { + e = e.currentTarget; + r.mediaElementDuration = e.duration + })), e.event("seeking").pipe(ao(o.seekEventThrottleMs), hr(e => kc(t, "seeking", e)), Za(e => { + var t = e.currentTarget, + e = t.currentTime; + if (t.readyState >= t.HAVE_METADATA) { + const i = n.seekTo; + !i || !i.fromEvent && 1e-5 < Math.abs(i.pos - e) ? a.inGaplessMode ? function(e, t, i, r, n) { + let s = !1; + e < t.playingItem.itemStartOffset && (n.warn(`[Gapless] Seeking past track boundary oldSeek=${e}, adjustedSeek=${t.playingItem.itemStartOffset}`), e = t.playingItem.itemStartOffset, s = !0), t.isPreloading && (e > t.loadingItem.itemStartOffset && (n.warn(`[Gapless] Seeking past track boundary oldSeek=${e}, adjustedSeek=${t.loadingItem.itemStartOffset}`), e = t.loadingItem.itemStartOffset, s = !0), t.dequeueSource("SeekToUnbufferedTimeRanges")), s ? i.resetMediaSource(e) : r.setSeekToPos(e, !0) + }(e, a, s, r, l) : s && s.hasOwnProperty("liveSeekableWindow") && ne(s.getliveSeekableWindow().start) && ne(s.getliveSeekableWindow().end) && (e < s.getliveSeekableWindow().start || e > s.getliveSeekableWindow().end) ? function(e, t, i, r, n, s) { + let a = e; + if (e < t) a = t; + else if (i < e) { + let e = r.defaultTargetDuration; + ne(r.liveSyncDuration) ? e = r.liveSyncDuration : ne(r.liveSyncDurationCount) && (e = r.liveSyncDurationCount * r.defaultTargetDuration), a = Math.max(0, i - e) + } + s.warn(`[live] liveAdjustedSeek seekTo:${se(e,3)}, adjustedSeek:${se(a,3)}, liveWindowStart:${se(t,3)}, liveWindowEnd:${se(i,3)}`), n.setSeekToPos(a, !0) + }(e, s.getliveSeekableWindow().start, s.getliveSeekableWindow().end, o, r, l) : r.setSeekToPos(e, !0) : r.seeking = !0 + } + })), e.event("seeked").pipe(hr(e => kc(t, "seeked", e)), Za(() => { + r.setSeekToPos(null, !0) + })), e.event("play").pipe(hr(e => kc(t, "play", e)), bo(n.desiredRate$), hr(([e]) => { + var t = e.currentTarget; + r.paused = t.paused; + var i = s.expectPlayEvent, + t = t.controls || o.nativeControlsEnabled; + return !i && t && (s.checkForReplay(), r.desiredRate = 1), s.expectPlayEvent = !1, e + })), e.event("playing").pipe(hr(e => kc(t, "playing", e)), Za(e => { + e = e.currentTarget; + r.paused = e.paused, r.gotPlayingEvent() + })), e.event("loadstart").pipe(hr(e => kc(t, "loadstart", e)), Za(() => { + r.gotLoadStartEvent() + })), e.event("pause").pipe(hr(e => kc(t, "pause", e)), Za(e => { + var t = e.currentTarget; + r.paused = t.paused; + e = s.expectPauseEvent, t = t.controls || o.nativeControlsEnabled; + !e && t && (r.desiredRate = 0), s.expectPauseEvent = !1 + })), e.event("loadedmetadata").pipe(hr(e => kc(t, "loadedmetadata", e))), e.event("loadeddata").pipe(hr(e => kc(t, "loadeddata", e))), e.event("canplay").pipe(hr(e => kc(t, "canplay", e))), e.event("canplaythrough").pipe(hr(e => kc(t, "canplaythrough", e))), e.event("waiting").pipe(hr(e => kc(t, "waiting", e))), e.event("emptied").pipe(hr(e => kc(t, "emptied", e))), e.event("error").pipe(hr(e => kc(t, "error", e)), Wn(e => Vi(t.error))), e.event("ended").pipe(hr(e => kc(t, "ended", e)))).pipe(bo(n.bufferedRangeTuple$), ll(([e]) => { + var t = e.currentTarget, + e = t.readyState; + r.readyState = e, r.ended = t.ended + }), Vn(e => (e instanceof MediaError ? (l.warn(`mediaElementError, code: ${e.code}, message: ${e.message}`), null == i || i.handleMediaElementError(e)) : l.error(`media event error: ${e.message}`), Ii)), $a(on), Vn(e => e instanceof MediaError ? (l.warn(`mediaElementError, code: ${e.code}, message: ${e.message}`), Vi(e)) : (l.error(`media event error: ${e.message}`), Ii))) + }, + bf = new class extends fl { + constructor() { + super({}, { + name: "media-element-store", + producerFn: su + }), this._activeId = "" + } + get activeId() { + return this._activeId + } + startMediaSession(i, r, n, s) { + return Do("playback.session.start"), this._activeId = `media session: ${(new Date).toISOString()}`, al(() => { + var e = s, + t = Math.max(e, r - e), + t = { + id: this.activeId, + desiredRate: !i.autoplay && i.paused ? 0 : 1, + paused: i.paused, + gotPlaying: !1, + gotLoadStart: !1, + firstPlayTime: void 0, + seeking: i.seeking, + flushing: !1, + readyState: i.readyState, + ended: i.ended, + bufferedRanges: [], + haveEnough: !1, + mediaSourceEntity: null, + expectedSbCount: NaN, + bufferMonitorInfo: { + waterLevelType: null, + almostDryWaterLevelSeconds: n, + lowWaterLevelSeconds: e, + highWaterLevelSeconds: t, + maxBufferSeconds: r + }, + mediaOptionParsedSubtitleRecord: [], + textTracksCreated: !1, + waitingForDisco: !1 + }; + this.add(t), this.setActive(this.activeId) + }), this.logger = Qe().child({ + name: "UpdateBufferedSegments" + }), this.activeId + } + setMediaSourceEntity(t, i) { + Do("playback.set.msObjectUrl"), this.updateActive(e => { + e.mediaSourceEntity = null != t && null != i ? { + objectUrl: t, + readyState: i, + duration: NaN, + sourceBufferEntities: [null, null] + } : null, e.bufferedRanges = [], e.haveEnough = !1, e.readyState = 0, e.bufferMonitorInfo.waterLevelType = null + }) + } + set mediaElementDuration(t) { + Do("playback.set.mediaElementDuration"), this.updateActive(e => { + e && (e.mediaElementDuration = t) + }) + } + set msReadyState(t) { + Do("playback.set.msReadyState"), this.updateActive(({ + mediaSourceEntity: e + }) => { + e && (e.readyState = t) + }) + } + set readyState(t) { + Do(`playback.set.readyState ${t}`), this.updateActive(e => { + e.readyState = t + }) + } + set ended(t) { + Do(`playback.set.ended ${t}`), this.updateActive(e => { + e.ended = t + }) + } + set msDuration(t) { + Do("playback.set.msDuration"), this.updateActive(e => { + e.mediaSourceEntity.duration = t + }) + } + set textTracksCreated(t) { + Do("playback.set.textTracksCreated ${created}"), this.updateActive(e => { + e.textTracksCreated = t + }) + } + set expectedSbCount(t) { + Do("playback.set.expectedSbCount"), this.updateActive(e => { + e.expectedSbCount = t + }) + } + set postFlushSeek(e) { + this.updateActive({ + postFlushSeek: e + }) + } + setSeekToPos(t, i, r) { + Do(`playback.set.seekToPos: ${null==t?void 0:t.toFixed(3)} cc: ${r}`), this.updateActive(e => { + ne(t) ? (e.seekTo = { + pos: t, + fromEvent: i, + discoSeqNum: r + }, e.gotPlaying = !1, e.haveEnough = !1) : (e.seekTo = null, e.postFlushSeek = void 0), i && (e.seeking = ne(t)) + }) + } + set seeking(t) { + Do(`playback.set.seeking: ${t}`), this.updateActive(e => { + e.seeking = t + }) + } + set paused(t) { + Do(`playback.set.paused: ${t}`), this.updateActive(e => { + (e.paused = t) && (e.gotPlaying = !1) + }) + } + gotPlayingEvent() { + Do("playback.set.playing"), this.updateActive(e => { + e.paused || (e.gotPlaying = !0, e.firstPlayTime = e.firstPlayTime || performance.now()) + }) + } + gotLoadStartEvent() { + Do("playback.set.loadstart"), this.updateActive(e => { + e.gotLoadStart = !0 + }) + } + set desiredRate(t) { + Do(`playback.set.desiredRate: ${t}`), this.updateActive(e => { + e.desiredRate = t + }) + } + set haveEnough(t) { + Do(`playback.set.haveEnough: ${t}`), this.updateActive(e => { + e.haveEnough = t + }) + } + set flushing(e) { + Do(`playback.set.flushing: ${e}`), this.updateActive({ + flushing: e + }) + } + set waitingForDisco(t) { + Do(`playback.set.waitingForDisco: ${t}`), this.updateActive(e => { + e && (e.waitingForDisco = t) + }) + } + setSourceBufferUpdating(i) { + Do(`playback.set.sourcebuffers[${Uu[i]}].updating`), this.updateActive(({ + mediaSourceEntity: e + }) => { + const t = null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[i]; + t && (t.updating = !0, t.error = void 0) + }) + } + setTimestampOffset(i, r) { + Do(`playback.set.sourcebuffers[${Uu[i]}].timestampOffset`), this.updateActive(({ + mediaSourceEntity: e + }) => { + const t = null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[i]; + t && (t.timestampOffset = r) + }) + } + setBufferedRangesUpdated(a, o, l, d, u) { + Do(`playback.set.sourcebuffers[${Uu[a]}].bufferupdated`), this.updateActive(e => { + var t, i, r; + const n = null == e ? void 0 : e.mediaSourceEntity, + s = null === (r = null == n ? void 0 : n.sourceBufferEntities) || void 0 === r ? void 0 : r[a]; + if (s) { + const a = null == n ? void 0 : n.duration; + s.updating = !1, s.bufferedRanges = [...o], t = s, i = t.inFlight, r = t.bufferedSegments, i && ne(i.startPTS) && ne(i.endPTS) && function(t, i) { + let r = !1; + for (let e = t.length - 1; - 1 < e; e--) { + const s = t[e], + a = Math.max(i.startPTS, s.startPTS), + o = Math.min(i.endPTS, s.endPTS); + var n; + o <= a || ((n = (1 - (o - a) / (s.endPTS - s.startPTS)) * s.bytes) <= 0 ? t.splice(e, 1) : (s.bytes = n, s.startPTS < i.startPTS ? s.endPTS = a : (s.startPTS = o, r || (t.splice(e, 0, i), r = !0)))) + } + r || t.push(i) + }(r, i), t.inFlight = null, + function(e, t, i, r, n) { + const { + maxBufferHole: s, + bufferedSegmentEjectionToleranceMs: a + } = r, o = e.bufferedSegments, l = e.bufferedRanges; + let d, u = 0, + c = !1; + if (l.length) + for (let e = o.length - 1; - 1 < e; e--) { + const t = o[e], + r = !t.frag.iframe; + r && t.frag.isLastFragment && (d = t.frag); + var h = t.endPTS - t.startPTS; + if (h <= 0) o.splice(e, 1), null == n || n.warn(`Ejecting segment from bufferedSegments due to segmentDuration <= 0 > segment=${ae(t)}`), c = t.frag === d; + else { + var p = Bp(l, t); + if (p) { + var f = Math.max(p.start, t.startPTS), + p = Math.min(p.end, t.endPTS), + f = p - f; + if (u += t.bytes * f / h, r) + if (f < Math.min(h, s)) o.splice(e, 1), null == n || n.warn(`Ejecting segment from bufferedSegments due to tiny overlaps > segment=${ae(t)}, bufferedRanges=${ae(l)}`), c = t.frag === d; + else { + const r = t.appendedDuration, + u = (r || 0) - f, + m = Math.min(.001 * a, h); + r ? !(u > m && f != h) || t.frag.isLastFragment && p === i || (o.splice(e, 1), null == n || n.warn(`Ejecting segment from bufferedSegments due to change in current overlap > segment=${ae(t)}, delta=${u}, bufferedRanges=${ae(l)}`), c = t.frag === d) : t.appendedDuration = f + } + } else null == n || n.warn(`Ejecting segment from bufferedSegments due to no overlap > segment=${ae(t)}, bufferedRanges=${ae(l)}`), o.splice(e, 1), c = t.frag === d + } + } else o.length && o.splice(0, o.length); + e.totalDuration = d && !c && 0 < l.length ? l[l.length - 1].end : 1 / 0, e.gotQuotaExceeded = e.gotQuotaExceeded || t, e.totalBytes = u, e.maxTotalBytes = Math.max(e.totalBytes, e.maxTotalBytes) + }(s, d, a, u, this.logger) + } + e.bufferedRanges = [...l] + }) + } + setSourceBufferEntity(n, s) { + Do(`playback.set.sourcebuffers[${Uu[n]}].setSourceBufferEntity`), this.updateActive(({ + mediaSourceEntity: e + }) => { + var t, i, r; + e && ({ + mimeType: t, + audioCodec: i, + videoCodec: r + } = s, e.sourceBufferEntities[n] = { + mimeType: t, + audioCodec: i, + videoCodec: r, + updating: !1, + bufferedRanges: [], + timestampOffset: 0, + inFlight: null, + bufferedSegments: [], + totalBytes: 0, + maxTotalBytes: 0, + gotQuotaExceeded: !1, + totalDuration: 1 / 0 + }) + }) + } + setInflightSegment(i, r) { + Do(`playback.set.sourcebuffers[${Uu[i]}].setInflightSegment`), this.updateActive(({ + mediaSourceEntity: e + }) => { + const t = null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[i]; + t && (t.inFlight = r) + }) + } + setInitSegmentEntity(i, r) { + Do(`playback.set.sourcebuffers[${Uu[i]}].setInitSegmentEntity`), this.updateActive(({ + mediaSourceEntity: e + }) => { + const t = null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[i]; + t && (t.initSegmentInfo = r) + }) + } + setSourceBufferError(i, r) { + Do(`playback.set.sourcebuffers[${i}].error: ${r}`), this.updateActive(({ + mediaSourceEntity: e + }) => { + const t = null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[i]; + t && (t.inFlight = null, t.updating = !1, t.error = r) + }) + } + setStallInfo(t) { + Do(`playback.set.stallInfo stalled=${null!=t}`), this.updateActive(e => { + e.stallInfo = t + }) + } + setNudgeInfo(t) { + Do(`playback.set.nudgeInfo ${ae(t)}`), this.updateActive(e => { + e.nudgeInfo = t + }) + } + updateWaterLevels(t, i) { + Do("playback.set.updateWaterLevels"), this.updateActive(e => { + e.bufferMonitorInfo.waterLevelType = { + combined: t, + sbTuple: [...i] + } + }) + } + set bufferMonitorTargetDuration(i) { + Do(`playback.set.targetDuration: ${i}`), this.updateActive(e => { + if (ne(i) && 0 < i) { + const t = e.bufferMonitorInfo; + t.lowWaterLevelSeconds = Math.min(i, t.maxBufferSeconds), t.highWaterLevelSeconds = Math.max(t.lowWaterLevelSeconds, t.maxBufferSeconds - i) + } + }) + } + archiveParsedSubtitleFragmentRecord(i, r, n) { + Do(`playback.cues.set persistentId ${i} mediaSeqNum ${r}: parsed ${n.count} time-range ${n.startTime}:${n.endTime}`), this.updateActive(e => { + let t = e.mediaOptionParsedSubtitleRecord[i]; + t || (t = {}, e.mediaOptionParsedSubtitleRecord[i] = t), t[r] = n + }) + } + }; + class Tf extends bi { + constructor(e) { + super(e), this.store = e, this.displaySupportsHdr$ = this.select("supportsHdr"), this.platformInfo$ = this.select("platformInfo"), this.viewportInfo$ = this.select("viewportInfo") + } + get platformInfo() { + return this.getValue().platformInfo + } + get displaySupportsHdr() { + return this.getValue().supportsHdr + } + get viewportInfo() { + return this.getValue().viewportInfo + } + } + class Ef { + constructor(e) { + this.store = e + } + getQuery() { + return new Tf(this.store) + } + updateSupportsHdr(t) { + this.store.update(e => { + e.supportsHdr = t + }) + } + updatePlatformInfo(e) { + this.store.update({ + platformInfo: e + }) + } + updateViewportInfo(t) { + this.store.update(e => { + e.viewportInfo = t + }) + } + } + const If = new class extends dl { + constructor() { + super({ + supportsHdr: !0 + }, { + name: "platform", + producerFn: su + }) + } + }; + let wf = null; + + function Af() { + return wf = wf || new Ef(If), wf + } + class Of extends $t { + constructor(e, t) { + super(e => { + null == t || t.add(), e.add(this._handler$.pipe(jr(([e, t, i]) => e(...t).pipe(Za({ + next(e) { + i(e, null) + }, + error(e) { + i(null, e) + } + })))).subscribe()) + }), this._vanillaRPC = e, this.teardownWG$ = t, this._handler$ = new Xt + } + register(e, i) { + return this._vanillaRPC.register(e, (...t) => e => { + this._handler$.next([i, t, e]) + }) + } + invoke(e, t, r) { + return new $t(i => { + this._vanillaRPC.invoke(e, t, r)((e, t) => { + null != t ? i.error(t) : (i.next(e), i.complete()) + }) + }) + } + } + class kf extends Of { + constructor(e) { + super(e) + } + decrypt(e, t, i, r, n) { + return this.invoke("decrypt", [e, t, i, r, n], [e, t, r]) + } + } + class Cf extends Of { + constructor(e) { + super(e), this.rpcService = e, this.sessions = {}, this._onEvent = (e, t, i) => () => { + null != this.sessions[e] && this.sessions[e].observer.trigger(t, i) + }, this.rpcService.register("demuxer.event", this._onEvent) + } + init(e, t, i) { + return [{ + maxSeekHole: r, + maxBufferHole: n, + audioPrimingDelay: s, + stretchShortVideoTrack: a, + forceKeyFrameOnDiscontinuity: o + }] = [t], this.invoke("demuxer.init", [e, t = { + maxSeekHole: r, + maxBufferHole: n, + audioPrimingDelay: s, + stretchShortVideoTrack: a, + forceKeyFrameOnDiscontinuity: o + }, i], []).pipe(hr(e => { + var t = new Df(this, e); + return this.sessions[e] = t + })); + var r, n, s, a, o + } + } + class Df { + constructor(e, t) { + this.rpc = e, this.demuxSessionID = t, this.observer = new a + } + push(e, t, i, r, n, s, a, o, l, d, u, c, h) { + return this.rpc.invoke("demuxer.push", [this.demuxSessionID, e, t, i, r, n, s, a, o, l, d, u, c], null != h ? h : [e]) + } + pushWithoutTransfer(e, t, i, r, n, s, a, o, l, d, u, c) { + return this.push(e, t, i, r, n, s, a, o, l, d, u, c, []) + } + destroy() { + this.observer.removeAllListeners(), this.rpc.invoke("demuxer.destroy", [this.demuxSessionID], []).subscribe() + } + } + class Mf { + constructor() { + this.handlers = {} + } + register(e, t) { + if (null != this.handlers[e]) return !1; + this.handlers[e] = t + } + unregister(e) { + if (null != this.handlers[e]) return !1; + delete this.handlers[e] + } + invoke(e, i) { + return (t = Mf._fallbackCallback) => { + try { + if (null == this.handlers[e]) throw new Error(`command ${e} not found`); + this.handlers[e](...i)(t) + } catch (e) { + t(void 0, e) + } + } + } + teardown(e) { + this.handlers = null, e() + } + } + Mf._fallbackCallback = (e, t) => { + if (null != t) throw t + }; + const xf = e => { + e = e.child({ + name: "InlineRPCService" + }); + var t = new Mf; + return new i(t, e), new ot(t, e), t + }; + let Pf; + const Rf = e => { + try { + if (null == Pf) { + const e = new Blob(["var exports = {};var module = { exports: exports };function define(f){f()};define.amd = true;(" + Xy.toString() + ")(true);"], { + type: "text/javascript" + }), + r = URL.createObjectURL(e); + Pf = new Worker(r) + } + var t = new lt(Pf); + return i = t, n = e.child({ + name: "WorkerRPCService" + }), i.register("logger.log", (e, i, ...r) => t => { + try { + for (const i of e) n = n.child(i); + "function" == typeof n[i] && n[i](...r), t() + } catch (e) { + t(void 0, e) + } + }), t + } catch (e) { + throw new Error("Failed to create WebWorker") + } + var i, n + }, + Lf = (...r) => i => { + for (let t = 0; t < r.length; t++) { + const e = r[t]; + try { + return e(i) + } catch (e) { + if (t === r.length - 1) throw e; + i.warn(e) + } + } + }, + _f = e => Lf(Rf, xf)(e); + class Nf {} + Nf.PlayEnded = 6101, Nf.Periodic = 6110, Nf.PlayStalled = 6103, Nf.KeySessionComplete = 6104, Nf.PlayLikelyToKeepUp = 6105, Nf.PlayRateChanged = 6106, Nf.PlayError = 6107, Nf.MediaEngineStalled = 6108, Nf.SwitchComplete = 6109, Nf.VariantEnded = 6111, Nf.NwError = 6202; + const Ff = { + avc1: 1, + avc3: 1, + hvc1: { + SDR: 2, + HLG: 10, + PQ: 11 + }, + hev1: { + SDR: 2, + HLG: 10, + PQ: 11 + }, + vp09: { + SDR: 3, + HLG: 14, + PQ: 13 + }, + dvh1: { + PQ: 12 + } + }; + class Bf { + constructor(e, t) { + this.query = e, this.logger = t + } + setReportingAgent(e) { + this.reportingAgent = e + } + sendPlayEnded(e) { + var t = Nf.PlayEnded; + this.fillAndFire(t, this.query.playEnded(e)) + } + sendPlayStalled(e) { + var t = Nf.PlayStalled; + this.fillAndFire(t, this.query.playStalled(e)) + } + sendMediaEngineStalled(e) { + var t = Nf.MediaEngineStalled; + this.fillAndFire(t, this.query.mediaEngineStalled(e)) + } + sendKeySessionComplete(e) { + var t = Nf.KeySessionComplete; + this.fillAndFire(t, this.query.keySessionComplete(e)) + } + sendPlayLikelyToKeepUp(e) { + var t = Nf.PlayLikelyToKeepUp; + this.fillAndFire(t, this.query.playLikelyToKeepUp(e)) + } + sendPlayRateChange(e) { + var t = Nf.PlayRateChanged; + this.fillAndFire(t, this.query.playRateChanged(e)) + } + sendSwitchComplete(e) { + var t = Nf.SwitchComplete; + this.fillAndFire(t, this.query.switchComplete(e)) + } + sendVariantEnded(e) { + var t = Nf.VariantEnded; + this.fillAndFire(t, this.query.variantEnded(e)) + } + sendPlayError(e) { + var t = Nf.PlayError; + this.fillAndFire(t, this.query.playError(e)) + } + sendNwError(e) { + var t = Nf.NwError; + this.fillAndFire(t, this.query.nwError(e)) + } + sendPeriodic(e) { + var t = Nf.Periodic; + this.fillAndFire(t, this.query.periodic(e)) + } + fillAndFire(e, t) { + if (e !== Nf.Periodic || t.PlayTimeWC || t.ADT) { + var r = e === Nf.PlayEnded || e === Nf.Periodic ? 1 : 0; + if (this.reportingAgent) { + let i = {}; + Object.entries(t).forEach(([e, t]) => { + "object" == typeof(t = ne(t) ? Number(Number(t).toFixed(2)) : t) ? "ServerInfo" === e && Object.entries(t).forEach(([e, t]) => { + i[e] = t + }): i[e] = t + }), i = JSON.parse(JSON.stringify(i)); + try { + this.reportingAgent.issueReportingEvent(e, i, r) + } catch (e) {} + } + } + } + } + class Uf extends kl { + constructor(e, t) { + super(e), this.logger = t + } + get activeEntity() { + return this.getActive() + } + entity(e) { + return this.getEntity(e) + } + playEnded(e) { + return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.playEndedRecord + } + periodic(e) { + return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.periodicRecord + } + playStalled(e) { + return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.playStalledRecord + } + mediaEngineStalled(e) { + return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.mediaEngineStalledRecord + } + keySessionComplete(e) { + return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.keySessionCompleteRecord + } + playLikelyToKeepUp(e) { + return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.playLikelyToKeepUpRecord + } + playRateChanged(e) { + return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.playRateChangedRecord + } + switchComplete(e) { + return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.switchCompleteRecord + } + variantEnded(e) { + return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.variantEndedRecord + } + playError(e) { + return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.playErrorRecord + } + nwError(e) { + return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.nwErrorRecord + } + } + class $f extends fl { + constructor(e) { + super({}, { + name: "rtc-store", + idKey: "itemId", + producerFn: su, + resettable: !0 + }), this.logger = e + } + createEntity(e) { + e = { + itemId: e, + sessionControlRecord: { + state: "RTC_STATE_INIT", + rate: 0, + oldRate: 0, + eventStartTime: Date.now(), + sessionStartTime: Date.now(), + lastLikelyToKeepUpTime: Date.now(), + lastPeriodicTime: Date.now(), + playLikelyToKeepUpEventCounter: 1, + periodicEventCounter: 1, + activeKeySessions: {}, + intervalVariantList: {}, + sessionVariantList: {} + }, + playEndedRecord: {}, + periodicRecord: {}, + playStalledRecord: {}, + keySessionCompleteRecord: {}, + playLikelyToKeepUpRecord: {}, + playRateChangedRecord: {}, + playErrorRecord: {}, + mediaEngineStalledRecord: {}, + switchCompleteRecord: {}, + variantEndedRecord: {}, + nwErrorRecord: {} + }; + this.add(e) + } + updateEnded(e) { + this._prepareEventPlayEnded(e) + } + updatePeriodic(e, t) { + this._prepareEventPeriodic(e, { + isFinal: t + }) + } + updateBufferStalled(e, t) { + this.update(e, ({ + sessionControlRecord: e, + variantEndedRecord: t, + periodicRecord: i, + playEndedRecord: r + }) => { + e.rate = 0, t.StallCount = (t.StallCount || 0) + 1, i.StallCount = (i.StallCount || 0) + 1, r.StallCount = (r.StallCount || 0) + 1 + }), this._prepareEventPlayStalled(e, t) + } + updateSegmentKeyLoaded(e, r) { + this.update(e, ({ + sessionControlRecord: e + }) => { + const t = {}, + i = r.timestamp; + t.keyFormat = "identity", t.keyDeliveryTime = r.adt, t.currentMediaTime = r.currentTime, "RTC_STATE_INIT" === e.state && (t.keyInitTime = i - e.sessionStartTime), e.activeKeySessions[r.keyuri] = t + }), this._prepareEventKeySessionComplete(e, r) + } + updateLicenseResponseProcessed(e, i) { + this.update(e, ({ + sessionControlRecord: e + }) => { + const t = e.activeKeySessions[i.keyuri]; + t.licenseResponseProcessTime = i.timestamp - t.licenseResponseSubmitTime, e.lastKeyDeliveryTime = t.keyDeliveryTime = i.timestamp - t.licenseChallengeStartTime, t.currentMediaTime = i.currentTime, e.finishedKeyUri = i.keyuri + }), this._prepareEventKeySessionComplete(e, i) + } + updateLicenseChallengeError(e, i) { + this.update(e, ({ + sessionControlRecord: e + }) => { + const t = e.activeKeySessions[i.keyuri]; + e.lastKeyErrorType = t.keyErrorType = "licenseChallengeError", e.finishedKeyUri = i.keyuri + }), this._prepareEventKeySessionComplete(e, i) + } + updateLicenseResponseError(e, i) { + this.update(e, ({ + sessionControlRecord: e + }) => { + const t = e.activeKeySessions[i.keyuri]; + e.lastKeyErrorType = t.keyErrorType = "licenseResponseError", e.finishedKeyUri = i.keyuri + }), this._prepareEventKeySessionComplete(e, i) + } + updateKeyAborted(e, t) { + this.update(e, ({ + sessionControlRecord: e + }) => { + e.activeKeySessions[t.keyuri].keyErrorType = "keyAborted", e.finishedKeyUri = t.keyuri + }), this._prepareEventKeySessionComplete(e, t) + } + updateCanPlay(e, t) { + this.update(e, ({}) => {}), this._prepareEventPlayLikelyToKeepUp(e, t) + } + updateRateChanged(e, t) { + this.update(e, ({ + sessionControlRecord: e + }) => { + e.rate = 100 * t.rate, 0 < t.rate && 0 === e.oldRate && (e.playInfo || (e.playInfo = []), e.playInfo.push({ + latency: t.latency + }), e.curLevelUrl || (e.curLevelUrl = t.url)) + }), this._prepareEventPlayRateChanged(e, t) + } + updateMediaError(e, r) { + this.update(e, ({ + sessionControlRecord: e, + periodicRecord: t, + playEndedRecord: i + }) => { + t.PlayerErrCount = (t.PlayerErrCount || 0) + 1, i.PlayerErrCount = (i.PlayerErrCount || 0) + 1, r.fatal && (e.rate = 0, t.FatalPlayerErrCount = (t.FatalPlayerErrCount || 0) + 1, i.FatalPlayerErrCount = (i.FatalPlayerErrCount || 0) + 1, i.ErrCode = r.details, i.ErrReason = r.code, i.ErrIsFatal = !0, i.ErrDomain = "mediaError") + }), r.fatal ? this._prepareEventPlayError(e, r) : this.update(e, ({ + sessionControlRecord: e + }) => { + var t = r.details + "/" + r.code; + e.nonFatalPlayErrList[t] = (e.nonFatalPlayErrList[t] || 0) + 1 + }) + } + updateMediaElementError(e, t) { + let i; + try { + i = JSON.parse(t.message) + } catch (e) { + this.logger.warn(`message is not JSON, ignoring; ${t.message}`) + } + var r = i ? parseInt(i.ErrReason) : null, + n = i ? parseInt(i.ErrDetail) : null; + this._prepareEventPlayError(e, { + domain: "mediaElementError", + mediaElemCode: t.code, + mediaElemReason: r, + mediaElemDetail: n, + code: null, + details: null, + fatal: null + }) + } + updateMediaEngineStalled(e, t) { + this.update(e, ({ + sessionControlRecord: e, + variantEndedRecord: t, + periodicRecord: i, + playEndedRecord: r + }) => { + e.rate = 0, t.MediaEngineStallCount = (t.MediaEngineStallCount || 0) + 1, i.MediaEngineStallCount = (i.MediaEngineStallCount || 0) + 1, r.MediaEngineStallCount = (r.MediaEngineStallCount || 0) + 1 + }), this._prepareEventMediaEngineStalled(e, t) + } + updateLevelSwitched(e, d) { + this.update(e, ({ + sessionControlRecord: e, + switchCompleteRecord: t, + periodicRecord: i, + playEndedRecord: r + }) => { + i.SwCnt = (i.SwCnt || 0) + 1, r.SwCnt = (r.SwCnt || 0) + 1; + var n = Date.now(), + s = e.curLevelUrl, + a = d.url, + i = this._getVariantInfo(s, e), + r = this._getVariantInfo(a, e); + let o, l = !1; + s && (o = r.bandwidth < i.bandwidth ? "Down" : "Up", l = r.iframes), t.BadSw = this._isBadSw(o, e.lastSwitchDir || o, l, e.lastLevelIsIframe || l, n, e.lastSwitchTime || n, d.isSeeking), e.lastSwitchDir = o, e.lastSwitchTime = n, e.lastLevelIsIframe = l, e.curLevelUrl = a, e.variantStartTimeMedia = d.currentTime + }), this._prepareEventSwitchComplete(e, d) + } + updateLevelLoadError(e, o) { + this.update(e, ({ + sessionControlRecord: e, + switchCompleteRecord: t, + periodicRecord: i, + playEndedRecord: r + }) => { + var n = Date.now(); + let s, a = !1; + if (e.curLevelUrl) { + const t = this._getVariantInfo(e.curLevelUrl, e), + i = this._getVariantInfo(o.url, e); + s = i.bandwidth < t.bandwidth ? "Down" : "Up", a = i.iframes + } + i.SwCnt = (i.SwCnt || 0) + 1, r.SwCnt = (r.SwCnt || 0) + 1, t.BadSw = this._isBadSw(s, e.lastSwitchDir || s, a, e.lastLevelIsIframe || a, n, e.lastSwitchTime || n, o.isSeeking), t.SwFail = !0 + }), this._prepareEventSwitchComplete(e, o) + } + updateVariantEnd(e, t) { + this._prepareEventVariantEnded(e, t) + } + updateNwError(e, r) { + this.update(e, ({ + sessionControlRecord: e, + periodicRecord: t, + playEndedRecord: i + }) => { + t.NwErrCount = (t.NwErrCount || 0) + 1, i.NwErrCount = (i.NwErrCount || 0) + 1, r.fatal && (e.rate = 0, t.FatalNwErrCount = (t.FatalNwErrCount || 0) + 1, i.FatalNwErrCount = (i.FatalNwErrCount || 0) + 1, i.ErrCode = r.details, i.ErrReason = r.code, i.ErrIsFatal = !0, i.ErrDomain = "networkError") + }), r.fatal ? this._prepareEventNwError(e, r) : this.update(e, ({ + sessionControlRecord: e + }) => { + var t = r.details + "/" + r.code; + e.nonFatalNwErrList[t] = (e.nonFatalNwErrList[t] || 0) + 1 + }) + } + finalize(e, t) { + switch (t) { + case Nf.PlayEnded: + this.update(e, ({ + sessionControlRecord: e + }) => { + e.state = "RTC_STATE_STOP", e.oldRate = 0 + }), this.update(e, e => { + e.playEndedRecord = {} + }); + break; + case Nf.Periodic: + this.update(e, ({ + sessionControlRecord: t + }) => { + t.lastPeriodicTime = Date.now(), t.periodicEventCounter += 1, Object.keys(t.intervalVariantList).forEach(e => { + t.intervalVariantList[e].playTime = 0 + }) + }), this.update(e, e => { + e.periodicRecord = {} + }); + break; + case Nf.PlayStalled: + this.update(e, ({ + sessionControlRecord: e + }) => { + e.state = "RTC_STATE_STALL", e.oldRate = 0 + }), this.update(e, e => { + e.playStalledRecord = {} + }); + break; + case Nf.KeySessionComplete: + this.update(e, ({ + sessionControlRecord: e + }) => { + delete e.activeKeySessions[e.finishedKeyUri] + }), this.update(e, e => { + e.keySessionCompleteRecord = {} + }); + break; + case Nf.PlayLikelyToKeepUp: + this.update(e, ({ + sessionControlRecord: e + }) => { + "RTC_STATE_PLAY" !== e.state && (e.state = "RTC_STATE_CANPLAY", e.lastLikelyToKeepUpTime = Date.now(), e.playLikelyToKeepUpEventCounter += 1) + }), this.update(e, e => { + e.playLikelyToKeepUpRecord = {} + }); + break; + case Nf.PlayRateChanged: + this.update(e, ({ + sessionControlRecord: e, + playEndedRecord: t, + playStalledRecord: i, + mediaEngineStalledRecord: r, + playErrorRecord: n, + nwErrorRecord: s + }) => { + 0 !== e.rate ? (e.state = "RTC_STATE_PLAY", delete t.LastStall, delete t.LastMediaEngineStall, delete t.LastPause, delete n.LastPause, delete s.LastPause) : (e.state = "RTC_STATE_PAUSE", delete i.LastResume, delete r.LastResume, delete n.LastResume, delete s.LastResume), e.oldRate = e.rate + }), this.update(e, e => { + e.playRateChangedRecord = {} + }); + break; + case Nf.PlayError: + this.update(e, ({ + sessionControlRecord: e + }) => { + e.state = "RTC_STATE_PLAYERROR" + }), this.update(e, e => { + e.playErrorRecord = {} + }); + break; + case Nf.MediaEngineStalled: + this.update(e, ({ + sessionControlRecord: e + }) => { + e.state = "RTC_STATE_MEDIAENGINESTALL", e.oldRate = 0 + }), this.update(e, e => { + e.mediaEngineStalledRecord = {} + }); + break; + case Nf.SwitchComplete: + this.update(e, e => { + e.switchCompleteRecord = {}, e.sessionControlRecord.prevLevelUrl = e.sessionControlRecord.curLevelUrl + }); + break; + case Nf.VariantEnded: + this.update(e, ({ + sessionControlRecord: e + }) => { + e.decodedFramesForVariant = 0, e.decodedFramesForVariantSampleCount = 0 + }), this.update(e, e => { + e.variantEndedRecord = {} + }); + break; + case Nf.NwError: + this.update(e, ({ + sessionControlRecord: e + }) => { + e.state = "RTC_STATE_NWERROR" + }), this.update(e, e => { + e.nwErrorRecord = {} + }) + } + this.update(e, ({ + sessionControlRecord: e + }) => { + e.eventStartTime = Date.now() + }) + } + updatePlaybackInfo(e, s) { + this.update(e, ({ + sessionControlRecord: e, + periodicRecord: t, + playEndedRecord: i + }) => { + s.droppedVideoFrames < e.droppedVideoFrames && (e.droppedVideoFrames = 0), s.decodedFrameCount < e.decodedFrameCount && (e.decodedFrameCount = 0); + var r = s.droppedVideoFrames - (e.droppedVideoFrames || 0), + n = s.decodedFrameCount - (e.decodedFrameCount || 0); + e.droppedVideoFrames = s.droppedVideoFrames, e.decodedFrameCount = s.decodedFrameCount, e.decodedFramesForVariant += n, e.decodedFramesForVariantSampleCount += 1, r && (3 <= r ? (t.GroupViFrDr = (t.GroupViFrDr || 0) + r, t.GroupViFrDrEvtCount = (t.GroupViFrDrEvtCount || 0) + 1, i.GroupViFrDr = (i.GroupViFrDr || 0) + r, i.GroupViFrDrEvtCount = (i.GroupViFrDrEvtCount || 0) + 1) : (t.SparseViFrDr = (t.SparseViFrDr || 0) + r, t.SparseViFrDrEvtCount = (t.SparseViFrDrEvtCount || 0) + 1, i.SparseViFrDr = (i.SparseViFrDr || 0) + r, i.SparseViFrDrEvtCount = (i.SparseViFrDrEvtCount || 0) + 1)) + }) + } + updateBufferAppended(e, t) { + this.update(e, ({ + sessionControlRecord: e + }) => { + e.bufferAppendInfo || (e.bufferAppendInfo = []), e.bufferAppendInfo.push(t) + }) + } + updateSeeked(e, t) { + this.update(e, ({ + sessionControlRecord: e + }) => { + e.seekInfo || (e.seekInfo = []), e.seekInfo.push(t) + }) + } + updateManifestParsed(e, r) { + this.update(e, ({ + sessionControlRecord: e, + periodicRecord: t, + playEndedRecord: i + }) => { + t.MasterPlaylistADT = (t.MasterPlaylistADT || 0) + r.adt, i.MasterPlaylistADT = (i.MasterPlaylistADT || 0) + r.adt; + t = this._computeVariantInfo(r.levels); + i.IsAudioOnly = r.isAudioOnly, i.IsGapless = r.isGapless, i.IsFirstItem = r.isFirstItem, i.ItemID = r.itemID, i.MaxVideoQltyIndex = t.maxVideoQltyIndex, i.MaxReWd = t.maxWidth, i.MaxReHt = t.maxHeight, e.manifestData = { + variantList: t.variantList, + varListString: t.varListString + } + }) + } + updateFragLoaded(e, r) { + this.update(e, ({ + sessionControlRecord: e, + periodicRecord: t, + playEndedRecord: i + }) => { + if (t.MediaRequestsSent = (t.MediaRequestsSent || 0) + 1, i.MediaRequestsSent = (i.MediaRequestsSent || 0) + 1, r.cdnServer) { + const e = r.cdnServer.toLowerCase(); + "aapl" !== e && "akam" !== e && "llnw" !== e || (i.LastMediaCDNServer = e) + } + r.serverInfo && (i.ServerInfo = r.serverInfo), e.segmentMimeTypes || (e.segmentMimeTypes = []), void 0 === e.segmentMimeTypes.find(e => e == r.contentType) && e.segmentMimeTypes.push(r.contentType), r.fragType === gu.Variant ? (e.variantVideoBytes = (e.variantVideoBytes || 0) + r.bytes, e.variantVideoDuration = (e.variantVideoDuration || 0) + r.duration, e.intervalVideoBytes = (e.intervalVideoBytes || 0) + r.bytes, e.intervalVideoDuration = (e.intervalVideoDuration || 0) + r.duration, e.sessionVideoBytes = (e.sessionVideoBytes || 0) + r.bytes, e.sessionVideoDuration = (e.sessionVideoDuration || 0) + r.duration, e.obrLast = 8 * r.bytes / (r.duration / 1e3), t.NetBytes = (t.NetBytes || 0) + r.bytes, t.ADT = (t.ADT || 0) + r.adt, t.SegmentProcessTime = (t.SegmentProcessTime || 0) + r.processTime, i.ADT = (i.ADT || 0) + r.adt, i.NetBytes = (i.NetBytes || 0) + r.bytes, i.SegmentProcessTime = (i.SegmentProcessTime || 0) + r.processTime) : r.fragType === gu.AltAudio && (e.variantAudioBytes = (e.variantAudioBytes || 0) + r.bytes, e.variantAudioDuration = (e.variantAudioDuration || 0) + r.duration, e.intervalAudioBytes = (e.intervalAudioBytes || 0) + r.bytes, e.intervalAudioDuration = (e.intervalAudioDuration || 0) + r.duration, e.sessionAudioBytes = (e.sessionAudioBytes || 0) + r.bytes, e.sessionAudioDuration = (e.sessionAudioDuration || 0) + r.duration) + }) + } + updateFragBuffered(e, i) { + this.update(e, ({ + periodicRecord: e, + playEndedRecord: t + }) => { + i.fragType === gu.Variant && (e.SegmentParseTime = (e.SegmentParseTime || 0) + i.parseTime, t.SegmentParseTime = (t.SegmentParseTime || 0) + i.parseTime) + }) + } + updateLevelLoaded(e, n) { + this.update(e, ({ + sessionControlRecord: e, + playLikelyToKeepUpRecord: t, + periodicRecord: i, + playEndedRecord: r + }) => { + t.PlaylistADT = (t.PlaylistADT || 0) + n.adt, i.PlaylistADT = (i.PlaylistADT || 0) + n.adt, r.PlaylistADT = (r.PlaylistADT || 0) + n.adt, i.MaxPlaylistDT = n.adt > i.MaxPlaylistDT ? n.adt : i.MaxPlaylistDT, r.MaxPlaylistDT = n.adt > r.MaxPlaylistDT ? n.adt : r.MaxPlaylistDT, r.PlayType = n.playType, this._setTargetDuration(n.url, n.targetduration, e), e.playlistMimeTypes || (e.playlistMimeTypes = []), void 0 === e.playlistMimeTypes.find(e => e == n.contentType) && e.playlistMimeTypes.push(n.contentType); + i = n.url, r = this._getVariantInfo(i, e); + e.intervalVariantList[i] || (e.intervalVariantList[i] = Object.assign({}, r)), e.sessionVariantList[i] || (e.sessionVariantList[i] = Object.assign({}, r)) + }) + } + updateLevelsChanged(e, r) { + this.update(e, ({ + sessionControlRecord: t, + playEndedRecord: e + }) => { + const i = this._computeVariantInfo(r.levels); + e.MaxVideoQltyIndex = i.maxVideoQltyIndex, e.MaxReWd = i.maxWidth, e.MaxReHt = i.maxHeight, t.manifestData = { + variantList: i.variantList, + varListString: i.varListString + }, Object.keys(i.variantList).forEach(e => { + t.intervalVariantList[e] && (t.intervalVariantList[e].brRnk = i.variantList[e].brRnk), t.sessionVariantList[e] && (t.sessionVariantList[e].brRnk = i.variantList[e].brRnk) + }) + }) + } + updateLicenseChallengeRequested(e, r) { + this.update(e, ({ + sessionControlRecord: e + }) => { + const t = {}, + i = r.timestamp; + t.licenseChallengeStartTime = i, "RTC_STATE_INIT" === e.state && (t.keyInitTime = i - e.sessionStartTime), e.activeKeySessions[r.keyuri] = t + }) + } + updateLicenseChallengeReceived(e, i) { + this.update(e, ({ + sessionControlRecord: e + }) => { + const t = e.activeKeySessions[i.keyuri]; + t.licenseChallengeRequestTime = i.timestamp - t.licenseChallengeStartTime + }) + } + updateLicenseChallengeSubmitted(e, i) { + this.update(e, ({ + sessionControlRecord: e + }) => { + const t = e.activeKeySessions[i.keyuri]; + t.keyFormat = i.keyFormat, t.licenseChallengeSubmitTime = i.timestamp + }) + } + updateLicenseChallengeCreated(e, i) { + this.update(e, ({ + sessionControlRecord: e + }) => { + const t = e.activeKeySessions[i.keyuri]; + t.cdmVersion = i.cdmVersion, t.licenseChallengeCreationTime = i.timestamp - t.licenseChallengeSubmitTime + }) + } + updateLicenseResponseRequested(e, t) { + this.update(e, ({ + sessionControlRecord: e + }) => { + e.activeKeySessions[t.keyuri].licenseResponseRequestTime = t.timestamp + }) + } + updateLicenseResponseReceived(e, i) { + this.update(e, ({ + sessionControlRecord: e + }) => { + const t = e.activeKeySessions[i.keyuri]; + t.licenseResponseReceiveTime = i.timestamp - t.licenseResponseRequestTime + }) + } + updateLicenseResponseSubmitted(e, t) { + this.update(e, ({ + sessionControlRecord: e + }) => { + e.activeKeySessions[t.keyuri].licenseResponseSubmitTime = t.timestamp + }) + } + _prepareEventPlayEnded(e) { + this.update(e, ({ + sessionControlRecord: e, + playEndedRecord: t + }) => { + t.AvgVideoBitrate = 8 * (e.sessionVideoBytes || 0) / (e.sessionVideoDuration || 1), t.AvgAudioBitrate = 8 * (e.sessionAudioBytes || 0) / (e.sessionAudioDuration || 1); + var i = 1e3 * Math.round((t.NetBytes || 0) / 1e3); + t.NetBytes = i; + var r = t.ADT || 1, + n = t.ADT + t.SegmentProcessTime || 1, + s = t.ADT + t.SegmentProcessTime + t.SegmentParseTime || 1; + t.TWOBR = 8 * i / (r / 1e3), t.PerceivedTWOBR = 8 * i / (n / 1e3), t.NetTWOBR = 8 * i / (s / 1e3); + var s = this._findTimeWeightedValues("RTC_STATE_PLAY" === e.state ? Date.now() - e.eventStartTime : 0, e.sessionVariantList, e.curLevelUrl); + t.PlayerTWIBR = s.twIBR, t.PlayerTWIABR = s.twIABR, t.TWBitRk = s.twBRnk; + s = this._getVariantInfo(e.curLevelUrl, e); + t.TargetDur = s.targetduration, t.ReWd = s.width, t.ReHt = s.height, t.VariantList = null === (s = e.manifestData) || void 0 === s ? void 0 : s.varListString; + let a = ""; + e.playlistMimeTypes && (e.playlistMimeTypes.forEach(e => { + a += e + "," + }), a = a.slice(0, -1)); + let o = ""; + e.segmentMimeTypes && (e.segmentMimeTypes.forEach(e => { + o += e + "," + }), o = o.slice(0, -1)), t.PlaylistMimeType = a, t.SegmentMimeType = o, t.Rate = e.rate + }), this._aggregateTimes(e) + } + _prepareEventPeriodic(e, o) { + this.update(e, ({ + sessionControlRecord: e, + periodicRecord: t, + playEndedRecord: i + }) => { + t.EventCounter = e.periodicEventCounter, t.PInterval = Date.now() - e.lastPeriodicTime, t.AvgVideoBitrate = 8 * (e.intervalVideoBytes || 0) / (e.intervalVideoDuration || 1), t.AvgAudioBitrate = 8 * (e.intervalAudioBytes || 0) / (e.intervalAudioDuration || 1); + let r = t.NetBytes || 0; + o.isFinal && (t.NetBytes = r = 1e3 * Math.round(r / 1e3)); + var n = t.ADT || 1, + s = t.ADT + t.SegmentProcessTime || 1, + a = t.ADT + t.SegmentProcessTime + t.SegmentParseTime || 1; + t.TWOBR = 8 * r / (n / 1e3), t.PerceivedTWOBR = 8 * r / (s / 1e3), t.NetTWOBR = 8 * r / (a / 1e3); + var a = this._findTimeWeightedValues("RTC_STATE_PLAY" === e.state ? Date.now() - e.eventStartTime : 0, e.intervalVariantList, e.curLevelUrl); + t.PlayerTWIBR = a.twIBR, t.PlayerTWIABR = a.twIABR, t.TWBitRk = a.twBRnk; + a = this._getVariantInfo(e.curLevelUrl, e); + t.TargetDur = a.targetduration, t.ReWd = a.width, t.ReHt = a.height, t.VariantList = null === (a = e.manifestData) || void 0 === a ? void 0 : a.varListString, t.Rate = e.rate; + e = this._computeMediaStats(e); + e && (t.MedianBufferAppendLatency = e.bufLatencyInfo.median, t.MaxBufferAppendLatency = e.bufLatencyInfo.max, t.MedianBufferAppendSize = e.bufSizeInfo.median, t.MaxBufferAppendSize = e.bufSizeInfo.max, t.MedianSeekLatency = e.seekLatencyInfo.median, t.MaxSeekLatency = e.seekLatencyInfo.max, t.MedianPlayLatency = e.playLatencyInfo.median, t.MaxPlayLatency = e.playLatencyInfo.max), this._copyCommonKeys(t, i) + }), this._aggregateTimes(e) + } + _prepareEventPlayStalled(e, s) { + this.update(e, ({ + sessionControlRecord: e, + playStalledRecord: t, + playEndedRecord: i + }) => { + var r = this._getVariantInfo(e.curLevelUrl, e), + n = Date.now(); + t.MediaDur = s.mediaDur, t.BitRnk = r.brRnk, t.Codecs = r.codecs, t.LastLikelyToKeepUp = n - e.lastLikelyToKeepUpTime, t.LastSwitch = n - e.lastSwitchTime, t.StallDetectionTime = s.stallDurationMs, t.StallType = s.type, t.BufferLength = s.bufferLen, "networkError" === i.ErrDomain && (t.NwErrTime = i.NwErrTime, t.NwErrCode = i.ErrCode), t.LaSwDir = e.lastSwitchDir, t.TargetDur = r.targetduration, t.PlayerIABR = r.avgBandwidth, t.PlayerIBR = r.bandwidth, t.Rate = e.rate, t.OBRLast = e.obrLast, t.OBRMean = 8 * i.NetBytes / (i.ADT / 1e3), this._copyCommonKeys(t, i) + }), this._aggregateTimes(e) + } + _prepareEventKeySessionComplete(e, r) { + this.update(e, ({ + sessionControlRecord: e, + keySessionCompleteRecord: t, + playEndedRecord: i + }) => { + e = e.activeKeySessions[r.keyuri]; + e ? (t.KeyFormat = e.keyFormat, t.CDMVersion = e.cdmVersion, t.LicenseChallengeRequestTime = e.licenseChallengeRequestTime, t.LicenseChallengeCreationTime = e.licenseChallengeCreationTime, t.LicenseResponseReceiveTime = e.licenseResponseReceiveTime, t.LicenseResponseProcessTime = e.licenseResponseProcessTime, t.KeyDeliveryTime = e.keyDeliveryTime, t.CurrentMediaTime = 1e3 * e.currentMediaTime, t.KeyInitTime = e.keyInitTime, t.KeyErrorType = e.keyErrorType, this._copyCommonKeys(t, i)) : this.logger.warn(`_prepareEventKeySessionComplete no keySessionInfo for ${le(r.keyuri)}`) + }), this._aggregateTimes(e) + } + _prepareEventPlayLikelyToKeepUp(e, n) { + this.update(e, ({ + sessionControlRecord: e, + playLikelyToKeepUpRecord: t, + playEndedRecord: i + }) => { + var r = this._getVariantInfo(e.curLevelUrl, e); + t.EventCounter = e.playLikelyToKeepUpEventCounter, t.PlayerIABR = r.avgBandwidth, t.PlayerIBR = r.bandwidth, t.MediaDur = n.mediaDur, t.BitRnk = r.brRnk, t.Codecs = r.codecs, t.TargetDur = r.targetduration, this._copyCommonKeys(t, i) + }), this._aggregateTimes(e) + } + _prepareEventPlayRateChanged(e, n) { + this.update(e, ({ + sessionControlRecord: e, + playRateChangedRecord: t, + playEndedRecord: i + }) => { + var r = this._getVariantInfo(e.curLevelUrl, e); + t.Rate = e.rate, t.StNPT = n.currentTime, t.PlayerIABR = r.avgBandwidth, t.PlayerIBR = r.bandwidth, t.BitRnk = r.brRnk, t.MediaDur = n.mediaDur, t.Codecs = r.codecs, this._copyCommonKeys(t, i) + }), this._aggregateTimes(e) + } + _prepareEventPlayError(e, s) { + this.update(e, ({ + sessionControlRecord: e, + playErrorRecord: t, + playEndedRecord: i + }) => { + var r = this._getVariantInfo(e.curLevelUrl, e), + n = Date.now(); + "mediaElementError" == s.domain ? (t.ErrDomain = t.ErrCode = "mediaElementError", t.ErrIsFatal = !0, t.ErrCodeMediaElement = s.mediaElemCode, t.ErrReason = s.mediaElemReason, t.ErrDetail = s.mediaElemDetail) : (t.ErrCode = s.details, t.ErrReason = s.code, t.ErrIsFatal = s.fatal, t.ErrDomain = "mediaError"), t.PlayerErrCount = s.count || 1, t.BitRnk = r.brRnk, t.MediaDur = s.mediaDur, t.PlayTime = i.PlayTime, t.PlayTimeWC = i.PlayTimeWC, t.PlayerIABR = r.avgBandwidth, t.PlayerIBR = r.bandwidth, t.LastLikelyToKeepUp = n - e.lastLikelyToKeepUpTime, t.LastSwitch = n - e.lastSwitchTime, t.VideoQltyIndex = r.qltyIndex, t.Rate = e.rate, t.KeyErrorType = e.lastKeyErrorType, t.KeyDeliveryTime = e.lastKeyDeliveryTime, this._copyCommonKeys(t, i) + }), this._aggregateTimes(e) + } + _prepareEventMediaEngineStalled(e, s) { + this.update(e, ({ + sessionControlRecord: e, + mediaEngineStalledRecord: t, + playEndedRecord: i + }) => { + var r = Date.now(), + n = this._getVariantInfo(e.curLevelUrl, e); + t.MediaDur = s.mediaDur, t.BitRnk = n.brRnk, t.Codecs = n.codecs, t.LastLikelyToKeepUp = r - e.lastLikelyToKeepUpTime, t.LastSwitch = r - e.lastSwitchTime, t.StallDetectionTime = s.stallDurationMs, t.StallType = s.type, t.BufferLength = s.bufferLen, t.LaSwDir = e.lastSwitchDir, t.TargetDur = n.targetduration, t.PlayerIABR = n.avgBandwidth, t.PlayerIBR = n.bandwidth, t.Rate = e.rate, t.OBRLast = e.obrLast, t.OBRMean = 8 * i.NetBytes / (i.ADT / 1e3), this._copyCommonKeys(t, i) + }), this._aggregateTimes(e) + } + _prepareEventSwitchComplete(e, s) { + this.update(e, ({ + sessionControlRecord: e, + switchCompleteRecord: t, + playEndedRecord: i + }) => { + var r = this._getVariantInfo(e.prevLevelUrl, e), + n = this._getVariantInfo(e.curLevelUrl, e); + t.FrBitRnk = r.brRnk, t.ToBitRnk = n.brRnk, t.TimeToBitrate = Date.now() - e.sessionStartTime, t.MediaDur = s.mediaDur, t.Rate = e.rate, t.PlayerIBR = n.bandwidth, t.PlayerIABR = n.avgBandwidth, t.LastPlayerIBR = r.bandwidth, t.LastPlayerIABR = r.avgBandwidth, t.ReWd = n.width, t.ReHt = n.height, this._copyCommonKeys(t, i) + }), this._aggregateTimes(e) + } + _prepareEventVariantEnded(e, n) { + this.update(e, ({ + sessionControlRecord: e, + variantEndedRecord: t, + playEndedRecord: i + }) => { + var r = this._getVariantInfo(e.curLevelUrl, e); + t.Rate = e.rate, t.VarAvgBitrate = r.avgBandwidth, t.VarPeakBitrate = r.bandwidth, t.VarBitRk = r.brRnk, t.VarSTTime = e.variantStartTimeMedia, t.VarEndTime = 1e3 * n.currentTime, t.IFR = r.framerate, t.ODR = e.decodedFramesForVariant / e.decodedFramesForVariantSampleCount, t.ReWd = r.width, t.ReHt = r.height, t.Codecs = r.codecs, t.AvgVideoBitrate = 8 * (e.variantVideoBytes || 0) / (e.variantVideoDuration || 1), t.AvgAudioBitrate = 8 * (e.variantAudioBytes || 0) / (e.variantAudioDuration || 1), this._copyCommonKeys(t, i) + }), this._aggregateTimes(e) + } + _prepareEventNwError(e, n) { + this.update(e, ({ + sessionControlRecord: e, + nwErrorRecord: t, + playEndedRecord: i + }) => { + var r = this._getVariantInfo(e.curLevelUrl, e); + t.ErrCode = n.details, t.ErrReason = n.code, t.ErrIsFatal = n.fatal, t.NwErrCount = n.count || 1, t.ErrDomain = "networkError", t.PlayTime = i.PlayTime, t.PlayTimeWC = i.PlayTimeWC, t.BitRnk = r.brRnk, t.Rate = e.rate, t.KeyErrorType = e.lastKeyErrorType, t.KeyDeliveryTime = e.lastKeyDeliveryTime, this._copyCommonKeys(t, i) + }), this._aggregateTimes(e) + } + _copyCommonKeys(e, t) { + e.PlayType = t.PlayType, e.LastMediaCDNServer = t.LastMediaCDNServer, e.MaxVideoQltyIndex = t.MaxVideoQltyIndex, e.MaxReWd = t.MaxReWd, e.MaxReHt = t.MaxReHt, e.IsGapless = t.IsGapless, e.IsAudioOnly = t.IsAudioOnly, e.IsFirstItem = t.IsFirstItem, e.ItemID = t.ItemID, e.ServerInfo = t.ServerInfo + } + _computeVariantInfo(e) { + const i = {}; + let r = 0, + n = 0, + s = 0, + a = 0; + const o = this._getMaxNormalizedPeak(e); + e.forEach(e => { + if (e.attrs) { + const r = this._getVideoFourCC(e.attrs.CODECS), + n = this._getVideoQualityIndex(r, e.attrs["VIDEO-RANGE"]) || 0, + s = { + codecs: e.attrs.CODECS, + width: e.width, + height: e.height, + bandwidth: e.attrs.BANDWIDTH, + avgBandwidth: e.attrs["AVERAGE-BANDWIDTH"], + framerate: e.attrs["FRAME-RATE"], + iframes: e.iframes, + brRnk: this._getBitrateRank(e.attrs.BANDWIDTH, r, o), + qltyIndex: n, + targetduration: 0, + playTime: 0 + }; + a = n > a ? n : a, i[e.url] = s + } + var t = e.width * e.height; + t > s && (s = t, r = e.width, n = e.height) + }); + e = 0 < Object.keys(i).length ? Object.values(i).map(e => e.bandwidth + ":" + e.avgBandwidth).join(",") : void 0; + return { + variantList: i, + varListString: e, + maxVideoQltyIndex: a, + maxWidth: r, + maxHeight: n + } + } + _getMaxNormalizedPeak(e) { + let n = 0; + return e.forEach(e => { + e = e.attrs; + if (e) { + const t = e.BANDWIDTH || 0, + i = this._getNormalizedPeak(t, this._getVideoFourCC(e.CODECS)), + r = Math.max(t, i); + n = r > n ? r : n + } + }), n + } + _getNormalizedPeak(e, t) { + return "object" == typeof Ff[t] ? 1.5 * e : +e + } + _getVideoFourCC(e) { + return e && e.split(",").map(e => e.split(".")[0].trim()).find(e => !!Ff[e]) + } + _getVideoQualityIndex(e, t) { + return "object" == typeof Ff[e] ? Ff[e][t] : Ff[e] + } + _getBitrateRank(e, t, i) { + t = Math.max(1, this._getNormalizedPeak(e, t)); + return Math.ceil(100 * t / i) + } + _findTimeWeightedValues(o, l, d) { + const e = { + twBRnk: 0, + twIBR: 0, + twIABR: 0 + }; + if (l) { + let i = 0, + r = 0, + n = 0, + s = 0, + a = 0; + Object.values(l).forEach(e => { + let t = e.playTime; + e === l[d] && (t += o || 0), t && (r += e.bandwidth * t, i += e.brRnk * t, n += t, e.avgBandwidth && (s += e.avgBandwidth * t, a += t)) + }), n && (e.twBRnk = i / n, e.twIBR = r / n), s && a && (e.twIABR = s / a) + } + return e + } + _computeMediaStats(e) { + const t = e.bufferAppendInfo; + let i = []; + const r = []; + t && t.forEach && t.forEach(e => { + i.push(e.latency), r.push(e.size) + }); + var n = this._computeStats(i), + s = this._computeStats(r), + a = e.seekInfo; + i = [], a && a.forEach && e.seekInfo.forEach(e => i.push(e.latency)); + var o = this._computeStats(i), + a = e.playInfo; + return i = [], a && a.forEach && e.playInfo.forEach(e => i.push(e.latency)), { + bufLatencyInfo: n, + bufSizeInfo: s, + seekLatencyInfo: o, + playLatencyInfo: this._computeStats(i) + } + } + _computeStats(e) { + let t, i; + if (e) { + const r = e.filter(e => 0 < e); + if (0 < r.length) { + t = r.reduce((e, t) => Math.max(e, t)); + const e = r.length, + n = r.sort((e, t) => e - t), + s = Math.ceil(e / 2); + i = e % 2 == 0 ? (n[s] + n[s - 1]) / 2 : n[s - 1] + } + } + return { + max: t, + median: i + } + } + _getVariantInfo(e, t) { + return e && t.manifestData && t.manifestData.variantList && t.manifestData.variantList[e] ? t.manifestData.variantList[e] : {} + } + _setTargetDuration(e, t, i) { + e && i.manifestData && i.manifestData.variantList && i.manifestData.variantList[e] && (i.manifestData.variantList[e].targetduration = t) + } + _isBadSw(e, t, i, r, n, s, a) { + let o = n - s < 1e4 && t !== e && r === i && !a ? !0 : !1; + return o + } + _aggregateTimes(e) { + this.update(e, ({ + sessionControlRecord: t, + playLikelyToKeepUpRecord: i, + playStalledRecord: r, + mediaEngineStalledRecord: n, + switchCompleteRecord: s, + playRateChangedRecord: a, + variantEndedRecord: o, + playErrorRecord: l, + nwErrorRecord: d, + periodicRecord: u, + playEndedRecord: c + }) => { + var h = Date.now() - t.eventStartTime; + switch (t.state) { + case "RTC_STATE_INIT": + i.StartupTime = (i.StartupTime || 0) + h, u.InitTime = (u.InitTime || 0) + h, c.InitTime = (c.InitTime || 0) + h; + break; + case "RTC_STATE_CANPLAY": + a.StartupTime = (a.StartupTime || 0) + h, u.InitTime = (u.InitTime || 0) + h, c.InitTime = (c.InitTime || 0) + h; + break; + case "RTC_STATE_PAUSE": + u.PauseTime = (u.PauseTime || 0) + h, c.PauseTime = (c.PauseTime || 0) + h, a.LastPause = (a.LastPause || 0) + h, c.LastPause = (c.LastPause || 0) + h, l.LastPause = (l.LastPause || 0) + h, d.LastPause = (d.LastPause || 0) + h; + break; + case "RTC_STATE_STALL": + o.StallTime = (o.StallTime || 0) + h, u.StallTime = (u.StallTime || 0) + h, c.StallTime = (c.StallTime || 0) + h, a.LastStall = (a.LastStall || 0) + h, l.LastStall = (l.LastStall || 0) + h, c.LastStall = (c.LastStall || 0) + h; + break; + case "RTC_STATE_MEDIAENGINESTALL": + o.MediaEngineStallTime = (o.MediaEngineStallTime || 0) + h, u.MediaEngineStallTime = (u.MediaEngineStallTime || 0) + h, c.MediaEngineStallTime = (c.MediaEngineStallTime || 0) + h, a.LastMediaEngineStall = (a.LastMediaEngineStall || 0) + h, l.LastMediaEngineStall = (l.LastMediaEngineStall || 0) + h, c.LastMediaEngineStall = (c.LastMediaEngineStall || 0) + h; + break; + case "RTC_STATE_NWERROR": + c.NwErrTime = (c.NwErrTime || 0) + h, u.NwErrTime = (u.NwErrTime || 0) + h; + break; + case "RTC_STATE_PLAYERROR": + u.PlayErrTime = (u.PlayErrTime || 0) + h, c.PlayErrTime = (c.PlayErrTime || 0) + h; + break; + case "RTC_STATE_PLAY": { + a.RateChangePlayTime = (a.RateChangePlayTime || 0) + h, s.PlayTime = (s.PlayTime || 0) + h, s.PlayTimeLastSW = (s.PlayTimeLastSW || 0) + h * (t.oldRate / 100), r.LastResume = (r.LastResume || 0) + h, n.LastResume = (n.LastResume || 0) + h, l.LastResume = (l.LastResume || 0) + h, d.LastResume = (d.LastResume || 0) + h, o.VarPlayTimeWC = (o.VarPlayTimeWC || 0) + h, o.VarPlayTime = (o.VarPlayTime || 0) + h * (t.oldRate / 100), u.PlayTimeWC = (u.PlayTimeWC || 0) + h, u.PlayTime = (u.PlayTime || 0) + h * (t.oldRate / 100), c.PlayTimeWC = (c.PlayTimeWC || 0) + h, c.PlayTime = (c.PlayTime || 0) + h * (t.oldRate / 100), n.PlayTime = c.PlayTime, r.PlayTime = c.PlayTime; + const i = t.curLevelUrl; + let e = t.intervalVariantList[i]; + e.playTime = (e.playTime || 0) + h, e = t.sessionVariantList[i], e.playTime = (e.playTime || 0) + h; + break + } + } + }) + } + } + const Vf = { + name: "rtc-service" + }; + class Kf { + constructor(e, t, i, r) { + this.hls = e, this.config = t, this.accessLog = i, this.logger = r, this.destroy$ = new Xt, this.isSeeking = !1, this.seekStart = null, this.periodicInterval = t.rtcIntervalTimeout || 3e5, this.intervalFunc = null, this.rtcStore = new $f(this.logger), this.rtcQuery = new Uf(this.rtcStore, this.logger), this.rtcComponent = new Bf(this.rtcQuery, this.logger), i.setRTCQuery(this.rtcQuery), this.subscribeAndUpdateStore(), this.registerForEvents() + } + destroy() { + this.destroy$.next(), this.clearPeriodic(), this.rtcStore.reset() + } + detachMedia() { + this.clearPeriodic(); + var e; + try { + e = this.hls.realCurrentTime, this.rtcStore.updateVariantEnd(this.rtcEventItemId(!0), { + currentTime: e + }), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.VariantEnded), this.rtcStore.updatePeriodic(this.rtcEventItemId(!0), !0), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.Periodic), this.rtcStore.updateEnded(this.rtcEventItemId(!0)), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.PlayEnded) + } catch (e) { + this.logger.warn(Vf, e) + } + } + handleError(e) { + var t = e instanceof p ? e : new V(!0, e.message, $.InternalError); + t instanceof uc && !t.fatal ? (this.rtcStore.updateLevelLoadError(this.rtcEventItemId(), { + url: t.url, + mediaDur: this.hls.bufferedDuration, + isSeeking: this.isSeeking + }), this.sendAndFinalize(this.rtcEventItemId(), Nf.SwitchComplete)) : t.type === o ? (this.rtcStore.updateNwError(this.rtcEventItemId(), { + fatal: t.fatal, + details: t.details, + code: null === (e = t.response) || void 0 === e ? void 0 : e.code + }), this.sendAndFinalize(this.rtcEventItemId(), Nf.NwError)) : (this.rtcStore.updateMediaError(this.rtcEventItemId(), { + fatal: t.fatal, + details: t.details, + code: null === (t = t.response) || void 0 === t ? void 0 : t.code + }), this.sendAndFinalize(this.rtcEventItemId(), Nf.PlayError)) + } + handleMediaElementError(e) { + this.rtcStore.updateMediaElementError(this.rtcEventItemId(), e), this.sendAndFinalize(this.rtcEventItemId(), Nf.PlayError) + } + handleFragLoaded(e, t) { + var i, r, n; + this.checkMediaOptionType(e.mediaOptionType) && (e.itemId !== this.rtcEventItemId() && this.logger.warn(Vf, `Frag id does not match current item id. Frag Id=${e.itemId}, playing id=${this.rtcEventItemId(!0)}, loading id=${null===(n=this.hls.loadingItem)||void 0===n?void 0:n.itemId}`), i = t.tload - t.trequest, r = t.tload - t.tfirst, n = this.serverInfoInstance || {}, this.rtcStore.updateFragLoaded(e.itemId, { + fragType: e.mediaOptionType, + bytes: t.loaded, + duration: e.duration, + adt: i, + processTime: r, + contentType: t.contentType, + cdnServer: t.cdnServer, + serverInfo: n + }), this.accessLog.updateFragLoaded(e.itemId, this.isSeeking, { + fragType: e.mediaOptionType, + bytes: t.loaded, + duration: e.duration, + adt: i, + processTime: r, + startPTS: e.start, + endPTS: e.start + e.duration + })) + } + handleFragBuffered(e) { + var t; + this.checkMediaOptionType(e.fragmentType) && (t = e.endDataAppend - e.startDataAppend, this.rtcStore.updateFragBuffered(this.rtcEventItemId(), { + fragType: e.fragmentType, + bytes: e.dataBytesAppend, + parseTime: t + })) + } + handleLevelLoaded(e, t) { + var i; + e ? (e.itemId !== this.rtcEventItemId() && this.logger.warn(Vf, `media option id does not match current item id. media Id=${e.itemId}, current id=${this.rtcEventItemId}`), e.mediaOptionType === gu.Variant && (i = t.tload - t.trequest, this.rtcStore.updateLevelLoaded(this.rtcEventItemId(), { + url: e.url, + targetduration: e.targetduration, + adt: i, + contentType: t.contentType, + playType: e.type + }))) : this.logger.warn(`handleLevelLoaded called with mediaOptionDetails as ${e}`) + } + handleLevelSwitched(e) { + var t = { + url: e.url, + isSeeking: this.isSeeking, + mediaDur: this.hls.bufferedDuration, + currentTime: this.hls.realCurrentTime + }; + e.oldVariant && (this.rtcStore.updateVariantEnd(this.rtcEventItemId(), { + currentTime: this.hls.realCurrentTime + }), this.sendAndFinalize(this.rtcEventItemId(), Nf.VariantEnded)), this.rtcStore.updateLevelSwitched(this.rtcEventItemId(), t), this.sendAndFinalize(this.rtcEventItemId(), Nf.SwitchComplete) + } + handleLevelSwitching(e) { + this.levelSwitchingUrl = e + } + handleLevelsChanged(e) { + this.rtcStore.updateLevelsChanged(this.rtcEventItemId(), { + levels: e + }) + } + handleManifestParsed(e) { + var t = e.stats.tload - e.stats.trequest; + this.rtcStore.updateManifestParsed(this.rtcEventItemId(), { + levels: e.levels, + adt: t, + contentType: e.stats.contentType, + isAudioOnly: this.hls.inGaplessMode, + isGapless: this.hls.inGaplessMode, + isFirstItem: this.hls.isFirstItem, + itemID: ((null === (e = this.hls.reportingAgent) || void 0 === e ? void 0 : e.SessionID) || Zl()) + "-" + this.rtcEventItemId() + }) + } + handleSeek(e) { + if ("SEEKING" === e) this.isSeeking = !0, this.seekStart = Date.now(); + else if ("SEEKED" === e) { + this.isSeeking = !1; + let e = 0; + this.seekStart && (e = Date.now() - this.seekStart), this.seekStart = null, this.rtcStore.updateSeeked(this.rtcEventItemId(!0), { + latency: e + }) + } + } + handleDesiredRateChanged(e, t) { + 0 === t || 1 < Math.abs(e) && 1 < Math.abs(t) ? (this.rtcStore.updateRateChanged(this.rtcEventItemId(!0), { + rate: t, + latency: 0, + mediaDur: this.hls.bufferedDuration, + currentTime: this.hls.realCurrentTime, + url: this.levelSwitchingUrl + }), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.PlayRateChanged)) : 1 !== e && 1 === t && (this.playStart = Date.now()), this.oldRate = e, this.newRate = t + } + handleVariantBufferAppended(e, t) { + let i = 0; + e && (i = Date.now() - e), this.rtcStore.updateBufferAppended(this.rtcEventItemId(), { + latency: i, + size: t + }) + } + handleStalled(e, t) { + var i = { + type: e.type, + stallDurationMs: e.stallDurationMs, + bufferLen: t, + mediaDur: this.hls.bufferedDuration + }, + t = this.rtcQuery.getEntity(this.rtcEventItemId(!0)).sessionControlRecord.state; + e.type === Cp.LowBuffer || e.type === Cp.Seek && e.isLowBufferStall ? "RTC_STATE_PLAY" === t && (this.rtcStore.updateBufferStalled(this.rtcEventItemId(!0), i), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.PlayStalled)) : "RTC_STATE_PLAY" === t && (this.rtcStore.updateMediaEngineStalled(this.rtcEventItemId(!0), i), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.MediaEngineStalled)) + } + handlePlaybackInfo(e, t) { + this.rtcStore.updatePlaybackInfo(this.rtcEventItemId(!0), { + droppedVideoFrames: e, + decodedFrameCount: t + }), this.accessLog.updatePlaybackInfo(this.rtcEventItemId(!0), { + droppedVideoFrames: e, + decodedFrameCount: t + }) + } + checkMediaOptionType(e) { + return e === gu.Variant || e === gu.AltAudio || (this.logger.error(Vf, 'Should not have media option type = "%s" in RTC', Nu[e]), !1) + } + rtcEventItemId(e = !1) { + return (this.hls.isPreloading ? e ? this.hls.playingItem : this.hls.loadingItem : this.hls.currentItem).itemId + } + subscribeAndUpdateStore() { + this.hls.publicQueries$.pipe(La(([, e]) => this.mediaElementQueryListener(e)), Va(this.destroy$)).subscribe(), this.hls.itemQueue.activeItemById$.pipe(Za(t => { + if (t) { + let e = !1; + if (this.hls.userInfo ? this.hls.userInfo.internalBuild ? e = !0 : this.hls.userInfo.diagnosticsAndUsage && (e = this.config.enableRtcReporting) : e = this.config.enableRtcReporting, e) { + const i = this.hls.reportingAgent; + i ? this.rtcComponent.setReportingAgent(i) : this.logger.warn(Vf, "[RTCA] - Reporting is enabled but reportingAgent is null") + } else this.rtcComponent.setReportingAgent(null); + this.serverInfoInstance = null; + t = t.itemId; + this.rtcStore.createEntity(t), !this.hls.isFirstItem && this.hls.inGaplessMode || this.setPeriodic(t) + } + }), Va(this.destroy$)).subscribe() + } + itemTransitioned(e, t) { + this.rtcStore.updateVariantEnd(e, { + currentTime: this.hls.realCurrentTime + }), this.sendAndFinalize(e, Nf.VariantEnded), this.rtcStore.updatePeriodic(e, !0), this.sendAndFinalize(e, Nf.Periodic), this.rtcStore.updateEnded(e), this.sendAndFinalize(e, Nf.PlayEnded), this.setPeriodic(t) + } + mediaElementQueryListener(e) { + return e.gotPlaying$.pipe(Za(e => { + if (e) { + const e = this.oldRate, + t = this.newRate || 1; + 1 < Math.abs(e) && 1 < Math.abs(t) || (this.rtcStore.updateCanPlay(this.rtcEventItemId(!0), { + mediaDur: this.hls.bufferedDuration + }), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.PlayLikelyToKeepUp), this.rtcStore.updateRateChanged(this.rtcEventItemId(!0), { + rate: t, + latency: ne(this.playStart) ? Date.now() - this.playStart : 0, + mediaDur: this.hls.bufferedDuration, + currentTime: this.hls.realCurrentTime, + url: this.levelSwitchingUrl + }), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.PlayRateChanged)) + } + })) + } + registerForEvents() { + const e = Oc(this.hls, this); + an(e.event(P.KEY_REQUEST_STARTED, this.keyRequestStarted, this), e.event(P.KEY_LOADED, this.keyLoaded, this)).pipe(Va(this.destroy$)).subscribe() + } + keyRequestStarted(e) { + e.timestamp = Date.now(), this.rtcStore.updateLicenseChallengeRequested(this.rtcEventItemId(), e) + } + keyLoaded(e) { + e.timestamp = Date.now(), e.currentTime = this.hls.realCurrentTime, this.rtcStore.updateSegmentKeyLoaded(this.rtcEventItemId(), e) + } + licenseChallengeReceived(e) { + this.rtcStore.updateLicenseChallengeReceived(this.rtcEventItemId(), { + timestamp: Date.now(), + keyuri: e.keyuri + }) + } + licenseChallengeSubmitted(e) { + this.rtcStore.updateLicenseChallengeSubmitted(this.rtcEventItemId(), { + timestamp: Date.now(), + keyFormat: e.keyFormat, + keyuri: e.keyuri + }) + } + licenseChallengeCreated(e) { + this.rtcStore.updateLicenseChallengeCreated(this.rtcEventItemId(), { + timestamp: Date.now(), + cdmVersion: e.cdmVersion, + keyuri: e.keyuri + }), this.rtcStore.updateLicenseResponseRequested(this.rtcEventItemId(), { + timestamp: Date.now(), + keyuri: e.keyuri + }) + } + licenseResponseSubmitted(e) { + this.rtcStore.updateLicenseResponseReceived(this.rtcEventItemId(), { + timestamp: Date.now(), + keyuri: e.keyuri + }), this.rtcStore.updateLicenseResponseSubmitted(this.rtcEventItemId(), { + timestamp: Date.now(), + keyuri: e.keyuri + }) + } + licenseResponseProcessed(e) { + this.rtcStore.updateLicenseResponseProcessed(this.rtcEventItemId(), { + timestamp: Date.now(), + keyuri: e.keyuri, + currentTime: this.hls.realCurrentTime + }), this.sendAndFinalize(this.rtcEventItemId(), Nf.KeySessionComplete) + } + licenseChallengeError(e) { + this.rtcStore.updateLicenseChallengeError(this.rtcEventItemId(), { + timestamp: Date.now(), + keyuri: e.keyuri + }), this.sendAndFinalize(this.rtcEventItemId(), Nf.KeySessionComplete) + } + licenseResponseError(e) { + this.rtcStore.updateLicenseResponseError(this.rtcEventItemId(), { + timestamp: Date.now(), + keyuri: e.keyuri + }), this.sendAndFinalize(this.rtcEventItemId(), Nf.KeySessionComplete) + } + keyAborted(e) { + var t; + null != (null === (t = this.rtcQuery.getEntity(this.rtcEventItemId())) || void 0 === t ? void 0 : t.sessionControlRecord.activeKeySessions[e.keyuri]) ? (this.rtcStore.updateKeyAborted(this.rtcEventItemId(), { + timestamp: Date.now(), + keyuri: e.keyuri + }), this.sendAndFinalize(this.rtcEventItemId(), Nf.KeySessionComplete)) : this.logger.warn(`keyAbort called without active key session ${le(e.keyuri)}`) + } + setPeriodic(e) { + this.clearPeriodic(), this.intervalFunc = setInterval(this.handlePeriodic.bind(this, e), this.periodicInterval) + } + handlePeriodic(e) { + this.rtcStore.updatePeriodic(e, !1), this.sendAndFinalize(e, Nf.Periodic) + } + clearPeriodic() { + this.intervalFunc && clearInterval(this.intervalFunc), this.intervalFunc = null + } + sendAndFinalize(e, t) { + switch (this.accessLog.addPlayTime(e), t) { + case Nf.PlayEnded: + this.rtcComponent.sendPlayEnded(e); + break; + case Nf.Periodic: + this.rtcComponent.sendPeriodic(e); + break; + case Nf.PlayStalled: + this.accessLog.updateStallCount(e), this.rtcComponent.sendPlayStalled(e); + break; + case Nf.KeySessionComplete: + this.rtcComponent.sendKeySessionComplete(e); + break; + case Nf.PlayLikelyToKeepUp: + this.accessLog.updateCanPlay(e), this.rtcComponent.sendPlayLikelyToKeepUp(e); + break; + case Nf.PlayRateChanged: + this.rtcComponent.sendPlayRateChange(e); + break; + case Nf.PlayError: + this.accessLog.addToErrorLog(e, "mediaError"), this.rtcComponent.sendPlayError(e); + break; + case Nf.MediaEngineStalled: + this.accessLog.updateMediaEngineStallCount(e), this.rtcComponent.sendMediaEngineStalled(e); + break; + case Nf.SwitchComplete: + this.accessLog.addToAccessLog(e), this.rtcComponent.sendSwitchComplete(e); + break; + case Nf.VariantEnded: + this.rtcComponent.sendVariantEnded(e); + break; + case Nf.NwError: + this.accessLog.addToErrorLog(e, "networkError"), this.rtcComponent.sendNwError(e); + break; + default: + return void this.logger.error(Vf, `Unknown rtc event eventGroupId:${e}`) + } + this.rtcStore.finalize(e, t) + } + } + const qf = a => (e, t) => { + let i = 0, + r = 0; + for (var { + timestamp: n, + value: s + } of e) { + const e = Math.pow(Math.max(0, n - t) / 1e3, a); + i += e * s, r += e + } + return i / r + }, + Hf = { + "uniform-time-weighted": qf(0), + "linear-time-weighted": qf(1), + "quadratic-time-weighted": qf(2) + }; + class jf { + constructor(e, t = "quadratic-time-weighted", i = { + avgLatencyMs: NaN, + avgBandwidth: NaN + }) { + this.windowSize = e, this.aggregationMethod = t, this.latencyEntries = [], this.bandwidthEntries = [], this.minEntries = 1, this.cleanUpExpiredEntries = this.cleanUpExpiredEntries.bind(this), this.bwSubject = new yi(i) + } + get estimate$() { + return this.bwSubject.asObservable() + } + record(e) { + var { + trequest: t, + tfirst: i, + tload: r, + bitsDownloaded: e + } = e; + t !== r && (this.recordLatency(t, i), this.recordBandwidth(t, r, 1e3 * e / (r - t)), this.bwSubject.closed || (t = this.getEstimate(), this.bwSubject.next(t))) + } + getEstimate() { + if (this.latencyEntries.length < this.minEntries) return { + avgLatencyMs: NaN, + avgBandwidth: NaN + }; + const e = performance.now() - this.windowSize, + t = Hf[this.aggregationMethod], + i = this.latencyEntries.map(({ + start: e, + end: t + }) => ({ + timestamp: t, + value: t - e, + duration: 1 + })); + this.bandwidthEntries = function(r) { + function n(t, i) { + if (t.length) { + for (let e = 0; e < t.length; e++) + if (t[e].start > i.start || t[e].start === i.start && t[e].end > i.end) { + t.splice(e, 0, i); + break + } + } else t.push(i) + } + const s = [...r].sort((e, t) => e.start !== t.start ? e.start - t.start : e.end - t.end), + t = []; + for (; s.length;) { + const r = s[0]; + let e; + if (s.shift(), t.length && (e = t[t.length - 1]), 0 === t.length || e.end <= r.start) t.push(r); + else if (r.start === e.start) r.end === e.end ? e.bitsPerSec += r.bitsPerSec : r.end < e.end || (e.bitsPerSec += r.bitsPerSec, r.start = e.end, n(s, r)); + else { + var a = e.end, + o = e.bitsPerSec; + e.end = r.start; + var i = { + start: r.start, + end: Math.min(a, r.end), + bitsPerSec: r.bitsPerSec + o + }; + if (t.push(i), a !== r.end) { + let e = 0, + t = 0, + i = 0; + i = a < r.end ? (e = a, t = r.end, r.bitsPerSec) : (e = r.end, t = a, o), n(s, { + start: e, + end: t, + bitsPerSec: i + }) + } + } + } + return t + }(this.bandwidthEntries); + var r = this.bandwidthEntries.map(({ + end: e, + bitsPerSec: t + }) => ({ + timestamp: e, + duration: 1, + value: t + })); + return { + avgLatencyMs: t(i, e), + avgBandwidth: t(r, e) + } + } + getLatest() { + if (0 === this.latencyEntries.length) return { + avgLatencyMs: NaN, + avgBandwidth: NaN + }; + var e = this.latencyEntries[this.latencyEntries.length - 1], + t = this.bandwidthEntries[this.bandwidthEntries.length - 1]; + return { + avgLatencyMs: e.end - e.start, + avgBandwidth: t.bitsPerSec + } + } + recordLatency(e, t) { + this.latencyEntries.push({ + start: e, + end: t + }), this.updateCleanupTimeout(t) + } + recordBandwidth(e, t, i) { + this.bandwidthEntries.push({ + start: e, + end: t, + bitsPerSec: i + }), this.updateCleanupTimeout(t) + } + setCleanupTimeout(e) { + this.cleanupTimeout = setTimeout(this.cleanUpExpiredEntries, Math.max(e - performance.now(), 0)), this.cleanupTimestamp = e + } + clearCleanupTimeout() { + void 0 !== this.cleanupTimeout && (clearTimeout(this.cleanupTimeout), this.cleanupTimeout = void 0), this.cleanupTimestamp = void 0 + } + updateCleanupTimeout(e) { + e += this.windowSize; + (!this.cleanupTimestamp || e < this.cleanupTimestamp) && (this.clearCleanupTimeout(), this.setCleanupTimeout(e)) + } + cleanUpExpiredEntries() { + this.clearCleanupTimeout(); + const t = performance.now() - this.windowSize; + if (this.latencyEntries = this.latencyEntries.filter(e => e.end >= t), this.bandwidthEntries = this.bandwidthEntries.filter(e => e.end >= t), this.bwSubject.closed || this.bwSubject.next(this.getEstimate()), 0 < this.latencyEntries.length || 0 < this.bandwidthEntries.length) { + const t = Math.min(...this.latencyEntries.map(e => e.end), ...this.bandwidthEntries.map(e => e.end)); + this.updateCleanupTimeout(t) + } + } + destroy() { + this.clearCleanupTimeout() + } + } + const Qf = { + setCombinedEstimate: function(t, i, r) { + const n = Qe(); + if (void 0 !== t.storage.set) { + var s = t.bandwidthHistoryStorageKey, + a = { + avgLatencyMs: i.avgLatencyMs, + avgBandwidth: i.avgBandwidth + }, + a = Object.assign({}, a, { + expires: Date.now() + t.bandwidthHistoryTTL + }); + try { + t.storage.set(s, JSON.stringify(a)) + } catch (t) { + n.warn(`Error stringifying! Not persisting bandwidth estimates: ${t.message}`) + } + i = { + maxDuration: i.maxDurationSec, + avgFragParseTimeMs: i.avgParseTimeMs, + avgFragBufferCreationDelayMs: i.avgBufferCreateMs, + avgPlaylistLoadTimeMs: i.avgPlaylistLoadTimeMs, + avgPlaylistParseTimeMs: i.avgPlaylistParseTimeMs, + avgInitFragAppendMs: i.avgInitFragAppendMs, + avgDataFragAppendMs: i.avgDataFragAppendMs + }; + let e = t.storageKeyPrefix; + r && (e += r); + try { + t.storage.set(e, JSON.stringify(i)) + } catch (t) { + n.warn(`Error stringifying! Not persisting bandwidth estimates: ${t.message}`) + } + } else n.warn("storage.set is not supported! Not persisting bandwidth estimates") + }, + getCombinedEstimate: function(t, e) { + const i = Qe(); + let r = {}; + if (void 0 === t.storage.get) return i.warn("storage.get is not supported! unable to retreive bandwidth estimates"), this.convertStorageJsonToCombinedEstimate(r); + try { + let e = JSON.parse(t.storage.get(t.bandwidthHistoryStorageKey)); + e = null != e && e.expires && e.expires < Date.now() ? null : { + avgLatencyMs: null == e ? void 0 : e.avgLatencyMs, + avgBandwidth: null == e ? void 0 : e.avgBandwidth + }, r = Object.assign(Object.assign({}, r), e) + } catch (t) { + i.warn(`Unable to get persisted bandwidth history: ${t.message}`) + } + let n = t.storageKeyPrefix; + e && (n += e); + try { + const e = JSON.parse(t.storage.get(n)); + r = Object.assign(Object.assign({}, r), e) + } catch (t) { + i.warn(`Unable to get persisted bandwidth history: ${t.message}`) + } + return this.convertStorageJsonToCombinedEstimate(r) + }, + convertStorageJsonToCombinedEstimate: function(e) { + return { + avgLatencyMs: (null == e ? void 0 : e.avgLatencyMs) || NaN, + avgBandwidth: (null == e ? void 0 : e.avgBandwidth) || NaN, + maxDurationSec: (null == e ? void 0 : e.maxDuration) || NaN, + avgParseTimeMs: (null == e ? void 0 : e.avgFragParseTimeMs) || NaN, + avgBufferCreateMs: (null == e ? void 0 : e.avgFragBufferCreationDelayMs) || NaN, + avgPlaylistLoadTimeMs: (null == e ? void 0 : e.avgPlaylistLoadTimeMs) || NaN, + avgPlaylistParseTimeMs: (null == e ? void 0 : e.avgPlaylistParseTimeMs) || NaN, + avgInitFragAppendMs: (null == e ? void 0 : e.avgInitFragAppendMs) || NaN, + avgDataFragAppendMs: (null == e ? void 0 : e.avgDataFragAppendMs) || NaN + } + }, + getBandwidthEstimate: function(e, t) { + const i = this.getCombinedEstimate(e, t), + r = { + avgLatencyMs: null == i ? void 0 : i.avgLatencyMs, + avgBandwidth: null == i ? void 0 : i.avgBandwidth + }; + return ne(r.avgLatencyMs) || (r.avgLatencyMs = NaN), ne(r.avgBandwidth) || (r.avgBandwidth = NaN), r + }, + getPlaylistEstimate: function(e, t) { + const i = this.getCombinedEstimate(e, t), + r = { + avgPlaylistLoadTimeMs: null == i ? void 0 : i.avgPlaylistLoadTimeMs, + avgPlaylistParseTimeMs: null == i ? void 0 : i.avgPlaylistParseTimeMs + }; + return ne(r.avgPlaylistLoadTimeMs) || (r.avgPlaylistLoadTimeMs = NaN), ne(r.avgPlaylistParseTimeMs) || (r.avgPlaylistParseTimeMs = NaN), r + }, + getFragEstimate: function(e, t) { + const i = this.getCombinedEstimate(e, t), + r = { + maxDurationSec: null == i ? void 0 : i.maxDurationSec, + avgParseTimeMs: null == i ? void 0 : i.avgParseTimeMs + }; + return ne(r.maxDurationSec) || (r.maxDurationSec = NaN), ne(r.avgParseTimeMs) || (r.avgParseTimeMs = NaN), r + }, + getBufferEstimate: function(e, t) { + const i = this.getCombinedEstimate(e, t), + r = { + avgBufferCreateMs: null == i ? void 0 : i.avgBufferCreateMs, + avgInitFragAppendMs: null == i ? void 0 : i.avgInitFragAppendMs, + avgDataFragAppendMs: null == i ? void 0 : i.avgDataFragAppendMs + }; + return ne(r.avgBufferCreateMs) || (r.avgBufferCreateMs = NaN), ne(r.avgInitFragAppendMs) || (r.avgInitFragAppendMs = NaN), ne(r.avgDataFragAppendMs) || (r.avgDataFragAppendMs = NaN), r + } + }; + var Wf = Qf; + class Gf { + constructor(e = 0) { + this._minSamples = e, this._sum = 0, this._max = Number.NEGATIVE_INFINITY, this._numSamples = 0 + } + get avg() { + return this._numSamples < this._minSamples ? NaN : this._sum / this._numSamples + } + get max() { + return 0 < this.count ? this._max : NaN + } + get count() { + return this._numSamples + } + reset() { + this._sum = 0, this._numSamples = 0, this._max = Number.NEGATIVE_INFINITY + } + add(e) { + this._sum += e, this._max = Math.max(this._max, e), ++this._numSamples + } + } + class zf extends kl { + constructor(e, t) { + super(e), this.id = t + } + getBandwidthEstimate(e, t) { + var i; + const r = Object.assign({}, null === (i = this.statsEntity) || void 0 === i ? void 0 : i.bandwidthEstimate); + if (ne(r.avgBandwidth) && ne(r.avgLatencyMs)) return r; + if (e) { + const i = Qf.getBandwidthEstimate(e, t); + ne(r.avgBandwidth) || (r.avgBandwidth = i.avgBandwidth), ne(r.avgLatencyMs) || (r.avgLatencyMs = i.avgLatencyMs) + } + return r + } + getPlaylistEstimate(e, t) { + var i; + const r = Object.assign({}, null === (i = this.statsEntity) || void 0 === i ? void 0 : i.playlistEstimate), + n = e => ne(e.avgPlaylistLoadTimeMs) && ne(e.avgPlaylistParseTimeMs); + if (n(r)) return r; + if (e) { + const i = Qf.getPlaylistEstimate(e, t); + if (ne(r.avgPlaylistLoadTimeMs) || (r.avgPlaylistLoadTimeMs = i.avgPlaylistLoadTimeMs), ne(r.avgPlaylistParseTimeMs) || (r.avgPlaylistParseTimeMs = i.avgPlaylistParseTimeMs), n(r)) return r; + ne(r.avgPlaylistLoadTimeMs) || (r.avgPlaylistLoadTimeMs = e.statDefaults.playlistLoadTimeMs), ne(r.avgPlaylistParseTimeMs) || (r.avgPlaylistParseTimeMs = e.statDefaults.playlistParseTimeMs) + } + return r + } + getBufferEstimate(e, t) { + var i; + const r = Object.assign({}, null === (i = this.statsEntity) || void 0 === i ? void 0 : i.bufferEstimate), + n = e => ne(e.avgBufferCreateMs) && ne(e.avgDataFragAppendMs) && ne(e.avgInitFragAppendMs); + if (n(r)) return r; + if (e) { + const i = Qf.getBufferEstimate(e, t); + if (ne(r.avgBufferCreateMs) || (r.avgBufferCreateMs = i.avgBufferCreateMs), ne(r.avgDataFragAppendMs) || (r.avgDataFragAppendMs = i.avgDataFragAppendMs), ne(r.avgInitFragAppendMs) || (r.avgInitFragAppendMs = i.avgInitFragAppendMs), n(r)) return r; + ne(r.avgBufferCreateMs) || (r.avgBufferCreateMs = e.statDefaults.fragBufferCreationDelayMs), ne(r.avgDataFragAppendMs) || (r.avgDataFragAppendMs = e.statDefaults.dataFragAppendMs), ne(r.avgInitFragAppendMs) || (r.avgInitFragAppendMs = e.statDefaults.initFragAppendMs) + } + return r + } + getFragEstimate(e, t) { + var i; + const r = Object.assign({}, null === (i = this.statsEntity) || void 0 === i ? void 0 : i.fragEstimate), + n = e => ne(e.maxDurationSec) && ne(e.avgParseTimeMs); + if (n(r)) return r; + if (e) { + const i = Qf.getFragEstimate(e, t); + if (ne(r.maxDurationSec) || (r.maxDurationSec = i.maxDurationSec), ne(r.avgParseTimeMs) || (r.avgParseTimeMs = i.avgParseTimeMs), n(r)) return r; + ne(r.maxDurationSec) || (r.maxDurationSec = e.defaultTargetDuration), ne(r.avgParseTimeMs) || (r.avgParseTimeMs = e.statDefaults.fragParseTimeMs) + } + return r + } + getCombinedEstimate() { + return Object.assign(Object.assign(Object.assign(Object.assign({}, this.getFragEstimate()), this.getPlaylistEstimate()), this.getBufferEstimate()), this.getBandwidthEstimate()) + } + get statsEntity() { + return this.getEntity(this.id) + } + get bandwidthSample() { + var e; + return null === (e = this.statsEntity) || void 0 === e ? void 0 : e.bandwidthSample + } + get bandwidthStatus() { + var e; + return null === (e = this.statsEntity) || void 0 === e ? void 0 : e.bandwidthStatus + } + get fragSample() { + var e; + return null === (e = this.statsEntity) || void 0 === e ? void 0 : e.fragSample + } + get bandwidthEstimate$() { + return this.selectEntity(this.id, "bandwidthEstimate") + } + get fragEstimate$() { + return this.selectEntity(this.id, "fragEstimate") + } + get playlistEstimate$() { + return this.selectEntity(this.id, "playlistEstimate") + } + get bufferEstimate$() { + return this.selectEntity(this.id, "bufferEstimate") + } + get bandwidthSample$() { + return this.selectEntity(this.id, ({ + bandwidthSample: e + }) => e).pipe(Kp()) + } + get fragSample$() { + return this.selectEntity(this.id, ({ + fragSample: e + }) => e).pipe(Kp()) + } + get playlistSample$() { + return this.selectEntity(this.id, ({ + playlistSample: e + }) => e).pipe(Kp()) + } + get bufferMetric$() { + return this.selectEntity(this.id, ({ + bufferMetric: e + }) => e).pipe(Kp()) + } + } + class Xf { + constructor(e) { + this.statsStore = e + } + getQuery() { + return new kl(this.statsStore) + } + getQueryForItem(e) { + return new zf(this.statsStore, e) + } + remove(e) { + this.statsStore.remove(e) + } + removeAll() { + this.statsStore.remove() + } + setBandwidthSample(e) { + this.statsStore.bandwidthSample = e + } + setFragSample(e) { + this.statsStore.fragSample = e + } + setPlaylistSample(e) { + this.statsStore.playlistSample = e + } + setBufferMetric(e) { + this.statsStore.bufferMetric = e + } + setBandwidthEstimate(e) { + this.statsStore.bandwidthEstimate = e + } + setFragEstimate(e) { + this.statsStore.fragEstimate = e + } + setPlaylistEstimate(e) { + this.statsStore.playlistEstimate = e + } + setBufferEstimate(e) { + this.statsStore.bufferEstimate = e + } + } + const Yf = new class extends fl { + constructor() { + super({}, { + name: "stats-store", + producerFn: su + }) + } + set statsEntity(e) { + Do("statsStore.set.stats"), al(() => { + this.add(e), this.setActive(e.id) + }) + } + set playlistSample(t) { + Do(`stats.set.playlistSample: ${t}`), this.updateActive(e => { + e.playlistSample = t + }) + } + set bandwidthSample(t) { + Do(`stats.set.bandwidthSample: ${t}`), this.updateActive(e => { + e.bandwidthSample = t, e.bandwidthStatus.bandwidthSampleCount += 1, e.bandwidthStatus.instantBw = 8e3 * t.loaded / (t.tload - t.trequest) + }) + } + set fragSample(t) { + Do(`stats.set.fragSample: ${t}`), this.updateActive(e => { + e.fragSample = t + }) + } + set bufferMetric(t) { + Do(`stats.set.bufferMetric: ${t}`), this.updateActive(e => { + e.bufferMetric = t + }) + } + set bandwidthEstimate(t) { + Do(`stats.set.bandwidthEstimate: ${t}`), this.updateActive(e => { + e.bandwidthEstimate = t + }) + } + set fragEstimate(t) { + Do(`stats.set.fragEstimate: ${t}`), this.updateActive(e => { + e.fragEstimate = t + }) + } + set playlistEstimate(t) { + Do(`stats.set.playlistEstimate: ${t}`), this.updateActive(e => { + e.playlistEstimate = t + }) + } + set bufferEstimate(t) { + Do(`stats.set.bufferEstimate: ${t}`), this.updateActive(e => { + e.bufferEstimate = t + }) + } + }; + let Jf = null; + const Zf = e => new zf(Yf, e); + + function em(e, t) { + if (e === t) return !0; + if (!e || !t) return !1; + let i = Object.keys(e).length === Object.keys(t).length; + for (const r of Object.keys(e)) i = i && (isNaN(e[r]) && isNaN(t[r]) || e[r] === t[r]); + return i + } + + function tm(f, m, g) { + return new $t(e => { + (e => { + const t = Zf(e); + t.hasEntity(e) ? $i(t) : (i = Yf, e = e, Do("stats.loading"), i.setLoading(!0), i.statsEntity = { + id: e, + bandwidthEstimate: { + avgLatencyMs: NaN, + avgBandwidth: NaN + }, + bandwidthStatus: { + bandwidthSampleCount: 0, + instantBw: NaN + }, + fragEstimate: { + maxDurationSec: NaN, + avgParseTimeMs: NaN + }, + playlistEstimate: { + avgPlaylistLoadTimeMs: NaN, + avgPlaylistParseTimeMs: NaN + }, + bufferEstimate: { + avgBufferCreateMs: NaN, + avgInitFragAppendMs: NaN, + avgDataFragAppendMs: NaN + } + }, i.setLoading(!1), Do("stats.loaded")); + var i + })(g.itemId); + const t = Zf(g.itemId), + { + fragSample$: i, + playlistSample$: r, + bandwidthSample$: n, + bufferMetric$: s + } = t; + return an(r.pipe(ji(tr), (h = f, p = m, e => e.pipe(tc("statsPlaylistProcessingEpic.in"), sa((e, t) => (e.playlistLoadTimeMs.add(t.playlistLoadTimeMs), e.playlistParseTimeMs.add(t.playlistParseTimeMs), e), { + playlistLoadTimeMs: new Gf(h.minPlaylistCount), + playlistParseTimeMs: new Gf(h.minPlaylistCount) + }), hr(e => ({ + avgPlaylistLoadTimeMs: e.playlistLoadTimeMs.avg, + avgPlaylistParseTimeMs: e.playlistParseTimeMs.avg + })), Is(em), Za(e => { + p.setPlaylistEstimate(e) + })))), n.pipe(ji(tr), (u = f, c = m, n => new $t(e => { + let t = new jf(u.bandwidthHistoryWindowSize, u.bandwidthHistoryAggregationMethod, { + avgLatencyMs: NaN, + avgBandwidth: NaN + }); + const i = t.estimate$, + r = an(n.pipe(ln(e => e.complete), Za(e => {}), hr(e => ({ + trequest: e.trequest, + tfirst: e.tfirst, + tload: e.tload, + bitsDownloaded: 8 * e.loaded + })), tc("statsBandwidthProcessingEpic.in"), La(e => (t.record(e), Ii))), i.pipe(Is(), tc("statsBandwidthProcessingEpic.change"), Za(e => { + c && c.setBandwidthEstimate(e) + }))).subscribe(e); + return () => { + r.unsubscribe(), t.destroy(), t = void 0 + } + }))), i.pipe(ji(tr), (l = f, d = m, e => e.pipe(tc("statsFragProcessingEpic.in"), sa((e, t) => (e.durationSec.add(t.durationSec), e.fragParseMs.add(t.parseTimeMs), e), { + durationSec: new Gf, + fragParseMs: new Gf(l.minFragmentCount) + }), hr(e => ({ + maxDurationSec: e.durationSec.max, + avgParseTimeMs: e.fragParseMs.avg + })), Is(em), Za(e => d.setFragEstimate(e))))), s.pipe(ji(tr), (a = f, o = m, e => e.pipe(tc("statsBufferMetricProcessingEpic.in"), sa((e, t) => (ne(t.bufferCreationStart) && ne(t.bufferCreationEnd) && e.bufferCreateMs.add(t.bufferCreationEnd - t.bufferCreationStart), ne(t.startInitAppend) && ne(t.endInitAppend) && e.initFragAppendMs.add(t.endInitAppend - t.startInitAppend), ne(t.startDataAppend) && ne(t.endDataAppend) && e.dataFragAppendMs.add(t.endDataAppend - t.startDataAppend), e), { + bufferCreateMs: new Gf, + initFragAppendMs: new Gf, + dataFragAppendMs: new Gf(a.minFragmentCount) + }), hr(e => ({ + avgBufferCreateMs: e.bufferCreateMs.avg, + avgInitFragAppendMs: e.initFragAppendMs.avg, + avgDataFragAppendMs: e.dataFragAppendMs.avg + })), Is(em), Za(e => { + o.setBufferEstimate(e) + }))))).pipe($a(Ii)).subscribe(e), () => { + Wf.setCombinedEstimate(f, Object.assign(Object.assign(Object.assign(Object.assign({}, t.getFragEstimate()), t.getPlaylistEstimate()), t.getBufferEstimate()), t.getBandwidthEstimate()), g.serviceName), m.remove(g.itemId) + }; + var a, o, l, d, u, c, h, p + }) + } + const im = { + isWebkitMediaElement: e => "webkitDroppedFrameCount" in e, + isHtmlVideoElement: e => "getVideoPlaybackQuality" in e, + timeRangeToArray(t) { + const i = []; + for (let e = 0; e < t.length; e++) i.push([t.start(e), t.end(e)]); + return i + } + }; + class rm { + constructor(e, t) { + this.hls = e, this.sessionID = t, this.rtcQuery = null, this.accessLogData = this.createAccessLogEntry(), this.accesslog = [], this.errorlog = [] + } + destroy() { + this.rtcQuery = null, this.accesslog = [], this.errorlog = [], this.accessLogData = void 0, this.accessLogReporter = void 0 + } + setRTCQuery(e) { + this.rtcQuery = e + } + setupReporter(e) { + this.accessLogReporter = { + SessionID: this.sessionID, + ClientName: null == e ? void 0 : e.clientName, + ServiceName: null == e ? void 0 : e.serviceName + } + } + addPlayTime(e) { + var t, e = null === (t = this.rtcQuery) || void 0 === t ? void 0 : t.getEntity(e); + !e || "RTC_STATE_PLAY" === (e = e.sessionControlRecord).state && (this.accessLogData.PlayTimeWC = (this.accessLogData.PlayTimeWC || 0) + e.eventStartTime) + } + updatePlaybackInfo(e, t) { + this.accessLogData.ViFrDr = this.rtcQuery.getEntity(e).sessionControlRecord.droppedVideoFrames || 0 + } + updateStallCount(e) { + "RTC_STATE_PLAY" === this.rtcQuery.getEntity(e).sessionControlRecord.state && this.accessLogData.StallCount++ + } + updateMediaEngineStallCount(e) { + "RTC_STATE_PLAY" === this.rtcQuery.getEntity(e).sessionControlRecord.state && this.accessLogData.MediaEngineStallCount++ + } + updateCanPlay(e) { + this.accessLogData.StartupTime = this.rtcQuery.getEntity(e).sessionControlRecord.eventStartTime + } + updateFragLoaded(e, t, i) { + var r; + i.fragType === gu.Variant ? (this.accessLogData.NetBytes += i.bytes, this.accessLogData.ADT += i.adt, r = this.aggregateFragObserverdBitrate(i, ++this.accessLogData.fragmentCnt, this.accessLogData.NetBytes, this.accessLogData.ADT), this.accessLogData.OBRLast = r.obrLast, this.accessLogData.OBRMean = r.obrMean, this.aggregateFragMinMaxBitrate(this.accessLogData, r.obr), this.hls.realCurrentTime > i.startPTS && !t && this.accessLogData.overdue++, this.hasGap(i.startPTS, i.endPTS, this.accessLogData.lastStartPTS, this.accessLogData.lastEndPTS) && this.addToAccessLog(e), this.accessLogData.startPTS || (this.accessLogData.startPTS = i.startPTS), this.accessLogData.lastStartPTS = i.startPTS, this.accessLogData.lastEndPTS = i.endPTS, this.accessLogData.videoBytes += i.bytes, this.accessLogData.videoDuration += i.duration) : i.fragType === gu.AltAudio && (this.accessLogData.audioBytes += i.bytes, this.accessLogData.audioDuration += i.duration) + } + addToAccessLog(e) { + var t = this.getVariantInfo(e), + i = this.rtcQuery.getEntity(e).sessionControlRecord.curLevelUrl, + r = this.rtcQuery.getEntity(e).playEndedRecord.PlayType; + if (i && "" !== i) { + r = this.translateToAccessLogItem(e, i, t, r); + if (r) { + const n = this.accesslog.length - 20; + 0 < n && this.accesslog.splice(0, n), this.accesslog.push(r) + } + this.accessLogData = this.createAccessLogEntry(); + e = this.rtcQuery.getEntity(e).switchCompleteRecord.MediaDur; + this.accessLogData.lastMediaDur = e || this.hls.bufferedDuration + } + } + addToErrorLog(e, t) { + var i = null === (r = this.rtcQuery) || void 0 === r ? void 0 : r.getEntity(e); + if (i) { + var r = Number(("mediaError" === t ? i.playErrorRecord : i.nwErrorRecord).ErrCode), + i = i.sessionControlRecord.curLevelUrl, + r = this.translateToErrorLogItem(e, i, { + domain: t, + code: r + }); + if (r) { + const e = this.errorlog.length - 20; + 0 < e && this.errorlog.splice(0, e), this.errorlog.push(r) + } + } + } + getAccessLog(e) { + var t; + const i = this.accesslog.slice(0), + r = null === (t = this.rtcQuery) || void 0 === t ? void 0 : t.getEntity(e); + if (i && r) { + const t = r.sessionControlRecord.curLevelUrl; + if (t && "" !== t) { + const r = this.getVariantInfo(e), + n = this.translateToAccessLogItem(e, t, r, this.rtcQuery.getEntity(e).playEndedRecord.PlayType); + n && (n["c-provisional-entry"] = !0, i.push(n)) + } + } + return i + } + get errorLog() { + return this.errorlog + } + createAccessLogEntry() { + return { + fragmentCnt: 0, + overdue: 0, + startPTS: 0, + obrMax: 0, + obrMin: 0, + audioBytes: 0, + audioDuration: 0, + videoBytes: 0, + videoDuration: 0, + svrAddrChanged: 0, + svrAddr: "", + PlayTimeWC: 0, + ViFrDr: 0, + StallCount: 0, + MediaEngineStallCount: 0, + ADT: 0, + NetBytes: 0, + StartupTime: 0, + OBRMean: 0, + OBRLast: 0 + } + } + convertStringObjectToPrimitive(e) { + return e ? "object" == typeof e ? e.toString() : e : "" + } + updateSvrAddrStats(t) { + const i = bu.parseURL(t); + if (i && i.netLoc) { + const t = i.netLoc.indexOf(":"); + let e = 0 <= t ? i.netLoc.slice(0, t) : i.netLoc; + e.startsWith("//") && (e = e.slice(2)), this.accessLogData.svrAddr ? e !== this.accessLogData.svrAddr && this.accessLogData.svrAddrChanged++ : this.accessLogData.svrAddrChanged = 0, this.accessLogData.svrAddr = e + } + } + translateToAccessLogItem(e, t, i, r) { + t = this.convertStringObjectToPrimitive(t); + this.updateSvrAddrStats(t); + let n = this.rtcQuery.getEntity(e).switchCompleteRecord.MediaDur; + n = n || this.hls.bufferedDuration, n = n || 0; + const s = { + uri: t, + "s-ip": this.accessLogData.svrAddr, + "s-ip-changes": this.accessLogData.svrAddrChanged, + "sc-wwan-count": -1, + "c-transfer-duration": this.accessLogData.ADT, + bytes: this.accessLogData.NetBytes, + "c-total-media-requests": this.accessLogData.fragmentCnt, + "cs-guid": this.accessLogReporter.SessionID, + "c-start-time": this.accessLogData.startPTS, + "c-startup-time": this.accessLogData.StartupTime, + "c-duration-watched": this.accessLogData.PlayTimeWC / 1e3, + "c-frames-dropped": this.accessLogData.ViFrDr, + "c-stalls": this.accessLogData.StallCount + this.accessLogData.MediaEngineStallCount, + "c-duration-downloaded": this.accessLogData.lastMediaDur ? n - this.accessLogData.lastMediaDur : n, + "c-overdue": this.accessLogData.overdue, + "c-avg-video-bitrate": 8 * this.accessLogData.videoBytes / (this.accessLogData.videoDuration || 1), + "c-observed-max-bitrate": this.accessLogData.obrMax, + "c-observed-min-bitrate": this.accessLogData.obrMin, + "sc-indicated-bitrate": i.bandwidth || 0, + "sc-indicated-avg-bitrate": i.avgBandwidth || 0, + "c-observed-bitrate": this.accessLogData.OBRMean, + "c-switch-bitrate": this.accessLogData.OBRLast, + "c-provisional-entry": !1 + }; + return s["s-playback-type"] = r, this.accessLogData.audioBytes && (s["c-avg-audio-bitrate"] = 8 * this.accessLogData.audioBytes / (this.accessLogData.audioDuration || 1)), s + } + translateToErrorLogItem(e, t, i) { + t = this.convertStringObjectToPrimitive(t); + return this.updateSvrAddrStats(t), { + date: new Date, + "cs-guid": this.accessLogReporter.SessionID + "-" + e, + uri: t, + "s-ip": this.accessLogData.svrAddr, + status: "" + i.code, + domain: i.domain + } + } + hasGap(e, t, i, r) { + return void 0 !== e && void 0 !== i && (1 < e - r || 1 < i - t) + } + aggregateFragObserverdBitrate(e, t, i, r) { + r = 8 * i / (r / 1e3); + return { + obr: r, + obrLast: 8 * e.bytes / (e.adt / 1e3), + obrMean: r / t + } + } + aggregateFragMinMaxBitrate(e, t) { + (!e.obrMax || t > e.obrMax) && (e.obrMax = t), (!e.obrMin || t < e.obrMin) && (e.obrMin = t) + } + getVariantInfo(e) { + var t = this.rtcQuery.getEntity(e).sessionControlRecord.curLevelUrl, + e = null === (e = this.rtcQuery.getEntity(e).sessionControlRecord.manifestData) || void 0 === e ? void 0 : e.variantList; + return t && e && e[t] ? e[t] : {} + } + } + const nm = (r, e, t, i, n, s) => { + var a, { + absoluteUrl: o, + byteRangeOffset: l, + keyTagInfo: d, + iframe: u, + isInitSegment: c + } = r, + h = o, + p = d["method"], + { + start: o, + end: d + } = l, + t = Lc({ + url: h + }, t); + let f, m = o, + g = d, + y = !1, + v = ne(o) || ne(d) ? l : void 0; + if ("AES-128" === p && d && (u || c)) { + const r = d - o; + r % 16 && (g = d + (16 - r % 16)), 0 !== o && (y = !0, m = o - 16), v = { + start: m, + end: g + } + } + return n && ne(r.mediaSeqNum) && r.mediaOptionType === gu.Variant && (f = [], null === (n = t.reportHTTPResponseHeaders) || void 0 === n || n.forEach(function(e) { + Ou.includes(e) ? f.push(e) : Qe().warn({ + name: "load-media-fragment" + }, `${e} is not in approved privacy list. Actions required.`) + }), 0 === f.length && (f = void 0)), Rc({ + url: h, + byteRangeOffset: v, + checkContentLength: !0, + extendMaxTTFB: s, + collectServerInstanceInfo: f, + onProgress: i, + xhrSetup: e.xhrSetup + }, t).pipe(hr(([e, t, i]) => { + if (y) { + const t = e; + r.keyTagInfo.iv = new Uint8Array(t.slice(0, 16)), e = t.slice(16) + } + return [r, e, t, i] + }), (a = r, e => e.pipe(Vn(e => { + if (e instanceof pc) throw new hc(!1, "Timeout", 0, $.FragmentTimeoutError, !0, a, e.stats); + if (e instanceof oc) throw new hc(!1, e.message, e.code, { + code: e.code, + text: "Fragment Network Error" + }, !1, a); + throw e + })))) + }, + sm = { + clearkey: th, + fairplaystreaming: Sc, + playready: Fc, + widevine: Bc + }, + am = { + getKeySystemFormat(e) { + e = sm[e]; + return e ? e.keyFormatString : "" + }, + getKeySystemSecurityLevel(e) { + e = sm[e]; + return e ? e.securityLevels : null + } + }, + om = { + NONE: "", + "AES-128": "", + "ISO-23001-7": "", + "SAMPLE-AES": "", + "SAMPLE-AES-CTR": "" + }, + lm = { + NONE: 0, + "TYPE-0": 1, + "TYPE-1": 2, + "TYPE-2": 3 + }; + + function dm(e) { + return e in lm + } + + function um(e) { + return null == e ? 4 : lm[e] + } + const cm = ["SDR", "PQ", "HLG"], + hm = { + afr: "af", + aka: "ak", + amh: "am", + ara: "ar", + arg: "an", + asm: "as", + ava: "av", + ave: "ae", + aym: "ay", + aze: "az", + bam: "bm", + bel: "be", + ben: "bn", + bih: "bh", + bod: "bo", + bos: "bs", + bre: "br", + bul: "bg", + cat: "ca", + ces: "cs", + cha: "ch", + che: "ce", + chu: "cu", + chv: "cv", + cor: "kw", + cos: "co", + cre: "cr", + cym: "cy", + dan: "da", + deu: "de", + div: "dv", + dzo: "dz", + ell: "el", + eng: "en", + epo: "eo", + est: "et", + eus: "eu", + ewe: "ee", + fao: "fo", + fas: "fa", + fin: "fi", + fra: "fr", + fry: "fy", + ful: "ff", + gla: "gd", + gle: "ga", + glg: "gl", + glv: "gv", + grn: "gn", + guj: "gu", + hat: "ht", + heb: "he", + her: "hz", + hin: "hi", + hmo: "ho", + hrv: "hr", + hun: "hu", + hye: "hy", + ibo: "ig", + ido: "io", + iii: "ii", + iku: "iu", + ile: "ie", + ina: "ia", + ind: "id", + isl: "is", + ita: "it", + jav: "jv", + jpn: "ja", + kal: "kl", + kan: "kn", + kas: "ks", + kat: "ka", + kau: "kr", + kaz: "kk", + khm: "km", + kik: "ki", + kin: "rw", + kir: "ky", + kom: "kv", + kon: "kg", + kor: "ko", + kua: "kj", + kur: "ku", + lao: "lo", + lat: "la", + lav: "lv", + lim: "li", + lit: "lt", + ltz: "lb", + lub: "lu", + lug: "lg", + mah: "mh", + mal: "ml", + mar: "mr", + mkd: "mk", + mlg: "mg", + mlt: "mt", + mol: "mo", + mon: "mn", + mri: "mi", + msa: "ms", + mya: "my", + nav: "nv", + nbl: "nr", + nde: "nd", + ndo: "ng", + nep: "ne", + nld: "nl", + nno: "nn", + nob: "nb", + nya: "ny", + oci: "oc", + oji: "oj", + ori: "or", + orm: "om", + oss: "os", + pan: "pa", + pli: "pi", + pol: "pl", + por: "pt", + pus: "ps", + que: "qu", + roh: "rm", + ron: "ro", + run: "rn", + rus: "ru", + san: "sa", + sin: "si", + slk: "sk", + slv: "sl", + sme: "se", + snd: "sd", + som: "so", + spa: "es", + sqi: "sq", + srd: "sc", + srp: "sr", + sun: "su", + swa: "sw", + swe: "sv", + tah: "ty", + tam: "ta", + tat: "tt", + tel: "te", + tgk: "tg", + tgl: "tl", + tha: "th", + tir: "ti", + ton: "to", + tuk: "tk", + tur: "tr", + uig: "ug", + ukr: "uk", + urd: "ur", + uzb: "uz", + ven: "ve", + vie: "vi", + wln: "wa", + yid: "yi", + zha: "za", + zho: "zh" + }, + pm = { + isLanguageCode: e => e in hm, + shortenLanguageCode(e) { + let t; + var i, r; + return e && (r = 0 <= (i = e.indexOf("-")) ? e.slice(0, i) : e, pm.isLanguageCode(r) && (t = hm[r]), t = t || r, 0 < i && (t += "-" + e.slice(i + 1))), t + } + }, + fm = { + getRichestVideoCodec(e) { + if (e && e.length) { + e = e.sort((e, t) => qp(t) - qp(e)); + return e && e.length ? e[0] : void 0 + } + }, + getRichestAudioCodec(e) { + if (e && e.length) { + e = e.sort((e, t) => jp(t) - jp(e)); + return e && e.length ? e[0] : void 0 + } + }, + getRichestChannelLayoutForGroupId(t, i) { + if (t && i && i.length) { + let e; + const r = i.filter(e => e.groupId === t); + if (r && r.length) { + const t = r.sort((e, t) => be.getChannelCount(t.channels) - be.getChannelCount(e.channels)); + t && t.length && (e = t[0].channels) + } + return e + } + } + }; + + function mm(e) { + return new R(L, "steeringManifestParsingError", !1, e, $.FormatError) + } + class gm { + constructor(e) { + this._url = null, this._programDateTime = null, this._byteRange = null, this.relurl = null, this.baseurl = null, this.isInitSegment = !1, this.mediaSeqNum = NaN, this.cc = NaN, this.iframe = !1, this.bitrate = NaN, this.start = NaN, this.duration = NaN, this.lastByteRangeEndOffset = NaN, this.inheritQuery = e, this.tagList = new Array, this.iframe = !1 + } + getMediaFragment(e, t, i) { + const r = { + mediaOptionType: i, + absoluteUrl: this.url, + start: this.start, + duration: this.duration, + mediaSeqNum: this.mediaSeqNum, + discoSeqNum: this.cc, + mediaOptionId: t, + itemId: e, + isLastFragment: !1, + isInitSegment: this.isInitSegment + }; + return null !== (e = this.byteRange) && void 0 !== e && e.length && (r.byteRangeOffset = { + start: this.byteRangeStartOffset, + end: this.byteRangeEndOffset + }), this.iframe && (r.iframe = this.iframe), this.levelkey && (r.keyTagInfo = this.levelkey), this.programDateTime && (r.programDateTime = this.programDateTime), r + } + get url() { + return !this._url && this.relurl && this.baseurl && (this._url = bu.buildAbsoluteURL(this.baseurl, this.relurl, { + alwaysNormalize: !0, + inheritQuery: this.inheritQuery + })), this._url + } + set url(e) { + this._url = e + } + get programDateTime() { + return !this._programDateTime && this.rawProgramDateTime && (this._programDateTime = new Date(Date.parse(this.rawProgramDateTime))), this._programDateTime + } + get byteRange() { + if (!this._byteRange) { + const i = new Array(2); + var e, t; + this.rawByteRange && (1 === (e = this.rawByteRange.split("@", 2)).length ? (t = this["lastByteRangeEndOffset"], i[0] = t || 0) : i[0] = parseInt(e[1]), i[1] = parseInt(e[0]) + i[0]), this._byteRange = i + } + return this._byteRange + } + get byteRangeStartOffset() { + return this.byteRange[0] + } + get byteRangeEndOffset() { + return this.byteRange[1] + } + get rangeString() { + return 0 <= this.start && 0 <= this.duration ? `${this.start.toFixed(2)}-${(this.start+this.duration).toFixed(2)}` : "N/A" + } + get fragTag() { + return `sn/cc/levelId: ${this.mediaSeqNum}/${this.cc}` + } + } + const ym = { + parseMediaCharacteristics: e => e ? e.split(/\s*,\s*/) : new Array, + addMediaToSelectionArray(e, t, i) { + if (void 0 === e) return -1; + const r = e.MediaSelectionGroupOptions; + let n = r.find(e => e.MediaSelectionOptionsMediaType === t.mediaType && e.MediaSelectionOptionsName === t.name && e.MediaSelectionOptionsExtendedLanguageTag === t.lang); + return n || (n = { + MediaSelectionOptionsMediaType: t.mediaType, + MediaSelectionOptionsExtendedLanguageTag: t.lang, + MediaSelectionOptionsIsDefault: t.default, + MediaSelectionOptionsName: t.name, + MediaSelectionOptionsPersistentID: i, + MediaSelectionOptionsTaggedMediaCharacteristics: t.characteristics + }, t.mediaType === Su.SUBTITLE && (n.MediaSelectionOptionsDisplaysNonForcedSubtitles = t.forced ? vu.NO : vu.YES), i++, r.push(n)), t.persistentID = n.MediaSelectionOptionsPersistentID, i + }, + addDefaultClosedCaptionOption(e, t, i, r) { + e = { + itemId: e, + mediaOptionType: gu.Subtitle, + id: 0, + mediaOptionId: "cc1_" + Zl(), + mediaType: Su.CLOSEDCAPTION, + inStreamID: "CC1", + groupId: "cc", + name: "English-CC", + type: "CLOSED-CAPTIONS", + default: !1, + autoselect: !1, + forced: !1, + lang: "en", + characteristics: ["public.accessibility.transcribes-spoken-dialog", "public.accessibility.describes-music-and-sound"], + persistentID: r + }; + t.push(e), ym.addMediaToSelectionArray(i, e, r) + } + }, + vm = { + BANDWIDTH: NaN, + "AVERAGE-BANDWIDTH": NaN + }, + Sm = { + "TIME-OFFSET": NaN, + "FRAME-RATE": NaN, + SCORE: NaN, + "PLANNED-DURATION": NaN, + DURATION: NaN + }, + bm = /^(\d+)x(\d+)$/, + Tm = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g; + class Em { + constructor(e) { + this.validTags = e + } + isKey(e) { + return e in this.validTags + } + trySetValue(e, t, i) { + return !!this.isKey(e) && (i[e] = this.parseFunc(t), !0) + } + } + class Im { + static parseTags(t) { + let i; + var r = {}; + if (!t) return r; + for (Tm.lastIndex = 0; null !== (i = Tm.exec(t));) { + const t = i[1].toUpperCase(); + let e = i[2]; + 0 === e.indexOf('"') && e.lastIndexOf('"') === e.length - 1 && (e = e.slice(1, -1)); + for (const i of Im.tagParsers) + if (i.trySetValue(t, e, r)) break + } + return r + } + } + Im.tagParsers = [new class extends Em { + parseFunc(e) { + return e + } + }({ + NAME: "", + TYPE: "", + DEFAULT: "", + AUTOSELECT: "", + FORCED: "", + LANGUAGE: "", + URI: "", + AUDIO: "", + "VIDEO-RANGE": "", + "CLOSED-CAPTIONS": "", + CODECS: "", + BYTERANGE: "", + "INSTREAM-ID": "", + "GROUP-ID": "", + CHANNELS: "", + CHARACTERISTICS: "", + KEYFORMAT: "", + KEYFORMATVERSIONS: "", + "DATA-ID": "", + VALUE: "", + METHOD: "", + "HDCP-LEVEL": "", + "ALLOWED-CPC": "", + SUBTITLES: "", + ID: "", + CLASS: "", + "START-DATE": "", + "END-DATE": "", + "END-ON-NEXT": "", + "SERVER-URI": "", + "PATHWAY-ID": "" + }), new class extends Em { + parseFunc(e) { + e = parseInt(e); + return e > Number.MAX_SAFE_INTEGER ? 1 / 0 : e + } + }(vm), new class extends Em { + constructor() { + super(...arguments), this.parseFunc = parseFloat + } + }(Sm), new class extends Em { + parseFunc(e) { + let t = (e || "0x").slice(2); + t = (1 & t.length ? "0" : "") + t; + const i = new Uint8Array(t.length / 2); + for (let e = 0; e < t.length / 2; e++) { + var r = parseInt(t.slice(2 * e, 2 * e + 2), 16); + if (!ne(r)) return; + i[e] = r + } + return i + } + }({ + IV: null + }), new class extends Em { + parseFunc(e) { + e = bm.exec(e); + let t; + return null !== e && (t = { + width: parseInt(e[1], 10), + height: parseInt(e[2], 10) + }), t + } + }({ + RESOLUTION: null + })]; + const wm = { + ExtractVariableParameter: /{\$(.*?)}/g, + LevelPlaylistFast: /#EXTINF:(\d*(?:\.\d+)?)(?:,(.*))?|(?!#)(\S.+)|#EXT-X-BYTERANGE: *(.+)|#EXT-X-PROGRAM-DATE-TIME:(.+)|#EXT-X-BITRATE:(.+)|#EXT-X-DATERANGE:(.+)|#.*/g, + LevelPlaylistSlow: /(?:(?:#(EXTM3U))|(?:#EXT-X-(PLAYLIST-TYPE):(.+))|(?:#EXT-X-(MEDIA-SEQUENCE): *(\d+))|(?:#EXT-X-(TARGETDURATION): *(\d+))|(?:#EXT-X-(KEY):(.+))|(?:#EXT-X-(START):(.+))|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DISCONTINUITY-SEQ)UENCE:(\d+))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(VERSION):(\d+))|(?:#EXT-X-(MAP):(.+))|(?:#EXT-X-(I-FRAMES)-ONLY)|(?:#EXT-X-(DEFINE):(.+))|(?:(#)(.*):(.*))|(?:(#)(.*))(?:.*)\r?\n?/, + MasterPlaylist: /#EXT-X-STREAM-INF:([^\n\r]*)[\r\n]+([^\r\n]+)|#EXT-X-I-FRAME-STREAM-INF:([^\r\n]+)|#EXT-X-DEFINE:([^\n\r]*)|#EXT-X-CONTENT-STEERING:([^\n\r]*)/g, + MasterPlaylistAlternateMedia: /#EXT-X-MEDIA:(.*)/g, + SessionData: /#EXT-X-SESSION-DATA[^:]*:(.*)/g, + SessionKeys: /#EXT-X-SESSION-KEY:([^\n\r]*)/g, + VARIABLE_PLAYLIST_REGEX: /(NAME|VALUE)=\"(.*)\",(NAME|VALUE)=\"(.*)\"|(IMPORT)=\"(.*)\"/ + }; + + function Am(e, t, i) { + return cu.buildAbsoluteURL(t, e, { + alwaysNormalize: !0, + inheritQuery: i + }) + } + class Om { + static isValidPlaylist(e) { + return 0 === e.indexOf("#EXTM3U") + } + static isMediaPlaylist(e) { + return 0 < e.indexOf("#EXTINF:") || 0 < e.indexOf("#EXT-X-PLAYLIST-TYPE:") + } + static replaceVariables(e, t) { + let i, r = !1; + return e && t && (i = e.replace(wm.ExtractVariableParameter, e => { + wm.ExtractVariableParameter.lastIndex = 0; + e = wm.ExtractVariableParameter.exec(e)[1]; + if (e && t.hasOwnProperty(e)) return t[e]; + r = !0 + })), { + updatedString: i, + error: r + } + } + static parseDecryptData(e, t, i) { + const r = Im.parseTags(e), + n = (e = r.METHOD) && e in om ? r.METHOD : null; + e = null !== (e = r.KEYFORMAT) && void 0 !== e ? e : null; + if (n && Om.shouldSelectKeyTag(e, n, i)) { + const s = r.URI, + i = r.IV || null; + if (s && r.IV && !i) { + const s = new R(L, _, !0, `Invalid IV: ${r.IV}`, $.PlaylistErrorInvalidEntry); + throw s.url = t, s + } + const a = s ? cu.buildAbsoluteURL(t, s, { + alwaysNormalize: !0 + }) : t, + o = (r.KEYFORMATVERSIONS || "1").split("/").map(Number).filter(isFinite); + return new zc(n, a, i, e, o) + } + } + static shouldSelectKeyTag(e, t, i) { + return "AES-128" === t || "NONE" === t || null == i || e === am.getKeySystemFormat(i) + } + static optOutClosedCaption(t) { + let i = !1, + r = !1; + if (t) + for (let e = 0; e < t.length; ++e) { + const n = t[e]; + if (n.videoCodec && ((r = !0) !== n.iframes && n.closedcaption && "none" === n.closedcaption.toLowerCase())) { + i = !0; + break + } + } + return !r || i + } + static parseRootPlaylistAlternateMediaOptions(a, o, l, d, u, c) { + let h, p; + var f = { + MediaSelectionGroupAllowEmptySelection: 1, + MediaSelectionGroupMediaCharacteristics: ["public.audible"], + MediaSelectionGroupMediaType: Su.AUDIO, + MediaSelectionGroupOptions: [] + }, + m = { + MediaSelectionGroupAllowEmptySelection: 1, + MediaSelectionGroupMediaCharacteristics: ["public.legible"], + MediaSelectionGroupMediaType: Su.SUBTITLE, + MediaSelectionGroupOptions: [] + }, + g = { + videoAlternateOptions: [], + audioAlternateOptions: [], + subtitleAlternateOptions: [], + audioMediaSelectionGroup: f, + subtitleMediaSelectionGroup: m + }; + let y = 0; + for (wm.MasterPlaylistAlternateMedia.lastIndex = 0; null != (h = wm.MasterPlaylistAlternateMedia.exec(o));) { + const o = Om.replaceVariables(h[1], c); + if (o.error) { + p = new R(L, N, !0, $.PlaylistErrorInvalidEXTXDEFINE.text, $.PlaylistErrorInvalidEXTXDEFINE); + break + } + var v = Im.parseTags(o.updatedString); + let e, t, i, r = Su.UNKNOWN; + const S = ym.parseMediaCharacteristics(v.CHARACTERISTICS), + b = v["GROUP-ID"], + T = v.CHANNELS; + let n, s = null; + switch (v.TYPE) { + case "VIDEO": + r = Su.VIDEO, t = g.videoAlternateOptions; + break; + case "AUDIO": + r = Su.AUDIO, s = gu.AltAudio, t = g.audioAlternateOptions, i = f; + const a = d.find(e => e.audioGroupId === b); + n = a ? a.audioCodecList : []; + break; + case "SUBTITLES": + r = Su.SUBTITLE, s = gu.Subtitle, t = g.subtitleAlternateOptions, i = m; + break; + case "CLOSED-CAPTIONS": + r = Su.CLOSEDCAPTION, s = gu.Subtitle, e = v["INSTREAM-ID"], t = g.subtitleAlternateOptions, i = m + } + const E = { + itemId: a, + mediaOptionType: s, + mediaType: r, + groupId: b, + channels: T, + groupCodecList: n, + name: v.NAME, + type: v.TYPE, + default: "YES" === v.DEFAULT, + autoselect: "YES" === v.AUTOSELECT, + forced: "YES" === v.FORCED, + characteristics: S, + persistentID: y, + id: t ? t.length : 0, + mediaOptionId: `${v.NAME}_${b}_${y}`, + lang: pm.shortenLanguageCode(v.LANGUAGE) + }; + v.URI && (E.url = Am(v.URI, l, u)), E.name || (E.name = E.lang, E.mediaType === Su.CLOSEDCAPTION && (E.name += " CC")), E.mediaType === Su.CLOSEDCAPTION && e && (E.inStreamID = e), t && (E.id = t.length, t.push(E)), y = ym.addMediaToSelectionArray(i, E, y) + } + return 0 !== g.subtitleAlternateOptions.length || Om.optOutClosedCaption(d) || ym.addDefaultClosedCaptionOption(a, g.subtitleAlternateOptions, m, y), { + alternateMediaInfo: g, + playlistParsingError: p + } + } + static parseMediaOptionPlaylist(e, t, i = !0, r, n, s, a, o, l, d = 0, u = !1) { + var c; + let h = 0, + p = 0; + const f = { + itemId: s, + mediaOptionId: a, + mediaOptionType: o, + type: "", + version: 0, + url: t, + initSegments: {}, + fragments: [], + liveOrEvent: !0, + startSN: 0, + endSN: 0, + iframesOnly: u, + targetduration: 0, + totalduration: 0, + averagetargetduration: 0, + ptsKnown: !1 + }; + let m, g, y, v = new zc("NONE", t, null, null, null), + S = !1, + b = !1, + T = 0, + E = null, + I = new gm(i), + w = 0; + const A = {}; + let O, k, C, D = !0, + M = !0; + wm.LevelPlaylistFast.lastIndex = 0; + for (var x = () => new R(L, _, !0, "Invalid key system preference for the playlist", $.IncompatibleAsset); null !== (m = wm.LevelPlaylistFast.exec(e));) { + const e = m[1]; + if (e) { + I.duration = parseFloat(e); + const t = (" " + m[2]).slice(1); + I.title = t || null, I.tagList.push(t ? ["INF", e, t] : ["INF", e]) + } else if (m[3]) { + if (ne(I.duration)) { + const e = h++; + if (I.start = p + d, I.levelkey = v, b && !S) { + k = x(); + break + } + if (S = !1, b = !1, I.mediaSeqNum = e, I.cc = T, I.iframe = f.iframesOnly, I.baseurl = t, (O = Om.replaceVariables((" " + m[3]).slice(1), A)).error) { + k = new R(L, N, !0, $.PlaylistErrorInvalidEXTXDEFINE.text, $.PlaylistErrorInvalidEXTXDEFINE); + break + } + if (I.relurl = O.updatedString, I.bitrate = ne(I.byteRangeEndOffset) ? 8 * (I.byteRangeEndOffset - I.byteRangeStartOffset) / I.duration : w, null != y) { + I.rawProgramDateTime = y, I.tagList.push(["PROGRAM-DATE-TIME", I.rawProgramDateTime]); + const e = I.programDateTime.getTime(); + f.programDateTimeMap = null !== (c = f.programDateTimeMap) && void 0 !== c ? c : {}, f.programDateTimeMap[e] = I.mediaSeqNum, f.dateMediaTimePairs = null !== (c = f.dateMediaTimePairs) && void 0 !== c ? c : [], f.dateMediaTimePairs.push([e, I.start]), y = void 0 + } + if (f.fragments.push(I.getMediaFragment(s, a, o)), E = I, p += I.duration, D || !f.initSegments[T] || M) + if (f.iframesOnly && 0 < I.byteRangeStartOffset && !f.initSegments[T] && !M) { + const e = new gm(i); + if (e.url = I.url, e.rawByteRange = Math.min(I.byteRangeStartOffset, 1316) + "@0", e.baseurl = t, e.isInitSegment = !0, e.cc = T, e.levelkey = v, e.iframe = !0, b && !S) { + k = x(); + break + } + S = !1, b = !1, f.initSegments[T] = e.getMediaFragment(s, a, o) + } else C && (C.discoSeqNum = T, f.initSegments[T] = C); + D = !1, M = !1, I = new gm(i) + } + } else if (m[4]) { + if (I.rawByteRange = (" " + m[4]).slice(1), E) { + const e = E.byteRangeEndOffset; + e && (I.lastByteRangeEndOffset = e) + } + } else if (m[5]) y = (" " + m[5]).slice(1); + else if (m[6]) { + const e = parseInt(m[6]); + ne(e) && (w = 1e3 * e) + } else if (m[7]) { + const e = m[7], + t = Im.parseTags(e); + t.ID && (f.daterangeTags || (f.daterangeTags = {}), f.daterangeTags[t.ID] = t) + } else { + for (m = m[0].match(wm.LevelPlaylistSlow), g = 1; g < m.length && void 0 === m[g]; g++); + const e = Om.replaceVariables((" " + m[g + 1]).slice(1), A), + l = Om.replaceVariables((" " + m[g + 2]).slice(1), A); + if (e.error || l.error) { + k = new R(L, N, !0, $.PlaylistErrorInvalidEXTXDEFINE.text, $.PlaylistErrorInvalidEXTXDEFINE); + break + } + const d = e.updatedString, + u = l.updatedString; + switch (m[g]) { + case "#": + I.tagList.push(u ? [d, u] : [d]); + break; + case "PLAYLIST-TYPE": + f.type = d.toUpperCase(), "VOD" === f.type && (f.liveOrEvent = !1); + break; + case "MEDIA-SEQUENCE": + 0 === f.fragments.length && (h = f.startSN = parseInt(d)); + break; + case "TARGETDURATION": + f.targetduration = parseFloat(d); + break; + case "VERSION": + f.version = parseInt(d); + break; + case "EXTM3U": + break; + case "ENDLIST": + f.liveOrEvent = !1; + break; + case "DIS": + T++, I.tagList.push(["DIS"]), D = !0; + break; + case "DISCONTINUITY-SEQ": + T = parseInt(d); + break; + case "KEY": + const e = d; + if (b = !0, !S) { + try { + v = Om.parseDecryptData(e, t, r) + } catch (e) { + k = e + } + v && (S = !0) + } + break; + case "START": + const l = d, + P = Im.parseTags(l)["TIME-OFFSET"]; + ne(P) && (f.startTimeOffset = P); + break; + case "I-FRAMES": + f.iframesOnly = !0; + break; + case "MAP": + const c = Im.parseTags(d); + if (I.relurl = c.URI, I.rawByteRange = c.BYTERANGE, I.baseurl = t, I.isInitSegment = !0, I.levelkey = v, b && !S) { + k = x(); + break + } + S = !1, b = !1, C = I.getMediaFragment(s, a, o), M = !0, I = new gm(i); + break; + case "DEFINE": + const p = wm.VARIABLE_PLAYLIST_REGEX.exec(d), + m = "NAME" === p[1] ? p[2] : p[4], + g = "VALUE" === p[1] ? p[2] : p[4], + y = p[5], + N = p[6]; + if (m || g || "IMPORT" !== y || !n.hasOwnProperty(N)) { + if (!m || y || p[1] === p[3] || A.hasOwnProperty(m)) { + k = new R(L, _, !0, $.PlaylistErrorMissingImportReference.text, $.PlaylistErrorMissingImportReference); + break + } + A[m] = g + } else A[N] = n[N] + } + } + } + return I = E, I && !I.relurl && (f.fragments.pop(), p -= I.duration), !f.liveOrEvent && 0 < f.fragments.length && (f.fragments[f.fragments.length - 1].isLastFragment = !0), f.totalduration = p, f.averagetargetduration = p / f.fragments.length, f.endSN = h - 1, { + mediaOptionDetails: f, + playlistParsingError: k + } + } + static parseRootPlaylist(t, e, i, r) { + const n = [], + s = {}; + let a, o, l, d, u = null, + c = !0; + for (wm.MasterPlaylist.lastIndex = 0; null != (a = wm.MasterPlaylist.exec(e));) + if (a[4]) { + a = wm.VARIABLE_PLAYLIST_REGEX.exec(a[4]); + const t = "NAME" === a[1] ? a[2] : a[4], + e = "VALUE" === a[1] ? a[2] : a[4], + i = a[5]; + if (!t || s.hasOwnProperty(t) || i || a[1] === a[3]) { + d = new R(L, _, !0, $.PlaylistErrorInvalidEXTXDEFINE.text, $.PlaylistErrorInvalidEXTXDEFINE); + break + } + s[t] = e + } else if (a[5]) { + const t = Om.replaceVariables(a[5], s); + if (t.error) { + d = new R(L, _, !0, $.PlaylistErrorInvalidEXTXDEFINE.text, $.PlaylistErrorInvalidEXTXDEFINE); + break + } + const e = Im.parseTags(t.updatedString); + if ("string" != typeof e["SERVER-URI"]) { + d = new R(L, _, !0, $.PlaylistErrorInvalidSERVERURI.text, $.PlaylistErrorInvalidSERVERURI); + break + } + if (null != e["PATHWAY-ID"] && "string" != typeof e["PATHWAY-ID"]) { + d = new R(L, _, !0, $.PlaylistErrorInvalidPATHWAYID.text, $.PlaylistErrorInvalidPATHWAYID); + break + } + u = { + serverURI: Am(e["SERVER-URI"], i, !1), + initPathwayID: e["PATHWAY-ID"] || "." + } + } else { + l = Om.replaceVariables(a[1] || a[3], s); + const e = Im.parseTags(l.updatedString); + if (o = Om.replaceVariables(a[2] || e.URI, s), l.error || o.error) { + d = new R(L, _, !0, $.PlaylistErrorInvalidEXTXDEFINE.text, $.PlaylistErrorInvalidEXTXDEFINE); + break + } + if (void 0 !== e.SCORE && !ne(e.SCORE) || e.SCORE < 0) { + d = new R(L, _, !0, $.PlaylistErrorInvalidSCORE.text, $.PlaylistErrorInvalidSCORE), c = !1; + break + } + c && void 0 === e.SCORE && (c = !1); + const u = e.BANDWIDTH, + p = e["AVERAGE-BANDWIDTH"], + f = p || u, + m = null !== (h = e["VIDEO-RANGE"]) && void 0 !== h ? h : "SDR"; + if (null == (h = m) || !cm.includes(h)) continue; + const g = { + itemId: t, + mediaOptionId: `level_${(f||0)+n.length%1e3/1e3}`, + mediaOptionType: gu.Variant, + attrs: e, + url: Am(o.updatedString, i, r), + name: e.NAME, + audioGroupId: e.AUDIO, + subtitleGroupId: e.SUBTITLES, + iframes: !!a[3], + bandwidth: u, + avgBandwidth: p, + bitrate: f, + videoRange: m, + frameRate: e["FRAME-RATE"], + allowedCPCMap: Om.parseAllowedCPC(e["ALLOWED-CPC"]), + closedcaption: e["CLOSED-CAPTIONS"], + levelCodec: e.CODECS, + score: e.SCORE, + pathwayID: e["PATHWAY-ID"] || "." + }, + y = e["HDCP-LEVEL"]; + dm(y) && (g.hdcpLevel = y); + var h = e.RESOLUTION; + if (h && (g.width = h.width, g.height = h.height), e.CODECS) { + g.videoCodecList = new Array, g.audioCodecList = new Array; + const t = e.CODECS.split(/[ ,]+/), + i = t["length"]; + for (let e = 0; e < i; e++) { + const i = t[e]; + switch (i.slice(0, 4)) { + case "avc1": + g.videoCodec = be.avc1toavcoti(i), g.videoCodecList.push(g.videoCodec); + break; + case "avc3": + case "dvav": + case "dva1": + case "hev1": + case "hvc1": + case "dvh1": + case "dvhe": + case "vp09": + g.videoCodec = i, g.videoCodecList.push(g.videoCodec); + break; + default: + g.audioCodec = i, g.audioCodecList.push(g.audioCodec) + } + } + 1 < g.audioCodecList.length && (g.audioCodec = fm.getRichestAudioCodec(g.audioCodecList)), 1 < g.videoCodecList.length && (g.videoCodec = fm.getRichestVideoCodec(g.videoCodecList)) + } + if (null != (d = "string" != typeof(h = g.pathwayID) ? mm("invalid steering manifest PATHWAY-PRIORITY list item data type") : /^[\w\-\.]+$/.test(h) ? void 0 : mm("steering manifest contains invalid pathway ID: " + h))) break; + let cpc = g.allowedCPCMap ? JSON.stringify(g.allowedCPCMap) : "null"; + if (!cpc.includes("WIDEVINE_HARDWARE") && !g.url.includes('trickPlay') && !g.videoCodec.includes("hvc1")) + n.push(g) + } + try{ + // console.log(n, window.screen.width) + let ok = (n.map( function(item){return{height : item.height, content: item}})); + let screenHeight = (app.cfg.visual.videoRes ?? window.screen.height) ; + ok.sort(function (a, b) { + return a.height - b.height; + }); + for (var i = 0; i < ok.length; i++){ + if (ok[i].height > screenHeight){ + if (i == 0){n.splice(0,n.length);n.push(ok[i].content)} + else{n.splice(0,n.length);n.push(ok[i-1].content)} + console.log('selected' , n[0].height) + break; + + } + + } + if (n.length > 1){ + n.splice(0,n.length - 1); + } + // console.log(n) + // console.log(ok) + } catch (e){ console.log(e)} + return { + variantMediaOptions: n, + contentSteeringOption: u, + masterVariableList: s, + playlistParsingError: d, + scoreAvailable: c + } + } + static parseAllowedCPC(e) { + if ("string" != typeof e) return null; + const n = {}; + return e.split(",").forEach(e => { + const t = e.split(":"); + let i, r; + if (2 === t.length) i = t[0].trim(), r = t[1].trim(); + else { + if (!(2 < t.length)) return; + r = t[t.length - 1].trim(), t.pop(), i = t.join(":") + } + if (!(i in n)) { + let e = new Array; + "" !== r && (e = r.split("/").map(e => e.trim())), n[i] = e + } + }), n + } + static parseSessionKeys(e, t, i) { + var r; + const n = []; + for (wm.SessionData.lastIndex = 0; r = wm.SessionKeys.exec(e);) try { + const e = Om.parseDecryptData(r[1], t, i); + e && e.isEncrypted && n.push(e) + } catch (e) {} + return n + } + static parseSessionData(e, t) { + var i; + const r = [], + n = new Set; + for (wm.SessionData.lastIndex = 0; null != (i = wm.SessionData.exec(e));) { + const e = Im.parseTags(i[1]); + e.LANGUAGE = pm.shortenLanguageCode(e.LANGUAGE); + const t = e.LANGUAGE ? e["DATA-ID"] + "|" + e.LANGUAGE : void 0; + "DATA-ID" in e ? t && n.has(t) || ("com.apple.hls.other-tags" === e["DATA-ID"] && (e.VALUE = function(t) { + let i; + try { + i = JSON.parse(qc.base64DecodeToStr(t)) + } catch (e) { + i = t + } + return i + }(e.VALUE)), r.push(e), t && n.add(t)) : Qe().error(`Error processing DATA-ID ${e["DATA-ID"]} and LANGUAGE ${e.LANGUAGE}`) + } + return { + itemList: r, + baseUrl: t + } + } + } + var km, Cm, Dm, Mm, xm, Pm, Rm = Om; + const Lm = (e, t, i) => { + i = Object.assign(Object.assign({}, e), { + method: "GET", + responseType: "text", + extendMaxTTFB: i + }); + return Tu(i.url) ? Pc().load(i, t).pipe(hr(e => ({ + responseText: e.data.response.data.toString(), + responseURL: e.data.response.uri, + stats: e.stats + }))) : Cc(i, t).pipe(hr(([e, t]) => ({ + responseText: e.responseText, + responseURL: e.responseURL, + stats: t + }))) + }, + _m = (e, n, t, i, s, a, o, l, r) => { + const { + url: d, + itemId: u, + mediaOptionId: c, + mediaOptionType: h, + iframes: p = !1 + } = e, f = Lc(e, i); + return Lm({ + url: d, + xhrSetup: t.xhrSetup + }, f, r).pipe(hr(({ + responseText: e, + responseURL: t, + stats: i + }) => { + t || (s.warn("Missing response url. Reusing request url as base url"), t = d); + var r = performance.now(), + e = Om.parseMediaOptionPlaylist(e, t, !0, a, l, u, c, h, s, n, p); + Nc(e.mediaOptionDetails); + var t = performance.now(), + e = e["mediaOptionDetails"], + r = { + playlistLoadTimeMs: i.tload - i.trequest, + playlistParseTimeMs: t - r + }; + return o.setPlaylistSample(r), { + mediaOptionDetails: e, + stats: i + } + }), (m = h, g = c, y = d, e => e.pipe(Vn(e => { + if (e instanceof pc) throw new uc(!1, "Timeout", 0, $.PlaylistTimeoutError, !0, m, g, y); + if (e instanceof oc) throw new uc(!1, e.message, e.code, { + code: e.code, + text: "Playlist Network Error" + }, !1, m, g, y); + throw e + })))); + var m, g, y + }, + Nm = (f, e, m, t, g) => $i(e).pipe(La(e => { + const { + keyTagInfo: t, + isInitSegment: i, + iframe: r, + byteRangeOffset: n + } = f, s = t["method"], { + start: a, + end: o + } = n; + if ("AES-128" !== s) return $i(e); { + !t.uri || t.iv || t.format && "identity" !== t.format || (t.iv = function(t) { + const i = new Uint8Array(16); + for (let e = 12; e < 16; e++) i[e] = t >> 8 * (15 - e) & 255; + return i + }(f.mediaSeqNum)); + const n = e, + s = t.key.buffer, + l = t.iv.buffer, + d = o && (r || i) ? o - a : void 0, + u = !m.enableWebCrypto || !!d, + c = s.slice(0), + h = l.slice(0), + p = { + useJSCrypto: u, + plainTextLength: d + }; + return g.decrypt(c, h, "AES-CBC", n, p) + } + })); + + function Fm(e, t) { + var i = e.fragments, + r = t.mediaSeqNum - e.startSN; + return 0 <= r && r < e.fragments.length && $p(t, i[r]) + } + + function Bm(t, r, n, s, a = !1, o = !1) { + if (Fm(t, r)) { + var l = r.mediaSeqNum - t.startSN; + let i = t.fragments, + e = i[l]; + var { + startDtsTs: d, + startPts: u, + endPts: r + } = r; + a && (i = t.fragments = t.fragments.slice(), e = i[l] = Object.assign({}, e)), e.startDtsTs = d, e.startPts = u, e.endPts = r, !s && void 0 === e.isIframeStart || (e.isIframeStart = s), e.start = n, e.duration = b(r, u); + for (let e = l, t = !0; 0 < e && (o || t); e--) t = Um(i, e, e - 1, s, u.timescale, a); + for (let e = l, t = !0; e < i.length - 1 && (o || t); e++) t = Um(i, e, e + 1, s, u.timescale, a); + l = i[i.length - 1]; + t.totalduration = l.start + l.duration - i[0].start, t.ptsKnown = !0 + } + } + + function Um(e, t, i, r, n, s = !1) { + var a = e[t]; + let o = e[i]; + var l = null != r && null != o.isIframeStart && o.isIframeStart !== r && o.discoSeqNum === a.discoSeqNum; + let d = o.start; + !l && null != o.startPts || (d = t < i ? a.start + a.duration : Math.max(a.start - o.duration, 0)); + n = ne(n) ? 1 / n : Number.EPSILON, n = Math.abs(o.start - d) > n; + return !(!l && !n || (s && (o = e[i] = Object.assign({}, o)), n && (o.start = d), l && (o.isIframeStart = r), 0)) + } + + function $m(e) { + if (e.programDateTimeMap) { + e.dateMediaTimePairs = []; + for (var [t, i] of Object.entries(e.programDateTimeMap)) { + t = Number(t), i = e.fragments[i - e.startSN]; + i && (i = i.start, e.dateMediaTimePairs.push([t, i])) + } + e.dateMediaTimePairs.sort((e, t) => e[0] - t[0]) + } + } + class Vm { + constructor(e) { + this.option = e + } + get name() { + return this.option.name + } + get priority() { + return this.option.priority + } + get expiry() { + return this.option.expiry + } + filter(i, e) { + const r = this.option.initFn && this.option.initFn(i, e) || (e ? Object.assign({}, e) : {}); + let t = i; + return this.option.firstPassFn && i.forEach((e, t) => this.option.firstPassFn(e, t, r, i)), this.option.filterFn && (t = i.filter((e, t) => this.option.filterFn(e, t, r, i))), null != this.option.filterFn && 0 !== t.length || !this.option.minSortingFn || (t = i.sort((e, t) => this.option.minSortingFn(e, t, r, i))), this.option.finalFn && this.option.finalFn(t, r, i), t + } + } + + function Km(e, t, i) { + return (t || []).reduce((e, t) => t.filter(e, i), Array.from(e)) + } + + function qm(t, e) { + return e.filter(e => { + return Au(t, null !== (e = e.url) && void 0 !== e ? e : null) + }) + } + + function Hm() { + return [new Vm({ + name: "Remove Filter", + priority: 0, + filterFn: (t, e, i) => !i || i.removed.every(e => t.mediaOptionId !== e) + }), new Vm({ + name: "Penalty Box Filter", + priority: 1, + filterFn: (t, e, i) => { + const r = performance.now(); + return !i || i.penaltyBoxQueue.every(e => e.expiry <= r || t.mediaOptionId !== e.mediaOptionId) + } + }), new Vm({ + name: "Compatible IDs Filter", + priority: 1, + filterFn: (t, e, i) => !i || null == i.compatibleIds || i.compatibleIds.some(e => e === t.mediaOptionId) + })] + } + class jm extends kl { + constructor(e, t, i) { + super(e), this.itemId = t, this.mediaOptionType = i, this.allowFilters = this._initFilters() + } + get mediaOptionList() { + var e; + return (null === (e = this.mediaOptionListInfo) || void 0 === e ? void 0 : e.mediaOptions) || null + } + get mediaOptionList$() { + return this.mediaOptionListInfo$.pipe(hr(({ + mediaOptions: e + }) => e)) + } + mediaOptionFromId(t) { + var e; + return null !== (e = (null !== (e = this.mediaOptionList) && void 0 !== e ? e : []).find(e => e.mediaOptionId === t)) && void 0 !== e ? e : null + } + _getFilteredList(e) { + return Km(e.mediaOptions, this.allowFilters, e) + } + get filteredMediaOptionList() { + return this.mediaOptionListInfo ? this._getFilteredList(this.mediaOptionListInfo) : null + } + get filteredMediaOptionList$() { + return this.mediaOptionListInfo$.pipe(La(e => { + const t = [Wu], + i = performance.now(); + for (const r of e.penaltyBoxQueue) ne(r.expiry) && r.expiry > i && t.push(bn(r.expiry - i)); + return an(...t).pipe(hr(() => this._getFilteredList(e))) + }), vl()) + } + get preferredMediaOptionList() { + return this.filteredMediaOptionList ? qm(this.preferredHost, this.filteredMediaOptionList) : [] + } + get preferredMediaOptionList$() { + return ed([this.preferredHost$, this.filteredMediaOptionList$]).pipe(hr(([e, t]) => qm(e, t))) + } + getNewHost(e) { + e = this.getFallbackVariant(e, !1, !0); + return null != e && e.url ? Eu(e.url) : this.preferredHost + } + } + + function Qm(e) { + return "PQ" === e.videoRange || "HLG" === e.videoRange + } + + function Wm(e, t) { + return t.iframes === e + } + + function Gm(e, t, i, r) { + return !r || i.bitrate > r.bitrate && i.bitrate <= e.bitrate ? km.Better : i.bitrate === r.bitrate ? km.Same : km.Worse + } + + function zm(e, t) { + return e && !t ? -1 : !e && t ? 1 : 0 + }(dl = km = km || {})[dl.Better = 1] = "Better", dl[dl.Same = 0] = "Same", dl[dl.Worse = -1] = "Worse"; + class Xm extends jm { + constructor(e, t) { + super(e, t, gu.Variant) + } + static makeFilters() { + return [...Hm().concat([new Vm({ + name: "HDR Filter", + priority: 1, + filterFn: (e, t, i) => !i || (i.hasHdrLevels && i.preferHDR) === Qm(e) + }), new Vm({ + name: "Viewport Filter", + priority: 1, + firstPassFn: (e, t, i) => { + if (i && e && !e.iframes && e.videoCodec) { + const t = !i.lowestBitrate || e.bitrate < i.lowestBitrate ? e.bitrate : i.lowestBitrate; + i.lowestBitrate = t + } + }, + filterFn: (e, t, i) => !(e && i && i.viewportInfo && e.videoCodec && i.lowestBitrate) || function(e, t) { + return e.width < 1.35 * t.width && e.height < 1.35 * t.height && e.width * e.height < t.width * t.height * 1.35 + }({ + width: e.width, + height: e.height + }, i.viewportInfo) || e.bitrate === i.lowestBitrate + }), new Vm({ + name: "HDCP Filter", + priority: 2, + filterFn: (e, t, i) => !i || !dm(i.maxHdcpLevel) || um(e.hdcpLevel) < um(i.maxHdcpLevel) + })])].sort((e, t) => { + return (null !== (e = e.priority) && void 0 !== e ? e : Number.MAX_SAFE_INTEGER) - (null !== (t = t.priority) && void 0 !== t ? t : Number.MAX_SAFE_INTEGER) + }) + } + _initFilters() { + return Xm.kAllowFilters + } + get preferredHost() { + var e; + return null !== (e = null === (e = this.mediaOptionListInfo) || void 0 === e ? void 0 : e.preferredHost) && void 0 !== e ? e : null + } + get preferredHost$() { + return this.selectEntity(this.itemId, e => { + return null !== (e = null == e ? void 0 : e.mediaOptionListTuple[gu.Variant].preferredHost) && void 0 !== e ? e : null + }) + } + get mediaOptionListInfo() { + var e; + return null !== (e = null === (e = this.getEntity(this.itemId)) || void 0 === e ? void 0 : e.mediaOptionListTuple[gu.Variant]) && void 0 !== e ? e : null + } + get mediaOptionListInfo$() { + return this.selectEntity(this.itemId, e => { + return null === (e = null == e ? void 0 : e.mediaOptionListTuple) || void 0 === e ? void 0 : e[gu.Variant] + }).pipe(Kp()) + } + get hdrMode$() { + return this.mediaOptionListInfo$.pipe(hr(e => !0 === e.preferHDR && e.hasHdrLevels), Is()) + } + get maxHdcpLevel$() { + return this.selectEntity(this.itemId, e => { + e = null === (e = null == e ? void 0 : e.mediaOptionListTuple) || void 0 === e ? void 0 : e[gu.Variant]; + return null == e ? void 0 : e.maxHdcpLevel + }).pipe(Is()) + } + listFallbackVariants(t, e, i, r, n) { + var s = this.mediaOptionListInfo, + a = null === (o = this.mediaOptionList) || void 0 === o ? void 0 : o.find(e => e.mediaOptionId === t); + if (!a || !s) return null; + var o = this.makeFilteredListFromVariant(a, e, n); + if (!o) return null; + e = Eu(a.url), s = s.hasScore; + return Xm._listFallbackVariants(o, a, e, s, i, r, n) + } + getFallbackVariant(t, e, i, r) { + var n = this.mediaOptionListInfo, + s = null === (s = this.mediaOptionList) || void 0 === s ? void 0 : s.find(e => e.mediaOptionId === t); + if (!s || !n) return null; + e = this.makeFilteredListFromVariant(s, e, r); + if (!e) return null; + r = Eu(s.url), n = n.hasScore; + return Xm._getFallbackVariant(e, s, r, n, i) + } + makeFilteredListFromVariant(e, t, i) { + let r = this.mediaOptionListInfo; + if (!e || !this.mediaOptionList || !r) return null; + r = Object.assign(Object.assign({}, r), { + includeAllEligiblePathways: !0 + }); + e = Array.from(this.mediaOptionList); + let n = t ? Km(e, this.allowFilters, Object.assign(Object.assign({}, r), { + preferHDR: !1, + compatibleIds: null + })) : this._getFilteredList(r); + return n ? (i && 0 < i.length && (n = n.filter(e => !i.includes(e.mediaOptionId))), n) : null + } + get hasIframes() { + var e; + return null !== (e = null === (e = this.mediaOptionListInfo) || void 0 === e ? void 0 : e.hasIframeLevels) && void 0 !== e && e + } + canSwitchToSDR(e, t, i = !1) { + var r = this.mediaOptionListInfo; + if (!this.mediaOptionList || !r) return !1; + var n = this.mediaOptionFromId(e); + if (!n) return !1; + if (!Qm(n)) return !1; + var s = Eu(n.url), + e = Km(Array.from(this.mediaOptionList), this.allowFilters, Object.assign(Object.assign({}, r), { + preferHDR: !1, + compatibleIds: null + })), + r = r.hasScore; + return null != Xm._getFallbackVariant(e, n, s, r, t, i) + } + static _listFallbackVariants(e, r, n, t, s, a = !1, i = null) { + let o = !1; + const l = function(e, s, a) { + const t = [...e], + o = Eu(s.url), + l = s.audioGroupId; + return t.sort((e, t) => { + let i = 0; + var r = a && ne(e.score) && ne(t.score), + n = r ? e.score > t.score && e.score <= s.score : e.bitrate > t.bitrate && e.bitrate <= s.bitrate, + r = r ? e.score === t.score : e.bitrate === t.bitrate; + return n ? i = -1 : r ? (i = zm(Au(o, e.url), Au(o, t.url)), 0 === i && (i = zm(!l || e.audioGroupId === l, !l || t.audioGroupId === l))) : i = 1, i + }), t + }(e.filter(e => { + var t = !(e.iframes !== r.iframes || s && Au(n, e.url)), + i = a ? e.bitrate < r.bitrate : e.bitrate <= r.bitrate, + i = t && i; + return r.mediaOptionId === e.mediaOptionId ? (o = i, !1) : i + }), r, t); + return !o || i && i.includes(r.mediaOptionId) || l.unshift(r), l + } + static _getFallbackVariant(e, t, i, r, n, s = !1) { + let a = null; + e = (e = s ? e.filter(e => e.bitrate < t.bitrate) : e).filter(e => e.mediaOptionId !== t.mediaOptionId && e.iframes === t.iframes); + if (n && null != i) + for (const o of e) Gm(t, 0, o, a) !== km.Better || Au(i, o.url) || (a = o); + else + for (const o of e) { + const r = Gm(t, 0, o, a); + r !== km.Better && (r !== km.Same || !Au(i, o.url) || Au(i, a.url) && o.audioGroupId !== t.audioGroupId) || (a = o) + } + return a + } + getMatchingVariant(e, t) { + var i = this.mediaOptionFromId(e), + r = Eu(null == i ? void 0 : i.url), + n = t.mediaOptionType === gu.AltAudio ? "audioGroupId" : "subtitleGroupId"; + let s = null; + this.mediaOptionListInfo.hasScore; + for (const e of this.filteredMediaOptionList) + if (e[n] === t.groupId) { + if (!i) { + s = e; + break + } + var a = Gm(i, 0, e, s); + a !== km.Better && (a !== km.Same || s.mediaOptionId === i.mediaOptionId || e.mediaOptionId !== i.mediaOptionId && !Au(r, e.url)) || (s = e) + } return s + } + get currentPathwayID() { + var e; + return null === (e = this.mediaOptionListInfo) || void 0 === e ? void 0 : e.currentPathwayID + } + } + + function Ym(e, t) { + switch (e) { + case Cm.SendAlternateToPenaltyBox: + e = Cm.RetryRequest, 401 !== t && 403 !== t && 407 !== t && t !== $.CorruptStream.code && t !== $.LivePlaylistUpdateError.code || (e = Cm.SendEndCallback); + break; + case Cm.RemoveAlternatePermanently: + e = Cm.SendEndCallback + } + return e + } + + function Jm(e, t, i, r, n, s, a, o = !1) { + const l = s.mediaOptionListQueries[n], + d = 0 != (e.errorActionFlags & Dm.MoveAllAlternatesMatchingHost), + u = s.mediaOptionListQueries[n].mediaOptionFromId(r), + c = a.getFallbackMediaOptionTupleFromMediaOptionId(s, n, r, u.backingMediaOptionId, !1, d, o); + let { + errorAction: h, + errorActionFlags: p + } = e; + return s.isValidMediaOptionTuple(c) ? Au(l.preferredHost, c[n].url) && (p &= ~Dm.MoveAllAlternatesMatchingHost) : (t || (h = Ym(h, i), p = 0), l instanceof Xm && (!0 === l.mediaOptionFromId(r).iframes ? (h = Cm.DoNothing, p = 0) : !t && a.canSwitchToSDR(s, r, d, o) && (h = e.errorAction, p = Dm.SwitchToSDR))), { + errorAction: h, + errorActionFlags: p + } + } + + function Zm(e) { + let t, i = 0; + switch (e) { + case 0: + t = Cm.SendAlternateToPenaltyBox, i = Dm.MoveAllAlternatesMatchingHost; + break; + case 410: + t = Cm.RemoveAlternatePermanently; + break; + case 500: + case 502: + case 503: + case 504: + case 404: + case 409: + case 401: + case 403: + case 407: + case $.LivePlaylistUpdateError.code: + case $.PlaylistNotReceived.code: + default: + t = Cm.SendAlternateToPenaltyBox, i = 0 + } + return { + errorAction: t, + errorActionFlags: i + } + } + + function eg(i, r, n, s, a, o, l) { + var { + errorAction: d, + errorActionFlags: u + } = i; + let e = !0; + switch (d) { + case Cm.RemoveAlternatePermanently: + case Cm.SendAlternateToPenaltyBox: { + if (null == a || null == o) return r.handled = !1; + const i = n.itemId; + let e = o, + t = n.mediaOptionListQueries[a].mediaOptionFromId(o); + t.backingMediaOptionId && (e = t.backingMediaOptionId, t = n.mediaOptionListQueries[a].mediaOptionFromId(e)); + var c = 0 != (u & Dm.MoveAllAlternatesMatchingHost), + h = 0 != (u & Dm.MoveAllAlternatesMatchingHDCP), + p = 0 != (u & Dm.SwitchToSDR), + f = d === Cm.RemoveAlternatePermanently; + if (h && "hdcpLevel" in t) { + const r = t.hdcpLevel; + s.setMaxHdcpLevel(i, r) + } + if (p && s.switchToSDROnly(i), c) { + const r = Eu(t.url); + s.moveAllWithMatchingHosts(i, a, r, f) + } else f ? s.removePermanently(i, a, e) : s.addToPenaltyBox(i, a, e); + if (n.enabledMediaOptionIdByType(a) === o) { + let e = [Lu, Lu, Lu]; + e = s.getFallbackMediaOptionTupleFromMediaOptionId(n, a, o, null, !1, c, l), n.isValidMediaOptionTuple(e) ? s.setPreferredHost(i, Eu(e[gu.Variant].url)) : r.fatal = !0, r.fatal && (e = [Lu, Lu, Lu]), s.setNextMediaOptions(n.itemId, e) + } + break + } + case Cm.SendEndCallback: + r.fatal = !0; + break; + case Cm.RetryRequest: + case Cm.DoNothing: + default: + e = !1 + } + return r.handled = e, e + } + + function tg(e, t) { + t instanceof dc || t instanceof uc || t instanceof hc || (t instanceof gc || t instanceof mc) && le(t.keyuri) + } + + function ig(t, i, r) { + if (t.handled = !0, r && i < r.maxNumRetry && ne(r.retryDelayMs)) { + let e; + return e = "linear" === r.backoff ? (i + 1) * r.retryDelayMs : Math.pow(2, i) * r.retryDelayMs, e = Math.min(r.maxRetryDelayMs, e), tg(0, t), bn(e) + } + return t.fatal = !0, tg(0, t), Vi(t) + } + + function rg(e, t, i, r, n, s, a, o, l = !1) { + return (null == r ? void 0 : r.errorAction) === Cm.RetryRequest ? ig(e, t, i, s.logger) : ng(e, 0, r, n, s, a, o, l) + } + + function ng(e, t, i, r, n, s, a, o = !1) { + const l = new er; + return al(() => { + i && (tg(n.logger, e), eg(i, e, r, n, s, a, o)), l.error(e) + }), l + } + + function sg(t, i, r, n) { + return e => e.pipe(Vn(e => { + if (t.logger.error(`Got demux error ${e.message}`), e instanceof D || e instanceof F) { + Cm.SendAlternateToPenaltyBox; + return ng(e, 0, { + errorAction: e.fatal ? Cm.SendEndCallback : e instanceof F ? Cm.SendAlternateToPenaltyBox : Cm.RemoveAlternatePermanently, + errorActionFlags: 0 + }, i, t, r, n) + } + throw e + })) + } + + function ag(e, t, i, r, n) { + e = null !== (e = null === (e = n.getKeyInfo(e)) || void 0 === e ? void 0 : e.mediaOptionIds) && void 0 !== e ? e : []; + for (const s of e) + for (const n of i.mediaOptionListQueries) null != n.mediaOptionFromId(s) && r.updateConsecutiveTimeouts(i.itemId, n.mediaOptionType, t, "key") + } + + function og(r, t, e, n, s, a) { + const o = n.logger, + i = s.enabledMediaOptionKeys, + l = []; + for (const t of r.mediaOptionIds) { + const r = i.some(e => e.mediaOptionId === t), + e = s.mediaOptionListQueries.find(e => null != e.mediaOptionFromId(t)); + if (e) { + const n = e.mediaOptionType, + a = { + mediaOptionId: t, + mediaOptionType: n + }; + r ? l.push(a) : l.unshift(a) + } else o.warn(`Couldn't find query for ${t}`) + } + const d = new er; + return al(() => { + const e = r instanceof mc; + ag(r.keyuri, e, s, n, a); + let t, i = !1; + for (const { + mediaOptionId: e, + mediaOptionType: a + } of l) t = function(e, t, i, r, n) { + let s = { + errorAction: Cm.SendAlternateToPenaltyBox, + errorActionFlags: 0 + }; + if (e instanceof mc) s.errorAction = Cm.SendAlternateToPenaltyBox; + else { + const t = e.isOkToRetry; + e.keyErrorReason === rc.OutputRestricted ? (s.errorAction = Cm.RemoveAlternatePermanently, s.errorActionFlags |= Dm.MoveAllAlternatesMatchingHDCP) : t ? s = Zm(e.code) : s.errorAction = Cm.RemoveAlternatePermanently + } + s.errorActionFlags &= ~Dm.MoveAllAlternatesMatchingHost; + var a = r.enabledMediaOptionIdByType(i); + return t === a ? Jm(s, !1, e.code, a, i, r, n, e.isTimeout) : s + }(r, e, a, s, n), o.error(`[Keys] handleNetworkError uri=${le(r.keyuri)} mediaOptionId=${e} mediaOptionType=${a} action=${JSON.stringify(t)}`), t.errorAction === Cm.RetryRequest && (i = !0), eg(t, r, s, n, a, e); + i ? (d.next(), d.complete()) : (tg(0, r), d.error(r)) + }), d.pipe(La(() => ig(r, t, e, n.logger))) + } + + function lg(t, i, r, n, s, a, o, l) { + return n = Math.max(0, n), e => e.pipe(Za(() => { + null != i && o.updateConsecutiveTimeouts(t, i, !1, "load") + }), va(e => e.pipe(jr((e, t) => function(e, t, i, r, n, s, a, o) { + var l; + if (!(e instanceof p)) return Vi(e); + let d, u, c, h = !1; + if (e instanceof dc) c = { + errorAction: Ym(Zm((l = e).response.code).errorAction, l.response.code), + errorActionFlags: 0 + }; + else if (e instanceof uc || e instanceof hc) { + ({ + mediaOptionType: u, + mediaOptionId: d, + isTimeout: h + } = e); + const t = null === (l = s.mediaOptionListQueries[u]) || void 0 === l ? void 0 : l.mediaOptionFromId(d); + if (!r && e.isTimeout && null != t && (!("iframes" in t) || !0 !== t.iframes) && (a.updateConsecutiveTimeouts(s.itemId, e.mediaOptionType, !0, "load"), e instanceof hc && e.stats)) { + const t = performance.now(); + o.setBandwidthSample(Object.assign(Object.assign({}, e.stats), { + tfirst: e.stats.tfirst || t, + tload: e.stats.tload || t, + complete: !0, + mediaOptionType: u + })) + } + c = function(e, t, i, r, n) { + var s = e.response.code; + let a = Zm(s); + var { + mediaOptionId: o, + mediaOptionType: l + } = e; + return t ? a.errorActionFlags &= ~Dm.MoveAllAlternatesMatchingHost : a = function(e, t, i, r, n) { + let { + errorAction: s, + errorActionFlags: a + } = t; + if (e.isTimeout) { + const t = e["mediaOptionType"], + o = null !== (n = null === (n = n.getErrorInfoByType(t)) || void 0 === n ? void 0 : n.timeouts.load) && void 0 !== n ? n : 0; + !i && r <= o && (s = Cm.DoNothing, a = 0) + } + return { + errorAction: s, + errorActionFlags: a + } + }(e, a, t, i, r), a = Jm(a, t, s, o, l, r, n, e.isTimeout), a + }(e, r, n, s, a) + } + return rg(e, t, i, c, s, a, u, d, h) + }(e, t, _c(e, r), s, n, a, o, l))))) + } + Xm.kAllowFilters = Xm.makeFilters(), (dl = Cm = Cm || {})[dl.DoNothing = 0] = "DoNothing", dl[dl.SendEndCallback = 1] = "SendEndCallback", dl[dl.SendAlternateToPenaltyBox = 2] = "SendAlternateToPenaltyBox", dl[dl.RemoveAlternatePermanently = 3] = "RemoveAlternatePermanently", dl[dl.InsertDiscontinuity = 4] = "InsertDiscontinuity", dl[dl.RetryRequest = 5] = "RetryRequest", (dl = Dm = Dm || {})[dl.MoveAllAlternatesMatchingHost = 1] = "MoveAllAlternatesMatchingHost", dl[dl.MoveAllAlternatesMatchingHDCP = 2] = "MoveAllAlternatesMatchingHDCP", dl[dl.SwitchToSDR = 4] = "SwitchToSDR"; + class dg extends kl { + constructor(e) { + super(e) + } + get currentConfig() { + var e; + return null === (e = this.getActive()) || void 0 === e ? void 0 : e.config + } + get extendMaxTTFB() { + var e; + return null === (e = this.getActive()) || void 0 === e ? void 0 : e.extendMaxTTFB + } + get config$() { + return this.selectActive(e => null == e ? void 0 : e.config) + } + get userSeek$() { + return this.selectActive(e => null == e ? void 0 : e.userSeek) + } + } + A(); + class ug extends fl { + constructor() { + super({}, { + name: "hls-store", + producerFn: su + }) + } + } + class cg { + constructor(e) { + this.store = e + } + getQuery() { + return new dg(this.store) + } + setHlsEntity(e) { + const t = e.id; + Do(`hls.set.entity ${t}`), al(() => { + this.store.add(de(e)), this.store.setActive(t) + }) + } + removeEntity(e) { + Do(`hls.remove ${e}`), this.store.remove(e) + } + setStartTime(t) { + this.store.updateActive(e => { + e.config.startPosition = t + }) + } + setUserSeek(t) { + this.store.updateActive(e => { + e.userSeek = t + }) + } + setExtendMaxTTFB(t) { + this.store.updateActive(e => { + e.extendMaxTTFB = t + }) + } + } + let hg, pg; + + function fg() { + return hg = hg || new cg(new ug), hg + } + + function mg() { + return fg().getQuery().currentConfig + } + + function gg(r, n) { + if ("VOD" !== r.type && "VOD" !== n.type && r.iframesOnly === n.iframesOnly) { + const s = r.mediaOptionId === n.mediaOptionId, + a = Math.max(r.startSN, n.startSN) - n.startSN, + o = Math.min(r.endSN, n.endSN) - n.startSN, + l = n.startSN - r.startSN, + d = r.fragments, + u = n.fragments; + let t = 0; + for (let e = a; e <= o; ++e) + if (d[l + e] && u[e]) { + t = d[l + e].discoSeqNum - u[e].discoSeqNum; + break + } const c = {}; + let i = null; + for (let e = 0; e < u.length; e++) { + const h = d[l + e], + a = u[e]; + if (t) { + const r = a.discoSeqNum + t; + n.initSegments[a.discoSeqNum] && (n.initSegments[a.discoSeqNum].discoSeqNum = r, c[r] = n.initSegments[a.discoSeqNum], delete n.initSegments[a.discoSeqNum]), a.discoSeqNum = r + } + s && a.mediaSeqNum === (null == h ? void 0 : h.mediaSeqNum) && null != h.startPts && (a.start = h.start, a.duration = h.duration, a.startDtsTs = h.startDtsTs, a.endDtsTs = h.endDtsTs, a.startPts = h.startPts, a.endPts = h.endPts, i = a) + } + if (Object.keys(c).length && (n.initSegments = c), i) Bm(n, i, i.start, void 0, !1, !0); + else if (0 <= l && l < d.length) { + const r = d[l].start, + h = n.fragments; + for (let e = 0; e < u.length; e++) h[e].start += r + } + n.ptsKnown = n.ptsKnown || s && !0 === r.ptsKnown && r.endSN >= n.startSN, $m(n) + } + } + + function yg(e, t, i) { + let r = t.targetduration; + return ne(i.liveSyncDuration) ? r = i.liveSyncDuration : ne(i.liveSyncDurationCount) && (r = i.liveSyncDurationCount * t.targetduration), e + Math.max(0, t.totalduration - r) + } + + function vg(e, t, i, r, n) { + if (!t.ptsKnown) return 0; + var s = t.targetduration, + a = t.fragments[0].start, + r = n.canContinuePlaybackWithoutGap(t, i, { + avgPlaylistLoadTimeMs: 0, + avgPlaylistParseTimeMs: 0 + }, r); + let o = Math.max(0, e - s); + return e < a && !r && (o = a), o + } + + function Sg(e) { + return 1e3 * (e.averagetargetduration || e.targetduration) + } + + function bg(e, t) { + var i = Sg(e), + t = performance.now() - t; + return e.liveOrEvent && i <= t + } + + function Tg(t, i, r = NaN) { + var n = t.fragments; + for (let e = r > t.startSN ? r - t.startSN : 0; e < n.length; ++e) { + const t = n[e], + r = t["start"]; + if (i(t)) return { + timelineOffset: r, + mediaFragment: t + } + } + return null + } + + function Eg(e, t, i) { + var r = 0 < i.duration, + n = i.start + i.duration, + n = null == t || t < n || t - n < 1 && i.isLastFragment, + e = e.every(e => !$p(e.frag, i)); + return r && e && n + } + + function Ig(e, t, i, r, n) { + i = null !== (i = Tg(r, Eg.bind(null, n, void 0), i)) && void 0 !== i ? i : null; + let s = null; + i && (s = null !== (e = Tg(r, Eg.bind(null, n, e), i.mediaFragment.mediaSeqNum)) && void 0 !== e ? e : null, s || !r.liveOrEvent || r.ptsKnown || (s = i)); + let a = NaN; + null != s && ne(t) && s.mediaFragment.discoSeqNum !== t && (a = s.mediaFragment.discoSeqNum, s = null); + t = n.some(e => 0 < e.frag.framesWithoutIDR); + return s && t && n[n.length - 1].frag.mediaOptionId !== r.mediaOptionId && (s = null !== (r = Tg(r, e => e.mediaSeqNum === s.mediaFragment.mediaSeqNum - 1)) && void 0 !== r ? r : s), { + foundFrag: s, + nextDisco: a + } + } + + function wg(e) { + e = e.fragments[e.fragments.length - 1]; + return e ? e.start + e.duration : 0 + } + class Ag extends kl { + constructor(e, t) { + super(e), this.mediaOption = t + } + get itemId() { + return this.mediaOption.itemId + } + get mediaOptionId() { + return this.mediaOption.mediaOptionId + } + get initSegmentEntities() { + var e; + return null === (e = this.mediaOptionDetailsEntity) || void 0 === e ? void 0 : e.initSegmentCacheEntities + } + get mediaLibraryEntity() { + return this.getEntity(this.itemId) + } + get mediaOptionDetailsEntityRecord() { + var e; + return null === (e = this.mediaLibraryEntity) || void 0 === e ? void 0 : e.mediaOptionDetailsEntityRecord + } + get mediaOptionDetailsEntity() { + return this.mediaOptionDetailsEntityRecord ? this.mediaOptionDetailsEntityRecord[this.mediaOptionId] : null + } + get mediaOptionDetails() { + var e; + return null === (e = this.mediaOptionDetailsEntity) || void 0 === e ? void 0 : e.mediaOptionDetails + } + get playlistDuration() { + var e; + return null === (e = this.mediaOptionDetailsEntity) || void 0 === e ? void 0 : e.playlistDuration + } + get mediaOptionDetailsEntity$() { + const { + itemId: e, + mediaOptionId: t + } = this; + return this.selectEntity(e, e => { + if (null != e && e.mediaOptionDetailsEntityRecord) return null == e ? void 0 : e.mediaOptionDetailsEntityRecord[t] + }) + } + get mediaOptionDetails$() { + return this.selectEntity(this.itemId, e => { + return null === (e = null == e ? void 0 : e.mediaOptionDetailsEntityRecord[this.mediaOptionId]) || void 0 === e ? void 0 : e.mediaOptionDetails + }).pipe(Kp()) + } + get playlistDuration$() { + return this.mediaOptionDetailsEntity$.pipe(hr(e => null == e ? void 0 : e.playlistDuration), Kp(), Is()) + } + get live$() { + return this.mediaOptionDetails$.pipe(hr(e => null == e ? void 0 : e.liveOrEvent), Is()) + } + } + class Og extends fl { + constructor() { + super({}, { + name: "media-library-store", + idKey: "itemId", + producerFn: su + }) + } + } + class kg { + constructor(e) { + this.store = e + } + getQuery() { + return new kl(this.store) + } + getQueryForOption(e) { + return new Ag(this.store, e) + } + createMediaLibraryEntity(e) { + var t = { + itemId: e, + mediaOptionDetailsEntityRecord: {} + }; + Do(`library.entity.create: ${e}`), this.store.add(t) + } + setDetailsLoading(e) { + const { + itemId: t, + mediaOptionId: i + } = e; + Do(`library.details.loading: ${i}`), this.store.update(t, ({ + mediaOptionDetailsEntityRecord: e + }) => { + e[i] || (e[i] = { + initSegmentCacheEntities: {}, + unchangedCount: 0 + }), e[i].detailsLoading = !0 + }) + } + archiveMediaOptionDetails(i, r, n) { + const { + itemId: e, + mediaOptionId: s + } = i, a = performance.now(), o = wg(i); + Do(`library.details.loaded: ${s}`), this.store.update(e, e => { + const t = e.mediaOptionDetailsEntityRecord[s]; + t.detailsLoading = !1, t.mediaOptionDetails = i, t.lastUpdateMillis = a, n ? t.unchangedCount = 0 : ++t.unchangedCount, t.playlistDuration = o, t.stats = r, e.liveOrEvent = i.liveOrEvent + }) + } + setInitSegmentLoading(e) { + const { + itemId: t, + mediaOptionId: i, + discoSeqNum: r + } = e; + Do(`library.initsegs.loading: ${i}/${r}`), this.store.update(t, e => { + e.mediaOptionDetailsEntityRecord[i].initSegLoading = r + }) + } + archiveInitSegmentEntity(i, r) { + const { + itemId: e, + mediaOptionId: n, + discoSeqNum: s + } = i; + Do(`library.initseg.loaded: ${n}/${s}`), this.store.update(e, ({ + mediaOptionDetailsEntityRecord: e + }) => { + const t = e[n]; + t.initSegmentCacheEntities[s] = [i, r], t.initSegLoading = null + }) + } + updatePTSDTS(e, i, t, r) { + var n = null === (n = this.getQueryForOption({ + itemId: e, + mediaOptionId: i + })) || void 0 === n ? void 0 : n.mediaOptionDetails; + if (n && Fm(n, r)) { + const s = r["startDtsTs"], + { + variantDTS: a, + timelineOffset: o, + iframeMode: l + } = t, + d = b(s, a) + o, + u = Object.assign({}, n); + Bm(u, r, d, l, !0), $m(u); + const c = wg(u); + this.store.update(e, ({ + mediaOptionDetailsEntityRecord: e + }) => { + const t = e[i]; + null != t && t.mediaOptionDetails && (t.mediaOptionDetails = u, t.playlistDuration = c) + }) + } + } + remove(e) { + this.store.remove(e) + } + clear() { + this.store.remove() + } + } + + function Cg() { + return pg = pg || new kg(new Og), pg + } + const Dg = e => Cg().getQueryForOption(e), + Mg = (d, e, t = !1, i = !1) => { + if (null == e || !_u(e)) return $i(null); + const r = e["itemId"], + n = d["mediaLibraryService"], + s = n.getQueryForOption(e); + s.hasEntity(r) || n.createMediaLibraryEntity(r); + var a = s.mediaOptionDetailsEntity, + o = s.mediaOptionDetails; + return null == o || i || "VOD" !== o.type && (!o.liveOrEvent || bg(o, a.lastUpdateMillis)) ? (n.setDetailsLoading(e), function(u, e) { + var t; + const { + logger: i, + config: c, + rootPlaylistQuery: h, + rootPlaylistService: p, + statsService: r, + mediaLibraryService: f, + mediaSink: n + } = d, m = n.mediaQuery, s = c.playlistLoadPolicy, a = c.keySystemPreference, o = h.masterVariableList, l = null === (t = null === (t = fg()) || void 0 === t ? void 0 : t.getQuery()) || void 0 === t ? void 0 : t.extendMaxTTFB; + return _m(u, h.itemStartOffset, c, s, i, a, r, o, l).pipe(hr(e => { + const t = f.getQueryForOption(u), + i = t.mediaOptionDetails, + r = e["mediaOptionDetails"], + n = e["stats"]; + let s = !0; + if (r.liveOrEvent) { + const d = r["mediaOptionType"]; + e = r, s = null == i || e.endSN !== i.endSN || e.liveOrEvent !== i.liveOrEvent, i && gg(i, r); + const u = h.lastLoadedMediaOptionByType(d), + t = u ? null === (l = f.getQueryForOption(u)) || void 0 === l ? void 0 : l.mediaOptionDetails : null; + !r.ptsKnown && t && t.mediaOptionId !== (null == i ? void 0 : i.mediaOptionId) && gg(t, r) + } + r && al(() => { + f.archiveMediaOptionDetails(r, n, s), p.setLastLoadedMediaOptionByType(h.itemId, u.mediaOptionType, u) + }); + const a = !s && t.mediaOptionDetailsEntity.unchangedCount >= c.liveMaxUnchangedPlaylistRefresh, + o = function(e, t, i, r) { + t = vg(r.currentTime, e, t, i, r), i = e.fragments[e.fragments.length - 1], r = (null == i ? void 0 : i.start) + (null == i ? void 0 : i.duration); + return { + expired: null != i && e.liveOrEvent && e.ptsKnown && r < t, + windowEnd: r, + minPosition: t + } + }(r, n.tload, c.maxBufferHole, m); + if (a || o.expired) { + var l = a ? $.LivePlaylistUpdateError : { + text: `Live window too far in the past end:${o.windowEnd.toFixed(3)} minPosition:${o.minPosition}`, + code: 0 + }; + throw new uc(!1, l.text, l.code, l, !1, u.mediaOptionType, u.mediaOptionId, u.url) + } + return r + }), tc("getMediaOptionDetailsCommon.emit.loaded"), lg(u.itemId, u.mediaOptionType, Lc(u, s), c.maxNumAddLevelToPenaltyBox, e, h, p, r)).pipe(Ds(1)) + }(e, t)) : $i(o).pipe(tc("retrieveMediaOptionDetails.emit.cached")).pipe(Ds(1)) + }; + const xg = (t, i) => { + if (!i) return $i(null); + const r = t["logger"], + { + mediaLibraryService: e, + mediaParser: n + } = t, + s = e.getQueryForOption(i), + { + mediaOption: a, + mediaOptionDetailsEntityRecord: o, + mediaOptionDetails: l + } = s, + d = a["mediaOptionId"]; + if (null == o || !o[d]) throw new Error("retrieveInitSegmentCacheEntity no details entity"); + if (!l) throw new Error("retrieveInitSegmentCacheEntity no details"); + var u = o[d]["initSegmentCacheEntities"], + c = l["initSegments"], + h = i["discoSeqNum"]; + if (u[h]) { + const [t, r] = u[h]; + let e = t; + return r && (e = n.willBeTrackSwitch(i) ? t : r), $i(e) + } + const p = c[h]; + return p ? (e.setInitSegmentLoading(p), r.child({ + name: "timing" + }), Rg(t, p, !1, !1).pipe(Za(() => {}), ji(tr), La(e => Pg(e, p, t)), Za(() => {}))) : $i(null) + }; + + function Pg(e, n, t) { + const { + logger: s, + mediaSink: i, + rootPlaylistService: r, + rootPlaylistQuery: a, + mediaParser: o, + mediaLibraryService: l, + gaplessInstance: d + } = t, u = i["mediaQuery"], c = l.getQueryForOption(n), { + mediaOption: h, + mediaOptionDetails: p + } = c, { + itemId: f, + mediaOptionId: m + } = h, { + keyTagInfo: g, + discoSeqNum: y, + mediaOptionType: v + } = n, S = u.seeking, b = p.liveOrEvent, T = v === gu.Variant && p.ptsKnown; + let E, I; + n.isInitSegment ? I = new Uint8Array(e) : E = new Uint8Array(e); + var e = { + segment: E, + initSegment: I, + frag: n, + ptsKnown: T, + seeking: S, + live: b, + totalDuration: p.totalduration + }; + return o.parseInitSegment(e, null !== (e = null === navigator || void 0 === navigator ? void 0 : navigator.vendor) && void 0 !== e ? e : "").pipe(hr(e => { + var { + track: t, + moovData: i, + mimeType: r + } = e, e = t["initSegment"]; + d.inGaplessMode && be.isVideoCodec(t.codec) && (s.warn(`Video codec discovered in gapless mode codec:${t.codec}`), d.dequeueSource("InvalidFormat")); + r = { + itemId: f, + mediaOptionId: m, + discoSeqNum: y, + initParsedData: i, + data: e, + mimeType: r, + keyTagInfo: g, + fragment: n + }; + return l.archiveInitSegmentEntity(r), r + }), sg(r, a, v, m)) + } + + function Rg(n, s, i, r) { + var e; + const { + rootPlaylistQuery: t, + rootPlaylistService: a, + config: o, + rtcService: l, + statsService: d + } = n, { + itemId: u, + mediaOptionType: c + } = s, h = o.fragLoadPolicy, p = ne(s.mediaSeqNum); + let f; + p && (f = { + getData: !1, + cb: (e, t, i, r) => (a.updateInflightFrag(u, s.mediaOptionType, s, "loading", i), !1) + }); + let m = !1; + r && null === l.serverInfoInstance && (m = !0); + const g = nm(s, o, h, f, m, null === (e = null === (e = fg()) || void 0 === e ? void 0 : e.getQuery()) || void 0 === e ? void 0 : e.extendMaxTTFB).pipe(Za(([, , e, t]) => { + i && d.setBandwidthSample(Object.assign(Object.assign({}, e), { + mediaOptionType: s.mediaOptionType + })), r && m && (l.serverInfoInstance = t), p && a.updateInflightFrag(u, s.mediaOptionType, s, "loaded", e) + }), Za(([e, , t]) => { + r && l.handleFragLoaded(e, t) + }), lg(u, c, Lc(s, h), o.maxNumAddLevelToPenaltyBox, !1, t, a, d)); + return "AES-128" === (null === (e = s.keyTagInfo) || void 0 === e ? void 0 : e.method) ? en([_g(n, s.keyTagInfo, { + itemId: s.itemId, + mediaOptionId: s.mediaOptionId + }), g]).pipe(La(([e, t]) => { + const [i, r] = t; + return i.keyTagInfo.key = e.key, Nm(i, r, o, n.logger, n.rpcClients.crypto) + })) : g.pipe(hr(e => e[1])) + } + const Lg = (i, r, e) => { + const { + rootPlaylistService: n, + rootPlaylistQuery: s + } = i, { + timelineOffset: a, + mediaFragment: o + } = e.foundFrag, { + itemId: l, + discoSeqNum: d + } = o; + return xg(i, o).pipe(La(t => (n.updateInflightFrag(l, o.mediaOptionType, o, "loading", null), Rg(i, o, !0, !0).pipe(La(e => { + return n.updateInflightFrag(l, r, o, "parsing", null), + function(e, t, i, y, v, S) { + const r = new Uint8Array(e), + { + legibleSystemAdapter: n, + rootPlaylistService: s, + mediaSink: a, + mediaParser: o, + rootPlaylistQuery: l, + mediaLibraryService: b + } = S, + d = a["mediaQuery"], + u = b.getQueryForOption(y), + { + mediaOption: c, + mediaOptionDetails: h + } = u, + p = h["initSegments"], + { + itemId: T, + mediaOptionId: E + } = c, + { + discoSeqNum: I, + mediaSeqNum: w, + mediaOptionType: f, + isLastFragment: A + } = y, + m = d.seeking, + g = h.liveOrEvent, + O = p[I], + k = f === gu.Variant && h.ptsKnown, + C = { + segment: r, + frag: y, + seeking: m, + live: g, + ptsKnown: k, + totalDuration: h.totalduration, + defaultInitPTS: t, + iframeMediaStart: Up(y) ? y.iframeMediaStart : void 0, + iframeDuration: Up(y) ? y.iframeMediaDuration : void 0, + iframeOriginalStart: Up(y) ? y.iframeOriginalStart : void 0 + }; + let D; + if (null != i && (null === (t = y.keyTagInfo) || void 0 === t ? void 0 : t.uri) === (null === (t = i.keyTagInfo) || void 0 === t ? void 0 : t.uri)) D = $i(i); + else if (null != i) { + const M = Object.assign(Object.assign({}, i.fragment), { + keyTagInfo: y.keyTagInfo + }); + D = Pg(O ? i.data : e, M, S) + } else D = Pg(e, y, S); + return D.pipe(La(m => { + const g = performance.now(); + if (null != m) { + const g = m["data"], + e = new Uint8Array(g); + C.initSegment = e + } + return y.mediaOptionType === gu.Variant && (null == n || n.setupForFrag(y)), o.parseSegment(C, "").pipe(hr(e => { + var t = performance.now(), + { + startPTS: i, + startDTS: r, + endPTS: n, + endDTS: s, + firstKeyframePts: a, + framesWithoutIDR: o, + dropped: l, + data1: d, + data2: u, + captionData: c, + id3Samples: h, + parsedInitSegment: e + } = e, + t = { + durationSec: n.baseTime / n.timescale - i.baseTime / i.timescale, + parseTimeMs: t - g + }; + S.statsService.setFragSample(t); + let p = Object.assign({}, m); + if (e) { + const { + track: g, + moovData: f, + mimeType: v + } = e, S = g["initSegment"]; + p = { + itemId: T, + mediaOptionId: E, + discoSeqNum: I, + initParsedData: f, + data: S, + mimeType: v, + keyTagInfo: y.keyTagInfo, + fragment: y + }, b.archiveInitSegmentEntity(m, p) + } + e = y.keyTagInfo; + return [p, { + itemId: T, + mediaOptionId: E, + mediaSeqNum: w, + discoSeqNum: I, + startDtsTs: r, + endDtsTs: s, + timelineOffset: v, + firstKeyframePts: a, + framesWithoutIDR: o, + dropped: l, + data1: d, + data2: u, + startPts: i, + endPts: n, + keyTagInfo: e, + isLastFragment: A, + iframe: null !== (e = y.iframe) && void 0 !== e && e, + duration: y.duration, + iframeMediaDuration: Up(y) ? y.iframeMediaDuration : void 0, + iframeOriginalStart: Up(y) ? y.iframeOriginalStart : void 0, + captionData: c, + id3Samples: h + }] + })) + }), sg(s, l, f, E)) + }(e, null === (e = s.getInitPTS(d)) || void 0 === e ? void 0 : e.offsetTimestamp, t, o, a, i) + }), Za(e => { + n.updateInflightFrag(l, r, o, "parsed", null) + }), tc(`retrieveMediaFragmentCacheEntity.${r}.emit`)))), Ds(1)) + }; + + function _g(e, t, i) { + const { + keySystemAdapter: r, + rootPlaylistQuery: n, + rootPlaylistService: s, + config: a + } = e; + return r.getKeyFromDecryptData(t, i).pipe((o = t.uri, l = Lc({ + url: t.uri + }, a.keyLoadPolicy), d = n, u = s, c = r.ksQuery, e => e.pipe(ll(() => { + ag(o, !1, d, u, c) + }), va(e => e.pipe(jr((e, t) => { + if (e instanceof mc || e instanceof gc) return og(e, t, _c(e, l), u, d, c); + throw e + })))))); + var o, l, d, u, c + } + class Ng { + constructor(e, t, i) { + this.hls = e, this.destroy$ = new Xt, this.iframeSwitchStart = 0, this.logger = t.child({ + name: "hls-player-events" + }), this.rtc = i, this.subscribeAndEmit() + } + destroy() { + this.destroy$.next() + } + subscribeAndEmit() { + var e = this.loaderQueryListener(new bc(Ec)), + t = this.hls.publicQueries$.pipe(La(([e, t]) => an(this.rootPlaylistQueryListener(e, t), this.mediaElementQueryListener(t, e)))); + an(e, t, this.activeItemListener(this.hls.itemQueue)).pipe(Vn(e => { + var t = e.message; + return this.logger.error(`Got error in HlsPlayerEvents ${t}`, e), Ii + }), Va(this.destroy$), Vs(() => {})).subscribe() + } + activeItemListener(e) { + return e.activeItemById$.pipe(Kp(), La(e => { + e = e.url; + return this.hls.trigger(P.MANIFEST_LOADING, { + url: e + }), Ii + })) + } + rootPlaylistQueryListener(t, e) { + var i = t.enabledMediaOptionByType$(gu.Variant).pipe(ln(e => !!e), La(e => { + var t; + return this.hls.trigger(P.LEVEL_SWITCHING, e), null === (t = this.rtc) || void 0 === t || t.handleLevelSwitching(e.url), Ii + })), + r = t.enabledMediaOptionByType$(gu.Variant).pipe(La(i => Dg(i).mediaOptionDetailsEntity$.pipe(ln(e => !0 === (null == e ? void 0 : e.detailsLoading)), Za(e => { + var t = { + url: le(null == i ? void 0 : i.url), + level: i.mediaOptionId, + type: Nu[i.mediaOptionType] + }; + return this.hls.trigger(P.LEVEL_LOADING, t), Ii + })))), + n = t.enabledMediaOptionByType$(gu.Variant).pipe(La(e => { + const t = Dg(e); + let i = 0; + return t.mediaOptionDetailsEntity$.pipe(Kp(), ln(e => { + var t = null !== e.stats && !1 === e.detailsLoading && e.lastUpdateMillis > i; + return i = null !== (e = e.lastUpdateMillis) && void 0 !== e ? e : 0, t + })) + }), La(e => { + var t = e.mediaOptionDetails, + i = e.stats, + r = { + mediaOptionId: t.mediaOptionId, + details: t, + playlistType: t.type, + stats: i + }; + if (null === (i = this.rtc) || void 0 === i || i.handleLevelLoaded(t, r.stats), this.hls.trigger(P.LEVEL_LOADED, r), 0 === e.unchangedCount) { + const e = { + level: 0, + details: t + }; + this.hls.trigger(P.LEVEL_UPDATED, e) + } + if (null != t && t.daterangeTags) { + const e = { + daterangeTags: t.daterangeTags + }; + this.hls.trigger(P.DATERANGE_UPDATED, e) + } + return Ii + })), + s = t.enableMediaOptionSwitchedForType$(gu.AltAudio).pipe(La(e => { + e = t.alternateMediaOptionById(gu.AltAudio, e.mediaOptionId); + return e && this.triggerAudioSwitch(e), Ii + })), + a = t.rootPlaylistEntity$.pipe(ln(e => null !== e.enabledMediaOptionKeys[gu.AltAudio].mediaOptionId), Ds(1), La(e => { + const t = e.enabledMediaOptionKeys[gu.AltAudio].mediaOptionId; + return t && (e = e.mediaOptionListTuple[gu.AltAudio].mediaOptions.find(e => e.mediaOptionId === t), this.triggerAudioSwitch(Object.assign(Object.assign({}, e), { + url: le(null == e ? void 0 : e.url) + }))), Ii + })); + return an(i, s, Gu(e.textTracksCreated$, e => e).pipe(La(() => t.enabledMediaOptionByType$(gu.Subtitle).pipe(La(e => { + e = t.alternateMediaOptionById(gu.Subtitle, e.mediaOptionId); + return e ? this.hls.trigger(P.SUBTITLE_TRACK_SWITCH, { + track: Object.assign({}, e), + hidden: !1 + }) : this.hls.trigger(P.SUBTITLE_TRACK_SWITCH, { + track: void 0, + hidden: !1 + }), Ii + })))), t.sessionData$.pipe(ln(e => null != e.complete), Ds(1), Za(e => {}), hr(e => { + this.hls.trigger(P.SESSION_DATA_COMPLETE, e) + })), t.getPreferredMediaOptionsByType$(gu.Variant).pipe(ka(1), hr(e => { + const t = e; + null === (e = this.rtc) || void 0 === e || e.handleLevelsChanged(t), t.forEach(e => {}), this.hls.trigger(P.LEVELS_CHANGED, { + requiresReset: !1, + levels: t + }) + })), t.getPreferredMediaOptionsByType$(gu.AltAudio).pipe(hr(e => { + this.hls.trigger(P.AUDIO_TRACKS_UPDATED, { + audioTracks: e + }) + })), Gu(e.textTracksCreated$, e => e).pipe(La(() => t.getPreferredMediaOptionsByType$(gu.Subtitle).pipe(Ds(1), hr(e => { + this.hls.trigger(P.SUBTITLE_TRACKS_UPDATED, { + subtitleTracks: e + }), this.hls.trigger(P.SUBTITLE_TRACKS_CREATED) + })))), n, r, a) + } + mediaElementQueryListener(s, e) { + return an(s.seekTo$.pipe(hr(e => { + var t; + e && ne(e.pos) ? (null === (t = this.rtc) || void 0 === t || t.handleSeek("SEEKING"), this.hls.trigger(P.SEEKING, { + seekToPos: e.pos + })) : null === e && (null === (e = this.rtc) || void 0 === e || e.handleSeek("SEEKED"), this.hls.trigger(P.SEEKED)) + })), s.desiredRate$.pipe(Ra(0), ha(), hr(e => { + var t = e[0], + i = e[1]; + Wp(i) ? 0 == this.iframeSwitchStart && (this.iframeSwitchStart = performance.now()) : this.iframeSwitchStart = 0, this.hls.trigger(P.DESIRED_RATE_CHANGED, { + oldRate: t, + newRate: i + }), null === (e = this.rtc) || void 0 === e || e.handleDesiredRateChanged(t, i) + })), s.sourceBufferEntityByType$(yu.AltAudio).pipe(ln(e => !!e), Is((e, t) => e.totalBytes === t.totalBytes), hr(e => { + this.hls.trigger(P.BUFFER_APPENDED) + })), s.sourceBufferEntityByType$(yu.Variant).pipe(ln(e => !!e), Is((e, t) => e.totalBytes === t.totalBytes), hr(e => { + var t; + null === (t = this.rtc) || void 0 === t || t.handleVariantBufferAppended(e.timestampOffset, e.totalBytes), this.hls.trigger(P.BUFFER_APPENDED) + })), s.stallInfo$.pipe(Kp(), bo(s.combinedBuffer$), hr(([e]) => { + var t; + null === (t = this.rtc) || void 0 === t || t.handleStalled(e, s.getCombinedBufferInfo(e.currentTime, 0).len), this.hls.trigger(P.STALLED, e) + })), Mr([$i(e), ed([s.timeupdate$, s.bufferedSegmentsByType$(yu.Variant)]).pipe(ao(1e3), hr(([t, e]) => null == e ? void 0 : e.find(e => e.startPTS <= t && e.endPTS > t)), ln(e => !!e), Ra(null), ha())]).pipe(La(([e, [t, i]]) => { + var r = null == i ? void 0 : i.frag, + t = null == t ? void 0 : t.frag; + if (r && !$p(t, r) && (this.hls.trigger(P.FRAG_CHANGED, i), this.hls.inGaplessMode && this.checkAndTriggerReadyForNext(s, i), !t || r.mediaOptionId !== t.mediaOptionId)) { + const s = e.mediaOptionListQueries[gu.Variant].mediaOptionFromId(r.mediaOptionId); + if (!s) return this.logger.warn("variantInfo is undefined in fragChangeMonitor"), Ii; + const n = t ? t.mediaOptionId : "", + i = r.mediaOptionId; + s.iframes && (performance.now(), this.iframeSwitchStart), null === (r = this.rtc) || void 0 === r || r.handleLevelSwitched({ + url: s.url, + mediaOptionId: s.mediaOptionId, + oldVariant: "" !== n ? n : void 0, + newVariant: i + }), this.hls.trigger(P.LEVEL_SWITCHED, s) + } + return Ii + })), s.isBufferedToEnd$(this.hls.config.maxBufferHole, !1).pipe(ln(e => !0 === e), ji(tr), hr(e => { + if (e && !this.hls.itemQueue.isPreloading() && this.hls.inGaplessMode) { + const i = s.getCombinedBufferInfo(s.currentTime, 0); + var t = 0; + i && (t = i.end, e = s.mediaElementDuration, 0 < t && e - s.currentTime < 10 && this.hls.trigger(P.READY_FOR_NEXT_ITEM, { + duration: t + })) + } + }))) + } + checkAndTriggerReadyForNext(e, t) { + var i, r; + t && t.frag && (i = e.currentTime, (r = e.getCombinedBufferInfo(i, 0)) && (i = r.end, r = e.mediaElementDuration, e = e.bufferMonitorInfo, e = Math.max(e.almostDryWaterLevelSeconds, e.lowWaterLevelSeconds / 2), (r - t.endPTS <= e || t.frag.isLastFragment) && this.hls.inGaplessMode && !this.hls.isPreloading && this.hls.trigger(P.READY_FOR_NEXT_ITEM, { + duration: i + }))) + } + loaderQueryListener(e) { + return an(e.unresolvedUriLoading$.pipe(hr(e => e.map(e => { + e = { + uri: e.uri, + responseType: e.responseType, + userAgent: e.userAgent + }; + this.hls.trigger(P.UNRESOLVED_URI_LOADING, e) + })))) + } + triggerAudioSwitch(e) { + e && this.hls.trigger(P.AUDIO_TRACK_SWITCHED, { + id: e.id + }) + } + triggerManifestLoaded(e) { + e = { + levels: e.rootMediaOptionsTuple[gu.Variant], + audioTracks: e.rootMediaOptionsTuple[gu.AltAudio], + subtitleTracks: e.rootMediaOptionsTuple[gu.Subtitle], + url: e.baseUrl, + audioMediaSelectionGroup: e.audioMediaSelectionGroup, + subtitleMediaSelectionGroup: e.subtitleMediaSelectionGroup, + stats: e.stats, + isMediaPlaylist: e.isMediaPlaylist + }; + this.hls.trigger(P.MANIFEST_LOADED, e) + } + triggerManifestParsed(e) { + var t = { + levels: e.mediaOptionListQueries[gu.Variant].filteredMediaOptionList, + firstLevel: 0, + audio: !1, + video: !0, + altAudio: !1, + audioTracks: e.mediaOptionListQueries[gu.AltAudio].filteredMediaOptionList, + audioMediaSelectionGroup: e.audioMediaSelectionGroup, + subtitleMediaSelectionGroup: e.subtitleMediaSelectionGroup, + stats: e.loadStats + }; + null === (e = this.rtc) || void 0 === e || e.handleManifestParsed(t), this.hls.trigger(P.MANIFEST_PARSED, t) + } + urlRedactedManifestLoaded(e) { + const t = Object.assign({}, e); + return t.url = le(t.url), t.levels = ue(t.levels), t.audioTracks = ce(t.audioTracks), t.subtitleTracks = ce(t.subtitleTracks), t + } + urlRedactedManifestParsed(e) { + const t = Object.assign({}, e); + return t.levels = ue(t.levels), t.audioTracks = ce(t.audioTracks), t + } + }(A = Mm = Mm || {}).LowBandwidth = "LowBandwidth", A.HighBandwidth = "HighBandwidth", A.PreferredListChanged = "PreferredListChanged", A.IframeModeChange = "IframeModeChange", A.None = ""; + const Fg = { + minValidBitrate: 2e6, + maxValidBitrate: 5e6, + maxPreferredBitrate: 3e6, + minValidHeight: 480, + maxValidHeight: 720 + }; + + function Bg(e, a, o, l, d, u) { + return e.reduce((e, t) => { + if (t.iframes) return e; + let i = e; + const r = (n = t.score, s = a && o && l && d && !Ug(t, a, o, l, d, u) ? mu.INVALID : mu.VALID, new Qp(s, n)); + var n, s; + return (!e || r.isGreaterThan(e.bestRank) || r.isEqualTo(e.bestRank) && t.bandwidth < e.selected.bandwidth) && (i = { + selected: t, + bestRank: r + }), i + }, null).selected + } + + function Ug(e, t, i, r, n, s) { + var { + targetDuration: a, + targetStartupMs: o + } = i, l = r["avgPlaylistLoadTimeMs"], d = n["avgParseTimeMs"], { + avgBufferCreateMs: i, + avgInitFragAppendMs: r, + avgDataFragAppendMs: n + } = s, { + avgBandwidth: s, + avgLatencyMs: t + } = t; + return e.bandwidth <= s && (e.avgBandwidth || e.bandwidth) * a / s * 1e3 + l + i + +(t + d + (r + n)) <= o + } + const $g = { + name: "abr" + }; + + function Vg(e, t) { + return ne(e) ? Math.min(e, t) : t + } + + function Kg(e) { + return ne(null == e ? void 0 : e.avgBandwidth) + } + + function qg(e, t) { + return e.getCurrentWaterLevelByType(yu.Variant, t) / (0 !== e.playbackRate ? Math.abs(e.playbackRate) : 1) + } + + function Hg(t, i, e, r) { + if (e) return t; + let n = -1; + if (t < 0) return n; + for (let e = t; e < i.length; ++e) { + const t = Qg(i[e], r); + if (t.altAudio && t.subtitle) { + n = e; + break + } + } + return n + } + + function jg(t, e, i, r, n, s, a) { + const o = i.preferredMediaOptions[gu.Variant].filter(e => e.iframes === t); + if (!o.length) return { + variantMediaOption: Lu.mediaOptionId, + holdOffDuration: 0, + lowestCandidate: null + }; + let l = 0; + const d = i.nextMinAutoOptionId; + if (d !== Lu.mediaOptionId) { + const t = o.findIndex(e => e.mediaOptionId === d); + 0 <= t && (l = t) + } + if (l = Hg(l, o, t, i), l < 0) return { + variantMediaOption: Lu.mediaOptionId, + holdOffDuration: 0, + lowestCandidate: null + }; + let u = o.length - 1; + const c = i.nextMaxAutoOptionId; + if (c !== Lu.mediaOptionId) { + const t = o.findIndex(e => e.mediaOptionId === c); + 0 <= t && (u = t) + } + var h = i.variantMediaOptionById(r.mediaOptionId), + p = r.mediaOptionDetails, + f = (null == h ? void 0 : h.iframes) !== t ? 0 : qg(n, e.maxBufferHole); + let m, g; + if (t) { + const t = e.desiredIframeFPS; + g = p ? p.targetduration / t : 0, g = Math.max(1 / t, g) + } else g = p ? p.targetduration : 0; + h = e.abrBandWidthUpFactor, p = e.abrBandWidthFactor; + return m = Wg(o, g, l, u, f, t, s.getCombinedEstimate(), s.bandwidthStatus, p, h, e, i, r, n, a), m.variantMediaOption === Lu.mediaOptionId && (m = Wg(o, g, l, u, f + Vg(g, e.maxStarvationDelay), t, s.getCombinedEstimate(), s.bandwidthStatus, p, h, e, i, r, n, a), m.variantMediaOption === Lu.mediaOptionId && 0 <= l && (m.variantMediaOption = o[l].mediaOptionId, m.alternates = t ? null : Qg(o[l], i))), m + } + + function Qg(e, t) { + var i = t.enabledMediaOptionKeys, + r = i[gu.AltAudio], + r = _u(r) ? t.mediaOptionListQueries[gu.AltAudio].getMatchingAlternate(r.mediaOptionId, e) : Lu, + i = i[gu.Subtitle]; + return { + altAudio: r, + subtitle: _u(i) ? t.mediaOptionListQueries[gu.Subtitle].getMatchingAlternate(i.mediaOptionId, e) : Lu + } + } + + function Wg(i, r, n, e, s, a, o, l, t, d, u, c, h, p, f) { + "bandwidth-history-controller" !== u.abrBandwidthEstimator && f.warn(`Unsupported configuration: ${u.abrBandwidthEstimator} for ABR bandwidth estimator`); + const m = l.bandwidthSampleCount, + g = c.abrStatus, + y = p.maxBufferSize, + v = u.minTargetDurations || 1, + S = c.mediaOptionListQueries[gu.Variant].mediaOptionListInfo.hasScore; + if (!i.length) return { + variantMediaOption: Lu.mediaOptionId, + holdOffDuration: -1, + lowestCandidate: null + }; + const b = c.enabledMediaOptionIdByType(gu.Variant), + T = c.variantMediaOptionById(b), + E = null != i.find(e => e.mediaOptionId === b), + I = T && T.iframes === a, + w = T && I ? T.score : void 0, + A = T && I ? T.frameRate : void 0, + O = T && I ? T.height : void 0, + k = I ? qg(p, u.maxBufferHole) : 0; + e = Math.max(0, Math.min(i.length - 1, e)), n = Math.max(0, Math.min(i.length - 1, n)); + var C = h.mediaOptionDetailsEntityRecord, + D = Gg(o.avgBandwidth, d, t, m); + let M; + for (let t = e; t >= n; t--) { + const n = i[t]; + let e = n.mediaOptionId; + const u = n.score, + h = C && C[e] ? C[e].mediaOptionDetails : void 0, + p = C && C[e] ? C[e].lastUpdateMillis : null, + f = h ? h.totalduration / h.fragments.length : r, + B = null != T && n.bitrate > T.bitrate, + m = null != T && n.bitrate < T.bitrate, + U = !(null != A && (B && n.frameRate < A || m && n.frameRate > A)), + $ = !(B && null != O && O > n.height), + V = !(m && (n.bitrate === T.bitrate - 1 || n.bitrate === T.bitrate + 1)), + K = !(S && B && null != w && (u < w || u === w && T && n.bitrate >= T.bitrate)), + q = n.iframes === a; + if (ne(f) && q && U && $ && V && K) { + var { + adjustedbw: x, + bitrate: P, + fetchDuration: R, + rejectLevelDueToPeakBW: L, + canFitMultipleSegments: _, + requireAlternates: N, + alternates: F + } = function(e, t, i, r, n, s, a, o, l, d, u, c) { + var h = n ? a.bwUp : a.bwDown, + n = e.bitrate, + a = t ? t.totalduration / t.fragments.length : o, + s = zg(e, t, o, h, ne(s.avgPlaylistLoadTimeMs) ? s.avgPlaylistLoadTimeMs : s.avgLatencyMs, d), + d = e.bandwidth, + i = n < h && h < d && i <= 2 * a, + l = r * ((d || n || 0) * ((null == t ? void 0 : t.targetduration) || a)) / 8 <= l; + let p = null; + u = !u; + return u && (p = Qg(e, c), p.altAudio && p.subtitle || (p = null)), { + adjustedbw: h, + bitrate: n, + fetchDuration: s, + rejectLevelDueToPeakBW: i, + canFitMultipleSegments: l, + requireAlternates: u, + alternates: p + } + }(n, h, k, v, B, o, D, r, y, p, a, c); + if (N && Boolean(F) && (M = e), P < x && _ && !L && (!N || Boolean(F)) && (a || !R || R < s)) { + if (L = x, N = g, x = (x = l).instantBw, (N.fragDownloadSlow || N.fragDownloadTooSlow || ne(x) && x < L) && E && I) + if (k <= 2 * f && B) e = b; + else if (B && d * l.instantBw < P) continue; + return { + variantMediaOption: e, + holdOffDuration: R, + alternates: F, + lowestCandidate: M + } + } + } + } + return { + variantMediaOption: Lu.mediaOptionId, + holdOffDuration: -1, + lowestCandidate: M + } + } + + function Gg(e, t, i, r) { + let n, s; + return 4 <= r ? (n = e * t, s = e * i) : n = s = e / 1.8, { + bwUp: n, + bwDown: s + } + } + + function zg(e, t, i, r, n, s) { + let a = e.bitrate * (t ? t.totalduration / t.fragments.length : i) / r; + return null == t || !t.liveOrEvent || t.ptsKnown && !Yg(t.totalduration, n, s) || (a *= 2), ne(n) && (!ne(s) || null != t && t.liveOrEvent) && (a += n / 1e3), a + } + + function Xg(t, e) { + let i = 1 / 0; + if (t === Lu.mediaOptionId) return i; + var r = e.find(e => e.mediaOptionId === t); + if (!r) return 1 / 0; + const n = r.iframes, + s = r.bitrate, + a = r.frameRate, + o = e.find(e => e.iframes === n && (void 0 === a || e.frameRate >= a) && e.bitrate > s); + return o && (i = o.bitrate), i + } + + function Yg(e, t, i) { + return t = ne(t) ? t : 0, !ne(i) || performance.now() - i + t > 1e3 * e + } + + function Jg(e, t) { + return (null == e ? void 0 : e.fragDownloadSlow) === (null == t ? void 0 : t.fragDownloadSlow) && e.fragDownloadTooSlow === (null == t ? void 0 : t.fragDownloadTooSlow) + } + + function Zg(e) { + return "loading" === (null == e ? void 0 : e.state) && ne(null === (e = e.bwSample) || void 0 === e ? void 0 : e.trequest) + } + const ey = 2e3; + + function ty(e, t, i, r) { + let { + fragDownloadSlow: n, + fragDownloadTooSlow: s + } = t; + var a = i.variantMediaOptionById(e.mediaOptionId).bitrate, + t = e.bwSample; + r = r.child($g); + i = t.total || Math.max(t.loaded, Math.round(e.duration * a / 8)), a = performance.now() - t.tfirst, i = t.loaded * e.duration * 1e3 / i; + return a >= ey && 1e3 <= a - i && (n || r.warn(`flow indicates low bandwidth, after time/duration behind real time: ${a}/${a-i}`), n = !0), a >= 1e3 * e.duration && (s || r.warn(`too much time spent downloading fragment, likely to switch down ${a} > ${1e3*e.duration}`), s = !0), { + fragDownloadSlow: n, + fragDownloadTooSlow: s + } + } + + function iy(e, t, i, r, n) { + var s; + const a = n.logger.child($g), + o = r.isIframeRate, + l = i.mediaOptionListQueries[gu.Variant].preferredMediaOptionList, + d = i.enabledMediaOptionKeys[gu.Variant]; + let u = e; + if (u !== Mm.None || l.some(e => e.mediaOptionId === d.mediaOptionId) || (u = Mm.PreferredListChanged), u !== Mm.None && !(o && u !== Mm.IframeModeChange && r.getBufferedSegmentsByType(yu.Variant).filter(e => e.frag.iframe).length < t.minFramesBeforeSwitchingLevel)) { + o && u === Mm.IframeModeChange && n.setEnabledVariantMediaOptionIdBeforeTrickplaySwitch(i.itemId, d.mediaOptionId); + const h = Zf(i.itemId), + p = Dg(d), + f = [Lu, Lu, Lu]; + if (!o && e === Mm.IframeModeChange) { + const e = function(e, t, i, r) { + const n = i.mediaOptionListQueries[gu.Variant].preferredMediaOptionList, + s = t.targetStartupMs, + a = { + avgPlaylistParseTimeMs: 0, + avgPlaylistLoadTimeMs: 0 + }, + o = r.getFragEstimate(), + l = { + avgBufferCreateMs: 0, + avgInitFragAppendMs: 0, + avgDataFragAppendMs: 0 + }, + d = o.maxDurationSec, + u = { + avgLatencyMs: 0, + avgBandwidth: r.getBandwidthEstimate().avgBandwidth + }, + c = { + targetDuration: d, + targetStartupMs: s, + metricsOverride: { + maxValidHeight: 0, + maxValidBitrate: 0, + maxPreferredBitrate: 0 + } + }, + h = n.filter(e => !e.iframes && (!e.width || !e.height || e.width * e.height <= 2488320)); + let p = e; + 0 < h.length && (p = h[0].mediaOptionId); + e = h.filter(e => Ug(e, u, c, a, o, l)); + return 0 < e.length && (p = e[e.length - 1].mediaOptionId), p + }(i.enabledVariantMediaOptionIdBeforeTrickplaySwitch, t, i, h); + n.setNextMaxAutoOptionId(i.itemId, e), n.setEnabledVariantMediaOptionIdBeforeTrickplaySwitch(i.itemId, void 0) + } + var c = function(e, t, i, r, n, s, a) { + let o = t.nextMaxAutoOptionId; + if (o === Lu.mediaOptionId || Kg(s.getBandwidthEstimate())) return l = e, d = t, u = r, e = n, r = s, s = a.child({ + name: "abr" + }), a = e.isIframeRate, d.enabledMediaOptionIdByType(gu.Variant), jg(a, l, d, u, e, r, s); + if (n.isIframeRate) return { + variantMediaOption: o, + holdOffDuration: 0, + lowestCandidate: null + }; { + const c = t.variantMediaOptionById(o), + h = t.enabledAlternateMediaOptionByType(gu.Subtitle), + n = t.enabledAlternateMediaOptionByType(gu.AltAudio), + p = null == n ? void 0 : n.persistentID, + f = null == h ? void 0 : h.persistentID, + m = !t.preferHDR, + g = i.getBestMediaOptionTupleFromVariantAndPersistentId(t, c, p, f, void 0, [], m, !1, !1); + return t.isValidMediaOptionTuple(g) ? (o = g[gu.Variant].mediaOptionId, { + variantMediaOption: o, + holdOffDuration: 0, + alternates: { + altAudio: g[gu.AltAudio], + subtitle: g[gu.Subtitle] + }, + lowestCandidate: null + }) : { + variantMediaOption: t.enabledMediaOptionKeys[gu.Variant].mediaOptionId, + holdOffDuration: 0, + lowestCandidate: null + } + } + var l, d, u + }(t, i, n, p, r, h, a); + if (o || e !== Mm.IframeModeChange || n.setNextMaxAutoOptionId(i.itemId, Lu.mediaOptionId), c.variantMediaOption !== d.mediaOptionId) { + f[gu.Variant] = { + itemId: i.itemId, + mediaOptionId: c.variantMediaOption + }; + for (const e of [gu.AltAudio, gu.Subtitle]) { + const t = i.enabledMediaOptionIdByType(e); + if (t !== Lu.mediaOptionId) { + const r = e === gu.AltAudio ? null === (s = c.alternates) || void 0 === s ? void 0 : s.altAudio : null === (s = c.alternates) || void 0 === s ? void 0 : s.subtitle; + f[e] = r || { + itemId: i.itemId, + mediaOptionId: t + } + } + } + return n.setEnabledMediaOptions(i.itemId, f), 1 + } + } + } + const ry = { + name: "iframes" + }; + (A = xm = xm || {})[A.DISABLED = 0] = "DISABLED", A[A.ERRORED = 1] = "ERRORED", A[A.SUCCESS = 2] = "SUCCESS"; + const ny = new fl({}, { + name: "item-queue", + producerFn: su, + idKey: "itemId", + resettable: !0 + }), + sy = new class extends kl {}(ny); + class ay { + constructor() { + this.firstItem = !0, this.playingEntity = null, this.loadingEntity = null + } + static createItem(e, t, i = NaN, r, n) { + const s = new Date, + a = `${s.getHours()}:${s.getMinutes()}:${s.getSeconds()}`; + Qe(); + var o = function(e) { + e = cu.parseURL(e).fragment.substr(1); + if (0 === e.length) return null; + const t = new URLSearchParams(e); + if (!t.has("t")) return null; + e = Number(t.get("t")); + return ne(e) ? e : null + }(t); + if (ne(o)) i = o; + else { + const e = mg(); + ne(null == e ? void 0 : e.startPosition) && (i = e.startPosition) + } + return { + itemId: `${e}_${a}`, + name: e, + url: t, + serviceName: n, + createTime: a, + initialSeekTime: i, + itemStartOffset: 0, + platformInfo: r, + config: {} + } + } + get activeItemById$() { + return sy.selectActiveId().pipe(hr(e => sy.getActive())) + } + get removedItems$() { + return sy.selectEntityAction(Eo.Remove).pipe(hr(e => e)) + } + get activeItem() { + return sy.getActive() + } + get queueItems$() { + return sy.selectAll().pipe(hr(e => null != e ? e : [])) + } + get isFirstItem() { + return this.firstItem + } + get playingItem() { + return this.playingEntity + } + get loadingItem() { + return this.loadingEntity + } + addQueueItem(e, t, i, r, n, s) { + sy.getCount(); + const a = ay.createItem(e, t, i, r, s); + null != this.playingEntity && (a.initialSeekTime = void 0), n && (a.itemStartOffset = n, this.firstItem = !1, this.playingEntity = this.activeItem, this.loadingEntity = a), Do(`queue.add.item: ${e}`), al(() => { + ny.add(a), ny.setActive(a.itemId) + }) + } + updatePlayingItemId() { + this.playingEntity = this.loadingEntity, this.loadingEntity = null, this.clearAllButActive() + } + resetLoadingItem() { + this.removeQueueItem(this.loadingEntity.itemId), this.loadingEntity = null, al(() => { + ny.setActive(this.playingEntity.itemId) + }) + } + isPreloading() { + return null !== this.playingEntity && null !== this.loadingEntity + } + setQueueItem(t, i, r, n, s) { + Do("queue.set.item"), this.loadingEntity = null, al(() => { + ny.reset(); + var e = ay.createItem(t, i, r, n, s); + ny.add(e), ny.setActive(e.itemId) + }), this.playingEntity = this.activeItem + } + removeQueueItem(e) { + ny.remove(e) + } + clearQueue() { + ny.reset() + } + clearAllButActive() { + var e; + const t = null === (e = this.activeItem) || void 0 === e ? void 0 : e.itemId; + al(() => { + sy.getAll().forEach(e => { + e.itemId !== t && ny.remove(e.itemId) + }) + }) + } + set earlyAudioSelection(t) { + ny.updateActive(e => { + e.earlySelection || (e.earlySelection = {}), e.earlySelection.audioPersistentId = t + }) + } + get earlyAudioSelection() { + var e; + return null === (e = this.activeItem.earlySelection) || void 0 === e ? void 0 : e.audioPersistentId + } + set earlySubtitleSelection(t) { + ny.updateActive(e => { + e.earlySelection || (e.earlySelection = {}), e.earlySelection.subtitlePersistentId = t + }) + } + get earlySubtitleSelection() { + var e; + return null === (e = this.activeItem.earlySelection) || void 0 === e ? void 0 : e.subtitlePersistentId + } + } + + function oy(e, t, i, r, n, s) { + return ng(e, 0, Jm({ + errorAction: Cm.RemoveAlternatePermanently, + errorActionFlags: 0 + }, !1, e.response.code, i, t, s, n), s, n, t, i).pipe(Vn(e => { + throw !1 === e.fatal && r.resetMediaSource(), e + })) + } + class ly { + constructor(e, t, i, r, n, s) { + this.logger = e, this._rootPlaylistService = t, this._rootQuery = i, this._mediaQuery = r, this._iframeMachine = n, this._anchorMSNs = [NaN, NaN], this._avDetails = [null, null], this.logger = e.child({ + name: "fpicker" + }), this._discoSeqNum = NaN, this.lookUpTolerance = Math.max(s.maxBufferHole, s.maxFragLookUpTolerance), this.firstAudioMustOverlapVideoStart = s.firstAudioMustOverlapVideoStart, this.lookUpToleranceAlt = s.firstAudioMustOverlapVideoStart ? 0 : s.maxFragLookUpTolerance + } + destroy() { + this._anchorMSNs = [NaN, NaN], this._avDetails = [null, null], this._rootQuery = null, this._mediaQuery = null, this._rootPlaylistService = null, this._iframeMachine = null + } + get discoSeqNum() { + return this._discoSeqNum + } + get _discoSeqNum() { + return this._rootQuery.discoSeqNum + } + set _discoSeqNum(e) { + this._rootPlaylistService.setDiscoSeqNum(this._rootQuery.itemId, e) + } + get anchorMSNs() { + return this._anchorMSNs + } + _resolvePosition(e, t, i) { + let r = e; + var t = this._avDetails[t]; + if ((null == t ? void 0 : t.mediaOptionId) !== (null == i ? void 0 : i.mediaOptionId) && i.liveOrEvent && !1 === i.ptsKnown && (!t || Math.max(t.startSN, i.startSN) > Math.min(t.endSN, i.endSN))) { + const e = 3 * i.targetduration, + n = (null === (t = i.fragments[0]) || void 0 === t ? void 0 : t.start) + i.totalduration; + r = Math.max(0, n - e) + } + return r + } + getDiscoSeqNumForTime(e, t) { + return this._mediaQuery.isIframeRate && e.iframesOnly ? Qu.discoSeqNumForTime(e.fragments, t) : (r = t, null == (e = null !== (e = Tg(e, e => { + var t = 0 < e.duration, + i = e.start + e.duration, + e = r < i || r - i < 1 && e.isLastFragment; + return t && e + })) && void 0 !== e ? e : null) ? void 0 : e.mediaFragment.discoSeqNum); + var r + } + _updateAnchorByPosition(e, t) { + let i = NaN; + const r = t[yu.Variant]; + let n = e; + if (r) { + const t = r.fragments; + if (n = this._resolvePosition(e, yu.Variant, r), i = this.getDiscoSeqNumForTime(r, n), !ne(i)) { + const r = t[0], + s = t[t.length - 1], + a = null == r ? void 0 : r.start, + o = (null == s ? void 0 : s.start) + (null == s ? void 0 : s.duration); + this.logger.warn(`${e.toFixed(3)} out of range [${null==a?void 0:a.toFixed(3)},${null==o?void 0:o.toFixed(3)}]`), n <= a ? i = r.discoSeqNum : n >= o ? i = s.discoSeqNum : this.logger.warn(`Unable to determine newCC. fragFirst: ${JSON.stringify(r)} fragLast: ${JSON.stringify(s)}`), ne(i) || this.logger.warn(`Unable to determine newCC. fragFirst: ${JSON.stringify(r)} fragLast: ${JSON.stringify(s)}`) + } + } else this.logger.warn("No variant details for anchoring"); + return this._updateAnchor(i, t), n + } + _updateAnchor(e, s) { + const a = e !== this._discoSeqNum; + a && (this._discoSeqNum = e), Bu.forEach(e => { + const t = this._avDetails[e], + i = s[e], + r = (null == t ? void 0 : t.mediaOptionId) !== (null == i ? void 0 : i.mediaOptionId); + if (a || r) this._updateAnchorForType($u(e), i); + else if (i) { + const { + mediaOptionId: s, + ptsKnown: a, + dateMediaTimePairs: t, + startSN: r, + endSN: n + } = i; + this._avDetails[e] = { + mediaOptionId: s, + ptsKnown: a, + dateMediaTimePairs: t, + startSN: r, + endSN: n + } + } + }) + } + getNextFragments(i, r, n) { + const { + position: s, + bufferInfoTuple: e, + switchContexts: a + } = i; + let t = e.map((e, t) => dy(s, r[t], a[t], null == e ? void 0 : e.buffered, t === gu.AltAudio ? this.lookUpToleranceAlt : this.lookUpTolerance)).reduce((e, t) => Math.min(t, e), Number.POSITIVE_INFINITY); + if (ne(i.discoSeqNum)) { + const s = r.reduce((e, t) => { + if (!t) return e; + t = Qu.getTimeRangeForCC(t.fragments, i.discoSeqNum, n); + return [Math.max(e[0], t[0]), Math.min(e[1], t[1])] + }, [Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY]); + t = Math.min(Math.max(t, s[0]), s[1]) + } + return i.position = this._updateAnchorByPosition(t, r), this._getNextFragmentsInternal(i, r) + } + _getNextFragmentsInternal(r, n) { + const s = [null, null]; + let a; + n.forEach((e, t) => { + var i; + this.firstAudioMustOverlapVideoStart && t === gu.AltAudio && this._mediaQuery.seeking && null !== (i = s[gu.Variant]) && void 0 !== i && i.foundFrag && (a = r.position, r.position = s[gu.Variant].foundFrag.mediaFragment.start), s[t] = this._getNextFragmentForType(r, n, t) + }); + var e = s[yu.Variant], + t = s[yu.AltAudio], + i = null === (o = null == e ? void 0 : e.foundFrag) || void 0 === o ? void 0 : o.mediaFragment, + o = null === (o = null == t ? void 0 : t.foundFrag) || void 0 === o ? void 0 : o.mediaFragment; + if (i && o && (o.start > i.start + i.duration ? (this.logger.warn("Audio too far ahead"), s[yu.AltAudio] = ly.noopResult) : i.start > o.start + o.duration && !this._mediaQuery.isIframeRate && (this.logger.warn("Video too far ahead"), s[yu.Variant] = ly.noopResult)), !isFinite(null == e ? void 0 : e.nextDisco) || null != t && !ne(t.nextDisco)) return s; { + const l = s[yu.Variant].nextDisco; + return this._updateAnchor(l, n), ne(a) && (r.position = a, a = NaN), this._getNextFragmentsInternal(r, n) + } + } + _getNextFragmentForType(e, t, i) { + var { + position: r, + bufferInfoTuple: n, + switchContexts: s + } = e, a = t[i], o = null !== (d = null === (c = n[i]) || void 0 === c ? void 0 : c.buffered) && void 0 !== d ? d : { + start: r, + end: r, + len: 0 + }, l = this._mediaQuery.getBufferedSegmentsByType(i), d = null !== (c = null === (e = s[i]) || void 0 === e ? void 0 : e.userInitiated) && void 0 !== c && c, u = dy(r, a, s[i], o, this.lookUpTolerance); + if (!a) return null; + var { + highWaterLevelSeconds: e, + lowWaterLevelSeconds: c + } = this._mediaQuery.bufferMonitorInfo, r = o.len; + if (!d && e <= r) return ly.noopResult; + var e = i === yu.Variant ? yu.AltAudio : yu.Variant, + n = null === (n = n[e]) || void 0 === n ? void 0 : n.buffered, + e = null !== (e = null === (e = s[e]) || void 0 === e ? void 0 : e.userInitiated) && void 0 !== e && e; + let h = !1; + i === yu.Variant && c <= r && 1 < this._mediaQuery.expectedSbCount && null != n && n.end < o.end && (e || n.end - n.start < c) && (h = !0); + let p, f = null, + m = NaN; + if (this._mediaQuery.isIframeRate && i === yu.Variant && a.iframesOnly) { + const g = function(e, t, i) { + e = i.nextFragment(a.fragments, (null == e ? void 0 : e.fragments) || [], t, u); + if (!e) return null; + var { + frag: t, + newMediaRootTime: e + } = e; + return { + foundFrag: { + timelineOffset: t.iframeMediaStart, + mediaFragment: t + }, + nextDisco: NaN, + newMediaRootTime: e + } + }(t[yu.AltAudio], this._mediaQuery.desiredRate, this._iframeMachine); + if (g) { + ({ + foundFrag: f, + nextDisco: m, + newMediaRootTime: p + } = g); + const i = f.mediaFragment; + i.discoSeqNum !== this._discoSeqNum && this._updateAnchor(i.discoSeqNum, t) + } + } else { + const g = this._anchorMSNs[i]; + ({ + foundFrag: f, + nextDisco: m, + newMediaRootTime: p + } = Ig(u, this._discoSeqNum, g, a, l)) + } + return h && this._rootQuery.getInitPTS(null == f ? void 0 : f.mediaFragment.discoSeqNum) ? ly.noopResult : { + foundFrag: f, + nextDisco: m, + newMediaRootTime: p + } + } + _updateAnchorForType(e, t) { + var i, r, n, s, a; + if (!t) return this._anchorMSNs[e] = NaN, void(this._avDetails[e] = null); + ne(this._discoSeqNum) ? (a = this._discoSeqNum, a = null !== (s = null == (n = (r = t.fragments, i = a, r.find(e => e.discoSeqNum === i))) ? void 0 : n.mediaSeqNum) && void 0 !== s ? s : t.startSN, this._anchorMSNs[e] = a, { + mediaOptionId: r, + ptsKnown: n, + dateMediaTimePairs: s, + startSN: a, + endSN: t + } = t, this._avDetails[e] = { + mediaOptionId: r, + ptsKnown: n, + dateMediaTimePairs: s, + startSN: a, + endSN: t + }) : this.logger.warn("Trying to anchor with non-finite discoSeqNum") + } + } + + function dy(e, t, i, r, n) { + r = null != r ? r : { + start: e, + end: e, + len: 0 + }; + i = null !== (i = null == i ? void 0 : i.userInitiated) && void 0 !== i && i, n = null != t && t.iframesOnly ? 0 : n; + return i || 0 === r.len ? e : r.end + n + } + ly.noopResult = { + foundFrag: null, + nextDisco: NaN + }; + const uy = { + name: "avpipe" + }; + + function cy(r) { + const { + config: o, + rootPlaylistService: l, + rootPlaylistQuery: d, + mediaSink: e, + gaplessInstance: t + } = r, u = e.mediaQuery, i = ed(Bu.map(e => d.enabledMediaOptionSwitchForType$(e).pipe(Za(e => {})))).pipe(La(e => { + if (!_u({ + itemId: d.itemId, + mediaOptionId: e[gu.Variant].toId + })) throw new V(!0, `No valid variant enabled id:${e[gu.Variant].toId}`, $.NoValidAlternates); + e = e.map(({ + fromId: e, + toId: t + }, i) => function(t, i, r, n, s) { + var a, o; + const { + rootPlaylistQuery: l, + rootPlaylistService: d, + mediaSink: u, + mediaParser: c, + config: h, + iframeMachine: p + } = t, f = u.mediaQuery; + if (!n || !s || n === s && (r === gu.AltAudio || !p.isStarted)) return Wu; + switch (r) { + case gu.Variant: { + n !== s && c.reset(gu.Variant); + const t = $u(r), + g = l.variantMediaOptionById(n), + y = l.variantMediaOptionById(s), + v = l.itemId; + if (null == y || null == g) return Wu; + var e = !y.iframes && p.isStarted; + if (g.iframes !== y.iframes || e) return u.toggleTrickPlaybackMode(y.iframes), e && (ne(u.mediaQuery.postFlushSeek) || (u.postFlushSeek = p.iframeClockTimeSeconds), p.stop()), u.pause(), (e ? u.flushAll(0, 1 / 0, !0) : u.flushData(t, 0, 1 / 0, !0)).pipe(Za(() => { + var e = u.mediaQuery.postFlushSeek; + ne(e) && d.setPendingSeek(v, e) + })); + if (!h.allowFastSwitchUp || y.iframes) return Wu; + var m = Dg(g).mediaOptionDetails; + if (null != m && null != y && g.bitrate < y.bitrate) { + const r = m.targetduration, + n = Dg(y), + s = n.mediaOptionDetails, + d = n.mediaOptionDetailsEntity.lastUpdateMillis, + c = f.getCurrentWaterLevelByType(t, h.maxBufferHole), + p = function(e, t, i, r, n, s, a, o) { + if (n.nextMaxAutoOptionId !== Lu.mediaOptionId && !Kg(s.getBandwidthEstimate())) return Number.POSITIVE_INFINITY; + a = Gg(s.getBandwidthEstimate().avgBandwidth, a.abrBandWidthUpFactor, a.abrBandWidthFactor, s.bandwidthStatus.bandwidthSampleCount), s = ne(s.getPlaylistEstimate().avgPlaylistLoadTimeMs) ? s.getPlaylistEstimate().avgPlaylistLoadTimeMs : s.getBandwidthEstimate().avgLatencyMs, a = t.bitrate > e.bitrate ? a.bwUp : a.bwDown; + return null == i || !i.liveOrEvent || i.ptsKnown && !Yg(i.totalduration, s, o) ? zg(t, i, r, a, s, o) : Number.POSITIVE_INFINITY + }(g, y, s, r, l.abrStatus, i, h, d) + h.maxStarvationDelay, + S = f.currentTime + p, + b = null === (o = null === (a = f.sourceBufferEntityByType(t)) || void 0 === a ? void 0 : a.bufferedSegments) || void 0 === o ? void 0 : o.find(e => e.startPTS >= S); + let e; + if (b) { + const t = b.endPTS - b.startPTS; + e = b.startPTS + Math.min(Math.max(t - h.maxFragLookUpTolerance, .5 * t), .75 * t) + } + if (ne(e) && c >= p) return u.flushData(t, e, 1 / 0) + } + } + break; + case gu.AltAudio: + e = l, m = s, o = "Nah" === (a = n) ? null : e.alternateMediaOptionById(gu.AltAudio, a), o = Boolean(o && o.url), m = "Nah" === a ? null : e.alternateMediaOptionById(gu.AltAudio, m), m = Boolean(m && m.url), o && !m && (d.setEnabledMediaOptionSwitchContextByType(l.itemId, gu.AltAudio, s, void 0), u.resetMediaSource(f.currentTime)), c.reset(gu.AltAudio) + } + return Wu + }(r, n, i, e, t)); + return Jr($i(!0), en(e).pipe(Zs(!1))) + }), tc("mediaOptionSwitch.audiovideo.out")), n = Zf(d.itemId), s = r.logger.child(uy), a = new ly(s, l, d, u, r.iframeMachine, o); + return ed([d.anchorTime$.pipe(tc("anchorTime.audiovideo.in")), i]).pipe(La(([i, e]) => e ? Ii : u.needData$(o.maxBufferHole, t.inGaplessMode).pipe(hr(e => { + var t = [d.enabledMediaOptionSwitchContexts[gu.Variant], d.enabledMediaOptionSwitchContexts[gu.AltAudio]]; + return u.getSourceBufferInfoAction(e, i, t, o.maxBufferHole) + }), _s(e => e ? fn($i(e).pipe(hy(r, a), my(r)), Gu(function(e) { + const { + mediaSink: t, + rootPlaylistQuery: i, + rootPlaylistService: r + } = e, n = t.mediaQuery, s = e.logger.child($g); + return an((a = i, o = s, ed([n.fellBelowLowWater$, a.getInFlightFragByType$(gu.Variant)]).pipe(La(e => { + var [, t] = e; + if (!Zg(t)) return Ii; + const i = performance.now() - t.bwSample.trequest, + r = ey - i, + n = 1e3 * t.duration - i, + s = [Wu]; + return 0 < r && s.push(bn(r)), 0 < n && s.push(bn(n)), an(...s).pipe(Zs(e)) + }), sa((e, [t, i]) => { + const r = Object.assign({}, e); + return t && (r.fragDownloadSlow = !0), ty(i, r, a, o) + }, { + fragDownloadSlow: !1, + fragDownloadTooSlow: !1 + }), Ra({ + fragDownloadSlow: !1, + fragDownloadTooSlow: !1 + }), Is(Jg))), function(r) { + const s = r.mediaSink.mediaQuery, + { + rootPlaylistQuery: e, + config: a + } = r; + return s.desiredRate$.pipe(La(t => 0 === t ? Ii : ed([e.getInFlightFragByType$(gu.Variant), e.mediaOptionListQueries[gu.Variant].preferredMediaOptionList$.pipe(hr(e => e.filter(Wm.bind(null, Wp(t)))))])), ao(100), La(e => { + const [t, i] = e; + if (!Zg(t) || i.findIndex(e => e.mediaOptionId === t.mediaOptionId) <= 0) return Ii; + var r = performance.now() - t.bwSample.trequest, + n = Vg(t.duration, a.maxStarvationDelay), + n = Math.min(1e3 * n, 500 * t.duration / s.playbackRate); + return bn(Math.max(0, n - r), 100).pipe(Zs(e)) + })).pipe(sa((e, [t, i]) => function(t, i, r, e) { + let { + fragDownloadSlow: n, + fragDownloadTooSlow: s + } = t; + const { + config: a, + rootPlaylistService: o, + rootPlaylistQuery: l, + mediaSink: d, + statsService: u, + mediaLibraryService: c + } = e, h = e.logger.child($g), p = d.mediaQuery; + if (p.paused) return t; + e = i.bwSample; + if (!ne(e.tfirst)) return t; + const f = performance.now(), + m = f - e.trequest, + g = Vg(i.duration, a.maxStarvationDelay), + y = gu.Variant, + v = i.mediaOptionId, + S = l.variantMediaOptionById(v), + b = c.getQueryForOption(S), + T = S.bitrate, + E = Math.max(1, 8e3 * e.loaded / m), + I = 8 * ((ne(e.total) ? e.total : Math.max(e.loaded, Math.round(i.duration * T / 8))) - e.loaded) / E, + w = qg(p, a.maxBufferHole); + let A; + if (ne(w) && 0 < w && !ne(null === (O = p.seekTo) || void 0 === O ? void 0 : O.pos)) A = w; + else { + const N = m / 1e3; + A = N < g ? g - N : g + } + var O = n; + ({ + fragDownloadSlow: n, + fragDownloadTooSlow: s + } = ty(i, t, l, h)); + t = 2 * ((null === (t = b.mediaOptionDetails) || void 0 === t ? void 0 : t.targetduration) || i.duration); + if (!(w <= t && (I >= A || n))) return fg().getQuery().extendMaxTTFB && fg().setExtendMaxTTFB(0), { + fragDownloadSlow: n, + fragDownloadTooSlow: s + }; + O || h.warn(`likely to stall ${ae({maxTimeToLoadSec:A,minSwitchDuration:t,stats:e,elapsedMs:m,remainingTimeSec:I,instantBw:E,bufferAheadSec:w,fragDownloadSlow:n})}`), n = !0, fg().getQuery().extendMaxTTFB || fg().setExtendMaxTTFB(6e5); + let k; + const C = i.itemId, + D = u.getQueryForItem(C), + M = D.getCombinedEstimate(), + x = Object.assign(Object.assign({}, M), { + avgBandwidth: E + }), + P = D.bandwidthStatus, + R = S.iframes, + L = I >= A && !R, + _ = Hg(0, r, R, l); + if (_ < 0) return { + fragDownloadSlow: n, + fragDownloadTooSlow: s + }; + t = Math.max(_, r.findIndex(e => e && e.mediaOptionId === S.mediaOptionId)); + if (L) { + let e = Wg(r, i.duration, _, t, A, R, x, P, 1, 1, a, l, b, p, h); + const F = Lu.mediaOptionId; + k = e.variantMediaOption !== F || (e = Wg(r, i.duration, _, t, I, R, x, P, 1, 1, a, l, b, p, h)).variantMediaOption !== F ? e.variantMediaOption : e.lowestCandidate + } else { + const N = Hg(0, r.slice(_, t).reverse(), R, l), + i = t - 1 - N; + (0 <= N || t === _) && (k = r[i].mediaOptionId) + } + if (null != k && k !== l.abrStatus.nextMaxAutoOptionId && o.setNextMaxAutoOptionId(C, k), L) throw h.warn(`loading too slow, abort fragment loading and switch to level ${k}`), u.setBandwidthSample(Object.assign(Object.assign({}, e), { + tfirst: e.tfirst || f, + tload: e.tload || f, + complete: !0, + mediaOptionType: y + })), s = !0, new fc({ + mediaOptionType: y, + mediaOptionId: v + }, k, $.FragmentAbortError); + return { + fragDownloadSlow: n, + fragDownloadTooSlow: s + } + }(e, t, i, r), { + fragDownloadSlow: !1, + fragDownloadTooSlow: !1 + }), Ra({ + fragDownloadSlow: !1, + fragDownloadTooSlow: !1 + }), Is(Jg)) + }(e)).pipe(Ra({ + fragDownloadSlow: !1, + fragDownloadTooSlow: !1 + }), sa((e, t) => ({ + fragDownloadSlow: e.fragDownloadSlow || t.fragDownloadSlow, + fragDownloadTooSlow: e.fragDownloadTooSlow || t.fragDownloadTooSlow + })), Is(Jg), hr(e => (r.setFragLoadSlow(i.itemId, e), !1)), Vn(e => { + if (e instanceof fc) { + const e = { + fragDownloadSlow: !0, + fragDownloadTooSlow: !0 + }; + return r.setFragLoadSlow(i.itemId, e), $i(!0) + } + return Vi(e) + })); + var a, o + }(r), e => e)).pipe(Ds(1), Vs(() => { + Bu.forEach(e => { + l.updateInflightFrag(d.itemId, e, null, null, null) + }) + })) : Ii))), hr(() => { + if (!d.getEntity(d.itemId).manualMode) { + let e = Mm.None; + var i, r, n, s; + i = Af(), r = u, n = o, s = null == r ? void 0 : r.clientWidth, a = null == r ? void 0 : r.clientHeight, r = "object" == typeof window && window.devicePixelRatio ? window.devicePixelRatio : 1, a = s && a ? { + width: s * r, + height: a * r + } : void 0, r = (r = (null === (r = i.getQuery()) || void 0 === r ? void 0 : r.viewportInfo) || {}) && a && (r.width !== a.width || r.height !== a.height), n.useViewportSizeForLevelCap && r && (i.updateViewportInfo(a), 1) && (e = Mm.PreferredListChanged); + let t = !1; + var a = d.enabledVariantMediaOption; + ! function(e, t) { + const i = l.logger.child($g), + r = e.abrStatus, + n = r.fragDownloadSlow || r.fragDownloadTooSlow, + s = ne(null === (t = t.seekTo) || void 0 === t ? void 0 : t.pos); + return n && !r.fragDownloadTooSlow && s ? (i.warn("could be ignoring low bandwidth due to seek"), 0) : n + }(d, u) ? u.playbackStarted && function(e, t) { + const i = Zf(t.itemId), + r = i.getBandwidthEstimate(), + n = t.abrStatus; + if (Kg(r)) { + var t = (null === (t = i.bandwidthStatus) || void 0 === t ? void 0 : t.bandwidthSampleCount) || 0, + t = Gg(r.avgBandwidth, e.abrBandWidthUpFactor, e.abrBandWidthFactor, t)["bwUp"]; + return t > n.highBWTrigger + } + }(o, d) && (e = Mm.HighBandwidth, l.setNextMinAutoOptionId(a.itemId, a.mediaOptionId)): (e = Mm.LowBandwidth, d.nextMaxAutoOptionId === Lu.mediaOptionId && (l.setNextMaxAutoOptionId(a.itemId, a.mediaOptionId), t = !0)), iy(e, o, d, u, l), t ? l.setNextMaxAutoOptionId(a.itemId, Lu.mediaOptionId) : e === Mm.HighBandwidth && l.setNextMinAutoOptionId(a.itemId, Lu.mediaOptionId) + } + }), Vs(() => {})) + } + const hy = (r, n) => e => { + const { + rootPlaylistQuery: t, + mediaSink: a + } = r, i = r.logger.child(uy); + return e.pipe(ji(tr), bo(t.enabledMediaOptionKeys$), La(([s, e]) => En(py(s, gu.Variant, r, e).pipe(Za(e => { + var e = e.detailsEntity; + if (!e.mediaOptionDetails.liveOrEvent || e.mediaOptionDetails.ptsKnown) { + const t = e.playlistDuration, + i = (null === (e = s.bufferInfoTuple[0]) || void 0 === e ? void 0 : e.buffered.end) || 0, + r = (null === (e = s.bufferInfoTuple[1]) || void 0 === e ? void 0 : e.buffered.end) || 0, + n = Math.max(i, r); + a.msDuration = ne(a.msDuration) ? Math.max(a.msDuration, t, n) : t + } + })), py(s, gu.AltAudio, r, e)).pipe(hr(e => ({ + action: s, + detailsAndContext: e + })))), La(({ + action: e, + detailsAndContext: t + }) => function t(n, s, a, i, d) { + var e; + const { + mediaSink: u, + iframeMachine: c, + rootPlaylistQuery: o + } = a, r = [d[gu.Variant].detailsEntity.mediaOptionDetails, null === (e = null === (e = d[gu.AltAudio]) || void 0 === e ? void 0 : e.detailsEntity) || void 0 === e ? void 0 : e.mediaOptionDetails]; + let l = s.getNextFragments(i, r, n); + const h = l.reduce((e, t) => Math.max(e, ne(null == t ? void 0 : t.newMediaRootTime) ? t.newMediaRootTime : -1 / 0), -1 / 0); + return ne(h) && (u.seekTo = h, l = [null, null]), l.every(e => null == (null == e ? void 0 : e.foundFrag)) ? $i(null) : En(...l.map((t, l) => { + if (t && null != t.foundFrag) { + const i = t.foundFrag["mediaFragment"], + r = _g(a, i.keyTagInfo, { + itemId: i.itemId, + mediaOptionId: i.mediaOptionId + }); + let e = Lg(a, l, t).pipe(Za(e => { + const t = e[1], + i = d[l].switchContext; + t.switchPosition = null == i ? void 0 : i.switchPosition; + const r = null !== (e = null == i ? void 0 : i.userInitiated) && void 0 !== e && e, + n = u["mediaQuery"], + { + desiredRate: s, + isIframeRate: a + } = n, + o = a && c.isStarted && s && s < 0 && s !== c.iframeRate; + (r || o) && (t.flushBeforeAppend = { + start: 0, + end: Number.POSITIVE_INFINITY + }) + })); + return e = l === gu.Variant ? e.pipe(Za(e => { + const t = function(t, i, r, n) { + if (!r) return null; + const { + rootPlaylistService: s, + rootPlaylistQuery: a + } = i, o = a.itemId, l = r[1], d = l.iframe; + let u = a.getInitPTS(n); + if (null == u || !d && u.iframeMode) { + const i = null !== (r = l.startDtsTs) && void 0 !== r ? r : null; + if (null == i) return t.warn("updateInitPTS: Variant data missing."), null; + let e = null !== (t = l.timelineOffset) && void 0 !== t ? t : 0; + d && (e = null !== (t = l.iframeOriginalStart) && void 0 !== t ? t : 0), i.timescale < 1e3 && (i.timescale = 1e3 * i.timescale, i.baseTime = 1e3 * i.baseTime); + const a = B(e, i.timescale), + c = { + baseTime: i.baseTime - a.baseTime, + timescale: i.timescale + }; + s.setInitPTS(o, n, i, e, c, d), u = { + variantDTS: i, + timelineOffset: e, + offsetTimestamp: c, + iframeMode: d + } + } + return u + }(n, a, e, s.discoSeqNum); + fy(n, a, e, t) + })) : en([e, Gu(o.initPTS$(s.discoSeqNum), e => { + const t = u.mediaQuery.isIframeRate; + return null != e && (t || !e.iframeMode) + })]).pipe(hr(([e, t]) => (fy(n, a, e, t), e))), en([r, e]).pipe(hr(e => e[1])) + } + return $i(null) + })).pipe(hr(e => function(g, t, e, i) { + const { + rootPlaylistQuery: r, + mediaSink: n, + config: y + } = t, v = n.mediaQuery, s = v.isIframeRate, a = r.getInitPTS(e); + if (null == a) return g.warn("No initPTS info found"), null; + const o = i[gu.Variant], + l = null == o ? void 0 : o[1]; + if (l && l.iframe !== s) return g.warn(`frag mediaSeqNum ${l.mediaSeqNum} discoSeqNum ${l.discoSeqNum} mediaOptionId ${l.mediaOptionId} doesn't match mediaSink's iframeMode ${s}; discard`), null; + const S = [null, null]; + if (o) { + const [g, t] = o; + let e = a.offsetTimestamp; + if (s) { + const g = t.startDtsTs, + i = B(t.timelineOffset, g.timescale); + e = { + baseTime: g.baseTime - i.baseTime, + timescale: g.timescale + } + } + S[yu.Variant] = { + initSeg: g, + dataSeg: t, + offsetTimestamp: e + } + } + const d = i[gu.AltAudio]; + if (null != d) { + const [g, t] = d; + S[yu.AltAudio] = { + initSeg: g, + dataSeg: t, + offsetTimestamp: a.offsetTimestamp + } + } + const u = S.map((e, t) => { + const i = null == e ? void 0 : e.dataSeg; + if (i) { + const { + itemId: r, + mediaOptionId: n, + mediaSeqNum: s, + discoSeqNum: a, + startPts: o, + endPts: l, + duration: d, + iframe: u + } = i, c = e["offsetTimestamp"], h = b(o, c), p = b(l, c), f = Dg(i), m = S[0]; + return m && m.dataSeg.dropped || i.flushBeforeAppend || !((null === (e = null === (e = v.getBufferInfo(h, y.maxBufferHole)[t]) || void 0 === e ? void 0 : e.buffered) || void 0 === e ? void 0 : e.len) >= p - h) ? { + start: h, + duration: u ? d : p - h, + itemId: r, + mediaOptionId: n, + mediaSeqNum: s, + discoSeqNum: a, + targetDuration: f.mediaOptionDetails.targetduration + } : (g.warn(`${Nu[t]} Discarding append due to complete overlap with existing buffer`), S[t] = null) + } + return null + }); + return u.every(e => !e) ? null : { + appendDataTuple: S, + inFlightFrags: u, + initPTSInfo: a + } + }(n, a, s.discoSeqNum, e)), La(e => { + if (e) return $i(e); { + const e = function(e, r) { + const n = e.enabledMediaOptionKeys, + s = [null, null], + a = [null, null]; + return Bu.map(e => { + var t; + if (_u(n[e])) { + const i = Dg(n[e]).mediaOptionDetailsEntity; + a[e] = null === (t = i.mediaOptionDetails) || void 0 === t ? void 0 : t.ptsKnown, s[e] = { + detailsEntity: i, + switchContext: null === (e = r[e]) || void 0 === e ? void 0 : e.switchContext + } + } + }), s + }(o, d); + return t(n, s, a, i, e) + } + })) + }(i, n, r, e, t)), tc("mediaProducerEpic.emit")) + }; + + function py(e, i, t, r) { + const { + rootPlaylistQuery: n, + mediaLibraryService: s, + config: a + } = t, o = r[i]; + if (t.logger.child({ + name: Nu[i] + }), !o || "Nah" === o.mediaOptionId) return $i({ + detailsEntity: null, + switchContext: null + }); + const l = s.getQueryForOption(o); + return Mr([$i(e), l.mediaOptionDetailsEntity$.pipe(Is((e, t) => (null == e ? void 0 : e.lastUpdateMillis) === (null == t ? void 0 : t.lastUpdateMillis)), ln(e => { + var t = null == e ? void 0 : e.mediaOptionDetails; + if (!t) return !0; + var i = performance.now(), + r = e.lastUpdateMillis || i, + e = t.liveOrEvent, + t = t.targetduration; + return !e || i - r < a.livePlaylistUpdateStaleness * t * 1e3 + }))]).pipe(ln(([, e]) => { + if (i === gu.AltAudio && !n.altMediaOptionHasValidUrl(i, o.mediaOptionId)) return !0; + var t = null == e ? void 0 : e.mediaOptionDetails; + return null != t && (e = null !== (e = e.lastUpdateMillis) && void 0 !== e ? e : 0, !t.liveOrEvent || !t.ptsKnown || !Yg(null == t ? void 0 : t.totalduration, 0, e)) + }), Ds(1), bo(n.enabledMediaOptionSwitchContextsByType$(i)), hr(([ + [, e], t + ]) => ({ + detailsEntity: e, + switchContext: t + }))) + } + + function fy(e, t, i, r) { + const { + mediaLibraryService: n, + rootPlaylistQuery: s, + mediaSink: a + } = t, o = s.itemId; + null != r && (a.mediaQuery.isIframeRate || !r.iframeMode ? i && !ne(i[1].iframeMediaDuration) && (performance.now(), n.updatePTSDTS(o, i[1].mediaOptionId, r, i[1])) : e.warn("updatePTSDTS iframeMode mismatch")) + } + const my = t => e => { + const { + rootPlaylistQuery: g, + rootPlaylistService: y, + mediaSink: v, + legibleSystemAdapter: o, + statsService: l, + rtcService: d + } = t; + return e.pipe(tc("mediaConsumerEpic.in"), La(e => { + if (!e) return $i(!1); + const { + appendDataTuple: r, + inFlightFrags: m, + initPTSInfo: t + } = e, i = t["offsetTimestamp"]; + return m.forEach((e, t) => { + e && y.updateInflightFrag(e.itemId, t, e, "appending", null) + }), r.forEach(e => { + e && (e = e.dataSeg, o.addLegibleSamples(i, e.captionData, e.id3Samples, e.endPts)) + }), v.appendData(r, (e, t, i, r, n) => { + var s, a, o, l, d, u, c, h, p, f = null !== (f = m[t].targetDuration) && void 0 !== f ? f : 10; + return s = v, a = e, o = t, l = i, d = f, u = r, c = g, h = y, p = n, e => e.pipe(Za(() => { + h.updateConsecutiveTimeouts(c.itemId, o, !1, "append") + }), va(e => e.pipe(jr((e, t) => { + var i = e instanceof df && e.isTimeout; + if (h.updateConsecutiveTimeouts(c.itemId, o, i, "append"), i) return function(e, t, i, r, n, s, a, o) { + let l = { + errorAction: Cm.SendAlternateToPenaltyBox, + errorActionFlags: 0 + }; + var d = s.getCurrentWaterLevel(i.maxBufferHole), + u = d < i.almostDryBufferSec; + let c = NaN; + s = i.appendErrorMaxRetry, i = a.rootPlaylistEntity.errorsByType[r].timeouts.append; + u && s <= i || s <= t ? l.errorAction = Cm.SendEndCallback : c = 1e3 * d; + s = { + retryDelayMs: c, + maxNumRetry: s, + maxRetryDelayMs: c + }; + return l = Jm(l, !1, e.response.code, n, r, a, o), rg(e, t, s, l, a, o, r, n).pipe() + }(e, t, u, o, l, p, c, h); + if (e instanceof uf) return function(e, t, i, r, n, s, a, o, l, d) { + var u = t.type, + u = o.getCurrentWaterLevelByType(u, n.maxBufferHole); + if (u >= n.almostDryBufferSec && !o.isIframeRate) { + const t = 1e3 * r, + n = { + errorAction: Cm.RetryRequest, + errorActionFlags: 0 + }; + return 1e3 * u < t && (d.hasFallbackMediaOptionTuple(l, s, a, !1) ? n.errorAction = Cm.SendAlternateToPenaltyBox : n.errorAction = Cm.SendEndCallback), rg(e, i, { + retryDelayMs: t, + maxNumRetry: 1 / 0, + maxRetryDelayMs: t + }, n, l, d, s, a) + } + return i < n.appendErrorMaxRetry ? t.remove(0, Number.POSITIVE_INFINITY) : (e.fatal = !0, Vi(e)) + }(e, a, t, d, u, o, l, p, c, h); + if (e instanceof hf) { + const { + mediaOptionType: a, + mediaOptionId: o + } = e; + return oy(e, a, o, s, h, c) + } + throw e + })))) + }, g.highestVideoCodec).pipe(hr(e => { + m.forEach((e, t) => { + e && y.updateInflightFrag(e.itemId, t, e, "appended", null) + }); + var t = e.filter(e => (null == e ? void 0 : e.fragmentType) === gu.Variant); + t.length && (l.setBufferMetric(t[0]), null == d || d.handleFragBuffered(t[0])); + e = r[yu.AltAudio]; + if (null !== (t = null == e ? void 0 : e.dataSeg) && void 0 !== t && t.flushBeforeAppend || ne(null === (t = null == e ? void 0 : e.dataSeg) || void 0 === t ? void 0 : t.switchPosition)) { + const { + itemId: i, + mediaOptionId: r + } = e.dataSeg; + y.setEnabledMediaOptionSwitchContextByType(i, gu.AltAudio, r, void 0) + } + return !0 + }), (n = v, s = y, a = g, e => e.pipe(Vn(e => { + if (e instanceof lf) { + var { + mediaOptionType: t, + mediaOptionId: i + } = e; + return oy(e, t, i, n, s, a) + } + throw e + })))); + var n, s, a + })) + }; + + function gy(d, u, c, h, p, f) { + return u = u.child({ + name: "seek" + }), e => e.pipe(La((e, t) => { + if (null == e) return Ii; + var i, r, n, s, a, o, l = c.mediaQuery.seekTo$.pipe(ka(1), Kp()); + return i = 0 === t, r = d, n = u, s = p, t = f, (null == (e = e) ? Ii : e instanceof Date ? (a = e, o = t, s.enabledMediaOptionByType$(gu.Variant).pipe(La(e => o.getQueryForOption(e).mediaOptionDetails$), hr(e => function(e, t) { + if (!e || 0 === e.length) return 0; + const i = [...e].sort((e, t) => t[0] - e[0]), + r = t.getTime(), + n = null !== (t = i.find(([e]) => r >= e)) && void 0 !== t ? t : i[i.length - 1], + [s, a] = n, + o = a + (r - s) / 1e3; + return Math.max(0, o) + }(e.dateMediaTimePairs, a)), Ds(1))) : function(r, e, a, n, t, o) { + let i = n.enabledMediaOptionByType$(gu.Variant).pipe(La(e => t.getQueryForOption(e).mediaOptionDetails$), ln(e => ne(null == e ? void 0 : e.totalduration)), Ds(1), hr(s => { + var e = !s.liveOrEvent, + t = s.totalduration, + i = n.itemStartOffset; + return e ? ne(r) ? 0 <= r ? r : i + (t + r) : i + (ne(s.startTimeOffset) ? s.startTimeOffset : 0) : !ne(r) || r < 0 || 0 === r && a.liveEdgeForZeroStartPositon ? yg(0, s, a) : function(e, t) { + let i = e; + var r = s.fragments[0].start, + n = s.fragments[s.fragments.length - 1].start + s.fragments[s.fragments.length - 1].duration; + return e < r ? i = r : n < e && (i = yg(0, s, a)), (e < r || n < e) && t.warn(`[live] sanitizeLiveSeek seekTo:${se(e,3)}, sanitizedSeek:${se(i,3)}, liveWindowStart:${se(r,3)}, liveWindowEnd:${se(n,3)}`), i + }(r, o) + })); + return e && (i = i.pipe(Za(e => {}))), i + }(e, i, r, s, t, n)).pipe(Vs(() => { + h.setPendingSeek(p.itemId, void 0) + }), Va(l)) + }), Za(e => { + ne(e) && (c.seekTo = e) + })) + } + + function yy(e) { + const { + logger: r, + rootPlaylistService: n, + rootPlaylistQuery: t + } = e, s = t.itemId; + return e => e.pipe(va(e => e.pipe(jr(e => { + if (r.error(`Got error in pipeline ${e.message} fatal:${null==e?void 0:e.fatal} handled:${null==e?void 0:e.handled}`), !(e instanceof p) || e.fatal) throw e; + return e.handled ? (t = n, i = s, bn(0).pipe(hr(() => { + t.updateEnabledMediaOptions(i) + }))) : Ii; + var t, i + })))) + } + const vy = () => e => e.pipe(tc("mediaFragmentPipelineEpic.in"), La(i => { + if (!i) return Ii; + const { + logger: e, + config: t, + platformService: r, + rootPlaylistService: n, + rootPlaylistQuery: s, + keySystemAdapter: a, + mediaSink: o, + mediaParser: l, + gaplessInstance: d, + mediaLibraryService: u + } = i, c = s["itemId"], h = o["mediaQuery"], p = a.keyStatusChange$.pipe((f = i, e => e.pipe(La(e => { + const { + decryptdata: t, + status: i, + error: r + } = e, n = f["rootPlaylistQuery"]; + if ("needs-renewal" === i) return _g(f, t, null); + if ("error" !== i || !(r instanceof gc || r instanceof mc) || r.handled) return Ii; { + const { + rootPlaylistService: e, + keySystemAdapter: t + } = f; + return og(r, 0, null, e, n, t.ksQuery) + } + }), La(() => Ii)))); + var f; + const m = r.getQuery(), + g = m.displaySupportsHdr$.pipe(Is(), La(e => (n.setHDRPreference(c, e, !0), Ii))), + y = m.viewportInfo$.pipe(Is((e, t) => e && t && e.width === t.width && e.height === t.height), Za(e => { + t.useViewportSizeForLevelCap && n.setViewportInfo(c, e) + }), $a(Ii)), + v = ed([s.hdrMode$.pipe(Is()), s.maxHdcpLevel$.pipe(Is())]).pipe(La(([]) => (d.inGaplessMode || 0 !== s.itemStartOffset || (o.resetMediaSource(), l.reset()), Ii))), + S = an(function(i) { + const { + rootPlaylistQuery: t, + mediaSink: r + } = i, e = t.enabledMediaOptions$.pipe(Kp(), Za(e => { + e = e[gu.AltAudio], e = _u(e) && null != (null == e ? void 0 : e.url) ? 2 : 1; + r.setExpectedSbCount(e) + })), n = en([t.enabledMediaOptionByType$(gu.Variant).pipe(ln(e => _u(e)), La(e => Dg(e).mediaOptionDetails$), Ds(1)), Gu(r.mediaQuery.updating$, e => e)]).pipe(ji(tr), Za(([e]) => { + r.bufferMonitorTargetDuration = e.targetduration + })), s = Fu.map(e => t.enabledMediaOptionByType$(e).pipe(tc("mediaOptionRetrieve.switch"), La(t => { + if (!t || !t.url || !_u(t)) return Ii; + var e = r.mediaQuery.desiredRate$.pipe(hr(e => 0 !== e), Is()); + return Mg(i, t).pipe(tc("mediaOptionRetrieve.first"), $a(e), La(e => e ? function e(t, i) { + const r = t.mediaLibraryService; + return function(e) { + if (!e) return Ii; + var { + mediaOptionDetails: t, + lastUpdateMillis: i, + unchangedCount: e + } = e; + if (null == t || !t.liveOrEvent) return Ii; + if (bg(t, i)) return bn(0).pipe(Za(() => {})); + let r = Sg(t); + return 0 < e && (r /= 2, r = Math.max(r, 5e3)), r -= performance.now() - i, r += 0, r = Math.max(1e3, Math.round(r)), bn(r).pipe(Za(() => {})) + }(r.getQueryForOption(i).mediaOptionDetailsEntity).pipe(La(() => Mg(t, i, !1, !0)), La(() => e(t, i))) + }(i, t) : Ii)) + }))); + return an(e, n, an(...s)).pipe($a(Ii)) + }(i), cy(i), function(t) { + const { + rootPlaylistQuery: i, + mediaSink: e + } = t; + return Gu(e.mediaQuery.mediaElementEntity$, e => e).pipe(La(e => i.anchorTime$.pipe(tc("anchorTime.subtitle.in"), ln(e => ne(null == e ? void 0 : e.pos)), (s => e => { + const { + rootPlaylistQuery: i, + rootPlaylistService: t, + legibleSystemAdapter: r + } = s, n = i.enabledAlternateMediaOptionByType(gu.Subtitle); + if (r.gotTracks) r.selectedTrack = n; + else { + const s = i.preferredMediaOptions[gu.Subtitle]; + r.setTracks(s, n, i.getDisabledMediaOption(gu.Subtitle)) + } + return e.pipe(tc("subtitleEpic.select.in"), La(() => an(r.nativeSubtitleTrackChange$.pipe(La(e => (e.mediaOptionId !== r.selectedMediaOption.mediaOptionId && t.setEnabledMediaOptionByType(e.itemId, gu.Subtitle, e), Ii))), i.enabledMediaOptionByType$(gu.Subtitle).pipe(hr(e => { + const t = _u(e) ? i.alternateMediaOptionById(gu.Subtitle, e.mediaOptionId) : e; + return r.selectedMediaOption = t, t + })).pipe(Is((e, t) => (null == e ? void 0 : e.mediaOptionId) === (null == t ? void 0 : t.mediaOptionId))))), tc("subtitleEpic.select.emit")) + })(t), (s => e => { + const { + mediaSink: t, + rootPlaylistQuery: i, + legibleSystemAdapter: r, + logger: n + } = s; + return e.pipe(tc("subtitleEpic.process.in"), La(e => { + if (!e || !e.url || !_u(e)) return $i([null, null, null]); + return ed([Dg(e).mediaOptionDetails$, i.discoSeqNum$.pipe(ln(e => ne(e)))]).pipe(La(([e, t]) => ((i, r, e) => { + const { + legibleSystemAdapter: n, + rootPlaylistQuery: t + } = i; + return t.initPTS$(e).pipe(La(t => !t || t.iframeMode ? on : n.findFrags$(r, e).pipe(La(e => r && (null == e ? void 0 : e.foundFrags) ? Sy(i, t.offsetTimestamp, e, r) : Wu)))) + })(s, e, t))) + }), tc("subtitleEpic.process.emit")) + })(t)))) + }(i), p).pipe(Zs(void 0), yy(i)), + b = h.seekTo$.pipe(ln(e => ne(null == e ? void 0 : e.pos)), Is((e, t) => Math.abs(e.pos - t.pos) < Number.EPSILON), La(e => (n.setAnchorTime(c, e), Ii))), + T = h.gotPlaying$.pipe(ln(e => e), Za(e => { + s.mediaOptionListQueries[gu.Variant].filteredMediaOptionList.forEach(e => {}) + }), Ds(1), $a(Ii)); + return an(s.pendingSeek$.pipe(gy(t, e, o, n, s, u)), function() { + const { + config: n, + mediaSink: s, + rootPlaylistQuery: e, + mediaLibraryService: t + } = i, a = (i.logger.child({ + name: "live" + }), s.mediaQuery); + return e.enabledMediaOptionByType$(gu.Variant).pipe(ln(_u), La(e => t.getQueryForOption(e).mediaOptionDetailsEntity$.pipe(ln(e => { + var t; + return (null === (t = null == e ? void 0 : e.mediaOptionDetails) || void 0 === t ? void 0 : t.ptsKnown) && e.mediaOptionDetails.liveOrEvent + }), Is((e, t) => (null == e ? void 0 : e.lastUpdateMillis) === (null == t ? void 0 : t.lastUpdateMillis)))), hr(e => { + var t = e.mediaOptionDetails, + i = a.currentTime; + a.msDuration < e.playlistDuration ? s.msDuration = e.playlistDuration : ne(s.msDuration) && (s.msDuration = s.msDuration + n.livePlaylistDurationNudge); + let r = NaN; + return i < vg(i, t, e.lastUpdateMillis, n.maxBufferHole, a) && (r = yg(t.fragments[0].start, t, n), s.seekTo = r), r + })) + }(), b, S, g, y, v, function() { + const e = i.mediaSink.mediaQuery; + return Mr([$i(i), e.desiredRate$.pipe(ha())]).pipe(La(([e, [t, i]]) => { + const { + rootPlaylistQuery: s, + rootPlaylistService: r, + config: a, + mediaSink: n, + mediaLibraryService: o, + statsService: l + } = e, d = n.mediaQuery; + if (Wp(t) !== Wp(i)) iy(Mm.IframeModeChange, a, s, d, r); + else if (0 === t && 1 === i && !Bu.every(e => { + const t = s.enabledMediaOptionKeys[e], + i = o.getQueryForOption(t), + r = l.getQueryForItem(s.itemId), + n = i.mediaOptionDetailsEntity; + return !(null !== (e = null == n ? void 0 : n.mediaOptionDetails) && void 0 !== e && e.ptsKnown) || d.canContinuePlaybackWithoutGap(n.mediaOptionDetails, n.lastUpdateMillis, r.getPlaylistEstimate(), a.maxBufferHole) + })) return n.pause(), n.flushAll(0, 1 / 0, !0); + return Ii + }), $a(Ii)) + }(), function(e) { + const t = e.rootPlaylistQuery, + i = e.mediaSink.mediaQuery, + r = t.enabledMediaOptionByType$(gu.Variant); + return Mr([$i(e), i.desiredRate$.pipe(ha())]).pipe(Is((e, t) => e[1] === t[1]), bo(r), La(([ + [e, [t, i]], r + ]) => { + t = Wp(t), i = Wp(i); + if (t === i) return Ii; + const n = e["rootPlaylistService"]; + return i && e.rootPlaylistQuery.nextMaxAutoOptionId === Lu.mediaOptionId && n.setNextMaxAutoOptionId(e.rootPlaylistQuery.itemId, r.mediaOptionId), Ii + })) + }(i), T).pipe(tc("mediaFragmentPiplineEpic.emit"), Zs(void 0)) + })), + Sy = (r, e, t, i) => { + const n = r.legibleSystemAdapter, + s = t.foundFrags; + return Fr(s).pipe(hr(t => { + return ((e, t, i) => { + const { + rootPlaylistQuery: r, + legibleSystemAdapter: n + } = e; + return Zr(() => ((t, i) => Rg(e, i, !1, !1).pipe(hr(e => ({ + initPTS: t, + data: e, + mediaFragment: i + })), tc("retrieveSubtitleFragmentCacheEntity.emit")))(t, i).pipe(hr(({ + initPTS: e, + data: t, + mediaFragment: i + }) => ({ + frag: i, + cueRange: function(e, t, i, r, n) { + if (e) return n.processSubtitleFrag(e, t, i, r) + }(r.enabledAlternateMediaOptionByType(gu.Subtitle), i, e, t, n) + })))) + })(r, e, t).pipe((i = e => n.checkReadyToLoadNextSubtitleFragment$(t, s).pipe(ln(e => e)), function(e) { + return e.lift(new gs(i)) + })); + var i + }), Yr(r.config.vttConcurrentLoadCount), Za(e => { + n.reviewParsedFrag(e, t, i) !== kp.CloseEnough && r.legibleSystemAdapter.tryAgain$.next(!0) + })) + }, + by = (e, t) => { + let i, r = ""; + return i = e.videoCodec && e.audioCodec ? (r = `${e.videoCodec}, ${e.audioCodec}`, t = null != t ? t : "video/mp4", "audiovideo") : e.videoCodec ? (r = `${e.videoCodec}`, t = null != t ? t : "video/mp4", "video") : (r = `${null!==(e=e.audioCodec)&&void 0!==e?e:""}`, t = null != t ? t : "audio/mp4", "audio"), { + mimeType: `${t};codecs=${r}`, + codec: r, + container: t, + type: i + } + }; + class Ty { + constructor(e, t, i) { + this.config = e, this.logger = t, this.demuxClient = i, this.typeSupported = { + mp4: MediaSource.isTypeSupported("video/mp4"), + mpeg: MediaSource.isTypeSupported("audio/mpeg"), + mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'), + ac3: MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"'), + ec3: MediaSource.isTypeSupported('audio/mp4; codecs="ec-3"') + }, this.demuxers = [], this.lastInitFrags = [], this.lastFrags = [] + } + parseInitSegment(h, e) { + return this.getDemuxerInfo(h, this.lastInitFrags, e, this.demuxClient).pipe(La(({ + demuxer: e, + contiguous: t, + trackSwitch: i, + discontinuity: r, + accurateTimeOffset: n + }) => { + const s = h["frag"], + { + keyTagInfo: a, + start: o, + mediaOptionType: l + } = s; + if (this.lastInitFrags[l] = s, h.initSegment) { + const e = ze.remuxInitSegment(new Uint8Array(h.initSegment), this.logger, a), + t = Ze.parseInitSegment(e), + { + mimeType: i, + type: r, + codec: n, + container: s + } = by(t); + return $i({ + moovData: t, + mimeType: i, + track: { + type: r, + codec: n, + initSegment: e, + container: s + } + }) + } + const d = h.segment || h.initSegment, + u = d ? h.initSegment : void 0, + c = Oc(e.observer); + return $i(c.event(v.FRAG_PARSING_INIT_SEGMENT).pipe(hr(this.handleInitSegmentData)), c.event(x.INTERNAL_ERROR).pipe(La(this.handleError)), e.pushWithoutTransfer(d, a, u, o, r, i, t, h.totalDuration, n, void 0, h.iframeMediaStart, h.iframeDuration).pipe($a(Ii))).pipe(Yr(), Ds(1)) + })) + } + parseSegment(y, e) { + return this.getDemuxerInfo(y, this.lastFrags, e, this.demuxClient).pipe(La(({ + demuxer: e, + contiguous: t, + trackSwitch: i, + discontinuity: r, + accurateTimeOffset: n + }) => { + const { + frag: h, + defaultInitPTS: p + } = y, { + keyTagInfo: s, + start: a, + duration: f, + mediaOptionType: m + } = h; + let g; + this.lastFrags[m] = h; + const o = Oc(e.observer); + return $i(o.event(v.FRAG_PARSING_INIT_SEGMENT).pipe(La(e => { + var t; + return e.track.initSegment.byteLength !== (null === (t = y.initSegment) || void 0 === t ? void 0 : t.byteLength) && (g = this.handleInitSegmentData(e)), Ii + })), o.event(v.FRAG_PARSING_DATA).pipe(hr(e => { + var { + startPTS: t, + startDTS: i, + firstKeyframePts: r, + framesWithoutIDR: n, + dropped: s, + data1: a, + data2: o, + captionData: l, + id3Samples: d + } = e; + let { + endPTS: u, + endDTS: c + } = e; + return null == u && (this.logger.warn(`${Nu[m]} ${Vp(h)}: null endPTS parsed, using duration ${f}`), u = Object.assign(Object.assign({}, t), { + baseTime: t.baseTime + B(f, t.timescale).baseTime + })), null == c && (this.logger.warn(`${Nu[m]} ${Vp(h)}: null endDTS parsed, using duration ${f}`), c = Object.assign(Object.assign({}, i), { + baseTime: i.baseTime + B(f, i.timescale).baseTime + })), ne(y.iframeMediaStart) || function(e, t, i, r) { + let n = NaN, + s = NaN; + if (ne(i)) s = i, n = .01, isFinite(s) && isFinite(r) && (s += r); + else { + { + const o = void 0 + } + } + var { + startPTS: a, + startDTS: i, + endPTS: r, + endDTS: t + } = t; + if (!(0 <= a.baseTime && 0 <= i.baseTime && 0 < e.duration && (null == r || 0 < b(r, a)) && (null == t || 0 < b(t, i)) && (!ne(n) || !ne(s) || Math.abs(S(i) - s) <= n))) throw new D(!1, `Failed demuxer sanity check frag=${Vp(e)} parsed=${JSON.stringify({startPTS:a,endPTS:r,startDTS:i,endDTS:t})} ${ae({expectedStartDTS:s,fudge:n})}`, $.FailedDemuxerSanityCheck) + }(h, e, (p, y.iframeMediaStart), this.config.audioPrimingDelay), { + startPTS: t, + endPTS: u, + startDTS: i, + endDTS: c, + firstKeyframePts: r, + framesWithoutIDR: n, + dropped: s, + data1: a, + data2: o, + captionData: l, + id3Samples: d, + parsedInitSegment: g + } + })), o.event(x.INTERNAL_ERROR).pipe(La(this.handleError)), e.push(y.segment, s, y.initSegment, a, r, i, t, y.totalDuration, n, p, y.iframeMediaStart, y.iframeDuration).pipe($a(Ii))).pipe(Yr(), Ds(1)) + })) + } + reset(e) { + if (null == e) return this.demuxers.forEach(e => { + e && e.destroy() + }), void(this.demuxers = []); + const t = this.demuxers[e]; + null == t || t.destroy(), this.demuxers[e] = null + } + destroy(e) { + null != e ? this.reset(e) : this.reset() + } + willBeTrackSwitch(e, t) { + var { + mediaOptionType: i, + mediaOptionId: e + } = e, i = (t || this.lastFrags)[i]; + return !(i && i.mediaOptionId === e) + } + getDemuxerInfo(e, r, t, i) { + const { + frag: n, + ptsKnown: s, + seeking: a, + live: o + } = e, { + discoSeqNum: l, + mediaSeqNum: d, + mediaOptionType: u + } = n; + return Zr(() => { + var e = this.demuxers[u]; + return e ? $i(e) : i.init(this.typeSupported, this.config, t).pipe(Za(e => this.demuxers[u] = e)) + }).pipe(hr(e => { + var t = r[u], + i = this.willBeTrackSwitch(n, r); + return { + demuxer: e, + trackSwitch: i, + discontinuity: !(t && l === t.discoSeqNum), + contiguous: !!t && !i && t.mediaSeqNum + 1 === d, + accurateTimeOffset: !a && (s || !o) + } + })) + } + handleInitSegmentData(e) { + var t = e["track"], + i = t["initSegment"], + r = Ze.parseInitSegment(i), + { + mimeType: n, + type: s, + codec: a, + container: e + } = by(r, t.container); + return { + moovData: r, + mimeType: n, + track: Object.assign(Object.assign({}, t), { + type: s, + codec: a, + initSegment: i, + container: e + }) + } + } + handleError(e) { + return Vi(e) + } + } + + function Ey(a, e, t, h, p, i, r, n) { + var s = h["combined"], + o = function(e) { + let t = 1 / 0; + h.playingFrag && (t = null !== (s = null === (n = e.fragments[h.playingFrag.mediaSeqNum - e.startSN]) || void 0 === n ? void 0 : n.duration) && void 0 !== s ? s : 1 / 0); + var { + minRequiredStartDuration: i, + maxRequiredStartDuration: r, + startTargetDurationFactor: n + } = a, { + targetduration: s, + averagetargetduration: e + } = e, r = n * Math.min(t, e, s, r); + return Math.max(i, r) + }(t.details); + let l = function(e, t, i) { + const { + pos: r, + combined: n, + playingFrag: s + } = h; + if (0 === n.len) return !1; + var a = t.details, + o = a.fragments; + let l = 0 != p && 1 != p || n.len >= i; + var d = o[a.fragments.length - 1], + t = o[0].start + a.totalduration; + let u = !1; + if (s) { + const c = qu.search(o, e => s.discoSeqNum - e.discoSeqNum); + u = e && s.discoSeqNum !== e.discoSeqNum || null == c || $p(c, s) + } + return l && a.liveOrEvent ? l = r <= t - d.duration : a.liveOrEvent || (l = l || t - i <= r), l = l || u, l + }(e, t, o); + return !l && 0 < s.len && null != e && e.state && (l = function(n, e, t, i, r, s, a) { + var o = null === (l = h.sbTuple[gu.Variant]) || void 0 === l ? void 0 : l.buffered, + l = null === (l = h.sbTuple[gu.AltAudio]) || void 0 === l ? void 0 : l.buffered; + if ((null == o ? void 0 : o.len) >= t && (!l || l.len >= t)) return 0; + if (!(o && n && (d = h.pos, (l = n.start + n.duration) > o.end && (n.start - o.end <= a || n.start <= o.end) && t <= l - d))) return 1 / 0; + var d = n.state; + let u = n.tstart, + c = 0; + switch (d) { + case "loading": + c += function(e, t) { + var { + bwSample: i, + duration: r + } = n; + if (!i) return 1 / 0; + r = ne(i.total) ? 8 * i.total : Math.ceil(r * e), e = 8 * i.loaded, r -= e, i = e / (performance.now() - i.tfirst) * 1e3; + if (!ne(i)) return 1 / 0; + t = t.avgBandwidth; + return r / Math.min(t, i) + }(e, i), u = n.tstart + 1e3 * c; + case "loaded": + case "parsing": + c += function(e, t) { + t = ne(t.avgParseTimeMs) ? t.avgParseTimeMs : 0; + return performance.now() < e ? t / 1e3 : Math.max(0, t - (performance.now() - e)) / 1e3 + }(u, r), u = n.tstart + 1e3 * c; + case "parsed": + case "appending": + c += function(e, t) { + t = ne(t.avgDataFragAppendMs) ? t.avgDataFragAppendMs : 0; + return performance.now() < e ? t / 1e3 : Math.max(0, t - (performance.now() - e)) / 1e3 + }(u, s); + break; + default: + c = 1 / 0 + } + return c + }(e, t.variant.bitrate, o, i, r, n, a.maxBufferHole) <= s.len), l + } + + function Iy(e, t, i, r) { + if (200 === t && r && 10 < r.length) { + if (Rm.isValidPlaylist(r)) return !0; { + const t = new R(o, _, !0, "response doesnt have #EXTM3U tag", $.PlaylistErrorMissingEXTM3U); + throw t.url = e, t + } + } + return !1 + } + const wy = { + name: "pltfrm" + }; + + function Ay(e, t) { + t = Ah.getKeySystemSecurityLevel(t); + return null != e && void 0 !== t[e] + } + + function Oy(e) { + return e.every(e => e.iframes) + } + + function ky(e, t) { + return !ne(e) || !ne(t) || e <= t + } + + function Cy() { + const n = new Set, + s = new Set; + return e => { + const i = (e, t) => { + t = t ? "audio" : "video"; + n.has(e) || s.has(e) || (((e, t) => { + let i = MediaSource.isTypeSupported(`${e}/mp4;codecs=${t}`); + return "mp4a.40.34" !== t || i || (i = MediaSource.isTypeSupported(`${e}/mpeg`)), i + })(t, e) ? n : s).add(e) + }, + t = (e, t) => (i(e, t), s.has(e)); + let r = !1; + return e.audioCodecList && (r = e.audioCodecList.some(e => t(e, !0))), !r && e.videoCodecList && (r = e.videoCodecList.some(e => t(e, !1))), !r + } + } + + function Dy(e, t) { + for (const i in e) + if (e[i].type === t) return e[i]; + return {} + } + + function My(e, t, i) { + t.filter(e => !i.includes(e)).map(e => e.mediaOptionId) + } + + function xy(e, i, s) { + const a = new Map, + r = new Array; + return e.forEach(t => { + var e = Array(); + ! function(e, t, i) { + var r = Wc.getCapabilities(t.videoCodecList, t.audioCodecList), + t = JSON.stringify(r); + let n; + a.has(t) ? n = a.get(t) : (n = Ah.requestKeySystemAccess(e, r, void 0, s).pipe(hr(() => !0), Vn(e => (s.warn(`Request key system error: ${e.message}`), $i(!1))), Oa({ + bufferSize: 1, + refCount: !0 + })), a.set(t, n)), i.push(n) + }(i, t, e); + e = en(e).pipe(hr(e => { + if (void 0 === e.find(e => !1 === e)) return t + })); + r.push(e) + }), en(r).pipe(hr(e => e.filter(e => Boolean(e)))) + } + + function Py(e, r) { + const o = new Set, + l = new Set, + d = !MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.2"; channels="-1"'), + u = d && !MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.2"; channels="2"; features="INVALID"'), + t = e.filter(e => { + let t = !1; + var i; + return e.audioCodecList && e.audioGroupId && (i = fm.getRichestChannelLayoutForGroupId(e.audioGroupId, r), 0 < e.audioCodecList.length && i && (t = ((e, t) => { + var i, r, n, s = be.isDolbyAtmos(e, t); + if (u || d && !s) { + n = `${i=e}/${r=t}`, o.has(n) || l.has(n) || (((e, t) => { + const i = t.split("/"), + r = parseInt(i[0]); + let n, s; + if (1 < i.length) { + const t = i[1].split(",")[0]; + n = `audio/mp4;codecs="${e}";channels="${r}";features="${t}"`, s = `audio/mp4;codecs="${e}";channels="8";features="${t}"` + } else n = `audio/mp4;codecs="${e}";channels="${r}"`; + let a = MediaSource.isTypeSupported(n); + return !a && s && (a = MediaSource.isTypeSupported(s)), a + })(i, r) ? o : l).add(n); + const a = `${e}/${t}`; + return l.has(a) + } + return !!s + })(fm.getRichestAudioCodec(e.audioCodecList), i))), !t + }); + return My(0, e, t), t + } + + function Ry(e, t, l) { + const n = 0 < (null == t ? void 0 : t.length), + i = e.filter(o => { + var e = function() { + if (!l) return { + highestPlayableAverageBitRate: void 0, + highestPlayablePeakBitRate: void 0, + highestPlayableWidth: void 0, + highestPlayableHeight: void 0, + highestPlayableFrameRate: void 0 + }; + const e = o.videoCodec, + t = o.videoRange, + i = l.videoDynamicRangeFormats, + r = l.videoCodecs, + n = be.getDynamicRangeType(t, e), + s = be.getCompressionType(e), + a = function(e, t, i, r) { + if (!r && !i) return {}; + var n, s, t = i ? Dy(i, t) : {}, + r = r ? Dy(r, e) : {}; + let a, o; + return o = e === fe.SDR ? (a = t, r) : (a = r, t), n = Object.assign({}, a), s = o, Object.keys(s).forEach(e => { + n[e] || (n[e] = s[e]) + }), n + }(n, s, r, i); + return s !== me.VP09 && (a.highestPlayablePeakBitRateForClearContent = void 0), a + }(), + t = e["highestPlayablePeakBitRateForClearContent"], + i = o.allowedCPCMap || n, + r = ky(o.bandwidth, e.highestPlayablePeakBitRate); + return (i || !t ? r : r || ky(o.bandwidth, t)) && ky(o.avgBandwidth, e.highestPlayableAverageBitRate) && ky(o.width, e.highestPlayableWidth) && ky(o.height, e.highestPlayableHeight) && ky(o.frameRate, e.highestPlayableFrameRate) + }); + return My(0, e, i), i + } + + function Ly(e, a, o, l, d, u) { + var r = (null == l ? void 0 : l.maxHdcpLevel) || void 0; + let c = [...e]; + (0 < d.disableVideoCodecList.size || 0 < d.disableAudioCodecList.size) && (c = function(e, t, i) { + let r = e.filter(e => !e.videoCodec || e.videoCodecList.every(e => { + e = qp(e); + return !t.has(e) + })); + return r = r.filter(e => !(!e.iframes && e.audioCodec) || e.audioCodecList.every(e => { + e = jp(e); + return !i.has(e) + })), My(0, e, r), r + }(c, d.disableVideoCodecList, d.disableAudioCodecList)), r && dm(r) && (c = function(e) { + const t = um(r), + i = e.filter(e => { + e = e.hdcpLevel; + return !e || um(e) <= t + }); + return My(0, e, i), i + }(c)); + var t = null == l ? void 0 : l.maxSecurityLevel, + e = null == d ? void 0 : d.keySystemPreference; + t && e && Ay(t, e) && (c = function(e, t, i) { + function r(e) { + return Ay(e, i) ? n[e] : -1 + } + const n = Ah.getKeySystemSecurityLevel(i), + s = Ah.getKeySystemFormat(i), + a = r(t), + o = e.filter(e => { + e = null !== (e = null === (e = e.allowedCPCMap) || void 0 === e ? void 0 : e[s]) && void 0 !== e ? e : []; + let t = !0; + for (const i of e) + if (t = r(i) <= a, !t) break; + return t + }); + return My(0, e, o), [...o] + }(c, t, e)), c = c.map(t => { + var e; + return t.audioCodecList && t.audioGroupId && ((e = null == (e = a.find(e => e.groupId === t.audioGroupId)) ? void 0 : e.channels) && (t.audioChannelCount = parseInt(e))), t + }); + const h = !(null == d || !d.useMediaKeySystemAccessFilter) && e && navigator && "function" == typeof navigator.requestMediaKeySystemAccess; + return (h ? xy(c, e, u) : $i(c)).pipe(La(e => { + if (0 === e.length || Oy(e)) throw new R(L, f, void 0, "no media option with compatible codecs found in playlist", void 0); + h && My(0, c, e); + const t = navigator && navigator.mediaCapabilities, + n = !(null == d || !d.useMediaCapabilities) && t && "function" == typeof t.decodingInfo; + let i; + return i = n ? function(e, n, s) { + const a = [], + o = Cy(), + l = function(o) { + const l = new Map, + d = navigator && navigator.mediaCapabilities; + return (i, e, t, n, r) => { + const s = { + type: "media-source" + }; + n ? s.video = function(e) { + const t = { + contentType: `video/mp4;codecs=${e}`, + width: i.width, + height: i.height, + bitrate: i.bandwidth || i.avgBandwidth, + framerate: i.iframes ? 8 : i.frameRate + }; + if (i.videoRange) switch (i.videoRange) { + case "PQ": + be.isDolby(e) ? (t.hdrMetadataType = Pm.DoVi, t.colorGamut = "rec2020") : (be.isHEVC(e) || be.isVP09(e)) && (t.hdrMetadataType = Pm.HDR10, t.colorGamut = "rec2020"), t.transferFunction = "pq"; + break; + case "HLG": + t.colorGamut = "rec2020", t.transferFunction = "hlg" + } + return t + }(t) : s.audio = function(e, t, i) { + const r = { + contentType: `audio/mp4;codecs=${e}` + }, + n = fm.getRichestChannelLayoutForGroupId(t.audioGroupId, i); + return n && (r.channels = be.getChannelCount(n).toString(), r.spatialRendering = be.isDolbyAtmos(e, n)), r + }(t, i, e); + e = JSON.stringify(s); + let a; + return l.has(e) ? a = l.get(e) : (a = Fr(d.decodingInfo(s)).pipe(hr(e => { + const t = e.configuration || e.supportedConfiguration, + i = t instanceof Object && (!s.video || null == Object.keys(s.video).find(e => !(e in t.video))) && (!s.audio || null == Object.keys(s.audio).find(e => !(e in t.audio))), + r = e.supported && (!n || e.powerEfficient) && i; + return r || o.warn(wy, `Unsupported config ${e.supported}/${e.powerEfficient}/${i} ${JSON.stringify(s)} supportedConfig=${JSON.stringify(t)}`), r + })), l.set(e, a)), [...r, a] + } + }(s); + return e.forEach(t => { + var e; + let i = []; + if (null === (e = t.videoCodecList) || void 0 === e || e.forEach(e => { + i = l(t, n, e, !0, i) + }), 0 < (null === (e = t.audioCodecList) || void 0 === e ? void 0 : e.length)) { + const s = fm.getRichestAudioCodec(t.audioCodecList); + i = l(t, n, s, !1, i) + } + let r = $i(t); + 0 < i.length && (r = en(i).pipe(hr(e => null == e.find(e => !1 === e) ? t : null), Vn(e => (s.warn(wy, `decodingInfo errror: ${e.message}`), $i(o(t) ? t : null))))), a.push(r) + }), en(a).pipe(hr(e => e.filter(e => Boolean(e)))) + }(e, a, u) : $i(e = Py((r = e, s = Cy(), s = r.filter(s), My(0, r, s), e = s), a)), i.pipe(hr(e => { + if (0 === e.length || Oy(e)) throw new R(L, f, void 0, "no media option with compatible codecs found in manifest", void 0); + if (0 === (t = e = Ry(e, o, l), r = t.filter(e => !e.iframes || !e.width || !e.height || e.width * e.height <= 2488320), My(0, t, r), (e = r).length) || Oy(e)) throw new R(L, f, void 0, "no media option with compatible codecs found in manifest", void 0); + var t; + let i = (null == l ? void 0 : l.videoDynamicRangeFormats) || []; + n && 0 === i.length && (i = [{ + type: fe.SDR + }, { + type: fe.HDR + }, { + type: fe.HDR10 + }, { + type: fe.DolbyVision + }, { + type: fe.HLG + }]); + var { + hdrMediaOptions: r, + sdrMediaOptions: e + } = function(e, t) { + const i = t.reduce((e, t) => { + switch (t.type) { + case fe.DolbyVision: + e.doViSupported = !0; + break; + case fe.HDR10: + e.hdr10Supported = !0; + break; + case fe.HLG: + e.hlgSupported = !0 + } + return e + }, { + doViSupported: !1, + hdr10Supported: !1, + hlgSupported: !1 + }), + { + doViSupported: r, + hdr10Supported: n, + hlgSupported: s + } = i; + return e.reduce((e, t) => { + var i; + switch (be.getDynamicRangeType(t.videoRange, null !== (i = t.videoCodec) && void 0 !== i ? i : "")) { + case fe.HDR: + case fe.HDR10: + n && e.hdrMediaOptions.push(t); + break; + case fe.DolbyVision: + r && e.hdrMediaOptions.push(t); + break; + case fe.HLG: + s && e.hdrMediaOptions.push(t); + break; + default: + "SDR" !== t.videoRange && null != t.videoRange || e.sdrMediaOptions.push(t) + } + return e + }, { + hdrMediaOptions: new Array, + sdrMediaOptions: new Array + }) + }(e, i); + if (0 === r.length && 0 === e.length || Oy(r) && Oy(e)) throw new R(L, "manifestIncompatibleVideoRangeError", void 0, "mediaOption with compatible VIDEO-RANGE not found in manifest", void 0); + return { + hdrMediaOptions: r, + sdrMediaOptions: e + } + }), Vn(e => { + throw e instanceof R && (e.fatal = !0, e.response = $.IncompatibleAsset), e + })); + var r, s + })) + } + + function _y(e, t) { + return t.mediaOptionId !== e.mediaOptionId && t.persistentID === e.persistentID && t.groupId !== e.groupId + }(A = Pm = Pm || {}).HDR10 = "smpteSt2086", A.DoVi = "smpteSt2094-10", A.HDR10Plus = "smpteSt2094-40"; + class Ny extends jm { + constructor(e, t, i) { + super(e, t, i) + } + static makeFilters() { + return Hm() + } + _initFilters() { + return Ny.kAllowFilters + } + get _mediaOptionType() { + return this.mediaOptionType + } + get preferredHost() { + return null + } + get preferredHost$() { + return $i(null) + } + get mediaOptionListInfo() { + var e; + return null !== (e = null === (e = this.getEntity(this.itemId)) || void 0 === e ? void 0 : e.mediaOptionListTuple[this._mediaOptionType]) && void 0 !== e ? e : null + } + get mediaOptionListInfo$() { + return this.selectEntity(this.itemId, e => e && e.mediaOptionListTuple ? e.mediaOptionListTuple[this._mediaOptionType] : null).pipe(Kp()) + } + getFallbackVariant(t, e, i, r) { + var n; + const s = null === (n = this.mediaOptionList) || void 0 === n ? void 0 : n.find(e => e.mediaOptionId === t); + if (!s) return null; + const a = this.filteredMediaOptionList; + if (!a) return null; + const o = Eu(s.url); + if (i) return null !== (i = a.find(e => _y(s, e) && !Au(o, e.url))) && void 0 !== i ? i : null; + let l = null; + for (const t of a) !_y(s, t) || l && !Au(o, t.url) || (l = t); + return l + } + getMatchingAlternateWithPersistentId(t, i, r) { + var e; + return null !== (e = this.preferredMediaOptionList.find(e => !(0 < (null == r ? void 0 : r.length) && r.includes(e.mediaOptionId)) && (!ne(t) || e.persistentID === t) && (!i || this.matchGroup(e, i.audioGroupId, i.subtitleGroupId, i.closedcaption)))) && void 0 !== e ? e : null + } + matchGroup(e, t, i, r) { + let n = !1; + switch (e.type) { + case "CLOSED-CAPTIONS": + n = !r || e.groupId === r; + break; + case "SUBTITLES": + n = !i || e.groupId === i; + break; + case "AUDIO": + n = !t || e.groupId === t + } + return n + } + getMatchingAlternate(e, t) { + e = this.mediaOptionFromId(e); + return this.getMatchingAlternateWithPersistentId(null == e ? void 0 : e.persistentID, t, []) + } + packageAlternateMediaOption(e, t, i) { + return t.mediaType === Su.CLOSEDCAPTION ? this.augmentClosedCaptionsWithForcedSubtitles(null == e ? void 0 : e.subtitleGroupId, t, i) : t + } + augmentClosedCaptionsWithForcedSubtitles(e, t, i) { + i = this.pairForcedSubtitleMediaOptionWithClosedCaption(e, t, i); + return i ? Object.assign(Object.assign({}, t), { + url: i.url, + backingMediaOptionId: i.mediaOptionId + }) : t + } + pairForcedSubtitleMediaOptionWithClosedCaption(t, i, r) { + let n; + if (i && i.mediaType === Su.CLOSEDCAPTION) { + let e = this.mediaOptionList; + r && (e = this.preferredMediaOptionList), n = Ny.pairForcedSubtitleMediaOptionWithClosedCaptionInList(t, i, e) + } + return n + } + static pairForcedSubtitleMediaOptionWithClosedCaptionInList(t, i, e) { + return e.find(function(e) { + return e.mediaType === Su.SUBTITLE && e.lang === i.lang && e.forced && e.autoselect && (!t || e.groupId === t) + }) + } + } + Ny.kAllowFilters = Ny.makeFilters(); + class Fy extends kl { + constructor(e, t) { + super(e), this.itemId = t, this.mediaOptionListQueries = [new Xm(e, this.itemId), new Ny(e, this.itemId, gu.AltAudio), new Ny(e, this.itemId, gu.Subtitle)] + } + get rootPlaylistEntity() { + return this.getEntity(this.itemId) + } + get rootMediaOptionsTuple() { + var e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.mediaOptionListTuple; + return e ? [e[0].mediaOptions, e[1].mediaOptions, e[2].mediaOptions] : [ + [], + [], + [] + ] + } + get itemStartOffset() { + var e, t; + return null !== (e = this.rootPlaylistEntity) && void 0 !== e && e.itemStartOffset && ne(null === (t = this.rootPlaylistEntity) || void 0 === t ? void 0 : t.itemStartOffset) ? null === (t = this.rootPlaylistEntity) || void 0 === t ? void 0 : t.itemStartOffset : 0 + } + get highestVideoCodec() { + var e; + return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.highestVideoCodec + } + get baseUrl() { + var e; + return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.baseUrl + } + get anchorTime() { + var e; + return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.anchorTime + } + get discoSeqNum() { + var e; + return null !== (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.discoSeqNum) && void 0 !== e ? e : NaN + } + get discoSeqNum$() { + return this.selectEntity(this.itemId, "discoSeqNum") + } + get audioMediaSelectionGroup() { + var e; + return null !== (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.audioMediaSelectionGroup) && void 0 !== e ? e : null + } + get subtitleMediaSelectionGroup() { + var e; + return null !== (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.subtitleMediaSelectionGroup) && void 0 !== e ? e : null + } + get audioMediaSelectionOptions() { + var e; + return null !== (e = null === (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.audioMediaSelectionGroup) || void 0 === e ? void 0 : e.MediaSelectionGroupOptions) && void 0 !== e ? e : [] + } + get subtitleMediaSelectionOptions() { + var e; + return null !== (e = null === (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.subtitleMediaSelectionGroup) || void 0 === e ? void 0 : e.MediaSelectionGroupOptions) && void 0 !== e ? e : [] + } + get contentSteeringOption() { + var e; + return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.contentSteeringOption + } + get masterVariableList() { + var e; + return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.masterVariableList + } + get loadStats() { + var e; + return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.loadStats + } + get isMediaPlaylist() { + var e; + return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.isMediaPlaylist + } + getInitPTS(e) { + var t; + return null === (t = this.rootPlaylistEntity) || void 0 === t ? void 0 : t.initPtsRecord[e] + } + get abrStatus$() { + return this.selectEntity(this.itemId, e => null == e ? void 0 : e.abrStatus) + } + get abrStatus() { + var e; + return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.abrStatus + } + get nextMaxAutoOptionId() { + var e; + return null === (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.abrStatus) || void 0 === e ? void 0 : e.nextMaxAutoOptionId + } + get nextMinAutoOptionId() { + var e; + return null === (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.abrStatus) || void 0 === e ? void 0 : e.nextMinAutoOptionId + } + initPTS$(t) { + return this.selectEntity(this.itemId, ({ + initPtsRecord: e + }) => e[t]) + } + get rootPlaylistEntity$() { + return this.selectEntity(this.itemId).pipe(ln(e => Boolean(e)), hr(e => e)) + } + get rootPlaylistEntityAdded$() { + return this.selectEntityAction(Eo.Add).pipe(hr(e => e.map(e => this.getEntity(e)))) + } + get rootMediaOptionsTuple$() { + return ed([this.selectEntity(this.itemId, e => e.mediaOptionListTuple[0].mediaOptions), this.selectEntity(this.itemId, e => e.mediaOptionListTuple[1].mediaOptions), this.selectEntity(this.itemId, e => e.mediaOptionListTuple[2].mediaOptions)]) + } + get sessionData() { + var e; + return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.sessionData + } + get sessionData$() { + return this.selectEntity(this.itemId, ({ + sessionData: e + }) => e).pipe(Kp()) + } + get anchorTime$() { + return this.selectEntity(this.itemId, "anchorTime").pipe(La(e => { + var t; + return ne(null == e ? void 0 : e.pos) ? (null == e ? void 0 : e.pos) !== (null === (t = this.anchorTime) || void 0 === t ? void 0 : t.pos) ? (Qe().warn(`anchorTime doesn't match stored value! ${null==e?void 0:e.pos} !== ${null===(t=this.anchorTime)||void 0===t?void 0:t.pos}`), Ii) : $i(e) : Ii + })) + } + get pendingSeek$() { + return this.selectEntity(this.itemId, ({ + pendingSeek: e + }) => e).pipe(Is((e, t) => e === t || "number" == typeof e && "number" == typeof t && isNaN(e) && isNaN(t))) + } + get enabledMediaOptionKeys$() { + return this.selectEntity(this.itemId, "enabledMediaOptionKeys").pipe(ln(e => Boolean(e))) + } + get enabledMediaOptionKeys() { + var e; + return null !== (e = null === (e = this.getEntity(this.itemId)) || void 0 === e ? void 0 : e.enabledMediaOptionKeys) && void 0 !== e ? e : [Lu, Lu, Lu] + } + get enabledMediaOptionSwitchContexts() { + var e; + return null !== (e = null === (e = this.getEntity(this.itemId)) || void 0 === e ? void 0 : e.mediaOptionSwitchContexts) && void 0 !== e ? e : [null, null, null] + } + enabledMediaOptionSwitchContextsByType$(t) { + return this.selectEntity(this.itemId, "mediaOptionSwitchContexts").pipe(hr(e => null == e ? void 0 : e[t])) + } + get enabledMediaOptions$() { + return ed([this.enabledMediaOptionByType$(gu.Variant), this.enabledMediaOptionByType$(gu.AltAudio), this.enabledMediaOptionByType$(gu.Subtitle)]) + } + get enabledAVOptions$() { + return ed([this.enabledMediaOptionByType$(gu.Variant), this.enabledMediaOptionByType$(gu.AltAudio)]) + } + rawEnabledMediaOptionByType$(t) { + return this.enabledMediaOptionKeys$.pipe(hr(e => { + const i = e[t]; + return _u(i) && this.rootMediaOptionsTuple[t].find(e => { + return t = i, e.itemId === t.itemId && e.mediaOptionId === t.mediaOptionId; + var t + }) || Lu + })) + } + enabledMediaOptionByType$(e) { + return this.rawEnabledMediaOptionByType$(e).pipe(Is((e, t) => e.mediaOptionId === t.mediaOptionId && e.url === t.url)) + } + enabledMediaOptionSwitchForType$(e) { + return this.rawEnabledMediaOptionByType$(e).pipe(bo(this.enabledMediaOptionSwitchContextsByType$(e)), Ra(null), ha(), hr(([e, t]) => ({ + fromId: null == e ? void 0 : e[0].mediaOptionId, + toId: null == t ? void 0 : t[0].mediaOptionId, + switchContext: null == t ? void 0 : t[1] + })), Is((e, t) => e.fromId === t.fromId && e.toId === t.toId)) + } + enableMediaOptionSwitchedForType$(t) { + return this.enabledMediaOptionByType$(t).pipe(La(e => Gu(Mr([$i(e), this.enabledMediaOptionSwitchContextsByType$(t).pipe(ha())]), ([, e]) => e[0] && !e[1])), hr(([e]) => e)) + } + enabledMediaOptionIdByType(e) { + return this.getEntity(this.itemId).enabledMediaOptionKeys[e].mediaOptionId + } + get enabledVariantMediaOptionIdBeforeTrickplaySwitch() { + return this.getEntity(this.itemId).enabledVariantMediaOptionIdBeforeTrickplaySwitch + } + variantMediaOptionById(e) { + return this.mediaOptionListQueries[gu.Variant].mediaOptionFromId(e) + } + alternateMediaOptionById(e, t) { + return this.mediaOptionListQueries[e].mediaOptionFromId(t) + } + enabledAlternateMediaOptionByType(e) { + var t = this.enabledMediaOptionIdByType(e); + return this.alternateMediaOptionById(e, t) + } + get enabledVariantMediaOption() { + var e = this.enabledMediaOptionIdByType(gu.Variant); + return this.variantMediaOptionById(e) + } + lastLoadedMediaOptionByType(e) { + var t; + return null === (t = this.getEntity(this.itemId).lastLoadedMediaOptionKeys) || void 0 === t ? void 0 : t[e] + } + get nextMediaOptionsKeys$() { + return this.selectEntity(this.itemId, "nextMediaOptionKeys") + } + get preferredMediaOptions() { + return [this.mediaOptionListQueries[0].preferredMediaOptionList, this.mediaOptionListQueries[1].preferredMediaOptionList, this.mediaOptionListQueries[2].preferredMediaOptionList] + } + get preferredMediaOptions$() { + return ed([this.mediaOptionListQueries[0].preferredMediaOptionList$, this.mediaOptionListQueries[1].preferredMediaOptionList$, this.mediaOptionListQueries[2].preferredMediaOptionList$]) + } + get filteredMediaOptions() { + return [this.mediaOptionListQueries[0].filteredMediaOptionList, this.mediaOptionListQueries[1].filteredMediaOptionList, this.mediaOptionListQueries[2].filteredMediaOptionList] + } + getDisabledMediaOption(e) { + return { + itemId: this.itemId, + mediaOptionType: e, + mediaOptionId: "Nah" + } + } + getEnabledMediaOptionMask() { + return this.enabledMediaOptionKeys.map(e => _u(e)) + } + getPreferredMediaOptionsByType$(e) { + return this.mediaOptionListQueries[e].preferredMediaOptionList$ + } + altMediaOptionHasValidUrl(e, t) { + t = this.alternateMediaOptionById(e, t); + return Boolean(null == t ? void 0 : t.url) + } + get hdrMode$() { + return this.mediaOptionListQueries[gu.Variant].hdrMode$ + } + get maxHdcpLevel$() { + return this.mediaOptionListQueries[gu.Variant].maxHdcpLevel$ + } + get currentPathwayID() { + return this.mediaOptionListQueries[gu.Variant].currentPathwayID + } + get preferredHost() { + return this.mediaOptionListQueries[gu.Variant].preferredHost + } + getErrorInfoByType(e) { + var t; + return null != (null === (t = this.rootPlaylistEntity) || void 0 === t ? void 0 : t.errorsByType) ? this.rootPlaylistEntity.errorsByType[e] : null + } + getInFlightFragByType(e) { + var t; + return null !== (e = null === (t = null === (t = this.getEntity(this.itemId)) || void 0 === t ? void 0 : t.inFlightFrags) || void 0 === t ? void 0 : t[e]) && void 0 !== e ? e : null + } + getInFlightFragByType$(t) { + return this.selectEntity(this.itemId, e => { + return null === (e = null == e ? void 0 : e.inFlightFrags) || void 0 === e ? void 0 : e[t] + }) + } + matchAlternates(e, t, i, r) { + t = ne(t) ? this.mediaOptionListQueries[gu.AltAudio].getMatchingAlternateWithPersistentId(t, e, r) : void 0, r = ne(i) ? this.mediaOptionListQueries[gu.Subtitle].getMatchingAlternateWithPersistentId(i, e, r) : void 0; + return [t || Lu, r || Lu] + } + getLegacyMatchingAlternateWithPersistentId(e, t, i) { + let r = this.mediaOptionListQueries[e].getMatchingAlternateWithPersistentId(t, i, []); + return r = r || this.mediaOptionListQueries[e].getMatchingAlternateWithPersistentId(t, void 0, []), r + } + isValidMediaOptionTuple(i, e) { + const r = e || this.getEnabledMediaOptionMask(); + return [gu.Variant, gu.AltAudio, gu.Subtitle].reduce((e, t) => e && r[t] === _u(i[t]), !0) + } + matchGroup(e, t, i, r) { + var n = e.mediaOptionType; + return this.mediaOptionListQueries[n].matchGroup(e, t, i, r) + } + get preferHDR() { + return this.mediaOptionListQueries[gu.Variant].mediaOptionListInfo.preferHDR + } + } + const By = { + name: "rps" + }; + class Uy { + constructor(e, t) { + this.store = e, this.logger = t + } + getQuery() { + return new kl(this.store) + } + getQueryForId(e) { + return new Fy(this.store, e) + } + set rootPlaylistEntity(e) { + Do("root.add.rootPlaylist"), this.store.add(e) + } + removeItems(e) { + Do(`root.add.remove ${JSON.stringify(e)}`), this.store.remove(e) + } + removeAll() { + Do("root.add.clear"), this.store.remove() + } + setRootPlaylistEntity(e, t) { + Do("root.set.rootPlaylistEntity"), this.store.update(e, e => t) + } + setSessionData(e, t) { + Do("root.set.sessionData"), this.store.update(e, e => { + e.sessionData = t + }) + } + setAnchorTime(e, t) { + Do(`root.set.anchorTime: ${null==t?void 0:t.pos} ${null==t?void 0:t.discoSeqNum}`), this.store.update(e, e => { + e.anchorTime = t + }) + } + setDiscoSeqNum(e, t) { + Do(`root.set.discoSeqNum: ${t}`), this.store.update(e, e => { + e.discoSeqNum = t + }) + } + setPendingSeek(e, t) { + Do("root.set.pendingSeek"), this.store.update(e, e => { + e.pendingSeek = t + }), void 0 === t && fg().setUserSeek(t) + } + setEnabledMediaOptionSwitchContextByType(e, i, r, n) { + this.store.update(e, e => { + var t; + if (e.enabledMediaOptionKeys[i].mediaOptionId === r) { + const r = null !== (t = e.mediaOptionSwitchContexts) && void 0 !== t ? t : [null, null, null]; + r[i] = n ? { + userInitiated: n.userInitiated, + switchPosition: n.switchPosition + } : null, e.mediaOptionSwitchContexts = r + } else Do(`root.set.mediaOptionSwitchContextByType ${r} doesn't match existing mediaOption ${e.enabledMediaOptionKeys[i].mediaOptionId}`) + }) + } + setEnabledVariantMediaOptionIdBeforeTrickplaySwitch(e, t) { + this.store.update(e, e => { + e.enabledVariantMediaOptionIdBeforeTrickplaySwitch = t + }) + } + setEnabledMediaOptionByType(r, n, s, a = !1, o) { + s = s || { + itemId: r, + mediaOptionType: n, + mediaOptionId: "Nah" + }, this.store.update(r, e => { + var t; + const i = null !== (t = [...e.enabledMediaOptionKeys]) ? t : [Lu, Lu, Lu]; + if (i[n] = { + itemId: r, + mediaOptionId: s.mediaOptionId + }, this._updateEnabledMediaOptionKeys(e, i), a) { + const r = null !== (t = e.mediaOptionSwitchContexts) && void 0 !== t ? t : [null, null, null]; + r[n] = o ? { + userInitiated: o.userInitiated, + switchPosition: o.switchPosition + } : null, e.mediaOptionSwitchContexts = r + } + }) + } + _associateForcedSubtitleWithClosedCaption(e, t, i, r) { + if ((null == i ? void 0 : i.mediaType) === Su.CLOSEDCAPTION) { + t = r.variantMediaOptionById(t), r = r.mediaOptionListQueries[gu.Subtitle].packageAlternateMediaOption(t, i, !0); + if (r.url !== i.url) { + const n = jy(t, r, e.mediaOptionListTuple[gu.Subtitle].mediaOptions, Qe()); + e.mediaOptionListTuple[gu.Subtitle].mediaOptions = n + } + } + } + _updateEnabledMediaOptionKeys(t, i) { + var e, r; + const n = null !== (e = t.enabledMediaOptionKeys) && void 0 !== e ? e : [Lu, Lu, Lu]; + let s; + for (let e = 0; e < i.length; ++e) { + var a = i[e], + o = n[e].mediaOptionId !== a.mediaOptionId; + if (o && (n[e] = Object.assign({}, a)), e === gu.Variant) { + const i = this.getQueryForId(a.itemId).mediaOptionListQueries[e].mediaOptionList; + o ? t.abrStatus = (r = a.mediaOptionId, o = i, o = Xg(r, o), { + fragDownloadSlow: !1, + fragDownloadTooSlow: !1, + nextMinAutoOptionId: Lu.mediaOptionId, + nextMaxAutoOptionId: Lu.mediaOptionId, + highBWTrigger: o + }) : t.abrStatus.highBWTrigger = Xg(a.mediaOptionId, i), s = a + } else if (e === gu.Subtitle && _u(a)) { + const i = this.getQueryForId(a.itemId), + n = i.alternateMediaOptionById(e, a.mediaOptionId); + this._associateForcedSubtitleWithClosedCaption(t, s.mediaOptionId, n, i) + } + } + t.enabledMediaOptionKeys = n, t.nextMediaOptionKeys = void 0 + } + setManualMode(e, t) { + this.store.update(e, e => { + e.manualMode = t + }) + } + setEnabledMediaOptions(e, i) { + this.store.update(e, e => { + var t = i.map(({ + mediaOptionId: e, + itemId: t + }) => ({ + mediaOptionId: e, + itemId: t + })); + this._updateEnabledMediaOptionKeys(e, t) + }) + } + setEnabledMediaOptionsAndSwitchContexts(e, i, r) { + this.store.update(e, e => { + var t = i.map(({ + mediaOptionId: e, + itemId: t + }) => ({ + mediaOptionId: e, + itemId: t + })); + this._updateEnabledMediaOptionKeys(e, t), e.mediaOptionSwitchContexts = r + }) + } + setNextMediaOptions(e, i) { + Do(`root.set.nextMediaOptions: ${JSON.stringify(null==i?void 0:i.map(e=>e.mediaOptionId))}`), this.store.update(e, e => { + var t = i ? i.map(({ + itemId: e, + mediaOptionId: t + }) => ({ + itemId: e, + mediaOptionId: t + })) : null; + e.nextMediaOptionKeys = t + }) + } + updateEnabledMediaOptions(e) { + Do("root.set.updateEnabledMediaOptions"), this.store.update(e, e => { + e.nextMediaOptionKeys && !0 !== e.manualMode && (Do(`root.set.updateEnabledMediaOptions ${JSON.stringify(e.nextMediaOptionKeys)}`), this._updateEnabledMediaOptionKeys(e, [...e.nextMediaOptionKeys])), e.nextMediaOptionKeys = void 0 + }) + } + setLastLoadedMediaOptionByType(r, n, s) { + Do(`root.set.lastLoadedMediaOptionByType: ${n} ${(s=s||{itemId:r,mediaOptionType:n,mediaOptionId:"Nah"}).mediaOptionId}`), this.store.update(r, e => { + var t; + const i = null !== (t = e.lastLoadedMediaOptionKeys) && void 0 !== t ? t : [Lu, Lu, Lu]; + i[n] = { + itemId: r, + mediaOptionId: s.mediaOptionId + }, e.lastLoadedMediaOptionKeys = i + }) + } + setPreferredHost(e, t) { + Do(`root.set.preferredHost: ${t}`), this.store.update(e, e => { + e && (e.mediaOptionListTuple[gu.Variant].preferredHost = t) + }) + } + setViewportInfo(e, t) { + Do(`root.set.viewportInfo: ${JSON.stringify(t)}`), this.store.update(e, e => { + e && (e.mediaOptionListTuple[gu.Variant].viewportInfo = t) + }) + } + static getExistingPersistentIds(e) { + var t; + const i = {}, + r = null === (t = e.enabledMediaOptionKeys[gu.AltAudio]) || void 0 === t ? void 0 : t.mediaOptionId; + if ("Nah" !== r) { + const s = e.mediaOptionListTuple[gu.AltAudio], + t = Km(s.mediaOptions, Ny.kAllowFilters, s).find(e => e.mediaOptionId === r); + i.audioPersistentId = null == t ? void 0 : t.persistentID + } + const n = null === (t = e.enabledMediaOptionKeys[gu.Subtitle]) || void 0 === t ? void 0 : t.mediaOptionId; + if ("Nah" !== n) { + const s = e.mediaOptionListTuple[gu.Subtitle], + t = Km(s.mediaOptions, Ny.kAllowFilters, s).find(e => e.mediaOptionId === n); + i.subtitlePersistentId = null == t ? void 0 : t.persistentID + } + return i + } + static doUpdateRootHDRSwitch(e, t, i, r) { + const n = e.mediaOptionListTuple.map(e => Object.assign({}, e)); + n[gu.Variant].preferHDR = t, n[gu.Variant].hasHdrLevels = i; + const s = mg(), + a = sy.getEntity(e.itemId), + o = Zf(e.itemId), + l = o.getBandwidthEstimate(s, null == a ? void 0 : a.serviceName), + d = o.getPlaylistEstimate(s, null == a ? void 0 : a.serviceName), + u = o.getFragEstimate(s, null == a ? void 0 : a.serviceName), + c = o.getBufferEstimate(s, null == a ? void 0 : a.serviceName), + h = { + targetDuration: u.maxDurationSec || (null == s ? void 0 : s.defaultTargetDuration), + targetStartupMs: null == s ? void 0 : s.targetStartupMs + }, + p = Uy.getExistingPersistentIds(e); + return Qy(Object.assign(Object.assign({}, e), { + mediaOptionListTuple: n, + nextMediaOptionKeys: null + }), p, r, l, h, d, u, c) + } + switchToSDROnly(e) { + Do("root.switchToSDROnly"), this.store.update(e, e => { + var t = Uy.doUpdateRootHDRSwitch(e, !1, !1, this.logger)["mediaOptionListTuple"]; + e.mediaOptionListTuple = t + }) + } + setHDRPreference(e, i, r) { + Do(`root.set.HDRPreference: ${i}`), this.store.update(e, e => { + var t = e.mediaOptionListTuple[gu.Variant]; + if (t.preferHDR !== i && (!i || t.hasHdrLevels)) { + t = Uy.doUpdateRootHDRSwitch(e, i, t.hasHdrLevels, this.logger); + if (r) return t; + e.mediaOptionListTuple = t.mediaOptionListTuple + } + }) + } + setPathwayPriority(e, i) { + Do(`root.set.PathwayPriority: [ ${i.join(", ")} ]`), this.store.update(e, e => { + if (e) { + const t = e.mediaOptionListTuple[gu.Variant]; + t.pathwayPriority = i, t.preferredHost = null + } + }) + } + setCurrentPathwayID(e, t) { + Do(`root.set.currentPathwayID: ${t}`), this.store.update(e, e => { + e && (e.mediaOptionListTuple[gu.Variant].currentPathwayID = t) + }) + } + setInitPTS(e, t, i, r, n, s) { + Do(`root.set.initPTS: ${e} ${t} variantDTS:${JSON.stringify(i)} timelineOffset: ${r}`), this.store.update(e, e => { + e.initPtsRecord[t] = { + variantDTS: i, + timelineOffset: r, + offsetTimestamp: n, + iframeMode: s + } + }) + } + static prunePenaltyBox(e, t) { + return e.filter(e => !(e.expiry <= t)) + } + static addToPenaltyBox(e, t, i) { + return e.push({ + mediaOptionId: i, + expiry: t + 12e4 + }) + } + addToPenaltyBox(e, r, n) { + Do(`root.set.penaltyBox: ${r}: ${n}`), this.store.update(e, ({ + mediaOptionListTuple: e + }) => { + const t = e[r], + i = performance.now(); + t.penaltyBoxQueue = Uy.prunePenaltyBox(t.penaltyBoxQueue, i), Uy.addToPenaltyBox(t.penaltyBoxQueue, i, n) + }) + } + prunePenaltyBox(e, r = null) { + Do(`root.set.prunePenaltyBox: ${r}`), this.store.update(e, ({ + mediaOptionListTuple: e + }) => { + var e = r ? [e[r]] : e, + t = performance.now(); + for (const i of e) i.penaltyBoxQueue = Uy.prunePenaltyBox(i.penaltyBoxQueue, t) + }) + } + removePermanently(e, r, n) { + Do(`root.set.removePermanently: ${r}: ${n}`), this.store.update(e, ({ + mediaOptionListTuple: e + }) => { + const t = e[r], + i = new Set(t.removed); + i.add(n), t.removed = Array.from(i) + }) + } + moveAllWithMatchingHosts(e, r, n, s) { + Do(`root.set.moveAllMatchingHosts: ${r}:${n} remove:${s}`), this.store.update(e, ({ + mediaOptionListTuple: e + }) => { + const t = e[r], + i = [...t.mediaOptions].filter(e => Au(n, e.url)).map(e => e.mediaOptionId); + if (s) { + const e = new Set([...t.removed, ...i]); + t.removed = Array.from(e) + } else { + const e = performance.now(); + t.penaltyBoxQueue = Uy.prunePenaltyBox(t.penaltyBoxQueue, e); + for (const r of i) Uy.addToPenaltyBox(t.penaltyBoxQueue, e, r) + } + }) + } + setMaxHdcpLevel(e, i, r = !1) { + Do(`root.set.maxHdcpLevel: ${i}`), this.store.update(e, ({ + mediaOptionListTuple: e + }) => { + const t = e[gu.Variant]; + (r || um(i) < um(t.maxHdcpLevel)) && (t.maxHdcpLevel = i) + }) + } + updateConsecutiveTimeouts(e, i, r, n) { + this.store.update(e, e => { + const t = e.errorsByType || [{ + timeouts: { + load: 0, + append: 0, + key: 0 + } + }, { + timeouts: { + load: 0, + append: 0, + key: 0 + } + }, { + timeouts: { + load: 0, + append: 0, + key: 0 + } + }]; + r ? ++t[i].timeouts[n] : t[i].timeouts[n] = 0, e.errorsByType = t + }) + } + updateInflightFrag(l, d, u, c, h) { + Do("root.set.updateInflightFrag"), this.store.update(l, r => { + if (r.inFlightFrags || (r.inFlightFrags = [null, null]), !(d === gu.Subtitle || u && u.itemId !== l)) + if (u) { + let { + start: e, + duration: t + } = u; + var { + mediaOptionId: n, + mediaSeqNum: s, + discoSeqNum: a + } = u, o = r.inFlightFrags[d]; + let i = null == o ? void 0 : o.tstart; + c !== (null == o ? void 0 : o.state) && (i = performance.now()), $p(o, u) && (e = o.start, t = o.duration), r.inFlightFrags[d] = { + itemId: l, + mediaOptionId: n, + mediaSeqNum: s, + discoSeqNum: a, + start: e, + duration: t, + tstart: i, + state: c, + bwSample: Object.assign({}, h) + } + } else r.inFlightFrags[d] = null + }) + } + setNextMaxAutoOptionId(e, t) { + Do(`root.set.nextMaxAutoOptionId: ${t}`), this.store.update(e, ({ + abrStatus: e + }) => { + e.nextMaxAutoOptionId = t + }) + } + setNextMinAutoOptionId(e, t) { + Do(`root.set.nextMinAutoOptionId: ${t}`), this.store.update(e, ({ + abrStatus: e + }) => { + e.nextMinAutoOptionId = t + }) + } + setHighBWTrigger(e, t) { + Do(`root.set.setHighBWTrigger: ${t}`), this.store.update(e, ({ + abrStatus: e + }) => { + e.highBWTrigger = t + }) + } + setFragLoadSlow(e, t) { + Do(`root.set.setFragLoadSlow ${e} ${JSON.stringify(t)}`), this.store.update(e, ({ + abrStatus: e + }) => { + e.fragDownloadSlow = t.fragDownloadSlow, e.fragDownloadTooSlow = t.fragDownloadTooSlow + }) + } + pickMediaOptionTupleByPersistentId(e, t, i, r = !1, n = !1) { + var s = e.enabledMediaOptionIdByType(gu.Variant), + s = e.variantMediaOptionById(s); + let a, o; + if (t === gu.AltAudio) { + const t = e.enabledAlternateMediaOptionByType(gu.Subtitle); + o = null == t ? void 0 : t.persistentID, a = i + } else { + const t = e.enabledAlternateMediaOptionByType(gu.AltAudio); + a = null == t ? void 0 : t.persistentID, o = i + } + const l = e.getEnabledMediaOptionMask(); + return l[t] = !!(ne(i) && 0 <= i), s ? this.getBestMediaOptionTupleFromVariantAndPersistentId(e, s, a, o, l, void 0, r, n, !1) : [Lu, Lu, Lu] + } + getFallbackMediaOptionTupleFromMediaOptionId(e, t, i, r, n = !1, s = !1, a = !1) { + var o = r ? [r] : [i], + l = e.enabledMediaOptionIdByType(gu.Variant), + r = e.variantMediaOptionById(l), + l = t === gu.AltAudio ? e.alternateMediaOptionById(gu.AltAudio, i) : e.enabledAlternateMediaOptionByType(gu.AltAudio), + l = null == l ? void 0 : l.persistentID, + i = t === gu.Subtitle ? e.alternateMediaOptionById(gu.Subtitle, i) : e.enabledAlternateMediaOptionByType(gu.Subtitle), + i = null == i ? void 0 : i.persistentID; + return r ? this.getBestMediaOptionTupleFromVariantAndPersistentId(e, r, l, i, void 0, o, n, s, a) : [Lu, Lu, Lu] + } + hasFallbackMediaOptionTuple(e, t, i, r) { + var n = e.mediaOptionListQueries[t].mediaOptionFromId(i); + return e.isValidMediaOptionTuple(this.getFallbackMediaOptionTupleFromMediaOptionId(e, t, i, n.backingMediaOptionId, !1, r)) + } + setLegacyAlternateMediaOption(e, t, i, r, n) { + var s = e.enabledMediaOptionIdByType(gu.Variant), + s = e.variantMediaOptionById(s), + s = e.getLegacyMatchingAlternateWithPersistentId(i, r, s); + s ? this.setEnabledMediaOptionByType(t, i, s, !0, n) : this.logger.warn(`${Nu[i]} can't find matching mediaOption for persistent id ${r}`) + } + setEnabledMediaOptionTupleWithMatchedGroups(t, i, e, r) { + const n = Ky(t), + s = this.pickMediaOptionTupleByPersistentId(n, i, e); + if (!n.isValidMediaOptionTuple(s)) return this.setLegacyAlternateMediaOption(n, t, i, e, r); + al(() => { + this.setEnabledMediaOptionByType(t, i, s[i], !0, r), s[gu.Variant].mediaOptionId !== n.enabledMediaOptionIdByType(gu.Variant) && this.setPreferredHost(t, Eu(s[gu.Variant].url)), this.setEnabledMediaOptionByType(t, gu.Variant, s[gu.Variant]); + var e = i === gu.AltAudio ? gu.Subtitle : gu.AltAudio; + s[e].mediaOptionId !== n.enabledMediaOptionIdByType(e) && this.setEnabledMediaOptionByType(t, e, s[e], !1) + }) + } + canSwitchToSDR(e, t, i, r = !1) { + var n = e.mediaOptionListQueries[gu.Variant].mediaOptionFromId(t), + r = this.getFallbackMediaOptionTupleFromMediaOptionId(e, gu.Variant, t, n.backingMediaOptionId, !0, i, r); + return e.isValidMediaOptionTuple(r) + } + getBestMediaOptionTupleFromVariantAndPersistentId(t, e, i, r, n, s, a, o, l) { + var d, u = t.mediaOptionListQueries[gu.Variant].listFallbackVariants(e.mediaOptionId, a, o, l, s); + let c = [Lu, Lu, Lu]; + for (let e = 0; e < u.length; ++e) { + const a = u[e]; + if (d = t.matchAlternates(a, i, r, s), t.isValidMediaOptionTuple([a, ...d], n)) { + c = [a, ...d]; + break + } + } + return c + } + } + const $y = new class extends fl { + constructor() { + super({}, { + name: "root-playlist-store", + idKey: "itemId", + producerFn: su + }) + } + akitaPreAddEntity(e) { + return null == e.errorsByType ? Object.assign(Object.assign({}, e), { + errorsByType: [{ + timeouts: { + load: 0, + append: 0, + key: 0 + } + }, { + timeouts: { + load: 0, + append: 0, + key: 0 + } + }, { + timeouts: { + load: 0, + append: 0, + key: 0 + } + }] + }) : e + } + }; + new kl($y); + let Vy = null; + + function Ky(e) { + return new Fy($y, e) + } + const qy = (n, e, t, s, i) => { + const { + rootMediaOptionsTuple: r, + sessionKeys: a + } = n, o = Array.from(r[gu.Variant]), l = Array.from(r[gu.AltAudio]); + let d = !1, + u = !1, + c = o.map(e => (d = d || Boolean(e.videoCodec), u = u || Boolean(e.audioCodec) || Boolean(e.audioGroupId), e)); + return d && u && (c = c.filter(({ + videoCodec: e + }) => Boolean(e))), Ly(o, l, a, e, t, i).pipe(hr(({ + hdrMediaOptions: e, + sdrMediaOptions: t + }) => { + var i = e.concat(t), + r = 0 < e.length; + return e.concat(t), + function(e, t, i, r) { + var { + itemId: n, + itemStartOffset: s, + rootMediaOptionsTuple: a, + audioMediaSelectionGroup: o, + subtitleMediaSelectionGroup: l + } = e, d = Array.from(a[gu.AltAudio]), u = Array.from(a[gu.Subtitle]), c = t.every(e => ne(e.score)), h = t.some(e => Wm(!0, e)), p = function(e, t) { + const i = [...e]; + return t ? i.sort((e, t) => e.score - t.score || t.bitrate - e.bitrate) : i.sort((e, t) => e.bitrate - t.bitrate), i + }(t, c), f = e.baseUrl, t = null === (a = e.contentSteeringOption) || void 0 === a ? void 0 : a.initPathwayID, a = e.sessionData; + return { + itemId: n, + baseUrl: f, + mediaOptionListTuple: [{ + mediaOptions: p, + hasHdrLevels: i, + hasIframeLevels: h, + hasScore: c, + preferHDR: r, + compatibleIds: null, + penaltyBoxQueue: [], + removed: [], + currentPathwayID: t + }, { + mediaOptions: d, + compatibleIds: null, + penaltyBoxQueue: [], + removed: [] + }, { + mediaOptions: u, + penaltyBoxQueue: [], + removed: [] + }], + audioMediaSelectionGroup: o, + subtitleMediaSelectionGroup: l, + enabledMediaOptionKeys: [Lu, Lu, Lu], + mediaOptionSwitchContexts: [null, null, null], + anchorTime: { + pos: 0 + }, + discoSeqNum: NaN, + pendingSeek: void 0, + itemStartOffset: s, + initPtsRecord: {}, + contentSteeringOption: e.contentSteeringOption, + masterVariableList: e.masterVariableList, + loadStats: e.stats, + isMediaPlaylist: e.isMediaPlaylist, + abrStatus: { + fragDownloadSlow: !1, + fragDownloadTooSlow: !1, + nextMinAutoOptionId: Lu.mediaOptionId, + nextMaxAutoOptionId: Lu.mediaOptionId, + highBWTrigger: NaN + }, + sessionData: a + } + }(n, i, r, s) + })) + }; + + function Hy(e, t, i, r, n, s, a) { + var o, l, d, u, c, h, p, f = e.mediaOptionListTuple[gu.Variant], + m = Km(f.mediaOptions, Xm.kAllowFilters, Object.assign(Object.assign({}, f), { + compatibleIds: null + })), + g = qm(f.preferredHost, m); + return { + firstVariant: (o = g, e = Fg, f = f.hasScore, t = t, i = i, r = r, n = n, s = s, a = a, !o || o.length < 1 || o.every(e => e.iframes) ? void t.warn("no non-iframe media option found") : ((o = f ? Bg(o, i, r, n, s, a) : (l = e, d = i, u = r, c = n, h = s, p = a, o.reduce((e, t) => { + if (t.iframes) return e; + let i = e; + const r = function(e, t, i, r, n, s, a) { + var o, l, d = (o = e.bitrate, u = e.height, (l = (e, t, i) => (e - t) * (e - i) <= 0)(o, t.minValidBitrate, t.maxValidBitrate) && l(u, t.minValidHeight, t.maxValidHeight) ? mu.VALID : mu.INVALID), + o = "PQ" === (c = e.videoRange) ? pu.PQ : "HLG" === c ? pu.HLG : "SDR" === c ? pu.SDR : pu.UNKNOWN, + { + videoCodecRank: u, + audioCodecRank: c + } = { + videoCodecRank: qp((l = e).videoCodec), + audioCodecRank: jp(l.audioCodec) + }, + l = e.bitrate < t.maxPreferredBitrate ? mu.VALID : mu.INVALID, + t = e.audioChannelCount || 1, + a = i && r && n && s && !Ug(e, i, r, n, s, a) ? mu.INVALID : mu.VALID; + return new Qp(d, o, u, t, c, a, l, e.height) + }(t, l, d, u, c, h, p); + return (!e || r.isGreaterThan(e.bestRank) || r.isEqualTo(e.bestRank) && t.bitrate > e.selected.bitrate) && (i = { + selected: t, + bestRank: r + }), i + }, null).selected)) || t.warn("no valid first media option found"), o)), + filteredVariants: m, + preferredVariants: g + } + } + + function jy(e, t, i) { + if ((null == t ? void 0 : t.mediaType) === Su.CLOSEDCAPTION) { + const r = Ny.pairForcedSubtitleMediaOptionWithClosedCaptionInList(e.subtitleGroupId, t, i); + if (r) return t = Object.assign(Object.assign({}, t), { + url: r.url, + backingMediaOptionId: r.mediaOptionId + }), i.map(e => e.mediaOptionId === t.mediaOptionId ? t : e) + } + return i + } + + function Qy(e, t, i, r, n, s, a, o) { + var l; + const d = e.itemId, + u = e.mediaOptionListTuple[gu.Variant], + c = e.mediaOptionListTuple[gu.AltAudio], + h = e.mediaOptionListTuple[gu.Subtitle], + p = Km(c.mediaOptions, Ny.kAllowFilters, c), + f = Km(h.mediaOptions, Ny.kAllowFilters, h); + let { + firstVariant: m, + filteredVariants: g + } = Hy(e, i, r, n, s, a, o); + if (!m) { + const U = u.preferHDR; + u.preferHDR = !U && u.hasHdrLevels, u.preferHDR !== U && (i.warn(`No valid first variant found, toggling hdr preference=${U}->${u.preferHDR}`), { + firstVariant: m, + filteredVariants: g + } = Hy(e, i, r, n, s, a, o)) + } + if (!m) throw new V(!0, "No valid first variant found", $.NoValidAlternates); + const y = Eu(m.url), + v = { + itemId: d, + mediaOptionId: null !== (o = null == m ? void 0 : m.mediaOptionId) && void 0 !== o ? o : null + }, + S = null != p && p.length ? null === (o = ((i, r, e, n) => { + if (e) { + let t; + return t = ne(i) ? e.MediaSelectionGroupOptions.find(function(e) { + return e.MediaSelectionOptionsPersistentID === i + }) : e.MediaSelectionGroupOptions.find(function(e) { + return e.MediaSelectionOptionsIsDefault + }), t = t || e.MediaSelectionGroupOptions[0], n.find(e => (!r || e.groupId === r) && e.persistentID === (null == t ? void 0 : t.MediaSelectionOptionsPersistentID)) + } + })(null == t ? void 0 : t.audioPersistentId, m.audioGroupId, e.audioMediaSelectionGroup, p)) || void 0 === o ? void 0 : o.mediaOptionId : null, + b = S ? { + itemId: d, + mediaOptionId: S + } : Lu, + T = ((i, r, n, s, a, o) => { + if (s) { + let t, e; + return t = ne(i) ? s.MediaSelectionGroupOptions.find(function(e) { + return e.MediaSelectionOptionsPersistentID === i + }) : s.MediaSelectionGroupOptions.find(function(e) { + return e.MediaSelectionOptionsIsDefault + }), t && (e = a.find(e => e.mediaType === Su.CLOSEDCAPTION ? (!r || e.groupId === r) && e.persistentID === t.MediaSelectionOptionsPersistentID : e.mediaType === Su.SUBTITLE ? (!n || e.groupId === n) && e.persistentID === t.MediaSelectionOptionsPersistentID : void o.warn(By, `subtitle media option has unknown type ${e.mediaType}`))), e + } + })(null == t ? void 0 : t.subtitlePersistentId, m.closedcaption, m.subtitleGroupId, e.subtitleMediaSelectionGroup, f, i), + E = null != f && f.length ? null == T ? void 0 : T.mediaOptionId : null, + I = E ? { + itemId: d, + mediaOptionId: E, + mediaOptionType: gu.Subtitle + } : Lu, + { + mediaOptions: w, + audioGroups: A, + subtitleGroups: O + } = (t = g, l = m, t.reduce((e, t) => { + if (((e, t) => { + let i = !0; + e.videoCodec && t.videoCodec && (i = be.isCompatibleVideoCodec(e.videoCodec, t.videoCodec)); + let r = !1; + e.videoRange && t.videoRange ? r = e.videoRange == t.videoRange : e.videoRange || t.videoRange || (r = !0); + let n = !0; + return e.audioCodec && t.audioCodec && (n = be.isCompatibleAudioCodec(e.audioCodec, t.audioCodec)), i && r && n + })(l, t)) { + const l = t.audioGroupId; + l && e.audioGroups.add(l), e.mediaOptions.add(t) + } + var i = t.subtitleGroupId; + i && e.subtitleGroups.add(i); + t = t.closedcaption; + return t && e.closedCaptionGroups.add(t), e + }, { + mediaOptions: new Set, + audioGroups: new Set, + subtitleGroups: new Set, + closedCaptionGroups: new Set + })), + k = Array.from(w).map(e => e.mediaOptionId), + C = m.pathwayID, + D = Object.assign(Object.assign({}, u), { + compatibleIds: k, + preferredHost: y, + currentPathwayID: C + }), + M = [], + x = c.mediaOptions.reduce((e, t) => (A.has(t.groupId) && (e.persistentIds.add(t.persistentID), M.push(t.mediaOptionId), e.filteredAudioMediaOptions.push(t), e.altAudio || (e.altAudio = !!t.url)), e), { + filteredAudioMediaOptions: [], + persistentIds: new Set, + altAudio: !1 + }), + P = Object.assign(Object.assign({}, c), { + compatibleIds: M + }); + let R = e.audioMediaSelectionGroup; + const L = null == R ? void 0 : R.MediaSelectionGroupOptions; + if (L) { + const e = L.reduce((e, t) => (x.persistentIds.has(t.MediaSelectionOptionsPersistentID) && e.push(t), e), new Array); + R = Object.assign(Object.assign({}, R), { + MediaSelectionGroupOptions: e + }) + } + h.mediaOptions = jy(m, T, h.mediaOptions); + const _ = h.mediaOptions.reduce((e, t) => (O.has(t.groupId) && (e.persistentIds.add(t.persistentID), e.filteredSubtitleMediaOptions.push(t)), e), { + filteredSubtitleMediaOptions: [], + persistentIds: new Set + }); + let N = e.subtitleMediaSelectionGroup; + const F = null == N ? void 0 : N.MediaSelectionGroupOptions; + if (F) { + const e = F.reduce((e, t) => (_.persistentIds.has(t.MediaSelectionOptionsPersistentID) && e.push(t), e), new Array); + N = Object.assign(Object.assign({}, N), { + MediaSelectionGroupOptions: e + }) + } + i = [D, P, h]; + let B = new Map; + mg().useHighestVideoCodecPrivate && (B = null == D ? void 0 : D.mediaOptions.reduce((e, t) => { + const i = t.videoCodecList; + if (i) + for (const t of i) { + const i = Hp(t), + r = e.get(i); + be.isHigherCodecByFamily(r, t) && e.set(i, t) + } + return e + }, B)), B.size && B.forEach((e, t) => {}); + t = { + fragDownloadSlow: !1, + fragDownloadTooSlow: !1, + nextMinAutoOptionId: Lu.mediaOptionId, + nextMaxAutoOptionId: Lu.mediaOptionId, + highBWTrigger: Xg(v.mediaOptionId, D.mediaOptions) + }; + return Object.assign(Object.assign({}, e), { + enabledMediaOptionKeys: [v, b, I], + mediaOptionListTuple: i, + audioMediaSelectionGroup: R, + abrStatus: t, + highestVideoCodec: B + }) + } + const Wy = (o, l, d, u, c, h, p) => e => e.pipe(tc("retrieveRootMediaOptions.input"), La(t => { + var e; + if (!t) return Ii; + const { + itemId: i, + platformInfo: r + } = t, n = Ky(i), s = l["logger"]; + if (n.hasEntity(i)) return $i(n); + $y.setLoading(!0); + const a = performance.now(); + return function(e, t, u, c, i) { + const { + itemId: h, + url: p, + itemStartOffset: f + } = e, r = Lc(e, t); + return Lm({ + url: p, + onProgress: { + getData: !0, + cb: Iy + }, + xhrSetup: c.xhrSetup + }, r, i).pipe(hr(({ + responseText: e, + responseURL: t, + stats: i + }) => { + var r = c["keySystemPreference"]; + if (t || (u.warn("Missing response url. Reusing request url as base url"), t = p), Rm.isMediaPlaylist(e)) { + const c = "media-pl-" + Zl(), + d = Rm.parseMediaOptionPlaylist(e, t, !0, r, {}, h, c, gu.Variant, u, f); + Nc(d.mediaOptionDetails); + var n = { + itemId: h, + mediaOptionId: c, + mediaOptionType: gu.Variant, + url: p, + bandwidth: 0, + bitrate: 0, + iframes: d.mediaOptionDetails.iframesOnly, + pathwayID: "." + }; + return { + itemId: h, + itemStartOffset: f, + rootMediaOptionsTuple: [ + [n], + [], + [] + ], + stats: i, + baseUrl: t, + initialDetails: d.mediaOptionDetails, + isMediaPlaylist: !0 + } + } { + const u = Rm.parseSessionData(e, t), + c = Rm.parseSessionKeys(e, t, r), + p = Rm.parseRootPlaylist(h, e, t, !0); + if (p.playlistParsingError) throw p.playlistParsingError; + var { + variantMediaOptions: s, + contentSteeringOption: a, + masterVariableList: o + } = p, l = Rm.parseRootPlaylistAlternateMediaOptions(h, e, t, p.variantMediaOptions, !0, o); + if (l.playlistParsingError) throw l.playlistParsingError; + var { + audioAlternateOptions: n, + subtitleAlternateOptions: r, + audioMediaSelectionGroup: e, + subtitleMediaSelectionGroup: l + } = l.alternateMediaInfo; + return { + itemId: h, + itemStartOffset: f, + rootMediaOptionsTuple: [s, n, r], + stats: i, + baseUrl: t, + audioMediaSelectionGroup: e, + subtitleMediaSelectionGroup: l, + contentSteeringOption: a, + sessionData: u, + sessionKeys: c, + masterVariableList: o + } + } + }), e => e.pipe(Vn(e => { + if (e instanceof pc) throw new dc(!1, "Timeout", 0, $.ManifestTimeoutError, !0); + if (e instanceof oc) throw new dc(!1, e.message, e.code, { + code: e.code, + text: "Manifest network error" + }, !1); + throw e + }))) + }(t, o, s, d, null === (e = null === (e = fg()) || void 0 === e ? void 0 : e.getQuery()) || void 0 === e ? void 0 : e.extendMaxTTFB).pipe(Za(e => p.triggerManifestLoaded(e)), Za(({ + initialDetails: e, + stats: t + }) => { + e && (e = e, t = t, Cg().archiveMediaOptionDetails(e, t, !0)) + }), bo(u.displaySupportsHdr$), La(([e, t]) => qy(e, r, d, t, s)), hr(e => (l.rootPlaylistEntity = function(e, t, i, r, n, s) { + const { + itemId: a, + initialSeekTime: o + } = e, l = Zf(a), d = n.enableAdaptiveStartup ? l.getBandwidthEstimate(n, e.serviceName) : void 0, u = n.enableAdaptiveStartup ? l.getPlaylistEstimate(n, e.serviceName) : void 0, c = n.enableAdaptiveStartup ? l.getFragEstimate(n, e.serviceName) : void 0, h = n.enableAdaptiveStartup ? l.getBufferEstimate(n, e.serviceName) : void 0, p = performance.now() - r; + let f; + n.targetStartupMs > p ? f = n.targetStartupMs - p : (f = n.targetStartupMs, s.warn(`Manifest load took ${p}ms and exceeds targetStartupMs: ${n.targetStartupMs}; resetting targetStartupMs to ${n.targetStartupMs}`)); + const m = n.enableAdaptiveStartup ? { + targetDuration: c.maxDurationSec || n.defaultTargetDuration, + targetStartupMs: f + } : void 0, + g = Qy(t, i, s, d, m, u, c, h); + return g.pendingSeek = o, g + }(t, e, c, a, d, s), n)), lg(i, null, Lc(t, o), 0, !1, n, l, h), Vs(() => { + $y.setLoading(!1) + })) + }), tc("retrieveRootMediaOptions.emit")); + + function Gy(t, o, l, d, u, c, h) { + return e => e.pipe(Kp(), La(e => { + return e ? Mr([$i(e).pipe(Wy(t.manifestLoadPolicy, l, t, d, null, u, c)), (n = t, s = o, a = h.mux, new $t(e => { + const t = new Ty(n, s, a); + return e.next(t), () => { + t.destroy() + } + })), (i = t.trickPlaybackConfig, r = o, new $t(e => { + var t = new Zu(i, r); + return e.next(t), () => {} + }))]).pipe(hr(([e, t, i]) => ({ + rootPlaylistQuery: e, + mediaParser: t, + iframeMachine: i + }))) : $i(null); + var i, r, n, s, a + })) + } + wc; + return class zy extends wc { + constructor(e = {}, t) { + var i; + if (super(), this.destroy$ = new Xt, this.mediaElement$ = new yi(null), this.publicQueriesInternal$ = new yi(null), this.mediaElementAdapter = null, this.rpcService = null, this.rpcClients = null, this.platformService = Af(), this.keySystemAdapter = null, this.legibleSystemAdapter = null, this.sessionID = Zl(), this.statsService = (Jf = Jf || new Xf(Yf), Jf), this.gaplessCapable = !0, this.teardownWG$ = new Xp, this.itemQueue = new ay, (e.liveSyncDurationCount || e.liveMaxLatencyDurationCount) && (e.liveSyncDuration || e.liveMaxLatencyDuration)) throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration"); + const m = Object.assign(Object.assign({}, Ru), e); + if (m.maxRequiredStartDuration < m.minRequiredStartDuration || m.minRequiredStartDuration < 0) throw new Error("Illegal config: bad maxRequiredStartDuration or minRequiredStartDuration"); + i = (this.hlsConfig = m).buildType, oe = "production" === i; + const r = this.sessionID; + let n = "silent"; + e.debug && (n = m.debugLevel), au(!0), this.logger = null !== (i = this.logger) && void 0 !== i ? i : ([M, h = {}] = [r, (c = { + sendLogs: m.sendLogs || null, + level: "log" === n ? "debug" : n, + consoleOverride: "boolean" != typeof e.debug ? e.debug : void 0, + buildType: m.buildType + }, x = c.consoleOverride, Object.assign({ + name: "hls", + timestamp: c.sendLogs ? Oe.stdTimeFunctions.epochTime : Oe.stdTimeFunctions.isoTime, + browser: { + asObject: !0, + serialize: !0, + transmit: { + send: (e, t) => {} + }, + write: { + debug: qe.bind(null, Ke(x || console, "debug"), "debug"), + info: qe.bind(null, Ke(x || console, "info"), "info"), + warn: qe.bind(null, Ke(x || console, "warn"), "warn"), + error: qe.bind(null, Ke(x || console, "error"), "error"), + fatal: qe.bind(null, Ke(x || console, "error"), "fatal") + } + } + }, c))], Ue && Ue.sessionId === M ? Ue.warn("Logger Singleton already setup, returning existing singleton") : (Ue = Oe($e(h)).child({ + sessionId: M, + name: "hls" + }), Ue.qe = e => Ue.info(e), Ue.sessionId = M), Ue), _o = !1, Lo && (delete window.$$stores, delete window.$$queries), this.hlsConfig.audioPrimingDelay = 0, this.rootPlaylistService = (x = this.logger, Vy = new Uy($y, x), Vy), this.customUrlLoader = Pc(), this.sessionDataLoader = new Fp(m, Cc, this.customUrlLoader.load, this.logger); + var s, a, o, l, d, u, c = m.liveMaxLatencyDurationCount, + h = m.liveSyncDurationCount; + if (ne(c) && ne(h) && c <= h) throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be gt "liveSyncDurationCount"'); + if (ne(m.liveMaxLatencyDuration) && (m.liveMaxLatencyDuration <= m.liveSyncDuration || !ne(m.liveSyncDuration))) throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be gt "liveSyncDuration"'); + const p = fg(); + p.setHlsEntity({ + id: r, + config: m + }); + const f = Cg(), + g = new Tf(If), + y = (Rh = Rh || new xh(Ph), Rh); + this.accessLogInstance = new rm(this, r), this.rtcService = new Kf(this, m, this.accessLogInstance, this.logger), this.playerEvents = new Ng(this, this.logger, this.rtcService); + const v = (M = this.platformService, u = M, (() => { + if ("function" == typeof matchMedia) { + var e = matchMedia("(dynamic-range: high)"), + t = matchMedia("bad query"); + if (e.media !== t.media) return an($i(e), rn(e, "change")).pipe(hr(e => e.matches)) + } + return $i(!0) + })().pipe(Za(e => { + u.updateSupportsHdr(e) + })).pipe($a(Ii))), + S = this.mediaElement$.pipe((s = m, o = (a = this).logger, l = this.teardownWG$, d = this.rtcService, e => e.pipe(tc("playback.mediaElementServiceEpic.in"), La(e => { + if (!e) return $i(null); + const t = new vf(e, bf, s, a, o, l, d); + return t.openMediaSource(new MediaSource), an($i(t), t) + }), tc("playback.mediaElementServiceEpic.emit"))), Aa()), + b = this.itemQueue.activeItemById$.pipe(La(e => e ? tm(m, this.statsService, e, this.logger) : Ii)); + this.rpcService = (() => { + let e = null; + return null != m.createRPCService && (e = Lf(m.createRPCService, xf)), m.enableWorker && null == e && (e = _f), null == e && (e = xf), e(this.logger) + })(), this.rpcClients = (x = this.rpcService, { + crypto: new kf(x), + mux: new Cf(x) + }); + var T, E, I, w, A, O, k, C, D, c = Mr([this.itemQueue.activeItemById$.pipe(Gy(m, this.logger, this.rootPlaylistService, g, this.statsService, this.playerEvents, this.rpcClients), Za(e => { + var t = null == e ? void 0 : e.rootPlaylistQuery; + this.publicQueriesInternal$.next([t, null]), this.iframeMachine = null == e ? void 0 : e.iframeMachine, t && this.playerEvents.triggerManifestParsed(t) + })), S.pipe((I = this.itemQueue.removedItems$, w = y, A = m, O = g, C = (k = this).rtcService, D = this.logger, e => Mr([((n, s, a, o, l, d, u) => e.pipe(tc("[Keys] playback.keySystemServiceEpic.in"), La(r => r ? new $t(e => { + let t = new Dh(n, r, a, o, l, d, u); + const i = an($i(t), s.pipe(jr(e => t.removeKeysForItems(e)), $a(Ii))).subscribe(e); + return function() { + u.warn("[Keys] playback.keySystemServiceEpic.unsubscribe"), i.unsubscribe(), t.destroy().subscribe(), t = void 0 + } + }) : $i(null)), tc("[Keys] playback.keySystemServiceEpic.emit")))(w, I, A, O, k, C, D), ((t, i, r) => e.pipe(tc("playback.legibleServiceEpic.in"), La(e => e ? an($i(e = new _p(e, t, i, r)), e) : $i(null)), tc("playback.legibleServiceEpic.emit")))(A, k, D), e]).pipe(hr(([e, t, i]) => ({ + keySystemAdapter: e, + legibleSystemAdapter: t, + mediaSink: i + })))), Za(({ + keySystemAdapter: e, + legibleSystemAdapter: t, + mediaSink: i + }) => { + this.keySystemAdapter = e, this.legibleSystemAdapter = t, this.mediaElementAdapter = i + }))]).pipe(hr(([e, t]) => { + var { + keySystemAdapter: i, + legibleSystemAdapter: r, + mediaSink: n + } = t; + if (!(e && i && r && n)) return null; + var { + rootPlaylistQuery: s, + iframeMachine: t, + mediaParser: e + } = e; + return { + logger: this.logger, + config: m, + platformService: this.platformService, + statsService: this.statsService, + rtcService: this.rtcService, + rpcClients: this.rpcClients, + rootPlaylistService: this.rootPlaylistService, + rootPlaylistQuery: s, + mediaLibraryService: f, + keySystemAdapter: i, + legibleSystemAdapter: r, + mediaSink: n, + mediaParser: e, + iframeMachine: t, + customUrlLoader: this.customUrlLoader, + gaplessInstance: this + } + }), Aa()).pipe(La(u => { + if (!u) return Ii; + const { + rootPlaylistQuery: t, + mediaSink: e, + mediaLibraryService: i + } = u; + this.publicQueriesInternal$.next([t, e.mediaQuery]); + const n = e.mediaQuery, + r = $i(u).pipe(vy()), + s = t.rootPlaylistEntity$.pipe(Kp(), Ds(1), Za(() => { + this.commitEarlySelection(u.logger) + })), + a = Gu(Mr([n.haveEnough$, t.sessionData$]), ([e]) => !0 === e, 1).pipe(La(([, e]) => this.sessionDataLoader.loadSessionData(e)), Za(e => { + this.rootPlaylistService.setSessionData(t.itemId, e) + }), Vn(e => (this.logger.error(e.message), Ii))), + o = Zf(t.itemId), + l = function(d, r, u, n, s) { + const a = u.mediaQuery; + return Gu(a.combinedBuffer$, e => 0 < (null == e ? void 0 : e.length)).pipe(La(() => { + var e = ed([a.seekTo$, a.bufferedSegmentsByType$(yu.Variant)]).pipe(hr(([e, t]) => { + const i = ne(null == e ? void 0 : e.pos) ? e.pos : a.currentTime, + r = t.find(e => e.startPTS <= i && e.endPTS > i), + n = a.getBufferInfo(i, d.maxBufferHole), + s = a.getCombinedBufferInfo(i, d.maxBufferHole); + return { + pos: i, + sbTuple: n, + combined: s, + playingFrag: null !== (t = null == r ? void 0 : r.frag) && void 0 !== t ? t : null + } + })), + t = ed([r.getInFlightFragByType$(gu.Variant), r.enabledMediaOptionByType$(gu.Variant)]).pipe(La(([e, t]) => { + var i = n.getQueryForOption(t); + return En($i(e), $i(t), i.mediaOptionDetails$) + }), hr(([e, t, i]) => [e, { + variant: t, + details: i + }])), + i = ed([s.bandwidthEstimate$, s.fragEstimate$, s.bufferEstimate$]); + return ed([a.readyState$, t, e, a.desiredRate$, i]) + }), ao(100, tr, { + leading: !0, + trailing: !0 + }), hr(([, e, t, i, r]) => { + var [n, s] = e, [a, e, r] = r; + let o = e, + l = r; + return e && (o = { + maxDurationSec: ne(e.maxDurationSec) ? e.maxDurationSec : d.defaultTargetDuration, + avgParseTimeMs: ne(e.avgParseTimeMs) ? e.avgParseTimeMs : d.statDefaults.fragParseTimeMs + }), r && (l = { + avgBufferCreateMs: ne(r.avgBufferCreateMs) ? r.avgBufferCreateMs : d.statDefaults.fragBufferCreationDelayMs, + avgInitFragAppendMs: ne(r.avgInitFragAppendMs) ? r.avgInitFragAppendMs : d.statDefaults.initFragAppendMs, + avgDataFragAppendMs: ne(r.avgDataFragAppendMs) ? r.avgDataFragAppendMs : d.statDefaults.dataFragAppendMs + }), a = Ey(d, n, s, t, i, a, o, l), u.haveEnough = a + }), Is(), Za(e => {}), $a(Ii)) + }(m, (this.logger, t), e, i, o), + d = function() { + const { + config: e, + mediaSink: t, + rootPlaylistQuery: i, + mediaLibraryService: r, + gaplessInstance: n + } = u, s = t.mediaQuery, a = i.enabledAVOptions$.pipe(La(e => En(...e.map(e => _u(e) ? r.getQueryForOption(e).mediaOptionDetails$ : $i(null))))); + return Gu(s.combinedBuffer$, e => 0 < (null == e ? void 0 : e.length)).pipe($a(ed([a, s.msReadyState$, s.updating$, s.isIframeRate$, s.isBufferedToEnd$(e.maxBufferHole, !n.inGaplessMode)])), tc("checkForEndOfStream"), ln(([, e, t, i, r]) => "open" === e && !1 === t && !i && r), Za(([e]) => { + null != e[0] && e.every(e => null == e || !1 === e.liveOrEvent && !1 === e.iframesOnly) && !n.inGaplessMode && t.endStream() + }), $a(Ii)) + }(), + c = function() { + const { + config: o, + iframeMachine: l, + mediaSink: i + } = u, d = i.mediaQuery; + return d.desiredRate$.pipe(La(a => Wp(a) ? bn(0, Math.abs(1e3 / a)).pipe(hr(() => { + let e = null; + const t = d.seekable; + if (!l.isStarted || t.length < 1) return e; + var i = l.iframeClockTimeSeconds, + r = o.leftMediaTimeToAutoPause, + n = t.start(0), + s = t.end(t.length - 1); + return 1 < a && s - i < r ? (e = { + newRate: 0, + postFlushSeek: s - r + }, l.pause()) : a < 0 && i - n < a / -2 && (e = { + newRate: 1, + postFlushSeek: n + }), e + }), Kp(), Za(({ + newRate: e, + postFlushSeek: t + }) => { + i.postFlushSeek = t, i.desiredRate = e + }), $a(Ii)) : Ii)) + }(), + h = (f = u).config.enableIFramePreloading ? function(t) { + const { + mediaSink: e, + rootPlaylistQuery: r, + mediaLibraryService: n + } = t, i = e.mediaQuery; + return ed([i.desiredRate$, i.waterLevelChangedForType$(yu.Variant)]).pipe(La(([e, t]) => Wp(e) || t !== Dp.AboveHighWater ? Ii : r.enabledMediaOptionByType$(gu.Variant).pipe(hr(e => { + var t = r.mediaOptionListQueries[gu.Variant].hasIframes, + i = null !== (i = null === (i = n.getQuery().getEntity(r.itemId)) || void 0 === i ? void 0 : i.liveOrEvent) && void 0 !== i && i; + return !t || i ? xm.DISABLED : e + }))), Ds(1), _s(e => function(e, t) { + const i = t.logger; + return e === xm.DISABLED ? $i(e) : function(e, t) { + const { + rootPlaylistQuery: i, + logger: r, + config: n, + mediaSink: s, + statsService: a + } = t, o = s.mediaQuery, l = a.getQueryForItem(i.itemId), d = Dg(e), u = jg(!0, n, i, d, o, l, r), c = i.variantMediaOptionById(u.variantMediaOption); + return Mg(t, c, !0) + }(e, t).pipe(_s(r => function(e) { + var { + mediaSink: t, + rootPlaylistQuery: i + } = e, t = Ig((t = t.mediaQuery).currentTime, i.discoSeqNum, 0, r, []); + return null !== (i = null == t ? void 0 : t.foundFrag) && void 0 !== i && i.mediaFragment ? (t = t.foundFrag.mediaFragment, en([_g(e, t.keyTagInfo, { + itemId: t.itemId, + mediaOptionId: t.mediaOptionId + }), xg(e, t)]).pipe(Za(() => {}), Zs(xm.SUCCESS))) : Vi("Unable to find fragment for iframe prefetch") + }(t)), Zs(xm.SUCCESS), Vn(e => (i.error(ry, `got error ${e.message} in prefetch`), $i(xm.ERRORED)))) + }(e, t)), Vs(() => {})) + }(f) : $i(xm.DISABLED), + p = [s, r, a, l, Gu(ed([n.gotPlaying$, n.gotLoadStart$, n.readyState$]), ([e, t, i]) => !0 === e || !0 === t || 1 <= i).pipe(La(() => n.ended$), La(e => bn(0, e ? void 0 : 1e3)), Za(() => { + this.playbackInfo(m, n) + })), c, h, n.timeupdate$.pipe(hr(e => { + if (this.inGaplessMode && this.isPreloading && ne(this.loadingItem.itemStartOffset) && e >= this.loadingItem.itemStartOffset) { + const e = this.itemQueue.playingItem.itemId, + t = this.itemQueue.loadingItem.itemId, + i = { + prevItemId: e, + nextItemId: t, + nextStartTime: this.loadingItem.itemStartOffset, + nextDuration: n.msDuration - this.loadingItem.itemStartOffset + }; + this.itemQueue.updatePlayingItemId(), this.trigger(P.ITEM_TRANSITIONED, i), this.rtcService.itemTransitioned(e, t) + } + })), this.updateLiveSeekableRange(t, e), d]; + var f; + if (m.enablePerformanceLogging) { + this.logger.child({ + name: "timing" + }); + const u = En(...Bu.map(r => t.getInFlightFragByType$(r).pipe(Is((e, t) => (null == e ? void 0 : e.state) === (null == t ? void 0 : t.state)), Kp(), bo(n.bufferedRangeTuple$), Za(([e, t]) => { + const i = Object.assign(Object.assign({}, e), { + event: "fragment", + name: Nu[r], + buffered: void 0 + }); + "appended" === e.state && (i.buffered = t) + }), Vn(() => Ii)))), + e = En(...Fu.map(e => t.enabledMediaOptionByType$(e).pipe(La(t => null != (null == t ? void 0 : t.url) && _u(t) ? Dg(t).mediaOptionDetailsEntity$.pipe(Kp(), hr(e => ({ + entity: e, + option: t + })), Is((e, t) => (null == e ? void 0 : e.entity.detailsLoading) === (null == t ? void 0 : t.entity.detailsLoading)), Za(({}) => {})) : Ii), Vn(() => Ii)))); + p.push(u, e) + } + return an(...p) + })), + h = this.itemQueue.removedItems$.pipe(ll(e => { + var t; + t = e, Cg().remove(t), this.rootPlaylistService.removeItems(e) + })), + M = p.getQuery().userSeek$.pipe((T = this.itemQueue, E = this.rootPlaylistService, e => e.pipe(Kp(), La(e => en([$i(e), T.activeItemById$.pipe(Kp(), La(e => E.getQueryForId(e.itemId).rootPlaylistEntity$), Ds(1))])), hr(([e, t]) => (E.setPendingSeek(t.itemId, e), e))))), + x = p.getQuery().selectEntityAction(Eo.Add).pipe(Za(() => { + this.logger.warn(`new Hls instance added while old one still active sessionId:${r}`) + })); + an(S.pipe(Vn(() => Ii)), an(v, M, b, h, c, this.teardownWG$).pipe(Vn(e => this._handleError(e)))).pipe(Vs(() => { + var e, t; + try { + this.detachMedia(), this.trigger(P.DESTROYING), this.playerEvents.destroy(), null === (e = this.accessLogInstance) || void 0 === e || e.destroy(), null === (t = this.rtcService) || void 0 === t || t.destroy(), Cg().clear(), this.rootPlaylistService.removeAll(), this.itemQueue.clearQueue(), p.removeEntity(this.sessionID) + } catch (e) { + this.logger.error(`Got error in finalize ${e.message}`) + } + }), Va(fn(this.destroy$, x))).subscribe() + } + get publicQueries$() { + return this.publicQueriesInternal$.pipe(ln(e => Boolean(e) && Boolean(e[0]) && Boolean(e[1]))) + } + get _activeRootQuery() { + var e = this.publicQueriesInternal$.value; + return null !== (e = null == e ? void 0 : e[0]) && void 0 !== e ? e : null + } + get _mediaElementQuery() { + var e = this.publicQueriesInternal$.value; + return null !== (e = null == e ? void 0 : e[1]) && void 0 !== e ? e : null + } + static get version() { + return "2.162.2" + } + static get Events() { + return P + } + get Events() { + return zy.Events + } + static get DefaultConfig() { + return de(Ru) + } + get DefaultConfig() { + return zy.DefaultConfig + } + static isSupported() { + try { + const e = window.MediaSource || window.WebKitMediaSource, + t = window.SourceBuffer || window.WebKitSourceBuffer, + i = e && "function" == typeof e.isTypeSupported && e.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"'), + r = !t || t.prototype && "function" == typeof t.prototype.appendBuffer && "function" == typeof t.prototype.remove; + return !!i && !!r + } catch (e) { + return !1 + } + } + commitEarlySelection(e) { + var t = this.itemQueue.earlyAudioSelection; + ne(t) && (this.audioSelectedPersistentID = t, this.itemQueue.earlyAudioSelection = null), ne(t = this.itemQueue.earlySubtitleSelection) && (this.subtitleSelectedPersistentID = t, this.itemQueue.earlySubtitleSelection = null) + } + _handleError(i) { + var r; + try { + let t, e = i.message; + if (this.logger.error(`Got unhandled or fatal error ${e}`, i), null === (r = this.rtcService) || void 0 === r || r.handleError(i), t = i instanceof p ? i : new V(!0, i.message, $.InternalError), t.fatal && this.isPreloading && (this.logger.warn("Fatal error seen while preloading, calling dequeueSource"), this.dequeueSource("FatalErrorWhileLoading")), t.fatal) { + let e = Wu; + if (this.mediaElementAdapter) { + const r = this.mediaElementAdapter.mediaQuery, + n = r.getCombinedBufferInfo(r.currentTime, 0); + 0 < (null == n ? void 0 : n.len) && (e = Gu(this.mediaElementAdapter.mediaQuery.stallInfo$, e => null != e).pipe(hr(() => {}))) + } + return e.pipe(La(() => (this.trigger(P.ERROR, t), Ii))) + } + this.trigger(P.ERROR, t) + } catch (i) { + throw this.logger.error(`Error thrown inside _handleError ${i.message}`, i), i + } + return Ii + } + updateLiveSeekableRange(e, t) { + return e.enabledMediaOptionByType$(gu.Variant).pipe(La(e => { + const t = Dg(e); + let i = 0; + return t.mediaOptionDetailsEntity$.pipe(Kp(), ln(e => { + var t = null !== e.stats && !1 === e.detailsLoading && e.lastUpdateMillis > i; + return i = null !== (e = e.lastUpdateMillis) && void 0 !== e ? e : 0, t + })) + }), La(e => (0 === e.unchangedCount && (e.mediaOptionDetails.liveOrEvent ? t.updateLiveSeekableRange(e.mediaOptionDetails) : t.clearLiveSeekableRange()), Ii))) + } + playbackInfo(i, r) { + const n = this.mediaElement$.getValue(); + if (n) { + const s = n.readyState >= n.HAVE_FUTURE_DATA, + a = { + readyToPlay: s, + playbackLikelyToKeepUp: r.haveEnough && s, + rate: n.playbackRate, + paused: n.paused, + position: n.currentTime, + duration: n.duration, + seekableTimeRanges: im.timeRangeToArray(n.seekable), + loadedTimeRanges: im.timeRangeToArray(n.buffered) + }; + let e = 0, + t = 0; + if (im.isHtmlVideoElement(n)) { + const o = n.getVideoPlaybackQuality; + if (o && typeof o == typeof Function) { + const o = n.getVideoPlaybackQuality(); + e = a.droppedVideoFrames = o.droppedVideoFrames, a.corruptedVideoFrames = o.corruptedVideoFrames, a.totalVideoFrames = o.totalVideoFrames, t = a.totalVideoFrames - e + } + } else im.isWebkitMediaElement(n) && (e = a.droppedVideoFrames = n.webkitDroppedFrameCount, t = a.decodedFrameCount = n.webkitDecodedFrameCount); + i.enablePerformanceLogging && r.getCombinedMediaSourceBufferInfo(i.maxBufferHole), null === (i = this.rtcService) || void 0 === i || i.handlePlaybackInfo(e, t) + } + } + get currentItem() { + return this.isPreloading ? this.playingItem : this.itemQueue.activeItem + } + get realCurrentTime() { + var e, t = this._mediaElementQuery; + if (!t) return NaN; + if (null !== (e = this.iframeMachine) && void 0 !== e && e.isStarted) { + const r = t.mediaElementDuration, + e = this.iframeMachine.iframeClockTimeSeconds; + return e > r ? r : e + } + let i = ne(t.postFlushSeek) ? t.postFlushSeek : t.currentTime; + return ne(i) && ne(null === (e = this.playingItem) || void 0 === e ? void 0 : e.itemStartOffset) && (i -= this.playingItem.itemStartOffset), i + } + set realCurrentTime(e) { + var t; + ne(null === (t = this.playingItem) || void 0 === t ? void 0 : t.itemStartOffset) && (e += this.playingItem.itemStartOffset), this.seekTo = e + } + get bufferedDuration() { + var e; + const t = this._mediaElementQuery; + return null !== (e = null == t ? void 0 : t.getBufferedDuration()) && void 0 !== e ? e : 0 + } + get sessionData() { + var e = this._activeRootQuery; + return null == e ? void 0 : e.sessionData + } + get supportedFrameRates() { + const e = this.hlsConfig.trickPlaybackConfig.enabled, + t = [0, 1], + i = this._activeRootQuery, + r = Cg().getQuery(); + return e && i && r.getEntity(i.itemId) && !1 === r.getEntity(i.itemId).liveOrEvent && t.push(8, 24, 48, 96), t + } + loadSource(e, i, t) { + var r, n, s, a, o, l; + if ("playready" === this.config.keySystemPreference && !this.config.enablePlayReadyKeySystem) throw new V(!0, "Playready key system is not supported now", $.UnsupportedKeySystemError); + if (!e || !e.trim().length) throw new V(!0, "Empty loadSource url", $.EmptyLoadSourceError); + if (e = bu.buildAbsoluteURL(window.location.href, e, { + alwaysNormalize: !0 + }), i && Object.keys(i).filter(e => 0 <= ["itemId", "streamID"].indexOf(e)).reduce((e, t) => t in i ? Object.assign(e, { + [t]: i[t] + }) : e, {}), null !== (l = null == i ? void 0 : i.appData) && void 0 !== l && l.reportingAgent && (this.reportingAgent = i.appData.reportingAgent), null != i && i.userInfo && (this.userInfo = i.userInfo), null === (a = this.accessLogInstance) || void 0 === a || a.setupReporter(i.appData), null != i && i.platformInfo && this.platformService.updatePlatformInfo(i.platformInfo), function(e, t) { + if (t) { + const t = Eu(e); + return void 0 !== Iu.find(e => new RegExp(e).exec(t)) + } + }(e, this.config.enableQueryParamsForITunes)) { + const d = { + language: i.language, + dsid: i.dsid, + subs: i.subs + }; + r = e, n = null == i ? void 0 : i.platformInfo, s = d, o = n.model, l = n.manufacturer, o && l || Qe().warn(`Missing model/manufacturer in platformInfo model ${o} manufacturer ${l}`), o && l && (a = r, o = n.model, l = n.manufacturer, n = -1 !== a.indexOf("?"), o = encodeURIComponent(o), l = encodeURIComponent(l), r = (a = n ? a : a + "?") + wu.deviceName + l + wu.deviceModel + o), e = function(e, t) { + let i; + e = -1 !== e.indexOf("?") ? e : e + "?"; + const r = Qe(); + for (i in t) t[i] ? e += wu[i] + ("subs" === i ? encodeURIComponent(t[i]) : t[i]) : r.warn(`Missing ${i} info`); + return e + }(r, s), i.inheritQuery = !1 + } + this.itemQueue.setQueueItem(`item:${null!==(s=null==i?void 0:i.itemId)&&void 0!==s?s:Zl()}`, e, t, null == i ? void 0 : i.platformInfo, null === (t = null == i ? void 0 : i.appData) || void 0 === t ? void 0 : t.serviceName), fg().setStartTime(void 0) + } + queueSource(e, t, i) { + var r; + null != t && t.userInfo && (this.userInfo = t.userInfo); + var n = null === (r = this._mediaElementQuery) || void 0 === r ? void 0 : r.getCombinedBufferInfo(null === (n = this._mediaElementQuery) || void 0 === n ? void 0 : n.currentTime, 0); + let s = 0; + n && (s = n.end), this.itemQueue.addQueueItem(`item:${null!==(n=null==t?void 0:t.itemId)&&void 0!==n?n:Zl()}`, e, i, null == t ? void 0 : t.platformInfo, s, null === (t = null == t ? void 0 : t.appData) || void 0 === t ? void 0 : t.serviceName) + } + dequeueSource(e = "ApplicationInitiated") { + if (!this.isPreloading && "InvalidFormat" === e && this.isFirstItem) return this.logger.error("First item has invalid format for gapless. Probably video. Disabling gapless."), void(this.gaplessCapable = !1); + var t, i; + this.isPreloading ? (t = this.loadingItem.url, i = this.loadingItem.itemId, this.mediaElementAdapter.flushData(yu.Variant, this.loadingItem.itemStartOffset, 1 / 0), this.mediaElementAdapter.msDuration = this.loadingItem.itemStartOffset, this.itemQueue.resetLoadingItem(), "InvalidFormat" !== e && "FatalErrorWhileLoading" !== e || (this.gaplessCapable = !1), this.triggerItemEvicted({ + url: t, + itemId: i + }, e)) : this.logger.warn(`Nothing to dequeue, no item is preloading dequeue reason: ${e}`) + } + triggerItemEvicted(e, t) { + null !== e ? (t = { + url: e.url, + evictedItemId: e.itemId, + reason: t + }, Object.assign(Object.assign({}, t), { + url: le(e.url) + }), this.trigger(P.ITEM_EVICTED, t)) : this.logger.error("dequeueSource called with no playing or loading item") + } + endSource() { + this.gaplessCapable = !1, this.isPreloading && (this.logger.warn("EndSource called during preloading. Loading item will be removed"), this.mediaElementAdapter.flushData(yu.Variant, this.loadingItem.itemStartOffset, 1 / 0), this.mediaElementAdapter.msDuration = this.loadingItem.itemStartOffset, this.itemQueue.resetLoadingItem()) + } + get inGaplessMode() { + return mg().gapless && this.gaplessCapable + } + get isPreloading() { + return this.itemQueue.isPreloading() + } + get isFirstItem() { + return this.itemQueue.isFirstItem + } + get loadingItem() { + return this.itemQueue.loadingItem + } + get playingItem() { + return this.itemQueue.playingItem + } + get url() { + return this.playingItem ? this.playingItem.url : this.loadingItem ? this.loadingItem.url : void 0 + } + destroy() { + const t = this.logger; + return this.destroy$.next(), null != this.rpcService && (this.teardownWG$.add(), this.rpcService.teardown(e => { + e && t.error("RPCService teardown error:", e), this.teardownWG$.done() + }), this.rpcService = null), null != this.iframeMachine && (this.iframeMachine.destroy(), this.iframeMachine = null), this.teardownWG$.toPromise() + } + attachMedia(e) { + this.trigger(P.MEDIA_ATTACHING, { + media: e + }), this.mediaElement$.next(e), this.trigger(P.MEDIA_ATTACHED, { + media: e + }) + } + detachMedia() { + var e; + this.mediaElement$.getValue() && (this.trigger(P.MEDIA_DETACHING), null === (e = this.rtcService) || void 0 === e || e.detachMedia(), null != this.iframeMachine && this.iframeMachine.stop(), this.mediaElement$.next(null), this.trigger(P.MEDIA_DETACHED)) + } + handleResolvedUri(e, t) { + this.customUrlLoader.setCustomUrlResponse(e, { + uri: t.uri, + response: t + }) + } + get variantOptions$() { + return this.publicQueries$.pipe(La(e => { + const [t, i] = e; + return Mr([t.preferredMediaOptions$, i.desiredRate$]).pipe(hr(([e]) => { + const t = i.isIframeRate; + return e[gu.Variant].filter(e => (null !== (e = e.iframes) && void 0 !== e && e) === t).map(e => e.mediaOptionId) + })) + })) + } + get altAudioOptions$() { + return this.publicQueries$.pipe(La(e => { + var [e] = e; + return $i(e.audioMediaSelectionOptions) + })) + } + get subtitleOptions$() { + return this.publicQueries$.pipe(La(e => { + var [e] = e; + return $i([{ + MediaSelectionOptionsName: "Disable subtitle", + MediaSelectionOptionsPersistentID: -1 + }].concat(e.subtitleMediaSelectionOptions)) + })) + } + get levels() { + var e; + return null !== (e = null === (e = this._activeRootQuery) || void 0 === e ? void 0 : e.preferredMediaOptions[gu.Variant]) && void 0 !== e ? e : [] + } + get audioTracks() { + var e; + return null !== (e = null === (e = this._activeRootQuery) || void 0 === e ? void 0 : e.preferredMediaOptions[gu.AltAudio]) && void 0 !== e ? e : [] + } + get audioMediaOptions() { + var e; + return null !== (e = null === (e = this._activeRootQuery) || void 0 === e ? void 0 : e.audioMediaSelectionOptions) && void 0 !== e ? e : [] + } + get subtitleMediaOptions() { + var e; + return null !== (e = null === (e = this._activeRootQuery) || void 0 === e ? void 0 : e.subtitleMediaSelectionOptions) && void 0 !== e ? e : [] + } + get playbackLikelyToKeepUp() { + var e; + return null !== (e = null === (e = this._mediaElementQuery) || void 0 === e ? void 0 : e.playbackLikelyToKeepUp) && void 0 !== e && e + } + get duration$() { + return this.publicQueries$.pipe(La(e => { + var [, e] = e; + return e.mediaElementDuration$ + })) + } + get timeupdate$() { + return this.publicQueries$.pipe(La(e => { + var [, e] = e; + return e.timeupdate$ + })) + } + get playing$() { + return this.publicQueries$.pipe(La(e => { + var [, e] = e; + return e.gotPlaying$ + })) + } + get desiredRate$() { + return this.publicQueries$.pipe(La(e => { + var [, e] = e; + return e.desiredRate$ + })) + } + set desiredRate(e) { + null != e && this.setRate(e) + } + get desiredRate() { + var e; + return null !== (e = null === (e = this._mediaElementQuery) || void 0 === e ? void 0 : e.desiredRate) && void 0 !== e ? e : 0 + } + get effectiveRate() { + var e; + return null !== (e = null === (e = this._mediaElementQuery) || void 0 === e ? void 0 : e.effectiveRate) && void 0 !== e ? e : 0 + } + get iframeMode() { + var e; + return null !== (e = null === (e = this._mediaElementQuery) || void 0 === e ? void 0 : e.isIframeRate) && void 0 !== e && e + } + get accessLog() { + return this.accessLogInstance && this._activeRootQuery ? this.accessLogInstance.getAccessLog(this._activeRootQuery.itemId) : [] + } + get errorLog() { + return this.accessLogInstance ? this.accessLogInstance.errorLog : [] + } + setRate(e) { + var t; + const i = this.logger.child({ + name: "iframes" + }); + if (e === this.desiredRate) return -2; + const r = this.mediaElementAdapter; + if (!r || isNaN(e)) return i.warn("unable to switch to rate, missing adapter or newRate isNaN"), -1; + e = Number(e); + const n = Math.abs(e); + if (!this.supportedFrameRates.some(e => e === n)) return i.warn(`unsupported rate(${e})`), -3; + const s = Wp(e), + a = this.iframeMachine; + if (s) { + const e = this._activeRootQuery; + if (null == e || !e.mediaOptionListQueries[gu.Variant].hasIframes) return i.warn("no iframe variants available"), -1; + r.postFlushSeek = null + } else null != a && a.isStarted && !ne(null === (t = r.mediaQuery) || void 0 === t ? void 0 : t.postFlushSeek) && (a.pause(), r.postFlushSeek = a.iframeClockTimeSeconds); + return r.desiredRate = e, 0 + } + get sessionData$() { + return this.publicQueries$.pipe(La(([e]) => e.sessionData$)) + } + set skip(e) { + this._mediaElementQuery && (this.realCurrentTime = Math.max(0, this.realCurrentTime + e)) + } + gaplessSeekTo(e) { + e < this.playingItem.itemStartOffset && (this.logger.warn(`[Gapless] Seeking past track boundary oldSeek=${e}, adjustedSeek=${this.playingItem.itemStartOffset}`), e = this.playingItem.itemStartOffset), this.isPreloading && (e > this.loadingItem.itemStartOffset && (this.logger.warn(`[Gapless] Seeking past track boundary oldSeek=${e}, adjustedSeek=${this.loadingItem.itemStartOffset}`), e = this.loadingItem.itemStartOffset), e < this._mediaElementQuery.getBufferInfo(this._mediaElementQuery.currentTime, this.config.maxBufferHole)[0].buffered.start && this.dequeueSource("SeekToUnbufferedTimeRanges")), fg().setUserSeek(e) + } + isIframeInternalSeek(e) { + return e === (null === (e = this.iframeMachine) || void 0 === e ? void 0 : e.mediaRootTime) + } + set seekTo(e) { + var t, i = Number(e); + if (ne(i)) + if (this.inGaplessMode) this.gaplessSeekTo(i); + else { + const r = this.mediaElementAdapter; + !r || !ne(null === (t = r.mediaQuery) || void 0 === t ? void 0 : t.postFlushSeek) || r.mediaQuery.seekTo && !this.isIframeInternalSeek(r.mediaQuery.seekTo.pos) ? fg().setUserSeek(i) : r.schedulePostFlushSeek(i) + } + else this.logger.error(`[seek] got invalid seek value ${e}`) + } + seekToDate(e) { + fg().setUserSeek(e) + } + get availableProgramDateTime() { + return new Map(this._currentDateToMediaTimeTuple) + } + get _currentDateToMediaTimeTuple() { + if (!this._activeRootQuery) return []; + var e = this._activeRootQuery.enabledMediaOptionKeys[gu.Variant]; + return _u(e) && null !== (e = null === (e = Cg().getQueryForOption(e).mediaOptionDetails) || void 0 === e ? void 0 : e.dateMediaTimePairs) && void 0 !== e ? e : [] + } + get playingDate() { + return function(e, t) { + if (e && 0 !== e.length) { + const i = [...e].sort((e, t) => t[1] - e[1]), + r = null !== (e = i.find(([, e]) => e <= t)) && void 0 !== e ? e : i[i.length - 1], + [n, s] = r; + return new Date(n + 1e3 * (t - s)) + } + }(this._currentDateToMediaTimeTuple, this.realCurrentTime) + } + set variantId(e) {} + set audioSelectedPersistentID(e) { + var t = this._activeRootQuery; + null != t && t.preferredMediaOptions[gu.AltAudio] ? (t = t.itemId, e !== this.audioSelectedPersistentID && this.rootPlaylistService.setEnabledMediaOptionTupleWithMatchedGroups(t, gu.AltAudio, e, { + userInitiated: !0 + })) : !ne(e) || e < 0 || (this.logger.warn(`[audio] no active item, defer audio track selection: persistentId ${e}`), this.itemQueue.earlyAudioSelection = e) + } + get audioSelectedPersistentID() { + var e; + return this._activeRootQuery ? null === (e = this._activeRootQuery.enabledAlternateMediaOptionByType(gu.AltAudio)) || void 0 === e ? void 0 : e.persistentID : this.itemQueue.earlyAudioSelection + } + set subtitleSelectedPersistentID(e) { + var t = this._activeRootQuery, + i = null == t ? void 0 : t.preferredMediaOptions[gu.Subtitle]; + i ? e !== this.subtitleSelectedPersistentID && (t = t.itemId, 0 === i.length && (!ne(e) || e < 0) || (ne(e) && -1 !== e ? this.rootPlaylistService.setEnabledMediaOptionTupleWithMatchedGroups(t, gu.Subtitle, e) : this.rootPlaylistService.setEnabledMediaOptionByType(t, gu.Subtitle, Lu))) : !ne(e) || e < 0 || (this.logger.warn(`[subtitle] no active item, defer subtitle track selection: persistentId ${e}`), this.itemQueue.earlySubtitleSelection = e) + } + get subtitleSelectedPersistentID() { + var e; + return this._activeRootQuery ? null === (e = this._activeRootQuery.enabledAlternateMediaOptionByType(gu.Subtitle)) || void 0 === e ? void 0 : e.persistentID : this.itemQueue.earlySubtitleSelection + } + get selectedMediaArray() { + const e = this._activeRootQuery; + if (!e) return []; + const t = [], + i = e.enabledAlternateMediaOptionByType(gu.AltAudio), + r = e.enabledAlternateMediaOptionByType(gu.Subtitle), + n = i ? e.audioMediaSelectionOptions.find(e => e.MediaSelectionOptionsPersistentID === i.persistentID) : void 0, + s = r ? e.subtitleMediaSelectionOptions.find(e => e.MediaSelectionOptionsPersistentID === r.persistentID) : void 0; + if (n) { + const e = { + MediaSelectionGroupMediaType: Su.AUDIO, + MediaSelectionOptionsPersistentID: n.MediaSelectionOptionsPersistentID + }; + t.push(e) + } + if (s) { + let e = vu.NO; + s.MediaSelectionOptionsDisplaysNonForcedSubtitles && (e = s.MediaSelectionOptionsDisplaysNonForcedSubtitles); + const i = { + MediaSelectionGroupMediaType: Su.SUBTITLE, + MediaSelectionOptionsDisplaysNonForcedSubtitles: e, + MediaSelectionOptionsPersistentID: s.MediaSelectionOptionsPersistentID + }; + t.push(i) + } + return t + } + set selectedMediaArray(e) { + this._activeRootQuery ? e.forEach(e => { + e.MediaSelectionGroupMediaType === Su.AUDIO || e.MediaSelectionOptionsMediaType === Su.AUDIO ? this.audioSelectedPersistentID = e.MediaSelectionOptionsPersistentID : e.MediaSelectionGroupMediaType !== Su.SUBTITLE && e.MediaSelectionOptionsMediaType !== Su.SUBTITLE && e.MediaSelectionOptionsMediaType !== Su.CLOSEDCAPTION || (this.subtitleSelectedPersistentID = e.MediaSelectionOptionsPersistentID) + }) : this.logger.warn("selectedMediaArray: no active item") + } + getHTMLTextTrack(e) { + return this.legibleSystemAdapter.getExistingHTMLTextTrackWithSubtitleTrackId(e) + } + get keysystems() { + return this.keySystemAdapter.availableKeySystems + } + setProtectionData(e) { + this.keySystemAdapter.initialize(e) + } + generateKeyRequest(e, t) { + this.keySystemAdapter.generateRequest(e, t), this.rtcService.licenseChallengeReceived({ + keyuri: e + }) + } + setLicenseResponse(e, t) { + this.keySystemAdapter.setLicenseResponse(e, t) + } + get bufferInfo$() { + return this.publicQueries$.pipe(La(e => { + const [, t] = e, i = fg().getQuery().currentConfig; + return an(t.timeupdate$, t.bufferedRangeTuple$).pipe(ao(1e3), hr(() => { + var e = t.currentTime; + return { + combined: t.getCombinedBufferInfo(e, i.maxBufferHole), + sbTuple: t.getBufferInfo(e, i.maxBufferHole) + } + })) + })) + } + bufferInfoByType$(t) { + return this.bufferInfo$.pipe(hr(e => null === (e = null == e ? void 0 : e.sbTuple) || void 0 === e ? void 0 : e[t])) + } + levelWithPersistentId(e) { + this.logger.warn("levelWithPersistentId is deprecated") + } + startLoad(e) { + this.logger.warn("startLoad is deprecated"), ne(e) && (this.logger.warn(`[seek] Seeking to ${null==e?void 0:e.toFixed(3)} via deprecated "startLoad" method. Use loadSource(url, options, startTime) instead.`), this.seekTo = e) + } + stopLoad() {} + get config() { + return Object.assign(Object.assign({}, de(mg())), { + set startPosition(e) { + Qe().warn(`Setting start position ${null==e?void 0:e.toFixed(3)} using deprecated method`), fg().setStartTime(e) + } + }) + } + get media() { + return null != this.mediaElement$.value + } + set subtitleDisplay(e) { + this.logger.warn(`set subtitleDisplay ${e} is deprecated`) + } + } + }, "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : (e = "undefined" != typeof globalThis ? globalThis : e || Jy).Hls = t() +}(!1); +//# sourceMappingURL=hls.js.map \ No newline at end of file diff --git a/src/renderer/apple-hls.js b/src/renderer/apple-hls.js index fbbc38fc..7b414d26 100644 --- a/src/renderer/apple-hls.js +++ b/src/renderer/apple-hls.js @@ -1,25453 +1,49963 @@ -/*! For license information please see hls.js.LICENSE.txt */ ! function Xy(Yy) { - const Jy = this; - var e, t; - e = this, t = function() { - "use strict"; - var P, e = e => e && e.Math === Math && e, - l = e("object" == typeof globalThis && globalThis) || e("object" == typeof window && window) || e("object" == typeof Jy && Jy) || e("object" == typeof global && global) || Function("return this")(); - class d { - constructor() { - this.keySize = null, this.ksRows = null, this.keySchedule = null, this.invKeySchedule = null, this.rcon = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54], this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)], this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)], this.sBox = new Uint32Array(256), this.invSBox = new Uint32Array(256), this.key = new Uint32Array(0), this.initTable() - } - uint8ArrayToUint32Array_(e) { - const t = new DataView(e), - i = Math.floor(t.byteLength / 4), - r = new Uint32Array(i); - for (let e = 0; e < i; e++) r[e] = t.getUint32(4 * e); - return r - } - initTable() { - const e = this["sBox"], - t = this["invSBox"], - i = this["subMix"], - r = i[0], - n = i[1], - s = i[2], - a = i[3], - o = this["invSubMix"], - l = o[0], - d = o[1], - u = o[2], - c = o[3], - h = new Uint32Array(256); - let p = 0, - f = 0, - m = 0; - for (m = 0; m < 256; m++) h[m] = m < 128 ? m << 1 : m << 1 ^ 283; - for (m = 0; m < 256; m++) { - var g = (g = f ^ f << 1 ^ f << 2 ^ f << 3 ^ f << 4) >>> 8 ^ 255 & g ^ 99; - e[p] = g, t[g] = p; - const o = h[p], - m = h[o], - v = h[m]; - var y = 257 * h[g] ^ 16843008 * g; - r[p] = y << 24 | y >>> 8, n[p] = y << 16 | y >>> 16, s[p] = y << 8 | y >>> 24, a[p] = y, y = 16843009 * v ^ 65537 * m ^ 257 * o ^ 16843008 * p, l[g] = y << 24 | y >>> 8, d[g] = y << 16 | y >>> 16, u[g] = y << 8 | y >>> 24, c[g] = y, p ? (p = o ^ h[h[h[v ^ o]]], f ^= h[h[f]]) : p = f = 1 - } - } - expandKey(e) { - var n = this.uint8ArrayToUint32Array_(e); - let t = !0, - i = 0; - for (; i < n.length && t;) t = n[i] === this.key[i], i++; - if (!t) { - this.key = n; - var s = this.keySize = n.length; - if (4 !== s && 6 !== s && 8 !== s) throw new Error("Invalid aes key size=" + s); - var a = this.ksRows = 4 * (s + 6 + 1); - let e, t; - const o = this.keySchedule = new Uint32Array(a), - l = this.invKeySchedule = new Uint32Array(a), - d = this.sBox, - u = this["rcon"], - c = this["invSubMix"], - h = c[0], - p = c[1], - f = c[2], - m = c[3]; - let i, r; - for (e = 0; e < a; e++) e < s ? i = o[e] = n[e] : (r = i, e % s == 0 ? (r = r << 8 | r >>> 24, r = d[r >>> 24] << 24 | d[r >>> 16 & 255] << 16 | d[r >>> 8 & 255] << 8 | d[255 & r], r ^= u[e / s | 0] << 24) : 6 < s && e % s == 4 && (r = d[r >>> 24] << 24 | d[r >>> 16 & 255] << 16 | d[r >>> 8 & 255] << 8 | d[255 & r]), o[e] = i = (o[e - s] ^ r) >>> 0); - for (t = 0; t < a; t++) e = a - t, r = 3 & t ? o[e] : o[e - 4], l[t] = t < 4 || e <= 4 ? r : h[d[r >>> 24]] ^ p[d[r >>> 16 & 255]] ^ f[d[r >>> 8 & 255]] ^ m[d[255 & r]], l[t] = l[t] >>> 0 - } - } - networkToHostOrderSwap(e) { - return e << 24 | (65280 & e) << 8 | (16711680 & e) >> 8 | e >>> 24 - } - decrypt(e, t, i) { - var r = this.keySize + 6, - n = this["invKeySchedule"], - s = this.invSBox, - a = this["invSubMix"], - o = a[0], - l = a[1], - d = a[2], - u = a[3], - i = this.uint8ArrayToUint32Array_(i); - let c = i[0], - h = i[1], - p = i[2], - f = i[3]; - const m = new Int32Array(e), - g = new Int32Array(m.length); - let y, v, S, b, T, E, I, w, A, O, k, C, D, M; - const x = this.networkToHostOrderSwap; - for (; t < m.length;) { - for (A = x(m[t]), O = x(m[t + 1]), k = x(m[t + 2]), C = x(m[t + 3]), T = A ^ n[0], E = C ^ n[1], I = k ^ n[2], w = O ^ n[3], D = 4, M = 1; M < r; M++) y = o[T >>> 24] ^ l[E >> 16 & 255] ^ d[I >> 8 & 255] ^ u[255 & w] ^ n[D], v = o[E >>> 24] ^ l[I >> 16 & 255] ^ d[w >> 8 & 255] ^ u[255 & T] ^ n[D + 1], S = o[I >>> 24] ^ l[w >> 16 & 255] ^ d[T >> 8 & 255] ^ u[255 & E] ^ n[D + 2], b = o[w >>> 24] ^ l[T >> 16 & 255] ^ d[E >> 8 & 255] ^ u[255 & I] ^ n[D + 3], T = y, E = v, I = S, w = b, D += 4; - y = s[T >>> 24] << 24 ^ s[E >> 16 & 255] << 16 ^ s[I >> 8 & 255] << 8 ^ s[255 & w] ^ n[D], v = s[E >>> 24] << 24 ^ s[I >> 16 & 255] << 16 ^ s[w >> 8 & 255] << 8 ^ s[255 & T] ^ n[D + 1], S = s[I >>> 24] << 24 ^ s[w >> 16 & 255] << 16 ^ s[T >> 8 & 255] << 8 ^ s[255 & E] ^ n[D + 2], b = s[w >>> 24] << 24 ^ s[T >> 16 & 255] << 16 ^ s[E >> 8 & 255] << 8 ^ s[255 & I] ^ n[D + 3], D += 3, g[t] = x(y ^ c), g[t + 1] = x(b ^ h), g[t + 2] = x(S ^ p), g[t + 3] = x(v ^ f), c = A, h = O, p = k, f = C, t += 4 - } - return g.buffer - } - destroy() { - this.key = void 0, this.keySize = void 0, this.ksRows = void 0, this.sBox = void 0, this.invSBox = void 0, this.subMix = void 0, this.invSubMix = void 0, this.keySchedule = void 0, this.invKeySchedule = void 0, this.rcon = void 0 - } +/*! For license information please see hls.js.LICENSE.txt +TL,DR: Don't misuse this file for piracy purpose, all rights of this file and its usage belong to Apple and media copyright holders*/ +(function __HLS_UMD_BUNDLE__(__IN_WORKER__){const self = this; + (function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Hls = factory()); + })(this, (function () { 'use strict'; + + function hasUMDWorker() { + return typeof __HLS_UMD_BUNDLE__ === 'function'; + } + + const check = (it) => it && it.Math === Math && it; + var global$1 = // eslint-disable-next-line no-undef + (check(typeof globalThis == 'object' && globalThis) || + check(typeof window == 'object' && window) || + check(typeof self == 'object' && self) || + check(typeof global == 'object' && global) || + Function('return this')()); + + + class JSAESDecryptor { + constructor() { + this.keySize = null; + this.ksRows = null; + this.keySchedule = null; + this.invKeySchedule = null; + // Static after running initTable + this.rcon = [0, 1, 2, 4, 8, 16, 32, 64, 128, 27, 54]; + this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]; + this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]; + this.sBox = new Uint32Array(256); + this.invSBox = new Uint32Array(256); + // Changes during runtime + this.key = new Uint32Array(0); + this.initTable(); + } + // Using view.getUint32() also swaps the byte order. + uint8ArrayToUint32Array_(arrayBuffer) { + const view = new DataView(arrayBuffer); + const length = Math.floor(view.byteLength / 4); + const newArray = new Uint32Array(length); + for (let i = 0; i < length; i++) { + newArray[i] = view.getUint32(i * 4); + } + return newArray; + } + initTable() { + const { sBox } = this; + const { invSBox } = this; + const { subMix } = this; + const subMix0 = subMix[0]; + const subMix1 = subMix[1]; + const subMix2 = subMix[2]; + const subMix3 = subMix[3]; + const { invSubMix } = this; + const invSubMix0 = invSubMix[0]; + const invSubMix1 = invSubMix[1]; + const invSubMix2 = invSubMix[2]; + const invSubMix3 = invSubMix[3]; + const d = new Uint32Array(256); + let x = 0; + let xi = 0; + let i = 0; + for (i = 0; i < 256; i++) { + if (i < 128) { + d[i] = i << 1; + } + else { + d[i] = (i << 1) ^ 283; + } + } + for (i = 0; i < 256; i++) { + let sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4); + sx = (sx >>> 8) ^ (sx & 255) ^ 99; + sBox[x] = sx; + invSBox[sx] = x; + // Compute multiplication + const x2 = d[x]; + const x4 = d[x2]; + const x8 = d[x4]; + // Compute sub/invSub bytes, mix columns tables + let t = (d[sx] * 257) ^ (sx * 16843008); + subMix0[x] = (t << 24) | (t >>> 8); + subMix1[x] = (t << 16) | (t >>> 16); + subMix2[x] = (t << 8) | (t >>> 24); + subMix3[x] = t; + // Compute inv sub bytes, inv mix columns tables + t = (x8 * 16843009) ^ (x4 * 65537) ^ (x2 * 257) ^ (x * 16843008); + invSubMix0[sx] = (t << 24) | (t >>> 8); + invSubMix1[sx] = (t << 16) | (t >>> 16); + invSubMix2[sx] = (t << 8) | (t >>> 24); + invSubMix3[sx] = t; + // Compute next counter + if (!x) { + x = xi = 1; + } + else { + x = x2 ^ d[d[d[x8 ^ x2]]]; + xi ^= d[d[xi]]; + } + } + } + expandKey(keyBuffer) { + // convert keyBuffer to Uint32Array + const key = this.uint8ArrayToUint32Array_(keyBuffer); + let sameKey = true; + let offset = 0; + while (offset < key.length && sameKey) { + sameKey = key[offset] === this.key[offset]; + offset++; + } + if (sameKey) { + return; + } + this.key = key; + const keySize = (this.keySize = key.length); + if (keySize !== 4 && keySize !== 6 && keySize !== 8) { + throw new Error('Invalid aes key size=' + keySize); + } + const ksRows = (this.ksRows = (keySize + 6 + 1) * 4); + let ksRow; + let invKsRow; + const keySchedule = (this.keySchedule = new Uint32Array(ksRows)); + const invKeySchedule = (this.invKeySchedule = new Uint32Array(ksRows)); + const sbox = this.sBox; + const { rcon } = this; + const { invSubMix } = this; + const invSubMix0 = invSubMix[0]; + const invSubMix1 = invSubMix[1]; + const invSubMix2 = invSubMix[2]; + const invSubMix3 = invSubMix[3]; + let prev; + let t; + for (ksRow = 0; ksRow < ksRows; ksRow++) { + if (ksRow < keySize) { + prev = keySchedule[ksRow] = key[ksRow]; + continue; + } + t = prev; + if (ksRow % keySize === 0) { + // Rot word + t = (t << 8) | (t >>> 24); + // Sub word + t = (sbox[t >>> 24] << 24) | (sbox[(t >>> 16) & 255] << 16) | (sbox[(t >>> 8) & 255] << 8) | sbox[t & 255]; + // Mix Rcon + t ^= rcon[(ksRow / keySize) | 0] << 24; + } + else if (keySize > 6 && ksRow % keySize === 4) { + // Sub word + t = (sbox[t >>> 24] << 24) | (sbox[(t >>> 16) & 255] << 16) | (sbox[(t >>> 8) & 255] << 8) | sbox[t & 255]; + } + keySchedule[ksRow] = prev = (keySchedule[ksRow - keySize] ^ t) >>> 0; + } + for (invKsRow = 0; invKsRow < ksRows; invKsRow++) { + ksRow = ksRows - invKsRow; + if (invKsRow & 3) { + t = keySchedule[ksRow]; + } + else { + t = keySchedule[ksRow - 4]; + } + if (invKsRow < 4 || ksRow <= 4) { + invKeySchedule[invKsRow] = t; + } + else { + invKeySchedule[invKsRow] = invSubMix0[sbox[t >>> 24]] ^ invSubMix1[sbox[(t >>> 16) & 255]] ^ invSubMix2[sbox[(t >>> 8) & 255]] ^ invSubMix3[sbox[t & 255]]; + } + invKeySchedule[invKsRow] = invKeySchedule[invKsRow] >>> 0; + } + } + // Adding this as a method greatly improves performance. + networkToHostOrderSwap(word) { + return (word << 24) | ((word & 65280) << 8) | ((word & 16711680) >> 8) | (word >>> 24); + } + decrypt(inputArrayBuffer, offset, aesIV) { + const nRounds = this.keySize + 6; + const { invKeySchedule } = this; + const invSBOX = this.invSBox; + const { invSubMix } = this; + const invSubMix0 = invSubMix[0]; + const invSubMix1 = invSubMix[1]; + const invSubMix2 = invSubMix[2]; + const invSubMix3 = invSubMix[3]; + const initVector = this.uint8ArrayToUint32Array_(aesIV); + let initVector0 = initVector[0]; + let initVector1 = initVector[1]; + let initVector2 = initVector[2]; + let initVector3 = initVector[3]; + const inputInt32 = new Int32Array(inputArrayBuffer); + const outputInt32 = new Int32Array(inputInt32.length); + let t0, t1, t2, t3; + let s0, s1, s2, s3; + let inputWords0, inputWords1, inputWords2, inputWords3; + let ksRow, i; + const swapWord = this.networkToHostOrderSwap; + while (offset < inputInt32.length) { + inputWords0 = swapWord(inputInt32[offset]); + inputWords1 = swapWord(inputInt32[offset + 1]); + inputWords2 = swapWord(inputInt32[offset + 2]); + inputWords3 = swapWord(inputInt32[offset + 3]); + s0 = inputWords0 ^ invKeySchedule[0]; + s1 = inputWords3 ^ invKeySchedule[1]; + s2 = inputWords2 ^ invKeySchedule[2]; + s3 = inputWords1 ^ invKeySchedule[3]; + ksRow = 4; + // Iterate through the rounds of decryption + for (i = 1; i < nRounds; i++) { + t0 = invSubMix0[s0 >>> 24] ^ invSubMix1[(s1 >> 16) & 255] ^ invSubMix2[(s2 >> 8) & 255] ^ invSubMix3[s3 & 255] ^ invKeySchedule[ksRow]; + t1 = invSubMix0[s1 >>> 24] ^ invSubMix1[(s2 >> 16) & 255] ^ invSubMix2[(s3 >> 8) & 255] ^ invSubMix3[s0 & 255] ^ invKeySchedule[ksRow + 1]; + t2 = invSubMix0[s2 >>> 24] ^ invSubMix1[(s3 >> 16) & 255] ^ invSubMix2[(s0 >> 8) & 255] ^ invSubMix3[s1 & 255] ^ invKeySchedule[ksRow + 2]; + t3 = invSubMix0[s3 >>> 24] ^ invSubMix1[(s0 >> 16) & 255] ^ invSubMix2[(s1 >> 8) & 255] ^ invSubMix3[s2 & 255] ^ invKeySchedule[ksRow + 3]; + // Update state + s0 = t0; + s1 = t1; + s2 = t2; + s3 = t3; + ksRow = ksRow + 4; + } + // Shift rows, sub bytes, add round key + t0 = (invSBOX[s0 >>> 24] << 24) ^ (invSBOX[(s1 >> 16) & 255] << 16) ^ (invSBOX[(s2 >> 8) & 255] << 8) ^ invSBOX[s3 & 255] ^ invKeySchedule[ksRow]; + t1 = (invSBOX[s1 >>> 24] << 24) ^ (invSBOX[(s2 >> 16) & 255] << 16) ^ (invSBOX[(s3 >> 8) & 255] << 8) ^ invSBOX[s0 & 255] ^ invKeySchedule[ksRow + 1]; + t2 = (invSBOX[s2 >>> 24] << 24) ^ (invSBOX[(s3 >> 16) & 255] << 16) ^ (invSBOX[(s0 >> 8) & 255] << 8) ^ invSBOX[s1 & 255] ^ invKeySchedule[ksRow + 2]; + t3 = (invSBOX[s3 >>> 24] << 24) ^ (invSBOX[(s0 >> 16) & 255] << 16) ^ (invSBOX[(s1 >> 8) & 255] << 8) ^ invSBOX[s2 & 255] ^ invKeySchedule[ksRow + 3]; + ksRow = ksRow + 3; + // Write + outputInt32[offset] = swapWord(t0 ^ initVector0); + outputInt32[offset + 1] = swapWord(t3 ^ initVector1); + outputInt32[offset + 2] = swapWord(t2 ^ initVector2); + outputInt32[offset + 3] = swapWord(t1 ^ initVector3); + // reset initVector to last 4 unsigned int + initVector0 = inputWords0; + initVector1 = inputWords1; + initVector2 = inputWords2; + initVector3 = inputWords3; + offset = offset + 4; + } + return outputInt32.buffer; + } + destroy() { + this.key = undefined; + this.keySize = undefined; + this.ksRows = undefined; + this.sBox = undefined; + this.invSBox = undefined; + this.subMix = undefined; + this.invSubMix = undefined; + this.keySchedule = undefined; + this.invKeySchedule = undefined; + this.rcon = undefined; + } + } + + function removePadding(decryptedData) { + const decryptedDataView = new Uint8Array(decryptedData); + const padding = decryptedDataView[decryptedData.byteLength - 1]; + const endOffset = decryptedData.byteLength - 1; + // Check for PKCS-7 padding: https://tools.ietf.org/html/rfc2315#section-10.3 + let checkedBytes = 0; + if (padding >= 1 && padding <= 16) { + for (let i = endOffset; i > endOffset - padding; i--) { + if (decryptedDataView[i] !== padding) { + break; + } + checkedBytes++; + } + } + if (checkedBytes === padding) { + decryptedData = decryptedData.slice(0, endOffset - padding + 1); // keep 0 till 'endOffset - padding', both inclusive, to remove the padding bytes + } + return decryptedData; + } + + // CryptoRPCServer can run in either Worker or main process depending on config. + // So we want to minimize the dependency for it in case it's running in Worker. + // Thus we use the vanilla RPCService interface that uses old-school callback + // patterns. It has zero dependency: no Promise, no RxJS, just plain JavaScript. + class CryptoRPCServer { + constructor(rpc, logger) { + this.rpc = rpc; + this.logger = logger; + this.decrypt = (key, iv, alg, cipherText, options) => callback => { + const crypto = global$1.crypto; + if ((options === null || options === void 0 ? void 0 : options.useJSCrypto) || !(crypto === null || crypto === void 0 ? void 0 : crypto.subtle)) { + /** + * JSCrypto + */ + const jsCrypto = new JSAESDecryptor(); + let plainText; + jsCrypto.expandKey(key); + const result = jsCrypto.decrypt(cipherText, 0, iv); + if (options.plainTextLength) { + plainText = result.slice(0, options.plainTextLength); // The input buffer doesn't have padding, just return the expected bytes. + } + else { + plainText = removePadding(result); + } + this.logger.info(`[JSCrypto]: ${cipherText.byteLength} => ${plainText.byteLength} bytes`); + callback(plainText, undefined, [plainText]); + } + else { + /** + * WebCrypto + */ + crypto.subtle + .importKey('raw', key, alg, false, ['decrypt']) + .then((key) => crypto.subtle.decrypt({ name: alg, iv }, key, cipherText)) + .then((plainText) => { + this.logger.info(`[WebCrypto]: ${cipherText.byteLength} => ${plainText.byteLength} bytes`); + callback(plainText, undefined, [plainText]); + }) + .catch((error) => callback(undefined, error)); + } + }; + rpc.register('decrypt', this.decrypt); + } + } + + const LoggerRPCClient = (rpcService) => { + const bindLogger = (bindings = []) => { + const logFn = (level) => (...args) => { + rpcService.invoke('logger.log', [bindings, level, ...args])((res, err) => { + if (err != null) + throw err; + }); + }; + const logger = {}; + ['fatal', 'error', 'warn', 'info', 'debug', 'trace', 'qe'].forEach((k) => (logger[k] = logFn(k))); + logger.child = (b) => bindLogger([...bindings, b]); + return logger; + }; + return bindLogger(); + }; + + /** + * HLS Events + * + * + * + * + */ + /** + * @readonly + * @enum {string} + */ + var HlsEvent; + (function (HlsEvent) { + // fired before MediaSource is attaching to media element - data: { media } + HlsEvent["MEDIA_ATTACHING"] = "hlsMediaAttaching"; + // fired when MediaSource has been succesfully attached to media element - data: { media } + HlsEvent["MEDIA_ATTACHED"] = "hlsMediaAttached"; + // fired before detaching MediaSource from media element - data: { } + HlsEvent["MEDIA_DETACHING"] = "hlsMediaDetaching"; + // fired when MediaSource has been detached from media element + HlsEvent["MEDIA_DETACHED"] = "hlsMediaDetached"; + // fired when sourcebuffers have been created - data: { tracks : tracks } + HlsEvent["BUFFER_CREATED"] = "hlsBufferCreated"; + // fired when we append a segment to the buffer - data: { segment: segment object } + HlsEvent["BUFFER_APPENDING"] = "hlsBufferAppending"; + // fired when we are done with appending a media segment to the buffer - data : { parent : segment parent that triggered BUFFER_APPENDING, pending : nb of segments waiting for appending for this segment, timeRanges : bufferd time ranges } + HlsEvent["BUFFER_APPENDED"] = "hlsBufferAppended"; + // fired when the media buffer has been flushed + HlsEvent["BUFFER_FLUSHED"] = "hlsBufferFlushed"; + // fired to signal that a manifest loading starts - data: { url: manifestURL } + HlsEvent["MANIFEST_LOADING"] = "hlsManifestLoading"; + // fired after manifest has been loaded - data: { levels : [available quality levels including iframe levels], audioTracks : [ available audio tracks], subtitleTracks : [ available subtitle tracks], url : manifestURL, stats : { trequest, tfirst, tload, mtime}, audioMediaSelectionGroup: audio selection group, subtitleMediaSelectionGroup: subtitle selection group} + HlsEvent["MANIFEST_LOADED"] = "hlsManifestLoaded"; + // fired after manifest has been parsed - data: { levels : [available quality levels including iframe levels], firstLevel : index of first quality level appearing in Manifest, stats: stats, audio: audio codec, video: video codec, altAudio: if alternate audio present (true/false), audioTracks: audio tracks, audioMediaSelectionGroup: audio selection group} + HlsEvent["MANIFEST_PARSED"] = "hlsManifestParsed"; + // fired when a level switch is requested - data: { level : id of new level } + HlsEvent["LEVEL_SWITCHING"] = "hlsLevelSwitching"; + // fired when a level switch is effective - data: { level : id of new level } + HlsEvent["LEVEL_SWITCHED"] = "hlsLevelSwitched"; + // fired when a level playlist loading starts - data: { url : level URL, level : level, persistentId: persistent id of level being loaded} + HlsEvent["LEVEL_LOADING"] = "hlsLevelLoading"; + // fired when a level playlist loading finishes - data: { details : levelDetails object, level : level, persistentId : persistent id of loaded level, stats : { trequest, tfirst, tload, mtime}, playlistType : playlist type } + HlsEvent["LEVEL_LOADED"] = "hlsLevelLoaded"; + // fired when a level's details have been updated based on previous details, after it has been loaded - data: { details : levelDetails object, level : id of updated level } + HlsEvent["LEVEL_UPDATED"] = "hlsLevelUpdated"; + // fired when levels list has changed due to any error + HlsEvent["LEVELS_CHANGED"] = "hlsLevelsChanged"; + // fired to notify that audio track lists has been updated - data: { audioTracks : audioTracks } + HlsEvent["AUDIO_TRACKS_UPDATED"] = "hlsAudioTracksUpdated"; + // fired when an audio track switch occurs - data: { id : audio track id, type: track type, url: url } // deprecated in favor AUDIO_TRACK_SWITCHING + HlsEvent["AUDIO_TRACK_SWITCH"] = "hlsAudioTrackSwitch"; + // fired when an audio track switch actually occurs - data: { id : audio track id } + HlsEvent["AUDIO_TRACK_SWITCHED"] = "hlsAudioTrackSwitched"; + // fired when an audio track loading finishes - data: { details : levelDetails object, id : audio track id, stats : { trequest, tfirst, tload, mtime } } + HlsEvent["AUDIO_TRACK_LOADED"] = "hlsAudioTrackLoaded"; + // fired to notify that subtitle track lists has been updated - data: { subtitleTracks : subtitleTracks } + HlsEvent["SUBTITLE_TRACKS_UPDATED"] = "hlsSubtitleTracksUpdated"; + // fired to notify that subtitle HTML5 text tracks have been created - data: undefined + HlsEvent["SUBTITLE_TRACKS_CREATED"] = "hlsSubtitleTracksCreated"; + // fired when an subtitle track switch occurs - data: { track : subtitle track } + HlsEvent["SUBTITLE_TRACK_SWITCH"] = "hlsSubtitleTrackSwitch"; + // fired when inline webvtt styles have been parsed - data: { styles: styles } + HlsEvent["INLINE_STYLES_PARSED"] = "hlsInlineStylesParsed"; + // fired when all the session data URI loading finished - data: { } + HlsEvent["SESSION_DATA_COMPLETE"] = "hlsSessionDataComplete"; + // fired when a fragment loading starts - data: { frag : fragment object } + HlsEvent["FRAG_LOADING"] = "hlsFragLoading"; + // fired when a fragment loading is completed - data: { frag : fragment object, payload : fragment payload, stats : { trequest, tfirst, tload, length } } + HlsEvent["FRAG_LOADED"] = "hlsFragLoaded"; + // fired when fragment remuxed MP4 boxes have all been appended into SourceBuffer - data: { id : demuxer id, frag : fragment object, stats : { trequest, tfirst, tload, tparsed, tappendStart, tbuffered, length, bwEstimate } } + HlsEvent["FRAG_BUFFERED"] = "hlsFragBuffered"; + // fired when fragment matching with current media position is changing - data : { frag : fragment object } + HlsEvent["FRAG_CHANGED"] = "hlsFragChanged"; + // Identifier for an internal error event - data: { type : error type, details : error details, fatal : emitter's suggestion whether the error is fatal or not. can be overruled by handler } + HlsEvent["INTERNAL_ERROR"] = "hlsInternalError"; + // Identifier for an public error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data } + HlsEvent["ERROR"] = "hlsError"; + // fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example - data: { } + HlsEvent["DESTROYING"] = "hlsDestroying"; + // Key request started data: { keyuri: '' , decryptdata: DecryptData object, timestamp: timestamp of the event } + HlsEvent["KEY_REQUEST_STARTED"] = "hlsKeyRequestStarted"; + // data: { 'keyuri': 'licenseChallenge': Uint8Array(challengeBytes) keysystem: } + HlsEvent["LICENSE_CHALLENGE_CREATED"] = "hlsLicenseChallengeCreated"; + // EME: + // Session remove() was called and returned license-release message. data: { keysystem: '', itemId: itemId, releaseRecord: { CDM specific record destruction } } + HlsEvent["LICENSE_RELEASED"] = "hlsLicenseReleased"; + // fired when a decrypt key loading is completed - data: { decryptdata: '', keyuri: '', stats : { trequest, tfirst, tload }, timestamp: timestamp of the event } + HlsEvent["KEY_LOADED"] = "hlsKeyLoaded"; + // We encountered a url that doesn't start with http data: { uri: ''} + HlsEvent["UNRESOLVED_URI_LOADING"] = "hlsUnresolvedUriLoading"; + // fired when trickplay rate changing completed + HlsEvent["DESIRED_RATE_CHANGED"] = "hlsDesiredRateChanged"; + // data: { playbackLikelyToKeepUp } + HlsEvent["PLAYER_STATE_CHANGE"] = "hlsPlayerStateChange"; + // When seek starts data: {} + HlsEvent["SEEKING"] = "hlsSeeking"; + // When seek ends data: {} + HlsEvent["SEEKED"] = "hlsSeeked"; + // When stall detected. Stream controller decides if it will report an error. data: { isLowBufferStall: true|false } + HlsEvent["STALLED"] = "hlsStalled"; + // When resumed from stall, either playback restart or user hit pause. + HlsEvent["RESUME_FROM_STALL"] = "hlsResumeFromStall"; + // Item fully appended + HlsEvent["READY_FOR_NEXT_ITEM"] = "hlsReadyForNextItem"; + // Playback transitioned to next item + HlsEvent["ITEM_TRANSITIONED"] = "hlsItemTransitioned"; + // Evict loading item + HlsEvent["ITEM_EVICTED"] = "hlsItemEvicted"; + // New EXT-X-DATERANGE data available + HlsEvent["DATERANGE_UPDATED"] = "hlsDaterangeUpdated"; + })(HlsEvent || (HlsEvent = {})); + var HlsEvent$1 = HlsEvent; + + /* + * Demuxer Events + * + * + */ + /** + * @readonly + * @enum {string} + */ + var DemuxerEvent; + (function (DemuxerEvent) { + // fired when Init Segment has been extracted from fragment - data: { tracks: tracks } + DemuxerEvent["FRAG_PARSING_INIT_SEGMENT"] = "hlsFragParsingInitSegment"; + // fired when data have been extracted from fragment - data: { data1: segment, startPTS: start PTS, endPTS: end PTS, startDTS: start DTS, endDTS: end DTS, type: video / audio, nb: number of samples, dropped: is dropped } + DemuxerEvent["FRAG_PARSING_DATA"] = "hlsFragParsingData"; + // fired when fragment parsing is completed + DemuxerEvent["FRAG_PARSED"] = "hlsFragParsed"; + // fired when the first timestamp is found - data: { initPTS90k: initPTS90k } + DemuxerEvent["INIT_PTS_FOUND"] = "hlsInitPtsFound"; + })(DemuxerEvent || (DemuxerEvent = {})); + + /** + * HLS Error + * + * + * + * + */ + class HlsError extends Error { + constructor(type, details, fatal, reason, response) { + super(reason); + this.type = type; + this.details = details; + this.fatal = fatal; + this.reason = reason; + this.response = response; + this.handled = false; + } + } + // Inherited error codes from Core Media + const PlaylistNotReceived = { code: -12884, text: 'Playlist not received' }; + const CryptResponseReceivedSlowly = { code: -16833, text: 'Crypt key received slowly' }; + const LivePlaylistUpdateError = { code: -12888, text: 'Live playlist not updated' }; + const NoResponseFromMediaRequest = { code: -12889, text: 'No response for fragment' }; + const IncompatibleAsset = { code: -12927, text: 'IncompatibleAsset' }; + const CorruptStream = { code: -16041, text: 'Corrupt fragment' }; + const InternalError = { code: -12645, text: 'InternalException' }; + const CantSwitchInTime = { code: -12644, text: 'CantSwitchInTime' }; + const VideoDecoderBadDataErr = { code: -12909, text: 'Buffer error' }; + const InsufficientDataAvailable = { code: -12928, text: 'Incomplete data' }; + const AllocationFailed = { code: -12862, text: 'AllocationFailed' }; + const PlaylistErrorMissingEXTM3U = { code: -12269, text: 'Response doesnt have #EXTM3U tag' }; + const PlaylistErrorInvalidEntry = { code: -12264, text: 'Invalid entry' }; + const PlaylistErrorBadTargetDuration = { code: -12271, text: 'Invalid targetduration' }; + const NoValidAlternates = { code: -12925, text: 'No valid alternates' }; + const FormatError = { code: -12642, text: 'Incorrect playlist format' }; + // HLSJS Specific error codes + const UnsupportedKeySystemError = { code: -60000, text: 'Unsupported Key System' }; + const EmptyLoadSourceError = { code: -60001, text: 'Empty loadSource url' }; + const UndefinedItemIdError = { code: -60002, text: 'Undefined itemId' }; + const ManifestParseError = { code: -60003, text: 'Manifest parse error' }; + const DemuxWorkerError = { code: -60004, text: 'Demux worker error' }; + const DecryptWorkerError = { code: -60005, text: 'Decrypt worker error' }; + const OutOfRangeSeekError = { code: -60006, text: 'Seeked out of playable range' }; + const ExceptionInKeyLoadError = { code: -60007, text: 'Exception in Key load' }; + const FragmentAbortError$1 = { code: -60008, text: 'Fragment abort error' }; + const ManifestTimeoutError = { code: -60009, text: 'Manifest Timeout Error' }; + const PlaylistTimeoutError = { code: -60010, text: 'Playlist Timeout Error' }; + const FragmentTimeoutError = { code: -60011, text: 'Fragment Timeout Error' }; + const IncompleteSessionData = { code: -60012, text: 'Session data not complete after loading all items' }; + const SessionDataLoadTimeout = { code: -60013, text: 'Session data load timeout' }; + const FailedDemuxerSanityCheck = { code: -60014, text: 'Failed demuxer sanity check' }; + const InvalidADTSSamplingIndex = { code: -60015, text: 'Invalid ADTS sampling index' }; + const DemuxerNotFound = { code: -60016, text: 'No demux matching with content found' }; + const InvalidInitTimestamp = { code: -60017, text: 'Invalid initPTS or initDTS' }; + const NoAVSamplesFound = { code: -60018, text: 'no audio/video samples found' }; + const NoTSSyncByteFound = { code: -60019, text: 'TS packet did not start with 0x47' }; + const PESDidNotStartWithADTS = { code: -60020, text: 'AAC PES did not start with ADTS header' }; + const NoADTSHeaderInPES = { code: -60021, text: 'No ADTS header found in AAC PES' }; + const InvalidDolbyAudioMagic = { code: -60022, text: 'Invalid dolby audio magic' }; + const FailedToAllocateVideoMdat = { code: -60023, text: 'Fail allocating video mdat' }; + const FailedToAllocateAudioMdat = { code: -60024, text: 'Fail allocating audio mdat' }; + const InsufficientEC3Data = { code: -60025, text: 'Error parsing ec-3, not enough data' }; + const InvalidEC3Magic = { code: -60026, text: 'Invalid ec-3 magic' }; + const ReservedStreamType = { code: -60027, text: 'Reserved stream type' }; + const InsufficientAC3Data = { code: -60028, text: 'error parsing ac-3, not enough data' }; + const InvalidAC3Magic = { code: -60029, text: 'Invalid ac-3 magic' }; + const InvalidAC3SamplingRateCode = { code: -60030, text: 'Invalid ac-3 samplingRateCode' }; + const PlaylistErrorInvalidEXTXDEFINE = { code: -61000, text: 'Encountered undefined/not imported EXT-X-DEFINE property' }; + const PlaylistErrorMissingImportReference = { code: -61001, text: 'IMPORT references variable not in master playlist and/or NAME' }; + const PlaylistErrorInvalidSERVERURI = { code: -61002, text: 'Encountered undefined/invalid SERVER-URI attribute for EXT-X-CONTENT-STEERING tag' }; + const PlaylistErrorInvalidPATHWAYID = { code: -61003, text: 'Encountered invalid PATHWAY-ID attribute for EXT-X-CONTENT-STEERING tag' }; + const PlaylistErrorInvalidSCORE = { code: -61004, text: 'Encountered negative/non-number SCORE property' }; + const KeySystemFailedToUpdateSession = { code: -62000, text: 'KeySystem: Promise Rejected while updating session' }; + const KeySystemFailedToGenerateLicenseRenewal = { code: -62001, text: 'KeySystem: Failed to generate license renewal' }; + const KeySystemFailedToGenerateLicenseRequest = { code: -62002, text: 'KeySystem: Failed to generate license request' }; + const KeySystemAbort = { code: -62003, text: 'KeySystem: Aborted' }; + const KeySystemUnexpectedStateTransition = { code: -62004, text: 'KeySystem: Unexpected state transition' }; + const KeySystemUnexpectedState = { code: -62005, text: 'KeySystem: Unexpected state' }; + const KeySystemCDMUnknownError = { code: -62006, text: 'KeySystem: Unknown error from CDM' }; + const KeySystemRequestTimedOut = { code: -62007, text: 'Key request timed out' }; + const KeySystemUnexpectedMETHOD = { code: -62008, text: 'Unexpected METHOD attribute' }; + const KeySystemUnmatchedString = { code: -62009, text: 'KeySystem: string does not match' }; + const KeySystemInternalError = { code: -62010, text: 'KeySystem: internal-error' }; + const KeySystemOutputRestricted = { code: -62011, text: 'KeySystem: output-restricted' }; + const KeySystemSetupError = { code: -62012, text: 'KeySystem: setup error' }; + const KeySystemFailedToInitialize = { code: -62013, text: 'KeySystem: could not initialize' }; + const KeySystemFailedToCreateSession = { code: -62014, text: 'KeySystem: could not create session' }; + const KeySystemUndefinedNavigator = { code: -62015, text: 'KeySystem: navigator undefined' }; + const KeySystemNoKeySystemsToTry = { code: -62016, text: 'KeySystem: no key systems to try' }; + const KeySystemNoConstructor = { code: -62017, text: 'KeySystem: No constructor' }; + const KeySystemNoKeySystemAccess = { code: -62018, text: 'KeySystem: No KeySystemAccess' }; + const KeySystemCertificateLoadError = { code: -62019, text: 'KeySystem: Certificate Load Error' }; + class PlaylistParsingError extends HlsError { + constructor(type, details, fatal, reason, response) { + super(type, details, fatal, reason, response); + this.response = response; + } + } + const ErrorResponses = { + // Inherited error codes from Core Media + PlaylistNotReceived, + CryptResponseReceivedSlowly, + LivePlaylistUpdateError, + NoResponseFromMediaRequest, + IncompatibleAsset, + CorruptStream, + InternalError, + CantSwitchInTime, + VideoDecoderBadDataErr, + InsufficientDataAvailable, + AllocationFailed, + PlaylistErrorMissingEXTM3U, + PlaylistErrorInvalidEntry, + PlaylistErrorBadTargetDuration, + NoValidAlternates, + FormatError, + // HLSJS Specific error codes + UnsupportedKeySystemError, + EmptyLoadSourceError, + UndefinedItemIdError, + ManifestParseError, + DemuxWorkerError, + DecryptWorkerError, + OutOfRangeSeekError, + ExceptionInKeyLoadError, + FragmentAbortError: FragmentAbortError$1, + ManifestTimeoutError, + PlaylistTimeoutError, + FragmentTimeoutError, + IncompleteSessionData, + SessionDataLoadTimeout, + FailedDemuxerSanityCheck, + InvalidADTSSamplingIndex, + DemuxerNotFound, + InvalidAC3Magic, + InvalidInitTimestamp, + NoAVSamplesFound, + NoTSSyncByteFound, + PESDidNotStartWithADTS, + NoADTSHeaderInPES, + InvalidDolbyAudioMagic, + FailedToAllocateVideoMdat, + FailedToAllocateAudioMdat, + InsufficientEC3Data, + InvalidEC3Magic, + ReservedStreamType, + InsufficientAC3Data, + InvalidAC3SamplingRateCode, + PlaylistErrorInvalidEXTXDEFINE, + PlaylistErrorMissingImportReference, + PlaylistErrorInvalidSERVERURI, + PlaylistErrorInvalidPATHWAYID, + PlaylistErrorInvalidSCORE, + KeySystemFailedToUpdateSession, + KeySystemFailedToGenerateLicenseRenewal, + KeySystemFailedToGenerateLicenseRequest, + KeySystemAbort, + KeySystemUnexpectedStateTransition, + KeySystemUnexpectedState, + KeySystemCDMUnknownError, + KeySystemRequestTimedOut, + KeySystemUnexpectedMETHOD, + KeySystemUnmatchedString, + KeySystemInternalError, + KeySystemOutputRestricted, + KeySystemSetupError, + KeySystemFailedToInitialize, + KeySystemFailedToCreateSession, + KeySystemUndefinedNavigator, + KeySystemNoKeySystemsToTry, + KeySystemNoConstructor, + KeySystemNoKeySystemAccess, + KeySystemCertificateLoadError, + }; + const ErrorTypes = { + // Identifier for a network error (loading error / timeout ...) + NETWORK_ERROR: 'networkError', + // Identifier for a media Error (video/parsing/mediasource error) + MEDIA_ERROR: 'mediaError', + // Identifier for a mux Error (demuxing/remuxing) + MUX_ERROR: 'muxError', + // Identifier for a key system error (EME) + KEY_SYSTEM_ERROR: 'keySystemError', + // Identifier for all other errors + OTHER_ERROR: 'otherError', + }; + const ErrorDetails = { + // Identifier for a manifest load error - data: { url : faulty URL, response : { code: error code, text: error text }} + MANIFEST_LOAD_ERROR: 'manifestLoadError', + // Identifier for a manifest load timeout - data: { url : faulty URL, response : { code: error code, text: error text }} + MANIFEST_LOAD_TIMEOUT: 'manifestLoadTimeOut', + // Identifier for a manifest parsing error - data: { url : faulty URL, reason : error reason, response : { code: error code, text: error text }} + MANIFEST_PARSING_ERROR: 'manifestParsingError', + // Identifier for a manifest with only incompatible codecs error - data: { url : faulty URL, reason : error reason, response : { code: error code, text: error text }} + MANIFEST_INCOMPATIBLE_CODECS_ERROR: 'manifestIncompatibleCodecsError', + // Identifier for a manifest with only incompatible video-range error - data: { url : faulty URL, reason : error reason, response : { code: error code, text: error text }} + MANIFEST_INCOMPATIBLE_VIDEO_RANGE_ERROR: 'manifestIncompatibleVideoRangeError', + // Identifier for a level load error - data: { url : faulty URL, response : { code: error code, text: error text }} + LEVEL_LOAD_ERROR: 'levelLoadError', + // Identifier for a level load timeout - data: { url : faulty URL, response : { code: error code, text: error text }} + LEVEL_LOAD_TIMEOUT: 'levelLoadTimeOut', + // Identifier for a level switch error - data: { level : faulty level Id, event : error description, response : { code: error code, text: error text }} + LEVEL_SWITCH_ERROR: 'levelSwitchError', + // Identifier for an audio track load error - data: { url : faulty URL, response : { code: error code, text: error text }} + AUDIO_TRACK_LOAD_ERROR: 'audioTrackLoadError', + // Identifier for an audio track load timeout - data: { url : faulty URL, response : { code: error code, text: error text }} + AUDIO_TRACK_LOAD_TIMEOUT: 'audioTrackLoadTimeOut', + // Identifier for an subtitle track load error - data: { url : faulty URL, response : { code: error code, text: error text }} + SUBTITLE_TRACK_LOAD_ERROR: 'subtitleTrackLoadError', + // Identifier for an subtitle track load timeout - data: { url : faulty URL, response : { code: error code, text: error text }} + SUBTITLE_TRACK_LOAD_TIMEOUT: 'subtitleTrackLoadTimeout', + // Identifier for fragment load error - data: { frag : fragment object, response : { code: error code, text: error text }} + FRAG_LOAD_ERROR: 'fragLoadError', + // Identifier for fragment loop loading error - data: { frag : fragment object, fatal:, response: { code: error code, text: error text }} + FRAG_LOOP_LOADING_ERROR: 'fragLoopLoadingError', + // Identifier for fragment load timeout error - data: { frag : fragment object, response : { code: error code, text: error text }} + FRAG_LOAD_TIMEOUT: 'fragLoadTimeOut', + // Identifier for a fragment decryption error event - data: {id : demuxer Id,frag: fragment object, reason : parsing error description , response : { code: error code, text: error text }} + FRAG_DECRYPT_ERROR: 'fragDecryptError', + // Identifier for a fragment parsing error event - data: { id : demuxer Id, reason : parsing error description, response : { code: error code, text: error text } } + // will be renamed DEMUX_PARSING_ERROR and switched to MUX_ERROR in the next major release + FRAG_PARSING_ERROR: 'fragParsingError', + // Identifier for a remux alloc error event - data: { id : demuxer Id, frag : fragment object, bytes : nb of bytes on which allocation failed , reason : error text, response: { code: error code, text: error text }} + REMUX_ALLOC_ERROR: 'remuxAllocError', + // Triggered when an exception occurs while adding a sourceBuffer to MediaSource - data : { err : exception , mimeType : mimeType , response : { code: error code, text: error text }} + BUFFER_ADD_CODEC_ERROR: 'bufferAddCodecError', + // Identifier for a buffer append error - data: {append error description, response : { code: error code, text: error text }} + BUFFER_APPEND_ERROR: 'bufferAppendError', + // Identifier for a buffer appending error event - data: {appending error description, response : { code: error code, text: error text }} + BUFFER_APPENDING_ERROR: 'bufferAppendingError', + // Identifier for a buffer stalled error event - data: {fatal:, bufferLen: , response : { code: error code, text: error text }} + BUFFER_STALLED_ERROR: 'bufferStalledError', + // Identifier for a buffer full event - data: {fatal:, response : { code: error code, text: error text }} + BUFFER_FULL_ERROR: 'bufferFullError', + // Identifier for a buffer seek over hole event- data: {fatal:, response : { code: error code, text: error text }} + BUFFER_SEEK_OVER_HOLE: 'bufferSeekOverHole', + // Identifier for a buffer nudge on stall (playback is stuck although currentTime is in a buffered area) data: {fatal:, response : { code: error code, text: error text }} + BUFFER_NUDGE_ON_STALL: 'bufferNudgeOnStall', + // Identifier for an internal exception happening inside hls.js while handling an event + // data: { url: , fatal:, response: { code: error code, text: error text } } + INTERNAL_EXCEPTION: 'internalException', + // Malformed WebVTT contents + WEBVTT_EXCEPTION: 'webVTTException', + // KEY REQUEST ERRORS + // Identifier for decrypt key load error - data: { decryptdata: DecryptData object, stats: { tfirstissue, tlastissue, failCount } response: {code: status code returned from server, text: error message}} + KEY_LOAD_ERROR: 'keyLoadError', + // Identifier for decrypt key load timeout error - data: { decryptdata: DecryptData object, stats: { tfirstissue, tlastissue, failCount }, response: { code: error code, text: error message }} + KEY_LOAD_TIMEOUT: 'keyLoadTimeOut', + // Identifier for CDM error - data: { decryptdata: DecryptData object, stats: { tfirstissue, tlastissue, failCount }, reason: error message, response: { code: error code, text: error message }} + KEY_SYSTEM_GENERIC_ERROR: 'keySystemGenericError', + // data: { url: , fatal:, response: { code: error code, text: error text } } + CERT_LOAD_ERROR: 'certificateLoadError', + // data: { url: , fatal:, response: { code: error code, text: error text } } + CERT_LOAD_TIMEOUT: 'certificateLoadTimeOut', + // Identifier for session data item load error - data: { sessionId: '', response : { code: error code, text: error text }} + SESSION_DATA_LOAD_ERROR: 'sessionDataLoadError', + // Identifier for session data item load timeout error - data: { sessionId: '' , response : { code: error code, text: error text }} + SESSION_DATA_LOAD_TIMEOUT: 'sessionDataLoadTimeOut', + // Identifier for a Conetent Steering manifest load error - data: { url : faulty URL, response : { code: error code, text: error text }} + STEERING_MANIFEST_LOAD_ERROR: 'steeringManifestLoadError', + // Identifier for a Conetent Steering manifest load timeout - data: { url : faulty URL, response : { code: error code, text: error text }} + STEERING_MANIFEST_LOAD_TIMEOUT: 'steeringManifestLoadTimeOut', + // Identifier for a Conetent Steering manifest parsing error - data: { url : faulty URL, reason : error reason, response : { code: error code, text: error text }} + STEERING_MANIFEST_PARSING_ERROR: 'steeringManifestParsingError', + }; + /** + * @brief If no other error fits, use this error. + */ + class ExceptionError extends HlsError { + constructor(fatal, reason, response) { + super(ErrorTypes.OTHER_ERROR, ErrorDetails.INTERNAL_EXCEPTION, fatal, reason, response); + } + } + + class FragParsingError extends HlsError { + constructor(fatal, reason, response) { + super(ErrorTypes.MEDIA_ERROR, ErrorDetails.FRAG_PARSING_ERROR, fatal, reason, response); + } + } + class RemuxAllocError extends HlsError { + constructor(fatal, reason, response, bytes) { + super(ErrorTypes.MUX_ERROR, ErrorDetails.REMUX_ALLOC_ERROR, fatal, reason, response); + this.bytes = bytes; + } + } + + function convertTimestampToSeconds(ts) { + return ts.baseTime / ts.timescale; + } + function convertSecondsToTimestamp(sec, timescale) { + return { + baseTime: Math.floor(sec * timescale), + timescale, + }; + } + function convertTimescale(ts, timescale) { + return { + baseTime: Math.floor((ts.baseTime * timescale) / ts.timescale), + timescale, + }; + } + function determineMinTimestamp(a, b) { + const aSec = convertTimestampToSeconds(a); + const bSec = convertTimestampToSeconds(b); + return aSec < bSec ? a : b; + } + function determineMaxTimestamp(a, b) { + const aSec = convertTimestampToSeconds(a); + const bSec = convertTimestampToSeconds(b); + return aSec > bSec ? a : b; + } + /** + * @returns returns a - b in seconds + */ + function diffSeconds(a, b) { + return convertTimestampToSeconds(a) - convertTimestampToSeconds(b); + } + + class EventEmitterPolyfill { + constructor() { + this.eventMap = {}; + } + _on(eventName, eventRecord, prepend = false) { + if (this.eventMap[eventName] == null) { + this.eventMap[eventName] = []; + } + if (prepend) { + this.eventMap[eventName].splice(0, 0, eventRecord); + } + else { + this.eventMap[eventName].push(eventRecord); + } + return this; + } + _off(eventName, record) { + if (this.eventMap[eventName] != null) { + this.eventMap[eventName] = this.eventMap[eventName].filter(r => r.listener !== record.listener); + if (this.eventMap[eventName].length === 0) { + delete this.eventMap[eventName]; + } + } + return this; + } + on(eventName, listener) { + return this._on(eventName, { listener, once: false }); + } + off(eventName, listener) { + return this._off(eventName, { listener }); + } + addListener(eventName, listener) { + return this.on(eventName, listener); + } + once(eventName, listener) { + return this._on(eventName, { listener, once: true }); + } + removeListener(eventName, listener) { + return this.off(eventName, listener); + } + removeAllListeners(event) { + delete this.eventMap[event]; + return this; + } + setMaxListeners(n) { + return this; + } + getMaxListeners() { + return Infinity; + } + // eslint-disable-next-line @typescript-eslint/ban-types + listeners(eventName) { + if (this.eventMap[eventName] == null) { + return []; + } + return this.eventMap[eventName].map(r => r.listener); + } + // eslint-disable-next-line @typescript-eslint/ban-types + rawListeners(eventName) { + return this.listeners(eventName); + } + emit(eventName, ...args) { + if (this.eventMap[eventName] == null) { + return false; + } + let emitted = false; + for (const record of this.eventMap[eventName]) { + try { + record.listener.apply(this, args); + // eslint-disable-next-line no-empty + } + catch (err) { } + emitted = true; + } + return emitted; + } + listenerCount(eventName) { + if (this.eventMap[eventName] == null) { + return 0; + } + return this.eventMap[eventName].length; + } + prependListener(eventName, listener) { + return this._on(eventName, { listener, once: false }, true); + } + prependOnceListener(eventName, listener) { + return this._on(eventName, { listener, once: true }, true); + } + eventNames() { + return Object.keys(this.eventMap); + } + } + const EventEmitter = typeof global$1.Buffer !== 'undefined' ? (r => r('events'))(require).EventEmitter : EventEmitterPolyfill; + + /** + * Simple adapter sub-class of Nodejs-like EventEmitter. + */ + class Observer extends EventEmitter { + /** + * We simply want to pass along the event-name itself + * in every call to a handler, which is the purpose of our `trigger` method + * extending the standard API. + */ + trigger(event, data) { + this.emit(event, data); + } + } + + + const loggerName$l = { name: 'ADTS' }; + const ADTS = { + getAudioConfig: function (observer, data, offset, audioCodec, logger) { + let adtsObjectType; // :int + let extensionSamplingIndex; // :int + let adtsChanelConfig; // :int + let config; + const userAgent = navigator.userAgent.toLowerCase(); + const adtsSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350]; + // byte 2 + adtsObjectType = ((data[offset + 2] & 192) >>> 6) + 1; + const adtsSamplingIndex = (data[offset + 2] & 60) >>> 2; + if (adtsSamplingIndex > adtsSamplingRates.length - 1) { + const payload = new FragParsingError(true, `invalid ADTS sampling index:${adtsSamplingIndex}`, ErrorResponses.InvalidADTSSamplingIndex); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return; + } + adtsChanelConfig = (data[offset + 2] & 1) << 2; + // byte 3 + adtsChanelConfig |= (data[offset + 3] & 192) >>> 6; + logger.info(loggerName$l, `manifest codec:${audioCodec},ADTS data:type:${adtsObjectType},samplingIndex:${adtsSamplingIndex}[${adtsSamplingRates[adtsSamplingIndex]}Hz],channelConfig:${adtsChanelConfig}`); + // firefox: freq less than 24kHz = AAC SBR (HE-AAC) + if (/firefox/i.test(userAgent)) { + if (adtsSamplingIndex >= 6) { + adtsObjectType = 5; + config = new Array(4); + // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies + // there is a factor 2 between frame sample rate and output sample rate + // multiply frequency by 2 (see table below, equivalent to substract 3) + extensionSamplingIndex = adtsSamplingIndex - 3; + } + else { + adtsObjectType = 2; + config = new Array(2); + } + // Android : always use AAC + } + else if (userAgent.indexOf('android') !== -1) { + adtsObjectType = 2; + config = new Array(2); + } + else { + /* for other browsers (Chrome/Vivaldi/Opera ...) + always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...) + */ + adtsObjectType = 5; + config = new Array(4); + // if (manifest codec is HE-AAC or HE-AACv2) OR (manifest codec not specified AND frequency less than 24kHz) + if ((audioCodec && (audioCodec.indexOf('mp4a.40.29') !== -1 || audioCodec.indexOf('mp4a.40.5') !== -1)) || (!audioCodec && adtsSamplingIndex >= 6)) { + // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies + // there is a factor 2 between frame sample rate and output sample rate + // multiply frequency by 2 (see table below, equivalent to substract 3) + extensionSamplingIndex = adtsSamplingIndex - 3; + } + else { + // if (manifest codec is AAC) OR (manifest codec not specified and mono audio) + // Chrome fails to play back with low frequency AAC LC mono when initialized with HE-AAC. This is not a problem with stereo. + if ((audioCodec && audioCodec.indexOf('mp4a.40.2') !== -1) || (!audioCodec && adtsChanelConfig === 1)) { + adtsObjectType = 2; + config = new Array(2); + } + extensionSamplingIndex = adtsSamplingIndex; + } + } + /* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config + ISO 14496-3 (AAC).pdf - Table 1.13 — Syntax of AudioSpecificConfig() + Audio Profile / Audio Object Type + 0: Null + 1: AAC Main + 2: AAC LC (Low Complexity) + 3: AAC SSR (Scalable Sample Rate) + 4: AAC LTP (Long Term Prediction) + 5: SBR (Spectral Band Replication) + 6: AAC Scalable + sampling freq + 0: 96000 Hz + 1: 88200 Hz + 2: 64000 Hz + 3: 48000 Hz + 4: 44100 Hz + 5: 32000 Hz + 6: 24000 Hz + 7: 22050 Hz + 8: 16000 Hz + 9: 12000 Hz + 10: 11025 Hz + 11: 8000 Hz + 12: 7350 Hz + 13: Reserved + 14: Reserved + 15: frequency is written explictly + Channel Configurations + These are the channel configurations: + 0: Defined in AOT Specifc Config + 1: 1 channel: front-center + 2: 2 channels: front-left, front-right + */ + // audioObjectType = profile => profile, the MPEG-4 Audio Object Type minus 1 + config[0] = adtsObjectType << 3; + // samplingFrequencyIndex + config[0] |= (adtsSamplingIndex & 14) >> 1; + config[1] |= (adtsSamplingIndex & 1) << 7; + // channelConfiguration + config[1] |= adtsChanelConfig << 3; + if (adtsObjectType === 5) { + // extensionSamplingIndex + config[1] |= (extensionSamplingIndex & 14) >> 1; + config[2] = (extensionSamplingIndex & 1) << 7; + // adtsObjectType (force to 2, chrome is checking that object type is less than 5 ??? + // https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc + config[2] |= 2 << 2; + config[3] = 0; + } + const audioConfig = { esdsConfig: config, samplerate: adtsSamplingRates[adtsSamplingIndex], channelCount: adtsChanelConfig, segmentCodec: 'aac', codec: 'mp4a.40.' + adtsObjectType }; + return audioConfig; + }, + }; + + class DemuxerBase { + constructor(observer, remuxer, config, typeSupported, logger) { + this.observer = observer; + this.remuxer = remuxer; + this.config = config; + this.typeSupported = typeSupported; + this.logger = logger; + } + static probe(data, logger) { + throw new Error('Method not implemented'); + } + resetTimeStamp(initPTS90k) { } + resetInitSegment(initSegment, duration, keyTagInfo, discontinuity) { } + destroy() { } + } + class EsDemuxer extends DemuxerBase { + constructor(observer, remuxer, config, typeSupported, logger) { + super(observer, remuxer, config, typeSupported, logger); + this.observer = observer; + this.remuxer = remuxer; + this.config = config; + this.typeSupported = typeSupported; + this.logger = logger; + this.esRemuxer = remuxer; + } + } + class RemuxerBase { + constructor(observer, config, logger) { + this.observer = observer; + this.config = config; + this.logger = logger; + } + resetInitSegment() { } + resetTimeStamp(initPTS90k) { } + destroy() { } + } + + /* + * 2018 Apple Inc. All rights reserved. + */ + var _a$1; + let te; + let td; + const BrowserBufferUtils = { + /** + * Convert a string object into a uint8Array containing utf-8 encoded text + * @param str The string to convert + * @returns A uint8Array containing utf-8 encoded text + + + */ + strToUtf8array(str) { + if (!te) { + te = new TextEncoder(); + } + return te.encode(str); + }, + /** + * Convert a uint8Array containing utf-8 encoded text into a string object + * @param array The array to convert containing utf-8 encoded text + * @returns A DOMString containing the decoded text + */ + utf8arrayToStr(array) { + if (!td) { + td = new TextDecoder('utf-8'); + } + return td.decode(array); + }, + }; + const NodeJSBufferUtils = { + /** + * Convert a string object into a uint8Array containing utf-8 encoded text + * @param str The string to convert + * @returns A uint8Array containing utf-8 encoded text + */ + strToUtf8array(str) { + const buffer = global$1.Buffer.from(str, 'utf-8'); + return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength); + }, + /** + * Convert a uint8Array containing utf-8 encoded text into a string object + * @param array The array to convert containing utf-8 encoded text + * @returns A DOMString containing the decoded text + */ + utf8arrayToStr(array) { + return global$1.Buffer.from(array).toString('utf-8'); + }, + }; + const FallbackBufferUtils = { + /** + * Convert a string object into a uint8Array containing utf-8 encoded text + * @param str The string to convert + * @returns A uint8Array containing utf-8 encoded text + */ + strToUtf8array(str) { + const utf8 = unescape(encodeURIComponent(str)); + const result = new Uint8Array(utf8.length); + for (let i = 0; i < utf8.length; i++) { + result[i] = utf8.charCodeAt(i); + } + return result; + }, + /** + * Convert a uint8Array containing utf-8 encoded text into a string object + * @param array The array to convert containing utf-8 encoded text + * @returns A DOMString containing the decoded text + */ + utf8arrayToStr(array) { + return String.fromCharCode.apply(null, Array.from(array)); + }, + }; + let BufferUtils = FallbackBufferUtils; + if (typeof TextEncoder !== 'undefined' && typeof TextDecoder !== 'undefined') { + BufferUtils = BrowserBufferUtils; + } + else if (typeof ((_a$1 = global$1.Buffer) === null || _a$1 === void 0 ? void 0 : _a$1.from) === 'function') { + BufferUtils = NodeJSBufferUtils; + } + + + const loggerName$k = { name: 'ID3' }; + class ID3 { + // TODO: I am not comfortable with infinite loop .... + constructor(data, logger) { + this.logger = logger; + this._hasTimeStamp = false; + this._audioType = null; + this._length = 0; + this._frames = []; + let offset = 0, tagSize, endPos, header, len; + do { + header = ID3.readUTF(data, offset, 3); + offset += 3; + // first check for ID3 header + if (header === 'ID3') { + // v2.* tags only + // skip 16 bit version + this._minor = data[offset++]; + this._revision = data[offset++]; + const tagFlags = data[offset++]; // 1 byte flag (top 3 bits used) + if (tagFlags & 128) { + // is unsynchroized + this._unsynchronized = true; + this.logger.error(loggerName$k, 'id3 tag is unsynchronized'); + } + if (tagFlags & 64) { + this._hasExtendedHeader = true; + this.logger.warn(loggerName$k, 'id3 tag has extended header'); + } + // retrieve tag(s) length + // The ID3v2 tag size is the sum of the byte length of the extended + // header, the padding and the frames after unsynchronisation. If a + // footer is present this equals to ('total size' - 20) bytes, otherwise + // ('total size' - 10) bytes. + tagSize = ID3.readSynchSafeUint32(data.subarray(offset, offset + 4)); + offset += 4; + endPos = offset + tagSize; // tagSize accounts for extended header + if (this._hasExtendedHeader) { + // has extended header + const extendedHeaderSize = ID3.readSynchSafeUint32(data.subarray(offset, offset + 4)); + this.logger.warn(loggerName$k, `id3 tag has ${extendedHeaderSize}-byte extended header. usually 6 or 10 bytes`); + offset += extendedHeaderSize; + } + // read ID3 frames + if (this.minor > 2) { + this._parseID3Frames(data, offset, endPos); + } + else { + this.logger.error(loggerName$k, '[id3] doesn\'t support older than v2.3 tags'); + } + offset = endPos; + } + else if (header === '3DI') { + // http://id3.org/id3v2.4.0-structure chapter 3.4. ID3v2 footer + offset += 7; + } + else { + offset -= 3; + len = offset; + if (len) { + if (!this.hasTimeStamp) { + this.logger.warn(loggerName$k, 'ID3 tag found, but no timestamp'); + } + this._length = len; + this._payload = data.slice(0, len); + } + return; + } + } while (true); // eslint-disable-line + } + static isHeader(data, offset) { + if (data[offset] === 73 && data[offset + 1] === 68 && data[offset + 2] === 51) { + // check version is within range + if (data[offset + 3] < 255 && data[offset + 4] < 255) { + // check size is within range + if (data[offset + 6] < 128 && data[offset + 7] < 128 && data[offset + 8] < 128 && data[offset + 9] < 128) { + return true; + } + } + } + return false; + } + static readSynchSafeUint32(data) { + return (data[0] & 127) * 2097152 + (data[1] & 127) * 16384 + (data[2] & 127) * 128 + (data[3] & 127); + } + static readUTF(data, start, len) { + let result = '', offset = start; + const end = start + len; + do { + result += String.fromCharCode(data[offset++]); + } while (offset < end); + return result; + } + isID3Frame(data, offset) { + if (data[offset + 4] < 128 && data[offset + 5] < 128 && data[offset + 6] < 128 && data[offset + 7] < 128) { + return true; + } + return false; + } + decodeID3Frame(frame) { + if (frame.type === 'TXXX') { + return this.decodeTxxxFrame(frame); + } + else if (frame.type === 'WXXX') { + return this.decodeWxxxFrame(frame); + } + else if (frame.type === 'PRIV') { + return this.decodePrivFrame(frame); + } + else if (frame.type[0] === 'T') { + return this.decodeTextFrame(frame); + } + else { + return { key: frame.type, data: frame.data }; + } + } + decodeTxxxFrame(frame) { + /* + Format: + [0] = {Text Encoding} + [1-?] = {Description}\0{Value} + */ + if (frame.size < 2) { + return undefined; + } + if (frame.data[0] !== 3) { + // only support UTF-8 + return undefined; + } + let index = 1; + const description = this.id3utf8ArrayToStr(frame.data.subarray(index)); + index += description.length + 1; + const value = this.id3utf8ArrayToStr(frame.data.subarray(index)); + return { key: 'TXXX', description, data: value }; + } + decodeWxxxFrame(frame) { + /* + Format: + [0] = {Text Encoding} + [1-?] = {Description}\0{Value} + */ + if (frame.size < 2) { + return undefined; + } + if (frame.data[0] !== 3) { + // only support UTF-8 + return undefined; + } + let index = 1; + const description = this.id3utf8ArrayToStr(frame.data.subarray(index)); + index += description.length + 1; + // Need to use the BufferUtils version of utf8arrayToStr since it works + // with arrays that doesn't have a '\0' in the end of the array. + const value = BufferUtils.utf8arrayToStr(frame.data.subarray(index)); + return { key: 'WXXX', description, data: value }; + } + decodeTextFrame(frame) { + /* + Format: + [0] = {Text Encoding} + [1-?] = {Value} + */ + if (frame.size < 2) { + return undefined; + } + if (frame.data[0] !== 3) { + // only support UTF-8 + return undefined; + } + const data = frame.data.subarray(1); + return { key: frame.type, data: this.id3utf8ArrayToStr(data) }; + } + decodePrivFrame(frame) { + /* + Format: \0 + */ + if (frame.size < 2) { + return undefined; + } + const owner = this.id3utf8ArrayToStr(frame.data); + const privateData = frame.data.slice(owner.length + 1); + return { key: 'PRIV', info: owner, data: privateData }; + } + _extractID3Frame(data, frameId, frameLen, frameBodyOffset, endPos) { + const frameEnd = frameBodyOffset + frameLen; + let frame; + if (frameEnd <= endPos) { + frame = { type: frameId, data: data.slice(frameBodyOffset, frameEnd) }; + } + else { + this.logger.error(loggerName$k, `id3 frame ${frameId} size ${frameLen} exceeded ${endPos}`); + } + return frame; + } + _parseID3Frames(data, offset, endPos) { + let tagId, tagLen, tagStart, timestamp; + while (offset + 8 <= endPos) { + this.logger.info(loggerName$k, `[id3] _parseID3Frames ${offset} ${endPos}`); + if (!this.isID3Frame(data, offset)) { + this.logger.error(loggerName$k, `[id3] illegal id3 frame @ offset ${offset}. skip this id3 tag`); + return; + } + tagId = ID3.readUTF(data, offset, 4); + offset += 4; + if (tagId === '') { + this.logger.info(loggerName$k, '[id3] empty tagId. padding.'); + return; + } + tagLen = ID3.readSynchSafeUint32(data.subarray(offset, offset + 4)); + if (tagLen === 0) { + this.logger.info(loggerName$k, '[id3] zero tag length. padding.'); + return; + } + offset += 4; + data[offset++] << (8 + data[offset++]); + tagStart = offset; + this.logger.info(loggerName$k, '[id3] tag id:' + tagId + ' tagLen ' + tagLen + ' offset ' + offset + ' endPos ' + endPos); + this.logger.qe({ critical: true, name: 'id3Parsed', data: { tagId, tagLen, offset, endPos } }); + const frame = this._extractID3Frame(data, tagId, tagLen, tagStart, endPos); + if (frame) { + const id3Frame = this.decodeID3Frame(frame); + this._frames.push(id3Frame); + } + switch (tagId) { + case 'PRIV': + // this.logger.info(loggerName, 'parse frame:' + Hex.hexDump(data.subarray(offset,endPos))); + // owner should be "com.apple.streaming.transportStreamTimestamp" + if (tagLen === 53 && ID3.readUTF(data, offset, 44) === 'com.apple.streaming.transportStreamTimestamp') { + offset += 44; + // smelling even better ! we found the right descriptor + // skip null character (string end) + 3 first bytes + offset += 4; + // timestamp is 33 bit expressed as a big-endian eight-octet number, with the upper 31 bits set to zero. + const pts33Bit = data[offset++] & 1; + this._hasTimeStamp = true; + timestamp = ((data[offset++] << 23) + (data[offset++] << 15) + (data[offset++] << 7) + data[offset++]) / 45; + if (pts33Bit) { + timestamp += 47721858.84; // 2^32 / 90 + } + timestamp = Math.round(timestamp); + this.logger.info(loggerName$k, `ID3 timestamp found: ${timestamp}`); + this._timeStamp = timestamp; + } + else if (tagLen >= 45 && ID3.readUTF(data, offset, 36) === 'com.apple.streaming.audioDescription') { + offset += 37; // skip tag and null terminator + this._audioType = ID3.readUTF(data, offset, 4); + offset += 4; + // skip everything else for now, don't think we need anything from the audio setup + offset += tagLen - 41; + this.logger.info(loggerName$k, `ID3 audio description found: ${this._audioType}`); + } + else { + offset += tagLen; + } + break; + default: + { + offset += tagLen; + } + break; + } + this.logger.info(loggerName$k, `[id3] ${tagId} default tagLen ${tagLen} offset ${offset} endPos ${endPos}`); + } + } + // id3utf8ArrayToStr is different from BufferUtils.utfarrayToStr. It exists when the string ends. + // BufferUtils.utfarrayToStr will decode the entire array. + // http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197 + // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt + /** utf.js - UTF-8 <=> UTF-16 convertion + * + * Copyright (C) 1999 Masanao Izumo + * Version: 1.0 + * LastModified: Dec 25 1999 + * This library is free. You can redistribute it and/or modify it. + */ + id3utf8ArrayToStr(array) { + let char2; + let char3; + let out = ''; + let i = 0; + const length = array.length; + while (i < length) { + const c = array[i++]; + switch (c >> 4) { + case 0: + return out; + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + // 0xxxxxxx + out += String.fromCharCode(c); + break; + case 12: + case 13: + // 110x xxxx 10xx xxxx + char2 = array[i++]; + out += String.fromCharCode(((c & 31) << 6) | (char2 & 63)); + break; + case 14: + // 1110 xxxx 10xx xxxx 10xx xxxx + char2 = array[i++]; + char3 = array[i++]; + out += String.fromCharCode(((c & 15) << 12) | ((char2 & 63) << 6) | ((char3 & 63) << 0)); + break; + } + } + } + get hasTimeStamp() { + return this._hasTimeStamp; + } + get timeStamp() { + return this._timeStamp; + } + get audioType() { + return this._audioType; + } + get length() { + return this._length; + } + get payload() { + return this._payload; + } + get frames() { + return this._frames; + } + get minor() { + return this._minor; + } + get revision() { + return this._revision; + } + } + var ID3$1 = ID3; + + + const loggerName$j = { name: 'AACDemuxer' }; + class AACDemuxer extends EsDemuxer { + resetInitSegment(initSegment, duration) { + this.audioConfig = undefined; + this.audioTrack = undefined; + this.duration = duration; + } + static probe(data, logger) { + // check if data contains ID3 timestamp and ADTS sync word + const id3 = new ID3$1(data, logger); + let offset, length; + // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1 + // Layer bits (position 14 and 15) in header should be always 0 for ADTS + // More info https://wiki.multimedia.cx/index.php?title=ADTS + for (offset = id3.length, length = Math.min(data.length - 1, offset + 100); offset < length; offset++) { + if (data[offset] === 255 && (data[offset + 1] & 246) === 240) { + return true; + } + } + return false; + } + // feed incoming data to the front of the parsing pipeline + append(data, timeOffset, contiguous, accurateTimeOffset, keyTagInfo) { + const id3 = new ID3$1(data, this.logger); + const pts = id3.hasTimeStamp ? 90 * id3.timeStamp : 90000 * timeOffset; + if (!id3.hasTimeStamp) { + this.logger.info(loggerName$j, `missing id3 timestamp at timeOffset ${timeOffset.toFixed(3)}`); + } + let frameLength, frameIndex, offset, headerLength, stamp, length; + let aacSample; + let id3Track = undefined; + let frames = undefined; + let payload = undefined; + if (id3.length) { + payload = id3.payload; + if (id3.frames.length) { + frames = id3.frames; + } + this.logger.info(loggerName$j, `[id3] init id3 tag pts=${pts} frames=${id3.frames.length}`); + id3Track = { id3Samples: [{ pts: pts, dts: pts, data: payload, frames: frames }], inputTimescale: 90000 }; + } + // Look for ADTS header + for (offset = id3.length, length = data.length; offset < length - 1; offset++) { + if (data[offset] === 255 && (data[offset + 1] & 246) === 240) { + break; + } + } + if (!this.audioConfig) { + this.audioConfig = ADTS.getAudioConfig(this.observer, data, offset, undefined, this.logger); + if (!this.audioConfig) { + throw 'failed to parse adts config'; + } + this.logger.info(loggerName$j, `parsed codec:${this.audioConfig.codec},rate:${this.audioConfig.samplerate},nb channel:${this.audioConfig.channelCount}`); + } + if (!this.audioTrack) { + const info = { id: 258, inputTimescale: 90000, timescale: NaN, duration: this.duration, encrypted: false, keyTagInfo }; + const parsingData = { len: 0, sequenceNumber: 0, esSamples: [] }; + this.audioTrack = { info, parsingData, type: 'audio', config: this.audioConfig }; + } + if (id3.audioType === 'zaac' || id3.audioType === 'zach' || id3.audioType === 'zacp') { + this.audioTrack.info.encrypted = true; + this.logger.info(loggerName$j, 'found encrypted aac'); + } + frameIndex = 0; + const frameDuration = 92160000 / this.audioConfig.samplerate; + while (offset + 5 < length) { + // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header + headerLength = data[offset + 1] & 1 ? 7 : 9; + // retrieve frame size + frameLength = ((data[offset + 3] & 3) << 11) | (data[offset + 4] << 3) | ((data[offset + 5] & 224) >>> 5); + frameLength -= headerLength; + // stamp = pes.pts; + if (frameLength > 0 && offset + headerLength + frameLength <= length) { + stamp = pts + frameIndex * frameDuration; + aacSample = { unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), pts: stamp, dts: stamp, keyTagInfo: keyTagInfo }; + this.audioTrack.parsingData.esSamples.push(aacSample); + this.audioTrack.parsingData.len += frameLength; + offset += frameLength + headerLength; + frameIndex++; + // look for ADTS header (0xFFFx) + for (; offset < length - 1; offset++) { + if (ID3$1.isHeader(data, offset)) { + const embedId3 = new ID3$1(data.subarray(offset), this.logger); + if (embedId3.length > 0) { + offset += embedId3.length; // parses the interleaved ID3 packet + const localPts = embedId3.hasTimeStamp ? 90 * embedId3.timeStamp : pts; + id3Track.id3Samples.push({ pts: localPts, dts: localPts, data: embedId3.payload, frames: embedId3.frames }); + } + else { + this.logger.error(loggerName$j, `[id3] invalid length ${length}`); + } + } + if (data[offset] === 255 && (data[offset + 1] & 246) === 240) { + break; + } + } + } + else { + break; + } + } + this.esRemuxer.remuxEsTracks(this.audioTrack, undefined, id3Track, undefined, timeOffset, contiguous, accurateTimeOffset, keyTagInfo); + } + } + + /* + * Utility methods to perform bit manipulations. + * + * 2019 Apple Inc. All rights reserved. + */ + class BitstreamUtils { + /** + * This method is used to read a value from a bit-range in the data buffer. + * + * @param {Uint8Array} data - Data buffer. + * @param {BitStream} bitStream - Current position in the data buffer; gets updated by this method. + * @param {number} numBits - Total number of bits to read. + * + * @return {number} Numeric value of the bits read. + * + * Usage: bsReadAndUpdate(data, {byteOffset: 1, usedBits: 3}, 7) + * => will read 5 bits from data[1] and 2 bits from data[2] and return the numeric value of those 7 bits. + * => will update the bitStream to {byteOffset: 2, usedBits: 2} + */ + bsReadAndUpdate(data, bitStream, numBits) { + const result = this.readBits(data, bitStream, numBits); + this.updateOffset(bitStream, numBits); + return result; + } + /** + * This method is used to write a value to a bit-range in the data buffer. + * + * @param {Uint8Array} data - Data buffer. + * @param {BitStream} bitStream - Current position in the data buffer; gets updated by this method. + * @param {number} numBits - Total number of bits to write. + * @param {number} value - The value to write. + * + * Usage: bsWriteAndUpdate(data, {byteOffset: 1, usedBits: 3}, 7) + * => will write most significant 5 bits of value to data[1] and remaining 4 bits to data[2]. + * => will update the bitStream to {byteOffset: 2, usedBits: 2} + */ + bsWriteAndUpdate(data, bitStream, numBits, value) { + const result = this.writeBits(data, bitStream, numBits, value); + this.updateOffset(bitStream, numBits); + return result; + } + /** + * This method is used to update the bitStream offsets. + * + * @param {BitStream} bitStream - Current offset (In) / New offset (Out). + * @param {number} numBits - Total number of bits to skip. + * + * Usage: bsSkip({byteOffset: 1, usedBits: 3}, 7) + * => will update the bitStream to {byteOffset: 2, usedBits: 2} + */ + bsSkip(bitStream, numBits) { + this.updateOffset(bitStream, numBits); + } + // private helper methods + readBits(data, bitStream, numBits) { + if (!data || !bitStream) { + return undefined; + } + let offset = bitStream.byteOffset; + const { usedBits } = bitStream; + if (usedBits >= 8 || usedBits + numBits > 32) { + return undefined; + } + let result; + // some strong typed variables for reliable bit manipulation + const temp = new Uint32Array(1); // unsigned 32 bit for temporary storage + const mask = new Uint32Array(1); // unsigned 32 bit mask value + const byte = new Uint8Array(1); // unsigned 8 bit for temporary storage + if (usedBits >= 8 || numBits > 32) { + return undefined; + } + /* + * read msb to lsb from data[offset] starting from the first unused bit of data[offset] + * for e.g. data[0] => 11110010 + * data[1] => 00111101 + * data[2] => 00101001 + * numBits => 18 + * usedBits => 2 + * byteOffset => 0 + * will fetch bits + * data[0] => xx100011 + * data[1] => 11011100 + * data[2] => 1011xxxx + * and return + * 00000000 00000010 00111101 11001011 + */ + if (usedBits) { + // read unused bits from the partial byte + const bits = 8 - usedBits; + const shift = numBits < bits ? bits - numBits : 0; + mask[0] = 4278190080 >>> (32 - bits); + result = (data[offset] & mask[0]) >>> shift; + offset += 1; + numBits -= bits; + } + while (numBits > 0) { + byte[0] = data[offset]; + // read remaining bits, upto 8 bits at a time + const bits = Math.min(numBits, 8); + const shift = 8 - bits; + mask[0] = (4278190080 >>> (24 + shift)) << shift; + temp[0] = (byte[0] & mask[0]) >> shift; + result = !result ? temp[0] : (result << bits) | temp[0]; + offset += 1; + numBits -= bits; + } + return result; + } + writeBits(data, bitStream, numBits, value) { + if (!data || !bitStream) { + return undefined; + } + let offset = bitStream.byteOffset; + const { usedBits } = bitStream; + if (usedBits >= 8 || usedBits + numBits > 32) { + return undefined; + } + // some strong typed variables for reliable bit manipulation + const tval = new Uint32Array(1); // unsigned 32 bit to store the incoming value + const temp = new Uint32Array(1); // unsigned 32 bit for temporary storage + const mask = new Uint32Array(1); // unsigned 32 bit mask value + const byte = new Uint8Array(1); // unsigned 8 bit for temporary storage + tval[0] = value; + /* + * write msb to lsb from value into data[offset] starting from the first unused bit of data[offset] + * for e.g. value => 00000000 00000010 00111101 11001011 + * numBits => 18 + * usedBits => 2 + * byteOffset => 0 + * will get written as + * data[0] => xx100011 + * data[1] => 11011100 + * data[2] => 1011xxxx + */ + if (usedBits) { + // left align the value, mask the bits, and then right align to exclude the used bits + temp[0] = tval[0] << (32 - numBits); + mask[0] = 4278190080; + byte[0] = (temp[0] & mask[0]) >>> (24 + usedBits); + // clear the bits and write + data[offset] &= ~(mask[0] >>> (24 + usedBits)); + data[offset] |= byte[0]; + offset += 1; + numBits -= 8 - usedBits; + } + while (numBits > 0) { + // left align the remaining bits of value and write in blocks of 8 + temp[0] = tval[0] << (32 - numBits); + mask[0] = 4278190080; + byte[0] = (temp[0] & mask[0]) >>> 24; + // right align the mask, and then right shift and left shift to clear the used bits + const shift = numBits < 0 ? 8 - numBits : 0; + data[offset] &= ~(((mask[0] >>> 24) >>> shift) << shift); + // write the bits + data[offset] |= byte[0]; + numBits -= 8; + offset += 1; + } + return 0; + } + updateOffset(bitStream, numBits) { + if (!bitStream || !numBits || bitStream.usedBits + numBits > 32) { + return; + } + // calculate the number of bits seen in the current byte offset + const bitsSeenInByte = bitStream.usedBits % 8; + // calculate the bytes and bits based on the last read/write operation + const bytesAdvanced = Math.floor((bitsSeenInByte + numBits) / 8); + const bitsAdvanced = (bitsSeenInByte + numBits) % 8; + // update the new position + bitStream.byteOffset += bytesAdvanced; + bitStream.usedBits = bitsAdvanced; + } + } + + const loggerName$i = { name: 'Dolby' }; + const samplingRateMap = [48000, 44100, 32000]; + const frameSizeMap = [ + 64, + 69, + 96, + 64, + 70, + 96, + 80, + 87, + 120, + 80, + 88, + 120, + 96, + 104, + 144, + 96, + 105, + 144, + 112, + 121, + 168, + 112, + 122, + 168, + 128, + 139, + 192, + 128, + 140, + 192, + 160, + 174, + 240, + 160, + 175, + 240, + 192, + 208, + 288, + 192, + 209, + 288, + 224, + 243, + 336, + 224, + 244, + 336, + 256, + 278, + 384, + 256, + 279, + 384, + 320, + 348, + 480, + 320, + 349, + 480, + 384, + 417, + 576, + 384, + 418, + 576, + 448, + 487, + 672, + 448, + 488, + 672, + 512, + 557, + 768, + 512, + 558, + 768, + 640, + 696, + 960, + 640, + 697, + 960, + 768, + 835, + 1152, + 768, + 836, + 1152, + 896, + 975, + 1344, + 896, + 976, + 1344, + 1024, + 1114, + 1536, + 1024, + 1115, + 1536, + 1152, + 1253, + 1728, + 1152, + 1254, + 1728, + 1280, + 1393, + 1920, + 1280, + 1394, + 1920, + ]; + const Dolby = { + getFrameDuration: function (config, timescale) { + return (1536 / config.samplerate) * timescale; + }, + getAudioConfig: function (observer, data, offset, logger) { + let payload; + if (offset + 8 > data.length) { + payload = new FragParsingError(true, 'error parsing ac-3, not enough data', ErrorResponses.InsufficientAC3Data); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return undefined; + } + if (data[offset] !== 11 || data[offset + 1] !== 119) { + // payload = { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: true, reason: 'invalid ac-3 magic' }; + payload = new FragParsingError(true, 'invalid ac-3 magic', ErrorResponses.InvalidAC3Magic); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return undefined; + } + const samplingRateCode = data[offset + 4] >> 6; + if (samplingRateCode >= 3) { + payload = new FragParsingError(true, `invalid ac-3 samplingRateCode:${samplingRateCode}`, ErrorResponses.InvalidAC3SamplingRateCode); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return undefined; + } + // get frame size + const frameSizeCode = data[offset + 4] & 63; + const frameLength = frameSizeMap[frameSizeCode * 3 + samplingRateCode] * 2; + const channelMode = data[offset + 6] >> 5; + let skipCount = 0; + if (channelMode === 2) { + skipCount += 2; + } + else { + if (channelMode & 1 && channelMode !== 1) { + skipCount += 2; + } + if (channelMode & 4) { + skipCount += 2; + } + } + const lfeon = (((data[offset + 6] << 8) | data[offset + 7]) >> (12 - skipCount)) & 1; + const channelsMap = [2, 1, 2, 3, 3, 4, 4, 5]; + const channelCount = channelsMap[channelMode] + lfeon; + const bsid = data[offset + 5] >> 3; + const bsmod = data[offset + 5] & 7; + const extraData = (samplingRateCode << 22) | (bsid << 17) | (bsmod << 14) | (channelMode << 11) | (lfeon << 10) | ((frameSizeCode >> 1) << 5); + const samplerate = samplingRateMap[samplingRateCode]; + logger.info(loggerName$i, `parsed codec: ac-3, rate:${samplerate}, nb channel:${channelCount}, first frameLength:${frameLength}`); + const audioConfig = { samplerate: samplerate, channelCount: channelCount, segmentCodec: 'ac3', codec: 'ac-3', extraData: extraData }; + return audioConfig; + }, + getFrameLength: function (observer, data, offset) { + let payload; + if (offset + 8 > data.length) { + payload = new FragParsingError(true, 'error parsing ac-3, not enough data', ErrorResponses.InsufficientAC3Data); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return undefined; + } + if (data[offset] !== 11 || data[offset + 1] !== 119) { + payload = new FragParsingError(true, 'invalid ac-3 magic', ErrorResponses.InvalidAC3Magic); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return undefined; + } + const samplingRateCode = data[offset + 4] >> 6; + if (samplingRateCode >= 3) { + payload = new FragParsingError(true, `invalid ac-3 samplingRateCode:${samplingRateCode}`, ErrorResponses.InvalidAC3SamplingRateCode); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return undefined; + } + // get frame size + const frameSizeCode = data[offset + 4] & 63; + return frameSizeMap[frameSizeCode * 3 + samplingRateCode] * 2; + }, + }; + + const loggerName$h = { name: 'AC3Demuxer' }; + class AC3Demuxer extends EsDemuxer { + resetInitSegment(initSegment, duration) { + this.audioConfig = undefined; + this.audioTrack = undefined; + this.duration = duration; + } + static probe(data, logger) { + // check if data contains ID3 timestamp and AC3 sync bytes + const id3 = new ID3$1(data, logger), offset = id3.length; + // look for the ac-3 sync bytes + if (id3.hasTimeStamp && data[offset] === 11 && data[offset + 1] === 119) { + // check the bsid to confirm ac-3 + const bu = new BitstreamUtils(); + const bsid = bu.bsReadAndUpdate(data, { byteOffset: offset + 5, usedBits: 0 }, 5); + if (bsid < 16) { + return true; + } + } + return false; + } + // feed incoming data to the front of the parsing pipeline + append(data, timeOffset, contiguous, accurateTimeOffset, keyTagInfo) { + const id3 = new ID3$1(data, this.logger); + const pts = 90 * id3.timeStamp; + const length = data.byteLength; + let frameIndex = 0; + let offset = id3.length; + if (!this.audioConfig) { + this.audioConfig = Dolby.getAudioConfig(this.observer, data, offset, this.logger); + } + if (!this.audioConfig) { + throw 'failed to parse ac3 config'; + } + if (!this.audioTrack) { + const info = { id: 258, inputTimescale: 90000, timescale: NaN, duration: this.duration, encrypted: false, keyTagInfo }; + const parsingData = { len: 0, sequenceNumber: 0, esSamples: [] }; + this.audioTrack = { info, parsingData, type: 'audio', config: this.audioConfig }; + } + const frameDuration = Dolby.getFrameDuration(this.audioConfig, this.audioTrack.info.inputTimescale); // (1536 / this.audioConfig.samplerate) * this.audioTrack.inputTimescale; + if (id3.audioType === 'zac3') { + this.audioTrack.info.encrypted = true; + this.logger.info(loggerName$h, 'found encrypted ac3'); + } + while (offset < length) { + if (ID3$1.isHeader(data, offset)) { + const id3 = new ID3$1(data.subarray(offset), this.logger); + offset += id3.length; // skip the interleaved ID3 packet + } + if (data[offset] !== 11 || data[offset + 1] !== 119) { + const payload = new FragParsingError(true, 'invalid ac-3 magic', ErrorResponses.InvalidAC3Magic); + this.observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return; + } + const frameLength = Dolby.getFrameLength(this.observer, data, offset); + const stamp = pts + frameIndex * frameDuration; + const ac3Sample = { unit: data.subarray(offset, offset + frameLength), pts: stamp, dts: stamp, keyTagInfo: keyTagInfo }; + this.audioTrack.parsingData.esSamples.push(ac3Sample); + this.audioTrack.parsingData.len += frameLength; + offset += frameLength; + frameIndex++; + } + this.esRemuxer.remuxEsTracks(this.audioTrack, undefined, { id3Samples: [{ pts: pts, dts: pts, data: id3.payload, frames: id3.frames }], inputTimescale: this.audioTrack.info.inputTimescale }, undefined, timeOffset, contiguous, accurateTimeOffset, keyTagInfo); + } + } + + const loggerName$g = { name: 'DDPlus' }; + const DDPlus = { + getFrameLength: function (observer, data, offset, logger) { + const bs = new BitstreamUtils(); + let firstIndSubstream = false; + let totalFrameLength = 0; + let payload; + while (offset < data.length) { + if (offset + 8 > data.length) { + payload = new FragParsingError(true, 'error parsing ec-3, not enough data', ErrorResponses.InsufficientEC3Data); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return undefined; + } + // skip the ID3 packet, if present + let id3Length = 0; + if (ID3$1.isHeader(data, offset)) { + const id3 = new ID3$1(data.subarray(offset), logger); + id3Length = id3.length || 0; + offset += id3Length; + } + // get syncword (16 bits) + if (data[offset] !== 11 || data[offset + 1] !== 119) { + payload = new FragParsingError(true, 'invalid ec-3 magic', ErrorResponses.InvalidEC3Magic); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return undefined; + } + // skip the syncword and start parsing + const bitStream = { byteOffset: offset + 2, usedBits: 0 }; + // get strmtyp & substreamid + const strmtyp = bs.bsReadAndUpdate(data, bitStream, 2); + const substreamid = bs.bsReadAndUpdate(data, bitStream, 3); + if (strmtyp === 0 || strmtyp === 2) { + if (firstIndSubstream === true) { + if (substreamid === 0) { + // we're seen all dependent sub-streams + break; + } + } + else { + firstIndSubstream = true; // mark that the first independent substream is seen + } + } + else if (strmtyp !== 1) { + payload = new FragParsingError(true, 'reserved stream type', ErrorResponses.ReservedStreamType); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return undefined; + } + // get frmsiz + const frmsiz = bs.bsReadAndUpdate(data, bitStream, 11); + // advance to the next syncframe + const frameLength = (frmsiz + 1) * 2; + offset += frameLength; + totalFrameLength += frameLength + (id3Length || 0); + } + return totalFrameLength; + }, + getAudioConfig: function (observer, data, offset, logger) { + const frameInfo = { + frmsiz: 0, + fscod: 0, + numblkscod: 0, + acmod: 0, + lfeon: 0, + bsid: 0, + strmtyp: 0, + substreamid: 0, + chanmape: 0, + chanmap: 0, + mixdef: 0, + mixdeflen: 0, + bsmod: 0, + }; + const sampleInfo = { + fscod: 0, + acmod: 0, + lfeon: 0, + bsid: 0, + bsmod: 0, + chan_loc: 0, + data_rate: 0, + num_ind_sub: 0, + num_dep_sub: [], + complexity_index_type_a: 0, + }; + const bs = new BitstreamUtils(); + let firstIndSubstream = false; + let totalFrameLength = 0; + let payload; + while (offset < data.length) { + if (offset + 8 > data.length) { + payload = new FragParsingError(true, 'error parsing ec-3, not enough data', ErrorResponses.InsufficientEC3Data); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return undefined; + } + // skip the ID3 packet, if present + let id3Length = 0; + if (ID3$1.isHeader(data, offset)) { + const id3 = new ID3$1(data.subarray(offset), logger); + id3Length = id3.length || 0; + offset += id3Length; + } + // get syncword (16 bits) + if (data[offset] !== 11 || data[offset + 1] !== 119) { + payload = new FragParsingError(true, 'invalid ec-3 magic', ErrorResponses.InvalidEC3Magic); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return undefined; + } + // skip the syncword and start parsing + const bitStream = { byteOffset: offset + 2, usedBits: 0 }; + // get strmtyp & substreamid + frameInfo.strmtyp = bs.bsReadAndUpdate(data, bitStream, 2); + frameInfo.substreamid = bs.bsReadAndUpdate(data, bitStream, 3); + if (frameInfo.strmtyp === 0 || frameInfo.strmtyp === 2) { + if (firstIndSubstream === true) { + if (frameInfo.substreamid === 0) { + // we're seen all dependent sub-streams + break; + } + } + else { + firstIndSubstream = true; // mark that the first independent substream is seen + } + sampleInfo.num_ind_sub++; // independent substream + sampleInfo.num_dep_sub.push(0); // initialize the dependent sub-stream count to 0 + } + else if (frameInfo.strmtyp === 1) { + sampleInfo.num_dep_sub[sampleInfo.num_ind_sub - 1]++; // dependent substream + } + else { + payload = new FragParsingError(true, 'reserved stream type', ErrorResponses.ReservedStreamType); + observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return undefined; + } + // get frmsiz + frameInfo.frmsiz = bs.bsReadAndUpdate(data, bitStream, 11); + // get fscod, numblkscod + frameInfo.fscod = bs.bsReadAndUpdate(data, bitStream, 2); + if (frameInfo.fscod === 3) { + bs.bsSkip(bitStream, 2); + frameInfo.numblkscod = 3; + } + else { + frameInfo.numblkscod = bs.bsReadAndUpdate(data, bitStream, 2); + } + // get acmod + frameInfo.acmod = bs.bsReadAndUpdate(data, bitStream, 3); + // get lfeon + frameInfo.lfeon = bs.bsReadAndUpdate(data, bitStream, 1); + // get bsid + frameInfo.bsid = bs.bsReadAndUpdate(data, bitStream, 5); + bs.bsSkip(bitStream, 5); + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 8); + } + if (frameInfo.acmod === 0) { + bs.bsSkip(bitStream, 5); + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 8); + } + } + if (frameInfo.strmtyp === 1) { + // get chanmape + frameInfo.chanmape = bs.bsReadAndUpdate(data, bitStream, 1); + if (frameInfo.chanmape) { + // get chanmap + frameInfo.chanmap = bs.bsReadAndUpdate(data, bitStream, 16); + } + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + if (frameInfo.acmod > 2) { + bs.bsSkip(bitStream, 2); + } + if (frameInfo.acmod & 1 && frameInfo.acmod > 2) { + bs.bsSkip(bitStream, 6); + } + if (frameInfo.acmod & 4) { + bs.bsSkip(bitStream, 6); + } + if (frameInfo.lfeon) { + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 5); + } + } + if (frameInfo.strmtyp === 0) { + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 6); + } + if (frameInfo.acmod === 0) { + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 6); + } + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 6); + } + // get mixdef + frameInfo.mixdef = bs.bsReadAndUpdate(data, bitStream, 2); + if (frameInfo.mixdef === 1) { + bs.bsSkip(bitStream, 5); + } + else if (frameInfo.mixdef === 2) { + bs.bsSkip(bitStream, 12); + } + else if (frameInfo.mixdef === 3) { + // get mixdeflen + frameInfo.mixdeflen = bs.bsReadAndUpdate(data, bitStream, 5); + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 5); + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 4); + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 4); + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 4); + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 4); + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 4); + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 4); + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 4); + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 4); + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 4); + } + } + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 5); + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 7); + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 8); + } + } + } + // skip entire block that includes mixdata and mixdatafill + const skipBytes = frameInfo.mixdeflen + 2 + (bitStream.usedBits ? 1 : 0); + bitStream.byteOffset += skipBytes; + } + if (frameInfo.acmod < 2) { + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 14); + } + if (frameInfo.acmod === 0) { + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 14); + } + } + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + if (frameInfo.numblkscod === 0) { + bs.bsSkip(bitStream, 5); + } + else { + for (let i = 0; i < frameInfo.numblkscod; i++) { + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 5); + } + } + } + } + } + } + frameInfo.bsmod = 0; + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + frameInfo.bsmod = bs.bsReadAndUpdate(data, bitStream, 3); + bs.bsSkip(bitStream, 2); + if (frameInfo.acmod === 2) { + bs.bsSkip(bitStream, 4); + } + if (frameInfo.acmod >= 6) { + bs.bsSkip(bitStream, 2); + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 8); + } + if (frameInfo.acmod === 0) { + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + bs.bsSkip(bitStream, 8); + } + } + if (frameInfo.fscod < 3) { + bs.bsSkip(bitStream, 1); + } + } + if (frameInfo.strmtyp === 0 && frameInfo.numblkscod !== 3) { + bs.bsSkip(bitStream, 1); + } + if (frameInfo.strmtyp === 2) { + let blkid; + if (frameInfo.numblkscod === 3) { + blkid = 1; + } + else { + blkid = bs.bsReadAndUpdate(data, bitStream, 1); + } + if (blkid) { + bs.bsReadAndUpdate(data, bitStream, 6); + } + } + if (bs.bsReadAndUpdate(data, bitStream, 1)) { + const addbsil = bs.bsReadAndUpdate(data, bitStream, 6); + if (frameInfo.strmtyp === 0 && frameInfo.substreamid === 0 && addbsil === 1) { + const flag_ec3_extension_type_reserved = bs.bsReadAndUpdate(data, bitStream, 7); + const flag_ec3_extension_type_a = bs.bsReadAndUpdate(data, bitStream, 1); + const complexity_index_type_a = bs.bsReadAndUpdate(data, bitStream, 8); + // Make sure the values are in range and if yes, flag ATMOS + if (flag_ec3_extension_type_reserved === 0 && flag_ec3_extension_type_a === 1 && complexity_index_type_a >= 1 && complexity_index_type_a <= 16) { + sampleInfo.complexity_index_type_a = complexity_index_type_a; + } + } + } + // find channel map + if (frameInfo.chanmape) { + sampleInfo.chan_loc |= frameInfo.chanmap; + } + else { + // look up channel map using acmod + const acmodToChannelMap = [ + 40960, + 16384, + 40960, + 57344, + 41472, + 57856, + 47104, + 63488, + ]; + sampleInfo.chan_loc |= acmodToChannelMap[frameInfo.acmod]; + } + if (frameInfo.strmtyp === 0) { + sampleInfo.fscod = frameInfo.fscod; + sampleInfo.bsid = frameInfo.bsid; + sampleInfo.bsmod = frameInfo.bsmod; + sampleInfo.acmod = frameInfo.acmod; + sampleInfo.lfeon = frameInfo.lfeon; + } + sampleInfo.chan_loc |= frameInfo.lfeon ? 1 : 0; + // advance to the next syncframe + const frameLength = (frameInfo.frmsiz + 1) * 2; + offset += frameLength; + totalFrameLength += frameLength + (id3Length || 0); + } + let channelCount = 0; + // get channel count + for (let i = 0; i < 16; i++) { + if (sampleInfo.chan_loc & (1 << i)) { + channelCount++; + } + } + if (sampleInfo.lfeon) { + channelCount++; + } + // generate DD+ magic cookie + let cookieSize = 10 + sampleInfo.num_ind_sub * 3; + const samplingRateMap = [48000, 44100, 32000]; + const samplerate = samplingRateMap[sampleInfo.fscod]; + sampleInfo.data_rate = (samplerate / 1536) * totalFrameLength * 8; + cookieSize = 10 + sampleInfo.num_ind_sub * 3; + for (let i = 0; i < sampleInfo.num_ind_sub; i++) { + if (sampleInfo.num_dep_sub[i] > 0) { + cookieSize++; + } + } + // for ATMOS + if (sampleInfo.complexity_index_type_a > 0) { + cookieSize += 2; + } + // write the cookie + const extraDataBytes = new Uint8Array(cookieSize); + const bitStream = { byteOffset: 0, usedBits: 0 }; + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 32, cookieSize); + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 32, 1684366131); // 'dec3' + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 13, sampleInfo.data_rate); // data_rate + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 3, sampleInfo.num_ind_sub); // num_ind_sub + for (let i = 0; i < sampleInfo.num_ind_sub; i++) { + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 2, sampleInfo.fscod); // fscod + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 5, sampleInfo.bsid); // bsid + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 1, 0); // reserved + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 1, i === 0 ? 0 : 1); // asvc + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 3, sampleInfo.bsmod); // bsmod + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 3, sampleInfo.acmod); // acmod + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 1, sampleInfo.lfeon); // lfeon + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 3, 0); // reserved + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 4, sampleInfo.num_dep_sub[i]); // num_dep_sub + if (sampleInfo.num_dep_sub[i] > 0) { + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 9, sampleInfo.chan_loc); // chan_loc + } + else { + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 1, 0); // reserved + } + } + if (sampleInfo.complexity_index_type_a > 0) { + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 7, 0); // flag_ec3_extension_type_reserved; reserved as 0 + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 1, 1); // flag_ec3_extension_type_a + bs.bsWriteAndUpdate(extraDataBytes, bitStream, 8, sampleInfo.complexity_index_type_a); // complexity_index_type_a + } + logger.debug(loggerName$g, `EC3 sampleInfo:${JSON.stringify(sampleInfo)}`); + logger.info(loggerName$g, `parsed codec:ec-3, isAtmos: ${sampleInfo.complexity_index_type_a > 0}, rate:${samplerate}, nb channel:${channelCount}, first totalFrameLength:${totalFrameLength}`); + const audioConfig = { samplerate: samplerate, channelCount: channelCount, segmentCodec: 'ec3', codec: 'ec-3', extraDataBytes: extraDataBytes }; + return audioConfig; + }, + }; + var DDPlus$1 = DDPlus; + + const loggerName$f = { name: 'EC3Demuxer' }; + class EC3Demuxer extends EsDemuxer { + resetInitSegment(initSegment, duration) { + this.audioConfig = undefined; + this.audioTrack = undefined; + this.duration = duration; + } + static probe(data, logger) { + // check if data contains ID3 timestamp and EC3 sync bytes + const id3 = new ID3$1(data, logger), offset = id3.length; + // look for the ec-3 sync bytes + if (id3.hasTimeStamp && data[offset] === 11 && data[offset + 1] === 119) { + // check the bsid to confirm ec-3 + const bu = new BitstreamUtils(); + const bsid = bu.bsReadAndUpdate(data, { byteOffset: offset + 5, usedBits: 0 }, 5); + if (bsid === 16) { + return true; + } + } + return false; + } + // feed incoming data to the front of the parsing pipeline + append(data, timeOffset, contiguous, accurateTimeOffset, keyTagInfo) { + const id3 = new ID3$1(data, this.logger); + const pts = 90 * id3.timeStamp; + const length = data.length; + let frameIndex = 0; + let offset = id3.length; + if (!this.audioConfig) { + this.audioConfig = DDPlus$1.getAudioConfig(this.observer, data, offset, this.logger); + } + if (!this.audioConfig) { + throw 'failed to parse ec-3 config'; + } + if (!this.audioTrack) { + const info = { id: 258, inputTimescale: 90000, timescale: NaN, duration: this.duration, encrypted: false, keyTagInfo }; + const parsingData = { len: 0, sequenceNumber: 0, esSamples: [] }; + this.audioTrack = { info, parsingData, type: 'audio', config: this.audioConfig }; + } + const frameDuration = Dolby.getFrameDuration(this.audioConfig, this.audioTrack.info.inputTimescale); // (1536 / this.audioConfig.samplerate) * this.audioTrack.inputTimescale; + if (id3.audioType === 'zec3') { + this.audioTrack.info.encrypted = true; + this.logger.info(loggerName$f, 'found encrypted ec3'); + } + while (offset < length) { + const frameLength = DDPlus$1.getFrameLength(this.observer, data, offset, this.logger); + const stamp = pts + frameIndex * frameDuration; + const ec3Sample = { unit: data.subarray(offset, offset + frameLength), pts: stamp, dts: stamp, keyTagInfo: keyTagInfo }; + this.audioTrack.parsingData.esSamples.push(ec3Sample); + this.audioTrack.parsingData.len += frameLength; + offset += frameLength; + frameIndex++; + } + this.esRemuxer.remuxEsTracks(this.audioTrack, undefined, { id3Samples: [{ pts: pts, dts: pts, data: id3.payload, frames: id3.frames }], inputTimescale: this.audioTrack.info.inputTimescale }, undefined, timeOffset, contiguous, accurateTimeOffset, keyTagInfo); + } + } + + + const MpegAudio = { + BitratesMap: [ + 32, + 64, + 96, + 128, + 160, + 192, + 224, + 256, + 288, + 320, + 352, + 384, + 416, + 448, + 32, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 160, + 192, + 224, + 256, + 320, + 384, + 32, + 40, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 160, + 192, + 224, + 256, + 320, + 32, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 144, + 160, + 176, + 192, + 224, + 256, + 8, + 16, + 24, + 32, + 40, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 144, + 160, + ], + SamplingRateMap: [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000], + SamplesCoefficients: [ + // MPEG 2.5 + [ + 0, + 72, + 144, + 12, + ], + // Reserved + [ + 0, + 0, + 0, + 0, + ], + // MPEG 2 + [ + 0, + 72, + 144, + 12, + ], + // MPEG 1 + [ + 0, + 144, + 144, + 12, + ], + ], + BytesInSlot: [ + 0, + 1, + 1, + 4, + ], + onFrame: function (parsingData, data, bitRate, samplerate, channelCount, frameIndex, pts) { + const frameDuration = 103680000 / samplerate; + const stamp = pts + frameIndex * frameDuration; + parsingData.esSamples.push({ unit: data, pts: stamp, dts: stamp }); + parsingData.len += data.length; + }, + onNoise: function (data, logger) { + logger.warn('mpeg audio has noise: ' + data.length + ' bytes'); + }, + parseFrames: function (parsingData, data, start, end, frameIndex, pts, logger) { + const BitratesMap = [ + 32, + 64, + 96, + 128, + 160, + 192, + 224, + 256, + 288, + 320, + 352, + 384, + 416, + 448, + 32, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 160, + 192, + 224, + 256, + 320, + 384, + 32, + 40, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 160, + 192, + 224, + 256, + 320, + 32, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 144, + 160, + 176, + 192, + 224, + 256, + 8, + 16, + 24, + 32, + 40, + 48, + 56, + 64, + 80, + 96, + 112, + 128, + 144, + 160, + ]; + const SamplingRateMap = [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000]; + if (start + 2 > end) { + return -1; // we need at least 2 bytes to detect sync pattern + } + if (data[start] === 255 || (data[start + 1] & 224) === 224) { + // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference + if (start + 24 > end) { + return -1; + } + const headerB = (data[start + 1] >> 3) & 3; + const headerC = (data[start + 1] >> 1) & 3; + const headerE = (data[start + 2] >> 4) & 15; + const headerF = (data[start + 2] >> 2) & 3; + const headerG = !!(data[start + 2] & 2); + if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) { + const columnInBitrates = headerB === 3 ? 3 - headerC : headerC === 3 ? 3 : 4; + const bitRate = BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000; + const columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2; + const sampleRate = SamplingRateMap[columnInSampleRates * 3 + headerF]; + const padding = headerG ? 1 : 0; + const channelCount = data[start + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono) + const frameLength = headerC === 3 ? (((headerB === 3 ? 12 : 6) * bitRate) / sampleRate + padding) << 2 : (((headerB === 3 ? 144 : 72) * bitRate) / sampleRate + padding) | 0; + if (start + frameLength > end) { + return -1; + } + MpegAudio.onFrame(parsingData, data.subarray(start, start + frameLength), bitRate, sampleRate, channelCount, frameIndex, pts); + return frameLength; + } + } + // noise or ID3, trying to skip + let offset = start + 2; + while (offset < end) { + if (data[offset - 1] === 255 && (data[offset] & 224) === 224) { + // sync pattern is found + MpegAudio.onNoise(data.subarray(start, offset - 1), logger); + return offset - start - 1; + } + offset++; + } + return -1; + }, + parse: function (parsingData, data, offset, pts, logger) { + const length = data.length; + let frameIndex = 0; + let parsed; + while (offset < length && (parsed = MpegAudio.parseFrames(parsingData, data, offset, length, frameIndex++, pts, logger)) > 0) { + offset += parsed; + } + }, + getAudioConfig: function (data, offset) { + const headerB = (data[offset + 1] >> 3) & 3; + const headerC = (data[offset + 1] >> 1) & 3; + const headerE = (data[offset + 2] >> 4) & 15; + const headerF = (data[offset + 2] >> 2) & 3; + const headerG = (data[offset + 2] >> 1) & 1; + if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) { + const columnInBitrates = headerB === 3 ? 3 - headerC : headerC === 3 ? 3 : 4; + const bitRate = MpegAudio.BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000; + const columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2; + const samplerate = MpegAudio.SamplingRateMap[columnInSampleRates * 3 + headerF]; + const channelCount = data[offset + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono) + const sampleCoefficient = MpegAudio.SamplesCoefficients[headerB][headerC]; + const bytesInSlot = MpegAudio.BytesInSlot[headerC]; + const frameLength = parseInt(((sampleCoefficient * bitRate) / samplerate + headerG), 10) * bytesInSlot; + const result = { segmentCodec: 'mp3', codec: 'mp3', samplerate, channelCount, frameLength }; + return result; + } + return undefined; + }, + isHeaderPattern: function (data, offset) { + return data[offset] === 255 && (data[offset + 1] & 224) === 224 && (data[offset + 1] & 6) !== 0; + }, + probe: function (data, offset) { + // same as isHeader but we also check that MPEG frame follows last MPEG frame + // or end of data is reached + if (offset + 1 < data.length && MpegAudio.isHeaderPattern(data, offset)) { + // MPEG header Length + const headerLength = 4; + // MPEG frame Length + const header = MpegAudio.getAudioConfig(data, offset); + let frameLength = headerLength; + if (header && header.frameLength) { + frameLength = header.frameLength; + } + const newOffset = offset + frameLength; + if (newOffset === data.length || (newOffset + 1 < data.length && MpegAudio.isHeaderPattern(data, newOffset))) { + return true; + } + } + return false; + }, + }; + var MpegAudio$1 = MpegAudio; + + + const loggerName$e = { name: 'MP3Demuxer' }; + class MP3Demuxer extends EsDemuxer { + resetInitSegment(initSegment, duration) { + this.audioConfig = undefined; + this.audioTrack = undefined; + this.duration = duration; + } + static probe(data, logger) { + // check if data contains ID3 timestamp and MPEG sync word + const id3 = new ID3$1(data, logger); + let offset, length; + if (id3.hasTimeStamp) { + // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1 + // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) + // More info http://www.mp3-tech.org/programmer/frame_header.html + for (offset = id3.length, length = Math.min(data.length - 1, offset + 100); offset < length; offset++) { + if (MpegAudio$1.probe(data, offset)) { + logger.warn(loggerName$e, 'MPEG Audio sync word found !'); + return true; + } + } + } + return false; + } + // feed incoming data to the front of the parsing pipeline + append(data, timeOffset, contiguous, accurateTimeOffset, keyTagInfo) { + const id3 = new ID3$1(data, this.logger); + const pts = 90 * id3.timeStamp; + if (!this.audioConfig) { + this.audioConfig = MpegAudio$1.getAudioConfig(data, id3.length); + } + if (!this.audioConfig) { + throw 'unable to parse mp3 header'; + } + if (!this.audioTrack) { + const info = { id: 258, inputTimescale: 90000, timescale: NaN, duration: this.duration, encrypted: false, keyTagInfo }; + const parsingData = { len: 0, sequenceNumber: 0, esSamples: [] }; + this.audioTrack = { info, parsingData, type: 'audio', config: this.audioConfig }; + } + MpegAudio$1.parse(this.audioTrack.parsingData, data, id3.length, pts, this.logger); + this.esRemuxer.remuxEsTracks(this.audioTrack, undefined, { id3Samples: [{ pts: pts, dts: pts, data: id3.payload, frames: id3.frames }], inputTimescale: 90000 }, undefined, timeOffset, contiguous, accurateTimeOffset); + } + } + + /** + * AAC Helper + * + * + * + * + */ + function getSilentFrame(codec, channelCount) { + switch (codec) { + case 'mp4a.40.2': + if (channelCount === 1) { + return new Uint8Array([0, 200, 0, 128, 35, 128]); + } + else if (channelCount === 2) { + return new Uint8Array([33, 0, 73, 144, 2, 25, 0, 35, 128]); + } + else if (channelCount === 3) { + return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 142]); + } + else if (channelCount === 4) { + return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 128, 44, 128, 8, 2, 56]); + } + else if (channelCount === 5) { + return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 130, 48, 4, 153, 0, 33, 144, 2, 56]); + } + else if (channelCount === 6) { + return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 130, 48, 4, 153, 0, 33, 144, 2, 0, 178, 0, 32, 8, 224]); + } + break; + // handle HE-AAC below (mp4a.40.5 / mp4a.40.29) + default: + if (channelCount === 1) { + // ffmpeg -y -f lavfi -i "aevalsrc=0:d=0.05" -c:a libfdk_aac -profile:a aac_he -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac + return new Uint8Array([ + 1, + 64, + 34, + 128, + 163, + 78, + 230, + 128, + 186, + 8, + 0, + 0, + 0, + 28, + 6, + 241, + 193, + 10, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 94, + ]); + } + else if (channelCount === 2) { + // ffmpeg -y -f lavfi -i "aevalsrc=0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac + return new Uint8Array([ + 1, + 64, + 34, + 128, + 163, + 94, + 230, + 128, + 186, + 8, + 0, + 0, + 0, + 0, + 149, + 0, + 6, + 241, + 161, + 10, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 94, + ]); + } + else if (channelCount === 3) { + // ffmpeg -y -f lavfi -i "aevalsrc=0|0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac + return new Uint8Array([ + 1, + 64, + 34, + 128, + 163, + 94, + 230, + 128, + 186, + 8, + 0, + 0, + 0, + 0, + 149, + 0, + 6, + 241, + 161, + 10, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 90, + 94, + ]); + } + break; + } + return null; + } + + function isFiniteNumber(value) { + return typeof value === 'number' && isFinite(value); + } + /** + * For getting float string for a given value with type checking + * @param val Number + * @param precision Number of digits after decimal + */ + function toFixed(val, precision) { + if (isFiniteNumber(val)) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + return val.toFixed(precision); + } + return `${val}`; + } + /** + * Stringify except print numbers with fixed precision + * @param obj the value to stringify + * @param precision Number of digits after decimal. Default 3 + */ + function stringifyWithPrecision(obj, precision = 3) { + return JSON.stringify(obj, (_key, value) => { + return !isNaN(value) && (value === null || value === void 0 ? void 0 : value.toFixed) ? Number(value === null || value === void 0 ? void 0 : value.toFixed(precision)) : value; + }); + } + /** + * Replace all occurrences of an instance in a string + * + * @param {string|RegExp} search - What to change + * @param {string} replacement - Replace search for this + * @param {string} target - Target string to have elements replaced + * @returns {string} + */ + const replaceAll = (search, replacement, target = '') => target.split(search).join(replacement); + let shouldRedactUrl = true; + function setupRedactUrl(buildType) { + shouldRedactUrl = buildType === 'production'; + } + function redactUrl(url) { + return shouldRedactUrl ? '' : url; + } + // Naive deep copy of any serializable object + // shallow copy for function, symbol + function deepCpy(obj) { + if (!obj) { + return obj; + } + switch (typeof obj) { + case 'object': + if (Array.isArray(obj)) { + return obj.map(deepCpy); + } + const result = {}; + for (const [key, value] of Object.entries(obj)) { + result[key] = deepCpy(value); + } + return result; + default: + return obj; + } + } + function urlRedactedLevelInfo(indata) { + const outdata = [...indata]; + for (let i = 0; i < outdata.length; i++) { + outdata[i] = Object.assign({}, outdata[i]); + outdata[i].url = redactUrl(outdata[i].url); + if (outdata[i].attrs) { + outdata[i].attrs = Object.assign({}, outdata[i].attrs); + outdata[i].attrs.URI = redactUrl(outdata[i].attrs.URI); + } + } + return outdata; + } + function urlRedactedAltMediaOption(indata) { + const outdata = [...indata]; + for (let i = 0; i < outdata.length; i++) { + outdata[i] = Object.assign({}, outdata[i]); + outdata[i].url = redactUrl(outdata[i].url); + } + return outdata; + } + + /** + * Generate MP4 Box + * + * + * + * + */ + const UINT32_MAX$1 = Math.pow(2, 32) - 1; + class MP4 { + static init() { + MP4.types = { + avc1: [], + avcC: [], + btrt: [], + dinf: [], + dref: [], + esds: [], + free: [], + ftyp: [], + hdlr: [], + mdat: [], + mdhd: [], + mdia: [], + mfhd: [], + minf: [], + moof: [], + moov: [], + mp4a: [], + '.mp3': [], + dac3: [], + 'ac-3': [], + dec3: [], + 'ec-3': [], + mvex: [], + mvhd: [], + pasp: [], + sdtp: [], + stbl: [], + stco: [], + stsc: [], + stsd: [], + stsz: [], + stts: [], + tfdt: [], + tfhd: [], + traf: [], + trak: [], + trun: [], + trex: [], + tkhd: [], + vmhd: [], + smhd: [], + uuid: [], + encv: [], + enca: [], + // map encryption boxes + frma: [], + schm: [], + schi: [], + senc: [], + saio: [], + saiz: [], + sinf: [], + tenc: [], + // moof encryption boxes + sbgp: [], + seig: [], + sgpd: [], + pssh: [], + }; + let i; + for (i in MP4.types) { + // eslint-disable-next-line no-prototype-builtins + if (MP4.types.hasOwnProperty(i)) { + MP4.types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)]; + } + } + const videoHdlr = new Uint8Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 118, + 105, + 100, + 101, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 86, + 105, + 100, + 101, + 111, + 72, + 97, + 110, + 100, + 108, + 101, + 114, + 0, + ]); + const audioHdlr = new Uint8Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 115, + 111, + 117, + 110, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 83, + 111, + 117, + 110, + 100, + 72, + 97, + 110, + 100, + 108, + 101, + 114, + 0, + ]); + MP4.HDLR_TYPES = { + video: videoHdlr, + audio: audioHdlr, + }; + const dref = new Uint8Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 12, + 117, + 114, + 108, + 32, + 0, + 0, + 0, + 1, + ]); + const stco = new Uint8Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); + MP4.STTS = MP4.STSC = MP4.STCO = stco; + MP4.STSZ = new Uint8Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); + MP4.VMHD = new Uint8Array([ + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); + MP4.SMHD = new Uint8Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + ]); + MP4.STSD = new Uint8Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + ]); // entry_count + const majorBrand = new Uint8Array([105, 115, 111, 109]); // isom + const avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1 + const minorVersion = new Uint8Array([0, 0, 0, 1]); + MP4.FTYP = MP4.box(MP4.types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand); + MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref)); + } + static set16(num, data, index) { + data[index] = (num >> 8) & 255; + data[index + 1] = num & 255; + return index + 2; + } + static set32(num, data, index) { + data[index] = (num >> 24) & 255; + data[index + 1] = (num >> 16) & 255; + data[index + 2] = (num >> 8) & 255; + data[index + 3] = num & 255; + return index + 4; + } + static box(type, ...params) { + // eslint-disable-next-line prefer-rest-params + const payload = Array.prototype.slice.call(arguments, 1); + let size = 8, i = payload.length; + const len = i; + // calculate the total size we need to allocate + while (i--) { + size += payload[i].byteLength; + } + const result = new Uint8Array(size); + result[0] = (size >> 24) & 255; + result[1] = (size >> 16) & 255; + result[2] = (size >> 8) & 255; + result[3] = size & 255; + result.set(type, 4); + // copy the payload into the result + for (i = 0, size = 8; i < len; i++) { + // copy payload[i] array @ offset size + result.set(payload[i], size); + size += payload[i].byteLength; + } + return result; + } + static hdlr(type) { + return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]); + } + static mdat(data) { + return MP4.box(MP4.types.mdat, data); + } + static mdhd(timescale, duration) { + duration *= timescale; + const upperWordDuration = Math.floor(duration / (UINT32_MAX$1 + 1)); + const lowerWordDuration = Math.floor(duration % (UINT32_MAX$1 + 1)); + return MP4.box(MP4.types.mdhd, new Uint8Array([ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3, + (timescale >> 24) & 255, + (timescale >> 16) & 255, + (timescale >> 8) & 255, + timescale & 255, + upperWordDuration >> 24, + (upperWordDuration >> 16) & 255, + (upperWordDuration >> 8) & 255, + upperWordDuration & 255, + lowerWordDuration >> 24, + (lowerWordDuration >> 16) & 255, + (lowerWordDuration >> 8) & 255, + lowerWordDuration & 255, + 85, + 196, + 0, + 0, + ])); + } + static mdia(track) { + const mdhd = MP4.mdhd(track.info.timescale, track.info.duration); + const hdlr = MP4.hdlr(track.type); + const minf = MP4.minf(track); + return MP4.box(MP4.types.mdia, mdhd, hdlr, minf); + } + static mfhd(sequenceNumber) { + return MP4.box(MP4.types.mfhd, new Uint8Array([ + 0, + 0, + 0, + 0, + sequenceNumber >> 24, + (sequenceNumber >> 16) & 255, + (sequenceNumber >> 8) & 255, + sequenceNumber & 255, // sequence_number + ])); + } + static minf(track) { + if (track.type === 'audio') { + return MP4.box(MP4.types.minf, MP4.box(MP4.types.smhd, MP4.SMHD), MP4.DINF, MP4.stbl(track)); + } + else { + return MP4.box(MP4.types.minf, MP4.box(MP4.types.vmhd, MP4.VMHD), MP4.DINF, MP4.stbl(track)); + } + } + static moof(baseMediaDecodeTime, track) { + if (!MP4.types) { + MP4.init(); + } + const traf = MP4.traf(track, baseMediaDecodeTime); + const moof = MP4.box(MP4.types.moof, MP4.mfhd(track.sequenceNumber), traf); + return moof; + } + /** + * @param tracks... (optional) {array} the tracks associated with this movie + */ + static moov(tracks) { + let i = tracks.length; + const boxes = []; + while (i--) { + boxes[i] = MP4.trak(tracks[i]); + } + return MP4.box.apply(null, [MP4.types.moov, MP4.mvhd(tracks[0].info.timescale, tracks[0].info.duration)].concat(boxes).concat(MP4.mvex(tracks))); + } + static mvex(tracks) { + let i = tracks.length; + const boxes = []; + while (i--) { + boxes[i] = MP4.trex(tracks[i]); + } + return MP4.box(MP4.types.mvex, ...boxes); + // return MP4.box.apply(null, [MP4.types.mvex, .concat(boxes)); + } + static mvhd(timescale, duration) { + duration *= timescale; + const upperWordDuration = Math.floor(duration / (UINT32_MAX$1 + 1)); + const lowerWordDuration = Math.floor(duration % (UINT32_MAX$1 + 1)); + const bytes = new Uint8Array([ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3, + (timescale >> 24) & 255, + (timescale >> 16) & 255, + (timescale >> 8) & 255, + timescale & 255, + upperWordDuration >> 24, + (upperWordDuration >> 16) & 255, + (upperWordDuration >> 8) & 255, + upperWordDuration & 255, + lowerWordDuration >> 24, + (lowerWordDuration >> 16) & 255, + (lowerWordDuration >> 8) & 255, + lowerWordDuration & 255, + 0, + 1, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 64, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 255, + 255, + 255, + 255, + ]); + return MP4.box(MP4.types.mvhd, bytes); + } + static sdtp(track) { + const samples = track.samples || [], bytes = new Uint8Array(4 + samples.length); + let flags, i; + // leave the full box header (4 bytes) all zero + // write the sample table + for (i = 0; i < samples.length; i++) { + flags = samples[i].flags; + bytes[i + 4] = (flags.dependsOn << 4) | (flags.isDependedOn << 2) | flags.hasRedundancy; + } + return MP4.box(MP4.types.sdtp, bytes); + } + static stbl(track) { + const stsd = MP4.stsd(track); + const stts = MP4.box(MP4.types.stts, MP4.STTS); + const stsc = MP4.box(MP4.types.stsc, MP4.STSC); + const stsz = MP4.box(MP4.types.stsz, MP4.STSZ); + const stco = MP4.box(MP4.types.stco, MP4.STCO); + return MP4.box(MP4.types.stbl, stsd, stts, stsc, stsz, stco); + } + static avc1(track) { + let sps = [], pps = [], i, data, len; + // assemble the SPSs + const codingName = track.info.encrypted ? MP4.types.encv : MP4.types.avc1; + for (i = 0; i < track.config.sps.length; i++) { + data = track.config.sps[i]; + len = data.byteLength; + sps.push((len >>> 8) & 255); + sps.push(len & 255); + sps = sps.concat(Array.prototype.slice.call(data)); // SPS + } + // assemble the PPSs + for (i = 0; i < track.config.pps.length; i++) { + data = track.config.pps[i]; + len = data.byteLength; + pps.push((len >>> 8) & 255); + pps.push(len & 255); + pps = pps.concat(Array.prototype.slice.call(data)); + } + const avcc = MP4.box(MP4.types.avcC, new Uint8Array([ + 1, + sps[3], + sps[4], + sps[5], + 255, + 224 | track.config.sps.length, // 3bit reserved (111) + numOfSequenceParameterSets + ] + .concat(sps) + .concat([ + track.config.pps.length, // numOfPictureParameterSets + ]) + .concat(pps))), // "PPS" + width = track.config.width, height = track.config.height, hSpacing = track.config.pixelRatio[0], vSpacing = track.config.pixelRatio[1]; + // console.log('avcc:' + Hex.hexDump(avcc)); + const sinf = track.info.encrypted && track.info.keyTagInfo ? MP4.sinf(track.info.keyTagInfo, track.type, MP4.types.avc1) : new Uint8Array(); + // console.log('video sinf:' + Hex.hexDump(sinf)); + return MP4.box(codingName, new Uint8Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + (width >> 8) & 255, + width & 255, + (height >> 8) & 255, + height & 255, + 0, + 72, + 0, + 0, + 0, + 72, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 18, + 100, + 97, + 105, + 108, + 121, + 109, + 111, + 116, + 105, + 111, + 110, + 47, + 104, + 108, + 115, + 46, + 106, + 115, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 24, + 17, + 17, + ]), // pre_defined = -1 + avcc, sinf, MP4.box(MP4.types.btrt, new Uint8Array([ + 0, + 28, + 156, + 128, + 0, + 45, + 198, + 192, + 0, + 45, + 198, + 192, + ])), // avgBitrate + MP4.box(MP4.types.pasp, new Uint8Array([ + hSpacing >> 24, + (hSpacing >> 16) & 255, + (hSpacing >> 8) & 255, + hSpacing & 255, + vSpacing >> 24, + (vSpacing >> 16) & 255, + (vSpacing >> 8) & 255, + vSpacing & 255, + ]))); + } + static esds(config) { + const configlen = config.esdsConfig.length; + return new Uint8Array([ + 0, + 0, + 0, + 0, + 3, + 23 + configlen, + 0, + 1, + 0, + 4, + 15 + configlen, + 64, + 21, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 5, + ] + .concat([configlen]) + .concat(config.esdsConfig) + .concat([6, 1, 2])); // GASpecificConfig)); // length + audio config descriptor + } + static audioStsd(config) { + const samplerate = config.samplerate; + return new Uint8Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + config.channelCount, + 0, + 16, + 0, + 0, + 0, + 0, + (samplerate >> 8) & 255, + samplerate & 255, + 0, + 0, + ]); + } + static dac3(config) { + const extraData = config.extraData; + return new Uint8Array([(extraData >> 16) & 255, (extraData >> 8) & 255, extraData & 255]); + } + static dec3(config) { + return config.extraDataBytes; + } + static mp4a(info, config) { + let codingName = MP4.types.mp4a; + let sinf = null; + if (info.encrypted && info.keyTagInfo) { + codingName = MP4.types.enca; + sinf = MP4.sinf(info.keyTagInfo, 'audio', MP4.types.mp4a); + } + else { + sinf = new Uint8Array(); + } + const stsd = MP4.audioStsd(config); + const esds = MP4.box(MP4.types.esds, MP4.esds(config)); + return MP4.box(codingName, stsd, esds, sinf); + } + static mp3(config) { + return MP4.box(MP4.types['.mp3'], MP4.audioStsd(config)); + } + static ac3(info, config) { + let codingName = MP4.types['ac-3']; + let sinf = null; + if (info.encrypted && info.keyTagInfo) { + codingName = MP4.types.enca; + sinf = MP4.sinf(info.keyTagInfo, 'audio', MP4.types['ac-3']); + } + else { + sinf = new Uint8Array(); + } + return MP4.box(codingName, MP4.audioStsd(config), MP4.box(MP4.types.dac3, MP4.dac3(config)), sinf); + } + static ec3(info, config) { + let codingName = MP4.types['ec-3']; + let sinf = null; + if (info.encrypted && info.keyTagInfo) { + codingName = MP4.types.enca; + sinf = MP4.sinf(info.keyTagInfo, 'audio', MP4.types['ec-3']); + } + else { + sinf = new Uint8Array(); + } + return MP4.box(codingName, MP4.audioStsd(config), MP4.box(MP4.types.dec3, MP4.dec3(config)), sinf); + } + static stsd(track) { + if (track.type === 'audio') { + if (track.config.segmentCodec === 'mp3' && track.config.codec === 'mp3') { + return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp3(track.config)); + } + if (track.config.segmentCodec === 'ac3') { + return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track.info, track.config)); + } + else if (track.config.segmentCodec === 'ec3') { + return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ec3(track.info, track.config)); + } + else if (track.config.segmentCodec === 'aac') { + return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track.info, track.config)); + } + else { + throw `unknown segmentCodec ${track.config.segmentCodec}`; + } + } + else { + return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track)); + } + } + static tkhd(track) { + const id = track.info.id; + const duration = track.info.duration * track.info.timescale; + const upperWordDuration = Math.floor(duration / (UINT32_MAX$1 + 1)); + const lowerWordDuration = Math.floor(duration % (UINT32_MAX$1 + 1)); + let width = 0; + let height = 0; + if (track.type === 'video') { + width = track.config.width; + height = track.config.height; + } + return MP4.box(MP4.types.tkhd, new Uint8Array([ + 1, + 0, + 0, + 7, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 3, + (id >> 24) & 255, + (id >> 16) & 255, + (id >> 8) & 255, + id & 255, + 0, + 0, + 0, + 0, + upperWordDuration >> 24, + (upperWordDuration >> 16) & 255, + (upperWordDuration >> 8) & 255, + upperWordDuration & 255, + lowerWordDuration >> 24, + (lowerWordDuration >> 16) & 255, + (lowerWordDuration >> 8) & 255, + lowerWordDuration & 255, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 64, + 0, + 0, + 0, + (width >> 8) & 255, + width & 255, + 0, + 0, + (height >> 8) & 255, + height & 255, + 0, + 0, + ])); + } + static traf(track, baseMediaDecodeTime) { + const sencOffset = 76; // mdat header + const sampleEncryptionBoxTuple = MP4.senc(track); + const sampleDependencyTable = MP4.sdtp(track), sampleEncryptionBox = sampleEncryptionBoxTuple.boxData, sampleEncryptionOffsetBox = sampleEncryptionBox.length ? MP4.saio(sencOffset) : new Uint8Array(), sampleEncryptionSizeBox = sampleEncryptionBox.length ? MP4.saiz(sampleEncryptionBoxTuple.defaultSampleInfoSize, sampleEncryptionBoxTuple.sampleInfoSizes) : new Uint8Array(), sampleToGroupBox = MP4.sbgp(track), sampleGroupDescriptionBox = MP4.sgpd(track), id = track.id, upperWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime / (UINT32_MAX$1 + 1)), lowerWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime % (UINT32_MAX$1 + 1)); + // console.log('sampleToGroupBox:' + Hex.hexDump(sampleToGroupBox)); + // console.log('sampleGroupDescriptionBox:' + Hex.hexDump(sampleGroupDescriptionBox)); + return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([ + 0, + 2, + 0, + 0, + id >> 24, + (id >> 16) & 255, + (id >> 8) & 255, + id & 255, // track_ID + ])), MP4.box(MP4.types.tfdt, new Uint8Array([ + 1, + 0, + 0, + 0, + upperWordBaseMediaDecodeTime >> 24, + (upperWordBaseMediaDecodeTime >> 16) & 255, + (upperWordBaseMediaDecodeTime >> 8) & 255, + upperWordBaseMediaDecodeTime & 255, + lowerWordBaseMediaDecodeTime >> 24, + (lowerWordBaseMediaDecodeTime >> 16) & 255, + (lowerWordBaseMediaDecodeTime >> 8) & 255, + lowerWordBaseMediaDecodeTime & 255, + ])), sampleEncryptionBox, sampleEncryptionOffsetBox, sampleEncryptionSizeBox, sampleToGroupBox, sampleGroupDescriptionBox, MP4.trun(track, sampleDependencyTable.length + + sampleEncryptionBox.length + + sampleToGroupBox.length + + sampleGroupDescriptionBox.length + + sampleEncryptionOffsetBox.length + + sampleEncryptionSizeBox.length + 16 + // tfhd + 20 + // tfdt + 8 + // traf header + 16 + // mfhd + 8 + // moof header + 8), // mdat header + sampleDependencyTable); + } + /** + * Generate a track box. + * @param track {object} a track definition + * @return {Uint8Array} the track box + */ + static trak(track) { + if ('trakData' in track) { + // cached trak + return track.trakData; + } + track.info.duration = track.info.duration || 4294967295; + const trak = MP4.types.trak; + const thkd = MP4.tkhd(track); + const mdia = MP4.mdia(track); + return MP4.box(trak, thkd, mdia); + } + static trex(track) { + const id = track.info.id; + return MP4.box(MP4.types.trex, new Uint8Array([ + 0, + 0, + 0, + 0, + id >> 24, + (id >> 16) & 255, + (id >> 8) & 255, + id & 255, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + 0, + 1, + ])); + } + static trun(track, offset) { + const samples = track.samples || [], len = samples.length, arraylen = 12 + 16 * len, array = new Uint8Array(arraylen); + let i, sample, duration, size, flags, cts; + offset += 8 + arraylen; + array.set([ + 0, + 0, + 15, + 1, + (len >>> 24) & 255, + (len >>> 16) & 255, + (len >>> 8) & 255, + len & 255, + (offset >>> 24) & 255, + (offset >>> 16) & 255, + (offset >>> 8) & 255, + offset & 255, // data_offset + ], 0); + for (i = 0; i < len; i++) { + sample = samples[i]; + duration = sample.duration; + size = sample.size; + flags = sample.flags; + cts = sample.cts; + array.set([ + (duration >>> 24) & 255, + (duration >>> 16) & 255, + (duration >>> 8) & 255, + duration & 255, + (size >>> 24) & 255, + (size >>> 16) & 255, + (size >>> 8) & 255, + size & 255, + (flags.isLeading << 2) | flags.dependsOn, + (flags.isDependedOn << 6) | (flags.hasRedundancy << 4) | (flags.paddingValue << 1) | flags.isNonSync, + flags.degradPrio & (240 << 8), + flags.degradPrio & 15, + (cts >>> 24) & 255, + (cts >>> 16) & 255, + (cts >>> 8) & 255, + cts & 255, // sample_composition_time_offset + ], 12 + 16 * i); + } + return MP4.box(MP4.types.trun, array); + } + static initSegment(tracks) { + if (!MP4.types) { + MP4.init(); + } + const movie = MP4.moov(tracks); + const result = new Uint8Array(MP4.FTYP.byteLength + movie.byteLength); + result.set(MP4.FTYP); + result.set(movie, MP4.FTYP.byteLength); + return result; + } + // encryption boxes + static saio(sencOffset) { + const subOffset = sencOffset + 4 + 4; // skip version/flags and sample_count + return MP4.box(MP4.types.saio, new Uint8Array([ + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 1, + (subOffset >> 24) & 255, + (subOffset >> 16) & 255, + (subOffset >> 8) & 255, + subOffset & 255, + ])); + } + static saiz(defaultSampleInfoSize, sampleInfoSizes) { + if (!isFiniteNumber(defaultSampleInfoSize)) { + defaultSampleInfoSize = 0; + } + const sampleCount = sampleInfoSizes.length; + const perSampleSizeData = defaultSampleInfoSize === 0 ? new Uint8Array(sampleInfoSizes) : new Uint8Array(); + return MP4.box(MP4.types.saiz, new Uint8Array([ + 0, + 0, + 0, + 0, + defaultSampleInfoSize, + (sampleCount >> 24) & 255, + (sampleCount >> 16) & 255, + (sampleCount >> 8) & 255, + sampleCount & 255, + ]), perSampleSizeData); + } + static senc(track) { + const samples = track.samples || [], sampleCount = samples.length; + let totalSubsamples = 0; + let lastSize = NaN; + let hasDefaultSampleSize = true; + const sampleInfoSizes = []; + if (!track.encrypted || sampleCount <= 0) { + return { boxData: new Uint8Array(), sampleInfoSizes, defaultSampleInfoSize: 0 }; + } + const defaultPerSampleIVSize = track.defaultPerSampleIVSize ? track.defaultPerSampleIVSize : 0; + for (const sample of samples) { + if (sample.subsamples) { + totalSubsamples += sample.subsamples.length; + } + } + if (totalSubsamples <= 0) { + // don't create a senc if there are no subsamples + return { boxData: new Uint8Array(), sampleInfoSizes, defaultSampleInfoSize: 0 }; + } + // 4 bytes for sample_count + // 2 bytes per sample for subsample_count + // 6 bytes per subsample for unsigned int(16) BytesOfClearData and unsigned int(32) BytesOfProtectedData; + // defaultPerSampleIVSize bytes per sample + const boxdata = new Uint8Array(4 + (sampleCount * 2 + sampleCount * defaultPerSampleIVSize + totalSubsamples * 6)); + let offset = this.set32(sampleCount, boxdata, 0); + for (const sample of samples) { + const subsamples = sample.subsamples ? sample.subsamples : []; + let subsampleSize = 2; // size in bytes of the subsample entry - start with 2 for the subsample count + if (sample.iv) { + // per sample IV from cenc aux data + boxdata.set(sample.iv, offset); + offset += sample.iv.byteLength; + subsampleSize += sample.iv.byteLength; + } + offset = this.set16(subsamples.length, boxdata, offset); + for (const subsample of subsamples) { + offset = this.set16(subsample[0], boxdata, offset); + offset = this.set32(subsample[1], boxdata, offset); + subsampleSize += 6; + } + sampleInfoSizes.push(subsampleSize); + if (!isFiniteNumber(lastSize)) { + lastSize = subsampleSize; + } + hasDefaultSampleSize = hasDefaultSampleSize && lastSize === subsampleSize; + lastSize = subsampleSize; + } + const boxData = MP4.box(MP4.types.senc, new Uint8Array([ + 0, + 0, + 0, + 2, + ]), boxdata); + return { boxData, defaultSampleInfoSize: hasDefaultSampleSize ? lastSize : 0, sampleInfoSizes }; + } + static sinf(decryptdata, type, originalCodingName) { + return MP4.box(MP4.types.sinf, MP4.frma(originalCodingName), MP4.schm(), MP4.schi(decryptdata, type)); + } + static frma(originalCodingName) { + return MP4.box(MP4.types.frma, new Uint8Array(originalCodingName)); + } + static schm() { + return MP4.box(MP4.types.schm, new Uint8Array([ + 0, + 0, + 0, + 0, + 99, + 98, + 99, + 115, + 0, + 1, + 0, + 0, + ])); + } + static schi(decryptdata, type) { + return MP4.box(MP4.types.schi, MP4.tenc(decryptdata, type)); + } + static tenc(decryptdata, trackType) { + let skipPattern = 0; + if (trackType === 'video') { + skipPattern = 25; // 1 default_crypt_byte_block, 9 default_skip_byte_block + } + const defaultIV = new Uint8Array(17); + defaultIV[0] = 16; // default_constant_IV_size (16) + if (decryptdata.iv && decryptdata.iv.byteLength === 16) { + defaultIV.set(decryptdata.iv, 1); + } + if (!decryptdata.keyId) { + throw 'tenc: no key id found in decryptdata'; + } + return MP4.box(MP4.types.tenc, new Uint8Array([ + 1, + 0, + 0, + 0, + 0, + skipPattern, + 1, + 0, + ]), decryptdata.keyId, // default_KID, 16 bytes + defaultIV); + } + // only using this to write seig entries + static sbgp(track) { + if (!track.encrypted || track.samples.length === 0 || !track.samples[0].keyTagInfo) { + return new Uint8Array(); + } + // at this point we're assuming all samples in the track have the same key id + // this should hold true for the current ts/mp4 remux path + const sampleCount = track.samples.length; + return MP4.box(MP4.types.sbgp, new Uint8Array([ + 0, + 0, + 0, + 0, + ]), new Uint8Array(MP4.types.seig), // grouping_type + new Uint8Array([ + 0, + 0, + 0, + 1, + (sampleCount >> 24) & 255, + (sampleCount >> 16) & 255, + (sampleCount >> 8) & 255, + sampleCount & 255, + 0, + 1, + 0, + 1, + ])); + } + // only using this to write seig entries + static sgpd(track) { + if (!track.encrypted || track.samples.length === 0 || !track.samples[0].keyTagInfo) { + return new Uint8Array(); + } + const sDecryptdata = track.samples[0].keyTagInfo; + let skipPattern = 0; + if (track.type === 'video') { + skipPattern = 25; // 1 default_crypt_byte_block, 9 default_skip_byte_block + } + const sizeAndIv = new Uint8Array(17); + sizeAndIv[0] = 16; + if (sDecryptdata.iv) { + sizeAndIv.set(sDecryptdata.iv, 1); + } + if (!sDecryptdata.keyId) { + throw 'sgpd: no keyid in decryptdata'; + } + return MP4.box(MP4.types.sgpd, new Uint8Array([ + 1, + 0, + 0, + 0, + ]), new Uint8Array(MP4.types.seig), // grouping_type + new Uint8Array([ + 0, + 0, + 0, + 37, + 0, + 0, + 0, + 1, + ]), new Uint8Array([ + 0, + skipPattern, + 1, + 0, + ]), sDecryptdata.keyId, sizeAndIv); + } + /** + * @param {Uint8Array} systemId 16 bytes + * @param {array of Uint8Array} keyids The list of key ids described by this PSSH box + * @param {Uint8Array} An arbitrary buffer of data to be used in the data field of the PSSH box + * @returns {Uint8Array} Bytes representing a PSSH box + */ + static pssh(systemId, keyids, data) { + if (!MP4.types) { + MP4.init(); + } + if (!systemId) { + throw new TypeError('Bad system id'); + } + if (systemId.byteLength !== 16) { + throw new RangeError('Invalid system id'); + } + let version; + let kids; + if (keyids) { + version = 1; + kids = new Uint8Array(keyids.length * 16); + for (let ix = 0; ix < keyids.length; ix++) { + const k = keyids[ix]; // uint8array + if (k.byteLength !== 16) { + throw new RangeError('Invalid key'); + } + kids.set(k, ix * 16); + } + } + else { + version = 0; + kids = new Uint8Array(); + } + let kidCount; + if (version > 0) { + kidCount = new Uint8Array(4); + if (keyids.length > 0) { + new DataView(kidCount.buffer).setUint32(0, keyids.length, false); // Big endian + } + } + else { + kidCount = new Uint8Array(); + } + const dataSize = new Uint8Array(4); // Mandatory field + if (data && data.byteLength > 0) { + new DataView(dataSize.buffer).setUint32(0, data.byteLength, false); // Big endian + } + return MP4.box(MP4.types.pssh, new Uint8Array([ + version, + 0, + 0, + 0, + ]), systemId, // 16 bytes + kidCount, kids, dataSize, data || new Uint8Array()); + } + } + var MP4$1 = MP4; + + var VideoDynamicRangeType; + (function (VideoDynamicRangeType) { + // should be same as AirPlayVideoDynamicRangeFormat + VideoDynamicRangeType[VideoDynamicRangeType["SDR"] = 0] = "SDR"; + VideoDynamicRangeType[VideoDynamicRangeType["HDR"] = 1] = "HDR"; + VideoDynamicRangeType[VideoDynamicRangeType["HDR10"] = 2] = "HDR10"; + VideoDynamicRangeType[VideoDynamicRangeType["DolbyVision"] = 3] = "DolbyVision"; + VideoDynamicRangeType[VideoDynamicRangeType["HLG"] = 4] = "HLG"; + })(VideoDynamicRangeType || (VideoDynamicRangeType = {})); + var CompressionType; + (function (CompressionType) { + // should be same as AirPlayCompressionType + CompressionType[CompressionType["H264"] = 16] = "H264"; + CompressionType[CompressionType["HEVC"] = 64] = "HEVC"; + CompressionType[CompressionType["VP09"] = 65] = "VP09"; + })(CompressionType || (CompressionType = {})); + + // import { AudioSegmentCodecType } from '../types/tracks'; + /* + * 2018 Apple Inc. All rights reserved. + */ + const ac3Codecs = new Set(['ac-3', 'mp4a.a5', 'mp4a.A5']); + const ec3Codecs = new Set(['ec-3', 'mp4a.a6', 'mp4a.A6']); + const SampleDurationMap = { aac: 1024, mp3: 1024, ac3: 1536, ec3: 1536 }; + const MediaUtil = { + isAC3(codec) { + return Boolean(codec && ac3Codecs.has(codec)); + }, + isEC3(codec) { + return Boolean(codec && ec3Codecs.has(codec)); + }, + isDolbyAtmos(codec, channels) { + const parameters = channels.split('/'); + return Boolean(MediaUtil.isEC3(codec) && parameters.length > 1 && parameters[1].split(',').find((x) => x === 'JOC')); + }, + isAAC(codec) { + let match; + return Boolean(codec && (codec === 'aac' || ((match = codec.match(/^mp4a\.40\.(.*)/)) !== null && match[1] !== '34'))); + }, + isMP3(codec) { + let match; + return Boolean(codec && (codec === 'mp3' || ((match = codec.match(/^mp4a\.40\.(.*)/)) !== null && match[1] === '34'))); + }, + isAVC(codec) { + // basic match without validation + return Boolean(codec && codec.match(/^avc[13]\.(.*)/)); + }, + isXHEAAC: function (codec) { + return Boolean(codec === 'mp4a.40.42'); + }, + isALAC: function (codec) { + return Boolean(codec === 'alac'); + }, + isFLAC: function (codec) { + return Boolean(codec === 'fLaC'); + }, + isHEVC(codec) { + // basic match without validation + return Boolean(codec && codec.match(/^(hev|hvc)1\..*/)); + }, + isDolby(codec) { + return Boolean(codec && codec.match(/^dv(h1|he|a1|av)\..*/)); + }, + isVP09(codec) { + // basic match without validation + return Boolean(codec && codec.match(/^vp09\..*/)); + }, + isCompatibleCodecString(c1, c2) { + const codecs1 = c1.split(','); + const codecs2 = c2.split(','); + const videoCodec1 = codecs1.filter((codec) => MediaUtil.isVideoCodec(codec)); + const videoCodec2 = codecs2.filter((codec) => MediaUtil.isVideoCodec(codec)); + const audioCodec1 = codecs1.filter((codec) => MediaUtil.isAudioCodec(codec)); + const audioCodec2 = codecs2.filter((codec) => MediaUtil.isAudioCodec(codec)); + const videoOk = (videoCodec1.length === 0 && videoCodec2.length === 0) || (videoCodec1.length === videoCodec2.length && MediaUtil.isCompatibleVideoCodec(videoCodec1[0], videoCodec2[0])); + const audioOk = (audioCodec1.length === 0 && audioCodec2.length === 0) || (audioCodec1.length === audioCodec2.length && MediaUtil.isCompatibleAudioCodec(audioCodec1[0], audioCodec2[0])); + return videoOk && audioOk; + }, + isVideoCodec(codec) { + return MediaUtil.isAVC(codec) || MediaUtil.isDolby(codec) || MediaUtil.isHEVC(codec) || MediaUtil.isVP09(codec); + }, + isAudioCodec(codec) { + return MediaUtil.isAC3(codec) || MediaUtil.isEC3(codec) || MediaUtil.isAAC(codec) || MediaUtil.isMP3(codec); + }, + isCompatibleVideoCodec(c1, c2) { + return Boolean(c1 && + c2 && + (c1 === c2 || + (MediaUtil.isDolby(c1) && MediaUtil.isDolby(c2)) || + (MediaUtil.isHEVC(c1) && MediaUtil.isHEVC(c2)) || + (MediaUtil.isAVC(c1) && MediaUtil.isAVC(c2)) || + (MediaUtil.isVP09(c1) && MediaUtil.isVP09(c2)))); + }, + isCompatibleAudioCodec(c1, c2) { + return Boolean(c1 && + c2 && + (c1 === c2 || + (MediaUtil.isAAC(c1) && MediaUtil.isAAC(c2)) || + (MediaUtil.isAC3(c1) && MediaUtil.isAC3(c2)) || + (MediaUtil.isEC3(c1) && MediaUtil.isEC3(c2)) || + (MediaUtil.isMP3(c1) && MediaUtil.isMP3(c2)))); + }, + getSegmentCodec(audioCodec) { + let segmentCodec; + if (MediaUtil.isAAC(audioCodec)) { + segmentCodec = 'aac'; + } + else if (MediaUtil.isAC3(audioCodec)) { + segmentCodec = 'ac3'; + } + else if (MediaUtil.isEC3(audioCodec)) { + segmentCodec = 'ec3'; + } + else if (audioCodec === 'mp3') { + segmentCodec = 'mp3'; + } + else { + throw new Error(`invalid audio config, codec ${audioCodec}`); + } + return segmentCodec; + }, + getChannelCount(channels) { + if (!channels) { + return 0; + } + const parameters = channels.split('/'); + const channelCount = parseInt(parameters[0]); + if (!isFiniteNumber(channelCount)) { + return 0; + } + return channelCount; + }, + avc1toavcoti(codec) { + var _a, _b; + const avcdata = codec.split('.'); + let result; + if (avcdata.length > 2) { + result = avcdata.shift() + '.'; + result += parseInt((_a = avcdata.shift()) !== null && _a !== void 0 ? _a : '').toString(16); + result += ('000' + parseInt((_b = avcdata.shift()) !== null && _b !== void 0 ? _b : '').toString(16)).substr(-4); + } + else { + result = codec; + } + return result; + }, + getDynamicRangeType(videoRange, videoCodec) { + let type = VideoDynamicRangeType.SDR; // default to SDR + if (videoRange === 'PQ' && MediaUtil.isDolby(videoCodec)) { + type = VideoDynamicRangeType.DolbyVision; + } + else if (videoRange === 'PQ' && (MediaUtil.isHEVC(videoCodec) || MediaUtil.isVP09(videoCodec))) { + type = VideoDynamicRangeType.HDR10; + } + else if (videoRange === 'HLG' && (videoCodec.indexOf('hvc1') !== -1 || MediaUtil.isVP09(videoCodec))) { + type = VideoDynamicRangeType.HLG; + } + return type; + }, + getCompressionType(videoCodec) { + let compressionType = CompressionType.H264; + if (MediaUtil.isHEVC(videoCodec) || MediaUtil.isDolby(videoCodec)) { + compressionType = CompressionType.HEVC; + } + else if (MediaUtil.isVP09(videoCodec)) { + compressionType = CompressionType.VP09; + } + return compressionType; + }, + isHigherCodecByFamily(currentCodec, newCodec) { + if (!currentCodec) { + return true; + } + const splitCurrentCodec = currentCodec.split('.'); + const splitNewCodec = newCodec.split('.'); + if (splitCurrentCodec[0] !== splitNewCodec[0]) { + throw new Error(`mismatch in codec family current/new: ${splitCurrentCodec[0]}/${splitNewCodec[0]}`); + } + switch (splitCurrentCodec[0]) { + case 'avc1': + case 'avc3': + return splitNewCodec[1] > splitCurrentCodec[1]; + case 'vp09': + return newCodec > currentCodec; + case 'hvc1': + case 'hev1': + const currentTier = splitCurrentCodec[3].substring(0, 1) === 'H' ? 1 : 0; + const currentLevel = splitCurrentCodec[3].substring(1); + const newTier = splitNewCodec[3].substring(0, 1) === 'H' ? 1 : 0; + const newLevel = splitNewCodec[3].substring(1); + return splitNewCodec[1] > splitCurrentCodec[1] || splitNewCodec[2] > splitCurrentCodec[2] || newTier > currentTier || newLevel > currentLevel; + case 'dvh1': + return splitNewCodec[1] > splitCurrentCodec[1] || splitNewCodec[2] > splitCurrentCodec[2]; + } + }, + }; + + class SilentAudio { + static getTrack(observer, id, codec, channelCount, logger) { + let track; + const segmentCodec = MediaUtil.getSegmentCodec(codec); + switch (segmentCodec) { + case 'aac': + { + let config; + if (channelCount === 1) { + config = ADTS.getAudioConfig(observer, new Uint8Array([255, 241, 92, 64, 1, 127, 252]), 0, codec, logger); + } + else { + config = ADTS.getAudioConfig(observer, new Uint8Array([255, 241, 92, 128, 1, 191, 252]), 0, codec, logger); + } + if (config) { + const info = { id, timescale: config.samplerate, duration: 0, encrypted: false, keyTagInfo: undefined }; + track = { type: 'audio', info, config }; + } + } + break; + case 'ac3': + case 'ec3': + { + const config = Dolby.getAudioConfig(observer, new Uint8Array([11, 119, 69, 17, 128, 64, 47, 132]), 0, logger); + if (config) { + const info = { id, timescale: config.samplerate, duration: 0, encrypted: false, keyTagInfo: undefined }; + track = { type: 'audio', info, config }; + } + } + break; + } + return track; + } + static getSample(codec, channelCount) { + let sample; + switch (codec) { + case 'mp4a.40.2': + case 'mp4a.40.5': + if (channelCount === 1) { + sample = new Uint8Array([0, 208, 0, 7]); + } + else { + sample = new Uint8Array([33, 0, 3, 64, 104, 28]); + } + break; + case 'ac-3': + case 'ec-3': + sample = new Uint8Array([ + 11, + 119, + 69, + 17, + 128, + 64, + 47, + 132, + 41, + 3, + 253, + 214, + 124, + 253, + 243, + 215, + 233, + 95, + 185, + 123, + 78, + 20, + 40, + 106, + 97, + 190, + 74, + 253, + 43, + 218, + 208, + 140, + 191, + 176, + 144, + 120, + 214, + 181, + 44, + 124, + 129, + 251, + 91, + 109, + 187, + 109, + 198, + 225, + 43, + 172, + 116, + 140, + 176, + 123, + 38, + 144, + 211, + 247, + 225, + 64, + 29, + 53, + 175, + 96, + 16, + 57, + 121, + 87, + 78, + 203, + 81, + 37, + 7, + 72, + 228, + 132, + 37, + 169, + 38, + 231, + 97, + 229, + 247, + 194, + 208, + 8, + 12, + 83, + 74, + 139, + 137, + 17, + 22, + 26, + 221, + 203, + 107, + 113, + 94, + 93, + 75, + 33, + 208, + 247, + 146, + 105, + 39, + 143, + 6, + 36, + 1, + 227, + 108, + 70, + 11, + 180, + 152, + 218, + 182, + 218, + 209, + 59, + 85, + 104, + 201, + 70, + 37, + 82, + 219, + 68, + 55, + 225, + 144, + 99, + 149, + 0, + 119, + 26, + 14, + 69, + 164, + 241, + 204, + 222, + 81, + 177, + 142, + 80, + 20, + 100, + 97, + 143, + 101, + 221, + 140, + 113, + 31, + 208, + 124, + 25, + 64, + 29, + 49, + 77, + 140, + 30, + 155, + 74, + 214, + 204, + 138, + 229, + 109, + 172, + 95, + 130, + 70, + 230, + 134, + 88, + 59, + 179, + 212, + 155, + 232, + 0, + 0, + 0, + 0, + 0, + 173, + 234, + ]); + break; + } + return sample; + } + static getSegment(silentTrack, sequenceNumber, startDtsTimescale, segmentDuration) { + if (!silentTrack) { + return; + } + const { timescale } = silentTrack.info; + const { segmentCodec } = silentTrack.config; + const silentFrame = SilentAudio.getSample(silentTrack.config.codec, silentTrack.config.channelCount); + if (!silentFrame) { + return; + } + const samples = []; + const track = { + id: silentTrack.info.id, + sequenceNumber, + type: 'audio', + encrypted: false, + samples, + defaultPerSampleIVSize: 0, + }; + const sampleDuration = SampleDurationMap[segmentCodec]; + const nbSamples = Math.ceil((segmentDuration * timescale) / sampleDuration); + const baseTime = Math.round(nbSamples * sampleDuration + startDtsTimescale); + const endTs = { baseTime, timescale }; + let offset = 0; + const mdatSize = nbSamples * silentFrame.byteLength + 8; + const mdat = new Uint8Array(mdatSize); + mdat[0] = (mdatSize >> 24) & 255; + mdat[1] = (mdatSize >> 16) & 255; + mdat[2] = (mdatSize >> 8) & 255; + mdat[3] = mdatSize & 255; + if (!MP4$1.types) { + MP4$1.init(); + } + mdat.set(MP4$1.types.mdat, 4); + offset += 8; + for (let i = 0; i < nbSamples; i++) { + samples.push({ + duration: sampleDuration, + size: silentFrame.byteLength, + cts: 0, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: 1, + isNonSync: 0, + paddingValue: 0, + }, + }); + mdat.set(silentFrame, offset); + offset += silentFrame.byteLength; + } + const moof = MP4$1.moof(startDtsTimescale, track); + const silentFragData = new Uint8Array(moof.byteLength + mdat.byteLength); + silentFragData.set(moof); + silentFragData.set(mdat, moof.byteLength); + return { silentFragData, endTs }; + } + } + + // 10 seconds + const MAX_SILENT_FRAME_DURATION = 10000; + class EsRemuxer extends RemuxerBase { + constructor(observer, config, typeSupported, vendor, logger) { + super(observer, config, logger); + this.typeSupported = typeSupported; + this.isVideoContiguous = false; + this.logger = logger.child({ name: 'EsRemuxer' }); + const userAgent = navigator.userAgent; + this.isSafari = vendor && vendor.indexOf('Apple') > -1 && userAgent && !userAgent.match('CriOS'); + } + resetTimeStamp(defaultTimeStamp) { + this._initPTS = this._initDTS = defaultTimeStamp; + } + resetInitSegment() { + this.currentInitTrack = undefined; + this._silentAudioTrack = undefined; + } + /** + * @param timeOffset The position in the media element corresponding to this fragment when playback rate = 1 + * @param contiguous Whether this fragment is contiguous with previously appended fragment + * @param accurateTimeOffset Whether timeOffset is reliable + * @param keyTagInfo Information from EXT-X-KEY tag + * @param iframeMediaStart in iframe mode, the desired start DTS of the remuxed fragment + * @param iframeDuration in iframe mode, the desired duration of the remuxed fragment + */ + remuxEsTracks(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset, keyTagInfo, iframeMediaStart, iframeDuration) { + var _a; + if (!contiguous) { + this.isVideoContiguous = false; + } + // generate Init Segment if needed + let silentAudioSegment; + const timelineOffset = typeof iframeMediaStart === 'undefined' ? timeOffset : iframeMediaStart; + if (!this.currentInitTrack) { + if (audioTrack && audioTrack.config.codec) { + // when we start out in non-iframe mode we need to store the audio parameters + // we won't get this info from the iframe playlist since the demuxer will skip audio samples + this._audioTrackInfo = { id: audioTrack.info.id, codec: audioTrack.config.codec, channelCount: audioTrack.config.channelCount }; + } + if (videoTrack && iframeDuration && this._audioTrackInfo) { + const silentAudioBase = SilentAudio.getTrack(this.observer, this._audioTrackInfo.id, this._audioTrackInfo.codec, this._audioTrackInfo.channelCount, this.logger); + if (silentAudioBase) { + this._silentAudioTrack = Object.assign(Object.assign({}, silentAudioBase), { info: Object.assign(Object.assign({}, silentAudioBase.info), { inputTimescale: 90000 }), parsingData: { len: 0, sequenceNumber: 0, esSamples: [] } }); + // need to populate samples for generateIS + const segmentStartPTS = this._initPTS + Math.round(timelineOffset * this._silentAudioTrack.info.timescale); + silentAudioSegment = SilentAudio.getSegment(this._silentAudioTrack, videoTrack.parsingData.sequenceNumber, segmentStartPTS, iframeDuration); + videoTrack.parsingData.sequenceNumber++; + } + } + else { + this._silentAudioTrack = undefined; + } + this.updateInitPTSDTS(videoTrack, audioTrack, timeOffset); // Use real audioTrack info to update PTSDTS + this.generateIS(this._silentAudioTrack ? this._silentAudioTrack : audioTrack, videoTrack); + } + if (this.currentInitTrack) { + const remuxVideo = videoTrack && videoTrack.parsingData.esSamples.length; + const isVideoContiguous = this.isVideoContiguous; + let audioData, videoData; + // Purposefully remuxing audio before video, so that remuxVideo can use nextAudioPts, which is + // calculated in remuxAudio. + if (videoTrack && iframeDuration && this._silentAudioTrack && !silentAudioSegment) { + // samples could have been generated before init segment generation + const segmentStartPTS = this._initPTS + Math.round((timelineOffset + this.config.audioPrimingDelay) * this._silentAudioTrack.info.timescale); + silentAudioSegment = SilentAudio.getSegment(this._silentAudioTrack, videoTrack.parsingData.sequenceNumber, segmentStartPTS, iframeDuration); + } + if (audioTrack && audioTrack.parsingData.esSamples.length) { + // if initSegment was generated without video samples, regenerate it again + if (!isFiniteNumber(audioTrack.info.timescale)) { + this.logger.warn('regenerate InitSegment as audio detected'); + this.updateInitPTSDTS(videoTrack, audioTrack, timeOffset); // Use real audioTrack info to update PTSDTS + this.generateIS(audioTrack, videoTrack); + } + audioData = this.remuxAudio(audioTrack, timelineOffset, contiguous, accurateTimeOffset, keyTagInfo); + if (remuxVideo) { + let audioTrackLength; + if (audioData) { + audioTrackLength = convertTimestampToSeconds(audioData.endPTS) - convertTimestampToSeconds(audioData.startPTS); + } + // if initSegment was generated without video samples, regenerate it again + if (!isFiniteNumber(videoTrack.info.timescale)) { + this.logger.warn('regenerate InitSegment as video detected'); + this.updateInitPTSDTS(videoTrack, audioTrack, timeOffset); // Use real audioTrack info to update PTSDTS + this.generateIS(audioTrack, videoTrack); + } + videoData = this.remuxVideo(videoTrack, timelineOffset, isVideoContiguous, audioTrackLength, iframeDuration); + } + } + else { + if (remuxVideo) { + videoData = this.remuxVideo(videoTrack, timelineOffset, isVideoContiguous, undefined, iframeDuration); + } + if (videoData && audioTrack && audioTrack.config.codec) { + audioData = this.remuxEmptyAudio(audioTrack, timelineOffset, contiguous, accurateTimeOffset, videoData, keyTagInfo); + } + } + let parsingData; + if (silentAudioSegment) { + parsingData = { + data1: videoData.data1, + data2: silentAudioSegment.silentFragData, + startDTS: videoData.startDTS, + startPTS: videoData.startPTS, + endDTS: determineMaxTimestamp(videoData.endDTS, silentAudioSegment.endTs), + endPTS: determineMaxTimestamp(videoData.endPTS, silentAudioSegment.endTs), + type: 'audiovideo', + track: this.currentInitTrack, + }; + } + else if (videoData && audioData) { + parsingData = { + data1: videoData.data1, + data2: audioData.data1, + startDTS: determineMinTimestamp(videoData.startDTS, audioData.startDTS), + startPTS: determineMinTimestamp(videoData.startPTS, audioData.startPTS), + endDTS: determineMaxTimestamp(videoData.endDTS, audioData.endDTS), + endPTS: determineMaxTimestamp(videoData.endPTS, audioData.endPTS), + type: 'audiovideo', + track: this.currentInitTrack, + dropped: videoData.dropped, + framesWithoutIDR: videoData.framesWithoutIDR, + firstKeyframePts: videoData.firstKeyframePts, + }; + } + else if (videoData) { + parsingData = { + data1: videoData.data1, + startDTS: videoData.startDTS, + startPTS: videoData.startPTS, + endDTS: videoData.endDTS, + endPTS: videoData.endPTS, + type: 'video', + track: this.currentInitTrack, + dropped: videoData.dropped, + framesWithoutIDR: videoData.framesWithoutIDR, + firstKeyframePts: videoData.firstKeyframePts, + }; + } + else if (audioData) { + parsingData = { + data1: audioData.data1, + startDTS: audioData.startDTS, + startPTS: audioData.startPTS, + endDTS: audioData.endDTS, + endPTS: audioData.endPTS, + type: 'audio', + track: this.currentInitTrack, + }; + } + else { + this.logger.error('Missing video and audio data'); + } + if (textTrack && textTrack.captionSamples.length) { + this.remuxText(textTrack, parsingData); + } + if ((_a = id3Track === null || id3Track === void 0 ? void 0 : id3Track.id3Samples) === null || _a === void 0 ? void 0 : _a.length) { + this.remuxID3(id3Track, parsingData); + } + this.observer.trigger(DemuxerEvent.FRAG_PARSING_DATA, parsingData); + } + else { + this.logger.error('failed to generate IS'); + } + // notify end of parsing + this.observer.trigger(DemuxerEvent.FRAG_PARSED); + } + /** + * @param videoTrack + * @param audioTrack + * @param timeOffset Position in media element corresponding to the beginning of this segment + */ + updateInitPTSDTS(videoTrack, audioTrack, timeOffset) { + let initPTS = Infinity, initDTS = Infinity; + const videoSamples = videoTrack ? videoTrack.parsingData.esSamples : []; + const audioSamples = audioTrack ? audioTrack.parsingData.esSamples : []; + if (isFiniteNumber(this._initPTS)) { + return; + } + if (audioTrack && audioSamples.length) { + // remember first PTS of this demuxing context. for audio, PTS = DTS + initPTS = initDTS = audioSamples[0].pts - audioTrack.info.inputTimescale * timeOffset; + } + if (videoTrack && videoSamples.length) { + // let's use input time scale as MP4 video timescale + // we use input time scale straight away to avoid rounding issues on frame duration / cts computation + const inputTimeScale = videoTrack.info.inputTimescale; + videoTrack.info.timescale = inputTimeScale; + initPTS = Math.min(initPTS, getVideoStartPts(videoSamples) - inputTimeScale * timeOffset); + initDTS = Math.min(initDTS, videoSamples[0].dts - inputTimeScale * timeOffset); + this.observer.trigger(DemuxerEvent.INIT_PTS_FOUND, { initPTS: convertSecondsToTimestamp(initPTS, inputTimeScale) }); + } + if (isFiniteNumber(initPTS) && isFiniteNumber(initDTS)) { + this._initPTS = initPTS; + this._initDTS = initDTS; + } + else { + const payload = new FragParsingError(false, 'invalid initPTS or initDTS', ErrorResponses.InvalidInitTimestamp); + this.observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + } + } + generateIS(audioTrack, videoTrack) { + // const observer = this.observer; + const videoSamples = videoTrack ? videoTrack.parsingData.esSamples : []; + const typeSupported = this.typeSupported; + let container = 'audio/mp4'; + let track; + if (audioTrack && videoTrack && videoSamples.length) { + const inputTimeScale = videoTrack.info.inputTimescale; + videoTrack.info.timescale = inputTimeScale; + audioTrack.info.timescale = audioTrack.config.samplerate; + const initSegment = MP4.initSegment([videoTrack, audioTrack]); + track = { + type: 'audiovideo', + container: 'video/mp4', + codec: `${videoTrack.config.codec},${audioTrack.config.codec}`, + initSegment, + }; + } + else if (audioTrack) { + // let's use audio sampling rate as MP4 time scale. + // rationale is that there is a integer nb of audio frames per audio sample (1024 for AAC) + // using audio sampling rate here helps having an integer MP4 frame duration + // this avoids potential rounding issue and AV sync issue + audioTrack.info.timescale = audioTrack.config.samplerate; + this.logger.info(`audio sampling rate : ${audioTrack.config.samplerate}`); + switch (audioTrack.config.segmentCodec) { + case 'mp3': + if (typeSupported.mpeg) { + // Chrome and Safari + container = 'audio/mpeg'; + audioTrack.config.codec = ''; + } + else if (typeSupported.mp3) { + // Firefox + audioTrack.config.codec = 'mp3'; + } + break; + } + const initSegment = audioTrack.config.segmentCodec === 'mp3' && typeSupported.mpeg ? new Uint8Array() : MP4.initSegment([audioTrack]); + track = { + type: 'audio', + container: container, + codec: audioTrack.config.codec, + initSegment, + }; + } + else if (videoTrack && videoSamples.length) { + // let's use input time scale as MP4 video timescale + // we use input time scale straight away to avoid rounding issues on frame duration / cts computation + const inputTimeScale = videoTrack.info.inputTimescale; + videoTrack.info.timescale = inputTimeScale; + const initSegment = MP4.initSegment([videoTrack]); + track = { + type: 'video', + container: 'video/mp4', + codec: videoTrack.config.codec, + initSegment, + }; + } + if (track) { + this.currentInitTrack = track; + const data = { track }; + this.observer.trigger(DemuxerEvent.FRAG_PARSING_INIT_SEGMENT, data); + } + else { + const payload = new FragParsingError(false, 'no audio/video samples found', ErrorResponses.NoAVSamplesFound); + this.observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + } + } + remuxVideo(track, timeOffset, contiguous, audioTrackLength, iframeDuration) { + let offset = 8; + let mp4SampleDuration; + let mdat; + let firstPTS; + let firstDTS; + let lastPTS; + let lastDTS; + let dropped = track.parsingData.dropped; + const dropFrames = !contiguous && this.config.forceKeyFrameOnDiscontinuity; + const inputSamples = track.parsingData.esSamples; + const timeScale = track.info.inputTimescale; + const outputSamples = []; + const encrypted = track.info.encrypted; + // PTS is coded on 33bits, and can loop from -2^32 to 2^32 + // normalizePts will make PTS/DTS value monotonic, we use last known DTS value as reference value + let nextAvcDts; + // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1) + if (contiguous) { + // if parsed fragment is contiguous with last one, let's use last DTS value as reference + nextAvcDts = this.nextAvcDts; + } + else { + // if not contiguous, let's use sample + nextAvcDts = normalizePts(inputSamples[0].dts, inputSamples[0].pts); + } + inputSamples.forEach(function (sample) { + sample.pts = normalizePts(sample.pts, nextAvcDts); + sample.dts = normalizePts(sample.dts, nextAvcDts); + }); + // sort video samples by DTS then PTS then demux id order + inputSamples.sort(function (a, b) { + const deltadts = a.dts - b.dts; + const deltapts = a.pts - b.pts; + return deltadts ? deltadts : deltapts ? deltapts : a.id - b.id; + }); + let firstKeyframePts; + const firstKeyframeIndex = inputSamples.findIndex((sample) => sample.key); + if (inputSamples[firstKeyframeIndex]) { + firstKeyframePts = inputSamples[firstKeyframeIndex].pts; + } + if (dropFrames) { + if (firstKeyframeIndex > 0) { + this.logger.warn(`Dropped ${firstKeyframeIndex} out of ${inputSamples.length} video samples due to a missing keyframe`); + inputSamples.splice(0, firstKeyframeIndex); + dropped += firstKeyframeIndex; + } + else if (firstKeyframeIndex === -1) { + this.logger.warn(`No keyframe found out of ${inputSamples.length} video samples`); + dropped += inputSamples.length; + } + } + const firstSample = inputSamples[0]; + const lastSample = inputSamples[inputSamples.length - 1]; + // handle broken streams with PTS < DTS, tolerance up 200ms (18000 in 90kHz timescale) + const PTSDTSshift = inputSamples.reduce((prev, curr) => Math.max(Math.min(prev, curr.pts - curr.dts), -18000), 0); + if (PTSDTSshift < 0) { + this.logger.warn(`PTS < DTS detected in video samples, shifting DTS by ${Math.round(PTSDTSshift / 90)} ms to overcome this issue`); + for (let i = 0; i < inputSamples.length; i++) { + inputSamples[i].dts += PTSDTSshift; + } + } + const isSafari = this.isSafari; + // on Safari let's signal the same sample duration for all samples + // sample duration (as expected by trun MP4 boxes), should be the delta between sample DTS + // set this constant duration as being the avg delta between consecutive DTS. + mp4SampleDuration = Math.round((lastSample.dts - firstSample.dts) / (inputSamples.length - 1)); + // compute first DTS/PTS, normalize them against reference value + firstDTS = Math.max(firstSample.dts, 0); + firstPTS = Math.max(firstSample.pts, 0); + if (isFiniteNumber(iframeDuration)) { + firstDTS = timeOffset * timeScale; + firstPTS = timeOffset * timeScale; + } + // if fragment are contiguous, detect hole/overlapping between fragments + if (contiguous) { + // check timestamp continuity across consecutive fragments (this is to remove inter-fragment gap/hole) + const delta = firstDTS - nextAvcDts; + const foundHole = delta > mp4SampleDuration; + const foundOverlap = delta < -1; + if (foundHole || foundOverlap) { + if (foundHole) { + this.logger.warn(`AVC: ${delta}/90000 hole between fragments detected`); + } + else { + this.logger.warn(`AVC: ${delta}/90000 overlapping between fragments detected`); + } + } + } + let nbNalu = 0; + let naluLen = 0; + const nbSamples = inputSamples.length; + for (let i = 0; i < nbSamples; i++) { + // compute total/avc sample length and nb of NAL units + const sample = inputSamples[i], units = sample.units, nbUnits = units.length; + let sampleLen = 0; + for (let j = 0; j < nbUnits; j++) { + sampleLen += units[j].data.length; + } + naluLen += sampleLen; + nbNalu += nbUnits; + sample.length = sampleLen; + // normalize PTS/DTS + if (isSafari) { + // sample DTS is computed using a constant decoding offset (mp4SampleDuration) between samples + sample.dts = firstDTS + i * mp4SampleDuration; + } + else { + // ensure sample monotonic DTS + sample.dts = Math.max(sample.dts, firstDTS); + } + // ensure that computed value is greater or equal than sample DTS + sample.pts = Math.max(sample.pts, sample.dts); + } + // compute lastPTS/lastDTS + lastDTS = Math.max(lastSample.dts, 0); + lastPTS = Math.max(lastSample.pts, 0, lastDTS); + if (isFiniteNumber(iframeDuration)) { + lastDTS = timeOffset * timeScale; + lastPTS = timeOffset * timeScale; + } + /* concatenate the video data and construct the mdat in place + (need 8 more bytes to fill length and mpdat type) */ + const mdatSize = naluLen + 4 * nbNalu + 8; + try { + mdat = new Uint8Array(mdatSize); + } + catch (err) { + const payload = new RemuxAllocError(false, `fail allocating video mdat ${mdatSize}`, ErrorResponses.FailedToAllocateVideoMdat, mdatSize); + this.observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return; + } + const view = new DataView(mdat.buffer); + view.setUint32(0, mdatSize); + mdat.set(MP4.types.mdat, 4); + for (let i = 0; i < nbSamples; i++) { + const avcSample = inputSamples[i], avcSampleUnits = avcSample.units; + let mp4SampleLength = 0, compositionTimeOffset; + // convert NALU bitstream to MP4 format (prepend NALU with size field) + const subsamples = []; + let bytesClear = 0; + for (let j = 0, nbUnits = avcSampleUnits.length; j < nbUnits; j++) { + const unit = avcSampleUnits[j], unitData = unit.data, unitDataLen = unit.data.byteLength; + view.setUint32(offset, unitDataLen); + offset += 4; + mdat.set(unitData, offset); + offset += unitDataLen; + mp4SampleLength += 4 + unitDataLen; + if (encrypted) { + if (unitDataLen <= 48 || (unit.type !== 1 && unit.type !== 5)) { + bytesClear += 4 + unitDataLen; + } + else { + let encryptedDataLen = unitDataLen - 32; + if (encryptedDataLen % 16 === 0) { + encryptedDataLen -= 16; // different than fMP4 encryption - TS always requires at least 1 clear byte at the end + } + subsamples.push([bytesClear + 36, encryptedDataLen]); + bytesClear = unitDataLen - 32 - encryptedDataLen; + } + } + } + if (bytesClear > 0) { + subsamples.push([bytesClear, 0]); + } + if (!isSafari) { + // expected sample duration is the Decoding Timestamp diff of consecutive samples + if (i < nbSamples - 1) { + mp4SampleDuration = inputSamples[i + 1].dts - avcSample.dts; + } + else { + const config = this.config, lastFrameDuration = avcSample.dts - inputSamples[i > 0 ? i - 1 : i].dts; + if (config.stretchShortVideoTrack) { + // In some cases, a segment's audio track duration may exceed the video track duration. + // Since we've already remuxed audio, and we know how long the audio track is, we look to + // see if the delta to the next segment is longer than the minimum of maxBufferHole and + // maxSeekHole. If so, playback would potentially get stuck, so we artificially inflate + // the duration of the last frame to minimize any potential gap between segments. + const maxBufferHole = config.maxBufferHole, maxSeekHole = config.maxSeekHole, gapTolerance = Math.floor(Math.min(maxBufferHole, maxSeekHole) * timeScale), deltaToFrameEnd = (audioTrackLength ? firstPTS + audioTrackLength * timeScale : this.nextAudioPts) - avcSample.pts; + if (deltaToFrameEnd > gapTolerance) { + // We subtract lastFrameDuration from deltaToFrameEnd to try to prevent any video + // frame overlap. maxBufferHole/maxSeekHole should be >> lastFrameDuration anyway. + mp4SampleDuration = deltaToFrameEnd - lastFrameDuration; + if (mp4SampleDuration < 0) { + mp4SampleDuration = lastFrameDuration; + } + this.logger.info(`It is approximately ${deltaToFrameEnd / 90} ms to the next segment; using duration ${mp4SampleDuration / 90} ms for the last video frame.`); + } + else { + mp4SampleDuration = lastFrameDuration; + } + } + else { + mp4SampleDuration = lastFrameDuration; + } + } + compositionTimeOffset = Math.round(avcSample.pts - avcSample.dts); + } + else { + compositionTimeOffset = Math.max(0, mp4SampleDuration * Math.round((avcSample.pts - avcSample.dts) / mp4SampleDuration)); + } + if (isFiniteNumber(iframeDuration)) { + compositionTimeOffset = 0; + mp4SampleDuration = iframeDuration * timeScale; + } + outputSamples.push({ + size: mp4SampleLength, + // constant duration + duration: mp4SampleDuration, + cts: compositionTimeOffset, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: avcSample.key ? 2 : 1, + isNonSync: avcSample.key ? 0 : 1, + paddingValue: 0, + }, + keyTagInfo: avcSample.keyTagInfo, + subsamples: subsamples, + }); + } + // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale) + this.nextAvcDts = lastDTS + mp4SampleDuration; + this.isVideoContiguous = true; + if (outputSamples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { + const flags = outputSamples[0].flags; + // chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue + // https://code.google.com/p/chromium/issues/detail?id=229412 + flags.dependsOn = 2; + flags.isNonSync = 0; + } + const moofSegment = { + sequenceNumber: track.parsingData.sequenceNumber++, + id: track.info.id, + type: track.type, + encrypted: track.info.encrypted, + samples: outputSamples, + defaultPerSampleIVSize: 0, + }; + const moof = MP4.moof(firstDTS + this.config.audioPrimingDelay * timeScale, moofSegment); + track.parsingData.esSamples = []; + const fullSegment = new Uint8Array(moof.byteLength + mdat.byteLength); + fullSegment.set(moof); + fullSegment.set(mdat, moof.byteLength); + const type = 'video'; + const data = { + data1: fullSegment, + startPTS: convertSecondsToTimestamp(firstPTS / timeScale, timeScale), + endPTS: convertSecondsToTimestamp((lastPTS + mp4SampleDuration) / timeScale, timeScale), + startDTS: convertSecondsToTimestamp(firstDTS / timeScale, timeScale), + endDTS: convertSecondsToTimestamp(this.nextAvcDts / timeScale, timeScale), + type, + dropped, + framesWithoutIDR: firstKeyframeIndex, + firstKeyframePts: convertSecondsToTimestamp(firstKeyframePts / timeScale, timeScale), + }; + return data; + } + remuxAudio(track, timeOffset, contiguous, accurateTimeOffset, keyTagInfo) { + const inputTimeScale = track.info.inputTimescale, mp4timeScale = track.info.timescale, scaleFactor = inputTimeScale / mp4timeScale, mp4SampleDuration = track.config.segmentCodec === 'aac' ? 1024 : track.config.segmentCodec === 'mp3' ? 1152 : 1536, inputSampleDuration = mp4SampleDuration * scaleFactor, rawMPEG = track.config.segmentCodec === 'mp3' && this.typeSupported.mpeg, outputSamples = [], encrypted = track.info.encrypted, timeOffsetMpegTS = this._initPTS + timeOffset * inputTimeScale; + let view, offset = rawMPEG ? 0 : 8, audioSample, mp4Sample, unit, mdat, moof, firstPTS, firstDTS, lastDTS, pts, dts, fillFrame, newStamp, nextAudioPts; + const bu = new BitstreamUtils(); + const inputSamples = track.parsingData.esSamples; + // for audio samples, also consider consecutive fragments as being contiguous (even if a level switch occurs), + // for sake of clarity: + // consecutive fragments are frags with + // - less than 100ms gaps between new time offset (if accurate) and next expected PTS OR + // - less than 20 audio frames distance + // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1) + // this helps ensuring audio continuity + // and this also avoids audio glitches/cut when switching quality, or reporting wrong duration on first audio frame + nextAudioPts = this.nextAudioPts; + contiguous = + contiguous || + (inputSamples.length && + nextAudioPts && + ((accurateTimeOffset && Math.abs(timeOffsetMpegTS - nextAudioPts) < 9000) || Math.abs(normalizePts(inputSamples[0].pts - nextAudioPts, timeOffsetMpegTS)) < 20 * inputSampleDuration)); + if (!contiguous) { + // if fragments are not contiguous, let's use sample + nextAudioPts = normalizePts(inputSamples[0].pts, this._initPTS); + } + // compute normalized PTS + inputSamples.forEach(function (sample) { + sample.pts = sample.dts = normalizePts(sample.pts, nextAudioPts); + }); + // If the audio track is missing samples, the frames seem to get "left-shifted" within the + // resulting mp4 segment, causing sync issues and leaving gaps at the end of the audio segment. + // In an effort to prevent this from happening, we inject frames here where there are gaps. + // When possible, we inject a silent frame; when that's not possible, we duplicate the last + // frame. + // only inject/drop audio frames in case time offset is accurate + if (accurateTimeOffset && track.config.segmentCodec === 'aac') { + for (let i = 0, nextPts = nextAudioPts; i < inputSamples.length;) { + // First, let's see how far off this frame is from where we expect it to be + const sample = inputSamples[i]; + pts = sample.pts; + const delta = pts - nextPts; + const duration = Math.abs((1000 * delta) / inputTimeScale); + // If we're overlapping by more than a duration, drop this sample + if (delta <= -inputSampleDuration) { + this.logger.warn(`Dropping 1 audio frame @ ${(nextPts / inputTimeScale).toFixed(3)}s due to ${duration} ms overlap.`); + inputSamples.splice(i, 1); + track.parsingData.len -= sample.unit.length; + // Don't touch nextPtsNorm or i + // Insert missing frames if: + // 1: We're more than one frame away + // 2: Not more than MAX_SILENT_FRAME_DURATION away + // 3: currentTime (aka nextPtsNorm) is not 0 + } + else if (delta >= inputSampleDuration && duration < MAX_SILENT_FRAME_DURATION && nextPts) { + const missing = Math.round(delta / inputSampleDuration); + this.logger.warn(`Injecting ${missing} audio frame @ ${(nextPts / inputTimeScale).toFixed(3)}s due to ${Math.round((1000 * delta) / inputTimeScale)} ms gap.`); + for (let j = 0; j < missing; j++) { + newStamp = Math.max(nextPts, 0); + fillFrame = getSilentFrame(track.config.codec, track.config.channelCount); + if (!fillFrame) { + this.logger.warn('Unable to get silent frame for given audio codec; duplicating last frame instead.'); + fillFrame = sample.unit.subarray(0); + } + inputSamples.splice(i, 0, { unit: fillFrame, pts: newStamp, dts: newStamp, keyTagInfo: keyTagInfo }); + track.parsingData.len += fillFrame.length; + nextPts += inputSampleDuration; + i += 1; + } + // Adjust sample to next expected pts + sample.pts = sample.dts = nextPts; + nextPts += inputSampleDuration; + i += 1; + // Otherwise, just adjust pts + } + else { + nextPts += inputSampleDuration; + if (i === 0) { + sample.pts = sample.dts = nextAudioPts; + } + else { + sample.pts = sample.dts = inputSamples[i - 1].pts + inputSampleDuration; + } + i += 1; + } + } + } + for (let j = 0, nbSamples = inputSamples.length; j < nbSamples; j++) { + audioSample = inputSamples[j]; + unit = audioSample.unit; + pts = audioSample.pts; + dts = audioSample.dts; + // if not first sample + if (lastDTS !== undefined) { + mp4Sample.duration = Math.round((dts - lastDTS) / scaleFactor); + } + else { + const delta = Math.round((1000 * (pts - nextAudioPts)) / inputTimeScale); + let numMissingFrames = 0; + // if fragment are contiguous, detect hole/overlapping between fragments + // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1) + if (contiguous && track.config.segmentCodec === 'aac') { + // log delta + if (delta) { + if (delta > 0 && delta < MAX_SILENT_FRAME_DURATION) { + numMissingFrames = Math.round((pts - nextAudioPts) / inputSampleDuration); + this.logger.info(`${delta} ms hole between AAC samples detected,filling it`); + if (numMissingFrames > 0) { + fillFrame = getSilentFrame(track.config.codec, track.config.channelCount); + if (!fillFrame) { + fillFrame = unit.subarray(0); + } + track.parsingData.len += numMissingFrames * fillFrame.length; + } + // if we have frame overlap, overlapping for more than half a frame duraion + } + else if (delta < -12) { + // drop overlapping audio frames... browser will deal with it + this.logger.info(`drop overlapping AAC sample, expected/parsed/delta:${(nextAudioPts / inputTimeScale).toFixed(3)}s/${(pts / inputTimeScale).toFixed(3)}s/${-delta}ms`); + track.parsingData.len -= unit.byteLength; + continue; + } + // set PTS/DTS to expected PTS/DTS + pts = dts = nextAudioPts; + } + } + // remember first PTS of our audioSamples, ensure value is positive + firstPTS = Math.max(0, pts); + firstDTS = Math.max(0, dts); + if (track.parsingData.len > 0) { + /* concatenate the audio data and construct the mdat in place + (need 8 more bytes to fill length and mdat type) */ + const mdatSize = rawMPEG ? track.parsingData.len : track.parsingData.len + 8; + try { + mdat = new Uint8Array(mdatSize); + } + catch (err) { + const payload = new RemuxAllocError(false, `fail allocating audio mdat ${mdatSize}`, ErrorResponses.FailedToAllocateAudioMdat, mdatSize); + this.observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return; + } + if (!rawMPEG) { + view = new DataView(mdat.buffer); + view.setUint32(0, mdatSize); + mdat.set(MP4.types.mdat, 4); + } + } + else { + // no audio samples + return; + } + for (let i = 0; i < numMissingFrames; i++) { + newStamp = pts - (numMissingFrames - i) * inputSampleDuration; + fillFrame = getSilentFrame(track.config.codec, track.config.channelCount); + if (!fillFrame) { + this.logger.warn('Unable to get silent frame for given audio codec; duplicating this frame instead.'); + fillFrame = unit.subarray(0); + } + mdat.set(fillFrame, offset); + offset += fillFrame.byteLength; + mp4Sample = { + size: fillFrame.byteLength, + cts: 0, + duration: 1024, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: 1, + paddingValue: 0, + isNonSync: 0, + }, + keyTagInfo: audioSample.keyTagInfo, + subsamples: encrypted ? [[fillFrame.byteLength, 0]] : [], + }; + outputSamples.push(mp4Sample); + } + } + mdat.set(unit, offset); + const unitLen = unit.byteLength; + offset += unitLen; + const subsamples = []; + if (encrypted) { + if (track.config.segmentCodec === 'ec3') { + let bytesSeen = 0; + // for ec3, a protected block is a single sync frame; the sample may have one or more sync frames + while (bytesSeen < unit.byteLength) { + const frameLength = (bu.bsReadAndUpdate(unit, { byteOffset: bytesSeen + 2, usedBits: 5 }, 11) + 1) * 2; + bytesSeen += frameLength; + const clearBytes = Math.min(frameLength, 16); // 16 bytes in the clear + subsamples.push([clearBytes, frameLength - clearBytes]); + } + } + else { + const clearBytes = Math.min(unitLen, 16); // both aac and ac3 have 16 bytes in the clear + subsamples.push([clearBytes, unitLen - clearBytes]); + } + } + mp4Sample = { + size: unitLen, + cts: 0, + duration: 0, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: 1, + paddingValue: 0, + isNonSync: 0, + }, + keyTagInfo: audioSample.keyTagInfo, + subsamples: subsamples, + }; + outputSamples.push(mp4Sample); + lastDTS = dts; + } + let lastSampleDuration = 0; + const nbSamples = outputSamples.length; + // set last sample duration as being identical to previous sample + if (nbSamples >= 2) { + lastSampleDuration = outputSamples[nbSamples - 2].duration; + mp4Sample.duration = lastSampleDuration; + } + if (nbSamples) { + // next audio sample PTS should be equal to last sample PTS + duration + this.nextAudioPts = pts + scaleFactor * lastSampleDuration; + track.parsingData.len = 0; + if (rawMPEG) { + moof = new Uint8Array(); + } + else { + const moofSegment = { + sequenceNumber: track.parsingData.sequenceNumber++, + id: track.info.id, + type: track.type, + encrypted: track.info.encrypted, + samples: outputSamples, + defaultPerSampleIVSize: 0, + }; + moof = MP4.moof((firstDTS + this.config.audioPrimingDelay * inputTimeScale) / scaleFactor, moofSegment); + } + const fullSegment = new Uint8Array(moof.byteLength + mdat.byteLength); + fullSegment.set(moof); + fullSegment.set(mdat, moof.byteLength); + track.parsingData.esSamples = []; + const audioData = { + data1: fullSegment, + startPTS: convertSecondsToTimestamp(firstPTS / inputTimeScale, inputTimeScale), + endPTS: convertSecondsToTimestamp(this.nextAudioPts / inputTimeScale, inputTimeScale), + startDTS: convertSecondsToTimestamp(firstDTS / inputTimeScale, inputTimeScale), + endDTS: convertSecondsToTimestamp((dts + scaleFactor * lastSampleDuration) / inputTimeScale, inputTimeScale), + type: 'audio', + }; + return audioData; + } + return null; + } + remuxEmptyAudio(track, timeOffset, contiguous, accurateTimeOffset, videoData, keyTagInfo) { + const inputTimeScale = track.info.inputTimescale, mp4timeScale = track.config.samplerate ? track.config.samplerate : inputTimeScale, scaleFactor = inputTimeScale / mp4timeScale, nextAudioPts = this.nextAudioPts, + // sync with video's timestamp + startDTS = (nextAudioPts !== undefined ? nextAudioPts : convertTimestampToSeconds(videoData.startDTS) * inputTimeScale) + this._initDTS, endDTS = convertTimestampToSeconds(videoData.endDTS) * inputTimeScale + this._initDTS, + // one sample's duration value + sampleDuration = 1024, frameDuration = scaleFactor * sampleDuration, + // samples count of this segment's duration + nbSamples = Math.ceil((endDTS - startDTS) / frameDuration), + // silent frame + silentFrame = getSilentFrame(track.config.codec, track.config.channelCount); + this.logger.warn('remux empty Audio'); + // Can't remux if we can't generate a silent frame... + if (!silentFrame) { + this.logger.error('Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec!'); + return null; + } + const samples = []; + for (let i = 0; i < nbSamples; i++) { + const stamp = startDTS + i * frameDuration; + samples.push({ unit: silentFrame, pts: stamp, dts: stamp, keyTagInfo: keyTagInfo }); + track.parsingData.len += silentFrame.length; + } + track.parsingData.esSamples = samples; + return this.remuxAudio(track, timeOffset, contiguous, accurateTimeOffset, keyTagInfo); + } + remuxID3(track, parsingData) { + const length = track.id3Samples.length; + let sample; + const inputTimeScale = track.inputTimescale; + // consume samples + if (length) { + for (let index = 0; index < length; index++) { + sample = track.id3Samples[index]; + // setting id3 pts, dts to relative time + sample.pts = sample.pts / inputTimeScale; + sample.dts = sample.dts / inputTimeScale; + } + parsingData.id3Samples = track.id3Samples; + } + track.id3Samples = []; + } + remuxText(track, parsingData) { + track.captionSamples.sort(function (a, b) { + return a.pts - b.pts; + }); + const length = track.captionSamples.length; + let sample; + const inputTimeScale = track.inputTimescale; + // consume samples + if (length) { + for (let index = 0; index < length; index++) { + sample = track.captionSamples[index]; + // setting text pts, dts to relative time + sample.pts = sample.pts / inputTimeScale; + } + if (!parsingData.captionData) { + parsingData.captionData = {}; + } + parsingData.captionData.ts = track.captionSamples; + } + track.captionSamples = []; + } + } + function normalizePts(value, reference) { + let offset; + if (reference === undefined) { + return value; + } + if (reference < value) { + // - 2^33 + offset = -8589934592; + } + else { + // + 2^33 + offset = 8589934592; + } + /* PTS is 33bit (from 0 to 2^33 -1) + if diff between value and reference is bigger than half of the amplitude (2^32) then it means that + PTS looping occured. fill the gap */ + while (Math.abs(value - reference) > 4294967296) { + value += offset; + } + return value; + } + function getVideoStartPts(videoSamples) { + const startPTS = videoSamples.reduce((minPTS, sample) => { + const delta = sample.pts - minPTS; + if (delta < -4294967296) { + // 2^32, see normalizePts for reasoning, but we're hitting a rollover here, and we don't want that to impact the timeOffset calculation + return normalizePts(minPTS, sample.pts); + } + else if (delta > 0) { + return minPTS; + } + else { + return sample.pts; + } + }, videoSamples[0].pts); + return startPTS; + } + + var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; + + function tryStringify (o) { + try { return JSON.stringify(o) } catch(e) { return '"[Circular]"' } + } + + var quickFormatUnescaped = format$1; + + function format$1(f, args, opts) { + var ss = (opts && opts.stringify) || tryStringify; + var offset = 1; + if (typeof f === 'object' && f !== null) { + var len = args.length + offset; + if (len === 1) return f + var objects = new Array(len); + objects[0] = ss(f); + for (var index = 1; index < len; index++) { + objects[index] = ss(args[index]); + } + return objects.join(' ') } - class i { - constructor(e, t) { - this.rpc = e, this.logger = t, this.decrypt = (r, n, s, a, o) => t => { - const i = l.crypto; - if (null != o && o.useJSCrypto || null == i || !i.subtle) { - const s = new d; - var e; - s.expandKey(r); - const i = s.decrypt(a, 0, n); - e = o.plainTextLength ? i.slice(0, o.plainTextLength) : function(e) { - var t = new Uint8Array(e), - i = t[e.byteLength - 1], - r = e.byteLength - 1; - let n = 0; - if (1 <= i && i <= 16) - for (let e = r; e > r - i && t[e] === i; e--) n++; - return e = n === i ? e.slice(0, r - i + 1) : e - }(i), t(e, void 0, [e]) - } else i.subtle.importKey("raw", r, s, !1, ["decrypt"]).then(e => i.subtle.decrypt({ - name: s, - iv: n - }, e, a)).then(e => { - t(e, void 0, [e]) - }).catch(e => t(void 0, e)) - }, e.register("decrypt", this.decrypt) - } - }(vr = P = P || {}).MEDIA_ATTACHING = "hlsMediaAttaching", vr.MEDIA_ATTACHED = "hlsMediaAttached", vr.MEDIA_DETACHING = "hlsMediaDetaching", vr.MEDIA_DETACHED = "hlsMediaDetached", vr.BUFFER_CREATED = "hlsBufferCreated", vr.BUFFER_APPENDING = "hlsBufferAppending", vr.BUFFER_APPENDED = "hlsBufferAppended", vr.BUFFER_FLUSHED = "hlsBufferFlushed", vr.MANIFEST_LOADING = "hlsManifestLoading", vr.MANIFEST_LOADED = "hlsManifestLoaded", vr.MANIFEST_PARSED = "hlsManifestParsed", vr.LEVEL_SWITCHING = "hlsLevelSwitching", vr.LEVEL_SWITCHED = "hlsLevelSwitched", vr.LEVEL_LOADING = "hlsLevelLoading", vr.LEVEL_LOADED = "hlsLevelLoaded", vr.LEVEL_UPDATED = "hlsLevelUpdated", vr.LEVELS_CHANGED = "hlsLevelsChanged", vr.AUDIO_TRACKS_UPDATED = "hlsAudioTracksUpdated", vr.AUDIO_TRACK_SWITCH = "hlsAudioTrackSwitch", vr.AUDIO_TRACK_SWITCHED = "hlsAudioTrackSwitched", vr.AUDIO_TRACK_LOADED = "hlsAudioTrackLoaded", vr.SUBTITLE_TRACKS_UPDATED = "hlsSubtitleTracksUpdated", vr.SUBTITLE_TRACKS_CREATED = "hlsSubtitleTracksCreated", vr.SUBTITLE_TRACK_SWITCH = "hlsSubtitleTrackSwitch", vr.INLINE_STYLES_PARSED = "hlsInlineStylesParsed", vr.SESSION_DATA_COMPLETE = "hlsSessionDataComplete", vr.FRAG_LOADING = "hlsFragLoading", vr.FRAG_LOADED = "hlsFragLoaded", vr.FRAG_BUFFERED = "hlsFragBuffered", vr.FRAG_CHANGED = "hlsFragChanged", vr.INTERNAL_ERROR = "hlsInternalError", vr.ERROR = "hlsError", vr.DESTROYING = "hlsDestroying", vr.KEY_REQUEST_STARTED = "hlsKeyRequestStarted", vr.LICENSE_CHALLENGE_CREATED = "hlsLicenseChallengeCreated", vr.LICENSE_RELEASED = "hlsLicenseReleased", vr.KEY_LOADED = "hlsKeyLoaded", vr.UNRESOLVED_URI_LOADING = "hlsUnresolvedUriLoading", vr.DESIRED_RATE_CHANGED = "hlsDesiredRateChanged", vr.PLAYER_STATE_CHANGE = "hlsPlayerStateChange", vr.SEEKING = "hlsSeeking", vr.SEEKED = "hlsSeeked", vr.STALLED = "hlsStalled", vr.RESUME_FROM_STALL = "hlsResumeFromStall", vr.READY_FOR_NEXT_ITEM = "hlsReadyForNextItem", vr.ITEM_TRANSITIONED = "hlsItemTransitioned", vr.ITEM_EVICTED = "hlsItemEvicted", vr.DATERANGE_UPDATED = "hlsDaterangeUpdated"; - var v, x = P; - (dl = v = v || {}).FRAG_PARSING_INIT_SEGMENT = "hlsFragParsingInitSegment", dl.FRAG_PARSING_DATA = "hlsFragParsingData", dl.FRAG_PARSED = "hlsFragParsed", dl.INIT_PTS_FOUND = "hlsInitPtsFound"; - class p extends Error { - constructor(e, t, i, r, n) { - super(r), this.type = e, this.details = t, this.fatal = i, this.reason = r, this.response = n, this.handled = !1 - } + if (typeof f !== 'string') { + return f } - class R extends p { - constructor(e, t, i, r, n) { - super(e, t, i, r, n), this.response = n + var argLen = args.length; + if (argLen === 0) return f + var str = ''; + var a = 1 - offset; + var lastPos = -1; + var flen = (f && f.length) || 0; + for (var i = 0; i < flen;) { + if (f.charCodeAt(i) === 37 && i + 1 < flen) { + lastPos = lastPos > -1 ? lastPos : 0; + switch (f.charCodeAt(i + 1)) { + case 100: // 'd' + if (a >= argLen) + break + if (lastPos < i) + str += f.slice(lastPos, i); + if (args[a] == null) break + str += Number(args[a]); + lastPos = i = i + 2; + break + case 79: // 'O' + case 111: // 'o' + case 106: // 'j' + if (a >= argLen) + break + if (lastPos < i) + str += f.slice(lastPos, i); + if (args[a] === undefined) break + var type = typeof args[a]; + if (type === 'string') { + str += '\'' + args[a] + '\''; + lastPos = i + 2; + i++; + break + } + if (type === 'function') { + str += args[a].name || ''; + lastPos = i + 2; + i++; + break + } + str += ss(args[a]); + lastPos = i + 2; + i++; + break + case 115: // 's' + if (a >= argLen) + break + if (lastPos < i) + str += f.slice(lastPos, i); + str += String(args[a]); + lastPos = i + 2; + i++; + break + case 37: // '%' + if (lastPos < i) + str += f.slice(lastPos, i); + str += '%'; + lastPos = i + 2; + i++; + break } + ++a; + } + ++i; } - const $ = { - PlaylistNotReceived: { - code: -12884, - text: "Playlist not received" - }, - CryptResponseReceivedSlowly: { - code: -16833, - text: "Crypt key received slowly" - }, - LivePlaylistUpdateError: { - code: -12888, - text: "Live playlist not updated" - }, - NoResponseFromMediaRequest: { - code: -12889, - text: "No response for fragment" - }, - IncompatibleAsset: { - code: -12927, - text: "IncompatibleAsset" - }, - CorruptStream: { - code: -16041, - text: "Corrupt fragment" - }, - InternalError: { - code: -12645, - text: "InternalException" - }, - CantSwitchInTime: { - code: -12644, - text: "CantSwitchInTime" - }, - VideoDecoderBadDataErr: { - code: -12909, - text: "Buffer error" - }, - InsufficientDataAvailable: { - code: -12928, - text: "Incomplete data" - }, - AllocationFailed: { - code: -12862, - text: "AllocationFailed" - }, - PlaylistErrorMissingEXTM3U: { - code: -12269, - text: "Response doesnt have #EXTM3U tag" - }, - PlaylistErrorInvalidEntry: { - code: -12264, - text: "Invalid entry" - }, - PlaylistErrorBadTargetDuration: { - code: -12271, - text: "Invalid targetduration" - }, - NoValidAlternates: { - code: -12925, - text: "No valid alternates" - }, - FormatError: { - code: -12642, - text: "Incorrect playlist format" - }, - UnsupportedKeySystemError: { - code: -6e4, - text: "Unsupported Key System" - }, - EmptyLoadSourceError: { - code: -60001, - text: "Empty loadSource url" - }, - UndefinedItemIdError: { - code: -60002, - text: "Undefined itemId" - }, - ManifestParseError: { - code: -60003, - text: "Manifest parse error" - }, - DemuxWorkerError: { - code: -60004, - text: "Demux worker error" - }, - DecryptWorkerError: { - code: -60005, - text: "Decrypt worker error" - }, - OutOfRangeSeekError: { - code: -60006, - text: "Seeked out of playable range" - }, - ExceptionInKeyLoadError: { - code: -60007, - text: "Exception in Key load" - }, - FragmentAbortError: { - code: -60008, - text: "Fragment abort error" - }, - ManifestTimeoutError: { - code: -60009, - text: "Manifest Timeout Error" - }, - PlaylistTimeoutError: { - code: -60010, - text: "Playlist Timeout Error" - }, - FragmentTimeoutError: { - code: -60011, - text: "Fragment Timeout Error" - }, - IncompleteSessionData: { - code: -60012, - text: "Session data not complete after loading all items" - }, - SessionDataLoadTimeout: { - code: -60013, - text: "Session data load timeout" - }, - FailedDemuxerSanityCheck: { - code: -60014, - text: "Failed demuxer sanity check" - }, - InvalidADTSSamplingIndex: { - code: -60015, - text: "Invalid ADTS sampling index" - }, - DemuxerNotFound: { - code: -60016, - text: "No demux matching with content found" - }, - InvalidAC3Magic: { - code: -60029, - text: "Invalid ac-3 magic" - }, - InvalidInitTimestamp: { - code: -60017, - text: "Invalid initPTS or initDTS" - }, - NoAVSamplesFound: { - code: -60018, - text: "no audio/video samples found" - }, - NoTSSyncByteFound: { - code: -60019, - text: "TS packet did not start with 0x47" - }, - PESDidNotStartWithADTS: { - code: -60020, - text: "AAC PES did not start with ADTS header" - }, - NoADTSHeaderInPES: { - code: -60021, - text: "No ADTS header found in AAC PES" - }, - InvalidDolbyAudioMagic: { - code: -60022, - text: "Invalid dolby audio magic" - }, - FailedToAllocateVideoMdat: { - code: -60023, - text: "Fail allocating video mdat" - }, - FailedToAllocateAudioMdat: { - code: -60024, - text: "Fail allocating audio mdat" - }, - InsufficientEC3Data: { - code: -60025, - text: "Error parsing ec-3, not enough data" - }, - InvalidEC3Magic: { - code: -60026, - text: "Invalid ec-3 magic" - }, - ReservedStreamType: { - code: -60027, - text: "Reserved stream type" - }, - InsufficientAC3Data: { - code: -60028, - text: "error parsing ac-3, not enough data" - }, - InvalidAC3SamplingRateCode: { - code: -60030, - text: "Invalid ac-3 samplingRateCode" - }, - PlaylistErrorInvalidEXTXDEFINE: { - code: -61e3, - text: "Encountered undefined/not imported EXT-X-DEFINE property" - }, - PlaylistErrorMissingImportReference: { - code: -61001, - text: "IMPORT references variable not in master playlist and/or NAME" - }, - PlaylistErrorInvalidSERVERURI: { - code: -61002, - text: "Encountered undefined/invalid SERVER-URI attribute for EXT-X-CONTENT-STEERING tag" - }, - PlaylistErrorInvalidPATHWAYID: { - code: -61003, - text: "Encountered invalid PATHWAY-ID attribute for EXT-X-CONTENT-STEERING tag" - }, - PlaylistErrorInvalidSCORE: { - code: -61004, - text: "Encountered negative/non-number SCORE property" - }, - KeySystemFailedToUpdateSession: { - code: -62e3, - text: "KeySystem: Promise Rejected while updating session" - }, - KeySystemFailedToGenerateLicenseRenewal: { - code: -62001, - text: "KeySystem: Failed to generate license renewal" - }, - KeySystemFailedToGenerateLicenseRequest: { - code: -62002, - text: "KeySystem: Failed to generate license request" - }, - KeySystemAbort: { - code: -62003, - text: "KeySystem: Aborted" - }, - KeySystemUnexpectedStateTransition: { - code: -62004, - text: "KeySystem: Unexpected state transition" - }, - KeySystemUnexpectedState: { - code: -62005, - text: "KeySystem: Unexpected state" - }, - KeySystemCDMUnknownError: { - code: -62006, - text: "KeySystem: Unknown error from CDM" - }, - KeySystemRequestTimedOut: { - code: -62007, - text: "Key request timed out" - }, - KeySystemUnexpectedMETHOD: { - code: -62008, - text: "Unexpected METHOD attribute" - }, - KeySystemUnmatchedString: { - code: -62009, - text: "KeySystem: string does not match" - }, - KeySystemInternalError: { - code: -62010, - text: "KeySystem: internal-error" - }, - KeySystemOutputRestricted: { - code: -62011, - text: "KeySystem: output-restricted" - }, - KeySystemSetupError: { - code: -62012, - text: "KeySystem: setup error" - }, - KeySystemFailedToInitialize: { - code: -62013, - text: "KeySystem: could not initialize" - }, - KeySystemFailedToCreateSession: { - code: -62014, - text: "KeySystem: could not create session" - }, - KeySystemUndefinedNavigator: { - code: -62015, - text: "KeySystem: navigator undefined" - }, - KeySystemNoKeySystemsToTry: { - code: -62016, - text: "KeySystem: no key systems to try" - }, - KeySystemNoConstructor: { - code: -62017, - text: "KeySystem: No constructor" - }, - KeySystemNoKeySystemAccess: { - code: -62018, - text: "KeySystem: No KeySystemAccess" - }, - KeySystemCertificateLoadError: { - code: -62019, - text: "KeySystem: Certificate Load Error" - } - }, - o = "networkError", - L = "mediaError", - s = "otherError", - _ = "manifestParsingError", - f = "manifestIncompatibleCodecsError", - N = "levelLoadError", - n = "bufferAppendError", - r = "internalException"; - class V extends p { - constructor(e, t, i) { - super(s, r, e, t, i) - } + if (lastPos === -1) + return f + else if (lastPos < flen) { + str += f.slice(lastPos); } - class D extends p { - constructor(e, t, i) { - super(L, "fragParsingError", e, t, i) - } + + return str + } + + const format = quickFormatUnescaped; + + var browser = pino; + + const _console = pfGlobalThisOrFallback().console || {}; + const stdSerializers = { + mapHttpRequest: mock, + mapHttpResponse: mock, + wrapRequestSerializer: passthrough, + wrapResponseSerializer: passthrough, + wrapErrorSerializer: passthrough, + req: mock, + res: mock, + err: asErrValue + }; + + function shouldSerialize (serialize, serializers) { + if (Array.isArray(serialize)) { + const hasToFilter = serialize.filter(function (k) { + return k !== '!stdSerializers.err' + }); + return hasToFilter + } else if (serialize === true) { + return Object.keys(serializers) } - class F extends p { - constructor(e, t, i, r) { - super("muxError", "remuxAllocError", e, t, i), this.bytes = r - } + + return false + } + + function pino (opts) { + opts = opts || {}; + opts.browser = opts.browser || {}; + + const transmit = opts.browser.transmit; + if (transmit && typeof transmit.send !== 'function') { throw Error('pino: transmit option must have a send function') } + + const proto = opts.browser.write || _console; + if (opts.browser.write) opts.browser.asObject = true; + const serializers = opts.serializers || {}; + const serialize = shouldSerialize(opts.browser.serialize, serializers); + let stdErrSerialize = opts.browser.serialize; + + if ( + Array.isArray(opts.browser.serialize) && + opts.browser.serialize.indexOf('!stdSerializers.err') > -1 + ) stdErrSerialize = false; + + const levels = ['error', 'fatal', 'warn', 'info', 'debug', 'trace']; + + if (typeof proto === 'function') { + proto.error = proto.fatal = proto.warn = + proto.info = proto.debug = proto.trace = proto; } - - function S(e) { - return e.baseTime / e.timescale - } - - function B(e, t) { - return { - baseTime: Math.floor(e * t), - timescale: t - } - } - - function g(e, t) { - return S(e) < S(t) ? e : t - } - - function y(e, t) { - return S(e) > S(t) ? e : t - } - - function b(e, t) { - return S(e) - S(t) - } - var t = void 0 !== l.Buffer ? require("events").EventEmitter : class { - constructor() { - this.eventMap = {} - } - _on(e, t, i = !1) { - return null == this.eventMap[e] && (this.eventMap[e] = []), i ? this.eventMap[e].splice(0, 0, t) : this.eventMap[e].push(t), this - } - _off(e, t) { - return null != this.eventMap[e] && (this.eventMap[e] = this.eventMap[e].filter(e => e.listener !== t.listener), 0 === this.eventMap[e].length && delete this.eventMap[e]), this - } - on(e, t) { - return this._on(e, { - listener: t, - once: !1 - }) - } - off(e, t) { - return this._off(e, { - listener: t - }) - } - addListener(e, t) { - return this.on(e, t) - } - once(e, t) { - return this._on(e, { - listener: t, - once: !0 - }) - } - removeListener(e, t) { - return this.off(e, t) - } - removeAllListeners(e) { - return delete this.eventMap[e], this - } - setMaxListeners(e) { - return this - } - getMaxListeners() { - return 1 / 0 - } - listeners(e) { - return null == this.eventMap[e] ? [] : this.eventMap[e].map(e => e.listener) - } - rawListeners(e) { - return this.listeners(e) - } - emit(e, ...t) { - if (null == this.eventMap[e]) return !1; - let i = !1; - for (const r of this.eventMap[e]) { - try { - r.listener.apply(this, t) - } catch (e) {} - i = !0 - } - return i - } - listenerCount(e) { - return null == this.eventMap[e] ? 0 : this.eventMap[e].length - } - prependListener(e, t) { - return this._on(e, { - listener: t, - once: !1 - }, !0) - } - prependOnceListener(e, t) { - return this._on(e, { - listener: t, - once: !0 - }, !0) - } - eventNames() { - return Object.keys(this.eventMap) - } - }; - class a extends t { - trigger(e, t) { - this.emit(e, t) - } - } - - function E(e, t, i, r, n) { - let s, a, o, l; - const d = navigator.userAgent.toLowerCase(), - u = [96e3, 88200, 64e3, 48e3, 44100, 32e3, 24e3, 22050, 16e3, 12e3, 11025, 8e3, 7350]; - s = 1 + ((192 & t[i + 2]) >>> 6); - var c = (60 & t[i + 2]) >>> 2; - if (!(u.length - 1 < c)) return o = (1 & t[i + 2]) << 2, o |= (192 & t[i + 3]) >>> 6, /firefox/i.test(d) ? 6 <= c ? (s = 5, l = new Array(4), a = c - 3) : (s = 2, l = new Array(2)) : -1 !== d.indexOf("android") ? (s = 2, l = new Array(2)) : (s = 5, l = new Array(4), a = r && (-1 !== r.indexOf("mp4a.40.29") || -1 !== r.indexOf("mp4a.40.5")) || !r && 6 <= c ? c - 3 : ((r && -1 !== r.indexOf("mp4a.40.2") || !r && 1 == o) && (s = 2, l = new Array(2)), c)), l[0] = s << 3, l[0] |= (14 & c) >> 1, l[1] |= (1 & c) << 7, l[1] |= o << 3, 5 === s && (l[1] |= (14 & a) >> 1, l[2] = (1 & a) << 7, l[2] |= 8, l[3] = 0), { - esdsConfig: l, - samplerate: u[c], - channelCount: o, - segmentCodec: "aac", - codec: "mp4a.40." + s - }; { - const t = new D(!0, `invalid ADTS sampling index:${c}`, $.InvalidADTSSamplingIndex); - e.trigger(x.INTERNAL_ERROR, t) - } - } - class u { - constructor(e, t, i, r, n) { - this.observer = e, this.remuxer = t, this.config = i, this.typeSupported = r, this.logger = n - } - static probe(e, t) { - throw new Error("Method not implemented") - } - resetTimeStamp(e) {} - resetInitSegment(e, t, i, r) {} - destroy() {} - } - class c extends u { - constructor(e, t, i, r, n) { - super(e, t, i, r, n), this.observer = e, this.remuxer = t, this.config = i, this.typeSupported = r, this.logger = n, this.esRemuxer = t - } - } - class h { - constructor(e, t, i) { - this.observer = e, this.config = t, this.logger = i - } - resetInitSegment() {} - resetTimeStamp(e) {} - destroy() {} - } - let m, I; - var w = { - strToUtf8array: e => (m = m || new TextEncoder, m.encode(e)), - utf8arrayToStr: e => (I = I || new TextDecoder("utf-8"), I.decode(e)) - }, - A = { - strToUtf8array(e) { - e = l.Buffer.from(e, "utf-8"); - return new Uint8Array(e.buffer, e.byteOffset, e.byteLength) - }, - utf8arrayToStr: e => l.Buffer.from(e).toString("utf-8") - }; - let O = { - strToUtf8array(e) { - const t = unescape(encodeURIComponent(e)), - i = new Uint8Array(t.length); - for (let e = 0; e < t.length; e++) i[e] = t.charCodeAt(e); - return i - }, - utf8arrayToStr: e => String.fromCharCode.apply(null, Array.from(e)) - }; - "undefined" != typeof TextEncoder && "undefined" != typeof TextDecoder ? O = w : "function" == typeof(null === (fl = l.Buffer) || void 0 === fl ? void 0 : fl.from) && (O = A); - const k = { - name: "ID3" - }; - class C { - constructor(e, t) { - this.logger = t, this._hasTimeStamp = !1, this._audioType = null, this._length = 0, this._frames = []; - let i, r, n, s, a = 0; - for (;;) - if (n = C.readUTF(e, a, 3), a += 3, "ID3" === n) { - this._minor = e[a++], this._revision = e[a++]; - const t = e[a++]; - if (128 & t && (this._unsynchronized = !0, this.logger.error(k, "id3 tag is unsynchronized")), 64 & t && (this._hasExtendedHeader = !0, this.logger.warn(k, "id3 tag has extended header")), i = C.readSynchSafeUint32(e.subarray(a, a + 4)), a += 4, r = a + i, this._hasExtendedHeader) { - const t = C.readSynchSafeUint32(e.subarray(a, a + 4)); - this.logger.warn(k, `id3 tag has ${t}-byte extended header. usually 6 or 10 bytes`), a += t - } - 2 < this.minor ? this._parseID3Frames(e, a, r) : this.logger.error(k, "[id3] doesn't support older than v2.3 tags"), a = r - } else { - if ("3DI" !== n) return a -= 3, void((s = a) && (this.hasTimeStamp || this.logger.warn(k, "ID3 tag found, but no timestamp"), this._length = s, this._payload = e.slice(0, s))); - a += 7 - } - } - static isHeader(e, t) { - return 73 === e[t] && 68 === e[t + 1] && 51 === e[t + 2] && e[t + 3] < 255 && e[t + 4] < 255 && e[t + 6] < 128 && e[t + 7] < 128 && e[t + 8] < 128 && e[t + 9] < 128 - } - static readSynchSafeUint32(e) { - return 2097152 * (127 & e[0]) + 16384 * (127 & e[1]) + 128 * (127 & e[2]) + (127 & e[3]) - } - static readUTF(e, t, i) { - let r = "", - n = t; - for (var s = t + i; r += String.fromCharCode(e[n++]), n < s;); - return r - } - isID3Frame(e, t) { - return e[t + 4] < 128 && e[t + 5] < 128 && e[t + 6] < 128 && e[t + 7] < 128 - } - decodeID3Frame(e) { - return "TXXX" === e.type ? this.decodeTxxxFrame(e) : "WXXX" === e.type ? this.decodeWxxxFrame(e) : "PRIV" === e.type ? this.decodePrivFrame(e) : "T" === e.type[0] ? this.decodeTextFrame(e) : { - key: e.type, - data: e.data - } - } - decodeTxxxFrame(e) { - if (!(e.size < 2) && 3 === e.data[0]) { - var t = 1, - i = this.id3utf8ArrayToStr(e.data.subarray(1)); - return t += i.length + 1, { - key: "TXXX", - description: i, - data: this.id3utf8ArrayToStr(e.data.subarray(t)) - } - } - } - decodeWxxxFrame(e) { - if (!(e.size < 2) && 3 === e.data[0]) { - var t = 1, - i = this.id3utf8ArrayToStr(e.data.subarray(1)); - return t += i.length + 1, { - key: "WXXX", - description: i, - data: O.utf8arrayToStr(e.data.subarray(t)) - } - } - } - decodeTextFrame(e) { - if (!(e.size < 2) && 3 === e.data[0]) { - var t = e.data.subarray(1); - return { - key: e.type, - data: this.id3utf8ArrayToStr(t) - } - } - } - decodePrivFrame(e) { - if (!(e.size < 2)) { - var t = this.id3utf8ArrayToStr(e.data); - return { - key: "PRIV", - info: t, - data: e.data.slice(t.length + 1) - } - } - } - _extractID3Frame(e, t, i, r, n) { - var s = r + i; - let a; - return s <= n ? a = { - type: t, - data: e.slice(r, s) - } : this.logger.error(k, `id3 frame ${t} size ${i} exceeded ${n}`), a - } - _parseID3Frames(e, t, i) { - let r, n, s, a; - for (; t + 8 <= i;) { - if (!this.isID3Frame(e, t)) return void this.logger.error(k, `[id3] illegal id3 frame @ offset ${t}. skip this id3 tag`); - if (r = C.readUTF(e, t, 4), t += 4, "" === r) return; - if (0 === (n = C.readSynchSafeUint32(e.subarray(t, t + 4)))) return; - t += 4, e[t++], e[t++], s = t; - var o = this._extractID3Frame(e, r, n, s, i); - if (o) { - const e = this.decodeID3Frame(o); - this._frames.push(e) - } - if ("PRIV" === r) - if (53 === n && "com.apple.streaming.transportStreamTimestamp" === C.readUTF(e, t, 44)) { - t += 44, t += 4; - const i = 1 & e[t++]; - this._hasTimeStamp = !0, a = ((e[t++] << 23) + (e[t++] << 15) + (e[t++] << 7) + e[t++]) / 45, i && (a += 47721858.84), a = Math.round(a), this._timeStamp = a - } else 45 <= n && "com.apple.streaming.audioDescription" === C.readUTF(e, t, 36) ? (t += 37, this._audioType = C.readUTF(e, t, 4), t += 4, t += n - 41) : t += n; - else t += n - } - } - id3utf8ArrayToStr(e) { - let t, i, r = "", - n = 0; - const s = e.length; - for (; n < s;) { - const s = e[n++]; - switch (s >> 4) { - case 0: - return r; - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - r += String.fromCharCode(s); - break; - case 12: - case 13: - t = e[n++], r += String.fromCharCode((31 & s) << 6 | 63 & t); - break; - case 14: - t = e[n++], i = e[n++], r += String.fromCharCode((15 & s) << 12 | (63 & t) << 6 | (63 & i) << 0) - } - } - } - get hasTimeStamp() { - return this._hasTimeStamp - } - get timeStamp() { - return this._timeStamp - } - get audioType() { - return this._audioType - } - get length() { - return this._length - } - get payload() { - return this._payload - } - get frames() { - return this._frames - } - get minor() { - return this._minor - } - get revision() { - return this._revision - } - } - var M = C; - const U = { - name: "AACDemuxer" - }; - class K extends c { - resetInitSegment(e, t) { - this.audioConfig = void 0, this.audioTrack = void 0, this.duration = t - } - static probe(e, t) { - let i, r; - for (i = new M(e, t).length, r = Math.min(e.length - 1, i + 100); i < r; i++) - if (255 === e[i] && 240 == (246 & e[i + 1])) return !0; - return !1 - } - append(e, t, i, r, n) { - var s = new M(e, this.logger), - a = s.hasTimeStamp ? 90 * s.timeStamp : 9e4 * t; - let o, l, d, u, c, h, p, f, m, g; - for (s.length && (g = s.payload, s.frames.length && (m = s.frames), f = { - id3Samples: [{ - pts: a, - dts: a, - data: g, - frames: m - }], - inputTimescale: 9e4 - }), d = s.length, h = e.length; d < h - 1 && (255 !== e[d] || 240 != (246 & e[d + 1])); d++); - if (!this.audioConfig && (this.audioConfig = E(this.observer, e, d, void 0, this.logger), !this.audioConfig)) throw "failed to parse adts config"; - if (!this.audioTrack) { - const e = { - id: 258, - inputTimescale: 9e4, - timescale: NaN, - duration: this.duration, - encrypted: !1, - keyTagInfo: n - }, - t = { - len: 0, - sequenceNumber: 0, - esSamples: [] - }; - this.audioTrack = { - info: e, - parsingData: t, - type: "audio", - config: this.audioConfig - } - } - "zaac" !== s.audioType && "zach" !== s.audioType && "zacp" !== s.audioType || (this.audioTrack.info.encrypted = !0), l = 0; - for (var y = 9216e4 / this.audioConfig.samplerate; d + 5 < h && (u = 1 & e[d + 1] ? 7 : 9, o = (3 & e[d + 3]) << 11 | e[d + 4] << 3 | (224 & e[d + 5]) >>> 5, o -= u, 0 < o && d + u + o <= h);) - for (c = a + l * y, p = { - unit: e.subarray(d + u, d + u + o), - pts: c, - dts: c, - keyTagInfo: n - }, this.audioTrack.parsingData.esSamples.push(p), this.audioTrack.parsingData.len += o, d += o + u, l++; d < h - 1; d++) { - if (M.isHeader(e, d)) { - const t = new M(e.subarray(d), this.logger); - if (0 < t.length) { - d += t.length; - const e = t.hasTimeStamp ? 90 * t.timeStamp : a; - f.id3Samples.push({ - pts: e, - dts: e, - data: t.payload, - frames: t.frames - }) - } else this.logger.error(U, `[id3] invalid length ${h}`) - } - if (255 === e[d] && 240 == (246 & e[d + 1])) break - } - this.esRemuxer.remuxEsTracks(this.audioTrack, void 0, f, void 0, t, i, r, n) - } - } - class q { - bsReadAndUpdate(e, t, i) { - e = this.readBits(e, t, i); - return this.updateOffset(t, i), e - } - bsWriteAndUpdate(e, t, i, r) { - r = this.writeBits(e, t, i, r); - return this.updateOffset(t, i), r - } - bsSkip(e, t) { - this.updateOffset(e, t) - } - readBits(i, r, n) { - if (i && r) { - let t = r.byteOffset; - const s = r["usedBits"]; - if (!(8 <= s || 32 < s + n)) { - let e; - const a = new Uint32Array(1), - o = new Uint32Array(1), - l = new Uint8Array(1); - if (!(8 <= s || 32 < n)) { - if (s) { - const r = 8 - s, - a = n < r ? r - n : 0; - o[0] = 4278190080 >>> 32 - r, e = (i[t] & o[0]) >>> a, t += 1, n -= r - } - for (; 0 < n;) { - l[0] = i[t]; - const r = Math.min(n, 8), - s = 8 - r; - o[0] = 4278190080 >>> 24 + s << s, a[0] = (l[0] & o[0]) >> s, e = e ? e << r | a[0] : a[0], t += 1, n -= r - } - return e - } - } - } - } - writeBits(t, i, r, n) { - if (t && i) { - let e = i.byteOffset; - var i = i["usedBits"]; - if (!(8 <= i || 32 < i + r)) { - const s = new Uint32Array(1), - a = new Uint32Array(1), - o = new Uint32Array(1), - l = new Uint8Array(1); - for (s[0] = n, i && (a[0] = s[0] << 32 - r, o[0] = 4278190080, l[0] = (a[0] & o[0]) >>> 24 + i, t[e] &= ~(o[0] >>> 24 + i), t[e] |= l[0], e += 1, r -= 8 - i); 0 < r;) { - a[0] = s[0] << 32 - r, o[0] = 4278190080, l[0] = (a[0] & o[0]) >>> 24; - const d = r < 0 ? 8 - r : 0; - t[e] &= ~(o[0] >>> 24 >>> d << d), t[e] |= l[0], r -= 8, e += 1 - } - return 0 - } - } - } - updateOffset(e, t) { - var i, r; - !e || !t || 32 < e.usedBits + t || (i = e.usedBits % 8, r = Math.floor((i + t) / 8), t = (i + t) % 8, e.byteOffset += r, e.usedBits = t) - } - } - - function H(e, t) { - return 1536 / e.samplerate * t - } - - function j(e, t, i, r) { - let n; - if (i + 8 > t.length) return n = new D(!0, "error parsing ac-3, not enough data", $.InsufficientAC3Data), void e.trigger(x.INTERNAL_ERROR, n); - if (11 !== t[i] || 119 !== t[i + 1]) return n = new D(!0, "invalid ac-3 magic", $.InvalidAC3Magic), void e.trigger(x.INTERNAL_ERROR, n); - var s = t[i + 4] >> 6; - if (3 <= s) return n = new D(!0, `invalid ac-3 samplingRateCode:${s}`, $.InvalidAC3SamplingRateCode), void e.trigger(x.INTERNAL_ERROR, n); - var a = 63 & t[i + 4], - o = t[i + 6] >> 5; - let l = 0; - 2 == o ? l += 2 : (1 & o && 1 != o && (l += 2), 4 & o && (l += 2)); - var d = (t[i + 6] << 8 | t[i + 7]) >> 12 - l & 1, - u = [2, 1, 2, 3, 3, 4, 4, 5][o] + d, - e = t[i + 5] >> 3, - i = 7 & t[i + 5]; - return { - samplerate: W[s], - channelCount: u, - segmentCodec: "ac3", - codec: "ac-3", - extraData: s << 22 | e << 17 | i << 14 | o << 11 | d << 10 | a >> 1 << 5 - } - } - - function Q(e, t, i) { - let r; - if (i + 8 > t.length) return r = new D(!0, "error parsing ac-3, not enough data", $.InsufficientAC3Data), void e.trigger(x.INTERNAL_ERROR, r); - if (11 !== t[i] || 119 !== t[i + 1]) return r = new D(!0, "invalid ac-3 magic", $.InvalidAC3Magic), void e.trigger(x.INTERNAL_ERROR, r); - var n = t[i + 4] >> 6; - return 3 <= n ? (r = new D(!0, `invalid ac-3 samplingRateCode:${n}`, $.InvalidAC3SamplingRateCode), void e.trigger(x.INTERNAL_ERROR, r)) : (i = 63 & t[i + 4], 2 * G[3 * i + n]) - } - const W = [48e3, 44100, 32e3], - G = [64, 69, 96, 64, 70, 96, 80, 87, 120, 80, 88, 120, 96, 104, 144, 96, 105, 144, 112, 121, 168, 112, 122, 168, 128, 139, 192, 128, 140, 192, 160, 174, 240, 160, 175, 240, 192, 208, 288, 192, 209, 288, 224, 243, 336, 224, 244, 336, 256, 278, 384, 256, 279, 384, 320, 348, 480, 320, 349, 480, 384, 417, 576, 384, 418, 576, 448, 487, 672, 448, 488, 672, 512, 557, 768, 512, 558, 768, 640, 696, 960, 640, 697, 960, 768, 835, 1152, 768, 836, 1152, 896, 975, 1344, 896, 976, 1344, 1024, 1114, 1536, 1024, 1115, 1536, 1152, 1253, 1728, 1152, 1254, 1728, 1280, 1393, 1920, 1280, 1394, 1920]; - class z extends c { - resetInitSegment(e, t) { - this.audioConfig = void 0, this.audioTrack = void 0, this.duration = t - } - static probe(e, t) { - var i = new M(e, t), - t = i.length; - return !!(i.hasTimeStamp && 11 === e[t] && 119 === e[t + 1] && (new q).bsReadAndUpdate(e, { - byteOffset: t + 5, - usedBits: 0 - }, 5) < 16) - } - append(e, t, i, r, n) { - var s = new M(e, this.logger), - a = 90 * s.timeStamp, - o = e.byteLength; - let l = 0, - d = s.length; - if (this.audioConfig || (this.audioConfig = j(this.observer, e, d, this.logger)), !this.audioConfig) throw "failed to parse ac3 config"; - if (!this.audioTrack) { - const e = { - id: 258, - inputTimescale: 9e4, - timescale: NaN, - duration: this.duration, - encrypted: !1, - keyTagInfo: n - }, - t = { - len: 0, - sequenceNumber: 0, - esSamples: [] - }; - this.audioTrack = { - info: e, - parsingData: t, - type: "audio", - config: this.audioConfig - } - } - var u = H(this.audioConfig, this.audioTrack.info.inputTimescale); - for ("zac3" === s.audioType && (this.audioTrack.info.encrypted = !0); d < o;) { - if (M.isHeader(e, d) && (d += new M(e.subarray(d), this.logger).length), 11 !== e[d] || 119 !== e[d + 1]) { - const e = new D(!0, "invalid ac-3 magic", $.InvalidAC3Magic); - return void this.observer.trigger(x.INTERNAL_ERROR, e) - } - const t = Q(this.observer, e, d), - i = a + l * u, - r = { - unit: e.subarray(d, d + t), - pts: i, - dts: i, - keyTagInfo: n - }; - this.audioTrack.parsingData.esSamples.push(r), this.audioTrack.parsingData.len += t, d += t, l++ - } - this.esRemuxer.remuxEsTracks(this.audioTrack, void 0, { - id3Samples: [{ - pts: a, - dts: a, - data: s.payload, - frames: s.frames - }], - inputTimescale: this.audioTrack.info.inputTimescale - }, void 0, t, i, r, n) - } - } - var X = function(t, i, r, n) { - const s = new q; - let a, o = !1, - l = 0; - for (; r < i.length;) { - if (r + 8 > i.length) return a = new D(!0, "error parsing ec-3, not enough data", $.InsufficientEC3Data), void t.trigger(x.INTERNAL_ERROR, a); - let e = 0; - if (M.isHeader(i, r) && (e = new M(i.subarray(r), n).length || 0, r += e), 11 !== i[r] || 119 !== i[r + 1]) return a = new D(!0, "invalid ec-3 magic", $.InvalidEC3Magic), void t.trigger(x.INTERNAL_ERROR, a); - var d = { - byteOffset: r + 2, - usedBits: 0 - }, - u = s.bsReadAndUpdate(i, d, 2), - c = s.bsReadAndUpdate(i, d, 3); - if (0 === u || 2 === u) - if (!0 === o) { - if (0 === c) break - } else o = !0; - else if (1 !== u) return a = new D(!0, "reserved stream type", $.ReservedStreamType), void t.trigger(x.INTERNAL_ERROR, a); - d = 2 * (s.bsReadAndUpdate(i, d, 11) + 1); - r += d, l += d + (e || 0) - } - return l - }, - Y = function(t, i, r, n) { - const s = { - frmsiz: 0, - fscod: 0, - numblkscod: 0, - acmod: 0, - lfeon: 0, - bsid: 0, - strmtyp: 0, - substreamid: 0, - chanmape: 0, - chanmap: 0, - mixdef: 0, - mixdeflen: 0, - bsmod: 0 - }, - a = { - fscod: 0, - acmod: 0, - lfeon: 0, - bsid: 0, - bsmod: 0, - chan_loc: 0, - data_rate: 0, - num_ind_sub: 0, - num_dep_sub: [], - complexity_index_type_a: 0 - }, - o = new q; - let l, d = !1, - u = 0; - for (; r < i.length;) { - if (r + 8 > i.length) return l = new D(!0, "error parsing ec-3, not enough data", $.InsufficientEC3Data), void t.trigger(x.INTERNAL_ERROR, l); - let e = 0; - if (M.isHeader(i, r) && (e = new M(i.subarray(r), n).length || 0, r += e), 11 !== i[r] || 119 !== i[r + 1]) return l = new D(!0, "invalid ec-3 magic", $.InvalidEC3Magic), void t.trigger(x.INTERNAL_ERROR, l); - const h = { - byteOffset: r + 2, - usedBits: 0 - }; - if (s.strmtyp = o.bsReadAndUpdate(i, h, 2), s.substreamid = o.bsReadAndUpdate(i, h, 3), 0 === s.strmtyp || 2 === s.strmtyp) { - if (!0 === d) { - if (0 === s.substreamid) break - } else d = !0; - a.num_ind_sub++, a.num_dep_sub.push(0) - } else { - if (1 !== s.strmtyp) return l = new D(!0, "reserved stream type", $.ReservedStreamType), void t.trigger(x.INTERNAL_ERROR, l); - a.num_dep_sub[a.num_ind_sub - 1]++ - } - if (s.frmsiz = o.bsReadAndUpdate(i, h, 11), s.fscod = o.bsReadAndUpdate(i, h, 2), 3 === s.fscod ? (o.bsSkip(h, 2), s.numblkscod = 3) : s.numblkscod = o.bsReadAndUpdate(i, h, 2), s.acmod = o.bsReadAndUpdate(i, h, 3), s.lfeon = o.bsReadAndUpdate(i, h, 1), s.bsid = o.bsReadAndUpdate(i, h, 5), o.bsSkip(h, 5), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 8), 0 === s.acmod && (o.bsSkip(h, 5), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 8)), 1 === s.strmtyp && (s.chanmape = o.bsReadAndUpdate(i, h, 1), s.chanmape && (s.chanmap = o.bsReadAndUpdate(i, h, 16))), o.bsReadAndUpdate(i, h, 1) && (2 < s.acmod && o.bsSkip(h, 2), 1 & s.acmod && 2 < s.acmod && o.bsSkip(h, 6), 4 & s.acmod && o.bsSkip(h, 6), s.lfeon && o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 5), 0 === s.strmtyp)) { - if (o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 6), 0 === s.acmod && o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 6), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 6), s.mixdef = o.bsReadAndUpdate(i, h, 2), 1 === s.mixdef) o.bsSkip(h, 5); - else if (2 === s.mixdef) o.bsSkip(h, 12); - else if (3 === s.mixdef) { - s.mixdeflen = o.bsReadAndUpdate(i, h, 5), o.bsReadAndUpdate(i, h, 1) && (o.bsSkip(h, 5), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && (o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 4))), o.bsReadAndUpdate(i, h, 1) && (o.bsSkip(h, 5), o.bsReadAndUpdate(i, h, 1) && (o.bsSkip(h, 7), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 8))); - const t = s.mixdeflen + 2 + (h.usedBits ? 1 : 0); - h.byteOffset += t - } - if (s.acmod < 2 && (o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 14), 0 === s.acmod && o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 14)), o.bsReadAndUpdate(i, h, 1)) - if (0 === s.numblkscod) o.bsSkip(h, 5); - else - for (let e = 0; e < s.numblkscod; e++) o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 5) - } - if (s.bsmod = 0, o.bsReadAndUpdate(i, h, 1) && (s.bsmod = o.bsReadAndUpdate(i, h, 3), o.bsSkip(h, 2), 2 === s.acmod && o.bsSkip(h, 4), 6 <= s.acmod && o.bsSkip(h, 2), o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 8), 0 === s.acmod && o.bsReadAndUpdate(i, h, 1) && o.bsSkip(h, 8), s.fscod < 3 && o.bsSkip(h, 1)), 0 === s.strmtyp && 3 !== s.numblkscod && o.bsSkip(h, 1), 2 !== s.strmtyp || (3 === s.numblkscod ? 1 : o.bsReadAndUpdate(i, h, 1)) && o.bsReadAndUpdate(i, h, 6), o.bsReadAndUpdate(i, h, 1)) { - const t = o.bsReadAndUpdate(i, h, 6); - if (0 === s.strmtyp && 0 === s.substreamid && 1 === t) { - const t = o.bsReadAndUpdate(i, h, 7), - r = o.bsReadAndUpdate(i, h, 1), - n = o.bsReadAndUpdate(i, h, 8); - 0 === t && 1 === r && 1 <= n && n <= 16 && (a.complexity_index_type_a = n) - } - } - if (s.chanmape) a.chan_loc |= s.chanmap; - else { - const t = [40960, 16384, 40960, 57344, 41472, 57856, 47104, 63488]; - a.chan_loc |= t[s.acmod] - } - 0 === s.strmtyp && (a.fscod = s.fscod, a.bsid = s.bsid, a.bsmod = s.bsmod, a.acmod = s.acmod, a.lfeon = s.lfeon), a.chan_loc |= s.lfeon ? 1 : 0; - const p = 2 * (s.frmsiz + 1); - r += p, u += p + (e || 0) - } - let c = 0; - for (let e = 0; e < 16; e++) a.chan_loc & 1 << e && c++; - a.lfeon && c++; - let h = 10 + 3 * a.num_ind_sub; - const p = [48e3, 44100, 32e3][a.fscod]; - a.data_rate = p / 1536 * u * 8, h = 10 + 3 * a.num_ind_sub; - for (let e = 0; e < a.num_ind_sub; e++) 0 < a.num_dep_sub[e] && h++; - 0 < a.complexity_index_type_a && (h += 2); - var f = new Uint8Array(h), - m = { - byteOffset: 0, - usedBits: 0 - }; - o.bsWriteAndUpdate(f, m, 32, h), o.bsWriteAndUpdate(f, m, 32, 1684366131), o.bsWriteAndUpdate(f, m, 13, a.data_rate), o.bsWriteAndUpdate(f, m, 3, a.num_ind_sub); - for (let e = 0; e < a.num_ind_sub; e++) o.bsWriteAndUpdate(f, m, 2, a.fscod), o.bsWriteAndUpdate(f, m, 5, a.bsid), o.bsWriteAndUpdate(f, m, 1, 0), o.bsWriteAndUpdate(f, m, 1, 0 === e ? 0 : 1), o.bsWriteAndUpdate(f, m, 3, a.bsmod), o.bsWriteAndUpdate(f, m, 3, a.acmod), o.bsWriteAndUpdate(f, m, 1, a.lfeon), o.bsWriteAndUpdate(f, m, 3, 0), o.bsWriteAndUpdate(f, m, 4, a.num_dep_sub[e]), 0 < a.num_dep_sub[e] ? o.bsWriteAndUpdate(f, m, 9, a.chan_loc) : o.bsWriteAndUpdate(f, m, 1, 0); - return 0 < a.complexity_index_type_a && (o.bsWriteAndUpdate(f, m, 7, 0), o.bsWriteAndUpdate(f, m, 1, 1), o.bsWriteAndUpdate(f, m, 8, a.complexity_index_type_a)), { - samplerate: p, - channelCount: c, - segmentCodec: "ec3", - codec: "ec-3", - extraDataBytes: f - } - }; - class J extends c { - resetInitSegment(e, t) { - this.audioConfig = void 0, this.audioTrack = void 0, this.duration = t - } - static probe(e, t) { - var i = new M(e, t), - t = i.length; - return !(!i.hasTimeStamp || 11 !== e[t] || 119 !== e[t + 1] || 16 !== (new q).bsReadAndUpdate(e, { - byteOffset: t + 5, - usedBits: 0 - }, 5)) - } - append(e, t, i, r, n) { - var s = new M(e, this.logger), - a = 90 * s.timeStamp, - o = e.length; - let l = 0, - d = s.length; - if (this.audioConfig || (this.audioConfig = Y(this.observer, e, d, this.logger)), !this.audioConfig) throw "failed to parse ec-3 config"; - if (!this.audioTrack) { - const e = { - id: 258, - inputTimescale: 9e4, - timescale: NaN, - duration: this.duration, - encrypted: !1, - keyTagInfo: n - }, - t = { - len: 0, - sequenceNumber: 0, - esSamples: [] - }; - this.audioTrack = { - info: e, - parsingData: t, - type: "audio", - config: this.audioConfig - } - } - var u = H(this.audioConfig, this.audioTrack.info.inputTimescale); - for ("zec3" === s.audioType && (this.audioTrack.info.encrypted = !0); d < o;) { - const t = X(this.observer, e, d, this.logger), - i = a + l * u, - r = { - unit: e.subarray(d, d + t), - pts: i, - dts: i, - keyTagInfo: n - }; - this.audioTrack.parsingData.esSamples.push(r), this.audioTrack.parsingData.len += t, d += t, l++ - } - this.esRemuxer.remuxEsTracks(this.audioTrack, void 0, { - id3Samples: [{ - pts: a, - dts: a, - data: s.payload, - frames: s.frames - }], - inputTimescale: this.audioTrack.info.inputTimescale - }, void 0, t, i, r, n) - } - } - const Z = { - BitratesMap: [32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160], - SamplingRateMap: [44100, 48e3, 32e3, 22050, 24e3, 16e3, 11025, 12e3, 8e3], - SamplesCoefficients: [ - [0, 72, 144, 12], - [0, 0, 0, 0], - [0, 72, 144, 12], - [0, 144, 144, 12] - ], - BytesInSlot: [0, 1, 1, 4], - onFrame: function(e, t, i, r, n, s, a) { - r = a + s * (10368e4 / r); - e.esSamples.push({ - unit: t, - pts: r, - dts: r - }), e.len += t.length - }, - onNoise: function(e, t) { - t.warn("mpeg audio has noise: " + e.length + " bytes") - }, - parseFrames: function(e, t, i, r, n, s, a) { - if (r < i + 2) return -1; - if (255 === t[i] || 224 == (224 & t[i + 1])) { - if (r < i + 24) return -1; - const a = t[i + 1] >> 3 & 3, - c = t[i + 1] >> 1 & 3, - h = t[i + 2] >> 4 & 15, - p = t[i + 2] >> 2 & 3, - f = !!(2 & t[i + 2]); - if (1 != a && 0 != h && 15 != h && 3 != p) { - var o = 1e3 * [32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160][14 * (3 == a ? 3 - c : 3 == c ? 3 : 4) + h - 1], - l = [44100, 48e3, 32e3, 22050, 24e3, 16e3, 11025, 12e3, 8e3][3 * (3 == a ? 0 : 2 == a ? 1 : 2) + p], - d = f ? 1 : 0, - u = t[i + 3] >> 6 == 3 ? 1 : 2, - d = 3 == c ? (3 == a ? 12 : 6) * o / l + d << 2 : (3 == a ? 144 : 72) * o / l + d | 0; - return r < i + d ? -1 : (Z.onFrame(e, t.subarray(i, i + d), o, l, u, n, s), d) - } - } - let c = i + 2; - for (; c < r;) { - if (255 === t[c - 1] && 224 == (224 & t[c])) return Z.onNoise(t.subarray(i, c - 1), a), c - i - 1; - c++ - } - return -1 - }, - parse: function(e, t, i, r, n) { - var s = t.length; - let a, o = 0; - for (; i < s && 0 < (a = Z.parseFrames(e, t, i, s, o++, r, n));) i += a - }, - getAudioConfig: function(e, t) { - var i = e[t + 1] >> 3 & 3, - r = e[t + 1] >> 1 & 3, - n = e[t + 2] >> 4 & 15, - s = e[t + 2] >> 2 & 3, - a = e[t + 2] >> 1 & 1; - if (1 != i && 0 != n && 15 != n && 3 != s) { - var o = 3 == i ? 3 - r : 3 == r ? 3 : 4, - o = 1e3 * Z.BitratesMap[14 * o + n - 1], - n = 3 == i ? 0 : 2 == i ? 1 : 2, - s = Z.SamplingRateMap[3 * n + s], - t = e[t + 3] >> 6 == 3 ? 1 : 2, - i = Z.SamplesCoefficients[i][r], - r = Z.BytesInSlot[r]; - return { - segmentCodec: "mp3", - codec: "mp3", - samplerate: s, - channelCount: t, - frameLength: parseInt(i * o / s + a, 10) * r - } - } - }, - isHeaderPattern: function(e, t) { - return 255 === e[t] && 224 == (224 & e[t + 1]) && 0 != (6 & e[t + 1]) - }, - probe: function(t, i) { - if (i + 1 < t.length && Z.isHeaderPattern(t, i)) { - var r = Z.getAudioConfig(t, i); - let e = 4; - r && r.frameLength && (e = r.frameLength); - i = i + e; - if (i === t.length || i + 1 < t.length && Z.isHeaderPattern(t, i)) return !0 - } - return !1 - } - }; - var ee = Z; - const te = { - name: "MP3Demuxer" - }; - class ie extends c { - resetInitSegment(e, t) { - this.audioConfig = void 0, this.audioTrack = void 0, this.duration = t - } - static probe(e, t) { - var i = new M(e, t); - let r, n; - if (i.hasTimeStamp) - for (r = i.length, n = Math.min(e.length - 1, r + 100); r < n; r++) - if (ee.probe(e, r)) return t.warn(te, "MPEG Audio sync word found !"), !0; - return !1 - } - append(e, t, i, r, n) { - var s = new M(e, this.logger), - a = 90 * s.timeStamp; - if (this.audioConfig || (this.audioConfig = ee.getAudioConfig(e, s.length)), !this.audioConfig) throw "unable to parse mp3 header"; - if (!this.audioTrack) { - const e = { - id: 258, - inputTimescale: 9e4, - timescale: NaN, - duration: this.duration, - encrypted: !1, - keyTagInfo: n - }, - t = { - len: 0, - sequenceNumber: 0, - esSamples: [] - }; - this.audioTrack = { - info: e, - parsingData: t, - type: "audio", - config: this.audioConfig - } - } - ee.parse(this.audioTrack.parsingData, e, s.length, a, this.logger), this.esRemuxer.remuxEsTracks(this.audioTrack, void 0, { - id3Samples: [{ - pts: a, - dts: a, - data: s.payload, - frames: s.frames - }], - inputTimescale: 9e4 - }, void 0, t, i, r) - } - } - - function re(e, t) { - if ("mp4a.40.2" === e) { - if (1 === t) return new Uint8Array([0, 200, 0, 128, 35, 128]); - if (2 === t) return new Uint8Array([33, 0, 73, 144, 2, 25, 0, 35, 128]); - if (3 === t) return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 142]); - if (4 === t) return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 128, 44, 128, 8, 2, 56]); - if (5 === t) return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 130, 48, 4, 153, 0, 33, 144, 2, 56]); - if (6 === t) return new Uint8Array([0, 200, 0, 128, 32, 132, 1, 38, 64, 8, 100, 0, 130, 48, 4, 153, 0, 33, 144, 2, 0, 178, 0, 32, 8, 224]) - } else { - if (1 === t) return new Uint8Array([1, 64, 34, 128, 163, 78, 230, 128, 186, 8, 0, 0, 0, 28, 6, 241, 193, 10, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 94]); - if (2 === t) return new Uint8Array([1, 64, 34, 128, 163, 94, 230, 128, 186, 8, 0, 0, 0, 0, 149, 0, 6, 241, 161, 10, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 94]); - if (3 === t) return new Uint8Array([1, 64, 34, 128, 163, 94, 230, 128, 186, 8, 0, 0, 0, 0, 149, 0, 6, 241, 161, 10, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 90, 94]) - } - return null - } - - function ne(e) { - return "number" == typeof e && isFinite(e) - } - - function se(e, t) { - return ne(e) ? e.toFixed(t) : `${e}` - } - - function ae(e, i = 3) { - return JSON.stringify(e, (e, t) => !isNaN(t) && null != t && t.toFixed ? Number(null == t ? void 0 : t.toFixed(i)) : t) - } - let oe = !0; - - function le(e) { - return oe ? "" : e - } - - function de(e) { - if (!e) return e; - if ("object" != typeof e) return e; { - if (Array.isArray(e)) return e.map(de); - const r = {}; - for (var [t, i] of Object.entries(e)) r[t] = de(i); - return r - } - } - - function ue(e) { - const t = [...e]; - for (let e = 0; e < t.length; e++) t[e] = Object.assign({}, t[e]), t[e].url = le(t[e].url), t[e].attrs && (t[e].attrs = Object.assign({}, t[e].attrs), t[e].attrs.URI = le(t[e].attrs.URI)); - return t - } - - function ce(e) { - const t = [...e]; - for (let e = 0; e < t.length; e++) t[e] = Object.assign({}, t[e]), t[e].url = le(t[e].url); - return t - } - const he = Math.pow(2, 32) - 1; - class pe { - static init() { - let e; - for (e in pe.types = { - avc1: [], - avcC: [], - btrt: [], - dinf: [], - dref: [], - esds: [], - free: [], - ftyp: [], - hdlr: [], - mdat: [], - mdhd: [], - mdia: [], - mfhd: [], - minf: [], - moof: [], - moov: [], - mp4a: [], - ".mp3": [], - dac3: [], - "ac-3": [], - dec3: [], - "ec-3": [], - mvex: [], - mvhd: [], - pasp: [], - sdtp: [], - stbl: [], - stco: [], - stsc: [], - stsd: [], - stsz: [], - stts: [], - tfdt: [], - tfhd: [], - traf: [], - trak: [], - trun: [], - trex: [], - tkhd: [], - vmhd: [], - smhd: [], - uuid: [], - encv: [], - enca: [], - frma: [], - schm: [], - schi: [], - senc: [], - saio: [], - saiz: [], - sinf: [], - tenc: [], - sbgp: [], - seig: [], - sgpd: [], - pssh: [] - }, pe.types) pe.types.hasOwnProperty(e) && (pe.types[e] = [e.charCodeAt(0), e.charCodeAt(1), e.charCodeAt(2), e.charCodeAt(3)]); - var t = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 118, 105, 100, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 86, 105, 100, 101, 111, 72, 97, 110, 100, 108, 101, 114, 0]), - i = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 115, 111, 117, 110, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 83, 111, 117, 110, 100, 72, 97, 110, 100, 108, 101, 114, 0]); - pe.HDLR_TYPES = { - video: t, - audio: i - }; - var r = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 12, 117, 114, 108, 32, 0, 0, 0, 1]), - n = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); - pe.STTS = pe.STSC = pe.STCO = n, pe.STSZ = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]), pe.VMHD = new Uint8Array([0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0]), pe.SMHD = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]), pe.STSD = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1]); - t = new Uint8Array([105, 115, 111, 109]), i = new Uint8Array([97, 118, 99, 49]), n = new Uint8Array([0, 0, 0, 1]); - pe.FTYP = pe.box(pe.types.ftyp, t, n, t, i), pe.DINF = pe.box(pe.types.dinf, pe.box(pe.types.dref, r)) - } - static set16(e, t, i) { - return t[i] = e >> 8 & 255, t[i + 1] = 255 & e, i + 2 - } - static set32(e, t, i) { - return t[i] = e >> 24 & 255, t[i + 1] = e >> 16 & 255, t[i + 2] = e >> 8 & 255, t[i + 3] = 255 & e, i + 4 - } - static box(e) { - var t = Array.prototype.slice.call(arguments, 1); - let i = 8, - r = t.length; - for (var n = r; r--;) i += t[r].byteLength; - const s = new Uint8Array(i); - for (s[0] = i >> 24 & 255, s[1] = i >> 16 & 255, s[2] = i >> 8 & 255, s[3] = 255 & i, s.set(e, 4), r = 0, i = 8; r < n; r++) s.set(t[r], i), i += t[r].byteLength; - return s - } - static hdlr(e) { - return pe.box(pe.types.hdlr, pe.HDLR_TYPES[e]) - } - static mdat(e) { - return pe.box(pe.types.mdat, e) - } - static mdhd(e, t) { - t *= e; - var i = Math.floor(t / (1 + he)), - t = Math.floor(t % (1 + he)); - return pe.box(pe.types.mdhd, new Uint8Array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, e >> 24 & 255, e >> 16 & 255, e >> 8 & 255, 255 & e, i >> 24, i >> 16 & 255, i >> 8 & 255, 255 & i, t >> 24, t >> 16 & 255, t >> 8 & 255, 255 & t, 85, 196, 0, 0])) - } - static mdia(e) { - var t = pe.mdhd(e.info.timescale, e.info.duration), - i = pe.hdlr(e.type), - e = pe.minf(e); - return pe.box(pe.types.mdia, t, i, e) - } - static mfhd(e) { - return pe.box(pe.types.mfhd, new Uint8Array([0, 0, 0, 0, e >> 24, e >> 16 & 255, e >> 8 & 255, 255 & e])) - } - static minf(e) { - return "audio" === e.type ? pe.box(pe.types.minf, pe.box(pe.types.smhd, pe.SMHD), pe.DINF, pe.stbl(e)) : pe.box(pe.types.minf, pe.box(pe.types.vmhd, pe.VMHD), pe.DINF, pe.stbl(e)) - } - static moof(e, t) { - pe.types || pe.init(); - e = pe.traf(t, e); - return pe.box(pe.types.moof, pe.mfhd(t.sequenceNumber), e) - } - static moov(e) { - let t = e.length; - const i = []; - for (; t--;) i[t] = pe.trak(e[t]); - return pe.box.apply(null, [pe.types.moov, pe.mvhd(e[0].info.timescale, e[0].info.duration)].concat(i).concat(pe.mvex(e))) - } - static mvex(e) { - let t = e.length; - const i = []; - for (; t--;) i[t] = pe.trex(e[t]); - return pe.box(pe.types.mvex, ...i) - } - static mvhd(e, t) { - t *= e; - var i = Math.floor(t / (1 + he)), - t = Math.floor(t % (1 + he)), - t = new Uint8Array([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, e >> 24 & 255, e >> 16 & 255, e >> 8 & 255, 255 & e, i >> 24, i >> 16 & 255, i >> 8 & 255, 255 & i, t >> 24, t >> 16 & 255, t >> 8 & 255, 255 & t, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255]); - return pe.box(pe.types.mvhd, t) - } - static sdtp(e) { - const t = e.samples || [], - i = new Uint8Array(4 + t.length); - let r, n; - for (n = 0; n < t.length; n++) r = t[n].flags, i[n + 4] = r.dependsOn << 4 | r.isDependedOn << 2 | r.hasRedundancy; - return pe.box(pe.types.sdtp, i) - } - static stbl(e) { - var t = pe.stsd(e), - i = pe.box(pe.types.stts, pe.STTS), - r = pe.box(pe.types.stsc, pe.STSC), - n = pe.box(pe.types.stsz, pe.STSZ), - e = pe.box(pe.types.stco, pe.STCO); - return pe.box(pe.types.stbl, t, i, r, n, e) - } - static avc1(e) { - let t, i, r, n = [], - s = []; - var a = e.info.encrypted ? pe.types.encv : pe.types.avc1; - for (t = 0; t < e.config.sps.length; t++) i = e.config.sps[t], r = i.byteLength, n.push(r >>> 8 & 255), n.push(255 & r), n = n.concat(Array.prototype.slice.call(i)); - for (t = 0; t < e.config.pps.length; t++) i = e.config.pps[t], r = i.byteLength, s.push(r >>> 8 & 255), s.push(255 & r), s = s.concat(Array.prototype.slice.call(i)); - var o = pe.box(pe.types.avcC, new Uint8Array([1, n[3], n[4], n[5], 255, 224 | e.config.sps.length].concat(n).concat([e.config.pps.length]).concat(s))), - l = e.config.width, - d = e.config.height, - u = e.config.pixelRatio[0], - c = e.config.pixelRatio[1], - h = e.info.encrypted && e.info.keyTagInfo ? pe.sinf(e.info.keyTagInfo, e.type, pe.types.avc1) : new Uint8Array; - return pe.box(a, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, l >> 8 & 255, 255 & l, d >> 8 & 255, 255 & d, 0, 72, 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, 0, 1, 18, 100, 97, 105, 108, 121, 109, 111, 116, 105, 111, 110, 47, 104, 108, 115, 46, 106, 115, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 17, 17]), o, h, pe.box(pe.types.btrt, new Uint8Array([0, 28, 156, 128, 0, 45, 198, 192, 0, 45, 198, 192])), pe.box(pe.types.pasp, new Uint8Array([u >> 24, u >> 16 & 255, u >> 8 & 255, 255 & u, c >> 24, c >> 16 & 255, c >> 8 & 255, 255 & c]))) - } - static esds(e) { - var t = e.esdsConfig.length; - return new Uint8Array([0, 0, 0, 0, 3, 23 + t, 0, 1, 0, 4, 15 + t, 64, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5].concat([t]).concat(e.esdsConfig).concat([6, 1, 2])) - } - static audioStsd(e) { - var t = e.samplerate; - return new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, e.channelCount, 0, 16, 0, 0, 0, 0, t >> 8 & 255, 255 & t, 0, 0]) - } - static dac3(e) { - e = e.extraData; - return new Uint8Array([e >> 16 & 255, e >> 8 & 255, 255 & e]) - } - static dec3(e) { - return e.extraDataBytes - } - static mp4a(e, t) { - let i = pe.types.mp4a, - r = null; - r = e.encrypted && e.keyTagInfo ? (i = pe.types.enca, pe.sinf(e.keyTagInfo, "audio", pe.types.mp4a)) : new Uint8Array; - e = pe.audioStsd(t), t = pe.box(pe.types.esds, pe.esds(t)); - return pe.box(i, e, t, r) - } - static mp3(e) { - return pe.box(pe.types[".mp3"], pe.audioStsd(e)) - } - static ac3(e, t) { - let i = pe.types["ac-3"], - r = null; - return r = e.encrypted && e.keyTagInfo ? (i = pe.types.enca, pe.sinf(e.keyTagInfo, "audio", pe.types["ac-3"])) : new Uint8Array, pe.box(i, pe.audioStsd(t), pe.box(pe.types.dac3, pe.dac3(t)), r) - } - static ec3(e, t) { - let i = pe.types["ec-3"], - r = null; - return r = e.encrypted && e.keyTagInfo ? (i = pe.types.enca, pe.sinf(e.keyTagInfo, "audio", pe.types["ec-3"])) : new Uint8Array, pe.box(i, pe.audioStsd(t), pe.box(pe.types.dec3, pe.dec3(t)), r) - } - static stsd(e) { - if ("audio" !== e.type) return pe.box(pe.types.stsd, pe.STSD, pe.avc1(e)); - if ("mp3" === e.config.segmentCodec && "mp3" === e.config.codec) return pe.box(pe.types.stsd, pe.STSD, pe.mp3(e.config)); - if ("ac3" === e.config.segmentCodec) return pe.box(pe.types.stsd, pe.STSD, pe.ac3(e.info, e.config)); - if ("ec3" === e.config.segmentCodec) return pe.box(pe.types.stsd, pe.STSD, pe.ec3(e.info, e.config)); - if ("aac" === e.config.segmentCodec) return pe.box(pe.types.stsd, pe.STSD, pe.mp4a(e.info, e.config)); - throw `unknown segmentCodec ${e.config.segmentCodec}` - } - static tkhd(e) { - var t = e.info.id, - i = e.info.duration * e.info.timescale, - r = Math.floor(i / (1 + he)), - i = Math.floor(i % (1 + he)); - let n = 0, - s = 0; - return "video" === e.type && (n = e.config.width, s = e.config.height), pe.box(pe.types.tkhd, new Uint8Array([1, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 3, t >> 24 & 255, t >> 16 & 255, t >> 8 & 255, 255 & t, 0, 0, 0, 0, r >> 24, r >> 16 & 255, r >> 8 & 255, 255 & r, i >> 24, i >> 16 & 255, i >> 8 & 255, 255 & i, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 0, 0, n >> 8 & 255, 255 & n, 0, 0, s >> 8 & 255, 255 & s, 0, 0])) - } - static traf(e, t) { - var i = pe.senc(e), - r = pe.sdtp(e), - n = i.boxData, - s = n.length ? pe.saio(76) : new Uint8Array, - a = n.length ? pe.saiz(i.defaultSampleInfoSize, i.sampleInfoSizes) : new Uint8Array, - o = pe.sbgp(e), - l = pe.sgpd(e), - d = e.id, - i = Math.floor(t / (1 + he)), - t = Math.floor(t % (1 + he)); - return pe.box(pe.types.traf, pe.box(pe.types.tfhd, new Uint8Array([0, 2, 0, 0, d >> 24, d >> 16 & 255, d >> 8 & 255, 255 & d])), pe.box(pe.types.tfdt, new Uint8Array([1, 0, 0, 0, i >> 24, i >> 16 & 255, i >> 8 & 255, 255 & i, t >> 24, t >> 16 & 255, t >> 8 & 255, 255 & t])), n, s, a, o, l, pe.trun(e, r.length + n.length + o.length + l.length + s.length + a.length + 16 + 20 + 8 + 16 + 8 + 8), r) - } - static trak(e) { - if ("trakData" in e) return e.trakData; - e.info.duration = e.info.duration || 4294967295; - var t = pe.types.trak, - i = pe.tkhd(e), - e = pe.mdia(e); - return pe.box(t, i, e) - } - static trex(e) { - e = e.info.id; - return pe.box(pe.types.trex, new Uint8Array([0, 0, 0, 0, e >> 24, e >> 16 & 255, e >> 8 & 255, 255 & e, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1])) - } - static trun(e, t) { - const i = e.samples || [], - r = i.length, - n = 12 + 16 * r, - s = new Uint8Array(n); - let a, o, l, d, u, c; - for (t += 8 + n, s.set([0, 0, 15, 1, r >>> 24 & 255, r >>> 16 & 255, r >>> 8 & 255, 255 & r, t >>> 24 & 255, t >>> 16 & 255, t >>> 8 & 255, 255 & t], 0), a = 0; a < r; a++) l = (o = i[a]).duration, d = o.size, u = o.flags, c = o.cts, s.set([l >>> 24 & 255, l >>> 16 & 255, l >>> 8 & 255, 255 & l, d >>> 24 & 255, d >>> 16 & 255, d >>> 8 & 255, 255 & d, u.isLeading << 2 | u.dependsOn, u.isDependedOn << 6 | u.hasRedundancy << 4 | u.paddingValue << 1 | u.isNonSync, 61440 & u.degradPrio, 15 & u.degradPrio, c >>> 24 & 255, c >>> 16 & 255, c >>> 8 & 255, 255 & c], 12 + 16 * a); - return pe.box(pe.types.trun, s) - } - static initSegment(e) { - pe.types || pe.init(); - const t = pe.moov(e), - i = new Uint8Array(pe.FTYP.byteLength + t.byteLength); - return i.set(pe.FTYP), i.set(t, pe.FTYP.byteLength), i - } - static saio(e) { - e = e + 4 + 4; - return pe.box(pe.types.saio, new Uint8Array([0, 0, 0, 0, 0, 0, 0, 1, e >> 24 & 255, e >> 16 & 255, e >> 8 & 255, 255 & e])) - } - static saiz(e, t) { - ne(e) || (e = 0); - var i = t.length, - t = 0 === e ? new Uint8Array(t) : new Uint8Array; - return pe.box(pe.types.saiz, new Uint8Array([0, 0, 0, 0, e, i >> 24 & 255, i >> 16 & 255, i >> 8 & 255, 255 & i]), t) - } - static senc(e) { - const t = e.samples || [], - i = t.length; - let r = 0, - n = NaN, - s = !0; - const a = []; - if (!e.encrypted || i <= 0) return { - boxData: new Uint8Array, - sampleInfoSizes: a, - defaultSampleInfoSize: 0 - }; - e = e.defaultPerSampleIVSize || 0; - for (const d of t) d.subsamples && (r += d.subsamples.length); - if (r <= 0) return { - boxData: new Uint8Array, - sampleInfoSizes: a, - defaultSampleInfoSize: 0 - }; - const o = new Uint8Array(2 * i + i * e + 6 * r + 4); - let l = this.set32(i, o, 0); - for (const d of t) { - const t = d.subsamples || []; - let e = 2; - d.iv && (o.set(d.iv, l), l += d.iv.byteLength, e += d.iv.byteLength), l = this.set16(t.length, o, l); - for (const d of t) l = this.set16(d[0], o, l), l = this.set32(d[1], o, l), e += 6; - a.push(e), ne(n) || (n = e), s = s && n === e, n = e - } - return { - boxData: pe.box(pe.types.senc, new Uint8Array([0, 0, 0, 2]), o), - defaultSampleInfoSize: s ? n : 0, - sampleInfoSizes: a - } - } - static sinf(e, t, i) { - return pe.box(pe.types.sinf, pe.frma(i), pe.schm(), pe.schi(e, t)) - } - static frma(e) { - return pe.box(pe.types.frma, new Uint8Array(e)) - } - static schm() { - return pe.box(pe.types.schm, new Uint8Array([0, 0, 0, 0, 99, 98, 99, 115, 0, 1, 0, 0])) - } - static schi(e, t) { - return pe.box(pe.types.schi, pe.tenc(e, t)) - } - static tenc(e, t) { - let i = 0; - "video" === t && (i = 25); - const r = new Uint8Array(17); - if (r[0] = 16, e.iv && 16 === e.iv.byteLength && r.set(e.iv, 1), !e.keyId) throw "tenc: no key id found in decryptdata"; - return pe.box(pe.types.tenc, new Uint8Array([1, 0, 0, 0, 0, i, 1, 0]), e.keyId, r) - } - static sbgp(e) { - if (!e.encrypted || 0 === e.samples.length || !e.samples[0].keyTagInfo) return new Uint8Array; - e = e.samples.length; - return pe.box(pe.types.sbgp, new Uint8Array([0, 0, 0, 0]), new Uint8Array(pe.types.seig), new Uint8Array([0, 0, 0, 1, e >> 24 & 255, e >> 16 & 255, e >> 8 & 255, 255 & e, 0, 1, 0, 1])) - } - static sgpd(e) { - if (!e.encrypted || 0 === e.samples.length || !e.samples[0].keyTagInfo) return new Uint8Array; - var t = e.samples[0].keyTagInfo; - let i = 0; - "video" === e.type && (i = 25); - const r = new Uint8Array(17); - if (r[0] = 16, t.iv && r.set(t.iv, 1), !t.keyId) throw "sgpd: no keyid in decryptdata"; - return pe.box(pe.types.sgpd, new Uint8Array([1, 0, 0, 0]), new Uint8Array(pe.types.seig), new Uint8Array([0, 0, 0, 37, 0, 0, 0, 1]), new Uint8Array([0, i, 1, 0]), t.keyId, r) - } - static pssh(e, t, i) { - if (pe.types || pe.init(), !e) throw new TypeError("Bad system id"); - if (16 !== e.byteLength) throw new RangeError("Invalid system id"); - let r, n, s; - if (t) { - r = 1, n = new Uint8Array(16 * t.length); - for (let e = 0; e < t.length; e++) { - const i = t[e]; - if (16 !== i.byteLength) throw new RangeError("Invalid key"); - n.set(i, 16 * e) - } - } else r = 0, n = new Uint8Array; - 0 < r ? (s = new Uint8Array(4), 0 < t.length && new DataView(s.buffer).setUint32(0, t.length, !1)) : s = new Uint8Array; - var a = new Uint8Array(4); - return i && 0 < i.byteLength && new DataView(a.buffer).setUint32(0, i.byteLength, !1), pe.box(pe.types.pssh, new Uint8Array([r, 0, 0, 0]), e, s, n, a, i || new Uint8Array) - } - } - var fe, me, ge = pe; - (vi = fe = fe || {})[vi.SDR = 0] = "SDR", vi[vi.HDR = 1] = "HDR", vi[vi.HDR10 = 2] = "HDR10", vi[vi.DolbyVision = 3] = "DolbyVision", vi[vi.HLG = 4] = "HLG", (bi = me = me || {})[bi.H264 = 16] = "H264", bi[bi.HEVC = 64] = "HEVC", bi[bi.VP09 = 65] = "VP09"; - const ye = new Set(["ac-3", "mp4a.a5", "mp4a.A5"]), - ve = new Set(["ec-3", "mp4a.a6", "mp4a.A6"]), - Se = { - aac: 1024, - mp3: 1024, - ac3: 1536, - ec3: 1536 - }, - be = { - isAC3: e => Boolean(e && ye.has(e)), - isEC3: e => Boolean(e && ve.has(e)), - isDolbyAtmos(e, t) { - const i = t.split("/"); - return Boolean(be.isEC3(e) && 1 < i.length && i[1].split(",").find(e => "JOC" === e)) - }, - isAAC(e) { - return Boolean(e && ("aac" === e || null !== (e = e.match(/^mp4a\.40\.(.*)/)) && "34" !== e[1])) - }, - isMP3(e) { - return Boolean(e && ("mp3" === e || null !== (e = e.match(/^mp4a\.40\.(.*)/)) && "34" === e[1])) - }, - isAVC: e => Boolean(e && e.match(/^avc[13]\.(.*)/)), - isXHEAAC: function(e) { - return Boolean("mp4a.40.42" === e) - }, - isALAC: function(e) { - return Boolean("alac" === e) - }, - isFLAC: function(e) { - return Boolean("fLaC" === e) - }, - isHEVC: e => Boolean(e && e.match(/^(hev|hvc)1\..*/)), - isDolby: e => Boolean(e && e.match(/^dv(h1|he|a1|av)\..*/)), - isVP09: e => Boolean(e && e.match(/^vp09\..*/)), - isCompatibleCodecString(e, t) { - const i = e.split(","), - r = t.split(","), - n = i.filter(e => be.isVideoCodec(e)), - s = r.filter(e => be.isVideoCodec(e)), - a = i.filter(e => be.isAudioCodec(e)), - o = r.filter(e => be.isAudioCodec(e)), - l = 0 === n.length && 0 === s.length || n.length === s.length && be.isCompatibleVideoCodec(n[0], s[0]), - d = 0 === a.length && 0 === o.length || a.length === o.length && be.isCompatibleAudioCodec(a[0], o[0]); - return l && d - }, - isVideoCodec: e => be.isAVC(e) || be.isDolby(e) || be.isHEVC(e) || be.isVP09(e), - isAudioCodec: e => be.isAC3(e) || be.isEC3(e) || be.isAAC(e) || be.isMP3(e), - isCompatibleVideoCodec: (e, t) => Boolean(e && t && (e === t || be.isDolby(e) && be.isDolby(t) || be.isHEVC(e) && be.isHEVC(t) || be.isAVC(e) && be.isAVC(t) || be.isVP09(e) && be.isVP09(t))), - isCompatibleAudioCodec: (e, t) => Boolean(e && t && (e === t || be.isAAC(e) && be.isAAC(t) || be.isAC3(e) && be.isAC3(t) || be.isEC3(e) && be.isEC3(t) || be.isMP3(e) && be.isMP3(t))), - getSegmentCodec(e) { - let t; - if (be.isAAC(e)) t = "aac"; - else if (be.isAC3(e)) t = "ac3"; - else if (be.isEC3(e)) t = "ec3"; - else { - if ("mp3" !== e) throw new Error(`invalid audio config, codec ${e}`); - t = "mp3" - } - return t - }, - getChannelCount(e) { - if (!e) return 0; - e = e.split("/"), e = parseInt(e[0]); - return ne(e) ? e : 0 - }, - avc1toavcoti(e) { - var t; - const i = e.split("."); - let r; - return 2 < i.length ? (r = i.shift() + ".", r += parseInt(null !== (t = i.shift()) && void 0 !== t ? t : "").toString(16), r += ("000" + parseInt(null !== (t = i.shift()) && void 0 !== t ? t : "").toString(16)).substr(-4)) : r = e, r - }, - getDynamicRangeType(e, t) { - let i = fe.SDR; - return "PQ" === e && be.isDolby(t) ? i = fe.DolbyVision : "PQ" === e && (be.isHEVC(t) || be.isVP09(t)) ? i = fe.HDR10 : "HLG" !== e || -1 === t.indexOf("hvc1") && !be.isVP09(t) || (i = fe.HLG), i - }, - getCompressionType(e) { - let t = me.H264; - return be.isHEVC(e) || be.isDolby(e) ? t = me.HEVC : be.isVP09(e) && (t = me.VP09), t - }, - isHigherCodecByFamily(e, t) { - if (!e) return !0; - const i = e.split("."), - r = t.split("."); - if (i[0] !== r[0]) throw new Error(`mismatch in codec family current/new: ${i[0]}/${r[0]}`); - switch (i[0]) { - case "avc1": - case "avc3": - return r[1] > i[1]; - case "vp09": - return e < t; - case "hvc1": - case "hev1": - var n = "H" === i[3].substring(0, 1) ? 1 : 0, - s = i[3].substring(1), - a = "H" === r[3].substring(0, 1) ? 1 : 0, - o = r[3].substring(1); - return r[1] > i[1] || r[2] > i[2] || n < a || s < o; - case "dvh1": - return r[1] > i[1] || r[2] > i[2] - } - } - }; - class Te { - static getTrack(e, t, i, r, n) { - let s; - switch (be.getSegmentCodec(i)) { - case "aac": - var a; - (a = E(e, 1 === r ? new Uint8Array([255, 241, 92, 64, 1, 127, 252]) : new Uint8Array([255, 241, 92, 128, 1, 191, 252]), 0, i)) && (s = { - type: "audio", - info: { - id: t, - timescale: a.samplerate, - duration: 0, - encrypted: !1, - keyTagInfo: void 0 - }, - config: a - }); - break; - case "ac3": - case "ec3": { - const i = j(e, new Uint8Array([11, 119, 69, 17, 128, 64, 47, 132]), 0); - i && (s = { - type: "audio", - info: { - id: t, - timescale: i.samplerate, - duration: 0, - encrypted: !1, - keyTagInfo: void 0 - }, - config: i - }) - } - } - return s - } - static getSample(e, t) { - let i; - switch (e) { - case "mp4a.40.2": - case "mp4a.40.5": - i = 1 === t ? new Uint8Array([0, 208, 0, 7]) : new Uint8Array([33, 0, 3, 64, 104, 28]); - break; - case "ac-3": - case "ec-3": - i = new Uint8Array([11, 119, 69, 17, 128, 64, 47, 132, 41, 3, 253, 214, 124, 253, 243, 215, 233, 95, 185, 123, 78, 20, 40, 106, 97, 190, 74, 253, 43, 218, 208, 140, 191, 176, 144, 120, 214, 181, 44, 124, 129, 251, 91, 109, 187, 109, 198, 225, 43, 172, 116, 140, 176, 123, 38, 144, 211, 247, 225, 64, 29, 53, 175, 96, 16, 57, 121, 87, 78, 203, 81, 37, 7, 72, 228, 132, 37, 169, 38, 231, 97, 229, 247, 194, 208, 8, 12, 83, 74, 139, 137, 17, 22, 26, 221, 203, 107, 113, 94, 93, 75, 33, 208, 247, 146, 105, 39, 143, 6, 36, 1, 227, 108, 70, 11, 180, 152, 218, 182, 218, 209, 59, 85, 104, 201, 70, 37, 82, 219, 68, 55, 225, 144, 99, 149, 0, 119, 26, 14, 69, 164, 241, 204, 222, 81, 177, 142, 80, 20, 100, 97, 143, 101, 221, 140, 113, 31, 208, 124, 25, 64, 29, 49, 77, 140, 30, 155, 74, 214, 204, 138, 229, 109, 172, 95, 130, 70, 230, 134, 88, 59, 179, 212, 155, 232, 0, 0, 0, 0, 0, 173, 234]) - } - return i - } - static getSegment(e, i, r, n) { - if (e) { - var s = e.info["timescale"], - a = e.config["segmentCodec"], - o = Te.getSample(e.config.codec, e.config.channelCount); - if (o) { - const l = [], - d = { - id: e.info.id, - sequenceNumber: i, - type: "audio", - encrypted: !1, - samples: l, - defaultPerSampleIVSize: 0 - }, - u = Se[a], - c = Math.ceil(n * s / u), - h = { - baseTime: Math.round(c * u + r), - timescale: s - }; - let t = 0; - const p = c * o.byteLength + 8, - f = new Uint8Array(p); - f[0] = p >> 24 & 255, f[1] = p >> 16 & 255, f[2] = p >> 8 & 255, f[3] = 255 & p, ge.types || ge.init(), f.set(ge.types.mdat, 4), t += 8; - for (let e = 0; e < c; e++) l.push({ - duration: u, - size: o.byteLength, - cts: 0, - flags: { - isLeading: 0, - isDependedOn: 0, - hasRedundancy: 0, - degradPrio: 0, - dependsOn: 1, - isNonSync: 0, - paddingValue: 0 - } - }), f.set(o, t), t += o.byteLength; - const m = ge.moof(r, d), - g = new Uint8Array(m.byteLength + f.byteLength); - return g.set(m), g.set(f, m.byteLength), { - silentFragData: g, - endTs: h - } - } - } - } - } - class Ee extends h { - constructor(e, t, i, r, n) { - super(e, t, n), this.typeSupported = i, this.isVideoContiguous = !1, this.logger = n.child({ - name: "EsRemuxer" - }); - const s = navigator.userAgent; - this.isSafari = r && -1 < r.indexOf("Apple") && s && !s.match("CriOS") - } - resetTimeStamp(e) { - this._initPTS = this._initDTS = e - } - resetInitSegment() { - this.currentInitTrack = void 0, this._silentAudioTrack = void 0 - } - remuxEsTracks(r, n, s, a, o, l, d, u, c, h) { - let p; - l || (this.isVideoContiguous = !1); - c = void 0 === c ? o : c; - if (!this.currentInitTrack) { - if (r && r.config.codec && (this._audioTrackInfo = { - id: r.info.id, - codec: r.config.codec, - channelCount: r.config.channelCount - }), n && h && this._audioTrackInfo) { - const r = Te.getTrack(this.observer, this._audioTrackInfo.id, this._audioTrackInfo.codec, this._audioTrackInfo.channelCount, this.logger); - if (r) { - this._silentAudioTrack = Object.assign(Object.assign({}, r), { - info: Object.assign(Object.assign({}, r.info), { - inputTimescale: 9e4 - }), - parsingData: { - len: 0, - sequenceNumber: 0, - esSamples: [] - } - }); - const s = this._initPTS + Math.round(c * this._silentAudioTrack.info.timescale); - p = Te.getSegment(this._silentAudioTrack, n.parsingData.sequenceNumber, s, h), n.parsingData.sequenceNumber++ - } - } else this._silentAudioTrack = void 0; - this.updateInitPTSDTS(n, r, o), this.generateIS(this._silentAudioTrack || r, n) - } - if (this.currentInitTrack) { - const f = n && n.parsingData.esSamples.length, - m = this.isVideoContiguous; - let t, i, e; - if (n && h && this._silentAudioTrack && !p) { - const r = this._initPTS + Math.round((c + this.config.audioPrimingDelay) * this._silentAudioTrack.info.timescale); - p = Te.getSegment(this._silentAudioTrack, n.parsingData.sequenceNumber, r, h) - } - if (r && r.parsingData.esSamples.length) { - if (ne(r.info.timescale) || (this.logger.warn("regenerate InitSegment as audio detected"), this.updateInitPTSDTS(n, r, o), this.generateIS(r, n)), t = this.remuxAudio(r, c, l, d, u), f) { - let e; - t && (e = S(t.endPTS) - S(t.startPTS)), ne(n.info.timescale) || (this.logger.warn("regenerate InitSegment as video detected"), this.updateInitPTSDTS(n, r, o), this.generateIS(r, n)), i = this.remuxVideo(n, c, m, e, h) - } - } else f && (i = this.remuxVideo(n, c, m, void 0, h)), i && r && r.config.codec && (t = this.remuxEmptyAudio(r, c, l, d, i, u)); - p ? e = { - data1: i.data1, - data2: p.silentFragData, - startDTS: i.startDTS, - startPTS: i.startPTS, - endDTS: y(i.endDTS, p.endTs), - endPTS: y(i.endPTS, p.endTs), - type: "audiovideo", - track: this.currentInitTrack - } : i && t ? e = { - data1: i.data1, - data2: t.data1, - startDTS: g(i.startDTS, t.startDTS), - startPTS: g(i.startPTS, t.startPTS), - endDTS: y(i.endDTS, t.endDTS), - endPTS: y(i.endPTS, t.endPTS), - type: "audiovideo", - track: this.currentInitTrack, - dropped: i.dropped, - framesWithoutIDR: i.framesWithoutIDR, - firstKeyframePts: i.firstKeyframePts - } : i ? e = { - data1: i.data1, - startDTS: i.startDTS, - startPTS: i.startPTS, - endDTS: i.endDTS, - endPTS: i.endPTS, - type: "video", - track: this.currentInitTrack, - dropped: i.dropped, - framesWithoutIDR: i.framesWithoutIDR, - firstKeyframePts: i.firstKeyframePts - } : t ? e = { - data1: t.data1, - startDTS: t.startDTS, - startPTS: t.startPTS, - endDTS: t.endDTS, - endPTS: t.endPTS, - type: "audio", - track: this.currentInitTrack - } : this.logger.error("Missing video and audio data"), a && a.captionSamples.length && this.remuxText(a, e), null !== (a = null == s ? void 0 : s.id3Samples) && void 0 !== a && a.length && this.remuxID3(s, e), this.observer.trigger(v.FRAG_PARSING_DATA, e) - } else this.logger.error("failed to generate IS"); - this.observer.trigger(v.FRAG_PARSED) - } - updateInitPTSDTS(e, t, i) { - let r = 1 / 0, - n = 1 / 0; - var s = e ? e.parsingData.esSamples : [], - a = t ? t.parsingData.esSamples : []; - if (!ne(this._initPTS)) { - if (t && a.length && (r = n = a[0].pts - t.info.inputTimescale * i), e && s.length) { - const t = e.info.inputTimescale; - e.info.timescale = t, r = Math.min(r, s.reduce((e, t) => { - var i = t.pts - e; - return i < -4294967296 ? Ie(e, t.pts) : 0 < i ? e : t.pts - }, s[0].pts) - t * i), n = Math.min(n, s[0].dts - t * i), this.observer.trigger(v.INIT_PTS_FOUND, { - initPTS: B(r, t) - }) - } - if (ne(r) && ne(n)) this._initPTS = r, this._initDTS = n; - else { - const e = new D(!1, "invalid initPTS or initDTS", $.InvalidInitTimestamp); - this.observer.trigger(x.INTERNAL_ERROR, e) - } - } - } - generateIS(e, t) { - const i = t ? t.parsingData.esSamples : [], - r = this.typeSupported; - let n, s = "audio/mp4"; - if (e && t && i.length) { - const i = t.info.inputTimescale; - t.info.timescale = i, e.info.timescale = e.config.samplerate; - const r = pe.initSegment([t, e]); - n = { - type: "audiovideo", - container: "video/mp4", - codec: `${t.config.codec},${e.config.codec}`, - initSegment: r - } - } else if (e) { - "mp3" === (e.info.timescale = e.config.samplerate, e.config.segmentCodec) && (r.mpeg ? (s = "audio/mpeg", e.config.codec = "") : r.mp3 && (e.config.codec = "mp3")); - const t = "mp3" === e.config.segmentCodec && r.mpeg ? new Uint8Array : pe.initSegment([e]); - n = { - type: "audio", - container: s, - codec: e.config.codec, - initSegment: t - } - } else if (t && i.length) { - const e = t.info.inputTimescale; - t.info.timescale = e; - const i = pe.initSegment([t]); - n = { - type: "video", - container: "video/mp4", - codec: t.config.codec, - initSegment: i - } - } - if (n) { - this.currentInitTrack = n; - const e = { - track: n - }; - this.observer.trigger(v.FRAG_PARSING_INIT_SEGMENT, e) - } else { - const e = new D(!1, "no audio/video samples found", $.NoAVSamplesFound); - this.observer.trigger(x.INTERNAL_ERROR, e) - } - } - remuxVideo(n, e, s, a, o) { - let l, d, u, c, h, t, p = 8, - i = n.parsingData.dropped; - const r = !s && this.config.forceKeyFrameOnDiscontinuity, - f = n.parsingData.esSamples, - m = n.info.inputTimescale, - g = [], - y = n.info.encrypted; - let v, S; - v = s ? this.nextAvcDts : Ie(f[0].dts, f[0].pts), f.forEach(function(e) { - e.pts = Ie(e.pts, v), e.dts = Ie(e.dts, v) - }), f.sort(function(e, t) { - var i = e.dts - t.dts, - r = e.pts - t.pts; - return i || r || e.id - t.id - }); - var b = f.findIndex(e => e.key); - f[b] && (S = f[b].pts), r && (0 < b ? (this.logger.warn(`Dropped ${b} out of ${f.length} video samples due to a missing keyframe`), f.splice(0, b), i += b) : -1 === b && (this.logger.warn(`No keyframe found out of ${f.length} video samples`), i += f.length)); - var T = f[0], - E = f[f.length - 1], - I = f.reduce((e, t) => Math.max(Math.min(e, t.pts - t.dts), -18e3), 0); - if (I < 0) { - this.logger.warn(`PTS < DTS detected in video samples, shifting DTS by ${Math.round(I/90)} ms to overcome this issue`); - for (let e = 0; e < f.length; e++) f[e].dts += I - } - var w = this.isSafari; - if (l = Math.round((E.dts - T.dts) / (f.length - 1)), c = Math.max(T.dts, 0), u = Math.max(T.pts, 0), ne(o) && (c = e * m, u = e * m), s) { - const n = c - v, - M = n > l, - s = n < -1; - (M || s) && (M ? this.logger.warn(`AVC: ${n}/90000 hole between fragments detected`) : this.logger.warn(`AVC: ${n}/90000 overlapping between fragments detected`)) - } - let A = 0, - O = 0; - var k = f.length; - for (let e = 0; e < k; e++) { - const M = f[e], - s = M.units, - a = s.length; - let t = 0; - for (let e = 0; e < a; e++) t += s[e].data.length; - O += t, A += a, M.length = t, M.dts = w ? c + e * l : Math.max(M.dts, c), M.pts = Math.max(M.pts, M.dts) - } - t = Math.max(E.dts, 0), h = Math.max(E.pts, 0, t), ne(o) && (t = e * m, h = e * m); - e = O + 4 * A + 8; - try { - d = new Uint8Array(e) - } catch (n) { - const M = new F(!1, `fail allocating video mdat ${e}`, $.FailedToAllocateVideoMdat, e); - return void this.observer.trigger(x.INTERNAL_ERROR, M) - } - const C = new DataView(d.buffer); - C.setUint32(0, e), d.set(pe.types.mdat, 4); - for (let t = 0; t < k; t++) { - const M = f[t], - s = M.units; - let e, i = 0; - const x = []; - let r = 0; - for (let e = 0, t = s.length; e < t; e++) { - const M = s[e], - a = M.data, - o = M.data.byteLength; - if (C.setUint32(p, o), p += 4, d.set(a, p), p += o, i += 4 + o, y) - if (o <= 48 || 1 !== M.type && 5 !== M.type) r += 4 + o; - else { - let e = o - 32; - e % 16 == 0 && (e -= 16), x.push([r + 36, e]), r = o - 32 - e - } - } - if (0 < r && x.push([r, 0]), w) e = Math.max(0, l * Math.round((M.pts - M.dts) / l)); - else { - if (t < k - 1) l = f[t + 1].dts - M.dts; - else { - const s = this.config, - o = M.dts - f[0 < t ? t - 1 : t].dts; - if (s.stretchShortVideoTrack) { - const n = s.maxBufferHole, - d = s.maxSeekHole, - c = Math.floor(Math.min(n, d) * m), - h = (a ? u + a * m : this.nextAudioPts) - M.pts; - h > c ? (l = h - o, l < 0 && (l = o)) : l = o - } else l = o - } - e = Math.round(M.pts - M.dts) - } - ne(o) && (e = 0, l = o * m), g.push({ - size: i, - duration: l, - cts: e, - flags: { - isLeading: 0, - isDependedOn: 0, - hasRedundancy: 0, - degradPrio: 0, - dependsOn: M.key ? 2 : 1, - isNonSync: M.key ? 0 : 1, - paddingValue: 0 - }, - keyTagInfo: M.keyTagInfo, - subsamples: x - }) - } - if (this.nextAvcDts = t + l, this.isVideoContiguous = !0, g.length && -1 < navigator.userAgent.toLowerCase().indexOf("chrome")) { - const n = g[0].flags; - n.dependsOn = 2, n.isNonSync = 0 - } - e = { - sequenceNumber: n.parsingData.sequenceNumber++, - id: n.info.id, - type: n.type, - encrypted: n.info.encrypted, - samples: g, - defaultPerSampleIVSize: 0 - }, e = pe.moof(c + this.config.audioPrimingDelay * m, e); - n.parsingData.esSamples = []; - const D = new Uint8Array(e.byteLength + d.byteLength); - return D.set(e), D.set(d, e.byteLength), { - data1: D, - startPTS: B(u / m, m), - endPTS: B((h + l) / m, m), - startDTS: B(c / m, m), - endDTS: B(this.nextAvcDts / m, m), - type: "video", - dropped: i, - framesWithoutIDR: b, - firstKeyframePts: B(S / m, m) - } - } - remuxAudio(r, i, n, e, s) { - const a = r.info.inputTimescale, - o = a / r.info.timescale, - l = ("aac" === r.config.segmentCodec ? 1024 : "mp3" === r.config.segmentCodec ? 1152 : 1536) * o, - d = "mp3" === r.config.segmentCodec && this.typeSupported.mpeg, - u = [], - c = r.info.encrypted, - t = this._initPTS + i * a; - let h, p, f, m, g, y, v, S, b, T, E, I, w, A, O = d ? 0 : 8; - const k = new q, - C = r.parsingData.esSamples; - if (A = this.nextAudioPts, (n = n || C.length && A && (e && Math.abs(t - A) < 9e3 || Math.abs(Ie(C[0].pts - A, t)) < 20 * l)) || (A = Ie(C[0].pts, this._initPTS)), C.forEach(function(e) { - e.pts = e.dts = Ie(e.pts, A) - }), e && "aac" === r.config.segmentCodec) - for (let t = 0, i = A; t < C.length;) { - const M = C[t]; - T = M.pts; - const o = T - i, - d = Math.abs(1e3 * o / a); - if (o <= -l) this.logger.warn(`Dropping 1 audio frame @ ${(i/a).toFixed(3)}s due to ${d} ms overlap.`), C.splice(t, 1), r.parsingData.len -= M.unit.length; - else if (o >= l && d < 1e4 && i) { - const d = Math.round(o / l); - this.logger.warn(`Injecting ${d} audio frame @ ${(i/a).toFixed(3)}s due to ${Math.round(1e3*o/a)} ms gap.`); - for (let e = 0; e < d; e++) w = Math.max(i, 0), I = re(r.config.codec, r.config.channelCount), I || (this.logger.warn("Unable to get silent frame for given audio codec; duplicating last frame instead."), I = M.unit.subarray(0)), C.splice(t, 0, { - unit: I, - pts: w, - dts: w, - keyTagInfo: s - }), r.parsingData.len += I.length, i += l, t += 1; - M.pts = M.dts = i, i += l, t += 1 - } else i += l, M.pts = M.dts = 0 === t ? A : C[t - 1].pts + l, t += 1 - } - for (let e = 0, t = C.length; e < t; e++) { - if (p = C[e], m = p.unit, T = p.pts, E = p.dts, void 0 !== b) f.duration = Math.round((E - b) / o); - else { - const i = Math.round(1e3 * (T - A) / a); - let t = 0; - if (n && "aac" === r.config.segmentCodec && i) { - if (0 < i && i < 1e4) t = Math.round((T - A) / l), 0 < t && (I = re(r.config.codec, r.config.channelCount), I = I || m.subarray(0), r.parsingData.len += t * I.length); - else if (i < -12) { - r.parsingData.len -= m.byteLength; - continue - } - T = E = A - } - if (v = Math.max(0, T), S = Math.max(0, E), !(0 < r.parsingData.len)) return; { - const i = d ? r.parsingData.len : r.parsingData.len + 8; - try { - g = new Uint8Array(i) - } catch (r) { - const n = new F(!1, `fail allocating audio mdat ${i}`, $.FailedToAllocateAudioMdat, i); - return void this.observer.trigger(x.INTERNAL_ERROR, n) - } - d || (h = new DataView(g.buffer), h.setUint32(0, i), g.set(pe.types.mdat, 4)) - } - for (let e = 0; e < t; e++) w = T - (t - e) * l, I = re(r.config.codec, r.config.channelCount), I || (this.logger.warn("Unable to get silent frame for given audio codec; duplicating this frame instead."), I = m.subarray(0)), g.set(I, O), O += I.byteLength, f = { - size: I.byteLength, - cts: 0, - duration: 1024, - flags: { - isLeading: 0, - isDependedOn: 0, - hasRedundancy: 0, - degradPrio: 0, - dependsOn: 1, - paddingValue: 0, - isNonSync: 0 - }, - keyTagInfo: p.keyTagInfo, - subsamples: c ? [ - [I.byteLength, 0] - ] : [] - }, u.push(f) - } - g.set(m, O); - const M = m.byteLength; - O += M; - const s = []; - if (c) - if ("ec3" === r.config.segmentCodec) { - let e = 0; - for (; e < m.byteLength;) { - const i = 2 * (k.bsReadAndUpdate(m, { - byteOffset: e + 2, - usedBits: 5 - }, 11) + 1); - e += i; - const n = Math.min(i, 16); - s.push([n, i - n]) - } - } else { - const r = Math.min(M, 16); - s.push([r, M - r]) - } f = { - size: M, - cts: 0, - duration: 0, - flags: { - isLeading: 0, - isDependedOn: 0, - hasRedundancy: 0, - degradPrio: 0, - dependsOn: 1, - paddingValue: 0, - isNonSync: 0 - }, - keyTagInfo: p.keyTagInfo, - subsamples: s - }, u.push(f), b = E - } - let D = 0; - e = u.length; - if (2 <= e && (D = u[e - 2].duration, f.duration = D), e) { - if (this.nextAudioPts = T + o * D, r.parsingData.len = 0, d) y = new Uint8Array; - else { - const i = { - sequenceNumber: r.parsingData.sequenceNumber++, - id: r.info.id, - type: r.type, - encrypted: r.info.encrypted, - samples: u, - defaultPerSampleIVSize: 0 - }; - y = pe.moof((S + this.config.audioPrimingDelay * a) / o, i) - } - const i = new Uint8Array(y.byteLength + g.byteLength); - return i.set(y), i.set(g, y.byteLength), r.parsingData.esSamples = [], { - data1: i, - startPTS: B(v / a, a), - endPTS: B(this.nextAudioPts / a, a), - startDTS: B(S / a, a), - endDTS: B((E + o * D) / a, a), - type: "audio" - } - } - return null - } - remuxEmptyAudio(t, e, i, r, n, s) { - var a = t.info.inputTimescale, - o = a / (t.config.samplerate || a), - l = this.nextAudioPts, - d = (void 0 !== l ? l : S(n.startDTS) * a) + this._initDTS, - a = S(n.endDTS) * a + this._initDTS, - u = 1024 * o, - c = Math.ceil((a - d) / u), - h = re(t.config.codec, t.config.channelCount); - if (this.logger.warn("remux empty Audio"), !h) return this.logger.error("Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec!"), null; - const p = []; - for (let e = 0; e < c; e++) { - const i = d + e * u; - p.push({ - unit: h, - pts: i, - dts: i, - keyTagInfo: s - }), t.parsingData.len += h.length - } - return t.parsingData.esSamples = p, this.remuxAudio(t, e, i, r, s) - } - remuxID3(t, e) { - var i = t.id3Samples.length; - let r; - var n = t.inputTimescale; - if (i) { - for (let e = 0; e < i; e++) r = t.id3Samples[e], r.pts = r.pts / n, r.dts = r.dts / n; - e.id3Samples = t.id3Samples - } - t.id3Samples = [] - } - remuxText(t, e) { - t.captionSamples.sort(function(e, t) { - return e.pts - t.pts - }); - var i = t.captionSamples.length; - let r; - var n = t.inputTimescale; - if (i) { - for (let e = 0; e < i; e++) r = t.captionSamples[e], r.pts = r.pts / n; - e.captionData || (e.captionData = {}), e.captionData.ts = t.captionSamples - } - t.captionSamples = [] - } - } - - function Ie(e, t) { - var i; - if (void 0 === t) return e; - for (i = t < e ? -8589934592 : 8589934592; 4294967296 < Math.abs(e - t);) e += i; - return e - } - e = "undefined" != typeof globalThis ? globalThis : "undefined" != typeof window ? window : "undefined" != typeof global ? global : void 0 !== Jy ? Jy : {}; - - function we(e) { - try { - return JSON.stringify(e) - } catch (e) { - return '"[Circular]"' - } - } - - function Ae(e, t, i) { - var r = i && i.stringify || we; - if ("object" == typeof e && null !== e) { - var n = t.length + 1; - if (1 === n) return e; - var s = new Array(n); - s[0] = r(e); - for (var a = 1; a < n; a++) s[a] = r(t[a]); - return s.join(" ") - } - if ("string" != typeof e) return e; - var o = t.length; - if (0 === o) return e; - for (var l = "", d = 0, u = -1, c = e && e.length || 0, h = 0; h < c;) { - if (37 === e.charCodeAt(h) && h + 1 < c) { - switch (u = -1 < u ? u : 0, e.charCodeAt(h + 1)) { - case 100: - if (o <= d) break; - if (u < h && (l += e.slice(u, h)), null == t[d]) break; - l += Number(t[d]), u = h += 2; - break; - case 79: - case 111: - case 106: - if (o <= d) break; - if (u < h && (l += e.slice(u, h)), void 0 === t[d]) break; - var p = typeof t[d]; - if ("string" == p) { - l += "'" + t[d] + "'", u = h + 2, h++; - break - } - if ("function" == p) { - l += t[d].name || "", u = h + 2, h++; - break - } - l += r(t[d]), u = h + 2, h++; - break; - case 115: - if (o <= d) break; - u < h && (l += e.slice(u, h)), l += String(t[d]), u = h + 2, h++; - break; - case 37: - u < h && (l += e.slice(u, h)), l += "%", u = h + 2, h++ - }++d - }++h - } - return -1 === u ? e : (u < c && (l += e.slice(u)), l) - } - var Oe = De; - const ke = function() { - function t(e) { - return void 0 !== e && e - } - try { - return "undefined" != typeof globalThis || Object.defineProperty(Object.prototype, "globalThis", { - get: function() { - return delete Object.prototype.globalThis, this.globalThis = this - }, - configurable: !0 - }), globalThis - } catch (e) { - return t(Jy) || t(window) || t(this) || {} - } - }().console || {}, - Ce = { - mapHttpRequest: Le, - mapHttpResponse: Le, - wrapRequestSerializer: _e, - wrapResponseSerializer: _e, - wrapErrorSerializer: _e, - req: Le, - res: Le, - err: function(e) { - const t = { - type: e.constructor.name, - msg: e.message, - stack: e.stack - }; - for (const i in e) void 0 === t[i] && (t[i] = e[i]); - return t - } - }; - - function De(s) { - (s = s || {}).browser = s.browser || {}; - const a = s.browser.transmit; - if (a && "function" != typeof a.send) throw Error("pino: transmit option must have a send function"); - const e = s.browser.write || ke; - s.browser.write && (s.browser.asObject = !0); - const o = s.serializers || {}, - l = (t = s.browser.serialize, i = o, Array.isArray(t) ? t.filter(function(e) { - return "!stdSerializers.err" !== e - }) : !0 === t && Object.keys(i)); - var t, i; - let r = s.browser.serialize; - Array.isArray(s.browser.serialize) && -1 < s.browser.serialize.indexOf("!stdSerializers.err") && (r = !1), "function" == typeof e && (e.error = e.fatal = e.warn = e.info = e.debug = e.trace = e), !1 === s.enabled && (s.level = "silent"); - const n = s.level || "info", - d = Object.create(e); - d.log || (d.log = Ne), Object.defineProperty(d, "levelVal", { - get: function() { - return "silent" === this.level ? 1 / 0 : this.levels.values[this.level] - } - }), Object.defineProperty(d, "level", { - get: function() { - return this._level - }, - set: function(e) { - if ("silent" !== e && !this.levels.values[e]) throw Error("unknown level " + e); - this._level = e, Me(u, d, "error", "log"), Me(u, d, "fatal", "error"), Me(u, d, "warn", "error"), Me(u, d, "info", "log"), Me(u, d, "debug", "log"), Me(u, d, "trace", "log") - } - }); - const u = { - transmit: a, - serialize: l, - asObject: s.browser.asObject, - levels: ["error", "fatal", "warn", "info", "debug", "trace"], - timestamp: "function" == typeof(i = s).timestamp ? i.timestamp : !1 === i.timestamp ? Fe : Be - }; - return d.levels = De.levels, d.level = n, d.setMaxListeners = d.getMaxListeners = d.emit = d.addListener = d.on = d.prependListener = d.once = d.prependOnceListener = d.removeListener = d.removeAllListeners = d.listeners = d.listenerCount = d.eventNames = d.write = d.flush = Ne, d.serializers = o, d._serialize = l, d._stdErrSerialize = r, d.child = function(t) { - if (!t) throw new Error("missing bindings for child Pino"); - var i, r, e = t.serializers; - - function n(e) { - this._childLevel = 1 + (0 | e._childLevel), this.error = Pe(e, t, "error"), this.fatal = Pe(e, t, "fatal"), this.warn = Pe(e, t, "warn"), this.info = Pe(e, t, "info"), this.debug = Pe(e, t, "debug"), this.trace = Pe(e, t, "trace"), i && (this.serializers = i, this._serialize = r), a && (this._logEvent = Re([].concat(e._logEvent.bindings, t))) - } - return l && e && (i = Object.assign({}, o, e), r = !0 === s.browser.serialize ? Object.keys(i) : l, delete t.serializers, xe([t], r, i, this._stdErrSerialize)), n.prototype = this, new n(this) - }, a && (d._logEvent = Re()), d - } - - function Me(e, t, i, r) { - var n, s, a, o, l = Object.getPrototypeOf(t); - t[i] = !(t.levelVal > t.levels.values[i]) && (l[i] || ke[i] || ke[r]) || Ne, s = t, a = i, !(n = e).transmit && s[a] === Ne || (s[a] = (o = s[a], function() { - const e = n.timestamp(), - t = new Array(arguments.length), - i = Object.getPrototypeOf && Object.getPrototypeOf(this) === ke ? ke : this; - for (var r = 0; r < t.length; r++) t[r] = arguments[r]; - if (n.serialize && !n.asObject && xe(t, this._serialize, this.serializers, this._stdErrSerialize), n.asObject ? o.call(i, function(e, t, i, r) { - e._serialize && xe(i, e._serialize, e.serializers, e._stdErrSerialize); - const n = i.slice(); - let s = n[0]; - const a = {}; - r && (a.time = r), a.level = De.levels.values[t]; - let o = 1 + (0 | e._childLevel); - if (o < 1 && (o = 1), null !== s && "object" == typeof s) { - for (; o-- && "object" == typeof n[0];) Object.assign(a, n.shift()); - s = n.length ? Ae(n.shift(), n) : void 0 - } else "string" == typeof s && (s = Ae(n.shift(), n)); - return void 0 !== s && (a.msg = s), a - }(this, a, t, e)) : o.apply(i, t), n.transmit) { - const o = n.transmit.level || s.level, - i = De.levels.values[o], - r = De.levels.values[a]; - r < i || function(e, t, i) { - const r = t.send, - n = t.ts, - s = t.methodLevel, - a = t.methodValue, - o = t.val, - l = e._logEvent.bindings; - xe(i, e._serialize || Object.keys(e.serializers), e.serializers, void 0 === e._stdErrSerialize || e._stdErrSerialize), e._logEvent.ts = n, e._logEvent.messages = i.filter(function(e) { - return -1 === l.indexOf(e) - }), e._logEvent.level.label = s, e._logEvent.level.value = a, r(s, e._logEvent, o), e._logEvent = Re(l) - }(this, { - ts: e, - methodLevel: a, - methodValue: r, - transmitLevel: o, - transmitValue: De.levels.values[n.transmit.level || s.level], - send: n.transmit.send, - val: s.levelVal - }, t) - } - })) - } - - function xe(e, t, i, r) { - for (const n in e) - if (r && e[n] instanceof Error) e[n] = De.stdSerializers.err(e[n]); - else if ("object" == typeof e[n] && !Array.isArray(e[n])) - for (const r in e[n]) t && -1 < t.indexOf(r) && r in i && (e[n][r] = i[r](e[n][r])) - } - - function Pe(i, r, n) { - return function() { - try{ const e = new Array(1 + arguments.length); - e[0] = r; - for (var t = 1; t < e.length; t++) - e[t] = arguments[t - 1]; - return i[n].apply(this, e) } catch (e){} - } - } - - function Re(e) { - return { - ts: 0, - messages: [], - bindings: e || [], - level: { - label: "", - value: 0 - } - } - } - - function Le() { - return {} - } - - function _e(e) { - return e - } - - function Ne() {} - - function Fe() { - return !1 - } - - function Be() { - return Date.now() - } - let Ue; - - function $e(e = {}) { - return Object.assign(Object.assign({}, e), { - customLevels: Object.assign(Object.assign({}, e.customLevels || {}), { - qe: 35 - }) - }) - } - De.levels = { - values: { - fatal: 60, - error: 50, - warn: 40, - info: 30, - debug: 20, - trace: 10 - }, - labels: { - 10: "trace", - 20: "debug", - 30: "info", - 40: "warn", - 50: "error", - 60: "fatal" - } - }, De.stdSerializers = Ce, De.stdTimeFunctions = Object.assign({}, { - nullTime: Fe, - epochTime: Be, - unixTime: function() { - return Math.round(Date.now() / 1e3) - }, - isoTime: function() { - return new Date(Date.now()).toISOString() - } + if (opts.enabled === false) opts.level = 'silent'; + const level = opts.level || 'info'; + const logger = Object.create(proto); + if (!logger.log) logger.log = noop$2; + + Object.defineProperty(logger, 'levelVal', { + get: getLevelVal }); - const Ve = () => {}; - - function Ke(e, t) { - const i = (e = t in e ? e : console)[t] || e.log; - return i ? i.bind(e) : Ve - } - - function qe(r, n, e) { - var { - time: s, - sessionId: t, - critical: i, - name: a, - msg: o - } = e; - let l = ""; - if ("data" in e) try { - const r = [], - n = []; - l = JSON.stringify(e.data, (e, t) => { - if ("object" == typeof t && null !== t) { - var i = n.indexOf(t); - if (-1 !== i) return `[Circular object reference: '${r[i]}']`; - r.push(e), n.push(t) - } - return t - }) - } catch (r) { - l = `Log serialization error: "${r}"` - } - r(`${function(){const e=new Date(s),t=e.getTimezoneOffset(),i=He(Math.floor(Math.abs(t)/60)),r=He(Math.abs(t)%60);let n=t<=0?"UTC+"+i:"UTC-"+i;return n=r?n+":"+r:n,e.getFullYear()+"-"+He(e.getMonth()+1)+"-"+He(e.getDate())+" "+He(e.getHours())+":"+He(e.getMinutes())+":"+He(e.getSeconds())+"."+(e.getMilliseconds()/1e3).toFixed(3).slice(2,5)+" "+n}()}| [SessionID: ${t}] | [${n}] >${i?" [QE Critical]":""} [${a}] ${o||""} ${l}`) - } - - function He(e) { - return e < 10 ? "0" + e : e.toString() - } - - function je(e, t = 1 / 0) { - if (!e) return ""; - const i = new Uint8Array(e); - let r, n = ""; - for (r = 0; r < i.length && r < t; r++) { - let e = i[r].toString(16); - e.length < 2 && (e = "0" + e), n += e - } - return n - } - const Qe = () => (Ue || (Ue = Oe($e()).child({ - name: "hls" - }), Ue.qe = e => Ue.info(e), Ue.warn("getLogger called without hls object instantiated, returning a logger that is not configured")), Ue), - We = { - bin2str: e => String.fromCharCode.apply(null, Array.from(e)), - readUint16(e, t) { - t = e[t] << 8 | e[t + 1]; - return t < 0 ? 65536 + t : t - }, - readSint32: (e, t) => e[t] << 24 | e[t + 1] << 16 | e[t + 2] << 8 | e[t + 3], - readUint32(e, t) { - t = We.readSint32(e, t); - return t < 0 ? 4294967296 + t : t - }, - writeUint32(e, t, i) { - e[t] = i >> 24, e[t + 1] = i >> 16 & 255, e[t + 2] = i >> 8 & 255, e[t + 3] = 255 & i - }, - readUint64(e, t) { - var i = We.readUint32(e, t); - return i *= Math.pow(2, 32), i += We.readUint32(e, t + 4) - }, - writeUint64(e, t, i) { - var r = Math.pow(2, 32) - 1, - n = Math.floor(i / (1 + r)), - r = Math.floor(i % (1 + r)); - We.writeUint32(e, t, n), We.writeUint32(e, t + 4, r) - }, - findBox(e, t) { - let i, r, n, s, a, o = []; - if (!t.length) return []; - for (i = 0; i < e.byteLength;) r = We.readUint32(e, i), n = We.bin2str(e.subarray(i + 4, i + 8)), s = 1 < r ? i + r : e.byteLength, n === t[0] && (1 === t.length ? o.push(e.subarray(i + 8, s)) : (a = We.findBox(e.subarray(i + 8, s), t.slice(1))).length && (o = o.concat(a))), i = s; - return o - }, - findBoxWithOffset(e, t, i, r) { - let n, s, a, o, l, d = []; - if (!i.length) return []; - for (n = 0; n < e.byteLength;) s = We.readUint32(e, n), a = We.bin2str(e.subarray(n + 4, n + 8)), o = 1 < s ? n + s : e.byteLength, a === i[0] && (r && r.push({ - offset: n + t, - type: a, - size: s - }), 1 === i.length ? d.push({ - offset: n + t, - type: a, - data: e.subarray(n + 8, o), - boxSize: s, - walkedPath: r ? r.slice(0) : void 0 - }) : (l = We.findBoxWithOffset(e.subarray(n + 8, o), n + t + 8, i.slice(1), r ? r.slice(0) : void 0)).length && (d = d.concat(l), r = r ? r.slice(0, -1) : void 0)), n = o; - return d - } - }, - Ge = { - name: "MP4EncryptionRemuxer" - }; - class ze extends h { - constructor(e, t, i, r, n) { - super(e, t, n) - } - static _isCommonEncryptionInternal(e) { - return Boolean(e && !("NONE" === e || "AES-128" === e)) - } - static remuxInitSegment(c, h, p, f) { - if (!p) return c; - let e = c; - if (ze._isCommonEncryptionInternal(p.method)) { - const m = p.keyId; - let u = !1; - const g = []; - if (We.findBoxWithOffset(c, 0, ["moov", "trak"]).forEach(t => { - const o = t.data; - let l, i = 0; - const d = We.findBoxWithOffset(o, 0, ["mdia", "minf", "stbl", "stsd"], [])[0], - e = d.data.subarray(8); - let r = !0, - n = We.findBoxWithOffset(e, d.offset + 16, ["enca"]); - 0 === n.length && (r = !1, n = We.findBoxWithOffset(e, d.offset + 16, ["encv"])), n.forEach(s => { - let e = null, - a = null; - i = r ? (e = s.data.subarray(28), l = "audio", d.offset + 16 + 8 + 28) : (e = s.data.subarray(78), l = "video", d.offset + 16 + 8 + 78), e && We.findBoxWithOffset(e, i, ["sinf"]).forEach(e => { - const t = e.data, - i = We.findBox(t, ["frma"])[0], - r = We.findBox(t, ["schm"])[0]; - if (i) - if (r) { - var n = We.bin2str(r.subarray(4, 8)); - if ("aac " === We.bin2str(i.subarray(0, 4)) && (ge.types || ge.init(), i.set(ge.types.mp4a, 0)), "cbcs" === n || "cenc" === n) { - f && f.push(t); - const e = We.findBox(t, ["schi", "tenc"])[0]; - if (e) { - const h = 8; - e.subarray(8, 24), e.set(m, 8); - const f = e[6], - t = e[7]; - if (1 === f && 0 === t) { - const h = e[24]; - 0 < h && p.iv && h === p.iv.length && e.set(p.iv, 25) - } - } - } else if ("cbc2" === n) { - u = !0, ge.types || ge.init(); - const h = We.findBoxWithOffset(t, 0, ["frma"])[0], - f = ge.box(ge.types.schi, ge.tenc(p, l)), - m = ge.box(ge.types.sinf, t.subarray(h.offset, h.boxSize), ge.schm(), f); - a = ge.box(ge.types.trak, o.subarray(0, e.offset), m, o.subarray(e.offset + e.boxSize)); - const i = a.subarray(8), - r = m.byteLength - e.boxSize; - d.walkedPath && (d.walkedPath.push({ - type: "stsd", - offset: s.offset, - size: s.boxSize - }), d.walkedPath.forEach(e => { - We.writeUint32(i, e.offset, e.size + r) - })) - } - } else h.error(Ge, "missing schm box"); - else h.error(Ge, "missing frma box") - }), a = a || c.subarray(t.offset, t.offset + t.boxSize), g.push(a) - }); - var s = We.findBoxWithOffset(o, 0, ["edts"])[0]; - s && (ge.types || ge.init(), o.set(ge.types.free, s.offset + 4)) - }), u) { - const p = ze.remuxCbc2InitSegment(c, g, h); - e = p || c - } - } - return e - } - static remuxCbc2InitSegment(i, r, n) { - const s = We.findBoxWithOffset(i, 0, ["ftyp"])[0]; - if (s) { - const a = We.findBoxWithOffset(i, s.boxSize, ["moov"])[0]; - let e = [], - t = 0; - for (; t < a.data.byteLength;) { - const i = We.readUint32(a.data, t), - n = We.bin2str(a.data.subarray(t + 4, t + 8)), - s = 1 < i ? t + i : a.data.byteLength; - "trak" === n ? r && (e = e.concat(r), r = void 0) : e.push(a.data.subarray(t, s)), t = s - } - const o = ge.box(ge.types.moov, ...e), - l = new Uint8Array(s.boxSize + o.byteLength); - return l.set(i.subarray(0, s.boxSize)), l.set(o, s.boxSize), l - } - n.error(Ge, "no ftyp found") - } - static remuxOverflowSegment(i, e) { - ge.types || ge.init(); - const t = We.findBoxWithOffset(i, 0, ["moof", "traf", "tfdt"], []); - let r, n = i.byteLength; - if (t.forEach(e => { - 0 === e.data[0] && (n += 4) - }), n > i.byteLength) { - r = new Uint8Array(n); - let e = 0, - t = 0; - for (; t < i.byteLength;) { - const n = We.readUint32(i, t), - s = We.bin2str(i.subarray(t + 4, t + 8)), - a = 1 < n ? t + n : i.byteLength; - if ("moof" === s) { - const n = ze.remuxOverflowMoof(i.subarray(t + 8, a)); - r.set(n, e), e += n.byteLength - } else r.set(i.subarray(t, a), e), e += n; - t = a - } - } else e.warn(Ge, "no increase in size"); - return r || i - } - static remuxOverflowMoof(e) { - let t = 0; - const i = []; - for (; t < e.byteLength;) { - const r = We.readUint32(e, t); - if ("traf" === We.bin2str(e.subarray(t + 4, t + 8))) { - const s = ze.remuxOverflowTraf(e.subarray(t + 8, t + r)); - i.push(s) - } else i.push(e.subarray(t, t + r)); - t = 1 < r ? t + r : e.byteLength - } - const r = ge.box(ge.types.moof, ...i), - s = r.byteLength - e.byteLength - 8; - return We.findBoxWithOffset(r, 0, ["moof", "traf", "trun"], []).forEach(e => { - var t; - 0 != (1 & e.data[3]) && (t = We.readUint32(e.data, 8), We.writeUint32(e.data, 8, t + s)) - }), We.findBoxWithOffset(r, 0, ["moof", "traf", "saio"], []).forEach(t => { - const i = 1 & t.data[0]; - let r = 4; - 1 & t.data[3] && (r += 8); - var n = We.readUint32(t.data, r); - if (r += 4, i) - for (let e = 0; e < n; e++) { - const i = We.readUint64(t.data, r); - We.writeUint64(t.data, r, i + s), r += 8 - } else - for (let e = 0; e < n; e++) { - const i = We.readUint32(t.data, r); - We.writeUint32(t.data, r, i + s), r += 4 - } - }), r - } - static remuxOverflowTraf(e) { - let t = 0; - const i = []; - for (; t < e.byteLength;) { - var r, n = We.readUint32(e, t); - "tfdt" === We.bin2str(e.subarray(t + 4, t + 8)) && 0 === e[t + 8] ? (r = We.readUint32(e, t + 12), r = ge.box(ge.types.tfdt, new Uint8Array([1, 0, 0, 0, 0, 0, 0, 0, r >> 24, r >> 16 & 255, r >> 8 & 255, 255 & r])), i.push(r)) : i.push(e.subarray(t, t + n)), t = 1 < n ? t + n : e.byteLength - } - return ge.box(ge.types.traf, ...i) - } - remuxText(e, t) { - e.captionSamples.sort(function(e, t) { - return e.pts - t.pts - }), e.captionSamples.length && (t.captionData || (t.captionData = {}), t.captionData.mp4 = e.captionSamples), e.captionSamples = [] - } - remuxIFrame(e, t, i, r, n) { - if (!t.samples || !t.samples.length || !t.samples[0].data) return null; - let s; - const a = ge.moof(e * t.timescale, t), - o = new Uint8Array(a.byteLength + t.samples[0].data.byteLength + 8); - o.set(a), We.writeUint32(o, a.byteLength, t.samples[0].data.byteLength + 8), o.set(ge.types.mdat, a.byteLength + 4), o.set(t.samples[0].data, a.byteLength + 8), t.sequenceNumber++; - var l = t.timescale, - d = B(e + r, l), - l = B(e, l); - let u, c; - u = i ? (s = "audiovideo", i.sequenceNumber = t.sequenceNumber, t.sequenceNumber++, c = Te.getSegment(i, i.sequenceNumber, e * i.info.timescale, r), y(c.endTs, d)) : (s = "video", d); - n = { - data1: o, - data2: null == c ? void 0 : c.silentFragData, - startPTS: l, - startDTS: l, - endPTS: u, - endDTS: u, - type: s, - dropped: 0, - track: n - }; - this.observer.trigger(v.FRAG_PARSING_DATA, n), this.observer.trigger(v.FRAG_PARSED) - } - remuxEmsgAndRawData(e, t, i, r, n, s, a, o, l, d) { - let u; - t && r ? u = "audiovideo" : t ? u = "audio" : r && (u = "video"); - const c = Math.max(i, e), - h = { - data1: l, - track: d, - startPTS: B(a, o), - startDTS: B(a, o), - endPTS: void 0, - endDTS: void 0, - type: u, - dropped: 0 - }; - return c && (h.endPTS = B(a + c, o), h.endDTS = B(a + c, o)), n && n.captionSamples.length && this.remuxText(n, h), s && 0 < s.id3Samples.length && this.remuxID3(s, h), this.observer.trigger(v.FRAG_PARSING_DATA, h), this.observer.trigger(v.FRAG_PARSED), h - } - remuxID3(t, e) { - let i; - var r = t.id3Samples.length; - if (r) { - for (let e = 0; e < r; e++) i = t.id3Samples[e], i.pts = i.pts - 10, i.dts = i.dts - 10; - e.id3Samples = [...t.id3Samples] - } - t.id3Samples = [] - } - } - const Xe = Math.pow(2, 32) - 1, - Ye = Math.pow(2, 20) - 1, - Je = { - name: "MP4Demuxer" - }; - class Ze extends u { - constructor(e, t, i, r, n) { - super(e, t, i, {}, n), this.mp4Remuxer = t, this.audioPrimingDelay = i.audioPrimingDelay - } - resetTimeStamp(e) { - ne(e) ? this.initData.audio && !this.initData.video ? this.initPtsTs = { - baseTime: Math.round(e * this.initData.audio.timescale / 9e4), - timescale: this.initData.audio.timescale - } : this.initPtsTs = { - baseTime: e, - timescale: 9e4 - } : this.initPtsTs = void 0 - } - static isHEVCFlavor(e) { - if (!e) return !1; - var t = e.indexOf("."), - t = t < 0 ? e : e.substring(0, t); - return "hvc1" === t || "hev1" === t || "chvc" === t || "qhvc" === t || "qhev" === t || "muxa" === t || "dvh1" === t || "dvhe" === t || "cdh1" === t || "qdh1" === t || "qdhe" === t - } - resetInitSegment(t, i, r) { - if (this._silentAudioTrack = void 0, t && t.byteLength) { - var n = ze.remuxInitSegment(t, this.logger, r), - s = this.initData = Ze.parseInitSegment(n); - let e; - s.foundLargeTimescale && this.logger.warn(Je, "large timescale found, will check for 32 bit tfdts"); - var a = s.audioCodec, - r = s.videoCodec; - if (s.audio && s.video ? e = { - type: "audiovideo", - container: "video/mp4", - codec: a + "," + r, - initSegment: n - } : (s.audio && a && (e = { - type: "audio", - container: "audio/mp4", - codec: a, - initSegment: n - }), s.video && r && (e = { - type: "video", - container: "video/mp4", - codec: r, - initSegment: n - })), s.video) { - const o = s.video, - l = t.subarray(o.trakOffset, o.trakOffset + o.trakSize); - this._videoTrack = Object.assign(Object.assign({}, o), { - info: { - id: o.id, - timescale: o.timescale, - duration: i - }, - trakData: l, - sequenceNumber: 0, - samples: [] - }), this.trySEICaptions = !be.isVP09(r), this._captionTrack = Object.assign(Object.assign({}, s.caption), { - sequenceNumber: 0, - captionSamples: [] - }) - } - s.audio && a && (this._audioTrack = Object.assign({}, s.audio)), s.caption && (this.trySEICaptions = !1, this._captionTrack = Object.assign(Object.assign({}, s.caption), { - sequenceNumber: 0, - captionSamples: [] - })), this.remuxedInitDataTrack = e; - s = { - track: e - }; - this.observer.trigger(v.FRAG_PARSING_INIT_SEGMENT, s) - } - } - static probe(e, t) { - return 0 < We.findBox(e.subarray(0, Math.min(e.length, 512e3)), ["moof"]).length - } - static parseHvcC(e) { - let t; - var i, r; - return e ? 1 === (i = e[0]) ? (r = e[1], t = { - profileSpace: r >> 6, - tierFlag: (32 & r) >> 5 ? "H" : "L", - profileIDC: 31 & r, - profileCompat: We.readUint32(e, 2), - constraintIndicator: e.subarray(6, 12), - levelIDC: e[12] - }) : Qe().warn(Je, `Unhandled version ${i} in hvcC box`) : Qe().warn(Je, "No hvcC box"), t - } - static hvcCToCodecString(t, i) { - const r = t + "." + (i.profileSpace ? String.fromCharCode(i.profileSpace + "A" - 1) : "") + i.profileIDC + "." + i.profileCompat.toString(16).toUpperCase() + "." + i.tierFlag + i.levelIDC; - let n = ""; - for (let e = i.constraintIndicator.length - 1; 0 <= e; --e) { - const r = i.constraintIndicator[e]; - if (0 !== r || "" !== n) { - const t = r.toString(16).toUpperCase(); - n = "." + ("" === n ? t : t + n) - } - } - return r + n - } - static parseDvcC(e) { - let t; - return e ? t = { - versionMajor: e[0], - versionMinor: e[1], - profile: e[2] >> 1 & 127, - level: e[2] << 5 & 32 | e[3] >> 3 & 31 - } : Qe().warn(Je, "No dvcC box"), t - } - static dvcCToCodecString(e, t) { - return e + "." + Ze.checkAndAddLeadingZero(t.profile) + "." + Ze.checkAndAddLeadingZero(t.level) - } - static parseVpcC(e) { - let t; - return e ? t = { - profile: e[4], - level: e[5], - bitDepth: e[6] >> 4 & 15 - } : Qe().warn(Je, "No vpcC box"), t - } - static vpcCToCodecString(e, t) { - return e + "." + Ze.checkAndAddLeadingZero(t.profile) + "." + Ze.checkAndAddLeadingZero(t.level) + "." + Ze.checkAndAddLeadingZero(t.bitDepth) - } - static checkAndAddLeadingZero(e) { - return (e < 10 ? "0" : "") + e - } - static parseInitSegment(e) { - const c = { - foundLargeTimescale: !1, - tracksById: {} - }; - return We.findBoxWithOffset(e, 0, ["moov", "trak"]).forEach(t => { - const i = t.data, - r = We.findBox(i, ["tkhd"])[0]; - if (r) { - var n = 0 === (a = r[0]) ? 12 : 20, - s = We.readUint32(r, n), - e = We.findBox(i, ["mdia", "mdhd"])[0]; - if (e) { - var a, o = 0 === (a = e[0]) ? 12 : 20, - n = We.readUint32(e, o); - o += 4, 1e6 <= n && (c.foundLargeTimescale = !0); - const l = 0 === a ? We.readUint32(e, o) : 0, - d = We.findBox(i, ["mdia", "hdlr"])[0]; - if (d) { - const r = We.bin2str(d.subarray(8, 12)), - u = { - soun: "audio", - vide: "video", - clcp: "caption" - } [r] || r; - if (u) { - const r = We.findBox(i, ["mdia", "minf", "stbl", "stsd"]); - if (r.length) { - const i = r[0]; - We.bin2str(i.subarray(12, 16)); - o = Ze.parseStsd(i); - let e; - if ("caption" === u) { - const t = Object.assign({ - id: s, - type: u, - timescale: n, - duration: l, - isTimingTrack: !1, - sequenceNumber: 0, - captionSamples: [] - }, o); - c.caption = t, e = t - } else { - const i = Object.assign({ - id: s, - type: u, - timescale: n, - duration: l, - isTimingTrack: !0, - trakOffset: t.offset, - trakSize: t.boxSize, - sequenceNumber: 0, - samples: [], - fragmentDuration: 0 - }, o); - "video" === u ? (c.video = i, c.videoCodec = i.codec) : (c.audio = i, c.audioCodec = i.codec), e = i - } - c.tracksById[s] = e - } - } - } - } - } - }), We.findBoxWithOffset(e, 0, ["moov", "mvex", "trex"]).forEach(e => { - var t = e.data, - e = We.readUint32(t, 4), - t = We.readUint32(t, 16); - c.tracksById[e].defaultSampleSize = t - }), c - } - static parseStsd(e) { - let r, t; - const i = e.subarray(8); - let n = We.bin2str(i.subarray(4, 8)), - s = null, - a = null; - "enca" === n ? (s = We.findBox(i, ["enca"])[0], a = s.subarray(28)) : "encv" === n && (s = We.findBox(i, ["encv"])[0], a = s.subarray(78)); - e = !!a; - r = 0, a && We.findBox(a, ["sinf"]).forEach(e => { - const t = We.findBox(e, ["schm"])[0]; - if (t) { - var i = We.bin2str(t.subarray(4, 8)); - if ("cbcs" === i || "cenc" === i) { - const t = We.findBox(e, ["frma"])[0]; - t && (n = We.bin2str(t)); - e = We.findBox(e, ["schi", "tenc"])[0]; - e && (r = e[7]) - } - } - }); - let o; - var l = i.subarray(86); - switch (n) { - case "mp4a": - t = "mp4a.40.5"; - break; - case "ac-3": - case "ec-3": - case "alac": - case "fLaC": - t = n; - break; - case "avc1": - case "avc3": - t = n + ".640028"; - break; - case "hvc1": - case "hev1": - const d = We.findBox(l, ["hvcC"])[0]; - o = Ze.parseHvcC(d), t = o ? Ze.hvcCToCodecString(n, o) : n + ".2.4.H150.B0"; - break; - case "dvh1": - case "dvhe": - const r = We.findBox(l, ["dvcC"])[0]; - o = Ze.parseDvcC(r), t = o ? Ze.dvcCToCodecString(n, o) : n + ".05.01"; - break; - case "c608": - t = n; - break; - case "vp09": - const i = We.findBox(l, ["vpcC"])[0]; - o = Ze.parseVpcC(i), t = Ze.vpcCToCodecString(n, o); - break; - default: - t = n - } - return { - codec: t, - encrypted: e, - defaultPerSampleIVSize: r - } - } - static has32BitTfdts(e) { - const t = We.findBox(e, ["moof", "traf", "tfdt"]); - let i = !1; - return t.forEach(e => { - 0 === e[0] && (i = !0) - }), i - } - static getStartDtsTs(r, e) { - const t = We.findBox(e, ["moof", "traf"]); - let n, s = Number.MAX_SAFE_INTEGER; - return t.map(function(i) { - return We.findBox(i, ["tfhd"]).forEach(e => { - var t = We.readUint32(e, 4), - e = r.tracksById[t]; - if (e) { - if (!e.isTimingTrack) return 1 / 0; - t = e.timescale || 9e4, e = We.findBox(i, ["tfdt"]).map(function(e) { - let t; - var i = e[0]; - return t = We.readUint32(e, 4), 1 === i && (t > Ye && Qe().warn(Je, `Value larger than can be represented by float for upper 32 bits ${t}`), t *= Math.pow(2, 32), t += We.readUint32(e, 8)), t - }), e = 0 < e.length ? e[0] : 1 / 0; - isFinite(e) && e / t < s && (s = e / t, n = { - baseTime: e, - timescale: t - }) - } - }) - }), n - } - static offsetStartDTS(r, e, l, n) { - We.findBox(e, ["moof", "traf"]).map(function(i) { - return We.findBox(i, ["tfhd"]).map(function(e) { - const t = We.readUint32(e, 4), - s = r.tracksById[t]; - if (s) { - const a = s.timescale || 9e4, - o = "caption" === s.type ? 0 : n; - We.findBox(i, ["tfdt"]).map(function(t) { - const i = t[0], - r = s.type; - if (0 === i) { - let e = We.readUint32(t, 4) - Math.round(l.baseTime * a / l.timescale); - "video" === r && e < 0 && (Qe().warn(Je, `video tdft would have gone negative by ${e/a} seconds`), e = 0), e += Math.round(o * a), e = Math.max(e, 0), We.writeUint32(t, 4, e) - } else { - const i = We.readUint32(t, 4); - i > Ye && Qe().error(Je, `baseMediaDecodeTime larger than can be represented by float for upper 32 bits ${i}`); - let e = i; - e *= Math.pow(2, 32), e += We.readUint32(t, 8), e -= Math.round(l.baseTime * a / l.timescale), "video" === r && e < 0 && (Qe().warn(Je, `video tdft would have gone negative by ${e/a} seconds`), e = 0), e += Math.round(o * a), e = Math.max(e, 0); - const n = Math.floor(e / (1 + Xe)), - s = Math.floor(e % (1 + Xe)); - We.writeUint32(t, 4, n), We.writeUint32(t, 8, s) - } - }) - } - }) - }) - } - static writeStartDTS(i, e, s) { - We.findBox(e, ["moof", "traf"]).map(function(t) { - return We.findBox(t, ["tfhd"]).map(function(e) { - e = We.readUint32(e, 4), e = i.tracksById[e]; - if (e) { - const r = e.timescale || 9e4, - n = Math.round(s * r) / r; - .01 < Math.abs(n - s) && Qe().warn(Je, `[iframes] large rounding error when adjusting timestamps, startDTS: ${s}, roundedStartDTS: ${n}`), We.findBox(t, ["tfdt"]).map(function(e) { - var t, i; - 0 === e[0] ? We.writeUint32(e, 4, n * r) : (i = n * r, i = Math.max(i, 0), t = Math.floor(i / (1 + Xe)), i = Math.floor(i % (1 + Xe)), We.writeUint32(e, 4, t), We.writeUint32(e, 8, i)) - }) - } - }) - }) - } - static parseSAIO(e) { - let t = 0, - i = 0; - var r = e[0]; - i += 4, 0 != (1 & We.readUint32(e, 0)) && (i += 8); - var n = 16777215 & We.readUint32(e, i); - return 1 == n ? (i += 4, t = We.readUint32(e, i), 1 === r && (i += 4, t *= Math.pow(2, 32), t += We.readUint32(e, i))) : Qe().error(Je, `saio entry count error, count is: ${n}`), t - } - static parseSAIZ(e) { - let t = 0, - i = 0; - return i += 4, 0 != (1 & We.readUint32(e, 0)) && (i += 8), t = e[i], i++, i += 4, 0 === t && (t = e[i]), t - } - static parseSubsample(e, t) { - const i = { - subsamples: [] - }; - let r = 0; - for (e && (i.iv = t.subarray(0, e), r += e), r += 2; r + 6 <= t.byteLength;) { - const e = We.readUint16(t, r); - r += 2; - var n = We.readUint32(t, r); - r += 4, i.subsamples.push([e, n]) - } - return i - } - static isSEIMessage(e, t) { - return e ? 39 === t || 40 === t : 6 === t - } - static parseCLCPSample(e, t, i) { - let r = 0; - const n = []; - let s = 0; - for (; r < i;) { - var a = t + r, - o = We.readUint32(e, a); - a += 4; - var l = We.bin2str(e.subarray(a, a + 4)); - if (a += 4, "cdat" !== l) break; { - const t = o - 8, - d = e.subarray(a, a + t); - s += t, n.push(d), r += o - } - } - return { - cdatList: n, - cdatTotalSize: s - } - } - static parseSamples(e, S, b, T, E, I) { - const w = b.timescale, - d = b.id; - let A, O = e, - k = 0, - C = !1; - We.findBoxWithOffset(S, 0, ["moof"]).map(function(e) { - const t = e.data, - v = e.offset; - We.findBox(t, ["traf"]).map(function(l) { - var e = We.findBox(l, ["tfdt"]).map(function(e) { - let t; - var i = e[0]; - return t = We.readUint32(e, 4), 1 === i && (t *= Math.pow(2, 32), t += We.readUint32(e, 8)), t / w - })[0]; - return void 0 !== e && (O = e), We.findBox(l, ["tfhd"]).map(function(e) { - var t = We.readUint32(e, 4), - i = 16777215 & We.readUint32(e, 0), - r = 0 != (1 & i), - n = 0 != (2 & i), - s = 0 != (8 & i); - let g = 0; - var a = 0 != (16 & i); - let y = 0; - i = 0 != (32 & i); - let o = 8; - if (ne(b.defaultSampleSize) && (y = b.defaultSampleSize), t === d) { - if (r && (We.readUint32(e, o), o += 4, We.readUint32(e, o), o += 4), n && (We.readUint32(e, o), o += 4), s && (g = We.readUint32(e, o), o += 4), a && (y = We.readUint32(e, o), o += 4), i && (We.readUint32(e, o), o += 4), "video" === b.type) { - let t = 0, - i = 0; - We.findBox(l, ["saio"]).map(function(e) { - t = Ze.parseSAIO(e) - }), We.findBox(l, ["saiz"]).map(function(e) { - i = Ze.parseSAIZ(e) - }), t && i && (A = Ze.parseSubsample(b.defaultPerSampleIVSize, S.subarray(t, t + i))), C = Ze.isHEVCFlavor(b.codec) - } - We.findBox(l, ["trun"]).map(function(i) { - var t = i[0], - e = 16777215 & We.readUint32(i, 0), - r = 0 != (1 & e); - let n = 0; - var s = 0 != (4 & e), - a = 0 != (256 & e); - let o = 0; - var l = 0 != (512 & e); - let d = 0; - var u = 0 != (1024 & e), - c = 0 != (2048 & e); - let h = 0; - var p = We.readUint32(i, 4); - let f = 8; - r && (n = We.readUint32(i, f), f += 4), s && (f += 4); - let m = n + v; - for (let e = 0; e < p && (I < 0 || k < I); e++) { - if (a ? (o = We.readUint32(i, f), f += 4) : o = g, l ? (d = We.readUint32(i, f), f += 4) : d = y, u && (f += 4), c && (h = 0 === t ? We.readUint32(i, f) : We.readSint32(i, f), f += 4), "video" === b.type) { - if (ne(T)) b.samples.push({ - data: S.subarray(m, m + d), - size: d, - duration: T * w, - cts: 0, - flags: { - isLeading: 0, - isDependedOn: 0, - hasRedundancy: 0, - degradPrio: 0, - dependsOn: 2, - isNonSync: 0, - paddingValue: 0 - }, - subsamples: A ? A.subsamples : [], - iv: A ? A.iv : void 0 - }); - else if (b.fragmentDuration += o, E) { - let e = 0; - for (; e < d;) { - const T = We.readUint32(S, m); - m += 4; - const E = 31 & S[m]; - if (b.seiSamples || (b.seiSamples = []), Ze.isSEIMessage(C, E)) { - const i = S.subarray(m, m + T); - b.seiSamples.push({ - pts: O + h / w, - type: E, - data: i, - sampleOffset: m, - naluSize: T - }) - } - m += T, e += T + 4 - } - } - } else if ("audio" === b.type) b.fragmentDuration += o; - else if ("caption" === b.type) { - const { - cdatList: i, - cdatTotalSize: T - } = Ze.parseCLCPSample(S, m, d); - if (m += d, i.length) { - let t; - if (1 === i.length) t = new Uint8Array(i[0]); - else if (1 < i.length) { - let e = 0; - t = new Uint8Array(T); - for (const T of i) t.set(T, e), e += T.length - } - b.captionSamples.push({ - type: 3, - pts: O, - bytes: t - }) - } - } - k++, O += o / w - } - }) - } - }) - }) - }) - } - static parseEmsg(e) { - let t, i, r, n, s, a = "", - o = "", - l = 0; - if (0 === e[0]) { - for (; - "\0" !== We.bin2str(e.subarray(l, l + 1));) a += We.bin2str(e.subarray(l, l + 1)), l += 1; - for (a += We.bin2str(e.subarray(l, l + 1)), l += 1; - "\0" !== We.bin2str(e.subarray(l, l + 1));) o += We.bin2str(e.subarray(l, l + 1)), l += 1; - o += We.bin2str(e.subarray(l, l + 1)), l += 1, t = We.readUint32(e, 12), i = We.readUint32(e, 16), n = We.readUint32(e, 20), s = We.readUint32(e, 24), l = 28 - } else { - l += 4, t = We.readUint32(e, l), l += 4; - const i = We.readUint32(e, l); - l += 4; - var d = We.readUint32(e, l); - for (l += 4, r = Math.pow(2, 32) * i + d, Number.isSafeInteger(r) || (r = Number.MAX_SAFE_INTEGER, Qe().warn(Je, "Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box")), n = We.readUint32(e, l), l += 4, s = We.readUint32(e, l), l += 4; - "\0" !== We.bin2str(e.subarray(l, l + 1));) a += We.bin2str(e.subarray(l, l + 1)), l += 1; - for (a += We.bin2str(e.subarray(l, l + 1)), l += 1; - "\0" !== We.bin2str(e.subarray(l, l + 1));) o += We.bin2str(e.subarray(l, l + 1)), l += 1; - o += We.bin2str(e.subarray(l, l + 1)), l += 1 - } - return { - schemeIdUri: a, - value: o, - timeScale: t, - presentationTime: r, - presentationTimeDelta: i, - eventDuration: n, - id: s, - payload: e.subarray(l, e.byteLength) - } - } - static extractID3PayloadCreateID3Track(e, t, i, r) { - const n = new M(e.payload, r), - s = new Uint8Array(e.payload), - a = s.byteLength; - let o = 0, - l = 0; - var d = ne(e.presentationTime) ? e.presentationTime / e.timeScale : t + e.presentationTimeDelta / e.timeScale; - if (ne(d)) { - const c = e.eventDuration, - h = s.subarray(0, 10), - p = We.bin2str(h.subarray(l, l + 3)); - l += 3, "ID3" !== p && Qe().error(Je, "No ID3 tag found when extracting ID3 payload"), l += 2; - var t = s.subarray(l, l + 1), - e = 64 & t[0], - u = 16 & t[0]; - if (l += 1, M.readSynchSafeUint32(s.subarray(l, l + 4)), l += 4, e) { - const f = M.readSynchSafeUint32(s.subarray(l, l + 4)); - l += 4, l += f - } - for (; l + 2 < a;) { - We.bin2str(s.subarray(l, l + 4)), l += 4; - const m = M.readSynchSafeUint32(s.subarray(l, l + 4)); - l += 4; - const r = d + o * c, - a = { - data: s, - pts: r, - dts: r, - keyTagInfo: void 0, - frames: n.frames - }; - i.id3Samples.push(a), l += m, o++, u && ("DI3" !== We.bin2str(s.subarray(l, l + 3)) && Qe().error(Je, "End should be DI3 if footer present in extracting ID3 payload"), l += 3, l += 7) - } - l + 2 === a && 0 !== We.readUint16(s, l) && Qe().warn(Je, "Padding should be 0 when extracting ID3 payload") - } else Qe().error(Je, "No pts found in emsg info when extracting ID3 payload") - } - append(e, t, i, r, n, s, a) { - let o = this.initData, - l = 0, - d = 0, - u = !1, - c = !1; - void 0 === o && (this.resetInitSegment(e, 0), o = this.initData); - let h, p = this.initPtsTs; - p || (h = Ze.getStartDtsTs(o, e), this.initPtsTs = p = { - baseTime: h.baseTime - Math.round(t * h.timescale), - timescale: h.timescale - }, this.observer.trigger(v.INIT_PTS_FOUND, { - initPTS: p - })), o.foundLargeTimescale && Ze.has32BitTfdts(e) && !a && (e = ze.remuxOverflowSegment(e, this.logger)), h = Ze.getStartDtsTs(o, e); - const f = We.findBox(e, ["emsg"]); - if (o.video && o.video.encrypted && (We.findBox(e, ["moof", "traf"]).find(function(e) { - return Boolean(We.findBox(e, ["senc"])[0] || We.findBox(e, ["saiz"])[0] && We.findBox(e, ["saio"])[0]) - }) || this.logger.warn(Je, `Missing subsample information for encrypted content codec=${o.videoCodec}`)), a) { - const t = this._videoTrack.timescale; - a = Math.ceil(a * t) / t; - const i = (h.baseTime + this.audioPrimingDelay * h.timescale) / h.timescale; - if (this._videoTrack && this._audioTrack && !this._silentAudioTrack) { - const e = Te.getTrack(this.observer, this._audioTrack.id, this._audioTrack.codec, 2, this.logger); - if (!e) throw `unable to create silent audio track for codec ${this._audioTrack.codec}`; - this._silentAudioTrack = Object.assign(Object.assign({}, e), { - sequenceNumber: 0 - }); - const t = ge.initSegment([this._videoTrack, this._silentAudioTrack]); - this.remuxedInitDataTrack = { - type: "audiovideo", - container: "video/mp4", - codec: this._silentAudioTrack.config.codec + "," + this._videoTrack.codec, - initSegment: t - }; - const i = { - track: this.remuxedInitDataTrack - }; - this.observer.trigger(v.FRAG_PARSING_INIT_SEGMENT, i) - } - Ze.parseSamples(i, e, this._videoTrack, a, !1, 1), this.mp4Remuxer.remuxIFrame(i, this._videoTrack, this._silentAudioTrack, a, this.remuxedInitDataTrack), this._videoTrack.samples = [] - } else { - a = (h.baseTime - this.audioPrimingDelay * h.timescale) / h.timescale, a = Math.max(0, a); - if (f && 0 < f.length) { - const e = f.map(e => { - e = Ze.parseEmsg(e); - return "https://aomedia.org/emsg/ID3\0" !== e.schemeIdUri || this._id3Track || (this._id3Track = { - id3Samples: [], - inputTimescale: 9e4 - }), e - }); - this._id3Track && e.map(e => { - Ze.extractID3PayloadCreateID3Track(e, t, this._id3Track, this.logger) - }) - } - this._videoTrack && (Ze.parseSamples(a, e, this._videoTrack, void 0, this.trySEICaptions, -1), l = this._videoTrack.fragmentDuration / this._videoTrack.timescale, u = !0, this._videoTrack.fragmentDuration = 0, this.trySEICaptions ? (Ze.extractSEICaptionsFromNALu(this._videoTrack.seiSamples, this._captionTrack), this._videoTrack.seiSamples = []) : this._captionTrack && Ze.parseSamples(a, e, this._captionTrack, void 0, !1, Number.MAX_SAFE_INTEGER)), this._audioTrack && (Ze.parseSamples(a, e, this._audioTrack, void 0, !1, -1), d = this._audioTrack.fragmentDuration / this._audioTrack.timescale, c = !0, this._audioTrack.fragmentDuration = 0), this.mp4Remuxer.remuxEmsgAndRawData(d, c, l, u, this._captionTrack, this._id3Track, a, h.timescale, e, this.remuxedInitDataTrack), this._id3Track && (this._id3Track.id3Samples = []) - } - } - static extractSEICaptionsFromNALu(a, o) { - if (a) { - for (let s = 0; s < a.length; ++s) { - var l = a[s], - d = l.pts; - let t = 0; - t++; - let e = 0, - i = 0, - r = !1, - n = 0; - for (; !r && t < l.data.length;) { - for (e = 0; !(t >= l.data.length) && (n = l.data[t++], e += n, 255 === n);); - for (i = 0; !(t >= l.data.length) && (n = l.data[t++], i += n, 255 === n);); - const a = l.data.length - t; - if (4 === e && t < l.data.length) { - if (r = !0, 181 === l.data[t++]) { - const a = We.readUint16(l.data, t); - if (t += 2, 49 === a) { - const a = We.readUint32(l.data, t); - if (t += 4, 1195456820 === a && 3 === l.data[t++]) { - const a = l.data[t++]; - t++; - const u = 31 & a, - c = []; - if (64 & a) - for (let e = 0; e < u; e++) { - const a = l.data[t++]; - if (a === (252 & a)) { - const o = 3 & a; - if (0 == o || 1 == o) { - const a = l.data[t++], - o = l.data[t++]; - c.push(a), c.push(o) - } - } else t += 2 - } - 0 < c.length && o.captionSamples.push({ - type: 3, - pts: d, - bytes: c - }) - } - } - } - } else if (i < a) t += i; - else if (i > a) break - } - } - return o - } - } - } - const et = { - name: "ExpGolomb" - }; - class tt { - constructor(e, t) { - this.data = e, this.logger = t, this._bytesAvailable = e.byteLength, this.word = 0, this.bitsAvailable = 0 - } - get bytesAvailable() { - return this._bytesAvailable - } - loadWord() { - const e = this.data, - t = this._bytesAvailable, - i = e.byteLength - t, - r = new Uint8Array(4), - n = Math.min(4, t); - if (0 === n) throw new Error("no bytes available"); - r.set(e.subarray(i, i + n)), this.word = new DataView(r.buffer).getUint32(0), this.bitsAvailable = 8 * n, this._bytesAvailable -= n - } - skipBits(e) { - var t; - this.bitsAvailable > e || (t = (e -= this.bitsAvailable) >> 3, e -= t >> 3, this._bytesAvailable -= t, this.loadWord()), this.word <<= e, this.bitsAvailable -= e - } - readBits(e) { - let t = Math.min(this.bitsAvailable, e); - var i = this.word >>> 32 - t; - return 32 < e && this.logger.error(et, "Cannot read more than 32 bits at a time"), this.bitsAvailable -= t, 0 < this.bitsAvailable ? this.word <<= t : 0 < this._bytesAvailable && this.loadWord(), t = e - t, 0 < t && this.bitsAvailable ? i << t | this.readBits(t) : i - } - skipLZ() { - let e; - for (e = 0; e < this.bitsAvailable; ++e) - if (0 != (this.word & 2147483648 >>> e)) return this.word <<= e, this.bitsAvailable -= e, e; - return this.loadWord(), e + this.skipLZ() - } - skipUEG() { - this.skipBits(1 + this.skipLZ()) - } - skipEG() { - this.skipBits(1 + this.skipLZ()) - } - readUEG() { - var e = this.skipLZ(); - return this.readBits(e + 1) - 1 - } - readEG() { - var e = this.readUEG(); - return 1 & e ? 1 + e >>> 1 : -1 * (e >>> 1) - } - readBoolean() { - return 1 === this.readBits(1) - } - readUByte() { - return this.readBits(8) - } - readUShort() { - return this.readBits(16) - } - readUInt() { - return this.readBits(32) - } - skipScalingList(e) { - let t, i, r = 8, - n = 8; - for (t = 0; t < e; t++) 0 !== n && (i = this.readEG(), n = (r + i + 256) % 256), r = 0 === n ? r : n - } - readSPS() { - let e, t, i, r = 0, - n = 0, - s = 0, - a = 0; - const o = this.readUByte.bind(this), - l = this.readBits.bind(this), - d = this.readUEG.bind(this), - u = this.readBoolean.bind(this), - c = this.skipBits.bind(this), - h = this.skipEG.bind(this), - p = this.skipUEG.bind(this), - f = this.skipScalingList.bind(this); - o(); - var m = o(); - if (l(5), c(3), o(), p(), 100 === m || 110 === m || 122 === m || 244 === m || 44 === m || 83 === m || 86 === m || 118 === m || 128 === m) { - const e = d(); - if (3 === e && c(1), p(), p(), c(1), u()) - for (t = 3 !== e ? 8 : 12, i = 0; i < t; i++) u() && f(i < 6 ? 16 : 64) - } - p(); - var g = d(); - if (0 === g) d(); - else if (1 === g) - for (c(1), h(), h(), e = d(), i = 0; i < e; i++) h(); - p(), c(1); - var y = d(), - m = d(), - g = l(1); - 0 === g && c(1), c(1), u() && (r = d(), n = d(), s = d(), a = d()); - let v = [1, 1]; - if (u() && u()) switch (o()) { - case 1: - v = [1, 1]; - break; - case 2: - v = [12, 11]; - break; - case 3: - v = [10, 11]; - break; - case 4: - v = [16, 11]; - break; - case 5: - v = [40, 33]; - break; - case 6: - v = [24, 11]; - break; - case 7: - v = [20, 11]; - break; - case 8: - v = [32, 11]; - break; - case 9: - v = [80, 33]; - break; - case 10: - v = [18, 11]; - break; - case 11: - v = [15, 11]; - break; - case 12: - v = [64, 33]; - break; - case 13: - v = [160, 99]; - break; - case 14: - v = [4, 3]; - break; - case 15: - v = [3, 2]; - break; - case 16: - v = [2, 1]; - break; - case 255: - v = [o() << 8 | o(), o() << 8 | o()] - } - return { - width: Math.ceil(16 * (y + 1) - 2 * r - 2 * n), - height: (2 - g) * (m + 1) * 16 - (g ? 2 : 4) * (s + a), - pixelRatio: v - } - } - readSliceType() { - return this.readUByte(), this.readUEG(), this.readUEG() - } - } - const it = { - name: "TS Demuxer" - }; - var rt, nt = class extends c { - constructor(e, t, i, r, n) { - super(e, t, i, r, n) - } - static probe(e, t) { - return 564 <= e.length && 71 === e[0] && 71 === e[188] && 71 === e[376] - } - resetInitSegment(e, t, i) { - this.pmtParsed = !1; - var r = { - id: this._pmtId = -1, - inputTimescale: 9e4, - timescale: NaN, - duration: 0, - encrypted: i && i.isEncrypted, - keyTagInfo: i - }, - i = { - len: 0, - sequenceNumber: 0 - }; - this._avcContext = { - info: Object.assign({}, r), - parsingData: Object.assign(Object.assign({}, i), { - esSamples: new Array, - dropped: 0 - }), - config: {}, - container: "video/mp2t", - type: "video" - }, this._audioContext = { - info: Object.assign({}, r), - parsingData: Object.assign(Object.assign({}, i), { - esSamples: new Array - }), - container: "video/mp2t", - type: "audio" - }, this._id3Track = { - id: -1, - inputTimescale: 9e4, - id3Samples: [] - }, this._txtTrack = { - inputTimescale: 9e4, - captionSamples: [] - }, this._duration = t, this._initSegment = e - } - append(e, t, i, r, n, s, a) { - let o, l, d, u, c, h = !1; - this.contiguous = i; - const p = this._avcContext, - f = this._audioContext, - m = this._id3Track; - let g = this.pmtParsed, - y = p.info.id, - v = f.info.id, - S = m.id, - b = this._pmtId, - T = p.pesData, - E = f.pesData, - I = m.pesData; - if (this.iframeMode = void 0 !== a, this._initSegment && 0 < this._initSegment.byteLength) { - const t = new Uint8Array(this._initSegment.byteLength + e.byteLength); - t.set(this._initSegment), t.set(e, this._initSegment.byteLength), this._initSegment = void 0, 71 === t[0] && (e = t) - } - let w, A, O = e.length; - for (O -= O % 188, o = 0; o < O; o += 188) { - if (71 !== e[o]) { - const e = new D(!1, "TS packet did not start with 0x47", $.NoTSSyncByteFound); - return void this.observer.trigger(x.INTERNAL_ERROR, e) - } - if (l = !!(64 & e[o + 1]), d = ((31 & e[o + 1]) << 8) + e[o + 2], 1 < (48 & e[o + 3]) >> 4) { - if (u = o + 5 + e[o + 4], u === o + 188) continue - } else u = o + 4; - switch (d) { - case y: - l && (T && (c = this._parsePES(T)) && this._parseAVCPES(c, !1), T = { - data: [], - size: 0, - keyTagInfo: n - }), T && (T.data.push(e.subarray(u, o + 188)), T.size += o + 188 - u); - break; - case v: - if (l && !this.iframeMode) { - if (E && (c = this._parsePES(E))) switch (f.segmentCodec) { - case "aac": - this._parseAACPES(c); - break; - case "mp3": - this._parseMPEGPES(c); - break; - case "ac3": - case "ec3": - this._parseDolbyPES(c) - } - E = { - data: [], - size: 0, - keyTagInfo: n - } - } - E && (E.data.push(e.subarray(u, o + 188)), E.size += o + 188 - u); - break; - case S: - l && (I && (c = this._parsePES(I)) && m.id3Samples.push(c), I = { - data: [], - size: 0 - }), I && (I.data.push(e.subarray(u, o + 188)), I.size += o + 188 - u); - break; - case 0: - l && (u += e[u] + 1), b = this._pmtId = this._parsePAT(e, u); - break; - case b: - l && (u += e[u] + 1); - const t = this._parsePMT(e, u, this.typeSupported); - y = t.avcId, 0 < y && (p.info.id = y, p.info.encrypted = t.videoEncrypted), v = t.audioId, 0 < v && (f.info.id = v, f.segmentCodec = t.audioSegmentCodec, f.info.encrypted = t.audioEncrypted), S = t.id3Id, 0 < S && (m.id = S), h && !g && (h = !1, o = -188), g = this.pmtParsed = !0; - break; - case 17: - case 8191: - break; - default: - h = !0 - } - } - if (T && (c = this._parsePES(T)) ? (this._parseAVCPES(c, !0), p.pesData = void 0) : p.pesData = T, E && (c = this._parsePES(E))) { - switch (f.segmentCodec) { - case "aac": - this._parseAACPES(c); - break; - case "mp3": - this._parseMPEGPES(c); - break; - case "ac3": - case "ec3": - this._parseDolbyPES(c) - } - f.pesData = void 0 - } else E && E.size && this.logger.warn(it, "last AAC PES packet truncated,might overlap between fragments"), f.pesData = E; - I && (c = this._parsePES(I)) ? (m.id3Samples.push(c), m.pesData = void 0) : m.pesData = I, f.config && f.segmentCodec && (w = { - type: "audio", - info: f.info, - config: f.config, - parsingData: f.parsingData - }); - var k, C = p.config; - "string" == typeof(k = C).codec && Array.isArray(k.sps) && Array.isArray(k.pps) && "number" == typeof k.width && "number" == typeof k.height && Array.isArray(k.pixelRatio) && (A = { - type: "video", - info: p.info, - config: C, - parsingData: p.parsingData - }), this.esRemuxer.remuxEsTracks(w, A, m, this._txtTrack, t, i, r, n, s, a) - } - destroy() { - this._duration = 0 - } - _parsePAT(e, t) { - return (31 & e[t + 10]) << 8 | e[t + 11] - } - _parsePMT(e, t, i) { - var r; - const n = { - audioId: -1, - avcId: -1, - id3Id: -1, - audioEncrypted: !1, - videoEncrypted: !1 - }, - s = t + 3 + ((15 & e[t + 1]) << 8 | e[t + 2]) - 4; - for (t += 12 + ((15 & e[t + 10]) << 8 | e[t + 11]); t < s;) { - switch (r = (31 & e[t + 1]) << 8 | e[t + 2], e[t]) { - case 207: - n.audioEncrypted = !0; - case 15: - -1 === n.audioId && (n.audioId = r, n.audioSegmentCodec = "aac"); - break; - case 21: - -1 === n.id3Id && (n.id3Id = r); - break; - case 219: - n.videoEncrypted = !0; - case 27: - -1 === n.avcId && (n.avcId = r); - break; - case 3: - case 4: - !0 !== i.mpeg && !0 !== i.mp3 ? this.logger.warn(it, "MPEG audio found, not supported in this browser for now") : -1 === n.audioId && (n.audioId = r, n.audioSegmentCodec = "mp3"); - break; - case 193: - n.audioEncrypted = !0; - case 129: - !0 !== i.ac3 ? this.logger.warn(it, "AC-3 audio found, not supported in this browser for now") : -1 === n.audioId && (n.audioId = r, n.audioSegmentCodec = "ac3"); - break; - case 194: - n.audioEncrypted = !0; - case 135: - !0 !== i.ec3 ? this.logger.warn(it, "EC-3 audio found, not supported in this browser for now") : -1 === n.audioId && (n.audioId = r, n.audioSegmentCodec = "ec3"); - break; - case 36: - this.logger.warn(it, "HEVC stream type found, not supported for now"); - break; - default: - this.logger.warn(it, "unkown stream type:" + e[t]) - } - t += 5 + ((15 & e[t + 3]) << 8 | e[t + 4]) - } - return n - } - _parsePES(e) { - let i, t, r, n, s, a, o = 0; - const l = e.data, - d = e.keyTagInfo; - let u = NaN, - c = NaN; - if (e && 0 !== e.size) { - for (; l[0].length < 19 && 1 < l.length;) { - const e = new Uint8Array(l[0].length + l[1].length); - e.set(l[0]), e.set(l[1], l[0].length), l[0] = e, l.splice(1, 1) - } - if (i = l[0], 1 === (i[0] << 16) + (i[1] << 8) + i[2] && (r = (i[4] << 8) + i[5], !(r && r > e.size - 6))) { - 192 & (t = i[7]) && (u = 536870912 * (14 & i[9]) + 4194304 * (255 & i[10]) + 16384 * (254 & i[11]) + 128 * (255 & i[12]) + (254 & i[13]) / 2, 64 & t ? (c = 536870912 * (14 & i[14]) + 4194304 * (255 & i[15]) + 16384 * (254 & i[16]) + 128 * (255 & i[17]) + (254 & i[18]) / 2, 54e5 < u - c && (this.logger.warn(it, `${Math.round((u-c)/9e4)}s delta between PTS and DTS, align them`), u = c)) : c = u), n = i[8], a = n + 9, e.size -= a, s = new Uint8Array(e.size); - for (let t = 0, e = l.length; t < e; t++) { - i = l[t]; - let e = i.byteLength; - if (a) { - if (a > e) { - a -= e; - continue - } - i = i.subarray(a), e -= a, a = 0 - } - s.set(i, o), o += e - } - return r && (r -= n + 3), { - data: s, - pts: u, - dts: c, - len: r, - keyTagInfo: d - } - } - } - } - pushAccesUnit(e, t) { - const i = e.avcSample; - if (i && i.units.length && i.frame) { - const r = e.parsingData.esSamples, - n = r.length; - (!0 === i.key || e.config.sps && (n || this.contiguous)) && (i.id = n, i.keyTagInfo = t, e.info.encrypted) && i.units.forEach(e => { - if (48 < e.data.byteLength) switch (e.type) { - case 1: - case 5: - e.data = this.discardEPB(e.data) - } - }), n || isFinite(i.pts) ? r.push(i) : e.parsingData.dropped++ - } - } - _parseAVCPES(o, e) { - if (!o.data) throw "invalid pes data"; - const l = this._avcContext, - t = this._parseAVCNALu(o.data); - let d, u, c, h = l.avcSample; - const p = o.keyTagInfo; - o.data = void 0, t.forEach(n => { - switch (n.type) { - case 1: - if (h && !this.iframeMode) { - u = !0, h.frame = !0; - const o = n.data; - if (4 < o.length) { - const n = new tt(o, this.logger).readSliceType(); - 2 !== n && 4 !== n && 7 !== n && 9 !== n || (h.key = !0) - } - } - break; - case 5: - u = !0, h && (h = h || (l.avcSample = this._createAVCSample(!0, o.pts, o.dts, "")), h.key = !0, h.frame = !0); - break; - case 6: - u = !0, d = new tt(this.discardEPB(n.data), this.logger), d.readUByte(); - let e = 0, - t = 0, - i = !1, - r = 0; - for (; !i && 1 < d.bytesAvailable;) { - for (e = 0; r = d.readUByte(), e += r, 255 === r;); - for (t = 0; r = d.readUByte(), t += r, 255 === r;); - if (4 === e && 0 !== d.bytesAvailable) { - if (i = !0, 181 === d.readUByte() && 49 === d.readUShort() && 1195456820 === d.readUInt() && 3 === d.readUByte()) { - const n = d.readUByte(), - l = 31 & n, - s = [n, d.readUByte()]; - for (c = 0; c < l; c++) s.push(d.readUByte()), s.push(d.readUByte()), s.push(d.readUByte()); - this._insertSampleInOrder(this._txtTrack.captionSamples, { - type: 3, - pts: o.pts, - bytes: s - }) - } - } else if (t < d.bytesAvailable) - for (c = 0; c < t; c++) d.readUByte() - } - break; - case 7: - if (u = !0, !l.config.sps) { - d = new tt(n.data, this.logger); - const o = d.readSPS(); - l.config.width = o.width, l.config.height = o.height, l.config.pixelRatio = o.pixelRatio, l.config.sps = [n.data], l.info.duration = this._duration; - const a = n.data.subarray(1, 4); - let t = "avc1."; - for (c = 0; c < 3; c++) { - let e = a[c].toString(16); - e.length < 2 && (e = "0" + e), t += e - } - l.config.codec = t - } - break; - case 8: - u = !0, l.config.pps || (l.config.pps = [n.data]); - break; - case 9: - u = !1, h && this.pushAccesUnit(l, p), h = l.avcSample = this._createAVCSample(!1, o.pts, o.dts, ""); - break; - case 12: - u = !1; - break; - default: - u = !1, h && (h.debug += "unknown NAL " + n.type + " ") - } - h && u && h.units.push(n) - }), e && h && (this.pushAccesUnit(l, p), l.avcSample = void 0) - } - _createAVCSample(e, t, i, r) { - return { - id: NaN, - key: e, - pts: t, - dts: i, - units: new Array, - debug: r - } - } - _insertSampleInOrder(t, i) { - var r = t.length; - if (0 < r) { - if (i.pts >= t[r - 1].pts) t.push(i); - else - for (let e = r - 1; 0 <= e; e--) - if (i.pts < t[e].pts) { - t.splice(e, 0, i); - break - } - } else t.push(i) - } - _getLastNalUnit() { - const e = this._avcContext; - let t, i = e.avcSample; - if (!i || 0 === i.units.length) { - const t = e.parsingData.esSamples; - i = t[t.length - 1] - } - if (i) { - const e = i.units; - t = e[e.length - 1] - } - return t - } - _parseAVCNALu(e) { - const t = e.byteLength; - let i, r, n = 0; - const s = this._avcContext; - let a = s.naluState || 0; - const o = a, - l = []; - let d, u, c, h = -1; - for (-1 === a && (h = 0, c = 31 & e[0], a = 0, n = 1); n < t;) - if (i = e[n++], a) - if (1 !== a) - if (i) - if (1 === i) { - if (0 <= h) d = { - data: e.subarray(h, n - a - 1), - type: c - }, l.push(d); - else { - const t = this._getLastNalUnit(); - if (t && (o && n <= 4 - o && t.state && (t.data = t.data.subarray(0, t.data.byteLength - o)), r = n - a - 1, 0 < r)) { - const i = new Uint8Array(t.data.byteLength + r); - i.set(t.data, 0), i.set(e.subarray(0, r), t.data.byteLength), t.data = i, t.state = 0 - } - } - a = n < t ? (u = 31 & e[n], h = n, c = u, 0) : -1 - } else a = 0; - else a = 3; - else a = i ? 0 : 2; - else a = i ? 0 : 1; - if (0 <= h && 0 <= a && (d = { - data: e.subarray(h, t), - type: c, - state: a - }, l.push(d)), 0 === l.length) { - const t = this._getLastNalUnit(); - if (t) { - const i = new Uint8Array(t.data.byteLength + e.byteLength); - i.set(t.data, 0), i.set(e, t.data.byteLength), t.data = i - } - } - return s.naluState = a, l - } - discardEPB(e) { - const t = e.byteLength, - i = []; - let r = 1; - for (; r < t - 2;) 0 === e[r] && 0 === e[r + 1] && 3 === e[r + 2] ? (i.push(r + 2), r += 2) : r++; - if (0 === i.length) return e; - const n = t - i.length, - s = new Uint8Array(n); - let a = 0; - for (r = 0; r < n; a++, r++) a === i[0] && (a++, i.shift()), s[r] = e[a]; - return s - } - _parseAACPES(e) { - const t = this._audioContext, - i = t.audioLastPTS, - r = e.keyTagInfo; - let n, s, a, o, l, d, u, c = e.data, - h = e.pts, - p = t.audioOverFlow; - if (p) { - const e = new Uint8Array(p.byteLength + c.byteLength); - e.set(p, 0), e.set(c, p.byteLength), c = e - } - for (a = 0, d = c.length; a < d - 1 && (255 !== c[a] || 240 != (240 & c[a + 1])); a++); - if (a) { - let e, t, i; - i = a < d - 1 ? (e = `AAC PES did not start with ADTS header,offset:${a}`, t = !1, $.PESDidNotStartWithADTS) : (e = "no ADTS header found in AAC PES", t = !0, $.NoADTSHeaderInPES), this.logger.warn(it, `parsing error:${e}`); - const r = new D(t, e, i); - if (this.observer.trigger(x.INTERNAL_ERROR, r), t) return - } - if (!t.config) { - const e = E(this.observer, c, a, void 0, this.logger); - if (!e) throw "unable to parse adts header"; - t.config = e - } - s = 0; - var f = 9216e4 / t.config.samplerate; - if (p && i) { - const e = i + f; - 1 < Math.abs(e - h) && (h = e) - } - for (; a + 5 < d && (o = 1 & c[a + 1] ? 7 : 9, n = (3 & c[a + 3]) << 11 | c[a + 4] << 3 | (224 & c[a + 5]) >>> 5, n -= o, 0 < n && a + o + n <= d);) - for (l = h + s * f, u = { - unit: c.subarray(a + o, a + o + n), - pts: l, - dts: l, - keyTagInfo: r - }, t.parsingData.esSamples.push(u), t.parsingData.len += n, a += n + o, s++; a < d - 1 && (255 !== c[a] || 240 != (240 & c[a + 1])); a++); - p = a < d ? c.subarray(a, d) : void 0, t.audioOverFlow = p, t.audioLastPTS = l - } - _parseMPEGPES(e) { - "mp3" === this._audioContext.segmentCodec && ee.parse(this._audioContext.parsingData, e.data, 0, e.pts, this.logger) - } - _parseDolbyPES(e) { - const t = this._audioContext; - let i = e.data, - r = e.pts; - var n = e.keyTagInfo; - let s = 0, - a = 0, - o = t.audioOverFlow; - e = t.audioLastPTS; - if (!t.config) { - let e; - if ("ac3" === t.segmentCodec ? e = j(this.observer, i, a, this.logger) : "ec3" === t.segmentCodec && (e = Y(this.observer, i, a, this.logger)), !e) throw "unable to parse dolby header"; - t.config = e - } - if ("ac3" !== t.config.segmentCodec && "ec3" !== t.config.segmentCodec) throw "unexpected config type"; - var l = 1536 / t.config.samplerate * t.info.inputTimescale; - if (o) { - const c = new Uint8Array(o.byteLength + i.byteLength); - c.set(o, 0), c.set(i, o.byteLength), i = c - } - var d = i.length; - if (o && e) { - const c = e + l; - 1 < Math.abs(c - r) && (r = c) - } - let u = 0; - for (; a + u <= d;) { - if (11 !== i[a] || 119 !== i[a + 1]) { - const c = new D(!0, "invalid dolby audio magic", $.InvalidDolbyAudioMagic); - return void this.observer.trigger(x.INTERNAL_ERROR, c) - } - "ac3" === t.segmentCodec ? u = Q(this.observer, i, a) : "ec3" === t.segmentCodec && (u = X(this.observer, i, a, this.logger)); - const c = r + s * l; - t.audioLastPTS = c; - const o = { - unit: i.subarray(a, a + u), - pts: c, - dts: c, - keyTagInfo: n - }; - t.parsingData.esSamples.push(o), t.info.duration = this._duration, t.parsingData.len += u, a += u, s++ - } - o = a < d ? i.subarray(a, d) : void 0, t.audioOverFlow = o - } - }; - class st extends a { - constructor(e, t, i, r) { - super(), this.typeSupported = e, this.config = t, this.vendor = i, this.logger = r - } - destroy() { - this.removeAllListeners(); - const e = this.demuxer, - t = this.remuxer; - e && e.destroy(), t && t.destroy() - } - push(t, i, r, n, s, a, o, l, d, u, c, h) { - if (t) { - let e = this.demuxer; - var p = new Uint8Array(t); - if (!e || (s || a) && !this.probeFn(p, this.logger)) { - const { - typeSupported: t, - config: i - } = this, r = [{ - demux: Ze, - remux: ze - }, { - demux: nt, - remux: Ee - }, { - demux: J, - remux: Ee - }, { - demux: z, - remux: Ee - }, { - demux: K, - remux: Ee - }, { - demux: ie, - remux: Ee - }]; - for (const n of r) { - const r = n.demux["probe"]; - if (r(p, this.logger)) { - this.remuxer = new n.remux(this, i, t, this.vendor, this.logger), e = new n.demux(this, this.remuxer, i, t, this.logger), this.probeFn = r; - break - } - } - if (!e) { - const t = new D(!0, "no demux matching with content found", $.DemuxerNotFound); - return void this.trigger(x.INTERNAL_ERROR, t) - } - this.demuxer = e - } - const f = this.remuxer, - m = !this.lastKeyTagInfo || i && "NONE" !== i.method && this.lastKeyTagInfo.uri !== i.uri; - if (this.lastKeyTagInfo = i, (s || a || m) && (e.resetInitSegment(new Uint8Array(r), l, i, s), f.resetInitSegment()), s) { - const t = u ? S(u) : void 0; - e.resetTimeStamp(t), f.resetTimeStamp(t) - } - e.append(p, n, o, d, i, c, h) - } - } - } - - function at() { - let e = `${Date.now()}-${Math.random()}`; - return "undefined" != typeof performance && "function" == typeof performance.now && (e += `-${performance.now()}`), e - } - class ot { - constructor(e, t) { - this.rpc = e, this.logger = t, this.init = (t, n, s) => e => { - const i = at(), - r = this.demuxers[i] = new st(t, n, s, this.logger); - [v.INIT_PTS_FOUND, v.FRAG_PARSING_INIT_SEGMENT, v.FRAG_PARSING_DATA, v.FRAG_PARSED, x.INTERNAL_ERROR].forEach(t => { - r.on(t, e => this.rpc.invoke("demuxer.event", [i, t, e])(() => {})) - }), e(i) - }, this.push = (i, r, n, s, a, o, l, d, u, c, h, p, f) => e => { - const t = this.demuxers[i]; - t ? (t.push(r, n, s, a, o, l, d, u, c, h, p, f), e()) : e(void 0, `Demuxer with id "${i}" does not exist on push`) - }, this.destroy = i => e => { - const t = this.demuxers[i]; - t ? (t.destroy(), delete this.demuxers[i], e()) : this.logger.error(`Demuxer with id "${i}" does not exist on destroy`) - }, this.demuxers = {}, e.register("demuxer.init", this.init), e.register("demuxer.push", this.push), e.register("demuxer.destroy", this.destroy) - } - } - class lt { - constructor(e) { - this.worker = e, this.handlers = {}, this.deferers = {}, this._messageHandler = e => { - var { - type: t, - id: i, - command: r, - args: n, - result: e, - error: s - } = e.data; - if (t === rt.Invoke) try { - if (null == this.handlers[r]) throw new Error(`command ${r} not found`); - this.handlers[r](...n)(this._respond.bind(this, i, r)) - } catch (s) { - this._respond(i, r, null, new Error(`command ${r} not found`)) - } else t === rt.Result && null != this.deferers[i] && (this.deferers[i](e, s), delete this.deferers[i]) - }, e.addEventListener("message", this._messageHandler) - } - register(e, t) { - if (null != this.handlers[e]) return !1; - this.handlers[e] = t - } - unregister(e) { - if (null != this.handlers[e]) return !1; - delete this.handlers[e] - } - invoke(i, r, n) { - return (e = lt._fallbackCallback) => { - var t = at(); - this.deferers[t] = e; - t = { - type: rt.Invoke, - id: t, - command: i, - args: r - }; - this._send(t, n) - } - } - teardown(e) { - this.worker.removeEventListener("message", this._messageHandler), e() - } - _respond(e, t, i, r, n) { - r instanceof Error && (r = `[${r.name}] ${r.message}\n${r.stack}`); - r = { - type: rt.Result, - id: e, - command: t, - result: i, - error: r - }; - this._send(r, n) - } - _send(e, t = []) { - this.worker.postMessage(e, t.map(e => ArrayBuffer.isView(e) ? e.buffer : e).filter(e => void 0 !== e)) - } - } - lt._fallbackCallback = (e, t) => { - if (null != t) throw t - }, (vr = rt = rt || {})[vr.Invoke = 0] = "Invoke", vr[vr.Result = 1] = "Result", ArrayBuffer.isView || (ArrayBuffer.isView = function(e) { - return null !== e && "object" == typeof e && e.buffer instanceof ArrayBuffer - }), void 0 !== Yy && Yy && (Vr = new lt(l), iu = (n => { - const t = (i = []) => { - const r = {}; - return ["fatal", "error", "warn", "info", "debug", "trace", "qe"].forEach(e => { - return r[e] = (t = e, (...e) => { - n.invoke("logger.log", [i, t, ...e])((e, t) => { - if (null != t) throw t - }) - }); - var t - }), r.child = e => t([...i, e]), r - }; - return t() - })(Vr), new i(Vr, iu), new ot(Vr, iu)); - var dt = function(e, t) { - return (dt = Object.setPrototypeOf || { - __proto__: [] - } - instanceof Array && function(e, t) { - e.__proto__ = t - } || function(e, t) { - for (var i in t) Object.prototype.hasOwnProperty.call(t, i) && (e[i] = t[i]) - })(e, t) - }; - - function ut(e, t) { - if ("function" != typeof t && null !== t) throw new TypeError("Class extends value " + String(t) + " is not a constructor or null"); - - function i() { - this.constructor = e - } - dt(e, t), e.prototype = null === t ? Object.create(t) : (i.prototype = t.prototype, new i) - } - var ct = function() { - return (ct = Object.assign || function(e) { - for (var t, i = 1, r = arguments.length; i < r; i++) - for (var n in t = arguments[i]) Object.prototype.hasOwnProperty.call(t, n) && (e[n] = t[n]); - return e - }).apply(this, arguments) - }; - - function ht(e, t, i, r) { - var n, s = arguments.length, - a = s < 3 ? t : null === r ? r = Object.getOwnPropertyDescriptor(t, i) : r; - if ("object" == typeof Reflect && "function" == typeof Reflect.decorate) a = Reflect.decorate(e, t, i, r); - else - for (var o = e.length - 1; 0 <= o; o--)(n = e[o]) && (a = (s < 3 ? n(a) : 3 < s ? n(t, i, a) : n(t, i)) || a); - return 3 < s && a && Object.defineProperty(t, i, a), a - } - - function pt(e, t) { - if ("object" == typeof Reflect && "function" == typeof Reflect.metadata) return Reflect.metadata(e, t) - } - - function ft(e) { - var t = "function" == typeof Symbol && Symbol.iterator, - i = t && e[t], - r = 0; - if (i) return i.call(e); - if (e && "number" == typeof e.length) return { - next: function() { - return { - value: (e = e && r >= e.length ? void 0 : e) && e[r++], - done: !e - } - } - }; - throw new TypeError(t ? "Object is not iterable." : "Symbol.iterator is not defined.") - } - - function mt(e, t) { - var i = "function" == typeof Symbol && e[Symbol.iterator]; - if (!i) return e; - var r, n, s = i.call(e), - a = []; - try { - for (; - (void 0 === t || 0 < t--) && !(r = s.next()).done;) a.push(r.value) - } catch (e) { - n = { - error: e - } - } finally { - try { - r && !r.done && (i = s.return) && i.call(s) - } finally { - if (n) throw n.error - } - } - return a - } - - function gt() { - for (var e = [], t = 0; t < arguments.length; t++) e = e.concat(mt(arguments[t])); - return e - } - - function yt(e) { - return "function" == typeof e - } - var vt = !1, - St = { - Promise: void 0, - set useDeprecatedSynchronousErrorHandling(e) { - vt = e - }, - get useDeprecatedSynchronousErrorHandling() { - return vt - } - }; - - function bt(e) { - setTimeout(function() { - throw e - }, 0) - } - var Tt = { - closed: !0, - next: function(e) {}, - error: function(e) { - if (St.useDeprecatedSynchronousErrorHandling) throw e; - bt(e) - }, - complete: function() {} - }, - Et = Array.isArray || function(e) { - return e && "number" == typeof e.length - }; - - function It(e) { - return null !== e && "object" == typeof e - } - var wt = (kt.prototype = Object.create(Error.prototype), kt), - At = (Ot.prototype.unsubscribe = function() { - var t; - if (!this.closed) { - var e = this._parentOrParents, - i = this._ctorUnsubscribe, - r = this._unsubscribe, - n = this._subscriptions; - if (this.closed = !0, this._parentOrParents = null, this._subscriptions = null, e instanceof Ot) e.remove(this); - else if (null !== e) - for (var s = 0; s < e.length; ++s) e[s].remove(this); - if (yt(r)) { - i && (this._unsubscribe = void 0); - try { - r.call(this) - } catch (e) { - t = e instanceof wt ? Ct(e.errors) : [e] - } - } - if (Et(n)) - for (var s = -1, a = n.length; ++s < a;) { - var o = n[s]; - if (It(o)) try { - o.unsubscribe() - } catch (e) { - t = t || [], e instanceof wt ? t = t.concat(Ct(e.errors)) : t.push(e) - } - } - if (t) throw new wt(t) - } - }, Ot.prototype.add = function(e) { - var t, i = e; - if (!e) return Ot.EMPTY; - switch (typeof e) { - case "function": - i = new Ot(e); - case "object": - if (i === this || i.closed || "function" != typeof i.unsubscribe) return i; - if (this.closed) return i.unsubscribe(), i; - i instanceof Ot || (t = i, (i = new Ot)._subscriptions = [t]); - break; - default: - throw new Error("unrecognized teardown " + e + " added to Subscription.") - } - var r = i._parentOrParents; - if (null === r) i._parentOrParents = this; - else if (r instanceof Ot) { - if (r === this) return i; - i._parentOrParents = [r, this] - } else { - if (-1 !== r.indexOf(this)) return i; - r.push(this) - } - r = this._subscriptions; - return null === r ? this._subscriptions = [i] : r.push(i), i - }, Ot.prototype.remove = function(e) { - var t = this._subscriptions; - !t || -1 !== (e = t.indexOf(e)) && t.splice(e, 1) - }, Ot.EMPTY = ((dl = new Ot).closed = !0, dl), Ot); - - function Ot(e) { - this.closed = !1, this._parentOrParents = null, this._subscriptions = null, e && (this._ctorUnsubscribe = !0, this._unsubscribe = e) - } - - function kt(e) { - return Error.call(this), this.message = e ? e.length + " errors occurred during unsubscription:\n" + e.map(function(e, t) { - return t + 1 + ") " + e.toString() - }).join("\n ") : "", this.name = "UnsubscriptionError", this.errors = e, this - } - - function Ct(e) { - return e.reduce(function(e, t) { - return e.concat(t instanceof wt ? t.errors : t) - }, []) - } - var Dt, Mt, xt = "function" == typeof Symbol ? Symbol("rxSubscriber") : "@@rxSubscriber_" + Math.random(), - Pt = (ut(_t, Mt = At), _t.prototype[xt] = function() { - return this - }, _t.create = function(e, t, i) { - i = new _t(e, t, i); - return i.syncErrorThrowable = !1, i - }, _t.prototype.next = function(e) { - this.isStopped || this._next(e) - }, _t.prototype.error = function(e) { - this.isStopped || (this.isStopped = !0, this._error(e)) - }, _t.prototype.complete = function() { - this.isStopped || (this.isStopped = !0, this._complete()) - }, _t.prototype.unsubscribe = function() { - this.closed || (this.isStopped = !0, Mt.prototype.unsubscribe.call(this)) - }, _t.prototype._next = function(e) { - this.destination.next(e) - }, _t.prototype._error = function(e) { - this.destination.error(e), this.unsubscribe() - }, _t.prototype._complete = function() { - this.destination.complete(), this.unsubscribe() - }, _t.prototype._unsubscribeAndRecycle = function() { - var e = this._parentOrParents; - return this._parentOrParents = null, this.unsubscribe(), this.closed = !1, this.isStopped = !1, this._parentOrParents = e, this - }, _t), - Rt = (ut(Lt, Dt = Pt), Lt.prototype.next = function(e) { - var t; - !this.isStopped && this._next && (t = this._parentSubscriber, St.useDeprecatedSynchronousErrorHandling && t.syncErrorThrowable ? this.__tryOrSetError(t, this._next, e) && this.unsubscribe() : this.__tryOrUnsub(this._next, e)) - }, Lt.prototype.error = function(e) { - if (!this.isStopped) { - var t = this._parentSubscriber, - i = St.useDeprecatedSynchronousErrorHandling; - if (this._error) i && t.syncErrorThrowable ? this.__tryOrSetError(t, this._error, e) : this.__tryOrUnsub(this._error, e), this.unsubscribe(); - else if (t.syncErrorThrowable) i ? (t.syncErrorValue = e, t.syncErrorThrown = !0) : bt(e), this.unsubscribe(); - else { - if (this.unsubscribe(), i) throw e; - bt(e) - } - } - }, Lt.prototype.complete = function() { - var e, t, i = this; - this.isStopped || (e = this._parentSubscriber, this._complete && (t = function() { - return i._complete.call(i._context) - }, St.useDeprecatedSynchronousErrorHandling && e.syncErrorThrowable ? this.__tryOrSetError(e, t) : this.__tryOrUnsub(t)), this.unsubscribe()) - }, Lt.prototype.__tryOrUnsub = function(e, t) { - try { - e.call(this._context, t) - } catch (e) { - if (this.unsubscribe(), St.useDeprecatedSynchronousErrorHandling) throw e; - bt(e) - } - }, Lt.prototype.__tryOrSetError = function(e, t, i) { - if (!St.useDeprecatedSynchronousErrorHandling) throw new Error("bad call"); - try { - t.call(this._context, i) - } catch (t) { - return St.useDeprecatedSynchronousErrorHandling ? (e.syncErrorValue = t, e.syncErrorThrown = !0) : bt(t), !0 - } - return !1 - }, Lt.prototype._unsubscribe = function() { - var e = this._parentSubscriber; - this._context = null, this._parentSubscriber = null, e.unsubscribe() - }, Lt); - - function Lt(e, t, i, r) { - var n, s = Dt.call(this) || this; - s._parentSubscriber = e; - e = s; - return yt(t) ? n = t : t && (n = t.next, i = t.error, r = t.complete, t !== Tt && (yt((e = Object.create(t)).unsubscribe) && s.add(e.unsubscribe.bind(e)), e.unsubscribe = s.unsubscribe.bind(s))), s._context = e, s._next = n, s._error = i, s._complete = r, s - } - - function _t(e, t, i) { - var r = Mt.call(this) || this; - switch (r.syncErrorValue = null, r.syncErrorThrown = !1, r.syncErrorThrowable = !1, r.isStopped = !1, arguments.length) { - case 0: - r.destination = Tt; - break; - case 1: - if (!e) { - r.destination = Tt; - break - } - if ("object" == typeof e) { - e instanceof _t ? (r.syncErrorThrowable = e.syncErrorThrowable, (r.destination = e).add(r)) : (r.syncErrorThrowable = !0, r.destination = new Rt(r, e)); - break - } - default: - r.syncErrorThrowable = !0, r.destination = new Rt(r, e, t, i) - } - return r - } - var Nt = "function" == typeof Symbol && Symbol.observable || "@@observable"; - - function Ft(e) { - return e - } - - function Bt() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; - return Ut(e) - } - - function Ut(t) { - return 0 === t.length ? Ft : 1 === t.length ? t[0] : function(e) { - return t.reduce(function(e, t) { - return t(e) - }, e) - } - } - var $t = (Vt.prototype.lift = function(e) { - var t = new Vt; - return t.source = this, t.operator = e, t - }, Vt.prototype.subscribe = function(e, t, i) { - var r = this.operator, - i = function(e, t, i) { - if (e) { - if (e instanceof Pt) return e; - if (e[xt]) return e[xt]() - } - return e || t || i ? new Pt(e, t, i) : new Pt(Tt) - }(e, t, i); - if (r ? i.add(r.call(i, this.source)) : i.add(this.source || St.useDeprecatedSynchronousErrorHandling && !i.syncErrorThrowable ? this._subscribe(i) : this._trySubscribe(i)), St.useDeprecatedSynchronousErrorHandling && i.syncErrorThrowable && (i.syncErrorThrowable = !1, i.syncErrorThrown)) throw i.syncErrorValue; - return i - }, Vt.prototype._trySubscribe = function(t) { - try { - return this._subscribe(t) - } catch (e) { - St.useDeprecatedSynchronousErrorHandling && (t.syncErrorThrown = !0, t.syncErrorValue = e), - function(e) { - for (; e;) { - var t = e, - i = t.closed, - r = t.destination, - t = t.isStopped; - if (i || t) return; - e = r && r instanceof Pt ? r : null - } - return 1 - }(t) ? t.error(e) : console.warn(e) - } - }, Vt.prototype.forEach = function(r, e) { - var n = this; - return new(e = Kt(e))(function(e, t) { - var i = n.subscribe(function(e) { - try { - r(e) - } catch (e) { - t(e), i && i.unsubscribe() - } - }, t, e) - }) - }, Vt.prototype._subscribe = function(e) { - var t = this.source; - return t && t.subscribe(e) - }, Vt.prototype[Nt] = function() { - return this - }, Vt.prototype.pipe = function() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; - return 0 === e.length ? this : Ut(e)(this) - }, Vt.prototype.toPromise = function(e) { - var r = this; - return new(e = Kt(e))(function(e, t) { - var i; - r.subscribe(function(e) { - return i = e - }, function(e) { - return t(e) - }, function() { - return e(i) - }) - }) - }, Vt.create = function(e) { - return new Vt(e) - }, Vt); - - function Vt(e) { - this._isScalar = !1, e && (this._subscribe = e) - } - - function Kt(e) { - if (!(e = e || (St.Promise || Promise))) throw new Error("no Promise impl found"); - return e - } - var qt, Ht, jt, Qt, Wt = (ii.prototype = Object.create(Error.prototype), ii), - Gt = (ut(ti, Qt = At), ti.prototype.unsubscribe = function() { - var e, t; - this.closed || (this.closed = !0, e = (t = this.subject).observers, this.subject = null, !e || 0 === e.length || t.isStopped || t.closed || -1 !== (t = e.indexOf(this.subscriber)) && e.splice(t, 1)) - }, ti), - zt = (ut(ei, jt = Pt), ei), - Xt = (ut(Zt, Ht = $t), Zt.prototype[xt] = function() { - return new zt(this) - }, Zt.prototype.lift = function(e) { - var t = new Yt(this, this); - return t.operator = e, t - }, Zt.prototype.next = function(e) { - if (this.closed) throw new Wt; - if (!this.isStopped) - for (var t = this.observers, i = t.length, r = t.slice(), n = 0; n < i; n++) r[n].next(e) - }, Zt.prototype.error = function(e) { - if (this.closed) throw new Wt; - this.hasError = !0, this.thrownError = e, this.isStopped = !0; - for (var t = this.observers, i = t.length, r = t.slice(), n = 0; n < i; n++) r[n].error(e); - this.observers.length = 0 - }, Zt.prototype.complete = function() { - if (this.closed) throw new Wt; - this.isStopped = !0; - for (var e = this.observers, t = e.length, i = e.slice(), r = 0; r < t; r++) i[r].complete(); - this.observers.length = 0 - }, Zt.prototype.unsubscribe = function() { - this.isStopped = !0, this.closed = !0, this.observers = null - }, Zt.prototype._trySubscribe = function(e) { - if (this.closed) throw new Wt; - return Ht.prototype._trySubscribe.call(this, e) - }, Zt.prototype._subscribe = function(e) { - if (this.closed) throw new Wt; - return this.hasError ? (e.error(this.thrownError), At.EMPTY) : this.isStopped ? (e.complete(), At.EMPTY) : (this.observers.push(e), new Gt(this, e)) - }, Zt.prototype.asObservable = function() { - var e = new $t; - return e.source = this, e - }, Zt.create = function(e, t) { - return new Yt(e, t) - }, Zt), - Yt = (ut(Jt, qt = Xt), Jt.prototype.next = function(e) { - var t = this.destination; - t && t.next && t.next(e) - }, Jt.prototype.error = function(e) { - var t = this.destination; - t && t.error && this.destination.error(e) - }, Jt.prototype.complete = function() { - var e = this.destination; - e && e.complete && this.destination.complete() - }, Jt.prototype._subscribe = function(e) { - return this.source ? this.source.subscribe(e) : At.EMPTY - }, Jt); - - function Jt(e, t) { - var i = qt.call(this) || this; - return i.destination = e, i.source = t, i - } - - function Zt() { - var e = Ht.call(this) || this; - return e.observers = [], e.closed = !1, e.isStopped = !1, e.hasError = !1, e.thrownError = null, e - } - - function ei(e) { - var t = jt.call(this, e) || this; - return t.destination = e, t - } - - function ti(e, t) { - var i = Qt.call(this) || this; - return i.subject = e, i.subscriber = t, i.closed = !1, i - } - - function ii() { - return Error.call(this), this.message = "object unsubscribed", this.name = "ObjectUnsubscribedError", this - } - - function ri() { - return function(e) { - return e.lift(new pi(e)) - } - } - var ni, si, ai, oi, li, di, ui, ci, hi, pi = (Li.prototype.call = function(e, t) { - var i = this.connectable; - i._refCount++; - e = new fi(e, i), t = t.subscribe(e); - return e.closed || (e.connection = i.connect()), t - }, Li), - fi = (ut(Ri, hi = Pt), Ri.prototype._unsubscribe = function() { - var e, t = this.connectable; - t ? (this.connectable = null, (e = t._refCount) <= 0 ? this.connection = null : (t._refCount = e - 1, 1 < e ? this.connection = null : (e = this.connection, t = t._connection, this.connection = null, !t || e && t !== e || t.unsubscribe()))) : this.connection = null - }, Ri), - w = (ut(Pi, ci = $t), Pi.prototype._subscribe = function(e) { - return this.getSubject().subscribe(e) - }, Pi.prototype.getSubject = function() { - var e = this._subject; - return e && !e.isStopped || (this._subject = this.subjectFactory()), this._subject - }, Pi.prototype.connect = function() { - var e = this._connection; - return e || (this._isComplete = !1, (e = this._connection = new At).add(this.source.subscribe(new gi(this.getSubject(), this))), e.closed && (this._connection = null, e = At.EMPTY)), e - }, Pi.prototype.refCount = function() { - return ri()(this) - }, Pi), - mi = { - operator: { - value: null - }, - _refCount: { - value: 0, - writable: !0 - }, - _subject: { - value: null, - writable: !0 - }, - _connection: { - value: null, - writable: !0 - }, - _subscribe: { - value: (fl = w.prototype)._subscribe - }, - _isComplete: { - value: fl._isComplete, - writable: !0 - }, - getSubject: { - value: fl.getSubject - }, - connect: { - value: fl.connect - }, - refCount: { - value: fl.refCount - } - }, - gi = (ut(xi, ui = zt), xi.prototype._error = function(e) { - this._unsubscribe(), ui.prototype._error.call(this, e) - }, xi.prototype._complete = function() { - this.connectable._isComplete = !0, this._unsubscribe(), ui.prototype._complete.call(this) - }, xi.prototype._unsubscribe = function() { - var e, t = this.connectable; - t && (this.connectable = null, e = t._connection, t._refCount = 0, t._subject = null, t._connection = null, e && e.unsubscribe()) - }, xi), - yi = (ut(Mi, di = Xt), Object.defineProperty(Mi.prototype, "value", { - get: function() { - return this.getValue() - }, - enumerable: !0, - configurable: !0 - }), Mi.prototype._subscribe = function(e) { - var t = di.prototype._subscribe.call(this, e); - return t && !t.closed && e.next(this._value), t - }, Mi.prototype.getValue = function() { - if (this.hasError) throw this.thrownError; - if (this.closed) throw new Wt; - return this._value - }, Mi.prototype.next = function(e) { - di.prototype.next.call(this, this._value = e) - }, Mi), - A = (ut(Di, li = At), Di.prototype.schedule = function(e, t) { - return this - }, ut(Ci, oi = Di), Ci.prototype.schedule = function(e, t) { - if (void 0 === t && (t = 0), this.closed) return this; - this.state = e; - var i = this.id, - e = this.scheduler; - return null != i && (this.id = this.recycleAsyncId(e, i, t)), this.pending = !0, this.delay = t, this.id = this.id || this.requestAsyncId(e, this.id, t), this - }, Ci.prototype.requestAsyncId = function(e, t, i) { - return void 0 === i && (i = 0), setInterval(e.flush.bind(e, this), i) - }, Ci.prototype.recycleAsyncId = function(e, t, i) { - if (null !== (i = void 0 === i ? 0 : i) && this.delay === i && !1 === this.pending) return t; - clearInterval(t) - }, Ci.prototype.execute = function(e, t) { - if (this.closed) return new Error("executing a cancelled action"); - this.pending = !1; - t = this._execute(e, t); - if (t) return t; - !1 === this.pending && null != this.id && (this.id = this.recycleAsyncId(this.scheduler, this.id, null)) - }, Ci.prototype._execute = function(e, t) { - var i = !1, - r = void 0; - try { - this.work(e) - } catch (e) { - i = !0, r = !!e && e || new Error(e) - } - if (i) return this.unsubscribe(), r - }, Ci.prototype._unsubscribe = function() { - var e = this.id, - t = this.scheduler, - i = t.actions, - r = i.indexOf(this); - this.work = null, this.state = null, this.pending = !1, this.scheduler = null, -1 !== r && i.splice(r, 1), null != e && (this.id = this.recycleAsyncId(t, e, null)), this.delay = null - }, Ci), - vi = (ut(ki, ai = A), ki.prototype.schedule = function(e, t) { - return 0 < (t = void 0 === t ? 0 : t) ? ai.prototype.schedule.call(this, e, t) : (this.delay = t, this.state = e, this.scheduler.flush(this), this) - }, ki.prototype.execute = function(e, t) { - return 0 < t || this.closed ? ai.prototype.execute.call(this, e, t) : this._execute(e, t) - }, ki.prototype.requestAsyncId = function(e, t, i) { - return null !== (i = void 0 === i ? 0 : i) && 0 < i || null === i && 0 < this.delay ? ai.prototype.requestAsyncId.call(this, e, t, i) : e.flush(this) - }, ki), - Si = (Oi.prototype.schedule = function(e, t, i) { - return void 0 === t && (t = 0), new this.SchedulerAction(this, e).schedule(i, t) - }, Oi.now = function() { - return Date.now() - }, Oi), - bi = (ut(Ai, si = Si), Ai.prototype.schedule = function(e, t, i) { - return void 0 === t && (t = 0), Ai.delegate && Ai.delegate !== this ? Ai.delegate.schedule(e, t, i) : si.prototype.schedule.call(this, e, t, i) - }, Ai.prototype.flush = function(e) { - var t, i = this.actions; - if (this.active) i.push(e); - else { - this.active = !0; - do { - if (t = e.execute(e.state, e.delay)) break - } while (e = i.shift()); - if (this.active = !1, t) { - for (; e = i.shift();) e.unsubscribe(); - throw t - } - } - }, Ai), - Ti = (ut(wi, ni = bi), new wi(vi)), - Ei = Ti, - Ii = new $t(function(e) { - return e.complete() - }); - - function wi() { - return null !== ni && ni.apply(this, arguments) || this - } - - function Ai(e, t) { - void 0 === t && (t = Si.now); - var i = si.call(this, e, function() { - return Ai.delegate && Ai.delegate !== i ? Ai.delegate.now() : t() - }) || this; - return i.actions = [], i.active = !1, i.scheduled = void 0, i - } - - function Oi(e, t) { - void 0 === t && (t = Oi.now), this.SchedulerAction = e, this.now = t - } - - function ki(e, t) { - var i = ai.call(this, e, t) || this; - return i.scheduler = e, i.work = t, i - } - - function Ci(e, t) { - var i = oi.call(this, e, t) || this; - return i.scheduler = e, i.work = t, i.pending = !1, i - } - - function Di(e, t) { - return li.call(this) || this - } - - function Mi(e) { - var t = di.call(this) || this; - return t._value = e, t - } - - function xi(e, t) { - e = ui.call(this, e) || this; - return e.connectable = t, e - } - - function Pi(e, t) { - var i = ci.call(this) || this; - return i.source = e, i.subjectFactory = t, i._refCount = 0, i._isComplete = !1, i - } - - function Ri(e, t) { - e = hi.call(this, e) || this; - return e.connectable = t, e - } - - function Li(e) { - this.connectable = e - } - - function _i(e) { - return e ? (t = e, new $t(function(e) { - return t.schedule(function() { - return e.complete() - }) - })) : Ii; - var t - } - - function Ni(e) { - return e && "function" == typeof e.schedule - } - var Fi = function(r) { - return function(e) { - for (var t = 0, i = r.length; t < i && !e.closed; t++) e.next(r[t]); - e.complete() - } - }; - - function Bi(r, n) { - return new $t(function(e) { - var t = new At, - i = 0; - return t.add(n.schedule(function() { - i !== r.length ? (e.next(r[i++]), e.closed || t.add(this.schedule())) : e.complete() - })), t - }) - } - - function Ui(e, t) { - return t ? Bi(e, t) : new $t(Fi(e)) - } - - function $i() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; - var i = e[e.length - 1]; - return Ni(i) ? (e.pop(), Bi(e, i)) : Ui(e) - } - - function Vi(t, i) { - return new $t(i ? function(e) { - return i.schedule(Ki, 0, { - error: t, - subscriber: e - }) - } : function(e) { - return e.error(t) - }) - } - - function Ki(e) { - var t = e.error; - e.subscriber.error(t) - } - var qi = (Hi.prototype.observe = function(e) { - switch (this.kind) { - case "N": - return e.next && e.next(this.value); - case "E": - return e.error && e.error(this.error); - case "C": - return e.complete && e.complete() - } - }, Hi.prototype.do = function(e, t, i) { - switch (this.kind) { - case "N": - return e && e(this.value); - case "E": - return t && t(this.error); - case "C": - return i && i() - } - }, Hi.prototype.accept = function(e, t, i) { - return e && "function" == typeof e.next ? this.observe(e) : this.do(e, t, i) - }, Hi.prototype.toObservable = function() { - switch (this.kind) { - case "N": - return $i(this.value); - case "E": - return Vi(this.error); - case "C": - return _i() - } - throw new Error("unexpected notification kind value") - }, Hi.createNext = function(e) { - return void 0 !== e ? new Hi("N", e) : Hi.undefinedValueNotification - }, Hi.createError = function(e) { - return new Hi("E", void 0, e) - }, Hi.createComplete = function() { - return Hi.completeNotification - }, Hi.completeNotification = new Hi("C"), Hi.undefinedValueNotification = new Hi("N", void 0), Hi); - - function Hi(e, t, i) { - this.kind = e, this.value = t, this.error = i, this.hasValue = "N" === e - } - - function ji(t, i) { - return void 0 === i && (i = 0), - function(e) { - return e.lift(new zi(t, i)) - } - } - var Qi, Wi, Gi, zi = (ar.prototype.call = function(e, t) { - return t.subscribe(new Xi(e, this.scheduler, this.delay)) - }, ar), - Xi = (ut(sr, Gi = Pt), sr.dispatch = function(e) { - var t = e.notification, - e = e.destination; - t.observe(e), this.unsubscribe() - }, sr.prototype.scheduleMessage = function(e) { - this.destination.add(this.scheduler.schedule(sr.dispatch, this.delay, new Yi(e, this.destination))) - }, sr.prototype._next = function(e) { - this.scheduleMessage(qi.createNext(e)) - }, sr.prototype._error = function(e) { - this.scheduleMessage(qi.createError(e)), this.unsubscribe() - }, sr.prototype._complete = function() { - this.scheduleMessage(qi.createComplete()), this.unsubscribe() - }, sr), - Yi = function(e, t) { - this.notification = e, this.destination = t - }, - Ji = (ut(nr, Wi = Xt), nr.prototype.nextInfiniteTimeWindow = function(e) { - var t; - this.isStopped || ((t = this._events).push(e), t.length > this._bufferSize && t.shift()), Wi.prototype.next.call(this, e) - }, nr.prototype.nextTimeWindow = function(e) { - this.isStopped || (this._events.push(new Zi(this._getNow(), e)), this._trimBufferThenGetEvents()), Wi.prototype.next.call(this, e) - }, nr.prototype._subscribe = function(e) { - var t, i = this._infiniteTimeWindow, - r = i ? this._events : this._trimBufferThenGetEvents(), - n = this.scheduler, - s = r.length; - if (this.closed) throw new Wt; - if (t = this.isStopped || this.hasError ? At.EMPTY : (this.observers.push(e), new Gt(this, e)), n && e.add(e = new Xi(e, n)), i) - for (var a = 0; a < s && !e.closed; a++) e.next(r[a]); - else - for (a = 0; a < s && !e.closed; a++) e.next(r[a].value); - return this.hasError ? e.error(this.thrownError) : this.isStopped && e.complete(), t - }, nr.prototype._getNow = function() { - return (this.scheduler || Ei).now() - }, nr.prototype._trimBufferThenGetEvents = function() { - for (var e = this._getNow(), t = this._bufferSize, i = this._windowTime, r = this._events, n = r.length, s = 0; s < n && !(e - r[s].time < i);) s++; - return 0 < (s = t < n ? Math.max(s, n - t) : s) && r.splice(0, s), r - }, nr), - Zi = function(e, t) { - this.time = e, this.value = t - }, - er = (ut(rr, Qi = Xt), rr.prototype._subscribe = function(e) { - return this.hasError ? (e.error(this.thrownError), At.EMPTY) : this.hasCompleted && this.hasNext ? (e.next(this.value), e.complete(), At.EMPTY) : Qi.prototype._subscribe.call(this, e) - }, rr.prototype.next = function(e) { - this.hasCompleted || (this.value = e, this.hasNext = !0) - }, rr.prototype.error = function(e) { - this.hasCompleted || Qi.prototype.error.call(this, e) - }, rr.prototype.complete = function() { - this.hasCompleted = !0, this.hasNext && Qi.prototype.next.call(this, this.value), Qi.prototype.complete.call(this) - }, rr), - tr = new bi(A), - ir = tr; - - function rr() { - var e = null !== Qi && Qi.apply(this, arguments) || this; - return e.value = null, e.hasNext = !1, e.hasCompleted = !1, e - } - - function nr(e, t, i) { - void 0 === e && (e = Number.POSITIVE_INFINITY), void 0 === t && (t = Number.POSITIVE_INFINITY); - var r = Wi.call(this) || this; - return r.scheduler = i, r._events = [], r._infiniteTimeWindow = !1, r._bufferSize = e < 1 ? 1 : e, r._windowTime = t < 1 ? 1 : t, t === Number.POSITIVE_INFINITY ? (r._infiniteTimeWindow = !0, r.next = r.nextInfiniteTimeWindow) : r.next = r.nextTimeWindow, r - } - - function sr(e, t, i) { - void 0 === i && (i = 0); - e = Gi.call(this, e) || this; - return e.scheduler = t, e.delay = i, e - } - - function ar(e, t) { - void 0 === t && (t = 0), this.scheduler = e, this.delay = t - } - - function or() {} - var lr = (cr.prototype = Object.create(Error.prototype), cr), - dr = (ur.prototype = Object.create(Error.prototype), ur); - - function ur() { - return Error.call(this), this.message = "Timeout has occurred", this.name = "TimeoutError", this - } - - function cr() { - return Error.call(this), this.message = "argument out of range", this.name = "ArgumentOutOfRangeError", this - } - - function hr(t, i) { - return function(e) { - if ("function" != typeof t) throw new TypeError("argument is not a function. Are you looking for `mapTo()`?"); - return e.lift(new gr(t, i)) - } - } - var pr, fr, mr, gr = (Ir.prototype.call = function(e, t) { - return t.subscribe(new yr(e, this.project, this.thisArg)) - }, Ir), - yr = (ut(Er, mr = Pt), Er.prototype._next = function(e) { - var t; - try { - t = this.project.call(this.thisArg, e, this.count++) - } catch (e) { - return void this.destination.error(e) - } - this.destination.next(t) - }, Er), - vr = (ut(Tr, fr = Pt), Tr.prototype.notifyNext = function(e, t, i, r, n) { - this.destination.next(t) - }, Tr.prototype.notifyError = function(e, t) { - this.destination.error(e) - }, Tr.prototype.notifyComplete = function(e) { - this.destination.complete() - }, Tr), - Sr = (ut(br, pr = Pt), br.prototype._next = function(e) { - this.parent.notifyNext(this.outerValue, e, this.outerIndex, this.index++, this) - }, br.prototype._error = function(e) { - this.parent.notifyError(e, this), this.unsubscribe() - }, br.prototype._complete = function() { - this.parent.notifyComplete(this), this.unsubscribe() - }, br); - - function br(e, t, i) { - var r = pr.call(this) || this; - return r.parent = e, r.outerValue = t, r.outerIndex = i, r.index = 0, r - } - - function Tr() { - return null !== fr && fr.apply(this, arguments) || this - } - - function Er(e, t, i) { - e = mr.call(this, e) || this; - return e.project = t, e.count = 0, e.thisArg = i || e, e - } - - function Ir(e, t) { - this.project = e, this.thisArg = t - } - var wr = "function" == typeof Symbol && Symbol.iterator ? Symbol.iterator : "@@iterator", - Ar = function(e) { - return e && "number" == typeof e.length && "function" != typeof e - }; - - function Or(e) { - return e && "function" != typeof e.subscribe && "function" == typeof e.then - } - var kr = function(e) { - if (e && "function" == typeof e[Nt]) return n = e, - function(e) { - var t = n[Nt](); - if ("function" != typeof t.subscribe) throw new TypeError("Provided object does not correctly implement Symbol.observable"); - return t.subscribe(e) - }; - if (Ar(e)) return Fi(e); - if (Or(e)) return i = e, - function(t) { - return i.then(function(e) { - t.closed || (t.next(e), t.complete()) - }, function(e) { - return t.error(e) - }).then(null, bt), t - }; - if (e && "function" == typeof e[wr]) return r = e, - function(t) { - for (var e = r[wr]();;) { - var i = void 0; - try { - i = e.next() - } catch (e) { - return t.error(e), t - } - if (i.done) { - t.complete(); - break - } - if (t.next(i.value), t.closed) break - } - return "function" == typeof e.return && t.add(function() { - e.return && e.return() - }), t - }; - var r, i, n, e = It(e) ? "an invalid object" : "'" + e + "'"; - throw new TypeError("You provided " + e + " where a stream was expected. You can provide an Observable, Promise, Array, or Iterable.") - }; - - function Cr(e, t, i, r, n) { - if (!(n = void 0 === n ? new Sr(e, i, r) : n).closed) return t instanceof $t ? t.subscribe(n) : kr(t)(n) - } - var Dr = {}; - - function Mr() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; - var i = void 0, - r = void 0; - return Ni(e[e.length - 1]) && (r = e.pop()), "function" == typeof e[e.length - 1] && (i = e.pop()), Ui(e = 1 === e.length && Et(e[0]) ? e[0] : e, r).lift(new Pr(i)) - } - var xr, Pr = (_r.prototype.call = function(e, t) { - return t.subscribe(new Rr(e, this.resultSelector)) - }, _r), - Rr = (ut(Lr, xr = vr), Lr.prototype._next = function(e) { - this.values.push(Dr), this.observables.push(e) - }, Lr.prototype._complete = function() { - var e = this.observables, - t = e.length; - if (0 === t) this.destination.complete(); - else { - this.active = t, this.toRespond = t; - for (var i = 0; i < t; i++) { - var r = e[i]; - this.add(Cr(this, r, void 0, i)) - } - } - }, Lr.prototype.notifyComplete = function(e) { - 0 == --this.active && this.destination.complete() - }, Lr.prototype.notifyNext = function(e, t, i) { - var r = this.values, - n = r[i], - n = this.toRespond ? n === Dr ? --this.toRespond : this.toRespond : 0; - r[i] = t, 0 === n && (this.resultSelector ? this._tryResultSelector(r) : this.destination.next(r.slice())) - }, Lr.prototype._tryResultSelector = function(e) { - var t; - try { - t = this.resultSelector.apply(this, e) - } catch (e) { - return void this.destination.error(e) - } - this.destination.next(t) - }, Lr); - - function Lr(e, t) { - e = xr.call(this, e) || this; - return e.resultSelector = t, e.active = 0, e.values = [], e.observables = [], e - } - - function _r(e) { - this.resultSelector = e - } - - function Nr(e, t) { - if (null != e) { - if (e && "function" == typeof e[Nt]) return s = e, a = t, new $t(function(t) { - var i = new At; - return i.add(a.schedule(function() { - var e = s[Nt](); - i.add(e.subscribe({ - next: function(e) { - i.add(a.schedule(function() { - return t.next(e) - })) - }, - error: function(e) { - i.add(a.schedule(function() { - return t.error(e) - })) - }, - complete: function() { - i.add(a.schedule(function() { - return t.complete() - })) - } - })) - })), i - }); - if (Or(e)) return r = e, n = t, new $t(function(t) { - var i = new At; - return i.add(n.schedule(function() { - return r.then(function(e) { - i.add(n.schedule(function() { - t.next(e), i.add(n.schedule(function() { - return t.complete() - })) - })) - }, function(e) { - i.add(n.schedule(function() { - return t.error(e) - })) - }) - })), i - }); - if (Ar(e)) return Bi(e, t); - if (e && "function" == typeof e[wr] || "string" == typeof e) return function(t, i) { - if (!t) throw new Error("Iterable cannot be null"); - return new $t(function(r) { - var n, e = new At; - return e.add(function() { - n && "function" == typeof n.return && n.return() - }), e.add(i.schedule(function() { - n = t[wr](), e.add(i.schedule(function() { - if (!r.closed) { - try { - var e = n.next(), - t = e.value, - i = e.done - } catch (t) { - return void r.error(t) - } - i ? r.complete() : (r.next(t), this.schedule()) - } - })) - })), e - }) - }(e, t) - } - var r, n, s, a; - throw new TypeError((null !== e && typeof e || e) + " is not observable") - } - - function Fr(e, t) { - return t ? Nr(e, t) : e instanceof $t ? e : new $t(kr(e)) - } - var Br, Ur, $r = (ut(qr, Ur = Pt), qr.prototype._next = function(e) { - this.parent.notifyNext(e) - }, qr.prototype._error = function(e) { - this.parent.notifyError(e), this.unsubscribe() - }, qr.prototype._complete = function() { - this.parent.notifyComplete(), this.unsubscribe() - }, qr), - Vr = (ut(Kr, Br = Pt), Kr.prototype.notifyNext = function(e) { - this.destination.next(e) - }, Kr.prototype.notifyError = function(e) { - this.destination.error(e) - }, Kr.prototype.notifyComplete = function() { - this.destination.complete() - }, Kr); - - function Kr() { - return null !== Br && Br.apply(this, arguments) || this - } - - function qr(e) { - var t = Ur.call(this) || this; - return t.parent = e, t - } - - function Hr(e, t) { - if (!t.closed) return e instanceof $t ? e.subscribe(t) : kr(e)(t) - } - - function jr(t, n, i) { - return void 0 === i && (i = Number.POSITIVE_INFINITY), "function" == typeof n ? function(e) { - return e.pipe(jr(function(i, r) { - return Fr(t(i, r)).pipe(hr(function(e, t) { - return n(i, e, r, t) - })) - }, i)) - } : ("number" == typeof n && (i = n), function(e) { - return e.lift(new Wr(t, i)) - }) - } - var Qr, Wr = (Xr.prototype.call = function(e, t) { - return t.subscribe(new Gr(e, this.project, this.concurrent)) - }, Xr), - Gr = (ut(zr, Qr = Vr), zr.prototype._next = function(e) { - this.active < this.concurrent ? this._tryNext(e) : this.buffer.push(e) - }, zr.prototype._tryNext = function(e) { - var t, i = this.index++; - try { - t = this.project(e, i) - } catch (e) { - return void this.destination.error(e) - } - this.active++, this._innerSub(t) - }, zr.prototype._innerSub = function(e) { - var t = new $r(this), - i = this.destination; - i.add(t); - e = Hr(e, t); - e !== t && i.add(e) - }, zr.prototype._complete = function() { - this.hasCompleted = !0, 0 === this.active && 0 === this.buffer.length && this.destination.complete(), this.unsubscribe() - }, zr.prototype.notifyNext = function(e) { - this.destination.next(e) - }, zr.prototype.notifyComplete = function() { - var e = this.buffer; - this.active--, 0 < e.length ? this._next(e.shift()) : 0 === this.active && this.hasCompleted && this.destination.complete() - }, zr); - - function zr(e, t, i) { - void 0 === i && (i = Number.POSITIVE_INFINITY); - e = Qr.call(this, e) || this; - return e.project = t, e.concurrent = i, e.hasCompleted = !1, e.buffer = [], e.active = 0, e.index = 0, e - } - - function Xr(e, t) { - void 0 === t && (t = Number.POSITIVE_INFINITY), this.project = e, this.concurrent = t - } - - function Yr(e) { - return jr(Ft, e = void 0 === e ? Number.POSITIVE_INFINITY : e) - } - - function Jr() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; - return Yr(1)($i.apply(void 0, e)) - } - - function Zr(i) { - return new $t(function(t) { - var e; - try { - e = i() - } catch (e) { - return void t.error(e) - } - return (e ? Fr(e) : _i()).subscribe(t) - }) - } - - function en() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; - if (1 === e.length) { - var i = e[0]; - if (Et(i)) return tn(i, null); - if (It(i) && Object.getPrototypeOf(i) === Object.prototype) { - var r = Object.keys(i); - return tn(r.map(function(e) { - return i[e] - }), r) - } - } - if ("function" != typeof e[e.length - 1]) return tn(e, null); - var n = e.pop(); - return tn(e = 1 === e.length && Et(e[0]) ? e[0] : e, null).pipe(hr(function(e) { - return n.apply(void 0, e) - })) - } - - function tn(l, d) { - return new $t(function(r) { - var n = l.length; - if (0 !== n) - for (var s = new Array(n), a = 0, o = 0, e = 0; e < n; e++) ! function(t) { - var e = Fr(l[t]), - i = !1; - r.add(e.subscribe({ - next: function(e) { - i || (i = !0, o++), s[t] = e - }, - error: function(e) { - return r.error(e) - }, - complete: function() { - ++a !== n && i || (o === n && r.next(d ? d.reduce(function(e, t, i) { - return e[t] = s[i], e - }, {}) : s), r.complete()) - } - })) - }(e); - else r.complete() - }) - } - - function rn(e, i, r, t) { - return yt(r) && (t = r, r = void 0), t ? rn(e, i, r).pipe(hr(function(e) { - return Et(e) ? t.apply(void 0, e) : t(e) - })) : new $t(function(t) { - ! function e(t, i, r, n, s) { - var a; - if (function(e) { - return e && "function" == typeof e.addEventListener && "function" == typeof e.removeEventListener - }(t)) { - var o = t; - t.addEventListener(i, r, s), a = function() { - return o.removeEventListener(i, r, s) - } - } else if (function(e) { - return e && "function" == typeof e.on && "function" == typeof e.off - }(t)) { - var l = t; - t.on(i, r), a = function() { - return l.off(i, r) - } - } else if (function(e) { - return e && "function" == typeof e.addListener && "function" == typeof e.removeListener - }(t)) { - var d = t; - t.addListener(i, r), a = function() { - return d.removeListener(i, r) - } - } else { - if (!t || !t.length) throw new TypeError("Invalid event target"); - for (var u = 0, c = t.length; u < c; u++) e(t[u], i, r, n, s) - } - n.add(a) - }(e, i, function(e) { - 1 < arguments.length ? t.next(Array.prototype.slice.call(arguments)) : t.next(e) - }, t, r) - }) - } - - function nn(e, t, i) { - return void 0 === t && (t = Ii), void 0 === i && (i = Ii), Zr(function() { - return e() ? t : i - }) - } - - function sn(e) { - return !Et(e) && 0 <= e - parseFloat(e) + 1 - } - - function an() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; - var i = Number.POSITIVE_INFINITY, - r = null, - n = e[e.length - 1]; - return Ni(n) ? (r = e.pop(), 1 < e.length && "number" == typeof e[e.length - 1] && (i = e.pop())) : "number" == typeof n && (i = e.pop()), null === r && 1 === e.length && e[0] instanceof $t ? e[0] : Yr(i)(Ui(e, r)) - } - var on = new $t(or); - - function ln(t, i) { - return function(e) { - return e.lift(new un(t, i)) - } - } - var dn, un = (pn.prototype.call = function(e, t) { - return t.subscribe(new cn(e, this.predicate, this.thisArg)) - }, pn), - cn = (ut(hn, dn = Pt), hn.prototype._next = function(e) { - var t; - try { - t = this.predicate.call(this.thisArg, e, this.count++) - } catch (e) { - return void this.destination.error(e) - } - t && this.destination.next(e) - }, hn); - - function hn(e, t, i) { - e = dn.call(this, e) || this; - return e.predicate = t, e.thisArg = i, e.count = 0, e - } - - function pn(e, t) { - this.predicate = e, this.thisArg = t - } - - function fn() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; - if (1 === e.length) { - if (!Et(e[0])) return e[0]; - e = e[0] - } - return Ui(e, void 0).lift(new gn) - } - var mn, gn = (Sn.prototype.call = function(e, t) { - return t.subscribe(new yn(e)) - }, Sn), - yn = (ut(vn, mn = vr), vn.prototype._next = function(e) { - this.observables.push(e) - }, vn.prototype._complete = function() { - var e = this.observables, - t = e.length; - if (0 === t) this.destination.complete(); - else { - for (var i = 0; i < t && !this.hasFirst; i++) { - var r = Cr(this, e[i], void 0, i); - this.subscriptions && this.subscriptions.push(r), this.add(r) - } - this.observables = null - } - }, vn.prototype.notifyNext = function(e, t, i) { - if (!this.hasFirst) { - this.hasFirst = !0; - for (var r, n = 0; n < this.subscriptions.length; n++) n !== i && ((r = this.subscriptions[n]).unsubscribe(), this.remove(r)); - this.subscriptions = null - } - this.destination.next(t) - }, vn); - - function vn(e) { - e = mn.call(this, e) || this; - return e.hasFirst = !1, e.observables = [], e.subscriptions = [], e - } - - function Sn() {} - - function bn(i, e, r) { - void 0 === i && (i = 0); - var n = -1; - return sn(e) ? n = Number(e) < 1 ? 1 : Number(e) : Ni(e) && (r = e), Ni(r) || (r = ir), new $t(function(e) { - var t = sn(i) ? i : +i - r.now(); - return r.schedule(Tn, t, { - index: 0, - period: n, - subscriber: e - }) - }) - } - - function Tn(e) { - var t = e.index, - i = e.period, - r = e.subscriber; - if (r.next(t), !r.closed) { - if (-1 === i) return r.complete(); - e.index = t + 1, this.schedule(e, i) - } - } - - function En() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; - var i = e[e.length - 1]; - return "function" == typeof i && e.pop(), Ui(e, void 0).lift(new An(i)) - } - var In, wn, An = (Ln.prototype.call = function(e, t) { - return t.subscribe(new On(e, this.resultSelector)) - }, Ln), - On = (ut(Rn, wn = Pt), Rn.prototype._next = function(e) { - var t = this.iterators; - Et(e) ? t.push(new Cn(e)) : "function" == typeof e[wr] ? t.push(new kn(e[wr]())) : t.push(new Dn(this.destination, this, e)) - }, Rn.prototype._complete = function() { - var e = this.iterators, - t = e.length; - if (this.unsubscribe(), 0 !== t) { - this.active = t; - for (var i = 0; i < t; i++) { - var r = e[i]; - r.stillUnsubscribed ? this.destination.add(r.subscribe()) : this.active-- - } - } else this.destination.complete() - }, Rn.prototype.notifyInactive = function() { - this.active--, 0 === this.active && this.destination.complete() - }, Rn.prototype.checkIterators = function() { - for (var e = this.iterators, t = e.length, i = this.destination, r = 0; r < t; r++) - if ("function" == typeof(a = e[r]).hasValue && !a.hasValue()) return; - for (var n = !1, s = [], r = 0; r < t; r++) { - var a, o = (a = e[r]).next(); - if (a.hasCompleted() && (n = !0), o.done) return void i.complete(); - s.push(o.value) - } - this.resultSelector ? this._tryresultSelector(s) : i.next(s), n && i.complete() - }, Rn.prototype._tryresultSelector = function(e) { - var t; - try { - t = this.resultSelector.apply(this, e) - } catch (e) { - return void this.destination.error(e) - } - this.destination.next(t) - }, Rn), - kn = (Pn.prototype.hasValue = function() { - return !0 - }, Pn.prototype.next = function() { - var e = this.nextResult; - return this.nextResult = this.iterator.next(), e - }, Pn.prototype.hasCompleted = function() { - var e = this.nextResult; - return Boolean(e && e.done) - }, Pn), - Cn = (xn.prototype[wr] = function() { - return this - }, xn.prototype.next = function(e) { - var t = this.index++, - i = this.array; - return t < this.length ? { - value: i[t], - done: !1 - } : { - value: null, - done: !0 - } - }, xn.prototype.hasValue = function() { - return this.array.length > this.index - }, xn.prototype.hasCompleted = function() { - return this.array.length === this.index - }, xn), - Dn = (ut(Mn, In = Vr), Mn.prototype[wr] = function() { - return this - }, Mn.prototype.next = function() { - var e = this.buffer; - return 0 === e.length && this.isComplete ? { - value: null, - done: !0 - } : { - value: e.shift(), - done: !1 - } - }, Mn.prototype.hasValue = function() { - return 0 < this.buffer.length - }, Mn.prototype.hasCompleted = function() { - return 0 === this.buffer.length && this.isComplete - }, Mn.prototype.notifyComplete = function() { - 0 < this.buffer.length ? (this.isComplete = !0, this.parent.notifyInactive()) : this.destination.complete() - }, Mn.prototype.notifyNext = function(e) { - this.buffer.push(e), this.parent.checkIterators() - }, Mn.prototype.subscribe = function() { - return Hr(this.observable, new $r(this)) - }, Mn); - - function Mn(e, t, i) { - e = In.call(this, e) || this; - return e.parent = t, e.observable = i, e.stillUnsubscribed = !0, e.buffer = [], e.isComplete = !1, e - } - - function xn(e) { - this.array = e, this.index = 0, this.length = 0, this.length = e.length - } - - function Pn(e) { - this.iterator = e, this.nextResult = e.next() - } - - function Rn(e, t, i) { - e = wn.call(this, e) || this; - return e.resultSelector = t, e.iterators = [], e.active = 0, e.resultSelector = "function" == typeof t ? t : void 0, e - } - - function Ln(e) { - this.resultSelector = e - } - var _n, Nn = (Un.prototype.call = function(e, t) { - return t.subscribe(new Fn(e, this.durationSelector)) - }, Un), - Fn = (ut(Bn, _n = Vr), Bn.prototype._next = function(e) { - if (this.value = e, this.hasValue = !0, !this.throttled) { - var t = void 0; - try { - t = (0, this.durationSelector)(e) - } catch (e) { - return this.destination.error(e) - } - t = Hr(t, new $r(this)); - !t || t.closed ? this.clearThrottle() : this.add(this.throttled = t) - } - }, Bn.prototype.clearThrottle = function() { - var e = this.value, - t = this.hasValue, - i = this.throttled; - i && (this.remove(i), this.throttled = void 0, i.unsubscribe()), t && (this.value = void 0, this.hasValue = !1, this.destination.next(e)) - }, Bn.prototype.notifyNext = function() { - this.clearThrottle() - }, Bn.prototype.notifyComplete = function() { - this.clearThrottle() - }, Bn); - - function Bn(e, t) { - e = _n.call(this, e) || this; - return e.durationSelector = t, e.hasValue = !1, e - } - - function Un(e) { - this.durationSelector = e - } - - function $n(e, t) { - return void 0 === t && (t = ir), i = function() { - return bn(e, t) - }, - function(e) { - return e.lift(new Nn(i)) - }; - var i - } - - function Vn(i) { - return function(e) { - var t = new qn(i), - e = e.lift(t); - return t.caught = e - } - } - var Kn, qn = (Qn.prototype.call = function(e, t) { - return t.subscribe(new Hn(e, this.selector, this.caught)) - }, Qn), - Hn = (ut(jn, Kn = Vr), jn.prototype.error = function(e) { - if (!this.isStopped) { - var t = void 0; - try { - t = this.selector(e, this.caught) - } catch (e) { - return void Kn.prototype.error.call(this, e) - } - this._unsubscribeAndRecycle(); - var i = new $r(this); - this.add(i); - t = Hr(t, i); - t !== i && this.add(t) - } - }, jn); - - function jn(e, t, i) { - e = Kn.call(this, e) || this; - return e.selector = t, e.caught = i, e - } - - function Qn(e) { - this.selector = e - } - - function Wn(e, t) { - return jr(e, t, 1) - } - - function Gn(t, i) { - return void 0 === i && (i = ir), - function(e) { - return e.lift(new Xn(t, i)) - } - } - var zn, Xn = (Zn.prototype.call = function(e, t) { - return t.subscribe(new Yn(e, this.dueTime, this.scheduler)) - }, Zn), - Yn = (ut(Jn, zn = Pt), Jn.prototype._next = function(e) { - this.clearDebounce(), this.lastValue = e, this.hasValue = !0, this.add(this.debouncedSubscription = this.scheduler.schedule(es, this.dueTime, this)) - }, Jn.prototype._complete = function() { - this.debouncedNext(), this.destination.complete() - }, Jn.prototype.debouncedNext = function() { - var e; - this.clearDebounce(), this.hasValue && (e = this.lastValue, this.lastValue = null, this.hasValue = !1, this.destination.next(e)) - }, Jn.prototype.clearDebounce = function() { - var e = this.debouncedSubscription; - null !== e && (this.remove(e), e.unsubscribe(), this.debouncedSubscription = null) - }, Jn); - - function Jn(e, t, i) { - e = zn.call(this, e) || this; - return e.dueTime = t, e.scheduler = i, e.debouncedSubscription = null, e.lastValue = null, e.hasValue = !1, e - } - - function Zn(e, t) { - this.dueTime = e, this.scheduler = t - } - - function es(e) { - e.debouncedNext() - } - var ts, is = (ss.prototype.call = function(e, t) { - return t.subscribe(new rs(e, this.defaultValue)) - }, ss), - rs = (ut(ns, ts = Pt), ns.prototype._next = function(e) { - this.isEmpty = !1, this.destination.next(e) - }, ns.prototype._complete = function() { - this.isEmpty && this.destination.next(this.defaultValue), this.destination.complete() - }, ns); - - function ns(e, t) { - e = ts.call(this, e) || this; - return e.defaultValue = t, e.isEmpty = !0, e - } - - function ss(e) { - this.defaultValue = e - } - - function as(e) { - return e instanceof Date && !isNaN(+e) - } - var os, ls = (hs.prototype.call = function(e, t) { - return t.subscribe(new ds(e, this.delay, this.scheduler)) - }, hs), - ds = (ut(cs, os = Pt), cs.dispatch = function(e) { - for (var t, i = e.source, r = i.queue, n = e.scheduler, s = e.destination; 0 < r.length && r[0].time - n.now() <= 0;) r.shift().notification.observe(s); - 0 < r.length ? (t = Math.max(0, r[0].time - n.now()), this.schedule(e, t)) : (this.unsubscribe(), i.active = !1) - }, cs.prototype._schedule = function(e) { - this.active = !0, this.destination.add(e.schedule(cs.dispatch, this.delay, { - source: this, - destination: this.destination, - scheduler: e - })) - }, cs.prototype.scheduleNotification = function(e) { - var t; - !0 !== this.errored && (t = this.scheduler, e = new us(t.now() + this.delay, e), this.queue.push(e), !1 === this.active && this._schedule(t)) - }, cs.prototype._next = function(e) { - this.scheduleNotification(qi.createNext(e)) - }, cs.prototype._error = function(e) { - this.errored = !0, this.queue = [], this.destination.error(e), this.unsubscribe() - }, cs.prototype._complete = function() { - this.scheduleNotification(qi.createComplete()), this.unsubscribe() - }, cs), - us = function(e, t) { - this.time = e, this.notification = t - }; - - function cs(e, t, i) { - e = os.call(this, e) || this; - return e.delay = t, e.scheduler = i, e.queue = [], e.active = !1, e.errored = !1, e - } - - function hs(e, t) { - this.delay = e, this.scheduler = t - } - var ps, fs, ms, gs = (Es.prototype.call = function(e, t) { - return t.subscribe(new ys(e, this.delayDurationSelector)) - }, Es), - ys = (ut(Ts, ms = vr), Ts.prototype.notifyNext = function(e, t, i, r, n) { - this.destination.next(e), this.removeSubscription(n), this.tryComplete() - }, Ts.prototype.notifyError = function(e, t) { - this._error(e) - }, Ts.prototype.notifyComplete = function(e) { - e = this.removeSubscription(e); - e && this.destination.next(e), this.tryComplete() - }, Ts.prototype._next = function(e) { - var t = this.index++; - try { - var i = this.delayDurationSelector(e, t); - i && this.tryDelay(i, e) - } catch (e) { - this.destination.error(e) - } - }, Ts.prototype._complete = function() { - this.completed = !0, this.tryComplete(), this.unsubscribe() - }, Ts.prototype.removeSubscription = function(e) { - e.unsubscribe(); - var t = this.delayNotifierSubscriptions.indexOf(e); - return -1 !== t && this.delayNotifierSubscriptions.splice(t, 1), e.outerValue - }, Ts.prototype.tryDelay = function(e, t) { - t = Cr(this, e, t); - t && !t.closed && (this.destination.add(t), this.delayNotifierSubscriptions.push(t)) - }, Ts.prototype.tryComplete = function() { - this.completed && 0 === this.delayNotifierSubscriptions.length && this.destination.complete() - }, Ts), - vs = (ut(bs, fs = $t), bs.prototype._subscribe = function(e) { - this.subscriptionDelay.subscribe(new vs(e, this.source)) - }, ut(Ss, ps = Pt), Ss.prototype._next = function(e) { - this.subscribeToSource() - }, Ss.prototype._error = function(e) { - this.unsubscribe(), this.parent.error(e) - }, Ss.prototype._complete = function() { - this.unsubscribe(), this.subscribeToSource() - }, Ss.prototype.subscribeToSource = function() { - this.sourceSubscribed || (this.sourceSubscribed = !0, this.unsubscribe(), this.source.subscribe(this.parent)) - }, Ss); - - function Ss(e, t) { - var i = ps.call(this) || this; - return i.parent = e, i.source = t, i.sourceSubscribed = !1, i - } - - function bs(e, t) { - var i = fs.call(this) || this; - return i.source = e, i.subscriptionDelay = t, i - } - - function Ts(e, t) { - e = ms.call(this, e) || this; - return e.delayDurationSelector = t, e.completed = !1, e.delayNotifierSubscriptions = [], e.index = 0, e - } - - function Es(e) { - this.delayDurationSelector = e - } - - function Is(t, i) { - return function(e) { - return e.lift(new As(t, i)) - } - } - var ws, As = (Cs.prototype.call = function(e, t) { - return t.subscribe(new Os(e, this.compare, this.keySelector)) - }, Cs), - Os = (ut(ks, ws = Pt), ks.prototype.compare = function(e, t) { - return e === t - }, ks.prototype._next = function(e) { - try { - var t = this.keySelector, - i = t ? t(e) : e - } catch (e) { - return this.destination.error(e) - } - t = !1; - if (this.hasKey) try { - t = (0, this.compare)(this.key, i) - } catch (e) { - return this.destination.error(e) - } else this.hasKey = !0; - t || (this.key = i, this.destination.next(e)) - }, ks); - - function ks(e, t, i) { - e = ws.call(this, e) || this; - return e.keySelector = i, e.hasKey = !1, "function" == typeof t && (e.compare = t), e - } - - function Cs(e, t) { - this.compare = e, this.keySelector = t - } - - function Ds(t) { - return function(e) { - return 0 === t ? _i() : e.lift(new xs(t)) - } - } - var Ms, xs = (Ls.prototype.call = function(e, t) { - return t.subscribe(new Ps(e, this.total)) - }, Ls), - Ps = (ut(Rs, Ms = Pt), Rs.prototype._next = function(e) { - var t = this.total, - i = ++this.count; - i <= t && (this.destination.next(e), i === t && (this.destination.complete(), this.unsubscribe())) - }, Rs); - - function Rs(e, t) { - e = Ms.call(this, e) || this; - return e.total = t, e.count = 0, e - } - - function Ls(e) { - if (this.total = e, this.total < 0) throw new lr - } - - function _s(t, n) { - return n ? function(e) { - return e.pipe(_s(function(i, r) { - return Fr(t(i, r)).pipe(hr(function(e, t) { - return n(i, e, r, t) - })) - })) - } : function(e) { - return e.lift(new Fs(t)) - } - } - var Ns, Fs = ($s.prototype.call = function(e, t) { - return t.subscribe(new Bs(e, this.project)) - }, $s), - Bs = (ut(Us, Ns = Vr), Us.prototype._next = function(e) { - this.hasSubscription || this.tryNext(e) - }, Us.prototype.tryNext = function(e) { - var t, i = this.index++; - try { - t = this.project(e, i) - } catch (e) { - return void this.destination.error(e) - } - this.hasSubscription = !0, this._innerSub(t) - }, Us.prototype._innerSub = function(e) { - var t = new $r(this), - i = this.destination; - i.add(t); - e = Hr(e, t); - e !== t && i.add(e) - }, Us.prototype._complete = function() { - this.hasCompleted = !0, this.hasSubscription || this.destination.complete(), this.unsubscribe() - }, Us.prototype.notifyNext = function(e) { - this.destination.next(e) - }, Us.prototype.notifyError = function(e) { - this.destination.error(e) - }, Us.prototype.notifyComplete = function() { - this.hasSubscription = !1, this.hasCompleted && this.destination.complete() - }, Us); - - function Us(e, t) { - e = Ns.call(this, e) || this; - return e.project = t, e.hasSubscription = !1, e.hasCompleted = !1, e.index = 0, e - } - - function $s(e) { - this.project = e - } - - function Vs(t) { - return function(e) { - return e.lift(new qs(t)) - } - } - var Ks, qs = (Qs.prototype.call = function(e, t) { - return t.subscribe(new Hs(e, this.callback)) - }, Qs), - Hs = (ut(js, Ks = Pt), js); - - function js(e, t) { - e = Ks.call(this, e) || this; - return e.add(new At(t)), e - } - - function Qs(e) { - this.callback = e - } - - function Ws(t) { - return function(e) { - return 0 === t ? _i() : e.lift(new zs(t)) - } - } - var Gs, zs = (Js.prototype.call = function(e, t) { - return t.subscribe(new Xs(e, this.total)) - }, Js), - Xs = (ut(Ys, Gs = Pt), Ys.prototype._next = function(e) { - var t = this.ring, - i = this.total, - r = this.count++; - t.length < i ? t.push(e) : t[r % i] = e - }, Ys.prototype._complete = function() { - var e = this.destination, - t = this.count; - if (0 < t) - for (var i = this.count >= this.total ? this.total : this.count, r = this.ring, n = 0; n < i; n++) { - var s = t++ % i; - e.next(r[s]) - } - e.complete() - }, Ys); - - function Ys(e, t) { - e = Gs.call(this, e) || this; - return e.total = t, e.ring = new Array, e.count = 0, e - } - - function Js(e) { - if (this.total = e, this.total < 0) throw new lr - } - - function Zs(t) { - return function(e) { - return e.lift(new ta(t)) - } - } - var ea, ta = (na.prototype.call = function(e, t) { - return t.subscribe(new ia(e, this.value)) - }, na), - ia = (ut(ra, ea = Pt), ra.prototype._next = function(e) { - this.destination.next(this.value) - }, ra); - - function ra(e, t) { - e = ea.call(this, e) || this; - return e.value = t, e - } - - function na(e) { - this.value = e - } - - function sa(t, i) { - var r = 2 <= arguments.length ? !0 : !1; - return function(e) { - return e.lift(new oa(t, i, r)) - } - } - var aa, oa = (ua.prototype.call = function(e, t) { - return t.subscribe(new la(e, this.accumulator, this.seed, this.hasSeed)) - }, ua), - la = (ut(da, aa = Pt), Object.defineProperty(da.prototype, "seed", { - get: function() { - return this._seed - }, - set: function(e) { - this.hasSeed = !0, this._seed = e - }, - enumerable: !0, - configurable: !0 - }), da.prototype._next = function(e) { - if (this.hasSeed) return this._tryNext(e); - this.seed = e, this.destination.next(e) - }, da.prototype._tryNext = function(e) { - var t, i = this.index++; - try { - t = this.accumulator(this.seed, e, i) - } catch (e) { - this.destination.error(e) - } - this.seed = t, this.destination.next(t) - }, da); - - function da(e, t, i, r) { - e = aa.call(this, e) || this; - return e.accumulator = t, e._seed = i, e.hasSeed = r, e.index = 0, e - } - - function ua(e, t, i) { - void 0 === i && (i = !1), this.accumulator = e, this.seed = t, this.hasSeed = i - } - ca.prototype.call = function(e, t) { - var i = this.selector, - r = this.subjectFactory(), - e = i(r).subscribe(e); - return e.add(t.subscribe(r)), e - }; - - function ca(e, t) { - this.subjectFactory = e, this.selector = t - } - - function ha() { - return function(e) { - return e.lift(new fa) - } - } - var pa, fa = (ya.prototype.call = function(e, t) { - return t.subscribe(new ma(e)) - }, ya), - ma = (ut(ga, pa = Pt), ga.prototype._next = function(e) { - var t; - this.hasPrev ? t = [this.prev, e] : this.hasPrev = !0, this.prev = e, t && this.destination.next(t) - }, ga); - - function ga(e) { - e = pa.call(this, e) || this; - return e.hasPrev = !1, e - } - - function ya() {} - - function va(t) { - return function(e) { - return e.lift(new ba(t, e)) - } - } - var Sa, ba = (Ia.prototype.call = function(e, t) { - return t.subscribe(new Ta(e, this.notifier, this.source)) - }, Ia), - Ta = (ut(Ea, Sa = Vr), Ea.prototype.error = function(e) { - if (!this.isStopped) { - var t = this.errors, - i = this.retries, - r = this.retriesSubscription; - if (i) this.errors = void 0, this.retriesSubscription = void 0; - else { - t = new Xt; - try { - i = (0, this.notifier)(t) - } catch (e) { - return Sa.prototype.error.call(this, e) - } - r = Hr(i, new $r(this)) - } - this._unsubscribeAndRecycle(), this.errors = t, this.retries = i, this.retriesSubscription = r, t.next(e) - } - }, Ea.prototype._unsubscribe = function() { - var e = this.errors, - t = this.retriesSubscription; - e && (e.unsubscribe(), this.errors = void 0), t && (t.unsubscribe(), this.retriesSubscription = void 0), this.retries = void 0 - }, Ea.prototype.notifyNext = function() { - var e = this._unsubscribe; - this._unsubscribe = null, this._unsubscribeAndRecycle(), this._unsubscribe = e, this.source.subscribe(this) - }, Ea); - - function Ea(e, t, i) { - e = Sa.call(this, e) || this; - return e.notifier = t, e.source = i, e - } - - function Ia(e, t) { - this.notifier = e, this.source = t - } - - function wa() { - return new Xt - } - - function Aa() { - return function(e) { - return ri()((t = e, i = "function" == typeof(r = wa) ? r : function() { - return r - }, (e = Object.create(t, mi)).source = t, e.subjectFactory = i, e)); - var t, i, r - } - } - - function Oa(e, t, i) { - var c = e && "object" == typeof e ? e : { - bufferSize: e, - windowTime: t, - refCount: !1, - scheduler: i - }; - return function(e) { - return e.lift((e = c.bufferSize, n = void 0 === e ? Number.POSITIVE_INFINITY : e, e = c.windowTime, s = void 0 === e ? Number.POSITIVE_INFINITY : e, a = c.refCount, o = c.scheduler, l = 0, u = d = !1, function(e) { - var t; - l++, !i || d ? (d = !1, i = new Ji(n, s, o), t = i.subscribe(this), r = e.subscribe({ - next: function(e) { - i.next(e) - }, - error: function(e) { - d = !0, i.error(e) - }, - complete: function() { - u = !0, r = void 0, i.complete() - } - }), u && (r = void 0)) : t = i.subscribe(this), this.add(function() { - l--, t.unsubscribe(), t = void 0, r && !u && a && 0 === l && (r.unsubscribe(), i = r = void 0) - }) - })); - var i, r, n, s, a, o, l, d, u - } - } - - function ka(t) { - return function(e) { - return e.lift(new Da(t)) - } - } - var Ca, Da = (Pa.prototype.call = function(e, t) { - return t.subscribe(new Ma(e, this.total)) - }, Pa), - Ma = (ut(xa, Ca = Pt), xa.prototype._next = function(e) { - ++this.count > this.total && this.destination.next(e) - }, xa); - - function xa(e, t) { - e = Ca.call(this, e) || this; - return e.total = t, e.count = 0, e - } - - function Pa(e) { - this.total = e - } - - function Ra() { - for (var t = [], e = 0; e < arguments.length; e++) t[e] = arguments[e]; - var i = t[t.length - 1]; - return Ni(i) ? (t.pop(), function(e) { - return Jr(t, e, i) - }) : function(e) { - return Jr(t, e) - } - } - - function La(t, n) { - return "function" == typeof n ? function(e) { - return e.pipe(La(function(i, r) { - return Fr(t(i, r)).pipe(hr(function(e, t) { - return n(i, e, r, t) - })) - })) - } : function(e) { - return e.lift(new Na(t)) - } - } - var _a, Na = (Ua.prototype.call = function(e, t) { - return t.subscribe(new Fa(e, this.project)) - }, Ua), - Fa = (ut(Ba, _a = Vr), Ba.prototype._next = function(e) { - var t, i = this.index++; - try { - t = this.project(e, i) - } catch (e) { - return void this.destination.error(e) - } - this._innerSub(t) - }, Ba.prototype._innerSub = function(e) { - var t = this.innerSubscription; - t && t.unsubscribe(); - var i = new $r(this), - t = this.destination; - t.add(i), this.innerSubscription = Hr(e, i), this.innerSubscription !== i && t.add(this.innerSubscription) - }, Ba.prototype._complete = function() { - var e = this.innerSubscription; - e && !e.closed || _a.prototype._complete.call(this), this.unsubscribe() - }, Ba.prototype._unsubscribe = function() { - this.innerSubscription = void 0 - }, Ba.prototype.notifyComplete = function() { - this.innerSubscription = void 0, this.isStopped && _a.prototype._complete.call(this) - }, Ba.prototype.notifyNext = function(e) { - this.destination.next(e) - }, Ba); - - function Ba(e, t) { - e = _a.call(this, e) || this; - return e.project = t, e.index = 0, e - } - - function Ua(e) { - this.project = e - } - - function $a(e, t) { - return t ? La(function() { - return e - }, t) : La(function() { - return e - }) - } - - function Va(t) { - return function(e) { - return e.lift(new qa(t)) - } - } - var Ka, qa = (Qa.prototype.call = function(e, t) { - var i = new Ha(e), - e = Hr(this.notifier, new $r(i)); - return e && !i.seenValue ? (i.add(e), t.subscribe(i)) : i - }, Qa), - Ha = (ut(ja, Ka = Vr), ja.prototype.notifyNext = function() { - this.seenValue = !0, this.complete() - }, ja.prototype.notifyComplete = function() {}, ja); - - function ja(e) { - e = Ka.call(this, e) || this; - return e.seenValue = !1, e - } - - function Qa(e) { - this.notifier = e - } - - function Wa(t, i) { - return void 0 === i && (i = !1), - function(e) { - return e.lift(new za(t, i)) - } - } - var Ga, za = (Ja.prototype.call = function(e, t) { - return t.subscribe(new Xa(e, this.predicate, this.inclusive)) - }, Ja), - Xa = (ut(Ya, Ga = Pt), Ya.prototype._next = function(e) { - var t, i = this.destination; - try { - t = this.predicate(e, this.index++) - } catch (e) { - return void i.error(e) - } - this.nextOrComplete(e, t) - }, Ya.prototype.nextOrComplete = function(e, t) { - var i = this.destination; - Boolean(t) ? i.next(e) : (this.inclusive && i.next(e), i.complete()) - }, Ya); - - function Ya(e, t, i) { - e = Ga.call(this, e) || this; - return e.predicate = t, e.inclusive = i, e.index = 0, e - } - - function Ja(e, t) { - this.predicate = e, this.inclusive = t - } - - function Za(t, i, r) { - return function(e) { - return e.lift(new to(t, i, r)) - } - } - var eo, to = (so.prototype.call = function(e, t) { - return t.subscribe(new io(e, this.nextOrObserver, this.error, this.complete)) - }, so), - io = (ut(no, eo = Pt), no.prototype._next = function(e) { - try { - this._tapNext.call(this._context, e) - } catch (e) { - return void this.destination.error(e) - } - this.destination.next(e) - }, no.prototype._error = function(e) { - try { - this._tapError.call(this._context, e) - } catch (e) { - return void this.destination.error(e) - } - this.destination.error(e) - }, no.prototype._complete = function() { - try { - this._tapComplete.call(this._context) - } catch (e) { - return void this.destination.error(e) - } - return this.destination.complete() - }, no), - ro = { - leading: !0, - trailing: !1 - }; - - function no(e, t, i, r) { - e = eo.call(this, e) || this; - return e._tapNext = or, e._tapError = or, e._tapComplete = or, e._tapError = i || or, e._tapComplete = r || or, yt(t) ? (e._context = e)._tapNext = t : t && (e._context = t, e._tapNext = t.next || or, e._tapError = t.error || or, e._tapComplete = t.complete || or), e - } - - function so(e, t, i) { - this.nextOrObserver = e, this.error = t, this.complete = i - } - - function ao(t, i, r) { - return void 0 === i && (i = ir), void 0 === r && (r = ro), - function(e) { - return e.lift(new lo(t, i, r.leading, r.trailing)) - } - } - var oo, lo = (ho.prototype.call = function(e, t) { - return t.subscribe(new uo(e, this.duration, this.scheduler, this.leading, this.trailing)) - }, ho), - uo = (ut(co, oo = Pt), co.prototype._next = function(e) { - this.throttled ? this.trailing && (this._trailingValue = e, this._hasTrailingValue = !0) : (this.add(this.throttled = this.scheduler.schedule(po, this.duration, { - subscriber: this - })), this.leading ? this.destination.next(e) : this.trailing && (this._trailingValue = e, this._hasTrailingValue = !0)) - }, co.prototype._complete = function() { - this._hasTrailingValue && this.destination.next(this._trailingValue), this.destination.complete() - }, co.prototype.clearThrottle = function() { - var e = this.throttled; - e && (this.trailing && this._hasTrailingValue && (this.destination.next(this._trailingValue), this._trailingValue = null, this._hasTrailingValue = !1), e.unsubscribe(), this.remove(e), this.throttled = null) - }, co); - - function co(e, t, i, r, n) { - e = oo.call(this, e) || this; - return e.duration = t, e.scheduler = i, e.leading = r, e.trailing = n, e._hasTrailingValue = !1, e._trailingValue = null, e - } - - function ho(e, t, i, r) { - this.duration = e, this.scheduler = t, this.leading = i, this.trailing = r - } - - function po(e) { - e.subscriber.clearThrottle() - } - var fo, mo = (vo.prototype.call = function(e, t) { - return t.subscribe(new go(e, this.absoluteTimeout, this.waitFor, this.withObservable, this.scheduler)) - }, vo), - go = (ut(yo, fo = Vr), yo.dispatchTimeout = function(e) { - var t = e.withObservable; - e._unsubscribeAndRecycle(), e.add(Hr(t, new $r(e))) - }, yo.prototype.scheduleTimeout = function() { - var e = this.action; - e ? this.action = e.schedule(this, this.waitFor) : this.add(this.action = this.scheduler.schedule(yo.dispatchTimeout, this.waitFor, this)) - }, yo.prototype._next = function(e) { - this.absoluteTimeout || this.scheduleTimeout(), fo.prototype._next.call(this, e) - }, yo.prototype._unsubscribe = function() { - this.action = void 0, this.scheduler = null, this.withObservable = null - }, yo); - - function yo(e, t, i, r, n) { - e = fo.call(this, e) || this; - return e.absoluteTimeout = t, e.waitFor = i, e.withObservable = r, e.scheduler = n, e.scheduleTimeout(), e - } - - function vo(e, t, i, r) { - this.waitFor = e, this.absoluteTimeout = t, this.withObservable = i, this.scheduler = r - } - - function So(e, t) { - return void 0 === t && (t = ir), r = e, n = Vi(new dr), void 0 === (s = t) && (s = ir), - function(e) { - var t = as(r), - i = t ? +r - s.now() : Math.abs(r); - return e.lift(new mo(i, t, n, s)) - }; - var r, n, s - } - - function bo() { - for (var i = [], e = 0; e < arguments.length; e++) i[e] = arguments[e]; - return function(e) { - var t; - return "function" == typeof i[i.length - 1] && (t = i.pop()), e.lift(new Io(i, t)) - } - } - var To, Eo, Io = (Co.prototype.call = function(e, t) { - return t.subscribe(new wo(e, this.observables, this.project)) - }, Co), - wo = (ut(ko, To = vr), ko.prototype.notifyNext = function(e, t, i) { - this.values[i] = t; - t = this.toRespond; - 0 < t.length && (-1 !== (i = t.indexOf(i)) && t.splice(i, 1)) - }, ko.prototype.notifyComplete = function() {}, ko.prototype._next = function(e) { - 0 === this.toRespond.length && (e = [e].concat(this.values), this.project ? this._tryProject(e) : this.destination.next(e)) - }, ko.prototype._tryProject = function(e) { - var t; - try { - t = this.project.apply(this, e) - } catch (e) { - return void this.destination.error(e) - } - this.destination.next(t) - }, ko), - Ao = { - type: null, - entityIds: null, - skip: !1 - }, - Oo = !1; - - function ko(e, t, i) { - var r = To.call(this, e) || this; - r.observables = t, r.project = i, r.toRespond = []; - var n = t.length; - r.values = new Array(n); - for (var s = 0; s < n; s++) r.toRespond.push(s); - for (s = 0; s < n; s++) { - var a = t[s]; - r.add(Cr(r, a, void 0, s)) - } - return r - } - - function Co(e, t) { - this.observables = e, this.project = t - } - - function Do(e, t) { - Mo(e, t), Oo = !0 - } - - function Mo(e, t) { - !1 === Oo && (Ao.type = e, Ao.entityIds = t) - } - - function xo(e, t) { - return e.hasOwnProperty(t) - } - - function Po(e) { - return null == e - } - - function Ro(e) { - return Po(e) ? [] : Array.isArray(e) ? e : [e] - }(iu = Eo = Eo || {}).Set = "Set", iu.Add = "Add", iu.Update = "Update", iu.Remove = "Remove"; - var Lo = "undefined" != typeof window, - _o = !0; - - function No(e) { - var t = typeof e; - return null != e && ("object" == t || "function" == t) - } - - function Fo(e) { - return Array.isArray(e) - } - - function Bo(e) { - return !1 === Po(e) - } - - function Uo(e) { - return Fo(e) && 0 === e.length - } - - function $o(e) { - return "function" == typeof e - } - - function Vo(e) { - return void 0 === e - } - - function Ko(e) { - return e.hasOwnProperty("active") - } - - function qo(e) { - return Fo(e) - } - - function Ho(e) { - var t, i = e.active, - r = e.ids, - n = e.entities; - return qo(i) ? (t = r, (r = (e = i).filter(function(e) { - return -1 < t.indexOf(e) - })).length === e.length ? e : r) : !1 === xo(n, i) ? null : i - } - - function jo(e, t) { - var i, r, n = {}; - try { - for (var s = ft(Object.keys(e)), a = s.next(); !a.done; a = s.next()) { - var o = a.value; - n[o] = t(e[o]) - } - } catch (e) { - i = { - error: e - } - } finally { - try { - a && !a.done && (r = s.return) && r.call(s) - } finally { - if (i) throw i.error - } - } - return n - } - var Qo = { - resettable: !1, - ttl: null, - producerFn: void 0 - }; - - function Wo(t) { - Object.freeze(t); - var i = "function" == typeof t, - r = Object.prototype.hasOwnProperty; - return Object.getOwnPropertyNames(t).forEach(function(e) { - !r.call(t, e) || i && ("caller" === e || "callee" === e || "arguments" === e) || null === t[e] || "object" != typeof t[e] && "function" != typeof t[e] || Object.isFrozen(t[e]) || Wo(t[e]) - }), t - } - var Go, zo = new Xt, - Xo = new Ji(50, 5e3), - Yo = new Xt; - - function Jo(e) { - return null != e && "" + e != "false" - } - - function Zo(e) { - return Jo(e) && "Object" === e.constructor.name - } - ut(function(e) { - return Go.call(this, e) || this - }, Go = Error); - var el = {}, - tl = {}; - Lo && (window.$$stores = el, window.$$queries = tl); - var il = new Xt, - rl = new yi(!1), - nl = { - activeTransactions: 0, - batchTransaction: null - }; - - function sl() { - return 0 < nl.activeTransactions - } - - function al(e, t) { - void 0 === t && (t = void 0), sl() || (nl.batchTransaction = new Xt), nl.activeTransactions++, rl.next(!0); - try { - return e.apply(t) - } finally { - Do("@Transaction"), 0 == --nl.activeTransactions && (nl.batchTransaction.next(!0), nl.batchTransaction.complete(), rl.next(!1), il.next(!0)) - } - } - - function ol() { - return function(e, t, i) { - var r = i.value; - return i.value = function() { - for (var e = this, t = [], i = 0; i < arguments.length; i++) t[i] = arguments[i]; - return al(function() { - return r.apply(e, t) - }, this) - }, i - } - } - - function ll(t) { - return function(e) { - return e.pipe(Za(function(e) { - return al(function() { - return t(e) - }) - })) - } - } - var dl = (ul.prototype.setLoading = function(t) { - (t = void 0 === t ? !1 : t) !== this._value().loading && (_o && Mo("Set Loading"), this._setState(function(e) { - return ct({}, e, { - loading: t - }) - })) - }, ul.prototype.setHasCache = function(e, t) { - var i, r = this; - void 0 === t && (t = { - restartTTL: !1 - }), e !== this.cache.active.value && this.cache.active.next(e), t.restartTTL && (i = this.getCacheTTL()) && (null !== this.cache.ttl && clearTimeout(this.cache.ttl), this.cache.ttl = setTimeout(function() { - return r.setHasCache(!1) - }, i)) - }, ul.prototype.getValue = function() { - return this.storeValue - }, ul.prototype.setError = function(t) { - t !== this._value().error && (_o && Mo("Set Error"), this._setState(function(e) { - return ct({}, e, { - error: t - }) - })) - }, ul.prototype._select = function(t) { - return this.store.asObservable().pipe(hr(function(e) { - return t(e.state) - }), Is()) - }, ul.prototype._value = function() { - return this.storeValue - }, ul.prototype._cache = function() { - return this.cache.active - }, Object.defineProperty(ul.prototype, "config", { - get: function() { - return this.constructor.akitaConfig || {} - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(ul.prototype, "storeName", { - get: function() { - return this.config.storeName || this.options.storeName || this.options.name - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(ul.prototype, "deepFreeze", { - get: function() { - return this.config.deepFreezeFn || this.options.deepFreezeFn || Wo - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(ul.prototype, "cacheConfig", { - get: function() { - return this.config.cache || this.options.cache - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(ul.prototype, "_producerFn", { - get: function() { - return this.config.producerFn || this.options.producerFn || Qo.producerFn - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(ul.prototype, "resettable", { - get: function() { - return (Bo(this.config.resettable) ? this.config : this.options).resettable - }, - enumerable: !0, - configurable: !0 - }), ul.prototype._setState = function(e, t) { - var i, r = this; - if (void 0 === t && (t = !0), $o(e) ? (i = e(this._value()), this.storeValue = _o ? this.deepFreeze(i) : i) : this.storeValue = e, !this.store) return this.store = new yi({ - state: this.storeValue - }), void(_o && this.store.subscribe(function(e) { - var t = e.action; - t && (e = r.storeName, Yo.next({ - storeName: e, - action: t - })) - })); - sl() ? this.handleTransaction() : this.dispatch(this.storeValue, t) - }, ul.prototype.reset = function() { - var e = this; - this.isResettable() ? (_o && Mo("Reset"), this._setState(function() { - return Object.assign({}, e._initialState) - }), this.setHasCache(!1)) : _o && console.warn("You need to enable the reset functionality") - }, ul.prototype.update = function(e) { - _o && Mo("Update"); - var t = this._value(), - e = $o(e) ? $o(this._producerFn) ? this._producerFn(t, e) : e(t) : e, - e = this.akitaPreUpdate(t, ct({}, t, e)), - e = Zo(t) ? e : new t.constructor(e); - this._setState(e) - }, ul.prototype.updateStoreConfig = function(e) { - this.options = ct({}, this.options, e) - }, ul.prototype.akitaPreUpdate = function(e, t) { - return t - }, ul.prototype.ngOnDestroy = function() { - this.destroy() - }, ul.prototype.destroy = function() { - var e; - Lo && window.hmrEnabled || this !== el[this.storeName] || (delete el[this.storeName], e = this.storeName, zo.next(e), this.setHasCache(!1), this.cache.active.complete(), this.store.complete()) - }, ul.prototype.onInit = function(e) { - var t, i; - (el[this.storeName] = this)._setState(function() { - return e - }), i = this.storeName, Xo.next(i), this.isResettable() && (this._initialState = e), _o && (t = this.storeName, i = this.constructor.name, t || console.error("@StoreConfig({ name }) is missing in " + i)) - }, ul.prototype.dispatch = function(e, t) { - var i = void 0; - (t = void 0 === t ? !0 : t) && (i = Ao, Oo = !1), this.store.next({ - state: e, - action: i - }) - }, ul.prototype.watchTransaction = function() { - var e = this; - (nl.batchTransaction ? nl.batchTransaction.asObservable() : $i(!0)).subscribe(function() { - e.inTransaction = !1, e.dispatch(e._value()) - }) - }, ul.prototype.isResettable = function() { - return !1 !== this.resettable && (this.resettable || Qo.resettable) - }, ul.prototype.handleTransaction = function() { - this.inTransaction || (this.watchTransaction(), this.inTransaction = !0) - }, ul.prototype.getCacheTTL = function() { - return this.cacheConfig && this.cacheConfig.ttl || Qo.ttl - }, ul); - - function ul(e, t) { - this.options = t = void 0 === t ? {} : t, this.inTransaction = !1, this.cache = { - active: new yi(!1), - ttl: null - }, this.onInit(e) - } - var cl, hl, pl, fl = (ut(yl, pl = dl), Object.defineProperty(yl.prototype, "selectEntityAction$", { - get: function() { - return this.entityActions.asObservable() - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(yl.prototype, "selectEntityIdChanges$", { - get: function() { - return this.entityIdChanges.asObservable() - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(yl.prototype, "idKey", { - get: function() { - return this.config.idKey || this.options.idKey || "id" - }, - enumerable: !0, - configurable: !0 - }), yl.prototype.set = function(a, o) { - var l, d = this; - void 0 === o && (o = {}), Po(a) || (_o && Mo("Set Entity"), l = this.akitaPreAddEntity === yl.prototype.akitaPreAddEntity, this.setHasCache(!0, { - restartTTL: !0 - }), this._setState(function(e) { - var t, i, r, n, s, e = (t = { - state: e, - entities: a, - idKey: d.idKey, - preAddEntity: d.akitaPreAddEntity, - isNativePreAdd: l - }, r = t.state, n = t.entities, s = t.idKey, e = t.preAddEntity, t = t.isNativePreAdd, e = Fo(n) ? (i = (s = function(e, t, i) { - var r, n, s = { - entities: {}, - ids: [] - }; - try { - for (var a = ft(e), o = a.next(); !o.done; o = a.next()) { - var l = i(o.value); - s.entities[l[t]] = l, s.ids.push(l[t]) - } - } catch (e) { - r = { - error: e - } - } finally { - try { - o && !o.done && (n = a.return) && n.call(a) - } finally { - if (r) throw r.error - } - } - return s - }(n, s, e)).entities, s.ids) : n.entities && n.ids ? (i = t ? n.entities : jo(n.entities, e), n.ids) : (i = t ? n : jo(n, e), Object.keys(i).map(function(e) { - return isNaN(e) ? e : Number(e) - })), e = ct({}, r, { - entities: i, - ids: e, - loading: !1 - }), Ko(r) && (e.active = Ho(e)), e); - return !1 === Vo(o.activeId) && (e.active = o.activeId), e - }), this.hasInitialUIState() && this.handleUICreation(), this.entityActions.next({ - type: Eo.Set, - ids: this.ids - })) - }, yl.prototype.add = function(e, t) { - void 0 === t && (t = { - loading: !1 - }); - var i, e = Ro(e); - Uo(e) || (i = function(e) { - var t, i, r = e.state, - n = e.entities, - s = e.idKey, - a = e.options, - o = void 0 === a ? {} : a, - l = e.preAddEntity, - d = {}, - u = [], - c = !1; - try { - for (var h = ft(n), p = h.next(); !p.done; p = h.next()) { - var f, m, g = p.value; - !1 === xo(r.entities, g[s]) && (d[m = (f = l(g))[s]] = f, o.prepend ? u.unshift(m) : u.push(m), c = !0) - } - } catch (e) { - t = { - error: e - } - } finally { - try { - p && !p.done && (i = h.return) && i.call(h) - } finally { - if (t) throw t.error - } - } - return c ? { - newState: ct({}, r, { - entities: ct({}, r.entities, d), - ids: o.prepend ? gt(u, r.ids) : gt(r.ids, u) - }), - newIds: u - } : null - }({ - state: this._value(), - preAddEntity: this.akitaPreAddEntity, - entities: e, - idKey: this.idKey, - options: t - })) && (_o && Mo("Add Entity"), i.newState.loading = t.loading, this._setState(function() { - return i.newState - }), this.hasInitialUIState() && this.handleUICreation(!0), this.entityActions.next({ - type: Eo.Add, - ids: i.newIds - })) - }, yl.prototype.update = function(t, i) { - var r, n, s = this; - Vo(i) ? pl.prototype.update.call(this, t) : (n = [], Uo(n = $o(t) ? this.ids.filter(function(e) { - return t(s.entities[e]) - }) : Po(t) ? this.ids : Ro(t)) || (_o && Mo("Update Entity", n), this._setState(function(e) { - return function(e) { - var t = e.state, - i = e.ids, - r = e.idKey, - n = e.newStateOrFn, - s = e.preUpdateEntity, - a = e.producerFn, - o = e.onEntityIdChanges, - l = {}, - d = !1; - try { - for (var u = ft(i), c = u.next(); !c.done; c = u.next()) { - var h, p, f, m, g, y, v = c.value; - !1 !== xo(t.entities, v) && (h = t.entities[v], p = void 0, f = (p = $o(n) ? $o(a) ? a(h, n) : n(h) : n).hasOwnProperty(r) && p[r] !== h[r], y = void 0, m = v, f && (d = !0, m = p[r]), g = ct({}, h, p), y = Zo(h) ? g : new(Zo(p) ? h : p).constructor(g), l[m] = s(h, y)) - } - } catch (e) { - T = { - error: e - } - } finally { - try { - c && !c.done && (b = u.return) && b.call(u) - } finally { - if (T) throw T.error - } - } - var S, b = t.ids, - T = t.entities; - return d && (S = mt(i, 1)[0], T = function(e, t) { - var i = {}; - for (n in e) Object.prototype.hasOwnProperty.call(e, n) && t.indexOf(n) < 0 && (i[n] = e[n]); - if (null != e && "function" == typeof Object.getOwnPropertySymbols) - for (var r = 0, n = Object.getOwnPropertySymbols(e); r < n.length; r++) t.indexOf(n[r]) < 0 && Object.prototype.propertyIsEnumerable.call(e, n[r]) && (i[n[r]] = e[n[r]]); - return i - }(t.entities, ["symbol" == typeof S ? S : S + ""]), b = t.ids.map(function(e) { - return e === S ? m : e - }), o(S, m)), ct({}, t, { - entities: ct({}, T, l), - ids: b - }) - }({ - idKey: s.idKey, - ids: n, - preUpdateEntity: s.akitaPreUpdateEntity, - state: e, - newStateOrFn: i, - producerFn: s._producerFn, - onEntityIdChanges: function(e, t) { - r = { - oldId: e, - newId: t - }, s.entityIdChanges.next(ct({}, r, { - pending: !0 - })) - } - }) - }), r && this.entityIdChanges.next(ct({}, r, { - pending: !1 - })), this.entityActions.next({ - type: Eo.Update, - ids: n - }))) - }, yl.prototype.upsert = function(e, i, r, t) { - var n = this; - void 0 === t && (t = {}); - var s = Ro(e), - e = function(t) { - return function(e) { - return xo(n.entities, e) === t - } - }, - a = $o(r) ? t.baseClass : r ? r.baseClass : void 0, - o = $o(a), - t = s.filter(e(!0)), - e = s.filter(e(!1)).map(function(e) { - var t = "function" == typeof i ? i({}) : i, - t = $o(r) ? r(e, t) : t, - t = ct({}, t, ((t = {})[n.idKey] = e, t)); - return o ? new a(t) : t - }); - this.update(t, i), this.add(e), _o && Do("Upsert Entity") - }, yl.prototype.upsertMany = function(e, t) { - var i, r; - void 0 === t && (t = {}); - var n = [], - s = [], - a = {}; - try { - for (var o = ft(e), l = o.next(); !l.done; l = o.next()) { - var d, u, c, h, p, f, m = l.value, - g = this.akitaPreCheckEntity(m), - y = g[this.idKey]; - xo(this.entities, y) ? (d = this._value().entities[y], u = ct({}, this._value().entities[y], g), c = t.baseClass ? new t.baseClass(u) : u, f = (h = this.akitaPreUpdateEntity(d, c))[this.idKey], a[f] = h, s.push(f)) : (p = t.baseClass ? new t.baseClass(g) : g, f = (h = this.akitaPreAddEntity(p))[this.idKey], n.push(f), a[f] = h) - } - } catch (e) { - i = { - error: e - } - } finally { - try { - l && !l.done && (r = o.return) && r.call(o) - } finally { - if (i) throw i.error - } - } - _o && Do("Upsert Many"), this._setState(function(e) { - return ct({}, e, { - ids: n.length ? gt(e.ids, n) : e.ids, - entities: ct({}, e.entities, a), - loading: !!t.loading - }) - }), s.length && this.entityActions.next({ - type: Eo.Update, - ids: s - }), n.length && this.entityActions.next({ - type: Eo.Add, - ids: n - }), n.length && this.hasUIStore() && this.handleUICreation(!0) - }, yl.prototype.replace = function(e, t) { - var i, r, n = Ro(e); - if (!Uo(n)) { - var s = {}; - try { - for (var a = ft(n), o = a.next(); !o.done; o = a.next()) { - var l = o.value; - t[this.idKey] = l, s[l] = t - } - } catch (e) { - i = { - error: e - } - } finally { - try { - o && !o.done && (r = a.return) && r.call(a) - } finally { - if (i) throw i.error - } - } - _o && Mo("Replace Entity", e), this._setState(function(e) { - return ct({}, e, { - entities: ct({}, e.entities, s) - }) - }) - } - }, yl.prototype.move = function(e, t) { - var i = this.ids.slice(); - i.splice(t < 0 ? i.length + t : t, 0, i.splice(e, 1)[0]), _o && Mo("Move Entity"), this._setState(function(e) { - return ct({}, e, { - entities: ct({}, e.entities), - ids: i - }) - }) - }, yl.prototype.remove = function(t) { - var e, i, r = this; - Uo(this.ids) || (e = Bo(t), i = [], Uo(i = $o(t) ? this.ids.filter(function(e) { - return t(r.entities[e]) - }) : e ? Ro(t) : this.ids) || (_o && Mo("Remove Entity", i), this._setState(function(e) { - return function(e) { - var t, i = e.state, - r = e.ids; - if (Po(r)) return ct({}, i, { - entities: {}, - ids: [], - active: qo(i.active) ? [] : null - }); - var n = i.entities, - s = {}; - try { - for (var a = ft(i.ids), o = a.next(); !o.done; o = a.next()) { - var l = o.value; - !1 === r.includes(l) && (s[l] = n[l]) - } - } catch (e) { - d = { - error: e - } - } finally { - try { - o && !o.done && (t = a.return) && t.call(a) - } finally { - if (d) throw d.error - } - } - var d = ct({}, i, { - entities: s, - ids: i.ids.filter(function(e) { - return !1 === r.includes(e) - }) - }); - return Ko(i) && (d.active = Ho(d)), d - }({ - state: e, - ids: i - }) - }), e || this.setHasCache(!1), this.handleUIRemove(i), this.entityActions.next({ - type: Eo.Remove, - ids: i - }))) - }, yl.prototype.updateActive = function(e) { - var t = Ro(this.active); - _o && Mo("Update Active", t), this.update(t, e) - }, yl.prototype.setActive = function(e) { - e = function(e, t, i) { - var r; - if (Fo(e)) r = e; - else if (No(e)) { - if (Po(i)) return; - e = Object.assign({ - wrap: !0 - }, e); - var n = t.indexOf(i); - if (e.prev) { - var s = 0 === n; - if (s && !e.wrap) return; - r = s ? t[t.length - 1] : t[n - 1] - } else if (e.next) { - s = t.length === n + 1; - if (s && !e.wrap) return; - r = s ? t[0] : t[n + 1] - } - } else { - if (e === i) return; - r = e - } - return r - }(e, this.ids, this.active); - void 0 !== e && (_o && Mo("Set Active", e), this._setActive(e)) - }, yl.prototype.addActive = function(e) { - var t = this, - i = Ro(e); - Uo(i) || i.every(function(e) { - return -1 < t.active.indexOf(e) - }) || (_o && Mo("Add Active", e), this._setState(function(e) { - var t = Array.from(new Set(gt(e.active, i))); - return ct({}, e, { - active: t - }) - })) - }, yl.prototype.removeActive = function(e) { - var t = this, - i = Ro(e); - Uo(i) || i.some(function(e) { - return -1 < t.active.indexOf(e) - }) && (_o && Mo("Remove Active", e), this._setState(function(e) { - return ct({}, e, { - active: Array.isArray(e.active) ? e.active.filter(function(e) { - return -1 === i.indexOf(e) - }) : null - }) - })) - }, yl.prototype.toggleActive = function(e) { - var i = this, - t = Ro(e), - r = function(t) { - return function(e) { - return i.active.includes(e) === t - } - }, - e = t.filter(r(!0)), - r = t.filter(r(!1)); - this.removeActive(e), this.addActive(r), _o && Do("Toggle Active") - }, yl.prototype.createUIStore = function(e, t) { - var i = { - name: "UI/" + this.storeName, - idKey: this.idKey - }; - return this.ui = new ml(e = void 0 === e ? {} : e, ct({}, i, t = void 0 === t ? {} : t)), this.ui - }, yl.prototype.destroy = function() { - pl.prototype.destroy.call(this), this.ui instanceof yl && this.ui.destroy(), this.entityActions.complete() - }, yl.prototype.akitaPreUpdateEntity = function(e, t) { - return t - }, yl.prototype.akitaPreAddEntity = function(e) { - return e - }, yl.prototype.akitaPreCheckEntity = function(e) { - return e - }, Object.defineProperty(yl.prototype, "ids", { - get: function() { - return this._value().ids - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(yl.prototype, "entities", { - get: function() { - return this._value().entities - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(yl.prototype, "active", { - get: function() { - return this._value().active - }, - enumerable: !0, - configurable: !0 - }), yl.prototype._setActive = function(t) { - this._setState(function(e) { - return ct({}, e, { - active: t - }) - }) - }, yl.prototype.handleUICreation = function(e) { - var r = this, - t = this.ids, - n = $o(this.ui._akitaCreateEntityFn), - i = function(e) { - var t = r.entities[e], - i = n ? r.ui._akitaCreateEntityFn(t) : r.ui._akitaCreateEntityFn; - return ct(((e = {})[r.idKey] = t[r.idKey], e), i) - }, - i = ((e = void 0 === e ? !1 : e) ? this.ids.filter(function(e) { - return Vo(r.ui.entities[e]) - }) : t).map(i); - e ? this.ui.add(i) : this.ui.set(i) - }, yl.prototype.hasInitialUIState = function() { - return this.hasUIStore() && !1 === Vo(this.ui._akitaCreateEntityFn) - }, yl.prototype.handleUIRemove = function(e) { - this.hasUIStore() && this.ui.remove(e) - }, yl.prototype.hasUIStore = function() { - return this.ui instanceof ml - }, ht([ol(), pt("design:type", Function), pt("design:paramtypes", [Object, Object, Object, Object]), pt("design:returntype", void 0)], yl.prototype, "upsert", null), ht([ol(), pt("design:type", Function), pt("design:paramtypes", ["function" == typeof(w = "undefined" != typeof T && T) ? w : Object]), pt("design:returntype", void 0)], yl.prototype, "toggleActive", null), yl), - ml = (ut(gl, hl = fl), gl.prototype.setInitialEntityState = function(e) { - this._akitaCreateEntityFn = e - }, gl); - - function gl(e, t) { - return hl.call(this, e = void 0 === e ? {} : e, t = void 0 === t ? {} : t) || this - } - - function yl(e, t) { - void 0 === t && (t = {}); - e = pl.call(this, ct({}, { - entities: {}, - ids: [], - loading: !0, - error: null - }, e = void 0 === e ? {} : e), t) || this; - return e.options = t, e.entityActions = new Xt, e.entityIdChanges = new Xt, e - } - - function vl() { - return Is(function(e, t) { - return e === t || !1 !== Fo(e) && !1 !== Fo(t) && (!(!Uo(e) || !Uo(t)) || !Sl(t, e) && !1 === Sl(e, t)) - }) - } - - function Sl(e, t) { - return t.some(function(t) { - return void 0 === e.find(function(e) { - return e === t - }) - }) - } - - function bl(i, e) { - for (var r, n, s, a = [], o = i.ids, l = i.entities, d = e.filterBy, t = e.limitTo, u = e.sortBy, e = e.sortByOrder, c = 0; c < o.length; c++) ! function(t) { - var i = l[o[t]]; - if (!d) return a.push(i); - Ro(d).every(function(e) { - return e(i, t) - }) && a.push(i) - }(c); - u && (s = $o(u) ? u : (r = u, void 0 === (n = e) && (n = cl.ASC), function(e, t) { - if (!e.hasOwnProperty(r) || !t.hasOwnProperty(r)) return 0; - var i = "string" == typeof e[r] ? e[r].toUpperCase() : e[r], - e = "string" == typeof t[r] ? t[r].toUpperCase() : t[r], - t = 0; - return e < i ? t = 1 : i < e && (t = -1), n == cl.DESC ? -1 * t : t - }), a = a.sort(function(e, t) { - return s(e, t, i) - })); - t = Math.min(t || a.length, a.length); - return t === a.length ? a : a.slice(0, t) - } - - function Tl(e) { - return "string" == typeof e - } - - function El(t, i) { - return function(e) { - e = e[t]; - if (!Vo(e)) return i ? Tl(i) ? e[i] : i(e) : e - } - }(vi = cl = cl || {}).ASC = "asc", vi.DESC = "desc"; - Il.prototype.select = function(t) { - var e, n; - if ($o(t)) e = t; - else if (Tl(t)) e = function(e) { - return e[t] - }; - else { - if (Array.isArray(t)) return this.store._select(function(e) { - return e - }).pipe(Is((n = t, function(t, i) { - var r = $o(n[0]); - return !1 === n.some(function(e) { - return r ? e(t) !== e(i) : t[e] !== i[e] - }) - })), hr(function(i) { - return $o(t[0]) ? t.map(function(e) { - return e(i) - }) : t.reduce(function(e, t) { - return e[t] = i[t], e - }, {}) - })); - e = function(e) { - return e - } - } - return this.store._select(e) - }, Il.prototype.selectLoading = function() { - return this.select(function(e) { - return e.loading - }) - }, Il.prototype.selectError = function() { - return this.select(function(e) { - return e.error - }) - }, Il.prototype.getValue = function() { - return this.store._value() - }, Il.prototype.selectHasCache = function() { - return this.store._cache().asObservable() - }, Il.prototype.getHasCache = function() { - return this.store._cache().value - }, Object.defineProperty(Il.prototype, "config", { - get: function() { - return this.constructor.akitaQueryConfig - }, - enumerable: !0, - configurable: !0 - }), bi = Il; - - function Il(e) { - this.store = e, this.__store__ = e, _o && (tl[e.storeName] = this) - } - - function wl(e) { - return e.pipe(ln(function(e) { - return null != e - })) - } - var Al, Ol, kl = (ut(Ml, Ol = bi), Ml.prototype.selectAll = function(e) { - var t = this; - return void 0 === e && (e = { - asObject: !1 - }), this.select(function(e) { - return e.entities - }).pipe(hr(function() { - return t.getAll(e) - })) - }, Ml.prototype.getAll = function(e) { - return (e = void 0 === e ? { - asObject: !1, - filterBy: void 0, - limitTo: void 0 - } : e).asObject ? function(e, t) { - var r = {}, - n = t.filterBy, - s = t.limitTo, - a = e.ids, - o = e.entities; - if (!n && !s) return o; - e = !1 === Po(s); - if (n && e) - for (var l = 0, i = 0, d = a.length; i < d && "break" !== function(t) { - if (l === s) return "break"; - var e = a[t], - i = o[e]; - Ro(n).every(function(e) { - return e(i, t) - }) && (r[e] = i, l++) - }(i); i++); - else - for (var u = Math.min(s || a.length, a.length), i = 0; i < u; i++) ! function(t) { - var e = a[t], - i = o[e]; - if (!n) return r[e] = i; - Ro(n).every(function(e) { - return e(i, t) - }) && (r[e] = i) - }(i); - return r - }(this.getValue(), e) : (t = e, i = this.config || this.options, t.sortBy = t.sortBy || i && i.sortBy, t.sortByOrder = t.sortByOrder || i && i.sortByOrder, bl(this.getValue(), e)); - var t, i - }, Ml.prototype.selectMany = function(e, i) { - return e && e.length ? this.select(function(e) { - return e.entities - }).pipe(hr(function(t) { - return n = function(e) { - return El(e, i)(t) - }, e.reduce(function(e, t, i, r) { - t = n(t); - return void 0 !== t && e.push(t), e - }, []); - var n - }), vl()) : $i([]) - }, Ml.prototype.selectEntity = function(e, t) { - var i = e; - return $o(e) && (i = function(e, t) { - var i, r; - try { - for (var n = ft(Object.keys(t)), s = n.next(); !s.done; s = n.next()) { - var a = s.value; - if (!0 === e(t[a])) return a - } - } catch (e) { - i = { - error: e - } - } finally { - try { - s && !s.done && (r = n.return) && r.call(n) - } finally { - if (i) throw i.error - } - } - }(e, this.getValue().entities)), this.select(function(e) { - return e.entities - }).pipe(hr(El(i, t)), Is()) - }, Ml.prototype.getEntity = function(e) { - return this.getValue().entities[e] - }, Ml.prototype.selectActiveId = function() { - return this.select(function(e) { - return e.active - }) - }, Ml.prototype.getActiveId = function() { - return this.getValue().active - }, Ml.prototype.selectActive = function(t) { - var i = this; - return Fo(this.getActive()) ? this.selectActiveId().pipe(La(function(e) { - return i.selectMany(e, t) - })) : this.selectActiveId().pipe(La(function(e) { - return i.selectEntity(e, t) - })) - }, Ml.prototype.getActive = function() { - var t = this, - e = this.getActiveId(); - return Fo(e) ? e.map(function(e) { - return t.getValue().entities[e] - }) : Jo(e) ? this.getEntity(e) : void 0 - }, Ml.prototype.selectCount = function(e) { - var t = this; - return this.select(function(e) { - return e.entities - }).pipe(hr(function() { - return t.getCount(e) - })) - }, Ml.prototype.getCount = function(e) { - return ($o(e) ? this.getAll().filter(e) : this.getValue().ids).length - }, Ml.prototype.selectLast = function(e) { - return this.selectAt(function(e) { - return e[e.length - 1] - }, e) - }, Ml.prototype.selectFirst = function(e) { - return this.selectAt(function(e) { - return e[0] - }, e) - }, Ml.prototype.selectEntityAction = function(e) { - if (Po(e)) return this.store.selectEntityAction$; - var t = Fo(e) ? function(e) { - return e - } : function(e) { - return e.ids - }, - i = Ro(e); - return this.store.selectEntityAction$.pipe(ln(function(e) { - e = e.type; - return i.includes(e) - }), hr(function(e) { - return t(e) - })) - }, Ml.prototype.hasEntity = function(e) { - var t = this; - return Po(e) ? 0 < this.getValue().ids.length : $o(e) ? this.getAll().some(e) : Fo(e) ? e.every(function(e) { - return e in t.getValue().entities - }) : e in this.getValue().entities - }, Ml.prototype.hasActive = function(e) { - var t = this.getValue().active, - i = Bo(e); - return Array.isArray(t) ? i ? t.includes(e) : 0 < t.length : i ? t === e : Bo(t) - }, Ml.prototype.createUIQuery = function() { - this.ui = new Cl(this.__store__.ui) - }, Ml.prototype.selectAt = function(e, t) { - var i = this; - return this.select(function(e) { - return e.ids - }).pipe(hr(e), Is(), La(function(e) { - return i.selectEntity(e, t) - })) - }, Ml), - Cl = (ut(Dl, Al = kl), Dl); - - function Dl(e) { - return Al.call(this, e) || this - } - - function Ml(e, t) { - void 0 === t && (t = {}); - var i = Ol.call(this, e) || this; - return i.options = t, i.__store__ = e, i - } - - function xl(e, t) { - return 1 === t.split(".").length ? e : t.split(".").slice(1).join(".").split(".").reduce(function(e, t) { - return e && e[t] - }, e) - } - - function Pl(e, t, r) { - var i = t.split("."); - if (1 === i.length) return ct({}, e, r); - e = ct({}, e); - var n = i.length - 2; - return t.split(".").slice(1).reduce(function(e, t, i) { - return e[t] = i !== n ? ct({}, e[t]) : Array.isArray(e[t]) || !No(e[t]) ? r : ct({}, e[t], r), e && e[t] - }, e), e - } - new Ji(1); - var Rl, Ll, _l, Nl, A = (Bl.prototype.getQuery = function() { - return this.query - }, Bl.prototype.getStore = function() { - return this.getQuery().__store__ - }, Bl.prototype.isEntityBased = Jo, Bl.prototype.selectSource = function(e, t) { - var i = this; - return this.isEntityBased(e) ? this.getQuery().selectEntity(e).pipe(wl) : t ? this.getQuery().select(function(e) { - return xl(e, i.withStoreName(t)) - }) : this.getQuery().select() - }, Bl.prototype.getSource = function(e, t) { - if (this.isEntityBased(e)) return this.getQuery().getEntity(e); - e = this.getQuery().getValue(); - return t ? xl(e, this.withStoreName(t)) : e - }, Bl.prototype.withStoreName = function(e) { - return this.storeName + "." + e - }, Object.defineProperty(Bl.prototype, "storeName", { - get: function() { - return this.getStore().storeName - }, - enumerable: !0, - configurable: !0 - }), Bl.prototype.updateStore = function(t, e, i) { - var r = this; - this.isEntityBased(e) ? this.getStore().update(e, t) : i ? this.getStore()._setState(function(e) { - return Pl(e, r.withStoreName(i), t) - }) : this.getStore()._setState(function(e) { - return ct({}, e, t) - }) - }, Bl.prototype.onReset = function(i) { - var r = this, - n = this.getStore().reset; - this.getStore().reset = function() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; - setTimeout(function() { - n.apply(r.getStore(), e), i() - }) - } - }, Bl), - Fl = { - pagesControls: !1, - range: !1, - startWith: 1, - cacheTimeout: void 0, - clearStoreWithCache: !0 - }; - - function Bl(e, t) { - this.query = e - } - - function Ul(e, t, i) { - void 0 === i && (i = {}); - var r = Rl.call(this, e) || this; - return r.query = e, r.factoryFnOrPath = t, r.params = i, r.params = ct({ - debounceTime: 300, - formKey: "akitaForm", - emitEvent: !1, - arrControlFactory: function(e) { - return r.builder.control(e) - } - }, i), r.isRootKeys = !1 === Jo(t), r.isKeyBased = Tl(t) || r.isRootKeys, r - } - - function $l(e, t) { - void 0 === t && (t = {}); - var i = Ll.call(this, e, { - resetFn: function() { - i.initial = !1, i.destroy({ - clearCache: !0, - currentPage: 1 - }) - } - }) || this; - i.query = e, i.config = t, i.metadata = new Map, i.pages = new Map, i.pagination = { - currentPage: 1, - perPage: 0, - total: 0, - lastPage: 0, - data: [] - }, i.initial = !0, i.isLoading$ = i.query.selectLoading().pipe(function(t) { - void 0 === t && (t = ir); - var i = as(0) ? 0 - t.now() : Math.abs(0); - return function(e) { - return e.lift(new ls(i, t)) - } - }()), i.config = Object.assign(Fl, t); - e = i.config, t = e.startWith, e = e.cacheTimeout; - return i.page = new yi(t), e && (e instanceof $t || "function" == typeof e.lift && "function" == typeof e.subscribe) && (i.clearCacheSubscription = e.subscribe(function() { - return i.clearCache() - })), i - } - ut($l, Ll = A), Object.defineProperty($l.prototype, "pageChanges", { - get: function() { - return this.page.asObservable() - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty($l.prototype, "currentPage", { - get: function() { - return this.pagination.currentPage - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty($l.prototype, "isFirst", { - get: function() { - return 1 === this.currentPage - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty($l.prototype, "isLast", { - get: function() { - return this.currentPage === this.pagination.lastPage - }, - enumerable: !0, - configurable: !0 - }), $l.prototype.withControls = function() { - return this.config.pagesControls = !0, this - }, $l.prototype.withRange = function() { - return this.config.range = !0, this - }, $l.prototype.setLoading = function(e) { - void 0 === e && (e = !0), this.getStore().setLoading(e) - }, $l.prototype.update = function(e) { - this.pagination = e, this.addPage(e.data) - }, $l.prototype.addPage = function(e) { - var t = this; - this.pages.set(this.currentPage, { - ids: e.map(function(e) { - return e[t.getStore().idKey] - }) - }), this.getStore().upsertMany(e) - }, $l.prototype.clearCache = function(e) { - void 0 === e && (e = {}), this.initial || (Do("@Pagination - Clear Cache"), !1 !== e.clearStore && (this.config.clearStoreWithCache || e.clearStore) && this.getStore().remove(), this.pages = new Map, this.metadata = new Map), this.initial = !1 - }, $l.prototype.clearPage = function(e) { - this.pages.delete(e) - }, $l.prototype.destroy = function(e) { - var t = void 0 === e ? {} : e, - e = t.clearCache, - t = t.currentPage; - this.clearCacheSubscription && this.clearCacheSubscription.unsubscribe(), e && this.clearCache(), Vo(t) || this.setPage(t), this.initial = !0 - }, $l.prototype.isPageActive = function(e) { - return this.currentPage === e - }, $l.prototype.setPage = function(e) { - e === this.currentPage && this.hasPage(e) || this.page.next(this.pagination.currentPage = e) - }, $l.prototype.nextPage = function() { - this.currentPage !== this.pagination.lastPage && this.setPage(this.pagination.currentPage + 1) - }, $l.prototype.prevPage = function() { - 1 < this.pagination.currentPage && this.setPage(this.pagination.currentPage - 1) - }, $l.prototype.setLastPage = function() { - this.setPage(this.pagination.lastPage) - }, $l.prototype.setFirstPage = function() { - this.setPage(1) - }, $l.prototype.hasPage = function(e) { - return this.pages.has(e) - }, $l.prototype.getPage = function(e) { - var t = this, - i = this.pagination.currentPage; - return this.hasPage(i) ? this.selectPage(i) : (this.setLoading(!0), Fr(e()).pipe(La(function(e) { - return i = e.currentPage, al(function() { - t.setLoading(!1), t.update(e) - }), t.selectPage(i) - }))) - }, $l.prototype.getQuery = function() { - return this.query - }, $l.prototype.refreshCurrentPage = function() { - !1 === Po(this.currentPage) && (this.clearPage(this.currentPage), this.setPage(this.currentPage)) - }, $l.prototype.getFrom = function() { - return this.isFirst ? 1 : (this.currentPage - 1) * this.pagination.perPage + 1 - }, $l.prototype.getTo = function() { - return this.isLast ? this.pagination.total : this.currentPage * this.pagination.perPage - }, $l.prototype.selectPage = function(n) { - var s = this; - return this.query.selectAll({ - asObject: !0 - }).pipe(Ds(1), hr(function(t) { - var e = ct({}, s.pagination, { - data: s.pages.get(n).ids.map(function(e) { - return t[e] - }) - }), - i = s.config, - r = i.range, - i = i.pagesControls; - return isNaN(s.pagination.total) && (1 === e.lastPage ? e.total = e.data ? e.data.length : 0 : e.total = e.perPage * e.lastPage, s.pagination.total = e.total), r && (e.from = s.getFrom(), e.to = s.getTo()), i && (e.pageControls = function(e, t) { - for (var i = Math.ceil(e / t), r = [], n = 0; n < i; n++) r.push(n + 1); - return r - }(s.pagination.total, s.pagination.perPage)), e - })) - }, ht([(_l = "@Pagination - New Page", function(e, t, i) { - var r = i.value; - return i.value = function() { - for (var e = [], t = 0; t < arguments.length; t++) e[t] = arguments[t]; - return Do(_l, Nl), r.apply(this, e) - }, i - }), pt("design:type", Function), pt("design:paramtypes", [Object]), pt("design:returntype", void 0)], $l.prototype, "update", null), ut(Ul, Rl = A), Ul.prototype.setForm = function(e, t) { - return this.form = e, this.builder = t, this.activate(), this - }, Ul.prototype.reset = function(e) { - var r = this, - e = e || (this.isKeyBased ? this.initialValue : this.factoryFnOrPath()); - this.isKeyBased && Object.keys(this.initialValue).forEach(function(i) { - var e, t = r.initialValue[i]; - Array.isArray(t) && r.builder && (e = r.form.controls[i], r.cleanArray(e), t.forEach(function(e, t) { - r.form.get(i).insert(t, r.params.arrControlFactory(e)) - })) - }), this.form.patchValue(e, { - emitEvent: this.params.emitEvent - }); - var t = this.isKeyBased ? Pl(this.getQuery().getValue(), this.getStore().storeName + "." + this.factoryFnOrPath, e) : ((t = {})[this.params.formKey] = e, t); - this.updateStore(t) - }, Ul.prototype.cleanArray = function(e) { - for (; 0 !== e.length;) e.removeAt(0) - }, Ul.prototype.resolveInitialValue = function(e, n) { - var s = this; - if (e) return Object.keys(e).reduce(function(e, i) { - var r, t = n[i]; - return Array.isArray(t) && s.builder && (r = s.params.arrControlFactory, s.cleanArray(s.form.get(i)), t.forEach(function(e, t) { - s.form.get(i).insert(t, r(e)) - })), e[i] = n[i], e - }, {}) - }, Ul.prototype.activate = function() { - var i, e, t, r = this; - this.isKeyBased ? (this.isRootKeys ? this.initialValue = this.resolveInitialValue(this.form.value, this.getQuery().getValue()) : (i = this.getStore().storeName + "." + this.factoryFnOrPath, e = xl(this.getQuery().getValue(), i), this.initialValue = this.resolveInitialValue(e, e)), this.form.patchValue(this.initialValue, { - emitEvent: this.params.emitEvent - })) : (this.getQuery().getValue()[this.params.formKey] || (Do("@PersistNgFormPlugin activate"), this.updateStore(((t = {})[this.params.formKey] = this.factoryFnOrPath(), t))), t = this.getQuery().getValue()[this.params.formKey], this.form.patchValue(t)), this.formChanges = this.form.valueChanges.pipe(Gn(this.params.debounceTime)).subscribe(function(t) { - var e; - Do("@PersistForm - Update"), e = r.isKeyBased ? r.isRootKeys ? function(e) { - return ct({}, e, t) - } : function(e) { - return Pl(e, i, t) - } : function() { - var e; - return (e = {})[r.params.formKey] = t, e - }, r.updateStore(e(r.getQuery().getValue())) - }) - }, Ul.prototype.destroy = function() { - this.formChanges && this.formChanges.unsubscribe(), this.form = null, this.builder = null - }; - var Vl, Kl, Vr = (jl.prototype.getEntity = function(e) { - return this.entities.get(e) - }, jl.prototype.hasEntity = function(e) { - return this.entities.has(e) - }, jl.prototype.removeEntity = function(e) { - return this.destroy(e), this.entities.delete(e) - }, jl.prototype.createEntity = function(e, t) { - return this.entities.set(e, t) - }, jl.prototype.getIds = function() { - return Vo(this.entityIds) ? this.query.getValue().ids : Ro(this.entityIds) - }, jl.prototype.resolvedIds = function(e) { - return Vo(e) ? this.getIds() : Ro(e) - }, jl.prototype.rebase = function(i, r) { - var n = this; - if (void 0 === r && (r = {}), Jo(i)) - if (Vo(this.entityIds)) { - for (var e = 0, t = i.length; e < t; e++) { - var s, a = i[e]; - !1 === this.hasEntity(a) && ($o(r.beforeAdd) && r.beforeAdd(a), s = this.instantiatePlugin(a), this.entities.set(a, s), $o(r.afterAdd) && r.afterAdd(s)) - } - this.entities.forEach(function(e, t) { - -1 === i.indexOf(t) && ($o(r.beforeRemove) && r.beforeRemove(e), n.removeEntity(t)) - }) - } else - for (var o = Ro(this.entityIds), e = 0, t = o.length; e < t; e++) a = o[e], -1 < i.indexOf(a) && !1 === this.hasEntity(a) ? ($o(r.beforeAdd) && r.beforeAdd(a), s = this.instantiatePlugin(a), this.entities.set(a, s), $o(r.afterAdd) && r.afterAdd(s)) : this.entities.forEach(function(e, t) { - -1 === i.indexOf(t) && !0 === n.hasEntity(t) && ($o(r.beforeRemove) && r.beforeRemove(e), n.removeEntity(t)) - }); - else this.getIds().forEach(function(e) { - n.hasEntity(e) || n.createEntity(e, n.instantiatePlugin(e)) - }) - }, jl.prototype.selectIds = function() { - return this.query.select(function(e) { - return e.ids - }) - }, jl.prototype.activate = function(e) { - this.rebase(e) - }, jl.prototype.forEachId = function(e, t) { - for (var i = this.resolvedIds(e), r = 0, n = i.length; r < n; r++) { - var s = i[r]; - this.hasEntity(s) && t(this.getEntity(s)) - } - }, jl), - ql = (ut(Hl, Vl = A), Object.defineProperty(Hl.prototype, "hasPast$", { - get: function() { - return this._hasPast$ - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(Hl.prototype, "hasFuture$", { - get: function() { - return this._hasFuture$ - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(Hl.prototype, "hasPast", { - get: function() { - return 0 < this.history.past.length - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(Hl.prototype, "hasFuture", { - get: function() { - return 0 < this.history.future.length - }, - enumerable: !0, - configurable: !0 - }), Object.defineProperty(Hl.prototype, "property", { - get: function() { - return this.params.watchProperty - }, - enumerable: !0, - configurable: !0 - }), Hl.prototype.updateHasHistory = function() { - this.hasFutureSubject.next(this.hasFuture), this.hasPastSubject.next(this.hasPast) - }, Hl.prototype.activate = function() { - var r = this; - this.hasPastSubject = new yi(!1), this._hasPast$ = this.hasPastSubject.asObservable().pipe(Is()), this.hasFutureSubject = new yi(!1), this._hasFuture$ = this.hasFutureSubject.asObservable().pipe(Is()), this.history.present = this.getSource(this._entityId, this.property), this.subscription = this.selectSource(this._entityId, this.property).pipe(ha()).subscribe(function(e) { - var t = mt(e, 2), - i = t[0], - e = t[1]; - r.skip ? r.skip = !1 : (t = r.params.comparator(i, e), !r.skipUpdate && t && (r.history.past.length === r.params.maxAge && (r.history.past = r.history.past.slice(1)), r.history.past = gt(r.history.past, [i]), r.history.present = e, r.updateHasHistory())) - }) - }, Hl.prototype.undo = function() { - var e, t, i; - 0 < this.history.past.length && (e = (i = this.history).past, t = i.present, i = e[e.length - 1], this.history.past = e.slice(0, e.length - 1), this.history.present = i, this.history.future = gt([t], this.history.future), this.update()) - }, Hl.prototype.redo = function() { - var e, t, i, r; - 0 < this.history.future.length && (e = (r = this.history).past, t = r.present, i = this.history.future[0], r = this.history.future.slice(1), this.history.past = gt(e, [t]), this.history.present = i, this.history.future = r, this.update("Redo")) - }, Hl.prototype.jumpToPast = function(e) { - var t, i, r, n; - e < 0 || e >= this.history.past.length || (t = (r = this.history).past, n = r.future, i = r.present, r = t.slice(0, e), n = gt(t.slice(e + 1), [i], n), e = t[e], this.history.past = r, this.history.present = e, this.history.future = n, this.update()) - }, Hl.prototype.jumpToFuture = function(e) { - var t, i, r; - e < 0 || e >= this.history.future.length || (i = (r = this.history).past, t = r.future, i = gt(i, [r.present], t.slice(0, e)), r = t[e], e = t.slice(e + 1), this.history.past = i, this.history.present = r, this.history.future = e, this.update("Redo")) - }, Hl.prototype.jump = function(e) { - return 0 < e ? this.jumpToFuture(e - 1) : e < 0 ? this.jumpToPast(this.history.past.length + e) : void 0 - }, Hl.prototype.clear = function(e) { - this.history = $o(e) ? e(this.history) : { - past: [], - present: null, - future: [] - }, this.updateHasHistory() - }, Hl.prototype.destroy = function(e) { - (e = void 0 === e ? !1 : e) && this.clear(), this.subscription.unsubscribe() - }, Hl.prototype.ignoreNext = function() { - this.skip = !0 - }, Hl.prototype.update = function(e) { - void 0 === e && (e = "Undo"), this.skipUpdate = !0, Do("@StateHistory - " + e), this.updateStore(this.history.present, this._entityId, this.property), this.updateHasHistory(), this.skipUpdate = !1 - }, Hl); - - function Hl(e, t, i) { - void 0 === t && (t = {}); - var r = Vl.call(this, e, { - resetFn: function() { - return r.clear() - } - }) || this; - return r.query = e, r.params = t, r._entityId = i, r.skip = !1, r.history = { - past: [], - present: null, - future: [] - }, r.skipUpdate = !1, t.maxAge = t.maxAge || 10, t.comparator = t.comparator || function() { - return !0 - }, r.activate(), r - } - - function jl(e, t) { - this.query = e, this.entityIds = t, this.entities = new Map - } - - function Ql(e, t) { - var i = Kl.call(this, e, (t = void 0 === t ? {} : t).entityIds) || this; - return i.query = e, (i.params = t).maxAge = Jo(t.maxAge) ? t.maxAge : 10, i.activate(), i.selectIds().pipe(ka(1)).subscribe(function(e) { - return i.activate(e) - }), i - } - ut(Ql, Kl = Vr), Ql.prototype.redo = function(e) { - this.forEachId(e, function(e) { - return e.redo() - }) - }, Ql.prototype.undo = function(e) { - this.forEachId(e, function(e) { - return e.undo() - }) - }, Ql.prototype.hasPast = function(e) { - if (this.hasEntity(e)) return this.getEntity(e).hasPast - }, Ql.prototype.hasFuture = function(e) { - if (this.hasEntity(e)) return this.getEntity(e).hasFuture - }, Ql.prototype.jumpToFuture = function(e, t) { - this.forEachId(e, function(e) { - return e.jumpToFuture(t) - }) - }, Ql.prototype.jumpToPast = function(e, t) { - this.forEachId(e, function(e) { - return e.jumpToPast(t) - }) - }, Ql.prototype.clear = function(e) { - this.forEachId(e, function(e) { - return e.clear() - }) - }, Ql.prototype.destroy = function(e, t) { - void 0 === t && (t = !1), this.forEachId(e, function(e) { - return e.destroy(t) - }) - }, Ql.prototype.ignoreNext = function(e) { - this.forEachId(e, function(e) { - return e.ignoreNext() - }) - }, Ql.prototype.instantiatePlugin = function(e) { - return new ql(this.query, this.params, e) - }; - var Wl = { - comparator: function(e, t) { - return JSON.stringify(e) !== JSON.stringify(t) - } - }; - - function Gl(e, t) { - return t.split(".").reduce(function(e, t) { - return e && "undefined" !== e[t] ? e[t] : void 0 - }, e) - } - var zl, Xl, Yl = (ut(Jl, zl = A), Jl.prototype.reset = function(e) { - var t = this.head; - $o((e = void 0 === e ? {} : e).updateFn) && (t = this.isEntityBased(this._entityId) ? e.updateFn(this.head, this.getQuery().getEntity(this._entityId)) : e.updateFn(this.head, this.getQuery().getValue())), Do("@DirtyCheck - Revert"), this.updateStore(t, this._entityId), this._reset.next() - }, Jl.prototype.setHead = function() { - return this.active ? this.head = this._getHead() : (this.activate(), this.active = !0), this.updateDirtiness(!1), this - }, Jl.prototype.isDirty = function() { - return !!this.dirty.value - }, Jl.prototype.hasHead = function() { - return !!this.getHead() - }, Jl.prototype.destroy = function() { - this.head = null, this.subscription && this.subscription.unsubscribe(), this._reset && this._reset.complete() - }, Jl.prototype.isPathDirty = function(e) { - var t = this.getHead(), - i = Gl(this.getQuery().getValue(), e), - e = Gl(t, e); - return this.params.comparator(i, e) - }, Jl.prototype.getHead = function() { - return this.head - }, Jl.prototype.activate = function() { - var i = this; - this.head = this._getHead(); - var e = this.params.watchProperty ? this.params.watchProperty.map(function(t) { - return i.query.select(function(e) { - return e[t] - }).pipe(hr(function(e) { - return { - val: e, - __akitaKey: t - } - })) - }) : [this.selectSource(this._entityId)]; - this.subscription = Mr.apply(void 0, gt(e)).pipe(ka(1)).subscribe(function(e) { - Vo(i.head) || (e = e.some(function(e) { - var t = e.__akitaKey ? i.head[e.__akitaKey] : i.head, - e = e.__akitaKey ? e.val : e; - return i.params.comparator(t, e) - }), i.updateDirtiness(e)) - }) - }, Jl.prototype.updateDirtiness = function(e) { - this.dirty.next(e) - }, Jl.prototype._getHead = function() { - var e = this.getSource(this._entityId); - return e = this.params.watchProperty ? this.getWatchedValues(e) : e - }, Jl.prototype.getWatchedValues = function(i) { - return this.params.watchProperty.reduce(function(e, t) { - return e[t] = i[t], e - }, {}) - }, Jl); - - function Jl(e, t, i) { - var r = zl.call(this, e) || this; - return r.query = e, r.params = t, r._entityId = i, r.dirty = new yi(!1), r.active = !1, r._reset = new Xt, r.isDirty$ = r.dirty.asObservable().pipe(Is()), r.reset$ = r._reset.asObservable(), r.params = ct({}, Wl, t), r.params.watchProperty && (t = Ro(r.params.watchProperty), e instanceof kl && t.includes("entities") && !t.includes("ids") && t.push("ids"), r.params.watchProperty = t), r - } - - function Zl() { - return Math.random().toString(36).slice(2) - } - - function ed(e) { - return Mr(e).pipe($n(0)) - } - - function td(e, t) { - var i = Xl.call(this, e, (t = void 0 === t ? {} : t).entityIds) || this; - return i.query = e, i.params = t, i._someDirty = new Xt, i.someDirty$ = an(i.query.select(function(e) { - return e.entities - }), i._someDirty.asObservable()).pipe($n(0), hr(function() { - return i.checkSomeDirty() - })), i.params = ct({}, Wl, t), i.activate(), i.selectIds().pipe(ka(1)).subscribe(function(e) { - Xl.prototype.rebase.call(i, e, { - afterAdd: function(e) { - return e.setHead() - } - }) - }), i - } - ut(td, Xl = Vr), td.prototype.setHead = function(e) { - if (this.params.entityIds && e) { - var t = Ro(e); - if (!1 === Ro(this.params.entityIds).some(function(e) { - return -1 < t.indexOf(e) - })) return this - } - return this.forEachId(e, function(e) { - return e.setHead() - }), this._someDirty.next(), this - }, td.prototype.hasHead = function(e) { - return !!this.entities.has(e) && this.getEntity(e).hasHead() - }, td.prototype.reset = function(e, t) { - void 0 === t && (t = {}), this.forEachId(e, function(e) { - return e.reset(t) - }) - }, td.prototype.isDirty = function(e, t) { - if (void 0 === t && (t = !0), this.entities.has(e)) { - e = this.getEntity(e); - return t ? e.isDirty$ : e.isDirty() - } - return !1 - }, td.prototype.someDirty = function() { - return this.checkSomeDirty() - }, td.prototype.isPathDirty = function(e, t) { - if (this.entities.has(e)) { - var i = this.getEntity(e).getHead(), - e = Gl(this.query.getEntity(e), t), - t = Gl(i, t); - return this.params.comparator(e, t) - } - return null - }, td.prototype.destroy = function(e) { - this.forEachId(e, function(e) { - return e.destroy() - }), e || this._someDirty.complete() - }, td.prototype.instantiatePlugin = function(e) { - return new Yl(this.query, this.params, e) - }, td.prototype.checkSomeDirty = function() { - var e, t, i = this.resolvedIds(); - try { - for (var r = ft(i), n = r.next(); !n.done; n = r.next()) { - var s = n.value; - if (this.getEntity(s).isDirty()) return !0 - } - } catch (t) { - e = { - error: t - } - } finally { - try { - n && !n.done && (t = r.return) && t.call(r) - } finally { - if (e) throw e.error - } - } - return !1 - }, (tu = tu || {}).Update = "UPDATE", {} [tu.Update] = "update", (vr = ru = ru || {}).Update = "UPDATE", vr.AddEntities = "ADD_ENTITIES", vr.SetEntities = "SET_ENTITIES", vr.UpdateEntities = "UPDATE_ENTITIES", vr.RemoveEntities = "REMOVE_ENTITIES", vr.UpsertEntities = "UPSERT_ENTITIES", vr.UpsertManyEntities = "UPSERT_MANY_ENTITIES", (iu = {})[ru.Update] = "update", iu[ru.AddEntities] = "add", iu[ru.SetEntities] = "set", iu[ru.UpdateEntities] = "update", iu[ru.RemoveEntities] = "remove", iu[ru.UpsertEntities] = "upsert", iu[ru.UpsertManyEntities] = "upsertMany"; - w = {}; - Object.defineProperty(w, "__esModule", { - value: !0 + Object.defineProperty(logger, 'level', { + get: getLevel, + set: setLevel }); - var vi = "undefined" != typeof Symbol && "symbol" == typeof Symbol("x"), - id = "undefined" != typeof Map, - rd = "undefined" != typeof Set, - nd = "undefined" != typeof Proxy && void 0 !== Proxy.revocable && "undefined" != typeof Reflect, - sd = vi ? Symbol.for("immer-nothing") : ((nu = {})["immer-nothing"] = !0, nu), - ad = vi ? Symbol.for("immer-draftable") : "__$immer_draftable", - od = vi ? Symbol.for("immer-state") : "__$immer_state", - ld = "undefined" != typeof Symbol && Symbol.iterator || "@@iterator", - dd = { - 0: "Illegal state", - 1: "Immer drafts cannot have computed properties", - 2: "This object has been frozen and should not be mutated", - 3: function(e) { - return "Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + e - }, - 4: "An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.", - 5: "Immer forbids circular references", - 6: "The first or second argument to `produce` must be a function", - 7: "The third argument to `produce` must be a function or undefined", - 8: "First argument to `createDraft` must be a plain object, an array, or an immerable object", - 9: "First argument to `finishDraft` must be a draft returned by `createDraft`", - 10: "The given draft is already finalized", - 11: "Object.defineProperty() cannot be used on an Immer draft", - 12: "Object.setPrototypeOf() cannot be used on an Immer draft", - 13: "Immer only supports deleting array indices", - 14: "Immer only supports setting array indices and the 'length' property", - 15: function(e) { - return "Cannot apply patch, path doesn't resolve: " + e - }, - 16: 'Sets cannot have "replace" patches.', - 17: function(e) { - return "Unsupported patch operation: " + e - }, - 18: function(e) { - return "The plugin for '" + e + "' has not been loaded into Immer. To enable the plugin, import and call `enable" + e + "()` when initializing your application." - }, - 20: "Cannot use proxies if Proxy, Proxy.revocable or Reflect are not available", - 21: function(e) { - return "produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '" + e + "'" - }, - 22: function(e) { - return "'current' expects a draft, got: " + e - }, - 23: function(e) { - return "'original' expects a draft, got: " + e - } - }; - - function ud(e) { - for (var t = arguments.length, i = new Array(1 < t ? t - 1 : 0), r = 1; r < t; r++) i[r - 1] = arguments[r]; - var n = dd[e], - e = n ? "function" == typeof n ? n.apply(null, i) : n : "unknown error nr: " + e; - throw new Error("[Immer] " + e) - } - - function cd(e) { - return !!e && !!e[od] - } - - function hd(t) { - return !!t && (function() { - if (!t || "object" != typeof t) return !1; - var e = Object.getPrototypeOf(t); - return !e || e === Object.prototype - }() || Array.isArray(t) || !!t[ad] || !!t.constructor[ad] || Td(t) || Ed(t)) - } - var pd = "undefined" != typeof Reflect && Reflect.ownKeys ? Reflect.ownKeys : void 0 !== Object.getOwnPropertySymbols ? function(e) { - return Object.getOwnPropertyNames(e).concat(Object.getOwnPropertySymbols(e)) - } : Object.getOwnPropertyNames, - fd = Object.getOwnPropertyDescriptors || function(t) { - var i = {}; - return pd(t).forEach(function(e) { - i[e] = Object.getOwnPropertyDescriptor(t, e) - }), i - }; - - function md(i, r, t) { - void 0 === t && (t = !1), 0 === gd(i) ? (t ? Object.keys : pd)(i).forEach(function(e) { - t && "symbol" == typeof e || r(e, i[e], i) - }) : i.forEach(function(e, t) { - return r(t, e, i) - }) - } - - function gd(e) { - var t = e[od]; - return t ? 3 < t.type_ ? t.type_ - 4 : t.type_ : Array.isArray(e) ? 1 : Td(e) ? 2 : Ed(e) ? 3 : 0 - } - - function yd(e, t) { - return 2 === gd(e) ? e.has(t) : Object.prototype.hasOwnProperty.call(e, t) - } - - function vd(e, t) { - return 2 === gd(e) ? e.get(t) : e[t] - } - - function Sd(e, t, i) { - var r = gd(e); - 2 === r ? e.set(t, i) : 3 === r ? (e.delete(t), e.add(i)) : e[t] = i - } - - function bd(e, t) { - return e === t ? 0 !== e || 1 / e == 1 / t : e != e && t != t - } - - function Td(e) { - return id && e instanceof Map - } - - function Ed(e) { - return rd && e instanceof Set - } - - function Id(e) { - return e.copy_ || e.base_ - } - - function wd(e) { - if (Array.isArray(e)) return Array.prototype.slice.call(e); - var t = fd(e); - delete t[od]; - for (var i = pd(t), r = 0; r < i.length; r++) { - var n = i[r], - s = t[n]; - !1 === s.writable && (s.writable = !0, s.configurable = !0), (s.get || s.set) && (t[n] = { - configurable: !0, - writable: !0, - enumerable: s.enumerable, - value: e[n] - }) - } - return Object.create(Object.getPrototypeOf(e), t) - } - - function Ad(e, t) { - kd(e) || cd(e) || !hd(e) || (1 < gd(e) && (e.set = e.add = e.clear = e.delete = Od), Object.freeze(e), t && md(e, function(e, t) { - return Ad(t, !0) - }, !0)) - } - - function Od() { - ud(2) - } - - function kd(e) { - return null == e || "object" != typeof e || Object.isFrozen(e) - } - var Cd, Dd = {}; - - function Md(e) { - var t = Dd[e]; - return t || ud(18, e), t - } - - function xd(e, t) { - Dd[e] || (Dd[e] = t) - } - - function Pd() { - return Cd || ud(0), Cd - } - - function Rd(e, t) { - t && (Md("Patches"), e.patches_ = [], e.inversePatches_ = [], e.patchListener_ = t) - } - - function Ld(e) { - _d(e), e.drafts_.forEach(Fd), e.drafts_ = null - } - - function _d(e) { - e === Cd && (Cd = e.parent_) - } - - function Nd(e) { - return Cd = { - drafts_: [], - parent_: Cd, - immer_: e, - canAutoFreeze_: !0, - unfinalizedDrafts_: 0 - } - } - - function Fd(e) { - e = e[od]; - 0 === e.type_ || 1 === e.type_ ? e.revoke_() : e.revoked_ = !0 - } - - function Bd(e, t) { - t.unfinalizedDrafts_ = t.drafts_.length; - var i = t.drafts_[0], - r = void 0 !== e && e !== i; - return t.immer_.useProxies_ || Md("ES5").willFinalizeES5_(t, e, r), r ? (i[od].modified_ && (Ld(t), ud(4)), hd(e) && (e = Ud(t, e), t.parent_ || Vd(t, e)), t.patches_ && Md("Patches").generateReplacementPatches_(i[od], e, t.patches_, t.inversePatches_)) : e = Ud(t, i, []), Ld(t), t.patches_ && t.patchListener_(t.patches_, t.inversePatches_), e !== sd ? e : void 0 - } - - function Ud(i, r, n) { - if (kd(r)) return r; - var s, a = r[od]; - return a ? a.scope_ !== i ? r : a.modified_ ? (a.finalized_ || (a.finalized_ = !0, a.scope_.unfinalizedDrafts_--, s = 4 === a.type_ || 5 === a.type_ ? a.copy_ = wd(a.draft_) : a.copy_, md(3 === a.type_ ? new Set(s) : s, function(e, t) { - return $d(i, a, s, e, t, n) - }), Vd(i, s, !1), n && i.patches_ && Md("Patches").generatePatches_(a, n, i.patches_, i.inversePatches_)), a.copy_) : (Vd(i, a.base_, !0), a.base_) : (md(r, function(e, t) { - return $d(i, a, r, e, t, n) - }, !0), r) - } - - function $d(e, t, i, r, n, s) { - if (n === i && ud(5), cd(n)) { - s = Ud(e, n, s && t && 3 !== t.type_ && !yd(t.assigned_, r) ? s.concat(r) : void 0); - if (Sd(i, r, s), !cd(s)) return; - e.canAutoFreeze_ = !1 - } - hd(n) && !kd(n) && (!e.immer_.autoFreeze_ && e.unfinalizedDrafts_ < 1 || (Ud(e, n), t && t.scope_.parent_ || Vd(e, n))) - } - - function Vd(e, t, i) { - void 0 === i && (i = !1), e.immer_.autoFreeze_ && e.canAutoFreeze_ && Ad(t, i) - } - var Kd = { - get: function(e, t) { - if (t === od) return e; - var i, r, n = Id(e); - if (!yd(n, t)) return i = e, (r = jd(n, t)) ? "value" in r ? r.value : null === (r = r.get) || void 0 === r ? void 0 : r.call(i.draft_) : void 0; - n = n[t]; - return !e.finalized_ && hd(n) && n === Hd(e.base_, t) ? (Wd(e), e.copy_[t] = zd(e.scope_.immer_, n, e)) : n - }, - has: function(e, t) { - return t in Id(e) - }, - ownKeys: function(e) { - return Reflect.ownKeys(Id(e)) - }, - set: function(e, t, i) { - var r = jd(Id(e), t); - if (null != r && r.set) return r.set.call(e.draft_, i), !0; - if (!e.modified_) { - var n = Hd(Id(e), t), - r = null == n ? void 0 : n[od]; - if (r && r.base_ === i) return e.copy_[t] = i, !(e.assigned_[t] = !1); - if (bd(i, n) && (void 0 !== i || yd(e.base_, t))) return !0; - Wd(e), Qd(e) - } - return e.copy_[t] = i, e.assigned_[t] = !0 - }, - deleteProperty: function(e, t) { - return void 0 !== Hd(e.base_, t) || t in e.base_ ? (e.assigned_[t] = !1, Wd(e), Qd(e)) : delete e.assigned_[t], e.copy_ && delete e.copy_[t], !0 - }, - getOwnPropertyDescriptor: function(e, t) { - var i = Id(e), - r = Reflect.getOwnPropertyDescriptor(i, t); - return r && { - writable: !0, - configurable: 1 !== e.type_ || "length" !== t, - enumerable: r.enumerable, - value: i[t] - } - }, - defineProperty: function() { - ud(11) - }, - getPrototypeOf: function(e) { - return Object.getPrototypeOf(e.base_) - }, - setPrototypeOf: function() { - ud(12) - } - }, - qd = {}; - - function Hd(e, t) { - var i = e[od]; - return (i ? Id(i) : e)[t] - } - - function jd(e, t) { - if (t in e) - for (var i = Object.getPrototypeOf(e); i;) { - var r = Object.getOwnPropertyDescriptor(i, t); - if (r) return r; - i = Object.getPrototypeOf(i) - } - } - - function Qd(e) { - e.modified_ || (e.modified_ = !0, e.parent_ && Qd(e.parent_)) - } - - function Wd(e) { - e.copy_ || (e.copy_ = wd(e.base_)) - } - md(Kd, function(e, t) { - qd[e] = function() { - return arguments[0] = arguments[0][0], t.apply(this, arguments) - } - }), qd.deleteProperty = function(e, t) { - return isNaN(parseInt(t)) && ud(13), Kd.deleteProperty.call(this, e[0], t) - }, qd.set = function(e, t, i) { - return "length" !== t && isNaN(parseInt(t)) && ud(14), Kd.set.call(this, e[0], t, i, e[0]) + + const setOpts = { + transmit, + serialize, + asObject: opts.browser.asObject, + levels, + timestamp: getTimeFunction(opts) }; - (A = Gd.prototype).produce = function(e, s, t) { - if ("function" == typeof e && "function" != typeof s) { - var a = s; - s = e; - var o = this; - return function(e) { - var t = this; - void 0 === e && (e = a); - for (var i = arguments.length, r = new Array(1 < i ? i - 1 : 0), n = 1; n < i; n++) r[n - 1] = arguments[n]; - return o.produce(e, function(e) { - return s.call.apply(s, [t, e].concat(r)) - }) - } - } - var i; - if ("function" != typeof s && ud(6), void 0 !== t && "function" != typeof t && ud(7), hd(e)) { - var r = Nd(this), - n = zd(this, e, void 0), - l = !0; - try { - i = s(n), l = !1 - } finally { - (l ? Ld : _d)(r) - } - return "undefined" != typeof Promise && i instanceof Promise ? i.then(function(e) { - return Rd(r, t), Bd(e, r) - }, function(e) { - throw Ld(r), e - }) : (Rd(r, t), Bd(i, r)) - } - if (!e || "object" != typeof e) return (i = s(e)) === sd ? void 0 : (void 0 === i && (i = e), this.autoFreeze_ && Ad(i, !0), i); - ud(21, e) - }, A.produceWithPatches = function(n, e, t) { - var i, r, s = this; - return "function" == typeof n ? function(e) { - for (var t = arguments.length, i = new Array(1 < t ? t - 1 : 0), r = 1; r < t; r++) i[r - 1] = arguments[r]; - return s.produceWithPatches(e, function(e) { - return n.apply(void 0, [e].concat(i)) - }) - } : [this.produce(n, e, function(e, t) { - i = e, r = t - }), i, r] - }, A.createDraft = function(e) { - hd(e) || ud(8), cd(e) && (e = Xd(e)); - var t = Nd(this), - e = zd(this, e, void 0); - return e[od].isManual_ = !0, _d(t), e - }, A.finishDraft = function(e, t) { - e = e && e[od]; - e && e.isManual_ || ud(9), e.finalized_ && ud(10); - e = e.scope_; - return Rd(e, t), Bd(void 0, e) - }, A.setAutoFreeze = function(e) { - this.autoFreeze_ = e - }, A.setUseProxies = function(e) { - e && !nd && ud(20), this.useProxies_ = e - }, A.applyPatches = function(e, t) { - for (var i = t.length - 1; 0 <= i; i--) { - var r = t[i]; - if (0 === r.path.length && "replace" === r.op) { - e = r.value; - break - } - } - var n = Md("Patches").applyPatches_; - return cd(e) ? n(e, t) : this.produce(e, function(e) { - return n(e, t.slice(i + 1)) - }) - }, Vr = Gd; - - function Gd(e) { - this.useProxies_ = nd, this.autoFreeze_ = !0, "boolean" == typeof(null == e ? void 0 : e.useProxies) && this.setUseProxies(e.useProxies), "boolean" == typeof(null == e ? void 0 : e.autoFreeze) && this.setAutoFreeze(e.autoFreeze), this.produce = this.produce.bind(this), this.produceWithPatches = this.produceWithPatches.bind(this) - } - - function zd(e, t, i) { - t = Td(t) ? Md("MapSet").proxyMap_(t, i) : Ed(t) ? Md("MapSet").proxySet_(t, i) : e.useProxies_ ? function(e, t) { - var i = Array.isArray(e), - r = { - type_: i ? 1 : 0, - scope_: t ? t.scope_ : Pd(), - modified_: !1, - finalized_: !1, - assigned_: {}, - parent_: t, - base_: e, - draft_: null, - copy_: null, - revoke_: null, - isManual_: !1 - }, - t = r, - e = Kd; - i && (t = [r], e = qd); - t = Proxy.revocable(t, e), e = t.revoke, t = t.proxy; - return r.draft_ = t, r.revoke_ = e, t - }(t, i) : Md("ES5").createES5Proxy_(t, i); - return (i ? i.scope_ : Pd()).drafts_.push(t), t - } - - function Xd(e) { - return cd(e) || ud(22, e), - function i(e) { - if (!hd(e)) return e; - var r, n = e[od], - t = gd(e); - if (n) { - if (!n.modified_ && (n.type_ < 4 || !Md("ES5").hasChanges_(n))) return n.base_; - n.finalized_ = !0, r = Yd(e, t), n.finalized_ = !1 - } else r = Yd(e, t); - return md(r, function(e, t) { - n && vd(n.base_, e) === t || Sd(r, e, i(t)) - }), 3 === t ? new Set(r) : r - }(e) - } - - function Yd(e, t) { - switch (t) { - case 2: - return new Map(e); - case 3: - return Array.from(e) - } - return wd(e) - } - - function Jd() { - var r = {}; - - function l(i, e) { - var t = r[i]; - return t ? t.enumerable = e : r[i] = t = { - configurable: !0, - enumerable: e, - get: function() { - var e = this[od]; - return a(e), Kd.get(e, i) - }, - set: function(e) { - var t = this[od]; - a(t), Kd.set(t, i, e) - } - }, t - } - - function n(e) { - for (var t = e.length - 1; 0 <= t; t--) { - var i = e[t][od]; - if (!i.modified_) switch (i.type_) { - case 5: - u(i) && Qd(i); - break; - case 4: - s(i) && Qd(i) - } - } - } - - function s(e) { - for (var t = e.base_, i = e.draft_, r = pd(i), n = r.length - 1; 0 <= n; n--) { - var s = r[n]; - if (s !== od) { - var a = t[s]; - if (void 0 === a && !yd(t, s)) return !0; - var o = i[s], - s = o && o[od]; - if (s ? s.base_ !== a : !bd(o, a)) return !0 - } - } - e = !!t[od]; - return r.length !== pd(t).length + (e ? 0 : 1) - } - - function u(e) { - var t = e.draft_; - if (t.length !== e.base_.length) return !0; - t = Object.getOwnPropertyDescriptor(t, t.length - 1); - return !(!t || t.get) - } - - function a(e) { - e.revoked_ && ud(3, JSON.stringify(Id(e))) - } - xd("ES5", { - createES5Proxy_: function(e, t) { - var i = Array.isArray(e), - r = function(e, t) { - if (e) { - for (var i = new Array(t.length), r = 0; r < t.length; r++) Object.defineProperty(i, "" + r, l(r, !0)); - return i - } - var n = fd(t); - delete n[od]; - for (var s = pd(n), a = 0; a < s.length; a++) { - var o = s[a]; - n[o] = l(o, e || !!n[o].enumerable) - } - return Object.create(Object.getPrototypeOf(t), n) - }(i, e), - e = { - type_: i ? 5 : 4, - scope_: t ? t.scope_ : Pd(), - modified_: !1, - finalized_: !1, - assigned_: {}, - parent_: t, - base_: e, - draft_: r, - copy_: null, - revoked_: !1, - isManual_: !1 - }; - return Object.defineProperty(r, od, { - value: e, - writable: !0 - }), r - }, - willFinalizeES5_: function(e, t, i) { - i ? cd(t) && t[od].scope_ === e && n(e.drafts_) : (e.patches_ && function t(e) { - if (e && "object" == typeof e) { - var i = e[od]; - if (i) { - var r = i.base_, - n = i.draft_, - s = i.assigned_; - if (4 === (e = i.type_)) md(n, function(e) { - e !== od && (void 0 !== r[e] || yd(r, e) ? s[e] || t(n[e]) : (s[e] = !0, Qd(i))) - }), md(r, function(e) { - void 0 !== n[e] || yd(n, e) || (s[e] = !1, Qd(i)) - }); - else if (5 === e) { - if (u(i) && (Qd(i), s.length = !0), n.length < r.length) - for (var a = n.length; a < r.length; a++) s[a] = !1; - else - for (var o = r.length; o < n.length; o++) s[o] = !0; - for (var l = Math.min(n.length, r.length), d = 0; d < l; d++) void 0 === s[d] && t(n[d]) - } - } - } - }(e.drafts_[0]), n(e.drafts_)) - }, - hasChanges_: function(e) { - return (4 === e.type_ ? s : u)(e) - } - }) - } - - function Zd() { - var m = "replace", - g = "add", - y = "remove"; - - function d(e) { - if (!hd(e)) return e; - if (Array.isArray(e)) return e.map(d); - if (Td(e)) return new Map(Array.from(e.entries()).map(function(e) { - return [e[0], d(e[1])] - })); - if (Ed(e)) return new Set(Array.from(e).map(d)); - var t, i = Object.create(Object.getPrototypeOf(e)); - for (t in e) i[t] = d(e[t]); - return i - } - - function v(e) { - return cd(e) ? d(e) : e - } - xd("Patches", { - applyPatches_: function(l, e) { - return e.forEach(function(e) { - for (var t = e.path, i = e.op, r = l, n = 0; n < t.length - 1; n++) "object" != typeof(r = vd(r, t[n])) && ud(15, t.join("/")); - var s = gd(r), - a = d(e.value), - o = t[t.length - 1]; - switch (i) { - case m: - switch (s) { - case 2: - return r.set(o, a); - case 3: - ud(16); - default: - return r[o] = a - } - case g: - switch (s) { - case 1: - return r.splice(o, 0, a); - case 2: - return r.set(o, a); - case 3: - return r.add(a); - default: - return r[o] = a - } - case y: - switch (s) { - case 1: - return r.splice(o, 1); - case 2: - return r.delete(o); - case 3: - return r.delete(e.value); - default: - return delete r[o] - } - default: - ud(17, i) - } - }), l - }, - generatePatches_: function(c, e, t, i) { - switch (c.type_) { - case 0: - case 4: - case 2: - return d = e, u = t, h = i, p = c.base_, f = c.copy_, void md(c.assigned_, function(e, t) { - var i = vd(p, e), - r = vd(f, e), - t = t ? yd(p, e) ? m : g : y; - i === r && t == m || (e = d.concat(e), u.push(t == y ? { - op: t, - path: e - } : { - op: t, - path: e, - value: r - }), h.push(t == g ? { - op: y, - path: e - } : t == y ? { - op: g, - path: e, - value: v(i) - } : { - op: m, - path: e, - value: v(i) - })) - }); - case 5: - case 1: - return function(e, t, i) { - var r, n = c.base_, - s = c.assigned_, - a = c.copy_; - a.length < n.length && (n = (r = [a, n])[0], a = r[1], t = (r = [i, t])[0], i = r[1]); - for (var o, l = 0; l < n.length; l++) s[l] && a[l] !== n[l] && (o = e.concat([l]), t.push({ - op: m, - path: o, - value: v(a[l]) - }), i.push({ - op: m, - path: o, - value: v(n[l]) - })); - for (var d = n.length; d < a.length; d++) { - var u = e.concat([d]); - t.push({ - op: g, - path: u, - value: v(a[d]) - }) - } - n.length < a.length && i.push({ - op: m, - path: e.concat(["length"]), - value: n.length - }) - }(e, t, i); - case 3: - return r = e, n = t, s = i, a = c.base_, o = c.copy_, l = 0, a.forEach(function(e) { - var t; - o.has(e) || (t = r.concat([l]), n.push({ - op: y, - path: t, - value: e - }), s.unshift({ - op: g, - path: t, - value: e - })), l++ - }), l = 0, void o.forEach(function(e) { - var t; - a.has(e) || (t = r.concat([l]), n.push({ - op: g, - path: t, - value: e - }), s.unshift({ - op: y, - path: t, - value: e - })), l++ - }) - } - var r, n, s, a, o, l, d, u, h, p, f - }, - generateReplacementPatches_: function(e, t, i, r) { - i.push({ - op: m, - path: [], - value: t - }), r.push({ - op: m, - path: [], - value: e.base_ - }) - } - }) - } - - function eu() { - var r = function(e, t) { - return (r = Object.setPrototypeOf || { - __proto__: [] - } - instanceof Array && function(e, t) { - e.__proto__ = t - } || function(e, t) { - for (var i in t) t.hasOwnProperty(i) && (e[i] = t[i]) - })(e, t) - }; - - function i(e, t) { - function i() { - this.constructor = e - } - r(e, t), e.prototype = (i.prototype = t.prototype, new i) - } - var n = function() { - function e(e, t) { - return this[od] = { - type_: 2, - parent_: t, - scope_: t ? t.scope_ : Pd(), - modified_: !1, - finalized_: !1, - copy_: void 0, - assigned_: void 0, - base_: e, - draft_: this, - isManual_: !1, - revoked_: !1 - }, this - } - i(e, Map); - var t = e.prototype; - return Object.defineProperty(t, "size", { - get: function() { - return Id(this[od]).size - } - }), t.has = function(e) { - return Id(this[od]).has(e) - }, t.set = function(e, t) { - var i = this[od]; - return l(i), Id(i).has(e) && Id(i).get(e) === t || (s(i), Qd(i), i.assigned_.set(e, !0), i.copy_.set(e, t), i.assigned_.set(e, !0)), this - }, t.delete = function(e) { - if (!this.has(e)) return !1; - var t = this[od]; - return l(t), s(t), Qd(t), t.assigned_.set(e, !1), t.copy_.delete(e), !0 - }, t.clear = function() { - var t = this[od]; - l(t), Id(t).size && (s(t), Qd(t), t.assigned_ = new Map, md(t.base_, function(e) { - t.assigned_.set(e, !1) - }), t.copy_.clear()) - }, t.forEach = function(r, n) { - var s = this; - Id(this[od]).forEach(function(e, t, i) { - r.call(n, s.get(t), t, s) - }) - }, t.get = function(e) { - var t = this[od]; - l(t); - var i = Id(t).get(e); - if (t.finalized_ || !hd(i)) return i; - if (i !== t.base_.get(e)) return i; - i = zd(t.scope_.immer_, i, t); - return s(t), t.copy_.set(e, i), i - }, t.keys = function() { - return Id(this[od]).keys() - }, t.values = function() { - var e, t = this, - i = this.keys(); - return (e = {})[ld] = function() { - return t.values() - }, e.next = function() { - var e = i.next(); - return e.done ? e : { - done: !1, - value: t.get(e.value) - } - }, e - }, t.entries = function() { - var e, i = this, - r = this.keys(); - return (e = {})[ld] = function() { - return i.entries() - }, e.next = function() { - var e = r.next(); - if (e.done) return e; - var t = i.get(e.value); - return { - done: !1, - value: [e.value, t] - } - }, e - }, t[ld] = function() { - return this.entries() - }, e - }(); - - function s(e) { - e.copy_ || (e.assigned_ = new Map, e.copy_ = new Map(e.base_)) - } - var a = function() { - function e(e, t) { - return this[od] = { - type_: 3, - parent_: t, - scope_: t ? t.scope_ : Pd(), - modified_: !1, - finalized_: !1, - copy_: void 0, - base_: e, - draft_: this, - drafts_: new Map, - revoked_: !1, - isManual_: !1 - }, this - } - i(e, Set); - var t = e.prototype; - return Object.defineProperty(t, "size", { - get: function() { - return Id(this[od]).size - } - }), t.has = function(e) { - var t = this[od]; - return l(t), t.copy_ ? !!t.copy_.has(e) || !(!t.drafts_.has(e) || !t.copy_.has(t.drafts_.get(e))) : t.base_.has(e) - }, t.add = function(e) { - var t = this[od]; - return l(t), this.has(e) || (o(t), Qd(t), t.copy_.add(e)), this - }, t.delete = function(e) { - if (!this.has(e)) return !1; - var t = this[od]; - return l(t), o(t), Qd(t), t.copy_.delete(e) || !!t.drafts_.has(e) && t.copy_.delete(t.drafts_.get(e)) - }, t.clear = function() { - var e = this[od]; - l(e), Id(e).size && (o(e), Qd(e), e.copy_.clear()) - }, t.values = function() { - var e = this[od]; - return l(e), o(e), e.copy_.values() - }, t.entries = function() { - var e = this[od]; - return l(e), o(e), e.copy_.entries() - }, t.keys = function() { - return this.values() - }, t[ld] = function() { - return this.values() - }, t.forEach = function(e, t) { - for (var i = this.values(), r = i.next(); !r.done;) e.call(t, r.value, r.value, this), r = i.next() - }, e - }(); - - function o(i) { - i.copy_ || (i.copy_ = new Set, i.base_.forEach(function(e) { - var t; - hd(e) ? (t = zd(i.scope_.immer_, e, i), i.drafts_.set(e, t), i.copy_.add(t)) : i.copy_.add(e) - })) - } - - function l(e) { - e.revoked_ && ud(3, JSON.stringify(Id(e))) - } - xd("MapSet", { - proxyMap_: function(e, t) { - return new n(e, t) - }, - proxySet_: function(e, t) { - return new a(e, t) - } - }) - } - var tu = new Vr, - vr = tu.produce, - iu = tu.produceWithPatches.bind(tu), - ru = tu.setAutoFreeze.bind(tu), - nu = tu.setUseProxies.bind(tu), - vi = tu.applyPatches.bind(tu), - A = tu.createDraft.bind(tu), - tu = tu.finishDraft.bind(tu); - w.Immer = Vr, w.applyPatches = vi, w.castDraft = function(e) { - return e - }, w.castImmutable = function(e) { - return e - }, w.createDraft = A, w.current = Xd, w.default = vr, w.enableAllPlugins = function() { - Jd(), eu(), Zd() - }, w.enableES5 = Jd; - A = w.enableMapSet = eu; - w.enablePatches = Zd, w.finishDraft = tu, w.immerable = ad, w.isDraft = cd, w.isDraftable = hd, w.nothing = sd, w.original = function(e) { - return cd(e) || ud(23, e), e[od].base_ - }; - var su = w.produce = vr; - w.produceWithPatches = iu; - var au = w.setAutoFreeze = ru; - w.setUseProxies = nu; - const ou = /^((?:[^\/;?#]+:)?)(\/\/[^\/\;?#]*)?(.*?)??(;.*?)?(\?.*?)?(#.*?)?$/, - lu = /^([^\/;?#]*)(.*)$/, - du = /(?:\/|^)\.(?=\/)/g, - uu = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g, - cu = { - buildAbsoluteURL: function(e, t, i) { - if (i = i || {}, e = e.trim(), !(t = t.trim())) { - if (!i.alwaysNormalize) return e; - const t = cu.parseURL(e); - if (!t) throw new Error("Error trying to parse base URL."); - return t.path = cu.normalizePath(t.path), cu.buildURLFromParts(t) - } - const r = cu.parseURL(t); - if (!r) throw new Error("Error trying to parse relative URL."); - if (r.scheme) return i.alwaysNormalize ? (r.path = cu.normalizePath(r.path), cu.buildURLFromParts(r)) : t; - const n = cu.parseURL(e); - if (!n) throw new Error("Error trying to parse base URL."); - if (!n.netLoc && n.path && "/" !== n.path[0]) { - const e = lu.exec(n.path); - n.netLoc = e[1], n.path = e[2] - } - n.netLoc && !n.path && (n.path = "/"); - const s = { - scheme: n.scheme, - netLoc: r.netLoc, - path: null, - params: r.params, - query: r.query, - fragment: r.fragment - }; - if (!r.netLoc && (s.netLoc = n.netLoc, "/" !== r.path[0])) - if (r.path) { - i.inheritQuery && (r.query || (s.query = n.query)); - const e = n.path, - t = e.substring(0, e.lastIndexOf("/") + 1) + r.path; - s.path = cu.normalizePath(t) - } else s.path = n.path, r.params || (s.params = n.params, r.query || (s.query = n.query)); - return null === s.path && (s.path = i.alwaysNormalize ? cu.normalizePath(r.path) : r.path), cu.buildURLFromParts(s) - }, - parseURL: function(e) { - e = ou.exec(e); - return e ? { - scheme: e[1] || "", - netLoc: e[2] || "", - path: e[3] || "", - params: e[4] || "", - query: e[5] || "", - fragment: e[6] || "" - } : null - }, - normalizePath: function(e) { - for (e = e.split("").reverse().join("").replace(du, ""); e.length !== (e = e.replace(uu, "")).length;); - return e.split("").reverse().join("") - }, - buildURLFromParts: function(e) { - return e.scheme + e.netLoc + e.path + e.params + e.query + e.fragment - }, - getHostName: function(e) { - let t; - return e && (t = -1 < e.indexOf("://") ? e.split("/")[2] : e.split("/")[0], t = t.split(":")[0], t = t.split("?")[0]), t - } - }; - var hu, pu, fu, mu, gu, yu, vu, Su, bu = cu; - - function Tu(e) { - return null != e && !e.startsWith("http://") && !e.startsWith("https://") - } - - function Eu(e) { - return null == e || Tu(e) ? null : cu.getHostName(e) - } - const Iu = [".*.itunes.apple.com"], - wu = { - deviceName: "&xapdn=", - deviceModel: "&xapdm=", - language: "&xapdl=", - dsid: "&xapdsid=", - subs: "&xapsub=" - }; - - function Au(e, t) { - return !e || e === Eu(t) - }(iu = hu = hu || {})[iu.DOVI = 4] = "DOVI", iu[iu.HEVC = 3] = "HEVC", iu[iu.VP09 = 2] = "VP09", iu[iu.AVC = 1] = "AVC", iu[iu.UNKNOWN = 0] = "UNKNOWN", (ru = pu = pu || {})[ru.PQ = 3] = "PQ", ru[ru.HLG = 2] = "HLG", ru[ru.SDR = 1] = "SDR", ru[ru.UNKNOWN = 0] = "UNKNOWN", (w = fu = fu || {})[w.ALAC = 7] = "ALAC", w[w.FLAC = 6] = "FLAC", w[w.EC3 = 5] = "EC3", w[w.AC3 = 4] = "AC3", w[w.XHEAAC = 3] = "XHEAAC", w[w.AAC = 2] = "AAC", w[w.MP3 = 1] = "MP3", w[w.UNKNOWN = 0] = "UNKNOWN", (nu = mu = mu || {})[nu.VALID = 1] = "VALID", nu[nu.INVALID = 0] = "INVALID"; - const Ou = ["via", "x-apple-request-uuid"], - ku = { - maxNumRetry: 4, - retryDelayMs: 0, - maxRetryDelayMs: 0 - }, - Cu = { - maxNumRetry: 6, - retryDelayMs: 1e3, - maxRetryDelayMs: 8e3 - }, - Du = { - maxNumRetry: 0, - retryDelayMs: 0, - maxRetryDelayMs: 0 - }, - Mu = { - default: { - maxTimeToFirstByteMs: 5e3, - maxLoadTimeMs: 2e4, - autoRetry: !1, - timeoutRetry: Du, - errorRetry: Du - }, - customURL: { - maxTimeToFirstByteMs: 1e4, - maxLoadTimeMs: 2e4, - autoRetry: !1, - timeoutRetry: Du, - errorRetry: Du - } - }, - xu = { - maxNumRetry: 8, - retryDelayMs: 1e3, - maxRetryDelayMs: 2e4, - backoff: "linear" - }, - Pu = Object.assign(Object.assign({}, xu), { - maxNumRetry: 1 - }), - Ru = { - autoStartLoad: !0, - startPosition: NaN, - defaultAudioCodec: void 0, - defaultVideoCodec: void 0, - debug: !1, - debugLevel: "info", - buildType: void 0, - minFramesBeforeSwitchingLevel: 11, - minTargetDurations: 3, - maxBufferLength: 60, - maxBufferHole: .5, - maxSeekHole: 2, - nudgeFromEventSeek: !0, - jaggedSeekTolerance: 0, - discontinuitySeekTolerance: 2, - bufferedSegmentEjectionToleranceMs: .5, - almostDryBufferSec: .5, - maxTotalDurationTolerance: .1, - lowBufferThreshold: .5, - lowBufferWatchdogPeriod: .5, - highBufferWatchdogPeriod: 3, - seekWatchdogPeriod: 5, - nudgeOffset: .1, - nudgeMaxRetry: 3, - maxFragLookUpTolerance: .2, - initialLiveManifestSize: 1, - liveSyncDurationCount: 3, - liveMaxLatencyDurationCount: 1 / 0, - liveSyncDuration: void 0, - liveMaxLatencyDuration: void 0, - liveFlushExpiredFrags: !0, - liveMaxUnchangedPlaylistRefresh: 3, - liveEdgeForZeroStartPositon: !1, - livePlaylistUpdateStaleness: 2, - livePlaylistDurationNudge: .001, - allowFastSwitchUp: !1, - minMatchGroupDuration: 5, - desiredIframeFPS: 8, - initialIframeFPS: 6, - minRemainingTimeInMediaPipeline: 3, - leftMediaTimeToAutoPause: 10, - startTargetDurationFactor: .9, - minRequiredStartDuration: 4, - maxRequiredStartDuration: 15, - enableWorker: !0, - enableWebCrypto: !0, - keySystemPreference: void 0, - useMultipleKeySessions: !1, - enablePlayReadyKeySystem: !1, - useMediaKeySystemAccessFilter: !1, - playReadyMessageFormat: "utf16", - startLevel: void 0, - livePlaylistRefreshDelay: 2500, - liveMinPlayingBufferLen: 5, - enableIFramePreloading: !0, - useMediaCapabilities: !1, - enableID3Cues: !0, - certLoadPolicy: Mu, - keyLoadPolicy: { - default: { - maxTimeToFirstByteMs: 5e3, - maxLoadTimeMs: 2e4, - autoRetry: !1, - timeoutRetry: Pu, - errorRetry: xu - }, - customURL: { - maxTimeToFirstByteMs: 1e4, - maxLoadTimeMs: 2e4, - autoRetry: !1, - timeoutRetry: Pu, - errorRetry: xu - } - }, - manifestLoadPolicy: { - default: { - maxTimeToFirstByteMs: 1e4, - maxLoadTimeMs: 2e4, - autoRetry: !1, - timeoutRetry: { - maxNumRetry: 2, - retryDelayMs: 0, - maxRetryDelayMs: 0 - }, - errorRetry: { - maxNumRetry: 1, - retryDelayMs: 1e3, - maxRetryDelayMs: 8e3 - } - }, - customURL: { - maxTimeToFirstByteMs: 1e4, - maxLoadTimeMs: 1e4, - autoRetry: !1, - timeoutRetry: { - maxNumRetry: 2, - retryDelayMs: 0, - maxRetryDelayMs: 0 - }, - errorRetry: { - maxNumRetry: 1, - retryDelayMs: 1e3, - maxRetryDelayMs: 8e3 - } - } - }, - trickPlaybackConfig: { - enabled: !0, - minIframeDuration: 8 - }, - playlistLoadPolicy: { - default: { - maxTimeToFirstByteMs: 1e4, - maxLoadTimeMs: 2e4, - autoRetry: !1, - timeoutRetry: { - maxNumRetry: 2, - retryDelayMs: 0, - maxRetryDelayMs: 0 - }, - errorRetry: { - maxNumRetry: 2, - retryDelayMs: 1e3, - maxRetryDelayMs: 8e3 - } - }, - customURL: { - maxTimeToFirstByteMs: 1e4, - maxLoadTimeMs: 1e4, - autoRetry: !1, - timeoutRetry: { - maxNumRetry: 2, - retryDelayMs: 0, - maxRetryDelayMs: 0 - }, - errorRetry: { - maxNumRetry: 2, - retryDelayMs: 1e3, - maxRetryDelayMs: 8e3 - } - } - }, - fragLoadPolicy: { - default: { - maxTimeToFirstByteMs: 5e3, - maxLoadTimeMs: 2e4, - autoRetry: !1, - timeoutRetry: ku, - errorRetry: Cu, - forceContentLenCheckIfNoHeader: !1, - reportCDNServer: !0 - }, - customURL: { - maxTimeToFirstByteMs: 1e4, - maxLoadTimeMs: 2e4, - autoRetry: !1, - timeoutRetry: ku, - errorRetry: Cu, - reportCDNServer: !0 - } - }, - steeringManifestLoadPolicy: { - default: { - maxTimeToFirstByteMs: 1e4, - maxLoadTimeMs: 2e4, - autoRetry: !1, - timeoutRetry: { - maxNumRetry: 2, - retryDelayMs: 0, - maxRetryDelayMs: 0 - }, - errorRetry: { - maxNumRetry: 1, - retryDelayMs: 1e3, - maxRetryDelayMs: 8e3 - } - }, - customURL: { - maxTimeToFirstByteMs: 1e4, - maxLoadTimeMs: 1e4, - autoRetry: !1, - timeoutRetry: { - maxNumRetry: 2, - retryDelayMs: 0, - maxRetryDelayMs: 0 - }, - errorRetry: { - maxNumRetry: 1, - retryDelayMs: 1e3, - maxRetryDelayMs: 8e3 - } - } - }, - maxNumAddLevelToPenaltyBox: 4, - firstAudioMustOverlapVideoStart: !1, - keyMinHoldTimeBeforeCleanup: 15e3, - startFragPrefetch: !1, - appendErrorMaxRetry: 3, - alwaysResetOnNewCC: !1, - fLoader: void 0, - pLoader: void 0, - xhrSetup: void 0, - iframeMaxExitSeekDuration: 2e3, - iframeStallMaxRetry: 5, - audioPrimingDelay: 0, - enableCEA708Captions: !0, - customTextTrackCueRenderer: !1, - enableWebVTT: !0, - captionsTextTrack1Label: "English", - captionsTextTrack1LanguageCode: "en", - captionsTextTrack2Label: "Spanish", - captionsTextTrack2LanguageCode: "es", - enableDualTrackSelection: !1, - condenseSubtitleTrack: !1, - nativeTextTrackChangeHandling: !0, - earlyFragTolerance: 7, - vttConcurrentLoadCount: 1, - trottleCheckInterval: 2e3, - subtitleLeadTime: 30, - lateTolerance: 2, - stretchShortVideoTrack: !1, - forceKeyFrameOnDiscontinuity: !0, - useFirstLevelAtIncompatDiscontinuity: !0, - abrBandwidthEstimator: "bandwidth-history-controller", - abrEwmaDefaultEstimate: 5e5, - abrDefaultEstimate: 5e5, - abrBandWidthFactor: .95, - abrBandWidthUpFactor: .9, - abrMaxWithRealBitrate: !1, - maxStarvationDelay: 4, - maxLoadingDelay: 4, - minAutoBitrate: 0, - enableRtcReporting: !1, - rtcIntervalTimeout: 3e5, - rtcSender: "HLSJS", - rtcSessionTag: "none", - useHTTPPlaybackSessionId: !1, - warmupCdms: !1, - enablePerformanceLogging: !1, - overridePlaybackRate: !1, - nativeControlsEnabled: !1, - useCustomMediaFunctions: !0, - seekEventThrottleMs: 150, - enableAdaptiveStartup: !0, - bandwidthHistoryWindowSize: 12e4, - bandwidthHistoryTTL: 6e5, - bandwidthHistoryAggregationMethod: "quadratic-time-weighted", - bandwidthHistoryGetEstimateThrottleMs: 1e3, - defaultTargetDuration: 10, - targetStartupMs: 4e3, - adaptiveStartupMetricsOverride: { - maxValidHeight: 1080, - maxValidBitrate: 1 / 0, - maxPreferredBitrate: 1 / 0 - }, - bandwidthHistoryStorageKey: "AppleHLS-bandwidth-estimation", - storageKeyPrefix: "AppleHLS-", - storage: { - get: "undefined" == typeof localStorage ? void 0 : localStorage.getItem.bind(localStorage), - set: "undefined" == typeof localStorage ? void 0 : localStorage.setItem.bind(localStorage) - }, - minFragmentCount: 10, - minPlaylistCount: 5, - enableCDNFallback: !0, - enableQueryParamsForITunes: !1, - gapless: !1, - useViewportSizeForLevelCap: !1, - statDefaults: { - playlistLoadTimeMs: 500, - playlistParseTimeMs: 50, - fragParseTimeMs: 50, - fragBufferCreationDelayMs: 200, - dataFragAppendMs: 50, - initFragAppendMs: 50 - }, - disableVideoCodecList: new Set([]), - disableAudioCodecList: new Set([fu.ALAC, fu.FLAC, fu.XHEAAC]), - useHighestVideoCodecPrivate: !0, - sessionDataAutoLoad: { - "com.apple.hls.chapters": !0 - } - }, - Lu = Object.assign(Object.assign({}, { - itemId: "Nah", - mediaOptionId: "Nah" - }), { - mediaOptionType: void 0 - }), - _u = e => { - var { - itemId: t, - mediaOptionId: e - } = e; - return "Nah" !== t && "Nah" !== e - }; - (iu = gu = gu || {})[iu.Variant = 0] = "Variant", iu[iu.AltAudio = 1] = "AltAudio", iu[iu.Subtitle = 2] = "Subtitle"; - const Nu = ["variant", "altAudio", "subtitle"], - Fu = [gu.Variant, gu.AltAudio, gu.Subtitle], - Bu = [gu.Variant, gu.AltAudio]; - (ru = yu = yu || {})[ru.Variant = 0] = "Variant", ru[ru.AltAudio = 1] = "AltAudio"; - const Uu = ["variant", "altAudio"]; - - function $u(e) { - switch (e) { - case gu.Variant: - return yu.Variant; - case gu.AltAudio: - return yu.AltAudio; - default: - return null - } - } - - function Vu(e) { - return e === yu.Variant ? gu.Variant : gu.AltAudio - }(w = vu = vu || {})[w.NO = 0] = "NO", w[w.YES = 1] = "YES", (nu = Su = Su || {}).UNKNOWN = "unkn", nu.VIDEO = "vide", nu.AUDIO = "soun", nu.SUBTITLE = "sbtl", nu.CLOSEDCAPTION = "clcp"; - const Ku = { - search: function(e, t) { - let i, r, n = 0, - s = (null == e ? void 0 : e.length) - 1; - for (; n <= s;) { - var a = t(r = e[i = (n + s) / 2 | 0]); - if (0 < a) n = 1 + i; - else { - if (!(a < 0)) return r; - s = i - 1 - } - } - return null - } - }; - var qu = Ku, - Hu = { - findFragmentBySNAndBuffer: function(e, t, i = 0, r = 0, n = 0) { - let s; - e = e ? t[e.mediaSeqNum - t[0].mediaSeqNum + 1] : null; - return s = i < r ? (r - n < i && (n = 0), e && !this.fragmentWithinToleranceTest(i, n, e) ? e : Ku.search(t, this.fragmentWithinToleranceTest.bind(null, i, n))) : t[t.length - 1], s - }, - fragmentWithinToleranceTest: function(e, t, i) { - t = Math.min(t, i.duration); - return i.start + i.duration - t <= e ? 1 : i.start - t > e && i.start ? -1 : 0 - } - }; - const ju = { - startFragmentInCC: function(e, t, i) { - let r = t - i.discoSeqNum; - if (0 === r) { - const t = e[i.mediaSeqNum - e[0].mediaSeqNum - 1]; - r = t && t.discoSeqNum === i.discoSeqNum ? -1 : 0 - } - return r - }, - endFragmentInCC: function(e, t, i) { - let r = t - i.discoSeqNum; - if (0 === r) { - const t = e[i.mediaSeqNum - e[0].mediaSeqNum + 1]; - r = t && t.discoSeqNum === i.discoSeqNum ? 1 : 0 - } - return r - }, - findStartEndFragmentsInCC: function(e, t, i) { - let r, n; - return 0 < (null == e ? void 0 : e.length) && ne(t) && (r = Ku.search(e, ju.startFragmentInCC.bind(null, e, t)), n = Ku.search(e, ju.endFragmentInCC.bind(null, e, t))), r && !ne(r.discoSeqNum) && (r = void 0), n && !ne(n.discoSeqNum) && (n = void 0), { - startFrag: r, - endFrag: n - } - }, - getTimeRangeDictForCC: function(e, t, i) { - var { - startFrag: t, - endFrag: i - } = ju.findStartEndFragmentsInCC(e, t, i); - return { - start: t.start, - end: i.start + i.duration - } - }, - getTimeRangeForCC: function(e, t, i) { - var { - startFrag: t, - endFrag: i - } = ju.findStartEndFragmentsInCC(e, t, i); - let r = []; - return t && i && (r = [t.start, i.start + i.duration]), r - }, - snapToCCTimeRange: function(e, t, i, r) { - r = ju.getTimeRangeForCC(t, i, r); - return null != r && r.length ? Math.min(r[1], Math.max(r[0], e)) : e - }, - getMinTimeForCC(e, t, i, r) { - if (null == e || !e.length) return 0; - let n = 0; - if (ne(i) && e) { - e = ju.getTimeRangeForCC(e, i, r); - if (e) { - const s = ju.getTimeRangeForCC(t, i, r); - n = null != s && s.length ? Math.max(s[0], e[0]) : e[0] - } - } - return n - }, - discoSeqNumForTime(e, t, i = 0) { - i = Ku.search(e, Hu.fragmentWithinToleranceTest.bind(null, t, i)); - return null == i ? void 0 : i.discoSeqNum - } - }; - var Qu = ju; - const Wu = $i(void 0); - - function Gu(e, t, i = 1) { - return e.pipe(ln(t), Ds(i)) - } - - function zu(e, t) { - var i = e.timeline; - return { - seconds: (e.seconds - i.rootTimeSeconds) / i.rate * t.rate + t.rootTimeSeconds, - timeline: t - } - } - class Xu { - constructor() { - this._timeline = null - } - get forward() { - var e; - return 0 < (null === (e = this.timeline) || void 0 === e ? void 0 : e.rate) - } - get isStarted() { - return Boolean(this.hostClock) - } - start(e) { - this.stopTime = null, this._timeline = Object.assign({}, e); - const t = { - rootTimeSeconds: performance.now() / 1e3, - rate: 1 - }; - this.hostClock = { - timeline: t, - getCurrentTime: () => ({ - seconds: performance.now() / 1e3, - timeline: t - }) - } - } - pause() { - this.isStarted && (this.stopTime = this.getCurrentTime()) - } - stop() { - this.pause(), this.hostClock = null, this._timeline = null - } - get timeline() { - return this._timeline - } - getCurrentTime() { - return this.stopTime || zu(this.hostClock.getCurrentTime(), this.timeline) - } - } - class Yu { - constructor(e) { - this._timeline = e, this.mediaRootTime = this.lastTimeSeconds = e.rootTimeSeconds - } - get timeline() { - return Object.assign({}, this._timeline) - } - getCurrentTime() { - return { - seconds: this.lastTimeSeconds, - timeline: Object.assign({}, this._timeline) - } - } - } - const Ju = { - name: "ifm" - }; - class Zu { - constructor(e, t) { - this.config = e, this.logger = t, this.scaledFragments = [], this.iframeClock = new Xu, this.hasMore$ = new yi(!0), this.logger = t.child(Ju) - } - destroy() { - this.stop() - } - resetScaledSegments() { - this.scaledFragments = [] - } - get anchorFrag() { - return this.scaledFragments.length ? this.scaledFragments[0] : null - } - get lastFrag() { - return this.scaledFragments.length ? this.scaledFragments.slice(-1)[0] : null - } - get isStarted() { - return Boolean(this.iframeClock.isStarted) - } - get iframeRate() { - var e; - return null !== (e = null === (e = this.iframeClock.timeline) || void 0 === e ? void 0 : e.rate) && void 0 !== e ? e : 0 - } - get iframeClockTimeSeconds() { - return Math.max(0, this.iframeClock.getCurrentTime().seconds) - } - get mediaAppendClockTimeSeconds() { - var e; - return null !== (e = null === (e = this.mediaAppendClock) || void 0 === e ? void 0 : e.getCurrentTime().seconds) && void 0 !== e ? e : 0 - } - get mediaRootTime() { - var e; - return null === (e = this.mediaAppendClock) || void 0 === e ? void 0 : e.mediaRootTime - } - pause() { - this.iframeClock.pause() - } - stop() { - this.hasMore$.next(!0), this.iframeClock.stop(), this.mediaAppendClock = null, this.resetScaledSegments() - } - checkHasMore() { - this.hasMore$.next(!0) - } - startClocksAndGetFirstFragment(e, t, i, r, n) { - let s = Ku.search(e, Hu.fragmentWithinToleranceTest.bind(null, r, 1e-4)); - if (!s && r >= e[e.length - 1].start && (s = e[e.length - 1]), !s) return this.logger.error(`startClocksAndGetFirstFragment => no anchorFrag for time ${r}`), this.hasMore$.next(!1), null; - var a = ne(n) ? n : s.discoSeqNum, - a = Qu.getMinTimeForCC(e, t, a, this.logger), - r = ne(n) && 1 < i ? a : r, - a = 1 < i ? Math.ceil(r) : Math.ceil(a); - return this.iframeClock.start({ - rootTimeSeconds: r, - rate: i - }), this.mediaAppendClock = new Yu({ - rootTimeSeconds: a, - rate: 1 - }), { - frag: s, - newMediaRootTime: a - } - } - getNextFragment(e, t, i) { - const r = this.lastFrag, - { - iframeClock: n, - mediaAppendClock: s - } = this, - a = n.timeline; - s.lastTimeSeconds = t; - var o = n.forward ? 1 : -1; - let l, d = Math.max(0, Math.min(r.mediaSeqNum - e[0].mediaSeqNum + o, e.length - 1)); - if (r.mediaSeqNum !== e[d].mediaSeqNum) - do { - l = e[d]; - const i = n.forward ? l.start : l.start + l.duration; - if (zu({ - seconds: i, - timeline: a - }, s.timeline).seconds >= t && (n.forward && i >= this.iframeClockTimeSeconds || !n.forward && i <= this.iframeClockTimeSeconds)) break - } while (d += o, 0 < d && d < e.length); - return l || (this.logger.error(`getNextFragment(bufferEnd: ${t}) => no more frags, but we should have found an end fragment, setting hasMore to false`), this.hasMore$.next(!1)), { - frag: l - } - } - nextFragment(e, t, i, r) { - let n, s; - if (this.isStarted && i !== this.iframeRate && (s = this.iframeClockTimeSeconds, this.stop()), this.isStarted) { - if (n = this.getNextFragment(e, r, i), !n.frag) return; - if (n.frag.discoSeqNum !== this.anchorFrag.discoSeqNum) { - const a = this.iframeClockTimeSeconds; - this.stop(); - const s = this.startClocksAndGetFirstFragment(e, t, i, a, n.frag.discoSeqNum)["newMediaRootTime"]; - n.newMediaRootTime = s - } - } else if (n = this.startClocksAndGetFirstFragment(e, t, i, null != s ? s : r), !n) return; - var r = n["frag"], - r = this.handleNextFrag(e, r); - return Object.assign(Object.assign({}, n), { - frag: r - }) - } - handleNextFrag(e, t) { - const i = Object.assign(Object.assign({}, t), { - iframeMediaStart: NaN, - iframeMediaDuration: NaN - }), - { - mediaAppendClock: r, - iframeClock: n - } = this; - this.iframeClockBounds = Qu.getTimeRangeDictForCC(e, i.discoSeqNum, this.logger); - var s = r.getCurrentTime().seconds, - t = n.timeline.rate; - return ne(i.iframeOriginalStart) || (i.iframeOriginalStart = i.start), i.start = i.iframeMediaStart = s, i.iframeMediaDuration = Math.max(i.duration / Math.abs(t), 1 / this.config.minIframeDuration), this.scaledFragments.push(i), this.isEndFrag(e, i) && this.hasMore$.next(!1), i - } - get hasMore() { - return this.hasMore$.value - } - handleWaitForMore() { - return this.isStarted ? (this.iframeClock.getCurrentTime(), Gu(this.hasMore$, e => !0 === e).pipe(Za(() => {}))) : Wu - } - isEndFrag(e, t) { - var i = e[0], - r = e[e.length - 1], - e = this.iframeClock.forward; - return !e && (null == i ? void 0 : i.mediaSeqNum) === t.mediaSeqNum || e && (null == r ? void 0 : r.mediaSeqNum) === t.mediaSeqNum - } - } - - function ec(e, t) { - for (var i = 0; i < t.length; i++) { - var r = t[i]; - r.enumerable = r.enumerable || !1, r.configurable = !0, "value" in r && (r.writable = !0), Object.defineProperty(e, r.key, r) - } - } - - function tc(t) { - return function(e) { - return e.lift(new nc(t)) - } - } - var ic, rc, nc = (ec(sc.prototype, [{ - key: "call", - value: function(e, t) { - return t.subscribe(e) - } - }]), sc); - - function sc(e) { - ! function(e) { - if (!(e instanceof sc)) throw new TypeError("Cannot call a class as a function") - }(this), "tag" in this ? Object.defineProperty(this, "tag", { - value: void 0, - enumerable: !0, - configurable: !0, - writable: !0 - }) : this.tag = void 0, this.tag = e - } - - function ac(e) { - const { - method: t, - isEncrypted: i, - uri: r, - format: n, - formatversions: s - } = e, a = { - method: t, - isEncrypted: i, - uri: r, - format: n, - formatversions: s, - iv: e.ivBuf ? new Uint8Array(e.ivBuf) : null - }; - return e.keyIdBuf && (a.keyId = new Uint8Array(e.keyIdBuf)), e.keyBuf && (a.key = new Uint8Array(e.keyBuf)), e.psshBuf && (a.pssh = new Uint8Array(e.psshBuf)), a - }(iu = ic = ic || {}).MustRequestResponse = "MustRequestResponse", iu.WaitingForKeyResponse = "WaitingForKeyResponse", iu.GotKeyResponse = "GotKeyResponse"; - class oc extends Error { - constructor(e, t) { - super(e), this.code = t - } - } - class lc extends p { - constructor(e, t, i, r, n, s) { - super(o, e, t, i, n), this.code = r, this.isTimeout = s, this.response = n - } - } - class dc extends lc { - constructor(e, t, i, r, n) { - super(n ? "manifestLoadTimeOut" : "manifestLoadError", e, t, i, r, n) - } - } - class uc extends lc { - constructor(e, t, i, r, n, s, a, o) { - switch (super("", e, t, i, r, n), this.mediaOptionType = s, this.mediaOptionId = a, this.url = o, s) { - case gu.Variant: - this.details = n ? "levelLoadTimeOut" : N; - break; - case gu.AltAudio: - this.details = n ? "audioTrackLoadTimeOut" : "audioTrackLoadError"; - break; - case gu.Subtitle: - this.details = n ? "subtitleTrackLoadTimeout" : "subtitleTrackLoadError" - } - } - } - class cc extends lc { - constructor(e, t, i, r) { - super("sessionDataLoadError", e, t, i, r, !1) - } - } - class hc extends lc { - constructor(e, t, i, r, n, s, a) { - super(n ? "fragLoadTimeOut" : "fragLoadError", e, t, i, r, n), this.mediaOptionId = s.mediaOptionId, this.mediaOptionType = s.mediaOptionType, this.stats = a - } - } - class pc extends dr { - constructor(e, t, i) { - super(), this.message = e, this.code = t, this.stats = i - } - } - class fc extends lc { - constructor(e, t, i) { - super("fragAbortError", !1, "Fragment abort", 0, i, !1), this.candidateMediaOptionId = t, this.mediaOptionId = e.mediaOptionId, this.mediaOptionType = e.mediaOptionType - } - } - class mc extends lc { - constructor(e, t, i, r = []) { - super("keyLoadTimeOut", !1, e, i.code, i, !0), this.keyuri = t, this.response = i, this.mediaOptionIds = r - } - }(ru = rc = rc || {})[ru.InvalidState = 0] = "InvalidState", ru[ru.Abort = 1] = "Abort", ru[ru.OutputRestricted = 2] = "OutputRestricted", ru[ru.AlreadyFailedKey = 3] = "AlreadyFailedKey", ru[ru.HttpError = 4] = "HttpError", ru[ru.InternalError = 5] = "InternalError", ru[ru.LicenseServerError = 6] = "LicenseServerError", ru[ru.InsufficientCPC = 7] = "InsufficientCPC"; - class gc extends lc { - constructor(e, t, i, r, n, s, a = !1, o = []) { - super("keyLoadError", a, e, i, r, !1), this.keyuri = t, this.isOkToRetry = n, this.keyErrorReason = s, this.mediaOptionIds = o - } - } - class yc extends p { - constructor(e, t, i, r, n) { - super(s, "keySystemGenericError", !0, e, r), this.keyuri = t, this.code = i, this.response = r, this.keysystemstring = n - } - } - - function vc(e, t) { - return e instanceof gc ? new gc(e.message, e.keyuri, e.code, e.response, e.isOkToRetry, e.keyErrorReason, e.fatal, t) : e instanceof mc ? new mc(e.message, e.keyuri, $.CryptResponseReceivedSlowly, t) : e ? new V(e.fatal, e.reason, $.InternalError) : null - } - const Sc = { - id: "fairplaystreaming", - systemStringPrefix: "com.apple.fps", - keyFormatString: "com.apple.streamingkeydelivery", - securityLevels: { - AppleBaseline: 0, - AppleMain: 1, - Main: 1, - Baseline: 0 - } - }; - class bc extends kl { - constructor(e) { - super(e), this.store = e - } - get unresolvedUriLoading$() { - return this.selectEntityAction(Eo.Add).pipe(hr(e => e.map(e => this.getEntity(e)))) - } - } - class Tc { - constructor(e) { - this.store = e - } - createUnresolvedUriLoading(e, t, i) { - Do("loader.create.unresolvedUriLoading"), this.store.add({ - uri: e, - responseType: t, - userAgent: i - }) - } - removeUnresolvedUriLoading(e) { - Do("loader.remove.unresolvedUriLoading"), this.store.remove(e) - } - } - const Ec = new class extends fl { - constructor() { - super({}, { - name: "loader", - producerFn: su, - idKey: "uri" - }) - } - }; - let Ic = null; - class wc extends t { - trigger(e, t) { - try { - this.emit(e, e, t), "hlsFragLoadProgress" !== e.toString() && ("hlsInternalError" !== e && "hlsError" !== e || !t.length || JSON.stringify(t[0], ["fatal", "details", "reason"]), e.toString()) - } catch (t) { - Qe().warn(`error in event listener for ${e}: ${t.message}`) - } - } - } - class Ac { - constructor(e, t) { - this.target = e, this._this = t - } - eventWithOptions(e, t, i, r = this._this) { - let n = rn(this.target, e, t); - return this.target instanceof wc && (n = n.pipe(hr(([, e]) => e))), i && (r && (i = i.bind(r)), n = n.pipe(Za(i))), n - } - event(e, t, i = this._this) { - return this.eventWithOptions(e, void 0, t, i) - } - listen(e, t, i, r = this._this) { - return this.event(e, i, r).pipe(Va(t)).subscribe() - } - } - - function Oc(e, t) { - return new Ac(e, t) - } - - function kc(e, t, i) { - var r; - return { - currentTarget: null !== (r = null == i ? void 0 : i.currentTarget) && void 0 !== r ? r : e, - target: null !== (i = null == i ? void 0 : i.target) && void 0 !== i ? i : e, - type: t - } - } - - function Cc(y, v) { - return new $t(e => { - const { - maxTimeToFirstByteMs: t, - maxLoadTimeMs: i - } = v, r = new XMLHttpRequest, n = { - trequest: performance.now(), - tfirst: NaN, - tload: NaN, - loaded: 0, - total: NaN, - complete: !1 - }, s = Oc(r), a = s.event("progress").pipe(Aa(), ao(300, Ti, { - leading: !0, - trailing: !0 - }), hr(e => (isNaN(n.tfirst) && (n.tfirst = performance.now()), n.loaded = e.loaded, e.lengthComputable && (n.total = e.total), e.target)), ln(e => 3 <= e.readyState)), o = s.event("readystatechange").pipe(Aa(), hr(e => e.target), ln(e => 2 <= e.readyState), Za(e => { - isNaN(n.tfirst) && 3 <= e.readyState && (n.tfirst = performance.now()) - })); - let l = Ii; - isFinite(t) && 0 < t && (l = an(a, o).pipe(Ds(1), So(0 < y.extendMaxTTFB ? y.extendMaxTTFB : t), La(() => Ii))); - let d = Ii; - isFinite(i) && 0 < i && (d = o.pipe(ln(e => 4 <= e.readyState), Ds(1), So(i), La(() => Ii))); - let u = Ii; - y.onProgress && (u = an($i(r), a).pipe(hr(e => { - const { - getData: t, - cb: i - } = y.onProgress; - return i(y.url, e.status, n, t ? e.response : void 0) - }), Wa(e => !e, !0)).pipe($a(Ii))); - const c = an(a.pipe(La(() => Ii)), o, l, d, u).pipe(ln(e => 4 <= e.readyState), Ds(1), La(e => { - if (n.complete = !0, 200 <= e.status && e.status < 300) { - if (n.tload = performance.now(), n.contentType = e.getResponseHeader("Content-Type"), v.reportCDNServer && (n.cdnServer = e.getResponseHeader("CDN-Server")), n.contentLength = v.forceContentLenCheckIfNoHeader ? function(e) { - let t; - const i = e.getResponseHeader("Content-Encoding"), - r = e.getResponseHeader("Transfer-Encoding"), - n = !i || i && "identity" === i.toLowerCase(), - s = !r || r && "identity" === r.toLowerCase(); - return n && s && (t = function(e) { - e = /([0-9]+)\-([0-9]+)\/([0-9]+)/.exec(e); - return e ? parseInt(e[2]) - parseInt(e[1]) + 1 : void 0 - }(e.getResponseHeader("Content-Range")), ne(t) || (t = parseInt(e.getResponseHeader("Content-Length")))), t - }(e) : null, i = e, (r = y).collectServerInstanceInfo && (r.serverInstanceInfo = {}, r.collectServerInstanceInfo.forEach(e => { - var t = i.getResponseHeader(e); - t && (r.serverInstanceInfo[e] = t) - })), "arraybuffer" === y.responseType ? n.loaded = e.response.byteLength : n.loaded = e.responseText.length, n.total = n.loaded, y.checkContentLength && (0 === n.total || ne(n.contentLength) && n.total != n.contentLength)) throw new oc("Network error", e.status); - return Nr($i([e, n]), Ti) - } - var i, r; - throw new oc("Network error", e.status) - }), Vn(e => { - if (e instanceof dr) throw new pc(e.message, 0, n); - if (!(e instanceof oc)) throw new oc(e.message, 0); - throw e - })).subscribe(e), - { - url: h, - method: p, - byteRangeOffset: f, - responseType: m, - body: g - } = y; - y.mimeType && r.overrideMimeType(y.mimeType); - try { - const v = y.xhrSetup; - if (v) try { - v(r, h) - } catch (e) { - r.open(p, y.url, !0), v(r, y.url) - } - r.readyState || r.open(p, y.url, !0) - } catch (e) { - throw new oc(e.message, r.status) - } - if (r.responseType = m, f && ne(f.start) && ne(f.end) && 0 <= f.start && f.end > f.start) { - const { - start: y, - end: v - } = f; - r.setRequestHeader("Range", `bytes=${y}-${v-1}`) - } - if (y.headers) - for (const [v, e] of Object.entries(y.headers)) r.setRequestHeader(v, e); - return "POST" === p && g ? r.send(g) : r.send(), () => { - r.abort(), c.unsubscribe() - } - }) - } - const Dc = { - name: "CustomUrlLoader" - }; - class Mc { - constructor(e) { - this.loaderService = e, this.requestMap = {}, this.logger = Qe() - } - load(a, o) { - return new $t(e => { - const t = a.url, - i = o["maxTimeToFirstByteMs"], - r = { - trequest: performance.now(), - tfirst: NaN, - tload: NaN, - loaded: 0, - total: NaN, - complete: !1 - }, - n = (this.requestMap[t] = new er).pipe(So(0 < a.extendMaxTTFB ? a.extendMaxTTFB : i), La(e => (r.tfirst = performance.now(), this.handleExternalResponse(e, a, o, r))), Vn(e => { - if (e instanceof dr) throw new pc(e.message, 0, r); - throw e - }), Vs(() => { - this.requestMap[t] = void 0, this.loaderService.removeUnresolvedUriLoading(t) - })); - a.onProgress && a.onProgress.cb(t, 0, r, void 0); - const s = n.subscribe(e); - return this.loaderService.createUnresolvedUriLoading(t, a.responseType, navigator.userAgent), () => { - s.unsubscribe(), this.requestMap[t] = void 0 - } - }) - } - setCustomUrlResponse(e, t) { - const i = this.requestMap[e]; - i && (i.next(t), i.complete(), this.requestMap[e] = void 0) - } - handleExternalResponse(e, t, i, r) { - r.tload = performance.now(); - var n = e.response.status || 200; - return 200 <= n && n < 300 ? ("arraybuffer" === t.responseType && e.response.data instanceof ArrayBuffer ? r.loaded = e.response.data.byteLength : r.loaded = e.response.data.toString().length, r.total = r.loaded, r.complete = !0, Nr($i({ - status: n, - data: e, - stats: r - }), Ti)) : 300 === n || 302 === n || 303 === n || 305 === n ? this.redirectRequest(e.response.uri, t, i, r) : (this.logger.warn(Dc, `unable to load custom url > uri=${le(e.response.uri)}, status=${n}`), Vi(new oc("Unable to load custom url", n))) - } - redirectRequest(e, t, i, n) { - var { - maxLoadTimeMs: r, - maxTimeToFirstByteMs: s - } = i, r = r - (performance.now() - n.trequest), s = 0 < t.extendMaxTTFB ? t.extendMaxTTFB : s - (performance.now() - n.trequest), i = Object.assign(Object.assign({}, i), { - maxLoadTimeMs: r, - maxTimeToFirstByteMs: s - }), e = Object.assign(Object.assign({}, t), { - url: e - }); - return r <= 0 || s <= 0 ? Vi(new dr) : Cc(e, i).pipe(hr(([e, t]) => { - var { - responseURL: i, - status: r - } = e, i = i || "", i = { - uri: i, - response: { - status: r, - uri: i, - data: e.response - } - }; - return n.loaded = n.total = t.loaded, n.tload = performance.now(), n.complete = !0, { - status: e.status, - data: i, - stats: n - } - })) - } - } - let xc; - - function Pc(e) { - return xc || (e = e || (Ic = Ic || new Tc(Ec), Ic), xc = new Mc(e)), xc - } - - function Rc(e, t) { - const i = Object.assign(Object.assign({}, e), { - method: "GET", - responseType: "arraybuffer" - }), - r = Pc(); - return Tu(i.url) ? r.load(i, t).pipe(hr(e => [e.data.response.data, e.stats, i.serverInstanceInfo])) : Cc(i, t).pipe(hr(([e, t]) => [e.response, t, i.serverInstanceInfo])) - } - - function Lc(e, t) { - return !e.url || Tu(e.url) ? t.customURL : t.default - } - - function _c(e, t) { - return e instanceof lc ? e.isTimeout ? t.timeoutRetry : t.errorRetry : null - } - - function Nc(e) { - var t = e.type, - i = e.liveOrEvent; - let r = "VOD"; - "EVENT" === t && i ? r = "EVENT" : t && 0 !== t.length && "LIVE" !== t || !i || (r = "LIVE"), e.type !== r && (e.type = r) - } - const Fc = { - id: "playready", - systemStringPrefix: "com.microsoft.playready", - keyFormatString: "com.microsoft.playready", - securityLevels: { - SL2000: 0, - SL3000: 1 - } - }, - Bc = { - id: "widevine", - systemStringPrefix: "com.widevine.alpha", - keyFormatString: "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed", - securityLevels: { - WIDEVINE_SOFTWARE: 0, - WIDEVINE_HARDWARE: 1 - } - }; - - function Uc(e) { - return e.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=+$/, "") - } - class $c { - static strToBase64Encode(e) { - return btoa(e) - } - static base64DecodeToStr(e) { - return atob(e) - } - static base64Encode(e) { - return btoa(String.fromCharCode(...e)) - } - static base64UrlEncode(e) { - return Uc($c.base64Encode(e)) - } - static base64Decode(e) { - return Uint8Array.from(atob(e), e => e.charCodeAt(0)) - } - } - class Vc { - static strToBase64Encode(e) { - return l.Buffer.from(e).toString("base64") - } - static base64DecodeToStr(e) { - return l.Buffer.from(e, "base64").toString() - } - static base64Encode(e) { - return l.Buffer.from(e).toString("base64") - } - static base64UrlEncode(e) { - return Uc(Vc.base64Encode(e)) - } - static base64Decode(e) { - e = l.Buffer.from(e, "base64"); - return new Uint8Array(e.buffer, e.byteOffset, e.byteLength) - } - } - var Kc, qc = void 0 !== l.Buffer ? Vc : $c; - const Hc = { - avc1: "video/mp4", - avc3: "video/mp4", - dvav: "video/mp4", - dva1: "video/mp4", - hev1: "video/mp4", - hvc1: "video/mp4", - dvh1: "video/mp4", - dvhe: "video/mp4" - }, - jc = { - mp4a: "audio/mp4", - "ac-3": "audio/mp4", - "ec-3": "audio/mp4" - }; - - function Qc(e) { - const t = O.strToUtf8array(e).subarray(0, 16), - i = new Uint8Array(16); - return i.set(t, 16 - t.length), i - } - const Wc = { - getCapabilities: function(e, t) { - const i = { - videoCapabilities: [], - audioCapabilities: [] - }; - return e && e.forEach(e => { - var t = e.split(".")[0].trim(); - t in Hc && i.videoCapabilities.push({ - contentType: Hc[t] + ";codecs=" + e, - robustness: "" - }) - }), t && t.forEach(e => { - var t = e.split(".")[0].trim(); - t in jc && i.audioCapabilities.push({ - contentType: jc[t] + ";codecs=" + e, - robustness: "" - }) - }), i - }, - changeEndianness: function(e) { - function t(e, t, i) { - var r = e[t]; - e[t] = e[i], e[i] = r - } - t(e, 0, 3), t(e, 1, 2), t(e, 4, 5), t(e, 6, 7) - }, - getKeyIdBytes: Qc, - convertDataUriToArrayBytes: function(e) { - const t = e.split(":"); - let i = null; - if ("data" === t[0] && 2 === t.length) { - const e = t[1].split(";"), - r = e[e.length - 1].split(","); - if (2 === r.length) { - const t = "base64" === r[0], - n = r[1]; - i = t ? (e.splice(-1, 1), qc.base64Decode(n)) : Qc(n) - } - } - return i - }, - makeKeyIdsInitData: function(e) { - e = { - kids: e.map(qc.base64UrlEncode) - }; - return O.strToUtf8array(JSON.stringify(e)) - }, - parsePSSHList: function(e) { - const i = new DataView(e); - let r = 0; - const n = {}; - for (; r < i.buffer.byteLength;) { - const e = r, - t = i.getUint32(r); - r += 4; - var s = e + t; - if (1886614376 === i.getUint32(r)) { - switch (r += 4, i.getUint8(r)) { - case 0: - case 1: - r += 1; - break; - default: - r = s - } - r += 3; - let t = ""; - for (let e = 0; e < 16; ++e, ++r) switch (t += i.getUint8(r).toString(16), e) { - case 4: - case 6: - case 8: - case 10: - t += "-" - } - r += 4, n[t] = i.buffer.slice(e, s) - } else r = s - } - return n - } - }; - let Gc = {}; - class zc { - constructor(e, t, i, r, n) { - if (this.method = e, this.uri = t, this.iv = i, this.format = r, this.formatversions = n, this.isEncrypted = this.method && "NONE" !== this.method, this.formatversions && 0 !== this.formatversions.length || (this.formatversions = [1]), this.key = void 0, this.keyId = void 0, this.isEncrypted) { - const a = Wc.convertDataUriToArrayBytes(this.uri); - if (a) switch (r) { - case Fc.keyFormatString: { - this.pssh = a; - const e = new Uint16Array(a.buffer, a.byteOffset, a.byteLength / 2), - t = String.fromCharCode.apply(null, Array.from(e)), - i = t.substring(t.indexOf("<"), t.length), - r = (new DOMParser).parseFromString(i, "text/xml").getElementsByTagName("KID")[0]; - if (r) { - var s = null; - if (s = r.childNodes[0] ? r.childNodes[0].nodeValue : r.getAttribute("VALUE")) { - const t = qc.base64Decode(s).subarray(0, 16); - Wc.changeEndianness(t), this.keyId = t - } - } - break - } - case Bc.keyFormatString: - this.pssh = a, 22 <= a.length && (this.keyId = a.subarray(a.length - 22, a.length - 6)); - break; - default: { - let e = a.subarray(0, 16); - if (16 !== e.length) { - const t = new Uint8Array(16); - t.set(e, 16 - e.length), e = t - } - this.keyId = e; - break - } - } - if (!this.keyId || 16 !== this.keyId.byteLength) { - let e = Gc[this.uri]; - if (!e) { - const t = Object.keys(Gc).length % Number.MAX_SAFE_INTEGER; - e = new Uint8Array(16), new DataView(e.buffer, 12, 4).setUint32(0, t), Gc[this.uri] = e - } - this.keyId = e - } - } - } - get keyTagInfo() { - var { - method: e, - isEncrypted: t, - uri: i, - iv: r, - keyId: n, - key: s, - format: a, - formatversions: o - } = this; - return { - method: e, - isEncrypted: t, - uri: i, - iv: r, - keyId: n, - key: s, - format: a, - formatversions: o - } - } - static clearKeyUriToKeyIdMap() { - Gc = {} - } - }(w = Kc = Kc || {}).NONE = "NONE", w.GET_REQUEST_INFO = "GET_REQUEST_INFO", w.GET_CHALLENGE = "GET_CHALLENGE", w.GET_KEY_RESPONSE = "GET_KEY_RESPONSE", w.PROCESS_LICENSE = "PROCESS_LICENSE"; - class Xc { - constructor(e, t, i, r) { - this.session = e, this.onkeystatuseschange = t, this.onkeymessage = i, this.logger = r, this.isClosing$ = new yi(!1), this.closed$ = new yi(!1); - const n = Oc(this.session); - n.listen("keystatuseschange", this.isClosing$.pipe(ln(e => !0 === e)), this.onkeystatuseschange), n.listen("message", this.closed$.pipe(ln(e => !0 === e)), this.onkeymessage) - } - get isClosing() { - return this.isClosing$.value - } - get isClosed() { - return this.closed$.value - } - destroy() { - this.isClosing$.next(!0); - const e = this.session; - return Fr(e.remove().catch(e => {}).then(() => e.close()).catch(e => {})).pipe(Za(() => { - this.isClosing$.next(!1), this.closed$.next(!0) - }), Vs(() => { - this.isClosing$.next(!1), this.closed$.next(!0) - })) - } - } - class Yc { - constructor(e, t = null) { - this.decryptdata = e, this._requestState$ = new yi(Kc.NONE), this.destroy$ = new Xt, this.currentObservable = null, this.session = null, this.oldSessions = [], this.session = t - } - get requestState() { - return this._requestState$.value - } - get onKeyRequestState$() { - return this._requestState$ - } - destroy() { - this.destroy$.next() - } - abort() { - var e; - this.requestState !== Kc.NONE && (e = new gc("Aborted", this.decryptdata.uri, 0, $.KeySystemAbort, !0, rc.Abort), this.error(e)) - } - setKeyRequestState(e) { - if (this.currentObservable) { - const t = new gc(`Unexpected state transition ${this.requestState}->${e}`, this.decryptdata.uri, 0, $.KeySystemUnexpectedStateTransition, !0, rc.InvalidState); - this.error(t) - } - this._requestState$.next(e); - const t = new er; - return e === Kc.NONE ? (t.complete(), this.currentObservable = null) : this.currentObservable = t, t - } - resolveState(e, t) { - if (this.currentObservable) - if (e === this.requestState) - if (t instanceof Error) this.error(t); - else { - const e = this.currentObservable; - this.currentObservable = null, e.next(t), e.complete() - } - else { - const t = new gc(`Unexpected state ${this.requestState} != ${e}`, this.decryptdata.uri, 0, $.KeySystemUnexpectedState, !0, rc.InvalidState); - this.error(t) - } - } - error(e) { - if (this.currentObservable) { - const t = this.currentObservable; - this.currentObservable = null, t.error(e) - } - this.setKeyRequestState(Kc.NONE) - } - } - - function Jc(e) { - return `uri=${le(e.uri)} keyId=${je(e.keyId)}` - } - class Zc { - constructor(e, t, i, r, n, s, a) { - this.mediaKeys = e, this.systemString = t, this.config = i, this.eventEmitter = r, this.useSingleKeySession = n, this.sessionHandler = s, this.logger = a, this.destroy$ = new Xt, this.setCert = !1, this.certificate$ = new yi(null), this._keyStatusChange$ = new Xt, this.shouldDestroyMediaKeys = !1, this.itemId = "", this.sessions = [], this.keyIdToKeyInfo = {}, this.keyUriToKeyInfo = {}, this.sessionIdToKeyUri = {}, this.onkeystatuseschange = this.handleKeyStatusesChange.bind(this), this.onkeymessage = this.handleKeyMessage.bind(this) - } - get keyStatusChange$() { - return this._keyStatusChange$ - } - destroy() { - this.isDestroying = !0, this.destroy$.next(); - for (const e of Object.values(this.keyIdToKeyInfo)) this._abortKeyRequest(e); - const e = this.sessions.map(e => e.destroy()), - t = nn(() => 0 === e.length, Wu, en(e)).pipe(Zs(void 0), Vs(() => { - this.mediaKeys = void 0, this.keyIdToKeyInfo = {}, this.keyUriToKeyInfo = {}, this.sessionIdToKeyUri = {} - })); - return zc.clearKeyUriToKeyIdMap(), t - } - setServerCertificate(e = null) { - return this.needsCert ? (e && this.certificate$.next(e), Gu(this.certificate$, e => null != e).pipe(So(1e4)).pipe(La(e => this.setCert ? $i(!0) : !this.setCertSubject || this.setCertSubject.isStopped ? (this.setCertSubject = new er, Fr(this.mediaKeys.setServerCertificate(e)).pipe(Za(e => { - e = void 0 === e || e; - this.setCert = e, this.setCertSubject.next(e), this.setCertSubject.complete() - }), Vn(e => (this.setCert = !1, this.setCertSubject.error(e), Wu)), La(() => this.setCertSubject))) : this.setCertSubject), Vn(e => { - throw e - }))) : $i(!0) - } - ensureKeyContext(e) { - var t = e.uri; - this.keyUriToKeyInfo[t] || (this.keyUriToKeyInfo[t] = new Yc(e)); - const i = this.keyUriToKeyInfo[t]; - if (i.session) return i; - if (this.useSingleKeySession && 0 < this.sessions.length) return i.session = this.sessions[0].session, i; - t = this.mediaKeys.createSession(); - return t && this.sessions.push(new Xc(t, this.onkeystatuseschange, this.onkeymessage, this.logger)), i.session = t, i - } - startKeyRequest(t) { - const i = t.uri, - r = this.ensureKeyContext(t); - if (null == r || !r.session) return Vi(new yc("Could not create key session", t.uri, 0, $.KeySystemFailedToCreateSession, this.systemString)); - var e = O.utf8arrayToStr(t.keyId); - return this.keyIdToKeyInfo[e] = r, en([this.getKeyRequestInfo(r), this.setServerCertificate()]).pipe(hr(e => e[0]), La(e => { - var t; - return null === (t = this.sessionHandler) || void 0 === t || t.licenseChallengeSubmitted({ - keyuri: i, - keyFormat: this.systemString - }), this.generateLicenseChallenge(r, e).pipe(Vn(e => { - var t; - throw null === (t = this.sessionHandler) || void 0 === t || t.licenseChallengeError({ - keyuri: i - }), e - })) - }), La(e => { - var t; - return null === (t = this.sessionHandler) || void 0 === t || t.licenseChallengeCreated({ - keyuri: i, - cdmVersion: this.cdmVersion - }), this.getKeyRequestResponse(r, e) - }), La(e => { - var t; - return null === (t = this.sessionHandler) || void 0 === t || t.licenseResponseSubmitted({ - keyuri: i - }), this.handleParsedKeyResponse(r, e).pipe(Vn(e => { - var t; - throw null === (t = this.sessionHandler) || void 0 === t || t.licenseResponseError({ - keyuri: i - }), e - })) - }), hr(() => { - var e; - return null === (e = this.sessionHandler) || void 0 === e || e.licenseResponseProcessed({ - keyuri: i - }), r.setKeyRequestState(Kc.NONE), this.removeSessions(r.decryptdata, !1).subscribe(), t - }), Vn(e => { - throw this.handleKeyExchangeError(r, e), e - }), Vs(() => { - this._abortKeyRequest(r) - })) - } - _abortKeyRequest(e) { - var t, i; - e && (i = e.decryptdata.uri, O.utf8arrayToStr(e.decryptdata.keyId), e.requestState !== Kc.NONE && (null === (t = this.sessionHandler) || void 0 === t || t.keyAborted({ - keyuri: i - })), e.abort()) - } - handleKeyExchangeError(e, t) { - e.error(t), O.utf8arrayToStr(e.decryptdata.keyId) - } - updateItemId(e) { - this.itemId = e - } - removeKey(e) { - return this.removeKeyInternal(e) - } - removeKeyInternal(e) { - const t = this.keyUriToKeyInfo[e.uri]; - if (t && t.session) { - var i = t.session; - t.abort(), t.destroy(); - var r = O.utf8arrayToStr(e.keyId); - return this.keyIdToKeyInfo[r] = void 0, this.keyUriToKeyInfo[e.uri] = void 0, this.removeSession(i) - } - } - removeSessions(e, t) { - const i = this.keyUriToKeyInfo[e.uri]; - if (i) { - const r = i.oldSessions.map(e => this.removeSession(e)); - return i.oldSessions = [], nn(() => 0 === r.length, Wu, en(r)).pipe(La(() => t ? this.removeKeyInternal(e) : Wu)) - } - return Wu - } - removeSession(t) { - const e = this.sessions.findIndex(e => e.session === t), - i = this.sessions[e]; - return -1 < e && this.sessions.splice(e, 1), this.sessionIdToKeyUri[t.sessionId] = void 0, i ? i.destroy() : Wu - } - getKeyRequestInfo(e) { - var t = e.decryptdata, - i = t.uri, - e = e.setKeyRequestState(Kc.GET_REQUEST_INFO); - return this.eventEmitter.trigger(x.KEY_REQUEST_STARTED, { - keyuri: i, - decryptdata: t, - timestamp: Date.now() - }), e - } - setKeyRequestInfo(e, t) { - const i = this.keyUriToKeyInfo[e]; - i && i.resolveState(Kc.GET_REQUEST_INFO, t) - } - sanitizeRequest(e) { - return e - } - generateLicenseChallengeInternal(t, e, i) { - const r = t.decryptdata, - n = t.session, - s = r.uri, - a = r.keyId; - let o; - var l = t.setKeyRequestState(Kc.GET_CHALLENGE); - if (n.generateRequestPromise) o = Nr(n.generateRequestPromise, Ti).pipe(La(() => this.generateRequestInitialized(t, "usable" === i))); - else { - const i = this.generateInitData(a, r, e); - n.generateRequestPromise = n.generateRequest(i.initDataType, i.initData), o = Nr(n.generateRequestPromise, Ti).pipe(Za(() => { - this.sessionIdToKeyUri[n.sessionId] = s - }), Vn(e => { - throw new yc(e.message, t.decryptdata.uri, 0, $.KeySystemFailedToGenerateLicenseRequest, this.systemString) - })) - } - return en([l, o]).pipe(hr(e => new Uint8Array(e[0]))) - } - generateLicenseChallenge(e, t) { - const i = e.decryptdata, - r = e.session, - n = i.uri, - s = i.keyId; - let a, o; - if (O.utf8arrayToStr(i.keyId), e.licenseChallenge && e.resolveState(Kc.GET_CHALLENGE, e.licenseChallenge), t = this.sanitizeRequest(t), e.requestInfo = t, this.sessionId = this.sessionId || t && t.sessionId || this.itemId, this.systemString === Fc.systemStringPrefix) { - const e = new Uint8Array(s); - Wc.changeEndianness(e), a = r.keyStatuses.get(e) - } else a = r.keyStatuses.get(s); - switch (a) { - case "status-pending": - case "usable": - case "expired": - case void 0: - o = this.generateLicenseChallengeInternal(e, t, a); - break; - default: - o = Vi(new yc(`Bad internal state state=${a}`, n, 0, $.KeySystemUnexpectedState, this.systemString)) - } - return o - } - setParsedResponse(e, t) { - const i = this.keyUriToKeyInfo[e]; - i && i.resolveState(Kc.GET_KEY_RESPONSE, t) - } - handleKeyStatusesChange(e) { - e.target.keyStatuses.forEach((e, t, i) => { - t = new Uint8Array(t); - this.systemString === Fc.systemStringPrefix && Wc.changeEndianness(t); - t = O.utf8arrayToStr(t), t = this.keyIdToKeyInfo[t]; - t && this.handleKeyStatusForKey(e, t) - }) - } - handleKeyStatusForKey(e, t) { - if (t) { - var i = t.decryptdata, - r = i.uri; - switch (e) { - case "internal-error": - this.logger.error(`${this.systemString} internal-error for key ${Jc(i)}`), this._signalError(t, new gc("Got internal error from key system", r, 0, $.KeySystemInternalError, !1, rc.InternalError)); - break; - case "usable": - t.requestState === Kc.PROCESS_LICENSE && t.resolveState(Kc.PROCESS_LICENSE, void 0); - break; - case "output-restricted": - this.logger.warn(`${this.systemString} output-restricted for key ${Jc(i)}`), t.session && this.removeSession(t.session).pipe(Va(this.destroy$)).subscribe(), this._signalError(t, new gc("output-restricted", r, 0, $.KeySystemOutputRestricted, !1, rc.OutputRestricted)); - break; - case "expired": - this.logger.warn(`${this.systemString} expired for key ${Jc(i)}. Attempting renewal`), this._signalRenewal(t) - } - } - } - _scheduleRenewal(e, t) { - bn(t).pipe(Za(() => this._signalRenewal(e)), Va(fn(e.destroy$, this.destroy$, Gu(e.onKeyRequestState$, e => e === Kc.GET_REQUEST_INFO)))).subscribe() - } - _signalRenewal(e) { - this._keyStatusChange$.next({ - decryptdata: e.decryptdata, - status: "needs-renewal" - }) - } - _signalError(e, t) { - this._keyStatusChange$.next({ - decryptdata: e.decryptdata, - status: "error", - error: t - }), e.error(t) - } - } - const eh = "org.w3.clearkey", - th = { - id: "clearkey", - systemStringPrefix: eh, - keyFormatString: eh, - securityLevels: { - NONE: 0 - } - }, - ih = ["clearkey", "fairplaystreaming", "playready", "widevine"], - rh = { - initDataTypes: ["keyids", "cenc"] - }; - var nh, nu = class extends Zc { - constructor(e, t, i, r, n, s) { - super(e, t, i, r, !1, n, s) - } - static get requestAccessConfig() { - return rh - } - get needsCert() { - return !1 - } - getKeyRequestResponse(e, t) { - return Rc({ - url: e.decryptdata.uri, - xhrSetup: this.config.xhrSetup - }, { - maxLoadTimeMs: 0, - maxTimeToFirstByteMs: 0, - autoRetry: !1, - timeoutRetry: null, - errorRetry: null - }).pipe(hr(([e]) => { - e = new Uint8Array(e); - return { - response: this.parseResponse(t, e) - } - })) - } - parseResponse(e, t) { - t = { - kty: "oct", - kid: qc.base64UrlEncode(e), - k: qc.base64UrlEncode(t) - }; - return O.strToUtf8array(JSON.stringify({ - keys: [t] - })) - } - handleParsedKeyResponse(i, e) { - e = e.response; - return en([i.setKeyRequestState(Kc.PROCESS_LICENSE), Fr(i.session.update(e)).pipe(Za(() => { - var e = i.decryptdata.keyId, - e = i.session.keyStatuses.get(e); - this.handleKeyStatusForKey(e, i) - }), Vn(e => { - var t = { - code: e.code, - text: "Failed to update with key response" - }; - throw new yc(e.message, i.decryptdata.uri, e.code, t, this.systemString) - }))]).pipe(Zs(void 0)) - } - generateInitData(e) { - return { - initData: Wc.makeKeyIdsInitData([e]), - initDataType: "keyids" - } - } - generateRequestInitialized() { - return Wu - } - sanitizeRequest(e) { - return e - } - handleKeyMessage(e) { - if (!this.isDestroying && "license-request" === e.messageType) { - e = new Uint8Array(e.message), e = JSON.parse(O.utf8arrayToStr(e).trim()); - if (1 === e.kids.length) { - const t = qc.base64Decode(e.kids[0]), - i = O.utf8arrayToStr(t), - r = this.keyIdToKeyInfo[i]; - r && r.resolveState(Kc.GET_CHALLENGE, t) - } - } - } - }; - const sh = { - initDataTypes: ["cenc"] - }, - ah = new Uint8Array([148, 206, 134, 251, 7, 255, 79, 67, 173, 184, 147, 210, 250, 150, 140, 162]); - (iu = nh = nh || {})[iu.CENC = 1667591779] = "CENC", iu[iu.CBCS = 1667392371] = "CBCS"; - class oh extends Zc { - constructor(e, t, i, r, n, s, a) { - super(e, t, i, r, n, s, a), this._hasSetRenewal = !1 - } - static get systemId() { - return ah - } - static get requestAccessConfig() { - return sh - } - get needsCert() { - return !0 - } - sanitizeRequest(e) { - return { - assetId: e && e.assetId ? new Uint8Array(e.assetId) : void 0, - ssc: e && e.ssc ? new Uint8Array(e.ssc) : void 0, - sessionId: e && e.sessionId ? e.sessionId : void 0 - } - } - _scheduleRenewal(e, t) { - this.useSingleKeySession ? this._hasSetRenewal || (this._hasSetRenewal = !0, bn(t).pipe(Za(() => { - var e = Object.values(this.keyUriToKeyInfo)[0]; - e && this._signalRenewal(e) - }), Vs(() => { - this._hasSetRenewal = !1 - }), Va(this.destroy$)).subscribe()) : super._scheduleRenewal(e, t) - } - handleParsedKeyResponse(t, e) { - var i = t.decryptdata.uri, - r = e.statusCode, - n = e.ckc && 0 !== e.ckc.byteLength ? e.ckc : e.license && 0 !== e.license.byteLength ? e.license : void 0; - if (0 === r && !n) return Vi(new gc("License request resulted in HTTP Error", i, r, { - code: r, - text: "HTTP Error" - }, !0, rc.HttpError)); - if (0 !== r) return Vi(new gc("License server responded with error", i, r, { - code: r, - text: "Server Error" - }, !1, rc.LicenseServerError)); - if (!n) return Vi(new gc("License server responded with invalid license", i, r, { - code: r, - text: "Invalid license" - }, !1, rc.LicenseServerError)); - const s = e.renewalDate, - a = new Date, - o = s > a ? s.getTime() - a.getTime() : 0; - 0 < o && this._scheduleRenewal(t, o); - const l = this.makeProcessLicenseRequestMessage(t, n, o), - d = t.session, - u = Fr(d.update(l)).pipe(Za(() => { - var e = t.decryptdata.keyId, - e = d.keyStatuses.get(e); - this.handleKeyStatusForKey(e, t) - }), Vn(e => { - throw new yc(e.message, t.decryptdata.uri, 0, $.KeySystemFailedToUpdateSession, this.systemString) - })); - return en([t.setKeyRequestState(Kc.PROCESS_LICENSE), u]).pipe(Zs(void 0)) - } - makeKeyRequests(e) { - const t = []; - for (const i of e) { - const e = i.decryptdata, - r = i.requestInfo, - n = e.keyId; - t.push({ - keyId: n, - assetId: r ? r.assetId : void 0, - ssc: r ? r.ssc : void 0, - versionList: e.formatversions - }) - } - return t - } - getSchemeAndFlags(e) { - return { - scheme: "ISO-23001-7" === e.method ? nh.CENC : nh.CBCS, - flags: 0 - } - } - generateInitData(e, t, i) { - var { - scheme: r, - flags: n - } = this.getSchemeAndFlags(t), i = this.makeKeyRequests([{ - decryptdata: t, - requestInfo: i - }]); - return { - initData: this.makeFpsKeySystemInitData(r, n, i), - initDataType: "cenc" - } - } - generateRequestInitialized(t, e) { - tc(`[Keys] challenge create start uri=${le(t.decryptdata.uri)} versions=${JSON.stringify(t.decryptdata.formatversions)}`); - e = this.makeKeyRequestMessage(t, e); - return e ? Fr(t.session.update(e)).pipe(Za(() => { - t.requestInfo = void 0 - }), Vn(e => { - throw tc(`[Keys] ${this.systemString} FAIL: generateRequestInitialized keyuri=${le(t.decryptdata.uri)} message=${e.message}`), new yc(e.message, t.decryptdata.uri, 0, $.KeySystemFailedToGenerateLicenseRenewal, this.systemString) - })) : Vi(new gc("Unable to generate request using existing keySession", t.decryptdata.uri, 0, $.KeySystemFailedToGenerateLicenseRequest, !0, rc.InvalidState)) - } - getKeyRequestResponse(e, t) { - var i = e.decryptdata.uri, - e = e.setKeyRequestState(Kc.GET_KEY_RESPONSE); - return this.eventEmitter.trigger(x.LICENSE_CHALLENGE_CREATED, { - keyuri: i, - licenseChallenge: t, - keysystem: this.systemString - }), e - } - resolveSPCPromise(e, t) { - e.resolveState(Kc.GET_CHALLENGE, t) - } - } - const lh = {}, - dh = 1919710053; - - function uh(e, t, i) { - if (!(i + 4 > e.byteLength)) { - t = t.getUint32(i); - if (!((i += 4) + t > e.byteLength)) { - e = e.slice(i, i + t); - return { - pos: i += t, - data: e - } - } - } - } - - function ch(t) { - const i = {}, - r = new DataView(t.buffer); - let n = 4; - const s = r.getUint32(n); - n += 4; - for (let e = 0; e < s && n < t.byteLength && !(n + 16 > t.byteLength); ++e) { - const s = t.slice(n, n + 16); - if (n += 16, n + 4 > t.byteLength) break; - var a = uh(t, r, n); - if (!a) break; - n = a.pos, i[O.utf8arrayToStr(s)] = a.data - } - return i - } - - function hh(e, t, i, r) { - var n = r ? r.byteLength : 0; - return t.setUint32(i, n), n && e.set(r, i + 4), i + (4 + n) - } - - function ph(e) { - let t = 4; - for (const i of e) t += 28 + (i.assetId ? i.assetId.byteLength : 0) + (i.ssc ? i.ssc.byteLength : 0) + (i.versionList ? 4 * i.versionList.length : 0); - return t - } - - function fh(e, t, i, r) { - t.setUint32(i, r.length), i += 4; - for (const n of r) - if (e.set(n.keyId, i), i = hh(e, t, i += 16, n.assetId), i = hh(e, t, i, n.ssc), t.setUint32(i, n.versionList ? n.versionList.length : 0), i += 4, n.versionList) - for (const e of n.versionList) t.setUint32(i, e), i += 4; - return i - } - class mh extends oh { - constructor(e, t, i, r, n, s) { - super(e, t, i, r, void 0 === i.useMultipleKeySessions || !i.useMultipleKeySessions, n, s) - } - makeProcessLicenseRequestMessage(e, t, i) { - t = new Uint8Array(t); - return function(e) { - let t = 0; - for (const i of e) t += 24 + i.ckc.byteLength; - let i = 0; - const r = new Uint8Array(8 + t), - n = new DataView(r.buffer); - n.setUint32(0, 1667982195), n.setUint32(4, e.length), i += 8; - for (const t of e) r.set(t.keyId, i), i += 16, n.setUint32(i, t.expirySec), i += 4, i = hh(r, n, i, t.ckc); - return r - }([{ - keyId: e.decryptdata.keyId, - expirySec: i / 1e3, - ckc: t - }]) - } - makeFpsKeySystemInitData(e, t, i) { - i = function(e, t, i) { - var r = ph(i); - const n = new Uint8Array(8 + r), - s = new DataView(n.buffer); - return s.setUint32(0, e), s.setUint32(4, 1 << 24 | 16777215 & t), fh(n, s, 8, i), n - }(e, t, i); - return ge.pssh(mh.systemId, [], i) - } - makeKeyRequestMessage(e) { - return function(e) { - var t = ph(e); - const i = new Uint8Array(4 + t), - r = new DataView(i.buffer); - return r.setUint32(0, 1668442994), fh(i, r, 4, e), i - }([{ - keyId: e.decryptdata.keyId, - assetId: e.requestInfo ? e.requestInfo.assetId : void 0, - ssc: e.requestInfo ? e.requestInfo.ssc : void 0, - versionList: e.decryptdata.formatversions - }]) - } - handleKeyMessage(e) { - if (e.message.byteLength < 4) this.logger.warn("Unexpected message"); - else { - const t = new Uint8Array(e.message), - i = new DataView(e.message), - r = i.getUint32(0); - if (this.isDestroying && r !== dh) this.logger.warn(`In the middle of destroying, ignore command: ${r.toString(16)}`); - else switch (r) { - case 1667592820: - this.logger.warn("Certificate not set!"); - break; - case 1919837559: { - const e = ch(t); - for (const t in e) - if (Object.prototype.hasOwnProperty.call(e, t)) { - const e = this.keyIdToKeyInfo[t]; - e && this._signalRenewal(e) - } break - } - case 1936745331: { - const e = ch(t); - for (const t in e) - if (Object.prototype.hasOwnProperty.call(e, t)) { - const i = this.keyIdToKeyInfo[t], - r = e[t]; - i && r && this.resolveSPCPromise(i, r) - } break - } - case dh: - this._handleLicenseRelease(t); - break; - case 1667525993: { - const e = uh(t, i, 4); - e && (this.cdmVersion = O.utf8arrayToStr(e.data)); - break - } - default: - this.logger.warn(`Unrecognized command:'0x${r.toString(16)}'`) - } - } - } - _handleLicenseRelease(e) { - const t = {}, - i = new DataView(e.buffer); - switch (i.getUint32(4)) { - case 1936946288: - lh, 0, t[lh.SessionId] = this.sessionId; - var r; - if (e.byteLength < 12) break; - t[lh.APIProvider] = i.getUint32(8) === nh.CENC ? "EC396D13-FB13-4993-9D0D-71518ACF3D6F" : "F19BF03B-7470-41A4-9655-86D078307D59", 0; - var n = uh(e, i, 12); - if (!n) break; - if (r = n.pos, t[lh.MovieID] = O.utf8arrayToStr(n.data), !(n = uh(e, i, r))) break; - if (r = n.pos, t[lh.SecureStopSPC] = n.data, !(n = uh(e, i, r))) break; - n.pos, t[lh.SessionLifespanSPC] = n.data - } - this.eventEmitter.trigger(x.LICENSE_RELEASED, { - keysystem: this.systemString, - itemId: this.itemId, - releaseRecord: t - }) - } - } - ru = mh; - const gh = { - fpsd: O.strToUtf8array("fpsd"), - fpsi: O.strToUtf8array("fpsi"), - fpsk: O.strToUtf8array("fpsk"), - fkri: O.strToUtf8array("fkri"), - fkai: O.strToUtf8array("fkai"), - fkcx: O.strToUtf8array("fkcx"), - fkvl: O.strToUtf8array("fkvl") - }; - t = class extends oh { - constructor(e, t, i, r, n, s) { - super(e, t, i, r, !1, n, s), this.sessions = [], this.keyIdToKeyInfo = {}, this.keyUriToKeyInfo = {}, this.sessionIdToKeyUri = {} - } - static get needsCert() { - return !0 - } - handleKeyExchangeError(e, t) { - this.removeKey(e.decryptdata).subscribe(), super.handleKeyExchangeError(e, t) - } - _abortKeyRequest(e) { - return !this.isDestroying && e && e.requestState !== Kc.NONE && this.removeKey(e.decryptdata).subscribe(), super._abortKeyRequest(e) - } - makeFpsKeySystemInitData(e, t, i) { - const r = [gh.fpsd, (n = e, e = t, t = new Uint8Array(4), ge.set32(n, t, 0), ge.box(gh.fpsi, new Uint8Array([0, e >> 16 & 255, e >> 8 & 255, 255 & e]), t))]; - var n; - for (const s of i) r.push(function(t, e, i, r) { - const n = [gh.fpsk], - s = ge.box(gh.fkri, new Uint8Array([0, 0, 0, 0]), t); - if (n.push(s), e && e.byteLength && n.push(ge.box(gh.fkai, e)), i && i.byteLength && n.push(ge.box(gh.fkcx, i)), r && r.length) { - const t = new Uint8Array(4 * r.length); - let e = 0; - for (const i of r) ge.set32(i, t, e), e += 4; - n.push(ge.box(gh.fkvl, t)) - } - return ge.box.apply(null, n) - }(s.keyId, s.assetId, s.ssc, s.versionList)); - i = ge.box.apply(null, r); - return ge.pssh(oh.systemId, null, i) - } - makeKeyRequestMessage(e, t) { - if (t) return O.strToUtf8array("renew") - } - makeProcessLicenseRequestMessage(e, t, i) { - t = JSON.stringify([{ - keyID: qc.base64Encode(e.decryptdata.keyId), - payload: qc.base64Encode(new Uint8Array(t)) - }]); - return O.strToUtf8array(t) - } - handleKeyMessage(e) { - const t = e.target, - i = t.sessionId, - r = e.messageType, - n = this.sessionIdToKeyUri[i]; - let s; - if (n) s = this.keyUriToKeyInfo[n]; - else - for (const e of Object.values(this.keyUriToKeyInfo)) e && e.session === t && (s = e); - if (s) switch (r) { - case "license-request": { - const t = new Uint8Array(e.message), - i = O.utf8arrayToStr(t); - try { - JSON.parse(i).forEach(e => { - var t = qc.base64DecodeToStr(e.keyID), - e = qc.base64Decode(e.payload), - t = this.keyIdToKeyInfo[t]; - t && this.resolveSPCPromise(t, e) - }) - } catch (e) { - this.logger.warn("[Keys] got unexpected license-request format"), this.resolveSPCPromise(s, t) - } - break - } - case "license-renewal": { - const t = new Uint8Array(e.message); - this.resolveSPCPromise(s, t); - break - } - case "license-release": - this._handleLicenseRelease(t); - break; - default: - this.logger.warn(`[Keys] Unexpected messageType ${r}`) - } else this.logger.warn("[Keys] No key associated with session") - } - _handleLicenseRelease(e) { - e.update(O.strToUtf8array("acknowledged")).catch(e => { - this.logger.error(`Promise error: ${e.message}`) - }) - } - }; - const yh = { - initDataTypes: ["cenc"] - }, - vh = new Uint8Array([154, 4, 240, 121, 152, 64, 66, 134, 171, 146, 230, 91, 224, 136, 95, 149]); - class Sh extends Zc { - constructor(e, t, i, r, n, s) { - super(e, t, i, r, !1, n, s), this.shouldDestroyMediaKeys = !0 - } - static get systemId() { - return vh - } - static get requestAccessConfig() { - return yh - } - get needsCert() { - return !1 - } - generateInitData(e, t) { - t = t.pssh; - return { - initData: ge.pssh(Sh.systemId, [], t), - initDataType: "cenc" - } - } - removeKey(e) { - return super.removeSessions(e, !0) - } - ensureKeyContext(e) { - const t = e.uri, - i = this.keyUriToKeyInfo[t]; - return null != i && i.session && (i.oldSessions.push(i.session), i.session = null), super.ensureKeyContext(e) - } - getKeyRequestResponse(e, t) { - var i = e.decryptdata.uri, - r = e.setKeyRequestState(Kc.GET_KEY_RESPONSE); - return this.eventEmitter.trigger(x.LICENSE_CHALLENGE_CREATED, { - keyuri: i, - licenseChallenge: t, - keysystem: this.systemString, - keyId: e.decryptdata.keyId - }), r - } - generateRequestInitialized(e) { - var t = e.licenseChallenge; - return e.requestInfo = void 0, e.resolveState(Kc.GET_CHALLENGE, t), Wu - } - handleParsedKeyResponse(t, e) { - const i = t.decryptdata.uri, - r = e.statusCode; - if (0 !== r) return Vi(new gc("License server responded with error", i, r, { - code: r, - text: "Server error" - }, !1, rc.LicenseServerError)); - if (!e.license || !e.license.byteLength) return Vi(new gc("License server responded with invalid license", i, r, { - code: r, - text: "Invalid license" - }, !1, rc.LicenseServerError)); - if (e.renewalDate) { - const i = e.renewalDate, - r = new Date, - n = i > r ? i.getTime() - r.getTime() : 0; - 0 < n && this._scheduleRenewal(t, n) - } - return en([t.setKeyRequestState(Kc.PROCESS_LICENSE), Fr(t.session.update(e.license)).pipe(Za(() => { - t.resolveState(Kc.PROCESS_LICENSE, void 0) - }), Vn(e => { - throw this.logger.error(`${this.systemString} FAIL: Failed to update with key response message=${e.message}`), new yc(e.message, t.decryptdata.uri, 0, $.KeySystemFailedToUpdateSession, this.systemString) - }))]).pipe(Zs(void 0)) - } - handleKeyMessage(e) { - if (this.isDestroying) this.logger.warn("In the middle of destroying, ignore key message"); - else { - const t = new DOMParser, - i = new("utf16" === this.config.playReadyMessageFormat ? Uint16Array : Uint8Array)(e.message.buffer || e.message), - r = String.fromCharCode.apply(null, Array.from(i)), - n = t.parseFromString(r, "application/xml").getElementsByTagName("PlayReadyKeyMessage")[0]; - if (n && "LicenseAcquisition" === n.getAttribute("type")) { - const s = t.parseFromString(r, "application/xml").getElementsByTagName("Challenge")[0]; - if (s && "base64encoded" === s.getAttribute("encoding") && 0 !== s.childNodes.length) { - const a = qc.base64Decode(s.childNodes[0].nodeValue), - o = O.utf8arrayToStr(a), - l = t.parseFromString(o, "application/xml").getElementsByTagName("KID")[0]; - e = null, e = l.childNodes[0] ? l.childNodes[0].nodeValue : l.getAttribute("VALUE"), e = qc.base64Decode(e).subarray(0, 16); - Wc.changeEndianness(e); - const d = this.keyIdToKeyInfo[O.utf8arrayToStr(e)]; - d && (d.licenseChallenge = a, d.resolveState(Kc.GET_CHALLENGE, a)) - } else this.logger.warn(`${this.systemString} wrong challenge format or empty challenge`) - } else this.logger.warn(`${this.systemString} unrecognized message ignore it`) - } - } - } - w = Sh; - const bh = { - initDataTypes: ["cenc", "keyids"] - }, - Th = new Uint8Array([237, 239, 139, 169, 121, 214, 74, 206, 163, 200, 39, 220, 213, 29, 33, 237]), - Eh = { - clearkey: th, - fairplaystreaming: Sc, - playready: Fc, - widevine: Bc - }, - Ih = { - clearkey: [ - ["org.w3.clearkey", nu] - ], - fairplaystreaming: [ - ["com.apple.fps.3_0", t], - ["com.apple.fps", ru] - ], - playready: [ - ["com.microsoft.playready.recommendation", w] - ], - widevine: [ - ["com.widevine.alpha", class extends Zc { - constructor(e, t, i, r, n, s) { - super(e, t, i, r, !1, n, s), this.shouldDestroyMediaKeys = !0 - } - static get systemId() { - return Th - } - static get requestAccessConfig() { - return bh - } - get needsCert() { - return !0 - } - removeKey(e) { - return super.removeSessions(e, !0) - } - ensureKeyContext(e) { - const t = e.uri, - i = this.keyUriToKeyInfo[t]; - return null != i && i.session && (i.oldSessions.push(i.session), i.session = null), super.ensureKeyContext(e) - } - getKeyRequestResponse(e, t) { - var i = e.decryptdata.uri, - r = e.setKeyRequestState(Kc.GET_KEY_RESPONSE); - return this.eventEmitter.trigger(x.LICENSE_CHALLENGE_CREATED, { - keyuri: i, - licenseChallenge: t, - keysystem: this.systemString, - keyId: e.decryptdata.keyId - }), r - } - handleParsedKeyResponse(t, e) { - t.licenseChallenge = void 0; - const i = t.decryptdata.uri, - r = e.statusCode; - if (0 !== r) return Vi(new gc("License server responded with error", i, r, { - code: r, - text: "Server error" - }, !1, rc.LicenseServerError)); - if (!e.license || !e.license.byteLength) return Vi(new gc("License server responded with invalid license", i, r, { - code: r, - text: "Invalid license" - }, !1, rc.LicenseServerError)); - if (e.renewalDate) { - const i = e.renewalDate, - r = new Date, - n = i > r ? i.getTime() - r.getTime() : 0; - 0 < n && this._scheduleRenewal(t, n) - } - return en([t.setKeyRequestState(Kc.PROCESS_LICENSE), Fr(t.session.update(e.license)).pipe(Za(() => { - var e = t.decryptdata.keyId, - e = t.session.keyStatuses.get(e); - this.handleKeyStatusForKey(e, t) - }), Vn(e => { - throw this.logger.error(`${this.systemString} FAIL: Failed to update with key response code=${e.code} message=${e.message}`), new yc(e.message, t.decryptdata.uri, e.code, { - code: e.code, - text: "Failed to update with key response" - }, this.systemString) - }))]).pipe(Zs(void 0)) - } - generateInitData(e, t) { - return { - initData: t.pssh, - initDataType: "cenc" - } - } - generateRequestInitialized(e) { - var t = e.licenseChallenge; - return e.requestInfo = void 0, e.resolveState(Kc.GET_CHALLENGE, t), Wu - } - handleKeyMessage(i) { - if (this.isDestroying) this.logger.warn("In the middle of destroying, ignore key message"); - else { - const r = i.target; - let e = null, - t = null; - if (r.sessionId in this.sessionIdToKeyUri) e = this.sessionIdToKeyUri[r.sessionId], t = this.keyUriToKeyInfo[e]; - else - for (const [i, n] of Object.entries(this.keyUriToKeyInfo)) - if (n.session === r) { - e = i, t = n; - break - } if (t) switch (i.messageType) { - case "license-request": { - const r = new Uint8Array(i.message); - t.resolveState(Kc.GET_CHALLENGE, r); - break - } - } else this.logger.warn(`${this.systemString} empty keyuri and keyInfo`) - } - } - }] - ] - }, - wh = th.id; - class Ah { - createMediaKeys(t, e, i, r, n) { - let s = Ah.idToMediaKeysInfoMap[t]; - if (!s) { - const a = Wc.getCapabilities(e, i), - o = new er; - Ah.requestKeySystemAccess(t, a, n, r).pipe(La(function(e) { - return Fr((Ah.idToMediaKeysInfoMap[t].keySystemAccess = e).createMediaKeys()) - }), Za(e => { - o.next(e), o.complete() - }), Vn(e => (o.error(new yc(`could not initialize key system: ${e.message}`, void 0, 0, $.KeySystemFailedToInitialize, t)), Ii)), Va(Ah.destroy$)).subscribe(), s = Ah.idToMediaKeysInfoMap[t] = { - mediaKeys$: o, - keySystemAccess: null - } - } - return s.mediaKeys$ - } - destroyMediaKeys() { - Ah.destroy$.next(), Ah.idToMediaKeysInfoMap = {} - } - static getKeySystemIdForDecryptData(e) { - let t; - if (e) { - t = wh; - var i = e.format; - for (const e of ih) { - var r = Eh[e]; - if ((null == r ? void 0 : r.keyFormatString) === i) { - t = e; - break - } - } - } - if (!t) throw Error("No matching key system"); - return t - } - static requestKeySystemAccess(e, t, i, r) { - if ("undefined" == typeof navigator || void 0 === navigator.requestMediaKeySystemAccess) return Vi(new yc("navigator undefined", void 0, 0, $.KeySystemUndefinedNavigator, e)); - const n = Ih[e]; - let s = Vi(new yc("no key systems to try", void 0, 0, $.KeySystemNoKeySystemsToTry, e)); - for (const e of n) { - const n = e[0], - o = e[1], - l = (a = i) && "object" == typeof a ? i : o.requestAccessConfig, - d = [Object.assign({}, l, t)]; - s = s.pipe(Vn(() => Ah.requestKeySystemInternal(n, d, r))) - } - var a; - return s - } - static requestKeySystemInternal(e, t, i) { - return Zr(() => Fr(navigator.requestMediaKeySystemAccess(e, t))) - } - make(e, t, i, r, n, s) { - var a = null === (l = Ah.idToMediaKeysInfoMap[e]) || void 0 === l ? void 0 : l.keySystemAccess; - if (!a) throw new yc(`No keySystemAccess for ${e}`, void 0, 0, $.KeySystemNoKeySystemAccess, e); - let o; - var l = Eh[e].systemStringPrefix; - for (const t of Ih[e]) - if (t[0] === a.keySystem) { - o = t[1]; - break - } if (!o) throw new yc(`No constructor associated with ${e}`, void 0, 0, $.KeySystemNoConstructor, l); - return new o(t, l, i, r, n, s) - } - static get availableKeySystems() { - return Object.keys(Eh) - } - static getKeySystemFormat(e) { - e = Eh[e]; - return e ? e.keyFormatString : "" - } - static getKeySystemSecurityLevel(e) { - e = Eh[e]; - return e ? e.securityLevels : void 0 - } - } - Ah.idToMediaKeysInfoMap = {}, Ah.destroy$ = new Xt; - const Oh = ["avc1.42E01E"], - kh = ["mp4a.40.2"]; - - function Ch(e, t, i) { - if (t) { - if (t instanceof dr) t = new mc("Key request timed out", e, $.KeySystemRequestTimedOut); - else if (t instanceof yc) { - const i = (null == t ? void 0 : t.response) || $.InternalError; - t = new gc(t.message, e, 0, i, !1, rc.InternalError, !0) - } - } else t = new gc("Unknown error from CDM", e, 0, $.KeySystemCDMUnknownError, !1, rc.InternalError); - return (t instanceof gc || t instanceof mc) && (t.mediaOptionIds = [...i]), t - } - class Dh { - constructor(e, t, i, r, n, s, a, o = new Ah) { - this.ksService = e, this.mediaSink = t, this.config = i, this.platformQuery = r, this.eventEmitter = n, this.sessionHandler = s, this.keySystemFactory = o, this.reset$ = new Xt, this.keyRequest$ = new Xt, this.abort$ = new Xt, this.keySystem$ = new yi(null), this._keyStatusChange$ = new Xt, this.protectionData = {}, this.keySystemId = null, this.keyUriToRequest = {}, this.ksQuery = e.getQuery(), this.logger = a.child({ - name: "eme" - }), this.config.warmupCdms && this.keySystemFactory.createMediaKeys(Sc.id, Oh, kh, this.logger, void 0).subscribe(), an(r.platformInfo$.pipe(Is((e, t) => e && t && e.requiresCDMAttachOnStart === t.requiresCDMAttachOnStart), La(e => null != e && e.requiresCDMAttachOnStart ? this.attachMediaKeys().pipe(Vn(e => (this.handleKeySystemError(e), Ii))) : Wu), $a(Ii)), this.keyRequest$.pipe(jr(e => e.pipe(Vn(() => Ii)))), this.keySystem$.pipe(La(e => e ? e.keyStatusChange$.pipe(Za(e => { - var t = e.decryptdata.uri, - i = this.ksQuery.getKeyInfo(t); - "needs-renewal" === e.status ? this.ksService.updateKeyRequestState(t, ic.MustRequestResponse, e => e === ic.GotKeyResponse) : (i = Ch(t, e.error, null !== (i = null == i ? void 0 : i.mediaOptionIds) && void 0 !== i ? i : []), this.ksService.setError(t, i)), this._keyStatusChange$.next(e) - })) : Ii)), this.isKeyCleanupSupported() ? this.mediaSink.mediaQuery.bufferedSegmentsTuple$.pipe(jr(e => { - const [t, i] = e, r = new Set; - return t.forEach(e => { - e = null === (e = null === (e = e.frag) || void 0 === e ? void 0 : e.keyTagInfo) || void 0 === e ? void 0 : e.uri; - e && r.add(e) - }), i.forEach(e => { - e = null === (e = null === (e = e.frag) || void 0 === e ? void 0 : e.keyTagInfo) || void 0 === e ? void 0 : e.uri; - e && r.add(e) - }), this.handleKeyCleanup(r) - })) : Wu).pipe(Va(this.reset$)).subscribe() - } - get keyStatusChange$() { - return this._keyStatusChange$ - } - get keySystem() { - return this.keySystem$.value - } - destroy() { - this.reset$.next(), this.ksService.removeAll(), this.keySystemId = null; - const e = this.keySystem; - let t = Wu; - return e && (this.keySystem$.next(null), e.shouldDestroyMediaKeys && this.keySystemFactory.destroyMediaKeys(), t = e.destroy()), Mr([t, this.mediaSink.clearMediaKeys()]).pipe(Zs(void 0)) - } - attachMediaKeys() { - if (this.keySystem) return Wu; - var e = this.config.keySystemPreference ? Ah.getKeySystemFormat(this.config.keySystemPreference) : Sc.keyFormatString; - return this.makeKeySystem(new zc("NONE", null, null, e, [1])).pipe(Zs(void 0)) - } - isKeyCleanupSupported() { - return !0 === this.config.useMultipleKeySessions || "widevine" === this.config.keySystemPreference || "playready" === this.config.keySystemPreference - } - handleKeyCleanup(i) { - if (this.ksQuery.getCount() < 6) return Wu; - const r = performance.now(), - e = this.ksQuery.getAll().map(e => { - var t = e.keyUri; - if (!i.has(t)) { - const i = ac(e.decryptdata); - if ("AES-128" !== i.method && r > e.minHoldTime) return this._removeKey(t, i) - } - return Wu - }); - return e.length ? en(e).pipe($a(Wu)) : Wu - } - _removeKey(e, t) { - return this.abort$.next(e), this.ksService.removeKey(e), this.keySystem.removeKey(t) - } - removeKeysForItems(i) { - const r = []; - return al(() => { - for (const e of i) { - this.ksService.removeAllKeysForItem(e); - const i = this.ksQuery.getAll({ - filterBy: e => 0 === e.itemIds.length - }); - for (const t of i) r.push(this._removeKey(t.keyUri, ac(t.decryptdata))) - } - }), r.length ? en(r).pipe(La(() => Wu)) : Wu - } - get availableKeySystems() { - return Ah.availableKeySystems - } - initialize(e) { - var t = this.protectionData; - this.protectionData = {}; - var i = this.config.keySystemPreference; - for (const a of Ah.availableKeySystems) { - var r = e[a]; - if (r) - if (i === a) { - var n, s = r.certificate, - r = r.serverCertUrl ? bu.buildAbsoluteURL(window.location.href, r.serverCertUrl) : void 0; - let e; - this.protectionData[a] = { - serverCertUrl: r, - certificate: s - }, s ? e = $i({ - keysystem: a, - certificate: s - }) : r && (null === (n = null == t ? void 0 : t[a]) || void 0 === n ? void 0 : n.serverCertUrl) !== r && (n = Tu(r) ? this.config.certLoadPolicy.customURL : this.config.certLoadPolicy.default, e = Rc({ - url: r, - xhrSetup: this.config.xhrSetup - }, n).pipe(hr(([e]) => ({ - keysystem: a, - certificate: new Uint8Array(e) - })))), e && e.pipe(La(e => this.onServerCertificateLoaded(e)), Vn(e => { - throw this.logger.error(`Error loading cert: ${e.message}`), this.eventEmitter.trigger(x.INTERNAL_ERROR, { - type: o, - details: "certificateLoadError", - fatal: !1, - handled: !0, - reason: "Error handling cert", - response: $.KeySystemCertificateLoadError, - message: e.message, - name: "certificateLoadError" - }), e - }), Va(this.reset$)).subscribe() - } else this.logger.warn(`Key system ${a} does not match preference ${i}, ignoring`) - } - } - generateRequest(e, t) { - this.keySystem && this.keySystem.setKeyRequestInfo(e, t) - } - setLicenseResponse(e, t) { - this.keySystem && this.keySystem.setParsedResponse(e, t) - } - getKeyFromDecryptData(e, t) { - if (!e || !e.isEncrypted) return $i(e); - let i; - return al(() => { - i = this._getKeyFromDecryptData(e, t) - }), i - } - _getKeyFromDecryptData(t, i) { - let r = null, - n = null; - i && (r = i.itemId, n = i.mediaOptionId); - const s = t.uri, - e = this.ksQuery.getKeyInfo(s); - if (e && null != i && this.ksService.addMediaOption(s, i), (null == e ? void 0 : e.error) instanceof gc && !1 === e.error.isOkToRetry) return Vi(e.error); - if (e && e.requestState !== ic.MustRequestResponse) return e.requestState === ic.GotKeyResponse ? $i(ac(e.decryptdata)) : this.keyUriToRequest[s]; { - const o = performance.now() + this.config.keyMinHoldTimeBeforeCleanup; - let e; - this.abort$.next(s), this.ksService.upsertKey({ - keyUri: s, - decryptdata: function(e) { - var { - method: t, - isEncrypted: i, - uri: r, - format: n, - formatversions: s - } = e; - return { - method: t, - isEncrypted: i, - uri: r, - format: n, - formatversions: s, - ivBuf: null !== (s = null === (s = e.iv) || void 0 === s ? void 0 : s.buffer) && void 0 !== s ? s : null, - keyIdBuf: null === (s = e.keyId) || void 0 === s ? void 0 : s.buffer, - keyBuf: null === (s = e.key) || void 0 === s ? void 0 : s.buffer, - psshBuf: null === (e = e.key) || void 0 === e ? void 0 : e.buffer - } - }(t), - minHoldTime: o, - mediaOptionIds: [n], - requestState: ic.WaitingForKeyResponse, - itemIds: [r] - }); - var a = t.method; - switch (a) { - case "SAMPLE-AES": - case "ISO-23001-7": - case "SAMPLE-AES-CTR": { - const o = this.config.keyLoadPolicy.customURL; - e = this.fetchKeyEME(t).pipe(So(o.maxLoadTimeMs)); - break - } - case "AES-128": - e = this.fetchKeyHTTP(t.uri, t, this.config.keyLoadPolicy); - break; - default: - return Vi(new V(!1, `Unexpected METHOD attribute ${a}`, $.KeySystemUnexpectedMETHOD)) - } - i = this.keyUriToRequest[s] = e.pipe(hr(e => { - var t = e.decryptdata; - return this.ksService.updateKeyValue(s, t.key), this.eventEmitter.trigger(x.KEY_LOADED, e), e.decryptdata - }), Vn(e => { - var t = this.ksQuery.getKeyInfo(s); - return e = Ch(s, e, null !== (t = null == t ? void 0 : t.mediaOptionIds) && void 0 !== t ? t : []), this.ksService.setError(s, e), Vi(e) - }), Vs(() => { - this.ksService.updateKeyRequestState(s, ic.MustRequestResponse, e => e === ic.WaitingForKeyResponse), this.keyUriToRequest[s] = null - }), Aa(), Va(fn(this.abort$.pipe(ln(e => e === s)), this.reset$).pipe(Za(e => this.logger.warn(e ? `aborted ${le(e)}` : "got reset"))))); - return this.keyRequest$.next(i), i - } - } - fetchKeyEME(e) { - return this.requestKey(e).pipe(hr(e => ({ - timestamp: performance.now(), - keyuri: e.uri, - decryptdata: e - }))) - } - fetchKeyHTTP(e, t, i) { - return Dh.fetchKeyHTTP(e, this.config, t, i) - } - static fetchKeyHTTP(t, e, i, r) { - e = { - url: t, - xhrSetup: e.xhrSetup - }; - return Rc(e, Lc(e, r)).pipe(hr(([e]) => (i.key = new Uint8Array(e), { - decryptdata: i, - keyuri: t, - timestamp: performance.now() - }))) - } - requestKey(t) { - return this.makeKeySystem(t).pipe(La(e => e.startKeyRequest(t))) - } - ensureKeySystem(t) { - return Zr(() => { - if (!this.keySystem && this.keySystemId) { - this.keySystem$.next(this.keySystemFactory.make(this.keySystemId, t, this.config, this.eventEmitter, this.sessionHandler, this.logger)); - var e = this.protectionData && this.protectionData[this.keySystemId]; - if (e) return this.keySystem.setServerCertificate(e.certificate).pipe(Zs(this.keySystem)) - } - return $i(this.keySystem) - }) - } - makeKeySystem(e) { - return this.ensureMediaKeys(e).pipe(La(e => this.mediaSink.setMediaKeys(e).pipe(La(() => this.ensureKeySystem(e))))) - } - ensureMediaKeys(e) { - var t = Ah.getKeySystemIdForDecryptData(e); - if (null == this.keySystemId) this.keySystemId = t; - else if (this.keySystemId !== t) return Vi(new gc(`New key system string does not match existing ${t} !== ${this.keySystemId}`, e.uri, 0, $.KeySystemUnmatchedString, !1, rc.InternalError)); - return this.keySystemFactory.createMediaKeys(this.keySystemId, Oh, kh, this.logger, null === (e = this.platformQuery.platformInfo) || void 0 === e ? void 0 : e.keySystemConfig) - } - onServerCertificateLoaded(e) { - var t = e.keysystem, - e = e.certificate; - return this.protectionData[t].certificate = e, this.keySystem && this.keySystemId === t ? this.keySystem.setServerCertificate(e).pipe(Zs(void 0)) : Wu - } - handleKeySystemError(e) { - e = new yc(e.message, void 0, void 0, $.KeySystemSetupError, void 0); - this.eventEmitter.trigger(x.INTERNAL_ERROR, e) - } - } - class Mh extends kl { - constructor(e) { - super(e) - } - getKeyInfo(e) { - e = this.getEntity(e); - return e ? Object.assign(Object.assign({}, e), { - error: vc(e.error, e.mediaOptionIds) - }) : null - } - getKeyInfo$(e) { - return this.selectEntity(e).pipe(hr(e => e ? Object.assign(Object.assign({}, e), { - error: vc(e.error, e.mediaOptionIds) - }) : null)) - } - getKeyRequestState$(e) { - return this.selectEntity(e, e => null == e ? void 0 : e.requestState) - } - getKeyStatus$(e) { - return this.selectEntity(e, e => null == e ? void 0 : e.status) - } - getKeyError$(e) { - return this.selectEntity(e, e => vc(null == e ? void 0 : e.error, null == e ? void 0 : e.mediaOptionIds)).pipe(wl) - } - } - class xh { - constructor(e) { - this.store = e - } - getQuery() { - return new Mh(this.store) - } - upsertKey(i) { - Do("keys.upsert", i.keyUri); - const r = new Set(i.itemIds.filter(e => null != e)), - n = new Set(i.mediaOptionIds.filter(e => null != e)); - this.store.upsert(i.keyUri, e => { - const t = Object.assign(Object.assign({}, e), i); - if ("itemIds" in e) - for (const i of e.itemIds) r.add(i); - if (t.itemIds = Array.from(r), "mediaOptionIds" in e) - for (const i of e.mediaOptionIds) n.add(i); - return t.mediaOptionIds = Array.from(n), t - }, () => Object.assign(Object.assign({}, i), { - itemIds: Array.from(r), - mediaOptionIds: Array.from(n) - })) - } - removeKey(e) { - Do("keys.removeKey", e), this.store.remove(e) - } - removeAllKeysForItem(i) { - Do(`keys.removeAllKeysForItem ${i}`), this.store.update(null, e => { - var t = e.itemIds.findIndex(e => e === i); - 0 <= t && e.itemIds.splice(t, 1) - }) - } - removeAll() { - Do("keys.remove"), this.store.remove() - } - updateKeyValue(e, t) { - Do("keys.updateKeyValue", e), this.store.update(e, e => { - null == e.decryptdata.keyBuf && null != t && (e.decryptdata.keyBuf = t.buffer), e.requestState = ic.GotKeyResponse - }) - } - updateKeyStatus(e, t) { - Do(`keys.updateKeyStatus ${t}`, e), this.store.update(e, e => { - e.status = t - }) - } - updateKeyRequestState(e, t, i) { - Do(`keys.updateKeyRequestState ${t}`, e), this.store.update(e, e => { - i && !i(e.requestState) || (e.requestState = t) - }) - } - addMediaOption(e, t) { - const { - itemId: i, - mediaOptionId: r - } = t; - Do(`keys.addMediaOption itemId: ${i}, mediaOptionId: ${r}`, e), this.store.update(e, e => { - null != r && e.mediaOptionIds.every(e => e !== r) && e.mediaOptionIds.push(r), null != i && e.itemIds.every(e => e !== i) && e.itemIds.push(i) - }) - } - setError(e, t) { - var i; - Do(`keys.setError ${null===(i=null==t?void 0:t.constructor)||void 0===i?void 0:i.name}`, e), this.store.update(e, e => { - e.error = vc(t), e.requestState = ic.MustRequestResponse - }) - } - } - const Ph = new class extends fl { - constructor() { - super({}, { - name: "key-system-store", - idKey: "keyUri", - producerFn: su - }) - } - }; - let Rh = null; - - function Lh(e) { - let t = e; - return Nh.hasOwnProperty(e) && (t = Nh[e]), String.fromCharCode(t) - } - - function _h(t) { - const i = []; - for (let e = 0; e < t.length; e++) i.push(t[e].toString(16)); - return i - } - const Nh = { - 42: 225, - 92: 233, - 94: 237, - 95: 243, - 96: 250, - 123: 231, - 124: 247, - 125: 209, - 126: 241, - 127: 9608, - 128: 174, - 129: 176, - 130: 189, - 131: 191, - 132: 8482, - 133: 162, - 134: 163, - 135: 9834, - 136: 224, - 137: 32, - 138: 232, - 139: 226, - 140: 234, - 141: 238, - 142: 244, - 143: 251, - 144: 193, - 145: 201, - 146: 211, - 147: 218, - 148: 220, - 149: 252, - 150: 8216, - 151: 161, - 152: 42, - 153: 8217, - 154: 9473, - 155: 169, - 156: 8480, - 157: 8226, - 158: 8220, - 159: 8221, - 160: 192, - 161: 194, - 162: 199, - 163: 200, - 164: 202, - 165: 203, - 166: 235, - 167: 206, - 168: 207, - 169: 239, - 170: 212, - 171: 217, - 172: 249, - 173: 219, - 174: 171, - 175: 187, - 176: 195, - 177: 227, - 178: 205, - 179: 204, - 180: 236, - 181: 210, - 182: 242, - 183: 213, - 184: 245, - 185: 123, - 186: 125, - 187: 92, - 188: 94, - 189: 95, - 190: 124, - 191: 8764, - 192: 196, - 193: 228, - 194: 214, - 195: 246, - 196: 223, - 197: 165, - 198: 164, - 199: 9475, - 200: 197, - 201: 229, - 202: 216, - 203: 248, - 204: 9487, - 205: 9491, - 206: 9495, - 207: 9499 - }, - Fh = 100, - Bh = { - 17: 1, - 18: 3, - 21: 5, - 22: 7, - 23: 9, - 16: 11, - 19: 12, - 20: 14 - }, - Uh = { - 17: 2, - 18: 4, - 21: 6, - 22: 8, - 23: 10, - 19: 13, - 20: 15 - }, - $h = { - 25: 1, - 26: 3, - 29: 5, - 30: 7, - 31: 9, - 24: 11, - 27: 12, - 28: 14 - }, - Vh = { - 25: 2, - 26: 4, - 29: 6, - 30: 8, - 31: 10, - 27: 13, - 28: 15 - }, - Kh = ["white", "green", "blue", "cyan", "red", "yellow", "magenta", "black", "transparent"], - qh = { - verboseFilter: { - DATA: 3, - DEBUG: 3, - INFO: 2, - WARNING: 2, - TEXT: 1, - ERROR: 0 - }, - time: null, - verboseLevel: 0, - setTime: function(e) { - this.time = e - }, - log: function(e, t) { - var i = this.verboseFilter[e]; - this.verboseLevel >= i && console.log(this.time + " [" + e + "] " + t) - } - }; - class Hh { - constructor(e, t, i, r, n) { - this.foreground = e || "white", this.underline = t || !1, this.italics = i || !1, this.background = r || "black", this.flash = n || !1 - } - reset() { - this.foreground = "white", this.underline = !1, this.italics = !1, this.background = "black", this.flash = !1 - } - setStyles(e) { - Object.assign(this, e) - } - isDefault() { - return "white" === this.foreground && !this.underline && !this.italics && "black" === this.background && !this.flash - } - equals(e) { - return this.foreground === e.foreground && this.underline === e.underline && this.italics === e.italics && this.background === e.background && this.flash === e.flash - } - copy(e) { - this.foreground = e.foreground, this.underline = e.underline, this.italics = e.italics, this.background = e.background, this.flash = e.flash - } - toString() { - return "color=" + this.foreground + ", underline=" + this.underline + ", italics=" + this.italics + ", background=" + this.background + ", flash=" + this.flash - } - } - class jh { - constructor(e, t, i, r, n, s) { - this.uchar = e || " ", this.penState = new Hh(t, i, r, n, s) - } - reset() { - this.uchar = " ", this.penState.reset() - } - setChar(e, t) { - this.uchar = e, this.penState.copy(t) - } - setPenState(e) { - this.penState.copy(e) - } - equals(e) { - return this.uchar === e.uchar && this.penState.equals(e.penState) - } - copy(e) { - this.uchar = e.uchar, this.penState.copy(e.penState) - } - isEmpty() { - return " " === this.uchar && this.penState.isDefault() - } - } - class Qh { - constructor() { - this.chars = []; - for (let e = 0; e < Fh; e++) this.chars.push(new jh); - this.pos = 0, this.currPenState = new Hh - } - equals(t) { - let i = !0; - for (let e = 0; e < Fh; e++) - if (!this.chars[e].equals(t.chars[e])) { - i = !1; - break - } return i - } - copy(t) { - for (let e = 0; e < Fh; e++) this.chars[e].copy(t.chars[e]) - } - isEmpty() { - let t = !0; - for (let e = 0; e < Fh; e++) - if (!this.chars[e].isEmpty()) { - t = !1; - break - } return t - } - setCursor(e) { - this.pos !== e && (this.pos = e), this.pos < 0 ? (qh.log("ERROR", "Negative cursor position " + this.pos), this.pos = 0) : this.pos > Fh && (qh.log("ERROR", "Too large cursor position " + this.pos), this.pos = Fh) - } - moveCursor(e) { - var t = this.pos + e; - if (1 < e) - for (let e = this.pos + 1; e < t + 1; e++) this.chars[e].setPenState(this.currPenState); - this.setCursor(t) - } - backSpace() { - this.moveCursor(-1), this.chars[this.pos].setChar(" ", this.currPenState) - } - insertChar(e) { - 144 <= e && this.backSpace(); - var t = Lh(e); - this.pos >= Fh ? qh.log("ERROR", "Cannot insert " + e.toString(16) + " (" + t + ") at position " + this.pos + ". Skipping it!") : (this.chars[this.pos].setChar(t, this.currPenState), this.moveCursor(1)) - } - clearFromPos(e) { - let t; - for (t = e; t < Fh; t++) this.chars[t].reset() - } - clear() { - this.clearFromPos(0), this.pos = 0, this.currPenState.reset() - } - clearToEndOfRow() { - this.clearFromPos(this.pos) - } - getTextString() { - const t = []; - let i = !0; - for (let e = 0; e < Fh; e++) { - var r = this.chars[e].uchar; - " " !== r && (i = !1), t.push(r) - } - return i ? "" : t.join("") - } - setPenStyles(e) { - this.currPenState.setStyles(e), this.chars[this.pos].setPenState(this.currPenState) - } - } - class Wh { - constructor() { - this.rows = []; - for (let e = 0; e < 15; e++) this.rows.push(new Qh); - this.currRow = 14, this.nrRollUpRows = null, this.reset() - } - reset() { - for (let e = 0; e < 15; e++) this.rows[e].clear(); - this.currRow = 14 - } - equals(t) { - let i = !0; - for (let e = 0; e < 15; e++) - if (!this.rows[e].equals(t.rows[e])) { - i = !1; - break - } return i - } - copy(t) { - for (let e = 0; e < 15; e++) this.rows[e].copy(t.rows[e]) - } - isEmpty() { - let t = !0; - for (let e = 0; e < 15; e++) - if (!this.rows[e].isEmpty()) { - t = !1; - break - } return t - } - backSpace() { - this.rows[this.currRow].backSpace() - } - clearToEndOfRow() { - this.rows[this.currRow].clearToEndOfRow() - } - insertChar(e) { - this.rows[this.currRow].insertChar(e) - } - setPen(e) { - this.rows[this.currRow].setPenStyles(e) - } - moveCursor(e) { - this.rows[this.currRow].moveCursor(e) - } - setCursor(e) { - qh.log("INFO", "setCursor: " + e), this.rows[this.currRow].setCursor(e) - } - setPAC(t) { - qh.log("INFO", "pacData = " + JSON.stringify(t)); - let i = t.row - 1; - if (this.nrRollUpRows && i < this.nrRollUpRows - 1 && (i = this.nrRollUpRows - 1), this.nrRollUpRows && this.currRow !== i) { - for (let e = 0; e < 15; e++) this.rows[e].clear(); - const t = this.currRow + 1 - this.nrRollUpRows, - r = this.lastOutputScreen; - if (r) { - const e = r.rows[t].cueStartTime; - if (e && e < qh.time) - for (let e = 0; e < this.nrRollUpRows; e++) this.rows[i - this.nrRollUpRows + e + 1].copy(r.rows[t + e]) - } - } - this.currRow = i; - const r = this.rows[this.currRow]; - if (null !== t.indent) { - const i = t.indent, - e = Math.max(i - 1, 0); - r.setCursor(t.indent), t.color = r.chars[e].penState.foreground - } - const e = { - foreground: t.color, - underline: t.underline, - italics: t.italics, - background: "black", - flash: !1 - }; - this.setPen(e) - } - setBkgData(e) { - qh.log("INFO", "bkgData = " + JSON.stringify(e)), this.backSpace(), this.setPen(e), this.insertChar(32) - } - setRollUpRows(e) { - this.nrRollUpRows = e - } - rollUp() { - if (null !== this.nrRollUpRows) { - qh.log("INFO", "TEXT " + this.getDisplayText()); - const e = this.currRow + 1 - this.nrRollUpRows, - t = this.rows.splice(e, 1)[0]; - t.clear(), this.rows.splice(this.currRow, 0, t), qh.log("INFO", "Rolling up") - } else qh.log("DEBUG", "roll_up but nrRollUpRows not set yet") - } - getDisplayText(t) { - t = t || !1; - const i = []; - let e = "", - r; - for (let e = 0; e < 15; e++) { - const n = this.rows[e].getTextString(); - n && (r = e + 1, t ? i.push("Row " + r + ": '" + n + "'") : i.push(n.trim())) - } - return 0 < i.length && (e = t ? "[" + i.join(" | ") + "]" : i.join("\n")), e - } - getTextAndFormat() { - return this.rows - } - } - class Gh { - constructor(e, t) { - this.chNr = e, this.outputFilter = t, this.mode = null, this.verbose = 0, this.displayedMemory = new Wh, this.nonDisplayedMemory = new Wh, this.lastOutputScreen = new Wh, this.currRollUpRow = this.displayedMemory.rows[14], this.writeScreen = this.displayedMemory, this.mode = null, this.cueStartTime = null - } - reset() { - this.mode = null, this.displayedMemory.reset(), this.nonDisplayedMemory.reset(), this.lastOutputScreen.reset(), this.currRollUpRow = this.displayedMemory.rows[14], this.writeScreen = this.displayedMemory, this.mode = null, this.cueStartTime = null, this.lastCueEndTime = null - } - getHandler() { - return this.outputFilter - } - setHandler(e) { - this.outputFilter = e - } - setPAC(e) { - this.writeScreen.setPAC(e) - } - setBkgData(e) { - this.writeScreen.setBkgData(e) - } - setMode(e) { - e !== this.mode && (this.mode = e, qh.log("INFO", "MODE=" + e), "MODE_POP-ON" === this.mode ? this.writeScreen = this.nonDisplayedMemory : (this.writeScreen = this.displayedMemory, this.writeScreen.reset()), "MODE_ROLL-UP" !== this.mode && (this.displayedMemory.nrRollUpRows = null, this.nonDisplayedMemory.nrRollUpRows = null), this.mode = e) - } - insertChars(t) { - for (let e = 0; e < t.length; e++) this.writeScreen.insertChar(t[e]); - var e = this.writeScreen === this.displayedMemory ? "DISP" : "NON_DISP"; - qh.log("INFO", e + ": " + this.writeScreen.getDisplayText(!0)), "MODE_PAINT-ON" !== this.mode && "MODE_ROLL-UP" !== this.mode || (qh.log("TEXT", "DISPLAYED: " + this.displayedMemory.getDisplayText(!0)), this.outputDataUpdate()) - } - ccRCL() { - qh.log("INFO", "RCL - Resume Caption Loading"), this.setMode("MODE_POP-ON") - } - ccBS() { - qh.log("INFO", "BS - BackSpace"), "MODE_TEXT" !== this.mode && (this.writeScreen.backSpace(), this.writeScreen === this.displayedMemory && this.outputDataUpdate()) - } - ccAOF() {} - ccAON() {} - ccDER() { - qh.log("INFO", "DER- Delete to End of Row"), this.writeScreen.clearToEndOfRow(), this.outputDataUpdate() - } - ccRU(e) { - qh.log("INFO", "RU(" + e + ") - Roll Up"), this.writeScreen = this.displayedMemory, this.setMode("MODE_ROLL-UP"), this.writeScreen.setRollUpRows(e) - } - ccFON() { - qh.log("INFO", "FON - Flash On"), this.writeScreen.setPen({ - flash: !0 - }) - } - ccRDC() { - qh.log("INFO", "RDC - Resume Direct Captioning"), this.setMode("MODE_PAINT-ON") - } - ccTR() { - qh.log("INFO", "TR"), this.setMode("MODE_TEXT") - } - ccRTD() { - qh.log("INFO", "RTD"), this.setMode("MODE_TEXT") - } - ccEDM() { - qh.log("INFO", "EDM - Erase Displayed Memory"), this.displayedMemory.reset(), this.outputDataUpdate(!0) - } - ccCR() { - qh.log("INFO", "CR - Carriage Return"), this.writeScreen.rollUp(), this.outputDataUpdate(!0) - } - ccENM() { - qh.log("INFO", "ENM - Erase Non-displayed Memory"), this.nonDisplayedMemory.reset() - } - ccEOC() { - var e; - qh.log("INFO", "EOC - End Of Caption"), "MODE_POP-ON" === this.mode && (e = this.displayedMemory, this.displayedMemory = this.nonDisplayedMemory, this.nonDisplayedMemory = e, this.writeScreen = this.nonDisplayedMemory, qh.log("TEXT", "DISP: " + this.displayedMemory.getDisplayText())), this.outputDataUpdate(!0) - } - ccTO(e) { - qh.log("INFO", "TO(" + e + ") - Tab Offset"), this.writeScreen.moveCursor(e) - } - ccMIDROW(e) { - const t = { - flash: !1, - underline: !1, - italics: !1 - }; - t.underline = e % 2 == 1, t.italics = 46 <= e, t.italics ? t.foreground = "white" : (e = Math.floor(e / 2) - 16, t.foreground = ["white", "green", "blue", "cyan", "red", "yellow", "magenta"][e]), qh.log("INFO", "MIDROW: " + JSON.stringify(t)), this.writeScreen.setPen(t) - } - outputDataUpdate(e = !1) { - var t = qh.time; - null !== t && this.outputFilter && (this.outputFilter.updateData && this.outputFilter.updateData(t, this.displayedMemory), null !== this.cueStartTime || this.displayedMemory.isEmpty() ? this.displayedMemory.equals(this.lastOutputScreen) || (this.outputFilter.newCue && (this.outputFilter.newCue(this.cueStartTime, t, this.lastOutputScreen), !0 === e && this.outputFilter.dispatchCue && this.outputFilter.dispatchCue()), this.cueStartTime = this.displayedMemory.isEmpty() ? null : t) : this.cueStartTime = t, this.lastOutputScreen.copy(this.displayedMemory)) - } - cueSplitAtTime(e) { - this.outputFilter && (this.displayedMemory.isEmpty() || (this.outputFilter.newCue && this.outputFilter.newCue(this.cueStartTime, e, this.displayedMemory), this.cueStartTime = e)) - } - } - var zh = class { - constructor(e = 1, t, i) { - this.field = e, this.currChNr = -1, this.lastCmdA = null, this.lastCmdB = null, this.channels = [new Gh(1, t), new Gh(2, i)], this.dataCounters = { - padding: 0, - char: 0, - cmd: 0, - other: 0 - } - } - getHandler(e) { - return this.channels[e].getHandler() - } - setHandler(e, t) { - this.channels[e].setHandler(t) - } - addData(e, t) { - let i, r, n, s = null; - qh.setTime(e); - for (let e = 0; e < t.length; e += 2) r = 127 & t[e], n = 127 & t[e + 1], 16 <= r && r <= 31 && r === this.lastCmdA && n === this.lastCmdB ? (this.lastCmdA = null, this.lastCmdB = null, qh.log("DEBUG", "Repeated command (" + _h([r, n]) + ") is dropped")) : 0 != r || 0 != n ? (qh.log("DATA", "[" + _h([t[e], t[e + 1]]) + "] -> (" + _h([r, n]) + ")"), i = this.parseCmd(r, n), i = i || this.parseMidrow(r, n), i = i || this.parsePAC(r, n), i = i || this.parseBackgroundAttributes(r, n), !i && (s = this.parseChars(r, n), s) && (this.currChNr && 0 <= this.currChNr ? this.channels[this.currChNr - 1].insertChars(s) : qh.log("WARNING", "No channel found yet. TEXT-MODE?")), i ? this.dataCounters.cmd += 2 : s ? this.dataCounters.char += 2 : (this.dataCounters.other += 2, qh.log("WARNING", "Couldn't parse cleaned data " + _h([r, n]) + " orig: " + _h([t[e], t[e + 1]])))) : this.dataCounters.padding += 2 - } - parseCmd(e, t) { - var i; - if (!((20 === e || 21 === e || 28 === e || 29 === e) && 32 <= t && t <= 47 || (23 === e || 31 === e) && 33 <= t && t <= 35)) return !1; - const r = this.channels[(i = 20 === e || 23 === e ? 1 : 2) - 1]; - return 20 === e || 21 === e || 28 === e || 29 === e ? 32 === t ? r.ccRCL() : 33 === t ? r.ccBS() : 34 === t ? r.ccAOF() : 35 === t ? r.ccAON() : 36 === t ? r.ccDER() : 37 === t ? r.ccRU(2) : 38 === t ? r.ccRU(3) : 39 === t ? r.ccRU(4) : 40 === t ? r.ccFON() : 41 === t ? r.ccRDC() : 42 === t ? r.ccTR() : 43 === t ? r.ccRTD() : 44 === t ? r.ccEDM() : 45 === t ? r.ccCR() : 46 === t ? r.ccENM() : 47 === t && r.ccEOC() : r.ccTO(t - 32), this.lastCmdA = e, this.lastCmdB = t, this.currChNr = i, !0 - } - parseMidrow(e, t) { - var i; - if ((17 === e || 25 === e) && 32 <= t && t <= 47) { - if ((i = 17 === e ? 1 : 2) !== this.currChNr) return qh.log("ERROR", "Mismatch channel in midrow parsing"), !1; - const r = this.channels[i - 1]; - return r.insertChars([32]), r.ccMIDROW(t), qh.log("DEBUG", "MIDROW (" + _h([e, t]) + ")"), this.lastCmdA = e, this.lastCmdB = t, !0 - } - return !1 - } - parsePAC(e, t) { - if (!((17 <= e && e <= 23 || 25 <= e && e <= 31) && 64 <= t && t <= 127 || (16 === e || 24 === e) && 64 <= t && t <= 95)) return !1; - var i = e <= 23 ? 1 : 2, - r = (64 <= t && t <= 95 ? 1 == i ? Bh : $h : 1 == i ? Uh : Vh)[e], - r = this.interpretPAC(r, t); - return this.channels[i - 1].setPAC(r), this.lastCmdA = e, this.lastCmdB = t, this.currChNr = i, !0 - } - interpretPAC(e, t) { - var i; - const r = { - color: null, - italics: !1, - indent: null, - underline: !1, - row: e - }; - return i = 95 < t ? t - 96 : t - 64, r.underline = 1 == (1 & i), i <= 13 ? r.color = ["white", "green", "blue", "cyan", "red", "yellow", "magenta", "white"][Math.floor(i / 2)] : i <= 15 ? (r.italics = !0, r.color = "white") : r.indent = 4 * Math.floor((i - 16) / 2), r - } - parseChars(e, t) { - let i = null, - r = null, - n = null; - var s; - if (n = 25 <= e ? (i = 2, e - 8) : (i = 1, e), 17 <= n && n <= 19 ? (s = t, s = 17 === n ? t + 80 : 18 === n ? t + 112 : t + 144, qh.log("INFO", "Special char '" + Lh(s) + "' in channel " + i), r = [s], this.lastCmdA = e, this.lastCmdB = t) : 32 <= e && e <= 127 && (r = 0 === t ? [e] : [e, t], this.lastCmdA = null, this.lastCmdB = null), r) { - const e = _h(r); - qh.log("DEBUG", `Char codes = ${e.join(",")}`) - } - return r - } - parseBackgroundAttributes(e, t) { - let i, r, n; - return ((16 === e || 24 === e) && 32 <= t && t <= 47 || (23 === e || 31 === e) && 45 <= t && t <= 47) && (i = { - underline: !1 - }, 16 === e || 24 === e ? (r = Math.floor((t - 32) / 2), i.background = Kh[r], t % 2 == 1 && (i.background = i.background + "_semi")) : 45 === t ? i.background = "transparent" : (i.foreground = "black", 47 === t && (i.underline = !0)), n = this.channels[(e < 24 ? 1 : 2) - 1], n.setBkgData(i), this.lastCmdA = null, !(this.lastCmdB = null)) - } - reset() { - for (let e = 0; e < this.channels.length; e++) this.channels[e] && this.channels[e].reset(); - this.lastCmdA = null, this.lastCmdB = null - } - cueSplitAtTime(t) { - for (let e = 0; e < this.channels.length; e++) this.channels[e] && this.channels[e].cueSplitAtTime(t) - } - }; - class Xh { - constructor(e, t) { - this.handler = e, this.track = t, this.startTime = null, this.endTime = null, this.screen = null - } - dispatchCue() { - null !== this.startTime && (this.handler.addCues("cc" + this.track, this.startTime, this.endTime, this.screen), this.startTime = null) - } - newCue(e, t, i) { - (null === this.startTime || this.startTime > e) && (this.startTime = e), this.endTime = t, this.screen = i, this.handler.createHTMLCaptionsTrack(this.track) - } - } - var Yh = {}, - iu = {}, - nu = {}, - t = {}; - Object.defineProperty(t, "__esModule", { - value: !0 - }), t.isValidPercentValue = function(e) { - return "number" == typeof e && 0 <= e && e <= 100 - }, t.isValidAlignSetting = function(e) { - return "string" == typeof e && ["start", "center", "end", "left", "right", "middle"].includes(e) - }, t.isValidDirectionSetting = function(e) { - return "string" == typeof e && ["", "rl", "lr"].includes(e) - }, t.isValidLineAndPositionSetting = function(e) { - return "number" == typeof e || "auto" === e - }, t.isValidLineAlignSetting = function(e) { - return "string" == typeof e && ["start", "center", "end"].includes(e) - }, t.isValidPositionAlignSetting = function(e) { - return "string" == typeof e && ["line-left", "center", "line-right", "auto", "left", "start", "middle", "end", "right"].includes(e) - }, t.isValidScrollSetting = function(e) { - return ["", "up"].includes(e) - }; - ru = {}; - Object.defineProperty(ru, "__esModule", { - value: !0 + logger.levels = pino.levels; + logger.level = level; + + logger.setMaxListeners = logger.getMaxListeners = + logger.emit = logger.addListener = logger.on = + logger.prependListener = logger.once = + logger.prependOnceListener = logger.removeListener = + logger.removeAllListeners = logger.listeners = + logger.listenerCount = logger.eventNames = + logger.write = logger.flush = noop$2; + logger.serializers = serializers; + logger._serialize = serialize; + logger._stdErrSerialize = stdErrSerialize; + logger.child = child; + + if (transmit) logger._logEvent = createLogEventShape(); + + function getLevelVal () { + return this.level === 'silent' + ? Infinity + : this.levels.values[this.level] + } + + function getLevel () { + return this._level + } + function setLevel (level) { + if (level !== 'silent' && !this.levels.values[level]) { + throw Error('unknown level ' + level) + } + this._level = level; + + set$1(setOpts, logger, 'error', 'log'); // <-- must stay first + set$1(setOpts, logger, 'fatal', 'error'); + set$1(setOpts, logger, 'warn', 'error'); + set$1(setOpts, logger, 'info', 'log'); + set$1(setOpts, logger, 'debug', 'log'); + set$1(setOpts, logger, 'trace', 'log'); + } + + function child (bindings) { + if (!bindings) { + throw new Error('missing bindings for child Pino') + } + const bindingsSerializers = bindings.serializers; + if (serialize && bindingsSerializers) { + var childSerializers = Object.assign({}, serializers, bindingsSerializers); + var childSerialize = opts.browser.serialize === true + ? Object.keys(childSerializers) + : serialize; + delete bindings.serializers; + applySerializers([bindings], childSerialize, childSerializers, this._stdErrSerialize); + } + function Child (parent) { + this._childLevel = (parent._childLevel | 0) + 1; + this.error = bind(parent, bindings, 'error'); + this.fatal = bind(parent, bindings, 'fatal'); + this.warn = bind(parent, bindings, 'warn'); + this.info = bind(parent, bindings, 'info'); + this.debug = bind(parent, bindings, 'debug'); + this.trace = bind(parent, bindings, 'trace'); + if (childSerializers) { + this.serializers = childSerializers; + this._serialize = childSerialize; + } + if (transmit) { + this._logEvent = createLogEventShape( + [].concat(parent._logEvent.bindings, bindings) + ); + } + } + Child.prototype = this; + return new Child(this) + } + return logger + } + + pino.levels = { + values: { + fatal: 60, + error: 50, + warn: 40, + info: 30, + debug: 20, + trace: 10 + }, + labels: { + 10: 'trace', + 20: 'debug', + 30: 'info', + 40: 'warn', + 50: 'error', + 60: 'fatal' + } + }; + + pino.stdSerializers = stdSerializers; + pino.stdTimeFunctions = Object.assign({}, { nullTime, epochTime, unixTime, isoTime }); + + function set$1 (opts, logger, level, fallback) { + const proto = Object.getPrototypeOf(logger); + logger[level] = logger.levelVal > logger.levels.values[level] + ? noop$2 + : (proto[level] ? proto[level] : (_console[level] || _console[fallback] || noop$2)); + + wrap(opts, logger, level); + } + + function wrap (opts, logger, level) { + if (!opts.transmit && logger[level] === noop$2) return + + logger[level] = (function (write) { + return function LOG () { + const ts = opts.timestamp(); + const args = new Array(arguments.length); + const proto = (Object.getPrototypeOf && Object.getPrototypeOf(this) === _console) ? _console : this; + for (var i = 0; i < args.length; i++) args[i] = arguments[i]; + + if (opts.serialize && !opts.asObject) { + applySerializers(args, this._serialize, this.serializers, this._stdErrSerialize); + } + if (opts.asObject) write.call(proto, asObject(this, level, args, ts)); + else write.apply(proto, args); + + if (opts.transmit) { + const transmitLevel = opts.transmit.level || logger.level; + const transmitValue = pino.levels.values[transmitLevel]; + const methodValue = pino.levels.values[level]; + if (methodValue < transmitValue) return + transmit(this, { + ts, + methodLevel: level, + methodValue, + transmitLevel, + transmitValue: pino.levels.values[opts.transmit.level || logger.level], + send: opts.transmit.send, + val: logger.levelVal + }, args); + } + } + })(logger[level]); + } + + function asObject (logger, level, args, ts) { + if (logger._serialize) applySerializers(args, logger._serialize, logger.serializers, logger._stdErrSerialize); + const argsCloned = args.slice(); + let msg = argsCloned[0]; + const o = {}; + if (ts) { + o.time = ts; + } + o.level = pino.levels.values[level]; + let lvl = (logger._childLevel | 0) + 1; + if (lvl < 1) lvl = 1; + // deliberate, catching objects, arrays + if (msg !== null && typeof msg === 'object') { + while (lvl-- && typeof argsCloned[0] === 'object') { + Object.assign(o, argsCloned.shift()); + } + msg = argsCloned.length ? format(argsCloned.shift(), argsCloned) : undefined; + } else if (typeof msg === 'string') msg = format(argsCloned.shift(), argsCloned); + if (msg !== undefined) o.msg = msg; + return o + } + + function applySerializers (args, serialize, serializers, stdErrSerialize) { + for (const i in args) { + if (stdErrSerialize && args[i] instanceof Error) { + args[i] = pino.stdSerializers.err(args[i]); + } else if (typeof args[i] === 'object' && !Array.isArray(args[i])) { + for (const k in args[i]) { + if (serialize && serialize.indexOf(k) > -1 && k in serializers) { + args[i][k] = serializers[k](args[i][k]); + } + } + } + } + } + + function bind (parent, bindings, level) { + return function () { + try{ + const args = new Array(1 + arguments.length); + args[0] = bindings; + for (var i = 1; i < args.length; i++) { + args[i] = arguments[i - 1]; + } + return parent[level].apply(this, args)} + catch(e){ + + } + } + } + + function transmit (logger, opts, args) { + const send = opts.send; + const ts = opts.ts; + const methodLevel = opts.methodLevel; + const methodValue = opts.methodValue; + const val = opts.val; + const bindings = logger._logEvent.bindings; + + applySerializers( + args, + logger._serialize || Object.keys(logger.serializers), + logger.serializers, + logger._stdErrSerialize === undefined ? true : logger._stdErrSerialize + ); + logger._logEvent.ts = ts; + logger._logEvent.messages = args.filter(function (arg) { + // bindings can only be objects, so reference equality check via indexOf is fine + return bindings.indexOf(arg) === -1 }); - const Jh = { - "&": "&", - "<": "<", - ">": ">", - "‎": "‎", - "‏": "‏", - " ": " " - }, - Zh = { - c: "span", - i: "i", - b: "b", - u: "u", - ruby: "ruby", - rt: "rt", - v: "span", - lang: "span" - }, - ep = { - v: "title", - lang: "lang" - }, - tp = { - rt: "ruby" - }, - ip = { - "text-combine-upright": "-webkit-text-combine:horizontal; text-orientation: mixed;" - }; - class rp { - static parseTimeStamp(e) { - function t(e) { - var [t, i, r, e] = e.map(e => e ? parseInt("" + e) : 0); - return 3600 * t + 60 * i + r + e / 1e3 - } - const i = /^(\d+):(\d{2})(:\d{2})?\.(\d{3})/.exec(e); - return i ? i[3] ? t([i[1], i[2], i[3].substring(1), i[4]]) : 59 < parseInt(i[1]) ? t([i[1], i[2], null, i[4]]) : t([null, i[1], i[2], i[4]]) : null - } - static parseContent(s, t, a) { - let i = t.text; - - function e(e) { - return Jh[e] - } - const r = s.document.createElement("div"), - n = []; - let o, l, d = r; - for (; null !== (o = function() { - if (!i) return null; - var e = (e = /^([^<]*)(<[^>]+>?)?/.exec(i))[1] || e[2]; - return i = i.substr(e.length), e - }());) - if ("<" !== o[0]) d.appendChild(s.document.createTextNode(o.replace(/&(amp|lt|gt|lrm|rlm|nbsp);/g, e))); - else { - if ("/" === o[1]) { - n.length && n[n.length - 1] === o.substr(2).replace(">", "") && (n.pop(), d = d.parentNode); - continue - } - const t = rp.parseTimeStamp(o.substr(1, o.length - 2)); - let e; - if (t) { - e = s.document.createProcessingInstruction("timestamp", t.toString()), d.appendChild(e); - continue - } - if (!(l = /^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/.exec(o))) continue; - if (e = function(e, t, i) { - var r = Zh[e]; - if (!r) return null; - const n = s.document.createElement(r); - if (n.dataset.localName = r, (e = ep[e]) && i && (n[e] = i.trim()), t) - if (a[t]) { - const s = function(e) { - let t = ""; - for (const i in e) t += ip[i] || i + ":" + e[i] + ";"; - return t - }(a[t]); - n.setAttribute("style", s) - } else console.info(`WebVTT: parseContent: Style referenced, but no style defined for '${t}'!`); - return n - }(l[1], l[2], l[3]), !e) continue; - if (u = d, c = e, tp[c.dataset.localName] && tp[c.dataset.localName] !== u.dataset.localName) continue; - n.push(l[1]), d.appendChild(e), d = e - } var u, c; - return r - } + + logger._logEvent.level.label = methodLevel; + logger._logEvent.level.value = methodValue; + + send(methodLevel, logger._logEvent, val); + + logger._logEvent = createLogEventShape(bindings); + } + + function createLogEventShape (bindings) { + return { + ts: 0, + messages: [], + bindings: bindings || [], + level: { label: '', value: 0 } } - ru.default = rp; - w = e && e.__decorate || function(e, t, i, r) { - var n, s = arguments.length, - a = s < 3 ? t : null === r ? r = Object.getOwnPropertyDescriptor(t, i) : r; - if ("object" == typeof Reflect && "function" == typeof Reflect.decorate) a = Reflect.decorate(e, t, i, r); - else - for (var o = e.length - 1; 0 <= o; o--)(n = e[o]) && (a = (s < 3 ? n(a) : 3 < s ? n(t, i, a) : n(t, i)) || a); - return 3 < s && a && Object.defineProperty(t, i, a), a + } + + function asErrValue (err) { + const obj = { + type: err.constructor.name, + msg: err.message, + stack: err.stack }; - Object.defineProperty(nu, "__esModule", { - value: !0 - }); - const np = t, - sp = ru; - w = w([function(e) { - let t = e; - return "undefined" != typeof window && null != window.VTTCue && (t = window.VTTCue, t.create = e.create, t.fromJSON = e.fromJSON, t.prototype.toJSON = e.prototype.toJSON), t - }], w = class { - constructor(e, t, i) { - this._id = "", this._pauseOnExit = !1, this._region = null, this._vertical = "", this._snapToLines = !0, this._line = "auto", this._lineAlign = "start", this._position = "auto", this._positionAlign = "auto", this._size = 100, this._align = "center", this.hasBeenReset = !1, this._startTime = e, this._endTime = t, this._text = i - } - get id() { - return this._id - } - set id(e) { - this._id = "" + e - } - get pauseOnExit() { - return this._pauseOnExit - } - set pauseOnExit(e) { - this._pauseOnExit = !!e - } - get startTime() { - return this._startTime - } - set startTime(e) { - if ("number" != typeof e) throw new TypeError(`Start time must be set to a number: ${e}`); - this._startTime = e, this.hasBeenReset = !0 - } - get endTime() { - return this._endTime - } - set endTime(e) { - if ("number" != typeof e) throw new TypeError(`End time must be set to a number: ${e}`); - this._endTime = e, this.hasBeenReset = !0 - } - get text() { - return this._text - } - set text(e) { - this._text = "" + e, this.hasBeenReset = !0 - } - get region() { - return this._region - } - set region(e) { - this._region = e, this.hasBeenReset = !0 - } - get vertical() { - return this._vertical - } - set vertical(e) { - if (!np.isValidDirectionSetting(e)) throw new SyntaxError(`An invalid or illegal string was specified for vertical: ${e}`); - this._vertical = e, this.hasBeenReset = !0 - } - get snapToLines() { - return this._snapToLines - } - set snapToLines(e) { - this._snapToLines = !!e, this.hasBeenReset = !0 - } - get line() { - return this._line - } - set line(e) { - if (!np.isValidLineAndPositionSetting(e)) throw new SyntaxError(`An invalid number or illegal string was specified for line: ${e}`); - this._line = e, this.hasBeenReset = !0 - } - get lineAlign() { - return this._lineAlign - } - set lineAlign(e) { - if (!np.isValidLineAlignSetting(e)) throw new SyntaxError(`An invalid or illegal string was specified for lineAlign: ${e}`); - this._lineAlign = e, this.hasBeenReset = !0 - } - get position() { - return this._position - } - set position(e) { - if (!np.isValidLineAndPositionSetting(e)) throw new Error(`Position must be between 0 and 100 or auto: ${e}`); - this._position = e, this.hasBeenReset = !0 - } - get positionAlign() { - return this._positionAlign - } - set positionAlign(e) { - if (!np.isValidPositionAlignSetting(e)) throw new SyntaxError(`An invalid or illegal string was specified for positionAlign: ${e}`); - this._positionAlign = e, this.hasBeenReset = !0 - } - get size() { - return this._size - } - set size(e) { - if (e < 0 || 100 < e) throw new Error(`Size must be between 0 and 100: ${e}`); - this._size = e, this.hasBeenReset = !0 - } - get align() { - return this._align - } - set align(e) { - if (!np.isValidAlignSetting(e)) throw new SyntaxError(`An invalid or illegal string was specified for align: ${e}`); - this._align = e, this.hasBeenReset = !0 - } - getCueAsHTML() { - return sp.default.parseContent(window, this, {}) - } - static create(t) { - if (!t.hasOwnProperty("startTime") || !t.hasOwnProperty("endTime") || !t.hasOwnProperty("text")) throw new Error("You must at least have start time, end time, and text."); - const i = new this(t.startTime, t.endTime, t.text); - return Object.keys(t).forEach(e => { - i.hasOwnProperty(e) && (i[e] = t[e]) - }), i - } - static fromJSON(e) { - return this.create(JSON.parse(e)) - } - toJSON() { - const t = {}; - return Object.keys(this).forEach(e => { - this.hasOwnProperty(e) && "getCueAsHTML" !== e && "hasBeenReset" !== e && "displayState" !== e && (t[e] = this[e]) - }), t - } - }); - nu.VTTCue = w; - w = {}, e = e && e.__decorate || function(e, t, i, r) { - var n, s = arguments.length, - a = s < 3 ? t : null === r ? r = Object.getOwnPropertyDescriptor(t, i) : r; - if ("object" == typeof Reflect && "function" == typeof Reflect.decorate) a = Reflect.decorate(e, t, i, r); - else - for (var o = e.length - 1; 0 <= o; o--)(n = e[o]) && (a = (s < 3 ? n(a) : 3 < s ? n(t, i, a) : n(t, i)) || a); - return 3 < s && a && Object.defineProperty(t, i, a), a - }; - Object.defineProperty(w, "__esModule", { - value: !0 - }); - const ap = t; - e = e([function(e) { - let t = e; - return "undefined" != typeof window && null != window.VTTRegion && (t = window.VTTRegion, t.create = e.create, t.fromJSON = e.fromJSON, t.prototype.toJSON = e.prototype.toJSON), t - }], e = class { - constructor() { - this._id = "", this._lines = 3, this._regionAnchorX = 0, this._regionAnchorY = 100, this._scroll = "", this._viewportAnchorX = 0, this._viewportAnchorY = 100, this._width = 100 - } - get id() { - return this._id - } - set id(e) { - if ("string" != typeof e) throw new Error("ID must be a string."); - this._id = e - } - get lines() { - return this._lines - } - set lines(e) { - if ("number" != typeof e) throw new TypeError("Lines must be set to a number."); - this._lines = e - } - get regionAnchorX() { - return this._regionAnchorX - } - set regionAnchorX(e) { - if (!ap.isValidPercentValue(e)) throw new TypeError("RegionAnchorX must be between 0 and 100."); - this._regionAnchorX = e - } - get regionAnchorY() { - return this._regionAnchorY - } - set regionAnchorY(e) { - if (!ap.isValidPercentValue(e)) throw new TypeError("RegionAnchorY must be between 0 and 100."); - this._regionAnchorY = e - } - get scroll() { - return this._scroll - } - set scroll(e) { - if ("string" == typeof e) { - e = e.toLowerCase(); - if (ap.isValidScrollSetting(e)) return void(this._scroll = e) - } - throw new SyntaxError("An invalid or illegal string was specified.") - } - get viewportAnchorX() { - return this._viewportAnchorX - } - set viewportAnchorX(e) { - if (!ap.isValidPercentValue(e)) throw new TypeError("ViewportAnchorX must be between 0 and 100."); - this._viewportAnchorX = e - } - get viewportAnchorY() { - return this._viewportAnchorY - } - set viewportAnchorY(e) { - if (!ap.isValidPercentValue(e)) throw new TypeError("ViewportAnchorY must be between 0 and 100."); - this._viewportAnchorY = e - } - get width() { - return this._width - } - set width(e) { - if (!ap.isValidPercentValue(e)) throw new TypeError("Width must be between 0 and 100."); - this._lines = e - } - toJSON() { - const t = {}; - return Object.keys(this).forEach(e => { - this.hasOwnProperty(e) && (t[e] = this[e]) - }), t - } - static create(t) { - const i = new this; - return Object.keys(t).forEach(e => { - i.hasOwnProperty(e) && (i[e] = t[e]) - }), i - } - static fromJSON(e) { - return this.create(JSON.parse(e)) - } - }); - w.VTTRegion = e, Object.defineProperty(iu, "__esModule", { - value: !0 - }); - const op = nu; - iu.VTTCue = op.VTTCue; - const lp = w; - iu.VTTRegion = lp.VTTRegion; - const dp = ru; - class up extends Error { - constructor(e, t) { - super(), this.name = "ParsingError", this.code = "number" == typeof e ? e : e.code, t ? this.message = t : e instanceof up && (this.message = e.message) - } - }(iu.ParsingError = up).Errors = { - BadSignature: new up(0, "Malformed WebVTT signature."), - BadTimeStamp: new up(1, "Malformed time stamp.") - }; - class cp { - constructor() { - this.values = {} - } - set(e, t) { - this.get(e) || "" === t || (this.values[e] = t) - } - get(e, t, i) { - return "object" == typeof t && "string" == typeof i ? this.has(e) ? this.values[e] : t[i] : this.has(e) ? this.values[e] : t - } - has(e) { - return e in this.values - } - alt(t, i, r) { - for (let e = 0; e < r.length; ++e) - if (i === r[e]) { - this.set(t, i); - break - } - } - integer(e, t) { - /^-?\d+$/.test(t) && this.set(e, parseInt(t, 10)) - } - percent(e, t) { - if (t.match(/^([\d]{1,3})(\.[\d]*)?%$/)) try { - var i = parseFloat(t); - if (0 <= i && i <= 100) return this.set(e, i), !0 - } catch (e) { - return !1 - } - return !1 - } + for (const key in err) { + if (obj[key] === undefined) { + obj[key] = err[key]; + } } - class hp { - constructor(e, t, i) { - this.window = e, this.state = "INITIAL", this.styleCollector = "", this.buffer = "", this.decoder = t || new TextDecoder("utf8"), this.regionList = [], this.onStylesParsedCallback = i, this._styles = {} - } - static StringDecoder() { - return { - decode: e => { - if (!e) return ""; - if ("string" != typeof e) throw new Error("Error - expected string data."); - return decodeURIComponent(encodeURIComponent(e)) - } - } - } - reportOrThrowError(e) { - if (!(e instanceof up && "function" == typeof this.onparsingerror)) throw e; - this.onparsingerror(e) - } - parseOptions(e, t, i, r) { - e = r ? e.split(r) : [e]; - for (const n of e) - if ("string" == typeof n) { - const r = n.split(i); - 2 === r.length && t(r[0], r[1]) - } - } - parseCue(t, e, a) { - const i = t, - r = () => { - var e = dp.default.parseTimeStamp(t); - if (null === e) throw new up(up.Errors.BadTimeStamp, "Malformed timestamp: " + i); - return t = t.replace(/^[^\sa-zA-Z-]+/, ""), e - }, - n = () => { - t = t.replace(/^\s+/, "") - }; - if (n(), e.startTime = r(), n(), "--\x3e" !== t.substr(0, 3)) throw new up(up.Errors.BadTimeStamp, `Malformed time stamp (time stamps must be separated by '--\x3e'): ${i}`); - t = t.substr(3), n(), e.endTime = r(), n(), ((e, t) => { - const s = new cp; - this.parseOptions(e, (t, i) => { - let e, r; - switch (t) { - case "region": - for (let e = a.length - 1; 0 <= e; e--) - if (a[e].id === i) { - s.set(t, a[e].region); - break - } break; - case "vertical": - s.alt(t, i, ["rl", "lr"]); - break; - case "line": - e = i.split(","), r = e[0], s.integer(t, r), s.percent(t, r) && s.set("snapToLines", !1), s.alt(t, r, ["auto"]), 2 === e.length && s.alt("lineAlign", e[1], ["start", "center", "end"]); - break; - case "position": - e = i.split(","), s.percent(t, e[0]), 2 === e.length && (n = ["line-left", "line-right", "center", "auto", "left", "start", "middle", "end", "right"], s.alt("positionAlign", e[1], n)); - break; - case "size": - s.percent(t, i); - break; - case "align": - var n = ["start", "center", "end", "left", "right", "middle"]; - s.alt(t, i, n) - } - }, /:/, /\s/), t.region = s.get("region", null), t.vertical = s.get("vertical", ""), t.line = s.get("line", void 0 === t.line ? "auto" : t.line), t.lineAlign = s.get("lineAlign", "start"), t.snapToLines = s.get("snapToLines", !0), t.size = s.get("size", 100); - e = s.get("align", "center"); - t.align = "middle" === e ? "center" : e, t.position = s.get("position", "auto"); - e = s.get("positionAlign", { - start: "start", - left: "start", - center: "center", - right: "end", - end: "end" - }, t.align); - t.positionAlign = { - start: "start", - "line-left": "start", - left: "start", - center: "center", - middle: "center", - "line-right": "end", - right: "end", - end: "end" - } [e] - })(t, e) - } - parseRegion(e) { - const n = new cp; - if (this.parseOptions(e, (e, t) => { - switch (e) { - case "id": - n.set(e, t); - break; - case "width": - n.percent(e, t); - break; - case "lines": - n.integer(e, t); - break; - case "regionanchor": - case "viewportanchor": { - var i = t.split(","); - if (2 !== i.length) break; - const r = new cp; - if (r.percent("x", i[0]), r.percent("y", i[1]), !r.has("x") || !r.has("y")) break; - n.set(e + "X", r.get("x")), n.set(e + "Y", r.get("y")); - break - } - case "scroll": - n.alt(e, t, ["up"]) - } - }, /=/, /\s/), n.has("id")) { - const e = new lp.VTTRegion; - e.width = n.get("width", 100), e.lines = n.get("lines", 3), e.regionAnchorX = n.get("regionanchorX", 0), e.regionAnchorY = n.get("regionanchorY", 100), e.viewportAnchorX = n.get("viewportanchorX", 0), e.viewportAnchorY = n.get("viewportanchorY", 100), e.scroll = n.get("scroll", ""), this.onregion && this.onregion(e), this.regionList.push({ - id: n.get("id"), - region: e - }) - } - } - parseStyle(i) { - const e = i.split("}"); - e.pop(); - for (const i of e) { - let e = null, - t = null; - const r = i.split("{"); - r[0] && (e = r[0].trim()), r[1] && (t = (e => { - const t = {}, - i = e.split(";"); - for (let e = 0; e < i.length; e++) - if (i[e].includes(":")) { - const r = i[e].split(":", 2), - n = r[0].trim(), - s = r[1].trim(); - "" !== n && "" !== s && (t[n] = s) - } return t - })(r[1])), e && t && (this._styles[e] = t) - } - this.onStylesParsedCallback && this.onStylesParsedCallback(this._styles) - } - parseHeader(e) { - this.parseOptions(e, function(e, t) { - "Region" === e && this.parseRegion(t) - }, /:/) - } - parse(i) { - i && (this.buffer += this.decoder.decode(i, { - stream: !0 - })); - const r = () => { - const e = this.buffer; - let t = 0; - let i = { - start: e.length, - length: 0 - }; - for (; t < e.length;) { - const r = ((t, i) => { - const r = { - start: -1, - length: -1 - }; - if ("\r" === t[i]) r.start = i, r.length = 1; - else if ("\n" === t[i]) r.start = i, r.length = 1; - else if ("<" === t[i] && i + 1 < t.length && "b" === t[i + 1] && i + 2 < t.length && "r" === t[i + 2]) { - let e = i + 2; - for (; e < t.length && ">" !== t[e++];); - r.start = i, r.length = e - i - } - return r - })(e, t); - if (0 < r.length) { - i = r; - break - }++t - } - const r = e.substr(0, i.start); - return this.buffer = e.substr(i.start + i.length), r - }; - try { - let e; - if ("INITIAL" === this.state) { - if (!/\r\n|\n/.test(this.buffer)) return this; - e = r(); - var n = /^()?WEBVTT([ \t].*)?$/.exec(e); - if (!n || !n[0]) throw new up(up.Errors.BadSignature); - this.state = "HEADER" - } - let t = !1; - for (; this.buffer;) { - if (!/\r\n|\n/.test(this.buffer)) return this; - switch (t ? t = !1 : e = r(), this.state) { - case "HEADER": - e.includes(":") ? this.parseHeader(e) : e || (this.state = "ID"); - continue; - case "NOTE": - e || (this.state = "ID"); - continue; - case "STYLE": - e ? this.styleCollector += e : (this.parseStyle(this.styleCollector), this.state = "ID", this.styleCollector = ""); - continue; - case "ID": - if (/^NOTE($|[ \t])/.test(e)) { - this.state = "NOTE"; - break - } - if (/^STYLE($|[ \t])/.test(e)) { - this.state = "STYLE"; - break - } - if (!e) continue; - if (this.cue = new op.VTTCue(0, 0, ""), this.state = "CUE", !e.includes("--\x3e")) { - this.cue.id = e; - continue - } - case "CUE": - try { - this.parseCue(e, this.cue, this.regionList) - } catch (i) { - this.reportOrThrowError(i), this.cue = null, this.state = "BADCUE"; - continue - } - this.state = "CUETEXT"; - continue; - case "CUETEXT": { - const r = e.includes("--\x3e"); - if (!e || r) { - t = !0, this.oncue && this.oncue(this.cue), this.cue = null, this.state = "ID"; - continue - } - this.cue.text && (this.cue.text += "\n"), this.cue.text += e; - continue - } - case "BADCUE": - e || (this.state = "ID"); - continue - } - } - } catch (i) { - this.reportOrThrowError(i), "CUETEXT" === this.state && this.cue && this.oncue && this.oncue(this.cue), this.cue = null, this.state = "INITIAL" === this.state ? "BADWEBVTT" : "BADCUE" - } - return this - } - flush() { - try { - if (this.buffer += this.decoder.decode(), !this.cue && "HEADER" !== this.state || (this.buffer += "\n\n", this.parse()), "INITIAL" === this.state) throw new up(up.Errors.BadSignature) - } catch (e) { - this.reportOrThrowError(e) - } - return this.onflush && this.onflush(), this - } - styles() { - return this._styles - } + return obj + } + + function getTimeFunction (opts) { + if (typeof opts.timestamp === 'function') { + return opts.timestamp } - iu.default = hp, iu.WebVTTParser = hp; - var pp, w = {}; - Object.defineProperty(w, "__esModule", { - value: !0 - }); - const fp = nu; - w.VTTCue = fp.VTTCue; - const mp = ru, - gp = [/^(::cue\()(\..*)(\))/, /^(::cue\()(#.*)(\))/, /^(::cue\()(c|i|b|u|ruby|rt|v|lang)(\))/], - yp = [ - [1470, 1470], - [1472, 1472], - [1475, 1475], - [1478, 1478], - [1488, 1514], - [1520, 1524], - [1544, 1544], - [1547, 1547], - [1549, 1549], - [1563, 1563], - [1566, 1610], - [1645, 1647], - [1649, 1749], - [1765, 1766], - [1774, 1775], - [1786, 1805], - [1807, 1808], - [1810, 1839], - [1869, 1957], - [1969, 1969], - [1984, 2026], - [2036, 2037], - [2042, 2042], - [2048, 2069], - [2074, 2074], - [2084, 2084], - [2088, 2088], - [2096, 2110], - [2112, 2136], - [2142, 2142], - [2208, 2208], - [2210, 2220], - [8207, 8207], - [64285, 64285], - [64287, 64296], - [64298, 64310], - [64312, 64316], - [64318, 64318], - [64320, 64321], - [64323, 64324], - [64326, 64449], - [64467, 64829], - [64848, 64911], - [64914, 64967], - [65008, 65020], - [65136, 65140], - [65142, 65276], - [67584, 67589], - [67592, 67592], - [67594, 67637], - [67639, 67640], - [67644, 67644], - [67647, 67669], - [67671, 67679], - [67840, 67867], - [67872, 67897], - [67903, 67903], - [67968, 68023], - [68030, 68031], - [68096, 68096], - [68112, 68115], - [68117, 68119], - [68121, 68147], - [68160, 68167], - [68176, 68184], - [68192, 68223], - [68352, 68405], - [68416, 68437], - [68440, 68466], - [68472, 68479], - [68608, 68680], - [126464, 126467], - [126469, 126495], - [126497, 126498], - [126500, 126500], - [126503, 126503], - [126505, 126514], - [126516, 126519], - [126521, 126521], - [126523, 126523], - [126530, 126530], - [126535, 126535], - [126537, 126537], - [126539, 126539], - [126541, 126543], - [126545, 126546], - [126548, 126548], - [126551, 126551], - [126553, 126553], - [126555, 126555], - [126557, 126557], - [126559, 126559], - [126561, 126562], - [126564, 126564], - [126567, 126570], - [126572, 126578], - [126580, 126583], - [126585, 126588], - [126590, 126590], - [126592, 126601], - [126603, 126619], - [126625, 126627], - [126629, 126633], - [126635, 126651], - [1114109, 1114109] - ]; - class vp { - applyStyles(e, t) { - t = t || this.div; - for (const i in e) e.hasOwnProperty(i) && (t.style[i] = e[i]) - } - formatStyle(e, t) { - return 0 === e ? "0" : e + t - } + if (opts.timestamp === false) { + return nullTime } - w.StyleBox = vp; - class Sp extends vp { - constructor(e, t, i, r, n) { - super(); - let s = { - textAlign: { - start: "left", - "line-left": "left", - left: "left", - center: "center", - middle: "center", - "line-right": "right", - right: "right", - end: "right" - } [(this.cue = t).positionAlign] || t.align, - whiteSpace: "pre-line", - position: "absolute" - }; - s.direction = this.determineBidi(this.cueDiv), s.writingMode = this.directionSettingToWritingMode(t.vertical), s.unicodeBidi = "plaintext", this.div = e.document.createElement("div"), this.applyStyles(s), s = { - backgroundColor: r.backgroundColor, - display: "inline-block" - }, this.parseOpacity(s.backgroundColor) && (s.padding = "5px", s.borderRadius = "5px"), this.backgroundDiv = e.document.createElement("div"), this.applyStyles(s, this.backgroundDiv), s = { - color: i.color, - backgroundColor: i.backgroundColor, - textShadow: i.textShadow, - fontSize: i.fontSize, - fontFamily: i.fontFamily, - position: "relative", - left: "0", - right: "0", - top: "0", - bottom: "0", - display: "inline-block", - textOrientation: "upright" - }, s.writingMode = this.directionSettingToWritingMode(t.vertical), s.unicodeBidi = "plaintext", this.cueDiv = mp.default.parseContent(e, t, n), this.applyStyles(s, this.cueDiv), this.backgroundDiv.appendChild(this.cueDiv), this.div.appendChild(this.backgroundDiv); - let a = 0; - if ("number" == typeof t.position) { - n = t.positionAlign || t.align; - if (n) switch (n) { - case "start": - case "left": - a = t.position; - break; - case "center": - case "middle": - a = t.position - t.size / 2; - break; - case "end": - case "right": - a = t.position - t.size - } - } - "" === t.vertical ? this.applyStyles({ - left: this.formatStyle(a, "%"), - width: this.formatStyle(t.size, "%") - }) : this.applyStyles({ - top: this.formatStyle(a, "%"), - height: this.formatStyle(t.size, "%") - }) - } - determineBidi(e) { - let t = [], - i = ""; - if (!e || !e.childNodes) return "ltr"; - - function n(t, i) { - for (let e = i.childNodes.length - 1; 0 <= e; e--) t.push(i.childNodes[e]) - } - for (n(t, e); i = function e(t) { - if (!t || !t.length) return null; - let i = t.pop(), - r = i.textContent || i.innerText; - if (r) { - const i = /^.*(\n|\r)/.exec(r); - return i ? i[t.length = 0] : r - } - return "ruby" === i.tagName ? e(t) : i.childNodes ? (n(t, i), e(t)) : void 0 - }(t);) - for (let e = 0; e < i.length; e++) - if (function(e, t) { - for (const i of t) - if (e >= i[0] && e <= i[1]) return 1 - }(i.charCodeAt(e), yp)) return "rtl"; - return "ltr" - } - parseOpacity(e) { - if (!e || "string" != typeof e) return null; - e = (e = e.replace(/ /g, "").replace("rgba(", "").replace(")", "")).split(","); - return e && 4 <= e.length ? e[3] : null - } - directionSettingToWritingMode(e) { - return "" === e ? "horizontal-tb" : "lr" === e ? "vertical-lr" : "vertical-rl" - } - move(e) { - this.applyStyles({ - top: this.formatStyle(e.top, "px"), - bottom: this.formatStyle(e.bottom, "px"), - left: this.formatStyle(e.left, "px"), - right: this.formatStyle(e.right, "px"), - height: this.formatStyle(e.height, "px"), - width: this.formatStyle(e.width, "px") - }) - } - } - w.CueStyleBox = Sp; - class bp { - constructor(e) { - var t; - let i, r, n, s, a, o; - if (e instanceof Sp && e.cue ? (t = e.cue) && "" !== t.vertical ? this.property = "width" : this.property = "height" : e instanceof bp && (this.property = e.property || "height"), e instanceof Sp && e.div) { - n = e.div.offsetHeight, s = e.div.offsetWidth, a = e.div.offsetTop; - const t = e.div.firstChild; - if (o = (t || e.div).getBoundingClientRect(), i = o && o[this.property] || null, t && t.firstChild) { - const e = t.firstChild; - e && "string" == typeof e.textContent && (r = i / this.calculateNewLines(e.textContent)) - } - } else e instanceof bp && (o = e); - this.left = o.left, this.right = o.right, this.top = o.top || a, this.height = o.height || n, this.bottom = o.bottom || a + (o.height || n), this.width = o.width || s, this.lineHeight = null !== i ? i : o.lineHeight, this.singleLineHeight = null !== r ? r : o.singleLineHeight, this.singleLineHeight || (this.singleLineHeight = 41) - } - calculateNewLines(t) { - let i = 1; - for (let e = 0; e < t.length; e++) "\n" === t[e] && i++; - return i - } - move(e, t) { - switch (t = void 0 !== t ? t : this.singleLineHeight, e) { - case "+x": - this.left += t, this.right += t; - break; - case "-x": - this.left -= t, this.right -= t; - break; - case "+y": - this.top += t, this.bottom += t; - break; - case "-y": - this.top -= t, this.bottom -= t - } - } - overlaps(e) { - return this.left < e.right && this.right > e.left && this.top < e.bottom && this.bottom > e.top - } - overlapsAny(e) { - for (const t of e) - if (this.overlaps(t)) return !0; - return !1 - } - within(e) { - return this.top >= e.top && this.bottom <= e.bottom && this.left >= e.left && this.right <= e.right - } - moveIfOutOfBounds(e, t) { - switch (t) { - case "+x": - this.left < e.left && (this.left = e.left, this.right = this.left + this.width); - break; - case "-x": - this.right > e.right && (this.right = e.right, this.left = this.right - this.width); - break; - case "+y": - this.top < e.top && (this.top = e.top, this.bottom = this.top + this.height); - break; - case "-y": - this.bottom > e.bottom && (this.bottom = e.bottom, this.top = this.bottom - this.height) - } - } - toCSSCompatValues(e) { - return { - top: this.top - e.top, - bottom: e.bottom - this.bottom, - left: this.left - e.left, - right: e.right - this.right, - height: this.height, - width: this.width - } - } - static getSimpleBoxPosition(e) { - let t = null; - e instanceof vp && e.div ? t = e.div : e instanceof HTMLElement && (t = e); - let i = t.offsetHeight || 0, - r = t.offsetWidth || 0, - n = t.offsetTop || 0, - s = n + i, - a = t.getBoundingClientRect(); - var { - left: o, - right: e - } = a; - return a.top && (n = a.top), a.height && (i = a.height), a.width && (r = a.width), a.bottom && (s = a.bottom), { - left: o, - right: e, - top: n, - height: i, - bottom: s, - width: r - } - } - static getBoxPosition(r, n) { - if (r && 0 < r.length) { - let t = 0, - i = r[0][n]; - for (let e = 0; e < r.length; e++) n in ["top", "right"] ? r[e][n] > i && (t = e, i = r[e][n]) : n in ["bottom", "left"] && r[e][n] < i && (t = e, i = r[e][n]); - return r[t] - } - return null - } - static moveToMinimumDistancePlacement(e, t, i) { - "height" === e.property ? "+y" === t ? (e.top = i.topMostBoxPosition.bottom + 0, e.bottom = e.top + e.height) : "-y" === t && (e.bottom = +i.bottomMostBoxPosition.top, e.top = e.bottom - e.height) : "width" === e.property && ("+x" === t ? (e.left = i.rightMostBoxPosition.right + 0, e.right = e.left + e.width) : "-x" === t && (e.right = +i.leftMostBoxPosition.left, e.left = e.right - e.width)) - } - static moveBoxToLinePosition(e, a, o) { - var n = e.cue; - let i, r = new bp(e), - s = function() { - if ("number" == typeof n.line && (n.snapToLines || 0 <= n.line && n.line <= 100)) return n.line; - if (!n.track || !n.track.textTrackList || !n.track.textTrackList.mediaElement) return -1; - let t = 0; - var i = n.track, - r = i.textTrackList; - for (let e = 0; e < r.length && r[e] !== i; e++) "showing" === r[e].mode && t++; - return -1 * ++t - }(), - l = []; - if (n.snapToLines) { - let t = 0; - switch (n.vertical) { - case "": - l = ["+y", "-y"], i = "height"; - break; - case "rl": - l = ["+x", "-x"], i = "width"; - break; - case "lr": - l = ["-x", "+x"], i = "width" - } - const o = r.lineHeight, - d = a[i] + o, - u = l[0]; - if (s < 0) { - let e = 0; - switch (n.vertical) { - case "": - e = a.height - o - .05 * a.height; - break; - case "rl": - case "lr": - e = -a.width + o + .05 * a.width - } - t = e, l = l.reverse() - } else { - switch (n.vertical) { - case "": - t = o * Math.round(s); - break; - case "rl": - t = a.width - o * Math.round(s); - break; - case "lr": - t = o * Math.round(s) - } - Math.abs(t) > d && (t = t < 0 ? -1 : 1, t *= Math.ceil(d / o) * o) - } - r.move(u, t) - } else { - const o = "" === n.vertical ? a.height : a.width, - i = r.lineHeight / o * 100; - switch (n.lineAlign) { - case "center": - s -= i / 2; - break; - case "end": - s -= i - } - switch (n.vertical) { - case "": - e.applyStyles({ - top: e.formatStyle(s, "%") - }); - break; - case "rl": - e.applyStyles({ - right: e.formatStyle(s, "%") - }); - break; - case "lr": - e.applyStyles({ - left: e.formatStyle(s, "%") - }) - } - l = ["+y", "-y", "+x", "-x"], "+y" === n.axis ? l = ["+y", "-y", "+x", "-x"] : "-y" === n.axis && (l = ["-y", "+y", "+x", "-x"]), r = new bp(e) - } - const d = function(r, n) { - let s; - for (let i = 0; i < n.length; i++) { - r.moveIfOutOfBounds(a, n[i]); - let e = 0, - t = !1; - for (; r.overlapsAny(o) && !(9 < e);) t ? r.move(n[i]) : (o && 0 < o.length && (s = s || { - topMostBoxPosition: bp.getBoxPosition(o, "top"), - bottomMostBoxPosition: bp.getBoxPosition(o, "bottom"), - leftMostBoxPosition: bp.getBoxPosition(o, "left"), - rightMostBoxPosition: bp.getBoxPosition(o, "right") - }, bp.moveToMinimumDistancePlacement(r, n[i], s)), t = !0), e++ - } - return r - }(r, l); - e.move(d.toCSSCompatValues(a)) - } - } - w.BoxPosition = bp; - class Tp { - constructor(e, t, i = !0) { - if (!e) return null; - this.window = e, this.overlay = t, this.loggingEnabled = i, this.foregroundStyleOptions = { - fontFamily: "Helvetica", - fontSize: "36px", - color: "rgba(255, 255, 255, 1)", - textShadow: "", - backgroundColor: "rgba(0, 0, 0, 0)" - }, this.backgroundStyleOptions = { - backgroundColor: "rgba(0, 0, 0, 0.5)" - }, this.globalStyleCollection = {}; - const r = e.document.createElement("div"); - r.style.position = "absolute", r.style.left = "0", r.style.right = "0", r.style.top = "0", r.style.bottom = "0", r.style.margin = "1.5%", this.paddedOverlay = r, t.appendChild(this.paddedOverlay), this.initSubtitleCSS() - } - initSubtitleCSS() { - var e = [new fp.VTTCue(0, 0, "String to init CSS - Won't be visible to user")]; - this.paddedOverlay.style.opacity = "0", this.processCues(e), this.processCues([]), this.paddedOverlay.style.opacity = "1" - } - convertCueToDOMTree(e) { - return e ? mp.default.parseContent(this.window, e, this.globalStyleCollection) : null - } - setStyles(i) { - function r(e, t, i) { - for (const r in t) t.hasOwnProperty(r) && (!0 === i && void 0 !== e[r] || !1 === i) && (e[r] = t[r]) - } - for (const a in i) { - let t = !1, - e = null; - "::cue" === a ? (e = this.foregroundStyleOptions, t = !0) : "::-webkit-media-text-track-display" === a && (e = this.backgroundStyleOptions, t = !0); - var n = i[a]; - if (!0 === t) r(e, n, t); - else - for (let e = 0; e < gp.length; e++) { - var s = gp[e].exec(a); - if (s && 4 === s.length) { - const i = s[2], - o = {}; - r(o, n, t), this.globalStyleCollection[i] = o - } - } - } - this.initSubtitleCSS(), this.loggingEnabled && (console.log("WebVTTRenderer setStyles foregroundStyleOptions: " + JSON.stringify(this.foregroundStyleOptions)), console.log("WebVTTRenderer setStyles backgroundStyleOptions: " + JSON.stringify(this.backgroundStyleOptions)), console.log("WebVTTRenderer setStyles globalStyleCollection: " + JSON.stringify(this.globalStyleCollection))) - } - processCues(r) { - if (r) { - for (; this.paddedOverlay.firstChild;) this.paddedOverlay.removeChild(this.paddedOverlay.firstChild); - if (function(t) { - for (let e = 0; e < t.length; e++) - if (t[e].hasBeenReset || !t[e].displayState) return 1 - }(r)) { - const n = [], - s = bp.getSimpleBoxPosition(this.paddedOverlay); - 1 < r.length && (r = function(t) { - const i = []; - let r = 0; - for (let e = 0; e < t.length; e++) { - var n = t[e]; - if ("number" != typeof n.line) return t; - r += n.line, i.push(n) - } - return r /= t.length, 50 < r ? (i.forEach(function(e) { - e.axis = "-y" - }), i.sort((e, t) => t.line - e.line)) : (i.forEach(function(e) { - e.axis = "+y" - }), i.sort((e, t) => e.line - t.line)), i - }(r)); - for (let i = 0; i < r.length; i++) { - let e = r[i], - t = new Sp(this.window, e, this.foregroundStyleOptions, this.backgroundStyleOptions, this.globalStyleCollection); - this.paddedOverlay.appendChild(t.div), bp.moveBoxToLinePosition(t, s, n), e.displayState = t.div, n.push(bp.getSimpleBoxPosition(t)) - } - } else - for (let e = 0; e < r.length; e++) this.paddedOverlay.appendChild(r[e].displayState) - } - } - setSize(e, t) { - e && (this.overlay.style.width = e + "px"), t && (this.overlay.style.height = t + "px") - } - getOverlay() { - return this.overlay - } - } - - function Ep(e) { - for (var t in e) pp.hasOwnProperty(t) || (pp[t] = e[t]) - } - w.default = Tp, w.WebVTTRenderer = Tp, pp = Yh, Object.defineProperty(pp, "__esModule", { - value: !0 - }), Ep(iu), Ep(w); - - function Ip(e, t, i) { - return e.substr(i || 0, t.length) === t - } - - function wp(e) { - let t = 5381, - i = e.length; - for (; i;) t = 33 * t ^ e.charCodeAt(--i); - return (t >>> 0).toString() - } - - function Ap(e) { - var t = Math.floor(e), - i = t + .5, - r = t + 1; - return i <= e ? r - e <= e - i ? r : i : i - e <= e - t ? i : t - } - - function Op(e, t = 0, i = 8589934592) { - if (!Number.isFinite(t)) return e; - var r = i / 2, - n = Math.abs(e - t) % i; - return t + (t < e ? -1 : 1) * (r < n ? i - n : -n) - } - var kp, Cp, Dp, Mp = function(e, t, i, r, n, s, a, o) { - const l = O.utf8arrayToStr(new Uint8Array(e)).trim().replace(/\r\n|\n\r|\n|\r/g, "\n").split("\n"), - d = { - baseTime: Math.floor(9e4 * t.baseTime / t.timescale), - timescale: 9e4 - }; - let u = 0, - c = 0; - const h = []; - let p = null, - f = !0; - const m = new Yh.WebVTTParser(window, Yh.WebVTTParser.StringDecoder(), a); - m.oncue = function(e) { - var t = S({ - baseTime: Op(Op(u) - d.baseTime, 9e4 * i), - timescale: 9e4 - }); - e.startTime = Op(e.startTime + t - c, 0, 95443.7176888889), e.endTime = Op(e.endTime + t - c, 0, 95443.7176888889), e.id = wp(Ap(e.startTime).toString()) + wp(Ap(e.endTime - e.startTime).toString()) + wp(e.text), e.text = decodeURIComponent(encodeURIComponent(e.text)), 0 < e.endTime && h.push(e) - }, m.onparsingerror = function(e) { - p = e - }, m.onflush = function() { - p && s ? s(p) : n(h) - }, l.forEach(a => f && Ip(a, "X-TIMESTAMP-MAP=") ? (f = !1, void a.substr(16).split(",").forEach(e => { - if (Ip(e, "LOCAL:")) { - let t; - try { - t = (i = e.substr(6), r = parseInt(i.substr(-3)), n = parseInt(i.substr(-6, 2)), s = parseInt(i.substr(-9, 2)), i = 9 < i.length ? parseInt(i.substr(0, i.indexOf(":"))) : 0, ne(r) && ne(n) && ne(s) && ne(i) ? (r += 1e3 * n, r += 6e4 * s, r += 36e5 * i) : -1) - } catch (e) { - t = -1 - } - 1 !== t ? c = t / 1e3 : p = new Error(`Malformed X-TIMESTAMP-MAP: ${a}`) - } else Ip(e, "MPEGTS:") && (u = parseInt(e.substr(7))); - var i, r, n, s - })) : void m.parse(a + "\n")), m.flush() + return epochTime + } + + function mock () { return {} } + function passthrough (a) { return a } + function noop$2 () {} + + function nullTime () { return false } + function epochTime () { return Date.now() } + function unixTime () { return Math.round(Date.now() / 1000.0) } + function isoTime () { return new Date(Date.now()).toISOString() } // using Date.now() for testability + + /* eslint-disable */ + /* istanbul ignore next */ + function pfGlobalThisOrFallback () { + function defd (o) { return typeof o !== 'undefined' && o } + try { + if (typeof globalThis !== 'undefined') return globalThis + Object.defineProperty(Object.prototype, 'globalThis', { + get: function () { + delete Object.prototype.globalThis; + return (this.globalThis = this) }, - xp = { - newCue: function(e, t, i, r, n) { - let s, a, o, l, d; - var u, c, h = { - foreground: !1, - background: !1, - italics: !1, - underline: !1, - flash: !1, - styleStack: [] - }; - for ([u, c] of r.rows.entries()) - if (a = !0, o = 0, l = "", !c.isEmpty()) { - for (let e = 0; e < c.chars.length; e++) c.chars[e].uchar.match(/\s/) && a ? o++ : (l += this.getFormattedChar(c.chars[e], h), a = !1); - (c.cueStartTime = t) === i && (i += 1e-4), l = l.trim().replace(//gi, "\n"), l += this.closeStyles(h), s = new Yh.VTTCue(t, i, l), 16 <= o ? o-- : o++, d = !navigator.userAgent.match(/Firefox\//) && 7 < u ? u : u + 1, s.snapToLines = !1, s.line = 10 + 5.33 * d, s.align = "left", s.position = this.getPosition(o, n), e.addCue(s) - } - }, - getPosition: function(e, t) { - let i = 1.3333333333333333; - t && t.offsetWidth && t.offsetHeight && 1.6 <= t.offsetWidth / t.offsetHeight && (i = 1.7777777777777777); - let r = 10 + e / 32 * 80, - n = 10, - s = 90; - return 1.7777777777777777 === i && (r = 12.5 + .75 * r, n = 20, s = 80), Math.max(n, Math.min(s, r + (navigator.userAgent.match(/Firefox\//) ? 50 : 0))) - }, - getRootStyleTag: function(e) { - var t = e[0]; - return "c" === t ? t : e - }, - closeStyles: function(t) { - let i = ""; - for (let e = t.styleStack.length - 1; 0 <= e; --e) i += "", t.styleStack.pop(); - return i - }, - beginStyleAndBalance: function(e, t) { - let i = ""; - return "c" === t[0] && (i += this.closeStyleAndBalance(e, "c")), i += "<" + t + ">", e.styleStack.push(t), i - }, - closeStyleAndBalance: function(t, i) { - var r = t.styleStack.length; - let n = 0, - s = ""; - for (let e = r - 1; 0 <= e; --e) { - var a = t.styleStack[e], - o = this.getRootStyleTag(a); - if (i[0] === a[0]) { - s += "", t.styleStack.splice(r - 1 - n); - break - } - "c" === o[0] ? (t.background = "", t.foreground = "", t.flash = !1, s += "") : "u" === o[0] ? (t.underline = !1, s += "") : "i" === o[0] && (t.italics = !1, s += ""), n++ - } - return s - }, - getFormattedChar: function(e, t) { - let i = "", - r = e.uchar, - n = ""; - var s = e.penState.foreground !== t.foreground, - a = e.penState.background !== t.background, - o = e.penState.flash !== t.flash; - return (s || a || o) && (n = "." + e.penState.foreground, n += ".bg_" + e.penState.background, e.penState.flash && o && (n += ".blink"), e.penState.foreground || e.penState.background || e.penState.blink ? i += this.beginStyleAndBalance(t, "c" + n) : i += this.closeStyleAndBalance(t, "c"), s && (t.foreground = e.penState.foreground), a && (t.background = e.penState.background), o && (t.flash = e.penState.flash)), e.penState.underline !== t.underline && (i += e.penState.underline ? this.beginStyleAndBalance(t, "u") : this.closeStyleAndBalance(t, "u"), t.underline = e.penState.underline), e.penState.italics !== t.italics && (i += e.penState.italics ? this.beginStyleAndBalance(t, "i") : this.closeStyleAndBalance(t, "i"), t.italics = e.penState.italics), i + r - } - }; - (w = kp = kp || {}).CloseEnough = "CloseEnough", w.TooFar = "TooFar", w.Unknown = "Unknown"; - const Pp = e => "cc1" === e || "cc2" === e; - - function Rp(t) { - const i = []; - for (let e = 0; e < t.length; e++) { - var r = t[e]; - ("captions" === r.kind || "subtitles" === r.kind || "metadata" === r.kind && r.customTextTrackCueRenderer) && i.push(r) - } - return i + configurable: true + }); + return globalThis + } catch (e) { + return defd(self) || defd(window) || defd(this) || {} } - - function Lp(e) { - if (e && e.cues) - for (; 0 < e.cues.length;) e.removeCue(e.cues[0]) - } - class _p extends $t { - constructor(e, t, i, r) { - super(e => { - const t = Oc(this.hls, this); - if (e.add(t.event(x.INLINE_STYLES_PARSED, this.onInlineStylesParsed).pipe(Vs(() => this.destroy())).subscribe()), e.add(bn(0, this.config.trottleCheckInterval).pipe(La(() => (this.checkReadyToLoadNextSubtitleFragment(), Wu))).subscribe()), this.config.nativeTextTrackChangeHandling) - if (this.mediaSink.textTracks && "onchange" in this.mediaSink.textTracks) { - const t = Oc(this.mediaSink.textTracks, this); - e.add(t.event("change", this._onTextTracksChanged).subscribe()) - } else e.add(bn(0, 500).pipe(La(() => (this._onTextTracksChanged(), Wu))).subscribe()) - }), this.config = t, this.hls = i, this.logger = r.child({ - name: "legible" - }), this.mediaSink = e, this.id3Track = e.id3TextTrack, this.enableCaption = !0, this.Cues = xp, this.tracks = [], this.cueRanges = [], this.channelToTrackMap = {}, this.htmlTextTrackMap = new Map, this.lastCueEndTime = 0, this.gotTracks = !1, this.tryAgain$ = new yi(!0), this.needNextSubtitle$ = new yi(!0) - } - destroy() { - Lp(this.textTrack1), Lp(this.textTrack2), this.mediaSink = void 0, this.nativeSubtitleTrackChange$ = void 0 - } - convertCuesIntoSubtitleFragInfo(t) { - const i = {}; - if (null != t && 0 < t.length) - for (let e = 0; e < t.length; e++) { - var r = t[e]; - if (ne(r.fragSN)) { - const n = i[r.fragSN]; - n ? (n.count++, n.startTime = Math.min(r.startTime, n.startTime), n.endTime = Math.max(r.endTime, n.endTime)) : i[r.fragSN] = { - count: 1, - startTime: r.startTime, - endTime: r.endTime - } - } - } - return i - } - checkReadyToLoadNextSubtitleFragment() { - let e = !1; - this.mediaSink.mediaQuery.currentTime >= this.lastCueEndTime - this.config.subtitleLeadTime && (e = !0), this.needNextSubtitle$.next(e) - } - checkReadyToLoadNextSubtitleFragment$(e, t) { - return e.mediaSeqNum === (null === (t = t[0]) || void 0 === t ? void 0 : t.mediaSeqNum) ? $i(!0) : (this.checkReadyToLoadNextSubtitleFragment(), this.needNextSubtitle$) - } - getNextFragment(e, t) { - t = t.mediaSeqNum + 1; - return t < e.fragments.length ? e.fragments[t - e.startSN] : null - } - calculateFragInfoMap(e, t, i, r) { - var n = this.convertCuesIntoSubtitleFragInfo(t); - let s = { - len: 0, - start: e, - end: e - }, - a = e, - o = e, - l = null, - d = null; - for (const t in n) - if (Object.prototype.hasOwnProperty.call(n, t)) { - var u = Number(t); - if (ne(u)) { - var c = n[u]; - if (this.isFragmentCompleteOrEmpty(u, c.count, i)) - if (u === r.startSN && e < c.startTime && (a = o = s.start = s.end = e = c.startTime), e >= c.startTime && (a === e || ne(l) && 1 < u - l) && (a = o = c.startTime), e >= c.startTime) o = c.endTime, l = u; - else { - if (!ne(l) || u - l != 1) { - d = u; - break - } - o = c.endTime, l = u - } - } else this.logger.warn(`$fragInfoMap has invalid key ${u}`) - } return s = { - len: o - a, - start: a, - end: o - }, { - fragInfoMap: n, - bufferInfo: s, - prevFragSN: l, - nextFragSN: d - } - } - findFrags$(t, i) { - return this.tryAgain$.pipe(ji(tr), La(() => { - var e = this.findFragmentsForPosition(this.mediaSink.mediaQuery.currentTime, i, t); - return e.foundFrags ? (this.lastCueEndTime = 0, this.needNextSubtitle$.next(!0), $i(e)) : Ii - })) - } - reviewParsedFrag(e, i, r) { - var n = e.frag, - s = e.cueRange, - t = i.subtitleBufferInfo, - a = i.subtitleParsedInfo, - o = this.mediaSink.mediaQuery.currentTime, - e = i.foundFrags; - let l = !0; - if (n.mediaSeqNum === e[0].mediaSeqNum) { - if (!i.timelineEstablished) return kp.TooFar; - if (!s) return this.logger.warn(`[subtitle] 1st frag sn ${n.mediaSeqNum} has no cue; details ${r.fragments.length} frags`), kp.Unknown; - if (s.startTime < o) { - const d = r.fragments, - i = n.mediaSeqNum - r.startSN; - let e = i, - t = s.startTime; - for (; e < d.length && (t += d[e].duration, !(t >= o)); ++e); - l = e - i + 1 <= this.config.earlyFragTolerance - } else if (s.startTime > o && n.mediaSeqNum !== r.startSN) { - const d = s.startTime - o, - i = t.prevFragSN; - l = n.mediaSeqNum === i + 1 && (null === (t = t.fragInfoMap[i]) || void 0 === t ? void 0 : t.count) === (null === (a = a[i]) || void 0 === a ? void 0 : a.count) || d <= this.config.lateTolerance - } - } - return l ? kp.CloseEnough : kp.TooFar - } - isFragmentEmpty(e) { - return e && !ne(e.startTime) && 0 === e.count - } - isFragmentCompleteOrEmpty(e, t, i) { - e = i ? i[e] : null; - return (null == e ? void 0 : e.count) === t || this.isFragmentEmpty(e) - } - getEarlierFragmentInSameDisco(e, t, i) { - var r = t.mediaSeqNum - e.startSN - 1; - if (r < 0 || r > e.fragments.length - 1) return this.logger.error(`[subtitle] getEarlierFragmentInSameDisco index ${r} out of range`), t; - r = e.fragments[r]; - return r && r.discoSeqNum === t.discoSeqNum && !i[t.mediaSeqNum] ? r : t - } - inferSubtitleFragmentForPosition(i, r, t, n, s) { - let a, o, e, l, d; - if (ne(n.prevFragSN) && (o = n.prevFragSN - s.startSN, e = t[n.prevFragSN]), ne(n.nextFragSN) && (l = n.nextFragSN - s.startSN, d = t[n.nextFragSN]), ne(o) && 0 <= o && o < s.fragments.length && e) { - let t = e.startTime; - const n = ne(l) ? l : s.fragments.length; - for (let e = o; e < n; ++e) { - const l = s.fragments[e]; - if (!ne(r) || l.discoSeqNum === r) { - if (e === n - 1) { - a = { - foundFrag: l, - timelineEstablished: !0 - }; - break - } - if (t + l.duration > i && e > o) { - a = { - foundFrag: l, - timelineEstablished: !0 - }; - break - } - t += l.duration - } - } - } else if (ne(l) && 0 <= l && l < s.fragments.length && d) { - let t = d.startTime; - for (let e = l - 1; 0 <= e; --e) { - const o = s.fragments[e]; - if (!ne(r) || o.discoSeqNum === r) { - if (t <= i) { - a = { - foundFrag: o, - timelineEstablished: !0 - }; - break - } - t -= o.duration - } - } - } else - for (let e = 0; e < s.fragments.length; ++e) { - const t = s.fragments[e]; - if (ne(r) && t.discoSeqNum === r) { - a = { - foundFrag: t, - timelineEstablished: !1 - }; - break - } - } - return a - } - generateFragmentBatch(t, i, e, r, n, s) { - var a; - const o = [], - l = null == e ? void 0 : e.foundFrag; - if (!l) return { - foundFrags: void 0, - subtitleParsedInfo: void 0, - subtitleBufferInfo: void 0, - timelineEstablished: null == e ? void 0 : e.timelineEstablished - }; - for (let e = l ? l.mediaSeqNum - s.startSN : s.fragments.length; e < s.fragments.length && o.length < t; ++e) { - const t = s.fragments[e]; - if (t.discoSeqNum === i) { - const l = null === (a = n.fragInfoMap[t.mediaSeqNum]) || void 0 === a ? void 0 : a.count; - this.isFragmentCompleteOrEmpty(t.mediaSeqNum, null != l ? l : 0, r) || o.push(t) - } - } - return { - foundFrags: o, - subtitleParsedInfo: r, - subtitleBufferInfo: n, - timelineEstablished: null == e ? void 0 : e.timelineEstablished - } - } - findFragmentsForPosition(e, t, i) { - var r = this.mediaSink.mediaQuery.getParsedSubtitleRecordsForMediaOption(this.selectedTrack.persistentID), - n = this.getCuesOfEnabledTrack(this.selectedMediaOption.mediaOptionId, !1), - s = this.calculateFragInfoMap(e, n, r, i), - n = s.bufferInfo, - n = Math.max(e, n.end), - n = this.inferSubtitleFragmentForPosition(n, t, r, s, i); - return this.generateFragmentBatch(1 / 0, t, n, r, s, i) - } - get selectedMediaOption() { - return this.selectedTrack || this._disabledMediaOption - } - set selectedMediaOption(e) { - this.selectedTrack = "groupId" in e ? e : void 0 - } - get selectedTrack() { - return this._selectedMediaOption - } - set selectedTrack(e) { - e !== this._selectedMediaOption && (this._selectedMediaOption = e, this.updateTextTrackState()) - } - getTrack(t) { - return this._availableMediaOptions.find(e => e.mediaOptionId === t) - } - updateTextTrackState() { - if (this.mediaSink.textTracks) { - const i = this.selectedTrack ? this.getExistingHTMLTextTrack(this.selectedTrack) : void 0, - r = Rp(this.mediaSink.textTracks); - for (let e = 0; e < r.length; e++) { - var t = r[e]; - t === i && "showing" !== r[e].mode ? r[e].mode = "showing" : t !== i && "hidden" !== r[e].mode && (r[e].mode = "hidden") - } - } - } - mapHTMLTextTrackIndexToMediaOptionId(e) { - const i = this.mediaSink.textTracks[e]; - let r; - return this.htmlTextTrackMap.forEach((e, t) => { - i === e && (r = t) - }), r - } - get mediaSelectionOptions() { - return this._availableMediaOptions - } - _makeDisableOption(e) { - return { - itemId: e.itemId, - mediaOptionType: e.mediaOptionType, - mediaOptionId: "Nah" - } - } - _onTextTracksChanged() { - if (this.mediaSink) { - let t, i = !1; - const r = Rp(this.mediaSink.textTracks); - for (let e = 0; e < r.length; e++) r[e].seen ? "showing" === r[e].mode && (t = r[e].persistentId) : (r[e].seen = !0, i = !0); - if (!i) { - const e = this.selectedTrack; - if ((null == e ? void 0 : e.persistentID) !== t) { - const e = this.mediaSelectionOptions.find(function(e) { - return e.persistentID === t - }); - this.nativeSubtitleTrackChange$.next(e || this._disabledMediaOption) - } - } - } - } - addCues(e, t, i, r) { - const n = this.cueRanges; - let s = !1; - for (let e = n.length; e--;) { - const r = n[e], - d = (a = r[0], o = r[1], l = t, Math.min(o, i) - Math.max(a, l)); - if (0 <= d && (r[0] = Math.min(r[0], t), r[1] = Math.max(r[1], i), s = !0, .5 < d / (i - t))) return - } - var a, o, l; - s || n.push([t, i]), this.Cues.newCue(this.channelToTrackMap[e], t, i, r, this.mediaSink) - } - getExistingHTMLTextTrackWithChannelNumber(t) { - var i = this.mediaSink; - if (i) - for (let e = 0; e < i.textTracks.length; e++) { - var r = i.textTracks[e], - n = "cc" + t; - if (Pp(n) && !0 === r[n]) return r - } - return null - } - sendAddTrackEvent(e, t) { - let i = null; - try { - i = new window.Event("addtrack") - } catch (e) { - i = document.createEvent("Event"), i.initEvent("addtrack", !1, !1) - } - i.track = e, t.dispatchEvent(i) - } - createHTMLCaptionsTrackGuts(e, t, i, r) { - var n = "cc" + e; - if (!this.channelToTrackMap[n]) { - e = this.getExistingHTMLTextTrackWithChannelNumber(e); - if (e) this.channelToTrackMap[n] = e, Lp(this.channelToTrackMap[n]), this.sendAddTrackEvent(this.channelToTrackMap[n], this.mediaSink); - else { - const s = this.createHTMLTextTrackGuts("captions", t, i, r); - s && Pp(n) && (s[n] = !0, this.channelToTrackMap[n] = s) - } - } - return this.channelToTrackMap[n] - } - createHTMLCaptionsTrack(e) { - return this.createHTMLCaptionsTrackGuts(e, this.config[1 === e ? "captionsTextTrack1Label" : "captionsTextTrack2Label"], this.config.captionsTextTrack1LanguageCode, !1) - } - getExistingHTMLTextTrack(e) { - return this.config.condenseSubtitleTrack ? this.htmlTextTrackMap.get(e.persistentID) : this.htmlTextTrackMap.get(e.id) - } - getExistingHTMLTextTrackWithSubtitleTrackId(t) { - var e = this._availableMediaOptions.find(e => e.id === t); - return e ? this.getExistingHTMLTextTrack(e) : void 0 - } - getExistingHTMLTextTrackIndex(e) { - var t = this.getExistingHTMLTextTrack(e), - i = this.mediaSink.textTracks; - let r = -1; - for (let e = 0; e < i.length; ++e) - if (i[e] === t) { - r = e; - break - } return r - } - setExistingHTMLTextTrack(e, t) { - return t.persistentId = e.persistentID, this.config.condenseSubtitleTrack ? this.htmlTextTrackMap.set(e.persistentID, t) : this.htmlTextTrackMap.set(e.id, t) - } - createHTMLTextTrack(t) { - let i = this.getExistingHTMLTextTrack(t); - if (i) this.tracksReused += 1; - else { - if ("sbtl" === t.mediaType) this.subtitleTracksCreated += 1, i = this.createHTMLTextTrackGuts("subtitles", t.name, t.lang, t.forced); - else { - let e = 1; - t.inStreamID && (e = Number(t.inStreamID.substring(2))), this.captionTracksCreated += 1, i = this.createHTMLCaptionsTrackGuts(e, t.name, t.lang, !1) - } - i ? this.setExistingHTMLTextTrack(t, i) : (this.logger.error(`failed to create HTML text track for track ${t.id}: persistent id ${t.persistentID} name ${t.name} lang ${t.lang} inStreamID ${t.inStreamID}`), this.tracksFailed += 1) - } - return i - } - createHTMLTextTrackGuts(t, i, r, e) { - const n = this.mediaSink; - if (n) { - let e = !1; - "metadata" !== t && this.config.customTextTrackCueRenderer && (e = !0, t = "metadata"); - const s = n.addTextTrack(t, i, r); - return e && (s.customTextTrackCueRenderer = !0), s - } - } - resetLoadSource() { - this.resetTracks() - } - resetTracks() { - this._cleanTracks(), this.cueRanges = [] - } - _cleanTracks() { - var e = this.mediaSink; - if (e) { - var t = e.textTracks; - if (t) - for (let e = 0; e < t.length; e++) Lp(t[e]) - } - } - getCuesOfEnabledTrack(e, t = !1) { - let i = []; - if (t) { - const t = this._getCuesOfEnabledTrack(e); - for (let e = 0; e < t.length; e++) { - var r = t[e]; - Boolean(r.webVTTCue) && i.push(r) - } - } else i = this._getCuesOfEnabledTrack(e); - return i - } - _getCuesOfEnabledTrack(e) { - e = this.getTrack(e), e = this.config.condenseSubtitleTrack ? null == e ? void 0 : e.persistentID : null == e ? void 0 : e.id, e = this.htmlTextTrackMap.get(e); - return e && e.cues ? Array.from(e.cues) : [] - } - attachSubtitleTracks() { - this.gotTracks && (this.subtitleTracksCreated = 0, this.captionTracksCreated = 0, this.tracksReused = 0, this.tracksFailed = 0, this.tracks.forEach(e => { - this.createHTMLTextTrack(e) - })) - } - setTracks(e, t, i) { - this._cleanTracks(), this.htmlTextTrackMap = new Map, this.cueRanges = [], this.config.enableWebVTT && (this.tracks = e || []), this.gotTracks = !0, this._availableMediaOptions = e, this._disabledMediaOption = i, this.attachSubtitleTracks(), this.selectedTrack = t, this.nativeSubtitleTrackChange$ = new Xt, this.mediaSink.textTracksCreated = !0 - } - onInlineStylesParsed(e) {} - processSubtitleFrag(e, t, i, r) { - var n = new Uint8Array(r), - e = this.getExistingHTMLTextTrackIndex(e); - if (t && r.byteLength) { - const s = this._parseVTTs(e, t, i, n); - return s && ne(s.startTime) && (this.lastCueEndTime = Math.max(this.lastCueEndTime, s.endTime)), s - } - } - _parseVTTs(r, n, e, t) { - let s; - return Mp(t, e, n.start, n.discoSeqNum, e => { - const t = this.mediaSink.textTracks[r], - i = { - count: 0, - startTime: Number.POSITIVE_INFINITY, - endTime: 0 - }; - e.map(e => { - !t || t.cues && t.cues.getCueById(e.id) || (e.fragSN = n.mediaSeqNum, e.webVTTCue = !0, t.addCue(e), i.count++), i.startTime = Math.min(e.startTime, i.startTime), i.endTime = Math.max(e.endTime, i.endTime) - }), s = i, this.mediaSink.archiveParsedSubtitleFragmentRecord(this.selectedTrack.persistentID, n.mediaSeqNum, i) - }, e => {}, e => { - this.hls.trigger(x.INLINE_STYLES_PARSED, { - styles: e - }) - }, this.logger), s - } - _ensureParser() { - var e, t; - this.cea608Parser || (e = new Xh(this, 1), t = new Xh(this, 2), this.cea608Parser = new zh(0, e, t)) - } - setupForFrag(e) { - e && e.mediaOptionType === gu.Variant && !e.iframe && ((e = e.mediaSeqNum) !== this.lastVariantSeqNum + 1 && this.resetClosedCaptionParser(), this.lastVariantSeqNum = e) - } - resetClosedCaptionParser() { - var e; - null === (e = this.cea608Parser) || void 0 === e || e.reset() - } - addLegibleSamples(e, t, i, r) { - t && this.addClosedCaptionSamples(e, t), i && 0 < i.length && this.addId3Samples(e, i, S(r)) - } - addClosedCaptionSamples(e, t) { - t.mp4 ? this.addMP4CaptionSamples(e, t.mp4) : t.ts && this.addTSCaptionSamples(e, t.ts) - } - addMP4CaptionSamples(e, i) { - if (this.enableCaption && this.config.enableCEA708Captions) { - var r = S(e); - this._ensureParser(); - for (let e = 0; e < i.length; e++) { - let t = i[e].pts - r; - var n = i[e].bytes; - for (let e = 0; e < n.length; e += 2) { - const i = []; - i.push(n[e]), e + 1 < n.length ? i.push(n[e + 1]) : i.push(80), this.cea608Parser.addData(t, i), t += .03336666666666667 - } - } - } - } - addTSCaptionSamples(e, t) { - if (this.enableCaption && this.config.enableCEA708Captions) { - var i = S(e); - this._ensureParser(); - for (let e = 0; e < t.length; e++) { - var r = t[e].pts - i, - n = _p.extractCea608Data(t[e].bytes); - this.cea608Parser.addData(r, n) - } - } - } - addId3Samples(e, t, r) { - if (this.config.enableID3Cues) { - const n = window.WebKitDataCue || window.VTTCue || window.TextTrackCue, - s = S(e); - for (let e = 0; e < t.length; e++) { - const a = t[e].pts - s; - let i = (e < t.length - 1 ? t[e + 1].pts : r) - s; - a === i && (i += 1e-4), t[e].frames && t[e].frames.forEach(e => { - if (e && !this.id3shouldIgnore(e)) { - const t = new n(a, i, ""); - t.value = e, this.id3Track.addCue(t) - } - }) - } - } - } - id3shouldIgnore(e) { - return "PRIV" === e.key && ("com.apple.streaming.transportStreamTimestamp" === e.info || "com.apple.streaming.audioDescription" === e.info) - } - static extractCea608Data(t) { - var i = 31 & t[0]; - let r, n, s, a = 2; - const o = []; - for (let e = 0; e < i; e++) r = t[a++], n = 127 & t[a++], s = 127 & t[a++], 0 == n && 0 == s || 0 != (4 & r) && 0 == (3 & r) && (o.push(n), o.push(s)); - return o - } - } - const Np = { - name: "plist" - }; - class Fp { - constructor(e, t, i, r) { - this.config = e, this.xhrLoader = t, this.customUrlLoader = i, this.sessionDataCheckForCompleteness = e => { - const t = this.config["sessionDataAutoLoad"], - i = Object.assign({}, e); - return e.complete || (e.itemList ? i.complete = e.itemList.every(e => t[e["DATA-ID"]] && !e.VALUE && !e._STATUS && e.URI ? (this.logger.warn(`Incomplete because ${e["DATA-ID"]} was autoloaded but no response yet`), !1) : (t[e["DATA-ID"]] && !e.URI && this.logger.warn(`id=${e["DATA-ID"]} missing uri`), !0)) : this.logger.warn("Uninitialized SessionData")), i - }, this.logger = r.child({ - name: "SessionDataLoader" - }) - } - loadSessionData(r) { - const n = this.config["sessionDataAutoLoad"], - t = r.itemList || []; - let s = $i(r); - return t.forEach(e => { - const i = e["DATA-ID"], - t = e.URI; - if (t && n[i]) { - const n = bu.buildAbsoluteURL(r.baseUrl, t, { - alwaysNormalize: !0 - }), - e = ""; - s = s.pipe(La(t => this.loadSessionDataItemWithUrl(n, i, "", this.config, t, this.xhrLoader, this.customUrlLoader).pipe(Vn(e => (this.logger.error(`Error loading SessionData > url=${n}, id=${i}, err=${e}`), $i(t)))))) - } - }), s.pipe(hr(e => { - if (t.length < 1) return e; - e = this.sessionDataCheckForCompleteness(e); - if (e.complete) return e; - throw new V(!1, "Session data not complete after loading all items", $.IncompleteSessionData) - }), Vs(() => {})) - } - loadSessionDataItemWithUrl(e, t, i, r, n, s, a) { - const o = Qe(), - l = { - url: e, - method: "GET", - responseType: i, - xhrSetup: r.xhrSetup, - mimeType: "application/xml" - }, - d = Lc({ - url: e - }, r.fragLoadPolicy); - let u; - return u = Tu(e) ? a(l, d).pipe(hr(e => this.onLoadSuccess(n, t, e.data.response.data.toString(), e.data.response.data))) : s(l, d).pipe(hr(([e]) => this.onLoadSuccess(n, t, e.response, e.responseXML))), u.pipe(Vn(e => (e instanceof dr ? e = new cc(!1, e.message, 0, $.SessionDataLoadTimeout) : e instanceof oc && (e = new cc(!1, e.message, e.code, { - code: e.code, - text: "Failed to load SessionData" - })), o.error(`Unable to load SessionData > err=${e}`), $i(this.onLoadError(n, t, e))))) - } - onLoadSuccess(e, t, i, r) { - let n = null, - s = e; - if (function(e) { - const t = /[\s]*<\?xml/i; - t.lastIndex = 0; - var i = t.exec(e); - return i || /[\s]*"), null) - } else if ("string" === i) { - const e = a[0]; - s = e ? e.nodeValue : null - } else if ("integer" === i) { - const e = a[0]; - s = e ? parseInt(e.nodeValue) : 0 - } else if ("float" === i) { - const e = a[0]; - s = e ? parseFloat(e.nodeValue) : 0 - } else if ("date" === i) { - const e = a[0]; - s = e ? new Date(e.nodeValue) : null - } else if ("data" === i) { - const e = a[0]; - s = e ? atob(e.nodeValue) : null - } else "true" === i ? s = !0 : "false" === i && (s = !1) - } else if (a.length < 1) t.warn(Np, `unknown node with unknown value > nodeType=${e.nodeType} tagName=${e.tagName} nodeName=${e.nodeName} value=${e.nodeValue}`); - else { - s = []; - for (let e = 0; e < a.length; ++e) { - const r = a[e]; - r.tagName && s.push(n(r)) - } - 1 === s.length && (s = s[0]) - } - return s - }(n); - s = this.setSessionData(e, t, "VALUE", i) - } else if (function(e) { - const t = /[\s]*[\{\[]/; - return t.lastIndex = 0, t.exec(e) - }(i)) try { - const r = JSON.parse(i); - s = this.setSessionData(e, t, "VALUE", r) - } catch (r) { - this.logger.error(`JSON parser error: ${r}`), s = this.setSessionData(e, t, "VALUE", i), s = this.setSessionData(s, t, "_STATUS", -1) - } else s = this.setSessionData(e, t, "VALUE", i); - return s - } - setSessionData(t, i, r, n) { - let s = t; - if (t.itemList) { - let e; - const a = [...t.itemList]; - for (e = 0; e < t.itemList.length; ++e) { - const t = Object.assign({}, a[e]); - if (t["DATA-ID"] === i) { - t[r] = n, a[e] = t; - break - } - } - e === t.itemList.length && this.logger.error(`Can't set ${r} of session data ${i}`), s = Object.assign(Object.assign({}, t), { - itemList: a - }) - } else this.logger.error(`Can't set ${r} on uninitialized session data`); - return s - } - onLoadError(e, t, i) { - return this.setSessionData(e, t, "_STATUS", null === (i = i.response) || void 0 === i ? void 0 : i.code) - } - } - - function Bp(e, t) { - let i, r = 0; - for (const a of e) - if (a.start <= t.endPTS && a.end > t.startPTS) { - const e = (n = t, s = a, Math.min(n.endPTS, s.end) - Math.max(n.startPTS, s.start)); - e > r && (i = a, r = e) - } else if (0 < r) break; - var n, s; - return i - } - - function Up(e) { - return null != e && "iframeMediaDuration" in e && "iframeMediaStart" in e - } - - function $p(e, t) { - return e === t || e && t && e.itemId === t.itemId && e.mediaOptionId === t.mediaOptionId && e.mediaSeqNum === t.mediaSeqNum && e.discoSeqNum === t.discoSeqNum - } - - function Vp(e) { - return JSON.stringify(e, ["mediaOptionId", "mediaSeqNum", "discoSeqNum", "start", "duration"]) - } - - function Kp() { - return e => e.pipe(ln(e => null != e), hr(e => e)) - } - - function qp(e) { - return be.isDolby(e) ? hu.DOVI : be.isHEVC(e) ? hu.HEVC : be.isVP09(e) ? hu.VP09 : be.isAVC(e) ? hu.AVC : hu.UNKNOWN - } - - function Hp(e) { - return null == e ? void 0 : e.split(".")[0] - } - - function jp(e) { - return be.isALAC(e) ? fu.ALAC : be.isFLAC(e) ? fu.FLAC : be.isEC3(e) ? fu.EC3 : be.isAC3(e) ? fu.AC3 : be.isXHEAAC(e) ? fu.XHEAAC : be.isAAC(e) ? fu.AAC : be.isMP3(e) ? fu.MP3 : fu.UNKNOWN - } - class Qp { - constructor(...e) { - this.identifier = e - } - ensureSameIdentifierLength(e) { - if (this.identifier.length !== e.identifier.length) throw new Error(`Identifiers have non-matching lengths! (${this.identifier.length} vs ${e.identifier.length})`) - } - isGreaterThan(t) { - this.ensureSameIdentifierLength(t); - for (let e = 0; e < this.identifier.length; ++e) { - if (this.identifier[e] < t.identifier[e]) return !1; - if (this.identifier[e] > t.identifier[e]) return !0 - } - return !1 - } - isEqualTo(i) { - return this.ensureSameIdentifierLength(i), this.identifier.every((e, t) => e === i.identifier[t]) - } - } - - function Wp(e) { - return ne(e) && 0 !== e && 1 !== e - } - class Gp extends Error {} - class zp { - constructor(e) { - this.value = e, this.waiters = [], this.wcounter = 0, this.rcounter = 0 - } - lock(e, t = !1) { - return this._lock(!0, e, t) - } - unlock() { - this._unlock(!0) - } - readLock(e, t = !1) { - return this._lock(!1, e, t) - } - readUnlock() { - this._unlock(!1) - } - _schedule() { - const t = []; - this.waiters = this.waiters.filter(e => !this._canLock(e.rw) || (e.rw ? ++this.wcounter : ++this.rcounter, t.push(e), !1)); - for (const e of t) e.observer.next(this.value), e.observer.complete() - } - _canLock(e) { - return e && 0 === this.wcounter && 0 === this.rcounter || !e && 0 === this.wcounter - } - _lock(i, e, r = !1) { - "boolean" == typeof e && ([r, e] = [e, void 0]); - const t = new $t(e => { - var t = this._canLock(i); - if (r && !t) throw new Gp; - t ? (i ? ++this.wcounter : ++this.rcounter, e.next(), e.complete()) : this.waiters.push({ - rw: i, - observer: e - }) - }); - return e ? t.pipe(jr(() => ((e, t, i, r) => { - if (!r) return $i(e); - let n, s; - try { - n = r(e, t) - } catch (e) { - s = Vi(() => e) - } - return s = s || (void 0 === n ? $i(e) : Fr(n)), s.pipe(Vs(i)) - })(this.value, e => { - this.value = e - }, () => this._unlock(i), e))) : t - } - _unlock(e) { - e ? this.wcounter = Math.max(this.wcounter - 1, 0) : this.rcounter = Math.max(this.rcounter - 1, 0), this._schedule() - } - } - class Xp extends $t { - constructor() { - super(e => this._count$.pipe(ln(e => 0 === e), Ds(1), Zs(void 0)).subscribe(e)), this._count$ = new yi(0) - } - wrap(e) { - return Zr(() => (this.add(), Fr(e))).pipe(Za({ - error: e => this._count$.error(e) - }), Vs(() => this.done())) - } - add(e = 1) { - this._count$.next(this._count$.value + e) - } - done(e = 1) { - this._count$.next(this._count$.value - e) - } - } - const Yp = { - isBuffered(t, i) { - for (let e = 0; t && e < t.length; e++) - if (i >= t.start(e) && i <= t.end(e)) return !0; - return !1 - }, - timeRangesToBufferedRange(t) { - const i = []; - for (let e = 0; t && e < t.length; e++) i.push({ - start: t.start(e), - end: t.end(e) - }); - return i - }, - subtitleBufferInfo(e, t, i) { - if (e) { - e = this.bufferedCues(e); - return this.getBufferedInfo(e, t, i) - } - return { - len: 0, - start: t, - end: t, - nextStart: void 0 - } - }, - fragmentsBufferedInfo(e, t, i) { - const r = []; - for (const t of e) r.push({ - start: t.start, - end: t.start + t.duration - }); - return this.getBufferedInfo(r, t, i) - }, - bufferedCues(t) { - const i = []; - if (t) - for (let e = 0; e < t.length; e++) i.push({ - start: t[e].startTime, - end: t[e].endTime - }); - return i - }, - bufferedInfoFromMedia: (e, t, i) => Yp.getBufferedInfo(Yp.timeRangesToBufferedRange(e.buffered), t, i), - getBufferedInfo(e, t, i) { - const r = []; - let n, s, a, o, l; - const d = e.map(({ - start: e, - end: t - }) => ({ - start: e, - end: t - })); - for (d.sort((e, t) => { - return e.start - t.start || t.end - e.end - }), l = 0; l < d.length; l++) { - const e = r.length; - if (e) { - const t = r[e - 1].end; - d[l].start - t < i ? d[l].end > t && (r[e - 1].end = d[l].end) : r.push(d[l]) - } else r.push(d[l]) - } - for (l = 0, n = 0, s = a = t; l < r.length; l++) { - const e = r[l]["start"], - d = r[l]["end"]; - if (t + i >= e && t < d) s = e, a = d, n = a - t; - else if (t + i < e) { - o = e; - break - } - } - return { - len: n, - start: s, - end: a, - nextStart: o - } - }, - toRangeString: e => `[${e.start.toFixed(3)},${e.end.toFixed(3)}]` - }; - (w = Cp = Cp || {}).Seek = "Seek", w.HighBuffer = "HighBuffer", w.LowBuffer = "LowBuffer", (w = Dp = Dp || {}).AlmostDry = "AlmostDry", w.LowWater = "LowWater", w.HighWater = "HighWater", w.AboveHighWater = "AboveHighWater"; - const Jp = { - [Dp.AlmostDry]: 0, - [Dp.LowWater]: 1, - [Dp.HighWater]: 2, - [Dp.AboveHighWater]: 3 - }; - - function Zp(t, e) { - return [{ - threshold: e.highWaterLevelSeconds, - level: Dp.HighWater - }, { - threshold: e.lowWaterLevelSeconds, - level: Dp.LowWater - }, { - threshold: e.almostDryWaterLevelSeconds, - level: Dp.AlmostDry - }].find(({ - threshold: e - }) => e < t) - } - - function ef(t, e) { - return [{ - threshold: e.almostDryWaterLevelSeconds, - level: Dp.AlmostDry - }, { - threshold: e.lowWaterLevelSeconds, - level: Dp.LowWater - }, { - threshold: e.highWaterLevelSeconds, - level: Dp.HighWater - }, { - threshold: 1 / 0, - level: Dp.AboveHighWater - }].find(({ - threshold: e - }) => t <= e) - } - - function tf(t, i) { - const e = ef(t.getCurrentWaterLevel(i), t.bufferMonitorInfo).level, - r = [null, null]; - return [yu.Variant, yu.AltAudio].forEach(e => { - null != t.sourceBufferEntityByType(e) && (r[e] = ef(t.getCurrentWaterLevelByType(e, i), t.bufferMonitorInfo).level) - }), { - combined: e, - sbTuple: r - } - } - - function rf(s, a) { - return ed([s.combinedBuffer$, s.gotPlaying$, s.seeking$, s.waterLevelChangedForType$(null), s.stallInfo$]).pipe(La(([e, t, i, r, n]) => 0 === e.length || !t || i || null == r || null != n ? Ii : function t(i, r, e) { - const n = i.getCurrentWaterLevel(r), - s = Zp(n, i.bufferMonitorInfo); - if (s) { - const e = s["threshold"]; - return bn(Math.ceil(1e3 * (n - e))).pipe(La(() => { - const e = Zp(i.getCurrentWaterLevel(r), i.bufferMonitorInfo); - return (null == e ? void 0 : e.level) === s.level ? t(i, r) : Wu - }), hr(() => tf(i, r))) - } - return Ii - }(s, a))) - } - class nf extends kl { - constructor(e, t) { - super(t), this.mediaElement = e - } - get mediaElementDuration$() { - return this.selectActive(({ - mediaElementDuration: e - }) => e) - } - get mediaElementDuration() { - var e; - return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.mediaElementDuration) && void 0 !== e ? e : 1 / 0 - } - get msDuration() { - var e; - return null !== (e = null === (e = this.mediaSourceEntity) || void 0 === e ? void 0 : e.duration) && void 0 !== e ? e : 1 / 0 - } - get minSBDuration() { - var e; - let i = Number.POSITIVE_INFINITY; - return null === (e = null === (e = this.mediaSourceEntity) || void 0 === e ? void 0 : e.sourceBufferEntities) || void 0 === e || e.forEach((e, t) => { - e && (i = ne(e.totalDuration) ? Math.min(i, e.totalDuration) : Number.NEGATIVE_INFINITY) - }), ne(i) ? i : 1 / 0 - } - get currentTime() { - return this.mediaElement.currentTime - } - get clientWidth() { - return this.mediaElement.clientWidth - } - get clientHeight() { - return this.mediaElement.clientHeight - } - getBufferedDuration(e = .5) { - var t = Yp.timeRangesToBufferedRange(this.mediaElement.buffered), - e = Yp.getBufferedInfo(t, this.currentTime, e); - return e.end - e.start - } - get mediaSourceEntity() { - var e; - return null === (e = this.getActive()) || void 0 === e ? void 0 : e.mediaSourceEntity - } - get msReadyState() { - var e; - return null === (e = this.mediaSourceEntity) || void 0 === e ? void 0 : e.readyState - } - get sourceBufferEntities() { - var e; - return null === (e = this.mediaSourceEntity) || void 0 === e ? void 0 : e.sourceBufferEntities - } - sourceBufferEntityByType(e) { - var t; - return null === (t = this.sourceBufferEntities) || void 0 === t ? void 0 : t[e] - } - initSegmentEntityByType(e) { - return null === (e = this.sourceBufferEntityByType(e)) || void 0 === e ? void 0 : e.initSegmentInfo - } - get maxBufferSize() { - var e = null === (e = this.sourceBufferEntities) || void 0 === e ? void 0 : e[yu.Variant]; - let t = 1 / 0; - return null != e && e.gotQuotaExceeded && (t = null !== (e = e.maxTotalBytes) && void 0 !== e ? e : 1 / 0), t - } - get postFlushSeek() { - var e; - return null === (e = this.getActive()) || void 0 === e ? void 0 : e.postFlushSeek - } - get seekable() { - return this.mediaElement.seekable - } - get desiredRate() { - var e; - return (null === (e = this.getActive()) || void 0 === e ? void 0 : e.desiredRate) || 0 - } - get desiredRate$() { - return this.selectActive(({ - desiredRate: e - }) => null != e ? e : 0) - } - get effectiveRate() { - return this.isIframeRate ? this.desiredRate : this.paused ? 0 : 1 - } - get playbackRate() { - return this.mediaElement.playbackRate - } - get isIframeRate() { - return Wp(this.desiredRate) - } - get isIframeRate$() { - return this.desiredRate$.pipe(hr(Wp)) - } - get msObjectUrl$() { - return this.selectActive(({ - mediaSourceEntity: e - }) => null == e ? void 0 : e.objectUrl).pipe(Is()) - } - get msReadyState$() { - return this.selectActive(({ - mediaSourceEntity: e - }) => { - return null !== (e = null == e ? void 0 : e.readyState) && void 0 !== e ? e : null - }) - } - get readyState() { - var e; - return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.readyState) && void 0 !== e ? e : 0 - } - get readyState$() { - return this.selectActive(({ - readyState: e - }) => null != e ? e : 0) - } - get mediaSourceEntity$() { - return this.selectActive(({ - mediaSourceEntity: e - }) => e) - } - get expectedSbCount$() { - return this.selectActive(({ - expectedSbCount: e - }) => e) - } - get expectedSbCount() { - var e; - return null === (e = this.getActive()) || void 0 === e ? void 0 : e.expectedSbCount - } - get paused$() { - return this.selectActive(({ - paused: e - }) => e) - } - get paused() { - var e; - return null === (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.paused) || void 0 === e || e - } - get playbackStarted() { - var e; - return ne(null === (e = this.getActive()) || void 0 === e ? void 0 : e.firstPlayTime) - } - get flushing$() { - return this.selectActive(({ - flushing: e - }) => e) - } - get flushing() { - var e; - return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.flushing) && void 0 !== e && e - } - get waitingForDisco$() { - return this.selectActive(({ - waitingForDisco: e - }) => e) - } - get waitingForDisco() { - var e; - return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.waitingForDisco) && void 0 !== e && e - } - get gotPlaying() { - var e; - return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.gotPlaying) && void 0 !== e && e - } - get gotPlaying$() { - return this.selectActive(({ - gotPlaying: e - }) => e) - } - get gotLoadStart$() { - return this.selectActive(({ - gotLoadStart: e - }) => e) - } - get seekTo() { - var e; - return null === (e = this.getActive()) || void 0 === e ? void 0 : e.seekTo - } - get seekTo$() { - return this.selectActive(({ - seekTo: e - }) => e) - } - get seeking() { - var e; - return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.seeking) && void 0 !== e && e - } - get seeking$() { - return this.selectActive(({ - seeking: e - }) => e) - } - get nudgeTarget$() { - return this.selectActive(({ - nudgeInfo: e - }) => null == e ? void 0 : e.nudgeTarget) - } - get nudgeCount() { - var e; - return null !== (e = null === (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.nudgeInfo) || void 0 === e ? void 0 : e.nudgeCount) && void 0 !== e ? e : 0 - } - get sourceBufferEntities$() { - return this.selectActive(({ - mediaSourceEntity: e - }) => null == e ? void 0 : e.sourceBufferEntities) - } - sourceBufferEntityByType$(t) { - return this.selectActive(({ - mediaSourceEntity: e - }) => { - return null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[t] - }) - } - bufferedSegmentsByType$(t) { - return this.selectActive(({ - mediaSourceEntity: e - }) => { - return null !== (e = null === (e = null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[t]) || void 0 === e ? void 0 : e.bufferedSegments) && void 0 !== e ? e : [] - }) - } - getBufferedSegmentsByType(e) { - return null !== (e = null === (e = this.sourceBufferEntityByType(e)) || void 0 === e ? void 0 : e.bufferedSegments) && void 0 !== e ? e : [] - } - get bufferedSegmentsTuple$() { - return ed([this.bufferedSegmentsByType$(yu.Variant), this.bufferedSegmentsByType$(yu.AltAudio)]).pipe(Gn(10)) - } - get timeupdate$() { - return Oc(this.mediaElement).event("timeupdate").pipe(ji(tr), Aa(), ao(125, void 0, { - leading: !0, - trailing: !0 - }), hr(e => this.currentTime), ln(e => ne(e))) - } - get playingEvent$() { - return Oc(this.mediaElement).event("playing").pipe(hr(() => {})) - } - get mediaElementEntity$() { - return this.selectActive(e => Boolean(e)) - } - get ended$() { - return this.selectActive(e => { - return null !== (e = null == e ? void 0 : e.ended) && void 0 !== e && e - }) - } - sbUpdating$(t) { - return this.selectActive(e => { - return null !== (e = null === (e = null === (e = null === (e = null == e ? void 0 : e.mediaSourceEntity) || void 0 === e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[t]) || void 0 === e ? void 0 : e.updating) && void 0 !== e && e - }) - } - sbUpdating(e) { - var t; - return null !== (e = null === (e = null === (t = null === (t = null === (t = this.getActive()) || void 0 === t ? void 0 : t.mediaSourceEntity) || void 0 === t ? void 0 : t.sourceBufferEntities) || void 0 === t ? void 0 : t[e]) || void 0 === e ? void 0 : e.updating) && void 0 !== e && e - } - sbError$(t) { - return this.selectActive(e => { - return null === (e = null === (e = null === (e = null == e ? void 0 : e.mediaSourceEntity) || void 0 === e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[t]) || void 0 === e ? void 0 : e.error - }) - } - get updating$() { - return ed([this.sbUpdating$(yu.Variant), this.sbUpdating$(yu.AltAudio)]).pipe(hr(e => e.some(e => e))) - } - get bufferedRangeTuple$() { - return ed([this.selectActive(e => { - return null !== (e = null === (e = null === (e = null === (e = null == e ? void 0 : e.mediaSourceEntity) || void 0 === e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[gu.Variant]) || void 0 === e ? void 0 : e.bufferedRanges) && void 0 !== e ? e : null - }), this.selectActive(e => { - return null !== (e = null === (e = null === (e = null === (e = null == e ? void 0 : e.mediaSourceEntity) || void 0 === e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[gu.AltAudio]) || void 0 === e ? void 0 : e.bufferedRanges) && void 0 !== e ? e : null - })]) - } - getBufferedRangeByType(e) { - return null !== (e = null === (e = this.sourceBufferEntities[e]) || void 0 === e ? void 0 : e.bufferedRanges) && void 0 !== e ? e : [] - } - get combinedBuffer$() { - return this.selectActive(e => { - return null !== (e = null == e ? void 0 : e.bufferedRanges) && void 0 !== e ? e : [] - }) - } - getBufferInfo(n, s) { - var e; - const t = null === (e = this.sourceBufferEntities) || void 0 === e ? void 0 : e.map(e => null == e ? void 0 : e.bufferedRanges), - i = { - buffered: { - start: n, - end: n, - len: 0 - }, - bufferedSegments: [] - }, - a = [i, i]; - return t && t.forEach((e, t) => { - if (e) { - const i = Yp.getBufferedInfo(e, n, s), - r = (null !== (e = this.sourceBufferEntities[t].bufferedSegments) && void 0 !== e ? e : []).filter(e => !(e.endPTS < i.start || e.startPTS > i.end)); - a[t] = { - buffered: i, - bufferedSegments: r - } - } - }), a - } - getCombinedBufferInfo(e, t) { - var i = this.getActive(); - return i ? Yp.getBufferedInfo(i.bufferedRanges, e, t) : null - } - get bufferMonitorInfo() { - var e; - return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.bufferMonitorInfo) && void 0 !== e ? e : null - } - get bufferMonitorThresholds$() { - return this.selectActive(e => { - var t = null == e ? void 0 : e.bufferMonitorInfo; - if (!t) return null; - var { - almostDryWaterLevelSeconds: i, - lowWaterLevelSeconds: r, - highWaterLevelSeconds: e, - maxBufferSeconds: t - } = t; - return { - almostDryWaterLevelSeconds: i, - lowWaterLevelSeconds: r, - highWaterLevelSeconds: e, - maxBufferSeconds: t - } - }).pipe(Is((e, t) => (null == e ? void 0 : e.lowWaterLevelSeconds) === (null == t ? void 0 : t.lowWaterLevelSeconds))) - } - get waterLevelType$() { - return this.selectActive(e => { - return null !== (e = null == e ? void 0 : e.bufferMonitorInfo.waterLevelType) && void 0 !== e ? e : null - }) - } - waterLevelForType(e) { - var t; - return null !== (e = null === (t = null === (t = this.getActive()) || void 0 === t ? void 0 : t.bufferMonitorInfo.waterLevelType) || void 0 === t ? void 0 : t.sbTuple[e]) && void 0 !== e ? e : null - } - waterLevelChangedForType$(t) { - return this.waterLevelType$.pipe(hr(e => null == e ? null : null == t ? e.combined : e.sbTuple[t])) - } - get fellBelowLowWater$() { - return this.waterLevelChangedForType$(yu.Variant).pipe(ha(), hr(([e, t]) => function(e, t) { - return Jp[e] > Jp[t] - }(e, t) && (t === Dp.LowWater || t === Dp.AlmostDry)), bo(this.seekTo$, this.waitingForDisco$), hr(([e, t, i]) => e && !ne(null == t ? void 0 : t.pos) && !i), Ra(!1)) - } - isBufferedToEnd$(s, a = !0) { - return ed([this.combinedBuffer$, this.selectActive(e => e.bufferMonitorInfo).pipe(Kp(), hr(e => a ? e.almostDryWaterLevelSeconds : Math.max(e.almostDryWaterLevelSeconds, e.lowWaterLevelSeconds / 2))), this.seeking$]).pipe(hr(([e, t]) => { - var i = this.minSBDuration; - if (!e || !ne(i) && a) return !1; - e = Yp.getBufferedInfo(e, this.currentTime, s).end; - let r, n; - return n = a ? (r = i, Math.abs(r - e) <= t) : (r = this.mediaElementDuration, r - e <= t), n - }), Is()) - } - needData$(e, n = !1) { - var t = !n; - return ed([this.msReadyState$, this.waterLevelChangedForType$(null), this.isBufferedToEnd$(e, t), this.bufferedRangeTuple$, this.seekTo$, this.mediaElementDuration$]).pipe(Gn(10), hr(([e, t, i, , r]) => { - if ("closed" === e) return !1; - if (n) return !0; - i = null == t || !i && t !== Dp.AboveHighWater, r = this.isIframeRate || !!r; - return i || t !== Dp.AboveHighWater && r - }), tc("needData")) - } - getSourceBufferInfoAction(e, t, i, r) { - var { - currentTime: n, - sourceBufferEntities: s, - msReadyState: a - } = this; - let o = [null, null]; - return !e && i.every(e => !(null != e && e.userInitiated)) ? null : "open" === a && s && null != s[0] ? (o = this.getBufferInfo(n, r), { - position: n, - discoSeqNum: null == t ? void 0 : t.discoSeqNum, - bufferInfoTuple: o, - switchContexts: i - }) : { - position: null == t ? void 0 : t.pos, - discoSeqNum: null == t ? void 0 : t.discoSeqNum, - bufferInfoTuple: o, - switchContexts: i - } - } - get haveEnough() { - var e; - return null !== (e = null === (e = this.getActive()) || void 0 === e ? void 0 : e.haveEnough) && void 0 !== e && e - } - get haveEnough$() { - return this.selectActive(({ - haveEnough: e - }) => e) - } - static likelyToKeepUp(e, t, i) { - return t && i >= e.HAVE_FUTURE_DATA - } - get playbackLikelyToKeepUp() { - return nf.likelyToKeepUp(this.mediaElement, this.haveEnough, this.readyState) - } - get playbackLikelyToKeepUp$() { - return ed([this.haveEnough$, this.readyState$]).pipe(hr(([e, t]) => nf.likelyToKeepUp(this.mediaElement, e, t))) - } - getCurrentWaterLevel(e) { - var t = this.currentTime, - i = null !== (i = null === (i = this.getActive()) || void 0 === i ? void 0 : i.bufferedRanges) && void 0 !== i ? i : []; - return Yp.getBufferedInfo(i, t, e).len - } - getCombinedMediaSourceBufferInfo(e) { - var t = this.currentTime, - [i, r] = null === (r = null === (i = this.getActive()) || void 0 === i ? void 0 : i.mediaSourceEntity) || void 0 === r ? void 0 : r.sourceBufferEntities; - return [Yp.getBufferedInfo(null !== (i = null == i ? void 0 : i.bufferedRanges) && void 0 !== i ? i : [], t, e), Yp.getBufferedInfo(null !== (r = null == r ? void 0 : r.bufferedRanges) && void 0 !== r ? r : [], t, e)] - } - getCurrentWaterLevelByType(e, t) { - var i = this.currentTime, - e = this.sourceBufferEntityByType(e), - e = null !== (e = null == e ? void 0 : e.bufferedRanges) && void 0 !== e ? e : []; - return Yp.getBufferedInfo(e, i, t).len - } - canContinuePlaybackWithoutGap(e, t, i, r) { - if ("LIVE" !== e.type) return !0; - if (!e.ptsKnown) return !1; - var n = this.currentTime, - i = performance.now() + i.avgPlaylistLoadTimeMs + 1e3 * e.targetduration, - t = e.fragments[0].start + (i - t) / 1e3; - let s = this.getCombinedBufferInfo(n, r).end; - return s >= e.fragments[0].start - r && s <= e.fragments[0].start + e.totalduration && (s = e.fragments[0].start + e.totalduration), t <= s - } - get stallInfo$() { - return this.selectActive(e => { - return null !== (e = null == e ? void 0 : e.stallInfo) && void 0 !== e ? e : null - }) - } - get textTracks() { - return this.mediaElement.textTracks - } - get textTracksCreated$() { - return this.selectActive(e => null == e ? void 0 : e.textTracksCreated) - } - get mediaOptionParsedSubtitleRecord() { - var e; - return null === (e = this.getActive()) || void 0 === e ? void 0 : e.mediaOptionParsedSubtitleRecord - } - getParsedSubtitleRecordsForMediaOption(e) { - return this.mediaOptionParsedSubtitleRecord ? this.mediaOptionParsedSubtitleRecord[e] || {} : null - } - } - class sf { - constructor(e, t, i, r) { - this.mediaSink = e, this.media = t, this.logger = r, this.useCustomMediaFunctions = i.useCustomMediaFunctions, this.overridePlaybackRate = i.overridePlaybackRate - } - install() { - const e = this.media; - e && (this.useCustomMediaFunctions && e && e.play && e.pause && (e.originalPlay || (e.originalPlay = e.play.bind(e)), e.originalPause || (e.originalPause = e.pause.bind(e)), e.play = () => (this.mediaSink.checkForReplay(), this.mediaSink.desiredRate = 1, 0 < e.currentTime && !e.paused && !e.ended && 2 < e.readyState ? Promise.resolve() : new Promise((e, t) => { - this.pendingPlayPromises || (this.pendingPlayPromises = []), this.pendingPlayPromises.push({ - resolve: e, - reject: t - }) - })), e.pause = () => { - this.mediaSink.desiredRate = 0 - }), "function" == typeof HTMLMediaElement && this.overridePlaybackRate && Object.defineProperty(e, "playbackRate", { - enumerable: !0, - configurable: !0, - get: function() { - return 1 - }, - set: function(e) { - Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, "playbackRate").set.call(this, e) - } - }), this.playPromise = null, this.expectPauseEvent = this.expectPlayEvent = !1) - } - uninstall() { - const e = this.media; - e && (e.originalPlay && (e.play = e.originalPlay, delete e.originalPlay), e.originalPause && (e.pause = e.originalPause, delete e.originalPause), this.overridePlaybackRate && (e.playbackRate = 1, delete e.playbackRate)), this.playPromise = null, this.expectPauseEvent = this.expectPlayEvent = !1 - } - play() { - var e; - this.media && (e = this.mediaSink.flushing, this.playPromise || e ? this.logger.warn(`Ignoring play command playPromise/flushing ${Boolean(this.playPromise)}/${e}`) : (this.expectPlayEvent = this.expectPlayEvent || this.media.paused, this.playPromise = this._mediaPlayInternal(), this.playPromise && this.playPromise.then(function() { - this.playPromise = null, this._handlePendingPlayPromises(null) - }.bind(this)).catch(function(e) { - this.playPromise = null, this.expectPlayEvent = !1, this._handlePendingPlayPromises(e || new Error("Play rejected for unknown reason")), "NotAllowedError" === (null == e ? void 0 : e.name) ? (this.logger.warn("play() not allowed, going back to rate 0"), this.mediaSink.desiredRate = 0) : this.logger.error(`play() error: ${null==e?void 0:e.message}`) - }.bind(this)))) - } - pause() { - this.media && (this.playPromise ? this.playPromise.then(() => { - var e = this.mediaSink.mediaQuery; - (0 === this.mediaSink.desiredRate || e.seeking && !e.playbackLikelyToKeepUp) && this._mediaPauseInternal() - }).catch(e => { - this.logger.error(`Promise error in pause(): ${e.message}`) - }) : this._mediaPauseInternal()) - } - _handlePendingPlayPromises(t) { - var e, i = null === (e = this.pendingPlayPromises) || void 0 === e ? void 0 : e.length; - if (t) - for (let e = 0; e < i; e++) this.pendingPlayPromises[e].reject(t); - else - for (let e = 0; e < i; e++) this.pendingPlayPromises[e].resolve(); - this.pendingPlayPromises = [] - } - _mediaPlayInternal() { - return (this.media.originalPlay || this.media.play.bind(this.media))() - } - _mediaPauseInternal() { - return this.expectPauseEvent = this.expectPauseEvent || !this.media.paused, (this.media.originalPause || this.media.pause.bind(this.media))() - } - } - class af extends Error {} - class of extends p { - constructor(e, t, i, r, n) { - super(L, e, t, i, r), this.sbType = n, this.response = r - } - } - class lf extends of { - constructor(e, t, i, r) { - super("bufferAddCodecError", !1, e, t, i), this.mediaOptionId = r, this.mediaOptionType = Vu(this.sbType) - } - } - class df extends of { - constructor(e, t, i, r, n, s) { - super(e, t, i, r, n), this.isTimeout = s, this.mediaOptionType = Vu(this.sbType) - } - } - class uf extends df { - constructor(e, t, i, r) { - super("bufferFullError", !1, e, t, i, !1), this.maxTotalBytes = r - } - } - class cf extends df { - constructor(e, t, i) { - super(n, !1, e, t, i, !0) - } - } - class hf extends df { - constructor(e, t, i, r) { - super(n, !1, e, t, i, !1), this.mediaOptionId = r, this.mediaOptionType = Vu(this.sbType) - } - } - class pf extends p { - constructor(e, t, i, r, n, s, a = NaN) { - super(L, e, t, i, r), this.stallType = n, this.bufferLen = s, this.nudgePosition = a, this.response = r - } - } - class ff extends $t { - constructor(n, e, s, a, o, l, d, u, c) { - super(e => { - const t = Oc(l), - i = u.child({ - sb: o - }); - n.setSourceBufferEntity(o, d), d.mimeType.includes("audio/mpeg") && (this.updateMp3Timestamps = !0); - const r = an(t.event("updatestart").pipe(Za(() => { - n.setSourceBufferUpdating(o) - })), t.event("updateend").pipe(ji(tr), Za(() => { - var e = Yp.timeRangesToBufferedRange(l.buffered), - t = Yp.timeRangesToBufferedRange(s.buffered); - n.setBufferedRangesUpdated(o, e, t, !1, c) - })), t.event("error").pipe(Za(() => { - n.setSourceBufferError(o, "Got source buffer error") - }))).pipe(La(() => Ii)).subscribe(e); - return () => { - r.unsubscribe(); - try { - "open" === a.readyState && l.abort(), a.removeSourceBuffer(l) - } catch (e) { - i.error(`Error aborting SourceBuffer on unsubscribe: ${e.message}`) - } - } - }), this.mediaElementStore = n, this.mediaElementQuery = e, this.mediaElement = s, this.type = o, this.sourceBuffer = l, this.config = c, this.updateMp3Timestamps = !1 - } - get buffered() { - return this.sourceBuffer.buffered - } - appendBuffer(e, t) { - return Zr(() => this.sourceBuffer.updating ? this._waitForUpdateEndOrError().pipe(La(() => this.appendBuffer(e, t))) : this._appendBufferAsync(e, t)) - } - _appendBufferAsync(e, t) { - let i = NaN, - r = null; - const n = ("startPTS" in t ? t.frag : t).mediaOptionId; - try { - "startPTS" in t && (r = { - startPTS: t.startPTS, - endPTS: t.endPTS, - bytes: t.bytes, - frag: Object.assign({}, t.frag) - }), this.mediaElementStore.setInflightSegment(this.type, r), i = performance.now(), this.sourceBuffer.appendBuffer(e) - } catch (e) { - return 22 !== e.code ? (this.mediaElementStore.setInflightSegment(this.type, null), this.mediaElement.error ? Vi(new hf(e.message, $.VideoDecoderBadDataErr, this.type, n)) : Vi(e)) : (this.mediaElementStore.setBufferedRangesUpdated(this.type, Yp.timeRangesToBufferedRange(this.sourceBuffer.buffered), Yp.timeRangesToBufferedRange(this.mediaElement.buffered), !0, this.config), Vi(new uf(e.message, $.AllocationFailed, this.type, this.maxTotalBytes))) - } - return this._waitForUpdateEndOrError().pipe(hr(() => ({ - startAppend: i, - endAppend: performance.now(), - bytesAppend: e.byteLength - })), So(1e4), Vn(e => { - throw e instanceof dr ? (this.sourceBuffer.abort(), e = new cf("Append took longer than 10000ms", $.InternalError, this.type)) : e instanceof of && (e = new hf("Decode error", $.VideoDecoderBadDataErr, this.type, n)), e - })) - } - remove(e, t) { - return this._waitForUpdateEndOrError().pipe(La(this._removeAsync.bind(this, e, t))) - } - _removeAsync(e, t) { - try { - this.sourceBuffer.remove(e, t) - } catch (e) { - return Vi(new of (r, !1, e.message, $.InternalError, this.type)) - } - return this._waitForUpdateEndOrError() - } - abort() { - try { - this.sourceBuffer.abort() - } catch (e) { - return Vi(new of (r, !1, e.message, $.InternalError, this.type)) - } - return this._waitForUpdateEndOrError() - } - get updating() { - return this.sourceBuffer.updating - } - get timestampOffset() { - return this.sourceBuffer.timestampOffset - } - set timestampOffset(e) { - this.sourceBuffer.timestampOffset = e - } - get gotQuotaExceeded() { - var e; - return null !== (e = null === (e = this.mediaElementQuery.sourceBufferEntityByType(this.type)) || void 0 === e ? void 0 : e.gotQuotaExceeded) && void 0 !== e && e - } - get bufferedSegments() { - var e; - return null !== (e = null === (e = this.mediaElementQuery.sourceBufferEntityByType(this.type)) || void 0 === e ? void 0 : e.bufferedSegments) && void 0 !== e ? e : [] - } - get totalBytes() { - var e; - return null !== (e = null === (e = this.mediaElementQuery.sourceBufferEntityByType(this.type)) || void 0 === e ? void 0 : e.totalBytes) && void 0 !== e ? e : 0 - } - get maxTotalBytes() { - var e = null !== (e = null === (e = this.mediaElementQuery.sourceBufferEntityByType(this.type)) || void 0 === e ? void 0 : e.maxTotalBytes) && void 0 !== e ? e : 1 / 0; - return this.gotQuotaExceeded ? e : 1 / 0 - } - _waitForUpdateEndOrError() { - return this.sourceBuffer.updating && this.mediaElementStore.setSourceBufferUpdating(this.type), this.mediaElementQuery.sbUpdating$(this.type).pipe(ln(e => !1 === e), bo(this.mediaElementQuery.sbError$(this.type)), hr(([, e]) => { - if (e) throw new of (r, !1, "Got error during sourceBuffer operation", $.InternalError, this.type) - }), Ds(1)) - } - } - class mf extends $t { - constructor(a, o, e, l, t) { - super(e => { - const t = Oc(l), - i = an(t.event("sourceopen"), t.event("sourceclose"), t.event("sourceended")).pipe(Za(e => { - e = (null !== (e = null == e ? void 0 : e.target) && void 0 !== e ? e : l).readyState; - o.msReadyState = e - })), - r = this.sourceBuffers$.pipe(La(e => e ? an(...e.filter(e => null != e)) : Ii)), - n = an(i, r).pipe(La(() => Ii)).subscribe(e), - s = URL.createObjectURL(l); - return a.src = s, o.setMediaSourceEntity(s, l.readyState), () => { - n.unsubscribe(), URL.revokeObjectURL(s), a.src === s && (a.removeAttribute("src"), a.load(), o.setMediaSourceEntity(null)), this.sourceBuffers$.next(null) - } - }), this.mediaElement = a, this.mediaElementStore = o, this.mediaElementQuery = e, this.mediaSource = l, this.logger = t, this.sourceBuffers$ = new yi(null) - } - get readyState() { - return this.mediaSource.readyState - } - set duration(e) { - this.mediaSource.duration = e - } - get duration() { - return this.mediaSource.duration - } - endOfStream(e) { - this.mediaSource.endOfStream(e) - } - createSourceBuffers(e, a) { - const o = this.mediaSource; - al(() => { - try { - const s = [null, null]; - e.forEach((t, i) => { - if (t) { - var { - mimeType: r, - mediaOptionId: n - } = t; - let e; - try { - e = o.addSourceBuffer(r) - } catch (t) { - throw new lf(t.message, $.IncompatibleAsset, i, n) - } - s[i] = new ff(this.mediaElementStore, this.mediaElementQuery, this.mediaElement, this.mediaSource, i, e, t, this.logger, a) - } - }), this.sourceBuffers$.next(s) - } catch (e) { - if (!(e instanceof p)) throw new af(`error initializing sourcebuffers ${e.message} readyState=${o.readyState}`); - throw e - } - }) - } - get needSourceBuffers() { - return null == this.sourceBuffers$.value || null == this.sourceBuffers$.value[0] - } - get sourceBuffers() { - return this.sourceBuffers$.value - } - getSourceBufferByType(e) { - var t = this.sourceBuffers$.value; - return t ? t[e] : null - } - updateLiveSeekableRange(e, t) { - const i = this.mediaSource; - null != i && i.setLiveSeekableRange && "open" === (null == i ? void 0 : i.readyState) && i.setLiveSeekableRange(e, t) - } - clearLiveSeekableRange() { - const e = this.mediaSource; - null != e && e.clearLiveSeekableRange && "open" === (null == e ? void 0 : e.readyState) && e.clearLiveSeekableRange() - } - } - - function gf(e, c, t) { - const { - lowBufferThreshold: h, - lowBufferWatchdogPeriod: p, - highBufferWatchdogPeriod: f, - seekWatchdogPeriod: m - } = t, i = ed([c.desiredRate$, c.ended$, c.combinedBuffer$, c.seekTo$]).pipe(hr(e => { - var t, [i, r, n, s] = e; - return t = c.currentTime, e = i, i = r, r = n, n = isFinite(null == s ? void 0 : s.pos), s = r.some(e => e.start <= t && e.end > t), !(1 !== e || i || 0 === r.length || n && !s) - }), Is()), r = c.combinedBuffer$.pipe(hr(() => c.getCurrentWaterLevel(0) <= t.lowBufferThreshold || !c.haveEnough ? Cp.LowBuffer : Cp.HighBuffer), Is()), n = ed([i, c.seekTo$, c.gotPlaying$, r]).pipe(La(e => { - var [t, i, r] = e; - if (!t) return $i(null); - var n, s, a, o, l, d, u, e = c.nudgeCount, - t = m + +e; - return isFinite(null == i ? void 0 : i.pos) || !r ? (n = c, s = performance.now(), t = t, a = h, bn(1e3 * t).pipe(hr(() => { - var e = n.currentTime, - t = n.getCombinedBufferInfo(e, 0); - return yf(Cp.Seek, e, s, t, a, n.haveEnough) - }))) : (o = c, l = p, d = f + +e, u = h, an($i(o.currentTime), o.timeupdate$).pipe(La(e => { - const t = performance.now(), - i = o.getCombinedBufferInfo(e, 0); - let r, n; - return r = i.len <= u || !o.haveEnough ? (n = l, Cp.LowBuffer) : (n = d, Cp.HighBuffer), bn(Math.max(100, 1e3 * n)).pipe(hr(() => e < o.currentTime ? null : yf(r, e, t, i, u, o.haveEnough))) - }))) - })), s = n.pipe(Kp(), bo(c.combinedBuffer$), La(([]) => ed([c.seeking$, c.paused$])), La(([e, t]) => e || t ? Ii : c.timeupdate$.pipe(ha(), ln(([e, t]) => ne(e) && ne(t) && e < t), Ds(1))), hr(() => null)); - return an(n, s) - } - - function yf(e, t, i, r, n, s) { - var a = performance.now() - i; - return { - type: e, - isLowBufferStall: r.len <= n || !s, - tstalled: i, - stallDurationMs: a, - currentTime: t - } - } - class vf extends $t { - constructor(w, A, e, t, i, r, n) { - super(e => { - const t = this.config, - i = A.startMediaSession(w, t.maxBufferLength, t.almostDryBufferSec, t.defaultTargetDuration), - r = Sf(w, A, this._mediaQuery, this, this.hlsGapless, t, this.logger, this.rtcService), - n = this.mediaSource$.pipe(La(e => e || Ii)), - s = this._mediaQuery.seekTo$.pipe((u = w, c = this._mediaQuery, p = (h = this).config, f = (f = this.logger).child({ - name: "seek" - }), e => e.pipe(Za(e => {}), ln(e => e && ne(e.pos)), La(e => c.readyState$.pipe(ln(e => e >= u.HAVE_METADATA), Ds(1), Zs(e), La(({ - pos: e, - fromEvent: t - }) => (h.checkForInconsistentStoreBufferRangesAndUpdate(), u.paused || h.pause(), t || (u.currentTime = e), Gu(c.haveEnough$, e => e).pipe(Zs({ - pos: e, - fromEvent: t - })))), La(({ - pos: e, - fromEvent: t - }) => { - var i = c.getCombinedBufferInfo(e, 0), - r = i.nextStart; - return (!t || p.nudgeFromEventSeek) && 0 === i.len && ne(r) && e < r && r - e <= p.maxSeekHole ? (h.seekTo = r, Ii) : $i(e) - }), bo(c.desiredRate$), hr(([, e]) => { - u.paused && 0 !== e && h.play() - }), $a(Ii), Vn(e => (f.error(`error during seek ${e.message}`), Ii))))))), - a = this._mediaQuery.desiredRate$.pipe((o = w, l = this._mediaQuery, d = this, e => e.pipe(bo(l.seekTo$), La(([e, t]) => ne(null == t ? void 0 : t.pos) ? Ii : 0 === e ? (o.paused || d.pause(), Ii) : Gu(l.haveEnough$, e => e).pipe(Za(() => { - o.paused && d.play() - }))), $a(Ii)))); - var o, l, d, u, c, h, p, f; - this.liveSeekableWindow = { - start: NaN, - end: NaN - }, this.mediaFunctions = this.mediaFunctions || new sf(this, w, t, this.logger), this.mediaFunctions.install(); - var m, g, y, v, S, b, T, E, I = an(gf(this.logger, this._mediaQuery, this.config).pipe(Za(e => { - A.setStallInfo(e) - })), this.mediaQuery.stallInfo$.pipe((g = A, y = (m = this).config, v = this.logger, e => e.pipe(_s(e => e ? function(i, e, r, n) { - const s = i.mediaQuery; - return an(ed([s.seekTo$, s.nudgeTarget$]).pipe(ln(([e, t]) => e && ne(e.pos) && ne(t) && (e.pos < t || e.pos - t > r.maxSeekHole)), Zs(null)), s.stallInfo$).pipe(bo(s.desiredRate$), ji(tr), hr(([c, e], t) => { - if (!c) return NaN; - var h = s.getCombinedBufferInfo(c.currentTime, 0), - e = Wp(e); - return function(e, t, i, r, n) { - var { - type: s, - isLowBufferStall: a, - currentTime: o - } = c, l = h.len, d = t.maxSeekHole; - let u = NaN; - if (a) { - const t = h.nextStart - o <= d ? h.nextStart : 1 / 0; - ne(t) ? u = t : e.mediaQuery.msDuration - o < .1 && (u = o + .1) - } else if (n < t.nudgeMaxRetry) u = o + t.nudgeOffset; - else { - if (!r) throw i.error(`still stuck in high buffer @${o} after ${t.nudgeMaxRetry}, raise fatal error`), new pf("bufferStalledError", !0, "got fatal buffer error", $.VideoDecoderBadDataErr, s, l); - i.error(`still stuck in high buffer @${o} after ${t.nudgeMaxRetry}, non fatal in iframeMode`) - } - return ne(u) && e.nudgeSeek(u, n + 1), u - }(i, r, n, e, t) - }), Wa(e => ne(e)), Vs(() => { - e.setNudgeInfo(null) - })) - }(m, g, y, v) : $i(NaN)))))); - an(r, n, s, a, I, (S = this.mediaQuery, b = A, I = t.maxBufferHole, an((E = I, ed([(T = S).bufferMonitorThresholds$, T.combinedBuffer$, T.seeking$]).pipe(hr(([e]) => null == e ? null : tf(T, E)))), rf(S, I)).pipe(ji(tr), Za(({ - combined: e, - sbTuple: t - }) => { - b.updateWaterLevels(e, t) - })))).pipe($a(Ii), Vs(() => { - A.remove(i), this.mediaFunctions.uninstall(), this.mediaFunctions = void 0 - })).subscribe(e) - }), this.mediaElement = w, this.mediaElementStore = A, this.config = e, this.hlsGapless = t, this.logger = i, this.teardownWG$ = r, this.rtcService = n, this.mediaSource$ = new yi(null), this.mediaKeysMutex = new zp, this._mediaQuery = new nf(w, A), this.logger = i.child({ - name: "mse" - }), this.createId3Track(w), this.mediaFunctions = new sf(this, w, e, this.logger) - } - get mediaSourceAdapter() { - return this.mediaSource$.value - } - get sourceBuffers() { - var e; - return null !== (e = null === (e = this.mediaSourceAdapter) || void 0 === e ? void 0 : e.sourceBuffers) && void 0 !== e ? e : [] - } - get needSourceBuffers() { - return !this.sourceBuffers[0] - } - get mediaQuery() { - return this._mediaQuery - } - sourceBuffersBufferedRangeByType(e) { - var t, e = null === (t = null === (t = this.mediaSourceAdapter) || void 0 === t ? void 0 : t.sourceBuffers) || void 0 === t ? void 0 : t[e]; - return e ? Yp.timeRangesToBufferedRange(e.sourceBuffer.buffered) : null - } - createId3Track(e) { - this.id3Track = e.addTextTrack("metadata", "id3"), this.id3Track.mode = "hidden" - } - checkForInconsistentStoreBufferRangesAndUpdate() { - var e = Yp.timeRangesToBufferedRange(this.mediaElement.buffered), - t = this.sourceBuffersBufferedRangeByType(yu.Variant), - i = this.sourceBuffersBufferedRangeByType(yu.AltAudio), - r = null !== (n = null === (r = this.mediaQuery.sourceBufferEntityByType(yu.Variant)) || void 0 === r ? void 0 : r.bufferedRanges) && void 0 !== n ? n : null, - n = null !== (n = null === (n = this.mediaQuery.sourceBufferEntityByType(yu.AltAudio)) || void 0 === n ? void 0 : n.bufferedRanges) && void 0 !== n ? n : null; - this.shouldUpdateStoreValues(t, r) && (this.logger.warn(`[${Uu[yu.Variant]}] SourceBuffer's loaded bufferedRanges ${JSON.stringify(t)} & mediaElementStore's bufferedRanges ${JSON.stringify(r)} are out of sync!`), this.updateMediaElementStoreBufferedRanges(e, yu.Variant)), this.shouldUpdateStoreValues(i, n) && (this.logger.warn(`[${Uu[yu.AltAudio]}] SourceBuffer's loaded bufferedRanges ${JSON.stringify(i)} & mediaElementStore's bufferedRanges ${JSON.stringify(n)} are out of sync!`), this.updateMediaElementStoreBufferedRanges(e, yu.AltAudio)) - } - shouldUpdateStoreValues(e, i) { - return !(null == e && null == i || (null == e ? void 0 : e.length) == (null == i ? void 0 : i.length) && !e.find(t => { - var e = Ku.search(i, e => t.start >= e.start && t.end <= e.end ? 0 : t.end < e.start ? -1 : 1); - return null == e || e.start != t.start || e.end != t.end || void 0 - })) - } - updateMediaElementStoreBufferedRanges(e, t) { - var i = this.sourceBuffersBufferedRangeByType(t); - i && !this.mediaQuery.sbUpdating(t) && this.mediaElementStore.setBufferedRangesUpdated(t, i, e, !1, this.config) - } - destroyMediaSource() { - this.mediaSource$.next(null) - } - makeMediaSource() { - return new MediaSource - } - openMediaSource(t) { - al(() => { - var e; - t ? (e = new mf(this.mediaElement, this.mediaElementStore, this.mediaQuery, t, this.logger), this.mediaSource$.next(e)) : this.mediaSource$.next(null) - }) - } - createSourceBuffers(e) { - const t = this.mediaSource$.value; - if (!t) throw new Error("createSourceBuffers empty mediaSource"); - t.createSourceBuffers(e, this.config) - } - _waitForMediaSourceOpen(i) { - const r = this.mediaQuery.mediaSourceEntity.objectUrl; - return ed([this.mediaQuery.msReadyState$, this.mediaQuery.msObjectUrl$]).pipe(La(([e, t]) => t !== r ? $i(null) : "open" === e || "ended" === e ? $i(i) : Ii)) - } - get appendOrder() { - return this.mediaQuery.isIframeRate ? [yu.Variant, yu.AltAudio] : [yu.AltAudio, yu.Variant] - } - clearFlush(e) { - e.forEach(e => { - e && (e.dataSeg.flushBeforeAppend = { - start: 0, - end: 0 - }) - }) - } - getSwitchPosition(e) { - return e.reduce((e, t) => { - t = t ? t.dataSeg.switchPosition : void 0; - return ne(t) ? ne(e) ? Math.min(e, t) : t : e - }, void 0) - } - checkForReplay() { - var e = this.mediaElement; - e.paused && !e.seeking && e.duration && e.currentTime && e.currentTime >= e.duration - this.config.maxTotalDurationTolerance && (this.seekTo = 0) - } - resetMediaSourceIfNeeded(r) { - const n = this["mediaQuery"], - e = n["sourceBufferEntities"], - t = n.getActive()["expectedSbCount"]; - if (!e || this.needSourceBuffers) return this._waitForMediaSourceOpen(r); - const s = function(e, s, t, i) { - const r = s.filter(e => Boolean(e)).length, - n = e.filter(e => Boolean(e)).length, - l = [null, null]; - e.forEach((e, t) => { - var i, r, n, s, a, o; - e && (s = e["offsetTimestamp"], a = S(s), i = e.initSeg.mimeType, { - audioCodec: r, - videoCodec: n - } = e.initSeg.initParsedData, o = e["dataSeg"], s = S(o.startPts) - a, a = S(o.endPts) - a, o = o.discoSeqNum, l[t] = { - audioCodec: r, - videoCodec: n, - mimeType: i, - startPTSSec: s, - endPTSSec: a, - discoSeqNum: o, - mediaOptionId: e.initSeg.mediaOptionId - }) - }); - let a = r === t, - o = n === t; - if (1 === n && n < t && 0 !== r) { - const t = e[gu.Variant] ? gu.Variant : gu.AltAudio, - h = 1 - t, - r = l[t], - n = l[h] = function(e, i, r, n, t, s) { - const a = null == e ? void 0 : e.bufferedSegments; - if (!a) return s.warn("getMatchingInfo trying to query null sbEntity"), null; - s = a.find(e => { - var t = e.frag.discoSeqNum === n, - e = Math.max(i, e.startPTS) < Math.min(r, e.endPTS); - return t && e - }); - if (null == s) return null; { - const { - audioCodec: i, - videoCodec: r, - mimeType: o - } = e; - return { - mimeType: o, - audioCodec: i, - videoCodec: r, - startPTSSec: s.startPTS, - endPTSSec: s.endPTS, - discoSeqNum: n, - mediaOptionId: t - } - } - }(s[h], r.startPTSSec, r.endPTSSec, r.discoSeqNum, r.mediaOptionId, i); - if (!n) - if (null !== (e = null === (e = e[gu.Variant]) || void 0 === e ? void 0 : e.dataSeg) && void 0 !== e && e.iframe && t === gu.Variant && l[t]) { - const p = s[t].videoCodec, - h = l[t].videoCodec; - if (a = a && (p === h || be.isCompatibleVideoCodec(p, h)), a) return { - compatible: a, - boundary: NaN, - allowance: NaN, - discoSeqNum: l[t].discoSeqNum - } - } else i.warn(`${Nu[t]} No matching frag found ${ae(r)} buffered=${ae(s[h].bufferedSegments.map(e=>{var{mediaSeqNum:t,discoSeqNum:i}=e.frag;return{mediaSeqNum:t,discoSeqNum:i,startPTS:e.startPTS,endPTS:e.endPTS}}))}`); - o = null != n - } - let d = NaN, - u = NaN, - c = NaN; - return o && l.forEach((e, t) => { - if (!e) return null; - ne(c) ? c !== e.discoSeqNum && (c = NaN) : c = e.discoSeqNum; - t = s[t]; - if (t) { - const s = t.audioCodec, - i = t.videoCodec, - { - audioCodec: r, - videoCodec: n - } = e; - a = a && (i === n || be.isCompatibleVideoCodec(i, n)), a = a && (s === r || be.isCompatibleAudioCodec(s, r)) - } else a = !1; - d = ne(d) ? (u = Math.abs(e.startPTSSec - d), Math.max(e.startPTSSec, d)) : (u = 0, e.startPTSSec) - }), { - compatible: a && o, - boundary: d, - allowance: u, - discoSeqNum: c - } - }(r, e, t, (this.config.maxBufferHole, this.logger)); - if (s.compatible) return this._waitForMediaSourceOpen(r); - let i = s.boundary; - const a = s.allowance, - o = this.getSwitchPosition(r); - if (ne(o) && (i = o), !ne(i)) return this.logger.warn("not enough info #disco"), $i(null); - const l = fn(Gu(an($i(n.currentTime), n.timeupdate$), e => e >= i), Gu(n.stallInfo$.pipe(hr(e => { - return null !== (e = null == e ? void 0 : e.currentTime) && void 0 !== e ? e : NaN - })), e => e >= i - a - this.config.discontinuitySeekTolerance)); - return this.mediaElementStore.waitingForDisco = !0, l.pipe(Zs(i), La(e => { - performance.now(); - const t = n.currentTime, - i = this.msDuration; - return this.resetMediaSource(Math.max(t, e), s.discoSeqNum), this._waitForMediaSourceOpen(r).pipe(Za(() => { - performance.now(), this.msDuration = i - })) - }), Vs(() => { - this.mediaElementStore.waitingForDisco = !1 - })) - } - resetMediaSource(e = NaN, t) { - var i; - ne(e) || (e = null !== (i = null === (i = this.mediaQuery.seekTo) || void 0 === i ? void 0 : i.pos) && void 0 !== i ? i : this.mediaQuery.currentTime), ne(t) || (t = null === (i = this.mediaQuery.seekTo) || void 0 === i ? void 0 : i.discoSeqNum), 0 < this.sourceBuffers.length && (this.openMediaSource(this.makeMediaSource()), this.setSeekToWithDiscontinuity(e, t)) - } - setExpectedSbCount(e) { - this.mediaElementStore.expectedSbCount = e - } - appendInitSegments(l, d) { - const { - mediaQuery: e, - mediaElementStore: u, - sourceBuffers: c - } = this, h = e["sourceBufferEntities"]; - if (!c) throw new Error("appendInitSegments: null sourceBuffers"); - if (!h) throw new Error("appendInitSegments: null sourceBufferEntities"); - var t = this.appendOrder.map(t => { - if (l[t]) { - const i = c[t], - r = l[t], - n = h[t], - s = r["initSeg"]; - if (!n) throw new Error(`appendInitSegments: sb[${Uu[t]}] null currentSbEntity`); - if (!i) throw new Error(`appendInitSegments: sb[${Uu[t]}] null source buffer`); - const a = n.initSegmentInfo, - o = function() { - var { - itemId: e, - mediaOptionId: t, - discoSeqNum: i, - keyTagInfo: r - } = s; - return { - itemId: e, - mediaOptionId: t, - discoSeqNum: i, - keyId: je(null == r ? void 0 : r.keyId) - } - }(); - if ((e = o) && a && e.itemId === a.itemId && e.mediaOptionId === a.mediaOptionId && e.discoSeqNum === a.discoSeqNum && e.keyId === a.keyId) return $i(null); - var e = Vu(t); - return i.appendBuffer(s.data, s).pipe(Za(e => { - u.setInitSegmentEntity(t, o) - }), d(i, e, s.mediaOptionId, this.config, this.mediaQuery)) - } - }).filter(e => Boolean(e)); - return 0 === t.length ? $i(null) : en(t) - } - appendDataSegments(m, g) { - var e = this.appendOrder.map(e => { - const t = m[e], - { - mediaQuery: i, - sourceBuffers: r - } = this, - n = i["sourceBufferEntities"]; - if (!r) throw new Error("appendDataSegments: null sourceBuffers"); - if (!n) throw new Error("appendDataSegments: null sourceBufferEntities"); - if (!t) return null; - const s = r[e], - a = m[e], - o = n[e]; - if (!o) throw new Error("appendDataSegments: null currentSbEntity"); - const l = o.initSegmentInfo, - d = a["dataSeg"]; - if (!l) throw new Error(`appendDataSegments: sb[${Uu[e]}] null currentInitSegmentInfo`); - if (!o) throw new Error(`appendDataSegments: sb[${Uu[e]}] null currentSbEntity`); - if (!s) throw new Error(`appendDataSegments: sb[${Uu[e]}] null source buffer`); - const u = s.timestampOffset, - c = { - startPTS: S(d.startPts) + u, - endPTS: S(d.endPts) + u, - firstKeyframePts: d.firstKeyframePts ? S(d.firstKeyframePts) + u : void 0, - bytes: d.data2 ? d.data1.byteLength + d.data2.byteLength : d.data1.byteLength, - frag: { - itemId: d.itemId, - mediaOptionId: d.mediaOptionId, - mediaSeqNum: d.mediaSeqNum, - discoSeqNum: d.discoSeqNum, - keyTagInfo: d.keyTagInfo, - isLastFragment: d.isLastFragment, - iframe: d.iframe, - framesWithoutIDR: d.framesWithoutIDR, - dropped: d.dropped - } - }, - h = Vu(e); - let p = Wu; - var f = t.dataSeg.flushBeforeAppend; - return f && f.start !== f.end && (p = this.flushData(e, f.start, f.end)), p.pipe(La(() => $i(d.data1, d.data2).pipe(Kp()).pipe(Wn(e => s.appendBuffer(e, c).pipe(g(s, h, d.mediaOptionId, this.config, this.mediaQuery))))), Za(() => { - i.getBufferedRangeByType(e) - })) - }).filter(e => Boolean(e)); - return 0 === e.length ? $i(null) : en(e) - } - setStoreSbTimeoffsets(s) { - const { - mediaElementStore: a, - sourceBuffers: e - } = this; - e.forEach((t, i) => { - if (t && s[i]) { - var { - offsetTimestamp: r, - dataSeg: n - } = s[i], n = S(n.startPts); - let e = -1 * S(r); - t.updateMp3Timestamps && .1 < Math.abs(t.timestampOffset - n) && (e = n + e), t.timestampOffset !== e && (t.timestampOffset = e, a.setTimestampOffset(i, t.timestampOffset)) - } - }) - } - adjustJaggedStart(e) { - const { - mediaQuery: t, - logger: n - } = this, { - sourceBufferEntities: i, - currentTime: r, - seekTo: s - } = t, a = e.reduce((e, t) => null != t && t.dataSeg.endPts ? Math.min(b(t.dataSeg.endPts, t.offsetTimestamp), e) : e, Number.POSITIVE_INFINITY); - if (!i) throw new Error("appendSourceBufferData null currentSbEntity"); - const o = (null == s ? void 0 : s.pos) || r; - let l = NaN; - i.forEach((e, t) => { - if (e) { - e = Yp.getBufferedInfo(e.bufferedRanges, o, 0); - if (0 === e.len) { - const i = e["nextStart"], - r = ne(this.config.jaggedSeekTolerance) ? this.config.jaggedSeekTolerance : 0; - n.warn(`sb[${Uu[t]}] jagged start: ${i} appendEndTime=${a} current=${l} tolerance=${r}`), ne(i) && (!ne(l) || i - l > r) && (l = i) - } - } - }), ne(l) && a > l && (n.warn(`[seek] jagged start, adjusting currentTime:${r.toFixed(3)} seekTo=${null===(e=null==s?void 0:s.pos)||void 0===e?void 0:e.toFixed(3)}->${l} appendEndTime=${a}`), this.seekTo = l) - } - addCues(e, t) { - const i = this.mediaElement.textTracks[e]; - i && t.forEach(e => { - i.addCue(e) - }) - } - _flushInternal(e, t, i) { - return Zr(() => e.remove(t, i)).pipe(Za(() => {})) - } - flushAll(i, r, n = !1) { - return 0 === this.sourceBuffers.length ? Wu : en(this.sourceBuffers.map((e, t) => e ? this.flushData(t, i, r, n) : Wu)).pipe(Zs(void 0)) - } - flushData(o, l, d, u = !1) { - const { - mediaQuery: t, - logger: c - } = this; - return Gu(t.updating$, e => !1 === e).pipe(La(() => { - var e = t["sourceBufferEntities"], - e = e[o]; - null != e && e.updating && this.logger.warn(`trying to flush while updating ${o}`); - const r = this.sourceBuffers[o]; - if (!r) return Wu; - let n, s, a = Wu; - e = -1 === navigator.userAgent.toLowerCase().indexOf("firefox"); - if (this.flushing = !0, e) return this._flushInternal(r, l, d); - for (let i = 0; i < r.buffered.length; i++) { - let e, t; - n = r.buffered.start(i), s = r.buffered.end(i), t = d === 1 / 0 ? (e = l, d) : (e = Math.max(n, l), Math.min(s, d)), Math.min(t, s) > e && (u || .5 < Math.min(t, s) - e) ? a = a.pipe($a(this._flushInternal(r, e, t))) : c.warn(`ignoring sb[${Uu[o]}] flush ${e},${t}`) - } - return a - }), Vs(() => { - this.flushing = !1 - })) - } - static convertInitSegToCompatInfo(e) { - return { - mimeType: e.mimeType, - audioCodec: e.initParsedData.audioCodec, - videoCodec: e.initParsedData.videoCodec, - startPTSSec: void 0, - endPTSSec: void 0, - discoSeqNum: e.discoSeqNum, - mediaOptionId: e.mediaOptionId - } - } - static combineAppendDataInfoWithCompatInfo(e, t, i, r = 0) { - const n = [...t]; - e.forEach((e, t) => null != e && e.initSeg ? n[t] = vf.convertInitSegToCompatInfo(e.initSeg) : null); - t = n[yu.Variant].videoCodec, e = Hp(t); - if (i && i.has(e)) { - const s = i.get(e); - n[yu.Variant].mimeType = n[yu.Variant].mimeType.replace(t, s), n[yu.Variant].videoCodec = s - } - return n - } - convertSourceBufferEntitiesToCompatInfo(e) { - const t = e.sourceBufferEntities, - i = [null, null]; - return t.forEach((e, t) => { - e && (i[t] = { - mimeType: e.mimeType, - audioCodec: e.audioCodec, - videoCodec: e.videoCodec, - startPTSSec: void 0, - endPTSSec: void 0, - discoSeqNum: void 0, - mediaOptionId: null === (e = e.initSegmentInfo) || void 0 === e ? void 0 : e.mediaOptionId - }) - }), i - } - appendData(e, i, r) { - const { - mediaQuery: t, - logger: n - } = this, o = this.convertSourceBufferEntitiesToCompatInfo(t); - return e.every(e => null == e) ? $i([]) : this.resetMediaSourceIfNeeded(e).pipe(La(e => e ? t.updating$.pipe(ln(e => !1 === e), Ds(1), Zs(e)) : $i(null)), La(t => { - if (!t) return $i([]); - let s = NaN, - a = NaN; - if (this.needSourceBuffers) { - s = performance.now(); - const i = vf.combineAppendDataInfoWithCompatInfo(t, o, r, n); - this.createSourceBuffers(i), a = performance.now(), this.clearFlush(t) - } - return t.forEach(e => { - e = null == e ? void 0 : e.dataSeg; - null != e && e.cues && ne(null == e ? void 0 : e.texttrackIdx) && this.addCues(e.texttrackIdx, e.cues) - }), this.setStoreSbTimeoffsets(t), Jr(Zr(() => this.appendInitSegments(t, i)), Zr(() => this.appendDataSegments(t, i))).pipe(function(r, i) { - return 2 <= arguments.length ? function(e) { - return Bt(sa(r, i), Ws(1), (void 0 === (t = i) && (t = null), function(e) { - return e.lift(new is(t)) - }))(e); - var t - } : function(e) { - return Bt(sa(function(e, t, i) { - return r(e, t) - }), Ws(1))(e) - } - }((e, t) => (e.push(t), e), new Array), hr(([i, r]) => { - const n = [null, null]; - return [yu.Variant, yu.AltAudio].forEach(e => { - var t; - null != (null == i ? void 0 : i[e]) && (t = { - fragmentType: Vu(e), - bufferCreationStart: s, - bufferCreationEnd: a, - startInitAppend: i[e].startAppend, - endInitAppend: i[e].endAppend, - initBytesAppend: i[e].bytesAppend, - startDataAppend: r[e].startAppend, - endDataAppend: r[e].endAppend, - dataBytesAppend: r[e].bytesAppend - }, n[e] = t) - }), n - }), Za(e => { - this.adjustJaggedStart(t) - })) - }), Ds(1)) - } - endStream() { - try { - this.mediaSourceAdapter.endOfStream() - } catch (e) { - this.logger.warn(`endOfStream failed: ${e.message}`) - } - } - setMediaKeys(e) { - return this.teardownWG$.wrap(this.mediaKeysMutex.lock(() => Fr(this.mediaElement.setMediaKeys(e))).pipe(Za(() => {}), va(e => e.pipe(jr((e, t) => { - if (t < 3) return bn(100 * t); - throw e - }))))) - } - clearMediaKeys() { - return Zr(() => { - if (!this.mediaElement) return Wu; - const e = -1 < navigator.userAgent.toLowerCase().indexOf("chrome"), - t = this.mediaElement.src; - return e && (this.mediaElement.src = ""), this.setMediaKeys(null).pipe(Za(() => this.mediaElement.src = t)) - }) - } - set postFlushSeek(e) { - this.mediaElementStore.postFlushSeek = e - } - schedulePostFlushSeek(e) { - al(() => { - this.mediaQuery.seekTo && (this.seekTo = null), this.postFlushSeek = e - }) - } - set seekTo(e) { - this.mediaElementStore.setSeekToPos(e, !1) - } - setSeekToWithDiscontinuity(e, t) { - this.mediaElementStore.setSeekToPos(e, !1, t) - } - nudgeSeek(e, t) { - al(() => { - this.mediaElementStore.setSeekToPos(e, !1), this.mediaElementStore.setNudgeInfo({ - nudgeTarget: e, - nudgeCount: t - }) - }) - } - set desiredRate(e) { - this.mediaElementStore.desiredRate = e - } - toggleTrickPlaybackMode(e) { - if (this.config.overridePlaybackRate) { - const t = e ? 2 : 1; - try { - this.mediaElement.playbackRate = t - } catch (e) { - this.logger.error({ - name: "iframes" - }, `Exception when setting playbackRate=${t}: ${e.message}`) - } - } - const t = this.muteValueOnTrickPlaybackToggle; - e && void 0 === t ? (this.muteValueOnTrickPlaybackToggle = this.mediaElement.muted, this.mediaElement.muted = e) : e || void 0 === t || (this.mediaElement.muted = t, this.muteValueOnTrickPlaybackToggle = void 0) - } - play() { - this.mediaFunctions.play() - } - pause() { - this.mediaFunctions.pause() - } - get expectPlayEvent() { - return this.mediaFunctions.expectPlayEvent - } - set expectPlayEvent(e) { - this.mediaFunctions.expectPlayEvent = e - } - get expectPauseEvent() { - return this.mediaFunctions.expectPauseEvent - } - set expectPauseEvent(e) { - this.mediaFunctions.expectPauseEvent = e - } - set textTracksCreated(e) { - const t = this["mediaElementStore"]; - t.textTracksCreated = e - } - get msDuration() { - return this._mediaQuery.msDuration - } - set msDuration(e) { - try { - const t = this["mediaElementStore"], - i = this.mediaSource$.value; - i.duration !== e && (i.duration = e, t.msDuration = e) - } catch (e) { - this.logger.warn(`Error setting duration ${e.message}`) - } - } - set haveEnough(e) { - this.mediaElementStore.haveEnough = e - } - set flushing(e) { - this.mediaElementStore.flushing = e - } - set bufferMonitorTargetDuration(e) { - this.mediaElementStore.bufferMonitorTargetDuration = e - } - get textTracks() { - return this.mediaElement.textTracks - } - get id3TextTrack() { - return this.id3Track - } - addTextTrack(e, t, i) { - return this.mediaElement.addTextTrack(e, t, i) - } - dispatchEvent(e) { - return this.mediaElement.dispatchEvent(e) - } - get offsetWidth() { - return this.mediaElement.offsetWidth - } - get offsetHeight() { - return this.mediaElement.offsetHeight - } - getliveSeekableWindow() { - return this.liveSeekableWindow - } - archiveParsedSubtitleFragmentRecord(e, t, i) { - return this.mediaElementStore.archiveParsedSubtitleFragmentRecord(e, t, i) - } - updateLiveSeekableRange(e) { - var t = e.fragments, - e = t.length; - if (1 < e) { - const i = Math.max(t[0].start, 0), - r = t[e - 1].start + t[e - 1].duration; - this.mediaSource$.value.updateLiveSeekableRange(i, r), this.liveSeekableWindow.start = i, this.liveSeekableWindow.end = r - } - } - clearLiveSeekableRange() { - this.mediaSource$.value.clearLiveSeekableRange(), this.liveSeekableWindow.start = NaN, this.liveSeekableWindow.end = NaN - } - } - const Sf = (t, r, n, s, a, o, l, i) => { - if (!t) return Ii; - const e = Oc(t); - return an(e.event("durationchange").pipe(hr(e => kc(t, "durationchange", e)), Za(e => { - e = e.currentTarget; - r.mediaElementDuration = e.duration - })), e.event("seeking").pipe(ao(o.seekEventThrottleMs), hr(e => kc(t, "seeking", e)), Za(e => { - var t = e.currentTarget, - e = t.currentTime; - if (t.readyState >= t.HAVE_METADATA) { - const i = n.seekTo; - !i || !i.fromEvent && 1e-5 < Math.abs(i.pos - e) ? a.inGaplessMode ? function(e, t, i, r, n) { - let s = !1; - e < t.playingItem.itemStartOffset && (n.warn(`[Gapless] Seeking past track boundary oldSeek=${e}, adjustedSeek=${t.playingItem.itemStartOffset}`), e = t.playingItem.itemStartOffset, s = !0), t.isPreloading && (e > t.loadingItem.itemStartOffset && (n.warn(`[Gapless] Seeking past track boundary oldSeek=${e}, adjustedSeek=${t.loadingItem.itemStartOffset}`), e = t.loadingItem.itemStartOffset, s = !0), t.dequeueSource("SeekToUnbufferedTimeRanges")), s ? i.resetMediaSource(e) : r.setSeekToPos(e, !0) - }(e, a, s, r, l) : s && s.hasOwnProperty("liveSeekableWindow") && ne(s.getliveSeekableWindow().start) && ne(s.getliveSeekableWindow().end) && (e < s.getliveSeekableWindow().start || e > s.getliveSeekableWindow().end) ? function(e, t, i, r, n, s) { - let a = e; - if (e < t) a = t; - else if (i < e) { - let e = r.defaultTargetDuration; - ne(r.liveSyncDuration) ? e = r.liveSyncDuration : ne(r.liveSyncDurationCount) && (e = r.liveSyncDurationCount * r.defaultTargetDuration), a = Math.max(0, i - e) - } - s.warn(`[live] liveAdjustedSeek seekTo:${se(e,3)}, adjustedSeek:${se(a,3)}, liveWindowStart:${se(t,3)}, liveWindowEnd:${se(i,3)}`), n.setSeekToPos(a, !0) - }(e, s.getliveSeekableWindow().start, s.getliveSeekableWindow().end, o, r, l) : r.setSeekToPos(e, !0) : r.seeking = !0 - } - })), e.event("seeked").pipe(hr(e => kc(t, "seeked", e)), Za(() => { - r.setSeekToPos(null, !0) - })), e.event("play").pipe(hr(e => kc(t, "play", e)), bo(n.desiredRate$), hr(([e]) => { - var t = e.currentTarget; - r.paused = t.paused; - var i = s.expectPlayEvent, - t = t.controls || o.nativeControlsEnabled; - return !i && t && (s.checkForReplay(), r.desiredRate = 1), s.expectPlayEvent = !1, e - })), e.event("playing").pipe(hr(e => kc(t, "playing", e)), Za(e => { - e = e.currentTarget; - r.paused = e.paused, r.gotPlayingEvent() - })), e.event("loadstart").pipe(hr(e => kc(t, "loadstart", e)), Za(() => { - r.gotLoadStartEvent() - })), e.event("pause").pipe(hr(e => kc(t, "pause", e)), Za(e => { - var t = e.currentTarget; - r.paused = t.paused; - e = s.expectPauseEvent, t = t.controls || o.nativeControlsEnabled; - !e && t && (r.desiredRate = 0), s.expectPauseEvent = !1 - })), e.event("loadedmetadata").pipe(hr(e => kc(t, "loadedmetadata", e))), e.event("loadeddata").pipe(hr(e => kc(t, "loadeddata", e))), e.event("canplay").pipe(hr(e => kc(t, "canplay", e))), e.event("canplaythrough").pipe(hr(e => kc(t, "canplaythrough", e))), e.event("waiting").pipe(hr(e => kc(t, "waiting", e))), e.event("emptied").pipe(hr(e => kc(t, "emptied", e))), e.event("error").pipe(hr(e => kc(t, "error", e)), Wn(e => Vi(t.error))), e.event("ended").pipe(hr(e => kc(t, "ended", e)))).pipe(bo(n.bufferedRangeTuple$), ll(([e]) => { - var t = e.currentTarget, - e = t.readyState; - r.readyState = e, r.ended = t.ended - }), Vn(e => (e instanceof MediaError ? (l.warn(`mediaElementError, code: ${e.code}, message: ${e.message}`), null == i || i.handleMediaElementError(e)) : l.error(`media event error: ${e.message}`), Ii)), $a(on), Vn(e => e instanceof MediaError ? (l.warn(`mediaElementError, code: ${e.code}, message: ${e.message}`), Vi(e)) : (l.error(`media event error: ${e.message}`), Ii))) - }, - bf = new class extends fl { - constructor() { - super({}, { - name: "media-element-store", - producerFn: su - }), this._activeId = "" - } - get activeId() { - return this._activeId - } - startMediaSession(i, r, n, s) { - return Do("playback.session.start"), this._activeId = `media session: ${(new Date).toISOString()}`, al(() => { - var e = s, - t = Math.max(e, r - e), - t = { - id: this.activeId, - desiredRate: !i.autoplay && i.paused ? 0 : 1, - paused: i.paused, - gotPlaying: !1, - gotLoadStart: !1, - firstPlayTime: void 0, - seeking: i.seeking, - flushing: !1, - readyState: i.readyState, - ended: i.ended, - bufferedRanges: [], - haveEnough: !1, - mediaSourceEntity: null, - expectedSbCount: NaN, - bufferMonitorInfo: { - waterLevelType: null, - almostDryWaterLevelSeconds: n, - lowWaterLevelSeconds: e, - highWaterLevelSeconds: t, - maxBufferSeconds: r - }, - mediaOptionParsedSubtitleRecord: [], - textTracksCreated: !1, - waitingForDisco: !1 - }; - this.add(t), this.setActive(this.activeId) - }), this.logger = Qe().child({ - name: "UpdateBufferedSegments" - }), this.activeId - } - setMediaSourceEntity(t, i) { - Do("playback.set.msObjectUrl"), this.updateActive(e => { - e.mediaSourceEntity = null != t && null != i ? { - objectUrl: t, - readyState: i, - duration: NaN, - sourceBufferEntities: [null, null] - } : null, e.bufferedRanges = [], e.haveEnough = !1, e.readyState = 0, e.bufferMonitorInfo.waterLevelType = null - }) - } - set mediaElementDuration(t) { - Do("playback.set.mediaElementDuration"), this.updateActive(e => { - e && (e.mediaElementDuration = t) - }) - } - set msReadyState(t) { - Do("playback.set.msReadyState"), this.updateActive(({ - mediaSourceEntity: e - }) => { - e && (e.readyState = t) - }) - } - set readyState(t) { - Do(`playback.set.readyState ${t}`), this.updateActive(e => { - e.readyState = t - }) - } - set ended(t) { - Do(`playback.set.ended ${t}`), this.updateActive(e => { - e.ended = t - }) - } - set msDuration(t) { - Do("playback.set.msDuration"), this.updateActive(e => { - e.mediaSourceEntity.duration = t - }) - } - set textTracksCreated(t) { - Do("playback.set.textTracksCreated ${created}"), this.updateActive(e => { - e.textTracksCreated = t - }) - } - set expectedSbCount(t) { - Do("playback.set.expectedSbCount"), this.updateActive(e => { - e.expectedSbCount = t - }) - } - set postFlushSeek(e) { - this.updateActive({ - postFlushSeek: e - }) - } - setSeekToPos(t, i, r) { - Do(`playback.set.seekToPos: ${null==t?void 0:t.toFixed(3)} cc: ${r}`), this.updateActive(e => { - ne(t) ? (e.seekTo = { - pos: t, - fromEvent: i, - discoSeqNum: r - }, e.gotPlaying = !1, e.haveEnough = !1) : (e.seekTo = null, e.postFlushSeek = void 0), i && (e.seeking = ne(t)) - }) - } - set seeking(t) { - Do(`playback.set.seeking: ${t}`), this.updateActive(e => { - e.seeking = t - }) - } - set paused(t) { - Do(`playback.set.paused: ${t}`), this.updateActive(e => { - (e.paused = t) && (e.gotPlaying = !1) - }) - } - gotPlayingEvent() { - Do("playback.set.playing"), this.updateActive(e => { - e.paused || (e.gotPlaying = !0, e.firstPlayTime = e.firstPlayTime || performance.now()) - }) - } - gotLoadStartEvent() { - Do("playback.set.loadstart"), this.updateActive(e => { - e.gotLoadStart = !0 - }) - } - set desiredRate(t) { - Do(`playback.set.desiredRate: ${t}`), this.updateActive(e => { - e.desiredRate = t - }) - } - set haveEnough(t) { - Do(`playback.set.haveEnough: ${t}`), this.updateActive(e => { - e.haveEnough = t - }) - } - set flushing(e) { - Do(`playback.set.flushing: ${e}`), this.updateActive({ - flushing: e - }) - } - set waitingForDisco(t) { - Do(`playback.set.waitingForDisco: ${t}`), this.updateActive(e => { - e && (e.waitingForDisco = t) - }) - } - setSourceBufferUpdating(i) { - Do(`playback.set.sourcebuffers[${Uu[i]}].updating`), this.updateActive(({ - mediaSourceEntity: e - }) => { - const t = null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[i]; - t && (t.updating = !0, t.error = void 0) - }) - } - setTimestampOffset(i, r) { - Do(`playback.set.sourcebuffers[${Uu[i]}].timestampOffset`), this.updateActive(({ - mediaSourceEntity: e - }) => { - const t = null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[i]; - t && (t.timestampOffset = r) - }) - } - setBufferedRangesUpdated(a, o, l, d, u) { - Do(`playback.set.sourcebuffers[${Uu[a]}].bufferupdated`), this.updateActive(e => { - var t, i, r; - const n = null == e ? void 0 : e.mediaSourceEntity, - s = null === (r = null == n ? void 0 : n.sourceBufferEntities) || void 0 === r ? void 0 : r[a]; - if (s) { - const a = null == n ? void 0 : n.duration; - s.updating = !1, s.bufferedRanges = [...o], t = s, i = t.inFlight, r = t.bufferedSegments, i && ne(i.startPTS) && ne(i.endPTS) && function(t, i) { - let r = !1; - for (let e = t.length - 1; - 1 < e; e--) { - const s = t[e], - a = Math.max(i.startPTS, s.startPTS), - o = Math.min(i.endPTS, s.endPTS); - var n; - o <= a || ((n = (1 - (o - a) / (s.endPTS - s.startPTS)) * s.bytes) <= 0 ? t.splice(e, 1) : (s.bytes = n, s.startPTS < i.startPTS ? s.endPTS = a : (s.startPTS = o, r || (t.splice(e, 0, i), r = !0)))) - } - r || t.push(i) - }(r, i), t.inFlight = null, - function(e, t, i, r, n) { - const { - maxBufferHole: s, - bufferedSegmentEjectionToleranceMs: a - } = r, o = e.bufferedSegments, l = e.bufferedRanges; - let d, u = 0, - c = !1; - if (l.length) - for (let e = o.length - 1; - 1 < e; e--) { - const t = o[e], - r = !t.frag.iframe; - r && t.frag.isLastFragment && (d = t.frag); - var h = t.endPTS - t.startPTS; - if (h <= 0) o.splice(e, 1), null == n || n.warn(`Ejecting segment from bufferedSegments due to segmentDuration <= 0 > segment=${ae(t)}`), c = t.frag === d; - else { - var p = Bp(l, t); - if (p) { - var f = Math.max(p.start, t.startPTS), - p = Math.min(p.end, t.endPTS), - f = p - f; - if (u += t.bytes * f / h, r) - if (f < Math.min(h, s)) o.splice(e, 1), null == n || n.warn(`Ejecting segment from bufferedSegments due to tiny overlaps > segment=${ae(t)}, bufferedRanges=${ae(l)}`), c = t.frag === d; - else { - const r = t.appendedDuration, - u = (r || 0) - f, - m = Math.min(.001 * a, h); - r ? !(u > m && f != h) || t.frag.isLastFragment && p === i || (o.splice(e, 1), null == n || n.warn(`Ejecting segment from bufferedSegments due to change in current overlap > segment=${ae(t)}, delta=${u}, bufferedRanges=${ae(l)}`), c = t.frag === d) : t.appendedDuration = f - } - } else null == n || n.warn(`Ejecting segment from bufferedSegments due to no overlap > segment=${ae(t)}, bufferedRanges=${ae(l)}`), o.splice(e, 1), c = t.frag === d - } - } else o.length && o.splice(0, o.length); - e.totalDuration = d && !c && 0 < l.length ? l[l.length - 1].end : 1 / 0, e.gotQuotaExceeded = e.gotQuotaExceeded || t, e.totalBytes = u, e.maxTotalBytes = Math.max(e.totalBytes, e.maxTotalBytes) - }(s, d, a, u, this.logger) - } - e.bufferedRanges = [...l] - }) - } - setSourceBufferEntity(n, s) { - Do(`playback.set.sourcebuffers[${Uu[n]}].setSourceBufferEntity`), this.updateActive(({ - mediaSourceEntity: e - }) => { - var t, i, r; - e && ({ - mimeType: t, - audioCodec: i, - videoCodec: r - } = s, e.sourceBufferEntities[n] = { - mimeType: t, - audioCodec: i, - videoCodec: r, - updating: !1, - bufferedRanges: [], - timestampOffset: 0, - inFlight: null, - bufferedSegments: [], - totalBytes: 0, - maxTotalBytes: 0, - gotQuotaExceeded: !1, - totalDuration: 1 / 0 - }) - }) - } - setInflightSegment(i, r) { - Do(`playback.set.sourcebuffers[${Uu[i]}].setInflightSegment`), this.updateActive(({ - mediaSourceEntity: e - }) => { - const t = null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[i]; - t && (t.inFlight = r) - }) - } - setInitSegmentEntity(i, r) { - Do(`playback.set.sourcebuffers[${Uu[i]}].setInitSegmentEntity`), this.updateActive(({ - mediaSourceEntity: e - }) => { - const t = null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[i]; - t && (t.initSegmentInfo = r) - }) - } - setSourceBufferError(i, r) { - Do(`playback.set.sourcebuffers[${i}].error: ${r}`), this.updateActive(({ - mediaSourceEntity: e - }) => { - const t = null === (e = null == e ? void 0 : e.sourceBufferEntities) || void 0 === e ? void 0 : e[i]; - t && (t.inFlight = null, t.updating = !1, t.error = r) - }) - } - setStallInfo(t) { - Do(`playback.set.stallInfo stalled=${null!=t}`), this.updateActive(e => { - e.stallInfo = t - }) - } - setNudgeInfo(t) { - Do(`playback.set.nudgeInfo ${ae(t)}`), this.updateActive(e => { - e.nudgeInfo = t - }) - } - updateWaterLevels(t, i) { - Do("playback.set.updateWaterLevels"), this.updateActive(e => { - e.bufferMonitorInfo.waterLevelType = { - combined: t, - sbTuple: [...i] - } - }) - } - set bufferMonitorTargetDuration(i) { - Do(`playback.set.targetDuration: ${i}`), this.updateActive(e => { - if (ne(i) && 0 < i) { - const t = e.bufferMonitorInfo; - t.lowWaterLevelSeconds = Math.min(i, t.maxBufferSeconds), t.highWaterLevelSeconds = Math.max(t.lowWaterLevelSeconds, t.maxBufferSeconds - i) - } - }) - } - archiveParsedSubtitleFragmentRecord(i, r, n) { - Do(`playback.cues.set persistentId ${i} mediaSeqNum ${r}: parsed ${n.count} time-range ${n.startTime}:${n.endTime}`), this.updateActive(e => { - let t = e.mediaOptionParsedSubtitleRecord[i]; - t || (t = {}, e.mediaOptionParsedSubtitleRecord[i] = t), t[r] = n - }) - } - }; - class Tf extends bi { - constructor(e) { - super(e), this.store = e, this.displaySupportsHdr$ = this.select("supportsHdr"), this.platformInfo$ = this.select("platformInfo"), this.viewportInfo$ = this.select("viewportInfo") - } - get platformInfo() { - return this.getValue().platformInfo - } - get displaySupportsHdr() { - return this.getValue().supportsHdr - } - get viewportInfo() { - return this.getValue().viewportInfo - } - } - class Ef { - constructor(e) { - this.store = e - } - getQuery() { - return new Tf(this.store) - } - updateSupportsHdr(t) { - this.store.update(e => { - e.supportsHdr = t - }) - } - updatePlatformInfo(e) { - this.store.update({ - platformInfo: e - }) - } - updateViewportInfo(t) { - this.store.update(e => { - e.viewportInfo = t - }) - } - } - const If = new class extends dl { - constructor() { - super({ - supportsHdr: !0 - }, { - name: "platform", - producerFn: su - }) - } - }; - let wf = null; - - function Af() { - return wf = wf || new Ef(If), wf - } - class Of extends $t { - constructor(e, t) { - super(e => { - null == t || t.add(), e.add(this._handler$.pipe(jr(([e, t, i]) => e(...t).pipe(Za({ - next(e) { - i(e, null) - }, - error(e) { - i(null, e) - } - })))).subscribe()) - }), this._vanillaRPC = e, this.teardownWG$ = t, this._handler$ = new Xt - } - register(e, i) { - return this._vanillaRPC.register(e, (...t) => e => { - this._handler$.next([i, t, e]) - }) - } - invoke(e, t, r) { - return new $t(i => { - this._vanillaRPC.invoke(e, t, r)((e, t) => { - null != t ? i.error(t) : (i.next(e), i.complete()) - }) - }) - } - } - class kf extends Of { - constructor(e) { - super(e) - } - decrypt(e, t, i, r, n) { - return this.invoke("decrypt", [e, t, i, r, n], [e, t, r]) - } - } - class Cf extends Of { - constructor(e) { - super(e), this.rpcService = e, this.sessions = {}, this._onEvent = (e, t, i) => () => { - null != this.sessions[e] && this.sessions[e].observer.trigger(t, i) - }, this.rpcService.register("demuxer.event", this._onEvent) - } - init(e, t, i) { - return [{ - maxSeekHole: r, - maxBufferHole: n, - audioPrimingDelay: s, - stretchShortVideoTrack: a, - forceKeyFrameOnDiscontinuity: o - }] = [t], this.invoke("demuxer.init", [e, t = { - maxSeekHole: r, - maxBufferHole: n, - audioPrimingDelay: s, - stretchShortVideoTrack: a, - forceKeyFrameOnDiscontinuity: o - }, i], []).pipe(hr(e => { - var t = new Df(this, e); - return this.sessions[e] = t - })); - var r, n, s, a, o - } - } - class Df { - constructor(e, t) { - this.rpc = e, this.demuxSessionID = t, this.observer = new a - } - push(e, t, i, r, n, s, a, o, l, d, u, c, h) { - return this.rpc.invoke("demuxer.push", [this.demuxSessionID, e, t, i, r, n, s, a, o, l, d, u, c], null != h ? h : [e]) - } - pushWithoutTransfer(e, t, i, r, n, s, a, o, l, d, u, c) { - return this.push(e, t, i, r, n, s, a, o, l, d, u, c, []) - } - destroy() { - this.observer.removeAllListeners(), this.rpc.invoke("demuxer.destroy", [this.demuxSessionID], []).subscribe() - } - } - class Mf { - constructor() { - this.handlers = {} - } - register(e, t) { - if (null != this.handlers[e]) return !1; - this.handlers[e] = t - } - unregister(e) { - if (null != this.handlers[e]) return !1; - delete this.handlers[e] - } - invoke(e, i) { - return (t = Mf._fallbackCallback) => { - try { - if (null == this.handlers[e]) throw new Error(`command ${e} not found`); - this.handlers[e](...i)(t) - } catch (e) { - t(void 0, e) - } - } - } - teardown(e) { - this.handlers = null, e() - } - } - Mf._fallbackCallback = (e, t) => { - if (null != t) throw t - }; - const xf = e => { - e = e.child({ - name: "InlineRPCService" - }); - var t = new Mf; - return new i(t, e), new ot(t, e), t - }; - let Pf; - const Rf = e => { - try { - if (null == Pf) { - const e = new Blob(["var exports = {};var module = { exports: exports };function define(f){f()};define.amd = true;(" + Xy.toString() + ")(true);"], { - type: "text/javascript" - }), - r = URL.createObjectURL(e); - Pf = new Worker(r) - } - var t = new lt(Pf); - return i = t, n = e.child({ - name: "WorkerRPCService" - }), i.register("logger.log", (e, i, ...r) => t => { - try { - for (const i of e) n = n.child(i); - "function" == typeof n[i] && n[i](...r), t() - } catch (e) { - t(void 0, e) - } - }), t - } catch (e) { - throw new Error("Failed to create WebWorker") - } - var i, n - }, - Lf = (...r) => i => { - for (let t = 0; t < r.length; t++) { - const e = r[t]; - try { - return e(i) - } catch (e) { - if (t === r.length - 1) throw e; - i.warn(e) - } - } - }, - _f = e => Lf(Rf, xf)(e); - class Nf {} - Nf.PlayEnded = 6101, Nf.Periodic = 6110, Nf.PlayStalled = 6103, Nf.KeySessionComplete = 6104, Nf.PlayLikelyToKeepUp = 6105, Nf.PlayRateChanged = 6106, Nf.PlayError = 6107, Nf.MediaEngineStalled = 6108, Nf.SwitchComplete = 6109, Nf.VariantEnded = 6111, Nf.NwError = 6202; - const Ff = { - avc1: 1, - avc3: 1, - hvc1: { - SDR: 2, - HLG: 10, - PQ: 11 - }, - hev1: { - SDR: 2, - HLG: 10, - PQ: 11 - }, - vp09: { - SDR: 3, - HLG: 14, - PQ: 13 - }, - dvh1: { - PQ: 12 - } - }; - class Bf { - constructor(e, t) { - this.query = e, this.logger = t - } - setReportingAgent(e) { - this.reportingAgent = e - } - sendPlayEnded(e) { - var t = Nf.PlayEnded; - this.fillAndFire(t, this.query.playEnded(e)) - } - sendPlayStalled(e) { - var t = Nf.PlayStalled; - this.fillAndFire(t, this.query.playStalled(e)) - } - sendMediaEngineStalled(e) { - var t = Nf.MediaEngineStalled; - this.fillAndFire(t, this.query.mediaEngineStalled(e)) - } - sendKeySessionComplete(e) { - var t = Nf.KeySessionComplete; - this.fillAndFire(t, this.query.keySessionComplete(e)) - } - sendPlayLikelyToKeepUp(e) { - var t = Nf.PlayLikelyToKeepUp; - this.fillAndFire(t, this.query.playLikelyToKeepUp(e)) - } - sendPlayRateChange(e) { - var t = Nf.PlayRateChanged; - this.fillAndFire(t, this.query.playRateChanged(e)) - } - sendSwitchComplete(e) { - var t = Nf.SwitchComplete; - this.fillAndFire(t, this.query.switchComplete(e)) - } - sendVariantEnded(e) { - var t = Nf.VariantEnded; - this.fillAndFire(t, this.query.variantEnded(e)) - } - sendPlayError(e) { - var t = Nf.PlayError; - this.fillAndFire(t, this.query.playError(e)) - } - sendNwError(e) { - var t = Nf.NwError; - this.fillAndFire(t, this.query.nwError(e)) - } - sendPeriodic(e) { - var t = Nf.Periodic; - this.fillAndFire(t, this.query.periodic(e)) - } - fillAndFire(e, t) { - if (e !== Nf.Periodic || t.PlayTimeWC || t.ADT) { - var r = e === Nf.PlayEnded || e === Nf.Periodic ? 1 : 0; - if (this.reportingAgent) { - let i = {}; - Object.entries(t).forEach(([e, t]) => { - "object" == typeof(t = ne(t) ? Number(Number(t).toFixed(2)) : t) ? "ServerInfo" === e && Object.entries(t).forEach(([e, t]) => { - i[e] = t - }): i[e] = t - }), i = JSON.parse(JSON.stringify(i)); - try { - this.reportingAgent.issueReportingEvent(e, i, r) - } catch (e) {} - } - } - } - } - class Uf extends kl { - constructor(e, t) { - super(e), this.logger = t - } - get activeEntity() { - return this.getActive() - } - entity(e) { - return this.getEntity(e) - } - playEnded(e) { - return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.playEndedRecord - } - periodic(e) { - return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.periodicRecord - } - playStalled(e) { - return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.playStalledRecord - } - mediaEngineStalled(e) { - return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.mediaEngineStalledRecord - } - keySessionComplete(e) { - return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.keySessionCompleteRecord - } - playLikelyToKeepUp(e) { - return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.playLikelyToKeepUpRecord - } - playRateChanged(e) { - return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.playRateChangedRecord - } - switchComplete(e) { - return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.switchCompleteRecord - } - variantEnded(e) { - return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.variantEndedRecord - } - playError(e) { - return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.playErrorRecord - } - nwError(e) { - return null === (e = this.getEntity(e)) || void 0 === e ? void 0 : e.nwErrorRecord - } - } - class $f extends fl { - constructor(e) { - super({}, { - name: "rtc-store", - idKey: "itemId", - producerFn: su, - resettable: !0 - }), this.logger = e - } - createEntity(e) { - e = { - itemId: e, - sessionControlRecord: { - state: "RTC_STATE_INIT", - rate: 0, - oldRate: 0, - eventStartTime: Date.now(), - sessionStartTime: Date.now(), - lastLikelyToKeepUpTime: Date.now(), - lastPeriodicTime: Date.now(), - playLikelyToKeepUpEventCounter: 1, - periodicEventCounter: 1, - activeKeySessions: {}, - intervalVariantList: {}, - sessionVariantList: {} - }, - playEndedRecord: {}, - periodicRecord: {}, - playStalledRecord: {}, - keySessionCompleteRecord: {}, - playLikelyToKeepUpRecord: {}, - playRateChangedRecord: {}, - playErrorRecord: {}, - mediaEngineStalledRecord: {}, - switchCompleteRecord: {}, - variantEndedRecord: {}, - nwErrorRecord: {} - }; - this.add(e) - } - updateEnded(e) { - this._prepareEventPlayEnded(e) - } - updatePeriodic(e, t) { - this._prepareEventPeriodic(e, { - isFinal: t - }) - } - updateBufferStalled(e, t) { - this.update(e, ({ - sessionControlRecord: e, - variantEndedRecord: t, - periodicRecord: i, - playEndedRecord: r - }) => { - e.rate = 0, t.StallCount = (t.StallCount || 0) + 1, i.StallCount = (i.StallCount || 0) + 1, r.StallCount = (r.StallCount || 0) + 1 - }), this._prepareEventPlayStalled(e, t) - } - updateSegmentKeyLoaded(e, r) { - this.update(e, ({ - sessionControlRecord: e - }) => { - const t = {}, - i = r.timestamp; - t.keyFormat = "identity", t.keyDeliveryTime = r.adt, t.currentMediaTime = r.currentTime, "RTC_STATE_INIT" === e.state && (t.keyInitTime = i - e.sessionStartTime), e.activeKeySessions[r.keyuri] = t - }), this._prepareEventKeySessionComplete(e, r) - } - updateLicenseResponseProcessed(e, i) { - this.update(e, ({ - sessionControlRecord: e - }) => { - const t = e.activeKeySessions[i.keyuri]; - t.licenseResponseProcessTime = i.timestamp - t.licenseResponseSubmitTime, e.lastKeyDeliveryTime = t.keyDeliveryTime = i.timestamp - t.licenseChallengeStartTime, t.currentMediaTime = i.currentTime, e.finishedKeyUri = i.keyuri - }), this._prepareEventKeySessionComplete(e, i) - } - updateLicenseChallengeError(e, i) { - this.update(e, ({ - sessionControlRecord: e - }) => { - const t = e.activeKeySessions[i.keyuri]; - e.lastKeyErrorType = t.keyErrorType = "licenseChallengeError", e.finishedKeyUri = i.keyuri - }), this._prepareEventKeySessionComplete(e, i) - } - updateLicenseResponseError(e, i) { - this.update(e, ({ - sessionControlRecord: e - }) => { - const t = e.activeKeySessions[i.keyuri]; - e.lastKeyErrorType = t.keyErrorType = "licenseResponseError", e.finishedKeyUri = i.keyuri - }), this._prepareEventKeySessionComplete(e, i) - } - updateKeyAborted(e, t) { - this.update(e, ({ - sessionControlRecord: e - }) => { - e.activeKeySessions[t.keyuri].keyErrorType = "keyAborted", e.finishedKeyUri = t.keyuri - }), this._prepareEventKeySessionComplete(e, t) - } - updateCanPlay(e, t) { - this.update(e, ({}) => {}), this._prepareEventPlayLikelyToKeepUp(e, t) - } - updateRateChanged(e, t) { - this.update(e, ({ - sessionControlRecord: e - }) => { - e.rate = 100 * t.rate, 0 < t.rate && 0 === e.oldRate && (e.playInfo || (e.playInfo = []), e.playInfo.push({ - latency: t.latency - }), e.curLevelUrl || (e.curLevelUrl = t.url)) - }), this._prepareEventPlayRateChanged(e, t) - } - updateMediaError(e, r) { - this.update(e, ({ - sessionControlRecord: e, - periodicRecord: t, - playEndedRecord: i - }) => { - t.PlayerErrCount = (t.PlayerErrCount || 0) + 1, i.PlayerErrCount = (i.PlayerErrCount || 0) + 1, r.fatal && (e.rate = 0, t.FatalPlayerErrCount = (t.FatalPlayerErrCount || 0) + 1, i.FatalPlayerErrCount = (i.FatalPlayerErrCount || 0) + 1, i.ErrCode = r.details, i.ErrReason = r.code, i.ErrIsFatal = !0, i.ErrDomain = "mediaError") - }), r.fatal ? this._prepareEventPlayError(e, r) : this.update(e, ({ - sessionControlRecord: e - }) => { - var t = r.details + "/" + r.code; - e.nonFatalPlayErrList[t] = (e.nonFatalPlayErrList[t] || 0) + 1 - }) - } - updateMediaElementError(e, t) { - let i; - try { - i = JSON.parse(t.message) - } catch (e) { - this.logger.warn(`message is not JSON, ignoring; ${t.message}`) - } - var r = i ? parseInt(i.ErrReason) : null, - n = i ? parseInt(i.ErrDetail) : null; - this._prepareEventPlayError(e, { - domain: "mediaElementError", - mediaElemCode: t.code, - mediaElemReason: r, - mediaElemDetail: n, - code: null, - details: null, - fatal: null - }) - } - updateMediaEngineStalled(e, t) { - this.update(e, ({ - sessionControlRecord: e, - variantEndedRecord: t, - periodicRecord: i, - playEndedRecord: r - }) => { - e.rate = 0, t.MediaEngineStallCount = (t.MediaEngineStallCount || 0) + 1, i.MediaEngineStallCount = (i.MediaEngineStallCount || 0) + 1, r.MediaEngineStallCount = (r.MediaEngineStallCount || 0) + 1 - }), this._prepareEventMediaEngineStalled(e, t) - } - updateLevelSwitched(e, d) { - this.update(e, ({ - sessionControlRecord: e, - switchCompleteRecord: t, - periodicRecord: i, - playEndedRecord: r - }) => { - i.SwCnt = (i.SwCnt || 0) + 1, r.SwCnt = (r.SwCnt || 0) + 1; - var n = Date.now(), - s = e.curLevelUrl, - a = d.url, - i = this._getVariantInfo(s, e), - r = this._getVariantInfo(a, e); - let o, l = !1; - s && (o = r.bandwidth < i.bandwidth ? "Down" : "Up", l = r.iframes), t.BadSw = this._isBadSw(o, e.lastSwitchDir || o, l, e.lastLevelIsIframe || l, n, e.lastSwitchTime || n, d.isSeeking), e.lastSwitchDir = o, e.lastSwitchTime = n, e.lastLevelIsIframe = l, e.curLevelUrl = a, e.variantStartTimeMedia = d.currentTime - }), this._prepareEventSwitchComplete(e, d) - } - updateLevelLoadError(e, o) { - this.update(e, ({ - sessionControlRecord: e, - switchCompleteRecord: t, - periodicRecord: i, - playEndedRecord: r - }) => { - var n = Date.now(); - let s, a = !1; - if (e.curLevelUrl) { - const t = this._getVariantInfo(e.curLevelUrl, e), - i = this._getVariantInfo(o.url, e); - s = i.bandwidth < t.bandwidth ? "Down" : "Up", a = i.iframes - } - i.SwCnt = (i.SwCnt || 0) + 1, r.SwCnt = (r.SwCnt || 0) + 1, t.BadSw = this._isBadSw(s, e.lastSwitchDir || s, a, e.lastLevelIsIframe || a, n, e.lastSwitchTime || n, o.isSeeking), t.SwFail = !0 - }), this._prepareEventSwitchComplete(e, o) - } - updateVariantEnd(e, t) { - this._prepareEventVariantEnded(e, t) - } - updateNwError(e, r) { - this.update(e, ({ - sessionControlRecord: e, - periodicRecord: t, - playEndedRecord: i - }) => { - t.NwErrCount = (t.NwErrCount || 0) + 1, i.NwErrCount = (i.NwErrCount || 0) + 1, r.fatal && (e.rate = 0, t.FatalNwErrCount = (t.FatalNwErrCount || 0) + 1, i.FatalNwErrCount = (i.FatalNwErrCount || 0) + 1, i.ErrCode = r.details, i.ErrReason = r.code, i.ErrIsFatal = !0, i.ErrDomain = "networkError") - }), r.fatal ? this._prepareEventNwError(e, r) : this.update(e, ({ - sessionControlRecord: e - }) => { - var t = r.details + "/" + r.code; - e.nonFatalNwErrList[t] = (e.nonFatalNwErrList[t] || 0) + 1 - }) - } - finalize(e, t) { - switch (t) { - case Nf.PlayEnded: - this.update(e, ({ - sessionControlRecord: e - }) => { - e.state = "RTC_STATE_STOP", e.oldRate = 0 - }), this.update(e, e => { - e.playEndedRecord = {} - }); - break; - case Nf.Periodic: - this.update(e, ({ - sessionControlRecord: t - }) => { - t.lastPeriodicTime = Date.now(), t.periodicEventCounter += 1, Object.keys(t.intervalVariantList).forEach(e => { - t.intervalVariantList[e].playTime = 0 - }) - }), this.update(e, e => { - e.periodicRecord = {} - }); - break; - case Nf.PlayStalled: - this.update(e, ({ - sessionControlRecord: e - }) => { - e.state = "RTC_STATE_STALL", e.oldRate = 0 - }), this.update(e, e => { - e.playStalledRecord = {} - }); - break; - case Nf.KeySessionComplete: - this.update(e, ({ - sessionControlRecord: e - }) => { - delete e.activeKeySessions[e.finishedKeyUri] - }), this.update(e, e => { - e.keySessionCompleteRecord = {} - }); - break; - case Nf.PlayLikelyToKeepUp: - this.update(e, ({ - sessionControlRecord: e - }) => { - "RTC_STATE_PLAY" !== e.state && (e.state = "RTC_STATE_CANPLAY", e.lastLikelyToKeepUpTime = Date.now(), e.playLikelyToKeepUpEventCounter += 1) - }), this.update(e, e => { - e.playLikelyToKeepUpRecord = {} - }); - break; - case Nf.PlayRateChanged: - this.update(e, ({ - sessionControlRecord: e, - playEndedRecord: t, - playStalledRecord: i, - mediaEngineStalledRecord: r, - playErrorRecord: n, - nwErrorRecord: s - }) => { - 0 !== e.rate ? (e.state = "RTC_STATE_PLAY", delete t.LastStall, delete t.LastMediaEngineStall, delete t.LastPause, delete n.LastPause, delete s.LastPause) : (e.state = "RTC_STATE_PAUSE", delete i.LastResume, delete r.LastResume, delete n.LastResume, delete s.LastResume), e.oldRate = e.rate - }), this.update(e, e => { - e.playRateChangedRecord = {} - }); - break; - case Nf.PlayError: - this.update(e, ({ - sessionControlRecord: e - }) => { - e.state = "RTC_STATE_PLAYERROR" - }), this.update(e, e => { - e.playErrorRecord = {} - }); - break; - case Nf.MediaEngineStalled: - this.update(e, ({ - sessionControlRecord: e - }) => { - e.state = "RTC_STATE_MEDIAENGINESTALL", e.oldRate = 0 - }), this.update(e, e => { - e.mediaEngineStalledRecord = {} - }); - break; - case Nf.SwitchComplete: - this.update(e, e => { - e.switchCompleteRecord = {}, e.sessionControlRecord.prevLevelUrl = e.sessionControlRecord.curLevelUrl - }); - break; - case Nf.VariantEnded: - this.update(e, ({ - sessionControlRecord: e - }) => { - e.decodedFramesForVariant = 0, e.decodedFramesForVariantSampleCount = 0 - }), this.update(e, e => { - e.variantEndedRecord = {} - }); - break; - case Nf.NwError: - this.update(e, ({ - sessionControlRecord: e - }) => { - e.state = "RTC_STATE_NWERROR" - }), this.update(e, e => { - e.nwErrorRecord = {} - }) - } - this.update(e, ({ - sessionControlRecord: e - }) => { - e.eventStartTime = Date.now() - }) - } - updatePlaybackInfo(e, s) { - this.update(e, ({ - sessionControlRecord: e, - periodicRecord: t, - playEndedRecord: i - }) => { - s.droppedVideoFrames < e.droppedVideoFrames && (e.droppedVideoFrames = 0), s.decodedFrameCount < e.decodedFrameCount && (e.decodedFrameCount = 0); - var r = s.droppedVideoFrames - (e.droppedVideoFrames || 0), - n = s.decodedFrameCount - (e.decodedFrameCount || 0); - e.droppedVideoFrames = s.droppedVideoFrames, e.decodedFrameCount = s.decodedFrameCount, e.decodedFramesForVariant += n, e.decodedFramesForVariantSampleCount += 1, r && (3 <= r ? (t.GroupViFrDr = (t.GroupViFrDr || 0) + r, t.GroupViFrDrEvtCount = (t.GroupViFrDrEvtCount || 0) + 1, i.GroupViFrDr = (i.GroupViFrDr || 0) + r, i.GroupViFrDrEvtCount = (i.GroupViFrDrEvtCount || 0) + 1) : (t.SparseViFrDr = (t.SparseViFrDr || 0) + r, t.SparseViFrDrEvtCount = (t.SparseViFrDrEvtCount || 0) + 1, i.SparseViFrDr = (i.SparseViFrDr || 0) + r, i.SparseViFrDrEvtCount = (i.SparseViFrDrEvtCount || 0) + 1)) - }) - } - updateBufferAppended(e, t) { - this.update(e, ({ - sessionControlRecord: e - }) => { - e.bufferAppendInfo || (e.bufferAppendInfo = []), e.bufferAppendInfo.push(t) - }) - } - updateSeeked(e, t) { - this.update(e, ({ - sessionControlRecord: e - }) => { - e.seekInfo || (e.seekInfo = []), e.seekInfo.push(t) - }) - } - updateManifestParsed(e, r) { - this.update(e, ({ - sessionControlRecord: e, - periodicRecord: t, - playEndedRecord: i - }) => { - t.MasterPlaylistADT = (t.MasterPlaylistADT || 0) + r.adt, i.MasterPlaylistADT = (i.MasterPlaylistADT || 0) + r.adt; - t = this._computeVariantInfo(r.levels); - i.IsAudioOnly = r.isAudioOnly, i.IsGapless = r.isGapless, i.IsFirstItem = r.isFirstItem, i.ItemID = r.itemID, i.MaxVideoQltyIndex = t.maxVideoQltyIndex, i.MaxReWd = t.maxWidth, i.MaxReHt = t.maxHeight, e.manifestData = { - variantList: t.variantList, - varListString: t.varListString - } - }) - } - updateFragLoaded(e, r) { - this.update(e, ({ - sessionControlRecord: e, - periodicRecord: t, - playEndedRecord: i - }) => { - if (t.MediaRequestsSent = (t.MediaRequestsSent || 0) + 1, i.MediaRequestsSent = (i.MediaRequestsSent || 0) + 1, r.cdnServer) { - const e = r.cdnServer.toLowerCase(); - "aapl" !== e && "akam" !== e && "llnw" !== e || (i.LastMediaCDNServer = e) - } - r.serverInfo && (i.ServerInfo = r.serverInfo), e.segmentMimeTypes || (e.segmentMimeTypes = []), void 0 === e.segmentMimeTypes.find(e => e == r.contentType) && e.segmentMimeTypes.push(r.contentType), r.fragType === gu.Variant ? (e.variantVideoBytes = (e.variantVideoBytes || 0) + r.bytes, e.variantVideoDuration = (e.variantVideoDuration || 0) + r.duration, e.intervalVideoBytes = (e.intervalVideoBytes || 0) + r.bytes, e.intervalVideoDuration = (e.intervalVideoDuration || 0) + r.duration, e.sessionVideoBytes = (e.sessionVideoBytes || 0) + r.bytes, e.sessionVideoDuration = (e.sessionVideoDuration || 0) + r.duration, e.obrLast = 8 * r.bytes / (r.duration / 1e3), t.NetBytes = (t.NetBytes || 0) + r.bytes, t.ADT = (t.ADT || 0) + r.adt, t.SegmentProcessTime = (t.SegmentProcessTime || 0) + r.processTime, i.ADT = (i.ADT || 0) + r.adt, i.NetBytes = (i.NetBytes || 0) + r.bytes, i.SegmentProcessTime = (i.SegmentProcessTime || 0) + r.processTime) : r.fragType === gu.AltAudio && (e.variantAudioBytes = (e.variantAudioBytes || 0) + r.bytes, e.variantAudioDuration = (e.variantAudioDuration || 0) + r.duration, e.intervalAudioBytes = (e.intervalAudioBytes || 0) + r.bytes, e.intervalAudioDuration = (e.intervalAudioDuration || 0) + r.duration, e.sessionAudioBytes = (e.sessionAudioBytes || 0) + r.bytes, e.sessionAudioDuration = (e.sessionAudioDuration || 0) + r.duration) - }) - } - updateFragBuffered(e, i) { - this.update(e, ({ - periodicRecord: e, - playEndedRecord: t - }) => { - i.fragType === gu.Variant && (e.SegmentParseTime = (e.SegmentParseTime || 0) + i.parseTime, t.SegmentParseTime = (t.SegmentParseTime || 0) + i.parseTime) - }) - } - updateLevelLoaded(e, n) { - this.update(e, ({ - sessionControlRecord: e, - playLikelyToKeepUpRecord: t, - periodicRecord: i, - playEndedRecord: r - }) => { - t.PlaylistADT = (t.PlaylistADT || 0) + n.adt, i.PlaylistADT = (i.PlaylistADT || 0) + n.adt, r.PlaylistADT = (r.PlaylistADT || 0) + n.adt, i.MaxPlaylistDT = n.adt > i.MaxPlaylistDT ? n.adt : i.MaxPlaylistDT, r.MaxPlaylistDT = n.adt > r.MaxPlaylistDT ? n.adt : r.MaxPlaylistDT, r.PlayType = n.playType, this._setTargetDuration(n.url, n.targetduration, e), e.playlistMimeTypes || (e.playlistMimeTypes = []), void 0 === e.playlistMimeTypes.find(e => e == n.contentType) && e.playlistMimeTypes.push(n.contentType); - i = n.url, r = this._getVariantInfo(i, e); - e.intervalVariantList[i] || (e.intervalVariantList[i] = Object.assign({}, r)), e.sessionVariantList[i] || (e.sessionVariantList[i] = Object.assign({}, r)) - }) - } - updateLevelsChanged(e, r) { - this.update(e, ({ - sessionControlRecord: t, - playEndedRecord: e - }) => { - const i = this._computeVariantInfo(r.levels); - e.MaxVideoQltyIndex = i.maxVideoQltyIndex, e.MaxReWd = i.maxWidth, e.MaxReHt = i.maxHeight, t.manifestData = { - variantList: i.variantList, - varListString: i.varListString - }, Object.keys(i.variantList).forEach(e => { - t.intervalVariantList[e] && (t.intervalVariantList[e].brRnk = i.variantList[e].brRnk), t.sessionVariantList[e] && (t.sessionVariantList[e].brRnk = i.variantList[e].brRnk) - }) - }) - } - updateLicenseChallengeRequested(e, r) { - this.update(e, ({ - sessionControlRecord: e - }) => { - const t = {}, - i = r.timestamp; - t.licenseChallengeStartTime = i, "RTC_STATE_INIT" === e.state && (t.keyInitTime = i - e.sessionStartTime), e.activeKeySessions[r.keyuri] = t - }) - } - updateLicenseChallengeReceived(e, i) { - this.update(e, ({ - sessionControlRecord: e - }) => { - const t = e.activeKeySessions[i.keyuri]; - t.licenseChallengeRequestTime = i.timestamp - t.licenseChallengeStartTime - }) - } - updateLicenseChallengeSubmitted(e, i) { - this.update(e, ({ - sessionControlRecord: e - }) => { - const t = e.activeKeySessions[i.keyuri]; - t.keyFormat = i.keyFormat, t.licenseChallengeSubmitTime = i.timestamp - }) - } - updateLicenseChallengeCreated(e, i) { - this.update(e, ({ - sessionControlRecord: e - }) => { - const t = e.activeKeySessions[i.keyuri]; - t.cdmVersion = i.cdmVersion, t.licenseChallengeCreationTime = i.timestamp - t.licenseChallengeSubmitTime - }) - } - updateLicenseResponseRequested(e, t) { - this.update(e, ({ - sessionControlRecord: e - }) => { - e.activeKeySessions[t.keyuri].licenseResponseRequestTime = t.timestamp - }) - } - updateLicenseResponseReceived(e, i) { - this.update(e, ({ - sessionControlRecord: e - }) => { - const t = e.activeKeySessions[i.keyuri]; - t.licenseResponseReceiveTime = i.timestamp - t.licenseResponseRequestTime - }) - } - updateLicenseResponseSubmitted(e, t) { - this.update(e, ({ - sessionControlRecord: e - }) => { - e.activeKeySessions[t.keyuri].licenseResponseSubmitTime = t.timestamp - }) - } - _prepareEventPlayEnded(e) { - this.update(e, ({ - sessionControlRecord: e, - playEndedRecord: t - }) => { - t.AvgVideoBitrate = 8 * (e.sessionVideoBytes || 0) / (e.sessionVideoDuration || 1), t.AvgAudioBitrate = 8 * (e.sessionAudioBytes || 0) / (e.sessionAudioDuration || 1); - var i = 1e3 * Math.round((t.NetBytes || 0) / 1e3); - t.NetBytes = i; - var r = t.ADT || 1, - n = t.ADT + t.SegmentProcessTime || 1, - s = t.ADT + t.SegmentProcessTime + t.SegmentParseTime || 1; - t.TWOBR = 8 * i / (r / 1e3), t.PerceivedTWOBR = 8 * i / (n / 1e3), t.NetTWOBR = 8 * i / (s / 1e3); - var s = this._findTimeWeightedValues("RTC_STATE_PLAY" === e.state ? Date.now() - e.eventStartTime : 0, e.sessionVariantList, e.curLevelUrl); - t.PlayerTWIBR = s.twIBR, t.PlayerTWIABR = s.twIABR, t.TWBitRk = s.twBRnk; - s = this._getVariantInfo(e.curLevelUrl, e); - t.TargetDur = s.targetduration, t.ReWd = s.width, t.ReHt = s.height, t.VariantList = null === (s = e.manifestData) || void 0 === s ? void 0 : s.varListString; - let a = ""; - e.playlistMimeTypes && (e.playlistMimeTypes.forEach(e => { - a += e + "," - }), a = a.slice(0, -1)); - let o = ""; - e.segmentMimeTypes && (e.segmentMimeTypes.forEach(e => { - o += e + "," - }), o = o.slice(0, -1)), t.PlaylistMimeType = a, t.SegmentMimeType = o, t.Rate = e.rate - }), this._aggregateTimes(e) - } - _prepareEventPeriodic(e, o) { - this.update(e, ({ - sessionControlRecord: e, - periodicRecord: t, - playEndedRecord: i - }) => { - t.EventCounter = e.periodicEventCounter, t.PInterval = Date.now() - e.lastPeriodicTime, t.AvgVideoBitrate = 8 * (e.intervalVideoBytes || 0) / (e.intervalVideoDuration || 1), t.AvgAudioBitrate = 8 * (e.intervalAudioBytes || 0) / (e.intervalAudioDuration || 1); - let r = t.NetBytes || 0; - o.isFinal && (t.NetBytes = r = 1e3 * Math.round(r / 1e3)); - var n = t.ADT || 1, - s = t.ADT + t.SegmentProcessTime || 1, - a = t.ADT + t.SegmentProcessTime + t.SegmentParseTime || 1; - t.TWOBR = 8 * r / (n / 1e3), t.PerceivedTWOBR = 8 * r / (s / 1e3), t.NetTWOBR = 8 * r / (a / 1e3); - var a = this._findTimeWeightedValues("RTC_STATE_PLAY" === e.state ? Date.now() - e.eventStartTime : 0, e.intervalVariantList, e.curLevelUrl); - t.PlayerTWIBR = a.twIBR, t.PlayerTWIABR = a.twIABR, t.TWBitRk = a.twBRnk; - a = this._getVariantInfo(e.curLevelUrl, e); - t.TargetDur = a.targetduration, t.ReWd = a.width, t.ReHt = a.height, t.VariantList = null === (a = e.manifestData) || void 0 === a ? void 0 : a.varListString, t.Rate = e.rate; - e = this._computeMediaStats(e); - e && (t.MedianBufferAppendLatency = e.bufLatencyInfo.median, t.MaxBufferAppendLatency = e.bufLatencyInfo.max, t.MedianBufferAppendSize = e.bufSizeInfo.median, t.MaxBufferAppendSize = e.bufSizeInfo.max, t.MedianSeekLatency = e.seekLatencyInfo.median, t.MaxSeekLatency = e.seekLatencyInfo.max, t.MedianPlayLatency = e.playLatencyInfo.median, t.MaxPlayLatency = e.playLatencyInfo.max), this._copyCommonKeys(t, i) - }), this._aggregateTimes(e) - } - _prepareEventPlayStalled(e, s) { - this.update(e, ({ - sessionControlRecord: e, - playStalledRecord: t, - playEndedRecord: i - }) => { - var r = this._getVariantInfo(e.curLevelUrl, e), - n = Date.now(); - t.MediaDur = s.mediaDur, t.BitRnk = r.brRnk, t.Codecs = r.codecs, t.LastLikelyToKeepUp = n - e.lastLikelyToKeepUpTime, t.LastSwitch = n - e.lastSwitchTime, t.StallDetectionTime = s.stallDurationMs, t.StallType = s.type, t.BufferLength = s.bufferLen, "networkError" === i.ErrDomain && (t.NwErrTime = i.NwErrTime, t.NwErrCode = i.ErrCode), t.LaSwDir = e.lastSwitchDir, t.TargetDur = r.targetduration, t.PlayerIABR = r.avgBandwidth, t.PlayerIBR = r.bandwidth, t.Rate = e.rate, t.OBRLast = e.obrLast, t.OBRMean = 8 * i.NetBytes / (i.ADT / 1e3), this._copyCommonKeys(t, i) - }), this._aggregateTimes(e) - } - _prepareEventKeySessionComplete(e, r) { - this.update(e, ({ - sessionControlRecord: e, - keySessionCompleteRecord: t, - playEndedRecord: i - }) => { - e = e.activeKeySessions[r.keyuri]; - e ? (t.KeyFormat = e.keyFormat, t.CDMVersion = e.cdmVersion, t.LicenseChallengeRequestTime = e.licenseChallengeRequestTime, t.LicenseChallengeCreationTime = e.licenseChallengeCreationTime, t.LicenseResponseReceiveTime = e.licenseResponseReceiveTime, t.LicenseResponseProcessTime = e.licenseResponseProcessTime, t.KeyDeliveryTime = e.keyDeliveryTime, t.CurrentMediaTime = 1e3 * e.currentMediaTime, t.KeyInitTime = e.keyInitTime, t.KeyErrorType = e.keyErrorType, this._copyCommonKeys(t, i)) : this.logger.warn(`_prepareEventKeySessionComplete no keySessionInfo for ${le(r.keyuri)}`) - }), this._aggregateTimes(e) - } - _prepareEventPlayLikelyToKeepUp(e, n) { - this.update(e, ({ - sessionControlRecord: e, - playLikelyToKeepUpRecord: t, - playEndedRecord: i - }) => { - var r = this._getVariantInfo(e.curLevelUrl, e); - t.EventCounter = e.playLikelyToKeepUpEventCounter, t.PlayerIABR = r.avgBandwidth, t.PlayerIBR = r.bandwidth, t.MediaDur = n.mediaDur, t.BitRnk = r.brRnk, t.Codecs = r.codecs, t.TargetDur = r.targetduration, this._copyCommonKeys(t, i) - }), this._aggregateTimes(e) - } - _prepareEventPlayRateChanged(e, n) { - this.update(e, ({ - sessionControlRecord: e, - playRateChangedRecord: t, - playEndedRecord: i - }) => { - var r = this._getVariantInfo(e.curLevelUrl, e); - t.Rate = e.rate, t.StNPT = n.currentTime, t.PlayerIABR = r.avgBandwidth, t.PlayerIBR = r.bandwidth, t.BitRnk = r.brRnk, t.MediaDur = n.mediaDur, t.Codecs = r.codecs, this._copyCommonKeys(t, i) - }), this._aggregateTimes(e) - } - _prepareEventPlayError(e, s) { - this.update(e, ({ - sessionControlRecord: e, - playErrorRecord: t, - playEndedRecord: i - }) => { - var r = this._getVariantInfo(e.curLevelUrl, e), - n = Date.now(); - "mediaElementError" == s.domain ? (t.ErrDomain = t.ErrCode = "mediaElementError", t.ErrIsFatal = !0, t.ErrCodeMediaElement = s.mediaElemCode, t.ErrReason = s.mediaElemReason, t.ErrDetail = s.mediaElemDetail) : (t.ErrCode = s.details, t.ErrReason = s.code, t.ErrIsFatal = s.fatal, t.ErrDomain = "mediaError"), t.PlayerErrCount = s.count || 1, t.BitRnk = r.brRnk, t.MediaDur = s.mediaDur, t.PlayTime = i.PlayTime, t.PlayTimeWC = i.PlayTimeWC, t.PlayerIABR = r.avgBandwidth, t.PlayerIBR = r.bandwidth, t.LastLikelyToKeepUp = n - e.lastLikelyToKeepUpTime, t.LastSwitch = n - e.lastSwitchTime, t.VideoQltyIndex = r.qltyIndex, t.Rate = e.rate, t.KeyErrorType = e.lastKeyErrorType, t.KeyDeliveryTime = e.lastKeyDeliveryTime, this._copyCommonKeys(t, i) - }), this._aggregateTimes(e) - } - _prepareEventMediaEngineStalled(e, s) { - this.update(e, ({ - sessionControlRecord: e, - mediaEngineStalledRecord: t, - playEndedRecord: i - }) => { - var r = Date.now(), - n = this._getVariantInfo(e.curLevelUrl, e); - t.MediaDur = s.mediaDur, t.BitRnk = n.brRnk, t.Codecs = n.codecs, t.LastLikelyToKeepUp = r - e.lastLikelyToKeepUpTime, t.LastSwitch = r - e.lastSwitchTime, t.StallDetectionTime = s.stallDurationMs, t.StallType = s.type, t.BufferLength = s.bufferLen, t.LaSwDir = e.lastSwitchDir, t.TargetDur = n.targetduration, t.PlayerIABR = n.avgBandwidth, t.PlayerIBR = n.bandwidth, t.Rate = e.rate, t.OBRLast = e.obrLast, t.OBRMean = 8 * i.NetBytes / (i.ADT / 1e3), this._copyCommonKeys(t, i) - }), this._aggregateTimes(e) - } - _prepareEventSwitchComplete(e, s) { - this.update(e, ({ - sessionControlRecord: e, - switchCompleteRecord: t, - playEndedRecord: i - }) => { - var r = this._getVariantInfo(e.prevLevelUrl, e), - n = this._getVariantInfo(e.curLevelUrl, e); - t.FrBitRnk = r.brRnk, t.ToBitRnk = n.brRnk, t.TimeToBitrate = Date.now() - e.sessionStartTime, t.MediaDur = s.mediaDur, t.Rate = e.rate, t.PlayerIBR = n.bandwidth, t.PlayerIABR = n.avgBandwidth, t.LastPlayerIBR = r.bandwidth, t.LastPlayerIABR = r.avgBandwidth, t.ReWd = n.width, t.ReHt = n.height, this._copyCommonKeys(t, i) - }), this._aggregateTimes(e) - } - _prepareEventVariantEnded(e, n) { - this.update(e, ({ - sessionControlRecord: e, - variantEndedRecord: t, - playEndedRecord: i - }) => { - var r = this._getVariantInfo(e.curLevelUrl, e); - t.Rate = e.rate, t.VarAvgBitrate = r.avgBandwidth, t.VarPeakBitrate = r.bandwidth, t.VarBitRk = r.brRnk, t.VarSTTime = e.variantStartTimeMedia, t.VarEndTime = 1e3 * n.currentTime, t.IFR = r.framerate, t.ODR = e.decodedFramesForVariant / e.decodedFramesForVariantSampleCount, t.ReWd = r.width, t.ReHt = r.height, t.Codecs = r.codecs, t.AvgVideoBitrate = 8 * (e.variantVideoBytes || 0) / (e.variantVideoDuration || 1), t.AvgAudioBitrate = 8 * (e.variantAudioBytes || 0) / (e.variantAudioDuration || 1), this._copyCommonKeys(t, i) - }), this._aggregateTimes(e) - } - _prepareEventNwError(e, n) { - this.update(e, ({ - sessionControlRecord: e, - nwErrorRecord: t, - playEndedRecord: i - }) => { - var r = this._getVariantInfo(e.curLevelUrl, e); - t.ErrCode = n.details, t.ErrReason = n.code, t.ErrIsFatal = n.fatal, t.NwErrCount = n.count || 1, t.ErrDomain = "networkError", t.PlayTime = i.PlayTime, t.PlayTimeWC = i.PlayTimeWC, t.BitRnk = r.brRnk, t.Rate = e.rate, t.KeyErrorType = e.lastKeyErrorType, t.KeyDeliveryTime = e.lastKeyDeliveryTime, this._copyCommonKeys(t, i) - }), this._aggregateTimes(e) - } - _copyCommonKeys(e, t) { - e.PlayType = t.PlayType, e.LastMediaCDNServer = t.LastMediaCDNServer, e.MaxVideoQltyIndex = t.MaxVideoQltyIndex, e.MaxReWd = t.MaxReWd, e.MaxReHt = t.MaxReHt, e.IsGapless = t.IsGapless, e.IsAudioOnly = t.IsAudioOnly, e.IsFirstItem = t.IsFirstItem, e.ItemID = t.ItemID, e.ServerInfo = t.ServerInfo - } - _computeVariantInfo(e) { - const i = {}; - let r = 0, - n = 0, - s = 0, - a = 0; - const o = this._getMaxNormalizedPeak(e); - e.forEach(e => { - if (e.attrs) { - const r = this._getVideoFourCC(e.attrs.CODECS), - n = this._getVideoQualityIndex(r, e.attrs["VIDEO-RANGE"]) || 0, - s = { - codecs: e.attrs.CODECS, - width: e.width, - height: e.height, - bandwidth: e.attrs.BANDWIDTH, - avgBandwidth: e.attrs["AVERAGE-BANDWIDTH"], - framerate: e.attrs["FRAME-RATE"], - iframes: e.iframes, - brRnk: this._getBitrateRank(e.attrs.BANDWIDTH, r, o), - qltyIndex: n, - targetduration: 0, - playTime: 0 - }; - a = n > a ? n : a, i[e.url] = s - } - var t = e.width * e.height; - t > s && (s = t, r = e.width, n = e.height) - }); - e = 0 < Object.keys(i).length ? Object.values(i).map(e => e.bandwidth + ":" + e.avgBandwidth).join(",") : void 0; - return { - variantList: i, - varListString: e, - maxVideoQltyIndex: a, - maxWidth: r, - maxHeight: n - } - } - _getMaxNormalizedPeak(e) { - let n = 0; - return e.forEach(e => { - e = e.attrs; - if (e) { - const t = e.BANDWIDTH || 0, - i = this._getNormalizedPeak(t, this._getVideoFourCC(e.CODECS)), - r = Math.max(t, i); - n = r > n ? r : n - } - }), n - } - _getNormalizedPeak(e, t) { - return "object" == typeof Ff[t] ? 1.5 * e : +e - } - _getVideoFourCC(e) { - return e && e.split(",").map(e => e.split(".")[0].trim()).find(e => !!Ff[e]) - } - _getVideoQualityIndex(e, t) { - return "object" == typeof Ff[e] ? Ff[e][t] : Ff[e] - } - _getBitrateRank(e, t, i) { - t = Math.max(1, this._getNormalizedPeak(e, t)); - return Math.ceil(100 * t / i) - } - _findTimeWeightedValues(o, l, d) { - const e = { - twBRnk: 0, - twIBR: 0, - twIABR: 0 - }; - if (l) { - let i = 0, - r = 0, - n = 0, - s = 0, - a = 0; - Object.values(l).forEach(e => { - let t = e.playTime; - e === l[d] && (t += o || 0), t && (r += e.bandwidth * t, i += e.brRnk * t, n += t, e.avgBandwidth && (s += e.avgBandwidth * t, a += t)) - }), n && (e.twBRnk = i / n, e.twIBR = r / n), s && a && (e.twIABR = s / a) - } - return e - } - _computeMediaStats(e) { - const t = e.bufferAppendInfo; - let i = []; - const r = []; - t && t.forEach && t.forEach(e => { - i.push(e.latency), r.push(e.size) - }); - var n = this._computeStats(i), - s = this._computeStats(r), - a = e.seekInfo; - i = [], a && a.forEach && e.seekInfo.forEach(e => i.push(e.latency)); - var o = this._computeStats(i), - a = e.playInfo; - return i = [], a && a.forEach && e.playInfo.forEach(e => i.push(e.latency)), { - bufLatencyInfo: n, - bufSizeInfo: s, - seekLatencyInfo: o, - playLatencyInfo: this._computeStats(i) - } - } - _computeStats(e) { - let t, i; - if (e) { - const r = e.filter(e => 0 < e); - if (0 < r.length) { - t = r.reduce((e, t) => Math.max(e, t)); - const e = r.length, - n = r.sort((e, t) => e - t), - s = Math.ceil(e / 2); - i = e % 2 == 0 ? (n[s] + n[s - 1]) / 2 : n[s - 1] - } - } - return { - max: t, - median: i - } - } - _getVariantInfo(e, t) { - return e && t.manifestData && t.manifestData.variantList && t.manifestData.variantList[e] ? t.manifestData.variantList[e] : {} - } - _setTargetDuration(e, t, i) { - e && i.manifestData && i.manifestData.variantList && i.manifestData.variantList[e] && (i.manifestData.variantList[e].targetduration = t) - } - _isBadSw(e, t, i, r, n, s, a) { - let o = n - s < 1e4 && t !== e && r === i && !a ? !0 : !1; - return o - } - _aggregateTimes(e) { - this.update(e, ({ - sessionControlRecord: t, - playLikelyToKeepUpRecord: i, - playStalledRecord: r, - mediaEngineStalledRecord: n, - switchCompleteRecord: s, - playRateChangedRecord: a, - variantEndedRecord: o, - playErrorRecord: l, - nwErrorRecord: d, - periodicRecord: u, - playEndedRecord: c - }) => { - var h = Date.now() - t.eventStartTime; - switch (t.state) { - case "RTC_STATE_INIT": - i.StartupTime = (i.StartupTime || 0) + h, u.InitTime = (u.InitTime || 0) + h, c.InitTime = (c.InitTime || 0) + h; - break; - case "RTC_STATE_CANPLAY": - a.StartupTime = (a.StartupTime || 0) + h, u.InitTime = (u.InitTime || 0) + h, c.InitTime = (c.InitTime || 0) + h; - break; - case "RTC_STATE_PAUSE": - u.PauseTime = (u.PauseTime || 0) + h, c.PauseTime = (c.PauseTime || 0) + h, a.LastPause = (a.LastPause || 0) + h, c.LastPause = (c.LastPause || 0) + h, l.LastPause = (l.LastPause || 0) + h, d.LastPause = (d.LastPause || 0) + h; - break; - case "RTC_STATE_STALL": - o.StallTime = (o.StallTime || 0) + h, u.StallTime = (u.StallTime || 0) + h, c.StallTime = (c.StallTime || 0) + h, a.LastStall = (a.LastStall || 0) + h, l.LastStall = (l.LastStall || 0) + h, c.LastStall = (c.LastStall || 0) + h; - break; - case "RTC_STATE_MEDIAENGINESTALL": - o.MediaEngineStallTime = (o.MediaEngineStallTime || 0) + h, u.MediaEngineStallTime = (u.MediaEngineStallTime || 0) + h, c.MediaEngineStallTime = (c.MediaEngineStallTime || 0) + h, a.LastMediaEngineStall = (a.LastMediaEngineStall || 0) + h, l.LastMediaEngineStall = (l.LastMediaEngineStall || 0) + h, c.LastMediaEngineStall = (c.LastMediaEngineStall || 0) + h; - break; - case "RTC_STATE_NWERROR": - c.NwErrTime = (c.NwErrTime || 0) + h, u.NwErrTime = (u.NwErrTime || 0) + h; - break; - case "RTC_STATE_PLAYERROR": - u.PlayErrTime = (u.PlayErrTime || 0) + h, c.PlayErrTime = (c.PlayErrTime || 0) + h; - break; - case "RTC_STATE_PLAY": { - a.RateChangePlayTime = (a.RateChangePlayTime || 0) + h, s.PlayTime = (s.PlayTime || 0) + h, s.PlayTimeLastSW = (s.PlayTimeLastSW || 0) + h * (t.oldRate / 100), r.LastResume = (r.LastResume || 0) + h, n.LastResume = (n.LastResume || 0) + h, l.LastResume = (l.LastResume || 0) + h, d.LastResume = (d.LastResume || 0) + h, o.VarPlayTimeWC = (o.VarPlayTimeWC || 0) + h, o.VarPlayTime = (o.VarPlayTime || 0) + h * (t.oldRate / 100), u.PlayTimeWC = (u.PlayTimeWC || 0) + h, u.PlayTime = (u.PlayTime || 0) + h * (t.oldRate / 100), c.PlayTimeWC = (c.PlayTimeWC || 0) + h, c.PlayTime = (c.PlayTime || 0) + h * (t.oldRate / 100), n.PlayTime = c.PlayTime, r.PlayTime = c.PlayTime; - const i = t.curLevelUrl; - let e = t.intervalVariantList[i]; - e.playTime = (e.playTime || 0) + h, e = t.sessionVariantList[i], e.playTime = (e.playTime || 0) + h; - break - } - } - }) - } - } - const Vf = { - name: "rtc-service" - }; - class Kf { - constructor(e, t, i, r) { - this.hls = e, this.config = t, this.accessLog = i, this.logger = r, this.destroy$ = new Xt, this.isSeeking = !1, this.seekStart = null, this.periodicInterval = t.rtcIntervalTimeout || 3e5, this.intervalFunc = null, this.rtcStore = new $f(this.logger), this.rtcQuery = new Uf(this.rtcStore, this.logger), this.rtcComponent = new Bf(this.rtcQuery, this.logger), i.setRTCQuery(this.rtcQuery), this.subscribeAndUpdateStore(), this.registerForEvents() - } - destroy() { - this.destroy$.next(), this.clearPeriodic(), this.rtcStore.reset() - } - detachMedia() { - this.clearPeriodic(); - var e; - try { - e = this.hls.realCurrentTime, this.rtcStore.updateVariantEnd(this.rtcEventItemId(!0), { - currentTime: e - }), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.VariantEnded), this.rtcStore.updatePeriodic(this.rtcEventItemId(!0), !0), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.Periodic), this.rtcStore.updateEnded(this.rtcEventItemId(!0)), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.PlayEnded) - } catch (e) { - this.logger.warn(Vf, e) - } - } - handleError(e) { - var t = e instanceof p ? e : new V(!0, e.message, $.InternalError); - t instanceof uc && !t.fatal ? (this.rtcStore.updateLevelLoadError(this.rtcEventItemId(), { - url: t.url, - mediaDur: this.hls.bufferedDuration, - isSeeking: this.isSeeking - }), this.sendAndFinalize(this.rtcEventItemId(), Nf.SwitchComplete)) : t.type === o ? (this.rtcStore.updateNwError(this.rtcEventItemId(), { - fatal: t.fatal, - details: t.details, - code: null === (e = t.response) || void 0 === e ? void 0 : e.code - }), this.sendAndFinalize(this.rtcEventItemId(), Nf.NwError)) : (this.rtcStore.updateMediaError(this.rtcEventItemId(), { - fatal: t.fatal, - details: t.details, - code: null === (t = t.response) || void 0 === t ? void 0 : t.code - }), this.sendAndFinalize(this.rtcEventItemId(), Nf.PlayError)) - } - handleMediaElementError(e) { - this.rtcStore.updateMediaElementError(this.rtcEventItemId(), e), this.sendAndFinalize(this.rtcEventItemId(), Nf.PlayError) - } - handleFragLoaded(e, t) { - var i, r, n; - this.checkMediaOptionType(e.mediaOptionType) && (e.itemId !== this.rtcEventItemId() && this.logger.warn(Vf, `Frag id does not match current item id. Frag Id=${e.itemId}, playing id=${this.rtcEventItemId(!0)}, loading id=${null===(n=this.hls.loadingItem)||void 0===n?void 0:n.itemId}`), i = t.tload - t.trequest, r = t.tload - t.tfirst, n = this.serverInfoInstance || {}, this.rtcStore.updateFragLoaded(e.itemId, { - fragType: e.mediaOptionType, - bytes: t.loaded, - duration: e.duration, - adt: i, - processTime: r, - contentType: t.contentType, - cdnServer: t.cdnServer, - serverInfo: n - }), this.accessLog.updateFragLoaded(e.itemId, this.isSeeking, { - fragType: e.mediaOptionType, - bytes: t.loaded, - duration: e.duration, - adt: i, - processTime: r, - startPTS: e.start, - endPTS: e.start + e.duration - })) - } - handleFragBuffered(e) { - var t; - this.checkMediaOptionType(e.fragmentType) && (t = e.endDataAppend - e.startDataAppend, this.rtcStore.updateFragBuffered(this.rtcEventItemId(), { - fragType: e.fragmentType, - bytes: e.dataBytesAppend, - parseTime: t - })) - } - handleLevelLoaded(e, t) { - var i; - e ? (e.itemId !== this.rtcEventItemId() && this.logger.warn(Vf, `media option id does not match current item id. media Id=${e.itemId}, current id=${this.rtcEventItemId}`), e.mediaOptionType === gu.Variant && (i = t.tload - t.trequest, this.rtcStore.updateLevelLoaded(this.rtcEventItemId(), { - url: e.url, - targetduration: e.targetduration, - adt: i, - contentType: t.contentType, - playType: e.type - }))) : this.logger.warn(`handleLevelLoaded called with mediaOptionDetails as ${e}`) - } - handleLevelSwitched(e) { - var t = { - url: e.url, - isSeeking: this.isSeeking, - mediaDur: this.hls.bufferedDuration, - currentTime: this.hls.realCurrentTime - }; - e.oldVariant && (this.rtcStore.updateVariantEnd(this.rtcEventItemId(), { - currentTime: this.hls.realCurrentTime - }), this.sendAndFinalize(this.rtcEventItemId(), Nf.VariantEnded)), this.rtcStore.updateLevelSwitched(this.rtcEventItemId(), t), this.sendAndFinalize(this.rtcEventItemId(), Nf.SwitchComplete) - } - handleLevelSwitching(e) { - this.levelSwitchingUrl = e - } - handleLevelsChanged(e) { - this.rtcStore.updateLevelsChanged(this.rtcEventItemId(), { - levels: e - }) - } - handleManifestParsed(e) { - var t = e.stats.tload - e.stats.trequest; - this.rtcStore.updateManifestParsed(this.rtcEventItemId(), { - levels: e.levels, - adt: t, - contentType: e.stats.contentType, - isAudioOnly: this.hls.inGaplessMode, - isGapless: this.hls.inGaplessMode, - isFirstItem: this.hls.isFirstItem, - itemID: ((null === (e = this.hls.reportingAgent) || void 0 === e ? void 0 : e.SessionID) || Zl()) + "-" + this.rtcEventItemId() - }) - } - handleSeek(e) { - if ("SEEKING" === e) this.isSeeking = !0, this.seekStart = Date.now(); - else if ("SEEKED" === e) { - this.isSeeking = !1; - let e = 0; - this.seekStart && (e = Date.now() - this.seekStart), this.seekStart = null, this.rtcStore.updateSeeked(this.rtcEventItemId(!0), { - latency: e - }) - } - } - handleDesiredRateChanged(e, t) { - 0 === t || 1 < Math.abs(e) && 1 < Math.abs(t) ? (this.rtcStore.updateRateChanged(this.rtcEventItemId(!0), { - rate: t, - latency: 0, - mediaDur: this.hls.bufferedDuration, - currentTime: this.hls.realCurrentTime, - url: this.levelSwitchingUrl - }), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.PlayRateChanged)) : 1 !== e && 1 === t && (this.playStart = Date.now()), this.oldRate = e, this.newRate = t - } - handleVariantBufferAppended(e, t) { - let i = 0; - e && (i = Date.now() - e), this.rtcStore.updateBufferAppended(this.rtcEventItemId(), { - latency: i, - size: t - }) - } - handleStalled(e, t) { - var i = { - type: e.type, - stallDurationMs: e.stallDurationMs, - bufferLen: t, - mediaDur: this.hls.bufferedDuration - }, - t = this.rtcQuery.getEntity(this.rtcEventItemId(!0)).sessionControlRecord.state; - e.type === Cp.LowBuffer || e.type === Cp.Seek && e.isLowBufferStall ? "RTC_STATE_PLAY" === t && (this.rtcStore.updateBufferStalled(this.rtcEventItemId(!0), i), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.PlayStalled)) : "RTC_STATE_PLAY" === t && (this.rtcStore.updateMediaEngineStalled(this.rtcEventItemId(!0), i), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.MediaEngineStalled)) - } - handlePlaybackInfo(e, t) { - this.rtcStore.updatePlaybackInfo(this.rtcEventItemId(!0), { - droppedVideoFrames: e, - decodedFrameCount: t - }), this.accessLog.updatePlaybackInfo(this.rtcEventItemId(!0), { - droppedVideoFrames: e, - decodedFrameCount: t - }) - } - checkMediaOptionType(e) { - return e === gu.Variant || e === gu.AltAudio || (this.logger.error(Vf, 'Should not have media option type = "%s" in RTC', Nu[e]), !1) - } - rtcEventItemId(e = !1) { - return (this.hls.isPreloading ? e ? this.hls.playingItem : this.hls.loadingItem : this.hls.currentItem).itemId - } - subscribeAndUpdateStore() { - this.hls.publicQueries$.pipe(La(([, e]) => this.mediaElementQueryListener(e)), Va(this.destroy$)).subscribe(), this.hls.itemQueue.activeItemById$.pipe(Za(t => { - if (t) { - let e = !1; - if (this.hls.userInfo ? this.hls.userInfo.internalBuild ? e = !0 : this.hls.userInfo.diagnosticsAndUsage && (e = this.config.enableRtcReporting) : e = this.config.enableRtcReporting, e) { - const i = this.hls.reportingAgent; - i ? this.rtcComponent.setReportingAgent(i) : this.logger.warn(Vf, "[RTCA] - Reporting is enabled but reportingAgent is null") - } else this.rtcComponent.setReportingAgent(null); - this.serverInfoInstance = null; - t = t.itemId; - this.rtcStore.createEntity(t), !this.hls.isFirstItem && this.hls.inGaplessMode || this.setPeriodic(t) - } - }), Va(this.destroy$)).subscribe() - } - itemTransitioned(e, t) { - this.rtcStore.updateVariantEnd(e, { - currentTime: this.hls.realCurrentTime - }), this.sendAndFinalize(e, Nf.VariantEnded), this.rtcStore.updatePeriodic(e, !0), this.sendAndFinalize(e, Nf.Periodic), this.rtcStore.updateEnded(e), this.sendAndFinalize(e, Nf.PlayEnded), this.setPeriodic(t) - } - mediaElementQueryListener(e) { - return e.gotPlaying$.pipe(Za(e => { - if (e) { - const e = this.oldRate, - t = this.newRate || 1; - 1 < Math.abs(e) && 1 < Math.abs(t) || (this.rtcStore.updateCanPlay(this.rtcEventItemId(!0), { - mediaDur: this.hls.bufferedDuration - }), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.PlayLikelyToKeepUp), this.rtcStore.updateRateChanged(this.rtcEventItemId(!0), { - rate: t, - latency: ne(this.playStart) ? Date.now() - this.playStart : 0, - mediaDur: this.hls.bufferedDuration, - currentTime: this.hls.realCurrentTime, - url: this.levelSwitchingUrl - }), this.sendAndFinalize(this.rtcEventItemId(!0), Nf.PlayRateChanged)) - } - })) - } - registerForEvents() { - const e = Oc(this.hls, this); - an(e.event(P.KEY_REQUEST_STARTED, this.keyRequestStarted, this), e.event(P.KEY_LOADED, this.keyLoaded, this)).pipe(Va(this.destroy$)).subscribe() - } - keyRequestStarted(e) { - e.timestamp = Date.now(), this.rtcStore.updateLicenseChallengeRequested(this.rtcEventItemId(), e) - } - keyLoaded(e) { - e.timestamp = Date.now(), e.currentTime = this.hls.realCurrentTime, this.rtcStore.updateSegmentKeyLoaded(this.rtcEventItemId(), e) - } - licenseChallengeReceived(e) { - this.rtcStore.updateLicenseChallengeReceived(this.rtcEventItemId(), { - timestamp: Date.now(), - keyuri: e.keyuri - }) - } - licenseChallengeSubmitted(e) { - this.rtcStore.updateLicenseChallengeSubmitted(this.rtcEventItemId(), { - timestamp: Date.now(), - keyFormat: e.keyFormat, - keyuri: e.keyuri - }) - } - licenseChallengeCreated(e) { - this.rtcStore.updateLicenseChallengeCreated(this.rtcEventItemId(), { - timestamp: Date.now(), - cdmVersion: e.cdmVersion, - keyuri: e.keyuri - }), this.rtcStore.updateLicenseResponseRequested(this.rtcEventItemId(), { - timestamp: Date.now(), - keyuri: e.keyuri - }) - } - licenseResponseSubmitted(e) { - this.rtcStore.updateLicenseResponseReceived(this.rtcEventItemId(), { - timestamp: Date.now(), - keyuri: e.keyuri - }), this.rtcStore.updateLicenseResponseSubmitted(this.rtcEventItemId(), { - timestamp: Date.now(), - keyuri: e.keyuri - }) - } - licenseResponseProcessed(e) { - this.rtcStore.updateLicenseResponseProcessed(this.rtcEventItemId(), { - timestamp: Date.now(), - keyuri: e.keyuri, - currentTime: this.hls.realCurrentTime - }), this.sendAndFinalize(this.rtcEventItemId(), Nf.KeySessionComplete) - } - licenseChallengeError(e) { - this.rtcStore.updateLicenseChallengeError(this.rtcEventItemId(), { - timestamp: Date.now(), - keyuri: e.keyuri - }), this.sendAndFinalize(this.rtcEventItemId(), Nf.KeySessionComplete) - } - licenseResponseError(e) { - this.rtcStore.updateLicenseResponseError(this.rtcEventItemId(), { - timestamp: Date.now(), - keyuri: e.keyuri - }), this.sendAndFinalize(this.rtcEventItemId(), Nf.KeySessionComplete) - } - keyAborted(e) { - var t; - null != (null === (t = this.rtcQuery.getEntity(this.rtcEventItemId())) || void 0 === t ? void 0 : t.sessionControlRecord.activeKeySessions[e.keyuri]) ? (this.rtcStore.updateKeyAborted(this.rtcEventItemId(), { - timestamp: Date.now(), - keyuri: e.keyuri - }), this.sendAndFinalize(this.rtcEventItemId(), Nf.KeySessionComplete)) : this.logger.warn(`keyAbort called without active key session ${le(e.keyuri)}`) - } - setPeriodic(e) { - this.clearPeriodic(), this.intervalFunc = setInterval(this.handlePeriodic.bind(this, e), this.periodicInterval) - } - handlePeriodic(e) { - this.rtcStore.updatePeriodic(e, !1), this.sendAndFinalize(e, Nf.Periodic) - } - clearPeriodic() { - this.intervalFunc && clearInterval(this.intervalFunc), this.intervalFunc = null - } - sendAndFinalize(e, t) { - switch (this.accessLog.addPlayTime(e), t) { - case Nf.PlayEnded: - this.rtcComponent.sendPlayEnded(e); - break; - case Nf.Periodic: - this.rtcComponent.sendPeriodic(e); - break; - case Nf.PlayStalled: - this.accessLog.updateStallCount(e), this.rtcComponent.sendPlayStalled(e); - break; - case Nf.KeySessionComplete: - this.rtcComponent.sendKeySessionComplete(e); - break; - case Nf.PlayLikelyToKeepUp: - this.accessLog.updateCanPlay(e), this.rtcComponent.sendPlayLikelyToKeepUp(e); - break; - case Nf.PlayRateChanged: - this.rtcComponent.sendPlayRateChange(e); - break; - case Nf.PlayError: - this.accessLog.addToErrorLog(e, "mediaError"), this.rtcComponent.sendPlayError(e); - break; - case Nf.MediaEngineStalled: - this.accessLog.updateMediaEngineStallCount(e), this.rtcComponent.sendMediaEngineStalled(e); - break; - case Nf.SwitchComplete: - this.accessLog.addToAccessLog(e), this.rtcComponent.sendSwitchComplete(e); - break; - case Nf.VariantEnded: - this.rtcComponent.sendVariantEnded(e); - break; - case Nf.NwError: - this.accessLog.addToErrorLog(e, "networkError"), this.rtcComponent.sendNwError(e); - break; - default: - return void this.logger.error(Vf, `Unknown rtc event eventGroupId:${e}`) - } - this.rtcStore.finalize(e, t) - } - } - const qf = a => (e, t) => { - let i = 0, - r = 0; - for (var { - timestamp: n, - value: s - } of e) { - const e = Math.pow(Math.max(0, n - t) / 1e3, a); - i += e * s, r += e - } - return i / r - }, - Hf = { - "uniform-time-weighted": qf(0), - "linear-time-weighted": qf(1), - "quadratic-time-weighted": qf(2) - }; - class jf { - constructor(e, t = "quadratic-time-weighted", i = { - avgLatencyMs: NaN, - avgBandwidth: NaN - }) { - this.windowSize = e, this.aggregationMethod = t, this.latencyEntries = [], this.bandwidthEntries = [], this.minEntries = 1, this.cleanUpExpiredEntries = this.cleanUpExpiredEntries.bind(this), this.bwSubject = new yi(i) - } - get estimate$() { - return this.bwSubject.asObservable() - } - record(e) { - var { - trequest: t, - tfirst: i, - tload: r, - bitsDownloaded: e - } = e; - t !== r && (this.recordLatency(t, i), this.recordBandwidth(t, r, 1e3 * e / (r - t)), this.bwSubject.closed || (t = this.getEstimate(), this.bwSubject.next(t))) - } - getEstimate() { - if (this.latencyEntries.length < this.minEntries) return { - avgLatencyMs: NaN, - avgBandwidth: NaN - }; - const e = performance.now() - this.windowSize, - t = Hf[this.aggregationMethod], - i = this.latencyEntries.map(({ - start: e, - end: t - }) => ({ - timestamp: t, - value: t - e, - duration: 1 - })); - this.bandwidthEntries = function(r) { - function n(t, i) { - if (t.length) { - for (let e = 0; e < t.length; e++) - if (t[e].start > i.start || t[e].start === i.start && t[e].end > i.end) { - t.splice(e, 0, i); - break - } - } else t.push(i) - } - const s = [...r].sort((e, t) => e.start !== t.start ? e.start - t.start : e.end - t.end), - t = []; - for (; s.length;) { - const r = s[0]; - let e; - if (s.shift(), t.length && (e = t[t.length - 1]), 0 === t.length || e.end <= r.start) t.push(r); - else if (r.start === e.start) r.end === e.end ? e.bitsPerSec += r.bitsPerSec : r.end < e.end || (e.bitsPerSec += r.bitsPerSec, r.start = e.end, n(s, r)); - else { - var a = e.end, - o = e.bitsPerSec; - e.end = r.start; - var i = { - start: r.start, - end: Math.min(a, r.end), - bitsPerSec: r.bitsPerSec + o - }; - if (t.push(i), a !== r.end) { - let e = 0, - t = 0, - i = 0; - i = a < r.end ? (e = a, t = r.end, r.bitsPerSec) : (e = r.end, t = a, o), n(s, { - start: e, - end: t, - bitsPerSec: i - }) - } - } - } - return t - }(this.bandwidthEntries); - var r = this.bandwidthEntries.map(({ - end: e, - bitsPerSec: t - }) => ({ - timestamp: e, - duration: 1, - value: t - })); - return { - avgLatencyMs: t(i, e), - avgBandwidth: t(r, e) - } - } - getLatest() { - if (0 === this.latencyEntries.length) return { - avgLatencyMs: NaN, - avgBandwidth: NaN - }; - var e = this.latencyEntries[this.latencyEntries.length - 1], - t = this.bandwidthEntries[this.bandwidthEntries.length - 1]; - return { - avgLatencyMs: e.end - e.start, - avgBandwidth: t.bitsPerSec - } - } - recordLatency(e, t) { - this.latencyEntries.push({ - start: e, - end: t - }), this.updateCleanupTimeout(t) - } - recordBandwidth(e, t, i) { - this.bandwidthEntries.push({ - start: e, - end: t, - bitsPerSec: i - }), this.updateCleanupTimeout(t) - } - setCleanupTimeout(e) { - this.cleanupTimeout = setTimeout(this.cleanUpExpiredEntries, Math.max(e - performance.now(), 0)), this.cleanupTimestamp = e - } - clearCleanupTimeout() { - void 0 !== this.cleanupTimeout && (clearTimeout(this.cleanupTimeout), this.cleanupTimeout = void 0), this.cleanupTimestamp = void 0 - } - updateCleanupTimeout(e) { - e += this.windowSize; - (!this.cleanupTimestamp || e < this.cleanupTimestamp) && (this.clearCleanupTimeout(), this.setCleanupTimeout(e)) - } - cleanUpExpiredEntries() { - this.clearCleanupTimeout(); - const t = performance.now() - this.windowSize; - if (this.latencyEntries = this.latencyEntries.filter(e => e.end >= t), this.bandwidthEntries = this.bandwidthEntries.filter(e => e.end >= t), this.bwSubject.closed || this.bwSubject.next(this.getEstimate()), 0 < this.latencyEntries.length || 0 < this.bandwidthEntries.length) { - const t = Math.min(...this.latencyEntries.map(e => e.end), ...this.bandwidthEntries.map(e => e.end)); - this.updateCleanupTimeout(t) - } - } - destroy() { - this.clearCleanupTimeout() - } - } - const Qf = { - setCombinedEstimate: function(t, i, r) { - const n = Qe(); - if (void 0 !== t.storage.set) { - var s = t.bandwidthHistoryStorageKey, - a = { - avgLatencyMs: i.avgLatencyMs, - avgBandwidth: i.avgBandwidth - }, - a = Object.assign({}, a, { - expires: Date.now() + t.bandwidthHistoryTTL - }); - try { - t.storage.set(s, JSON.stringify(a)) - } catch (t) { - n.warn(`Error stringifying! Not persisting bandwidth estimates: ${t.message}`) - } - i = { - maxDuration: i.maxDurationSec, - avgFragParseTimeMs: i.avgParseTimeMs, - avgFragBufferCreationDelayMs: i.avgBufferCreateMs, - avgPlaylistLoadTimeMs: i.avgPlaylistLoadTimeMs, - avgPlaylistParseTimeMs: i.avgPlaylistParseTimeMs, - avgInitFragAppendMs: i.avgInitFragAppendMs, - avgDataFragAppendMs: i.avgDataFragAppendMs - }; - let e = t.storageKeyPrefix; - r && (e += r); - try { - t.storage.set(e, JSON.stringify(i)) - } catch (t) { - n.warn(`Error stringifying! Not persisting bandwidth estimates: ${t.message}`) - } - } else n.warn("storage.set is not supported! Not persisting bandwidth estimates") - }, - getCombinedEstimate: function(t, e) { - const i = Qe(); - let r = {}; - if (void 0 === t.storage.get) return i.warn("storage.get is not supported! unable to retreive bandwidth estimates"), this.convertStorageJsonToCombinedEstimate(r); - try { - let e = JSON.parse(t.storage.get(t.bandwidthHistoryStorageKey)); - e = null != e && e.expires && e.expires < Date.now() ? null : { - avgLatencyMs: null == e ? void 0 : e.avgLatencyMs, - avgBandwidth: null == e ? void 0 : e.avgBandwidth - }, r = Object.assign(Object.assign({}, r), e) - } catch (t) { - i.warn(`Unable to get persisted bandwidth history: ${t.message}`) - } - let n = t.storageKeyPrefix; - e && (n += e); - try { - const e = JSON.parse(t.storage.get(n)); - r = Object.assign(Object.assign({}, r), e) - } catch (t) { - i.warn(`Unable to get persisted bandwidth history: ${t.message}`) - } - return this.convertStorageJsonToCombinedEstimate(r) - }, - convertStorageJsonToCombinedEstimate: function(e) { - return { - avgLatencyMs: (null == e ? void 0 : e.avgLatencyMs) || NaN, - avgBandwidth: (null == e ? void 0 : e.avgBandwidth) || NaN, - maxDurationSec: (null == e ? void 0 : e.maxDuration) || NaN, - avgParseTimeMs: (null == e ? void 0 : e.avgFragParseTimeMs) || NaN, - avgBufferCreateMs: (null == e ? void 0 : e.avgFragBufferCreationDelayMs) || NaN, - avgPlaylistLoadTimeMs: (null == e ? void 0 : e.avgPlaylistLoadTimeMs) || NaN, - avgPlaylistParseTimeMs: (null == e ? void 0 : e.avgPlaylistParseTimeMs) || NaN, - avgInitFragAppendMs: (null == e ? void 0 : e.avgInitFragAppendMs) || NaN, - avgDataFragAppendMs: (null == e ? void 0 : e.avgDataFragAppendMs) || NaN - } - }, - getBandwidthEstimate: function(e, t) { - const i = this.getCombinedEstimate(e, t), - r = { - avgLatencyMs: null == i ? void 0 : i.avgLatencyMs, - avgBandwidth: null == i ? void 0 : i.avgBandwidth - }; - return ne(r.avgLatencyMs) || (r.avgLatencyMs = NaN), ne(r.avgBandwidth) || (r.avgBandwidth = NaN), r - }, - getPlaylistEstimate: function(e, t) { - const i = this.getCombinedEstimate(e, t), - r = { - avgPlaylistLoadTimeMs: null == i ? void 0 : i.avgPlaylistLoadTimeMs, - avgPlaylistParseTimeMs: null == i ? void 0 : i.avgPlaylistParseTimeMs - }; - return ne(r.avgPlaylistLoadTimeMs) || (r.avgPlaylistLoadTimeMs = NaN), ne(r.avgPlaylistParseTimeMs) || (r.avgPlaylistParseTimeMs = NaN), r - }, - getFragEstimate: function(e, t) { - const i = this.getCombinedEstimate(e, t), - r = { - maxDurationSec: null == i ? void 0 : i.maxDurationSec, - avgParseTimeMs: null == i ? void 0 : i.avgParseTimeMs - }; - return ne(r.maxDurationSec) || (r.maxDurationSec = NaN), ne(r.avgParseTimeMs) || (r.avgParseTimeMs = NaN), r - }, - getBufferEstimate: function(e, t) { - const i = this.getCombinedEstimate(e, t), - r = { - avgBufferCreateMs: null == i ? void 0 : i.avgBufferCreateMs, - avgInitFragAppendMs: null == i ? void 0 : i.avgInitFragAppendMs, - avgDataFragAppendMs: null == i ? void 0 : i.avgDataFragAppendMs - }; - return ne(r.avgBufferCreateMs) || (r.avgBufferCreateMs = NaN), ne(r.avgInitFragAppendMs) || (r.avgInitFragAppendMs = NaN), ne(r.avgDataFragAppendMs) || (r.avgDataFragAppendMs = NaN), r - } - }; - var Wf = Qf; - class Gf { - constructor(e = 0) { - this._minSamples = e, this._sum = 0, this._max = Number.NEGATIVE_INFINITY, this._numSamples = 0 - } - get avg() { - return this._numSamples < this._minSamples ? NaN : this._sum / this._numSamples - } - get max() { - return 0 < this.count ? this._max : NaN - } - get count() { - return this._numSamples - } - reset() { - this._sum = 0, this._numSamples = 0, this._max = Number.NEGATIVE_INFINITY - } - add(e) { - this._sum += e, this._max = Math.max(this._max, e), ++this._numSamples - } - } - class zf extends kl { - constructor(e, t) { - super(e), this.id = t - } - getBandwidthEstimate(e, t) { - var i; - const r = Object.assign({}, null === (i = this.statsEntity) || void 0 === i ? void 0 : i.bandwidthEstimate); - if (ne(r.avgBandwidth) && ne(r.avgLatencyMs)) return r; - if (e) { - const i = Qf.getBandwidthEstimate(e, t); - ne(r.avgBandwidth) || (r.avgBandwidth = i.avgBandwidth), ne(r.avgLatencyMs) || (r.avgLatencyMs = i.avgLatencyMs) - } - return r - } - getPlaylistEstimate(e, t) { - var i; - const r = Object.assign({}, null === (i = this.statsEntity) || void 0 === i ? void 0 : i.playlistEstimate), - n = e => ne(e.avgPlaylistLoadTimeMs) && ne(e.avgPlaylistParseTimeMs); - if (n(r)) return r; - if (e) { - const i = Qf.getPlaylistEstimate(e, t); - if (ne(r.avgPlaylistLoadTimeMs) || (r.avgPlaylistLoadTimeMs = i.avgPlaylistLoadTimeMs), ne(r.avgPlaylistParseTimeMs) || (r.avgPlaylistParseTimeMs = i.avgPlaylistParseTimeMs), n(r)) return r; - ne(r.avgPlaylistLoadTimeMs) || (r.avgPlaylistLoadTimeMs = e.statDefaults.playlistLoadTimeMs), ne(r.avgPlaylistParseTimeMs) || (r.avgPlaylistParseTimeMs = e.statDefaults.playlistParseTimeMs) - } - return r - } - getBufferEstimate(e, t) { - var i; - const r = Object.assign({}, null === (i = this.statsEntity) || void 0 === i ? void 0 : i.bufferEstimate), - n = e => ne(e.avgBufferCreateMs) && ne(e.avgDataFragAppendMs) && ne(e.avgInitFragAppendMs); - if (n(r)) return r; - if (e) { - const i = Qf.getBufferEstimate(e, t); - if (ne(r.avgBufferCreateMs) || (r.avgBufferCreateMs = i.avgBufferCreateMs), ne(r.avgDataFragAppendMs) || (r.avgDataFragAppendMs = i.avgDataFragAppendMs), ne(r.avgInitFragAppendMs) || (r.avgInitFragAppendMs = i.avgInitFragAppendMs), n(r)) return r; - ne(r.avgBufferCreateMs) || (r.avgBufferCreateMs = e.statDefaults.fragBufferCreationDelayMs), ne(r.avgDataFragAppendMs) || (r.avgDataFragAppendMs = e.statDefaults.dataFragAppendMs), ne(r.avgInitFragAppendMs) || (r.avgInitFragAppendMs = e.statDefaults.initFragAppendMs) - } - return r - } - getFragEstimate(e, t) { - var i; - const r = Object.assign({}, null === (i = this.statsEntity) || void 0 === i ? void 0 : i.fragEstimate), - n = e => ne(e.maxDurationSec) && ne(e.avgParseTimeMs); - if (n(r)) return r; - if (e) { - const i = Qf.getFragEstimate(e, t); - if (ne(r.maxDurationSec) || (r.maxDurationSec = i.maxDurationSec), ne(r.avgParseTimeMs) || (r.avgParseTimeMs = i.avgParseTimeMs), n(r)) return r; - ne(r.maxDurationSec) || (r.maxDurationSec = e.defaultTargetDuration), ne(r.avgParseTimeMs) || (r.avgParseTimeMs = e.statDefaults.fragParseTimeMs) - } - return r - } - getCombinedEstimate() { - return Object.assign(Object.assign(Object.assign(Object.assign({}, this.getFragEstimate()), this.getPlaylistEstimate()), this.getBufferEstimate()), this.getBandwidthEstimate()) - } - get statsEntity() { - return this.getEntity(this.id) - } - get bandwidthSample() { - var e; - return null === (e = this.statsEntity) || void 0 === e ? void 0 : e.bandwidthSample - } - get bandwidthStatus() { - var e; - return null === (e = this.statsEntity) || void 0 === e ? void 0 : e.bandwidthStatus - } - get fragSample() { - var e; - return null === (e = this.statsEntity) || void 0 === e ? void 0 : e.fragSample - } - get bandwidthEstimate$() { - return this.selectEntity(this.id, "bandwidthEstimate") - } - get fragEstimate$() { - return this.selectEntity(this.id, "fragEstimate") - } - get playlistEstimate$() { - return this.selectEntity(this.id, "playlistEstimate") - } - get bufferEstimate$() { - return this.selectEntity(this.id, "bufferEstimate") - } - get bandwidthSample$() { - return this.selectEntity(this.id, ({ - bandwidthSample: e - }) => e).pipe(Kp()) - } - get fragSample$() { - return this.selectEntity(this.id, ({ - fragSample: e - }) => e).pipe(Kp()) - } - get playlistSample$() { - return this.selectEntity(this.id, ({ - playlistSample: e - }) => e).pipe(Kp()) - } - get bufferMetric$() { - return this.selectEntity(this.id, ({ - bufferMetric: e - }) => e).pipe(Kp()) - } - } - class Xf { - constructor(e) { - this.statsStore = e - } - getQuery() { - return new kl(this.statsStore) - } - getQueryForItem(e) { - return new zf(this.statsStore, e) - } - remove(e) { - this.statsStore.remove(e) - } - removeAll() { - this.statsStore.remove() - } - setBandwidthSample(e) { - this.statsStore.bandwidthSample = e - } - setFragSample(e) { - this.statsStore.fragSample = e - } - setPlaylistSample(e) { - this.statsStore.playlistSample = e - } - setBufferMetric(e) { - this.statsStore.bufferMetric = e - } - setBandwidthEstimate(e) { - this.statsStore.bandwidthEstimate = e - } - setFragEstimate(e) { - this.statsStore.fragEstimate = e - } - setPlaylistEstimate(e) { - this.statsStore.playlistEstimate = e - } - setBufferEstimate(e) { - this.statsStore.bufferEstimate = e - } - } - const Yf = new class extends fl { - constructor() { - super({}, { - name: "stats-store", - producerFn: su - }) - } - set statsEntity(e) { - Do("statsStore.set.stats"), al(() => { - this.add(e), this.setActive(e.id) - }) - } - set playlistSample(t) { - Do(`stats.set.playlistSample: ${t}`), this.updateActive(e => { - e.playlistSample = t - }) - } - set bandwidthSample(t) { - Do(`stats.set.bandwidthSample: ${t}`), this.updateActive(e => { - e.bandwidthSample = t, e.bandwidthStatus.bandwidthSampleCount += 1, e.bandwidthStatus.instantBw = 8e3 * t.loaded / (t.tload - t.trequest) - }) - } - set fragSample(t) { - Do(`stats.set.fragSample: ${t}`), this.updateActive(e => { - e.fragSample = t - }) - } - set bufferMetric(t) { - Do(`stats.set.bufferMetric: ${t}`), this.updateActive(e => { - e.bufferMetric = t - }) - } - set bandwidthEstimate(t) { - Do(`stats.set.bandwidthEstimate: ${t}`), this.updateActive(e => { - e.bandwidthEstimate = t - }) - } - set fragEstimate(t) { - Do(`stats.set.fragEstimate: ${t}`), this.updateActive(e => { - e.fragEstimate = t - }) - } - set playlistEstimate(t) { - Do(`stats.set.playlistEstimate: ${t}`), this.updateActive(e => { - e.playlistEstimate = t - }) - } - set bufferEstimate(t) { - Do(`stats.set.bufferEstimate: ${t}`), this.updateActive(e => { - e.bufferEstimate = t - }) - } - }; - let Jf = null; - const Zf = e => new zf(Yf, e); - - function em(e, t) { - if (e === t) return !0; - if (!e || !t) return !1; - let i = Object.keys(e).length === Object.keys(t).length; - for (const r of Object.keys(e)) i = i && (isNaN(e[r]) && isNaN(t[r]) || e[r] === t[r]); - return i - } - - function tm(f, m, g) { - return new $t(e => { - (e => { - const t = Zf(e); - t.hasEntity(e) ? $i(t) : (i = Yf, e = e, Do("stats.loading"), i.setLoading(!0), i.statsEntity = { - id: e, - bandwidthEstimate: { - avgLatencyMs: NaN, - avgBandwidth: NaN - }, - bandwidthStatus: { - bandwidthSampleCount: 0, - instantBw: NaN - }, - fragEstimate: { - maxDurationSec: NaN, - avgParseTimeMs: NaN - }, - playlistEstimate: { - avgPlaylistLoadTimeMs: NaN, - avgPlaylistParseTimeMs: NaN - }, - bufferEstimate: { - avgBufferCreateMs: NaN, - avgInitFragAppendMs: NaN, - avgDataFragAppendMs: NaN - } - }, i.setLoading(!1), Do("stats.loaded")); - var i - })(g.itemId); - const t = Zf(g.itemId), - { - fragSample$: i, - playlistSample$: r, - bandwidthSample$: n, - bufferMetric$: s - } = t; - return an(r.pipe(ji(tr), (h = f, p = m, e => e.pipe(tc("statsPlaylistProcessingEpic.in"), sa((e, t) => (e.playlistLoadTimeMs.add(t.playlistLoadTimeMs), e.playlistParseTimeMs.add(t.playlistParseTimeMs), e), { - playlistLoadTimeMs: new Gf(h.minPlaylistCount), - playlistParseTimeMs: new Gf(h.minPlaylistCount) - }), hr(e => ({ - avgPlaylistLoadTimeMs: e.playlistLoadTimeMs.avg, - avgPlaylistParseTimeMs: e.playlistParseTimeMs.avg - })), Is(em), Za(e => { - p.setPlaylistEstimate(e) - })))), n.pipe(ji(tr), (u = f, c = m, n => new $t(e => { - let t = new jf(u.bandwidthHistoryWindowSize, u.bandwidthHistoryAggregationMethod, { - avgLatencyMs: NaN, - avgBandwidth: NaN - }); - const i = t.estimate$, - r = an(n.pipe(ln(e => e.complete), Za(e => {}), hr(e => ({ - trequest: e.trequest, - tfirst: e.tfirst, - tload: e.tload, - bitsDownloaded: 8 * e.loaded - })), tc("statsBandwidthProcessingEpic.in"), La(e => (t.record(e), Ii))), i.pipe(Is(), tc("statsBandwidthProcessingEpic.change"), Za(e => { - c && c.setBandwidthEstimate(e) - }))).subscribe(e); - return () => { - r.unsubscribe(), t.destroy(), t = void 0 - } - }))), i.pipe(ji(tr), (l = f, d = m, e => e.pipe(tc("statsFragProcessingEpic.in"), sa((e, t) => (e.durationSec.add(t.durationSec), e.fragParseMs.add(t.parseTimeMs), e), { - durationSec: new Gf, - fragParseMs: new Gf(l.minFragmentCount) - }), hr(e => ({ - maxDurationSec: e.durationSec.max, - avgParseTimeMs: e.fragParseMs.avg - })), Is(em), Za(e => d.setFragEstimate(e))))), s.pipe(ji(tr), (a = f, o = m, e => e.pipe(tc("statsBufferMetricProcessingEpic.in"), sa((e, t) => (ne(t.bufferCreationStart) && ne(t.bufferCreationEnd) && e.bufferCreateMs.add(t.bufferCreationEnd - t.bufferCreationStart), ne(t.startInitAppend) && ne(t.endInitAppend) && e.initFragAppendMs.add(t.endInitAppend - t.startInitAppend), ne(t.startDataAppend) && ne(t.endDataAppend) && e.dataFragAppendMs.add(t.endDataAppend - t.startDataAppend), e), { - bufferCreateMs: new Gf, - initFragAppendMs: new Gf, - dataFragAppendMs: new Gf(a.minFragmentCount) - }), hr(e => ({ - avgBufferCreateMs: e.bufferCreateMs.avg, - avgInitFragAppendMs: e.initFragAppendMs.avg, - avgDataFragAppendMs: e.dataFragAppendMs.avg - })), Is(em), Za(e => { - o.setBufferEstimate(e) - }))))).pipe($a(Ii)).subscribe(e), () => { - Wf.setCombinedEstimate(f, Object.assign(Object.assign(Object.assign(Object.assign({}, t.getFragEstimate()), t.getPlaylistEstimate()), t.getBufferEstimate()), t.getBandwidthEstimate()), g.serviceName), m.remove(g.itemId) - }; - var a, o, l, d, u, c, h, p - }) - } - const im = { - isWebkitMediaElement: e => "webkitDroppedFrameCount" in e, - isHtmlVideoElement: e => "getVideoPlaybackQuality" in e, - timeRangeToArray(t) { - const i = []; - for (let e = 0; e < t.length; e++) i.push([t.start(e), t.end(e)]); - return i - } - }; - class rm { - constructor(e, t) { - this.hls = e, this.sessionID = t, this.rtcQuery = null, this.accessLogData = this.createAccessLogEntry(), this.accesslog = [], this.errorlog = [] - } - destroy() { - this.rtcQuery = null, this.accesslog = [], this.errorlog = [], this.accessLogData = void 0, this.accessLogReporter = void 0 - } - setRTCQuery(e) { - this.rtcQuery = e - } - setupReporter(e) { - this.accessLogReporter = { - SessionID: this.sessionID, - ClientName: null == e ? void 0 : e.clientName, - ServiceName: null == e ? void 0 : e.serviceName - } - } - addPlayTime(e) { - var t, e = null === (t = this.rtcQuery) || void 0 === t ? void 0 : t.getEntity(e); - !e || "RTC_STATE_PLAY" === (e = e.sessionControlRecord).state && (this.accessLogData.PlayTimeWC = (this.accessLogData.PlayTimeWC || 0) + e.eventStartTime) - } - updatePlaybackInfo(e, t) { - this.accessLogData.ViFrDr = this.rtcQuery.getEntity(e).sessionControlRecord.droppedVideoFrames || 0 - } - updateStallCount(e) { - "RTC_STATE_PLAY" === this.rtcQuery.getEntity(e).sessionControlRecord.state && this.accessLogData.StallCount++ - } - updateMediaEngineStallCount(e) { - "RTC_STATE_PLAY" === this.rtcQuery.getEntity(e).sessionControlRecord.state && this.accessLogData.MediaEngineStallCount++ - } - updateCanPlay(e) { - this.accessLogData.StartupTime = this.rtcQuery.getEntity(e).sessionControlRecord.eventStartTime - } - updateFragLoaded(e, t, i) { - var r; - i.fragType === gu.Variant ? (this.accessLogData.NetBytes += i.bytes, this.accessLogData.ADT += i.adt, r = this.aggregateFragObserverdBitrate(i, ++this.accessLogData.fragmentCnt, this.accessLogData.NetBytes, this.accessLogData.ADT), this.accessLogData.OBRLast = r.obrLast, this.accessLogData.OBRMean = r.obrMean, this.aggregateFragMinMaxBitrate(this.accessLogData, r.obr), this.hls.realCurrentTime > i.startPTS && !t && this.accessLogData.overdue++, this.hasGap(i.startPTS, i.endPTS, this.accessLogData.lastStartPTS, this.accessLogData.lastEndPTS) && this.addToAccessLog(e), this.accessLogData.startPTS || (this.accessLogData.startPTS = i.startPTS), this.accessLogData.lastStartPTS = i.startPTS, this.accessLogData.lastEndPTS = i.endPTS, this.accessLogData.videoBytes += i.bytes, this.accessLogData.videoDuration += i.duration) : i.fragType === gu.AltAudio && (this.accessLogData.audioBytes += i.bytes, this.accessLogData.audioDuration += i.duration) - } - addToAccessLog(e) { - var t = this.getVariantInfo(e), - i = this.rtcQuery.getEntity(e).sessionControlRecord.curLevelUrl, - r = this.rtcQuery.getEntity(e).playEndedRecord.PlayType; - if (i && "" !== i) { - r = this.translateToAccessLogItem(e, i, t, r); - if (r) { - const n = this.accesslog.length - 20; - 0 < n && this.accesslog.splice(0, n), this.accesslog.push(r) - } - this.accessLogData = this.createAccessLogEntry(); - e = this.rtcQuery.getEntity(e).switchCompleteRecord.MediaDur; - this.accessLogData.lastMediaDur = e || this.hls.bufferedDuration - } - } - addToErrorLog(e, t) { - var i = null === (r = this.rtcQuery) || void 0 === r ? void 0 : r.getEntity(e); - if (i) { - var r = Number(("mediaError" === t ? i.playErrorRecord : i.nwErrorRecord).ErrCode), - i = i.sessionControlRecord.curLevelUrl, - r = this.translateToErrorLogItem(e, i, { - domain: t, - code: r - }); - if (r) { - const e = this.errorlog.length - 20; - 0 < e && this.errorlog.splice(0, e), this.errorlog.push(r) - } - } - } - getAccessLog(e) { - var t; - const i = this.accesslog.slice(0), - r = null === (t = this.rtcQuery) || void 0 === t ? void 0 : t.getEntity(e); - if (i && r) { - const t = r.sessionControlRecord.curLevelUrl; - if (t && "" !== t) { - const r = this.getVariantInfo(e), - n = this.translateToAccessLogItem(e, t, r, this.rtcQuery.getEntity(e).playEndedRecord.PlayType); - n && (n["c-provisional-entry"] = !0, i.push(n)) - } - } - return i - } - get errorLog() { - return this.errorlog - } - createAccessLogEntry() { - return { - fragmentCnt: 0, - overdue: 0, - startPTS: 0, - obrMax: 0, - obrMin: 0, - audioBytes: 0, - audioDuration: 0, - videoBytes: 0, - videoDuration: 0, - svrAddrChanged: 0, - svrAddr: "", - PlayTimeWC: 0, - ViFrDr: 0, - StallCount: 0, - MediaEngineStallCount: 0, - ADT: 0, - NetBytes: 0, - StartupTime: 0, - OBRMean: 0, - OBRLast: 0 - } - } - convertStringObjectToPrimitive(e) { - return e ? "object" == typeof e ? e.toString() : e : "" - } - updateSvrAddrStats(t) { - const i = bu.parseURL(t); - if (i && i.netLoc) { - const t = i.netLoc.indexOf(":"); - let e = 0 <= t ? i.netLoc.slice(0, t) : i.netLoc; - e.startsWith("//") && (e = e.slice(2)), this.accessLogData.svrAddr ? e !== this.accessLogData.svrAddr && this.accessLogData.svrAddrChanged++ : this.accessLogData.svrAddrChanged = 0, this.accessLogData.svrAddr = e - } - } - translateToAccessLogItem(e, t, i, r) { - t = this.convertStringObjectToPrimitive(t); - this.updateSvrAddrStats(t); - let n = this.rtcQuery.getEntity(e).switchCompleteRecord.MediaDur; - n = n || this.hls.bufferedDuration, n = n || 0; - const s = { - uri: t, - "s-ip": this.accessLogData.svrAddr, - "s-ip-changes": this.accessLogData.svrAddrChanged, - "sc-wwan-count": -1, - "c-transfer-duration": this.accessLogData.ADT, - bytes: this.accessLogData.NetBytes, - "c-total-media-requests": this.accessLogData.fragmentCnt, - "cs-guid": this.accessLogReporter.SessionID, - "c-start-time": this.accessLogData.startPTS, - "c-startup-time": this.accessLogData.StartupTime, - "c-duration-watched": this.accessLogData.PlayTimeWC / 1e3, - "c-frames-dropped": this.accessLogData.ViFrDr, - "c-stalls": this.accessLogData.StallCount + this.accessLogData.MediaEngineStallCount, - "c-duration-downloaded": this.accessLogData.lastMediaDur ? n - this.accessLogData.lastMediaDur : n, - "c-overdue": this.accessLogData.overdue, - "c-avg-video-bitrate": 8 * this.accessLogData.videoBytes / (this.accessLogData.videoDuration || 1), - "c-observed-max-bitrate": this.accessLogData.obrMax, - "c-observed-min-bitrate": this.accessLogData.obrMin, - "sc-indicated-bitrate": i.bandwidth || 0, - "sc-indicated-avg-bitrate": i.avgBandwidth || 0, - "c-observed-bitrate": this.accessLogData.OBRMean, - "c-switch-bitrate": this.accessLogData.OBRLast, - "c-provisional-entry": !1 - }; - return s["s-playback-type"] = r, this.accessLogData.audioBytes && (s["c-avg-audio-bitrate"] = 8 * this.accessLogData.audioBytes / (this.accessLogData.audioDuration || 1)), s - } - translateToErrorLogItem(e, t, i) { - t = this.convertStringObjectToPrimitive(t); - return this.updateSvrAddrStats(t), { - date: new Date, - "cs-guid": this.accessLogReporter.SessionID + "-" + e, - uri: t, - "s-ip": this.accessLogData.svrAddr, - status: "" + i.code, - domain: i.domain - } - } - hasGap(e, t, i, r) { - return void 0 !== e && void 0 !== i && (1 < e - r || 1 < i - t) - } - aggregateFragObserverdBitrate(e, t, i, r) { - r = 8 * i / (r / 1e3); - return { - obr: r, - obrLast: 8 * e.bytes / (e.adt / 1e3), - obrMean: r / t - } - } - aggregateFragMinMaxBitrate(e, t) { - (!e.obrMax || t > e.obrMax) && (e.obrMax = t), (!e.obrMin || t < e.obrMin) && (e.obrMin = t) - } - getVariantInfo(e) { - var t = this.rtcQuery.getEntity(e).sessionControlRecord.curLevelUrl, - e = null === (e = this.rtcQuery.getEntity(e).sessionControlRecord.manifestData) || void 0 === e ? void 0 : e.variantList; - return t && e && e[t] ? e[t] : {} - } - } - const nm = (r, e, t, i, n, s) => { - var a, { - absoluteUrl: o, - byteRangeOffset: l, - keyTagInfo: d, - iframe: u, - isInitSegment: c - } = r, - h = o, - p = d["method"], - { - start: o, - end: d - } = l, - t = Lc({ - url: h - }, t); - let f, m = o, - g = d, - y = !1, - v = ne(o) || ne(d) ? l : void 0; - if ("AES-128" === p && d && (u || c)) { - const r = d - o; - r % 16 && (g = d + (16 - r % 16)), 0 !== o && (y = !0, m = o - 16), v = { - start: m, - end: g - } - } - return n && ne(r.mediaSeqNum) && r.mediaOptionType === gu.Variant && (f = [], null === (n = t.reportHTTPResponseHeaders) || void 0 === n || n.forEach(function(e) { - Ou.includes(e) ? f.push(e) : Qe().warn({ - name: "load-media-fragment" - }, `${e} is not in approved privacy list. Actions required.`) - }), 0 === f.length && (f = void 0)), Rc({ - url: h, - byteRangeOffset: v, - checkContentLength: !0, - extendMaxTTFB: s, - collectServerInstanceInfo: f, - onProgress: i, - xhrSetup: e.xhrSetup - }, t).pipe(hr(([e, t, i]) => { - if (y) { - const t = e; - r.keyTagInfo.iv = new Uint8Array(t.slice(0, 16)), e = t.slice(16) - } - return [r, e, t, i] - }), (a = r, e => e.pipe(Vn(e => { - if (e instanceof pc) throw new hc(!1, "Timeout", 0, $.FragmentTimeoutError, !0, a, e.stats); - if (e instanceof oc) throw new hc(!1, e.message, e.code, { - code: e.code, - text: "Fragment Network Error" - }, !1, a); - throw e - })))) - }, - sm = { - clearkey: th, - fairplaystreaming: Sc, - playready: Fc, - widevine: Bc - }, - am = { - getKeySystemFormat(e) { - e = sm[e]; - return e ? e.keyFormatString : "" - }, - getKeySystemSecurityLevel(e) { - e = sm[e]; - return e ? e.securityLevels : null - } - }, - om = { - NONE: "", - "AES-128": "", - "ISO-23001-7": "", - "SAMPLE-AES": "", - "SAMPLE-AES-CTR": "" - }, - lm = { - NONE: 0, - "TYPE-0": 1, - "TYPE-1": 2, - "TYPE-2": 3 - }; - - function dm(e) { - return e in lm - } - - function um(e) { - return null == e ? 4 : lm[e] - } - const cm = ["SDR", "PQ", "HLG"], - hm = { - afr: "af", - aka: "ak", - amh: "am", - ara: "ar", - arg: "an", - asm: "as", - ava: "av", - ave: "ae", - aym: "ay", - aze: "az", - bam: "bm", - bel: "be", - ben: "bn", - bih: "bh", - bod: "bo", - bos: "bs", - bre: "br", - bul: "bg", - cat: "ca", - ces: "cs", - cha: "ch", - che: "ce", - chu: "cu", - chv: "cv", - cor: "kw", - cos: "co", - cre: "cr", - cym: "cy", - dan: "da", - deu: "de", - div: "dv", - dzo: "dz", - ell: "el", - eng: "en", - epo: "eo", - est: "et", - eus: "eu", - ewe: "ee", - fao: "fo", - fas: "fa", - fin: "fi", - fra: "fr", - fry: "fy", - ful: "ff", - gla: "gd", - gle: "ga", - glg: "gl", - glv: "gv", - grn: "gn", - guj: "gu", - hat: "ht", - heb: "he", - her: "hz", - hin: "hi", - hmo: "ho", - hrv: "hr", - hun: "hu", - hye: "hy", - ibo: "ig", - ido: "io", - iii: "ii", - iku: "iu", - ile: "ie", - ina: "ia", - ind: "id", - isl: "is", - ita: "it", - jav: "jv", - jpn: "ja", - kal: "kl", - kan: "kn", - kas: "ks", - kat: "ka", - kau: "kr", - kaz: "kk", - khm: "km", - kik: "ki", - kin: "rw", - kir: "ky", - kom: "kv", - kon: "kg", - kor: "ko", - kua: "kj", - kur: "ku", - lao: "lo", - lat: "la", - lav: "lv", - lim: "li", - lit: "lt", - ltz: "lb", - lub: "lu", - lug: "lg", - mah: "mh", - mal: "ml", - mar: "mr", - mkd: "mk", - mlg: "mg", - mlt: "mt", - mol: "mo", - mon: "mn", - mri: "mi", - msa: "ms", - mya: "my", - nav: "nv", - nbl: "nr", - nde: "nd", - ndo: "ng", - nep: "ne", - nld: "nl", - nno: "nn", - nob: "nb", - nya: "ny", - oci: "oc", - oji: "oj", - ori: "or", - orm: "om", - oss: "os", - pan: "pa", - pli: "pi", - pol: "pl", - por: "pt", - pus: "ps", - que: "qu", - roh: "rm", - ron: "ro", - run: "rn", - rus: "ru", - san: "sa", - sin: "si", - slk: "sk", - slv: "sl", - sme: "se", - snd: "sd", - som: "so", - spa: "es", - sqi: "sq", - srd: "sc", - srp: "sr", - sun: "su", - swa: "sw", - swe: "sv", - tah: "ty", - tam: "ta", - tat: "tt", - tel: "te", - tgk: "tg", - tgl: "tl", - tha: "th", - tir: "ti", - ton: "to", - tuk: "tk", - tur: "tr", - uig: "ug", - ukr: "uk", - urd: "ur", - uzb: "uz", - ven: "ve", - vie: "vi", - wln: "wa", - yid: "yi", - zha: "za", - zho: "zh" - }, - pm = { - isLanguageCode: e => e in hm, - shortenLanguageCode(e) { - let t; - var i, r; - return e && (r = 0 <= (i = e.indexOf("-")) ? e.slice(0, i) : e, pm.isLanguageCode(r) && (t = hm[r]), t = t || r, 0 < i && (t += "-" + e.slice(i + 1))), t - } - }, - fm = { - getRichestVideoCodec(e) { - if (e && e.length) { - e = e.sort((e, t) => qp(t) - qp(e)); - return e && e.length ? e[0] : void 0 - } - }, - getRichestAudioCodec(e) { - if (e && e.length) { - e = e.sort((e, t) => jp(t) - jp(e)); - return e && e.length ? e[0] : void 0 - } - }, - getRichestChannelLayoutForGroupId(t, i) { - if (t && i && i.length) { - let e; - const r = i.filter(e => e.groupId === t); - if (r && r.length) { - const t = r.sort((e, t) => be.getChannelCount(t.channels) - be.getChannelCount(e.channels)); - t && t.length && (e = t[0].channels) - } - return e - } - } - }; - - function mm(e) { - return new R(L, "steeringManifestParsingError", !1, e, $.FormatError) - } - class gm { - constructor(e) { - this._url = null, this._programDateTime = null, this._byteRange = null, this.relurl = null, this.baseurl = null, this.isInitSegment = !1, this.mediaSeqNum = NaN, this.cc = NaN, this.iframe = !1, this.bitrate = NaN, this.start = NaN, this.duration = NaN, this.lastByteRangeEndOffset = NaN, this.inheritQuery = e, this.tagList = new Array, this.iframe = !1 - } - getMediaFragment(e, t, i) { - const r = { - mediaOptionType: i, - absoluteUrl: this.url, - start: this.start, - duration: this.duration, - mediaSeqNum: this.mediaSeqNum, - discoSeqNum: this.cc, - mediaOptionId: t, - itemId: e, - isLastFragment: !1, - isInitSegment: this.isInitSegment - }; - return null !== (e = this.byteRange) && void 0 !== e && e.length && (r.byteRangeOffset = { - start: this.byteRangeStartOffset, - end: this.byteRangeEndOffset - }), this.iframe && (r.iframe = this.iframe), this.levelkey && (r.keyTagInfo = this.levelkey), this.programDateTime && (r.programDateTime = this.programDateTime), r - } - get url() { - return !this._url && this.relurl && this.baseurl && (this._url = bu.buildAbsoluteURL(this.baseurl, this.relurl, { - alwaysNormalize: !0, - inheritQuery: this.inheritQuery - })), this._url - } - set url(e) { - this._url = e - } - get programDateTime() { - return !this._programDateTime && this.rawProgramDateTime && (this._programDateTime = new Date(Date.parse(this.rawProgramDateTime))), this._programDateTime - } - get byteRange() { - if (!this._byteRange) { - const i = new Array(2); - var e, t; - this.rawByteRange && (1 === (e = this.rawByteRange.split("@", 2)).length ? (t = this["lastByteRangeEndOffset"], i[0] = t || 0) : i[0] = parseInt(e[1]), i[1] = parseInt(e[0]) + i[0]), this._byteRange = i - } - return this._byteRange - } - get byteRangeStartOffset() { - return this.byteRange[0] - } - get byteRangeEndOffset() { - return this.byteRange[1] - } - get rangeString() { - return 0 <= this.start && 0 <= this.duration ? `${this.start.toFixed(2)}-${(this.start+this.duration).toFixed(2)}` : "N/A" - } - get fragTag() { - return `sn/cc/levelId: ${this.mediaSeqNum}/${this.cc}` - } - } - const ym = { - parseMediaCharacteristics: e => e ? e.split(/\s*,\s*/) : new Array, - addMediaToSelectionArray(e, t, i) { - if (void 0 === e) return -1; - const r = e.MediaSelectionGroupOptions; - let n = r.find(e => e.MediaSelectionOptionsMediaType === t.mediaType && e.MediaSelectionOptionsName === t.name && e.MediaSelectionOptionsExtendedLanguageTag === t.lang); - return n || (n = { - MediaSelectionOptionsMediaType: t.mediaType, - MediaSelectionOptionsExtendedLanguageTag: t.lang, - MediaSelectionOptionsIsDefault: t.default, - MediaSelectionOptionsName: t.name, - MediaSelectionOptionsPersistentID: i, - MediaSelectionOptionsTaggedMediaCharacteristics: t.characteristics - }, t.mediaType === Su.SUBTITLE && (n.MediaSelectionOptionsDisplaysNonForcedSubtitles = t.forced ? vu.NO : vu.YES), i++, r.push(n)), t.persistentID = n.MediaSelectionOptionsPersistentID, i - }, - addDefaultClosedCaptionOption(e, t, i, r) { - e = { - itemId: e, - mediaOptionType: gu.Subtitle, - id: 0, - mediaOptionId: "cc1_" + Zl(), - mediaType: Su.CLOSEDCAPTION, - inStreamID: "CC1", - groupId: "cc", - name: "English-CC", - type: "CLOSED-CAPTIONS", - default: !1, - autoselect: !1, - forced: !1, - lang: "en", - characteristics: ["public.accessibility.transcribes-spoken-dialog", "public.accessibility.describes-music-and-sound"], - persistentID: r - }; - t.push(e), ym.addMediaToSelectionArray(i, e, r) - } - }, - vm = { - BANDWIDTH: NaN, - "AVERAGE-BANDWIDTH": NaN - }, - Sm = { - "TIME-OFFSET": NaN, - "FRAME-RATE": NaN, - SCORE: NaN, - "PLANNED-DURATION": NaN, - DURATION: NaN - }, - bm = /^(\d+)x(\d+)$/, - Tm = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g; - class Em { - constructor(e) { - this.validTags = e - } - isKey(e) { - return e in this.validTags - } - trySetValue(e, t, i) { - return !!this.isKey(e) && (i[e] = this.parseFunc(t), !0) - } - } - class Im { - static parseTags(t) { - let i; - var r = {}; - if (!t) return r; - for (Tm.lastIndex = 0; null !== (i = Tm.exec(t));) { - const t = i[1].toUpperCase(); - let e = i[2]; - 0 === e.indexOf('"') && e.lastIndexOf('"') === e.length - 1 && (e = e.slice(1, -1)); - for (const i of Im.tagParsers) - if (i.trySetValue(t, e, r)) break - } - return r - } - } - Im.tagParsers = [new class extends Em { - parseFunc(e) { - return e - } - }({ - NAME: "", - TYPE: "", - DEFAULT: "", - AUTOSELECT: "", - FORCED: "", - LANGUAGE: "", - URI: "", - AUDIO: "", - "VIDEO-RANGE": "", - "CLOSED-CAPTIONS": "", - CODECS: "", - BYTERANGE: "", - "INSTREAM-ID": "", - "GROUP-ID": "", - CHANNELS: "", - CHARACTERISTICS: "", - KEYFORMAT: "", - KEYFORMATVERSIONS: "", - "DATA-ID": "", - VALUE: "", - METHOD: "", - "HDCP-LEVEL": "", - "ALLOWED-CPC": "", - SUBTITLES: "", - ID: "", - CLASS: "", - "START-DATE": "", - "END-DATE": "", - "END-ON-NEXT": "", - "SERVER-URI": "", - "PATHWAY-ID": "" - }), new class extends Em { - parseFunc(e) { - e = parseInt(e); - return e > Number.MAX_SAFE_INTEGER ? 1 / 0 : e - } - }(vm), new class extends Em { - constructor() { - super(...arguments), this.parseFunc = parseFloat - } - }(Sm), new class extends Em { - parseFunc(e) { - let t = (e || "0x").slice(2); - t = (1 & t.length ? "0" : "") + t; - const i = new Uint8Array(t.length / 2); - for (let e = 0; e < t.length / 2; e++) { - var r = parseInt(t.slice(2 * e, 2 * e + 2), 16); - if (!ne(r)) return; - i[e] = r - } - return i - } - }({ - IV: null - }), new class extends Em { - parseFunc(e) { - e = bm.exec(e); - let t; - return null !== e && (t = { - width: parseInt(e[1], 10), - height: parseInt(e[2], 10) - }), t - } - }({ - RESOLUTION: null - })]; - const wm = { - ExtractVariableParameter: /{\$(.*?)}/g, - LevelPlaylistFast: /#EXTINF:(\d*(?:\.\d+)?)(?:,(.*))?|(?!#)(\S.+)|#EXT-X-BYTERANGE: *(.+)|#EXT-X-PROGRAM-DATE-TIME:(.+)|#EXT-X-BITRATE:(.+)|#EXT-X-DATERANGE:(.+)|#.*/g, - LevelPlaylistSlow: /(?:(?:#(EXTM3U))|(?:#EXT-X-(PLAYLIST-TYPE):(.+))|(?:#EXT-X-(MEDIA-SEQUENCE): *(\d+))|(?:#EXT-X-(TARGETDURATION): *(\d+))|(?:#EXT-X-(KEY):(.+))|(?:#EXT-X-(START):(.+))|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DISCONTINUITY-SEQ)UENCE:(\d+))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(VERSION):(\d+))|(?:#EXT-X-(MAP):(.+))|(?:#EXT-X-(I-FRAMES)-ONLY)|(?:#EXT-X-(DEFINE):(.+))|(?:(#)(.*):(.*))|(?:(#)(.*))(?:.*)\r?\n?/, - MasterPlaylist: /#EXT-X-STREAM-INF:([^\n\r]*)[\r\n]+([^\r\n]+)|#EXT-X-I-FRAME-STREAM-INF:([^\r\n]+)|#EXT-X-DEFINE:([^\n\r]*)|#EXT-X-CONTENT-STEERING:([^\n\r]*)/g, - MasterPlaylistAlternateMedia: /#EXT-X-MEDIA:(.*)/g, - SessionData: /#EXT-X-SESSION-DATA[^:]*:(.*)/g, - SessionKeys: /#EXT-X-SESSION-KEY:([^\n\r]*)/g, - VARIABLE_PLAYLIST_REGEX: /(NAME|VALUE)=\"(.*)\",(NAME|VALUE)=\"(.*)\"|(IMPORT)=\"(.*)\"/ - }; - - function Am(e, t, i) { - return cu.buildAbsoluteURL(t, e, { - alwaysNormalize: !0, - inheritQuery: i - }) - } - class Om { - static isValidPlaylist(e) { - return 0 === e.indexOf("#EXTM3U") - } - static isMediaPlaylist(e) { - return 0 < e.indexOf("#EXTINF:") || 0 < e.indexOf("#EXT-X-PLAYLIST-TYPE:") - } - static replaceVariables(e, t) { - let i, r = !1; - return e && t && (i = e.replace(wm.ExtractVariableParameter, e => { - wm.ExtractVariableParameter.lastIndex = 0; - e = wm.ExtractVariableParameter.exec(e)[1]; - if (e && t.hasOwnProperty(e)) return t[e]; - r = !0 - })), { - updatedString: i, - error: r - } - } - static parseDecryptData(e, t, i) { - const r = Im.parseTags(e), - n = (e = r.METHOD) && e in om ? r.METHOD : null; - e = null !== (e = r.KEYFORMAT) && void 0 !== e ? e : null; - if (n && Om.shouldSelectKeyTag(e, n, i)) { - const s = r.URI, - i = r.IV || null; - if (s && r.IV && !i) { - const s = new R(L, _, !0, `Invalid IV: ${r.IV}`, $.PlaylistErrorInvalidEntry); - throw s.url = t, s - } - const a = s ? cu.buildAbsoluteURL(t, s, { - alwaysNormalize: !0 - }) : t, - o = (r.KEYFORMATVERSIONS || "1").split("/").map(Number).filter(isFinite); - return new zc(n, a, i, e, o) - } - } - static shouldSelectKeyTag(e, t, i) { - return "AES-128" === t || "NONE" === t || null == i || e === am.getKeySystemFormat(i) - } - static optOutClosedCaption(t) { - let i = !1, - r = !1; - if (t) - for (let e = 0; e < t.length; ++e) { - const n = t[e]; - if (n.videoCodec && ((r = !0) !== n.iframes && n.closedcaption && "none" === n.closedcaption.toLowerCase())) { - i = !0; - break - } - } - return !r || i - } - static parseRootPlaylistAlternateMediaOptions(a, o, l, d, u, c) { - let h, p; - var f = { - MediaSelectionGroupAllowEmptySelection: 1, - MediaSelectionGroupMediaCharacteristics: ["public.audible"], - MediaSelectionGroupMediaType: Su.AUDIO, - MediaSelectionGroupOptions: [] - }, - m = { - MediaSelectionGroupAllowEmptySelection: 1, - MediaSelectionGroupMediaCharacteristics: ["public.legible"], - MediaSelectionGroupMediaType: Su.SUBTITLE, - MediaSelectionGroupOptions: [] - }, - g = { - videoAlternateOptions: [], - audioAlternateOptions: [], - subtitleAlternateOptions: [], - audioMediaSelectionGroup: f, - subtitleMediaSelectionGroup: m - }; - let y = 0; - for (wm.MasterPlaylistAlternateMedia.lastIndex = 0; null != (h = wm.MasterPlaylistAlternateMedia.exec(o));) { - const o = Om.replaceVariables(h[1], c); - if (o.error) { - p = new R(L, N, !0, $.PlaylistErrorInvalidEXTXDEFINE.text, $.PlaylistErrorInvalidEXTXDEFINE); - break - } - var v = Im.parseTags(o.updatedString); - let e, t, i, r = Su.UNKNOWN; - const S = ym.parseMediaCharacteristics(v.CHARACTERISTICS), - b = v["GROUP-ID"], - T = v.CHANNELS; - let n, s = null; - switch (v.TYPE) { - case "VIDEO": - r = Su.VIDEO, t = g.videoAlternateOptions; - break; - case "AUDIO": - r = Su.AUDIO, s = gu.AltAudio, t = g.audioAlternateOptions, i = f; - const a = d.find(e => e.audioGroupId === b); - n = a ? a.audioCodecList : []; - break; - case "SUBTITLES": - r = Su.SUBTITLE, s = gu.Subtitle, t = g.subtitleAlternateOptions, i = m; - break; - case "CLOSED-CAPTIONS": - r = Su.CLOSEDCAPTION, s = gu.Subtitle, e = v["INSTREAM-ID"], t = g.subtitleAlternateOptions, i = m - } - const E = { - itemId: a, - mediaOptionType: s, - mediaType: r, - groupId: b, - channels: T, - groupCodecList: n, - name: v.NAME, - type: v.TYPE, - default: "YES" === v.DEFAULT, - autoselect: "YES" === v.AUTOSELECT, - forced: "YES" === v.FORCED, - characteristics: S, - persistentID: y, - id: t ? t.length : 0, - mediaOptionId: `${v.NAME}_${b}_${y}`, - lang: pm.shortenLanguageCode(v.LANGUAGE) - }; - v.URI && (E.url = Am(v.URI, l, u)), E.name || (E.name = E.lang, E.mediaType === Su.CLOSEDCAPTION && (E.name += " CC")), E.mediaType === Su.CLOSEDCAPTION && e && (E.inStreamID = e), t && (E.id = t.length, t.push(E)), y = ym.addMediaToSelectionArray(i, E, y) - } - return 0 !== g.subtitleAlternateOptions.length || Om.optOutClosedCaption(d) || ym.addDefaultClosedCaptionOption(a, g.subtitleAlternateOptions, m, y), { - alternateMediaInfo: g, - playlistParsingError: p - } - } - static parseMediaOptionPlaylist(e, t, i = !0, r, n, s, a, o, l, d = 0, u = !1) { - var c; - let h = 0, - p = 0; - const f = { - itemId: s, - mediaOptionId: a, - mediaOptionType: o, - type: "", - version: 0, - url: t, - initSegments: {}, - fragments: [], - liveOrEvent: !0, - startSN: 0, - endSN: 0, - iframesOnly: u, - targetduration: 0, - totalduration: 0, - averagetargetduration: 0, - ptsKnown: !1 - }; - let m, g, y, v = new zc("NONE", t, null, null, null), - S = !1, - b = !1, - T = 0, - E = null, - I = new gm(i), - w = 0; - const A = {}; - let O, k, C, D = !0, - M = !0; - wm.LevelPlaylistFast.lastIndex = 0; - for (var x = () => new R(L, _, !0, "Invalid key system preference for the playlist", $.IncompatibleAsset); null !== (m = wm.LevelPlaylistFast.exec(e));) { - const e = m[1]; - if (e) { - I.duration = parseFloat(e); - const t = (" " + m[2]).slice(1); - I.title = t || null, I.tagList.push(t ? ["INF", e, t] : ["INF", e]) - } else if (m[3]) { - if (ne(I.duration)) { - const e = h++; - if (I.start = p + d, I.levelkey = v, b && !S) { - k = x(); - break - } - if (S = !1, b = !1, I.mediaSeqNum = e, I.cc = T, I.iframe = f.iframesOnly, I.baseurl = t, (O = Om.replaceVariables((" " + m[3]).slice(1), A)).error) { - k = new R(L, N, !0, $.PlaylistErrorInvalidEXTXDEFINE.text, $.PlaylistErrorInvalidEXTXDEFINE); - break - } - if (I.relurl = O.updatedString, I.bitrate = ne(I.byteRangeEndOffset) ? 8 * (I.byteRangeEndOffset - I.byteRangeStartOffset) / I.duration : w, null != y) { - I.rawProgramDateTime = y, I.tagList.push(["PROGRAM-DATE-TIME", I.rawProgramDateTime]); - const e = I.programDateTime.getTime(); - f.programDateTimeMap = null !== (c = f.programDateTimeMap) && void 0 !== c ? c : {}, f.programDateTimeMap[e] = I.mediaSeqNum, f.dateMediaTimePairs = null !== (c = f.dateMediaTimePairs) && void 0 !== c ? c : [], f.dateMediaTimePairs.push([e, I.start]), y = void 0 - } - if (f.fragments.push(I.getMediaFragment(s, a, o)), E = I, p += I.duration, D || !f.initSegments[T] || M) - if (f.iframesOnly && 0 < I.byteRangeStartOffset && !f.initSegments[T] && !M) { - const e = new gm(i); - if (e.url = I.url, e.rawByteRange = Math.min(I.byteRangeStartOffset, 1316) + "@0", e.baseurl = t, e.isInitSegment = !0, e.cc = T, e.levelkey = v, e.iframe = !0, b && !S) { - k = x(); - break - } - S = !1, b = !1, f.initSegments[T] = e.getMediaFragment(s, a, o) - } else C && (C.discoSeqNum = T, f.initSegments[T] = C); - D = !1, M = !1, I = new gm(i) - } - } else if (m[4]) { - if (I.rawByteRange = (" " + m[4]).slice(1), E) { - const e = E.byteRangeEndOffset; - e && (I.lastByteRangeEndOffset = e) - } - } else if (m[5]) y = (" " + m[5]).slice(1); - else if (m[6]) { - const e = parseInt(m[6]); - ne(e) && (w = 1e3 * e) - } else if (m[7]) { - const e = m[7], - t = Im.parseTags(e); - t.ID && (f.daterangeTags || (f.daterangeTags = {}), f.daterangeTags[t.ID] = t) - } else { - for (m = m[0].match(wm.LevelPlaylistSlow), g = 1; g < m.length && void 0 === m[g]; g++); - const e = Om.replaceVariables((" " + m[g + 1]).slice(1), A), - l = Om.replaceVariables((" " + m[g + 2]).slice(1), A); - if (e.error || l.error) { - k = new R(L, N, !0, $.PlaylistErrorInvalidEXTXDEFINE.text, $.PlaylistErrorInvalidEXTXDEFINE); - break - } - const d = e.updatedString, - u = l.updatedString; - switch (m[g]) { - case "#": - I.tagList.push(u ? [d, u] : [d]); - break; - case "PLAYLIST-TYPE": - f.type = d.toUpperCase(), "VOD" === f.type && (f.liveOrEvent = !1); - break; - case "MEDIA-SEQUENCE": - 0 === f.fragments.length && (h = f.startSN = parseInt(d)); - break; - case "TARGETDURATION": - f.targetduration = parseFloat(d); - break; - case "VERSION": - f.version = parseInt(d); - break; - case "EXTM3U": - break; - case "ENDLIST": - f.liveOrEvent = !1; - break; - case "DIS": - T++, I.tagList.push(["DIS"]), D = !0; - break; - case "DISCONTINUITY-SEQ": - T = parseInt(d); - break; - case "KEY": - const e = d; - if (b = !0, !S) { - try { - v = Om.parseDecryptData(e, t, r) - } catch (e) { - k = e - } - v && (S = !0) - } - break; - case "START": - const l = d, - P = Im.parseTags(l)["TIME-OFFSET"]; - ne(P) && (f.startTimeOffset = P); - break; - case "I-FRAMES": - f.iframesOnly = !0; - break; - case "MAP": - const c = Im.parseTags(d); - if (I.relurl = c.URI, I.rawByteRange = c.BYTERANGE, I.baseurl = t, I.isInitSegment = !0, I.levelkey = v, b && !S) { - k = x(); - break - } - S = !1, b = !1, C = I.getMediaFragment(s, a, o), M = !0, I = new gm(i); - break; - case "DEFINE": - const p = wm.VARIABLE_PLAYLIST_REGEX.exec(d), - m = "NAME" === p[1] ? p[2] : p[4], - g = "VALUE" === p[1] ? p[2] : p[4], - y = p[5], - N = p[6]; - if (m || g || "IMPORT" !== y || !n.hasOwnProperty(N)) { - if (!m || y || p[1] === p[3] || A.hasOwnProperty(m)) { - k = new R(L, _, !0, $.PlaylistErrorMissingImportReference.text, $.PlaylistErrorMissingImportReference); - break - } - A[m] = g - } else A[N] = n[N] - } - } - } - return I = E, I && !I.relurl && (f.fragments.pop(), p -= I.duration), !f.liveOrEvent && 0 < f.fragments.length && (f.fragments[f.fragments.length - 1].isLastFragment = !0), f.totalduration = p, f.averagetargetduration = p / f.fragments.length, f.endSN = h - 1, { - mediaOptionDetails: f, - playlistParsingError: k - } - } - static parseRootPlaylist(t, e, i, r) { - const n = [], - s = {}; - let a, o, l, d, u = null, - c = !0; - for (wm.MasterPlaylist.lastIndex = 0; null != (a = wm.MasterPlaylist.exec(e));) - if (a[4]) { - a = wm.VARIABLE_PLAYLIST_REGEX.exec(a[4]); - const t = "NAME" === a[1] ? a[2] : a[4], - e = "VALUE" === a[1] ? a[2] : a[4], - i = a[5]; - if (!t || s.hasOwnProperty(t) || i || a[1] === a[3]) { - d = new R(L, _, !0, $.PlaylistErrorInvalidEXTXDEFINE.text, $.PlaylistErrorInvalidEXTXDEFINE); - break - } - s[t] = e - } else if (a[5]) { - const t = Om.replaceVariables(a[5], s); - if (t.error) { - d = new R(L, _, !0, $.PlaylistErrorInvalidEXTXDEFINE.text, $.PlaylistErrorInvalidEXTXDEFINE); - break - } - const e = Im.parseTags(t.updatedString); - if ("string" != typeof e["SERVER-URI"]) { - d = new R(L, _, !0, $.PlaylistErrorInvalidSERVERURI.text, $.PlaylistErrorInvalidSERVERURI); - break - } - if (null != e["PATHWAY-ID"] && "string" != typeof e["PATHWAY-ID"]) { - d = new R(L, _, !0, $.PlaylistErrorInvalidPATHWAYID.text, $.PlaylistErrorInvalidPATHWAYID); - break - } - u = { - serverURI: Am(e["SERVER-URI"], i, !1), - initPathwayID: e["PATHWAY-ID"] || "." - } - } else { - l = Om.replaceVariables(a[1] || a[3], s); - const e = Im.parseTags(l.updatedString); - if (o = Om.replaceVariables(a[2] || e.URI, s), l.error || o.error) { - d = new R(L, _, !0, $.PlaylistErrorInvalidEXTXDEFINE.text, $.PlaylistErrorInvalidEXTXDEFINE); - break - } - if (void 0 !== e.SCORE && !ne(e.SCORE) || e.SCORE < 0) { - d = new R(L, _, !0, $.PlaylistErrorInvalidSCORE.text, $.PlaylistErrorInvalidSCORE), c = !1; - break - } - c && void 0 === e.SCORE && (c = !1); - const u = e.BANDWIDTH, - p = e["AVERAGE-BANDWIDTH"], - f = p || u, - m = null !== (h = e["VIDEO-RANGE"]) && void 0 !== h ? h : "SDR"; - if (null == (h = m) || !cm.includes(h)) continue; - const g = { - itemId: t, - mediaOptionId: `level_${(f||0)+n.length%1e3/1e3}`, - mediaOptionType: gu.Variant, - attrs: e, - url: Am(o.updatedString, i, r), - name: e.NAME, - audioGroupId: e.AUDIO, - subtitleGroupId: e.SUBTITLES, - iframes: !!a[3], - bandwidth: u, - avgBandwidth: p, - bitrate: f, - videoRange: m, - frameRate: e["FRAME-RATE"], - allowedCPCMap: Om.parseAllowedCPC(e["ALLOWED-CPC"]), - closedcaption: e["CLOSED-CAPTIONS"], - levelCodec: e.CODECS, - score: e.SCORE, - pathwayID: e["PATHWAY-ID"] || "." - }, - y = e["HDCP-LEVEL"]; - dm(y) && (g.hdcpLevel = y); - var h = e.RESOLUTION; - if (h && (g.width = h.width, g.height = h.height), e.CODECS) { - g.videoCodecList = new Array, g.audioCodecList = new Array; - const t = e.CODECS.split(/[ ,]+/), - i = t["length"]; - for (let e = 0; e < i; e++) { - const i = t[e]; - switch (i.slice(0, 4)) { - case "avc1": - g.videoCodec = be.avc1toavcoti(i), g.videoCodecList.push(g.videoCodec); - break; - case "avc3": - case "dvav": - case "dva1": - case "hev1": - case "hvc1": - case "dvh1": - case "dvhe": - case "vp09": - g.videoCodec = i, g.videoCodecList.push(g.videoCodec); - break; - default: - g.audioCodec = i, g.audioCodecList.push(g.audioCodec) - } - } - 1 < g.audioCodecList.length && (g.audioCodec = fm.getRichestAudioCodec(g.audioCodecList)), 1 < g.videoCodecList.length && (g.videoCodec = fm.getRichestVideoCodec(g.videoCodecList)) - } - if (null != (d = "string" != typeof(h = g.pathwayID) ? mm("invalid steering manifest PATHWAY-PRIORITY list item data type") : /^[\w\-\.]+$/.test(h) ? void 0 : mm("steering manifest contains invalid pathway ID: " + h))) break; - let cpc = g.allowedCPCMap ? JSON.stringify(g.allowedCPCMap) : "null"; - if (!cpc.includes("WIDEVINE_HARDWARE") && !g.url.includes('trickPlay') && !g.videoCodec.includes("hvc1")) - n.push(g) - } - try{ - // console.log(n, window.screen.width) - let ok = (n.map( function(item){return{height : item.height, content: item}})); - let screenHeight = (app.cfg.visual.videoRes ?? window.screen.height) ; - ok.sort(function (a, b) { - return a.height - b.height; + } + + let LoggerSingleton; + function addQELevel(options = {}) { + return Object.assign(Object.assign({}, options), { customLevels: Object.assign(Object.assign({}, (options.customLevels || {})), { qe: 35 }) }); + } + const setupLoggerSingleton = (sessionId, name, options = {}, stream) => { + if (!LoggerSingleton || LoggerSingleton.sessionId !== sessionId) { + LoggerSingleton = browser(addQELevel(options)).child({ sessionId, name: name }); + LoggerSingleton.qe = (msg) => LoggerSingleton.info(msg); + LoggerSingleton.sessionId = sessionId; + } + else { + LoggerSingleton.warn('Logger Singleton already setup, returning existing singleton'); + } + return LoggerSingleton; + }; + /** + * Get the configuration for the Pino logger + * + * @param {object} config - Configuration that overrides/extends the defaults set by this function + * @return {object} + */ + function getLoggerConfig(config = {}) { + const { consoleOverride } = config; + return Object.assign({ name: 'hls', timestamp: !config.sendLogs ? browser.stdTimeFunctions.isoTime : browser.stdTimeFunctions.epochTime, browser: { + asObject: true, + serialize: true, + transmit: { + send: (level, logEvent) => { + if (config.sendLogs && logEvent) { + config.sendLogs(level, logEvent); // Custom send function + } + }, + }, + write: { + debug: consoleWriteFn.bind(null, logFn(consoleOverride || console, 'debug'), 'debug'), + info: consoleWriteFn.bind(null, logFn(consoleOverride || console, 'info'), 'info'), + warn: consoleWriteFn.bind(null, logFn(consoleOverride || console, 'warn'), 'warn'), + error: consoleWriteFn.bind(null, logFn(consoleOverride || console, 'error'), 'error'), + fatal: consoleWriteFn.bind(null, logFn(consoleOverride || console, 'error'), 'fatal'), + }, + } }, config); + } + const noop$1 = () => { }; + function logFn(consoleObj, consoleLevel) { + consoleObj = consoleLevel in consoleObj ? consoleObj : console; + const logFn = consoleObj[consoleLevel] || consoleObj.log; + if (logFn) { + return logFn.bind(consoleObj); + } + return noop$1; + } + function consoleWriteFn(method, logLevel, o) { + const { time, sessionId, critical, name, msg } = o; + let logData = ''; + if ('data' in o) { + try { + const keys = []; + const refs = []; + logData = JSON.stringify(o.data, (key, value) => { + if (typeof value === 'object' && value !== null) { + const circularRefIndex = refs.indexOf(value); + if (circularRefIndex !== -1) { + return `[Circular object reference: '${keys[circularRefIndex]}']`; + } + keys.push(key); + refs.push(value); + } + return value; + }); + } + catch (e) { + logData = `Log serialization error: "${e}"`; + } + } + method(`${getCustomLocaleTimeString(time)}| [SessionID: ${sessionId}] | [${logLevel}] >${critical ? ' [QE Critical]' : ''} [${name}] ${msg ? msg : ''} ${logData}`); + } + /** + * Returns local date and time with millisecond precision and timezone identifier + */ + function getCustomLocaleTimeString(isoDateTime) { + const date = new Date(isoDateTime); + const off = date.getTimezoneOffset(); + const hrs = pad(Math.floor(Math.abs(off) / 60)); + const min = pad(Math.abs(off) % 60); + let tz = off <= 0 ? 'UTC+' + hrs : 'UTC-' + hrs; + tz = min ? tz + ':' + min : tz; + return (date.getFullYear() + '-' + + pad(date.getMonth() + 1) + '-' + + pad(date.getDate()) + ' ' + + pad(date.getHours()) + ':' + + pad(date.getMinutes()) + ':' + + pad(date.getSeconds()) + '.' + + (date.getMilliseconds() / 1000).toFixed(3).slice(2, 5) + ' ' + + tz); + } + /** Print Helpers **/ + function pad(number) { + if (number < 10) { + return '0' + number; + } + return number.toString(); + } + /** + * getLogger get the logger singleton defined in hls.ts + * + * Usage: + * getLogger().info({ name: "yourModuleName" }, 'msg to log'); + * if you do not provide { name: "yourModuleName" }, log line will have 'name: "hls"'. A default name + */ + const getLogger = () => { + if (!LoggerSingleton) { + LoggerSingleton = browser(addQELevel()).child({ name: 'hls' }); + LoggerSingleton.qe = (msg) => LoggerSingleton.info(msg); + LoggerSingleton.warn('getLogger called without hls object instantiated, returning a logger that is not configured'); + } + return LoggerSingleton; + }; + + const MuxerHelper = { + bin2str(buffer) { + return String.fromCharCode.apply(null, Array.from(buffer)); + }, + readUint16(buffer, offset) { + const val = (buffer[offset] << 8) | buffer[offset + 1]; + return val < 0 ? 65536 + val : val; + }, + readSint32(buffer, offset) { + const val = (buffer[offset] << 24) | (buffer[offset + 1] << 16) | (buffer[offset + 2] << 8) | buffer[offset + 3]; + return val; + }, + readUint32(buffer, offset) { + const val = MuxerHelper.readSint32(buffer, offset); + return val < 0 ? 4294967296 + val : val; + }, + writeUint32(buffer, offset, value) { + buffer[offset] = value >> 24; + buffer[offset + 1] = (value >> 16) & 255; + buffer[offset + 2] = (value >> 8) & 255; + buffer[offset + 3] = value & 255; + }, + readUint64(buffer, offset) { + let result = MuxerHelper.readUint32(buffer, offset); + result *= Math.pow(2, 32); + result += MuxerHelper.readUint32(buffer, offset + 4); + return result; + }, + writeUint64(buffer, offset, value) { + const UINT32_MAX = Math.pow(2, 32) - 1; + const upper = Math.floor(value / (UINT32_MAX + 1)); + const lower = Math.floor(value % (UINT32_MAX + 1)); + MuxerHelper.writeUint32(buffer, offset, upper); + MuxerHelper.writeUint32(buffer, offset + 4, lower); + }, + // Find the data for a box specified by its path + findBox(data, path) { + let results = []; + let ix; + let size; + let type; + let end; + let subresults; + if (!path.length) { + // short-circuit the search for empty paths + return []; + } + for (ix = 0; ix < data.byteLength;) { + size = MuxerHelper.readUint32(data, ix); + type = MuxerHelper.bin2str(data.subarray(ix + 4, ix + 8)); + end = size > 1 ? ix + size : data.byteLength; + if (type === path[0]) { + if (path.length === 1) { + // this is the end of the path and we've found the box we were + // looking for + results.push(data.subarray(ix + 8, end)); + } + else { + // recursively search for the next box along the path + subresults = MuxerHelper.findBox(data.subarray(ix + 8, end), path.slice(1)); + if (subresults.length) { + results = results.concat(subresults); + } + } + } + ix = end; + } + // we've finished searching all of data + return results; + }, + // Find the offset and data for a box specified by its path + findBoxWithOffset(data, offset, path, walkedPath) { + let results = []; + let ix; + let size; + let type; + let end; + let subresults; + if (!path.length) { + // short-circuit the search for empty paths + return []; + } + for (ix = 0; ix < data.byteLength;) { + size = MuxerHelper.readUint32(data, ix); + type = MuxerHelper.bin2str(data.subarray(ix + 4, ix + 8)); + end = size > 1 ? ix + size : data.byteLength; + if (type === path[0]) { + if (walkedPath) { + walkedPath.push({ offset: ix + offset, type: type, size: size }); + } + if (path.length === 1) { + // this is the end of the path and we've found the box we were + // looking for + results.push({ offset: ix + offset, type: type, data: data.subarray(ix + 8, end), boxSize: size, walkedPath: walkedPath ? walkedPath.slice(0) : undefined }); + } + else { + // recursively search for the next box along the path + subresults = MuxerHelper.findBoxWithOffset(data.subarray(ix + 8, end), ix + offset + 8, path.slice(1), walkedPath ? walkedPath.slice(0) : undefined); + if (subresults.length) { + results = results.concat(subresults); + walkedPath = walkedPath ? walkedPath.slice(0, -1) : undefined; + } + } + } + ix = end; + } + // we've finished searching all of data + return results; + }, + }; + + /** + * Hex dumper class + * + * + * + * + */ + const Hex = { + hexDump: function (array, limit = Infinity) { + if (!array) { + return ''; + } + const buffer = new Uint8Array(array); + let i, str = ''; + for (i = 0; i < buffer.length && i < limit; i++) { + let h = buffer[i].toString(16); + if (h.length < 2) { + h = '0' + h; + } + str += h; + } + return str; + }, + }; + var Hex$1 = Hex; + + const loggerName$d = { name: 'MP4EncryptionRemuxer' }; + class MP4EncryptionRemuxer extends RemuxerBase { + constructor(observer, config, typeSupported, vendor, logger) { + super(observer, config, logger); + } + static _isCommonEncryptionInternal(decryptMethod) { + return Boolean(decryptMethod && !(decryptMethod === 'NONE' || decryptMethod === 'AES-128')); + } + static remuxInitSegment(initSegment, logger, keyTagInfo, sinfArray) { + if (!keyTagInfo) { + return initSegment; + } + let remuxedInitSegment = initSegment; + if (MP4EncryptionRemuxer._isCommonEncryptionInternal(keyTagInfo.method)) { + const keyId = keyTagInfo.keyId; + let isCbc2 = false; + const newTraks = []; // list of trak boxes + const traks = MuxerHelper.findBoxWithOffset(initSegment, 0, ['moov', 'trak']); + logger.info(loggerName$d, 'trying to patch map'); + traks.forEach((trakTuple) => { + const trak = trakTuple.data; + let mediaType; + let encBoxChildrenOffset = 0; + const stsdTuple = MuxerHelper.findBoxWithOffset(trak, 0, ['mdia', 'minf', 'stbl', 'stsd'], [])[0]; + // skip the sample entry count + const sampleEntries = stsdTuple.data.subarray(8); + let isAudio = true; + let encBoxes = MuxerHelper.findBoxWithOffset(sampleEntries, stsdTuple.offset + 16, ['enca']); + if (encBoxes.length === 0) { + isAudio = false; + encBoxes = MuxerHelper.findBoxWithOffset(sampleEntries, stsdTuple.offset + 16, ['encv']); + } + encBoxes.forEach((encBoxTuple) => { + let encBoxChildren = null; + let newTrak = null; + if (isAudio) { + // encrypted AudioSampleEntry - skip 28 bytes + encBoxChildren = encBoxTuple.data.subarray(28); + mediaType = 'audio'; + encBoxChildrenOffset = stsdTuple.offset + 16 + 8 + 28; + } + else { + // encrypted VisualSampleEntry - skip 78 bytes + encBoxChildren = encBoxTuple.data.subarray(78); + mediaType = 'video'; + encBoxChildrenOffset = stsdTuple.offset + 16 + 8 + 78; + } + if (encBoxChildren) { + const sinfTuples = MuxerHelper.findBoxWithOffset(encBoxChildren, encBoxChildrenOffset, ['sinf']); + sinfTuples.forEach((sinfTuple) => { + const sinf = sinfTuple.data; + const frma = MuxerHelper.findBox(sinf, ['frma'])[0]; + const schm = MuxerHelper.findBox(sinf, ['schm'])[0]; + if (!frma) { + logger.error(loggerName$d, 'missing frma box'); + return; + } + else if (!schm) { + logger.error(loggerName$d, 'missing schm box'); + return; + } + const scheme = MuxerHelper.bin2str(schm.subarray(4, 8)); + const format = MuxerHelper.bin2str(frma.subarray(0, 4)); + if (format === 'aac ') { + if (!MP4$1.types) { + MP4$1.init(); + } + logger.info(loggerName$d, 'found frma with type \'aac \', patching to \'mp4a\''); + frma.set(MP4$1.types.mp4a, 0); + } + if (scheme === 'cbcs' || scheme === 'cenc') { + if (sinfArray) { + sinfArray.push(sinf); // used for unit tests until we get a more generic parser + } + const tenc = MuxerHelper.findBox(sinf, ['schi', 'tenc'])[0]; + if (tenc) { + const kKeyIdOffset = 8; // keyID offset is always 8 within the tenc box + // Look for default key id: + const tencKeyId = tenc.subarray(kKeyIdOffset, kKeyIdOffset + 16); + logger.info(loggerName$d, `found 'tenc' patching map with keyId default:${Hex$1.hexDump(tencKeyId)}->${Hex$1.hexDump(keyId)}`); + tenc.set(keyId, kKeyIdOffset); + // we may not need to do this, but at some point we were authoring content with zeros for the IV + // chrome does use this value, so set it to be safe + const defaultIsProtected = tenc[6]; + const defaultPerSampleIVSize = tenc[7]; + if (defaultIsProtected === 1 && defaultPerSampleIVSize === 0) { + const defaultConstantIVSize = tenc[24]; + if (defaultConstantIVSize > 0 && keyTagInfo.iv && defaultConstantIVSize === keyTagInfo.iv.length) { + logger.info(loggerName$d, 'patching iv for good measure'); + tenc.set(keyTagInfo.iv, 25); + } + } + } + } + else if (scheme === 'cbc2') { + logger.info(loggerName$d, 'found \'cbc2\' scheme, converting to \'cbcs\''); + isCbc2 = true; + if (!MP4$1.types) { + MP4$1.init(); + } + // create a sinf from the existing format and new scheme and scheme info boxes + const frmaTuple = MuxerHelper.findBoxWithOffset(sinf, 0, ['frma'])[0]; + const newSchi = MP4$1.box(MP4$1.types.schi, MP4$1.tenc(keyTagInfo, mediaType)); + const newSinf = MP4$1.box(MP4$1.types.sinf, sinf.subarray(frmaTuple.offset, frmaTuple.boxSize), MP4$1.schm(), newSchi); + // create a new track from the existing trak with the new sinf spliced in + newTrak = MP4$1.box(MP4$1.types.trak, trak.subarray(0, sinfTuple.offset), newSinf, trak.subarray(sinfTuple.offset + sinfTuple.boxSize)); + const newTrackData = newTrak.subarray(8); + // need to update all the sizes down the box path in the new track + // we can re-use the offsets from the already parsed path since they're before the splice point + const sizeIncrease = newSinf.byteLength - sinfTuple.boxSize; + if (stsdTuple.walkedPath) { + stsdTuple.walkedPath.push({ type: 'stsd', offset: encBoxTuple.offset, size: encBoxTuple.boxSize }); // enc* offset is special + stsdTuple.walkedPath.forEach((step) => { + MuxerHelper.writeUint32(newTrackData, step.offset, step.size + sizeIncrease); + }); + } + } + }); + } + if (!newTrak) { + newTrak = initSegment.subarray(trakTuple.offset, trakTuple.offset + trakTuple.boxSize); + } + newTraks.push(newTrak); }); - for (var i = 0; i < ok.length; i++){ - if (ok[i].height > screenHeight){ - if (i == 0){n.splice(0,n.length);n.push(ok[i].content)} - else{n.splice(0,n.length);n.push(ok[i-1].content)} - console.log('selected' , n[0].height) - break; - - } - - } - if (n.length > 1){ - n.splice(0,n.length - 1); - } - // console.log(n) - // console.log(ok) - } catch (e){ console.log(e)} - return { - variantMediaOptions: n, - contentSteeringOption: u, - masterVariableList: s, - playlistParsingError: d, - scoreAvailable: c - } - } - static parseAllowedCPC(e) { - if ("string" != typeof e) return null; - const n = {}; - return e.split(",").forEach(e => { - const t = e.split(":"); - let i, r; - if (2 === t.length) i = t[0].trim(), r = t[1].trim(); - else { - if (!(2 < t.length)) return; - r = t[t.length - 1].trim(), t.pop(), i = t.join(":") - } - if (!(i in n)) { - let e = new Array; - "" !== r && (e = r.split("/").map(e => e.trim())), n[i] = e - } - }), n - } - static parseSessionKeys(e, t, i) { - var r; - const n = []; - for (wm.SessionData.lastIndex = 0; r = wm.SessionKeys.exec(e);) try { - const e = Om.parseDecryptData(r[1], t, i); - e && e.isEncrypted && n.push(e) - } catch (e) {} - return n - } - static parseSessionData(e, t) { - var i; - const r = [], - n = new Set; - for (wm.SessionData.lastIndex = 0; null != (i = wm.SessionData.exec(e));) { - const e = Im.parseTags(i[1]); - e.LANGUAGE = pm.shortenLanguageCode(e.LANGUAGE); - const t = e.LANGUAGE ? e["DATA-ID"] + "|" + e.LANGUAGE : void 0; - "DATA-ID" in e ? t && n.has(t) || ("com.apple.hls.other-tags" === e["DATA-ID"] && (e.VALUE = function(t) { - let i; - try { - i = JSON.parse(qc.base64DecodeToStr(t)) - } catch (e) { - i = t - } - return i - }(e.VALUE)), r.push(e), t && n.add(t)) : Qe().error(`Error processing DATA-ID ${e["DATA-ID"]} and LANGUAGE ${e.LANGUAGE}`) - } - return { - itemList: r, - baseUrl: t - } - } + const edtsTuple = MuxerHelper.findBoxWithOffset(trak, 0, ['edts'])[0]; + if (edtsTuple) { + if (!MP4$1.types) { + MP4$1.init(); + } + logger.info(loggerName$d, 'overwriting edts box'); + trak.set(MP4$1.types.free, edtsTuple.offset + 4); + } + }); + if (isCbc2) { + const cbcsInitSegment = MP4EncryptionRemuxer.remuxCbc2InitSegment(initSegment, newTraks, logger); + remuxedInitSegment = cbcsInitSegment ? cbcsInitSegment : initSegment; + } + } + return remuxedInitSegment; + } + /** + * @param traksToAppend Array of trak boxes to replace traks in initSegment with. + */ + static remuxCbc2InitSegment(initSegment, traksToAppend, logger) { + const ftypTuple = MuxerHelper.findBoxWithOffset(initSegment, 0, ['ftyp'])[0]; + if (!ftypTuple) { + logger.error(loggerName$d, 'no ftyp found'); + return; + } + const moovTuple = MuxerHelper.findBoxWithOffset(initSegment, ftypTuple.boxSize, ['moov'])[0]; + let moovChildren = []; + let ix = 0; + while (ix < moovTuple.data.byteLength) { + const size = MuxerHelper.readUint32(moovTuple.data, ix); + const type = MuxerHelper.bin2str(moovTuple.data.subarray(ix + 4, ix + 8)); + const end = size > 1 ? ix + size : moovTuple.data.byteLength; + switch (type) { + case 'trak': { + if (traksToAppend) { + // Just append these here where the traks used to be + moovChildren = moovChildren.concat(traksToAppend); + traksToAppend = undefined; + } + break; + } + default: + moovChildren.push(moovTuple.data.subarray(ix, end)); + break; + } + ix = end; + } + const newMoov = MP4$1.box(MP4$1.types.moov, ...moovChildren); + const newInitSegment = new Uint8Array(ftypTuple.boxSize + newMoov.byteLength); + newInitSegment.set(initSegment.subarray(0, ftypTuple.boxSize)); + newInitSegment.set(newMoov, ftypTuple.boxSize); + return newInitSegment; + } + static remuxOverflowSegment(data, logger) { + if (!MP4$1.types) { + MP4$1.init(); + } + const tfdts = MuxerHelper.findBoxWithOffset(data, 0, ['moof', 'traf', 'tfdt'], []); + let newDataSize = data.byteLength; + let newData; + // total all tfdts in segment so we know the size increase (can have multiple moofs in a segment) + tfdts.forEach((tfdt) => { + // increase the data size by 4 bytes for each 32 bit tfdt + if (tfdt.data[0] === 0) { + newDataSize += 4; + } + }); + if (newDataSize > data.byteLength) { + newData = new Uint8Array(newDataSize); + let newOffset = 0; + let dataBoxIndex = 0; + while (dataBoxIndex < data.byteLength) { + const size = MuxerHelper.readUint32(data, dataBoxIndex); + const type = MuxerHelper.bin2str(data.subarray(dataBoxIndex + 4, dataBoxIndex + 8)); + const dataBoxEnd = size > 1 ? dataBoxIndex + size : data.byteLength; + if (type === 'moof') { + const newMoof = MP4EncryptionRemuxer.remuxOverflowMoof(data.subarray(dataBoxIndex + 8, dataBoxEnd)); + newData.set(newMoof, newOffset); + newOffset += newMoof.byteLength; + } + else { + newData.set(data.subarray(dataBoxIndex, dataBoxEnd), newOffset); + newOffset += size; + } + dataBoxIndex = dataBoxEnd; + } + } + else { + logger.warn(loggerName$d, 'no increase in size'); + } + return newData ? newData : data; + } + static remuxOverflowMoof(moofData) { + let moofIndex = 0; + const moofChildren = []; + while (moofIndex < moofData.byteLength) { + const size = MuxerHelper.readUint32(moofData, moofIndex); + const type = MuxerHelper.bin2str(moofData.subarray(moofIndex + 4, moofIndex + 8)); + if (type === 'traf') { + const newTraf = MP4EncryptionRemuxer.remuxOverflowTraf(moofData.subarray(moofIndex + 8, moofIndex + size)); + moofChildren.push(newTraf); + } + else { + moofChildren.push(moofData.subarray(moofIndex, moofIndex + size)); + } + moofIndex = size > 1 ? moofIndex + size : moofData.byteLength; + } + const newMoof = MP4$1.box(MP4$1.types.moof, ...moofChildren); + const sizeDiff = newMoof.byteLength - moofData.byteLength - 8; // moofData doesn't include header, so subtract 8 + const truns = MuxerHelper.findBoxWithOffset(newMoof, 0, ['moof', 'traf', 'trun'], []); + truns.forEach((trun) => { + const dataOffsetPresent = (trun.data[3] & 1) !== 0; + if (dataOffsetPresent) { + const dataOffset = MuxerHelper.readUint32(trun.data, 8); + MuxerHelper.writeUint32(trun.data, 8, dataOffset + sizeDiff); + } + }); + const saios = MuxerHelper.findBoxWithOffset(newMoof, 0, ['moof', 'traf', 'saio'], []); + saios.forEach((saio) => { + const version = saio.data[0] & 1; + let dataOffset = 4; + if (saio.data[3] & 1) { + // aux info type and parameter + dataOffset += 8; + } + const ec = MuxerHelper.readUint32(saio.data, dataOffset); + dataOffset += 4; + if (!version) { + for (let i = 0; i < ec; i++) { + const value = MuxerHelper.readUint32(saio.data, dataOffset); + MuxerHelper.writeUint32(saio.data, dataOffset, value + sizeDiff); + dataOffset += 4; + } + } + else { + for (let i = 0; i < ec; i++) { + const value = MuxerHelper.readUint64(saio.data, dataOffset); + MuxerHelper.writeUint64(saio.data, dataOffset, value + sizeDiff); + dataOffset += 8; + } + } + }); + return newMoof; + } + static remuxOverflowTraf(trafData) { + let trafIndex = 0; + const trafChildren = []; + while (trafIndex < trafData.byteLength) { + const size = MuxerHelper.readUint32(trafData, trafIndex); + const type = MuxerHelper.bin2str(trafData.subarray(trafIndex + 4, trafIndex + 8)); + if (type === 'tfdt' && trafData[trafIndex + 8] === 0) { + const tfdtValue = MuxerHelper.readUint32(trafData, trafIndex + 12); + const newTfdt = MP4$1.box(MP4$1.types.tfdt, new Uint8Array([ + 1, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + tfdtValue >> 24, + (tfdtValue >> 16) & 255, + (tfdtValue >> 8) & 255, + tfdtValue & 255, + ])); + trafChildren.push(newTfdt); + } + else { + trafChildren.push(trafData.subarray(trafIndex, trafIndex + size)); + } + trafIndex = size > 1 ? trafIndex + size : trafData.byteLength; + } + const newTraf = MP4$1.box(MP4$1.types.traf, ...trafChildren); + return newTraf; + } + remuxText(track, parsingData) { + track.captionSamples.sort(function (a, b) { + return a.pts - b.pts; + }); + const length = track.captionSamples.length; + if (length) { + if (!parsingData.captionData) { + parsingData.captionData = {}; + } + parsingData.captionData.mp4 = track.captionSamples; + } + track.captionSamples = []; + } + remuxIFrame(startDTS, videoTrack, audioTrack, iframeDuration, track) { + if (!videoTrack.samples || !videoTrack.samples.length || !videoTrack.samples[0].data) { + return null; + } + let streamType; + const moof = MP4$1.moof(startDTS * videoTrack.timescale, videoTrack); + const iframeSegment = new Uint8Array(moof.byteLength + videoTrack.samples[0].data.byteLength + 8); + iframeSegment.set(moof); + MuxerHelper.writeUint32(iframeSegment, moof.byteLength, videoTrack.samples[0].data.byteLength + 8); + iframeSegment.set(MP4$1.types.mdat, moof.byteLength + 4); + iframeSegment.set(videoTrack.samples[0].data, moof.byteLength + 8); + videoTrack.sequenceNumber++; + const videoTimescale = videoTrack.timescale; + const videoEndTs = convertSecondsToTimestamp(startDTS + iframeDuration, videoTimescale); + const videoStartTs = convertSecondsToTimestamp(startDTS, videoTimescale); + let endTs; + let audioSegment; + if (audioTrack) { + streamType = 'audiovideo'; + audioTrack.sequenceNumber = videoTrack.sequenceNumber; + videoTrack.sequenceNumber++; + audioSegment = SilentAudio.getSegment(audioTrack, audioTrack.sequenceNumber, startDTS * audioTrack.info.timescale, iframeDuration); + endTs = determineMaxTimestamp(audioSegment.endTs, videoEndTs); + } + else { + streamType = 'video'; + endTs = videoEndTs; + } + const data = { + data1: iframeSegment, + data2: audioSegment === null || audioSegment === void 0 ? void 0 : audioSegment.silentFragData, + startPTS: videoStartTs, + startDTS: videoStartTs, + endPTS: endTs, + endDTS: endTs, + type: streamType, + dropped: 0, + track, + }; + // send the data + this.observer.trigger(DemuxerEvent.FRAG_PARSING_DATA, data); + // notify end of parsing + this.observer.trigger(DemuxerEvent.FRAG_PARSED); + } + remuxEmsgAndRawData(audioDuration, hasAudio, videoDuration, hasVideo, textTrack, id3Track, timeOffset, timescale, rawFragment, track) { + let streamType; + if (hasAudio && hasVideo) { + streamType = 'audiovideo'; + } + else if (hasAudio) { + streamType = 'audio'; + } + else if (hasVideo) { + streamType = 'video'; + } + const fragDuration = Math.max(videoDuration, audioDuration); + const data = { + data1: rawFragment, + track, + startPTS: convertSecondsToTimestamp(timeOffset, timescale), + startDTS: convertSecondsToTimestamp(timeOffset, timescale), + endPTS: undefined, + endDTS: undefined, + type: streamType, + dropped: 0, + }; + if (fragDuration) { + data.endPTS = convertSecondsToTimestamp(timeOffset + fragDuration, timescale); + data.endDTS = convertSecondsToTimestamp(timeOffset + fragDuration, timescale); + } + if (textTrack && textTrack.captionSamples.length) { + this.remuxText(textTrack, data); + } + if (id3Track && id3Track.id3Samples.length > 0) { + this.remuxID3(id3Track, data); + } + this.observer.trigger(DemuxerEvent.FRAG_PARSING_DATA, data); + this.observer.trigger(DemuxerEvent.FRAG_PARSED); + return data; + } + remuxID3(track, parsingData) { + let sample; + const length = track.id3Samples.length; + const PTSOffset = 10; + const DTSOffset = 10; + if (length) { + for (let index = 0; index < length; index++) { + sample = track.id3Samples[index]; + // emsg pts is aligned with media content and have 10s offset to avoid negative time in audio samples + sample.pts = sample.pts - PTSOffset; + sample.dts = sample.dts - DTSOffset; + } + parsingData.id3Samples = [...track.id3Samples]; + } + track.id3Samples = []; + } + } + + + const UINT32_MAX = Math.pow(2, 32) - 1; + const MAX_UPPER_32_VALUE = Math.pow(2, 20) - 1; // 52 bit integer float precision + const LARGE_TIMESCALE = 1000000; + const loggerName$c = { name: 'MP4Demuxer' }; + class MP4Demuxer extends DemuxerBase { + constructor(observer, remuxer, config, typeSupported = undefined, logger) { + super(observer, remuxer, config, {}, logger); + this.mp4Remuxer = remuxer; + // may need a little headroom for priming audio samples, so shift everything by the max value we expect + // that should be 2112 audio samples at 22050 hz + this.audioPrimingDelay = config.audioPrimingDelay; + if (this.audioPrimingDelay > 0) { + this.logger.info(loggerName$c, `using audioPrimingDelay of ${this.audioPrimingDelay}`); + } + } + resetTimeStamp(initPTS90k) { + if (isFiniteNumber(initPTS90k)) { + if (this.initData.audio && !this.initData.video) { + // if this is an audio only track go ahead and convert initPTS to the sample rate to avoid cumulative rounding errors + this.initPtsTs = { baseTime: Math.round((initPTS90k * this.initData.audio.timescale) / 90000), timescale: this.initData.audio.timescale }; + } + else { + this.initPtsTs = { baseTime: initPTS90k, timescale: 90000 }; + } + } + else { + this.initPtsTs = undefined; + } + } + static isHEVCFlavor(codec) { + if (!codec) { + return false; + } + const delimit = codec.indexOf('.'); + const baseCodec = delimit < 0 ? codec : codec.substring(0, delimit); + return (baseCodec === 'hvc1' || + baseCodec === 'hev1' /* + baseCodec === 'phvc' || + baseCodec === 'ehvc' || + baseCodec === 'zhvc' ||*/ + || + baseCodec === 'chvc' || + baseCodec === 'qhvc' || + baseCodec === 'qhev' || + baseCodec === 'muxa' || + // Dolby Vision + baseCodec === 'dvh1' || + baseCodec === 'dvhe' /* + baseCodec === 'ddh1' || + baseCodec === 'zdh1' ||*/ + || + baseCodec === 'cdh1' || + baseCodec === 'qdh1' || + baseCodec === 'qdhe'); + } + resetInitSegment(initSegment, duration, keyTagInfo) { + // jshint unused:false + this._silentAudioTrack = undefined; + if (initSegment && initSegment.byteLength) { + const remuxedInitSegment = MP4EncryptionRemuxer.remuxInitSegment(initSegment, this.logger, keyTagInfo); + const initData = (this.initData = MP4Demuxer.parseInitSegment(remuxedInitSegment)); + let track; + if (initData.foundLargeTimescale) { + this.logger.warn(loggerName$c, 'large timescale found, will check for 32 bit tfdts'); + } + const parsedAudioCodec = initData.audioCodec; + const parsedVideoCodec = initData.videoCodec; + if (initData.audio && initData.video) { + track = { type: 'audiovideo', container: 'video/mp4', codec: parsedAudioCodec + ',' + parsedVideoCodec, initSegment: remuxedInitSegment }; + } + else { + if (initData.audio && parsedAudioCodec) { + track = { type: 'audio', container: 'audio/mp4', codec: parsedAudioCodec, initSegment: remuxedInitSegment }; + } + if (initData.video && parsedVideoCodec) { + track = { type: 'video', container: 'video/mp4', codec: parsedVideoCodec, initSegment: remuxedInitSegment }; + } + } + if (initData.video) { + // needed to generate moofs for iframe mode frames + // we need the track data to create a new moof for muxed iframe mode + const parsedVideoTrack = initData.video; + const trakData = initSegment.subarray(parsedVideoTrack.trakOffset, parsedVideoTrack.trakOffset + parsedVideoTrack.trakSize); + this._videoTrack = Object.assign(Object.assign({}, parsedVideoTrack), { info: { id: parsedVideoTrack.id, timescale: parsedVideoTrack.timescale, duration }, trakData: trakData, sequenceNumber: 0, samples: [] }); + this.trySEICaptions = !MediaUtil.isVP09(parsedVideoCodec); + this._captionTrack = Object.assign(Object.assign({}, initData.caption), { sequenceNumber: 0, captionSamples: [] }); + this.logger.info(loggerName$c, 'assume SEI closed caption exists'); + } + if (initData.audio && parsedAudioCodec) { + // only need id and codec for silent audio generation + this._audioTrack = Object.assign({}, initData.audio); + } + if (initData.caption) { + // use clcp track if present + this.trySEICaptions = false; + this.logger.info(loggerName$c, `use clcp track id ${initData.caption.id}`); + this._captionTrack = Object.assign(Object.assign({}, initData.caption), { sequenceNumber: 0, captionSamples: [] }); + } + this.remuxedInitDataTrack = track; + const eventData = { track }; + this.observer.trigger(DemuxerEvent.FRAG_PARSING_INIT_SEGMENT, eventData); + } + } + static probe(data, logger) { + // ensure we find a moof box in the first 128kB + return MuxerHelper.findBox(data.subarray(0, Math.min(data.length, 512000)), ['moof']).length > 0; + } + static parseHvcC(data) { + let codecConfig; + if (data) { + const version = data[0]; + if (version === 1) { + const tmpByte = data[1]; + codecConfig = { + profileSpace: tmpByte >> 6, + tierFlag: (tmpByte & 32) >> 5 ? 'H' : 'L', + profileIDC: tmpByte & 31, + profileCompat: MuxerHelper.readUint32(data, 2), + constraintIndicator: data.subarray(6, 12), + levelIDC: data[12], + }; + } + else { + getLogger().warn(loggerName$c, `Unhandled version ${version} in hvcC box`); + } + } + else { + getLogger().warn(loggerName$c, 'No hvcC box'); + } + return codecConfig; + } + static hvcCToCodecString(baseCodecType, codecConfig) { + const profileSpaceStr = codecConfig.profileSpace ? String.fromCharCode(codecConfig.profileSpace + 'A' - 1) : ''; + const codecString = baseCodecType + '.' + profileSpaceStr + codecConfig.profileIDC + '.' + codecConfig.profileCompat.toString(16).toUpperCase() + '.' + codecConfig.tierFlag + codecConfig.levelIDC; + // append constraint indicators, ignoring trailing zero bytes + let constraintIndicatorStr = ''; + for (let i = codecConfig.constraintIndicator.length - 1; i >= 0; --i) { + const byte = codecConfig.constraintIndicator[i]; + if (!(byte === 0 && constraintIndicatorStr === '')) { + const encodedByte = byte.toString(16).toUpperCase(); + constraintIndicatorStr = '.' + (constraintIndicatorStr === '' ? encodedByte : encodedByte + constraintIndicatorStr); + } + } + return codecString + constraintIndicatorStr; + } + static parseDvcC(data) { + let codecConfig; + if (data) { + codecConfig = { + versionMajor: data[0], + versionMinor: data[1], + profile: (data[2] >> 1) & 127, + level: ((data[2] << 5) & 32) | ((data[3] >> 3) & 31), + }; + } + else { + getLogger().warn(loggerName$c, 'No dvcC box'); + } + return codecConfig; + } + static dvcCToCodecString(baseCodecType, codecConfig) { + const profileStr = MP4Demuxer.checkAndAddLeadingZero(codecConfig.profile); + const levelStr = MP4Demuxer.checkAndAddLeadingZero(codecConfig.level); + return baseCodecType + '.' + profileStr + '.' + levelStr; + } + static parseVpcC(data) { + let codecConfig; + if (data) { + codecConfig = { + profile: data[4], + level: data[5], + bitDepth: (data[6] >> 4) & 15, + }; + } + else { + getLogger().warn(loggerName$c, 'No vpcC box'); + } + return codecConfig; + } + static vpcCToCodecString(baseCodecType, codecConfig) { + const profileStr = MP4Demuxer.checkAndAddLeadingZero(codecConfig.profile); + const levelStr = MP4Demuxer.checkAndAddLeadingZero(codecConfig.level); + const subsampling = MP4Demuxer.checkAndAddLeadingZero(codecConfig.bitDepth); + return baseCodecType + '.' + profileStr + '.' + levelStr + '.' + subsampling; + } + static checkAndAddLeadingZero(num) { + return (num < 10 ? '0' : '') + num; + } + /** + * Parses an MP4 initialization segment and extracts stream type and + * timescale values for any declared tracks. Timescale values indicate the + * number of clock ticks per second to assume for time-based values + * elsewhere in the MP4. + * + * To determine the start time of an MP4, you need two pieces of + * information: the timescale unit and the earliest base media decode + * time. Multiple timescales can be specified within an MP4 but the + * base media decode time is always expressed in the timescale from + * the media header box for the track: + * ``` + * moov > trak > mdia > mdhd.timescale + * moov > trak > mdia > hdlr + * ``` + * @param init {Uint8Array} the bytes of the init segment + * @return {object} a hash of track type to timescale values or null if + * the init segment is malformed. + */ + static parseInitSegment(initSegment) { + const result = { foundLargeTimescale: false, tracksById: {} }; + const traksTuple = MuxerHelper.findBoxWithOffset(initSegment, 0, ['moov', 'trak']); + traksTuple.forEach((trakTuple) => { + const trak = trakTuple.data; + const tkhd = MuxerHelper.findBox(trak, ['tkhd'])[0]; + if (tkhd) { + let version = tkhd[0]; + const index = version === 0 ? 12 : 20; + const id = MuxerHelper.readUint32(tkhd, index); + const mdhd = MuxerHelper.findBox(trak, ['mdia', 'mdhd'])[0]; + if (mdhd) { + version = mdhd[0]; + let timescaleIndex = version === 0 ? 12 : 20; + const timescale = MuxerHelper.readUint32(mdhd, timescaleIndex); + timescaleIndex += 4; + if (timescale >= LARGE_TIMESCALE) { + result.foundLargeTimescale = true; + } + const duration = version === 0 ? MuxerHelper.readUint32(mdhd, timescaleIndex) : 0; // TODO: read 64 bit? Maybe we don't care, but TS sets this value and we're trying to share track data + const hdlr = MuxerHelper.findBox(trak, ['mdia', 'hdlr'])[0]; + if (hdlr) { + const hdlrType = MuxerHelper.bin2str(hdlr.subarray(8, 12)); + const hdlrToTypeMap = { soun: 'audio', vide: 'video', clcp: 'caption' }; + const type = hdlrToTypeMap[hdlrType] || hdlrType; + if (type) { + // extract codec info. TODO : parse codec details to be able to build MIME type + const codecBoxes = MuxerHelper.findBox(trak, ['mdia', 'minf', 'stbl', 'stsd']); + let codecType; + if (codecBoxes.length) { + const codecBox = codecBoxes[0]; + codecType = MuxerHelper.bin2str(codecBox.subarray(12, 16)); + getLogger().info(loggerName$c, `MP4Demuxer:${type}:${codecType} found`); + const stsdData = MP4Demuxer.parseStsd(codecBox); + let newTrack; + if (type === 'caption') { + const captionTrack = Object.assign({ id, type, timescale, duration, isTimingTrack: false, sequenceNumber: 0, captionSamples: [] }, stsdData); + result.caption = captionTrack; + newTrack = captionTrack; + } + else { + const mediaTrack = Object.assign({ id, + type, + timescale, + duration, isTimingTrack: true, trakOffset: trakTuple.offset, trakSize: trakTuple.boxSize, sequenceNumber: 0, samples: [], fragmentDuration: 0 }, stsdData); + if (type === 'video') { + result.video = mediaTrack; + result.videoCodec = mediaTrack.codec; + } + else { + result.audio = mediaTrack; + result.audioCodec = mediaTrack.codec; + } + newTrack = mediaTrack; + } + result.tracksById[id] = newTrack; + } + } + } + } + } + }); + const trexTuple = MuxerHelper.findBoxWithOffset(initSegment, 0, ['moov', 'mvex', 'trex']); + trexTuple.forEach((trexTuple) => { + const trex = trexTuple.data; + const id = MuxerHelper.readUint32(trex, 4); + const defaultSampleSize = MuxerHelper.readUint32(trex, 16); + result.tracksById[id].defaultSampleSize = defaultSampleSize; + }); + return result; + } + static parseStsd(stsd) { + let defaultPerSampleIVSize; + let codec; + // skip the sample entry count + const sampleEntries = stsd.subarray(8); + let baseCodecType = MuxerHelper.bin2str(sampleEntries.subarray(4, 8)); + let encBox = null; + let encBoxChildren = null; + if (baseCodecType === 'enca') { + // audio + encBox = MuxerHelper.findBox(sampleEntries, ['enca'])[0]; + encBoxChildren = encBox.subarray(28); + } + else if (baseCodecType === 'encv') { + // video + encBox = MuxerHelper.findBox(sampleEntries, ['encv'])[0]; + // FIXME: May have optional clap and pasp atoms after 78 bytes + encBoxChildren = encBox.subarray(78); + } + const encrypted = !!encBoxChildren; + defaultPerSampleIVSize = 0; + if (encBoxChildren) { + const sinfs = MuxerHelper.findBox(encBoxChildren, ['sinf']); + sinfs.forEach((sinf) => { + const schm = MuxerHelper.findBox(sinf, ['schm'])[0]; + if (schm) { + const scheme = MuxerHelper.bin2str(schm.subarray(4, 8)); + if (scheme === 'cbcs' || scheme === 'cenc') { + const frma = MuxerHelper.findBox(sinf, ['frma'])[0]; + if (frma) { + // for encrypted content baseCodecType will be in frma + baseCodecType = MuxerHelper.bin2str(frma); + } + const tenc = MuxerHelper.findBox(sinf, ['schi', 'tenc'])[0]; + if (tenc) { + defaultPerSampleIVSize = tenc[7]; + } + } + } + }); + } + let codecConfig; + const sampleEntriesEnd = sampleEntries.subarray(86); + switch (baseCodecType) { + case 'mp4a': + codec = 'mp4a.40.5'; + break; + case 'ac-3': + case 'ec-3': + case 'alac': + case 'fLaC': + codec = baseCodecType; + break; + case 'avc1': + case 'avc3': + codec = baseCodecType + '.640028'; + break; + case 'hvc1': + case 'hev1': + const hvcCBox = MuxerHelper.findBox(sampleEntriesEnd, ['hvcC'])[0]; // There may be other atoms right after sampleEntries + codecConfig = MP4Demuxer.parseHvcC(hvcCBox); + codec = codecConfig ? MP4Demuxer.hvcCToCodecString(baseCodecType, codecConfig) : baseCodecType + '.2.4.H150.B0'; + break; + case 'dvh1': + case 'dvhe': + const dvcCBox = MuxerHelper.findBox(sampleEntriesEnd, ['dvcC'])[0]; + codecConfig = MP4Demuxer.parseDvcC(dvcCBox); + codec = codecConfig ? MP4Demuxer.dvcCToCodecString(baseCodecType, codecConfig) : baseCodecType + '.05.01'; + break; + case 'c608': + codec = baseCodecType; + break; + case 'vp09': + const vpcCBox = MuxerHelper.findBox(sampleEntriesEnd, ['vpcC'])[0]; + codecConfig = MP4Demuxer.parseVpcC(vpcCBox); + codec = MP4Demuxer.vpcCToCodecString(baseCodecType, codecConfig); + break; + default: + codec = baseCodecType; + break; + } + return { codec, encrypted, defaultPerSampleIVSize }; + } + static has32BitTfdts(fragment) { + const tfdts = MuxerHelper.findBox(fragment, ['moof', 'traf', 'tfdt']); + let found = false; + tfdts.forEach((tfdt) => { + if (tfdt[0] === 0) { + // version 0 means 32 bit tfdt + found = true; + } + }); + return found; + } + /** + * Determine the base media decode start time, in seconds, for an MP4 + * fragment. If multiple fragments are specified, the earliest time is + * returned. + * + * The base media decode time can be parsed from track fragment + * metadata: + * ``` + * moof > traf > tfdt.baseMediaDecodeTime + * ``` + * It requires the timescale value from the mdhd to interpret. + * + * @param timescale {object} a hash of track ids to timescale values. + * @return {number} the earliest base media decode start time for the + * fragment, in seconds + */ + static getStartDtsTs(initData, fragment) { + // we need info from two childrend of each track fragment box + const trafs = MuxerHelper.findBox(fragment, ['moof', 'traf']); + let minTime = Number.MAX_SAFE_INTEGER; + let result; + // determine the start times for each track + trafs.map(function (traf) { + return MuxerHelper.findBox(traf, ['tfhd']).forEach((tfhd) => { + // get the track id from the tfhd + const id = MuxerHelper.readUint32(tfhd, 4); + const parsedTrak = initData.tracksById[id]; + let timescale; + if (!parsedTrak) { + return; + } + if (parsedTrak.isTimingTrack) { + // assume a 90kHz clock if no timescale was specified + timescale = parsedTrak.timescale || 90000; + } + else { + return Infinity; // Should just not be considered for time calculation + } + // get the base media decode time from the tfdt + const tfdtTimes = MuxerHelper.findBox(traf, ['tfdt']).map(function (tfdt) { + let result; + const version = tfdt[0]; + result = MuxerHelper.readUint32(tfdt, 4); + if (version === 1) { + if (result > MAX_UPPER_32_VALUE) { + getLogger().warn(loggerName$c, `Value larger than can be represented by float for upper 32 bits ${result}`); + } + result *= Math.pow(2, 32); + result += MuxerHelper.readUint32(tfdt, 8); + } + return result; + }); + const baseTime = tfdtTimes.length > 0 ? tfdtTimes[0] : Infinity; + if (isFinite(baseTime) && baseTime / timescale < minTime) { + minTime = baseTime / timescale; + result = { baseTime, timescale }; + } + }); + }); + return result; + } + static offsetStartDTS(initData, fragment, timeOffsetTs, audioPrimingDelaySec) { + MuxerHelper.findBox(fragment, ['moof', 'traf']).map(function (traf) { + return MuxerHelper.findBox(traf, ['tfhd']).map(function (tfhd) { + // get the track id from the tfhd + const id = MuxerHelper.readUint32(tfhd, 4); + const parsedTrak = initData.tracksById[id]; + if (!parsedTrak) { + return; + } + // assume a 90kHz clock if no timescale was specified + const timescale = parsedTrak.timescale || 90000; + const audioPrimingOffset = parsedTrak.type === 'caption' ? 0 : audioPrimingDelaySec; + // get the base media decode time from the tfdt + MuxerHelper.findBox(traf, ['tfdt']).map(function (tfdt) { + const version = tfdt[0]; + const mediaType = parsedTrak.type; + if (version === 0) { + const baseMediaDecodeTime = MuxerHelper.readUint32(tfdt, 4); + let offsetBaseMediaDecodeTime = baseMediaDecodeTime - Math.round((timeOffsetTs.baseTime * timescale) / timeOffsetTs.timescale); + // need to make sure video doesn't go negative after timestampOffset is applied + if (mediaType === 'video' && offsetBaseMediaDecodeTime < 0) { + getLogger().warn(loggerName$c, `video tdft would have gone negative by ${offsetBaseMediaDecodeTime / timescale} seconds`); + offsetBaseMediaDecodeTime = 0; + } + offsetBaseMediaDecodeTime = offsetBaseMediaDecodeTime + Math.round(audioPrimingOffset * timescale); + offsetBaseMediaDecodeTime = Math.max(offsetBaseMediaDecodeTime, 0); + MuxerHelper.writeUint32(tfdt, 4, offsetBaseMediaDecodeTime); + } + else { + const baseMediaDecodeTime = MuxerHelper.readUint32(tfdt, 4); + if (baseMediaDecodeTime > MAX_UPPER_32_VALUE) { + getLogger().error(loggerName$c, `baseMediaDecodeTime larger than can be represented by float for upper 32 bits ${baseMediaDecodeTime}`); + } + let offsetBaseMediaDecodeTime = baseMediaDecodeTime; + offsetBaseMediaDecodeTime *= Math.pow(2, 32); + offsetBaseMediaDecodeTime += MuxerHelper.readUint32(tfdt, 8); + offsetBaseMediaDecodeTime = offsetBaseMediaDecodeTime - Math.round((timeOffsetTs.baseTime * timescale) / timeOffsetTs.timescale); + // need to make sure video doesn't go negative after timestampOffset is applied + if (mediaType === 'video' && offsetBaseMediaDecodeTime < 0) { + getLogger().warn(loggerName$c, `video tdft would have gone negative by ${offsetBaseMediaDecodeTime / timescale} seconds`); + offsetBaseMediaDecodeTime = 0; + } + offsetBaseMediaDecodeTime = offsetBaseMediaDecodeTime + Math.round(audioPrimingOffset * timescale); + offsetBaseMediaDecodeTime = Math.max(offsetBaseMediaDecodeTime, 0); + const upper = Math.floor(offsetBaseMediaDecodeTime / (UINT32_MAX + 1)); + const lower = Math.floor(offsetBaseMediaDecodeTime % (UINT32_MAX + 1)); + MuxerHelper.writeUint32(tfdt, 4, upper); + MuxerHelper.writeUint32(tfdt, 8, lower); + } + }); + }); + }); + } + static writeStartDTS(initData, fragmentData, startDTS) { + MuxerHelper.findBox(fragmentData, ['moof', 'traf']).map(function (traf) { + return MuxerHelper.findBox(traf, ['tfhd']).map(function (tfhd) { + // get the track id from the tfhd + const id = MuxerHelper.readUint32(tfhd, 4); + const parsedTrak = initData.tracksById[id]; + if (!parsedTrak) { + return; + } + // assume a 90kHz clock if no timescale was specified + const timescale = parsedTrak.timescale || 90000; + // everything is in decimal seconds, so we need round timestamps or they'll get truncated and lose accuracy + const roundedStartDTS = Math.round(startDTS * timescale) / timescale; + if (Math.abs(roundedStartDTS - startDTS) > 0.01) { + // the rounding errors should be very small since we snap durations to the timescale + getLogger().warn(loggerName$c, `[iframes] large rounding error when adjusting timestamps, startDTS: ${startDTS}, roundedStartDTS: ${roundedStartDTS}`); + } + // get the base media decode time from the tfdt + MuxerHelper.findBox(traf, ['tfdt']).map(function (tfdt) { + const version = tfdt[0]; + if (version === 0) { + MuxerHelper.writeUint32(tfdt, 4, roundedStartDTS * timescale); + } + else { + let baseMediaDecodeTime = roundedStartDTS * timescale; + baseMediaDecodeTime = Math.max(baseMediaDecodeTime, 0); + const upper = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1)); + const lower = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1)); + MuxerHelper.writeUint32(tfdt, 4, upper); + MuxerHelper.writeUint32(tfdt, 8, lower); + } + }); + }); + }); + } + static parseSAIO(saio) { + let subsampleOffset = 0; + let offset = 0; + const version = saio[0]; + const flags = MuxerHelper.readUint32(saio, 0) & 16777215; + offset += 4; + if ((flags & 1) !== 0) { + offset += 8; + } + const entryCount = MuxerHelper.readUint32(saio, offset) & 16777215; + if (entryCount === 1) { + offset += 4; + subsampleOffset = MuxerHelper.readUint32(saio, offset); + if (version === 1) { + offset += 4; + subsampleOffset *= Math.pow(2, 32); + subsampleOffset += MuxerHelper.readUint32(saio, offset); + } + } + else { + getLogger().error(loggerName$c, `saio entry count error, count is: ${entryCount}`); + } + return subsampleOffset; + } + static parseSAIZ(saiz) { + let sampleSize = 0; + let offset = 0; + const flags = MuxerHelper.readUint32(saiz, 0) & 16777215; + offset += 4; + if ((flags & 1) !== 0) { + offset += 8; + } + sampleSize = saiz[offset]; + offset++; + // let sampleCount = MP4Demuxer.readUint32(saiz, offset); + offset += 4; // just get the first sample + if (sampleSize === 0) { + sampleSize = saiz[offset]; + } + return sampleSize; + } + static parseSubsample(perSampleIVSize, entry) { + const sampleAuxInfo = { subsamples: [] }; + let offset = 0; + if (perSampleIVSize) { + sampleAuxInfo.iv = entry.subarray(0, perSampleIVSize); + offset += perSampleIVSize; + } + offset += 2; // skip entry length + // need at least 6 bytes to read a subsample entry + while (offset + 6 <= entry.byteLength) { + const bytesClear = MuxerHelper.readUint16(entry, offset); + offset += 2; + const bytesEncrypted = MuxerHelper.readUint32(entry, offset); + offset += 4; + sampleAuxInfo.subsamples.push([bytesClear, bytesEncrypted]); + } + return sampleAuxInfo; + } + static isSEIMessage(isHEVCFlavor, naluType) { + return isHEVCFlavor ? naluType === 39 || naluType === 40 : naluType === 6; + } + static parseCLCPSample(fragment, startSampleOffset, sampleSize) { + let sampleIndex = 0; + const samples = []; + let totalSize = 0; + while (sampleIndex < sampleSize) { + let sampleOffset = startSampleOffset + sampleIndex; + const cdatSize = MuxerHelper.readUint32(fragment, sampleOffset); // get actual cdat size + sampleOffset += 4; + const cdatType = MuxerHelper.bin2str(fragment.subarray(sampleOffset, sampleOffset + 4)); + sampleOffset += 4; + if (cdatType === 'cdat') { + if (cdatSize !== sampleSize) { + getLogger().debug(loggerName$c, `clcp track: cdatSize ${cdatSize} @ offset ${sampleOffset}; sampleSize ${sampleSize}`); + } + const actualDataSize = cdatSize - 8; + const rawData = fragment.subarray(sampleOffset, sampleOffset + actualDataSize); + totalSize += actualDataSize; + samples.push(rawData); + sampleIndex += cdatSize; // actualDataSize + 8 + } + else { + getLogger().debug(loggerName$c, `clcp track: unknown atom type ${cdatType}`); + break; + } + } + return { cdatList: samples, cdatTotalSize: totalSize }; + } + // TODOs: + // * Parse cdt2 data + // * Handle 64-bit length in tfhd > baseDataOffset + static parseSamples(startDTS, fragment, track, iframeDuration, useSEICaptions, maxSamples) { + const timescale = track.timescale; + const trackId = track.id; + let pts = startDTS; + let parsedSampleCount = 0; + let sampleAuxInfo; + let isHEVCFlavor = false; + const moofs = MuxerHelper.findBoxWithOffset(fragment, 0, ['moof']); + moofs.map(function (moofTuple) { + const moof = moofTuple.data; + const moofOffset = moofTuple.offset; + const trafs = MuxerHelper.findBox(moof, ['traf']); + trafs.map(function (traf) { + // get the base media decode time from the tfdt + const baseTime = MuxerHelper.findBox(traf, ['tfdt']).map(function (tfdt) { + let result; + const version = tfdt[0]; + result = MuxerHelper.readUint32(tfdt, 4); + if (version === 1) { + result *= Math.pow(2, 32); + result += MuxerHelper.readUint32(tfdt, 8); + } + return result / timescale; + })[0]; + if (baseTime !== undefined) { + pts = baseTime; + } + return MuxerHelper.findBox(traf, ['tfhd']).map(function (tfhd) { + // tfhd default values + const id = MuxerHelper.readUint32(tfhd, 4); + const tfhdFlags = MuxerHelper.readUint32(tfhd, 0) & 16777215; + const baseDataOffsetPresent = (tfhdFlags & 1) !== 0; + const sampleDescriptionIndexPresent = (tfhdFlags & 2) !== 0; + const defaultSampleDurationPresent = (tfhdFlags & 8) !== 0; + let defaultSampleDuration = 0; + const defaultSampleSizePresent = (tfhdFlags & 16) !== 0; + let defaultSampleSize = 0; + const defaultSampleFlagsPresent = (tfhdFlags & 32) !== 0; + // let durationIsEmpty = (tfhdFlags & 0x010000) !== 0; + // let defaultBaseIsMoof = (tfhdFlags & 0x020000) !== 0; + let tfhdOffset = 8; + if (isFiniteNumber(track.defaultSampleSize)) { + defaultSampleSize = track.defaultSampleSize; + } + if (id === trackId) { + if (baseDataOffsetPresent) { + MuxerHelper.readUint32(tfhd, tfhdOffset); // Should be 64-bit + tfhdOffset += 4; + const mustBeZeroes = MuxerHelper.readUint32(tfhd, tfhdOffset); + tfhdOffset += 4; + if (mustBeZeroes !== 0) { + getLogger().info(loggerName$c, 'tfhd baseDataOffset has 64-bit value. Caption should be turned off'); + } + } + if (sampleDescriptionIndexPresent) { + MuxerHelper.readUint32(tfhd, tfhdOffset); + tfhdOffset += 4; + } + if (defaultSampleDurationPresent) { + defaultSampleDuration = MuxerHelper.readUint32(tfhd, tfhdOffset); + tfhdOffset += 4; + } + if (defaultSampleSizePresent) { + defaultSampleSize = MuxerHelper.readUint32(tfhd, tfhdOffset); + tfhdOffset += 4; + } + if (defaultSampleFlagsPresent) { + MuxerHelper.readUint32(tfhd, tfhdOffset); + tfhdOffset += 4; + } + if (track.type === 'video') { + let subsampleOffset = 0, subsampleSize = 0; + // only use the first one + MuxerHelper.findBox(traf, ['saio']).map(function (saio) { + subsampleOffset = MP4Demuxer.parseSAIO(saio); + }); + MuxerHelper.findBox(traf, ['saiz']).map(function (saiz) { + subsampleSize = MP4Demuxer.parseSAIZ(saiz); + }); + if (subsampleOffset && subsampleSize) { + sampleAuxInfo = MP4Demuxer.parseSubsample(track.defaultPerSampleIVSize, fragment.subarray(subsampleOffset, subsampleOffset + subsampleSize)); + } + isHEVCFlavor = MP4Demuxer.isHEVCFlavor(track.codec); + } + MuxerHelper.findBox(traf, ['trun']).map(function (trun) { + // trun specific values + const version = trun[0]; + const flags = MuxerHelper.readUint32(trun, 0) & 16777215; + const dataOffsetPresent = (flags & 1) !== 0; + let dataOffset = 0; + const firstSampleFlagsPresent = (flags & 4) !== 0; + const sampleDurationPresent = (flags & 256) !== 0; + let sampleDuration = 0; + const sampleSizePresent = (flags & 512) !== 0; + let sampleSize = 0; + const sampleFlagsPresent = (flags & 1024) !== 0; + const sampleCompositionOffsetsPresent = (flags & 2048) !== 0; + let compositionOffset = 0; + const sampleCount = MuxerHelper.readUint32(trun, 4); + let trunOffset = 8; // past version, flags, and sample count + if (dataOffsetPresent) { + dataOffset = MuxerHelper.readUint32(trun, trunOffset); + trunOffset += 4; + } + if (firstSampleFlagsPresent) { + trunOffset += 4; + } + let sampleOffset = dataOffset + moofOffset; + for (let ix = 0; ix < sampleCount && (maxSamples < 0 || parsedSampleCount < maxSamples); ix++) { + if (sampleDurationPresent) { + sampleDuration = MuxerHelper.readUint32(trun, trunOffset); + trunOffset += 4; + } + else { + sampleDuration = defaultSampleDuration; + } + if (sampleSizePresent) { + sampleSize = MuxerHelper.readUint32(trun, trunOffset); + trunOffset += 4; + } + else { + sampleSize = defaultSampleSize; + } + if (sampleFlagsPresent) { + trunOffset += 4; + } + if (sampleCompositionOffsetsPresent) { + if (version === 0) { + compositionOffset = MuxerHelper.readUint32(trun, trunOffset); + } + else { + compositionOffset = MuxerHelper.readSint32(trun, trunOffset); + } + trunOffset += 4; + } + // TODO: make this more generic + if (track.type === 'video') { + if (!isFiniteNumber(iframeDuration)) { + // non iframe + track.fragmentDuration += sampleDuration; + if (useSEICaptions) { + let naluTotalSize = 0; + // hijacking sei parsing to get video duration + while (naluTotalSize < sampleSize) { + const naluSize = MuxerHelper.readUint32(fragment, sampleOffset); + sampleOffset += 4; + const naluType = fragment[sampleOffset] & 31; + if (!track.seiSamples) { + track.seiSamples = []; + } + if (MP4Demuxer.isSEIMessage(isHEVCFlavor, naluType)) { + const sampleData = fragment.subarray(sampleOffset, sampleOffset + naluSize); + track.seiSamples.push({ pts: pts + compositionOffset / timescale, type: naluType, data: sampleData, sampleOffset: sampleOffset, naluSize: naluSize }); + } + sampleOffset += naluSize; + naluTotalSize += naluSize + 4; + } + } + } + else { + // iframe + track.samples.push({ + data: fragment.subarray(sampleOffset, sampleOffset + sampleSize), + size: sampleSize, + duration: iframeDuration * timescale, + cts: 0, + flags: { + isLeading: 0, + isDependedOn: 0, + hasRedundancy: 0, + degradPrio: 0, + dependsOn: 2, + isNonSync: 0, + paddingValue: 0, + }, + subsamples: sampleAuxInfo ? sampleAuxInfo.subsamples : [], + iv: sampleAuxInfo ? sampleAuxInfo.iv : undefined, + }); + } + } + else if (track.type === 'audio') { + track.fragmentDuration += sampleDuration; + } + else if (track.type === 'caption') { + const { cdatList, cdatTotalSize } = MP4Demuxer.parseCLCPSample(fragment, sampleOffset, sampleSize); + sampleOffset += sampleSize; + if (cdatList.length) { + let cdatArray; + if (cdatList.length === 1) { + cdatArray = new Uint8Array(cdatList[0]); + } + else if (cdatList.length > 1) { + let offset = 0; + cdatArray = new Uint8Array(cdatTotalSize); + for (const arr of cdatList) { + cdatArray.set(arr, offset); + offset += arr.length; + } + } + track.captionSamples.push({ type: 3, pts: pts, bytes: cdatArray }); + } + } + parsedSampleCount++; + pts += sampleDuration / timescale; + } // for ix + }); // trun map + } // if (id == trackId) + }); // tfhd map + }); // traf map + }); // moof map + } + static parseEmsg(data) { + const version = data[0]; + let schemeIdUri = ''; + let value = ''; + let timeScale; + let presentationTimeDelta; + let presentationTime; + let eventDuration; + let id; + let offset = 0; + if (version === 0) { + while (MuxerHelper.bin2str(data.subarray(offset, offset + 1)) !== '\0') { + schemeIdUri += MuxerHelper.bin2str(data.subarray(offset, offset + 1)); + offset += 1; + } + schemeIdUri += MuxerHelper.bin2str(data.subarray(offset, offset + 1)); + offset += 1; + while (MuxerHelper.bin2str(data.subarray(offset, offset + 1)) !== '\0') { + value += MuxerHelper.bin2str(data.subarray(offset, offset + 1)); + offset += 1; + } + value += MuxerHelper.bin2str(data.subarray(offset, offset + 1)); + offset += 1; + timeScale = MuxerHelper.readUint32(data, 12); + presentationTimeDelta = MuxerHelper.readUint32(data, 16); + eventDuration = MuxerHelper.readUint32(data, 20); + id = MuxerHelper.readUint32(data, 24); + offset = 28; + } + else { + offset += 4; + timeScale = MuxerHelper.readUint32(data, offset); + offset += 4; + const leftPresentationTime = MuxerHelper.readUint32(data, offset); + offset += 4; + const rightPresentationTime = MuxerHelper.readUint32(data, offset); + offset += 4; + presentationTime = Math.pow(2, 32) * leftPresentationTime + rightPresentationTime; + if (!Number.isSafeInteger(presentationTime)) { + presentationTime = Number.MAX_SAFE_INTEGER; + getLogger().warn(loggerName$c, 'Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box'); + } + eventDuration = MuxerHelper.readUint32(data, offset); + offset += 4; + id = MuxerHelper.readUint32(data, offset); + offset += 4; + while (MuxerHelper.bin2str(data.subarray(offset, offset + 1)) !== '\0') { + schemeIdUri += MuxerHelper.bin2str(data.subarray(offset, offset + 1)); + offset += 1; + } + schemeIdUri += MuxerHelper.bin2str(data.subarray(offset, offset + 1)); + offset += 1; + while (MuxerHelper.bin2str(data.subarray(offset, offset + 1)) !== '\0') { + value += MuxerHelper.bin2str(data.subarray(offset, offset + 1)); + offset += 1; + } + value += MuxerHelper.bin2str(data.subarray(offset, offset + 1)); + offset += 1; + } + const payload = data.subarray(offset, data.byteLength); + return { schemeIdUri, value, timeScale, presentationTime, presentationTimeDelta, eventDuration, id, payload }; + } + static extractID3PayloadCreateID3Track(emsgInfo, timeOffset, id3Track, logger) { + const id3 = new ID3$1(emsgInfo.payload, logger); + const data = new Uint8Array(emsgInfo.payload); + const length = data.byteLength; + let frameIndex = 0; + let offset = 0; + const pts = isFiniteNumber(emsgInfo.presentationTime) ? emsgInfo.presentationTime / emsgInfo.timeScale : timeOffset + emsgInfo.presentationTimeDelta / emsgInfo.timeScale; + if (!isFiniteNumber(pts)) { + getLogger().error(loggerName$c, 'No pts found in emsg info when extracting ID3 payload'); + return; + } + const frameDuration = emsgInfo.eventDuration; + const header = data.subarray(0, 10); + const fileIdentifier = MuxerHelper.bin2str(header.subarray(offset, offset + 3)); + offset += 3; + if (fileIdentifier !== 'ID3') { + getLogger().error(loggerName$c, 'No ID3 tag found when extracting ID3 payload'); + } + // skip versions + offset += 2; + const flags = data.subarray(offset, offset + 1); + const extendedHeader = flags[0] & 64; + const footerPresent = flags[0] & 16; + offset += 1; + ID3$1.readSynchSafeUint32(data.subarray(offset, offset + 4)); + offset += 4; + if (extendedHeader) { + const extHeaderSize = ID3$1.readSynchSafeUint32(data.subarray(offset, offset + 4)); + offset += 4; + offset += extHeaderSize; + } + while (offset + 2 < length) { + let frameId = ''; + frameId += MuxerHelper.bin2str(data.subarray(offset, offset + 4)); + offset += 4; + const frameLength = ID3$1.readSynchSafeUint32(data.subarray(offset, offset + 4)); + offset += 4; + const stamp = pts + frameIndex * frameDuration; + const id3Sample = { data: data, pts: stamp, dts: stamp, keyTagInfo: undefined, frames: id3.frames }; + id3Track.id3Samples.push(id3Sample); + offset += frameLength; + frameIndex++; + if (footerPresent) { + const di3 = MuxerHelper.bin2str(data.subarray(offset, offset + 3)); + if (di3 !== 'DI3') { + getLogger().error(loggerName$c, 'End should be DI3 if footer present in extracting ID3 payload'); + } + offset += 3; + // skip remaining 7 bits + offset += 7; + } + } + if (offset + 2 === length) { + const padding = MuxerHelper.readUint16(data, offset); + if (padding !== 0) { + getLogger().warn(loggerName$c, 'Padding should be 0 when extracting ID3 payload'); + } + } + } + /** + * @param data + * @param timeOffset The position in the media element corresponding to this fragment at rate 1 + * @param contiguous Contiguous with previously appeneded fragment + * @param keyTagInfo Object describing information about the key that was used to encrypt data + * @param iframeMediaStart If remuxing for iframe, the time in media element to remux fragment to + * @param iframeDuration If remuxing for iframe, the duration to remux fragment to + */ + append(data, timeOffset, contiguous, accurateTimeOffset, keyTagInfo, iframeMediaStart, iframeDuration) { + let initData = this.initData; + let videoDuration = 0, audioDuration = 0; + let hasVideo = false, hasAudio = false; + if (typeof initData === 'undefined') { + this.resetInitSegment(data, 0); + initData = this.initData; + } + let initPtsTs = this.initPtsTs; + let startDtsTs; + if (!initPtsTs) { + startDtsTs = MP4Demuxer.getStartDtsTs(initData, data); + this.initPtsTs = initPtsTs = { baseTime: startDtsTs.baseTime - Math.round(timeOffset * startDtsTs.timescale), timescale: startDtsTs.timescale }; + this.observer.trigger(DemuxerEvent.INIT_PTS_FOUND, { initPTS: initPtsTs }); + } + if (initData.foundLargeTimescale && MP4Demuxer.has32BitTfdts(data) && !iframeDuration) { + data = MP4EncryptionRemuxer.remuxOverflowSegment(data, this.logger); + } + // Don't remux start DTS, will be adjusted in source buffer timestampOffset instead + startDtsTs = MP4Demuxer.getStartDtsTs(initData, data); + const emsgs = MuxerHelper.findBox(data, ['emsg']); + if (initData.video && initData.video.encrypted) { + // Check for 'senc' or 'saiz' && 'saio' + const trafs = MuxerHelper.findBox(data, ['moof', 'traf']); + const gotSubsamples = trafs.find(function (traf) { + return Boolean(MuxerHelper.findBox(traf, ['senc'])[0] || (MuxerHelper.findBox(traf, ['saiz'])[0] && MuxerHelper.findBox(traf, ['saio'])[0])); + }); + if (!gotSubsamples) { + this.logger.warn(loggerName$c, `Missing subsample information for encrypted content codec=${initData.videoCodec}`); + } + } + if (iframeDuration) { + const timescale = this._videoTrack.timescale; + // snap the iframe durations so added the durations will result in timestamps that are always accurately representable when converted to uints + // we want to use ceil so durations aren't reduced, otherwise the same iframe could get chosen in the next round. + iframeDuration = Math.ceil(iframeDuration * timescale) / timescale; + // iframe timestamps are written directly, not offset, so we have to add the audioPrimingDelay since it'll + // still be shifted by the sourceBuffer.timestampOffset + const startDtsSec = (startDtsTs.baseTime + this.audioPrimingDelay * startDtsTs.timescale) / startDtsTs.timescale; + if (this._videoTrack && this._audioTrack && !this._silentAudioTrack) { + const silentTrackBase = SilentAudio.getTrack(this.observer, this._audioTrack.id, this._audioTrack.codec, 2, this.logger); + if (!silentTrackBase) { + throw `unable to create silent audio track for codec ${this._audioTrack.codec}`; + } + this._silentAudioTrack = Object.assign(Object.assign({}, silentTrackBase), { sequenceNumber: 0 }); + const iframeInitSegment = MP4$1.initSegment([this._videoTrack, this._silentAudioTrack]); + this.remuxedInitDataTrack = { type: 'audiovideo', container: 'video/mp4', codec: this._silentAudioTrack.config.codec + ',' + this._videoTrack.codec, initSegment: iframeInitSegment }; + const data = { track: this.remuxedInitDataTrack }; + this.observer.trigger(DemuxerEvent.FRAG_PARSING_INIT_SEGMENT, data); + } + MP4Demuxer.parseSamples(startDtsSec, data, this._videoTrack, iframeDuration, false, 1); + this.mp4Remuxer.remuxIFrame(startDtsSec, this._videoTrack, this._silentAudioTrack, iframeDuration, this.remuxedInitDataTrack); + this._videoTrack.samples = []; + } + else { + let startDtsSec = (startDtsTs.baseTime - this.audioPrimingDelay * startDtsTs.timescale) / startDtsTs.timescale; + startDtsSec = Math.max(0, startDtsSec); + if (emsgs && emsgs.length > 0) { + const emsgsInfos = emsgs.map((emsg) => { + const emsgInfo = MP4Demuxer.parseEmsg(emsg); + // HLSJS only processes id3 tags in emsgs + if (emsgInfo.schemeIdUri === 'https://aomedia.org/emsg/ID3\0' && !this._id3Track) { + this._id3Track = { id3Samples: [], inputTimescale: 90000 }; + } + return emsgInfo; + }); + if (this._id3Track) { + emsgsInfos.map((emsgInfo) => { + MP4Demuxer.extractID3PayloadCreateID3Track(emsgInfo, timeOffset, this._id3Track, this.logger); + }); + } + } + if (this._videoTrack) { + MP4Demuxer.parseSamples(startDtsSec, data, this._videoTrack, undefined, this.trySEICaptions, -1); + videoDuration = this._videoTrack.fragmentDuration / this._videoTrack.timescale; + hasVideo = true; + this._videoTrack.fragmentDuration = 0; + this.logger.info(loggerName$c, `frag video duration: ${videoDuration}`); + if (this.trySEICaptions) { + MP4Demuxer.extractSEICaptionsFromNALu(this._videoTrack.seiSamples, this._captionTrack); + this._videoTrack.seiSamples = []; + this.logger.info(loggerName$c, `sei samples: ${this._captionTrack.captionSamples.length}`); + } + else if (this._captionTrack) { + // clcp text track + MP4Demuxer.parseSamples(startDtsSec, data, this._captionTrack, undefined, false, Number.MAX_SAFE_INTEGER); + } + } + if (this._audioTrack) { + MP4Demuxer.parseSamples(startDtsSec, data, this._audioTrack, undefined, false, -1); + audioDuration = this._audioTrack.fragmentDuration / this._audioTrack.timescale; + hasAudio = true; + this._audioTrack.fragmentDuration = 0; + } + this.mp4Remuxer.remuxEmsgAndRawData(audioDuration, hasAudio, videoDuration, hasVideo, this._captionTrack, this._id3Track, startDtsSec, startDtsTs.timescale, data, this.remuxedInitDataTrack); + if (this._id3Track) { + this._id3Track.id3Samples = []; + } + } + } + static extractSEICaptionsFromNALu(units, textTrack) { + if (!units) { + return; + } + for (let u = 0; u < units.length; ++u) { + const unit = units[u]; + const pts = unit.pts; + let seiPtr = 0; + seiPtr++; + let payloadType = 0; + let payloadSize = 0; + let endOfCaptions = false; + let b = 0; + while (!endOfCaptions && seiPtr < unit.data.length) { + payloadType = 0; + do { + if (seiPtr >= unit.data.length) { + break; + } + b = unit.data[seiPtr++]; + payloadType += b; + } while (b === 255); + // Parse payload size. + payloadSize = 0; + do { + if (seiPtr >= unit.data.length) { + break; + } + b = unit.data[seiPtr++]; + payloadSize += b; + } while (b === 255); + const leftOver = unit.data.length - seiPtr; + if (payloadType === 4 && seiPtr < unit.data.length) { + endOfCaptions = true; + const countryCode = unit.data[seiPtr++]; + if (countryCode === 181) { + const providerCode = MuxerHelper.readUint16(unit.data, seiPtr); + seiPtr += 2; + if (providerCode === 49) { + const userStructure = MuxerHelper.readUint32(unit.data, seiPtr); + seiPtr += 4; + if (userStructure === 1195456820) { + const userDataType = unit.data[seiPtr++]; + // Raw CEA-608 bytes wrapped in CEA-708 packet + if (userDataType === 3) { + const firstByte = unit.data[seiPtr++]; + seiPtr++; // skip second byte + const totalCCs = 31 & firstByte; + const enabled = 64 & firstByte; + const byteArray = []; + if (enabled) { + for (let i = 0; i < totalCCs; i++) { + const ccData = unit.data[seiPtr++]; + const ccDataChecked = ccData & 252; + if (ccData === ccDataChecked) { + // valid ccData + const ccType = ccData & 3; + if (0 === ccType || 1 === ccType) { + // Exclude CEA708 CC data. + const b1 = unit.data[seiPtr++]; + const b2 = unit.data[seiPtr++]; + byteArray.push(b1); + byteArray.push(b2); + } + } + else { + seiPtr += 2; // Skip byte pair too + } + } + } + if (byteArray.length > 0) { + textTrack.captionSamples.push({ type: 3, pts: pts, bytes: byteArray }); + } + } + } + } + } + } + else if (payloadSize < leftOver) { + seiPtr += payloadSize; + } + else if (payloadSize > leftOver) { + break; + } + } + } + return textTrack; + } + } + + + const loggerName$b = { name: 'ExpGolomb' }; + class ExpGolomb { + constructor(data, logger) { + this.data = data; + this.logger = logger; + // the number of bytes left to examine in this.data + this._bytesAvailable = data.byteLength; + // the current word being examined + this.word = 0; // :uint + // the number of bits left to examine in the current word + this.bitsAvailable = 0; // :uint + } + get bytesAvailable() { + return this._bytesAvailable; + } + loadWord() { + const data = this.data, bytesAvailable = this._bytesAvailable, position = data.byteLength - bytesAvailable, workingBytes = new Uint8Array(4), availableBytes = Math.min(4, bytesAvailable); + if (availableBytes === 0) { + throw new Error('no bytes available'); + } + workingBytes.set(data.subarray(position, position + availableBytes)); + this.word = new DataView(workingBytes.buffer).getUint32(0); + // track the amount of this.data that has been processed + this.bitsAvailable = availableBytes * 8; + this._bytesAvailable -= availableBytes; + } + skipBits(count) { + let skipBytes; // :int + if (this.bitsAvailable > count) { + this.word <<= count; + this.bitsAvailable -= count; + } + else { + count -= this.bitsAvailable; + skipBytes = count >> 3; + count -= skipBytes >> 3; + this._bytesAvailable -= skipBytes; + this.loadWord(); + this.word <<= count; + this.bitsAvailable -= count; + } + } + readBits(size) { + let bits = Math.min(this.bitsAvailable, size); // :uint + const valu = this.word >>> (32 - bits); // :uint + if (size > 32) { + this.logger.error(loggerName$b, 'Cannot read more than 32 bits at a time'); + } + this.bitsAvailable -= bits; + if (this.bitsAvailable > 0) { + this.word <<= bits; + } + else if (this._bytesAvailable > 0) { + this.loadWord(); + } + bits = size - bits; + if (bits > 0 && this.bitsAvailable) { + return (valu << bits) | this.readBits(bits); + } + else { + return valu; + } + } + // ():uint + skipLZ() { + let leadingZeroCount; // :uint + for (leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) { + if (0 !== (this.word & (2147483648 >>> leadingZeroCount))) { + // the first bit of working word is 1 + this.word <<= leadingZeroCount; + this.bitsAvailable -= leadingZeroCount; + return leadingZeroCount; + } + } + // we exhausted word and still have not found a 1 + this.loadWord(); + return leadingZeroCount + this.skipLZ(); + } + // ():void + skipUEG() { + this.skipBits(1 + this.skipLZ()); + } + // ():void + skipEG() { + this.skipBits(1 + this.skipLZ()); + } + // ():uint + readUEG() { + const clz = this.skipLZ(); // :uint + return this.readBits(clz + 1) - 1; + } + // ():int + readEG() { + const valu = this.readUEG(); // :int + if (1 & valu) { + // the number is odd if the low order bit is set + return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2 + } + else { + return -1 * (valu >>> 1); // divide by two then make it negative + } + } + // Some convenience functions + // :Boolean + readBoolean() { + return 1 === this.readBits(1); + } + // ():int + readUByte() { + return this.readBits(8); + } + // ():int + readUShort() { + return this.readBits(16); + } + // ():int + readUInt() { + return this.readBits(32); + } + /** + * Advance the ExpGolomb decoder past a scaling list. The scaling + * list is optionally transmitted as part of a sequence parameter + * set and is not relevant to transmuxing. + * @param count {number} the number of entries in this scaling list + * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1 + */ + skipScalingList(count) { + let lastScale = 8, nextScale = 8, j, deltaScale; + for (j = 0; j < count; j++) { + if (nextScale !== 0) { + deltaScale = this.readEG(); + nextScale = (lastScale + deltaScale + 256) % 256; + } + lastScale = nextScale === 0 ? lastScale : nextScale; + } + } + /** + * Read a sequence parameter set and return some interesting video + * properties. A sequence parameter set is the H264 metadata that + * describes the properties of upcoming video frames. + * @param data {Uint8Array} the bytes of a sequence parameter set + * @return {object} an object with configuration parsed from the + * sequence parameter set, including the dimensions of the + * associated video frames. + */ + readSPS() { + let frameCropLeftOffset = 0, frameCropRightOffset = 0, frameCropTopOffset = 0, frameCropBottomOffset = 0, numRefFramesInPicOrderCntCycle, scalingListCount, i; + const readUByte = this.readUByte.bind(this), readBits = this.readBits.bind(this), readUEG = this.readUEG.bind(this), readBoolean = this.readBoolean.bind(this), skipBits = this.skipBits.bind(this), skipEG = this.skipEG.bind(this), skipUEG = this.skipUEG.bind(this), skipScalingList = this.skipScalingList.bind(this); + readUByte(); + const profileIdc = readUByte(); // profile_idc + readBits(5); // constraint_set[0-4]_flag, u(5) + skipBits(3); // reserved_zero_3bits u(3), + readUByte(); // level_idc u(8) + skipUEG(); // seq_parameter_set_id + // some profiles have more optional data we don't need + if (profileIdc === 100 || + profileIdc === 110 || + profileIdc === 122 || + profileIdc === 244 || + profileIdc === 44 || + profileIdc === 83 || + profileIdc === 86 || + profileIdc === 118 || + profileIdc === 128) { + const chromaFormatIdc = readUEG(); + if (chromaFormatIdc === 3) { + skipBits(1); // separate_colour_plane_flag + } + skipUEG(); // bit_depth_luma_minus8 + skipUEG(); // bit_depth_chroma_minus8 + skipBits(1); // qpprime_y_zero_transform_bypass_flag + if (readBoolean()) { + // seq_scaling_matrix_present_flag + scalingListCount = chromaFormatIdc !== 3 ? 8 : 12; + for (i = 0; i < scalingListCount; i++) { + if (readBoolean()) { + // seq_scaling_list_present_flag[ i ] + if (i < 6) { + skipScalingList(16); + } + else { + skipScalingList(64); + } + } + } + } + } + skipUEG(); // log2_max_frame_num_minus4 + const picOrderCntType = readUEG(); + if (picOrderCntType === 0) { + readUEG(); // log2_max_pic_order_cnt_lsb_minus4 + } + else if (picOrderCntType === 1) { + skipBits(1); // delta_pic_order_always_zero_flag + skipEG(); // offset_for_non_ref_pic + skipEG(); // offset_for_top_to_bottom_field + numRefFramesInPicOrderCntCycle = readUEG(); + for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) { + skipEG(); // offset_for_ref_frame[ i ] + } + } + skipUEG(); // max_num_ref_frames + skipBits(1); // gaps_in_frame_num_value_allowed_flag + const picWidthInMbsMinus1 = readUEG(); + const picHeightInMapUnitsMinus1 = readUEG(); + const frameMbsOnlyFlag = readBits(1); + if (frameMbsOnlyFlag === 0) { + skipBits(1); // mb_adaptive_frame_field_flag + } + skipBits(1); // direct_8x8_inference_flag + if (readBoolean()) { + // frame_cropping_flag + frameCropLeftOffset = readUEG(); + frameCropRightOffset = readUEG(); + frameCropTopOffset = readUEG(); + frameCropBottomOffset = readUEG(); + } + let pixelRatio = [1, 1]; + if (readBoolean()) { + // vui_parameters_present_flag + if (readBoolean()) { + // aspect_ratio_info_present_flag + const aspectRatioIdc = readUByte(); + switch (aspectRatioIdc) { + case 1: + pixelRatio = [1, 1]; + break; + case 2: + pixelRatio = [12, 11]; + break; + case 3: + pixelRatio = [10, 11]; + break; + case 4: + pixelRatio = [16, 11]; + break; + case 5: + pixelRatio = [40, 33]; + break; + case 6: + pixelRatio = [24, 11]; + break; + case 7: + pixelRatio = [20, 11]; + break; + case 8: + pixelRatio = [32, 11]; + break; + case 9: + pixelRatio = [80, 33]; + break; + case 10: + pixelRatio = [18, 11]; + break; + case 11: + pixelRatio = [15, 11]; + break; + case 12: + pixelRatio = [64, 33]; + break; + case 13: + pixelRatio = [160, 99]; + break; + case 14: + pixelRatio = [4, 3]; + break; + case 15: + pixelRatio = [3, 2]; + break; + case 16: + pixelRatio = [2, 1]; + break; + case 255: { + pixelRatio = [(readUByte() << 8) | readUByte(), (readUByte() << 8) | readUByte()]; + break; + } + } + } + } + return { + width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2), + height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset), + pixelRatio: pixelRatio, + }; + } + readSliceType() { + // skip NALu type + this.readUByte(); + // discard first_mb_in_slice + this.readUEG(); + // return slice_type + return this.readUEG(); + } + } + + + const TIMESCALE = 90000; + const loggerName$a = { name: 'TS Demuxer' }; + function isCompleteVideoConfig(config) { + const is = typeof config.codec === 'string' && + Array.isArray(config.sps) && + Array.isArray(config.pps) && + typeof config.width === 'number' && + typeof config.height === 'number' && + Array.isArray(config.pixelRatio); + return is; + } + class TsDemuxer extends EsDemuxer { + constructor(observer, remuxer, config, typeSupported, logger) { + super(observer, remuxer, config, typeSupported, logger); + } + static probe(data, logger) { + // a TS fragment should contain at least 3 TS packets, a PAT, a PMT, and one PID, each starting with 0x47 + if (data.length >= 564 && data[0] === 71 && data[188] === 71 && data[376] === 71) { + return true; + } + else { + return false; + } + } + resetInitSegment(initSegment, duration, keyTagInfo) { + this.pmtParsed = false; + this._pmtId = -1; + const baseInfo = { id: -1, inputTimescale: TIMESCALE, timescale: NaN, duration: 0, encrypted: keyTagInfo && keyTagInfo.isEncrypted, keyTagInfo }; + const baseParsingInfo = { len: 0, sequenceNumber: 0 }; + this._avcContext = { info: Object.assign({}, baseInfo), parsingData: Object.assign(Object.assign({}, baseParsingInfo), { esSamples: new Array(), dropped: 0 }), config: {}, container: 'video/mp2t', type: 'video' }; + this._audioContext = { info: Object.assign({}, baseInfo), parsingData: Object.assign(Object.assign({}, baseParsingInfo), { esSamples: new Array() }), container: 'video/mp2t', type: 'audio' }; + this._id3Track = { id: -1, inputTimescale: TIMESCALE, id3Samples: [] }; + this._txtTrack = { inputTimescale: TIMESCALE, captionSamples: [] }; + this._duration = duration; + this._initSegment = initSegment; + } + // feed incoming data to the front of the parsing pipeline + append(data, timeOffset, contiguous, accurateTimeOffset, keyTagInfo, iframeMediaStart, iframeDuration) { + let start, stt, pid, atf, offset, pes, unknownPIDs = false; + this.contiguous = contiguous; + const avcContext = this._avcContext, audioContext = this._audioContext, id3Track = this._id3Track; + let pmtParsed = this.pmtParsed, avcId = avcContext.info.id, audioId = audioContext.info.id, id3Id = id3Track.id, pmtId = this._pmtId, avcData = avcContext.pesData, audioData = audioContext.pesData, id3Data = id3Track.pesData; + this.iframeMode = typeof iframeDuration !== 'undefined'; + if (this._initSegment && this._initSegment.byteLength > 0) { + // if there's an init segment then it needs to be prepended to the first data + const newData = new Uint8Array(this._initSegment.byteLength + data.byteLength); + newData.set(this._initSegment); + newData.set(data, this._initSegment.byteLength); + this._initSegment = undefined; + // make sure its still valid TS + if (newData[0] === 71) { + data = newData; + } + } + let len = data.length; + // don't parse last TS packet if incomplete + len -= len % 188; + // loop through TS packets + for (start = 0; start < len; start += 188) { + if (data[start] === 71) { + // payload unit start indicator - pes immediately follows the ts header + stt = !!(data[start + 1] & 64); + // pid is a 13-bit field starting at the last bit of TS[1] + pid = ((data[start + 1] & 31) << 8) + data[start + 2]; + atf = (data[start + 3] & 48) >> 4; + // if an adaption field is present, its length is specified by the fifth byte of the TS packet header. + if (atf > 1) { + offset = start + 5 + data[start + 4]; + // continue if there is only adaptation field + if (offset === start + 188) { + continue; + } + } + else { + offset = start + 4; + } + switch (pid) { + case avcId: + if (stt) { + if (avcData && (pes = this._parsePES(avcData))) { + // new pes is starting and we have an current one, therefore the current pes is complete, so lets parse it + this._parseAVCPES(pes, false); + } + // reset the current pes data + avcData = { data: [], size: 0, keyTagInfo: keyTagInfo }; + } + if (avcData) { + avcData.data.push(data.subarray(offset, start + 188)); + avcData.size += start + 188 - offset; + } + break; + case audioId: + if (stt && !this.iframeMode) { + if (audioData && (pes = this._parsePES(audioData))) { + switch (audioContext.segmentCodec) { + case 'aac': + this._parseAACPES(pes); + break; + case 'mp3': + this._parseMPEGPES(pes); + break; + case 'ac3': + case 'ec3': + this._parseDolbyPES(pes); + break; + } + } + audioData = { data: [], size: 0, keyTagInfo: keyTagInfo }; + } + if (audioData) { + audioData.data.push(data.subarray(offset, start + 188)); + audioData.size += start + 188 - offset; + } + break; + case id3Id: + if (stt) { + if (id3Data && (pes = this._parsePES(id3Data))) { + id3Track.id3Samples.push(pes); + } + id3Data = { data: [], size: 0 }; + } + if (id3Data) { + id3Data.data.push(data.subarray(offset, start + 188)); + id3Data.size += start + 188 - offset; + } + break; + case 0: + if (stt) { + offset += data[offset] + 1; + } + pmtId = this._pmtId = this._parsePAT(data, offset); + break; + case pmtId: + if (stt) { + offset += data[offset] + 1; + } + const parsedPIDs = this._parsePMT(data, offset, this.typeSupported); + // only update track id if track PID found while parsing PMT + // this is to avoid resetting the PID to -1 in case + // track PID transiently disappears from the stream + // this could happen in case of transient missing audio samples for example + avcId = parsedPIDs.avcId; + if (avcId > 0) { + avcContext.info.id = avcId; + avcContext.info.encrypted = parsedPIDs.videoEncrypted; + } + audioId = parsedPIDs.audioId; + if (audioId > 0) { + audioContext.info.id = audioId; + audioContext.segmentCodec = parsedPIDs.audioSegmentCodec; + audioContext.info.encrypted = parsedPIDs.audioEncrypted; + } + id3Id = parsedPIDs.id3Id; + if (id3Id > 0) { + id3Track.id = id3Id; + } + if (unknownPIDs && !pmtParsed) { + this.logger.info(loggerName$a, 'reparse from beginning'); + unknownPIDs = false; + // we set it to -188, the += 188 in the for loop will reset start to 0 + start = -188; + } + pmtParsed = this.pmtParsed = true; + break; + case 17: // eslint-disable-line + case 8191: + break; + default: + unknownPIDs = true; + break; + } + } + else { + const payload = new FragParsingError(false, 'TS packet did not start with 0x47', ErrorResponses.NoTSSyncByteFound); + this.observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return; + } + } + // try to parse last PES packets + if (avcData && (pes = this._parsePES(avcData))) { + this._parseAVCPES(pes, true); + avcContext.pesData = undefined; + } + else { + // either avcData null or PES truncated, keep it for next frag parsing + avcContext.pesData = avcData; + } + if (audioData && (pes = this._parsePES(audioData))) { + switch (audioContext.segmentCodec) { + case 'aac': + this._parseAACPES(pes); + break; + case 'mp3': + this._parseMPEGPES(pes); + break; + case 'ac3': + case 'ec3': + this._parseDolbyPES(pes); + break; + } + audioContext.pesData = undefined; + } + else { + if (audioData && audioData.size) { + this.logger.warn(loggerName$a, 'last AAC PES packet truncated,might overlap between fragments'); + } + // either audioData null or PES truncated, keep it for next frag parsing + audioContext.pesData = audioData; + } + if (id3Data && (pes = this._parsePES(id3Data))) { + id3Track.id3Samples.push(pes); + id3Track.pesData = undefined; + } + else { + // either id3Data null or PES truncated, keep it for next frag parsing + id3Track.pesData = id3Data; + } + let audioTrack; + if (audioContext.config && audioContext.segmentCodec) { + audioTrack = { type: 'audio', info: audioContext.info, config: audioContext.config, parsingData: audioContext.parsingData }; + } + let videoTrack; + const pc = avcContext.config; + if (isCompleteVideoConfig(pc)) { + videoTrack = { type: 'video', info: avcContext.info, config: pc, parsingData: avcContext.parsingData }; + } + this.esRemuxer.remuxEsTracks(audioTrack, videoTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset, keyTagInfo, iframeMediaStart, iframeDuration); + } + destroy() { + this._duration = 0; + } + _parsePAT(data, offset) { + // skip the PSI header and parse the first PMT entry + return ((data[offset + 10] & 31) << 8) | data[offset + 11]; + } + _parsePMT(data, offset, typeSupported) { + let pid; // result = { audio : -1, avc : -1, id3 : -1, segmentCodec : 'aac', videoEncrypted : false, audioEncrypted :false }; + const result = { + audioId: -1, + avcId: -1, + id3Id: -1, + audioEncrypted: false, + videoEncrypted: false, + }; + const sectionLength = ((data[offset + 1] & 15) << 8) | data[offset + 2]; + const tableEnd = offset + 3 + sectionLength - 4; + // to determine where the table is, we have to figure out how + // long the program info descriptors are + const programInfoLength = ((data[offset + 10] & 15) << 8) | data[offset + 11]; + // advance the offset to the first entry in the mapping table + offset += 12 + programInfoLength; + while (offset < tableEnd) { + pid = ((data[offset + 1] & 31) << 8) | data[offset + 2]; + switch (data[offset]) { + case 207: + result.audioEncrypted = true; + /* falls through */ + // ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio) + case 15: + // logger.info('AAC PID:' + pid); + if (result.audioId === -1) { + result.audioId = pid; + result.audioSegmentCodec = 'aac'; + } + break; + // Packetized metadata (ID3) + case 21: + // logger.info('ID3 PID:' + pid); + if (result.id3Id === -1) { + result.id3Id = pid; + } + break; + case 219: + result.videoEncrypted = true; + /* falls through */ + // ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video) + case 27: + // logger.info('AVC PID:' + pid); + if (result.avcId === -1) { + result.avcId = pid; + } + break; + // ISO/IEC 11172-3 (MPEG-1 audio) + // or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio) + case 3: + case 4: + // logger.info('MPEG PID:' + pid); + if (typeSupported.mpeg !== true && typeSupported.mp3 !== true) { + this.logger.warn(loggerName$a, 'MPEG audio found, not supported in this browser for now'); + } + else if (result.audioId === -1) { + result.audioId = pid; + result.audioSegmentCodec = 'mp3'; + } + break; + case 193: + result.audioEncrypted = true; + this.logger.info(loggerName$a, 'TS encrypted AC-3 audio found'); + /* falls through */ + case 129: + if (typeSupported.ac3 !== true) { + this.logger.warn(loggerName$a, 'AC-3 audio found, not supported in this browser for now'); + } + else if (result.audioId === -1) { + result.audioId = pid; + result.audioSegmentCodec = 'ac3'; + } + break; + case 194: + result.audioEncrypted = true; + this.logger.info(loggerName$a, 'TS encrypted EC-3 audio found'); + /* falls through */ + case 135: + if (typeSupported.ec3 !== true) { + this.logger.warn(loggerName$a, 'EC-3 audio found, not supported in this browser for now'); + } + else if (result.audioId === -1) { + result.audioId = pid; + result.audioSegmentCodec = 'ec3'; + } + break; + case 36: + this.logger.warn(loggerName$a, 'HEVC stream type found, not supported for now'); + break; + default: + this.logger.warn(loggerName$a, 'unkown stream type:' + data[offset]); + break; + } + // move to the next table entry + // skip past the elementary stream descriptors, if present + offset += (((data[offset + 3] & 15) << 8) | data[offset + 4]) + 5; + } + return result; + } + _parsePES(stream) { + let i = 0, frag, pesFlags, pesLen, pesHdrLen, pesData, payloadStartOffset; + const data = stream.data, keyTagInfo = stream.keyTagInfo; + let pesPts = NaN, pesDts = NaN; + // safety check + if (!stream || stream.size === 0) { + return undefined; + } + // we might need up to 19 bytes to read PES header + // if first chunk of data is less than 19 bytes, let's merge it with following ones until we get 19 bytes + // usually only one merge is needed (and this is rare ...) + while (data[0].length < 19 && data.length > 1) { + const newData = new Uint8Array(data[0].length + data[1].length); + newData.set(data[0]); + newData.set(data[1], data[0].length); + data[0] = newData; + data.splice(1, 1); + } + // retrieve PTS/DTS from first fragment + frag = data[0]; + const pesPrefix = (frag[0] << 16) + (frag[1] << 8) + frag[2]; + if (pesPrefix === 1) { + pesLen = (frag[4] << 8) + frag[5]; + // if PES parsed length is not zero and greater than total received length, stop parsing. PES might be truncated + // minus 6 : PES header size + if (pesLen && pesLen > stream.size - 6) { + return undefined; + } + pesFlags = frag[7]; + if (pesFlags & 192) { + /* PES header described here : http://dvd.sourceforge.net/dvdinfo/pes-hdr.html + as PTS / DTS is 33 bit we cannot use bitwise operator in JS, + as Bitwise operators treat their operands as a sequence of 32 bits */ + pesPts = + (frag[9] & 14) * 536870912 + // 1 << 29 + (frag[10] & 255) * 4194304 + // 1 << 22 + (frag[11] & 254) * 16384 + // 1 << 14 + (frag[12] & 255) * 128 + // 1 << 7 + (frag[13] & 254) / 2; + if (pesFlags & 64) { + pesDts = + (frag[14] & 14) * 536870912 + // 1 << 29 + (frag[15] & 255) * 4194304 + // 1 << 22 + (frag[16] & 254) * 16384 + // 1 << 14 + (frag[17] & 255) * 128 + // 1 << 7 + (frag[18] & 254) / 2; + if (pesPts - pesDts > 5400000) { + this.logger.warn(loggerName$a, `${Math.round((pesPts - pesDts) / 90000)}s delta between PTS and DTS, align them`); + pesPts = pesDts; + } + } + else { + pesDts = pesPts; + } + } + pesHdrLen = frag[8]; + // 9 bytes : 6 bytes for PES header + 3 bytes for PES extension + payloadStartOffset = pesHdrLen + 9; + stream.size -= payloadStartOffset; + // reassemble PES packet + pesData = new Uint8Array(stream.size); + for (let j = 0, dataLen = data.length; j < dataLen; j++) { + frag = data[j]; + let len = frag.byteLength; + if (payloadStartOffset) { + if (payloadStartOffset > len) { + // trim full frag if PES header bigger than frag + payloadStartOffset -= len; + continue; + } + else { + // trim partial frag if PES header smaller than frag + frag = frag.subarray(payloadStartOffset); + len -= payloadStartOffset; + payloadStartOffset = 0; + } + } + pesData.set(frag, i); + i += len; + } + if (pesLen) { + // payload size : remove PES header + PES extension + pesLen -= pesHdrLen + 3; + } + return { data: pesData, pts: pesPts, dts: pesDts, len: pesLen, keyTagInfo: keyTagInfo }; + } + else { + return undefined; + } + } + pushAccesUnit(avcContext, keyTagInfo) { + const avcSample = avcContext.avcSample; + if (avcSample && avcSample.units.length && avcSample.frame) { + const samples = avcContext.parsingData.esSamples; + const nbSamples = samples.length; + if (avcSample.key === true || (avcContext.config.sps && (nbSamples || this.contiguous))) { + avcSample.id = nbSamples; + avcSample.keyTagInfo = keyTagInfo; + if (avcContext.info.encrypted) { + const units = avcSample.units; + units.forEach((unit) => { + if (unit.data.byteLength > 48) { + switch (unit.type) { + case 1: + case 5: + // need to remove EPBs from encrypted NALs now that they're parsed into units + unit.data = this.discardEPB(unit.data); + break; + } + } + }); + } + } + if (!nbSamples && !isFinite(avcSample.pts)) { + // dropping samples, no timestamp found + avcContext.parsingData.dropped++; + return; + } + samples.push(avcSample); + } + if (avcSample && avcSample.debug.length) { + this.logger.info(loggerName$a, avcSample.pts + '/' + avcSample.dts + ':' + avcSample.debug); + } + } + _parseAVCPES(pes, last) { + if (!pes.data) { + throw 'invalid pes data'; + } + const avcContext = this._avcContext, units = this._parseAVCNALu(pes.data); + let expGolombDecoder, avcSample = avcContext.avcSample, push, i; + const keyTagInfo = pes.keyTagInfo; + // free pes.data to save up some memory + pes.data = undefined; + units.forEach((unit) => { + switch (unit.type) { + // NDR + case 1: + if (avcSample && !this.iframeMode) { + // TODO: demuxer needs better support for partial nal units + push = true; + avcSample.frame = true; + // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR + const data = unit.data; + if (data.length > 4) { + const sliceType = new ExpGolomb(data, this.logger).readSliceType(); + // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice + // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples. + // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice. + // I slice: A slice that is not an SI slice that is decoded using intra prediction only. + // if (sliceType === 2 || sliceType === 7) { + if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) { + avcSample.key = true; + } + } + } + break; + // IDR + case 5: + push = true; + if (avcSample) { + // handle PES not starting with AUD + if (!avcSample) { + avcSample = avcContext.avcSample = this._createAVCSample(true, pes.pts, pes.dts, ''); + } + avcSample.key = true; + avcSample.frame = true; + } + break; + // SEI + case 6: + push = true; + expGolombDecoder = new ExpGolomb(this.discardEPB(unit.data), this.logger); + // skip frameType + expGolombDecoder.readUByte(); + let payloadType = 0; + let payloadSize = 0; + let endOfCaptions = false; + let b = 0; + while (!endOfCaptions && expGolombDecoder.bytesAvailable > 1) { + payloadType = 0; + do { + b = expGolombDecoder.readUByte(); + payloadType += b; + } while (b === 255); + // Parse payload size. + payloadSize = 0; + do { + b = expGolombDecoder.readUByte(); + payloadSize += b; + } while (b === 255); + // TODO: there can be more than one payload in an SEI packet... + // TODO: need to read type and size in a while loop to get them all + if (payloadType === 4 && expGolombDecoder.bytesAvailable !== 0) { + endOfCaptions = true; + const countryCode = expGolombDecoder.readUByte(); + if (countryCode === 181) { + const providerCode = expGolombDecoder.readUShort(); + if (providerCode === 49) { + const userStructure = expGolombDecoder.readUInt(); + if (userStructure === 1195456820) { + const userDataType = expGolombDecoder.readUByte(); + // Raw CEA-608 bytes wrapped in CEA-708 packet + if (userDataType === 3) { + const firstByte = expGolombDecoder.readUByte(); + const secondByte = expGolombDecoder.readUByte(); + const totalCCs = 31 & firstByte; + const byteArray = [firstByte, secondByte]; + for (i = 0; i < totalCCs; i++) { + // 3 bytes per CC + byteArray.push(expGolombDecoder.readUByte()); + byteArray.push(expGolombDecoder.readUByte()); + byteArray.push(expGolombDecoder.readUByte()); + } + this._insertSampleInOrder(this._txtTrack.captionSamples, { type: 3, pts: pes.pts, bytes: byteArray }); + } + } + } + } + } + else if (payloadSize < expGolombDecoder.bytesAvailable) { + for (i = 0; i < payloadSize; i++) { + expGolombDecoder.readUByte(); + } + } + } + break; + // SPS + case 7: + push = true; + if (!avcContext.config.sps) { + expGolombDecoder = new ExpGolomb(unit.data, this.logger); + const config = expGolombDecoder.readSPS(); + avcContext.config.width = config.width; + avcContext.config.height = config.height; + avcContext.config.pixelRatio = config.pixelRatio; + avcContext.config.sps = [unit.data]; + avcContext.info.duration = this._duration; + const codecarray = unit.data.subarray(1, 4); + let codecstring = 'avc1.'; + for (i = 0; i < 3; i++) { + let h = codecarray[i].toString(16); + if (h.length < 2) { + h = '0' + h; + } + codecstring += h; + } + avcContext.config.codec = codecstring; + } + break; + // PPS + case 8: + push = true; + if (!avcContext.config.pps) { + avcContext.config.pps = [unit.data]; + } + break; + // AUD + case 9: + push = false; + if (avcSample) { + this.pushAccesUnit(avcContext, keyTagInfo); + } + avcSample = avcContext.avcSample = this._createAVCSample(false, pes.pts, pes.dts, ''); + break; + // Filler Data + case 12: + push = false; + break; + default: + push = false; + if (avcSample) { + avcSample.debug += 'unknown NAL ' + unit.type + ' '; + } + break; + } + if (avcSample && push) { + const units = avcSample.units; + units.push(unit); + } + }); + // if last PES packet, push samples + if (last && avcSample) { + this.pushAccesUnit(avcContext, keyTagInfo); + avcContext.avcSample = undefined; + } + } + _createAVCSample(key, pts, dts, debug) { + return { id: NaN, key: key, pts: pts, dts: dts, units: new Array(), debug: debug }; + } + _insertSampleInOrder(arr, data) { + const len = arr.length; + if (len > 0) { + if (data.pts >= arr[len - 1].pts) { + arr.push(data); + } + else { + for (let pos = len - 1; pos >= 0; pos--) { + if (data.pts < arr[pos].pts) { + arr.splice(pos, 0, data); + break; + } + } + } + } + else { + arr.push(data); + } + } + _getLastNalUnit() { + const avcContext = this._avcContext; + let avcSample = avcContext.avcSample, lastUnit; + // try to fallback to previous sample if current one is empty + if (!avcSample || avcSample.units.length === 0) { + const samples = avcContext.parsingData.esSamples; + avcSample = samples[samples.length - 1]; + } + if (avcSample) { + const units = avcSample.units; + lastUnit = units[units.length - 1]; + } + return lastUnit; + } + _parseAVCNALu(array) { + const len = array.byteLength; + let i = 0, value, overflow; + const avcContext = this._avcContext; + let state = avcContext.naluState || 0; + const lastState = state; + const units = []; + let unit, unitType, lastUnitStart = -1, lastUnitType; + if (state === -1) { + // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet + lastUnitStart = 0; + // NALu type is value read from offset 0 + lastUnitType = array[0] & 31; + state = 0; + i = 1; + } + while (i < len) { + value = array[i++]; + // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case + if (!state) { + state = value ? 0 : 1; + continue; + } + if (state === 1) { + state = value ? 0 : 2; + continue; + } + // here we have state either equal to 2 or 3 + if (!value) { + state = 3; + } + else if (value === 1) { + if (lastUnitStart >= 0) { + unit = { data: array.subarray(lastUnitStart, i - state - 1), type: lastUnitType }; + units.push(unit); + } + else { + // lastUnitStart is -1 => this is the first start code found in this PES packet + // first check if start code delimiter is overlapping between 2 PES packets, + // ie it started in last packet (lastState not zero) + // and ended at the beginning of this PES packet (i <= 4 - lastState) + const lastUnit = this._getLastNalUnit(); + if (lastUnit) { + if (lastState && i <= 4 - lastState) { + // start delimiter overlapping between PES packets + // strip start delimiter bytes from the end of last NAL unit + // check if lastUnit had a state different from zero + if (lastUnit.state) { + // strip last bytes + lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState); + } + } + // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit. + overflow = i - state - 1; + if (overflow > 0) { + const tmp = new Uint8Array(lastUnit.data.byteLength + overflow); + tmp.set(lastUnit.data, 0); + tmp.set(array.subarray(0, overflow), lastUnit.data.byteLength); + lastUnit.data = tmp; + lastUnit.state = 0; + } + } + } + // check if we can read unit type + if (i < len) { + unitType = array[i] & 31; + lastUnitStart = i; + lastUnitType = unitType; + state = 0; + } + else { + // not enough byte to read unit type. let's read it on next PES parsing + state = -1; + } + } + else { + state = 0; + } + } + if (lastUnitStart >= 0 && state >= 0) { + unit = { data: array.subarray(lastUnitStart, len), type: lastUnitType, state }; + units.push(unit); + } + // no NALu found + if (units.length === 0) { + // append pes.data to previous NAL unit + const lastUnit = this._getLastNalUnit(); + if (lastUnit) { + const tmp = new Uint8Array(lastUnit.data.byteLength + array.byteLength); + tmp.set(lastUnit.data, 0); + tmp.set(array, lastUnit.data.byteLength); + lastUnit.data = tmp; + } + } + avcContext.naluState = state; + return units; + } + /** + * remove Emulation Prevention bytes from a RBSP + */ + discardEPB(data) { + const length = data.byteLength, EPBPositions = []; + let i = 1; + // Find all `Emulation Prevention Bytes` + while (i < length - 2) { + if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 3) { + EPBPositions.push(i + 2); + i += 2; + } + else { + i++; + } + } + // If no Emulation Prevention Bytes were found just return the original + // array + if (EPBPositions.length === 0) { + return data; + } + // Create a new array to hold the NAL unit data + const newLength = length - EPBPositions.length; + const newData = new Uint8Array(newLength); + let sourceIndex = 0; + for (i = 0; i < newLength; sourceIndex++, i++) { + if (sourceIndex === EPBPositions[0]) { + // Skip this byte + sourceIndex++; + // Remove this position index + EPBPositions.shift(); + } + newData[i] = data[sourceIndex]; + } + return newData; + } + _parseAACPES(pes) { + const audioContext = this._audioContext, audioLastPTS = audioContext.audioLastPTS, startOffset = 0, keyTagInfo = pes.keyTagInfo; + let data = pes.data, pts = pes.pts, audioOverFlow = audioContext.audioOverFlow, frameLength, frameIndex, offset, headerLength, stamp, len, aacSample; + if (audioOverFlow) { + const tmp = new Uint8Array(audioOverFlow.byteLength + data.byteLength); + tmp.set(audioOverFlow, 0); + tmp.set(data, audioOverFlow.byteLength); + data = tmp; + } + // look for ADTS header (0xFFFx) + for (offset = startOffset, len = data.length; offset < len - 1; offset++) { + if (data[offset] === 255 && (data[offset + 1] & 240) === 240) { + break; + } + } + // if ADTS header does not start straight from the beginning of the PES payload, raise an error + if (offset) { + let reason, fatal, response; + if (offset < len - 1) { + reason = `AAC PES did not start with ADTS header,offset:${offset}`; + fatal = false; + response = ErrorResponses.PESDidNotStartWithADTS; + } + else { + reason = 'no ADTS header found in AAC PES'; + fatal = true; + response = ErrorResponses.NoADTSHeaderInPES; + } + this.logger.warn(loggerName$a, `parsing error:${reason}`); + const payload = new FragParsingError(fatal, reason, response); + this.observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + if (fatal) { + return; + } + } + if (!audioContext.config) { + const config = ADTS.getAudioConfig(this.observer, data, offset, undefined, this.logger); + if (!config) { + throw 'unable to parse adts header'; + } + audioContext.config = config; + this.logger.info(loggerName$a, `parsed codec:${config.codec},rate:${config.samplerate},nb channel:${config.channelCount}`); + } + frameIndex = 0; + const frameDuration = 92160000 / audioContext.config.samplerate; + // if last AAC frame is overflowing, we should ensure timestamps are contiguous: + // first sample PTS should be equal to last sample PTS + frameDuration + if (audioOverFlow && audioLastPTS) { + const newPTS = audioLastPTS + frameDuration; + if (Math.abs(newPTS - pts) > 1) { + this.logger.info(loggerName$a, `AAC: align PTS for overlapping frames by ${Math.round((newPTS - pts) / 90)}`); + pts = newPTS; + } + } + while (offset + 5 < len) { + // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header + headerLength = data[offset + 1] & 1 ? 7 : 9; + // retrieve frame size + frameLength = ((data[offset + 3] & 3) << 11) | (data[offset + 4] << 3) | ((data[offset + 5] & 224) >>> 5); + frameLength -= headerLength; + if (frameLength > 0 && offset + headerLength + frameLength <= len) { + stamp = pts + frameIndex * frameDuration; + aacSample = { unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), pts: stamp, dts: stamp, keyTagInfo: keyTagInfo }; + audioContext.parsingData.esSamples.push(aacSample); + audioContext.parsingData.len += frameLength; + offset += frameLength + headerLength; + frameIndex++; + // look for ADTS header (0xFFFx) + for (; offset < len - 1; offset++) { + if (data[offset] === 255 && (data[offset + 1] & 240) === 240) { + break; + } + } + } + else { + break; + } + } + if (offset < len) { + audioOverFlow = data.subarray(offset, len); + } + else { + audioOverFlow = undefined; + } + audioContext.audioOverFlow = audioOverFlow; + audioContext.audioLastPTS = stamp; + } + _parseMPEGPES(pes) { + if (this._audioContext.segmentCodec === 'mp3') { + MpegAudio$1.parse(this._audioContext.parsingData, pes.data, 0, pes.pts, this.logger); + } + } + _parseDolbyPES(pes) { + const audioContext = this._audioContext; + let data = pes.data; + let pts = pes.pts; + const keyTagInfo = pes.keyTagInfo; + let frameIndex = 0; + let offset = 0; + let audioOverFlow = audioContext.audioOverFlow; + const audioLastPTS = audioContext.audioLastPTS; + if (!audioContext.config) { + let config; + if (audioContext.segmentCodec === 'ac3') { + config = Dolby.getAudioConfig(this.observer, data, offset, this.logger); + } + else if (audioContext.segmentCodec === 'ec3') { + config = DDPlus$1.getAudioConfig(this.observer, data, offset, this.logger); + } + if (!config) { + throw 'unable to parse dolby header'; + } + audioContext.config = config; + } + if (audioContext.config.segmentCodec !== 'ac3' && audioContext.config.segmentCodec !== 'ec3') { + throw 'unexpected config type'; + } + const frameDuration = (1536 / audioContext.config.samplerate) * audioContext.info.inputTimescale; + if (audioOverFlow) { + const tmp = new Uint8Array(audioOverFlow.byteLength + data.byteLength); + tmp.set(audioOverFlow, 0); + tmp.set(data, audioOverFlow.byteLength); + data = tmp; + } + const length = data.length; + if (audioOverFlow && audioLastPTS) { + const newPTS = audioLastPTS + frameDuration; + if (Math.abs(newPTS - pts) > 1) { + pts = newPTS; + } + } + let frameLength = 0; + while (offset + frameLength <= length) { + if (data[offset] !== 11 || data[offset + 1] !== 119) { + const payload = new FragParsingError(true, 'invalid dolby audio magic', ErrorResponses.InvalidDolbyAudioMagic); + this.observer.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return; + } + if (audioContext.segmentCodec === 'ac3') { + frameLength = Dolby.getFrameLength(this.observer, data, offset); + } + else if (audioContext.segmentCodec === 'ec3') { + frameLength = DDPlus$1.getFrameLength(this.observer, data, offset, this.logger); + } + const stamp = pts + frameIndex * frameDuration; + audioContext.audioLastPTS = stamp; + const dolbySample = { unit: data.subarray(offset, offset + frameLength), pts: stamp, dts: stamp, keyTagInfo: keyTagInfo }; + audioContext.parsingData.esSamples.push(dolbySample); + audioContext.info.duration = this._duration; + audioContext.parsingData.len += frameLength; + offset += frameLength; + frameIndex++; + } + if (offset < length) { + audioOverFlow = data.subarray(offset, length); + } + else { + audioOverFlow = undefined; + } + audioContext.audioOverFlow = audioOverFlow; + } + } + var TsDemuxer$1 = TsDemuxer; + + + class DemuxerInline extends Observer { + constructor(typeSupported, config, vendor, logger) { + super(); + this.typeSupported = typeSupported; + this.config = config; + this.vendor = vendor; + this.logger = logger; + } + destroy() { + this.removeAllListeners(); + const demuxer = this.demuxer; + const remuxer = this.remuxer; + if (demuxer) { + demuxer.destroy(); + } + if (remuxer) { + remuxer.destroy(); + } + } + push(data, keyTagInfo, initSegment, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS, iframeMediaStart, iframeDuration) { + if (!data) { + // if data is undefined and this.probeFn exists, + // all the demuxers will fail probing. We will still end up with the old probeFn. + // We will push Uint8Array(undefined) to the demuxer that owns this.probeFn, which will be fatal + return; + } + let demuxer = this.demuxer; + const _data = new Uint8Array(data); + if (!demuxer || + // in case of continuity change, we might switch from content type (AAC container to TS container for example). this is signaled through the discontinuity argument. + // also a variant switch can also change the content type (TS container to fMP4 contaner for example). this is signaled through the trackSwitch argument. + // so let's check that current demuxer is still valid + ((discontinuity || trackSwitch) && !this.probeFn(_data, this.logger))) { + const { typeSupported, config } = this; + const muxConfig = [ + { demux: MP4Demuxer, remux: MP4EncryptionRemuxer }, + { demux: TsDemuxer$1, remux: EsRemuxer }, + { demux: EC3Demuxer, remux: EsRemuxer }, + { demux: AC3Demuxer, remux: EsRemuxer }, + { demux: AACDemuxer, remux: EsRemuxer }, + { demux: MP3Demuxer, remux: EsRemuxer }, + ]; // move MP3Demuxer to last to avoid false positive mp3 detection. + // probe for content type + for (const mux of muxConfig) { + const { probe } = mux.demux; + if (probe(_data, this.logger)) { + this.remuxer = new mux.remux(this, config, typeSupported, this.vendor, this.logger); + demuxer = new mux.demux(this, this.remuxer, config, typeSupported, this.logger); + this.probeFn = probe; + break; + } + } + if (!demuxer) { + const payload = new FragParsingError(true, 'no demux matching with content found', ErrorResponses.DemuxerNotFound); + this.trigger(HlsEvent$1.INTERNAL_ERROR, payload); + return; + } + this.demuxer = demuxer; + } + const remuxer = this.remuxer; + const keyChange = !this.lastKeyTagInfo || (keyTagInfo && keyTagInfo.method !== 'NONE' && this.lastKeyTagInfo.uri !== keyTagInfo.uri); + this.lastKeyTagInfo = keyTagInfo; + if (discontinuity || trackSwitch || keyChange) { + // resetInitSegment error handling may need to know if we are in a discontinuity or track switch + demuxer.resetInitSegment(new Uint8Array(initSegment), duration, keyTagInfo, discontinuity); + remuxer.resetInitSegment(); + } + if (discontinuity) { + const pts = defaultInitPTS ? convertTimestampToSeconds(defaultInitPTS) : undefined; + demuxer.resetTimeStamp(pts); + remuxer.resetTimeStamp(pts); + } + demuxer.append(_data, timeOffset, contiguous, accurateTimeOffset, keyTagInfo, iframeMediaStart, iframeDuration); + } + } + + function generateUniqueID() { + let id = `${Date.now()}-${Math.random()}`; + if (typeof performance !== 'undefined' && typeof performance.now === 'function') { + id += `-${performance.now()}`; + } + return id; + } + + class DemuxRPCServer { + constructor(rpc, logger) { + this.rpc = rpc; + this.logger = logger; + this.init = (typeSupported, config, vendor) => callback => { + const demuxSessionID = generateUniqueID(); + const demuxer = (this.demuxers[demuxSessionID] = new DemuxerInline(typeSupported, config, vendor, this.logger)); + [ + DemuxerEvent.INIT_PTS_FOUND, + DemuxerEvent.FRAG_PARSING_INIT_SEGMENT, + DemuxerEvent.FRAG_PARSING_DATA, + DemuxerEvent.FRAG_PARSED, + HlsEvent$1.INTERNAL_ERROR + ].forEach(event => { + demuxer.on(event, (data) => this.rpc.invoke('demuxer.event', [demuxSessionID, event, data])(() => { })); + }); + callback(demuxSessionID); + }; + this.push = (id, data, keyTagInfo, initSegment, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS, iframeMediaStart, iframeDuration) => callback => { + const demuxer = this.demuxers[id]; + if (!demuxer) { + callback(undefined, `Demuxer with id "${id}" does not exist on push`); + return; + } + demuxer.push(data, keyTagInfo, initSegment, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS, iframeMediaStart, iframeDuration); + callback(); + }; + this.destroy = (id) => callback => { + const demuxer = this.demuxers[id]; + if (!demuxer) { + this.logger.error(`Demuxer with id "${id}" does not exist on destroy`); + return; + } + demuxer.destroy(); + delete this.demuxers[id]; + callback(); + }; + this.demuxers = {}; + rpc.register('demuxer.init', this.init); + rpc.register('demuxer.push', this.push); + rpc.register('demuxer.destroy', this.destroy); + } + } + + // RPCWorkerService has client and server counterparts, where the service server + // runs in a Web Worker process, while the client remains in the main process. + class RPCWorkerService { + constructor(worker) { + this.worker = worker; + this.handlers = {}; + this.deferers = {}; + this._messageHandler = (event) => { + const { type, id, command, args, result, error } = event.data; + if (type === RPCWorkerMessageType.Invoke) { + try { + if (this.handlers[command] == null) { + throw new Error(`command ${command} not found`); + } + this.handlers[command](...args)(this._respond.bind(this, id, command)); + } + catch (error) { + this._respond(id, command, null, new Error(`command ${command} not found`)); + } + } + else if (type === RPCWorkerMessageType.Result) { + if (this.deferers[id] == null) { + return; + } + this.deferers[id](result, error); + delete this.deferers[id]; + } + }; + worker.addEventListener('message', this._messageHandler); + } + register(command, handler) { + if (this.handlers[command] != null) { + return false; + } + this.handlers[command] = handler; + } + unregister(command) { + if (this.handlers[command] != null) { + return false; + } + delete this.handlers[command]; + } + invoke(command, args, transfer) { + return (callback = RPCWorkerService._fallbackCallback) => { + const id = generateUniqueID(); + this.deferers[id] = callback; + const message = { + type: RPCWorkerMessageType.Invoke, + id, + command, + args, + }; + this._send(message, transfer); + }; + } + teardown(done) { + this.worker.removeEventListener('message', this._messageHandler); + done(); + } + _respond(id, command, result, error, transfer) { + if (error instanceof Error) { + error = `[${error.name}] ${error.message}\n${error.stack}`; + } + const message = { + type: RPCWorkerMessageType.Result, + id, + command, + result, + error, + }; + this._send(message, transfer); + } + _send(message, transfer = []) { + this.worker.postMessage(message, transfer.map((value) => (ArrayBuffer.isView(value) ? value.buffer : value)).filter((value) => value !== undefined)); + } + } + RPCWorkerService._fallbackCallback = (result, error) => { + if (error != null) { + throw error; + } + }; + var RPCWorkerMessageType; + (function (RPCWorkerMessageType) { + RPCWorkerMessageType[RPCWorkerMessageType["Invoke"] = 0] = "Invoke"; + RPCWorkerMessageType[RPCWorkerMessageType["Result"] = 1] = "Result"; + })(RPCWorkerMessageType || (RPCWorkerMessageType = {})); + // Minimal polyfill + // ArrayBuffer.isView() is relatively new + // https://caniuse.com/?search=arraybuffer + if (!ArrayBuffer['isView']) { + ArrayBuffer.isView = function isView(a) { + return a !== null && typeof a === 'object' && a['buffer'] instanceof ArrayBuffer; + }; + } + + const startWorker = () => { + const ctx = global$1; + const rpcService = new RPCWorkerService(ctx); + const logger = LoggerRPCClient(rpcService); + new CryptoRPCServer(rpcService, logger); + new DemuxRPCServer(rpcService, logger); + logger.info('WebWorker RPCService has started'); + }; + + if (hasUMDWorker() && typeof __IN_WORKER__ !== 'undefined' && __IN_WORKER__) { + startWorker(); + } + + /*! ***************************************************************************** + Copyright (c) Microsoft Corporation. + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + PERFORMANCE OF THIS SOFTWARE. + ***************************************************************************** */ + /* global Reflect, Promise */ + + var extendStatics = function(d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + + function __extends(d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + } + + var __assign = function() { + __assign = Object.assign || function __assign(t) { + for (var s, i = 1, n = arguments.length; i < n; i++) { + s = arguments[i]; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p]; + } + return t; + }; + return __assign.apply(this, arguments); + }; + + function __rest(s, e) { + var t = {}; + for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) + t[p] = s[p]; + if (s != null && typeof Object.getOwnPropertySymbols === "function") + for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { + if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) + t[p[i]] = s[p[i]]; + } + return t; + } + + function __decorate$2(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + } + + function __metadata(metadataKey, metadataValue) { + if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue); + } + + function __values(o) { + var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0; + if (m) return m.call(o); + if (o && typeof o.length === "number") return { + next: function () { + if (o && i >= o.length) o = void 0; + return { value: o && o[i++], done: !o }; + } + }; + throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined."); + } + + function __read(o, n) { + var m = typeof Symbol === "function" && o[Symbol.iterator]; + if (!m) return o; + var i = m.call(o), r, ar = [], e; + try { + while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value); + } + catch (error) { e = { error: error }; } + finally { + try { + if (r && !r.done && (m = i["return"])) m.call(i); + } + finally { if (e) throw e.error; } + } + return ar; + } + + /** @deprecated */ + function __spread() { + for (var ar = [], i = 0; i < arguments.length; i++) + ar = ar.concat(__read(arguments[i])); + return ar; + } + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + function isFunction$1(x) { + return typeof x === 'function'; + } + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + var _enable_super_gross_mode_that_will_cause_bad_things = false; + var config = { + Promise: undefined, + set useDeprecatedSynchronousErrorHandling(value) { + _enable_super_gross_mode_that_will_cause_bad_things = value; + }, + get useDeprecatedSynchronousErrorHandling() { + return _enable_super_gross_mode_that_will_cause_bad_things; + }, + }; + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + function hostReportError(err) { + setTimeout(function () { throw err; }, 0); + } + + /** PURE_IMPORTS_START _config,_util_hostReportError PURE_IMPORTS_END */ + var empty$1 = { + closed: true, + next: function (value) { }, + error: function (err) { + if (config.useDeprecatedSynchronousErrorHandling) { + throw err; + } + else { + hostReportError(err); + } + }, + complete: function () { } + }; + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + var isArray$1 = /*@__PURE__*/ (function () { return Array.isArray || (function (x) { return x && typeof x.length === 'number'; }); })(); + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + function isObject$1(x) { + return x !== null && typeof x === 'object'; + } + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + var UnsubscriptionErrorImpl = /*@__PURE__*/ (function () { + function UnsubscriptionErrorImpl(errors) { + Error.call(this); + this.message = errors ? + errors.length + " errors occurred during unsubscription:\n" + errors.map(function (err, i) { return i + 1 + ") " + err.toString(); }).join('\n ') : ''; + this.name = 'UnsubscriptionError'; + this.errors = errors; + return this; + } + UnsubscriptionErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); + return UnsubscriptionErrorImpl; + })(); + var UnsubscriptionError = UnsubscriptionErrorImpl; + + /** PURE_IMPORTS_START _util_isArray,_util_isObject,_util_isFunction,_util_UnsubscriptionError PURE_IMPORTS_END */ + var Subscription = /*@__PURE__*/ (function () { + function Subscription(unsubscribe) { + this.closed = false; + this._parentOrParents = null; + this._subscriptions = null; + if (unsubscribe) { + this._ctorUnsubscribe = true; + this._unsubscribe = unsubscribe; + } + } + Subscription.prototype.unsubscribe = function () { + var errors; + if (this.closed) { + return; + } + var _a = this, _parentOrParents = _a._parentOrParents, _ctorUnsubscribe = _a._ctorUnsubscribe, _unsubscribe = _a._unsubscribe, _subscriptions = _a._subscriptions; + this.closed = true; + this._parentOrParents = null; + this._subscriptions = null; + if (_parentOrParents instanceof Subscription) { + _parentOrParents.remove(this); + } + else if (_parentOrParents !== null) { + for (var index = 0; index < _parentOrParents.length; ++index) { + var parent_1 = _parentOrParents[index]; + parent_1.remove(this); + } + } + if (isFunction$1(_unsubscribe)) { + if (_ctorUnsubscribe) { + this._unsubscribe = undefined; + } + try { + _unsubscribe.call(this); + } + catch (e) { + errors = e instanceof UnsubscriptionError ? flattenUnsubscriptionErrors(e.errors) : [e]; + } + } + if (isArray$1(_subscriptions)) { + var index = -1; + var len = _subscriptions.length; + while (++index < len) { + var sub = _subscriptions[index]; + if (isObject$1(sub)) { + try { + sub.unsubscribe(); + } + catch (e) { + errors = errors || []; + if (e instanceof UnsubscriptionError) { + errors = errors.concat(flattenUnsubscriptionErrors(e.errors)); + } + else { + errors.push(e); + } + } + } + } + } + if (errors) { + throw new UnsubscriptionError(errors); + } + }; + Subscription.prototype.add = function (teardown) { + var subscription = teardown; + if (!teardown) { + return Subscription.EMPTY; + } + switch (typeof teardown) { + case 'function': + subscription = new Subscription(teardown); + case 'object': + if (subscription === this || subscription.closed || typeof subscription.unsubscribe !== 'function') { + return subscription; + } + else if (this.closed) { + subscription.unsubscribe(); + return subscription; + } + else if (!(subscription instanceof Subscription)) { + var tmp = subscription; + subscription = new Subscription(); + subscription._subscriptions = [tmp]; + } + break; + default: { + throw new Error('unrecognized teardown ' + teardown + ' added to Subscription.'); + } + } + var _parentOrParents = subscription._parentOrParents; + if (_parentOrParents === null) { + subscription._parentOrParents = this; + } + else if (_parentOrParents instanceof Subscription) { + if (_parentOrParents === this) { + return subscription; + } + subscription._parentOrParents = [_parentOrParents, this]; + } + else if (_parentOrParents.indexOf(this) === -1) { + _parentOrParents.push(this); + } + else { + return subscription; + } + var subscriptions = this._subscriptions; + if (subscriptions === null) { + this._subscriptions = [subscription]; + } + else { + subscriptions.push(subscription); + } + return subscription; + }; + Subscription.prototype.remove = function (subscription) { + var subscriptions = this._subscriptions; + if (subscriptions) { + var subscriptionIndex = subscriptions.indexOf(subscription); + if (subscriptionIndex !== -1) { + subscriptions.splice(subscriptionIndex, 1); + } + } + }; + Subscription.EMPTY = (function (empty) { + empty.closed = true; + return empty; + }(new Subscription())); + return Subscription; + }()); + function flattenUnsubscriptionErrors(errors) { + return errors.reduce(function (errs, err) { return errs.concat((err instanceof UnsubscriptionError) ? err.errors : err); }, []); + } + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + var rxSubscriber = /*@__PURE__*/ (function () { + return typeof Symbol === 'function' + ? /*@__PURE__*/ Symbol('rxSubscriber') + : '@@rxSubscriber_' + /*@__PURE__*/ Math.random(); + })(); + + /** PURE_IMPORTS_START tslib,_util_isFunction,_Observer,_Subscription,_internal_symbol_rxSubscriber,_config,_util_hostReportError PURE_IMPORTS_END */ + var Subscriber = /*@__PURE__*/ (function (_super) { + __extends(Subscriber, _super); + function Subscriber(destinationOrNext, error, complete) { + var _this = _super.call(this) || this; + _this.syncErrorValue = null; + _this.syncErrorThrown = false; + _this.syncErrorThrowable = false; + _this.isStopped = false; + switch (arguments.length) { + case 0: + _this.destination = empty$1; + break; + case 1: + if (!destinationOrNext) { + _this.destination = empty$1; + break; + } + if (typeof destinationOrNext === 'object') { + if (destinationOrNext instanceof Subscriber) { + _this.syncErrorThrowable = destinationOrNext.syncErrorThrowable; + _this.destination = destinationOrNext; + destinationOrNext.add(_this); + } + else { + _this.syncErrorThrowable = true; + _this.destination = new SafeSubscriber(_this, destinationOrNext); + } + break; + } + default: + _this.syncErrorThrowable = true; + _this.destination = new SafeSubscriber(_this, destinationOrNext, error, complete); + break; + } + return _this; + } + Subscriber.prototype[rxSubscriber] = function () { return this; }; + Subscriber.create = function (next, error, complete) { + var subscriber = new Subscriber(next, error, complete); + subscriber.syncErrorThrowable = false; + return subscriber; + }; + Subscriber.prototype.next = function (value) { + if (!this.isStopped) { + this._next(value); + } + }; + Subscriber.prototype.error = function (err) { + if (!this.isStopped) { + this.isStopped = true; + this._error(err); + } + }; + Subscriber.prototype.complete = function () { + if (!this.isStopped) { + this.isStopped = true; + this._complete(); + } + }; + Subscriber.prototype.unsubscribe = function () { + if (this.closed) { + return; + } + this.isStopped = true; + _super.prototype.unsubscribe.call(this); + }; + Subscriber.prototype._next = function (value) { + this.destination.next(value); + }; + Subscriber.prototype._error = function (err) { + this.destination.error(err); + this.unsubscribe(); + }; + Subscriber.prototype._complete = function () { + this.destination.complete(); + this.unsubscribe(); + }; + Subscriber.prototype._unsubscribeAndRecycle = function () { + var _parentOrParents = this._parentOrParents; + this._parentOrParents = null; + this.unsubscribe(); + this.closed = false; + this.isStopped = false; + this._parentOrParents = _parentOrParents; + return this; + }; + return Subscriber; + }(Subscription)); + var SafeSubscriber = /*@__PURE__*/ (function (_super) { + __extends(SafeSubscriber, _super); + function SafeSubscriber(_parentSubscriber, observerOrNext, error, complete) { + var _this = _super.call(this) || this; + _this._parentSubscriber = _parentSubscriber; + var next; + var context = _this; + if (isFunction$1(observerOrNext)) { + next = observerOrNext; + } + else if (observerOrNext) { + next = observerOrNext.next; + error = observerOrNext.error; + complete = observerOrNext.complete; + if (observerOrNext !== empty$1) { + context = Object.create(observerOrNext); + if (isFunction$1(context.unsubscribe)) { + _this.add(context.unsubscribe.bind(context)); + } + context.unsubscribe = _this.unsubscribe.bind(_this); + } + } + _this._context = context; + _this._next = next; + _this._error = error; + _this._complete = complete; + return _this; + } + SafeSubscriber.prototype.next = function (value) { + if (!this.isStopped && this._next) { + var _parentSubscriber = this._parentSubscriber; + if (!config.useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { + this.__tryOrUnsub(this._next, value); + } + else if (this.__tryOrSetError(_parentSubscriber, this._next, value)) { + this.unsubscribe(); + } + } + }; + SafeSubscriber.prototype.error = function (err) { + if (!this.isStopped) { + var _parentSubscriber = this._parentSubscriber; + var useDeprecatedSynchronousErrorHandling = config.useDeprecatedSynchronousErrorHandling; + if (this._error) { + if (!useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { + this.__tryOrUnsub(this._error, err); + this.unsubscribe(); + } + else { + this.__tryOrSetError(_parentSubscriber, this._error, err); + this.unsubscribe(); + } + } + else if (!_parentSubscriber.syncErrorThrowable) { + this.unsubscribe(); + if (useDeprecatedSynchronousErrorHandling) { + throw err; + } + hostReportError(err); + } + else { + if (useDeprecatedSynchronousErrorHandling) { + _parentSubscriber.syncErrorValue = err; + _parentSubscriber.syncErrorThrown = true; + } + else { + hostReportError(err); + } + this.unsubscribe(); + } + } + }; + SafeSubscriber.prototype.complete = function () { + var _this = this; + if (!this.isStopped) { + var _parentSubscriber = this._parentSubscriber; + if (this._complete) { + var wrappedComplete = function () { return _this._complete.call(_this._context); }; + if (!config.useDeprecatedSynchronousErrorHandling || !_parentSubscriber.syncErrorThrowable) { + this.__tryOrUnsub(wrappedComplete); + this.unsubscribe(); + } + else { + this.__tryOrSetError(_parentSubscriber, wrappedComplete); + this.unsubscribe(); + } + } + else { + this.unsubscribe(); + } + } + }; + SafeSubscriber.prototype.__tryOrUnsub = function (fn, value) { + try { + fn.call(this._context, value); + } + catch (err) { + this.unsubscribe(); + if (config.useDeprecatedSynchronousErrorHandling) { + throw err; + } + else { + hostReportError(err); + } + } + }; + SafeSubscriber.prototype.__tryOrSetError = function (parent, fn, value) { + if (!config.useDeprecatedSynchronousErrorHandling) { + throw new Error('bad call'); + } + try { + fn.call(this._context, value); + } + catch (err) { + if (config.useDeprecatedSynchronousErrorHandling) { + parent.syncErrorValue = err; + parent.syncErrorThrown = true; + return true; + } + else { + hostReportError(err); + return true; + } + } + return false; + }; + SafeSubscriber.prototype._unsubscribe = function () { + var _parentSubscriber = this._parentSubscriber; + this._context = null; + this._parentSubscriber = null; + _parentSubscriber.unsubscribe(); + }; + return SafeSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START _Subscriber PURE_IMPORTS_END */ + function canReportError(observer) { + while (observer) { + var _a = observer, closed_1 = _a.closed, destination = _a.destination, isStopped = _a.isStopped; + if (closed_1 || isStopped) { + return false; + } + else if (destination && destination instanceof Subscriber) { + observer = destination; + } + else { + observer = null; + } + } + return true; + } + + /** PURE_IMPORTS_START _Subscriber,_symbol_rxSubscriber,_Observer PURE_IMPORTS_END */ + function toSubscriber(nextOrObserver, error, complete) { + if (nextOrObserver) { + if (nextOrObserver instanceof Subscriber) { + return nextOrObserver; + } + if (nextOrObserver[rxSubscriber]) { + return nextOrObserver[rxSubscriber](); + } + } + if (!nextOrObserver && !error && !complete) { + return new Subscriber(empty$1); + } + return new Subscriber(nextOrObserver, error, complete); + } + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + var observable = /*@__PURE__*/ (function () { return typeof Symbol === 'function' && Symbol.observable || '@@observable'; })(); + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + function identity(x) { + return x; + } + + /** PURE_IMPORTS_START _identity PURE_IMPORTS_END */ + function pipe() { + var fns = []; + for (var _i = 0; _i < arguments.length; _i++) { + fns[_i] = arguments[_i]; + } + return pipeFromArray(fns); + } + function pipeFromArray(fns) { + if (fns.length === 0) { + return identity; + } + if (fns.length === 1) { + return fns[0]; + } + return function piped(input) { + return fns.reduce(function (prev, fn) { return fn(prev); }, input); + }; + } + + /** PURE_IMPORTS_START _util_canReportError,_util_toSubscriber,_symbol_observable,_util_pipe,_config PURE_IMPORTS_END */ + var Observable = /*@__PURE__*/ (function () { + function Observable(subscribe) { + this._isScalar = false; + if (subscribe) { + this._subscribe = subscribe; + } + } + Observable.prototype.lift = function (operator) { + var observable = new Observable(); + observable.source = this; + observable.operator = operator; + return observable; + }; + Observable.prototype.subscribe = function (observerOrNext, error, complete) { + var operator = this.operator; + var sink = toSubscriber(observerOrNext, error, complete); + if (operator) { + sink.add(operator.call(sink, this.source)); + } + else { + sink.add(this.source || (config.useDeprecatedSynchronousErrorHandling && !sink.syncErrorThrowable) ? + this._subscribe(sink) : + this._trySubscribe(sink)); + } + if (config.useDeprecatedSynchronousErrorHandling) { + if (sink.syncErrorThrowable) { + sink.syncErrorThrowable = false; + if (sink.syncErrorThrown) { + throw sink.syncErrorValue; + } + } + } + return sink; + }; + Observable.prototype._trySubscribe = function (sink) { + try { + return this._subscribe(sink); + } + catch (err) { + if (config.useDeprecatedSynchronousErrorHandling) { + sink.syncErrorThrown = true; + sink.syncErrorValue = err; + } + if (canReportError(sink)) { + sink.error(err); + } + else { + console.warn(err); + } + } + }; + Observable.prototype.forEach = function (next, promiseCtor) { + var _this = this; + promiseCtor = getPromiseCtor(promiseCtor); + return new promiseCtor(function (resolve, reject) { + var subscription; + subscription = _this.subscribe(function (value) { + try { + next(value); + } + catch (err) { + reject(err); + if (subscription) { + subscription.unsubscribe(); + } + } + }, reject, resolve); + }); + }; + Observable.prototype._subscribe = function (subscriber) { + var source = this.source; + return source && source.subscribe(subscriber); + }; + Observable.prototype[observable] = function () { + return this; + }; + Observable.prototype.pipe = function () { + var operations = []; + for (var _i = 0; _i < arguments.length; _i++) { + operations[_i] = arguments[_i]; + } + if (operations.length === 0) { + return this; + } + return pipeFromArray(operations)(this); + }; + Observable.prototype.toPromise = function (promiseCtor) { + var _this = this; + promiseCtor = getPromiseCtor(promiseCtor); + return new promiseCtor(function (resolve, reject) { + var value; + _this.subscribe(function (x) { return value = x; }, function (err) { return reject(err); }, function () { return resolve(value); }); + }); + }; + Observable.create = function (subscribe) { + return new Observable(subscribe); + }; + return Observable; + }()); + function getPromiseCtor(promiseCtor) { + if (!promiseCtor) { + promiseCtor = config.Promise || Promise; + } + if (!promiseCtor) { + throw new Error('no Promise impl found'); + } + return promiseCtor; + } + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + var ObjectUnsubscribedErrorImpl = /*@__PURE__*/ (function () { + function ObjectUnsubscribedErrorImpl() { + Error.call(this); + this.message = 'object unsubscribed'; + this.name = 'ObjectUnsubscribedError'; + return this; + } + ObjectUnsubscribedErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); + return ObjectUnsubscribedErrorImpl; + })(); + var ObjectUnsubscribedError = ObjectUnsubscribedErrorImpl; + + /** PURE_IMPORTS_START tslib,_Subscription PURE_IMPORTS_END */ + var SubjectSubscription = /*@__PURE__*/ (function (_super) { + __extends(SubjectSubscription, _super); + function SubjectSubscription(subject, subscriber) { + var _this = _super.call(this) || this; + _this.subject = subject; + _this.subscriber = subscriber; + _this.closed = false; + return _this; + } + SubjectSubscription.prototype.unsubscribe = function () { + if (this.closed) { + return; + } + this.closed = true; + var subject = this.subject; + var observers = subject.observers; + this.subject = null; + if (!observers || observers.length === 0 || subject.isStopped || subject.closed) { + return; + } + var subscriberIndex = observers.indexOf(this.subscriber); + if (subscriberIndex !== -1) { + observers.splice(subscriberIndex, 1); + } + }; + return SubjectSubscription; + }(Subscription)); + + /** PURE_IMPORTS_START tslib,_Observable,_Subscriber,_Subscription,_util_ObjectUnsubscribedError,_SubjectSubscription,_internal_symbol_rxSubscriber PURE_IMPORTS_END */ + var SubjectSubscriber = /*@__PURE__*/ (function (_super) { + __extends(SubjectSubscriber, _super); + function SubjectSubscriber(destination) { + var _this = _super.call(this, destination) || this; + _this.destination = destination; + return _this; + } + return SubjectSubscriber; + }(Subscriber)); + var Subject = /*@__PURE__*/ (function (_super) { + __extends(Subject, _super); + function Subject() { + var _this = _super.call(this) || this; + _this.observers = []; + _this.closed = false; + _this.isStopped = false; + _this.hasError = false; + _this.thrownError = null; + return _this; + } + Subject.prototype[rxSubscriber] = function () { + return new SubjectSubscriber(this); + }; + Subject.prototype.lift = function (operator) { + var subject = new AnonymousSubject(this, this); + subject.operator = operator; + return subject; + }; + Subject.prototype.next = function (value) { + if (this.closed) { + throw new ObjectUnsubscribedError(); + } + if (!this.isStopped) { + var observers = this.observers; + var len = observers.length; + var copy = observers.slice(); + for (var i = 0; i < len; i++) { + copy[i].next(value); + } + } + }; + Subject.prototype.error = function (err) { + if (this.closed) { + throw new ObjectUnsubscribedError(); + } + this.hasError = true; + this.thrownError = err; + this.isStopped = true; + var observers = this.observers; + var len = observers.length; + var copy = observers.slice(); + for (var i = 0; i < len; i++) { + copy[i].error(err); + } + this.observers.length = 0; + }; + Subject.prototype.complete = function () { + if (this.closed) { + throw new ObjectUnsubscribedError(); + } + this.isStopped = true; + var observers = this.observers; + var len = observers.length; + var copy = observers.slice(); + for (var i = 0; i < len; i++) { + copy[i].complete(); + } + this.observers.length = 0; + }; + Subject.prototype.unsubscribe = function () { + this.isStopped = true; + this.closed = true; + this.observers = null; + }; + Subject.prototype._trySubscribe = function (subscriber) { + if (this.closed) { + throw new ObjectUnsubscribedError(); + } + else { + return _super.prototype._trySubscribe.call(this, subscriber); + } + }; + Subject.prototype._subscribe = function (subscriber) { + if (this.closed) { + throw new ObjectUnsubscribedError(); + } + else if (this.hasError) { + subscriber.error(this.thrownError); + return Subscription.EMPTY; + } + else if (this.isStopped) { + subscriber.complete(); + return Subscription.EMPTY; + } + else { + this.observers.push(subscriber); + return new SubjectSubscription(this, subscriber); + } + }; + Subject.prototype.asObservable = function () { + var observable = new Observable(); + observable.source = this; + return observable; + }; + Subject.create = function (destination, source) { + return new AnonymousSubject(destination, source); + }; + return Subject; + }(Observable)); + var AnonymousSubject = /*@__PURE__*/ (function (_super) { + __extends(AnonymousSubject, _super); + function AnonymousSubject(destination, source) { + var _this = _super.call(this) || this; + _this.destination = destination; + _this.source = source; + return _this; + } + AnonymousSubject.prototype.next = function (value) { + var destination = this.destination; + if (destination && destination.next) { + destination.next(value); + } + }; + AnonymousSubject.prototype.error = function (err) { + var destination = this.destination; + if (destination && destination.error) { + this.destination.error(err); + } + }; + AnonymousSubject.prototype.complete = function () { + var destination = this.destination; + if (destination && destination.complete) { + this.destination.complete(); + } + }; + AnonymousSubject.prototype._subscribe = function (subscriber) { + var source = this.source; + if (source) { + return this.source.subscribe(subscriber); + } + else { + return Subscription.EMPTY; + } + }; + return AnonymousSubject; + }(Subject)); + + /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + function refCount() { + return function refCountOperatorFunction(source) { + return source.lift(new RefCountOperator(source)); + }; + } + var RefCountOperator = /*@__PURE__*/ (function () { + function RefCountOperator(connectable) { + this.connectable = connectable; + } + RefCountOperator.prototype.call = function (subscriber, source) { + var connectable = this.connectable; + connectable._refCount++; + var refCounter = new RefCountSubscriber(subscriber, connectable); + var subscription = source.subscribe(refCounter); + if (!refCounter.closed) { + refCounter.connection = connectable.connect(); + } + return subscription; + }; + return RefCountOperator; + }()); + var RefCountSubscriber = /*@__PURE__*/ (function (_super) { + __extends(RefCountSubscriber, _super); + function RefCountSubscriber(destination, connectable) { + var _this = _super.call(this, destination) || this; + _this.connectable = connectable; + return _this; + } + RefCountSubscriber.prototype._unsubscribe = function () { + var connectable = this.connectable; + if (!connectable) { + this.connection = null; + return; + } + this.connectable = null; + var refCount = connectable._refCount; + if (refCount <= 0) { + this.connection = null; + return; + } + connectable._refCount = refCount - 1; + if (refCount > 1) { + this.connection = null; + return; + } + var connection = this.connection; + var sharedConnection = connectable._connection; + this.connection = null; + if (sharedConnection && (!connection || sharedConnection === connection)) { + sharedConnection.unsubscribe(); + } + }; + return RefCountSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_Subject,_Observable,_Subscriber,_Subscription,_operators_refCount PURE_IMPORTS_END */ + var ConnectableObservable = /*@__PURE__*/ (function (_super) { + __extends(ConnectableObservable, _super); + function ConnectableObservable(source, subjectFactory) { + var _this = _super.call(this) || this; + _this.source = source; + _this.subjectFactory = subjectFactory; + _this._refCount = 0; + _this._isComplete = false; + return _this; + } + ConnectableObservable.prototype._subscribe = function (subscriber) { + return this.getSubject().subscribe(subscriber); + }; + ConnectableObservable.prototype.getSubject = function () { + var subject = this._subject; + if (!subject || subject.isStopped) { + this._subject = this.subjectFactory(); + } + return this._subject; + }; + ConnectableObservable.prototype.connect = function () { + var connection = this._connection; + if (!connection) { + this._isComplete = false; + connection = this._connection = new Subscription(); + connection.add(this.source + .subscribe(new ConnectableSubscriber(this.getSubject(), this))); + if (connection.closed) { + this._connection = null; + connection = Subscription.EMPTY; + } + } + return connection; + }; + ConnectableObservable.prototype.refCount = function () { + return refCount()(this); + }; + return ConnectableObservable; + }(Observable)); + var connectableObservableDescriptor = /*@__PURE__*/ (function () { + var connectableProto = ConnectableObservable.prototype; + return { + operator: { value: null }, + _refCount: { value: 0, writable: true }, + _subject: { value: null, writable: true }, + _connection: { value: null, writable: true }, + _subscribe: { value: connectableProto._subscribe }, + _isComplete: { value: connectableProto._isComplete, writable: true }, + getSubject: { value: connectableProto.getSubject }, + connect: { value: connectableProto.connect }, + refCount: { value: connectableProto.refCount } + }; + })(); + var ConnectableSubscriber = /*@__PURE__*/ (function (_super) { + __extends(ConnectableSubscriber, _super); + function ConnectableSubscriber(destination, connectable) { + var _this = _super.call(this, destination) || this; + _this.connectable = connectable; + return _this; + } + ConnectableSubscriber.prototype._error = function (err) { + this._unsubscribe(); + _super.prototype._error.call(this, err); + }; + ConnectableSubscriber.prototype._complete = function () { + this.connectable._isComplete = true; + this._unsubscribe(); + _super.prototype._complete.call(this); + }; + ConnectableSubscriber.prototype._unsubscribe = function () { + var connectable = this.connectable; + if (connectable) { + this.connectable = null; + var connection = connectable._connection; + connectable._refCount = 0; + connectable._subject = null; + connectable._connection = null; + if (connection) { + connection.unsubscribe(); + } + } + }; + return ConnectableSubscriber; + }(SubjectSubscriber)); + + /** PURE_IMPORTS_START tslib,_Subject,_util_ObjectUnsubscribedError PURE_IMPORTS_END */ + var BehaviorSubject = /*@__PURE__*/ (function (_super) { + __extends(BehaviorSubject, _super); + function BehaviorSubject(_value) { + var _this = _super.call(this) || this; + _this._value = _value; + return _this; + } + Object.defineProperty(BehaviorSubject.prototype, "value", { + get: function () { + return this.getValue(); + }, + enumerable: true, + configurable: true + }); + BehaviorSubject.prototype._subscribe = function (subscriber) { + var subscription = _super.prototype._subscribe.call(this, subscriber); + if (subscription && !subscription.closed) { + subscriber.next(this._value); + } + return subscription; + }; + BehaviorSubject.prototype.getValue = function () { + if (this.hasError) { + throw this.thrownError; + } + else if (this.closed) { + throw new ObjectUnsubscribedError(); + } + else { + return this._value; + } + }; + BehaviorSubject.prototype.next = function (value) { + _super.prototype.next.call(this, this._value = value); + }; + return BehaviorSubject; + }(Subject)); + + /** PURE_IMPORTS_START tslib,_Subscription PURE_IMPORTS_END */ + var Action = /*@__PURE__*/ (function (_super) { + __extends(Action, _super); + function Action(scheduler, work) { + return _super.call(this) || this; + } + Action.prototype.schedule = function (state, delay) { + return this; + }; + return Action; + }(Subscription)); + + /** PURE_IMPORTS_START tslib,_Action PURE_IMPORTS_END */ + var AsyncAction = /*@__PURE__*/ (function (_super) { + __extends(AsyncAction, _super); + function AsyncAction(scheduler, work) { + var _this = _super.call(this, scheduler, work) || this; + _this.scheduler = scheduler; + _this.work = work; + _this.pending = false; + return _this; + } + AsyncAction.prototype.schedule = function (state, delay) { + if (delay === void 0) { + delay = 0; + } + if (this.closed) { + return this; + } + this.state = state; + var id = this.id; + var scheduler = this.scheduler; + if (id != null) { + this.id = this.recycleAsyncId(scheduler, id, delay); + } + this.pending = true; + this.delay = delay; + this.id = this.id || this.requestAsyncId(scheduler, this.id, delay); + return this; + }; + AsyncAction.prototype.requestAsyncId = function (scheduler, id, delay) { + if (delay === void 0) { + delay = 0; + } + return setInterval(scheduler.flush.bind(scheduler, this), delay); + }; + AsyncAction.prototype.recycleAsyncId = function (scheduler, id, delay) { + if (delay === void 0) { + delay = 0; + } + if (delay !== null && this.delay === delay && this.pending === false) { + return id; + } + clearInterval(id); + return undefined; + }; + AsyncAction.prototype.execute = function (state, delay) { + if (this.closed) { + return new Error('executing a cancelled action'); + } + this.pending = false; + var error = this._execute(state, delay); + if (error) { + return error; + } + else if (this.pending === false && this.id != null) { + this.id = this.recycleAsyncId(this.scheduler, this.id, null); + } + }; + AsyncAction.prototype._execute = function (state, delay) { + var errored = false; + var errorValue = undefined; + try { + this.work(state); + } + catch (e) { + errored = true; + errorValue = !!e && e || new Error(e); + } + if (errored) { + this.unsubscribe(); + return errorValue; + } + }; + AsyncAction.prototype._unsubscribe = function () { + var id = this.id; + var scheduler = this.scheduler; + var actions = scheduler.actions; + var index = actions.indexOf(this); + this.work = null; + this.state = null; + this.pending = false; + this.scheduler = null; + if (index !== -1) { + actions.splice(index, 1); + } + if (id != null) { + this.id = this.recycleAsyncId(scheduler, id, null); + } + this.delay = null; + }; + return AsyncAction; + }(Action)); + + /** PURE_IMPORTS_START tslib,_AsyncAction PURE_IMPORTS_END */ + var QueueAction = /*@__PURE__*/ (function (_super) { + __extends(QueueAction, _super); + function QueueAction(scheduler, work) { + var _this = _super.call(this, scheduler, work) || this; + _this.scheduler = scheduler; + _this.work = work; + return _this; + } + QueueAction.prototype.schedule = function (state, delay) { + if (delay === void 0) { + delay = 0; + } + if (delay > 0) { + return _super.prototype.schedule.call(this, state, delay); + } + this.delay = delay; + this.state = state; + this.scheduler.flush(this); + return this; + }; + QueueAction.prototype.execute = function (state, delay) { + return (delay > 0 || this.closed) ? + _super.prototype.execute.call(this, state, delay) : + this._execute(state, delay); + }; + QueueAction.prototype.requestAsyncId = function (scheduler, id, delay) { + if (delay === void 0) { + delay = 0; + } + if ((delay !== null && delay > 0) || (delay === null && this.delay > 0)) { + return _super.prototype.requestAsyncId.call(this, scheduler, id, delay); + } + return scheduler.flush(this); + }; + return QueueAction; + }(AsyncAction)); + + var Scheduler = /*@__PURE__*/ (function () { + function Scheduler(SchedulerAction, now) { + if (now === void 0) { + now = Scheduler.now; + } + this.SchedulerAction = SchedulerAction; + this.now = now; + } + Scheduler.prototype.schedule = function (work, delay, state) { + if (delay === void 0) { + delay = 0; + } + return new this.SchedulerAction(this, work).schedule(state, delay); + }; + Scheduler.now = function () { return Date.now(); }; + return Scheduler; + }()); + + /** PURE_IMPORTS_START tslib,_Scheduler PURE_IMPORTS_END */ + var AsyncScheduler = /*@__PURE__*/ (function (_super) { + __extends(AsyncScheduler, _super); + function AsyncScheduler(SchedulerAction, now) { + if (now === void 0) { + now = Scheduler.now; + } + var _this = _super.call(this, SchedulerAction, function () { + if (AsyncScheduler.delegate && AsyncScheduler.delegate !== _this) { + return AsyncScheduler.delegate.now(); + } + else { + return now(); + } + }) || this; + _this.actions = []; + _this.active = false; + _this.scheduled = undefined; + return _this; + } + AsyncScheduler.prototype.schedule = function (work, delay, state) { + if (delay === void 0) { + delay = 0; + } + if (AsyncScheduler.delegate && AsyncScheduler.delegate !== this) { + return AsyncScheduler.delegate.schedule(work, delay, state); + } + else { + return _super.prototype.schedule.call(this, work, delay, state); + } + }; + AsyncScheduler.prototype.flush = function (action) { + var actions = this.actions; + if (this.active) { + actions.push(action); + return; + } + var error; + this.active = true; + do { + if (error = action.execute(action.state, action.delay)) { + break; + } + } while (action = actions.shift()); + this.active = false; + if (error) { + while (action = actions.shift()) { + action.unsubscribe(); + } + throw error; + } + }; + return AsyncScheduler; + }(Scheduler)); + + /** PURE_IMPORTS_START tslib,_AsyncScheduler PURE_IMPORTS_END */ + var QueueScheduler = /*@__PURE__*/ (function (_super) { + __extends(QueueScheduler, _super); + function QueueScheduler() { + return _super !== null && _super.apply(this, arguments) || this; + } + return QueueScheduler; + }(AsyncScheduler)); + + /** PURE_IMPORTS_START _QueueAction,_QueueScheduler PURE_IMPORTS_END */ + var queueScheduler = /*@__PURE__*/ new QueueScheduler(QueueAction); + var queue = queueScheduler; + + /** PURE_IMPORTS_START _Observable PURE_IMPORTS_END */ + var EMPTY = /*@__PURE__*/ new Observable(function (subscriber) { return subscriber.complete(); }); + function empty(scheduler) { + return scheduler ? emptyScheduled(scheduler) : EMPTY; + } + function emptyScheduled(scheduler) { + return new Observable(function (subscriber) { return scheduler.schedule(function () { return subscriber.complete(); }); }); + } + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + function isScheduler(value) { + return value && typeof value.schedule === 'function'; + } + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + var subscribeToArray = function (array) { + return function (subscriber) { + for (var i = 0, len = array.length; i < len && !subscriber.closed; i++) { + subscriber.next(array[i]); + } + subscriber.complete(); + }; + }; + + /** PURE_IMPORTS_START _Observable,_Subscription PURE_IMPORTS_END */ + function scheduleArray(input, scheduler) { + return new Observable(function (subscriber) { + var sub = new Subscription(); + var i = 0; + sub.add(scheduler.schedule(function () { + if (i === input.length) { + subscriber.complete(); + return; + } + subscriber.next(input[i++]); + if (!subscriber.closed) { + sub.add(this.schedule()); + } + })); + return sub; + }); + } + + /** PURE_IMPORTS_START _Observable,_util_subscribeToArray,_scheduled_scheduleArray PURE_IMPORTS_END */ + function fromArray(input, scheduler) { + if (!scheduler) { + return new Observable(subscribeToArray(input)); + } + else { + return scheduleArray(input, scheduler); + } + } + + /** PURE_IMPORTS_START _util_isScheduler,_fromArray,_scheduled_scheduleArray PURE_IMPORTS_END */ + function of() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + var scheduler = args[args.length - 1]; + if (isScheduler(scheduler)) { + args.pop(); + return scheduleArray(args, scheduler); + } + else { + return fromArray(args); + } + } + + /** PURE_IMPORTS_START _Observable PURE_IMPORTS_END */ + function throwError(error, scheduler) { + if (!scheduler) { + return new Observable(function (subscriber) { return subscriber.error(error); }); + } + else { + return new Observable(function (subscriber) { return scheduler.schedule(dispatch$1, 0, { error: error, subscriber: subscriber }); }); + } + } + function dispatch$1(_a) { + var error = _a.error, subscriber = _a.subscriber; + subscriber.error(error); + } + + /** PURE_IMPORTS_START _observable_empty,_observable_of,_observable_throwError PURE_IMPORTS_END */ + var Notification = /*@__PURE__*/ (function () { + function Notification(kind, value, error) { + this.kind = kind; + this.value = value; + this.error = error; + this.hasValue = kind === 'N'; + } + Notification.prototype.observe = function (observer) { + switch (this.kind) { + case 'N': + return observer.next && observer.next(this.value); + case 'E': + return observer.error && observer.error(this.error); + case 'C': + return observer.complete && observer.complete(); + } + }; + Notification.prototype.do = function (next, error, complete) { + var kind = this.kind; + switch (kind) { + case 'N': + return next && next(this.value); + case 'E': + return error && error(this.error); + case 'C': + return complete && complete(); + } + }; + Notification.prototype.accept = function (nextOrObserver, error, complete) { + if (nextOrObserver && typeof nextOrObserver.next === 'function') { + return this.observe(nextOrObserver); + } + else { + return this.do(nextOrObserver, error, complete); + } + }; + Notification.prototype.toObservable = function () { + var kind = this.kind; + switch (kind) { + case 'N': + return of(this.value); + case 'E': + return throwError(this.error); + case 'C': + return empty(); + } + throw new Error('unexpected notification kind value'); + }; + Notification.createNext = function (value) { + if (typeof value !== 'undefined') { + return new Notification('N', value); + } + return Notification.undefinedValueNotification; + }; + Notification.createError = function (err) { + return new Notification('E', undefined, err); + }; + Notification.createComplete = function () { + return Notification.completeNotification; + }; + Notification.completeNotification = new Notification('C'); + Notification.undefinedValueNotification = new Notification('N', undefined); + return Notification; + }()); + + /** PURE_IMPORTS_START tslib,_Subscriber,_Notification PURE_IMPORTS_END */ + function observeOn(scheduler, delay) { + if (delay === void 0) { + delay = 0; + } + return function observeOnOperatorFunction(source) { + return source.lift(new ObserveOnOperator(scheduler, delay)); + }; + } + var ObserveOnOperator = /*@__PURE__*/ (function () { + function ObserveOnOperator(scheduler, delay) { + if (delay === void 0) { + delay = 0; + } + this.scheduler = scheduler; + this.delay = delay; + } + ObserveOnOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ObserveOnSubscriber(subscriber, this.scheduler, this.delay)); + }; + return ObserveOnOperator; + }()); + var ObserveOnSubscriber = /*@__PURE__*/ (function (_super) { + __extends(ObserveOnSubscriber, _super); + function ObserveOnSubscriber(destination, scheduler, delay) { + if (delay === void 0) { + delay = 0; + } + var _this = _super.call(this, destination) || this; + _this.scheduler = scheduler; + _this.delay = delay; + return _this; + } + ObserveOnSubscriber.dispatch = function (arg) { + var notification = arg.notification, destination = arg.destination; + notification.observe(destination); + this.unsubscribe(); + }; + ObserveOnSubscriber.prototype.scheduleMessage = function (notification) { + var destination = this.destination; + destination.add(this.scheduler.schedule(ObserveOnSubscriber.dispatch, this.delay, new ObserveOnMessage(notification, this.destination))); + }; + ObserveOnSubscriber.prototype._next = function (value) { + this.scheduleMessage(Notification.createNext(value)); + }; + ObserveOnSubscriber.prototype._error = function (err) { + this.scheduleMessage(Notification.createError(err)); + this.unsubscribe(); + }; + ObserveOnSubscriber.prototype._complete = function () { + this.scheduleMessage(Notification.createComplete()); + this.unsubscribe(); + }; + return ObserveOnSubscriber; + }(Subscriber)); + var ObserveOnMessage = /*@__PURE__*/ (function () { + function ObserveOnMessage(notification, destination) { + this.notification = notification; + this.destination = destination; + } + return ObserveOnMessage; + }()); + + /** PURE_IMPORTS_START tslib,_Subject,_scheduler_queue,_Subscription,_operators_observeOn,_util_ObjectUnsubscribedError,_SubjectSubscription PURE_IMPORTS_END */ + var ReplaySubject = /*@__PURE__*/ (function (_super) { + __extends(ReplaySubject, _super); + function ReplaySubject(bufferSize, windowTime, scheduler) { + if (bufferSize === void 0) { + bufferSize = Number.POSITIVE_INFINITY; + } + if (windowTime === void 0) { + windowTime = Number.POSITIVE_INFINITY; + } + var _this = _super.call(this) || this; + _this.scheduler = scheduler; + _this._events = []; + _this._infiniteTimeWindow = false; + _this._bufferSize = bufferSize < 1 ? 1 : bufferSize; + _this._windowTime = windowTime < 1 ? 1 : windowTime; + if (windowTime === Number.POSITIVE_INFINITY) { + _this._infiniteTimeWindow = true; + _this.next = _this.nextInfiniteTimeWindow; + } + else { + _this.next = _this.nextTimeWindow; + } + return _this; + } + ReplaySubject.prototype.nextInfiniteTimeWindow = function (value) { + if (!this.isStopped) { + var _events = this._events; + _events.push(value); + if (_events.length > this._bufferSize) { + _events.shift(); + } + } + _super.prototype.next.call(this, value); + }; + ReplaySubject.prototype.nextTimeWindow = function (value) { + if (!this.isStopped) { + this._events.push(new ReplayEvent(this._getNow(), value)); + this._trimBufferThenGetEvents(); + } + _super.prototype.next.call(this, value); + }; + ReplaySubject.prototype._subscribe = function (subscriber) { + var _infiniteTimeWindow = this._infiniteTimeWindow; + var _events = _infiniteTimeWindow ? this._events : this._trimBufferThenGetEvents(); + var scheduler = this.scheduler; + var len = _events.length; + var subscription; + if (this.closed) { + throw new ObjectUnsubscribedError(); + } + else if (this.isStopped || this.hasError) { + subscription = Subscription.EMPTY; + } + else { + this.observers.push(subscriber); + subscription = new SubjectSubscription(this, subscriber); + } + if (scheduler) { + subscriber.add(subscriber = new ObserveOnSubscriber(subscriber, scheduler)); + } + if (_infiniteTimeWindow) { + for (var i = 0; i < len && !subscriber.closed; i++) { + subscriber.next(_events[i]); + } + } + else { + for (var i = 0; i < len && !subscriber.closed; i++) { + subscriber.next(_events[i].value); + } + } + if (this.hasError) { + subscriber.error(this.thrownError); + } + else if (this.isStopped) { + subscriber.complete(); + } + return subscription; + }; + ReplaySubject.prototype._getNow = function () { + return (this.scheduler || queue).now(); + }; + ReplaySubject.prototype._trimBufferThenGetEvents = function () { + var now = this._getNow(); + var _bufferSize = this._bufferSize; + var _windowTime = this._windowTime; + var _events = this._events; + var eventsCount = _events.length; + var spliceCount = 0; + while (spliceCount < eventsCount) { + if ((now - _events[spliceCount].time) < _windowTime) { + break; + } + spliceCount++; + } + if (eventsCount > _bufferSize) { + spliceCount = Math.max(spliceCount, eventsCount - _bufferSize); + } + if (spliceCount > 0) { + _events.splice(0, spliceCount); + } + return _events; + }; + return ReplaySubject; + }(Subject)); + var ReplayEvent = /*@__PURE__*/ (function () { + function ReplayEvent(time, value) { + this.time = time; + this.value = value; + } + return ReplayEvent; + }()); + + /** PURE_IMPORTS_START tslib,_Subject,_Subscription PURE_IMPORTS_END */ + var AsyncSubject = /*@__PURE__*/ (function (_super) { + __extends(AsyncSubject, _super); + function AsyncSubject() { + var _this = _super !== null && _super.apply(this, arguments) || this; + _this.value = null; + _this.hasNext = false; + _this.hasCompleted = false; + return _this; + } + AsyncSubject.prototype._subscribe = function (subscriber) { + if (this.hasError) { + subscriber.error(this.thrownError); + return Subscription.EMPTY; + } + else if (this.hasCompleted && this.hasNext) { + subscriber.next(this.value); + subscriber.complete(); + return Subscription.EMPTY; + } + return _super.prototype._subscribe.call(this, subscriber); + }; + AsyncSubject.prototype.next = function (value) { + if (!this.hasCompleted) { + this.value = value; + this.hasNext = true; + } + }; + AsyncSubject.prototype.error = function (error) { + if (!this.hasCompleted) { + _super.prototype.error.call(this, error); + } + }; + AsyncSubject.prototype.complete = function () { + this.hasCompleted = true; + if (this.hasNext) { + _super.prototype.next.call(this, this.value); + } + _super.prototype.complete.call(this); + }; + return AsyncSubject; + }(Subject)); + + /** PURE_IMPORTS_START _AsyncAction,_AsyncScheduler PURE_IMPORTS_END */ + var asyncScheduler = /*@__PURE__*/ new AsyncScheduler(AsyncAction); + var async = asyncScheduler; + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + function noop() { } + + /** PURE_IMPORTS_START _Observable PURE_IMPORTS_END */ + function isObservable(obj) { + return !!obj && (obj instanceof Observable || (typeof obj.lift === 'function' && typeof obj.subscribe === 'function')); + } + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + var ArgumentOutOfRangeErrorImpl = /*@__PURE__*/ (function () { + function ArgumentOutOfRangeErrorImpl() { + Error.call(this); + this.message = 'argument out of range'; + this.name = 'ArgumentOutOfRangeError'; + return this; + } + ArgumentOutOfRangeErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); + return ArgumentOutOfRangeErrorImpl; + })(); + var ArgumentOutOfRangeError = ArgumentOutOfRangeErrorImpl; + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + var TimeoutErrorImpl = /*@__PURE__*/ (function () { + function TimeoutErrorImpl() { + Error.call(this); + this.message = 'Timeout has occurred'; + this.name = 'TimeoutError'; + return this; + } + TimeoutErrorImpl.prototype = /*@__PURE__*/ Object.create(Error.prototype); + return TimeoutErrorImpl; + })(); + var TimeoutError = TimeoutErrorImpl; + + /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + function map(project, thisArg) { + return function mapOperation(source) { + if (typeof project !== 'function') { + throw new TypeError('argument is not a function. Are you looking for `mapTo()`?'); + } + return source.lift(new MapOperator(project, thisArg)); + }; + } + var MapOperator = /*@__PURE__*/ (function () { + function MapOperator(project, thisArg) { + this.project = project; + this.thisArg = thisArg; + } + MapOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new MapSubscriber(subscriber, this.project, this.thisArg)); + }; + return MapOperator; + }()); + var MapSubscriber = /*@__PURE__*/ (function (_super) { + __extends(MapSubscriber, _super); + function MapSubscriber(destination, project, thisArg) { + var _this = _super.call(this, destination) || this; + _this.project = project; + _this.count = 0; + _this.thisArg = thisArg || _this; + return _this; + } + MapSubscriber.prototype._next = function (value) { + var result; + try { + result = this.project.call(this.thisArg, value, this.count++); + } + catch (err) { + this.destination.error(err); + return; + } + this.destination.next(result); + }; + return MapSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + var OuterSubscriber = /*@__PURE__*/ (function (_super) { + __extends(OuterSubscriber, _super); + function OuterSubscriber() { + return _super !== null && _super.apply(this, arguments) || this; + } + OuterSubscriber.prototype.notifyNext = function (outerValue, innerValue, outerIndex, innerIndex, innerSub) { + this.destination.next(innerValue); + }; + OuterSubscriber.prototype.notifyError = function (error, innerSub) { + this.destination.error(error); + }; + OuterSubscriber.prototype.notifyComplete = function (innerSub) { + this.destination.complete(); + }; + return OuterSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + var InnerSubscriber = /*@__PURE__*/ (function (_super) { + __extends(InnerSubscriber, _super); + function InnerSubscriber(parent, outerValue, outerIndex) { + var _this = _super.call(this) || this; + _this.parent = parent; + _this.outerValue = outerValue; + _this.outerIndex = outerIndex; + _this.index = 0; + return _this; + } + InnerSubscriber.prototype._next = function (value) { + this.parent.notifyNext(this.outerValue, value, this.outerIndex, this.index++, this); + }; + InnerSubscriber.prototype._error = function (error) { + this.parent.notifyError(error, this); + this.unsubscribe(); + }; + InnerSubscriber.prototype._complete = function () { + this.parent.notifyComplete(this); + this.unsubscribe(); + }; + return InnerSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START _hostReportError PURE_IMPORTS_END */ + var subscribeToPromise = function (promise) { + return function (subscriber) { + promise.then(function (value) { + if (!subscriber.closed) { + subscriber.next(value); + subscriber.complete(); + } + }, function (err) { return subscriber.error(err); }) + .then(null, hostReportError); + return subscriber; + }; + }; + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + function getSymbolIterator() { + if (typeof Symbol !== 'function' || !Symbol.iterator) { + return '@@iterator'; + } + return Symbol.iterator; + } + var iterator = /*@__PURE__*/ getSymbolIterator(); + + /** PURE_IMPORTS_START _symbol_iterator PURE_IMPORTS_END */ + var subscribeToIterable = function (iterable) { + return function (subscriber) { + var iterator$1 = iterable[iterator](); + do { + var item = void 0; + try { + item = iterator$1.next(); + } + catch (err) { + subscriber.error(err); + return subscriber; + } + if (item.done) { + subscriber.complete(); + break; + } + subscriber.next(item.value); + if (subscriber.closed) { + break; + } + } while (true); + if (typeof iterator$1.return === 'function') { + subscriber.add(function () { + if (iterator$1.return) { + iterator$1.return(); + } + }); + } + return subscriber; + }; + }; + + /** PURE_IMPORTS_START _symbol_observable PURE_IMPORTS_END */ + var subscribeToObservable = function (obj) { + return function (subscriber) { + var obs = obj[observable](); + if (typeof obs.subscribe !== 'function') { + throw new TypeError('Provided object does not correctly implement Symbol.observable'); + } + else { + return obs.subscribe(subscriber); + } + }; + }; + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + var isArrayLike = (function (x) { return x && typeof x.length === 'number' && typeof x !== 'function'; }); + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + function isPromise(value) { + return !!value && typeof value.subscribe !== 'function' && typeof value.then === 'function'; + } + + /** PURE_IMPORTS_START _subscribeToArray,_subscribeToPromise,_subscribeToIterable,_subscribeToObservable,_isArrayLike,_isPromise,_isObject,_symbol_iterator,_symbol_observable PURE_IMPORTS_END */ + var subscribeTo = function (result) { + if (!!result && typeof result[observable] === 'function') { + return subscribeToObservable(result); + } + else if (isArrayLike(result)) { + return subscribeToArray(result); + } + else if (isPromise(result)) { + return subscribeToPromise(result); + } + else if (!!result && typeof result[iterator] === 'function') { + return subscribeToIterable(result); + } + else { + var value = isObject$1(result) ? 'an invalid object' : "'" + result + "'"; + var msg = "You provided " + value + " where a stream was expected." + + ' You can provide an Observable, Promise, Array, or Iterable.'; + throw new TypeError(msg); + } + }; + + /** PURE_IMPORTS_START _InnerSubscriber,_subscribeTo,_Observable PURE_IMPORTS_END */ + function subscribeToResult(outerSubscriber, result, outerValue, outerIndex, innerSubscriber) { + if (innerSubscriber === void 0) { + innerSubscriber = new InnerSubscriber(outerSubscriber, outerValue, outerIndex); + } + if (innerSubscriber.closed) { + return undefined; + } + if (result instanceof Observable) { + return result.subscribe(innerSubscriber); + } + return subscribeTo(result)(innerSubscriber); + } + + /** PURE_IMPORTS_START tslib,_util_isScheduler,_util_isArray,_OuterSubscriber,_util_subscribeToResult,_fromArray PURE_IMPORTS_END */ + var NONE = {}; + function combineLatest() { + var observables = []; + for (var _i = 0; _i < arguments.length; _i++) { + observables[_i] = arguments[_i]; + } + var resultSelector = undefined; + var scheduler = undefined; + if (isScheduler(observables[observables.length - 1])) { + scheduler = observables.pop(); + } + if (typeof observables[observables.length - 1] === 'function') { + resultSelector = observables.pop(); + } + if (observables.length === 1 && isArray$1(observables[0])) { + observables = observables[0]; + } + return fromArray(observables, scheduler).lift(new CombineLatestOperator(resultSelector)); + } + var CombineLatestOperator = /*@__PURE__*/ (function () { + function CombineLatestOperator(resultSelector) { + this.resultSelector = resultSelector; + } + CombineLatestOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new CombineLatestSubscriber(subscriber, this.resultSelector)); + }; + return CombineLatestOperator; + }()); + var CombineLatestSubscriber = /*@__PURE__*/ (function (_super) { + __extends(CombineLatestSubscriber, _super); + function CombineLatestSubscriber(destination, resultSelector) { + var _this = _super.call(this, destination) || this; + _this.resultSelector = resultSelector; + _this.active = 0; + _this.values = []; + _this.observables = []; + return _this; + } + CombineLatestSubscriber.prototype._next = function (observable) { + this.values.push(NONE); + this.observables.push(observable); + }; + CombineLatestSubscriber.prototype._complete = function () { + var observables = this.observables; + var len = observables.length; + if (len === 0) { + this.destination.complete(); + } + else { + this.active = len; + this.toRespond = len; + for (var i = 0; i < len; i++) { + var observable = observables[i]; + this.add(subscribeToResult(this, observable, undefined, i)); + } + } + }; + CombineLatestSubscriber.prototype.notifyComplete = function (unused) { + if ((this.active -= 1) === 0) { + this.destination.complete(); + } + }; + CombineLatestSubscriber.prototype.notifyNext = function (_outerValue, innerValue, outerIndex) { + var values = this.values; + var oldVal = values[outerIndex]; + var toRespond = !this.toRespond + ? 0 + : oldVal === NONE ? --this.toRespond : this.toRespond; + values[outerIndex] = innerValue; + if (toRespond === 0) { + if (this.resultSelector) { + this._tryResultSelector(values); + } + else { + this.destination.next(values.slice()); + } + } + }; + CombineLatestSubscriber.prototype._tryResultSelector = function (values) { + var result; + try { + result = this.resultSelector.apply(this, values); + } + catch (err) { + this.destination.error(err); + return; + } + this.destination.next(result); + }; + return CombineLatestSubscriber; + }(OuterSubscriber)); + + /** PURE_IMPORTS_START _Observable,_Subscription,_symbol_observable PURE_IMPORTS_END */ + function scheduleObservable(input, scheduler) { + return new Observable(function (subscriber) { + var sub = new Subscription(); + sub.add(scheduler.schedule(function () { + var observable$1 = input[observable](); + sub.add(observable$1.subscribe({ + next: function (value) { sub.add(scheduler.schedule(function () { return subscriber.next(value); })); }, + error: function (err) { sub.add(scheduler.schedule(function () { return subscriber.error(err); })); }, + complete: function () { sub.add(scheduler.schedule(function () { return subscriber.complete(); })); }, + })); + })); + return sub; + }); + } + + /** PURE_IMPORTS_START _Observable,_Subscription PURE_IMPORTS_END */ + function schedulePromise(input, scheduler) { + return new Observable(function (subscriber) { + var sub = new Subscription(); + sub.add(scheduler.schedule(function () { + return input.then(function (value) { + sub.add(scheduler.schedule(function () { + subscriber.next(value); + sub.add(scheduler.schedule(function () { return subscriber.complete(); })); + })); + }, function (err) { + sub.add(scheduler.schedule(function () { return subscriber.error(err); })); + }); + })); + return sub; + }); + } + + /** PURE_IMPORTS_START _Observable,_Subscription,_symbol_iterator PURE_IMPORTS_END */ + function scheduleIterable(input, scheduler) { + if (!input) { + throw new Error('Iterable cannot be null'); + } + return new Observable(function (subscriber) { + var sub = new Subscription(); + var iterator$1; + sub.add(function () { + if (iterator$1 && typeof iterator$1.return === 'function') { + iterator$1.return(); + } + }); + sub.add(scheduler.schedule(function () { + iterator$1 = input[iterator](); + sub.add(scheduler.schedule(function () { + if (subscriber.closed) { + return; + } + var value; + var done; + try { + var result = iterator$1.next(); + value = result.value; + done = result.done; + } + catch (err) { + subscriber.error(err); + return; + } + if (done) { + subscriber.complete(); + } + else { + subscriber.next(value); + this.schedule(); + } + })); + })); + return sub; + }); + } + + /** PURE_IMPORTS_START _symbol_observable PURE_IMPORTS_END */ + function isInteropObservable(input) { + return input && typeof input[observable] === 'function'; + } + + /** PURE_IMPORTS_START _symbol_iterator PURE_IMPORTS_END */ + function isIterable(input) { + return input && typeof input[iterator] === 'function'; + } + + /** PURE_IMPORTS_START _scheduleObservable,_schedulePromise,_scheduleArray,_scheduleIterable,_util_isInteropObservable,_util_isPromise,_util_isArrayLike,_util_isIterable PURE_IMPORTS_END */ + function scheduled(input, scheduler) { + if (input != null) { + if (isInteropObservable(input)) { + return scheduleObservable(input, scheduler); + } + else if (isPromise(input)) { + return schedulePromise(input, scheduler); + } + else if (isArrayLike(input)) { + return scheduleArray(input, scheduler); + } + else if (isIterable(input) || typeof input === 'string') { + return scheduleIterable(input, scheduler); + } + } + throw new TypeError((input !== null && typeof input || input) + ' is not observable'); + } + + /** PURE_IMPORTS_START _Observable,_util_subscribeTo,_scheduled_scheduled PURE_IMPORTS_END */ + function from(input, scheduler) { + if (!scheduler) { + if (input instanceof Observable) { + return input; + } + return new Observable(subscribeTo(input)); + } + else { + return scheduled(input, scheduler); + } + } + + /** PURE_IMPORTS_START tslib,_Subscriber,_Observable,_util_subscribeTo PURE_IMPORTS_END */ + var SimpleInnerSubscriber = /*@__PURE__*/ (function (_super) { + __extends(SimpleInnerSubscriber, _super); + function SimpleInnerSubscriber(parent) { + var _this = _super.call(this) || this; + _this.parent = parent; + return _this; + } + SimpleInnerSubscriber.prototype._next = function (value) { + this.parent.notifyNext(value); + }; + SimpleInnerSubscriber.prototype._error = function (error) { + this.parent.notifyError(error); + this.unsubscribe(); + }; + SimpleInnerSubscriber.prototype._complete = function () { + this.parent.notifyComplete(); + this.unsubscribe(); + }; + return SimpleInnerSubscriber; + }(Subscriber)); + var SimpleOuterSubscriber = /*@__PURE__*/ (function (_super) { + __extends(SimpleOuterSubscriber, _super); + function SimpleOuterSubscriber() { + return _super !== null && _super.apply(this, arguments) || this; + } + SimpleOuterSubscriber.prototype.notifyNext = function (innerValue) { + this.destination.next(innerValue); + }; + SimpleOuterSubscriber.prototype.notifyError = function (err) { + this.destination.error(err); + }; + SimpleOuterSubscriber.prototype.notifyComplete = function () { + this.destination.complete(); + }; + return SimpleOuterSubscriber; + }(Subscriber)); + function innerSubscribe(result, innerSubscriber) { + if (innerSubscriber.closed) { + return undefined; + } + if (result instanceof Observable) { + return result.subscribe(innerSubscriber); + } + return subscribeTo(result)(innerSubscriber); + } + + /** PURE_IMPORTS_START tslib,_map,_observable_from,_innerSubscribe PURE_IMPORTS_END */ + function mergeMap(project, resultSelector, concurrent) { + if (concurrent === void 0) { + concurrent = Number.POSITIVE_INFINITY; + } + if (typeof resultSelector === 'function') { + return function (source) { return source.pipe(mergeMap(function (a, i) { return from(project(a, i)).pipe(map(function (b, ii) { return resultSelector(a, b, i, ii); })); }, concurrent)); }; + } + else if (typeof resultSelector === 'number') { + concurrent = resultSelector; + } + return function (source) { return source.lift(new MergeMapOperator(project, concurrent)); }; + } + var MergeMapOperator = /*@__PURE__*/ (function () { + function MergeMapOperator(project, concurrent) { + if (concurrent === void 0) { + concurrent = Number.POSITIVE_INFINITY; + } + this.project = project; + this.concurrent = concurrent; + } + MergeMapOperator.prototype.call = function (observer, source) { + return source.subscribe(new MergeMapSubscriber(observer, this.project, this.concurrent)); + }; + return MergeMapOperator; + }()); + var MergeMapSubscriber = /*@__PURE__*/ (function (_super) { + __extends(MergeMapSubscriber, _super); + function MergeMapSubscriber(destination, project, concurrent) { + if (concurrent === void 0) { + concurrent = Number.POSITIVE_INFINITY; + } + var _this = _super.call(this, destination) || this; + _this.project = project; + _this.concurrent = concurrent; + _this.hasCompleted = false; + _this.buffer = []; + _this.active = 0; + _this.index = 0; + return _this; + } + MergeMapSubscriber.prototype._next = function (value) { + if (this.active < this.concurrent) { + this._tryNext(value); + } + else { + this.buffer.push(value); + } + }; + MergeMapSubscriber.prototype._tryNext = function (value) { + var result; + var index = this.index++; + try { + result = this.project(value, index); + } + catch (err) { + this.destination.error(err); + return; + } + this.active++; + this._innerSub(result); + }; + MergeMapSubscriber.prototype._innerSub = function (ish) { + var innerSubscriber = new SimpleInnerSubscriber(this); + var destination = this.destination; + destination.add(innerSubscriber); + var innerSubscription = innerSubscribe(ish, innerSubscriber); + if (innerSubscription !== innerSubscriber) { + destination.add(innerSubscription); + } + }; + MergeMapSubscriber.prototype._complete = function () { + this.hasCompleted = true; + if (this.active === 0 && this.buffer.length === 0) { + this.destination.complete(); + } + this.unsubscribe(); + }; + MergeMapSubscriber.prototype.notifyNext = function (innerValue) { + this.destination.next(innerValue); + }; + MergeMapSubscriber.prototype.notifyComplete = function () { + var buffer = this.buffer; + this.active--; + if (buffer.length > 0) { + this._next(buffer.shift()); + } + else if (this.active === 0 && this.hasCompleted) { + this.destination.complete(); + } + }; + return MergeMapSubscriber; + }(SimpleOuterSubscriber)); + + /** PURE_IMPORTS_START _mergeMap,_util_identity PURE_IMPORTS_END */ + function mergeAll(concurrent) { + if (concurrent === void 0) { + concurrent = Number.POSITIVE_INFINITY; + } + return mergeMap(identity, concurrent); + } + + /** PURE_IMPORTS_START _mergeAll PURE_IMPORTS_END */ + function concatAll() { + return mergeAll(1); + } + + /** PURE_IMPORTS_START _of,_operators_concatAll PURE_IMPORTS_END */ + function concat() { + var observables = []; + for (var _i = 0; _i < arguments.length; _i++) { + observables[_i] = arguments[_i]; + } + return concatAll()(of.apply(void 0, observables)); + } + + /** PURE_IMPORTS_START _Observable,_from,_empty PURE_IMPORTS_END */ + function defer(observableFactory) { + return new Observable(function (subscriber) { + var input; + try { + input = observableFactory(); + } + catch (err) { + subscriber.error(err); + return undefined; + } + var source = input ? from(input) : empty(); + return source.subscribe(subscriber); + }); + } + + /** PURE_IMPORTS_START _Observable,_util_isArray,_operators_map,_util_isObject,_from PURE_IMPORTS_END */ + function forkJoin() { + var sources = []; + for (var _i = 0; _i < arguments.length; _i++) { + sources[_i] = arguments[_i]; + } + if (sources.length === 1) { + var first_1 = sources[0]; + if (isArray$1(first_1)) { + return forkJoinInternal(first_1, null); + } + if (isObject$1(first_1) && Object.getPrototypeOf(first_1) === Object.prototype) { + var keys = Object.keys(first_1); + return forkJoinInternal(keys.map(function (key) { return first_1[key]; }), keys); + } + } + if (typeof sources[sources.length - 1] === 'function') { + var resultSelector_1 = sources.pop(); + sources = (sources.length === 1 && isArray$1(sources[0])) ? sources[0] : sources; + return forkJoinInternal(sources, null).pipe(map(function (args) { return resultSelector_1.apply(void 0, args); })); + } + return forkJoinInternal(sources, null); + } + function forkJoinInternal(sources, keys) { + return new Observable(function (subscriber) { + var len = sources.length; + if (len === 0) { + subscriber.complete(); + return; + } + var values = new Array(len); + var completed = 0; + var emitted = 0; + var _loop_1 = function (i) { + var source = from(sources[i]); + var hasValue = false; + subscriber.add(source.subscribe({ + next: function (value) { + if (!hasValue) { + hasValue = true; + emitted++; + } + values[i] = value; + }, + error: function (err) { return subscriber.error(err); }, + complete: function () { + completed++; + if (completed === len || !hasValue) { + if (emitted === len) { + subscriber.next(keys ? + keys.reduce(function (result, key, i) { return (result[key] = values[i], result); }, {}) : + values); + } + subscriber.complete(); + } + } + })); + }; + for (var i = 0; i < len; i++) { + _loop_1(i); + } + }); + } + + /** PURE_IMPORTS_START _Observable,_util_isArray,_util_isFunction,_operators_map PURE_IMPORTS_END */ + function fromEvent(target, eventName, options, resultSelector) { + if (isFunction$1(options)) { + resultSelector = options; + options = undefined; + } + if (resultSelector) { + return fromEvent(target, eventName, options).pipe(map(function (args) { return isArray$1(args) ? resultSelector.apply(void 0, args) : resultSelector(args); })); + } + return new Observable(function (subscriber) { + function handler(e) { + if (arguments.length > 1) { + subscriber.next(Array.prototype.slice.call(arguments)); + } + else { + subscriber.next(e); + } + } + setupSubscription(target, eventName, handler, subscriber, options); + }); + } + function setupSubscription(sourceObj, eventName, handler, subscriber, options) { + var unsubscribe; + if (isEventTarget(sourceObj)) { + var source_1 = sourceObj; + sourceObj.addEventListener(eventName, handler, options); + unsubscribe = function () { return source_1.removeEventListener(eventName, handler, options); }; + } + else if (isJQueryStyleEventEmitter(sourceObj)) { + var source_2 = sourceObj; + sourceObj.on(eventName, handler); + unsubscribe = function () { return source_2.off(eventName, handler); }; + } + else if (isNodeStyleEventEmitter(sourceObj)) { + var source_3 = sourceObj; + sourceObj.addListener(eventName, handler); + unsubscribe = function () { return source_3.removeListener(eventName, handler); }; + } + else if (sourceObj && sourceObj.length) { + for (var i = 0, len = sourceObj.length; i < len; i++) { + setupSubscription(sourceObj[i], eventName, handler, subscriber, options); + } + } + else { + throw new TypeError('Invalid event target'); + } + subscriber.add(unsubscribe); + } + function isNodeStyleEventEmitter(sourceObj) { + return sourceObj && typeof sourceObj.addListener === 'function' && typeof sourceObj.removeListener === 'function'; + } + function isJQueryStyleEventEmitter(sourceObj) { + return sourceObj && typeof sourceObj.on === 'function' && typeof sourceObj.off === 'function'; + } + function isEventTarget(sourceObj) { + return sourceObj && typeof sourceObj.addEventListener === 'function' && typeof sourceObj.removeEventListener === 'function'; + } + + /** PURE_IMPORTS_START _defer,_empty PURE_IMPORTS_END */ + function iif(condition, trueResult, falseResult) { + if (trueResult === void 0) { + trueResult = EMPTY; + } + if (falseResult === void 0) { + falseResult = EMPTY; + } + return defer(function () { return condition() ? trueResult : falseResult; }); + } + + /** PURE_IMPORTS_START _isArray PURE_IMPORTS_END */ + function isNumeric(val) { + return !isArray$1(val) && (val - parseFloat(val) + 1) >= 0; + } + + /** PURE_IMPORTS_START _Observable,_util_isScheduler,_operators_mergeAll,_fromArray PURE_IMPORTS_END */ + function merge() { + var observables = []; + for (var _i = 0; _i < arguments.length; _i++) { + observables[_i] = arguments[_i]; + } + var concurrent = Number.POSITIVE_INFINITY; + var scheduler = null; + var last = observables[observables.length - 1]; + if (isScheduler(last)) { + scheduler = observables.pop(); + if (observables.length > 1 && typeof observables[observables.length - 1] === 'number') { + concurrent = observables.pop(); + } + } + else if (typeof last === 'number') { + concurrent = observables.pop(); + } + if (scheduler === null && observables.length === 1 && observables[0] instanceof Observable) { + return observables[0]; + } + return mergeAll(concurrent)(fromArray(observables, scheduler)); + } + + /** PURE_IMPORTS_START _Observable,_util_noop PURE_IMPORTS_END */ + var NEVER = /*@__PURE__*/ new Observable(noop); + + /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + function filter(predicate, thisArg) { + return function filterOperatorFunction(source) { + return source.lift(new FilterOperator(predicate, thisArg)); + }; + } + var FilterOperator = /*@__PURE__*/ (function () { + function FilterOperator(predicate, thisArg) { + this.predicate = predicate; + this.thisArg = thisArg; + } + FilterOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new FilterSubscriber(subscriber, this.predicate, this.thisArg)); + }; + return FilterOperator; + }()); + var FilterSubscriber = /*@__PURE__*/ (function (_super) { + __extends(FilterSubscriber, _super); + function FilterSubscriber(destination, predicate, thisArg) { + var _this = _super.call(this, destination) || this; + _this.predicate = predicate; + _this.thisArg = thisArg; + _this.count = 0; + return _this; + } + FilterSubscriber.prototype._next = function (value) { + var result; + try { + result = this.predicate.call(this.thisArg, value, this.count++); + } + catch (err) { + this.destination.error(err); + return; + } + if (result) { + this.destination.next(value); + } + }; + return FilterSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_util_isArray,_fromArray,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + function race() { + var observables = []; + for (var _i = 0; _i < arguments.length; _i++) { + observables[_i] = arguments[_i]; + } + if (observables.length === 1) { + if (isArray$1(observables[0])) { + observables = observables[0]; + } + else { + return observables[0]; + } + } + return fromArray(observables, undefined).lift(new RaceOperator()); + } + var RaceOperator = /*@__PURE__*/ (function () { + function RaceOperator() { + } + RaceOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new RaceSubscriber(subscriber)); + }; + return RaceOperator; + }()); + var RaceSubscriber = /*@__PURE__*/ (function (_super) { + __extends(RaceSubscriber, _super); + function RaceSubscriber(destination) { + var _this = _super.call(this, destination) || this; + _this.hasFirst = false; + _this.observables = []; + _this.subscriptions = []; + return _this; + } + RaceSubscriber.prototype._next = function (observable) { + this.observables.push(observable); + }; + RaceSubscriber.prototype._complete = function () { + var observables = this.observables; + var len = observables.length; + if (len === 0) { + this.destination.complete(); + } + else { + for (var i = 0; i < len && !this.hasFirst; i++) { + var observable = observables[i]; + var subscription = subscribeToResult(this, observable, undefined, i); + if (this.subscriptions) { + this.subscriptions.push(subscription); + } + this.add(subscription); + } + this.observables = null; + } + }; + RaceSubscriber.prototype.notifyNext = function (_outerValue, innerValue, outerIndex) { + if (!this.hasFirst) { + this.hasFirst = true; + for (var i = 0; i < this.subscriptions.length; i++) { + if (i !== outerIndex) { + var subscription = this.subscriptions[i]; + subscription.unsubscribe(); + this.remove(subscription); + } + } + this.subscriptions = null; + } + this.destination.next(innerValue); + }; + return RaceSubscriber; + }(OuterSubscriber)); + + /** PURE_IMPORTS_START _Observable,_scheduler_async,_util_isNumeric,_util_isScheduler PURE_IMPORTS_END */ + function timer(dueTime, periodOrScheduler, scheduler) { + if (dueTime === void 0) { + dueTime = 0; + } + var period = -1; + if (isNumeric(periodOrScheduler)) { + period = Number(periodOrScheduler) < 1 && 1 || Number(periodOrScheduler); + } + else if (isScheduler(periodOrScheduler)) { + scheduler = periodOrScheduler; + } + if (!isScheduler(scheduler)) { + scheduler = async; + } + return new Observable(function (subscriber) { + var due = isNumeric(dueTime) + ? dueTime + : (+dueTime - scheduler.now()); + return scheduler.schedule(dispatch, due, { + index: 0, period: period, subscriber: subscriber + }); + }); + } + function dispatch(state) { + var index = state.index, period = state.period, subscriber = state.subscriber; + subscriber.next(index); + if (subscriber.closed) { + return; + } + else if (period === -1) { + return subscriber.complete(); + } + state.index = index + 1; + this.schedule(state, period); + } + + /** PURE_IMPORTS_START tslib,_fromArray,_util_isArray,_Subscriber,_.._internal_symbol_iterator,_innerSubscribe PURE_IMPORTS_END */ + function zip() { + var observables = []; + for (var _i = 0; _i < arguments.length; _i++) { + observables[_i] = arguments[_i]; + } + var resultSelector = observables[observables.length - 1]; + if (typeof resultSelector === 'function') { + observables.pop(); + } + return fromArray(observables, undefined).lift(new ZipOperator(resultSelector)); + } + var ZipOperator = /*@__PURE__*/ (function () { + function ZipOperator(resultSelector) { + this.resultSelector = resultSelector; + } + ZipOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ZipSubscriber(subscriber, this.resultSelector)); + }; + return ZipOperator; + }()); + var ZipSubscriber = /*@__PURE__*/ (function (_super) { + __extends(ZipSubscriber, _super); + function ZipSubscriber(destination, resultSelector, values) { + var _this = _super.call(this, destination) || this; + _this.resultSelector = resultSelector; + _this.iterators = []; + _this.active = 0; + _this.resultSelector = (typeof resultSelector === 'function') ? resultSelector : undefined; + return _this; + } + ZipSubscriber.prototype._next = function (value) { + var iterators = this.iterators; + if (isArray$1(value)) { + iterators.push(new StaticArrayIterator(value)); + } + else if (typeof value[iterator] === 'function') { + iterators.push(new StaticIterator(value[iterator]())); + } + else { + iterators.push(new ZipBufferIterator(this.destination, this, value)); + } + }; + ZipSubscriber.prototype._complete = function () { + var iterators = this.iterators; + var len = iterators.length; + this.unsubscribe(); + if (len === 0) { + this.destination.complete(); + return; + } + this.active = len; + for (var i = 0; i < len; i++) { + var iterator = iterators[i]; + if (iterator.stillUnsubscribed) { + var destination = this.destination; + destination.add(iterator.subscribe()); + } + else { + this.active--; + } + } + }; + ZipSubscriber.prototype.notifyInactive = function () { + this.active--; + if (this.active === 0) { + this.destination.complete(); + } + }; + ZipSubscriber.prototype.checkIterators = function () { + var iterators = this.iterators; + var len = iterators.length; + var destination = this.destination; + for (var i = 0; i < len; i++) { + var iterator = iterators[i]; + if (typeof iterator.hasValue === 'function' && !iterator.hasValue()) { + return; + } + } + var shouldComplete = false; + var args = []; + for (var i = 0; i < len; i++) { + var iterator = iterators[i]; + var result = iterator.next(); + if (iterator.hasCompleted()) { + shouldComplete = true; + } + if (result.done) { + destination.complete(); + return; + } + args.push(result.value); + } + if (this.resultSelector) { + this._tryresultSelector(args); + } + else { + destination.next(args); + } + if (shouldComplete) { + destination.complete(); + } + }; + ZipSubscriber.prototype._tryresultSelector = function (args) { + var result; + try { + result = this.resultSelector.apply(this, args); + } + catch (err) { + this.destination.error(err); + return; + } + this.destination.next(result); + }; + return ZipSubscriber; + }(Subscriber)); + var StaticIterator = /*@__PURE__*/ (function () { + function StaticIterator(iterator) { + this.iterator = iterator; + this.nextResult = iterator.next(); + } + StaticIterator.prototype.hasValue = function () { + return true; + }; + StaticIterator.prototype.next = function () { + var result = this.nextResult; + this.nextResult = this.iterator.next(); + return result; + }; + StaticIterator.prototype.hasCompleted = function () { + var nextResult = this.nextResult; + return Boolean(nextResult && nextResult.done); + }; + return StaticIterator; + }()); + var StaticArrayIterator = /*@__PURE__*/ (function () { + function StaticArrayIterator(array) { + this.array = array; + this.index = 0; + this.length = 0; + this.length = array.length; + } + StaticArrayIterator.prototype[iterator] = function () { + return this; + }; + StaticArrayIterator.prototype.next = function (value) { + var i = this.index++; + var array = this.array; + return i < this.length ? { value: array[i], done: false } : { value: null, done: true }; + }; + StaticArrayIterator.prototype.hasValue = function () { + return this.array.length > this.index; + }; + StaticArrayIterator.prototype.hasCompleted = function () { + return this.array.length === this.index; + }; + return StaticArrayIterator; + }()); + var ZipBufferIterator = /*@__PURE__*/ (function (_super) { + __extends(ZipBufferIterator, _super); + function ZipBufferIterator(destination, parent, observable) { + var _this = _super.call(this, destination) || this; + _this.parent = parent; + _this.observable = observable; + _this.stillUnsubscribed = true; + _this.buffer = []; + _this.isComplete = false; + return _this; + } + ZipBufferIterator.prototype[iterator] = function () { + return this; + }; + ZipBufferIterator.prototype.next = function () { + var buffer = this.buffer; + if (buffer.length === 0 && this.isComplete) { + return { value: null, done: true }; + } + else { + return { value: buffer.shift(), done: false }; + } + }; + ZipBufferIterator.prototype.hasValue = function () { + return this.buffer.length > 0; + }; + ZipBufferIterator.prototype.hasCompleted = function () { + return this.buffer.length === 0 && this.isComplete; + }; + ZipBufferIterator.prototype.notifyComplete = function () { + if (this.buffer.length > 0) { + this.isComplete = true; + this.parent.notifyInactive(); + } + else { + this.destination.complete(); + } + }; + ZipBufferIterator.prototype.notifyNext = function (innerValue) { + this.buffer.push(innerValue); + this.parent.checkIterators(); + }; + ZipBufferIterator.prototype.subscribe = function () { + return innerSubscribe(this.observable, new SimpleInnerSubscriber(this)); + }; + return ZipBufferIterator; + }(SimpleOuterSubscriber)); + + /** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ + function audit(durationSelector) { + return function auditOperatorFunction(source) { + return source.lift(new AuditOperator(durationSelector)); + }; + } + var AuditOperator = /*@__PURE__*/ (function () { + function AuditOperator(durationSelector) { + this.durationSelector = durationSelector; + } + AuditOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new AuditSubscriber(subscriber, this.durationSelector)); + }; + return AuditOperator; + }()); + var AuditSubscriber = /*@__PURE__*/ (function (_super) { + __extends(AuditSubscriber, _super); + function AuditSubscriber(destination, durationSelector) { + var _this = _super.call(this, destination) || this; + _this.durationSelector = durationSelector; + _this.hasValue = false; + return _this; + } + AuditSubscriber.prototype._next = function (value) { + this.value = value; + this.hasValue = true; + if (!this.throttled) { + var duration = void 0; + try { + var durationSelector = this.durationSelector; + duration = durationSelector(value); + } + catch (err) { + return this.destination.error(err); + } + var innerSubscription = innerSubscribe(duration, new SimpleInnerSubscriber(this)); + if (!innerSubscription || innerSubscription.closed) { + this.clearThrottle(); + } + else { + this.add(this.throttled = innerSubscription); + } + } + }; + AuditSubscriber.prototype.clearThrottle = function () { + var _a = this, value = _a.value, hasValue = _a.hasValue, throttled = _a.throttled; + if (throttled) { + this.remove(throttled); + this.throttled = undefined; + throttled.unsubscribe(); + } + if (hasValue) { + this.value = undefined; + this.hasValue = false; + this.destination.next(value); + } + }; + AuditSubscriber.prototype.notifyNext = function () { + this.clearThrottle(); + }; + AuditSubscriber.prototype.notifyComplete = function () { + this.clearThrottle(); + }; + return AuditSubscriber; + }(SimpleOuterSubscriber)); + + /** PURE_IMPORTS_START _scheduler_async,_audit,_observable_timer PURE_IMPORTS_END */ + function auditTime(duration, scheduler) { + if (scheduler === void 0) { + scheduler = async; + } + return audit(function () { return timer(duration, scheduler); }); + } + + /** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ + function catchError(selector) { + return function catchErrorOperatorFunction(source) { + var operator = new CatchOperator(selector); + var caught = source.lift(operator); + return (operator.caught = caught); + }; + } + var CatchOperator = /*@__PURE__*/ (function () { + function CatchOperator(selector) { + this.selector = selector; + } + CatchOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new CatchSubscriber(subscriber, this.selector, this.caught)); + }; + return CatchOperator; + }()); + var CatchSubscriber = /*@__PURE__*/ (function (_super) { + __extends(CatchSubscriber, _super); + function CatchSubscriber(destination, selector, caught) { + var _this = _super.call(this, destination) || this; + _this.selector = selector; + _this.caught = caught; + return _this; + } + CatchSubscriber.prototype.error = function (err) { + if (!this.isStopped) { + var result = void 0; + try { + result = this.selector(err, this.caught); + } + catch (err2) { + _super.prototype.error.call(this, err2); + return; + } + this._unsubscribeAndRecycle(); + var innerSubscriber = new SimpleInnerSubscriber(this); + this.add(innerSubscriber); + var innerSubscription = innerSubscribe(result, innerSubscriber); + if (innerSubscription !== innerSubscriber) { + this.add(innerSubscription); + } + } + }; + return CatchSubscriber; + }(SimpleOuterSubscriber)); + + /** PURE_IMPORTS_START _mergeMap PURE_IMPORTS_END */ + function concatMap(project, resultSelector) { + return mergeMap(project, resultSelector, 1); + } + + /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async PURE_IMPORTS_END */ + function debounceTime(dueTime, scheduler) { + if (scheduler === void 0) { + scheduler = async; + } + return function (source) { return source.lift(new DebounceTimeOperator(dueTime, scheduler)); }; + } + var DebounceTimeOperator = /*@__PURE__*/ (function () { + function DebounceTimeOperator(dueTime, scheduler) { + this.dueTime = dueTime; + this.scheduler = scheduler; + } + DebounceTimeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DebounceTimeSubscriber(subscriber, this.dueTime, this.scheduler)); + }; + return DebounceTimeOperator; + }()); + var DebounceTimeSubscriber = /*@__PURE__*/ (function (_super) { + __extends(DebounceTimeSubscriber, _super); + function DebounceTimeSubscriber(destination, dueTime, scheduler) { + var _this = _super.call(this, destination) || this; + _this.dueTime = dueTime; + _this.scheduler = scheduler; + _this.debouncedSubscription = null; + _this.lastValue = null; + _this.hasValue = false; + return _this; + } + DebounceTimeSubscriber.prototype._next = function (value) { + this.clearDebounce(); + this.lastValue = value; + this.hasValue = true; + this.add(this.debouncedSubscription = this.scheduler.schedule(dispatchNext$1, this.dueTime, this)); + }; + DebounceTimeSubscriber.prototype._complete = function () { + this.debouncedNext(); + this.destination.complete(); + }; + DebounceTimeSubscriber.prototype.debouncedNext = function () { + this.clearDebounce(); + if (this.hasValue) { + var lastValue = this.lastValue; + this.lastValue = null; + this.hasValue = false; + this.destination.next(lastValue); + } + }; + DebounceTimeSubscriber.prototype.clearDebounce = function () { + var debouncedSubscription = this.debouncedSubscription; + if (debouncedSubscription !== null) { + this.remove(debouncedSubscription); + debouncedSubscription.unsubscribe(); + this.debouncedSubscription = null; + } + }; + return DebounceTimeSubscriber; + }(Subscriber)); + function dispatchNext$1(subscriber) { + subscriber.debouncedNext(); + } + + /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + function defaultIfEmpty(defaultValue) { + if (defaultValue === void 0) { + defaultValue = null; + } + return function (source) { return source.lift(new DefaultIfEmptyOperator(defaultValue)); }; + } + var DefaultIfEmptyOperator = /*@__PURE__*/ (function () { + function DefaultIfEmptyOperator(defaultValue) { + this.defaultValue = defaultValue; + } + DefaultIfEmptyOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DefaultIfEmptySubscriber(subscriber, this.defaultValue)); + }; + return DefaultIfEmptyOperator; + }()); + var DefaultIfEmptySubscriber = /*@__PURE__*/ (function (_super) { + __extends(DefaultIfEmptySubscriber, _super); + function DefaultIfEmptySubscriber(destination, defaultValue) { + var _this = _super.call(this, destination) || this; + _this.defaultValue = defaultValue; + _this.isEmpty = true; + return _this; + } + DefaultIfEmptySubscriber.prototype._next = function (value) { + this.isEmpty = false; + this.destination.next(value); + }; + DefaultIfEmptySubscriber.prototype._complete = function () { + if (this.isEmpty) { + this.destination.next(this.defaultValue); + } + this.destination.complete(); + }; + return DefaultIfEmptySubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START PURE_IMPORTS_END */ + function isDate(value) { + return value instanceof Date && !isNaN(+value); + } + + /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_Subscriber,_Notification PURE_IMPORTS_END */ + function delay(delay, scheduler) { + if (scheduler === void 0) { + scheduler = async; + } + var absoluteDelay = isDate(delay); + var delayFor = absoluteDelay ? (+delay - scheduler.now()) : Math.abs(delay); + return function (source) { return source.lift(new DelayOperator(delayFor, scheduler)); }; + } + var DelayOperator = /*@__PURE__*/ (function () { + function DelayOperator(delay, scheduler) { + this.delay = delay; + this.scheduler = scheduler; + } + DelayOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DelaySubscriber(subscriber, this.delay, this.scheduler)); + }; + return DelayOperator; + }()); + var DelaySubscriber = /*@__PURE__*/ (function (_super) { + __extends(DelaySubscriber, _super); + function DelaySubscriber(destination, delay, scheduler) { + var _this = _super.call(this, destination) || this; + _this.delay = delay; + _this.scheduler = scheduler; + _this.queue = []; + _this.active = false; + _this.errored = false; + return _this; + } + DelaySubscriber.dispatch = function (state) { + var source = state.source; + var queue = source.queue; + var scheduler = state.scheduler; + var destination = state.destination; + while (queue.length > 0 && (queue[0].time - scheduler.now()) <= 0) { + queue.shift().notification.observe(destination); + } + if (queue.length > 0) { + var delay_1 = Math.max(0, queue[0].time - scheduler.now()); + this.schedule(state, delay_1); + } + else { + this.unsubscribe(); + source.active = false; + } + }; + DelaySubscriber.prototype._schedule = function (scheduler) { + this.active = true; + var destination = this.destination; + destination.add(scheduler.schedule(DelaySubscriber.dispatch, this.delay, { + source: this, destination: this.destination, scheduler: scheduler + })); + }; + DelaySubscriber.prototype.scheduleNotification = function (notification) { + if (this.errored === true) { + return; + } + var scheduler = this.scheduler; + var message = new DelayMessage(scheduler.now() + this.delay, notification); + this.queue.push(message); + if (this.active === false) { + this._schedule(scheduler); + } + }; + DelaySubscriber.prototype._next = function (value) { + this.scheduleNotification(Notification.createNext(value)); + }; + DelaySubscriber.prototype._error = function (err) { + this.errored = true; + this.queue = []; + this.destination.error(err); + this.unsubscribe(); + }; + DelaySubscriber.prototype._complete = function () { + this.scheduleNotification(Notification.createComplete()); + this.unsubscribe(); + }; + return DelaySubscriber; + }(Subscriber)); + var DelayMessage = /*@__PURE__*/ (function () { + function DelayMessage(time, notification) { + this.time = time; + this.notification = notification; + } + return DelayMessage; + }()); + + /** PURE_IMPORTS_START tslib,_Subscriber,_Observable,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + function delayWhen(delayDurationSelector, subscriptionDelay) { + if (subscriptionDelay) { + return function (source) { + return new SubscriptionDelayObservable(source, subscriptionDelay) + .lift(new DelayWhenOperator(delayDurationSelector)); + }; + } + return function (source) { return source.lift(new DelayWhenOperator(delayDurationSelector)); }; + } + var DelayWhenOperator = /*@__PURE__*/ (function () { + function DelayWhenOperator(delayDurationSelector) { + this.delayDurationSelector = delayDurationSelector; + } + DelayWhenOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DelayWhenSubscriber(subscriber, this.delayDurationSelector)); + }; + return DelayWhenOperator; + }()); + var DelayWhenSubscriber = /*@__PURE__*/ (function (_super) { + __extends(DelayWhenSubscriber, _super); + function DelayWhenSubscriber(destination, delayDurationSelector) { + var _this = _super.call(this, destination) || this; + _this.delayDurationSelector = delayDurationSelector; + _this.completed = false; + _this.delayNotifierSubscriptions = []; + _this.index = 0; + return _this; + } + DelayWhenSubscriber.prototype.notifyNext = function (outerValue, _innerValue, _outerIndex, _innerIndex, innerSub) { + this.destination.next(outerValue); + this.removeSubscription(innerSub); + this.tryComplete(); + }; + DelayWhenSubscriber.prototype.notifyError = function (error, innerSub) { + this._error(error); + }; + DelayWhenSubscriber.prototype.notifyComplete = function (innerSub) { + var value = this.removeSubscription(innerSub); + if (value) { + this.destination.next(value); + } + this.tryComplete(); + }; + DelayWhenSubscriber.prototype._next = function (value) { + var index = this.index++; + try { + var delayNotifier = this.delayDurationSelector(value, index); + if (delayNotifier) { + this.tryDelay(delayNotifier, value); + } + } + catch (err) { + this.destination.error(err); + } + }; + DelayWhenSubscriber.prototype._complete = function () { + this.completed = true; + this.tryComplete(); + this.unsubscribe(); + }; + DelayWhenSubscriber.prototype.removeSubscription = function (subscription) { + subscription.unsubscribe(); + var subscriptionIdx = this.delayNotifierSubscriptions.indexOf(subscription); + if (subscriptionIdx !== -1) { + this.delayNotifierSubscriptions.splice(subscriptionIdx, 1); + } + return subscription.outerValue; + }; + DelayWhenSubscriber.prototype.tryDelay = function (delayNotifier, value) { + var notifierSubscription = subscribeToResult(this, delayNotifier, value); + if (notifierSubscription && !notifierSubscription.closed) { + var destination = this.destination; + destination.add(notifierSubscription); + this.delayNotifierSubscriptions.push(notifierSubscription); + } + }; + DelayWhenSubscriber.prototype.tryComplete = function () { + if (this.completed && this.delayNotifierSubscriptions.length === 0) { + this.destination.complete(); + } + }; + return DelayWhenSubscriber; + }(OuterSubscriber)); + var SubscriptionDelayObservable = /*@__PURE__*/ (function (_super) { + __extends(SubscriptionDelayObservable, _super); + function SubscriptionDelayObservable(source, subscriptionDelay) { + var _this = _super.call(this) || this; + _this.source = source; + _this.subscriptionDelay = subscriptionDelay; + return _this; + } + SubscriptionDelayObservable.prototype._subscribe = function (subscriber) { + this.subscriptionDelay.subscribe(new SubscriptionDelaySubscriber(subscriber, this.source)); + }; + return SubscriptionDelayObservable; + }(Observable)); + var SubscriptionDelaySubscriber = /*@__PURE__*/ (function (_super) { + __extends(SubscriptionDelaySubscriber, _super); + function SubscriptionDelaySubscriber(parent, source) { + var _this = _super.call(this) || this; + _this.parent = parent; + _this.source = source; + _this.sourceSubscribed = false; + return _this; + } + SubscriptionDelaySubscriber.prototype._next = function (unused) { + this.subscribeToSource(); + }; + SubscriptionDelaySubscriber.prototype._error = function (err) { + this.unsubscribe(); + this.parent.error(err); + }; + SubscriptionDelaySubscriber.prototype._complete = function () { + this.unsubscribe(); + this.subscribeToSource(); + }; + SubscriptionDelaySubscriber.prototype.subscribeToSource = function () { + if (!this.sourceSubscribed) { + this.sourceSubscribed = true; + this.unsubscribe(); + this.source.subscribe(this.parent); + } + }; + return SubscriptionDelaySubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + function distinctUntilChanged(compare, keySelector) { + return function (source) { return source.lift(new DistinctUntilChangedOperator(compare, keySelector)); }; + } + var DistinctUntilChangedOperator = /*@__PURE__*/ (function () { + function DistinctUntilChangedOperator(compare, keySelector) { + this.compare = compare; + this.keySelector = keySelector; + } + DistinctUntilChangedOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new DistinctUntilChangedSubscriber(subscriber, this.compare, this.keySelector)); + }; + return DistinctUntilChangedOperator; + }()); + var DistinctUntilChangedSubscriber = /*@__PURE__*/ (function (_super) { + __extends(DistinctUntilChangedSubscriber, _super); + function DistinctUntilChangedSubscriber(destination, compare, keySelector) { + var _this = _super.call(this, destination) || this; + _this.keySelector = keySelector; + _this.hasKey = false; + if (typeof compare === 'function') { + _this.compare = compare; + } + return _this; + } + DistinctUntilChangedSubscriber.prototype.compare = function (x, y) { + return x === y; + }; + DistinctUntilChangedSubscriber.prototype._next = function (value) { + var key; + try { + var keySelector = this.keySelector; + key = keySelector ? keySelector(value) : value; + } + catch (err) { + return this.destination.error(err); + } + var result = false; + if (this.hasKey) { + try { + var compare = this.compare; + result = compare(this.key, key); + } + catch (err) { + return this.destination.error(err); + } + } + else { + this.hasKey = true; + } + if (!result) { + this.key = key; + this.destination.next(value); + } + }; + return DistinctUntilChangedSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ + function take(count) { + return function (source) { + if (count === 0) { + return empty(); + } + else { + return source.lift(new TakeOperator(count)); + } + }; + } + var TakeOperator = /*@__PURE__*/ (function () { + function TakeOperator(total) { + this.total = total; + if (this.total < 0) { + throw new ArgumentOutOfRangeError; + } + } + TakeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TakeSubscriber(subscriber, this.total)); + }; + return TakeOperator; + }()); + var TakeSubscriber = /*@__PURE__*/ (function (_super) { + __extends(TakeSubscriber, _super); + function TakeSubscriber(destination, total) { + var _this = _super.call(this, destination) || this; + _this.total = total; + _this.count = 0; + return _this; + } + TakeSubscriber.prototype._next = function (value) { + var total = this.total; + var count = ++this.count; + if (count <= total) { + this.destination.next(value); + if (count === total) { + this.destination.complete(); + this.unsubscribe(); + } + } + }; + return TakeSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_map,_observable_from,_innerSubscribe PURE_IMPORTS_END */ + function exhaustMap(project, resultSelector) { + if (resultSelector) { + return function (source) { return source.pipe(exhaustMap(function (a, i) { return from(project(a, i)).pipe(map(function (b, ii) { return resultSelector(a, b, i, ii); })); })); }; + } + return function (source) { + return source.lift(new ExhaustMapOperator(project)); + }; + } + var ExhaustMapOperator = /*@__PURE__*/ (function () { + function ExhaustMapOperator(project) { + this.project = project; + } + ExhaustMapOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ExhaustMapSubscriber(subscriber, this.project)); + }; + return ExhaustMapOperator; + }()); + var ExhaustMapSubscriber = /*@__PURE__*/ (function (_super) { + __extends(ExhaustMapSubscriber, _super); + function ExhaustMapSubscriber(destination, project) { + var _this = _super.call(this, destination) || this; + _this.project = project; + _this.hasSubscription = false; + _this.hasCompleted = false; + _this.index = 0; + return _this; + } + ExhaustMapSubscriber.prototype._next = function (value) { + if (!this.hasSubscription) { + this.tryNext(value); + } + }; + ExhaustMapSubscriber.prototype.tryNext = function (value) { + var result; + var index = this.index++; + try { + result = this.project(value, index); + } + catch (err) { + this.destination.error(err); + return; + } + this.hasSubscription = true; + this._innerSub(result); + }; + ExhaustMapSubscriber.prototype._innerSub = function (result) { + var innerSubscriber = new SimpleInnerSubscriber(this); + var destination = this.destination; + destination.add(innerSubscriber); + var innerSubscription = innerSubscribe(result, innerSubscriber); + if (innerSubscription !== innerSubscriber) { + destination.add(innerSubscription); + } + }; + ExhaustMapSubscriber.prototype._complete = function () { + this.hasCompleted = true; + if (!this.hasSubscription) { + this.destination.complete(); + } + this.unsubscribe(); + }; + ExhaustMapSubscriber.prototype.notifyNext = function (innerValue) { + this.destination.next(innerValue); + }; + ExhaustMapSubscriber.prototype.notifyError = function (err) { + this.destination.error(err); + }; + ExhaustMapSubscriber.prototype.notifyComplete = function () { + this.hasSubscription = false; + if (this.hasCompleted) { + this.destination.complete(); + } + }; + return ExhaustMapSubscriber; + }(SimpleOuterSubscriber)); + + /** PURE_IMPORTS_START tslib,_Subscriber,_Subscription PURE_IMPORTS_END */ + function finalize$1(callback) { + return function (source) { return source.lift(new FinallyOperator(callback)); }; + } + var FinallyOperator = /*@__PURE__*/ (function () { + function FinallyOperator(callback) { + this.callback = callback; + } + FinallyOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new FinallySubscriber(subscriber, this.callback)); + }; + return FinallyOperator; + }()); + var FinallySubscriber = /*@__PURE__*/ (function (_super) { + __extends(FinallySubscriber, _super); + function FinallySubscriber(destination, callback) { + var _this = _super.call(this, destination) || this; + _this.add(new Subscription(callback)); + return _this; + } + return FinallySubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_Subscriber,_util_ArgumentOutOfRangeError,_observable_empty PURE_IMPORTS_END */ + function takeLast(count) { + return function takeLastOperatorFunction(source) { + if (count === 0) { + return empty(); + } + else { + return source.lift(new TakeLastOperator(count)); + } + }; + } + var TakeLastOperator = /*@__PURE__*/ (function () { + function TakeLastOperator(total) { + this.total = total; + if (this.total < 0) { + throw new ArgumentOutOfRangeError; + } + } + TakeLastOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TakeLastSubscriber(subscriber, this.total)); + }; + return TakeLastOperator; + }()); + var TakeLastSubscriber = /*@__PURE__*/ (function (_super) { + __extends(TakeLastSubscriber, _super); + function TakeLastSubscriber(destination, total) { + var _this = _super.call(this, destination) || this; + _this.total = total; + _this.ring = new Array(); + _this.count = 0; + return _this; + } + TakeLastSubscriber.prototype._next = function (value) { + var ring = this.ring; + var total = this.total; + var count = this.count++; + if (ring.length < total) { + ring.push(value); + } + else { + var index = count % total; + ring[index] = value; + } + }; + TakeLastSubscriber.prototype._complete = function () { + var destination = this.destination; + var count = this.count; + if (count > 0) { + var total = this.count >= this.total ? this.total : this.count; + var ring = this.ring; + for (var i = 0; i < total; i++) { + var idx = (count++) % total; + destination.next(ring[idx]); + } + } + destination.complete(); + }; + return TakeLastSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + function mapTo(value) { + return function (source) { return source.lift(new MapToOperator(value)); }; + } + var MapToOperator = /*@__PURE__*/ (function () { + function MapToOperator(value) { + this.value = value; + } + MapToOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new MapToSubscriber(subscriber, this.value)); + }; + return MapToOperator; + }()); + var MapToSubscriber = /*@__PURE__*/ (function (_super) { + __extends(MapToSubscriber, _super); + function MapToSubscriber(destination, value) { + var _this = _super.call(this, destination) || this; + _this.value = value; + return _this; + } + MapToSubscriber.prototype._next = function (x) { + this.destination.next(this.value); + }; + return MapToSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + function scan(accumulator, seed) { + var hasSeed = false; + if (arguments.length >= 2) { + hasSeed = true; + } + return function scanOperatorFunction(source) { + return source.lift(new ScanOperator(accumulator, seed, hasSeed)); + }; + } + var ScanOperator = /*@__PURE__*/ (function () { + function ScanOperator(accumulator, seed, hasSeed) { + if (hasSeed === void 0) { + hasSeed = false; + } + this.accumulator = accumulator; + this.seed = seed; + this.hasSeed = hasSeed; + } + ScanOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ScanSubscriber(subscriber, this.accumulator, this.seed, this.hasSeed)); + }; + return ScanOperator; + }()); + var ScanSubscriber = /*@__PURE__*/ (function (_super) { + __extends(ScanSubscriber, _super); + function ScanSubscriber(destination, accumulator, _seed, hasSeed) { + var _this = _super.call(this, destination) || this; + _this.accumulator = accumulator; + _this._seed = _seed; + _this.hasSeed = hasSeed; + _this.index = 0; + return _this; + } + Object.defineProperty(ScanSubscriber.prototype, "seed", { + get: function () { + return this._seed; + }, + set: function (value) { + this.hasSeed = true; + this._seed = value; + }, + enumerable: true, + configurable: true + }); + ScanSubscriber.prototype._next = function (value) { + if (!this.hasSeed) { + this.seed = value; + this.destination.next(value); + } + else { + return this._tryNext(value); + } + }; + ScanSubscriber.prototype._tryNext = function (value) { + var index = this.index++; + var result; + try { + result = this.accumulator(this.seed, value, index); + } + catch (err) { + this.destination.error(err); + } + this.seed = result; + this.destination.next(result); + }; + return ScanSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START _scan,_takeLast,_defaultIfEmpty,_util_pipe PURE_IMPORTS_END */ + function reduce(accumulator, seed) { + if (arguments.length >= 2) { + return function reduceOperatorFunctionWithSeed(source) { + return pipe(scan(accumulator, seed), takeLast(1), defaultIfEmpty(seed))(source); + }; + } + return function reduceOperatorFunction(source) { + return pipe(scan(function (acc, value, index) { return accumulator(acc, value, index + 1); }), takeLast(1))(source); + }; + } + + /** PURE_IMPORTS_START _observable_ConnectableObservable PURE_IMPORTS_END */ + function multicast(subjectOrSubjectFactory, selector) { + return function multicastOperatorFunction(source) { + var subjectFactory; + if (typeof subjectOrSubjectFactory === 'function') { + subjectFactory = subjectOrSubjectFactory; + } + else { + subjectFactory = function subjectFactory() { + return subjectOrSubjectFactory; + }; + } + if (typeof selector === 'function') { + return source.lift(new MulticastOperator(subjectFactory, selector)); + } + var connectable = Object.create(source, connectableObservableDescriptor); + connectable.source = source; + connectable.subjectFactory = subjectFactory; + return connectable; + }; + } + var MulticastOperator = /*@__PURE__*/ (function () { + function MulticastOperator(subjectFactory, selector) { + this.subjectFactory = subjectFactory; + this.selector = selector; + } + MulticastOperator.prototype.call = function (subscriber, source) { + var selector = this.selector; + var subject = this.subjectFactory(); + var subscription = selector(subject).subscribe(subscriber); + subscription.add(source.subscribe(subject)); + return subscription; + }; + return MulticastOperator; + }()); + + /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + function pairwise() { + return function (source) { return source.lift(new PairwiseOperator()); }; + } + var PairwiseOperator = /*@__PURE__*/ (function () { + function PairwiseOperator() { + } + PairwiseOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new PairwiseSubscriber(subscriber)); + }; + return PairwiseOperator; + }()); + var PairwiseSubscriber = /*@__PURE__*/ (function (_super) { + __extends(PairwiseSubscriber, _super); + function PairwiseSubscriber(destination) { + var _this = _super.call(this, destination) || this; + _this.hasPrev = false; + return _this; + } + PairwiseSubscriber.prototype._next = function (value) { + var pair; + if (this.hasPrev) { + pair = [this.prev, value]; + } + else { + this.hasPrev = true; + } + this.prev = value; + if (pair) { + this.destination.next(pair); + } + }; + return PairwiseSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_Subject,_innerSubscribe PURE_IMPORTS_END */ + function retryWhen(notifier) { + return function (source) { return source.lift(new RetryWhenOperator(notifier, source)); }; + } + var RetryWhenOperator = /*@__PURE__*/ (function () { + function RetryWhenOperator(notifier, source) { + this.notifier = notifier; + this.source = source; + } + RetryWhenOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new RetryWhenSubscriber(subscriber, this.notifier, this.source)); + }; + return RetryWhenOperator; + }()); + var RetryWhenSubscriber = /*@__PURE__*/ (function (_super) { + __extends(RetryWhenSubscriber, _super); + function RetryWhenSubscriber(destination, notifier, source) { + var _this = _super.call(this, destination) || this; + _this.notifier = notifier; + _this.source = source; + return _this; + } + RetryWhenSubscriber.prototype.error = function (err) { + if (!this.isStopped) { + var errors = this.errors; + var retries = this.retries; + var retriesSubscription = this.retriesSubscription; + if (!retries) { + errors = new Subject(); + try { + var notifier = this.notifier; + retries = notifier(errors); + } + catch (e) { + return _super.prototype.error.call(this, e); + } + retriesSubscription = innerSubscribe(retries, new SimpleInnerSubscriber(this)); + } + else { + this.errors = undefined; + this.retriesSubscription = undefined; + } + this._unsubscribeAndRecycle(); + this.errors = errors; + this.retries = retries; + this.retriesSubscription = retriesSubscription; + errors.next(err); + } + }; + RetryWhenSubscriber.prototype._unsubscribe = function () { + var _a = this, errors = _a.errors, retriesSubscription = _a.retriesSubscription; + if (errors) { + errors.unsubscribe(); + this.errors = undefined; + } + if (retriesSubscription) { + retriesSubscription.unsubscribe(); + this.retriesSubscription = undefined; + } + this.retries = undefined; + }; + RetryWhenSubscriber.prototype.notifyNext = function () { + var _unsubscribe = this._unsubscribe; + this._unsubscribe = null; + this._unsubscribeAndRecycle(); + this._unsubscribe = _unsubscribe; + this.source.subscribe(this); + }; + return RetryWhenSubscriber; + }(SimpleOuterSubscriber)); + + /** PURE_IMPORTS_START _multicast,_refCount,_Subject PURE_IMPORTS_END */ + function shareSubjectFactory() { + return new Subject(); + } + function share() { + return function (source) { return refCount()(multicast(shareSubjectFactory)(source)); }; + } + + /** PURE_IMPORTS_START _ReplaySubject PURE_IMPORTS_END */ + function shareReplay(configOrBufferSize, windowTime, scheduler) { + var config; + if (configOrBufferSize && typeof configOrBufferSize === 'object') { + config = configOrBufferSize; + } + else { + config = { + bufferSize: configOrBufferSize, + windowTime: windowTime, + refCount: false, + scheduler: scheduler, + }; + } + return function (source) { return source.lift(shareReplayOperator(config)); }; + } + function shareReplayOperator(_a) { + var _b = _a.bufferSize, bufferSize = _b === void 0 ? Number.POSITIVE_INFINITY : _b, _c = _a.windowTime, windowTime = _c === void 0 ? Number.POSITIVE_INFINITY : _c, useRefCount = _a.refCount, scheduler = _a.scheduler; + var subject; + var refCount = 0; + var subscription; + var hasError = false; + var isComplete = false; + return function shareReplayOperation(source) { + refCount++; + var innerSub; + if (!subject || hasError) { + hasError = false; + subject = new ReplaySubject(bufferSize, windowTime, scheduler); + innerSub = subject.subscribe(this); + subscription = source.subscribe({ + next: function (value) { + subject.next(value); + }, + error: function (err) { + hasError = true; + subject.error(err); + }, + complete: function () { + isComplete = true; + subscription = undefined; + subject.complete(); + }, + }); + if (isComplete) { + subscription = undefined; + } + } + else { + innerSub = subject.subscribe(this); + } + this.add(function () { + refCount--; + innerSub.unsubscribe(); + innerSub = undefined; + if (subscription && !isComplete && useRefCount && refCount === 0) { + subscription.unsubscribe(); + subscription = undefined; + subject = undefined; + } + }); + }; + } + + /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + function skip(count) { + return function (source) { return source.lift(new SkipOperator(count)); }; + } + var SkipOperator = /*@__PURE__*/ (function () { + function SkipOperator(total) { + this.total = total; + } + SkipOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new SkipSubscriber(subscriber, this.total)); + }; + return SkipOperator; + }()); + var SkipSubscriber = /*@__PURE__*/ (function (_super) { + __extends(SkipSubscriber, _super); + function SkipSubscriber(destination, total) { + var _this = _super.call(this, destination) || this; + _this.total = total; + _this.count = 0; + return _this; + } + SkipSubscriber.prototype._next = function (x) { + if (++this.count > this.total) { + this.destination.next(x); + } + }; + return SkipSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START _observable_concat,_util_isScheduler PURE_IMPORTS_END */ + function startWith() { + var array = []; + for (var _i = 0; _i < arguments.length; _i++) { + array[_i] = arguments[_i]; + } + var scheduler = array[array.length - 1]; + if (isScheduler(scheduler)) { + array.pop(); + return function (source) { return concat(array, source, scheduler); }; + } + else { + return function (source) { return concat(array, source); }; + } + } + + /** PURE_IMPORTS_START tslib,_map,_observable_from,_innerSubscribe PURE_IMPORTS_END */ + function switchMap(project, resultSelector) { + if (typeof resultSelector === 'function') { + return function (source) { return source.pipe(switchMap(function (a, i) { return from(project(a, i)).pipe(map(function (b, ii) { return resultSelector(a, b, i, ii); })); })); }; + } + return function (source) { return source.lift(new SwitchMapOperator(project)); }; + } + var SwitchMapOperator = /*@__PURE__*/ (function () { + function SwitchMapOperator(project) { + this.project = project; + } + SwitchMapOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new SwitchMapSubscriber(subscriber, this.project)); + }; + return SwitchMapOperator; + }()); + var SwitchMapSubscriber = /*@__PURE__*/ (function (_super) { + __extends(SwitchMapSubscriber, _super); + function SwitchMapSubscriber(destination, project) { + var _this = _super.call(this, destination) || this; + _this.project = project; + _this.index = 0; + return _this; + } + SwitchMapSubscriber.prototype._next = function (value) { + var result; + var index = this.index++; + try { + result = this.project(value, index); + } + catch (error) { + this.destination.error(error); + return; + } + this._innerSub(result); + }; + SwitchMapSubscriber.prototype._innerSub = function (result) { + var innerSubscription = this.innerSubscription; + if (innerSubscription) { + innerSubscription.unsubscribe(); + } + var innerSubscriber = new SimpleInnerSubscriber(this); + var destination = this.destination; + destination.add(innerSubscriber); + this.innerSubscription = innerSubscribe(result, innerSubscriber); + if (this.innerSubscription !== innerSubscriber) { + destination.add(this.innerSubscription); + } + }; + SwitchMapSubscriber.prototype._complete = function () { + var innerSubscription = this.innerSubscription; + if (!innerSubscription || innerSubscription.closed) { + _super.prototype._complete.call(this); + } + this.unsubscribe(); + }; + SwitchMapSubscriber.prototype._unsubscribe = function () { + this.innerSubscription = undefined; + }; + SwitchMapSubscriber.prototype.notifyComplete = function () { + this.innerSubscription = undefined; + if (this.isStopped) { + _super.prototype._complete.call(this); + } + }; + SwitchMapSubscriber.prototype.notifyNext = function (innerValue) { + this.destination.next(innerValue); + }; + return SwitchMapSubscriber; + }(SimpleOuterSubscriber)); + + /** PURE_IMPORTS_START _switchMap PURE_IMPORTS_END */ + function switchMapTo(innerObservable, resultSelector) { + return resultSelector ? switchMap(function () { return innerObservable; }, resultSelector) : switchMap(function () { return innerObservable; }); + } + + /** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ + function takeUntil(notifier) { + return function (source) { return source.lift(new TakeUntilOperator(notifier)); }; + } + var TakeUntilOperator = /*@__PURE__*/ (function () { + function TakeUntilOperator(notifier) { + this.notifier = notifier; + } + TakeUntilOperator.prototype.call = function (subscriber, source) { + var takeUntilSubscriber = new TakeUntilSubscriber(subscriber); + var notifierSubscription = innerSubscribe(this.notifier, new SimpleInnerSubscriber(takeUntilSubscriber)); + if (notifierSubscription && !takeUntilSubscriber.seenValue) { + takeUntilSubscriber.add(notifierSubscription); + return source.subscribe(takeUntilSubscriber); + } + return takeUntilSubscriber; + }; + return TakeUntilOperator; + }()); + var TakeUntilSubscriber = /*@__PURE__*/ (function (_super) { + __extends(TakeUntilSubscriber, _super); + function TakeUntilSubscriber(destination) { + var _this = _super.call(this, destination) || this; + _this.seenValue = false; + return _this; + } + TakeUntilSubscriber.prototype.notifyNext = function () { + this.seenValue = true; + this.complete(); + }; + TakeUntilSubscriber.prototype.notifyComplete = function () { + }; + return TakeUntilSubscriber; + }(SimpleOuterSubscriber)); + + /** PURE_IMPORTS_START tslib,_Subscriber PURE_IMPORTS_END */ + function takeWhile(predicate, inclusive) { + if (inclusive === void 0) { + inclusive = false; + } + return function (source) { + return source.lift(new TakeWhileOperator(predicate, inclusive)); + }; + } + var TakeWhileOperator = /*@__PURE__*/ (function () { + function TakeWhileOperator(predicate, inclusive) { + this.predicate = predicate; + this.inclusive = inclusive; + } + TakeWhileOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TakeWhileSubscriber(subscriber, this.predicate, this.inclusive)); + }; + return TakeWhileOperator; + }()); + var TakeWhileSubscriber = /*@__PURE__*/ (function (_super) { + __extends(TakeWhileSubscriber, _super); + function TakeWhileSubscriber(destination, predicate, inclusive) { + var _this = _super.call(this, destination) || this; + _this.predicate = predicate; + _this.inclusive = inclusive; + _this.index = 0; + return _this; + } + TakeWhileSubscriber.prototype._next = function (value) { + var destination = this.destination; + var result; + try { + result = this.predicate(value, this.index++); + } + catch (err) { + destination.error(err); + return; + } + this.nextOrComplete(value, result); + }; + TakeWhileSubscriber.prototype.nextOrComplete = function (value, predicateResult) { + var destination = this.destination; + if (Boolean(predicateResult)) { + destination.next(value); + } + else { + if (this.inclusive) { + destination.next(value); + } + destination.complete(); + } + }; + return TakeWhileSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_Subscriber,_util_noop,_util_isFunction PURE_IMPORTS_END */ + function tap(nextOrObserver, error, complete) { + return function tapOperatorFunction(source) { + return source.lift(new DoOperator(nextOrObserver, error, complete)); + }; + } + var DoOperator = /*@__PURE__*/ (function () { + function DoOperator(nextOrObserver, error, complete) { + this.nextOrObserver = nextOrObserver; + this.error = error; + this.complete = complete; + } + DoOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TapSubscriber(subscriber, this.nextOrObserver, this.error, this.complete)); + }; + return DoOperator; + }()); + var TapSubscriber = /*@__PURE__*/ (function (_super) { + __extends(TapSubscriber, _super); + function TapSubscriber(destination, observerOrNext, error, complete) { + var _this = _super.call(this, destination) || this; + _this._tapNext = noop; + _this._tapError = noop; + _this._tapComplete = noop; + _this._tapError = error || noop; + _this._tapComplete = complete || noop; + if (isFunction$1(observerOrNext)) { + _this._context = _this; + _this._tapNext = observerOrNext; + } + else if (observerOrNext) { + _this._context = observerOrNext; + _this._tapNext = observerOrNext.next || noop; + _this._tapError = observerOrNext.error || noop; + _this._tapComplete = observerOrNext.complete || noop; + } + return _this; + } + TapSubscriber.prototype._next = function (value) { + try { + this._tapNext.call(this._context, value); + } + catch (err) { + this.destination.error(err); + return; + } + this.destination.next(value); + }; + TapSubscriber.prototype._error = function (err) { + try { + this._tapError.call(this._context, err); + } + catch (err) { + this.destination.error(err); + return; + } + this.destination.error(err); + }; + TapSubscriber.prototype._complete = function () { + try { + this._tapComplete.call(this._context); + } + catch (err) { + this.destination.error(err); + return; + } + return this.destination.complete(); + }; + return TapSubscriber; + }(Subscriber)); + + /** PURE_IMPORTS_START tslib,_innerSubscribe PURE_IMPORTS_END */ + var defaultThrottleConfig = { + leading: true, + trailing: false + }; + + /** PURE_IMPORTS_START tslib,_Subscriber,_scheduler_async,_throttle PURE_IMPORTS_END */ + function throttleTime(duration, scheduler, config) { + if (scheduler === void 0) { + scheduler = async; + } + if (config === void 0) { + config = defaultThrottleConfig; + } + return function (source) { return source.lift(new ThrottleTimeOperator(duration, scheduler, config.leading, config.trailing)); }; + } + var ThrottleTimeOperator = /*@__PURE__*/ (function () { + function ThrottleTimeOperator(duration, scheduler, leading, trailing) { + this.duration = duration; + this.scheduler = scheduler; + this.leading = leading; + this.trailing = trailing; + } + ThrottleTimeOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new ThrottleTimeSubscriber(subscriber, this.duration, this.scheduler, this.leading, this.trailing)); + }; + return ThrottleTimeOperator; + }()); + var ThrottleTimeSubscriber = /*@__PURE__*/ (function (_super) { + __extends(ThrottleTimeSubscriber, _super); + function ThrottleTimeSubscriber(destination, duration, scheduler, leading, trailing) { + var _this = _super.call(this, destination) || this; + _this.duration = duration; + _this.scheduler = scheduler; + _this.leading = leading; + _this.trailing = trailing; + _this._hasTrailingValue = false; + _this._trailingValue = null; + return _this; + } + ThrottleTimeSubscriber.prototype._next = function (value) { + if (this.throttled) { + if (this.trailing) { + this._trailingValue = value; + this._hasTrailingValue = true; + } + } + else { + this.add(this.throttled = this.scheduler.schedule(dispatchNext, this.duration, { subscriber: this })); + if (this.leading) { + this.destination.next(value); + } + else if (this.trailing) { + this._trailingValue = value; + this._hasTrailingValue = true; + } + } + }; + ThrottleTimeSubscriber.prototype._complete = function () { + if (this._hasTrailingValue) { + this.destination.next(this._trailingValue); + this.destination.complete(); + } + else { + this.destination.complete(); + } + }; + ThrottleTimeSubscriber.prototype.clearThrottle = function () { + var throttled = this.throttled; + if (throttled) { + if (this.trailing && this._hasTrailingValue) { + this.destination.next(this._trailingValue); + this._trailingValue = null; + this._hasTrailingValue = false; + } + throttled.unsubscribe(); + this.remove(throttled); + this.throttled = null; + } + }; + return ThrottleTimeSubscriber; + }(Subscriber)); + function dispatchNext(arg) { + var subscriber = arg.subscriber; + subscriber.clearThrottle(); + } + + /** PURE_IMPORTS_START tslib,_scheduler_async,_util_isDate,_innerSubscribe PURE_IMPORTS_END */ + function timeoutWith(due, withObservable, scheduler) { + if (scheduler === void 0) { + scheduler = async; + } + return function (source) { + var absoluteTimeout = isDate(due); + var waitFor = absoluteTimeout ? (+due - scheduler.now()) : Math.abs(due); + return source.lift(new TimeoutWithOperator(waitFor, absoluteTimeout, withObservable, scheduler)); + }; + } + var TimeoutWithOperator = /*@__PURE__*/ (function () { + function TimeoutWithOperator(waitFor, absoluteTimeout, withObservable, scheduler) { + this.waitFor = waitFor; + this.absoluteTimeout = absoluteTimeout; + this.withObservable = withObservable; + this.scheduler = scheduler; + } + TimeoutWithOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new TimeoutWithSubscriber(subscriber, this.absoluteTimeout, this.waitFor, this.withObservable, this.scheduler)); + }; + return TimeoutWithOperator; + }()); + var TimeoutWithSubscriber = /*@__PURE__*/ (function (_super) { + __extends(TimeoutWithSubscriber, _super); + function TimeoutWithSubscriber(destination, absoluteTimeout, waitFor, withObservable, scheduler) { + var _this = _super.call(this, destination) || this; + _this.absoluteTimeout = absoluteTimeout; + _this.waitFor = waitFor; + _this.withObservable = withObservable; + _this.scheduler = scheduler; + _this.scheduleTimeout(); + return _this; + } + TimeoutWithSubscriber.dispatchTimeout = function (subscriber) { + var withObservable = subscriber.withObservable; + subscriber._unsubscribeAndRecycle(); + subscriber.add(innerSubscribe(withObservable, new SimpleInnerSubscriber(subscriber))); + }; + TimeoutWithSubscriber.prototype.scheduleTimeout = function () { + var action = this.action; + if (action) { + this.action = action.schedule(this, this.waitFor); + } + else { + this.add(this.action = this.scheduler.schedule(TimeoutWithSubscriber.dispatchTimeout, this.waitFor, this)); + } + }; + TimeoutWithSubscriber.prototype._next = function (value) { + if (!this.absoluteTimeout) { + this.scheduleTimeout(); + } + _super.prototype._next.call(this, value); + }; + TimeoutWithSubscriber.prototype._unsubscribe = function () { + this.action = undefined; + this.scheduler = null; + this.withObservable = null; + }; + return TimeoutWithSubscriber; + }(SimpleOuterSubscriber)); + + /** PURE_IMPORTS_START _scheduler_async,_util_TimeoutError,_timeoutWith,_observable_throwError PURE_IMPORTS_END */ + function timeout(due, scheduler) { + if (scheduler === void 0) { + scheduler = async; + } + return timeoutWith(due, throwError(new TimeoutError()), scheduler); + } + + /** PURE_IMPORTS_START tslib,_OuterSubscriber,_util_subscribeToResult PURE_IMPORTS_END */ + function withLatestFrom() { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return function (source) { + var project; + if (typeof args[args.length - 1] === 'function') { + project = args.pop(); + } + var observables = args; + return source.lift(new WithLatestFromOperator(observables, project)); + }; + } + var WithLatestFromOperator = /*@__PURE__*/ (function () { + function WithLatestFromOperator(observables, project) { + this.observables = observables; + this.project = project; + } + WithLatestFromOperator.prototype.call = function (subscriber, source) { + return source.subscribe(new WithLatestFromSubscriber(subscriber, this.observables, this.project)); + }; + return WithLatestFromOperator; + }()); + var WithLatestFromSubscriber = /*@__PURE__*/ (function (_super) { + __extends(WithLatestFromSubscriber, _super); + function WithLatestFromSubscriber(destination, observables, project) { + var _this = _super.call(this, destination) || this; + _this.observables = observables; + _this.project = project; + _this.toRespond = []; + var len = observables.length; + _this.values = new Array(len); + for (var i = 0; i < len; i++) { + _this.toRespond.push(i); + } + for (var i = 0; i < len; i++) { + var observable = observables[i]; + _this.add(subscribeToResult(_this, observable, undefined, i)); + } + return _this; + } + WithLatestFromSubscriber.prototype.notifyNext = function (_outerValue, innerValue, outerIndex) { + this.values[outerIndex] = innerValue; + var toRespond = this.toRespond; + if (toRespond.length > 0) { + var found = toRespond.indexOf(outerIndex); + if (found !== -1) { + toRespond.splice(found, 1); + } + } + }; + WithLatestFromSubscriber.prototype.notifyComplete = function () { + }; + WithLatestFromSubscriber.prototype._next = function (value) { + if (this.toRespond.length === 0) { + var args = [value].concat(this.values); + if (this.project) { + this._tryProject(args); + } + else { + this.destination.next(args); + } + } + }; + WithLatestFromSubscriber.prototype._tryProject = function (args) { + var result; + try { + result = this.project.apply(this, args); + } + catch (err) { + this.destination.error(err); + return; + } + this.destination.next(result); + }; + return WithLatestFromSubscriber; + }(OuterSubscriber)); + + var currentAction = { + type: null, + entityIds: null, + skip: false, + }; + var customActionActive = false; + function resetCustomAction() { + customActionActive = false; + } + // public API for custom actions. Custom action always wins + function logAction(type, entityIds) { + setAction(type, entityIds); + customActionActive = true; + } + function setAction(type, entityIds) { + if (customActionActive === false) { + currentAction.type = type; + currentAction.entityIds = entityIds; + } + } + function action(action, entityIds) { + return function (target, propertyKey, descriptor) { + var originalMethod = descriptor.value; + descriptor.value = function () { + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + logAction(action, entityIds); + return originalMethod.apply(this, args); + }; + return descriptor; + }; + } + + // @internal + function hasEntity(entities, id) { + return entities.hasOwnProperty(id); + } + + // @internal + function addEntities(_a) { + var state = _a.state, entities = _a.entities, idKey = _a.idKey, _b = _a.options, options = _b === void 0 ? {} : _b, preAddEntity = _a.preAddEntity; + var e_1, _c; + var newEntities = {}; + var newIds = []; + var hasNewEntities = false; + try { + for (var entities_1 = __values(entities), entities_1_1 = entities_1.next(); !entities_1_1.done; entities_1_1 = entities_1.next()) { + var entity = entities_1_1.value; + if (hasEntity(state.entities, entity[idKey]) === false) { + // evaluate the middleware first to support dynamic ids + var current = preAddEntity(entity); + var entityId = current[idKey]; + newEntities[entityId] = current; + if (options.prepend) + newIds.unshift(entityId); + else + newIds.push(entityId); + hasNewEntities = true; + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (entities_1_1 && !entities_1_1.done && (_c = entities_1.return)) _c.call(entities_1); + } + finally { if (e_1) throw e_1.error; } + } + return hasNewEntities + ? { + newState: __assign({}, state, { entities: __assign({}, state.entities, newEntities), ids: options.prepend ? __spread(newIds, state.ids) : __spread(state.ids, newIds) }), + newIds: newIds + } + : null; + } + + // @internal + function isNil(v) { + return v === null || v === undefined; + } + + // @internal + function coerceArray(value) { + if (isNil(value)) { + return []; + } + return Array.isArray(value) ? value : [value]; + } + + var DEFAULT_ID_KEY = 'id'; + + var EntityActions; + (function (EntityActions) { + EntityActions["Set"] = "Set"; + EntityActions["Add"] = "Add"; + EntityActions["Update"] = "Update"; + EntityActions["Remove"] = "Remove"; + })(EntityActions || (EntityActions = {})); + + var isBrowser = typeof window !== 'undefined'; + + // @internal + function isObject(value) { + var type = typeof value; + return value != null && (type == 'object' || type == 'function'); + } + + // @internal + function isArray(value) { + return Array.isArray(value); + } + + // @internal + function getActiveEntities(idOrOptions, ids, currentActive) { + var result; + if (isArray(idOrOptions)) { + result = idOrOptions; + } + else { + if (isObject(idOrOptions)) { + if (isNil(currentActive)) + return; + idOrOptions = Object.assign({ wrap: true }, idOrOptions); + var currentIdIndex = ids.indexOf(currentActive); + if (idOrOptions.prev) { + var isFirst = currentIdIndex === 0; + if (isFirst && !idOrOptions.wrap) + return; + result = isFirst ? ids[ids.length - 1] : ids[currentIdIndex - 1]; + } + else if (idOrOptions.next) { + var isLast = ids.length === currentIdIndex + 1; + if (isLast && !idOrOptions.wrap) + return; + result = isLast ? ids[0] : ids[currentIdIndex + 1]; + } + } + else { + if (idOrOptions === currentActive) + return; + result = idOrOptions; + } + } + return result; + } + + // @internal + var getInitialEntitiesState = function () { + return ({ + entities: {}, + ids: [], + loading: true, + error: null + }); + }; + + // @internal + function isDefined(val) { + return isNil(val) === false; + } + + // @internal + function isEmpty(arr) { + if (isArray(arr)) { + return arr.length === 0; + } + return false; + } + + // @internal + function isFunction(value) { + return typeof value === 'function'; + } + + // @internal + function isUndefined(value) { + return value === undefined; + } + + // @internal + function hasActiveState(state) { + return state.hasOwnProperty('active'); + } + // @internal + function isMultiActiveState(active) { + return isArray(active); + } + // @internal + function resolveActiveEntity(_a) { + var active = _a.active, ids = _a.ids, entities = _a.entities; + if (isMultiActiveState(active)) { + return getExitingActives(active, ids); + } + if (hasEntity(entities, active) === false) { + return null; + } + return active; + } + // @internal + function getExitingActives(currentActivesIds, newIds) { + var filtered = currentActivesIds.filter(function (id) { return newIds.indexOf(id) > -1; }); + /** Return the same reference if nothing has changed */ + if (filtered.length === currentActivesIds.length) { + return currentActivesIds; + } + return filtered; + } + + // @internal + function removeEntities(_a) { + var state = _a.state, ids = _a.ids; + var e_1, _b; + if (isNil(ids)) + return removeAllEntities(state); + var entities = state.entities; + var newEntities = {}; + try { + for (var _c = __values(state.ids), _d = _c.next(); !_d.done; _d = _c.next()) { + var id = _d.value; + if (ids.includes(id) === false) { + newEntities[id] = entities[id]; + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_d && !_d.done && (_b = _c.return)) _b.call(_c); + } + finally { if (e_1) throw e_1.error; } + } + var newState = __assign({}, state, { entities: newEntities, ids: state.ids.filter(function (current) { return ids.includes(current) === false; }) }); + if (hasActiveState(state)) { + newState.active = resolveActiveEntity(newState); + } + return newState; + } + // @internal + function removeAllEntities(state) { + return __assign({}, state, { entities: {}, ids: [], active: isMultiActiveState(state.active) ? [] : null }); + } + + // @internal + function toEntitiesObject(entities, idKey, preAddEntity) { + var e_1, _a; + var acc = { + entities: {}, + ids: [] + }; + try { + for (var entities_1 = __values(entities), entities_1_1 = entities_1.next(); !entities_1_1.done; entities_1_1 = entities_1.next()) { + var entity = entities_1_1.value; + // evaluate the middleware first to support dynamic ids + var current = preAddEntity(entity); + acc.entities[current[idKey]] = current; + acc.ids.push(current[idKey]); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1); + } + finally { if (e_1) throw e_1.error; } + } + return acc; + } + + // @internal + function isEntityState(state) { + return state.entities && state.ids; + } + // @internal + function applyMiddleware(entities, preAddEntity) { + var e_1, _a; + var mapped = {}; + try { + for (var _b = __values(Object.keys(entities)), _c = _b.next(); !_c.done; _c = _b.next()) { + var id = _c.value; + mapped[id] = preAddEntity(entities[id]); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_1) throw e_1.error; } + } + return mapped; + } + // @internal + function setEntities(_a) { + var state = _a.state, entities = _a.entities, idKey = _a.idKey, preAddEntity = _a.preAddEntity, isNativePreAdd = _a.isNativePreAdd; + var newEntities; + var newIds; + if (isArray(entities)) { + var resolve = toEntitiesObject(entities, idKey, preAddEntity); + newEntities = resolve.entities; + newIds = resolve.ids; + } + else if (isEntityState(entities)) { + newEntities = isNativePreAdd ? entities.entities : applyMiddleware(entities.entities, preAddEntity); + newIds = entities.ids; + } + else { + // it's an object + newEntities = isNativePreAdd ? entities : applyMiddleware(entities, preAddEntity); + newIds = Object.keys(newEntities).map(function (id) { return (isNaN(id) ? id : Number(id)); }); + } + var newState = __assign({}, state, { entities: newEntities, ids: newIds, loading: false }); + if (hasActiveState(state)) { + newState.active = resolveActiveEntity(newState); + } + return newState; + } + + var CONFIG = { + resettable: false, + ttl: null, + producerFn: undefined + }; + // @internal + function getAkitaConfig() { + return CONFIG; + } + function getGlobalProducerFn() { + return CONFIG.producerFn; + } + + // @internal + function deepFreeze(o) { + Object.freeze(o); + var oIsFunction = typeof o === 'function'; + var hasOwnProp = Object.prototype.hasOwnProperty; + Object.getOwnPropertyNames(o).forEach(function (prop) { + if (hasOwnProp.call(o, prop) && + (oIsFunction ? prop !== 'caller' && prop !== 'callee' && prop !== 'arguments' : true) && + o[prop] !== null && + (typeof o[prop] === 'object' || typeof o[prop] === 'function') && + !Object.isFrozen(o[prop])) { + deepFreeze(o[prop]); + } + }); + return o; + } + + // @internal + var $$deleteStore = new Subject(); + // @internal + var $$addStore = new ReplaySubject(50, 5000); + // @internal + var $$updateStore = new Subject(); + // @internal + function dispatchDeleted(storeName) { + $$deleteStore.next(storeName); + } + // @internal + function dispatchAdded(storeName) { + $$addStore.next(storeName); + } + // @internal + function dispatchUpdate(storeName, action) { + $$updateStore.next({ storeName: storeName, action: action }); + } + + // @internal + /** @class */ ((function (_super) { + __extends(AkitaError, _super); + function AkitaError(message) { + return _super.call(this, message) || this; + } + return AkitaError; + })(Error)); + // @internal + function assertStoreHasName(name, className) { + if (!name) { + console.error("@StoreConfig({ name }) is missing in " + className); + } + } + + // @internal + function toBoolean(value) { + return value != null && "" + value !== 'false'; + } + + // @internal + function isPlainObject$1(value) { + return toBoolean(value) && value.constructor.name === 'Object'; + } + + var configKey = 'akitaConfig'; + + // @internal + var __stores__ = {}; + // @internal + var __queries__ = {}; + if (isBrowser) { + window.$$stores = __stores__; + window.$$queries = __queries__; + } + + // @internal + var transactionFinished = new Subject(); + // @internal + var transactionInProcess = new BehaviorSubject(false); + // @internal + var transactionManager = { + activeTransactions: 0, + batchTransaction: null + }; + // @internal + function startBatch() { + if (!isTransactionInProcess()) { + transactionManager.batchTransaction = new Subject(); + } + transactionManager.activeTransactions++; + transactionInProcess.next(true); + } + // @internal + function endBatch() { + if (--transactionManager.activeTransactions === 0) { + transactionManager.batchTransaction.next(true); + transactionManager.batchTransaction.complete(); + transactionInProcess.next(false); + transactionFinished.next(true); + } + } + // @internal + function isTransactionInProcess() { + return transactionManager.activeTransactions > 0; + } + // @internal + function commit() { + return transactionManager.batchTransaction ? transactionManager.batchTransaction.asObservable() : of(true); + } + /** + * A logical transaction. + * Use this transaction to optimize the dispatch of all the stores. + * The following code will update the store, BUT emits only once + * + * @example + * applyTransaction(() => { + * this.todosStore.add(new Todo(1, title)); + * this.todosStore.add(new Todo(2, title)); + * }); + * + */ + function applyTransaction(action, thisArg) { + if (thisArg === void 0) { thisArg = undefined; } + startBatch(); + try { + return action.apply(thisArg); + } + finally { + logAction('@Transaction'); + endBatch(); + } + } + /** + * A logical transaction. + * Use this transaction to optimize the dispatch of all the stores. + * + * The following code will update the store, BUT emits only once. + * + * @example + * @transaction + * addTodos() { + * this.todosStore.add(new Todo(1, title)); + * this.todosStore.add(new Todo(2, title)); + * } + * + * + */ + function transaction() { + return function (target, propertyKey, descriptor) { + var originalMethod = descriptor.value; + descriptor.value = function () { + var _this = this; + var args = []; + for (var _i = 0; _i < arguments.length; _i++) { + args[_i] = arguments[_i]; + } + return applyTransaction(function () { + return originalMethod.apply(_this, args); + }, this); + }; + return descriptor; + }; + } + /** + * + * RxJS custom operator that wraps the callback inside transaction + * + * @example + * + * return http.get().pipe( + * withTransaction(response > { + * store.setActive(1); + * store.update(); + * store.updateEntity(1, {}); + * }) + * ) + * + */ + function withTransaction(next) { + return function (source) { + return source.pipe(tap(function (value) { return applyTransaction(function () { return next(value); }); })); + }; + } + + /** + * + * Store for managing any type of data + * + * @example + * + * export interface SessionState { + * token: string; + * userDetails: UserDetails + * } + * + * export function createInitialState(): SessionState { + * return { + * token: '', + * userDetails: null + * }; + * } + * + * @StoreConfig({ name: 'session' }) + * export class SessionStore extends Store { + * constructor() { + * super(createInitialState()); + * } + * } + */ + var Store = /** @class */ (function () { + function Store(initialState, options) { + if (options === void 0) { options = {}; } + this.options = options; + this.inTransaction = false; + this.cache = { + active: new BehaviorSubject(false), + ttl: null, + }; + this.onInit(initialState); + } + /** + * Set the loading state + * + * @example + * + * store.setLoading(true) + * + */ + Store.prototype.setLoading = function (loading) { + if (loading === void 0) { loading = false; } + if (loading !== this._value().loading) { + setAction('Set Loading'); + this._setState(function (state) { return (__assign({}, state, { loading: loading })); }); + } + }; + /** + * + * Set whether the data is cached + * + * @example + * + * store.setHasCache(true) + * store.setHasCache(false) + * store.setHasCache(true, { restartTTL: true }) + * + */ + Store.prototype.setHasCache = function (hasCache, options) { + var _this = this; + if (options === void 0) { options = { restartTTL: false }; } + if (hasCache !== this.cache.active.value) { + this.cache.active.next(hasCache); + } + if (options.restartTTL) { + var ttlConfig = this.getCacheTTL(); + if (ttlConfig) { + if (this.cache.ttl !== null) { + clearTimeout(this.cache.ttl); + } + this.cache.ttl = setTimeout(function () { return _this.setHasCache(false); }, ttlConfig); + } + } + }; + /** + * + * Sometimes we need to access the store value from a store + * + * @example middleware + * + */ + Store.prototype.getValue = function () { + return this.storeValue; + }; + /** + * Set the error state + * + * @example + * + * store.setError({text: 'unable to load data' }) + * + */ + Store.prototype.setError = function (error) { + if (error !== this._value().error) { + setAction('Set Error'); + this._setState(function (state) { return (__assign({}, state, { error: error })); }); + } + }; + // @internal + Store.prototype._select = function (project) { + return this.store.asObservable().pipe(map(function (snapshot) { return project(snapshot.state); }), distinctUntilChanged()); + }; + // @internal + Store.prototype._value = function () { + return this.storeValue; + }; + // @internal + Store.prototype._cache = function () { + return this.cache.active; + }; + Object.defineProperty(Store.prototype, "config", { + // @internal + get: function () { + return this.constructor[configKey] || {}; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "storeName", { + // @internal + get: function () { + return this.config.storeName || this.options.storeName || this.options.name; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "deepFreeze", { + // @internal + get: function () { + return this.config.deepFreezeFn || this.options.deepFreezeFn || deepFreeze; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "cacheConfig", { + // @internal + get: function () { + return this.config.cache || this.options.cache; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "_producerFn", { + get: function () { + return this.config.producerFn || this.options.producerFn || getGlobalProducerFn(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(Store.prototype, "resettable", { + // @internal + get: function () { + return isDefined(this.config.resettable) ? this.config.resettable : this.options.resettable; + }, + enumerable: true, + configurable: true + }); + // @internal + Store.prototype._setState = function (newState, _dispatchAction) { + var _this = this; + if (_dispatchAction === void 0) { _dispatchAction = true; } + if (isFunction(newState)) { + var _newState = newState(this._value()); + this.storeValue = this.deepFreeze(_newState) ; + } + else { + this.storeValue = newState; + } + if (!this.store) { + this.store = new BehaviorSubject({ state: this.storeValue }); + { + this.store.subscribe(function (_a) { + var action = _a.action; + if (action) { + dispatchUpdate(_this.storeName, action); + } + }); + } + return; + } + if (isTransactionInProcess()) { + this.handleTransaction(); + return; + } + this.dispatch(this.storeValue, _dispatchAction); + }; + /** + * + * Reset the current store back to the initial value + * + * @example + * + * store.reset() + * + */ + Store.prototype.reset = function () { + var _this = this; + if (this.isResettable()) { + setAction('Reset'); + this._setState(function () { return Object.assign({}, _this._initialState); }); + this.setHasCache(false); + } + else { + console.warn("You need to enable the reset functionality"); + } + }; + Store.prototype.update = function (stateOrCallback) { + setAction('Update'); + var newState; + var currentState = this._value(); + if (isFunction(stateOrCallback)) { + newState = isFunction(this._producerFn) ? this._producerFn(currentState, stateOrCallback) : stateOrCallback(currentState); + } + else { + newState = stateOrCallback; + } + var withHook = this.akitaPreUpdate(currentState, __assign({}, currentState, newState)); + var resolved = isPlainObject$1(currentState) ? withHook : new currentState.constructor(withHook); + this._setState(resolved); + }; + Store.prototype.updateStoreConfig = function (newOptions) { + this.options = __assign({}, this.options, newOptions); + }; + // @internal + Store.prototype.akitaPreUpdate = function (_, nextState) { + return nextState; + }; + Store.prototype.ngOnDestroy = function () { + this.destroy(); + }; + /** + * + * Destroy the store + * + * @example + * + * store.destroy() + * + */ + Store.prototype.destroy = function () { + var hmrEnabled = isBrowser ? window.hmrEnabled : false; + if (!hmrEnabled && this === __stores__[this.storeName]) { + delete __stores__[this.storeName]; + dispatchDeleted(this.storeName); + this.setHasCache(false); + this.cache.active.complete(); + this.store.complete(); + } + }; + Store.prototype.onInit = function (initialState) { + __stores__[this.storeName] = this; + this._setState(function () { return initialState; }); + dispatchAdded(this.storeName); + if (this.isResettable()) { + this._initialState = initialState; + } + assertStoreHasName(this.storeName, this.constructor.name); + }; + Store.prototype.dispatch = function (state, _dispatchAction) { + if (_dispatchAction === void 0) { _dispatchAction = true; } + var action = undefined; + if (_dispatchAction) { + action = currentAction; + resetCustomAction(); + } + this.store.next({ state: state, action: action }); + }; + Store.prototype.watchTransaction = function () { + var _this = this; + commit().subscribe(function () { + _this.inTransaction = false; + _this.dispatch(_this._value()); + }); + }; + Store.prototype.isResettable = function () { + if (this.resettable === false) { + return false; + } + return this.resettable || getAkitaConfig().resettable; + }; + Store.prototype.handleTransaction = function () { + if (!this.inTransaction) { + this.watchTransaction(); + this.inTransaction = true; + } + }; + Store.prototype.getCacheTTL = function () { + return (this.cacheConfig && this.cacheConfig.ttl) || getAkitaConfig().ttl; + }; + return Store; + }()); + + // @internal + function updateEntities(_a) { + var state = _a.state, ids = _a.ids, idKey = _a.idKey, newStateOrFn = _a.newStateOrFn, preUpdateEntity = _a.preUpdateEntity, producerFn = _a.producerFn, onEntityIdChanges = _a.onEntityIdChanges; + var e_1, _b; + var updatedEntities = {}; + var isUpdatingIdKey = false; + var idToUpdate; + try { + for (var ids_1 = __values(ids), ids_1_1 = ids_1.next(); !ids_1_1.done; ids_1_1 = ids_1.next()) { + var id = ids_1_1.value; + // if the entity doesn't exist don't do anything + if (hasEntity(state.entities, id) === false) { + continue; + } + var oldEntity = state.entities[id]; + var newState = void 0; + if (isFunction(newStateOrFn)) { + newState = isFunction(producerFn) ? producerFn(oldEntity, newStateOrFn) : newStateOrFn(oldEntity); + } + else { + newState = newStateOrFn; + } + var isIdChanged = newState.hasOwnProperty(idKey) && newState[idKey] !== oldEntity[idKey]; + var newEntity = void 0; + idToUpdate = id; + if (isIdChanged) { + isUpdatingIdKey = true; + idToUpdate = newState[idKey]; + } + var merged = __assign({}, oldEntity, newState); + if (isPlainObject$1(oldEntity)) { + newEntity = merged; + } + else { + /** + * In case that new state is class of it's own, there's + * a possibility that it will be different than the old + * class. + * For example, Old state is an instance of animal class + * and new state is instance of person class. + * To avoid run over new person class with the old animal + * class we check if the new state is a class of it's own. + * If so, use it. Otherwise, use the old state class + */ + if (isPlainObject$1(newState)) { + newEntity = new oldEntity.constructor(merged); + } + else { + newEntity = new newState.constructor(merged); + } + } + updatedEntities[idToUpdate] = preUpdateEntity(oldEntity, newEntity); + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (ids_1_1 && !ids_1_1.done && (_b = ids_1.return)) _b.call(ids_1); + } + finally { if (e_1) throw e_1.error; } + } + var updatedIds = state.ids; + var stateEntities = state.entities; + if (isUpdatingIdKey) { + var _c = __read(ids, 1), id_1 = _c[0]; + var _d = state.entities, _e = id_1, rest = __rest(_d, [typeof _e === "symbol" ? _e : _e + ""]); + stateEntities = rest; + updatedIds = state.ids.map(function (current) { return (current === id_1 ? idToUpdate : current); }); + onEntityIdChanges(id_1, idToUpdate); + } + return __assign({}, state, { entities: __assign({}, stateEntities, updatedEntities), ids: updatedIds }); + } + + /** + * + * Store for managing a collection of entities + * + * @example + * + * export interface WidgetsState extends EntityState { } + * + * @StoreConfig({ name: 'widgets' }) + * export class WidgetsStore extends EntityStore { + * constructor() { + * super(); + * } + * } + * + * + */ + var EntityStore = /** @class */ (function (_super) { + __extends(EntityStore, _super); + function EntityStore(initialState, options) { + if (initialState === void 0) { initialState = {}; } + if (options === void 0) { options = {}; } + var _this = _super.call(this, __assign({}, getInitialEntitiesState(), initialState), options) || this; + _this.options = options; + _this.entityActions = new Subject(); + _this.entityIdChanges = new Subject(); + return _this; + } + Object.defineProperty(EntityStore.prototype, "selectEntityAction$", { + // @internal + get: function () { + return this.entityActions.asObservable(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EntityStore.prototype, "selectEntityIdChanges$", { + // @internal + get: function () { + return this.entityIdChanges.asObservable(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EntityStore.prototype, "idKey", { + // @internal + get: function () { + return this.config.idKey || this.options.idKey || DEFAULT_ID_KEY; + }, + enumerable: true, + configurable: true + }); + /** + * + * Replace current collection with provided collection + * + * @example + * + * this.store.set([Entity, Entity]) + * this.store.set({ids: [], entities: {}}) + * this.store.set({ 1: {}, 2: {}}) + * + */ + EntityStore.prototype.set = function (entities, options) { + var _this = this; + if (options === void 0) { options = {}; } + if (isNil(entities)) + return; + setAction('Set Entity'); + var isNativePreAdd = this.akitaPreAddEntity === EntityStore.prototype.akitaPreAddEntity; + this.setHasCache(true, { restartTTL: true }); + this._setState(function (state) { + var newState = setEntities({ + state: state, + entities: entities, + idKey: _this.idKey, + preAddEntity: _this.akitaPreAddEntity, + isNativePreAdd: isNativePreAdd, + }); + if (isUndefined(options.activeId) === false) { + newState.active = options.activeId; + } + return newState; + }); + if (this.hasInitialUIState()) { + this.handleUICreation(); + } + this.entityActions.next({ type: EntityActions.Set, ids: this.ids }); + }; + /** + * Add entities + * + * @example + * + * this.store.add([Entity, Entity]) + * this.store.add(Entity) + * this.store.add(Entity, { prepend: true }) + * + * this.store.add(Entity, { loading: false }) + */ + EntityStore.prototype.add = function (entities, options) { + if (options === void 0) { options = { loading: false }; } + var collection = coerceArray(entities); + if (isEmpty(collection)) + return; + var data = addEntities({ + state: this._value(), + preAddEntity: this.akitaPreAddEntity, + entities: collection, + idKey: this.idKey, + options: options, + }); + if (data) { + setAction('Add Entity'); + data.newState.loading = options.loading; + this._setState(function () { return data.newState; }); + if (this.hasInitialUIState()) { + this.handleUICreation(true); + } + this.entityActions.next({ type: EntityActions.Add, ids: data.newIds }); + } + }; + EntityStore.prototype.update = function (idsOrFnOrState, newStateOrFn) { + var _this = this; + if (isUndefined(newStateOrFn)) { + _super.prototype.update.call(this, idsOrFnOrState); + return; + } + var ids = []; + if (isFunction(idsOrFnOrState)) { + // We need to filter according the predicate function + ids = this.ids.filter(function (id) { return idsOrFnOrState(_this.entities[id]); }); + } + else { + // If it's nil we want all of them + ids = isNil(idsOrFnOrState) ? this.ids : coerceArray(idsOrFnOrState); + } + if (isEmpty(ids)) + return; + setAction('Update Entity', ids); + var entityIdChanged; + this._setState(function (state) { + return updateEntities({ + idKey: _this.idKey, + ids: ids, + preUpdateEntity: _this.akitaPreUpdateEntity, + state: state, + newStateOrFn: newStateOrFn, + producerFn: _this._producerFn, + onEntityIdChanges: function (oldId, newId) { + entityIdChanged = { oldId: oldId, newId: newId }; + _this.entityIdChanges.next(__assign({}, entityIdChanged, { pending: true })); + }, + }); + }); + if (entityIdChanged) { + this.entityIdChanges.next(__assign({}, entityIdChanged, { pending: false })); + } + this.entityActions.next({ type: EntityActions.Update, ids: ids }); + }; + EntityStore.prototype.upsert = function (ids, newState, onCreate, options) { + var _this = this; + if (options === void 0) { options = {}; } + var toArray = coerceArray(ids); + var predicate = function (isUpdate) { return function (id) { return hasEntity(_this.entities, id) === isUpdate; }; }; + var baseClass = isFunction(onCreate) ? options.baseClass : onCreate ? onCreate.baseClass : undefined; + var isClassBased = isFunction(baseClass); + var updateIds = toArray.filter(predicate(true)); + var newEntities = toArray.filter(predicate(false)).map(function (id) { + var _a; + var newStateObj = typeof newState === 'function' ? newState({}) : newState; + var entity = isFunction(onCreate) ? onCreate(id, newStateObj) : newStateObj; + var withId = __assign({}, entity, (_a = {}, _a[_this.idKey] = id, _a)); + if (isClassBased) { + return new baseClass(withId); + } + return withId; + }); + // it can be any of the three types + this.update(updateIds, newState); + this.add(newEntities); + logAction('Upsert Entity'); + }; + /** + * + * Upsert entity collection (idKey must be present) + * + * @example + * + * store.upsertMany([ { id: 1 }, { id: 2 }]); + * + * store.upsertMany([ { id: 1 }, { id: 2 }], { loading: true }); + * store.upsertMany([ { id: 1 }, { id: 2 }], { baseClass: Todo }); + * + */ + EntityStore.prototype.upsertMany = function (entities, options) { + if (options === void 0) { options = {}; } + var e_1, _a; + var addedIds = []; + var updatedIds = []; + var updatedEntities = {}; + try { + // Update the state directly to optimize performance + for (var entities_1 = __values(entities), entities_1_1 = entities_1.next(); !entities_1_1.done; entities_1_1 = entities_1.next()) { + var entity = entities_1_1.value; + var withPreCheckHook = this.akitaPreCheckEntity(entity); + var id = withPreCheckHook[this.idKey]; + if (hasEntity(this.entities, id)) { + var prev = this._value().entities[id]; + var merged = __assign({}, this._value().entities[id], withPreCheckHook); + var next = options.baseClass ? new options.baseClass(merged) : merged; + var withHook = this.akitaPreUpdateEntity(prev, next); + var nextId = withHook[this.idKey]; + updatedEntities[nextId] = withHook; + updatedIds.push(nextId); + } + else { + var newEntity = options.baseClass ? new options.baseClass(withPreCheckHook) : withPreCheckHook; + var withHook = this.akitaPreAddEntity(newEntity); + var nextId = withHook[this.idKey]; + addedIds.push(nextId); + updatedEntities[nextId] = withHook; + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (entities_1_1 && !entities_1_1.done && (_a = entities_1.return)) _a.call(entities_1); + } + finally { if (e_1) throw e_1.error; } + } + logAction('Upsert Many'); + this._setState(function (state) { return (__assign({}, state, { ids: addedIds.length ? __spread(state.ids, addedIds) : state.ids, entities: __assign({}, state.entities, updatedEntities), loading: !!options.loading })); }); + updatedIds.length && this.entityActions.next({ type: EntityActions.Update, ids: updatedIds }); + addedIds.length && this.entityActions.next({ type: EntityActions.Add, ids: addedIds }); + if (addedIds.length && this.hasUIStore()) { + this.handleUICreation(true); + } + }; + /** + * + * Replace one or more entities (except the id property) + * + * + * @example + * + * this.store.replace(5, newEntity) + * this.store.replace([1,2,3], newEntity) + */ + EntityStore.prototype.replace = function (ids, newState) { + var e_2, _a; + var toArray = coerceArray(ids); + if (isEmpty(toArray)) + return; + var replaced = {}; + try { + for (var toArray_1 = __values(toArray), toArray_1_1 = toArray_1.next(); !toArray_1_1.done; toArray_1_1 = toArray_1.next()) { + var id = toArray_1_1.value; + newState[this.idKey] = id; + replaced[id] = newState; + } + } + catch (e_2_1) { e_2 = { error: e_2_1 }; } + finally { + try { + if (toArray_1_1 && !toArray_1_1.done && (_a = toArray_1.return)) _a.call(toArray_1); + } + finally { if (e_2) throw e_2.error; } + } + setAction('Replace Entity', ids); + this._setState(function (state) { return (__assign({}, state, { entities: __assign({}, state.entities, replaced) })); }); + }; + /** + * + * Move entity inside the collection + * + * + * @example + * + * this.store.move(fromIndex, toIndex) + */ + EntityStore.prototype.move = function (from, to) { + var ids = this.ids.slice(); + ids.splice(to < 0 ? ids.length + to : to, 0, ids.splice(from, 1)[0]); + setAction('Move Entity'); + this._setState(function (state) { return (__assign({}, state, { + // Change the entities reference so that selectAll emit + entities: __assign({}, state.entities), ids: ids })); }); + }; + EntityStore.prototype.remove = function (idsOrFn) { + var _this = this; + if (isEmpty(this.ids)) + return; + var idPassed = isDefined(idsOrFn); + // null means remove all + var ids = []; + if (isFunction(idsOrFn)) { + ids = this.ids.filter(function (entityId) { return idsOrFn(_this.entities[entityId]); }); + } + else { + ids = idPassed ? coerceArray(idsOrFn) : this.ids; + } + if (isEmpty(ids)) + return; + setAction('Remove Entity', ids); + this._setState(function (state) { return removeEntities({ state: state, ids: ids }); }); + if (!idPassed) { + this.setHasCache(false); + } + this.handleUIRemove(ids); + this.entityActions.next({ type: EntityActions.Remove, ids: ids }); + }; + /** + * + * Update the active entity + * + * @example + * + * this.store.updateActive({ completed: true }) + * this.store.updateActive(active => { + * return { + * config: { + * ..active.config, + * date + * } + * } + * }) + */ + EntityStore.prototype.updateActive = function (newStateOrCallback) { + var ids = coerceArray(this.active); + setAction('Update Active', ids); + this.update(ids, newStateOrCallback); + }; + EntityStore.prototype.setActive = function (idOrOptions) { + var active = getActiveEntities(idOrOptions, this.ids, this.active); + if (active === undefined) { + return; + } + setAction('Set Active', active); + this._setActive(active); + }; + /** + * Add active entities + * + * @example + * + * store.addActive(2); + * store.addActive([3, 4, 5]); + */ + EntityStore.prototype.addActive = function (ids) { + var _this = this; + var toArray = coerceArray(ids); + if (isEmpty(toArray)) + return; + var everyExist = toArray.every(function (id) { return _this.active.indexOf(id) > -1; }); + if (everyExist) + return; + setAction('Add Active', ids); + this._setState(function (state) { + /** Protect against case that one of the items in the array exist */ + var uniques = Array.from(new Set(__spread(state.active, toArray))); + return __assign({}, state, { active: uniques }); + }); + }; + /** + * Remove active entities + * + * @example + * + * store.removeActive(2) + * store.removeActive([3, 4, 5]) + */ + EntityStore.prototype.removeActive = function (ids) { + var _this = this; + var toArray = coerceArray(ids); + if (isEmpty(toArray)) + return; + var someExist = toArray.some(function (id) { return _this.active.indexOf(id) > -1; }); + if (!someExist) + return; + setAction('Remove Active', ids); + this._setState(function (state) { + return __assign({}, state, { active: Array.isArray(state.active) ? state.active.filter(function (currentId) { return toArray.indexOf(currentId) === -1; }) : null }); + }); + }; + /** + * Toggle active entities + * + * @example + * + * store.toggle(2) + * store.toggle([3, 4, 5]) + */ + EntityStore.prototype.toggleActive = function (ids) { + var _this = this; + var toArray = coerceArray(ids); + var filterExists = function (remove) { return function (id) { return _this.active.includes(id) === remove; }; }; + var remove = toArray.filter(filterExists(true)); + var add = toArray.filter(filterExists(false)); + this.removeActive(remove); + this.addActive(add); + logAction('Toggle Active'); + }; + /** + * + * Create sub UI store for managing Entity's UI state + * + * @example + * + * export type ProductUI = { + * isLoading: boolean; + * isOpen: boolean + * } + * + * interface ProductsUIState extends EntityState {} + * + * export class ProductsStore EntityStore { + * ui: EntityUIStore; + * + * constructor() { + * super(); + * this.createUIStore(); + * } + * + * } + */ + EntityStore.prototype.createUIStore = function (initialState, storeConfig) { + if (initialState === void 0) { initialState = {}; } + if (storeConfig === void 0) { storeConfig = {}; } + var defaults = { name: "UI/" + this.storeName, idKey: this.idKey }; + this.ui = new EntityUIStore(initialState, __assign({}, defaults, storeConfig)); + return this.ui; + }; + // @internal + EntityStore.prototype.destroy = function () { + _super.prototype.destroy.call(this); + if (this.ui instanceof EntityStore) { + this.ui.destroy(); + } + this.entityActions.complete(); + }; + // @internal + EntityStore.prototype.akitaPreUpdateEntity = function (_, nextEntity) { + return nextEntity; + }; + // @internal + EntityStore.prototype.akitaPreAddEntity = function (newEntity) { + return newEntity; + }; + // @internal + EntityStore.prototype.akitaPreCheckEntity = function (newEntity) { + return newEntity; + }; + Object.defineProperty(EntityStore.prototype, "ids", { + get: function () { + return this._value().ids; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EntityStore.prototype, "entities", { + get: function () { + return this._value().entities; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(EntityStore.prototype, "active", { + get: function () { + return this._value().active; + }, + enumerable: true, + configurable: true + }); + EntityStore.prototype._setActive = function (ids) { + this._setState(function (state) { + return __assign({}, state, { active: ids }); + }); + }; + EntityStore.prototype.handleUICreation = function (add) { + var _this = this; + if (add === void 0) { add = false; } + var ids = this.ids; + var isFunc = isFunction(this.ui._akitaCreateEntityFn); + var uiEntities; + var createFn = function (id) { + var _a; + var current = _this.entities[id]; + var ui = isFunc ? _this.ui._akitaCreateEntityFn(current) : _this.ui._akitaCreateEntityFn; + return __assign((_a = {}, _a[_this.idKey] = current[_this.idKey], _a), ui); + }; + if (add) { + uiEntities = this.ids.filter(function (id) { return isUndefined(_this.ui.entities[id]); }).map(createFn); + } + else { + uiEntities = ids.map(createFn); + } + add ? this.ui.add(uiEntities) : this.ui.set(uiEntities); + }; + EntityStore.prototype.hasInitialUIState = function () { + return this.hasUIStore() && isUndefined(this.ui._akitaCreateEntityFn) === false; + }; + EntityStore.prototype.handleUIRemove = function (ids) { + if (this.hasUIStore()) { + this.ui.remove(ids); + } + }; + EntityStore.prototype.hasUIStore = function () { + return this.ui instanceof EntityUIStore; + }; + var _b; + __decorate$2([ + transaction(), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object, Object, Object, Object]), + __metadata("design:returntype", void 0) + ], EntityStore.prototype, "upsert", null); + __decorate$2([ + transaction(), + __metadata("design:type", Function), + __metadata("design:paramtypes", [typeof (_b = typeof T !== "undefined" && T) === "function" ? _b : Object]), + __metadata("design:returntype", void 0) + ], EntityStore.prototype, "toggleActive", null); + return EntityStore; + }(Store)); + // @internal + var EntityUIStore = /** @class */ (function (_super) { + __extends(EntityUIStore, _super); + function EntityUIStore(initialState, storeConfig) { + if (initialState === void 0) { initialState = {}; } + if (storeConfig === void 0) { storeConfig = {}; } + return _super.call(this, initialState, storeConfig) || this; + } + /** + * + * Set the initial UI entity state. This function will determine the entity's + * initial state when we call `set()` or `add()`. + * + * @example + * + * constructor() { + * super(); + * this.createUIStore().setInitialEntityState(entity => ({ isLoading: false, isOpen: true })); + * this.createUIStore().setInitialEntityState({ isLoading: false, isOpen: true }); + * } + * + */ + EntityUIStore.prototype.setInitialEntityState = function (createFn) { + this._akitaCreateEntityFn = createFn; + }; + return EntityUIStore; + }(EntityStore)); + // @internal + function distinctUntilArrayItemChanged() { + return distinctUntilChanged(function (prevCollection, currentCollection) { + if (prevCollection === currentCollection) { + return true; + } + if (isArray(prevCollection) === false || isArray(currentCollection) === false) { + return false; + } + if (isEmpty(prevCollection) && isEmpty(currentCollection)) { + return true; + } + // if item is new in the current collection but not exist in the prev collection + var hasNewItem = hasChange(currentCollection, prevCollection); + if (hasNewItem) { + return false; + } + var isOneOfItemReferenceChanged = hasChange(prevCollection, currentCollection); + // return false means there is a change and we want to call next() + return isOneOfItemReferenceChanged === false; + }); + } + // @internal + function hasChange(first, second) { + var hasChange = second.some(function (currentItem) { + var oldItem = first.find(function (prevItem) { return prevItem === currentItem; }); + return oldItem === undefined; + }); + return hasChange; + } + + var Order; + (function (Order) { + Order["ASC"] = "asc"; + Order["DESC"] = "desc"; + })(Order || (Order = {})); + // @internal + function compareValues(key, order) { + if (order === void 0) { order = Order.ASC; } + return function (a, b) { + if (!a.hasOwnProperty(key) || !b.hasOwnProperty(key)) { + return 0; + } + var varA = typeof a[key] === 'string' ? a[key].toUpperCase() : a[key]; + var varB = typeof b[key] === 'string' ? b[key].toUpperCase() : b[key]; + var comparison = 0; + if (varA > varB) { + comparison = 1; + } + else if (varA < varB) { + comparison = -1; + } + return order == Order.DESC ? comparison * -1 : comparison; + }; + } + + // @internal + function entitiesToArray(state, options) { + var arr = []; + var ids = state.ids, entities = state.entities; + var filterBy = options.filterBy, limitTo = options.limitTo, sortBy = options.sortBy, sortByOrder = options.sortByOrder; + var _loop_1 = function (i) { + var entity = entities[ids[i]]; + if (!filterBy) { + arr.push(entity); + return "continue"; + } + var toArray = coerceArray(filterBy); + var allPass = toArray.every(function (fn) { return fn(entity, i); }); + if (allPass) { + arr.push(entity); + } + }; + for (var i = 0; i < ids.length; i++) { + _loop_1(i); + } + if (sortBy) { + var _sortBy_1 = isFunction(sortBy) ? sortBy : compareValues(sortBy, sortByOrder); + arr = arr.sort(function (a, b) { return _sortBy_1(a, b, state); }); + } + var length = Math.min(limitTo || arr.length, arr.length); + return length === arr.length ? arr : arr.slice(0, length); + } + + // @internal + function entitiesToMap(state, options) { + var map = {}; + var filterBy = options.filterBy, limitTo = options.limitTo; + var ids = state.ids, entities = state.entities; + if (!filterBy && !limitTo) { + return entities; + } + var hasLimit = isNil(limitTo) === false; + if (filterBy && hasLimit) { + var count = 0; + var _loop_1 = function (i, length_1) { + if (count === limitTo) + return "break"; + var id = ids[i]; + var entity = entities[id]; + var allPass = coerceArray(filterBy).every(function (fn) { return fn(entity, i); }); + if (allPass) { + map[id] = entity; + count++; + } + }; + for (var i = 0, length_1 = ids.length; i < length_1; i++) { + var state_1 = _loop_1(i, length_1); + if (state_1 === "break") + break; + } + } + else { + var finalLength = Math.min(limitTo || ids.length, ids.length); + var _loop_2 = function (i) { + var id = ids[i]; + var entity = entities[id]; + if (!filterBy) { + map[id] = entity; + return "continue"; + } + var allPass = coerceArray(filterBy).every(function (fn) { return fn(entity, i); }); + if (allPass) { + map[id] = entity; + } + }; + for (var i = 0; i < finalLength; i++) { + _loop_2(i); + } + } + return map; + } + + // @internal + function isString(value) { + return typeof value === 'string'; + } + + // @internal + function findEntityByPredicate(predicate, entities) { + var e_1, _a; + try { + for (var _b = __values(Object.keys(entities)), _c = _b.next(); !_c.done; _c = _b.next()) { + var entityId = _c.value; + if (predicate(entities[entityId]) === true) { + return entityId; + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (_c && !_c.done && (_a = _b.return)) _a.call(_b); + } + finally { if (e_1) throw e_1.error; } + } + return undefined; + } + // @internal + function getEntity(id, project) { + return function (entities) { + var entity = entities[id]; + if (isUndefined(entity)) { + return undefined; + } + if (!project) { + return entity; + } + if (isString(project)) { + return entity[project]; + } + return project(entity); + }; + } + + // @internal + function mapSkipUndefined(arr, callbackFn) { + return arr.reduce(function (result, value, index, array) { + var val = callbackFn(value, index, array); + if (val !== undefined) { + result.push(val); + } + return result; + }, []); + } + + var queryConfigKey = 'akitaQueryConfig'; + + function compareKeys(keysOrFuncs) { + return function (prevState, currState) { + var isFns = isFunction(keysOrFuncs[0]); + // Return when they are NOT changed + return keysOrFuncs.some(function (keyOrFunc) { + if (isFns) { + return keyOrFunc(prevState) !== keyOrFunc(currState); + } + return prevState[keyOrFunc] !== currState[keyOrFunc]; + }) === false; + }; + } + + var Query = /** @class */ (function () { + function Query(store) { + this.store = store; + this.__store__ = store; + { + // @internal + __queries__[store.storeName] = this; + } + } + Query.prototype.select = function (project) { + var mapFn; + if (isFunction(project)) { + mapFn = project; + } + else if (isString(project)) { + mapFn = function (state) { return state[project]; }; + } + else if (Array.isArray(project)) { + return this.store + ._select(function (state) { return state; }) + .pipe(distinctUntilChanged(compareKeys(project)), map(function (state) { + if (isFunction(project[0])) { + return project.map(function (func) { return func(state); }); + } + return project.reduce(function (acc, k) { + acc[k] = state[k]; + return acc; + }, {}); + })); + } + else { + mapFn = function (state) { return state; }; + } + return this.store._select(mapFn); + }; + /** + * Select the loading state + * + * @example + * + * this.query.selectLoading().subscribe(isLoading => {}) + */ + Query.prototype.selectLoading = function () { + return this.select(function (state) { return state.loading; }); + }; + /** + * Select the error state + * + * @example + * + * this.query.selectError().subscribe(error => {}) + */ + Query.prototype.selectError = function () { + return this.select(function (state) { return state.error; }); + }; + /** + * Get the store's value + * + * @example + * + * this.query.getValue() + * + */ + Query.prototype.getValue = function () { + return this.store._value(); + }; + /** + * Select the cache state + * + * @example + * + * this.query.selectHasCache().pipe( + * switchMap(hasCache => { + * return hasCache ? of() : http().pipe(res => store.set(res)) + * }) + * ) + */ + Query.prototype.selectHasCache = function () { + return this.store._cache().asObservable(); + }; + /** + * Whether we've cached data + * + * @example + * + * this.query.getHasCache() + * + */ + Query.prototype.getHasCache = function () { + return this.store._cache().value; + }; + Object.defineProperty(Query.prototype, "config", { + // @internal + get: function () { + return this.constructor[queryConfigKey]; + }, + enumerable: true, + configurable: true + }); + return Query; + }()); + + // @internal + function sortByOptions(options, config) { + options.sortBy = options.sortBy || (config && config.sortBy); + options.sortByOrder = options.sortByOrder || (config && config.sortByOrder); + } + + /** + * + * The Entity Query is similar to the general Query, with additional functionality tailored for EntityStores. + * + * class WidgetsQuery extends QueryEntity { + * constructor(protected store: WidgetsStore) { + * super(store); + * } + * } + * + * + * + */ + var QueryEntity = /** @class */ (function (_super) { + __extends(QueryEntity, _super); + function QueryEntity(store, options) { + if (options === void 0) { options = {}; } + var _this = _super.call(this, store) || this; + _this.options = options; + _this.__store__ = store; + return _this; + } + QueryEntity.prototype.selectAll = function (options) { + var _this = this; + if (options === void 0) { options = { + asObject: false, + }; } + return this.select(function (state) { return state.entities; }).pipe(map(function () { return _this.getAll(options); })); + }; + QueryEntity.prototype.getAll = function (options) { + if (options === void 0) { options = { asObject: false, filterBy: undefined, limitTo: undefined }; } + if (options.asObject) { + return entitiesToMap(this.getValue(), options); + } + sortByOptions(options, this.config || this.options); + return entitiesToArray(this.getValue(), options); + }; + QueryEntity.prototype.selectMany = function (ids, project) { + if (!ids || !ids.length) + return of([]); + return this.select(function (state) { return state.entities; }).pipe(map(function (entities) { return mapSkipUndefined(ids, function (id) { return getEntity(id, project)(entities); }); }), distinctUntilArrayItemChanged()); + }; + QueryEntity.prototype.selectEntity = function (idOrPredicate, project) { + var id = idOrPredicate; + if (isFunction(idOrPredicate)) { + // For performance reason we expect the entity to be in the store + id = findEntityByPredicate(idOrPredicate, this.getValue().entities); + } + return this.select(function (state) { return state.entities; }).pipe(map(getEntity(id, project)), distinctUntilChanged()); + }; + /** + * Get an entity by id + * + * @example + * + * this.query.getEntity(1); + */ + QueryEntity.prototype.getEntity = function (id) { + return this.getValue().entities[id]; + }; + /** + * Select the active entity's id + * + * @example + * + * this.query.selectActiveId() + */ + QueryEntity.prototype.selectActiveId = function () { + return this.select(function (state) { return state.active; }); + }; + /** + * Get the active id + * + * @example + * + * this.query.getActiveId() + */ + QueryEntity.prototype.getActiveId = function () { + return this.getValue().active; + }; + QueryEntity.prototype.selectActive = function (project) { + var _this = this; + if (isArray(this.getActive())) { + return this.selectActiveId().pipe(switchMap(function (ids) { return _this.selectMany(ids, project); })); + } + return this.selectActiveId().pipe(switchMap(function (ids) { return _this.selectEntity(ids, project); })); + }; + QueryEntity.prototype.getActive = function () { + var _this = this; + var activeId = this.getActiveId(); + if (isArray(activeId)) { + return activeId.map(function (id) { return _this.getValue().entities[id]; }); + } + return toBoolean(activeId) ? this.getEntity(activeId) : undefined; + }; + /** + * Select the store's entity collection length + * + * @example + * + * this.query.selectCount() + * this.query.selectCount(entity => entity.completed) + */ + QueryEntity.prototype.selectCount = function (predicate) { + var _this = this; + return this.select(function (state) { return state.entities; }).pipe(map(function () { return _this.getCount(predicate); })); + }; + /** + * Get the store's entity collection length + * + * @example + * + * this.query.getCount() + * this.query.getCount(entity => entity.completed) + */ + QueryEntity.prototype.getCount = function (predicate) { + if (isFunction(predicate)) { + return this.getAll().filter(predicate).length; + } + return this.getValue().ids.length; + }; + QueryEntity.prototype.selectLast = function (project) { + return this.selectAt(function (ids) { return ids[ids.length - 1]; }, project); + }; + QueryEntity.prototype.selectFirst = function (project) { + return this.selectAt(function (ids) { return ids[0]; }, project); + }; + QueryEntity.prototype.selectEntityAction = function (actionOrActions) { + if (isNil(actionOrActions)) { + return this.store.selectEntityAction$; + } + var project = isArray(actionOrActions) ? function (action) { return action; } : function (_a) { + var ids = _a.ids; + return ids; + }; + var actions = coerceArray(actionOrActions); + return this.store.selectEntityAction$.pipe(filter(function (_a) { + var type = _a.type; + return actions.includes(type); + }), map(function (action) { return project(action); })); + }; + QueryEntity.prototype.hasEntity = function (projectOrIds) { + var _this = this; + if (isNil(projectOrIds)) { + return this.getValue().ids.length > 0; + } + if (isFunction(projectOrIds)) { + return this.getAll().some(projectOrIds); + } + if (isArray(projectOrIds)) { + return projectOrIds.every(function (id) { return id in _this.getValue().entities; }); + } + return projectOrIds in this.getValue().entities; + }; + /** + * Returns whether entity store has an active entity + * + * @example + * + * this.query.hasActive() + * this.query.hasActive(3) + * + */ + QueryEntity.prototype.hasActive = function (id) { + var active = this.getValue().active; + var isIdProvided = isDefined(id); + if (Array.isArray(active)) { + if (isIdProvided) { + return active.includes(id); + } + return active.length > 0; + } + return isIdProvided ? active === id : isDefined(active); + }; + /** + * + * Create sub UI query for querying Entity's UI state + * + * @example + * + * + * export class ProductsQuery extends QueryEntity { + * ui: EntityUIQuery; + * + * constructor(protected store: ProductsStore) { + * super(store); + * this.createUIQuery(); + * } + * + * } + */ + QueryEntity.prototype.createUIQuery = function () { + this.ui = new EntityUIQuery(this.__store__.ui); + }; + QueryEntity.prototype.selectAt = function (mapFn, project) { + var _this = this; + return this.select(function (state) { return state.ids; }).pipe(map(mapFn), distinctUntilChanged(), switchMap(function (id) { return _this.selectEntity(id, project); })); + }; + return QueryEntity; + }(Query)); + // @internal + var EntityUIQuery = /** @class */ (function (_super) { + __extends(EntityUIQuery, _super); + function EntityUIQuery(store) { + return _super.call(this, store) || this; + } + return EntityUIQuery; + }(QueryEntity)); + + /** + * @example + * + * query.selectEntity(2).pipe(filterNil) + */ + var filterNil = function (source) { return source.pipe(filter(function (value) { return value !== null && typeof value !== 'undefined'; })); }; + + /** + * @internal + * + * @example + * + * getValue(state, 'todos.ui') + * + */ + function getValue(obj, prop) { + /** return the whole state */ + if (prop.split('.').length === 1) { + return obj; + } + var removeStoreName = prop + .split('.') + .slice(1) + .join('.'); + return removeStoreName.split('.').reduce(function (acc, part) { return acc && acc[part]; }, obj); + } + + /** + * @internal + * + * @example + * setValue(state, 'todos.ui', { filter: {} }) + */ + function setValue(obj, prop, val) { + var split = prop.split('.'); + if (split.length === 1) { + return __assign({}, obj, val); + } + obj = __assign({}, obj); + var lastIndex = split.length - 2; + var removeStoreName = prop.split('.').slice(1); + removeStoreName.reduce(function (acc, part, index) { + if (index !== lastIndex) { + acc[part] = __assign({}, acc[part]); + return acc && acc[part]; + } + acc[part] = Array.isArray(acc[part]) || !isObject(acc[part]) ? val : __assign({}, acc[part], val); + return acc && acc[part]; + }, obj); + return obj; + } + new ReplaySubject(1); + + var AkitaPlugin = /** @class */ (function () { + function AkitaPlugin(query, config) { + this.query = query; + } + /** This method is responsible for getting access to the query. */ + AkitaPlugin.prototype.getQuery = function () { + return this.query; + }; + /** This method is responsible for getting access to the store. */ + AkitaPlugin.prototype.getStore = function () { + return this.getQuery().__store__; + }; + /** This method is responsible tells whether the plugin is entityBased or not. */ + AkitaPlugin.prototype.isEntityBased = function (entityId) { + return toBoolean(entityId); + }; + /** This method is responsible for selecting the source; it can be the whole store or one entity. */ + AkitaPlugin.prototype.selectSource = function (entityId, property) { + var _this = this; + if (this.isEntityBased(entityId)) { + return this.getQuery().selectEntity(entityId).pipe(filterNil); + } + if (property) { + return this.getQuery().select(function (state) { return getValue(state, _this.withStoreName(property)); }); + } + return this.getQuery().select(); + }; + AkitaPlugin.prototype.getSource = function (entityId, property) { + if (this.isEntityBased(entityId)) { + return this.getQuery().getEntity(entityId); + } + var state = this.getQuery().getValue(); + if (property) { + return getValue(state, this.withStoreName(property)); + } + return state; + }; + AkitaPlugin.prototype.withStoreName = function (prop) { + return this.storeName + "." + prop; + }; + Object.defineProperty(AkitaPlugin.prototype, "storeName", { + get: function () { + return this.getStore().storeName; + }, + enumerable: true, + configurable: true + }); + /** This method is responsible for updating the store or one entity; it can be the whole store or one entity. */ + AkitaPlugin.prototype.updateStore = function (newState, entityId, property) { + var _this = this; + if (this.isEntityBased(entityId)) { + this.getStore().update(entityId, newState); + } + else { + if (property) { + this.getStore()._setState(function (state) { + return setValue(state, _this.withStoreName(property), newState); + }); + return; + } + this.getStore()._setState(function (state) { return (__assign({}, state, newState)); }); + } + }; + /** + * Function to invoke upon reset + */ + AkitaPlugin.prototype.onReset = function (fn) { + var _this = this; + var original = this.getStore().reset; + this.getStore().reset = function () { + var params = []; + for (var _i = 0; _i < arguments.length; _i++) { + params[_i] = arguments[_i]; + } + /** It should run after the plugin destroy method */ + setTimeout(function () { + original.apply(_this.getStore(), params); + fn(); + }); + }; + }; + return AkitaPlugin; + }()); + + var paginatorDefaults = { + pagesControls: false, + range: false, + startWith: 1, + cacheTimeout: undefined, + clearStoreWithCache: true + }; + /** @class */ ((function (_super) { + __extends(PaginatorPlugin, _super); + function PaginatorPlugin(query, config) { + if (config === void 0) { config = {}; } + var _this = _super.call(this, query, { + resetFn: function () { + _this.initial = false; + _this.destroy({ clearCache: true, currentPage: 1 }); + } + }) || this; + _this.query = query; + _this.config = config; + /** Save current filters, sorting, etc. in cache */ + _this.metadata = new Map(); + _this.pages = new Map(); + _this.pagination = { + currentPage: 1, + perPage: 0, + total: 0, + lastPage: 0, + data: [] + }; + /** + * When the user navigates to a different page and return + * we don't want to call `clearCache` on first time. + */ + _this.initial = true; + /** + * Proxy to the query loading + */ + _this.isLoading$ = _this.query.selectLoading().pipe(delay(0)); + _this.config = Object.assign(paginatorDefaults, config); + var _a = _this.config, startWith = _a.startWith, cacheTimeout = _a.cacheTimeout; + _this.page = new BehaviorSubject(startWith); + if (isObservable(cacheTimeout)) { + _this.clearCacheSubscription = cacheTimeout.subscribe(function () { return _this.clearCache(); }); + } + return _this; + } + Object.defineProperty(PaginatorPlugin.prototype, "pageChanges", { + /** + * Listen to page changes + */ + get: function () { + return this.page.asObservable(); + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(PaginatorPlugin.prototype, "currentPage", { + /** + * Get the current page number + */ + get: function () { + return this.pagination.currentPage; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(PaginatorPlugin.prototype, "isFirst", { + /** + * Check if current page is the first one + */ + get: function () { + return this.currentPage === 1; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(PaginatorPlugin.prototype, "isLast", { + /** + * Check if current page is the last one + */ + get: function () { + return this.currentPage === this.pagination.lastPage; + }, + enumerable: true, + configurable: true + }); + /** + * Whether to generate an array of pages for *ngFor + * [1, 2, 3, 4] + */ + PaginatorPlugin.prototype.withControls = function () { + this.config.pagesControls = true; + return this; + }; + /** + * Whether to generate the `from` and `to` keys + * [1, 2, 3, 4] + */ + PaginatorPlugin.prototype.withRange = function () { + this.config.range = true; + return this; + }; + /** + * Set the loading state + */ + PaginatorPlugin.prototype.setLoading = function (value) { + if (value === void 0) { value = true; } + this.getStore().setLoading(value); + }; + /** + * Update the pagination object and add the page + */ + PaginatorPlugin.prototype.update = function (response) { + this.pagination = response; + this.addPage(response.data); + }; + /** + * + * Set the ids and add the page to store + */ + PaginatorPlugin.prototype.addPage = function (data) { + var _this = this; + this.pages.set(this.currentPage, { ids: data.map(function (entity) { return entity[_this.getStore().idKey]; }) }); + this.getStore().upsertMany(data); + }; + /** + * Clear the cache. + */ + PaginatorPlugin.prototype.clearCache = function (options) { + if (options === void 0) { options = {}; } + if (!this.initial) { + logAction('@Pagination - Clear Cache'); + if (options.clearStore !== false && (this.config.clearStoreWithCache || options.clearStore)) { + this.getStore().remove(); + } + this.pages = new Map(); + this.metadata = new Map(); + } + this.initial = false; + }; + PaginatorPlugin.prototype.clearPage = function (page) { + this.pages.delete(page); + }; + /** + * Clear the cache timeout and optionally the pages + */ + PaginatorPlugin.prototype.destroy = function (_a) { + var _b = _a === void 0 ? {} : _a, clearCache = _b.clearCache, currentPage = _b.currentPage; + if (this.clearCacheSubscription) { + this.clearCacheSubscription.unsubscribe(); + } + if (clearCache) { + this.clearCache(); + } + if (!isUndefined(currentPage)) { + this.setPage(currentPage); + } + this.initial = true; + }; + /** + * Whether the provided page is active + */ + PaginatorPlugin.prototype.isPageActive = function (page) { + return this.currentPage === page; + }; + /** + * Set the current page + */ + PaginatorPlugin.prototype.setPage = function (page) { + if (page !== this.currentPage || !this.hasPage(page)) { + this.page.next((this.pagination.currentPage = page)); + } + }; + /** + * Increment current page + */ + PaginatorPlugin.prototype.nextPage = function () { + if (this.currentPage !== this.pagination.lastPage) { + this.setPage(this.pagination.currentPage + 1); + } + }; + /** + * Decrement current page + */ + PaginatorPlugin.prototype.prevPage = function () { + if (this.pagination.currentPage > 1) { + this.setPage(this.pagination.currentPage - 1); + } + }; + /** + * Set current page to last + */ + PaginatorPlugin.prototype.setLastPage = function () { + this.setPage(this.pagination.lastPage); + }; + /** + * Set current page to first + */ + PaginatorPlugin.prototype.setFirstPage = function () { + this.setPage(1); + }; + /** + * Check if page exists in cache + */ + PaginatorPlugin.prototype.hasPage = function (page) { + return this.pages.has(page); + }; + /** + * Get the current page if it's in cache, otherwise invoke the request + */ + PaginatorPlugin.prototype.getPage = function (req) { + var _this = this; + var page = this.pagination.currentPage; + if (this.hasPage(page)) { + return this.selectPage(page); + } + else { + this.setLoading(true); + return from(req()).pipe(switchMap(function (config) { + page = config.currentPage; + applyTransaction(function () { + _this.setLoading(false); + _this.update(config); + }); + return _this.selectPage(page); + })); + } + }; + PaginatorPlugin.prototype.getQuery = function () { + return this.query; + }; + PaginatorPlugin.prototype.refreshCurrentPage = function () { + if (isNil(this.currentPage) === false) { + this.clearPage(this.currentPage); + this.setPage(this.currentPage); + } + }; + PaginatorPlugin.prototype.getFrom = function () { + if (this.isFirst) { + return 1; + } + return (this.currentPage - 1) * this.pagination.perPage + 1; + }; + PaginatorPlugin.prototype.getTo = function () { + if (this.isLast) { + return this.pagination.total; + } + return this.currentPage * this.pagination.perPage; + }; + /** + * Select the page + */ + PaginatorPlugin.prototype.selectPage = function (page) { + var _this = this; + return this.query.selectAll({ asObject: true }).pipe(take(1), map(function (entities) { + var response = __assign({}, _this.pagination, { data: _this.pages.get(page).ids.map(function (id) { return entities[id]; }) }); + var _a = _this.config, range = _a.range, pagesControls = _a.pagesControls; + /** If no total - calc it */ + if (isNaN(_this.pagination.total)) { + if (response.lastPage === 1) { + response.total = response.data ? response.data.length : 0; + } + else { + response.total = response.perPage * response.lastPage; + } + _this.pagination.total = response.total; + } + if (range) { + response.from = _this.getFrom(); + response.to = _this.getTo(); + } + if (pagesControls) { + response.pageControls = generatePages(_this.pagination.total, _this.pagination.perPage); + } + return response; + })); + }; + __decorate$2([ + action('@Pagination - New Page'), + __metadata("design:type", Function), + __metadata("design:paramtypes", [Object]), + __metadata("design:returntype", void 0) + ], PaginatorPlugin.prototype, "update", null); + return PaginatorPlugin; + })(AkitaPlugin)); + /** + * Generate an array so we can ngFor them to navigate between pages + */ + function generatePages(total, perPage) { + var len = Math.ceil(total / perPage); + var arr = []; + for (var i = 0; i < len; i++) { + arr.push(i + 1); + } + return arr; + } + + /** @class */ ((function (_super) { + __extends(PersistNgFormPlugin, _super); + function PersistNgFormPlugin(query, factoryFnOrPath, params) { + if (params === void 0) { params = {}; } + var _this = _super.call(this, query) || this; + _this.query = query; + _this.factoryFnOrPath = factoryFnOrPath; + _this.params = params; + _this.params = __assign({ debounceTime: 300, formKey: 'akitaForm', emitEvent: false, arrControlFactory: function (v) { return _this.builder.control(v); } }, params); + _this.isRootKeys = toBoolean(factoryFnOrPath) === false; + _this.isKeyBased = isString(factoryFnOrPath) || _this.isRootKeys; + return _this; + } + PersistNgFormPlugin.prototype.setForm = function (form, builder) { + this.form = form; + this.builder = builder; + this.activate(); + return this; + }; + PersistNgFormPlugin.prototype.reset = function (initialState) { + var _this = this; + var _a; + var value; + if (initialState) { + value = initialState; + } + else { + value = this.isKeyBased ? this.initialValue : this.factoryFnOrPath(); + } + if (this.isKeyBased) { + Object.keys(this.initialValue).forEach(function (stateKey) { + var value = _this.initialValue[stateKey]; + if (Array.isArray(value) && _this.builder) { + var formArray = _this.form.controls[stateKey]; + _this.cleanArray(formArray); + value.forEach(function (v, i) { + _this.form.get(stateKey).insert(i, _this.params.arrControlFactory(v)); + }); + } + }); + } + this.form.patchValue(value, { emitEvent: this.params.emitEvent }); + var storeValue = this.isKeyBased ? setValue(this.getQuery().getValue(), this.getStore().storeName + "." + this.factoryFnOrPath, value) : (_a = {}, _a[this.params.formKey] = value, _a); + this.updateStore(storeValue); + }; + PersistNgFormPlugin.prototype.cleanArray = function (control) { + while (control.length !== 0) { + control.removeAt(0); + } + }; + PersistNgFormPlugin.prototype.resolveInitialValue = function (formValue, root) { + var _this = this; + if (!formValue) + return; + return Object.keys(formValue).reduce(function (acc, stateKey) { + var value = root[stateKey]; + if (Array.isArray(value) && _this.builder) { + var factory_1 = _this.params.arrControlFactory; + _this.cleanArray(_this.form.get(stateKey)); + value.forEach(function (v, i) { + _this.form.get(stateKey).insert(i, factory_1(v)); + }); + } + acc[stateKey] = root[stateKey]; + return acc; + }, {}); + }; + PersistNgFormPlugin.prototype.activate = function () { + var _this = this; + var _a; + var path; + if (this.isKeyBased) { + if (this.isRootKeys) { + this.initialValue = this.resolveInitialValue(this.form.value, this.getQuery().getValue()); + this.form.patchValue(this.initialValue, { emitEvent: this.params.emitEvent }); + } + else { + path = this.getStore().storeName + "." + this.factoryFnOrPath; + var root = getValue(this.getQuery().getValue(), path); + this.initialValue = this.resolveInitialValue(root, root); + this.form.patchValue(this.initialValue, { emitEvent: this.params.emitEvent }); + } + } + else { + if (!this.getQuery().getValue()[this.params.formKey]) { + logAction('@PersistNgFormPlugin activate'); + this.updateStore((_a = {}, _a[this.params.formKey] = this.factoryFnOrPath(), _a)); + } + var value = this.getQuery().getValue()[this.params.formKey]; + this.form.patchValue(value); + } + this.formChanges = this.form.valueChanges.pipe(debounceTime(this.params.debounceTime)).subscribe(function (value) { + logAction('@PersistForm - Update'); + var newState; + if (_this.isKeyBased) { + if (_this.isRootKeys) { + newState = function (state) { return (__assign({}, state, value)); }; + } + else { + newState = function (state) { return setValue(state, path, value); }; + } + } + else { + newState = function () { + var _a; + return (_a = {}, _a[_this.params.formKey] = value, _a); + }; + } + _this.updateStore(newState(_this.getQuery().getValue())); + }); + }; + PersistNgFormPlugin.prototype.destroy = function () { + this.formChanges && this.formChanges.unsubscribe(); + this.form = null; + this.builder = null; + }; + return PersistNgFormPlugin; + })(AkitaPlugin)); + + /** + * Each plugin that wants to add support for entities should extend this interface. + */ + var EntityCollectionPlugin = /** @class */ (function () { + function EntityCollectionPlugin(query, entityIds) { + this.query = query; + this.entityIds = entityIds; + this.entities = new Map(); + } + /** + * Get the entity plugin instance. + */ + EntityCollectionPlugin.prototype.getEntity = function (id) { + return this.entities.get(id); + }; + /** + * Whether the entity plugin exist. + */ + EntityCollectionPlugin.prototype.hasEntity = function (id) { + return this.entities.has(id); + }; + /** + * Remove the entity plugin instance. + */ + EntityCollectionPlugin.prototype.removeEntity = function (id) { + this.destroy(id); + return this.entities.delete(id); + }; + /** + * Set the entity plugin instance. + */ + EntityCollectionPlugin.prototype.createEntity = function (id, plugin) { + return this.entities.set(id, plugin); + }; + /** + * If the user passes `entityIds` we take them; otherwise, we take all. + */ + EntityCollectionPlugin.prototype.getIds = function () { + return isUndefined(this.entityIds) ? this.query.getValue().ids : coerceArray(this.entityIds); + }; + /** + * When you call one of the plugin methods, you can pass id/ids or undefined which means all. + */ + EntityCollectionPlugin.prototype.resolvedIds = function (ids) { + return isUndefined(ids) ? this.getIds() : coerceArray(ids); + }; + /** + * Call this method when you want to activate the plugin on init or when you need to listen to add/remove of entities dynamically. + * + * For example in your plugin you may do the following: + * + * this.query.select(state => state.ids).pipe(skip(1)).subscribe(ids => this.activate(ids)); + */ + EntityCollectionPlugin.prototype.rebase = function (ids, actions) { + var _this = this; + if (actions === void 0) { actions = {}; } + /** + * + * If the user passes `entityIds` & we have new ids check if we need to add/remove instances. + * + * This phase will be called only upon update. + */ + if (toBoolean(ids)) { + /** + * Which means all + */ + if (isUndefined(this.entityIds)) { + for (var i = 0, len = ids.length; i < len; i++) { + var entityId = ids[i]; + if (this.hasEntity(entityId) === false) { + isFunction(actions.beforeAdd) && actions.beforeAdd(entityId); + var plugin = this.instantiatePlugin(entityId); + this.entities.set(entityId, plugin); + isFunction(actions.afterAdd) && actions.afterAdd(plugin); + } + } + this.entities.forEach(function (plugin, entityId) { + if (ids.indexOf(entityId) === -1) { + isFunction(actions.beforeRemove) && actions.beforeRemove(plugin); + _this.removeEntity(entityId); + } + }); + } + else { + /** + * Which means the user passes specific ids + */ + var _ids = coerceArray(this.entityIds); + for (var i = 0, len = _ids.length; i < len; i++) { + var entityId = _ids[i]; + /** The Entity in current ids and doesn't exist, add it. */ + if (ids.indexOf(entityId) > -1 && this.hasEntity(entityId) === false) { + isFunction(actions.beforeAdd) && actions.beforeAdd(entityId); + var plugin = this.instantiatePlugin(entityId); + this.entities.set(entityId, plugin); + isFunction(actions.afterAdd) && actions.afterAdd(plugin); + } + else { + this.entities.forEach(function (plugin, entityId) { + /** The Entity not in current ids and exists, remove it. */ + if (ids.indexOf(entityId) === -1 && _this.hasEntity(entityId) === true) { + isFunction(actions.beforeRemove) && actions.beforeRemove(plugin); + _this.removeEntity(entityId); + } + }); + } + } + } + } + else { + /** + * Otherwise, start with the provided ids or all. + */ + this.getIds().forEach(function (id) { + if (!_this.hasEntity(id)) + _this.createEntity(id, _this.instantiatePlugin(id)); + }); + } + }; + /** + * Listen for add/remove entities. + */ + EntityCollectionPlugin.prototype.selectIds = function () { + return this.query.select(function (state) { return state.ids; }); + }; + /** + * Base method for activation, you can override it if you need to. + */ + EntityCollectionPlugin.prototype.activate = function (ids) { + this.rebase(ids); + }; + /** + * Loop over each id and invoke the plugin method. + */ + EntityCollectionPlugin.prototype.forEachId = function (ids, cb) { + var _ids = this.resolvedIds(ids); + for (var i = 0, len = _ids.length; i < len; i++) { + var id = _ids[i]; + if (this.hasEntity(id)) { + cb(this.getEntity(id)); + } + } + }; + return EntityCollectionPlugin; + }()); + + var StateHistoryPlugin = /** @class */ (function (_super) { + __extends(StateHistoryPlugin, _super); + function StateHistoryPlugin(query, params, _entityId) { + if (params === void 0) { params = {}; } + var _this = _super.call(this, query, { + resetFn: function () { return _this.clear(); } + }) || this; + _this.query = query; + _this.params = params; + _this._entityId = _entityId; + /** Allow skipping an update from outside */ + _this.skip = false; + _this.history = { + past: [], + present: null, + future: [] + }; + /** Skip the update when redo/undo */ + _this.skipUpdate = false; + params.maxAge = !!params.maxAge ? params.maxAge : 10; + params.comparator = params.comparator || (function () { return true; }); + _this.activate(); + return _this; + } + Object.defineProperty(StateHistoryPlugin.prototype, "hasPast$", { + /** + * Observable stream representing whether the history plugin has an available past + * + */ + get: function () { + return this._hasPast$; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(StateHistoryPlugin.prototype, "hasFuture$", { + /** + * Observable stream representing whether the history plugin has an available future + * + */ + get: function () { + return this._hasFuture$; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(StateHistoryPlugin.prototype, "hasPast", { + get: function () { + return this.history.past.length > 0; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(StateHistoryPlugin.prototype, "hasFuture", { + get: function () { + return this.history.future.length > 0; + }, + enumerable: true, + configurable: true + }); + Object.defineProperty(StateHistoryPlugin.prototype, "property", { + get: function () { + return this.params.watchProperty; + }, + enumerable: true, + configurable: true + }); + /* Updates the hasPast$ hasFuture$ observables*/ + StateHistoryPlugin.prototype.updateHasHistory = function () { + this.hasFutureSubject.next(this.hasFuture); + this.hasPastSubject.next(this.hasPast); + }; + StateHistoryPlugin.prototype.activate = function () { + var _this = this; + this.hasPastSubject = new BehaviorSubject(false); + this._hasPast$ = this.hasPastSubject.asObservable().pipe(distinctUntilChanged()); + this.hasFutureSubject = new BehaviorSubject(false); + this._hasFuture$ = this.hasFutureSubject.asObservable().pipe(distinctUntilChanged()); + this.history.present = this.getSource(this._entityId, this.property); + this.subscription = this + .selectSource(this._entityId, this.property) + .pipe(pairwise()) + .subscribe(function (_a) { + var _b = __read(_a, 2), past = _b[0], present = _b[1]; + if (_this.skip) { + _this.skip = false; + return; + } + /** + * comparator: (prev, current) => isEqual(prev, current) === false + */ + var shouldUpdate = _this.params.comparator(past, present); + if (!_this.skipUpdate && shouldUpdate) { + if (_this.history.past.length === _this.params.maxAge) { + _this.history.past = _this.history.past.slice(1); + } + _this.history.past = __spread(_this.history.past, [past]); + _this.history.present = present; + _this.updateHasHistory(); + } + }); + }; + StateHistoryPlugin.prototype.undo = function () { + if (this.history.past.length > 0) { + var _a = this.history, past = _a.past, present = _a.present; + var previous = past[past.length - 1]; + this.history.past = past.slice(0, past.length - 1); + this.history.present = previous; + this.history.future = __spread([present], this.history.future); + this.update(); + } + }; + StateHistoryPlugin.prototype.redo = function () { + if (this.history.future.length > 0) { + var _a = this.history, past = _a.past, present = _a.present; + var next = this.history.future[0]; + var newFuture = this.history.future.slice(1); + this.history.past = __spread(past, [present]); + this.history.present = next; + this.history.future = newFuture; + this.update('Redo'); + } + }; + StateHistoryPlugin.prototype.jumpToPast = function (index) { + if (index < 0 || index >= this.history.past.length) + return; + var _a = this.history, past = _a.past, future = _a.future, present = _a.present; + /** + * + * const past = [1, 2, 3, 4, 5]; + * const present = 6; + * const future = [7, 8, 9]; + * const index = 2; + * + * newPast = past.slice(0, index) = [1, 2]; + * newPresent = past[index] = 3; + * newFuture = [...past.slice(index + 1),present, ...future] = [4, 5, 6, 7, 8, 9]; + * + */ + var newPast = past.slice(0, index); + var newFuture = __spread(past.slice(index + 1), [present], future); + var newPresent = past[index]; + this.history.past = newPast; + this.history.present = newPresent; + this.history.future = newFuture; + this.update(); + }; + StateHistoryPlugin.prototype.jumpToFuture = function (index) { + if (index < 0 || index >= this.history.future.length) + return; + var _a = this.history, past = _a.past, future = _a.future, present = _a.present; + /** + * + * const past = [1, 2, 3, 4, 5]; + * const present = 6; + * const future = [7, 8, 9, 10] + * const index = 1 + * + * newPast = [...past, present, ...future.slice(0, index) = [1, 2, 3, 4, 5, 6, 7]; + * newPresent = future[index] = 8; + * newFuture = futrue.slice(index+1) = [9, 10]; + * + */ + var newPast = __spread(past, [present], future.slice(0, index)); + var newPresent = future[index]; + var newFuture = future.slice(index + 1); + this.history.past = newPast; + this.history.present = newPresent; + this.history.future = newFuture; + this.update('Redo'); + }; + /** + * + * jump n steps in the past or forward + * + */ + StateHistoryPlugin.prototype.jump = function (n) { + if (n > 0) + return this.jumpToFuture(n - 1); + if (n < 0) + return this.jumpToPast(this.history.past.length + n); + }; + /** + * Clear the history + * + * @param customUpdateFn Callback function for only clearing part of the history + * + * @example + * + * stateHistory.clear((history) => { + * return { + * past: history.past, + * present: history.present, + * future: [] + * }; + * }); + */ + StateHistoryPlugin.prototype.clear = function (customUpdateFn) { + this.history = isFunction(customUpdateFn) + ? customUpdateFn(this.history) + : { + past: [], + present: null, + future: [] + }; + this.updateHasHistory(); + }; + StateHistoryPlugin.prototype.destroy = function (clearHistory) { + if (clearHistory === void 0) { clearHistory = false; } + if (clearHistory) { + this.clear(); + } + this.subscription.unsubscribe(); + }; + StateHistoryPlugin.prototype.ignoreNext = function () { + this.skip = true; + }; + StateHistoryPlugin.prototype.update = function (action) { + if (action === void 0) { action = 'Undo'; } + this.skipUpdate = true; + logAction("@StateHistory - " + action); + this.updateStore(this.history.present, this._entityId, this.property); + this.updateHasHistory(); + this.skipUpdate = false; + }; + return StateHistoryPlugin; + }(AkitaPlugin)); + + /** @class */ ((function (_super) { + __extends(EntityStateHistoryPlugin, _super); + function EntityStateHistoryPlugin(query, params) { + if (params === void 0) { params = {}; } + var _this = _super.call(this, query, params.entityIds) || this; + _this.query = query; + _this.params = params; + params.maxAge = toBoolean(params.maxAge) ? params.maxAge : 10; + _this.activate(); + _this.selectIds() + .pipe(skip(1)) + .subscribe(function (ids) { return _this.activate(ids); }); + return _this; + } + EntityStateHistoryPlugin.prototype.redo = function (ids) { + this.forEachId(ids, function (e) { return e.redo(); }); + }; + EntityStateHistoryPlugin.prototype.undo = function (ids) { + this.forEachId(ids, function (e) { return e.undo(); }); + }; + EntityStateHistoryPlugin.prototype.hasPast = function (id) { + if (this.hasEntity(id)) { + return this.getEntity(id).hasPast; + } + }; + EntityStateHistoryPlugin.prototype.hasFuture = function (id) { + if (this.hasEntity(id)) { + return this.getEntity(id).hasFuture; + } + }; + EntityStateHistoryPlugin.prototype.jumpToFuture = function (ids, index) { + this.forEachId(ids, function (e) { return e.jumpToFuture(index); }); + }; + EntityStateHistoryPlugin.prototype.jumpToPast = function (ids, index) { + this.forEachId(ids, function (e) { return e.jumpToPast(index); }); + }; + EntityStateHistoryPlugin.prototype.clear = function (ids) { + this.forEachId(ids, function (e) { return e.clear(); }); + }; + EntityStateHistoryPlugin.prototype.destroy = function (ids, clearHistory) { + if (clearHistory === void 0) { clearHistory = false; } + this.forEachId(ids, function (e) { return e.destroy(clearHistory); }); + }; + EntityStateHistoryPlugin.prototype.ignoreNext = function (ids) { + this.forEachId(ids, function (e) { return e.ignoreNext(); }); + }; + EntityStateHistoryPlugin.prototype.instantiatePlugin = function (id) { + return new StateHistoryPlugin(this.query, this.params, id); + }; + return EntityStateHistoryPlugin; + })(EntityCollectionPlugin)); + + var ɵ0 = function (head, current) { return JSON.stringify(head) !== JSON.stringify(current); }; + var dirtyCheckDefaultParams = { + comparator: ɵ0 + }; + function getNestedPath(nestedObj, path) { + var pathAsArray = path.split('.'); + return pathAsArray.reduce(function (obj, key) { return (obj && obj[key] !== 'undefined' ? obj[key] : undefined); }, nestedObj); + } + var DirtyCheckPlugin = /** @class */ (function (_super) { + __extends(DirtyCheckPlugin, _super); + function DirtyCheckPlugin(query, params, _entityId) { + var _this = _super.call(this, query) || this; + _this.query = query; + _this.params = params; + _this._entityId = _entityId; + _this.dirty = new BehaviorSubject(false); + _this.active = false; + _this._reset = new Subject(); + _this.isDirty$ = _this.dirty.asObservable().pipe(distinctUntilChanged()); + _this.reset$ = _this._reset.asObservable(); + _this.params = __assign({}, dirtyCheckDefaultParams, params); + if (_this.params.watchProperty) { + var watchProp = coerceArray(_this.params.watchProperty); + if (query instanceof QueryEntity && watchProp.includes('entities') && !watchProp.includes('ids')) { + watchProp.push('ids'); + } + _this.params.watchProperty = watchProp; + } + return _this; + } + DirtyCheckPlugin.prototype.reset = function (params) { + if (params === void 0) { params = {}; } + var currentValue = this.head; + if (isFunction(params.updateFn)) { + if (this.isEntityBased(this._entityId)) { + currentValue = params.updateFn(this.head, this.getQuery().getEntity(this._entityId)); + } + else { + currentValue = params.updateFn(this.head, this.getQuery().getValue()); + } + } + logAction("@DirtyCheck - Revert"); + this.updateStore(currentValue, this._entityId); + this._reset.next(); + }; + DirtyCheckPlugin.prototype.setHead = function () { + if (!this.active) { + this.activate(); + this.active = true; + } + else { + this.head = this._getHead(); + } + this.updateDirtiness(false); + return this; + }; + DirtyCheckPlugin.prototype.isDirty = function () { + return !!this.dirty.value; + }; + DirtyCheckPlugin.prototype.hasHead = function () { + return !!this.getHead(); + }; + DirtyCheckPlugin.prototype.destroy = function () { + this.head = null; + this.subscription && this.subscription.unsubscribe(); + this._reset && this._reset.complete(); + }; + DirtyCheckPlugin.prototype.isPathDirty = function (path) { + var head = this.getHead(); + var current = this.getQuery().getValue(); + var currentPathValue = getNestedPath(current, path); + var headPathValue = getNestedPath(head, path); + return this.params.comparator(currentPathValue, headPathValue); + }; + DirtyCheckPlugin.prototype.getHead = function () { + return this.head; + }; + DirtyCheckPlugin.prototype.activate = function () { + var _this = this; + this.head = this._getHead(); + /** if we are tracking specific properties select only the relevant ones */ + var source = this.params.watchProperty + ? this.params.watchProperty.map(function (prop) { + return _this.query + .select(function (state) { return state[prop]; }) + .pipe(map(function (val) { return ({ + val: val, + __akitaKey: prop + }); })); + }) + : [this.selectSource(this._entityId)]; + this.subscription = combineLatest.apply(void 0, __spread(source)).pipe(skip(1)) + .subscribe(function (currentState) { + if (isUndefined(_this.head)) + return; + /** __akitaKey is used to determine if we are tracking a specific property or a store change */ + var isChange = currentState.some(function (state) { + var head = state.__akitaKey ? _this.head[state.__akitaKey] : _this.head; + var compareTo = state.__akitaKey ? state.val : state; + return _this.params.comparator(head, compareTo); + }); + _this.updateDirtiness(isChange); + }); + }; + DirtyCheckPlugin.prototype.updateDirtiness = function (isDirty) { + this.dirty.next(isDirty); + }; + DirtyCheckPlugin.prototype._getHead = function () { + var head = this.getSource(this._entityId); + if (this.params.watchProperty) { + head = this.getWatchedValues(head); + } + return head; + }; + DirtyCheckPlugin.prototype.getWatchedValues = function (source) { + return this.params.watchProperty.reduce(function (watched, prop) { + watched[prop] = source[prop]; + return watched; + }, {}); + }; + return DirtyCheckPlugin; + }(AkitaPlugin)); + + /** @class */ ((function (_super) { + __extends(EntityDirtyCheckPlugin, _super); + function EntityDirtyCheckPlugin(query, params) { + if (params === void 0) { params = {}; } + var _this = _super.call(this, query, params.entityIds) || this; + _this.query = query; + _this.params = params; + _this._someDirty = new Subject(); + _this.someDirty$ = merge(_this.query.select(function (state) { return state.entities; }), _this._someDirty.asObservable()).pipe(auditTime(0), map(function () { return _this.checkSomeDirty(); })); + _this.params = __assign({}, dirtyCheckDefaultParams, params); + // TODO lazy activate? + _this.activate(); + _this.selectIds() + .pipe(skip(1)) + .subscribe(function (ids) { + _super.prototype.rebase.call(_this, ids, { afterAdd: function (plugin) { return plugin.setHead(); } }); + }); + return _this; + } + EntityDirtyCheckPlugin.prototype.setHead = function (ids) { + if (this.params.entityIds && ids) { + var toArray_1 = coerceArray(ids); + var someAreWatched = coerceArray(this.params.entityIds).some(function (id) { return toArray_1.indexOf(id) > -1; }); + if (someAreWatched === false) { + return this; + } + } + this.forEachId(ids, function (e) { return e.setHead(); }); + this._someDirty.next(); + return this; + }; + EntityDirtyCheckPlugin.prototype.hasHead = function (id) { + if (this.entities.has(id)) { + var entity = this.getEntity(id); + return entity.hasHead(); + } + return false; + }; + EntityDirtyCheckPlugin.prototype.reset = function (ids, params) { + if (params === void 0) { params = {}; } + this.forEachId(ids, function (e) { return e.reset(params); }); + }; + EntityDirtyCheckPlugin.prototype.isDirty = function (id, asObservable) { + if (asObservable === void 0) { asObservable = true; } + if (this.entities.has(id)) { + var entity = this.getEntity(id); + return asObservable ? entity.isDirty$ : entity.isDirty(); + } + return false; + }; + EntityDirtyCheckPlugin.prototype.someDirty = function () { + return this.checkSomeDirty(); + }; + EntityDirtyCheckPlugin.prototype.isPathDirty = function (id, path) { + if (this.entities.has(id)) { + var head = this.getEntity(id).getHead(); + var current = this.query.getEntity(id); + var currentPathValue = getNestedPath(current, path); + var headPathValue = getNestedPath(head, path); + return this.params.comparator(currentPathValue, headPathValue); + } + return null; + }; + EntityDirtyCheckPlugin.prototype.destroy = function (ids) { + this.forEachId(ids, function (e) { return e.destroy(); }); + /** complete only when the plugin destroys */ + if (!ids) { + this._someDirty.complete(); + } + }; + EntityDirtyCheckPlugin.prototype.instantiatePlugin = function (id) { + return new DirtyCheckPlugin(this.query, this.params, id); + }; + EntityDirtyCheckPlugin.prototype.checkSomeDirty = function () { + var e_1, _a; + var entitiesIds = this.resolvedIds(); + try { + for (var entitiesIds_1 = __values(entitiesIds), entitiesIds_1_1 = entitiesIds_1.next(); !entitiesIds_1_1.done; entitiesIds_1_1 = entitiesIds_1.next()) { + var id = entitiesIds_1_1.value; + if (this.getEntity(id).isDirty()) { + return true; + } + } + } + catch (e_1_1) { e_1 = { error: e_1_1 }; } + finally { + try { + if (entitiesIds_1_1 && !entitiesIds_1_1.done && (_a = entitiesIds_1.return)) _a.call(entitiesIds_1); + } + finally { if (e_1) throw e_1.error; } + } + return false; + }; + return EntityDirtyCheckPlugin; + })(EntityCollectionPlugin)); + + /** + * Generate random guid + * + * @example + * + * { + * id: guid() + * } + * + * @remarks this isn't a GUID, but a 10 char random alpha-num + */ + function guid() { + return Math.random() + .toString(36) + .slice(2); + } + + var _a, _b; + var StoreAction; + (function (StoreAction) { + StoreAction["Update"] = "UPDATE"; + })(StoreAction || (StoreAction = {})); + (_a = {}, + _a[StoreAction.Update] = 'update', + _a); + var EntityStoreAction; + (function (EntityStoreAction) { + EntityStoreAction["Update"] = "UPDATE"; + EntityStoreAction["AddEntities"] = "ADD_ENTITIES"; + EntityStoreAction["SetEntities"] = "SET_ENTITIES"; + EntityStoreAction["UpdateEntities"] = "UPDATE_ENTITIES"; + EntityStoreAction["RemoveEntities"] = "REMOVE_ENTITIES"; + EntityStoreAction["UpsertEntities"] = "UPSERT_ENTITIES"; + EntityStoreAction["UpsertManyEntities"] = "UPSERT_MANY_ENTITIES"; + })(EntityStoreAction || (EntityStoreAction = {})); + (_b = {}, + _b[EntityStoreAction.Update] = 'update', + _b[EntityStoreAction.AddEntities] = 'add', + _b[EntityStoreAction.SetEntities] = 'set', + _b[EntityStoreAction.UpdateEntities] = 'update', + _b[EntityStoreAction.RemoveEntities] = 'remove', + _b[EntityStoreAction.UpsertEntities] = 'upsert', + _b[EntityStoreAction.UpsertManyEntities] = 'upsertMany', + _b); + + function combineQueries(observables) { + return combineLatest(observables).pipe(auditTime(0)); + } + + var immer_cjs_development = {}; + + Object.defineProperty(immer_cjs_development, '__esModule', { value: true }); + + var _ref; + + // Should be no imports here! + // Some things that should be evaluated before all else... + // We only want to know if non-polyfilled symbols are available + var hasSymbol = typeof Symbol !== "undefined" && typeof + /*#__PURE__*/ + Symbol("x") === "symbol"; + var hasMap = typeof Map !== "undefined"; + var hasSet = typeof Set !== "undefined"; + var hasProxies = typeof Proxy !== "undefined" && typeof Proxy.revocable !== "undefined" && typeof Reflect !== "undefined"; + /** + * The sentinel value returned by producers to replace the draft with undefined. + */ + + var NOTHING = hasSymbol ? + /*#__PURE__*/ + Symbol.for("immer-nothing") : (_ref = {}, _ref["immer-nothing"] = true, _ref); + /** + * To let Immer treat your class instances as plain immutable objects + * (albeit with a custom prototype), you must define either an instance property + * or a static property on each of your custom classes. + * + * Otherwise, your class instance will never be drafted, which means it won't be + * safe to mutate in a produce callback. + */ + + var DRAFTABLE = hasSymbol ? + /*#__PURE__*/ + Symbol.for("immer-draftable") : "__$immer_draftable"; + var DRAFT_STATE = hasSymbol ? + /*#__PURE__*/ + Symbol.for("immer-state") : "__$immer_state"; // Even a polyfilled Symbol might provide Symbol.iterator + + var iteratorSymbol = typeof Symbol != "undefined" && Symbol.iterator || "@@iterator"; + + var errors = { + 0: "Illegal state", + 1: "Immer drafts cannot have computed properties", + 2: "This object has been frozen and should not be mutated", + 3: function _(data) { + return "Cannot use a proxy that has been revoked. Did you pass an object from inside an immer function to an async process? " + data; + }, + 4: "An immer producer returned a new value *and* modified its draft. Either return a new value *or* modify the draft.", + 5: "Immer forbids circular references", + 6: "The first or second argument to `produce` must be a function", + 7: "The third argument to `produce` must be a function or undefined", + 8: "First argument to `createDraft` must be a plain object, an array, or an immerable object", + 9: "First argument to `finishDraft` must be a draft returned by `createDraft`", + 10: "The given draft is already finalized", + 11: "Object.defineProperty() cannot be used on an Immer draft", + 12: "Object.setPrototypeOf() cannot be used on an Immer draft", + 13: "Immer only supports deleting array indices", + 14: "Immer only supports setting array indices and the 'length' property", + 15: function _(path) { + return "Cannot apply patch, path doesn't resolve: " + path; + }, + 16: 'Sets cannot have "replace" patches.', + 17: function _(op) { + return "Unsupported patch operation: " + op; + }, + 18: function _(plugin) { + return "The plugin for '" + plugin + "' has not been loaded into Immer. To enable the plugin, import and call `enable" + plugin + "()` when initializing your application."; + }, + 20: "Cannot use proxies if Proxy, Proxy.revocable or Reflect are not available", + 21: function _(thing) { + return "produce can only be called on things that are draftable: plain objects, arrays, Map, Set or classes that are marked with '[immerable]: true'. Got '" + thing + "'"; + }, + 22: function _(thing) { + return "'current' expects a draft, got: " + thing; + }, + 23: function _(thing) { + return "'original' expects a draft, got: " + thing; } - var km, Cm, Dm, Mm, xm, Pm, Rm = Om; - const Lm = (e, t, i) => { - i = Object.assign(Object.assign({}, e), { - method: "GET", - responseType: "text", - extendMaxTTFB: i - }); - return Tu(i.url) ? Pc().load(i, t).pipe(hr(e => ({ - responseText: e.data.response.data.toString(), - responseURL: e.data.response.uri, - stats: e.stats - }))) : Cc(i, t).pipe(hr(([e, t]) => ({ - responseText: e.responseText, - responseURL: e.responseURL, - stats: t - }))) - }, - _m = (e, n, t, i, s, a, o, l, r) => { - const { - url: d, - itemId: u, - mediaOptionId: c, - mediaOptionType: h, - iframes: p = !1 - } = e, f = Lc(e, i); - return Lm({ - url: d, - xhrSetup: t.xhrSetup - }, f, r).pipe(hr(({ - responseText: e, - responseURL: t, - stats: i - }) => { - t || (s.warn("Missing response url. Reusing request url as base url"), t = d); - var r = performance.now(), - e = Om.parseMediaOptionPlaylist(e, t, !0, a, l, u, c, h, s, n, p); - Nc(e.mediaOptionDetails); - var t = performance.now(), - e = e["mediaOptionDetails"], - r = { - playlistLoadTimeMs: i.tload - i.trequest, - playlistParseTimeMs: t - r - }; - return o.setPlaylistSample(r), { - mediaOptionDetails: e, - stats: i - } - }), (m = h, g = c, y = d, e => e.pipe(Vn(e => { - if (e instanceof pc) throw new uc(!1, "Timeout", 0, $.PlaylistTimeoutError, !0, m, g, y); - if (e instanceof oc) throw new uc(!1, e.message, e.code, { - code: e.code, - text: "Playlist Network Error" - }, !1, m, g, y); - throw e - })))); - var m, g, y - }, - Nm = (f, e, m, t, g) => $i(e).pipe(La(e => { - const { - keyTagInfo: t, - isInitSegment: i, - iframe: r, - byteRangeOffset: n - } = f, s = t["method"], { - start: a, - end: o - } = n; - if ("AES-128" !== s) return $i(e); { - !t.uri || t.iv || t.format && "identity" !== t.format || (t.iv = function(t) { - const i = new Uint8Array(16); - for (let e = 12; e < 16; e++) i[e] = t >> 8 * (15 - e) & 255; - return i - }(f.mediaSeqNum)); - const n = e, - s = t.key.buffer, - l = t.iv.buffer, - d = o && (r || i) ? o - a : void 0, - u = !m.enableWebCrypto || !!d, - c = s.slice(0), - h = l.slice(0), - p = { - useJSCrypto: u, - plainTextLength: d - }; - return g.decrypt(c, h, "AES-CBC", n, p) - } - })); - - function Fm(e, t) { - var i = e.fragments, - r = t.mediaSeqNum - e.startSN; - return 0 <= r && r < e.fragments.length && $p(t, i[r]) + }; + function die(error) { + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; } - - function Bm(t, r, n, s, a = !1, o = !1) { - if (Fm(t, r)) { - var l = r.mediaSeqNum - t.startSN; - let i = t.fragments, - e = i[l]; - var { - startDtsTs: d, - startPts: u, - endPts: r - } = r; - a && (i = t.fragments = t.fragments.slice(), e = i[l] = Object.assign({}, e)), e.startDtsTs = d, e.startPts = u, e.endPts = r, !s && void 0 === e.isIframeStart || (e.isIframeStart = s), e.start = n, e.duration = b(r, u); - for (let e = l, t = !0; 0 < e && (o || t); e--) t = Um(i, e, e - 1, s, u.timescale, a); - for (let e = l, t = !0; e < i.length - 1 && (o || t); e++) t = Um(i, e, e + 1, s, u.timescale, a); - l = i[i.length - 1]; - t.totalduration = l.start + l.duration - i[0].start, t.ptsKnown = !0 - } + + { + var e = errors[error]; + var msg = !e ? "unknown error nr: " + error : typeof e === "function" ? e.apply(null, args) : e; + throw new Error("[Immer] " + msg); } - - function Um(e, t, i, r, n, s = !1) { - var a = e[t]; - let o = e[i]; - var l = null != r && null != o.isIframeStart && o.isIframeStart !== r && o.discoSeqNum === a.discoSeqNum; - let d = o.start; - !l && null != o.startPts || (d = t < i ? a.start + a.duration : Math.max(a.start - o.duration, 0)); - n = ne(n) ? 1 / n : Number.EPSILON, n = Math.abs(o.start - d) > n; - return !(!l && !n || (s && (o = e[i] = Object.assign({}, o)), n && (o.start = d), l && (o.isIframeStart = r), 0)) + } + + var ArchtypeObject = 0; + var ArchtypeArray = 1; + var ArchtypeMap = 2; + var ArchtypeSet = 3; + var ProxyTypeProxyObject = 0; + var ProxyTypeProxyArray = 1; + var ProxyTypeES5Object = 4; + var ProxyTypeES5Array = 5; + var ProxyTypeMap = 2; + var ProxyTypeSet = 3; + + /** Returns true if the given value is an Immer draft */ + + + + function isDraft(value) { + return !!value && !!value[DRAFT_STATE]; + } + /** Returns true if the given value can be drafted by Immer */ + + + + function isDraftable(value) { + if (!value) return false; + return isPlainObject(value) || Array.isArray(value) || !!value[DRAFTABLE] || !!value.constructor[DRAFTABLE] || isMap(value) || isSet(value); + } + + + function isPlainObject(value) { + if (!value || typeof value !== "object") return false; + var proto = Object.getPrototypeOf(value); + return !proto || proto === Object.prototype; + } + function original(value) { + if (!isDraft(value)) die(23, value); + return value[DRAFT_STATE].base_; + } + + + var ownKeys = typeof Reflect !== "undefined" && Reflect.ownKeys ? Reflect.ownKeys : typeof Object.getOwnPropertySymbols !== "undefined" ? function (obj) { + return Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj)); + } : + /* istanbul ignore next */ + Object.getOwnPropertyNames; + var getOwnPropertyDescriptors = Object.getOwnPropertyDescriptors || function getOwnPropertyDescriptors(target) { + // Polyfill needed for Hermes and IE, see https://github.com/facebook/hermes/issues/274 + var res = {}; + ownKeys(target).forEach(function (key) { + res[key] = Object.getOwnPropertyDescriptor(target, key); + }); + return res; + }; + function each(obj, iter, enumerableOnly) { + if (enumerableOnly === void 0) { + enumerableOnly = false; } - - function $m(e) { - if (e.programDateTimeMap) { - e.dateMediaTimePairs = []; - for (var [t, i] of Object.entries(e.programDateTimeMap)) { - t = Number(t), i = e.fragments[i - e.startSN]; - i && (i = i.start, e.dateMediaTimePairs.push([t, i])) - } - e.dateMediaTimePairs.sort((e, t) => e[0] - t[0]) - } + + if (getArchtype(obj) === ArchtypeObject) { + (enumerableOnly ? Object.keys : ownKeys)(obj).forEach(function (key) { + if (!enumerableOnly || typeof key !== "symbol") iter(key, obj[key], obj); + }); + } else { + obj.forEach(function (entry, index) { + return iter(index, entry, obj); + }); } - class Vm { - constructor(e) { - this.option = e - } - get name() { - return this.option.name - } - get priority() { - return this.option.priority - } - get expiry() { - return this.option.expiry - } - filter(i, e) { - const r = this.option.initFn && this.option.initFn(i, e) || (e ? Object.assign({}, e) : {}); - let t = i; - return this.option.firstPassFn && i.forEach((e, t) => this.option.firstPassFn(e, t, r, i)), this.option.filterFn && (t = i.filter((e, t) => this.option.filterFn(e, t, r, i))), null != this.option.filterFn && 0 !== t.length || !this.option.minSortingFn || (t = i.sort((e, t) => this.option.minSortingFn(e, t, r, i))), this.option.finalFn && this.option.finalFn(t, r, i), t - } + } + + + function getArchtype(thing) { + /* istanbul ignore next */ + var state = thing[DRAFT_STATE]; + return state ? state.type_ > 3 ? state.type_ - 4 // cause Object and Array map back from 4 and 5 + : state.type_ // others are the same + : Array.isArray(thing) ? ArchtypeArray : isMap(thing) ? ArchtypeMap : isSet(thing) ? ArchtypeSet : ArchtypeObject; + } + + + function has(thing, prop) { + return getArchtype(thing) === ArchtypeMap ? thing.has(prop) : Object.prototype.hasOwnProperty.call(thing, prop); + } + + + function get(thing, prop) { + // @ts-ignore + return getArchtype(thing) === ArchtypeMap ? thing.get(prop) : thing[prop]; + } + + + function set(thing, propOrOldValue, value) { + var t = getArchtype(thing); + if (t === ArchtypeMap) thing.set(propOrOldValue, value);else if (t === ArchtypeSet) { + thing.delete(propOrOldValue); + thing.add(value); + } else thing[propOrOldValue] = value; + } + + + function is(x, y) { + // From: https://github.com/facebook/fbjs/blob/c69904a511b900266935168223063dd8772dfc40/packages/fbjs/src/core/shallowEqual.js + if (x === y) { + return x !== 0 || 1 / x === 1 / y; + } else { + return x !== x && y !== y; } - - function Km(e, t, i) { - return (t || []).reduce((e, t) => t.filter(e, i), Array.from(e)) + } + + + function isMap(target) { + return hasMap && target instanceof Map; + } + + + function isSet(target) { + return hasSet && target instanceof Set; + } + + + function latest(state) { + return state.copy_ || state.base_; + } + + + function shallowCopy(base) { + if (Array.isArray(base)) return Array.prototype.slice.call(base); + var descriptors = getOwnPropertyDescriptors(base); + delete descriptors[DRAFT_STATE]; + var keys = ownKeys(descriptors); + + for (var i = 0; i < keys.length; i++) { + var key = keys[i]; + var desc = descriptors[key]; + + if (desc.writable === false) { + desc.writable = true; + desc.configurable = true; + } // like object.assign, we will read any _own_, get/set accessors. This helps in dealing + // with libraries that trap values, like mobx or vue + // unlike object.assign, non-enumerables will be copied as well + + + if (desc.get || desc.set) descriptors[key] = { + configurable: true, + writable: true, + enumerable: desc.enumerable, + value: base[key] + }; } - - function qm(t, e) { - return e.filter(e => { - return Au(t, null !== (e = e.url) && void 0 !== e ? e : null) - }) - } - - function Hm() { - return [new Vm({ - name: "Remove Filter", - priority: 0, - filterFn: (t, e, i) => !i || i.removed.every(e => t.mediaOptionId !== e) - }), new Vm({ - name: "Penalty Box Filter", - priority: 1, - filterFn: (t, e, i) => { - const r = performance.now(); - return !i || i.penaltyBoxQueue.every(e => e.expiry <= r || t.mediaOptionId !== e.mediaOptionId) - } - }), new Vm({ - name: "Compatible IDs Filter", - priority: 1, - filterFn: (t, e, i) => !i || null == i.compatibleIds || i.compatibleIds.some(e => e === t.mediaOptionId) - })] - } - class jm extends kl { - constructor(e, t, i) { - super(e), this.itemId = t, this.mediaOptionType = i, this.allowFilters = this._initFilters() - } - get mediaOptionList() { - var e; - return (null === (e = this.mediaOptionListInfo) || void 0 === e ? void 0 : e.mediaOptions) || null - } - get mediaOptionList$() { - return this.mediaOptionListInfo$.pipe(hr(({ - mediaOptions: e - }) => e)) - } - mediaOptionFromId(t) { - var e; - return null !== (e = (null !== (e = this.mediaOptionList) && void 0 !== e ? e : []).find(e => e.mediaOptionId === t)) && void 0 !== e ? e : null - } - _getFilteredList(e) { - return Km(e.mediaOptions, this.allowFilters, e) - } - get filteredMediaOptionList() { - return this.mediaOptionListInfo ? this._getFilteredList(this.mediaOptionListInfo) : null - } - get filteredMediaOptionList$() { - return this.mediaOptionListInfo$.pipe(La(e => { - const t = [Wu], - i = performance.now(); - for (const r of e.penaltyBoxQueue) ne(r.expiry) && r.expiry > i && t.push(bn(r.expiry - i)); - return an(...t).pipe(hr(() => this._getFilteredList(e))) - }), vl()) - } - get preferredMediaOptionList() { - return this.filteredMediaOptionList ? qm(this.preferredHost, this.filteredMediaOptionList) : [] - } - get preferredMediaOptionList$() { - return ed([this.preferredHost$, this.filteredMediaOptionList$]).pipe(hr(([e, t]) => qm(e, t))) - } - getNewHost(e) { - e = this.getFallbackVariant(e, !1, !0); - return null != e && e.url ? Eu(e.url) : this.preferredHost - } - } - - function Qm(e) { - return "PQ" === e.videoRange || "HLG" === e.videoRange - } - - function Wm(e, t) { - return t.iframes === e - } - - function Gm(e, t, i, r) { - return !r || i.bitrate > r.bitrate && i.bitrate <= e.bitrate ? km.Better : i.bitrate === r.bitrate ? km.Same : km.Worse - } - - function zm(e, t) { - return e && !t ? -1 : !e && t ? 1 : 0 - }(dl = km = km || {})[dl.Better = 1] = "Better", dl[dl.Same = 0] = "Same", dl[dl.Worse = -1] = "Worse"; - class Xm extends jm { - constructor(e, t) { - super(e, t, gu.Variant) - } - static makeFilters() { - return [...Hm().concat([new Vm({ - name: "HDR Filter", - priority: 1, - filterFn: (e, t, i) => !i || (i.hasHdrLevels && i.preferHDR) === Qm(e) - }), new Vm({ - name: "Viewport Filter", - priority: 1, - firstPassFn: (e, t, i) => { - if (i && e && !e.iframes && e.videoCodec) { - const t = !i.lowestBitrate || e.bitrate < i.lowestBitrate ? e.bitrate : i.lowestBitrate; - i.lowestBitrate = t - } - }, - filterFn: (e, t, i) => !(e && i && i.viewportInfo && e.videoCodec && i.lowestBitrate) || function(e, t) { - return e.width < 1.35 * t.width && e.height < 1.35 * t.height && e.width * e.height < t.width * t.height * 1.35 - }({ - width: e.width, - height: e.height - }, i.viewportInfo) || e.bitrate === i.lowestBitrate - }), new Vm({ - name: "HDCP Filter", - priority: 2, - filterFn: (e, t, i) => !i || !dm(i.maxHdcpLevel) || um(e.hdcpLevel) < um(i.maxHdcpLevel) - })])].sort((e, t) => { - return (null !== (e = e.priority) && void 0 !== e ? e : Number.MAX_SAFE_INTEGER) - (null !== (t = t.priority) && void 0 !== t ? t : Number.MAX_SAFE_INTEGER) - }) - } - _initFilters() { - return Xm.kAllowFilters - } - get preferredHost() { - var e; - return null !== (e = null === (e = this.mediaOptionListInfo) || void 0 === e ? void 0 : e.preferredHost) && void 0 !== e ? e : null - } - get preferredHost$() { - return this.selectEntity(this.itemId, e => { - return null !== (e = null == e ? void 0 : e.mediaOptionListTuple[gu.Variant].preferredHost) && void 0 !== e ? e : null - }) - } - get mediaOptionListInfo() { - var e; - return null !== (e = null === (e = this.getEntity(this.itemId)) || void 0 === e ? void 0 : e.mediaOptionListTuple[gu.Variant]) && void 0 !== e ? e : null - } - get mediaOptionListInfo$() { - return this.selectEntity(this.itemId, e => { - return null === (e = null == e ? void 0 : e.mediaOptionListTuple) || void 0 === e ? void 0 : e[gu.Variant] - }).pipe(Kp()) - } - get hdrMode$() { - return this.mediaOptionListInfo$.pipe(hr(e => !0 === e.preferHDR && e.hasHdrLevels), Is()) - } - get maxHdcpLevel$() { - return this.selectEntity(this.itemId, e => { - e = null === (e = null == e ? void 0 : e.mediaOptionListTuple) || void 0 === e ? void 0 : e[gu.Variant]; - return null == e ? void 0 : e.maxHdcpLevel - }).pipe(Is()) - } - listFallbackVariants(t, e, i, r, n) { - var s = this.mediaOptionListInfo, - a = null === (o = this.mediaOptionList) || void 0 === o ? void 0 : o.find(e => e.mediaOptionId === t); - if (!a || !s) return null; - var o = this.makeFilteredListFromVariant(a, e, n); - if (!o) return null; - e = Eu(a.url), s = s.hasScore; - return Xm._listFallbackVariants(o, a, e, s, i, r, n) - } - getFallbackVariant(t, e, i, r) { - var n = this.mediaOptionListInfo, - s = null === (s = this.mediaOptionList) || void 0 === s ? void 0 : s.find(e => e.mediaOptionId === t); - if (!s || !n) return null; - e = this.makeFilteredListFromVariant(s, e, r); - if (!e) return null; - r = Eu(s.url), n = n.hasScore; - return Xm._getFallbackVariant(e, s, r, n, i) - } - makeFilteredListFromVariant(e, t, i) { - let r = this.mediaOptionListInfo; - if (!e || !this.mediaOptionList || !r) return null; - r = Object.assign(Object.assign({}, r), { - includeAllEligiblePathways: !0 - }); - e = Array.from(this.mediaOptionList); - let n = t ? Km(e, this.allowFilters, Object.assign(Object.assign({}, r), { - preferHDR: !1, - compatibleIds: null - })) : this._getFilteredList(r); - return n ? (i && 0 < i.length && (n = n.filter(e => !i.includes(e.mediaOptionId))), n) : null - } - get hasIframes() { - var e; - return null !== (e = null === (e = this.mediaOptionListInfo) || void 0 === e ? void 0 : e.hasIframeLevels) && void 0 !== e && e - } - canSwitchToSDR(e, t, i = !1) { - var r = this.mediaOptionListInfo; - if (!this.mediaOptionList || !r) return !1; - var n = this.mediaOptionFromId(e); - if (!n) return !1; - if (!Qm(n)) return !1; - var s = Eu(n.url), - e = Km(Array.from(this.mediaOptionList), this.allowFilters, Object.assign(Object.assign({}, r), { - preferHDR: !1, - compatibleIds: null - })), - r = r.hasScore; - return null != Xm._getFallbackVariant(e, n, s, r, t, i) - } - static _listFallbackVariants(e, r, n, t, s, a = !1, i = null) { - let o = !1; - const l = function(e, s, a) { - const t = [...e], - o = Eu(s.url), - l = s.audioGroupId; - return t.sort((e, t) => { - let i = 0; - var r = a && ne(e.score) && ne(t.score), - n = r ? e.score > t.score && e.score <= s.score : e.bitrate > t.bitrate && e.bitrate <= s.bitrate, - r = r ? e.score === t.score : e.bitrate === t.bitrate; - return n ? i = -1 : r ? (i = zm(Au(o, e.url), Au(o, t.url)), 0 === i && (i = zm(!l || e.audioGroupId === l, !l || t.audioGroupId === l))) : i = 1, i - }), t - }(e.filter(e => { - var t = !(e.iframes !== r.iframes || s && Au(n, e.url)), - i = a ? e.bitrate < r.bitrate : e.bitrate <= r.bitrate, - i = t && i; - return r.mediaOptionId === e.mediaOptionId ? (o = i, !1) : i - }), r, t); - return !o || i && i.includes(r.mediaOptionId) || l.unshift(r), l - } - static _getFallbackVariant(e, t, i, r, n, s = !1) { - let a = null; - e = (e = s ? e.filter(e => e.bitrate < t.bitrate) : e).filter(e => e.mediaOptionId !== t.mediaOptionId && e.iframes === t.iframes); - if (n && null != i) - for (const o of e) Gm(t, 0, o, a) !== km.Better || Au(i, o.url) || (a = o); - else - for (const o of e) { - const r = Gm(t, 0, o, a); - r !== km.Better && (r !== km.Same || !Au(i, o.url) || Au(i, a.url) && o.audioGroupId !== t.audioGroupId) || (a = o) - } - return a - } - getMatchingVariant(e, t) { - var i = this.mediaOptionFromId(e), - r = Eu(null == i ? void 0 : i.url), - n = t.mediaOptionType === gu.AltAudio ? "audioGroupId" : "subtitleGroupId"; - let s = null; - this.mediaOptionListInfo.hasScore; - for (const e of this.filteredMediaOptionList) - if (e[n] === t.groupId) { - if (!i) { - s = e; - break - } - var a = Gm(i, 0, e, s); - a !== km.Better && (a !== km.Same || s.mediaOptionId === i.mediaOptionId || e.mediaOptionId !== i.mediaOptionId && !Au(r, e.url)) || (s = e) - } return s - } - get currentPathwayID() { - var e; - return null === (e = this.mediaOptionListInfo) || void 0 === e ? void 0 : e.currentPathwayID - } - } - - function Ym(e, t) { - switch (e) { - case Cm.SendAlternateToPenaltyBox: - e = Cm.RetryRequest, 401 !== t && 403 !== t && 407 !== t && t !== $.CorruptStream.code && t !== $.LivePlaylistUpdateError.code || (e = Cm.SendEndCallback); - break; - case Cm.RemoveAlternatePermanently: - e = Cm.SendEndCallback - } - return e - } - - function Jm(e, t, i, r, n, s, a, o = !1) { - const l = s.mediaOptionListQueries[n], - d = 0 != (e.errorActionFlags & Dm.MoveAllAlternatesMatchingHost), - u = s.mediaOptionListQueries[n].mediaOptionFromId(r), - c = a.getFallbackMediaOptionTupleFromMediaOptionId(s, n, r, u.backingMediaOptionId, !1, d, o); - let { - errorAction: h, - errorActionFlags: p - } = e; - return s.isValidMediaOptionTuple(c) ? Au(l.preferredHost, c[n].url) && (p &= ~Dm.MoveAllAlternatesMatchingHost) : (t || (h = Ym(h, i), p = 0), l instanceof Xm && (!0 === l.mediaOptionFromId(r).iframes ? (h = Cm.DoNothing, p = 0) : !t && a.canSwitchToSDR(s, r, d, o) && (h = e.errorAction, p = Dm.SwitchToSDR))), { - errorAction: h, - errorActionFlags: p - } - } - - function Zm(e) { - let t, i = 0; - switch (e) { - case 0: - t = Cm.SendAlternateToPenaltyBox, i = Dm.MoveAllAlternatesMatchingHost; - break; - case 410: - t = Cm.RemoveAlternatePermanently; - break; - case 500: - case 502: - case 503: - case 504: - case 404: - case 409: - case 401: - case 403: - case 407: - case $.LivePlaylistUpdateError.code: - case $.PlaylistNotReceived.code: - default: - t = Cm.SendAlternateToPenaltyBox, i = 0 - } - return { - errorAction: t, - errorActionFlags: i - } - } - - function eg(i, r, n, s, a, o, l) { - var { - errorAction: d, - errorActionFlags: u - } = i; - let e = !0; - switch (d) { - case Cm.RemoveAlternatePermanently: - case Cm.SendAlternateToPenaltyBox: { - if (null == a || null == o) return r.handled = !1; - const i = n.itemId; - let e = o, - t = n.mediaOptionListQueries[a].mediaOptionFromId(o); - t.backingMediaOptionId && (e = t.backingMediaOptionId, t = n.mediaOptionListQueries[a].mediaOptionFromId(e)); - var c = 0 != (u & Dm.MoveAllAlternatesMatchingHost), - h = 0 != (u & Dm.MoveAllAlternatesMatchingHDCP), - p = 0 != (u & Dm.SwitchToSDR), - f = d === Cm.RemoveAlternatePermanently; - if (h && "hdcpLevel" in t) { - const r = t.hdcpLevel; - s.setMaxHdcpLevel(i, r) - } - if (p && s.switchToSDROnly(i), c) { - const r = Eu(t.url); - s.moveAllWithMatchingHosts(i, a, r, f) - } else f ? s.removePermanently(i, a, e) : s.addToPenaltyBox(i, a, e); - if (n.enabledMediaOptionIdByType(a) === o) { - let e = [Lu, Lu, Lu]; - e = s.getFallbackMediaOptionTupleFromMediaOptionId(n, a, o, null, !1, c, l), n.isValidMediaOptionTuple(e) ? s.setPreferredHost(i, Eu(e[gu.Variant].url)) : r.fatal = !0, r.fatal && (e = [Lu, Lu, Lu]), s.setNextMediaOptions(n.itemId, e) - } - break - } - case Cm.SendEndCallback: - r.fatal = !0; - break; - case Cm.RetryRequest: - case Cm.DoNothing: - default: - e = !1 - } - return r.handled = e, e - } - - function tg(e, t) { - t instanceof dc || t instanceof uc || t instanceof hc || (t instanceof gc || t instanceof mc) && le(t.keyuri) - } - - function ig(t, i, r) { - if (t.handled = !0, r && i < r.maxNumRetry && ne(r.retryDelayMs)) { - let e; - return e = "linear" === r.backoff ? (i + 1) * r.retryDelayMs : Math.pow(2, i) * r.retryDelayMs, e = Math.min(r.maxRetryDelayMs, e), tg(0, t), bn(e) - } - return t.fatal = !0, tg(0, t), Vi(t) - } - - function rg(e, t, i, r, n, s, a, o, l = !1) { - return (null == r ? void 0 : r.errorAction) === Cm.RetryRequest ? ig(e, t, i, s.logger) : ng(e, 0, r, n, s, a, o, l) - } - - function ng(e, t, i, r, n, s, a, o = !1) { - const l = new er; - return al(() => { - i && (tg(n.logger, e), eg(i, e, r, n, s, a, o)), l.error(e) - }), l - } - - function sg(t, i, r, n) { - return e => e.pipe(Vn(e => { - if (t.logger.error(`Got demux error ${e.message}`), e instanceof D || e instanceof F) { - Cm.SendAlternateToPenaltyBox; - return ng(e, 0, { - errorAction: e.fatal ? Cm.SendEndCallback : e instanceof F ? Cm.SendAlternateToPenaltyBox : Cm.RemoveAlternatePermanently, - errorActionFlags: 0 - }, i, t, r, n) - } - throw e - })) - } - - function ag(e, t, i, r, n) { - e = null !== (e = null === (e = n.getKeyInfo(e)) || void 0 === e ? void 0 : e.mediaOptionIds) && void 0 !== e ? e : []; - for (const s of e) - for (const n of i.mediaOptionListQueries) null != n.mediaOptionFromId(s) && r.updateConsecutiveTimeouts(i.itemId, n.mediaOptionType, t, "key") - } - - function og(r, t, e, n, s, a) { - const o = n.logger, - i = s.enabledMediaOptionKeys, - l = []; - for (const t of r.mediaOptionIds) { - const r = i.some(e => e.mediaOptionId === t), - e = s.mediaOptionListQueries.find(e => null != e.mediaOptionFromId(t)); - if (e) { - const n = e.mediaOptionType, - a = { - mediaOptionId: t, - mediaOptionType: n - }; - r ? l.push(a) : l.unshift(a) - } else o.warn(`Couldn't find query for ${t}`) - } - const d = new er; - return al(() => { - const e = r instanceof mc; - ag(r.keyuri, e, s, n, a); - let t, i = !1; - for (const { - mediaOptionId: e, - mediaOptionType: a - } of l) t = function(e, t, i, r, n) { - let s = { - errorAction: Cm.SendAlternateToPenaltyBox, - errorActionFlags: 0 - }; - if (e instanceof mc) s.errorAction = Cm.SendAlternateToPenaltyBox; - else { - const t = e.isOkToRetry; - e.keyErrorReason === rc.OutputRestricted ? (s.errorAction = Cm.RemoveAlternatePermanently, s.errorActionFlags |= Dm.MoveAllAlternatesMatchingHDCP) : t ? s = Zm(e.code) : s.errorAction = Cm.RemoveAlternatePermanently - } - s.errorActionFlags &= ~Dm.MoveAllAlternatesMatchingHost; - var a = r.enabledMediaOptionIdByType(i); - return t === a ? Jm(s, !1, e.code, a, i, r, n, e.isTimeout) : s - }(r, e, a, s, n), o.error(`[Keys] handleNetworkError uri=${le(r.keyuri)} mediaOptionId=${e} mediaOptionType=${a} action=${JSON.stringify(t)}`), t.errorAction === Cm.RetryRequest && (i = !0), eg(t, r, s, n, a, e); - i ? (d.next(), d.complete()) : (tg(0, r), d.error(r)) - }), d.pipe(La(() => ig(r, t, e, n.logger))) - } - - function lg(t, i, r, n, s, a, o, l) { - return n = Math.max(0, n), e => e.pipe(Za(() => { - null != i && o.updateConsecutiveTimeouts(t, i, !1, "load") - }), va(e => e.pipe(jr((e, t) => function(e, t, i, r, n, s, a, o) { - var l; - if (!(e instanceof p)) return Vi(e); - let d, u, c, h = !1; - if (e instanceof dc) c = { - errorAction: Ym(Zm((l = e).response.code).errorAction, l.response.code), - errorActionFlags: 0 - }; - else if (e instanceof uc || e instanceof hc) { - ({ - mediaOptionType: u, - mediaOptionId: d, - isTimeout: h - } = e); - const t = null === (l = s.mediaOptionListQueries[u]) || void 0 === l ? void 0 : l.mediaOptionFromId(d); - if (!r && e.isTimeout && null != t && (!("iframes" in t) || !0 !== t.iframes) && (a.updateConsecutiveTimeouts(s.itemId, e.mediaOptionType, !0, "load"), e instanceof hc && e.stats)) { - const t = performance.now(); - o.setBandwidthSample(Object.assign(Object.assign({}, e.stats), { - tfirst: e.stats.tfirst || t, - tload: e.stats.tload || t, - complete: !0, - mediaOptionType: u - })) - } - c = function(e, t, i, r, n) { - var s = e.response.code; - let a = Zm(s); - var { - mediaOptionId: o, - mediaOptionType: l - } = e; - return t ? a.errorActionFlags &= ~Dm.MoveAllAlternatesMatchingHost : a = function(e, t, i, r, n) { - let { - errorAction: s, - errorActionFlags: a - } = t; - if (e.isTimeout) { - const t = e["mediaOptionType"], - o = null !== (n = null === (n = n.getErrorInfoByType(t)) || void 0 === n ? void 0 : n.timeouts.load) && void 0 !== n ? n : 0; - !i && r <= o && (s = Cm.DoNothing, a = 0) - } - return { - errorAction: s, - errorActionFlags: a - } - }(e, a, t, i, r), a = Jm(a, t, s, o, l, r, n, e.isTimeout), a - }(e, r, n, s, a) - } - return rg(e, t, i, c, s, a, u, d, h) - }(e, t, _c(e, r), s, n, a, o, l))))) - } - Xm.kAllowFilters = Xm.makeFilters(), (dl = Cm = Cm || {})[dl.DoNothing = 0] = "DoNothing", dl[dl.SendEndCallback = 1] = "SendEndCallback", dl[dl.SendAlternateToPenaltyBox = 2] = "SendAlternateToPenaltyBox", dl[dl.RemoveAlternatePermanently = 3] = "RemoveAlternatePermanently", dl[dl.InsertDiscontinuity = 4] = "InsertDiscontinuity", dl[dl.RetryRequest = 5] = "RetryRequest", (dl = Dm = Dm || {})[dl.MoveAllAlternatesMatchingHost = 1] = "MoveAllAlternatesMatchingHost", dl[dl.MoveAllAlternatesMatchingHDCP = 2] = "MoveAllAlternatesMatchingHDCP", dl[dl.SwitchToSDR = 4] = "SwitchToSDR"; - class dg extends kl { - constructor(e) { - super(e) - } - get currentConfig() { - var e; - return null === (e = this.getActive()) || void 0 === e ? void 0 : e.config - } - get extendMaxTTFB() { - var e; - return null === (e = this.getActive()) || void 0 === e ? void 0 : e.extendMaxTTFB - } - get config$() { - return this.selectActive(e => null == e ? void 0 : e.config) - } - get userSeek$() { - return this.selectActive(e => null == e ? void 0 : e.userSeek) - } - } - A(); - class ug extends fl { - constructor() { - super({}, { - name: "hls-store", - producerFn: su - }) - } - } - class cg { - constructor(e) { - this.store = e - } - getQuery() { - return new dg(this.store) - } - setHlsEntity(e) { - const t = e.id; - Do(`hls.set.entity ${t}`), al(() => { - this.store.add(de(e)), this.store.setActive(t) - }) - } - removeEntity(e) { - Do(`hls.remove ${e}`), this.store.remove(e) - } - setStartTime(t) { - this.store.updateActive(e => { - e.config.startPosition = t - }) - } - setUserSeek(t) { - this.store.updateActive(e => { - e.userSeek = t - }) - } - setExtendMaxTTFB(t) { - this.store.updateActive(e => { - e.extendMaxTTFB = t - }) - } - } - let hg, pg; - - function fg() { - return hg = hg || new cg(new ug), hg - } - - function mg() { - return fg().getQuery().currentConfig - } - - function gg(r, n) { - if ("VOD" !== r.type && "VOD" !== n.type && r.iframesOnly === n.iframesOnly) { - const s = r.mediaOptionId === n.mediaOptionId, - a = Math.max(r.startSN, n.startSN) - n.startSN, - o = Math.min(r.endSN, n.endSN) - n.startSN, - l = n.startSN - r.startSN, - d = r.fragments, - u = n.fragments; - let t = 0; - for (let e = a; e <= o; ++e) - if (d[l + e] && u[e]) { - t = d[l + e].discoSeqNum - u[e].discoSeqNum; - break - } const c = {}; - let i = null; - for (let e = 0; e < u.length; e++) { - const h = d[l + e], - a = u[e]; - if (t) { - const r = a.discoSeqNum + t; - n.initSegments[a.discoSeqNum] && (n.initSegments[a.discoSeqNum].discoSeqNum = r, c[r] = n.initSegments[a.discoSeqNum], delete n.initSegments[a.discoSeqNum]), a.discoSeqNum = r - } - s && a.mediaSeqNum === (null == h ? void 0 : h.mediaSeqNum) && null != h.startPts && (a.start = h.start, a.duration = h.duration, a.startDtsTs = h.startDtsTs, a.endDtsTs = h.endDtsTs, a.startPts = h.startPts, a.endPts = h.endPts, i = a) - } - if (Object.keys(c).length && (n.initSegments = c), i) Bm(n, i, i.start, void 0, !1, !0); - else if (0 <= l && l < d.length) { - const r = d[l].start, - h = n.fragments; - for (let e = 0; e < u.length; e++) h[e].start += r - } - n.ptsKnown = n.ptsKnown || s && !0 === r.ptsKnown && r.endSN >= n.startSN, $m(n) - } - } - - function yg(e, t, i) { - let r = t.targetduration; - return ne(i.liveSyncDuration) ? r = i.liveSyncDuration : ne(i.liveSyncDurationCount) && (r = i.liveSyncDurationCount * t.targetduration), e + Math.max(0, t.totalduration - r) - } - - function vg(e, t, i, r, n) { - if (!t.ptsKnown) return 0; - var s = t.targetduration, - a = t.fragments[0].start, - r = n.canContinuePlaybackWithoutGap(t, i, { - avgPlaylistLoadTimeMs: 0, - avgPlaylistParseTimeMs: 0 - }, r); - let o = Math.max(0, e - s); - return e < a && !r && (o = a), o - } - - function Sg(e) { - return 1e3 * (e.averagetargetduration || e.targetduration) - } - - function bg(e, t) { - var i = Sg(e), - t = performance.now() - t; - return e.liveOrEvent && i <= t - } - - function Tg(t, i, r = NaN) { - var n = t.fragments; - for (let e = r > t.startSN ? r - t.startSN : 0; e < n.length; ++e) { - const t = n[e], - r = t["start"]; - if (i(t)) return { - timelineOffset: r, - mediaFragment: t - } - } - return null - } - - function Eg(e, t, i) { - var r = 0 < i.duration, - n = i.start + i.duration, - n = null == t || t < n || t - n < 1 && i.isLastFragment, - e = e.every(e => !$p(e.frag, i)); - return r && e && n - } - - function Ig(e, t, i, r, n) { - i = null !== (i = Tg(r, Eg.bind(null, n, void 0), i)) && void 0 !== i ? i : null; - let s = null; - i && (s = null !== (e = Tg(r, Eg.bind(null, n, e), i.mediaFragment.mediaSeqNum)) && void 0 !== e ? e : null, s || !r.liveOrEvent || r.ptsKnown || (s = i)); - let a = NaN; - null != s && ne(t) && s.mediaFragment.discoSeqNum !== t && (a = s.mediaFragment.discoSeqNum, s = null); - t = n.some(e => 0 < e.frag.framesWithoutIDR); - return s && t && n[n.length - 1].frag.mediaOptionId !== r.mediaOptionId && (s = null !== (r = Tg(r, e => e.mediaSeqNum === s.mediaFragment.mediaSeqNum - 1)) && void 0 !== r ? r : s), { - foundFrag: s, - nextDisco: a - } - } - - function wg(e) { - e = e.fragments[e.fragments.length - 1]; - return e ? e.start + e.duration : 0 - } - class Ag extends kl { - constructor(e, t) { - super(e), this.mediaOption = t - } - get itemId() { - return this.mediaOption.itemId - } - get mediaOptionId() { - return this.mediaOption.mediaOptionId - } - get initSegmentEntities() { - var e; - return null === (e = this.mediaOptionDetailsEntity) || void 0 === e ? void 0 : e.initSegmentCacheEntities - } - get mediaLibraryEntity() { - return this.getEntity(this.itemId) - } - get mediaOptionDetailsEntityRecord() { - var e; - return null === (e = this.mediaLibraryEntity) || void 0 === e ? void 0 : e.mediaOptionDetailsEntityRecord - } - get mediaOptionDetailsEntity() { - return this.mediaOptionDetailsEntityRecord ? this.mediaOptionDetailsEntityRecord[this.mediaOptionId] : null - } - get mediaOptionDetails() { - var e; - return null === (e = this.mediaOptionDetailsEntity) || void 0 === e ? void 0 : e.mediaOptionDetails - } - get playlistDuration() { - var e; - return null === (e = this.mediaOptionDetailsEntity) || void 0 === e ? void 0 : e.playlistDuration - } - get mediaOptionDetailsEntity$() { - const { - itemId: e, - mediaOptionId: t - } = this; - return this.selectEntity(e, e => { - if (null != e && e.mediaOptionDetailsEntityRecord) return null == e ? void 0 : e.mediaOptionDetailsEntityRecord[t] - }) - } - get mediaOptionDetails$() { - return this.selectEntity(this.itemId, e => { - return null === (e = null == e ? void 0 : e.mediaOptionDetailsEntityRecord[this.mediaOptionId]) || void 0 === e ? void 0 : e.mediaOptionDetails - }).pipe(Kp()) - } - get playlistDuration$() { - return this.mediaOptionDetailsEntity$.pipe(hr(e => null == e ? void 0 : e.playlistDuration), Kp(), Is()) - } - get live$() { - return this.mediaOptionDetails$.pipe(hr(e => null == e ? void 0 : e.liveOrEvent), Is()) - } - } - class Og extends fl { - constructor() { - super({}, { - name: "media-library-store", - idKey: "itemId", - producerFn: su - }) - } - } - class kg { - constructor(e) { - this.store = e - } - getQuery() { - return new kl(this.store) - } - getQueryForOption(e) { - return new Ag(this.store, e) - } - createMediaLibraryEntity(e) { - var t = { - itemId: e, - mediaOptionDetailsEntityRecord: {} - }; - Do(`library.entity.create: ${e}`), this.store.add(t) - } - setDetailsLoading(e) { - const { - itemId: t, - mediaOptionId: i - } = e; - Do(`library.details.loading: ${i}`), this.store.update(t, ({ - mediaOptionDetailsEntityRecord: e - }) => { - e[i] || (e[i] = { - initSegmentCacheEntities: {}, - unchangedCount: 0 - }), e[i].detailsLoading = !0 - }) - } - archiveMediaOptionDetails(i, r, n) { - const { - itemId: e, - mediaOptionId: s - } = i, a = performance.now(), o = wg(i); - Do(`library.details.loaded: ${s}`), this.store.update(e, e => { - const t = e.mediaOptionDetailsEntityRecord[s]; - t.detailsLoading = !1, t.mediaOptionDetails = i, t.lastUpdateMillis = a, n ? t.unchangedCount = 0 : ++t.unchangedCount, t.playlistDuration = o, t.stats = r, e.liveOrEvent = i.liveOrEvent - }) - } - setInitSegmentLoading(e) { - const { - itemId: t, - mediaOptionId: i, - discoSeqNum: r - } = e; - Do(`library.initsegs.loading: ${i}/${r}`), this.store.update(t, e => { - e.mediaOptionDetailsEntityRecord[i].initSegLoading = r - }) - } - archiveInitSegmentEntity(i, r) { - const { - itemId: e, - mediaOptionId: n, - discoSeqNum: s - } = i; - Do(`library.initseg.loaded: ${n}/${s}`), this.store.update(e, ({ - mediaOptionDetailsEntityRecord: e - }) => { - const t = e[n]; - t.initSegmentCacheEntities[s] = [i, r], t.initSegLoading = null - }) - } - updatePTSDTS(e, i, t, r) { - var n = null === (n = this.getQueryForOption({ - itemId: e, - mediaOptionId: i - })) || void 0 === n ? void 0 : n.mediaOptionDetails; - if (n && Fm(n, r)) { - const s = r["startDtsTs"], - { - variantDTS: a, - timelineOffset: o, - iframeMode: l - } = t, - d = b(s, a) + o, - u = Object.assign({}, n); - Bm(u, r, d, l, !0), $m(u); - const c = wg(u); - this.store.update(e, ({ - mediaOptionDetailsEntityRecord: e - }) => { - const t = e[i]; - null != t && t.mediaOptionDetails && (t.mediaOptionDetails = u, t.playlistDuration = c) - }) - } - } - remove(e) { - this.store.remove(e) - } - clear() { - this.store.remove() - } - } - - function Cg() { - return pg = pg || new kg(new Og), pg - } - const Dg = e => Cg().getQueryForOption(e), - Mg = (d, e, t = !1, i = !1) => { - if (null == e || !_u(e)) return $i(null); - const r = e["itemId"], - n = d["mediaLibraryService"], - s = n.getQueryForOption(e); - s.hasEntity(r) || n.createMediaLibraryEntity(r); - var a = s.mediaOptionDetailsEntity, - o = s.mediaOptionDetails; - return null == o || i || "VOD" !== o.type && (!o.liveOrEvent || bg(o, a.lastUpdateMillis)) ? (n.setDetailsLoading(e), function(u, e) { - var t; - const { - logger: i, - config: c, - rootPlaylistQuery: h, - rootPlaylistService: p, - statsService: r, - mediaLibraryService: f, - mediaSink: n - } = d, m = n.mediaQuery, s = c.playlistLoadPolicy, a = c.keySystemPreference, o = h.masterVariableList, l = null === (t = null === (t = fg()) || void 0 === t ? void 0 : t.getQuery()) || void 0 === t ? void 0 : t.extendMaxTTFB; - return _m(u, h.itemStartOffset, c, s, i, a, r, o, l).pipe(hr(e => { - const t = f.getQueryForOption(u), - i = t.mediaOptionDetails, - r = e["mediaOptionDetails"], - n = e["stats"]; - let s = !0; - if (r.liveOrEvent) { - const d = r["mediaOptionType"]; - e = r, s = null == i || e.endSN !== i.endSN || e.liveOrEvent !== i.liveOrEvent, i && gg(i, r); - const u = h.lastLoadedMediaOptionByType(d), - t = u ? null === (l = f.getQueryForOption(u)) || void 0 === l ? void 0 : l.mediaOptionDetails : null; - !r.ptsKnown && t && t.mediaOptionId !== (null == i ? void 0 : i.mediaOptionId) && gg(t, r) - } - r && al(() => { - f.archiveMediaOptionDetails(r, n, s), p.setLastLoadedMediaOptionByType(h.itemId, u.mediaOptionType, u) - }); - const a = !s && t.mediaOptionDetailsEntity.unchangedCount >= c.liveMaxUnchangedPlaylistRefresh, - o = function(e, t, i, r) { - t = vg(r.currentTime, e, t, i, r), i = e.fragments[e.fragments.length - 1], r = (null == i ? void 0 : i.start) + (null == i ? void 0 : i.duration); - return { - expired: null != i && e.liveOrEvent && e.ptsKnown && r < t, - windowEnd: r, - minPosition: t - } - }(r, n.tload, c.maxBufferHole, m); - if (a || o.expired) { - var l = a ? $.LivePlaylistUpdateError : { - text: `Live window too far in the past end:${o.windowEnd.toFixed(3)} minPosition:${o.minPosition}`, - code: 0 - }; - throw new uc(!1, l.text, l.code, l, !1, u.mediaOptionType, u.mediaOptionId, u.url) - } - return r - }), tc("getMediaOptionDetailsCommon.emit.loaded"), lg(u.itemId, u.mediaOptionType, Lc(u, s), c.maxNumAddLevelToPenaltyBox, e, h, p, r)).pipe(Ds(1)) - }(e, t)) : $i(o).pipe(tc("retrieveMediaOptionDetails.emit.cached")).pipe(Ds(1)) - }; - const xg = (t, i) => { - if (!i) return $i(null); - const r = t["logger"], - { - mediaLibraryService: e, - mediaParser: n - } = t, - s = e.getQueryForOption(i), - { - mediaOption: a, - mediaOptionDetailsEntityRecord: o, - mediaOptionDetails: l - } = s, - d = a["mediaOptionId"]; - if (null == o || !o[d]) throw new Error("retrieveInitSegmentCacheEntity no details entity"); - if (!l) throw new Error("retrieveInitSegmentCacheEntity no details"); - var u = o[d]["initSegmentCacheEntities"], - c = l["initSegments"], - h = i["discoSeqNum"]; - if (u[h]) { - const [t, r] = u[h]; - let e = t; - return r && (e = n.willBeTrackSwitch(i) ? t : r), $i(e) - } - const p = c[h]; - return p ? (e.setInitSegmentLoading(p), r.child({ - name: "timing" - }), Rg(t, p, !1, !1).pipe(Za(() => {}), ji(tr), La(e => Pg(e, p, t)), Za(() => {}))) : $i(null) + + return Object.create(Object.getPrototypeOf(base), descriptors); + } + function freeze(obj, deep) { + if (isFrozen(obj) || isDraft(obj) || !isDraftable(obj)) return; + + if (getArchtype(obj) > 1 + /* Map or Set */ + ) { + obj.set = obj.add = obj.clear = obj.delete = dontMutateFrozenCollections; + } + + Object.freeze(obj); + if (deep) each(obj, function (key, value) { + return freeze(value, true); + }, true); + } + + function dontMutateFrozenCollections() { + die(2); + } + + function isFrozen(obj) { + if (obj == null || typeof obj !== "object") return true; // See #600, IE dies on non-objects in Object.isFrozen + + return Object.isFrozen(obj); + } + + /** Plugin utilities */ + + var plugins = {}; + function getPlugin(pluginKey) { + var plugin = plugins[pluginKey]; + + if (!plugin) { + die(18, pluginKey); + } // @ts-ignore + + + return plugin; + } + function loadPlugin(pluginKey, implementation) { + if (!plugins[pluginKey]) plugins[pluginKey] = implementation; + } + + var currentScope; + function getCurrentScope() { + if ( !currentScope) die(0); + return currentScope; + } + + function createScope(parent_, immer_) { + return { + drafts_: [], + parent_: parent_, + immer_: immer_, + // Whenever the modified draft contains a draft from another scope, we + // need to prevent auto-freezing so the unowned draft can be finalized. + canAutoFreeze_: true, + unfinalizedDrafts_: 0 }; - - function Pg(e, n, t) { - const { - logger: s, - mediaSink: i, - rootPlaylistService: r, - rootPlaylistQuery: a, - mediaParser: o, - mediaLibraryService: l, - gaplessInstance: d - } = t, u = i["mediaQuery"], c = l.getQueryForOption(n), { - mediaOption: h, - mediaOptionDetails: p - } = c, { - itemId: f, - mediaOptionId: m - } = h, { - keyTagInfo: g, - discoSeqNum: y, - mediaOptionType: v - } = n, S = u.seeking, b = p.liveOrEvent, T = v === gu.Variant && p.ptsKnown; - let E, I; - n.isInitSegment ? I = new Uint8Array(e) : E = new Uint8Array(e); - var e = { - segment: E, - initSegment: I, - frag: n, - ptsKnown: T, - seeking: S, - live: b, - totalDuration: p.totalduration - }; - return o.parseInitSegment(e, null !== (e = null === navigator || void 0 === navigator ? void 0 : navigator.vendor) && void 0 !== e ? e : "").pipe(hr(e => { - var { - track: t, - moovData: i, - mimeType: r - } = e, e = t["initSegment"]; - d.inGaplessMode && be.isVideoCodec(t.codec) && (s.warn(`Video codec discovered in gapless mode codec:${t.codec}`), d.dequeueSource("InvalidFormat")); - r = { - itemId: f, - mediaOptionId: m, - discoSeqNum: y, - initParsedData: i, - data: e, - mimeType: r, - keyTagInfo: g, - fragment: n - }; - return l.archiveInitSegmentEntity(r), r - }), sg(r, a, v, m)) + } + + function usePatchesInScope(scope, patchListener) { + if (patchListener) { + getPlugin("Patches"); // assert we have the plugin + + scope.patches_ = []; + scope.inversePatches_ = []; + scope.patchListener_ = patchListener; } - - function Rg(n, s, i, r) { - var e; - const { - rootPlaylistQuery: t, - rootPlaylistService: a, - config: o, - rtcService: l, - statsService: d - } = n, { - itemId: u, - mediaOptionType: c - } = s, h = o.fragLoadPolicy, p = ne(s.mediaSeqNum); - let f; - p && (f = { - getData: !1, - cb: (e, t, i, r) => (a.updateInflightFrag(u, s.mediaOptionType, s, "loading", i), !1) + } + function revokeScope(scope) { + leaveScope(scope); + scope.drafts_.forEach(revokeDraft); // @ts-ignore + + scope.drafts_ = null; + } + function leaveScope(scope) { + if (scope === currentScope) { + currentScope = scope.parent_; + } + } + function enterScope(immer) { + return currentScope = createScope(currentScope, immer); + } + + function revokeDraft(draft) { + var state = draft[DRAFT_STATE]; + if (state.type_ === ProxyTypeProxyObject || state.type_ === ProxyTypeProxyArray) state.revoke_();else state.revoked_ = true; + } + + function processResult(result, scope) { + scope.unfinalizedDrafts_ = scope.drafts_.length; + var baseDraft = scope.drafts_[0]; + var isReplaced = result !== undefined && result !== baseDraft; + if (!scope.immer_.useProxies_) getPlugin("ES5").willFinalizeES5_(scope, result, isReplaced); + + if (isReplaced) { + if (baseDraft[DRAFT_STATE].modified_) { + revokeScope(scope); + die(4); + } + + if (isDraftable(result)) { + // Finalize the result in case it contains (or is) a subset of the draft. + result = finalize(scope, result); + if (!scope.parent_) maybeFreeze(scope, result); + } + + if (scope.patches_) { + getPlugin("Patches").generateReplacementPatches_(baseDraft[DRAFT_STATE], result, scope.patches_, scope.inversePatches_); + } + } else { + // Finalize the base draft. + result = finalize(scope, baseDraft, []); + } + + revokeScope(scope); + + if (scope.patches_) { + scope.patchListener_(scope.patches_, scope.inversePatches_); + } + + return result !== NOTHING ? result : undefined; + } + + function finalize(rootScope, value, path) { + // Don't recurse in tho recursive data structures + if (isFrozen(value)) return value; + var state = value[DRAFT_STATE]; // A plain object, might need freezing, might contain drafts + + if (!state) { + each(value, function (key, childValue) { + return finalizeProperty(rootScope, state, value, key, childValue, path); + }, true // See #590, don't recurse into non-enumarable of non drafted objects + ); + return value; + } // Never finalize drafts owned by another scope. + + + if (state.scope_ !== rootScope) return value; // Unmodified draft, return the (frozen) original + + if (!state.modified_) { + maybeFreeze(rootScope, state.base_, true); + return state.base_; + } // Not finalized yet, let's do that now + + + if (!state.finalized_) { + state.finalized_ = true; + state.scope_.unfinalizedDrafts_--; + var result = // For ES5, create a good copy from the draft first, with added keys and without deleted keys. + state.type_ === ProxyTypeES5Object || state.type_ === ProxyTypeES5Array ? state.copy_ = shallowCopy(state.draft_) : state.copy_; // Finalize all children of the copy + // For sets we clone before iterating, otherwise we can get in endless loop due to modifying during iteration, see #628 + // Although the original test case doesn't seem valid anyway, so if this in the way we can turn the next line + // back to each(result, ....) + + each(state.type_ === ProxyTypeSet ? new Set(result) : result, function (key, childValue) { + return finalizeProperty(rootScope, state, result, key, childValue, path); + }); // everything inside is frozen, we can freeze here + + maybeFreeze(rootScope, result, false); // first time finalizing, let's create those patches + + if (path && rootScope.patches_) { + getPlugin("Patches").generatePatches_(state, path, rootScope.patches_, rootScope.inversePatches_); + } + } + + return state.copy_; + } + + function finalizeProperty(rootScope, parentState, targetObject, prop, childValue, rootPath) { + if ( childValue === targetObject) die(5); + + if (isDraft(childValue)) { + var path = rootPath && parentState && parentState.type_ !== ProxyTypeSet && // Set objects are atomic since they have no keys. + !has(parentState.assigned_, prop) // Skip deep patches for assigned keys. + ? rootPath.concat(prop) : undefined; // Drafts owned by `scope` are finalized here. + + var res = finalize(rootScope, childValue, path); + set(targetObject, prop, res); // Drafts from another scope must prevented to be frozen + // if we got a draft back from finalize, we're in a nested produce and shouldn't freeze + + if (isDraft(res)) { + rootScope.canAutoFreeze_ = false; + } else return; + } // Search new objects for unfinalized drafts. Frozen objects should never contain drafts. + + + if (isDraftable(childValue) && !isFrozen(childValue)) { + if (!rootScope.immer_.autoFreeze_ && rootScope.unfinalizedDrafts_ < 1) { + // optimization: if an object is not a draft, and we don't have to + // deepfreeze everything, and we are sure that no drafts are left in the remaining object + // cause we saw and finalized all drafts already; we can stop visiting the rest of the tree. + // This benefits especially adding large data tree's without further processing. + // See add-data.js perf test + return; + } + + finalize(rootScope, childValue); // immer deep freezes plain objects, so if there is no parent state, we freeze as well + + if (!parentState || !parentState.scope_.parent_) maybeFreeze(rootScope, childValue); + } + } + + function maybeFreeze(scope, value, deep) { + if (deep === void 0) { + deep = false; + } + + if (scope.immer_.autoFreeze_ && scope.canAutoFreeze_) { + freeze(value, deep); + } + } + + /** + * Returns a new draft of the `base` object. + * + * The second argument is the parent draft-state (used internally). + */ + + function createProxyProxy(base, parent) { + var isArray = Array.isArray(base); + var state = { + type_: isArray ? ProxyTypeProxyArray : ProxyTypeProxyObject, + // Track which produce call this is associated with. + scope_: parent ? parent.scope_ : getCurrentScope(), + // True for both shallow and deep changes. + modified_: false, + // Used during finalization. + finalized_: false, + // Track which properties have been assigned (true) or deleted (false). + assigned_: {}, + // The parent draft state. + parent_: parent, + // The base state. + base_: base, + // The base proxy. + draft_: null, + // The base copy with any updated values. + copy_: null, + // Called by the `produce` function. + revoke_: null, + isManual_: false + }; // the traps must target something, a bit like the 'real' base. + // but also, we need to be able to determine from the target what the relevant state is + // (to avoid creating traps per instance to capture the state in closure, + // and to avoid creating weird hidden properties as well) + // So the trick is to use 'state' as the actual 'target'! (and make sure we intercept everything) + // Note that in the case of an array, we put the state in an array to have better Reflect defaults ootb + + var target = state; + var traps = objectTraps; + + if (isArray) { + target = [state]; + traps = arrayTraps; + } + + var _Proxy$revocable = Proxy.revocable(target, traps), + revoke = _Proxy$revocable.revoke, + proxy = _Proxy$revocable.proxy; + + state.draft_ = proxy; + state.revoke_ = revoke; + return proxy; + } + /** + * Object drafts + */ + + var objectTraps = { + get: function get(state, prop) { + if (prop === DRAFT_STATE) return state; + var source = latest(state); + + if (!has(source, prop)) { + // non-existing or non-own property... + return readPropFromProto(state, source, prop); + } + + var value = source[prop]; + + if (state.finalized_ || !isDraftable(value)) { + return value; + } // Check for existing draft in modified state. + // Assigned values are never drafted. This catches any drafts we created, too. + + + if (value === peek(state.base_, prop)) { + prepareCopy(state); + return state.copy_[prop] = createProxy(state.scope_.immer_, value, state); + } + + return value; + }, + has: function has(state, prop) { + return prop in latest(state); + }, + ownKeys: function ownKeys(state) { + return Reflect.ownKeys(latest(state)); + }, + set: function set(state, prop + /* strictly not, but helps TS */ + , value) { + var desc = getDescriptorFromProto(latest(state), prop); + + if (desc === null || desc === void 0 ? void 0 : desc.set) { + // special case: if this write is captured by a setter, we have + // to trigger it with the correct context + desc.set.call(state.draft_, value); + return true; + } + + if (!state.modified_) { + // the last check is because we need to be able to distinguish setting a non-existig to undefined (which is a change) + // from setting an existing property with value undefined to undefined (which is not a change) + var current = peek(latest(state), prop); // special case, if we assigning the original value to a draft, we can ignore the assignment + + var currentState = current === null || current === void 0 ? void 0 : current[DRAFT_STATE]; + + if (currentState && currentState.base_ === value) { + state.copy_[prop] = value; + state.assigned_[prop] = false; + return true; + } + + if (is(value, current) && (value !== undefined || has(state.base_, prop))) return true; + prepareCopy(state); + markChanged(state); + } // @ts-ignore + + + state.copy_[prop] = value; + state.assigned_[prop] = true; + return true; + }, + deleteProperty: function deleteProperty(state, prop) { + // The `undefined` check is a fast path for pre-existing keys. + if (peek(state.base_, prop) !== undefined || prop in state.base_) { + state.assigned_[prop] = false; + prepareCopy(state); + markChanged(state); + } else { + // if an originally not assigned property was deleted + delete state.assigned_[prop]; + } // @ts-ignore + + + if (state.copy_) delete state.copy_[prop]; + return true; + }, + // Note: We never coerce `desc.value` into an Immer draft, because we can't make + // the same guarantee in ES5 mode. + getOwnPropertyDescriptor: function getOwnPropertyDescriptor(state, prop) { + var owner = latest(state); + var desc = Reflect.getOwnPropertyDescriptor(owner, prop); + if (!desc) return desc; + return { + writable: true, + configurable: state.type_ !== ProxyTypeProxyArray || prop !== "length", + enumerable: desc.enumerable, + value: owner[prop] + }; + }, + defineProperty: function defineProperty() { + die(11); + }, + getPrototypeOf: function getPrototypeOf(state) { + return Object.getPrototypeOf(state.base_); + }, + setPrototypeOf: function setPrototypeOf() { + die(12); + } + }; + /** + * Array drafts + */ + + var arrayTraps = {}; + each(objectTraps, function (key, fn) { + // @ts-ignore + arrayTraps[key] = function () { + arguments[0] = arguments[0][0]; + return fn.apply(this, arguments); + }; + }); + + arrayTraps.deleteProperty = function (state, prop) { + if ( isNaN(parseInt(prop))) die(13); + return objectTraps.deleteProperty.call(this, state[0], prop); + }; + + arrayTraps.set = function (state, prop, value) { + if ( prop !== "length" && isNaN(parseInt(prop))) die(14); + return objectTraps.set.call(this, state[0], prop, value, state[0]); + }; // Access a property without creating an Immer draft. + + + function peek(draft, prop) { + var state = draft[DRAFT_STATE]; + var source = state ? latest(state) : draft; + return source[prop]; + } + + function readPropFromProto(state, source, prop) { + var _desc$get; + + var desc = getDescriptorFromProto(source, prop); + return desc ? "value" in desc ? desc.value : // This is a very special case, if the prop is a getter defined by the + // prototype, we should invoke it with the draft as context! + (_desc$get = desc.get) === null || _desc$get === void 0 ? void 0 : _desc$get.call(state.draft_) : undefined; + } + + function getDescriptorFromProto(source, prop) { + // 'in' checks proto! + if (!(prop in source)) return undefined; + var proto = Object.getPrototypeOf(source); + + while (proto) { + var desc = Object.getOwnPropertyDescriptor(proto, prop); + if (desc) return desc; + proto = Object.getPrototypeOf(proto); + } + + return undefined; + } + + function markChanged(state) { + if (!state.modified_) { + state.modified_ = true; + + if (state.parent_) { + markChanged(state.parent_); + } + } + } + function prepareCopy(state) { + if (!state.copy_) { + state.copy_ = shallowCopy(state.base_); + } + } + + var Immer = + /*#__PURE__*/ + function () { + function Immer(config) { + this.useProxies_ = hasProxies; + this.autoFreeze_ = true + /* istanbul ignore next */ + ; + if (typeof (config === null || config === void 0 ? void 0 : config.useProxies) === "boolean") this.setUseProxies(config.useProxies); + if (typeof (config === null || config === void 0 ? void 0 : config.autoFreeze) === "boolean") this.setAutoFreeze(config.autoFreeze); + this.produce = this.produce.bind(this); + this.produceWithPatches = this.produceWithPatches.bind(this); + } + /** + * The `produce` function takes a value and a "recipe function" (whose + * return value often depends on the base state). The recipe function is + * free to mutate its first argument however it wants. All mutations are + * only ever applied to a __copy__ of the base state. + * + * Pass only a function to create a "curried producer" which relieves you + * from passing the recipe function every time. + * + * Only plain objects and arrays are made mutable. All other objects are + * considered uncopyable. + * + * Note: This function is __bound__ to its `Immer` instance. + * + * @param {any} base - the initial state + * @param {Function} producer - function that receives a proxy of the base state as first argument and which can be freely modified + * @param {Function} patchListener - optional function that will be called with all the patches produced here + * @returns {any} a new state, or the initial state if nothing was modified + */ + + + var _proto = Immer.prototype; + + _proto.produce = function produce(base, recipe, patchListener) { + // curried invocation + if (typeof base === "function" && typeof recipe !== "function") { + var defaultBase = recipe; + recipe = base; + var self = this; + return function curriedProduce(base) { + var _this = this; + + if (base === void 0) { + base = defaultBase; + } + + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + return self.produce(base, function (draft) { + var _recipe; + + return (_recipe = recipe).call.apply(_recipe, [_this, draft].concat(args)); + }); // prettier-ignore + }; + } + + if (typeof recipe !== "function") die(6); + if (patchListener !== undefined && typeof patchListener !== "function") die(7); + var result; // Only plain objects, arrays, and "immerable classes" are drafted. + + if (isDraftable(base)) { + var scope = enterScope(this); + var proxy = createProxy(this, base, undefined); + var hasError = true; + + try { + result = recipe(proxy); + hasError = false; + } finally { + // finally instead of catch + rethrow better preserves original stack + if (hasError) revokeScope(scope);else leaveScope(scope); + } + + if (typeof Promise !== "undefined" && result instanceof Promise) { + return result.then(function (result) { + usePatchesInScope(scope, patchListener); + return processResult(result, scope); + }, function (error) { + revokeScope(scope); + throw error; + }); + } + + usePatchesInScope(scope, patchListener); + return processResult(result, scope); + } else if (!base || typeof base !== "object") { + result = recipe(base); + if (result === NOTHING) return undefined; + if (result === undefined) result = base; + if (this.autoFreeze_) freeze(result, true); + return result; + } else die(21, base); + }; + + _proto.produceWithPatches = function produceWithPatches(arg1, arg2, arg3) { + var _this2 = this; + + if (typeof arg1 === "function") { + return function (state) { + for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + args[_key2 - 1] = arguments[_key2]; + } + + return _this2.produceWithPatches(state, function (draft) { + return arg1.apply(void 0, [draft].concat(args)); + }); + }; + } + + var patches, inversePatches; + var nextState = this.produce(arg1, arg2, function (p, ip) { + patches = p; + inversePatches = ip; + }); + return [nextState, patches, inversePatches]; + }; + + _proto.createDraft = function createDraft(base) { + if (!isDraftable(base)) die(8); + if (isDraft(base)) base = current(base); + var scope = enterScope(this); + var proxy = createProxy(this, base, undefined); + proxy[DRAFT_STATE].isManual_ = true; + leaveScope(scope); + return proxy; + }; + + _proto.finishDraft = function finishDraft(draft, patchListener) { + var state = draft && draft[DRAFT_STATE]; + + { + if (!state || !state.isManual_) die(9); + if (state.finalized_) die(10); + } + + var scope = state.scope_; + usePatchesInScope(scope, patchListener); + return processResult(undefined, scope); + } + /** + * Pass true to automatically freeze all copies created by Immer. + * + * By default, auto-freezing is disabled in production. + */ + ; + + _proto.setAutoFreeze = function setAutoFreeze(value) { + this.autoFreeze_ = value; + } + /** + * Pass true to use the ES2015 `Proxy` class when creating drafts, which is + * always faster than using ES5 proxies. + * + * By default, feature detection is used, so calling this is rarely necessary. + */ + ; + + _proto.setUseProxies = function setUseProxies(value) { + if (value && !hasProxies) { + die(20); + } + + this.useProxies_ = value; + }; + + _proto.applyPatches = function applyPatches(base, patches) { + // If a patch replaces the entire state, take that replacement as base + // before applying patches + var i; + + for (i = patches.length - 1; i >= 0; i--) { + var patch = patches[i]; + + if (patch.path.length === 0 && patch.op === "replace") { + base = patch.value; + break; + } + } + + var applyPatchesImpl = getPlugin("Patches").applyPatches_; + + if (isDraft(base)) { + // N.B: never hits if some patch a replacement, patches are never drafts + return applyPatchesImpl(base, patches); + } // Otherwise, produce a copy of the base state. + + + return this.produce(base, function (draft) { + return applyPatchesImpl(draft, patches.slice(i + 1)); + }); + }; + + return Immer; + }(); + function createProxy(immer, value, parent) { + // precondition: createProxy should be guarded by isDraftable, so we know we can safely draft + var draft = isMap(value) ? getPlugin("MapSet").proxyMap_(value, parent) : isSet(value) ? getPlugin("MapSet").proxySet_(value, parent) : immer.useProxies_ ? createProxyProxy(value, parent) : getPlugin("ES5").createES5Proxy_(value, parent); + var scope = parent ? parent.scope_ : getCurrentScope(); + scope.drafts_.push(draft); + return draft; + } + + function current(value) { + if (!isDraft(value)) die(22, value); + return currentImpl(value); + } + + function currentImpl(value) { + if (!isDraftable(value)) return value; + var state = value[DRAFT_STATE]; + var copy; + var archType = getArchtype(value); + + if (state) { + if (!state.modified_ && (state.type_ < 4 || !getPlugin("ES5").hasChanges_(state))) return state.base_; // Optimization: avoid generating new drafts during copying + + state.finalized_ = true; + copy = copyHelper(value, archType); + state.finalized_ = false; + } else { + copy = copyHelper(value, archType); + } + + each(copy, function (key, childValue) { + if (state && get(state.base_, key) === childValue) return; // no need to copy or search in something that didn't change + + set(copy, key, currentImpl(childValue)); + }); // In the future, we might consider freezing here, based on the current settings + + return archType === ArchtypeSet ? new Set(copy) : copy; + } + + function copyHelper(value, archType) { + // creates a shallow copy, even if it is a map or set + switch (archType) { + case ArchtypeMap: + return new Map(value); + + case ArchtypeSet: + // Set will be cloned as array temporarily, so that we can replace individual items + return Array.from(value); + } + + return shallowCopy(value); + } + + function enableES5() { + function willFinalizeES5_(scope, result, isReplaced) { + if (!isReplaced) { + if (scope.patches_) { + markChangesRecursively(scope.drafts_[0]); + } // This is faster when we don't care about which attributes changed. + + + markChangesSweep(scope.drafts_); + } // When a child draft is returned, look for changes. + else if (isDraft(result) && result[DRAFT_STATE].scope_ === scope) { + markChangesSweep(scope.drafts_); + } + } + + function createES5Draft(isArray, base) { + if (isArray) { + var draft = new Array(base.length); + + for (var i = 0; i < base.length; i++) { + Object.defineProperty(draft, "" + i, proxyProperty(i, true)); + } + + return draft; + } else { + var _descriptors = getOwnPropertyDescriptors(base); + + delete _descriptors[DRAFT_STATE]; + var keys = ownKeys(_descriptors); + + for (var _i = 0; _i < keys.length; _i++) { + var key = keys[_i]; + _descriptors[key] = proxyProperty(key, isArray || !!_descriptors[key].enumerable); + } + + return Object.create(Object.getPrototypeOf(base), _descriptors); + } + } + + function createES5Proxy_(base, parent) { + var isArray = Array.isArray(base); + var draft = createES5Draft(isArray, base); + var state = { + type_: isArray ? ProxyTypeES5Array : ProxyTypeES5Object, + scope_: parent ? parent.scope_ : getCurrentScope(), + modified_: false, + finalized_: false, + assigned_: {}, + parent_: parent, + // base is the object we are drafting + base_: base, + // draft is the draft object itself, that traps all reads and reads from either the base (if unmodified) or copy (if modified) + draft_: draft, + copy_: null, + revoked_: false, + isManual_: false + }; + Object.defineProperty(draft, DRAFT_STATE, { + value: state, + // enumerable: false <- the default + writable: true + }); + return draft; + } // property descriptors are recycled to make sure we don't create a get and set closure per property, + // but share them all instead + + + var descriptors = {}; + + function proxyProperty(prop, enumerable) { + var desc = descriptors[prop]; + + if (desc) { + desc.enumerable = enumerable; + } else { + descriptors[prop] = desc = { + configurable: true, + enumerable: enumerable, + get: function get() { + var state = this[DRAFT_STATE]; + assertUnrevoked(state); // @ts-ignore + + return objectTraps.get(state, prop); + }, + set: function set(value) { + var state = this[DRAFT_STATE]; + assertUnrevoked(state); // @ts-ignore + + objectTraps.set(state, prop, value); + } + }; + } + + return desc; + } // This looks expensive, but only proxies are visited, and only objects without known changes are scanned. + + + function markChangesSweep(drafts) { + // The natural order of drafts in the `scope` array is based on when they + // were accessed. By processing drafts in reverse natural order, we have a + // better chance of processing leaf nodes first. When a leaf node is known to + // have changed, we can avoid any traversal of its ancestor nodes. + for (var i = drafts.length - 1; i >= 0; i--) { + var state = drafts[i][DRAFT_STATE]; + + if (!state.modified_) { + switch (state.type_) { + case ProxyTypeES5Array: + if (hasArrayChanges(state)) markChanged(state); + break; + + case ProxyTypeES5Object: + if (hasObjectChanges(state)) markChanged(state); + break; + } + } + } + } + + function markChangesRecursively(object) { + if (!object || typeof object !== "object") return; + var state = object[DRAFT_STATE]; + if (!state) return; + var base_ = state.base_, + draft_ = state.draft_, + assigned_ = state.assigned_, + type_ = state.type_; + + if (type_ === ProxyTypeES5Object) { + // Look for added keys. + // probably there is a faster way to detect changes, as sweep + recurse seems to do some + // unnecessary work. + // also: probably we can store the information we detect here, to speed up tree finalization! + each(draft_, function (key) { + if (key === DRAFT_STATE) return; // The `undefined` check is a fast path for pre-existing keys. + + if (base_[key] === undefined && !has(base_, key)) { + assigned_[key] = true; + markChanged(state); + } else if (!assigned_[key]) { + // Only untouched properties trigger recursion. + markChangesRecursively(draft_[key]); + } + }); // Look for removed keys. + + each(base_, function (key) { + // The `undefined` check is a fast path for pre-existing keys. + if (draft_[key] === undefined && !has(draft_, key)) { + assigned_[key] = false; + markChanged(state); + } }); - let m = !1; - r && null === l.serverInfoInstance && (m = !0); - const g = nm(s, o, h, f, m, null === (e = null === (e = fg()) || void 0 === e ? void 0 : e.getQuery()) || void 0 === e ? void 0 : e.extendMaxTTFB).pipe(Za(([, , e, t]) => { - i && d.setBandwidthSample(Object.assign(Object.assign({}, e), { - mediaOptionType: s.mediaOptionType - })), r && m && (l.serverInfoInstance = t), p && a.updateInflightFrag(u, s.mediaOptionType, s, "loaded", e) - }), Za(([e, , t]) => { - r && l.handleFragLoaded(e, t) - }), lg(u, c, Lc(s, h), o.maxNumAddLevelToPenaltyBox, !1, t, a, d)); - return "AES-128" === (null === (e = s.keyTagInfo) || void 0 === e ? void 0 : e.method) ? en([_g(n, s.keyTagInfo, { - itemId: s.itemId, - mediaOptionId: s.mediaOptionId - }), g]).pipe(La(([e, t]) => { - const [i, r] = t; - return i.keyTagInfo.key = e.key, Nm(i, r, o, n.logger, n.rpcClients.crypto) - })) : g.pipe(hr(e => e[1])) + } else if (type_ === ProxyTypeES5Array) { + if (hasArrayChanges(state)) { + markChanged(state); + assigned_.length = true; + } + + if (draft_.length < base_.length) { + for (var i = draft_.length; i < base_.length; i++) { + assigned_[i] = false; + } + } else { + for (var _i2 = base_.length; _i2 < draft_.length; _i2++) { + assigned_[_i2] = true; + } + } // Minimum count is enough, the other parts has been processed. + + + var min = Math.min(draft_.length, base_.length); + + for (var _i3 = 0; _i3 < min; _i3++) { + // Only untouched indices trigger recursion. + if (assigned_[_i3] === undefined) markChangesRecursively(draft_[_i3]); + } + } } - const Lg = (i, r, e) => { - const { - rootPlaylistService: n, - rootPlaylistQuery: s - } = i, { - timelineOffset: a, - mediaFragment: o - } = e.foundFrag, { - itemId: l, - discoSeqNum: d - } = o; - return xg(i, o).pipe(La(t => (n.updateInflightFrag(l, o.mediaOptionType, o, "loading", null), Rg(i, o, !0, !0).pipe(La(e => { - return n.updateInflightFrag(l, r, o, "parsing", null), - function(e, t, i, y, v, S) { - const r = new Uint8Array(e), - { - legibleSystemAdapter: n, - rootPlaylistService: s, - mediaSink: a, - mediaParser: o, - rootPlaylistQuery: l, - mediaLibraryService: b - } = S, - d = a["mediaQuery"], - u = b.getQueryForOption(y), - { - mediaOption: c, - mediaOptionDetails: h - } = u, - p = h["initSegments"], - { - itemId: T, - mediaOptionId: E - } = c, - { - discoSeqNum: I, - mediaSeqNum: w, - mediaOptionType: f, - isLastFragment: A - } = y, - m = d.seeking, - g = h.liveOrEvent, - O = p[I], - k = f === gu.Variant && h.ptsKnown, - C = { - segment: r, - frag: y, - seeking: m, - live: g, - ptsKnown: k, - totalDuration: h.totalduration, - defaultInitPTS: t, - iframeMediaStart: Up(y) ? y.iframeMediaStart : void 0, - iframeDuration: Up(y) ? y.iframeMediaDuration : void 0, - iframeOriginalStart: Up(y) ? y.iframeOriginalStart : void 0 - }; - let D; - if (null != i && (null === (t = y.keyTagInfo) || void 0 === t ? void 0 : t.uri) === (null === (t = i.keyTagInfo) || void 0 === t ? void 0 : t.uri)) D = $i(i); - else if (null != i) { - const M = Object.assign(Object.assign({}, i.fragment), { - keyTagInfo: y.keyTagInfo - }); - D = Pg(O ? i.data : e, M, S) - } else D = Pg(e, y, S); - return D.pipe(La(m => { - const g = performance.now(); - if (null != m) { - const g = m["data"], - e = new Uint8Array(g); - C.initSegment = e - } - return y.mediaOptionType === gu.Variant && (null == n || n.setupForFrag(y)), o.parseSegment(C, "").pipe(hr(e => { - var t = performance.now(), - { - startPTS: i, - startDTS: r, - endPTS: n, - endDTS: s, - firstKeyframePts: a, - framesWithoutIDR: o, - dropped: l, - data1: d, - data2: u, - captionData: c, - id3Samples: h, - parsedInitSegment: e - } = e, - t = { - durationSec: n.baseTime / n.timescale - i.baseTime / i.timescale, - parseTimeMs: t - g - }; - S.statsService.setFragSample(t); - let p = Object.assign({}, m); - if (e) { - const { - track: g, - moovData: f, - mimeType: v - } = e, S = g["initSegment"]; - p = { - itemId: T, - mediaOptionId: E, - discoSeqNum: I, - initParsedData: f, - data: S, - mimeType: v, - keyTagInfo: y.keyTagInfo, - fragment: y - }, b.archiveInitSegmentEntity(m, p) - } - e = y.keyTagInfo; - return [p, { - itemId: T, - mediaOptionId: E, - mediaSeqNum: w, - discoSeqNum: I, - startDtsTs: r, - endDtsTs: s, - timelineOffset: v, - firstKeyframePts: a, - framesWithoutIDR: o, - dropped: l, - data1: d, - data2: u, - startPts: i, - endPts: n, - keyTagInfo: e, - isLastFragment: A, - iframe: null !== (e = y.iframe) && void 0 !== e && e, - duration: y.duration, - iframeMediaDuration: Up(y) ? y.iframeMediaDuration : void 0, - iframeOriginalStart: Up(y) ? y.iframeOriginalStart : void 0, - captionData: c, - id3Samples: h - }] - })) - }), sg(s, l, f, E)) - }(e, null === (e = s.getInitPTS(d)) || void 0 === e ? void 0 : e.offsetTimestamp, t, o, a, i) - }), Za(e => { - n.updateInflightFrag(l, r, o, "parsed", null) - }), tc(`retrieveMediaFragmentCacheEntity.${r}.emit`)))), Ds(1)) + + function hasObjectChanges(state) { + var base_ = state.base_, + draft_ = state.draft_; // Search for added keys and changed keys. Start at the back, because + // non-numeric keys are ordered by time of definition on the object. + + var keys = ownKeys(draft_); + + for (var i = keys.length - 1; i >= 0; i--) { + var key = keys[i]; + if (key === DRAFT_STATE) continue; + var baseValue = base_[key]; // The `undefined` check is a fast path for pre-existing keys. + + if (baseValue === undefined && !has(base_, key)) { + return true; + } // Once a base key is deleted, future changes go undetected, because its + // descriptor is erased. This branch detects any missed changes. + else { + var value = draft_[key]; + + var _state = value && value[DRAFT_STATE]; + + if (_state ? _state.base_ !== baseValue : !is(value, baseValue)) { + return true; + } + } + } // At this point, no keys were added or changed. + // Compare key count to determine if keys were deleted. + + + var baseIsDraft = !!base_[DRAFT_STATE]; + return keys.length !== ownKeys(base_).length + (baseIsDraft ? 0 : 1); // + 1 to correct for DRAFT_STATE + } + + function hasArrayChanges(state) { + var draft_ = state.draft_; + if (draft_.length !== state.base_.length) return true; // See #116 + // If we first shorten the length, our array interceptors will be removed. + // If after that new items are added, result in the same original length, + // those last items will have no intercepting property. + // So if there is no own descriptor on the last position, we know that items were removed and added + // N.B.: splice, unshift, etc only shift values around, but not prop descriptors, so we only have to check + // the last one + + var descriptor = Object.getOwnPropertyDescriptor(draft_, draft_.length - 1); // descriptor can be null, but only for newly created sparse arrays, eg. new Array(10) + + if (descriptor && !descriptor.get) return true; // For all other cases, we don't have to compare, as they would have been picked up by the index setters + + return false; + } + + function hasChanges_(state) { + return state.type_ === ProxyTypeES5Object ? hasObjectChanges(state) : hasArrayChanges(state); + } + + function assertUnrevoked(state + /*ES5State | MapState | SetState*/ + ) { + if (state.revoked_) die(3, JSON.stringify(latest(state))); + } + + loadPlugin("ES5", { + createES5Proxy_: createES5Proxy_, + willFinalizeES5_: willFinalizeES5_, + hasChanges_: hasChanges_ + }); + } + + function enablePatches() { + var REPLACE = "replace"; + var ADD = "add"; + var REMOVE = "remove"; + + function generatePatches_(state, basePath, patches, inversePatches) { + switch (state.type_) { + case ProxyTypeProxyObject: + case ProxyTypeES5Object: + case ProxyTypeMap: + return generatePatchesFromAssigned(state, basePath, patches, inversePatches); + + case ProxyTypeES5Array: + case ProxyTypeProxyArray: + return generateArrayPatches(state, basePath, patches, inversePatches); + + case ProxyTypeSet: + return generateSetPatches(state, basePath, patches, inversePatches); + } + } + + function generateArrayPatches(state, basePath, patches, inversePatches) { + var base_ = state.base_, + assigned_ = state.assigned_; + var copy_ = state.copy_; // Reduce complexity by ensuring `base` is never longer. + + if (copy_.length < base_.length) { + var _ref = [copy_, base_]; + base_ = _ref[0]; + copy_ = _ref[1]; + var _ref2 = [inversePatches, patches]; + patches = _ref2[0]; + inversePatches = _ref2[1]; + } // Process replaced indices. + + + for (var i = 0; i < base_.length; i++) { + if (assigned_[i] && copy_[i] !== base_[i]) { + var path = basePath.concat([i]); + patches.push({ + op: REPLACE, + path: path, + // Need to maybe clone it, as it can in fact be the original value + // due to the base/copy inversion at the start of this function + value: clonePatchValueIfNeeded(copy_[i]) + }); + inversePatches.push({ + op: REPLACE, + path: path, + value: clonePatchValueIfNeeded(base_[i]) + }); + } + } // Process added indices. + + + for (var _i = base_.length; _i < copy_.length; _i++) { + var _path = basePath.concat([_i]); + + patches.push({ + op: ADD, + path: _path, + // Need to maybe clone it, as it can in fact be the original value + // due to the base/copy inversion at the start of this function + value: clonePatchValueIfNeeded(copy_[_i]) + }); + } + + if (base_.length < copy_.length) { + inversePatches.push({ + op: REPLACE, + path: basePath.concat(["length"]), + value: base_.length + }); + } + } // This is used for both Map objects and normal objects. + + + function generatePatchesFromAssigned(state, basePath, patches, inversePatches) { + var base_ = state.base_, + copy_ = state.copy_; + each(state.assigned_, function (key, assignedValue) { + var origValue = get(base_, key); + var value = get(copy_, key); + var op = !assignedValue ? REMOVE : has(base_, key) ? REPLACE : ADD; + if (origValue === value && op === REPLACE) return; + var path = basePath.concat(key); + patches.push(op === REMOVE ? { + op: op, + path: path + } : { + op: op, + path: path, + value: value + }); + inversePatches.push(op === ADD ? { + op: REMOVE, + path: path + } : op === REMOVE ? { + op: ADD, + path: path, + value: clonePatchValueIfNeeded(origValue) + } : { + op: REPLACE, + path: path, + value: clonePatchValueIfNeeded(origValue) + }); + }); + } + + function generateSetPatches(state, basePath, patches, inversePatches) { + var base_ = state.base_, + copy_ = state.copy_; + var i = 0; + base_.forEach(function (value) { + if (!copy_.has(value)) { + var path = basePath.concat([i]); + patches.push({ + op: REMOVE, + path: path, + value: value + }); + inversePatches.unshift({ + op: ADD, + path: path, + value: value + }); + } + + i++; + }); + i = 0; + copy_.forEach(function (value) { + if (!base_.has(value)) { + var path = basePath.concat([i]); + patches.push({ + op: ADD, + path: path, + value: value + }); + inversePatches.unshift({ + op: REMOVE, + path: path, + value: value + }); + } + + i++; + }); + } + + function generateReplacementPatches_(rootState, replacement, patches, inversePatches) { + patches.push({ + op: REPLACE, + path: [], + value: replacement + }); + inversePatches.push({ + op: REPLACE, + path: [], + value: rootState.base_ + }); + } + + function applyPatches_(draft, patches) { + patches.forEach(function (patch) { + var path = patch.path, + op = patch.op; + var base = draft; + + for (var i = 0; i < path.length - 1; i++) { + base = get(base, path[i]); + if (typeof base !== "object") die(15, path.join("/")); + } + + var type = getArchtype(base); + var value = deepClonePatchValue(patch.value); // used to clone patch to ensure original patch is not modified, see #411 + + var key = path[path.length - 1]; + + switch (op) { + case REPLACE: + switch (type) { + case ArchtypeMap: + return base.set(key, value); + + /* istanbul ignore next */ + + case ArchtypeSet: + die(16); + + default: + // if value is an object, then it's assigned by reference + // in the following add or remove ops, the value field inside the patch will also be modifyed + // so we use value from the cloned patch + // @ts-ignore + return base[key] = value; + } + + case ADD: + switch (type) { + case ArchtypeArray: + return base.splice(key, 0, value); + + case ArchtypeMap: + return base.set(key, value); + + case ArchtypeSet: + return base.add(value); + + default: + return base[key] = value; + } + + case REMOVE: + switch (type) { + case ArchtypeArray: + return base.splice(key, 1); + + case ArchtypeMap: + return base.delete(key); + + case ArchtypeSet: + return base.delete(patch.value); + + default: + return delete base[key]; + } + + default: + die(17, op); + } + }); + return draft; + } + + function deepClonePatchValue(obj) { + if (!isDraftable(obj)) return obj; + if (Array.isArray(obj)) return obj.map(deepClonePatchValue); + if (isMap(obj)) return new Map(Array.from(obj.entries()).map(function (_ref3) { + var k = _ref3[0], + v = _ref3[1]; + return [k, deepClonePatchValue(v)]; + })); + if (isSet(obj)) return new Set(Array.from(obj).map(deepClonePatchValue)); + var cloned = Object.create(Object.getPrototypeOf(obj)); + + for (var key in obj) { + cloned[key] = deepClonePatchValue(obj[key]); + } + + return cloned; + } + + function clonePatchValueIfNeeded(obj) { + if (isDraft(obj)) { + return deepClonePatchValue(obj); + } else return obj; + } + + loadPlugin("Patches", { + applyPatches_: applyPatches_, + generatePatches_: generatePatches_, + generateReplacementPatches_: generateReplacementPatches_ + }); + } + + // types only! + function enableMapSet() { + /* istanbul ignore next */ + var _extendStatics = function extendStatics(d, b) { + _extendStatics = Object.setPrototypeOf || { + __proto__: [] + } instanceof Array && function (d, b) { + d.__proto__ = b; + } || function (d, b) { + for (var p in b) { + if (b.hasOwnProperty(p)) d[p] = b[p]; + } + }; + + return _extendStatics(d, b); + }; // Ugly hack to resolve #502 and inherit built in Map / Set + + + function __extends(d, b) { + _extendStatics(d, b); + + function __() { + this.constructor = d; + } + + d.prototype = ( // @ts-ignore + __.prototype = b.prototype, new __()); + } + + var DraftMap = function (_super) { + __extends(DraftMap, _super); // Create class manually, cause #502 + + + function DraftMap(target, parent) { + this[DRAFT_STATE] = { + type_: ProxyTypeMap, + parent_: parent, + scope_: parent ? parent.scope_ : getCurrentScope(), + modified_: false, + finalized_: false, + copy_: undefined, + assigned_: undefined, + base_: target, + draft_: this, + isManual_: false, + revoked_: false + }; + return this; + } + + var p = DraftMap.prototype; + Object.defineProperty(p, "size", { + get: function get() { + return latest(this[DRAFT_STATE]).size; + } // enumerable: false, + // configurable: true + + }); + + p.has = function (key) { + return latest(this[DRAFT_STATE]).has(key); + }; + + p.set = function (key, value) { + var state = this[DRAFT_STATE]; + assertUnrevoked(state); + + if (!latest(state).has(key) || latest(state).get(key) !== value) { + prepareMapCopy(state); + markChanged(state); + state.assigned_.set(key, true); + state.copy_.set(key, value); + state.assigned_.set(key, true); + } + + return this; + }; + + p.delete = function (key) { + if (!this.has(key)) { + return false; + } + + var state = this[DRAFT_STATE]; + assertUnrevoked(state); + prepareMapCopy(state); + markChanged(state); + state.assigned_.set(key, false); + state.copy_.delete(key); + return true; + }; + + p.clear = function () { + var state = this[DRAFT_STATE]; + assertUnrevoked(state); + + if (latest(state).size) { + prepareMapCopy(state); + markChanged(state); + state.assigned_ = new Map(); + each(state.base_, function (key) { + state.assigned_.set(key, false); + }); + state.copy_.clear(); + } + }; + + p.forEach = function (cb, thisArg) { + var _this = this; + + var state = this[DRAFT_STATE]; + latest(state).forEach(function (_value, key, _map) { + cb.call(thisArg, _this.get(key), key, _this); + }); + }; + + p.get = function (key) { + var state = this[DRAFT_STATE]; + assertUnrevoked(state); + var value = latest(state).get(key); + + if (state.finalized_ || !isDraftable(value)) { + return value; + } + + if (value !== state.base_.get(key)) { + return value; // either already drafted or reassigned + } // despite what it looks, this creates a draft only once, see above condition + + + var draft = createProxy(state.scope_.immer_, value, state); + prepareMapCopy(state); + state.copy_.set(key, draft); + return draft; + }; + + p.keys = function () { + return latest(this[DRAFT_STATE]).keys(); + }; + + p.values = function () { + var _this2 = this, + _ref; + + var iterator = this.keys(); + return _ref = {}, _ref[iteratorSymbol] = function () { + return _this2.values(); + }, _ref.next = function next() { + var r = iterator.next(); + /* istanbul ignore next */ + + if (r.done) return r; + + var value = _this2.get(r.value); + + return { + done: false, + value: value + }; + }, _ref; + }; + + p.entries = function () { + var _this3 = this, + _ref2; + + var iterator = this.keys(); + return _ref2 = {}, _ref2[iteratorSymbol] = function () { + return _this3.entries(); + }, _ref2.next = function next() { + var r = iterator.next(); + /* istanbul ignore next */ + + if (r.done) return r; + + var value = _this3.get(r.value); + + return { + done: false, + value: [r.value, value] + }; + }, _ref2; + }; + + p[iteratorSymbol] = function () { + return this.entries(); + }; + + return DraftMap; + }(Map); + + function proxyMap_(target, parent) { + // @ts-ignore + return new DraftMap(target, parent); + } + + function prepareMapCopy(state) { + if (!state.copy_) { + state.assigned_ = new Map(); + state.copy_ = new Map(state.base_); + } + } + + var DraftSet = function (_super) { + __extends(DraftSet, _super); // Create class manually, cause #502 + + + function DraftSet(target, parent) { + this[DRAFT_STATE] = { + type_: ProxyTypeSet, + parent_: parent, + scope_: parent ? parent.scope_ : getCurrentScope(), + modified_: false, + finalized_: false, + copy_: undefined, + base_: target, + draft_: this, + drafts_: new Map(), + revoked_: false, + isManual_: false + }; + return this; + } + + var p = DraftSet.prototype; + Object.defineProperty(p, "size", { + get: function get() { + return latest(this[DRAFT_STATE]).size; + } // enumerable: true, + + }); + + p.has = function (value) { + var state = this[DRAFT_STATE]; + assertUnrevoked(state); // bit of trickery here, to be able to recognize both the value, and the draft of its value + + if (!state.copy_) { + return state.base_.has(value); + } + + if (state.copy_.has(value)) return true; + if (state.drafts_.has(value) && state.copy_.has(state.drafts_.get(value))) return true; + return false; + }; + + p.add = function (value) { + var state = this[DRAFT_STATE]; + assertUnrevoked(state); + + if (!this.has(value)) { + prepareSetCopy(state); + markChanged(state); + state.copy_.add(value); + } + + return this; + }; + + p.delete = function (value) { + if (!this.has(value)) { + return false; + } + + var state = this[DRAFT_STATE]; + assertUnrevoked(state); + prepareSetCopy(state); + markChanged(state); + return state.copy_.delete(value) || (state.drafts_.has(value) ? state.copy_.delete(state.drafts_.get(value)) : + /* istanbul ignore next */ + false); + }; + + p.clear = function () { + var state = this[DRAFT_STATE]; + assertUnrevoked(state); + + if (latest(state).size) { + prepareSetCopy(state); + markChanged(state); + state.copy_.clear(); + } + }; + + p.values = function () { + var state = this[DRAFT_STATE]; + assertUnrevoked(state); + prepareSetCopy(state); + return state.copy_.values(); + }; + + p.entries = function entries() { + var state = this[DRAFT_STATE]; + assertUnrevoked(state); + prepareSetCopy(state); + return state.copy_.entries(); + }; + + p.keys = function () { + return this.values(); + }; + + p[iteratorSymbol] = function () { + return this.values(); + }; + + p.forEach = function forEach(cb, thisArg) { + var iterator = this.values(); + var result = iterator.next(); + + while (!result.done) { + cb.call(thisArg, result.value, result.value, this); + result = iterator.next(); + } + }; + + return DraftSet; + }(Set); + + function proxySet_(target, parent) { + // @ts-ignore + return new DraftSet(target, parent); + } + + function prepareSetCopy(state) { + if (!state.copy_) { + // create drafts for all entries to preserve insertion order + state.copy_ = new Set(); + state.base_.forEach(function (value) { + if (isDraftable(value)) { + var draft = createProxy(state.scope_.immer_, value, state); + state.drafts_.set(value, draft); + state.copy_.add(draft); + } else { + state.copy_.add(value); + } + }); + } + } + + function assertUnrevoked(state + /*ES5State | MapState | SetState*/ + ) { + if (state.revoked_) die(3, JSON.stringify(latest(state))); + } + + loadPlugin("MapSet", { + proxyMap_: proxyMap_, + proxySet_: proxySet_ + }); + } + + function enableAllPlugins() { + enableES5(); + enableMapSet(); + enablePatches(); + } + + var immer = + /*#__PURE__*/ + new Immer(); + /** + * The `produce` function takes a value and a "recipe function" (whose + * return value often depends on the base state). The recipe function is + * free to mutate its first argument however it wants. All mutations are + * only ever applied to a __copy__ of the base state. + * + * Pass only a function to create a "curried producer" which relieves you + * from passing the recipe function every time. + * + * Only plain objects and arrays are made mutable. All other objects are + * considered uncopyable. + * + * Note: This function is __bound__ to its `Immer` instance. + * + * @param {any} base - the initial state + * @param {Function} producer - function that receives a proxy of the base state as first argument and which can be freely modified + * @param {Function} patchListener - optional function that will be called with all the patches produced here + * @returns {any} a new state, or the initial state if nothing was modified + */ + + var produce = immer.produce; + /** + * Like `produce`, but `produceWithPatches` always returns a tuple + * [nextState, patches, inversePatches] (instead of just the next state) + */ + + var produceWithPatches = + /*#__PURE__*/ + immer.produceWithPatches.bind(immer); + /** + * Pass true to automatically freeze all copies created by Immer. + * + * By default, auto-freezing is disabled in production. + */ + + var setAutoFreeze = + /*#__PURE__*/ + immer.setAutoFreeze.bind(immer); + /** + * Pass true to use the ES2015 `Proxy` class when creating drafts, which is + * always faster than using ES5 proxies. + * + * By default, feature detection is used, so calling this is rarely necessary. + */ + + var setUseProxies = + /*#__PURE__*/ + immer.setUseProxies.bind(immer); + /** + * Apply an array of Immer patches to the first argument. + * + * This function is a producer, which means copy-on-write is in effect. + */ + + var applyPatches = + /*#__PURE__*/ + immer.applyPatches.bind(immer); + /** + * Create an Immer draft from the given base state, which may be a draft itself. + * The draft can be modified until you finalize it with the `finishDraft` function. + */ + + var createDraft = + /*#__PURE__*/ + immer.createDraft.bind(immer); + /** + * Finalize an Immer draft from a `createDraft` call, returning the base state + * (if no changes were made) or a modified copy. The draft must *not* be + * mutated afterwards. + * + * Pass a function as the 2nd argument to generate Immer patches based on the + * changes that were made. + */ + + var finishDraft = + /*#__PURE__*/ + immer.finishDraft.bind(immer); + /** + * This function is actually a no-op, but can be used to cast an immutable type + * to an draft type and make TypeScript happy + * + * @param value + */ + + function castDraft(value) { + return value; + } + /** + * This function is actually a no-op, but can be used to cast a mutable type + * to an immutable type and make TypeScript happy + * @param value + */ + + function castImmutable(value) { + return value; + } + + immer_cjs_development.Immer = Immer; + immer_cjs_development.applyPatches = applyPatches; + immer_cjs_development.castDraft = castDraft; + immer_cjs_development.castImmutable = castImmutable; + immer_cjs_development.createDraft = createDraft; + immer_cjs_development.current = current; + immer_cjs_development.default = produce; + immer_cjs_development.enableAllPlugins = enableAllPlugins; + immer_cjs_development.enableES5 = enableES5; + var enableMapSet_1 = immer_cjs_development.enableMapSet = enableMapSet; + immer_cjs_development.enablePatches = enablePatches; + immer_cjs_development.finishDraft = finishDraft; + immer_cjs_development.immerable = DRAFTABLE; + immer_cjs_development.isDraft = isDraft; + immer_cjs_development.isDraftable = isDraftable; + immer_cjs_development.nothing = NOTHING; + immer_cjs_development.original = original; + var produce_1 = immer_cjs_development.produce = produce; + immer_cjs_development.produceWithPatches = produceWithPatches; + var setAutoFreeze_1 = immer_cjs_development.setAutoFreeze = setAutoFreeze; + immer_cjs_development.setUseProxies = setUseProxies; + + var rtc = {exports: {}}; + + /** + * + * Copyright (c) 2018 Apple (http://www.apple.com) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + + (function (module, exports) { + /*! Modules included in this bundle: + * name: process + * license: MIT + * version: 0.11.10 + * text: + * (The MIT License) + * + * Copyright (c) 2013 Roman Shtylman + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + * name: semver + * license: ISC + * version: 5.5.0 + * text: + * The ISC License + * + * Copyright (c) Isaac Z. Schlueter and Contributors + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR + * IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * + * name: webpack + * license: MIT + * version: 4.41.2 + * text: + * Copyright JS Foundation and other contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * 'Software'), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * + */ + (function webpackUniversalModuleDefinition(root, factory) { + module.exports = factory(); + })(commonjsGlobal, function() { + return /******/ (function(modules) { // webpackBootstrap + /******/ // The module cache + /******/ var installedModules = {}; + /******/ + /******/ // The require function + /******/ function __webpack_require__(moduleId) { + /******/ + /******/ // Check if module is in cache + /******/ if(installedModules[moduleId]) { + /******/ return installedModules[moduleId].exports; + /******/ } + /******/ // Create a new module (and put it into the cache) + /******/ var module = installedModules[moduleId] = { + /******/ i: moduleId, + /******/ l: false, + /******/ exports: {} + /******/ }; + /******/ + /******/ // Execute the module function + /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); + /******/ + /******/ // Flag the module as loaded + /******/ module.l = true; + /******/ + /******/ // Return the exports of the module + /******/ return module.exports; + /******/ } + /******/ + /******/ + /******/ // expose the modules object (__webpack_modules__) + /******/ __webpack_require__.m = modules; + /******/ + /******/ // expose the module cache + /******/ __webpack_require__.c = installedModules; + /******/ + /******/ // define getter function for harmony exports + /******/ __webpack_require__.d = function(exports, name, getter) { + /******/ if(!__webpack_require__.o(exports, name)) { + /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); + /******/ } + /******/ }; + /******/ + /******/ // define __esModule on exports + /******/ __webpack_require__.r = function(exports) { + /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { + /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); + /******/ } + /******/ Object.defineProperty(exports, '__esModule', { value: true }); + /******/ }; + /******/ + /******/ // create a fake namespace object + /******/ // mode & 1: value is a module id, require it + /******/ // mode & 2: merge all properties of value into the ns + /******/ // mode & 4: return value when already ns object + /******/ // mode & 8|1: behave like require + /******/ __webpack_require__.t = function(value, mode) { + /******/ if(mode & 1) value = __webpack_require__(value); + /******/ if(mode & 8) return value; + /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; + /******/ var ns = Object.create(null); + /******/ __webpack_require__.r(ns); + /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); + /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); + /******/ return ns; + /******/ }; + /******/ + /******/ // getDefaultExport function for compatibility with non-harmony modules + /******/ __webpack_require__.n = function(module) { + /******/ var getter = module && module.__esModule ? + /******/ function getDefault() { return module['default']; } : + /******/ function getModuleExports() { return module; }; + /******/ __webpack_require__.d(getter, 'a', getter); + /******/ return getter; + /******/ }; + /******/ + /******/ // Object.prototype.hasOwnProperty.call + /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; + /******/ + /******/ // __webpack_public_path__ + /******/ __webpack_require__.p = ""; + /******/ + /******/ + /******/ // Load entry module and return exports + /******/ return __webpack_require__(__webpack_require__.s = "./src/rtc_reporting/reporting_agent.ts"); + /******/ }) + /************************************************************************/ + /******/ ({ + + /***/ "./node_modules/process/browser.js": + /*!*****************************************!*\ + !*** ./node_modules/process/browser.js ***! + \*****************************************/ + /*! no static exports found */ + /***/ (function(module, exports) { + + // shim for using process in browser + var process = module.exports = {}; + + // cached from whatever global is present so that test runners that stub it + // don't break things. But we need to wrap it in a try catch in case it is + // wrapped in strict mode code which doesn't define any globals. It's inside a + // function because try/catches deoptimize in certain engines. + + var cachedSetTimeout; + var cachedClearTimeout; + + function defaultSetTimout() { + throw new Error('setTimeout has not been defined'); + } + function defaultClearTimeout () { + throw new Error('clearTimeout has not been defined'); + } + (function () { + try { + if (typeof setTimeout === 'function') { + cachedSetTimeout = setTimeout; + } else { + cachedSetTimeout = defaultSetTimout; + } + } catch (e) { + cachedSetTimeout = defaultSetTimout; + } + try { + if (typeof clearTimeout === 'function') { + cachedClearTimeout = clearTimeout; + } else { + cachedClearTimeout = defaultClearTimeout; + } + } catch (e) { + cachedClearTimeout = defaultClearTimeout; + } + } ()); + function runTimeout(fun) { + if (cachedSetTimeout === setTimeout) { + //normal enviroments in sane situations + return setTimeout(fun, 0); + } + // if setTimeout wasn't available but was latter defined + if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { + cachedSetTimeout = setTimeout; + return setTimeout(fun, 0); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedSetTimeout(fun, 0); + } catch(e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedSetTimeout.call(null, fun, 0); + } catch(e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error + return cachedSetTimeout.call(this, fun, 0); + } + } + + + } + function runClearTimeout(marker) { + if (cachedClearTimeout === clearTimeout) { + //normal enviroments in sane situations + return clearTimeout(marker); + } + // if clearTimeout wasn't available but was latter defined + if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { + cachedClearTimeout = clearTimeout; + return clearTimeout(marker); + } + try { + // when when somebody has screwed with setTimeout but no I.E. maddness + return cachedClearTimeout(marker); + } catch (e){ + try { + // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally + return cachedClearTimeout.call(null, marker); + } catch (e){ + // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. + // Some versions of I.E. have different rules for clearTimeout vs setTimeout + return cachedClearTimeout.call(this, marker); + } + } + + + + } + var queue = []; + var draining = false; + var currentQueue; + var queueIndex = -1; + + function cleanUpNextTick() { + if (!draining || !currentQueue) { + return; + } + draining = false; + if (currentQueue.length) { + queue = currentQueue.concat(queue); + } else { + queueIndex = -1; + } + if (queue.length) { + drainQueue(); + } + } + + function drainQueue() { + if (draining) { + return; + } + var timeout = runTimeout(cleanUpNextTick); + draining = true; + + var len = queue.length; + while(len) { + currentQueue = queue; + queue = []; + while (++queueIndex < len) { + if (currentQueue) { + currentQueue[queueIndex].run(); + } + } + queueIndex = -1; + len = queue.length; + } + currentQueue = null; + draining = false; + runClearTimeout(timeout); + } + + process.nextTick = function (fun) { + var args = new Array(arguments.length - 1); + if (arguments.length > 1) { + for (var i = 1; i < arguments.length; i++) { + args[i - 1] = arguments[i]; + } + } + queue.push(new Item(fun, args)); + if (queue.length === 1 && !draining) { + runTimeout(drainQueue); + } + }; + + // v8 likes predictible objects + function Item(fun, array) { + this.fun = fun; + this.array = array; + } + Item.prototype.run = function () { + this.fun.apply(null, this.array); + }; + process.title = 'browser'; + process.browser = true; + process.env = {}; + process.argv = []; + process.version = ''; // empty string to avoid regexp issues + process.versions = {}; + + function noop() {} + + process.on = noop; + process.addListener = noop; + process.once = noop; + process.off = noop; + process.removeListener = noop; + process.removeAllListeners = noop; + process.emit = noop; + process.prependListener = noop; + process.prependOnceListener = noop; + + process.listeners = function (name) { return [] }; + + process.binding = function (name) { + throw new Error('process.binding is not supported'); + }; + + process.cwd = function () { return '/' }; + process.chdir = function (dir) { + throw new Error('process.chdir is not supported'); + }; + process.umask = function() { return 0; }; + + + /***/ }), + + /***/ "./node_modules/semver/semver.js": + /*!***************************************!*\ + !*** ./node_modules/semver/semver.js ***! + \***************************************/ + /*! no static exports found */ + /***/ (function(module, exports, __webpack_require__) { + + /* WEBPACK VAR INJECTION */(function(process) {exports = module.exports = SemVer; + + // The debug function is excluded entirely from the minified version. + /* nomin */ var debug; + /* nomin */ if (typeof process === 'object' && + /* nomin */ process.env && + /* nomin */ process.env.NODE_DEBUG && + /* nomin */ /\bsemver\b/i.test(process.env.NODE_DEBUG)) + /* nomin */ debug = function() { + /* nomin */ var args = Array.prototype.slice.call(arguments, 0); + /* nomin */ args.unshift('SEMVER'); + /* nomin */ console.log.apply(console, args); + /* nomin */ }; + /* nomin */ else + /* nomin */ debug = function() {}; + + // Note: this is the semver.org version of the spec that it implements + // Not necessarily the package version of this code. + exports.SEMVER_SPEC_VERSION = '2.0.0'; + + var MAX_LENGTH = 256; + var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; + + // Max safe segment length for coercion. + var MAX_SAFE_COMPONENT_LENGTH = 16; + + // The actual regexps go on exports.re + var re = exports.re = []; + var src = exports.src = []; + var R = 0; + + // The following Regular Expressions can be used for tokenizing, + // validating, and parsing SemVer version strings. + + // ## Numeric Identifier + // A single `0`, or a non-zero digit followed by zero or more digits. + + var NUMERICIDENTIFIER = R++; + src[NUMERICIDENTIFIER] = '0|[1-9]\\d*'; + var NUMERICIDENTIFIERLOOSE = R++; + src[NUMERICIDENTIFIERLOOSE] = '[0-9]+'; + + + // ## Non-numeric Identifier + // Zero or more digits, followed by a letter or hyphen, and then zero or + // more letters, digits, or hyphens. + + var NONNUMERICIDENTIFIER = R++; + src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*'; + + + // ## Main Version + // Three dot-separated numeric identifiers. + + var MAINVERSION = R++; + src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')\\.' + + '(' + src[NUMERICIDENTIFIER] + ')'; + + var MAINVERSIONLOOSE = R++; + src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' + + '(' + src[NUMERICIDENTIFIERLOOSE] + ')'; + + // ## Pre-release Version Identifier + // A numeric identifier, or a non-numeric identifier. + + var PRERELEASEIDENTIFIER = R++; + src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] + + '|' + src[NONNUMERICIDENTIFIER] + ')'; + + var PRERELEASEIDENTIFIERLOOSE = R++; + src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] + + '|' + src[NONNUMERICIDENTIFIER] + ')'; + + + // ## Pre-release Version + // Hyphen, followed by one or more dot-separated pre-release version + // identifiers. + + var PRERELEASE = R++; + src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] + + '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))'; + + var PRERELEASELOOSE = R++; + src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] + + '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))'; + + // ## Build Metadata Identifier + // Any combination of digits, letters, or hyphens. + + var BUILDIDENTIFIER = R++; + src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+'; + + // ## Build Metadata + // Plus sign, followed by one or more period-separated build metadata + // identifiers. + + var BUILD = R++; + src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] + + '(?:\\.' + src[BUILDIDENTIFIER] + ')*))'; + + + // ## Full Version String + // A main version, followed optionally by a pre-release version and + // build metadata. + + // Note that the only major, minor, patch, and pre-release sections of + // the version string are capturing groups. The build metadata is not a + // capturing group, because it should not ever be used in version + // comparison. + + var FULL = R++; + var FULLPLAIN = 'v?' + src[MAINVERSION] + + src[PRERELEASE] + '?' + + src[BUILD] + '?'; + + src[FULL] = '^' + FULLPLAIN + '$'; + + // like full, but allows v1.2.3 and =1.2.3, which people do sometimes. + // also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty + // common in the npm registry. + var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] + + src[PRERELEASELOOSE] + '?' + + src[BUILD] + '?'; + + var LOOSE = R++; + src[LOOSE] = '^' + LOOSEPLAIN + '$'; + + var GTLT = R++; + src[GTLT] = '((?:<|>)?=?)'; + + // Something like "2.*" or "1.2.x". + // Note that "x.x" is a valid xRange identifer, meaning "any version" + // Only the first item is strictly required. + var XRANGEIDENTIFIERLOOSE = R++; + src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*'; + var XRANGEIDENTIFIER = R++; + src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*'; + + var XRANGEPLAIN = R++; + src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' + + '(?:' + src[PRERELEASE] + ')?' + + src[BUILD] + '?' + + ')?)?'; + + var XRANGEPLAINLOOSE = R++; + src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' + + '(?:' + src[PRERELEASELOOSE] + ')?' + + src[BUILD] + '?' + + ')?)?'; + + var XRANGE = R++; + src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$'; + var XRANGELOOSE = R++; + src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$'; + + // Coercion. + // Extract anything that could conceivably be a part of a valid semver + var COERCE = R++; + src[COERCE] = '(?:^|[^\\d])' + + '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' + + '(?:$|[^\\d])'; + + // Tilde ranges. + // Meaning is "reasonably at or greater than" + var LONETILDE = R++; + src[LONETILDE] = '(?:~>?)'; + + var TILDETRIM = R++; + src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+'; + re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g'); + var tildeTrimReplace = '$1~'; + + var TILDE = R++; + src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$'; + var TILDELOOSE = R++; + src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$'; + + // Caret ranges. + // Meaning is "at least and backwards compatible with" + var LONECARET = R++; + src[LONECARET] = '(?:\\^)'; + + var CARETTRIM = R++; + src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+'; + re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g'); + var caretTrimReplace = '$1^'; + + var CARET = R++; + src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$'; + var CARETLOOSE = R++; + src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$'; + + // A simple gt/lt/eq thing, or just "" to indicate "any version" + var COMPARATORLOOSE = R++; + src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$'; + var COMPARATOR = R++; + src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$'; + + + // An expression to strip any whitespace between the gtlt and the thing + // it modifies, so that `> 1.2.3` ==> `>1.2.3` + var COMPARATORTRIM = R++; + src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] + + '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')'; + + // this one has to use the /g flag + re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g'); + var comparatorTrimReplace = '$1$2$3'; + + + // Something like `1.2.3 - 1.2.4` + // Note that these all use the loose form, because they'll be + // checked against either the strict or loose comparator form + // later. + var HYPHENRANGE = R++; + src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAIN] + ')' + + '\\s*$'; + + var HYPHENRANGELOOSE = R++; + src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s+-\\s+' + + '(' + src[XRANGEPLAINLOOSE] + ')' + + '\\s*$'; + + // Star ranges basically just allow anything at all. + var STAR = R++; + src[STAR] = '(<|>)?=?\\s*\\*'; + + // Compile to actual regexp objects. + // All are flag-free, unless they were created above with a flag. + for (var i = 0; i < R; i++) { + debug(i, src[i]); + if (!re[i]) + re[i] = new RegExp(src[i]); + } + + exports.parse = parse; + function parse(version, loose) { + if (version instanceof SemVer) + return version; + + if (typeof version !== 'string') + return null; + + if (version.length > MAX_LENGTH) + return null; + + var r = loose ? re[LOOSE] : re[FULL]; + if (!r.test(version)) + return null; + + try { + return new SemVer(version, loose); + } catch (er) { + return null; + } + } + + exports.valid = valid; + function valid(version, loose) { + var v = parse(version, loose); + return v ? v.version : null; + } + + + exports.clean = clean; + function clean(version, loose) { + var s = parse(version.trim().replace(/^[=v]+/, ''), loose); + return s ? s.version : null; + } + + exports.SemVer = SemVer; + + function SemVer(version, loose) { + if (version instanceof SemVer) { + if (version.loose === loose) + return version; + else + version = version.version; + } else if (typeof version !== 'string') { + throw new TypeError('Invalid Version: ' + version); + } + + if (version.length > MAX_LENGTH) + throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters') + + if (!(this instanceof SemVer)) + return new SemVer(version, loose); + + debug('SemVer', version, loose); + this.loose = loose; + var m = version.trim().match(loose ? re[LOOSE] : re[FULL]); + + if (!m) + throw new TypeError('Invalid Version: ' + version); + + this.raw = version; + + // these are actually numbers + this.major = +m[1]; + this.minor = +m[2]; + this.patch = +m[3]; + + if (this.major > MAX_SAFE_INTEGER || this.major < 0) + throw new TypeError('Invalid major version') + + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) + throw new TypeError('Invalid minor version') + + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) + throw new TypeError('Invalid patch version') + + // numberify any prerelease numeric ids + if (!m[4]) + this.prerelease = []; + else + this.prerelease = m[4].split('.').map(function(id) { + if (/^[0-9]+$/.test(id)) { + var num = +id; + if (num >= 0 && num < MAX_SAFE_INTEGER) + return num; + } + return id; + }); + + this.build = m[5] ? m[5].split('.') : []; + this.format(); + } + + SemVer.prototype.format = function() { + this.version = this.major + '.' + this.minor + '.' + this.patch; + if (this.prerelease.length) + this.version += '-' + this.prerelease.join('.'); + return this.version; + }; + + SemVer.prototype.toString = function() { + return this.version; + }; + + SemVer.prototype.compare = function(other) { + debug('SemVer.compare', this.version, this.loose, other); + if (!(other instanceof SemVer)) + other = new SemVer(other, this.loose); + + return this.compareMain(other) || this.comparePre(other); + }; + + SemVer.prototype.compareMain = function(other) { + if (!(other instanceof SemVer)) + other = new SemVer(other, this.loose); + + return compareIdentifiers(this.major, other.major) || + compareIdentifiers(this.minor, other.minor) || + compareIdentifiers(this.patch, other.patch); + }; + + SemVer.prototype.comparePre = function(other) { + if (!(other instanceof SemVer)) + other = new SemVer(other, this.loose); + + // NOT having a prerelease is > having one + if (this.prerelease.length && !other.prerelease.length) + return -1; + else if (!this.prerelease.length && other.prerelease.length) + return 1; + else if (!this.prerelease.length && !other.prerelease.length) + return 0; + + var i = 0; + do { + var a = this.prerelease[i]; + var b = other.prerelease[i]; + debug('prerelease compare', i, a, b); + if (a === undefined && b === undefined) + return 0; + else if (b === undefined) + return 1; + else if (a === undefined) + return -1; + else if (a === b) + continue; + else + return compareIdentifiers(a, b); + } while (++i); + }; + + // preminor will bump the version up to the next minor release, and immediately + // down to pre-release. premajor and prepatch work the same way. + SemVer.prototype.inc = function(release, identifier) { + switch (release) { + case 'premajor': + this.prerelease.length = 0; + this.patch = 0; + this.minor = 0; + this.major++; + this.inc('pre', identifier); + break; + case 'preminor': + this.prerelease.length = 0; + this.patch = 0; + this.minor++; + this.inc('pre', identifier); + break; + case 'prepatch': + // If this is already a prerelease, it will bump to the next version + // drop any prereleases that might already exist, since they are not + // relevant at this point. + this.prerelease.length = 0; + this.inc('patch', identifier); + this.inc('pre', identifier); + break; + // If the input is a non-prerelease version, this acts the same as + // prepatch. + case 'prerelease': + if (this.prerelease.length === 0) + this.inc('patch', identifier); + this.inc('pre', identifier); + break; + + case 'major': + // If this is a pre-major version, bump up to the same major version. + // Otherwise increment major. + // 1.0.0-5 bumps to 1.0.0 + // 1.1.0 bumps to 2.0.0 + if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0) + this.major++; + this.minor = 0; + this.patch = 0; + this.prerelease = []; + break; + case 'minor': + // If this is a pre-minor version, bump up to the same minor version. + // Otherwise increment minor. + // 1.2.0-5 bumps to 1.2.0 + // 1.2.1 bumps to 1.3.0 + if (this.patch !== 0 || this.prerelease.length === 0) + this.minor++; + this.patch = 0; + this.prerelease = []; + break; + case 'patch': + // If this is not a pre-release version, it will increment the patch. + // If it is a pre-release it will bump up to the same patch version. + // 1.2.0-5 patches to 1.2.0 + // 1.2.0 patches to 1.2.1 + if (this.prerelease.length === 0) + this.patch++; + this.prerelease = []; + break; + // This probably shouldn't be used publicly. + // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction. + case 'pre': + if (this.prerelease.length === 0) + this.prerelease = [0]; + else { + var i = this.prerelease.length; + while (--i >= 0) { + if (typeof this.prerelease[i] === 'number') { + this.prerelease[i]++; + i = -2; + } + } + if (i === -1) // didn't increment anything + this.prerelease.push(0); + } + if (identifier) { + // 1.2.0-beta.1 bumps to 1.2.0-beta.2, + // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0 + if (this.prerelease[0] === identifier) { + if (isNaN(this.prerelease[1])) + this.prerelease = [identifier, 0]; + } else + this.prerelease = [identifier, 0]; + } + break; + + default: + throw new Error('invalid increment argument: ' + release); + } + this.format(); + this.raw = this.version; + return this; + }; + + exports.inc = inc; + function inc(version, release, loose, identifier) { + if (typeof(loose) === 'string') { + identifier = loose; + loose = undefined; + } + + try { + return new SemVer(version, loose).inc(release, identifier).version; + } catch (er) { + return null; + } + } + + exports.diff = diff; + function diff(version1, version2) { + if (eq(version1, version2)) { + return null; + } else { + var v1 = parse(version1); + var v2 = parse(version2); + if (v1.prerelease.length || v2.prerelease.length) { + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return 'pre'+key; + } + } + } + return 'prerelease'; + } + for (var key in v1) { + if (key === 'major' || key === 'minor' || key === 'patch') { + if (v1[key] !== v2[key]) { + return key; + } + } + } + } + } + + exports.compareIdentifiers = compareIdentifiers; + + var numeric = /^[0-9]+$/; + function compareIdentifiers(a, b) { + var anum = numeric.test(a); + var bnum = numeric.test(b); + + if (anum && bnum) { + a = +a; + b = +b; + } + + return (anum && !bnum) ? -1 : + (bnum && !anum) ? 1 : + a < b ? -1 : + a > b ? 1 : + 0; + } + + exports.rcompareIdentifiers = rcompareIdentifiers; + function rcompareIdentifiers(a, b) { + return compareIdentifiers(b, a); + } + + exports.major = major; + function major(a, loose) { + return new SemVer(a, loose).major; + } + + exports.minor = minor; + function minor(a, loose) { + return new SemVer(a, loose).minor; + } + + exports.patch = patch; + function patch(a, loose) { + return new SemVer(a, loose).patch; + } + + exports.compare = compare; + function compare(a, b, loose) { + return new SemVer(a, loose).compare(new SemVer(b, loose)); + } + + exports.compareLoose = compareLoose; + function compareLoose(a, b) { + return compare(a, b, true); + } + + exports.rcompare = rcompare; + function rcompare(a, b, loose) { + return compare(b, a, loose); + } + + exports.sort = sort; + function sort(list, loose) { + return list.sort(function(a, b) { + return exports.compare(a, b, loose); + }); + } + + exports.rsort = rsort; + function rsort(list, loose) { + return list.sort(function(a, b) { + return exports.rcompare(a, b, loose); + }); + } + + exports.gt = gt; + function gt(a, b, loose) { + return compare(a, b, loose) > 0; + } + + exports.lt = lt; + function lt(a, b, loose) { + return compare(a, b, loose) < 0; + } + + exports.eq = eq; + function eq(a, b, loose) { + return compare(a, b, loose) === 0; + } + + exports.neq = neq; + function neq(a, b, loose) { + return compare(a, b, loose) !== 0; + } + + exports.gte = gte; + function gte(a, b, loose) { + return compare(a, b, loose) >= 0; + } + + exports.lte = lte; + function lte(a, b, loose) { + return compare(a, b, loose) <= 0; + } + + exports.cmp = cmp; + function cmp(a, op, b, loose) { + var ret; + switch (op) { + case '===': + if (typeof a === 'object') a = a.version; + if (typeof b === 'object') b = b.version; + ret = a === b; + break; + case '!==': + if (typeof a === 'object') a = a.version; + if (typeof b === 'object') b = b.version; + ret = a !== b; + break; + case '': case '=': case '==': ret = eq(a, b, loose); break; + case '!=': ret = neq(a, b, loose); break; + case '>': ret = gt(a, b, loose); break; + case '>=': ret = gte(a, b, loose); break; + case '<': ret = lt(a, b, loose); break; + case '<=': ret = lte(a, b, loose); break; + default: throw new TypeError('Invalid operator: ' + op); + } + return ret; + } + + exports.Comparator = Comparator; + function Comparator(comp, loose) { + if (comp instanceof Comparator) { + if (comp.loose === loose) + return comp; + else + comp = comp.value; + } + + if (!(this instanceof Comparator)) + return new Comparator(comp, loose); + + debug('comparator', comp, loose); + this.loose = loose; + this.parse(comp); + + if (this.semver === ANY) + this.value = ''; + else + this.value = this.operator + this.semver.version; + + debug('comp', this); + } + + var ANY = {}; + Comparator.prototype.parse = function(comp) { + var r = this.loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; + var m = comp.match(r); + + if (!m) + throw new TypeError('Invalid comparator: ' + comp); + + this.operator = m[1]; + if (this.operator === '=') + this.operator = ''; + + // if it literally is just '>' or '' then allow anything. + if (!m[2]) + this.semver = ANY; + else + this.semver = new SemVer(m[2], this.loose); + }; + + Comparator.prototype.toString = function() { + return this.value; + }; + + Comparator.prototype.test = function(version) { + debug('Comparator.test', version, this.loose); + + if (this.semver === ANY) + return true; + + if (typeof version === 'string') + version = new SemVer(version, this.loose); + + return cmp(version, this.operator, this.semver, this.loose); + }; + + Comparator.prototype.intersects = function(comp, loose) { + if (!(comp instanceof Comparator)) { + throw new TypeError('a Comparator is required'); + } + + var rangeTmp; + + if (this.operator === '') { + rangeTmp = new Range(comp.value, loose); + return satisfies(this.value, rangeTmp, loose); + } else if (comp.operator === '') { + rangeTmp = new Range(this.value, loose); + return satisfies(comp.semver, rangeTmp, loose); + } + + var sameDirectionIncreasing = + (this.operator === '>=' || this.operator === '>') && + (comp.operator === '>=' || comp.operator === '>'); + var sameDirectionDecreasing = + (this.operator === '<=' || this.operator === '<') && + (comp.operator === '<=' || comp.operator === '<'); + var sameSemVer = this.semver.version === comp.semver.version; + var differentDirectionsInclusive = + (this.operator === '>=' || this.operator === '<=') && + (comp.operator === '>=' || comp.operator === '<='); + var oppositeDirectionsLessThan = + cmp(this.semver, '<', comp.semver, loose) && + ((this.operator === '>=' || this.operator === '>') && + (comp.operator === '<=' || comp.operator === '<')); + var oppositeDirectionsGreaterThan = + cmp(this.semver, '>', comp.semver, loose) && + ((this.operator === '<=' || this.operator === '<') && + (comp.operator === '>=' || comp.operator === '>')); + + return sameDirectionIncreasing || sameDirectionDecreasing || + (sameSemVer && differentDirectionsInclusive) || + oppositeDirectionsLessThan || oppositeDirectionsGreaterThan; + }; + + + exports.Range = Range; + function Range(range, loose) { + if (range instanceof Range) { + if (range.loose === loose) { + return range; + } else { + return new Range(range.raw, loose); + } + } + + if (range instanceof Comparator) { + return new Range(range.value, loose); + } + + if (!(this instanceof Range)) + return new Range(range, loose); + + this.loose = loose; + + // First, split based on boolean or || + this.raw = range; + this.set = range.split(/\s*\|\|\s*/).map(function(range) { + return this.parseRange(range.trim()); + }, this).filter(function(c) { + // throw out any that are not relevant for whatever reason + return c.length; + }); + + if (!this.set.length) { + throw new TypeError('Invalid SemVer Range: ' + range); + } + + this.format(); + } + + Range.prototype.format = function() { + this.range = this.set.map(function(comps) { + return comps.join(' ').trim(); + }).join('||').trim(); + return this.range; + }; + + Range.prototype.toString = function() { + return this.range; + }; + + Range.prototype.parseRange = function(range) { + var loose = this.loose; + range = range.trim(); + debug('range', range, loose); + // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4` + var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE]; + range = range.replace(hr, hyphenReplace); + debug('hyphen replace', range); + // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5` + range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace); + debug('comparator trim', range, re[COMPARATORTRIM]); + + // `~ 1.2.3` => `~1.2.3` + range = range.replace(re[TILDETRIM], tildeTrimReplace); + + // `^ 1.2.3` => `^1.2.3` + range = range.replace(re[CARETTRIM], caretTrimReplace); + + // normalize spaces + range = range.split(/\s+/).join(' '); + + // At this point, the range is completely trimmed and + // ready to be split into comparators. + + var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR]; + var set = range.split(' ').map(function(comp) { + return parseComparator(comp, loose); + }).join(' ').split(/\s+/); + if (this.loose) { + // in loose mode, throw out any that are not valid comparators + set = set.filter(function(comp) { + return !!comp.match(compRe); + }); + } + set = set.map(function(comp) { + return new Comparator(comp, loose); + }); + + return set; + }; + + Range.prototype.intersects = function(range, loose) { + if (!(range instanceof Range)) { + throw new TypeError('a Range is required'); + } + + return this.set.some(function(thisComparators) { + return thisComparators.every(function(thisComparator) { + return range.set.some(function(rangeComparators) { + return rangeComparators.every(function(rangeComparator) { + return thisComparator.intersects(rangeComparator, loose); + }); + }); + }); + }); + }; + + // Mostly just for testing and legacy API reasons + exports.toComparators = toComparators; + function toComparators(range, loose) { + return new Range(range, loose).set.map(function(comp) { + return comp.map(function(c) { + return c.value; + }).join(' ').trim().split(' '); + }); + } + + // comprised of xranges, tildes, stars, and gtlt's at this point. + // already replaced the hyphen ranges + // turn into a set of JUST comparators. + function parseComparator(comp, loose) { + debug('comp', comp); + comp = replaceCarets(comp, loose); + debug('caret', comp); + comp = replaceTildes(comp, loose); + debug('tildes', comp); + comp = replaceXRanges(comp, loose); + debug('xrange', comp); + comp = replaceStars(comp, loose); + debug('stars', comp); + return comp; + } + + function isX(id) { + return !id || id.toLowerCase() === 'x' || id === '*'; + } + + // ~, ~> --> * (any, kinda silly) + // ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0 + // ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0 + // ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0 + // ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0 + // ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0 + function replaceTildes(comp, loose) { + return comp.trim().split(/\s+/).map(function(comp) { + return replaceTilde(comp, loose); + }).join(' '); + } + + function replaceTilde(comp, loose) { + var r = loose ? re[TILDELOOSE] : re[TILDE]; + return comp.replace(r, function(_, M, m, p, pr) { + debug('tilde', comp, _, M, m, p, pr); + var ret; + + if (isX(M)) + ret = ''; + else if (isX(m)) + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; + else if (isX(p)) + // ~1.2 == >=1.2.0 <1.3.0 + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; + else if (pr) { + debug('replaceTilde pr', pr); + if (pr.charAt(0) !== '-') + pr = '-' + pr; + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + M + '.' + (+m + 1) + '.0'; + } else + // ~1.2.3 == >=1.2.3 <1.3.0 + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0'; + + debug('tilde return', ret); + return ret; + }); + } + + // ^ --> * (any, kinda silly) + // ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0 + // ^2.0, ^2.0.x --> >=2.0.0 <3.0.0 + // ^1.2, ^1.2.x --> >=1.2.0 <2.0.0 + // ^1.2.3 --> >=1.2.3 <2.0.0 + // ^1.2.0 --> >=1.2.0 <2.0.0 + function replaceCarets(comp, loose) { + return comp.trim().split(/\s+/).map(function(comp) { + return replaceCaret(comp, loose); + }).join(' '); + } + + function replaceCaret(comp, loose) { + debug('caret', comp, loose); + var r = loose ? re[CARETLOOSE] : re[CARET]; + return comp.replace(r, function(_, M, m, p, pr) { + debug('caret', comp, _, M, m, p, pr); + var ret; + + if (isX(M)) + ret = ''; + else if (isX(m)) + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; + else if (isX(p)) { + if (M === '0') + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; + else + ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0'; + } else if (pr) { + debug('replaceCaret pr', pr); + if (pr.charAt(0) !== '-') + pr = '-' + pr; + if (M === '0') { + if (m === '0') + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + M + '.' + m + '.' + (+p + 1); + else + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + M + '.' + (+m + 1) + '.0'; + } else + ret = '>=' + M + '.' + m + '.' + p + pr + + ' <' + (+M + 1) + '.0.0'; + } else { + debug('no pr'); + if (M === '0') { + if (m === '0') + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + m + '.' + (+p + 1); + else + ret = '>=' + M + '.' + m + '.' + p + + ' <' + M + '.' + (+m + 1) + '.0'; + } else + ret = '>=' + M + '.' + m + '.' + p + + ' <' + (+M + 1) + '.0.0'; + } + + debug('caret return', ret); + return ret; + }); + } + + function replaceXRanges(comp, loose) { + debug('replaceXRanges', comp, loose); + return comp.split(/\s+/).map(function(comp) { + return replaceXRange(comp, loose); + }).join(' '); + } + + function replaceXRange(comp, loose) { + comp = comp.trim(); + var r = loose ? re[XRANGELOOSE] : re[XRANGE]; + return comp.replace(r, function(ret, gtlt, M, m, p, pr) { + debug('xRange', comp, ret, gtlt, M, m, p, pr); + var xM = isX(M); + var xm = xM || isX(m); + var xp = xm || isX(p); + var anyX = xp; + + if (gtlt === '=' && anyX) + gtlt = ''; + + if (xM) { + if (gtlt === '>' || gtlt === '<') { + // nothing is allowed + ret = '<0.0.0'; + } else { + // nothing is forbidden + ret = '*'; + } + } else if (gtlt && anyX) { + // replace X with 0 + if (xm) + m = 0; + if (xp) + p = 0; + + if (gtlt === '>') { + // >1 => >=2.0.0 + // >1.2 => >=1.3.0 + // >1.2.3 => >= 1.2.4 + gtlt = '>='; + if (xm) { + M = +M + 1; + m = 0; + p = 0; + } else if (xp) { + m = +m + 1; + p = 0; + } + } else if (gtlt === '<=') { + // <=0.7.x is actually <0.8.0, since any 0.7.x should + // pass. Similarly, <=7.x is actually <8.0.0, etc. + gtlt = '<'; + if (xm) + M = +M + 1; + else + m = +m + 1; + } + + ret = gtlt + M + '.' + m + '.' + p; + } else if (xm) { + ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'; + } else if (xp) { + ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'; + } + + debug('xRange return', ret); + + return ret; + }); + } + + // Because * is AND-ed with everything else in the comparator, + // and '' means "any version", just remove the *s entirely. + function replaceStars(comp, loose) { + debug('replaceStars', comp, loose); + // Looseness is ignored here. star is always as loose as it gets! + return comp.trim().replace(re[STAR], ''); + } + + // This function is passed to string.replace(re[HYPHENRANGE]) + // M, m, patch, prerelease, build + // 1.2 - 3.4.5 => >=1.2.0 <=3.4.5 + // 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do + // 1.2 - 3.4 => >=1.2.0 <3.5.0 + function hyphenReplace($0, + from, fM, fm, fp, fpr, fb, + to, tM, tm, tp, tpr, tb) { + + if (isX(fM)) + from = ''; + else if (isX(fm)) + from = '>=' + fM + '.0.0'; + else if (isX(fp)) + from = '>=' + fM + '.' + fm + '.0'; + else + from = '>=' + from; + + if (isX(tM)) + to = ''; + else if (isX(tm)) + to = '<' + (+tM + 1) + '.0.0'; + else if (isX(tp)) + to = '<' + tM + '.' + (+tm + 1) + '.0'; + else if (tpr) + to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr; + else + to = '<=' + to; + + return (from + ' ' + to).trim(); + } + + + // if ANY of the sets match ALL of its comparators, then pass + Range.prototype.test = function(version) { + if (!version) + return false; + + if (typeof version === 'string') + version = new SemVer(version, this.loose); + + for (var i = 0; i < this.set.length; i++) { + if (testSet(this.set[i], version)) + return true; + } + return false; + }; + + function testSet(set, version) { + for (var i = 0; i < set.length; i++) { + if (!set[i].test(version)) + return false; + } + + if (version.prerelease.length) { + // Find the set of versions that are allowed to have prereleases + // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0 + // That should allow `1.2.3-pr.2` to pass. + // However, `1.2.4-alpha.notready` should NOT be allowed, + // even though it's within the range set by the comparators. + for (var i = 0; i < set.length; i++) { + debug(set[i].semver); + if (set[i].semver === ANY) + continue; + + if (set[i].semver.prerelease.length > 0) { + var allowed = set[i].semver; + if (allowed.major === version.major && + allowed.minor === version.minor && + allowed.patch === version.patch) + return true; + } + } + + // Version has a -pre, but it's not one of the ones we like. + return false; + } + + return true; + } + + exports.satisfies = satisfies; + function satisfies(version, range, loose) { + try { + range = new Range(range, loose); + } catch (er) { + return false; + } + return range.test(version); + } + + exports.maxSatisfying = maxSatisfying; + function maxSatisfying(versions, range, loose) { + var max = null; + var maxSV = null; + try { + var rangeObj = new Range(range, loose); + } catch (er) { + return null; + } + versions.forEach(function (v) { + if (rangeObj.test(v)) { // satisfies(v, range, loose) + if (!max || maxSV.compare(v) === -1) { // compare(max, v, true) + max = v; + maxSV = new SemVer(max, loose); + } + } + }); + return max; + } + + exports.minSatisfying = minSatisfying; + function minSatisfying(versions, range, loose) { + var min = null; + var minSV = null; + try { + var rangeObj = new Range(range, loose); + } catch (er) { + return null; + } + versions.forEach(function (v) { + if (rangeObj.test(v)) { // satisfies(v, range, loose) + if (!min || minSV.compare(v) === 1) { // compare(min, v, true) + min = v; + minSV = new SemVer(min, loose); + } + } + }); + return min; + } + + exports.validRange = validRange; + function validRange(range, loose) { + try { + // Return '*' instead of '' so that truthiness works. + // This will throw if it's invalid anyway + return new Range(range, loose).range || '*'; + } catch (er) { + return null; + } + } + + // Determine if version is less than all the versions possible in the range + exports.ltr = ltr; + function ltr(version, range, loose) { + return outside(version, range, '<', loose); + } + + // Determine if version is greater than all the versions possible in the range. + exports.gtr = gtr; + function gtr(version, range, loose) { + return outside(version, range, '>', loose); + } + + exports.outside = outside; + function outside(version, range, hilo, loose) { + version = new SemVer(version, loose); + range = new Range(range, loose); + + var gtfn, ltefn, ltfn, comp, ecomp; + switch (hilo) { + case '>': + gtfn = gt; + ltefn = lte; + ltfn = lt; + comp = '>'; + ecomp = '>='; + break; + case '<': + gtfn = lt; + ltefn = gte; + ltfn = gt; + comp = '<'; + ecomp = '<='; + break; + default: + throw new TypeError('Must provide a hilo val of "<" or ">"'); + } + + // If it satisifes the range it is not outside + if (satisfies(version, range, loose)) { + return false; + } + + // From now on, variable terms are as if we're in "gtr" mode. + // but note that everything is flipped for the "ltr" function. + + for (var i = 0; i < range.set.length; ++i) { + var comparators = range.set[i]; + + var high = null; + var low = null; + + comparators.forEach(function(comparator) { + if (comparator.semver === ANY) { + comparator = new Comparator('>=0.0.0'); + } + high = high || comparator; + low = low || comparator; + if (gtfn(comparator.semver, high.semver, loose)) { + high = comparator; + } else if (ltfn(comparator.semver, low.semver, loose)) { + low = comparator; + } + }); + + // If the edge version comparator has a operator then our version + // isn't outside it + if (high.operator === comp || high.operator === ecomp) { + return false; + } + + // If the lowest version comparator has an operator and our version + // is less than it then it isn't higher than the range + if ((!low.operator || low.operator === comp) && + ltefn(version, low.semver)) { + return false; + } else if (low.operator === ecomp && ltfn(version, low.semver)) { + return false; + } + } + return true; + } + + exports.prerelease = prerelease; + function prerelease(version, loose) { + var parsed = parse(version, loose); + return (parsed && parsed.prerelease.length) ? parsed.prerelease : null; + } + + exports.intersects = intersects; + function intersects(r1, r2, loose) { + r1 = new Range(r1, loose); + r2 = new Range(r2, loose); + return r1.intersects(r2) + } + + exports.coerce = coerce; + function coerce(version) { + if (version instanceof SemVer) + return version; + + if (typeof version !== 'string') + return null; + + var match = version.match(re[COERCE]); + + if (match == null) + return null; + + return parse((match[1] || '0') + '.' + (match[2] || '0') + '.' + (match[3] || '0')); + } + + /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../process/browser.js */ "./node_modules/process/browser.js"))); + + /***/ }), + + /***/ "./node_modules/webpack/buildin/harmony-module.js": + /*!*******************************************!*\ + !*** (webpack)/buildin/harmony-module.js ***! + \*******************************************/ + /*! no static exports found */ + /***/ (function(module, exports) { + + module.exports = function(originalModule) { + if (!originalModule.webpackPolyfill) { + var module = Object.create(originalModule); + // module.parent = undefined by default + if (!module.children) module.children = []; + Object.defineProperty(module, "loaded", { + enumerable: true, + get: function() { + return module.l; + } + }); + Object.defineProperty(module, "id", { + enumerable: true, + get: function() { + return module.i; + } + }); + Object.defineProperty(module, "exports", { + enumerable: true + }); + module.webpackPolyfill = 1; + } + return module; + }; + + + /***/ }), + + /***/ "./src/rtc_reporting/reporting_agent.ts": + /*!**********************************************!*\ + !*** ./src/rtc_reporting/reporting_agent.ts ***! + \**********************************************/ + /*! exports provided: default */ + /***/ (function(module, __webpack_exports__, __webpack_require__) { + __webpack_require__.r(__webpack_exports__); + /* harmony import */ var _reporting_reporter__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./reporting_reporter */ "./src/rtc_reporting/reporting_reporter.ts"); + /* harmony import */ var _reporting_event__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./reporting_event */ "./src/rtc_reporting/reporting_event.ts"); + /* harmony import */ var _reporting_storebag__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./reporting_storebag */ "./src/rtc_reporting/reporting_storebag.ts"); + /* harmony import */ var _reporting_config__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! ./reporting_config */ "./src/rtc_reporting/reporting_config.ts"); + + + + const { RTCReportingStoreBag } = _reporting_storebag__WEBPACK_IMPORTED_MODULE_2__; + const ReportingVersion = '1'; + + class RTCObjectRef { + constructor(objectNotToDestroy = null) { + this._objectNotToDestroy = objectNotToDestroy; + this._objectToDestroy = null; + } + addInstantiatedObject(objectToDestroy) { + this._objectNotToDestroy = this._objectToDestroy = objectToDestroy; + } + get instance() { + return this._objectNotToDestroy; + } + destroy() { + if (this._objectToDestroy) { + this._objectToDestroy.destroy(); + } + this._objectNotToDestroy = this._objectNotToDestroy = null; + } + } + class RTCReportingAgent { + constructor(reportingConfig = {}) { + this._sessionInfo = {}; + this._reportingDisabled = false; + this.storebag = new RTCObjectRef(); + this.reporter = null; + this.reportingVersion = ReportingVersion; + this.fuzzFactor = 0; + this.sessionStartTime = 0; + this._initializeReportingAgent(reportingConfig); + } + destroy() { + this.endSession(); + } + get ReportingAllowed() { + return !(this._reportingDisabled); + } + static get version() { + return "0.1.36"; + } + issueReportingEvent(eventID, eventPayload, eventType = _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentEventTypes"].BatchEvent) { + let error = null; + if (this.ReportingAllowed && this.reporter) { + let event = new _reporting_event__WEBPACK_IMPORTED_MODULE_1__["RTCReportingEvent"](eventID, _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingSessionEventStatus"].EventStatusSessionBegin, eventPayload, this._FuzzFactor, eventType); + error = this.reporter.issueReportingEvent(event, this._sessionInfo); + } + else { + error = new _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingError"]('Reporting not allowed.'); + } + return error; + } + endSession() { + if (this.ReportingAllowed && this.reporter) { + this._issueSessionEndEvent(); + this.reporter.flushAllEvents(); + } + this._sessionInfo = null; + this.reporter = null; + this.storebag.destroy(); + this.storebag = null; + } + set SessionInfo(session_info) { + this._sessionInfo = session_info; + } + get SessionInfo() { + return this._sessionInfo; + } + get SessionID() { + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].SessionID in this._sessionInfo) { + return this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].SessionID]; + } + return ''; + } + get ClientName() { + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ClientName in this._sessionInfo) { + return this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ClientName]; + } + return ''; + } + get ServiceName() { + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ServiceName in this._sessionInfo) { + return this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ServiceName]; + } + return ''; + } + get _FuzzFactor() { + return this.fuzzFactor; + } + _initializeReportingAgent(reportingConfig) { + let clientName = null; + let serviceName = null; + let applicationName = ''; + let sender = null; + let deviceName = null; + let osVersion = null; + let storebagURL = null; + let reportingDisabled = false; + let startTime = (new Date()).getTime(); + let hlsEventEmitter = null; + let sessionID = null; + let userInfoDict = null; + let senderOSVersion = null; + let storeBag = null; + let senderServiceID = null; + let senderAltConfig = null; + let useHTTPHeaders = null; + if (!this._sessionInfo) { + this._sessionInfo = {}; + } + if (reportingConfig) { + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ClientName in reportingConfig) { + clientName = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ClientName]; + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ServiceName in reportingConfig) { + serviceName = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ServiceName]; + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ApplicationName in reportingConfig) { + applicationName = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ApplicationName]; + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].Sender in reportingConfig) { + sender = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].Sender]; + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].OSVersion in reportingConfig) { + osVersion = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].OSVersion]; + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].StorebagURL in reportingConfig) { + storebagURL = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].StorebagURL]; + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].DeviceName in reportingConfig) { + deviceName = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].DeviceName]; + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ReportingDisabled in reportingConfig) { + reportingDisabled = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ReportingDisabled]; + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].HLSEventEmitter in reportingConfig) { + hlsEventEmitter = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].HLSEventEmitter]; + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].SessionID in reportingConfig) { + sessionID = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].SessionID]; + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].UserInfoDict in reportingConfig) { + userInfoDict = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].UserInfoDict]; + if (userInfoDict && _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].SenderAltConfig in userInfoDict) { + senderAltConfig = userInfoDict[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].SenderAltConfig]; + delete userInfoDict[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].SenderAltConfig]; + if (senderAltConfig) { + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ServiceIdentifier in senderAltConfig) { + senderServiceID = senderAltConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ServiceIdentifier]; + } + if (Object.keys(senderAltConfig)) { + Object.assign(userInfoDict, senderAltConfig); + } + } + } + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].SenderOSVersion in reportingConfig) { + senderOSVersion = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].SenderOSVersion]; + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ReportingStoreBag in reportingConfig) { + storeBag = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ReportingStoreBag]; + } + if (_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].UseHTTPHeaders in reportingConfig) { + useHTTPHeaders = reportingConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].UseHTTPHeaders]; + } + } + if (!sessionID) { + sessionID = RTCReportingAgent._createSessionID(); + } + this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ClientName] = clientName; + this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ServiceName] = serviceName; + this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ApplicationName] = applicationName; + this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].DeviceName] = deviceName; + this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].OSVersion] = osVersion; + this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].Sender] = sender; + this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].RTCSchemeVersion] = this.reportingVersion; + this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].RTCReportingVersion] = RTCReportingAgent.version; + this._reportingDisabled = reportingDisabled; + this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].SessionID] = sessionID; + this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"].SenderOSVersion] = senderOSVersion; + if (applicationName && serviceName) { + serviceName = serviceName + '.' + applicationName; + } + if (senderServiceID && serviceName) { + serviceName = serviceName + '.' + senderServiceID; + } + this._sessionInfo[_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"].ServiceName] = serviceName; + this.fuzzFactor = startTime - Math.floor(startTime / 60000) * 60000; + this.sessionStartTime = startTime - this.fuzzFactor; + Object.keys(this._sessionInfo).forEach((key) => (this._sessionInfo[key] == null) && delete this._sessionInfo[key]); + if (userInfoDict && Object.keys(userInfoDict)) { + Object.assign(this._sessionInfo, userInfoDict); + } + if (this._reportingDisabled) { + return; + } + if (storeBag) { + this.storebag = new RTCObjectRef(storeBag); + } + else { + this.storebag.addInstantiatedObject(new RTCReportingStoreBag(storebagURL, clientName, serviceName, applicationName, deviceName, osVersion)); + } + this.reporter = new _reporting_reporter__WEBPACK_IMPORTED_MODULE_0__["RTCReporter"](clientName, serviceName, sender, this.storebag.instance, this.reportingVersion, hlsEventEmitter, applicationName, senderServiceID, useHTTPHeaders, sessionID); + console.log(_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentDefaultConfig"].LogMsgPrefix + 'Created reporting agent using rtc.js version ' + RTCReportingAgent.version); + this._issueSessionBeginEvent(); + } + _issueSessionBeginEvent() { + let event = new _reporting_event__WEBPACK_IMPORTED_MODULE_1__["RTCReportingEvent"](_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentMethods"].ReportingSessionMethod, _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingSessionEventStatus"].EventStatusSessionBegin, null, this._FuzzFactor, _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentEventTypes"].BatchEvent); + if (this.reporter && event) { + this.reporter.issueReportingEvent(event, this.SessionInfo); + } + } + _issueSessionEndEvent() { + let event = new _reporting_event__WEBPACK_IMPORTED_MODULE_1__["RTCReportingEvent"](_reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentMethods"].ReportingSessionMethod, _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingSessionEventStatus"].EventStatusSessionEnd, null, this._FuzzFactor, _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentEventTypes"].RealTimeEvent); + if (this.reporter) { + this.reporter.issueReportingEvent(event, this.SessionInfo); + } + } + static _createSessionID() { + let word0 = Math.random() * 0xffffffff | 0; + let word1 = Math.random() * 0xffffffff | 0; + let word2 = Math.random() * 0xffffffff | 0; + let word3 = Math.random() * 0xffffffff | 0; + const lastNibble = 0x0f; + const lastByte = 0xff; + const secondByteShift = 8; + const thirdByteShift = 16; + const fourthByteShift = 24; + const uuidVersion = 0x40; + const uuidVariant = 0x80; + const nByteMask = 0x3f; + let guid = []; + let radix = 16; + let guidLength = 256; + for (let i = 0; i < guidLength; ++i) { + guid[i] = (i < radix ? '0' : '') + (i).toString(radix); + } + return guid[word0 & lastByte] + guid[word0 >> secondByteShift & lastByte] + guid[word0 >> thirdByteShift & lastByte] + guid[word0 >> fourthByteShift & lastByte] + '-' + + guid[word1 & lastByte] + guid[word1 >> secondByteShift & lastByte] + '-' + + guid[word1 >> secondByteShift & lastNibble | uuidVersion] + guid[word1 >> thirdByteShift & lastByte] + '-' + + guid[word2 & nByteMask | uuidVariant] + guid[word2 >> secondByteShift & lastByte] + '-' + + guid[word2 >> secondByteShift & lastByte] + guid[word2 >> thirdByteShift & lastByte] + + guid[word3 & lastByte] + guid[word3 >> secondByteShift & lastByte] + guid[word3 >> thirdByteShift & lastByte] + guid[word3 >> fourthByteShift & lastByte]; + } + } + /* harmony default export */ __webpack_exports__["default"] = ({ + RTCReportingAgent, + RTCStorebag: _reporting_storebag__WEBPACK_IMPORTED_MODULE_2__, + RTCReportingAgentDefaultConfig: _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentDefaultConfig"], + RTCReportingAgentConfigKeys: _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentConfigKeys"], + RTCReportingAgentEventTypes: _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingAgentEventTypes"], + RTCReportingHTTPHeaderDefault: _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingHTTPHeaderDefault"], + RTCReportingHTTPHeaderKeys: _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingHTTPHeaderKeys"], + RTCReportingEventMetadataKeys: _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingEventMetadataKeys"], + RTC_REPORTING_EVENTS_DISPATCHED: _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTC_REPORTING_EVENTS_DISPATCHED"], + RTCObjectRef, + RTCReportingStoreBagConfig: _reporting_config__WEBPACK_IMPORTED_MODULE_3__["RTCReportingStoreBagConfig"], + }); + + + /***/ }), + + /***/ "./src/rtc_reporting/reporting_config.ts": + /*!***********************************************!*\ + !*** ./src/rtc_reporting/reporting_config.ts ***! + \***********************************************/ + /*! exports provided: RTCReportingAgentDefaultConfig, RTCReportingAgentConfigKeys, RTCReportingAgentEventTypes, RTCReportingHTTPHeaderKeys, RTCReportingHTTPHeaderDefault, RTCReportingEventMetadataKeys, RTCReportingStoreBagConfig, RTCReportingReporterConfig, RTCReportingAgentMethods, RTCReportingSessionEventStatus, RTCReportingStoreBagKeys, RTCReportingError, RTCReportingBuildTypes, RTC_REPORTING_EVENTS_DISPATCHED */ + /***/ (function(module, __webpack_exports__, __webpack_require__) { + __webpack_require__.r(__webpack_exports__); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingAgentDefaultConfig", function() { return RTCReportingAgentDefaultConfig; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingAgentConfigKeys", function() { return RTCReportingAgentConfigKeys; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingAgentEventTypes", function() { return RTCReportingAgentEventTypes; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingHTTPHeaderKeys", function() { return RTCReportingHTTPHeaderKeys; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingHTTPHeaderDefault", function() { return RTCReportingHTTPHeaderDefault; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingEventMetadataKeys", function() { return RTCReportingEventMetadataKeys; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingStoreBagConfig", function() { return RTCReportingStoreBagConfig; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingReporterConfig", function() { return RTCReportingReporterConfig; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingAgentMethods", function() { return RTCReportingAgentMethods; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingSessionEventStatus", function() { return RTCReportingSessionEventStatus; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingStoreBagKeys", function() { return RTCReportingStoreBagKeys; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingError", function() { return RTCReportingError; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingBuildTypes", function() { return RTCReportingBuildTypes; }); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTC_REPORTING_EVENTS_DISPATCHED", function() { return RTC_REPORTING_EVENTS_DISPATCHED; }); + const RTCReportingHTTPHeaderKeys = { + ContentType: 'ContentType', + InternalBuild: 'X-RTC-Internal-Build', + ClientName: 'X-RTC-Client-Name', + ServiceName: 'X-RTC-Service-Name', + ClientVersion: 'X-RTC-Client-Version', + Sender: 'X-RTC-Sender', + UserAgent: 'User-Agent', + SessionID: 'X-RTC-Session-ID', + }; + const RTCReportingHTTPHeaderDefault = { + ContentTypekey: 'application/json;charset=UTF-8', + }; + const RTCReportingEventMetadataKeys = { + EventID: '_method', + EventType: '_eventType', + EventNumber: '_eventNumber', + EventTime: 'eventTime', + Sender: '_sender', + Status: '_status', + ClientTS: '_clientTS', + TimeZoneOffset: '_timezoneOffset', + PostTime: 'postTime', + Events: 'events', + SessionID: '_sessionID', + OSVersion: 'osVersion', + RTCReportingVersion: '_reportVers', + StoreBagVersion: 'StorebagVersion', + RTCSchemeVersion: '_reportScheme', + BagSchemeVersion: 'StorebagScheme', + StoreBagName: 'StorebagName', + SessionTag: 'SessionTag', + SenderOSVersion: 'SenderOSVersion', + }; + const RTCReportingAgentDefaultConfig = { + RunInlineIfNoWebWorker: true, + BatchEvent: true, + LogMsgPrefix: '[RTC_RA]: ', + }; + const RTCReportingAgentConfigKeys = { + ClientName: '_clientName', + ServiceName: '_serviceName', + StorebagURL: 'storebagURL', + ApplicationName: 'applicationName', + Sender: 'sender', + DeviceName: 'deviceName', + OSVersion: 'osVersion', + ReportingDisabled: 'reportingDisabled', + HLSEventEmitter: 'hlsEventEmitter', + BuildType: 'internal', + UserInfoDict: 'userInfoDict', + ReportingStoreBag: 'reportingStoreBag', + SenderAltConfig: 'senderAltConfig', + ServiceIdentifier: 'ServiceIdentifier', + UseHTTPHeaders: 'UseHTTPHeaders', + }; + const RTCReportingAgentEventTypes = { + BatchEvent: 0, + RealTimeEvent: 1, + }; + const RTCReportingAgentMethods = { + ReportingSessionMethod: 0, + }; + const RTCReportingStoreBagConfig = { + StoreBagFetchSyncMode: 0, + StoreBagFetchAsyncMode: 1, + StoreBagVersionSupported: 1, + }; + const RTCReportingReporterConfig = { + BatchThreshold: 50, + }; + const RTCReportingSessionEventStatus = { + EventStatusSessionBegin: 0, + EventStatusSessionEnd: 1, + EventStatusSessionEndWithError: 2, + }; + const RTCReportingStoreBagKeys = { + Version: 'Version', + Scheme: 'Scheme', + BagVersion: 'BagVersion', + Configurations: 'Configurations', + ConfigurationList: 'ConfigurationList', + PostURLs: 'PostURLs', + Filters: 'Filters', + BagList: 'BagList', + MinOSVersionDict: 'MinOSVersionDict', + ClientName: 'ClientName', + ServiceName: 'ServiceName', + ServiceNameList: 'ServiceNameList', + SenderServiceIDList: 'SenderServiceIDList', + ApplicationNameList: 'ApplicationNameList', + DeviceList: 'DeviceList', + Remove: 'Remove', + EventThreshold: 'EventThreshold', + SamplingThreshold: 'SamplingThreshold', + DeviceWhiteList: 'DeviceWhiteList', + FlushEventsInterval: 'FlushEventsInterval', + }; + const RTCReportingBuildTypes = { + BuildTypePublic: 0, + BuildTypeInternal: 1, + BuildTypeSeed: 2, + BuildTypeInternalSeed: 3 + }; + class RTCReportingError extends Error { + constructor(...args) { + super(...args); + Error.captureStackTrace(this, RTCReportingError); + } + } + const RTC_REPORTING_EVENTS_DISPATCHED = 'rtcReportingEventsDispatched'; + + + + /***/ }), + + /***/ "./src/rtc_reporting/reporting_event.ts": + /*!**********************************************!*\ + !*** ./src/rtc_reporting/reporting_event.ts ***! + \**********************************************/ + /*! exports provided: RTCReportingEvent */ + /***/ (function(module, __webpack_exports__, __webpack_require__) { + __webpack_require__.r(__webpack_exports__); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingEvent", function() { return RTCReportingEvent; }); + /* harmony import */ var _reporting_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./reporting_config */ "./src/rtc_reporting/reporting_config.ts"); + + class RTCReportingEvent { + constructor(eventID, status = 0, eventPayload = {}, fuzzFactor = 0, eventType = _reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingAgentEventTypes"].BatchEvent) { + this.payload = eventPayload ? eventPayload : {}; + this.payload[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].EventID] = eventID; + this.payload[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].Status] = status; + this.payload[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].EventType] = eventType; + this.fuzzFactor = fuzzFactor; + this.isEventRealTime = (eventType === _reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingAgentEventTypes"].RealTimeEvent); + this._addEventMetadata(); + } + prepareEventPayload(sessionInfo, sizeTillLastBatch, sizeOfLastBatch) { + Object.keys(sessionInfo).forEach(function (sessionInfoKey) { + this.payload[sessionInfoKey] = sessionInfo[sessionInfoKey]; + }.bind(this)); + if (this.isEventRealTime && sizeOfLastBatch && sizeTillLastBatch) { + this.payload.sizeOfLastBatch = sizeOfLastBatch; + this.payload.sizeTillLastBatch = sizeTillLastBatch; + } + } + get EventPayload() { + return this.payload; + } + get IsEventRealTime() { + return this.isEventRealTime; + } + _addEventMetadata() { + let date = new Date(); + this.payload[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].EventTime] = (date.getTime()) / 1000; + this.payload[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].TimeZoneOffset] = date.getTimezoneOffset() * 60; + } + } + + + + /***/ }), + + /***/ "./src/rtc_reporting/reporting_reporter.ts": + /*!*************************************************!*\ + !*** ./src/rtc_reporting/reporting_reporter.ts ***! + \*************************************************/ + /*! exports provided: RTCReporter */ + /***/ (function(module, __webpack_exports__, __webpack_require__) { + __webpack_require__.r(__webpack_exports__); + /* WEBPACK VAR INJECTION */(function(module) {/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReporter", function() { return RTCReporter; }); + /* harmony import */ var _reporting_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./reporting_config */ "./src/rtc_reporting/reporting_config.ts"); + + const ReportingContentType = 'application/json;charset=UTF-8'; + const ReportingVersion = 'RTCReportingJS/0.314'; + class EventDispatcher { + constructor(eventEmitter, sessionID, useHTTPHeaders = false) { + this._eventWriter = null; + this._sessionID = sessionID; + this._eventDispatcher = eventEmitter; + if (typeof navigator !== 'undefined' && navigator && (typeof navigator.sendBeacon !== 'undefined') && navigator.sendBeacon) { + this._eventWriter = this._sendEventDataWithHTTPPost; + } + else if ( this.module !== module) { + this._eventWriter = this._sendEventDataWithHTTPHeaders; + } + else { + this._eventWriter = this._sendEventDataWithHTTPPost; + } + if (useHTTPHeaders) { + this._eventWriter = this._sendEventDataWithHTTPHeaders; + } + } + sendEventData(url, eventDataAsJSON, clientName, serviceName, clientVersion, sender) { + if (this._eventWriter) { + this._eventWriter(url, eventDataAsJSON, clientName, serviceName, clientVersion, sender); + if (this._eventDispatcher) { + this._eventDispatcher.emit(_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTC_REPORTING_EVENTS_DISPATCHED"], eventDataAsJSON); + } + } + } + _sendEventDataUsingBeacon(url, eventDataAsJSON, clientName, serviceName, clientVersion, sender) { + let headerJSON = this._createEventPayloadAsJSON(clientName, serviceName, clientVersion, sender); + let formData = new FormData(); + formData.append('headers', headerJSON); + formData.append('payload', eventDataAsJSON); + navigator.sendBeacon(url, formData); + } + _sendEventDataWithHTTPPost(url, eventDataAsJSON, clientName, serviceName, clientVersion, sender) { + let headerJSON = this._createEventPayloadAsJSON(clientName, serviceName, clientVersion, sender); + let formData = new FormData(); + formData.append('headers', headerJSON); + formData.append('payload', eventDataAsJSON); + let request = new XMLHttpRequest(); + const method = 'POST'; + const async = true; + request.open(method, url, async); + request.send(formData); + } + _createEventPayloadAsJSON(clientName, serviceName, clientVersion, sender) { + let header = {}; + header[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].InternalBuild] = 0; + header[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].ClientName] = clientName; + header[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].ServiceName] = serviceName; + header[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].ClientVersion] = clientVersion; + header[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].Sender] = sender; + header[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].ContentType] = 'application/json;charset=UTF-8'; + let headerJSON = JSON.stringify(header); + return headerJSON; + } + _sendEventDataWithHTTPHeaders(url, eventDataAsJSON, clientName, serviceName, clientVersion, sender) { + let request = new XMLHttpRequest(); + const method = 'POST'; + const async = true; + request.onload = function () { + }; + request.open(method, url, async); + request.setRequestHeader(_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].ContentType, ReportingContentType); + request.setRequestHeader(_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].InternalBuild, '0'); + request.setRequestHeader(_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].ClientName, clientName); + request.setRequestHeader(_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].ServiceName, serviceName); + request.setRequestHeader(_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].ClientVersion, clientVersion); + request.setRequestHeader(_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].Sender, sender); + request.setRequestHeader(_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].ClientVersion, ReportingVersion); + request.setRequestHeader(_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingHTTPHeaderKeys"].SessionID, this._sessionID); + request.send(eventDataAsJSON); + } + } + class RTCReporter { + constructor(clientName, serviceName, sender, reportingStoreBag, clientVersion, hlsEventEmitter, applicationName = null, senderServiceID = null, useHTTPHeaders = false, sessionID = null) { + this.clientName = clientName; + this.serviceName = serviceName; + this.reportingStoreBag = reportingStoreBag; + this.sender = sender; + this.clientVersion = clientVersion; + this.eventList = []; + this._count = 0; + this._eventDispatcher = new EventDispatcher(hlsEventEmitter, sessionID, useHTTPHeaders); + this.sampleFactor = Math.random(); + this._storebagStats = {}; + this._canReport = undefined; + this._sizeTillLastBatch = this._sizeOfLastBatch = 0; + this.storeBag = null; + this.applicationName = applicationName; + this.senderServiceID = senderServiceID; + this.checkAndConfigureEventFlushTimeoutOnce = false; + } + issueReportingEvent(event, sessionInfo = {}) { + if (!event) { + return new _reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingError"]('not a valid event to report'); + } + this._updateEventMetadata(event); + event.prepareEventPayload(sessionInfo, this._sizeTillLastBatch, this._sizeOfLastBatch); + this.eventList.push(event); + if (!this.checkAndConfigureEventFlushTimeoutOnce) { + if (this.canReportEvents()) { + this._configureEventFlushTimer(); + } + } + if ((this.eventList.length > _reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingReporterConfig"].BatchThreshold) || event.IsEventRealTime) { + return this.flushAllEvents(); + } + return null; + } + canReportEvents() { + let sampleRate; + if (this._canReport !== undefined) { + return this._canReport; + } + if (this.reportingStoreBag && !this.storeBag) { + this.storeBag = this.reportingStoreBag.createParsedStoreBag(this.applicationName, this.senderServiceID); + if (!this.storeBag) { + return false; + } + } + if (this.storeBag && this.storeBag.IsValid) { + this._canReport = true; + sampleRate = this.storeBag.getStoreBagProperty(_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].SamplingThreshold); + if (sampleRate && (parseFloat(sampleRate) < this.sampleFactor)) { + this._canReport = false; + } + } + else if (this.storeBag) { + console.log(`StoreBag for client ${this.clientName}, service ${this.serviceName}, app ${this.applicationName} senderService ${this.senderServiceID} not valid ${this.storeBag.IsValid} `); + } + if (this._canReport) { + this._storebagStats[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].SamplingThreshold] = sampleRate; + this._storebagStats[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].BagSchemeVersion] = this.storeBag.StoreBagSchemeVersion; + this._storebagStats[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].StoreBagVersion] = this.storeBag.StoreBagVersion; + this._storebagStats[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].StoreBagName] = this.storeBag.StoreBagName; + let bagMinVersionDict = this.storeBag.BagMinVersionDict; + if (typeof bagMinVersionDict === 'object' && bagMinVersionDict) { + for (let key in bagMinVersionDict) { + if (bagMinVersionDict.hasOwnProperty(key)) { + this._storebagStats[key] = bagMinVersionDict[key]; + } + } + } + } + console.log(`${_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingAgentDefaultConfig"].LogMsgPrefix} Event reporting is ${this._canReport} for client ${this.clientName} service ${this.serviceName}, app ${this.applicationName}, senderService ${this.senderServiceID}`); + return this._canReport; + } + flushEventTimeout() { + this.flushAllEvents(); + } + flushAllEvents() { + let error = null; + if (this.canReportEvents()) { + this._sendAllEvents(); + } + else { + error = new _reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingError"]('no valid storebag configured or blocked by sampling threshold, ignoring events.'); + } + this.eventList = []; + return error; + } + batchedEventsCount() { + return this.eventList.length; + } + get _EventList() { + return this.eventList; + } + _updateEventMetadata(event) { + event.EventPayload[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].EventNumber] = this._eventCounter(); + event.EventPayload[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].ClientTS] = ((new Date()).getTime()) / 1000; + event.EventPayload[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].Sender] = this.sender; + event.EventPayload[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingAgentConfigKeys"].ClientName] = this.clientName; + event.EventPayload[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingAgentConfigKeys"].ServiceName] = this.serviceName; + } + _sendAllEvents() { + if (this.eventList.length === 0) { + return; + } + let backEnds = this.storeBag.listOfSupportedBackEnds(this.clientName, this.serviceName); + if (!backEnds || backEnds.length <= 0) { + return; + } + let blackListKeys = this.storeBag.listOfKeysToBlock(); + if (blackListKeys) { + this.eventList.forEach(function (event) { + event.prepareEventPayload(this._storebagStats, this._sizeTillLastBatch, this._sizeOfLastBatch); + blackListKeys.forEach(function (key) { + delete event.EventPayload[key]; + }); + }.bind(this)); + } + let eventData = this._marshallAsJSONString(); + this._sizeTillLastBatch += eventData.length; + this._sizeOfLastBatch = eventData.length; + backEnds.forEach(function (backEndURL) { + this._eventDispatcher.sendEventData(backEndURL, eventData, this.clientName, this.serviceName, this.clientVersion, this.sender); + }.bind(this)); + console.log(`${_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingAgentDefaultConfig"].LogMsgPrefix} events reported.`); + } + _marshallAsJSONString() { + let time = ((new Date()).getTime()) / 1000; + let postData = {}; + let events = []; + this.eventList.forEach(function (event) { + events.push(event.EventPayload); + }); + postData[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].PostTime] = time; + postData[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingEventMetadataKeys"].Events] = events; + let postBody = JSON.stringify(postData); + return postBody; + } + _eventCounter() { + return this._count++; + } + _configureEventFlushTimer() { + let flushTimeout = null; + this.checkAndConfigureEventFlushTimeoutOnce = true; + if (this.reportingStoreBag && !this.storeBag) { + this.storeBag = this.reportingStoreBag.createParsedStoreBag(this.applicationName, this.senderServiceID); + } + if (this.storeBag && this.storeBag.IsValid) { + flushTimeout = this.storeBag.getStoreBagProperty(_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].FlushEventsInterval); + } + if (flushTimeout) { + flushTimeout = parseInt(flushTimeout, 10) * 1000; + if (flushTimeout >= 10000) { + setInterval(this.flushEventTimeout.bind(this), flushTimeout); + } + } + } + } + + + /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../node_modules/webpack/buildin/harmony-module.js */ "./node_modules/webpack/buildin/harmony-module.js")(module))); + + /***/ }), + + /***/ "./src/rtc_reporting/reporting_storebag.ts": + /*!*************************************************!*\ + !*** ./src/rtc_reporting/reporting_storebag.ts ***! + \*************************************************/ + /*! exports provided: RTCReportingStoreBag */ + /***/ (function(module, __webpack_exports__, __webpack_require__) { + __webpack_require__.r(__webpack_exports__); + /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "RTCReportingStoreBag", function() { return RTCReportingStoreBag; }); + /* harmony import */ var _reporting_config__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./reporting_config */ "./src/rtc_reporting/reporting_config.ts"); + /* harmony import */ var semver__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! semver */ "./node_modules/semver/semver.js"); + + + class RTCReportingParsedStoreBag { + constructor(storeBagConfig, selectedBagName, bagVersion, bagMinVersionDict) { + this.storeBagConfig = storeBagConfig; + this.selectedBagName = selectedBagName; + this.bagVersion = bagVersion; + this.bagMinVersionDict = bagMinVersionDict; + } + listOfSupportedBackEnds() { + return this.getStoreBagProperty(_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].PostURLs); + } + listOfKeysToBlock() { + if (!this.storeBagConfig) { + return null; + } + let filters = this.storeBagConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].Filters]; + if (!filters) { + return null; + } + return filters[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].Remove]; + } + get IsValid() { + return Boolean(this.storeBagConfig); + } + get StoreBagName() { + return this.IsValid ? this.selectedBagName : null; + } + get StorebagVersion() { + return this.IsValid ? this.bagVersion : null; + } + get StoreBagSchemeVersion() { + return _reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagConfig"].StoreBagVersionSupported; + } + get BagMinVersionDict() { + return this.bagMinVersionDict; + } + getStoreBagProperty(propertyKey) { + let propertyValue = null; + if (this.IsValid && propertyKey && propertyKey in this.storeBagConfig) { + propertyValue = this.storeBagConfig[propertyKey]; + } + return propertyValue; + } + } + class RTCReportingStoreBag { + constructor(storebagURL, clientName = null, serviceName = null, applicationName = null, deviceName = null, bagMinVersionDict = null, mode = _reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagConfig"].StoreBagFetchAsyncMode, storebagAsJSON = null) { + this.storebagURL = storebagURL; + this.mode = mode; + this.storeBagConfig = null; + this.clientName = clientName; + this.serviceName = serviceName; + this.applicationName = applicationName; + this.deviceName = deviceName; + this.storebagLoader = null; + this.selectedBagName = null; + this.storebagAsJSON = storebagAsJSON; + this.bagVersion = null; + this.bagMinVersionDict = null; + if (typeof bagMinVersionDict === 'object') { + this.bagMinVersionDict = bagMinVersionDict; + } + this.fetchAndParseStoreBag(); + } + destroy() { + if (this.storebagLoader) { + this.storebagLoader.abort(); + this.storebagLoader = null; + } + } + fetchAndParseStoreBag() { + this._fetchStoreBag(); + } + createParsedStoreBag(applicationName = this.applicationName, senderServiceID = null) { + if (!this.storebagAsJSON) { + console.log('no bag available!'); + return null; + } + return this._parseAndReturnStoreBag(this.storebagAsJSON, applicationName, senderServiceID); + } + _checkbagMinVersion(bagMinVersionDict, currentVersionDict) { + if (bagMinVersionDict && currentVersionDict) { + for (let key in bagMinVersionDict) { + if (bagMinVersionDict.hasOwnProperty(key)) { + let bagMinVersion = bagMinVersionDict[key]; + if (currentVersionDict.hasOwnProperty(key)) { + let currentVersion = currentVersionDict[key]; + if (Object(semver__WEBPACK_IMPORTED_MODULE_1__["gte"])(currentVersion, bagMinVersion)) { + return true; + } + } + } + } + return false; + } + return true; + } + _parseAndReturnStoreBag(storebag, applicationName, senderServiceID) { + let selectedBag = null; + let selectedBagConfig = null; + let selectedBagConfigKey = null; + let selectedBagVersion = null; + storebag.forEach(function (bag) { + selectedBag = null; + if (bag[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].Scheme] === _reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagConfig"].StoreBagVersionSupported) { + let bagList = bag[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].BagList]; + Object.keys(bagList).some(function (bagConfigKey) { + let bagConfig = bagList[bagConfigKey]; + let bagMinVersionDict = bagConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].MinOSVersionDict]; + if (!this._checkbagMinVersion(bagMinVersionDict, this.bagMinVersionDict)) { + console.log(`failed to match the version # `); + return false; + } + if (!this._canUseBagConfig(bagConfig, applicationName, senderServiceID)) { + return false; + } + selectedBag = bagConfig; + selectedBagConfigKey = bagConfigKey; + selectedBagVersion = bag[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].BagVersion]; + return false; + }.bind(this)); + } + if (selectedBag) { + selectedBagConfig = this._fillSelectedBagConfig(selectedBag, bag); + } + }.bind(this)); + if (!selectedBagConfig) { + console.log(`${_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingAgentDefaultConfig"].LogMsgPrefix} Failed to select a storebag.`); + } + return new RTCReportingParsedStoreBag(selectedBagConfig, selectedBagConfigKey, selectedBagVersion, this.bagMinVersionDict); + } + _fetchStoreBag() { + if (this.storebagURL && !this.storebagAsJSON) { + this._requestStoreBagAsync(); + } + } + _requestStoreBagAsync() { + this.storebagLoader = new XMLHttpRequest(); + this.storebagLoader.onreadystatechange = function () { + if (this.storebagLoader && this.storebagLoader.readyState === 4 && this.storebagLoader.status === 200 && this.storebagLoader.responseText) { + this.storebagAsJSON = JSON.parse(this.storebagLoader.responseText); + } + }.bind(this); + this.storebagLoader.open('GET', this.storebagURL, true); + this.storebagLoader.send(null); + } + _canUseBagConfig(bagConfig, applicationName, senderServiceID) { + let bagClientName = bagConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].ClientName]; + if (bagClientName && bagClientName !== this.clientName) { + return false; + } + let bagServiceNameList = bagConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].ServiceNameList]; + let bagServiceName = bagConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].ServiceName]; + if (bagServiceNameList && bagServiceNameList.length > 0 && -1 === bagServiceNameList.indexOf(this.serviceName)) { + return false; + } + if (!bagServiceNameList && bagServiceName && bagServiceName !== this.serviceName) { + return false; + } + let bagApplicationNameList = bagConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].ApplicationNameList]; + if (bagApplicationNameList && bagApplicationNameList.length > 0 && -1 === bagApplicationNameList.indexOf(applicationName)) { + return false; + } + let bagDeviceList = bagConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].DeviceList]; + if (bagDeviceList && bagDeviceList.length > 0 && -1 === bagDeviceList.indexOf(this.deviceName)) { + return false; + } + let bagSenderServiceIDList = bagConfig[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].SenderServiceIDList]; + if (bagSenderServiceIDList && bagSenderServiceIDList.length > 0 && -1 === bagSenderServiceIDList.indexOf(senderServiceID)) { + return false; + } + return true; + } + _fillSelectedBagConfig(selectedBag, bag) { + let selectedBagConfig = null; + if (!selectedBag || !bag) { + return selectedBagConfig; + } + let configurationList = bag[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].ConfigurationList]; + let listOfConfigs = selectedBag[_reporting_config__WEBPACK_IMPORTED_MODULE_0__["RTCReportingStoreBagKeys"].Configurations]; + if (!listOfConfigs) { + return selectedBagConfig; + } + listOfConfigs.forEach(function (configName) { + let config = configurationList[configName]; + selectedBagConfig = selectedBagConfig ? selectedBagConfig : {}; + Object.assign(selectedBagConfig, config); + }); + return selectedBagConfig; + } + } + + + + /***/ }) + + /******/ })["default"]; + }); + + }(rtc)); + + var socket_io_min = {exports: {}}; + + /*! + * Socket.IO v4.0.2 + * (c) 2014-2021 Guillermo Rauch + * Released under the MIT License. + */ + + (function (module, exports) { + !function(t,e){module.exports=e();}(self,(function(){return function(t){var e={};function n(r){if(e[r])return e[r].exports;var o=e[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,n),o.l=!0,o.exports}return n.m=t,n.c=e,n.d=function(t,e,r){n.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r});},n.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0});},n.t=function(t,e){if(1&e&&(t=n(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(n.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var o in t)n.d(r,o,function(e){return t[e]}.bind(null,o));return r},n.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return n.d(e,"a",e),e},n.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},n.p="",n(n.s=18)}([function(t,e,n){var r=n(24),o=n(25),i=String.fromCharCode(30);t.exports={protocol:4,encodePacket:r,encodePayload:function(t,e){var n=t.length,o=new Array(n),s=0;t.forEach((function(t,c){r(t,!1,(function(t){o[c]=t,++s===n&&e(o.join(i));}));}));},decodePacket:o,decodePayload:function(t,e){for(var n=t.split(i),r=[],s=0;s0;case l.ACK:case l.BINARY_ACK:return Array.isArray(e)}}}]),n}(h);e.Decoder=b;var m=function(){function t(e){u(this,t),this.packet=e,this.buffers=[],this.reconPack=e;}return p(t,[{key:"takeBinaryData",value:function(t){if(this.buffers.push(t),this.buffers.length===this.reconPack.attachments){var e=y.reconstructPacket(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null}},{key:"finishedReconstruction",value:function(){this.reconPack=null,this.buffers=[];}}]),t}();},function(t,e){var n=/^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,r=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];t.exports=function(t){var e=t,o=t.indexOf("["),i=t.indexOf("]");-1!=o&&-1!=i&&(t=t.substring(0,o)+t.substring(o,i).replace(/:/g,";")+t.substring(i,t.length));for(var s,c,a=n.exec(t||""),u={},f=14;f--;)u[r[f]]=a[f]||"";return -1!=o&&-1!=i&&(u.source=e,u.host=u.host.substring(1,u.host.length-1).replace(/;/g,":"),u.authority=u.authority.replace("[","").replace("]","").replace(/;/g,":"),u.ipv6uri=!0),u.pathNames=function(t,e){var n=e.replace(/\/{2,9}/g,"/").split("/");"/"!=e.substr(0,1)&&0!==e.length||n.splice(0,1);"/"==e.substr(e.length-1,1)&&n.splice(n.length-1,1);return n}(0,u.path),u.queryKey=(s=u.query,c={},s.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,(function(t,e,n){e&&(c[e]=n);})),c),u};},function(t,e,n){function r(t){return (r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function o(t,e){for(var n=0;n=this._reconnectionAttempts)this.backoff.reset(),this.emitReserved("reconnect_failed"),this._reconnecting=!1;else {var n=this.backoff.duration();this._reconnecting=!0;var r=setTimeout((function(){e.skipReconnect||(t.emitReserved("reconnect_attempt",e.backoff.attempts),e.skipReconnect||e.open((function(n){n?(e._reconnecting=!1,e.reconnect(),t.emitReserved("reconnect_error",n)):e.onreconnect();})));}),n);this.opts.autoUnref&&r.unref(),this.subs.push((function(){clearTimeout(r);}));}}},{key:"onreconnect",value:function(){var t=this.backoff.attempts;this._reconnecting=!1,this.backoff.reset(),this.emitReserved("reconnect",t);}}])&&o(e.prototype,n),y}(n(17).StrictEventEmitter);e.Manager=y;},function(t,e,n){var r=n(9),o=n(23),i=n(27),s=n(28);e.polling=function(t){var e=!1,n=!1,s=!1!==t.jsonp;if("undefined"!=typeof location){var c="https:"===location.protocol,a=location.port;a||(a=c?443:80),e=t.hostname!==location.hostname||a!==t.port,n=t.secure!==c;}if(t.xdomain=e,t.xscheme=n,"open"in new r(t)&&!t.forceJSONP)return new o(t);if(!s)throw new Error("JSONP disabled");return new i(t)},e.websocket=s;},function(t,e,n){var r=n(22),o=n(2);t.exports=function(t){var e=t.xdomain,n=t.xscheme,i=t.enablesXDR;try{if("undefined"!=typeof XMLHttpRequest&&(!e||r))return new XMLHttpRequest}catch(t){}try{if("undefined"!=typeof XDomainRequest&&!n&&i)return new XDomainRequest}catch(t){}if(!e)try{return new(o[["Active"].concat("Object").join("X")])("Microsoft.XMLHTTP")}catch(t){}};},function(t,e,n){function r(t){return (r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function o(t,e){if(!(t instanceof e))throw new TypeError("Cannot call a class as a function")}function i(t,e){for(var n=0;n0);return e}function u(){var t=a(+new Date);return t!==r?(s=0,r=t):t+"."+a(s++)}for(;c<64;c++)i[o[c]]=c;u.encode=a,u.decode=function(t){var e=0;for(c=0;c1?e-1:0),r=1;r=t.length?{done:!0}:{done:!1,value:t[r++]}},e:function(t){throw t},f:o}}throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.")}var s,c=!0,a=!1;return {s:function(){n=t[Symbol.iterator]();},n:function(){var t=n.next();return c=t.done,t},e:function(t){a=!0,s=t;},f:function(){try{c||null==n.return||n.return();}finally{if(a)throw s}}}}function i(t,e){(null==e||e>t.length)&&(e=t.length);for(var n=0,r=new Array(e);n1?e-1:0),r=1;r1?n-1:0),o=1;o1?n-1:0),o=1;o1&&void 0!==arguments[1]?arguments[1]:"",n=arguments.length>2?arguments[2]:void 0,o=t;n=n||"undefined"!=typeof location&&location,null==t&&(t=n.protocol+"//"+n.host),"string"==typeof t&&("/"===t.charAt(0)&&(t="/"===t.charAt(1)?n.protocol+t:n.host+t),/^(https?|wss?):\/\//.test(t)||(t=void 0!==n?n.protocol+"//"+t:"https://"+t),o=r(t)),o.port||(/^(http|ws)$/.test(o.protocol)?o.port="80":/^(http|ws)s$/.test(o.protocol)&&(o.port="443")),o.path=o.path||"/";var i=-1!==o.host.indexOf(":"),s=i?"["+o.host+"]":o.host;return o.id=o.protocol+"://"+s+":"+o.port+e,o.href=o.protocol+"://"+s+(n&&n.port===o.port?"":":"+o.port),o};},function(t,e,n){var r=n(21);t.exports=function(t,e){return new r(t,e)},t.exports.Socket=r,t.exports.protocol=r.protocol,t.exports.Transport=n(3),t.exports.transports=n(8),t.exports.parser=n(0);},function(t,e,n){function r(){return (r=Object.assign||function(t){for(var e=1;e1&&void 0!==arguments[1]?arguments[1]:{};return i(this,l),e=f.call(this),t&&"object"===o(t)&&(n=t,t=null),t?(t=y(t),n.hostname=t.host,n.secure="https"===t.protocol||"wss"===t.protocol,n.port=t.port,t.query&&(n.query=t.query)):n.host&&(n.hostname=y(n.host).host),e.secure=null!=n.secure?n.secure:"undefined"!=typeof location&&"https:"===location.protocol,n.hostname&&!n.port&&(n.port=e.secure?"443":"80"),e.hostname=n.hostname||("undefined"!=typeof location?location.hostname:"localhost"),e.port=n.port||("undefined"!=typeof location&&location.port?location.port:e.secure?443:80),e.transports=n.transports||["polling","websocket"],e.readyState="",e.writeBuffer=[],e.prevBufferLen=0,e.opts=r({path:"/engine.io",agent:!1,withCredentials:!1,upgrade:!0,jsonp:!0,timestampParam:"t",rememberUpgrade:!1,rejectUnauthorized:!0,perMessageDeflate:{threshold:1024},transportOptions:{}},n),e.opts.path=e.opts.path.replace(/\/$/,"")+"/","string"==typeof e.opts.query&&(e.opts.query=d.decode(e.opts.query)),e.id=null,e.upgrades=null,e.pingInterval=null,e.pingTimeout=null,e.pingTimeoutTimer=null,"function"==typeof addEventListener&&(addEventListener("beforeunload",(function(){e.transport&&(e.transport.removeAllListeners(),e.transport.close());}),!1),"localhost"!==e.hostname&&(e.offlineEventListener=function(){e.onClose("transport close");},addEventListener("offline",e.offlineEventListener,!1))),e.open(),e}return e=l,(n=[{key:"createTransport",value:function(t){var e=function(t){var e={};for(var n in t)t.hasOwnProperty(n)&&(e[n]=t[n]);return e}(this.opts.query);e.EIO=h.protocol,e.transport=t,this.id&&(e.sid=this.id);var n=r({},this.opts.transportOptions[t],this.opts,{query:e,socket:this,hostname:this.hostname,secure:this.secure,port:this.port});return new p[t](n)}},{key:"open",value:function(){var t;if(this.opts.rememberUpgrade&&l.priorWebsocketSuccess&&-1!==this.transports.indexOf("websocket"))t="websocket";else {if(0===this.transports.length){var e=this;return void setTimeout((function(){e.emit("error","No transports available");}),0)}t=this.transports[0];}this.readyState="opening";try{t=this.createTransport(t);}catch(t){return this.transports.shift(),void this.open()}t.open(),this.setTransport(t);}},{key:"setTransport",value:function(t){var e=this;this.transport&&this.transport.removeAllListeners(),this.transport=t,t.on("drain",(function(){e.onDrain();})).on("packet",(function(t){e.onPacket(t);})).on("error",(function(t){e.onError(t);})).on("close",(function(){e.onClose("transport close");}));}},{key:"probe",value:function(t){var e=this.createTransport(t,{probe:1}),n=!1,r=this;function o(){if(r.onlyBinaryUpgrades){var t=!this.supportsBinary&&r.transport.supportsBinary;n=n||t;}n||(e.send([{type:"ping",data:"probe"}]),e.once("packet",(function(t){if(!n)if("pong"===t.type&&"probe"===t.data){if(r.upgrading=!0,r.emit("upgrading",e),!e)return;l.priorWebsocketSuccess="websocket"===e.name,r.transport.pause((function(){n||"closed"!==r.readyState&&(f(),r.setTransport(e),e.send([{type:"upgrade"}]),r.emit("upgrade",e),e=null,r.upgrading=!1,r.flush());}));}else {var o=new Error("probe error");o.transport=e.name,r.emit("upgradeError",o);}})));}function i(){n||(n=!0,f(),e.close(),e=null);}function s(t){var n=new Error("probe error: "+t);n.transport=e.name,i(),r.emit("upgradeError",n);}function c(){s("transport closed");}function a(){s("socket closed");}function u(t){e&&t.name!==e.name&&i();}function f(){e.removeListener("open",o),e.removeListener("error",s),e.removeListener("close",c),r.removeListener("close",a),r.removeListener("upgrading",u);}l.priorWebsocketSuccess=!1,e.once("open",o),e.once("error",s),e.once("close",c),this.once("close",a),this.once("upgrading",u),e.open();}},{key:"onOpen",value:function(){if(this.readyState="open",l.priorWebsocketSuccess="websocket"===this.transport.name,this.emit("open"),this.flush(),"open"===this.readyState&&this.opts.upgrade&&this.transport.pause)for(var t=0,e=this.upgrades.length;t0&&void 0!==arguments[0]?arguments[0]:{};return o(t,{xd:this.xd,xs:this.xs},this.opts),new w(this.uri(),t)}},{key:"doWrite",value:function(t,e){var n=this.request({method:"POST",data:t}),r=this;n.on("success",e),n.on("error",(function(t){r.onError("xhr post error",t);}));}},{key:"doPoll",value:function(){var t=this.request(),e=this;t.on("data",(function(t){e.onData(t);})),t.on("error",(function(t){e.onError("xhr poll error",t);})),this.pollXhr=t;}}]),n}(y),w=function(t){a(n,t);var e=f(n);function n(t,r){var o;return i(this,n),(o=e.call(this)).opts=r,o.method=r.method||"GET",o.uri=t,o.async=!1!==r.async,o.data=void 0!==r.data?r.data:null,o.create(),o}return c(n,[{key:"create",value:function(){var t=v(this.opts,"agent","enablesXDR","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","autoUnref");t.xdomain=!!this.opts.xd,t.xscheme=!!this.opts.xs;var e=this.xhr=new h(t),r=this;try{e.open(this.method,this.uri,this.async);try{if(this.opts.extraHeaders)for(var o in e.setDisableHeaderCheck&&e.setDisableHeaderCheck(!0),this.opts.extraHeaders)this.opts.extraHeaders.hasOwnProperty(o)&&e.setRequestHeader(o,this.opts.extraHeaders[o]);}catch(t){}if("POST"===this.method)try{e.setRequestHeader("Content-type","text/plain;charset=UTF-8");}catch(t){}try{e.setRequestHeader("Accept","*/*");}catch(t){}"withCredentials"in e&&(e.withCredentials=this.opts.withCredentials),this.opts.requestTimeout&&(e.timeout=this.opts.requestTimeout),this.hasXDR()?(e.onload=function(){r.onLoad();},e.onerror=function(){r.onError(e.responseText);}):e.onreadystatechange=function(){4===e.readyState&&(200===e.status||1223===e.status?r.onLoad():setTimeout((function(){r.onError("number"==typeof e.status?e.status:0);}),0));},e.send(this.data);}catch(t){return void setTimeout((function(){r.onError(t);}),0)}"undefined"!=typeof document&&(this.index=n.requestsCount++,n.requests[this.index]=this);}},{key:"onSuccess",value:function(){this.emit("success"),this.cleanup();}},{key:"onData",value:function(t){this.emit("data",t),this.onSuccess();}},{key:"onError",value:function(t){this.emit("error",t),this.cleanup(!0);}},{key:"cleanup",value:function(t){if(void 0!==this.xhr&&null!==this.xhr){if(this.hasXDR()?this.xhr.onload=this.xhr.onerror=m:this.xhr.onreadystatechange=m,t)try{this.xhr.abort();}catch(t){}"undefined"!=typeof document&&delete n.requests[this.index],this.xhr=null;}}},{key:"onLoad",value:function(){var t=this.xhr.responseText;null!==t&&this.onData(t);}},{key:"hasXDR",value:function(){return "undefined"!=typeof XDomainRequest&&!this.xs&&this.enablesXDR}},{key:"abort",value:function(){this.cleanup();}}]),n}(d);if(w.requestsCount=0,w.requests={},"undefined"!=typeof document)if("function"==typeof attachEvent)attachEvent("onunload",_);else if("function"==typeof addEventListener){addEventListener("onpagehide"in b?"pagehide":"unload",_,!1);}function _(){for(var t in w.requests)w.requests.hasOwnProperty(t)&&w.requests[t].abort();}t.exports=k,t.exports.Request=w;},function(t,e,n){var r=n(11).PACKET_TYPES,o="function"==typeof Blob||"undefined"!=typeof Blob&&"[object BlobConstructor]"===Object.prototype.toString.call(Blob),i="function"==typeof ArrayBuffer,s=function(t,e){var n=new FileReader;return n.onload=function(){var t=n.result.split(",")[1];e("b"+t);},n.readAsDataURL(t)};t.exports=function(t,e,n){var c,a=t.type,u=t.data;return o&&u instanceof Blob?e?n(u):s(u,n):i&&(u instanceof ArrayBuffer||(c=u,"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(c):c&&c.buffer instanceof ArrayBuffer))?e?n(u instanceof ArrayBuffer?u:u.buffer):s(new Blob([u]),n):n(r[a]+(u||""))};},function(t,e,n){var r,o=n(11),i=o.PACKET_TYPES_REVERSE,s=o.ERROR_PACKET;"function"==typeof ArrayBuffer&&(r=n(26));var c=function(t,e){if(r){var n=r.decode(t);return a(n,e)}return {base64:!0,data:t}},a=function(t,e){switch(e){case"blob":return t instanceof ArrayBuffer?new Blob([t]):t;case"arraybuffer":default:return t}};t.exports=function(t,e){if("string"!=typeof t)return {type:"message",data:a(t,e)};var n=t.charAt(0);return "b"===n?{type:"message",data:c(t.substring(1),e)}:i[n]?t.length>1?{type:i[n],data:t.substring(1)}:{type:i[n]}:s};},function(t,e){!function(t){e.encode=function(e){var n,r=new Uint8Array(e),o=r.length,i="";for(n=0;n>2],i+=t[(3&r[n])<<4|r[n+1]>>4],i+=t[(15&r[n+1])<<2|r[n+2]>>6],i+=t[63&r[n+2]];return o%3==2?i=i.substring(0,i.length-1)+"=":o%3==1&&(i=i.substring(0,i.length-2)+"=="),i},e.decode=function(e){var n,r,o,i,s,c=.75*e.length,a=e.length,u=0;"="===e[e.length-1]&&(c--,"="===e[e.length-2]&&c--);var f=new ArrayBuffer(c),p=new Uint8Array(f);for(n=0;n>4,p[u++]=(15&o)<<4|i>>2,p[u++]=(3&i)<<6|63&s;return f};}("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");},function(t,e,n){function r(t){return (r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function o(t,e){for(var n=0;n';n=document.createElement(t);}catch(t){(n=document.createElement("iframe")).name=r.iframeId,n.src="javascript:0";}n.id=r.iframeId,r.form.appendChild(n),r.iframe=n;}this.form.action=this.uri(),a(),t=t.replace(d,"\\\n"),this.area.value=t.replace(y,"\\n");try{this.form.submit();}catch(t){}this.iframe.attachEvent?this.iframe.onreadystatechange=function(){"complete"===r.iframe.readyState&&c();}:this.iframe.onload=c;}},{key:"supportsBinary",get:function(){return !1}}])&&o(e.prototype,n),l}(l);t.exports=v;},function(t,e,n){function r(t){return (r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function o(t,e){for(var n=0;n0&&t.jitter<=1?t.jitter:0,this.attempts=0;}t.exports=n,n.prototype.duration=function(){var t=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var e=Math.random(),n=Math.floor(e*this.jitter*t);t=0==(1&Math.floor(10*e))?t-n:t+n;}return 0|Math.min(t,this.max)},n.prototype.reset=function(){this.attempts=0;},n.prototype.setMin=function(t){this.ms=t;},n.prototype.setMax=function(t){this.max=t;},n.prototype.setJitter=function(t){this.jitter=t;};}])})); + + }(socket_io_min)); + + var lzString_min = {exports: {}}; + + (function (module) { + var LZString=function(){function o(o,r){if(!t[o]){t[o]={};for(var n=0;ne;e++){var s=r.charCodeAt(e);n[2*e]=s>>>8,n[2*e+1]=s%256;}return n},decompressFromUint8Array:function(o){if(null===o||void 0===o)return i.decompress(o);for(var n=new Array(o.length/2),e=0,t=n.length;t>e;e++)n[e]=256*o[2*e]+o[2*e+1];var s=[];return n.forEach(function(o){s.push(r(o));}),i.decompress(s.join(""))},compressToEncodedURIComponent:function(o){return null==o?"":i._compress(o,6,function(o){return e.charAt(o)})},decompressFromEncodedURIComponent:function(r){return null==r?"":""==r?null:(r=r.replace(/ /g,"+"),i._decompress(r.length,32,function(n){return o(e,r.charAt(n))}))},compress:function(o){return i._compress(o,16,function(o){return r(o)})},_compress:function(o,r,n){if(null==o)return "";var e,t,i,s={},p={},u="",c="",a="",l=2,f=3,h=2,d=[],m=0,v=0;for(i=0;ie;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;}else {for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a];}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++),s[c]=f++,a=String(u);}if(""!==a){if(Object.prototype.hasOwnProperty.call(p,a)){if(a.charCodeAt(0)<256){for(e=0;h>e;e++)m<<=1,v==r-1?(v=0,d.push(n(m)),m=0):v++;for(t=a.charCodeAt(0),e=0;8>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;}else {for(t=1,e=0;h>e;e++)m=m<<1|t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t=0;for(t=a.charCodeAt(0),e=0;16>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;}l--,0==l&&(l=Math.pow(2,h),h++),delete p[a];}else for(t=s[a],e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;l--,0==l&&(l=Math.pow(2,h),h++);}for(t=2,e=0;h>e;e++)m=m<<1|1&t,v==r-1?(v=0,d.push(n(m)),m=0):v++,t>>=1;for(;;){if(m<<=1,v==r-1){d.push(n(m));break}v++;}return d.join("")},decompress:function(o){return null==o?"":""==o?null:i._decompress(o.length,32768,function(r){return o.charCodeAt(r)})},_decompress:function(o,n,e){var i,s,p,u,c,a,l,f=[],h=4,d=4,m=3,v="",w=[],A={val:e(0),position:n,index:1};for(i=0;3>i;i+=1)f[i]=i;for(p=0,c=Math.pow(2,2),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;l=r(p);break;case 2:return ""}for(f[3]=l,s=l,w.push(l);;){if(A.index>o)return "";for(p=0,c=Math.pow(2,m),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;switch(l=p){case 0:for(p=0,c=Math.pow(2,8),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 1:for(p=0,c=Math.pow(2,16),a=1;a!=c;)u=A.val&A.position,A.position>>=1,0==A.position&&(A.position=n,A.val=e(A.index++)),p|=(u>0?1:0)*a,a<<=1;f[d++]=r(p),l=d-1,h--;break;case 2:return w.join("")}if(0==h&&(h=Math.pow(2,m),m++),f[l])v=f[l];else {if(l!==d)return null;v=s+s.charAt(0);}w.push(v),f[d++]=s+v.charAt(0),h--,s=v,0==h&&(h=Math.pow(2,m),m++);}}};return i}();null!=module&&(module.exports=LZString); + }(lzString_min)); + + var lzstring = lzString_min.exports; + + /** + * This file contains methods related to retrieving, storing and reading the logs + * from a remote server, it is used for QA only and all of them should not get + * included in the bundle when using production mode + */ + /** + * Logger utility methods used for debugging + * + * @param {store} store - A storage solution, either sessionStorage, localStorage, indexeddb, etc + * @constructor + */ + const LoggerExternals = (store = sessionStorage) => { + { + return { + /** + * Compress a string to take less space during transmission + * + * @param {string} string - The string to compress + * @returns {string} - The compressed string + */ + compress: (string) => { + return lzstring.compressToUTF16(string); + }, + /** + * Deletes the logs stored in the store + * + * @return {void} + */ + logClear: () => { + return store.clear(); + }, + /** + * Read the logs that were stored in the long term storage (compressed) + * + * @param {number} chunk - The chunk to read from the log file + * @param {number} chunkSize - The size of the chunk + * @return {string} + */ + logRead: (chunk = 0, chunkSize = LoggerExternals(store).logReadLength()) => { + const startPosition = chunk * chunkSize; + const endPosition = startPosition + chunkSize; + const log = []; + // Read each stored line until we read all the lines in a chunk + for (let i = startPosition; i <= endPosition; i += 1) { + const item = lzstring.decompress(store.getItem(String(i))); + if (item) + log.push(JSON.parse(item)); + } + return JSON.stringify(log); + }, + /** + * Read the size of the logs stored in the sessionStorage, used by the server to calculate + * chunk transmission size + * + * @return {number} + */ + logReadLength: () => { + return parseInt(store.getItem('logLength'), 10) || 0; + }, + /** + * Store logs in the storage mechanism for later retrieval + * + * @param {string} level - The console level (ex: info, warn, etc) + * @param {*} logEvent - The data + */ + logStore: (level, logEvent) => { + store.setItem('logLength', String(LoggerExternals(store).logReadLength() + 1)); + store.setItem(String(store.length), lzstring.compress(JSON.stringify(logEvent))); + }, + }; + } + }; + var LoggerExternals$1 = LoggerExternals; + + /** + * see https://tools.ietf.org/html/rfc1808 + * + * + * This file was modified by Apple Inc. + * Modifications Copyright (c) 2018- + * + * The initial developer is https://github.com/tjenkinson/url-toolkit. Copyright (c) 2016 Tom Jenkinson. + * + * Licensed under the Apache License, Version 2.0 (the “License”); you may not + * use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed + * under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + */ + const URL_REGEX = /^((?:[^\/;?#]+:)?)(\/\/[^\/\;?#]*)?(.*?)??(;.*?)?(\?.*?)?(#.*?)?$/; + const FIRST_SEGMENT_REGEX = /^([^\/;?#]*)(.*)$/; + const SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g; + const SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g; + const URLToolkit = { + // jshint ignore:line + // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or // + // E.g + // With opts.alwaysNormalize = false (default, spec compliant) + // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g + // With opts.alwaysNormalize = true (default, not spec compliant) + // http://a.com/b/cd + /e/f/../g => http://a.com/e/g + buildAbsoluteURL: function (baseURL, relativeURL, opts) { + opts = opts || {}; + // remove any remaining space and CRLF + baseURL = baseURL.trim(); + relativeURL = relativeURL.trim(); + if (!relativeURL) { + // 2a) If the embedded URL is entirely empty, it inherits the + // entire base URL (i.e., is set equal to the base URL) + // and we are done. + if (!opts.alwaysNormalize) { + return baseURL; + } + const basePartsForNormalise = URLToolkit.parseURL(baseURL); + if (!basePartsForNormalise) { + throw new Error('Error trying to parse base URL.'); + } + basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path); + return URLToolkit.buildURLFromParts(basePartsForNormalise); + } + const relativeParts = URLToolkit.parseURL(relativeURL); + if (!relativeParts) { + throw new Error('Error trying to parse relative URL.'); + } + if (relativeParts.scheme) { + // 2b) If the embedded URL starts with a scheme name, it is + // interpreted as an absolute URL and we are done. + if (!opts.alwaysNormalize) { + return relativeURL; + } + relativeParts.path = URLToolkit.normalizePath(relativeParts.path); + return URLToolkit.buildURLFromParts(relativeParts); + } + const baseParts = URLToolkit.parseURL(baseURL); + if (!baseParts) { + throw new Error('Error trying to parse base URL.'); + } + if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') { + // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc + // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a' + const pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path); + baseParts.netLoc = pathParts[1]; + baseParts.path = pathParts[2]; + } + if (baseParts.netLoc && !baseParts.path) { + baseParts.path = '/'; + } + const builtParts = { + // 2c) Otherwise, the embedded URL inherits the scheme of + // the base URL. + scheme: baseParts.scheme, + netLoc: relativeParts.netLoc, + path: null, + params: relativeParts.params, + query: relativeParts.query, + fragment: relativeParts.fragment, + }; + if (!relativeParts.netLoc) { + // 3) If the embedded URL's is non-empty, we skip to + // Step 7. Otherwise, the embedded URL inherits the + // (if any) of the base URL. + builtParts.netLoc = baseParts.netLoc; + // 4) If the embedded URL path is preceded by a slash "/", the + // path is not relative and we skip to Step 7. + if (relativeParts.path[0] !== '/') { + if (!relativeParts.path) { + // 5) If the embedded URL path is empty (and not preceded by a + // slash), then the embedded URL inherits the base URL path + builtParts.path = baseParts.path; + // 5a) if the embedded URL's is non-empty, we skip to + // step 7; otherwise, it inherits the of the base + // URL (if any) and + if (!relativeParts.params) { + builtParts.params = baseParts.params; + // 5b) if the embedded URL's is non-empty, we skip to + // step 7; otherwise, it inherits the of the base + // URL (if any) and we skip to step 7. + if (!relativeParts.query) { + builtParts.query = baseParts.query; + } + } + } + else { + // Non-standard, allow non-empty paths to inherit the query + if (opts.inheritQuery) { + if (!relativeParts.query) { + builtParts.query = baseParts.query; + } + } + // 6) The last segment of the base URL's path (anything + // following the rightmost slash "/", or the entire path if no + // slash is present) is removed and the embedded URL's path is + // appended in its place. + const baseURLPath = baseParts.path; + const newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path; + builtParts.path = URLToolkit.normalizePath(newPath); + } + } + } + if (builtParts.path === null) { + builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path; + } + return URLToolkit.buildURLFromParts(builtParts); + }, + parseURL: function (url) { + const parts = URL_REGEX.exec(url); + if (!parts) { + return null; + } + return { + scheme: parts[1] || '', + netLoc: parts[2] || '', + path: parts[3] || '', + params: parts[4] || '', + query: parts[5] || '', + fragment: parts[6] || '', + }; + }, + normalizePath: function (path) { + // The following operations are + // then applied, in order, to the new path: + // 6a) All occurrences of "./", where "." is a complete path + // segment, are removed. + // 6b) If the path ends with "." as a complete path segment, + // that "." is removed. + path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, ''); + // 6c) All occurrences of "/../", where is a + // complete path segment not equal to "..", are removed. + // Removal of these path segments is performed iteratively, + // removing the leftmost matching pattern on each iteration, + // until no matching pattern remains. + // 6d) If the path ends with "/..", where is a + // complete path segment not equal to "..", that + // "/.." is removed. + while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) { } // eslint-disable-line + return path.split('').reverse().join(''); + }, + buildURLFromParts: function (parts) { + return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment; + }, + getHostName: function (url) { + let hostname; + if (!url) { + return hostname; + } + if (url.indexOf('://') > -1) { + hostname = url.split('/')[2]; + } + else { + hostname = url.split('/')[0]; + } + // remove port and query if present + hostname = hostname.split(':')[0]; + hostname = hostname.split('?')[0]; + return hostname; + }, + }; + var URLToolkit$1 = URLToolkit; + + /** + * URL utils for hostname and other stuff + */ + function isCustomUrl(url) { + return url != null && !url.startsWith('http://') && !url.startsWith('https://'); + } + function getHostName(url) { + if (url == null) { + return null; + } + if (isCustomUrl(url)) { + return null; + } + return URLToolkit.getHostName(url); + } + const hostsRequiringQueries = ['.*.itunes.apple.com']; + const URLQueries = { + deviceName: '&xapdn=', + deviceModel: '&xapdm=', + language: '&xapdl=', + dsid: '&xapdsid=', + subs: '&xapsub=', + }; + function urlNeedsUpdate(url, enableQueryParamsForITunes) { + if (enableQueryParamsForITunes) { + const hostName = getHostName(url); + return (undefined !== + hostsRequiringQueries.find((regStr) => { + const regex = new RegExp(regStr); + return regex.exec(hostName); + })); + } + return false; + } + function shouldUpdateUrlWithDeviceNameAndModel(platformInfo) { + const deviceModel = platformInfo.model, deviceName = platformInfo.manufacturer; + if (!deviceModel || !deviceName) { + getLogger().warn(`Missing model/manufacturer in platformInfo model ${deviceModel} manufacturer ${deviceName}`); + } + return !!deviceModel && !!deviceName; + } + function updateUrlWithDeviceNameAndModel(url, platformInfo) { + const deviceModel = platformInfo.model, deviceName = platformInfo.manufacturer, urlHasQueryParams = url.indexOf('?') !== -1, deviceModelEnc = encodeURIComponent(deviceModel), deviceNameEnc = encodeURIComponent(deviceName); + url = urlHasQueryParams ? url : url + '?'; + return url + URLQueries.deviceName + deviceNameEnc + URLQueries.deviceModel + deviceModelEnc; + } + function updateUrlWithOptionsQuery(url, queryParameters) { + const urlHasQueryParams = url.indexOf('?') !== -1; + url = urlHasQueryParams ? url : url + '?'; + let query; + const logger = getLogger(); + for (query in queryParameters) { + if (!queryParameters[query]) { + logger.warn(`Missing ${query} info`); + } + else { + url += URLQueries[query] + (query === 'subs' ? encodeURIComponent(queryParameters[query]) : queryParameters[query]); + } + } + return url; + } + function updateUrlWithQueryStrings(url, platformInfo, queryParameters) { + if (shouldUpdateUrlWithDeviceNameAndModel(platformInfo)) { + url = updateUrlWithDeviceNameAndModel(url, platformInfo); + } + url = updateUrlWithOptionsQuery(url, queryParameters); + return url; + } + function hasMatchingHost(hostName, url) { + return !hostName || hostName === getHostName(url); + } + function isRedirectStatusCode(status) { + return status === 300 || status === 302 || status === 303 || status === 305; + } + /** + * Return all the GET parameters passed to a page + * This function is different from parseParamsFromURL(), + * where it just extracts all the parameters + * without trying to determine the correct type + * + * @param {string} href - The url to extract + * @param {array} exclude - A list of parameters to exclude from the list + * @return {object} - Key value pair with parameter name as key and value + */ + const getURLParams = (href, exclude = []) => { + const params = {}; + try { + const urlObject = new URL(href); + const urlSearch = urlObject.search; + const searchParams = new URLSearchParams(urlSearch); + for (const param of searchParams.entries()) { + if (!exclude.includes(param[0])) + params[param[0]] = param[1]; + } + } + catch (e) { + getLogger().error(e); + } + return params; + }; + function getTimeOffsetParameter(url) { + const paramString = URLToolkit.parseURL(url).fragment.substr(1); + if (paramString.length === 0) { + return null; + } + const searchParams = new URLSearchParams(paramString); + if (!searchParams.has('t')) { + return null; + } + const timeOffsetValue = Number(searchParams.get('t')); + if (isFiniteNumber(timeOffsetValue)) { + return timeOffsetValue; + } + return null; + } + /** + * Get specified parameters from the search query while + * trying to extract the correct type from each one. + * + * @param {array} extract - The list of parameters to extract + * @return {*} + */ + const parseParamsFromURL = (input, extract) => { + const url = new URL(input); + const params = url.searchParams; + const result = {}; + const getTypedParam = (name, defaultValue = undefined, json = false, transform) => { + const param = params.get(name); + let value; + switch (param) { + case 'undefined': + value = undefined; + break; + case 'null': + value = null; + break; + case 'False': + case 'false': + value = false; + break; + case 'True': + case 'true': + value = true; + break; + default: + if (param) { + const num = Number(param); + if (isFiniteNumber(num)) { + value = num; + } + else { + value = param; + } + } + break; + } + if (typeof value === 'string' && json) { + value = safeParseJSON(value); + } + if (typeof value === 'string' && transform) { + value = transform(value); + } + return value !== null && value !== void 0 ? value : defaultValue; + }; + extract.forEach((param) => { + result[param.name] = getTypedParam(param.name, param.default, param.json, param.transform); + }); + return result; + }; + /** + * Safely parse a json, prevent throwing an error on non-parseable input + * + * @param {string} data - Hopefully a JSON string + * @return {*} + */ + const safeParseJSON = (data) => { + let value; + if (typeof data !== 'string') + return data; + try { + value = JSON.parse(data); + } + catch (err) { + // eslint-disable-next-line no-console + console.error(`failed to parse param: ${data}`); + } + return value; + }; + /* + * Get a list of the dynamic range formats the user has selected + * + * Type 0: SDR + * Type 2: HDR10 + * Type 3: DV + * Type 4: HLG + * + * @returns {array} + */ + function getVideoDynamicRangeFormats(dynamicRangeFormatSDR, dynamicRangeFormatHDR10, dynamicRangeFormatDV, dynamicRangeFormatHLG, averageBitrateCap, peakBitrateCap, clearBitrateCap) { + const videoDynamicRangeFormats = [ + { type: 0, highestPlayableAverageBitRate: undefined, highestPlayablePeakBitRate: undefined }, + { type: 2, highestPlayableAverageBitRate: undefined, highestPlayablePeakBitRate: undefined }, + { type: 3, highestPlayableAverageBitRate: undefined, highestPlayablePeakBitRate: undefined }, + { type: 4, highestPlayableAverageBitRate: undefined, highestPlayablePeakBitRate: undefined }, + ]; + const videoDynamicRangeFormatsMap = { + 0: dynamicRangeFormatSDR, + 2: dynamicRangeFormatHDR10, + 3: dynamicRangeFormatDV, + 4: dynamicRangeFormatHLG, + }; + for (let formatIndex = 0; formatIndex < videoDynamicRangeFormats.length; ++formatIndex) { + videoDynamicRangeFormats[formatIndex].highestPlayableAverageBitRate = averageBitrateCap; + videoDynamicRangeFormats[formatIndex].highestPlayablePeakBitRate = peakBitrateCap; + videoDynamicRangeFormats[formatIndex].highestPlayablePeakBitRateForClearContent = clearBitrateCap; + } + return videoDynamicRangeFormats.filter(function (format) { + return !!videoDynamicRangeFormatsMap[format.type]; + }); + } + /* + * Get a list of the video codecs the user has selected + * + * Type 16: H264 + * Type 64: HEVC + * + * @returns {array} + */ + function getVideoCodecs(videoCodecH264, videoCodecHEVC, averageBitrateCap, peakBitrateCap) { + const videoCodecs = [ + { type: 16, highestPlayableAverageBitRate: undefined, highestPlayablePeakBitRate: undefined }, + { type: 64, highestPlayableAverageBitRate: undefined, highestPlayablePeakBitRate: undefined }, + ]; + const videoCodecsMap = { 16: videoCodecH264, 64: videoCodecHEVC }; + for (let formatIndex = 0; formatIndex < videoCodecs.length; ++formatIndex) { + videoCodecs[formatIndex].highestPlayableAverageBitRate = averageBitrateCap; + videoCodecs[formatIndex].highestPlayablePeakBitRate = peakBitrateCap; + } + return videoCodecs.filter(function (format) { + return !!videoCodecsMap[format.type]; + }); + } + + class AutomationRemote { + constructor() { + this.onStop$ = new Subject(); + this._logger = console; + this._playbackData = { + audio: [], + subtitles: [], + variants: [], + }; + this._timers$ = { + cpu: undefined, + memory: undefined, + system: undefined, + }; + /** + * Register the current parameters or player configuration + * This is used to check if the sockets from the automation server + * are requesting the page to be reloaded + * or the server is trying to load another player url + * + * @param {object} config - The list of GET parameters passed to the URL or other configuration values + * @return {void} + */ + this.setConfig = (config = {}) => { + this._config = JSON.stringify(config); + }; + /** + * Trigger a socket command back to the server to perform backend tasks + * + * @return {void} + */ + this.triggerSocketCommand = (command) => { + if (!command) + throw new Error('No command value was passed'); + this.socket.emit('action', { + type: 'SOCKETS/TRIGGER_CONTROL_SSH', + command, + }); + }; + } + /** + * Do setup/teardown if needed on hls change + * + * @param {object} hls + * @setter + * @return {void} + */ + setHls(hls) { + if (!hls) { + this.onStop$.next(); + this._hls = null; + this._playbackData = { + audio: [], + subtitles: [], + variants: [], + }; + return; + } + this._hls = hls; + this._hls.altAudioOptions$ + .pipe(tap((altAudioOptions) => (this._playbackData.audio = altAudioOptions)), takeUntil(this.onStop$)) + .subscribe(); + this._hls.subtitleOptions$ + .pipe(tap((subtitleOptions) => (this._playbackData.subtitles = subtitleOptions)), takeUntil(this.onStop$)) + .subscribe(); + this._hls.variantOptions$ + .pipe(tap((variantOptions) => (this._playbackData.variants = variantOptions)), takeUntil(this.onStop$)) + .subscribe(); + } + /** + * Set the logger instance + * + * @param {object} logger + * @setter + * @return {void} + */ + setLogger(logger) { + this._logger = logger; + } + /** + * Execute a function that matches the command name + * + * @param {string} command - The function name (ex: seek) + * @param {string} value - The value to pass to the function (ex: 1000) + * @return {*} + */ + applyCommand(command, value) { + if (typeof this[command] === 'function') { + try { + return this[command](value) || true; // Always return a value for the socket server to know we responded + } + catch (e) { + this._logger.error(e); + } + } + } + respondToSocketAction(data, callback) { + const loggerExternals = LoggerExternals$1(); + this._logger.qe({ critical: true, name: 'interact', data }); + if (!this._hls) { + return this._logger.warn(`hls object not initialized while trying to execute ${data.command}`); + } + switch (data.command) { + case 'cpu': + this._logger.qe({ critical: true, name: 'cpu', data: replaceAll('\n', ' | ', data.value) }); + break; + case 'free-memory': + this._logger.qe({ critical: true, name: 'freeMemory', data: replaceAll('\n', ' | ', data.value) }); + break; + case 'log-clear': + callback(loggerExternals.logClear()); + break; + case 'log-end': + //callback(utils.logEnd()); + break; + case 'log-read': + callback({ chunk: data.chunk, log: loggerExternals.compress(loggerExternals.logRead(data.chunk, data.chunkSize)) }); + break; + case 'log-read-length': + callback({ size: loggerExternals.logReadLength(), format: 'array' }); + break; + case 'log-start': + //callback(Utils.logStart()); + break; + case 'memory': + this._logger.qe({ critical: true, name: 'memory', data: getJSMemory() }); + break; + case 'access-log': + this._logger.qe({ critical: true, name: 'accessLog', data: { accessLogItems: this._hls.accessLog } }); + break; + case 'error-log': + this._logger.qe({ critical: true, name: 'errorLog', data: { errorLogItems: this._hls.errorLog } }); + break; + default: + callback(this.applyCommand(data.command, data.value)); + } + } + /** + * Connect to a remote socket.io server + * + * @return {object} + */ + initializeSocketService(socketurl, socketid) { + if (!socketurl) + throw new Error('Attempted to call logConnect() without a socket URL'); + if (!socketid) + throw new Error('Attempted to call logConnect() without a socket ID'); + this.socket = socket_io_min.exports.io(socketurl, { query: { ip: socketid } }); + this._logger.info(`Connecting socket service with server: ${socketurl} and id: ${socketid}`); + this.socket.on('connect', () => this._logger.info(`Connected to: ${socketurl}`)); + this.socket.on('disconnect', () => this._logger.info(`Disonnected from: ${socketurl}`)); + this.socket.on('error', (e) => this._logger.error(e)); + this.socket.on('action', this.respondToSocketAction.bind(this)); + } + /** + * Load a playlist + * + * @param {object} data - Server object containing data needed for playback + * @param {object} options + * @param {number} initialSeekTime + * @return {void} + */ + loadURL(data, options = {}, initialSeekTime = 0) { + var _a; + const pageUrl = data.fullURL || location.href; + const params = parseParamsFromURL(pageUrl, [ + { name: 'averageBitrateCap' }, + { name: 'dynamicRangeFormatSDR', default: true }, + { name: 'dynamicRangeFormatHDR10', default: true }, + { name: 'dynamicRangeFormatDV', default: true }, + { name: 'dynamicRangeFormatHLG', default: true }, + { name: 'maxHdcpLevel', default: 'TYPE-2', transform: decodeURIComponent }, + { name: 'maxSecurityLevel', transform: decodeURIComponent }, + { name: 'peakBitrateCap' }, + { name: 'requiresCDMAttachOnStart', default: false, json: true }, + { name: 'src' }, + { name: 'rand' }, + { name: 'videoCodecH264', default: false }, + { name: 'videoCodecHEVC', default: false }, + ]); + // Reload if the server requests a different player + if (this._config !== JSON.stringify(getURLParams(pageUrl, ['src', 'rand'])) && !((_a = getURLParams(location === null || location === void 0 ? void 0 : location.href)) === null || _a === void 0 ? void 0 : _a.dev)) { + location.href = pageUrl; + return; + } + const platformInfo = { + videoDynamicRangeFormats: getVideoDynamicRangeFormats(params.dynamicRangeFormatSDR, params.dynamicRangeFormatHDR10, params.dynamicRangeFormatDV, params.dynamicRangeFormatHLG, params.averageBitrateCap, params.peakBitrateCap, params.clearBitrateCap), + videoCodecs: getVideoCodecs(params.videoCodecH264, params.videoCodecHEVC, params.averageBitrateCap, params.peakBitrateCap), + requiresCDMAttachOnStart: params.requiresCDMAttachOnStart, + maxHdcpLevel: params.maxHdcpLevel, + maxSecurityLevel: params.maxSecurityLevel, + }; + const storebag_url = 'https://mediaservices.cdn-apple.com/store_bags/hlsjs_qa/v1/rtc_storebag.json'; + const reportingStorebag = new rtc.exports.RTCStorebag.RTCReportingStoreBag(storebag_url, 'HLSJSDemoPlayer', 'com.apple.hlsjs.demo', 'DemoPage', '', 1, 0); + const reportingAgent = new rtc.exports.RTCReportingAgent({ + sender: 'HLSJS', + _clientName: 'HLSJSDemoPlayer', + _serviceName: 'com.apple.hlsjs.demo', + reportingStoreBag: reportingStorebag, + applicationName: 'DemoPage', + }); + const copyOptions = Object.assign({ appData: { clientName: 'HLSJSDemoPlayer', serviceName: 'com.apple.hlsjs.demo', appName: 'DemoPage', reportingAgent: reportingAgent }, userInfo: { internalBuild: true }, platformInfo }, options); + this._hls.loadSource(data.url, copyOptions, initialSeekTime); + } + /** + * Start playback if video is not detached + */ + play() { + this._hls.desiredRate = 1; + } + /** + * Set the scrubbing speed + * + * @param {number|string} rate + */ + playbackRate(rate) { + this._hls.setRate(rate); + } + /** + * Pause playback + */ + pause() { + this._hls.desiredRate = 0; + } + /** + * Reload page + */ + reload() { + window.location.reload(); + } + /** + * Seek to a time value + * + * @param {number} time - Seek position + */ + seekTo(time) { + this._hls.seekTo = time; + } + /** + * Seek to a date + * + * @param {Date} date - Seek position + */ + seekToDate(date) { + this._hls.seekToDate(date); + } + /** + * Set the current audio + * + * @param {string} lang (ex: French) + */ + setAudio(lang) { + const mediaOption = this._playbackData.audio.find((option) => { + return option.MediaSelectionOptionsExtendedLanguageTag === lang || option.MediaSelectionOptionsName === lang; + }) || {}; + this._hls.audioSelectedPersistentID = mediaOption.MediaSelectionOptionsPersistentID; + } + /** + * Set the current caption given a language + * + * @param {string} lang (ex: fr) + */ + setCaption(lang) { + const mediaOption = this._playbackData.subtitles.find((option) => { + return option.MediaSelectionOptionsExtendedLanguageTag === lang || option.MediaSelectionOptionsName === lang; + }) || {}; + this._hls.subtitleSelectedPersistentID = mediaOption.MediaSelectionOptionsPersistentID; + } + /** + * Start a timer tied to the lifecycle of this class + * that will request a cpu value from the server + * + * @param schedule The periodicity of the timer + * @public + */ + setTimerCpu(schedule = 2000) { + if (this._timers$.cpu) + return; + this._timers$.cpu = timer(200, schedule) + .pipe(tap(() => { + this.triggerSocketCommand('cpu'); + }), takeUntil(this.onStop$)) + .subscribe(); + return this._timers$.cpu; + } + /** + * Start a timer tied to the lifecycle of this class + * that will request the JS memory + * + * @param schedule The periodicity of the timer + * @public + */ + setTimerJSMemory(schedule = 2000) { + if (this._timers$.memory) + return; + this._timers$.memory = timer(200, schedule) + .pipe(tap(() => { + const { jsHeapSizeLimit, totalJSHeapSize, usedJSHeapSize } = getJSMemory(); + this._logger.qe({ critical: true, name: 'memory', data: { jsHeapSizeLimit, totalJSHeapSize, usedJSHeapSize } }); + }), takeUntil(this.onStop$)) + .subscribe(); + return this._timers$.memory; + } + /** + * Start a timer tied to the lifecycle of this class + * that will request the system memory (RAM) value from the server + * + * @param schedule The periodicity of the timer + * @public + */ + setTimerSystemMemory(schedule = 2000) { + if (this._timers$.system) + return; + this._timers$.system = timer(200, schedule) + .pipe(tap(() => { + this.triggerSocketCommand('free-memory'); + }), takeUntil(this.onStop$)) + .subscribe(); + return this._timers$.system; + } + /** + * Trigger onStop observable + * + * @return {void} + */ + stop() { + if (!this._hls) + return; + this.onStop$.next(); + } + } + let remote; + /** + * Get the JS memory if supported by the browser, otherwise return defaults + * + * @return {object} + */ + const getJSMemory = () => { + const defaultMemory = { + usedJSHeapSize: 0, + jsHeapSizeLimit: 0, + }; + const performance = window.performance || {}; + return performance.memory || defaultMemory; + }; + /** + * Create an automation instance singleton. + * + * @param socketurl The socket server URL + * @param socketid The socket identifier + */ + function initialize(socketurl, socketid) { + if (socketurl && socketid) { + if (!remote) { + remote = new AutomationRemote(); + remote.initializeSocketService(socketurl, socketid); + } + return remote; + } + } + /** + * Set the hls instance on the automation remote + */ + function setHls(hls) { + remote === null || remote === void 0 ? void 0 : remote.setHls(hls); + } + /** + * Set the logger instance + */ + function setLogger(logger) { + remote === null || remote === void 0 ? void 0 : remote.setLogger(logger); + } + + var VideoCodecRank; + (function (VideoCodecRank) { + VideoCodecRank[VideoCodecRank["DOVI"] = 4] = "DOVI"; + VideoCodecRank[VideoCodecRank["HEVC"] = 3] = "HEVC"; + VideoCodecRank[VideoCodecRank["VP09"] = 2] = "VP09"; + VideoCodecRank[VideoCodecRank["AVC"] = 1] = "AVC"; + VideoCodecRank[VideoCodecRank["UNKNOWN"] = 0] = "UNKNOWN"; + })(VideoCodecRank || (VideoCodecRank = {})); + var VideoRangeRank; + (function (VideoRangeRank) { + VideoRangeRank[VideoRangeRank["PQ"] = 3] = "PQ"; + VideoRangeRank[VideoRangeRank["HLG"] = 2] = "HLG"; + VideoRangeRank[VideoRangeRank["SDR"] = 1] = "SDR"; + VideoRangeRank[VideoRangeRank["UNKNOWN"] = 0] = "UNKNOWN"; + })(VideoRangeRank || (VideoRangeRank = {})); + var AudioCodecRank; + (function (AudioCodecRank) { + AudioCodecRank[AudioCodecRank["ALAC"] = 7] = "ALAC"; + AudioCodecRank[AudioCodecRank["FLAC"] = 6] = "FLAC"; + AudioCodecRank[AudioCodecRank["EC3"] = 5] = "EC3"; + AudioCodecRank[AudioCodecRank["AC3"] = 4] = "AC3"; + AudioCodecRank[AudioCodecRank["XHEAAC"] = 3] = "XHEAAC"; + AudioCodecRank[AudioCodecRank["AAC"] = 2] = "AAC"; + AudioCodecRank[AudioCodecRank["MP3"] = 1] = "MP3"; + AudioCodecRank[AudioCodecRank["UNKNOWN"] = 0] = "UNKNOWN"; + })(AudioCodecRank || (AudioCodecRank = {})); + var MatchRanking; + (function (MatchRanking) { + MatchRanking[MatchRanking["VALID"] = 1] = "VALID"; + MatchRanking[MatchRanking["INVALID"] = 0] = "INVALID"; + })(MatchRanking || (MatchRanking = {})); + + + // List of privacy allowed config headers for ServerInfo + // Headers need to be approved by Privacy. Here is a sample radar on how to request approval + // rdar://78172175 + const privacyAllowedLoadConfigHeaders = ['via', 'x-apple-request-uuid']; + const defaultManifestLoadPolicy = () => ({ + default: { + // Overall request configs + maxTimeToFirstByteMs: 10000, + maxLoadTimeMs: 20000, + autoRetry: false, + // Retries: + timeoutRetry: { + maxNumRetry: 2, + retryDelayMs: 0, + maxRetryDelayMs: 0, // Maximum delay between retries + }, + errorRetry: { + maxNumRetry: 1, + retryDelayMs: 1000, + maxRetryDelayMs: 8000, // Maximum delay between retries + }, + }, + customURL: { + // Overall request configs + maxTimeToFirstByteMs: 10000, + maxLoadTimeMs: 10000, + autoRetry: false, + // Retries: + timeoutRetry: { + maxNumRetry: 2, + retryDelayMs: 0, + maxRetryDelayMs: 0, // Maximum delay between retries + }, + errorRetry: { + maxNumRetry: 1, + retryDelayMs: 1000, + maxRetryDelayMs: 8000, // Maximum delay between retries + }, + }, + }); + const fragTimeoutRetryDefaultConfig = { + // Timeout Retries default value + maxNumRetry: 4, + retryDelayMs: 0, + maxRetryDelayMs: 0, // Maximum delay between retries + }; + const fragErrorRetryDefaultConfig = { + // Error Retries default value + maxNumRetry: 6, + retryDelayMs: 1000, + maxRetryDelayMs: 8000, // Maximum delay between retries + }; + const defaultSessionDataAutoLoad = { + 'com.apple.hls.chapters': true, + }; + const defaultCertRetry = { + maxNumRetry: 0, + retryDelayMs: 0, + maxRetryDelayMs: 0, // Maximum delay between retries + }; + const defaultCertLoadPolicy = { + default: { + maxTimeToFirstByteMs: 5000, + maxLoadTimeMs: 20000, + autoRetry: false, + timeoutRetry: defaultCertRetry, + errorRetry: defaultCertRetry, + }, + customURL: { + maxTimeToFirstByteMs: 10000, + maxLoadTimeMs: 20000, + autoRetry: false, + timeoutRetry: defaultCertRetry, + errorRetry: defaultCertRetry, + }, + }; + // max overall load time should be ~30s ish + const defaultKeyRetry = { + maxNumRetry: 8, + retryDelayMs: 1000, + maxRetryDelayMs: 20000, + backoff: 'linear', + }; + const timeoutKeyRetry = Object.assign(Object.assign({}, defaultKeyRetry), { maxNumRetry: 1 }); + const defaultKeyLoadPolicy = { + default: { + maxTimeToFirstByteMs: 5000, + maxLoadTimeMs: 20000, + autoRetry: false, + timeoutRetry: timeoutKeyRetry, + errorRetry: defaultKeyRetry, + }, + customURL: { + maxTimeToFirstByteMs: 10000, + maxLoadTimeMs: 20000, + autoRetry: false, + timeoutRetry: timeoutKeyRetry, + errorRetry: defaultKeyRetry, + }, + }; + const defaultTrickPlaybackConfig = { + enabled: true, + minIframeDuration: 8, + }; + const hlsDefaultConfig = { + autoStartLoad: true, + startPosition: NaN, + defaultAudioCodec: void 0, + defaultVideoCodec: void 0, + debug: false, + debugLevel: 'info', + buildType: void 0, + minFramesBeforeSwitchingLevel: 11, + minTargetDurations: 3, + maxBufferLength: 60, + maxBufferHole: 0.5, + maxSeekHole: 2, + nudgeFromEventSeek: true, + jaggedSeekTolerance: 0, + discontinuitySeekTolerance: 2, + bufferedSegmentEjectionToleranceMs: 0.5, + almostDryBufferSec: 0.5, + maxTotalDurationTolerance: 0.1, + lowBufferThreshold: 0.5, + lowBufferWatchdogPeriod: 0.5, + highBufferWatchdogPeriod: 3, + seekWatchdogPeriod: 5, + nudgeOffset: 0.1, + nudgeMaxRetry: 3, + maxFragLookUpTolerance: 0.2, + initialLiveManifestSize: 1, + liveSyncDurationCount: 3, + liveMaxLatencyDurationCount: Infinity, + liveSyncDuration: void 0, + liveMaxLatencyDuration: void 0, + liveFlushExpiredFrags: true, + liveMaxUnchangedPlaylistRefresh: 3, + liveEdgeForZeroStartPositon: false, + livePlaylistUpdateStaleness: 2, + livePlaylistDurationNudge: 0.001, + allowFastSwitchUp: false, + minMatchGroupDuration: 5, + desiredIframeFPS: 8, + initialIframeFPS: 6, + minRemainingTimeInMediaPipeline: 3, + leftMediaTimeToAutoPause: 10, + startTargetDurationFactor: 0.9, + minRequiredStartDuration: 4, + maxRequiredStartDuration: 15, + enableWorker: true, + enableWebCrypto: true, + keySystemPreference: void 0, + useMultipleKeySessions: false, + enablePlayReadyKeySystem: false, + useMediaKeySystemAccessFilter: false, + playReadyMessageFormat: 'utf16', + startLevel: void 0, + livePlaylistRefreshDelay: 2500, + liveMinPlayingBufferLen: 5, + enableIFramePreloading: true, + useMediaCapabilities: false, + enableID3Cues: true, + certLoadPolicy: defaultCertLoadPolicy, + keyLoadPolicy: defaultKeyLoadPolicy, + manifestLoadPolicy: defaultManifestLoadPolicy(), + trickPlaybackConfig: defaultTrickPlaybackConfig, + playlistLoadPolicy: { + default: { + // Overall request configs + maxTimeToFirstByteMs: 10000, + maxLoadTimeMs: 20000, + autoRetry: false, + // Retries: + timeoutRetry: { + maxNumRetry: 2, + retryDelayMs: 0, + maxRetryDelayMs: 0, // Maximum delay between retries + }, + errorRetry: { + maxNumRetry: 2, + retryDelayMs: 1000, + maxRetryDelayMs: 8000, // Maximum delay between retries + }, + }, + customURL: { + // Overall request configs + maxTimeToFirstByteMs: 10000, + maxLoadTimeMs: 10000, + autoRetry: false, + // Retries: + timeoutRetry: { + maxNumRetry: 2, + retryDelayMs: 0, + maxRetryDelayMs: 0, // Maximum delay between retries + }, + errorRetry: { + maxNumRetry: 2, + retryDelayMs: 1000, + maxRetryDelayMs: 8000, // Maximum delay between retries + }, + }, + }, + fragLoadPolicy: { + default: { + // Overall request configs + maxTimeToFirstByteMs: 5000, + maxLoadTimeMs: 20000, + autoRetry: false, + // Retries: + timeoutRetry: fragTimeoutRetryDefaultConfig, + errorRetry: fragErrorRetryDefaultConfig, + forceContentLenCheckIfNoHeader: false, + reportCDNServer: true, + }, + customURL: { + // Overall request configs + maxTimeToFirstByteMs: 10000, + maxLoadTimeMs: 20000, + autoRetry: false, + // Retries: + timeoutRetry: fragTimeoutRetryDefaultConfig, + errorRetry: fragErrorRetryDefaultConfig, + reportCDNServer: true, + }, + }, + steeringManifestLoadPolicy: defaultManifestLoadPolicy(), + maxNumAddLevelToPenaltyBox: 4, + firstAudioMustOverlapVideoStart: false, + keyMinHoldTimeBeforeCleanup: 15000, + startFragPrefetch: false, + appendErrorMaxRetry: 3, + alwaysResetOnNewCC: false, + // loader: XhrLoader, // deprecated in favor of rxjs fetch + // loader: FetchLoader, // deprecated in favor of rxjs fetch + fLoader: void 0, + pLoader: void 0, + xhrSetup: void 0, + // fetchSetup: undefined, + // abrController: AbrController, // deprecated in favor or redux/rxjs + // bufferController: BufferController, // deprecated in favor or redux/rxjs + // #if altaudio + // audioStreamController: AudioStreamController, // deprecated in favor or redux/rxjs + // audioTrackController: AudioTrackController, // deprecated in favor or redux/rxjs + iframeMaxExitSeekDuration: 2000, + iframeStallMaxRetry: 5, + audioPrimingDelay: 0, + // #endif + // #if subtitle + // subtitleStreamController: SubtitleStreamController, // deprecated in favor or redux/rxjs + // subtitleTrackController: SubtitleTrackController, // deprecated in favor or redux/rxjs + // timelineController: TimelineController, // deprecated in favor or redux/rxjs + enableCEA708Captions: true, + customTextTrackCueRenderer: false, + enableWebVTT: true, + captionsTextTrack1Label: 'English', + captionsTextTrack1LanguageCode: 'en', + captionsTextTrack2Label: 'Spanish', + captionsTextTrack2LanguageCode: 'es', + enableDualTrackSelection: false, + condenseSubtitleTrack: false, + nativeTextTrackChangeHandling: true, + earlyFragTolerance: 7, + vttConcurrentLoadCount: 1, + trottleCheckInterval: 2000, + subtitleLeadTime: 30, + lateTolerance: 2, + // #endif + stretchShortVideoTrack: false, + forceKeyFrameOnDiscontinuity: true, + useFirstLevelAtIncompatDiscontinuity: true, + abrBandwidthEstimator: 'bandwidth-history-controller', + abrEwmaDefaultEstimate: 500000, + abrDefaultEstimate: 500000, + abrBandWidthFactor: 0.95, + abrBandWidthUpFactor: 0.9, + abrMaxWithRealBitrate: false, + maxStarvationDelay: 4, + maxLoadingDelay: 4, + minAutoBitrate: 0, + enableRtcReporting: false, + rtcIntervalTimeout: 300000, + rtcSender: 'HLSJS', + rtcSessionTag: 'none', + useHTTPPlaybackSessionId: false, + warmupCdms: false, + enablePerformanceLogging: false, + overridePlaybackRate: false, + nativeControlsEnabled: false, + useCustomMediaFunctions: true, + seekEventThrottleMs: 150, + enableAdaptiveStartup: true, + bandwidthHistoryWindowSize: 120000, + bandwidthHistoryTTL: 600000, + bandwidthHistoryAggregationMethod: 'quadratic-time-weighted', + bandwidthHistoryGetEstimateThrottleMs: 1000, + defaultTargetDuration: 10, + targetStartupMs: 4000, + adaptiveStartupMetricsOverride: { + maxValidHeight: 1080, + maxValidBitrate: Infinity, + maxPreferredBitrate: Infinity, // the purpose of `maxPreferredBitrate` is to limit startup time. In adaptive startup we use `targetStartupMs` instead. + }, + bandwidthHistoryStorageKey: 'AppleHLS-bandwidth-estimation', + storageKeyPrefix: 'AppleHLS-', + storage: { + get: typeof localStorage === 'undefined' ? undefined : localStorage.getItem.bind(localStorage), + set: typeof localStorage === 'undefined' ? undefined : localStorage.setItem.bind(localStorage), + }, + minFragmentCount: 10, + minPlaylistCount: 5, + enableCDNFallback: true, + enableQueryParamsForITunes: false, + gapless: false, + useViewportSizeForLevelCap: false, + statDefaults: { + playlistLoadTimeMs: 500, + playlistParseTimeMs: 50, + fragParseTimeMs: 50, + fragBufferCreationDelayMs: 200, + dataFragAppendMs: 50, + initFragAppendMs: 50, + }, + disableVideoCodecList: new Set([ + // codecs that will be ignored during playback + ]), + disableAudioCodecList: new Set([AudioCodecRank.ALAC, AudioCodecRank.FLAC, AudioCodecRank.XHEAAC]), + useHighestVideoCodecPrivate: true, + sessionDataAutoLoad: defaultSessionDataAutoLoad, // Attributes listed here will be auto-fetched if its URI is given. + }; + + const NoMediaOptionKey = { itemId: 'Nah', mediaOptionId: 'Nah' }; + const NoMediaOption = Object.assign(Object.assign({}, NoMediaOptionKey), { mediaOptionType: undefined }); + const isEnabledMediaOption = (mediaOptionInfo) => { + const { itemId, mediaOptionId } = mediaOptionInfo; + return itemId !== 'Nah' && mediaOptionId !== 'Nah'; + }; + const mediaOptionKeyEquals = (firstMediaOption, secondMediaOption) => { + return firstMediaOption.itemId === secondMediaOption.itemId && firstMediaOption.mediaOptionId === secondMediaOption.mediaOptionId; + }; + var MediaOptionType; + (function (MediaOptionType) { + MediaOptionType[MediaOptionType["Variant"] = 0] = "Variant"; + MediaOptionType[MediaOptionType["AltAudio"] = 1] = "AltAudio"; + MediaOptionType[MediaOptionType["Subtitle"] = 2] = "Subtitle"; + })(MediaOptionType || (MediaOptionType = {})); + const MediaOptionNames = ['variant', 'altAudio', 'subtitle']; + const MediaOptionTypes = [MediaOptionType.Variant, MediaOptionType.AltAudio, MediaOptionType.Subtitle]; + const AVMediaOptionTypes = [MediaOptionType.Variant, MediaOptionType.AltAudio]; + + var SourceBufferType; + (function (SourceBufferType) { + SourceBufferType[SourceBufferType["Variant"] = 0] = "Variant"; + SourceBufferType[SourceBufferType["AltAudio"] = 1] = "AltAudio"; + })(SourceBufferType || (SourceBufferType = {})); + const SourceBufferNames = ['variant', 'altAudio']; + function mediaOptionTypeToSourceBufferType(optionType) { + switch (optionType) { + case MediaOptionType.Variant: + return SourceBufferType.Variant; + case MediaOptionType.AltAudio: + return SourceBufferType.AltAudio; + default: + return null; + } + } + function sourceBufferTypeToMediaOptionType(sbType) { + return sbType === SourceBufferType.Variant ? MediaOptionType.Variant : MediaOptionType.AltAudio; + } + function initSegmentEquals(a, b) { + const equals = a && b && a.itemId === b.itemId && a.mediaOptionId === b.mediaOptionId && a.discoSeqNum === b.discoSeqNum && a.keyId === b.keyId; + return equals; + } + + var Allowed; + (function (Allowed) { + Allowed[Allowed["NO"] = 0] = "NO"; + Allowed[Allowed["YES"] = 1] = "YES"; + })(Allowed || (Allowed = {})); + + var MediaTypeFourCC; + (function (MediaTypeFourCC) { + MediaTypeFourCC["UNKNOWN"] = "unkn"; + MediaTypeFourCC["VIDEO"] = "vide"; + MediaTypeFourCC["AUDIO"] = "soun"; + MediaTypeFourCC["SUBTITLE"] = "sbtl"; + MediaTypeFourCC["CLOSEDCAPTION"] = "clcp"; + })(MediaTypeFourCC || (MediaTypeFourCC = {})); + + + const BinarySearch = { + /** + * Searches for an item in an array which matches a certain condition. + * This requires the condition to only match one item in the array, + * and for the array to be ordered. + * + * @param {Array} list The array to search. + * @param {Function} comparisonFunction + * Called and provided a candidate item as the first argument. + * Should return: + * > -1 if the item should be located at a lower index than the provided item. + * > 1 if the item should be located at a higher index than the provided item. + * > 0 if the item is the item you're looking for. + * + * @return {*} The object if it is found or undefined otherwise. + */ + search: function (list, comparisonFunction) { + let minIndex = 0; + let maxIndex = (list === null || list === void 0 ? void 0 : list.length) - 1; + let currentIndex; + let currentElement; + while (minIndex <= maxIndex) { + currentIndex = ((minIndex + maxIndex) / 2) | 0; + currentElement = list[currentIndex]; + const comparisonResult = comparisonFunction(currentElement); + if (comparisonResult > 0) { + minIndex = currentIndex + 1; + } + else if (comparisonResult < 0) { + maxIndex = currentIndex - 1; + } + else { + return currentElement; + } + } + return null; + }, + }; + var BinarySearch$1 = BinarySearch; + + /** + * Fragment finder utils, providing methods for determining next fragment to fetch + * + * + * + * + */ + function fragTag(frag, level) { + return `sn/cc/level: ${frag.mediaSeqNum}/${frag.discoSeqNum}/${level}`; + } + function rangeString(frag) { + let result = 'N/A'; + if (frag.start >= 0 && frag.duration >= 0) { + result = `${frag.start.toFixed(2)}-${(frag.start + frag.duration).toFixed(2)}`; + } + return result; + } + const FragmentFetchHelper = { + /** + * Finds a fragment based on the SN of the previous fragment and current buffer. + * @param fragPrevious - The last frag successfully appended + * @param fragments - The array of fragments in level + * @param bufferEnd - The end of the contiguous buffer range within which playhead is + * @param end - End time of the level + * @param maxFragLookUpTolerance - acceptable tolerance limit + */ + findFragmentBySNAndBuffer: function (fragPrevious, fragments, bufferEnd = 0, end = 0, maxFragLookUpTolerance = 0) { + let foundFrag; + const fragNext = fragPrevious ? fragments[fragPrevious.mediaSeqNum - fragments[0].mediaSeqNum + 1] : null; + if (bufferEnd < end) { + if (bufferEnd > end - maxFragLookUpTolerance) { + maxFragLookUpTolerance = 0; + } + // Prefer the next fragment if it's within tolerance + if (fragNext && !this.fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, fragNext)) { + foundFrag = fragNext; + } + else { + foundFrag = BinarySearch.search(fragments, this.fragmentWithinToleranceTest.bind(null, bufferEnd, maxFragLookUpTolerance)); + } + } + else { + // reach end of playlist + foundFrag = fragments[fragments.length - 1]; + } + return foundFrag; + }, + fragmentWithinToleranceTest: function (bufferEnd, maxFragLookUpTolerance, candidate) { + // offset should be within fragment boundary - config.maxFragLookUpTolerance + // this is to cope with situations like + // bufferEnd = 9.991 + // frag[Ø] : [0,10] + // frag[1] : [10,20] + // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here + // frag start frag start+duration + // |-----------------------------| + // <---> <---> + // ...--------><-----------------------------><---------.... + // previous frag matching fragment next frag + // return -1 return 0 return 1 + // logger.info(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`); + // Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments + const candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration); + if (candidate.start + candidate.duration - candidateLookupTolerance <= bufferEnd) { + return 1; + } + else if (candidate.start - candidateLookupTolerance > bufferEnd && candidate.start) { + // if maxFragLookUpTolerance will have negative value then don't return -1 for first element + return -1; + } + return 0; + }, + }; + var FragmentFetchHelper$1 = FragmentFetchHelper; + + const loggerName$9 = { name: 'disco' }; + const DiscoHelper = { + startFragmentInCC: function (fragments, cc, candidate) { + let rank = cc - candidate.discoSeqNum; + if (rank === 0) { + const fragPrevious = fragments[candidate.mediaSeqNum - fragments[0].mediaSeqNum - 1]; + rank = fragPrevious && fragPrevious.discoSeqNum === candidate.discoSeqNum ? -1 : 0; + } + return rank; + }, + endFragmentInCC: function (fragments, cc, candidate) { + let rank = cc - candidate.discoSeqNum; + if (rank === 0) { + const fragNext = fragments[candidate.mediaSeqNum - fragments[0].mediaSeqNum + 1]; + rank = fragNext && fragNext.discoSeqNum === candidate.discoSeqNum ? 1 : 0; + } + return rank; + }, + findStartEndFragmentsInCC: function (fragments, cc, logger) { + let startFrag; + let endFrag; + if ((fragments === null || fragments === void 0 ? void 0 : fragments.length) > 0 && isFiniteNumber(cc)) { + startFrag = BinarySearch.search(fragments, DiscoHelper.startFragmentInCC.bind(null, fragments, cc)); + endFrag = BinarySearch.search(fragments, DiscoHelper.endFragmentInCC.bind(null, fragments, cc)); + } + if (!endFrag) { + logger.info(loggerName$9, `cc ${cc} does not appear in fragments anymore`); + } + // handle bogus results due to undefined cc + if (startFrag && !isFiniteNumber(startFrag.discoSeqNum)) { + logger.info(loggerName$9, `findStartEndFragmentsInCC startFrag ${startFrag.mediaSeqNum} has undefined cc ${cc}`); + startFrag = undefined; + } + if (endFrag && !isFiniteNumber(endFrag.discoSeqNum)) { + logger.info(loggerName$9, `findStartEndFragmentsInCC endFrag ${endFrag.mediaSeqNum} has undefined cc ${cc}`); + endFrag = undefined; + } + return { startFrag, endFrag }; + }, + getTimeRangeDictForCC: function (fragments, cc, logger) { + const ccFragRange = DiscoHelper.findStartEndFragmentsInCC(fragments, cc, logger); + const { startFrag, endFrag } = ccFragRange; + const start = startFrag.start; + const end = endFrag.start + endFrag.duration; + return { start, end }; + }, + getTimeRangeForCC: function (fragments, cc, logger) { + const ccFragRange = DiscoHelper.findStartEndFragmentsInCC(fragments, cc, logger); + const { startFrag, endFrag } = ccFragRange; + let ccTimeRange = []; + if (startFrag && endFrag) { + ccTimeRange = [startFrag.start, endFrag.start + endFrag.duration]; + } + return ccTimeRange; + }, + snapToCCTimeRange: function (targetTime, fragments, cc, logger) { + const ccRange = DiscoHelper.getTimeRangeForCC(fragments, cc, logger); + if (!(ccRange === null || ccRange === void 0 ? void 0 : ccRange.length)) { + return targetTime; + } + return Math.min(ccRange[1], Math.max(ccRange[0], targetTime)); + }, + getMinTimeForCC(mainFragments, audioFragments, cc, logger) { + if (!(mainFragments === null || mainFragments === void 0 ? void 0 : mainFragments.length)) { + return 0; + } + // const fragments = this.variantManager.getVariantInfoIfValid(this.currentVariantId)?.details?.fragments; + let startCCTime = 0; + if (isFiniteNumber(cc) && mainFragments) { + const ccRange = DiscoHelper.getTimeRangeForCC(mainFragments, cc, logger); + if (ccRange) { + const audioCCRange = DiscoHelper.getTimeRangeForCC(audioFragments, cc, logger); // this.audioStreamController?.getAudioTimeRangeForCC(cc); + if (audioCCRange === null || audioCCRange === void 0 ? void 0 : audioCCRange.length) { + startCCTime = Math.max(audioCCRange[0], ccRange[0]); + } + else { + startCCTime = ccRange[0]; + } + } + } + return startCCTime; + }, + discoSeqNumForTime(fragments, pos, tolerance = 0) { + const foundFrag = BinarySearch.search(fragments, FragmentFetchHelper$1.fragmentWithinToleranceTest.bind(null, pos, tolerance)); + return foundFrag === null || foundFrag === void 0 ? void 0 : foundFrag.discoSeqNum; + }, + }; + var DiscoHelper$1 = DiscoHelper; + + const VOID = of(void 0); + /** + * Wait for some condition to be true on an observable + * @param source Source observable + * @param condition Condition to apply + * @param times How many times to allow observable to fire before completing. By default, 1 + */ + function waitFor(source, condition, times = 1) { + return source.pipe(filter(condition), take(times)); + } + + /* + * Simple clock abstraction to help transform between clocks and their timelines + * + * 2020 Apple Inc. All rights reserved. + */ + /** + * Convert a SyncTimelineValue into a different timeline + * + * @param sourceValue The value to map to a different timeline + * @param destTimeline The timeline to map into + * + * @returns A new SyncTimelineValue in destTimeline + */ + function mapValueToTimeline(sourceValue, destTimeline) { + const sourceTimeline = sourceValue.timeline; + const newValue = ((sourceValue.seconds - sourceTimeline.rootTimeSeconds) / sourceTimeline.rate) * destTimeline.rate + destTimeline.rootTimeSeconds; + return { seconds: newValue, timeline: destTimeline }; + } + + class IframeClock { + constructor() { + this._timeline = null; + } + get forward() { + var _a; + return ((_a = this.timeline) === null || _a === void 0 ? void 0 : _a.rate) > 0; + } + get isStarted() { + return Boolean(this.hostClock); + } + start(timeline) { + this.stopTime = null; + this._timeline = Object.assign({}, timeline); + const performanceTimeline = { rootTimeSeconds: performance.now() / 1000, rate: 1 }; + this.hostClock = { + timeline: performanceTimeline, + getCurrentTime: () => { + return { seconds: performance.now() / 1000, timeline: performanceTimeline }; + }, + }; + } + pause() { + if (this.isStarted) { + this.stopTime = this.getCurrentTime(); + } + } + stop() { + this.pause(); + this.hostClock = null; + this._timeline = null; + } + // ClockSyncAdapter interface + get timeline() { + return this._timeline; + } + getCurrentTime() { + if (this.stopTime) { + return this.stopTime; + } + // map the performance clock into the iframe timeline + const hostValue = this.hostClock.getCurrentTime(); + const myValue = mapValueToTimeline(hostValue, this.timeline); + return myValue; + } + } + + /** + * This is a view into our media element we're appending into. We don't care about any time other + * than the buffer end, so that's our time source + */ + class MediaAppendClock { + constructor(_timeline) { + this._timeline = _timeline; + // our start time is our current buffer end + this.mediaRootTime = this.lastTimeSeconds = _timeline.rootTimeSeconds; + } + // ClockSyncAdapter interface + get timeline() { + return Object.assign({}, this._timeline); + } + getCurrentTime() { + // the latest time we know about + return { seconds: this.lastTimeSeconds, timeline: Object.assign({}, this._timeline) }; + } + } + + /* + * Trickplay implementation + * + * 2020 Apple Inc. All rights reserved. + */ + const loggerName$8 = { name: 'ifm' }; + /** + * + * A sort of 'controller' for iframes + * + * Managages two clocks: + * The iframe clock has a real time host and is untied to the media progression + * The media append clock represents the media element, where we are actually appending the iframes + * + * The responsibility of this class is to supply the iframes fragments via nextFragment so that + * the media append clock keeps pace with the iframe clock (no drift) + * + */ + class IframeMachine { + constructor(config, logger) { + this.config = config; + this.logger = logger; + // Fragments we've modified and need to cleanup + this.scaledFragments = []; + // this is the 'iframe clock', running at the iframe rate + // with a timebase anchored at the time trickplay startart + this.iframeClock = new IframeClock(); + this.hasMore$ = new BehaviorSubject(true); + this.logger = logger.child(loggerName$8); + } + destroy() { + this.stop(); + } + resetScaledSegments() { + this.scaledFragments = []; + } + get anchorFrag() { + return this.scaledFragments.length ? this.scaledFragments[0] : null; + } + get lastFrag() { + return this.scaledFragments.length ? this.scaledFragments.slice(-1)[0] : null; + } + get isStarted() { + return Boolean(this.iframeClock.isStarted); + } + get iframeRate() { + var _a, _b; + return (_b = (_a = this.iframeClock.timeline) === null || _a === void 0 ? void 0 : _a.rate) !== null && _b !== void 0 ? _b : 0; + } + get iframeClockTimeSeconds() { + return Math.max(0, this.iframeClock.getCurrentTime().seconds); + } + get mediaAppendClockTimeSeconds() { + var _a, _b; + return (_b = (_a = this.mediaAppendClock) === null || _a === void 0 ? void 0 : _a.getCurrentTime().seconds) !== null && _b !== void 0 ? _b : 0; + } + get mediaRootTime() { + var _a; + return (_a = this.mediaAppendClock) === null || _a === void 0 ? void 0 : _a.mediaRootTime; + } + pause() { + this.iframeClock.pause(); + } + stop() { + this.logger.debug('stop'); + this.hasMore$.next(true); + this.iframeClock.stop(); + this.mediaAppendClock = null; + this.resetScaledSegments(); + } + checkHasMore() { + this.hasMore$.next(true); + } + /** + * Re-anchor the iframe clock + * @param iframeFragments + * @param audioFragments + * @param rate + * @param pos + */ + startClocksAndGetFirstFragment(iframeFragments, audioFragments, rate, pos, disco) { + this.logger.info(`startClocksAndGetFirstFragment: rate(${rate}), pos(${pos})`); + let anchorFrag = BinarySearch.search(iframeFragments, FragmentFetchHelper$1.fragmentWithinToleranceTest.bind(null, pos, 0.0001)); + if (!anchorFrag && pos >= iframeFragments[iframeFragments.length - 1].start) { + this.logger.info(`startClocksAndGetFirstFragment: pick last frag, start=${iframeFragments[iframeFragments.length - 1].start}`); + anchorFrag = iframeFragments[iframeFragments.length - 1]; + } + if (!anchorFrag) { + this.logger.error(`startClocksAndGetFirstFragment => no anchorFrag for time ${pos}`); + this.hasMore$.next(false); + return null; + } + const nextDisco = isFiniteNumber(disco) ? disco : anchorFrag.discoSeqNum; + const startCCTime = DiscoHelper$1.getMinTimeForCC(iframeFragments, audioFragments, nextDisco, this.logger); + const newPosition = isFiniteNumber(disco) && rate > 1 ? startCCTime : pos; + let newMediaRootTime; + if (rate > 1) { + // fast forward should always have enough to anchor at the current point + newMediaRootTime = Math.ceil(newPosition); + } + else { + newMediaRootTime = Math.ceil(startCCTime); + } + this.iframeClock.start({ rootTimeSeconds: newPosition, rate }); + this.mediaAppendClock = new MediaAppendClock({ rootTimeSeconds: newMediaRootTime, rate: 1 }); + return { frag: anchorFrag, newMediaRootTime }; + } + getNextFragment(fragments, bufferEnd, rate) { + const lastFrag = this.lastFrag; + const { iframeClock, mediaAppendClock } = this; + const iframeTimeline = iframeClock.timeline; + mediaAppendClock.lastTimeSeconds = bufferEnd; + this.logger.qe({ + critical: true, + name: 'iframes', + data: { maxMediaTime: bufferEnd, rate, ifmat: this.mediaAppendClock.timeline.rootTimeSeconds, ifbt: this.iframeClock.timeline.rootTimeSeconds }, + }); + const ixStep = iframeClock.forward ? 1 : -1; + let ix = Math.max(0, Math.min(lastFrag.mediaSeqNum - fragments[0].mediaSeqNum + ixStep, fragments.length - 1)); + let nextFrag; + if (lastFrag.mediaSeqNum !== fragments[ix].mediaSeqNum) { + // always allow first step to work for EOS cleanliness + do { + nextFrag = fragments[ix]; + const seconds = iframeClock.forward ? nextFrag.start : nextFrag.start + nextFrag.duration; + const startAppendTime = mapValueToTimeline({ seconds, timeline: iframeTimeline }, mediaAppendClock.timeline); + if (startAppendTime.seconds >= bufferEnd && ((iframeClock.forward && seconds >= this.iframeClockTimeSeconds) || (!iframeClock.forward && seconds <= this.iframeClockTimeSeconds))) { + break; + } + ix += ixStep; + } while (ix > 0 && ix < fragments.length); + } + if (!nextFrag) { + // stream controller should have stopped and waited until we said so, and we should have found something new + this.logger.error(`getNextFragment(bufferEnd: ${bufferEnd}) => no more frags, but we should have found an end fragment, setting hasMore to false`); + this.hasMore$.next(false); + } + return { frag: nextFrag }; + } + /** + * Get the next fragment to fetch + * @param iframeFragments + * @param audioFragments + * @param rate + * @param pos + * @param bufferLen + * @return anchor fragment + */ + nextFragment(iframeFragments, audioFragments, rate, pos) { + let foundFrag; + let rateSwitchPos; + if (this.isStarted && rate !== this.iframeRate) { + rateSwitchPos = this.iframeClockTimeSeconds; + this.stop(); + } + if (!this.isStarted) { + foundFrag = this.startClocksAndGetFirstFragment(iframeFragments, audioFragments, rate, rateSwitchPos !== null && rateSwitchPos !== void 0 ? rateSwitchPos : pos); + if (!foundFrag) { + return; + } + } + else { + foundFrag = this.getNextFragment(iframeFragments, pos, rate); + if (!foundFrag.frag) { + return; + } + else if (foundFrag.frag.discoSeqNum !== this.anchorFrag.discoSeqNum) { + const iframeTimeSeconds = this.iframeClockTimeSeconds; + this.logger.info(`nextFragment iframeClockTime(${iframeTimeSeconds.toFixed(3)}) => found new disco, ${this.anchorFrag.discoSeqNum} to ${foundFrag.frag.discoSeqNum}`); + this.stop(); + const { newMediaRootTime } = this.startClocksAndGetFirstFragment(iframeFragments, audioFragments, rate, iframeTimeSeconds, foundFrag.frag.discoSeqNum); + foundFrag.newMediaRootTime = newMediaRootTime; + } + } + const { frag, newMediaRootTime } = foundFrag; + const updatedFrag = this.handleNextFrag(iframeFragments, frag); + const appendStart = updatedFrag.iframeMediaStart, appendEnd = appendStart + updatedFrag.iframeMediaDuration; + this.logger.info(`nextFragment iframeClockTime(${this.iframeClockTimeSeconds.toFixed(3)}) => picked frag: ${fragTag(updatedFrag)}, source range: ${rangeString(updatedFrag)}, media append times: ${appendStart.toFixed(3)}-${appendEnd.toFixed(3)}${isFiniteNumber(newMediaRootTime) ? ', newMediaRootTime: ' + newMediaRootTime.toFixed(3) : ''}`); + return Object.assign(Object.assign({}, foundFrag), { frag: updatedFrag }); + } + handleNextFrag(fragments, frag) { + const cpFrag = Object.assign(Object.assign({}, frag), { iframeMediaStart: NaN, iframeMediaDuration: NaN }); + const { mediaAppendClock, iframeClock } = this; + // update our bounds just in case we're live + this.iframeClockBounds = DiscoHelper$1.getTimeRangeDictForCC(fragments, cpFrag.discoSeqNum, this.logger); + // the start of our iframe is the latest time of the media append clock + const bufferEnd = mediaAppendClock.getCurrentTime().seconds; + const iframeRate = iframeClock.timeline.rate; + if (!isFiniteNumber(cpFrag.iframeOriginalStart)) { + // iframe mode shouldn't have mangled frag.start + // we need the original frag.start to calculate initPTS. + // stash it in frag.iframeOriginalStart + cpFrag.iframeOriginalStart = cpFrag.start; + } + cpFrag.start = cpFrag.iframeMediaStart = bufferEnd; + cpFrag.iframeMediaDuration = Math.max(cpFrag.duration / Math.abs(iframeRate), 1 / this.config.minIframeDuration); + // track which frags we've modifed - TODO find a better way to do this + this.scaledFragments.push(cpFrag); + // check for EOS + if (this.isEndFrag(fragments, cpFrag)) { + this.logger.info(`checkEOS(frag: ${cpFrag.mediaSeqNum}) => found end frag, setting hasMore to false`); + this.hasMore$.next(false); + } + return cpFrag; + } + get hasMore() { + return this.hasMore$.value; + } + // anchor isn't necessary valid in this function, so don't reference it + /** + * Wait for there to be more fragments to be downloaded + */ + handleWaitForMore() { + this.logger.trace(`handleWaitForMore(): isStarted: ${this.isStarted}`); + if (!this.isStarted) { + return VOID; + } + const currentIframeTime = this.iframeClock.getCurrentTime(); + const iframeClockTime = currentIframeTime.seconds; + const rate = currentIframeTime.timeline.rate; + const edgeTime = rate > 1 ? this.iframeClockBounds.end : this.iframeClockBounds.start; + const diff = edgeTime - currentIframeTime.seconds; + const wait = diff / rate; // the +/- work out with the rate and the diff + this.logger.info(`handleWaitForMore(): iframeClockTime: ${iframeClockTime}, edgeTime: ${edgeTime}, diff ${diff} => wait: ${wait}`); + return waitFor(this.hasMore$, (hasMore) => hasMore === true).pipe(tap(() => { + this.logger.info(`handleWaitForMore(): iframeClockTime: ${iframeClockTime} => hasMore became true while waiting`); + })); + } + isEndFrag(fragments, frag) { + const startFrag = fragments[0]; + const endFrag = fragments[fragments.length - 1]; + const ff = this.iframeClock.forward; + const rw = !ff; + return (rw && (startFrag === null || startFrag === void 0 ? void 0 : startFrag.mediaSeqNum) === frag.mediaSeqNum) || (ff && (endFrag === null || endFrag === void 0 ? void 0 : endFrag.mediaSeqNum) === frag.mediaSeqNum); + } + } + function createIframeMachine(config, logger) { + return new Observable((subscriber) => { + const iframeMachine = new IframeMachine(config, logger); + subscriber.next(iframeMachine); + return () => { + // cleanup + }; + }); + } + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + /** + * Use of this source code is governed by an MIT-style license that + * can be found in the LICENSE file at https://github.com/cartant/rxjs-spy + */ + + /*tslint:disable:no-use-before-declare*/ + function tag(tag) { + return function tagOperation(source) { + return source.lift(new TagOperator(tag)); }; - - function _g(e, t, i) { - const { - keySystemAdapter: r, - rootPlaylistQuery: n, - rootPlaylistService: s, - config: a - } = e; - return r.getKeyFromDecryptData(t, i).pipe((o = t.uri, l = Lc({ - url: t.uri - }, a.keyLoadPolicy), d = n, u = s, c = r.ksQuery, e => e.pipe(ll(() => { - ag(o, !1, d, u, c) - }), va(e => e.pipe(jr((e, t) => { - if (e instanceof mc || e instanceof gc) return og(e, t, _c(e, l), u, d, c); - throw e - })))))); - var o, l, d, u, c + } + + var TagOperator = /*#__PURE__*/function () { + // It would be better if this were a symbol. However ... + // error TS1166: A computed property name in a class property declaration must directly refer to a built-in symbol. + function TagOperator(tag) { + _classCallCheck(this, TagOperator); + + _defineProperty(this, "tag", void 0); + + this.tag = tag; } - class Ng { - constructor(e, t, i) { - this.hls = e, this.destroy$ = new Xt, this.iframeSwitchStart = 0, this.logger = t.child({ - name: "hls-player-events" - }), this.rtc = i, this.subscribeAndEmit() + + _createClass(TagOperator, [{ + key: "call", + value: function call(subscriber, source) { + return source.subscribe(subscriber); + } + }]); + + return TagOperator; + }(); + + // Key request state from a macro level (substates in KeyRequestContext) + var KeyRequestMacroState; + (function (KeyRequestMacroState) { + KeyRequestMacroState["MustRequestResponse"] = "MustRequestResponse"; + KeyRequestMacroState["WaitingForKeyResponse"] = "WaitingForKeyResponse"; + KeyRequestMacroState["GotKeyResponse"] = "GotKeyResponse"; + })(KeyRequestMacroState || (KeyRequestMacroState = {})); + // TODO convert DecryptData types to ArrayBuffer so we don't need these anymore + function keyTagInfoToEntity(keyInfo) { + var _a, _b, _c, _d, _e; + const { method, isEncrypted, uri, format, formatversions } = keyInfo; + return { + method, + isEncrypted, + uri, + format, + formatversions, + ivBuf: (_b = (_a = keyInfo.iv) === null || _a === void 0 ? void 0 : _a.buffer) !== null && _b !== void 0 ? _b : null, + keyIdBuf: (_c = keyInfo.keyId) === null || _c === void 0 ? void 0 : _c.buffer, + keyBuf: (_d = keyInfo.key) === null || _d === void 0 ? void 0 : _d.buffer, + psshBuf: (_e = keyInfo.key) === null || _e === void 0 ? void 0 : _e.buffer, + }; + } + function entityToKeyTagInfo(entity) { + const { method, isEncrypted, uri, format, formatversions } = entity; + const keyTagInfo = { + method, + isEncrypted, + uri, + format, + formatversions, + iv: entity.ivBuf ? new Uint8Array(entity.ivBuf) : null, + }; + if (entity.keyIdBuf) { + keyTagInfo.keyId = new Uint8Array(entity.keyIdBuf); + } + if (entity.keyBuf) { + keyTagInfo.key = new Uint8Array(entity.keyBuf); + } + if (entity.psshBuf) { + keyTagInfo.pssh = new Uint8Array(entity.psshBuf); + } + return keyTagInfo; + } + + // Generic network error + class GenericNetworkError extends Error { + constructor(message, code) { + super(message); + this.code = code; + } + } + class HlsNetworkError extends HlsError { + constructor(details, fatal, reason, code, response, isTimeout) { + super(ErrorTypes.NETWORK_ERROR, details, fatal, reason, response); + this.code = code; + this.isTimeout = isTimeout; + this.response = response; + } + } + class ManifestNetworkError extends HlsNetworkError { + constructor(fatal, reason, code, response, isTimeout) { + super(isTimeout ? ErrorDetails.MANIFEST_LOAD_TIMEOUT : ErrorDetails.MANIFEST_LOAD_ERROR, fatal, reason, code, response, isTimeout); + } + } + // Problem when loading playlist or manifest + class PlaylistNetworkError extends HlsNetworkError { + constructor(fatal, reason, code, response, isTimeout, mediaOptionType, mediaOptionId, url) { + super('', fatal, reason, code, response, isTimeout); + this.mediaOptionType = mediaOptionType; + this.mediaOptionId = mediaOptionId; + this.url = url; + switch (mediaOptionType) { + case MediaOptionType.Variant: + this.details = isTimeout ? ErrorDetails.LEVEL_LOAD_TIMEOUT : ErrorDetails.LEVEL_LOAD_ERROR; + break; + case MediaOptionType.AltAudio: + this.details = isTimeout ? ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT : ErrorDetails.AUDIO_TRACK_LOAD_ERROR; + break; + case MediaOptionType.Subtitle: + this.details = isTimeout ? ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT : ErrorDetails.SUBTITLE_TRACK_LOAD_ERROR; + break; + } + } + } + // Problem when loading SessionData + class SessionDataNetworkError extends HlsNetworkError { + constructor(fatal, reason, code, response) { + super(ErrorDetails.SESSION_DATA_LOAD_ERROR, fatal, reason, code, response, false); + } + } + // Problem when loading fragment + class FragmentNetworkError extends HlsNetworkError { + constructor(fatal, reason, code, response, isTimeout, frag, stats) { + super(isTimeout ? ErrorDetails.FRAG_LOAD_TIMEOUT : ErrorDetails.FRAG_LOAD_ERROR, fatal, reason, code, response, isTimeout); + this.mediaOptionId = frag.mediaOptionId; + this.mediaOptionType = frag.mediaOptionType; + this.stats = stats; + } + } + class GenericTimeoutError extends TimeoutError { + constructor(message, code, stats) { + super(); + this.message = message; + this.code = code; + this.stats = stats; + } + } + class FragmentAbortError extends HlsNetworkError { + constructor(frag, candidateMediaOptionId, response) { + super('fragAbortError', false, 'Fragment abort', 0, response, false); + this.candidateMediaOptionId = candidateMediaOptionId; + this.mediaOptionId = frag.mediaOptionId; + this.mediaOptionType = frag.mediaOptionType; + } + } + + // Errors generated by CDM / KeySystem + class KeyRequestTimeoutError extends HlsNetworkError { + constructor(message, keyuri, response, mediaOptionIds = []) { + super(ErrorDetails.KEY_LOAD_TIMEOUT, false, message, response.code, response, true); + this.keyuri = keyuri; + this.response = response; + this.mediaOptionIds = mediaOptionIds; + } + } + var KeyRequestErrorReason; + (function (KeyRequestErrorReason) { + KeyRequestErrorReason[KeyRequestErrorReason["InvalidState"] = 0] = "InvalidState"; + KeyRequestErrorReason[KeyRequestErrorReason["Abort"] = 1] = "Abort"; + KeyRequestErrorReason[KeyRequestErrorReason["OutputRestricted"] = 2] = "OutputRestricted"; + KeyRequestErrorReason[KeyRequestErrorReason["AlreadyFailedKey"] = 3] = "AlreadyFailedKey"; + KeyRequestErrorReason[KeyRequestErrorReason["HttpError"] = 4] = "HttpError"; + KeyRequestErrorReason[KeyRequestErrorReason["InternalError"] = 5] = "InternalError"; + KeyRequestErrorReason[KeyRequestErrorReason["LicenseServerError"] = 6] = "LicenseServerError"; + KeyRequestErrorReason[KeyRequestErrorReason["InsufficientCPC"] = 7] = "InsufficientCPC"; + })(KeyRequestErrorReason || (KeyRequestErrorReason = {})); + // Something that failed in the actual request to the license server + class KeyRequestError extends HlsNetworkError { + constructor(message, keyuri, code, response, isOkToRetry, keyErrorReason, fatal = false, mediaOptionIds = []) { + super(ErrorDetails.KEY_LOAD_ERROR, fatal, message, code, response, false); + this.keyuri = keyuri; + this.isOkToRetry = isOkToRetry; + this.keyErrorReason = keyErrorReason; + this.mediaOptionIds = mediaOptionIds; + } + } + // Something that failed due to key exchange or key system initialization + class KeySystemError extends HlsError { + constructor(message, keyuri, code, response, keysystemstring) { + super(ErrorTypes.OTHER_ERROR, ErrorDetails.KEY_SYSTEM_GENERIC_ERROR, true, message, response); + this.keyuri = keyuri; + this.code = code; + this.response = response; + this.keysystemstring = keysystemstring; + } + } + function copyKeyError(error, mediaOptionIds) { + if (error instanceof KeyRequestError) { + return new KeyRequestError(error.message, error.keyuri, error.code, error.response, error.isOkToRetry, error.keyErrorReason, error.fatal, mediaOptionIds); + } + else if (error instanceof KeyRequestTimeoutError) { + return new KeyRequestTimeoutError(error.message, error.keyuri, ErrorResponses.CryptResponseReceivedSlowly, mediaOptionIds); + } + else if (error) { + return new ExceptionError(error.fatal, error.reason, ErrorResponses.InternalError); + } + return null; + } + + /* + * FairPlayStreaming key system constants + * + * 2018 Apple Inc. All rights reserved. + */ + const FairPlaySecurityLevels = { + AppleBaseline: 0, + AppleMain: 1, + Main: 1, + Baseline: 0, + }; + const FairPlayStreamingKeySystemProperties = { + id: 'fairplaystreaming', + systemStringPrefix: 'com.apple.fps', + keyFormatString: 'com.apple.streamingkeydelivery', + securityLevels: FairPlaySecurityLevels, + }; + + class LoaderQuery extends QueryEntity { + constructor(store) { + super(store); + this.store = store; + } + get unresolvedUriLoading$() { + return this.selectEntityAction(EntityActions.Add).pipe(map((entityIds) => entityIds.map((id) => this.getEntity(id)))); + } + } + + class LoaderStore extends EntityStore { + constructor() { + super({}, { name: 'loader', producerFn: produce_1, idKey: 'uri' }); + } + } + + class LoaderService { + constructor(store) { + this.store = store; + } + createUnresolvedUriLoading(uri, responseType, userAgent) { + logAction('loader.create.unresolvedUriLoading'); + this.store.add({ uri, responseType, userAgent }); + } + removeUnresolvedUriLoading(uri) { + logAction('loader.remove.unresolvedUriLoading'); + this.store.remove(uri); + } + } + const store$1 = new LoaderStore(); + let service$2 = null; // To be instantiated in loaderService() + /*********************************************** + * Static helper functions that specifically use the above singletons + */ + function createLoaderQuery() { + return new LoaderQuery(store$1); + } + /** + * @returns The global instance of LoaderService that operates on global LoaderStore + */ + function getLoaderService() { + if (!service$2) { + service$2 = new LoaderService(store$1); + } + return service$2; + } + + /* + * HLS Event Emitter + * + * + */ + /** + * Simple adapter sub-class of Nodejs-like EventEmitter. + */ + class HlsEventEmitter extends EventEmitter { + /** + * We simply want to pass along the event-name itself + * in every call to a handler, which is the purpose of our `trigger` method + * extending the standard API. + */ + trigger(event, data) { + try { + this.emit(event, event, data); + if (event.toString() !== 'hlsFragLoadProgress') { + // do not log fragLoadProgress, too noisy + // Do NOT log data, which can be very large (for instance the data1: and data2: arguments from demuxer + // console will hold onto the arguments in here until the next event loop and can cause memory to balloon over time + let details = ''; + if ((event === 'hlsInternalError' || event === 'hlsError') && data.length) { + details = JSON.stringify(data[0], ['fatal', 'details', 'reason']); + } + const eventData = { event: event.toString() }; + if (details.length > 0) { + eventData.details = details; + } + getLogger().qe({ critical: true, name: 'eventTrigger', data: eventData }); + } + } + catch (err) { + getLogger().warn(`error in event listener for ${event}: ${err.message}`); + } + } + } + + class EventTarget { + constructor(target, _this) { + this.target = target; + this._this = _this; + } + eventWithOptions(event, options, handler, _this = this._this) { + let observable = fromEvent(this.target, event, options); + if (this.target instanceof HlsEventEmitter) { + // HLS's raw event handlers accepts two arguments: + // - event: _Event + // - data: EventData<_EventDataMap, _Event> + // We need to map it to single data argument + observable = observable.pipe(map(([_, data]) => data)); + } + if (handler) { + if (_this) { + handler = handler.bind(_this); + } + observable = observable.pipe(tap(handler)); + } + return observable; + } + event(event, handler, _this = this._this) { + return this.eventWithOptions(event, undefined, handler, _this); + } + listen(event, notifier, handler, _this = this._this) { + return this.event(event, handler, _this).pipe(takeUntil(notifier)).subscribe(); + } + } + function fromEventTarget(target, _this) { + return new EventTarget(target, _this); + } + // Note that vuze doesn't implement some events so may not be safe to use ev directly. Use to fill + function convertEvent(currentTarget, type, ev) { + var _b, _c; + return { + currentTarget: (_b = ev === null || ev === void 0 ? void 0 : ev.currentTarget) !== null && _b !== void 0 ? _b : currentTarget, + target: (_c = ev === null || ev === void 0 ? void 0 : ev.target) !== null && _c !== void 0 ? _c : currentTarget, + type: type, + }; + } + + + function getByteRangeLength(byteRangeHeader) { + const BYTERANGE = /([0-9]+)\-([0-9]+)\/([0-9]+)/; + const result = BYTERANGE.exec(byteRangeHeader); + return result ? parseInt(result[2]) - parseInt(result[1]) + 1 : undefined; + } + function getContentLength(xhr) { + let contentLength; + const contentEncoding = xhr.getResponseHeader('Content-Encoding'); + const transferEncoding = xhr.getResponseHeader('Transfer-Encoding'); + const noContentEncoding = !contentEncoding || (contentEncoding && contentEncoding.toLowerCase() === 'identity'); + const noTransferEncoding = !transferEncoding || (transferEncoding && transferEncoding.toLowerCase() === 'identity'); + if (noContentEncoding && noTransferEncoding) { + // Length only makes sense without encoding/compression + contentLength = getByteRangeLength(xhr.getResponseHeader('Content-Range')); + if (!isFiniteNumber(contentLength)) { + contentLength = parseInt(xhr.getResponseHeader('Content-Length')); + } + } + return contentLength; + } + function fromXMLHttpRequest(context, loadConfig) { + return new Observable((subscriber) => { + const { maxTimeToFirstByteMs, maxLoadTimeMs } = loadConfig; + const xhr = new XMLHttpRequest(); + const stats = { + trequest: performance.now(), + tfirst: NaN, + tload: NaN, + loaded: 0, + total: NaN, + complete: false, + }; + const xhrTarget = fromEventTarget(xhr); + // register for events + const onprogress$ = xhrTarget.event('progress').pipe(share(), throttleTime(300, queueScheduler, { leading: true, trailing: true }), map((progressEvent) => { + if (isNaN(stats.tfirst)) { + stats.tfirst = performance.now(); + } + stats.loaded = progressEvent.loaded; + if (progressEvent.lengthComputable) { + stats.total = progressEvent.total; + } + return progressEvent.target; + }), filter((req) => req.readyState >= 3)); + const onreadystate$ = xhrTarget.event('readystatechange').pipe(share(), map((event) => event.target), filter((req) => req.readyState >= 2), tap((req) => { + if (isNaN(stats.tfirst) && req.readyState >= 3) { + stats.tfirst = performance.now(); + } + })); + let timeToFirstByteTimeout$ = EMPTY; + if (isFinite(maxTimeToFirstByteMs) && maxTimeToFirstByteMs > 0) { + timeToFirstByteTimeout$ = merge(onprogress$, onreadystate$).pipe(take(1), timeout(context.extendMaxTTFB > 0 ? context.extendMaxTTFB : maxTimeToFirstByteMs), switchMap(() => EMPTY)); + } + let totalLoadTimeout$ = EMPTY; + if (isFinite(maxLoadTimeMs) && maxLoadTimeMs > 0) { + totalLoadTimeout$ = onreadystate$.pipe(filter((req) => req.readyState >= 4), take(1), timeout(maxLoadTimeMs), switchMap(() => EMPTY)); + } + let onProgressCb$ = EMPTY; + if (context.onProgress) { + onProgressCb$ = merge(of(xhr), onprogress$) // Emit on subscribe and then on every 'onprogress' event + .pipe(map((xhrReq) => { + const { getData, cb } = context.onProgress; + return cb(context.url, xhrReq.status, stats, getData ? xhrReq.response : undefined); + }), takeWhile((done) => !done, true)) + .pipe(switchMapTo(EMPTY)); + } + const sub = merge(onprogress$.pipe(switchMap(() => EMPTY)), onreadystate$, timeToFirstByteTimeout$, totalLoadTimeout$, onProgressCb$) + .pipe(filter((req) => req.readyState >= 4), take(1), switchMap((req) => { + stats.complete = true; + if (req.status >= 200 && req.status < 300) { + stats.tload = performance.now(); + stats.contentType = req.getResponseHeader('Content-Type'); + if (loadConfig.reportCDNServer) { + stats.cdnServer = req.getResponseHeader('CDN-Server'); + } + stats.contentLength = loadConfig.forceContentLenCheckIfNoHeader ? getContentLength(req) : null; // forceContentLenCheckIfNoHeader is true only for non-browser platforms + _relateServerInstanceInfoToRequest(req, context); + if (context.responseType === 'arraybuffer') { + stats.loaded = req.response.byteLength; + } + else { + stats.loaded = req.responseText.length; + } + stats.total = stats.loaded; + if (context.checkContentLength && // checkContentLength is true only when loading fragment + (stats.total === 0 || (isFiniteNumber(stats.contentLength) && stats.total != stats.contentLength))) { + throw new GenericNetworkError('Network error', req.status); + } + const res = [req, stats]; + return scheduled(of(res), queueScheduler); + } + else { + throw new GenericNetworkError('Network error', req.status); + } + }), catchError((err) => { + if (err instanceof TimeoutError) { + throw new GenericTimeoutError(err.message, 0, stats); + } + if (!(err instanceof GenericNetworkError)) { + throw new GenericNetworkError(err.message, 0); + } + throw err; + })) + .subscribe(subscriber); + // Setup XHR request + const { url, method, byteRangeOffset, responseType, body } = context; + if (context.mimeType) { + xhr.overrideMimeType(context.mimeType); + } + try { + const xhrSetup = context.xhrSetup; + if (xhrSetup) { + try { + xhrSetup(xhr, url); + } + catch (e) { + // fix xhrSetup: (xhr, url) => {xhr.setRequestHeader("Content-Language", "test");} + // not working, as xhr.setRequestHeader expects xhr.readyState === OPEN + xhr.open(method, context.url, true); + xhrSetup(xhr, context.url); + } + } + if (!xhr.readyState) { + xhr.open(method, context.url, true); + } + } + catch (e) { + // IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS + throw new GenericNetworkError(e.message, xhr.status); + } + xhr.responseType = responseType; + // Set headers + if (byteRangeOffset && isFiniteNumber(byteRangeOffset.start) && isFiniteNumber(byteRangeOffset.end) && byteRangeOffset.start >= 0 && byteRangeOffset.end > byteRangeOffset.start) { + const { start, end } = byteRangeOffset; + xhr.setRequestHeader('Range', `bytes=${start}-${end - 1}`); + } + if (context.headers) { + for (const [name, value] of Object.entries(context.headers)) { + xhr.setRequestHeader(name, value); + } + } + // Start request + if (method === 'POST' && body) { + xhr.send(body); + } + else { + xhr.send(); + } + return () => { + xhr.abort(); + sub.unsubscribe(); + }; + }); + } + function _relateServerInstanceInfoToRequest(xhr, context) { + if (context.collectServerInstanceInfo) { + context.serverInstanceInfo = {}; + context.collectServerInstanceInfo.forEach((header) => { + const headerValue = xhr.getResponseHeader(header); + if (headerValue) { + context.serverInstanceInfo[header] = headerValue; + } + }); + } + } + + const loggerName$7 = { name: 'CustomUrlLoader' }; + class CustomUrlLoader { + constructor(loaderService) { + this.loaderService = loaderService; + this.requestMap = {}; + this.logger = getLogger(); + } + load(context, loadConfig) { + return new Observable((observer) => { + const url = context.url; + const { maxTimeToFirstByteMs } = loadConfig; + const stats = { + trequest: performance.now(), + tfirst: NaN, + tload: NaN, + loaded: 0, + total: NaN, + complete: false, + }; + const request = (this.requestMap[url] = new AsyncSubject()); + const source = request.pipe(timeout(context.extendMaxTTFB > 0 ? context.extendMaxTTFB : maxTimeToFirstByteMs), switchMap((response) => { + this.logger.info(loggerName$7, `loaded originalUri=${redactUrl(url)} uri=${redactUrl(response.uri)} durationMs=${performance.now() - stats.trequest}`); + stats.tfirst = performance.now(); + return this.handleExternalResponse(response, context, loadConfig, stats); + }), catchError((err) => { + if (err instanceof TimeoutError) { + throw new GenericTimeoutError(err.message, 0, stats); + } + throw err; + }), finalize$1(() => { + this.requestMap[url] = undefined; + this.loaderService.removeUnresolvedUriLoading(url); + })); + this.logger.info(loggerName$7, `loading originalUri=${redactUrl(url)}`); + if (context.onProgress) { + context.onProgress.cb(url, 0, stats, undefined); + } + const sub = source.subscribe(observer); + this.loaderService.createUnresolvedUriLoading(url, context.responseType, navigator.userAgent); + return () => { + sub.unsubscribe(); + this.requestMap[url] = undefined; + }; + }); + } + setCustomUrlResponse(url, response) { + const source = this.requestMap[url]; + if (source) { + source.next(response); + source.complete(); + this.requestMap[url] = undefined; + } + } + handleExternalResponse(data, context, loadConfig, stats) { + stats.tload = performance.now(); + const status = data.response.status || 200; + // http status of 200 to 299 are all successfull + if (status >= 200 && status < 300) { + if (context.responseType === 'arraybuffer' && data.response.data instanceof ArrayBuffer) { + stats.loaded = data.response.data.byteLength; + } + else { + stats.loaded = data.response.data.toString().length; + } + stats.total = stats.loaded; + stats.complete = true; + return scheduled(of({ status, data, stats }), queueScheduler); + } + else if (isRedirectStatusCode(status)) { + this.logger.info(loggerName$7, `redirect status=${status} url=${redactUrl(data.response.uri)}`); + return this.redirectRequest(data.response.uri, context, loadConfig, stats); + } + else { + this.logger.warn(loggerName$7, `unable to load custom url > uri=${redactUrl(data.response.uri)}, status=${status}`); + return throwError(new GenericNetworkError('Unable to load custom url', status)); + } + } + redirectRequest(uri, context, loadConfig, stats) { + const { maxLoadTimeMs, maxTimeToFirstByteMs } = loadConfig; + const updatedMaxLoadTimeMs = maxLoadTimeMs - (performance.now() - stats.trequest); // Recalculate timeout based on request time + const updatedMaxTimeToFirstByteMs = context.extendMaxTTFB > 0 ? context.extendMaxTTFB : maxTimeToFirstByteMs - (performance.now() - stats.trequest); // Recalculate timeout based on request time + const newConfig = Object.assign(Object.assign({}, loadConfig), { maxLoadTimeMs: updatedMaxLoadTimeMs, maxTimeToFirstByteMs: updatedMaxTimeToFirstByteMs }); + const newContext = Object.assign(Object.assign({}, context), { url: uri }); + if (updatedMaxLoadTimeMs <= 0 || updatedMaxTimeToFirstByteMs <= 0) { + return throwError(new TimeoutError()); + } + return fromXMLHttpRequest(newContext, newConfig).pipe(map(([xhr, sample]) => { + const { responseURL, status } = xhr; + const uri = responseURL || ''; + const data = { + uri, + response: { + status: status, + uri, + data: xhr.response, + }, + }; + stats.loaded = stats.total = sample.loaded; + stats.tload = performance.now(); + stats.complete = true; + return { status: xhr.status, data, stats }; + })); + } + } + let loader; + function getCustomUrlLoader(loaderService) { + if (!loader) { + const service = loaderService || getLoaderService(); + loader = new CustomUrlLoader(service); + } + return loader; + } + + function fromUrlArrayBuffer(context, loadConfig) { + const ctx = Object.assign(Object.assign({}, context), { method: 'GET', responseType: 'arraybuffer' }); + const customUrlLoader = getCustomUrlLoader(); + return isCustomUrl(ctx.url) + ? customUrlLoader.load(ctx, loadConfig).pipe(map((res) => [res.data.response.data, res.stats, ctx.serverInstanceInfo])) + : fromXMLHttpRequest(ctx, loadConfig).pipe(map(([xhr, bwSample]) => [xhr.response, bwSample, ctx.serverInstanceInfo])); + } + + /** + * Generic load utils for retry and such + */ + function getLoadConfig(loadable, policy) { + return !loadable.url || isCustomUrl(loadable.url) ? policy.customURL : policy.default; + } + function getRetryConfig(error, config) { + if (error instanceof HlsNetworkError) { + return error.isTimeout ? config.timeoutRetry : config.errorRetry; + } + return null; + } + /** + * Operator for converting TimeoutError & GenericNetworkError into ManifestNetworkError + */ + function convertToManifestNetworkError(fatal) { + return (source) => source.pipe(catchError((err) => { + if (err instanceof GenericTimeoutError) { + throw new ManifestNetworkError(fatal, 'Timeout', 0, ErrorResponses.ManifestTimeoutError, true); + } + else if (err instanceof GenericNetworkError) { + throw new ManifestNetworkError(fatal, err.message, err.code, { code: err.code, text: 'Manifest network error' }, false); + } + throw err; + })); + } + /** + * Operator for converting TimeoutError & GenericNetworkError into PlaylistNetworkError + */ + function convertToPlaylistNetworkError(mediaOptionType, mediaOptionId, fatal, url) { + return (source) => source.pipe(catchError((err) => { + if (err instanceof GenericTimeoutError) { + throw new PlaylistNetworkError(fatal, 'Timeout', 0, ErrorResponses.PlaylistTimeoutError, true, mediaOptionType, mediaOptionId, url); + } + else if (err instanceof GenericNetworkError) { + throw new PlaylistNetworkError(fatal, err.message, err.code, { code: err.code, text: 'Playlist Network Error' }, false, mediaOptionType, mediaOptionId, url); + } + throw err; + })); + } + /** + * Operator for converting TimeoutError & GenericNetworkError into FragmentError + */ + function convertToFragmentNetworkError(frag, fatal) { + return (source) => source.pipe(catchError((err) => { + if (err instanceof GenericTimeoutError) { + throw new FragmentNetworkError(fatal, 'Timeout', 0, ErrorResponses.FragmentTimeoutError, true, frag, err.stats); + } + else if (err instanceof GenericNetworkError) { + throw new FragmentNetworkError(fatal, err.message, err.code, { code: err.code, text: 'Fragment Network Error' }, false, frag); + } + throw err; + })); + } + /** + * Helper function to update playtype of levels + */ + function updatePlaylistAttributes(levelDetails) { + const playlistTypeString = levelDetails.type; + const isLive = levelDetails.liveOrEvent; + let playlistType = 'VOD'; + if (playlistTypeString === 'EVENT' && isLive) { + playlistType = 'EVENT'; + } + else if ((!playlistTypeString || playlistTypeString.length === 0 || playlistTypeString === 'LIVE') && isLive) { + playlistType = 'LIVE'; + } + if (levelDetails.type !== playlistType) { + getLogger().info(`Playlist type updated from ${levelDetails.type} to ${playlistType}`); + levelDetails.type = playlistType; + } + } + + /** + * 2018 Apple Inc. All rights reserved. + */ + const PlayReadySecurityLevels = { + SL2000: 0, + SL3000: 1, + }; + const PlayReadyKeySystemProperties = { + id: 'playready', + systemStringPrefix: 'com.microsoft.playready', + keyFormatString: 'com.microsoft.playready', + securityLevels: PlayReadySecurityLevels, + }; + + /** + * 2018 Apple Inc. All rights reserved. + */ + const WidevineSecurityLevels = { + WIDEVINE_SOFTWARE: 0, + WIDEVINE_HARDWARE: 1, + }; + const WidevineKeySystemProperties = { + id: 'widevine', + systemStringPrefix: 'com.widevine.alpha', + keyFormatString: 'urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed', + securityLevels: WidevineSecurityLevels, + }; + + /* + * Utilities for numeric encodings + * + * 2018 Apple Inc. All rights reserved. + */ + function base64ToBase64Url(base64encodedStr) { + return base64encodedStr.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, ''); + } + class BrowserNumericEncodingUtils { + static strToBase64Encode(str) { + return btoa(str); + } + static base64DecodeToStr(str) { + return atob(str); + } + static base64Encode(input) { + return btoa(String.fromCharCode(...input)); + } + static base64UrlEncode(input) { + return base64ToBase64Url(BrowserNumericEncodingUtils.base64Encode(input)); + } + static base64Decode(base64encodedStr) { + return Uint8Array.from(atob(base64encodedStr), (c) => c.charCodeAt(0)); + } + } + class NodeJSNumericEncodingUtils { + static strToBase64Encode(str) { + return global$1.Buffer.from(str).toString('base64'); + } + static base64DecodeToStr(str) { + return global$1.Buffer.from(str, 'base64').toString(); + } + static base64Encode(input) { + return global$1.Buffer.from(input).toString('base64'); + } + static base64UrlEncode(input) { + return base64ToBase64Url(NodeJSNumericEncodingUtils.base64Encode(input)); + } + static base64Decode(base64encodedStr) { + const buffer = global$1.Buffer.from(base64encodedStr, 'base64'); + return new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength); + } + } + const NumericEncodingUtils = typeof global$1.Buffer !== 'undefined' ? NodeJSNumericEncodingUtils : BrowserNumericEncodingUtils; + var NumericEncodingUtils$1 = NumericEncodingUtils; + + /* + * Utilities for key systems + * + * 2018 Apple Inc. All rights reserved. + */ + // Map of prefix to mimeType + const kVideoCodecToMimeType = { + // AVC + avc1: 'video/mp4', + avc3: 'video/mp4', + dvav: 'video/mp4', + dva1: 'video/mp4', + // HEVC + hev1: 'video/mp4', + hvc1: 'video/mp4', + dvh1: 'video/mp4', + dvhe: 'video/mp4', // Dolby vission + }; + const kAudioCodecToMimeType = { + mp4a: 'audio/mp4', + 'ac-3': 'audio/mp4', + 'ec-3': 'audio/mp4', + }; + function isMediaKeySystemConfig(config) { + return config && typeof config === 'object'; // requestMediaKeySystemAccess accepts {} + } + // Input: set of codec strings for each type + // Output: + // { + // videoCapabilities: [ + // { contentType: 'video/mp4;codecs=avc.###', robustness: '' } ], + // audioCapabilities: [] + // } + function getMediaKeysystemMediaCapability(videoCodecs, audioCodecs) { + const result = { + videoCapabilities: [], + audioCapabilities: [], + }; + if (videoCodecs) { + videoCodecs.forEach((videoCodec) => { + const prefix = videoCodec.split('.')[0].trim(); + if (prefix in kVideoCodecToMimeType) { + result.videoCapabilities.push({ + contentType: kVideoCodecToMimeType[prefix] + ';codecs=' + videoCodec, + robustness: '', + }); + } + }); + } + if (audioCodecs) { + audioCodecs.forEach((audioCodec) => { + const prefix = audioCodec.split('.')[0].trim(); + if (prefix in kAudioCodecToMimeType) { + result.audioCapabilities.push({ + contentType: kAudioCodecToMimeType[prefix] + ';codecs=' + audioCodec, + robustness: '', + }); + } + }); + } + return result; + } + /** + * Utility to change the endianness of Key ID + * @param keyID uint8array representing the keyId + * @returns uint8array the keyId after changing the endianness + */ + function changeEndianness(keyId) { + // eslint-disable-next-line func-style + const swap = function (array, from, to) { + const cur = array[from]; + array[from] = array[to]; + array[to] = cur; + }; + swap(keyId, 0, 3); + swap(keyId, 1, 2); + swap(keyId, 4, 5); + swap(keyId, 6, 7); + } + /** + * Generate key id from an arbitrary string + * @param str String to convert into a 16 byte key + * @returns uint8array representing the keyId + */ + function getKeyIdBytes(str) { + const keyIdbytes = BufferUtils.strToUtf8array(str).subarray(0, 16); + const paddedkeyIdbytes = new Uint8Array(16); + paddedkeyIdbytes.set(keyIdbytes, 16 - keyIdbytes.length); + return paddedkeyIdbytes; + } + /** + * @param URI string + * @returns Uint8Array of URI data + */ + function convertDataUriToArrayBytes(uri) { + // data:[ + const colonsplit = uri.split(':'); + let keydata = null; + if (colonsplit[0] === 'data' && colonsplit.length === 2) { + const semicolonsplit = colonsplit[1].split(';'); + const commasplit = semicolonsplit[semicolonsplit.length - 1].split(','); + if (commasplit.length === 2) { + const isbase64 = commasplit[0] === 'base64'; + const data = commasplit[1]; + if (isbase64) { + semicolonsplit.splice(-1, 1); // remove from processing + keydata = NumericEncodingUtils$1.base64Decode(data); + } + else { + keydata = getKeyIdBytes(data); + } + } + } + return keydata; + } + /** + * Generate 'keyids' init data from an array of key ids + * @param keyIds Array of key ids represented as uint8array[16] objects + * @returns The initData string to pass to generateRequest as part of EME + */ + function makeKeyIdsInitData(keyIds) { + const initDataObj = { + kids: keyIds.map(NumericEncodingUtils$1.base64UrlEncode), + }; + // Convert string to utf-8 array + return BufferUtils.strToUtf8array(JSON.stringify(initDataObj)); + } + // parse pssh list and convert to dictionary: + function parsePSSHList(psshList) { + const dv = new DataView(psshList); + let whichByte = 0; + const pssh = {}; // PSSH: systemID => data + while (whichByte < dv.buffer.byteLength) { + const thisBox = whichByte; + const boxSize = dv.getUint32(whichByte); + whichByte += 4; + const nextBox = thisBox + boxSize; + if (dv.getUint32(whichByte) !== 1886614376) { + whichByte = nextBox; + continue; // next box + } + whichByte += 4; + const version = dv.getUint8(whichByte); + switch (version) { + case 0: // NO KEY IDS + case 1: + whichByte += 1; + break; + default: + whichByte = nextBox; + break; + } + whichByte += 3; // flags + // UUID: 4 bytes - 2 bytes - 2 bytes - 2 bytes - 6 bytes + let systemID = ''; + // 16 bytes + for (let i = 0; i < 16; ++i, ++whichByte) { + systemID += dv.getUint8(whichByte).toString(16); + switch (i) { + case 4: + case 6: + case 8: + case 10: + systemID += '-'; + break; + } + } + whichByte += 4; // Data size + pssh[systemID] = dv.buffer.slice(thisBox, nextBox); + } + return pssh; + } + const keysystemutil = { + getCapabilities: getMediaKeysystemMediaCapability, + changeEndianness, + getKeyIdBytes, + convertDataUriToArrayBytes, + makeKeyIdsInitData, + parsePSSHList, + }; + + /* + * Decrypt data + * + * 2018 Apple Inc. All rights reserved. + */ + // Note that this will not get copied into Worker + let keyUriToKeyIdMap = {}; + // Created in playlist-loader, modified in demux / remux + class DecryptData { + /** + * @param method From METHOD attribute, mandatory + * @param uri Absolute url of the key (calculated from URI) + * @param iv 128-bit unsigned int representing Initialization Vector, used when KEYFORMAT="identity", optional + * @param format From KEYFORMAT, optional with default value of "identity" + * @param formatversions Array From KEYFORMATVERSION, an array of unsigned int, optional + */ + constructor(method, uri, iv, format, formatversions) { + this.method = method; + this.uri = uri; + this.iv = iv; + this.format = format; + this.formatversions = formatversions; + this.isEncrypted = this.method && this.method !== 'NONE'; + if (!this.formatversions || this.formatversions.length === 0) { + this.formatversions = [1]; + } + // Set later + this.key = undefined; + this.keyId = undefined; + if (!this.isEncrypted) { + return; + } + // Initialize keyId if possible + const keyBytes = keysystemutil.convertDataUriToArrayBytes(this.uri); + if (keyBytes) { + switch (format) { + case PlayReadyKeySystemProperties.keyFormatString: { + // Playready + this.pssh = keyBytes; + const keyBytesUtf16 = new Uint16Array(keyBytes.buffer, keyBytes.byteOffset, keyBytes.byteLength / 2); + const keyByteStr = String.fromCharCode.apply(null, Array.from(keyBytesUtf16)); + // we got entire PSSH data. Incase of Playready it's WRMHEADER XML object. Parse it. + const xmlKeyBytes = keyByteStr.substring(keyByteStr.indexOf('<'), keyByteStr.length); + const parser = new DOMParser(); + const xmlDoc = parser.parseFromString(xmlKeyBytes, 'text/xml'); + const keyData = xmlDoc.getElementsByTagName('KID')[0]; + if (keyData) { + let keyId = null; + if (keyData.childNodes[0]) { + keyId = keyData.childNodes[0].nodeValue; + } + else { + keyId = keyData.getAttribute('VALUE'); + } + if (keyId) { + const keyIdArray = NumericEncodingUtils$1.base64Decode(keyId).subarray(0, 16); + // KID value in PRO is a base64-encoded little endian GUID interpretation of UUID + // KID value in ‘tenc’ is a big endian UUID GUID interpretation of UUID + keysystemutil.changeEndianness(keyIdArray); + this.keyId = keyIdArray; + } + } + break; + } + case WidevineKeySystemProperties.keyFormatString: { + // Widevine + this.pssh = keyBytes; + // In case of widevine keyID is embedded in PSSH box. Read Key ID. + if (keyBytes.length >= 22) { + this.keyId = keyBytes.subarray(keyBytes.length - 22, keyBytes.length - 6); + } + break; + } + default: { + // default handling + let keydata = keyBytes.subarray(0, 16); + if (keydata.length !== 16) { + const padded = new Uint8Array(16); + padded.set(keydata, 16 - keydata.length); + keydata = padded; + } + this.keyId = keydata; + break; + } + } + } + // Default behavior + if (!this.keyId || this.keyId.byteLength !== 16) { + let keyId = keyUriToKeyIdMap[this.uri]; + if (!keyId) { + const val = Object.keys(keyUriToKeyIdMap).length % Number.MAX_SAFE_INTEGER; + // MAX_SAFE_INTEGER is huge (9007199254740991) so this /should/ be safe... + keyId = new Uint8Array(16); + const dv = new DataView(keyId.buffer, 12, 4); // Just set the last 4 bytes + dv.setUint32(0, val); + keyUriToKeyIdMap[this.uri] = keyId; + } + this.keyId = keyId; + } + } + get keyTagInfo() { + const { method, isEncrypted, uri, iv, keyId, key, format, formatversions } = this; + const keyTagInfo = { method, isEncrypted, uri, iv, keyId, key, format, formatversions }; + return keyTagInfo; + } + static clearKeyUriToKeyIdMap() { + keyUriToKeyIdMap = {}; + } + } + + var KeyRequestState; + (function (KeyRequestState) { + KeyRequestState["NONE"] = "NONE"; + KeyRequestState["GET_REQUEST_INFO"] = "GET_REQUEST_INFO"; + KeyRequestState["GET_CHALLENGE"] = "GET_CHALLENGE"; + KeyRequestState["GET_KEY_RESPONSE"] = "GET_KEY_RESPONSE"; + KeyRequestState["PROCESS_LICENSE"] = "PROCESS_LICENSE"; + })(KeyRequestState || (KeyRequestState = {})); + // Wrap subscription and teardown of MediaKeySession callbacks + class MediaKeySessionContext { + constructor(session, onkeystatuseschange, onkeymessage, logger) { + this.session = session; + this.onkeystatuseschange = onkeystatuseschange; + this.onkeymessage = onkeymessage; + this.logger = logger; + this.isClosing$ = new BehaviorSubject(false); + this.closed$ = new BehaviorSubject(false); + const target = fromEventTarget(this.session); + target.listen('keystatuseschange', this.isClosing$.pipe(filter((x) => x === true)), this.onkeystatuseschange); + // Message is weird because we still want the message until after close() + target.listen('message', this.closed$.pipe(filter((x) => x === true)), this.onkeymessage); + } + get isClosing() { + return this.isClosing$.value; + } + get isClosed() { + return this.closed$.value; + } + /** + * Remove and close all keys in MediaKeySession + */ + destroy() { + this.logger.info(`[Keys] : remove licenses & keys for session : ${this.session.sessionId} and close it`); + this.isClosing$.next(true); + const session = this.session; + // Unabortable section. Should execute no matter what. + return from(session + .remove() + .catch((error) => { + this.logger.info(`Could not remove session: ${error.message}`); + }) + .then(() => { + return session.close(); + }) + .catch((error) => { + this.logger.info(`Could not close session: ${error.message}`); + })).pipe(tap(() => { + this.isClosing$.next(false); + this.closed$.next(true); + }), finalize$1(() => { + this.isClosing$.next(false); + this.closed$.next(true); + })); + } + } + /** + * @brief KeyContext keeps track of the current key request state and information about a given key. + * + * A key may exist without a key request being in progress. When a key request is in progress, there are + * multiple phases / states. Each state of a key request is asynchronous and can be completed either by + * the CDM or by the client. If we get out of sync with the current key request state, we will throw an error + * and all subscribers will be notified. We will also throw an error if aborted in the middle of the request. + * Note that subscribers will not get the abort error if they unsubscribe from the observable. + */ + class KeyContext { + constructor(decryptdata, session = null) { + this.decryptdata = decryptdata; + this._requestState$ = new BehaviorSubject(KeyRequestState.NONE); // Key request state. Note this is different from keystatus from CDM + this.destroy$ = new Subject(); // Destroy key. This should stop any renewal timers + // If in progress, the current observable + this.currentObservable = null; + this.session = null; // Current session. Could be shared by others (config.useMultipleKeySessions !== true) + this.oldSessions = []; + this.session = session; + } + get requestState() { + return this._requestState$.value; + } + get onKeyRequestState$() { + return this._requestState$; + } + destroy() { + this.destroy$.next(); + // TODO actual cleanup? + } + abort() { + if (this.requestState !== KeyRequestState.NONE) { + const err = new KeyRequestError('Aborted', this.decryptdata.uri, 0, ErrorResponses.KeySystemAbort, true, KeyRequestErrorReason.Abort); + this.error(err); + } + } + /** + * Change state and return observable that gets completed when resolveState is called + * + * @param state The state to switch to + */ + setKeyRequestState(state) { + if (this.currentObservable) { + const err = new KeyRequestError(`Unexpected state transition ${this.requestState}->${state}`, this.decryptdata.uri, 0, ErrorResponses.KeySystemUnexpectedStateTransition, true, KeyRequestErrorReason.InvalidState); + this.error(err); + } + this._requestState$.next(state); + const returnVal = new AsyncSubject(); + if (state === KeyRequestState.NONE) { + returnVal.complete(); + this.currentObservable = null; + } + else { + //@ts-ignore + this.currentObservable = returnVal; + } + return returnVal; + } + /** + * Resolve current state observable + * @param state State to resolve + * @param value The value to resolve current observable with + */ + resolveState(state, value) { + if (!this.currentObservable) { + return; + } + if (state !== this.requestState) { + const err = new KeyRequestError(`Unexpected state ${this.requestState} != ${state}`, this.decryptdata.uri, 0, ErrorResponses.KeySystemUnexpectedState, true, KeyRequestErrorReason.InvalidState); + this.error(err); + return; + } + if (value instanceof Error) { + this.error(value); + } + else { + const obs = this.currentObservable; + this.currentObservable = null; + obs.next(value); + obs.complete(); + } + } + /** + * Signal error for this key. Could be due to key request or some other internal error from CDM + * @param err The error + */ + error(err) { + if (this.currentObservable) { + const obs = this.currentObservable; + this.currentObservable = null; + obs.error(err); + } + this.setKeyRequestState(KeyRequestState.NONE); + // TODO: any other cleanup ? + } + } + + /* + * Base class for key systems + * + * 2018 Apple Inc. All rights reserved. + */ + const MAX_CERT_LOAD_TIME_MS = 10000; // Maximum time for loading the certificate + function printKeyTag(tag) { + return `uri=${redactUrl(tag.uri)} keyId=${Hex$1.hexDump(tag.keyId)}`; + } + class KeySystem { + constructor(mediaKeys, systemString, config, eventEmitter, useSingleKeySession, sessionHandler, logger) { + this.mediaKeys = mediaKeys; + this.systemString = systemString; + this.config = config; + this.eventEmitter = eventEmitter; + this.useSingleKeySession = useSingleKeySession; + this.sessionHandler = sessionHandler; + this.logger = logger; + this.destroy$ = new Subject(); + this.setCert = false; // Has set cert on MediaKeySession + this.certificate$ = new BehaviorSubject(null); + // This event will be signalled when a renewal request is needed or if an error occurs. + // The key system will not auto renew. Instead client is expected to call startKeyRequest + this._keyStatusChange$ = new Subject(); + this.shouldDestroyMediaKeys = false; + // TJH: will playingItem raise a license challenge ? + //this.itemId = hls.loadingItem?.itemId || ''; + this.itemId = ''; + this.sessions = []; + this.keyIdToKeyInfo = {}; + this.keyUriToKeyInfo = {}; + this.sessionIdToKeyUri = {}; + this.onkeystatuseschange = this.handleKeyStatusesChange.bind(this); + this.onkeymessage = this.handleKeyMessage.bind(this); + } + get keyStatusChange$() { + return this._keyStatusChange$; + } + destroy() { + this.isDestroying = true; + this.destroy$.next(); + for (const info of Object.values(this.keyIdToKeyInfo)) { + this._abortKeyRequest(info); + } + // Remove sessions + const destroyPromises = this.sessions.map((ctx) => ctx.destroy()); + const p = iif(() => destroyPromises.length === 0, VOID, forkJoin(destroyPromises)).pipe(mapTo(undefined), finalize$1(() => { + this.mediaKeys = undefined; + this.keyIdToKeyInfo = {}; + this.keyUriToKeyInfo = {}; + this.sessionIdToKeyUri = {}; + })); + DecryptData.clearKeyUriToKeyIdMap(); + return p; + } + /** + * @param certificate Used for generating challenge + * @returns {Promise} Fired when non-null certificate has been set on the object. + */ + setServerCertificate(certificate = null) { + this.logger.info(this.systemString + ' setServerCertificate(' + (certificate ? 'nonnull' : 'null') + ')'); + if (!this.needsCert) { + return of(true); + } + if (certificate) { + this.certificate$.next(certificate); + } + const waitForCert$ = waitFor(this.certificate$, (x) => x != null).pipe(timeout(MAX_CERT_LOAD_TIME_MS)); + return waitForCert$.pipe(switchMap((cert) => { + if (this.setCert) { + return of(true); + } + if (!this.setCertSubject || this.setCertSubject.isStopped) { + this.setCertSubject = new AsyncSubject(); + return from(this.mediaKeys.setServerCertificate(cert)).pipe(tap((v) => { + // Sometimes the promise returned by the CDM doesn't carry boolean + // This will cause playback failure. + const certStatus = v !== undefined ? v : true; + this.setCert = certStatus; + this.setCertSubject.next(certStatus); + this.setCertSubject.complete(); + }), catchError((err) => { + this.logger.info('Failed setServerCertificate'); + this.setCert = false; + this.setCertSubject.error(err); + return VOID; + }), switchMap(() => { + return this.setCertSubject; + })); + } + return this.setCertSubject; + }), catchError((err) => { + this.logger.info(`setServerCertificate err=${err.message}`); + throw err; + })); + } + ensureKeyContext(decryptdata) { + const keyuri = decryptdata.uri; + if (!this.keyUriToKeyInfo[keyuri]) { + this.keyUriToKeyInfo[keyuri] = new KeyContext(decryptdata); + } + const keyInfo = this.keyUriToKeyInfo[keyuri]; + if (keyInfo.session) { + return keyInfo; + } + if (this.useSingleKeySession && this.sessions.length > 0) { + keyInfo.session = this.sessions[0].session; + return keyInfo; + } + this.logger.info(`${this.systemString} createSession`); + const session = this.mediaKeys.createSession(); + if (session) { + this.sessions.push(new MediaKeySessionContext(session, this.onkeystatuseschange, this.onkeymessage, this.logger)); + } + else { + this.logger.info(`${this.systemString} FAIL: could not create MediaKeysSession`); + } + keyInfo.session = session; + return keyInfo; + } + /** + * Start a key request. called from key-system controller or internally on renewal. + * + * @param decryptdata DecryptData object from EXT-X-KEY + * @returns Promise (decryptdata) + */ + startKeyRequest(decryptdata) { + const keyuri = decryptdata.uri; + const keyInfo = this.ensureKeyContext(decryptdata); + const session = keyInfo === null || keyInfo === void 0 ? void 0 : keyInfo.session; + if (!session) { + return throwError(new KeySystemError('Could not create key session', decryptdata.uri, 0, ErrorResponses.KeySystemFailedToCreateSession, this.systemString)); + } + const keyIdStr = BufferUtils.utf8arrayToStr(decryptdata.keyId); + this.keyIdToKeyInfo[keyIdStr] = keyInfo; + this.logger.info(`${this.systemString} startKeyRequest ${printKeyTag(decryptdata)} state=${keyInfo.requestState}`); + this.logger.qe({ critical: true, name: 'startKeyRequest', data: { uri: keyuri, keyId: keyIdStr } }); + return forkJoin([this.getKeyRequestInfo(keyInfo), this.setServerCertificate()]).pipe(map((values) => values[0]), switchMap((requestInfo) => { + var _a; + (_a = this.sessionHandler) === null || _a === void 0 ? void 0 : _a.licenseChallengeSubmitted({ keyuri: keyuri, keyFormat: this.systemString }); + return this.generateLicenseChallenge(keyInfo, requestInfo).pipe(catchError((error) => { + var _a; + this.logger.info(`${this.systemString} generateLicenseChallenge Failed ${error.message} ${printKeyTag(decryptdata)}`); + (_a = this.sessionHandler) === null || _a === void 0 ? void 0 : _a.licenseChallengeError({ keyuri: keyuri }); + throw error; + })); + }), switchMap((licenseChallenge) => { + var _a; + this.logger.info(`${this.systemString} challenge created ${printKeyTag(decryptdata)}`); + this.logger.qe({ critical: true, name: 'challengeCreated', data: { uri: keyuri, keyId: keyIdStr } }); + (_a = this.sessionHandler) === null || _a === void 0 ? void 0 : _a.licenseChallengeCreated({ keyuri: keyuri, cdmVersion: this.cdmVersion }); + return this.getKeyRequestResponse(keyInfo, licenseChallenge); + }), switchMap((parsedKeyResponse) => { + var _a; + this.logger.info(`${this.systemString} onKeyResponseParsed ${printKeyTag(decryptdata)}`); + this.logger.qe({ critical: true, name: 'onKeyResponseParsed', data: { uri: keyuri, keyId: keyIdStr } }); + (_a = this.sessionHandler) === null || _a === void 0 ? void 0 : _a.licenseResponseSubmitted({ keyuri: keyuri }); + return this.handleParsedKeyResponse(keyInfo, parsedKeyResponse).pipe(catchError((error) => { + var _a; + this.logger.info(`${this.systemString} ParseKeyResponse Failed ${printKeyTag(decryptdata)} error=${error.message}`); + (_a = this.sessionHandler) === null || _a === void 0 ? void 0 : _a.licenseResponseError({ keyuri: keyuri }); + throw error; + })); + }), map(() => { + var _a; + this.logger.info(`${this.systemString} New usable key: ${printKeyTag(decryptdata)} CDMVersion=${this.cdmVersion}`); + this.logger.qe({ critical: true, name: 'newUsableKey', data: { uri: keyuri, keyId: keyIdStr } }); + (_a = this.sessionHandler) === null || _a === void 0 ? void 0 : _a.licenseResponseProcessed({ keyuri: keyuri }); + keyInfo.setKeyRequestState(KeyRequestState.NONE); + this.removeSessions(keyInfo.decryptdata, false).subscribe(); + return decryptdata; // Resolve + }), catchError((err) => { + this.handleKeyExchangeError(keyInfo, err); + throw err; + }), + // takeUntil(this.destroy$), + finalize$1(() => { + this._abortKeyRequest(keyInfo); + })); + } + /** + * Internal cleanup of key request + * @param keyInfo The key request to abort + */ + _abortKeyRequest(keyInfo) { + var _a; + if (keyInfo) { + const keyuri = keyInfo.decryptdata.uri; + const keyIdStr = BufferUtils.utf8arrayToStr(keyInfo.decryptdata.keyId); + if (keyInfo.requestState !== KeyRequestState.NONE) { + this.logger.info(`Aborting key ${redactUrl(keyuri)} state=${keyInfo.requestState}`); + this.logger.qe({ critical: true, name: 'keyAborted', data: { uri: keyuri, keyId: keyIdStr } }); + (_a = this.sessionHandler) === null || _a === void 0 ? void 0 : _a.keyAborted({ keyuri }); + } + keyInfo.abort(); + } + } + // Do cleanup if needed + handleKeyExchangeError(keyInfo, err) { + keyInfo.error(err); // Mark as error so we don't erroneously report abort + const keyuri = keyInfo.decryptdata.uri; + const keyIdStr = BufferUtils.utf8arrayToStr(keyInfo.decryptdata.keyId); + this.logger.qe({ critical: true, name: 'keyExchangeError', data: { uri: keyuri, keyId: keyIdStr, errMsg: err.message } }); + } + updateItemId(itemId) { + // Update the itemId to which the key belongs to. The license release message carries this info. + // When you generate challenge this itemId was being used. + this.itemId = itemId; + } + /** + * Remove a key from the keysystem + * @param decryptdata + */ + removeKey(decryptdata) { + return this.removeKeyInternal(decryptdata); + } + removeKeyInternal(decryptdata) { + this.logger.info(`removing key ${redactUrl(decryptdata.uri)}`); + const keyInfo = this.keyUriToKeyInfo[decryptdata.uri]; + if (keyInfo && keyInfo.session) { + const session = keyInfo.session; + keyInfo.abort(); + keyInfo.destroy(); + const keyIdStr = BufferUtils.utf8arrayToStr(decryptdata.keyId); + this.keyIdToKeyInfo[keyIdStr] = undefined; + this.keyUriToKeyInfo[decryptdata.uri] = undefined; + return this.removeSession(session); + } + } + removeSessions(decryptdata, shouldRemoveCurrentSession) { + const keyInfo = this.keyUriToKeyInfo[decryptdata.uri]; + if (keyInfo) { + // Remove all previous sessions + const oldRemoves = keyInfo.oldSessions.map((session) => { + return this.removeSession(session); + }); + keyInfo.oldSessions = []; + return iif(() => oldRemoves.length === 0, VOID, forkJoin(oldRemoves)).pipe(switchMap(() => { + if (shouldRemoveCurrentSession) { + // Finally remove the current session + return this.removeKeyInternal(decryptdata); + } + return VOID; + })); + } + return VOID; + } + removeSession(session) { + this.logger.info(`remove session ${session.sessionId}`); + const index = this.sessions.findIndex((sessionCtx) => { + return sessionCtx.session === session; + }); + const sessionCtx = this.sessions[index]; + if (index > -1) { + this.sessions.splice(index, 1); + } + this.sessionIdToKeyUri[session.sessionId] = undefined; + if (sessionCtx) { + return sessionCtx.destroy(); + } + return VOID; + } + getKeyRequestInfo(keyInfo) { + const decryptdata = keyInfo.decryptdata; + const keyuri = decryptdata.uri; + const obs = keyInfo.setKeyRequestState(KeyRequestState.GET_REQUEST_INFO); + this.eventEmitter.trigger(HlsEvent$1.KEY_REQUEST_STARTED, { keyuri: keyuri, decryptdata: decryptdata, timestamp: Date.now() }); + return obs; + } + setKeyRequestInfo(keyuri, requestInfo) { + const keyInfo = this.keyUriToKeyInfo[keyuri]; + if (!keyInfo) { + this.logger.info(`Unexpected key requested ${redactUrl(keyuri)}`); + return; + } + keyInfo.resolveState(KeyRequestState.GET_REQUEST_INFO, requestInfo); + } + sanitizeRequest(requestInfo) { + return requestInfo; + } + /** + * Generate a license challenge for the key + * @param keyInfo The key we're requesting + * @param requestInfo Additional parameters for making the key request + * @param state MediaKeyStatus for the keyId associated with keyInfo + */ + generateLicenseChallengeInternal(keyInfo, requestInfo, state) { + if (state === 'status-pending') { + this.logger.info('Request already in progress'); + } + const decryptdata = keyInfo.decryptdata; + const session = keyInfo.session; + const keyuri = decryptdata.uri; + const keyId = decryptdata.keyId; + let generateRequest$; + const getKeyResponse$ = keyInfo.setKeyRequestState(KeyRequestState.GET_CHALLENGE); + if (!session.generateRequestPromise) { + const initDataResult = this.generateInitData(keyId, decryptdata, requestInfo); + this.logger.info(`challenge create start uri=${redactUrl(keyInfo.decryptdata.uri)} versions=${JSON.stringify(keyInfo.decryptdata.formatversions)}`); + session.generateRequestPromise = session.generateRequest(initDataResult.initDataType, initDataResult.initData); + // May only be called once + generateRequest$ = scheduled(session.generateRequestPromise, queueScheduler).pipe(tap(() => { + this.sessionIdToKeyUri[session.sessionId] = keyuri; + }), catchError((reason) => { + this.logger.info(`${this.systemString} FAIL: generateRequest fail keyuri=${redactUrl(keyInfo.decryptdata.uri)} message=${reason.message}`); + throw new KeySystemError(reason.message, keyInfo.decryptdata.uri, 0, ErrorResponses.KeySystemFailedToGenerateLicenseRequest, this.systemString); + })); + } + else { + // If generateRequest was already called + generateRequest$ = scheduled(session.generateRequestPromise, queueScheduler).pipe(switchMap(() => this.generateRequestInitialized(keyInfo, state === 'usable'))); + } + return forkJoin([getKeyResponse$, generateRequest$]).pipe(map((result) => new Uint8Array(result[0]))); + } + /** + * This is different from startKeyRequest in that it could be triggered + * from an externally generated event. (Hls.generateKeyRequest) + * + * @param keyInfo Key info object + * @param requestInfo Extra information needed to make this request + */ + generateLicenseChallenge(keyInfo, requestInfo) { + const decryptdata = keyInfo.decryptdata; + const session = keyInfo.session; + const keyuri = decryptdata.uri; + const keyId = decryptdata.keyId; + const keyIdStr = BufferUtils.utf8arrayToStr(decryptdata.keyId); + this.logger.info(`${this.systemString} generateLicenseChallenge ${printKeyTag(decryptdata)}`); + this.logger.qe({ critical: true, name: 'generateLicenseChallenge', data: { uri: keyuri, keyId: keyIdStr } }); + if (keyInfo.licenseChallenge) { + // This is handling for key systems like Widevine where the license challenge for renewal is already generated and we just need to use it. + keyInfo.resolveState(KeyRequestState.GET_CHALLENGE, keyInfo.licenseChallenge); + } + requestInfo = this.sanitizeRequest(requestInfo); + keyInfo.requestInfo = requestInfo; + // In gapless mode itemId needs to be updated to the newly loading item + //this.itemId = this.hls.inGaplessMode ? this.hls.loadingItem.itemId : this.itemId; + this.sessionId = this.sessionId || (requestInfo && requestInfo.sessionId) || this.itemId; // Use first id + let state; + // If it's playready Key system we need to change the endianness of the keyid in the key status + if (this.systemString === PlayReadyKeySystemProperties.systemStringPrefix) { + // Use a different copy of KID, otherwise it will change the endianness of KID in DecryptData + // since it's a referrence + const curKid = new Uint8Array(keyId); + keysystemutil.changeEndianness(curKid); + state = session.keyStatuses.get(curKid); + } + else { + state = session.keyStatuses.get(keyId); // Keysystem state + } + let createRequestPromise; + switch (state) { + case 'status-pending': // Challenge created or key known + case 'usable': // Finished key request already, should renew + case 'expired': + case undefined: { + createRequestPromise = this.generateLicenseChallengeInternal(keyInfo, requestInfo, state); + break; + } + default: + createRequestPromise = throwError(new KeySystemError(`Bad internal state state=${state}`, keyuri, 0, ErrorResponses.KeySystemUnexpectedState, this.systemString)); + break; + } + return createRequestPromise; + } + /** + * Set the parsed license response + * @param keyuri URI of key corresponding to request + * @param parsedResponse The parsed response for the key request. Specific to key system + */ + setParsedResponse(keyuri, parsedResponse) { + const keyInfo = this.keyUriToKeyInfo[keyuri]; + if (keyInfo) { + keyInfo.resolveState(KeyRequestState.GET_KEY_RESPONSE, parsedResponse); + } + else { + this.logger.info(`${this.systemString} no keyInfo for keyuri: ${redactUrl(keyuri)}`); + } + } + // keystatuseschange handler + handleKeyStatusesChange(event) { + const ks = event.target; + ks.keyStatuses.forEach((status, keyid, parent) => { + const currentKeyId = new Uint8Array(keyid); + // If it's playready Key system we need to change the endianness of the keyid in the key status + if (this.systemString === PlayReadyKeySystemProperties.systemStringPrefix) { + keysystemutil.changeEndianness(currentKeyId); + } + const keyIdStr = BufferUtils.utf8arrayToStr(currentKeyId); + const keyInfo = this.keyIdToKeyInfo[keyIdStr]; + if (keyInfo) { + this.handleKeyStatusForKey(status, keyInfo); + } + }); + } + handleKeyStatusForKey(status, keyInfo) { + if (!keyInfo) { + return; + } + const decryptdata = keyInfo.decryptdata; + const keyuri = decryptdata.uri; + switch (status) { + case 'internal-error': { + this.logger.error(`${this.systemString} internal-error for key ${printKeyTag(decryptdata)}`); + this._signalError(keyInfo, new KeyRequestError('Got internal error from key system', keyuri, 0, ErrorResponses.KeySystemInternalError, false, KeyRequestErrorReason.InternalError)); + break; + } + case 'usable': { + if (keyInfo.requestState === KeyRequestState.PROCESS_LICENSE) { + keyInfo.resolveState(KeyRequestState.PROCESS_LICENSE, undefined); + } + break; + } + case 'output-restricted': { + this.logger.warn(`${this.systemString} output-restricted for key ${printKeyTag(decryptdata)}`); + if (keyInfo.session) { + // Cleanup the session, so that we don't keep the keys and try to renew it later + this.removeSession(keyInfo.session).pipe(takeUntil(this.destroy$)).subscribe(); + } + this._signalError(keyInfo, new KeyRequestError('output-restricted', keyuri, 0, ErrorResponses.KeySystemOutputRestricted, false, KeyRequestErrorReason.OutputRestricted)); + break; + } + case 'expired': { + this.logger.warn(`${this.systemString} expired for key ${printKeyTag(decryptdata)}. Attempting renewal`); + this._signalRenewal(keyInfo); + break; + } + default: + this.logger.info(`key status ${printKeyTag(decryptdata)} ${status}`); + break; + } + } + _scheduleRenewal(keyInfo, renewalMs) { + this.logger.info(`${this.systemString} Scheduling renewal for ${printKeyTag(keyInfo.decryptdata)} in ${renewalMs} ms`); + timer(renewalMs) + .pipe(tap(() => this._signalRenewal(keyInfo)), takeUntil(race(keyInfo.destroy$, this.destroy$, waitFor(keyInfo.onKeyRequestState$, (state) => state === KeyRequestState.GET_REQUEST_INFO)))) + .subscribe(); + } + _signalRenewal(keyInfo) { + this._keyStatusChange$.next({ decryptdata: keyInfo.decryptdata, status: 'needs-renewal' }); + } + _signalError(keyInfo, error) { + this._keyStatusChange$.next({ decryptdata: keyInfo.decryptdata, status: 'error', error }); + keyInfo.error(error); + } + } + + const kClearKeySystemString = 'org.w3.clearkey'; + const ClearKeySecurityLevels = { + NONE: 0, + }; + const ClearKeySystemProperties = { + id: 'clearkey', + systemStringPrefix: kClearKeySystemString, + keyFormatString: kClearKeySystemString, + securityLevels: ClearKeySecurityLevels, + }; + + /** + * 2018 Apple Inc. All rights reserved. + */ + const KeySystemIdValues = ['clearkey', 'fairplaystreaming', 'playready', 'widevine']; + + /** + * Clear key system implementation + * + * Uses July 2016 spec: + * https://www.w3.org/TR/2016/CR-encrypted-media-20160705/ + * + * 2018 Apple Inc. All rights reserved. + */ + // default configuration to pass into requestMediaKeySystemAccess + const kClearKeySystemConfig = { + initDataTypes: ['keyids', 'cenc'], + }; + class ClearKeySystem extends KeySystem { + constructor(mediaKeys, keySystemString, config, eventEmitter, sessionHandler, logger) { + super(mediaKeys, keySystemString, config, eventEmitter, false, sessionHandler, logger); + } + // destroy() + static get requestAccessConfig() { + return kClearKeySystemConfig; + } + get needsCert() { + return false; + } + getKeyRequestResponse(keyInfo, licenseChallenge) { + const keyuri = keyInfo.decryptdata.uri; + const loaderConfig = { + maxLoadTimeMs: 0, + maxTimeToFirstByteMs: 0, + autoRetry: false, + timeoutRetry: null, + errorRetry: null, + }; + return fromUrlArrayBuffer({ url: keyuri, xhrSetup: this.config.xhrSetup }, loaderConfig).pipe(map(([loadArrayBufferResult, _]) => { + const key = new Uint8Array(loadArrayBufferResult); + const response = { + response: this.parseResponse(licenseChallenge, key), + }; + return response; + })); + } + parseResponse(licenseChallenge, key) { + const keyObj = { + kty: 'oct', + kid: NumericEncodingUtils$1.base64UrlEncode(licenseChallenge), + k: NumericEncodingUtils$1.base64UrlEncode(key), + }; + return BufferUtils.strToUtf8array(JSON.stringify({ keys: [keyObj] })); + } + handleParsedKeyResponse(keyInfo, keyResponse) { + const responseBlob = keyResponse.response; + const processLicense = keyInfo.setKeyRequestState(KeyRequestState.PROCESS_LICENSE); + const updateSession = from(keyInfo.session.update(responseBlob)).pipe(tap(() => { + const keyId = keyInfo.decryptdata.keyId; + const state = keyInfo.session.keyStatuses.get(keyId); + this.handleKeyStatusForKey(state, keyInfo); + }), catchError((reason) => { + const response = { code: reason.code, text: 'Failed to update with key response' }; + const err = new KeySystemError(reason.message, keyInfo.decryptdata.uri, reason.code, response, this.systemString); + throw err; + })); + return forkJoin([processLicense, updateSession]).pipe(mapTo(undefined)); + } + generateInitData(keyId /* decryptdata, requestInfo*/) { + return { initData: keysystemutil.makeKeyIdsInitData([keyId]), initDataType: 'keyids' }; + } + generateRequestInitialized() { + return VOID; + } + sanitizeRequest(requestInfo) { + return requestInfo; + } + // message + handleKeyMessage(event) { + if (this.isDestroying) { + this.logger.info('In the middle of destroying, ignore key message'); + return; + } + if (event.messageType !== 'license-request') { + this.logger.info(`${this.systemString} Unexpected key message type ${event.messageType}`); + return; + } + const message = new Uint8Array(event.message); + const request = JSON.parse(BufferUtils.utf8arrayToStr(message).trim()); + if (request.kids.length !== 1) { + this.logger.info(`${this.systemString} Unexpected number of keyIds ${request.kids.length}`); + return; + } + const keyId = NumericEncodingUtils$1.base64Decode(request.kids[0]); // just care about the first for now + const keyIdStr = BufferUtils.utf8arrayToStr(keyId); + const keyInfo = this.keyIdToKeyInfo[keyIdStr]; + if (keyInfo) { + keyInfo.resolveState(KeyRequestState.GET_CHALLENGE, keyId); + } + else { + this.logger.info(`${this.systemString} no keyInfo for keyId ${JSON.stringify(keyId)}`); + } + } + } + var ClearKeySystem$1 = ClearKeySystem; + + /* + * FairPlayStreaming key system constants + * + * 2018 Apple Inc. All rights reserved. + */ + const kFairPlayStreamingKeySystemConfig = { + initDataTypes: ['cenc'], + }; + const kFairPlayStreamingKeySystemUUID = new Uint8Array([148, 206, 134, 251, 7, 255, 79, 67, 173, 184, 147, 210, 250, 150, 140, 162]); + var SchemeFourCC; + (function (SchemeFourCC) { + SchemeFourCC[SchemeFourCC["CENC"] = 1667591779] = "CENC"; + SchemeFourCC[SchemeFourCC["CBCS"] = 1667392371] = "CBCS"; + })(SchemeFourCC || (SchemeFourCC = {})); + /** + * Base class for FPS key systems + */ + class FairPlayStreamingKeySystemBase extends KeySystem { + constructor(mediaKeys, keySystemString, config, eventEmitter, useSingleKeySession, sessionHandler, logger) { + super(mediaKeys, keySystemString, config, eventEmitter, useSingleKeySession, sessionHandler, logger); + this._hasSetRenewal = false; + } + static get systemId() { + return kFairPlayStreamingKeySystemUUID; + } + static get requestAccessConfig() { + return kFairPlayStreamingKeySystemConfig; + } + get needsCert() { + return true; + } + sanitizeRequest(requestInfo) { + return { + assetId: requestInfo && requestInfo.assetId ? new Uint8Array(requestInfo.assetId) : undefined, + ssc: requestInfo && requestInfo.ssc ? new Uint8Array(requestInfo.ssc) : undefined, + sessionId: requestInfo && requestInfo.sessionId ? requestInfo.sessionId : undefined, + }; + } + _scheduleRenewal(keyInfo, renewalMs) { + if (!this.useSingleKeySession) { + // Each key has its own timer + super._scheduleRenewal(keyInfo, renewalMs); + } + else if (!this._hasSetRenewal) { + this._hasSetRenewal = true; + this.logger.info(`${this.systemString} Scheduling renewal in ${renewalMs} ms`); + timer(renewalMs) + .pipe(tap(() => { + // use first available keyInfo + const keyInfo = Object.values(this.keyUriToKeyInfo)[0]; + if (keyInfo) { + this._signalRenewal(keyInfo); + } + }), finalize$1(() => { + this._hasSetRenewal = false; + }), takeUntil(this.destroy$)) + .subscribe(); + } + } + /** + * Handle a parsed key response + * @param {Object} keyInfo Info about the key + * @param {Object} keyResponse The parsed key response object { statusCode, ckc, renewalData } + * @returns {Observable} message to pass to update() for processing license + */ + handleParsedKeyResponse(keyInfo, keyResponse) { + const keyuri = keyInfo.decryptdata.uri; + const statusCode = keyResponse.statusCode; + const licenseResponse = keyResponse.ckc && keyResponse.ckc.byteLength !== 0 ? keyResponse.ckc : keyResponse.license && keyResponse.license.byteLength !== 0 ? keyResponse.license : undefined; + // Special handling in case of HTTP Errors during key requests. + if (statusCode === 0 && !licenseResponse) { + return throwError(new KeyRequestError('License request resulted in HTTP Error', keyuri, statusCode, { code: statusCode, text: 'HTTP Error' }, true, KeyRequestErrorReason.HttpError)); + } + if (statusCode !== 0) { + return throwError(new KeyRequestError('License server responded with error', keyuri, statusCode, { code: statusCode, text: 'Server Error' }, false, KeyRequestErrorReason.LicenseServerError)); + } + if (!licenseResponse) { + const err = new KeyRequestError('License server responded with invalid license', keyuri, statusCode, { code: statusCode, text: 'Invalid license' }, false, KeyRequestErrorReason.LicenseServerError); + return throwError(err); + } + const renewalDate = keyResponse.renewalDate; + const now = new Date(); + const renewalMs = renewalDate > now ? renewalDate.getTime() - now.getTime() : 0; + if (renewalMs > 0) { + this._scheduleRenewal(keyInfo, renewalMs); + } + const ckcArray = this.makeProcessLicenseRequestMessage(keyInfo, licenseResponse, renewalMs); + // Only resolve if we got success, either from 'usable' keystatuschange event + // or on promise completion with 'usable' key status. Otherwise reject + const session = keyInfo.session; + const updateSession = from(session.update(ckcArray)).pipe(tap(() => { + const keyId = keyInfo.decryptdata.keyId; + const status = session.keyStatuses.get(keyId); + // Slightly faster than waiting for keystatuschange + this.handleKeyStatusForKey(status, keyInfo); + }), catchError((error) => { + const err = new KeySystemError(error.message, keyInfo.decryptdata.uri, 0, ErrorResponses.KeySystemFailedToUpdateSession, this.systemString); + throw err; + })); + const getLicense = keyInfo.setKeyRequestState(KeyRequestState.PROCESS_LICENSE); + return forkJoin([getLicense, updateSession]).pipe(mapTo(undefined)); + } + makeKeyRequests(requestInfoArray) { + const keyRequests = []; + for (const r of requestInfoArray) { + const decryptdata = r.decryptdata; + const requestInfo = r.requestInfo; + const keyId = decryptdata.keyId; + keyRequests.push({ + keyId: keyId, + assetId: requestInfo ? requestInfo.assetId : undefined, + ssc: requestInfo ? requestInfo.ssc : undefined, + versionList: decryptdata.formatversions, + }); + } + return keyRequests; + } + getSchemeAndFlags(decryptdata) { + const scheme = decryptdata.method === 'ISO-23001-7' ? SchemeFourCC.CENC : SchemeFourCC.CBCS; // TODO pass real scheme through. + const flags = 0; + // TODO + /* if (this.hls.playingItem.secureStop) { + flags |= 0x01; + } */ + return { scheme, flags }; + } + generateInitData(keyId, decryptdata, requestInfo) { + // Generate a PSSH + const { scheme, flags } = this.getSchemeAndFlags(decryptdata); + const keyRequests = this.makeKeyRequests([{ decryptdata, requestInfo }]); + const pssh = this.makeFpsKeySystemInitData(scheme, flags, keyRequests); + // console.log('PSSH=' + Hex.hexDump(pssh)); + return { initData: pssh, initDataType: 'cenc' }; + } + /** + * Generate a key renewal request on an already initialized session + * @param keyInfo Info about the key we're requesting + * @param isRenewal Is this a renewal requewst + * @returns promise for when update call is done. + **/ + generateRequestInitialized(keyInfo, isRenewal) { + tag(`[Keys] challenge create start uri=${redactUrl(keyInfo.decryptdata.uri)} versions=${JSON.stringify(keyInfo.decryptdata.formatversions)}`); + const message = this.makeKeyRequestMessage(keyInfo, isRenewal); + if (!message) { + const err = new KeyRequestError('Unable to generate request using existing keySession', keyInfo.decryptdata.uri, 0, ErrorResponses.KeySystemFailedToGenerateLicenseRequest, true, KeyRequestErrorReason.InvalidState); + return throwError(err); + } + return from(keyInfo.session.update(message)).pipe(tap(() => { + keyInfo.requestInfo = undefined; // Don't need requestInfo anymore. + }), catchError((reason) => { + tag(`[Keys] ${this.systemString} FAIL: generateRequestInitialized keyuri=${redactUrl(keyInfo.decryptdata.uri)} message=${reason.message}`); + throw new KeySystemError(reason.message, keyInfo.decryptdata.uri, 0, ErrorResponses.KeySystemFailedToGenerateLicenseRenewal, this.systemString); + })); + } + /** + * @param {Object} keyInfo Info about the key we're requesting + * @param {Uint8Array} licenseChallenge Challenge bytes + * @returns {Observable} Promise returning the license response + */ + getKeyRequestResponse(keyInfo, licenseChallenge) { + const keyuri = keyInfo.decryptdata.uri; + const getKeyResponse = keyInfo.setKeyRequestState(KeyRequestState.GET_KEY_RESPONSE); + this.eventEmitter.trigger(HlsEvent$1.LICENSE_CHALLENGE_CREATED, { + keyuri: keyuri, + licenseChallenge: licenseChallenge, + keysystem: this.systemString, + }); + return getKeyResponse; + } + resolveSPCPromise(keyInfo, spc) { + keyInfo.resolveState(KeyRequestState.GET_CHALLENGE, spc); + } + } + + /* + * FairPlayStreaming key system + * + * 2018 Apple Inc. All rights reserved. + */ + //declare var __SECURESTOP_KEYS__: IFPSSecureStopKeys; + //const SecureStopKeys = __SECURESTOP_KEYS__; + const SecureStopKeys = {}; + const MessageFourCC = { + NONE: 0, + CRKR: 1668442994, + RMKY: 1919773561, + CKCS: 1667982195, + CERT: 1667592820, + SPCS: 1936745331, + RNEW: 1919837559, + RLSE: 1919710053, + SSTP: 1936946288, + CDMI: 1667525993, + }; + const APIProviderId = { + CENC: 'EC396D13-FB13-4993-9D0D-71518ACF3D6F', + CBCS: 'F19BF03B-7470-41A4-9655-86D078307D59', + }; + /** + * Read a generic data field + * + * Format: 4B length of data, nB data bytes + * + * @param {Uint8Array} inBuf Buffer referenced by dv + * @param {DataView} dv DataView wrapper around inBuf + * @param {number} pos Starting position in buffer to read from + * @returns {Object} { pos: next byte to read, data: Uint8Array holding data parsed out of inBuf } + */ + function readDataField(inBuf, dv, pos) { + if (pos + 4 > inBuf.byteLength) { + return undefined; + } + const dataLen = dv.getUint32(pos); + pos += 4; + if (pos + dataLen > inBuf.byteLength) { + return undefined; + } + const data = inBuf.slice(pos, pos + dataLen); + pos += dataLen; + return { pos: pos, data: data }; + } + // Read message: + // cmd (4) + // arrayLen (4) + // keyId (16) + // dataLen (4) + // data (dataLen) + function parseData(message) { + const keyIdToData = {}; + const dv = new DataView(message.buffer); + let pos = 4; + const arrayLen = dv.getUint32(pos); + pos += 4; + for (let i = 0; i < arrayLen && pos < message.byteLength; ++i) { + if (pos + 16 > message.byteLength) { + break; + } + const keyId = message.slice(pos, pos + 16); + pos += 16; + if (pos + 4 > message.byteLength) { + break; + } + const tmp = readDataField(message, dv, pos); + if (tmp) { + pos = tmp.pos; + keyIdToData[BufferUtils.utf8arrayToStr(keyId)] = tmp.data; + } + else { + break; + } + } + return keyIdToData; + } + function addDataField(outBuf, dv, pos, data) { + const len = data ? data.byteLength : 0; + dv.setUint32(pos, len); + if (len) { + outBuf.set(data, pos + 4); + } + pos += 4 + len; + return pos; + } + // Including 4 byte arrayLen + function getKeyRequestArraySize(keyRequests) { + // arrayLen (4) + // --------- For each key: + // keyId(16) + // assetIdLen (4), assetId + // sscLen (4), ssc + // versionLen (4), versionList (each one 4B) + let bytesForArray = 4; + for (const kr of keyRequests) { + bytesForArray += 28 + (kr.assetId ? kr.assetId.byteLength : 0) + (kr.ssc ? kr.ssc.byteLength : 0) + (kr.versionList ? kr.versionList.length * 4 : 0); + } + return bytesForArray; + } + /** + * Fill existing array w/ key request data + * @param array uint8array + * @param dv dataview for array + * @param pos position to write to within array + * @param keyRequests Array of key requests [ { assetId, ssc, versionList } ] + * @returns + */ + function addKeyRequestArray(array, dv, pos, keyRequests) { + dv.setUint32(pos, keyRequests.length); // arrayLen + pos += 4; + for (const kr of keyRequests) { + array.set(kr.keyId, pos); + pos += 16; + pos = addDataField(array, dv, pos, kr.assetId); + pos = addDataField(array, dv, pos, kr.ssc); + dv.setUint32(pos, kr.versionList ? kr.versionList.length : 0); + pos += 4; + if (kr.versionList) { + for (const vl of kr.versionList) { + dv.setUint32(pos, vl); + pos += 4; + } + } + } + // console.log(`addKeyRequestArray: ${Hex.hexDump(array)}`); + return pos; + } + function makeFpsKeySystemInitData$1(scheme, protocol, flags, keyRequests) { + // Generate a PSSH + // Data for FPS: + // scheme: CC 4B + // protocol: 1B + // flags: 3B + // keyrequestarray + const keyRequestBytes = getKeyRequestArraySize(keyRequests); + let pos = 0; + const data = new Uint8Array(8 + keyRequestBytes); + const dv = new DataView(data.buffer); + dv.setUint32(pos, scheme); // schm + dv.setUint32(pos + 4, (protocol << 24) | (flags & 16777215)); // version | flags + pos += 8; + pos = addKeyRequestArray(data, dv, pos, keyRequests); + // console.log(`makeFpsKeySystemInitData: ${Hex.hexDump(data)}`); + return data; + } + function makeCreateKeyRequestMessage(keyRequests) { + // cmd (4) + // keyrequest array + // first calculate bytes to allocate + const bytesForArray = getKeyRequestArraySize(keyRequests); + let pos = 0; + const req = new Uint8Array(4 + bytesForArray); + const dv = new DataView(req.buffer); + dv.setUint32(0, MessageFourCC.CRKR); // cmd + pos += 4; + pos = addKeyRequestArray(req, dv, pos, keyRequests); + // console.log(`makeCreateKeyRequestMessage: ${Hex.hexDump(req)}`); + return req; + } + function makeProcessLicenseRequestMessage(licenses) { + // cmd (4) + // arrayLen (4) + // keyId (16) + // expiry (4) + // ckcLen (4) + // ckcBytes + // first calculate bytes to allocate + let bytesForArray = 0; + for (const l of licenses) { + bytesForArray += 24 + l.ckc.byteLength; + } + let pos = 0; + const req = new Uint8Array(8 + bytesForArray); + const dv = new DataView(req.buffer); + dv.setUint32(0, MessageFourCC.CKCS); // cmd + dv.setUint32(4, licenses.length); // arrayLen + pos += 8; + for (const l of licenses) { + req.set(l.keyId, pos); + pos += 16; + dv.setUint32(pos, l.expirySec); + pos += 4; + pos = addDataField(req, dv, pos, l.ckc); + } + // console.log(`makeProcessLicenseRequestMessage: ${Hex.hexDump(req)}`); + return req; + } + class FairPlayStreamingKeySystem extends FairPlayStreamingKeySystemBase { + constructor(mediaKeys, keySystemString, config, eventEmitter, sessionHandler, logger) { + const singleKeySession = typeof config.useMultipleKeySessions === 'undefined' || !config.useMultipleKeySessions; + super(mediaKeys, keySystemString, config, eventEmitter, singleKeySession, sessionHandler, logger); + } + makeProcessLicenseRequestMessage(keyInfo, licenseResponse, renewalMs) { + const ckcBytes = new Uint8Array(licenseResponse); + const ckcArray = makeProcessLicenseRequestMessage([ + { + keyId: keyInfo.decryptdata.keyId, + expirySec: renewalMs / 1000, + ckc: ckcBytes, + }, + ]); + return ckcArray; + } + makeFpsKeySystemInitData(scheme, flags, keyRequests) { + const protocol = 1; + const data = makeFpsKeySystemInitData$1(scheme, protocol, flags, keyRequests); + const pssh = MP4$1.pssh(FairPlayStreamingKeySystem.systemId, [], data); + return pssh; + } + makeKeyRequestMessage(keyInfo /* , isRenewal*/) { + const crkrArray = makeCreateKeyRequestMessage([ + { + keyId: keyInfo.decryptdata.keyId, + assetId: keyInfo.requestInfo ? keyInfo.requestInfo.assetId : undefined, + ssc: keyInfo.requestInfo ? keyInfo.requestInfo.ssc : undefined, + versionList: keyInfo.decryptdata.formatversions, + }, + ]); + return crkrArray; + } + handleKeyMessage(event) { + if (event.message.byteLength < 4) { + this.logger.warn('Unexpected message'); + return; + } + // event.message (ArrayBuffer) + const message = new Uint8Array(event.message); + const dv = new DataView(event.message); + const cmd = dv.getUint32(0); + if (this.isDestroying && cmd !== MessageFourCC.RLSE) { + this.logger.warn(`In the middle of destroying, ignore command: ${cmd.toString(16)}`); + return; + } + this.logger.info(`[Keys] ${this.systemString} message 0x${cmd.toString(16)}`); + switch (cmd) { + case MessageFourCC.CERT: + this.logger.warn('Certificate not set!'); + break; + case MessageFourCC.RNEW: { + const keyIdToData = parseData(message); + for (const keyIdStr in keyIdToData) { + if (Object.prototype.hasOwnProperty.call(keyIdToData, keyIdStr)) { + const keyInfo = this.keyIdToKeyInfo[keyIdStr]; + if (keyInfo) { + this._signalRenewal(keyInfo); + } + } + } + break; + } + case MessageFourCC.SPCS: { + const keyIdToData = parseData(message); + for (const keyIdStr in keyIdToData) { + if (Object.prototype.hasOwnProperty.call(keyIdToData, keyIdStr)) { + const keyInfo = this.keyIdToKeyInfo[keyIdStr]; + const spc = keyIdToData[keyIdStr]; + if (keyInfo && spc) { + this.resolveSPCPromise(keyInfo, spc); + } + } + } + break; + } + case MessageFourCC.RLSE: { + this._handleLicenseRelease(message); + break; + } + case MessageFourCC.CDMI: { + // Message length = 4 + // CDM version = remaining data + const pos = 4; + const tmp = readDataField(message, dv, pos); + if (tmp) { + const cdmVersion = (this.cdmVersion = BufferUtils.utf8arrayToStr(tmp.data)); + this.logger.info(`[Keys] ${this.systemString} CDM Version : ${cdmVersion}`); + } + break; + } + default: + this.logger.warn(`Unrecognized command:'0x${cmd.toString(16)}'`); + break; + } + } + _handleLicenseRelease(message) { + const releaseRecord = {}; + const dv = new DataView(message.buffer); + const type = dv.getUint32(4); // Release type + switch (type) { + case MessageFourCC.SSTP: { + // Secure stop + if (!SecureStopKeys) { + this.logger.warn('No secure stop keys defined'); + break; + } + releaseRecord[SecureStopKeys.SessionId] = this.sessionId; + // scheme (uint32) + // movieIdLen (uint32) + // movieIdStr + // secureStopSPCLen (uint32) + // secureStopSPC + // sessionLifespanSPCLen (uint32) + // sessionLifespanSPC + let pos = 8; + if (pos + 4 > message.byteLength) { + break; + } + releaseRecord[SecureStopKeys.APIProvider] = dv.getUint32(pos) === SchemeFourCC.CENC ? APIProviderId.CENC : APIProviderId.CBCS; + pos += 4; + let tmp = readDataField(message, dv, pos); + if (tmp) { + pos = tmp.pos; + releaseRecord[SecureStopKeys.MovieID] = BufferUtils.utf8arrayToStr(tmp.data); + } + else { + break; + } + tmp = readDataField(message, dv, pos); + if (tmp) { + pos = tmp.pos; + releaseRecord[SecureStopKeys.SecureStopSPC] = tmp.data; + } + else { + break; + } + tmp = readDataField(message, dv, pos); + if (tmp) { + pos = tmp.pos; + releaseRecord[SecureStopKeys.SessionLifespanSPC] = tmp.data; + } + else { + break; + } + break; + } + } + this.eventEmitter.trigger(HlsEvent$1.LICENSE_RELEASED, { + keysystem: this.systemString, + itemId: this.itemId, + releaseRecord: releaseRecord, + }); + } + } + var FPSKeySystem = FairPlayStreamingKeySystem; + + const FpsBoxTypes = { + fpsd: BufferUtils.strToUtf8array('fpsd'), + fpsi: BufferUtils.strToUtf8array('fpsi'), + fpsk: BufferUtils.strToUtf8array('fpsk'), + fkri: BufferUtils.strToUtf8array('fkri'), + fkai: BufferUtils.strToUtf8array('fkai'), + fkcx: BufferUtils.strToUtf8array('fkcx'), + fkvl: BufferUtils.strToUtf8array('fkvl'), // version list + }; + /** + * @param scheme cbcs or cenc + * @param flags keySystem flags + * @returns FpsKeySystemInitDataBox + */ + function makeFpsKeySystemInfoBox(scheme, flags) { + const schemeArray = new Uint8Array(4); + MP4$1.set32(scheme, schemeArray, 0); + return MP4$1.box(FpsBoxTypes.fpsi, new Uint8Array([ + 0, + (flags >> 16) & 255, + (flags >> 8) & 255, + flags & 255, + ]), schemeArray); + } + /** + * Generate a FpsKeyRequestBox + * @param {ArrayBuffer} keyId The key ID + * @param {ArrayBuffer} assetId AssetID + * @param {ArrayBuffer} ssc Remote secure context + * @param {Array} versionList A list of Number from EXT-X-KEY:KEYFORMATVERSIONS + * @returns {ArrayBuffer} a FpsKeyRequestBox + */ + function makeFpsKeyRequestBox(keyId, assetId, ssc, versionList) { + const args = [FpsBoxTypes.fpsk]; + // Mandatory + const fkri = MP4$1.box(FpsBoxTypes.fkri, new Uint8Array([0, 0, 0, 0]), keyId); + args.push(fkri); + // Optional boxes + if (assetId && assetId.byteLength) { + args.push(MP4$1.box(FpsBoxTypes.fkai, assetId)); + } + if (ssc && ssc.byteLength) { + args.push(MP4$1.box(FpsBoxTypes.fkcx, ssc)); + } + if (versionList && versionList.length) { + // List of integers + const versionListBuffer = new Uint8Array(4 * versionList.length); + let pos = 0; + for (const version of versionList) { + MP4$1.set32(version, versionListBuffer, pos); + pos += 4; + } + args.push(MP4$1.box(FpsBoxTypes.fkvl, versionListBuffer)); + } + const fpsk = MP4$1.box.apply(null, args); + return fpsk; + } + /** + * @param {SchemeFourCC} scheme cbcs or cenc (fourCC) + * @param {number} flags keySystem flags (number) + * @param {Array} keyRequests array of requests [ { keyId, assetId, ssc, versionList } ] + * @returns {Uint8Array} a PSSH box for initializing the key system + */ + function makeFpsKeySystemInitData(scheme, flags, keyRequests) { + // Mandatory box for key system initialization + const args = [FpsBoxTypes.fpsd, makeFpsKeySystemInfoBox(scheme, flags)]; + for (const kr of keyRequests) { + args.push(makeFpsKeyRequestBox(kr.keyId, kr.assetId, kr.ssc, kr.versionList)); + } + const data = MP4$1.box.apply(null, args); + const pssh = MP4$1.pssh(FairPlayStreamingKeySystemBase.systemId, null, data); + return pssh; + } + class FairPlayStreamingKeySystemV3 extends FairPlayStreamingKeySystemBase { + constructor(mediaKeys, keySystemString, config, eventEmitter, sessionHandler, logger) { + super(mediaKeys, keySystemString, config, eventEmitter, false, sessionHandler, logger); + this.sessions = []; + this.keyIdToKeyInfo = {}; + this.keyUriToKeyInfo = {}; + this.sessionIdToKeyUri = {}; + } + static get needsCert() { + return true; + } + handleKeyExchangeError(keyInfo, err) { + // If at any point we had an error during key exchange we need to destroy this session + this.removeKey(keyInfo.decryptdata).subscribe(); + super.handleKeyExchangeError(keyInfo, err); + } + _abortKeyRequest(keyInfo) { + if (!this.isDestroying && keyInfo && keyInfo.requestState !== KeyRequestState.NONE) { + this.removeKey(keyInfo.decryptdata).subscribe(); + } + return super._abortKeyRequest(keyInfo); + } + makeFpsKeySystemInitData(scheme, flags, keyRequests) { + return makeFpsKeySystemInitData(scheme, flags, keyRequests); + } + makeKeyRequestMessage(keyInfo, isRenewal) { + if (isRenewal) { + return BufferUtils.strToUtf8array('renew'); + } + return undefined; + } + makeProcessLicenseRequestMessage(keyInfo, licenseResponse, renewalMs) { + const responseStr = JSON.stringify([ + { + keyID: NumericEncodingUtils$1.base64Encode(keyInfo.decryptdata.keyId), + payload: NumericEncodingUtils$1.base64Encode(new Uint8Array(licenseResponse)), + }, + ]); + this.logger.debug(`[Keys] processLicense msg=${responseStr}`); + return BufferUtils.strToUtf8array(responseStr); + } + handleKeyMessage(event) { + const session = event.target; + const sessionId = session.sessionId; + const messageType = event.messageType; + this.logger.info(`[Keys] onKeyMessage sessionId=${sessionId} type=${messageType}`); + const keyuri = this.sessionIdToKeyUri[sessionId]; + let keyInfo; + if (!keyuri) { + // key message could happen before generateRequest() promise completes + for (const ki of Object.values(this.keyUriToKeyInfo)) { + if (ki && ki.session === session) { + keyInfo = ki; + } + } + } + else { + keyInfo = this.keyUriToKeyInfo[keyuri]; + } + if (!keyInfo) { + this.logger.warn('[Keys] No key associated with session'); + return; + } + switch (messageType) { + case 'license-request': { + const message = new Uint8Array(event.message); + const messagestr = BufferUtils.utf8arrayToStr(message); + try { + // Expect only one but we'll handle all of them anyway + const spcArray = JSON.parse(messagestr); + spcArray.forEach((payload) => { + const keyIdStr = NumericEncodingUtils$1.base64DecodeToStr(payload.keyID); + const spc = NumericEncodingUtils$1.base64Decode(payload.payload); + const keyInfo = this.keyIdToKeyInfo[keyIdStr]; + if (keyInfo) { + this.resolveSPCPromise(keyInfo, spc); + } + }); + } + catch (error) { + this.logger.warn('[Keys] got unexpected license-request format'); + this.resolveSPCPromise(keyInfo, message); // Last ditch effort + } + break; + } + case 'license-renewal': { + const spc = new Uint8Array(event.message); + this.resolveSPCPromise(keyInfo, spc); + break; + } + case 'license-release': + this._handleLicenseRelease(session); + break; + default: + this.logger.warn(`[Keys] Unexpected messageType ${messageType}`); + break; + } + } + _handleLicenseRelease(keySession) { + keySession.update(BufferUtils.strToUtf8array('acknowledged')).catch((error) => { + this.logger.error(`Promise error: ${error.message}`); + }); + } + } + var FPSKeySystemV3 = FairPlayStreamingKeySystemV3; + + /** + * PlayReady key system + */ + const kPlayReadyKeySystemConfig = { + initDataTypes: ['cenc'], + }; + const kPlayReadyKeySystemUUID = new Uint8Array([154, 4, 240, 121, 152, 64, 66, 134, 171, 146, 230, 91, 224, 136, 95, 149]); + class PlayReadyKeySystem extends KeySystem { + constructor(mediaKeys, systemString, config, eventEmitter, sessionHandler, logger) { + super(mediaKeys, systemString, config, eventEmitter, false, sessionHandler, logger); + this.shouldDestroyMediaKeys = true; + } + // destroy() + static get systemId() { + return kPlayReadyKeySystemUUID; + } + static get requestAccessConfig() { + return kPlayReadyKeySystemConfig; + } + get needsCert() { + return false; + } + generateInitData(keyId, decryptdata /* , requestInfo */) { + const data = decryptdata.pssh; + const pssh = MP4$1.pssh(PlayReadyKeySystem.systemId, [], data); + // console.log('PSSH=' + Hex.hexDump(pssh)); + return { initData: pssh, initDataType: 'cenc' }; + } + removeKey(decryptdata) { + return super.removeSessions(decryptdata, true); + } + ensureKeyContext(decryptdata) { + const keyuri = decryptdata.uri; + const keyInfo = this.keyUriToKeyInfo[keyuri]; + // Create a new session for current key request and preserve previous sessions for + // cleanup later + if (keyInfo === null || keyInfo === void 0 ? void 0 : keyInfo.session) { + keyInfo.oldSessions.push(keyInfo.session); + keyInfo.session = null; + } + return super.ensureKeyContext(decryptdata); + } + getKeyRequestResponse(keyInfo, licenseChallenge) { + const keyuri = keyInfo.decryptdata.uri; + const getKeyResponse = keyInfo.setKeyRequestState(KeyRequestState.GET_KEY_RESPONSE); + this.eventEmitter.trigger(HlsEvent$1.LICENSE_CHALLENGE_CREATED, { + keyuri: keyuri, + licenseChallenge: licenseChallenge, + keysystem: this.systemString, + keyId: keyInfo.decryptdata.keyId, + }); + return getKeyResponse; + } + /** + * Generate a key renewal request + * @param keyInfo Info about the key we're requesting + * @returns {Observable} Promise returning the license challenge + **/ + generateRequestInitialized(keyInfo) { + const keyuri = keyInfo.decryptdata.uri; + const challenge = keyInfo.licenseChallenge; + this.logger.debug(`[Keys] challenge create start uri=${redactUrl(keyuri)} versions=${JSON.stringify(keyInfo.decryptdata.formatversions)}`); + keyInfo.requestInfo = undefined; // Don't need requestInfo anymore. + keyInfo.resolveState(KeyRequestState.GET_CHALLENGE, challenge); + return VOID; + } + // Default behavior is to just update using data.response + handleParsedKeyResponse(keyInfo, keyResponse) { + const keyuri = keyInfo.decryptdata.uri; + const statusCode = keyResponse.statusCode; + if (statusCode !== 0) { + return throwError(new KeyRequestError('License server responded with error', keyuri, statusCode, { code: statusCode, text: 'Server error' }, false, KeyRequestErrorReason.LicenseServerError)); + } + if (!keyResponse.license || !keyResponse.license.byteLength) { + return throwError(new KeyRequestError('License server responded with invalid license', keyuri, statusCode, { code: statusCode, text: 'Invalid license' }, false, KeyRequestErrorReason.LicenseServerError)); + } + if (keyResponse.renewalDate) { + const renewalDate = keyResponse.renewalDate; + const now = new Date(); + const renewalMs = renewalDate > now ? renewalDate.getTime() - now.getTime() : 0; + if (renewalMs > 0) { + this._scheduleRenewal(keyInfo, renewalMs); + } + } + const processLicense = keyInfo.setKeyRequestState(KeyRequestState.PROCESS_LICENSE); + const updateSession = from(keyInfo.session.update(keyResponse.license)).pipe(tap(() => { + // PR CDM on Edge won't change key status till the buffers are pushed. To push buffer the key status should be usable + // So don't rely on key status to change the state like other key systems. + keyInfo.resolveState(KeyRequestState.PROCESS_LICENSE, undefined); + }), catchError((reason) => { + this.logger.error(`${this.systemString} FAIL: Failed to update with key response message=${reason.message}`); + const err = new KeySystemError(reason.message, keyInfo.decryptdata.uri, 0, ErrorResponses.KeySystemFailedToUpdateSession, this.systemString); + throw err; + })); + return forkJoin([processLicense, updateSession]).pipe(mapTo(undefined)); + } + // message + handleKeyMessage(event) { + if (this.isDestroying) { + this.logger.warn('In the middle of destroying, ignore key message'); + return; + } + const parser = new DOMParser(); + // Verify the message format encoding, based on that create the typed array + // TO DO + const messageBuffer = this.config.playReadyMessageFormat === 'utf16' ? new Uint16Array(event.message.buffer || event.message) : new Uint8Array(event.message.buffer || event.message); + const message = String.fromCharCode.apply(null, Array.from(messageBuffer)); + // Expected license challenge format + // + // + // base64Challenge + // + // + // + const keyMessage = parser.parseFromString(message, 'application/xml').getElementsByTagName('PlayReadyKeyMessage')[0]; + if (!keyMessage || keyMessage.getAttribute('type') !== 'LicenseAcquisition') { + this.logger.warn(`${this.systemString} unrecognized message ignore it`); + return; + } + const challengeNode = parser.parseFromString(message, 'application/xml').getElementsByTagName('Challenge')[0]; + if (!challengeNode || challengeNode.getAttribute('encoding') !== 'base64encoded' || challengeNode.childNodes.length === 0) { + this.logger.warn(`${this.systemString} wrong challenge format or empty challenge`); + return; + } + const challenge = NumericEncodingUtils$1.base64Decode(challengeNode.childNodes[0].nodeValue); + const challengeStr = BufferUtils.utf8arrayToStr(challenge); + // The license challenge contains the base64 encoded Key ID, extract it. + const keyData = parser.parseFromString(challengeStr, 'application/xml').getElementsByTagName('KID')[0]; + let keyIdBase64 = null; + if (keyData.childNodes[0]) { + keyIdBase64 = keyData.childNodes[0].nodeValue; + } + else { + keyIdBase64 = keyData.getAttribute('VALUE'); + } + // Using KeyID extract KeyInfo and Key URI + // KID value in PRO is a base64-encoded little endian GUID interpretation of UUID + // KID value in ‘tenc’ is a big endian UUID GUID interpretation of UUID + const keyId = NumericEncodingUtils$1.base64Decode(keyIdBase64).subarray(0, 16); + keysystemutil.changeEndianness(keyId); + const keyInfo = this.keyIdToKeyInfo[BufferUtils.utf8arrayToStr(keyId)]; + if (!keyInfo) { + this.logger.info(`${this.systemString} no keyInfo for keyId ${JSON.stringify(keyId)}`); + return; + } + keyInfo.licenseChallenge = challenge; + // Check demo/index.xml for more details on how exactly license is acquired. There is switch-case statement for LICENSE_CHALLENGE_CREATED + keyInfo.resolveState(KeyRequestState.GET_CHALLENGE, challenge); + } + } + var PlayReadyKeySystem$1 = PlayReadyKeySystem; + + /** + * Widevine key system + */ + const kWidevineKeySystemConfig = { + initDataTypes: ['cenc', 'keyids'], + }; + const kWidevineKeySystemUUID = new Uint8Array([237, 239, 139, 169, 121, 214, 74, 206, 163, 200, 39, 220, 213, 29, 33, 237]); + class WidevineKeySystem extends KeySystem { + constructor(mediaKeys, systemString, config, eventEmitter, sessionHandler, logger) { + super(mediaKeys, systemString, config, eventEmitter, false, sessionHandler, logger); + this.shouldDestroyMediaKeys = true; + } + // destroy() + static get systemId() { + return kWidevineKeySystemUUID; + } + static get requestAccessConfig() { + return kWidevineKeySystemConfig; + } + get needsCert() { + return true; + } + removeKey(decryptdata) { + return super.removeSessions(decryptdata, true); + } + ensureKeyContext(decryptdata) { + const keyuri = decryptdata.uri; + const keyInfo = this.keyUriToKeyInfo[keyuri]; + // Create a new session for current key request and preserve previous sessions for + // cleanup later + if (keyInfo === null || keyInfo === void 0 ? void 0 : keyInfo.session) { + keyInfo.oldSessions.push(keyInfo.session); + keyInfo.session = null; + } + return super.ensureKeyContext(decryptdata); + } + getKeyRequestResponse(keyInfo, licenseChallenge) { + const keyuri = keyInfo.decryptdata.uri; + const getKeyResponse = keyInfo.setKeyRequestState(KeyRequestState.GET_KEY_RESPONSE); + this.eventEmitter.trigger(HlsEvent$1.LICENSE_CHALLENGE_CREATED, { + keyuri: keyuri, + licenseChallenge: licenseChallenge, + keysystem: this.systemString, + keyId: keyInfo.decryptdata.keyId, + }); + return getKeyResponse; + } + handleParsedKeyResponse(keyInfo, keyResponse) { + // clear the license challenge + keyInfo.licenseChallenge = undefined; + const keyuri = keyInfo.decryptdata.uri; + const statusCode = keyResponse.statusCode; + if (statusCode !== 0) { + return throwError(new KeyRequestError('License server responded with error', keyuri, statusCode, { code: statusCode, text: 'Server error' }, false, KeyRequestErrorReason.LicenseServerError)); + } + if (!keyResponse.license || !keyResponse.license.byteLength) { + const err = new KeyRequestError('License server responded with invalid license', keyuri, statusCode, { code: statusCode, text: 'Invalid license' }, false, KeyRequestErrorReason.LicenseServerError); + return throwError(err); + } + if (keyResponse.renewalDate) { + const renewalDate = keyResponse.renewalDate; + const now = new Date(); + const renewalMs = renewalDate > now ? renewalDate.getTime() - now.getTime() : 0; + if (renewalMs > 0) { + this._scheduleRenewal(keyInfo, renewalMs); + } + } + const processLicense = keyInfo.setKeyRequestState(KeyRequestState.PROCESS_LICENSE); + const updateSession = from(keyInfo.session.update(keyResponse.license)).pipe(tap(() => { + const keyId = keyInfo.decryptdata.keyId; + const state = keyInfo.session.keyStatuses.get(keyId); + this.handleKeyStatusForKey(state, keyInfo); + }), catchError((reason) => { + this.logger.error(`${this.systemString} FAIL: Failed to update with key response code=${reason.code} message=${reason.message}`); + const err = new KeySystemError(reason.message, keyInfo.decryptdata.uri, reason.code, { code: reason.code, text: 'Failed to update with key response' }, this.systemString); + throw err; + })); + return forkJoin([processLicense, updateSession]).pipe(mapTo(undefined)); + } + /* + The pssh has to be generated from the manifest: + 1. Extract the keyID from the EXT-X-KEY tag (for example URI =“data:;base64,DZ/7Ld9qTnyTi92l+VPlBg==“) + 2. Generate the initdata using generateInitData + + generate Widevine pssh + 0:3 : size of the 'pssh' box + 4:7 : 'pssh' + 8:11 : not set (all zeros) + 12:27 : Widevine Key System (edef8ba9-79d6-4ace-a3c8-27dcd51d21ed) + 28:31 : Widevine pssh data size (size of what follows) + 32:51 : Protobuf-encoded Widevine pssh data + 32 & 33 : (id = 0, wireType = int, value = 1) means this is using AES-CTR + 34 & 35 : (id = 1, wireType = bytes, length = 16) size of the keyId + 36:51 : KeyId bytes + */ + generateInitData(keyId, decryptdata /* , requestInfo*/) { + return { initData: decryptdata.pssh, initDataType: 'cenc' }; + } + /** + * Generate a key renewal request + * @param keyInfo Info about the key we're requesting + * @returns {Observable} Promise returning the license challenge + **/ + generateRequestInitialized(keyInfo) { + const keyuri = keyInfo.decryptdata.uri; + const challenge = keyInfo.licenseChallenge; + //this.logger.info(`[Keys] challenge create start uri=${this.hls.redactUrl(keyuri)} versions=${JSON.stringify(keyInfo.decryptdata.formatversions)}`); + this.logger.debug(`[Keys] challenge create start uri=${redactUrl(keyuri)} versions=${JSON.stringify(keyInfo.decryptdata.formatversions)}`); + keyInfo.requestInfo = undefined; // Don't need requestInfo anymore. + keyInfo.resolveState(KeyRequestState.GET_CHALLENGE, challenge); + return VOID; + } + // message + handleKeyMessage(event) { + if (this.isDestroying) { + this.logger.warn('In the middle of destroying, ignore key message'); + return; + } + this.logger.info(`[Keys] ${this.systemString} message : ${event.messageType}`); + // Extract KeyUri and KeyInfo from the sessions info strored. + const session = event.target; + let keyuri = null; + let keyInfo = null; + if (session.sessionId in this.sessionIdToKeyUri) { + keyuri = this.sessionIdToKeyUri[session.sessionId]; + keyInfo = this.keyUriToKeyInfo[keyuri]; + } + else { + for (const [uri, info] of Object.entries(this.keyUriToKeyInfo)) { + if (info.session === session) { + keyuri = uri; + keyInfo = info; + break; + } + } + } + if (!keyInfo) { + this.logger.warn(`${this.systemString} empty keyuri and keyInfo`); + return; + } + const cmd = event.messageType; + switch (cmd) { + case 'license-request': { + // For widevine just base64 encode the challenge returned by the browser. + const challenge = new Uint8Array(event.message); + // Check demo/index.xml for more details on how exactly license is acquired. There is switch-case statement for LICENSE_CHALLENGE_CREATED + keyInfo.resolveState(KeyRequestState.GET_CHALLENGE, challenge); + break; + } + } + } + } + var WidevineKeySystem$1 = WidevineKeySystem; + + /* + * Factory for making a key system + * + * + */ + const kKeySystemIdToPropertiesMap$1 = { + clearkey: ClearKeySystemProperties, + fairplaystreaming: FairPlayStreamingKeySystemProperties, + playready: PlayReadyKeySystemProperties, + widevine: WidevineKeySystemProperties, + }; + const kKeySystemIdToConstructorsMap = { + clearkey: [['org.w3.clearkey', ClearKeySystem$1]], + fairplaystreaming: [ + ['com.apple.fps.3_0', FPSKeySystemV3], + ['com.apple.fps', FPSKeySystem], + ], + playready: [['com.microsoft.playready.recommendation', PlayReadyKeySystem$1]], + widevine: [['com.widevine.alpha', WidevineKeySystem$1]], + }; + const DEFAULT_KEY_SYSTEM = ClearKeySystemProperties.id; + /** + * Generate a KeySystem object given a KeySystemProperties + */ + class KeySystemFactory$1 { + createMediaKeys(keysystemId, videoCodecs, audioCodecs, logger, keySystemConfig) { + let res = KeySystemFactory$1.idToMediaKeysInfoMap[keysystemId]; + if (!res) { + logger.info(`[Keys] ${keysystemId} requesting key system access`); + const mediaCapabilities = keysystemutil.getCapabilities(videoCodecs, audioCodecs); + const mediaKeys$ = new AsyncSubject(); + KeySystemFactory$1.requestKeySystemAccess(keysystemId, mediaCapabilities, keySystemConfig, logger) + .pipe(switchMap(function (keySystemAccess) { + KeySystemFactory$1.idToMediaKeysInfoMap[keysystemId].keySystemAccess = keySystemAccess; + // TODO: probably should use keySystemAccess.getConfiguration() to get supported types. + logger.info(`[Keys] ${keysystemId} creating CDM`); + return from(keySystemAccess.createMediaKeys()); + }), tap((mediaKeys) => { + logger.info(`[Keys] ${keysystemId} created CDM`); + mediaKeys$.next(mediaKeys); + mediaKeys$.complete(); + }), catchError((err) => { + logger.info(`[Keys] FAIL: could not initialize ${keysystemId} key system: ${err.message}`); + mediaKeys$.error(new KeySystemError(`could not initialize key system: ${err.message}`, undefined, 0, ErrorResponses.KeySystemFailedToInitialize, keysystemId)); + return EMPTY; + }), takeUntil(KeySystemFactory$1.destroy$) // Keep alive until destroyMediaKeys + ) + .subscribe(); + res = KeySystemFactory$1.idToMediaKeysInfoMap[keysystemId] = { mediaKeys$, keySystemAccess: null }; + } + return res.mediaKeys$; + } + destroyMediaKeys() { + // Remove keysystemstring => requestKeySystemAccess promise + KeySystemFactory$1.destroy$.next(); + KeySystemFactory$1.idToMediaKeysInfoMap = {}; + } + static getKeySystemIdForDecryptData(decryptdata) { + let selectedId; + if (decryptdata) { + selectedId = DEFAULT_KEY_SYSTEM; + const formatString = decryptdata.format; + for (const id of KeySystemIdValues) { + const p = kKeySystemIdToPropertiesMap$1[id]; + if ((p === null || p === void 0 ? void 0 : p.keyFormatString) === formatString) { + selectedId = id; + break; + } + } + } + if (!selectedId) { + throw Error('No matching key system'); + } + return selectedId; + } + // Pick the first key system that works for this id + static requestKeySystemAccess(keysystemId, mediaCapabilities, keySystemConfig, logger) { + if (typeof navigator === 'undefined' || typeof navigator.requestMediaKeySystemAccess === 'undefined') { + return throwError(new KeySystemError('navigator undefined', undefined, 0, ErrorResponses.KeySystemUndefinedNavigator, keysystemId)); + } + const keySystemConstructors = kKeySystemIdToConstructorsMap[keysystemId]; + let obs = throwError(new KeySystemError('no key systems to try', undefined, 0, ErrorResponses.KeySystemNoKeySystemsToTry, keysystemId)); + for (const pair of keySystemConstructors) { + const keySystemString = pair[0]; + const KeySystemConstructor = pair[1]; + const accesConfig = isMediaKeySystemConfig(keySystemConfig) ? keySystemConfig : KeySystemConstructor.requestAccessConfig; + const options = [Object.assign({}, accesConfig, mediaCapabilities)]; + obs = obs.pipe(catchError(() => { + return KeySystemFactory$1.requestKeySystemInternal(keySystemString, options, logger); + })); + } + return obs; + } + /** + * @param keySystemString keySystemString passed to requestMediaKeySystemAccess. e.g. 'com.apple.fps' + * @param options MediaKeySystemConfiguration passed to requestMediaKeySystemAccess + */ + static requestKeySystemInternal(keySystemString, options, logger) { + logger.info(`[Keys] requestKeySystemAccess ${keySystemString} ${JSON.stringify(options)}`); + return defer(() => from(navigator.requestMediaKeySystemAccess(keySystemString, options))); + } + /** + * Create KeySystem object + * + * @param keysystemId Identifier for key system used by HLS.js. e.g. 'fairplaystreaming' 'widevine' + * @param hls Hls object + * @param mediaKeys MediaKeys for KeySystem to manage + * @param keyLoader The loader object + */ + make(keysystemId, mediaKeys, config, eventEmitter, sessionHandler, logger) { + var _a; + const keySystemAccess = (_a = KeySystemFactory$1.idToMediaKeysInfoMap[keysystemId]) === null || _a === void 0 ? void 0 : _a.keySystemAccess; + if (!keySystemAccess) { + throw new KeySystemError(`No keySystemAccess for ${keysystemId}`, undefined, 0, ErrorResponses.KeySystemNoKeySystemAccess, keysystemId); + } + let KeySystemConstructor; + // NOTE: this system string is used in the LICENSE_CHALLENGE_CREATED event for playready and widevine + // as the "keysystem" parameter. Change with caution. + const systemString = kKeySystemIdToPropertiesMap$1[keysystemId].systemStringPrefix; + // Find the matching constructor for the key system string + for (const pair of kKeySystemIdToConstructorsMap[keysystemId]) { + if (pair[0] === keySystemAccess.keySystem) { + KeySystemConstructor = pair[1]; + break; + } + } + if (!KeySystemConstructor) { + throw new KeySystemError(`No constructor associated with ${keysystemId}`, undefined, 0, ErrorResponses.KeySystemNoConstructor, systemString); + } + const keySystem = new KeySystemConstructor(mediaKeys, systemString, config, eventEmitter, sessionHandler, logger); + return keySystem; + } + static get availableKeySystems() { + return Object.keys(kKeySystemIdToPropertiesMap$1); + } + /** + * Get the EXT-X-KEY:FORMAT value associated with the key system + * @param keySystemId Identifier string to request keyFormatString property for + */ + static getKeySystemFormat(keySystemId) { + const properties = kKeySystemIdToPropertiesMap$1[keySystemId]; + return properties ? properties.keyFormatString : ''; + } + static getKeySystemSecurityLevel(keySystemId) { + const properties = kKeySystemIdToPropertiesMap$1[keySystemId]; + return properties ? properties.securityLevels : undefined; + } + } + KeySystemFactory$1.idToMediaKeysInfoMap = {}; + KeySystemFactory$1.destroy$ = new Subject(); + + // Lets set the max slots as couple of slots less than the normally allowed max slot of 8 + const MAX_ALLOWED_KEY_SLOTS = 6; + const kMediaKeySystemConfigCodecs = { + video: ['avc1.42E01E'], + audio: ['mp4a.40.2'], + }; + function convertError(keyUri, error, mediaOptionIds) { + // Convert error if needed + if (!error) { + error = new KeyRequestError('Unknown error from CDM', keyUri, 0, ErrorResponses.KeySystemCDMUnknownError, false, KeyRequestErrorReason.InternalError); + } + else if (error instanceof TimeoutError) { + error = new KeyRequestTimeoutError('Key request timed out', keyUri, ErrorResponses.KeySystemRequestTimedOut); + } + else if (error instanceof KeySystemError) { + const response = (error === null || error === void 0 ? void 0 : error.response) || ErrorResponses.InternalError; + error = new KeyRequestError(error.message, keyUri, 0, response, false, KeyRequestErrorReason.InternalError, true); + } + if (error instanceof KeyRequestError || error instanceof KeyRequestTimeoutError) { + error.mediaOptionIds = [...mediaOptionIds]; + } + return error; + } + class KeySystemAdapter { + constructor(ksService, mediaSink, config, platformQuery, eventEmitter, sessionHandler, logger, keySystemFactory = new KeySystemFactory$1() // For testing + ) { + this.ksService = ksService; + this.mediaSink = mediaSink; + this.config = config; + this.platformQuery = platformQuery; + this.eventEmitter = eventEmitter; + this.sessionHandler = sessionHandler; + this.keySystemFactory = keySystemFactory; + this.reset$ = new Subject(); + this.keyRequest$ = new Subject(); + this.abort$ = new Subject(); // Abort(keyuri) + this.keySystem$ = new BehaviorSubject(null); + this._keyStatusChange$ = new Subject(); + this.protectionData = {}; + this.keySystemId = null; // Identifier for keySystem + this.keyUriToRequest = {}; // keyUri => request observable + this.ksQuery = ksService.getQuery(); + this.logger = logger.child({ name: 'eme' }); + if (this.config.warmupCdms) { + this.keySystemFactory.createMediaKeys(FairPlayStreamingKeySystemProperties.id, kMediaKeySystemConfigCodecs.video, kMediaKeySystemConfigCodecs.audio, this.logger, undefined).subscribe(); + } + const platformInfoChange$ = platformQuery.platformInfo$.pipe(distinctUntilChanged((a, b) => a && b && a.requiresCDMAttachOnStart === b.requiresCDMAttachOnStart), switchMap((platformInfo) => { + if (platformInfo === null || platformInfo === void 0 ? void 0 : platformInfo.requiresCDMAttachOnStart) { + return this.attachMediaKeys().pipe(catchError((err) => { + this.logger.info(`onMediaAttach err:${err.message}`); + this.handleKeySystemError(err); + return EMPTY; + })); + } + else { + return VOID; + } + }), switchMapTo(EMPTY)); + // This is where the key requests actually get subscribed + const keyRequests$ = this.keyRequest$.pipe(mergeMap((request) => { + return request.pipe(catchError(() => { + // In band error gets bubbled up to caller + return EMPTY; + })); + })); + const keyStatusChange$ = this.keySystem$.pipe(switchMap((ks) => { + if (!ks) { + return EMPTY; + } + return ks.keyStatusChange$.pipe(tap((event) => { + var _a; + const keyUri = event.decryptdata.uri; + this.logger.info(`key status change uri=${redactUrl(keyUri)} status=${event.status}`); + const entity = this.ksQuery.getKeyInfo(keyUri); + if (event.status === 'needs-renewal') { + this.ksService.updateKeyRequestState(keyUri, KeyRequestMacroState.MustRequestResponse, (state) => state === KeyRequestMacroState.GotKeyResponse); + } + else { + const error = convertError(keyUri, event.error, (_a = entity === null || entity === void 0 ? void 0 : entity.mediaOptionIds) !== null && _a !== void 0 ? _a : []); + this.ksService.setError(keyUri, error); + } + this._keyStatusChange$.next(event); + })); + })); + const bufferedKeyHandling$ = this.isKeyCleanupSupported() + ? this.mediaSink.mediaQuery.bufferedSegmentsTuple$.pipe(mergeMap((bufferedSegments) => { + const [videoSegments, audioSegments] = bufferedSegments; + const bufferedKeyUris = new Set(); + videoSegments.forEach((bufferedSegment) => { + var _a, _b; + const uri = (_b = (_a = bufferedSegment.frag) === null || _a === void 0 ? void 0 : _a.keyTagInfo) === null || _b === void 0 ? void 0 : _b.uri; + if (uri) { + bufferedKeyUris.add(uri); + } + }); + audioSegments.forEach((bufferedSegment) => { + var _a, _b; + const uri = (_b = (_a = bufferedSegment.frag) === null || _a === void 0 ? void 0 : _a.keyTagInfo) === null || _b === void 0 ? void 0 : _b.uri; + if (uri) { + bufferedKeyUris.add(uri); + } + }); + return this.handleKeyCleanup(bufferedKeyUris); + })) + : VOID; + merge(platformInfoChange$, keyRequests$, keyStatusChange$, bufferedKeyHandling$).pipe(takeUntil(this.reset$)).subscribe(); + } + get keyStatusChange$() { + return this._keyStatusChange$; + } + get keySystem() { + return this.keySystem$.value; + } + destroy() { + this.reset$.next(); // Stop all requests & stop listening to events + this.ksService.removeAll(); // Clear state + this.keySystemId = null; + const keySystem = this.keySystem; + let ksDestroyed$ = VOID; + if (keySystem) { + this.keySystem$.next(null); + if (keySystem.shouldDestroyMediaKeys) { + this.keySystemFactory.destroyMediaKeys(); + } + ksDestroyed$ = keySystem.destroy(); + } + return combineLatest([ksDestroyed$, this.mediaSink.clearMediaKeys()]).pipe(mapTo(undefined)); + } + attachMediaKeys() { + if (this.keySystem) { + // we already wait inside makeKeySystem. nothing to do. Could happen if + // detach happened after keysystem was created (recoverMediaError?) + return VOID; + } + else { + // Create and attach key system. + // Use 'NONE' so we don't get assigned a keyId for no reason + const keyFormatString = this.config.keySystemPreference ? KeySystemFactory$1.getKeySystemFormat(this.config.keySystemPreference) : FairPlayStreamingKeySystemProperties.keyFormatString; + return this.makeKeySystem(new DecryptData('NONE', null, null, keyFormatString, [1])).pipe(mapTo(void 0)); + } + } + isKeyCleanupSupported() { + // At this point of time we have two configurations for fairplay. One with single key session and one with multiple key sessions. + // In case of single key session, we cannot remove the session or key. The cleanup is supported only when multiple key sessions are supported + // Till the app enables that configuration, we should not call cleanup + // By default PlayReady and Widevine use multiple key sessions. But app doesn't pass useMultipleKeySessions in this case. So check for the preference and enable it based on that. + return this.config.useMultipleKeySessions === true || this.config.keySystemPreference === 'widevine' || this.config.keySystemPreference === 'playready'; + } + handleKeyCleanup(bufferedKeyUris) { + if (this.ksQuery.getCount() < MAX_ALLOWED_KEY_SLOTS) { + return VOID; + } + const currentTime = performance.now(); + const keyEntities = this.ksQuery.getAll(); + const removes$ = keyEntities.map((ksEntity) => { + const keyUri = ksEntity.keyUri; + if (!bufferedKeyUris.has(keyUri)) { + const keyTagInfo = entityToKeyTagInfo(ksEntity.decryptdata); + // We don't need to key cleanup for AES-128 and also check if the key has met the minimum holdtime + if (keyTagInfo.method !== 'AES-128' && currentTime > ksEntity.minHoldTime) { + // Remove the entry from store, so that it's no longer tracked + return this._removeKey(keyUri, keyTagInfo); + } + } + return VOID; + }); + return removes$.length ? forkJoin(removes$).pipe(switchMapTo(VOID)) : VOID; + } + _removeKey(keyUri, keyTagInfo) { + this.abort$.next(keyUri); + this.ksService.removeKey(keyUri); + return this.keySystem.removeKey(keyTagInfo); + } + removeKeysForItems(ids) { + const removes$ = []; + applyTransaction(() => { + for (const itemId of ids) { + this.ksService.removeAllKeysForItem(itemId); + const keyInfos = this.ksQuery.getAll({ + filterBy: (entity) => entity.itemIds.length === 0, + }); + for (const keyInfo of keyInfos) { + removes$.push(this._removeKey(keyInfo.keyUri, entityToKeyTagInfo(keyInfo.decryptdata))); + } + } + }); + return removes$.length ? forkJoin(removes$).pipe(switchMap(() => VOID)) : VOID; + } + get availableKeySystems() { + return KeySystemFactory$1.availableKeySystems; + } + initialize(config) { + var _a; + const oldProtectionData = this.protectionData; + this.protectionData = {}; + const preference = this.config.keySystemPreference; + for (const keySystemId of KeySystemFactory$1.availableKeySystems) { + const keysystemConfig = config[keySystemId]; + if (!keysystemConfig) { + continue; + } + if (preference !== keySystemId) { + this.logger.warn(`Key system ${keySystemId} does not match preference ${preference}, ignoring`); + continue; + } + this.logger.info(`Setting protectionData for ${keySystemId}`); + const cert = keysystemConfig.certificate; + const serverCertUrl = keysystemConfig.serverCertUrl ? URLToolkit$1.buildAbsoluteURL(window.location.href, keysystemConfig.serverCertUrl) : undefined; + this.protectionData[keySystemId] = { + serverCertUrl: serverCertUrl, + certificate: cert, + }; + let loadCert$; + if (cert) { + this.logger.info('Got cert'); + loadCert$ = of({ keysystem: keySystemId, certificate: cert }); + } + else if (serverCertUrl && ((_a = oldProtectionData === null || oldProtectionData === void 0 ? void 0 : oldProtectionData[keySystemId]) === null || _a === void 0 ? void 0 : _a.serverCertUrl) !== serverCertUrl) { + this.logger.info(`Loading cert ${redactUrl(serverCertUrl)} `); + let loaderConfig; + if (isCustomUrl(serverCertUrl)) { + loaderConfig = this.config.certLoadPolicy.customURL; + } + else { + loaderConfig = this.config.certLoadPolicy.default; + } + loadCert$ = fromUrlArrayBuffer({ url: serverCertUrl, xhrSetup: this.config.xhrSetup }, loaderConfig).pipe(map(([loadArrayBufferResult, _]) => { + return { keysystem: keySystemId, certificate: new Uint8Array(loadArrayBufferResult) }; + })); + } + if (loadCert$) { + loadCert$ + .pipe(switchMap((data) => this.onServerCertificateLoaded(data)), catchError((err) => { + this.logger.error(`Error loading cert: ${err.message}`); + this.eventEmitter.trigger(HlsEvent$1.INTERNAL_ERROR, { + type: ErrorTypes.NETWORK_ERROR, + details: ErrorDetails.CERT_LOAD_ERROR, + fatal: false, + handled: true, + reason: 'Error handling cert', + response: ErrorResponses.KeySystemCertificateLoadError, + message: err.message, + name: 'certificateLoadError', + }); + // TODO: Save the error and reject any key requests relying on this. + throw err; + }), takeUntil(this.reset$)) + .subscribe(); + } + } + } + /** + * Generate challenge using request info + */ + generateRequest(keyuri, requestInfo) { + if (this.keySystem) { + this.keySystem.setKeyRequestInfo(keyuri, requestInfo); + } + } + /** + * Set the challenge response + */ + setLicenseResponse(keyuri, response) { + if (this.keySystem) { + this.keySystem.setParsedResponse(keyuri, response); + } + } + /** + * Called from externally to retrieve key. + * @param decryptdata Info about the key to retrieve + * @param mediaOptionId Id of level associated with this key request. Used for error handling + * @returns Observable of type DecryptData. Will signal on success, or throw an Error + **/ + getKeyFromDecryptData(decryptdata, mediaOptionKey) { + if (!decryptdata || !decryptdata.isEncrypted) { + return of(decryptdata); // Return immediately + } + let obs; + applyTransaction(() => { + obs = this._getKeyFromDecryptData(decryptdata, mediaOptionKey); + }); + return obs; + } + _getKeyFromDecryptData(decryptdata, mediaOptionKey) { + let itemId = null; + let mediaOptionId = null; + if (mediaOptionKey) { + itemId = mediaOptionKey.itemId; + mediaOptionId = mediaOptionKey.mediaOptionId; + } + const keyUri = decryptdata.uri; + const keyEntity = this.ksQuery.getKeyInfo(keyUri); + if (keyEntity && mediaOptionKey != null) { + this.ksService.addMediaOption(keyUri, mediaOptionKey); + } + const permanentError = (keyEntity === null || keyEntity === void 0 ? void 0 : keyEntity.error) instanceof KeyRequestError && keyEntity.error.isOkToRetry === false; + if (permanentError) { + return throwError(keyEntity.error); + } + else if (!keyEntity || keyEntity.requestState === KeyRequestMacroState.MustRequestResponse) { + this.logger.info(`getKeyFromDecryptData mediaOptionId=${mediaOptionId} method=${decryptdata.method} keyuri=${redactUrl(keyUri)} state=${keyEntity === null || keyEntity === void 0 ? void 0 : keyEntity.requestState}`); + // specify minimum duration a key may exist in the system before it’s eligible for eviction + const minHoldTime = performance.now() + this.config.keyMinHoldTimeBeforeCleanup; + // Abort any old requests + this.abort$.next(keyUri); + this.ksService.upsertKey({ + keyUri, + decryptdata: keyTagInfoToEntity(decryptdata), + minHoldTime, + mediaOptionIds: [mediaOptionId], + requestState: KeyRequestMacroState.WaitingForKeyResponse, + itemIds: [itemId], + }); + // Now start fetching key + let request; + const method = decryptdata.method; + switch (method) { + case 'SAMPLE-AES': + case 'ISO-23001-7': + case 'SAMPLE-AES-CTR': { + const loadPolicy = this.config.keyLoadPolicy.customURL; + request = this.fetchKeyEME(decryptdata).pipe(timeout(loadPolicy.maxLoadTimeMs)); + break; + } + case 'AES-128': + request = this.fetchKeyHTTP(decryptdata.uri, decryptdata, this.config.keyLoadPolicy); + break; + default: + const err = new ExceptionError(false, `Unexpected METHOD attribute ${method}`, ErrorResponses.KeySystemUnexpectedMETHOD); + return throwError(err); + } + // TODO: stats + const request$ = (this.keyUriToRequest[keyUri] = request.pipe(map((keyLoadedData) => { + const decryptdata = keyLoadedData.decryptdata; + this.ksService.updateKeyValue(keyUri, decryptdata.key); + this.eventEmitter.trigger(HlsEvent$1.KEY_LOADED, keyLoadedData); + return keyLoadedData.decryptdata; + }), catchError((err) => { + var _a; + const keyEntity = this.ksQuery.getKeyInfo(keyUri); + err = convertError(keyUri, err, (_a = keyEntity === null || keyEntity === void 0 ? void 0 : keyEntity.mediaOptionIds) !== null && _a !== void 0 ? _a : []); + this.ksService.setError(keyUri, err); + return throwError(err); + }), finalize$1(() => { + // Update state for abort() only if we were in progress + this.ksService.updateKeyRequestState(keyUri, KeyRequestMacroState.MustRequestResponse, (state) => state === KeyRequestMacroState.WaitingForKeyResponse); + this.keyUriToRequest[keyUri] = null; + this.logger.debug(`${printKeyTag(decryptdata)} finalize state=${this.ksQuery.getKeyInfo(keyUri).requestState}`); + }), share(), takeUntil(race(this.abort$.pipe(filter((uri) => uri === keyUri)), this.reset$).pipe(tap((uri) => this.logger.warn(uri ? `aborted ${redactUrl(uri)}` : 'got reset')))))); + this.keyRequest$.next(request$); // Subscribes to key request + return request$; + } + else if (keyEntity.requestState === KeyRequestMacroState.GotKeyResponse) { + return of(entityToKeyTagInfo(keyEntity.decryptdata)); + } + // requestState === KeyRequestMacroState.WaitingForKeyResponse + return this.keyUriToRequest[keyUri]; + } + fetchKeyEME(decryptdata) { + return this.requestKey(decryptdata).pipe(map((decryptdata) => { + const now = performance.now(); + const data = { + timestamp: now, + keyuri: decryptdata.uri, + decryptdata, + /*stats: { + trequest: keyInfo.stats.trequest, + tfirst: keyInfo.stats.tfirst, + tload: now + }*/ + }; + return data; + })); + } + /** + * Make generic HTTP request for key + * @param uri The URL to make request to + * @param decryptdata Associated DecryptData object + * @param loadTimeoutMs + */ + fetchKeyHTTP(uri, decryptdata, loadPolicy) { + return KeySystemAdapter.fetchKeyHTTP(uri, this.config, decryptdata, loadPolicy); + } + static fetchKeyHTTP(uri, config, decryptdata, loadPolicy) { + const loadable = { + url: uri, + xhrSetup: config.xhrSetup, + }; + return fromUrlArrayBuffer(loadable, getLoadConfig(loadable, loadPolicy)).pipe(map(([loadArrayBufferResult, _]) => { + decryptdata.key = new Uint8Array(loadArrayBufferResult); + const loadedData = { + decryptdata, + keyuri: uri, + timestamp: performance.now(), + /*stats: { + trequest: keyInfo.stats.trequest, + tfirst: keyInfo.stats.tfirst, + tload: now + }*/ + }; + return loadedData; + })); + } + requestKey(decryptdata) { + // Assumed called from key loader and that it already checked the method and that it's encrypted, etc. + this.logger.debug(`requestKey - uri=${redactUrl(decryptdata.uri)}`); + return this.makeKeySystem(decryptdata).pipe(switchMap((keySystem) => { + return keySystem.startKeyRequest(decryptdata); + })); + } + ensureKeySystem(mediaKeys) { + return defer(() => { + if (!this.keySystem && this.keySystemId) { + this.keySystem$.next(this.keySystemFactory.make(this.keySystemId, mediaKeys, this.config, this.eventEmitter, this.sessionHandler, this.logger)); + const protData = this.protectionData && this.protectionData[this.keySystemId]; + if (protData) { + return this.keySystem.setServerCertificate(protData.certificate).pipe(mapTo(this.keySystem)); + } + } + return of(this.keySystem); + }); + } + /** + * Make MediaKeys object for decryptdata and attach to current media element + * Also creates a KeySystem object + * @param decryptdata The KeyTagInfo object + */ + makeKeySystem(decryptdata) { + this.logger.debug(`makeKeySystem - uri=${redactUrl(decryptdata.uri)}`); + return this.ensureMediaKeys(decryptdata).pipe(switchMap((mediaKeys) => { + this.logger.debug(`setMediaKeys - uri=${redactUrl(decryptdata.uri)}`); + return this.mediaSink.setMediaKeys(mediaKeys).pipe(switchMap(() => { + this.logger.debug(`ensureKeySystem - uri=${redactUrl(decryptdata.uri)}`); + // Create KeySystem object + return this.ensureKeySystem(mediaKeys); + })); + })); + } + // Create MediaKeys object. + ensureMediaKeys(decryptdata) { + var _a; + const newKeySystemId = KeySystemFactory$1.getKeySystemIdForDecryptData(decryptdata); + if (this.keySystemId == null) { + this.keySystemId = newKeySystemId; + } + else if (this.keySystemId !== newKeySystemId) { + const err = new KeyRequestError(`New key system string does not match existing ${newKeySystemId} !== ${this.keySystemId}`, decryptdata.uri, 0, ErrorResponses.KeySystemUnmatchedString, false, KeyRequestErrorReason.InternalError); + return throwError(err); + } + // CreateMediaKeys will return the same promise for a given key system ID if it's already being created. + return this.keySystemFactory.createMediaKeys(this.keySystemId, kMediaKeySystemConfigCodecs.video, kMediaKeySystemConfigCodecs.audio, this.logger, (_a = this.platformQuery.platformInfo) === null || _a === void 0 ? void 0 : _a.keySystemConfig); + } + /** + * + * @param data { keysystem: keysystem identifier, certificate: the buffer containing the cert } + */ + onServerCertificateLoaded(data) { + const keySystemId = data.keysystem, certificate = data.certificate; + this.logger.info(`KeySystemAdapter: ${keySystemId} cert loaded ${certificate ? 'nonnull' : 'null'}`); + const protData = this.protectionData[keySystemId]; + protData.certificate = certificate; + this.logger.debug(`current keySystemId: ${this.keySystemId}`); + if (this.keySystem && this.keySystemId === keySystemId) { + return this.keySystem.setServerCertificate(certificate).pipe(mapTo(undefined)); + } + return VOID; + } + // Trigger error due to key system setup issue + handleKeySystemError(error) { + const keyError = new KeySystemError(error.message, undefined, undefined, ErrorResponses.KeySystemSetupError, undefined); + this.eventEmitter.trigger(HlsEvent$1.INTERNAL_ERROR, keyError); + } + } + + class KeySystemQuery extends QueryEntity { + constructor(store) { + super(store); + } + /** + * @returns the key info for a particular key + */ + getKeyInfo(keyuri) { + const entity = this.getEntity(keyuri); + if (entity) { + // Populate with proper mediaOptionIds every time + return Object.assign(Object.assign({}, entity), { error: copyKeyError(entity.error, entity.mediaOptionIds) }); + } + return null; + } + getKeyInfo$(keyuri) { + return this.selectEntity(keyuri).pipe(map((entity) => { + if (entity) { + // Populate with proper mediaOptionIds every time + return Object.assign(Object.assign({}, entity), { error: copyKeyError(entity.error, entity.mediaOptionIds) }); + } + else { + return null; + } + })); + } + getKeyRequestState$(keyuri) { + return this.selectEntity(keyuri, (entity) => entity === null || entity === void 0 ? void 0 : entity.requestState); + } + /** + * @returns an observable that emits whenever the key status changes + */ + getKeyStatus$(keyuri) { + return this.selectEntity(keyuri, (entity) => entity === null || entity === void 0 ? void 0 : entity.status); + } + /** + * @returns an observable that emits whenever we got an error on a particular key + */ + getKeyError$(keyuri) { + return this.selectEntity(keyuri, (entity) => copyKeyError(entity === null || entity === void 0 ? void 0 : entity.error, entity === null || entity === void 0 ? void 0 : entity.mediaOptionIds)).pipe(filterNil); + } + } + + /** + * @brief Backing store for the playback state + */ + class KeySystemStore extends EntityStore { + constructor() { + super({}, { name: 'key-system-store', idKey: 'keyUri', producerFn: produce_1 }); + } + } + + /** + * @brief Service that manages playback state + */ + class KeySystemService { + constructor(store) { + this.store = store; + } + getQuery() { + return new KeySystemQuery(this.store); + } + upsertKey(keyEntity) { + logAction('keys.upsert', keyEntity.keyUri); + const newItemIdSet = new Set(keyEntity.itemIds.filter((x) => x != null)); + const newMediaOptionIdSet = new Set(keyEntity.mediaOptionIds.filter((x) => x != null)); + this.store.upsert(keyEntity.keyUri, + // Update existing + (oldEntity) => { + const mergedEntity = Object.assign(Object.assign({}, oldEntity), keyEntity); + // Merge itemIds & mediaOptionIds + if ('itemIds' in oldEntity) { + for (const itemId of oldEntity.itemIds) { + newItemIdSet.add(itemId); + } + } + mergedEntity.itemIds = Array.from(newItemIdSet); + if ('mediaOptionIds' in oldEntity) { + for (const mediaOptionId of oldEntity.mediaOptionIds) { + newMediaOptionIdSet.add(mediaOptionId); + } + } + mergedEntity.mediaOptionIds = Array.from(newMediaOptionIdSet); + return mergedEntity; + }, + // Create new entity + () => (Object.assign(Object.assign({}, keyEntity), { itemIds: Array.from(newItemIdSet), mediaOptionIds: Array.from(newMediaOptionIdSet) }))); + } + removeKey(keyUri) { + logAction('keys.removeKey', keyUri); + this.store.remove(keyUri); + } + removeAllKeysForItem(itemId) { + logAction(`keys.removeAllKeysForItem ${itemId}`); + this.store.update(null, (entity) => { + // Remove itemid from itemId list + const idx = entity.itemIds.findIndex((id) => id === itemId); + if (idx >= 0) { + entity.itemIds.splice(idx, 1); + } + }); + } + removeAll() { + logAction('keys.remove'); + this.store.remove(); + } + updateKeyValue(keyUri, key) { + logAction('keys.updateKeyValue', keyUri); + this.store.update(keyUri, (entity) => { + if (entity.decryptdata.keyBuf == null && key != null) { + entity.decryptdata.keyBuf = key.buffer; + } + entity.requestState = KeyRequestMacroState.GotKeyResponse; + }); + } + updateKeyStatus(keyUri, status) { + logAction(`keys.updateKeyStatus ${status}`, keyUri); + this.store.update(keyUri, (entity) => { + entity.status = status; + }); + } + updateKeyRequestState(keyUri, requestState, predicate) { + logAction(`keys.updateKeyRequestState ${requestState}`, keyUri); + this.store.update(keyUri, (entity) => { + if (!predicate || predicate(entity.requestState)) { + entity.requestState = requestState; + } + }); + } + addMediaOption(keyUri, mediaOptionKey) { + const { itemId, mediaOptionId } = mediaOptionKey; + logAction(`keys.addMediaOption itemId: ${itemId}, mediaOptionId: ${mediaOptionId}`, keyUri); + this.store.update(keyUri, (entity) => { + if (mediaOptionId != null) { + if (entity.mediaOptionIds.every((id) => id !== mediaOptionId)) { + entity.mediaOptionIds.push(mediaOptionId); + } + } + if (itemId != null) { + if (entity.itemIds.every((id) => id !== itemId)) { + entity.itemIds.push(itemId); + } + } + }); + } + setError(keyUri, error) { + var _a; + logAction(`keys.setError ${(_a = error === null || error === void 0 ? void 0 : error.constructor) === null || _a === void 0 ? void 0 : _a.name}`, keyUri); + this.store.update(keyUri, (entity) => { + // Make a copy so that entity.error is not modified after storage + entity.error = copyKeyError(error); + entity.requestState = KeyRequestMacroState.MustRequestResponse; + }); + } + } + // state for the service functions + const keySystemStore = new KeySystemStore(); + let globalKeySystemService = null; + function keySystemService() { + if (!globalKeySystemService) { + globalKeySystemService = new KeySystemService(keySystemStore); + } + return globalKeySystemService; + } + const makeKeySystemService = (keySystemService, source$, itemRemove$, config, platformQuery, eventEmitter, sessionHandler, logger) => { + return source$.pipe(tag('[Keys] playback.keySystemServiceEpic.in'), switchMap((mediaSink) => { + if (!mediaSink) { + return of(null); + } + return new Observable((subscriber) => { + let keySystemAdapter = new KeySystemAdapter(keySystemService, mediaSink, config, platformQuery, eventEmitter, sessionHandler, logger); + const sub = merge(of(keySystemAdapter), itemRemove$.pipe(mergeMap((ids) => { + return keySystemAdapter.removeKeysForItems(ids); + }), switchMapTo(EMPTY))).subscribe(subscriber); + return function unsubscribe() { + logger.warn('[Keys] playback.keySystemServiceEpic.unsubscribe'); + sub.unsubscribe(); + keySystemAdapter.destroy().subscribe(); + keySystemAdapter = undefined; + }; + }); + }), tag('[Keys] playback.keySystemServiceEpic.emit')); + }; + + function isAlternateMediaOption(option) { + return 'groupId' in option; + } + + function safeAssignProperties(target, source) { + return Object.assign(target, source); + } + /** + * Exceptions from regular ASCII. CodePoints are mapped to UTF-16 codes + */ + const specialCea608CharsCodes = { + 42: 225, + 92: 233, + 94: 237, + 95: 243, + 96: 250, + 123: 231, + 124: 247, + 125: 209, + 126: 241, + 127: 9608, + // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F + // THIS MEANS THAT \x50 MUST BE ADDED TO THE VALUES + 128: 174, + 129: 176, + 130: 189, + 131: 191, + 132: 8482, + 133: 162, + 134: 163, + 135: 9834, + 136: 224, + 137: 32, + 138: 232, + 139: 226, + 140: 234, + 141: 238, + 142: 244, + 143: 251, + // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F + 144: 193, + 145: 201, + 146: 211, + 147: 218, + 148: 220, + 149: 252, + 150: 8216, + 151: 161, + 152: 42, + 153: 8217, + 154: 9473, + 155: 169, + 156: 8480, + 157: 8226, + 158: 8220, + 159: 8221, + 160: 192, + 161: 194, + 162: 199, + 163: 200, + 164: 202, + 165: 203, + 166: 235, + 167: 206, + 168: 207, + 169: 239, + 170: 212, + 171: 217, + 172: 249, + 173: 219, + 174: 171, + 175: 187, + // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS + // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F + 176: 195, + 177: 227, + 178: 205, + 179: 204, + 180: 236, + 181: 210, + 182: 242, + 183: 213, + 184: 245, + 185: 123, + 186: 125, + 187: 92, + 188: 94, + 189: 95, + 190: 124, + 191: 8764, + 192: 196, + 193: 228, + 194: 214, + 195: 246, + 196: 223, + 197: 165, + 198: 164, + 199: 9475, + 200: 197, + 201: 229, + 202: 216, + 203: 248, + 204: 9487, + 205: 9491, + 206: 9495, + 207: 9499, // Box drawings heavy up and left + }; + /** + * Utils + */ + const getCharForByte = function (byte) { + let charCode = byte; + // eslint-disable-next-line no-prototype-builtins + if (specialCea608CharsCodes.hasOwnProperty(byte)) { + charCode = specialCea608CharsCodes[byte]; + } + return String.fromCharCode(charCode); + }; + const NR_ROWS = 15, NR_COLS = 100; + // Tables to look up row from PAC data + const rowsLowCh1 = { 17: 1, 18: 3, 21: 5, 22: 7, 23: 9, 16: 11, 19: 12, 20: 14 }; + const rowsHighCh1 = { 17: 2, 18: 4, 21: 6, 22: 8, 23: 10, 19: 13, 20: 15 }; + const rowsLowCh2 = { 25: 1, 26: 3, 29: 5, 30: 7, 31: 9, 24: 11, 27: 12, 28: 14 }; + const rowsHighCh2 = { 25: 2, 26: 4, 29: 6, 30: 8, 31: 10, 27: 13, 28: 15 }; + const backgroundColors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'black', 'transparent']; + const logger = { + verboseFilter: { DATA: 3, DEBUG: 3, INFO: 2, WARNING: 2, TEXT: 1, ERROR: 0 }, + time: null, + verboseLevel: 0, + setTime: function (newTime) { + this.time = newTime; + }, + log: function (severity, msg) { + const minLevel = this.verboseFilter[severity]; + if (this.verboseLevel >= minLevel) { + // eslint-disable-next-line no-console + console.log(this.time + ' [' + severity + '] ' + msg); + } + }, + }; + const numArrayToHexArray = function (numArray) { + const hexArray = []; + for (let j = 0; j < numArray.length; j++) { + hexArray.push(numArray[j].toString(16)); + } + return hexArray; + }; + class PenState { + constructor(foreground, underline, italics, background, flash) { + this.foreground = foreground || 'white'; + this.underline = underline || false; + this.italics = italics || false; + this.background = background || 'black'; + this.flash = flash || false; + } + reset() { + this.foreground = 'white'; + this.underline = false; + this.italics = false; + this.background = 'black'; + this.flash = false; + } + setStyles(styles) { + safeAssignProperties(this, styles); + } + isDefault() { + return this.foreground === 'white' && !this.underline && !this.italics && this.background === 'black' && !this.flash; + } + equals(other) { + return this.foreground === other.foreground && this.underline === other.underline && this.italics === other.italics && this.background === other.background && this.flash === other.flash; + } + copy(newPenState) { + this.foreground = newPenState.foreground; + this.underline = newPenState.underline; + this.italics = newPenState.italics; + this.background = newPenState.background; + this.flash = newPenState.flash; + } + toString() { + return 'color=' + this.foreground + ', underline=' + this.underline + ', italics=' + this.italics + ', background=' + this.background + ', flash=' + this.flash; + } + } + /** + * Unicode character with styling and background. + * @constructor + */ + class StyledUnicodeChar { + constructor(uchar, foreground, underline, italics, background, flash) { + this.uchar = uchar || ' '; // unicode character + this.penState = new PenState(foreground, underline, italics, background, flash); + } + reset() { + this.uchar = ' '; + this.penState.reset(); + } + setChar(uchar, newPenState) { + this.uchar = uchar; + this.penState.copy(newPenState); + } + setPenState(newPenState) { + this.penState.copy(newPenState); + } + equals(other) { + return this.uchar === other.uchar && this.penState.equals(other.penState); + } + copy(newChar) { + this.uchar = newChar.uchar; + this.penState.copy(newChar.penState); + } + isEmpty() { + return this.uchar === ' ' && this.penState.isDefault(); + } + } + /** + * CEA-608 row consisting of NR_COLS instances of StyledUnicodeChar. + * @constructor + */ + class Row { + constructor() { + this.chars = []; + for (let i = 0; i < NR_COLS; i++) { + this.chars.push(new StyledUnicodeChar()); + } + this.pos = 0; + this.currPenState = new PenState(); + } + equals(other) { + let equal = true; + for (let i = 0; i < NR_COLS; i++) { + if (!this.chars[i].equals(other.chars[i])) { + equal = false; + break; + } + } + return equal; + } + copy(other) { + for (let i = 0; i < NR_COLS; i++) { + this.chars[i].copy(other.chars[i]); + } + } + isEmpty() { + let empty = true; + for (let i = 0; i < NR_COLS; i++) { + if (!this.chars[i].isEmpty()) { + empty = false; + break; + } + } + return empty; + } + /** + * Set the cursor to a valid column. + */ + setCursor(absPos) { + if (this.pos !== absPos) { + this.pos = absPos; + } + if (this.pos < 0) { + logger.log('ERROR', 'Negative cursor position ' + this.pos); + this.pos = 0; + } + else if (this.pos > NR_COLS) { + logger.log('ERROR', 'Too large cursor position ' + this.pos); + this.pos = NR_COLS; + } + } + /** + * Move the cursor relative to current position. + */ + moveCursor(relPos) { + const newPos = this.pos + relPos; + if (relPos > 1) { + for (let i = this.pos + 1; i < newPos + 1; i++) { + this.chars[i].setPenState(this.currPenState); + } + } + this.setCursor(newPos); + } + /** + * Backspace, move one step back and clear character. + */ + backSpace() { + this.moveCursor(-1); + this.chars[this.pos].setChar(' ', this.currPenState); + } + insertChar(byte) { + if (byte >= 144) { + // Extended char + this.backSpace(); + } + const char = getCharForByte(byte); + if (this.pos >= NR_COLS) { + logger.log('ERROR', 'Cannot insert ' + byte.toString(16) + ' (' + char + ') at position ' + this.pos + '. Skipping it!'); + return; + } + this.chars[this.pos].setChar(char, this.currPenState); + this.moveCursor(1); + } + clearFromPos(startPos) { + let i; + for (i = startPos; i < NR_COLS; i++) { + this.chars[i].reset(); + } + } + clear() { + this.clearFromPos(0); + this.pos = 0; + this.currPenState.reset(); + } + clearToEndOfRow() { + this.clearFromPos(this.pos); + } + getTextString() { + const chars = []; + let empty = true; + for (let i = 0; i < NR_COLS; i++) { + const char = this.chars[i].uchar; + if (char !== ' ') { + empty = false; + } + chars.push(char); + } + if (empty) { + return ''; + } + else { + return chars.join(''); + } + } + setPenStyles(styles) { + this.currPenState.setStyles(styles); + const currChar = this.chars[this.pos]; + currChar.setPenState(this.currPenState); + } + } + /** + * Keep a CEA-608 screen of 32x15 styled characters + * @constructor + */ + class CaptionScreen { + constructor() { + this.rows = []; + for (let i = 0; i < NR_ROWS; i++) { + this.rows.push(new Row()); // Note that we use zero-based numbering (0-14) + } + this.currRow = NR_ROWS - 1; + this.nrRollUpRows = null; + this.reset(); + } + reset() { + for (let i = 0; i < NR_ROWS; i++) { + this.rows[i].clear(); + } + this.currRow = NR_ROWS - 1; + } + equals(other) { + let equal = true; + for (let i = 0; i < NR_ROWS; i++) { + if (!this.rows[i].equals(other.rows[i])) { + equal = false; + break; + } + } + return equal; + } + copy(other) { + for (let i = 0; i < NR_ROWS; i++) { + this.rows[i].copy(other.rows[i]); + } + } + isEmpty() { + let empty = true; + for (let i = 0; i < NR_ROWS; i++) { + if (!this.rows[i].isEmpty()) { + empty = false; + break; + } + } + return empty; + } + backSpace() { + const row = this.rows[this.currRow]; + row.backSpace(); + } + clearToEndOfRow() { + const row = this.rows[this.currRow]; + row.clearToEndOfRow(); + } + /** + * Insert a character (without styling) in the current row. + */ + insertChar(char) { + const row = this.rows[this.currRow]; + row.insertChar(char); + } + setPen(styles) { + const row = this.rows[this.currRow]; + row.setPenStyles(styles); + } + moveCursor(relPos) { + const row = this.rows[this.currRow]; + row.moveCursor(relPos); + } + setCursor(absPos) { + logger.log('INFO', 'setCursor: ' + absPos); + const row = this.rows[this.currRow]; + row.setCursor(absPos); + } + setPAC(pacData) { + logger.log('INFO', 'pacData = ' + JSON.stringify(pacData)); + let newRow = pacData.row - 1; + if (this.nrRollUpRows && newRow < this.nrRollUpRows - 1) { + newRow = this.nrRollUpRows - 1; + } + // Make sure this only affects Roll-up Captions by checking this.nrRollUpRows + if (this.nrRollUpRows && this.currRow !== newRow) { + // clear all rows first + for (let i = 0; i < NR_ROWS; i++) { + this.rows[i].clear(); + } + // Copy this.nrRollUpRows rows from lastOutputScreen and place it in the newRow location + // topRowIndex - the start of rows to copy (inclusive index) + const topRowIndex = this.currRow + 1 - this.nrRollUpRows; + // We only copy if the last position was already shown. + // We use the cueStartTime value to check this. + const lastOutputScreen = this.lastOutputScreen; + if (lastOutputScreen) { + const prevLineTime = lastOutputScreen.rows[topRowIndex].cueStartTime; + if (prevLineTime && prevLineTime < logger.time) { + for (let i = 0; i < this.nrRollUpRows; i++) { + this.rows[newRow - this.nrRollUpRows + i + 1].copy(lastOutputScreen.rows[topRowIndex + i]); + } + } + } + } + this.currRow = newRow; + const row = this.rows[this.currRow]; + if (pacData.indent !== null) { + const indent = pacData.indent; + const prevPos = Math.max(indent - 1, 0); + row.setCursor(pacData.indent); + pacData.color = row.chars[prevPos].penState.foreground; + } + const styles = { foreground: pacData.color, underline: pacData.underline, italics: pacData.italics, background: 'black', flash: false }; + this.setPen(styles); + } + /** + * Set background/extra foreground, but first do back_space, and then insert space (backwards compatibility). + */ + setBkgData(bkgData) { + logger.log('INFO', 'bkgData = ' + JSON.stringify(bkgData)); + this.backSpace(); + this.setPen(bkgData); + this.insertChar(32); // Space + } + setRollUpRows(nrRows) { + this.nrRollUpRows = nrRows; + } + rollUp() { + if (this.nrRollUpRows === null) { + logger.log('DEBUG', 'roll_up but nrRollUpRows not set yet'); + return; // Not properly setup + } + logger.log('INFO', 'TEXT ' + this.getDisplayText()); + const topRowIndex = this.currRow + 1 - this.nrRollUpRows; + const topRow = this.rows.splice(topRowIndex, 1)[0]; + topRow.clear(); + this.rows.splice(this.currRow, 0, topRow); + logger.log('INFO', 'Rolling up'); + } + /** + * Get all non-empty rows with as unicode text. + */ + getDisplayText(asOneRow) { + asOneRow = asOneRow || false; + const displayText = []; + let text = ''; + let rowNr = -1; + for (let i = 0; i < NR_ROWS; i++) { + const rowText = this.rows[i].getTextString(); + if (rowText) { + rowNr = i + 1; + if (asOneRow) { + displayText.push('Row ' + rowNr + ': \'' + rowText + '\''); + } + else { + displayText.push(rowText.trim()); + } + } + } + if (displayText.length > 0) { + if (asOneRow) { + text = '[' + displayText.join(' | ') + ']'; + } + else { + text = displayText.join('\n'); + } + } + return text; + } + getTextAndFormat() { + return this.rows; + } + } + // var modes = ['MODE_ROLL-UP', 'MODE_POP-ON', 'MODE_PAINT-ON', 'MODE_TEXT']; + class Cea608Channel { + constructor(channelNumber, outputFilter) { + this.chNr = channelNumber; + this.outputFilter = outputFilter; + this.mode = null; + this.verbose = 0; + this.displayedMemory = new CaptionScreen(); + this.nonDisplayedMemory = new CaptionScreen(); + this.lastOutputScreen = new CaptionScreen(); + this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1]; + this.writeScreen = this.displayedMemory; + this.mode = null; + this.cueStartTime = null; // Keeps track of where a cue started. + } + reset() { + this.mode = null; + this.displayedMemory.reset(); + this.nonDisplayedMemory.reset(); + this.lastOutputScreen.reset(); + this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1]; + this.writeScreen = this.displayedMemory; + this.mode = null; + this.cueStartTime = null; + this.lastCueEndTime = null; + } + getHandler() { + return this.outputFilter; + } + setHandler(newHandler) { + this.outputFilter = newHandler; + } + setPAC(pacData) { + this.writeScreen.setPAC(pacData); + } + setBkgData(bkgData) { + this.writeScreen.setBkgData(bkgData); + } + setMode(newMode) { + if (newMode === this.mode) { + return; + } + this.mode = newMode; + logger.log('INFO', 'MODE=' + newMode); + if (this.mode === 'MODE_POP-ON') { + this.writeScreen = this.nonDisplayedMemory; + } + else { + this.writeScreen = this.displayedMemory; + this.writeScreen.reset(); + } + if (this.mode !== 'MODE_ROLL-UP') { + this.displayedMemory.nrRollUpRows = null; + this.nonDisplayedMemory.nrRollUpRows = null; + } + this.mode = newMode; + } + insertChars(chars) { + for (let i = 0; i < chars.length; i++) { + this.writeScreen.insertChar(chars[i]); + } + const screen = this.writeScreen === this.displayedMemory ? 'DISP' : 'NON_DISP'; + logger.log('INFO', screen + ': ' + this.writeScreen.getDisplayText(true)); + if (this.mode === 'MODE_PAINT-ON' || this.mode === 'MODE_ROLL-UP') { + logger.log('TEXT', 'DISPLAYED: ' + this.displayedMemory.getDisplayText(true)); + this.outputDataUpdate(); + } + } + ccRCL() { + // Resume Caption Loading (switch mode to Pop On) + logger.log('INFO', 'RCL - Resume Caption Loading'); + this.setMode('MODE_POP-ON'); + } + ccBS() { + // BackSpace + logger.log('INFO', 'BS - BackSpace'); + if (this.mode === 'MODE_TEXT') { + return; + } + this.writeScreen.backSpace(); + if (this.writeScreen === this.displayedMemory) { + this.outputDataUpdate(); + } + } + ccAOF() { + // Reserved (formerly Alarm Off) + return; + } + ccAON() { + // Reserved (formerly Alarm On) + return; + } + ccDER() { + // Delete to End of Row + logger.log('INFO', 'DER- Delete to End of Row'); + this.writeScreen.clearToEndOfRow(); + this.outputDataUpdate(); + } + ccRU(nrRows) { + // Roll-Up Captions-2,3,or 4 Rows + logger.log('INFO', 'RU(' + nrRows + ') - Roll Up'); + this.writeScreen = this.displayedMemory; + this.setMode('MODE_ROLL-UP'); + this.writeScreen.setRollUpRows(nrRows); + } + ccFON() { + // Flash On + logger.log('INFO', 'FON - Flash On'); + this.writeScreen.setPen({ flash: true }); + } + ccRDC() { + // Resume Direct Captioning (switch mode to PaintOn) + logger.log('INFO', 'RDC - Resume Direct Captioning'); + this.setMode('MODE_PAINT-ON'); + } + ccTR() { + // Text Restart in text mode (not supported, however) + logger.log('INFO', 'TR'); + this.setMode('MODE_TEXT'); + } + ccRTD() { + // Resume Text Display in Text mode (not supported, however) + logger.log('INFO', 'RTD'); + this.setMode('MODE_TEXT'); + } + ccEDM() { + // Erase Displayed Memory + logger.log('INFO', 'EDM - Erase Displayed Memory'); + this.displayedMemory.reset(); + this.outputDataUpdate(true); + } + ccCR() { + // Carriage Return + logger.log('INFO', 'CR - Carriage Return'); + this.writeScreen.rollUp(); + this.outputDataUpdate(true); + } + ccENM() { + // Erase Non-Displayed Memory + logger.log('INFO', 'ENM - Erase Non-displayed Memory'); + this.nonDisplayedMemory.reset(); + } + ccEOC() { + // End of Caption (Flip Memories) + logger.log('INFO', 'EOC - End Of Caption'); + if (this.mode === 'MODE_POP-ON') { + const tmp = this.displayedMemory; + this.displayedMemory = this.nonDisplayedMemory; + this.nonDisplayedMemory = tmp; + this.writeScreen = this.nonDisplayedMemory; + logger.log('TEXT', 'DISP: ' + this.displayedMemory.getDisplayText()); + } + this.outputDataUpdate(true); + } + ccTO(nrCols) { + // Tab Offset 1,2, or 3 columns + logger.log('INFO', 'TO(' + nrCols + ') - Tab Offset'); + this.writeScreen.moveCursor(nrCols); + } + ccMIDROW(secondByte) { + // Parse MIDROW command + const styles = { flash: false, underline: false, italics: false }; + styles.underline = secondByte % 2 === 1; + styles.italics = secondByte >= 46; + if (!styles.italics) { + const colorIndex = Math.floor(secondByte / 2) - 16; + const colors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta']; + styles.foreground = colors[colorIndex]; + } + else { + styles.foreground = 'white'; + } + logger.log('INFO', 'MIDROW: ' + JSON.stringify(styles)); + this.writeScreen.setPen(styles); + } + outputDataUpdate(dispatch = false) { + const t = logger.time; + if (t === null) { + return; + } + if (this.outputFilter) { + if (this.outputFilter.updateData) { + this.outputFilter.updateData(t, this.displayedMemory); + } + if (this.cueStartTime === null && !this.displayedMemory.isEmpty()) { + // Start of a new cue + this.cueStartTime = t; + } + else { + if (!this.displayedMemory.equals(this.lastOutputScreen)) { + if (this.outputFilter.newCue) { + this.outputFilter.newCue(this.cueStartTime, t, this.lastOutputScreen); + if (dispatch === true && this.outputFilter.dispatchCue) { + this.outputFilter.dispatchCue(); + } + } + this.cueStartTime = this.displayedMemory.isEmpty() ? null : t; + } + } + this.lastOutputScreen.copy(this.displayedMemory); + } + } + cueSplitAtTime(t) { + if (this.outputFilter) { + if (!this.displayedMemory.isEmpty()) { + if (this.outputFilter.newCue) { + this.outputFilter.newCue(this.cueStartTime, t, this.displayedMemory); + } + this.cueStartTime = t; + } + } + } + } + class Cea608Parser { + constructor(field = 1, out1, out2) { + this.field = field; + this.currChNr = -1; // Will be 1 or 2 + this.lastCmdA = null; // First byte of last command + this.lastCmdB = null; // Second byte of last command + this.channels = [new Cea608Channel(1, out1), new Cea608Channel(2, out2)]; + this.dataCounters = { padding: 0, char: 0, cmd: 0, other: 0 }; + } + getHandler(index) { + return this.channels[index].getHandler(); + } + setHandler(index, newHandler) { + this.channels[index].setHandler(newHandler); + } + /** + * Add data for time t in forms of list of bytes (unsigned ints). The bytes are treated as pairs. + */ + addData(t, byteList) { + let cmdFound, a, b; + let charsFound = null; + logger.setTime(t); + for (let i = 0; i < byteList.length; i += 2) { + a = byteList[i] & 127; + b = byteList[i + 1] & 127; + if (a >= 16 && a <= 31 && a === this.lastCmdA && b === this.lastCmdB) { + this.lastCmdA = null; + this.lastCmdB = null; + logger.log('DEBUG', 'Repeated command (' + numArrayToHexArray([a, b]) + ') is dropped'); + continue; // Repeated commands are dropped (once) + } + if (a === 0 && b === 0) { + this.dataCounters.padding += 2; + continue; + } + else { + logger.log('DATA', '[' + numArrayToHexArray([byteList[i], byteList[i + 1]]) + '] -> (' + numArrayToHexArray([a, b]) + ')'); + } + cmdFound = this.parseCmd(a, b); + if (!cmdFound) { + cmdFound = this.parseMidrow(a, b); + } + if (!cmdFound) { + cmdFound = this.parsePAC(a, b); + } + if (!cmdFound) { + cmdFound = this.parseBackgroundAttributes(a, b); + } + if (!cmdFound) { + charsFound = this.parseChars(a, b); + if (charsFound) { + if (this.currChNr && this.currChNr >= 0) { + const channel = this.channels[this.currChNr - 1]; + channel.insertChars(charsFound); + } + else { + logger.log('WARNING', 'No channel found yet. TEXT-MODE?'); + } + } + } + if (cmdFound) { + this.dataCounters.cmd += 2; + } + else if (charsFound) { + this.dataCounters.char += 2; + } + else { + this.dataCounters.other += 2; + logger.log('WARNING', 'Couldn\'t parse cleaned data ' + numArrayToHexArray([a, b]) + ' orig: ' + numArrayToHexArray([byteList[i], byteList[i + 1]])); + } + } + } + /** + * Parse Command. + * @returns {Boolean} Tells if a command was found + */ + parseCmd(a, b) { + let chNr = null; + const cond1 = (a === 20 || a === 21 || a === 28 || a === 29) && 32 <= b && b <= 47; + const cond2 = (a === 23 || a === 31) && 33 <= b && b <= 35; + if (!(cond1 || cond2)) { + return false; + } + if (a === 20 || a === 23) { + chNr = 1; + } + else { + chNr = 2; // (a === 0x1C || a=== 0x1f) + } + const channel = this.channels[chNr - 1]; + if (a === 20 || a === 21 || a === 28 || a === 29) { + if (b === 32) { + channel.ccRCL(); + } + else if (b === 33) { + channel.ccBS(); + } + else if (b === 34) { + channel.ccAOF(); + } + else if (b === 35) { + channel.ccAON(); + } + else if (b === 36) { + channel.ccDER(); + } + else if (b === 37) { + channel.ccRU(2); + } + else if (b === 38) { + channel.ccRU(3); + } + else if (b === 39) { + channel.ccRU(4); + } + else if (b === 40) { + channel.ccFON(); + } + else if (b === 41) { + channel.ccRDC(); + } + else if (b === 42) { + channel.ccTR(); + } + else if (b === 43) { + channel.ccRTD(); + } + else if (b === 44) { + channel.ccEDM(); + } + else if (b === 45) { + channel.ccCR(); + } + else if (b === 46) { + channel.ccENM(); + } + else if (b === 47) { + channel.ccEOC(); + } + } + else { + // a == 0x17 || a == 0x1F + channel.ccTO(b - 32); + } + this.lastCmdA = a; + this.lastCmdB = b; + this.currChNr = chNr; + return true; + } + /** + * Parse midrow styling command + * @returns {Boolean} + */ + parseMidrow(a, b) { + let chNr = null; + if ((a === 17 || a === 25) && 32 <= b && b <= 47) { + if (a === 17) { + chNr = 1; + } + else { + chNr = 2; + } + if (chNr !== this.currChNr) { + logger.log('ERROR', 'Mismatch channel in midrow parsing'); + return false; + } + const channel = this.channels[chNr - 1]; + // cea608 spec says midrow codes should inject a space + channel.insertChars([32]); + channel.ccMIDROW(b); + logger.log('DEBUG', 'MIDROW (' + numArrayToHexArray([a, b]) + ')'); + this.lastCmdA = a; + this.lastCmdB = b; + return true; + } + return false; + } + /** + * Parse Preable Access Codes (Table 53). + * @returns {Boolean} Tells if PAC found + */ + parsePAC(a, b) { + let chNr = null; + let row = null; + const case1 = ((17 <= a && a <= 23) || (25 <= a && a <= 31)) && 64 <= b && b <= 127; + const case2 = (a === 16 || a === 24) && 64 <= b && b <= 95; + if (!(case1 || case2)) { + return false; + } + chNr = a <= 23 ? 1 : 2; + if (64 <= b && b <= 95) { + row = chNr === 1 ? rowsLowCh1[a] : rowsLowCh2[a]; + } + else { + // 0x60 <= b <= 0x7F + row = chNr === 1 ? rowsHighCh1[a] : rowsHighCh2[a]; + } + const pacData = this.interpretPAC(row, b); + const channel = this.channels[chNr - 1]; + channel.setPAC(pacData); + this.lastCmdA = a; + this.lastCmdB = b; + this.currChNr = chNr; + return true; + } + /** + * Interpret the second byte of the pac, and return the information. + * @returns {Object} pacData with style parameters. + */ + interpretPAC(row, byte) { + let pacIndex = byte; + const pacData = { color: null, italics: false, indent: null, underline: false, row: row }; + if (byte > 95) { + pacIndex = byte - 96; + } + else { + pacIndex = byte - 64; + } + pacData.underline = (pacIndex & 1) === 1; + if (pacIndex <= 13) { + pacData.color = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'white'][Math.floor(pacIndex / 2)]; + } + else if (pacIndex <= 15) { + pacData.italics = true; + pacData.color = 'white'; + } + else { + pacData.indent = Math.floor((pacIndex - 16) / 2) * 4; + } + return pacData; // Note that row has zero offset. The spec uses 1. + } + /** + * Parse characters. + * @returns An array with 1 to 2 codes corresponding to chars, if found. null otherwise. + */ + parseChars(a, b) { + let channelNr = null, charCodes = null, charCode1 = null; + if (a >= 25) { + channelNr = 2; + charCode1 = a - 8; + } + else { + channelNr = 1; + charCode1 = a; + } + if (17 <= charCode1 && charCode1 <= 19) { + // Special character + let oneCode = b; + if (charCode1 === 17) { + oneCode = b + 80; + } + else if (charCode1 === 18) { + oneCode = b + 112; + } + else { + oneCode = b + 144; + } + logger.log('INFO', 'Special char \'' + getCharForByte(oneCode) + '\' in channel ' + channelNr); + charCodes = [oneCode]; + this.lastCmdA = a; + this.lastCmdB = b; + } + else if (32 <= a && a <= 127) { + charCodes = b === 0 ? [a] : [a, b]; + this.lastCmdA = null; + this.lastCmdB = null; + } + if (charCodes) { + const hexCodes = numArrayToHexArray(charCodes); + logger.log('DEBUG', `Char codes = ${hexCodes.join(',')}`); + } + return charCodes; + } + /** + * Parse extended background attributes as well as new foreground color black. + * @returns{Boolean} Tells if background attributes are found + */ + parseBackgroundAttributes(a, b) { + let bkgData, index, chNr, channel; + const case1 = (a === 16 || a === 24) && 32 <= b && b <= 47; + const case2 = (a === 23 || a === 31) && 45 <= b && b <= 47; + if (!(case1 || case2)) { + return false; + } + // eslint-disable-next-line prefer-const + bkgData = { underline: false }; + if (a === 16 || a === 24) { + index = Math.floor((b - 32) / 2); + bkgData.background = backgroundColors[index]; + if (b % 2 === 1) { + bkgData.background = bkgData.background + '_semi'; + } + } + else if (b === 45) { + bkgData.background = 'transparent'; + } + else { + bkgData.foreground = 'black'; + if (b === 47) { + bkgData.underline = true; + } + } + // eslint-disable-next-line prefer-const + chNr = a < 24 ? 1 : 2; + // eslint-disable-next-line prefer-const + channel = this.channels[chNr - 1]; + channel.setBkgData(bkgData); + this.lastCmdA = null; + this.lastCmdB = null; + return true; + } + /** + * Reset state of parser and its channels. + */ + reset() { + for (let i = 0; i < this.channels.length; i++) { + if (this.channels[i]) { + this.channels[i].reset(); + } + } + this.lastCmdA = null; + this.lastCmdB = null; + } + /** + * Trigger the generation of a cue, and the start of a new one if displayScreens are not empty. + */ + cueSplitAtTime(t) { + for (let i = 0; i < this.channels.length; i++) { + if (this.channels[i]) { + this.channels[i].cueSplitAtTime(t); + } + } + } + } + var Cea608Parser$1 = Cea608Parser; + + class OutputFilter { + constructor(handler, track) { + this.handler = handler; + this.track = track; + this.startTime = null; + this.endTime = null; + this.screen = null; + } + dispatchCue() { + if (this.startTime === null) { + return; + } + this.handler.addCues('cc' + this.track, this.startTime, this.endTime, this.screen); + this.startTime = null; + } + newCue(startTime, endTime, screen) { + if (this.startTime === null || this.startTime > startTime) { + this.startTime = startTime; + } + this.endTime = endTime; + this.screen = screen; + this.handler.createHTMLCaptionsTrack(this.track); + } + } + + var lib = {}; + + var vttparser = {}; + + var vttcue = {}; + + var vttvalidator = {}; + + /** + * Copyright 2019 vtt.js Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + Object.defineProperty(vttvalidator, "__esModule", { value: true }); + function isValidPercentValue(value) { + return typeof value === 'number' && value >= 0 && value <= 100; + } + vttvalidator.isValidPercentValue = isValidPercentValue; + function isValidAlignSetting(value) { + return (typeof value === 'string' && + ['start', 'center', 'end', 'left', 'right', 'middle'].includes(value)); + } + vttvalidator.isValidAlignSetting = isValidAlignSetting; + function isValidDirectionSetting(value) { + return typeof value === 'string' && ['', 'rl', 'lr'].includes(value); + } + vttvalidator.isValidDirectionSetting = isValidDirectionSetting; + function isValidLineAndPositionSetting(value) { + return typeof value === 'number' || value === 'auto'; + } + vttvalidator.isValidLineAndPositionSetting = isValidLineAndPositionSetting; + function isValidLineAlignSetting(value) { + return typeof value === 'string' && ['start', 'center', 'end'].includes(value); + } + vttvalidator.isValidLineAlignSetting = isValidLineAlignSetting; + function isValidPositionAlignSetting(value) { + return (typeof value === 'string' && + ['line-left', 'center', 'line-right', 'auto', 'left', 'start', 'middle', 'end', 'right'].includes(value)); + } + vttvalidator.isValidPositionAlignSetting = isValidPositionAlignSetting; + function isValidScrollSetting(value) { + return ['', 'up'].includes(value); + } + vttvalidator.isValidScrollSetting = isValidScrollSetting; + + var vttparserUtility = {}; + + Object.defineProperty(vttparserUtility, "__esModule", { value: true }); + const ESCAPE = { + '&': '&', + '<': '<', + '>': '>', + '‎': '\u200e', + '‏': '\u200f', + ' ': '\u00a0' + }; + const TAG_NAME = { + c: 'span', + i: 'i', + b: 'b', + u: 'u', + ruby: 'ruby', + rt: 'rt', + v: 'span', + lang: 'span' + }; + const TAG_ANNOTATION = { + v: 'title', + lang: 'lang' + }; + const NEEDS_PARENT = { + rt: 'ruby' + }; + // "text-combine-upright" must be remapped into "-webkit-text-combine" + // until browsers support this property + // In the future: https://developer.mozilla.org/en-US/docs/Web/CSS/text-combine-upright + const styleMap = { + 'text-combine-upright': '-webkit-text-combine:horizontal; text-orientation: mixed;' + }; + class ParserUtility { + // Try to parse input as a time stamp. + static parseTimeStamp(input) { + function computeSeconds(arr) { + const [h, m, s, f] = arr.map(n => (n ? parseInt('' + n) : 0)); + return h * 3600 + m * 60 + s + f / 1000; + } + const m = /^(\d+):(\d{2})(:\d{2})?\.(\d{3})/.exec(input); + if (!m) { + return null; + } + if (m[3]) { + // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds] + return computeSeconds([m[1], m[2], m[3].substring(1), m[4]]); + } + else if (parseInt(m[1]) > 59) { + // Timestamp takes the form of [hours]:[minutes].[milliseconds] + // First position is hours as it's over 59. + return computeSeconds([m[1], m[2], null, m[4]]); + } + else { + // Timestamp takes the form of [minutes]:[seconds].[milliseconds] + return computeSeconds([null, m[1], m[2], m[4]]); + } + } + // Parse content into a document fragment. + static parseContent(window, cue, globalStyleCollection) { + let input = cue.text; + function nextToken() { + // Check for end-of-string. + if (!input) { + return null; + } + // Consume 'n' characters from the input. + function consume(result) { + input = input.substr(result.length); + return result; + } + const m = /^([^<]*)(<[^>]+>?)?/.exec(input); + // If there is some text before the next tag, return it, otherwise return + // the tag. + return consume(m[1] ? m[1] : m[2]); + } + // Unescape a string 's'. + function unescape1(e) { + return ESCAPE[e]; + } + function unescape(s) { + return s.replace(/&(amp|lt|gt|lrm|rlm|nbsp);/g, unescape1); + } + function shouldAdd(current, element) { + return (!NEEDS_PARENT[element.dataset.localName] || + NEEDS_PARENT[element.dataset.localName] === current.dataset.localName); + } + // Create an element for this tag. + function createElement(type, cssSelector, annotation) { + function createStyleString(styles) { + let styleString = ''; + for (const key in styles) { + if (styleMap[key]) { + styleString += styleMap[key]; + } + else { + styleString += key + ':' + styles[key] + ';'; + } + } + return styleString; + } + const tagName = TAG_NAME[type]; + if (!tagName) { + return null; + } + const element = window.document.createElement(tagName); + // localName is a read only property, but we can store this as an HTML data-attribute + element.dataset.localName = tagName; + const name = TAG_ANNOTATION[type]; + if (name && annotation) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + element[name] = annotation.trim(); + } + if (cssSelector) { + if (globalStyleCollection[cssSelector]) { + const elementStyleCollection = globalStyleCollection[cssSelector]; + const styleString = createStyleString(elementStyleCollection); + element.setAttribute('style', styleString); + } + else { + console.info(`WebVTT: parseContent: Style referenced, but no style defined for '${cssSelector}'!`); + } + } + return element; + } + const rootDiv = window.document.createElement('div'), tagStack = []; + let current = rootDiv, t, m; + while ((t = nextToken()) !== null) { + if (t[0] === '<') { + if (t[1] === '/') { + // If the closing tag matches, move back up to the parent node. + if (tagStack.length && + tagStack[tagStack.length - 1] === t.substr(2).replace('>', '')) { + tagStack.pop(); + current = current.parentNode; + } + // Otherwise just ignore the end tag. + continue; + } + const ts = ParserUtility.parseTimeStamp(t.substr(1, t.length - 2)); + let node; + if (ts) { + // Timestamps are lead nodes as well. + node = window.document.createProcessingInstruction('timestamp', ts.toString() + // eslint-disable-next-line @typescript-eslint/no-explicit-any + ); + current.appendChild(node); + continue; + } + m = /^<([^.\s/0-9>]+)(\.[^\s\\>]+)?([^>\\]+)?(\\?)>?$/.exec(t); + // If we can't parse the tag, skip to the next tag. + if (!m) { + continue; + } + // Try to construct an element, and ignore the tag if we couldn't. + node = createElement(m[1], m[2], m[3]); + if (!node) { + continue; + } + // Determine if the tag should be added based on the context of where it + // is placed in the cuetext. + if (!shouldAdd(current, node)) { + continue; + } + // Append the node to the current node, and enter the scope of the new + // node. + tagStack.push(m[1]); + current.appendChild(node); + current = node; + continue; + } + // Text nodes are leaf nodes. + current.appendChild(window.document.createTextNode(unescape(t))); + } + return rootDiv; + } + } + vttparserUtility.default = ParserUtility; + + /** + * Copyright 2013 vtt.js Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + var __decorate$1 = (commonjsGlobal && commonjsGlobal.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(vttcue, "__esModule", { value: true }); + const vttvalidator_1$1 = vttvalidator; + const vttparser_utility_1$2 = vttparserUtility; + let VTTCue = class VTTCue { + constructor(startTime, endTime, text) { + /** + * VTTCue and TextTrackCue properties + * http://dev.w3.org/html5/webvtt/#vttcue-interface + */ + this._id = ''; + this._pauseOnExit = false; + this._region = null; + this._vertical = ''; + this._snapToLines = true; + this._line = 'auto'; + this._lineAlign = 'start'; + this._position = 'auto'; + this._positionAlign = 'auto'; + this._size = 100; + this._align = 'center'; + /** + * Other spec defined properties + */ + /** + * Shim implementation specific properties. These properties are not in + * the spec. + */ + // Lets us know when the VTTCue's data has changed in such a way that we need + // to recompute its display state. This lets us compute its display state + // lazily. + this.hasBeenReset = false; + this._startTime = startTime; + this._endTime = endTime; + this._text = text; + } + get id() { + return this._id; + } + set id(value) { + this._id = '' + value; + } + get pauseOnExit() { + return this._pauseOnExit; + } + set pauseOnExit(value) { + this._pauseOnExit = !!value; + } + get startTime() { + return this._startTime; + } + set startTime(value) { + if (typeof value !== 'number') { + throw new TypeError(`Start time must be set to a number: ${value}`); + } + this._startTime = value; + this.hasBeenReset = true; + } + get endTime() { + return this._endTime; + } + set endTime(value) { + if (typeof value !== 'number') { + throw new TypeError(`End time must be set to a number: ${value}`); + } + this._endTime = value; + this.hasBeenReset = true; + } + get text() { + return this._text; + } + set text(value) { + this._text = '' + value; + this.hasBeenReset = true; + } + get region() { + return this._region; + } + set region(value) { + this._region = value; + this.hasBeenReset = true; + } + get vertical() { + return this._vertical; + } + set vertical(value) { + if (!vttvalidator_1$1.isValidDirectionSetting(value)) { + throw new SyntaxError(`An invalid or illegal string was specified for vertical: ${value}`); + } + this._vertical = value; + this.hasBeenReset = true; + } + get snapToLines() { + return this._snapToLines; + } + set snapToLines(value) { + this._snapToLines = !!value; + this.hasBeenReset = true; + } + get line() { + return this._line; + } + set line(value) { + if (!vttvalidator_1$1.isValidLineAndPositionSetting(value)) { + throw new SyntaxError(`An invalid number or illegal string was specified for line: ${value}`); + } + this._line = value; + this.hasBeenReset = true; + } + get lineAlign() { + return this._lineAlign; + } + set lineAlign(value) { + if (!vttvalidator_1$1.isValidLineAlignSetting(value)) { + throw new SyntaxError(`An invalid or illegal string was specified for lineAlign: ${value}`); + } + this._lineAlign = value; + this.hasBeenReset = true; + } + get position() { + return this._position; + } + set position(value) { + if (!vttvalidator_1$1.isValidLineAndPositionSetting(value)) { + throw new Error(`Position must be between 0 and 100 or auto: ${value}`); + } + this._position = value; + this.hasBeenReset = true; + } + get positionAlign() { + return this._positionAlign; + } + set positionAlign(value) { + if (!vttvalidator_1$1.isValidPositionAlignSetting(value)) { + throw new SyntaxError(`An invalid or illegal string was specified for positionAlign: ${value}`); + } + this._positionAlign = value; + this.hasBeenReset = true; + } + get size() { + return this._size; + } + set size(value) { + if (value < 0 || value > 100) { + throw new Error(`Size must be between 0 and 100: ${value}`); + } + this._size = value; + this.hasBeenReset = true; + } + get align() { + return this._align; + } + set align(value) { + if (!vttvalidator_1$1.isValidAlignSetting(value)) { + throw new SyntaxError(`An invalid or illegal string was specified for align: ${value}`); + } + this._align = value; + this.hasBeenReset = true; + } + getCueAsHTML() { + return vttparser_utility_1$2.default.parseContent(window, this, {}); + } + static create(options) { + if (!options.hasOwnProperty('startTime') || + !options.hasOwnProperty('endTime') || + !options.hasOwnProperty('text')) { + throw new Error('You must at least have start time, end time, and text.'); + } + const cue = new this(options.startTime, options.endTime, options.text); + Object.keys(options).forEach((key) => { + if (cue.hasOwnProperty(key)) { + cue[key] = options[key]; + } + }); + return cue; + } + static fromJSON(json) { + return this.create(JSON.parse(json)); + } + toJSON() { + const json = {}; + Object.keys(this).forEach((key) => { + if (this.hasOwnProperty(key) && + key !== 'getCueAsHTML' && + key !== 'hasBeenReset' && + key !== 'displayState') { + json[key] = this[key]; + } + }); + return json; + } + }; + VTTCue = __decorate$1([ + replaceVTTCue + ], VTTCue); + vttcue.VTTCue = VTTCue; + function replaceVTTCue(constructor) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let cls = constructor; + if (typeof window !== 'undefined' && window.VTTCue != null) { + cls = window.VTTCue; + cls.create = constructor.create; + cls.fromJSON = constructor.fromJSON; + cls.prototype.toJSON = constructor.prototype.toJSON; + } + return cls; + } + + var vttregion = {}; + + /** + * Copyright 2013 vtt.js Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + var __decorate = (commonjsGlobal && commonjsGlobal.__decorate) || function (decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); + else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; + }; + Object.defineProperty(vttregion, "__esModule", { value: true }); + const vttvalidator_1 = vttvalidator; + // VTTRegion shim http://dev.w3.org/html5/webvtt/#vttregion-interface + let VTTRegion = class VTTRegion { + constructor() { + this._id = ''; + this._lines = 3; + this._regionAnchorX = 0; + this._regionAnchorY = 100; + this._scroll = ''; + this._viewportAnchorX = 0; + this._viewportAnchorY = 100; + this._width = 100; + } + get id() { + return this._id; + } + set id(value) { + if (typeof value !== 'string') { + throw new Error('ID must be a string.'); + } + this._id = value; + } + get lines() { + return this._lines; + } + set lines(value) { + if (typeof value !== 'number') { + throw new TypeError('Lines must be set to a number.'); + } + this._lines = value; + } + get regionAnchorX() { + return this._regionAnchorX; + } + set regionAnchorX(value) { + if (!vttvalidator_1.isValidPercentValue(value)) { + throw new TypeError('RegionAnchorX must be between 0 and 100.'); + } + this._regionAnchorX = value; + } + get regionAnchorY() { + return this._regionAnchorY; + } + set regionAnchorY(value) { + if (!vttvalidator_1.isValidPercentValue(value)) { + throw new TypeError('RegionAnchorY must be between 0 and 100.'); + } + this._regionAnchorY = value; + } + get scroll() { + return this._scroll; + } + set scroll(value) { + if (typeof value === 'string') { + const setting = value.toLowerCase(); + if (vttvalidator_1.isValidScrollSetting(setting)) { + this._scroll = setting; + return; + } + } + throw new SyntaxError('An invalid or illegal string was specified.'); + } + get viewportAnchorX() { + return this._viewportAnchorX; + } + set viewportAnchorX(value) { + if (!vttvalidator_1.isValidPercentValue(value)) { + throw new TypeError('ViewportAnchorX must be between 0 and 100.'); + } + this._viewportAnchorX = value; + } + get viewportAnchorY() { + return this._viewportAnchorY; + } + set viewportAnchorY(value) { + if (!vttvalidator_1.isValidPercentValue(value)) { + throw new TypeError('ViewportAnchorY must be between 0 and 100.'); + } + this._viewportAnchorY = value; + } + get width() { + return this._width; + } + set width(value) { + if (!vttvalidator_1.isValidPercentValue(value)) { + throw new TypeError('Width must be between 0 and 100.'); + } + this._lines = value; + } + toJSON() { + const json = {}; + Object.keys(this).forEach((key) => { + if (this.hasOwnProperty(key)) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + json[key] = this[key]; + } + }); + return json; + } + static create(options) { + const region = new this(); + Object.keys(options).forEach((key) => { + if (region.hasOwnProperty(key)) { + region[key] = options[key]; + } + }); + return region; + } + static fromJSON(json) { + return this.create(JSON.parse(json)); + } + }; + VTTRegion = __decorate([ + replaceVTTRegion + ], VTTRegion); + vttregion.VTTRegion = VTTRegion; + function replaceVTTRegion(constructor) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let cls = constructor; + if (typeof window !== 'undefined' && window.VTTRegion != null) { + cls = window.VTTRegion; + cls.create = constructor.create; + cls.fromJSON = constructor.fromJSON; + cls.prototype.toJSON = constructor.prototype.toJSON; + } + return cls; + } + + /** + * Copyright 2013 vtt.js Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + Object.defineProperty(vttparser, "__esModule", { value: true }); + const vttcue_1$1 = vttcue; + vttparser.VTTCue = vttcue_1$1.VTTCue; + const vttregion_1 = vttregion; + vttparser.VTTRegion = vttregion_1.VTTRegion; + const vttparser_utility_1$1 = vttparserUtility; + // Creates a new ParserError object from an errorData object. The errorData + // object should have default code and message properties. The default message + // property can be overridden by passing in a message parameter. + // See ParsingError.Errors below for acceptable errors. + class ParsingError extends Error { + constructor(errorData, message) { + super(); + this.name = 'ParsingError'; + if (typeof errorData === 'number') { + this.code = errorData; + } + else { + this.code = errorData.code; + } + if (message) { + this.message = message; + } + else if (errorData instanceof ParsingError) { + this.message = errorData.message; + } + } + } + vttparser.ParsingError = ParsingError; + // ParsingError metadata for acceptable ParsingErrors. + ParsingError.Errors = { + BadSignature: new ParsingError(0, 'Malformed WebVTT signature.'), + BadTimeStamp: new ParsingError(1, 'Malformed time stamp.') + }; + // A settings object holds key/value pairs and will ignore anything but the first + // assignment to a specific key. + class Settings { + constructor() { + this.values = {}; + } + // Only accept the first assignment to any key. + set(k, v) { + if (!this.get(k) && v !== '') { + this.values[k] = v; + } + } + get(k, dflt, defaultKey) { + if (typeof dflt === 'object' && typeof defaultKey === 'string') { + return this.has(k) ? this.values[k] : dflt[defaultKey]; + } + return this.has(k) ? this.values[k] : dflt; + } + // Check whether we have a value for a key. + has(k) { + return k in this.values; + } + // Accept a setting if its one of the given alternatives. + alt(k, v, a) { + for (let n = 0; n < a.length; ++n) { + if (v === a[n]) { + this.set(k, v); + break; + } + } + } + // Accept a setting if its a valid (signed) integer. + integer(k, v) { + if (/^-?\d+$/.test(v)) { + this.set(k, parseInt(v, 10)); + } + } + // Accept a setting if its a valid percentage. + percent(k, v) { + const m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/); + if (m) { + try { + const vf = parseFloat(v); + if (vf >= 0 && vf <= 100) { + this.set(k, vf); + return true; + } + } + catch (err) { + return false; + } + } + return false; + } + } + class WebVTTParser { + constructor(window, decoder, onStylesParsedCallback) { + this.window = window; + this.state = 'INITIAL'; + this.styleCollector = ''; + this.buffer = ''; + this.decoder = decoder || new TextDecoder('utf8'); + this.regionList = []; + this.onStylesParsedCallback = onStylesParsedCallback; + this._styles = {}; + } + // Helper to allow strings to be decoded instead of the default binary utf8 data. + static StringDecoder() { + return { + decode: (input) => { + if (!input) { + return ''; + } + if (typeof input !== 'string') { + throw new Error('Error - expected string data.'); + } + return decodeURIComponent(encodeURIComponent(input)); + } + }; + } + // If the error is a ParsingError then report it to the consumer if + // possible. If it's not a ParsingError then throw it like normal. + reportOrThrowError(e) { + if (e instanceof ParsingError && + typeof this.onparsingerror === 'function') { + this.onparsingerror(e); + } + else { + throw e; + } + } + // Helper function to parse input into groups separated by 'groupDelim', and + // interprete each group as a key/value pair separated by 'keyValueDelim'. + parseOptions(input, callback, keyValueDelim, groupDelim) { + const groups = groupDelim ? input.split(groupDelim) : [input]; + for (const group of groups) { + if (typeof group !== 'string') { + continue; + } + const kv = group.split(keyValueDelim); + if (kv.length !== 2) { + continue; + } + const k = kv[0]; + const v = kv[1]; + callback(k, v); + } + } + parseCue(input, cue, regionList) { + // Remember the original input if we need to throw an error. + const oInput = input; + // 4.1 WebVTT timestamp + const consumeTimeStamp = () => { + const ts = vttparser_utility_1$1.default.parseTimeStamp(input); + if (ts === null) { + throw new ParsingError(ParsingError.Errors.BadTimeStamp, 'Malformed timestamp: ' + oInput); + } + // Remove time stamp from input. + input = input.replace(/^[^\sa-zA-Z-]+/, ''); + return ts; + }; + // 4.4.2 WebVTT cue settings + const consumeCueSettings = (input, cue) => { + const settings = new Settings(); + this.parseOptions(input, (k, v) => { + let vals, vals0; + switch (k) { + case 'region': + // Find the last region we parsed with the same region id. + for (let i = regionList.length - 1; i >= 0; i--) { + if (regionList[i].id === v) { + settings.set(k, regionList[i].region); + break; + } + } + break; + case 'vertical': + settings.alt(k, v, ['rl', 'lr']); + break; + case 'line': + vals = v.split(','); + vals0 = vals[0]; + settings.integer(k, vals0); + if (settings.percent(k, vals0)) { + settings.set('snapToLines', false); + } + settings.alt(k, vals0, ['auto']); + if (vals.length === 2) { + settings.alt('lineAlign', vals[1], [ + 'start', + 'center', + 'end' + ]); + } + break; + case 'position': + vals = v.split(','); + settings.percent(k, vals[0]); + if (vals.length === 2) { + let positionAlignSettings = ['line-left', 'line-right', + 'center', 'auto', 'left', 'start', 'middle', 'end', 'right']; + settings.alt('positionAlign', vals[1], positionAlignSettings); + } + break; + case 'size': + settings.percent(k, v); + break; + case 'align': + let alignSettings = ['start', 'center', 'end', 'left', 'right', 'middle']; + settings.alt(k, v, alignSettings); + break; + } + }, /:/, /\s/); + // Apply default values for any missing fields. + cue.region = settings.get('region', null); + cue.vertical = settings.get('vertical', ''); + // Workaround to rdar://57719277 + // Waiting Webkit team to provide a proper fix, tracking in rdar://57778093 + cue.line = settings.get('line', typeof cue.line === 'undefined' ? 'auto' : cue.line); + cue.lineAlign = settings.get('lineAlign', 'start'); + cue.snapToLines = settings.get('snapToLines', true); + cue.size = settings.get('size', 100); + let tempAlign = settings.get('align', 'center'); + cue.align = tempAlign === 'middle' ? 'center' : tempAlign; + cue.position = settings.get('position', 'auto'); + let tempPositionAlign = settings.get('positionAlign', { + 'start': 'start', + 'left': 'start', + 'center': 'center', + 'right': 'end', + 'end': 'end', + }, cue.align // We should default to 'auto' here - but might break in other places + ); + let positionAlignMap = { + 'start': 'start', + 'line-left': 'start', + 'left': 'start', + 'center': 'center', + 'middle': 'center', + 'line-right': 'end', + 'right': 'end', + 'end': 'end', + }; + cue.positionAlign = positionAlignMap[tempPositionAlign]; + }; + const skipWhitespace = () => { + input = input.replace(/^\s+/, ''); + }; + // 4.1 WebVTT cue timings. + skipWhitespace(); + cue.startTime = consumeTimeStamp(); // (1) collect cue start time + skipWhitespace(); + if (input.substr(0, 3) !== '-->') { + // (3) next characters must match "-->" + throw new ParsingError(ParsingError.Errors.BadTimeStamp, `Malformed time stamp (time stamps must be separated by '-->'): ${oInput}`); + } + input = input.substr(3); + skipWhitespace(); + cue.endTime = consumeTimeStamp(); // (5) collect cue end time + // 4.1 WebVTT cue settings list. + skipWhitespace(); + consumeCueSettings(input, cue); + } + // 3.4 WebVTT region and WebVTT region settings syntax + parseRegion(input) { + const settings = new Settings(); + this.parseOptions(input, (k, v) => { + switch (k) { + case 'id': + settings.set(k, v); + break; + case 'width': + settings.percent(k, v); + break; + case 'lines': + settings.integer(k, v); + break; + case 'regionanchor': + case 'viewportanchor': { + const xy = v.split(','); + if (xy.length !== 2) { + break; + } + // We have to make sure both x and y parse, so use a temporary + // settings object here. + const anchor = new Settings(); + anchor.percent('x', xy[0]); + anchor.percent('y', xy[1]); + if (!anchor.has('x') || !anchor.has('y')) { + break; + } + settings.set(k + 'X', anchor.get('x')); + settings.set(k + 'Y', anchor.get('y')); + break; + } + case 'scroll': + settings.alt(k, v, ['up']); + break; + } + }, /=/, /\s/); + // Create the region, using default values for any values that were not + // specified. + if (settings.has('id')) { + const region = new vttregion_1.VTTRegion(); + region.width = settings.get('width', 100); + region.lines = settings.get('lines', 3); + region.regionAnchorX = settings.get('regionanchorX', 0); + region.regionAnchorY = settings.get('regionanchorY', 100); + region.viewportAnchorX = settings.get('viewportanchorX', 0); + region.viewportAnchorY = settings.get('viewportanchorY', 100); + region.scroll = settings.get('scroll', ''); + // Register the region. + if (this.onregion) { + this.onregion(region); + } + // Remember the VTTRegion for later in case we parse any VTTCues that + // reference it. + this.regionList.push({ + id: settings.get('id'), + region: region + }); + } + } + // 1.3 WebVTT style block syntax + // Works on single line CSS only + parseStyle(input) { + const parseStyles = (css) => { + const rules = {}; + const properties = css.split(';'); + for (let i = 0; i < properties.length; i++) { + if (properties[i].includes(':')) { + const keyValue = properties[i].split(':', 2); + const key = keyValue[0].trim(); + const value = keyValue[1].trim(); + if (key !== '' && value !== '') { + rules[key] = value; + } + } + } + return rules; + }; + const blocks = input.split('}'); + blocks.pop(); + for (const block of blocks) { + let selector = null; + let styles = null; + const pair = block.split('{'); + if (pair[0]) { + selector = pair[0].trim(); + } + if (pair[1]) { + styles = parseStyles(pair[1]); + } + if (selector && styles) { + this._styles[selector] = styles; + } + } + if (this.onStylesParsedCallback) { + this.onStylesParsedCallback(this._styles); + } + } + // 3.2 WebVTT metadata header syntax + parseHeader(input) { + this.parseOptions(input, function (k, v) { + switch (k) { + case 'Region': + // 3.3 WebVTT region metadata header syntax + this.parseRegion(v); + break; + } + }, /:/); + } + parse(data) { + // If there is no data then we won't decode it, but will just try to parse + // whatever is in buffer already. This may occur in circumstances, for + // example when flush() is called. + if (data) { + // Try to decode the data that we received. + this.buffer += this.decoder.decode(data, { stream: true }); + } + const collectNextLine = () => { + const buffer = this.buffer; + let pos = 0; + const calculateBreakPosition = (buffer, index) => { + const breakPosition = { start: -1, length: -1 }; + if (buffer[index] === '\r') { + breakPosition.start = index; + breakPosition.length = 1; + } + else if (buffer[index] === '\n') { + breakPosition.start = index; + breakPosition.length = 1; + } + else if (buffer[index] === '<') { + if (index + 1 < buffer.length && buffer[index + 1] === 'b') { + if (index + 2 < buffer.length && buffer[index + 2] === 'r') { + let endPosition = index + 2; + while (endPosition < buffer.length) { + if (buffer[endPosition++] === '>') { + break; + } + } + breakPosition.start = index; + breakPosition.length = endPosition - index; + } + } + } + return breakPosition; + }; + let breakPosition = { start: buffer.length, length: 0 }; + while (pos < buffer.length) { + const foundBreakPosition = calculateBreakPosition(buffer, pos); + if (foundBreakPosition.length > 0) { + breakPosition = foundBreakPosition; + break; + } + ++pos; + } + const line = buffer.substr(0, breakPosition.start); + this.buffer = buffer.substr(breakPosition.start + breakPosition.length); + return line; + }; + // 5.1 WebVTT file parsing. + try { + let line; + if (this.state === 'INITIAL') { + // We can't start parsing until we have the first line. + if (!/\r\n|\n/.test(this.buffer)) { + return this; + } + line = collectNextLine(); + // strip of UTF-8 BOM if any + // https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8 + const m = /^()?WEBVTT([ \t].*)?$/.exec(line); + if (!m || !m[0]) { + throw new ParsingError(ParsingError.Errors.BadSignature); + } + this.state = 'HEADER'; + } + let alreadyCollectedLine = false; + while (this.buffer) { + // We can't parse a line until we have the full line. + if (!/\r\n|\n/.test(this.buffer)) { + return this; + } + if (!alreadyCollectedLine) { + line = collectNextLine(); + } + else { + alreadyCollectedLine = false; + } + switch (this.state) { + case 'HEADER': + // 13-18 - Allow a header (metadata) under the WEBVTT line. + if (line.includes(':')) { + this.parseHeader(line); + } + else if (!line) { + // An empty line terminates the header and starts the body (cues). + this.state = 'ID'; + } + continue; + case 'NOTE': + // Ignore NOTE blocks. + if (!line) { + this.state = 'ID'; + } + continue; + case 'STYLE': + // Parse style blocks. + if (!line) { + this.parseStyle(this.styleCollector); + this.state = 'ID'; + this.styleCollector = ''; + } + else { + this.styleCollector += line; + } + continue; + case 'ID': + // Check for the start of NOTE blocks. + if (/^NOTE($|[ \t])/.test(line)) { + this.state = 'NOTE'; + break; + } + if (/^STYLE($|[ \t])/.test(line)) { + this.state = 'STYLE'; + break; + } + // 19-29 - Allow any number of line terminators, then initialize new cue values. + if (!line) { + continue; + } + this.cue = new vttcue_1$1.VTTCue(0, 0, ''); + this.state = 'CUE'; + // 30-39 - Check if self line contains an optional identifier or timing data. + if (!line.includes('-->')) { + this.cue.id = line; + continue; + } + // Process line as start of a cue. + /* falls through */ + case 'CUE': + // 40 - Collect cue timings and settings. + try { + this.parseCue(line, this.cue, this.regionList); + } + catch (e) { + this.reportOrThrowError(e); + // In case of an error ignore rest of the cue. + this.cue = null; + this.state = 'BADCUE'; + continue; + } + this.state = 'CUETEXT'; + continue; + case 'CUETEXT': { + const hasSubstring = line.includes('-->'); + // 34 - If we have an empty line then report the cue. + // 35 - If we have the special substring '-->' then report the cue, + // but do not collect the line as we need to process the current + // one as a new cue. + if (!line || hasSubstring) { + // We are done parsing self cue. + alreadyCollectedLine = true; + if (this.oncue) { + this.oncue(this.cue); + } + this.cue = null; + this.state = 'ID'; + continue; + } + if (this.cue.text) { + this.cue.text += '\n'; + } + this.cue.text += line; + continue; + } + case 'BADCUE': // BADCUE + // 54-62 - Collect and discard the remaining cue. + if (!line) { + this.state = 'ID'; + } + continue; + } + } + } + catch (e) { + this.reportOrThrowError(e); + // If we are currently parsing a cue, report what we have. + if (this.state === 'CUETEXT' && this.cue && this.oncue) { + this.oncue(this.cue); + } + this.cue = null; + // Enter BADWEBVTT state if header was not parsed correctly otherwise + // another exception occurred so enter BADCUE state. + this.state = this.state === 'INITIAL' ? 'BADWEBVTT' : 'BADCUE'; + } + return this; + } + flush() { + try { + // Finish decoding the stream. + this.buffer += this.decoder.decode(); + // Synthesize the end of the current cue or region. + if (this.cue || this.state === 'HEADER') { + this.buffer += '\n\n'; + this.parse(); + } + // If we've flushed, parsed, and we're still on the INITIAL state then + // that means we don't have enough of the stream to parse the first + // line. + if (this.state === 'INITIAL') { + throw new ParsingError(ParsingError.Errors.BadSignature); + } + } + catch (e) { + this.reportOrThrowError(e); + } + if (this.onflush) { + this.onflush(); + } + return this; + } + styles() { + return this._styles; + } + } + vttparser.default = WebVTTParser; + vttparser.WebVTTParser = WebVTTParser; + + var vtthtmlrenderer = {}; + + /** + * Copyright 2013 vtt.js Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + Object.defineProperty(vtthtmlrenderer, "__esModule", { value: true }); + const vttcue_1 = vttcue; + vtthtmlrenderer.VTTCue = vttcue_1.VTTCue; + const vttparser_utility_1 = vttparserUtility; + const CUE_BACKGROUND_PADDING = '1.5%'; + const OVERLAP_PADDING = 0; // Pixels + const PERCENT_EDGE_OFFSET = 0.05; + const MAX_PLACEMENT_ATTEMPTS = 10; + const DEFAULT_LINE_HEIGHT = 41; // line height for 36px Helvetica font + const foregroundStyleSelector = '::cue'; + const backgroundStyleSelector = '::-webkit-media-text-track-display'; + const classSelectorRegex = /^(::cue\()(\..*)(\))/; + const idSelectorRegex = /^(::cue\()(#.*)(\))/; + const typeSelectorRegex = /^(::cue\()(c|i|b|u|ruby|rt|v|lang)(\))/; + const globalStyleRegex = [ + classSelectorRegex, + idSelectorRegex, + typeSelectorRegex + ]; + // This is a list of all the Unicode characters that have a strong + // right-to-left category. What this means is that these characters are + // written right-to-left for sure. It was generated by pulling all the strong + // right-to-left characters out of the Unicode data table. That table can + // found at: http://www.unicode.org/Public/UNIDATA/UnicodeData.txt + const strongRTLRanges = [ + [0x5be, 0x5be], + [0x5c0, 0x5c0], + [0x5c3, 0x5c3], + [0x5c6, 0x5c6], + [0x5d0, 0x5ea], + [0x5f0, 0x5f4], + [0x608, 0x608], + [0x60b, 0x60b], + [0x60d, 0x60d], + [0x61b, 0x61b], + [0x61e, 0x64a], + [0x66d, 0x66f], + [0x671, 0x6d5], + [0x6e5, 0x6e6], + [0x6ee, 0x6ef], + [0x6fa, 0x70d], + [0x70f, 0x710], + [0x712, 0x72f], + [0x74d, 0x7a5], + [0x7b1, 0x7b1], + [0x7c0, 0x7ea], + [0x7f4, 0x7f5], + [0x7fa, 0x7fa], + [0x800, 0x815], + [0x81a, 0x81a], + [0x824, 0x824], + [0x828, 0x828], + [0x830, 0x83e], + [0x840, 0x858], + [0x85e, 0x85e], + [0x8a0, 0x8a0], + [0x8a2, 0x8ac], + [0x200f, 0x200f], + [0xfb1d, 0xfb1d], + [0xfb1f, 0xfb28], + [0xfb2a, 0xfb36], + [0xfb38, 0xfb3c], + [0xfb3e, 0xfb3e], + [0xfb40, 0xfb41], + [0xfb43, 0xfb44], + [0xfb46, 0xfbc1], + [0xfbd3, 0xfd3d], + [0xfd50, 0xfd8f], + [0xfd92, 0xfdc7], + [0xfdf0, 0xfdfc], + [0xfe70, 0xfe74], + [0xfe76, 0xfefc], + [0x10800, 0x10805], + [0x10808, 0x10808], + [0x1080a, 0x10835], + [0x10837, 0x10838], + [0x1083c, 0x1083c], + [0x1083f, 0x10855], + [0x10857, 0x1085f], + [0x10900, 0x1091b], + [0x10920, 0x10939], + [0x1093f, 0x1093f], + [0x10980, 0x109b7], + [0x109be, 0x109bf], + [0x10a00, 0x10a00], + [0x10a10, 0x10a13], + [0x10a15, 0x10a17], + [0x10a19, 0x10a33], + [0x10a40, 0x10a47], + [0x10a50, 0x10a58], + [0x10a60, 0x10a7f], + [0x10b00, 0x10b35], + [0x10b40, 0x10b55], + [0x10b58, 0x10b72], + [0x10b78, 0x10b7f], + [0x10c00, 0x10c48], + [0x1ee00, 0x1ee03], + [0x1ee05, 0x1ee1f], + [0x1ee21, 0x1ee22], + [0x1ee24, 0x1ee24], + [0x1ee27, 0x1ee27], + [0x1ee29, 0x1ee32], + [0x1ee34, 0x1ee37], + [0x1ee39, 0x1ee39], + [0x1ee3b, 0x1ee3b], + [0x1ee42, 0x1ee42], + [0x1ee47, 0x1ee47], + [0x1ee49, 0x1ee49], + [0x1ee4b, 0x1ee4b], + [0x1ee4d, 0x1ee4f], + [0x1ee51, 0x1ee52], + [0x1ee54, 0x1ee54], + [0x1ee57, 0x1ee57], + [0x1ee59, 0x1ee59], + [0x1ee5b, 0x1ee5b], + [0x1ee5d, 0x1ee5d], + [0x1ee5f, 0x1ee5f], + [0x1ee61, 0x1ee62], + [0x1ee64, 0x1ee64], + [0x1ee67, 0x1ee6a], + [0x1ee6c, 0x1ee72], + [0x1ee74, 0x1ee77], + [0x1ee79, 0x1ee7c], + [0x1ee7e, 0x1ee7e], + [0x1ee80, 0x1ee89], + [0x1ee8b, 0x1ee9b], + [0x1eea1, 0x1eea3], + [0x1eea5, 0x1eea9], + [0x1eeab, 0x1eebb], + [0x10fffd, 0x10fffd] + ]; + class StyleBox { + // Apply styles to a div. If there is no div passed then it defaults to the + // div on 'this'. + applyStyles(styles, div) { + div = div || this.div; + for (const prop in styles) { + if (styles.hasOwnProperty(prop)) { + div.style[prop] = styles[prop]; + } + } + } + formatStyle(val, unit) { + return val === 0 ? '0' : val + unit; + } + } + vtthtmlrenderer.StyleBox = StyleBox; + // Constructs the computed display state of the cue (a div). Places the div + // into the overlay which should be a block level element (usually a div). + class CueStyleBox extends StyleBox { + constructor(window, cue, foregroundStyleOptions, backgroundStyleOptions, globalStyleCollection) { + super(); + this.cue = cue; + let textAlignMap = { + 'start': 'left', + 'line-left': 'left', + 'left': 'left', + 'center': 'center', + 'middle': 'center', + 'line-right': 'right', + 'right': 'right', + 'end': 'right', + }; + let textAlign = textAlignMap[cue.positionAlign] || cue.align; + let styles = { + textAlign: textAlign, + whiteSpace: 'pre-line', + position: 'absolute' + }; + styles.direction = this.determineBidi(this.cueDiv); + styles.writingMode = this.directionSettingToWritingMode(cue.vertical); + styles.unicodeBidi = 'plaintext'; + // Create an absolutely positioned div that will be used to position the cue + // div. Note, all WebVTT cue-setting alignments are equivalent to the CSS + // mirrors of them except "middle" which is "center" in CSS. + this.div = window.document.createElement('div'); + this.applyStyles(styles); + styles = { + backgroundColor: backgroundStyleOptions.backgroundColor, + display: 'inline-block' + }; + // If the background opacity is 0 or undefined don't want to increase + // the cue size by adding padding to the background div + if (this.parseOpacity(styles.backgroundColor)) { + styles.padding = '5px'; + styles.borderRadius = '5px'; + } + this.backgroundDiv = window.document.createElement('div'); + this.applyStyles(styles, this.backgroundDiv); + styles = { + color: foregroundStyleOptions.color, + backgroundColor: foregroundStyleOptions.backgroundColor, + textShadow: foregroundStyleOptions.textShadow, + fontSize: foregroundStyleOptions.fontSize, + fontFamily: foregroundStyleOptions.fontFamily, + position: 'relative', + left: '0', + right: '0', + top: '0', + bottom: '0', + display: 'inline-block', + textOrientation: 'upright' + }; + styles.writingMode = this.directionSettingToWritingMode(cue.vertical); + styles.unicodeBidi = 'plaintext'; + // Parse our cue's text into a DOM tree rooted at 'cueDiv'. This div will + // have inline-block positioning and will function as the cue foreground box. + this.cueDiv = vttparser_utility_1.default.parseContent(window, cue, globalStyleCollection); + this.applyStyles(styles, this.cueDiv); + this.backgroundDiv.appendChild(this.cueDiv); + this.div.appendChild(this.backgroundDiv); + // Calculate the distance from the reference edge of the viewport to the text + // position of the cue box. The reference edge will be resolved later when + // the box orientation styles are applied. + let textPos = 0; + if (typeof cue.position === 'number') { + let cueAlignment = cue.positionAlign || cue.align; + if (cueAlignment) { + switch (cueAlignment) { + case 'start': + case 'left': + textPos = cue.position; + break; + case 'center': + case 'middle': + textPos = cue.position - cue.size / 2; + break; + case 'end': + case 'right': + textPos = cue.position - cue.size; + break; + } + } + } + // Horizontal box orientation; textPos is the distance from the left edge of the + // area to the left edge of the box and cue.size is the distance extending to + // the right from there. + if (cue.vertical === '') { + this.applyStyles({ + left: this.formatStyle(textPos, '%'), + width: this.formatStyle(cue.size, '%') + }); + // Vertical box orientation; textPos is the distance from the top edge of the + // area to the top edge of the box and cue.size is the height extending + // downwards from there. + } + else { + this.applyStyles({ + top: this.formatStyle(textPos, '%'), + height: this.formatStyle(cue.size, '%') + }); + } + } + determineBidi(cueDiv) { + let nodeStack = []; + let text = ''; + let charCode; + if (!cueDiv || !cueDiv.childNodes) { + return 'ltr'; + } + function pushNodes(nodeStack, node) { + for (let i = node.childNodes.length - 1; i >= 0; i--) { + nodeStack.push(node.childNodes[i]); + } + } + function nextTextNode(nodeStack) { + if (!nodeStack || !nodeStack.length) { + return null; + } + let node = nodeStack.pop(); + let text = node.textContent || node.innerText; + if (text) { + // TODO: This should match all unicode type B characters (paragraph + // separator characters). See issue #115. + const m = /^.*(\n|\r)/.exec(text); + if (m) { + nodeStack.length = 0; + return m[0]; + } + return text; + } + if (node.tagName === 'ruby') { + return nextTextNode(nodeStack); + } + if (node.childNodes) { + pushNodes(nodeStack, node); + return nextTextNode(nodeStack); + } + } + function isContainedInCharacterList(charCode, characterList) { + for (const currentRange of characterList) { + if (charCode >= currentRange[0] && charCode <= currentRange[1]) { + return true; + } + } + return false; + } + pushNodes(nodeStack, cueDiv); + while ((text = nextTextNode(nodeStack))) { + for (let i = 0; i < text.length; i++) { + charCode = text.charCodeAt(i); + if (isContainedInCharacterList(charCode, strongRTLRanges)) { + return 'rtl'; + } + } + } + return 'ltr'; + } + parseOpacity(colorString) { + if (!colorString || typeof colorString !== 'string') { + return null; + } + // Trim whitespace, strip out "rgba(" and ")" from the string + colorString = colorString.replace(/ /g, '').replace('rgba(', '').replace(')', ''); + // Split r, g, b, a into an array for easier processing - we just need the opacity + const parts = colorString.split(','); + if (parts && parts.length >= 4) { + return parts[3]; + } + return null; + } + directionSettingToWritingMode(directionSetting) { + if (directionSetting === '') { + return 'horizontal-tb'; + } + else if (directionSetting === 'lr') { + return 'vertical-lr'; + } + else { + return 'vertical-rl'; + } + } + move(box) { + this.applyStyles({ + top: this.formatStyle(box.top, 'px'), + bottom: this.formatStyle(box.bottom, 'px'), + left: this.formatStyle(box.left, 'px'), + right: this.formatStyle(box.right, 'px'), + height: this.formatStyle(box.height, 'px'), + width: this.formatStyle(box.width, 'px') + }); + } + } + vtthtmlrenderer.CueStyleBox = CueStyleBox; + // Represents the co-ordinates of an Element in a way that we can easily + // compute things with such as if it overlaps or intersects with another Element. + // Can initialize it with either a StyleBox or another BoxPosition. + class BoxPosition { + constructor(obj) { + function isVertical(cue) { + return cue && cue.vertical !== ''; + } + if (obj instanceof CueStyleBox && obj.cue) { + if (isVertical(obj.cue)) { + this.property = 'width'; + } + else { + this.property = 'height'; + } + } + else if (obj instanceof BoxPosition) { + this.property = obj.property || 'height'; + } + // Either a BoxPosition was passed in and we need to copy it, or a StyleBox + // was passed in and we need to copy the results of 'getBoundingClientRect' + // as the object returned is readonly. All co-ordinate values are in reference + // to the viewport origin (top left). + let lh, slh, height, width, top, rect; + if (obj instanceof CueStyleBox && obj.div) { + height = obj.div.offsetHeight; + width = obj.div.offsetWidth; + top = obj.div.offsetTop; + const outerDiv = obj.div.firstChild; + // When we're initializing a BoxPosition with a CueStyleBox want to use the child element which just has + // the width / length of the text, otherwise the full length will overflow outside of the container + // causing the CueStyleBox.within function to return false, and then the overlap algorithm to fail + if (outerDiv) { + rect = outerDiv.getBoundingClientRect(); + } + else { + rect = obj.div.getBoundingClientRect(); + } + lh = (rect && rect[this.property]) || null; + if (outerDiv && outerDiv.firstChild) { + const innerDiv = outerDiv.firstChild; + if (innerDiv && typeof innerDiv.textContent === 'string') { + const numOfLines = this.calculateNewLines(innerDiv.textContent); + slh = lh / numOfLines; + } + } + } + else if (obj instanceof BoxPosition) { + rect = obj; + } + this.left = rect.left; + this.right = rect.right; + this.top = rect.top || top; + this.height = rect.height || height; + this.bottom = rect.bottom || top + (rect.height || height); + this.width = rect.width || width; + this.lineHeight = lh !== null ? lh : rect.lineHeight; + this.singleLineHeight = slh !== null ? slh : rect.singleLineHeight; + if (!this.singleLineHeight) { + this.singleLineHeight = DEFAULT_LINE_HEIGHT; + } + } + calculateNewLines(str) { + let numOfLines = 1; + for (let i = 0; i < str.length; i++) { + if (str[i] === '\n') { + numOfLines++; + } + } + return numOfLines; + } + // Move the box along a particular axis. Optionally pass in an amount to move + // the box. If no amount is passed then the default is the line height of the + // box. + move(axis, toMove) { + toMove = toMove !== undefined ? toMove : this.singleLineHeight; + switch (axis) { + case '+x': + this.left += toMove; + this.right += toMove; + break; + case '-x': + this.left -= toMove; + this.right -= toMove; + break; + case '+y': + this.top += toMove; + this.bottom += toMove; + break; + case '-y': + this.top -= toMove; + this.bottom -= toMove; + break; + } + } + // Check if this box overlaps another box, b2. + overlaps(b2) { + return (this.left < b2.right && + this.right > b2.left && + this.top < b2.bottom && + this.bottom > b2.top); + } + // Check if this box overlaps any other boxes in boxes. + overlapsAny(boxes) { + for (const box of boxes) { + if (this.overlaps(box)) { + return true; + } + } + return false; + } + // Check if this box is within another box. + within(container) { + return (this.top >= container.top && + this.bottom <= container.bottom && + this.left >= container.left && + this.right <= container.right); + } + // Check if this box is entirely within the container or it is overlapping + // on the edge opposite of the axis direction passed. For example, if "+x" is + // passed and the box is overlapping on the left edge of the container, then + // move the left edge of the box to align with the left edge of the container + moveIfOutOfBounds(container, axis) { + switch (axis) { + case '+x': + if (this.left < container.left) { + this.left = container.left; + this.right = this.left + this.width; + } + break; + case '-x': + if (this.right > container.right) { + this.right = container.right; + this.left = this.right - this.width; + } + break; + case '+y': + if (this.top < container.top) { + this.top = container.top; + this.bottom = this.top + this.height; + } + break; + case '-y': + if (this.bottom > container.bottom) { + this.bottom = container.bottom; + this.top = this.bottom - this.height; + } + break; + } + } + // Convert the positions from this box to CSS compatible positions using + // the reference container's positions. This has to be done because this + // box's positions are in reference to the viewport origin, whereas, CSS + // values are in reference to their respective edges. + toCSSCompatValues(reference) { + return { + top: this.top - reference.top, + bottom: reference.bottom - this.bottom, + left: this.left - reference.left, + right: reference.right - this.right, + height: this.height, + width: this.width + }; + } + // Get an object that represents the box's position without anything extra. + // Can pass a StyleBox or HTMLElement. + static getSimpleBoxPosition(obj) { + let element = null; + if (obj instanceof StyleBox && obj.div) { + element = obj.div; + } + else if (obj instanceof HTMLElement) { + element = obj; + } + let height = element.offsetHeight || 0; + let width = element.offsetWidth || 0; + let top = element.offsetTop || 0; + let bottom = top + height; + let rect = element.getBoundingClientRect(); + const { left, right } = rect; + if (rect.top) { + top = rect.top; + } + if (rect.height) { + height = rect.height; + } + if (rect.width) { + width = rect.width; + } + if (rect.bottom) { + bottom = rect.bottom; + } + return { left, right, top, height, bottom, width }; + } + static getBoxPosition(boxPositions, direction) { + if (boxPositions && boxPositions.length > 0) { + let savedIndex = 0; + let maxValue = boxPositions[0][direction]; + for (let i = 0; i < boxPositions.length; i++) { + if (direction in ['top', 'right']) { + if (boxPositions[i][direction] > maxValue) { + savedIndex = i; + maxValue = boxPositions[i][direction]; + } + } + else if (direction in ['bottom', 'left']) { + if (boxPositions[i][direction] < maxValue) { + savedIndex = i; + maxValue = boxPositions[i][direction]; + } + } + } + return boxPositions[savedIndex]; + } + else { + return null; + } + } + // Move our box (cue), b, so there's no overlap with any other cues along that axis + // For example, if our axis is +y, we'll want to place our box right above the top-most cue + static moveToMinimumDistancePlacement(b, axis, boxes) { + if (b.property === 'height') { + if (axis === '+y') { + b.top = boxes.topMostBoxPosition.bottom + OVERLAP_PADDING; + b.bottom = b.top + b.height; + } + else if (axis === '-y') { + b.bottom = boxes.bottomMostBoxPosition.top - OVERLAP_PADDING; + b.top = b.bottom - b.height; + } + // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition + } + else if (b.property === 'width') { + if (axis === '+x') { + b.left = boxes.rightMostBoxPosition.right + OVERLAP_PADDING; + b.right = b.left + b.width; + } + else if (axis === '-x') { + b.right = boxes.leftMostBoxPosition.left - OVERLAP_PADDING; + b.left = b.right - b.width; + } + } + } + // Move a StyleBox to its specified, or next best, position. The containerBox + // is the box that contains the StyleBox, such as a div. boxPositions are + // a list of other boxes that the styleBox can't overlap with. + static moveBoxToLinePosition(styleBox, containerBox, boxPositions) { + // Find the best position for a cue box, b, on the video. The axis parameter + // is a list of axis, the order of which, it will move the box along. For example: + // Passing ["+x", "-x"] will move the box first along the x axis in the positive + // direction. If it doesn't find a good position for it there it will then move + // it along the x axis in the negative direction. + function findBestPosition(b, axis) { + let minimumDistanceBoxes; + for (let i = 0; i < axis.length; i++) { + // Step 1. check if our current cue is within our render area boundary and re-position our cue if needed + b.moveIfOutOfBounds(containerBox, axis[i]); + // We'll do our best to place the next cue, but want + // another way to exit this while loop in case we get stuck + let currentAttempt = 0; + let attemptedMinimumDistancePlacement = false; + // Step 2. check if our current cue overlaps any other cues and re-position our cue if needed + while (b.overlapsAny(boxPositions)) { + if (currentAttempt > MAX_PLACEMENT_ATTEMPTS - 1) { + break; + } + if (!attemptedMinimumDistancePlacement) { + if (boxPositions && boxPositions.length > 0) { + // Calculating here so we don't spend time on this calculation unless there was collision + if (!minimumDistanceBoxes) { + minimumDistanceBoxes = { + topMostBoxPosition: BoxPosition.getBoxPosition(boxPositions, 'top'), + bottomMostBoxPosition: BoxPosition.getBoxPosition(boxPositions, 'bottom'), + leftMostBoxPosition: BoxPosition.getBoxPosition(boxPositions, 'left'), + rightMostBoxPosition: BoxPosition.getBoxPosition(boxPositions, 'right') + }; + } + BoxPosition.moveToMinimumDistancePlacement(b, axis[i], minimumDistanceBoxes); + } + attemptedMinimumDistancePlacement = true; + } + else { + b.move(axis[i]); + } + currentAttempt++; + } + } + // Our original position if we were already in bounds and + // didn't overlap with other cues, otherwise was moved by above + return b; + } + function computeLinePos(cue) { + if (typeof cue.line === 'number' && + (cue.snapToLines || (cue.line >= 0 && cue.line <= 100))) { + return cue.line; + } + if (!cue.track || + !cue.track.textTrackList || + !cue.track.textTrackList.mediaElement) { + return -1; + } + let count = 0; + const track = cue.track, trackList = track.textTrackList; + for (let i = 0; i < trackList.length && trackList[i] !== track; i++) { + if (trackList[i].mode === 'showing') { + count++; + } + } + return ++count * -1; + } + const cue = styleBox.cue; + let boxPosition = new BoxPosition(styleBox), linePos = computeLinePos(cue), axis = [], size; + // If we have a line number to align the cue to. + if (cue.snapToLines) { + let position = 0; + switch (cue.vertical) { + case '': + axis = ['+y', '-y']; + size = 'height'; + break; + case 'rl': + axis = ['+x', '-x']; + size = 'width'; + break; + case 'lr': + axis = ['-x', '+x']; + size = 'width'; + break; + } + const step = boxPosition.lineHeight, maxPosition = containerBox[size] + step, initialAxis = axis[0]; + // If computed line position returns negative then line numbers are + // relative to the bottom of the video instead of the top. Therefore, we + // need to increase our initial position by the length or width of the + // video, depending on the writing direction, and reverse our axis directions. + // Provide a reasonable automatic position that is closest to the "towards direction" + if (linePos < 0) { + let initialOffset = 0; + switch (cue.vertical) { + case '': + initialOffset = containerBox.height - step - containerBox.height * PERCENT_EDGE_OFFSET; // default to bottom + break; + // Depending on positioning, chromium will fip layout when writing-mode:vertical-rl is set + // https://chromium.googlesource.com/chromium/src/+/master/third_party/blink/renderer/core/layout/README.md + // In this case, chromium writing-mode:vertical-lr property defaults the div to the left, and writing-mode:vertical-rl defaults the div to the right + // For WebVTT default values we want to render the initial values at the opposite end, so will use the equations below to adjust + case 'rl': + initialOffset = -containerBox.width + step + containerBox.width * PERCENT_EDGE_OFFSET; // rl: 100% <----- 0%; default to left + break; + case 'lr': + initialOffset = -containerBox.width + step + containerBox.width * PERCENT_EDGE_OFFSET; // lr: 0% -----> 100%; default to right + break; + } + position = initialOffset; + axis = axis.reverse(); + } + else { + // We have been given an actual line position. Calculate appropriate layout. + switch (cue.vertical) { + case '': + position = step * Math.round(linePos); // step down from top + break; + case 'rl': + position = containerBox.width - step * Math.round(linePos); // step down from right side + break; + case 'lr': + position = step * Math.round(linePos); // step down from left side + break; + } + // If the specified initial position is greater then the max position then + // clamp the box to the amount of steps it would take for the box to + // reach the max position. + if (Math.abs(position) > maxPosition) { + position = position < 0 ? -1 : 1; + position *= Math.ceil(maxPosition / step) * step; + } + } + // Move the box to the specified position. This may not be its best position. + boxPosition.move(initialAxis, position); + } + else { + // If we have a percentage line value for the cue. + const percentageBase = cue.vertical === '' ? containerBox.height : containerBox.width; + const calculatedPercentage = (boxPosition.lineHeight / percentageBase) * 100; + switch (cue.lineAlign) { + case 'center': + linePos -= calculatedPercentage / 2; + break; + case 'end': + linePos -= calculatedPercentage; + break; + } + // Apply initial line position to the cue box. + switch (cue.vertical) { + case '': + styleBox.applyStyles({ + top: styleBox.formatStyle(linePos, '%') // step down from top + }); + break; + case 'rl': + styleBox.applyStyles({ + right: styleBox.formatStyle(linePos, '%') // step down from right + }); + break; + case 'lr': + styleBox.applyStyles({ + left: styleBox.formatStyle(linePos, '%') // step down from left + }); + break; + } + axis = ['+y', '-y', '+x', '-x']; + // Using this as a hint of which direction we want to place the next cues + // Only doing this for horizontally positioned subtitles for now + // TODO: rdar://50386682 (Improve overlap rendering for vertical subtitles [Nice to Have]) + if (cue.axis === '+y') { + axis = ['+y', '-y', '+x', '-x']; + } + else if (cue.axis === '-y') { + axis = ['-y', '+y', '+x', '-x']; + } + // Get the box position again after we've applied the specified positioning to it. + boxPosition = new BoxPosition(styleBox); + } + const bestPosition = findBestPosition(boxPosition, axis); + styleBox.move(bestPosition.toCSSCompatValues(containerBox)); + } + } + vtthtmlrenderer.BoxPosition = BoxPosition; + class WebVTTRenderer { + constructor(window, overlay, loggingEnabled = true) { + if (!window) { + return null; + } + this.window = window; + this.overlay = overlay; + this.loggingEnabled = loggingEnabled; + this.foregroundStyleOptions = { + fontFamily: 'Helvetica', + fontSize: '36px', + color: 'rgba(255, 255, 255, 1)', + textShadow: '', + backgroundColor: 'rgba(0, 0, 0, 0)' // Text Highlight + }; + this.backgroundStyleOptions = { + backgroundColor: 'rgba(0, 0, 0, 0.5)' + }; + this.globalStyleCollection = {}; + const paddedOverlay = window.document.createElement('div'); + paddedOverlay.style.position = 'absolute'; + paddedOverlay.style.left = '0'; + paddedOverlay.style.right = '0'; + paddedOverlay.style.top = '0'; + paddedOverlay.style.bottom = '0'; + paddedOverlay.style.margin = CUE_BACKGROUND_PADDING; + this.paddedOverlay = paddedOverlay; + overlay.appendChild(this.paddedOverlay); + this.initSubtitleCSS(); + } + /** + * Sometimes, certain CSS isn't being applied to the first cue rendered + * We can init some of the CSS here by rendering a dummy cue + */ + initSubtitleCSS() { + const dummyCue = new vttcue_1.VTTCue(0, 0, `String to init CSS - Won't be visible to user`); + const dummyCueTextTrack = [dummyCue]; + // Hide the overlay so we don't get a flash of the text + this.paddedOverlay.style.opacity = '0'; + this.processCues(dummyCueTextTrack); + this.processCues([]); + this.paddedOverlay.style.opacity = '1'; + } + convertCueToDOMTree(cue) { + if (!cue) { + return null; + } + return vttparser_utility_1.default.parseContent(this.window, cue, this.globalStyleCollection); + } + setStyles(styles) { + function applyStyles(collection, newStyles, confine) { + for (const prop in newStyles) { + if (newStyles.hasOwnProperty(prop)) { + if (confine === true && collection[prop] !== undefined) { + collection[prop] = newStyles[prop]; + } + else if (confine === false) { + collection[prop] = newStyles[prop]; + } + } + } + } + for (const selector in styles) { + let confinedStyleOption = false; + let styleOptions = null; + if (selector === foregroundStyleSelector) { + styleOptions = this.foregroundStyleOptions; + confinedStyleOption = true; + } + else if (selector === backgroundStyleSelector) { + styleOptions = this.backgroundStyleOptions; + confinedStyleOption = true; + } + const selectorStyles = styles[selector]; + if (confinedStyleOption === true) { + // Limited set of allowed styles to update + applyStyles(styleOptions, selectorStyles, confinedStyleOption); + } + else { + // Styles with a selector. Parse out selector: ::cue() + for (let index = 0; index < globalStyleRegex.length; index++) { + const regex = globalStyleRegex[index]; + const match = regex.exec(selector); + if (match && match.length === 4) { + const newSelector = match[2]; + const newCollection = {}; + applyStyles(newCollection, selectorStyles, confinedStyleOption); + this.globalStyleCollection[newSelector] = newCollection; + } + } + } + } + this.initSubtitleCSS(); + if (this.loggingEnabled) { + console.log('WebVTTRenderer setStyles foregroundStyleOptions: ' + JSON.stringify(this.foregroundStyleOptions)); + console.log('WebVTTRenderer setStyles backgroundStyleOptions: ' + JSON.stringify(this.backgroundStyleOptions)); + console.log('WebVTTRenderer setStyles globalStyleCollection: ' + JSON.stringify(this.globalStyleCollection)); + } + } + /** + * Runs the WebVTT processing model over the cues passed to it. + * See https://www.w3.org/TR/webvtt1/#rendering for implementation details. + * + * @param {VTTCue|TextTrackCueList} cues list of cues to be rendered + */ + processCues(cues) { + if (!cues) { + return; + } + // Remove all previous children. + while (this.paddedOverlay.firstChild) { + this.paddedOverlay.removeChild(this.paddedOverlay.firstChild); + } + function sortCues(cues) { + const cueList = []; + let avgLine = 0; + for (let i = 0; i < cues.length; i++) { + let cue = cues[i]; + // If any of the line positions aren't a number return our original TextTrackCueList + if (typeof cue.line !== 'number') { + return cues; + } + avgLine += cue.line; + cueList.push(cue); + } + avgLine = avgLine / cues.length; + if (avgLine > 50) { + // Render highest line value first + cueList.forEach(function (cue) { + cue.axis = '-y'; + }); + cueList.sort((a, b) => b.line - a.line); + } + else { + // Render lowest line value first + cueList.forEach(function (cue) { + cue.axis = '+y'; + }); + cueList.sort((a, b) => a.line - b.line); + } + // Just a normal array where we had a TextTrackCueList before, but should be okay + return cueList; + } + // Determine if we need to compute the display states of the cues. This could + // be the case if a cue's state has been changed since the last computation or + // if it has not been computed yet. + function shouldCompute(cues) { + for (let i = 0; i < cues.length; i++) { + if (cues[i].hasBeenReset || !cues[i].displayState) { + return true; + } + } + return false; + } + // We don't need to recompute the cues' display states. Just reuse them. + if (!shouldCompute(cues)) { + for (let i = 0; i < cues.length; i++) { + this.paddedOverlay.appendChild(cues[i].displayState); + } + return; + } + const boxPositions = [], containerBox = BoxPosition.getSimpleBoxPosition(this.paddedOverlay); + // cues is of type TextTrackCueList which has a length property + if (cues.length > 1) { + cues = sortCues(cues); + } + for (let i = 0; i < cues.length; i++) { + let cue = cues[i]; + // Compute the initial position and styles of the cue div. + let styleBox = new CueStyleBox(this.window, cue, this.foregroundStyleOptions, this.backgroundStyleOptions, this.globalStyleCollection); + this.paddedOverlay.appendChild(styleBox.div); + // Move the cue div to it's correct line position. + BoxPosition.moveBoxToLinePosition(styleBox, containerBox, boxPositions); + // Remember the computed div so that we don't have to recompute it later + // if we don't have too. + cue.displayState = styleBox.div; + boxPositions.push(BoxPosition.getSimpleBoxPosition(styleBox)); + } + } + /** + * Update the dimensions of the overlay container + * @param width width in pixels + * @param height height in pixels + */ + setSize(width, height) { + if (width) { + this.overlay.style.width = width + "px"; + } + if (height) { + this.overlay.style.height = height + "px"; + } + } + /** + * Returns the HTMLElement passed in to instantiate this class + */ + getOverlay() { + return this.overlay; + } + } + vtthtmlrenderer.default = WebVTTRenderer; + vtthtmlrenderer.WebVTTRenderer = WebVTTRenderer; + + (function (exports) { + /** + * Copyright 2013 vtt.js Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + function __export(m) { + for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p]; + } + Object.defineProperty(exports, "__esModule", { value: true }); + // Default exports for Node. Export the extended versions of VTTCue and + // VTTRegion in Node since we likely want the capability to convert back and + // forth between JSON. If we don't then it's not that big of a deal since we're + // off browser. + __export(vttparser); + __export(vtthtmlrenderer); + }(lib)); + + + const tsTimescale = 90000; // MPEG-TS timescale value + function isExtendedTextTrackCue(value) { + return Boolean(value.webVTTCue); + } + // String.prototype.startsWith is not supported in IE11 + function startsWith(input, search, position) { + return input.substr(position || 0, search.length) === search; + } + function cueString2millis(timeString) { + let ts = parseInt(timeString.substr(-3)); + const secs = parseInt(timeString.substr(-6, 2)); + const mins = parseInt(timeString.substr(-9, 2)); + const hours = timeString.length > 9 ? parseInt(timeString.substr(0, timeString.indexOf(':'))) : 0; + if (!isFiniteNumber(ts) || !isFiniteNumber(secs) || !isFiniteNumber(mins) || !isFiniteNumber(hours)) { + return -1; + } + ts += 1000 * secs; + ts += 60000 * mins; + ts += 3600000 * hours; + return ts; + } + // From https://github.com/darkskyapp/string-hash + function hash(text) { + let hash = 5381; + let i = text.length; + while (i) { + hash = (hash * 33) ^ text.charCodeAt(--i); + } + return (hash >>> 0).toString(); + } + function snapToHalfSecond(time) { + const lower = Math.floor(time); + const mid = lower + 0.5; + const upper = lower + 1; + let snappedTime; + if (time >= mid) { + snappedTime = time - mid >= upper - time ? upper : mid; + } + else { + snappedTime = time - lower >= mid - time ? mid : lower; + } + return snappedTime; + } + // find value x where: + // x = value + n * cardinality, for some integer n such that: + // |x - reference| <= cardinality / 2 + function wrapPtsInteger(value, reference = 0, cardinality = 8589934592) { + if (!Number.isFinite(reference)) { + return value; + } + const midpoint = cardinality / 2; + const diff = Math.abs(value - reference) % cardinality; + const offset = diff > midpoint ? cardinality - diff : -diff; + const direction = value > reference ? -1 : 1; + return reference + direction * offset; + } + const HLSVTTParser = { + parse: function (vttByteArray, initPTS, // initPTS of current cc + wrapReference, // reference time for wrapping overflowed timestamp (pick a time close to the actual time. e.g., frag.start) + cc, // current cc + callBack, errorCallBack, stylesParsedCallback, logger) { + // Convert byteArray into string, replacing any somewhat exotic linefeeds with "\n", then split on that character. + const re = /\r\n|\n\r|\n|\r/g; + // Uint8Array.prototype.reduce is not implemented in IE11 + const vttLines = BufferUtils.utf8arrayToStr(new Uint8Array(vttByteArray)).trim().replace(re, '\n').split('\n'); + const initPTS90k = convertTimescale(initPTS, tsTimescale); // Convert initPTS to 90k timescale + // X-TIMESTAMP-MAP MPEGTS attribute integer value (in 90k timescale) + // assume MPEGTS = 0 if X-TIMESTAMP-MAP is absent + let mpegTs = 0; // basetime with timescale 90000 + // X-TIMESTAMP-MAP LOCAL attribute value parsed as number of seconds + let localTime = 0; + // Calculate wrapped ((MPEGTS - initPTS) / 90k) + // The number of seconds on the player timeline that the anchor point of the X-TIMESTAMP-MAP maps to + function calculateTimelineOffset() { + // wrapReference should be close to the final wrapped integer to prevent overflowed number after wrapped + // logger.debug(`mpegTs ${wrapPtsInteger(mpegTs)} - initPTS ${initPTS}, reference ${wrapReference} (${wrapReference * 90000})`); + return convertTimestampToSeconds({ + baseTime: wrapPtsInteger(wrapPtsInteger(mpegTs) - initPTS90k.baseTime, wrapReference * tsTimescale), + timescale: tsTimescale, + }); + } + const cues = []; + let parsingError = null; + let inHeader = true; + // let VTTCue = VTTCue || window.TextTrackCue; + // Create parser object using VTTCue with TextTrackCue fallback on certain browsers. + const parser = new lib.WebVTTParser(window, lib.WebVTTParser.StringDecoder(), stylesParsedCallback); + parser.oncue = function (cue) { + // cue offset = MPEGTS adjusted by InitPTS, assuming localtime is zero + const cueOffset = calculateTimelineOffset(); + // logger.log(`cueOffset ${cueOffset}`); + cue.startTime = wrapPtsInteger(cue.startTime + cueOffset - localTime, 0, 95443.7176888889); + cue.endTime = wrapPtsInteger(cue.endTime + cueOffset - localTime, 0, 95443.7176888889); + // Create a unique hash id for a cue based on start/end times and text. + // This helps timeline-controller to avoid showing repeated captions. + cue.id = hash(snapToHalfSecond(cue.startTime).toString()) + hash(snapToHalfSecond(cue.endTime - cue.startTime).toString()) + hash(cue.text); + // Fix encoding of special characters. TODO: Test with all sorts of weird characters. + cue.text = decodeURIComponent(encodeURIComponent(cue.text)); + if (cue.endTime > 0) { + cues.push(cue); + } + }; + parser.onparsingerror = function (e) { + parsingError = e; + }; + parser.onflush = function () { + if (parsingError && errorCallBack) { + errorCallBack(parsingError); + return; + } + callBack(cues); + }; + // Go through contents line by line. + vttLines.forEach((line) => { + if (inHeader) { + // Look for X-TIMESTAMP-MAP in header. + if (startsWith(line, 'X-TIMESTAMP-MAP=')) { + // Once found, no more are allowed anyway, so stop searching. + inHeader = false; + // Extract LOCAL and MPEGTS. + line + .substr(16) + .split(',') + .forEach((timestamp) => { + if (startsWith(timestamp, 'LOCAL:')) { + let localTimeMs; + try { + localTimeMs = cueString2millis(timestamp.substr(6)); + } + catch (e) { + localTimeMs = -1; + } + if (localTimeMs !== -1) { + localTime = localTimeMs / 1000; + } + else { + parsingError = new Error(`Malformed X-TIMESTAMP-MAP: ${line}`); + } + } + else if (startsWith(timestamp, 'MPEGTS:')) { + mpegTs = parseInt(timestamp.substr(7)); + } + }); + // Return without parsing X-TIMESTAMP-MAP line. + return; + } + } + // Parse line by default. + parser.parse(line + '\n'); + }); + parser.flush(); + }, + }; + var HLSVTTParser$1 = HLSVTTParser; + + + const Cues = { + newCue: function (track, startTime, endTime, screen, media) { + let cue; + let indenting; + let indent; + let text; + let line; + const context = { + foreground: false, + background: false, + italics: false, + underline: false, + flash: false, + styleStack: [], + }; + for (const [r, row] of screen.rows.entries()) { + indenting = true; + indent = 0; + text = ''; + if (!row.isEmpty()) { + for (let c = 0; c < row.chars.length; c++) { + if (row.chars[c].uchar.match(/\s/) && indenting) { + indent++; + } + else { + text += this.getFormattedChar(row.chars[c], context); + indenting = false; + } + } + // To be used for cleaning-up orphaned roll-up captions + row.cueStartTime = startTime; + // Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE + if (startTime === endTime) { + endTime += 0.0001; + } + text = text.trim().replace(//gi, '\n'); + text += this.closeStyles(context); + // console.log('VTTCue ' + startTime + ' - ' + endTime + ': ' + text); + cue = new lib.VTTCue(startTime, endTime, text); + if (indent >= 16) { + indent--; + } + else { + indent++; + } + // VTTCue.line get's flakey when using controls, so let's now include line 13&14 + // also, drop line 1 since it's to close to the top + if (navigator.userAgent.match(/Firefox\//)) { + line = r + 1; + } + else { + line = r > 7 ? r : r + 1; + } + cue.snapToLines = false; // Need this to be false for position % to work + cue.line = 10 + line * 5.33; // ( 10% top padding + line number * (80% vertical render length / 15 rows) ) + cue.align = 'left'; + cue.position = this.getPosition(indent, media); + track.addCue(cue); + } + } + }, + getPosition: function (indent, media) { + // Assume our aspect ratio is 4:3 + let aspectRatio = 1.3333333333333333; + if (media && media.offsetWidth && media.offsetHeight) { + const mediaRatio = media.offsetWidth / media.offsetHeight; + // There are some 16:10 screens out there, which is closer to 16:9 than a 4:3 aspect ratio + // So, anything with a ratio of 16:10 or wider will treat as a 16:9 screen for our calculations + if (mediaRatio >= 1.6) { + aspectRatio = 1.7777777777777777; + } + } + // CEA-608 has 32 columns, and defines 10% padding on each side + // ( 10% left padding + indent * (80% horizontal render length / 32 columns) ) + let position = 10 + (indent / 32) * 80; + let leftBound = 10; + let rightBound = 90; + if (aspectRatio === 1.7777777777777777) { + // CEA-608 captions assume a 4:3 screen size, but since we'll be playing on a 16:9 need to adjust some numbers + // 4:3 => 12:9, and a 12:9 screen's width is 3/4 the size of a 16:9 screen + // 3/4 * 16 = 4, and 4/16 = 25%, which means we need to pad by 12.5% on each side + // Also, when calculating the left offset it was originally for a 4:3 screen, so need to divide by 3/4 to adjust to 16:9 percentages + position = 12.5 + position * 0.75; + leftBound = 20; + rightBound = 80; + } + // Clamp the position based on the aspect ratio - if out of these bounds, Firefox throws an exception and captions break + return Math.max(leftBound, Math.min(rightBound, position + (navigator.userAgent.match(/Firefox\//) ? 50 : 0))); + }, + // return 'c' for all possible class tags (for color and bg_color); or the given style tag untouched (e.g., u, i, blink) + getRootStyleTag: function (style) { + const styleKey = style[0]; + return styleKey === 'c' ? styleKey : style; + }, + closeStyles: function (context) { + let postfix = ''; + for (let i = context.styleStack.length - 1; i >= 0; --i) { + const styleTag = this.getRootStyleTag(context.styleStack[i]); + postfix += ''; + context.styleStack.pop(); + } + return postfix; + }, + beginStyleAndBalance: function (context, newStyle) { + let balancedStyles = ''; + if (newStyle[0] === 'c') { + balancedStyles += this.closeStyleAndBalance(context, 'c'); + } + balancedStyles += '<' + newStyle + '>'; + context.styleStack.push(newStyle); + return balancedStyles; + }, + closeStyleAndBalance: function (context, oldStyle) { + const totalStyleCount = context.styleStack.length; + let childrenStyleCount = 0; + let closingChildrenStyles = ''; + let balancedStyles = ''; + for (let i = totalStyleCount - 1; i >= 0; --i) { + const styleUsed = context.styleStack[i]; + const styleTag = this.getRootStyleTag(styleUsed); + if (oldStyle[0] === styleUsed[0]) { + closingChildrenStyles += ''; + context.styleStack.splice(totalStyleCount - 1 - childrenStyleCount); + break; + } + else { + if (styleTag[0] === 'c') { + context.background = ''; + context.foreground = ''; + context.flash = false; + closingChildrenStyles += ''; + } + else if (styleTag[0] === 'u') { + context.underline = false; + closingChildrenStyles += ''; + } + else if (styleTag[0] === 'i') { + context.italics = false; + closingChildrenStyles += ''; + } + childrenStyleCount++; + } + } + balancedStyles = closingChildrenStyles; + return balancedStyles; + }, + getFormattedChar: function (c, context) { + let prefix = ''; + let formattedChar = c.uchar; + // color styles + let newColorStyle = ''; + const foregroundChanged = c.penState.foreground !== context.foreground; + const backgroundChanged = c.penState.background !== context.background; + const blinkChanged = c.penState.flash !== context.flash; + if (foregroundChanged || backgroundChanged || blinkChanged) { + // always include current colors in tag + newColorStyle = '.' + c.penState.foreground; + newColorStyle += '.bg_' + c.penState.background; + if (c.penState.flash && blinkChanged) { + newColorStyle += '.blink'; // but blink may be optional + } + if (c.penState.foreground || c.penState.background || c.penState.blink) { + prefix += this.beginStyleAndBalance(context, 'c' + newColorStyle); + } + else { + // changed to undefined foreground _and_ background color + prefix += this.closeStyleAndBalance(context, 'c'); + } + if (foregroundChanged) { + context.foreground = c.penState.foreground; + } + if (backgroundChanged) { + context.background = c.penState.background; + } + if (blinkChanged) { + context.flash = c.penState.flash; + } + } + // other styles + if (c.penState.underline !== context.underline) { + prefix += c.penState.underline ? this.beginStyleAndBalance(context, 'u') : this.closeStyleAndBalance(context, 'u'); + context.underline = c.penState.underline; + } + if (c.penState.italics !== context.italics) { + prefix += c.penState.italics ? this.beginStyleAndBalance(context, 'i') : this.closeStyleAndBalance(context, 'i'); + context.italics = c.penState.italics; + } + formattedChar = prefix + formattedChar; + return formattedChar; + }, + }; + var Cues$1 = Cues; + + /* + * LegibleSystemAdapter + * + * + */ + var ParsedFragQuality; + (function (ParsedFragQuality) { + ParsedFragQuality["CloseEnough"] = "CloseEnough"; + ParsedFragQuality["TooFar"] = "TooFar"; + ParsedFragQuality["Unknown"] = "Unknown"; + })(ParsedFragQuality || (ParsedFragQuality = {})); + const isChannelTextTrackKey = (value) => { + return value === 'cc1' || value === 'cc2'; + }; + function filterSelectableTextTracks(textTrackList) { + const tracks = []; + for (let ix = 0; ix < textTrackList.length; ix++) { + const textTrack = textTrackList[ix]; + if (textTrack.kind === 'captions' || textTrack.kind === 'subtitles' || (textTrack.kind === 'metadata' && textTrack.customTextTrackCueRenderer)) { + tracks.push(textTrack); + } + } + return tracks; + } + const cea608Duration = 0.03336666666666667; + function clearCurrentCues(track) { + if (track && track.cues) { + while (track.cues.length > 0) { + track.removeCue(track.cues[0]); + } + } + } + function intersection(x1, x2, y1, y2) { + return Math.min(x2, y2) - Math.max(x1, y1); + } + class LegibleSystemAdapter extends Observable { + constructor(mediaSink, config, hls, logger) { + super((subscriber) => { + const hlsTarget = fromEventTarget(this.hls, this); + subscriber.add(hlsTarget + .event(HlsEvent$1.INLINE_STYLES_PARSED, this.onInlineStylesParsed) + .pipe(finalize$1(() => this.destroy())) + .subscribe()); + subscriber.add(timer(0, this.config.trottleCheckInterval) + .pipe(switchMap(() => { + this.checkReadyToLoadNextSubtitleFragment(); + return VOID; + })) + .subscribe()); + if (this.config.nativeTextTrackChangeHandling) { + const useTextTrackPolling = !(this.mediaSink.textTracks && 'onchange' in this.mediaSink.textTracks); + if (useTextTrackPolling) { + subscriber.add(timer(0, 500) + .pipe(switchMap(() => { + this._onTextTracksChanged(); + return VOID; + })) + .subscribe()); + } + else { + const textTrackListTarget = fromEventTarget(this.mediaSink.textTracks, this); + subscriber.add(textTrackListTarget.event('change', this._onTextTracksChanged).subscribe()); + } + } + }); + this.config = config; + this.hls = hls; + this.logger = logger.child({ name: 'legible' }); + this.mediaSink = mediaSink; + // Add the id3 text track to the legibleSystemAdapter + this.id3Track = mediaSink.id3TextTrack; + this.enableCaption = true; + this.Cues = Cues$1; + this.tracks = []; + this.cueRanges = []; + this.channelToTrackMap = {}; + this.htmlTextTrackMap = new Map(); + this.lastCueEndTime = 0; + this.gotTracks = false; + this.tryAgain$ = new BehaviorSubject(true); + this.needNextSubtitle$ = new BehaviorSubject(true); + } + destroy() { + clearCurrentCues(this.textTrack1); + clearCurrentCues(this.textTrack2); + this.mediaSink = undefined; + this.nativeSubtitleTrackChange$ = undefined; + } + convertCuesIntoSubtitleFragInfo(cues) { + const fragCueRecord = {}; + if (cues != null && cues.length > 0) { + // When we parse WebVTT cues, we assign the cue's containing + // fragment's sequence number to cue.fragSN. + for (let i = 0; i < cues.length; i++) { + const cue = cues[i]; + if (!isFiniteNumber(cue.fragSN)) { + continue; + } + const record = fragCueRecord[cue.fragSN]; + if (!record) { + fragCueRecord[cue.fragSN] = { count: 1, startTime: cue.startTime, endTime: cue.endTime }; + } + else { + record.count++; + record.startTime = Math.min(cue.startTime, record.startTime); + record.endTime = Math.max(cue.endTime, record.endTime); + } + } + } + return fragCueRecord; + } + checkReadyToLoadNextSubtitleFragment() { + const currentTime = this.mediaSink.mediaQuery.currentTime; + const lastParsedCue = this.lastCueEndTime; + let need = false; + if (currentTime >= lastParsedCue - this.config.subtitleLeadTime) { + need = true; // less than (default) 30 seconds away from last parsed cue + } + this.logger.trace(`[subtitle] needtoLoadNextSubtitle: ${need}: ${currentTime} >= ${lastParsedCue} - ${this.config.subtitleLeadTime}`); + this.needNextSubtitle$.next(need); + } + checkReadyToLoadNextSubtitleFragment$(frag, generatedFrags) { + var _a; + if (frag.mediaSeqNum === ((_a = generatedFrags[0]) === null || _a === void 0 ? void 0 : _a.mediaSeqNum)) { + return of(true); // don't delay the first fragment because we may need to reload if it's too far + } + this.checkReadyToLoadNextSubtitleFragment(); + return this.needNextSubtitle$; + } + getNextFragment(details, mediaFragment) { + const nextSN = mediaFragment.mediaSeqNum + 1; + const nextFrag = nextSN < details.fragments.length ? details.fragments[nextSN - details.startSN] : null; + return nextFrag; + } + // aggressive loading: always load subtitles until there is none left, + // without checking for waterlevel in the current HTML5 texttrack + // + // In mediaSink.archiveParsedSubtitleFragmentRecord(): + // After the legibleSystemAdapter parses a subtitle fragment, + // it records the startTime (of the first cue in the fragment), endTime (of the last cue), + // and number of added cues in the fragment on its corresponding fragInfo object. + // In this way, hls.js knows the time boundary of each parsed subtitle fragment and how many cues it has generated. + // In legibleSystemAdapter.calculateFragInfoMap(): + // Iterate through the cues in the current HTML5 texttrack + // to tally the cues for each fragment. + // * If this "remaining cue" count does not match the "added cue" count in archiveParsedSubtitleFragmentRecord(), + // we will pretend that the fragment has not been loaded (probably error'ed out during cue creation) + // + // When starting up, seeking or track switching, hls.js needs to find the first subtitle fragment to load. + // It is the first unloaded fragment beyond the playback position. + // * Ignore subtitle fragments earlier than the current playback position + // * Find any continuous series of loaded subtitle fragments that overlaps the current playback position. + // Determine the media sequence number of the last fragment in this series. hls.js will load the next fragment. + // * If no fragment overlaps the playback position, find the 1st fragment with start time >= + // playback position. + // * To deal with subtitle playlist timestamp inaccuracies, we use the parsed timestamps calculated in + // mediaSink.archiveParsedSubtitleFragmentRecord() to anchor the timeline. That way, we can use each fragment's + // parsed duration to approximate the right fragment to load. + calculateFragInfoMap(position, cues, subtitleParsedRecord, details) { + const actualFragInfoMap = this.convertCuesIntoSubtitleFragInfo(cues); + let bufferInfo = { len: 0, start: position, end: position }; + let start = position; + let end = position; + let prevFragSN = null; + let nextFragSN = null; + // ES6 will sort numeric property keys in ascending order automatically + // https://exploringjs.com/es6/ch_oop-besides-classes.html#_traversal-order-of-properties + for (const propertyKey in actualFragInfoMap) { + if (!Object.prototype.hasOwnProperty.call(actualFragInfoMap, propertyKey)) { + continue; + } + const sn = Number(propertyKey); + if (!isFiniteNumber(sn)) { + this.logger.warn(`$fragInfoMap has invalid key ${sn}`); + continue; // should not happen + } + const fragInfo = actualFragInfoMap[sn]; + if (!this.isFragmentCompleteOrEmpty(sn, fragInfo.count, subtitleParsedRecord)) { + continue; + } + // current search position is earlier than the very 1st cue in the entire fragment details + // re-adjust search position to 1st cue's start time so that we can anchor the search from this 1st (buffered) frag + if (sn === details.startSN && position < fragInfo.startTime) { + start = end = bufferInfo.start = bufferInfo.end = position = fragInfo.startTime; + } + // found a fragment where start < position and is the leading frag in a frag sequence + if (position >= fragInfo.startTime && + (start === position || // 1st frag in the loadPos + (isFiniteNumber(prevFragSN) && sn - prevFragSN > 1))) { + // frag disjoint from the last one + start = end = fragInfo.startTime; + } + // look for the overlapping fragment + if (position >= fragInfo.startTime) { + // potentially inside a frag, assume its endTime as our next load position, and keep going ... + end = fragInfo.endTime; + prevFragSN = sn; + continue; + } + if (isFiniteNumber(prevFragSN) && sn - prevFragSN === 1) { + // Consecutive SN between 2 neighboring cues, keep scanning down the frag chain + end = fragInfo.endTime; + prevFragSN = sn; + continue; + } + // found the end of a continuous fragment series which overlaps the current position + nextFragSN = sn; + break; + } + bufferInfo = { len: end - start, start, end }; + const subtitleBufferInfo = { fragInfoMap: actualFragInfoMap, bufferInfo, prevFragSN, nextFragSN }; + return subtitleBufferInfo; + } + findFrags$(details, activeDiscoSeq) { + return this.tryAgain$.pipe(observeOn(asyncScheduler), switchMap(() => { + const findFragResult = this.findFragmentsForPosition(this.mediaSink.mediaQuery.currentTime, activeDiscoSeq, details); + const nextFrags = findFragResult.foundFrags; + if (!nextFrags) { + return EMPTY; // no more frag left to process + } + this.lastCueEndTime = 0; // reset needNextSubtitle$ check + this.needNextSubtitle$.next(true); + return of(findFragResult); + })); + } + // return true to continue loading the entire fragment batch + reviewParsedFrag(parsedFragResult, findFragsResult, details) { + var _a, _b, _c, _d; + const frag = parsedFragResult.frag; + const parsedCueRecord = parsedFragResult.cueRange; + const subtitleBufferInfo = findFragsResult.subtitleBufferInfo; + const subtitleParsedInfo = findFragsResult.subtitleParsedInfo; + const currentTime = this.mediaSink.mediaQuery.currentTime; + const generatedFrags = findFragsResult.foundFrags; + let success = true; + if (frag.mediaSeqNum === generatedFrags[0].mediaSeqNum) { + if (!findFragsResult.timelineEstablished) { + this.logger.info(`[subtitle] frag sn ${frag.mediaSeqNum} parsed. retry findFrag`); + return ParsedFragQuality.TooFar; + } + // only check the starting frag + if (!parsedCueRecord) { + // missing parsed data: should not happen. let currentTime move until the findFrag returns another frag + this.logger.warn(`[subtitle] 1st frag sn ${frag.mediaSeqNum} has no cue; details ${details.fragments.length} frags`); + return ParsedFragQuality.Unknown; + } + // Check whether we inferred the first frag close enough. + if (parsedCueRecord.startTime < currentTime) { + const actualDurationToPlayHead = currentTime - parsedCueRecord.startTime; + const fragments = details.fragments; + const startIdx = frag.mediaSeqNum - details.startSN; + let i = startIdx; + let fragTime = parsedCueRecord.startTime; + for (; i < fragments.length; ++i) { + fragTime += fragments[i].duration; + if (fragTime >= currentTime) { + break; + } + } + const fragCount = i - startIdx + 1; + success = fragCount <= this.config.earlyFragTolerance; // find another frag if we are too many fragments away + this.logger.info(`[subtitle] 1st frag sn ${frag.mediaSeqNum} is ${actualDurationToPlayHead}s earlier and ${fragCount} frags away from currentTime ${currentTime}; success ${success}`); + } + else if (parsedCueRecord.startTime > currentTime && frag.mediaSeqNum !== details.startSN) { + const actualDurationToPlayHead = parsedCueRecord.startTime - currentTime; + const latestBufferedFrag = subtitleBufferInfo.prevFragSN; + this.logger.info(`[subtitle] last buffered frag is complete: ${(_a = subtitleBufferInfo.fragInfoMap[latestBufferedFrag]) === null || _a === void 0 ? void 0 : _a.count} vs ${(_b = subtitleParsedInfo[latestBufferedFrag]) === null || _b === void 0 ? void 0 : _b.count}`); + if (frag.mediaSeqNum === latestBufferedFrag + 1 && ((_c = subtitleBufferInfo.fragInfoMap[latestBufferedFrag]) === null || _c === void 0 ? void 0 : _c.count) === ((_d = subtitleParsedInfo[latestBufferedFrag]) === null || _d === void 0 ? void 0 : _d.count)) { + // parsed frag is later than currentTime, but it is just the next consecutive frag from a latest *processed* frag. success. + success = true; + this.logger.info(`[subtitle] parsed frag sn ${frag.mediaSeqNum} is ${actualDurationToPlayHead}s later than currentTime ${currentTime} but continues from previous frag ${subtitleBufferInfo.prevFragSN}; success ${success}`); + } + else { + // parsed frag is later than currentTime. This should not happen because the cues are usually later than the frag start time, + // causing the findFrag function to (always) return an earlier frag than expected. + success = actualDurationToPlayHead <= this.config.lateTolerance; + this.logger.info(`[subtitle] 1st frag sn ${frag.mediaSeqNum} is ${actualDurationToPlayHead}s later than currentTime ${currentTime} and is ${frag.duration}s long; success ${success}`); + } + } + } + return success ? ParsedFragQuality.CloseEnough : ParsedFragQuality.TooFar; + } + isFragmentEmpty(subtitleParsedRecord) { + return subtitleParsedRecord && !isFiniteNumber(subtitleParsedRecord.startTime) && subtitleParsedRecord.count === 0; + } + isFragmentCompleteOrEmpty(mediaSeqNum, cueCount, subtitleParsedRecord) { + const parsedRecord = subtitleParsedRecord ? subtitleParsedRecord[mediaSeqNum] : null; + const result = (parsedRecord === null || parsedRecord === void 0 ? void 0 : parsedRecord.count) === cueCount || this.isFragmentEmpty(parsedRecord); + return result; + } + // use one *unbuffered* fragment early because we used cue startTime as fragment startTime to find media fragment + getEarlierFragmentInSameDisco(details, frag, subtitleParsedRecord) { + const fragIdx = frag.mediaSeqNum - details.startSN - 1; + if (fragIdx < 0 || fragIdx > details.fragments.length - 1) { + this.logger.error(`[subtitle] getEarlierFragmentInSameDisco index ${fragIdx} out of range`); + return frag; // return original frag + } + const prevFrag = details.fragments[fragIdx]; + return prevFrag && prevFrag.discoSeqNum === frag.discoSeqNum && !subtitleParsedRecord[frag.mediaSeqNum] ? prevFrag : frag; + } + // subtitleParsedInfo: Records containing cue count, start and end time of parsed fragments. + // subtitleParsedInfo is calculated when we parse a frag to add its WebVTT cues + // (archiveParsedSubtitleFragmentRecord) + // subtitleBufferInfo: Records containing cue count, start and end time of buffered fragments. + // subtitleBufferInfo is calculated when we enumarate added (remaining) cues in text tracks + // (calculateFragInfoMap) + // 2 main differences: + // (1) subtitleParsedInfo is stored in akita store for the entire session. Each subtitle persistent id + // has one record (That means primary and backup subtitle track may share the same record). + // subtitleBufferInfo is calculated on-demand from the cues in the current enabled text track only. + // (2) The calculated frag (cue) start and end time in subtitleParsedInfo should be more accurate than + // the start and end time in subtitleBufferInfo. This is in case MSE or the app removes used cues from the text tracks. + inferSubtitleFragmentForPosition(position, activeDiscoSeqNum, subtitleParsedInfo, subtitleBufferInfo, details) { + let foundFrag; + let startFragIdx; + let startParsedRecord; + let endFragIdx; + let endParsedRecord; + if (isFiniteNumber(subtitleBufferInfo.prevFragSN)) { + startFragIdx = subtitleBufferInfo.prevFragSN - details.startSN; + startParsedRecord = subtitleParsedInfo[subtitleBufferInfo.prevFragSN]; + } + if (isFiniteNumber(subtitleBufferInfo.nextFragSN)) { + endFragIdx = subtitleBufferInfo.nextFragSN - details.startSN; + endParsedRecord = subtitleParsedInfo[subtitleBufferInfo.nextFragSN]; + } + if (isFiniteNumber(startFragIdx) && startFragIdx >= 0 && startFragIdx < details.fragments.length && startParsedRecord) { + let startPos = startParsedRecord.startTime; + const lastIdx = isFiniteNumber(endFragIdx) ? endFragIdx : details.fragments.length; + for (let i = startFragIdx; i < lastIdx; ++i) { + const mediaFragment = details.fragments[i]; + if (isFiniteNumber(activeDiscoSeqNum) && mediaFragment.discoSeqNum !== activeDiscoSeqNum) { + continue; + } + if (i === lastIdx - 1) { + // get the fragment before nextFragSN (in case we guessed too late during the last inference) + this.logger.info(`[subtitle] from frag sn ${subtitleBufferInfo.prevFragSN}, infer frag sn ${mediaFragment.mediaSeqNum} using lastIdx ${lastIdx}. startPos ${startPos} duration ${mediaFragment.duration} position ${position}`); + foundFrag = { foundFrag: mediaFragment, timelineEstablished: true }; + break; + } + else if (startPos + mediaFragment.duration > position && i > startFragIdx) { + // we have already loaded frag prevFragSN, so we are really only interested in frag (prevFragSN + 1) and beyond + this.logger.info(`[subtitle] from frag sn ${subtitleBufferInfo.prevFragSN}, infer frag sn ${mediaFragment.mediaSeqNum}: startPos ${startPos} + duration ${mediaFragment.duration} > ${position}`); + foundFrag = { foundFrag: mediaFragment, timelineEstablished: true }; + break; + } + startPos += mediaFragment.duration; + } + } + else if (isFiniteNumber(endFragIdx) && endFragIdx >= 0 && endFragIdx < details.fragments.length && endParsedRecord) { + let startPos = endParsedRecord.startTime; // endParsedRecord.startTime is fragments[endFragIdx].start + for (let i = endFragIdx - 1; i >= 0; --i) { + // keep probing the earlier fragment as long as the later/previous fragment's startPos > position + const mediaFragment = details.fragments[i]; + if (isFiniteNumber(activeDiscoSeqNum) && mediaFragment.discoSeqNum !== activeDiscoSeqNum) { + continue; + } + if (startPos <= position) { + // we want one fragment earlier than position, which is mediaFragment (because startPos belongs to the later/previous fragment) + this.logger.info(`[subtitle] from frag sn ${subtitleBufferInfo.nextFragSN}, infer frag sn ${mediaFragment.mediaSeqNum}: startPos ${startPos} <= ${position}`); + foundFrag = { foundFrag: mediaFragment, timelineEstablished: true }; + break; + } + startPos -= mediaFragment.duration; + } + } + else { + for (let i = 0; i < details.fragments.length; ++i) { + const mediaFragment = details.fragments[i]; + if (isFiniteNumber(activeDiscoSeqNum) && mediaFragment.discoSeqNum === activeDiscoSeqNum) { + this.logger.info(`[subtitle] infer frag sn ${mediaFragment.mediaSeqNum} in cc ${activeDiscoSeqNum}`); + foundFrag = { foundFrag: mediaFragment, timelineEstablished: false }; + break; + } + } + } + return foundFrag; + } + generateFragmentBatch(batchSize, activeDiscoSeqNum, fragResult, subtitleParsedInfo, subtitleBufferInfo, details) { + var _a; + const generatedFrags = []; + const anchorFrag = fragResult === null || fragResult === void 0 ? void 0 : fragResult.foundFrag; + if (!anchorFrag) { + return { foundFrags: undefined, subtitleParsedInfo: undefined, subtitleBufferInfo: undefined, timelineEstablished: fragResult === null || fragResult === void 0 ? void 0 : fragResult.timelineEstablished }; + } + const startIdx = anchorFrag ? anchorFrag.mediaSeqNum - details.startSN : details.fragments.length; + for (let i = startIdx; i < details.fragments.length && generatedFrags.length < batchSize; ++i) { + const frag = details.fragments[i]; + if (frag.discoSeqNum !== activeDiscoSeqNum) { + continue; + } + const bufferedCueCount = (_a = subtitleBufferInfo.fragInfoMap[frag.mediaSeqNum]) === null || _a === void 0 ? void 0 : _a.count; + if (!this.isFragmentCompleteOrEmpty(frag.mediaSeqNum, bufferedCueCount !== null && bufferedCueCount !== void 0 ? bufferedCueCount : 0, subtitleParsedInfo)) { + generatedFrags.push(frag); // include frag if not fully parsed + } + } + return { foundFrags: generatedFrags, subtitleParsedInfo, subtitleBufferInfo, timelineEstablished: fragResult === null || fragResult === void 0 ? void 0 : fragResult.timelineEstablished }; + } + findFragmentsForPosition(currentTime, activeDiscoSeqNum, details) { + // get past addCue records + const subtitleParsedRecord = this.mediaSink.mediaQuery.getParsedSubtitleRecordsForMediaOption(this.selectedTrack.persistentID); + // compose addCue records from remaining cues in current texttrack + const cues = this.getCuesOfEnabledTrack(this.selectedMediaOption.mediaOptionId, false /* this.enabledTrack.sideTrackId !== undefined */); + const subtitleBufferInfo = this.calculateFragInfoMap(currentTime, cues, subtitleParsedRecord, details); + const bufferInfo = subtitleBufferInfo.bufferInfo; + const loadPos = Math.max(currentTime, bufferInfo.end); + const fragResult = this.inferSubtitleFragmentForPosition(loadPos, activeDiscoSeqNum, subtitleParsedRecord, subtitleBufferInfo, details); + return this.generateFragmentBatch(Infinity, activeDiscoSeqNum, fragResult, subtitleParsedRecord, subtitleBufferInfo, details); + } + get selectedMediaOption() { + return this.selectedTrack ? this.selectedTrack : this._disabledMediaOption; + } + set selectedMediaOption(mediaOption) { + this.selectedTrack = isAlternateMediaOption(mediaOption) ? mediaOption : undefined; + } + get selectedTrack() { + return this._selectedMediaOption; + } + set selectedTrack(track) { + if (track === this._selectedMediaOption) { + return; + } + this.logger.info(`[subtitle] pick track #${track === null || track === void 0 ? void 0 : track.id} mediaOptionId ${track === null || track === void 0 ? void 0 : track.mediaOptionId} persistentId ${track === null || track === void 0 ? void 0 : track.persistentID}`); + this._selectedMediaOption = track; + this.updateTextTrackState(); + } + getTrack(mediaOptionId) { + return this._availableMediaOptions.find((mediaOption) => mediaOption.mediaOptionId === mediaOptionId); + } + updateTextTrackState() { + if (!this.mediaSink.textTracks) { + // catch an update later + return; + } + const selectedHTMLTextTrack = this.selectedTrack ? this.getExistingHTMLTextTrack(this.selectedTrack) : undefined; + const textTracks = filterSelectableTextTracks(this.mediaSink.textTracks); + for (let id = 0; id < textTracks.length; id++) { + const aTrack = textTracks[id]; + if (aTrack === selectedHTMLTextTrack && textTracks[id].mode !== 'showing') { + textTracks[id].mode = 'showing'; + this.logger.info(`textTracks[${id}].mode = 'showing'`); + } + else if (aTrack !== selectedHTMLTextTrack && textTracks[id].mode !== 'hidden') { + textTracks[id].mode = 'hidden'; + this.logger.info(`textTracks[${id}].mode = 'hidden'`); + } + } + } + mapHTMLTextTrackIndexToMediaOptionId(searchIndex) { + const searchTextTrack = this.mediaSink.textTracks[searchIndex]; + let foundOptionId; + this.htmlTextTrackMap.forEach((textTrack, optionId) => { + if (searchTextTrack === textTrack) { + foundOptionId = optionId; + } + }); + return foundOptionId; + } + get mediaSelectionOptions() { + return this._availableMediaOptions; + } + _makeDisableOption(mediaOption) { + return { itemId: mediaOption.itemId, mediaOptionType: mediaOption.mediaOptionType, mediaOptionId: 'Nah' }; + } + _onTextTracksChanged() { + // Media is undefined when switching streams via loadSource() + if (!this.mediaSink) { + return; + } + let newTrackEvent = false; + let showingPersistentId; + const textTracks = filterSelectableTextTracks(this.mediaSink.textTracks); + let newTracksSeenAndIgnored = 0; + for (let id = 0; id < textTracks.length; id++) { + if (textTracks[id].seen) { + // Existing text tracks + if (textTracks[id].mode === 'showing') { + showingPersistentId = textTracks[id].persistentId; + } + } + else { + // Newly added text track always has 'hidden' mode by default. + // We must not make any selection change based on "new track" event. + // A real track change event may be in-flight and will be overriden later if we re-select an active subtitle track or 'undefined' now. + textTracks[id].seen = true; + newTracksSeenAndIgnored += 1; + newTrackEvent = true; + } + } + this.logger.info(`New tracks marked seen and ignored: ${newTracksSeenAndIgnored} vs total: ${textTracks.length}`); + if (!newTrackEvent) { + const currentSelectedMediaOption = this.selectedTrack; + if ((currentSelectedMediaOption === null || currentSelectedMediaOption === void 0 ? void 0 : currentSelectedMediaOption.persistentID) !== showingPersistentId) { + const newOption = this.mediaSelectionOptions.find(function (mediaOption) { + return mediaOption.persistentID === showingPersistentId; + }); + this.nativeSubtitleTrackChange$.next(newOption ? newOption : this._disabledMediaOption); + } + } + } + addCues(channel, startTime, endTime, screen) { + // skip cues which overlap more than 50% with previously parsed time ranges + const ranges = this.cueRanges; + let merged = false; + for (let i = ranges.length; i--;) { + const cueRange = ranges[i]; + const overlap = intersection(cueRange[0], cueRange[1], startTime, endTime); + if (overlap >= 0) { + cueRange[0] = Math.min(cueRange[0], startTime); + cueRange[1] = Math.max(cueRange[1], endTime); + merged = true; + if (overlap / (endTime - startTime) > 0.5) { + return; + } + } + } + if (!merged) { + ranges.push([startTime, endTime]); + } + this.Cues.newCue(this.channelToTrackMap[channel], startTime, endTime, screen, this.mediaSink); + } + getExistingHTMLTextTrackWithChannelNumber(channelNumber) { + const mediaSink = this.mediaSink; + if (mediaSink) { + for (let i = 0; i < mediaSink.textTracks.length; i++) { + const textTrack = mediaSink.textTracks[i]; + const propName = 'cc' + channelNumber; + if (isChannelTextTrackKey(propName) && textTrack[propName] === true) { + return textTrack; + } + } + } + return null; + } + sendAddTrackEvent(track, mediaSink) { + let e = null; + try { + e = new window.Event('addtrack'); + } + catch (err) { + // for IE11 + e = document.createEvent('Event'); + e.initEvent('addtrack', false, false); + } + e.track = track; + mediaSink.dispatchEvent(e); + } + createHTMLCaptionsTrackGuts(channelNumber, name, languageCode, forced) { + const trackVar = 'cc' + channelNumber; + if (!this.channelToTrackMap[trackVar]) { + // Enable reuse of existing closed caption text track. + const existingHTMLTrack = this.getExistingHTMLTextTrackWithChannelNumber(channelNumber); + if (!existingHTMLTrack) { + const textTrack = this.createHTMLTextTrackGuts('captions', name, languageCode, forced); + if (textTrack && isChannelTextTrackKey(trackVar)) { + textTrack[trackVar] = true; + this.channelToTrackMap[trackVar] = textTrack; + } + } + else { + this.channelToTrackMap[trackVar] = existingHTMLTrack; + clearCurrentCues(this.channelToTrackMap[trackVar]); + this.sendAddTrackEvent(this.channelToTrackMap[trackVar], this.mediaSink); + } + } + return this.channelToTrackMap[trackVar]; + } + createHTMLCaptionsTrack(channel) { + return this.createHTMLCaptionsTrackGuts(channel, this.config[channel === 1 ? 'captionsTextTrack1Label' : 'captionsTextTrack2Label'], this.config.captionsTextTrack1LanguageCode, false); + } + getExistingHTMLTextTrack(track) { + // this.logger.info(`getExistingHTMLTextTrack condenseSubtitleTrack ${this.config.condenseSubtitleTrack} track.persistentID ${track.persistentID} track.id ${track.id}`); + return this.config.condenseSubtitleTrack ? this.htmlTextTrackMap.get(track.persistentID) : this.htmlTextTrackMap.get(track.id); + } + getExistingHTMLTextTrackWithSubtitleTrackId(id) { + const altOption = this._availableMediaOptions.find((mediaOption) => mediaOption.id === id); + const htmlTextTrack = altOption ? this.getExistingHTMLTextTrack(altOption) : undefined; + this.logger.debug(`[subtitle] map track id ${id} to ${htmlTextTrack === null || htmlTextTrack === void 0 ? void 0 : htmlTextTrack.label} ${htmlTextTrack === null || htmlTextTrack === void 0 ? void 0 : htmlTextTrack.kind} ${htmlTextTrack === null || htmlTextTrack === void 0 ? void 0 : htmlTextTrack.language}`); + return htmlTextTrack; + } + getExistingHTMLTextTrackIndex(track) { + const htmlTextTrack = this.getExistingHTMLTextTrack(track); + const tracks = this.mediaSink.textTracks; + let found = -1; + for (let i = 0; i < tracks.length; ++i) { + if (tracks[i] === htmlTextTrack) { + found = i; + break; + } + } + return found; + } + setExistingHTMLTextTrack(track, textTrack) { + textTrack.persistentId = track.persistentID; + if (this.config.condenseSubtitleTrack) { + // this.logger.info(`setExistingHTMLTextTrack: track.persistentID ${track.persistentID}, textTrack ${textTrack?.id} ${textTrack}`); + return this.htmlTextTrackMap.set(track.persistentID, textTrack); + } + else { + // this.logger.info(`setExistingHTMLTextTrack: track.id ${track.id}, textTrack ${textTrack?.id} ${textTrack}`); + return this.htmlTextTrackMap.set(track.id, textTrack); + } + } + createHTMLTextTrack(track) { + let HTMLTextTrack = this.getExistingHTMLTextTrack(track); + if (!HTMLTextTrack) { + if (track.mediaType === 'sbtl') { + // this.logger.info(`create subtitle track ${track.name} from manifest`); + this.subtitleTracksCreated += 1; + HTMLTextTrack = this.createHTMLTextTrackGuts('subtitles', track.name, track.lang, track.forced); + } + else { + let channelNumber = 1; + if (track.inStreamID) { + channelNumber = Number(track.inStreamID.substring(2)); + } + // this.logger.info(`create caption track ${track.name} from manifest`); + this.captionTracksCreated += 1; + HTMLTextTrack = this.createHTMLCaptionsTrackGuts(channelNumber, track.name, track.lang, false); + } + if (HTMLTextTrack) { + this.setExistingHTMLTextTrack(track, HTMLTextTrack); + /* const data = { + trackId: track.id, + mediaOptionId: track.mediaOptionId, + persistentId: track.persistentID, + kind: HTMLTextTrack.kind, + label: HTMLTextTrack.label, + language: HTMLTextTrack.language, + }; + this.logger.info(`textTrackCreated: ${JSON.stringify(data)}`); + */ + } + else { + this.logger.error(`failed to create HTML text track for track ${track.id}: persistent id ${track.persistentID} name ${track.name} lang ${track.lang} inStreamID ${track.inStreamID}`); + this.tracksFailed += 1; + } + } + else { + // this.logger.info(`reuse HTML text track for track ${track.id}: persistent id ${HTMLTextTrack.persistentId} kind ${HTMLTextTrack.kind} label ${HTMLTextTrack.label} lang ${HTMLTextTrack.language}`); + this.tracksReused += 1; + } + return HTMLTextTrack; + } + createHTMLTextTrackGuts(kind, label, lang, forced) { + const mediaSink = this.mediaSink; + if (mediaSink) { + let customTextTrackCueRenderer = false; + if (kind !== 'metadata' && this.config.customTextTrackCueRenderer) { + customTextTrackCueRenderer = true; + kind = 'metadata'; + } + const textTrack = mediaSink.addTextTrack(kind, label, lang); + if (customTextTrackCueRenderer) { + textTrack.customTextTrackCueRenderer = true; + } + return textTrack; + } + return undefined; + } + resetLoadSource() { + this.resetTracks(); + } + resetTracks() { + this.logger.info('clean out all cues and cueRanges'); + this._cleanTracks(); + this.cueRanges = []; + } + _cleanTracks() { + // clear outdated subtitles + const mediaSink = this.mediaSink; + if (mediaSink) { + const textTracks = mediaSink.textTracks; + if (textTracks) { + for (let i = 0; i < textTracks.length; i++) { + clearCurrentCues(textTracks[i]); + } + } + } + } + getCuesOfEnabledTrack(trackId, checkForWebVTTCue = false) { + let cues = []; + if (!checkForWebVTTCue) { + this.logger.info(`[subtitle] return regular cue list for track ${trackId}`); + cues = this._getCuesOfEnabledTrack(trackId); + } + else { + const sideTrackCues = this._getCuesOfEnabledTrack(trackId); + for (let i = 0; i < sideTrackCues.length; i++) { + const targetCue = sideTrackCues[i]; + if (isExtendedTextTrackCue(targetCue)) { + // this.logger.info(`[subtitle] consider WebVTT cue: ${targetCue.startTime} ${targetCue.endTime} '${targetCue.text}'`); + cues.push(targetCue); + } + } + } + return cues; + } + _getCuesOfEnabledTrack(trackId) { + const track = this.getTrack(trackId); + const htmlTrackKey = this.config.condenseSubtitleTrack ? track === null || track === void 0 ? void 0 : track.persistentID : track === null || track === void 0 ? void 0 : track.id; + const enabledHTMLTrack = this.htmlTextTrackMap.get(htmlTrackKey); + if (enabledHTMLTrack && enabledHTMLTrack.cues) { + return Array.from(enabledHTMLTrack.cues); + } + else { + return []; + } + } + attachSubtitleTracks() { + // We need both onSubtitleTrackUpdated and onMediaAttaching to resolve before attaching subtitle tracks + if (!this.gotTracks) { + return; + } + this.subtitleTracksCreated = 0; + this.captionTracksCreated = 0; + this.tracksReused = 0; + this.tracksFailed = 0; + this.tracks.forEach((track) => { + this.createHTMLTextTrack(track); + }); + const data = { + totalTracks: this.tracks.length, + subtitleTracksCreated: this.subtitleTracksCreated, + captionTracksCreated: this.captionTracksCreated, + tracksReused: this.tracksReused, + tracksFailed: this.tracksFailed, + }; + this.logger.qe({ critical: true, name: 'textTrackCreated', data }); + } + setTracks(mediaOptions, enabledMediaOption, disabledMediaOption) { + this._cleanTracks(); + this.htmlTextTrackMap = new Map(); + this.cueRanges = []; + if (this.config.enableWebVTT) { + this.tracks = mediaOptions || []; + } + this.gotTracks = true; + this._availableMediaOptions = mediaOptions; + this._disabledMediaOption = disabledMediaOption; + this.attachSubtitleTracks(); + this.selectedTrack = enabledMediaOption; + this.nativeSubtitleTrackChange$ = new Subject(); + this.mediaSink.textTracksCreated = true; + } + // Need this to suppress errors when no client is listening for these styles + onInlineStylesParsed(data) { } + processSubtitleFrag(enabledMediaOption, frag, initPTS, payload) { + const data = new Uint8Array(payload); + const trackIndex = this.getExistingHTMLTextTrackIndex(enabledMediaOption); + // Convert byteArray into string, replacing any somewhat exotic linefeeds with "\n", then split on that character. + /* + let re = /\r\n|\n\r|\n|\r/g; + let vtt = BufferUtils.utf8arrayToStr(data).trim().replace(re, '\n').split('\n'); + vtt.forEach((line) => { + this.logger.info(`vtt.subtitle: ${line}`); + }); + */ + if (frag && payload.byteLength) { + const fragCueRange = this._parseVTTs(trackIndex, frag, initPTS, data); + if (fragCueRange && isFiniteNumber(fragCueRange.startTime)) { + // startTime is valid only if there are cues (duplicated or otherwise) in the fragment + this.lastCueEndTime = Math.max(this.lastCueEndTime, fragCueRange.endTime); + this.logger.debug(`[subtitle] update lastCueEndTime ${this.lastCueEndTime}`); + } + return fragCueRange; + } + return; + } + _parseVTTs(textTrackIdx, frag, initPTS, payload) { + // Parse the WebVTT file contents. + let parsedfragCueRecord; + HLSVTTParser$1.parse(payload, initPTS, frag.start, frag.discoSeqNum, (cues) => { + // Sometimes there are cue overlaps on segmented vtts so the same + // cue can appear more than once in different vtt files. + // This avoid showing duplicated cues with same timecode and text. + const textTrack = this.mediaSink.textTracks[textTrackIdx]; + // Add cues and trigger event with success true. + const fragCueRecord = { count: 0, startTime: Number.POSITIVE_INFINITY, endTime: 0 }; + cues.map((cue) => { + if (textTrack && (!textTrack.cues || !textTrack.cues.getCueById(cue.id))) { + this.logger.trace(`new cue for subtitle track[${frag.mediaOptionId}] frag sn ${frag.mediaSeqNum}: #${cue.id} [${cue.startTime}, ${cue.endTime}]: ${cue.text}`); + cue.fragSN = frag.mediaSeqNum; + cue.webVTTCue = true; + textTrack.addCue(cue); + fragCueRecord.count++; + this.logger.debug(`[subtitle] cue added. fragCueRecord ${JSON.stringify(fragCueRecord)}`); + } + fragCueRecord.startTime = Math.min(cue.startTime, fragCueRecord.startTime); + fragCueRecord.endTime = Math.max(cue.endTime, fragCueRecord.endTime); + this.logger.debug(`[subtitle] frag processed. fragCueRecord ${JSON.stringify(fragCueRecord)}`); + }); + this.logger.debug(`[subtitle] frag processing done. fragCueRecord ${JSON.stringify(fragCueRecord)}`); + // fragCueRecord: + // * startTime is a finite number only if there is at least 1 cue in the fragment + // * count may be zero if duplicated cue (finite startTime) or no cue (PostiveInfinity startTime) + parsedfragCueRecord = fragCueRecord; + this.mediaSink.archiveParsedSubtitleFragmentRecord(this.selectedTrack.persistentID, frag.mediaSeqNum, fragCueRecord); + }, (e) => { + // Something went wrong while parsing. Trigger event with success false. + this.logger.info(`Failed to parse VTT cue: ${e}`); + }, (styles) => { + this.hls.trigger(HlsEvent$1.INLINE_STYLES_PARSED, { styles: styles }); + }, this.logger); + return parsedfragCueRecord; + } + _ensureParser() { + if (!this.cea608Parser) { + const channel1 = new OutputFilter(this, 1); + const channel2 = new OutputFilter(this, 2); + this.cea608Parser = new Cea608Parser$1(0, channel1, channel2); + } + } + setupForFrag(frag) { + if (!frag || frag.mediaOptionType !== MediaOptionType.Variant || frag.iframe) { + return; + } + const sn = frag.mediaSeqNum; + // if this frag isn't contiguous, clear the parser so cues with bad start/end times aren't added to the textTrack + if (sn !== this.lastVariantSeqNum + 1) { + this.resetClosedCaptionParser(); + } + this.lastVariantSeqNum = sn; + } + resetClosedCaptionParser() { + var _a; + this.logger.info('reset cea608Parser'); + (_a = this.cea608Parser) === null || _a === void 0 ? void 0 : _a.reset(); + } + /** + * Add CLCP / ID3 samples + * @param offsetTimestamp offsetTimestamp is offset used for SourceBuffer timestampOffset calculation. All samples should be shifted by this value + * @param captionData Parsed caption data from demuxer + * @param id3Samples Parsed id3 samples from demuxer + * @param endPTS end Presentation Timestamp of the fragment + */ + addLegibleSamples(offsetTimestamp, captionData, id3Samples, endPTS) { + if (captionData) { + this.addClosedCaptionSamples(offsetTimestamp, captionData); + } + if (id3Samples && id3Samples.length > 0) { + this.addId3Samples(offsetTimestamp, id3Samples, convertTimestampToSeconds(endPTS)); + } + } + addClosedCaptionSamples(offsetTimestamp, captionData) { + if (captionData.mp4) { + this.addMP4CaptionSamples(offsetTimestamp, captionData.mp4); + } + else if (captionData.ts) { + this.addTSCaptionSamples(offsetTimestamp, captionData.ts); + } + } + addMP4CaptionSamples(offsetTimestamp, samples) { + // push all of the CEA-608 messages into the interpreter + // immediately. It will create the proper timestamps based on our PTS value + if (this.enableCaption && this.config.enableCEA708Captions) { + const offsetTimestampSec = convertTimestampToSeconds(offsetTimestamp); + this._ensureParser(); + for (let i = 0; i < samples.length; i++) { + let startSec = samples[i].pts - offsetTimestampSec; + const ccData = samples[i].bytes; + for (let j = 0; j < ccData.length; j += 2) { + const doubleByte = []; + doubleByte.push(ccData[j]); + if (j + 1 < ccData.length) { + doubleByte.push(ccData[j + 1]); + } + else { + this.logger.info(`CEA608 sample ${i} not even length (index ${j + 1} >= length ${ccData.length})`); + doubleByte.push(80); // safest character to push is 80 (noop) + } + this.cea608Parser.addData(startSec, doubleByte); + startSec += cea608Duration; + } + } + } + } + addTSCaptionSamples(offsetTimestamp, samples) { + if (this.enableCaption && this.config.enableCEA708Captions) { + const offsetTimestampSec = convertTimestampToSeconds(offsetTimestamp); + this._ensureParser(); + for (let i = 0; i < samples.length; i++) { + const startSec = samples[i].pts - offsetTimestampSec; + const ccdatas = LegibleSystemAdapter.extractCea608Data(samples[i].bytes); + this.cea608Parser.addData(startSec, ccdatas); + } + } + } + addId3Samples(offsetTimestamp, samples, endPTSSec) { + if (!this.config.enableID3Cues) { + this.logger.info('id3 cues disabled by config'); + return; + } + // Attempt to recreate Safari functionality by creating + // WebKitDataCue objects when available and store the decoded + // ID3 data in the value property of the cue + const Cue = window.WebKitDataCue || window.VTTCue || window.TextTrackCue; + const offsetTimestampSec = convertTimestampToSeconds(offsetTimestamp); + for (let i = 0; i < samples.length; i++) { + const startSec = samples[i].pts - offsetTimestampSec; + let endSec = (i < samples.length - 1 ? samples[i + 1].pts : endPTSSec) - offsetTimestampSec; + // Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE + if (startSec === endSec) { + endSec += 0.0001; + } + if (samples[i].frames) { + samples[i].frames.forEach((frame) => { + if (frame && !this.id3shouldIgnore(frame)) { + const cue = new Cue(startSec, endSec, ''); + cue.value = frame; + this.logger.trace(`[id3] addCue [${startSec},${endSec}] ${JSON.stringify(cue.value).substr(0, 20)}...`); + this.id3Track.addCue(cue); + } + }); + } + } + } + id3shouldIgnore(frame) { + return frame.key === 'PRIV' && (frame.info === 'com.apple.streaming.transportStreamTimestamp' || frame.info === 'com.apple.streaming.audioDescription'); + } + static extractCea608Data(byteArray) { + const count = byteArray[0] & 31; + let position = 2; + let tmpByte, ccbyte1, ccbyte2, ccValid, ccType; + const actualCCBytes = []; + for (let j = 0; j < count; j++) { + tmpByte = byteArray[position++]; + ccbyte1 = 127 & byteArray[position++]; + ccbyte2 = 127 & byteArray[position++]; + ccValid = (4 & tmpByte) !== 0; + ccType = 3 & tmpByte; + if (ccbyte1 === 0 && ccbyte2 === 0) { + continue; + } + if (ccValid) { + if (ccType === 0) { + // || ccType === 1 + actualCCBytes.push(ccbyte1); + actualCCBytes.push(ccbyte2); + } + } + } + return actualCCBytes; + } + } + + const makeLegibleService = (source$, config, eventEmitter, logger) => { + return source$.pipe(tag('playback.legibleServiceEpic.in'), switchMap((mediaSink) => { + if (!mediaSink) { + return of(null); + } + const textTrackAdapter = new LegibleSystemAdapter(mediaSink, config, eventEmitter, logger); + return merge(of(textTrackAdapter), textTrackAdapter); + }), tag('playback.legibleServiceEpic.emit')); + }; + + const SessionDataStatusCode = { + // Typucally (positive) HTTP Status code for network errors. + // Plus these ... + JSON_PARSE_ERROR: -1, + }; + + const loggerName$6 = { name: 'plist' }; + function stringIsXMLPlist(data) { + const XML_TAG_REGEX = /[\s]*<\?xml/i; + const PLIST_TAG_REGEX = /[\s]* ${cTagName}`); + } + } + else { + value = convertPlistToJSON(child); + jsonObj[key] = value; + key = null; + value = null; + validIndex++; + } + } + } + if (key) { + // Last value may be missing. Treat it as NULL. + jsonObj[key] = value; + logger.info(loggerName$6, `Orphaned pair: ${key} = ${JSON.stringify(value)}`); + key = null; + value = null; + } + } + else if (tagName === 'key') { + // Leaf dict elements are wrapped in the sole child of TextNode. Reach down 1 level to get the value. + const firstChild = childNodes[0]; + if (firstChild) { + jsonObj = firstChild.nodeValue; + } + else { + // Should not happen + logger.warn(loggerName$6, 'Invalid dict key: Key is null, probably like this: '); + jsonObj = null; + } + } + else if (tagName === 'string') { + const firstChild = childNodes[0]; + if (firstChild) { + jsonObj = firstChild.nodeValue; + } + else { + jsonObj = null; + } + } + else if (tagName === 'integer') { + const firstChild = childNodes[0]; + if (firstChild) { + jsonObj = parseInt(firstChild.nodeValue); + } + else { + jsonObj = 0; + } + } + else if (tagName === 'float') { + const firstChild = childNodes[0]; + if (firstChild) { + jsonObj = parseFloat(firstChild.nodeValue); + } + else { + jsonObj = 0; + } + } + else if (tagName === 'date') { + const firstChild = childNodes[0]; + if (firstChild) { + jsonObj = new Date(firstChild.nodeValue); + } + else { + jsonObj = null; + } + } + else if (tagName === 'data') { + const firstChild = childNodes[0]; + if (firstChild) { + // base64 encoded + jsonObj = atob(firstChild.nodeValue); + } + else { + jsonObj = null; + } + } + else if (tagName === 'true') { + jsonObj = true; + } + else if (tagName === 'false') { + jsonObj = false; + } + } + else { + // Node without tagName may exist in the XML DOM tree. + // See if its child/children are valid plist elements. + if (childNodes.length < 1) { + // This should never happen + logger.warn(loggerName$6, `unknown node with unknown value > nodeType=${domNode.nodeType} tagName=${domNode.tagName} nodeName=${domNode.nodeName} value=${domNode.nodeValue}`); + } + else { + jsonObj = []; + for (let i = 0; i < childNodes.length; ++i) { + const child = childNodes[i]; + if (child.tagName) { + jsonObj.push(convertPlistToJSON(child)); + } + } + if (jsonObj.length === 1) { + // If unknown node has only 1 child, replace it with its child's value. + // Essentially drop the unknown node. + jsonObj = jsonObj[0]; + } + } + } + } + return jsonObj; + } + + class SessionDataLoader { + constructor(config, xhrLoader, customUrlLoader, logger) { + this.config = config; + this.xhrLoader = xhrLoader; + this.customUrlLoader = customUrlLoader; + this.sessionDataCheckForCompleteness = (sessionData) => { + const { sessionDataAutoLoad } = this.config; + const result = Object.assign({}, sessionData); + if (!sessionData.complete) { + if (sessionData.itemList) { + // Playlist wihout SESSION-DATA tag has an empty itemList array + result.complete = sessionData.itemList.every((metadata) => { + if (sessionDataAutoLoad[metadata['DATA-ID']] && !metadata.VALUE && !metadata._STATUS && metadata.URI) { + this.logger.warn(`Incomplete because ${metadata['DATA-ID']} was autoloaded but no response yet`); + return false; + } + if (sessionDataAutoLoad[metadata['DATA-ID']] && !metadata.URI) { + this.logger.warn(`id=${metadata['DATA-ID']} missing uri`); + } + return true; + }); + } + else { + this.logger.warn('Uninitialized SessionData'); + } + } + return result; + }; + this.logger = logger.child({ name: 'SessionDataLoader' }); + } + loadSessionData(sessionData) { + const { sessionDataAutoLoad } = this.config; + const itemList = sessionData.itemList || []; + let loadItems$ = of(sessionData); + itemList.forEach((item) => { + const id = item['DATA-ID']; + const uri = item.URI; + //this.logger.info(`SessionData autoload ${id}: ${JSON.stringify(sessionDataAutoLoad[id])}`); + if (uri && sessionDataAutoLoad[id]) { + const url = URLToolkit$1.buildAbsoluteURL(sessionData.baseUrl, uri, { alwaysNormalize: true }); + const responseType = ''; // Use default responseType to allow XML parsing where necessary + loadItems$ = loadItems$.pipe(switchMap((sd) => { + return this.loadSessionDataItemWithUrl(url, id, responseType, this.config, sd, this.xhrLoader, this.customUrlLoader).pipe(catchError((err) => { + this.logger.error(`Error loading SessionData > url=${url}, id=${id}, err=${err}`); + return of(sd); + })); + })); + } + }); + return loadItems$.pipe(map((sd) => { + if (itemList.length < 1) { + return sd; + } + const updated = this.sessionDataCheckForCompleteness(sd); + if (updated.complete) { + return updated; + } + else { + throw new ExceptionError(false, 'Session data not complete after loading all items', ErrorResponses.IncompleteSessionData); + } + }), finalize$1(() => { + this.logger.info('finalized'); + })); + } + // Loading session data + // url : session data attribute URL, id : sessionData attribute data-id, responseType: "text", "arraybuffer", or other XMLHTTPRequest responseType + loadSessionDataItemWithUrl(url, id, responseType, config, sessionData, xhrLoader, customUrlLoader) { + const logger = getLogger(); + const context = { + url, + method: 'GET', + responseType, + xhrSetup: config.xhrSetup, + mimeType: 'application/xml', + }; + const loadConfig = getLoadConfig({ url }, config.fragLoadPolicy); + let loader; + if (isCustomUrl(url)) { + loader = customUrlLoader(context, loadConfig).pipe(map((res) => { + return this.onLoadSuccess(sessionData, id, res.data.response.data.toString(), res.data.response.data); + })); + } + else { + loader = xhrLoader(context, loadConfig).pipe(map(([xhr]) => { + return this.onLoadSuccess(sessionData, id, xhr.response, xhr.responseXML); + })); + } + return loader.pipe(catchError((err) => { + if (err instanceof TimeoutError) { + err = new SessionDataNetworkError(false, err.message, 0, ErrorResponses.SessionDataLoadTimeout); + } + else if (err instanceof GenericNetworkError) { + err = new SessionDataNetworkError(false, err.message, err.code, { code: err.code, text: 'Failed to load SessionData' }); + } + logger.error(`Unable to load SessionData > err=${err}`); + return of(this.onLoadError(sessionData, id, err)); + })); + } + onLoadSuccess(sessionData, id, response, responseXML) { + let plistDOM = null; + let result = sessionData; + if (stringIsXMLPlist(response) && (plistDOM = responseXML)) { + // Looks like XML. Use built-in XMLHttpRequest XML parser. + const attrValue = convertPlistToJSON(plistDOM); + result = this.setSessionData(sessionData, id, 'VALUE', attrValue); + } + else if (stringIsJSONPlist(response)) { + // JSON format + try { + const attrValue = JSON.parse(response); + result = this.setSessionData(sessionData, id, 'VALUE', attrValue); + } + catch (err) { + this.logger.error(`JSON parser error: ${err}`); + result = this.setSessionData(sessionData, id, 'VALUE', response); + result = this.setSessionData(result, id, '_STATUS', SessionDataStatusCode.JSON_PARSE_ERROR); + } + } + else { + this.logger.info(`Unknown ${id} format. Using raw value`); + result = this.setSessionData(sessionData, id, 'VALUE', response); + } + return result; + } + setSessionData(sessionData, id, attrName, obj) { + let result = sessionData; + if (sessionData.itemList) { + let i; + const itemList = [...sessionData.itemList]; + for (i = 0; i < sessionData.itemList.length; ++i) { + const metadata = Object.assign({}, itemList[i]); + if (metadata['DATA-ID'] === id) { + metadata[attrName] = obj; + itemList[i] = metadata; + break; + } + } + if (i === sessionData.itemList.length) { + this.logger.error(`Can't set ${attrName} of session data ${id}`); + } + result = Object.assign(Object.assign({}, sessionData), { itemList }); + } + else { + this.logger.error(`Can't set ${attrName} on uninitialized session data`); + } + return result; + } + onLoadError(sessionData, id, err) { + var _a; + return this.setSessionData(sessionData, id, '_STATUS', (_a = err.response) === null || _a === void 0 ? void 0 : _a.code); + } + } + + function cacheEntityToInfoEntity(initSegmentEntity) { + const { itemId, mediaOptionId, discoSeqNum, keyTagInfo } = initSegmentEntity; + const initSegmentInfo = { itemId, mediaOptionId, discoSeqNum, keyId: Hex.hexDump(keyTagInfo === null || keyTagInfo === void 0 ? void 0 : keyTagInfo.keyId) }; + return initSegmentInfo; + } + class MediaElementStore extends EntityStore { + constructor() { + super({}, { name: 'media-element-store', producerFn: produce_1 }); + this._activeId = ''; + } + get activeId() { + return this._activeId; + } + startMediaSession(meState, maxBufferSeconds, almostDryWaterLevelSeconds, targetDurationSeconds) { + logAction('playback.session.start'); + this._activeId = `media session: ${new Date().toISOString()}`; + applyTransaction(() => { + const lowWaterLevelSeconds = targetDurationSeconds; + const highWaterLevelSeconds = Math.max(lowWaterLevelSeconds, maxBufferSeconds - lowWaterLevelSeconds); + const mediaElementEntity = { + id: this.activeId, + desiredRate: !meState.autoplay && meState.paused ? 0 : 1, + paused: meState.paused, + gotPlaying: false, + gotLoadStart: false, + firstPlayTime: undefined, + seeking: meState.seeking, + flushing: false, + readyState: meState.readyState, + ended: meState.ended, + bufferedRanges: [], + haveEnough: false, + mediaSourceEntity: null, + expectedSbCount: NaN, + bufferMonitorInfo: { + waterLevelType: null, + almostDryWaterLevelSeconds, + lowWaterLevelSeconds, + highWaterLevelSeconds, + maxBufferSeconds, + }, + mediaOptionParsedSubtitleRecord: [], + textTracksCreated: false, + waitingForDisco: false, + }; + this.add(mediaElementEntity); + this.setActive(this.activeId); + }); + this.logger = getLogger().child({ name: 'UpdateBufferedSegments' }); + return this.activeId; + } + setMediaSourceEntity(objectUrl, readyState) { + logAction('playback.set.msObjectUrl'); + this.updateActive((mediaElementEntity) => { + if (objectUrl != null && readyState != null) { + mediaElementEntity.mediaSourceEntity = { objectUrl, readyState, duration: NaN, sourceBufferEntities: [null, null] }; + } + else { + mediaElementEntity.mediaSourceEntity = null; + } + mediaElementEntity.bufferedRanges = []; + mediaElementEntity.haveEnough = false; + mediaElementEntity.readyState = 0; + mediaElementEntity.bufferMonitorInfo.waterLevelType = null; + }); + } + set mediaElementDuration(duration) { + logAction('playback.set.mediaElementDuration'); + this.updateActive((mediaElementEntity) => { + if (mediaElementEntity) { + mediaElementEntity.mediaElementDuration = duration; + } + }); + } + set msReadyState(readyState) { + logAction('playback.set.msReadyState'); + this.updateActive(({ mediaSourceEntity }) => { + if (mediaSourceEntity) { + mediaSourceEntity.readyState = readyState; + } + }); + } + set readyState(readyState) { + logAction(`playback.set.readyState ${readyState}`); + this.updateActive((active) => { + active.readyState = readyState; + }); + } + set ended(ended) { + logAction(`playback.set.ended ${ended}`); + this.updateActive((active) => { + active.ended = ended; + }); + } + set msDuration(duration) { + logAction('playback.set.msDuration'); + this.updateActive((mediaElementEntity) => { + mediaElementEntity.mediaSourceEntity.duration = duration; + }); + } + set textTracksCreated(created) { + logAction('playback.set.textTracksCreated ${created}'); + this.updateActive((mediaElementEntity) => { + mediaElementEntity.textTracksCreated = created; + }); + } + set expectedSbCount(expectedSbCount) { + logAction('playback.set.expectedSbCount'); + this.updateActive((active) => { + active.expectedSbCount = expectedSbCount; + }); + } + set postFlushSeek(postFlushSeek) { + this.updateActive({ postFlushSeek }); + } + setSeekToPos(seekToPos, fromEvent, discoSeqNum) { + logAction(`playback.set.seekToPos: ${seekToPos === null || seekToPos === void 0 ? void 0 : seekToPos.toFixed(3)} cc: ${discoSeqNum}`); + this.updateActive((entity) => { + if (isFiniteNumber(seekToPos)) { + entity.seekTo = { pos: seekToPos, fromEvent, discoSeqNum }; + entity.gotPlaying = false; + entity.haveEnough = false; + } + else { + entity.seekTo = null; + entity.postFlushSeek = undefined; + } + if (fromEvent) { + entity.seeking = isFiniteNumber(seekToPos); + } + }); + } + set seeking(seeking) { + logAction(`playback.set.seeking: ${seeking}`); + this.updateActive((entity) => { + entity.seeking = seeking; + }); + } + set paused(paused) { + logAction(`playback.set.paused: ${paused}`); + this.updateActive((entity) => { + entity.paused = paused; + if (paused) { + entity.gotPlaying = false; + } + }); + } + // Got playing event + gotPlayingEvent() { + logAction('playback.set.playing'); + this.updateActive((entity) => { + if (!entity.paused) { + entity.gotPlaying = true; + entity.firstPlayTime = entity.firstPlayTime || performance.now(); + } + }); + } + gotLoadStartEvent() { + logAction('playback.set.loadstart'); + this.updateActive((entity) => { + entity.gotLoadStart = true; + }); + } + set desiredRate(desiredRate) { + logAction(`playback.set.desiredRate: ${desiredRate}`); + this.updateActive((active) => { + active.desiredRate = desiredRate; + }); + } + set haveEnough(haveEnough) { + logAction(`playback.set.haveEnough: ${haveEnough}`); + this.updateActive((active) => { + active.haveEnough = haveEnough; + }); + } + set flushing(flushing) { + logAction(`playback.set.flushing: ${flushing}`); + this.updateActive({ flushing }); + } + set waitingForDisco(waitingForDisco) { + logAction(`playback.set.waitingForDisco: ${waitingForDisco}`); + this.updateActive((entity) => { + if (entity) { + entity.waitingForDisco = waitingForDisco; + } + }); + } + setSourceBufferUpdating(type) { + logAction(`playback.set.sourcebuffers[${SourceBufferNames[type]}].updating`); + this.updateActive(({ mediaSourceEntity }) => { + var _a; + const sbEntity = (_a = mediaSourceEntity === null || mediaSourceEntity === void 0 ? void 0 : mediaSourceEntity.sourceBufferEntities) === null || _a === void 0 ? void 0 : _a[type]; + if (sbEntity) { + sbEntity.updating = true; + sbEntity.error = undefined; + } + }); + } + setTimestampOffset(type, timestampOffset) { + logAction(`playback.set.sourcebuffers[${SourceBufferNames[type]}].timestampOffset`); + this.updateActive(({ mediaSourceEntity }) => { + var _a; + const sbEntity = (_a = mediaSourceEntity === null || mediaSourceEntity === void 0 ? void 0 : mediaSourceEntity.sourceBufferEntities) === null || _a === void 0 ? void 0 : _a[type]; + if (sbEntity) { + sbEntity.timestampOffset = timestampOffset; + } + }); + } + setBufferedRangesUpdated(type, bufferedRanges, combinedBuffer, gotQuotaExceeded, config) { + logAction(`playback.set.sourcebuffers[${SourceBufferNames[type]}].bufferupdated`); + this.updateActive((mediaElementEntity) => { + var _a; + const mediaSourceEntity = mediaElementEntity === null || mediaElementEntity === void 0 ? void 0 : mediaElementEntity.mediaSourceEntity; + const sbEntity = (_a = mediaSourceEntity === null || mediaSourceEntity === void 0 ? void 0 : mediaSourceEntity.sourceBufferEntities) === null || _a === void 0 ? void 0 : _a[type]; + if (sbEntity) { + const msDuration = mediaSourceEntity === null || mediaSourceEntity === void 0 ? void 0 : mediaSourceEntity.duration; + sbEntity.updating = false; + sbEntity.bufferedRanges = [...bufferedRanges]; + mergeInflightSegment(sbEntity); + // updateBufferedSegments even if bufferedRanges is empty + updateBufferedSegments(sbEntity, gotQuotaExceeded, msDuration, config, this.logger); + } + mediaElementEntity.bufferedRanges = [...combinedBuffer]; + }); + } + setSourceBufferEntity(type, compatInfo) { + logAction(`playback.set.sourcebuffers[${SourceBufferNames[type]}].setSourceBufferEntity`); + this.updateActive(({ mediaSourceEntity }) => { + if (!mediaSourceEntity) + return; + const { mimeType, audioCodec, videoCodec } = compatInfo; + const sbEntity = { + mimeType, + audioCodec, + videoCodec, + updating: false, + bufferedRanges: [], + timestampOffset: 0, + // Calculated + inFlight: null, + bufferedSegments: [], + totalBytes: 0, + maxTotalBytes: 0, + gotQuotaExceeded: false, + totalDuration: Infinity, + }; + mediaSourceEntity.sourceBufferEntities[type] = sbEntity; + }); + } + setInflightSegment(type, segment) { + logAction(`playback.set.sourcebuffers[${SourceBufferNames[type]}].setInflightSegment`); + this.updateActive(({ mediaSourceEntity }) => { + var _a; + const sbEntity = (_a = mediaSourceEntity === null || mediaSourceEntity === void 0 ? void 0 : mediaSourceEntity.sourceBufferEntities) === null || _a === void 0 ? void 0 : _a[type]; + if (sbEntity) { + sbEntity.inFlight = segment; + } + }); + } + setInitSegmentEntity(type, initSegmentEntity) { + logAction(`playback.set.sourcebuffers[${SourceBufferNames[type]}].setInitSegmentEntity`); + this.updateActive(({ mediaSourceEntity }) => { + var _a; + const sbEntity = (_a = mediaSourceEntity === null || mediaSourceEntity === void 0 ? void 0 : mediaSourceEntity.sourceBufferEntities) === null || _a === void 0 ? void 0 : _a[type]; + if (sbEntity) { + sbEntity.initSegmentInfo = initSegmentEntity; + } + }); + } + setSourceBufferError(type, error) { + logAction(`playback.set.sourcebuffers[${type}].error: ${error}`); + this.updateActive(({ mediaSourceEntity }) => { + var _a; + const sbEntity = (_a = mediaSourceEntity === null || mediaSourceEntity === void 0 ? void 0 : mediaSourceEntity.sourceBufferEntities) === null || _a === void 0 ? void 0 : _a[type]; + if (sbEntity) { + sbEntity.inFlight = null; + sbEntity.updating = false; + sbEntity.error = error; + } + }); + } + setStallInfo(stallInfo) { + logAction(`playback.set.stallInfo stalled=${stallInfo != null}`); + this.updateActive((active) => { + active.stallInfo = stallInfo; + }); + } + setNudgeInfo(nudgeInfo) { + logAction(`playback.set.nudgeInfo ${stringifyWithPrecision(nudgeInfo)}`); + this.updateActive((active) => { + active.nudgeInfo = nudgeInfo; + }); + } + /** + * Update water level information + * @param combined Water level of combined buffer + * @param sbTuple Water level of each source buffer + */ + updateWaterLevels(combined, sbTuple) { + logAction('playback.set.updateWaterLevels'); + this.updateActive((entity) => { + entity.bufferMonitorInfo.waterLevelType = { combined, sbTuple: [...sbTuple] }; + }); + } + /** + * @param targetDurationSeconds The duration in seconds to use for determining low and high water. + * Low water threshold is 1 target duration, high water threshold is Math.max(lowWater, maxBuffer - targetDuration) + */ + set bufferMonitorTargetDuration(targetDurationSeconds) { + logAction(`playback.set.targetDuration: ${targetDurationSeconds}`); + this.updateActive((entity) => { + if (isFiniteNumber(targetDurationSeconds) && targetDurationSeconds > 0) { + const bufMonitorInfo = entity.bufferMonitorInfo; + bufMonitorInfo.lowWaterLevelSeconds = Math.min(targetDurationSeconds, bufMonitorInfo.maxBufferSeconds); + bufMonitorInfo.highWaterLevelSeconds = Math.max(bufMonitorInfo.lowWaterLevelSeconds, bufMonitorInfo.maxBufferSeconds - targetDurationSeconds); + } + }); + } + archiveParsedSubtitleFragmentRecord(persistentId, mediaSeqNum, fragCueRecord) { + logAction(`playback.cues.set persistentId ${persistentId} mediaSeqNum ${mediaSeqNum}: parsed ${fragCueRecord.count} time-range ${fragCueRecord.startTime}:${fragCueRecord.endTime}`); + this.updateActive((entity) => { + let trackInfo = entity.mediaOptionParsedSubtitleRecord[persistentId]; + if (!trackInfo) { + trackInfo = {}; + entity.mediaOptionParsedSubtitleRecord[persistentId] = trackInfo; + } + trackInfo[mediaSeqNum] = fragCueRecord; + }); + } + } + function mergeInflightSegment(sbEntity) { + const inFlight = sbEntity.inFlight; + const mergedBufferSeg = sbEntity.bufferedSegments; + if (inFlight && isFiniteNumber(inFlight.startPTS) && isFiniteNumber(inFlight.endPTS)) { + mergeToBuffer(mergedBufferSeg, inFlight); + } + sbEntity.inFlight = null; + } + // Helper functions for updating buffered segments, modifies sbEntity.bufferedSegments in-place + function updateBufferedSegments(sbEntity, gotQuotaExceeded, msDuration, config, logger) { + const { maxBufferHole, bufferedSegmentEjectionToleranceMs } = config; + // Modify in-place since we do not read anyway + const mergedBufferSeg = sbEntity.bufferedSegments; + const realBuffered = sbEntity.bufferedRanges; + let totalBytes = 0; + let lastFragEjected = false; + let lastFrag; + if (realBuffered.length) { + for (let i = mergedBufferSeg.length - 1; i > -1; i--) { + const segment = mergedBufferSeg[i]; + const notIframe = !segment.frag.iframe; + if (notIframe && segment.frag.isLastFragment) { + lastFrag = segment.frag; + } + const segmentDuration = segment.endPTS - segment.startPTS; + if (segmentDuration <= 0) { + mergedBufferSeg.splice(i, 1); + logger === null || logger === void 0 ? void 0 : logger.warn(`Ejecting segment from bufferedSegments due to segmentDuration <= 0 > segment=${stringifyWithPrecision(segment)}`); + lastFragEjected = segment.frag === lastFrag; + continue; // Should never happen but... + } + const overlapping = getMaxOverlappingRange(realBuffered, segment); + // startPTS/endPTS is sometimes a bit off from what is reported in buffered, + // especially for audio. Add the byteLength approximation with overlap factor. + if (overlapping) { + // Estimate total bytes using all buffered time ranges + const overlapStart = Math.max(overlapping.start, segment.startPTS); + const overlapEnd = Math.min(overlapping.end, segment.endPTS); + const appendedDuration = overlapEnd - overlapStart; + totalBytes += (segment.bytes * appendedDuration) / segmentDuration; + if (notIframe) { + // ignore tiny overlaps + if (appendedDuration < Math.min(segmentDuration, maxBufferHole)) { + mergedBufferSeg.splice(i, 1); + logger === null || logger === void 0 ? void 0 : logger.warn(`Ejecting segment from bufferedSegments due to tiny overlaps > segment=${stringifyWithPrecision(segment)}, bufferedRanges=${stringifyWithPrecision(realBuffered)}`); + lastFragEjected = segment.frag === lastFrag; + continue; + } + // Keep track of initial appended overlap, and remove the segment if the current overlap has changed + // except when the segment duration was modified because of an overlapping append in mergeToBuffer + const initialAppendedValue = segment.appendedDuration; + const appendedDurationDelta = (initialAppendedValue || 0) - appendedDuration; + // rdar://84943043 tolerance is used to avoid floating point arithmetic issues + const tolerance = Math.min(bufferedSegmentEjectionToleranceMs * 0.001, segmentDuration); + if (!initialAppendedValue) { + segment.appendedDuration = appendedDuration; + } + else if (appendedDurationDelta > tolerance && appendedDuration !== segmentDuration) { + // Eject, unless last segment and buffer end equals known mediaSink duration + if (!segment.frag.isLastFragment || overlapEnd !== msDuration) { + mergedBufferSeg.splice(i, 1); + logger === null || logger === void 0 ? void 0 : logger.warn(`Ejecting segment from bufferedSegments due to change in current overlap > segment=${stringifyWithPrecision(segment)}, delta=${appendedDurationDelta}, bufferedRanges=${stringifyWithPrecision(realBuffered)}`); + lastFragEjected = segment.frag === lastFrag; + continue; + } + } + } + } + else { + logger === null || logger === void 0 ? void 0 : logger.warn(`Ejecting segment from bufferedSegments due to no overlap > segment=${stringifyWithPrecision(segment)}, bufferedRanges=${stringifyWithPrecision(realBuffered)}`); + mergedBufferSeg.splice(i, 1); + lastFragEjected = segment.frag === lastFrag; + } + } + } + else if (mergedBufferSeg.length) { + logger === null || logger === void 0 ? void 0 : logger.info('Flushing buffered segments in response to flush'); + mergedBufferSeg.splice(0, mergedBufferSeg.length); // flushed out + } + // if last frag was ejected this round, realBuffered[realBuffered.length - 1].end will not have the right totalduration. + // if everything was flushed out, totalduration will remain unchanged (lastFrag will be false) + sbEntity.totalDuration = lastFrag && !lastFragEjected && realBuffered.length > 0 ? realBuffered[realBuffered.length - 1].end : Infinity; + sbEntity.gotQuotaExceeded = sbEntity.gotQuotaExceeded || gotQuotaExceeded; + sbEntity.totalBytes = totalBytes; + sbEntity.maxTotalBytes = Math.max(sbEntity.totalBytes, sbEntity.maxTotalBytes); + } + // Merge the newly appended segment into what we think we already have buffered + // Assumed buffered has no overlaps already and is sorted by startPTS + // Modifies in-place + function mergeToBuffer(buffered, appended) { + let appendedAdded = false; + for (let i = buffered.length - 1; i > -1; i--) { + const seg = buffered[i]; + const overlapStart = Math.max(appended.startPTS, seg.startPTS); + const overlapEnd = Math.min(appended.endPTS, seg.endPTS); + if (overlapStart >= overlapEnd) { + continue; + } + const bytes = (1 - (overlapEnd - overlapStart) / (seg.endPTS - seg.startPTS)) * seg.bytes; + if (bytes <= 0) { + buffered.splice(i, 1); + continue; + } + seg.bytes = bytes; + if (seg.startPTS < appended.startPTS) { + seg.endPTS = overlapStart; + } + else { + seg.startPTS = overlapEnd; + if (!appendedAdded) { + buffered.splice(i, 0, appended); + appendedAdded = true; + } + } + } + if (!appendedAdded) { + buffered.push(appended); + } + } + /** + * Returns length of overlap of segment with given bufferedRange + */ + function segmentOverlapLength(segment, range) { + return Math.min(segment.endPTS, range.end) - Math.max(segment.startPTS, range.start); + } + /** + * Returns the BufferedRange that has maximum ovelap with given segment. + */ + function getMaxOverlappingRange(bufferedRanges, segment) { + let maxOverlappingRange = undefined; + let lastOverlapLength = 0; + // TODO: Use binary search over bufferedRanges rdar://89159252 + for (const range of bufferedRanges) { + // Check only when there is overlap + if (range.start <= segment.endPTS && range.end > segment.startPTS) { + const currentOverlapLength = segmentOverlapLength(segment, range); + if (currentOverlapLength > lastOverlapLength) { + maxOverlappingRange = range; + lastOverlapLength = currentOverlapLength; + } + } + else if (lastOverlapLength > 0) { + // bufferedRanges is Normalized TimeRanges object i.e "The ranges in such an object are ordered, don't overlap, and don't touch" + // No point iterating through rest of ranges if overlap is found previously and no overlap afterwards. + break; + } + } + return maxOverlappingRange; + } + + function isIframeMediaFragment(frag) { + return frag != null && 'iframeMediaDuration' in frag && 'iframeMediaStart' in frag; + } + function fragEqual(a, b) { + return a === b || (a && b && a.itemId === b.itemId && a.mediaOptionId === b.mediaOptionId && a.mediaSeqNum === b.mediaSeqNum && a.discoSeqNum === b.discoSeqNum); + } + function fragPrint(frag) { + return JSON.stringify(frag, ['mediaOptionId', 'mediaSeqNum', 'discoSeqNum', 'start', 'duration']); + } + + function filterNullOrUndefined() { + return (source) => source.pipe(filter((x) => x != null), map((x) => x)); + } + + function getVideoCodecRanking(videoCodec) { + if (MediaUtil.isDolby(videoCodec)) { + return VideoCodecRank.DOVI; + } + else if (MediaUtil.isHEVC(videoCodec)) { + return VideoCodecRank.HEVC; + } + else if (MediaUtil.isVP09(videoCodec)) { + return VideoCodecRank.VP09; + } + else if (MediaUtil.isAVC(videoCodec)) { + return VideoCodecRank.AVC; + } + else { + return VideoCodecRank.UNKNOWN; + } + } + function getVideoCodecFamily(videoCodec) { + return videoCodec === null || videoCodec === void 0 ? void 0 : videoCodec.split('.')[0]; + } + function getAudioCodecRanking(audioCodec) { + if (MediaUtil.isALAC(audioCodec)) { + return AudioCodecRank.ALAC; + } + else if (MediaUtil.isFLAC(audioCodec)) { + return AudioCodecRank.FLAC; + } + else if (MediaUtil.isEC3(audioCodec)) { + return AudioCodecRank.EC3; + } + else if (MediaUtil.isAC3(audioCodec)) { + return AudioCodecRank.AC3; + } + else if (MediaUtil.isXHEAAC(audioCodec)) { + return AudioCodecRank.XHEAAC; + } + else if (MediaUtil.isAAC(audioCodec)) { + return AudioCodecRank.AAC; + } + else if (MediaUtil.isMP3(audioCodec)) { + return AudioCodecRank.MP3; + } + else { + return AudioCodecRank.UNKNOWN; + } + } + function getVideoRangeRanking(videoRange) { + if (videoRange === 'PQ') { + return VideoRangeRank.PQ; + } + else if (videoRange === 'HLG') { + return VideoRangeRank.HLG; + } + else if (videoRange === 'SDR') { + return VideoRangeRank.SDR; + } + else { + return VideoRangeRank.UNKNOWN; + } + } + function getAudioVideoCodecRanks(codecInfo) { + return { videoCodecRank: getVideoCodecRanking(codecInfo.videoCodec), audioCodecRank: getAudioCodecRanking(codecInfo.audioCodec) }; + } + class MediaOptionRank { + constructor(...identifier) { + this.identifier = identifier; + } + ensureSameIdentifierLength(rank) { + if (this.identifier.length !== rank.identifier.length) { + throw new Error(`Identifiers have non-matching lengths! (${this.identifier.length} vs ${rank.identifier.length})`); + } + } + isGreaterThan(rank) { + this.ensureSameIdentifierLength(rank); + for (let i = 0; i < this.identifier.length; ++i) { + if (this.identifier[i] < rank.identifier[i]) { + return false; + } + else if (this.identifier[i] > rank.identifier[i]) { + return true; + } + } + return false; + } + isEqualTo(rank) { + this.ensureSameIdentifierLength(rank); + return this.identifier.every((entry, i) => entry === rank.identifier[i]); + } + } + + function isIframeRate(rate) { + return isFiniteNumber(rate) && rate !== 0 && rate !== 1; + } + + class MutexBusyError extends Error { + } + const __executeAndUnlock = (value, update, unlock, operation) => { + if (!operation) { + // no operation() provided, caller should excplicitly call unlock() + return of(value); + } + let result; + let obs; + try { + result = operation(value, update); + } + catch (err) { + obs = throwError(() => err); + } + if (!obs) { + if (typeof result === 'undefined') { + obs = of(value); + } + else { + obs = from(result); + } + } + // auto-unlock if operation() has been called + return obs.pipe(finalize$1(unlock)); + }; + /** + * Mutex is a concept borrowed from multi-threaded programming. Although it's + * not the first time multi-thread syncing primitives are used in single + * threaded program. Python 3 for example has a collection of single threaded + * coroutine syncing primitives in the asyncio package. Any single threaded + * program with an event loop would have the problem of "data race" before and + * after the event loop context switch. Note that event loop achieves concurrent + * processing similar to system level process and thread scheduling on a single + * core. Therefore, classical syncing primitives are also applicable for event + * loops. + * + * IMPORTANT: Always justify your reason if you use Mutex in your code. The only + * permitable use-case is the protected operation can be called from different + * threads and only one can execute at one time. If the component has multiple + * such operations need this synchronization, please consider using command + * queue pattern instead of Mutex. + */ + class Mutex { + /** + * Mutex can optionally carry the data it's protecting. Doing so can make it + * more explicit that the data should be accessed while holing a lock of this + * mutex. + * @param value data to be stored on the Mutex + */ + constructor(value) { + this.value = value; + this.waiters = []; + this.wcounter = 0; + this.rcounter = 0; + } + lock(operation, tryLock = false) { + return this._lock(true, operation, tryLock); + } + /** + * Release a lock previously acquired on the mutex. + */ + unlock() { + this._unlock(true); + } + readLock(operation, tryLock = false) { + return this._lock(false, operation, tryLock); + } + /** + * Release a read-only lock previously acquired on the mutex. + */ + readUnlock() { + this._unlock(false); + } + _schedule() { + const _waitersToWake = []; + this.waiters = this.waiters.filter(waiter => { + if (this._canLock(waiter.rw)) { + if (waiter.rw) { + ++this.wcounter; + } + else { + ++this.rcounter; + } + _waitersToWake.push(waiter); + return false; + } + return true; + }); + for (const waiter of _waitersToWake) { + waiter.observer.next(this.value); + waiter.observer.complete(); + } + } + _canLock(rw) { + return (rw && this.wcounter === 0 && this.rcounter === 0) || (!rw && this.wcounter === 0); + } + _lock(rw, operation, tryLock = false) { + if (typeof operation === 'boolean') { + [tryLock, operation] = [operation, undefined]; + } + const obs = new Observable(observer => { + const canLock = this._canLock(rw); + if (tryLock && !canLock) { + throw new MutexBusyError(); + } + if (canLock) { + if (rw) { + ++this.wcounter; + } + else { + ++this.rcounter; + } + observer.next(); + observer.complete(); + } + else { + this.waiters.push({ rw, observer }); + } + }); + if (operation) { + return obs.pipe(mergeMap(() => __executeAndUnlock(this.value, newval => void (this.value = newval), () => this._unlock(rw), operation))); + } + return obs; + } + _unlock(rw) { + if (rw) { + this.wcounter = Math.max(this.wcounter - 1, 0); + } + else { + this.rcounter = Math.max(this.rcounter - 1, 0); + } + this._schedule(); + } + } + /** + * WaitGroup is a concept borrowed from Golang used to wait for async routines + * to finish. We can use it to signal async shutdown completion to subscribers. + * + * class Hls { + * public destroy$ = new Subject(); + * public destroyWG = new WaitGroup(); + * constructor() { + * observableWithAsyncTeardown(this).pipe( + * takeUntil(this.destroy$) // chain its lifecycle to Hls + * ).subscribe(); + * } + * public destroy(): Promise { + * this.destroy$.next(); + * // all lifecyles chained below Hls will teardown. Async routines + * // will be registered with hls.destroyWG.add() + * + * return this.destroyWG.toPromise(); + * // returned promise will resolve when all async routines + * // registered are finished with hls.destroyWG.done() + * } + * } + * const observableWithAsyncTeardown = (hls: Hls) => + * new Observable((subscriber) => { + * return () => { // teardown function + * hls.destroyWG.add(); // register an async routine + * setTimeout(() => { + * console.log('async teardown finished'); + * hls.destroyWG.done(); // signal finish of a async routine + * }, 1000); + * }; + * }); + * + * And there is a shorthand for Promise or Observable like async routine: + * + * wg.wrap(promise).subscribe(); + * + */ + class WaitGroup extends Observable { + constructor() { + super((subscriber) => this._count$ + .pipe( + // emits only when count reaches to zero + filter((count) => count === 0), + // completes once count reaches to zero + take(1), + // convert to empty value (undefined) + mapTo(void 0)) + .subscribe(subscriber)); + this._count$ = new BehaviorSubject(0); + } + /** + * Wrap an async operation to wait for. + * @param routine an async operation to wait for + */ + wrap(routine) { + return defer(() => { + this.add(); + return from(routine); + }).pipe( + // propagate error in the async routine + tap({ error: (e) => this._count$.error(e) }), + // decrement the count when the async routine finishes + finalize$1(() => this.done())); + } + /** + * Increase the wait counter. + * @param n delta to increase the counter + */ + add(n = 1) { + this._count$.next(this._count$.value + n); + } + /** + * Decrease the wait counter. + * @param n delta to decrease the counter + */ + done(n = 1) { + this._count$.next(this._count$.value - n); + } + } + + + const BufferHelper = { + isBuffered(buffered, position) { + for (let i = 0; buffered && i < buffered.length; i++) { + if (position >= buffered.start(i) && position <= buffered.end(i)) { + return true; + } + } + return false; + }, + /** + * Return Array of [ {start, end} ] + * @param media Something with a buffered TimeRanges property (HTMLMediaElement, SourceBuffer, etc) + */ + timeRangesToBufferedRange(timeRanges) { + const bufferedRangeArray = []; + for (let ix = 0; timeRanges && ix < timeRanges.length; ix++) { + bufferedRangeArray.push({ + start: timeRanges.start(ix), + end: timeRanges.end(ix), + }); + } + return bufferedRangeArray; + }, + subtitleBufferInfo(cues, pos, maxHoleDuration) { + if (cues) { + const buffered = this.bufferedCues(cues); + return this.getBufferedInfo(buffered, pos, maxHoleDuration); + } + return { len: 0, start: pos, end: pos, nextStart: undefined }; + }, + fragmentsBufferedInfo(bufferedFrags, pos, maxHoleDuration) { + const buffered = []; + for (const frag of bufferedFrags) { + buffered.push({ start: frag.start, end: frag.start + frag.duration }); + } + return this.getBufferedInfo(buffered, pos, maxHoleDuration); + }, + bufferedCues(cues) { + const buffered = []; + if (cues) { + for (let ix = 0; ix < cues.length; ix++) { + buffered.push({ start: cues[ix].startTime, end: cues[ix].endTime }); + } + } + return buffered; + }, + bufferedInfoFromMedia(media, pos, maxHoleDuration) { + return BufferHelper.getBufferedInfo(BufferHelper.timeRangesToBufferedRange(media.buffered), pos, maxHoleDuration); + }, + getBufferedInfo(inBufferedRanges, pos, maxHoleDuration) { + const buffered2 = []; + // bufferStart and bufferEnd are buffer boundaries around current video position + let bufferLen, bufferStart, bufferEnd, bufferStartNext, i; + // sort on buffer.start/smaller end (IE does not always return sorted buffered range) + const bufferedRanges = inBufferedRanges.map(({ start, end }) => ({ start, end })); + bufferedRanges.sort((a, b) => { + const diff = a.start - b.start; + if (diff) { + return diff; + } + return b.end - a.end; + }); + // there might be some small holes between buffer time range + // consider that holes smaller than maxHoleDuration are irrelevant and build another + // buffer time range representations that discards those holes + for (i = 0; i < bufferedRanges.length; i++) { + const buf2len = buffered2.length; + if (buf2len) { + const buf2end = buffered2[buf2len - 1].end; + // if small hole (value between 0 or maxHoleDuration ) or overlapping (negative) + if (bufferedRanges[i].start - buf2end < maxHoleDuration) { + // merge overlapping time ranges + // update lastRange.end only if smaller than item.end + // e.g. [ 1, 15] with [ 2,8] => [ 1,15] (no need to modify lastRange.end) + // whereas [ 1, 8] with [ 2,15] => [ 1,15] ( lastRange should switch from [1,8] to [1,15]) + if (bufferedRanges[i].end > buf2end) { + buffered2[buf2len - 1].end = bufferedRanges[i].end; + } + } + else { + // big hole + buffered2.push(bufferedRanges[i]); + } + } + else { + // first value + buffered2.push(bufferedRanges[i]); + } + } + for (i = 0, bufferLen = 0, bufferStart = bufferEnd = pos; i < buffered2.length; i++) { + const { start } = buffered2[i], { end } = buffered2[i]; + if (pos + maxHoleDuration >= start && pos < end) { + // play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length + bufferStart = start; + bufferEnd = end; + bufferLen = bufferEnd - pos; + } + else if (pos + maxHoleDuration < start) { + bufferStartNext = start; + break; + } + } + return { + len: bufferLen, + start: bufferStart, + end: bufferEnd, + nextStart: bufferStartNext, + }; + }, + toRangeString(bufferInfo) { + return `[${bufferInfo.start.toFixed(3)},${bufferInfo.end.toFixed(3)}]`; + }, + }; + + var StallType; + (function (StallType) { + StallType["Seek"] = "Seek"; + StallType["HighBuffer"] = "HighBuffer"; + StallType["LowBuffer"] = "LowBuffer"; + })(StallType || (StallType = {})); + var BufferWaterLevel; + (function (BufferWaterLevel) { + BufferWaterLevel["AlmostDry"] = "AlmostDry"; + BufferWaterLevel["LowWater"] = "LowWater"; + BufferWaterLevel["HighWater"] = "HighWater"; + BufferWaterLevel["AboveHighWater"] = "AboveHighWater"; + })(BufferWaterLevel || (BufferWaterLevel = {})); + const waterLevelToValue = { + [BufferWaterLevel.AlmostDry]: 0, + [BufferWaterLevel.LowWater]: 1, + [BufferWaterLevel.HighWater]: 2, + [BufferWaterLevel.AboveHighWater]: 3, + }; + /** + * @returns whether from water level is > to water level + */ + function waterLevelFell(from, to) { + return waterLevelToValue[from] > waterLevelToValue[to]; + } + + /** + * Highest threshold less than waterLevelSec + * @param waterLevelSec The waterlevel + */ + function getHighestThresholdBelowWater(waterLevelSec, info) { + const result = [ + { threshold: info.highWaterLevelSeconds, level: BufferWaterLevel.HighWater }, + { threshold: info.lowWaterLevelSeconds, level: BufferWaterLevel.LowWater }, + { threshold: info.almostDryWaterLevelSeconds, level: BufferWaterLevel.AlmostDry }, + ].find(({ threshold }) => { + return waterLevelSec > threshold; + }); + return result; + } + /** + * Lowest threshold higher than waterLevelSec + * @param waterLevelSec The water level + */ + function getLowestThresholdAboveWaterLevel(waterLevelSec, info) { + const result = [ + { threshold: info.almostDryWaterLevelSeconds, level: BufferWaterLevel.AlmostDry }, + { threshold: info.lowWaterLevelSeconds, level: BufferWaterLevel.LowWater }, + { threshold: info.highWaterLevelSeconds, level: BufferWaterLevel.HighWater }, + { threshold: Infinity, level: BufferWaterLevel.AboveHighWater }, + ].find(({ threshold }) => { + return waterLevelSec <= threshold; + }); + return result; + } + function updateWaterLevels(meQuery, maxBufferHole) { + const combined = getLowestThresholdAboveWaterLevel(meQuery.getCurrentWaterLevel(maxBufferHole), meQuery.bufferMonitorInfo).level; + const sbTuple = [null, null]; + [SourceBufferType.Variant, SourceBufferType.AltAudio].forEach((sbType) => { + if (meQuery.sourceBufferEntityByType(sbType) != null) { + sbTuple[sbType] = getLowestThresholdAboveWaterLevel(meQuery.getCurrentWaterLevelByType(sbType, maxBufferHole), meQuery.bufferMonitorInfo).level; + } + }); + return { combined, sbTuple }; + } + /** + * Observable that re-calculates water level when threshold or buffered range changes + */ + function waterLevelChangedFromBuffer(meQuery, maxBufferHole) { + return combineQueries([meQuery.bufferMonitorThresholds$, meQuery.combinedBuffer$, meQuery.seeking$]).pipe(map(([thresholds]) => { + if (thresholds == null) { + return null; + } + return updateWaterLevels(meQuery, maxBufferHole); + })); + } + /** + * Observable that re-calculates water level during regular playback + */ + function waterLevelChangedFromPlayback(meQuery, maxBufferHole, logger) { + return combineQueries([meQuery.combinedBuffer$, meQuery.gotPlaying$, meQuery.seeking$, meQuery.waterLevelChangedForType$(null), meQuery.stallInfo$]).pipe(switchMap(([combinedBuffer, playing, seeking, waterLevelType, stallInfo]) => { + if (combinedBuffer.length === 0 || !playing || seeking || waterLevelType == null || stallInfo != null) { + return EMPTY; + } + return scheduleWaterLevelCheck(meQuery, maxBufferHole); + })); + } + function scheduleWaterLevelCheck(meQuery, maxBufferHole, logger) { + // Combined water level should be minimum of the source buffers + const combinedWaterLevel = meQuery.getCurrentWaterLevel(maxBufferHole); + const thresholdInfo = getHighestThresholdBelowWater(combinedWaterLevel, meQuery.bufferMonitorInfo); + if (thresholdInfo) { + const { threshold } = thresholdInfo; + const delayMs = Math.ceil((combinedWaterLevel - threshold) * 1000); + return timer(delayMs).pipe(switchMap(() => { + const newCombinedWaterLevel = meQuery.getCurrentWaterLevel(maxBufferHole); + const newThresholdInfo = getHighestThresholdBelowWater(newCombinedWaterLevel, meQuery.bufferMonitorInfo); + if ((newThresholdInfo === null || newThresholdInfo === void 0 ? void 0 : newThresholdInfo.level) === thresholdInfo.level) { + // Didn't actually cross threshold, re-schedule check + return scheduleWaterLevelCheck(meQuery, maxBufferHole); + } + return VOID; + }), map(() => { + return updateWaterLevels(meQuery, maxBufferHole); + })); + } + return EMPTY; + } + /** + * Epic for updating the water level + */ + function bufferMonitorEpic(meQuery, meStore, maxBufferHole, logger) { + return merge(waterLevelChangedFromBuffer(meQuery, maxBufferHole), waterLevelChangedFromPlayback(meQuery, maxBufferHole)).pipe(observeOn(asyncScheduler), tap(({ combined, sbTuple }) => { + meStore.updateWaterLevels(combined, sbTuple); + })); + } + + /** + * @brief Query interface to playback state + */ + class MediaElementQuery extends QueryEntity { + constructor(mediaElement, mediaElementStore) { + super(mediaElementStore); + this.mediaElement = mediaElement; + } + get mediaElementDuration$() { + return this.selectActive(({ mediaElementDuration }) => mediaElementDuration); + } + get mediaElementDuration() { + var _a, _b; + return (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.mediaElementDuration) !== null && _b !== void 0 ? _b : Infinity; + } + get msDuration() { + var _a, _b; + return (_b = (_a = this.mediaSourceEntity) === null || _a === void 0 ? void 0 : _a.duration) !== null && _b !== void 0 ? _b : Infinity; + } + // return the smaller of all the sourceBuffer totaldurations + // return infinity if any of the source buffer is not fully buffered + get minSBDuration() { + var _a, _b; + let minDuration = Number.POSITIVE_INFINITY; + (_b = (_a = this.mediaSourceEntity) === null || _a === void 0 ? void 0 : _a.sourceBufferEntities) === null || _b === void 0 ? void 0 : _b.forEach((entity, index) => { + if (entity) { + // may be muxed or unmuxed + if (isFiniteNumber(entity.totalDuration)) { + // entity.totalDuration is a finite number if sb is fully buffered + minDuration = Math.min(minDuration, entity.totalDuration); + } + else { + // one of the SBs not fully buffered + minDuration = Number.NEGATIVE_INFINITY; + } + } + }); + return isFiniteNumber(minDuration) ? minDuration : Infinity; + } + get currentTime() { + return this.mediaElement.currentTime; + } + get clientWidth() { + return this.mediaElement.clientWidth; + } + get clientHeight() { + return this.mediaElement.clientHeight; + } + getBufferedDuration(maxBufferDuration = 0.5) { + const ranges = BufferHelper.timeRangesToBufferedRange(this.mediaElement.buffered); + const bufferInfo = BufferHelper.getBufferedInfo(ranges, this.currentTime, maxBufferDuration); + return bufferInfo.end - bufferInfo.start; + } + get mediaSourceEntity() { + var _a; + return (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.mediaSourceEntity; + } + get msReadyState() { + var _a; + return (_a = this.mediaSourceEntity) === null || _a === void 0 ? void 0 : _a.readyState; + } + get sourceBufferEntities() { + var _a; + return (_a = this.mediaSourceEntity) === null || _a === void 0 ? void 0 : _a.sourceBufferEntities; + } + sourceBufferEntityByType(sbType) { + var _a; + return (_a = this.sourceBufferEntities) === null || _a === void 0 ? void 0 : _a[sbType]; + } + initSegmentEntityByType(sbType) { + var _a; + return (_a = this.sourceBufferEntityByType(sbType)) === null || _a === void 0 ? void 0 : _a.initSegmentInfo; + } + get maxBufferSize() { + var _a, _b; + const entity = (_a = this.sourceBufferEntities) === null || _a === void 0 ? void 0 : _a[SourceBufferType.Variant]; + let maxBufferSize = Infinity; + if (entity === null || entity === void 0 ? void 0 : entity.gotQuotaExceeded) { + maxBufferSize = (_b = entity.maxTotalBytes) !== null && _b !== void 0 ? _b : Infinity; + } + return maxBufferSize; + } + get postFlushSeek() { + var _a; + return (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.postFlushSeek; + } + get seekable() { + return this.mediaElement.seekable; + } + get desiredRate() { + var _a; + return ((_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.desiredRate) || 0; + } + get desiredRate$() { + return this.selectActive(({ desiredRate }) => desiredRate !== null && desiredRate !== void 0 ? desiredRate : 0); + } + get effectiveRate() { + if (this.isIframeRate) { + return this.desiredRate; + } + return this.paused ? 0 : 1; + } + get playbackRate() { + return this.mediaElement.playbackRate; + } + get isIframeRate() { + const rate = this.desiredRate; + return isIframeRate(rate); + } + get isIframeRate$() { + return this.desiredRate$.pipe(map(isIframeRate)); + } + get msObjectUrl$() { + return this.selectActive(({ mediaSourceEntity }) => mediaSourceEntity === null || mediaSourceEntity === void 0 ? void 0 : mediaSourceEntity.objectUrl).pipe(distinctUntilChanged()); + } + get msReadyState$() { + return this.selectActive(({ mediaSourceEntity }) => { var _a; return (_a = mediaSourceEntity === null || mediaSourceEntity === void 0 ? void 0 : mediaSourceEntity.readyState) !== null && _a !== void 0 ? _a : null; }); + } + get readyState() { + var _a, _b; + return (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.readyState) !== null && _b !== void 0 ? _b : 0; + } + get readyState$() { + return this.selectActive(({ readyState }) => readyState !== null && readyState !== void 0 ? readyState : 0); + } + get mediaSourceEntity$() { + return this.selectActive(({ mediaSourceEntity }) => mediaSourceEntity); + } + get expectedSbCount$() { + return this.selectActive(({ expectedSbCount }) => expectedSbCount); + } + get expectedSbCount() { + var _a; + return (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.expectedSbCount; + } + get paused$() { + return this.selectActive(({ paused }) => paused); + } + get paused() { + var _a, _b; + return (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.paused) !== null && _b !== void 0 ? _b : true; + } + get playbackStarted() { + var _a; + return isFiniteNumber((_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.firstPlayTime); + } + get flushing$() { + return this.selectActive(({ flushing }) => flushing); + } + get flushing() { + var _a, _b; + return (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.flushing) !== null && _b !== void 0 ? _b : false; + } + get waitingForDisco$() { + return this.selectActive(({ waitingForDisco }) => waitingForDisco); + } + get waitingForDisco() { + var _a, _b; + return (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.waitingForDisco) !== null && _b !== void 0 ? _b : false; + } + get gotPlaying() { + var _a, _b; + return (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.gotPlaying) !== null && _b !== void 0 ? _b : false; + } + get gotPlaying$() { + return this.selectActive(({ gotPlaying }) => gotPlaying); + } + get gotLoadStart$() { + return this.selectActive(({ gotLoadStart }) => gotLoadStart); + } + get seekTo() { + var _a; + return (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.seekTo; + } + get seekTo$() { + return this.selectActive(({ seekTo }) => seekTo); + } + get seeking() { + var _a, _b; + return (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.seeking) !== null && _b !== void 0 ? _b : false; + } + get seeking$() { + return this.selectActive(({ seeking }) => seeking); + } + get nudgeTarget$() { + return this.selectActive(({ nudgeInfo }) => nudgeInfo === null || nudgeInfo === void 0 ? void 0 : nudgeInfo.nudgeTarget); + } + get nudgeCount() { + var _a, _b, _c; + return (_c = (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.nudgeInfo) === null || _b === void 0 ? void 0 : _b.nudgeCount) !== null && _c !== void 0 ? _c : 0; + } + get sourceBufferEntities$() { + return this.selectActive(({ mediaSourceEntity }) => mediaSourceEntity === null || mediaSourceEntity === void 0 ? void 0 : mediaSourceEntity.sourceBufferEntities); + } + sourceBufferEntityByType$(sbType) { + return this.selectActive(({ mediaSourceEntity }) => { var _a; return (_a = mediaSourceEntity === null || mediaSourceEntity === void 0 ? void 0 : mediaSourceEntity.sourceBufferEntities) === null || _a === void 0 ? void 0 : _a[sbType]; }); + } + // Get info on what we think is currently in the buffer by type + bufferedSegmentsByType$(sbType) { + return this.selectActive(({ mediaSourceEntity }) => { var _a, _b, _c; return (_c = (_b = (_a = mediaSourceEntity === null || mediaSourceEntity === void 0 ? void 0 : mediaSourceEntity.sourceBufferEntities) === null || _a === void 0 ? void 0 : _a[sbType]) === null || _b === void 0 ? void 0 : _b.bufferedSegments) !== null && _c !== void 0 ? _c : []; }); + } + getBufferedSegmentsByType(sbType) { + var _a, _b; + return (_b = (_a = this.sourceBufferEntityByType(sbType)) === null || _a === void 0 ? void 0 : _a.bufferedSegments) !== null && _b !== void 0 ? _b : []; + } + // Get info on what we think is currently in both source buffers + get bufferedSegmentsTuple$() { + return combineQueries([this.bufferedSegmentsByType$(SourceBufferType.Variant), this.bufferedSegmentsByType$(SourceBufferType.AltAudio)]).pipe(debounceTime(10)); + } + get timeupdate$() { + const target = fromEventTarget(this.mediaElement); + return target.event('timeupdate').pipe(observeOn(asyncScheduler), share(), throttleTime(125, undefined, { leading: true, trailing: true }), map((_) => this.currentTime), filter((pos) => isFiniteNumber(pos))); + } + get playingEvent$() { + const target = fromEventTarget(this.mediaElement); + return target.event('playing').pipe(map(() => undefined)); + } + get mediaElementEntity$() { + return this.selectActive((entity) => Boolean(entity)); + } + get ended$() { + return this.selectActive((entity) => { var _a; return (_a = entity === null || entity === void 0 ? void 0 : entity.ended) !== null && _a !== void 0 ? _a : false; }); + } + sbUpdating$(type) { + return this.selectActive((entity) => { var _a, _b, _c, _d; return (_d = (_c = (_b = (_a = entity === null || entity === void 0 ? void 0 : entity.mediaSourceEntity) === null || _a === void 0 ? void 0 : _a.sourceBufferEntities) === null || _b === void 0 ? void 0 : _b[type]) === null || _c === void 0 ? void 0 : _c.updating) !== null && _d !== void 0 ? _d : false; }); + } + sbUpdating(type) { + var _a, _b, _c, _d, _e; + return (_e = (_d = (_c = (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.mediaSourceEntity) === null || _b === void 0 ? void 0 : _b.sourceBufferEntities) === null || _c === void 0 ? void 0 : _c[type]) === null || _d === void 0 ? void 0 : _d.updating) !== null && _e !== void 0 ? _e : false; + } + sbError$(type) { + return this.selectActive((entity) => { var _a, _b, _c; return (_c = (_b = (_a = entity === null || entity === void 0 ? void 0 : entity.mediaSourceEntity) === null || _a === void 0 ? void 0 : _a.sourceBufferEntities) === null || _b === void 0 ? void 0 : _b[type]) === null || _c === void 0 ? void 0 : _c.error; }); + } + get updating$() { + return combineQueries([this.sbUpdating$(SourceBufferType.Variant), this.sbUpdating$(SourceBufferType.AltAudio)]).pipe(map((values) => values.some((updating) => updating))); + } + get bufferedRangeTuple$() { + return combineQueries([ + this.selectActive((entity) => { var _a, _b, _c, _d; return (_d = (_c = (_b = (_a = entity === null || entity === void 0 ? void 0 : entity.mediaSourceEntity) === null || _a === void 0 ? void 0 : _a.sourceBufferEntities) === null || _b === void 0 ? void 0 : _b[MediaOptionType.Variant]) === null || _c === void 0 ? void 0 : _c.bufferedRanges) !== null && _d !== void 0 ? _d : null; }), + this.selectActive((entity) => { var _a, _b, _c, _d; return (_d = (_c = (_b = (_a = entity === null || entity === void 0 ? void 0 : entity.mediaSourceEntity) === null || _a === void 0 ? void 0 : _a.sourceBufferEntities) === null || _b === void 0 ? void 0 : _b[MediaOptionType.AltAudio]) === null || _c === void 0 ? void 0 : _c.bufferedRanges) !== null && _d !== void 0 ? _d : null; }), + ]); + } + getBufferedRangeByType(type) { + var _a, _b; + return (_b = (_a = this.sourceBufferEntities[type]) === null || _a === void 0 ? void 0 : _a.bufferedRanges) !== null && _b !== void 0 ? _b : []; + } + get combinedBuffer$() { + return this.selectActive((entity) => { var _a; return (_a = entity === null || entity === void 0 ? void 0 : entity.bufferedRanges) !== null && _a !== void 0 ? _a : []; }); + } + getBufferInfo(pos, maxBufferHole) { + var _a; + const bufferedRanges = (_a = this.sourceBufferEntities) === null || _a === void 0 ? void 0 : _a.map((x) => x === null || x === void 0 ? void 0 : x.bufferedRanges); + const defaultInfo = { buffered: { start: pos, end: pos, len: 0 }, bufferedSegments: [] }; + const res = [defaultInfo, defaultInfo]; + if (!bufferedRanges) { + return res; + } + bufferedRanges.forEach((ranges, type) => { + var _a; + if (ranges) { + const bufferInfo = BufferHelper.getBufferedInfo(ranges, pos, maxBufferHole); + const segments = (_a = this.sourceBufferEntities[type].bufferedSegments) !== null && _a !== void 0 ? _a : []; + const bufferedSegments = segments.filter((s) => !(s.endPTS < bufferInfo.start || s.startPTS > bufferInfo.end)); + res[type] = { buffered: bufferInfo, bufferedSegments }; + } + }); + return res; + } + getCombinedBufferInfo(pos, maxBufferHole) { + const entity = this.getActive(); + if (entity) { + return BufferHelper.getBufferedInfo(entity.bufferedRanges, pos, maxBufferHole); + } + return null; + } + get bufferMonitorInfo() { + var _a, _b; + return (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.bufferMonitorInfo) !== null && _b !== void 0 ? _b : null; + } + get bufferMonitorThresholds$() { + return this.selectActive((entity) => { + const info = entity === null || entity === void 0 ? void 0 : entity.bufferMonitorInfo; + if (!info) { + return null; + } + const { almostDryWaterLevelSeconds, lowWaterLevelSeconds, highWaterLevelSeconds, maxBufferSeconds } = info; + return { almostDryWaterLevelSeconds, lowWaterLevelSeconds, highWaterLevelSeconds, maxBufferSeconds }; + }).pipe(distinctUntilChanged((a, b) => (a === null || a === void 0 ? void 0 : a.lowWaterLevelSeconds) === (b === null || b === void 0 ? void 0 : b.lowWaterLevelSeconds))); + } + get waterLevelType$() { + return this.selectActive((entity) => { var _a; return (_a = entity === null || entity === void 0 ? void 0 : entity.bufferMonitorInfo.waterLevelType) !== null && _a !== void 0 ? _a : null; }); + } + waterLevelForType(sbType) { + var _a, _b, _c; + return (_c = (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.bufferMonitorInfo.waterLevelType) === null || _b === void 0 ? void 0 : _b.sbTuple[sbType]) !== null && _c !== void 0 ? _c : null; + } + waterLevelChangedForType$(sbType) { + return this.waterLevelType$.pipe(map((waterLevelType) => { + if (waterLevelType == null) { + return null; + } + if (sbType == null) { + return waterLevelType.combined; + } + return waterLevelType.sbTuple[sbType]; + })); + } + /** + * @returns emits whether we crossed LowWaterLevel or AlmostDryWater thresholds + */ + get fellBelowLowWater$() { + return this.waterLevelChangedForType$(SourceBufferType.Variant).pipe(pairwise(), map(([from, to]) => { + return waterLevelFell(from, to) && (to === BufferWaterLevel.LowWater || to === BufferWaterLevel.AlmostDry); + }), withLatestFrom(this.seekTo$, this.waitingForDisco$), map(([fellBelowThreshold, seekTo, waitingForDisco]) => { + return fellBelowThreshold && !isFiniteNumber(seekTo === null || seekTo === void 0 ? void 0 : seekTo.pos) && !waitingForDisco; + }), startWith(false)); + } + /** + * @returns whether the current position has buffer all the way up to the end + * FIXME: Gapless mode uses mediaElementDuration because current sb.totalduration may confuse item preloading + * (consider reseting sb.totalduration or use another variable to track preloading state) + */ + isBufferedToEnd$(maxBufferHole, nonGapless = true) { + return combineQueries([ + this.combinedBuffer$, + this.selectActive((entity) => entity.bufferMonitorInfo).pipe(filterNullOrUndefined(), + // Threshold value + map((bufferInfo) => (nonGapless ? bufferInfo.almostDryWaterLevelSeconds : Math.max(bufferInfo.almostDryWaterLevelSeconds, bufferInfo.lowWaterLevelSeconds / 2)))), + this.seeking$, + ]).pipe(map(([combinedBuffer, closeToEndThreshold]) => { + const totalSBBufferedDuration = this.minSBDuration; + if (!combinedBuffer || (!isFiniteNumber(totalSBBufferedDuration) && nonGapless)) { + return false; + } + // TODO: may need to tweak this for gapless mode + const combined = BufferHelper.getBufferedInfo(combinedBuffer, this.currentTime, maxBufferHole); + const bufferEnd = combined.end; + let duration; + let ended; + if (nonGapless) { + // one last combined buffer duration check against known sb.totalduration (should be the same value) + duration = totalSBBufferedDuration; + ended = Math.abs(duration - bufferEnd) <= closeToEndThreshold; // remove closeToEndThreshould if we can confirm combined buffered len is as accurate as sb buffered len + } + else { + duration = this.mediaElementDuration; + ended = duration - bufferEnd <= closeToEndThreshold; + } + getLogger().trace('Fully buffered calculation duration=%d, bufferEnd=%d, closeToEndThreshold=%d ended=%d', totalSBBufferedDuration, bufferEnd, closeToEndThreshold, ended); + return ended; + }), distinctUntilChanged()); + } + needData$(maxBufferHole, inGaplessMode = false) { + const useMinSbDuration = !inGaplessMode; + return combineQueries([ + this.msReadyState$, + this.waterLevelChangedForType$(null), + this.isBufferedToEnd$(maxBufferHole, useMinSbDuration), + this.bufferedRangeTuple$, + this.seekTo$, + this.mediaElementDuration$, + ]).pipe(debounceTime(10), + // observeOn(asyncScheduler), debounceTime already uses asyncScheduler + map(([readyState, waterLevelType, bufferedToEnd, _, seekTo]) => { + if (readyState === 'closed') { + return false; // Allow append if 'ended' + } + if (inGaplessMode) { + getLogger().debug({ name: 'MediaQuery' }, 'Preloading is true, forcing needData to true'); + return true; + } + const haveRoomToLoad = waterLevelType == null || (!bufferedToEnd && waterLevelType !== BufferWaterLevel.AboveHighWater); + const userIsActive = this.isIframeRate || !!seekTo; // buffered to end but user may seek or trickplay away to unbuffered ranges + getLogger().debug({ name: 'MediaQuery' }, `needData = haveRoomToLoad ${haveRoomToLoad} = (bufferedToEnd: ${bufferedToEnd}, waterLevelType: ${waterLevelType}) or userIsActive ${userIsActive} = (isIframeRate: ${this.isIframeRate}, seekTo.pos: ${seekTo === null || seekTo === void 0 ? void 0 : seekTo.pos})`); + return haveRoomToLoad || (waterLevelType !== BufferWaterLevel.AboveHighWater && userIsActive); + }), tag('needData')); + } + getSourceBufferInfoAction(needData, anchorTime, switchContexts, maxBufferHole) { + const { currentTime, sourceBufferEntities, msReadyState } = this; + let bufferInfoTuple = [null, null]; + if (!needData && switchContexts.every((x) => !(x === null || x === void 0 ? void 0 : x.userInitiated))) { + return null; + } + if (msReadyState !== 'open' || !sourceBufferEntities || sourceBufferEntities[0] == null) { + return { position: anchorTime === null || anchorTime === void 0 ? void 0 : anchorTime.pos, discoSeqNum: anchorTime === null || anchorTime === void 0 ? void 0 : anchorTime.discoSeqNum, bufferInfoTuple, switchContexts }; + } + bufferInfoTuple = this.getBufferInfo(currentTime, maxBufferHole); + return { position: currentTime, discoSeqNum: anchorTime === null || anchorTime === void 0 ? void 0 : anchorTime.discoSeqNum, bufferInfoTuple, switchContexts }; + } + get haveEnough() { + var _a, _b; + return (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.haveEnough) !== null && _b !== void 0 ? _b : false; + } + get haveEnough$() { + return this.selectActive(({ haveEnough }) => haveEnough); + } + static likelyToKeepUp(mediaElement, haveEnough, readyState) { + return haveEnough && readyState >= mediaElement.HAVE_FUTURE_DATA; + } + get playbackLikelyToKeepUp() { + return MediaElementQuery.likelyToKeepUp(this.mediaElement, this.haveEnough, this.readyState); + } + get playbackLikelyToKeepUp$() { + return combineQueries([this.haveEnough$, this.readyState$]).pipe(map(([haveEnough, readyState]) => MediaElementQuery.likelyToKeepUp(this.mediaElement, haveEnough, readyState))); + } + // Combined water level at current position + getCurrentWaterLevel(maxBufferHole) { + var _a, _b; + const currentTime = this.currentTime; + const bufferedRanges = (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.bufferedRanges) !== null && _b !== void 0 ? _b : []; + const bufferInfo = BufferHelper.getBufferedInfo(bufferedRanges, currentTime, maxBufferHole); + return bufferInfo.len; + } + //Returns buffer level info, this is only being used for QE logging. + getCombinedMediaSourceBufferInfo(maxBufferHole) { + var _a, _b, _c, _d; + const currentTime = this.currentTime; + const [variantBufferedRanges, altAudioBufferedRanges] = (_b = (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.mediaSourceEntity) === null || _b === void 0 ? void 0 : _b.sourceBufferEntities; + const variantBufferInfo = BufferHelper.getBufferedInfo((_c = variantBufferedRanges === null || variantBufferedRanges === void 0 ? void 0 : variantBufferedRanges.bufferedRanges) !== null && _c !== void 0 ? _c : [], currentTime, maxBufferHole); + const altAudioBufferInfo = BufferHelper.getBufferedInfo((_d = altAudioBufferedRanges === null || altAudioBufferedRanges === void 0 ? void 0 : altAudioBufferedRanges.bufferedRanges) !== null && _d !== void 0 ? _d : [], currentTime, maxBufferHole); + return [variantBufferInfo, altAudioBufferInfo]; + } + // SourceBuffer water level at current position + getCurrentWaterLevelByType(sbType, maxBufferHole) { + var _a; + const currentTime = this.currentTime; + const sbEntity = this.sourceBufferEntityByType(sbType); + const bufferedRanges = (_a = sbEntity === null || sbEntity === void 0 ? void 0 : sbEntity.bufferedRanges) !== null && _a !== void 0 ? _a : []; + const bufferInfo = BufferHelper.getBufferedInfo(bufferedRanges, currentTime, maxBufferHole); + return bufferInfo.len; + } + /** + * For live, returns true, If the live update, has overlap with + * already appended buffer, and therefore can be played back without gap + */ + canContinuePlaybackWithoutGap(details, lastUpdateMillis, playlistEstimate, maxBufferHole) { + if (details.type !== 'LIVE') { + // even for EVENT, return true as predictedStartPosition will be 0. + return true; + } + if (!details.ptsKnown) { + return false; + } + const position = this.currentTime; + const predictedReceiveTime = performance.now() + playlistEstimate.avgPlaylistLoadTimeMs + details.targetduration * 1000; + const predictedStartPosition = details.fragments[0].start + (predictedReceiveTime - lastUpdateMillis) / 1000; + const bufferInfo = this.getCombinedBufferInfo(position, maxBufferHole); + let end = bufferInfo.end; + if (end >= details.fragments[0].start - maxBufferHole && end <= details.fragments[0].start + details.totalduration) { + end = details.fragments[0].start + details.totalduration; + } + return predictedStartPosition <= end; + } + get stallInfo$() { + return this.selectActive((entity) => { var _a; return (_a = entity === null || entity === void 0 ? void 0 : entity.stallInfo) !== null && _a !== void 0 ? _a : null; }); + } + get textTracks() { + return this.mediaElement.textTracks; + } + get textTracksCreated$() { + return this.selectActive((entity) => entity === null || entity === void 0 ? void 0 : entity.textTracksCreated); + } + get mediaOptionParsedSubtitleRecord() { + var _a; + return (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.mediaOptionParsedSubtitleRecord; + } + getParsedSubtitleRecordsForMediaOption(persistentId) { + if (!this.mediaOptionParsedSubtitleRecord) + return null; + const subtitleParsedInfoDict = this.mediaOptionParsedSubtitleRecord[persistentId]; + return subtitleParsedInfoDict ? subtitleParsedInfoDict : {}; + } + } + + class MediaFunctions { + constructor(mediaSink, media, config, logger) { + this.mediaSink = mediaSink; + this.media = media; + this.logger = logger; + this.useCustomMediaFunctions = config.useCustomMediaFunctions; + this.overridePlaybackRate = config.overridePlaybackRate; + } + install() { + const media = this.media; + if (!media) { + return; + } + // * guarantee original play, pause methods will be in media.originalPlay and media.originalPause. + // * always override media.play and media.pause with new methods. + if (this.useCustomMediaFunctions && media && media.play && media.pause) { + // not a bogus media object for unit tests + // always preserve the original play, pause methods + if (!media.originalPlay) { + media.originalPlay = media.play.bind(media); + } + if (!media.originalPause) { + media.originalPause = media.pause.bind(media); + } + // always install new functions over old ones. + this.logger.debug('install custom pause & play methods'); + media.play = () => { + this.logger.debug('overridden play'); + this.mediaSink.checkForReplay(); + this.mediaSink.desiredRate = 1; + const isPlaying = media.currentTime > 0 && !media.paused && !media.ended && media.readyState > 2 ? true : false; + if (!isPlaying) { + // media is not playing now, save the promise and handle whenever the originalPlay promise returns + return new Promise((resolve, reject) => { + if (!this.pendingPlayPromises) { + this.pendingPlayPromises = []; + } + this.pendingPlayPromises.push({ resolve, reject }); + }); + } + else { + return Promise.resolve(); + } + }; + media.pause = () => { + this.logger.debug('overridden pause'); + this.mediaSink.desiredRate = 0; + }; + } + if (typeof HTMLMediaElement === 'function' && this.overridePlaybackRate) { + Object.defineProperty(media, 'playbackRate', { + enumerable: true, + configurable: true, + get: function () { + // pretend it's always 1 to all callers + return 1; + }, + set: function (newValue) { + // allow passthrough set to signal trickplay mode to the media engine + Object.getOwnPropertyDescriptor(HTMLMediaElement.prototype, 'playbackRate').set.call(this, newValue); + }, + }); + } + this.playPromise = null; + this.expectPauseEvent = this.expectPlayEvent = false; + } + uninstall() { + const media = this.media; + if (media) { + if (media.originalPlay) { + this.logger.debug('restore media.play'); + media.play = media.originalPlay; + delete media.originalPlay; + } + if (media.originalPause) { + this.logger.debug('restore media.pause'); + media.pause = media.originalPause; + delete media.originalPause; + } + if (this.overridePlaybackRate) { + media.playbackRate = 1; // restore original value + delete media.playbackRate; + } + } + this.playPromise = null; + this.expectPauseEvent = this.expectPlayEvent = false; + } + play() { + if (this.media) { + const flushing = this.mediaSink.flushing; + if (this.playPromise || flushing) { + this.logger.warn(`Ignoring play command playPromise/flushing ${Boolean(this.playPromise)}/${flushing}`); + return; + } + this.expectPlayEvent = this.expectPlayEvent || this.media.paused; + this.logger.info(`play() expectPlayEvent=${this.expectPlayEvent}`); + this.playPromise = this._mediaPlayInternal(); + if (this.playPromise) { + this.playPromise + .then(function () { + this.logger.info('play() complete'); + this.playPromise = null; + this._handlePendingPlayPromises(null); + }.bind(this)) + .catch(function (err) { + // Could be aborted by pause + this.playPromise = null; + this.expectPlayEvent = false; + this._handlePendingPlayPromises(err || new Error('Play rejected for unknown reason')); + if ((err === null || err === void 0 ? void 0 : err.name) === 'NotAllowedError') { + this.logger.warn('play() not allowed, going back to rate 0'); + this.mediaSink.desiredRate = 0; + } + else { + this.logger.error(`play() error: ${err === null || err === void 0 ? void 0 : err.message}`); + } + }.bind(this)); + } + } + } + pause() { + if (this.media) { + this.logger.info(`pause() playPromise=${Boolean(this.playPromise)}`); + if (this.playPromise) { + // this.playPromise is cleared as soon + // as it resolves in _mediaPlay so we shouldn't fall in here twice + this.playPromise + .then(() => { + const mediaQuery = this.mediaSink.mediaQuery; + if (this.mediaSink.desiredRate === 0 || (mediaQuery.seeking && !mediaQuery.playbackLikelyToKeepUp)) { + this._mediaPauseInternal(); + } + }) + .catch((error) => { + this.logger.error(`Promise error in pause(): ${error.message}`); + }); + } + else { + this._mediaPauseInternal(); + } + } + } + _handlePendingPlayPromises(err) { + var _a; + const count = (_a = this.pendingPlayPromises) === null || _a === void 0 ? void 0 : _a.length; + if (!err) { + // resolve all pending promises + for (let i = 0; i < count; i++) { + this.pendingPlayPromises[i].resolve(); + } + } + else { + // reject all pending promises + for (let i = 0; i < count; i++) { + this.pendingPlayPromises[i].reject(err); + } + } + this.pendingPlayPromises = []; + } + _mediaPlayInternal() { + const playFn = this.media.originalPlay || this.media.play.bind(this.media); + return playFn(); + } + _mediaPauseInternal() { + this.expectPauseEvent = this.expectPauseEvent || !this.media.paused; + this.logger.info(`pause() expectPauseEvent=${this.expectPauseEvent}`); + const pauseFn = this.media.originalPause || this.media.pause.bind(this.media); + return pauseFn(); + } + } + + class MseError extends Error { + } + // Generic SourceBuffer error + class SourceBufferError extends HlsError { + constructor(details, fatal, reason, response, sbType) { + super(ErrorTypes.MEDIA_ERROR, details, fatal, reason, response); + this.sbType = sbType; + this.response = response; + } + } + // Got error when calling addSourceBuffer (unsupported format) + class CreateSourceBufferError extends SourceBufferError { + constructor(message, response, sbType, mediaOptionId) { + super(ErrorDetails.BUFFER_ADD_CODEC_ERROR, false, message, response, sbType); + this.mediaOptionId = mediaOptionId; + this.mediaOptionType = sourceBufferTypeToMediaOptionType(this.sbType); + } + } + class AppendBufferError extends SourceBufferError { + constructor(details, fatal, reason, response, sbType, isTimeout) { + super(details, fatal, reason, response, sbType); + this.isTimeout = isTimeout; + this.mediaOptionType = sourceBufferTypeToMediaOptionType(this.sbType); + } + } + class BufferFullError extends AppendBufferError { + constructor(reason, response, sbType, maxTotalBytes) { + super(ErrorDetails.BUFFER_FULL_ERROR, false, reason, response, sbType, false); + this.maxTotalBytes = maxTotalBytes; + } + } + class AppendTimeoutError extends AppendBufferError { + constructor(reason, response, sbType) { + super(ErrorDetails.BUFFER_APPEND_ERROR, false, reason, response, sbType, true); + } + } + // Got decode error during append + class MediaDecodeError extends AppendBufferError { + constructor(reason, response, sbType, mediaOptionId) { + super(ErrorDetails.BUFFER_APPEND_ERROR, false, reason, response, sbType, false); + this.mediaOptionId = mediaOptionId; + this.mediaOptionType = sourceBufferTypeToMediaOptionType(this.sbType); + } + } + class BufferStallError extends HlsError { + constructor(details, fatal, reason, response, stallType, bufferLen, nudgePosition = NaN) { + super(ErrorTypes.MEDIA_ERROR, details, fatal, reason, response); + this.stallType = stallType; + this.bufferLen = bufferLen; + this.nudgePosition = nudgePosition; + this.response = response; + } + } + + /** + * Observable SourceBuffer adapter that cleans up after itself on unsubscribe + * From src/mse-rxjs/sourcebuffer-adapter.ts + */ + const APPEND_MAX_MS = 10000; // Failsafe append timeout value + class SourceBufferAdapter extends Observable { + constructor(mediaElementStore, mediaElementQuery, mediaElement, mediaSource, type, sourceBuffer, compatInfo, parentLogger, config) { + super((subscriber) => { + const target = fromEventTarget(sourceBuffer); + const logger = parentLogger.child({ sb: type }); + mediaElementStore.setSourceBufferEntity(type, compatInfo); + if (compatInfo.mimeType.includes('audio/mpeg')) { + this.updateMp3Timestamps = true; + } + const sub = merge(target.event('updatestart').pipe(tap(() => { + logger.trace(`[sb${this.type}] updatestart`); + mediaElementStore.setSourceBufferUpdating(type); + })), + // Note we need to do this to keep buffer data updated, + // unless we start allowing abort() as part of teardown / finalize on append/remove + target.event('updateend').pipe(observeOn(asyncScheduler), tap(() => { + const bufferedRanges = BufferHelper.timeRangesToBufferedRange(sourceBuffer.buffered); + const combinedBuffer = BufferHelper.timeRangesToBufferedRange(mediaElement.buffered); + logger.trace(`[sb${this.type}] updateend`); + mediaElementStore.setBufferedRangesUpdated(type, bufferedRanges, combinedBuffer, false, config); + })), target.event('error').pipe(tap(() => { + mediaElementStore.setSourceBufferError(type, 'Got source buffer error'); + }))) + .pipe(switchMap(() => EMPTY)) + .subscribe(subscriber); + return () => { + sub.unsubscribe(); + try { + if (mediaSource.readyState === 'open') { + sourceBuffer.abort(); + } + mediaSource.removeSourceBuffer(sourceBuffer); + } + catch (err) { + logger.error(`Error aborting SourceBuffer on unsubscribe: ${err.message}`); + } + }; + }); + this.mediaElementStore = mediaElementStore; + this.mediaElementQuery = mediaElementQuery; + this.mediaElement = mediaElement; + this.type = type; + this.sourceBuffer = sourceBuffer; + this.config = config; + this.updateMp3Timestamps = false; + } + get buffered() { + return this.sourceBuffer.buffered; + } + /** + * @param data The buffer to append + * @param segment Info about the segment. null means init segment + */ + appendBuffer(data, segment) { + // Use defer so that on re-subscribe we will call appendBuffer again + return defer(() => { + if (this.sourceBuffer.updating) { + return this._waitForUpdateEndOrError().pipe(switchMap(() => this.appendBuffer(data, segment))); + } + return this._appendBufferAsync(data, segment); + }); + } + _appendBufferAsync(data, segment) { + let startAppend = NaN; + let inFlight = null; + const mediaOptionId = 'startPTS' in segment ? segment.frag.mediaOptionId : segment.mediaOptionId; + try { + if ('startPTS' in segment) { + inFlight = { startPTS: segment.startPTS, endPTS: segment.endPTS, bytes: segment.bytes, frag: Object.assign({}, segment.frag) }; + } + this.mediaElementStore.setInflightSegment(this.type, inFlight); + startAppend = performance.now(); + this.sourceBuffer.appendBuffer(data); + } + catch (err) { + // Synchronous errors come from prepare append algorithm + // https://www.w3.org/TR/media-source/#sourcebuffer-prepare-append + switch (err.code) { + case 22: { + // QuotaExceededError: http://www.w3.org/TR/html5/infrastructure.html#quotaexceedederror + // let's stop appending any segments, and report BUFFER_FULL_ERROR error + this.mediaElementStore.setBufferedRangesUpdated(this.type, BufferHelper.timeRangesToBufferedRange(this.sourceBuffer.buffered), BufferHelper.timeRangesToBufferedRange(this.mediaElement.buffered), true, this.config); + return throwError(new BufferFullError(err.message, ErrorResponses.AllocationFailed, this.type, this.maxTotalBytes)); + } + default: + this.mediaElementStore.setInflightSegment(this.type, null); + if (this.mediaElement.error) { + return throwError(new MediaDecodeError(err.message, ErrorResponses.VideoDecoderBadDataErr, this.type, mediaOptionId)); + } + // 1. buffer removed from MediaSource + // 2. updating === true + // 3. other reason + return throwError(err); + } + } + return this._waitForUpdateEndOrError().pipe(map(() => ({ + startAppend: startAppend, + endAppend: performance.now(), + bytesAppend: data.byteLength, + })), timeout(APPEND_MAX_MS), catchError((err) => { + if (err instanceof TimeoutError) { + this.sourceBuffer.abort(); + err = new AppendTimeoutError(`Append took longer than ${APPEND_MAX_MS}ms`, ErrorResponses.InternalError, this.type); + } + else if (err instanceof SourceBufferError) { + err = new MediaDecodeError('Decode error', ErrorResponses.VideoDecoderBadDataErr, this.type, mediaOptionId); + } + throw err; // always throw + })); + } + remove(start, end) { + return this._waitForUpdateEndOrError().pipe(switchMap(this._removeAsync.bind(this, start, end))); + } + _removeAsync(start, end) { + try { + this.sourceBuffer.remove(start, end); + } + catch (err) { + return throwError(new SourceBufferError(ErrorDetails.INTERNAL_EXCEPTION, false, err.message, ErrorResponses.InternalError, this.type)); + } + return this._waitForUpdateEndOrError(); + } + abort() { + try { + this.sourceBuffer.abort(); + } + catch (err) { + return throwError(new SourceBufferError(ErrorDetails.INTERNAL_EXCEPTION, false, err.message, ErrorResponses.InternalError, this.type)); + } + return this._waitForUpdateEndOrError(); + } + get updating() { + return this.sourceBuffer.updating; + } + get timestampOffset() { + return this.sourceBuffer.timestampOffset; + } + set timestampOffset(value) { + this.sourceBuffer.timestampOffset = value; + } + /** + * Whether we ever got QuotaExceeded error + */ + get gotQuotaExceeded() { + var _a, _b; + return (_b = (_a = this.mediaElementQuery.sourceBufferEntityByType(this.type)) === null || _a === void 0 ? void 0 : _a.gotQuotaExceeded) !== null && _b !== void 0 ? _b : false; + } + /** + * Which segments we think are in the buffer + */ + get bufferedSegments() { + var _a, _b; + return (_b = (_a = this.mediaElementQuery.sourceBufferEntityByType(this.type)) === null || _a === void 0 ? void 0 : _a.bufferedSegments) !== null && _b !== void 0 ? _b : []; + } + /** + * Currently appended byte total + */ + get totalBytes() { + var _a, _b; + return (_b = (_a = this.mediaElementQuery.sourceBufferEntityByType(this.type)) === null || _a === void 0 ? void 0 : _a.totalBytes) !== null && _b !== void 0 ? _b : 0; + } + /** + * Max number of bytes we estimate this SourceBuffer can hold + */ + get maxTotalBytes() { + var _a, _b; + const maxTotalBytes = (_b = (_a = this.mediaElementQuery.sourceBufferEntityByType(this.type)) === null || _a === void 0 ? void 0 : _a.maxTotalBytes) !== null && _b !== void 0 ? _b : Infinity; + return this.gotQuotaExceeded ? maxTotalBytes : Infinity; + } + _waitForUpdateEndOrError() { + if (this.sourceBuffer.updating) { + // Just in case we haven't fired updatestart yet + this.mediaElementStore.setSourceBufferUpdating(this.type); + } + // Ensures we have up to date buffered range values by using updating$ + return this.mediaElementQuery.sbUpdating$(this.type).pipe(filter((updating) => updating === false), withLatestFrom(this.mediaElementQuery.sbError$(this.type)), map(([_updating, error]) => { + if (error) { + throw new SourceBufferError(ErrorDetails.INTERNAL_EXCEPTION, false, 'Got error during sourceBuffer operation', ErrorResponses.InternalError, this.type); + } + return undefined; + }), take(1)); + } + } + + /** + * Observable MediaSOurce adapter that cleans up after itself on unsubscribe + * From src/mse-rxjs/mediasource-adapter.ts + */ + class MediaSourceAdapter extends Observable { + constructor(mediaElement, mediaElementStore, mediaElementQuery, mediaSource, logger) { + // Register for events. This updates the mediaElementStore and also sets up teardown + // when this object is unsubscribed from + super((subscriber) => { + const target = fromEventTarget(mediaSource); + const readyStateChange$ = merge(target.event('sourceopen'), target.event('sourceclose'), target.event('sourceended')).pipe(tap((event) => { + var _a; + // Vuze doesn't implement event + const ms = (_a = event === null || event === void 0 ? void 0 : event.target) !== null && _a !== void 0 ? _a : mediaSource; + const readyState = ms.readyState; + mediaElementStore.msReadyState = readyState; + })); + const sourceBuffers$ = this.sourceBuffers$.pipe(switchMap((sbTuple) => { + if (!sbTuple) + return EMPTY; + return merge(...sbTuple.filter((info) => info != null)); + })); + const sub = merge(readyStateChange$, sourceBuffers$) + .pipe(switchMap(() => EMPTY)) + .subscribe(subscriber); + const objectUrl = URL.createObjectURL(mediaSource); + mediaElement.src = objectUrl; + logger.info(`set MediaSource ${objectUrl}`); + mediaElementStore.setMediaSourceEntity(objectUrl, mediaSource.readyState); + return () => { + sub.unsubscribe(); + // Hand off clean up to teardownWorker to avoid race with clearMediaKeys + logger.info(`remove MediaSource ${objectUrl}`); + URL.revokeObjectURL(objectUrl); + if (mediaElement.src === objectUrl) { + mediaElement.removeAttribute('src'); + mediaElement.load(); + mediaElementStore.setMediaSourceEntity(null); + } + this.sourceBuffers$.next(null); + }; + }); + this.mediaElement = mediaElement; + this.mediaElementStore = mediaElementStore; + this.mediaElementQuery = mediaElementQuery; + this.mediaSource = mediaSource; + this.logger = logger; + this.sourceBuffers$ = new BehaviorSubject(null); + } + get readyState() { + return this.mediaSource.readyState; + } + set duration(value) { + this.mediaSource.duration = value; + } + get duration() { + return this.mediaSource.duration; + } + endOfStream(error) { + this.mediaSource.endOfStream(error); + } + createSourceBuffers(compatInfoTuple, config) { + const mediaSource = this.mediaSource; + applyTransaction(() => { + try { + const newSbs = [null, null]; + compatInfoTuple.forEach((compatInfo, type) => { + if (compatInfo) { + const { mimeType, mediaOptionId } = compatInfo; + let sb; + try { + sb = mediaSource.addSourceBuffer(mimeType); + this.logger.info(`[${SourceBufferNames[type]}]: sourceBuffer added ${mimeType}`); + } + catch (err) { + throw new CreateSourceBufferError(err.message, ErrorResponses.IncompatibleAsset, type, mediaOptionId); + } + newSbs[type] = new SourceBufferAdapter(this.mediaElementStore, this.mediaElementQuery, this.mediaElement, this.mediaSource, type, sb, compatInfo, this.logger, config); + } + }); + this.sourceBuffers$.next(newSbs); + } + catch (err) { + if (!(err instanceof HlsError)) { + throw new MseError(`error initializing sourcebuffers ${err.message} readyState=${mediaSource.readyState}`); + } + throw err; + } + }); + } + get needSourceBuffers() { + return this.sourceBuffers$.value == null || this.sourceBuffers$.value[0] == null; + } + get sourceBuffers() { + return this.sourceBuffers$.value; + } + getSourceBufferByType(type) { + const sbAdapterTuple = this.sourceBuffers$.value; + if (!sbAdapterTuple) { + return null; + } + return sbAdapterTuple[type]; + } + updateLiveSeekableRange(start, end) { + const mediaSource = this.mediaSource; + if ((mediaSource === null || mediaSource === void 0 ? void 0 : mediaSource.setLiveSeekableRange) && (mediaSource === null || mediaSource === void 0 ? void 0 : mediaSource.readyState) === 'open') { + this.logger.debug(`setLiveSeekableRange range called with ${start} to ${end}`); + mediaSource.setLiveSeekableRange(start, end); + } + } + clearLiveSeekableRange() { + const mediaSource = this.mediaSource; + if ((mediaSource === null || mediaSource === void 0 ? void 0 : mediaSource.clearLiveSeekableRange) && (mediaSource === null || mediaSource === void 0 ? void 0 : mediaSource.readyState) === 'open') { + this.logger.debug('clearLiveSeekableRange called'); + mediaSource.clearLiveSeekableRange(); + } + } + } + + const MIN_STALL_CHECK_MS = 100; // Minimum time for next check + function shouldCheckForStall(pos, desiredRate, ended, combinedBuffer, seeking) { + const isBuffered = combinedBuffer.some((r) => r.start <= pos && r.end > pos); + return !(desiredRate !== 1 || ended || combinedBuffer.length === 0 || (seeking && !isBuffered)); + } + /** + * monitors the media element for stalling in low and high buffer or on seek + * It will update media store with information about the stall if the position has not changed + * after the configured period. emits stall info if stall is detected, null if we're not stalled + */ + function stallMonitor(logger, meQuery, config) { + const { lowBufferThreshold, lowBufferWatchdogPeriod, highBufferWatchdogPeriod, seekWatchdogPeriod } = config; + const shouldCheckForStall$ = combineQueries([meQuery.desiredRate$, meQuery.ended$, meQuery.combinedBuffer$, meQuery.seekTo$]).pipe(map((state) => { + const [desiredRate, ended, combinedBuffer, seekTo] = state; + const currentTime = meQuery.currentTime; + const seeking = isFinite(seekTo === null || seekTo === void 0 ? void 0 : seekTo.pos); + return shouldCheckForStall(currentTime, desiredRate, ended, combinedBuffer, seeking); + }), distinctUntilChanged()); + const combinedBufferChanged$ = meQuery.combinedBuffer$.pipe(map(() => { + return meQuery.getCurrentWaterLevel(0) <= config.lowBufferThreshold || !meQuery.haveEnough ? StallType.LowBuffer : StallType.HighBuffer; + }), distinctUntilChanged()); + const gotStall$ = combineQueries([shouldCheckForStall$, meQuery.seekTo$, meQuery.gotPlaying$, combinedBufferChanged$]).pipe(switchMap((state) => { + const [shouldCheckForStall, seekTo, gotPlaying] = state; + logger.debug(`[stall] state=${stringifyWithPrecision({ shouldCheckForStall, seekTo, gotPlaying })}`); + if (!shouldCheckForStall) { + return of(null); + } + // some systems need longer wait period for seeks / playback resume, use backoff period + const backoffSec = 1; + const nudgeCount = meQuery.nudgeCount; + const seekPeriodSec = seekWatchdogPeriod + nudgeCount * backoffSec; + const seeking = isFinite(seekTo === null || seekTo === void 0 ? void 0 : seekTo.pos); + if (seeking || !gotPlaying) { + logger.debug(`[stall] start seek stall watchdog period=${seekPeriodSec}s`); + return startSeekStallWatchdog(meQuery, performance.now(), seekPeriodSec, lowBufferThreshold); + } + const highBufferPeriodSec = highBufferWatchdogPeriod + nudgeCount * backoffSec; + logger.debug(`[stall] start playing stall watchdog low period=${lowBufferWatchdogPeriod} high period=${highBufferPeriodSec}`); + return startPlayingStallWatchdog(meQuery, logger, lowBufferWatchdogPeriod, highBufferPeriodSec, lowBufferThreshold); + })); + const gotResume$ = gotStall$.pipe(filterNullOrUndefined(), withLatestFrom(meQuery.combinedBuffer$), switchMap(([stallInfo, combinedBuffer]) => { + logger.debug(`[stall] got stall ${stringifyWithPrecision(stallInfo)} buffered:${stringifyWithPrecision(combinedBuffer)}`); + return combineQueries([meQuery.seeking$, meQuery.paused$]); + }), switchMap(([seeking, paused]) => { + if (seeking || paused) { + return EMPTY; + } + return meQuery.timeupdate$.pipe(pairwise(), filter(([a, b]) => isFiniteNumber(a) && isFiniteNumber(b) && b > a), take(1)); + }), map(() => { + logger.info('[stall] resume from stall'); + return null; + })); + return merge(gotStall$, gotResume$); + } + // start seek watchdog timer. Assumed that at start, seeking || !playing + // Fire if time from (seeking -> playing) or (seeked -> playing) >= seekWatchdogPeriod + function startSeekStallWatchdog(meQuery, tstalled, seekWatchdogPeriod, lowBufferThreshold) { + return timer(seekWatchdogPeriod * 1000).pipe(map(() => { + const currentTime = meQuery.currentTime; + const bufferInfo = meQuery.getCombinedBufferInfo(currentTime, 0); + return makeStallInfo(StallType.Seek, currentTime, tstalled, bufferInfo, lowBufferThreshold, meQuery.haveEnough); + })); + } + // start playing watchdog timer: when now - tlastCurrentTime > low or high buffer watchdog period + function startPlayingStallWatchdog(meQuery, logger, lowBufferWatchdogPeriod, highBufferWatchdogPeriod, lowBufferThreshold) { + // Current position hasn't changed for some period of time + return merge(of(meQuery.currentTime), meQuery.timeupdate$).pipe(switchMap((lastCurrentTime) => { + const tstalled = performance.now(); + const bufferInfo = meQuery.getCombinedBufferInfo(lastCurrentTime, 0); + let type; + let watchdogPeriod; // How long from reference point to schedule the check + const bufferLenSec = bufferInfo.len; + // prettier-ignore + if ((bufferLenSec <= lowBufferThreshold) || !meQuery.haveEnough) { + watchdogPeriod = lowBufferWatchdogPeriod; + type = StallType.LowBuffer; + } + else { + watchdogPeriod = highBufferWatchdogPeriod; + type = StallType.HighBuffer; + } + const stallCheckMs = Math.max(MIN_STALL_CHECK_MS, watchdogPeriod * 1000); + return timer(stallCheckMs).pipe(map(() => { + // Check once to see if playback has moved ahead before making stallInfo Object + // On Roku 'timeupdate' event (aka meQuery.timeupdate$ triggers) are inconsistent. + if (lastCurrentTime < meQuery.currentTime) { + return null; + } + return makeStallInfo(type, lastCurrentTime, tstalled, bufferInfo, lowBufferThreshold, meQuery.haveEnough); + })); + })); + } + /** + * Check if we've changed positions since we armed the timers + */ + function makeStallInfo(type, currentTime, tstalled, bufferInfo, lowBufferThreshold, haveEnough) { + const now = performance.now(); + const stallDurationMs = now - tstalled; + const bufferLenSec = bufferInfo.len; + // prettier-ignore + const isLowBufferStall = (bufferLenSec <= lowBufferThreshold) || !haveEnough; + return { type, isLowBufferStall, tstalled, stallDurationMs, currentTime }; + } + + class MediaSink extends Observable { + constructor(mediaElement, mediaElementStore, config, hlsGapless, logger, teardownWG$, rtcService) { + super((subscriber) => { + this.logger.info('subscribe MediaSink'); + const config = this.config; + const sessionId = mediaElementStore.startMediaSession(mediaElement, config.maxBufferLength, config.almostDryBufferSec, config.defaultTargetDuration); + const mediaElementEvents$ = hookMediaElementsEvents(mediaElement, mediaElementStore, this._mediaQuery, this, this.hlsGapless, config, this.logger, this.rtcService); + const mediaSource$ = this.mediaSource$.pipe(switchMap((ms) => ms || EMPTY)); + const seeks$ = this._mediaQuery.seekTo$.pipe(seekEpic(mediaElement, this._mediaQuery, this, this.config, this.logger)); + const rateChange$ = this._mediaQuery.desiredRate$.pipe(rateChangeEpic(mediaElement, this._mediaQuery, this)); + this.liveSeekableWindow = { start: NaN, end: NaN }; + // setup media functions overrides + this.mediaFunctions = this.mediaFunctions || new MediaFunctions(this, mediaElement, config, this.logger); + this.mediaFunctions.install(); + const stallHandling$ = merge(stallMonitor(this.logger, this._mediaQuery, this.config).pipe(tap((stallInfo) => { + mediaElementStore.setStallInfo(stallInfo); + })), this.mediaQuery.stallInfo$.pipe(stallEpic(this, mediaElementStore, this.config, this.logger))); + const bufferMonitor$ = bufferMonitorEpic(this.mediaQuery, mediaElementStore, config.maxBufferHole); + merge(mediaElementEvents$, mediaSource$, seeks$, rateChange$, stallHandling$, bufferMonitor$) + .pipe(switchMapTo(EMPTY), finalize$1(() => { + this.logger.info('finalize MediaSink'); + mediaElementStore.remove(sessionId); + this.mediaFunctions.uninstall(); + this.mediaFunctions = undefined; + })) + .subscribe(subscriber); // Propagates any errors up + // extra teardown ? + }); + this.mediaElement = mediaElement; + this.mediaElementStore = mediaElementStore; + this.config = config; + this.hlsGapless = hlsGapless; + this.logger = logger; + this.teardownWG$ = teardownWG$; + this.rtcService = rtcService; + this.mediaSource$ = new BehaviorSubject(null); + // We use a Mutex here because only one setMediaKeys() can run at one time, + // and multiple setMediaKeys() operations can be queued for execution from + // different threads. Since it's the only type of operation needs + // synchronization in the MediaSink component, using a Mutex makes more sense + // than converting to command queue pattern. + this.mediaKeysMutex = new Mutex(); + this._mediaQuery = new MediaElementQuery(mediaElement, mediaElementStore); + this.logger = logger.child({ name: 'mse' }); + // Muze/Vuze: Need to create id3 text track before MEDIA_ATTACHED + this.createId3Track(mediaElement); + this.mediaFunctions = new MediaFunctions(this, mediaElement, config, this.logger); + } + get mediaSourceAdapter() { + return this.mediaSource$.value; + } + get sourceBuffers() { + var _a, _b; + return (_b = (_a = this.mediaSourceAdapter) === null || _a === void 0 ? void 0 : _a.sourceBuffers) !== null && _b !== void 0 ? _b : []; + } + get needSourceBuffers() { + return this.sourceBuffers[0] ? false : true; + } + get mediaQuery() { + return this._mediaQuery; + } + sourceBuffersBufferedRangeByType(type) { + var _a, _b; + const sb = (_b = (_a = this.mediaSourceAdapter) === null || _a === void 0 ? void 0 : _a.sourceBuffers) === null || _b === void 0 ? void 0 : _b[type]; + if (sb) { + return BufferHelper.timeRangesToBufferedRange(sb.sourceBuffer.buffered); + } + else { + return null; + } + } + createId3Track(mediaElement) { + this.logger.info('create id3 texttrack'); + this.id3Track = mediaElement.addTextTrack('metadata', 'id3'); + this.id3Track.mode = 'hidden'; + } + /** + * Check if mediaElementStore buffer values are in-sync with real SourceBuffer + * especially when trying to seek. + */ + checkForInconsistentStoreBufferRangesAndUpdate() { + var _a, _b, _c, _d; + const mediaElementTimeRanges = BufferHelper.timeRangesToBufferedRange(this.mediaElement.buffered); + const realVariantBufferedRanges = this.sourceBuffersBufferedRangeByType(SourceBufferType.Variant); + const realAltAudioBufferedRanges = this.sourceBuffersBufferedRangeByType(SourceBufferType.AltAudio); + const storeVariantBufferedRanges = (_b = (_a = this.mediaQuery.sourceBufferEntityByType(SourceBufferType.Variant)) === null || _a === void 0 ? void 0 : _a.bufferedRanges) !== null && _b !== void 0 ? _b : null; + const storeAltAudioBufferedRanges = (_d = (_c = this.mediaQuery.sourceBufferEntityByType(SourceBufferType.AltAudio)) === null || _c === void 0 ? void 0 : _c.bufferedRanges) !== null && _d !== void 0 ? _d : null; + // If there is discrepency with any of sourceBufferRanges then update store values + if (this.shouldUpdateStoreValues(realVariantBufferedRanges, storeVariantBufferedRanges)) { + this.logger.warn(`[${SourceBufferNames[SourceBufferType.Variant]}] SourceBuffer's loaded bufferedRanges ${JSON.stringify(realVariantBufferedRanges)} & mediaElementStore's bufferedRanges ${JSON.stringify(storeVariantBufferedRanges)} are out of sync!`); + this.updateMediaElementStoreBufferedRanges(mediaElementTimeRanges, SourceBufferType.Variant); + } + if (this.shouldUpdateStoreValues(realAltAudioBufferedRanges, storeAltAudioBufferedRanges)) { + this.logger.warn(`[${SourceBufferNames[SourceBufferType.AltAudio]}] SourceBuffer's loaded bufferedRanges ${JSON.stringify(realAltAudioBufferedRanges)} & mediaElementStore's bufferedRanges ${JSON.stringify(storeAltAudioBufferedRanges)} are out of sync!`); + this.updateMediaElementStoreBufferedRanges(mediaElementTimeRanges, SourceBufferType.AltAudio); + } + } + shouldUpdateStoreValues(sbLoadedTimeRanges, storeBufferedRanges) { + if (sbLoadedTimeRanges == null && storeBufferedRanges == null) { + return false; + } + // Unequal lengths then we need updating + if ((sbLoadedTimeRanges === null || sbLoadedTimeRanges === void 0 ? void 0 : sbLoadedTimeRanges.length) != (storeBufferedRanges === null || storeBufferedRanges === void 0 ? void 0 : storeBufferedRanges.length)) { + return true; + } + // If there is discrepency with any of the loaded Ranges then update store values + return !!sbLoadedTimeRanges.find((sbCurrentLoadedTimeRange) => { + const binarySearchcompareFunction = (storeCurrentInterval) => { + if (sbCurrentLoadedTimeRange.start >= storeCurrentInterval.start && sbCurrentLoadedTimeRange.end <= storeCurrentInterval.end) { + return 0; + } + else if (sbCurrentLoadedTimeRange.end < storeCurrentInterval.start) { + return -1; + } + else { + return 1; + } + }; + // Binary search as SourceBuffer.buffered is normalized TimeRanges object. + const matchingStoreBufferedRange = BinarySearch.search(storeBufferedRanges, binarySearchcompareFunction); + // If no match we need updating + if (matchingStoreBufferedRange == null) { + return true; + } + if (matchingStoreBufferedRange.start != sbCurrentLoadedTimeRange.start || matchingStoreBufferedRange.end != sbCurrentLoadedTimeRange.end) { + return true; + } + }); + } + /** + * Update mediaElementStore with latest from SourceBuffers. + */ + updateMediaElementStoreBufferedRanges(mediaElementBuffered, type) { + const sb = this.sourceBuffersBufferedRangeByType(type); + if (sb && !this.mediaQuery.sbUpdating(type)) { + this.logger.info(`[${SourceBufferNames[type]}] Updating buffer status`); + this.mediaElementStore.setBufferedRangesUpdated(type, sb, mediaElementBuffered, false, this.config); + } + } + destroyMediaSource() { + this.mediaSource$.next(null); + } + // Can stub over this for testing if needed + makeMediaSource() { + return new MediaSource(); + } + openMediaSource(mediaSource) { + applyTransaction(() => { + if (mediaSource) { + const mediaAdapter = new MediaSourceAdapter(this.mediaElement, this.mediaElementStore, this.mediaQuery, mediaSource, this.logger); + this.mediaSource$.next(mediaAdapter); + } + else { + this.mediaSource$.next(null); + } + }); + } + createSourceBuffers(compatInfoTuple) { + this.logger.info(`createSourceBuffers ${JSON.stringify(compatInfoTuple.map((c) => c === null || c === void 0 ? void 0 : c.mimeType))}`); + const msAdapter = this.mediaSource$.value; + if (!msAdapter) { + throw new Error('createSourceBuffers empty mediaSource'); + } + msAdapter.createSourceBuffers(compatInfoTuple, this.config); + } + _waitForMediaSourceOpen(appendData) { + const msObjectUrl = this.mediaQuery.mediaSourceEntity.objectUrl; // msAdapter at the start of wait + return combineQueries([this.mediaQuery.msReadyState$, this.mediaQuery.msObjectUrl$]).pipe(switchMap(([state, msUrl]) => { + if (msUrl !== msObjectUrl) { + this.logger.info('media source changed while waiting'); + return of(null); + } + if (state === 'open' || state === 'ended') { + // Can append again even if ended + return of(appendData); + } + return EMPTY; + })); + } + get appendOrder() { + return this.mediaQuery.isIframeRate ? [SourceBufferType.Variant, SourceBufferType.AltAudio] : [SourceBufferType.AltAudio, SourceBufferType.Variant]; + } + clearFlush(appendData) { + appendData.forEach((sbData) => { + if (sbData) { + // hls will not reset the MediaOptionSwitchContext if flushBeforeAppend is null/undefined. + sbData.dataSeg.flushBeforeAppend = { start: 0, end: 0 }; + } + }); + } + getSwitchPosition(appendData) { + return appendData.reduce((prevSwitchPos, next) => { + const switchPos = next ? next.dataSeg.switchPosition : undefined; + if (isFiniteNumber(switchPos)) { + return isFiniteNumber(prevSwitchPos) ? Math.min(prevSwitchPos, switchPos) : switchPos; + } + else { + return prevSwitchPos; + } + }, undefined); + } + checkForReplay() { + const media = this.mediaElement; + // rdar://80790960 ([HLS JS 2.1 beta] Marcom - Retry playback fails). Pulled from 2.0 stream-controller.ts. + if (media.paused && !media.seeking && media.duration && media.currentTime && media.currentTime >= media.duration - this.config.maxTotalDurationTolerance) { + this.seekTo = 0; // auto-restart if scrubber is at end of video. media.ended may be reset upon seek or append. + } + } + /** + * Reset media source if needed and wait for 'open' + * @returns an observable that emits AppendDataTuple if we should append, null if we something did a reset + */ + resetMediaSourceIfNeeded(appendData) { + const { mediaQuery } = this; + const { sourceBufferEntities } = mediaQuery; + const { expectedSbCount } = mediaQuery.getActive(); + if (!sourceBufferEntities || this.needSourceBuffers) { + this.logger.info('need source buffers #disco'); + return this._waitForMediaSourceOpen(appendData); + } + const compatInfo = isCompatibleWithSourceBuffers(appendData, sourceBufferEntities, expectedSbCount, this.config.maxBufferHole, this.logger); + if (compatInfo.compatible) { + this.logger.info('got compatible source buffers #disco'); + this.logger.qe({ critical: true, name: 'disco', data: { type: 'compatible' } }); + return this._waitForMediaSourceOpen(appendData); + } + let boundary = compatInfo.boundary; + const boundaryAllowance = compatInfo.allowance; + const requestedSwitchPos = this.getSwitchPosition(appendData); + if (isFiniteNumber(requestedSwitchPos)) { + this.logger.info(`override boundary with switch position=${boundary.toFixed(3)}->${requestedSwitchPos}`); + boundary = requestedSwitchPos; + } + if (!isFiniteNumber(boundary)) { + this.logger.warn('not enough info #disco'); + return of(null); + } + this.logger.info(`start wait pos=${mediaQuery.currentTime.toFixed(3)} boundary=${boundary.toFixed(3)} allowance=${boundaryAllowance.toFixed(3)} #disco`); + this.logger.qe({ critical: true, name: 'disco', data: { type: 'incompatible' } }); + const hitDiscoBoundary$ = race( + // Play or seek across boundary (0 tolerance) + waitFor(merge(of(mediaQuery.currentTime), mediaQuery.timeupdate$), (pos) => pos >= boundary), + // Stalled playback and we're close enough + waitFor(mediaQuery.stallInfo$.pipe(map((stallInfo) => { var _a; return (_a = stallInfo === null || stallInfo === void 0 ? void 0 : stallInfo.currentTime) !== null && _a !== void 0 ? _a : NaN; })), (pos) => pos >= boundary - boundaryAllowance - this.config.discontinuitySeekTolerance)); + this.mediaElementStore.waitingForDisco = true; + return hitDiscoBoundary$.pipe(mapTo(boundary), switchMap((boundary) => { + const startResetTime = performance.now(); + const currentTime = mediaQuery.currentTime; + this.logger.info(`end wait pos=${currentTime.toFixed(3)} boundary=${boundary.toFixed(3)} #disco`); + this.logger.qe({ critical: true, name: 'disco', data: { type: 'resetStart' } }); + const duration = this.msDuration; + this.resetMediaSource(Math.max(currentTime, boundary), compatInfo.discoSeqNum); + return this._waitForMediaSourceOpen(appendData).pipe(tap(() => { + const resetDuration = performance.now() - startResetTime; + this.logger.qe({ critical: true, name: 'disco', data: { type: 'resetComplete', resetDuration } }); + this.msDuration = duration; + })); + }), finalize$1(() => { + this.mediaElementStore.waitingForDisco = false; + })); + } + /** + * @brief External hard media reset + * @param position The position to seek to on reset. NaN means use current seekTo or currentTime. seekTo has precedence > currentTime + */ + resetMediaSource(position = NaN, discoSeqNum) { + var _a, _b, _c; + this.logger.info(`resetMediaSource ${position} ${discoSeqNum}`); + if (!isFiniteNumber(position)) { + position = (_b = (_a = this.mediaQuery.seekTo) === null || _a === void 0 ? void 0 : _a.pos) !== null && _b !== void 0 ? _b : this.mediaQuery.currentTime; + } + if (!isFiniteNumber(discoSeqNum)) { + discoSeqNum = (_c = this.mediaQuery.seekTo) === null || _c === void 0 ? void 0 : _c.discoSeqNum; + } + if (this.sourceBuffers.length > 0) { + this.openMediaSource(this.makeMediaSource()); + this.logger.info(`resetMediaSource seek=${position} cc=${discoSeqNum}`); + this.setSeekToWithDiscontinuity(position, discoSeqNum); // always update after reset, use given cc + } + } + setExpectedSbCount(sbCount) { + this.mediaElementStore.expectedSbCount = sbCount; + } + appendInitSegments(appendData, appendErrorPolicy) { + const { mediaQuery, mediaElementStore, sourceBuffers, logger } = this; + const { sourceBufferEntities } = mediaQuery; + if (!sourceBuffers) { + throw new Error('appendInitSegments: null sourceBuffers'); + } + if (!sourceBufferEntities) { + throw new Error('appendInitSegments: null sourceBufferEntities'); + } + const initAppendOps$ = this.appendOrder + .map((type) => { + const sbDataTuple = appendData[type]; + if (!sbDataTuple) + return; + const sb = sourceBuffers[type]; + const sbAppendData = appendData[type]; + const currentSbEntity = sourceBufferEntities[type]; + const { initSeg } = sbAppendData; + if (!currentSbEntity) { + throw new Error(`appendInitSegments: sb[${SourceBufferNames[type]}] null currentSbEntity`); + } + if (!sb) { + throw new Error(`appendInitSegments: sb[${SourceBufferNames[type]}] null source buffer`); + } + const curInitInfo = currentSbEntity.initSegmentInfo; + const newInitInfo = cacheEntityToInfoEntity(initSeg); + if (initSegmentEquals(newInitInfo, curInitInfo)) { + logger.debug(`[${MediaOptionNames[type]}] skip init segment append ${JSON.stringify(newInitInfo)}`); + return of(null); // noop + } + const mediaOptionType = sourceBufferTypeToMediaOptionType(type); + return sb.appendBuffer(initSeg.data, initSeg).pipe(tap((am) => { + logger.info(`[${MediaOptionNames[type]}] append init segment ${JSON.stringify(newInitInfo)} ${am === null || am === void 0 ? void 0 : am.startAppend}/${am === null || am === void 0 ? void 0 : am.endAppend}/${am === null || am === void 0 ? void 0 : am.bytesAppend}`); + mediaElementStore.setInitSegmentEntity(type, newInitInfo); + }), appendErrorPolicy(sb, mediaOptionType, initSeg.mediaOptionId, this.config, this.mediaQuery)); + }) + .filter((initAppendOp) => Boolean(initAppendOp)); + if (initAppendOps$.length === 0) { + return of(null); // noop + } + return forkJoin(initAppendOps$); + } + appendDataSegments(appendData, appendErrorPolicy) { + const dataAppendOps$ = this.appendOrder + .map((type) => { + const sbDataTuple = appendData[type]; + const { mediaQuery, sourceBuffers, logger } = this; + const { sourceBufferEntities } = mediaQuery; + if (!sourceBuffers) { + throw new Error('appendDataSegments: null sourceBuffers'); + } + if (!sourceBufferEntities) { + throw new Error('appendDataSegments: null sourceBufferEntities'); + } + if (!sbDataTuple) + return null; + const sb = sourceBuffers[type]; + const sbAppendData = appendData[type]; + const currentSbEntity = sourceBufferEntities[type]; + if (!currentSbEntity) { + throw new Error('appendDataSegments: null currentSbEntity'); + } + const currentInitSegmentInfo = currentSbEntity.initSegmentInfo; + const { dataSeg } = sbAppendData; + if (!currentInitSegmentInfo) { + throw new Error(`appendDataSegments: sb[${SourceBufferNames[type]}] null currentInitSegmentInfo`); + } + if (!currentSbEntity) { + throw new Error(`appendDataSegments: sb[${SourceBufferNames[type]}] null currentSbEntity`); + } + if (!sb) { + throw new Error(`appendDataSegments: sb[${SourceBufferNames[type]}] null source buffer`); + } + const timestampOffset = sb.timestampOffset; + const startPTS = convertTimestampToSeconds(dataSeg.startPts) + timestampOffset; + const endPTS = convertTimestampToSeconds(dataSeg.endPts) + timestampOffset; + const firstKeyframePts = dataSeg.firstKeyframePts ? convertTimestampToSeconds(dataSeg.firstKeyframePts) + timestampOffset : undefined; + const seginfo = { + startPTS, + endPTS, + firstKeyframePts, + bytes: dataSeg.data2 ? dataSeg.data1.byteLength + dataSeg.data2.byteLength : dataSeg.data1.byteLength, + frag: { + itemId: dataSeg.itemId, + mediaOptionId: dataSeg.mediaOptionId, + mediaSeqNum: dataSeg.mediaSeqNum, + discoSeqNum: dataSeg.discoSeqNum, + keyTagInfo: dataSeg.keyTagInfo, + isLastFragment: dataSeg.isLastFragment, + iframe: dataSeg.iframe, + framesWithoutIDR: dataSeg.framesWithoutIDR, + dropped: dataSeg.dropped, + }, + }; + const mediaOptionType = sourceBufferTypeToMediaOptionType(type); + let optionalFlush = VOID; + const flushRange = sbDataTuple.dataSeg.flushBeforeAppend; + if (flushRange && flushRange.start !== flushRange.end) { + logger.info(`sb[${SourceBufferNames[type]}] flush [${flushRange.start},${flushRange.end}] before append`); + optionalFlush = this.flushData(type, flushRange.start, flushRange.end); + } + return optionalFlush.pipe(switchMap(() => { + const source = of(dataSeg.data1, dataSeg.data2).pipe(filterNullOrUndefined()); + return source.pipe(concatMap((data) => { + logger.info(`[${MediaOptionNames[type]}] appending timestampOffset:${currentSbEntity.timestampOffset} segment:${stringifyWithPrecision({ + startPTS, + endPTS, + frag: fragPrint(dataSeg), + key: redactUrl(dataSeg.keyTagInfo.uri), + })}`); + return sb.appendBuffer(data, seginfo).pipe(appendErrorPolicy(sb, mediaOptionType, dataSeg.mediaOptionId, this.config, this.mediaQuery)); + })); + }), tap(() => { + const bufferedRanges = mediaQuery.getBufferedRangeByType(type); + logger.info(`[${MediaOptionNames[type]}] appended pos:${mediaQuery.currentTime.toFixed(3)} buffered:${stringifyWithPrecision(bufferedRanges)}`); + })); + }) + .filter((dataAppendOp) => Boolean(dataAppendOp)); + if (dataAppendOps$.length === 0) { + return of(null); // noop + } + return forkJoin(dataAppendOps$); + } + setStoreSbTimeoffsets(appendDataTuple) { + const { mediaElementStore, sourceBuffers } = this; + sourceBuffers.forEach((sb, type) => { + if (!sb || !appendDataTuple[type]) { + return; + } + const { offsetTimestamp, dataSeg } = appendDataTuple[type]; + const startPts = convertTimestampToSeconds(dataSeg.startPts); + let timestampOffset = convertTimestampToSeconds(offsetTimestamp) * -1; + if (sb.updateMp3Timestamps) { + // Adjusting `SourceBuffer.timestampOffset` (desired point in the timeline where the next frames should be appended) + // in Chrome browser when we detect MPEG audio container and time delta between level PTS and `SourceBuffer.timestampOffset` + // is greater than 100ms (this is enough to handle seek for VOD or level change for LIVE videos). At the time of change we issue + // More info here: https://github.com/video-dev/hls.js/issues/332#issuecomment-257986486 + const delta = Math.abs(sb.timestampOffset - startPts); + // adjust timestamp offset if time delta is greater than 100ms + if (delta > 0.1) { + timestampOffset = startPts + timestampOffset; + } + } + if (sb.timestampOffset !== timestampOffset) { + this.logger.info(`${MediaOptionNames[type]} timestampOffset=${timestampOffset}`); + sb.timestampOffset = timestampOffset; + mediaElementStore.setTimestampOffset(type, sb.timestampOffset); + } + }); + } + adjustJaggedStart(appendData) { + var _a; + const { mediaQuery, logger } = this; + const { sourceBufferEntities, currentTime, seekTo } = mediaQuery; + const appendEndTime = appendData.reduce((end, append) => { + return (append === null || append === void 0 ? void 0 : append.dataSeg.endPts) ? Math.min(diffSeconds(append.dataSeg.endPts, append.offsetTimestamp), end) : end; + }, Number.POSITIVE_INFINITY); + if (!sourceBufferEntities) { + throw new Error('appendSourceBufferData null currentSbEntity'); + } + const position = (seekTo === null || seekTo === void 0 ? void 0 : seekTo.pos) || currentTime; + let seekToPos = NaN; + sourceBufferEntities.forEach((sbEntity, type) => { + if (!sbEntity) + return; + const bufferInfo = BufferHelper.getBufferedInfo(sbEntity.bufferedRanges, position, 0); + if (bufferInfo.len === 0) { + const { nextStart } = bufferInfo; + const tolerance = isFiniteNumber(this.config.jaggedSeekTolerance) ? this.config.jaggedSeekTolerance : 0; + logger.warn(`sb[${SourceBufferNames[type]}] jagged start: ${nextStart} appendEndTime=${appendEndTime} current=${seekToPos} tolerance=${tolerance}`); + if (isFiniteNumber(nextStart) && (!isFiniteNumber(seekToPos) || nextStart - seekToPos > tolerance)) { + seekToPos = nextStart; + } + } + }); + if (isFiniteNumber(seekToPos) && appendEndTime > seekToPos) { + // when appendEndTime < seekToPos, no need to initiate jagged seek. + // wait for subsequent append to fill the buffer. + logger.warn(`[seek] jagged start, adjusting currentTime:${currentTime.toFixed(3)} seekTo=${(_a = seekTo === null || seekTo === void 0 ? void 0 : seekTo.pos) === null || _a === void 0 ? void 0 : _a.toFixed(3)}->${seekToPos} appendEndTime=${appendEndTime}`); + this.seekTo = seekToPos; + } + } + addCues(texttrackIdx, cues) { + const textTrack = this.mediaElement.textTracks[texttrackIdx]; + if (textTrack) { + cues.forEach((cue) => { + textTrack.addCue(cue); + }); + } + } + _flushInternal(sb, start, end) { + return defer(() => { + this.logger.info(`[${SourceBufferNames[sb.type]}] remove(${start},${end}) start`); + return sb.remove(start, end); + }).pipe(tap(() => { + this.logger.info(`[${SourceBufferNames[sb.type]}] remove(${start},${end}) end`); + })); + } + // Flush all source buffers + flushAll(start, end, force = false) { + if (this.sourceBuffers.length === 0) { + return VOID; + } + return forkJoin(this.sourceBuffers.map((sb, type) => (sb ? this.flushData(type, start, end, force) : VOID))).pipe(mapTo(undefined)); + } + flushData(sbType, start, end, force = false) { + var _a; + const { mediaQuery, logger } = this; + this.logger.info(`flushData ${start.toFixed(3)} ${end.toFixed(3)} updating=${(_a = mediaQuery.sourceBufferEntities) === null || _a === void 0 ? void 0 : _a.some((sb) => sb === null || sb === void 0 ? void 0 : sb.updating)}`); + return waitFor(mediaQuery.updating$, (updating) => updating === false).pipe(switchMap(() => { + const { sourceBufferEntities } = mediaQuery; + const sbEntity = sourceBufferEntities[sbType]; + if (sbEntity === null || sbEntity === void 0 ? void 0 : sbEntity.updating) { + this.logger.warn(`trying to flush while updating ${sbType}`); + } + const sb = this.sourceBuffers[sbType]; + if (!sb) { + return VOID; + } + let bufStart; + let bufEnd; + let flushes = VOID; + const flushSingleRange = navigator.userAgent.toLowerCase().indexOf('firefox') === -1; + this.flushing = true; + if (flushSingleRange) { + return this._flushInternal(sb, start, end); + } + // workaround firefox not able to properly flush multiple buffered range. + for (let i = 0; i < sb.buffered.length; i++) { + let flushStart; + let flushEnd; + bufStart = sb.buffered.start(i); + bufEnd = sb.buffered.end(i); + if (end === Infinity) { + flushStart = start; + flushEnd = end; + } + else { + flushStart = Math.max(bufStart, start); + flushEnd = Math.min(bufEnd, end); + } + /* + sometimes sourcebuffer.remove() does not flush + the exact expected time range. + to avoid rounding issues/infinite loop, + only flush buffer range of length greater than 500ms. + */ + if (Math.min(flushEnd, bufEnd) > flushStart && (force || Math.min(flushEnd, bufEnd) - flushStart > 0.5)) { + flushes = flushes.pipe(switchMapTo(this._flushInternal(sb, flushStart, flushEnd))); + } + else { + logger.warn(`ignoring sb[${SourceBufferNames[sbType]}] flush ${flushStart},${flushEnd}`); + } + } + return flushes; + }), finalize$1(() => { + // clear flushing + this.flushing = false; + })); + } + static convertInitSegToCompatInfo(initSeg) { + return { + mimeType: initSeg.mimeType, + audioCodec: initSeg.initParsedData.audioCodec, + videoCodec: initSeg.initParsedData.videoCodec, + startPTSSec: undefined, + endPTSSec: undefined, + discoSeqNum: initSeg.discoSeqNum, + mediaOptionId: initSeg.mediaOptionId, + }; + } + static combineAppendDataInfoWithCompatInfo(appendData, oldCompatInfoTuple, highestVideoCodec, logger = null) { + const newCompatInfo = [...oldCompatInfoTuple]; + appendData.forEach((d, type) => ((d === null || d === void 0 ? void 0 : d.initSeg) ? (newCompatInfo[type] = MediaSink.convertInitSegToCompatInfo(d.initSeg)) : null)); + const currentVideoCodecString = newCompatInfo[SourceBufferType.Variant].videoCodec; + const currentVideoCodecFamily = getVideoCodecFamily(currentVideoCodecString); + // Replace to highest video codec string. + if (highestVideoCodec && highestVideoCodec.has(currentVideoCodecFamily)) { + // if configured to use highestCodec + const highestVideoCodecString = highestVideoCodec.get(currentVideoCodecFamily); + logger === null || logger === void 0 ? void 0 : logger.info(`override with highest video codec ${highestVideoCodecString}`); + newCompatInfo[SourceBufferType.Variant].mimeType = newCompatInfo[SourceBufferType.Variant].mimeType.replace(currentVideoCodecString, highestVideoCodecString); + newCompatInfo[SourceBufferType.Variant].videoCodec = highestVideoCodecString; + logger === null || logger === void 0 ? void 0 : logger.info(`compatibility info overrridden from ${currentVideoCodecString} to ${newCompatInfo[SourceBufferType.Variant].videoCodec}/${newCompatInfo[SourceBufferType.Variant].mimeType}`); + } + return newCompatInfo; + } + convertSourceBufferEntitiesToCompatInfo(mediaQuery) { + const sbEntities = mediaQuery.sourceBufferEntities; + const compatInfos = [null, null]; + sbEntities.forEach((sbEntity, type) => { + var _a; + if (!sbEntity) + return; + compatInfos[type] = { + mimeType: sbEntity.mimeType, + audioCodec: sbEntity.audioCodec, + videoCodec: sbEntity.videoCodec, + startPTSSec: undefined, + endPTSSec: undefined, + discoSeqNum: undefined, + // right after resetMediaSource, sbEntity may not have initSegmentInfo yet. + // mediaOptionId to be filled in by combineAppendDataInfoWithCompatInfo before createSourceBuffers. + mediaOptionId: (_a = sbEntity.initSegmentInfo) === null || _a === void 0 ? void 0 : _a.mediaOptionId, + }; + }); + return compatInfos; + } + appendData(appendData, appendErrorPolicy, highestVideoCodec) { + const { mediaQuery, logger } = this; + const oldCompatInfoTuple = this.convertSourceBufferEntitiesToCompatInfo(mediaQuery); + if (appendData.every((data) => data == null)) { + return of([]); // noop + } + return this.resetMediaSourceIfNeeded(appendData).pipe(switchMap((appendData) => { + if (!appendData) { + return of(null); // Got an unexpected reset, stop what we're doing + } + // Don't want updating to trigger switchMap above which will abort the append below. + return mediaQuery.updating$.pipe(filter((updating) => updating === false), take(1), mapTo(appendData)); + }), switchMap((appendData) => { + if (!appendData) { + return of([]); + } + let bufferCreationStart = NaN; + let bufferCreationEnd = NaN; + if (this.needSourceBuffers) { + bufferCreationStart = performance.now(); + const newCompatInfoTuple = MediaSink.combineAppendDataInfoWithCompatInfo(appendData, oldCompatInfoTuple, highestVideoCodec, logger); + this.createSourceBuffers(newCompatInfoTuple); + bufferCreationEnd = performance.now(); + logger.info(`[Buffers] source buffers created start/end: ${bufferCreationStart}/${bufferCreationEnd}`); + this.clearFlush(appendData); + } + appendData.forEach((data) => { + const mediaData = data === null || data === void 0 ? void 0 : data.dataSeg; + if ((mediaData === null || mediaData === void 0 ? void 0 : mediaData.cues) && isFiniteNumber(mediaData === null || mediaData === void 0 ? void 0 : mediaData.texttrackIdx)) { + this.addCues(mediaData.texttrackIdx, mediaData.cues); + } + }); + this.setStoreSbTimeoffsets(appendData); + return concat(defer(() => this.appendInitSegments(appendData, appendErrorPolicy)), defer(() => this.appendDataSegments(appendData, appendErrorPolicy))).pipe(reduce((acc, value) => { + acc.push(value); + return acc; + }, new Array()), map(([initSegmentMetrics, dataSegmentMetrics]) => { + const metrics = [null, null]; + [SourceBufferType.Variant, SourceBufferType.AltAudio].forEach((type) => { + if ((initSegmentMetrics === null || initSegmentMetrics === void 0 ? void 0 : initSegmentMetrics[type]) == null) + return; + const metric = { + fragmentType: sourceBufferTypeToMediaOptionType(type), + bufferCreationStart, + bufferCreationEnd, + startInitAppend: initSegmentMetrics[type].startAppend, + endInitAppend: initSegmentMetrics[type].endAppend, + initBytesAppend: initSegmentMetrics[type].bytesAppend, + startDataAppend: dataSegmentMetrics[type].startAppend, + endDataAppend: dataSegmentMetrics[type].endAppend, + dataBytesAppend: dataSegmentMetrics[type].bytesAppend, + }; + metrics[type] = metric; + }); + return metrics; + }), tap((_) => { + this.adjustJaggedStart(appendData); + })); + }), take(1)); + } + endStream() { + // Will throw error if msReadyState !== open or any source buffer is updating + try { + this.mediaSourceAdapter.endOfStream(); + } + catch (err) { + this.logger.warn(`endOfStream failed: ${err.message}`); + } + } + /** + * Set CDM on media element + * @param mediaKeys The CDM to attach, or null + */ + setMediaKeys(mediaKeys) { + const { logger } = this; + return this.teardownWG$.wrap(this.mediaKeysMutex + .lock(() => from(this.mediaElement.setMediaKeys(mediaKeys))) + .pipe(tap(() => { + logger.info(`[Keys] setMediaKeys(${mediaKeys}) success`); + }), retryWhen((errors) => errors.pipe(mergeMap((err, i) => { + logger.info(`[Keys] setMediaKeys(${mediaKeys} fail ${err.message})`); + if (i < 3) { + logger.info(`[Keys] Retry setMediaKeys delay=${i * 100}`); + return timer(i * 100); + } + throw err; + }))))); + } + clearMediaKeys() { + return defer(() => { + if (!this.mediaElement) { + return VOID; + } + const isChrome = navigator.userAgent.toLowerCase().indexOf('chrome') > -1; + const src = this.mediaElement.src; + if (isChrome) { + //https://github.com/Dash-Industry-Forum/dash.js/issues/623 + this.mediaElement.src = ''; + } + return this.setMediaKeys(null).pipe(tap(() => (this.mediaElement.src = src))); + }); + } + set postFlushSeek(time) { + this.mediaElementStore.postFlushSeek = time; + } + schedulePostFlushSeek(newSeekTo) { + applyTransaction(() => { + if (this.mediaQuery.seekTo) { + // forget unfinished internal seek + this.seekTo = null; // this will also clear mediaSink.postFlushSeek + } + this.postFlushSeek = newSeekTo; + }); + } + set seekTo(seekTo) { + this.mediaElementStore.setSeekToPos(seekTo, false); + } + setSeekToWithDiscontinuity(seekTo, discoSeqNum) { + this.mediaElementStore.setSeekToPos(seekTo, false, discoSeqNum); + } + nudgeSeek(seekTo, nudgeCount) { + applyTransaction(() => { + this.mediaElementStore.setSeekToPos(seekTo, false); + this.mediaElementStore.setNudgeInfo({ nudgeTarget: seekTo, nudgeCount }); + }); + } + set desiredRate(desiredRate) { + this.mediaElementStore.desiredRate = desiredRate; + // play / pause done in rateChange epic + } + toggleTrickPlaybackMode(enabled) { + // Override playbackRate value + if (this.config.overridePlaybackRate) { + const mediaRate = enabled ? 2 : 1; + try { + this.mediaElement.playbackRate = mediaRate; + this.logger.info({ name: 'iframes' }, `setting playbackRate to ${mediaRate}`); + } + catch (err) { + this.logger.error({ name: 'iframes' }, `Exception when setting playbackRate=${mediaRate}: ${err.message}`); + } + } + // Toggle mute value + const prev = this.muteValueOnTrickPlaybackToggle; + if (enabled && prev === undefined) { + this.muteValueOnTrickPlaybackToggle = this.mediaElement.muted; + this.mediaElement.muted = enabled; + } + else if (!enabled && prev !== undefined) { + this.mediaElement.muted = prev; + this.muteValueOnTrickPlaybackToggle = undefined; + } + } + play() { + this.logger.info('play()'); + this.mediaFunctions.play(); + } + pause() { + this.logger.info('pause()'); + this.mediaFunctions.pause(); + } + get expectPlayEvent() { + return this.mediaFunctions.expectPlayEvent; + } + set expectPlayEvent(value) { + this.mediaFunctions.expectPlayEvent = value; + } + get expectPauseEvent() { + return this.mediaFunctions.expectPauseEvent; + } + set expectPauseEvent(value) { + this.mediaFunctions.expectPauseEvent = value; + } + set textTracksCreated(created) { + const { mediaElementStore } = this; + mediaElementStore.textTracksCreated = created; + } + get msDuration() { + return this._mediaQuery.msDuration; + } + set msDuration(duration) { + try { + const { mediaElementStore } = this; + const mediaSource = this.mediaSource$.value; + if (mediaSource.duration !== duration) { + this.logger.debug(`set msduration ${duration}`); + mediaSource.duration = duration; + mediaElementStore.msDuration = duration; + } + } + catch (err) { + this.logger.warn(`Error setting duration ${err.message}`); + } + } + set haveEnough(haveEnough) { + this.mediaElementStore.haveEnough = haveEnough; + } + set flushing(flushing) { + this.mediaElementStore.flushing = flushing; + } + set bufferMonitorTargetDuration(durationSeconds) { + this.mediaElementStore.bufferMonitorTargetDuration = durationSeconds; + } + get textTracks() { + return this.mediaElement.textTracks; + } + get id3TextTrack() { + return this.id3Track; + } + addTextTrack(kind, label, language) { + return this.mediaElement.addTextTrack(kind, label, language); + } + dispatchEvent(e) { + return this.mediaElement.dispatchEvent(e); + } + get offsetWidth() { + return this.mediaElement.offsetWidth; + } + get offsetHeight() { + return this.mediaElement.offsetHeight; + } + getliveSeekableWindow() { + return this.liveSeekableWindow; + } + archiveParsedSubtitleFragmentRecord(persistentId, mediaSeqNum, fragCueRecord) { + return this.mediaElementStore.archiveParsedSubtitleFragmentRecord(persistentId, mediaSeqNum, fragCueRecord); + } + updateLiveSeekableRange(levelDetails) { + const fragments = levelDetails.fragments; + const len = fragments.length; + if (len > 1) { + const start = Math.max(fragments[0].start, 0); + const end = fragments[len - 1].start + fragments[len - 1].duration; + this.mediaSource$.value.updateLiveSeekableRange(start, end); + this.liveSeekableWindow.start = start; + this.liveSeekableWindow.end = end; + } + } + clearLiveSeekableRange() { + this.mediaSource$.value.clearLiveSeekableRange(); + this.liveSeekableWindow.start = NaN; + this.liveSeekableWindow.end = NaN; + } + } + /** + * @brief Translate media element events to store updates + */ + const hookMediaElementsEvents = (mediaElement, mediaElementStore, mediaQuery, mediaSink, gaplessInstance, config, logger, rtcService) => { + if (!mediaElement) + return EMPTY; + const target = fromEventTarget(mediaElement); + return merge(target.event('durationchange').pipe(map((ev) => convertEvent(mediaElement, 'durationchange', ev)), tap((ev) => { + const media = ev.currentTarget; + mediaElementStore.mediaElementDuration = media.duration; + })), target.event('seeking').pipe(throttleTime(config.seekEventThrottleMs), map((ev) => convertEvent(mediaElement, 'seeking', ev)), tap((ev) => { + const media = ev.currentTarget; + const seekToValue = media.currentTime; + if (media.readyState >= media.HAVE_METADATA) { + const seekTo = mediaQuery.seekTo; + if (!seekTo || (!seekTo.fromEvent && Math.abs(seekTo.pos - seekToValue) > 0.00001)) { + // Gapless Stuff + if (gaplessInstance.inGaplessMode) { + gaplessSeek(seekToValue, gaplessInstance, mediaSink, mediaElementStore, logger); + return; + } + else { + if (mediaSink && + // eslint-disable-next-line no-prototype-builtins + mediaSink.hasOwnProperty('liveSeekableWindow') && + isFiniteNumber(mediaSink.getliveSeekableWindow().start) && + isFiniteNumber(mediaSink.getliveSeekableWindow().end) && + (seekToValue < mediaSink.getliveSeekableWindow().start || seekToValue > mediaSink.getliveSeekableWindow().end)) { + // Adjust seek to within live window + liveAdjustedSeek(seekToValue, mediaSink.getliveSeekableWindow().start, mediaSink.getliveSeekableWindow().end, config, mediaElementStore, logger); + } + else { + mediaElementStore.setSeekToPos(seekToValue, true); + } + } + } + else { + // seekTo is already set (not from event) so we just need to update seeking state until "seeked" + mediaElementStore.seeking = true; + } + } + })), target.event('seeked').pipe(map((ev) => convertEvent(mediaElement, 'seeked', ev)), tap(() => { + mediaElementStore.setSeekToPos(null, true); + const currentTime = mediaElement.currentTime; + if (isFiniteNumber(currentTime)) { + logger.qe({ critical: true, name: 'seeked', data: { seekTo: currentTime.toFixed(3) } }); + } + })), target.event('play').pipe(map((ev) => convertEvent(mediaElement, 'play', ev)), withLatestFrom(mediaQuery.desiredRate$), map(([ev, desiredRate]) => { + const media = ev.currentTarget; + mediaElementStore.paused = media.paused; + const expectPlayEvent = mediaSink.expectPlayEvent; + const allowSetRate = media.controls || config.nativeControlsEnabled; + logger.info(`media play desiredRate=${desiredRate} expectPlayEvent=${expectPlayEvent} allowed=${allowSetRate}`); + if (!expectPlayEvent && allowSetRate) { + mediaSink.checkForReplay(); + mediaElementStore.desiredRate = 1; + } + mediaSink.expectPlayEvent = false; + return ev; + })), target.event('playing').pipe(map((ev) => convertEvent(mediaElement, 'playing', ev)), tap((ev) => { + const media = ev.currentTarget; + // log playing event immediately. + logger.qe({ critical: true, name: 'playerEvent', data: { type: ev.type, pos: media.currentTime.toFixed(3), dur: media.duration.toFixed(3) } }); + mediaElementStore.paused = media.paused; + mediaElementStore.gotPlayingEvent(); + })), target.event('loadstart').pipe(map((ev) => convertEvent(mediaElement, 'loadstart', ev)), tap(() => { + mediaElementStore.gotLoadStartEvent(); + })), target.event('pause').pipe(map((ev) => convertEvent(mediaElement, 'pause', ev)), tap((ev) => { + const media = ev.currentTarget; + mediaElementStore.paused = media.paused; + const expectPauseEvent = mediaSink.expectPauseEvent; + const allowSetRate = media.controls || config.nativeControlsEnabled; + logger.info(`media pause desiredRate=${mediaQuery.desiredRate} expectPauseEvent=${expectPauseEvent} allowed=${allowSetRate}`); + if (!expectPauseEvent && allowSetRate) { + mediaElementStore.desiredRate = 0; + } + mediaSink.expectPauseEvent = false; + })), + // readystate change events. May not be fired for all platforms so take + // with a grain of salt + target.event('loadedmetadata').pipe(map((ev) => convertEvent(mediaElement, 'loadedmetadata', ev))), // HAVE_METADATA + target.event('loadeddata').pipe(map((ev) => convertEvent(mediaElement, 'loadeddata', ev))), // HAVE_CURRENT_DATA + target.event('canplay').pipe(map((ev) => convertEvent(mediaElement, 'canplay', ev))), // HAVE_FUTURE_DATA + target.event('canplaythrough').pipe(map((ev) => convertEvent(mediaElement, 'canplaythrough', ev))), // HAVE_ENOUGH_DATA + target.event('waiting').pipe(map((ev) => convertEvent(mediaElement, 'waiting', ev))), // less than HAVE_CURRENT_DATA and not paused + target.event('emptied').pipe(map((ev) => convertEvent(mediaElement, 'emptied', ev))), // HAVE_NOTHING (media.load() was called) + target.event('error').pipe(map((ev) => convertEvent(mediaElement, 'error', ev)), concatMap((ev) => { + return throwError(mediaElement.error); + })), // HAVE_NOTHING + target.event('ended').pipe(map((ev) => convertEvent(mediaElement, 'ended', ev)))).pipe(withLatestFrom(mediaQuery.bufferedRangeTuple$), withTransaction(([ev, bufferedRanges]) => { + const media = ev.currentTarget; + const readyState = media.readyState; + mediaElementStore.readyState = readyState; + mediaElementStore.ended = media.ended; + logger.info(`media event: ${ev.type} pos:${media.currentTime.toFixed(3)} dur:${media.duration.toFixed(3)} buffered:${stringifyWithPrecision(bufferedRanges)} readyState:${readyState}`); + // playing event is logged immediately when event occurs, so skip it here + if (ev.type != 'playing') { + logger.qe({ critical: true, name: 'playerEvent', data: { type: ev.type, pos: media.currentTime.toFixed(3), dur: media.duration.toFixed(3) } }); + } + }), catchError((err) => { + if (err instanceof MediaError) { + logger.warn(`mediaElementError, code: ${err.code}, message: ${err.message}`); + rtcService === null || rtcService === void 0 ? void 0 : rtcService.handleMediaElementError(err); + } + else { + logger.error(`media event error: ${err.message}`); + } + return EMPTY; + }), switchMapTo(NEVER), catchError((err) => { + if (err instanceof MediaError) { + logger.warn(`mediaElementError, code: ${err.code}, message: ${err.message}`); + return throwError(err); // re-throw media element errors for reporting + } + else { + logger.error(`media event error: ${err.message}`); + return EMPTY; + } + })); + }; + // Helper function for seeking when in gapless mode + function gaplessSeek(seekToValue, gaplessInstance, mediaSink, mediaElementStore, logger) { + let seekValueAdjusted = false; + // Adjust seekToValue if needed + logger.debug(`seekTo before gapless adjust: ${seekToValue}, startOffset: ${gaplessInstance.playingItem.itemStartOffset}`); + if (seekToValue < gaplessInstance.playingItem.itemStartOffset) { + logger.warn(`[Gapless] Seeking past track boundary oldSeek=${seekToValue}, adjustedSeek=${gaplessInstance.playingItem.itemStartOffset}`); + seekToValue = gaplessInstance.playingItem.itemStartOffset; + seekValueAdjusted = true; + } + if (gaplessInstance.isPreloading) { + if (seekToValue > gaplessInstance.loadingItem.itemStartOffset) { + logger.warn(`[Gapless] Seeking past track boundary oldSeek=${seekToValue}, adjustedSeek=${gaplessInstance.loadingItem.itemStartOffset}`); + seekToValue = gaplessInstance.loadingItem.itemStartOffset; + seekValueAdjusted = true; + } + // Unable to get the state before the seek event, hence need to dequeue on every seek. + gaplessInstance.dequeueSource('SeekToUnbufferedTimeRanges'); + } + if (seekValueAdjusted) { + mediaSink.resetMediaSource(seekToValue); + } + else { + mediaElementStore.setSeekToPos(seekToValue, true); + } + } + // TODO consolidate this with live-position sanitizeLiveSeek + function liveAdjustedSeek(seekValue, liveSeekableWindowStart, liveSeekableWindowEnd, config, mediaElementStore, logger) { + let adjustedSeek = seekValue; + if (seekValue < liveSeekableWindowStart) { + adjustedSeek = liveSeekableWindowStart; + } + else if (seekValue > liveSeekableWindowEnd) { + let targetLatency = config.defaultTargetDuration; + if (isFiniteNumber(config.liveSyncDuration)) { + targetLatency = config.liveSyncDuration; + } + else if (isFiniteNumber(config.liveSyncDurationCount)) { + targetLatency = config.liveSyncDurationCount * config.defaultTargetDuration; + } + adjustedSeek = Math.max(0, liveSeekableWindowEnd - targetLatency); + } + logger.warn(`[live] liveAdjustedSeek seekTo:${toFixed(seekValue, 3)}, adjustedSeek:${toFixed(adjustedSeek, 3)}, liveWindowStart:${toFixed(liveSeekableWindowStart, 3)}, liveWindowEnd:${toFixed(liveSeekableWindowEnd, 3)}`); + mediaElementStore.setSeekToPos(adjustedSeek, true); + } + function getMatchingInfo(sbEntity, fragStart, fragEnd, discoSeqNum, mediaOptionId, logger) { + const buffered = sbEntity === null || sbEntity === void 0 ? void 0 : sbEntity.bufferedSegments; + if (!buffered) { + logger.warn('getMatchingInfo trying to query null sbEntity'); + return null; + } + const seg = buffered.find((seg) => { + const matchDisco = seg.frag.discoSeqNum === discoSeqNum; + const overlap = Math.max(fragStart, seg.startPTS) < Math.min(fragEnd, seg.endPTS); + return matchDisco && overlap; + }); + if (seg != null) { + const { audioCodec, videoCodec, mimeType } = sbEntity; + return { mimeType, audioCodec, videoCodec, startPTSSec: seg.startPTS, endPTSSec: seg.endPTS, discoSeqNum, mediaOptionId }; + } + return null; + } + /** + * check whether the appendData is compatible with the curent source buffers. + * @param appendData the data to append. It is assumed that we will never have mismatched discoSeqNum for these two + * @param offsetTimestamp timestamp value that will be used to SourceBuffer.timestampOffset. Use for position calculation + * @param sourceBufferEntities info about the current set of buffers + * @param expectedSbCount the expected number of sourceBuffers + * @param logger a logger instance + * @returns compatible: if compatible with current source buffers + * boundary: the discontinuity boundary. NaN if we didn't get enough info + */ + function isCompatibleWithSourceBuffers(appendData, sourceBufferEntities, expectedSbCount, gapTolerance, logger) { + var _a, _b; + const curSbCount = sourceBufferEntities.filter((sbEntity) => Boolean(sbEntity)).length; + const newSbCount = appendData.filter((sbDataTuple) => Boolean(sbDataTuple)).length; + const compatInfos = [null, null]; + appendData.forEach((sbAppendData, type) => { + if (sbAppendData) { + const { offsetTimestamp } = sbAppendData; + const tsOffsetSec = convertTimestampToSeconds(offsetTimestamp); + const mimeType = sbAppendData.initSeg.mimeType; + const { audioCodec, videoCodec } = sbAppendData.initSeg.initParsedData; + const { dataSeg } = sbAppendData; + const startPTS = convertTimestampToSeconds(dataSeg.startPts) - tsOffsetSec; + const endPTS = convertTimestampToSeconds(dataSeg.endPts) - tsOffsetSec; + const discoSeqNum = dataSeg.discoSeqNum; + compatInfos[type] = { audioCodec, videoCodec, mimeType, startPTSSec: startPTS, endPTSSec: endPTS, discoSeqNum, mediaOptionId: sbAppendData.initSeg.mediaOptionId }; + logger.trace(`compatInfos[${type}] = { ${audioCodec}, ${videoCodec}, ${mimeType}, ${startPTS}, ${endPTS}, ${discoSeqNum}, ${sbAppendData.initSeg.mediaOptionId}`); + } + }); + let compatible = curSbCount === expectedSbCount; + let gotEnoughData = newSbCount === expectedSbCount; + // check appendData count against enabled tracks (expectedSbCount) instead of existing sourceBuffer counts (curSbCount). + // when switching between muxed and alt audio, curSbCount may be wrong and need a resetMediaSource before appending. + logger.trace(`mediaSink setup: enabledMediaOption# ${expectedSbCount} sourceBuffer# ${curSbCount} appendData# ${newSbCount}: compatible ${compatible} gotEnoughData ${gotEnoughData}`); + if (newSbCount === 1 && newSbCount < expectedSbCount && curSbCount !== 0) { + const curType = appendData[MediaOptionType.Variant] ? MediaOptionType.Variant : MediaOptionType.AltAudio; + const otherType = 1 - curType; + const curFrag = compatInfos[curType]; + const foundFrag = (compatInfos[otherType] = getMatchingInfo(sourceBufferEntities[otherType], curFrag.startPTSSec, curFrag.endPTSSec, curFrag.discoSeqNum, curFrag.mediaOptionId, logger)); + if (!foundFrag) { + const iframeMode = (_b = (_a = appendData[MediaOptionType.Variant]) === null || _a === void 0 ? void 0 : _a.dataSeg) === null || _b === void 0 ? void 0 : _b.iframe; + if (iframeMode && curType === MediaOptionType.Variant && compatInfos[curType]) { + // in iframe mode, audio may have large loading gaps. Allow iframes to append even when no matching audio frag is present. + const currentVideoCodec = sourceBufferEntities[curType].videoCodec; + const newVideoCodec = compatInfos[curType].videoCodec; + compatible = compatible && (currentVideoCodec === newVideoCodec || MediaUtil.isCompatibleVideoCodec(currentVideoCodec, newVideoCodec)); + if (compatible) { + logger.info(`${MediaOptionNames[curType]} allow iframe=${currentVideoCodec}->${newVideoCodec} with no matching audio #disco`); + return { compatible, boundary: NaN, allowance: NaN, discoSeqNum: compatInfos[curType].discoSeqNum }; + } + } + else { + logger.warn(`${MediaOptionNames[curType]} No matching frag found ${stringifyWithPrecision(curFrag)} buffered=${stringifyWithPrecision(sourceBufferEntities[otherType].bufferedSegments.map((seg) => { + const { mediaSeqNum, discoSeqNum } = seg.frag; + return { mediaSeqNum, discoSeqNum, startPTS: seg.startPTS, endPTS: seg.endPTS }; + }))}`); + } + } + else { + logger.trace(`foundFrag = compatInfos[${otherType}] = { ${foundFrag.audioCodec}, ${foundFrag.videoCodec}, ${foundFrag.mimeType}, ${foundFrag.startPTSSec}, ${foundFrag.endPTSSec}, ${foundFrag.discoSeqNum}, ${foundFrag.mediaOptionId}`); + } + gotEnoughData = foundFrag != null; + } + let boundary = NaN; + let allowance = NaN; + let cc = NaN; + if (gotEnoughData) { + compatInfos.forEach((info, type) => { + var _a; + if (!info) + return null; + // to be used for incompat. discontinuity only: + // set cc if all append & buffered data at this pos have the same cc + // determine the next discontinuity sequence number after resetMediaSource + if (!isFiniteNumber(cc)) { + cc = info.discoSeqNum; + } + else if (cc !== info.discoSeqNum) { + cc = NaN; + } + const currentSbEntity = sourceBufferEntities[type]; + if (!currentSbEntity) { + compatible = false; + } + else { + const currentAudioCodec = currentSbEntity.audioCodec; + const currentVideoCodec = currentSbEntity.videoCodec; + const { audioCodec: newAudioCodec, videoCodec: newVideoCodec } = info; + compatible = compatible && (currentVideoCodec === newVideoCodec || MediaUtil.isCompatibleVideoCodec(currentVideoCodec, newVideoCodec)); + compatible = compatible && (currentAudioCodec === newAudioCodec || MediaUtil.isCompatibleAudioCodec(currentAudioCodec, newAudioCodec)); + logger.info(`${MediaOptionNames[type]} video=${currentVideoCodec}->${newVideoCodec} audio=${currentAudioCodec}->${newAudioCodec} start=${(_a = info.startPTSSec) === null || _a === void 0 ? void 0 : _a.toFixed(3)} #disco`); + } + if (isFiniteNumber(boundary)) { + allowance = Math.abs(info.startPTSSec - boundary); + boundary = Math.max(info.startPTSSec, boundary); + } + else { + allowance = 0; + boundary = info.startPTSSec; + } + }); + } + return { compatible: compatible && gotEnoughData, boundary, allowance, discoSeqNum: cc }; + } + function seekEpic(mediaElement, mediaQuery, mediaSink, config, logger) { + logger = logger.child({ name: 'seek' }); + return (seekTo$) => seekTo$.pipe(tap((seekTo) => logger.info(`seekTo=${JSON.stringify(seekTo)}`)), filter((seekTo) => seekTo && isFiniteNumber(seekTo.pos)), switchMap((seekTo) => { + return mediaQuery.readyState$.pipe(filter((readyState) => readyState >= mediaElement.HAVE_METADATA), take(1), mapTo(seekTo), switchMap(({ pos, fromEvent }) => { + logger.info(`startSeek(${pos === null || pos === void 0 ? void 0 : pos.toFixed(3)},${fromEvent})`); + mediaSink.checkForInconsistentStoreBufferRangesAndUpdate(); + if (!mediaElement.paused) { + mediaSink.pause(); + } + if (!fromEvent) { + mediaElement.currentTime = pos; + } + return waitFor(mediaQuery.haveEnough$, (x) => x).pipe(mapTo({ pos, fromEvent })); + }), switchMap(({ pos, fromEvent }) => { + const combinedBuffer = mediaQuery.getCombinedBufferInfo(pos, 0); + const nextStart = combinedBuffer.nextStart; + const applyNudge = !fromEvent || config.nudgeFromEventSeek; + logger.info(`haveEnough readystate=${mediaQuery.readyState} fromEvent=${fromEvent} applyNudge=${applyNudge} nextStart ${nextStart} pos ${pos} combinedBuffer.len ${combinedBuffer.len}`); + if (applyNudge && combinedBuffer.len === 0 && isFiniteNumber(nextStart) && nextStart > pos && nextStart - pos <= config.maxSeekHole) { + logger.info(`haveEnough but current position not actually buffered, nudge ${pos.toFixed(3)}->${nextStart.toFixed(3)}`); + mediaSink.seekTo = nextStart; + return EMPTY; + } + return of(pos); + }), withLatestFrom(mediaQuery.desiredRate$), map(([pos, desiredRate]) => { + logger.info(`completeSeek(${pos === null || pos === void 0 ? void 0 : pos.toFixed(3)}) readyState=${mediaElement.readyState} paused=${mediaElement.paused} desiredRate=${desiredRate}`); + if (mediaElement.paused && desiredRate !== 0) { + mediaSink.play(); + } + }), switchMapTo(EMPTY), catchError((err) => { + logger.error(`error during seek ${err.message}`); + return EMPTY; + })); + })); + } + function rateChangeEpic(mediaElement, mediaQuery, mediaSink, logger) { + return (rateChange$) => rateChange$.pipe(withLatestFrom(mediaQuery.seekTo$), switchMap(([rate, seekTo]) => { + if (isFiniteNumber(seekTo === null || seekTo === void 0 ? void 0 : seekTo.pos)) { + return EMPTY; // Taken care of by seekEpic + } + if (rate === 0) { + if (!mediaElement.paused) { + mediaSink.pause(); + } + return EMPTY; + } + return waitFor(mediaQuery.haveEnough$, (x) => x).pipe(tap(() => { + if (mediaElement.paused) { + mediaSink.play(); + } + })); + }), switchMapTo(EMPTY)); + } + function stallEpic(mediaSink, meStore, config, logger) { + return (stallInfo$) => stallInfo$.pipe(exhaustMap((stallInfo) => { + if (!stallInfo) { + return of(NaN); + } + return nudgeSeek(mediaSink, meStore, config, logger); + })); + } + function nudgeSeek(mediaSink, meStore, config, logger) { + const meQuery = mediaSink.mediaQuery; + const gotUserSeek$ = combineQueries([meQuery.seekTo$, meQuery.nudgeTarget$]).pipe(filter(([seekTo, nudgeTarget]) => seekTo && isFiniteNumber(seekTo.pos) && isFiniteNumber(nudgeTarget) && (seekTo.pos < nudgeTarget || seekTo.pos - nudgeTarget > config.maxSeekHole)), mapTo(null)); + return merge(gotUserSeek$, meQuery.stallInfo$).pipe(withLatestFrom(meQuery.desiredRate$), observeOn(asyncScheduler), map(([stallInfo, desiredRate], index) => { + if (!stallInfo) { + return NaN; + } + const bufferInfo = meQuery.getCombinedBufferInfo(stallInfo.currentTime, 0); // maxBufferHole 0 + const iframeMode = isIframeRate(desiredRate); + return nudgeIfNeeded(mediaSink, config, logger, stallInfo, bufferInfo, iframeMode, index); + }), takeWhile((nudgeTarget) => isFiniteNumber(nudgeTarget)), finalize$1(() => { + logger.debug('nudge seek finalize'); + meStore.setNudgeInfo(null); + })); + } + function nudgeIfNeeded(mediaSink, config, logger, stallInfo, bufferInfo, iframeMode, nudgeRetry) { + const { type, isLowBufferStall, currentTime } = stallInfo; + const bufferLen = bufferInfo.len; + const jumpThreshold = config.maxSeekHole; + let nudgePosition = NaN; + if (isLowBufferStall) { + // if buffer len is below threshold, try to jump to start of next buffer range if close + // no buffer available @ currentTime, check if next buffer is close (within a config.maxSeekHole second range) + const nextBufferStart = bufferInfo.nextStart - currentTime <= jumpThreshold ? bufferInfo.nextStart : Infinity; //this._ensureNudgeStartTimeForIncompatibleCC(bufferInfo.nextStart, currentTime, config.maxSeekHole); + if (isFiniteNumber(nextBufferStart)) { + // next buffer is close ! adjust currentTime to nextBufferStart + // this will ensure effective video decoding + logger.info(`adjust currentTime from ${currentTime} to next buffered @ ${nextBufferStart}`); + nudgePosition = nextBufferStart; + } + else if (mediaSink.mediaQuery.msDuration - currentTime < 0.1) { + nudgePosition = currentTime + 0.1; + } + } + else { + // if (this.relaxHighBufferStallRule) { + // this.relaxHighBufferStallRule--; + // logger.log(`ignore stall. ${this.relaxHighBufferStallRule} free pass available. staying put @ ${media.currentTime}`); + // payload = new BufferStallError(ErrorDetails.BUFFER_STALLED_ERROR, false, + // 'got buffer stalled error', ErrorResponses.VideoDecoderBadDataErr, type, bufferLen); + // } else + if (nudgeRetry < config.nudgeMaxRetry) { + // playback stalled in buffered area ... let's nudge currentTime to try to overcome this + nudgePosition = currentTime + config.nudgeOffset; + } + else if (iframeMode) { + // maxed number of nudges - but in iframeMode + logger.error(`still stuck in high buffer @${currentTime} after ${config.nudgeMaxRetry}, non fatal in iframeMode`); + } + else { + logger.error(`still stuck in high buffer @${currentTime} after ${config.nudgeMaxRetry}, raise fatal error`); + throw new BufferStallError(ErrorDetails.BUFFER_STALLED_ERROR, true, 'got fatal buffer error', ErrorResponses.VideoDecoderBadDataErr, type, bufferLen); + } + } + if (isFiniteNumber(nudgePosition)) { + logger.info(`stall ${type} nudge ${currentTime}->${nudgePosition} nudgeRetry=${nudgeRetry}`); + mediaSink.nudgeSeek(nudgePosition, nudgeRetry + 1); + } + return nudgePosition; + } + + /** + * @brief Service that manages playback state + */ + // state for the service functions + const mediaElementStore = new MediaElementStore(); + /** + * media component managment + */ + const mediaElementServiceEpic = (makeMediaSource, config, gaplessInstance, logger, teardownWG$, rtcService) => (mediaElementSource$) => { + return mediaElementSource$.pipe(tag('playback.mediaElementServiceEpic.in'), switchMap((mediaElement) => { + if (!mediaElement) { + return of(null); + } + const mediaSink = new MediaSink(mediaElement, mediaElementStore, config, gaplessInstance, logger, teardownWG$, rtcService); + mediaSink.openMediaSource(makeMediaSource()); + return merge(of(mediaSink), mediaSink); + }), tag('playback.mediaElementServiceEpic.emit')); + }; + + const displaySupportsHdr$ = () => { + if (typeof matchMedia === 'function') { + const mediaQueryList = matchMedia('(dynamic-range: high)'); + const badQuery = matchMedia('bad query'); + if (mediaQueryList.media !== badQuery.media) { + return merge(of(mediaQueryList), fromEvent(mediaQueryList, 'change')).pipe(map((e) => e.matches)); + } + } + // assume hdr is supported by display + return of(true); + }; + + class PlatformQuery extends Query { + constructor(store) { + super(store); + this.store = store; + this.displaySupportsHdr$ = this.select('supportsHdr'); + this.platformInfo$ = this.select('platformInfo'); + this.viewportInfo$ = this.select('viewportInfo'); + } + get platformInfo() { + return this.getValue().platformInfo; + } + get displaySupportsHdr() { + return this.getValue().supportsHdr; + } + get viewportInfo() { + return this.getValue().viewportInfo; + } + } + + function createInitialState() { + return { supportsHdr: true }; + } + class PlatformStore extends Store { + constructor() { + super(createInitialState(), { name: 'platform', producerFn: produce_1 }); + } + } + + class PlatformService { + constructor(store) { + this.store = store; + } + getQuery() { + return new PlatformQuery(this.store); + } + updateSupportsHdr(supported) { + this.store.update((state) => { + state.supportsHdr = supported; + }); + } + updatePlatformInfo(platformInfo) { + this.store.update({ platformInfo }); + } + updateViewportInfo(viewportInfo) { + this.store.update((state) => { + state.viewportInfo = viewportInfo; + }); + } + } + const store = new PlatformStore(); + let service$1 = null; // To be instantiated in platformService(); + /*********************************************** + * Static helper functions that specifically use the above singletons + */ + function createPlatformQuery() { + return new PlatformQuery(store); + } + /** + * @returns The global instance of PlatformService that operates on global PlatformStore + */ + function platformService() { + if (!service$1) { + service$1 = new PlatformService(store); + } + return service$1; + } + function listenForHdrUpdates(platformService) { + return displaySupportsHdr$().pipe(tap((supported) => { + platformService.updateSupportsHdr(supported); + })); + } + + // The vanilla RPCService uses old-school callback pattern to minimize the + // dependency surface in restricted environments, e.g. in Web Worker. + // However, if resource permitted, one can use this RPCServiceObservable wrapper + // to convert RPCService interface into Observable pattern for better + // interoperability with RxJs code. + class RPCServiceObservable extends Observable { + constructor(_vanillaRPC, teardownWG$) { + super(subscriber => { + teardownWG$ === null || teardownWG$ === void 0 ? void 0 : teardownWG$.add(); + subscriber.add(this._handler$ + .pipe(mergeMap(([handler, args, callback]) => handler(...args).pipe(tap({ + next(result) { + callback(result, null); + }, + error(error) { + callback(null, error); + }, + })))) + .subscribe()); + }); + this._vanillaRPC = _vanillaRPC; + this.teardownWG$ = teardownWG$; + this._handler$ = new Subject(); + } + register(command, handler) { + const asyncHandler = (...args) => (callback) => { + this._handler$.next([handler, args, callback]); + }; + return this._vanillaRPC.register(command, asyncHandler); + } + invoke(command, args, transfer) { + return new Observable((subscriber) => { + this._vanillaRPC.invoke(command, args, transfer)((result, error) => { + if (error != null) { + subscriber.error(error); + } + else { + subscriber.next(result); + subscriber.complete(); + } + }); + }); + } + } + + // CryptoRPCClient only runs in main process, so I has more resources to include + // the RxJS as dependency and use the Observable pattern. So we can extend on + // RPCServiceObservable instead of the vanilla RPCService class. + class CryptoRPCClient extends RPCServiceObservable { + constructor(rpcService) { + super(rpcService); + } + decrypt(key, iv, alg, cipherText, options) { + return this.invoke('decrypt', [key, iv, alg, cipherText, options], [key, iv, cipherText]); + } + } + + class DemuxRPCClient extends RPCServiceObservable { + constructor(rpcService) { + super(rpcService); + this.rpcService = rpcService; + this.sessions = {}; + this._onEvent = (demuxSessionID, event, data) => () => { + if (this.sessions[demuxSessionID] != null) { + this.sessions[demuxSessionID].observer.trigger(event, data); + } + }; + this.rpcService.register('demuxer.event', this._onEvent); + } + init(typeSupported, config, vendor) { + config = (({ maxSeekHole, maxBufferHole, audioPrimingDelay, stretchShortVideoTrack, forceKeyFrameOnDiscontinuity }) => ({ + maxSeekHole, + maxBufferHole, + audioPrimingDelay, + stretchShortVideoTrack, + forceKeyFrameOnDiscontinuity, + }))(config); + return this.invoke('demuxer.init', [typeSupported, config, vendor], []).pipe(map((demuxSessionID) => { + const session = new DemuxRPCSession(this, demuxSessionID); + this.sessions[demuxSessionID] = session; + return session; + })); + } + } + class DemuxRPCSession { + constructor(rpc, demuxSessionID) { + this.rpc = rpc; + this.demuxSessionID = demuxSessionID; + this.observer = new Observer(); + } + push(data, keyTagInfo, initSegment, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS, iframeMediaStart, iframeDuration, transfer) { + return this.rpc.invoke('demuxer.push', [this.demuxSessionID, data, keyTagInfo, initSegment, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS, iframeMediaStart, iframeDuration], transfer !== null && transfer !== void 0 ? transfer : [data]); + } + pushWithoutTransfer(data, keyTagInfo, initSegment, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS, iframeMediaStart, iframeDuration) { + return this.push(data, keyTagInfo, initSegment, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS, iframeMediaStart, iframeDuration, []); + } + destroy() { + this.observer.removeAllListeners(); + this.rpc.invoke('demuxer.destroy', [this.demuxSessionID], []).subscribe(); + } + } + + const createRPCClients = (rpcService) => ({ + crypto: new CryptoRPCClient(rpcService), + mux: new DemuxRPCClient(rpcService), + }); + + const LoggerRPCServer = (rpcService, logger) => { + rpcService.register('logger.log', (bindings, level, ...args) => (callback) => { + try { + for (const b of bindings) { + logger = logger.child(b); + } + if (typeof logger[level] === 'function') { + // eslint-disable-next-line @typescript-eslint/ban-types + logger[level](...args); + } + callback(); + } + catch (err) { + callback(undefined, err); + } + }); + }; + + // Inline service treat the client and server running in the same process, and + // just forward messages directly by function calls. + class RPCInlineService { + constructor() { + this.handlers = {}; + } + register(command, handler) { + if (this.handlers[command] != null) { + return false; + } + this.handlers[command] = handler; + } + unregister(command) { + if (this.handlers[command] != null) { + return false; + } + delete this.handlers[command]; + } + invoke(command, args) { + return (callback = RPCInlineService._fallbackCallback) => { + try { + if (this.handlers[command] == null) { + throw new Error(`command ${command} not found`); + } + this.handlers[command](...args)(callback); + } + catch (error) { + callback(undefined, error); + } + }; + } + teardown(done) { + this.handlers = null; + done(); + } + } + RPCInlineService._fallbackCallback = (result, error) => { + if (error != null) { + throw error; + } + }; + + const createRPCInlineService = (logger) => { + logger = logger.child({ name: 'InlineRPCService' }); + const rpcService = new RPCInlineService(); + new CryptoRPCServer(rpcService, logger); + new DemuxRPCServer(rpcService, logger); + logger.info('Inline RPCService has started'); + return rpcService; + }; + let worker; + const createRPCWorkerService = (logger) => { + if (!hasUMDWorker()) { + throw new Error('UMD WebWorker is not bundled in this file'); + } + try { + if (worker == null) { + const blob = new Blob(['var exports = {};var module = { exports: exports };function define(f){f()};define.amd = true;(' + __HLS_UMD_BUNDLE__.toString() + ')(true);'], { + type: 'text/javascript', + }); + const workerUrl = URL.createObjectURL(blob); + worker = new Worker(workerUrl); + } + const rpcService = new RPCWorkerService(worker); + LoggerRPCServer(rpcService, logger.child({ name: 'WorkerRPCService' })); + return rpcService; + } + catch (err) { + throw new Error('Failed to create WebWorker'); + } + }; + const createRPCServiceWithFallbacks = (...fallbacks) => (logger) => { + for (let i = 0; i < fallbacks.length; i++) { + const create = fallbacks[i]; + try { + return create(logger); + } + catch (err) { + if (i === fallbacks.length - 1) { + throw err; + } + logger.warn(err); + } + } + }; + const createRPCService = (logger) => { + return createRPCServiceWithFallbacks(createRPCWorkerService, createRPCInlineService)(logger); + }; + + /* + * RTC types + * + * + */ + /* + * Events that RTC sends out to the backend. + * https://confluence.sd.apple.com/display/ISM/Matchpoint+Event-Key+List + */ + class RTCEventGroup { + } + RTCEventGroup.PlayEnded = 6101; + RTCEventGroup.Periodic = 6110; + RTCEventGroup.PlayStalled = 6103; + RTCEventGroup.KeySessionComplete = 6104; + RTCEventGroup.PlayLikelyToKeepUp = 6105; + RTCEventGroup.PlayRateChanged = 6106; + RTCEventGroup.PlayError = 6107; + RTCEventGroup.MediaEngineStalled = 6108; + RTCEventGroup.SwitchComplete = 6109; + RTCEventGroup.VariantEnded = 6111; + RTCEventGroup.NwError = 6202; + const RTCVideoQualityIndex = { + avc1: 1, + avc3: 1, + hvc1: { SDR: 2, HLG: 10, PQ: 11 }, + hev1: { SDR: 2, HLG: 10, PQ: 11 }, + vp09: { SDR: 3, HLG: 14, PQ: 13 }, + dvh1: { PQ: 12 }, + }; + + const loggerName$5 = { name: 'rtc-component' }; + class RTCComponent { + constructor(query, logger) { + this.query = query; + this.logger = logger; + } + setReportingAgent(reportingAgent) { + this.reportingAgent = reportingAgent; + } + sendPlayEnded(id) { + this.logger.debug(loggerName$5, `sendPlayEnded id=${id}`); + const method = RTCEventGroup.PlayEnded; + this.fillAndFire(method, this.query.playEnded(id)); + } + sendPlayStalled(id) { + const method = RTCEventGroup.PlayStalled; + this.fillAndFire(method, this.query.playStalled(id)); + } + sendMediaEngineStalled(id) { + const method = RTCEventGroup.MediaEngineStalled; + this.fillAndFire(method, this.query.mediaEngineStalled(id)); + } + sendKeySessionComplete(id) { + const method = RTCEventGroup.KeySessionComplete; + this.fillAndFire(method, this.query.keySessionComplete(id)); + } + sendPlayLikelyToKeepUp(id) { + const method = RTCEventGroup.PlayLikelyToKeepUp; + this.fillAndFire(method, this.query.playLikelyToKeepUp(id)); + } + sendPlayRateChange(id) { + this.logger.debug(loggerName$5, `sendPlayRateChange id=${id}`); + const method = RTCEventGroup.PlayRateChanged; + this.fillAndFire(method, this.query.playRateChanged(id)); + } + sendSwitchComplete(id) { + const method = RTCEventGroup.SwitchComplete; + this.fillAndFire(method, this.query.switchComplete(id)); + } + sendVariantEnded(id) { + const method = RTCEventGroup.VariantEnded; + this.fillAndFire(method, this.query.variantEnded(id)); + } + sendPlayError(id) { + const method = RTCEventGroup.PlayError; + this.fillAndFire(method, this.query.playError(id)); + } + sendNwError(id) { + const method = RTCEventGroup.NwError; + this.fillAndFire(method, this.query.nwError(id)); + } + sendPeriodic(id) { + const method = RTCEventGroup.Periodic; + this.fillAndFire(method, this.query.periodic(id)); + } + fillAndFire(method, record) { + this.logger.debug(loggerName$5, 'RTC fillAndFire'); + // don't send periodic event if there is no playtime or active fragment download time + if (method === RTCEventGroup.Periodic && !record.PlayTimeWC && !record.ADT) { + this.logger.info(loggerName$5, `RTC Method: ${method}, Skipping due to no playback/download activity`); + return; + } + const type = method === RTCEventGroup.PlayEnded || method === RTCEventGroup.Periodic ? 1 : 0; + this.logger.info(loggerName$5, `RTC Method: ${method}, Type:${type}`); + if (this.reportingAgent) { + this.logger.debug(loggerName$5, 'Reporting Agent is set!'); + let eventPayload = {}; + Object.entries(record).forEach(([key, value]) => { + // round all numeric values to two decimal points + if (isFiniteNumber(value)) { + value = Number(Number(value).toFixed(2)); + } + // Extract keys from ServerInfo and populate the event payload; no other object types allowed. Ref: privacyAllowedLoadConfigHeaders + if (typeof value === 'object') { + if (key === 'ServerInfo') { + Object.entries(value).forEach(([k, v]) => { + eventPayload[k] = v; + }); + } + else { + this.logger.debug(loggerName$5, `Object type unsupported for key: ${key}`); + } + } + else { + eventPayload[key] = value; + } + }); + this.logger.debug(loggerName$5, 'RTC Method: %s, payload=%o', method, eventPayload); + // copy record into extensible payload + eventPayload = JSON.parse(JSON.stringify(eventPayload)); + try { + this.reportingAgent.issueReportingEvent(method, eventPayload, type); + } + catch (err) { + this.logger.info(loggerName$5, 'Cannot report, reportingAgent failed'); + } + } + } + } + + class RTCQuery extends QueryEntity { + constructor(rtcStore, logger) { + super(rtcStore); + this.logger = logger; + } + // generic getters + get activeEntity() { + return this.getActive(); + } + entity(id) { + return this.getEntity(id); + } + playEnded(id) { + var _a; + return (_a = this.getEntity(id)) === null || _a === void 0 ? void 0 : _a.playEndedRecord; + } + periodic(id) { + var _a; + return (_a = this.getEntity(id)) === null || _a === void 0 ? void 0 : _a.periodicRecord; + } + playStalled(id) { + var _a; + return (_a = this.getEntity(id)) === null || _a === void 0 ? void 0 : _a.playStalledRecord; + } + mediaEngineStalled(id) { + var _a; + return (_a = this.getEntity(id)) === null || _a === void 0 ? void 0 : _a.mediaEngineStalledRecord; + } + keySessionComplete(id) { + var _a; + return (_a = this.getEntity(id)) === null || _a === void 0 ? void 0 : _a.keySessionCompleteRecord; + } + playLikelyToKeepUp(id) { + var _a; + return (_a = this.getEntity(id)) === null || _a === void 0 ? void 0 : _a.playLikelyToKeepUpRecord; + } + playRateChanged(id) { + var _a; + return (_a = this.getEntity(id)) === null || _a === void 0 ? void 0 : _a.playRateChangedRecord; + } + switchComplete(id) { + var _a; + return (_a = this.getEntity(id)) === null || _a === void 0 ? void 0 : _a.switchCompleteRecord; + } + variantEnded(id) { + var _a; + return (_a = this.getEntity(id)) === null || _a === void 0 ? void 0 : _a.variantEndedRecord; + } + playError(id) { + var _a; + return (_a = this.getEntity(id)) === null || _a === void 0 ? void 0 : _a.playErrorRecord; + } + nwError(id) { + var _a; + return (_a = this.getEntity(id)) === null || _a === void 0 ? void 0 : _a.nwErrorRecord; + } + } + + class RTCStore extends EntityStore { + constructor(logger) { + super({}, { name: 'rtc-store', idKey: 'itemId', producerFn: produce_1, resettable: true }); + this.logger = logger; + } + /* + * Create an RTC Entity with the necessary records + */ + createEntity(itemId) { + const rtcEntity = { + itemId, + sessionControlRecord: { + state: 'RTC_STATE_INIT', + rate: 0, + oldRate: 0, + eventStartTime: Date.now(), + sessionStartTime: Date.now(), + lastLikelyToKeepUpTime: Date.now(), + lastPeriodicTime: Date.now(), + playLikelyToKeepUpEventCounter: 1, + periodicEventCounter: 1, + activeKeySessions: {}, + intervalVariantList: {}, + sessionVariantList: {}, + }, + playEndedRecord: {}, + periodicRecord: {}, + playStalledRecord: {}, + keySessionCompleteRecord: {}, + playLikelyToKeepUpRecord: {}, + playRateChangedRecord: {}, + playErrorRecord: {}, + mediaEngineStalledRecord: {}, + switchCompleteRecord: {}, + variantEndedRecord: {}, + nwErrorRecord: {}, + }; + this.add(rtcEntity); + } + /* + * The below updates require an RTC event to be sent out, and hence an event payload needs to be prepared. + * => PlayEnded (6101) + * => Periodic (6110) + * => PlayStalled (6103) + * => KeySessionComplete (6104) + * => PlayLikelyToKeepUp (6105) + * => PlayRateChanged (6106) + * => PlayError (6107) + * => MediaEngineStalled (6108) + * => SwitchComplete (6109) + * => VariantEnded (6111) + * => NwError (6202) + */ + /* Indicates playback ended */ + updateEnded(itemId) { + this._prepareEventPlayEnded(itemId); + } + /* Indicates periodic timer fired */ + updatePeriodic(itemId, isFinal) { + this._prepareEventPeriodic(itemId, { isFinal }); + } + /* Indicates playback stall due to bad network */ + updateBufferStalled(itemId, data) { + this.update(itemId, ({ sessionControlRecord, variantEndedRecord, periodicRecord, playEndedRecord }) => { + sessionControlRecord.rate = 0; + variantEndedRecord.StallCount = (variantEndedRecord.StallCount || 0) + 1; + periodicRecord.StallCount = (periodicRecord.StallCount || 0) + 1; + playEndedRecord.StallCount = (playEndedRecord.StallCount || 0) + 1; + }); + this._prepareEventPlayStalled(itemId, data); + } + /* Indicates identity key session success / failure */ + updateSegmentKeyLoaded(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + const keySessionInfo = {}; + const now = data.timestamp; + keySessionInfo.keyFormat = 'identity'; + keySessionInfo.keyDeliveryTime = data.adt; + keySessionInfo.currentMediaTime = data.currentTime; + if (sessionControlRecord.state === 'RTC_STATE_INIT') { + keySessionInfo.keyInitTime = now - sessionControlRecord.sessionStartTime; + } + sessionControlRecord.activeKeySessions[data.keyuri] = keySessionInfo; + }); + this._prepareEventKeySessionComplete(itemId, data); + } + /* Indicates non-identity (FPS/Widevine/PlayReady) key session success / failure events */ + updateLicenseResponseProcessed(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + const keySessionInfo = sessionControlRecord.activeKeySessions[data.keyuri]; + keySessionInfo.licenseResponseProcessTime = data.timestamp - keySessionInfo.licenseResponseSubmitTime; + sessionControlRecord.lastKeyDeliveryTime = keySessionInfo.keyDeliveryTime = data.timestamp - keySessionInfo.licenseChallengeStartTime; + keySessionInfo.currentMediaTime = data.currentTime; + sessionControlRecord.finishedKeyUri = data.keyuri; + }); + this._prepareEventKeySessionComplete(itemId, data); + } + updateLicenseChallengeError(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + const keySessionInfo = sessionControlRecord.activeKeySessions[data.keyuri]; + sessionControlRecord.lastKeyErrorType = keySessionInfo.keyErrorType = 'licenseChallengeError'; + sessionControlRecord.finishedKeyUri = data.keyuri; + }); + this._prepareEventKeySessionComplete(itemId, data); + } + updateLicenseResponseError(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + const keySessionInfo = sessionControlRecord.activeKeySessions[data.keyuri]; + sessionControlRecord.lastKeyErrorType = keySessionInfo.keyErrorType = 'licenseResponseError'; + sessionControlRecord.finishedKeyUri = data.keyuri; + }); + this._prepareEventKeySessionComplete(itemId, data); + } + updateKeyAborted(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + const keySessionInfo = sessionControlRecord.activeKeySessions[data.keyuri]; + keySessionInfo.keyErrorType = 'keyAborted'; + sessionControlRecord.finishedKeyUri = data.keyuri; + }); + this._prepareEventKeySessionComplete(itemId, data); + } + /* Indicates likely to keep up */ + updateCanPlay(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + // if (sessionControlRecord.state === 'RTC_STATE_PLAY') { + // /* Ignore if we're already in PLAY */ + // return; + // } + }); + this._prepareEventPlayLikelyToKeepUp(itemId, data); + } + /* Indicates rate changed */ + updateRateChanged(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + sessionControlRecord.rate = data.rate * 100; + if (data.rate > 0 && sessionControlRecord.oldRate === 0) { + if (!sessionControlRecord.playInfo) { + sessionControlRecord.playInfo = []; + } + sessionControlRecord.playInfo.push({ latency: data.latency }); + // if curLevelUrl is not set at this point, we are in a scenario where the play event beats the levelSwitched event in a race + // conditon. To avoid a crash, set the curLevelUrl here. + if (!sessionControlRecord.curLevelUrl) { + sessionControlRecord.curLevelUrl = data.url; + } + } + }); + this._prepareEventPlayRateChanged(itemId, data); + } + /* Indicates play error */ + updateMediaError(itemId, data) { + this.update(itemId, ({ sessionControlRecord, periodicRecord, playEndedRecord }) => { + periodicRecord.PlayerErrCount = (periodicRecord.PlayerErrCount || 0) + 1; + playEndedRecord.PlayerErrCount = (playEndedRecord.PlayerErrCount || 0) + 1; + if (data.fatal) { + sessionControlRecord.rate = 0; + periodicRecord.FatalPlayerErrCount = (periodicRecord.FatalPlayerErrCount || 0) + 1; + playEndedRecord.FatalPlayerErrCount = (playEndedRecord.FatalPlayerErrCount || 0) + 1; + playEndedRecord.ErrCode = data.details; + playEndedRecord.ErrReason = data.code; + playEndedRecord.ErrIsFatal = true; + playEndedRecord.ErrDomain = 'mediaError'; + } + }); + if (data.fatal) { + this._prepareEventPlayError(itemId, data); + } + else { + this.update(itemId, ({ sessionControlRecord }) => { + // Non-fatal errors won't be sent immediately for each occurrence. Instead, they will be sent before the periodic event with the count of occurrences. + // We send separate events if the error code and reason are different. For e.g. fragLoadError due to 403 and fragLoadError due to 404 are counted separately. + const key = data.details + '/' + data.code; + sessionControlRecord.nonFatalPlayErrList[key] = (sessionControlRecord.nonFatalPlayErrList[key] || 0) + 1; + }); + } + } + /* Indicates media element error */ + updateMediaElementError(itemId, data) { + /* + Parse the media element error message. Luna encodes ErrReason & ErrDetail as follows. + "message": '{ + "ErrReason": -5001, // Luna error code + "ErrDetail": "0xce0defba" // Third party error code + }' + + Note: mediaElementError is not inserted into the 6101 payload. + */ + let errJson; + try { + errJson = JSON.parse(data.message); + } + catch (error) { + this.logger.warn(`message is not JSON, ignoring; ${data.message}`); + } + const mediaElemReason = errJson ? parseInt(errJson['ErrReason']) : null; + const mediaElemDetail = errJson ? parseInt(errJson['ErrDetail']) : null; + this._prepareEventPlayError(itemId, { + domain: 'mediaElementError', + mediaElemCode: data.code, + mediaElemReason: mediaElemReason, + mediaElemDetail: mediaElemDetail, + // These are fixed values for mediaElementError and set in _prepareEventPlayError + code: null, + details: null, + fatal: null, + }); + } + /* Indicates plaback stall due to media engine problem */ + updateMediaEngineStalled(itemId, data) { + this.update(itemId, ({ sessionControlRecord, variantEndedRecord, periodicRecord, playEndedRecord }) => { + sessionControlRecord.rate = 0; + variantEndedRecord.MediaEngineStallCount = (variantEndedRecord.MediaEngineStallCount || 0) + 1; + periodicRecord.MediaEngineStallCount = (periodicRecord.MediaEngineStallCount || 0) + 1; + playEndedRecord.MediaEngineStallCount = (playEndedRecord.MediaEngineStallCount || 0) + 1; + }); + this._prepareEventMediaEngineStalled(itemId, data); + } + /* Indicates level switch complete */ + updateLevelSwitched(itemId, data) { + this.update(itemId, ({ sessionControlRecord, switchCompleteRecord, periodicRecord, playEndedRecord }) => { + periodicRecord.SwCnt = (periodicRecord.SwCnt || 0) + 1; + playEndedRecord.SwCnt = (playEndedRecord.SwCnt || 0) + 1; + const now = Date.now(); + const prevLevelUrl = sessionControlRecord.curLevelUrl; + const curLevelUrl = data.url; + const prevVarInfo = this._getVariantInfo(prevLevelUrl, sessionControlRecord); + const curVarInfo = this._getVariantInfo(curLevelUrl, sessionControlRecord); + let curSwDir; + let iframes = false; + if (prevLevelUrl) { + if (curVarInfo.bandwidth < prevVarInfo.bandwidth) { + curSwDir = 'Down'; + } + else { + curSwDir = 'Up'; + } + iframes = curVarInfo.iframes; + } + switchCompleteRecord.BadSw = this._isBadSw(curSwDir, sessionControlRecord.lastSwitchDir || curSwDir, iframes, sessionControlRecord.lastLevelIsIframe || iframes, now, sessionControlRecord.lastSwitchTime || now, data.isSeeking); + sessionControlRecord.lastSwitchDir = curSwDir; + sessionControlRecord.lastSwitchTime = now; + sessionControlRecord.lastLevelIsIframe = iframes; + sessionControlRecord.curLevelUrl = curLevelUrl; + sessionControlRecord.variantStartTimeMedia = data.currentTime; + }); + this._prepareEventSwitchComplete(itemId, data); + } + /* Indicates level switch error */ + updateLevelLoadError(itemId, data) { + this.update(itemId, ({ sessionControlRecord, switchCompleteRecord, periodicRecord, playEndedRecord }) => { + const now = Date.now(); + let curSwDir; + let iframes = false; + if (sessionControlRecord.curLevelUrl) { + const prevVarInfo = this._getVariantInfo(sessionControlRecord.curLevelUrl, sessionControlRecord); + const curVarInfo = this._getVariantInfo(data.url, sessionControlRecord); + if (curVarInfo.bandwidth < prevVarInfo.bandwidth) { + curSwDir = 'Down'; + } + else { + curSwDir = 'Up'; + } + iframes = curVarInfo.iframes; + } + periodicRecord.SwCnt = (periodicRecord.SwCnt || 0) + 1; + playEndedRecord.SwCnt = (playEndedRecord.SwCnt || 0) + 1; + switchCompleteRecord.BadSw = this._isBadSw(curSwDir, sessionControlRecord.lastSwitchDir || curSwDir, iframes, sessionControlRecord.lastLevelIsIframe || iframes, now, sessionControlRecord.lastSwitchTime || now, data.isSeeking); + switchCompleteRecord.SwFail = true; + }); + this._prepareEventSwitchComplete(itemId, data); + } + /* Indicates variant end */ + updateVariantEnd(itemId, data) { + this._prepareEventVariantEnded(itemId, data); + } + /* Indicates network error */ + updateNwError(itemId, data) { + this.update(itemId, ({ sessionControlRecord, periodicRecord, playEndedRecord }) => { + periodicRecord.NwErrCount = (periodicRecord.NwErrCount || 0) + 1; + playEndedRecord.NwErrCount = (playEndedRecord.NwErrCount || 0) + 1; + if (data.fatal) { + sessionControlRecord.rate = 0; + periodicRecord.FatalNwErrCount = (periodicRecord.FatalNwErrCount || 0) + 1; + playEndedRecord.FatalNwErrCount = (playEndedRecord.FatalNwErrCount || 0) + 1; + playEndedRecord.ErrCode = data.details; + playEndedRecord.ErrReason = data.code; + playEndedRecord.ErrIsFatal = true; + playEndedRecord.ErrDomain = 'networkError'; + } + }); + if (data.fatal) { + this._prepareEventNwError(itemId, data); + } + else { + this.update(itemId, ({ sessionControlRecord }) => { + // Non-fatal errors won't be sent immediately for each occurrence. Instead, they will be sent before the periodic event with the count of occurrences. + // We send separate events if the error code and reason are different. For e.g. fragLoadError due to 403 and fragLoadError due to 404 are counted separately. + const key = data.details + '/' + data.code; + sessionControlRecord.nonFatalNwErrList[key] = (sessionControlRecord.nonFatalNwErrList[key] || 0) + 1; + }); + } + } + /* + * The below method is called after an event is sent out, to update the internal states. + */ + finalize(itemId, event) { + switch (event) { + case RTCEventGroup.PlayEnded: { + this.update(itemId, ({ sessionControlRecord }) => { + sessionControlRecord.state = 'RTC_STATE_STOP'; + sessionControlRecord.oldRate = 0; + }); + /* Reset the event record */ + this.update(itemId, (state) => { + state.playEndedRecord = {}; + }); + break; + } + case RTCEventGroup.Periodic: { + this.update(itemId, ({ sessionControlRecord }) => { + sessionControlRecord.lastPeriodicTime = Date.now(); + sessionControlRecord.periodicEventCounter += 1; + /* Re-initialize playtime for all levels */ + const levels = Object.keys(sessionControlRecord.intervalVariantList); + levels.forEach((level) => { + sessionControlRecord.intervalVariantList[level].playTime = 0; + }); + }); + /* Reset the event record */ + this.update(itemId, (state) => { + state.periodicRecord = {}; + }); + break; + } + case RTCEventGroup.PlayStalled: { + this.update(itemId, ({ sessionControlRecord }) => { + sessionControlRecord.state = 'RTC_STATE_STALL'; + sessionControlRecord.oldRate = 0; + }); + /* Reset the event record */ + this.update(itemId, (state) => { + state.playStalledRecord = {}; + }); + break; + } + case RTCEventGroup.KeySessionComplete: { + this.update(itemId, ({ sessionControlRecord }) => { + delete sessionControlRecord.activeKeySessions[sessionControlRecord.finishedKeyUri]; + }); + /* Reset the event record */ + this.update(itemId, (state) => { + state.keySessionCompleteRecord = {}; + }); + break; + } + case RTCEventGroup.PlayLikelyToKeepUp: { + this.update(itemId, ({ sessionControlRecord }) => { + if (sessionControlRecord.state !== 'RTC_STATE_PLAY') { + /* Ignore if we're already in PLAY */ + sessionControlRecord.state = 'RTC_STATE_CANPLAY'; + sessionControlRecord.lastLikelyToKeepUpTime = Date.now(); + sessionControlRecord.playLikelyToKeepUpEventCounter += 1; + } + }); + /* Reset the event record */ + this.update(itemId, (state) => { + state.playLikelyToKeepUpRecord = {}; + }); + break; + } + case RTCEventGroup.PlayRateChanged: { + this.update(itemId, ({ sessionControlRecord, playEndedRecord, playStalledRecord, mediaEngineStalledRecord, playErrorRecord, nwErrorRecord }) => { + if (sessionControlRecord.rate !== 0) { + sessionControlRecord.state = 'RTC_STATE_PLAY'; + // reset the below keys as we resume playback + delete playEndedRecord.LastStall; + delete playEndedRecord.LastMediaEngineStall; + delete playEndedRecord.LastPause; + delete playErrorRecord.LastPause; + delete nwErrorRecord.LastPause; + } + else { + sessionControlRecord.state = 'RTC_STATE_PAUSE'; + // reset the below keys as we pause playback + delete playStalledRecord.LastResume; + delete mediaEngineStalledRecord.LastResume; + delete playErrorRecord.LastResume; + delete nwErrorRecord.LastResume; + } + /* Update the old rate */ + sessionControlRecord.oldRate = sessionControlRecord.rate; + }); + /* Reset the event record */ + this.update(itemId, (state) => { + state.playRateChangedRecord = {}; + }); + break; + } + case RTCEventGroup.PlayError: { + this.update(itemId, ({ sessionControlRecord }) => { + sessionControlRecord.state = 'RTC_STATE_PLAYERROR'; + }); + /* Reset the event record */ + this.update(itemId, (state) => { + state.playErrorRecord = {}; + }); + break; + } + case RTCEventGroup.MediaEngineStalled: { + this.update(itemId, ({ sessionControlRecord }) => { + sessionControlRecord.state = 'RTC_STATE_MEDIAENGINESTALL'; + sessionControlRecord.oldRate = 0; + }); + /* Reset the event record */ + this.update(itemId, (state) => { + state.mediaEngineStalledRecord = {}; + }); + break; + } + case RTCEventGroup.SwitchComplete: { + /* Reset the event record */ + this.update(itemId, (state) => { + state.switchCompleteRecord = {}; + // record the level transition + state.sessionControlRecord.prevLevelUrl = state.sessionControlRecord.curLevelUrl; + }); + break; + } + case RTCEventGroup.VariantEnded: { + this.update(itemId, ({ sessionControlRecord }) => { + sessionControlRecord.decodedFramesForVariant = 0; + sessionControlRecord.decodedFramesForVariantSampleCount = 0; + }); + /* Reset the event record */ + this.update(itemId, (state) => { + state.variantEndedRecord = {}; + }); + break; + } + case RTCEventGroup.NwError: { + this.update(itemId, ({ sessionControlRecord }) => { + sessionControlRecord.state = 'RTC_STATE_NWERROR'; + }); + /* Reset the event record */ + this.update(itemId, (state) => { + state.nwErrorRecord = {}; + }); + break; + } + } + /* Finally, reset the event clock */ + this.update(itemId, ({ sessionControlRecord }) => { + sessionControlRecord.eventStartTime = Date.now(); + }); + } + /* + * The below updates don't involve an RTC event to be sent out. + * These updates just aggregate data in the store for various RTC records. + */ + updatePlaybackInfo(itemId, data) { + this.update(itemId, ({ sessionControlRecord, periodicRecord, playEndedRecord }) => { + if (data.droppedVideoFrames < sessionControlRecord.droppedVideoFrames) { + // MSE counter reset, so reset ours too + sessionControlRecord.droppedVideoFrames = 0; + } + if (data.decodedFrameCount < sessionControlRecord.decodedFrameCount) { + // MSE counter reset, so reset ours too + sessionControlRecord.decodedFrameCount = 0; + } + // we get the dropped frames and decoded frames from the MSE object every second. Calculate the current frame drop and frame rate. + const curFrameDrop = data.droppedVideoFrames - (sessionControlRecord.droppedVideoFrames || 0); + const decodedFramesPerSec = data.decodedFrameCount - (sessionControlRecord.decodedFrameCount || 0); + sessionControlRecord.droppedVideoFrames = data.droppedVideoFrames; + sessionControlRecord.decodedFrameCount = data.decodedFrameCount; + // update the decode frames for the current variant to compute average decode frame count for the variant + sessionControlRecord.decodedFramesForVariant += decodedFramesPerSec; + sessionControlRecord.decodedFramesForVariantSampleCount += 1; + if (curFrameDrop) { + // if we drop 3 or more frames per second, then that's a group frame drop. + if (curFrameDrop >= 3) { + periodicRecord.GroupViFrDr = (periodicRecord.GroupViFrDr || 0) + curFrameDrop; + periodicRecord.GroupViFrDrEvtCount = (periodicRecord.GroupViFrDrEvtCount || 0) + 1; + playEndedRecord.GroupViFrDr = (playEndedRecord.GroupViFrDr || 0) + curFrameDrop; + playEndedRecord.GroupViFrDrEvtCount = (playEndedRecord.GroupViFrDrEvtCount || 0) + 1; + } + else { + periodicRecord.SparseViFrDr = (periodicRecord.SparseViFrDr || 0) + curFrameDrop; + periodicRecord.SparseViFrDrEvtCount = (periodicRecord.SparseViFrDrEvtCount || 0) + 1; + playEndedRecord.SparseViFrDr = (playEndedRecord.SparseViFrDr || 0) + curFrameDrop; + playEndedRecord.SparseViFrDrEvtCount = (playEndedRecord.SparseViFrDrEvtCount || 0) + 1; + } + } + }); + } + updateBufferAppended(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + if (!sessionControlRecord.bufferAppendInfo) { + sessionControlRecord.bufferAppendInfo = []; + } + sessionControlRecord.bufferAppendInfo.push(data); + }); + } + updateSeeked(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + if (!sessionControlRecord.seekInfo) { + sessionControlRecord.seekInfo = []; + } + sessionControlRecord.seekInfo.push(data); + }); + } + updateManifestParsed(itemId, data) { + this.update(itemId, ({ sessionControlRecord, periodicRecord, playEndedRecord }) => { + periodicRecord.MasterPlaylistADT = (periodicRecord.MasterPlaylistADT || 0) + data.adt; + playEndedRecord.MasterPlaylistADT = (playEndedRecord.MasterPlaylistADT || 0) + data.adt; + const result = this._computeVariantInfo(data.levels); + // update the common keys + playEndedRecord.IsAudioOnly = data.isAudioOnly; + playEndedRecord.IsGapless = data.isGapless; + playEndedRecord.IsFirstItem = data.isFirstItem; + playEndedRecord.ItemID = data.itemID; + playEndedRecord.MaxVideoQltyIndex = result.maxVideoQltyIndex; + playEndedRecord.MaxReWd = result.maxWidth; + playEndedRecord.MaxReHt = result.maxHeight; + sessionControlRecord.manifestData = { variantList: result.variantList, varListString: result.varListString }; + }); + } + updateFragLoaded(itemId, data) { + this.update(itemId, ({ sessionControlRecord, periodicRecord, playEndedRecord }) => { + periodicRecord.MediaRequestsSent = (periodicRecord.MediaRequestsSent || 0) + 1; + playEndedRecord.MediaRequestsSent = (playEndedRecord.MediaRequestsSent || 0) + 1; + if (data.cdnServer) { + const cdnServer = data.cdnServer.toLowerCase(); + if (cdnServer === 'aapl' || cdnServer === 'akam' || cdnServer === 'llnw') { + playEndedRecord.LastMediaCDNServer = cdnServer; + } + } + if (data.serverInfo) { + playEndedRecord.ServerInfo = data.serverInfo; + } + if (!sessionControlRecord.segmentMimeTypes) { + sessionControlRecord.segmentMimeTypes = []; + } + if (sessionControlRecord.segmentMimeTypes.find((mime) => mime == data.contentType) === undefined) { + sessionControlRecord.segmentMimeTypes.push(data.contentType); + } + if (data.fragType === MediaOptionType.Variant) { + // accumulate the 'main' (muxed) / 'video' (unmuxed) bytes & duration to calculate AvgVideoBitrate + sessionControlRecord.variantVideoBytes = (sessionControlRecord.variantVideoBytes || 0) + data.bytes; + sessionControlRecord.variantVideoDuration = (sessionControlRecord.variantVideoDuration || 0) + data.duration; + sessionControlRecord.intervalVideoBytes = (sessionControlRecord.intervalVideoBytes || 0) + data.bytes; + sessionControlRecord.intervalVideoDuration = (sessionControlRecord.intervalVideoDuration || 0) + data.duration; + sessionControlRecord.sessionVideoBytes = (sessionControlRecord.sessionVideoBytes || 0) + data.bytes; + sessionControlRecord.sessionVideoDuration = (sessionControlRecord.sessionVideoDuration || 0) + data.duration; + sessionControlRecord.obrLast = (data.bytes * 8) / (data.duration / 1000); + /* Update periodic record */ + periodicRecord.NetBytes = (periodicRecord.NetBytes || 0) + data.bytes; + periodicRecord.ADT = (periodicRecord.ADT || 0) + data.adt; + periodicRecord.SegmentProcessTime = (periodicRecord.SegmentProcessTime || 0) + data.processTime; + /* Update playEnded record */ + playEndedRecord.ADT = (playEndedRecord.ADT || 0) + data.adt; + playEndedRecord.NetBytes = (playEndedRecord.NetBytes || 0) + data.bytes; + playEndedRecord.SegmentProcessTime = (playEndedRecord.SegmentProcessTime || 0) + data.processTime; + } + else if (data.fragType === MediaOptionType.AltAudio) { + // accumulate the 'audio' bytes & duration to calculate AvgAudioBitrate + sessionControlRecord.variantAudioBytes = (sessionControlRecord.variantAudioBytes || 0) + data.bytes; + sessionControlRecord.variantAudioDuration = (sessionControlRecord.variantAudioDuration || 0) + data.duration; + sessionControlRecord.intervalAudioBytes = (sessionControlRecord.intervalAudioBytes || 0) + data.bytes; + sessionControlRecord.intervalAudioDuration = (sessionControlRecord.intervalAudioDuration || 0) + data.duration; + sessionControlRecord.sessionAudioBytes = (sessionControlRecord.sessionAudioBytes || 0) + data.bytes; + sessionControlRecord.sessionAudioDuration = (sessionControlRecord.sessionAudioDuration || 0) + data.duration; + } + }); + } + updateFragBuffered(itemId, data) { + this.update(itemId, ({ periodicRecord, playEndedRecord }) => { + // use only 'main' (muxed) / 'video' (unmuxed) data to calculate below keys + if (data.fragType === MediaOptionType.Variant) { + /* Update periodic record */ + periodicRecord.SegmentParseTime = (periodicRecord.SegmentParseTime || 0) + data.parseTime; + /* Update playEnded record */ + playEndedRecord.SegmentParseTime = (playEndedRecord.SegmentParseTime || 0) + data.parseTime; + } + }); + } + updateLevelLoaded(itemId, data) { + this.update(itemId, ({ sessionControlRecord, playLikelyToKeepUpRecord, periodicRecord, playEndedRecord }) => { + playLikelyToKeepUpRecord.PlaylistADT = (playLikelyToKeepUpRecord.PlaylistADT || 0) + data.adt; + periodicRecord.PlaylistADT = (periodicRecord.PlaylistADT || 0) + data.adt; + playEndedRecord.PlaylistADT = (playEndedRecord.PlaylistADT || 0) + data.adt; + periodicRecord.MaxPlaylistDT = data.adt > periodicRecord.MaxPlaylistDT ? data.adt : periodicRecord.MaxPlaylistDT; + playEndedRecord.MaxPlaylistDT = data.adt > playEndedRecord.MaxPlaylistDT ? data.adt : playEndedRecord.MaxPlaylistDT; + playEndedRecord.PlayType = data.playType; + this._setTargetDuration(data.url, data.targetduration, sessionControlRecord); + if (!sessionControlRecord.playlistMimeTypes) { + sessionControlRecord.playlistMimeTypes = []; + } + if (sessionControlRecord.playlistMimeTypes.find((mime) => mime == data.contentType) === undefined) { + sessionControlRecord.playlistMimeTypes.push(data.contentType); + } + const curLevelUrl = data.url; + const curVarInfo = this._getVariantInfo(curLevelUrl, sessionControlRecord); + // Initialize the variant info if it's not seen before + if (!sessionControlRecord.intervalVariantList[curLevelUrl]) { + sessionControlRecord.intervalVariantList[curLevelUrl] = Object.assign({}, curVarInfo); + } + if (!sessionControlRecord.sessionVariantList[curLevelUrl]) { + sessionControlRecord.sessionVariantList[curLevelUrl] = Object.assign({}, curVarInfo); + } + }); + } + updateLevelsChanged(itemId, data) { + this.update(itemId, ({ sessionControlRecord, playEndedRecord }) => { + const result = this._computeVariantInfo(data.levels); + playEndedRecord.MaxVideoQltyIndex = result.maxVideoQltyIndex; + playEndedRecord.MaxReWd = result.maxWidth; + playEndedRecord.MaxReHt = result.maxHeight; + sessionControlRecord.manifestData = { variantList: result.variantList, varListString: result.varListString }; + // Update bitrate rank in sessionVariantList and intervalVariantList + Object.keys(result.variantList).forEach((url) => { + if (sessionControlRecord.intervalVariantList[url]) { + sessionControlRecord.intervalVariantList[url].brRnk = result.variantList[url].brRnk; + } + if (sessionControlRecord.sessionVariantList[url]) { + sessionControlRecord.sessionVariantList[url].brRnk = result.variantList[url].brRnk; + } + }); + }); + } + updateLicenseChallengeRequested(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + const keySessionInfo = {}; + const now = data.timestamp; + keySessionInfo.licenseChallengeStartTime = now; + // send the keyInitTime if we're initializing playback + if (sessionControlRecord.state === 'RTC_STATE_INIT') { + keySessionInfo.keyInitTime = now - sessionControlRecord.sessionStartTime; + } + sessionControlRecord.activeKeySessions[data.keyuri] = keySessionInfo; + }); + } + updateLicenseChallengeReceived(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + const keySessionInfo = sessionControlRecord.activeKeySessions[data.keyuri]; + keySessionInfo.licenseChallengeRequestTime = data.timestamp - keySessionInfo.licenseChallengeStartTime; + }); + } + updateLicenseChallengeSubmitted(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + const keySessionInfo = sessionControlRecord.activeKeySessions[data.keyuri]; + keySessionInfo.keyFormat = data.keyFormat; + keySessionInfo.licenseChallengeSubmitTime = data.timestamp; + }); + } + updateLicenseChallengeCreated(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + const keySessionInfo = sessionControlRecord.activeKeySessions[data.keyuri]; + keySessionInfo.cdmVersion = data.cdmVersion; + keySessionInfo.licenseChallengeCreationTime = data.timestamp - keySessionInfo.licenseChallengeSubmitTime; + }); + } + updateLicenseResponseRequested(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + const keySessionInfo = sessionControlRecord.activeKeySessions[data.keyuri]; + keySessionInfo.licenseResponseRequestTime = data.timestamp; + }); + } + updateLicenseResponseReceived(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + const keySessionInfo = sessionControlRecord.activeKeySessions[data.keyuri]; + keySessionInfo.licenseResponseReceiveTime = data.timestamp - keySessionInfo.licenseResponseRequestTime; + }); + } + updateLicenseResponseSubmitted(itemId, data) { + this.update(itemId, ({ sessionControlRecord }) => { + const keySessionInfo = sessionControlRecord.activeKeySessions[data.keyuri]; + keySessionInfo.licenseResponseSubmitTime = data.timestamp; + }); + } + /* + * Private Methods + */ + _prepareEventPlayEnded(itemId) { + this.update(itemId, ({ sessionControlRecord, playEndedRecord }) => { + var _a; + playEndedRecord.AvgVideoBitrate = ((sessionControlRecord.sessionVideoBytes || 0) * 8) / (sessionControlRecord.sessionVideoDuration || 1); + playEndedRecord.AvgAudioBitrate = ((sessionControlRecord.sessionAudioBytes || 0) * 8) / (sessionControlRecord.sessionAudioDuration || 1); + // round off NetBytes to the nearest 1000 to obscure the content size - as requested by privacy team. + const netBytes = Math.round((playEndedRecord.NetBytes || 0) / 1000) * 1000; + playEndedRecord.NetBytes = netBytes; + const adt = playEndedRecord.ADT || 1; + const processTime = playEndedRecord.ADT + playEndedRecord.SegmentProcessTime || 1; + const parseTime = playEndedRecord.ADT + playEndedRecord.SegmentProcessTime + playEndedRecord.SegmentParseTime || 1; + playEndedRecord.TWOBR = (netBytes * 8) / (adt / 1000); + playEndedRecord.PerceivedTWOBR = (netBytes * 8) / (processTime / 1000); + playEndedRecord.NetTWOBR = (netBytes * 8) / (parseTime / 1000); + // Use the playtime for the current level too, before generating time weighted values + const twVal = this._findTimeWeightedValues(sessionControlRecord.state === 'RTC_STATE_PLAY' ? Date.now() - sessionControlRecord.eventStartTime : 0, sessionControlRecord.sessionVariantList, sessionControlRecord.curLevelUrl); + playEndedRecord.PlayerTWIBR = twVal.twIBR; + playEndedRecord.PlayerTWIABR = twVal.twIABR; + playEndedRecord.TWBitRk = twVal.twBRnk; + const varInfo = this._getVariantInfo(sessionControlRecord.curLevelUrl, sessionControlRecord); + playEndedRecord.TargetDur = varInfo.targetduration; + playEndedRecord.ReWd = varInfo.width; + playEndedRecord.ReHt = varInfo.height; + playEndedRecord.VariantList = (_a = sessionControlRecord.manifestData) === null || _a === void 0 ? void 0 : _a.varListString; + let playlistMimeType = ''; + if (sessionControlRecord.playlistMimeTypes) { + sessionControlRecord.playlistMimeTypes.forEach((item) => { + playlistMimeType += item + ','; + }); + playlistMimeType = playlistMimeType.slice(0, -1); // remove the trailing comma + } + let segmentMimeType = ''; + if (sessionControlRecord.segmentMimeTypes) { + sessionControlRecord.segmentMimeTypes.forEach((item) => { + segmentMimeType += item + ','; + }); + segmentMimeType = segmentMimeType.slice(0, -1); // remove the trailing comma + } + playEndedRecord.PlaylistMimeType = playlistMimeType; + playEndedRecord.SegmentMimeType = segmentMimeType; + playEndedRecord.Rate = sessionControlRecord.rate; + }); + this._aggregateTimes(itemId); + } + _prepareEventPeriodic(itemId, data) { + this.update(itemId, ({ sessionControlRecord, periodicRecord, playEndedRecord }) => { + var _a; + periodicRecord.EventCounter = sessionControlRecord.periodicEventCounter; + periodicRecord.PInterval = Date.now() - sessionControlRecord.lastPeriodicTime; + periodicRecord.AvgVideoBitrate = ((sessionControlRecord.intervalVideoBytes || 0) * 8) / (sessionControlRecord.intervalVideoDuration || 1); + periodicRecord.AvgAudioBitrate = ((sessionControlRecord.intervalAudioBytes || 0) * 8) / (sessionControlRecord.intervalAudioDuration || 1); + let netBytes = periodicRecord.NetBytes || 0; + if (data.isFinal) { + // round off NetBytes to the nearest 1000 to obscure the content size - as requested by privacy team. (for periodic event, we need this only for the last event) + periodicRecord.NetBytes = netBytes = Math.round(netBytes / 1000) * 1000; + } + const adt = periodicRecord.ADT || 1; + const processTime = periodicRecord.ADT + periodicRecord.SegmentProcessTime || 1; + const parseTime = periodicRecord.ADT + periodicRecord.SegmentProcessTime + periodicRecord.SegmentParseTime || 1; + periodicRecord.TWOBR = (netBytes * 8) / (adt / 1000); + periodicRecord.PerceivedTWOBR = (netBytes * 8) / (processTime / 1000); + periodicRecord.NetTWOBR = (netBytes * 8) / (parseTime / 1000); + // Use the playtime for the current level too, before generating time weighted values + const twVal = this._findTimeWeightedValues(sessionControlRecord.state === 'RTC_STATE_PLAY' ? Date.now() - sessionControlRecord.eventStartTime : 0, sessionControlRecord.intervalVariantList, sessionControlRecord.curLevelUrl); + periodicRecord.PlayerTWIBR = twVal.twIBR; + periodicRecord.PlayerTWIABR = twVal.twIABR; + periodicRecord.TWBitRk = twVal.twBRnk; + const varInfo = this._getVariantInfo(sessionControlRecord.curLevelUrl, sessionControlRecord); + periodicRecord.TargetDur = varInfo.targetduration; + periodicRecord.ReWd = varInfo.width; + periodicRecord.ReHt = varInfo.height; + periodicRecord.VariantList = (_a = sessionControlRecord.manifestData) === null || _a === void 0 ? void 0 : _a.varListString; + periodicRecord.Rate = sessionControlRecord.rate; + const stats = this._computeMediaStats(sessionControlRecord); + if (stats) { + periodicRecord.MedianBufferAppendLatency = stats.bufLatencyInfo.median; + periodicRecord.MaxBufferAppendLatency = stats.bufLatencyInfo.max; + periodicRecord.MedianBufferAppendSize = stats.bufSizeInfo.median; + periodicRecord.MaxBufferAppendSize = stats.bufSizeInfo.max; + periodicRecord.MedianSeekLatency = stats.seekLatencyInfo.median; + periodicRecord.MaxSeekLatency = stats.seekLatencyInfo.max; + periodicRecord.MedianPlayLatency = stats.playLatencyInfo.median; + periodicRecord.MaxPlayLatency = stats.playLatencyInfo.max; + } + this._copyCommonKeys(periodicRecord, playEndedRecord); + }); + this._aggregateTimes(itemId); + } + _prepareEventPlayStalled(itemId, data) { + this.update(itemId, ({ sessionControlRecord, playStalledRecord, playEndedRecord }) => { + const varInfo = this._getVariantInfo(sessionControlRecord.curLevelUrl, sessionControlRecord); + const now = Date.now(); + playStalledRecord.MediaDur = data.mediaDur; + playStalledRecord.BitRnk = varInfo.brRnk; + playStalledRecord.Codecs = varInfo.codecs; + playStalledRecord.LastLikelyToKeepUp = now - sessionControlRecord.lastLikelyToKeepUpTime; + playStalledRecord.LastSwitch = now - sessionControlRecord.lastSwitchTime; + playStalledRecord.StallDetectionTime = data.stallDurationMs; + playStalledRecord.StallType = data.type; + playStalledRecord.BufferLength = data.bufferLen; + if (playEndedRecord.ErrDomain === 'networkError') { + playStalledRecord.NwErrTime = playEndedRecord.NwErrTime; + playStalledRecord.NwErrCode = playEndedRecord.ErrCode; + } + playStalledRecord.LaSwDir = sessionControlRecord.lastSwitchDir; + playStalledRecord.TargetDur = varInfo.targetduration; + playStalledRecord.PlayerIABR = varInfo.avgBandwidth; + playStalledRecord.PlayerIBR = varInfo.bandwidth; + playStalledRecord.Rate = sessionControlRecord.rate; + playStalledRecord.OBRLast = sessionControlRecord.obrLast; + playStalledRecord.OBRMean = (playEndedRecord.NetBytes * 8) / (playEndedRecord.ADT / 1000); + this._copyCommonKeys(playStalledRecord, playEndedRecord); + }); + this._aggregateTimes(itemId); + } + _prepareEventKeySessionComplete(itemId, data) { + this.update(itemId, ({ sessionControlRecord, keySessionCompleteRecord, playEndedRecord }) => { + const keySessionInfo = sessionControlRecord.activeKeySessions[data.keyuri]; + if (!keySessionInfo) { + this.logger.warn(`_prepareEventKeySessionComplete no keySessionInfo for ${redactUrl(data.keyuri)}`); + return; + } + keySessionCompleteRecord.KeyFormat = keySessionInfo.keyFormat; + keySessionCompleteRecord.CDMVersion = keySessionInfo.cdmVersion; + keySessionCompleteRecord.LicenseChallengeRequestTime = keySessionInfo.licenseChallengeRequestTime; + keySessionCompleteRecord.LicenseChallengeCreationTime = keySessionInfo.licenseChallengeCreationTime; + keySessionCompleteRecord.LicenseResponseReceiveTime = keySessionInfo.licenseResponseReceiveTime; + keySessionCompleteRecord.LicenseResponseProcessTime = keySessionInfo.licenseResponseProcessTime; + keySessionCompleteRecord.KeyDeliveryTime = keySessionInfo.keyDeliveryTime; + keySessionCompleteRecord.CurrentMediaTime = keySessionInfo.currentMediaTime * 1000; + keySessionCompleteRecord.KeyInitTime = keySessionInfo.keyInitTime; + keySessionCompleteRecord.KeyErrorType = keySessionInfo.keyErrorType; + this._copyCommonKeys(keySessionCompleteRecord, playEndedRecord); + }); + this._aggregateTimes(itemId); + } + _prepareEventPlayLikelyToKeepUp(itemId, data) { + this.update(itemId, ({ sessionControlRecord, playLikelyToKeepUpRecord, playEndedRecord }) => { + const varInfo = this._getVariantInfo(sessionControlRecord.curLevelUrl, sessionControlRecord); + playLikelyToKeepUpRecord.EventCounter = sessionControlRecord.playLikelyToKeepUpEventCounter; + playLikelyToKeepUpRecord.PlayerIABR = varInfo.avgBandwidth; + playLikelyToKeepUpRecord.PlayerIBR = varInfo.bandwidth; + playLikelyToKeepUpRecord.MediaDur = data.mediaDur; + playLikelyToKeepUpRecord.BitRnk = varInfo.brRnk; + playLikelyToKeepUpRecord.Codecs = varInfo.codecs; + playLikelyToKeepUpRecord.TargetDur = varInfo.targetduration; + this._copyCommonKeys(playLikelyToKeepUpRecord, playEndedRecord); + }); + this._aggregateTimes(itemId); + } + _prepareEventPlayRateChanged(itemId, data) { + this.update(itemId, ({ sessionControlRecord, playRateChangedRecord, playEndedRecord }) => { + const varInfo = this._getVariantInfo(sessionControlRecord.curLevelUrl, sessionControlRecord); + playRateChangedRecord.Rate = sessionControlRecord.rate; + playRateChangedRecord.StNPT = data.currentTime; + playRateChangedRecord.PlayerIABR = varInfo.avgBandwidth; + playRateChangedRecord.PlayerIBR = varInfo.bandwidth; + playRateChangedRecord.BitRnk = varInfo.brRnk; + playRateChangedRecord.MediaDur = data.mediaDur; + playRateChangedRecord.Codecs = varInfo.codecs; + this._copyCommonKeys(playRateChangedRecord, playEndedRecord); + }); + this._aggregateTimes(itemId); + } + _prepareEventPlayError(itemId, data) { + this.update(itemId, ({ sessionControlRecord, playErrorRecord, playEndedRecord }) => { + const varInfo = this._getVariantInfo(sessionControlRecord.curLevelUrl, sessionControlRecord); + const now = Date.now(); + if (data.domain == 'mediaElementError') { + playErrorRecord.ErrDomain = playErrorRecord.ErrCode = 'mediaElementError'; + playErrorRecord.ErrIsFatal = true; + playErrorRecord.ErrCodeMediaElement = data.mediaElemCode; + playErrorRecord.ErrReason = data.mediaElemReason; + playErrorRecord.ErrDetail = data.mediaElemDetail; + } + else { + playErrorRecord.ErrCode = data.details; + playErrorRecord.ErrReason = data.code; + playErrorRecord.ErrIsFatal = data.fatal; + playErrorRecord.ErrDomain = 'mediaError'; + } + playErrorRecord.PlayerErrCount = data.count || 1; + playErrorRecord.BitRnk = varInfo.brRnk; + playErrorRecord.MediaDur = data.mediaDur; + playErrorRecord.PlayTime = playEndedRecord.PlayTime; + playErrorRecord.PlayTimeWC = playEndedRecord.PlayTimeWC; + playErrorRecord.PlayerIABR = varInfo.avgBandwidth; + playErrorRecord.PlayerIBR = varInfo.bandwidth; + playErrorRecord.LastLikelyToKeepUp = now - sessionControlRecord.lastLikelyToKeepUpTime; + playErrorRecord.LastSwitch = now - sessionControlRecord.lastSwitchTime; + playErrorRecord.VideoQltyIndex = varInfo.qltyIndex; + playErrorRecord.Rate = sessionControlRecord.rate; + playErrorRecord.KeyErrorType = sessionControlRecord.lastKeyErrorType; + playErrorRecord.KeyDeliveryTime = sessionControlRecord.lastKeyDeliveryTime; + this._copyCommonKeys(playErrorRecord, playEndedRecord); + }); + this._aggregateTimes(itemId); + } + _prepareEventMediaEngineStalled(itemId, data) { + this.update(itemId, ({ sessionControlRecord, mediaEngineStalledRecord, playEndedRecord }) => { + const now = Date.now(); + const varInfo = this._getVariantInfo(sessionControlRecord.curLevelUrl, sessionControlRecord); + mediaEngineStalledRecord.MediaDur = data.mediaDur; + mediaEngineStalledRecord.BitRnk = varInfo.brRnk; + mediaEngineStalledRecord.Codecs = varInfo.codecs; + mediaEngineStalledRecord.LastLikelyToKeepUp = now - sessionControlRecord.lastLikelyToKeepUpTime; + mediaEngineStalledRecord.LastSwitch = now - sessionControlRecord.lastSwitchTime; + mediaEngineStalledRecord.StallDetectionTime = data.stallDurationMs; + mediaEngineStalledRecord.StallType = data.type; + mediaEngineStalledRecord.BufferLength = data.bufferLen; + mediaEngineStalledRecord.LaSwDir = sessionControlRecord.lastSwitchDir; + mediaEngineStalledRecord.TargetDur = varInfo.targetduration; + mediaEngineStalledRecord.PlayerIABR = varInfo.avgBandwidth; + mediaEngineStalledRecord.PlayerIBR = varInfo.bandwidth; + mediaEngineStalledRecord.Rate = sessionControlRecord.rate; + mediaEngineStalledRecord.OBRLast = sessionControlRecord.obrLast; + mediaEngineStalledRecord.OBRMean = (playEndedRecord.NetBytes * 8) / (playEndedRecord.ADT / 1000); + this._copyCommonKeys(mediaEngineStalledRecord, playEndedRecord); + }); + this._aggregateTimes(itemId); + } + _prepareEventSwitchComplete(itemId, data) { + this.update(itemId, ({ sessionControlRecord, switchCompleteRecord, playEndedRecord }) => { + const fromVarInfo = this._getVariantInfo(sessionControlRecord.prevLevelUrl, sessionControlRecord); + const toVarInfo = this._getVariantInfo(sessionControlRecord.curLevelUrl, sessionControlRecord); + switchCompleteRecord.FrBitRnk = fromVarInfo.brRnk; + switchCompleteRecord.ToBitRnk = toVarInfo.brRnk; + switchCompleteRecord.TimeToBitrate = Date.now() - sessionControlRecord.sessionStartTime; + switchCompleteRecord.MediaDur = data.mediaDur; + switchCompleteRecord.Rate = sessionControlRecord.rate; + switchCompleteRecord.PlayerIBR = toVarInfo.bandwidth; + switchCompleteRecord.PlayerIABR = toVarInfo.avgBandwidth; + switchCompleteRecord.LastPlayerIBR = fromVarInfo.bandwidth; + switchCompleteRecord.LastPlayerIABR = fromVarInfo.avgBandwidth; + switchCompleteRecord.ReWd = toVarInfo.width; + switchCompleteRecord.ReHt = toVarInfo.height; + this._copyCommonKeys(switchCompleteRecord, playEndedRecord); + }); + this._aggregateTimes(itemId); + } + _prepareEventVariantEnded(itemId, data) { + this.update(itemId, ({ sessionControlRecord, variantEndedRecord, playEndedRecord }) => { + const varInfo = this._getVariantInfo(sessionControlRecord.curLevelUrl, sessionControlRecord); + variantEndedRecord.Rate = sessionControlRecord.rate; + variantEndedRecord.VarAvgBitrate = varInfo.avgBandwidth; + variantEndedRecord.VarPeakBitrate = varInfo.bandwidth; + variantEndedRecord.VarBitRk = varInfo.brRnk; + variantEndedRecord.VarSTTime = sessionControlRecord.variantStartTimeMedia; + variantEndedRecord.VarEndTime = data.currentTime * 1000; + variantEndedRecord.IFR = varInfo.framerate; + variantEndedRecord.ODR = sessionControlRecord.decodedFramesForVariant / sessionControlRecord.decodedFramesForVariantSampleCount; + variantEndedRecord.ReWd = varInfo.width; + variantEndedRecord.ReHt = varInfo.height; + variantEndedRecord.Codecs = varInfo.codecs; + variantEndedRecord.AvgVideoBitrate = ((sessionControlRecord.variantVideoBytes || 0) * 8) / (sessionControlRecord.variantVideoDuration || 1); + variantEndedRecord.AvgAudioBitrate = ((sessionControlRecord.variantAudioBytes || 0) * 8) / (sessionControlRecord.variantAudioDuration || 1); + this._copyCommonKeys(variantEndedRecord, playEndedRecord); + }); + this._aggregateTimes(itemId); + } + _prepareEventNwError(itemId, data) { + this.update(itemId, ({ sessionControlRecord, nwErrorRecord, playEndedRecord }) => { + const varInfo = this._getVariantInfo(sessionControlRecord.curLevelUrl, sessionControlRecord); + nwErrorRecord.ErrCode = data.details; + nwErrorRecord.ErrReason = data.code; + nwErrorRecord.ErrIsFatal = data.fatal; + nwErrorRecord.NwErrCount = data.count || 1; + nwErrorRecord.ErrDomain = 'networkError'; + nwErrorRecord.PlayTime = playEndedRecord.PlayTime; + nwErrorRecord.PlayTimeWC = playEndedRecord.PlayTimeWC; + nwErrorRecord.BitRnk = varInfo.brRnk; + nwErrorRecord.Rate = sessionControlRecord.rate; + nwErrorRecord.KeyErrorType = sessionControlRecord.lastKeyErrorType; + nwErrorRecord.KeyDeliveryTime = sessionControlRecord.lastKeyDeliveryTime; + this._copyCommonKeys(nwErrorRecord, playEndedRecord); + }); + this._aggregateTimes(itemId); + } + _copyCommonKeys(dst, src) { + dst.PlayType = src.PlayType; + dst.LastMediaCDNServer = src.LastMediaCDNServer; + dst.MaxVideoQltyIndex = src.MaxVideoQltyIndex; + dst.MaxReWd = src.MaxReWd; + dst.MaxReHt = src.MaxReHt; + dst.IsGapless = src.IsGapless; + dst.IsAudioOnly = src.IsAudioOnly; + dst.IsFirstItem = src.IsFirstItem; + dst.ItemID = src.ItemID; + dst.ServerInfo = src.ServerInfo; + } + _computeVariantInfo(levels) { + const variantList = {}; + let maxWidth = 0; + let maxHeight = 0; + let maxProd = 0; + let maxVideoQltyIndex = 0; + const maxNormalizedPeak = this._getMaxNormalizedPeak(levels); + levels.forEach((item) => { + if (item.attrs) { + const fourCC = this._getVideoFourCC(item.attrs.CODECS); + const qltyIndex = this._getVideoQualityIndex(fourCC, item.attrs['VIDEO-RANGE']) || 0; + const variantInfo = { + codecs: item.attrs.CODECS, + width: item.width, + height: item.height, + bandwidth: item.attrs.BANDWIDTH, + avgBandwidth: item.attrs['AVERAGE-BANDWIDTH'], + framerate: item.attrs['FRAME-RATE'], + iframes: item.iframes, + brRnk: this._getBitrateRank(item.attrs.BANDWIDTH, fourCC, maxNormalizedPeak), + qltyIndex: qltyIndex, + targetduration: 0, + playTime: 0, + }; + maxVideoQltyIndex = qltyIndex > maxVideoQltyIndex ? qltyIndex : maxVideoQltyIndex; + // store variant data against the url + variantList[item.url] = variantInfo; + } + const prod = item.width * item.height; + if (prod > maxProd) { + maxProd = prod; + maxWidth = item.width; + maxHeight = item.height; + } + }); + const varListString = Object.keys(variantList).length > 0 + ? Object.values(variantList) + .map((item) => item.bandwidth + ':' + item.avgBandwidth) + .join(',') + : undefined; + return { variantList: variantList, varListString: varListString, maxVideoQltyIndex: maxVideoQltyIndex, maxWidth: maxWidth, maxHeight: maxHeight }; + } + _getMaxNormalizedPeak(levels) { + let maxNormalizedPeak = 0; + levels.forEach((level) => { + const attrs = level.attrs; + if (attrs) { + const peak = attrs.BANDWIDTH || 0; + const normPeak = this._getNormalizedPeak(peak, this._getVideoFourCC(attrs.CODECS)); + const curMax = Math.max(peak, normPeak); + maxNormalizedPeak = curMax > maxNormalizedPeak ? curMax : maxNormalizedPeak; + } + }); + return maxNormalizedPeak; + } + _getNormalizedPeak(peak, fourCC) { + // Normalization factor for avc family is 1 and for hevc/dolby family is 1.5. + return typeof RTCVideoQualityIndex[fourCC] === 'object' ? peak * 1.5 : peak * 1; + } + _getVideoFourCC(codecs) { + return codecs + ? codecs + .split(',') + .map((item) => item.split('.')[0].trim()) + .find((item) => !!RTCVideoQualityIndex[item]) + : codecs; + } + _getVideoQualityIndex(fourCC, range) { + return typeof RTCVideoQualityIndex[fourCC] === 'object' ? RTCVideoQualityIndex[fourCC][range] : RTCVideoQualityIndex[fourCC]; + } + _getBitrateRank(peak, fourCC, maxNormalizedPeak) { + const normPeak = Math.max(1, this._getNormalizedPeak(peak, fourCC)); + return Math.ceil((normPeak * 100) / maxNormalizedPeak); + } + _findTimeWeightedValues(playTime, list, curLevel) { + const result = { twBRnk: 0, twIBR: 0, twIABR: 0 }; + if (list) { + let totalBitRk = 0; + let totalTWIB = 0; + let totalPlaytime = 0; + let totalTWIAB = 0; + let totalPlaytimeForAvgBw = 0; + Object.values(list).forEach((item) => { + let varPlayTime = item.playTime; + if (item === list[curLevel]) { + varPlayTime += playTime || 0; + } + if (varPlayTime) { + totalTWIB += item.bandwidth * varPlayTime; + totalBitRk += item.brRnk * varPlayTime; + totalPlaytime += varPlayTime; + // Avg bandwidth is optional, use its playtime only if it's reported + if (item.avgBandwidth) { + totalTWIAB += item.avgBandwidth * varPlayTime; + totalPlaytimeForAvgBw += varPlayTime; + } + } + }); + if (totalPlaytime) { + result.twBRnk = totalBitRk / totalPlaytime; + result.twIBR = totalTWIB / totalPlaytime; + } + if (totalTWIAB && totalPlaytimeForAvgBw) { + result.twIABR = totalTWIAB / totalPlaytimeForAvgBw; + } + } + return result; + } + _computeMediaStats(record) { + const bufferAppendInfo = record.bufferAppendInfo; + let latencies = []; + const sizes = []; + if (bufferAppendInfo && bufferAppendInfo.forEach) { + bufferAppendInfo.forEach((item) => { + latencies.push(item.latency); + sizes.push(item.size); + }); + } + const bufLatencyInfo = this._computeStats(latencies); + const bufSizeInfo = this._computeStats(sizes); + // calculate seek stats + const seekInfo = record.seekInfo; + latencies = []; + if (seekInfo && seekInfo.forEach) { + record.seekInfo.forEach((item) => latencies.push(item.latency)); + } + const seekLatencyInfo = this._computeStats(latencies); + // calculate play stats + const playInfo = record.playInfo; + latencies = []; + if (playInfo && playInfo.forEach) { + record.playInfo.forEach((item) => latencies.push(item.latency)); + } + const playLatencyInfo = this._computeStats(latencies); + return { bufLatencyInfo, bufSizeInfo, seekLatencyInfo, playLatencyInfo }; + } + _computeStats(inputArr) { + let max; + let median; + if (inputArr) { + const arr = inputArr.filter((value) => value > 0); // ensure there are only positive values + if (arr.length > 0) { + max = arr.reduce((a, b) => { + return Math.max(a, b); + }); + const len = arr.length; + const sortedArr = arr.sort((a, b) => a - b); + const mid = Math.ceil(len / 2); + median = len % 2 === 0 ? (sortedArr[mid] + sortedArr[mid - 1]) / 2 : sortedArr[mid - 1]; + } + } + return { max, median }; + } + _getVariantInfo(url, record) { + return url && record.manifestData && record.manifestData.variantList && record.manifestData.variantList[url] ? record.manifestData.variantList[url] : {}; + } + _setTargetDuration(url, targetduration, record) { + if (url && record.manifestData && record.manifestData.variantList && record.manifestData.variantList[url]) { + record.manifestData.variantList[url].targetduration = targetduration; + } + } + _isBadSw(curSwDir, lastSwitchDir, curLevelIsIframe, lastLevelIsIframe, curSwitchTime, lastSwitchTime, isSeeking) { + const minSwitchWindow = 10000; // if we reverse the switch direction within this window, it's considered as a bad switch + let isBadSw = false; + if (curSwitchTime - lastSwitchTime < minSwitchWindow && lastSwitchDir !== curSwDir && lastLevelIsIframe === curLevelIsIframe && !isSeeking) { + isBadSw = true; + } + return isBadSw; + } + _aggregateTimes(itemId) { + this.update(itemId, ({ sessionControlRecord, playLikelyToKeepUpRecord, playStalledRecord, mediaEngineStalledRecord, switchCompleteRecord, playRateChangedRecord, variantEndedRecord, playErrorRecord, nwErrorRecord, periodicRecord, playEndedRecord, }) => { + const now = Date.now(); + const eventTime = now - sessionControlRecord.eventStartTime; + const state = sessionControlRecord.state; + switch (state) { + case 'RTC_STATE_INIT': { + playLikelyToKeepUpRecord.StartupTime = (playLikelyToKeepUpRecord.StartupTime || 0) + eventTime; + periodicRecord.InitTime = (periodicRecord.InitTime || 0) + eventTime; + playEndedRecord.InitTime = (playEndedRecord.InitTime || 0) + eventTime; + break; + } + case 'RTC_STATE_CANPLAY': { + playRateChangedRecord.StartupTime = (playRateChangedRecord.StartupTime || 0) + eventTime; + periodicRecord.InitTime = (periodicRecord.InitTime || 0) + eventTime; + playEndedRecord.InitTime = (playEndedRecord.InitTime || 0) + eventTime; + break; + } + case 'RTC_STATE_PAUSE': { + periodicRecord.PauseTime = (periodicRecord.PauseTime || 0) + eventTime; + playEndedRecord.PauseTime = (playEndedRecord.PauseTime || 0) + eventTime; + playRateChangedRecord.LastPause = (playRateChangedRecord.LastPause || 0) + eventTime; + playEndedRecord.LastPause = (playEndedRecord.LastPause || 0) + eventTime; + playErrorRecord.LastPause = (playErrorRecord.LastPause || 0) + eventTime; + nwErrorRecord.LastPause = (nwErrorRecord.LastPause || 0) + eventTime; + break; + } + case 'RTC_STATE_STALL': { + variantEndedRecord.StallTime = (variantEndedRecord.StallTime || 0) + eventTime; + periodicRecord.StallTime = (periodicRecord.StallTime || 0) + eventTime; + playEndedRecord.StallTime = (playEndedRecord.StallTime || 0) + eventTime; + playRateChangedRecord.LastStall = (playRateChangedRecord.LastStall || 0) + eventTime; + playErrorRecord.LastStall = (playErrorRecord.LastStall || 0) + eventTime; + playEndedRecord.LastStall = (playEndedRecord.LastStall || 0) + eventTime; + break; + } + case 'RTC_STATE_MEDIAENGINESTALL': { + variantEndedRecord.MediaEngineStallTime = (variantEndedRecord.MediaEngineStallTime || 0) + eventTime; + periodicRecord.MediaEngineStallTime = (periodicRecord.MediaEngineStallTime || 0) + eventTime; + playEndedRecord.MediaEngineStallTime = (playEndedRecord.MediaEngineStallTime || 0) + eventTime; + playRateChangedRecord.LastMediaEngineStall = (playRateChangedRecord.LastMediaEngineStall || 0) + eventTime; + playErrorRecord.LastMediaEngineStall = (playErrorRecord.LastMediaEngineStall || 0) + eventTime; + playEndedRecord.LastMediaEngineStall = (playEndedRecord.LastMediaEngineStall || 0) + eventTime; + break; + } + case 'RTC_STATE_NWERROR': { + playEndedRecord.NwErrTime = (playEndedRecord.NwErrTime || 0) + eventTime; + periodicRecord.NwErrTime = (periodicRecord.NwErrTime || 0) + eventTime; + break; + } + case 'RTC_STATE_PLAYERROR': { + periodicRecord.PlayErrTime = (periodicRecord.PlayErrTime || 0) + eventTime; + playEndedRecord.PlayErrTime = (playEndedRecord.PlayErrTime || 0) + eventTime; + break; + } + case 'RTC_STATE_PLAY': { + playRateChangedRecord.RateChangePlayTime = (playRateChangedRecord.RateChangePlayTime || 0) + eventTime; + switchCompleteRecord.PlayTime = (switchCompleteRecord.PlayTime || 0) + eventTime; + switchCompleteRecord.PlayTimeLastSW = (switchCompleteRecord.PlayTimeLastSW || 0) + eventTime * (sessionControlRecord.oldRate / 100); + playStalledRecord.LastResume = (playStalledRecord.LastResume || 0) + eventTime; + mediaEngineStalledRecord.LastResume = (mediaEngineStalledRecord.LastResume || 0) + eventTime; + playErrorRecord.LastResume = (playErrorRecord.LastResume || 0) + eventTime; + nwErrorRecord.LastResume = (nwErrorRecord.LastResume || 0) + eventTime; + variantEndedRecord.VarPlayTimeWC = (variantEndedRecord.VarPlayTimeWC || 0) + eventTime; + variantEndedRecord.VarPlayTime = (variantEndedRecord.VarPlayTime || 0) + eventTime * (sessionControlRecord.oldRate / 100); + periodicRecord.PlayTimeWC = (periodicRecord.PlayTimeWC || 0) + eventTime; + periodicRecord.PlayTime = (periodicRecord.PlayTime || 0) + eventTime * (sessionControlRecord.oldRate / 100); + playEndedRecord.PlayTimeWC = (playEndedRecord.PlayTimeWC || 0) + eventTime; + playEndedRecord.PlayTime = (playEndedRecord.PlayTime || 0) + eventTime * (sessionControlRecord.oldRate / 100); + // Update stall playtime + mediaEngineStalledRecord.PlayTime = playEndedRecord.PlayTime; + playStalledRecord.PlayTime = playEndedRecord.PlayTime; + // Update the playtime for the current level to calculate time weighted values + const curLevelUrl = sessionControlRecord.curLevelUrl; + let variantInfo = sessionControlRecord.intervalVariantList[curLevelUrl]; + variantInfo.playTime = (variantInfo.playTime || 0) + eventTime; + variantInfo = sessionControlRecord.sessionVariantList[curLevelUrl]; + variantInfo.playTime = (variantInfo.playTime || 0) + eventTime; + break; + } + } + }); + } + } + + const loggerName$4 = { name: 'rtc-service' }; + const defaultIntervalValue = 300000; + /* + * @brief RTC service is responsible for subscribing to queries and updating the RTC store. + */ + class RTCService { + constructor(hls, config, accessLog, logger) { + this.hls = hls; + this.config = config; + this.accessLog = accessLog; + this.logger = logger; + this.destroy$ = new Subject(); + this.isSeeking = false; + this.seekStart = null; + this.periodicInterval = config.rtcIntervalTimeout ? config.rtcIntervalTimeout : defaultIntervalValue; + this.intervalFunc = null; + this.rtcStore = new RTCStore(this.logger); + this.rtcQuery = new RTCQuery(this.rtcStore, this.logger); + this.rtcComponent = new RTCComponent(this.rtcQuery, this.logger); + accessLog.setRTCQuery(this.rtcQuery); + this.subscribeAndUpdateStore(); + this.registerForEvents(); + } + destroy() { + this.destroy$.next(); + this.clearPeriodic(); + this.rtcStore.reset(); + } + detachMedia() { + this.clearPeriodic(); + let t = 0; + try { + // if detach was as a result of an error, this will throw. + t = this.hls.realCurrentTime; + this.logger.qe({ critical: true, name: 'playEnded', data: { itemId: this.rtcEventItemId(true) } }); + this.rtcStore.updateVariantEnd(this.rtcEventItemId(true), { currentTime: t }); + this.sendAndFinalize(this.rtcEventItemId(true), RTCEventGroup.VariantEnded); + this.rtcStore.updatePeriodic(this.rtcEventItemId(true), true); + this.sendAndFinalize(this.rtcEventItemId(true), RTCEventGroup.Periodic); + this.rtcStore.updateEnded(this.rtcEventItemId(true)); + this.sendAndFinalize(this.rtcEventItemId(true), RTCEventGroup.PlayEnded); + } + catch (err) { + this.logger.warn(loggerName$4, err); + } + } + handleError(error) { + var _a, _b; + let newError; + if (error instanceof HlsError) { + newError = error; + } + else { + newError = new ExceptionError(true, error.message, ErrorResponses.InternalError); + } + if (newError instanceof PlaylistNetworkError && !newError.fatal) { + this.rtcStore.updateLevelLoadError(this.rtcEventItemId(), { url: newError.url, mediaDur: this.hls.bufferedDuration, isSeeking: this.isSeeking }); + this.sendAndFinalize(this.rtcEventItemId(), RTCEventGroup.SwitchComplete); + } + else if (newError.type === ErrorTypes.NETWORK_ERROR) { + this.rtcStore.updateNwError(this.rtcEventItemId(), { fatal: newError.fatal, details: newError.details, code: (_a = newError.response) === null || _a === void 0 ? void 0 : _a.code }); + this.sendAndFinalize(this.rtcEventItemId(), RTCEventGroup.NwError); + } + else { + this.rtcStore.updateMediaError(this.rtcEventItemId(), { fatal: newError.fatal, details: newError.details, code: (_b = newError.response) === null || _b === void 0 ? void 0 : _b.code }); + this.sendAndFinalize(this.rtcEventItemId(), RTCEventGroup.PlayError); + } + } + handleMediaElementError(err) { + this.rtcStore.updateMediaElementError(this.rtcEventItemId(), err); + this.sendAndFinalize(this.rtcEventItemId(), RTCEventGroup.PlayError); + } + handleFragLoaded(frag, stats) { + var _a; + this.logger.trace(loggerName$4, 'MediaFragment=%o, Stats=%o', fragPrint(frag), stats); + if (!this.checkMediaOptionType(frag.mediaOptionType)) { + return; + } + if (frag.itemId !== this.rtcEventItemId()) { + this.logger.warn(loggerName$4, `Frag id does not match current item id. Frag Id=${frag.itemId}, playing id=${this.rtcEventItemId(true)}, loading id=${(_a = this.hls.loadingItem) === null || _a === void 0 ? void 0 : _a.itemId}`); + } + const adt = stats.tload - stats.trequest; + const processTime = stats.tload - stats.tfirst; + const serverInfo = this.serverInfoInstance ? this.serverInfoInstance : {}; + this.rtcStore.updateFragLoaded(frag.itemId, { + fragType: frag.mediaOptionType, + bytes: stats.loaded, + duration: frag.duration, + adt: adt, + processTime: processTime, + contentType: stats.contentType, + cdnServer: stats.cdnServer, + serverInfo: serverInfo, + }); + this.accessLog.updateFragLoaded(frag.itemId, this.isSeeking, { + fragType: frag.mediaOptionType, + bytes: stats.loaded, + duration: frag.duration, + adt: adt, + processTime: processTime, + startPTS: frag.start, + endPTS: frag.start + frag.duration, + }); + } + handleFragBuffered(metric) { + this.logger.trace(loggerName$4, 'Frag Buffered, metrics=%o', metric); + if (!this.checkMediaOptionType(metric.fragmentType)) { + return; + } + const parseTime = metric.endDataAppend - metric.startDataAppend; + this.rtcStore.updateFragBuffered(this.rtcEventItemId(), { fragType: metric.fragmentType, bytes: metric.dataBytesAppend, parseTime: parseTime }); + } + handleLevelLoaded(mediaOptionDetails, stats) { + if (!mediaOptionDetails) { + // This happens on Safari and FireFox, browsers that block autoplay. + // rdar://82312973 + this.logger.warn(`handleLevelLoaded called with mediaOptionDetails as ${mediaOptionDetails}`); + return; + } + if (mediaOptionDetails.itemId !== this.rtcEventItemId()) { + this.logger.warn(loggerName$4, `media option id does not match current item id. media Id=${mediaOptionDetails.itemId}, current id=${this.rtcEventItemId}`); + } + if (mediaOptionDetails.mediaOptionType === MediaOptionType.Variant) { + const adt = stats.tload - stats.trequest; + this.rtcStore.updateLevelLoaded(this.rtcEventItemId(), { + url: mediaOptionDetails.url, + targetduration: mediaOptionDetails.targetduration, + adt: adt, + contentType: stats.contentType, + playType: mediaOptionDetails.type, + }); + } + } + handleLevelSwitched(data) { + const levelData = { url: data.url, isSeeking: this.isSeeking, mediaDur: this.hls.bufferedDuration, currentTime: this.hls.realCurrentTime }; + this.logger.trace(loggerName$4, `RTC level Switched, oldVariant: ${data.oldVariant}, newVariant: ${data.newVariant}`); + if (data.oldVariant) { + // Playing variant ended. + this.rtcStore.updateVariantEnd(this.rtcEventItemId(), { currentTime: this.hls.realCurrentTime }); + this.sendAndFinalize(this.rtcEventItemId(), RTCEventGroup.VariantEnded); + } + // Playback switched to new variant. + this.rtcStore.updateLevelSwitched(this.rtcEventItemId(), levelData); + this.sendAndFinalize(this.rtcEventItemId(), RTCEventGroup.SwitchComplete); + } + handleLevelSwitching(levelUrl) { + this.levelSwitchingUrl = levelUrl; + } + handleLevelsChanged(variantList) { + this.logger.trace(loggerName$4, 'Levels Changed Levels=%o', variantList); + this.rtcStore.updateLevelsChanged(this.rtcEventItemId(), { levels: variantList }); + } + handleManifestParsed(manifestParsedData) { + var _a; + this.logger.trace(loggerName$4, 'RTC Manifest Parsed, levels=%o, stats=%o', manifestParsedData.levels, manifestParsedData.stats); + const adt = manifestParsedData.stats.tload - manifestParsedData.stats.trequest; + this.rtcStore.updateManifestParsed(this.rtcEventItemId(), { + levels: manifestParsedData.levels, + adt: adt, + contentType: manifestParsedData.stats.contentType, + isAudioOnly: this.hls.inGaplessMode, + isGapless: this.hls.inGaplessMode, + isFirstItem: this.hls.isFirstItem, + itemID: (((_a = this.hls.reportingAgent) === null || _a === void 0 ? void 0 : _a.SessionID) || guid()) + '-' + this.rtcEventItemId(), + }); + this.logger.qe({ + critical: true, + name: 'gapless', + data: { + isGapless: this.hls.inGaplessMode, + isAudioOnly: this.hls.inGaplessMode, + isFirstItem: this.hls.isFirstItem, + sessionId: this.hls.sessionID, + itemId: this.rtcEventItemId(), + }, + }); + } + handleSeek(event) { + this.logger.trace(loggerName$4, `RTC seek event: ${event}`); + if (event === 'SEEKING') { + this.isSeeking = true; + this.seekStart = Date.now(); + } + else if (event === 'SEEKED') { + // seeked update store + this.isSeeking = false; + let latency = 0; + if (this.seekStart) { + latency = Date.now() - this.seekStart; + } + this.seekStart = null; + this.logger.trace(loggerName$4, `RTC seeked, latency=${latency}`); + this.rtcStore.updateSeeked(this.rtcEventItemId(true), { latency: latency }); + } + } + handleDesiredRateChanged(oldRate, newRate) { + if (newRate === 0 || (Math.abs(oldRate) > 1 && Math.abs(newRate) > 1)) { + /* Desired rate to actual rate transition is immediate for the following + - any rate to 0 + - between any trick play rates (2x <-> 4x etc.) + So, update the store right away. + */ + this.logger.trace(loggerName$4, `RTC rate changed oldRate=${oldRate} newRate=${newRate}`); + this.logger.qe({ critical: true, name: 'rateChanged', data: { oldRate, newRate } }); + this.rtcStore.updateRateChanged(this.rtcEventItemId(true), { + rate: newRate, + latency: 0, + mediaDur: this.hls.bufferedDuration, + currentTime: this.hls.realCurrentTime, + url: this.levelSwitchingUrl, + }); + this.sendAndFinalize(this.rtcEventItemId(true), RTCEventGroup.PlayRateChanged); + } + else if (oldRate !== 1 && newRate === 1) { + // moving from pause/trickplay to playing rate + this.playStart = Date.now(); + } + /* Save the desired rate and wait for playback to switch to that rate (store update happens later for those cases) */ + this.oldRate = oldRate; + this.newRate = newRate; + } + handleVariantBufferAppended(timestampOffset, totalBytes) { + this.logger.trace(loggerName$4, 'RTC Variant Buffer Appended'); + let latency = 0; + if (timestampOffset) { + latency = Date.now() - timestampOffset; + } + this.rtcStore.updateBufferAppended(this.rtcEventItemId(), { latency: latency, size: totalBytes }); + } + handleStalled(stallInfo, bufferLen) { + this.logger.trace(loggerName$4, 'Stall Info data=%o', stallInfo); + const data = { type: stallInfo.type, stallDurationMs: stallInfo.stallDurationMs, bufferLen: bufferLen, mediaDur: this.hls.bufferedDuration }; + const state = this.rtcQuery.getEntity(this.rtcEventItemId(true)).sessionControlRecord.state; + if (stallInfo.type === StallType.LowBuffer || (stallInfo.type === StallType.Seek && stallInfo.isLowBufferStall)) { + // Low Buffer Stall + if (state !== 'RTC_STATE_PLAY') { + // don't report stall events if we're not playing + this.logger.info(loggerName$4, `skipping low buffer stall event because we're not playing, state: ${state}`); + } + else { + this.rtcStore.updateBufferStalled(this.rtcEventItemId(true), data); + this.sendAndFinalize(this.rtcEventItemId(true), RTCEventGroup.PlayStalled); + } + } + else { + // High Buffer Stall + if (state !== 'RTC_STATE_PLAY') { + // don't report stall events if we're not playing + this.logger.info(loggerName$4, `skipping high buffer stall event because we're not playing, state: ${state}`); + } + else { + this.rtcStore.updateMediaEngineStalled(this.rtcEventItemId(true), data); + this.sendAndFinalize(this.rtcEventItemId(true), RTCEventGroup.MediaEngineStalled); + } + } + } + handlePlaybackInfo(droppedVideoFrames, decodedFrameCount) { + this.logger.trace(loggerName$4, `RTC Playback Info, droppedVideoFrames=${droppedVideoFrames}, decodedFrameCount=${decodedFrameCount}`); + this.rtcStore.updatePlaybackInfo(this.rtcEventItemId(true), { droppedVideoFrames: droppedVideoFrames, decodedFrameCount: decodedFrameCount }); + this.accessLog.updatePlaybackInfo(this.rtcEventItemId(true), { droppedVideoFrames: droppedVideoFrames, decodedFrameCount: decodedFrameCount }); + } + checkMediaOptionType(mediaOptionType) { + if (mediaOptionType === MediaOptionType.Variant || mediaOptionType === MediaOptionType.AltAudio) { + return true; + } + else { + this.logger.error(loggerName$4, 'Should not have media option type = "%s" in RTC', MediaOptionNames[mediaOptionType]); + return false; + } + } + // Return currently playing id or loading item id if preloading. + // Force return playing id if forcePlayingId is set to true. + rtcEventItemId(forcePlayingId = false) { + if (this.hls.isPreloading) { + if (forcePlayingId) { + return this.hls.playingItem.itemId; + } + return this.hls.loadingItem.itemId; + } + return this.hls.currentItem.itemId; + } + subscribeAndUpdateStore() { + this.hls.publicQueries$ + .pipe(switchMap(([, mediaElementQuery]) => { + return this.mediaElementQueryListener(mediaElementQuery); + }), takeUntil(this.destroy$)) + .subscribe(); + this.hls.itemQueue.activeItemById$ + .pipe(tap((item) => { + var _a, _b; + if (item) { + // if this is an internal build, always send RTC + // if D&U flag is true, use the configuration + // if there is no userInfo use the configuration + let enableRtc = false; + if (this.hls.userInfo) { + if (this.hls.userInfo.internalBuild) { + enableRtc = true; + } + else if (this.hls.userInfo.diagnosticsAndUsage) { + enableRtc = this.config.enableRtcReporting; + } + } + else { + enableRtc = this.config.enableRtcReporting; + } + if (enableRtc) { + // set reporting agent. + const reportingAgent = this.hls.reportingAgent; + if (reportingAgent) { + this.rtcComponent.setReportingAgent(reportingAgent); + this.logger.qe({ critical: true, name: 'rtcStart', data: { baseSessionId: reportingAgent.SessionID, itemId: (_a = this.hls.itemQueue.activeItem) === null || _a === void 0 ? void 0 : _a.itemId } }); + } + else { + this.logger.warn(loggerName$4, '[RTCA] - Reporting is enabled but reportingAgent is null'); + } + } + else { + // RTC is disabled. + this.rtcComponent.setReportingAgent(null); + this.logger.info(loggerName$4, '[RTCA] - Reporting is disabled'); + } + this.serverInfoInstance = null; + // create new entity with item.itemId + const id = item.itemId; + this.rtcStore.createEntity(id); + // if this is the first item or we are not in gapless mode, start the periodic timer + if (this.hls.isFirstItem || !this.hls.inGaplessMode) { + this.setPeriodic(id); + } + this.logger.trace(`RTC Manifest loading: ${(_b = this.hls.itemQueue.activeItem) === null || _b === void 0 ? void 0 : _b.url}`); + } + }), takeUntil(this.destroy$)) + .subscribe(); + } + itemTransitioned(oldItem, newItem) { + // item transitioned + this.logger.trace(loggerName$4, `RTC Item transitioned oldItem=${oldItem} newItem=${newItem}`); + this.rtcStore.updateVariantEnd(oldItem, { currentTime: this.hls.realCurrentTime }); + this.sendAndFinalize(oldItem, RTCEventGroup.VariantEnded); + this.rtcStore.updatePeriodic(oldItem, true); + this.sendAndFinalize(oldItem, RTCEventGroup.Periodic); + this.rtcStore.updateEnded(oldItem); + this.sendAndFinalize(oldItem, RTCEventGroup.PlayEnded); + // attach/subscribe to timer for periodic events. Note set periodic calls clearPeriodic + this.setPeriodic(newItem); + } + mediaElementQueryListener(mediaElementQuery) { + const playingSource$ = mediaElementQuery.gotPlaying$.pipe(tap((playing) => { + if (playing) { + const oldRate = this.oldRate; + const newRate = this.newRate || 1; // set 1 for default cases, e.g. autoplay + if (Math.abs(oldRate) > 1 && Math.abs(newRate) > 1) { + return; // Ignore gotPlaying notifications for transition between trick play rates + } + this.logger.info(loggerName$4, `RTC rate changed oldRate=${oldRate} newRate=${newRate}`); + this.logger.qe({ critical: true, name: 'rateChanged', data: { oldRate, newRate } }); + this.rtcStore.updateCanPlay(this.rtcEventItemId(true), { mediaDur: this.hls.bufferedDuration }); + this.sendAndFinalize(this.rtcEventItemId(true), RTCEventGroup.PlayLikelyToKeepUp); + this.rtcStore.updateRateChanged(this.rtcEventItemId(true), { + rate: newRate, + latency: isFiniteNumber(this.playStart) ? Date.now() - this.playStart : 0, + mediaDur: this.hls.bufferedDuration, + currentTime: this.hls.realCurrentTime, + url: this.levelSwitchingUrl, + }); + this.sendAndFinalize(this.rtcEventItemId(true), RTCEventGroup.PlayRateChanged); + } + })); + return playingSource$; + } + // Event based + registerForEvents() { + const target = fromEventTarget(this.hls, this); + merge(target.event(HlsEvent.KEY_REQUEST_STARTED, this.keyRequestStarted, this), target.event(HlsEvent.KEY_LOADED, this.keyLoaded, this)).pipe(takeUntil(this.destroy$)).subscribe(); + } + keyRequestStarted(data) { + this.logger.trace(loggerName$4, 'RTC key request started %o', data); + data.timestamp = Date.now(); + this.rtcStore.updateLicenseChallengeRequested(this.rtcEventItemId(), data); + } + keyLoaded(data) { + this.logger.trace(loggerName$4, 'RTC key loaded, data: %o', data); + data.timestamp = Date.now(); + data.currentTime = this.hls.realCurrentTime; + this.rtcStore.updateSegmentKeyLoaded(this.rtcEventItemId(), data); + } + // End of Event based + // Key session handler methods + licenseChallengeReceived(data) { + this.logger.trace(loggerName$4, 'RTC licenseChallenge received data: %o', data); + this.rtcStore.updateLicenseChallengeReceived(this.rtcEventItemId(), { timestamp: Date.now(), keyuri: data.keyuri }); + } + licenseChallengeSubmitted(data) { + this.logger.trace(loggerName$4, 'RTC licenseChallenge submitted data: %o', data); + this.rtcStore.updateLicenseChallengeSubmitted(this.rtcEventItemId(), { timestamp: Date.now(), keyFormat: data.keyFormat, keyuri: data.keyuri }); + } + licenseChallengeCreated(data) { + this.logger.trace(loggerName$4, 'RTC licenseChallenge created data: %o', data); + this.rtcStore.updateLicenseChallengeCreated(this.rtcEventItemId(), { timestamp: Date.now(), cdmVersion: data.cdmVersion, keyuri: data.keyuri }); + this.rtcStore.updateLicenseResponseRequested(this.rtcEventItemId(), { timestamp: Date.now(), keyuri: data.keyuri }); // both licenseChallengeCreated and licenseResponseRequested happen simultaneouly + } + licenseResponseSubmitted(data) { + this.logger.trace(loggerName$4, 'RTC licenseResponse submitted data: %o', data); + this.rtcStore.updateLicenseResponseReceived(this.rtcEventItemId(), { timestamp: Date.now(), keyuri: data.keyuri }); // both licenseResponseReceived and licenseResponseSubmitted happen simultaneouly + this.rtcStore.updateLicenseResponseSubmitted(this.rtcEventItemId(), { timestamp: Date.now(), keyuri: data.keyuri }); + } + licenseResponseProcessed(data) { + this.logger.trace(loggerName$4, 'RTC licenseResponse processed data: %o', data); + this.rtcStore.updateLicenseResponseProcessed(this.rtcEventItemId(), { timestamp: Date.now(), keyuri: data.keyuri, currentTime: this.hls.realCurrentTime }); + this.sendAndFinalize(this.rtcEventItemId(), RTCEventGroup.KeySessionComplete); + } + licenseChallengeError(data) { + this.logger.trace(loggerName$4, 'RTC licenseChallenge error data: %o', data); + this.rtcStore.updateLicenseChallengeError(this.rtcEventItemId(), { timestamp: Date.now(), keyuri: data.keyuri }); + this.sendAndFinalize(this.rtcEventItemId(), RTCEventGroup.KeySessionComplete); + } + licenseResponseError(data) { + this.logger.trace(loggerName$4, 'RTC licenseResponse error data: %o', data); + this.rtcStore.updateLicenseResponseError(this.rtcEventItemId(), { timestamp: Date.now(), keyuri: data.keyuri }); + this.sendAndFinalize(this.rtcEventItemId(), RTCEventGroup.KeySessionComplete); + } + keyAborted(data) { + var _a; + this.logger.trace(loggerName$4, 'RTC key aborted data: %o', data); + if (((_a = this.rtcQuery.getEntity(this.rtcEventItemId())) === null || _a === void 0 ? void 0 : _a.sessionControlRecord.activeKeySessions[data.keyuri]) != null) { + this.rtcStore.updateKeyAborted(this.rtcEventItemId(), { timestamp: Date.now(), keyuri: data.keyuri }); + this.sendAndFinalize(this.rtcEventItemId(), RTCEventGroup.KeySessionComplete); + } + else { + this.logger.warn(`keyAbort called without active key session ${redactUrl(data.keyuri)}`); + } + } + // End of key session handler methods + setPeriodic(id) { + this.clearPeriodic(); + this.intervalFunc = setInterval(this.handlePeriodic.bind(this, id), this.periodicInterval); + } + handlePeriodic(id) { + this.logger.trace(loggerName$4, `sendPeriodic id=${id}`); + this.rtcStore.updatePeriodic(id, false); + this.sendAndFinalize(id, RTCEventGroup.Periodic); + } + clearPeriodic() { + if (this.intervalFunc) { + clearInterval(this.intervalFunc); + } + this.intervalFunc = null; + } + sendAndFinalize(id, rtcEvent) { + this.accessLog.addPlayTime(id); + switch (rtcEvent) { + case RTCEventGroup.PlayEnded: + this.rtcComponent.sendPlayEnded(id); + break; + case RTCEventGroup.Periodic: + this.rtcComponent.sendPeriodic(id); + break; + case RTCEventGroup.PlayStalled: + this.accessLog.updateStallCount(id); + this.rtcComponent.sendPlayStalled(id); + break; + case RTCEventGroup.KeySessionComplete: + this.rtcComponent.sendKeySessionComplete(id); + break; + case RTCEventGroup.PlayLikelyToKeepUp: + this.accessLog.updateCanPlay(id); + this.rtcComponent.sendPlayLikelyToKeepUp(id); + break; + case RTCEventGroup.PlayRateChanged: + this.rtcComponent.sendPlayRateChange(id); + break; + case RTCEventGroup.PlayError: + this.accessLog.addToErrorLog(id, 'mediaError'); + this.rtcComponent.sendPlayError(id); + break; + case RTCEventGroup.MediaEngineStalled: + this.accessLog.updateMediaEngineStallCount(id); + this.rtcComponent.sendMediaEngineStalled(id); + break; + case RTCEventGroup.SwitchComplete: + this.accessLog.addToAccessLog(id); + this.rtcComponent.sendSwitchComplete(id); + break; + case RTCEventGroup.VariantEnded: + this.rtcComponent.sendVariantEnded(id); + break; + case RTCEventGroup.NwError: + this.accessLog.addToErrorLog(id, 'networkError'); + this.rtcComponent.sendNwError(id); + break; + default: + this.logger.error(loggerName$4, `Unknown rtc event eventGroupId:${id}`); + return; + } + this.rtcStore.finalize(id, rtcEvent); + } + } + + const genericTimeWeightedAggregation = (power) => { + return (entries, windowStartTimestamp) => { + let totalValue = 0; + let totalWeight = 0; + for (const { timestamp, value } of entries) { + // Math.max(0, ...) to guard against privacy.resistFingerprinting or privacy.reduceTimerPrecision, + // which may leave expired entries in the array. + const weight = Math.pow(Math.max(0, timestamp - windowStartTimestamp) / 1000, power); + totalValue += weight * value; + totalWeight += weight; + } + return totalValue / totalWeight; + }; + }; + const AggregationImplementations = { + 'uniform-time-weighted': genericTimeWeightedAggregation(0), + 'linear-time-weighted': genericTimeWeightedAggregation(1), + 'quadratic-time-weighted': genericTimeWeightedAggregation(2), + }; + + /** + * Returns a new array of intervals where overlapping intervals are joined together + * into non-overlapping intervals. + * @param intervals An array of intervals + * @param join A function that takes in two overlapping intervals and joins them + * + * @note this function only joins strict overlaps, interval pairs such as ((1, 2), (2, 3)) + * are not joined. + */ + function accumulateBW(intervals) { + const sorted_intervals = [...intervals].sort((a, b) => { + if (a.start !== b.start) { + return a.start - b.start; + } + else { + return a.end - b.end; + } + }); + const result = []; + const insertEntry = function (arr, entry) { + if (!arr.length) { + arr.push(entry); + return; + } + for (let i = 0; i < arr.length; i++) { + if (arr[i].start > entry.start || (arr[i].start === entry.start && arr[i].end > entry.end)) { + arr.splice(i, 0, entry); + break; + } + } + }; + while (sorted_intervals.length) { + const interval = sorted_intervals[0]; + sorted_intervals.shift(); + let lastInterval; + if (result.length) { + lastInterval = result[result.length - 1]; + } + if (result.length === 0 || lastInterval.end <= interval.start) { + // No overlap + result.push(interval); + } + else if (interval.start === lastInterval.start) { + // Overlap with same start + if (interval.end === lastInterval.end) { + // same end also .. so just accumulate + lastInterval.bitsPerSec += interval.bitsPerSec; + } + else if (!(interval.end < lastInterval.end)) { + // End of interval in consideration is larger than last interval. + // Accumulate for the overlap. create an new entry for + // extended part and insert it back in sorted entries. + lastInterval.bitsPerSec += interval.bitsPerSec; + interval.start = lastInterval.end; + insertEntry(sorted_intervals, interval); + } + } + else { + // there is initial part that does not overlap + const prevend = lastInterval.end; + const prevBitsPerSec = lastInterval.bitsPerSec; + lastInterval.end = interval.start; + // make a new entry for the overlap + const entry = { + start: interval.start, + end: Math.min(prevend, interval.end), + bitsPerSec: interval.bitsPerSec + prevBitsPerSec, + }; + result.push(entry); + if (prevend !== interval.end) { + // create new entry for the extended part and insert it back + // in sorted entries. + let start = 0, end = 0, bitsPerSec = 0; + if (prevend < interval.end) { + start = prevend; + end = interval.end; + bitsPerSec = interval.bitsPerSec; + } + else { + start = interval.end; + end = prevend; + bitsPerSec = prevBitsPerSec; + } + const newEntry = { start, end, bitsPerSec }; + insertEntry(sorted_intervals, newEntry); + } + } + } + return result; + } + + class HistoryBasedBandwidthEstimator { + constructor(windowSize, aggregationMethod = 'quadratic-time-weighted', initValue = { + avgLatencyMs: NaN, + avgBandwidth: NaN, + }) { + this.windowSize = windowSize; + this.aggregationMethod = aggregationMethod; + this.latencyEntries = []; + this.bandwidthEntries = []; + this.minEntries = 1; + this.cleanUpExpiredEntries = this.cleanUpExpiredEntries.bind(this); + this.bwSubject = new BehaviorSubject(initValue); + } + get estimate$() { + return this.bwSubject.asObservable(); + } + record(details) { + // logger.debug(`Recording Bandwidth entry: ${JSON.stringify(details)}`); + const { trequest, tfirst, tload, bitsDownloaded } = details; + if (trequest === tload) { + return; + } + this.recordLatency(trequest, tfirst); + // tfirst tends to be delayed in the beginning of playback, which + // inflates bandwidth estimation (especially for small downloads) + // Thus we use trequest to get a more stable measurement of + // bandwidth. Note that this will decrease the bandwidth + // estimation by a little (~3% on a 40Mbps network at the office). + this.recordBandwidth(trequest, tload, (bitsDownloaded * 1000) / (tload - trequest)); + if (this.bwSubject.closed) { + return; + } + const estimate = this.getEstimate(); + this.bwSubject.next(estimate); + } + getEstimate() { + if (this.latencyEntries.length < this.minEntries) { + return { + avgLatencyMs: NaN, + avgBandwidth: NaN, + }; + } + const windowStartTimestamp = performance.now() - this.windowSize; + const aggregationFn = AggregationImplementations[this.aggregationMethod]; + const latencyEntries = this.latencyEntries.map(({ start, end }) => ({ + timestamp: end, + value: end - start, + duration: 1, + })); + this.bandwidthEntries = accumulateBW(this.bandwidthEntries); + const bandwidthEntries = this.bandwidthEntries.map(({ start, end, bitsPerSec }) => ({ + timestamp: end, + duration: 1, + value: bitsPerSec, // bits + })); + return { + avgLatencyMs: aggregationFn(latencyEntries, windowStartTimestamp), + avgBandwidth: aggregationFn(bandwidthEntries, windowStartTimestamp), + }; + } + getLatest() { + if (this.latencyEntries.length === 0) { + return { + avgLatencyMs: NaN, + avgBandwidth: NaN, + }; + } + const lastLatency = this.latencyEntries[this.latencyEntries.length - 1]; + const lastBw = this.bandwidthEntries[this.bandwidthEntries.length - 1]; + return { + avgLatencyMs: lastLatency.end - lastLatency.start, + avgBandwidth: lastBw.bitsPerSec, + }; + } + recordLatency(start, end) { + this.latencyEntries.push({ start, end }); + this.updateCleanupTimeout(end); + } + recordBandwidth(start, end, bitsPerSec) { + this.bandwidthEntries.push({ start, end, bitsPerSec }); + this.updateCleanupTimeout(end); + } + setCleanupTimeout(cleanupTimestamp) { + this.cleanupTimeout = setTimeout(this.cleanUpExpiredEntries, Math.max(cleanupTimestamp - performance.now(), 0)); + this.cleanupTimestamp = cleanupTimestamp; + } + clearCleanupTimeout() { + if (typeof this.cleanupTimeout !== 'undefined') { + clearTimeout(this.cleanupTimeout); + this.cleanupTimeout = undefined; + } + this.cleanupTimestamp = undefined; + } + updateCleanupTimeout(timestamp) { + const cleanupTimestamp = timestamp + this.windowSize; + if (!this.cleanupTimestamp || cleanupTimestamp < this.cleanupTimestamp) { + this.clearCleanupTimeout(); + this.setCleanupTimeout(cleanupTimestamp); + } + } + cleanUpExpiredEntries() { + this.clearCleanupTimeout(); + const windowStartTimestamp = performance.now() - this.windowSize; + this.latencyEntries = this.latencyEntries.filter((entry) => entry.end >= windowStartTimestamp); + this.bandwidthEntries = this.bandwidthEntries.filter((entry) => entry.end >= windowStartTimestamp); + // during cleanup + if (!this.bwSubject.closed) { + this.bwSubject.next(this.getEstimate()); + } + if (this.latencyEntries.length > 0 || this.bandwidthEntries.length > 0) { + const timestampOfEarliestEntry = Math.min(...this.latencyEntries.map((entry) => entry.end), ...this.bandwidthEntries.map((entry) => entry.end)); + this.updateCleanupTimeout(timestampOfEarliestEntry); + } + } + destroy() { + this.clearCleanupTimeout(); + } + } + + /** + * Helper functions that deal with storing data on device storage. + */ + const PersistStats = { + setCombinedEstimate: function (hlsStorage, estimate, serviceName) { + const logger = getLogger(); + if (typeof hlsStorage.storage.set === 'undefined') { + logger.warn('storage.set is not supported! Not persisting bandwidth estimates'); + return; + } + // splitting into two different records + const bwStorageKey = hlsStorage.bandwidthHistoryStorageKey; + const bwEstimate = { avgLatencyMs: estimate.avgLatencyMs, avgBandwidth: estimate.avgBandwidth }; + const bwRecord = Object.assign({}, bwEstimate, { expires: Date.now() + hlsStorage.bandwidthHistoryTTL }); + try { + hlsStorage.storage.set(bwStorageKey, JSON.stringify(bwRecord)); + } + catch (err) { + logger.warn(`Error stringifying! Not persisting bandwidth estimates: ${err.message}`); + } + const serviceStats = { + maxDuration: estimate.maxDurationSec, + avgFragParseTimeMs: estimate.avgParseTimeMs, + avgFragBufferCreationDelayMs: estimate.avgBufferCreateMs, + avgPlaylistLoadTimeMs: estimate.avgPlaylistLoadTimeMs, + avgPlaylistParseTimeMs: estimate.avgPlaylistParseTimeMs, + avgInitFragAppendMs: estimate.avgInitFragAppendMs, + avgDataFragAppendMs: estimate.avgDataFragAppendMs, + }; + let storageKey = hlsStorage.storageKeyPrefix; + if (serviceName) { + storageKey += serviceName; + } + try { + hlsStorage.storage.set(storageKey, JSON.stringify(serviceStats)); + } + catch (err) { + logger.warn(`Error stringifying! Not persisting bandwidth estimates: ${err.message}`); + } + }, + getCombinedEstimate: function (hlsStorage, serviceName) { + const logger = getLogger(); + let combinedEstimate = {}; + if (typeof hlsStorage.storage.get === 'undefined') { + logger.warn('storage.get is not supported! unable to retreive bandwidth estimates'); + return this.convertStorageJsonToCombinedEstimate(combinedEstimate); + } + try { + let bwParsed = JSON.parse(hlsStorage.storage.get(hlsStorage.bandwidthHistoryStorageKey)); + if ((bwParsed === null || bwParsed === void 0 ? void 0 : bwParsed.expires) && bwParsed.expires < Date.now()) { + bwParsed = null; + } + else { + bwParsed = { avgLatencyMs: bwParsed === null || bwParsed === void 0 ? void 0 : bwParsed.avgLatencyMs, avgBandwidth: bwParsed === null || bwParsed === void 0 ? void 0 : bwParsed.avgBandwidth }; + } + combinedEstimate = Object.assign(Object.assign({}, combinedEstimate), bwParsed); + } + catch (err) { + logger.warn(`Unable to get persisted bandwidth history: ${err.message}`); + } + let storageKey = hlsStorage.storageKeyPrefix; + if (serviceName) { + storageKey += serviceName; + } + try { + const restEstimateParsed = JSON.parse(hlsStorage.storage.get(storageKey)); + combinedEstimate = Object.assign(Object.assign({}, combinedEstimate), restEstimateParsed); + } + catch (err) { + logger.warn(`Unable to get persisted bandwidth history: ${err.message}`); + } + return this.convertStorageJsonToCombinedEstimate(combinedEstimate); + }, + convertStorageJsonToCombinedEstimate: function (storageJson) { + const combinedEstimate = { + avgLatencyMs: (storageJson === null || storageJson === void 0 ? void 0 : storageJson.avgLatencyMs) || NaN, + avgBandwidth: (storageJson === null || storageJson === void 0 ? void 0 : storageJson.avgBandwidth) || NaN, + maxDurationSec: (storageJson === null || storageJson === void 0 ? void 0 : storageJson.maxDuration) || NaN, + avgParseTimeMs: (storageJson === null || storageJson === void 0 ? void 0 : storageJson.avgFragParseTimeMs) || NaN, + avgBufferCreateMs: (storageJson === null || storageJson === void 0 ? void 0 : storageJson.avgFragBufferCreationDelayMs) || NaN, + avgPlaylistLoadTimeMs: (storageJson === null || storageJson === void 0 ? void 0 : storageJson.avgPlaylistLoadTimeMs) || NaN, + avgPlaylistParseTimeMs: (storageJson === null || storageJson === void 0 ? void 0 : storageJson.avgPlaylistParseTimeMs) || NaN, + avgInitFragAppendMs: (storageJson === null || storageJson === void 0 ? void 0 : storageJson.avgInitFragAppendMs) || NaN, + avgDataFragAppendMs: (storageJson === null || storageJson === void 0 ? void 0 : storageJson.avgDataFragAppendMs) || NaN, + }; + return combinedEstimate; + }, + getBandwidthEstimate: function (hlsStorage, serviceName) { + const estimate = this.getCombinedEstimate(hlsStorage, serviceName); + const bwEstimate = { avgLatencyMs: estimate === null || estimate === void 0 ? void 0 : estimate.avgLatencyMs, avgBandwidth: estimate === null || estimate === void 0 ? void 0 : estimate.avgBandwidth }; + if (!isFiniteNumber(bwEstimate.avgLatencyMs)) { + bwEstimate.avgLatencyMs = NaN; + } + if (!isFiniteNumber(bwEstimate.avgBandwidth)) { + bwEstimate.avgBandwidth = NaN; + } + return bwEstimate; + }, + getPlaylistEstimate: function (hlsStorage, serviceName) { + const estimate = this.getCombinedEstimate(hlsStorage, serviceName); + const playlistEstimate = { avgPlaylistLoadTimeMs: estimate === null || estimate === void 0 ? void 0 : estimate.avgPlaylistLoadTimeMs, avgPlaylistParseTimeMs: estimate === null || estimate === void 0 ? void 0 : estimate.avgPlaylistParseTimeMs }; + if (!isFiniteNumber(playlistEstimate.avgPlaylistLoadTimeMs)) { + playlistEstimate.avgPlaylistLoadTimeMs = NaN; + } + if (!isFiniteNumber(playlistEstimate.avgPlaylistParseTimeMs)) { + playlistEstimate.avgPlaylistParseTimeMs = NaN; + } + return playlistEstimate; + }, + getFragEstimate: function (hlsStorage, serviceName) { + const estimate = this.getCombinedEstimate(hlsStorage, serviceName); + const fragEstimate = { maxDurationSec: estimate === null || estimate === void 0 ? void 0 : estimate.maxDurationSec, avgParseTimeMs: estimate === null || estimate === void 0 ? void 0 : estimate.avgParseTimeMs }; + if (!isFiniteNumber(fragEstimate.maxDurationSec)) { + fragEstimate.maxDurationSec = NaN; + } + if (!isFiniteNumber(fragEstimate.avgParseTimeMs)) { + fragEstimate.avgParseTimeMs = NaN; + } + return fragEstimate; + }, + getBufferEstimate: function (hlsStorage, serviceName) { + const estimate = this.getCombinedEstimate(hlsStorage, serviceName); + const bufferEstimate = { + avgBufferCreateMs: estimate === null || estimate === void 0 ? void 0 : estimate.avgBufferCreateMs, + avgInitFragAppendMs: estimate === null || estimate === void 0 ? void 0 : estimate.avgInitFragAppendMs, + avgDataFragAppendMs: estimate === null || estimate === void 0 ? void 0 : estimate.avgDataFragAppendMs, + }; + if (!isFiniteNumber(bufferEstimate.avgBufferCreateMs)) { + bufferEstimate.avgBufferCreateMs = NaN; + } + if (!isFiniteNumber(bufferEstimate.avgInitFragAppendMs)) { + bufferEstimate.avgInitFragAppendMs = NaN; + } + if (!isFiniteNumber(bufferEstimate.avgDataFragAppendMs)) { + bufferEstimate.avgDataFragAppendMs = NaN; + } + return bufferEstimate; + }, + }; + var PersistStats$1 = PersistStats; + + // Accumulator for doing simple stats + class SimpleAccumulator { + constructor(_minSamples = 0) { + this._minSamples = _minSamples; + this._sum = 0; + this._max = Number.NEGATIVE_INFINITY; + this._numSamples = 0; + } + get avg() { + if (this._numSamples < this._minSamples) { + return NaN; + } + return this._sum / this._numSamples; + } + get max() { + return this.count > 0 ? this._max : NaN; + } + get count() { + return this._numSamples; + } + reset() { + this._sum = 0; + this._numSamples = 0; + this._max = Number.NEGATIVE_INFINITY; + } + add(value) { + this._sum += value; + this._max = Math.max(this._max, value); + ++this._numSamples; + } + } + + /** + * @brief Query interface to the stats store + */ + class StatsQuery extends QueryEntity { + constructor(statsStore, id) { + super(statsStore); + this.id = id; + } + getBandwidthEstimate(hlsStorage, serviceName) { + var _a; + const bwEstimate = Object.assign({}, (_a = this.statsEntity) === null || _a === void 0 ? void 0 : _a.bandwidthEstimate); + if (isFiniteNumber(bwEstimate.avgBandwidth) && isFiniteNumber(bwEstimate.avgLatencyMs)) { + return bwEstimate; + } + // fallback: check in persistent storage + if (hlsStorage) { + const parsedEstimate = PersistStats.getBandwidthEstimate(hlsStorage, serviceName); + if (!isFiniteNumber(bwEstimate.avgBandwidth)) { + bwEstimate.avgBandwidth = parsedEstimate.avgBandwidth; + } + if (!isFiniteNumber(bwEstimate.avgLatencyMs)) { + bwEstimate.avgLatencyMs = parsedEstimate.avgLatencyMs; + } + } + return bwEstimate; + } + getPlaylistEstimate(hlsStorage, serviceName) { + var _a; + const playlistEstimate = Object.assign({}, (_a = this.statsEntity) === null || _a === void 0 ? void 0 : _a.playlistEstimate); + const checkValidity = (playlistEstimate) => { + return isFiniteNumber(playlistEstimate.avgPlaylistLoadTimeMs) && isFiniteNumber(playlistEstimate.avgPlaylistParseTimeMs); + }; + if (checkValidity(playlistEstimate)) { + return playlistEstimate; + } + // fallback: check in persistent storage + if (hlsStorage) { + const parsedEstimate = PersistStats.getPlaylistEstimate(hlsStorage, serviceName); + if (!isFiniteNumber(playlistEstimate.avgPlaylistLoadTimeMs)) { + playlistEstimate.avgPlaylistLoadTimeMs = parsedEstimate.avgPlaylistLoadTimeMs; + } + if (!isFiniteNumber(playlistEstimate.avgPlaylistParseTimeMs)) { + playlistEstimate.avgPlaylistParseTimeMs = parsedEstimate.avgPlaylistParseTimeMs; + } + if (checkValidity(playlistEstimate)) { + return playlistEstimate; + } + //pick config default + if (!isFiniteNumber(playlistEstimate.avgPlaylistLoadTimeMs)) { + playlistEstimate.avgPlaylistLoadTimeMs = hlsStorage.statDefaults.playlistLoadTimeMs; + } + if (!isFiniteNumber(playlistEstimate.avgPlaylistParseTimeMs)) { + playlistEstimate.avgPlaylistParseTimeMs = hlsStorage.statDefaults.playlistParseTimeMs; + } + } + return playlistEstimate; + } + getBufferEstimate(hlsStorage, serviceName) { + var _a; + const bufferEstimate = Object.assign({}, (_a = this.statsEntity) === null || _a === void 0 ? void 0 : _a.bufferEstimate); + const checkValidity = (bufferEstimate) => { + return isFiniteNumber(bufferEstimate.avgBufferCreateMs) && isFiniteNumber(bufferEstimate.avgDataFragAppendMs) && isFiniteNumber(bufferEstimate.avgInitFragAppendMs); + }; + if (checkValidity(bufferEstimate)) { + return bufferEstimate; + } + // fallback: check in persistent storage + if (hlsStorage) { + const parsedEstimate = PersistStats.getBufferEstimate(hlsStorage, serviceName); + if (!isFiniteNumber(bufferEstimate.avgBufferCreateMs)) { + bufferEstimate.avgBufferCreateMs = parsedEstimate.avgBufferCreateMs; + } + if (!isFiniteNumber(bufferEstimate.avgDataFragAppendMs)) { + bufferEstimate.avgDataFragAppendMs = parsedEstimate.avgDataFragAppendMs; + } + if (!isFiniteNumber(bufferEstimate.avgInitFragAppendMs)) { + bufferEstimate.avgInitFragAppendMs = parsedEstimate.avgInitFragAppendMs; + } + if (checkValidity(bufferEstimate)) { + return bufferEstimate; + } + //pick config default + if (!isFiniteNumber(bufferEstimate.avgBufferCreateMs)) { + bufferEstimate.avgBufferCreateMs = hlsStorage.statDefaults.fragBufferCreationDelayMs; + } + if (!isFiniteNumber(bufferEstimate.avgDataFragAppendMs)) { + bufferEstimate.avgDataFragAppendMs = hlsStorage.statDefaults.dataFragAppendMs; + } + if (!isFiniteNumber(bufferEstimate.avgInitFragAppendMs)) { + bufferEstimate.avgInitFragAppendMs = hlsStorage.statDefaults.initFragAppendMs; + } + } + return bufferEstimate; + } + getFragEstimate(hlsStorage, serviceName) { + var _a; + const fragEstimate = Object.assign({}, (_a = this.statsEntity) === null || _a === void 0 ? void 0 : _a.fragEstimate); + const checkValidity = (fragEstimate) => { + return isFiniteNumber(fragEstimate.maxDurationSec) && isFiniteNumber(fragEstimate.avgParseTimeMs); + }; + if (checkValidity(fragEstimate)) { + return fragEstimate; + } + // fallback: check in persistent storage + if (hlsStorage) { + const parsedEstimate = PersistStats.getFragEstimate(hlsStorage, serviceName); + if (!isFiniteNumber(fragEstimate.maxDurationSec)) { + fragEstimate.maxDurationSec = parsedEstimate.maxDurationSec; + } + if (!isFiniteNumber(fragEstimate.avgParseTimeMs)) { + fragEstimate.avgParseTimeMs = parsedEstimate.avgParseTimeMs; + } + if (checkValidity(fragEstimate)) { + return fragEstimate; + } + //pick config default + if (!isFiniteNumber(fragEstimate.maxDurationSec)) { + fragEstimate.maxDurationSec = hlsStorage.defaultTargetDuration; + } + if (!isFiniteNumber(fragEstimate.avgParseTimeMs)) { + fragEstimate.avgParseTimeMs = hlsStorage.statDefaults.fragParseTimeMs; + } + } + return fragEstimate; + } + getCombinedEstimate() { + return Object.assign(Object.assign(Object.assign(Object.assign({}, this.getFragEstimate()), this.getPlaylistEstimate()), this.getBufferEstimate()), this.getBandwidthEstimate()); + } + // getters + get statsEntity() { + return this.getEntity(this.id); + } + get bandwidthSample() { + var _a; + return (_a = this.statsEntity) === null || _a === void 0 ? void 0 : _a.bandwidthSample; + } + get bandwidthStatus() { + var _a; + return (_a = this.statsEntity) === null || _a === void 0 ? void 0 : _a.bandwidthStatus; + } + get fragSample() { + var _a; + return (_a = this.statsEntity) === null || _a === void 0 ? void 0 : _a.fragSample; + } + // Aggregated data + get bandwidthEstimate$() { + return this.selectEntity(this.id, 'bandwidthEstimate'); + } + get fragEstimate$() { + return this.selectEntity(this.id, 'fragEstimate'); + } + get playlistEstimate$() { + return this.selectEntity(this.id, 'playlistEstimate'); + } + get bufferEstimate$() { + return this.selectEntity(this.id, 'bufferEstimate'); + } + // Individual samples + get bandwidthSample$() { + return this.selectEntity(this.id, ({ bandwidthSample }) => bandwidthSample).pipe(filterNullOrUndefined()); + } + get fragSample$() { + return this.selectEntity(this.id, ({ fragSample }) => fragSample).pipe(filterNullOrUndefined()); + } + get playlistSample$() { + return this.selectEntity(this.id, ({ playlistSample }) => playlistSample).pipe(filterNullOrUndefined()); + } + get bufferMetric$() { + return this.selectEntity(this.id, ({ bufferMetric }) => bufferMetric).pipe(filterNullOrUndefined()); + } + } + + /** + * @brief Backing store for stats + */ + /** + * @brief Store that keeps track of stats measured for network requests, + * demuxing, and buffering + */ + class StatsStore extends EntityStore { + constructor() { + super({}, { name: 'stats-store', producerFn: produce_1 }); + } + set statsEntity(statsEntity) { + logAction('statsStore.set.stats'); + applyTransaction(() => { + this.add(statsEntity); + this.setActive(statsEntity.id); + }); + } + set playlistSample(playlistSample) { + logAction(`stats.set.playlistSample: ${playlistSample}`); + this.updateActive((statsEntity) => { + statsEntity.playlistSample = playlistSample; + }); + } + set bandwidthSample(bandwidthSample) { + logAction(`stats.set.bandwidthSample: ${bandwidthSample}`); + this.updateActive((statsEntity) => { + statsEntity.bandwidthSample = bandwidthSample; + statsEntity.bandwidthStatus.bandwidthSampleCount += 1; + statsEntity.bandwidthStatus.instantBw = (bandwidthSample.loaded * 8000) / (bandwidthSample.tload - bandwidthSample.trequest); + }); + } + set fragSample(fragSample) { + logAction(`stats.set.fragSample: ${fragSample}`); + this.updateActive((statsEntity) => { + statsEntity.fragSample = fragSample; + }); + } + set bufferMetric(bufferMetric) { + logAction(`stats.set.bufferMetric: ${bufferMetric}`); + this.updateActive((statsEntity) => { + statsEntity.bufferMetric = bufferMetric; + }); + } + set bandwidthEstimate(bandwidthEstimate) { + logAction(`stats.set.bandwidthEstimate: ${bandwidthEstimate}`); + this.updateActive((statsEntity) => { + statsEntity.bandwidthEstimate = bandwidthEstimate; + }); + } + set fragEstimate(fragEstimate) { + logAction(`stats.set.fragEstimate: ${fragEstimate}`); + this.updateActive((statsEntity) => { + statsEntity.fragEstimate = fragEstimate; + }); + } + set playlistEstimate(playlistEstimate) { + logAction(`stats.set.playlistEstimate: ${playlistEstimate}`); + this.updateActive((statsEntity) => { + statsEntity.playlistEstimate = playlistEstimate; + }); + } + set bufferEstimate(bufferEstimate) { + logAction(`stats.set.bufferEstimate: ${bufferEstimate}`); + this.updateActive((statsEntity) => { + statsEntity.bufferEstimate = bufferEstimate; + }); + } + } + + /** + * @brief Service for managing stats samples and estimates + */ + class StatsService { + constructor(statsStore) { + this.statsStore = statsStore; + } + getQuery() { + return new QueryEntity(this.statsStore); + } + getQueryForItem(itemId) { + return new StatsQuery(this.statsStore, itemId); + } + remove(itemId) { + this.statsStore.remove(itemId); + } + removeAll() { + this.statsStore.remove(); + } + setBandwidthSample(bandwidthSample) { + this.statsStore.bandwidthSample = bandwidthSample; + } + setFragSample(fragSample) { + this.statsStore.fragSample = fragSample; + } + setPlaylistSample(playlistSample) { + this.statsStore.playlistSample = playlistSample; + } + setBufferMetric(bufferMetric) { + this.statsStore.bufferMetric = bufferMetric; + } + setBandwidthEstimate(bandwidthEstimate) { + this.statsStore.bandwidthEstimate = bandwidthEstimate; + } + setFragEstimate(fragEstimate) { + this.statsStore.fragEstimate = fragEstimate; + } + setPlaylistEstimate(playlistEstimate) { + this.statsStore.playlistEstimate = playlistEstimate; + } + setBufferEstimate(bufferEstimate) { + this.statsStore.bufferEstimate = bufferEstimate; + } + } + const statsStore = new StatsStore(); + let statsService = null; // + const createStatsQuery = (id) => { + return new StatsQuery(statsStore, id); + }; + function statsServiceSingleton() { + if (!statsService) { + statsService = new StatsService(statsStore); + } + return statsService; + } + const initializeStatsFunc = (itemId) => { + const statsQuery = createStatsQuery(itemId); + if (statsQuery.hasEntity(itemId)) { + return of(statsQuery); + } + return addStatsEntity(statsStore, itemId); + }; + function addStatsEntity(statsStore, itemId) { + logAction('stats.loading'); + statsStore.setLoading(true); + const statsEntity = { + id: itemId, + // bandwidthSample: { trequest: 0, tfirst: 0, tload: 0, loaded: 0, total: 0, complete: true }, + bandwidthEstimate: { avgLatencyMs: NaN, avgBandwidth: NaN }, + bandwidthStatus: { bandwidthSampleCount: 0, instantBw: NaN }, + // fragSample: { duration: 0, parseTimeMs: 0 }, + fragEstimate: { maxDurationSec: NaN, avgParseTimeMs: NaN }, + // playlistSample: { playlistLoadTimeMs: 0, playlistParseTimeMs: 0 }, + playlistEstimate: { avgPlaylistLoadTimeMs: NaN, avgPlaylistParseTimeMs: NaN }, + // bufferMetric: { + // fragmentType: MediaOptionType.Variant, + // startInitAppend: 0, + // endInitAppend: 0, + // initBytesAppend: 0, + // startDataAppend: 0, + // endDataAppend: 0, + // dataBytesAppend: 0, + // bufferCreationStart: 0, + // bufferCreationEnd: 0, + // }, + bufferEstimate: { + avgBufferCreateMs: NaN, + avgInitFragAppendMs: NaN, + avgDataFragAppendMs: NaN, + }, + }; + statsStore.statsEntity = statsEntity; + statsStore.setLoading(false); + logAction('stats.loaded'); + } + /** + * @brief Service that retrieves past stats and seeds + * the current sample and add to the store + */ + /* + export const initializeStats = () => (pipelineActionSource$: Observable<[RootPlaylistQuery, MediaElementQuery]>) => { + return pipelineActionSource$.pipe( + tag('stats.initializing'), + switchMap(item => { + if (!item) return EMPTY; + + const { itemId } = item; + // const configurationQuery = createConfigurationQuery(); + const statsQuery = createStatsQuery(itemId); + if(statsQuery.hasEntity(itemId)) { + return of(statsQuery); } - destroy() { - this.destroy$.next() - } - subscribeAndEmit() { - var e = this.loaderQueryListener(new bc(Ec)), - t = this.hls.publicQueries$.pipe(La(([e, t]) => an(this.rootPlaylistQueryListener(e, t), this.mediaElementQueryListener(t, e)))); - an(e, t, this.activeItemListener(this.hls.itemQueue)).pipe(Vn(e => { - var t = e.message; - return this.logger.error(`Got error in HlsPlayerEvents ${t}`, e), Ii - }), Va(this.destroy$), Vs(() => {})).subscribe() - } - activeItemListener(e) { - return e.activeItemById$.pipe(Kp(), La(e => { - e = e.url; - return this.hls.trigger(P.MANIFEST_LOADING, { - url: e - }), Ii - })) - } - rootPlaylistQueryListener(t, e) { - var i = t.enabledMediaOptionByType$(gu.Variant).pipe(ln(e => !!e), La(e => { - var t; - return this.hls.trigger(P.LEVEL_SWITCHING, e), null === (t = this.rtc) || void 0 === t || t.handleLevelSwitching(e.url), Ii - })), - r = t.enabledMediaOptionByType$(gu.Variant).pipe(La(i => Dg(i).mediaOptionDetailsEntity$.pipe(ln(e => !0 === (null == e ? void 0 : e.detailsLoading)), Za(e => { - var t = { - url: le(null == i ? void 0 : i.url), - level: i.mediaOptionId, - type: Nu[i.mediaOptionType] - }; - return this.hls.trigger(P.LEVEL_LOADING, t), Ii - })))), - n = t.enabledMediaOptionByType$(gu.Variant).pipe(La(e => { - const t = Dg(e); - let i = 0; - return t.mediaOptionDetailsEntity$.pipe(Kp(), ln(e => { - var t = null !== e.stats && !1 === e.detailsLoading && e.lastUpdateMillis > i; - return i = null !== (e = e.lastUpdateMillis) && void 0 !== e ? e : 0, t - })) - }), La(e => { - var t = e.mediaOptionDetails, - i = e.stats, - r = { - mediaOptionId: t.mediaOptionId, - details: t, - playlistType: t.type, - stats: i - }; - if (null === (i = this.rtc) || void 0 === i || i.handleLevelLoaded(t, r.stats), this.hls.trigger(P.LEVEL_LOADED, r), 0 === e.unchangedCount) { - const e = { - level: 0, - details: t - }; - this.hls.trigger(P.LEVEL_UPDATED, e) - } - if (null != t && t.daterangeTags) { - const e = { - daterangeTags: t.daterangeTags - }; - this.hls.trigger(P.DATERANGE_UPDATED, e) - } - return Ii - })), - s = t.enableMediaOptionSwitchedForType$(gu.AltAudio).pipe(La(e => { - e = t.alternateMediaOptionById(gu.AltAudio, e.mediaOptionId); - return e && this.triggerAudioSwitch(e), Ii - })), - a = t.rootPlaylistEntity$.pipe(ln(e => null !== e.enabledMediaOptionKeys[gu.AltAudio].mediaOptionId), Ds(1), La(e => { - const t = e.enabledMediaOptionKeys[gu.AltAudio].mediaOptionId; - return t && (e = e.mediaOptionListTuple[gu.AltAudio].mediaOptions.find(e => e.mediaOptionId === t), this.triggerAudioSwitch(Object.assign(Object.assign({}, e), { - url: le(null == e ? void 0 : e.url) - }))), Ii - })); - return an(i, s, Gu(e.textTracksCreated$, e => e).pipe(La(() => t.enabledMediaOptionByType$(gu.Subtitle).pipe(La(e => { - e = t.alternateMediaOptionById(gu.Subtitle, e.mediaOptionId); - return e ? this.hls.trigger(P.SUBTITLE_TRACK_SWITCH, { - track: Object.assign({}, e), - hidden: !1 - }) : this.hls.trigger(P.SUBTITLE_TRACK_SWITCH, { - track: void 0, - hidden: !1 - }), Ii - })))), t.sessionData$.pipe(ln(e => null != e.complete), Ds(1), Za(e => {}), hr(e => { - this.hls.trigger(P.SESSION_DATA_COMPLETE, e) - })), t.getPreferredMediaOptionsByType$(gu.Variant).pipe(ka(1), hr(e => { - const t = e; - null === (e = this.rtc) || void 0 === e || e.handleLevelsChanged(t), t.forEach(e => {}), this.hls.trigger(P.LEVELS_CHANGED, { - requiresReset: !1, - levels: t - }) - })), t.getPreferredMediaOptionsByType$(gu.AltAudio).pipe(hr(e => { - this.hls.trigger(P.AUDIO_TRACKS_UPDATED, { - audioTracks: e - }) - })), Gu(e.textTracksCreated$, e => e).pipe(La(() => t.getPreferredMediaOptionsByType$(gu.Subtitle).pipe(Ds(1), hr(e => { - this.hls.trigger(P.SUBTITLE_TRACKS_UPDATED, { - subtitleTracks: e - }), this.hls.trigger(P.SUBTITLE_TRACKS_CREATED) - })))), n, r, a) - } - mediaElementQueryListener(s, e) { - return an(s.seekTo$.pipe(hr(e => { - var t; - e && ne(e.pos) ? (null === (t = this.rtc) || void 0 === t || t.handleSeek("SEEKING"), this.hls.trigger(P.SEEKING, { - seekToPos: e.pos - })) : null === e && (null === (e = this.rtc) || void 0 === e || e.handleSeek("SEEKED"), this.hls.trigger(P.SEEKED)) - })), s.desiredRate$.pipe(Ra(0), ha(), hr(e => { - var t = e[0], - i = e[1]; - Wp(i) ? 0 == this.iframeSwitchStart && (this.iframeSwitchStart = performance.now()) : this.iframeSwitchStart = 0, this.hls.trigger(P.DESIRED_RATE_CHANGED, { - oldRate: t, - newRate: i - }), null === (e = this.rtc) || void 0 === e || e.handleDesiredRateChanged(t, i) - })), s.sourceBufferEntityByType$(yu.AltAudio).pipe(ln(e => !!e), Is((e, t) => e.totalBytes === t.totalBytes), hr(e => { - this.hls.trigger(P.BUFFER_APPENDED) - })), s.sourceBufferEntityByType$(yu.Variant).pipe(ln(e => !!e), Is((e, t) => e.totalBytes === t.totalBytes), hr(e => { - var t; - null === (t = this.rtc) || void 0 === t || t.handleVariantBufferAppended(e.timestampOffset, e.totalBytes), this.hls.trigger(P.BUFFER_APPENDED) - })), s.stallInfo$.pipe(Kp(), bo(s.combinedBuffer$), hr(([e]) => { - var t; - null === (t = this.rtc) || void 0 === t || t.handleStalled(e, s.getCombinedBufferInfo(e.currentTime, 0).len), this.hls.trigger(P.STALLED, e) - })), Mr([$i(e), ed([s.timeupdate$, s.bufferedSegmentsByType$(yu.Variant)]).pipe(ao(1e3), hr(([t, e]) => null == e ? void 0 : e.find(e => e.startPTS <= t && e.endPTS > t)), ln(e => !!e), Ra(null), ha())]).pipe(La(([e, [t, i]]) => { - var r = null == i ? void 0 : i.frag, - t = null == t ? void 0 : t.frag; - if (r && !$p(t, r) && (this.hls.trigger(P.FRAG_CHANGED, i), this.hls.inGaplessMode && this.checkAndTriggerReadyForNext(s, i), !t || r.mediaOptionId !== t.mediaOptionId)) { - const s = e.mediaOptionListQueries[gu.Variant].mediaOptionFromId(r.mediaOptionId); - if (!s) return this.logger.warn("variantInfo is undefined in fragChangeMonitor"), Ii; - const n = t ? t.mediaOptionId : "", - i = r.mediaOptionId; - s.iframes && (performance.now(), this.iframeSwitchStart), null === (r = this.rtc) || void 0 === r || r.handleLevelSwitched({ - url: s.url, - mediaOptionId: s.mediaOptionId, - oldVariant: "" !== n ? n : void 0, - newVariant: i - }), this.hls.trigger(P.LEVEL_SWITCHED, s) - } - return Ii - })), s.isBufferedToEnd$(this.hls.config.maxBufferHole, !1).pipe(ln(e => !0 === e), ji(tr), hr(e => { - if (e && !this.hls.itemQueue.isPreloading() && this.hls.inGaplessMode) { - const i = s.getCombinedBufferInfo(s.currentTime, 0); - var t = 0; - i && (t = i.end, e = s.mediaElementDuration, 0 < t && e - s.currentTime < 10 && this.hls.trigger(P.READY_FOR_NEXT_ITEM, { - duration: t - })) - } - }))) - } - checkAndTriggerReadyForNext(e, t) { - var i, r; - t && t.frag && (i = e.currentTime, (r = e.getCombinedBufferInfo(i, 0)) && (i = r.end, r = e.mediaElementDuration, e = e.bufferMonitorInfo, e = Math.max(e.almostDryWaterLevelSeconds, e.lowWaterLevelSeconds / 2), (r - t.endPTS <= e || t.frag.isLastFragment) && this.hls.inGaplessMode && !this.hls.isPreloading && this.hls.trigger(P.READY_FOR_NEXT_ITEM, { - duration: i - }))) - } - loaderQueryListener(e) { - return an(e.unresolvedUriLoading$.pipe(hr(e => e.map(e => { - e = { - uri: e.uri, - responseType: e.responseType, - userAgent: e.userAgent - }; - this.hls.trigger(P.UNRESOLVED_URI_LOADING, e) - })))) - } - triggerAudioSwitch(e) { - e && this.hls.trigger(P.AUDIO_TRACK_SWITCHED, { - id: e.id - }) - } - triggerManifestLoaded(e) { - e = { - levels: e.rootMediaOptionsTuple[gu.Variant], - audioTracks: e.rootMediaOptionsTuple[gu.AltAudio], - subtitleTracks: e.rootMediaOptionsTuple[gu.Subtitle], - url: e.baseUrl, - audioMediaSelectionGroup: e.audioMediaSelectionGroup, - subtitleMediaSelectionGroup: e.subtitleMediaSelectionGroup, - stats: e.stats, - isMediaPlaylist: e.isMediaPlaylist + statsStore.setLoading(true); + return of(item).pipe( + map(() => { + logAction(`stats.loaded`); + // TODO get it from persistent store + // or rebuild from configs + const statsEntity: StatsEntity = { + id: itemId, + bandwidthSample: {trequest: 0, tparsed: 0, tfirst: 0, tload: 0, loaded: 0}, + bandwidthEstimate: {avgLatencyMs: 0, avgBandwidth: 0}, + fragSample: { duration: 0, parseTimeMs: 0, bufferCreationDelayMs: 0, bufferTimeMs: 0}, + fragEstimate: { maxDuration: 0, maxParseTimeMs: 0, maxBufferCreationDelayMs: 0, maxBufferTimeMs: 0 }, + playlistSample: { playlistLoadTimeMs: 10 }, + playlistEstimate: { maxPlaylistLoadTimeMs: 10} }; - this.hls.trigger(P.MANIFEST_LOADED, e) - } - triggerManifestParsed(e) { - var t = { - levels: e.mediaOptionListQueries[gu.Variant].filteredMediaOptionList, - firstLevel: 0, - audio: !1, - video: !0, - altAudio: !1, - audioTracks: e.mediaOptionListQueries[gu.AltAudio].filteredMediaOptionList, - audioMediaSelectionGroup: e.audioMediaSelectionGroup, - subtitleMediaSelectionGroup: e.subtitleMediaSelectionGroup, - stats: e.loadStats - }; - null === (e = this.rtc) || void 0 === e || e.handleManifestParsed(t), this.hls.trigger(P.MANIFEST_PARSED, t) - } - urlRedactedManifestLoaded(e) { - const t = Object.assign({}, e); - return t.url = le(t.url), t.levels = ue(t.levels), t.audioTracks = ce(t.audioTracks), t.subtitleTracks = ce(t.subtitleTracks), t - } - urlRedactedManifestParsed(e) { - const t = Object.assign({}, e); - return t.levels = ue(t.levels), t.audioTracks = ce(t.audioTracks), t - } - }(A = Mm = Mm || {}).LowBandwidth = "LowBandwidth", A.HighBandwidth = "HighBandwidth", A.PreferredListChanged = "PreferredListChanged", A.IframeModeChange = "IframeModeChange", A.None = ""; - const Fg = { - minValidBitrate: 2e6, - maxValidBitrate: 5e6, - maxPreferredBitrate: 3e6, - minValidHeight: 480, - maxValidHeight: 720 - }; - - function Bg(e, a, o, l, d, u) { - return e.reduce((e, t) => { - if (t.iframes) return e; - let i = e; - const r = (n = t.score, s = a && o && l && d && !Ug(t, a, o, l, d, u) ? mu.INVALID : mu.VALID, new Qp(s, n)); - var n, s; - return (!e || r.isGreaterThan(e.bestRank) || r.isEqualTo(e.bestRank) && t.bandwidth < e.selected.bandwidth) && (i = { - selected: t, - bestRank: r - }), i - }, null).selected - } - - function Ug(e, t, i, r, n, s) { - var { - targetDuration: a, - targetStartupMs: o - } = i, l = r["avgPlaylistLoadTimeMs"], d = n["avgParseTimeMs"], { - avgBufferCreateMs: i, - avgInitFragAppendMs: r, - avgDataFragAppendMs: n - } = s, { - avgBandwidth: s, - avgLatencyMs: t - } = t; - return e.bandwidth <= s && (e.avgBandwidth || e.bandwidth) * a / s * 1e3 + l + i + +(t + d + (r + n)) <= o - } - const $g = { - name: "abr" - }; - - function Vg(e, t) { - return ne(e) ? Math.min(e, t) : t - } - - function Kg(e) { - return ne(null == e ? void 0 : e.avgBandwidth) - } - - function qg(e, t) { - return e.getCurrentWaterLevelByType(yu.Variant, t) / (0 !== e.playbackRate ? Math.abs(e.playbackRate) : 1) - } - - function Hg(t, i, e, r) { - if (e) return t; - let n = -1; - if (t < 0) return n; - for (let e = t; e < i.length; ++e) { - const t = Qg(i[e], r); - if (t.altAudio && t.subtitle) { - n = e; - break - } - } - return n - } - - function jg(t, e, i, r, n, s, a) { - const o = i.preferredMediaOptions[gu.Variant].filter(e => e.iframes === t); - if (!o.length) return { - variantMediaOption: Lu.mediaOptionId, - holdOffDuration: 0, - lowestCandidate: null - }; - let l = 0; - const d = i.nextMinAutoOptionId; - if (d !== Lu.mediaOptionId) { - const t = o.findIndex(e => e.mediaOptionId === d); - 0 <= t && (l = t) - } - if (l = Hg(l, o, t, i), l < 0) return { - variantMediaOption: Lu.mediaOptionId, - holdOffDuration: 0, - lowestCandidate: null - }; - let u = o.length - 1; - const c = i.nextMaxAutoOptionId; - if (c !== Lu.mediaOptionId) { - const t = o.findIndex(e => e.mediaOptionId === c); - 0 <= t && (u = t) - } - var h = i.variantMediaOptionById(r.mediaOptionId), - p = r.mediaOptionDetails, - f = (null == h ? void 0 : h.iframes) !== t ? 0 : qg(n, e.maxBufferHole); - let m, g; - if (t) { - const t = e.desiredIframeFPS; - g = p ? p.targetduration / t : 0, g = Math.max(1 / t, g) - } else g = p ? p.targetduration : 0; - h = e.abrBandWidthUpFactor, p = e.abrBandWidthFactor; - return m = Wg(o, g, l, u, f, t, s.getCombinedEstimate(), s.bandwidthStatus, p, h, e, i, r, n, a), m.variantMediaOption === Lu.mediaOptionId && (m = Wg(o, g, l, u, f + Vg(g, e.maxStarvationDelay), t, s.getCombinedEstimate(), s.bandwidthStatus, p, h, e, i, r, n, a), m.variantMediaOption === Lu.mediaOptionId && 0 <= l && (m.variantMediaOption = o[l].mediaOptionId, m.alternates = t ? null : Qg(o[l], i))), m - } - - function Qg(e, t) { - var i = t.enabledMediaOptionKeys, - r = i[gu.AltAudio], - r = _u(r) ? t.mediaOptionListQueries[gu.AltAudio].getMatchingAlternate(r.mediaOptionId, e) : Lu, - i = i[gu.Subtitle]; - return { - altAudio: r, - subtitle: _u(i) ? t.mediaOptionListQueries[gu.Subtitle].getMatchingAlternate(i.mediaOptionId, e) : Lu - } - } - - function Wg(i, r, n, e, s, a, o, l, t, d, u, c, h, p, f) { - "bandwidth-history-controller" !== u.abrBandwidthEstimator && f.warn(`Unsupported configuration: ${u.abrBandwidthEstimator} for ABR bandwidth estimator`); - const m = l.bandwidthSampleCount, - g = c.abrStatus, - y = p.maxBufferSize, - v = u.minTargetDurations || 1, - S = c.mediaOptionListQueries[gu.Variant].mediaOptionListInfo.hasScore; - if (!i.length) return { - variantMediaOption: Lu.mediaOptionId, - holdOffDuration: -1, - lowestCandidate: null - }; - const b = c.enabledMediaOptionIdByType(gu.Variant), - T = c.variantMediaOptionById(b), - E = null != i.find(e => e.mediaOptionId === b), - I = T && T.iframes === a, - w = T && I ? T.score : void 0, - A = T && I ? T.frameRate : void 0, - O = T && I ? T.height : void 0, - k = I ? qg(p, u.maxBufferHole) : 0; - e = Math.max(0, Math.min(i.length - 1, e)), n = Math.max(0, Math.min(i.length - 1, n)); - var C = h.mediaOptionDetailsEntityRecord, - D = Gg(o.avgBandwidth, d, t, m); - let M; - for (let t = e; t >= n; t--) { - const n = i[t]; - let e = n.mediaOptionId; - const u = n.score, - h = C && C[e] ? C[e].mediaOptionDetails : void 0, - p = C && C[e] ? C[e].lastUpdateMillis : null, - f = h ? h.totalduration / h.fragments.length : r, - B = null != T && n.bitrate > T.bitrate, - m = null != T && n.bitrate < T.bitrate, - U = !(null != A && (B && n.frameRate < A || m && n.frameRate > A)), - $ = !(B && null != O && O > n.height), - V = !(m && (n.bitrate === T.bitrate - 1 || n.bitrate === T.bitrate + 1)), - K = !(S && B && null != w && (u < w || u === w && T && n.bitrate >= T.bitrate)), - q = n.iframes === a; - if (ne(f) && q && U && $ && V && K) { - var { - adjustedbw: x, - bitrate: P, - fetchDuration: R, - rejectLevelDueToPeakBW: L, - canFitMultipleSegments: _, - requireAlternates: N, - alternates: F - } = function(e, t, i, r, n, s, a, o, l, d, u, c) { - var h = n ? a.bwUp : a.bwDown, - n = e.bitrate, - a = t ? t.totalduration / t.fragments.length : o, - s = zg(e, t, o, h, ne(s.avgPlaylistLoadTimeMs) ? s.avgPlaylistLoadTimeMs : s.avgLatencyMs, d), - d = e.bandwidth, - i = n < h && h < d && i <= 2 * a, - l = r * ((d || n || 0) * ((null == t ? void 0 : t.targetduration) || a)) / 8 <= l; - let p = null; - u = !u; - return u && (p = Qg(e, c), p.altAudio && p.subtitle || (p = null)), { - adjustedbw: h, - bitrate: n, - fetchDuration: s, - rejectLevelDueToPeakBW: i, - canFitMultipleSegments: l, - requireAlternates: u, - alternates: p - } - }(n, h, k, v, B, o, D, r, y, p, a, c); - if (N && Boolean(F) && (M = e), P < x && _ && !L && (!N || Boolean(F)) && (a || !R || R < s)) { - if (L = x, N = g, x = (x = l).instantBw, (N.fragDownloadSlow || N.fragDownloadTooSlow || ne(x) && x < L) && E && I) - if (k <= 2 * f && B) e = b; - else if (B && d * l.instantBw < P) continue; - return { - variantMediaOption: e, - holdOffDuration: R, - alternates: F, - lowestCandidate: M - } - } - } - } - return { - variantMediaOption: Lu.mediaOptionId, - holdOffDuration: -1, - lowestCandidate: M - } - } - - function Gg(e, t, i, r) { - let n, s; - return 4 <= r ? (n = e * t, s = e * i) : n = s = e / 1.8, { - bwUp: n, - bwDown: s - } - } - - function zg(e, t, i, r, n, s) { - let a = e.bitrate * (t ? t.totalduration / t.fragments.length : i) / r; - return null == t || !t.liveOrEvent || t.ptsKnown && !Yg(t.totalduration, n, s) || (a *= 2), ne(n) && (!ne(s) || null != t && t.liveOrEvent) && (a += n / 1e3), a - } - - function Xg(t, e) { - let i = 1 / 0; - if (t === Lu.mediaOptionId) return i; - var r = e.find(e => e.mediaOptionId === t); - if (!r) return 1 / 0; - const n = r.iframes, - s = r.bitrate, - a = r.frameRate, - o = e.find(e => e.iframes === n && (void 0 === a || e.frameRate >= a) && e.bitrate > s); - return o && (i = o.bitrate), i - } - - function Yg(e, t, i) { - return t = ne(t) ? t : 0, !ne(i) || performance.now() - i + t > 1e3 * e - } - - function Jg(e, t) { - return (null == e ? void 0 : e.fragDownloadSlow) === (null == t ? void 0 : t.fragDownloadSlow) && e.fragDownloadTooSlow === (null == t ? void 0 : t.fragDownloadTooSlow) - } - - function Zg(e) { - return "loading" === (null == e ? void 0 : e.state) && ne(null === (e = e.bwSample) || void 0 === e ? void 0 : e.trequest) - } - const ey = 2e3; - - function ty(e, t, i, r) { - let { - fragDownloadSlow: n, - fragDownloadTooSlow: s - } = t; - var a = i.variantMediaOptionById(e.mediaOptionId).bitrate, - t = e.bwSample; - r = r.child($g); - i = t.total || Math.max(t.loaded, Math.round(e.duration * a / 8)), a = performance.now() - t.tfirst, i = t.loaded * e.duration * 1e3 / i; - return a >= ey && 1e3 <= a - i && (n || r.warn(`flow indicates low bandwidth, after time/duration behind real time: ${a}/${a-i}`), n = !0), a >= 1e3 * e.duration && (s || r.warn(`too much time spent downloading fragment, likely to switch down ${a} > ${1e3*e.duration}`), s = !0), { - fragDownloadSlow: n, - fragDownloadTooSlow: s - } - } - - function iy(e, t, i, r, n) { - var s; - const a = n.logger.child($g), - o = r.isIframeRate, - l = i.mediaOptionListQueries[gu.Variant].preferredMediaOptionList, - d = i.enabledMediaOptionKeys[gu.Variant]; - let u = e; - if (u !== Mm.None || l.some(e => e.mediaOptionId === d.mediaOptionId) || (u = Mm.PreferredListChanged), u !== Mm.None && !(o && u !== Mm.IframeModeChange && r.getBufferedSegmentsByType(yu.Variant).filter(e => e.frag.iframe).length < t.minFramesBeforeSwitchingLevel)) { - o && u === Mm.IframeModeChange && n.setEnabledVariantMediaOptionIdBeforeTrickplaySwitch(i.itemId, d.mediaOptionId); - const h = Zf(i.itemId), - p = Dg(d), - f = [Lu, Lu, Lu]; - if (!o && e === Mm.IframeModeChange) { - const e = function(e, t, i, r) { - const n = i.mediaOptionListQueries[gu.Variant].preferredMediaOptionList, - s = t.targetStartupMs, - a = { - avgPlaylistParseTimeMs: 0, - avgPlaylistLoadTimeMs: 0 - }, - o = r.getFragEstimate(), - l = { - avgBufferCreateMs: 0, - avgInitFragAppendMs: 0, - avgDataFragAppendMs: 0 - }, - d = o.maxDurationSec, - u = { - avgLatencyMs: 0, - avgBandwidth: r.getBandwidthEstimate().avgBandwidth - }, - c = { - targetDuration: d, - targetStartupMs: s, - metricsOverride: { - maxValidHeight: 0, - maxValidBitrate: 0, - maxPreferredBitrate: 0 - } - }, - h = n.filter(e => !e.iframes && (!e.width || !e.height || e.width * e.height <= 2488320)); - let p = e; - 0 < h.length && (p = h[0].mediaOptionId); - e = h.filter(e => Ug(e, u, c, a, o, l)); - return 0 < e.length && (p = e[e.length - 1].mediaOptionId), p - }(i.enabledVariantMediaOptionIdBeforeTrickplaySwitch, t, i, h); - n.setNextMaxAutoOptionId(i.itemId, e), n.setEnabledVariantMediaOptionIdBeforeTrickplaySwitch(i.itemId, void 0) - } - var c = function(e, t, i, r, n, s, a) { - let o = t.nextMaxAutoOptionId; - if (o === Lu.mediaOptionId || Kg(s.getBandwidthEstimate())) return l = e, d = t, u = r, e = n, r = s, s = a.child({ - name: "abr" - }), a = e.isIframeRate, d.enabledMediaOptionIdByType(gu.Variant), jg(a, l, d, u, e, r, s); - if (n.isIframeRate) return { - variantMediaOption: o, - holdOffDuration: 0, - lowestCandidate: null - }; { - const c = t.variantMediaOptionById(o), - h = t.enabledAlternateMediaOptionByType(gu.Subtitle), - n = t.enabledAlternateMediaOptionByType(gu.AltAudio), - p = null == n ? void 0 : n.persistentID, - f = null == h ? void 0 : h.persistentID, - m = !t.preferHDR, - g = i.getBestMediaOptionTupleFromVariantAndPersistentId(t, c, p, f, void 0, [], m, !1, !1); - return t.isValidMediaOptionTuple(g) ? (o = g[gu.Variant].mediaOptionId, { - variantMediaOption: o, - holdOffDuration: 0, - alternates: { - altAudio: g[gu.AltAudio], - subtitle: g[gu.Subtitle] - }, - lowestCandidate: null - }) : { - variantMediaOption: t.enabledMediaOptionKeys[gu.Variant].mediaOptionId, - holdOffDuration: 0, - lowestCandidate: null - } - } - var l, d, u - }(t, i, n, p, r, h, a); - if (o || e !== Mm.IframeModeChange || n.setNextMaxAutoOptionId(i.itemId, Lu.mediaOptionId), c.variantMediaOption !== d.mediaOptionId) { - f[gu.Variant] = { - itemId: i.itemId, - mediaOptionId: c.variantMediaOption - }; - for (const e of [gu.AltAudio, gu.Subtitle]) { - const t = i.enabledMediaOptionIdByType(e); - if (t !== Lu.mediaOptionId) { - const r = e === gu.AltAudio ? null === (s = c.alternates) || void 0 === s ? void 0 : s.altAudio : null === (s = c.alternates) || void 0 === s ? void 0 : s.subtitle; - f[e] = r || { - itemId: i.itemId, - mediaOptionId: t - } - } - } - return n.setEnabledMediaOptions(i.itemId, f), 1 - } - } - } - const ry = { - name: "iframes" - }; - (A = xm = xm || {})[A.DISABLED = 0] = "DISABLED", A[A.ERRORED = 1] = "ERRORED", A[A.SUCCESS = 2] = "SUCCESS"; - const ny = new fl({}, { - name: "item-queue", - producerFn: su, - idKey: "itemId", - resettable: !0 - }), - sy = new class extends kl {}(ny); - class ay { - constructor() { - this.firstItem = !0, this.playingEntity = null, this.loadingEntity = null - } - static createItem(e, t, i = NaN, r, n) { - const s = new Date, - a = `${s.getHours()}:${s.getMinutes()}:${s.getSeconds()}`; - Qe(); - var o = function(e) { - e = cu.parseURL(e).fragment.substr(1); - if (0 === e.length) return null; - const t = new URLSearchParams(e); - if (!t.has("t")) return null; - e = Number(t.get("t")); - return ne(e) ? e : null - }(t); - if (ne(o)) i = o; - else { - const e = mg(); - ne(null == e ? void 0 : e.startPosition) && (i = e.startPosition) - } - return { - itemId: `${e}_${a}`, - name: e, - url: t, - serviceName: n, - createTime: a, - initialSeekTime: i, - itemStartOffset: 0, - platformInfo: r, - config: {} - } - } - get activeItemById$() { - return sy.selectActiveId().pipe(hr(e => sy.getActive())) - } - get removedItems$() { - return sy.selectEntityAction(Eo.Remove).pipe(hr(e => e)) - } - get activeItem() { - return sy.getActive() - } - get queueItems$() { - return sy.selectAll().pipe(hr(e => null != e ? e : [])) - } - get isFirstItem() { - return this.firstItem - } - get playingItem() { - return this.playingEntity - } - get loadingItem() { - return this.loadingEntity - } - addQueueItem(e, t, i, r, n, s) { - sy.getCount(); - const a = ay.createItem(e, t, i, r, s); - null != this.playingEntity && (a.initialSeekTime = void 0), n && (a.itemStartOffset = n, this.firstItem = !1, this.playingEntity = this.activeItem, this.loadingEntity = a), Do(`queue.add.item: ${e}`), al(() => { - ny.add(a), ny.setActive(a.itemId) - }) - } - updatePlayingItemId() { - this.playingEntity = this.loadingEntity, this.loadingEntity = null, this.clearAllButActive() - } - resetLoadingItem() { - this.removeQueueItem(this.loadingEntity.itemId), this.loadingEntity = null, al(() => { - ny.setActive(this.playingEntity.itemId) - }) - } - isPreloading() { - return null !== this.playingEntity && null !== this.loadingEntity - } - setQueueItem(t, i, r, n, s) { - Do("queue.set.item"), this.loadingEntity = null, al(() => { - ny.reset(); - var e = ay.createItem(t, i, r, n, s); - ny.add(e), ny.setActive(e.itemId) - }), this.playingEntity = this.activeItem - } - removeQueueItem(e) { - ny.remove(e) - } - clearQueue() { - ny.reset() - } - clearAllButActive() { - var e; - const t = null === (e = this.activeItem) || void 0 === e ? void 0 : e.itemId; - al(() => { - sy.getAll().forEach(e => { - e.itemId !== t && ny.remove(e.itemId) - }) - }) - } - set earlyAudioSelection(t) { - ny.updateActive(e => { - e.earlySelection || (e.earlySelection = {}), e.earlySelection.audioPersistentId = t - }) - } - get earlyAudioSelection() { - var e; - return null === (e = this.activeItem.earlySelection) || void 0 === e ? void 0 : e.audioPersistentId - } - set earlySubtitleSelection(t) { - ny.updateActive(e => { - e.earlySelection || (e.earlySelection = {}), e.earlySelection.subtitlePersistentId = t - }) - } - get earlySubtitleSelection() { - var e; - return null === (e = this.activeItem.earlySelection) || void 0 === e ? void 0 : e.subtitlePersistentId - } - } - - function oy(e, t, i, r, n, s) { - return ng(e, 0, Jm({ - errorAction: Cm.RemoveAlternatePermanently, - errorActionFlags: 0 - }, !1, e.response.code, i, t, s, n), s, n, t, i).pipe(Vn(e => { - throw !1 === e.fatal && r.resetMediaSource(), e - })) - } - class ly { - constructor(e, t, i, r, n, s) { - this.logger = e, this._rootPlaylistService = t, this._rootQuery = i, this._mediaQuery = r, this._iframeMachine = n, this._anchorMSNs = [NaN, NaN], this._avDetails = [null, null], this.logger = e.child({ - name: "fpicker" - }), this._discoSeqNum = NaN, this.lookUpTolerance = Math.max(s.maxBufferHole, s.maxFragLookUpTolerance), this.firstAudioMustOverlapVideoStart = s.firstAudioMustOverlapVideoStart, this.lookUpToleranceAlt = s.firstAudioMustOverlapVideoStart ? 0 : s.maxFragLookUpTolerance - } - destroy() { - this._anchorMSNs = [NaN, NaN], this._avDetails = [null, null], this._rootQuery = null, this._mediaQuery = null, this._rootPlaylistService = null, this._iframeMachine = null - } - get discoSeqNum() { - return this._discoSeqNum - } - get _discoSeqNum() { - return this._rootQuery.discoSeqNum - } - set _discoSeqNum(e) { - this._rootPlaylistService.setDiscoSeqNum(this._rootQuery.itemId, e) - } - get anchorMSNs() { - return this._anchorMSNs - } - _resolvePosition(e, t, i) { - let r = e; - var t = this._avDetails[t]; - if ((null == t ? void 0 : t.mediaOptionId) !== (null == i ? void 0 : i.mediaOptionId) && i.liveOrEvent && !1 === i.ptsKnown && (!t || Math.max(t.startSN, i.startSN) > Math.min(t.endSN, i.endSN))) { - const e = 3 * i.targetduration, - n = (null === (t = i.fragments[0]) || void 0 === t ? void 0 : t.start) + i.totalduration; - r = Math.max(0, n - e) - } - return r - } - getDiscoSeqNumForTime(e, t) { - return this._mediaQuery.isIframeRate && e.iframesOnly ? Qu.discoSeqNumForTime(e.fragments, t) : (r = t, null == (e = null !== (e = Tg(e, e => { - var t = 0 < e.duration, - i = e.start + e.duration, - e = r < i || r - i < 1 && e.isLastFragment; - return t && e - })) && void 0 !== e ? e : null) ? void 0 : e.mediaFragment.discoSeqNum); - var r - } - _updateAnchorByPosition(e, t) { - let i = NaN; - const r = t[yu.Variant]; - let n = e; - if (r) { - const t = r.fragments; - if (n = this._resolvePosition(e, yu.Variant, r), i = this.getDiscoSeqNumForTime(r, n), !ne(i)) { - const r = t[0], - s = t[t.length - 1], - a = null == r ? void 0 : r.start, - o = (null == s ? void 0 : s.start) + (null == s ? void 0 : s.duration); - this.logger.warn(`${e.toFixed(3)} out of range [${null==a?void 0:a.toFixed(3)},${null==o?void 0:o.toFixed(3)}]`), n <= a ? i = r.discoSeqNum : n >= o ? i = s.discoSeqNum : this.logger.warn(`Unable to determine newCC. fragFirst: ${JSON.stringify(r)} fragLast: ${JSON.stringify(s)}`), ne(i) || this.logger.warn(`Unable to determine newCC. fragFirst: ${JSON.stringify(r)} fragLast: ${JSON.stringify(s)}`) - } - } else this.logger.warn("No variant details for anchoring"); - return this._updateAnchor(i, t), n - } - _updateAnchor(e, s) { - const a = e !== this._discoSeqNum; - a && (this._discoSeqNum = e), Bu.forEach(e => { - const t = this._avDetails[e], - i = s[e], - r = (null == t ? void 0 : t.mediaOptionId) !== (null == i ? void 0 : i.mediaOptionId); - if (a || r) this._updateAnchorForType($u(e), i); - else if (i) { - const { - mediaOptionId: s, - ptsKnown: a, - dateMediaTimePairs: t, - startSN: r, - endSN: n - } = i; - this._avDetails[e] = { - mediaOptionId: s, - ptsKnown: a, - dateMediaTimePairs: t, - startSN: r, - endSN: n - } - } - }) - } - getNextFragments(i, r, n) { - const { - position: s, - bufferInfoTuple: e, - switchContexts: a - } = i; - let t = e.map((e, t) => dy(s, r[t], a[t], null == e ? void 0 : e.buffered, t === gu.AltAudio ? this.lookUpToleranceAlt : this.lookUpTolerance)).reduce((e, t) => Math.min(t, e), Number.POSITIVE_INFINITY); - if (ne(i.discoSeqNum)) { - const s = r.reduce((e, t) => { - if (!t) return e; - t = Qu.getTimeRangeForCC(t.fragments, i.discoSeqNum, n); - return [Math.max(e[0], t[0]), Math.min(e[1], t[1])] - }, [Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY]); - t = Math.min(Math.max(t, s[0]), s[1]) - } - return i.position = this._updateAnchorByPosition(t, r), this._getNextFragmentsInternal(i, r) - } - _getNextFragmentsInternal(r, n) { - const s = [null, null]; - let a; - n.forEach((e, t) => { - var i; - this.firstAudioMustOverlapVideoStart && t === gu.AltAudio && this._mediaQuery.seeking && null !== (i = s[gu.Variant]) && void 0 !== i && i.foundFrag && (a = r.position, r.position = s[gu.Variant].foundFrag.mediaFragment.start), s[t] = this._getNextFragmentForType(r, n, t) - }); - var e = s[yu.Variant], - t = s[yu.AltAudio], - i = null === (o = null == e ? void 0 : e.foundFrag) || void 0 === o ? void 0 : o.mediaFragment, - o = null === (o = null == t ? void 0 : t.foundFrag) || void 0 === o ? void 0 : o.mediaFragment; - if (i && o && (o.start > i.start + i.duration ? (this.logger.warn("Audio too far ahead"), s[yu.AltAudio] = ly.noopResult) : i.start > o.start + o.duration && !this._mediaQuery.isIframeRate && (this.logger.warn("Video too far ahead"), s[yu.Variant] = ly.noopResult)), !isFinite(null == e ? void 0 : e.nextDisco) || null != t && !ne(t.nextDisco)) return s; { - const l = s[yu.Variant].nextDisco; - return this._updateAnchor(l, n), ne(a) && (r.position = a, a = NaN), this._getNextFragmentsInternal(r, n) - } - } - _getNextFragmentForType(e, t, i) { - var { - position: r, - bufferInfoTuple: n, - switchContexts: s - } = e, a = t[i], o = null !== (d = null === (c = n[i]) || void 0 === c ? void 0 : c.buffered) && void 0 !== d ? d : { - start: r, - end: r, - len: 0 - }, l = this._mediaQuery.getBufferedSegmentsByType(i), d = null !== (c = null === (e = s[i]) || void 0 === e ? void 0 : e.userInitiated) && void 0 !== c && c, u = dy(r, a, s[i], o, this.lookUpTolerance); - if (!a) return null; - var { - highWaterLevelSeconds: e, - lowWaterLevelSeconds: c - } = this._mediaQuery.bufferMonitorInfo, r = o.len; - if (!d && e <= r) return ly.noopResult; - var e = i === yu.Variant ? yu.AltAudio : yu.Variant, - n = null === (n = n[e]) || void 0 === n ? void 0 : n.buffered, - e = null !== (e = null === (e = s[e]) || void 0 === e ? void 0 : e.userInitiated) && void 0 !== e && e; - let h = !1; - i === yu.Variant && c <= r && 1 < this._mediaQuery.expectedSbCount && null != n && n.end < o.end && (e || n.end - n.start < c) && (h = !0); - let p, f = null, - m = NaN; - if (this._mediaQuery.isIframeRate && i === yu.Variant && a.iframesOnly) { - const g = function(e, t, i) { - e = i.nextFragment(a.fragments, (null == e ? void 0 : e.fragments) || [], t, u); - if (!e) return null; - var { - frag: t, - newMediaRootTime: e - } = e; - return { - foundFrag: { - timelineOffset: t.iframeMediaStart, - mediaFragment: t - }, - nextDisco: NaN, - newMediaRootTime: e - } - }(t[yu.AltAudio], this._mediaQuery.desiredRate, this._iframeMachine); - if (g) { - ({ - foundFrag: f, - nextDisco: m, - newMediaRootTime: p - } = g); - const i = f.mediaFragment; - i.discoSeqNum !== this._discoSeqNum && this._updateAnchor(i.discoSeqNum, t) - } - } else { - const g = this._anchorMSNs[i]; - ({ - foundFrag: f, - nextDisco: m, - newMediaRootTime: p - } = Ig(u, this._discoSeqNum, g, a, l)) - } - return h && this._rootQuery.getInitPTS(null == f ? void 0 : f.mediaFragment.discoSeqNum) ? ly.noopResult : { - foundFrag: f, - nextDisco: m, - newMediaRootTime: p - } - } - _updateAnchorForType(e, t) { - var i, r, n, s, a; - if (!t) return this._anchorMSNs[e] = NaN, void(this._avDetails[e] = null); - ne(this._discoSeqNum) ? (a = this._discoSeqNum, a = null !== (s = null == (n = (r = t.fragments, i = a, r.find(e => e.discoSeqNum === i))) ? void 0 : n.mediaSeqNum) && void 0 !== s ? s : t.startSN, this._anchorMSNs[e] = a, { - mediaOptionId: r, - ptsKnown: n, - dateMediaTimePairs: s, - startSN: a, - endSN: t - } = t, this._avDetails[e] = { - mediaOptionId: r, - ptsKnown: n, - dateMediaTimePairs: s, - startSN: a, - endSN: t - }) : this.logger.warn("Trying to anchor with non-finite discoSeqNum") - } - } - - function dy(e, t, i, r, n) { - r = null != r ? r : { - start: e, - end: e, - len: 0 - }; - i = null !== (i = null == i ? void 0 : i.userInitiated) && void 0 !== i && i, n = null != t && t.iframesOnly ? 0 : n; - return i || 0 === r.len ? e : r.end + n - } - ly.noopResult = { - foundFrag: null, - nextDisco: NaN - }; - const uy = { - name: "avpipe" - }; - - function cy(r) { - const { - config: o, - rootPlaylistService: l, - rootPlaylistQuery: d, - mediaSink: e, - gaplessInstance: t - } = r, u = e.mediaQuery, i = ed(Bu.map(e => d.enabledMediaOptionSwitchForType$(e).pipe(Za(e => {})))).pipe(La(e => { - if (!_u({ - itemId: d.itemId, - mediaOptionId: e[gu.Variant].toId - })) throw new V(!0, `No valid variant enabled id:${e[gu.Variant].toId}`, $.NoValidAlternates); - e = e.map(({ - fromId: e, - toId: t - }, i) => function(t, i, r, n, s) { - var a, o; - const { - rootPlaylistQuery: l, - rootPlaylistService: d, - mediaSink: u, - mediaParser: c, - config: h, - iframeMachine: p - } = t, f = u.mediaQuery; - if (!n || !s || n === s && (r === gu.AltAudio || !p.isStarted)) return Wu; - switch (r) { - case gu.Variant: { - n !== s && c.reset(gu.Variant); - const t = $u(r), - g = l.variantMediaOptionById(n), - y = l.variantMediaOptionById(s), - v = l.itemId; - if (null == y || null == g) return Wu; - var e = !y.iframes && p.isStarted; - if (g.iframes !== y.iframes || e) return u.toggleTrickPlaybackMode(y.iframes), e && (ne(u.mediaQuery.postFlushSeek) || (u.postFlushSeek = p.iframeClockTimeSeconds), p.stop()), u.pause(), (e ? u.flushAll(0, 1 / 0, !0) : u.flushData(t, 0, 1 / 0, !0)).pipe(Za(() => { - var e = u.mediaQuery.postFlushSeek; - ne(e) && d.setPendingSeek(v, e) - })); - if (!h.allowFastSwitchUp || y.iframes) return Wu; - var m = Dg(g).mediaOptionDetails; - if (null != m && null != y && g.bitrate < y.bitrate) { - const r = m.targetduration, - n = Dg(y), - s = n.mediaOptionDetails, - d = n.mediaOptionDetailsEntity.lastUpdateMillis, - c = f.getCurrentWaterLevelByType(t, h.maxBufferHole), - p = function(e, t, i, r, n, s, a, o) { - if (n.nextMaxAutoOptionId !== Lu.mediaOptionId && !Kg(s.getBandwidthEstimate())) return Number.POSITIVE_INFINITY; - a = Gg(s.getBandwidthEstimate().avgBandwidth, a.abrBandWidthUpFactor, a.abrBandWidthFactor, s.bandwidthStatus.bandwidthSampleCount), s = ne(s.getPlaylistEstimate().avgPlaylistLoadTimeMs) ? s.getPlaylistEstimate().avgPlaylistLoadTimeMs : s.getBandwidthEstimate().avgLatencyMs, a = t.bitrate > e.bitrate ? a.bwUp : a.bwDown; - return null == i || !i.liveOrEvent || i.ptsKnown && !Yg(i.totalduration, s, o) ? zg(t, i, r, a, s, o) : Number.POSITIVE_INFINITY - }(g, y, s, r, l.abrStatus, i, h, d) + h.maxStarvationDelay, - S = f.currentTime + p, - b = null === (o = null === (a = f.sourceBufferEntityByType(t)) || void 0 === a ? void 0 : a.bufferedSegments) || void 0 === o ? void 0 : o.find(e => e.startPTS >= S); - let e; - if (b) { - const t = b.endPTS - b.startPTS; - e = b.startPTS + Math.min(Math.max(t - h.maxFragLookUpTolerance, .5 * t), .75 * t) - } - if (ne(e) && c >= p) return u.flushData(t, e, 1 / 0) - } - } - break; - case gu.AltAudio: - e = l, m = s, o = "Nah" === (a = n) ? null : e.alternateMediaOptionById(gu.AltAudio, a), o = Boolean(o && o.url), m = "Nah" === a ? null : e.alternateMediaOptionById(gu.AltAudio, m), m = Boolean(m && m.url), o && !m && (d.setEnabledMediaOptionSwitchContextByType(l.itemId, gu.AltAudio, s, void 0), u.resetMediaSource(f.currentTime)), c.reset(gu.AltAudio) - } - return Wu - }(r, n, i, e, t)); - return Jr($i(!0), en(e).pipe(Zs(!1))) - }), tc("mediaOptionSwitch.audiovideo.out")), n = Zf(d.itemId), s = r.logger.child(uy), a = new ly(s, l, d, u, r.iframeMachine, o); - return ed([d.anchorTime$.pipe(tc("anchorTime.audiovideo.in")), i]).pipe(La(([i, e]) => e ? Ii : u.needData$(o.maxBufferHole, t.inGaplessMode).pipe(hr(e => { - var t = [d.enabledMediaOptionSwitchContexts[gu.Variant], d.enabledMediaOptionSwitchContexts[gu.AltAudio]]; - return u.getSourceBufferInfoAction(e, i, t, o.maxBufferHole) - }), _s(e => e ? fn($i(e).pipe(hy(r, a), my(r)), Gu(function(e) { - const { - mediaSink: t, - rootPlaylistQuery: i, - rootPlaylistService: r - } = e, n = t.mediaQuery, s = e.logger.child($g); - return an((a = i, o = s, ed([n.fellBelowLowWater$, a.getInFlightFragByType$(gu.Variant)]).pipe(La(e => { - var [, t] = e; - if (!Zg(t)) return Ii; - const i = performance.now() - t.bwSample.trequest, - r = ey - i, - n = 1e3 * t.duration - i, - s = [Wu]; - return 0 < r && s.push(bn(r)), 0 < n && s.push(bn(n)), an(...s).pipe(Zs(e)) - }), sa((e, [t, i]) => { - const r = Object.assign({}, e); - return t && (r.fragDownloadSlow = !0), ty(i, r, a, o) - }, { - fragDownloadSlow: !1, - fragDownloadTooSlow: !1 - }), Ra({ - fragDownloadSlow: !1, - fragDownloadTooSlow: !1 - }), Is(Jg))), function(r) { - const s = r.mediaSink.mediaQuery, - { - rootPlaylistQuery: e, - config: a - } = r; - return s.desiredRate$.pipe(La(t => 0 === t ? Ii : ed([e.getInFlightFragByType$(gu.Variant), e.mediaOptionListQueries[gu.Variant].preferredMediaOptionList$.pipe(hr(e => e.filter(Wm.bind(null, Wp(t)))))])), ao(100), La(e => { - const [t, i] = e; - if (!Zg(t) || i.findIndex(e => e.mediaOptionId === t.mediaOptionId) <= 0) return Ii; - var r = performance.now() - t.bwSample.trequest, - n = Vg(t.duration, a.maxStarvationDelay), - n = Math.min(1e3 * n, 500 * t.duration / s.playbackRate); - return bn(Math.max(0, n - r), 100).pipe(Zs(e)) - })).pipe(sa((e, [t, i]) => function(t, i, r, e) { - let { - fragDownloadSlow: n, - fragDownloadTooSlow: s - } = t; - const { - config: a, - rootPlaylistService: o, - rootPlaylistQuery: l, - mediaSink: d, - statsService: u, - mediaLibraryService: c - } = e, h = e.logger.child($g), p = d.mediaQuery; - if (p.paused) return t; - e = i.bwSample; - if (!ne(e.tfirst)) return t; - const f = performance.now(), - m = f - e.trequest, - g = Vg(i.duration, a.maxStarvationDelay), - y = gu.Variant, - v = i.mediaOptionId, - S = l.variantMediaOptionById(v), - b = c.getQueryForOption(S), - T = S.bitrate, - E = Math.max(1, 8e3 * e.loaded / m), - I = 8 * ((ne(e.total) ? e.total : Math.max(e.loaded, Math.round(i.duration * T / 8))) - e.loaded) / E, - w = qg(p, a.maxBufferHole); - let A; - if (ne(w) && 0 < w && !ne(null === (O = p.seekTo) || void 0 === O ? void 0 : O.pos)) A = w; - else { - const N = m / 1e3; - A = N < g ? g - N : g - } - var O = n; - ({ - fragDownloadSlow: n, - fragDownloadTooSlow: s - } = ty(i, t, l, h)); - t = 2 * ((null === (t = b.mediaOptionDetails) || void 0 === t ? void 0 : t.targetduration) || i.duration); - if (!(w <= t && (I >= A || n))) return fg().getQuery().extendMaxTTFB && fg().setExtendMaxTTFB(0), { - fragDownloadSlow: n, - fragDownloadTooSlow: s - }; - O || h.warn(`likely to stall ${ae({maxTimeToLoadSec:A,minSwitchDuration:t,stats:e,elapsedMs:m,remainingTimeSec:I,instantBw:E,bufferAheadSec:w,fragDownloadSlow:n})}`), n = !0, fg().getQuery().extendMaxTTFB || fg().setExtendMaxTTFB(6e5); - let k; - const C = i.itemId, - D = u.getQueryForItem(C), - M = D.getCombinedEstimate(), - x = Object.assign(Object.assign({}, M), { - avgBandwidth: E - }), - P = D.bandwidthStatus, - R = S.iframes, - L = I >= A && !R, - _ = Hg(0, r, R, l); - if (_ < 0) return { - fragDownloadSlow: n, - fragDownloadTooSlow: s - }; - t = Math.max(_, r.findIndex(e => e && e.mediaOptionId === S.mediaOptionId)); - if (L) { - let e = Wg(r, i.duration, _, t, A, R, x, P, 1, 1, a, l, b, p, h); - const F = Lu.mediaOptionId; - k = e.variantMediaOption !== F || (e = Wg(r, i.duration, _, t, I, R, x, P, 1, 1, a, l, b, p, h)).variantMediaOption !== F ? e.variantMediaOption : e.lowestCandidate - } else { - const N = Hg(0, r.slice(_, t).reverse(), R, l), - i = t - 1 - N; - (0 <= N || t === _) && (k = r[i].mediaOptionId) - } - if (null != k && k !== l.abrStatus.nextMaxAutoOptionId && o.setNextMaxAutoOptionId(C, k), L) throw h.warn(`loading too slow, abort fragment loading and switch to level ${k}`), u.setBandwidthSample(Object.assign(Object.assign({}, e), { - tfirst: e.tfirst || f, - tload: e.tload || f, - complete: !0, - mediaOptionType: y - })), s = !0, new fc({ - mediaOptionType: y, - mediaOptionId: v - }, k, $.FragmentAbortError); - return { - fragDownloadSlow: n, - fragDownloadTooSlow: s - } - }(e, t, i, r), { - fragDownloadSlow: !1, - fragDownloadTooSlow: !1 - }), Ra({ - fragDownloadSlow: !1, - fragDownloadTooSlow: !1 - }), Is(Jg)) - }(e)).pipe(Ra({ - fragDownloadSlow: !1, - fragDownloadTooSlow: !1 - }), sa((e, t) => ({ - fragDownloadSlow: e.fragDownloadSlow || t.fragDownloadSlow, - fragDownloadTooSlow: e.fragDownloadTooSlow || t.fragDownloadTooSlow - })), Is(Jg), hr(e => (r.setFragLoadSlow(i.itemId, e), !1)), Vn(e => { - if (e instanceof fc) { - const e = { - fragDownloadSlow: !0, - fragDownloadTooSlow: !0 - }; - return r.setFragLoadSlow(i.itemId, e), $i(!0) - } - return Vi(e) - })); - var a, o - }(r), e => e)).pipe(Ds(1), Vs(() => { - Bu.forEach(e => { - l.updateInflightFrag(d.itemId, e, null, null, null) - }) - })) : Ii))), hr(() => { - if (!d.getEntity(d.itemId).manualMode) { - let e = Mm.None; - var i, r, n, s; - i = Af(), r = u, n = o, s = null == r ? void 0 : r.clientWidth, a = null == r ? void 0 : r.clientHeight, r = "object" == typeof window && window.devicePixelRatio ? window.devicePixelRatio : 1, a = s && a ? { - width: s * r, - height: a * r - } : void 0, r = (r = (null === (r = i.getQuery()) || void 0 === r ? void 0 : r.viewportInfo) || {}) && a && (r.width !== a.width || r.height !== a.height), n.useViewportSizeForLevelCap && r && (i.updateViewportInfo(a), 1) && (e = Mm.PreferredListChanged); - let t = !1; - var a = d.enabledVariantMediaOption; - ! function(e, t) { - const i = l.logger.child($g), - r = e.abrStatus, - n = r.fragDownloadSlow || r.fragDownloadTooSlow, - s = ne(null === (t = t.seekTo) || void 0 === t ? void 0 : t.pos); - return n && !r.fragDownloadTooSlow && s ? (i.warn("could be ignoring low bandwidth due to seek"), 0) : n - }(d, u) ? u.playbackStarted && function(e, t) { - const i = Zf(t.itemId), - r = i.getBandwidthEstimate(), - n = t.abrStatus; - if (Kg(r)) { - var t = (null === (t = i.bandwidthStatus) || void 0 === t ? void 0 : t.bandwidthSampleCount) || 0, - t = Gg(r.avgBandwidth, e.abrBandWidthUpFactor, e.abrBandWidthFactor, t)["bwUp"]; - return t > n.highBWTrigger - } - }(o, d) && (e = Mm.HighBandwidth, l.setNextMinAutoOptionId(a.itemId, a.mediaOptionId)): (e = Mm.LowBandwidth, d.nextMaxAutoOptionId === Lu.mediaOptionId && (l.setNextMaxAutoOptionId(a.itemId, a.mediaOptionId), t = !0)), iy(e, o, d, u, l), t ? l.setNextMaxAutoOptionId(a.itemId, Lu.mediaOptionId) : e === Mm.HighBandwidth && l.setNextMinAutoOptionId(a.itemId, Lu.mediaOptionId) - } - }), Vs(() => {})) - } - const hy = (r, n) => e => { - const { - rootPlaylistQuery: t, - mediaSink: a - } = r, i = r.logger.child(uy); - return e.pipe(ji(tr), bo(t.enabledMediaOptionKeys$), La(([s, e]) => En(py(s, gu.Variant, r, e).pipe(Za(e => { - var e = e.detailsEntity; - if (!e.mediaOptionDetails.liveOrEvent || e.mediaOptionDetails.ptsKnown) { - const t = e.playlistDuration, - i = (null === (e = s.bufferInfoTuple[0]) || void 0 === e ? void 0 : e.buffered.end) || 0, - r = (null === (e = s.bufferInfoTuple[1]) || void 0 === e ? void 0 : e.buffered.end) || 0, - n = Math.max(i, r); - a.msDuration = ne(a.msDuration) ? Math.max(a.msDuration, t, n) : t - } - })), py(s, gu.AltAudio, r, e)).pipe(hr(e => ({ - action: s, - detailsAndContext: e - })))), La(({ - action: e, - detailsAndContext: t - }) => function t(n, s, a, i, d) { - var e; - const { - mediaSink: u, - iframeMachine: c, - rootPlaylistQuery: o - } = a, r = [d[gu.Variant].detailsEntity.mediaOptionDetails, null === (e = null === (e = d[gu.AltAudio]) || void 0 === e ? void 0 : e.detailsEntity) || void 0 === e ? void 0 : e.mediaOptionDetails]; - let l = s.getNextFragments(i, r, n); - const h = l.reduce((e, t) => Math.max(e, ne(null == t ? void 0 : t.newMediaRootTime) ? t.newMediaRootTime : -1 / 0), -1 / 0); - return ne(h) && (u.seekTo = h, l = [null, null]), l.every(e => null == (null == e ? void 0 : e.foundFrag)) ? $i(null) : En(...l.map((t, l) => { - if (t && null != t.foundFrag) { - const i = t.foundFrag["mediaFragment"], - r = _g(a, i.keyTagInfo, { - itemId: i.itemId, - mediaOptionId: i.mediaOptionId - }); - let e = Lg(a, l, t).pipe(Za(e => { - const t = e[1], - i = d[l].switchContext; - t.switchPosition = null == i ? void 0 : i.switchPosition; - const r = null !== (e = null == i ? void 0 : i.userInitiated) && void 0 !== e && e, - n = u["mediaQuery"], - { - desiredRate: s, - isIframeRate: a - } = n, - o = a && c.isStarted && s && s < 0 && s !== c.iframeRate; - (r || o) && (t.flushBeforeAppend = { - start: 0, - end: Number.POSITIVE_INFINITY - }) - })); - return e = l === gu.Variant ? e.pipe(Za(e => { - const t = function(t, i, r, n) { - if (!r) return null; - const { - rootPlaylistService: s, - rootPlaylistQuery: a - } = i, o = a.itemId, l = r[1], d = l.iframe; - let u = a.getInitPTS(n); - if (null == u || !d && u.iframeMode) { - const i = null !== (r = l.startDtsTs) && void 0 !== r ? r : null; - if (null == i) return t.warn("updateInitPTS: Variant data missing."), null; - let e = null !== (t = l.timelineOffset) && void 0 !== t ? t : 0; - d && (e = null !== (t = l.iframeOriginalStart) && void 0 !== t ? t : 0), i.timescale < 1e3 && (i.timescale = 1e3 * i.timescale, i.baseTime = 1e3 * i.baseTime); - const a = B(e, i.timescale), - c = { - baseTime: i.baseTime - a.baseTime, - timescale: i.timescale - }; - s.setInitPTS(o, n, i, e, c, d), u = { - variantDTS: i, - timelineOffset: e, - offsetTimestamp: c, - iframeMode: d - } - } - return u - }(n, a, e, s.discoSeqNum); - fy(n, a, e, t) - })) : en([e, Gu(o.initPTS$(s.discoSeqNum), e => { - const t = u.mediaQuery.isIframeRate; - return null != e && (t || !e.iframeMode) - })]).pipe(hr(([e, t]) => (fy(n, a, e, t), e))), en([r, e]).pipe(hr(e => e[1])) - } - return $i(null) - })).pipe(hr(e => function(g, t, e, i) { - const { - rootPlaylistQuery: r, - mediaSink: n, - config: y - } = t, v = n.mediaQuery, s = v.isIframeRate, a = r.getInitPTS(e); - if (null == a) return g.warn("No initPTS info found"), null; - const o = i[gu.Variant], - l = null == o ? void 0 : o[1]; - if (l && l.iframe !== s) return g.warn(`frag mediaSeqNum ${l.mediaSeqNum} discoSeqNum ${l.discoSeqNum} mediaOptionId ${l.mediaOptionId} doesn't match mediaSink's iframeMode ${s}; discard`), null; - const S = [null, null]; - if (o) { - const [g, t] = o; - let e = a.offsetTimestamp; - if (s) { - const g = t.startDtsTs, - i = B(t.timelineOffset, g.timescale); - e = { - baseTime: g.baseTime - i.baseTime, - timescale: g.timescale - } - } - S[yu.Variant] = { - initSeg: g, - dataSeg: t, - offsetTimestamp: e - } - } - const d = i[gu.AltAudio]; - if (null != d) { - const [g, t] = d; - S[yu.AltAudio] = { - initSeg: g, - dataSeg: t, - offsetTimestamp: a.offsetTimestamp - } - } - const u = S.map((e, t) => { - const i = null == e ? void 0 : e.dataSeg; - if (i) { - const { - itemId: r, - mediaOptionId: n, - mediaSeqNum: s, - discoSeqNum: a, - startPts: o, - endPts: l, - duration: d, - iframe: u - } = i, c = e["offsetTimestamp"], h = b(o, c), p = b(l, c), f = Dg(i), m = S[0]; - return m && m.dataSeg.dropped || i.flushBeforeAppend || !((null === (e = null === (e = v.getBufferInfo(h, y.maxBufferHole)[t]) || void 0 === e ? void 0 : e.buffered) || void 0 === e ? void 0 : e.len) >= p - h) ? { - start: h, - duration: u ? d : p - h, - itemId: r, - mediaOptionId: n, - mediaSeqNum: s, - discoSeqNum: a, - targetDuration: f.mediaOptionDetails.targetduration - } : (g.warn(`${Nu[t]} Discarding append due to complete overlap with existing buffer`), S[t] = null) - } - return null - }); - return u.every(e => !e) ? null : { - appendDataTuple: S, - inFlightFrags: u, - initPTSInfo: a - } - }(n, a, s.discoSeqNum, e)), La(e => { - if (e) return $i(e); { - const e = function(e, r) { - const n = e.enabledMediaOptionKeys, - s = [null, null], - a = [null, null]; - return Bu.map(e => { - var t; - if (_u(n[e])) { - const i = Dg(n[e]).mediaOptionDetailsEntity; - a[e] = null === (t = i.mediaOptionDetails) || void 0 === t ? void 0 : t.ptsKnown, s[e] = { - detailsEntity: i, - switchContext: null === (e = r[e]) || void 0 === e ? void 0 : e.switchContext - } - } - }), s - }(o, d); - return t(n, s, a, i, e) - } - })) - }(i, n, r, e, t)), tc("mediaProducerEpic.emit")) - }; - - function py(e, i, t, r) { - const { - rootPlaylistQuery: n, - mediaLibraryService: s, - config: a - } = t, o = r[i]; - if (t.logger.child({ - name: Nu[i] - }), !o || "Nah" === o.mediaOptionId) return $i({ - detailsEntity: null, - switchContext: null + statsStore.statsEntity = statsEntity; + return statsQuery; + }), + finalize(() => { + statsStore.setLoading(false); + }) + ); + }), + tag('stats.initialized'), + ); + } + */ + + function objectIsEqual(a, b) { + if (a === b) + return true; + if (!a || !b) + return false; + let same = Object.keys(a).length === Object.keys(b).length; + for (const k of Object.keys(a)) { + same = same && ((isNaN(a[k]) && isNaN(b[k])) || a[k] === b[k]); + } + return same; + } + /** + Stats processing Epic. Listens to samples (bandwidth, playlist, + frag) and processes the data and sends back estimates to the store + */ + const statsBandwidthProcessingEpic = (config, statsService, logger) => (bandwidthSampleSource$) => { + return new Observable((subscriber) => { + let historyBasedBandwidthEstimator = new HistoryBasedBandwidthEstimator(config.bandwidthHistoryWindowSize, config.bandwidthHistoryAggregationMethod, { avgLatencyMs: NaN, avgBandwidth: NaN }); + const estimate$ = historyBasedBandwidthEstimator.estimate$; + const sub = merge( + // feed the estimator + bandwidthSampleSource$.pipe(filter((sample) => sample.complete), tap((sample) => { + logger.qe({ + critical: true, + name: 'bandwidthRecordDetails', + data: { + type: MediaOptionNames[sample.mediaOptionType], + observedBitrateExcludingLatency: ((sample.loaded * 8) / (sample.tload - sample.tfirst)) * 1000, + observedBitrate: ((sample.loaded * 8) / (sample.tload - sample.trequest)) * 1000, + }, + }); + }), map((bandwidthSample) => ({ + trequest: bandwidthSample.trequest, + tfirst: bandwidthSample.tfirst, + tload: bandwidthSample.tload, + bitsDownloaded: bandwidthSample.loaded * 8, + })), tag('statsBandwidthProcessingEpic.in'), switchMap((value) => { + historyBasedBandwidthEstimator.record(value); + return EMPTY; + })), estimate$.pipe(distinctUntilChanged(), tag('statsBandwidthProcessingEpic.change'), tap((value) => { + if (statsService) { + statsService.setBandwidthEstimate(value); + logger.qe({ critical: true, name: 'bandwidthEstimate', data: { bandwidthEstimate: value } }); + } + }))).subscribe(subscriber); + return () => { + sub.unsubscribe(); + historyBasedBandwidthEstimator.destroy(); + historyBasedBandwidthEstimator = undefined; + }; + }); + }; + const statsFragProcessingEpic = (config, statsService) => (fragSampleSource$) => { + return fragSampleSource$.pipe(tag('statsFragProcessingEpic.in'), scan((acc, sample) => { + acc.durationSec.add(sample.durationSec); + acc.fragParseMs.add(sample.parseTimeMs); + return acc; + }, { + durationSec: new SimpleAccumulator(), + fragParseMs: new SimpleAccumulator(config.minFragmentCount), + }), map((value) => ({ + maxDurationSec: value.durationSec.max, + avgParseTimeMs: value.fragParseMs.avg, + })), distinctUntilChanged(objectIsEqual), tap((estimate) => statsService.setFragEstimate(estimate))); + }; + const statsPlaylistProcessingEpic = (config, statsService) => (playlistSampleSource$) => { + return playlistSampleSource$.pipe(tag('statsPlaylistProcessingEpic.in'), scan((acc, sample) => { + acc.playlistLoadTimeMs.add(sample.playlistLoadTimeMs); + acc.playlistParseTimeMs.add(sample.playlistParseTimeMs); + return acc; + }, { + playlistLoadTimeMs: new SimpleAccumulator(config.minPlaylistCount), + playlistParseTimeMs: new SimpleAccumulator(config.minPlaylistCount), + }), map((value) => ({ + avgPlaylistLoadTimeMs: value.playlistLoadTimeMs.avg, + avgPlaylistParseTimeMs: value.playlistParseTimeMs.avg, + })), distinctUntilChanged(objectIsEqual), tap((estimate) => { + statsService.setPlaylistEstimate(estimate); + })); + }; + const statsBufferProcessingEpic = (config, statsService) => (bufferMetricSource$) => { + return bufferMetricSource$.pipe(tag('statsBufferMetricProcessingEpic.in'), scan((acc, metric) => { + if (isFiniteNumber(metric.bufferCreationStart) && isFiniteNumber(metric.bufferCreationEnd)) { + acc.bufferCreateMs.add(metric.bufferCreationEnd - metric.bufferCreationStart); + } + if (isFiniteNumber(metric.startInitAppend) && isFiniteNumber(metric.endInitAppend)) { + acc.initFragAppendMs.add(metric.endInitAppend - metric.startInitAppend); + } + if (isFiniteNumber(metric.startDataAppend) && isFiniteNumber(metric.endDataAppend)) { + acc.dataFragAppendMs.add(metric.endDataAppend - metric.startDataAppend); + } + return acc; + }, { + // Seed value + bufferCreateMs: new SimpleAccumulator(), + initFragAppendMs: new SimpleAccumulator(), + dataFragAppendMs: new SimpleAccumulator(config.minFragmentCount), + }), map((value) => ({ + avgBufferCreateMs: value.bufferCreateMs.avg, + avgInitFragAppendMs: value.initFragAppendMs.avg, + avgDataFragAppendMs: value.dataFragAppendMs.avg, + })), distinctUntilChanged(objectIsEqual), tap((estimate) => { + statsService.setBufferEstimate(estimate); + })); + }; + function statsProcessor(config, statsService, item, logger) { + return new Observable((subscriber) => { + initializeStatsFunc(item.itemId); + const statsQuery = createStatsQuery(item.itemId); + const { fragSample$, playlistSample$, bandwidthSample$, bufferMetric$ } = statsQuery; + merge(playlistSample$.pipe(observeOn(asyncScheduler), statsPlaylistProcessingEpic(config, statsService)), bandwidthSample$.pipe(observeOn(asyncScheduler), statsBandwidthProcessingEpic(config, statsService, logger)), fragSample$.pipe(observeOn(asyncScheduler), statsFragProcessingEpic(config, statsService)), bufferMetric$.pipe(observeOn(asyncScheduler), statsBufferProcessingEpic(config, statsService))) + .pipe(switchMapTo(EMPTY)) + .subscribe(subscriber); + return () => { + PersistStats$1.setCombinedEstimate(config, Object.assign(Object.assign(Object.assign(Object.assign({}, statsQuery.getFragEstimate()), statsQuery.getPlaylistEstimate()), statsQuery.getBufferEstimate()), statsQuery.getBandwidthEstimate()), item.serviceName); + statsService.remove(item.itemId); + }; + }); + } + + /** + * MediaSource helper + */ + function getMediaSource() { + return window.MediaSource || window.WebKitMediaSource; + } + + /** + * MediaSource helper + */ + function isSupported() { + try { + const mediaSource = getMediaSource(); + const sourceBuffer = window.SourceBuffer || window.WebKitSourceBuffer; + const isTypeSupported = mediaSource && typeof mediaSource.isTypeSupported === 'function' && mediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"'); + // if SourceBuffer is exposed ensure its API is valid + // safari and old version of Chrome do not expose SourceBuffer globally so checking SourceBuffer.prototype is impossible + const sourceBufferValidAPI = !sourceBuffer || (sourceBuffer.prototype && typeof sourceBuffer.prototype.appendBuffer === 'function' && typeof sourceBuffer.prototype.remove === 'function'); + return !!isTypeSupported && !!sourceBufferValidAPI; + } + catch (_a) { + return false; + } + } + + /* + * Media Element Helper: Contains wrapper class information for HTMLMediaElement + * + * + */ + const MediaElementHelper = { + isWebkitMediaElement(media) { + return 'webkitDroppedFrameCount' in media; + }, + isHtmlVideoElement(media) { + return 'getVideoPlaybackQuality' in media; + }, + timeRangeToArray(timeRange) { + const rangeArr = []; + for (let i = 0; i < timeRange.length; i++) { + rangeArr.push([timeRange.start(i), timeRange.end(i)]); + } + return rangeArr; + }, + }; + + /* + * Access Log Types + * + * + */ + const loggerName$3 = { name: 'access-log' }; + class AccessLog { + constructor(hls, sessionID) { + this.hls = hls; + this.sessionID = sessionID; + this.rtcQuery = null; + this.accessLogData = this.createAccessLogEntry(); + this.accesslog = []; + this.errorlog = []; + } + destroy() { + this.rtcQuery = null; + this.accesslog = []; + this.errorlog = []; + this.accessLogData = undefined; + this.accessLogReporter = undefined; + } + setRTCQuery(query) { + this.rtcQuery = query; + } + setupReporter(appData) { + this.accessLogReporter = { SessionID: this.sessionID, ClientName: appData === null || appData === void 0 ? void 0 : appData.clientName, ServiceName: appData === null || appData === void 0 ? void 0 : appData.serviceName }; + } + addPlayTime(itemId) { + var _a; + const entity = (_a = this.rtcQuery) === null || _a === void 0 ? void 0 : _a.getEntity(itemId); + if (!entity) { + return; + } + const sessionControlRecord = entity.sessionControlRecord; + if (sessionControlRecord.state === 'RTC_STATE_PLAY') { + this.accessLogData.PlayTimeWC = (this.accessLogData.PlayTimeWC || 0) + sessionControlRecord.eventStartTime; + } + getLogger().trace(loggerName$3, 'accessLogData playtime: %d', this.accessLogData.PlayTimeWC); + } + updatePlaybackInfo(itemId, data) { + this.accessLogData.ViFrDr = this.rtcQuery.getEntity(itemId).sessionControlRecord.droppedVideoFrames || 0; + getLogger().trace(loggerName$3, 'accessLogData Framedrop: %d', this.accessLogData.ViFrDr); + } + updateStallCount(itemId) { + const state = this.rtcQuery.getEntity(itemId).sessionControlRecord.state; + if (state !== 'RTC_STATE_PLAY') { + // don't report stall events if we're not playing + getLogger().info(loggerName$3, `skipping low buffer stall event because we're not playing, state: ${state}`); + return; + } + this.accessLogData.StallCount++; + } + updateMediaEngineStallCount(itemId) { + const state = this.rtcQuery.getEntity(itemId).sessionControlRecord.state; + if (state !== 'RTC_STATE_PLAY') { + // don't report stall events if we're not playing + getLogger().info(loggerName$3, `skipping high buffer stall event because we're not playing, state: ${state}`); + return; + } + this.accessLogData.MediaEngineStallCount++; + } + updateCanPlay(itemId) { + this.accessLogData.StartupTime = this.rtcQuery.getEntity(itemId).sessionControlRecord.eventStartTime; + } + updateFragLoaded(itemId, isSeeking, data) { + // use only 'main' (muxed) / 'video' (unmuxed) data to calculate below keys + if (data.fragType === MediaOptionType.Variant) { + this.accessLogData.NetBytes += data.bytes; + this.accessLogData.ADT += data.adt; + //this._accessLogData.SegmentProcessTime += data.processTime; + const accessLogBitrateStats = this.aggregateFragObserverdBitrate(data, ++this.accessLogData.fragmentCnt, this.accessLogData.NetBytes, this.accessLogData.ADT); + this.accessLogData.OBRLast = accessLogBitrateStats.obrLast; + this.accessLogData.OBRMean = accessLogBitrateStats.obrMean; + this.aggregateFragMinMaxBitrate(this.accessLogData, accessLogBitrateStats.obr); + const currentTime = this.hls.realCurrentTime; + if (currentTime > data.startPTS && !isSeeking) { + this.accessLogData.overdue++; + } + if (this.hasGap(data.startPTS, data.endPTS, this.accessLogData.lastStartPTS, this.accessLogData.lastEndPTS)) { + // New continuous playback stream due to timejump + getLogger().info(loggerName$3, 'summarize stats for seek to startPTS: ' + data.startPTS + 's'); + this.addToAccessLog(itemId); + } + if (!this.accessLogData.startPTS) { + // mark first ever fragment start time for fresh access log entry + this.accessLogData.startPTS = data.startPTS; + } + // Remember last loaded fragment start and end time to detect new seek later + this.accessLogData.lastStartPTS = data.startPTS; + this.accessLogData.lastEndPTS = data.endPTS; + // accumulate the 'main' (muxed) / 'video' (unmuxed) bytes & duration to calculate AvgVideoBitrate + this.accessLogData.videoBytes += data.bytes; + this.accessLogData.videoDuration += data.duration; + } + else if (data.fragType === MediaOptionType.AltAudio) { + // accumulate the 'audio' bytes & duration to calculate AvgAudioBitrate + this.accessLogData.audioBytes += data.bytes; + this.accessLogData.audioDuration += data.duration; + } + getLogger().trace(loggerName$3, 'Fragloaded, accessLogData=%o', this.accessLogData); + } + addToAccessLog(itemId) { + const varInfo = this.getVariantInfo(itemId); + const url = this.rtcQuery.getEntity(itemId).sessionControlRecord.curLevelUrl; + const playType = this.rtcQuery.getEntity(itemId).playEndedRecord.PlayType; + if (!url || url === '') { + // Too early, fields may be undefined. + return; + } + const translatedData = this.translateToAccessLogItem(itemId, url, varInfo, playType); + if (translatedData) { + const overage = this.accesslog.length - 20; + if (overage > 0) { + this.accesslog.splice(0, overage); // trim access log entries + } + this.accesslog.push(translatedData); + getLogger().trace(loggerName$3, 'accesslog=%o', this.accesslog); + } + // reset accessLogData after we added an entry to the log + this.accessLogData = this.createAccessLogEntry(); + const recordMediaDur = this.rtcQuery.getEntity(itemId).switchCompleteRecord.MediaDur; + this.accessLogData.lastMediaDur = recordMediaDur ? recordMediaDur : this.hls.bufferedDuration; + } + addToErrorLog(itemId, domain) { + var _a; + const entity = (_a = this.rtcQuery) === null || _a === void 0 ? void 0 : _a.getEntity(itemId); + if (!entity) { + return; + } + let code; + if (domain === 'mediaError') { + code = Number(entity.playErrorRecord.ErrCode); + } + else { + // else if (networkError) + code = Number(entity.nwErrorRecord.ErrCode); + } + const errorInfo = { domain, code }; + const url = entity.sessionControlRecord.curLevelUrl; + const translatedData = this.translateToErrorLogItem(itemId, url, errorInfo); + if (translatedData) { + const overage = this.errorlog.length - 20; + if (overage > 0) { + this.errorlog.splice(0, overage); // trim error log entries + } + this.errorlog.push(translatedData); + getLogger().trace(loggerName$3, 'errorlog=%o', this.errorlog); + } + } + getAccessLog(itemId) { + var _a; + const finalAccessLog = this.accesslog.slice(0); // Make a copy of the access log array + const entity = (_a = this.rtcQuery) === null || _a === void 0 ? void 0 : _a.getEntity(itemId); + if (finalAccessLog && entity) { + // Add the current "open" variant entry + const url = entity.sessionControlRecord.curLevelUrl; + if (url && url !== '') { + const varInfo = this.getVariantInfo(itemId); + const translatedData = this.translateToAccessLogItem(itemId, url, varInfo, this.rtcQuery.getEntity(itemId).playEndedRecord.PlayType); + if (translatedData) { + translatedData['c-provisional-entry'] = true; // mark item as incomplete + finalAccessLog.push(translatedData); + } + } + } + return finalAccessLog; + } + get errorLog() { + return this.errorlog; + } + createAccessLogEntry() { + const entry = { + fragmentCnt: 0, + overdue: 0, + startPTS: 0, + obrMax: 0, + obrMin: 0, + audioBytes: 0, + audioDuration: 0, + videoBytes: 0, + videoDuration: 0, + svrAddrChanged: 0, + svrAddr: '', + PlayTimeWC: 0, + ViFrDr: 0, + StallCount: 0, + MediaEngineStallCount: 0, + ADT: 0, + NetBytes: 0, + StartupTime: 0, + OBRMean: 0, + OBRLast: 0, + }; + return entry; + } + convertStringObjectToPrimitive(str) { + // Prevent crashing Netflix because it assumes url is always a valid string + let output; + if (!str) { + output = ''; + } + else if (typeof str === 'object') { + output = str.toString(); + } + else { + output = str; + } + return output; + } + updateSvrAddrStats(url) { + const urlParts = URLToolkit$1.parseURL(url); + if (urlParts && urlParts.netLoc) { + const portLoc = urlParts.netLoc.indexOf(':'); + let svrAddr = portLoc >= 0 ? urlParts.netLoc.slice(0, portLoc) : urlParts.netLoc; + if (svrAddr.startsWith('//')) { + svrAddr = svrAddr.slice(2); + } + if (!this.accessLogData.svrAddr) { + this.accessLogData.svrAddrChanged = 0; + } + else if (svrAddr !== this.accessLogData.svrAddr) { + this.accessLogData.svrAddrChanged++; + } + // server address + this.accessLogData.svrAddr = svrAddr; + } + } + translateToAccessLogItem(itemId, url, varInfo, playType) { + const uri = this.convertStringObjectToPrimitive(url); + this.updateSvrAddrStats(uri); + // accumulated duration of the media downloaded, in seconds + let currentMediaDur = this.rtcQuery.getEntity(itemId).switchCompleteRecord.MediaDur; + if (!currentMediaDur) { + // total data downloaded after seek, or live/last access log entry + currentMediaDur = this.hls.bufferedDuration; + } + if (!currentMediaDur) { + currentMediaDur = 0; + } + const item = { + // User info -- Internal use only. Commented out to prevent leaking project via access log serviceName (com.apple.hlsjs.airplay) + // item['c-client-name'] = this._accessLogReporter.ClientName; + // item['c-service-identifier'] = this._accessLogReporter.ServiceName; + // Server related + uri: uri, + 's-ip': this.accessLogData.svrAddr, + 's-ip-changes': this.accessLogData.svrAddrChanged, + // number of network read requests over WWAN, mediaRequestsWWAN. Not sure if TV is connected via WiFi. + 'sc-wwan-count': -1, + // transfer duration + 'c-transfer-duration': this.accessLogData.ADT, + // number of bytes transferred + bytes: this.accessLogData.NetBytes, + // number of media requests + 'c-total-media-requests': this.accessLogData.fragmentCnt, + // Playback related + // GUID identifies playback session + 'cs-guid': this.accessLogReporter.SessionID, + // start time + 'c-start-time': this.accessLogData.startPTS, + // startup time + 'c-startup-time': this.accessLogData.StartupTime, + // duration watched (in seconds) + 'c-duration-watched': this.accessLogData.PlayTimeWC / 1000, + // number of dropped video frames + 'c-frames-dropped': this.accessLogData.ViFrDr, + // total number of playback stalls encountered + 'c-stalls': this.accessLogData.StallCount + this.accessLogData.MediaEngineStallCount, + // subtract total data downloaded since last access log entry, if available + 'c-duration-downloaded': this.accessLogData.lastMediaDur ? currentMediaDur - this.accessLogData.lastMediaDur : currentMediaDur, + // total number of times the download of the segments took too long + 'c-overdue': this.accessLogData.overdue, + // video track’s average bit rate, in bits per second 'c-avg-video-bitrate' + 'c-avg-video-bitrate': (this.accessLogData.videoBytes * 8) / (this.accessLogData.videoDuration || 1), + // maximum observed segment download bit rate 'c-observed-max-bitrate' + 'c-observed-max-bitrate': this.accessLogData.obrMax, + // minimum observed segment download bit rate 'c-observed-min-bitrate' + 'c-observed-min-bitrate': this.accessLogData.obrMin, + // throughput, in bits per second, required to play the stream, as advertised by the server. + 'sc-indicated-bitrate': varInfo.bandwidth ? varInfo.bandwidth : 0, + // average throughput, in bits per second, required to play the stream, as advertised by the server + 'sc-indicated-avg-bitrate': varInfo.avgBandwidth ? varInfo.avgBandwidth : 0, + // empirical throughput, in bits per second, across all media downloaded. + 'c-observed-bitrate': this.accessLogData.OBRMean, + // bandwidth that caused a switch (up or down) 'c-switch-bitrate' + 'c-switch-bitrate': this.accessLogData.OBRLast, + // mark item as complete + 'c-provisional-entry': false, + }; + // playback type + item['s-playback-type'] = playType; + // audio track’s average bit rate, in bits per second 'c-avg-audio-bitrate' + if (this.accessLogData.audioBytes) { + // don't show this for muxed content + item['c-avg-audio-bitrate'] = (this.accessLogData.audioBytes * 8) / (this.accessLogData.audioDuration || 1); + } + return item; + } + translateToErrorLogItem(itemId, url, errorInfo) { + const uri = this.convertStringObjectToPrimitive(url); + this.updateSvrAddrStats(uri); // extract URL host name to svrAddr attribute + const item = { + date: new Date(), + 'cs-guid': this.accessLogReporter.SessionID + '-' + itemId, + uri: uri, + 's-ip': this.accessLogData.svrAddr, + status: '' + errorInfo.code, + domain: errorInfo.domain, + }; + return item; + } + hasGap(newStartPTS, newEndPTS, oldStartPTS, oldEndPTS) { + if (typeof newStartPTS === 'undefined' || typeof oldStartPTS === 'undefined') { + // ignore initSegment and newly create access log entry + return false; + } + // seek gap is at least 1 second + return newStartPTS - oldEndPTS > 1 || oldStartPTS - newEndPTS > 1; + } + aggregateFragObserverdBitrate(data, fragments, bytes, adt) { + const obr = (bytes * 8) / (adt / 1000); + const obrLast = (data.bytes * 8) / (data.adt / 1000); + const obrMean = obr / fragments; + return { obr: obr, obrLast: obrLast, obrMean: obrMean }; + } + aggregateFragMinMaxBitrate(target, obr) { + if (!target.obrMax || obr > target.obrMax) { + target.obrMax = obr; + } + if (!target.obrMin || obr < target.obrMin) { + target.obrMin = obr; + } + } + getVariantInfo(itemId) { + var _a; + const url = this.rtcQuery.getEntity(itemId).sessionControlRecord.curLevelUrl; + const variantList = (_a = this.rtcQuery.getEntity(itemId).sessionControlRecord.manifestData) === null || _a === void 0 ? void 0 : _a.variantList; + return url && variantList && variantList[url] ? variantList[url] : {}; + } + } + + const loadMediaFragment = (mediaFragment, config, loadPolicy, onProgress, requestServerInfo, extendMaxTTFB) => { + var _a; + const { absoluteUrl, byteRangeOffset, discoSeqNum, keyTagInfo, iframe, isInitSegment } = mediaFragment; + const url = absoluteUrl; + const { method } = keyTagInfo; + const { start, end } = byteRangeOffset; + const loadConfig = getLoadConfig({ url }, loadPolicy); + let byteRangeStart = start; + let byteRangeEnd = end; + let resetIV = false; + let byterange = !isFiniteNumber(start) && !isFiniteNumber(end) ? undefined : byteRangeOffset; + if (method === 'AES-128' && end && (iframe || isInitSegment)) { + // Map fragment encrypted with method 'AES-128', when served with HTTP Range, has the unencrypted size specified in the range. Modify the range to ensure we read the padding bytes as well. + /** + * Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6 + * + * If the encryption METHOD is AES-128 and the Media Segment is part of + * an I-frame Playlist (Section 4.4.3.6) and it has an EXT-X-BYTERANGE + * tag applied to it, special care needs to be taken in loading and + * decrypting the segment, because the resource identified by the URI is + * encrypted in 16-byte blocks from the start of the resource. + * + * The decrypted I-frame can be recovered by first widening its byte + * range, as specified by the EXT-X-BYTERANGE tag, so that it starts and + * ends on 16-byte boundaries from the start of the resource. + * */ + const fragmentLen = end - start; + if (fragmentLen % 16) { + byteRangeEnd = end + (16 - (fragmentLen % 16)); + } + /** + * Next, the byte range is widened further to include a 16-byte block at + * the beginning of the range. This 16-byte block allows the correct IV + * for the following block to be calculated. + * */ + if (start !== 0) { + resetIV = true; + byteRangeStart = start - 16; + } + byterange = { start: byteRangeStart, end: byteRangeEnd }; + } + let collectServerInstanceInfo = undefined; + if (requestServerInfo && isFiniteNumber(mediaFragment.mediaSeqNum) && mediaFragment.mediaOptionType === MediaOptionType.Variant) { + // Sanitize the list + collectServerInstanceInfo = []; + (_a = loadConfig.reportHTTPResponseHeaders) === null || _a === void 0 ? void 0 : _a.forEach(function (header) { + if (privacyAllowedLoadConfigHeaders.includes(header)) { + collectServerInstanceInfo.push(header); + } + else { + getLogger().warn({ name: 'load-media-fragment' }, `${header} is not in approved privacy list. Actions required.`); + } + }); + if (collectServerInstanceInfo.length === 0) { + collectServerInstanceInfo = undefined; + } + } + const context = { + url: url, + byteRangeOffset: byterange, + checkContentLength: true, + extendMaxTTFB: extendMaxTTFB, + collectServerInstanceInfo: collectServerInstanceInfo, + onProgress, + xhrSetup: config.xhrSetup, + }; + return fromUrlArrayBuffer(context, loadConfig).pipe(map(([loadedData, bwSample, serverInfo]) => { + if (resetIV) { + const buf = loadedData; + mediaFragment.keyTagInfo.iv = new Uint8Array(buf.slice(0, 16)); // First 16 bytes -> IV + loadedData = buf.slice(16); // rest is the actual fragment + } + return [mediaFragment, loadedData, bwSample, serverInfo]; + }), convertToFragmentNetworkError(mediaFragment, false)); + }; + + const kKeySystemIdToPropertiesMap = { + clearkey: ClearKeySystemProperties, + fairplaystreaming: FairPlayStreamingKeySystemProperties, + playready: PlayReadyKeySystemProperties, + widevine: WidevineKeySystemProperties, + }; + /** + * Get the EXT-X-KEY:FORMAT value associated with the key system + * @param keySystemId Identifier string to request keyFormatString property for + */ + const KeySystemFactory = { + getKeySystemFormat(keySystemId) { + const properties = kKeySystemIdToPropertiesMap[keySystemId]; + return properties ? properties.keyFormatString : ''; + }, + getKeySystemSecurityLevel(keySystemId) { + const properties = kKeySystemIdToPropertiesMap[keySystemId]; + return properties ? properties.securityLevels : null; + }, + }; + + const decryptmethods = { NONE: '', 'AES-128': '', 'ISO-23001-7': '', 'SAMPLE-AES': '', 'SAMPLE-AES-CTR': '' }; + function isDecryptMethod(tagValue) { + if (!tagValue) { + return false; + } + return tagValue in decryptmethods; + } + + const HdcpLevels = { + NONE: 0, + 'TYPE-0': 1, + 'TYPE-1': 2, + 'TYPE-2': 3, + }; + function isHdcpLevel(x) { + return x in HdcpLevels; + } + function hdcpLevelToInt(hdcpLevel) { + if (hdcpLevel == null) { + return 4; + } + return HdcpLevels[hdcpLevel]; + } + + const SessionDataKey = { + CHAPTER: 'com.apple.hls.chapters', + TITLE: 'com.apple.hls.title', + TITLE_DESCRIPTIONS: 'com.apple.hls.description', + EPISODE_TITLE: 'com.apple.hls.episode-title', + ARTWORK: 'com.apple.hls.poster', + ROTTEN_TOMATOES_RATING: 'com.apple.hls.rt-rating', + ROTTEN_TOMATOES_AUDIENCE_SCORE: 'com.apple.hls.rt-audience-score', + GENRE: 'com.apple.hls.genre', + RELEASE_DATE: 'com.apple.hls.release-date', + CONTENT_RATING_BADGE: 'com.apple.hls.rating-tag', + OTHER_TEXT_BADGES: 'com.apple.hls.other-tags', + FORMAT: 'com.apple.hls.format', + QUALITY: 'com.apple.hls.quality', + ACCESSIBILITY: 'com.apple.hls.accessibility', + }; + const isSessionDataItem = (item) => 'DATA-ID' in item; + + const VideoRangeValues = ['SDR', 'PQ', 'HLG']; + function isVideoRange(vr) { + return vr != null && VideoRangeValues.includes(vr); + } + + /* + * 2018 Apple Inc. All rights reserved. + */ + const bcp47ShortestLangCode = { + afr: 'af', + aka: 'ak', + amh: 'am', + ara: 'ar', + arg: 'an', + asm: 'as', + ava: 'av', + ave: 'ae', + aym: 'ay', + aze: 'az', + bam: 'bm', + bel: 'be', + ben: 'bn', + bih: 'bh', + bod: 'bo', + bos: 'bs', + bre: 'br', + bul: 'bg', + cat: 'ca', + ces: 'cs', + cha: 'ch', + che: 'ce', + chu: 'cu', + chv: 'cv', + cor: 'kw', + cos: 'co', + cre: 'cr', + cym: 'cy', + dan: 'da', + deu: 'de', + div: 'dv', + dzo: 'dz', + ell: 'el', + eng: 'en', + epo: 'eo', + est: 'et', + eus: 'eu', + ewe: 'ee', + fao: 'fo', + fas: 'fa', + fin: 'fi', + fra: 'fr', + fry: 'fy', + ful: 'ff', + gla: 'gd', + gle: 'ga', + glg: 'gl', + glv: 'gv', + grn: 'gn', + guj: 'gu', + hat: 'ht', + heb: 'he', + her: 'hz', + hin: 'hi', + hmo: 'ho', + hrv: 'hr', + hun: 'hu', + hye: 'hy', + ibo: 'ig', + ido: 'io', + iii: 'ii', + iku: 'iu', + ile: 'ie', + ina: 'ia', + ind: 'id', + isl: 'is', + ita: 'it', + jav: 'jv', + jpn: 'ja', + kal: 'kl', + kan: 'kn', + kas: 'ks', + kat: 'ka', + kau: 'kr', + kaz: 'kk', + khm: 'km', + kik: 'ki', + kin: 'rw', + kir: 'ky', + kom: 'kv', + kon: 'kg', + kor: 'ko', + kua: 'kj', + kur: 'ku', + lao: 'lo', + lat: 'la', + lav: 'lv', + lim: 'li', + lit: 'lt', + ltz: 'lb', + lub: 'lu', + lug: 'lg', + mah: 'mh', + mal: 'ml', + mar: 'mr', + mkd: 'mk', + mlg: 'mg', + mlt: 'mt', + mol: 'mo', + mon: 'mn', + mri: 'mi', + msa: 'ms', + mya: 'my', + nav: 'nv', + nbl: 'nr', + nde: 'nd', + ndo: 'ng', + nep: 'ne', + nld: 'nl', + nno: 'nn', + nob: 'nb', + nya: 'ny', + oci: 'oc', + oji: 'oj', + ori: 'or', + orm: 'om', + oss: 'os', + pan: 'pa', + pli: 'pi', + pol: 'pl', + por: 'pt', + pus: 'ps', + que: 'qu', + roh: 'rm', + ron: 'ro', + run: 'rn', + rus: 'ru', + san: 'sa', + sin: 'si', + slk: 'sk', + slv: 'sl', + sme: 'se', + snd: 'sd', + som: 'so', + spa: 'es', + sqi: 'sq', + srd: 'sc', + srp: 'sr', + sun: 'su', + swa: 'sw', + swe: 'sv', + tah: 'ty', + tam: 'ta', + tat: 'tt', + tel: 'te', + tgk: 'tg', + tgl: 'tl', + tha: 'th', + tir: 'ti', + ton: 'to', + tuk: 'tk', + tur: 'tr', + uig: 'ug', + ukr: 'uk', + urd: 'ur', + uzb: 'uz', + ven: 've', + vie: 'vi', + wln: 'wa', + yid: 'yi', + zha: 'za', + zho: 'zh', + }; + const bcp47Utils = { + isLanguageCode(value) { + return value in bcp47ShortestLangCode; + }, + shortenLanguageCode(langTag) { + let shortenedLangTag; + if (langTag) { + const langCodePos = langTag.indexOf('-'); + const langCode = langCodePos >= 0 ? langTag.slice(0, langCodePos) : langTag; + if (bcp47Utils.isLanguageCode(langCode)) { + shortenedLangTag = bcp47ShortestLangCode[langCode]; + } + if (!shortenedLangTag) { + shortenedLangTag = langCode; + } + if (langCodePos > 0) { + const theRest = langTag.slice(langCodePos + 1); + shortenedLangTag += '-' + theRest; + } + } + return shortenedLangTag; + }, + }; + + const RichestMedia = { + getRichestVideoCodec(videoCodecList) { + if (!videoCodecList || !videoCodecList.length) { + return undefined; + } + const sortedCodecs = videoCodecList.sort((codec1, codec2) => getVideoCodecRanking(codec2) - getVideoCodecRanking(codec1)); + return sortedCodecs && sortedCodecs.length ? sortedCodecs[0] : undefined; + }, + getRichestAudioCodec(audioCodecList) { + if (!audioCodecList || !audioCodecList.length) { + return undefined; + } + const sortedCodecs = audioCodecList.sort((codec1, codec2) => getAudioCodecRanking(codec2) - getAudioCodecRanking(codec1)); + return sortedCodecs && sortedCodecs.length ? sortedCodecs[0] : undefined; + }, + getRichestChannelLayoutForGroupId(audioGroupId, audioMediaOptions) { + if (!audioGroupId || !audioMediaOptions || !audioMediaOptions.length) { + return undefined; + } + let channels = undefined; + const tracksforGroupdId = audioMediaOptions.filter((track) => track.groupId === audioGroupId); + if (tracksforGroupdId && tracksforGroupdId.length) { + const sortedTracksForGroupId = tracksforGroupdId.sort((track1, track2) => MediaUtil.getChannelCount(track2.channels) - MediaUtil.getChannelCount(track1.channels)); + if (sortedTracksForGroupId && sortedTracksForGroupId.length) { + channels = sortedTracksForGroupId[0].channels; + } + } + return channels; + }, + }; + + function _formatError(msg) { + return new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.STEERING_MANIFEST_PARSING_ERROR, false, msg, ErrorResponses.FormatError); + } + function validatePathwayID(pathwayID) { + if (typeof pathwayID !== 'string') { + return _formatError('invalid steering manifest PATHWAY-PRIORITY list item data type'); + } + if (!/^[\w\-\.]+$/.test(pathwayID)) { + return _formatError('steering manifest contains invalid pathway ID: ' + pathwayID); + } + return void 0; + } + + /* + * Fragment + * + * 2018 Apple Inc. All rights reserved. + */ + class FragmentBuilder { + // public backtracked?: boolean; + // public dropped?: number; + // public startDTS?: number; + // public startPTS?: number; + // public endDTS?: number; + // public endPTS?: number; + // public deltaPTS?: number; + // public autoLevel?: boolean; + // public bitrateTest?: boolean; + // public loader?: any; // TODO: fix any + // public loadSuccess?: boolean; + // public data?: Uint8Array; + // public iframeMediaStart?: number; + // public iframeMediaDuration?: number; + // public trackId?: number; // used by subtitle-stream-controller, should be moved + // public loaded: number; // used by fragment loader + // public itemId: string; + constructor(inheritQuery) { + this._url = null; + this._programDateTime = null; + this._byteRange = null; + this.relurl = null; + this.baseurl = null; + this.isInitSegment = false; + this.mediaSeqNum = NaN; + this.cc = NaN; + this.iframe = false; + this.bitrate = NaN; + this.start = NaN; + this.duration = NaN; + this.lastByteRangeEndOffset = NaN; + this.inheritQuery = inheritQuery; + this.tagList = new Array(); + this.iframe = false; + } + getMediaFragment(itemId, mediaOptionId, mediaOptionType) { + var _a; + const fragment = { + mediaOptionType, + absoluteUrl: this.url, + start: this.start, + duration: this.duration, + mediaSeqNum: this.mediaSeqNum, + discoSeqNum: this.cc, + mediaOptionId, + itemId, + isLastFragment: false, + isInitSegment: this.isInitSegment, + }; + if ((_a = this.byteRange) === null || _a === void 0 ? void 0 : _a.length) { + fragment.byteRangeOffset = { start: this.byteRangeStartOffset, end: this.byteRangeEndOffset }; + } + if (this.iframe) { + fragment.iframe = this.iframe; + } + if (this.levelkey) { + fragment.keyTagInfo = this.levelkey; + } + if (this.programDateTime) { + fragment.programDateTime = this.programDateTime; + } + return fragment; + } + // static getStartEndString(frag: Fragment) { + // return `[${toFixed(frag.startDTS || frag.start, 3)},${toFixed(frag.endPTS || frag.start + frag.duration, 3)}]`; + // } + /* + * @returns whether this fragment is equivalent to another fragment + */ + // equals(fragInfo: Fragment) { + // return fragInfo.mediaSeqNum === this.mediaSeqNum && fragInfo.levelId === this.levelId; + // } + get url() { + if (!this._url && this.relurl && this.baseurl) { + this._url = URLToolkit$1.buildAbsoluteURL(this.baseurl, this.relurl, { alwaysNormalize: true, inheritQuery: this.inheritQuery }); + } + return this._url; + } + set url(value) { + this._url = value; + } + get programDateTime() { + if (!this._programDateTime && this.rawProgramDateTime) { + this._programDateTime = new Date(Date.parse(this.rawProgramDateTime)); + } + return this._programDateTime; + } + get byteRange() { + if (!this._byteRange) { + const byteRange = new Array(2); + if (this.rawByteRange) { + const params = this.rawByteRange.split('@', 2); + if (params.length === 1) { + const { lastByteRangeEndOffset } = this; + byteRange[0] = lastByteRangeEndOffset ? lastByteRangeEndOffset : 0; + } + else { + byteRange[0] = parseInt(params[1]); + } + byteRange[1] = parseInt(params[0]) + byteRange[0]; + } + this._byteRange = byteRange; + } + return this._byteRange; + } + get byteRangeStartOffset() { + return this.byteRange[0]; + } + get byteRangeEndOffset() { + return this.byteRange[1]; + } + get rangeString() { + if (this.start >= 0 && this.duration >= 0) { + return `${this.start.toFixed(2)}-${(this.start + this.duration).toFixed(2)}`; + } + return 'N/A'; + } + // useful for a consistent logging message tag + get fragTag() { + return `sn/cc/levelId: ${this.mediaSeqNum}/${this.cc}`; + } + } + + const MediaSelectionHelper = { + parseMediaCharacteristics(mediaCharacteristics) { + return mediaCharacteristics ? mediaCharacteristics.split(/\s*,\s*/) : new Array(); + }, + addMediaToSelectionArray(mediaSelectionGroup, mediaEntry, nextPersistentID) { + if (typeof mediaSelectionGroup === 'undefined') { + return -1; + } + const mediaSelectionGroupOptionArray = mediaSelectionGroup.MediaSelectionGroupOptions; + let mediaSelectionOption = mediaSelectionGroupOptionArray.find((mediaSelectionOption) => mediaSelectionOption.MediaSelectionOptionsMediaType === mediaEntry.mediaType && + mediaSelectionOption.MediaSelectionOptionsName === mediaEntry.name && + mediaSelectionOption.MediaSelectionOptionsExtendedLanguageTag === mediaEntry.lang + // TODO: need ISO 639 code - MediaSelectionOptionsLanguageCode + ); + if (!mediaSelectionOption) { + mediaSelectionOption = { + MediaSelectionOptionsMediaType: mediaEntry.mediaType, + MediaSelectionOptionsExtendedLanguageTag: mediaEntry.lang, + MediaSelectionOptionsIsDefault: mediaEntry.default, + MediaSelectionOptionsName: mediaEntry.name, + MediaSelectionOptionsPersistentID: nextPersistentID, + MediaSelectionOptionsTaggedMediaCharacteristics: mediaEntry.characteristics, + }; + if (mediaEntry.mediaType === MediaTypeFourCC.SUBTITLE) { + mediaSelectionOption.MediaSelectionOptionsDisplaysNonForcedSubtitles = mediaEntry.forced ? Allowed.NO : Allowed.YES; + } + nextPersistentID++; // created a new group, increment the next ID + mediaSelectionGroupOptionArray.push(mediaSelectionOption); + } + mediaEntry.persistentID = mediaSelectionOption.MediaSelectionOptionsPersistentID; + return nextPersistentID; + }, + addDefaultClosedCaptionOption(queueItemId, playlistSubtitleTracks, subtitleMediaSelectionGroup, nextPersistentID) { + const mediaEntry = { + // only 1 text track + itemId: queueItemId, + mediaOptionType: MediaOptionType.Subtitle, + id: 0, + mediaOptionId: 'cc1_' + guid(), + mediaType: MediaTypeFourCC.CLOSEDCAPTION, + inStreamID: 'CC1', + groupId: 'cc', + name: 'English-CC', + type: 'CLOSED-CAPTIONS', + default: false, + autoselect: false, + forced: false, + lang: 'en', + // 'public.easy-to-read' is not default in CoreMedia + characteristics: ['public.accessibility.transcribes-spoken-dialog', 'public.accessibility.describes-music-and-sound'], + persistentID: nextPersistentID, + }; + playlistSubtitleTracks.push(mediaEntry); + MediaSelectionHelper.addMediaToSelectionArray(subtitleMediaSelectionGroup, mediaEntry, nextPersistentID); + }, + }; + + const StringTags = { + NAME: '', + TYPE: '', + DEFAULT: '', + AUTOSELECT: '', + FORCED: '', + LANGUAGE: '', + URI: '', + AUDIO: '', + 'VIDEO-RANGE': '', + 'CLOSED-CAPTIONS': '', + CODECS: '', + BYTERANGE: '', + 'INSTREAM-ID': '', + 'GROUP-ID': '', + CHANNELS: '', + CHARACTERISTICS: '', + KEYFORMAT: '', + KEYFORMATVERSIONS: '', + 'DATA-ID': '', + VALUE: '', + METHOD: '', + 'HDCP-LEVEL': '', + 'ALLOWED-CPC': '', + SUBTITLES: '', + // ext-daterange + ID: '', + CLASS: '', + 'START-DATE': '', + 'END-DATE': '', + 'END-ON-NEXT': '', + // content-steering + 'SERVER-URI': '', + 'PATHWAY-ID': '', + }; + const IntegerTags = { + BANDWIDTH: NaN, + 'AVERAGE-BANDWIDTH': NaN, + }; + const FloatTags = { + 'TIME-OFFSET': NaN, + 'FRAME-RATE': NaN, + SCORE: NaN, + 'PLANNED-DURATION': NaN, + DURATION: NaN, + }; + const HexTags = { + IV: null, + }; + const ResolutionTags = { + RESOLUTION: null, + }; + + /* + * 2019 Apple Inc. All rights reserved. + */ + const DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/; + const ATTR_LIST_REGEX = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g; + // T is a string union type of keys + // K is the value type + class TagParser { + constructor(validTags) { + this.validTags = validTags; + } + isKey(tag) { + return tag in this.validTags; + } + trySetValue(tag, tagValue, outBag) { + if (this.isKey(tag)) { + outBag[tag] = this.parseFunc(tagValue); + return true; + } + return false; + } + } + class StringTagParser extends TagParser { + parseFunc(tag) { + return tag; + } + } + class IntegerTagParser extends TagParser { + parseFunc(tagValue) { + const parsedValue = parseInt(tagValue); + if (parsedValue > Number.MAX_SAFE_INTEGER) { + return Infinity; + } + return parsedValue; + } + } + class FloatTagParser extends TagParser { + constructor() { + super(...arguments); + this.parseFunc = parseFloat; + } + } + class HexTagParser extends TagParser { + parseFunc(tagValue) { + let stringValue = (tagValue || '0x').slice(2); + stringValue = (stringValue.length & 1 ? '0' : '') + stringValue; + const value = new Uint8Array(stringValue.length / 2); + for (let i = 0; i < stringValue.length / 2; i++) { + const val = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16); + if (!isFiniteNumber(val)) { + return undefined; + } + value[i] = val; + } + return value; + } + } + class ResolutionTagParser extends TagParser { + parseFunc(tagValue) { + const res = DECIMAL_RESOLUTION_REGEX.exec(tagValue); + let resolution; + if (res !== null) { + resolution = { + width: parseInt(res[1], 10), + height: parseInt(res[2], 10), + }; + } + return resolution; + } + } + class PlaylistTagParser { + static parseTags(input) { + let match; + const parsedTags = {}; + if (!input) { + return parsedTags; + } + ATTR_LIST_REGEX.lastIndex = 0; + while ((match = ATTR_LIST_REGEX.exec(input)) !== null) { + const tag = match[1].toUpperCase(), quote = '"'; + let value = match[2]; + if (value.indexOf(quote) === 0 && value.lastIndexOf(quote) === value.length - 1) { + value = value.slice(1, -1); + } + for (const tagParser of PlaylistTagParser.tagParsers) { + if (tagParser.trySetValue(tag, value, parsedTags)) { + break; + } + } + } + return parsedTags; + } + } + PlaylistTagParser.tagParsers = [new StringTagParser(StringTags), new IntegerTagParser(IntegerTags), new FloatTagParser(FloatTags), new HexTagParser(HexTags), new ResolutionTagParser(ResolutionTags)]; + + /* + * Playlist Parser + * + * 2018 Apple Inc. All rights reserved. + */ + // https://regex101.com is your friend + const HlsRegex = { + ExtractVariableParameter: /{\$(.*?)}/g, + LevelPlaylistFast: /#EXTINF:(\d*(?:\.\d+)?)(?:,(.*))?|(?!#)(\S.+)|#EXT-X-BYTERANGE: *(.+)|#EXT-X-PROGRAM-DATE-TIME:(.+)|#EXT-X-BITRATE:(.+)|#EXT-X-DATERANGE:(.+)|#.*/g, + LevelPlaylistSlow: /(?:(?:#(EXTM3U))|(?:#EXT-X-(PLAYLIST-TYPE):(.+))|(?:#EXT-X-(MEDIA-SEQUENCE): *(\d+))|(?:#EXT-X-(TARGETDURATION): *(\d+))|(?:#EXT-X-(KEY):(.+))|(?:#EXT-X-(START):(.+))|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DISCONTINUITY-SEQ)UENCE:(\d+))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(VERSION):(\d+))|(?:#EXT-X-(MAP):(.+))|(?:#EXT-X-(I-FRAMES)-ONLY)|(?:#EXT-X-(DEFINE):(.+))|(?:(#)(.*):(.*))|(?:(#)(.*))(?:.*)\r?\n?/, + MasterPlaylist: /#EXT-X-STREAM-INF:([^\n\r]*)[\r\n]+([^\r\n]+)|#EXT-X-I-FRAME-STREAM-INF:([^\r\n]+)|#EXT-X-DEFINE:([^\n\r]*)|#EXT-X-CONTENT-STEERING:([^\n\r]*)/g, + MasterPlaylistAlternateMedia: /#EXT-X-MEDIA:(.*)/g, + SessionData: /#EXT-X-SESSION-DATA[^:]*:(.*)/g, + SessionKeys: /#EXT-X-SESSION-KEY:([^\n\r]*)/g, + VARIABLE_PLAYLIST_REGEX: /(NAME|VALUE)=\"(.*)\",(NAME|VALUE)=\"(.*)\"|(IMPORT)=\"(.*)\"/, + }; + function resolve(url, baseUrl, inheritQuery) { + return URLToolkit.buildAbsoluteURL(baseUrl, url, { alwaysNormalize: true, inheritQuery }); + } + function tryDecodeBase64Metadata(data) { + let decodedObj; + try { + decodedObj = JSON.parse(NumericEncodingUtils$1.base64DecodeToStr(data)); + } + catch (err) { + // logger.error(`Error ${err} decoding metadata value: '${data}'`); + decodedObj = data; + } + return decodedObj; + } + class PlaylistParser { + static isValidPlaylist(playlist) { + return playlist.indexOf('#EXTM3U') === 0; + } + static isMediaPlaylist(playlist) { + return playlist.indexOf('#EXTINF:') > 0 || playlist.indexOf('#EXT-X-PLAYLIST-TYPE:') > 0; + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static replaceVariables(input, variableMap) { + let updatedString, error = false; + if (input && variableMap) { + updatedString = input.replace(HlsRegex.ExtractVariableParameter, (result) => { + HlsRegex.ExtractVariableParameter.lastIndex = 0; + const variable = HlsRegex.ExtractVariableParameter.exec(result); + const property = variable[1]; + // eslint-disable-next-line no-prototype-builtins + if (property && variableMap.hasOwnProperty(property)) { + return variableMap[property]; + } + error = true; + }); + } + return { updatedString, error }; + } + static parseDecryptData(decryptparams, baseurl, keySystemPreference) { + var _a; + const keyAttrs = PlaylistTagParser.parseTags(decryptparams); + const decryptmethod = isDecryptMethod(keyAttrs.METHOD) ? keyAttrs.METHOD : null; + const decryptformat = (_a = keyAttrs.KEYFORMAT) !== null && _a !== void 0 ? _a : null; + if (decryptmethod && PlaylistParser.shouldSelectKeyTag(decryptformat, decryptmethod, keySystemPreference)) { + const decrypturi = keyAttrs.URI; + const decryptiv = keyAttrs.IV ? keyAttrs.IV : null; + if (decrypturi) { + // if there's an IV specified, make sure it's valid + if (keyAttrs.IV && !decryptiv) { + const playlistParsingError = new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_PARSING_ERROR, true, `Invalid IV: ${keyAttrs.IV}`, ErrorResponses.PlaylistErrorInvalidEntry); + playlistParsingError.url = baseurl; + throw playlistParsingError; + } + } + // do not use inheritQuery on key URIs + const resolvedUri = decrypturi ? URLToolkit.buildAbsoluteURL(baseurl, decrypturi, { alwaysNormalize: true }) : baseurl; + const decryptformatversions = (keyAttrs.KEYFORMATVERSIONS ? keyAttrs.KEYFORMATVERSIONS : '1').split('/').map(Number).filter(isFinite); + return new DecryptData(decryptmethod, resolvedUri, decryptiv, decryptformat, decryptformatversions); + } + } + /* + * Utility method to pick a desired key system based on the app preference, EXT-X-KEY tag values. + */ + static shouldSelectKeyTag(decryptformat, decryptmethod, keySystemPreference) { + // If it's Segment encryption or No encryption, just select that key system + // There is no app preference, so select the key system from the manifest + // Now try to match the app preference with the key system in the manifest + return 'AES-128' === decryptmethod || 'NONE' === decryptmethod || null == keySystemPreference || decryptformat === KeySystemFactory.getKeySystemFormat(keySystemPreference); + } + static optOutClosedCaption(levels) { + let noClosedCaption = false; + let hasVideo = false; + if (levels) { + // may be null for playlist with 1 audio only track + for (let i = 0; i < levels.length; ++i) { + const levelInfo = levels[i]; + if (levelInfo.videoCodec) { + hasVideo = true; + if (levelInfo.iframes !== true && levelInfo.closedcaption && levelInfo.closedcaption.toLowerCase() === 'none') { + noClosedCaption = true; // 'CLOSED-CAPTION=NONE' must be present in all levels if it exists in one. + break; + } + } + } + } + return !hasVideo || noClosedCaption; + } + static parseRootPlaylistAlternateMediaOptions(queueItemId, playlist, baseurl, levels, inheritQuery, masterVariableList) { + let result; + let playlistParsingError; + const audioMediaSelectionGroup = { + MediaSelectionGroupAllowEmptySelection: 1, + MediaSelectionGroupMediaCharacteristics: ['public.audible'], + MediaSelectionGroupMediaType: MediaTypeFourCC.AUDIO, + MediaSelectionGroupOptions: [], + }; + const subtitleMediaSelectionGroup = { + MediaSelectionGroupAllowEmptySelection: 1, + MediaSelectionGroupMediaCharacteristics: ['public.legible'], + MediaSelectionGroupMediaType: MediaTypeFourCC.SUBTITLE, + MediaSelectionGroupOptions: [], + }; + const alternateMediaInfo = { + videoAlternateOptions: [], + audioAlternateOptions: [], + subtitleAlternateOptions: [], + audioMediaSelectionGroup, + subtitleMediaSelectionGroup, + }; + let nextPersistentID = 0; + HlsRegex.MasterPlaylistAlternateMedia.lastIndex = 0; + while ((result = HlsRegex.MasterPlaylistAlternateMedia.exec(playlist)) != null) { + const updatedAttrList = PlaylistParser.replaceVariables(result[1], masterVariableList); + if (updatedAttrList.error) { + playlistParsingError = new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.LEVEL_LOAD_ERROR, true, ErrorResponses.PlaylistErrorInvalidEXTXDEFINE.text, ErrorResponses.PlaylistErrorInvalidEXTXDEFINE); + break; + } + const attrs = PlaylistTagParser.parseTags(updatedAttrList.updatedString); + let mediaType = MediaTypeFourCC.UNKNOWN; + let inStreamID; + let trackArray; + let mediaSelectionGroup; + const characteristics = MediaSelectionHelper.parseMediaCharacteristics(attrs.CHARACTERISTICS); + const groupId = attrs['GROUP-ID']; + const channels = attrs.CHANNELS; + let groupCodecList; + let mediaOptionType = null; + switch (attrs.TYPE) { + case 'VIDEO': + mediaType = MediaTypeFourCC.VIDEO; + // mediaOptionType = TODO + trackArray = alternateMediaInfo.videoAlternateOptions; + // no selection options yet + break; + case 'AUDIO': + mediaType = MediaTypeFourCC.AUDIO; + mediaOptionType = MediaOptionType.AltAudio; + trackArray = alternateMediaInfo.audioAlternateOptions; + mediaSelectionGroup = audioMediaSelectionGroup; + const matchingLevel = levels.find((level) => level.audioGroupId === groupId); + groupCodecList = matchingLevel ? matchingLevel.audioCodecList : []; + break; + case 'SUBTITLES': + mediaType = MediaTypeFourCC.SUBTITLE; + mediaOptionType = MediaOptionType.Subtitle; + trackArray = alternateMediaInfo.subtitleAlternateOptions; + mediaSelectionGroup = subtitleMediaSelectionGroup; + break; + case 'CLOSED-CAPTIONS': + mediaType = MediaTypeFourCC.CLOSEDCAPTION; + mediaOptionType = MediaOptionType.Subtitle; + inStreamID = attrs['INSTREAM-ID']; + trackArray = alternateMediaInfo.subtitleAlternateOptions; + mediaSelectionGroup = subtitleMediaSelectionGroup; + break; + } + const mediaEntry = { + itemId: queueItemId, + mediaOptionType, + mediaType, + groupId, + channels, + groupCodecList, + name: attrs.NAME, + type: attrs.TYPE, + default: attrs.DEFAULT === 'YES', + autoselect: attrs.AUTOSELECT === 'YES', + forced: attrs.FORCED === 'YES', + characteristics, + persistentID: nextPersistentID, + id: trackArray ? trackArray.length : 0, + mediaOptionId: `${attrs.NAME}_${groupId}_${nextPersistentID}`, + lang: bcp47Utils.shortenLanguageCode(attrs.LANGUAGE), + }; + if (attrs.URI) { + mediaEntry.url = resolve(attrs.URI, baseurl, inheritQuery); + } + if (!mediaEntry.name) { + mediaEntry.name = mediaEntry.lang; + if (mediaEntry.mediaType === MediaTypeFourCC.CLOSEDCAPTION) { + mediaEntry.name += ' CC'; + } + } + if (mediaEntry.mediaType === MediaTypeFourCC.CLOSEDCAPTION && inStreamID) { + mediaEntry.inStreamID = inStreamID; + } + if (trackArray) { + mediaEntry.id = trackArray.length; + trackArray.push(mediaEntry); + } + nextPersistentID = MediaSelectionHelper.addMediaToSelectionArray(mediaSelectionGroup, mediaEntry, nextPersistentID); + } + if (alternateMediaInfo.subtitleAlternateOptions.length === 0 && !PlaylistParser.optOutClosedCaption(levels)) { + MediaSelectionHelper.addDefaultClosedCaptionOption(queueItemId, alternateMediaInfo.subtitleAlternateOptions, subtitleMediaSelectionGroup, nextPersistentID); + } + return { alternateMediaInfo, playlistParsingError }; + } + static parseMediaOptionPlaylist(playlist, baseurl, inheritQuery = true, keySystemPreference, masterVariableList, itemId, mediaOptionId, mediaOptionType, logger, fragStartTime = 0, // itemStartOffset + iFrameStreamInf = false) { + var _a, _b; + let currentSN = 0; + let totalduration = 0; + const levelDetails = { + itemId, + mediaOptionId, + mediaOptionType, + type: '', + version: 0, + url: baseurl, + initSegments: {}, + fragments: [], + liveOrEvent: true, + startSN: 0, + endSN: 0, + iframesOnly: iFrameStreamInf, + targetduration: 0, + totalduration: 0, + averagetargetduration: 0, + ptsKnown: false, + }; + let decryptData = new DecryptData('NONE', baseurl, null, null, null); + let isKeySystemSelected = false; + let isEncrypted = false; + let cc = 0; + let prevFrag = null; + let frag = new FragmentBuilder(inheritQuery); + let result; + let ix; + let activeBitrate = 0; + let lastProgramDateTime; // Latest unmatched PROGRAM-DATE-TIME tag + const tempVariableList = {}; + let unprocessedRelURL; + let playlistParsingError; + let prevInitSegment; + let ensureMapForDiscontinuity = true; + let ensureMapRefresh = true; + HlsRegex.LevelPlaylistFast.lastIndex = 0; + const incompatibleAssetError = () => new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_PARSING_ERROR, true, 'Invalid key system preference for the playlist', ErrorResponses.IncompatibleAsset); + while ((result = HlsRegex.LevelPlaylistFast.exec(playlist)) !== null) { + const duration = result[1]; + if (duration) { + // INF + frag.duration = parseFloat(duration); + // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 + const title = (' ' + result[2]).slice(1); + frag.title = title ? title : null; + frag.tagList.push(title ? ['INF', duration, title] : ['INF', duration]); + } + else if (result[3]) { + // url + if (isFiniteNumber(frag.duration)) { + const sn = currentSN++; + // frag.type = type; + frag.start = totalduration + fragStartTime; + frag.levelkey = decryptData; + if (isEncrypted && !isKeySystemSelected) { + playlistParsingError = incompatibleAssetError(); + break; + } + // The decrypt data has been associated with the fragment, reset the key system selected flag. + // So that if the key changes for the future segments, we can select it. + isKeySystemSelected = false; + isEncrypted = false; + frag.mediaSeqNum = sn; + // frag.levelId = levelId; // persistent level id + frag.cc = cc; + frag.iframe = levelDetails.iframesOnly; + frag.baseurl = baseurl; + // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 + unprocessedRelURL = PlaylistParser.replaceVariables((' ' + result[3]).slice(1), tempVariableList); + if (unprocessedRelURL.error) { + playlistParsingError = new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.LEVEL_LOAD_ERROR, true, ErrorResponses.PlaylistErrorInvalidEXTXDEFINE.text, ErrorResponses.PlaylistErrorInvalidEXTXDEFINE); + break; + } + frag.relurl = unprocessedRelURL.updatedString; + // bitrate for fragment + frag.bitrate = isFiniteNumber(frag.byteRangeEndOffset) ? ((frag.byteRangeEndOffset - frag.byteRangeStartOffset) * 8) / frag.duration : activeBitrate; + if (lastProgramDateTime != null) { + frag.rawProgramDateTime = lastProgramDateTime; + frag.tagList.push(['PROGRAM-DATE-TIME', frag.rawProgramDateTime]); + const dt = frag.programDateTime.getTime(); + levelDetails.programDateTimeMap = (_a = levelDetails.programDateTimeMap) !== null && _a !== void 0 ? _a : {}; + levelDetails.programDateTimeMap[dt] = frag.mediaSeqNum; + levelDetails.dateMediaTimePairs = (_b = levelDetails.dateMediaTimePairs) !== null && _b !== void 0 ? _b : []; + levelDetails.dateMediaTimePairs.push([dt, frag.start]); + lastProgramDateTime = undefined; + } + levelDetails.fragments.push(frag.getMediaFragment(itemId, mediaOptionId, mediaOptionType)); + prevFrag = frag; + totalduration += frag.duration; + // implicit init segment for iframes or after a discontinuity + if (ensureMapForDiscontinuity || !levelDetails.initSegments[cc] || ensureMapRefresh) { + if (levelDetails.iframesOnly && frag.byteRangeStartOffset > 0 && !levelDetails.initSegments[cc] && !ensureMapRefresh) { + const ifrag = new FragmentBuilder(inheritQuery); + ifrag.url = frag.url; + // allow the init segment to be up to 7 * 188 (must be multiples of 188) + ifrag.rawByteRange = Math.min(frag.byteRangeStartOffset, 1316) + '@0'; + ifrag.baseurl = baseurl; + // ifrag.levelId = levelId; + // ifrag.type = type; + ifrag.isInitSegment = true; + ifrag.cc = cc; + ifrag.levelkey = decryptData; + ifrag.iframe = true; + if (isEncrypted && !isKeySystemSelected) { + playlistParsingError = incompatibleAssetError(); + break; + } + // The decrypt data has been associated with the fragment, reset the key system selected flag. + // So that if the key changes for the future segments, we can select it. + isKeySystemSelected = false; + isEncrypted = false; + levelDetails.initSegments[cc] = ifrag.getMediaFragment(itemId, mediaOptionId, mediaOptionType); + logger.info(`generated implicit initSegment in cc ${cc}`); + } + else if (prevInitSegment) { + if (isFiniteNumber(prevInitSegment.discoSeqNum)) { + // use the last cc's *declared* initSegment (for fmp4 when MAP for a discontinuity is not defined) + logger.info(`assume initSegment from ${prevInitSegment.discoSeqNum} in cc ${cc}`); + } + // deferred assignment of cc + prevInitSegment.discoSeqNum = cc; + levelDetails.initSegments[cc] = prevInitSegment; + } + } + ensureMapForDiscontinuity = false; + ensureMapRefresh = false; + frag = new FragmentBuilder(inheritQuery); + } + } + else if (result[4]) { + // X-BYTERANGE + frag.rawByteRange = (' ' + result[4]).slice(1); + if (prevFrag) { + const lastByteRangeEndOffset = prevFrag.byteRangeEndOffset; + if (lastByteRangeEndOffset) { + frag.lastByteRangeEndOffset = lastByteRangeEndOffset; + } + } + } + else if (result[5]) { + // PROGRAM-DATE-TIME + // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 + lastProgramDateTime = (' ' + result[5]).slice(1); + } + else if (result[6]) { + // X-BITRATE + // Note that we assume that if this tag exists in the playlist then it should exist for every fragment + const parsedBitrate = parseInt(result[6]); + if (isFiniteNumber(parsedBitrate)) { + activeBitrate = parsedBitrate * 1000; + } + } + else if (result[7]) { + // DATERANGE + const rawDaterange = result[7]; + const daterangeTags = PlaylistTagParser.parseTags(rawDaterange); + if (daterangeTags.ID) { + if (!levelDetails.daterangeTags) { + levelDetails.daterangeTags = {}; + } + levelDetails.daterangeTags[daterangeTags.ID] = daterangeTags; + } + } + else { + result = result[0].match(HlsRegex.LevelPlaylistSlow); + for (ix = 1; ix < result.length; ix++) { + if (result[ix] !== undefined) { + break; + } + } + // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 + const unprocessedValue1 = PlaylistParser.replaceVariables((' ' + result[ix + 1]).slice(1), tempVariableList); + const unprocessedValue2 = PlaylistParser.replaceVariables((' ' + result[ix + 2]).slice(1), tempVariableList); + if (unprocessedValue1.error || unprocessedValue2.error) { + playlistParsingError = new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.LEVEL_LOAD_ERROR, true, ErrorResponses.PlaylistErrorInvalidEXTXDEFINE.text, ErrorResponses.PlaylistErrorInvalidEXTXDEFINE); + break; + } + const value1 = unprocessedValue1.updatedString; + const value2 = unprocessedValue2.updatedString; + switch (result[ix]) { + case '#': + frag.tagList.push(value2 ? [value1, value2] : [value1]); + break; + case 'PLAYLIST-TYPE': + levelDetails.type = value1.toUpperCase(); + if (levelDetails.type === 'VOD') { + levelDetails.liveOrEvent = false; + } + break; + case 'MEDIA-SEQUENCE': + if (levelDetails.fragments.length === 0) { + currentSN = levelDetails.startSN = parseInt(value1); + } + break; + case 'TARGETDURATION': + levelDetails.targetduration = parseFloat(value1); + break; + case 'VERSION': + levelDetails.version = parseInt(value1); + break; + case 'EXTM3U': + break; + case 'ENDLIST': + levelDetails.liveOrEvent = false; + break; + case 'DIS': + cc++; + frag.tagList.push(['DIS']); + ensureMapForDiscontinuity = true; + break; + case 'DISCONTINUITY-SEQ': + cc = parseInt(value1); + break; + case 'KEY': + // https://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.4.4 + const decryptparams = value1; + isEncrypted = true; + if (!isKeySystemSelected) { + try { + decryptData = PlaylistParser.parseDecryptData(decryptparams, baseurl, keySystemPreference); + } + catch (error) { + playlistParsingError = error; + } + if (decryptData) { + // Now mark that the key system has been selected. No more playing around with decryptData + isKeySystemSelected = true; + } + } + // if (!isKeySystemSelected || !decryptData) { + // logger.warn(`[Keys] Not selecting EXT-X-KEY tag : ${decryptparams}, IsKeySystemSelected: ${isKeySystemSelected}`); + // } + break; + case 'START': + const startParams = value1; + const startAttrs = PlaylistTagParser.parseTags(startParams); + const startTimeOffset = startAttrs['TIME-OFFSET']; + // TIME-OFFSET can be 0 + if (isFiniteNumber(startTimeOffset)) { + levelDetails.startTimeOffset = startTimeOffset; + } + break; + case 'I-FRAMES': + levelDetails.iframesOnly = true; + break; + case 'MAP': + const mapAttrs = PlaylistTagParser.parseTags(value1); + frag.relurl = mapAttrs.URI; + frag.rawByteRange = mapAttrs.BYTERANGE; + frag.baseurl = baseurl; + // frag.levelId = levelId; + frag.isInitSegment = true; + frag.levelkey = decryptData; // TODO: handle MAP before KEY + if (isEncrypted && !isKeySystemSelected) { + playlistParsingError = incompatibleAssetError(); + break; + } + // The decrypt data has been associated with the fragment, reset the key system selected flag. + // So that if the key changes for the future segments, we can select it. + isKeySystemSelected = false; + isEncrypted = false; + // The occurence of map indicates that it applies to all + // media fragments that follows until the next map. The + // DISCONTINUITY tag can come before or after or never + // relative to MAP tag. + // Common Scenario DISCO -> MAP -> FRAGMENT1 -> Fragment 2 + // Uncommon Scenario Map -> Disco -> FRAGMENTS (live case) + // Defer insertion into initSegments[cc] until media + // fragments are seen + prevInitSegment = frag.getMediaFragment(itemId, mediaOptionId, mediaOptionType); + ensureMapRefresh = true; + // logger.info(`creating new initSegment`); + frag = new FragmentBuilder(inheritQuery); + break; + case 'DEFINE': + const defineList = HlsRegex.VARIABLE_PLAYLIST_REGEX.exec(value1); + const name = 'NAME' === defineList[1] ? defineList[2] : defineList[4], value = 'VALUE' === defineList[1] ? defineList[2] : defineList[4], importName = defineList[5], importValue = defineList[6]; + // eslint-disable-next-line no-prototype-builtins + if (!name && !value && importName === 'IMPORT' && masterVariableList.hasOwnProperty(importValue)) { + tempVariableList[importValue] = masterVariableList[importValue]; + // eslint-disable-next-line no-prototype-builtins + } + else if (name && !importName && defineList[1] !== defineList[3] && !tempVariableList.hasOwnProperty(name)) { + tempVariableList[name] = value; + } + else { + playlistParsingError = new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_PARSING_ERROR, true, ErrorResponses.PlaylistErrorMissingImportReference.text, ErrorResponses.PlaylistErrorMissingImportReference); + break; + } + break; + } + } + // frag.itemId = this.itemId; + } + frag = prevFrag; + // logger.info('found ' + levelDetails.fragments.length + ' fragments'); + if (frag && !frag.relurl) { + levelDetails.fragments.pop(); + totalduration -= frag.duration; + } + if (!levelDetails.liveOrEvent && levelDetails.fragments.length > 0) { + levelDetails.fragments[levelDetails.fragments.length - 1].isLastFragment = true; + } + levelDetails.totalduration = totalduration; + levelDetails.averagetargetduration = totalduration / levelDetails.fragments.length; + levelDetails.endSN = currentSN - 1; + return { mediaOptionDetails: levelDetails, playlistParsingError }; + } + static parseRootPlaylist(queueItemId, playlist, baseurl, inheritQuery) { + var _a; + const levelArray = []; + const masterVariableList = {}; + let contentSteeringOption = null; + let result; + let updatedURI; + let updatedAttrList; + let playlistParsingError; + let scoreAvailable = true; + HlsRegex.MasterPlaylist.lastIndex = 0; + while ((result = HlsRegex.MasterPlaylist.exec(playlist)) != null) { + // result[4] -> regex capture group 4, i.e. EXT-X-DEFINE + if (result[4]) { + // result[1] -> NAME/VALUE keyword, result[2] -> value of result[1], result[3] -> NAME/VALUE keyword, + // result[4] -> value of result[3], result[5] -> IMPORT keyword, result[6] -> value of IMPORT + result = HlsRegex.VARIABLE_PLAYLIST_REGEX.exec(result[4]); + const name = result[1] === 'NAME' ? result[2] : result[4], value = result[1] === 'VALUE' ? result[2] : result[4], importName = result[5]; + // eslint-disable-next-line no-prototype-builtins + if (name && !masterVariableList.hasOwnProperty(name) && !importName && result[1] !== result[3]) { + masterVariableList[name] = value; + } + else { + playlistParsingError = new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_PARSING_ERROR, true, ErrorResponses.PlaylistErrorInvalidEXTXDEFINE.text, ErrorResponses.PlaylistErrorInvalidEXTXDEFINE); + break; + } + } + else if (result[5]) { + // result[5] -> regex capture group 5, i.e. EXT-X-CONTENT-STEERING + const updatedAttrList = PlaylistParser.replaceVariables(result[5], masterVariableList); + if (updatedAttrList.error) { + playlistParsingError = new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_PARSING_ERROR, true, ErrorResponses.PlaylistErrorInvalidEXTXDEFINE.text, ErrorResponses.PlaylistErrorInvalidEXTXDEFINE); + break; + } + const attrs = PlaylistTagParser.parseTags(updatedAttrList.updatedString); + if (typeof attrs['SERVER-URI'] !== 'string') { + playlistParsingError = new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_PARSING_ERROR, true, ErrorResponses.PlaylistErrorInvalidSERVERURI.text, ErrorResponses.PlaylistErrorInvalidSERVERURI); + break; + } + if (attrs['PATHWAY-ID'] != null && typeof attrs['PATHWAY-ID'] !== 'string') { + playlistParsingError = new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_PARSING_ERROR, true, ErrorResponses.PlaylistErrorInvalidPATHWAYID.text, ErrorResponses.PlaylistErrorInvalidPATHWAYID); + break; + } + contentSteeringOption = { + serverURI: resolve(attrs['SERVER-URI'], baseurl, false), + initPathwayID: attrs['PATHWAY-ID'] || '.', + }; + } + else { + // result[1] -> regex capture group 1, i.e. EXT-X-STREAM-INF + // otherwise, result[3] -> regex capture group 3, i.e. EXT-X-I-FRAME-STREAM-INF + updatedAttrList = PlaylistParser.replaceVariables(result[1] || result[3], masterVariableList); + const attrs = PlaylistTagParser.parseTags(updatedAttrList.updatedString); + // result[2] -> regex capture group 2, i.e. URI to be read from the new line (for EXT-X-STREAM-INF) + // otherwise, arrts.URI -> URI to be read from the attributes (for EXT-X-I-FRAME-STREAM-INF) + updatedURI = PlaylistParser.replaceVariables(result[2] || attrs.URI, masterVariableList); + if (updatedAttrList.error || updatedURI.error) { + playlistParsingError = new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_PARSING_ERROR, true, ErrorResponses.PlaylistErrorInvalidEXTXDEFINE.text, ErrorResponses.PlaylistErrorInvalidEXTXDEFINE); + break; + } + if ((attrs.SCORE !== undefined && !isFiniteNumber(attrs.SCORE)) || attrs.SCORE < 0) { + playlistParsingError = new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_PARSING_ERROR, true, ErrorResponses.PlaylistErrorInvalidSCORE.text, ErrorResponses.PlaylistErrorInvalidSCORE); + scoreAvailable = false; + break; + // if SCORE attribute is missing in any level, default to bandwidth + } + else if (scoreAvailable && attrs.SCORE === undefined) { + // logger.warn('SCORE attribute missing in one of the levels, defaulting to bandwidth selection'); + scoreAvailable = false; + } + const bandwidth = attrs.BANDWIDTH; + const avgBandwidth = attrs['AVERAGE-BANDWIDTH']; + const bitrate = avgBandwidth || bandwidth; + const videoRange = (_a = attrs['VIDEO-RANGE']) !== null && _a !== void 0 ? _a : 'SDR'; + if (!isVideoRange(videoRange)) { + // Skip this one. + continue; + } + // Note this is different from audio persistentId; this is an arbitrarily calculated value based on bitrate and index + const mediaOptionId = `level_${(bitrate || 0) + (levelArray.length % 1000) / 1000}`; + const levelInfo = { + itemId: queueItemId, + mediaOptionId, + mediaOptionType: MediaOptionType.Variant, + attrs, + url: resolve(updatedURI.updatedString, baseurl, inheritQuery), + name: attrs.NAME, + audioGroupId: attrs.AUDIO, + subtitleGroupId: attrs.SUBTITLES, + iframes: !!result[3], + bandwidth, + avgBandwidth, + bitrate, + videoRange, + frameRate: attrs['FRAME-RATE'], + allowedCPCMap: PlaylistParser.parseAllowedCPC(attrs['ALLOWED-CPC']), + closedcaption: attrs['CLOSED-CAPTIONS'], + levelCodec: attrs.CODECS, + score: attrs.SCORE, + pathwayID: attrs['PATHWAY-ID'] || '.', + }; + const hdcpString = attrs['HDCP-LEVEL']; + if (isHdcpLevel(hdcpString)) { + levelInfo.hdcpLevel = hdcpString; + } + const resolution = attrs.RESOLUTION; + if (resolution) { + levelInfo.width = resolution.width; + levelInfo.height = resolution.height; + } + if (attrs.CODECS) { + levelInfo.videoCodecList = new Array(); + levelInfo.audioCodecList = new Array(); + const codecs = attrs.CODECS.split(/[ ,]+/); + const { length } = codecs; + for (let i = 0; i < length; i++) { + const codec = codecs[i]; + const prefix = codec.slice(0, 4); + switch (prefix) { + case 'avc1': + levelInfo.videoCodec = MediaUtil.avc1toavcoti(codec); + levelInfo.videoCodecList.push(levelInfo.videoCodec); + break; + case 'avc3': + case 'dvav': + case 'dva1': + // HEVC + case 'hev1': // eslint-disable-line + case 'hvc1': + case 'dvh1': + case 'dvhe': + case 'vp09': + levelInfo.videoCodec = codec; + levelInfo.videoCodecList.push(levelInfo.videoCodec); + break; + case 'mp4a': + case 'ec-3': + case 'ac-3': + levelInfo.audioCodec = codec; + levelInfo.audioCodecList.push(levelInfo.audioCodec); + break; + default: + // logger.warn(`Unrecognized codec ${codec}`); + levelInfo.audioCodec = codec; // Just set it to whatever, maybe it will work. + levelInfo.audioCodecList.push(levelInfo.audioCodec); + break; + } + } + if (levelInfo.audioCodecList.length > 1) { + levelInfo.audioCodec = RichestMedia.getRichestAudioCodec(levelInfo.audioCodecList); + } + if (levelInfo.videoCodecList.length > 1) { + levelInfo.videoCodec = RichestMedia.getRichestVideoCodec(levelInfo.videoCodecList); + } + } + if ((playlistParsingError = validatePathwayID(levelInfo.pathwayID)) != null) { + break; + } + levelArray.push(levelInfo); + } + } + return { variantMediaOptions: levelArray, contentSteeringOption, masterVariableList, playlistParsingError, scoreAvailable }; + } + /** + * Encryption Stuff + */ + static parseAllowedCPC(allowedCPCString) { + if (typeof allowedCPCString !== 'string') { + return null; + } + const allowedCPCMap = {}; + allowedCPCString.split(',').forEach((keyFormatToCPCListStr) => { + const splitList = keyFormatToCPCListStr.split(':'); + let keyFormat, cpcStr; + if (splitList.length === 2) { + // The keyformat + keyFormat = splitList[0].trim(); + cpcStr = splitList[1].trim(); + } + else if (splitList.length > 2) { + cpcStr = splitList[splitList.length - 1].trim(); + splitList.pop(); + keyFormat = splitList.join(':'); + } + else { + return; + } + if (keyFormat in allowedCPCMap) { + return; + } + let cpcLabels = new Array(); + if (cpcStr !== '') { + cpcLabels = cpcStr.split('/').map((cpcLabel) => cpcLabel.trim()); + } + allowedCPCMap[keyFormat] = cpcLabels; + }); + return allowedCPCMap; + } + static parseSessionKeys(playlist, baseurl, keySystemPreference) { + let result; + const sessionKeys = []; + HlsRegex.SessionData.lastIndex = 0; + while ((result = HlsRegex.SessionKeys.exec(playlist))) { + try { + const decryptData = PlaylistParser.parseDecryptData(result[1], baseurl, keySystemPreference); + if (decryptData && decryptData.isEncrypted) { + sessionKeys.push(decryptData); + } + } + catch (error) { + // PlaylistParsingError: "Invalid IV..." + } + } + return sessionKeys; + } + //#region session data + static parseSessionData(sessionDataString, baseurl) { + let result; + const sessionDataItemList = []; + const existingItems = new Set(); + HlsRegex.SessionData.lastIndex = 0; + while ((result = HlsRegex.SessionData.exec(sessionDataString)) != null) { + const attrs = PlaylistTagParser.parseTags(result[1]); + attrs.LANGUAGE = bcp47Utils.shortenLanguageCode(attrs.LANGUAGE); + const uniqueKey = attrs.LANGUAGE ? attrs['DATA-ID'] + '|' + attrs.LANGUAGE : undefined; + if (isSessionDataItem(attrs)) { + if (!uniqueKey || !existingItems.has(uniqueKey)) { + // Prevent duplicated DATA-ID & LANGUAGE pair. + // If LANGUAGE is absent, duplicated DATA-ID items may be okay. + if (attrs['DATA-ID'] === SessionDataKey.OTHER_TEXT_BADGES) { + attrs.VALUE = tryDecodeBase64Metadata(attrs.VALUE); + } + sessionDataItemList.push(attrs); + if (uniqueKey) { + existingItems.add(uniqueKey); + } + } + } + else { + const logger = getLogger(); + logger.error(`Error processing DATA-ID ${attrs['DATA-ID']} and LANGUAGE ${attrs.LANGUAGE}`); + } + } + const sessionData = { + itemList: sessionDataItemList, + baseUrl: baseurl, + }; + return sessionData; + } + } + //#endregion session data + var PlaylistParser$1 = PlaylistParser; + + const fromUrlText = (requestCtx, loadConfig, extendMaxTTFB) => { + const ctx = Object.assign(Object.assign({}, requestCtx), { method: 'GET', responseType: 'text', extendMaxTTFB: extendMaxTTFB }); + const url = ctx.url; + if (isCustomUrl(url)) { + const customUrlLoader = getCustomUrlLoader(); + return customUrlLoader.load(ctx, loadConfig).pipe(map((res) => { + const responseText = res.data.response.data.toString(); + return { + responseText, + responseURL: res.data.response.uri, + stats: res.stats, + }; + })); + } + else { + return fromXMLHttpRequest(ctx, loadConfig).pipe(map(([xhr, stats]) => ({ + responseText: xhr.responseText, + responseURL: xhr.responseURL, + stats: stats, + }))); + } + }; + + const loadMediaOptionDetails = (mediaOption, itemStartOffset, config, loadPolicy, logger, keySystemPreference, statsService, masterVariableList, extendMaxTTFB) => { + const { url, itemId, mediaOptionId, mediaOptionType, iframes = false } = mediaOption; + const loadConfig = getLoadConfig(mediaOption, loadPolicy); + return fromUrlText({ url, xhrSetup: config.xhrSetup }, loadConfig, extendMaxTTFB).pipe(map(({ responseText: rawPlaylist, responseURL: rURL, stats }) => { + logger.debug(`media playlist url: ${url}, baseUrl: ${rURL}`); + if (!rURL) { + logger.warn('Missing response url. Reusing request url as base url'); + rURL = url; + } + const t1 = performance.now(); + const parsed = PlaylistParser.parseMediaOptionPlaylist(rawPlaylist, rURL, true, keySystemPreference, masterVariableList, itemId, mediaOptionId, mediaOptionType, logger, itemStartOffset, iframes); + updatePlaylistAttributes(parsed.mediaOptionDetails); + const t2 = performance.now(); + const { mediaOptionDetails } = parsed; + const playlistSample = { + playlistLoadTimeMs: stats.tload - stats.trequest, + playlistParseTimeMs: t2 - t1, + }; + statsService.setPlaylistSample(playlistSample); + const results = { mediaOptionDetails, stats }; + return results; + }), convertToPlaylistNetworkError(mediaOptionType, mediaOptionId, false, url)); + }; + + const decryptMediaFragment = (mediaFragment, data, config, logger, crypto) => { + return of(data).pipe(switchMap((data) => { + const { keyTagInfo, isInitSegment, iframe, byteRangeOffset } = mediaFragment; + const { method } = keyTagInfo; + const { start, end } = byteRangeOffset; + if (method === 'AES-128') { + if (keyTagInfo.uri && !keyTagInfo.iv && (!keyTagInfo.format || keyTagInfo.format === 'identity')) { + keyTagInfo.iv = createInitializationVector(mediaFragment.mediaSeqNum); + } + const cipherText = data; + const key = keyTagInfo.key.buffer; + const iv = keyTagInfo.iv.buffer; + // For Iframes and Initsegments, byterange indicates the plainText length - ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6 + // For media segments, byterange indicates the length of the cipherText, and the decrypt method will remove the padding to recover the plainText length + const plainTextLength = end && (iframe || isInitSegment) ? end - start : undefined; + /* WebCrypto is the default choice. However, we use JSCrypto in the following scenarios. + 1. WebCrypto disabled via config. + 2. Due to [rdar://73772133], Iframes or Initsegment served via byte-ranges. + */ + const useJSCrypto = !config.enableWebCrypto || !!plainTextLength; + const keyCopy = key.slice(0); // Copy the Key and IV for the webworker. + const ivCopy = iv.slice(0); // Copy the Key and IV for the webworker. + const options = { useJSCrypto, plainTextLength }; + return crypto.decrypt(keyCopy, ivCopy, 'AES-CBC', cipherText, options); + } + else { + // If method is not AES-128, just return the original buffer + return of(data); + } + })); + }; + /** + * Utility method to create an initialization vector from integer + * @param ivValue 128-bit unsigned int representing initialization vector + * @returns {Uint8Array} Buffer representation of the IV + */ + function createInitializationVector(ivValue) { + const uint8View = new Uint8Array(16); + for (let i = 12; i < 16; i++) { + uint8View[i] = (ivValue >> (8 * (15 - i))) & 255; + } + return uint8View; + } + + function fragIsInDetails(details, parsedFrag) { + const fragments = details.fragments; + const fragIdx = parsedFrag.mediaSeqNum - details.startSN; + return fragIdx >= 0 && fragIdx < details.fragments.length && fragEqual(parsedFrag, fragments[fragIdx]); + } + /** + * Update fragment lookup table + */ + function updateFragPTSDTS(details, parsedFrag, startOffset, iframeMode, cpyModified = false, forceUpdateAll = false) { + if (!fragIsInDetails(details, parsedFrag)) { + return; + } + const fragIdx = parsedFrag.mediaSeqNum - details.startSN; + let fragments = details.fragments; + let frag = fragments[fragIdx]; + const { startDtsTs, startPts, endPts } = parsedFrag; + if (cpyModified) { + fragments = details.fragments = details.fragments.slice(); // shallow cpy + frag = fragments[fragIdx] = Object.assign({}, frag); + } + frag.startDtsTs = startDtsTs; + frag.startPts = startPts; + frag.endPts = endPts; + if (iframeMode || frag.isIframeStart !== undefined) { + frag.isIframeStart = iframeMode; + } + frag.start = startOffset; + frag.duration = diffSeconds(endPts, startPts); + // adjust fragment PTS/duration from seqnum-1 to frag 0 + for (let i = fragIdx, canContinue = true; i > 0 && (forceUpdateAll || canContinue); i--) { + canContinue = updatePTS(fragments, i, i - 1, iframeMode, startPts.timescale, cpyModified); + } + // adjust fragment PTS/duration from seqnum to last frag + for (let i = fragIdx, canContinue = true; i < fragments.length - 1 && (forceUpdateAll || canContinue); i++) { + canContinue = updatePTS(fragments, i, i + 1, iframeMode, startPts.timescale, cpyModified); + } + const endFrag = fragments[fragments.length - 1]; + details.totalduration = endFrag.start + endFrag.duration - fragments[0].start; + details.ptsKnown = true; + } + /** + * + * @param fragments fragment list + * @param fromIdx index inside fragments of the reference fragment + * @param toIdx index inside fragments of the fragment to modify + * @param iframeMode (optional) the iframeMode when this was called + * @param timescale (optional) use timescale for deciding if value is changed enough to update + * @param cpyModified (optional) whether we should make a copy of the fragment rather than modify in place + * @returns whether it should continue iterating + */ + function updatePTS(fragments, fromIdx, toIdx, iframeMode, timescale, cpyModified = false) { + const fragFrom = fragments[fromIdx]; + let fragTo = fragments[toIdx]; + const forceUpdateStartTime = iframeMode != null && fragTo.isIframeStart != null && fragTo.isIframeStart !== iframeMode && fragTo.discoSeqNum === fragFrom.discoSeqNum; + let newStart = fragTo.start; + // Update all frag start times in disco after iframe transition or unknown pts + if (forceUpdateStartTime || fragTo.startPts == null) { + if (toIdx > fromIdx) { + newStart = fragFrom.start + fragFrom.duration; + } + else { + newStart = Math.max(fragFrom.start - fragTo.duration, 0); + } + } + const precision = isFiniteNumber(timescale) ? 1 / timescale : Number.EPSILON; + const timeChanged = Math.abs(fragTo.start - newStart) > precision; + if (forceUpdateStartTime || timeChanged) { + // Never update duration. These are parsed values. + if (cpyModified) { + fragTo = fragments[toIdx] = Object.assign({}, fragTo); + } + if (timeChanged) { + fragTo.start = newStart; + } + if (forceUpdateStartTime) { + fragTo.isIframeStart = iframeMode; + } + return true; + } + return false; + } + function updateDateToMediaTimeMap(details) { + if (details.programDateTimeMap) { + details.dateMediaTimePairs = []; + for (const [dtStr, sn] of Object.entries(details.programDateTimeMap)) { + const dt = Number(dtStr); + const frag = details.fragments[sn - details.startSN]; + if (frag) { + const startTime = frag.start; + details.dateMediaTimePairs.push([dt, startTime]); + } + } + details.dateMediaTimePairs.sort((a, b) => { + return a[0] - b[0]; + }); + } + } + + class SimpleListFilter { + constructor(option) { + this.option = option; + } + get name() { + return this.option.name; + } + get priority() { + return this.option.priority; + } + get expiry() { + return this.option.expiry; + } + filter(originalList, options) { + const context = (this.option.initFn && this.option.initFn(originalList, options)) || (options ? Object.assign({}, options) : {}); + let filteredList = originalList; + if (this.option.firstPassFn) { + originalList.forEach((value, index) => this.option.firstPassFn(value, index, context, originalList)); + } + if (this.option.filterFn) { + filteredList = originalList.filter((value, index) => this.option.filterFn(value, index, context, originalList)); + } + if ((this.option.filterFn == null || filteredList.length === 0) && this.option.minSortingFn) { + filteredList = originalList.sort((a, b) => this.option.minSortingFn(a, b, context, originalList)); + } + if (this.option.finalFn) { + this.option.finalFn(filteredList, context, originalList); + } + return filteredList; + } + } + function applyFilters(originalList, filters, options) { + return (filters || []).reduce((list, filter) => filter.filter(list, options), Array.from(originalList)); + } + function isExpired(expiry, now) { + return now >= expiry; + } + function sortFilters(filters) { + const cpy = [...filters]; + return cpy.sort((a, b) => { + var _a, _b; + const priorityA = (_a = a.priority) !== null && _a !== void 0 ? _a : Number.MAX_SAFE_INTEGER; + const priorityB = (_b = b.priority) !== null && _b !== void 0 ? _b : Number.MAX_SAFE_INTEGER; + return priorityA - priorityB; + }); + } + + function getPreferredList(preferredHost, filteredMediaOptionList) { + return filteredMediaOptionList.filter((x) => { var _a; return hasMatchingHost(preferredHost, (_a = x.url) !== null && _a !== void 0 ? _a : null); }); + } + function newRemoveFilter() { + return new SimpleListFilter({ + name: 'Remove Filter', + priority: 0, + filterFn: (value, index, options) => { + return !options || options.removed.every((mediaOptionId) => value.mediaOptionId !== mediaOptionId); + }, + }); + } + function newPenaltyBoxFilter() { + return new SimpleListFilter({ + name: 'Penalty Box Filter', + priority: 1, + filterFn: (value, index, options) => { + const now = performance.now(); + return !options || options.penaltyBoxQueue.every((info) => isExpired(info.expiry, now) || value.mediaOptionId !== info.mediaOptionId); + }, + }); + } + function newCompatibleIdsFilter() { + return new SimpleListFilter({ + name: 'Compatible IDs Filter', + priority: 1, + filterFn: (value, index, options) => { + return !options || options.compatibleIds == null || options.compatibleIds.some((id) => id === value.mediaOptionId); + }, + }); + } + function makeCommonFilters() { + // prettier-ignore + return [ + newRemoveFilter(), + newPenaltyBoxFilter(), + newCompatibleIdsFilter(), + ]; + } + /** + * @brief Query used to get the filtered media option list for a given media option type. + */ + class MediaOptionListQuery extends QueryEntity { + constructor(store, itemId, mediaOptionType) { + super(store); + this.itemId = itemId; + this.mediaOptionType = mediaOptionType; + this.allowFilters = this._initFilters(); + } + /** + * Get the unfiltered media option list + */ + get mediaOptionList() { + var _a; + return ((_a = this.mediaOptionListInfo) === null || _a === void 0 ? void 0 : _a.mediaOptions) || null; + } + get mediaOptionList$() { + return this.mediaOptionListInfo$.pipe(map(({ mediaOptions }) => mediaOptions)); + } + mediaOptionFromId(mediaOptionId) { + var _a, _b; + const list = (_a = this.mediaOptionList) !== null && _a !== void 0 ? _a : []; + return (_b = list.find((option) => option.mediaOptionId === mediaOptionId)) !== null && _b !== void 0 ? _b : null; + } + _getFilteredList(mediaOptionListInfo) { + const mediaOptionList = mediaOptionListInfo.mediaOptions; + return applyFilters(mediaOptionList, this.allowFilters, mediaOptionListInfo); + } + /** + * Get the filtered media option list + */ + get filteredMediaOptionList() { + return this.mediaOptionListInfo ? this._getFilteredList(this.mediaOptionListInfo) : null; + } + get filteredMediaOptionList$() { + return this.mediaOptionListInfo$.pipe(switchMap((mediaOptionListInfo) => { + const fire$ = [VOID]; + const now = performance.now(); + for (const penalty of mediaOptionListInfo.penaltyBoxQueue) { + if (isFiniteNumber(penalty.expiry) && penalty.expiry > now) { + fire$.push(timer(penalty.expiry - now)); + } + } + return merge(...fire$).pipe(map(() => { + return this._getFilteredList(mediaOptionListInfo); + })); + }), distinctUntilArrayItemChanged()); + } + /** + * Get the filtered media option list that also has a hostname matching the preferred host + */ + get preferredMediaOptionList() { + return this.filteredMediaOptionList ? getPreferredList(this.preferredHost, this.filteredMediaOptionList) : []; + } + get preferredMediaOptionList$() { + return combineQueries([this.preferredHost$, this.filteredMediaOptionList$]).pipe(map(([preferredHost, filteredMediaOptionList]) => { + return getPreferredList(preferredHost, filteredMediaOptionList); + })); + } + /** + * @param fromId Media option id used as reference for the switch + * @returns The new host to switch to, or the current host if not switching + */ + getNewHost(fromId) { + const fallback = this.getFallbackVariant(fromId, false, true); + if (fallback === null || fallback === void 0 ? void 0 : fallback.url) { + return getHostName(fallback.url); + } + return this.preferredHost; + } + } + + function isHDRLevel(levelInfo) { + return levelInfo.videoRange === 'PQ' || levelInfo.videoRange === 'HLG'; + } + function isIframeLevel(option) { + return isMatchingIframeLevel(true, option); + } + function isMatchingIframeLevel(iframeMode, option) { + return option.iframes === iframeMode; + } + /** + * Sort function for variants. Ordering determines how ABR mediaOptionList is stored. + * Order will be preserved even after filtering + * + * @param variantList The list to sort + * @param hasScore Use SCORE to sort the list + * @returns Sorted list + */ + function sortVariants(variantList, hasScore) { + const cpy = [...variantList]; + if (hasScore) { + // ascending order by score, then descending order by bitrate + cpy.sort((a, b) => { + return a.score - b.score || b.bitrate - a.bitrate; + }); + } + else { + // bitrate in ascending order + cpy.sort((a, b) => { + return a.bitrate - b.bitrate; + }); + } + return cpy; + } + var Rank; + (function (Rank) { + Rank[Rank["Better"] = 1] = "Better"; + Rank[Rank["Same"] = 0] = "Same"; + Rank[Rank["Worse"] = -1] = "Worse"; + })(Rank || (Rank = {})); + /** + * @param fromVariant The variant we're switching from. We will try to choose something with similar bitrate in all cases + * @param hasScore Whether we should consider SCORE attribute when comparing candidates + * @param candidate Candidate to consider + * @param currentBest Current best candidate + * @returns Better if candidate is better than currentBest, Same if candidate is same as currentBest, else Worse + */ + function compareCandidate(fromVariant, hasScore, candidate, currentBest) { + // TODO: handle SCORE + if (!currentBest || (candidate.bitrate > currentBest.bitrate && candidate.bitrate <= fromVariant.bitrate)) { + return Rank.Better; + } + else if (candidate.bitrate === currentBest.bitrate) { + return Rank.Same; + } + return Rank.Worse; + } + function rankComparison(matchA, matchB) { + if (matchA && !matchB) { + return -1; + } + else if (!matchA && matchB) { + return 1; + } + return 0; + } + function sortFallbackVariants(variantList, fromVariant, hasScore) { + const cpy = [...variantList]; + const fromHost = getHostName(fromVariant.url); + const fromAudioGroup = fromVariant.audioGroupId; + // sort score or bitrate in descending order + // tie breaker: hostname, then audioGroup + cpy.sort((a, b) => { + let rank = 0; + const useScore = hasScore && isFiniteNumber(a.score) && isFiniteNumber(b.score); + const ABetterThanB = useScore ? a.score > b.score && a.score <= fromVariant.score : a.bitrate > b.bitrate && a.bitrate <= fromVariant.bitrate; + const AEqualB = useScore ? a.score === b.score : a.bitrate === b.bitrate; + if (ABetterThanB) { + rank = -1; + } + else if (AEqualB) { + const AmatchHost = hasMatchingHost(fromHost, a.url); + const BmatchHost = hasMatchingHost(fromHost, b.url); + rank = rankComparison(AmatchHost, BmatchHost); + if (rank === 0) { + const AmatchGroup = !fromAudioGroup || a.audioGroupId === fromAudioGroup; + const BmatchGroup = !fromAudioGroup || b.audioGroupId === fromAudioGroup; + rank = rankComparison(AmatchGroup, BmatchGroup); + } + } + else { + rank = 1; + } + return rank; + }); + return cpy; + } + function newHDRFilter() { + return new SimpleListFilter({ + name: 'HDR Filter', + priority: 1, + filterFn: (value, index, options) => { + return !options || (options.hasHdrLevels && options.preferHDR) === isHDRLevel(value); + }, + }); + } + function isSizeWithinTolerance(variant, viewportInfo) { + const TOLERANCE_FACTOR = 1.35; // Value inherited from native stack + return (variant.width < viewportInfo.width * TOLERANCE_FACTOR && + variant.height < viewportInfo.height * TOLERANCE_FACTOR && + variant.width * variant.height < viewportInfo.width * viewportInfo.height * TOLERANCE_FACTOR); + } + function newViewportFilter() { + return new SimpleListFilter({ + name: 'Viewport Filter', + priority: 1, + firstPassFn: (value, index, context) => { + if (context && value && !value.iframes && value.videoCodec) { + // Find the lowest non-iframe level with video + const lowestBitrate = !context.lowestBitrate || value.bitrate < context.lowestBitrate ? value.bitrate : context.lowestBitrate; + context.lowestBitrate = lowestBitrate; + } + }, + filterFn: (value, index, context) => { + if (!value || !context || !context.viewportInfo || !value.videoCodec || !context.lowestBitrate) { + // Don't apply this filter + return true; + } + // Allow only the sizes within tolerance or if it's the lowest bitrate + return isSizeWithinTolerance({ width: value.width, height: value.height }, context.viewportInfo) || value.bitrate === context.lowestBitrate; + }, + }); + } + function newHDCPFilter() { + return new SimpleListFilter({ + name: 'HDCP Filter', + priority: 2, + filterFn: (value, index, options) => { + return !options || !isHdcpLevel(options.maxHdcpLevel) || hdcpLevelToInt(value.hdcpLevel) < hdcpLevelToInt(options.maxHdcpLevel); + }, + }); + } + class VariantMediaOptionListQuery extends MediaOptionListQuery { + constructor(store, itemId) { + super(store, itemId, MediaOptionType.Variant); + } + static makeFilters() { + // prettier-ignore + return sortFilters(makeCommonFilters().concat([ + newHDRFilter(), + newViewportFilter(), + newHDCPFilter(), + // TODO: Fix rdar://81171922 and re-enable content steering + // newPathwayFilter(), + ])); + } + _initFilters() { + return VariantMediaOptionListQuery.kAllowFilters; + } + get preferredHost() { + var _a, _b; + return (_b = (_a = this.mediaOptionListInfo) === null || _a === void 0 ? void 0 : _a.preferredHost) !== null && _b !== void 0 ? _b : null; + } + get preferredHost$() { + return this.selectEntity(this.itemId, (entity) => { + var _a; + return (_a = entity === null || entity === void 0 ? void 0 : entity.mediaOptionListTuple[MediaOptionType.Variant].preferredHost) !== null && _a !== void 0 ? _a : null; + }); + } + get mediaOptionListInfo() { + var _a, _b; + return (_b = (_a = this.getEntity(this.itemId)) === null || _a === void 0 ? void 0 : _a.mediaOptionListTuple[MediaOptionType.Variant]) !== null && _b !== void 0 ? _b : null; + } + get mediaOptionListInfo$() { + return this.selectEntity(this.itemId, (entity) => { var _a; return (_a = entity === null || entity === void 0 ? void 0 : entity.mediaOptionListTuple) === null || _a === void 0 ? void 0 : _a[MediaOptionType.Variant]; }).pipe(filterNullOrUndefined()); + } + get hdrMode$() { + return this.mediaOptionListInfo$.pipe(map((info) => { + return info.preferHDR === true && info.hasHdrLevels; + }), distinctUntilChanged()); + } + get maxHdcpLevel$() { + return this.selectEntity(this.itemId, (entity) => { + var _a; + const info = (_a = entity === null || entity === void 0 ? void 0 : entity.mediaOptionListTuple) === null || _a === void 0 ? void 0 : _a[MediaOptionType.Variant]; + return info === null || info === void 0 ? void 0 : info.maxHdcpLevel; + }).pipe(distinctUntilChanged()); + } + // excludingOptions is useful for skipping visited variants in successive getFallbackVariant + listFallbackVariants(fromId, sdrOnly, shouldSwitchHosts, shouldDownswitch, excludingOptions = undefined) { + var _a; + const mediaOptionListInfo = this.mediaOptionListInfo; + const fromVariant = (_a = this.mediaOptionList) === null || _a === void 0 ? void 0 : _a.find((x) => x.mediaOptionId === fromId); + if (!fromVariant || !mediaOptionListInfo) { + return null; + } + const filteredList = this.makeFilteredListFromVariant(fromVariant, sdrOnly, excludingOptions); + if (!filteredList) { + return null; + } + const fromHost = getHostName(fromVariant.url); + const hasScore = mediaOptionListInfo.hasScore; + return VariantMediaOptionListQuery._listFallbackVariants(filteredList, fromVariant, fromHost, hasScore, shouldSwitchHosts, shouldDownswitch, excludingOptions); + } + // excludingOptions is useful for skipping visited variants in successive getFallbackVariant + getFallbackVariant(fromId, sdrOnly, shouldSwitchHosts, excludingOptions = undefined) { + var _a; + const mediaOptionListInfo = this.mediaOptionListInfo; + const fromVariant = (_a = this.mediaOptionList) === null || _a === void 0 ? void 0 : _a.find((x) => x.mediaOptionId === fromId); + if (!fromVariant || !mediaOptionListInfo) { + return null; + } + const filteredList = this.makeFilteredListFromVariant(fromVariant, sdrOnly, excludingOptions); + if (!filteredList) { + return null; + } + const fromHost = getHostName(fromVariant.url); + const hasScore = mediaOptionListInfo.hasScore; + return VariantMediaOptionListQuery._getFallbackVariant(filteredList, fromVariant, fromHost, hasScore, shouldSwitchHosts); + } + makeFilteredListFromVariant(fromVariant, sdrOnly, excludingOptions = undefined) { + let mediaOptionListInfo = this.mediaOptionListInfo; + if (!fromVariant || !this.mediaOptionList || !mediaOptionListInfo) { + return null; + } + mediaOptionListInfo = Object.assign(Object.assign({}, mediaOptionListInfo), { includeAllEligiblePathways: true }); + const originalList = Array.from(this.mediaOptionList); + let filteredList = sdrOnly ? applyFilters(originalList, this.allowFilters, Object.assign(Object.assign({}, mediaOptionListInfo), { preferHDR: false, compatibleIds: null })) : this._getFilteredList(mediaOptionListInfo); + if (!filteredList) { + return null; + } + if (excludingOptions && excludingOptions.length > 0) { + filteredList = filteredList.filter((option) => !excludingOptions.includes(option.mediaOptionId)); + } + return filteredList; + } + get hasIframes() { + var _a, _b; + return (_b = (_a = this.mediaOptionListInfo) === null || _a === void 0 ? void 0 : _a.hasIframeLevels) !== null && _b !== void 0 ? _b : false; + } + /** + * @returns true if fromId is an HDR level and we can switch to a SDR level + */ + canSwitchToSDR(fromId, shouldSwitchHosts, shouldDownswitch = false) { + const mediaOptionListInfo = this.mediaOptionListInfo; + if (!this.mediaOptionList || !mediaOptionListInfo) { + return false; + } + const fromVariant = this.mediaOptionFromId(fromId); + if (!fromVariant) { + return false; + } + if (!isHDRLevel(fromVariant)) { + return false; + } + const fromHost = getHostName(fromVariant.url); + // Get filtered list but override hdr preference to false + const originalList = Array.from(this.mediaOptionList); + const filteredList = applyFilters(originalList, this.allowFilters, Object.assign(Object.assign({}, mediaOptionListInfo), { preferHDR: false, compatibleIds: null })); + const hasScore = mediaOptionListInfo.hasScore; + return VariantMediaOptionListQuery._getFallbackVariant(filteredList, fromVariant, fromHost, hasScore, shouldSwitchHosts, shouldDownswitch) != null; + } + // Generic helper function for finding a fallback given a filtered list and the failing variant + static _listFallbackVariants(filteredList, fromVariant, fromHost, hasScore, shouldSwitchHosts, shouldDownswitch = false, excludingOptions = null) { + // shouldDownswitch: On timeout we want to pick variant with bitrate lower than currently being downloaded + let needFromVariant = false; + const candidateList = filteredList.filter((candidate) => { + const validCandidate = candidate.iframes === fromVariant.iframes && (!shouldSwitchHosts || !hasMatchingHost(fromHost, candidate.url)); + const lesserTier = shouldDownswitch ? candidate.bitrate < fromVariant.bitrate : candidate.bitrate <= fromVariant.bitrate; + const success = validCandidate && lesserTier; + if (fromVariant.mediaOptionId === candidate.mediaOptionId) { + needFromVariant = success; // if needed, add fromVariant to the top of candidateList later + return false; + } + return success; + }); + const result = sortFallbackVariants(candidateList, fromVariant, hasScore); + if (needFromVariant && (!excludingOptions || !excludingOptions.includes(fromVariant.mediaOptionId))) { + result.unshift(fromVariant); // put fromVariant at the top if not excluded + } + return result; + } + // Generic helper function for finding a fallback given a filtered list and the failing variant + static _getFallbackVariant(filteredList, fromVariant, fromHost, hasScore, shouldSwitchHosts, shouldDownswitch = false) { + // Choose best option between these two + let newVariant = null; + // On timeout we want to pick variant with bitrate lower than currently being downloaded + if (shouldDownswitch) { + filteredList = filteredList.filter((option) => option.bitrate < fromVariant.bitrate); + } + const candidateList = filteredList.filter((candidate) => candidate.mediaOptionId !== fromVariant.mediaOptionId && candidate.iframes === fromVariant.iframes); + if (shouldSwitchHosts && fromHost != null) { + // Choose from different host + for (const candidate of candidateList) { + if (compareCandidate(fromVariant, hasScore, candidate, newVariant) === Rank.Better && !hasMatchingHost(fromHost, candidate.url)) { + newVariant = candidate; + } + } + } + else { + // Choose best one from any host. Tie breaker favors current host + // If hosts are same between candidates, favor same audio group + for (const candidate of candidateList) { + const rank = compareCandidate(fromVariant, hasScore, candidate, newVariant); + if (rank === Rank.Better || + (rank === Rank.Same && hasMatchingHost(fromHost, candidate.url) && (!hasMatchingHost(fromHost, newVariant.url) || candidate.audioGroupId === fromVariant.audioGroupId))) { + newVariant = candidate; + } + } + } + return newVariant; + } + // Get media option matching the input mediaOption groupID + getMatchingVariant(fromId, mediaOption) { + const fromVariant = this.mediaOptionFromId(fromId); + const fromHost = getHostName(fromVariant === null || fromVariant === void 0 ? void 0 : fromVariant.url); + const whichGroup = mediaOption.mediaOptionType === MediaOptionType.AltAudio ? 'audioGroupId' : 'subtitleGroupId'; + let bestCandidate = null; + const hasScore = this.mediaOptionListInfo.hasScore; + for (const candidate of this.filteredMediaOptionList) { + if (candidate[whichGroup] !== mediaOption.groupId) { + continue; + } + if (!fromVariant) { + bestCandidate = candidate; // First mediaOption with matching groupId + break; + } + const rank = compareCandidate(fromVariant, hasScore, candidate, bestCandidate); + if (rank === Rank.Better || + (rank === Rank.Same && bestCandidate.mediaOptionId !== fromVariant.mediaOptionId && (candidate.mediaOptionId === fromVariant.mediaOptionId || hasMatchingHost(fromHost, candidate.url)))) { + bestCandidate = candidate; + } + } + return bestCandidate; + } + get currentPathwayID() { + var _a; + return (_a = this.mediaOptionListInfo) === null || _a === void 0 ? void 0 : _a.currentPathwayID; + } + } + VariantMediaOptionListQuery.kAllowFilters = VariantMediaOptionListQuery.makeFilters(); + + /** + * Error handling policies. This file handles general error handling + * + * @copyright 2018 Apple Inc. All rights reserved. + */ + /** + * @brief Common error actions + */ + var NetworkErrorAction; + (function (NetworkErrorAction) { + NetworkErrorAction[NetworkErrorAction["DoNothing"] = 0] = "DoNothing"; + NetworkErrorAction[NetworkErrorAction["SendEndCallback"] = 1] = "SendEndCallback"; + NetworkErrorAction[NetworkErrorAction["SendAlternateToPenaltyBox"] = 2] = "SendAlternateToPenaltyBox"; + NetworkErrorAction[NetworkErrorAction["RemoveAlternatePermanently"] = 3] = "RemoveAlternatePermanently"; + NetworkErrorAction[NetworkErrorAction["InsertDiscontinuity"] = 4] = "InsertDiscontinuity"; + NetworkErrorAction[NetworkErrorAction["RetryRequest"] = 5] = "RetryRequest"; + })(NetworkErrorAction || (NetworkErrorAction = {})); + /** + * PenaltyBox or Remove only. Additional flags for determining what we should do + */ + var ErrorActionFlags; + (function (ErrorActionFlags) { + ErrorActionFlags[ErrorActionFlags["MoveAllAlternatesMatchingHost"] = 1] = "MoveAllAlternatesMatchingHost"; + ErrorActionFlags[ErrorActionFlags["MoveAllAlternatesMatchingHDCP"] = 2] = "MoveAllAlternatesMatchingHDCP"; + ErrorActionFlags[ErrorActionFlags["SwitchToSDR"] = 4] = "SwitchToSDR"; + })(ErrorActionFlags || (ErrorActionFlags = {})); + /** + * Modify the error action if we have no fallbacks to switch to + * + * @param errorAction Original error action + * @param errorCode The error code + */ + function _modifyErrorActionIfLastValidVariantInternal(errorAction, errorCode) { + switch (errorAction) { + case NetworkErrorAction.SendAlternateToPenaltyBox: + errorAction = NetworkErrorAction.RetryRequest; + if (errorCode === 401 || + errorCode === 403 || + errorCode === 407 || + errorCode === ErrorResponses.CorruptStream.code || + errorCode === ErrorResponses.LivePlaylistUpdateError.code // We've already retried up to max at this point + ) { + errorAction = NetworkErrorAction.SendEndCallback; + } + break; + case NetworkErrorAction.RemoveAlternatePermanently: + errorAction = NetworkErrorAction.SendEndCallback; + break; + } + return errorAction; + } + /** + * Common code to modify if it's the last variant of its kind in the controller + * @param originalAction Original action from getActionForCommonError + * @param isPrefetch Was a prefetch (or not needed immediately for playback) + * @param errorCode The error code + * @param mediaOptionId Option id + * @param mediaOptionType The type + * @param rootQuery Root playlist query + */ + function modifyErrorActionIfCurrentLevelIsLastValidLevel(originalAction, isPrefetch, errorCode, mediaOptionId, mediaOptionType, rootQuery, rootService, isTimeout = false) { + const mediaListQuery = rootQuery.mediaOptionListQueries[mediaOptionType]; + const shouldSwitchHosts = (originalAction.errorActionFlags & ErrorActionFlags.MoveAllAlternatesMatchingHost) != 0; + const mediaOption = rootQuery.mediaOptionListQueries[mediaOptionType].mediaOptionFromId(mediaOptionId); + const fallbackMediaOptions = rootService.getFallbackMediaOptionTupleFromMediaOptionId(rootQuery, mediaOptionType, mediaOptionId, mediaOption.backingMediaOptionId, false, shouldSwitchHosts, isTimeout); + let { errorAction, errorActionFlags } = originalAction; + if (!rootQuery.isValidMediaOptionTuple(fallbackMediaOptions)) { + if (!isPrefetch) { + errorAction = _modifyErrorActionIfLastValidVariantInternal(errorAction, errorCode); + errorActionFlags = 0; + } + if (mediaListQuery instanceof VariantMediaOptionListQuery) { + const mediaOption = mediaListQuery.mediaOptionFromId(mediaOptionId); + if (mediaOption.iframes === true) { + errorAction = NetworkErrorAction.DoNothing; + errorActionFlags = 0; + rootService.logger.debug(`[modifyErrorAction] skip error handling for iframes; isPrefetch ${isPrefetch} fallback ${fallbackMediaOptions}`); + } + else if (!isPrefetch && rootService.canSwitchToSDR(rootQuery, mediaOptionId, shouldSwitchHosts, isTimeout)) { + errorAction = originalAction.errorAction; + errorActionFlags = ErrorActionFlags.SwitchToSDR; + rootService.logger.debug(`[modifyErrorAction] switchToSDR; isPrefetch ${isPrefetch} fallback ${fallbackMediaOptions} isTimeout ${isTimeout}`); + } + } + } + else if (hasMatchingHost(mediaListQuery.preferredHost, fallbackMediaOptions[mediaOptionType].url)) { + errorActionFlags &= ~ErrorActionFlags.MoveAllAlternatesMatchingHost; + rootService.logger.debug(`[modifyErrorAction] matched preferredHost ${mediaListQuery.preferredHost}; isPrefetch ${isPrefetch} fallback ${fallbackMediaOptions}`); + } + return { errorAction, errorActionFlags }; + } + // Get default error action based on error codes + function _getActionForCommonNetworkError(errorCode) { + let errorAction; + let errorActionFlags = 0; + switch (errorCode) { + case 0: + // 0 is sometimes returned as a CORS error or a "muted" error when "--disable-web-security --ignore-certificate-errors" are used in Chrome. + errorAction = NetworkErrorAction.SendAlternateToPenaltyBox; + errorActionFlags = ErrorActionFlags.MoveAllAlternatesMatchingHost; + break; + case 410: + errorAction = NetworkErrorAction.RemoveAlternatePermanently; + break; + case 500: // Internal Server error + case 502: // Bad gateway + case 503: // Service unavailable + case 504: // Gateway timeout + case 404: // NotFound + case 409: // Conflict + case 401: // Unauthorized + case 403: // Forbidden + case 407: // Proxy error + case ErrorResponses.LivePlaylistUpdateError.code: // -12888 + case ErrorResponses.PlaylistNotReceived.code: // -12884 + default: + errorAction = NetworkErrorAction.SendAlternateToPenaltyBox; + errorActionFlags = 0; + break; + } + return { errorAction, errorActionFlags }; + } + /** + * Modify the error action if we think we're going to take too long.... + * + * @param error The error + * @param originalAction The original action + * @param isPrefetch true if this is not for playback but optimistic load + * @param maxTimeouts The maximum number of consecutive timeouts allowed + * @param rootQuery The root playlist query + * @return modified error action + */ + function _modifyActionOnTimeout(error, originalAction, isPrefetch, maxTimeouts, rootQuery) { + var _a, _b; + let { errorAction, errorActionFlags } = originalAction; + if (error.isTimeout) { + const { mediaOptionType } = error; + const consecutiveTimeouts = (_b = (_a = rootQuery.getErrorInfoByType(mediaOptionType)) === null || _a === void 0 ? void 0 : _a.timeouts['load']) !== null && _b !== void 0 ? _b : 0; + if (!isPrefetch && consecutiveTimeouts >= maxTimeouts) { + // this.logger.warn(`Hit max consecutive penalty due to timeout count: ${error.details}`); + errorAction = NetworkErrorAction.DoNothing; // don't treat timeouts as fatal, if the NW conditions don't improve, low buffer will lead to end playback + errorActionFlags = 0; + } + } + return { errorAction, errorActionFlags }; + } + /** + * Get the policy for a manifest load error + * @param error The error + */ + function getActionForManifestError(error) { + let { errorAction, errorActionFlags } = _getActionForCommonNetworkError(error.response.code); + errorAction = _modifyErrorActionIfLastValidVariantInternal(errorAction, error.response.code); + errorActionFlags = 0; // cannot default to a backup CDN for manifest + // this.logger.info(`Manifest error ${errorAction}`); + return { errorAction, errorActionFlags }; + } + /** + * Get the policy for a playlist load error + * @param error The error + * @param isPrefetch true if this is not critical for playback. For instance i-frame + * @param maxTimeouts The maximum number of consecutive timeouts allowed + * @param rootQuery The root playlist query + * @returns The action to perform + */ + function getActionForPlaylistOrFragError(error, isPrefetch, maxTimeouts, rootQuery, rootService) { + const errorCode = error.response.code; + let result = _getActionForCommonNetworkError(errorCode); + const { mediaOptionId, mediaOptionType } = error; + if (isPrefetch) { + // Do not modify on last level in prefetch mode + result.errorActionFlags &= ~ErrorActionFlags.MoveAllAlternatesMatchingHost; + } + else { + result = _modifyActionOnTimeout(error, result, isPrefetch, maxTimeouts, rootQuery); + } + result = modifyErrorActionIfCurrentLevelIsLastValidLevel(result, isPrefetch, errorCode, mediaOptionId, mediaOptionType, rootQuery, rootService, error.isTimeout); + // rootService.logger.info(`[${error.type}] Got playlist error ${errorCode} ${error.message} ${JSON.stringify(result)}`); + return result; + } + /** + * Handle a network error due to crypt key failure + * @param error KeyError + * @param mediaOptionId One of the media options associated with this error + * @param mediaOptionType The media option type associated with the media option id + * @param rootQuery The root query to use + */ + function getActionForCryptKeyNetworkError(error, mediaOptionId, mediaOptionType, rootQuery, rootService) { + let action = { + errorAction: NetworkErrorAction.SendAlternateToPenaltyBox, + errorActionFlags: 0, + }; + if (error instanceof KeyRequestTimeoutError) { + action.errorAction = NetworkErrorAction.SendAlternateToPenaltyBox; + } + else { + const isOkToRetry = error.isOkToRetry; + const isHDCPError = error.keyErrorReason === KeyRequestErrorReason.OutputRestricted; + if (isHDCPError) { + action.errorAction = NetworkErrorAction.RemoveAlternatePermanently; + action.errorActionFlags |= ErrorActionFlags.MoveAllAlternatesMatchingHDCP; + } + else if (!isOkToRetry) { + action.errorAction = NetworkErrorAction.RemoveAlternatePermanently; + } + else { + action = _getActionForCommonNetworkError(error.code); + } + } + // Don't force host switch for key errors + action.errorActionFlags &= ~ErrorActionFlags.MoveAllAlternatesMatchingHost; + const enabledOptionId = rootQuery.enabledMediaOptionIdByType(mediaOptionType); + if (mediaOptionId === enabledOptionId) { + return modifyErrorActionIfCurrentLevelIsLastValidLevel(action, false, error.code, enabledOptionId, mediaOptionType, rootQuery, rootService, error.isTimeout); + } + else { + return action; + } + } + /** + * Common error action handling. use if error can't be handled automatically by handleErrorCommon. + * Note that this will modify the store multiple times so it should be wrapped in a transaction + * + * @param action Error action to handle + * @param rootQuery The root playlist query + * @param rootService The root playlist service + * @param mediaOptionType The media option type that the error originates from. Required for penalty box + * @param mediaOptionId The media option id that the error originates from. Required for penalty box + * @returns true if error was handled (we did something in reaction to it) + */ + function handleErrorAction(action, error, rootQuery, rootService, mediaOptionType, mediaOptionId, isTimeOut) { + const { errorAction, errorActionFlags } = action; + let errorHandled = true; + switch (errorAction) { + case NetworkErrorAction.RemoveAlternatePermanently: + case NetworkErrorAction.SendAlternateToPenaltyBox: { + if (mediaOptionType == null || mediaOptionId == null) { + error.handled = false; + return false; + } + const itemId = rootQuery.itemId; + let failedMediaOptionId = mediaOptionId; + let failedMediaOption = rootQuery.mediaOptionListQueries[mediaOptionType].mediaOptionFromId(mediaOptionId); + if (failedMediaOption.backingMediaOptionId) { + failedMediaOptionId = failedMediaOption.backingMediaOptionId; + failedMediaOption = rootQuery.mediaOptionListQueries[mediaOptionType].mediaOptionFromId(failedMediaOptionId); + } + const shouldSwitchHosts = (errorActionFlags & ErrorActionFlags.MoveAllAlternatesMatchingHost) != 0; + const shouldMoveMatchingHDCP = (errorActionFlags & ErrorActionFlags.MoveAllAlternatesMatchingHDCP) != 0; + const shouldSwitchToSDR = (errorActionFlags & ErrorActionFlags.SwitchToSDR) != 0; + const shouldRemove = errorAction === NetworkErrorAction.RemoveAlternatePermanently; + rootService.logger.debug(`[handleErrorAction] mediaOptionId ${mediaOptionId} shouldSwitchHosts ${shouldSwitchHosts} shouldMoveMatchingHDCP ${shouldMoveMatchingHDCP} shouldSwitchToSDR ${shouldSwitchToSDR} shouldRemove ${shouldRemove} fatal ${error.fatal}`); + if (shouldMoveMatchingHDCP && 'hdcpLevel' in failedMediaOption) { + const maxHdcpLevel = failedMediaOption.hdcpLevel; + rootService.setMaxHdcpLevel(itemId, maxHdcpLevel); + } + if (shouldSwitchToSDR) { + rootService.switchToSDROnly(itemId); + } + if (shouldSwitchHosts) { + const hostName = getHostName(failedMediaOption.url); + rootService.moveAllWithMatchingHosts(itemId, mediaOptionType, hostName, shouldRemove); + } + else { + if (shouldRemove) { + rootService.removePermanently(itemId, mediaOptionType, failedMediaOptionId); + } + else { + rootService.addToPenaltyBox(itemId, mediaOptionType, failedMediaOptionId); + } + } + // Switch enabled option only if we're the enabled option + const currentMediaOptionId = rootQuery.enabledMediaOptionIdByType(mediaOptionType); + if (currentMediaOptionId === mediaOptionId) { + let newOptions = [NoMediaOption, NoMediaOption, NoMediaOption]; + newOptions = rootService.getFallbackMediaOptionTupleFromMediaOptionId(rootQuery, mediaOptionType, mediaOptionId, null, false, shouldSwitchHosts, isTimeOut); + if (rootQuery.isValidMediaOptionTuple(newOptions)) { + rootService.setPreferredHost(itemId, getHostName(newOptions[MediaOptionType.Variant].url)); + } + else { + error.fatal = true; // Couldn't find matching variant + } + rootService.logger.debug(`[handleErrorAction] ${error.fatal ? 'rejected' : 'picked'} ${JSON.stringify(newOptions)}`); + if (error.fatal) { + newOptions = [NoMediaOption, NoMediaOption, NoMediaOption]; + } + rootService.setNextMediaOptions(rootQuery.itemId, newOptions); + } + break; + } + case NetworkErrorAction.SendEndCallback: + error.fatal = true; + break; + case NetworkErrorAction.RetryRequest: // Retry should be handled outside of this using retryWhen + case NetworkErrorAction.DoNothing: + default: + errorHandled = false; + break; + } + error.handled = errorHandled; + return errorHandled; + } + function logError(logger, error, action, retryCount, nextRetry) { + var _a, _b; + let requestID; + if (error instanceof ManifestNetworkError) { + requestID = 'manifest'; + } + else if (error instanceof PlaylistNetworkError || error instanceof FragmentNetworkError) { + requestID = `${MediaOptionNames[error.mediaOptionType]}:${error.mediaOptionId}`; + } + else if (error instanceof KeyRequestError || error instanceof KeyRequestTimeoutError) { + requestID = `key:${redactUrl(error.keyuri)}`; + } + const data = { + requestID, + type: error.type, + details: error.details, + fatal: error.fatal, + code: (_b = (_a = error.response) === null || _a === void 0 ? void 0 : _a.code) !== null && _b !== void 0 ? _b : NaN, + errorAction: action === null || action === void 0 ? void 0 : action.errorAction, + errorActionFlags: action === null || action === void 0 ? void 0 : action.errorActionFlags, + retryCount, + nextRetry, + }; + logger.qe({ + critical: true, + name: 'internalError', + data, + }); + } + + /** + * Common helpers / operators for handling errors + */ + function handleRetryAction(error, retryCount, retryConfig, logger) { + error.handled = true; + if (retryConfig && retryCount < retryConfig.maxNumRetry && isFiniteNumber(retryConfig.retryDelayMs)) { + let timeoutMs; + switch (retryConfig.backoff) { + case 'linear': + timeoutMs = (retryCount + 1) * retryConfig.retryDelayMs; + break; + default: + // exponential + timeoutMs = Math.pow(2, retryCount) * retryConfig.retryDelayMs; + break; + } + timeoutMs = Math.min(retryConfig.maxRetryDelayMs, timeoutMs); + logError(logger, error, { errorAction: NetworkErrorAction.RetryRequest, errorActionFlags: 0 }, retryCount, timeoutMs); + return timer(timeoutMs); + } + error.fatal = true; + logError(logger, error, { errorAction: NetworkErrorAction.SendEndCallback, errorActionFlags: 0 }, retryCount); + return throwError(error); + } + /** + * For use in retryWhen. This is a generic helper just in case we don't want to go + * through getActionAndHandleError. + * + * @returns timer() if we should retry, else throws if we should stop / abort the request + */ + function handleErrorWithRetry(error, retryCount, retryConfig, action, rootQuery, rootService, mediaOptionType, mediaOptionId, isTimeOut = false) { + if ((action === null || action === void 0 ? void 0 : action.errorAction) === NetworkErrorAction.RetryRequest) { + return handleRetryAction(error, retryCount, retryConfig, rootService.logger); + } + return handleErrorActionCommon(error, retryCount, action, rootQuery, rootService, mediaOptionType, mediaOptionId, isTimeOut); + } + function handleErrorActionCommon(error, retryCount, action, rootQuery, rootService, mediaOptionType, mediaOptionId, isTimeOut = false) { + const async$ = new AsyncSubject(); + // Wrap handleErrorAction in transaction because it will update the store a few times + applyTransaction(() => { + if (action) { + logError(rootService.logger, error, action, retryCount); + handleErrorAction(action, error, rootQuery, rootService, mediaOptionType, mediaOptionId, isTimeOut); + } + async$.error(error); + }); + return async$; + } + + /** + * Handle errors coming from demuxers + */ + /** + * An operator for handling demux errors + */ + function addDemuxErrorHandlingPolicy(rootService, rootQuery, mediaOptionType, mediaOptionId) { + return (source$) => source$.pipe(catchError((error) => { + rootService.logger.error(`Got demux error ${error.message}`); + if (error instanceof FragParsingError || error instanceof RemuxAllocError) { + let errorAction = NetworkErrorAction.SendAlternateToPenaltyBox; + if (error.fatal) { + errorAction = NetworkErrorAction.SendEndCallback; + } + else if (error instanceof RemuxAllocError) { + errorAction = NetworkErrorAction.SendAlternateToPenaltyBox; + } + else { + errorAction = NetworkErrorAction.RemoveAlternatePermanently; + } + const action = { + errorAction, + errorActionFlags: 0, + }; + // No retry allowed + return handleErrorActionCommon(error, 0, action, rootQuery, rootService, mediaOptionType, mediaOptionId); + } + throw error; + })); + } + + /** + * Handle errors that occur while fetching keys or from keysystem + */ + function updateKeyTimeouts(keyUri, increment, rootQuery, rootService, ksQuery) { + var _a, _b; + const mediaOptionIds = (_b = (_a = ksQuery.getKeyInfo(keyUri)) === null || _a === void 0 ? void 0 : _a.mediaOptionIds) !== null && _b !== void 0 ? _b : []; + for (const mediaOptionId of mediaOptionIds) { + for (const query of rootQuery.mediaOptionListQueries) { + if (query.mediaOptionFromId(mediaOptionId) != null) { + rootService.updateConsecutiveTimeouts(rootQuery.itemId, query.mediaOptionType, increment, 'key'); + } + } + } + } + function addKeyErrorHandlingPolicy(keyUri, loadConfig, rootQuery, rootService, ksQuery) { + return (source) => source.pipe(withTransaction(() => { + updateKeyTimeouts(keyUri, false, rootQuery, rootService, ksQuery); + }), retryWhen((errors) => errors.pipe(mergeMap((err, retryCount) => { + if (err instanceof KeyRequestTimeoutError || err instanceof KeyRequestError) { + return handleKeyError(err, retryCount, getRetryConfig(err, loadConfig), rootService, rootQuery, ksQuery); + } + throw err; + })))); + } + function handleKeyError(error, retryCount, retryConfig, rootService, rootQuery, ksQuery) { + const logger = rootService.logger; + const enabledOptions = rootQuery.enabledMediaOptionKeys; + const mediaOptions = []; + for (const mediaOptionId of error.mediaOptionIds) { + const isEnabled = enabledOptions.some((key) => key.mediaOptionId === mediaOptionId); + const query = rootQuery.mediaOptionListQueries.find((query) => query.mediaOptionFromId(mediaOptionId) != null); + if (!query) { + logger.warn(`Couldn't find query for ${mediaOptionId}`); + continue; + } + const mediaOptionType = query.mediaOptionType; + const obj = { mediaOptionId, mediaOptionType }; + if (isEnabled) { + mediaOptions.push(obj); // Handle after all other options because of fallback handling + } + else { + mediaOptions.unshift(obj); + } + } + const async$ = new AsyncSubject(); + applyTransaction(() => { + const isTimeout = error instanceof KeyRequestTimeoutError; + updateKeyTimeouts(error.keyuri, isTimeout, rootQuery, rootService, ksQuery); + let shouldRetry = false; + let action; + for (const { mediaOptionId, mediaOptionType } of mediaOptions) { + action = getActionForCryptKeyNetworkError(error, mediaOptionId, mediaOptionType, rootQuery, rootService); + logger.error(`[Keys] handleNetworkError uri=${redactUrl(error.keyuri)} mediaOptionId=${mediaOptionId} mediaOptionType=${mediaOptionType} action=${JSON.stringify(action)}`); + if (action.errorAction === NetworkErrorAction.RetryRequest) { + // This means there's no fallback! + shouldRetry = true; + } + handleErrorAction(action, error, rootQuery, rootService, mediaOptionType, mediaOptionId); + } + if (shouldRetry) { + async$.next(); + async$.complete(); + } + else { + logError(logger, error, action, retryCount); // Log final action + async$.error(error); + } + }); + return async$.pipe(switchMap(() => { + return handleRetryAction(error, retryCount, retryConfig, rootService.logger); + })); + } + + /** + * Handle root playlist, media playlist and fragment load errors + */ + /** + * An operator for handling Playlist and Fragment load errors. It will either retry or abort the observable by throwing error + * Additional error handling may happen by putting something into penalty box + */ + function addLoadErrorHandlingPolicy(itemId, mediaOptionType, loadConfig, maxTimeouts, isPrefetch, rootQuery, rootPlaylistService, statsService) { + maxTimeouts = Math.max(0, maxTimeouts); + return (source) => source.pipe( + // Expected that the source will throw appropriate HlsError. + tap(() => { + if (mediaOptionType != null) { + rootPlaylistService.updateConsecutiveTimeouts(itemId, mediaOptionType, false, 'load'); + } + }), retryWhen((errors) => errors.pipe(mergeMap((error, retryCount) => { + return getActionAndHandleError(error, retryCount, getRetryConfig(error, loadConfig), isPrefetch, maxTimeouts, rootQuery, rootPlaylistService, statsService); + })))); + } + /** + * Meant to be used within retryWhen for an Observable + * @param retryCount How many times we have retried this observable. 0 means this was the original request + * @param retryConfig Retry config to use + * @param isPrefetch true if this is a prefetch (not needed for playback) + * @param maxTimeouts Maximum number of consecutive timeouts allowed before failing + * @param rootQuery The root playlist query + * @param rootService The root playlist service + * @returns Retry timer if retrying, else throws error + */ + function getActionAndHandleError(error, retryCount, retryConfig, isPrefetch, maxTimeouts, rootQuery, rootService, statsService) { + var _a; + if (!(error instanceof HlsError)) { + return throwError(error); + } + let mediaOptionId; + let mediaOptionType; + let action; + let isTimeout = false; + if (error instanceof ManifestNetworkError) { + action = getActionForManifestError(error); + } + else if (error instanceof PlaylistNetworkError || error instanceof FragmentNetworkError) { + ({ mediaOptionType, mediaOptionId, isTimeout } = error); + const mediaOption = (_a = rootQuery.mediaOptionListQueries[mediaOptionType]) === null || _a === void 0 ? void 0 : _a.mediaOptionFromId(mediaOptionId); + if (!isPrefetch && error.isTimeout && mediaOption != null && !('iframes' in mediaOption && mediaOption.iframes === true)) { + rootService.updateConsecutiveTimeouts(rootQuery.itemId, error.mediaOptionType, true, 'load'); + // record only for fragments + if (error instanceof FragmentNetworkError && error.stats) { + const now = performance.now(); + statsService.setBandwidthSample(Object.assign(Object.assign({}, error.stats), { tfirst: error.stats.tfirst || now, tload: error.stats.tload || now, complete: true, mediaOptionType: mediaOptionType })); + } + } + action = getActionForPlaylistOrFragError(error, isPrefetch, maxTimeouts, rootQuery, rootService); + } + // TODO: add more error types + return handleErrorWithRetry(error, retryCount, retryConfig, action, rootQuery, rootService, mediaOptionType, mediaOptionId, isTimeout); + } + + class HlsQuery extends QueryEntity { + constructor(store) { + super(store); + } + get currentConfig() { + var _a; + return (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.config; + } + get extendMaxTTFB() { + var _a; + return (_a = this.getActive()) === null || _a === void 0 ? void 0 : _a.extendMaxTTFB; + } + get config$() { + return this.selectActive((entity) => entity === null || entity === void 0 ? void 0 : entity.config); + } + get userSeek$() { + return this.selectActive((entity) => entity === null || entity === void 0 ? void 0 : entity.userSeek); + } + } + + // rdar://84941644 ([HLS JS 2.1a beta] [DoW] The plugin for 'MapSet' has not been loaded into Immer) + // hls.js uses Set and/or Map in highestVideoCodecs and various entities. + // dev-app occasionally throws "The plugin for 'MapSet' has not been loaded into Immer." error during error handling (e.g., switchToSDROnly). + // need to call enableMapSet() after and including immer 6. + // reference: https://immerjs.github.io/immer/map-set/ + enableMapSet_1(); + /** + * @brief Store for keeping track of things that are associated with Hls lifetime + */ + class HlsStore extends EntityStore { + constructor() { + super({}, { name: 'hls-store', producerFn: produce_1 }); + } + } + + /** + * @brief Service for HlsStore + */ + class HlsService { + constructor(store) { + this.store = store; + } + getQuery() { + return new HlsQuery(this.store); + } + /** + * Add and set active HlsEntity + */ + setHlsEntity(entity) { + const id = entity.id; + logAction(`hls.set.entity ${id}`); + applyTransaction(() => { + this.store.add(deepCpy(entity)); + this.store.setActive(id); + }); + } + removeEntity(id) { + logAction(`hls.remove ${id}`); + this.store.remove(id); + } + // legacy hack to get airplay to work + setStartTime(startTimeSec) { + this.store.updateActive((active) => { + active.config.startPosition = startTimeSec; + }); + } + // If we got a user seek + setUserSeek(position) { + this.store.updateActive((active) => { + active.userSeek = position; + }); + } + setExtendMaxTTFB(value) { + this.store.updateActive((active) => { + active.extendMaxTTFB = value; + }); + } + } + let service; + function globalHlsService() { + if (!service) { + service = new HlsService(new HlsStore()); + } + return service; + } + function createHlsQuery() { + return globalHlsService().getQuery(); + } + /** + * Config for active hls entity + */ + function getCurrentConfig() { + return globalHlsService().getQuery().currentConfig; + } + + const detailFields = ['mediaOptionId', 'startSN', 'endSN', 'ptsKnown']; + /** + * Used for live, merge the two playlists together + * @param oldDetails + * @param newDetails Newly parsed variant info from PlaylistParser. Assumption is that fragments[0].start === 0 + */ + function mergeDetails(oldDetails, newDetails, logger) { + var _a; + // no merging required in VOD or if not matching in iframe mode + if (oldDetails.type === 'VOD' || newDetails.type === 'VOD' || oldDetails.iframesOnly !== newDetails.iframesOnly) { + return; + } + logger.info(`[live] merging ${JSON.stringify(oldDetails, detailFields)}+${JSON.stringify(newDetails, detailFields)}`); + const matchingMediaOption = oldDetails.mediaOptionId === newDetails.mediaOptionId; + const startIdx = Math.max(oldDetails.startSN, newDetails.startSN) - newDetails.startSN; // start of overlap inside newfragments + const endIdx = Math.min(oldDetails.endSN, newDetails.endSN) - newDetails.startSN; // end of overlap inside newfragments + const idxDelta = newDetails.startSN - oldDetails.startSN; + const oldfragments = oldDetails.fragments; + const newfragments = newDetails.fragments; + let discoSeqNumOffset = 0; // oldCC - newCC + for (let i = startIdx; i <= endIdx; ++i) { + // Expect this to only be O(1) usually... + if (oldfragments[idxDelta + i] && newfragments[i]) { + discoSeqNumOffset = oldfragments[idxDelta + i].discoSeqNum - newfragments[i].discoSeqNum; + // non zero value means the content is in violation of authoring guidelines + logger.debug(`[live] merging details for mediaOptionId: ${newDetails.mediaOptionId} using discoSeqNumOffset: ${discoSeqNumOffset}`); + break; + } + } + // loop through overlapping SN and update startPTS , cc, and duration if any found + const mergedInitSegments = {}; + let lastPtsFrag = null; + for (let i = 0; i < newfragments.length; i++) { + // logger.info(`[live] merging details for media sequence number: ${newfragments[i].mediaSeqNum}`); + const oldFrag = oldfragments[idxDelta + i]; + const newFrag = newfragments[i]; + if (discoSeqNumOffset) { + // This block is to handle publisher side issue, where the + // authoring guideline below is not handled: 8.17. If + // live/linear content will ever contain an EXT-X-DISCONTINUITY + // tag, the EXT-X-DISCONTINUITY-SEQUENCE tag MUST always be + // present. + const discoSeqNum = newFrag.discoSeqNum + discoSeqNumOffset; + if (newDetails.initSegments[newFrag.discoSeqNum]) { + // logger.info(`[live] merging details offseting discoSeqNum from ${newFrag.discoSeqNum} to ${discoSeqNum}`); + newDetails.initSegments[newFrag.discoSeqNum].discoSeqNum = discoSeqNum; // update the frag details + mergedInitSegments[discoSeqNum] = newDetails.initSegments[newFrag.discoSeqNum]; // update the record + delete newDetails.initSegments[newFrag.discoSeqNum]; // delete from the old record. + } + newFrag.discoSeqNum = discoSeqNum; + } + if (matchingMediaOption && newFrag.mediaSeqNum === (oldFrag === null || oldFrag === void 0 ? void 0 : oldFrag.mediaSeqNum) && oldFrag.startPts != null) { + // logger.info(`[live] merging timestamps for media sequence number: ${newFrag.mediaSeqNum} using start PTS: ${oldFrag.startPTS}`); + newFrag.start = oldFrag.start; + newFrag.duration = oldFrag.duration; + newFrag.startDtsTs = oldFrag.startDtsTs; + newFrag.endDtsTs = oldFrag.endDtsTs; + newFrag.startPts = oldFrag.startPts; + newFrag.endPts = oldFrag.endPts; + lastPtsFrag = newFrag; + } + } + if (Object.keys(mergedInitSegments).length) { + newDetails.initSegments = mergedInitSegments; + } + if (lastPtsFrag) { + // Force update entire list to ensure correct sliding window + updateFragPTSDTS(newDetails, lastPtsFrag, lastPtsFrag.start, undefined, false, true); + } + else { + // ensure that delta is within oldfragments range + // also adjust sliding in case delta is 0 (we could have old=[50-60] and new=old=[50-61]) + // in that case we also need to adjust start offset of all fragments + if (idxDelta >= 0 && idxDelta < oldfragments.length) { + // adjust start by sliding offset + const sliding = oldfragments[idxDelta].start; + logger.info(`[live] merging details using sliding ${sliding}`); + const mergedFragments = newDetails.fragments; + for (let i = 0; i < newfragments.length; i++) { + mergedFragments[i].start += sliding; + } + } + } + // If overlapping use oldDetails.PTSKnown + newDetails.ptsKnown = newDetails.ptsKnown || (matchingMediaOption && oldDetails.ptsKnown === true && oldDetails.endSN >= newDetails.startSN); + updateDateToMediaTimeMap(newDetails); + const startPos = (_a = newDetails.fragments[0]) === null || _a === void 0 ? void 0 : _a.start; + const endPos = startPos + newDetails.totalduration; + logger.info(`[live] merged ${JSON.stringify(oldDetails, detailFields)}+${JSON.stringify(newDetails, detailFields)}=[${startPos === null || startPos === void 0 ? void 0 : startPos.toFixed(3)},${endPos === null || endPos === void 0 ? void 0 : endPos.toFixed(3)}]`); + } + + function computeLivePosition(sliding, mediaOptionDetails, config, logger) { + let targetLatency = mediaOptionDetails.targetduration; + if (isFiniteNumber(config.liveSyncDuration)) { + targetLatency = config.liveSyncDuration; + } + else if (isFiniteNumber(config.liveSyncDurationCount)) { + targetLatency = config.liveSyncDurationCount * mediaOptionDetails.targetduration; + } + const result = sliding + Math.max(0, mediaOptionDetails.totalduration - targetLatency); + logger.info(`[live] computeLivePosition: ${toFixed(result, 3)}`); + return result; + } + function sanitizeLiveSeek(seekTo, details, config, logger) { + let santizedSeek = seekTo; + const liveWindowStart = details.fragments[0].start; + const liveWindowEnd = details.fragments[details.fragments.length - 1].start + details.fragments[details.fragments.length - 1].duration; + if (seekTo < liveWindowStart) { + santizedSeek = liveWindowStart; + } + else if (seekTo > liveWindowEnd) { + santizedSeek = computeLivePosition(0, details, config, logger); + } + if (seekTo < liveWindowStart || seekTo > liveWindowEnd) { + logger.warn(`[live] sanitizeLiveSeek seekTo:${toFixed(seekTo, 3)}, sanitizedSeek:${toFixed(santizedSeek, 3)}, liveWindowStart:${toFixed(liveWindowStart, 3)}, liveWindowEnd:${toFixed(liveWindowEnd, 3)}`); + } + return santizedSeek; + } + function getMinPlayablePosition(pos, details, lastUpdateMillis, maxBufferHole, mediaQuery) { + if (!details.ptsKnown) { + return 0; + } + const targetDuration = details.targetduration; + const liveWindowStart = details.fragments[0].start; + const playlistEstimate = { avgPlaylistLoadTimeMs: 0, avgPlaylistParseTimeMs: 0 }; + const canPlayThrough = mediaQuery.canContinuePlaybackWithoutGap(details, lastUpdateMillis, playlistEstimate, maxBufferHole); + let minPosition = Math.max(0, pos - targetDuration); + if (pos < liveWindowStart && !canPlayThrough) { + minPosition = liveWindowStart; + } + return minPosition; + } + /** + * @returns An observable that will ensure playback stays within the live window on playlist refresh + * emits the seek position + */ + function ensurePlaybackWithinWindow(context) { + const { config, mediaSink, rootPlaylistQuery, mediaLibraryService } = context; + const logger = context.logger.child({ name: 'live' }); + const mediaQuery = mediaSink.mediaQuery; + return rootPlaylistQuery.enabledMediaOptionByType$(MediaOptionType.Variant).pipe(filter(isEnabledMediaOption), switchMap((option) => { + const libQuery = mediaLibraryService.getQueryForOption(option); + return libQuery.mediaOptionDetailsEntity$.pipe(filter((entity) => { var _a; return ((_a = entity === null || entity === void 0 ? void 0 : entity.mediaOptionDetails) === null || _a === void 0 ? void 0 : _a.ptsKnown) && entity.mediaOptionDetails.liveOrEvent; }), distinctUntilChanged((a, b) => (a === null || a === void 0 ? void 0 : a.lastUpdateMillis) === (b === null || b === void 0 ? void 0 : b.lastUpdateMillis))); + }), map((entity) => { + const details = entity.mediaOptionDetails; + const pos = mediaQuery.currentTime; + const duration = mediaQuery.msDuration; + if (duration < entity.playlistDuration) { + logger.info(`msDuration < playlistDuration, updating ${duration}->${entity.playlistDuration}`); + mediaSink.msDuration = entity.playlistDuration; + } + else if (isFiniteNumber(mediaSink.msDuration)) { + // audio could have been behind video and stagnated, and missed out on last opportunity + // and video playlist could stagnate subsequently + // doing this will trigger the needData, giving a chance to survive low buffer stall + mediaSink.msDuration = mediaSink.msDuration + config.livePlaylistDurationNudge; + } + const minPosition = getMinPlayablePosition(pos, details, entity.lastUpdateMillis, config.maxBufferHole, mediaQuery); + let seekTo = NaN; + if (pos < minPosition) { + seekTo = computeLivePosition(details.fragments[0].start, details, config, logger); + logger.info(`${pos.toFixed(3)} too far behind window start:${minPosition} seek to live=${seekTo}`); + mediaSink.seekTo = seekTo; + } + return seekTo; + })); + } + + /* + * deals with live/event refresh related aspects + * + * + * + */ + // in milliseconds + function getLiveRefreshInterval(mediaOptionDetails) { + return 1000 * (mediaOptionDetails.averagetargetduration ? mediaOptionDetails.averagetargetduration : mediaOptionDetails.targetduration); + } + function needToRefreshLevel(mediaOptionDetails, lastUpdateMillis) { + const refreshInterval = getLiveRefreshInterval(mediaOptionDetails); + const timeSinceLastLevelRequest = performance.now() - lastUpdateMillis; + return mediaOptionDetails.liveOrEvent && timeSinceLastLevelRequest >= refreshInterval; + } + /** + * + * @param curDetails + * @param newDetails + * @param logger + * @returns Whether there has been a change between curDetails && newDetails + */ + function mediaOptionDetailsHasChanged(curDetails, newDetails) { + return curDetails == null || newDetails.endSN !== curDetails.endSN || newDetails.liveOrEvent !== curDetails.liveOrEvent; + } + function getReloadTimer(mediaOptionDetailsEntity, slowDownTimeInMillis, logger) { + // In gapless mode it is possible the mediaOptionDetailsEntity is null right after an item eviction + if (!mediaOptionDetailsEntity) { + logger.info('mediaOptionDetailsEntity is null'); + return EMPTY; + } + const { mediaOptionDetails, lastUpdateMillis, unchangedCount } = mediaOptionDetailsEntity; + if (!(mediaOptionDetails === null || mediaOptionDetails === void 0 ? void 0 : mediaOptionDetails.liveOrEvent)) { + logger.info(`End of event refresh for mediaOptionId: ${mediaOptionDetails.mediaOptionId}`); + return EMPTY; + } + if (needToRefreshLevel(mediaOptionDetails, lastUpdateMillis)) { + return timer(0).pipe(tap(() => logger.info(`[live] immediate live refresh for mediaOptionId: ${mediaOptionDetails.mediaOptionId}`))); + } + let reloadInterval = getLiveRefreshInterval(mediaOptionDetails); + if (unchangedCount > 0) { + reloadInterval /= 2; + reloadInterval = Math.max(reloadInterval, 5000); // Have a minumum 5 seconds gap + } + // decrement reloadInterval with level loading delay + const now = performance.now(); + const sinceLastUpdate = now - lastUpdateMillis; + reloadInterval -= sinceLastUpdate; + reloadInterval += slowDownTimeInMillis; + // in any case, don't reload more than every second + reloadInterval = Math.max(1000, Math.round(reloadInterval)); + return timer(reloadInterval).pipe(tap(() => logger.info(`[live] live refresh after ${reloadInterval} for mediaOptionId: ${mediaOptionDetails.mediaOptionId}`))); + } + /** + * @returns whether the live playlist is too far in the past. This means the sliding window end is too + * far behind the minimum playback position + */ + function livePlaylistExpired(details, lastUpdateMillis, maxBufferHole, mediaQuery) { + const minPosition = getMinPlayablePosition(mediaQuery.currentTime, details, lastUpdateMillis, maxBufferHole, mediaQuery); + const lastFrag = details.fragments[details.fragments.length - 1]; + const windowEnd = (lastFrag === null || lastFrag === void 0 ? void 0 : lastFrag.start) + (lastFrag === null || lastFrag === void 0 ? void 0 : lastFrag.duration); + const expired = lastFrag != null && details.liveOrEvent && details.ptsKnown && windowEnd < minPosition; + return { expired, windowEnd, minPosition }; + } + + function findFragForCriteria(details, fn, startSN = NaN) { + const fragList = details.fragments; + const startIdx = startSN > details.startSN ? startSN - details.startSN : 0; + for (let idx = startIdx; idx < fragList.length; ++idx) { + const mediaFragment = fragList[idx]; + const { start: timelineOffset } = mediaFragment; + if (fn(mediaFragment)) { + return { timelineOffset, mediaFragment }; + } + } + return null; + } + function discoSeqNumForTime(details, position) { + var _a; + const foundFrag = (_a = findFragForCriteria(details, (f) => { + const positiveDuration = f.duration > 0; + const fragEnd = f.start + f.duration; + const validPosition = fragEnd > position || (position - fragEnd < 1 && f.isLastFragment); + return positiveDuration && validPosition; + })) !== null && _a !== void 0 ? _a : null; + return foundFrag === null || foundFrag === void 0 ? void 0 : foundFrag.mediaFragment.discoSeqNum; + } + function validUnbufferedFragment(bufferedSeg, position, f) { + const positiveDuration = f.duration > 0; + const fragEnd = f.start + f.duration; + // edge case : position is after last segment, but its very close, use the last fragment. + const validPosition = position == null || fragEnd > position || (position - fragEnd < 1 && f.isLastFragment); + const fragNotAlreadyBuffered = bufferedSeg.every((seg) => !fragEqual(seg.frag, f)); + return positiveDuration && fragNotAlreadyBuffered && validPosition; + } + function findFragment(position, activeDiscoSeqNum, anchorMSN, details, bufferedSeg) { + var _a, _b, _c; + const firstUnbufferedFrag = (_a = findFragForCriteria(details, validUnbufferedFragment.bind(null, bufferedSeg, undefined), anchorMSN)) !== null && _a !== void 0 ? _a : null; + let foundFrag = null; + if (firstUnbufferedFrag) { + // Try to find fragment with valid position using previous result as hint + foundFrag = (_b = findFragForCriteria(details, validUnbufferedFragment.bind(null, bufferedSeg, position), firstUnbufferedFrag.mediaFragment.mediaSeqNum)) !== null && _b !== void 0 ? _b : null; + if (!foundFrag && details.liveOrEvent && !details.ptsKnown) { + // this is fall back for fairly rare case + foundFrag = firstUnbufferedFrag; + } + } + let nextDisco = NaN; + if (foundFrag != null && isFiniteNumber(activeDiscoSeqNum) && foundFrag.mediaFragment.discoSeqNum !== activeDiscoSeqNum) { + nextDisco = foundFrag.mediaFragment.discoSeqNum; + foundFrag = null; + } + // When switching variants and segments have the potential to drop frames, return mediaSeqNum -1 + // to retieve the start of the GOP needed to buffer the start of the segment time range. + const canDropFrames = bufferedSeg.some((seg) => seg.frag.framesWithoutIDR > 0); + if (foundFrag && canDropFrames) { + const levelSwitch = bufferedSeg[bufferedSeg.length - 1].frag.mediaOptionId !== details.mediaOptionId; + if (levelSwitch) { + foundFrag = (_c = findFragForCriteria(details, (f) => f.mediaSeqNum === foundFrag.mediaFragment.mediaSeqNum - 1)) !== null && _c !== void 0 ? _c : foundFrag; + } + } + return { foundFrag, nextDisco }; + } + function findIframeFragmentForPosition(position, details, audioDetails, rate, iframeMachine) { + const nextFragResult = iframeMachine.nextFragment(details.fragments, (audioDetails === null || audioDetails === void 0 ? void 0 : audioDetails.fragments) || [], rate, position); + if (!nextFragResult) { + return null; + } + const { frag, newMediaRootTime } = nextFragResult; + const foundFrag = { timelineOffset: frag.iframeMediaStart, mediaFragment: frag }; + return { foundFrag, nextDisco: NaN, newMediaRootTime }; + } + function calculatePlaylistEnd(details) { + const fragEnd = details.fragments[details.fragments.length - 1]; + return fragEnd ? fragEnd.start + fragEnd.duration : 0; + } + + /** + * @brief Query interface to the media library. Holds the details and init segments + */ + class MediaLibraryQuery extends QueryEntity { + constructor(mediaLibraryStore, mediaOption) { + super(mediaLibraryStore); + this.mediaOption = mediaOption; + } + get itemId() { + return this.mediaOption.itemId; + } + get mediaOptionId() { + return this.mediaOption.mediaOptionId; + } + get initSegmentEntities() { + var _a; + return (_a = this.mediaOptionDetailsEntity) === null || _a === void 0 ? void 0 : _a.initSegmentCacheEntities; + } + get mediaLibraryEntity() { + return this.getEntity(this.itemId); + } + get mediaOptionDetailsEntityRecord() { + var _a; + return (_a = this.mediaLibraryEntity) === null || _a === void 0 ? void 0 : _a.mediaOptionDetailsEntityRecord; + } + get mediaOptionDetailsEntity() { + if (!this.mediaOptionDetailsEntityRecord) + return null; + return this.mediaOptionDetailsEntityRecord[this.mediaOptionId]; + } + get mediaOptionDetails() { + var _a; + return (_a = this.mediaOptionDetailsEntity) === null || _a === void 0 ? void 0 : _a.mediaOptionDetails; + } + get playlistDuration() { + var _a; + return (_a = this.mediaOptionDetailsEntity) === null || _a === void 0 ? void 0 : _a.playlistDuration; + } + get mediaOptionDetailsEntity$() { + const { itemId, mediaOptionId } = this; + return this.selectEntity(itemId, (libraryEntity) => { + if (libraryEntity === null || libraryEntity === void 0 ? void 0 : libraryEntity.mediaOptionDetailsEntityRecord) { + return libraryEntity === null || libraryEntity === void 0 ? void 0 : libraryEntity.mediaOptionDetailsEntityRecord[mediaOptionId]; + } + }); + } + get mediaOptionDetails$() { + return this.selectEntity(this.itemId, (entity) => { var _a; return (_a = entity === null || entity === void 0 ? void 0 : entity.mediaOptionDetailsEntityRecord[this.mediaOptionId]) === null || _a === void 0 ? void 0 : _a.mediaOptionDetails; }).pipe(filterNullOrUndefined()); + } + get playlistDuration$() { + return this.mediaOptionDetailsEntity$.pipe(map((mediaOptionDetailsEntity) => mediaOptionDetailsEntity === null || mediaOptionDetailsEntity === void 0 ? void 0 : mediaOptionDetailsEntity.playlistDuration), filterNullOrUndefined(), distinctUntilChanged()); + } + get live$() { + return this.mediaOptionDetails$.pipe(map((mediaOptionDetails) => mediaOptionDetails === null || mediaOptionDetails === void 0 ? void 0 : mediaOptionDetails.liveOrEvent), distinctUntilChanged()); + } + } + + class MediaLibraryStore extends EntityStore { + constructor() { + super({}, { name: 'media-library-store', idKey: 'itemId', producerFn: produce_1 }); + } + } + + /** + * @brief Service interface to the media library. Will fetch and catch media options details and init segments, and find + * the right fragments to load. + * + * The retrieve functions are rxjs pipeable operators: + * https://rxjs.dev/guide/v6/pipeable-operators + */ + class MediaLibraryService { + constructor(store) { + this.store = store; + } + getQuery() { + return new QueryEntity(this.store); + } + getQueryForOption(mediaOption) { + return new MediaLibraryQuery(this.store, mediaOption); + } + createMediaLibraryEntity(itemId) { + const libraryEntity = { itemId, mediaOptionDetailsEntityRecord: {} }; + logAction(`library.entity.create: ${itemId}`); + this.store.add(libraryEntity); + } + setDetailsLoading(mediaOption) { + const { itemId, mediaOptionId } = mediaOption; + logAction(`library.details.loading: ${mediaOptionId}`); + this.store.update(itemId, ({ mediaOptionDetailsEntityRecord }) => { + if (!mediaOptionDetailsEntityRecord[mediaOptionId]) { + mediaOptionDetailsEntityRecord[mediaOptionId] = { initSegmentCacheEntities: {}, unchangedCount: 0 }; + } + mediaOptionDetailsEntityRecord[mediaOptionId].detailsLoading = true; + }); + } + archiveMediaOptionDetails(mediaOptionDetails, stats, changed) { + const { itemId, mediaOptionId } = mediaOptionDetails; + const lastUpdateMillis = performance.now(); + const playlistDuration = calculatePlaylistEnd(mediaOptionDetails); + logAction(`library.details.loaded: ${mediaOptionId}`); + this.store.update(itemId, (libraryEntity) => { + const detailsEntity = libraryEntity.mediaOptionDetailsEntityRecord[mediaOptionId]; + detailsEntity.detailsLoading = false; + detailsEntity.mediaOptionDetails = mediaOptionDetails; + detailsEntity.lastUpdateMillis = lastUpdateMillis; + if (changed) { + detailsEntity.unchangedCount = 0; + } + else { + ++detailsEntity.unchangedCount; + } + detailsEntity.playlistDuration = playlistDuration; + detailsEntity.stats = stats; + libraryEntity.liveOrEvent = mediaOptionDetails.liveOrEvent; + }); + } + setInitSegmentLoading(initSegment) { + const { itemId, mediaOptionId, discoSeqNum } = initSegment; + logAction(`library.initsegs.loading: ${mediaOptionId}/${discoSeqNum}`); + this.store.update(itemId, (libraryEntity) => { + libraryEntity.mediaOptionDetailsEntityRecord[mediaOptionId].initSegLoading = discoSeqNum; + }); + } + archiveInitSegmentEntity(original, generated) { + const { itemId, mediaOptionId, discoSeqNum } = original; + logAction(`library.initseg.loaded: ${mediaOptionId}/${discoSeqNum}`); + this.store.update(itemId, ({ mediaOptionDetailsEntityRecord }) => { + const detailsEntity = mediaOptionDetailsEntityRecord[mediaOptionId]; + detailsEntity.initSegmentCacheEntities[discoSeqNum] = [original, generated]; + detailsEntity.initSegLoading = null; + }); + } + updatePTSDTS(itemId, mediaOptionId, initPTSInfo, parsedFrag) { + var _a; + const origDetails = (_a = this.getQueryForOption({ itemId, mediaOptionId })) === null || _a === void 0 ? void 0 : _a.mediaOptionDetails; + if (!origDetails || !fragIsInDetails(origDetails, parsedFrag)) { + return; + } + // immer read/write is slow, modify details cpy outside of store update + const { startDtsTs } = parsedFrag; + const { variantDTS, timelineOffset, iframeMode } = initPTSInfo; + const startOffset = diffSeconds(startDtsTs, variantDTS) + timelineOffset; + const newDetails = Object.assign({}, origDetails); // shallow copy + updateFragPTSDTS(newDetails, parsedFrag, startOffset, iframeMode, true); + updateDateToMediaTimeMap(newDetails); + const newPlaylistDuration = calculatePlaylistEnd(newDetails); + this.store.update(itemId, ({ mediaOptionDetailsEntityRecord }) => { + const entity = mediaOptionDetailsEntityRecord[mediaOptionId]; + if (!(entity === null || entity === void 0 ? void 0 : entity.mediaOptionDetails)) { + return; + } + entity.mediaOptionDetails = newDetails; + entity.playlistDuration = newPlaylistDuration; + }); + } + remove(ids) { + this.store.remove(ids); + } + clear() { + this.store.remove(); + } + } + /** + * Global state store for the service + */ + let libraryService; + function mediaLibraryService() { + if (!libraryService) { + libraryService = new MediaLibraryService(new MediaLibraryStore()); + } + return libraryService; + } + /** + * Creates a new media library query from any media option info (MediaOption, MediaOptionDetails, etc) + * Uses the global store for read only query, but protects the store access + * + * @param {MediaOptionDetails} mediaOptionDetails The details to store + */ + const createMediaLibraryQuery = (mediaOption) => { + return mediaLibraryService().getQueryForOption(mediaOption); + }; + /** + * Upserts a MediaOptionDetails to the store + * + * @param {MediaOptionDetails} mediaOptionDetails The details to store + */ + const archiveMediaOptionDetails = (mediaOptionDetails, stats, changed) => { + mediaLibraryService().archiveMediaOptionDetails(mediaOptionDetails, stats, changed); + }; + /** + * Retrieve the details (a mediaplaylist.m3u8) for a media option (variant or alternate) + * Uses the global store for read only query, but protects the store access + * + * @param {MediaOption} mediaOption media option to load the details for + * @param {LoadPolicy} loadPolicy the policy determining how to load the details + * @param {number} maxTimeouts Maximum number of consecutive timeouts allowed + * @returns {Observable} retrieved (cached or loaded) details for the source media option + */ + const retrieveMediaOptionDetails = (libContext, mediaOption, isPrefetch = false, force = false) => { + if (mediaOption == null || !isEnabledMediaOption(mediaOption)) { + return of(null); + } + const { itemId } = mediaOption; + const { mediaLibraryService: libraryService } = libContext; + const libraryQuery = libraryService.getQueryForOption(mediaOption); + if (!libraryQuery.hasEntity(itemId)) { + libraryService.createMediaLibraryEntity(itemId); + } + const entity = libraryQuery.mediaOptionDetailsEntity; + const details = libraryQuery.mediaOptionDetails; + if (details != null && !force && (details.type === 'VOD' || (details.liveOrEvent && !needToRefreshLevel(details, entity.lastUpdateMillis)))) { + return of(details).pipe(tag('retrieveMediaOptionDetails.emit.cached')).pipe(take(1)); + } + libraryService.setDetailsLoading(mediaOption); + return getMediaOptionDetailsCommon(libContext, mediaOption, isPrefetch); + }; + /** + * Schedules playlist refresh if needed + */ + function refreshMediaOptionDetails(libContext, mediaOption) { + const { mediaLibraryService, logger } = libContext; + const mediaLibraryQuery = mediaLibraryService.getQueryForOption(mediaOption); + // getReloadTimer returns EMPTY if non-live so it shouldn't emit + return getReloadTimer(mediaLibraryQuery.mediaOptionDetailsEntity, 0, logger).pipe(switchMap(() => retrieveMediaOptionDetails(libContext, mediaOption, false, true)), switchMap(() => refreshMediaOptionDetails(libContext, mediaOption))); + } + // Load media option details with error handling + function getMediaOptionDetailsCommon(libContext, mediaOption, isPrefetch) { + var _a, _b; + const { logger, config, rootPlaylistQuery: rootQuery, rootPlaylistService: rootService, statsService, mediaLibraryService, mediaSink } = libContext; + const mediaQuery = mediaSink.mediaQuery; + const loadPolicy = config.playlistLoadPolicy; + const keySystemPreference = config.keySystemPreference; + const masterVariableList = rootQuery.masterVariableList; + const extendMaxTTFB = (_b = (_a = globalHlsService()) === null || _a === void 0 ? void 0 : _a.getQuery()) === null || _b === void 0 ? void 0 : _b.extendMaxTTFB; + return loadMediaOptionDetails(mediaOption, rootQuery.itemStartOffset, config, loadPolicy, logger, keySystemPreference, statsService, masterVariableList, extendMaxTTFB) + .pipe(map((loadMediaOptionDetailsResult) => { + var _a; + // Handle merging with previous level for live/event + const curLibraryQuery = mediaLibraryService.getQueryForOption(mediaOption); + const thisMediaOptionDetails = curLibraryQuery.mediaOptionDetails; + const { mediaOptionDetails: newMediaOptionDetails } = loadMediaOptionDetailsResult; + const { stats } = loadMediaOptionDetailsResult; + let changed = true; + if (newMediaOptionDetails.liveOrEvent) { + const { mediaOptionType } = newMediaOptionDetails; + changed = mediaOptionDetailsHasChanged(thisMediaOptionDetails, newMediaOptionDetails); + // Try merging with self first, then last loaded + if (thisMediaOptionDetails) { + mergeDetails(thisMediaOptionDetails, newMediaOptionDetails, logger); + } + const lastLoadedOption = rootQuery.lastLoadedMediaOptionByType(mediaOptionType); + const lastMediaOptionDetails = lastLoadedOption ? (_a = mediaLibraryService.getQueryForOption(lastLoadedOption)) === null || _a === void 0 ? void 0 : _a.mediaOptionDetails : null; + if (!newMediaOptionDetails.ptsKnown && lastMediaOptionDetails && lastMediaOptionDetails.mediaOptionId !== (thisMediaOptionDetails === null || thisMediaOptionDetails === void 0 ? void 0 : thisMediaOptionDetails.mediaOptionId)) { + mergeDetails(lastMediaOptionDetails, newMediaOptionDetails, logger); + } + } + if (newMediaOptionDetails) { + applyTransaction(() => { + mediaLibraryService.archiveMediaOptionDetails(newMediaOptionDetails, stats, changed); + rootService.setLastLoadedMediaOptionByType(rootQuery.itemId, mediaOption.mediaOptionType, mediaOption); + }); + } + const hitUnchangedMaxCount = !changed && curLibraryQuery.mediaOptionDetailsEntity.unchangedCount >= config.liveMaxUnchangedPlaylistRefresh; + const isExpiredInfo = livePlaylistExpired(newMediaOptionDetails, stats.tload, config.maxBufferHole, mediaQuery); + if (hitUnchangedMaxCount || isExpiredInfo.expired) { + let response; + if (hitUnchangedMaxCount) { + response = ErrorResponses.LivePlaylistUpdateError; + } + else { + response = { + text: `Live window too far in the past end:${isExpiredInfo.windowEnd.toFixed(3)} minPosition:${isExpiredInfo.minPosition}`, + code: 0, + }; + } + throw new PlaylistNetworkError(false, response.text, response.code, response, false, mediaOption.mediaOptionType, mediaOption.mediaOptionId, mediaOption.url); + } + return newMediaOptionDetails; + }), tag('getMediaOptionDetailsCommon.emit.loaded'), addLoadErrorHandlingPolicy(mediaOption.itemId, mediaOption.mediaOptionType, getLoadConfig(mediaOption, loadPolicy), config.maxNumAddLevelToPenaltyBox, isPrefetch, rootQuery, rootService, statsService)) + .pipe(take(1)); + } + /** + * Retrieve the corresponding init segment (#EXT-X-MAP) for a media fragment + * + * @param {MediaFragment} mediaFragment get init segment for this media fragment + * @returns {Observable} retrieved (cached or loaded) init segment + */ + const retrieveInitSegmentCacheEntity = (libContext, mediaFragment) => { + if (!mediaFragment) + return of(null); + const { logger } = libContext; + const { mediaLibraryService, mediaParser } = libContext; + const mediaLibraryQuery = mediaLibraryService.getQueryForOption(mediaFragment); + const { mediaOption, mediaOptionDetailsEntityRecord, mediaOptionDetails } = mediaLibraryQuery; + const { mediaOptionId } = mediaOption; + if (!(mediaOptionDetailsEntityRecord === null || mediaOptionDetailsEntityRecord === void 0 ? void 0 : mediaOptionDetailsEntityRecord[mediaOptionId])) + throw new Error('retrieveInitSegmentCacheEntity no details entity'); + if (!mediaOptionDetails) + throw new Error('retrieveInitSegmentCacheEntity no details'); + const { initSegmentCacheEntities } = mediaOptionDetailsEntityRecord[mediaOptionId]; + const { initSegments } = mediaOptionDetails; + const { mediaSeqNum, discoSeqNum } = mediaFragment; + if (initSegmentCacheEntities[discoSeqNum]) { + logger.debug({ mediaOptionId, mediaSeqNum, discoSeqNum }, 'found cached init segment'); + const [original, generated] = initSegmentCacheEntities[discoSeqNum]; + let entity = original; + if (generated) { + const trackSwitch = mediaParser.willBeTrackSwitch(mediaFragment); + entity = trackSwitch ? original : generated; + } + return of(entity); + } + const initFrag = initSegments[discoSeqNum]; + if (!initFrag) { + logger.debug({ mediaOptionId, mediaSeqNum, discoSeqNum }, 'no init segment entry'); + return of(null); + } + mediaLibraryService.setInitSegmentLoading(initFrag); + logger.info({ mediaSeqNum, discoSeqNum }, 'loading init segment'); + const timingObj = { + mediaOptionId, + mediaSeqNum: 'initSegment', + discoSeqNum, + name: MediaOptionNames[mediaFragment.mediaOptionType], + state: 'loading', + }; + const tlog = logger.child({ name: 'timing' }); + tlog.info(`${JSON.stringify(timingObj)}`); + return getMediaFragmentCommon(libContext, initFrag, false, false).pipe(tap(() => { + tlog.info(`${JSON.stringify(Object.assign(Object.assign({}, timingObj), { state: 'loaded' }))}`); + }), observeOn(asyncScheduler), switchMap((data) => { + tlog.info(`${JSON.stringify(Object.assign(Object.assign({}, timingObj), { state: 'parsing' }))}`); + return parseInitSegment(data, initFrag, libContext); + }), tap(() => { + tlog.info(`${JSON.stringify(Object.assign(Object.assign({}, timingObj), { state: 'parsed' }))}`); + })); + }; + function parseInitSegment(data, frag, libContext) { + var _a; + const { logger, mediaSink, rootPlaylistService, rootPlaylistQuery, mediaParser, mediaLibraryService, gaplessInstance } = libContext; + const { mediaQuery } = mediaSink; + const mediaLibraryQuery = mediaLibraryService.getQueryForOption(frag); + const { mediaOption, mediaOptionDetails } = mediaLibraryQuery; + const { itemId, mediaOptionId } = mediaOption; + const { keyTagInfo, discoSeqNum, mediaSeqNum, mediaOptionType } = frag; + const seeking = mediaQuery.seeking; + const live = mediaOptionDetails.liveOrEvent; + const ptsKnown = mediaOptionType === MediaOptionType.Variant ? mediaOptionDetails.ptsKnown : false; + let segment; + let initSegment; + if (frag.isInitSegment) { + initSegment = new Uint8Array(data); + } + else { + segment = new Uint8Array(data); + } + const parserContext = { + segment, + initSegment, + frag, + ptsKnown, + seeking, + live, + totalDuration: mediaOptionDetails.totalduration, + }; + return mediaParser.parseInitSegment(parserContext, (_a = navigator === null || navigator === void 0 ? void 0 : navigator.vendor) !== null && _a !== void 0 ? _a : '').pipe(map((parsedInitSegment) => { + const { track, moovData, mimeType } = parsedInitSegment; + const { initSegment } = track; + if (gaplessInstance.inGaplessMode && MediaUtil.isVideoCodec(track.codec)) { + logger.warn(`Video codec discovered in gapless mode codec:${track.codec}`); + gaplessInstance.dequeueSource('InvalidFormat'); + } + const initSegmentCacheEntity = { itemId, mediaOptionId, discoSeqNum, initParsedData: moovData, data: initSegment, mimeType, keyTagInfo, fragment: frag }; + mediaLibraryService.archiveInitSegmentEntity(initSegmentCacheEntity); + logger.info({ mediaOptionId, mediaSeqNum, discoSeqNum }, 'loaded init segment'); + return initSegmentCacheEntity; + }), addDemuxErrorHandlingPolicy(rootPlaylistService, rootPlaylistQuery, mediaOptionType, mediaOptionId)); + } + function parseSegment(data, defaultInitPTS, initSegmentCacheEntity, frag, timelineOffset, libContext) { + var _a, _b; + const segment = new Uint8Array(data); + const { legibleSystemAdapter, rootPlaylistService, mediaSink, mediaParser, rootPlaylistQuery, mediaLibraryService } = libContext; + const { mediaQuery } = mediaSink; + const mediaLibraryQuery = mediaLibraryService.getQueryForOption(frag); + const { mediaOption, mediaOptionDetails } = mediaLibraryQuery; + const { initSegments } = mediaOptionDetails; + const { itemId, mediaOptionId } = mediaOption; + const { discoSeqNum, mediaSeqNum, mediaOptionType, isLastFragment } = frag; + const seeking = mediaQuery.seeking; + const live = mediaOptionDetails.liveOrEvent; + const initSeg = initSegments[discoSeqNum]; + const ptsKnown = mediaOptionType === MediaOptionType.Variant ? mediaOptionDetails.ptsKnown : false; + const parserContext = { + segment, + frag, + seeking, + live, + ptsKnown, + totalDuration: mediaOptionDetails.totalduration, + defaultInitPTS, + iframeMediaStart: isIframeMediaFragment(frag) ? frag.iframeMediaStart : undefined, + iframeDuration: isIframeMediaFragment(frag) ? frag.iframeMediaDuration : undefined, + iframeOriginalStart: isIframeMediaFragment(frag) ? frag.iframeOriginalStart : undefined, + }; + let source; + if (initSegmentCacheEntity != null && ((_a = frag.keyTagInfo) === null || _a === void 0 ? void 0 : _a.uri) === ((_b = initSegmentCacheEntity.keyTagInfo) === null || _b === void 0 ? void 0 : _b.uri)) { + source = of(initSegmentCacheEntity); + } + else if (initSegmentCacheEntity != null) { + // key rotated, update cached init segment with current frag keyTagInfo, pass to demuxer for parsing + const updatedFrag = Object.assign(Object.assign({}, initSegmentCacheEntity.fragment), { keyTagInfo: frag.keyTagInfo }); + const initData = initSeg ? initSegmentCacheEntity.data : data; + source = parseInitSegment(initData, updatedFrag, libContext); + } + else { + source = parseInitSegment(data, frag, libContext); + } + return source.pipe(switchMap((initSegmentCacheEntity) => { + const parseStartTime = performance.now(); + if (initSegmentCacheEntity != null) { + const { data: is } = initSegmentCacheEntity; + const initSegment = new Uint8Array(is); + parserContext.initSegment = initSegment; + } + if (frag.mediaOptionType === MediaOptionType.Variant) { + legibleSystemAdapter === null || legibleSystemAdapter === void 0 ? void 0 : legibleSystemAdapter.setupForFrag(frag); + } + return mediaParser.parseSegment(parserContext, '').pipe(map((parsedSegment) => { + var _a; + const parseEndTime = performance.now(); + const { startPTS, startDTS: startDtsTs, endPTS, endDTS: endDtsTs, firstKeyframePts, framesWithoutIDR, dropped, data1, data2, captionData, id3Samples, parsedInitSegment } = parsedSegment; + const fragSample = { + durationSec: endPTS.baseTime / endPTS.timescale - startPTS.baseTime / startPTS.timescale, + parseTimeMs: parseEndTime - parseStartTime, + }; + libContext.statsService.setFragSample(fragSample); + // parser updated initSegment, possibly due to silent audio insertion + let generatedInitSegmentCacheEntity = Object.assign({}, initSegmentCacheEntity); + if (parsedInitSegment) { + const { track, moovData, mimeType } = parsedInitSegment; + const { initSegment } = track; + generatedInitSegmentCacheEntity = { itemId, mediaOptionId, discoSeqNum, initParsedData: moovData, data: initSegment, mimeType, keyTagInfo: frag.keyTagInfo, fragment: frag }; + mediaLibraryService.archiveInitSegmentEntity(initSegmentCacheEntity, generatedInitSegmentCacheEntity); + } + const keyTagInfo = frag.keyTagInfo; + const mediaFragmentCacheEntity = { + itemId, + mediaOptionId, + mediaSeqNum, + discoSeqNum, + startDtsTs, + endDtsTs, + timelineOffset, + firstKeyframePts, + framesWithoutIDR, + dropped, + data1, + data2, + startPts: startPTS, + endPts: endPTS, + keyTagInfo, + isLastFragment, + iframe: (_a = frag.iframe) !== null && _a !== void 0 ? _a : false, + duration: frag.duration, + iframeMediaDuration: isIframeMediaFragment(frag) ? frag.iframeMediaDuration : undefined, + iframeOriginalStart: isIframeMediaFragment(frag) ? frag.iframeOriginalStart : undefined, + captionData, + id3Samples, + }; + const appendDataTuple = [generatedInitSegmentCacheEntity, mediaFragmentCacheEntity]; + return appendDataTuple; + })); + }), addDemuxErrorHandlingPolicy(rootPlaylistService, rootPlaylistQuery, mediaOptionType, mediaOptionId)); + } + // Fragment loading with error handling + function getMediaFragmentCommon(libContext, frag, updateStats, updateRtc) { + var _a, _b, _c; + const { rootPlaylistQuery: rootQuery, rootPlaylistService: rootService, config, rtcService, statsService } = libContext; + const { itemId, mediaOptionType } = frag; + const loadPolicy = config.fragLoadPolicy; + const isMediaFragment = isFiniteNumber(frag.mediaSeqNum); + let onProgress; + if (isMediaFragment) { + const updateSample = (_url, _status, stats, _data) => { + rootService.updateInflightFrag(itemId, frag.mediaOptionType, frag, 'loading', stats); + return false; + }; + onProgress = { getData: false, cb: updateSample }; + } + let requestServerInfo = false; + if (updateRtc) { + if (rtcService.serverInfoInstance === null) { + requestServerInfo = true; + } + } + const fetchSegment$ = loadMediaFragment(frag, config, loadPolicy, onProgress, requestServerInfo, (_b = (_a = globalHlsService()) === null || _a === void 0 ? void 0 : _a.getQuery()) === null || _b === void 0 ? void 0 : _b.extendMaxTTFB).pipe(tap(([, , stats, serverInfo]) => { + libContext.logger.qe({ + critical: true, + name: 'resourceTimingMonitor', + data: { + type: MediaOptionNames[frag.mediaOptionType], + trequest: stats.trequest, + tfirst: stats.tfirst, + tload: stats.tload, + bitsDownloaded: stats.total, + mediaSeqNum: frag.mediaSeqNum, + mediaOpitonId: frag.mediaOptionId, + }, + }); + if (updateStats) { + statsService.setBandwidthSample(Object.assign(Object.assign({}, stats), { mediaOptionType: frag.mediaOptionType })); + } + if (updateRtc && requestServerInfo) { + rtcService.serverInfoInstance = serverInfo; + } + if (isMediaFragment) { + rootService.updateInflightFrag(itemId, frag.mediaOptionType, frag, 'loaded', stats); + } + }), tap(([mediaFragment, , stats]) => { + if (updateRtc) { + const { logger } = rootService; + logger.qe({ + critical: true, + name: 'fragLoaded', + data: { mediaOptionType: frag.mediaOptionType, fragLoadingProcessingMs: stats.tload - stats.tfirst, loaded: stats.loaded, duration: frag.duration, fragLoadMs: stats.tload - stats.trequest }, + }); + rtcService.handleFragLoaded(mediaFragment, stats); + } + }), addLoadErrorHandlingPolicy(itemId, mediaOptionType, getLoadConfig(frag, loadPolicy), config.maxNumAddLevelToPenaltyBox, false, rootQuery, rootService, statsService)); + // Don't need to wait for key if segment is not encrypted! + const shouldWaitForKey = ((_c = frag.keyTagInfo) === null || _c === void 0 ? void 0 : _c.method) === 'AES-128'; + if (shouldWaitForKey) { + return forkJoin([loadKey(libContext, frag.keyTagInfo, { itemId: frag.itemId, mediaOptionId: frag.mediaOptionId }), fetchSegment$]).pipe(switchMap(([keyTagInfo, fragInfo]) => { + const [mediaFragment, fragDataBuf] = fragInfo; + mediaFragment.keyTagInfo.key = keyTagInfo.key; + return decryptMediaFragment(mediaFragment, fragDataBuf, config, libContext.logger, libContext.rpcClients.crypto); + })); + } + return fetchSegment$.pipe(map((fragInfo) => fragInfo[1])); + } + /** + * Retrieve the data for a media fragment + * + * @param {MediaFragment} retrieveFragAction the media fragment to retrieve data for + * @returns {Observable} the init segment data and fragment data + */ + const retrieveSubtitleFragmentCacheEntity = (libContext, initPTS, mediaFragment) => { + const { logger } = libContext; + const { mediaOptionType, mediaOptionId, discoSeqNum, mediaSeqNum } = mediaFragment; + logger.info(`[${MediaOptionNames[mediaOptionType]}] loading media fragment ${JSON.stringify({ mediaOptionId, discoSeqNum, mediaSeqNum })}`); + return getMediaFragmentCommon(libContext, mediaFragment, false, false).pipe(map((data) => { + logger.info(`[${MediaOptionNames[mediaOptionType]}] loaded media fragment ${JSON.stringify({ mediaOptionId, discoSeqNum, mediaSeqNum })}`); + return { initPTS, data, mediaFragment }; + }), tag('retrieveSubtitleFragmentCacheEntity.emit')); + }; + /** + * Retrieve the data for a media fragment + * + * @param {MediaFragment} retrieveFragAction the media fragment to retrieve data for + * @returns {Observable} the init segment data and fragment data + */ + const retrieveMediaFragmentCacheEntity = (libContext, mediaOptionType, retrieveFragAction) => { + const { rootPlaylistService: rootService, rootPlaylistQuery: rootQuery } = libContext; + const { logger } = rootService; + const { timelineOffset, mediaFragment } = retrieveFragAction.foundFrag; + const { itemId, mediaOptionId, discoSeqNum, mediaSeqNum } = mediaFragment; + logger.info(`[${MediaOptionNames[mediaOptionType]}] loading media fragment ${JSON.stringify({ mediaOptionId, discoSeqNum, mediaSeqNum })}`); + return retrieveInitSegmentCacheEntity(libContext, mediaFragment).pipe(switchMap((initSegmentCacheEntity) => { + rootService.updateInflightFrag(itemId, mediaFragment.mediaOptionType, mediaFragment, 'loading', null); + return getMediaFragmentCommon(libContext, mediaFragment, true, true).pipe(switchMap((data) => { + var _a; + rootService.updateInflightFrag(itemId, mediaOptionType, mediaFragment, 'parsing', null); + return parseSegment(data, (_a = rootQuery.getInitPTS(discoSeqNum)) === null || _a === void 0 ? void 0 : _a.offsetTimestamp, initSegmentCacheEntity, mediaFragment, timelineOffset, libContext); + }), tap((parsedData) => { + rootService.updateInflightFrag(itemId, mediaOptionType, mediaFragment, 'parsed', null); + const { startPts, endPts, startDtsTs, endDtsTs } = parsedData[1]; + logger.info(`[${MediaOptionNames[mediaOptionType]}] ${JSON.stringify({ mediaOptionId, discoSeqNum, mediaSeqNum })} parsed: ${JSON.stringify({ startPts, endPts, startDtsTs, endDtsTs })}`); + }), tag(`retrieveMediaFragmentCacheEntity.${mediaOptionType}.emit`)); + }), take(1)); + }; + function loadKey(context, keyTagInfo, mediaOptionKey) { + const { keySystemAdapter, rootPlaylistQuery, rootPlaylistService, config } = context; + return keySystemAdapter + .getKeyFromDecryptData(keyTagInfo, mediaOptionKey) + .pipe(addKeyErrorHandlingPolicy(keyTagInfo.uri, getLoadConfig({ url: keyTagInfo.uri }, config.keyLoadPolicy), rootPlaylistQuery, rootPlaylistService, keySystemAdapter.ksQuery)); + } + function mediaLibraryRemove(ids) { + mediaLibraryService().remove(ids); + } + function mediaLibraryClear() { + mediaLibraryService().clear(); + } + + /* + * HLS Player Events + * + * + */ + /* + * @brief subscribes to hls public queries and sends out events. + */ + class HlsPlayerEvents { + constructor(hls, logger, rtcService) { + this.hls = hls; + this.destroy$ = new Subject(); + this.iframeSwitchStart = 0; + this.logger = logger.child({ name: 'hls-player-events' }); + this.rtc = rtcService; + this.subscribeAndEmit(); + } + destroy() { + this.destroy$.next(); + } + subscribeAndEmit() { + const loaderQuery$ = this.loaderQueryListener(createLoaderQuery()); + const publicQueries$ = this.hls.publicQueries$.pipe(switchMap(([rootPlaylistQuery, mediaElementQuery]) => { + return merge(this.rootPlaylistQueryListener(rootPlaylistQuery, mediaElementQuery), this.mediaElementQueryListener(mediaElementQuery, rootPlaylistQuery)); + })); + const activeItemQuery$ = this.activeItemListener(this.hls.itemQueue); + merge(loaderQuery$, publicQueries$, activeItemQuery$) + .pipe(catchError((err) => { + let errMessage = err.message; + { + errMessage = err.stack; + } + this.logger.error(`Got error in HlsPlayerEvents ${errMessage}`, err); + return EMPTY; + }), takeUntil(this.destroy$), finalize$1(() => { + this.logger.info('HlsPlayerEvents finalized'); + })) + .subscribe(); + } + activeItemListener(itemQueue) { + const manifestLoadSource$ = itemQueue.activeItemById$.pipe(filterNullOrUndefined(), switchMap((item) => { + var _a; + const url = item.url; + this.logger.debug(`Manifest loading: ${(_a = this.hls.itemQueue.activeItem) === null || _a === void 0 ? void 0 : _a.url}`); + this.hls.trigger(HlsEvent.MANIFEST_LOADING, { url }); + return EMPTY; + })); + return manifestLoadSource$; + } + rootPlaylistQueryListener(rootPlaylistQuery, mediaElementQuery) { + const variantSource$ = rootPlaylistQuery.enabledMediaOptionByType$(MediaOptionType.Variant).pipe(filter((mediaOption) => !!mediaOption), switchMap((mediaOption) => { + var _a; + this.logger.debug(`Switching to level: ${mediaOption.mediaOptionId}`); + this.hls.trigger(HlsEvent.LEVEL_SWITCHING, mediaOption); + (_a = this.rtc) === null || _a === void 0 ? void 0 : _a.handleLevelSwitching(mediaOption.url); + return EMPTY; + })); + const levelLoading$ = rootPlaylistQuery.enabledMediaOptionByType$(MediaOptionType.Variant).pipe(switchMap((mediaOption) => { + const mediaLibQuery = createMediaLibraryQuery(mediaOption); + return mediaLibQuery.mediaOptionDetailsEntity$.pipe(filter((entity) => (entity === null || entity === void 0 ? void 0 : entity.detailsLoading) === true), tap((_) => { + const levelLoading = { + url: redactUrl(mediaOption === null || mediaOption === void 0 ? void 0 : mediaOption.url), + level: mediaOption.mediaOptionId, + type: MediaOptionNames[mediaOption.mediaOptionType], + }; + this.logger.qe({ critical: true, name: 'levelLoading', data: levelLoading }); + this.hls.trigger(HlsEvent.LEVEL_LOADING, levelLoading); + return EMPTY; + })); + })); + const levelLoaded$ = rootPlaylistQuery.enabledMediaOptionByType$(MediaOptionType.Variant).pipe(switchMap((mediaOption) => { + const query = createMediaLibraryQuery(mediaOption); + let lastUpdate = 0; + // filter on entities that have loading set to false and have stats filled and have been updated + return query.mediaOptionDetailsEntity$.pipe(filterNullOrUndefined(), filter((entity) => { + var _a; + const retValue = entity.stats !== null && entity.detailsLoading === false && entity.lastUpdateMillis > lastUpdate; + lastUpdate = (_a = entity.lastUpdateMillis) !== null && _a !== void 0 ? _a : 0; + return retValue; + })); + }), switchMap((mediaDetailsEntity) => { + var _a; + const mediaOptionDetails = mediaDetailsEntity.mediaOptionDetails; + const stats = mediaDetailsEntity.stats; + const levelLoadedData = { + mediaOptionId: mediaOptionDetails.mediaOptionId, + details: mediaOptionDetails, + playlistType: mediaOptionDetails.type, + stats: stats, + }; + // this.logger.debug('Level loaded %o', levelLoadedData); + this.logger.qe({ + critical: true, + name: 'levelLoaded', + data: { url: redactUrl(mediaOptionDetails === null || mediaOptionDetails === void 0 ? void 0 : mediaOptionDetails.url), level: mediaOptionDetails.mediaOptionId, type: MediaOptionNames[mediaOptionDetails.mediaOptionType], adt: stats.tload - stats.trequest }, + }); + (_a = this.rtc) === null || _a === void 0 ? void 0 : _a.handleLevelLoaded(mediaOptionDetails, levelLoadedData.stats); + this.hls.trigger(HlsEvent.LEVEL_LOADED, levelLoadedData); + if (mediaDetailsEntity.unchangedCount === 0) { + const levelUpdatedData = { + level: 0, + details: mediaOptionDetails, + }; + // This is critical for gapless. Muze uses this to set media duration + this.hls.trigger(HlsEvent.LEVEL_UPDATED, levelUpdatedData); + } + if (mediaOptionDetails === null || mediaOptionDetails === void 0 ? void 0 : mediaOptionDetails.daterangeTags) { + const dateRangeTags = { daterangeTags: mediaOptionDetails.daterangeTags }; + // this.logger.debug('Date range tags parsed: %o', dateRangeTags); + this.logger.qe({ critical: true, name: 'dateRangeTags', data: { dateRangeTags } }); + this.hls.trigger(HlsEvent.DATERANGE_UPDATED, dateRangeTags); + } + return EMPTY; + })); + const audioSource$ = rootPlaylistQuery.enableMediaOptionSwitchedForType$(MediaOptionType.AltAudio).pipe(switchMap((mediaOption) => { + const altOption = rootPlaylistQuery.alternateMediaOptionById(MediaOptionType.AltAudio, mediaOption.mediaOptionId); + if (altOption) { + this.triggerAudioSwitch(altOption); + } + return EMPTY; + })); + // The first audio switch occurs before the rootPlaylistQuery is ready. As a result, we'll monitor the + // rootPlaylistEntity to trigger the first audio switch + const firstAudioSwitch$ = rootPlaylistQuery.rootPlaylistEntity$.pipe(filter((rootEntity) => rootEntity.enabledMediaOptionKeys[MediaOptionType.AltAudio].mediaOptionId !== null), take(1), switchMap((rootEntity) => { + const mediaOptionId = rootEntity.enabledMediaOptionKeys[MediaOptionType.AltAudio].mediaOptionId; + if (mediaOptionId) { + const altOption = rootEntity.mediaOptionListTuple[MediaOptionType.AltAudio].mediaOptions.find((option) => option.mediaOptionId === mediaOptionId); + this.triggerAudioSwitch(Object.assign(Object.assign({}, altOption), { url: redactUrl(altOption === null || altOption === void 0 ? void 0 : altOption.url) })); + } + return EMPTY; + })); + // Trigger SUBTITLE_TRACK_SWITCH only when the HTML5 texttracks have been created. + // MatchPoint will call addCueChange on the enabled texttrack. + const subtitleSource$ = waitFor(mediaElementQuery.textTracksCreated$, (created) => created).pipe(switchMap(() => { + return rootPlaylistQuery.enabledMediaOptionByType$(MediaOptionType.Subtitle).pipe(switchMap((mediaOption) => { + const altOption = rootPlaylistQuery.alternateMediaOptionById(MediaOptionType.Subtitle, mediaOption.mediaOptionId); + if (altOption) { + const data = { + trackId: altOption.id, + mediaOptionId: altOption.mediaOptionId, + groupId: altOption.groupId, + persistentId: altOption.persistentID, + name: altOption.name, + }; + this.logger.qe({ critical: true, name: 'textTrackSwitch', data }); + this.logger.debug(`Subtitle track switch track: ${altOption.id}`); + this.hls.trigger(HlsEvent.SUBTITLE_TRACK_SWITCH, { + track: Object.assign({}, altOption), + hidden: false, + }); + } + else { + this.hls.trigger(HlsEvent.SUBTITLE_TRACK_SWITCH, { + track: undefined, + hidden: false, + }); + this.logger.qe({ critical: true, name: 'textTrackSwitch', data: { unselected: true } }); + } + return EMPTY; + })); + })); + const sessionData$ = rootPlaylistQuery.sessionData$.pipe(filter((d) => d.complete != undefined), take(1), tap((sd) => this.logger.debug('Session Data Complete: %o', sd)), map((sd) => { + this.hls.trigger(HlsEvent.SESSION_DATA_COMPLETE, sd); + })); + const prefferedLevels$ = rootPlaylistQuery.getPreferredMediaOptionsByType$(MediaOptionType.Variant).pipe(skip(1), map((prefLevels) => { + var _a; + const variantList = prefLevels; + this.logger.debug('Levels changed %o', variantList); + (_a = this.rtc) === null || _a === void 0 ? void 0 : _a.handleLevelsChanged(variantList); + this.logger.qe({ critical: true, name: 'levelsChanged', data: { numLevels: variantList.length } }); + variantList.forEach((level) => { + const { mediaOptionId, bandwidth, bitrate, videoCodec, audioCodec, height, width, videoRange, iframes } = level; + this.logger.qe({ critical: true, name: 'manifestLevel', data: { mediaOptionId, bandwidth, bitrate, videoCodec, audioCodec, height, width, videoRange, iframes } }); + }); + this.hls.trigger(HlsEvent.LEVELS_CHANGED, { requiresReset: false, levels: variantList }); + //TODO fix require reset + })); + const altPrefferedLevels$ = rootPlaylistQuery.getPreferredMediaOptionsByType$(MediaOptionType.AltAudio).pipe(map((prefLevels) => { + const altList = prefLevels; + this.logger.debug('Audio tracks updated %o', altList); + this.hls.trigger(HlsEvent.AUDIO_TRACKS_UPDATED, { audioTracks: altList }); + //TODO fix require reset + })); + // Trigger SUBTITLE_TRACKS_UPDATED only when the HTML5 texttracks have been created. + // MatchPoint may query the available media option list and relate them to the HTML5 texttracks + const subPrefferedLevels$ = waitFor(mediaElementQuery.textTracksCreated$, (created) => created).pipe(switchMap(() => { + return rootPlaylistQuery.getPreferredMediaOptionsByType$(MediaOptionType.Subtitle).pipe(take(1), map((prefLevels) => { + const altList = prefLevels; + // Potentially large object log + // this.logger.info('Subtitle tracks updated %o', altList); + this.hls.trigger(HlsEvent.SUBTITLE_TRACKS_UPDATED, { subtitleTracks: altList }); + this.logger.info('Subtitle tracks created'); + this.hls.trigger(HlsEvent.SUBTITLE_TRACKS_CREATED); + //TODO fix require reset + })); + })); + return merge(variantSource$, audioSource$, subtitleSource$, sessionData$, prefferedLevels$, altPrefferedLevels$, subPrefferedLevels$, levelLoaded$, levelLoading$, firstAudioSwitch$); + } + mediaElementQueryListener(mediaElementQuery, rootPlaylistQuery) { + const seekSource$ = mediaElementQuery.seekTo$.pipe(map((seekTo) => { + var _a, _b; + if (seekTo && isFiniteNumber(seekTo.pos)) { + this.logger.debug(`Seeking pos:${seekTo.pos}`); + (_a = this.rtc) === null || _a === void 0 ? void 0 : _a.handleSeek('SEEKING'); + this.hls.trigger(HlsEvent.SEEKING, { seekToPos: seekTo.pos }); + } + else if (seekTo === null) { + (_b = this.rtc) === null || _b === void 0 ? void 0 : _b.handleSeek('SEEKED'); + this.logger.debug('Seeked'); + this.hls.trigger(HlsEvent.SEEKED); + } + })); + const desiredRateSource$ = mediaElementQuery.desiredRate$.pipe(startWith(0), pairwise(), map((pair) => { + var _a; + const oldRate = pair[0]; + const newRate = pair[1]; + if (isIframeRate(newRate)) { + if (this.iframeSwitchStart == 0) { + this.iframeSwitchStart = performance.now(); + } + } + else { + this.iframeSwitchStart = 0; + } + this.logger.debug(`Rate changed oldRate: ${oldRate}, newRate: ${newRate}`); + this.hls.trigger(HlsEvent.DESIRED_RATE_CHANGED, { oldRate, newRate }); + (_a = this.rtc) === null || _a === void 0 ? void 0 : _a.handleDesiredRateChanged(oldRate, newRate); + })); + const audioBufferSource$ = mediaElementQuery.sourceBufferEntityByType$(SourceBufferType.AltAudio).pipe(filter((buffer) => !!buffer), distinctUntilChanged((prev, cur) => prev.totalBytes === cur.totalBytes), map((buffer) => { + this.logger.trace('Audio Buffer Appended. Buffer= %o', buffer); + this.hls.trigger(HlsEvent.BUFFER_APPENDED); + })); + const variantBufferSource$ = mediaElementQuery.sourceBufferEntityByType$(SourceBufferType.Variant).pipe(filter((buffer) => !!buffer), distinctUntilChanged((prev, cur) => prev.totalBytes === cur.totalBytes), map((buffer) => { + var _a; + this.logger.trace('Variant Buffer Appended. Buffer= %o', buffer); + (_a = this.rtc) === null || _a === void 0 ? void 0 : _a.handleVariantBufferAppended(buffer.timestampOffset, buffer.totalBytes); + this.hls.trigger(HlsEvent.BUFFER_APPENDED); + })); + const stallInfo$ = mediaElementQuery.stallInfo$.pipe(filterNullOrUndefined(), withLatestFrom(mediaElementQuery.combinedBuffer$), map(([stallInfo, buffered]) => { + var _a; + this.logger.qe({ critical: true, name: 'stall', data: Object.assign(Object.assign({}, stallInfo), { buffered }) }); + (_a = this.rtc) === null || _a === void 0 ? void 0 : _a.handleStalled(stallInfo, mediaElementQuery.getCombinedBufferInfo(stallInfo.currentTime, 0).len); + this.hls.trigger(HlsEvent.STALLED, stallInfo); + })); + const fragChangeMonitor$ = combineLatest([ + of(rootPlaylistQuery), + combineQueries([mediaElementQuery.timeupdate$, mediaElementQuery.bufferedSegmentsByType$(SourceBufferType.Variant)]).pipe(throttleTime(1000), map(([currentTime, bufferedSegments]) => { + const playingFrag = bufferedSegments === null || bufferedSegments === void 0 ? void 0 : bufferedSegments.find((seg) => seg.startPTS <= currentTime && seg.endPTS > currentTime); + return playingFrag; + }), filter((playingFrag) => !!playingFrag), startWith(null), pairwise()), + ]).pipe(switchMap(([rootPlaylistQuery, [a, b]]) => { + var _a; + const playingFrag = b === null || b === void 0 ? void 0 : b.frag; + const previousFrag = a === null || a === void 0 ? void 0 : a.frag; + if (playingFrag && !fragEqual(previousFrag, playingFrag)) { + this.hls.trigger(HlsEvent.FRAG_CHANGED, b); + if (this.hls.inGaplessMode) { + this.checkAndTriggerReadyForNext(mediaElementQuery, b); + } + if (!previousFrag || playingFrag.mediaOptionId !== previousFrag.mediaOptionId) { + const variantOption = rootPlaylistQuery.mediaOptionListQueries[MediaOptionType.Variant].mediaOptionFromId(playingFrag.mediaOptionId); + if (!variantOption) { + // TODO investigate this.. sometimes happens in gapless mode + this.logger.warn('variantInfo is undefined in fragChangeMonitor'); + return EMPTY; + } + const oldVariant = previousFrag ? previousFrag.mediaOptionId : ''; + const newVariant = playingFrag.mediaOptionId; + const data = { + oldVariant, + newVariant, + bitrate: variantOption.bitrate, + bandwidth: variantOption.bandwidth, + avgBandwidth: variantOption.avgBandwidth, + width: variantOption.width, + height: variantOption.height, + levelCodec: variantOption.levelCodec, + frameRate: variantOption.frameRate, + }; + if (variantOption.iframes) { + const rate = mediaElementQuery.desiredRate; + const state = mediaElementQuery.isIframeRate; + const startupTime = performance.now() - this.iframeSwitchStart; + this.logger.qe({ critical: true, name: 'iframes', data: { state, rate, oldVariant, newVariant, startupTime } }); + } + this.logger.qe({ critical: true, name: 'variantSwitched', data }); + (_a = this.rtc) === null || _a === void 0 ? void 0 : _a.handleLevelSwitched({ url: variantOption.url, mediaOptionId: variantOption.mediaOptionId, oldVariant: oldVariant !== '' ? oldVariant : undefined, newVariant: newVariant }); + this.hls.trigger(HlsEvent.LEVEL_SWITCHED, variantOption); + } + } + return EMPTY; + })); + const fullyBuffered$ = mediaElementQuery.isBufferedToEnd$(this.hls.config.maxBufferHole, false).pipe(filter((isBuffered) => isBuffered === true), + // Use asyncScheduler as the READY_FOR_NEXT_ITEM event could cause an item queue change + observeOn(asyncScheduler), map((isBuffered) => { + if (isBuffered && !this.hls.itemQueue.isPreloading() && this.hls.inGaplessMode) { + const combinedBuffer = mediaElementQuery.getCombinedBufferInfo(mediaElementQuery.currentTime, 0); + let duration = 0; + if (combinedBuffer) { + duration = combinedBuffer.end; + } + else { + this.logger.info('Combined Buffer is not ready, do not trigger READY_FOR_NEXT_ITEM'); + return; + } + const mediaDuration = mediaElementQuery.mediaElementDuration; + // Only trigger READY_FOR_NEXT with less than 10 seconds to play. This is a Muze hack so that + // short songs are not skipped + if (duration > 0 && mediaDuration - mediaElementQuery.currentTime < 10) { + this.logger.info(`Trigger READY_FOR_NEXT_ITEM BufferDuration=${duration}, mediaDuration=${mediaDuration}`); + this.hls.trigger(HlsEvent.READY_FOR_NEXT_ITEM, { duration }); + } + } + })); + return merge(seekSource$, desiredRateSource$, audioBufferSource$, variantBufferSource$, stallInfo$, fragChangeMonitor$, fullyBuffered$); + } + checkAndTriggerReadyForNext(mediaElementQuery, currentFrag) { + if (!currentFrag || !currentFrag.frag) { + return; + } + this.logger.trace('currentFrag %o', currentFrag); + const pos = mediaElementQuery.currentTime; + const combinedBuffer = mediaElementQuery.getCombinedBufferInfo(pos, 0); + if (!combinedBuffer) { + this.logger.info('Combined Buffer is not ready, do not trigger READY_FOR_NEXT_ITEM'); + return; + } + const duration = combinedBuffer.end; + const mediaDuration = mediaElementQuery.mediaElementDuration; + const bufInfoMonitor = mediaElementQuery.bufferMonitorInfo; + // The closeToEndThreashold is typically half of the target duration. This differs from 2.0 that uses the full target duration. + // This leaves short time to buffer the next item. Good for audio but will not work for video. + // If we ever want to do gapless video we'll need to re-visit the using the fullyBuffered$ method above + const closeToEndThreshold = Math.max(bufInfoMonitor.almostDryWaterLevelSeconds, bufInfoMonitor.lowWaterLevelSeconds / 2); + if (mediaDuration - currentFrag.endPTS <= closeToEndThreshold || currentFrag.frag.isLastFragment) { + if (this.hls.inGaplessMode && !this.hls.isPreloading) { + this.logger.info(`Trigger READY_FOR_NEXT_ITEM BufferDuration=${duration}, mediaDuration=${mediaDuration}`); + this.hls.trigger(HlsEvent.READY_FOR_NEXT_ITEM, { duration }); + } + } + } + loaderQueryListener(loaderQuery) { + const unresolvedUriLoadingSource$ = loaderQuery.unresolvedUriLoading$.pipe(map((entities) => { + return entities.map((entity) => { + this.logger.debug('Unresolved Uri Loading, data= %o', entity); + const data = { uri: entity.uri, responseType: entity.responseType, userAgent: entity.userAgent }; + this.hls.trigger(HlsEvent.UNRESOLVED_URI_LOADING, data); + }); + })); + return merge(unresolvedUriLoadingSource$); + } + triggerAudioSwitch(altOption) { + if (altOption) { + this.logger.info(`Audio track switched id: ${altOption.id} ${altOption.mediaOptionId}`); + this.logger.qe({ critical: true, name: 'audioTrackSwitched', data: { altOption } }); + this.hls.trigger(HlsEvent.AUDIO_TRACK_SWITCHED, { id: altOption.id }); + } + } + triggerManifestLoaded(loadRootMediaOptionsResult) { + var _a; + const payload = { + levels: loadRootMediaOptionsResult.rootMediaOptionsTuple[MediaOptionType.Variant], + audioTracks: loadRootMediaOptionsResult.rootMediaOptionsTuple[MediaOptionType.AltAudio], + subtitleTracks: loadRootMediaOptionsResult.rootMediaOptionsTuple[MediaOptionType.Subtitle], + url: loadRootMediaOptionsResult.baseUrl, + audioMediaSelectionGroup: loadRootMediaOptionsResult.audioMediaSelectionGroup, + subtitleMediaSelectionGroup: loadRootMediaOptionsResult.subtitleMediaSelectionGroup, + stats: loadRootMediaOptionsResult.stats, + isMediaPlaylist: loadRootMediaOptionsResult.isMediaPlaylist, + }; + this.logger.debug('Manifest loaded'); + this.logger.qe({ critical: true, name: 'manifestLoaded', data: { numLevels: (_a = payload.levels) === null || _a === void 0 ? void 0 : _a.length } }); + this.hls.trigger(HlsEvent.MANIFEST_LOADED, payload); + } + triggerManifestParsed(rootQuery) { + var _a, _b; + // TODO Fix firstlevel, audio, video and altAudio + const manifestParsedData = { + levels: rootQuery.mediaOptionListQueries[MediaOptionType.Variant].filteredMediaOptionList, + firstLevel: 0, + audio: false, + video: true, + altAudio: false, + audioTracks: rootQuery.mediaOptionListQueries[MediaOptionType.AltAudio].filteredMediaOptionList, + audioMediaSelectionGroup: rootQuery.audioMediaSelectionGroup, + subtitleMediaSelectionGroup: rootQuery.subtitleMediaSelectionGroup, + stats: rootQuery.loadStats, + }; + this.logger.debug('Manifest parsed %o', manifestParsedData); + this.logger.qe({ critical: true, name: 'manifestParsed', data: { numLevels: (_a = manifestParsedData.levels) === null || _a === void 0 ? void 0 : _a.length } }); + (_b = this.rtc) === null || _b === void 0 ? void 0 : _b.handleManifestParsed(manifestParsedData); + this.hls.trigger(HlsEvent.MANIFEST_PARSED, manifestParsedData); + } + urlRedactedManifestLoaded(indata) { + const outdata = Object.assign({}, indata); + outdata.url = redactUrl(outdata.url); + outdata.levels = urlRedactedLevelInfo(outdata.levels); + outdata.audioTracks = urlRedactedAltMediaOption(outdata.audioTracks); + outdata.subtitleTracks = urlRedactedAltMediaOption(outdata.subtitleTracks); + return outdata; + } + urlRedactedManifestParsed(indata) { + const outdata = Object.assign({}, indata); + outdata.levels = urlRedactedLevelInfo(outdata.levels); + outdata.audioTracks = urlRedactedAltMediaOption(outdata.audioTracks); + return outdata; + } + } + + var SwitchReason; + (function (SwitchReason) { + SwitchReason["LowBandwidth"] = "LowBandwidth"; + SwitchReason["HighBandwidth"] = "HighBandwidth"; + SwitchReason["PreferredListChanged"] = "PreferredListChanged"; + SwitchReason["IframeModeChange"] = "IframeModeChange"; + SwitchReason["None"] = ""; + })(SwitchReason || (SwitchReason = {})); + + const firstMediaOptionSelectionMetrics = { + minValidBitrate: 2000000, + maxValidBitrate: 5000000, + maxPreferredBitrate: 3000000, + minValidHeight: 480, + maxValidHeight: 720, + }; + // Filter media options that are not compatible with the starting tier + // Currently we hit an issue when switching between audio codecs: ac-3 / ec-3 / mp4a.40.* + // and video codecs: dolby, hevc, avc + const isCompatible = (mediaOption1, mediaOption2) => { + let isVideoCompatible = true; + if (mediaOption1.videoCodec && mediaOption2.videoCodec) { + isVideoCompatible = MediaUtil.isCompatibleVideoCodec(mediaOption1.videoCodec, mediaOption2.videoCodec); + } + let isVideoRangeSame = false; + if (mediaOption1.videoRange && mediaOption2.videoRange) { + isVideoRangeSame = mediaOption1.videoRange == mediaOption2.videoRange; + } + else if (!mediaOption1.videoRange && !mediaOption2.videoRange) { + isVideoRangeSame = true; + } + let isAudioCompatible = true; + if (mediaOption1.audioCodec && mediaOption2.audioCodec) { + // Both have valid audio codecs, check with MediaUtil.isAudioCompatible + isAudioCompatible = MediaUtil.isCompatibleAudioCodec(mediaOption1.audioCodec, mediaOption2.audioCodec); + } + return isVideoCompatible && isVideoRangeSame && isAudioCompatible; + }; + function filterMediaOptionsBasedOnFirstMediaOptions(mediaOptions, firstMediaOptionInfo) { + // Filter out again according to starting level, and save audioGroups of filtered levels if present + return mediaOptions.reduce((prev, cur) => { + const validMediaOption = isCompatible(firstMediaOptionInfo, cur); + if (validMediaOption) { + const audioGroup = cur.audioGroupId; + if (audioGroup) { + prev.audioGroups.add(audioGroup); + } + prev.mediaOptions.add(cur); + } + const subtitleGroup = cur.subtitleGroupId; + if (subtitleGroup) { + prev.subtitleGroups.add(subtitleGroup); + } + const closedCaptionGroup = cur.closedcaption; + if (closedCaptionGroup) { + prev.closedCaptionGroups.add(closedCaptionGroup); + } + return prev; + }, { mediaOptions: new Set(), audioGroups: new Set(), subtitleGroups: new Set(), closedCaptionGroups: new Set() }); + } + function chooseFirstMediaOptionBasedOnScore(mediaOptions, bandwidthHistory, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate) { + const ranking = mediaOptions.reduce((prev, cur) => { + if (cur.iframes) { + return prev; + } + let result = prev; + const rank = getScoreRank(cur, bandwidthHistory, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate); + if (!prev || rank.isGreaterThan(prev.bestRank) || (rank.isEqualTo(prev.bestRank) && cur.bandwidth < prev.selected.bandwidth)) { + result = { selected: cur, bestRank: rank }; + } + return result; + }, null); + return ranking.selected; + } + // Determine the highest ranking MediaOption to be used as first MediaOption (breaking ties by prioritizing the higher bitrate if two MediaOptions have the same rank) + function chooseFirstMediaOptionBasedOnRanking(mediaOptions, metrics, bandwidthHistory, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate) { + const ranking = mediaOptions.reduce((prev, cur) => { + if (cur.iframes) { + return prev; + } + let result = prev; + const rank = getMediaOptionRank(cur, metrics, bandwidthHistory, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate); + if (!prev || rank.isGreaterThan(prev.bestRank) || (rank.isEqualTo(prev.bestRank) && cur.bitrate > prev.selected.bitrate)) { + result = { selected: cur, bestRank: rank }; + } + return result; + }, null); + return ranking.selected; + } + // A MediaOption SCORE ranking is determined by a multisort comparison of 2 properties in order of importance: + // (1) within bandwidth cap + // (2) SCORE (determined by the playlist) + function getScoreRank(mediaInfo, bandwidthHistory, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate) { + const score = mediaInfo.score; + const isWithinCap = bandwidthHistory && + adaptiveStartupConfig && + playlistEstimate && + fragEstimate && + !isWithinBandwidthCap(mediaInfo, bandwidthHistory, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate) + ? MatchRanking.INVALID + : MatchRanking.VALID; // If no bandwidthHistory or adaptiveStartupConfig specified, then don't mark the media option as INVALID; + return new MediaOptionRank(isWithinCap, score); + } + // A MediaOption's ranking is determined by a multisort comparison on 8 properties in order of importance: + // (1) validMetrics (between 2-5 mbps, height between 480-720p) + // (2) videoRangeRank (PQ/HLG/SDR) + // (3) videoRank (video codec rank) + // (4) audioChannelRank (if available and supported, then a 5.1 ac-3 tier takes precedence over stereo ec-3 tier) + // (5) audioCodecRank (audio codec rank) + // (6) within bandwidth cap + // (7) preferred max bitrate (less than 3 mbps) + // (8) MediaOption's height + function getMediaOptionRank(mediaInfo, firstMediaOptionSelectionMetrics, bandwidthHistory, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate) { + const validMetrics = hasValidMetrics(mediaInfo.bitrate, mediaInfo.height, firstMediaOptionSelectionMetrics) ? MatchRanking.VALID : MatchRanking.INVALID; + const videoRangeRank = getVideoRangeRanking(mediaInfo.videoRange); + const { videoCodecRank, audioCodecRank } = getAudioVideoCodecRanks(mediaInfo); + const lessThanMaxPreferredBitrate = mediaInfo.bitrate < firstMediaOptionSelectionMetrics.maxPreferredBitrate ? MatchRanking.VALID : MatchRanking.INVALID; + const DEFAULT_AUDIO_CHANNEL_COUNT = 1; + const audioChannelCount = mediaInfo.audioChannelCount || DEFAULT_AUDIO_CHANNEL_COUNT; // use the channel count for ranking; + const isWithinCap = bandwidthHistory && + adaptiveStartupConfig && + playlistEstimate && + fragEstimate && + !isWithinBandwidthCap(mediaInfo, bandwidthHistory, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate) + ? MatchRanking.INVALID + : MatchRanking.VALID; // If no bandwidthHistory or adaptiveStartupConfig specified, then don't mark the MediaOption as INVALID + return new MediaOptionRank(validMetrics, videoRangeRank, videoCodecRank, audioChannelCount, audioCodecRank, isWithinCap, lessThanMaxPreferredBitrate, mediaInfo.height); + } + function isWithinBandwidthCap(mediaInfo, bandwidthHistory, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate) { + const { targetDuration, targetStartupMs } = adaptiveStartupConfig; + const { avgPlaylistLoadTimeMs } = playlistEstimate; + const { avgParseTimeMs } = fragEstimate; + const { avgBufferCreateMs, avgInitFragAppendMs, avgDataFragAppendMs } = bufferEstimate; + const avgBufferTimeMs = avgInitFragAppendMs + avgDataFragAppendMs; + const { avgBandwidth, avgLatencyMs } = bandwidthHistory; + const fragCount = 1; // Math.ceil(targetDuration / maxDuration); + return (mediaInfo.bandwidth <= avgBandwidth && // has sufficient bandwidth + (((mediaInfo.avgBandwidth || mediaInfo.bandwidth) * targetDuration) / avgBandwidth) * 1000 + + avgPlaylistLoadTimeMs + + avgBufferCreateMs + + (avgLatencyMs + avgParseTimeMs + avgBufferTimeMs) * fragCount <= + targetStartupMs); // can be loaded within targetStartupMs + } + function onlyIFrames(mediaOptions) { + return mediaOptions.every((opt) => opt.iframes); + } + function hasValidMetrics(bitrate, height, metrics) { + const inRange = (input, minVal, maxVal) => (input - minVal) * (input - maxVal) <= 0; + return inRange(bitrate, metrics.minValidBitrate, metrics.maxValidBitrate) && inRange(height, metrics.minValidHeight, metrics.maxValidHeight); + } + /** + * Choose first MediaOption from MediaOption array + * + * @param mediaOptions Set of MediaOptions to choose from + * @param hasScoreAvailable If SCORE metric is available + * @param metrics Metrics to use for selection + * @param bandwidthHistory Bandwidth history + * @param adaptiveStartupConfig Override configs + */ + function chooseFirstMediaOption(mediaOptions, metrics, hasScoreAvailable, logger, bandwidthHistory, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate) { + let firstMediaOption; + if (!mediaOptions || mediaOptions.length < 1 || onlyIFrames(mediaOptions)) { + logger.warn('no non-iframe media option found'); + return; + } + if (hasScoreAvailable) { + firstMediaOption = chooseFirstMediaOptionBasedOnScore(mediaOptions, bandwidthHistory, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate); + } + else { + firstMediaOption = chooseFirstMediaOptionBasedOnRanking(mediaOptions, metrics, bandwidthHistory, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate); + } + if (!firstMediaOption) { + logger.warn('no valid first media option found'); + } + return firstMediaOption; + } + function filterRegularLevelsBasedOnCapOn1080p(levels) { + // standard 1080p resolution with a slight tolerance of 1.2 + const PIXELS_CAP = 2488320; + const capedLevels = levels.filter((levelInfo) => !levelInfo.iframes && (!levelInfo.width || !levelInfo.height || levelInfo.width * levelInfo.height <= PIXELS_CAP)); + return capedLevels; + } + + /* + * simple ABR level switcher + * - compute next level based on last fragment bw heuristics + * - implement an abandon rules triggered if we have less than 2 frag buffered and if computed bw shows that we risk buffer stalling + * + * 2018 Apple Inc. All rights reserved. + */ + const abrLogName = { name: 'abr' }; + function getMaxStarvationDelaySec(fragDuration, maxBufStarvationSec) { + return isFiniteNumber(fragDuration) ? Math.min(fragDuration, maxBufStarvationSec) : maxBufStarvationSec; + } + function instantBwTooSlow(avgBw, abrStatus, bwStatus) { + const instantBw = bwStatus.instantBw; + return abrStatus.fragDownloadSlow || abrStatus.fragDownloadTooSlow || (isFiniteNumber(instantBw) && instantBw < avgBw); + } + function gotSlowMedia(abrStatus) { + return abrStatus.fragDownloadSlow || abrStatus.fragDownloadTooSlow; + } + function hasReliableBandwidthEstimate(bandwidthEstimate) { + return isFiniteNumber(bandwidthEstimate === null || bandwidthEstimate === void 0 ? void 0 : bandwidthEstimate.avgBandwidth); + } + function nextAutoMediaOption(abrConfig, rootPlaylistQuery, rootPlaylistService, mediaLibraryQuery, mediaElementQuery, statsQuery, logger) { + let defaultAutoVariantOption = rootPlaylistQuery.nextMaxAutoOptionId; + // In 2.0 arch, There was check to see if the nextMaxAutoOptionId was + // in penalty box. Don't see a possibility of that, here in 2.1 as + // the nexMaxAutoOptionId is set from the preferrdList. + if (defaultAutoVariantOption !== NoMediaOption.mediaOptionId && !hasReliableBandwidthEstimate(statsQuery.getBandwidthEstimate())) { + if (mediaElementQuery.isIframeRate) { + // no group matching for iframe mode + return { variantMediaOption: defaultAutoVariantOption, holdOffDuration: 0, lowestCandidate: null }; + } + else { + // defaultAutoVariantOption (rootPlaylistQuery.nextMaxAutoOptionId) gets set in likelyToStall. + // Even though it has matching altAudio and subtitle mediaOptions at that time. + // It might not at this moment due to penalized alternates. + // getBestMediaOptionTupleFromVariantAndPersistentId will look for a + // fallback variant that has matching alternates. + // If it comes up short, then continue to use the current variant and alternates. + const variantMediaOption = rootPlaylistQuery.variantMediaOptionById(defaultAutoVariantOption); + const subtitleAltOption = rootPlaylistQuery.enabledAlternateMediaOptionByType(MediaOptionType.Subtitle); + const audioAltOption = rootPlaylistQuery.enabledAlternateMediaOptionByType(MediaOptionType.AltAudio); + const audioPersistentId = audioAltOption === null || audioAltOption === void 0 ? void 0 : audioAltOption.persistentID; + const subtitlePersistentId = subtitleAltOption === null || subtitleAltOption === void 0 ? void 0 : subtitleAltOption.persistentID; + const sdrOnly = !rootPlaylistQuery.preferHDR; + const mediaOptionTuple = rootPlaylistService.getBestMediaOptionTupleFromVariantAndPersistentId(rootPlaylistQuery, variantMediaOption, audioPersistentId, subtitlePersistentId, undefined, [], sdrOnly, false, false); + logger.info(`capped at sdrOnly ${sdrOnly} ${JSON.stringify(defaultAutoVariantOption)} with matching alternates: ${JSON.stringify(mediaOptionTuple)}`); + if (rootPlaylistQuery.isValidMediaOptionTuple(mediaOptionTuple)) { + defaultAutoVariantOption = mediaOptionTuple[MediaOptionType.Variant].mediaOptionId; + const alternates = { + altAudio: mediaOptionTuple[MediaOptionType.AltAudio], + subtitle: mediaOptionTuple[MediaOptionType.Subtitle], + }; + return { variantMediaOption: defaultAutoVariantOption, holdOffDuration: 0, alternates, lowestCandidate: null }; + } + else { + logger.info(`No fallback variant with matching alternates for (capped) level ${defaultAutoVariantOption}: use currentVariant instead.`); + } + const currentVariant = rootPlaylistQuery.enabledMediaOptionKeys[MediaOptionType.Variant]; + return { variantMediaOption: currentVariant.mediaOptionId, holdOffDuration: 0, lowestCandidate: null }; // by using currentVariant, switch will be skipped. + } + } + return nextABRAutoMediaOption(abrConfig, rootPlaylistQuery, mediaLibraryQuery, mediaElementQuery, statsQuery, logger); + } + function nextABRAutoMediaOption(abrConfig, rootPlaylistQuery, mediaLibraryQuery, mediaElementQuery, statsQuery, logger) { + const loggerChild = logger.child({ name: 'abr' }); + const iframeMode = mediaElementQuery.isIframeRate; + const enabledVariantOptionId = rootPlaylistQuery.enabledMediaOptionIdByType(MediaOptionType.Variant); + const autoVariantOption = findNextABRAutoVariantOptionInMode(iframeMode, abrConfig, rootPlaylistQuery, mediaLibraryQuery, mediaElementQuery, statsQuery, loggerChild); + loggerChild.info(`nextABRAutoMediaOption ${enabledVariantOptionId}->${autoVariantOption.variantMediaOption}`); + return autoVariantOption; + } + function getABRPlaybackRate(mediaElementQuery) { + // playbackRate is the absolute value of the playback rate; if v.playbackRate is 0, we use 1 to load as + // if we're playing back at the normal rate. + return mediaElementQuery.playbackRate !== 0 ? Math.abs(mediaElementQuery.playbackRate) : 1; + } + // Scaled buffer ahead in seconds for video buffer + function getBufferAheadSec(mediaElementQuery, maxBufferHole) { + return mediaElementQuery.getCurrentWaterLevelByType(SourceBufferType.Variant, maxBufferHole) / getABRPlaybackRate(mediaElementQuery); + } + function findLowestValidVariantIndex(minAutoVariantIndex, availableVariantOptions, iframeMode, rootPlaylistQuery) { + if (iframeMode) { + return minAutoVariantIndex; + } + let lowestValidVariantId = -1; + if (minAutoVariantIndex < 0) { + return lowestValidVariantId; + } + for (let i = minAutoVariantIndex; i < availableVariantOptions.length; ++i) { + const variantMediaOption = availableVariantOptions[i]; + const alternates = getMatchingAlternates(variantMediaOption, rootPlaylistQuery); + if (alternates.altAudio && alternates.subtitle) { + lowestValidVariantId = i; + break; + } + } + return lowestValidVariantId; + } + function findNextABRAutoVariantOptionInMode(iframeMode, abrConfig, rootPlaylistQuery, curOptionQuery, mediaElementQuery, statsQuery, logger) { + const variantOptions = rootPlaylistQuery.preferredMediaOptions[MediaOptionType.Variant].filter((variantOption) => variantOption.iframes === iframeMode); + if (!variantOptions.length) { + return { variantMediaOption: NoMediaOption.mediaOptionId, holdOffDuration: 0, lowestCandidate: null }; + } + // Find minAutoVariant if it was set because of highBWTrigger + let minAutoVariantOption = 0; + const minAutoVariantOptionId = rootPlaylistQuery.nextMinAutoOptionId; + if (minAutoVariantOptionId !== NoMediaOption.mediaOptionId) { + const found = variantOptions.findIndex((variantOption) => variantOption.mediaOptionId === minAutoVariantOptionId); + if (found >= 0) { + minAutoVariantOption = found; + logger.info(`minAutoVariantOptionId set=${minAutoVariantOptionId}, minAutoVariantOption=${minAutoVariantOption}/${variantOptions.length}`); + } + } + // Ensure the minAutoVariant is valid + minAutoVariantOption = findLowestValidVariantIndex(minAutoVariantOption, variantOptions, iframeMode, rootPlaylistQuery); + if (minAutoVariantOption < 0) { + return { variantMediaOption: NoMediaOption.mediaOptionId, holdOffDuration: 0, lowestCandidate: null }; + } + let maxAutoVariantOption = variantOptions.length - 1; + // if forced auto level has been defined, use it to cap ABR computed quality level + const maxAutoVariantOptionId = rootPlaylistQuery.nextMaxAutoOptionId; + if (maxAutoVariantOptionId !== NoMediaOption.mediaOptionId) { + const found = variantOptions.findIndex((variantOption) => variantOption.mediaOptionId === maxAutoVariantOptionId); + if (found >= 0) { + maxAutoVariantOption = found; + logger.info(`maxAutoVariantOptionId set=${maxAutoVariantOptionId}, maxAutoVariantOption=${maxAutoVariantOption}/${variantOptions.length}`); + } + } + const currentVariant = rootPlaylistQuery.variantMediaOptionById(curOptionQuery.mediaOptionId); + const currentVariantOptionDetails = curOptionQuery.mediaOptionDetails; // Get info even if in penalty box TODO check here + const bufferedDuration = (currentVariant === null || currentVariant === void 0 ? void 0 : currentVariant.iframes) !== iframeMode ? 0 : getBufferAheadSec(mediaElementQuery, abrConfig.maxBufferHole); + let bestLevelHeuristics, currentFragDuration; + if (!iframeMode) { + currentFragDuration = currentVariantOptionDetails ? currentVariantOptionDetails.targetduration : 0; + // TODO where is the current Frag and get duration, be careful not to use init segment + } + else { + const desiredIframeFPS = abrConfig.desiredIframeFPS; + currentFragDuration = currentVariantOptionDetails ? currentVariantOptionDetails.targetduration / desiredIframeFPS : 0; + // lower limit on iframe duration - dense tracks likely have a target duration that would achieve more than 8 fps if we displayed everything + currentFragDuration = Math.max(1 / desiredIframeFPS, currentFragDuration); + } + const bwUpFactor = abrConfig.abrBandWidthUpFactor; + const bwFactor = abrConfig.abrBandWidthFactor; + // First, look to see if we can find a level matching with our avg bandwidth AND that could also guarantee no rebuffering at all + bestLevelHeuristics = findBestVariantOption(variantOptions, currentFragDuration, minAutoVariantOption, maxAutoVariantOption, bufferedDuration, iframeMode, statsQuery.getCombinedEstimate(), statsQuery.bandwidthStatus, bwFactor, bwUpFactor, abrConfig, rootPlaylistQuery, curOptionQuery, mediaElementQuery, logger, true); + if (bestLevelHeuristics.variantMediaOption === NoMediaOption.mediaOptionId) { + logger.trace('rebuffering expected to happen, lets try to find a quality level minimizing the rebuffering'); + // not possible to get rid of rebuffering ... let's try to find + // level that will guarantee less than maxStarvationDelay of + // rebuffering if no matching level found, logic will return 0 + const maxStarvationDelay = getMaxStarvationDelaySec(currentFragDuration, abrConfig.maxStarvationDelay); + bestLevelHeuristics = findBestVariantOption(variantOptions, currentFragDuration, minAutoVariantOption, maxAutoVariantOption, bufferedDuration + maxStarvationDelay, iframeMode, statsQuery.getCombinedEstimate(), statsQuery.bandwidthStatus, bwFactor, bwUpFactor, abrConfig, rootPlaylistQuery, curOptionQuery, mediaElementQuery, logger, true); + if (bestLevelHeuristics.variantMediaOption === NoMediaOption.mediaOptionId && minAutoVariantOption >= 0) { + bestLevelHeuristics.variantMediaOption = variantOptions[minAutoVariantOption].mediaOptionId; + bestLevelHeuristics.alternates = iframeMode ? null : getMatchingAlternates(variantOptions[minAutoVariantOption], rootPlaylistQuery); + } + } + logger.debug(`[ findNextABRAutoVariantOptionInMode] Returning ${JSON.stringify(bestLevelHeuristics)}`); + return bestLevelHeuristics; + } + function getMatchingAlternates(variant, rootPlaylistQuery) { + const enabledMediaOptions = rootPlaylistQuery.enabledMediaOptionKeys; + const altAudioKey = enabledMediaOptions[MediaOptionType.AltAudio]; + const altAudio = isEnabledMediaOption(altAudioKey) ? rootPlaylistQuery.mediaOptionListQueries[MediaOptionType.AltAudio].getMatchingAlternate(altAudioKey.mediaOptionId, variant) : NoMediaOption; + const subtitleKey = enabledMediaOptions[MediaOptionType.Subtitle]; + const subtitle = isEnabledMediaOption(subtitleKey) ? rootPlaylistQuery.mediaOptionListQueries[MediaOptionType.Subtitle].getMatchingAlternate(subtitleKey.mediaOptionId, variant) : NoMediaOption; + return { altAudio, subtitle }; + } + const minItemsInBandwidthAverage = 4; + const shortTermBandwidthFactor = 1.8; + /** + * @param variantOptionList The filtered list of candidate variants + */ + function findBestVariantOption(variantOptionList, currentFragDuration, minAutoVariant, maxAutoVariant, maxFetchDuration, iframeMode, estimate, bwStatus, bwFactor, bwUpFactor, abrConfig, rootPlaylistQuery, mediaLibraryQuery, mediaElementQuery, logger, dumpParams = false) { + if (abrConfig.abrBandwidthEstimator !== 'bandwidth-history-controller') { + logger.warn(`Unsupported configuration: ${abrConfig.abrBandwidthEstimator} for ABR bandwidth estimator`); + } + const bandwidthSamples = bwStatus.bandwidthSampleCount; + const abrStatus = rootPlaylistQuery.abrStatus; + const maxBufferSize = mediaElementQuery.maxBufferSize; + const minTargetDurations = abrConfig.minTargetDurations || 1; + const mediaOptionListInfo = rootPlaylistQuery.mediaOptionListQueries[MediaOptionType.Variant].mediaOptionListInfo; + const hasScore = mediaOptionListInfo.hasScore; + if (!variantOptionList.length) { + return { variantMediaOption: NoMediaOption.mediaOptionId, holdOffDuration: -1, lowestCandidate: null }; + } + const enabledVariantOptionId = rootPlaylistQuery.enabledMediaOptionIdByType(MediaOptionType.Variant); + const enabledVariantOption = rootPlaylistQuery.variantMediaOptionById(enabledVariantOptionId); + const enabledVariantOptionIsValid = variantOptionList.find((variantOption) => variantOption.mediaOptionId === enabledVariantOptionId) != null; + const isSameMode = enabledVariantOption && enabledVariantOption.iframes === iframeMode; + const currentScore = enabledVariantOption && isSameMode ? enabledVariantOption.score : undefined; + const currentFrameRate = enabledVariantOption && isSameMode ? enabledVariantOption.frameRate : undefined; + const currentHeight = enabledVariantOption && isSameMode ? enabledVariantOption.height : undefined; + const bufferedDuration = isSameMode ? getBufferAheadSec(mediaElementQuery, abrConfig.maxBufferHole) : 0; + // Sanity capping + maxAutoVariant = Math.max(0, Math.min(variantOptionList.length - 1, maxAutoVariant)); + minAutoVariant = Math.max(0, Math.min(variantOptionList.length - 1, minAutoVariant)); + if (dumpParams) { + logger.debug(`[abr] findBestLevel params=${stringifyWithPrecision({ + enabledVariantOptionId, + currentFragDuration, + estimate, + minAutoVariant, + maxAutoVariant, + bwFactor, + bwUpFactor, + maxFetchDuration, + iframeMode, + maxBufferSize, + bufferedDuration, + currentScore, + currentFrameRate, + currentHeight, + })}`); + } + const mediaOptionDetailsEntityRecord = mediaLibraryQuery.mediaOptionDetailsEntityRecord; + const adjustedBwObj = getAdjustedBW(estimate.avgBandwidth, bwUpFactor, bwFactor, bandwidthSamples, logger); + let lowestCandidate; + for (let i = maxAutoVariant; i >= minAutoVariant; i--) { + const variantOption = variantOptionList[i]; + let variantOptionId = variantOption.mediaOptionId; + const variantOptionScore = variantOption.score; + const variantOptionDetails = mediaOptionDetailsEntityRecord && mediaOptionDetailsEntityRecord[variantOptionId] ? mediaOptionDetailsEntityRecord[variantOptionId].mediaOptionDetails : undefined; + const lastUpdateMs = mediaOptionDetailsEntityRecord && mediaOptionDetailsEntityRecord[variantOptionId] ? mediaOptionDetailsEntityRecord[variantOptionId].lastUpdateMillis : null; + const avgDuration = variantOptionDetails ? variantOptionDetails.totalduration / variantOptionDetails.fragments.length : currentFragDuration; + const isUpSwitch = enabledVariantOption != null && variantOption.bitrate > enabledVariantOption.bitrate; + const isDownSwitch = enabledVariantOption != null && variantOption.bitrate < enabledVariantOption.bitrate; + // don't switch up to a lower frame rate or switch down to a higher frame rate + const validFrameRate = !(currentFrameRate != null && ((isUpSwitch && variantOption.frameRate < currentFrameRate) || (isDownSwitch && variantOption.frameRate > currentFrameRate))); + // pick only ascending levels that have greater than/equal height than the current level + const validHeight = !(isUpSwitch && currentHeight != null && currentHeight > variantOption.height); + /* + Note: AMP playlists are set up in such a way that + - fragments are served from a primary host (e.g. Apple CDN, vod-ap1-aoc.tv.apple.com) and alternate host (e.g. Akamai, vod-ak-aoc.tv.apple.com) + - for both of these, the playlists are served from a single host (e.g. play.itunes.apple.com) + - the primary and alternate hosts are identified by a bitrate difference of 1 bit (primary bitrate > alternate bitrate by 1) + */ + // Don't use same bitrate on alternate CDN for downswitch. + const validCDN = !(isDownSwitch && (variantOption.bitrate === enabledVariantOption.bitrate - 1 || variantOption.bitrate === enabledVariantOption.bitrate + 1)); + // if SCORE attribute is available, pick ascending levels that have greater/equal SCORE than the current level + // if SCORE attributes are the same, ignore levels that have higher bitrate than current selected level + const validScore = !(hasScore && + isUpSwitch && + currentScore != null && + (variantOptionScore < currentScore || (variantOptionScore === currentScore && enabledVariantOption && variantOption.bitrate >= enabledVariantOption.bitrate))); + const validMode = variantOption.iframes === iframeMode; + if (!isFiniteNumber(avgDuration) || !validMode || !validFrameRate || !validHeight || !validCDN || !validScore) { + logger.debug(`variant ${variantOptionId} ineligible ${stringifyWithPrecision({ avgDuration, validMode, validFrameRate, validHeight, validCDN, validScore })}`); + continue; + } + const { adjustedbw, bitrate, fetchDuration, rejectLevelDueToPeakBW, canFitMultipleSegments, requireAlternates, alternates } = getVariantOptionMetrics(variantOption, variantOptionDetails, bufferedDuration, minTargetDurations, isUpSwitch, estimate, adjustedBwObj, currentFragDuration, maxBufferSize, lastUpdateMs, iframeMode, rootPlaylistQuery, logger); + if (dumpParams) { + logger.debug(`getVariantOptionMetrics in=${stringifyWithPrecision({ + bufferedDuration, + minTargetDurations, + isUpSwitch, + estimate, + bwUpFactor, + bwFactor, + currentFragDuration, + maxBufferSize, + bandwidthSamples, + lastUpdateMs, + iframeMode, + })} out=${stringifyWithPrecision({ adjustedbw, bitrate, fetchDuration, rejectLevelDueToPeakBW, canFitMultipleSegments, requireAlternates })} alternates=${JSON.stringify(alternates)}`); + } + // lowestCandidate is also guaranteed to be delivering fragments from same host. + if (requireAlternates && Boolean(alternates)) { + lowestCandidate = variantOptionId; + } + // if adjusted bw is greater than level bitrate AND + if (adjustedbw > bitrate && + canFitMultipleSegments && + !rejectLevelDueToPeakBW && + (!requireAlternates || Boolean(alternates)) && + // fragment fetchDuration unknown OR fragment fetchDuration less than max allowed fetch duration, then this level matches + (iframeMode || !fetchDuration || fetchDuration < maxFetchDuration)) { + // as we are looping from highest to lowest, this will return the best achievable quality level + if (instantBwTooSlow(adjustedbw, abrStatus, bwStatus) && enabledVariantOptionIsValid && isSameMode) { + // low inst bw and a switchdown imminent, only switch up if we have enough buffer + if (bufferedDuration <= 2 * avgDuration && isUpSwitch) { + // do not switch up + logger.info(`Last fragment too slow, not switching up instantBw=${bwStatus.instantBw}, ${enabledVariantOptionId}->${variantOptionId}, ${stringifyWithPrecision({ + bufferedDuration, + avgDuration, + isUpSwitch, + })}`); + variantOptionId = enabledVariantOptionId; + } + else if (isUpSwitch && bwUpFactor * bwStatus.instantBw < bitrate) { + // instantBw might be too slow for this level, but we could upswitch to a lower level + continue; + } + } + return { variantMediaOption: variantOptionId, holdOffDuration: fetchDuration, alternates: alternates, lowestCandidate: lowestCandidate }; + } + } + // not enough time budget even with quality level 0 ... rebuffering might happen + return { variantMediaOption: NoMediaOption.mediaOptionId, holdOffDuration: -1, lowestCandidate: lowestCandidate }; + } + // Get metrics used for switching + function getVariantOptionMetrics(variantOption, variantOptionDetails, bufferedDuration, minTargetDurations, isUpSwitch, estimate, adjustedBwObj, defaultFragDuration, maxBufferSize, lastUpdateMs, iframeMode, rootPlaylistQuery, logger) { + const adjustedbw = isUpSwitch ? adjustedBwObj.bwUp : adjustedBwObj.bwDown; + const bitrate = variantOption.bitrate; + // const live = levelDetails != null && levelDetails.live; + const avgDuration = variantOptionDetails ? variantOptionDetails.totalduration / variantOptionDetails.fragments.length : defaultFragDuration; + const playlistFetchMs = isFiniteNumber(estimate.avgPlaylistLoadTimeMs) ? estimate.avgPlaylistLoadTimeMs : estimate.avgLatencyMs; + const fetchDuration = calcFetchDuration(variantOption, variantOptionDetails, defaultFragDuration, adjustedbw, playlistFetchMs, lastUpdateMs, logger); + const peakBandwidth = variantOption.bandwidth; + // have atleast 2 target-durations buffered to switch if peak exceeds measured bandwidth + const rejectLevelDueToPeakBW = adjustedbw > bitrate && adjustedbw < peakBandwidth && bufferedDuration <= 2 * avgDuration; + const peakSegSizeBits = (peakBandwidth || bitrate || 0) * ((variantOptionDetails === null || variantOptionDetails === void 0 ? void 0 : variantOptionDetails.targetduration) || avgDuration); + // can fit at least this.minTargetDurations * targetDuration seconds into the buffer + const canFitMultipleSegments = (minTargetDurations * peakSegSizeBits) / 8 <= maxBufferSize; + // must have matching alternates + let alternates = null; + const requireAlternates = !iframeMode; + if (requireAlternates) { + alternates = getMatchingAlternates(variantOption, rootPlaylistQuery); + logger.debug(`${variantOption.mediaOptionId} matched alternate audio ${JSON.stringify(alternates.altAudio)} subtitle ${JSON.stringify(alternates.subtitle)}`); + if (!alternates.altAudio || !alternates.subtitle) { + logger.info(`no matching alternates ${alternates.altAudio} or ${alternates.subtitle}, skipping ${variantOption.mediaOptionId}`); + alternates = null; + } + } + return { + adjustedbw, + bitrate, + fetchDuration, + rejectLevelDueToPeakBW, + canFitMultipleSegments, + requireAlternates, + alternates, + }; + } + function getAdjustedBW(bw, switchUpFactor, switchDownFactor, numBandwidthSamples, logger) { + let bwUp; + let bwDown; + if (numBandwidthSamples >= minItemsInBandwidthAverage) { + bwUp = bw * switchUpFactor; + bwDown = bw * switchDownFactor; + } + else { + bwUp = bwDown = bw / shortTermBandwidthFactor; + } + logger.trace(`[getAdjustedBW] numSamples: ${numBandwidthSamples}, minSamples: ${minItemsInBandwidthAverage} - returning up: ${bwUp}, down: ${bwDown}`); + return { bwUp, bwDown }; + } + function calcFetchDuration(variantOption, variantOptionDetails, defaultFragDuration, bw, playlistFetchMs, lastUpdateMs, logger) { + const bitrate = variantOption.bitrate; + const avgDuration = variantOptionDetails ? variantOptionDetails.totalduration / variantOptionDetails.fragments.length : defaultFragDuration; + const avgSegSizeBits = bitrate * avgDuration; + let fetchDuration = avgSegSizeBits / bw; + // if live and !ptsKnown or outOfDate + if ((variantOptionDetails === null || variantOptionDetails === void 0 ? void 0 : variantOptionDetails.liveOrEvent) && (!variantOptionDetails.ptsKnown || likelyOutOfDate(variantOptionDetails.totalduration, playlistFetchMs, lastUpdateMs))) { + // live fetch duration could be up to two segments if it doesn't have time info + // First segment could be the wrong segment if we don't have PTS since we choose the middle frag + logger.debug(`PTS unknown / expired, using 2x fetchDuration for ${variantOption.mediaOptionId}`); + fetchDuration *= 2; + } + // if this is first update for this option or if live + if (isFiniteNumber(playlistFetchMs) && (!isFiniteNumber(lastUpdateMs) || (variantOptionDetails === null || variantOptionDetails === void 0 ? void 0 : variantOptionDetails.liveOrEvent))) { + fetchDuration += playlistFetchMs / 1000; + } + return fetchDuration; + } + function minSwitchBufferAheadSec(from, to, toDetails, currentFragDuration, abrStatus, statsQuery, abrConfig, lastUpdateMs, logger) { + const forcedAutoOptionId = abrStatus.nextMaxAutoOptionId; + // in case next auto level has been forced, and bw not available or not reliable, return false + if (forcedAutoOptionId !== NoMediaOption.mediaOptionId && !hasReliableBandwidthEstimate(statsQuery.getBandwidthEstimate())) { + return Number.POSITIVE_INFINITY; + } + const bw = getAdjustedBW(statsQuery.getBandwidthEstimate().avgBandwidth, abrConfig.abrBandWidthUpFactor, abrConfig.abrBandWidthFactor, statsQuery.bandwidthStatus.bandwidthSampleCount, logger); + const playlistFetchMs = isFiniteNumber(statsQuery.getPlaylistEstimate().avgPlaylistLoadTimeMs) + ? statsQuery.getPlaylistEstimate().avgPlaylistLoadTimeMs + : statsQuery.getBandwidthEstimate().avgLatencyMs; + // How much buffer ahead required to switch + // duration from playhead pos that is necessary to stay at this level + const isUpSwitch = to.bitrate > from.bitrate; + const bwValue = isUpSwitch ? bw.bwUp : bw.bwDown; + if ((toDetails === null || toDetails === void 0 ? void 0 : toDetails.liveOrEvent) && (!toDetails.ptsKnown || likelyOutOfDate(toDetails.totalduration, playlistFetchMs, lastUpdateMs))) { + // Don't allow flush if we won't have overlapping window + return Number.POSITIVE_INFINITY; + } + return calcFetchDuration(to, toDetails, currentFragDuration, bwValue, playlistFetchMs, lastUpdateMs, logger); + } + function getLowestSuperiorBW(mediaOptionId, variantOptions) { + let lowestSuperiorBW = Infinity; + if (mediaOptionId === NoMediaOption.mediaOptionId) { + return lowestSuperiorBW; + } + const givenMediaOption = variantOptions.find((mediaOption) => mediaOption.mediaOptionId === mediaOptionId); + if (!givenMediaOption) { + return Infinity; + } + const iframeMode = givenMediaOption.iframes; + const bitrate = givenMediaOption.bitrate; + const frameRate = givenMediaOption.frameRate; + const nextMediaOption = variantOptions.find((mediaOption) => mediaOption.iframes === iframeMode && (frameRate === undefined || mediaOption.frameRate >= frameRate) && mediaOption.bitrate > bitrate); + if (nextMediaOption) { + lowestSuperiorBW = nextMediaOption.bitrate; + } + return lowestSuperiorBW; + } + /** + * Returns whether it's likely that the refreshed level will end up with a non-overlapping playlist (causing ptsKnown == false) + * @param totalDurationSecs Details about the level + * @param playlistFetchMs time in MS to get playlist refresh + * @param lastUpdateMillis timeStamp of the last update + */ + function likelyOutOfDate(totalDurationSecs, playlistFetchMs, lastUpdateMillis) { + playlistFetchMs = isFiniteNumber(playlistFetchMs) ? playlistFetchMs : 0; + return !isFiniteNumber(lastUpdateMillis) || performance.now() - lastUpdateMillis + playlistFetchMs > totalDurationSecs * 1000; + } + /** + * Choose option to resume at when coming out of trickplay + */ + function getTrickPlayMaxResumeOption(preIframeOptionId, config, logger, rootPlaylistQuery, statsQuery) { + const vQuery = rootPlaylistQuery.mediaOptionListQueries[MediaOptionType.Variant]; + const vOptions = vQuery.preferredMediaOptionList; + const targetStartupMs = config.targetStartupMs; + const playlistEstimate = { + // statsQuery.playlistEstimate + avgPlaylistParseTimeMs: 0, + avgPlaylistLoadTimeMs: 0, + }; // Do not use max values for this estimate + const fragEstimate = statsQuery.getFragEstimate(); + const bufferEstimate = { + avgBufferCreateMs: 0, + avgInitFragAppendMs: 0, + avgDataFragAppendMs: 0, + }; + const fragDuration = fragEstimate.maxDurationSec; + // Use 0 latency since it is already wrapped into bw estimate(See BandwidthHistoryController.record). Change back if we use tload - tfirst instead + const bw = { + avgLatencyMs: 0, + avgBandwidth: statsQuery.getBandwidthEstimate().avgBandwidth, + }; + const startConfig = { + targetDuration: fragDuration, + targetStartupMs: targetStartupMs, + metricsOverride: { + maxValidHeight: 0, + maxValidBitrate: 0, + maxPreferredBitrate: 0, + }, + }; + logger.info(`trickplay resume selection params=${stringifyWithPrecision({ startConfig, bw, playlistEstimate, fragEstimate, bufferEstimate })}`); + const regOptions = filterRegularLevelsBasedOnCapOn1080p(vOptions); + let resumeId = preIframeOptionId; + if (regOptions.length > 0) { + resumeId = regOptions[0].mediaOptionId; + } + const candidates = regOptions.filter((option) => isWithinBandwidthCap(option, bw, startConfig, playlistEstimate, fragEstimate, bufferEstimate)); + logger.debug(`Picking resume level from validLevels/regLevels/resumeLevels ${vOptions.length}/${regOptions.length}/${candidates.length}`); + if (candidates.length > 0) { + resumeId = candidates[candidates.length - 1].mediaOptionId; + } + logger.info(`trickplay start/max resume:${preIframeOptionId}/${resumeId}`); + return resumeId; + } + function fragAbrStatusDidChange(a, b) { + return (a === null || a === void 0 ? void 0 : a.fragDownloadSlow) === (b === null || b === void 0 ? void 0 : b.fragDownloadSlow) && a.fragDownloadTooSlow === (b === null || b === void 0 ? void 0 : b.fragDownloadTooSlow); + } + /** + * @returns Returns whether we should abort the fragment and also updates AbrStatus + */ + function shouldAbortFrag(context) { + // Slow fragment download: + const { mediaSink, rootPlaylistQuery, rootPlaylistService } = context; + const mediaQuery = mediaSink.mediaQuery; + const logger = context.logger.child(abrLogName); + return merge(slowFragmentCheck(rootPlaylistQuery, mediaQuery, logger), likelyToStallCheck(context)).pipe(startWith({ fragDownloadSlow: false, fragDownloadTooSlow: false }), scan((curState, newState) => { + return { + fragDownloadSlow: curState.fragDownloadSlow || newState.fragDownloadSlow, + fragDownloadTooSlow: curState.fragDownloadTooSlow || newState.fragDownloadTooSlow, + }; + }), distinctUntilChanged(fragAbrStatusDidChange), map((status) => { + logger.debug(`abrStatus update=${JSON.stringify(status)}`); + rootPlaylistService.setFragLoadSlow(rootPlaylistQuery.itemId, status); + return false; + }), catchError((err) => { + if (err instanceof FragmentAbortError) { + const wayTooSlow = { fragDownloadSlow: true, fragDownloadTooSlow: true }; + rootPlaylistService.setFragLoadSlow(rootPlaylistQuery.itemId, wayTooSlow); + return of(true); + } + return throwError(err); + })); + } + function slowFragmentCheck(rootPlaylistQuery, mediaQuery, logger) { + return combineQueries([mediaQuery.fellBelowLowWater$, rootPlaylistQuery.getInFlightFragByType$(MediaOptionType.Variant)]).pipe(switchMap((value) => { + const [, inFlight] = value; + if (!hasValidSample(inFlight)) { + return EMPTY; + } + const elapsedMs = performance.now() - inFlight.bwSample.trequest; + const flowDeadline = kFlowMeasurementPeriodMS - elapsedMs; + const durationDeadline = inFlight.duration * 1000 - elapsedMs; + const timers$ = [VOID]; + if (flowDeadline > 0) { + timers$.push(timer(flowDeadline)); + } + if (durationDeadline > 0) { + timers$.push(timer(durationDeadline)); + } + return merge(...timers$).pipe(mapTo(value)); + }), scan((curState, [fellBelowLowWater, inFlight]) => { + const newState = Object.assign({}, curState); + if (fellBelowLowWater) { + newState.fragDownloadSlow = true; + } + return gotSlowFlow(inFlight, newState, rootPlaylistQuery, logger); + }, { fragDownloadSlow: false, fragDownloadTooSlow: false }), startWith({ fragDownloadSlow: false, fragDownloadTooSlow: false }), distinctUntilChanged(fragAbrStatusDidChange)); + } + function hasValidSample(inFlight) { + var _a; + return (inFlight === null || inFlight === void 0 ? void 0 : inFlight.state) === 'loading' && isFiniteNumber((_a = inFlight.bwSample) === null || _a === void 0 ? void 0 : _a.trequest); + } + const kFlowMeasurementPeriodMS = 2000; + const kUnderflowAllowanceMS = 1000; + function gotSlowFlow(inFlight, curStatus, rootQuery, logger) { + let { fragDownloadSlow, fragDownloadTooSlow } = curStatus; + const variant = rootQuery.variantMediaOptionById(inFlight.mediaOptionId); + const bitrate = variant.bitrate; + const stats = inFlight.bwSample; + logger = logger.child(abrLogName); + const totalBytes = stats.total ? stats.total : Math.max(stats.loaded, Math.round((inFlight.duration * bitrate) / 8)), timeSpentMS = performance.now() - stats.tfirst, // time spent downloading the fragment + durationDownloadedMS = (stats.loaded * inFlight.duration * 1000) / totalBytes; + if (timeSpentMS >= kFlowMeasurementPeriodMS && timeSpentMS - durationDownloadedMS >= kUnderflowAllowanceMS) { + if (!fragDownloadSlow) { + logger.warn(`flow indicates low bandwidth, after time/duration behind real time: ${timeSpentMS}/${timeSpentMS - durationDownloadedMS}`); + } + fragDownloadSlow = true; + } + if (timeSpentMS >= inFlight.duration * 1000) { + if (!fragDownloadTooSlow) { + logger.warn(`too much time spent downloading fragment, likely to switch down ${timeSpentMS} > ${inFlight.duration * 1000}`); + } + fragDownloadTooSlow = true; + } + return { fragDownloadSlow, fragDownloadTooSlow }; + } + function likelyToStallCheck(context) { + const mediaQuery = context.mediaSink.mediaQuery; + const { rootPlaylistQuery: rootQuery, config } = context; + return mediaQuery.desiredRate$ + .pipe(switchMap((desiredRate) => { + // Don't emit if about to pause or if we are currently paused + if (desiredRate === 0) { + return EMPTY; + } + return combineQueries([ + rootQuery.getInFlightFragByType$(MediaOptionType.Variant), + rootQuery.mediaOptionListQueries[MediaOptionType.Variant].preferredMediaOptionList$.pipe(map((list) => list.filter(isMatchingIframeLevel.bind(null, isIframeRate(desiredRate))))), + ]); + }), throttleTime(100), switchMap((value) => { + const [inFlight, preferredList] = value; + // Make sure inFlight fragment in correct mode && not the lowest bitrate variant + if (!hasValidSample(inFlight) || preferredList.findIndex((o) => o.mediaOptionId === inFlight.mediaOptionId) <= 0) { + return EMPTY; + } + // Wait some min threshold for stable bitrate calculation then check every 100ms + const now = performance.now(); + const elapsedMs = now - inFlight.bwSample.trequest; + const maxStarvationDelaySec = getMaxStarvationDelaySec(inFlight.duration, config.maxStarvationDelay); + const minThresholdMs = Math.min(maxStarvationDelaySec * 1000, (inFlight.duration * 500) / mediaQuery.playbackRate); + const delayMs = Math.max(0, minThresholdMs - elapsedMs); + return timer(delayMs, 100).pipe(mapTo(value)); + })) + .pipe(scan((curStatus, [inFlight, preferredList]) => { + return likelyToStall(curStatus, inFlight, preferredList, context); + }, { fragDownloadSlow: false, fragDownloadTooSlow: false }), startWith({ fragDownloadSlow: false, fragDownloadTooSlow: false }), distinctUntilChanged(fragAbrStatusDidChange)); + } + /** + * Determine whether we are going to stall based on the remaining time to download the inflight fragment and buffer then switch fragments if needed. + * Can initiate abort of inflight fragment and switch down through limiting the nexMaxAutoOptiondId to either a level that can download in time or the lowest option. + * Alternatively, can just initiate a switch down without abort by limiting nextMaxAutoOptionId to one level down if the current fragment can download quickly enough. + */ + function likelyToStall(curStatus, inFlight, preferredList, // filtered list in the correct mode + context) { + var _a, _b; + let { fragDownloadSlow, fragDownloadTooSlow } = curStatus; + const { config, rootPlaylistService, rootPlaylistQuery: rootQuery, mediaSink, statsService, mediaLibraryService } = context; + const logger = context.logger.child(abrLogName); + const mediaQuery = mediaSink.mediaQuery; + // Prevent switch down if we are not in play state + if (mediaQuery.paused) { + return curStatus; + } + const stats = inFlight.bwSample; + // likelyToStall not required to check until download has started + if (!isFiniteNumber(stats.tfirst)) { + return curStatus; + } + const now = performance.now(); + const elapsedMs = now - stats.trequest; + const maxStarvationDelaySec = getMaxStarvationDelaySec(inFlight.duration, config.maxStarvationDelay); + const mediaOptionType = MediaOptionType.Variant; + const mediaOptionId = inFlight.mediaOptionId; + const curVariant = rootQuery.variantMediaOptionById(mediaOptionId); + const curLibQuery = mediaLibraryService.getQueryForOption(curVariant); + const variantBitrate = curVariant.bitrate; + const instantBw = Math.max(1, (stats.loaded * 8000) / elapsedMs); // instant load rate in bits per second + const totalBytes = isFiniteNumber(stats.total) ? stats.total : Math.max(stats.loaded, Math.round((inFlight.duration * variantBitrate) / 8)); // fragment size in bytes + const remainingTimeSec = ((totalBytes - stats.loaded) * 8) / instantBw; // how much longer until this fragment will be loaded in seconds + const bufferAheadSec = getBufferAheadSec(mediaQuery, config.maxBufferHole); + let maxTimeToLoadSec; // Max time to load the next fragment + if (isFiniteNumber(bufferAheadSec) && bufferAheadSec > 0 && !isFiniteNumber((_a = mediaQuery.seekTo) === null || _a === void 0 ? void 0 : _a.pos)) { + maxTimeToLoadSec = bufferAheadSec; + } + else { + // bufferAheadSec === 0 or NaN or seeking + const elapsedSec = elapsedMs / 1000; + if (elapsedSec < maxStarvationDelaySec) { + // Already starving. Try to complete load by maxStarvationDelaySec relative to current load + maxTimeToLoadSec = maxStarvationDelaySec - elapsedSec; + } + else { + // Could happen if we have high target duration. Start over with maxStarvationDelay + maxTimeToLoadSec = maxStarvationDelaySec; + } + } + const wasSlow = fragDownloadSlow; // To make logs less chatty + ({ fragDownloadSlow, fragDownloadTooSlow } = gotSlowFlow(inFlight, curStatus, rootQuery, logger)); + // consider emergency switch down only if we have less than 2 frag buffered AND + // time to finish loading current fragment is bigger than buffer starvation delay + // ie if we risk buffer starvation if bw does not increase quickly. + const minSwitchDuration = 2 * (((_b = curLibQuery.mediaOptionDetails) === null || _b === void 0 ? void 0 : _b.targetduration) || inFlight.duration); + const likelyToStall = bufferAheadSec <= minSwitchDuration && (remainingTimeSec >= maxTimeToLoadSec || fragDownloadSlow); + if (!likelyToStall) { + if (globalHlsService().getQuery().extendMaxTTFB) { + logger.info('[abr] re-enabling TTFB'); + globalHlsService().setExtendMaxTTFB(0); + } + return { fragDownloadSlow, fragDownloadTooSlow }; + } + if (!wasSlow) { + logger.warn(`likely to stall ${stringifyWithPrecision({ + maxTimeToLoadSec, + minSwitchDuration, + stats, + elapsedMs, + remainingTimeSec, + instantBw, + bufferAheadSec, + fragDownloadSlow, + })}`); + } + fragDownloadSlow = true; + if (!globalHlsService().getQuery().extendMaxTTFB) { + logger.info('[abr] temporarily disabling TTFB'); + globalHlsService().setExtendMaxTTFB(600000); // set to 10mins + } + // TODO: in native stack we only downswitch if our predictedBitrate < levelBitrate. However + // seems like we need to investigate if bandwidth estimate is reacting fast enough to bandwidth changes + let nextOptionId = undefined; + // Find something that will take less than bufferStarvationDelay (avoids rebuffering) + const itemId = inFlight.itemId; + const statsQuery = statsService.getQueryForItem(itemId); + const avgEstimate = statsQuery.getCombinedEstimate(); + const instantEstimate = Object.assign(Object.assign({}, avgEstimate), { avgBandwidth: instantBw }); + const bwStatus = statsQuery.bandwidthStatus; + const bwFactor = 1; + const bwUpFactor = 1; + const iframeMode = curVariant.iframes; + // We should probably switch down either way after this fragment, but whether we abort depends on whether we can avoid rebuffering + // Never abort in iframe mode. It kills the av-pipe when handleVariantSwitch does not switch + const shouldAbort = remainingTimeSec >= maxTimeToLoadSec && !iframeMode; + // Find lowest index with matching group or 0 if none + const minLevelIdx = findLowestValidVariantIndex(0, preferredList, iframeMode, rootQuery); + // There is no variant with audio/subtitle group matching, so there is nothing to switch to + if (minLevelIdx < 0) { + logger.info('findLowestValidVariantIndex could not find a valid level'); + return { fragDownloadSlow, fragDownloadTooSlow }; + } + // Set max level to current level or the lowest valid level, whichever is greater + const maxLevelIdx = Math.max(minLevelIdx, preferredList.findIndex((v) => v && v.mediaOptionId === curVariant.mediaOptionId)); + if (shouldAbort) { + // We will stall if we do not abort and switch down + // Check if we can find a variant that will load within maxTimeToLoadSec + let res = findBestVariantOption(preferredList, inFlight.duration, minLevelIdx, maxLevelIdx, maxTimeToLoadSec, iframeMode, instantEstimate, bwStatus, bwFactor, bwUpFactor, config, rootQuery, curLibQuery, mediaQuery, logger); + const badOptionId = NoMediaOption.mediaOptionId; + if (res.variantMediaOption !== badOptionId) { + logger.info(`buffered: ${bufferAheadSec} got option ${res.variantMediaOption} that will load in conditions ${stringifyWithPrecision({ maxTimeToLoadSec, instantBw })}`); + nextOptionId = res.variantMediaOption; + } + else if ((res = findBestVariantOption(preferredList, inFlight.duration, minLevelIdx, maxLevelIdx, remainingTimeSec, iframeMode, instantEstimate, bwStatus, bwFactor, bwUpFactor, config, rootQuery, curLibQuery, mediaQuery, logger)).variantMediaOption !== badOptionId) { + // Found variant that will load before current frag is done + nextOptionId = res.variantMediaOption; + logger.info(`buffered: ${bufferAheadSec} got option ${nextOptionId} that will load in conditions ${stringifyWithPrecision({ remainingTimeSec, instantBw })}`); + } + else { + // Waiting for current frag to finish will result in rebuffering, fall back to minimum option and abort + nextOptionId = res.lowestCandidate; + logger.info(`buffered: ${bufferAheadSec} got fallback option ${nextOptionId}, could not find variant that will load in conditions ${stringifyWithPrecision({ remainingTimeSec, instantBw })}`); + } + } + else { + // Can finish current frag download and then switch down by one level, matching alternates. + // Reverse preferredList in range [minLevelIdx, maxLevelIdx-1] inclusive then look for the lowest idx with valid variant audio/subtitle matching. + // This will return -1 on failure, maxLevelIdx-1 if iframeMode, or the offset from the tail of the highest idx below maxLevelIdx with matching + const offsetFromTail = findLowestValidVariantIndex(0, preferredList.slice(minLevelIdx, maxLevelIdx).reverse(), iframeMode, rootQuery); + const nextMatchingGroupIdx = maxLevelIdx - 1 - offsetFromTail; // maxLevelIdx-1 since slice(start, end) excludes the end idx + // Cap to next highest level with audio/subtitle group matching. If there is no lower group, stay at the currently enabled option and log it. + if (offsetFromTail >= 0 || maxLevelIdx === minLevelIdx) { + logger.info('cap to next highest level with audio/subtitle matching'); + nextOptionId = preferredList[nextMatchingGroupIdx].mediaOptionId; + } + else { + logger.info(`no lower level with matching audio/subtitle to switch to level=${curVariant.mediaOptionId}`); + } + } + if (nextOptionId != null && nextOptionId !== rootQuery.abrStatus.nextMaxAutoOptionId) { + logger.info(`capping level to ${nextOptionId} because likelyToStall`); + rootPlaylistService.setNextMaxAutoOptionId(itemId, nextOptionId); + } + // only emergency switch down if it takes less time to load new fragment at lowest level instead + // of finishing loading current one ... + if (shouldAbort) { + logger.warn(`loading too slow, abort fragment loading and switch to level ${nextOptionId}`); + logger.debug(`abort stats=${stringifyWithPrecision({ + instantBw, + loadedBytes: stats.loaded, + totalBytes, + elapsedMs, + remainingTimeSec, + maxTimeToLoadSec, + fragDownloadSlow, + })}`); + // force next load level in auto mode + // update bw estimate for this fragment before cancelling load (this will help reducing the bw) + logger.info(`[${MediaOptionNames[mediaOptionType]}] fragLoadingProcessingMs/stats.loaded: ${elapsedMs}/${stats.loaded}`); // also log when too slow + statsService.setBandwidthSample(Object.assign(Object.assign({}, stats), { tfirst: stats.tfirst || now, tload: stats.tload || now, complete: true, mediaOptionType: mediaOptionType })); + fragDownloadTooSlow = true; + // Abort current sequence + throw new FragmentAbortError({ mediaOptionType, mediaOptionId }, nextOptionId, ErrorResponses.FragmentAbortError); + } + return { fragDownloadSlow, fragDownloadTooSlow }; + } + function initializeAbrStatus(mediaOptionId, variantOptions) { + const highBWTrigger = getLowestSuperiorBW(mediaOptionId, variantOptions); + return { + fragDownloadSlow: false, + fragDownloadTooSlow: false, + nextMinAutoOptionId: NoMediaOption.mediaOptionId, + nextMaxAutoOptionId: NoMediaOption.mediaOptionId, + highBWTrigger: highBWTrigger, + }; + } + /** + * @brief Use ABR logic to switch VariantMediaOption if needed + * + * We should not switch levels unless we hit certain triggers: + * 1. Bandwidth High / Low change + * 2. Crossing low water level threshold + * 3. Hitting end of buffer + * 4. Slow fragment or level loading + * 5. iframe mode change + * 6. gapless item transition + * Exceptions are when we are at a discontinuity or seeking, when we should only switch if we have + * slow media (timeout) current level was sent to penalty box. + * When switching variant, handleVariantSwitch will ensure altAudio and subtitle mediaOptions follow + * the same groups specified by the chosen variant. + */ + function handleVariantSwitch(originalReason, config, rootQuery, mediaElementQuery, rootService) { + var _a, _b; + const logger = rootService.logger.child(abrLogName); + const iframeMode = mediaElementQuery.isIframeRate; + const optionListQuery = rootQuery.mediaOptionListQueries[MediaOptionType.Variant]; + const preferredList = optionListQuery.preferredMediaOptionList; + const curVariant = rootQuery.enabledMediaOptionKeys[MediaOptionType.Variant]; + let switchReason = originalReason; + if (switchReason === SwitchReason.None && !preferredList.some((option) => option.mediaOptionId === curVariant.mediaOptionId)) { + switchReason = SwitchReason.PreferredListChanged; + logger.info(`overriding switchReason: ${originalReason}->${switchReason}`); + } + const shouldSwitchVariant = switchReason !== SwitchReason.None; + if (!shouldSwitchVariant) { + logger.info(`Skipping variant switch as switchReason: ${switchReason}`); + return false; + } + if (iframeMode && switchReason !== SwitchReason.IframeModeChange) { + const bufferedIframes = mediaElementQuery.getBufferedSegmentsByType(SourceBufferType.Variant).filter((seg) => seg.frag.iframe); + if (bufferedIframes.length < config.minFramesBeforeSwitchingLevel) { + logger.info(`[iframes] only ${bufferedIframes.length} iframes, stay at current mediaOption ${curVariant.mediaOptionId}`); + // TODO: Force needData$. If fragment was aborted and water level is almost/dry, av-pipe will die. <@robert-walch 3/8/2022> + return false; + } + } + if (iframeMode && switchReason === SwitchReason.IframeModeChange) { + rootService.setEnabledVariantMediaOptionIdBeforeTrickplaySwitch(rootQuery.itemId, curVariant.mediaOptionId); + } + const statsQuery = createStatsQuery(rootQuery.itemId); + const mediaLibraryQuery = createMediaLibraryQuery(curVariant); + const newOptions = [NoMediaOption, NoMediaOption, NoMediaOption]; + // rdar://85517745 (Resume from trickplay level calculation needs functional parity with 2.15) + // On leaving trickplay, set the nextMaxAutioOptionId, this will in turn be used in nextAutoMediaOption + if (!iframeMode && originalReason === SwitchReason.IframeModeChange) { + const maxResumeOption = getTrickPlayMaxResumeOption(rootQuery.enabledVariantMediaOptionIdBeforeTrickplaySwitch, config, logger, rootQuery, statsQuery); + logger.info(`Capping variant selection to ${maxResumeOption} because switchReason: ${originalReason}`); + rootService.setNextMaxAutoOptionId(rootQuery.itemId, maxResumeOption); + rootService.setEnabledVariantMediaOptionIdBeforeTrickplaySwitch(rootQuery.itemId, undefined); + } + const nextVariant = nextAutoMediaOption(config, rootQuery, rootService, mediaLibraryQuery, mediaElementQuery, statsQuery, logger); + if (!iframeMode && originalReason === SwitchReason.IframeModeChange) { + // reset nextMaxAutioOptionId that was set above + rootService.setNextMaxAutoOptionId(rootQuery.itemId, NoMediaOption.mediaOptionId); + } + if (nextVariant.variantMediaOption === curVariant.mediaOptionId) { + logger.info(`Skipping variant switch as no better candidate than ${curVariant.mediaOptionId}`); + // TODO: Force needData$. If fragment was aborted and water level is almost/dry, av-pipe will die. <@robert-walch 3/8/2022> + return false; + } + logger.qe({ critical: true, name: 'shouldSwitchVariant', data: { value: shouldSwitchVariant, reason: switchReason } }); + newOptions[MediaOptionType.Variant] = { itemId: rootQuery.itemId, mediaOptionId: nextVariant.variantMediaOption }; + for (const i of [MediaOptionType.AltAudio, MediaOptionType.Subtitle]) { + const curOptionId = rootQuery.enabledMediaOptionIdByType(i); + if (curOptionId !== NoMediaOption.mediaOptionId) { + // newAlternate may be null for iframe, NoMediaOption, or reuse existing variant (no switch): reuse existing alternates + const newAlternate = i === MediaOptionType.AltAudio ? (_a = nextVariant.alternates) === null || _a === void 0 ? void 0 : _a.altAudio : (_b = nextVariant.alternates) === null || _b === void 0 ? void 0 : _b.subtitle; + newOptions[i] = newAlternate ? newAlternate : { itemId: rootQuery.itemId, mediaOptionId: curOptionId }; + } + } + rootService.setEnabledMediaOptions(rootQuery.itemId, newOptions); + logger.info(`Switching variant ${switchReason}: ${curVariant.mediaOptionId} variant/audio/subtitle ${newOptions[0].mediaOptionId}/${newOptions[1].mediaOptionId}/${newOptions[2].mediaOptionId}`); + return true; + } + /** + * @returns whether we should check for downswitch + */ + function gotLowBw(rootQuery, mediaElementQuery, rootService) { + var _a; + const logger = rootService.logger.child(abrLogName); + const abrStatus = rootQuery.abrStatus; + const slowMedia = gotSlowMedia(abrStatus); + const seeking = isFiniteNumber((_a = mediaElementQuery.seekTo) === null || _a === void 0 ? void 0 : _a.pos); + if (slowMedia && !abrStatus.fragDownloadTooSlow && seeking) { + logger.warn('could be ignoring low bandwidth due to seek'); + return false; + } + return slowMedia; + } + /** + * @returns whether we should check for upswitch + */ + function gotHighBw(config, rootQuery, rootService) { + var _a; + const logger = rootService.logger; + const statsQuery = createStatsQuery(rootQuery.itemId); + const bwe = statsQuery.getBandwidthEstimate(); + const abrStatus = rootQuery.abrStatus; + if (!hasReliableBandwidthEstimate(bwe)) { + return false; + } + const bandwidthSampleCount = ((_a = statsQuery.bandwidthStatus) === null || _a === void 0 ? void 0 : _a.bandwidthSampleCount) || 0; + const { bwUp } = getAdjustedBW(bwe.avgBandwidth, config.abrBandWidthUpFactor, config.abrBandWidthFactor, bandwidthSampleCount, logger); + if (bwUp > abrStatus.highBWTrigger) { + logger.info(`[abr] bandwidth high ${Math.round(bwe.avgBandwidth / 1000)}kbps trigger=${abrStatus.highBWTrigger / 1000}kbps factor=${config.abrBandWidthUpFactor}`); + return true; + } + return false; + } + + const loggerName$2 = { name: 'iframes' }; + var PrefetchResult; + (function (PrefetchResult) { + PrefetchResult[PrefetchResult["DISABLED"] = 0] = "DISABLED"; + PrefetchResult[PrefetchResult["ERRORED"] = 1] = "ERRORED"; + PrefetchResult[PrefetchResult["SUCCESS"] = 2] = "SUCCESS"; + })(PrefetchResult || (PrefetchResult = {})); + const checkForIframePrefetch = (context) => { + const { config, logger } = context; + if (!config.enableIFramePreloading) { + logger.info(loggerName$2, 'Iframe prefetch disabled'); + return of(PrefetchResult.DISABLED); + } + return waitTillAllowedPrefetch(context); + }; + function waitTillAllowedPrefetch(context) { + const { mediaSink, rootPlaylistQuery, mediaLibraryService, logger } = context; + const { mediaQuery } = mediaSink; + return combineQueries([mediaQuery.desiredRate$, mediaQuery.waterLevelChangedForType$(SourceBufferType.Variant)]).pipe( + // startup: don't subscribe to root playlist query until needed + switchMap(([desiredRate, waterLevel]) => { + if (isIframeRate(desiredRate) || waterLevel !== BufferWaterLevel.AboveHighWater) { + return EMPTY; + } + return rootPlaylistQuery.enabledMediaOptionByType$(MediaOptionType.Variant).pipe(map((enabledVariantMediaOption) => { + var _a, _b; + const variantQuery = rootPlaylistQuery.mediaOptionListQueries[MediaOptionType.Variant]; + const hasIframes = variantQuery.hasIframes; + const libQuery = mediaLibraryService.getQuery(); + const isLiveOrEvent = (_b = (_a = libQuery.getEntity(rootPlaylistQuery.itemId)) === null || _a === void 0 ? void 0 : _a.liveOrEvent) !== null && _b !== void 0 ? _b : false; + if (!hasIframes || isLiveOrEvent) { + logger.info(loggerName$2, `Skipping iframe prefetch hasIframes=${hasIframes}, liveOrEvent=${isLiveOrEvent}`); + return PrefetchResult.DISABLED; + } + return enabledVariantMediaOption; + })); + }), take(1), exhaustMap((variantOption) => beginPrefetch(variantOption, context)), finalize$1(() => { + logger.trace(loggerName$2, 'finalizing iframe prefetch'); + })); + } + function beginPrefetch(variantOption, context) { + const { logger } = context; + if (variantOption === PrefetchResult.DISABLED) { + return of(variantOption); + } + return prefetchPlaylist(variantOption, context).pipe(exhaustMap((mediaOptionDetails) => prefetchInitSegmentAndKey(mediaOptionDetails, context)), mapTo(PrefetchResult.SUCCESS), catchError((err) => { + logger.error(loggerName$2, `got error ${err.message} in prefetch`); + return of(PrefetchResult.ERRORED); + })); + } + function prefetchPlaylist(variantOption, context) { + const { rootPlaylistQuery, logger, config, mediaSink, statsService } = context; + const { mediaQuery } = mediaSink; + const statsQuery = statsService.getQueryForItem(rootPlaylistQuery.itemId); + const mediaLibraryQuery = createMediaLibraryQuery(variantOption); + const iframeMediaChoice = findNextABRAutoVariantOptionInMode(true, config, rootPlaylistQuery, mediaLibraryQuery, mediaQuery, statsQuery, logger); + const iframeMediaOption = rootPlaylistQuery.variantMediaOptionById(iframeMediaChoice.variantMediaOption); + logger.info(loggerName$2, `prefetching variant ${iframeMediaChoice.variantMediaOption}`); + return retrieveMediaOptionDetails(context, iframeMediaOption, true); + } + function prefetchInitSegmentAndKey(mediaOptionDetails, context) { + var _a; + const { logger, mediaSink, rootPlaylistQuery } = context; + const { mediaQuery } = mediaSink; + const fragResult = findFragment(mediaQuery.currentTime, rootPlaylistQuery.discoSeqNum, 0, mediaOptionDetails, []); + if (!((_a = fragResult === null || fragResult === void 0 ? void 0 : fragResult.foundFrag) === null || _a === void 0 ? void 0 : _a.mediaFragment)) { + return throwError('Unable to find fragment for iframe prefetch'); + } + const mediaFragment = fragResult.foundFrag.mediaFragment; + const prefetchKey$ = loadKey(context, mediaFragment.keyTagInfo, { itemId: mediaFragment.itemId, mediaOptionId: mediaFragment.mediaOptionId }); + const prefetchInitSeg$ = retrieveInitSegmentCacheEntity(context, mediaFragment); + return forkJoin([prefetchKey$, prefetchInitSeg$]).pipe(tap(() => logger.info(loggerName$2, `prefetched variant ${mediaOptionDetails.mediaOptionId}`)), mapTo(PrefetchResult.SUCCESS)); + } + function checkForIframeAutoPause(context) { + const { config, logger, iframeMachine, mediaSink } = context; + const { mediaQuery } = mediaSink; + return mediaQuery.desiredRate$.pipe(switchMap((rate) => { + if (!isIframeRate(rate)) { + return EMPTY; + } + const period = Math.abs(1000 / rate); + return timer(0, period).pipe(map(() => { + let result = null; + const seekable = mediaQuery.seekable; + if (!iframeMachine.isStarted || seekable.length < 1) { + return result; + } + const iframeReferenceClockTime = iframeMachine.iframeClockTimeSeconds; + const { leftMediaTimeToAutoPause } = config; + const minTime = seekable.start(0); + const maxTime = seekable.end(seekable.length - 1); + if (rate > 1 && maxTime - iframeReferenceClockTime < leftMediaTimeToAutoPause) { + logger.info({ name: 'iframes' }, `near the end of media, pausing normal playback, maxTime ${maxTime}, ifrct ${iframeReferenceClockTime}`); + result = { newRate: 0, postFlushSeek: maxTime - leftMediaTimeToAutoPause }; + iframeMachine.pause(); + } + else if (rate < 0 && iframeReferenceClockTime - minTime < rate / -2) { + logger.info({ name: 'iframes' }, `near the start of media, resuming playback, minTime ${minTime}, ifrct: ${iframeReferenceClockTime}`); + result = { newRate: 1, postFlushSeek: minTime }; + } + return result; + }), filterNullOrUndefined(), tap(({ newRate, postFlushSeek }) => { + mediaSink.postFlushSeek = postFlushSeek; + mediaSink.desiredRate = newRate; + }), switchMapTo(EMPTY)); + })); + } + function desiredRateChange(context) { + const mediaQuery = context.mediaSink.mediaQuery; + return combineLatest([of(context), mediaQuery.desiredRate$.pipe(pairwise())]).pipe(switchMap(([context, [oldRate, newRate]]) => { + const { rootPlaylistQuery, rootPlaylistService, config, logger, mediaSink, mediaLibraryService, statsService } = context; + const mediaQuery = mediaSink.mediaQuery; + const wasIframeRate = isIframeRate(oldRate); + const iframeRate = isIframeRate(newRate); + if (wasIframeRate !== iframeRate) { + logger.qe({ critical: true, name: 'iframes', data: { state: iframeRate, prevRate: oldRate, newRate } }); + handleVariantSwitch(SwitchReason.IframeModeChange, config, rootPlaylistQuery, mediaQuery, rootPlaylistService); + } + else if (oldRate === 0 && newRate === 1) { + const canPlayWithoutGap = AVMediaOptionTypes.every((type) => { + var _a; + const enabledOption = rootPlaylistQuery.enabledMediaOptionKeys[type]; + const libQuery = mediaLibraryService.getQueryForOption(enabledOption); + const statsQuery = statsService.getQueryForItem(rootPlaylistQuery.itemId); + const entity = libQuery.mediaOptionDetailsEntity; + return (!((_a = entity === null || entity === void 0 ? void 0 : entity.mediaOptionDetails) === null || _a === void 0 ? void 0 : _a.ptsKnown) || + mediaQuery.canContinuePlaybackWithoutGap(entity.mediaOptionDetails, entity.lastUpdateMillis, statsQuery.getPlaylistEstimate(), config.maxBufferHole)); + }); + if (!canPlayWithoutGap) { + logger.info('flush due to live gap [0, Infinity]'); + mediaSink.pause(); + return mediaSink.flushAll(0, Infinity, true); + } + } + return EMPTY; + }), switchMapTo(EMPTY)); + } + function capToEnabledIframeOption(context) { + const rootQuery = context.rootPlaylistQuery; + const mediaQuery = context.mediaSink.mediaQuery; + const enabledOption$ = rootQuery.enabledMediaOptionByType$(MediaOptionType.Variant); + return combineLatest([of(context), mediaQuery.desiredRate$.pipe(pairwise())]).pipe( + // distinct until desiredRate changed + distinctUntilChanged((prev, cur) => prev[1] === cur[1]), withLatestFrom(enabledOption$), // Startup: don't subscribe to root playlist query until needed + switchMap(([[context, [oldRate, newRate]], enabledMediaOption]) => { + const wasIframeRate = isIframeRate(oldRate); + const iframeRate = isIframeRate(newRate); + // Early return if trickplay was not toggled + if (wasIframeRate === iframeRate) { + return EMPTY; + } + const { rootPlaylistService, logger } = context; + if (iframeRate) { + // Check if level uncapped + if (context.rootPlaylistQuery.nextMaxAutoOptionId === NoMediaOption.mediaOptionId) { + // Attach to rootPlaylistQuery so it lasts throughout trickplay + rootPlaylistService.setNextMaxAutoOptionId(context.rootPlaylistQuery.itemId, enabledMediaOption.mediaOptionId); + logger.info(`Capped level to ${enabledMediaOption.mediaOptionId} on entering trickplay`); + } + } + return EMPTY; + })); + } + + class QueueItemQuery extends QueryEntity { + } + const queueItemStore = new EntityStore({}, { name: 'item-queue', producerFn: produce_1, idKey: 'itemId', resettable: true }); + const queueItemQuery = new QueueItemQuery(queueItemStore); + /** + * @brief The playback queue + */ + class ItemQueue { + constructor() { + this.firstItem = true; + this.playingEntity = null; + this.loadingEntity = null; + } + static createItem(name, url, initialSeekTime = NaN, platformInfo, serviceName) { + const today = new Date(); + const createTime = `${today.getHours()}:${today.getMinutes()}:${today.getSeconds()}`; + // The MediaFragment URI start time we get from URL will take precedence over: + // hls.config.startPosition > TIME-OFFSET in playlist > initialSeekTime parameter in this function. + const logger = getLogger(); + const timeOffset = getTimeOffsetParameter(url); + logger.info(`timeOffset parsed from URL ${timeOffset}`); + if (isFiniteNumber(timeOffset)) { + initialSeekTime = timeOffset; + } + else { + const hlsConfig = getCurrentConfig(); + if (isFiniteNumber(hlsConfig === null || hlsConfig === void 0 ? void 0 : hlsConfig.startPosition)) { + // override only if finite value + initialSeekTime = hlsConfig.startPosition; + logger.info(`override initialSeekTime with startPosition ${initialSeekTime}`); + } + } + const queueItem = { + itemId: `${name}_${createTime}`, + name, + url, + serviceName, + createTime, + initialSeekTime, + itemStartOffset: 0, + platformInfo, + config: {}, + }; + return queueItem; + } + /** + * @returns obervable for whenever active item changes + */ + get activeItemById$() { + return queueItemQuery.selectActiveId().pipe(map((_) => queueItemQuery.getActive())); + } + get removedItems$() { + return queueItemQuery.selectEntityAction(EntityActions.Remove).pipe(map((ids) => ids)); + } + /** + * @returns the active item. The active item is the currently downloading item + * which could be different from the playing item. + */ + get activeItem() { + return queueItemQuery.getActive(); + } + /** + * @returns the entire play queue + */ + get queueItems$() { + return queueItemQuery.selectAll().pipe(map((queueItems) => queueItems !== null && queueItems !== void 0 ? queueItems : [])); + } + /* + * @returns whether this is the first item + */ + get isFirstItem() { + return this.firstItem; + } + get playingItem() { + return this.playingEntity; + } + get loadingItem() { + return this.loadingEntity; + } + /** + * Add item to the queue and makes it active but keeps the former active item. + * + * @param name Identifier for the item + * @param url URL for the root playlist + */ + addQueueItem(name, url, initialSeekTime, platformInfo, itemStartOffset, serviceName) { + // TODO: Kola to investigate not exposing itemStartOffset in the public API of addQueueItem + queueItemQuery.getCount(); + const queueItem = ItemQueue.createItem(name, url, initialSeekTime, platformInfo, serviceName); + if (this.playingEntity != null) { + // clear initialSeekTime if it's not the playing item + queueItem.initialSeekTime = undefined; + } + if (itemStartOffset) { + queueItem.itemStartOffset = itemStartOffset; + getLogger().debug(`itemStartOffset=${queueItem.itemStartOffset}`); + this.firstItem = false; + // set playing and loading item ids + this.playingEntity = this.activeItem; + this.loadingEntity = queueItem; + } + logAction(`queue.add.item: ${name}`); + applyTransaction(() => { + queueItemStore.add(queueItem); + queueItemStore.setActive(queueItem.itemId); + }); + } + /** + * Update the playing item id from loading item id and clear loading item id + */ + updatePlayingItemId() { + this.playingEntity = this.loadingEntity; + this.loadingEntity = null; + this.clearAllButActive(); + } + /** + * Reset loading item + */ + resetLoadingItem() { + this.removeQueueItem(this.loadingEntity.itemId); + this.loadingEntity = null; + applyTransaction(() => { + queueItemStore.setActive(this.playingEntity.itemId); + }); + } + /** + * Returns whether a track is currently preloading but not yet playing + */ + isPreloading() { + // If both loading and playing items are not undefined then a track is currently loading + return this.playingEntity !== null && this.loadingEntity !== null; + } + setQueueItem(name, url, initialSeekTime, platformInfo, serviceName) { + logAction('queue.set.item'); + this.loadingEntity = null; + applyTransaction(() => { + queueItemStore.reset(); + const queueItem = ItemQueue.createItem(name, url, initialSeekTime, platformInfo, serviceName); + queueItemStore.add(queueItem); + queueItemStore.setActive(queueItem.itemId); + }); + this.playingEntity = this.activeItem; + } + /** + * Remove an item from the queue + */ + removeQueueItem(itemId) { + queueItemStore.remove(itemId); + } + /** + * Clear the queue + */ + clearQueue() { + queueItemStore.reset(); + } + /** + * Clear all items but the active item + */ + clearAllButActive() { + var _a; + const activeId = (_a = this.activeItem) === null || _a === void 0 ? void 0 : _a.itemId; + applyTransaction(() => { + const items = queueItemQuery.getAll(); + items.forEach((item) => { + if (item.itemId !== activeId) { + queueItemStore.remove(item.itemId); + } + }); + }); + } + set earlyAudioSelection(audioPersistentId) { + queueItemStore.updateActive((activeItem) => { + if (!activeItem.earlySelection) { + activeItem.earlySelection = {}; + } + activeItem.earlySelection.audioPersistentId = audioPersistentId; + }); + } + get earlyAudioSelection() { + var _a; + return (_a = this.activeItem.earlySelection) === null || _a === void 0 ? void 0 : _a.audioPersistentId; + } + set earlySubtitleSelection(subtitlePersistentId) { + queueItemStore.updateActive((activeItem) => { + if (!activeItem.earlySelection) { + activeItem.earlySelection = {}; + } + activeItem.earlySelection.subtitlePersistentId = subtitlePersistentId; + }); + } + get earlySubtitleSelection() { + var _a; + return (_a = this.activeItem.earlySelection) === null || _a === void 0 ? void 0 : _a.subtitlePersistentId; + } + } + + /** + * Handle MSE related errors: sourcebuffer creation, appending, etc. + */ + // Append error handling + function addAppendErrorHandlingPolicy(mediaSink, sbAdapter, mediaOptionType, mediaOptionId, targetDuration, config, rootQuery, rootService, meQuery) { + return (source) => source.pipe(tap(() => { + rootService.updateConsecutiveTimeouts(rootQuery.itemId, mediaOptionType, false, 'append'); + }), retryWhen((errors) => errors.pipe(mergeMap((err, retryCount) => { + const isTimeout = err instanceof AppendBufferError && err.isTimeout; + rootService.updateConsecutiveTimeouts(rootQuery.itemId, mediaOptionType, isTimeout, 'append'); + if (isTimeout) { + return handleAppendTimeout(err, retryCount, config, mediaOptionType, mediaOptionId, meQuery, rootQuery, rootService); + } + if (err instanceof BufferFullError) { + return handleBufferFullError(err, sbAdapter, retryCount, targetDuration, config, mediaOptionType, mediaOptionId, meQuery, rootQuery, rootService); + } + if (err instanceof MediaDecodeError) { + const { mediaOptionType, mediaOptionId } = err; + return handleFormatError(err, mediaOptionType, mediaOptionId, mediaSink, rootService, rootQuery); + } + throw err; + })))); + } + function handleBufferFullError(error, sb, retryCount, targetDuration, config, mediaOptionType, mediaOptionId, meQuery, rootQuery, rootService) { + const sbType = sb.type; + const currentWaterLevel = meQuery.getCurrentWaterLevelByType(sbType, config.maxBufferHole); // Always use real position for buffer full errors + const mediaBuffered = currentWaterLevel >= config.almostDryBufferSec; + // Ejecting media from the buffer in iframe-mode will allow trick-play to recover when the forward audio buffer is full + // TODO: This should be considered for normal playback. Retries buy time, but they don't help reduce buffer usage. + // Flushing back and forward buffer is a better long-term solution for reclaiming memory. + if (mediaBuffered && !meQuery.isIframeRate) { + // this.logger.debug(`${self} bufferFullError waterLevel:${currentWaterLevel} targetDur:${this.targetDuration}`); + const retryDelayMs = targetDuration * 1000; + const action = { + errorAction: NetworkErrorAction.RetryRequest, + errorActionFlags: 0, + }; + if (currentWaterLevel * 1000 < retryDelayMs) { + if (rootService.hasFallbackMediaOptionTuple(rootQuery, mediaOptionType, mediaOptionId, false)) { + // Put into penalty box if we might run out of buffer before we can append again + action.errorAction = NetworkErrorAction.SendAlternateToPenaltyBox; + } + else { + // can only fit 1 target duration so likely to stall. Probably pointless to keep going + // this.logger.warn(`${self} buffer too small to sustain playback @${pos.toFixed(3)} buffered:${TimeRangesExt.toString(this.buffered)}`); + action.errorAction = NetworkErrorAction.SendEndCallback; + } + } + // this.logger.info(`${self} handleBufferFullError errorAction:${errorAction} ${retryDelayMs}ms`); + const retryConfig = { + retryDelayMs, + maxNumRetry: Infinity, + maxRetryDelayMs: retryDelayMs, + }; + return handleErrorWithRetry(error, retryCount, retryConfig, action, rootQuery, rootService, mediaOptionType, mediaOptionId); + } + else if (retryCount < config.appendErrorMaxRetry) { + // current position is not buffered, but browser is still complaining about buffer full error + // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708 + // in that case flush the whole buffer to recover and retry + // this.logger.warn(`${self} buffer full and not buffered. flush everything and retry`); + return sb.remove(0, Number.POSITIVE_INFINITY); + } + else { + error.fatal = true; + } + return throwError(error); // Rethrow + } + function handleAppendTimeout(err, retryCount, config, mediaOptionType, mediaOptionId, meQuery, rootQuery, rootService) { + // Should be rare. If append timeout occurs possibly there is some platform or media issue so safer to + // reset and switch + let errorAction = { + errorAction: NetworkErrorAction.SendAlternateToPenaltyBox, + errorActionFlags: 0, + }; + const waterLevel = meQuery.getCurrentWaterLevel(config.maxBufferHole); + const bufferEmpty = waterLevel < config.almostDryBufferSec; + let retryDelayMs = NaN; + const maxAppendFails = config.appendErrorMaxRetry; + const appendTimeouts = rootQuery.rootPlaylistEntity.errorsByType[mediaOptionType].timeouts.append; + if ((bufferEmpty && appendTimeouts >= maxAppendFails) || retryCount >= maxAppendFails) { + errorAction.errorAction = NetworkErrorAction.SendEndCallback; + } + else { + retryDelayMs = waterLevel * 1000; // should only trigger on last variant + } + const retryConfig = { + retryDelayMs, + maxNumRetry: maxAppendFails, + maxRetryDelayMs: retryDelayMs, + }; + errorAction = modifyErrorActionIfCurrentLevelIsLastValidLevel(errorAction, false, err.response.code, mediaOptionId, mediaOptionType, rootQuery, rootService); + return handleErrorWithRetry(err, retryCount, retryConfig, errorAction, rootQuery, rootService, mediaOptionType, mediaOptionId) + .pipe(); + } + function handleFormatError(err, mediaOptionType, mediaOptionId, mediaSink, rootService, rootQuery) { + let action = { + errorAction: NetworkErrorAction.RemoveAlternatePermanently, + errorActionFlags: 0, + }; + action = modifyErrorActionIfCurrentLevelIsLastValidLevel(action, false, err.response.code, mediaOptionId, mediaOptionType, rootQuery, rootService); + // No retry + return handleErrorActionCommon(err, 0, action, rootQuery, rootService, mediaOptionType, mediaOptionId).pipe(catchError((err) => { + if (err.fatal === false) { + mediaSink.resetMediaSource(); + } + throw err; + })); + } + // Handle CreateSourceBufferError. For combined audio video append, handle the error here so that + // reset does not destroy the other SourceBuffer too early + function addCreateSourceBufferErrorHandlingPolicy(mediaSink, rootService, rootQuery) { + return (source$) => source$.pipe(catchError((err) => { + if (err instanceof CreateSourceBufferError) { + const { mediaOptionType, mediaOptionId } = err; + return handleFormatError(err, mediaOptionType, mediaOptionId, mediaSink, rootService, rootQuery); + } + throw err; + })); + } + + class FragmentPicker { + constructor(logger, _rootPlaylistService, _rootQuery, _mediaQuery, _iframeMachine, config) { + this.logger = logger; + this._rootPlaylistService = _rootPlaylistService; + this._rootQuery = _rootQuery; + this._mediaQuery = _mediaQuery; + this._iframeMachine = _iframeMachine; + this._anchorMSNs = [NaN, NaN]; // First fragment in current discontinuity + this._avDetails = [null, null]; // enabled AV options latest details (just the relevant info) + this.logger = logger.child({ name: 'fpicker' }); + this._discoSeqNum = NaN; + this.lookUpTolerance = Math.max(config.maxBufferHole, config.maxFragLookUpTolerance); + this.firstAudioMustOverlapVideoStart = config.firstAudioMustOverlapVideoStart; + this.lookUpToleranceAlt = config.firstAudioMustOverlapVideoStart ? 0 : config.maxFragLookUpTolerance; + this.logger.info(`new item ${this._rootQuery.itemId}`); + } + destroy() { + this._anchorMSNs = [NaN, NaN]; + this._avDetails = [null, null]; + this._rootQuery = null; + this._mediaQuery = null; + this._rootPlaylistService = null; + this._iframeMachine = null; + } + get discoSeqNum() { + return this._discoSeqNum; + } + get _discoSeqNum() { + return this._rootQuery.discoSeqNum; + } + set _discoSeqNum(cc) { + // Should we just have a separate store for fragment selection? + this._rootPlaylistService.setDiscoSeqNum(this._rootQuery.itemId, cc); + } + get anchorMSNs() { + return this._anchorMSNs; + } + _resolvePosition(position, type, newDetails) { + var _a; + let resolvedPosition = position; + const curDetails = this._avDetails[type]; + // LIVE: + // 1. try choosing position based on program date time @ the switch position + // 2. if ptsknown, we can reasonably choose @ switch position + // 3. else, choose at live offset from end + // Has END-TAG: + // Simple case. just use position. + if ((curDetails === null || curDetails === void 0 ? void 0 : curDetails.mediaOptionId) !== (newDetails === null || newDetails === void 0 ? void 0 : newDetails.mediaOptionId) && + newDetails.liveOrEvent && + newDetails.ptsKnown === false && + (!curDetails || Math.max(curDetails.startSN, newDetails.startSN) > Math.min(curDetails.endSN, newDetails.endSN)) // no overlap + ) { + // Commenting out PDT based revisal as it needs new position as opposed to old position, before rebasing to newDetails. + // if (curDetails?.dateMediaTimePairs != null && newDetails.dateMediaTimePairs) { + // const date = resolvePTSToDate(curDetails.dateMediaTimePairs, position); + // resolvedPosition = resolveDateToPTS(newDetails.dateMediaTimePairs, date); + // this.logger.info(`Use program date time ${date.toISOString()} ${position.toFixed(3)}->${resolvedPosition.toFixed(3)}`); + // } else { + const liveOffsetSec = 3 * newDetails.targetduration; // HLS native chooses 3 * targetDuration, or HOLD-BACK property + const liveEdge = ((_a = newDetails.fragments[0]) === null || _a === void 0 ? void 0 : _a.start) + newDetails.totalduration; + resolvedPosition = Math.max(0, liveEdge - liveOffsetSec); + this.logger.info(`Use live offset ${liveOffsetSec.toFixed(3)} ${position.toFixed(3)}->${resolvedPosition.toFixed(3)}`); + // } + } + return resolvedPosition; + } + getDiscoSeqNumForTime(details, position) { + if (this._mediaQuery.isIframeRate && details.iframesOnly) { + return DiscoHelper$1.discoSeqNumForTime(details.fragments, position); // from iframeMachine startClocksAndGetFirstFragment + } + else { + return discoSeqNumForTime(details, position); + } + } + /** + * After a seek, need to re-anchor using latest details + * @param position Position for re-anchor + * @param detailsTuple details + */ + _updateAnchorByPosition(position, detailsTuple) { + let newCC = NaN; + const vDetails = detailsTuple[SourceBufferType.Variant]; + let resolvedPosition = position; + if (vDetails) { + const fragments = vDetails.fragments; + resolvedPosition = this._resolvePosition(position, SourceBufferType.Variant, vDetails); + newCC = this.getDiscoSeqNumForTime(vDetails, resolvedPosition); // used resolved position to get CC + if (!isFiniteNumber(newCC)) { + const firstFrag = fragments[0]; + const lastFrag = fragments[fragments.length - 1]; + const startPTSSec = firstFrag === null || firstFrag === void 0 ? void 0 : firstFrag.start; + const endPTSSec = (lastFrag === null || lastFrag === void 0 ? void 0 : lastFrag.start) + (lastFrag === null || lastFrag === void 0 ? void 0 : lastFrag.duration); + this.logger.warn(`${position.toFixed(3)} out of range [${startPTSSec === null || startPTSSec === void 0 ? void 0 : startPTSSec.toFixed(3)},${endPTSSec === null || endPTSSec === void 0 ? void 0 : endPTSSec.toFixed(3)}]`); + if (resolvedPosition <= startPTSSec) { + // Gapless preloading + newCC = firstFrag.discoSeqNum; + } + else if (resolvedPosition >= endPTSSec) { + newCC = lastFrag.discoSeqNum; + } + else { + this.logger.warn(`Unable to determine newCC. fragFirst: ${JSON.stringify(firstFrag)} fragLast: ${JSON.stringify(lastFrag)}`); + } + if (!isFiniteNumber(newCC)) { + this.logger.warn(`Unable to determine newCC. fragFirst: ${JSON.stringify(firstFrag)} fragLast: ${JSON.stringify(lastFrag)}`); + } + } + } + else { + this.logger.warn('No variant details for anchoring'); + } + if (newCC !== this._discoSeqNum) { + this.logger.info(`Update anchor by position:${resolvedPosition.toFixed(3)} cc:${this._discoSeqNum}->${newCC}`); + } + this._updateAnchor(newCC, detailsTuple); + return resolvedPosition; + } + _updateAnchor(newCC, detailsTuple) { + const ccDidChange = newCC !== this._discoSeqNum; + if (ccDidChange) { + this.logger.info(`Update anchor cc:${this._discoSeqNum}->${newCC}`); + this._discoSeqNum = newCC; + } + AVMediaOptionTypes.forEach((type) => { + const curDetails = this._avDetails[type]; + const newDetails = detailsTuple[type]; + const detailsChanged = (curDetails === null || curDetails === void 0 ? void 0 : curDetails.mediaOptionId) !== (newDetails === null || newDetails === void 0 ? void 0 : newDetails.mediaOptionId); + if (ccDidChange || detailsChanged) { + this._updateAnchorForType(mediaOptionTypeToSourceBufferType(type), newDetails); + } + else if (newDetails) { + const { mediaOptionId, ptsKnown, dateMediaTimePairs, startSN, endSN } = newDetails; + this._avDetails[type] = { mediaOptionId, ptsKnown, dateMediaTimePairs, startSN, endSN }; + } + }); + } + /** + * Get next set of fragments to choose. + * @param pos The playback position (media.currentTime) + * @param detailsTuple The latest details for variant and alt audio + * @param usePosition if true, use position for selection. else, use buffer end + */ + getNextFragments(action, detailsTuple, logger) { + const { position, bufferInfoTuple, switchContexts } = action; + const searchPositions = bufferInfoTuple.map((bufferInfo, type) => { + return getSearchPosition(position, detailsTuple[type], switchContexts[type], bufferInfo === null || bufferInfo === void 0 ? void 0 : bufferInfo.buffered, type === MediaOptionType.AltAudio ? this.lookUpToleranceAlt : this.lookUpTolerance); + }); + let anchorPosition = searchPositions.reduce((lastMin, position) => Math.min(position, lastMin), Number.POSITIVE_INFINITY); + if (isFiniteNumber(action.discoSeqNum)) { + // if anchorTime specifies a discontinuity sequence number, find only fragments in the specified cc. + // happens right after resetting mediaSource when crossing an incompat. discontinuity + const combinedRange = detailsTuple.reduce((finalRange, details) => { + if (!details) { + return finalRange; + } + const thisRange = DiscoHelper$1.getTimeRangeForCC(details.fragments, action.discoSeqNum, logger); // adjust anchorPosition in case the anchor cc is a bit off + return [Math.max(finalRange[0], thisRange[0]), Math.min(finalRange[1], thisRange[1])]; + }, [Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY]); + anchorPosition = Math.min(Math.max(anchorPosition, combinedRange[0]), combinedRange[1]); + this.logger.info(`adjusted anchorPosition ${anchorPosition} to fit in cc ${action.discoSeqNum} [${combinedRange[0]}-${combinedRange[1]}]`); + } + action.position = this._updateAnchorByPosition(anchorPosition, detailsTuple); + return this._getNextFragmentsInternal(action, detailsTuple); + } + _getNextFragmentsInternal(action, detailsTuple) { + var _a, _b; + const chosenFrags = [null, null]; + let origPosition; + detailsTuple.forEach((_, type) => { + var _a; + if (this.firstAudioMustOverlapVideoStart && type === MediaOptionType.AltAudio && this._mediaQuery.seeking) { + // this ensures the audio is not ahead of the video; + // especially, decode from iframes could further pull back the + // seek position, and we should ensure that the corresponding audio is + // also available + if ((_a = chosenFrags[MediaOptionType.Variant]) === null || _a === void 0 ? void 0 : _a.foundFrag) { + // retain original position in case of next discontinuity + origPosition = action.position; + action.position = chosenFrags[MediaOptionType.Variant].foundFrag.mediaFragment.start; + this.logger.info(`revised altAudio frag search position to ${action.position} based on variant frag start`); + } + } + chosenFrags[type] = this._getNextFragmentForType(action, detailsTuple, type); + }); + const vResult = chosenFrags[SourceBufferType.Variant]; + const aResult = chosenFrags[SourceBufferType.AltAudio]; + // One buffer is too far ahead of the other one + // 1. Could happen in i-frame mode where audio fragments are much longer + // 2. Audio switch that causes complete flush + const vFrag = (_a = vResult === null || vResult === void 0 ? void 0 : vResult.foundFrag) === null || _a === void 0 ? void 0 : _a.mediaFragment; + const aFrag = (_b = aResult === null || aResult === void 0 ? void 0 : aResult.foundFrag) === null || _b === void 0 ? void 0 : _b.mediaFragment; + if (vFrag && aFrag) { + if (aFrag.start > vFrag.start + vFrag.duration) { + // must not load audio too far ahead in iframe mode, cause unrecoverable bufferFull error in AirPlay + this.logger.warn('Audio too far ahead'); + chosenFrags[SourceBufferType.AltAudio] = FragmentPicker.noopResult; + } + else if (vFrag.start > aFrag.start + aFrag.duration && !this._mediaQuery.isIframeRate) { + this.logger.warn('Video too far ahead'); + chosenFrags[SourceBufferType.Variant] = FragmentPicker.noopResult; + } + } + // All fragments in next discontinuity. Advance and try selecting again + if (isFinite(vResult === null || vResult === void 0 ? void 0 : vResult.nextDisco) && (aResult == null || isFiniteNumber(aResult.nextDisco))) { + const newDisco = chosenFrags[SourceBufferType.Variant].nextDisco; + this.logger.info(`Advance anchor cc: ${this._discoSeqNum}->${newDisco}`); + this._updateAnchor(newDisco, detailsTuple); + if (isFiniteNumber(origPosition)) { + // reinstate the original position due to previous firstAudioMustOverlapVideoStart override + action.position = origPosition; + origPosition = NaN; + } + return this._getNextFragmentsInternal(action, detailsTuple); + } + return chosenFrags; + } + /** + * Get fragment for position for particular buffer + * @param pos position that fragment should overlap + * @param usePos if true, choose fragment overlapping pos (e.g. immediate switch w/o flush). else, use bufferEnd (default) + */ + _getNextFragmentForType(action, detailsTuple, type) { + var _a, _b, _c, _d, _e, _f, _g, _h; + const { position: pos, bufferInfoTuple, switchContexts } = action; + const newDetails = detailsTuple[type]; + const bufferInfo = (_b = (_a = bufferInfoTuple[type]) === null || _a === void 0 ? void 0 : _a.buffered) !== null && _b !== void 0 ? _b : { start: pos, end: pos, len: 0 }; + const bufferedSeg = this._mediaQuery.getBufferedSegmentsByType(type); + const willFlush = (_d = (_c = switchContexts[type]) === null || _c === void 0 ? void 0 : _c.userInitiated) !== null && _d !== void 0 ? _d : false; // Full flush of this buffer will happen + const searchPos = getSearchPosition(pos, newDetails, switchContexts[type], bufferInfo, this.lookUpTolerance); + if (!newDetails) { + return null; + } + const { highWaterLevelSeconds, lowWaterLevelSeconds } = this._mediaQuery.bufferMonitorInfo; + const bufferAhead = bufferInfo.len; // Future bufferAhead + if (!willFlush && bufferAhead >= highWaterLevelSeconds) { + this.logger.info(`[${MediaOptionNames[type]}] got high buffer @${pos.toFixed(3)} ${bufferInfo.len.toFixed(3)} > ${highWaterLevelSeconds}`); + return FragmentPicker.noopResult; + } + // Special case where audio is starving but variant is ok + const otherType = type === SourceBufferType.Variant ? SourceBufferType.AltAudio : SourceBufferType.Variant; + const otherBuffer = (_e = bufferInfoTuple[otherType]) === null || _e === void 0 ? void 0 : _e.buffered; + const otherWillFlush = (_g = (_f = switchContexts[otherType]) === null || _f === void 0 ? void 0 : _f.userInitiated) !== null && _g !== void 0 ? _g : false; + let wantToDelayVariant = false; + if (type === SourceBufferType.Variant && + bufferAhead >= lowWaterLevelSeconds && + // Yield to altaudio (but ensure audio load-n-append does not punt back to variant, causing an infinite loop): + // A) Audio buffer ends earlier than video buffer. + // If buffered audio ends later than video, audio append may abort & wait for video append to catch up in mediaSink.getMatchingInfo + this._mediaQuery.expectedSbCount > 1 && + otherBuffer != null && + otherBuffer.end < bufferInfo.end && + // B) Audio was starting from an empty buffer (true starvation after audio track switch): _total_ audio buffer < 1 target duration + // Use (otherBuffer.end - otherBuffer.start) instead of otherBuffer.len (which begins from currentTime, not start of buffer) + (otherWillFlush || otherBuffer.end - otherBuffer.start < lowWaterLevelSeconds)) { + this.logger.info(`[${MediaOptionNames[type]}] want to delay variant due to audio starvation @${pos.toFixed(3)} ${bufferInfo.len.toFixed(3)} > ${lowWaterLevelSeconds}`); + wantToDelayVariant = true; + } + let foundFrag = null; + let nextDisco = NaN; + let newMediaRootTime = undefined; + if (this._mediaQuery.isIframeRate && type === SourceBufferType.Variant && newDetails.iframesOnly) { + const result = findIframeFragmentForPosition(searchPos, newDetails, detailsTuple[SourceBufferType.AltAudio], this._mediaQuery.desiredRate, this._iframeMachine); + if (result) { + ({ foundFrag, nextDisco, newMediaRootTime } = result); + const frag = foundFrag.mediaFragment; + if (frag.discoSeqNum !== this._discoSeqNum) { + this.logger.info(`[iframes] overrride cc:${frag.discoSeqNum}`); + // OK to advance here since Variant will run before AltAudio. + this._updateAnchor(frag.discoSeqNum, detailsTuple); + } + } + } + else { + // Find eligible segment in the specified discontinuity + const anchorMSN = this._anchorMSNs[type]; + ({ foundFrag, nextDisco, newMediaRootTime } = findFragment(searchPos, this._discoSeqNum, anchorMSN, newDetails, bufferedSeg)); + } + if (wantToDelayVariant && this._rootQuery.getInitPTS(foundFrag === null || foundFrag === void 0 ? void 0 : foundFrag.mediaFragment.discoSeqNum)) { + this.logger.info(`[${MediaOptionNames[type]}] delay variant since initPTS[${foundFrag === null || foundFrag === void 0 ? void 0 : foundFrag.mediaFragment.discoSeqNum}] is available`); + return FragmentPicker.noopResult; + } + this.logger.info(`[${MediaOptionNames[type]}] getNextFragmentForType searchPos:${searchPos.toFixed(3)} pos:${pos.toFixed(3)} bufferEnd:${bufferInfo === null || bufferInfo === void 0 ? void 0 : bufferInfo.end.toFixed(3)} willFlush: ${willFlush} cc:${this._discoSeqNum} foundFrag:${fragPrint(foundFrag === null || foundFrag === void 0 ? void 0 : foundFrag.mediaFragment)} timelineOffset:${(_h = foundFrag === null || foundFrag === void 0 ? void 0 : foundFrag.timelineOffset) === null || _h === void 0 ? void 0 : _h.toFixed(3)} nextDisco:${nextDisco} newMediaRootTime:${newMediaRootTime}`); + return { foundFrag, nextDisco, newMediaRootTime }; + } + _updateAnchorForType(type, newDetails) { + var _a; + if (!newDetails) { + this._anchorMSNs[type] = NaN; + this._avDetails[type] = null; + return; + } + if (!isFiniteNumber(this._discoSeqNum)) { + this.logger.warn('Trying to anchor with non-finite discoSeqNum'); + return; + } + const cc = this._discoSeqNum; + const anchorFrag = getAnchorFrag(newDetails.fragments, cc); + const mediaSeqNum = (_a = anchorFrag === null || anchorFrag === void 0 ? void 0 : anchorFrag.mediaSeqNum) !== null && _a !== void 0 ? _a : newDetails.startSN; // No anchor, use first fragment as fallback + this._anchorMSNs[type] = mediaSeqNum; + const { mediaOptionId, ptsKnown, dateMediaTimePairs, startSN, endSN } = newDetails; + this._avDetails[type] = { mediaOptionId, ptsKnown, dateMediaTimePairs, startSN, endSN }; + this.logger.info(`[${MediaOptionNames[type]}] Anchor update id:${mediaOptionId} sn:${this._anchorMSNs[type]}->${mediaSeqNum} discoSeqNum:${this._discoSeqNum}`); + } + } + FragmentPicker.noopResult = { foundFrag: null, nextDisco: NaN }; // prevents us from skipping ahead in disco + /** + * Get first fragment in discontinuity cc + */ + function getAnchorFrag(fragments, cc) { + return fragments.find((f) => f.discoSeqNum === cc); + } + function getSearchPosition(pos, details, switchContext, bufferInfo, defaultTolerance) { + var _a; + bufferInfo = bufferInfo !== null && bufferInfo !== void 0 ? bufferInfo : { start: pos, end: pos, len: 0 }; + const willFlush = (_a = switchContext === null || switchContext === void 0 ? void 0 : switchContext.userInitiated) !== null && _a !== void 0 ? _a : false; // Full flush of this buffer will happen + const tolerance = (details === null || details === void 0 ? void 0 : details.iframesOnly) ? 0 : defaultTolerance; + return willFlush || bufferInfo.len === 0 ? pos : bufferInfo.end + tolerance; + } + + const avPipeLogName = { name: 'avpipe' }; + // AVPipe + function makeAVPipe(context) { + const { config, rootPlaylistService, rootPlaylistQuery, mediaSink, gaplessInstance } = context; + const mediaQuery = mediaSink.mediaQuery; + const mediaOptionSwitching$ = combineQueries(AVMediaOptionTypes.map((type) => { + return rootPlaylistQuery.enabledMediaOptionSwitchForType$(type).pipe(tap((switchInfo) => logger.info(`${MediaOptionNames[type]} mediaOptionSwitch: ${JSON.stringify(switchInfo)}`))); + })).pipe(switchMap((switchInfo) => { + // Teardown if no enabled option for at least main + if (!isEnabledMediaOption({ itemId: rootPlaylistQuery.itemId, mediaOptionId: switchInfo[MediaOptionType.Variant].toId })) { + throw new ExceptionError(true, `No valid variant enabled id:${switchInfo[MediaOptionType.Variant].toId}`, ErrorResponses.NoValidAlternates); + } + // Handle switch + const switchActions$ = switchInfo.map(({ fromId, toId }, mediaOptionType) => { + return pipelineHandleOptionSwitch(context, statsQuery, mediaOptionType, fromId, toId); + }); + return concat(of(true), // Emit on switch start + forkJoin(switchActions$).pipe(mapTo(false)) // Emit when we're ready to select fragments + ); + }), tag('mediaOptionSwitch.audiovideo.out')); + const statsQuery = createStatsQuery(rootPlaylistQuery.itemId); + const logger = context.logger.child(avPipeLogName); + const fragmentPicker = new FragmentPicker(logger, rootPlaylistService, rootPlaylistQuery, mediaQuery, context.iframeMachine, config); + const anchorTimeChange$ = rootPlaylistQuery.anchorTime$.pipe(tag('anchorTime.audiovideo.in')); + // Switch subscriptions when: + // 1. anchor time change + // 2. enabled option change + // Exhaust / wait for pipeline to finalize when: + // 1. Need data changes + return combineQueries([anchorTimeChange$, mediaOptionSwitching$]).pipe(switchMap(([anchorTime, handlingSwitch]) => { + logger.info(`anchorTime=${anchorTime === null || anchorTime === void 0 ? void 0 : anchorTime.pos.toFixed(3)} cc=${anchorTime === null || anchorTime === void 0 ? void 0 : anchorTime.discoSeqNum} handlingSwitch=${handlingSwitch}`); + if (handlingSwitch) { + return EMPTY; // Just cancel any loads in progress. This is to stop stuff from loading while we're flushing + } + return mediaQuery.needData$(config.maxBufferHole, gaplessInstance.inGaplessMode).pipe(map((needData) => { + const switchContexts = [ + rootPlaylistQuery.enabledMediaOptionSwitchContexts[MediaOptionType.Variant], + rootPlaylistQuery.enabledMediaOptionSwitchContexts[MediaOptionType.AltAudio], + ]; + return mediaQuery.getSourceBufferInfoAction(needData, anchorTime, switchContexts, config.maxBufferHole); + }), exhaustMap((sbAction) => { + if (!sbAction) { + return EMPTY; // Don't emit if we didn't do anything + } + logger.debug(`start hoses ${JSON.stringify(sbAction)}`); + const producerConsumer$ = of(sbAction).pipe(mediaProducerEpic(context, fragmentPicker), mediaConsumerEpic(context)); + return race(producerConsumer$, waitFor(shouldAbortFrag(context), (abort) => abort) // If this emits, we'll abort the whole chain + ).pipe(take(1), finalize$1(() => { + AVMediaOptionTypes.forEach((type) => { + rootPlaylistService.updateInflightFrag(rootPlaylistQuery.itemId, type, null, null, null); + }); + })); + })); + }), map(() => { + if (rootPlaylistQuery.getEntity(rootPlaylistQuery.itemId).manualMode) { + return; + } + // Check for automatic / lazy switch + let switchReason = SwitchReason.None; + if (checkAndUpdateViewportInfo(platformService(), mediaQuery, config, logger)) { + switchReason = SwitchReason.PreferredListChanged; + } + let cappedForLowBandwidth = false; + const enabledOption = rootPlaylistQuery.enabledVariantMediaOption; + if (gotLowBw(rootPlaylistQuery, mediaQuery, rootPlaylistService)) { + switchReason = SwitchReason.LowBandwidth; + // Only add a cap if there is not already one in place + if (rootPlaylistQuery.nextMaxAutoOptionId === NoMediaOption.mediaOptionId) { + // Should uncap automatically if we select a different variant, ensure this is released after handling switch + rootPlaylistService.setNextMaxAutoOptionId(enabledOption.itemId, enabledOption.mediaOptionId); + logger.info(`Capping variant selection to ${enabledOption.mediaOptionId} because switchReason: ${switchReason}`); + cappedForLowBandwidth = true; + } + } + else if (mediaQuery.playbackStarted && gotHighBw(config, rootPlaylistQuery, rootPlaylistService)) { + switchReason = SwitchReason.HighBandwidth; + rootPlaylistService.setNextMinAutoOptionId(enabledOption.itemId, enabledOption.mediaOptionId); + logger.info(`Capping variant selection to above ${enabledOption.mediaOptionId} because switchReason: ${switchReason}`); + } + handleVariantSwitch(switchReason, config, rootPlaylistQuery, mediaQuery, rootPlaylistService); + // Remove any max/min level caps we placed before level selection + if (cappedForLowBandwidth) { + rootPlaylistService.setNextMaxAutoOptionId(enabledOption.itemId, NoMediaOption.mediaOptionId); + } + else if (switchReason === SwitchReason.HighBandwidth) { + rootPlaylistService.setNextMinAutoOptionId(enabledOption.itemId, NoMediaOption.mediaOptionId); + } + }), finalize$1(() => { + // on (i) active item change, or (ii) after handleErrorActionCommon which will reuse fragmentPicker. + // let fragmentPicker garbage collect automatically. + logger.debug('teardown'); + })); + } + function audioMediaOptionRemoved(rootPlaylistQuery, fromId, toId) { + const fromAlternate = fromId === 'Nah' ? null : rootPlaylistQuery.alternateMediaOptionById(MediaOptionType.AltAudio, fromId); + const fromIdHasAudio = Boolean(fromAlternate && fromAlternate.url); + const toAlternate = fromId === 'Nah' ? null : rootPlaylistQuery.alternateMediaOptionById(MediaOptionType.AltAudio, toId); + const toIdHasAudio = Boolean(toAlternate && toAlternate.url); + // rename function to audioMediaOptionRemovedOrAdded if uncommenting following line. + // return fromIdHasAudio !== toIdHasAudio; + return fromIdHasAudio && !toIdHasAudio; + } + /** + * @brief Handle option switch at start of pipeline + * Called within pipeline as an action before fetch, as part of switching the enabled option + * May be called before or after destination option has actually fully loaded + */ + function pipelineHandleOptionSwitch(libContext, statsQuery, mediaOptionType, fromId, toId) { + var _a, _b; + const { rootPlaylistQuery, rootPlaylistService, mediaSink, mediaParser, logger, config, iframeMachine } = libContext; + const meQuery = mediaSink.mediaQuery; + if (!fromId || !toId || (fromId === toId && (mediaOptionType === MediaOptionType.AltAudio || !iframeMachine.isStarted))) { + // if iframeMachine.isStarted, must check for trickplay exit condition + return VOID; + } + switch (mediaOptionType) { + case MediaOptionType.Variant: + { + if (fromId !== toId) { + mediaParser.reset(MediaOptionType.Variant); + } + const sbType = mediaOptionTypeToSourceBufferType(mediaOptionType); + const fromVariant = rootPlaylistQuery.variantMediaOptionById(fromId); + const toVariant = rootPlaylistQuery.variantMediaOptionById(toId); + const itemId = rootPlaylistQuery.itemId; + if (toVariant == null || fromVariant == null) { + return VOID; + } + const resumingFromTrickPlay = !toVariant.iframes && iframeMachine.isStarted; + if (fromVariant.iframes !== toVariant.iframes || resumingFromTrickPlay) { + mediaSink.toggleTrickPlaybackMode(toVariant.iframes); + if (resumingFromTrickPlay) { + if (!isFiniteNumber(mediaSink.mediaQuery.postFlushSeek)) { + mediaSink.postFlushSeek = iframeMachine.iframeClockTimeSeconds; + } + iframeMachine.stop(); + } + logger.info(`pause & flush for iframeMode transition [0, Infinity] postFlushSeek=${mediaSink.mediaQuery.postFlushSeek} resumingFromTrickPlay=${resumingFromTrickPlay}`); + mediaSink.pause(); + // (2.1a) Flush audio and video when resuming from trickplay to avoid fpicker getting stuck with audio gaps after rewind + const postTrickPlayFlush$ = resumingFromTrickPlay ? mediaSink.flushAll(0, Infinity, true) : mediaSink.flushData(sbType, 0, Infinity, true); + return postTrickPlayFlush$.pipe(tap(() => { + const postFlushSeek = mediaSink.mediaQuery.postFlushSeek; + if (isFiniteNumber(postFlushSeek)) { + rootPlaylistService.setPendingSeek(itemId, postFlushSeek); + } + })); + } + // From stream-controller.ts attemptSafeNextLevelSwitch + if (!config.allowFastSwitchUp || toVariant.iframes) { + return VOID; + } + const fromQuery = createMediaLibraryQuery(fromVariant); + const fromDetails = fromQuery.mediaOptionDetails; + if (fromDetails != null && toVariant != null && fromVariant.bitrate < toVariant.bitrate) { + const fromTargetDuration = fromDetails.targetduration; + const mediaLibraryQuery = createMediaLibraryQuery(toVariant); + const toDetails = mediaLibraryQuery.mediaOptionDetails; + const lastUpdateMs = mediaLibraryQuery.mediaOptionDetailsEntity.lastUpdateMillis; + const currentWaterLevel = meQuery.getCurrentWaterLevelByType(sbType, config.maxBufferHole); + const minBufferAhead = minSwitchBufferAheadSec(fromVariant, toVariant, toDetails, fromTargetDuration, rootPlaylistQuery.abrStatus, statsQuery, config, lastUpdateMs, logger) + config.maxStarvationDelay; // add a safety delay of maxStarvationDelay + logger.info(`fastSwitchUp: currentWaterLevel:${currentWaterLevel} minBufferAhead:${minBufferAhead}`); + // Flush from somewhere past midpoint of first segment after minFlushStart position + const minFlushStart = meQuery.currentTime + minBufferAhead; + const segment = (_b = (_a = meQuery.sourceBufferEntityByType(sbType)) === null || _a === void 0 ? void 0 : _a.bufferedSegments) === null || _b === void 0 ? void 0 : _b.find((seg) => seg.startPTS >= minFlushStart); + // Flush position will remove half to a quarter (at least maxFragLookUpTolerance) of the buffered segment + // to avoid gaps left by appening new segments with dropped samples up to the first IDR frame + let flushStart; + if (segment) { + const duration = segment.endPTS - segment.startPTS; + flushStart = segment.startPTS + Math.min(Math.max(duration - config.maxFragLookUpTolerance, duration * 0.5), duration * 0.75); + } + if (isFiniteNumber(flushStart) && currentWaterLevel >= minBufferAhead) { + // Note: old code had special handling for live to preserve fragPrevious + logger.info(`flush for fast switchup [${flushStart.toFixed(3)}, Infinity]`); + return mediaSink.flushData(sbType, flushStart, Infinity); + } + } + } + break; + case MediaOptionType.AltAudio: + if (audioMediaOptionRemoved(rootPlaylistQuery, fromId, toId)) { + // altAudio -> muxed handling is tricky because: + // when hls switches audio, the new altAudio mediaOption (toId) has no url; audio is muxed with video. + // During switching, getDetailsAndSwitchContext(altAudio) will ignore toId because it has no mediaOptionDetails too. + // As a result, chooseAndFetchFragment will never sees toId's switchContext to trigger any audio switching logic. + // Cleaner to shortcircuit the process: reset the mediaSource now, reload and seek with only the muxed mediaOption. + logger.info(`alt audio removed/added while switching fromId ${fromId} to toId ${toId} @ ${meQuery.currentTime}`); + rootPlaylistService.setEnabledMediaOptionSwitchContextByType(rootPlaylistQuery.itemId, MediaOptionType.AltAudio, toId, undefined); + mediaSink.resetMediaSource(meQuery.currentTime); + } + mediaParser.reset(MediaOptionType.AltAudio); + break; + } + return VOID; + } + function checkAndUpdateViewportInfo(platformService, mediaQuery, config, logger) { + var _a; + const clientWidth = mediaQuery === null || mediaQuery === void 0 ? void 0 : mediaQuery.clientWidth; + const clientHeight = mediaQuery === null || mediaQuery === void 0 ? void 0 : mediaQuery.clientHeight; + const devicePixelRatio = typeof window === 'object' && window.devicePixelRatio ? window.devicePixelRatio : 1; + const newViewportInfo = clientWidth && clientHeight ? { width: clientWidth * devicePixelRatio, height: clientHeight * devicePixelRatio } : undefined; + const curViewportInfo = ((_a = platformService.getQuery()) === null || _a === void 0 ? void 0 : _a.viewportInfo) || {}; + const isViewportSizeChanged = curViewportInfo && newViewportInfo && (curViewportInfo.width !== newViewportInfo.width || curViewportInfo.height !== newViewportInfo.height); + if (config.useViewportSizeForLevelCap && isViewportSizeChanged) { + platformService.updateViewportInfo(newViewportInfo); + logger.info(`ViewportSize changed, old: ${JSON.stringify(curViewportInfo)}, new: ${JSON.stringify(newViewportInfo)}`); + return true; + } + return false; + } + // AV Producer should work as follows: + // 1. wait for details for enabled variant & alt audio + // 2. choose fragment(s) for the position if < highWaterLevelSeconds + // 3. fetch & parse candidate fragments(s) + // 4. on parse, check if we need to re-select any fragment (ptsKnown changed) + // 5. advance discontinuity if needed + // 6. fetch & parse any new candidate fragment(s) + const mediaProducerEpic = (pipelineContext, fragmentPicker) => (bufferInfoAction$) => { + const { rootPlaylistQuery: rootQuery, mediaSink } = pipelineContext; + const logger = pipelineContext.logger.child(avPipeLogName); + return bufferInfoAction$.pipe(observeOn(asyncScheduler), withLatestFrom(rootQuery.enabledMediaOptionKeys$), + // Wait for details and switch context + switchMap(([action, enabledOptionKeys]) => { + return zip(getDetailsAndSwitchContext(action, MediaOptionType.Variant, pipelineContext, enabledOptionKeys).pipe(tap((detailsAndContext) => { + var _a, _b; + const varEntity = detailsAndContext.detailsEntity; + if (!varEntity.mediaOptionDetails.liveOrEvent || varEntity.mediaOptionDetails.ptsKnown) { + const newDuration = varEntity.playlistDuration; + // When AltAudio buffer is ahead of Variant and AltAudio buffer end is slightly greater than playlistDuration + // then setting mediaSink.msDuration based on variant alone will lead to mediaSource buffer trucation to set duration. + // This behavior is seen on Chrome browser, Roku & LG + const variantBufferEnd = ((_a = action.bufferInfoTuple[0]) === null || _a === void 0 ? void 0 : _a.buffered.end) || 0; + const altAudioBufferEnd = ((_b = action.bufferInfoTuple[1]) === null || _b === void 0 ? void 0 : _b.buffered.end) || 0; + const maxBufferEnd = Math.max(variantBufferEnd, altAudioBufferEnd); + mediaSink.msDuration = isFiniteNumber(mediaSink.msDuration) ? Math.max(mediaSink.msDuration, newDuration, maxBufferEnd) : newDuration; + } + })), getDetailsAndSwitchContext(action, MediaOptionType.AltAudio, pipelineContext, enabledOptionKeys)).pipe(map((detailsAndContext) => ({ action, detailsAndContext }))); + }), + // Choose, fetch, & parse fragments: + switchMap(({ action, detailsAndContext }) => chooseAndFetchFragments(logger, fragmentPicker, pipelineContext, action, detailsAndContext)), tag('mediaProducerEpic.emit')); + }; + function playlistIsValid(mediaOptionDetails, lastUpdateMillis) { + return !mediaOptionDetails.liveOrEvent || !mediaOptionDetails.ptsKnown || !likelyOutOfDate(mediaOptionDetails === null || mediaOptionDetails === void 0 ? void 0 : mediaOptionDetails.totalduration, 0, lastUpdateMillis); + } + function getDetailsAndSwitchContext(bufferInfoAction, type, pipelineContext, enabledMediaOptionKeys) { + const { rootPlaylistQuery, mediaLibraryService, gaplessInstance, config } = pipelineContext; + // Note we don't expect enabled option to change while we're in this hose because of makeAVPipe. + const option = enabledMediaOptionKeys[type]; + const logger = pipelineContext.logger.child({ name: MediaOptionNames[type] }); + if (!option || option.mediaOptionId === 'Nah') { + return of({ detailsEntity: null, switchContext: null }); + } + const playlistQuery = mediaLibraryService.getQueryForOption(option); + return combineLatest([ + of(bufferInfoAction), + playlistQuery.mediaOptionDetailsEntity$.pipe(distinctUntilChanged((a, b) => (a === null || a === void 0 ? void 0 : a.lastUpdateMillis) === (b === null || b === void 0 ? void 0 : b.lastUpdateMillis)), filter((mediaOptionDetailsEntity) => { + const mediaOptionDetails = mediaOptionDetailsEntity === null || mediaOptionDetailsEntity === void 0 ? void 0 : mediaOptionDetailsEntity.mediaOptionDetails; + if (!mediaOptionDetails) { + return true; // audio/muxed content + } + const now = performance.now(); + const lastUpdateMillis = mediaOptionDetailsEntity.lastUpdateMillis || now; + const liveOrEvent = mediaOptionDetails.liveOrEvent; + const targetDuration = mediaOptionDetails.targetduration; + // logger.info(`playlist updated at ${lastUpdateMillis}, current time is ${now}, delta is ${(now - lastUpdateMillis) / 1000} gt/lt ${2 * targetDuration}`); + // Ensure live mediaOptionDetails is not stale from a previous use of this level. + return !liveOrEvent || now - lastUpdateMillis < config.livePlaylistUpdateStaleness * targetDuration * 1000; + })), + ]).pipe(filter(([bufferInfoAction, detailsEntity]) => { + var _a, _b; + logger.info(`details entity ${(_a = detailsEntity === null || detailsEntity === void 0 ? void 0 : detailsEntity.mediaOptionDetails) === null || _a === void 0 ? void 0 : _a.mediaOptionId} updated`); + if (type === MediaOptionType.AltAudio && !rootPlaylistQuery.altMediaOptionHasValidUrl(type, option.mediaOptionId)) { + logger.info('manifest may have unused alternate audio options'); + return true; + } + const mediaOptionDetails = detailsEntity === null || detailsEntity === void 0 ? void 0 : detailsEntity.mediaOptionDetails; + // Old code tries to save effort by blocking variant if it has already loaded the last segment. + // but altAudio may backtrack to media.currentTime when user switches audio. + // In which case, hls needs both variant and altaudio to emit getDetailsAndSwitchContext @ media.currentTime + // const bufferedSegments = bufferInfoAction.bufferInfoTuple[type]?.bufferedSegments ?? []; + return (mediaOptionDetails != null && playlistIsValid(mediaOptionDetails, (_b = detailsEntity.lastUpdateMillis) !== null && _b !== void 0 ? _b : 0) + // && (!lastFragmentBuffered(mediaOptionDetails, bufferedSegments, rootPlaylistQuery.itemStartOffset, gaplessInstance.inGaplessMode)) + ); + }), take(1), withLatestFrom(rootPlaylistQuery.enabledMediaOptionSwitchContextsByType$(type)), map(([[, detailsEntity], switchContext]) => { + var _a, _b, _c; + const details = detailsEntity === null || detailsEntity === void 0 ? void 0 : detailsEntity.mediaOptionDetails; + if (details) { + const lastFrag = details.fragments[details.fragments.length - 1]; + logger.info(`${details.mediaOptionId} got details MSNs:[${details.startSN},${details.endSN}] range=[${(_b = (_a = details.fragments[0]) === null || _a === void 0 ? void 0 : _a.start) === null || _b === void 0 ? void 0 : _b.toFixed(3)},${(_c = ((lastFrag === null || lastFrag === void 0 ? void 0 : lastFrag.start) + (lastFrag === null || lastFrag === void 0 ? void 0 : lastFrag.duration))) === null || _c === void 0 ? void 0 : _c.toFixed(3)}]`); + } + return { detailsEntity, switchContext }; + })); + } + function chooseAndFetchFragments(logger, fragmentPicker, pipelineContext, action, detailsAndContext) { + var _a, _b; + const { mediaSink, iframeMachine, rootPlaylistQuery } = pipelineContext; + const detailsTuple = [detailsAndContext[MediaOptionType.Variant].detailsEntity.mediaOptionDetails, (_b = (_a = detailsAndContext[MediaOptionType.AltAudio]) === null || _a === void 0 ? void 0 : _a.detailsEntity) === null || _b === void 0 ? void 0 : _b.mediaOptionDetails]; + let chosenFrags = fragmentPicker.getNextFragments(action, detailsTuple, logger); + logger.info(`chosen frags=${JSON.stringify(chosenFrags.map((x) => { + var _a; + return ({ + foundFrag: fragPrint((_a = x === null || x === void 0 ? void 0 : x.foundFrag) === null || _a === void 0 ? void 0 : _a.mediaFragment), + nextDisco: x === null || x === void 0 ? void 0 : x.nextDisco, + newMediaRootTime: x === null || x === void 0 ? void 0 : x.newMediaRootTime, }); - const l = s.getQueryForOption(o); - return Mr([$i(e), l.mediaOptionDetailsEntity$.pipe(Is((e, t) => (null == e ? void 0 : e.lastUpdateMillis) === (null == t ? void 0 : t.lastUpdateMillis)), ln(e => { - var t = null == e ? void 0 : e.mediaOptionDetails; - if (!t) return !0; - var i = performance.now(), - r = e.lastUpdateMillis || i, - e = t.liveOrEvent, - t = t.targetduration; - return !e || i - r < a.livePlaylistUpdateStaleness * t * 1e3 - }))]).pipe(ln(([, e]) => { - if (i === gu.AltAudio && !n.altMediaOptionHasValidUrl(i, o.mediaOptionId)) return !0; - var t = null == e ? void 0 : e.mediaOptionDetails; - return null != t && (e = null !== (e = e.lastUpdateMillis) && void 0 !== e ? e : 0, !t.liveOrEvent || !t.ptsKnown || !Yg(null == t ? void 0 : t.totalduration, 0, e)) - }), Ds(1), bo(n.enabledMediaOptionSwitchContextsByType$(i)), hr(([ - [, e], t - ]) => ({ - detailsEntity: e, - switchContext: t - }))) - } - - function fy(e, t, i, r) { - const { - mediaLibraryService: n, - rootPlaylistQuery: s, - mediaSink: a - } = t, o = s.itemId; - null != r && (a.mediaQuery.isIframeRate || !r.iframeMode ? i && !ne(i[1].iframeMediaDuration) && (performance.now(), n.updatePTSDTS(o, i[1].mediaOptionId, r, i[1])) : e.warn("updatePTSDTS iframeMode mismatch")) - } - const my = t => e => { - const { - rootPlaylistQuery: g, - rootPlaylistService: y, - mediaSink: v, - legibleSystemAdapter: o, - statsService: l, - rtcService: d - } = t; - return e.pipe(tc("mediaConsumerEpic.in"), La(e => { - if (!e) return $i(!1); - const { - appendDataTuple: r, - inFlightFrags: m, - initPTSInfo: t - } = e, i = t["offsetTimestamp"]; - return m.forEach((e, t) => { - e && y.updateInflightFrag(e.itemId, t, e, "appending", null) - }), r.forEach(e => { - e && (e = e.dataSeg, o.addLegibleSamples(i, e.captionData, e.id3Samples, e.endPts)) - }), v.appendData(r, (e, t, i, r, n) => { - var s, a, o, l, d, u, c, h, p, f = null !== (f = m[t].targetDuration) && void 0 !== f ? f : 10; - return s = v, a = e, o = t, l = i, d = f, u = r, c = g, h = y, p = n, e => e.pipe(Za(() => { - h.updateConsecutiveTimeouts(c.itemId, o, !1, "append") - }), va(e => e.pipe(jr((e, t) => { - var i = e instanceof df && e.isTimeout; - if (h.updateConsecutiveTimeouts(c.itemId, o, i, "append"), i) return function(e, t, i, r, n, s, a, o) { - let l = { - errorAction: Cm.SendAlternateToPenaltyBox, - errorActionFlags: 0 - }; - var d = s.getCurrentWaterLevel(i.maxBufferHole), - u = d < i.almostDryBufferSec; - let c = NaN; - s = i.appendErrorMaxRetry, i = a.rootPlaylistEntity.errorsByType[r].timeouts.append; - u && s <= i || s <= t ? l.errorAction = Cm.SendEndCallback : c = 1e3 * d; - s = { - retryDelayMs: c, - maxNumRetry: s, - maxRetryDelayMs: c - }; - return l = Jm(l, !1, e.response.code, n, r, a, o), rg(e, t, s, l, a, o, r, n).pipe() - }(e, t, u, o, l, p, c, h); - if (e instanceof uf) return function(e, t, i, r, n, s, a, o, l, d) { - var u = t.type, - u = o.getCurrentWaterLevelByType(u, n.maxBufferHole); - if (u >= n.almostDryBufferSec && !o.isIframeRate) { - const t = 1e3 * r, - n = { - errorAction: Cm.RetryRequest, - errorActionFlags: 0 - }; - return 1e3 * u < t && (d.hasFallbackMediaOptionTuple(l, s, a, !1) ? n.errorAction = Cm.SendAlternateToPenaltyBox : n.errorAction = Cm.SendEndCallback), rg(e, i, { - retryDelayMs: t, - maxNumRetry: 1 / 0, - maxRetryDelayMs: t - }, n, l, d, s, a) - } - return i < n.appendErrorMaxRetry ? t.remove(0, Number.POSITIVE_INFINITY) : (e.fatal = !0, Vi(e)) - }(e, a, t, d, u, o, l, p, c, h); - if (e instanceof hf) { - const { - mediaOptionType: a, - mediaOptionId: o - } = e; - return oy(e, a, o, s, h, c) - } - throw e - })))) - }, g.highestVideoCodec).pipe(hr(e => { - m.forEach((e, t) => { - e && y.updateInflightFrag(e.itemId, t, e, "appended", null) - }); - var t = e.filter(e => (null == e ? void 0 : e.fragmentType) === gu.Variant); - t.length && (l.setBufferMetric(t[0]), null == d || d.handleFragBuffered(t[0])); - e = r[yu.AltAudio]; - if (null !== (t = null == e ? void 0 : e.dataSeg) && void 0 !== t && t.flushBeforeAppend || ne(null === (t = null == e ? void 0 : e.dataSeg) || void 0 === t ? void 0 : t.switchPosition)) { - const { - itemId: i, - mediaOptionId: r - } = e.dataSeg; - y.setEnabledMediaOptionSwitchContextByType(i, gu.AltAudio, r, void 0) - } - return !0 - }), (n = v, s = y, a = g, e => e.pipe(Vn(e => { - if (e instanceof lf) { - var { - mediaOptionType: t, - mediaOptionId: i - } = e; - return oy(e, t, i, n, s, a) - } - throw e - })))); - var n, s, a - })) - }; - - function gy(d, u, c, h, p, f) { - return u = u.child({ - name: "seek" - }), e => e.pipe(La((e, t) => { - if (null == e) return Ii; - var i, r, n, s, a, o, l = c.mediaQuery.seekTo$.pipe(ka(1), Kp()); - return i = 0 === t, r = d, n = u, s = p, t = f, (null == (e = e) ? Ii : e instanceof Date ? (a = e, o = t, s.enabledMediaOptionByType$(gu.Variant).pipe(La(e => o.getQueryForOption(e).mediaOptionDetails$), hr(e => function(e, t) { - if (!e || 0 === e.length) return 0; - const i = [...e].sort((e, t) => t[0] - e[0]), - r = t.getTime(), - n = null !== (t = i.find(([e]) => r >= e)) && void 0 !== t ? t : i[i.length - 1], - [s, a] = n, - o = a + (r - s) / 1e3; - return Math.max(0, o) - }(e.dateMediaTimePairs, a)), Ds(1))) : function(r, e, a, n, t, o) { - let i = n.enabledMediaOptionByType$(gu.Variant).pipe(La(e => t.getQueryForOption(e).mediaOptionDetails$), ln(e => ne(null == e ? void 0 : e.totalduration)), Ds(1), hr(s => { - var e = !s.liveOrEvent, - t = s.totalduration, - i = n.itemStartOffset; - return e ? ne(r) ? 0 <= r ? r : i + (t + r) : i + (ne(s.startTimeOffset) ? s.startTimeOffset : 0) : !ne(r) || r < 0 || 0 === r && a.liveEdgeForZeroStartPositon ? yg(0, s, a) : function(e, t) { - let i = e; - var r = s.fragments[0].start, - n = s.fragments[s.fragments.length - 1].start + s.fragments[s.fragments.length - 1].duration; - return e < r ? i = r : n < e && (i = yg(0, s, a)), (e < r || n < e) && t.warn(`[live] sanitizeLiveSeek seekTo:${se(e,3)}, sanitizedSeek:${se(i,3)}, liveWindowStart:${se(r,3)}, liveWindowEnd:${se(n,3)}`), i - }(r, o) - })); - return e && (i = i.pipe(Za(e => {}))), i - }(e, i, r, s, t, n)).pipe(Vs(() => { - h.setPendingSeek(p.itemId, void 0) - }), Va(l)) - }), Za(e => { - ne(e) && (c.seekTo = e) - })) - } - - function yy(e) { - const { - logger: r, - rootPlaylistService: n, - rootPlaylistQuery: t - } = e, s = t.itemId; - return e => e.pipe(va(e => e.pipe(jr(e => { - if (r.error(`Got error in pipeline ${e.message} fatal:${null==e?void 0:e.fatal} handled:${null==e?void 0:e.handled}`), !(e instanceof p) || e.fatal) throw e; - return e.handled ? (t = n, i = s, bn(0).pipe(hr(() => { - t.updateEnabledMediaOptions(i) - }))) : Ii; - var t, i - })))) - } - const vy = () => e => e.pipe(tc("mediaFragmentPipelineEpic.in"), La(i => { - if (!i) return Ii; - const { - logger: e, - config: t, - platformService: r, - rootPlaylistService: n, - rootPlaylistQuery: s, - keySystemAdapter: a, - mediaSink: o, - mediaParser: l, - gaplessInstance: d, - mediaLibraryService: u - } = i, c = s["itemId"], h = o["mediaQuery"], p = a.keyStatusChange$.pipe((f = i, e => e.pipe(La(e => { - const { - decryptdata: t, - status: i, - error: r - } = e, n = f["rootPlaylistQuery"]; - if ("needs-renewal" === i) return _g(f, t, null); - if ("error" !== i || !(r instanceof gc || r instanceof mc) || r.handled) return Ii; { - const { - rootPlaylistService: e, - keySystemAdapter: t - } = f; - return og(r, 0, null, e, n, t.ksQuery) - } - }), La(() => Ii)))); - var f; - const m = r.getQuery(), - g = m.displaySupportsHdr$.pipe(Is(), La(e => (n.setHDRPreference(c, e, !0), Ii))), - y = m.viewportInfo$.pipe(Is((e, t) => e && t && e.width === t.width && e.height === t.height), Za(e => { - t.useViewportSizeForLevelCap && n.setViewportInfo(c, e) - }), $a(Ii)), - v = ed([s.hdrMode$.pipe(Is()), s.maxHdcpLevel$.pipe(Is())]).pipe(La(([]) => (d.inGaplessMode || 0 !== s.itemStartOffset || (o.resetMediaSource(), l.reset()), Ii))), - S = an(function(i) { - const { - rootPlaylistQuery: t, - mediaSink: r - } = i, e = t.enabledMediaOptions$.pipe(Kp(), Za(e => { - e = e[gu.AltAudio], e = _u(e) && null != (null == e ? void 0 : e.url) ? 2 : 1; - r.setExpectedSbCount(e) - })), n = en([t.enabledMediaOptionByType$(gu.Variant).pipe(ln(e => _u(e)), La(e => Dg(e).mediaOptionDetails$), Ds(1)), Gu(r.mediaQuery.updating$, e => e)]).pipe(ji(tr), Za(([e]) => { - r.bufferMonitorTargetDuration = e.targetduration - })), s = Fu.map(e => t.enabledMediaOptionByType$(e).pipe(tc("mediaOptionRetrieve.switch"), La(t => { - if (!t || !t.url || !_u(t)) return Ii; - var e = r.mediaQuery.desiredRate$.pipe(hr(e => 0 !== e), Is()); - return Mg(i, t).pipe(tc("mediaOptionRetrieve.first"), $a(e), La(e => e ? function e(t, i) { - const r = t.mediaLibraryService; - return function(e) { - if (!e) return Ii; - var { - mediaOptionDetails: t, - lastUpdateMillis: i, - unchangedCount: e - } = e; - if (null == t || !t.liveOrEvent) return Ii; - if (bg(t, i)) return bn(0).pipe(Za(() => {})); - let r = Sg(t); - return 0 < e && (r /= 2, r = Math.max(r, 5e3)), r -= performance.now() - i, r += 0, r = Math.max(1e3, Math.round(r)), bn(r).pipe(Za(() => {})) - }(r.getQueryForOption(i).mediaOptionDetailsEntity).pipe(La(() => Mg(t, i, !1, !0)), La(() => e(t, i))) - }(i, t) : Ii)) - }))); - return an(e, n, an(...s)).pipe($a(Ii)) - }(i), cy(i), function(t) { - const { - rootPlaylistQuery: i, - mediaSink: e - } = t; - return Gu(e.mediaQuery.mediaElementEntity$, e => e).pipe(La(e => i.anchorTime$.pipe(tc("anchorTime.subtitle.in"), ln(e => ne(null == e ? void 0 : e.pos)), (s => e => { - const { - rootPlaylistQuery: i, - rootPlaylistService: t, - legibleSystemAdapter: r - } = s, n = i.enabledAlternateMediaOptionByType(gu.Subtitle); - if (r.gotTracks) r.selectedTrack = n; - else { - const s = i.preferredMediaOptions[gu.Subtitle]; - r.setTracks(s, n, i.getDisabledMediaOption(gu.Subtitle)) - } - return e.pipe(tc("subtitleEpic.select.in"), La(() => an(r.nativeSubtitleTrackChange$.pipe(La(e => (e.mediaOptionId !== r.selectedMediaOption.mediaOptionId && t.setEnabledMediaOptionByType(e.itemId, gu.Subtitle, e), Ii))), i.enabledMediaOptionByType$(gu.Subtitle).pipe(hr(e => { - const t = _u(e) ? i.alternateMediaOptionById(gu.Subtitle, e.mediaOptionId) : e; - return r.selectedMediaOption = t, t - })).pipe(Is((e, t) => (null == e ? void 0 : e.mediaOptionId) === (null == t ? void 0 : t.mediaOptionId))))), tc("subtitleEpic.select.emit")) - })(t), (s => e => { - const { - mediaSink: t, - rootPlaylistQuery: i, - legibleSystemAdapter: r, - logger: n - } = s; - return e.pipe(tc("subtitleEpic.process.in"), La(e => { - if (!e || !e.url || !_u(e)) return $i([null, null, null]); - return ed([Dg(e).mediaOptionDetails$, i.discoSeqNum$.pipe(ln(e => ne(e)))]).pipe(La(([e, t]) => ((i, r, e) => { - const { - legibleSystemAdapter: n, - rootPlaylistQuery: t - } = i; - return t.initPTS$(e).pipe(La(t => !t || t.iframeMode ? on : n.findFrags$(r, e).pipe(La(e => r && (null == e ? void 0 : e.foundFrags) ? Sy(i, t.offsetTimestamp, e, r) : Wu)))) - })(s, e, t))) - }), tc("subtitleEpic.process.emit")) - })(t)))) - }(i), p).pipe(Zs(void 0), yy(i)), - b = h.seekTo$.pipe(ln(e => ne(null == e ? void 0 : e.pos)), Is((e, t) => Math.abs(e.pos - t.pos) < Number.EPSILON), La(e => (n.setAnchorTime(c, e), Ii))), - T = h.gotPlaying$.pipe(ln(e => e), Za(e => { - s.mediaOptionListQueries[gu.Variant].filteredMediaOptionList.forEach(e => {}) - }), Ds(1), $a(Ii)); - return an(s.pendingSeek$.pipe(gy(t, e, o, n, s, u)), function() { - const { - config: n, - mediaSink: s, - rootPlaylistQuery: e, - mediaLibraryService: t - } = i, a = (i.logger.child({ - name: "live" - }), s.mediaQuery); - return e.enabledMediaOptionByType$(gu.Variant).pipe(ln(_u), La(e => t.getQueryForOption(e).mediaOptionDetailsEntity$.pipe(ln(e => { - var t; - return (null === (t = null == e ? void 0 : e.mediaOptionDetails) || void 0 === t ? void 0 : t.ptsKnown) && e.mediaOptionDetails.liveOrEvent - }), Is((e, t) => (null == e ? void 0 : e.lastUpdateMillis) === (null == t ? void 0 : t.lastUpdateMillis)))), hr(e => { - var t = e.mediaOptionDetails, - i = a.currentTime; - a.msDuration < e.playlistDuration ? s.msDuration = e.playlistDuration : ne(s.msDuration) && (s.msDuration = s.msDuration + n.livePlaylistDurationNudge); - let r = NaN; - return i < vg(i, t, e.lastUpdateMillis, n.maxBufferHole, a) && (r = yg(t.fragments[0].start, t, n), s.seekTo = r), r - })) - }(), b, S, g, y, v, function() { - const e = i.mediaSink.mediaQuery; - return Mr([$i(i), e.desiredRate$.pipe(ha())]).pipe(La(([e, [t, i]]) => { - const { - rootPlaylistQuery: s, - rootPlaylistService: r, - config: a, - mediaSink: n, - mediaLibraryService: o, - statsService: l - } = e, d = n.mediaQuery; - if (Wp(t) !== Wp(i)) iy(Mm.IframeModeChange, a, s, d, r); - else if (0 === t && 1 === i && !Bu.every(e => { - const t = s.enabledMediaOptionKeys[e], - i = o.getQueryForOption(t), - r = l.getQueryForItem(s.itemId), - n = i.mediaOptionDetailsEntity; - return !(null !== (e = null == n ? void 0 : n.mediaOptionDetails) && void 0 !== e && e.ptsKnown) || d.canContinuePlaybackWithoutGap(n.mediaOptionDetails, n.lastUpdateMillis, r.getPlaylistEstimate(), a.maxBufferHole) - })) return n.pause(), n.flushAll(0, 1 / 0, !0); - return Ii - }), $a(Ii)) - }(), function(e) { - const t = e.rootPlaylistQuery, - i = e.mediaSink.mediaQuery, - r = t.enabledMediaOptionByType$(gu.Variant); - return Mr([$i(e), i.desiredRate$.pipe(ha())]).pipe(Is((e, t) => e[1] === t[1]), bo(r), La(([ - [e, [t, i]], r - ]) => { - t = Wp(t), i = Wp(i); - if (t === i) return Ii; - const n = e["rootPlaylistService"]; - return i && e.rootPlaylistQuery.nextMaxAutoOptionId === Lu.mediaOptionId && n.setNextMaxAutoOptionId(e.rootPlaylistQuery.itemId, r.mediaOptionId), Ii - })) - }(i), T).pipe(tc("mediaFragmentPiplineEpic.emit"), Zs(void 0)) - })), - Sy = (r, e, t, i) => { - const n = r.legibleSystemAdapter, - s = t.foundFrags; - return Fr(s).pipe(hr(t => { - return ((e, t, i) => { - const { - rootPlaylistQuery: r, - legibleSystemAdapter: n - } = e; - return Zr(() => ((t, i) => Rg(e, i, !1, !1).pipe(hr(e => ({ - initPTS: t, - data: e, - mediaFragment: i - })), tc("retrieveSubtitleFragmentCacheEntity.emit")))(t, i).pipe(hr(({ - initPTS: e, - data: t, - mediaFragment: i - }) => ({ - frag: i, - cueRange: function(e, t, i, r, n) { - if (e) return n.processSubtitleFrag(e, t, i, r) - }(r.enabledAlternateMediaOptionByType(gu.Subtitle), i, e, t, n) - })))) - })(r, e, t).pipe((i = e => n.checkReadyToLoadNextSubtitleFragment$(t, s).pipe(ln(e => e)), function(e) { - return e.lift(new gs(i)) - })); - var i - }), Yr(r.config.vttConcurrentLoadCount), Za(e => { - n.reviewParsedFrag(e, t, i) !== kp.CloseEnough && r.legibleSystemAdapter.tryAgain$.next(!0) - })) - }, - by = (e, t) => { - let i, r = ""; - return i = e.videoCodec && e.audioCodec ? (r = `${e.videoCodec}, ${e.audioCodec}`, t = null != t ? t : "video/mp4", "audiovideo") : e.videoCodec ? (r = `${e.videoCodec}`, t = null != t ? t : "video/mp4", "video") : (r = `${null!==(e=e.audioCodec)&&void 0!==e?e:""}`, t = null != t ? t : "audio/mp4", "audio"), { - mimeType: `${t};codecs=${r}`, - codec: r, - container: t, - type: i - } - }; - class Ty { - constructor(e, t, i) { - this.config = e, this.logger = t, this.demuxClient = i, this.typeSupported = { - mp4: MediaSource.isTypeSupported("video/mp4"), - mpeg: MediaSource.isTypeSupported("audio/mpeg"), - mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'), - ac3: MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"'), - ec3: MediaSource.isTypeSupported('audio/mp4; codecs="ec-3"') - }, this.demuxers = [], this.lastInitFrags = [], this.lastFrags = [] - } - parseInitSegment(h, e) { - return this.getDemuxerInfo(h, this.lastInitFrags, e, this.demuxClient).pipe(La(({ - demuxer: e, - contiguous: t, - trackSwitch: i, - discontinuity: r, - accurateTimeOffset: n - }) => { - const s = h["frag"], - { - keyTagInfo: a, - start: o, - mediaOptionType: l - } = s; - if (this.lastInitFrags[l] = s, h.initSegment) { - const e = ze.remuxInitSegment(new Uint8Array(h.initSegment), this.logger, a), - t = Ze.parseInitSegment(e), - { - mimeType: i, - type: r, - codec: n, - container: s - } = by(t); - return $i({ - moovData: t, - mimeType: i, - track: { - type: r, - codec: n, - initSegment: e, - container: s - } - }) - } - const d = h.segment || h.initSegment, - u = d ? h.initSegment : void 0, - c = Oc(e.observer); - return $i(c.event(v.FRAG_PARSING_INIT_SEGMENT).pipe(hr(this.handleInitSegmentData)), c.event(x.INTERNAL_ERROR).pipe(La(this.handleError)), e.pushWithoutTransfer(d, a, u, o, r, i, t, h.totalDuration, n, void 0, h.iframeMediaStart, h.iframeDuration).pipe($a(Ii))).pipe(Yr(), Ds(1)) - })) - } - parseSegment(y, e) { - return this.getDemuxerInfo(y, this.lastFrags, e, this.demuxClient).pipe(La(({ - demuxer: e, - contiguous: t, - trackSwitch: i, - discontinuity: r, - accurateTimeOffset: n - }) => { - const { - frag: h, - defaultInitPTS: p - } = y, { - keyTagInfo: s, - start: a, - duration: f, - mediaOptionType: m - } = h; - let g; - this.lastFrags[m] = h; - const o = Oc(e.observer); - return $i(o.event(v.FRAG_PARSING_INIT_SEGMENT).pipe(La(e => { - var t; - return e.track.initSegment.byteLength !== (null === (t = y.initSegment) || void 0 === t ? void 0 : t.byteLength) && (g = this.handleInitSegmentData(e)), Ii - })), o.event(v.FRAG_PARSING_DATA).pipe(hr(e => { - var { - startPTS: t, - startDTS: i, - firstKeyframePts: r, - framesWithoutIDR: n, - dropped: s, - data1: a, - data2: o, - captionData: l, - id3Samples: d - } = e; - let { - endPTS: u, - endDTS: c - } = e; - return null == u && (this.logger.warn(`${Nu[m]} ${Vp(h)}: null endPTS parsed, using duration ${f}`), u = Object.assign(Object.assign({}, t), { - baseTime: t.baseTime + B(f, t.timescale).baseTime - })), null == c && (this.logger.warn(`${Nu[m]} ${Vp(h)}: null endDTS parsed, using duration ${f}`), c = Object.assign(Object.assign({}, i), { - baseTime: i.baseTime + B(f, i.timescale).baseTime - })), ne(y.iframeMediaStart) || function(e, t, i, r) { - let n = NaN, - s = NaN; - if (ne(i)) s = i, n = .01, isFinite(s) && isFinite(r) && (s += r); - else { - { - const o = void 0 - } - } - var { - startPTS: a, - startDTS: i, - endPTS: r, - endDTS: t - } = t; - if (!(0 <= a.baseTime && 0 <= i.baseTime && 0 < e.duration && (null == r || 0 < b(r, a)) && (null == t || 0 < b(t, i)) && (!ne(n) || !ne(s) || Math.abs(S(i) - s) <= n))) throw new D(!1, `Failed demuxer sanity check frag=${Vp(e)} parsed=${JSON.stringify({startPTS:a,endPTS:r,startDTS:i,endDTS:t})} ${ae({expectedStartDTS:s,fudge:n})}`, $.FailedDemuxerSanityCheck) - }(h, e, (p, y.iframeMediaStart), this.config.audioPrimingDelay), { - startPTS: t, - endPTS: u, - startDTS: i, - endDTS: c, - firstKeyframePts: r, - framesWithoutIDR: n, - dropped: s, - data1: a, - data2: o, - captionData: l, - id3Samples: d, - parsedInitSegment: g - } - })), o.event(x.INTERNAL_ERROR).pipe(La(this.handleError)), e.push(y.segment, s, y.initSegment, a, r, i, t, y.totalDuration, n, p, y.iframeMediaStart, y.iframeDuration).pipe($a(Ii))).pipe(Yr(), Ds(1)) - })) - } - reset(e) { - if (null == e) return this.demuxers.forEach(e => { - e && e.destroy() - }), void(this.demuxers = []); - const t = this.demuxers[e]; - null == t || t.destroy(), this.demuxers[e] = null - } - destroy(e) { - null != e ? this.reset(e) : this.reset() - } - willBeTrackSwitch(e, t) { - var { - mediaOptionType: i, - mediaOptionId: e - } = e, i = (t || this.lastFrags)[i]; - return !(i && i.mediaOptionId === e) - } - getDemuxerInfo(e, r, t, i) { - const { - frag: n, - ptsKnown: s, - seeking: a, - live: o - } = e, { - discoSeqNum: l, - mediaSeqNum: d, - mediaOptionType: u - } = n; - return Zr(() => { - var e = this.demuxers[u]; - return e ? $i(e) : i.init(this.typeSupported, this.config, t).pipe(Za(e => this.demuxers[u] = e)) - }).pipe(hr(e => { - var t = r[u], - i = this.willBeTrackSwitch(n, r); - return { - demuxer: e, - trackSwitch: i, - discontinuity: !(t && l === t.discoSeqNum), - contiguous: !!t && !i && t.mediaSeqNum + 1 === d, - accurateTimeOffset: !a && (s || !o) - } - })) - } - handleInitSegmentData(e) { - var t = e["track"], - i = t["initSegment"], - r = Ze.parseInitSegment(i), - { - mimeType: n, - type: s, - codec: a, - container: e - } = by(r, t.container); - return { - moovData: r, - mimeType: n, - track: Object.assign(Object.assign({}, t), { - type: s, - codec: a, - initSegment: i, - container: e - }) - } - } - handleError(e) { - return Vi(e) - } - } - - function Ey(a, e, t, h, p, i, r, n) { - var s = h["combined"], - o = function(e) { - let t = 1 / 0; - h.playingFrag && (t = null !== (s = null === (n = e.fragments[h.playingFrag.mediaSeqNum - e.startSN]) || void 0 === n ? void 0 : n.duration) && void 0 !== s ? s : 1 / 0); - var { - minRequiredStartDuration: i, - maxRequiredStartDuration: r, - startTargetDurationFactor: n - } = a, { - targetduration: s, - averagetargetduration: e - } = e, r = n * Math.min(t, e, s, r); - return Math.max(i, r) - }(t.details); - let l = function(e, t, i) { - const { - pos: r, - combined: n, - playingFrag: s - } = h; - if (0 === n.len) return !1; - var a = t.details, - o = a.fragments; - let l = 0 != p && 1 != p || n.len >= i; - var d = o[a.fragments.length - 1], - t = o[0].start + a.totalduration; - let u = !1; - if (s) { - const c = qu.search(o, e => s.discoSeqNum - e.discoSeqNum); - u = e && s.discoSeqNum !== e.discoSeqNum || null == c || $p(c, s) - } - return l && a.liveOrEvent ? l = r <= t - d.duration : a.liveOrEvent || (l = l || t - i <= r), l = l || u, l - }(e, t, o); - return !l && 0 < s.len && null != e && e.state && (l = function(n, e, t, i, r, s, a) { - var o = null === (l = h.sbTuple[gu.Variant]) || void 0 === l ? void 0 : l.buffered, - l = null === (l = h.sbTuple[gu.AltAudio]) || void 0 === l ? void 0 : l.buffered; - if ((null == o ? void 0 : o.len) >= t && (!l || l.len >= t)) return 0; - if (!(o && n && (d = h.pos, (l = n.start + n.duration) > o.end && (n.start - o.end <= a || n.start <= o.end) && t <= l - d))) return 1 / 0; - var d = n.state; - let u = n.tstart, - c = 0; - switch (d) { - case "loading": - c += function(e, t) { - var { - bwSample: i, - duration: r - } = n; - if (!i) return 1 / 0; - r = ne(i.total) ? 8 * i.total : Math.ceil(r * e), e = 8 * i.loaded, r -= e, i = e / (performance.now() - i.tfirst) * 1e3; - if (!ne(i)) return 1 / 0; - t = t.avgBandwidth; - return r / Math.min(t, i) - }(e, i), u = n.tstart + 1e3 * c; - case "loaded": - case "parsing": - c += function(e, t) { - t = ne(t.avgParseTimeMs) ? t.avgParseTimeMs : 0; - return performance.now() < e ? t / 1e3 : Math.max(0, t - (performance.now() - e)) / 1e3 - }(u, r), u = n.tstart + 1e3 * c; - case "parsed": - case "appending": - c += function(e, t) { - t = ne(t.avgDataFragAppendMs) ? t.avgDataFragAppendMs : 0; - return performance.now() < e ? t / 1e3 : Math.max(0, t - (performance.now() - e)) / 1e3 - }(u, s); - break; - default: - c = 1 / 0 - } - return c - }(e, t.variant.bitrate, o, i, r, n, a.maxBufferHole) <= s.len), l - } - - function Iy(e, t, i, r) { - if (200 === t && r && 10 < r.length) { - if (Rm.isValidPlaylist(r)) return !0; { - const t = new R(o, _, !0, "response doesnt have #EXTM3U tag", $.PlaylistErrorMissingEXTM3U); - throw t.url = e, t - } - } - return !1 - } - const wy = { - name: "pltfrm" - }; - - function Ay(e, t) { - t = Ah.getKeySystemSecurityLevel(t); - return null != e && void 0 !== t[e] - } - - function Oy(e) { - return e.every(e => e.iframes) - } - - function ky(e, t) { - return !ne(e) || !ne(t) || e <= t - } - - function Cy() { - const n = new Set, - s = new Set; - return e => { - const i = (e, t) => { - t = t ? "audio" : "video"; - n.has(e) || s.has(e) || (((e, t) => { - let i = MediaSource.isTypeSupported(`${e}/mp4;codecs=${t}`); - return "mp4a.40.34" !== t || i || (i = MediaSource.isTypeSupported(`${e}/mpeg`)), i - })(t, e) ? n : s).add(e) - }, - t = (e, t) => (i(e, t), s.has(e)); - let r = !1; - return e.audioCodecList && (r = e.audioCodecList.some(e => t(e, !0))), !r && e.videoCodecList && (r = e.videoCodecList.some(e => t(e, !1))), !r - } - } - - function Dy(e, t) { - for (const i in e) - if (e[i].type === t) return e[i]; - return {} - } - - function My(e, t, i) { - t.filter(e => !i.includes(e)).map(e => e.mediaOptionId) - } - - function xy(e, i, s) { - const a = new Map, - r = new Array; - return e.forEach(t => { - var e = Array(); - ! function(e, t, i) { - var r = Wc.getCapabilities(t.videoCodecList, t.audioCodecList), - t = JSON.stringify(r); - let n; - a.has(t) ? n = a.get(t) : (n = Ah.requestKeySystemAccess(e, r, void 0, s).pipe(hr(() => !0), Vn(e => (s.warn(`Request key system error: ${e.message}`), $i(!1))), Oa({ - bufferSize: 1, - refCount: !0 - })), a.set(t, n)), i.push(n) - }(i, t, e); - e = en(e).pipe(hr(e => { - if (void 0 === e.find(e => !1 === e)) return t - })); - r.push(e) - }), en(r).pipe(hr(e => e.filter(e => Boolean(e)))) - } - - function Py(e, r) { - const o = new Set, - l = new Set, - d = !MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.2"; channels="-1"'), - u = d && !MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.2"; channels="2"; features="INVALID"'), - t = e.filter(e => { - let t = !1; - var i; - return e.audioCodecList && e.audioGroupId && (i = fm.getRichestChannelLayoutForGroupId(e.audioGroupId, r), 0 < e.audioCodecList.length && i && (t = ((e, t) => { - var i, r, n, s = be.isDolbyAtmos(e, t); - if (u || d && !s) { - n = `${i=e}/${r=t}`, o.has(n) || l.has(n) || (((e, t) => { - const i = t.split("/"), - r = parseInt(i[0]); - let n, s; - if (1 < i.length) { - const t = i[1].split(",")[0]; - n = `audio/mp4;codecs="${e}";channels="${r}";features="${t}"`, s = `audio/mp4;codecs="${e}";channels="8";features="${t}"` - } else n = `audio/mp4;codecs="${e}";channels="${r}"`; - let a = MediaSource.isTypeSupported(n); - return !a && s && (a = MediaSource.isTypeSupported(s)), a - })(i, r) ? o : l).add(n); - const a = `${e}/${t}`; - return l.has(a) - } - return !!s - })(fm.getRichestAudioCodec(e.audioCodecList), i))), !t - }); - return My(0, e, t), t - } - - function Ry(e, t, l) { - const n = 0 < (null == t ? void 0 : t.length), - i = e.filter(o => { - var e = function() { - if (!l) return { - highestPlayableAverageBitRate: void 0, - highestPlayablePeakBitRate: void 0, - highestPlayableWidth: void 0, - highestPlayableHeight: void 0, - highestPlayableFrameRate: void 0 - }; - const e = o.videoCodec, - t = o.videoRange, - i = l.videoDynamicRangeFormats, - r = l.videoCodecs, - n = be.getDynamicRangeType(t, e), - s = be.getCompressionType(e), - a = function(e, t, i, r) { - if (!r && !i) return {}; - var n, s, t = i ? Dy(i, t) : {}, - r = r ? Dy(r, e) : {}; - let a, o; - return o = e === fe.SDR ? (a = t, r) : (a = r, t), n = Object.assign({}, a), s = o, Object.keys(s).forEach(e => { - n[e] || (n[e] = s[e]) - }), n - }(n, s, r, i); - return s !== me.VP09 && (a.highestPlayablePeakBitRateForClearContent = void 0), a - }(), - t = e["highestPlayablePeakBitRateForClearContent"], - i = o.allowedCPCMap || n, - r = ky(o.bandwidth, e.highestPlayablePeakBitRate); - return (i || !t ? r : r || ky(o.bandwidth, t)) && ky(o.avgBandwidth, e.highestPlayableAverageBitRate) && ky(o.width, e.highestPlayableWidth) && ky(o.height, e.highestPlayableHeight) && ky(o.frameRate, e.highestPlayableFrameRate) - }); - return My(0, e, i), i - } - - function Ly(e, a, o, l, d, u) { - var r = (null == l ? void 0 : l.maxHdcpLevel) || void 0; - let c = [...e]; - (0 < d.disableVideoCodecList.size || 0 < d.disableAudioCodecList.size) && (c = function(e, t, i) { - let r = e.filter(e => !e.videoCodec || e.videoCodecList.every(e => { - e = qp(e); - return !t.has(e) - })); - return r = r.filter(e => !(!e.iframes && e.audioCodec) || e.audioCodecList.every(e => { - e = jp(e); - return !i.has(e) - })), My(0, e, r), r - }(c, d.disableVideoCodecList, d.disableAudioCodecList)), r && dm(r) && (c = function(e) { - const t = um(r), - i = e.filter(e => { - e = e.hdcpLevel; - return !e || um(e) <= t - }); - return My(0, e, i), i - }(c)); - var t = null == l ? void 0 : l.maxSecurityLevel, - e = null == d ? void 0 : d.keySystemPreference; - t && e && Ay(t, e) && (c = function(e, t, i) { - function r(e) { - return Ay(e, i) ? n[e] : -1 - } - const n = Ah.getKeySystemSecurityLevel(i), - s = Ah.getKeySystemFormat(i), - a = r(t), - o = e.filter(e => { - e = null !== (e = null === (e = e.allowedCPCMap) || void 0 === e ? void 0 : e[s]) && void 0 !== e ? e : []; - let t = !0; - for (const i of e) - if (t = r(i) <= a, !t) break; - return t - }); - return My(0, e, o), [...o] - }(c, t, e)), c = c.map(t => { - var e; - return t.audioCodecList && t.audioGroupId && ((e = null == (e = a.find(e => e.groupId === t.audioGroupId)) ? void 0 : e.channels) && (t.audioChannelCount = parseInt(e))), t - }); - const h = !(null == d || !d.useMediaKeySystemAccessFilter) && e && navigator && "function" == typeof navigator.requestMediaKeySystemAccess; - return (h ? xy(c, e, u) : $i(c)).pipe(La(e => { - if (0 === e.length || Oy(e)) throw new R(L, f, void 0, "no media option with compatible codecs found in playlist", void 0); - h && My(0, c, e); - const t = navigator && navigator.mediaCapabilities, - n = !(null == d || !d.useMediaCapabilities) && t && "function" == typeof t.decodingInfo; - let i; - return i = n ? function(e, n, s) { - const a = [], - o = Cy(), - l = function(o) { - const l = new Map, - d = navigator && navigator.mediaCapabilities; - return (i, e, t, n, r) => { - const s = { - type: "media-source" - }; - n ? s.video = function(e) { - const t = { - contentType: `video/mp4;codecs=${e}`, - width: i.width, - height: i.height, - bitrate: i.bandwidth || i.avgBandwidth, - framerate: i.iframes ? 8 : i.frameRate - }; - if (i.videoRange) switch (i.videoRange) { - case "PQ": - be.isDolby(e) ? (t.hdrMetadataType = Pm.DoVi, t.colorGamut = "rec2020") : (be.isHEVC(e) || be.isVP09(e)) && (t.hdrMetadataType = Pm.HDR10, t.colorGamut = "rec2020"), t.transferFunction = "pq"; - break; - case "HLG": - t.colorGamut = "rec2020", t.transferFunction = "hlg" - } - return t - }(t) : s.audio = function(e, t, i) { - const r = { - contentType: `audio/mp4;codecs=${e}` - }, - n = fm.getRichestChannelLayoutForGroupId(t.audioGroupId, i); - return n && (r.channels = be.getChannelCount(n).toString(), r.spatialRendering = be.isDolbyAtmos(e, n)), r - }(t, i, e); - e = JSON.stringify(s); - let a; - return l.has(e) ? a = l.get(e) : (a = Fr(d.decodingInfo(s)).pipe(hr(e => { - const t = e.configuration || e.supportedConfiguration, - i = t instanceof Object && (!s.video || null == Object.keys(s.video).find(e => !(e in t.video))) && (!s.audio || null == Object.keys(s.audio).find(e => !(e in t.audio))), - r = e.supported && (!n || e.powerEfficient) && i; - return r || o.warn(wy, `Unsupported config ${e.supported}/${e.powerEfficient}/${i} ${JSON.stringify(s)} supportedConfig=${JSON.stringify(t)}`), r - })), l.set(e, a)), [...r, a] - } - }(s); - return e.forEach(t => { - var e; - let i = []; - if (null === (e = t.videoCodecList) || void 0 === e || e.forEach(e => { - i = l(t, n, e, !0, i) - }), 0 < (null === (e = t.audioCodecList) || void 0 === e ? void 0 : e.length)) { - const s = fm.getRichestAudioCodec(t.audioCodecList); - i = l(t, n, s, !1, i) - } - let r = $i(t); - 0 < i.length && (r = en(i).pipe(hr(e => null == e.find(e => !1 === e) ? t : null), Vn(e => (s.warn(wy, `decodingInfo errror: ${e.message}`), $i(o(t) ? t : null))))), a.push(r) - }), en(a).pipe(hr(e => e.filter(e => Boolean(e)))) - }(e, a, u) : $i(e = Py((r = e, s = Cy(), s = r.filter(s), My(0, r, s), e = s), a)), i.pipe(hr(e => { - if (0 === e.length || Oy(e)) throw new R(L, f, void 0, "no media option with compatible codecs found in manifest", void 0); - if (0 === (t = e = Ry(e, o, l), r = t.filter(e => !e.iframes || !e.width || !e.height || e.width * e.height <= 2488320), My(0, t, r), (e = r).length) || Oy(e)) throw new R(L, f, void 0, "no media option with compatible codecs found in manifest", void 0); - var t; - let i = (null == l ? void 0 : l.videoDynamicRangeFormats) || []; - n && 0 === i.length && (i = [{ - type: fe.SDR - }, { - type: fe.HDR - }, { - type: fe.HDR10 - }, { - type: fe.DolbyVision - }, { - type: fe.HLG - }]); - var { - hdrMediaOptions: r, - sdrMediaOptions: e - } = function(e, t) { - const i = t.reduce((e, t) => { - switch (t.type) { - case fe.DolbyVision: - e.doViSupported = !0; - break; - case fe.HDR10: - e.hdr10Supported = !0; - break; - case fe.HLG: - e.hlgSupported = !0 - } - return e - }, { - doViSupported: !1, - hdr10Supported: !1, - hlgSupported: !1 - }), - { - doViSupported: r, - hdr10Supported: n, - hlgSupported: s - } = i; - return e.reduce((e, t) => { - var i; - switch (be.getDynamicRangeType(t.videoRange, null !== (i = t.videoCodec) && void 0 !== i ? i : "")) { - case fe.HDR: - case fe.HDR10: - n && e.hdrMediaOptions.push(t); - break; - case fe.DolbyVision: - r && e.hdrMediaOptions.push(t); - break; - case fe.HLG: - s && e.hdrMediaOptions.push(t); - break; - default: - "SDR" !== t.videoRange && null != t.videoRange || e.sdrMediaOptions.push(t) - } - return e - }, { - hdrMediaOptions: new Array, - sdrMediaOptions: new Array - }) - }(e, i); - if (0 === r.length && 0 === e.length || Oy(r) && Oy(e)) throw new R(L, "manifestIncompatibleVideoRangeError", void 0, "mediaOption with compatible VIDEO-RANGE not found in manifest", void 0); - return { - hdrMediaOptions: r, - sdrMediaOptions: e - } - }), Vn(e => { - throw e instanceof R && (e.fatal = !0, e.response = $.IncompatibleAsset), e - })); - var r, s - })) - } - - function _y(e, t) { - return t.mediaOptionId !== e.mediaOptionId && t.persistentID === e.persistentID && t.groupId !== e.groupId - }(A = Pm = Pm || {}).HDR10 = "smpteSt2086", A.DoVi = "smpteSt2094-10", A.HDR10Plus = "smpteSt2094-40"; - class Ny extends jm { - constructor(e, t, i) { - super(e, t, i) - } - static makeFilters() { - return Hm() - } - _initFilters() { - return Ny.kAllowFilters - } - get _mediaOptionType() { - return this.mediaOptionType - } - get preferredHost() { - return null - } - get preferredHost$() { - return $i(null) - } - get mediaOptionListInfo() { - var e; - return null !== (e = null === (e = this.getEntity(this.itemId)) || void 0 === e ? void 0 : e.mediaOptionListTuple[this._mediaOptionType]) && void 0 !== e ? e : null - } - get mediaOptionListInfo$() { - return this.selectEntity(this.itemId, e => e && e.mediaOptionListTuple ? e.mediaOptionListTuple[this._mediaOptionType] : null).pipe(Kp()) - } - getFallbackVariant(t, e, i, r) { - var n; - const s = null === (n = this.mediaOptionList) || void 0 === n ? void 0 : n.find(e => e.mediaOptionId === t); - if (!s) return null; - const a = this.filteredMediaOptionList; - if (!a) return null; - const o = Eu(s.url); - if (i) return null !== (i = a.find(e => _y(s, e) && !Au(o, e.url))) && void 0 !== i ? i : null; - let l = null; - for (const t of a) !_y(s, t) || l && !Au(o, t.url) || (l = t); - return l - } - getMatchingAlternateWithPersistentId(t, i, r) { - var e; - return null !== (e = this.preferredMediaOptionList.find(e => !(0 < (null == r ? void 0 : r.length) && r.includes(e.mediaOptionId)) && (!ne(t) || e.persistentID === t) && (!i || this.matchGroup(e, i.audioGroupId, i.subtitleGroupId, i.closedcaption)))) && void 0 !== e ? e : null - } - matchGroup(e, t, i, r) { - let n = !1; - switch (e.type) { - case "CLOSED-CAPTIONS": - n = !r || e.groupId === r; - break; - case "SUBTITLES": - n = !i || e.groupId === i; - break; - case "AUDIO": - n = !t || e.groupId === t - } - return n - } - getMatchingAlternate(e, t) { - e = this.mediaOptionFromId(e); - return this.getMatchingAlternateWithPersistentId(null == e ? void 0 : e.persistentID, t, []) - } - packageAlternateMediaOption(e, t, i) { - return t.mediaType === Su.CLOSEDCAPTION ? this.augmentClosedCaptionsWithForcedSubtitles(null == e ? void 0 : e.subtitleGroupId, t, i) : t - } - augmentClosedCaptionsWithForcedSubtitles(e, t, i) { - i = this.pairForcedSubtitleMediaOptionWithClosedCaption(e, t, i); - return i ? Object.assign(Object.assign({}, t), { - url: i.url, - backingMediaOptionId: i.mediaOptionId - }) : t - } - pairForcedSubtitleMediaOptionWithClosedCaption(t, i, r) { - let n; - if (i && i.mediaType === Su.CLOSEDCAPTION) { - let e = this.mediaOptionList; - r && (e = this.preferredMediaOptionList), n = Ny.pairForcedSubtitleMediaOptionWithClosedCaptionInList(t, i, e) - } - return n - } - static pairForcedSubtitleMediaOptionWithClosedCaptionInList(t, i, e) { - return e.find(function(e) { - return e.mediaType === Su.SUBTITLE && e.lang === i.lang && e.forced && e.autoselect && (!t || e.groupId === t) - }) - } - } - Ny.kAllowFilters = Ny.makeFilters(); - class Fy extends kl { - constructor(e, t) { - super(e), this.itemId = t, this.mediaOptionListQueries = [new Xm(e, this.itemId), new Ny(e, this.itemId, gu.AltAudio), new Ny(e, this.itemId, gu.Subtitle)] - } - get rootPlaylistEntity() { - return this.getEntity(this.itemId) - } - get rootMediaOptionsTuple() { - var e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.mediaOptionListTuple; - return e ? [e[0].mediaOptions, e[1].mediaOptions, e[2].mediaOptions] : [ - [], - [], - [] - ] - } - get itemStartOffset() { - var e, t; - return null !== (e = this.rootPlaylistEntity) && void 0 !== e && e.itemStartOffset && ne(null === (t = this.rootPlaylistEntity) || void 0 === t ? void 0 : t.itemStartOffset) ? null === (t = this.rootPlaylistEntity) || void 0 === t ? void 0 : t.itemStartOffset : 0 - } - get highestVideoCodec() { - var e; - return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.highestVideoCodec - } - get baseUrl() { - var e; - return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.baseUrl - } - get anchorTime() { - var e; - return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.anchorTime - } - get discoSeqNum() { - var e; - return null !== (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.discoSeqNum) && void 0 !== e ? e : NaN - } - get discoSeqNum$() { - return this.selectEntity(this.itemId, "discoSeqNum") - } - get audioMediaSelectionGroup() { - var e; - return null !== (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.audioMediaSelectionGroup) && void 0 !== e ? e : null - } - get subtitleMediaSelectionGroup() { - var e; - return null !== (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.subtitleMediaSelectionGroup) && void 0 !== e ? e : null - } - get audioMediaSelectionOptions() { - var e; - return null !== (e = null === (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.audioMediaSelectionGroup) || void 0 === e ? void 0 : e.MediaSelectionGroupOptions) && void 0 !== e ? e : [] - } - get subtitleMediaSelectionOptions() { - var e; - return null !== (e = null === (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.subtitleMediaSelectionGroup) || void 0 === e ? void 0 : e.MediaSelectionGroupOptions) && void 0 !== e ? e : [] - } - get contentSteeringOption() { - var e; - return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.contentSteeringOption - } - get masterVariableList() { - var e; - return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.masterVariableList - } - get loadStats() { - var e; - return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.loadStats - } - get isMediaPlaylist() { - var e; - return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.isMediaPlaylist - } - getInitPTS(e) { - var t; - return null === (t = this.rootPlaylistEntity) || void 0 === t ? void 0 : t.initPtsRecord[e] - } - get abrStatus$() { - return this.selectEntity(this.itemId, e => null == e ? void 0 : e.abrStatus) - } - get abrStatus() { - var e; - return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.abrStatus - } - get nextMaxAutoOptionId() { - var e; - return null === (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.abrStatus) || void 0 === e ? void 0 : e.nextMaxAutoOptionId - } - get nextMinAutoOptionId() { - var e; - return null === (e = null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.abrStatus) || void 0 === e ? void 0 : e.nextMinAutoOptionId - } - initPTS$(t) { - return this.selectEntity(this.itemId, ({ - initPtsRecord: e - }) => e[t]) - } - get rootPlaylistEntity$() { - return this.selectEntity(this.itemId).pipe(ln(e => Boolean(e)), hr(e => e)) - } - get rootPlaylistEntityAdded$() { - return this.selectEntityAction(Eo.Add).pipe(hr(e => e.map(e => this.getEntity(e)))) - } - get rootMediaOptionsTuple$() { - return ed([this.selectEntity(this.itemId, e => e.mediaOptionListTuple[0].mediaOptions), this.selectEntity(this.itemId, e => e.mediaOptionListTuple[1].mediaOptions), this.selectEntity(this.itemId, e => e.mediaOptionListTuple[2].mediaOptions)]) - } - get sessionData() { - var e; - return null === (e = this.rootPlaylistEntity) || void 0 === e ? void 0 : e.sessionData - } - get sessionData$() { - return this.selectEntity(this.itemId, ({ - sessionData: e - }) => e).pipe(Kp()) - } - get anchorTime$() { - return this.selectEntity(this.itemId, "anchorTime").pipe(La(e => { - var t; - return ne(null == e ? void 0 : e.pos) ? (null == e ? void 0 : e.pos) !== (null === (t = this.anchorTime) || void 0 === t ? void 0 : t.pos) ? (Qe().warn(`anchorTime doesn't match stored value! ${null==e?void 0:e.pos} !== ${null===(t=this.anchorTime)||void 0===t?void 0:t.pos}`), Ii) : $i(e) : Ii - })) - } - get pendingSeek$() { - return this.selectEntity(this.itemId, ({ - pendingSeek: e - }) => e).pipe(Is((e, t) => e === t || "number" == typeof e && "number" == typeof t && isNaN(e) && isNaN(t))) - } - get enabledMediaOptionKeys$() { - return this.selectEntity(this.itemId, "enabledMediaOptionKeys").pipe(ln(e => Boolean(e))) - } - get enabledMediaOptionKeys() { - var e; - return null !== (e = null === (e = this.getEntity(this.itemId)) || void 0 === e ? void 0 : e.enabledMediaOptionKeys) && void 0 !== e ? e : [Lu, Lu, Lu] - } - get enabledMediaOptionSwitchContexts() { - var e; - return null !== (e = null === (e = this.getEntity(this.itemId)) || void 0 === e ? void 0 : e.mediaOptionSwitchContexts) && void 0 !== e ? e : [null, null, null] - } - enabledMediaOptionSwitchContextsByType$(t) { - return this.selectEntity(this.itemId, "mediaOptionSwitchContexts").pipe(hr(e => null == e ? void 0 : e[t])) - } - get enabledMediaOptions$() { - return ed([this.enabledMediaOptionByType$(gu.Variant), this.enabledMediaOptionByType$(gu.AltAudio), this.enabledMediaOptionByType$(gu.Subtitle)]) - } - get enabledAVOptions$() { - return ed([this.enabledMediaOptionByType$(gu.Variant), this.enabledMediaOptionByType$(gu.AltAudio)]) - } - rawEnabledMediaOptionByType$(t) { - return this.enabledMediaOptionKeys$.pipe(hr(e => { - const i = e[t]; - return _u(i) && this.rootMediaOptionsTuple[t].find(e => { - return t = i, e.itemId === t.itemId && e.mediaOptionId === t.mediaOptionId; - var t - }) || Lu - })) - } - enabledMediaOptionByType$(e) { - return this.rawEnabledMediaOptionByType$(e).pipe(Is((e, t) => e.mediaOptionId === t.mediaOptionId && e.url === t.url)) - } - enabledMediaOptionSwitchForType$(e) { - return this.rawEnabledMediaOptionByType$(e).pipe(bo(this.enabledMediaOptionSwitchContextsByType$(e)), Ra(null), ha(), hr(([e, t]) => ({ - fromId: null == e ? void 0 : e[0].mediaOptionId, - toId: null == t ? void 0 : t[0].mediaOptionId, - switchContext: null == t ? void 0 : t[1] - })), Is((e, t) => e.fromId === t.fromId && e.toId === t.toId)) - } - enableMediaOptionSwitchedForType$(t) { - return this.enabledMediaOptionByType$(t).pipe(La(e => Gu(Mr([$i(e), this.enabledMediaOptionSwitchContextsByType$(t).pipe(ha())]), ([, e]) => e[0] && !e[1])), hr(([e]) => e)) - } - enabledMediaOptionIdByType(e) { - return this.getEntity(this.itemId).enabledMediaOptionKeys[e].mediaOptionId - } - get enabledVariantMediaOptionIdBeforeTrickplaySwitch() { - return this.getEntity(this.itemId).enabledVariantMediaOptionIdBeforeTrickplaySwitch - } - variantMediaOptionById(e) { - return this.mediaOptionListQueries[gu.Variant].mediaOptionFromId(e) - } - alternateMediaOptionById(e, t) { - return this.mediaOptionListQueries[e].mediaOptionFromId(t) - } - enabledAlternateMediaOptionByType(e) { - var t = this.enabledMediaOptionIdByType(e); - return this.alternateMediaOptionById(e, t) - } - get enabledVariantMediaOption() { - var e = this.enabledMediaOptionIdByType(gu.Variant); - return this.variantMediaOptionById(e) - } - lastLoadedMediaOptionByType(e) { - var t; - return null === (t = this.getEntity(this.itemId).lastLoadedMediaOptionKeys) || void 0 === t ? void 0 : t[e] - } - get nextMediaOptionsKeys$() { - return this.selectEntity(this.itemId, "nextMediaOptionKeys") - } - get preferredMediaOptions() { - return [this.mediaOptionListQueries[0].preferredMediaOptionList, this.mediaOptionListQueries[1].preferredMediaOptionList, this.mediaOptionListQueries[2].preferredMediaOptionList] - } - get preferredMediaOptions$() { - return ed([this.mediaOptionListQueries[0].preferredMediaOptionList$, this.mediaOptionListQueries[1].preferredMediaOptionList$, this.mediaOptionListQueries[2].preferredMediaOptionList$]) - } - get filteredMediaOptions() { - return [this.mediaOptionListQueries[0].filteredMediaOptionList, this.mediaOptionListQueries[1].filteredMediaOptionList, this.mediaOptionListQueries[2].filteredMediaOptionList] - } - getDisabledMediaOption(e) { - return { - itemId: this.itemId, - mediaOptionType: e, - mediaOptionId: "Nah" - } - } - getEnabledMediaOptionMask() { - return this.enabledMediaOptionKeys.map(e => _u(e)) - } - getPreferredMediaOptionsByType$(e) { - return this.mediaOptionListQueries[e].preferredMediaOptionList$ - } - altMediaOptionHasValidUrl(e, t) { - t = this.alternateMediaOptionById(e, t); - return Boolean(null == t ? void 0 : t.url) - } - get hdrMode$() { - return this.mediaOptionListQueries[gu.Variant].hdrMode$ - } - get maxHdcpLevel$() { - return this.mediaOptionListQueries[gu.Variant].maxHdcpLevel$ - } - get currentPathwayID() { - return this.mediaOptionListQueries[gu.Variant].currentPathwayID - } - get preferredHost() { - return this.mediaOptionListQueries[gu.Variant].preferredHost - } - getErrorInfoByType(e) { - var t; - return null != (null === (t = this.rootPlaylistEntity) || void 0 === t ? void 0 : t.errorsByType) ? this.rootPlaylistEntity.errorsByType[e] : null - } - getInFlightFragByType(e) { - var t; - return null !== (e = null === (t = null === (t = this.getEntity(this.itemId)) || void 0 === t ? void 0 : t.inFlightFrags) || void 0 === t ? void 0 : t[e]) && void 0 !== e ? e : null - } - getInFlightFragByType$(t) { - return this.selectEntity(this.itemId, e => { - return null === (e = null == e ? void 0 : e.inFlightFrags) || void 0 === e ? void 0 : e[t] - }) - } - matchAlternates(e, t, i, r) { - t = ne(t) ? this.mediaOptionListQueries[gu.AltAudio].getMatchingAlternateWithPersistentId(t, e, r) : void 0, r = ne(i) ? this.mediaOptionListQueries[gu.Subtitle].getMatchingAlternateWithPersistentId(i, e, r) : void 0; - return [t || Lu, r || Lu] - } - getLegacyMatchingAlternateWithPersistentId(e, t, i) { - let r = this.mediaOptionListQueries[e].getMatchingAlternateWithPersistentId(t, i, []); - return r = r || this.mediaOptionListQueries[e].getMatchingAlternateWithPersistentId(t, void 0, []), r - } - isValidMediaOptionTuple(i, e) { - const r = e || this.getEnabledMediaOptionMask(); - return [gu.Variant, gu.AltAudio, gu.Subtitle].reduce((e, t) => e && r[t] === _u(i[t]), !0) - } - matchGroup(e, t, i, r) { - var n = e.mediaOptionType; - return this.mediaOptionListQueries[n].matchGroup(e, t, i, r) - } - get preferHDR() { - return this.mediaOptionListQueries[gu.Variant].mediaOptionListInfo.preferHDR - } - } - const By = { - name: "rps" - }; - class Uy { - constructor(e, t) { - this.store = e, this.logger = t - } - getQuery() { - return new kl(this.store) - } - getQueryForId(e) { - return new Fy(this.store, e) - } - set rootPlaylistEntity(e) { - Do("root.add.rootPlaylist"), this.store.add(e) - } - removeItems(e) { - Do(`root.add.remove ${JSON.stringify(e)}`), this.store.remove(e) - } - removeAll() { - Do("root.add.clear"), this.store.remove() - } - setRootPlaylistEntity(e, t) { - Do("root.set.rootPlaylistEntity"), this.store.update(e, e => t) - } - setSessionData(e, t) { - Do("root.set.sessionData"), this.store.update(e, e => { - e.sessionData = t - }) - } - setAnchorTime(e, t) { - Do(`root.set.anchorTime: ${null==t?void 0:t.pos} ${null==t?void 0:t.discoSeqNum}`), this.store.update(e, e => { - e.anchorTime = t - }) - } - setDiscoSeqNum(e, t) { - Do(`root.set.discoSeqNum: ${t}`), this.store.update(e, e => { - e.discoSeqNum = t - }) - } - setPendingSeek(e, t) { - Do("root.set.pendingSeek"), this.store.update(e, e => { - e.pendingSeek = t - }), void 0 === t && fg().setUserSeek(t) - } - setEnabledMediaOptionSwitchContextByType(e, i, r, n) { - this.store.update(e, e => { - var t; - if (e.enabledMediaOptionKeys[i].mediaOptionId === r) { - const r = null !== (t = e.mediaOptionSwitchContexts) && void 0 !== t ? t : [null, null, null]; - r[i] = n ? { - userInitiated: n.userInitiated, - switchPosition: n.switchPosition - } : null, e.mediaOptionSwitchContexts = r - } else Do(`root.set.mediaOptionSwitchContextByType ${r} doesn't match existing mediaOption ${e.enabledMediaOptionKeys[i].mediaOptionId}`) - }) - } - setEnabledVariantMediaOptionIdBeforeTrickplaySwitch(e, t) { - this.store.update(e, e => { - e.enabledVariantMediaOptionIdBeforeTrickplaySwitch = t - }) - } - setEnabledMediaOptionByType(r, n, s, a = !1, o) { - s = s || { - itemId: r, - mediaOptionType: n, - mediaOptionId: "Nah" - }, this.store.update(r, e => { - var t; - const i = null !== (t = [...e.enabledMediaOptionKeys]) ? t : [Lu, Lu, Lu]; - if (i[n] = { - itemId: r, - mediaOptionId: s.mediaOptionId - }, this._updateEnabledMediaOptionKeys(e, i), a) { - const r = null !== (t = e.mediaOptionSwitchContexts) && void 0 !== t ? t : [null, null, null]; - r[n] = o ? { - userInitiated: o.userInitiated, - switchPosition: o.switchPosition - } : null, e.mediaOptionSwitchContexts = r - } - }) - } - _associateForcedSubtitleWithClosedCaption(e, t, i, r) { - if ((null == i ? void 0 : i.mediaType) === Su.CLOSEDCAPTION) { - t = r.variantMediaOptionById(t), r = r.mediaOptionListQueries[gu.Subtitle].packageAlternateMediaOption(t, i, !0); - if (r.url !== i.url) { - const n = jy(t, r, e.mediaOptionListTuple[gu.Subtitle].mediaOptions, Qe()); - e.mediaOptionListTuple[gu.Subtitle].mediaOptions = n - } - } - } - _updateEnabledMediaOptionKeys(t, i) { - var e, r; - const n = null !== (e = t.enabledMediaOptionKeys) && void 0 !== e ? e : [Lu, Lu, Lu]; - let s; - for (let e = 0; e < i.length; ++e) { - var a = i[e], - o = n[e].mediaOptionId !== a.mediaOptionId; - if (o && (n[e] = Object.assign({}, a)), e === gu.Variant) { - const i = this.getQueryForId(a.itemId).mediaOptionListQueries[e].mediaOptionList; - o ? t.abrStatus = (r = a.mediaOptionId, o = i, o = Xg(r, o), { - fragDownloadSlow: !1, - fragDownloadTooSlow: !1, - nextMinAutoOptionId: Lu.mediaOptionId, - nextMaxAutoOptionId: Lu.mediaOptionId, - highBWTrigger: o - }) : t.abrStatus.highBWTrigger = Xg(a.mediaOptionId, i), s = a - } else if (e === gu.Subtitle && _u(a)) { - const i = this.getQueryForId(a.itemId), - n = i.alternateMediaOptionById(e, a.mediaOptionId); - this._associateForcedSubtitleWithClosedCaption(t, s.mediaOptionId, n, i) - } - } - t.enabledMediaOptionKeys = n, t.nextMediaOptionKeys = void 0 - } - setManualMode(e, t) { - this.store.update(e, e => { - e.manualMode = t - }) - } - setEnabledMediaOptions(e, i) { - this.store.update(e, e => { - var t = i.map(({ - mediaOptionId: e, - itemId: t - }) => ({ - mediaOptionId: e, - itemId: t - })); - this._updateEnabledMediaOptionKeys(e, t) - }) - } - setEnabledMediaOptionsAndSwitchContexts(e, i, r) { - this.store.update(e, e => { - var t = i.map(({ - mediaOptionId: e, - itemId: t - }) => ({ - mediaOptionId: e, - itemId: t - })); - this._updateEnabledMediaOptionKeys(e, t), e.mediaOptionSwitchContexts = r - }) - } - setNextMediaOptions(e, i) { - Do(`root.set.nextMediaOptions: ${JSON.stringify(null==i?void 0:i.map(e=>e.mediaOptionId))}`), this.store.update(e, e => { - var t = i ? i.map(({ - itemId: e, - mediaOptionId: t - }) => ({ - itemId: e, - mediaOptionId: t - })) : null; - e.nextMediaOptionKeys = t - }) - } - updateEnabledMediaOptions(e) { - Do("root.set.updateEnabledMediaOptions"), this.store.update(e, e => { - e.nextMediaOptionKeys && !0 !== e.manualMode && (Do(`root.set.updateEnabledMediaOptions ${JSON.stringify(e.nextMediaOptionKeys)}`), this._updateEnabledMediaOptionKeys(e, [...e.nextMediaOptionKeys])), e.nextMediaOptionKeys = void 0 - }) - } - setLastLoadedMediaOptionByType(r, n, s) { - Do(`root.set.lastLoadedMediaOptionByType: ${n} ${(s=s||{itemId:r,mediaOptionType:n,mediaOptionId:"Nah"}).mediaOptionId}`), this.store.update(r, e => { - var t; - const i = null !== (t = e.lastLoadedMediaOptionKeys) && void 0 !== t ? t : [Lu, Lu, Lu]; - i[n] = { - itemId: r, - mediaOptionId: s.mediaOptionId - }, e.lastLoadedMediaOptionKeys = i - }) - } - setPreferredHost(e, t) { - Do(`root.set.preferredHost: ${t}`), this.store.update(e, e => { - e && (e.mediaOptionListTuple[gu.Variant].preferredHost = t) - }) - } - setViewportInfo(e, t) { - Do(`root.set.viewportInfo: ${JSON.stringify(t)}`), this.store.update(e, e => { - e && (e.mediaOptionListTuple[gu.Variant].viewportInfo = t) - }) - } - static getExistingPersistentIds(e) { - var t; - const i = {}, - r = null === (t = e.enabledMediaOptionKeys[gu.AltAudio]) || void 0 === t ? void 0 : t.mediaOptionId; - if ("Nah" !== r) { - const s = e.mediaOptionListTuple[gu.AltAudio], - t = Km(s.mediaOptions, Ny.kAllowFilters, s).find(e => e.mediaOptionId === r); - i.audioPersistentId = null == t ? void 0 : t.persistentID - } - const n = null === (t = e.enabledMediaOptionKeys[gu.Subtitle]) || void 0 === t ? void 0 : t.mediaOptionId; - if ("Nah" !== n) { - const s = e.mediaOptionListTuple[gu.Subtitle], - t = Km(s.mediaOptions, Ny.kAllowFilters, s).find(e => e.mediaOptionId === n); - i.subtitlePersistentId = null == t ? void 0 : t.persistentID - } - return i - } - static doUpdateRootHDRSwitch(e, t, i, r) { - const n = e.mediaOptionListTuple.map(e => Object.assign({}, e)); - n[gu.Variant].preferHDR = t, n[gu.Variant].hasHdrLevels = i; - const s = mg(), - a = sy.getEntity(e.itemId), - o = Zf(e.itemId), - l = o.getBandwidthEstimate(s, null == a ? void 0 : a.serviceName), - d = o.getPlaylistEstimate(s, null == a ? void 0 : a.serviceName), - u = o.getFragEstimate(s, null == a ? void 0 : a.serviceName), - c = o.getBufferEstimate(s, null == a ? void 0 : a.serviceName), - h = { - targetDuration: u.maxDurationSec || (null == s ? void 0 : s.defaultTargetDuration), - targetStartupMs: null == s ? void 0 : s.targetStartupMs - }, - p = Uy.getExistingPersistentIds(e); - return Qy(Object.assign(Object.assign({}, e), { - mediaOptionListTuple: n, - nextMediaOptionKeys: null - }), p, r, l, h, d, u, c) - } - switchToSDROnly(e) { - Do("root.switchToSDROnly"), this.store.update(e, e => { - var t = Uy.doUpdateRootHDRSwitch(e, !1, !1, this.logger)["mediaOptionListTuple"]; - e.mediaOptionListTuple = t - }) - } - setHDRPreference(e, i, r) { - Do(`root.set.HDRPreference: ${i}`), this.store.update(e, e => { - var t = e.mediaOptionListTuple[gu.Variant]; - if (t.preferHDR !== i && (!i || t.hasHdrLevels)) { - t = Uy.doUpdateRootHDRSwitch(e, i, t.hasHdrLevels, this.logger); - if (r) return t; - e.mediaOptionListTuple = t.mediaOptionListTuple - } - }) - } - setPathwayPriority(e, i) { - Do(`root.set.PathwayPriority: [ ${i.join(", ")} ]`), this.store.update(e, e => { - if (e) { - const t = e.mediaOptionListTuple[gu.Variant]; - t.pathwayPriority = i, t.preferredHost = null - } - }) - } - setCurrentPathwayID(e, t) { - Do(`root.set.currentPathwayID: ${t}`), this.store.update(e, e => { - e && (e.mediaOptionListTuple[gu.Variant].currentPathwayID = t) - }) - } - setInitPTS(e, t, i, r, n, s) { - Do(`root.set.initPTS: ${e} ${t} variantDTS:${JSON.stringify(i)} timelineOffset: ${r}`), this.store.update(e, e => { - e.initPtsRecord[t] = { - variantDTS: i, - timelineOffset: r, - offsetTimestamp: n, - iframeMode: s - } - }) - } - static prunePenaltyBox(e, t) { - return e.filter(e => !(e.expiry <= t)) - } - static addToPenaltyBox(e, t, i) { - return e.push({ - mediaOptionId: i, - expiry: t + 12e4 - }) - } - addToPenaltyBox(e, r, n) { - Do(`root.set.penaltyBox: ${r}: ${n}`), this.store.update(e, ({ - mediaOptionListTuple: e - }) => { - const t = e[r], - i = performance.now(); - t.penaltyBoxQueue = Uy.prunePenaltyBox(t.penaltyBoxQueue, i), Uy.addToPenaltyBox(t.penaltyBoxQueue, i, n) - }) - } - prunePenaltyBox(e, r = null) { - Do(`root.set.prunePenaltyBox: ${r}`), this.store.update(e, ({ - mediaOptionListTuple: e - }) => { - var e = r ? [e[r]] : e, - t = performance.now(); - for (const i of e) i.penaltyBoxQueue = Uy.prunePenaltyBox(i.penaltyBoxQueue, t) - }) - } - removePermanently(e, r, n) { - Do(`root.set.removePermanently: ${r}: ${n}`), this.store.update(e, ({ - mediaOptionListTuple: e - }) => { - const t = e[r], - i = new Set(t.removed); - i.add(n), t.removed = Array.from(i) - }) - } - moveAllWithMatchingHosts(e, r, n, s) { - Do(`root.set.moveAllMatchingHosts: ${r}:${n} remove:${s}`), this.store.update(e, ({ - mediaOptionListTuple: e - }) => { - const t = e[r], - i = [...t.mediaOptions].filter(e => Au(n, e.url)).map(e => e.mediaOptionId); - if (s) { - const e = new Set([...t.removed, ...i]); - t.removed = Array.from(e) - } else { - const e = performance.now(); - t.penaltyBoxQueue = Uy.prunePenaltyBox(t.penaltyBoxQueue, e); - for (const r of i) Uy.addToPenaltyBox(t.penaltyBoxQueue, e, r) - } - }) - } - setMaxHdcpLevel(e, i, r = !1) { - Do(`root.set.maxHdcpLevel: ${i}`), this.store.update(e, ({ - mediaOptionListTuple: e - }) => { - const t = e[gu.Variant]; - (r || um(i) < um(t.maxHdcpLevel)) && (t.maxHdcpLevel = i) - }) - } - updateConsecutiveTimeouts(e, i, r, n) { - this.store.update(e, e => { - const t = e.errorsByType || [{ - timeouts: { - load: 0, - append: 0, - key: 0 - } - }, { - timeouts: { - load: 0, - append: 0, - key: 0 - } - }, { - timeouts: { - load: 0, - append: 0, - key: 0 - } - }]; - r ? ++t[i].timeouts[n] : t[i].timeouts[n] = 0, e.errorsByType = t - }) - } - updateInflightFrag(l, d, u, c, h) { - Do("root.set.updateInflightFrag"), this.store.update(l, r => { - if (r.inFlightFrags || (r.inFlightFrags = [null, null]), !(d === gu.Subtitle || u && u.itemId !== l)) - if (u) { - let { - start: e, - duration: t - } = u; - var { - mediaOptionId: n, - mediaSeqNum: s, - discoSeqNum: a - } = u, o = r.inFlightFrags[d]; - let i = null == o ? void 0 : o.tstart; - c !== (null == o ? void 0 : o.state) && (i = performance.now()), $p(o, u) && (e = o.start, t = o.duration), r.inFlightFrags[d] = { - itemId: l, - mediaOptionId: n, - mediaSeqNum: s, - discoSeqNum: a, - start: e, - duration: t, - tstart: i, - state: c, - bwSample: Object.assign({}, h) - } - } else r.inFlightFrags[d] = null - }) - } - setNextMaxAutoOptionId(e, t) { - Do(`root.set.nextMaxAutoOptionId: ${t}`), this.store.update(e, ({ - abrStatus: e - }) => { - e.nextMaxAutoOptionId = t - }) - } - setNextMinAutoOptionId(e, t) { - Do(`root.set.nextMinAutoOptionId: ${t}`), this.store.update(e, ({ - abrStatus: e - }) => { - e.nextMinAutoOptionId = t - }) - } - setHighBWTrigger(e, t) { - Do(`root.set.setHighBWTrigger: ${t}`), this.store.update(e, ({ - abrStatus: e - }) => { - e.highBWTrigger = t - }) - } - setFragLoadSlow(e, t) { - Do(`root.set.setFragLoadSlow ${e} ${JSON.stringify(t)}`), this.store.update(e, ({ - abrStatus: e - }) => { - e.fragDownloadSlow = t.fragDownloadSlow, e.fragDownloadTooSlow = t.fragDownloadTooSlow - }) - } - pickMediaOptionTupleByPersistentId(e, t, i, r = !1, n = !1) { - var s = e.enabledMediaOptionIdByType(gu.Variant), - s = e.variantMediaOptionById(s); - let a, o; - if (t === gu.AltAudio) { - const t = e.enabledAlternateMediaOptionByType(gu.Subtitle); - o = null == t ? void 0 : t.persistentID, a = i - } else { - const t = e.enabledAlternateMediaOptionByType(gu.AltAudio); - a = null == t ? void 0 : t.persistentID, o = i - } - const l = e.getEnabledMediaOptionMask(); - return l[t] = !!(ne(i) && 0 <= i), s ? this.getBestMediaOptionTupleFromVariantAndPersistentId(e, s, a, o, l, void 0, r, n, !1) : [Lu, Lu, Lu] - } - getFallbackMediaOptionTupleFromMediaOptionId(e, t, i, r, n = !1, s = !1, a = !1) { - var o = r ? [r] : [i], - l = e.enabledMediaOptionIdByType(gu.Variant), - r = e.variantMediaOptionById(l), - l = t === gu.AltAudio ? e.alternateMediaOptionById(gu.AltAudio, i) : e.enabledAlternateMediaOptionByType(gu.AltAudio), - l = null == l ? void 0 : l.persistentID, - i = t === gu.Subtitle ? e.alternateMediaOptionById(gu.Subtitle, i) : e.enabledAlternateMediaOptionByType(gu.Subtitle), - i = null == i ? void 0 : i.persistentID; - return r ? this.getBestMediaOptionTupleFromVariantAndPersistentId(e, r, l, i, void 0, o, n, s, a) : [Lu, Lu, Lu] - } - hasFallbackMediaOptionTuple(e, t, i, r) { - var n = e.mediaOptionListQueries[t].mediaOptionFromId(i); - return e.isValidMediaOptionTuple(this.getFallbackMediaOptionTupleFromMediaOptionId(e, t, i, n.backingMediaOptionId, !1, r)) - } - setLegacyAlternateMediaOption(e, t, i, r, n) { - var s = e.enabledMediaOptionIdByType(gu.Variant), - s = e.variantMediaOptionById(s), - s = e.getLegacyMatchingAlternateWithPersistentId(i, r, s); - s ? this.setEnabledMediaOptionByType(t, i, s, !0, n) : this.logger.warn(`${Nu[i]} can't find matching mediaOption for persistent id ${r}`) - } - setEnabledMediaOptionTupleWithMatchedGroups(t, i, e, r) { - const n = Ky(t), - s = this.pickMediaOptionTupleByPersistentId(n, i, e); - if (!n.isValidMediaOptionTuple(s)) return this.setLegacyAlternateMediaOption(n, t, i, e, r); - al(() => { - this.setEnabledMediaOptionByType(t, i, s[i], !0, r), s[gu.Variant].mediaOptionId !== n.enabledMediaOptionIdByType(gu.Variant) && this.setPreferredHost(t, Eu(s[gu.Variant].url)), this.setEnabledMediaOptionByType(t, gu.Variant, s[gu.Variant]); - var e = i === gu.AltAudio ? gu.Subtitle : gu.AltAudio; - s[e].mediaOptionId !== n.enabledMediaOptionIdByType(e) && this.setEnabledMediaOptionByType(t, e, s[e], !1) - }) - } - canSwitchToSDR(e, t, i, r = !1) { - var n = e.mediaOptionListQueries[gu.Variant].mediaOptionFromId(t), - r = this.getFallbackMediaOptionTupleFromMediaOptionId(e, gu.Variant, t, n.backingMediaOptionId, !0, i, r); - return e.isValidMediaOptionTuple(r) - } - getBestMediaOptionTupleFromVariantAndPersistentId(t, e, i, r, n, s, a, o, l) { - var d, u = t.mediaOptionListQueries[gu.Variant].listFallbackVariants(e.mediaOptionId, a, o, l, s); - let c = [Lu, Lu, Lu]; - for (let e = 0; e < u.length; ++e) { - const a = u[e]; - if (d = t.matchAlternates(a, i, r, s), t.isValidMediaOptionTuple([a, ...d], n)) { - c = [a, ...d]; - break - } - } - return c - } - } - const $y = new class extends fl { - constructor() { - super({}, { - name: "root-playlist-store", - idKey: "itemId", - producerFn: su - }) - } - akitaPreAddEntity(e) { - return null == e.errorsByType ? Object.assign(Object.assign({}, e), { - errorsByType: [{ - timeouts: { - load: 0, - append: 0, - key: 0 - } - }, { - timeouts: { - load: 0, - append: 0, - key: 0 - } - }, { - timeouts: { - load: 0, - append: 0, - key: 0 - } - }] - }) : e - } - }; - new kl($y); - let Vy = null; - - function Ky(e) { - return new Fy($y, e) - } - const qy = (n, e, t, s, i) => { - const { - rootMediaOptionsTuple: r, - sessionKeys: a - } = n, o = Array.from(r[gu.Variant]), l = Array.from(r[gu.AltAudio]); - let d = !1, - u = !1, - c = o.map(e => (d = d || Boolean(e.videoCodec), u = u || Boolean(e.audioCodec) || Boolean(e.audioGroupId), e)); - return d && u && (c = c.filter(({ - videoCodec: e - }) => Boolean(e))), Ly(o, l, a, e, t, i).pipe(hr(({ - hdrMediaOptions: e, - sdrMediaOptions: t - }) => { - var i = e.concat(t), - r = 0 < e.length; - return e.concat(t), - function(e, t, i, r) { - var { - itemId: n, - itemStartOffset: s, - rootMediaOptionsTuple: a, - audioMediaSelectionGroup: o, - subtitleMediaSelectionGroup: l - } = e, d = Array.from(a[gu.AltAudio]), u = Array.from(a[gu.Subtitle]), c = t.every(e => ne(e.score)), h = t.some(e => Wm(!0, e)), p = function(e, t) { - const i = [...e]; - return t ? i.sort((e, t) => e.score - t.score || t.bitrate - e.bitrate) : i.sort((e, t) => e.bitrate - t.bitrate), i - }(t, c), f = e.baseUrl, t = null === (a = e.contentSteeringOption) || void 0 === a ? void 0 : a.initPathwayID, a = e.sessionData; - return { - itemId: n, - baseUrl: f, - mediaOptionListTuple: [{ - mediaOptions: p, - hasHdrLevels: i, - hasIframeLevels: h, - hasScore: c, - preferHDR: r, - compatibleIds: null, - penaltyBoxQueue: [], - removed: [], - currentPathwayID: t - }, { - mediaOptions: d, - compatibleIds: null, - penaltyBoxQueue: [], - removed: [] - }, { - mediaOptions: u, - penaltyBoxQueue: [], - removed: [] - }], - audioMediaSelectionGroup: o, - subtitleMediaSelectionGroup: l, - enabledMediaOptionKeys: [Lu, Lu, Lu], - mediaOptionSwitchContexts: [null, null, null], - anchorTime: { - pos: 0 - }, - discoSeqNum: NaN, - pendingSeek: void 0, - itemStartOffset: s, - initPtsRecord: {}, - contentSteeringOption: e.contentSteeringOption, - masterVariableList: e.masterVariableList, - loadStats: e.stats, - isMediaPlaylist: e.isMediaPlaylist, - abrStatus: { - fragDownloadSlow: !1, - fragDownloadTooSlow: !1, - nextMinAutoOptionId: Lu.mediaOptionId, - nextMaxAutoOptionId: Lu.mediaOptionId, - highBWTrigger: NaN - }, - sessionData: a - } - }(n, i, r, s) - })) - }; - - function Hy(e, t, i, r, n, s, a) { - var o, l, d, u, c, h, p, f = e.mediaOptionListTuple[gu.Variant], - m = Km(f.mediaOptions, Xm.kAllowFilters, Object.assign(Object.assign({}, f), { - compatibleIds: null - })), - g = qm(f.preferredHost, m); - return { - firstVariant: (o = g, e = Fg, f = f.hasScore, t = t, i = i, r = r, n = n, s = s, a = a, !o || o.length < 1 || o.every(e => e.iframes) ? void t.warn("no non-iframe media option found") : ((o = f ? Bg(o, i, r, n, s, a) : (l = e, d = i, u = r, c = n, h = s, p = a, o.reduce((e, t) => { - if (t.iframes) return e; - let i = e; - const r = function(e, t, i, r, n, s, a) { - var o, l, d = (o = e.bitrate, u = e.height, (l = (e, t, i) => (e - t) * (e - i) <= 0)(o, t.minValidBitrate, t.maxValidBitrate) && l(u, t.minValidHeight, t.maxValidHeight) ? mu.VALID : mu.INVALID), - o = "PQ" === (c = e.videoRange) ? pu.PQ : "HLG" === c ? pu.HLG : "SDR" === c ? pu.SDR : pu.UNKNOWN, - { - videoCodecRank: u, - audioCodecRank: c - } = { - videoCodecRank: qp((l = e).videoCodec), - audioCodecRank: jp(l.audioCodec) - }, - l = e.bitrate < t.maxPreferredBitrate ? mu.VALID : mu.INVALID, - t = e.audioChannelCount || 1, - a = i && r && n && s && !Ug(e, i, r, n, s, a) ? mu.INVALID : mu.VALID; - return new Qp(d, o, u, t, c, a, l, e.height) - }(t, l, d, u, c, h, p); - return (!e || r.isGreaterThan(e.bestRank) || r.isEqualTo(e.bestRank) && t.bitrate > e.selected.bitrate) && (i = { - selected: t, - bestRank: r - }), i - }, null).selected)) || t.warn("no valid first media option found"), o)), - filteredVariants: m, - preferredVariants: g - } - } - - function jy(e, t, i) { - if ((null == t ? void 0 : t.mediaType) === Su.CLOSEDCAPTION) { - const r = Ny.pairForcedSubtitleMediaOptionWithClosedCaptionInList(e.subtitleGroupId, t, i); - if (r) return t = Object.assign(Object.assign({}, t), { - url: r.url, - backingMediaOptionId: r.mediaOptionId - }), i.map(e => e.mediaOptionId === t.mediaOptionId ? t : e) - } - return i - } - - function Qy(e, t, i, r, n, s, a, o) { - var l; - const d = e.itemId, - u = e.mediaOptionListTuple[gu.Variant], - c = e.mediaOptionListTuple[gu.AltAudio], - h = e.mediaOptionListTuple[gu.Subtitle], - p = Km(c.mediaOptions, Ny.kAllowFilters, c), - f = Km(h.mediaOptions, Ny.kAllowFilters, h); - let { - firstVariant: m, - filteredVariants: g - } = Hy(e, i, r, n, s, a, o); - if (!m) { - const U = u.preferHDR; - u.preferHDR = !U && u.hasHdrLevels, u.preferHDR !== U && (i.warn(`No valid first variant found, toggling hdr preference=${U}->${u.preferHDR}`), { - firstVariant: m, - filteredVariants: g - } = Hy(e, i, r, n, s, a, o)) - } - if (!m) throw new V(!0, "No valid first variant found", $.NoValidAlternates); - const y = Eu(m.url), - v = { - itemId: d, - mediaOptionId: null !== (o = null == m ? void 0 : m.mediaOptionId) && void 0 !== o ? o : null - }, - S = null != p && p.length ? null === (o = ((i, r, e, n) => { - if (e) { - let t; - return t = ne(i) ? e.MediaSelectionGroupOptions.find(function(e) { - return e.MediaSelectionOptionsPersistentID === i - }) : e.MediaSelectionGroupOptions.find(function(e) { - return e.MediaSelectionOptionsIsDefault - }), t = t || e.MediaSelectionGroupOptions[0], n.find(e => (!r || e.groupId === r) && e.persistentID === (null == t ? void 0 : t.MediaSelectionOptionsPersistentID)) - } - })(null == t ? void 0 : t.audioPersistentId, m.audioGroupId, e.audioMediaSelectionGroup, p)) || void 0 === o ? void 0 : o.mediaOptionId : null, - b = S ? { - itemId: d, - mediaOptionId: S - } : Lu, - T = ((i, r, n, s, a, o) => { - if (s) { - let t, e; - return t = ne(i) ? s.MediaSelectionGroupOptions.find(function(e) { - return e.MediaSelectionOptionsPersistentID === i - }) : s.MediaSelectionGroupOptions.find(function(e) { - return e.MediaSelectionOptionsIsDefault - }), t && (e = a.find(e => e.mediaType === Su.CLOSEDCAPTION ? (!r || e.groupId === r) && e.persistentID === t.MediaSelectionOptionsPersistentID : e.mediaType === Su.SUBTITLE ? (!n || e.groupId === n) && e.persistentID === t.MediaSelectionOptionsPersistentID : void o.warn(By, `subtitle media option has unknown type ${e.mediaType}`))), e - } - })(null == t ? void 0 : t.subtitlePersistentId, m.closedcaption, m.subtitleGroupId, e.subtitleMediaSelectionGroup, f, i), - E = null != f && f.length ? null == T ? void 0 : T.mediaOptionId : null, - I = E ? { - itemId: d, - mediaOptionId: E, - mediaOptionType: gu.Subtitle - } : Lu, - { - mediaOptions: w, - audioGroups: A, - subtitleGroups: O - } = (t = g, l = m, t.reduce((e, t) => { - if (((e, t) => { - let i = !0; - e.videoCodec && t.videoCodec && (i = be.isCompatibleVideoCodec(e.videoCodec, t.videoCodec)); - let r = !1; - e.videoRange && t.videoRange ? r = e.videoRange == t.videoRange : e.videoRange || t.videoRange || (r = !0); - let n = !0; - return e.audioCodec && t.audioCodec && (n = be.isCompatibleAudioCodec(e.audioCodec, t.audioCodec)), i && r && n - })(l, t)) { - const l = t.audioGroupId; - l && e.audioGroups.add(l), e.mediaOptions.add(t) - } - var i = t.subtitleGroupId; - i && e.subtitleGroups.add(i); - t = t.closedcaption; - return t && e.closedCaptionGroups.add(t), e - }, { - mediaOptions: new Set, - audioGroups: new Set, - subtitleGroups: new Set, - closedCaptionGroups: new Set - })), - k = Array.from(w).map(e => e.mediaOptionId), - C = m.pathwayID, - D = Object.assign(Object.assign({}, u), { - compatibleIds: k, - preferredHost: y, - currentPathwayID: C - }), - M = [], - x = c.mediaOptions.reduce((e, t) => (A.has(t.groupId) && (e.persistentIds.add(t.persistentID), M.push(t.mediaOptionId), e.filteredAudioMediaOptions.push(t), e.altAudio || (e.altAudio = !!t.url)), e), { - filteredAudioMediaOptions: [], - persistentIds: new Set, - altAudio: !1 - }), - P = Object.assign(Object.assign({}, c), { - compatibleIds: M - }); - let R = e.audioMediaSelectionGroup; - const L = null == R ? void 0 : R.MediaSelectionGroupOptions; - if (L) { - const e = L.reduce((e, t) => (x.persistentIds.has(t.MediaSelectionOptionsPersistentID) && e.push(t), e), new Array); - R = Object.assign(Object.assign({}, R), { - MediaSelectionGroupOptions: e - }) - } - h.mediaOptions = jy(m, T, h.mediaOptions); - const _ = h.mediaOptions.reduce((e, t) => (O.has(t.groupId) && (e.persistentIds.add(t.persistentID), e.filteredSubtitleMediaOptions.push(t)), e), { - filteredSubtitleMediaOptions: [], - persistentIds: new Set - }); - let N = e.subtitleMediaSelectionGroup; - const F = null == N ? void 0 : N.MediaSelectionGroupOptions; - if (F) { - const e = F.reduce((e, t) => (_.persistentIds.has(t.MediaSelectionOptionsPersistentID) && e.push(t), e), new Array); - N = Object.assign(Object.assign({}, N), { - MediaSelectionGroupOptions: e - }) - } - i = [D, P, h]; - let B = new Map; - mg().useHighestVideoCodecPrivate && (B = null == D ? void 0 : D.mediaOptions.reduce((e, t) => { - const i = t.videoCodecList; - if (i) - for (const t of i) { - const i = Hp(t), - r = e.get(i); - be.isHigherCodecByFamily(r, t) && e.set(i, t) - } - return e - }, B)), B.size && B.forEach((e, t) => {}); - t = { - fragDownloadSlow: !1, - fragDownloadTooSlow: !1, - nextMinAutoOptionId: Lu.mediaOptionId, - nextMaxAutoOptionId: Lu.mediaOptionId, - highBWTrigger: Xg(v.mediaOptionId, D.mediaOptions) - }; - return Object.assign(Object.assign({}, e), { - enabledMediaOptionKeys: [v, b, I], - mediaOptionListTuple: i, - audioMediaSelectionGroup: R, - abrStatus: t, - highestVideoCodec: B - }) - } - const Wy = (o, l, d, u, c, h, p) => e => e.pipe(tc("retrieveRootMediaOptions.input"), La(t => { - var e; - if (!t) return Ii; - const { - itemId: i, - platformInfo: r - } = t, n = Ky(i), s = l["logger"]; - if (n.hasEntity(i)) return $i(n); - $y.setLoading(!0); - const a = performance.now(); - return function(e, t, u, c, i) { - const { - itemId: h, - url: p, - itemStartOffset: f - } = e, r = Lc(e, t); - return Lm({ - url: p, - onProgress: { - getData: !0, - cb: Iy - }, - xhrSetup: c.xhrSetup - }, r, i).pipe(hr(({ - responseText: e, - responseURL: t, - stats: i - }) => { - var r = c["keySystemPreference"]; - if (t || (u.warn("Missing response url. Reusing request url as base url"), t = p), Rm.isMediaPlaylist(e)) { - const c = "media-pl-" + Zl(), - d = Rm.parseMediaOptionPlaylist(e, t, !0, r, {}, h, c, gu.Variant, u, f); - Nc(d.mediaOptionDetails); - var n = { - itemId: h, - mediaOptionId: c, - mediaOptionType: gu.Variant, - url: p, - bandwidth: 0, - bitrate: 0, - iframes: d.mediaOptionDetails.iframesOnly, - pathwayID: "." - }; - return { - itemId: h, - itemStartOffset: f, - rootMediaOptionsTuple: [ - [n], - [], - [] - ], - stats: i, - baseUrl: t, - initialDetails: d.mediaOptionDetails, - isMediaPlaylist: !0 - } - } { - const u = Rm.parseSessionData(e, t), - c = Rm.parseSessionKeys(e, t, r), - p = Rm.parseRootPlaylist(h, e, t, !0); - if (p.playlistParsingError) throw p.playlistParsingError; - var { - variantMediaOptions: s, - contentSteeringOption: a, - masterVariableList: o - } = p, l = Rm.parseRootPlaylistAlternateMediaOptions(h, e, t, p.variantMediaOptions, !0, o); - if (l.playlistParsingError) throw l.playlistParsingError; - var { - audioAlternateOptions: n, - subtitleAlternateOptions: r, - audioMediaSelectionGroup: e, - subtitleMediaSelectionGroup: l - } = l.alternateMediaInfo; - return { - itemId: h, - itemStartOffset: f, - rootMediaOptionsTuple: [s, n, r], - stats: i, - baseUrl: t, - audioMediaSelectionGroup: e, - subtitleMediaSelectionGroup: l, - contentSteeringOption: a, - sessionData: u, - sessionKeys: c, - masterVariableList: o - } - } - }), e => e.pipe(Vn(e => { - if (e instanceof pc) throw new dc(!1, "Timeout", 0, $.ManifestTimeoutError, !0); - if (e instanceof oc) throw new dc(!1, e.message, e.code, { - code: e.code, - text: "Manifest network error" - }, !1); - throw e - }))) - }(t, o, s, d, null === (e = null === (e = fg()) || void 0 === e ? void 0 : e.getQuery()) || void 0 === e ? void 0 : e.extendMaxTTFB).pipe(Za(e => p.triggerManifestLoaded(e)), Za(({ - initialDetails: e, - stats: t - }) => { - e && (e = e, t = t, Cg().archiveMediaOptionDetails(e, t, !0)) - }), bo(u.displaySupportsHdr$), La(([e, t]) => qy(e, r, d, t, s)), hr(e => (l.rootPlaylistEntity = function(e, t, i, r, n, s) { - const { - itemId: a, - initialSeekTime: o - } = e, l = Zf(a), d = n.enableAdaptiveStartup ? l.getBandwidthEstimate(n, e.serviceName) : void 0, u = n.enableAdaptiveStartup ? l.getPlaylistEstimate(n, e.serviceName) : void 0, c = n.enableAdaptiveStartup ? l.getFragEstimate(n, e.serviceName) : void 0, h = n.enableAdaptiveStartup ? l.getBufferEstimate(n, e.serviceName) : void 0, p = performance.now() - r; - let f; - n.targetStartupMs > p ? f = n.targetStartupMs - p : (f = n.targetStartupMs, s.warn(`Manifest load took ${p}ms and exceeds targetStartupMs: ${n.targetStartupMs}; resetting targetStartupMs to ${n.targetStartupMs}`)); - const m = n.enableAdaptiveStartup ? { - targetDuration: c.maxDurationSec || n.defaultTargetDuration, - targetStartupMs: f - } : void 0, - g = Qy(t, i, s, d, m, u, c, h); - return g.pendingSeek = o, g - }(t, e, c, a, d, s), n)), lg(i, null, Lc(t, o), 0, !1, n, l, h), Vs(() => { - $y.setLoading(!1) - })) - }), tc("retrieveRootMediaOptions.emit")); - - function Gy(t, o, l, d, u, c, h) { - return e => e.pipe(Kp(), La(e => { - return e ? Mr([$i(e).pipe(Wy(t.manifestLoadPolicy, l, t, d, null, u, c)), (n = t, s = o, a = h.mux, new $t(e => { - const t = new Ty(n, s, a); - return e.next(t), () => { - t.destroy() - } - })), (i = t.trickPlaybackConfig, r = o, new $t(e => { - var t = new Zu(i, r); - return e.next(t), () => {} - }))]).pipe(hr(([e, t, i]) => ({ - rootPlaylistQuery: e, - mediaParser: t, - iframeMachine: i - }))) : $i(null); - var i, r, n, s, a - })) - } - wc; - return class zy extends wc { - constructor(e = {}, t) { - var i; - if (super(), this.destroy$ = new Xt, this.mediaElement$ = new yi(null), this.publicQueriesInternal$ = new yi(null), this.mediaElementAdapter = null, this.rpcService = null, this.rpcClients = null, this.platformService = Af(), this.keySystemAdapter = null, this.legibleSystemAdapter = null, this.sessionID = Zl(), this.statsService = (Jf = Jf || new Xf(Yf), Jf), this.gaplessCapable = !0, this.teardownWG$ = new Xp, this.itemQueue = new ay, (e.liveSyncDurationCount || e.liveMaxLatencyDurationCount) && (e.liveSyncDuration || e.liveMaxLatencyDuration)) throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration"); - const m = Object.assign(Object.assign({}, Ru), e); - if (m.maxRequiredStartDuration < m.minRequiredStartDuration || m.minRequiredStartDuration < 0) throw new Error("Illegal config: bad maxRequiredStartDuration or minRequiredStartDuration"); - i = (this.hlsConfig = m).buildType, oe = "production" === i; - const r = this.sessionID; - let n = "silent"; - e.debug && (n = m.debugLevel), au(!0), this.logger = null !== (i = this.logger) && void 0 !== i ? i : ([M, h = {}] = [r, (c = { - sendLogs: m.sendLogs || null, - level: "log" === n ? "debug" : n, - consoleOverride: "boolean" != typeof e.debug ? e.debug : void 0, - buildType: m.buildType - }, x = c.consoleOverride, Object.assign({ - name: "hls", - timestamp: c.sendLogs ? Oe.stdTimeFunctions.epochTime : Oe.stdTimeFunctions.isoTime, - browser: { - asObject: !0, - serialize: !0, - transmit: { - send: (e, t) => {} - }, - write: { - debug: qe.bind(null, Ke(x || console, "debug"), "debug"), - info: qe.bind(null, Ke(x || console, "info"), "info"), - warn: qe.bind(null, Ke(x || console, "warn"), "warn"), - error: qe.bind(null, Ke(x || console, "error"), "error"), - fatal: qe.bind(null, Ke(x || console, "error"), "fatal") - } - } - }, c))], Ue && Ue.sessionId === M ? Ue.warn("Logger Singleton already setup, returning existing singleton") : (Ue = Oe($e(h)).child({ - sessionId: M, - name: "hls" - }), Ue.qe = e => Ue.info(e), Ue.sessionId = M), Ue), _o = !1, Lo && (delete window.$$stores, delete window.$$queries), this.hlsConfig.audioPrimingDelay = 0, this.rootPlaylistService = (x = this.logger, Vy = new Uy($y, x), Vy), this.customUrlLoader = Pc(), this.sessionDataLoader = new Fp(m, Cc, this.customUrlLoader.load, this.logger); - var s, a, o, l, d, u, c = m.liveMaxLatencyDurationCount, - h = m.liveSyncDurationCount; - if (ne(c) && ne(h) && c <= h) throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be gt "liveSyncDurationCount"'); - if (ne(m.liveMaxLatencyDuration) && (m.liveMaxLatencyDuration <= m.liveSyncDuration || !ne(m.liveSyncDuration))) throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be gt "liveSyncDuration"'); - const p = fg(); - p.setHlsEntity({ - id: r, - config: m - }); - const f = Cg(), - g = new Tf(If), - y = (Rh = Rh || new xh(Ph), Rh); - this.accessLogInstance = new rm(this, r), this.rtcService = new Kf(this, m, this.accessLogInstance, this.logger), this.playerEvents = new Ng(this, this.logger, this.rtcService); - const v = (M = this.platformService, u = M, (() => { - if ("function" == typeof matchMedia) { - var e = matchMedia("(dynamic-range: high)"), - t = matchMedia("bad query"); - if (e.media !== t.media) return an($i(e), rn(e, "change")).pipe(hr(e => e.matches)) - } - return $i(!0) - })().pipe(Za(e => { - u.updateSupportsHdr(e) - })).pipe($a(Ii))), - S = this.mediaElement$.pipe((s = m, o = (a = this).logger, l = this.teardownWG$, d = this.rtcService, e => e.pipe(tc("playback.mediaElementServiceEpic.in"), La(e => { - if (!e) return $i(null); - const t = new vf(e, bf, s, a, o, l, d); - return t.openMediaSource(new MediaSource), an($i(t), t) - }), tc("playback.mediaElementServiceEpic.emit"))), Aa()), - b = this.itemQueue.activeItemById$.pipe(La(e => e ? tm(m, this.statsService, e, this.logger) : Ii)); - this.rpcService = (() => { - let e = null; - return null != m.createRPCService && (e = Lf(m.createRPCService, xf)), m.enableWorker && null == e && (e = _f), null == e && (e = xf), e(this.logger) - })(), this.rpcClients = (x = this.rpcService, { - crypto: new kf(x), - mux: new Cf(x) - }); - var T, E, I, w, A, O, k, C, D, c = Mr([this.itemQueue.activeItemById$.pipe(Gy(m, this.logger, this.rootPlaylistService, g, this.statsService, this.playerEvents, this.rpcClients), Za(e => { - var t = null == e ? void 0 : e.rootPlaylistQuery; - this.publicQueriesInternal$.next([t, null]), this.iframeMachine = null == e ? void 0 : e.iframeMachine, t && this.playerEvents.triggerManifestParsed(t) - })), S.pipe((I = this.itemQueue.removedItems$, w = y, A = m, O = g, C = (k = this).rtcService, D = this.logger, e => Mr([((n, s, a, o, l, d, u) => e.pipe(tc("[Keys] playback.keySystemServiceEpic.in"), La(r => r ? new $t(e => { - let t = new Dh(n, r, a, o, l, d, u); - const i = an($i(t), s.pipe(jr(e => t.removeKeysForItems(e)), $a(Ii))).subscribe(e); - return function() { - u.warn("[Keys] playback.keySystemServiceEpic.unsubscribe"), i.unsubscribe(), t.destroy().subscribe(), t = void 0 - } - }) : $i(null)), tc("[Keys] playback.keySystemServiceEpic.emit")))(w, I, A, O, k, C, D), ((t, i, r) => e.pipe(tc("playback.legibleServiceEpic.in"), La(e => e ? an($i(e = new _p(e, t, i, r)), e) : $i(null)), tc("playback.legibleServiceEpic.emit")))(A, k, D), e]).pipe(hr(([e, t, i]) => ({ - keySystemAdapter: e, - legibleSystemAdapter: t, - mediaSink: i - })))), Za(({ - keySystemAdapter: e, - legibleSystemAdapter: t, - mediaSink: i - }) => { - this.keySystemAdapter = e, this.legibleSystemAdapter = t, this.mediaElementAdapter = i - }))]).pipe(hr(([e, t]) => { - var { - keySystemAdapter: i, - legibleSystemAdapter: r, - mediaSink: n - } = t; - if (!(e && i && r && n)) return null; - var { - rootPlaylistQuery: s, - iframeMachine: t, - mediaParser: e - } = e; - return { - logger: this.logger, - config: m, - platformService: this.platformService, - statsService: this.statsService, - rtcService: this.rtcService, - rpcClients: this.rpcClients, - rootPlaylistService: this.rootPlaylistService, - rootPlaylistQuery: s, - mediaLibraryService: f, - keySystemAdapter: i, - legibleSystemAdapter: r, - mediaSink: n, - mediaParser: e, - iframeMachine: t, - customUrlLoader: this.customUrlLoader, - gaplessInstance: this - } - }), Aa()).pipe(La(u => { - if (!u) return Ii; - const { - rootPlaylistQuery: t, - mediaSink: e, - mediaLibraryService: i - } = u; - this.publicQueriesInternal$.next([t, e.mediaQuery]); - const n = e.mediaQuery, - r = $i(u).pipe(vy()), - s = t.rootPlaylistEntity$.pipe(Kp(), Ds(1), Za(() => { - this.commitEarlySelection(u.logger) - })), - a = Gu(Mr([n.haveEnough$, t.sessionData$]), ([e]) => !0 === e, 1).pipe(La(([, e]) => this.sessionDataLoader.loadSessionData(e)), Za(e => { - this.rootPlaylistService.setSessionData(t.itemId, e) - }), Vn(e => (this.logger.error(e.message), Ii))), - o = Zf(t.itemId), - l = function(d, r, u, n, s) { - const a = u.mediaQuery; - return Gu(a.combinedBuffer$, e => 0 < (null == e ? void 0 : e.length)).pipe(La(() => { - var e = ed([a.seekTo$, a.bufferedSegmentsByType$(yu.Variant)]).pipe(hr(([e, t]) => { - const i = ne(null == e ? void 0 : e.pos) ? e.pos : a.currentTime, - r = t.find(e => e.startPTS <= i && e.endPTS > i), - n = a.getBufferInfo(i, d.maxBufferHole), - s = a.getCombinedBufferInfo(i, d.maxBufferHole); - return { - pos: i, - sbTuple: n, - combined: s, - playingFrag: null !== (t = null == r ? void 0 : r.frag) && void 0 !== t ? t : null - } - })), - t = ed([r.getInFlightFragByType$(gu.Variant), r.enabledMediaOptionByType$(gu.Variant)]).pipe(La(([e, t]) => { - var i = n.getQueryForOption(t); - return En($i(e), $i(t), i.mediaOptionDetails$) - }), hr(([e, t, i]) => [e, { - variant: t, - details: i - }])), - i = ed([s.bandwidthEstimate$, s.fragEstimate$, s.bufferEstimate$]); - return ed([a.readyState$, t, e, a.desiredRate$, i]) - }), ao(100, tr, { - leading: !0, - trailing: !0 - }), hr(([, e, t, i, r]) => { - var [n, s] = e, [a, e, r] = r; - let o = e, - l = r; - return e && (o = { - maxDurationSec: ne(e.maxDurationSec) ? e.maxDurationSec : d.defaultTargetDuration, - avgParseTimeMs: ne(e.avgParseTimeMs) ? e.avgParseTimeMs : d.statDefaults.fragParseTimeMs - }), r && (l = { - avgBufferCreateMs: ne(r.avgBufferCreateMs) ? r.avgBufferCreateMs : d.statDefaults.fragBufferCreationDelayMs, - avgInitFragAppendMs: ne(r.avgInitFragAppendMs) ? r.avgInitFragAppendMs : d.statDefaults.initFragAppendMs, - avgDataFragAppendMs: ne(r.avgDataFragAppendMs) ? r.avgDataFragAppendMs : d.statDefaults.dataFragAppendMs - }), a = Ey(d, n, s, t, i, a, o, l), u.haveEnough = a - }), Is(), Za(e => {}), $a(Ii)) - }(m, (this.logger, t), e, i, o), - d = function() { - const { - config: e, - mediaSink: t, - rootPlaylistQuery: i, - mediaLibraryService: r, - gaplessInstance: n - } = u, s = t.mediaQuery, a = i.enabledAVOptions$.pipe(La(e => En(...e.map(e => _u(e) ? r.getQueryForOption(e).mediaOptionDetails$ : $i(null))))); - return Gu(s.combinedBuffer$, e => 0 < (null == e ? void 0 : e.length)).pipe($a(ed([a, s.msReadyState$, s.updating$, s.isIframeRate$, s.isBufferedToEnd$(e.maxBufferHole, !n.inGaplessMode)])), tc("checkForEndOfStream"), ln(([, e, t, i, r]) => "open" === e && !1 === t && !i && r), Za(([e]) => { - null != e[0] && e.every(e => null == e || !1 === e.liveOrEvent && !1 === e.iframesOnly) && !n.inGaplessMode && t.endStream() - }), $a(Ii)) - }(), - c = function() { - const { - config: o, - iframeMachine: l, - mediaSink: i - } = u, d = i.mediaQuery; - return d.desiredRate$.pipe(La(a => Wp(a) ? bn(0, Math.abs(1e3 / a)).pipe(hr(() => { - let e = null; - const t = d.seekable; - if (!l.isStarted || t.length < 1) return e; - var i = l.iframeClockTimeSeconds, - r = o.leftMediaTimeToAutoPause, - n = t.start(0), - s = t.end(t.length - 1); - return 1 < a && s - i < r ? (e = { - newRate: 0, - postFlushSeek: s - r - }, l.pause()) : a < 0 && i - n < a / -2 && (e = { - newRate: 1, - postFlushSeek: n - }), e - }), Kp(), Za(({ - newRate: e, - postFlushSeek: t - }) => { - i.postFlushSeek = t, i.desiredRate = e - }), $a(Ii)) : Ii)) - }(), - h = (f = u).config.enableIFramePreloading ? function(t) { - const { - mediaSink: e, - rootPlaylistQuery: r, - mediaLibraryService: n - } = t, i = e.mediaQuery; - return ed([i.desiredRate$, i.waterLevelChangedForType$(yu.Variant)]).pipe(La(([e, t]) => Wp(e) || t !== Dp.AboveHighWater ? Ii : r.enabledMediaOptionByType$(gu.Variant).pipe(hr(e => { - var t = r.mediaOptionListQueries[gu.Variant].hasIframes, - i = null !== (i = null === (i = n.getQuery().getEntity(r.itemId)) || void 0 === i ? void 0 : i.liveOrEvent) && void 0 !== i && i; - return !t || i ? xm.DISABLED : e - }))), Ds(1), _s(e => function(e, t) { - const i = t.logger; - return e === xm.DISABLED ? $i(e) : function(e, t) { - const { - rootPlaylistQuery: i, - logger: r, - config: n, - mediaSink: s, - statsService: a - } = t, o = s.mediaQuery, l = a.getQueryForItem(i.itemId), d = Dg(e), u = jg(!0, n, i, d, o, l, r), c = i.variantMediaOptionById(u.variantMediaOption); - return Mg(t, c, !0) - }(e, t).pipe(_s(r => function(e) { - var { - mediaSink: t, - rootPlaylistQuery: i - } = e, t = Ig((t = t.mediaQuery).currentTime, i.discoSeqNum, 0, r, []); - return null !== (i = null == t ? void 0 : t.foundFrag) && void 0 !== i && i.mediaFragment ? (t = t.foundFrag.mediaFragment, en([_g(e, t.keyTagInfo, { - itemId: t.itemId, - mediaOptionId: t.mediaOptionId - }), xg(e, t)]).pipe(Za(() => {}), Zs(xm.SUCCESS))) : Vi("Unable to find fragment for iframe prefetch") - }(t)), Zs(xm.SUCCESS), Vn(e => (i.error(ry, `got error ${e.message} in prefetch`), $i(xm.ERRORED)))) - }(e, t)), Vs(() => {})) - }(f) : $i(xm.DISABLED), - p = [s, r, a, l, Gu(ed([n.gotPlaying$, n.gotLoadStart$, n.readyState$]), ([e, t, i]) => !0 === e || !0 === t || 1 <= i).pipe(La(() => n.ended$), La(e => bn(0, e ? void 0 : 1e3)), Za(() => { - this.playbackInfo(m, n) - })), c, h, n.timeupdate$.pipe(hr(e => { - if (this.inGaplessMode && this.isPreloading && ne(this.loadingItem.itemStartOffset) && e >= this.loadingItem.itemStartOffset) { - const e = this.itemQueue.playingItem.itemId, - t = this.itemQueue.loadingItem.itemId, - i = { - prevItemId: e, - nextItemId: t, - nextStartTime: this.loadingItem.itemStartOffset, - nextDuration: n.msDuration - this.loadingItem.itemStartOffset - }; - this.itemQueue.updatePlayingItemId(), this.trigger(P.ITEM_TRANSITIONED, i), this.rtcService.itemTransitioned(e, t) - } - })), this.updateLiveSeekableRange(t, e), d]; - var f; - if (m.enablePerformanceLogging) { - this.logger.child({ - name: "timing" - }); - const u = En(...Bu.map(r => t.getInFlightFragByType$(r).pipe(Is((e, t) => (null == e ? void 0 : e.state) === (null == t ? void 0 : t.state)), Kp(), bo(n.bufferedRangeTuple$), Za(([e, t]) => { - const i = Object.assign(Object.assign({}, e), { - event: "fragment", - name: Nu[r], - buffered: void 0 - }); - "appended" === e.state && (i.buffered = t) - }), Vn(() => Ii)))), - e = En(...Fu.map(e => t.enabledMediaOptionByType$(e).pipe(La(t => null != (null == t ? void 0 : t.url) && _u(t) ? Dg(t).mediaOptionDetailsEntity$.pipe(Kp(), hr(e => ({ - entity: e, - option: t - })), Is((e, t) => (null == e ? void 0 : e.entity.detailsLoading) === (null == t ? void 0 : t.entity.detailsLoading)), Za(({}) => {})) : Ii), Vn(() => Ii)))); - p.push(u, e) - } - return an(...p) - })), - h = this.itemQueue.removedItems$.pipe(ll(e => { - var t; - t = e, Cg().remove(t), this.rootPlaylistService.removeItems(e) - })), - M = p.getQuery().userSeek$.pipe((T = this.itemQueue, E = this.rootPlaylistService, e => e.pipe(Kp(), La(e => en([$i(e), T.activeItemById$.pipe(Kp(), La(e => E.getQueryForId(e.itemId).rootPlaylistEntity$), Ds(1))])), hr(([e, t]) => (E.setPendingSeek(t.itemId, e), e))))), - x = p.getQuery().selectEntityAction(Eo.Add).pipe(Za(() => { - this.logger.warn(`new Hls instance added while old one still active sessionId:${r}`) - })); - an(S.pipe(Vn(() => Ii)), an(v, M, b, h, c, this.teardownWG$).pipe(Vn(e => this._handleError(e)))).pipe(Vs(() => { - var e, t; - try { - this.detachMedia(), this.trigger(P.DESTROYING), this.playerEvents.destroy(), null === (e = this.accessLogInstance) || void 0 === e || e.destroy(), null === (t = this.rtcService) || void 0 === t || t.destroy(), Cg().clear(), this.rootPlaylistService.removeAll(), this.itemQueue.clearQueue(), p.removeEntity(this.sessionID) - } catch (e) { - this.logger.error(`Got error in finalize ${e.message}`) - } - }), Va(fn(this.destroy$, x))).subscribe() - } - get publicQueries$() { - return this.publicQueriesInternal$.pipe(ln(e => Boolean(e) && Boolean(e[0]) && Boolean(e[1]))) - } - get _activeRootQuery() { - var e = this.publicQueriesInternal$.value; - return null !== (e = null == e ? void 0 : e[0]) && void 0 !== e ? e : null - } - get _mediaElementQuery() { - var e = this.publicQueriesInternal$.value; - return null !== (e = null == e ? void 0 : e[1]) && void 0 !== e ? e : null - } - static get version() { - return "2.162.2" - } - static get Events() { - return P - } - get Events() { - return zy.Events - } - static get DefaultConfig() { - return de(Ru) - } - get DefaultConfig() { - return zy.DefaultConfig - } - static isSupported() { - try { - const e = window.MediaSource || window.WebKitMediaSource, - t = window.SourceBuffer || window.WebKitSourceBuffer, - i = e && "function" == typeof e.isTypeSupported && e.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"'), - r = !t || t.prototype && "function" == typeof t.prototype.appendBuffer && "function" == typeof t.prototype.remove; - return !!i && !!r - } catch (e) { - return !1 - } - } - commitEarlySelection(e) { - var t = this.itemQueue.earlyAudioSelection; - ne(t) && (this.audioSelectedPersistentID = t, this.itemQueue.earlyAudioSelection = null), ne(t = this.itemQueue.earlySubtitleSelection) && (this.subtitleSelectedPersistentID = t, this.itemQueue.earlySubtitleSelection = null) - } - _handleError(i) { - var r; - try { - let t, e = i.message; - if (this.logger.error(`Got unhandled or fatal error ${e}`, i), null === (r = this.rtcService) || void 0 === r || r.handleError(i), t = i instanceof p ? i : new V(!0, i.message, $.InternalError), t.fatal && this.isPreloading && (this.logger.warn("Fatal error seen while preloading, calling dequeueSource"), this.dequeueSource("FatalErrorWhileLoading")), t.fatal) { - let e = Wu; - if (this.mediaElementAdapter) { - const r = this.mediaElementAdapter.mediaQuery, - n = r.getCombinedBufferInfo(r.currentTime, 0); - 0 < (null == n ? void 0 : n.len) && (e = Gu(this.mediaElementAdapter.mediaQuery.stallInfo$, e => null != e).pipe(hr(() => {}))) - } - return e.pipe(La(() => (this.trigger(P.ERROR, t), Ii))) - } - this.trigger(P.ERROR, t) - } catch (i) { - throw this.logger.error(`Error thrown inside _handleError ${i.message}`, i), i - } - return Ii - } - updateLiveSeekableRange(e, t) { - return e.enabledMediaOptionByType$(gu.Variant).pipe(La(e => { - const t = Dg(e); - let i = 0; - return t.mediaOptionDetailsEntity$.pipe(Kp(), ln(e => { - var t = null !== e.stats && !1 === e.detailsLoading && e.lastUpdateMillis > i; - return i = null !== (e = e.lastUpdateMillis) && void 0 !== e ? e : 0, t - })) - }), La(e => (0 === e.unchangedCount && (e.mediaOptionDetails.liveOrEvent ? t.updateLiveSeekableRange(e.mediaOptionDetails) : t.clearLiveSeekableRange()), Ii))) - } - playbackInfo(i, r) { - const n = this.mediaElement$.getValue(); - if (n) { - const s = n.readyState >= n.HAVE_FUTURE_DATA, - a = { - readyToPlay: s, - playbackLikelyToKeepUp: r.haveEnough && s, - rate: n.playbackRate, - paused: n.paused, - position: n.currentTime, - duration: n.duration, - seekableTimeRanges: im.timeRangeToArray(n.seekable), - loadedTimeRanges: im.timeRangeToArray(n.buffered) - }; - let e = 0, - t = 0; - if (im.isHtmlVideoElement(n)) { - const o = n.getVideoPlaybackQuality; - if (o && typeof o == typeof Function) { - const o = n.getVideoPlaybackQuality(); - e = a.droppedVideoFrames = o.droppedVideoFrames, a.corruptedVideoFrames = o.corruptedVideoFrames, a.totalVideoFrames = o.totalVideoFrames, t = a.totalVideoFrames - e - } - } else im.isWebkitMediaElement(n) && (e = a.droppedVideoFrames = n.webkitDroppedFrameCount, t = a.decodedFrameCount = n.webkitDecodedFrameCount); - i.enablePerformanceLogging && r.getCombinedMediaSourceBufferInfo(i.maxBufferHole), null === (i = this.rtcService) || void 0 === i || i.handlePlaybackInfo(e, t) - } - } - get currentItem() { - return this.isPreloading ? this.playingItem : this.itemQueue.activeItem - } - get realCurrentTime() { - var e, t = this._mediaElementQuery; - if (!t) return NaN; - if (null !== (e = this.iframeMachine) && void 0 !== e && e.isStarted) { - const r = t.mediaElementDuration, - e = this.iframeMachine.iframeClockTimeSeconds; - return e > r ? r : e - } - let i = ne(t.postFlushSeek) ? t.postFlushSeek : t.currentTime; - return ne(i) && ne(null === (e = this.playingItem) || void 0 === e ? void 0 : e.itemStartOffset) && (i -= this.playingItem.itemStartOffset), i - } - set realCurrentTime(e) { - var t; - ne(null === (t = this.playingItem) || void 0 === t ? void 0 : t.itemStartOffset) && (e += this.playingItem.itemStartOffset), this.seekTo = e - } - get bufferedDuration() { - var e; - const t = this._mediaElementQuery; - return null !== (e = null == t ? void 0 : t.getBufferedDuration()) && void 0 !== e ? e : 0 - } - get sessionData() { - var e = this._activeRootQuery; - return null == e ? void 0 : e.sessionData - } - get supportedFrameRates() { - const e = this.hlsConfig.trickPlaybackConfig.enabled, - t = [0, 1], - i = this._activeRootQuery, - r = Cg().getQuery(); - return e && i && r.getEntity(i.itemId) && !1 === r.getEntity(i.itemId).liveOrEvent && t.push(8, 24, 48, 96), t - } - loadSource(e, i, t) { - var r, n, s, a, o, l; - if ("playready" === this.config.keySystemPreference && !this.config.enablePlayReadyKeySystem) throw new V(!0, "Playready key system is not supported now", $.UnsupportedKeySystemError); - if (!e || !e.trim().length) throw new V(!0, "Empty loadSource url", $.EmptyLoadSourceError); - if (e = bu.buildAbsoluteURL(window.location.href, e, { - alwaysNormalize: !0 - }), i && Object.keys(i).filter(e => 0 <= ["itemId", "streamID"].indexOf(e)).reduce((e, t) => t in i ? Object.assign(e, { - [t]: i[t] - }) : e, {}), null !== (l = null == i ? void 0 : i.appData) && void 0 !== l && l.reportingAgent && (this.reportingAgent = i.appData.reportingAgent), null != i && i.userInfo && (this.userInfo = i.userInfo), null === (a = this.accessLogInstance) || void 0 === a || a.setupReporter(i.appData), null != i && i.platformInfo && this.platformService.updatePlatformInfo(i.platformInfo), function(e, t) { - if (t) { - const t = Eu(e); - return void 0 !== Iu.find(e => new RegExp(e).exec(t)) - } - }(e, this.config.enableQueryParamsForITunes)) { - const d = { - language: i.language, - dsid: i.dsid, - subs: i.subs - }; - r = e, n = null == i ? void 0 : i.platformInfo, s = d, o = n.model, l = n.manufacturer, o && l || Qe().warn(`Missing model/manufacturer in platformInfo model ${o} manufacturer ${l}`), o && l && (a = r, o = n.model, l = n.manufacturer, n = -1 !== a.indexOf("?"), o = encodeURIComponent(o), l = encodeURIComponent(l), r = (a = n ? a : a + "?") + wu.deviceName + l + wu.deviceModel + o), e = function(e, t) { - let i; - e = -1 !== e.indexOf("?") ? e : e + "?"; - const r = Qe(); - for (i in t) t[i] ? e += wu[i] + ("subs" === i ? encodeURIComponent(t[i]) : t[i]) : r.warn(`Missing ${i} info`); - return e - }(r, s), i.inheritQuery = !1 - } - this.itemQueue.setQueueItem(`item:${null!==(s=null==i?void 0:i.itemId)&&void 0!==s?s:Zl()}`, e, t, null == i ? void 0 : i.platformInfo, null === (t = null == i ? void 0 : i.appData) || void 0 === t ? void 0 : t.serviceName), fg().setStartTime(void 0) - } - queueSource(e, t, i) { - var r; - null != t && t.userInfo && (this.userInfo = t.userInfo); - var n = null === (r = this._mediaElementQuery) || void 0 === r ? void 0 : r.getCombinedBufferInfo(null === (n = this._mediaElementQuery) || void 0 === n ? void 0 : n.currentTime, 0); - let s = 0; - n && (s = n.end), this.itemQueue.addQueueItem(`item:${null!==(n=null==t?void 0:t.itemId)&&void 0!==n?n:Zl()}`, e, i, null == t ? void 0 : t.platformInfo, s, null === (t = null == t ? void 0 : t.appData) || void 0 === t ? void 0 : t.serviceName) - } - dequeueSource(e = "ApplicationInitiated") { - if (!this.isPreloading && "InvalidFormat" === e && this.isFirstItem) return this.logger.error("First item has invalid format for gapless. Probably video. Disabling gapless."), void(this.gaplessCapable = !1); - var t, i; - this.isPreloading ? (t = this.loadingItem.url, i = this.loadingItem.itemId, this.mediaElementAdapter.flushData(yu.Variant, this.loadingItem.itemStartOffset, 1 / 0), this.mediaElementAdapter.msDuration = this.loadingItem.itemStartOffset, this.itemQueue.resetLoadingItem(), "InvalidFormat" !== e && "FatalErrorWhileLoading" !== e || (this.gaplessCapable = !1), this.triggerItemEvicted({ - url: t, - itemId: i - }, e)) : this.logger.warn(`Nothing to dequeue, no item is preloading dequeue reason: ${e}`) - } - triggerItemEvicted(e, t) { - null !== e ? (t = { - url: e.url, - evictedItemId: e.itemId, - reason: t - }, Object.assign(Object.assign({}, t), { - url: le(e.url) - }), this.trigger(P.ITEM_EVICTED, t)) : this.logger.error("dequeueSource called with no playing or loading item") - } - endSource() { - this.gaplessCapable = !1, this.isPreloading && (this.logger.warn("EndSource called during preloading. Loading item will be removed"), this.mediaElementAdapter.flushData(yu.Variant, this.loadingItem.itemStartOffset, 1 / 0), this.mediaElementAdapter.msDuration = this.loadingItem.itemStartOffset, this.itemQueue.resetLoadingItem()) - } - get inGaplessMode() { - return mg().gapless && this.gaplessCapable - } - get isPreloading() { - return this.itemQueue.isPreloading() - } - get isFirstItem() { - return this.itemQueue.isFirstItem - } - get loadingItem() { - return this.itemQueue.loadingItem - } - get playingItem() { - return this.itemQueue.playingItem - } - get url() { - return this.playingItem ? this.playingItem.url : this.loadingItem ? this.loadingItem.url : void 0 - } - destroy() { - const t = this.logger; - return this.destroy$.next(), null != this.rpcService && (this.teardownWG$.add(), this.rpcService.teardown(e => { - e && t.error("RPCService teardown error:", e), this.teardownWG$.done() - }), this.rpcService = null), null != this.iframeMachine && (this.iframeMachine.destroy(), this.iframeMachine = null), this.teardownWG$.toPromise() - } - attachMedia(e) { - this.trigger(P.MEDIA_ATTACHING, { - media: e - }), this.mediaElement$.next(e), this.trigger(P.MEDIA_ATTACHED, { - media: e - }) - } - detachMedia() { - var e; - this.mediaElement$.getValue() && (this.trigger(P.MEDIA_DETACHING), null === (e = this.rtcService) || void 0 === e || e.detachMedia(), null != this.iframeMachine && this.iframeMachine.stop(), this.mediaElement$.next(null), this.trigger(P.MEDIA_DETACHED)) - } - handleResolvedUri(e, t) { - this.customUrlLoader.setCustomUrlResponse(e, { - uri: t.uri, - response: t - }) - } - get variantOptions$() { - return this.publicQueries$.pipe(La(e => { - const [t, i] = e; - return Mr([t.preferredMediaOptions$, i.desiredRate$]).pipe(hr(([e]) => { - const t = i.isIframeRate; - return e[gu.Variant].filter(e => (null !== (e = e.iframes) && void 0 !== e && e) === t).map(e => e.mediaOptionId) - })) - })) - } - get altAudioOptions$() { - return this.publicQueries$.pipe(La(e => { - var [e] = e; - return $i(e.audioMediaSelectionOptions) - })) - } - get subtitleOptions$() { - return this.publicQueries$.pipe(La(e => { - var [e] = e; - return $i([{ - MediaSelectionOptionsName: "Disable subtitle", - MediaSelectionOptionsPersistentID: -1 - }].concat(e.subtitleMediaSelectionOptions)) - })) - } - get levels() { - var e; - return null !== (e = null === (e = this._activeRootQuery) || void 0 === e ? void 0 : e.preferredMediaOptions[gu.Variant]) && void 0 !== e ? e : [] - } - get audioTracks() { - var e; - return null !== (e = null === (e = this._activeRootQuery) || void 0 === e ? void 0 : e.preferredMediaOptions[gu.AltAudio]) && void 0 !== e ? e : [] - } - get audioMediaOptions() { - var e; - return null !== (e = null === (e = this._activeRootQuery) || void 0 === e ? void 0 : e.audioMediaSelectionOptions) && void 0 !== e ? e : [] - } - get subtitleMediaOptions() { - var e; - return null !== (e = null === (e = this._activeRootQuery) || void 0 === e ? void 0 : e.subtitleMediaSelectionOptions) && void 0 !== e ? e : [] - } - get playbackLikelyToKeepUp() { - var e; - return null !== (e = null === (e = this._mediaElementQuery) || void 0 === e ? void 0 : e.playbackLikelyToKeepUp) && void 0 !== e && e - } - get duration$() { - return this.publicQueries$.pipe(La(e => { - var [, e] = e; - return e.mediaElementDuration$ - })) - } - get timeupdate$() { - return this.publicQueries$.pipe(La(e => { - var [, e] = e; - return e.timeupdate$ - })) - } - get playing$() { - return this.publicQueries$.pipe(La(e => { - var [, e] = e; - return e.gotPlaying$ - })) - } - get desiredRate$() { - return this.publicQueries$.pipe(La(e => { - var [, e] = e; - return e.desiredRate$ - })) - } - set desiredRate(e) { - null != e && this.setRate(e) - } - get desiredRate() { - var e; - return null !== (e = null === (e = this._mediaElementQuery) || void 0 === e ? void 0 : e.desiredRate) && void 0 !== e ? e : 0 - } - get effectiveRate() { - var e; - return null !== (e = null === (e = this._mediaElementQuery) || void 0 === e ? void 0 : e.effectiveRate) && void 0 !== e ? e : 0 - } - get iframeMode() { - var e; - return null !== (e = null === (e = this._mediaElementQuery) || void 0 === e ? void 0 : e.isIframeRate) && void 0 !== e && e - } - get accessLog() { - return this.accessLogInstance && this._activeRootQuery ? this.accessLogInstance.getAccessLog(this._activeRootQuery.itemId) : [] - } - get errorLog() { - return this.accessLogInstance ? this.accessLogInstance.errorLog : [] - } - setRate(e) { - var t; - const i = this.logger.child({ - name: "iframes" - }); - if (e === this.desiredRate) return -2; - const r = this.mediaElementAdapter; - if (!r || isNaN(e)) return i.warn("unable to switch to rate, missing adapter or newRate isNaN"), -1; - e = Number(e); - const n = Math.abs(e); - if (!this.supportedFrameRates.some(e => e === n)) return i.warn(`unsupported rate(${e})`), -3; - const s = Wp(e), - a = this.iframeMachine; - if (s) { - const e = this._activeRootQuery; - if (null == e || !e.mediaOptionListQueries[gu.Variant].hasIframes) return i.warn("no iframe variants available"), -1; - r.postFlushSeek = null - } else null != a && a.isStarted && !ne(null === (t = r.mediaQuery) || void 0 === t ? void 0 : t.postFlushSeek) && (a.pause(), r.postFlushSeek = a.iframeClockTimeSeconds); - return r.desiredRate = e, 0 - } - get sessionData$() { - return this.publicQueries$.pipe(La(([e]) => e.sessionData$)) - } - set skip(e) { - this._mediaElementQuery && (this.realCurrentTime = Math.max(0, this.realCurrentTime + e)) - } - gaplessSeekTo(e) { - e < this.playingItem.itemStartOffset && (this.logger.warn(`[Gapless] Seeking past track boundary oldSeek=${e}, adjustedSeek=${this.playingItem.itemStartOffset}`), e = this.playingItem.itemStartOffset), this.isPreloading && (e > this.loadingItem.itemStartOffset && (this.logger.warn(`[Gapless] Seeking past track boundary oldSeek=${e}, adjustedSeek=${this.loadingItem.itemStartOffset}`), e = this.loadingItem.itemStartOffset), e < this._mediaElementQuery.getBufferInfo(this._mediaElementQuery.currentTime, this.config.maxBufferHole)[0].buffered.start && this.dequeueSource("SeekToUnbufferedTimeRanges")), fg().setUserSeek(e) - } - isIframeInternalSeek(e) { - return e === (null === (e = this.iframeMachine) || void 0 === e ? void 0 : e.mediaRootTime) - } - set seekTo(e) { - var t, i = Number(e); - if (ne(i)) - if (this.inGaplessMode) this.gaplessSeekTo(i); - else { - const r = this.mediaElementAdapter; - !r || !ne(null === (t = r.mediaQuery) || void 0 === t ? void 0 : t.postFlushSeek) || r.mediaQuery.seekTo && !this.isIframeInternalSeek(r.mediaQuery.seekTo.pos) ? fg().setUserSeek(i) : r.schedulePostFlushSeek(i) - } - else this.logger.error(`[seek] got invalid seek value ${e}`) - } - seekToDate(e) { - fg().setUserSeek(e) - } - get availableProgramDateTime() { - return new Map(this._currentDateToMediaTimeTuple) - } - get _currentDateToMediaTimeTuple() { - if (!this._activeRootQuery) return []; - var e = this._activeRootQuery.enabledMediaOptionKeys[gu.Variant]; - return _u(e) && null !== (e = null === (e = Cg().getQueryForOption(e).mediaOptionDetails) || void 0 === e ? void 0 : e.dateMediaTimePairs) && void 0 !== e ? e : [] - } - get playingDate() { - return function(e, t) { - if (e && 0 !== e.length) { - const i = [...e].sort((e, t) => t[1] - e[1]), - r = null !== (e = i.find(([, e]) => e <= t)) && void 0 !== e ? e : i[i.length - 1], - [n, s] = r; - return new Date(n + 1e3 * (t - s)) - } - }(this._currentDateToMediaTimeTuple, this.realCurrentTime) - } - set variantId(e) {} - set audioSelectedPersistentID(e) { - var t = this._activeRootQuery; - null != t && t.preferredMediaOptions[gu.AltAudio] ? (t = t.itemId, e !== this.audioSelectedPersistentID && this.rootPlaylistService.setEnabledMediaOptionTupleWithMatchedGroups(t, gu.AltAudio, e, { - userInitiated: !0 - })) : !ne(e) || e < 0 || (this.logger.warn(`[audio] no active item, defer audio track selection: persistentId ${e}`), this.itemQueue.earlyAudioSelection = e) - } - get audioSelectedPersistentID() { - var e; - return this._activeRootQuery ? null === (e = this._activeRootQuery.enabledAlternateMediaOptionByType(gu.AltAudio)) || void 0 === e ? void 0 : e.persistentID : this.itemQueue.earlyAudioSelection - } - set subtitleSelectedPersistentID(e) { - var t = this._activeRootQuery, - i = null == t ? void 0 : t.preferredMediaOptions[gu.Subtitle]; - i ? e !== this.subtitleSelectedPersistentID && (t = t.itemId, 0 === i.length && (!ne(e) || e < 0) || (ne(e) && -1 !== e ? this.rootPlaylistService.setEnabledMediaOptionTupleWithMatchedGroups(t, gu.Subtitle, e) : this.rootPlaylistService.setEnabledMediaOptionByType(t, gu.Subtitle, Lu))) : !ne(e) || e < 0 || (this.logger.warn(`[subtitle] no active item, defer subtitle track selection: persistentId ${e}`), this.itemQueue.earlySubtitleSelection = e) - } - get subtitleSelectedPersistentID() { - var e; - return this._activeRootQuery ? null === (e = this._activeRootQuery.enabledAlternateMediaOptionByType(gu.Subtitle)) || void 0 === e ? void 0 : e.persistentID : this.itemQueue.earlySubtitleSelection - } - get selectedMediaArray() { - const e = this._activeRootQuery; - if (!e) return []; - const t = [], - i = e.enabledAlternateMediaOptionByType(gu.AltAudio), - r = e.enabledAlternateMediaOptionByType(gu.Subtitle), - n = i ? e.audioMediaSelectionOptions.find(e => e.MediaSelectionOptionsPersistentID === i.persistentID) : void 0, - s = r ? e.subtitleMediaSelectionOptions.find(e => e.MediaSelectionOptionsPersistentID === r.persistentID) : void 0; - if (n) { - const e = { - MediaSelectionGroupMediaType: Su.AUDIO, - MediaSelectionOptionsPersistentID: n.MediaSelectionOptionsPersistentID - }; - t.push(e) - } - if (s) { - let e = vu.NO; - s.MediaSelectionOptionsDisplaysNonForcedSubtitles && (e = s.MediaSelectionOptionsDisplaysNonForcedSubtitles); - const i = { - MediaSelectionGroupMediaType: Su.SUBTITLE, - MediaSelectionOptionsDisplaysNonForcedSubtitles: e, - MediaSelectionOptionsPersistentID: s.MediaSelectionOptionsPersistentID - }; - t.push(i) - } - return t - } - set selectedMediaArray(e) { - this._activeRootQuery ? e.forEach(e => { - e.MediaSelectionGroupMediaType === Su.AUDIO || e.MediaSelectionOptionsMediaType === Su.AUDIO ? this.audioSelectedPersistentID = e.MediaSelectionOptionsPersistentID : e.MediaSelectionGroupMediaType !== Su.SUBTITLE && e.MediaSelectionOptionsMediaType !== Su.SUBTITLE && e.MediaSelectionOptionsMediaType !== Su.CLOSEDCAPTION || (this.subtitleSelectedPersistentID = e.MediaSelectionOptionsPersistentID) - }) : this.logger.warn("selectedMediaArray: no active item") - } - getHTMLTextTrack(e) { - return this.legibleSystemAdapter.getExistingHTMLTextTrackWithSubtitleTrackId(e) - } - get keysystems() { - return this.keySystemAdapter.availableKeySystems - } - setProtectionData(e) { - this.keySystemAdapter.initialize(e) - } - generateKeyRequest(e, t) { - this.keySystemAdapter.generateRequest(e, t), this.rtcService.licenseChallengeReceived({ - keyuri: e - }) - } - setLicenseResponse(e, t) { - this.keySystemAdapter.setLicenseResponse(e, t) - } - get bufferInfo$() { - return this.publicQueries$.pipe(La(e => { - const [, t] = e, i = fg().getQuery().currentConfig; - return an(t.timeupdate$, t.bufferedRangeTuple$).pipe(ao(1e3), hr(() => { - var e = t.currentTime; - return { - combined: t.getCombinedBufferInfo(e, i.maxBufferHole), - sbTuple: t.getBufferInfo(e, i.maxBufferHole) - } - })) - })) - } - bufferInfoByType$(t) { - return this.bufferInfo$.pipe(hr(e => null === (e = null == e ? void 0 : e.sbTuple) || void 0 === e ? void 0 : e[t])) - } - levelWithPersistentId(e) { - this.logger.warn("levelWithPersistentId is deprecated") - } - startLoad(e) { - this.logger.warn("startLoad is deprecated"), ne(e) && (this.logger.warn(`[seek] Seeking to ${null==e?void 0:e.toFixed(3)} via deprecated "startLoad" method. Use loadSource(url, options, startTime) instead.`), this.seekTo = e) - } - stopLoad() {} - get config() { - return Object.assign(Object.assign({}, de(mg())), { - set startPosition(e) { - Qe().warn(`Setting start position ${null==e?void 0:e.toFixed(3)} using deprecated method`), fg().setStartTime(e) - } - }) - } - get media() { - return null != this.mediaElement$.value - } - set subtitleDisplay(e) { - this.logger.warn(`set subtitleDisplay ${e} is deprecated`) - } - } - }, "object" == typeof exports && "undefined" != typeof module ? module.exports = t() : "function" == typeof define && define.amd ? define(t) : (e = "undefined" != typeof globalThis ? globalThis : e || Jy).Hls = t() -}(!1); -//# sourceMappingURL=hls.js.map \ No newline at end of file + }))}`); + const newMediaRootTime = chosenFrags.reduce((prev, cur) => { + return Math.max(prev, isFiniteNumber(cur === null || cur === void 0 ? void 0 : cur.newMediaRootTime) ? cur.newMediaRootTime : -Infinity); + }, -Infinity); + if (isFiniteNumber(newMediaRootTime)) { + logger.info(`seek to newMediaRootTime ${newMediaRootTime}`); + mediaSink.seekTo = newMediaRootTime; + chosenFrags = [null, null]; + } + if (chosenFrags.every((f) => (f === null || f === void 0 ? void 0 : f.foundFrag) == null)) { + return of(null); // no more to produce + } + return zip(...chosenFrags.map((f, type) => { + if (!f || f.foundFrag == null) { + return of(null); + } + else { + const { mediaFragment } = f.foundFrag; + const prefetchKey$ = loadKey(pipelineContext, mediaFragment.keyTagInfo, { itemId: mediaFragment.itemId, mediaOptionId: mediaFragment.mediaOptionId }); + let fetchAndParse$ = retrieveMediaFragmentCacheEntity(pipelineContext, type, f).pipe(tap((sbDataTuple) => { + var _a; + const mediaFragmentCacheEntity = sbDataTuple[1]; + const switchContext = detailsAndContext[type].switchContext; + mediaFragmentCacheEntity.switchPosition = switchContext === null || switchContext === void 0 ? void 0 : switchContext.switchPosition; + // if user initiated or rewind rate change + const userInitiated = (_a = switchContext === null || switchContext === void 0 ? void 0 : switchContext.userInitiated) !== null && _a !== void 0 ? _a : false; + const { mediaQuery } = mediaSink; + const { desiredRate, isIframeRate } = mediaQuery; + const rewindRateChange = isIframeRate && iframeMachine.isStarted && desiredRate && desiredRate < 0 && desiredRate !== iframeMachine.iframeRate; + if (userInitiated || rewindRateChange) { + logger.info(`creating flushBeforeAppend > userInitiated=${userInitiated}, rewindRateChange=${rewindRateChange} :: iframeMachine.isStarted=${iframeMachine.isStarted}, iframeMachine.iframeRate=${iframeMachine.iframeRate}, desiredRate=${desiredRate}`); + mediaFragmentCacheEntity.flushBeforeAppend = { start: 0, end: Number.POSITIVE_INFINITY }; + } + })); + // UpdatePTSDTS is expensive on startup + // try to parallelize with key request and/or fragment request + if (type === MediaOptionType.Variant) { + fetchAndParse$ = fetchAndParse$.pipe(tap((sbData) => { + const initPTSInfo = updateInitPTS(logger, pipelineContext, sbData, fragmentPicker.discoSeqNum); + updatePTSDTS(logger, pipelineContext, sbData, initPTSInfo); + })); + } + else { + const initPTSInfo$ = waitFor(rootPlaylistQuery.initPTS$(fragmentPicker.discoSeqNum), (initPTSInfo) => { + const iframeMode = mediaSink.mediaQuery.isIframeRate; + return initPTSInfo != null && (iframeMode || !initPTSInfo.iframeMode); + }); + fetchAndParse$ = forkJoin([fetchAndParse$, initPTSInfo$]).pipe(map(([sbData, initPTSInfo]) => { + updatePTSDTS(logger, pipelineContext, sbData, initPTSInfo); + return sbData; + })); + } + return forkJoin([prefetchKey$, fetchAndParse$]).pipe(map((res) => res[1])); + } + })).pipe(map((sbDataTuple) => getConsumerInput(logger, pipelineContext, fragmentPicker.discoSeqNum, sbDataTuple)), switchMap((consumerInput) => { + if (!consumerInput) { + // Potentially dangerous (unbounded loop) if we keep selecting the same fragment + logger.info('Attempting fragment reselection because of complete overlap with updated details'); + const updatedDetailsAndContext = getUpdatedDetailsAndContext(rootPlaylistQuery, detailsAndContext, logger); + return chooseAndFetchFragments(logger, fragmentPicker, pipelineContext, action, updatedDetailsAndContext); + } + return of(consumerInput); + })); + } + function getUpdatedDetailsAndContext(rootPlaylistQuery, detailsAndContext, logger) { + const enabledMediaKeys = rootPlaylistQuery.enabledMediaOptionKeys; + const updatedDetailsAndContext = [null, null]; + const detailsInfo = [null, null]; + AVMediaOptionTypes.map((type) => { + var _a, _b; + if (isEnabledMediaOption(enabledMediaKeys[type])) { + const libraryQuery = createMediaLibraryQuery(enabledMediaKeys[type]); + const detailsEntity = libraryQuery.mediaOptionDetailsEntity; + detailsInfo[type] = (_a = detailsEntity.mediaOptionDetails) === null || _a === void 0 ? void 0 : _a.ptsKnown; + updatedDetailsAndContext[type] = { detailsEntity, switchContext: (_b = detailsAndContext[type]) === null || _b === void 0 ? void 0 : _b.switchContext }; + } + }); + logger.info(`retrieve updated details entity ${JSON.stringify(detailsInfo)}`); + return updatedDetailsAndContext; + } + /** + * Update initPTS info + * @param vTuple the parsed fragment information for the variant + */ + function updateInitPTS(logger, pipelineContext, vTuple, activeDiscoSeqNum) { + var _a, _b, _c; + if (!vTuple) { + return null; + } + const { rootPlaylistService: rootService, rootPlaylistQuery: rootQuery } = pipelineContext; + const itemId = rootQuery.itemId; + const variantData = vTuple[1]; + const fragIsIframe = variantData.iframe; + // Favor non-iframe initPTS if available + let initPTSInfo = rootQuery.getInitPTS(activeDiscoSeqNum); + if (initPTSInfo == null || (!fragIsIframe && initPTSInfo.iframeMode)) { + const variantDTS = (_a = variantData.startDtsTs) !== null && _a !== void 0 ? _a : null; + if (variantDTS == null) { + logger.warn('updateInitPTS: Variant data missing.'); + return null; + } + let timelineOffset = (_b = variantData.timelineOffset) !== null && _b !== void 0 ? _b : 0; + if (fragIsIframe) { + logger.info(`updateInitPTS: initPTS timelineOffset is appendData iframeOriginalStart ${vTuple[1].iframeOriginalStart}`); + timelineOffset = (_c = variantData.iframeOriginalStart) !== null && _c !== void 0 ? _c : 0; + } + else { + logger.info(`updateInitPTS: initPTS timelineOffset is appendData timelineOffset ${vTuple[1].timelineOffset}`); + } + if (variantDTS.timescale < 1000) { + // this scaleup is especially useful when traversing 23.976/24 fps discos + variantDTS.timescale = variantDTS.timescale * 1000; + variantDTS.baseTime = variantDTS.baseTime * 1000; + } + const playlistTS = convertSecondsToTimestamp(timelineOffset, variantDTS.timescale); + const offsetTimestamp = { baseTime: variantDTS.baseTime - playlistTS.baseTime, timescale: variantDTS.timescale }; + logger.info(`initPTS[${activeDiscoSeqNum}] ${JSON.stringify({ variantDTS, timelineOffset, offsetTimestamp, fragIsIframe })}`); + rootService.setInitPTS(itemId, activeDiscoSeqNum, variantDTS, timelineOffset, offsetTimestamp, fragIsIframe); + initPTSInfo = { variantDTS, timelineOffset, offsetTimestamp, iframeMode: fragIsIframe }; + } + return initPTSInfo; + } + function updatePTSDTS(logger, pipelineContext, sbData, initPTSInfo) { + const { mediaLibraryService, rootPlaylistQuery, mediaSink } = pipelineContext; + const itemId = rootPlaylistQuery.itemId; + if (initPTSInfo == null) { + return; + } + // Don't update if we're in normal playback and we have an i-frame initPTS + const curIframeMode = mediaSink.mediaQuery.isIframeRate; + if (!curIframeMode && initPTSInfo.iframeMode) { + logger.warn('updatePTSDTS iframeMode mismatch'); + return; + } + // Update playlist offsets. Never update for main in i-frame mode + if (sbData && !isFiniteNumber(sbData[1].iframeMediaDuration)) { + const tstart = performance.now(); + mediaLibraryService.updatePTSDTS(itemId, sbData[1].mediaOptionId, initPTSInfo, sbData[1]); + logger.info(`updatePTSDTS took ${Math.round(performance.now() - tstart)}ms`); + } + } + function getConsumerInput(logger, pipelineContext, activeDiscoSeqNum, sbDataTuple) { + const { rootPlaylistQuery: rootQuery, mediaSink, config } = pipelineContext; + const mediaQuery = mediaSink.mediaQuery; + const iframeMode = mediaQuery.isIframeRate; + const initPTSInfo = rootQuery.getInitPTS(activeDiscoSeqNum); + if (initPTSInfo == null) { + logger.warn('No initPTS info found'); + return null; + } + const variantData = sbDataTuple[MediaOptionType.Variant]; + const variantFragData = variantData === null || variantData === void 0 ? void 0 : variantData[1]; + if (variantFragData && variantFragData.iframe !== iframeMode) { + logger.warn(`frag mediaSeqNum ${variantFragData.mediaSeqNum} discoSeqNum ${variantFragData.discoSeqNum} mediaOptionId ${variantFragData.mediaOptionId} doesn't match mediaSink's iframeMode ${iframeMode}; discard`); + return null; // repick frag since the parsed one doesn't match mediaSink's iframe mode + } + // Build output + const appendDataTuple = [null, null]; + if (variantData) { + const [initSeg, dataSeg] = variantData; + let offsetTimestamp = initPTSInfo.offsetTimestamp; + if (iframeMode) { + // Always offset video + const dts = dataSeg.startDtsTs; + const timelineOffset = dataSeg.timelineOffset; + const playlistTS = convertSecondsToTimestamp(timelineOffset, dts.timescale); + offsetTimestamp = { baseTime: dts.baseTime - playlistTS.baseTime, timescale: dts.timescale }; + } + appendDataTuple[SourceBufferType.Variant] = { initSeg, dataSeg, offsetTimestamp }; + } + const audioData = sbDataTuple[MediaOptionType.AltAudio]; + if (audioData != null) { + const [initSeg, dataSeg] = audioData; + appendDataTuple[SourceBufferType.AltAudio] = { initSeg, dataSeg, offsetTimestamp: initPTSInfo.offsetTimestamp }; + } + // Update in flight fragments + const inFlightFrags = appendDataTuple.map((data, type) => { + var _a, _b; + const dataSeg = data === null || data === void 0 ? void 0 : data.dataSeg; + if (dataSeg) { + const { itemId, mediaOptionId, mediaSeqNum, discoSeqNum, startPts, endPts, duration, iframe } = dataSeg; + const { offsetTimestamp } = data; + const startPTSSec = diffSeconds(startPts, offsetTimestamp); + const endPTSSec = diffSeconds(endPts, offsetTimestamp); + const libraryQuery = createMediaLibraryQuery(dataSeg); + logger.info(`${MediaOptionNames[type]} aboutToAppend iframe:${dataSeg.iframeMediaDuration != null} ${stringifyWithPrecision({ + mediaOptionId, + mediaSeqNum, + discoSeqNum, + offsetTimestamp, + startPTSSec, + endPTSSec, + startPts, + endPts, + })} buffered:${stringifyWithPrecision(mediaQuery.getBufferedRangeByType(type))}`); + const videoSegment = appendDataTuple[0]; + const noDroppedFrames = !videoSegment || !videoSegment.dataSeg.dropped; + if (noDroppedFrames && !dataSeg.flushBeforeAppend && ((_b = (_a = mediaQuery.getBufferInfo(startPTSSec, config.maxBufferHole)[type]) === null || _a === void 0 ? void 0 : _a.buffered) === null || _b === void 0 ? void 0 : _b.len) >= endPTSSec - startPTSSec) { + logger.warn(`${MediaOptionNames[type]} Discarding append due to complete overlap with existing buffer`); + appendDataTuple[type] = null; + return null; + } + return { + start: startPTSSec, + duration: iframe ? duration : endPTSSec - startPTSSec, + itemId, + mediaOptionId, + mediaSeqNum, + discoSeqNum, + targetDuration: libraryQuery.mediaOptionDetails.targetduration, + }; + } + return null; + }); + if (inFlightFrags.every((f) => !f)) { + return null; + } + return { appendDataTuple, inFlightFrags, initPTSInfo }; + } + /** + * @brief The media consumer side + * transform consumerInput to boolean indicating whether we actually appended anything + */ + const mediaConsumerEpic = (context) => (consumerInput) => { + const { rootPlaylistQuery: rootQuery, rootPlaylistService: rootService, mediaSink, legibleSystemAdapter, statsService, rtcService } = context; + return consumerInput.pipe(tag('mediaConsumerEpic.in'), + // Do Append + switchMap((appendData) => { + if (!appendData) { + return of(false); + } + const { appendDataTuple, inFlightFrags, initPTSInfo } = appendData; + const { offsetTimestamp } = initPTSInfo; + inFlightFrags.forEach((frag, type) => { + if (frag) { + rootService.updateInflightFrag(frag.itemId, type, frag, 'appending', null); + } + }); + // Pass rootQuery.itemStartOffset into appendData. + // add samples based on captiondata + appendDataTuple.forEach((sbAppendData) => { + if (sbAppendData) { + const dataSeg = sbAppendData.dataSeg; + legibleSystemAdapter.addLegibleSamples(offsetTimestamp, dataSeg.captionData, dataSeg.id3Samples, dataSeg.endPts); + } + }); + return mediaSink + .appendData(appendDataTuple, (sbAdapter, mediaOptionType, mediaOptionId, config, meQuery) => { + var _a; + const targetDuration = (_a = inFlightFrags[mediaOptionType].targetDuration) !== null && _a !== void 0 ? _a : 10; + return addAppendErrorHandlingPolicy(mediaSink, sbAdapter, mediaOptionType, mediaOptionId, targetDuration, config, rootQuery, rootService, meQuery); + }, rootQuery.highestVideoCodec) + .pipe(map((metrics) => { + var _a, _b; + inFlightFrags.forEach((frag, type) => { + if (frag) { + rootService.updateInflightFrag(frag.itemId, type, frag, 'appended', null); + } + }); + const metric = metrics.filter((metric) => (metric === null || metric === void 0 ? void 0 : metric.fragmentType) === MediaOptionType.Variant); + if (metric.length) { + statsService.setBufferMetric(metric[0]); + rtcService === null || rtcService === void 0 ? void 0 : rtcService.handleFragBuffered(metric[0]); + } + const audioAppendData = appendDataTuple[SourceBufferType.AltAudio]; + if (((_a = audioAppendData === null || audioAppendData === void 0 ? void 0 : audioAppendData.dataSeg) === null || _a === void 0 ? void 0 : _a.flushBeforeAppend) || isFiniteNumber((_b = audioAppendData === null || audioAppendData === void 0 ? void 0 : audioAppendData.dataSeg) === null || _b === void 0 ? void 0 : _b.switchPosition)) { + const { itemId, mediaOptionId } = audioAppendData.dataSeg; + rootService.setEnabledMediaOptionSwitchContextByType(itemId, MediaOptionType.AltAudio, mediaOptionId, undefined); + } + return true; + }), addCreateSourceBufferErrorHandlingPolicy(mediaSink, rootService, rootQuery)); + })); + }; + + /** + * Convert seek to date values into media position seeks + */ + /** + * @brief Get the media time for a given date + * @param dateMediaTimeTuple DateTimeMs, mediaTimeSec pairs sorted in ascending order + * @param searchDate date to resolve + * @returns media position. 0 if earlier than beginning of playlist + */ + function resolveDateToPTS(dateMediaTimeTuple, searchDate) { + var _a; + if (!dateMediaTimeTuple || dateMediaTimeTuple.length === 0) { + return 0; + } + // Scan for the max startDateTime that is smaller than the searchDate + const descendingTuple = [...dateMediaTimeTuple].sort((a, b) => b[0] - a[0]); + const searchDt = searchDate.getTime(); + const closest = (_a = descendingTuple.find(([dt]) => searchDt >= dt)) !== null && _a !== void 0 ? _a : descendingTuple[descendingTuple.length - 1]; + const [closestDt, startPTSSec] = closest; + const resolvedTime = startPTSSec + (searchDt - closestDt) / 1000; + return Math.max(0, resolvedTime); + } + /** + * @brief Get the date for a given position in media + * @param dateMediaTimeTuple DateTimeMs, mediaTimeSec pairs sorted in ascending order + * @param ptsSeconds time in media seconds + * @returns Date associated with this time. undefined if no date available + */ + function resolvePTSToDate(dateMediaTimeTuple, ptsSeconds) { + var _a; + if (!dateMediaTimeTuple || dateMediaTimeTuple.length === 0) { + return undefined; + } + const descendingTuple = [...dateMediaTimeTuple].sort((a, b) => b[1] - a[1]); + const closest = (_a = descendingTuple.find(([_, mediaTime]) => ptsSeconds >= mediaTime)) !== null && _a !== void 0 ? _a : descendingTuple[descendingTuple.length - 1]; + const [closestDt, startPTSSec] = closest; + const resolvedDate = new Date(closestDt + (ptsSeconds - startPTSSec) * 1000); + return resolvedDate; + } + function resolveSeekToDateAsync(seekTo, rootQuery, libService) { + return rootQuery.enabledMediaOptionByType$(MediaOptionType.Variant).pipe(switchMap((option) => { + return libService.getQueryForOption(option).mediaOptionDetails$; + }), map((details) => resolveDateToPTS(details.dateMediaTimePairs, seekTo)), take(1)); + } + /** + * Return seek position within media + * @param seekTo + * @param rootPlaylistQuery + * @returns + */ + function resolveSeekToAsync(seekTo, isFirstSeek, config, rootPlaylistQuery, libService, logger) { + // seekTo value meanings: + // non-finite = auto seek position. LIVE: live edge, VOD: startTimeOffset if available or start of item + // 0 = if (LIVE && liveEdgeForZeroStartPositon), live edge, else 0 + // positive = position in media timeline + // negative = LIVE: live edge, VOD: position relative to end of playlist + let resolvedSeekTo$ = rootPlaylistQuery.enabledMediaOptionByType$(MediaOptionType.Variant).pipe(switchMap((variant) => libService.getQueryForOption(variant).mediaOptionDetails$), filter((details) => isFiniteNumber(details === null || details === void 0 ? void 0 : details.totalduration)), take(1), map((details) => { + const hasEndTag = !details.liveOrEvent; + const totalDuration = details.totalduration; + const itemStartOffset = rootPlaylistQuery.itemStartOffset; + if (hasEndTag) { + if (!isFiniteNumber(seekTo)) { + const offset = isFiniteNumber(details.startTimeOffset) ? details.startTimeOffset : 0; + return itemStartOffset + offset; + } + else if (seekTo >= 0) { + // seekTo positive values are never pending + // seekTo 0 is pending only when liveEdgeForZeroStartPositon is opted in + return seekTo; + } + else { + // seekTo negative values + const offset = totalDuration + seekTo; + return itemStartOffset + offset; + } + } + else { + // live and event (no EXT-X-END tag) + if (!isFiniteNumber(seekTo) || seekTo < 0 || (seekTo === 0 && config.liveEdgeForZeroStartPositon)) { + // catch up to live edge + return computeLivePosition(0, details, config, logger); + } + else { + // isFinite(seekTo) && seekTo >= 0 && (seekTo !== 0 || !liveEdgeForZeroStartPosition) + // sanitize seek value + return sanitizeLiveSeek(seekTo, details, config, logger); + } + } + })); + if (isFirstSeek) { + resolvedSeekTo$ = resolvedSeekTo$.pipe(tap((seekTo) => { + logger.qe({ critical: true, name: 'initialSeekSet', data: { seekTo, source: 'rootPlaylistQuery.pendingSeek' } }); + })); + } + return resolvedSeekTo$; + } + /** + * Epic that just updates pending seek in active item from hls service. + * @param rootQuery + * @param libService + * @returns + */ + function userSeekEpic(itemQueue, rootService) { + return (userSeek$) => userSeek$.pipe(filterNullOrUndefined(), switchMap((seekTo) => forkJoin([ + of(seekTo), + itemQueue.activeItemById$.pipe(filterNullOrUndefined(), switchMap((entity) => { + return rootService.getQueryForId(entity.itemId).rootPlaylistEntity$; + }), take(1)), + ])), map(([seekTo, activeItem]) => { + rootService.setPendingSeek(activeItem.itemId, seekTo); + return seekTo; + })); + } + /** + * Epic that handles requested seeks made to the current active item and + * then updates mediaSink position. Expected to be subscribed on active item change + * + * @param config configuration for seek resolution + * @param logger logger object + * @param mediaSink MediaSink object + * @param rootQuery Query for the active item + * @param libService Media library service + * @returns + */ + function pendingSeekEpic(config, logger, mediaSink, rootService, rootQuery, libService) { + logger = logger.child({ name: 'seek' }); + return (pendingSeek$) => pendingSeek$.pipe(switchMap((pendingSeek, index) => { + if (pendingSeek == null) { + return EMPTY; + } + logger.info(`got pendingSeek=${pendingSeek}`); + const gotNewerSeek$ = mediaSink.mediaQuery.seekTo$.pipe(skip(1), // seekTo$ always emits the active seekTo value to new subscribers (which is populated while seeking, null otherwise) + filterNullOrUndefined()); + return resolveSeek(pendingSeek, index === 0, config, logger, rootQuery, libService).pipe(finalize$1(() => { + rootService.setPendingSeek(rootQuery.itemId, undefined); + }), takeUntil(gotNewerSeek$)); + }), tap((pendingSeek) => { + logger.info(`resolved pendingSeek=${pendingSeek}`); + if (isFiniteNumber(pendingSeek)) { + mediaSink.seekTo = pendingSeek; + } + })); + } + function resolveSeek(seekTo, isFirst, config, logger, rootQuery, libService) { + if (seekTo == null) { + return EMPTY; + } + if (seekTo instanceof Date) { + return resolveSeekToDateAsync(seekTo, rootQuery, libService); + } + else { + return resolveSeekToAsync(seekTo, isFirst, config, rootQuery, libService, logger); + } + } + + function updateEnabledMediaOptionsAsync(rootPlaylistService, itemId) { + return timer(0).pipe(map(() => { + rootPlaylistService.logger.debug(`[updateEnabledMediaOptions] itemId ${itemId}`); + rootPlaylistService.updateEnabledMediaOptions(itemId); + })); + } + /** + * Handle end of the pipeline error. Update the media options as needed based + * on nextMediaOptionsKeys (updated by ErrorHandlingPolicies) + * + * Should always be the last operator. + */ + function mediaPipelineHandleError(pipelineContext) { + const { logger, rootPlaylistService, rootPlaylistQuery } = pipelineContext; + const itemId = rootPlaylistQuery.itemId; + return (source) => source.pipe(retryWhen((errors) => errors.pipe(mergeMap((err) => { + logger.error(`Got error in pipeline ${err.message} fatal:${err === null || err === void 0 ? void 0 : err.fatal} handled:${err === null || err === void 0 ? void 0 : err.handled}`); + if (!(err instanceof HlsError) || err.fatal) { + throw err; + } + if (!err.handled) { + return EMPTY; // Do nothing, wait for next emit + } + // Not fatal, we can re-subscribe to source after updating media options + return updateEnabledMediaOptionsAsync(rootPlaylistService, itemId); + })))); + } + /** + * Core fetch and append epic. Listens to what details are available from the playlist service, + * and the playback and buffering state from the mediaPlaybackService. It can respond to either + * by changing downloading fragments or initiating track switching. + * + * store with these selections + * @param mediaPlaybackService + * @param config Configuration needed load and playback + */ + const mediaFragmentPipelineEpic = () => (pipelineActionSource$) => { + return pipelineActionSource$.pipe(tag('mediaFragmentPipelineEpic.in'), switchMap((pipelineContext) => { + if (!pipelineContext) { + return EMPTY; + } + const { logger, config, platformService, rootPlaylistService, rootPlaylistQuery, keySystemAdapter, mediaSink, mediaParser, gaplessInstance, mediaLibraryService } = pipelineContext; + const { itemId } = rootPlaylistQuery; + const { mediaQuery } = mediaSink; + logger.info(`mediaFragmentPipelineEpic start ${itemId}`); + const keyStatusChange$ = keySystemAdapter.keyStatusChange$.pipe(keyStatusChangeEpic(pipelineContext)); + const platformQuery = platformService.getQuery(); + const displayChange$ = platformQuery.displaySupportsHdr$.pipe(distinctUntilChanged(), switchMap((capable) => { + logger.info(`hdr display change=${capable}`); + rootPlaylistService.setHDRPreference(itemId, capable, true); + return EMPTY; + })); + const viewPortChange$ = platformQuery.viewportInfo$.pipe(distinctUntilChanged((a, b) => a && b && a.width === b.width && a.height === b.height), tap((viewportInfo) => { + logger.info(`viewport change=${JSON.stringify(viewportInfo)}, useViewportSizeForLevelCap: ${config.useViewportSizeForLevelCap}`); + if (config.useViewportSizeForLevelCap) { + rootPlaylistService.setViewportInfo(itemId, viewportInfo); + } + }), switchMapTo(EMPTY)); + const needsMediaReset$ = combineQueries([rootPlaylistQuery.hdrMode$.pipe(distinctUntilChanged()), rootPlaylistQuery.maxHdcpLevel$.pipe(distinctUntilChanged())]).pipe(switchMap(([hdrMode, maxHdcpLevel]) => { + logger.info(`hdrMode=${hdrMode} maxHdcpLevel=${maxHdcpLevel}`); + // if not in gapless mode and the current item is not offset on the timeline, reset the media source + if (!gaplessInstance.inGaplessMode && rootPlaylistQuery.itemStartOffset === 0) { + mediaSink.resetMediaSource(); + mediaParser.reset(); + } + return EMPTY; + })); + // Register this separately so errors here that may be recovered don't cause us to re-subscribe to the platform updates + const mediaPipes$ = merge( + // prettier-ignore + handleEnabledMediaOptions(pipelineContext), makeAVPipe(pipelineContext), makeSubtitlePipe(pipelineContext), keyStatusChange$).pipe( + // This updates enabled media options whenever an emit happens + mapTo(undefined), mediaPipelineHandleError(pipelineContext)); + const anchor$ = mediaQuery.seekTo$.pipe(filter((seekTo) => isFiniteNumber(seekTo === null || seekTo === void 0 ? void 0 : seekTo.pos)), distinctUntilChanged((a, b) => Math.abs(a.pos - b.pos) < Number.EPSILON), // Can choose different threshold + switchMap((anchorTime) => { + rootPlaylistService.setAnchorTime(itemId, anchorTime); + return EMPTY; + })); + // Non-functional, Logs compatible levels after playback starts + const logCompatibleLevels$ = mediaQuery.gotPlaying$.pipe(filter((playing) => playing), tap((_) => { + const mediaOptionQuery = rootPlaylistQuery.mediaOptionListQueries[MediaOptionType.Variant]; + const compatibleMediaLevels = mediaOptionQuery.filteredMediaOptionList; + logger.qe({ critical: true, name: 'compatibleLevels', data: { numLevels: compatibleMediaLevels.length } }); + compatibleMediaLevels.forEach((level) => { + const { mediaOptionId, bandwidth, bitrate, videoCodec, audioCodec, height, width, videoRange, iframes } = level; + logger.qe({ critical: true, name: 'manifestLevel', data: { mediaOptionId, bandwidth, bitrate, videoCodec, audioCodec, height, width, videoRange, iframes } }); + }); + }), take(1), switchMapTo(EMPTY)); + return merge( + // Seeking / position adjustments + rootPlaylistQuery.pendingSeek$.pipe(pendingSeekEpic(config, logger, mediaSink, rootPlaylistService, rootPlaylistQuery, mediaLibraryService)), ensurePlaybackWithinWindow(pipelineContext), // Adjust position within live window + anchor$, + // Download pipe + mediaPipes$, + // Following observables monitor state changes that may modify enabled media options + displayChange$, viewPortChange$, needsMediaReset$, desiredRateChange(pipelineContext), capToEnabledIframeOption(pipelineContext), logCompatibleLevels$).pipe(tag('mediaFragmentPiplineEpic.emit'), mapTo(undefined)); + })); + }; + /** + * update expected SbCount and load all media playlists + */ + function handleEnabledMediaOptions(pipelineContext) { + const { logger, rootPlaylistQuery, mediaSink } = pipelineContext; + const updateExpectedSbCount$ = rootPlaylistQuery.enabledMediaOptions$.pipe(filterNullOrUndefined(), tap((enabledOptions) => { + const altAudio = enabledOptions[MediaOptionType.AltAudio]; + const expectedSbCount = isEnabledMediaOption(altAudio) && (altAudio === null || altAudio === void 0 ? void 0 : altAudio.url) != null ? 2 : 1; + mediaSink.setExpectedSbCount(expectedSbCount); + })); + const setBufferTargetDuration$ = forkJoin([ + rootPlaylistQuery.enabledMediaOptionByType$(MediaOptionType.Variant).pipe(filter((option) => isEnabledMediaOption(option)), switchMap((option) => { + const query = createMediaLibraryQuery(option); + return query.mediaOptionDetails$; + }), take(1) // Just update on first load. Target duration should be consistent across variants + ), + waitFor(mediaSink.mediaQuery.updating$, (x) => x), // On startup don't need to do this immediately + ]).pipe(observeOn(asyncScheduler), tap(([details]) => { + mediaSink.bufferMonitorTargetDuration = details.targetduration; + })); + const loadMediaOptions = MediaOptionTypes.map((mediaOptionType) => { + return rootPlaylistQuery.enabledMediaOptionByType$(mediaOptionType).pipe(tag('mediaOptionRetrieve.switch'), switchMap((option) => { + if (!option || !option.url || !isEnabledMediaOption(option)) { + return EMPTY; + } + const allowRefresh$ = mediaSink.mediaQuery.desiredRate$.pipe(map((rate) => rate !== 0), distinctUntilChanged()); + // always request first copy. this will either return cached value or request from server + return retrieveMediaOptionDetails(pipelineContext, option).pipe(tag('mediaOptionRetrieve.first'), switchMapTo(allowRefresh$), switchMap((allowRefresh) => { + if (!allowRefresh) { + return EMPTY; // Don't refresh while in pause + } + // refresh + return refreshMediaOptionDetails(pipelineContext, option); + })); + })); + }); + return merge(updateExpectedSbCount$, setBufferTargetDuration$, merge(...loadMediaOptions)).pipe(switchMapTo(EMPTY)); + } + function makeSubtitlePipe(pipelineContext) { + const { rootPlaylistQuery, mediaSink } = pipelineContext; + const mediaQuery = mediaSink.mediaQuery; + return waitFor(mediaQuery.mediaElementEntity$, (entity) => entity).pipe(switchMap((_) => { + return rootPlaylistQuery.anchorTime$.pipe(tag('anchorTime.subtitle.in'), filter((anchorTime) => isFiniteNumber(anchorTime === null || anchorTime === void 0 ? void 0 : anchorTime.pos)), subtitleSelectionEpic(pipelineContext), subtitleLoadAndProcessEpic(pipelineContext)); + })); + } + function keyStatusChangeEpic(keyLoadContext) { + return (source) => source.pipe(switchMap((event) => { + const { decryptdata, status, error } = event; + const { rootPlaylistQuery } = keyLoadContext; + if (status === 'needs-renewal') { + return loadKey(keyLoadContext, decryptdata, null); + } + else if (status === 'error' && (error instanceof KeyRequestError || error instanceof KeyRequestTimeoutError) && !error.handled) { + // Don't actually retry, we're just doing penalty box stuff and updating enabled options + const { rootPlaylistService, keySystemAdapter } = keyLoadContext; + return handleKeyError(error, 0, null, rootPlaylistService, rootPlaylistQuery, keySystemAdapter.ksQuery); + } + return EMPTY; + }), switchMap(() => EMPTY)); + } + /** + * @brief subtitle selection + */ + const subtitleSelectionEpic = (pipelineContext) => (anchorTimeSource$) => { + const { rootPlaylistQuery, rootPlaylistService, legibleSystemAdapter } = pipelineContext; + // setup HTML5 texttracks + const enabledMediaOption = rootPlaylistQuery.enabledAlternateMediaOptionByType(MediaOptionType.Subtitle); + if (!legibleSystemAdapter.gotTracks) { + const availableAlternateMediaOptions = rootPlaylistQuery.preferredMediaOptions[MediaOptionType.Subtitle]; + legibleSystemAdapter.setTracks(availableAlternateMediaOptions, enabledMediaOption, rootPlaylistQuery.getDisabledMediaOption(MediaOptionType.Subtitle)); + } + else { + legibleSystemAdapter.selectedTrack = enabledMediaOption; + } + return anchorTimeSource$.pipe(tag('subtitleEpic.select.in'), switchMap(() => { + return merge(legibleSystemAdapter.nativeSubtitleTrackChange$.pipe( + // mediaOption selected via native media element video controller UI + switchMap((mediaOption) => { + if (mediaOption.mediaOptionId !== legibleSystemAdapter.selectedMediaOption.mediaOptionId) { + rootPlaylistService.setEnabledMediaOptionByType(mediaOption.itemId, MediaOptionType.Subtitle, mediaOption); + } + return EMPTY; + })), rootPlaylistQuery + .enabledMediaOptionByType$(MediaOptionType.Subtitle) + .pipe( + // mediaOption selected via hls.js API + map((option) => { + // option may be null initially + const altMediaOption = isEnabledMediaOption(option) ? rootPlaylistQuery.alternateMediaOptionById(MediaOptionType.Subtitle, option.mediaOptionId) : option; + legibleSystemAdapter.selectedMediaOption = altMediaOption; + return altMediaOption; + })) + .pipe(distinctUntilChanged((a, b) => { + return (a === null || a === void 0 ? void 0 : a.mediaOptionId) === (b === null || b === void 0 ? void 0 : b.mediaOptionId); + }))); + }), tag('subtitleEpic.select.emit')); + }; + const loadOneSubtitleFrag = (pipelineContext, initPTS, loadingFrag) => { + const { rootPlaylistQuery, legibleSystemAdapter } = pipelineContext; + return defer(() => { + return retrieveSubtitleFragmentCacheEntity(pipelineContext, initPTS, loadingFrag).pipe(map(({ initPTS, data, mediaFragment }) => { + const enabledAltMediaOption = rootPlaylistQuery.enabledAlternateMediaOptionByType(MediaOptionType.Subtitle); + const parsedCueRecord = processSubtitlePayload(enabledAltMediaOption, mediaFragment, initPTS, data, legibleSystemAdapter); + return { frag: mediaFragment, cueRange: parsedCueRecord }; + })); + }); + }; + const loadSubtitleBatch = (pipelineContext, initPTS, findFragsResult, details) => { + const legibleServiceAdapter = pipelineContext.legibleSystemAdapter; + const nextFrags = findFragsResult.foundFrags; + return from(nextFrags).pipe( + // emit next frags to load in a sequence + map((frag) => { + return loadOneSubtitleFrag(pipelineContext, initPTS, frag).pipe(delayWhen((parsedFragResult) => { + return legibleServiceAdapter.checkReadyToLoadNextSubtitleFragment$(frag, nextFrags).pipe(filter((x) => x)); + })); + }), mergeAll(pipelineContext.config.vttConcurrentLoadCount), tap((parsedFragResult) => { + if (legibleServiceAdapter.reviewParsedFrag(parsedFragResult, findFragsResult, details) !== ParsedFragQuality.CloseEnough) { + // may be too far from target frag. find another frag based on newly parsed cues + pipelineContext.legibleSystemAdapter.tryAgain$.next(true); + } + })); + }; + const loadSubtitleInDiscontinuity = (pipelineContext, details, activeSeqNum) => { + const { legibleSystemAdapter, rootPlaylistQuery } = pipelineContext; + return rootPlaylistQuery.initPTS$(activeSeqNum).pipe(switchMap((initPTS) => { + if (!initPTS || initPTS.iframeMode) { + return NEVER; + } + return legibleSystemAdapter.findFrags$(details, activeSeqNum).pipe(switchMap((findFragsResult) => { + if (!details || !(findFragsResult === null || findFragsResult === void 0 ? void 0 : findFragsResult.foundFrags)) + return VOID; + return loadSubtitleBatch(pipelineContext, initPTS.offsetTimestamp, findFragsResult, details); + })); + })); + }; + /** + * @brief subtitle loading & processing + */ + const subtitleLoadAndProcessEpic = (pipelineContext) => (bufferInfoSource$) => { + const { mediaSink, rootPlaylistQuery, legibleSystemAdapter, logger } = pipelineContext; + return bufferInfoSource$.pipe(tag('subtitleEpic.process.in'), switchMap((option) => { + if (!option || !option.url || !isEnabledMediaOption(option)) { + return of([null, null, null]); + } + const mediaLibraryQuery = createMediaLibraryQuery(option); + const details$ = mediaLibraryQuery.mediaOptionDetails$; + const activeDiscoSeqNum$ = rootPlaylistQuery.discoSeqNum$.pipe(filter((sn) => isFiniteNumber(sn))); + return combineQueries([details$, activeDiscoSeqNum$]).pipe(switchMap(([details, activeSeqNum]) => { + return loadSubtitleInDiscontinuity(pipelineContext, details, activeSeqNum); + })); + }), tag('subtitleEpic.process.emit')); + }; + function processSubtitlePayload(enabledMediaOption, mediaFragment, initPTS, data, legibleSystemAdapter) { + if (enabledMediaOption) { + return legibleSystemAdapter.processSubtitleFrag(enabledMediaOption, mediaFragment, initPTS, data); + } + return; + } + + const getMimeInfo = (initParsedData, container) => { + var _a; + let mimeType = ''; + let codec = ''; + let type; + if (initParsedData.videoCodec && initParsedData.audioCodec) { + codec = `${initParsedData.videoCodec}, ${initParsedData.audioCodec}`; + container = container !== null && container !== void 0 ? container : 'video/mp4'; + type = 'audiovideo'; + } + else if (initParsedData.videoCodec) { + codec = `${initParsedData.videoCodec}`; + container = container !== null && container !== void 0 ? container : 'video/mp4'; + type = 'video'; + } + else { + codec = `${(_a = initParsedData.audioCodec) !== null && _a !== void 0 ? _a : ''}`; + container = container !== null && container !== void 0 ? container : 'audio/mp4'; + type = 'audio'; + } + mimeType = `${container};codecs=${codec}`; + return { mimeType, codec, container, type }; + }; + + class MediaParser { + constructor(config, logger, demuxClient) { + this.config = config; + this.logger = logger; + this.demuxClient = demuxClient; + this.typeSupported = { + mp4: MediaSource.isTypeSupported('video/mp4'), + mpeg: MediaSource.isTypeSupported('audio/mpeg'), + mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'), + ac3: MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"'), + ec3: MediaSource.isTypeSupported('audio/mp4; codecs="ec-3"'), + }; + this.demuxers = []; + this.lastInitFrags = []; + this.lastFrags = []; + } + parseInitSegment(context, vendor) { + return this.getDemuxerInfo(context, this.lastInitFrags, vendor, this.demuxClient).pipe(switchMap(({ demuxer, contiguous, trackSwitch, discontinuity, accurateTimeOffset }) => { + const { frag } = context; + const { keyTagInfo, start: timeOffset, mediaOptionType } = frag; + this.lastInitFrags[mediaOptionType] = frag; + if (context.initSegment) { + const remuxedInitSegment = MP4EncryptionRemuxer.remuxInitSegment(new Uint8Array(context.initSegment), this.logger, keyTagInfo); + const initParsedData = MP4Demuxer.parseInitSegment(remuxedInitSegment); + const { mimeType, type, codec, container } = getMimeInfo(initParsedData); + return of({ moovData: initParsedData, mimeType, track: { type, codec, initSegment: remuxedInitSegment, container } }); + } + const segmentData = context.segment ? context.segment : context.initSegment; + const initSegmentData = segmentData ? context.initSegment : undefined; + const demuxerTarget = fromEventTarget(demuxer.observer); + return of(demuxerTarget.event(DemuxerEvent.FRAG_PARSING_INIT_SEGMENT).pipe(map(this.handleInitSegmentData)), demuxerTarget.event(HlsEvent$1.INTERNAL_ERROR).pipe(switchMap(this.handleError)), + // Because we're sending the same data to demuxers multiple times, we cannot transfer to worker here. + demuxer + .pushWithoutTransfer(segmentData, keyTagInfo, initSegmentData, timeOffset, discontinuity, trackSwitch, contiguous, context.totalDuration, accurateTimeOffset, undefined, // context.defaultInitPTS, + context.iframeMediaStart, context.iframeDuration) + .pipe(switchMapTo(EMPTY))).pipe(mergeAll(), take(1)); + })); + } + parseSegment(context, vendor) { + return this.getDemuxerInfo(context, this.lastFrags, vendor, this.demuxClient).pipe(switchMap(({ demuxer, contiguous, trackSwitch, discontinuity, accurateTimeOffset }) => { + const { frag, defaultInitPTS } = context; + const { keyTagInfo, start: timeOffset, duration, mediaOptionType } = frag; + this.lastFrags[mediaOptionType] = frag; + let parsedInitSegment; + const demuxerTarget = fromEventTarget(demuxer.observer); + return of(demuxerTarget.event(DemuxerEvent.FRAG_PARSING_INIT_SEGMENT).pipe(switchMap((data) => { + var _a; + if (data.track.initSegment.byteLength !== ((_a = context.initSegment) === null || _a === void 0 ? void 0 : _a.byteLength)) { + parsedInitSegment = this.handleInitSegmentData(data); + } + return EMPTY; + })), demuxerTarget.event(DemuxerEvent.FRAG_PARSING_DATA).pipe(map((parsedData) => { + const { startPTS, startDTS, firstKeyframePts, framesWithoutIDR, dropped, data1, data2, captionData, id3Samples } = parsedData; + let { endPTS, endDTS } = parsedData; + if (endPTS == null) { + this.logger.warn(`${MediaOptionNames[mediaOptionType]} ${fragPrint(frag)}: null endPTS parsed, using duration ${duration}`); + endPTS = Object.assign(Object.assign({}, startPTS), { baseTime: startPTS.baseTime + convertSecondsToTimestamp(duration, startPTS.timescale).baseTime }); + } + if (endDTS == null) { + this.logger.warn(`${MediaOptionNames[mediaOptionType]} ${fragPrint(frag)}: null endDTS parsed, using duration ${duration}`); + endDTS = Object.assign(Object.assign({}, startDTS), { baseTime: startDTS.baseTime + convertSecondsToTimestamp(duration, startDTS.timescale).baseTime }); + } + // Don't post sanity check error event for trickplay until we fix the rounding problem (due to small timescale) + if (!isFiniteNumber(context.iframeMediaStart)) { + // Just do duration check for now (accurateTimeOffset == false) + checkIfValidFragParsingData(frag, parsedData, defaultInitPTS, false, context.iframeMediaStart, this.config.audioPrimingDelay); + } + return { startPTS, endPTS, startDTS, endDTS, firstKeyframePts, framesWithoutIDR, dropped, data1, data2, captionData, id3Samples, parsedInitSegment }; + })), demuxerTarget.event(HlsEvent$1.INTERNAL_ERROR).pipe(switchMap(this.handleError)), demuxer + .push(context.segment, keyTagInfo, context.initSegment, timeOffset, discontinuity, trackSwitch, contiguous, context.totalDuration, accurateTimeOffset, defaultInitPTS, context.iframeMediaStart, context.iframeDuration) + .pipe(switchMapTo(EMPTY))).pipe(mergeAll(), take(1)); + })); + } + reset(mediaOptionType = undefined) { + if (mediaOptionType == undefined) { + this.demuxers.forEach((demuxer) => { + if (demuxer) + demuxer.destroy(); + }); + this.demuxers = []; + return; + } + const demuxer = this.demuxers[mediaOptionType]; + demuxer === null || demuxer === void 0 ? void 0 : demuxer.destroy(); + this.demuxers[mediaOptionType] = null; + } + destroy(mediaOptionType = undefined) { + if (mediaOptionType == undefined) { + this.reset(); + return; + } + this.reset(mediaOptionType); + } + willBeTrackSwitch(mediaFragment, frags) { + const { mediaOptionType, mediaOptionId } = mediaFragment; + const lastFrag = frags ? frags[mediaOptionType] : this.lastFrags[mediaOptionType]; + return !(lastFrag && lastFrag.mediaOptionId === mediaOptionId); + } + getDemuxerInfo(context, frags, vendor, demuxClient) { + const { frag, ptsKnown, seeking, live } = context; + const { discoSeqNum, mediaSeqNum, mediaOptionType } = frag; + return defer(() => { + const demuxer = this.demuxers[mediaOptionType]; + if (demuxer) { + return of(demuxer); + } + return demuxClient.init(this.typeSupported, this.config, vendor).pipe(tap((demuxer) => (this.demuxers[mediaOptionType] = demuxer))); + }).pipe(map((demuxer) => { + const lastFrag = frags[mediaOptionType]; + const trackSwitch = this.willBeTrackSwitch(frag, frags); + const discontinuity = !(lastFrag && discoSeqNum === lastFrag.discoSeqNum); + const contiguous = (lastFrag || false) && !trackSwitch && lastFrag.mediaSeqNum + 1 === mediaSeqNum; + const accurateTimeOffset = !seeking && (ptsKnown || !live); + return { demuxer, trackSwitch, discontinuity, contiguous, accurateTimeOffset }; + })); + } + handleInitSegmentData(data) { + const { track } = data; + const { initSegment } = track; + const moovData = MP4Demuxer.parseInitSegment(initSegment); + const { mimeType, type, codec, container } = getMimeInfo(moovData, track.container); + return { moovData, mimeType, track: Object.assign(Object.assign({}, track), { type, codec, initSegment, container }) }; + } + handleError(err) { + return throwError(err); + } + } + // Basic sanity check. Returns true if this is valid data, false if PTS/DTS values seem wrong + function checkIfValidFragParsingData(frag, data, initPTS, accurateTimeOffset, iframeMediaStart, audioPrimingDelay) { + let fudge = NaN; // max diff in seconds between parsed startDTS & expectedStartDTS + let expectedStartDTS = NaN; // expected value of parsed startDTS in seconds + if (isFiniteNumber(iframeMediaStart)) { + expectedStartDTS = iframeMediaStart; + // Most we should be off is one tick of the timescale due to rounding. iframe tracks can have small timescales, so allow up to 10 ms + fudge = 0.01; + if (isFinite(expectedStartDTS) && isFinite(audioPrimingDelay)) { + expectedStartDTS += audioPrimingDelay; + } + } + else if (accurateTimeOffset && initPTS) { + const timestampOffset = convertTimestampToSeconds(initPTS); + expectedStartDTS = frag.start + timestampOffset; + fudge = frag.duration; + } + const { startPTS, startDTS, endPTS, endDTS } = data; + // Technically we could have negative numbers if value > Number.MAX_SAFE_INTEGER + // but right now we would barf in LevelHelper adjustment + const success = startPTS.baseTime >= 0 && + startDTS.baseTime >= 0 && + frag.duration > 0 && + (endPTS == null || diffSeconds(endPTS, startPTS) > 0) && + (endDTS == null || diffSeconds(endDTS, startDTS) > 0) && + (!isFiniteNumber(fudge) || !isFiniteNumber(expectedStartDTS) || Math.abs(convertTimestampToSeconds(startDTS) - expectedStartDTS) <= fudge); + if (!success) { + throw new FragParsingError(false, `Failed demuxer sanity check frag=${fragPrint(frag)} parsed=${JSON.stringify({ startPTS, endPTS, startDTS, endDTS })} ${stringifyWithPrecision({ expectedStartDTS, fudge })}`, ErrorResponses.FailedDemuxerSanityCheck); + } + return success; + } + function createMediaParser(config, logger, demuxClient) { + return new Observable((subscriber) => { + const parser = new MediaParser(config, logger, demuxClient); + subscriber.next(parser); + return () => { + parser.destroy(); + }; + }); + } + + function getMinBufferAhead(config, details, bufferInfo) { + var _a, _b; + // Allow if we have a whole segment ahead + let playingFragDuration = Infinity; + if (bufferInfo.playingFrag) { + playingFragDuration = (_b = (_a = details.fragments[bufferInfo.playingFrag.mediaSeqNum - details.startSN]) === null || _a === void 0 ? void 0 : _a.duration) !== null && _b !== void 0 ? _b : Infinity; + } + const { minRequiredStartDuration, maxRequiredStartDuration, startTargetDurationFactor } = config; + const { targetduration, averagetargetduration } = details; + const requiredStartDuration = startTargetDurationFactor * Math.min(playingFragDuration, averagetargetduration, targetduration, maxRequiredStartDuration); + return Math.max(minRequiredStartDuration, requiredStartDuration); + } + /** + * @returns whether we probably have enough + */ + function probablyHaveEnough(config, fragInfo, playlistInfo, bufferInfo, desiredRate, bwEstimate, fragEstimate, bufferEstimate, logger) { + const { combined } = bufferInfo; + const minBufferAhead = getMinBufferAhead(config, playlistInfo.details, bufferInfo); + let haveEnough = haveEnoughBuffer(fragInfo, playlistInfo, bufferInfo, desiredRate, minBufferAhead); + if (!haveEnough && combined.len > 0 && (fragInfo === null || fragInfo === void 0 ? void 0 : fragInfo.state)) { + const estTimeToHaveEnough = getEstTimeToHaveEnough(fragInfo, playlistInfo.variant.bitrate, minBufferAhead, bufferInfo, bwEstimate, fragEstimate, bufferEstimate, config.maxBufferHole); + haveEnough = estTimeToHaveEnough <= combined.len; + logger === null || logger === void 0 ? void 0 : logger.info(`haveEnough (probably): ${haveEnough}, ${stringifyWithPrecision({ + bufferLen: combined.len, + pos: bufferInfo.pos, + estTimeToHaveEnough, + minBufferAhead, + state: fragInfo.state, + tstart: fragInfo.tstart, + avgParseMs: fragEstimate.avgParseTimeMs, + avgAppendMs: bufferEstimate.avgDataFragAppendMs, + })}`); + } + else if (!haveEnough && fragInfo == null) { + logger === null || logger === void 0 ? void 0 : logger.info(`haveEnough: ${haveEnough}, bufferLen: ${combined.len.toFixed(3)}`); + } + return haveEnough; + } + /** + * @returns whether we have enough buffer without using any estimation + */ + function haveEnoughBuffer(fragInfo, playlistInfo, bufferInfo, desiredRate, requiredStartDuration) { + const { pos, combined, playingFrag } = bufferInfo; + if (combined.len === 0) { + return false; + } + const iframeMode = desiredRate != 0 && desiredRate != 1; + const details = playlistInfo.details; + const fragments = details.fragments; + let haveEnough = iframeMode || combined.len >= requiredStartDuration; + const lastFrag = fragments[details.fragments.length - 1]; + const playlistStart = fragments[0].start; + const playlistEnd = playlistStart + details.totalduration; + let gotDiscontinuity = false; + if (playingFrag) { + const lastFragForCC = BinarySearch$1.search(fragments, (frag) => playingFrag.discoSeqNum - frag.discoSeqNum); + gotDiscontinuity = + (fragInfo && playingFrag.discoSeqNum !== fragInfo.discoSeqNum) || + lastFragForCC == null || // buffered cc does not appear in the refreshed playlist anymore + fragEqual(lastFragForCC, playingFrag); // buffered cc appears to end in refreshed playlist + } + // Also ensure that live is behind furthest live position + if (haveEnough && details.liveOrEvent) { + const behindLivePosition = playlistEnd - lastFrag.duration; + haveEnough = pos <= behindLivePosition; + } + else if (!details.liveOrEvent) { + // Allow play through if we're close to the end + haveEnough = haveEnough || pos >= playlistEnd - requiredStartDuration; + } + haveEnough = haveEnough || gotDiscontinuity; + return haveEnough; + } + /** + * @returns the duration relative to now when we think that the fragment will be + * buffered / complete + */ + function getEstTimeToHaveEnough(fragInfo, bitrate, minBufferAhead, bufferInfo, bwEstimate, fragEstimate, bufferEstimate, maxBufferHole) { + var _a, _b; + const mainBufferInfo = (_a = bufferInfo.sbTuple[MediaOptionType.Variant]) === null || _a === void 0 ? void 0 : _a.buffered; + const audioBufferInfo = (_b = bufferInfo.sbTuple[MediaOptionType.AltAudio]) === null || _b === void 0 ? void 0 : _b.buffered; + if ((mainBufferInfo === null || mainBufferInfo === void 0 ? void 0 : mainBufferInfo.len) >= minBufferAhead && (!audioBufferInfo || audioBufferInfo.len >= minBufferAhead)) { + // Already have enough buffered in main/audio sb. Its + // already assumed that if main suffices, audio will as well - but + // still checking for sanity + return 0; + } + else if (!mainBufferInfo || !fragInfo || !fragOverlapsEndAndSufficient(fragInfo, mainBufferInfo, maxBufferHole, minBufferAhead, bufferInfo.pos)) { + // Not enough fragment info + return Infinity; + } + const state = fragInfo.state; + let tStartOfState = fragInfo.tstart; + let timeToComplete = 0; + switch (state) { + case 'loading': + timeToComplete += getTimeToLoad(fragInfo, bitrate, bwEstimate); + tStartOfState = fragInfo.tstart + timeToComplete * 1000; + // fallthrough + case 'loaded': + case 'parsing': + timeToComplete += getTimeToParse(tStartOfState, fragEstimate); + tStartOfState = fragInfo.tstart + timeToComplete * 1000; + // fallthrough + case 'parsed': + case 'appending': + timeToComplete += getTimeToAppendBuffer(tStartOfState, bufferEstimate); + break; + case 'appended': + // 0 triggers a false positive for haveEnough; If appended, it + // should be factored in combined.len. + timeToComplete = Infinity; + break; + default: + timeToComplete = Infinity; + break; + } + return timeToComplete; + } + /** + * @returns the estimated remaining amount of time to load the fragment + */ + function getTimeToLoad(fragInfo, bitrate, bwEstimate) { + const { bwSample: fragStats, duration } = fragInfo; + if (!fragStats) { + return Infinity; + } + // Calculate based on current bw when we think the next frag will be finished + // Assume audio will take less time + const totalBits = isFiniteNumber(fragStats.total) ? fragStats.total * 8 : Math.ceil(duration * bitrate); // est bytes using bitrate & duration + const loadedBits = fragStats.loaded * 8; + const remainingBits = totalBits - loadedBits; + const instantBW = (loadedBits / (performance.now() - fragStats.tfirst)) * 1000; // bps + if (!isFiniteNumber(instantBW)) { + return Infinity; + } + const avgBW = bwEstimate.avgBandwidth; // bps + const bandwidth = Math.min(avgBW, instantBW); + return remainingBits / bandwidth; + } + /** + * @returns the estimated remaining amount of time to parse the fragment + */ + function getTimeToParse(tParseStart, fragEstimate) { + const avgParseTimeMs = isFiniteNumber(fragEstimate.avgParseTimeMs) ? fragEstimate.avgParseTimeMs : 0; + if (performance.now() < tParseStart) { + return avgParseTimeMs / 1000; + } + return Math.max(0, avgParseTimeMs - (performance.now() - tParseStart)) / 1000; + } + /** + * @returns the estimated remaining amount of time to append the fragment + */ + function getTimeToAppendBuffer(tAppendStart, bufferEstimate) { + const avgDataFragAppendMs = isFiniteNumber(bufferEstimate.avgDataFragAppendMs) ? bufferEstimate.avgDataFragAppendMs : 0; + if (performance.now() < tAppendStart) { + return avgDataFragAppendMs / 1000; + } + return Math.max(0, avgDataFragAppendMs - (performance.now() - tAppendStart)) / 1000; + } + /** + *@returns true if to frag overlaps end + */ + function fragOverlapsEndAndSufficient(fragInfo, bufferInfo, maxBufferHole, minBufferAhead, currentPosition) { + const fragend = fragInfo.start + fragInfo.duration; + return fragend > bufferInfo.end && (fragInfo.start - bufferInfo.end <= maxBufferHole || fragInfo.start <= bufferInfo.end) && fragend - currentPosition >= minBufferAhead; + } + + /** + * Observables that keep track of pipeline and media state to update playback state + */ + function checkForHaveEnough(config, logger, rootQuery, mediaSink, mediaLibraryService, statsQuery) { + const mediaQuery = mediaSink.mediaQuery; + // Startup perf: avoid subscribing to stuff on startup if we don't /need/ it + // Have enough won't ever be true until something gets buffered so don't listen to any observables until then. + return waitFor(mediaQuery.combinedBuffer$, (combinedBuffer) => (combinedBuffer === null || combinedBuffer === void 0 ? void 0 : combinedBuffer.length) > 0).pipe(switchMap(() => { + const mediaBufferInfo$ = combineQueries([mediaQuery.seekTo$, mediaQuery.bufferedSegmentsByType$(SourceBufferType.Variant)]).pipe(map(([seekTo, bufferedSeg]) => { + var _a; + const pos = isFiniteNumber(seekTo === null || seekTo === void 0 ? void 0 : seekTo.pos) ? seekTo.pos : mediaQuery.currentTime; + const playingSeg = bufferedSeg.find((seg) => seg.startPTS <= pos && seg.endPTS > pos); + const sbTuple = mediaQuery.getBufferInfo(pos, config.maxBufferHole); + const combined = mediaQuery.getCombinedBufferInfo(pos, config.maxBufferHole); + return { + pos, + sbTuple, + combined, + playingFrag: (_a = playingSeg === null || playingSeg === void 0 ? void 0 : playingSeg.frag) !== null && _a !== void 0 ? _a : null, + }; + })); + const inFlightInfo$ = combineQueries([ + rootQuery.getInFlightFragByType$(MediaOptionType.Variant), + rootQuery.enabledMediaOptionByType$(MediaOptionType.Variant), + ]).pipe(switchMap(([inFlightFrag, mediaOption]) => { + const playlistQuery = mediaLibraryService.getQueryForOption(mediaOption); + return zip(of(inFlightFrag), of(mediaOption), playlistQuery.mediaOptionDetails$); + }), map(([currentFrag, variant, details]) => { + return [currentFrag, { variant, details }]; + })); + const bwEstimates$ = combineQueries([statsQuery.bandwidthEstimate$, statsQuery.fragEstimate$, statsQuery.bufferEstimate$]); + return combineQueries([mediaQuery.readyState$, inFlightInfo$, mediaBufferInfo$, mediaQuery.desiredRate$, bwEstimates$]); + }), throttleTime(100, asyncScheduler, { leading: true, trailing: true }), map(([_readyState, inFlightInfo, bufferInfo, desiredRate, bwEstimates]) => { + const [fragInfo, playlistInfo] = inFlightInfo; + const [bwEstimate, fragEstimate, bufferEstimate] = bwEstimates; + let overrideFragEstimate = fragEstimate; + let overrideBufferEsimate = bufferEstimate; + if (fragEstimate) { + const maxDurationSec = isFiniteNumber(fragEstimate.maxDurationSec) ? fragEstimate.maxDurationSec : config.defaultTargetDuration; + const avgParseTimeMs = isFiniteNumber(fragEstimate.avgParseTimeMs) ? fragEstimate.avgParseTimeMs : config.statDefaults.fragParseTimeMs; + overrideFragEstimate = { maxDurationSec, avgParseTimeMs }; + } + if (bufferEstimate) { + const avgBufferCreateMs = isFiniteNumber(bufferEstimate.avgBufferCreateMs) ? bufferEstimate.avgBufferCreateMs : config.statDefaults.fragBufferCreationDelayMs; + const avgInitFragAppendMs = isFiniteNumber(bufferEstimate.avgInitFragAppendMs) ? bufferEstimate.avgInitFragAppendMs : config.statDefaults.initFragAppendMs; + const avgDataFragAppendMs = isFiniteNumber(bufferEstimate.avgDataFragAppendMs) ? bufferEstimate.avgDataFragAppendMs : config.statDefaults.dataFragAppendMs; + overrideBufferEsimate = { avgBufferCreateMs, avgInitFragAppendMs, avgDataFragAppendMs }; + } + const haveEnough = probablyHaveEnough(config, fragInfo, playlistInfo, bufferInfo, desiredRate, bwEstimate, overrideFragEstimate, overrideBufferEsimate, logger); + mediaSink.haveEnough = haveEnough; + return haveEnough; + }), distinctUntilChanged(), tap((haveEnough) => { + logger.info(`[seek] haveEnough=${haveEnough}`); + }), switchMapTo(EMPTY)); + } + function checkForEndOfStream(context) { + const { config, logger, mediaSink, rootPlaylistQuery: rootQuery, mediaLibraryService, gaplessInstance } = context; + const { mediaQuery } = mediaSink; + const currentEnabledDetails$ = rootQuery.enabledAVOptions$.pipe(switchMap((options) => zip(...options.map((option) => { + if (!isEnabledMediaOption(option)) { + return of(null); + } + return mediaLibraryService.getQueryForOption(option).mediaOptionDetails$; + })))); + // Startup: wait for something to be buffered before subscribe to enabledAVOptions & details + return waitFor(mediaQuery.combinedBuffer$, (combined) => { + return (combined === null || combined === void 0 ? void 0 : combined.length) > 0; + }).pipe(switchMapTo(combineQueries([ + currentEnabledDetails$, + mediaQuery.msReadyState$, + mediaQuery.updating$, + mediaQuery.isIframeRate$, + mediaQuery.isBufferedToEnd$(config.maxBufferHole, !gaplessInstance.inGaplessMode), + ])), tag('checkForEndOfStream'), filter(([_, readyState, updating, isIframeRate, bufferedToEnd]) => { + return readyState === 'open' && updating === false && !isIframeRate && bufferedToEnd; + }), tap(([details]) => { + logger.info('Reached end of stream'); + const isVOD = details[0] != null && details.every((x) => x == null || (x.liveOrEvent === false && x.iframesOnly === false)); + if (isVOD && !gaplessInstance.inGaplessMode) { + logger.info('Calling MediaSink endStream'); + mediaSink.endStream(); // May only be called when updating == false + } + }), switchMapTo(EMPTY)); + } + + function loadRootMediaOptions(item, loadPolicy, logger, config, statsService, extendMaxTTFB) { + const { itemId, url, itemStartOffset } = item; + const loadConfig = getLoadConfig(item, loadPolicy); + return fromUrlText({ + url, + onProgress: { getData: true, cb: loadProgress }, + xhrSetup: config.xhrSetup, + }, loadConfig, extendMaxTTFB).pipe(map(({ responseText: rawPlaylist, responseURL: rURL, stats }) => { + const { keySystemPreference } = config; + logger.debug(`root playlist url: ${url}, baseUrl: ${rURL}`); + if (!rURL) { + logger.warn('Missing response url. Reusing request url as base url'); + rURL = url; + } + if (PlaylistParser$1.isMediaPlaylist(rawPlaylist)) { + const mediaOptionId = 'media-pl-' + guid(); + const parsed = PlaylistParser$1.parseMediaOptionPlaylist(rawPlaylist, rURL, true, keySystemPreference, {}, itemId, mediaOptionId, MediaOptionType.Variant, logger, itemStartOffset); + updatePlaylistAttributes(parsed.mediaOptionDetails); + const variantOption = { + itemId, + mediaOptionId, + mediaOptionType: MediaOptionType.Variant, + url, + bandwidth: 0, + bitrate: 0, + iframes: parsed.mediaOptionDetails.iframesOnly, + pathwayID: '.', + }; + const rootMediaOptionsTuple = [[variantOption], [], []]; + const loadResult = { + itemId, + itemStartOffset, + rootMediaOptionsTuple, + stats, + baseUrl: rURL, + initialDetails: parsed.mediaOptionDetails, + isMediaPlaylist: true, + }; + return loadResult; + } + else { + const sessionData = PlaylistParser$1.parseSessionData(rawPlaylist, rURL); + const sessionKeys = PlaylistParser$1.parseSessionKeys(rawPlaylist, rURL, keySystemPreference); + const parsed = PlaylistParser$1.parseRootPlaylist(itemId, rawPlaylist, rURL, true); + if (parsed.playlistParsingError) { + throw parsed.playlistParsingError; + } + const { variantMediaOptions, contentSteeringOption, masterVariableList } = parsed; + const parsingResult = PlaylistParser$1.parseRootPlaylistAlternateMediaOptions(itemId, rawPlaylist, rURL, parsed.variantMediaOptions, true, masterVariableList); + if (parsingResult.playlistParsingError) { + throw parsingResult.playlistParsingError; + } + const { audioAlternateOptions, subtitleAlternateOptions, audioMediaSelectionGroup, subtitleMediaSelectionGroup } = parsingResult.alternateMediaInfo; + const rootMediaOptionsTuple = [variantMediaOptions, audioAlternateOptions, subtitleAlternateOptions]; + const loadResult = { + itemId, + itemStartOffset, + rootMediaOptionsTuple, + stats, + baseUrl: rURL, + audioMediaSelectionGroup, + subtitleMediaSelectionGroup, + contentSteeringOption, + sessionData, + sessionKeys, + masterVariableList, + }; + return loadResult; + } + }), convertToManifestNetworkError(false)); + } + function loadProgress(url, status, _stats, data) { + if (status === 200 && data && data.length > 10) { + // only look at responses which have status 200 and is at least 10 chars long + if (!PlaylistParser$1.isValidPlaylist(data)) { + const playlistParsingError = new PlaylistParsingError(ErrorTypes.NETWORK_ERROR, ErrorDetails.MANIFEST_PARSING_ERROR, true, 'response doesnt have #EXTM3U tag', ErrorResponses.PlaylistErrorMissingEXTM3U); + playlistParsingError.url = url; + throw playlistParsingError; + } + return true; + } + return false; + } + + const loggerName$1 = { name: 'pltfrm' }; + var HdrMetadataType; + (function (HdrMetadataType) { + HdrMetadataType["HDR10"] = "smpteSt2086"; + HdrMetadataType["DoVi"] = "smpteSt2094-10"; + HdrMetadataType["HDR10Plus"] = "smpteSt2094-40"; + })(HdrMetadataType || (HdrMetadataType = {})); + function isValidSecurityLevel(securityLevel, keySystemId) { + const securityLevels = KeySystemFactory$1.getKeySystemSecurityLevel(keySystemId); + return securityLevel != null && securityLevels[securityLevel] !== undefined; + } + function onlyIframes(mediaOptionList) { + return mediaOptionList.every((mediaOption) => mediaOption.iframes); + } + function isWithinCap(val, cap) { + // If there is no value or if there is no cap, then the value is vacuously within the cap + return !isFiniteNumber(val) || !isFiniteNumber(cap) || val <= cap; // TODO revert or fix <- this is not my todo, inherited from 2.0 + } + function makeVideoConfiguration(codec, mediaOption) { + const vConfig = { + contentType: `video/mp4;codecs=${codec}`, + width: mediaOption.width, + height: mediaOption.height, + bitrate: mediaOption.bandwidth || mediaOption.avgBandwidth, + framerate: mediaOption.iframes ? 8 : mediaOption.frameRate, + }; + if (mediaOption.videoRange) { + switch (mediaOption.videoRange) { + case 'PQ': + if (MediaUtil.isDolby(codec)) { + vConfig.hdrMetadataType = HdrMetadataType.DoVi; + vConfig.colorGamut = 'rec2020'; + } + else if (MediaUtil.isHEVC(codec) || MediaUtil.isVP09(codec)) { + // Assuming HDR10 + vConfig.hdrMetadataType = HdrMetadataType.HDR10; + vConfig.colorGamut = 'rec2020'; + } + vConfig.transferFunction = 'pq'; + break; + case 'HLG': + // no metadata + vConfig.colorGamut = 'rec2020'; + vConfig.transferFunction = 'hlg'; + break; + } + } + return vConfig; + } + function makeAudioConfiguration(codec, mediaOption, audioMediaOptions) { + const aConfig = { + contentType: `audio/mp4;codecs=${codec}`, + }; + // Optional fields + const channels = RichestMedia.getRichestChannelLayoutForGroupId(mediaOption.audioGroupId, audioMediaOptions); + if (channels) { + aConfig.channels = MediaUtil.getChannelCount(channels).toString(); + aConfig.spatialRendering = MediaUtil.isDolbyAtmos(codec, channels); + } + return aConfig; + } + function createHandleCodec(logger) { + // can reduce the number of calls by caching the results + const cachedDecodingInfo = new Map(); + const mediaCapabilities = navigator && navigator.mediaCapabilities; + return (mediaOption, audioMediaOptions, codec, isVideo, isTypeSupportedObservables) => { + const config = { + type: 'media-source', + }; + if (isVideo) { + config.video = makeVideoConfiguration(codec, mediaOption); + } + else { + config.audio = makeAudioConfiguration(codec, mediaOption, audioMediaOptions); + } + const configStr = JSON.stringify(config); + let decodingInfoObservable; + if (cachedDecodingInfo.has(configStr)) { + decodingInfoObservable = cachedDecodingInfo.get(configStr); + } + else { + decodingInfoObservable = from(mediaCapabilities.decodingInfo(config)).pipe(map((decodingInfo) => { + const supportedConfig = decodingInfo.configuration || decodingInfo.supportedConfiguration; + const hasRequestedFields = supportedConfig instanceof Object && + (!config.video || Object.keys(config.video).find((k) => !(k in supportedConfig.video)) == undefined) && + (!config.audio || Object.keys(config.audio).find((k) => !(k in supportedConfig.audio)) == undefined); + const isTypeSupported = decodingInfo.supported && (!isVideo || decodingInfo.powerEfficient) && hasRequestedFields; + if (!isTypeSupported) { + logger.warn(loggerName$1, `Unsupported config ${decodingInfo.supported}/${decodingInfo.powerEfficient}/${hasRequestedFields} ${JSON.stringify(config)} supportedConfig=${JSON.stringify(supportedConfig)}`); + } + return isTypeSupported; + })); + cachedDecodingInfo.set(configStr, decodingInfoObservable); + } + return [...isTypeSupportedObservables, decodingInfoObservable]; + }; + } + function createIsSupportedInternal() { + const supportedCodecs = new Set(); + const unsupportedCodecs = new Set(); + return (mediaOption) => { + const checkSupported = (type, codec) => { + let isSupported = MediaSource.isTypeSupported(`${type}/mp4;codecs=${codec}`); + if (codec === 'mp4a.40.34' && !isSupported) { + isSupported = MediaSource.isTypeSupported(`${type}/mpeg`); + } + return isSupported; + }; + const updateCachedCodecsSetsIfNecessary = (codec, isAudioCodec) => { + const type = isAudioCodec ? 'audio' : 'video'; + // Minimizes calls to chromium by caching supported/unsupported audio video codecs + if (!supportedCodecs.has(codec) && !unsupportedCodecs.has(codec)) { + if (checkSupported(type, codec)) { + supportedCodecs.add(codec); + } + else { + unsupportedCodecs.add(codec); + } + } + }; + // Update the codec support/notSupport cache based on MediaSource support. + // After that verify if that codec falls into unsupported category. + const checkUnsupportedCodec = (codec, isAudioCodec) => { + updateCachedCodecsSetsIfNecessary(codec, isAudioCodec); + return unsupportedCodecs.has(codec); + }; + let foundUnsupportedCodec = false; + if (mediaOption.audioCodecList) { + foundUnsupportedCodec = mediaOption.audioCodecList.some((codec) => checkUnsupportedCodec(codec, true)); + } + if (!foundUnsupportedCodec && mediaOption.videoCodecList) { + foundUnsupportedCodec = mediaOption.videoCodecList.some((codec) => checkUnsupportedCodec(codec, false)); + } + return !foundUnsupportedCodec; + }; + } + function useFallbackCapsIfMissing(result, fallback) { + const keys = Object.keys(fallback); + keys.forEach((key) => { + if (!result[key]) { + result[key] = fallback[key]; + } + }); + return result; + } + /** + * @param cappingInfoList Usually platformInfo.videoCodecs or platformInfo.videoDynamicRangeFormats + * @param type to use for lookup + * @returns Capping info with matching type + */ + function getCappingInfoFromType(cappingInfoList, type) { + for (const id in cappingInfoList) { + if (cappingInfoList[id].type === type) { + return cappingInfoList[id]; + } + } + return {}; + } + /** + * @param format DynamicRangeFormat + * @param compressionType CompressionType + * @param videoCodecs List of capping info [ { type: DynamicRangeFormat } ] + * @param videoDynamicRangeFormats List of capping info [ {type, CompressionType } ] + * @returns The capping information to use + */ + function getCappingInfo(vdrType, compressionType, videoCodecs, videoDynamicRangeFormats) { + if (!videoDynamicRangeFormats && !videoCodecs) { + return {}; + } + const cappingFromCompressionInfo = videoCodecs ? getCappingInfoFromType(videoCodecs, compressionType) : {}; + const cappingFromFormat = videoDynamicRangeFormats ? getCappingInfoFromType(videoDynamicRangeFormats, vdrType) : {}; + let preferredCaps; + let fallbackCaps; + switch (vdrType) { + case VideoDynamicRangeType.SDR: + // Prioritize using compressionType > dynamic range + preferredCaps = cappingFromCompressionInfo; + fallbackCaps = cappingFromFormat; + break; + default: + // Prioritize using dynamic range > compressionType + preferredCaps = cappingFromFormat; + fallbackCaps = cappingFromCompressionInfo; + } + const result = Object.assign({}, preferredCaps); + return useFallbackCapsIfMissing(result, fallbackCaps); + } + function getSupportedVideoFormats(videoDynamicRangeFormats) { + return videoDynamicRangeFormats.reduce((prev, cur) => { + switch (cur.type) { + case VideoDynamicRangeType.DolbyVision: + prev.doViSupported = true; + break; + case VideoDynamicRangeType.HDR10: + prev.hdr10Supported = true; + break; + case VideoDynamicRangeType.HLG: + prev.hlgSupported = true; + break; + } + return prev; + }, { doViSupported: false, hdr10Supported: false, hlgSupported: false }); + } + function filterMediaOptionInfoPrint(filterName, unfilteredMediaOptionInfo, filteredMediaOptionInfo, logger) { + const unfilteredMediaOptionCount = unfilteredMediaOptionInfo.length; + const filteredMediaOptionCount = filteredMediaOptionInfo.length; + const unchanged = unfilteredMediaOptionCount === filteredMediaOptionCount; + const removedMediaOptionIds = unfilteredMediaOptionInfo.filter((mediaOption) => !filteredMediaOptionInfo.includes(mediaOption)).map((mediaOption) => mediaOption.mediaOptionId); + logger.qe({ critical: true, name: 'mediaFiltering', data: { filterName, unchanged, remaining: filteredMediaOptionCount, filteredIds: removedMediaOptionIds } }); + } + function printMediaOptionInfo(mediaOptionInfo, logger) { + logger.info(loggerName$1, `Unsupported level ${JSON.stringify(mediaOptionInfo, ['avgBandwidth', 'audioCodecList', 'videoCodecList', 'videoRange', 'frameRate', 'width', 'height', 'iframes'])}`); + } + function filterMediaOptionsBasedOnHlsConfig(mediaOptions, disableVideoCodecList, disableAudioCodecList, logger) { + let filteredMediaOptions = mediaOptions.filter((mediaOption) => { + if (mediaOption.videoCodec) { + return mediaOption.videoCodecList.every((codec) => { + const videoCodecRank = getVideoCodecRanking(codec); + return !disableVideoCodecList.has(videoCodecRank); + }); + } + return true; + }); + filteredMediaOptions = filteredMediaOptions.filter((mediaOption) => { + if (!mediaOption.iframes && mediaOption.audioCodec) { + return mediaOption.audioCodecList.every((codec) => { + const audioCodecRank = getAudioCodecRanking(codec); + return !disableAudioCodecList.has(audioCodecRank); + }); + } + return true; + }); + filterMediaOptionInfoPrint('Disable Codec config', mediaOptions, filteredMediaOptions, logger); + return filteredMediaOptions; + } + function filterMediaOptionsBasedOnMaxAllowedHdcpLevel(mediaOptions, maxHdcpLevel, logger) { + const maxHdcpRanking = hdcpLevelToInt(maxHdcpLevel); + const filteredMediaOptions = mediaOptions.filter((mediaOption) => { + const curHdcpLevel = mediaOption.hdcpLevel; + return !curHdcpLevel || hdcpLevelToInt(curHdcpLevel) <= maxHdcpRanking; + }); + filterMediaOptionInfoPrint('Hdcp', mediaOptions, filteredMediaOptions, logger); + return filteredMediaOptions; + } + function filterMediaOptionsBasedOnSecurityLevel(mediaOptions, maxSecurityLevel, keySystemId, logger) { + const securityLevels = KeySystemFactory$1.getKeySystemSecurityLevel(keySystemId); + const keyFormat = KeySystemFactory$1.getKeySystemFormat(keySystemId); + const getSecurityLevelRanking = function (curSecurityLevel) { + return isValidSecurityLevel(curSecurityLevel, keySystemId) ? securityLevels[curSecurityLevel] : -1; + }; + const maxSecurityLevelRanking = getSecurityLevelRanking(maxSecurityLevel); + const filteredMediaOptions = mediaOptions.filter((mediaOption) => { + var _a, _b; + const cpcLabels = (_b = (_a = mediaOption.allowedCPCMap) === null || _a === void 0 ? void 0 : _a[keyFormat]) !== null && _b !== void 0 ? _b : []; + let selectLevel = true; + for (const currentLabelSecurityLevel of cpcLabels) { + const currentLabelSecurityLevelRanking = getSecurityLevelRanking(currentLabelSecurityLevel); + selectLevel = currentLabelSecurityLevelRanking <= maxSecurityLevelRanking; + if (!selectLevel) { + break; + } + } + return selectLevel; + }); + filterMediaOptionInfoPrint('Security Level', mediaOptions, filteredMediaOptions, logger); + return [...filteredMediaOptions]; + } + function filterMediaOptionsBasedOnMediaKeySystemAccess(mediaOptions, keySystemId, logger) { + const cachedKeySystemConfig = new Map(); + function handleKeySystem(keySystemId, levelInfo, isKeySystemSupportedPromises) { + const mediaCapabilities = keysystemutil.getCapabilities(levelInfo.videoCodecList, levelInfo.audioCodecList); + const configStr = JSON.stringify(mediaCapabilities); + let requestKeySystemPromise; + if (cachedKeySystemConfig.has(configStr)) { + requestKeySystemPromise = cachedKeySystemConfig.get(configStr); + } + else { + requestKeySystemPromise = KeySystemFactory$1.requestKeySystemAccess(keySystemId, mediaCapabilities, undefined, logger).pipe(map(() => { + return true; + }), catchError((error) => { + logger.warn(`Request key system error: ${error.message}`); + return of(false); + }), shareReplay({ bufferSize: 1, refCount: true })); + cachedKeySystemConfig.set(configStr, requestKeySystemPromise); + } + isKeySystemSupportedPromises.push(requestKeySystemPromise); + } + const isSupported = new Array(); + mediaOptions.forEach((mediaOption) => { + const isKeySystemSupportedPromises = Array(); + handleKeySystem(keySystemId, mediaOption, isKeySystemSupportedPromises); + const p = forkJoin(isKeySystemSupportedPromises).pipe(map((results) => { + // If no unsupported config found then we're good + if (results.find((supported) => supported === false) === undefined) { + return mediaOption; + } + })); + isSupported.push(p); + }); + return forkJoin(isSupported).pipe(map((results) => { + return results.filter((r) => Boolean(r)); + })); + } + /** + * Use MediaCapabilities API to determine whether we can play this. + * @param mediaOptions + * @param audioMediaOptions + */ + function filterBasedOnMediaCapabilities(mediaOptions, audioMediaOptions, logger) { + logger.debug(loggerName$1, 'Using MediaCapabilities'); + const observables = []; + const isSupportedInternal = createIsSupportedInternal(); + const handleCodec = createHandleCodec(logger); + mediaOptions.forEach((mediaOption) => { + var _a, _b; + let isTypeSupportedObservables = []; + (_a = mediaOption.videoCodecList) === null || _a === void 0 ? void 0 : _a.forEach((codec) => { + isTypeSupportedObservables = handleCodec(mediaOption, audioMediaOptions, codec, true, isTypeSupportedObservables); + }); + if (((_b = mediaOption.audioCodecList) === null || _b === void 0 ? void 0 : _b.length) > 0) { + const codec = RichestMedia.getRichestAudioCodec(mediaOption.audioCodecList); + isTypeSupportedObservables = handleCodec(mediaOption, audioMediaOptions, codec, false, isTypeSupportedObservables); + } + let o = of(mediaOption); + if (isTypeSupportedObservables.length > 0) { + o = forkJoin(isTypeSupportedObservables).pipe(map((results) => { + const supported = results.find((supported) => supported === false) == undefined; + if (!supported) { + printMediaOptionInfo(mediaOption, logger); + } + return supported ? mediaOption : null; + }), catchError((error) => { + logger.warn(loggerName$1, `decodingInfo errror: ${error.message}`); // Missing field probably + const supported = isSupportedInternal(mediaOption); + if (!supported) { + printMediaOptionInfo(mediaOption, logger); + } + return of(supported ? mediaOption : null); + })); + } + observables.push(o); + }); + return forkJoin(observables).pipe(map((list) => list.filter((item) => Boolean(item)))); + } + function filterMediaOptionsBasedOnAudioAndVideoCodecs(mediaOptions, logger) { + const isSupportedInternal = createIsSupportedInternal(); + const filteredMediaOptions = mediaOptions.filter(isSupportedInternal); + filterMediaOptionInfoPrint('Platform Supported Media Codec', mediaOptions, filteredMediaOptions, logger); + return filteredMediaOptions; + } + function filterMediaOptionsBasedOnExtendedAudioParameters(mediaOptions, audioMediaOptions, logger) { + const supportedMimeCodecs = new Set(); + const unsupportedMimeCodecs = new Set(); + /* Intentional negative tests to identify platform support for 'channels' and 'features' attributes via isTypeSupported(). + - If the platform supports 'channels' & 'features' attributes, then the below calls will return false. + - Otherwise, if we get true, that means the platform ignores the 'channels' and/or 'features' attribute and we cannot rely on this API for testing them. + */ + const shouldCheckChannels = !MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.2"; channels="-1"'); + const shouldCheckFeatures = shouldCheckChannels && !MediaSource.isTypeSupported('audio/mp4; codecs="mp4a.40.2"; channels="2"; features="INVALID"'); + logger.info(loggerName$1, `Platform support for channels: ${shouldCheckChannels}; features: ${shouldCheckFeatures}`); + const checkSupported = (codec, channels) => { + const parameters = channels.split('/'); + const channelCount = parseInt(parameters[0]); + let mimeCodec; + let mimeCodecAlternate; + if (parameters.length > 1) { + const features = parameters[1].split(',')[0]; // 'features' is the first in the comma separated list. + mimeCodec = `audio/mp4;codecs="${codec}";channels="${channelCount}";features="${features}"`; + mimeCodecAlternate = `audio/mp4;codecs="${codec}";channels="8";features="${features}"`; + } + else { + mimeCodec = `audio/mp4;codecs="${codec}";channels="${channelCount}"`; + } + let isSupported = MediaSource.isTypeSupported(mimeCodec); + // LG doesn't return isTypeSupported correctly for 16ch ATMOS. This workaround uses 8ch test for ATMOS support. + // This code should be removed when LG firmware fix is in place. + if (!isSupported && mimeCodecAlternate) { + isSupported = MediaSource.isTypeSupported(mimeCodecAlternate); + logger.info(loggerName$1, `Using mimeCodecAlternate, isSupported: ${isSupported}; mimeCodecAlternate: ${mimeCodecAlternate}`); + } + return isSupported; + }; + const updateCachedMimeCodecSetsIfNecessary = (codec, channels) => { + const mimeCodec = `${codec}/${channels}`; + if (!supportedMimeCodecs.has(mimeCodec) && !unsupportedMimeCodecs.has(mimeCodec)) { + if (checkSupported(codec, channels)) { + supportedMimeCodecs.add(mimeCodec); + } + else { + unsupportedMimeCodecs.add(mimeCodec); + } + } + }; + // Update the mimeCodec support/notSupport cache based on MediaSource support. + // After that verify if that mimeCodec falls into unsupported category. + const checkUnsupportedMimeCodec = (codec, channels, logger) => { + const isAtmos = MediaUtil.isDolbyAtmos(codec, channels); + if (shouldCheckFeatures || (shouldCheckChannels && !isAtmos)) { + // check for platform support + updateCachedMimeCodecSetsIfNecessary(codec, channels); + const mimeCodec = `${codec}/${channels}`; + return unsupportedMimeCodecs.has(mimeCodec); + } + else { + // Don't allow ATMOS if we cannot reliably check support via MediaSource.isTypeSupported() + // All other cases are already handled via codec support + return isAtmos ? true : false; + } + }; + const filteredMediaOptions = mediaOptions.filter((mediaOption) => { + let foundUnsupportedMimeCodec = false; + // Extended parameters are valid only if there is an audio group + if (mediaOption.audioCodecList && mediaOption.audioGroupId) { + const channels = RichestMedia.getRichestChannelLayoutForGroupId(mediaOption.audioGroupId, audioMediaOptions); + if (mediaOption.audioCodecList.length > 0 && channels) { + // pick the highest ranking audio codec to match with the channels tag + const richestCodec = RichestMedia.getRichestAudioCodec(mediaOption.audioCodecList); + // check only if channels attribute is present, otherwise, the case is already handled through audio codecs + foundUnsupportedMimeCodec = checkUnsupportedMimeCodec(richestCodec, channels); + } + } + return !foundUnsupportedMimeCodec; + }); + filterMediaOptionInfoPrint('Richest Audio Codec Selection', mediaOptions, filteredMediaOptions, logger); + return filteredMediaOptions; + } + function getCapsForMediaOption(mediaOption, platformInfo) { + if (!platformInfo) { + return { + highestPlayableAverageBitRate: undefined, + highestPlayablePeakBitRate: undefined, + highestPlayableWidth: undefined, + highestPlayableHeight: undefined, + highestPlayableFrameRate: undefined, + }; + } + const firstMediaOptionCodec = mediaOption.videoCodec; + const firstMediaOptionVideoRange = mediaOption.videoRange; + const videoDynamicRangeFormats = platformInfo.videoDynamicRangeFormats; + const videoCodecs = platformInfo.videoCodecs; + const vdrType = MediaUtil.getDynamicRangeType(firstMediaOptionVideoRange, firstMediaOptionCodec); + const compressionType = MediaUtil.getCompressionType(firstMediaOptionCodec); + const caps = getCappingInfo(vdrType, compressionType, videoCodecs, videoDynamicRangeFormats); + // Ignore highestPlayablePeakBitRateForClearContent for all codecs and formats except VP9 + if (compressionType !== CompressionType.VP09) { + caps.highestPlayablePeakBitRateForClearContent = undefined; + } + return caps; + } + function filterMediaOptionsByCaps(mediaOptions, sessionKeys, platformInfo, logger) { + const hasSessionKeys = (sessionKeys === null || sessionKeys === void 0 ? void 0 : sessionKeys.length) > 0; + const filteredMediaOptions = mediaOptions.filter((mediaOption) => { + const caps = getCapsForMediaOption(mediaOption, platformInfo); + const { highestPlayablePeakBitRateForClearContent } = caps; + // Use highestPlayablePeakBitRate|highestPlayablePeakBitRateForClearContent based on the presence of sessionKeys or allowedCPCMap which we assume means contents are encrypted + const assumeEncryptedContent = mediaOption.allowedCPCMap || hasSessionKeys; + const isWithinSafePeekBitRate = isWithinCap(mediaOption.bandwidth, caps.highestPlayablePeakBitRate); + const isWithinPeekBitRate = assumeEncryptedContent || !highestPlayablePeakBitRateForClearContent + ? isWithinSafePeekBitRate + : isWithinSafePeekBitRate || isWithinCap(mediaOption.bandwidth, highestPlayablePeakBitRateForClearContent); + return (isWithinPeekBitRate && + isWithinCap(mediaOption.avgBandwidth, caps.highestPlayableAverageBitRate) && + isWithinCap(mediaOption.width, caps.highestPlayableWidth) && + isWithinCap(mediaOption.height, caps.highestPlayableHeight) && + isWithinCap(mediaOption.frameRate, caps.highestPlayableFrameRate)); + }); + filterMediaOptionInfoPrint('Highest Playable Platform Caps', mediaOptions, filteredMediaOptions, logger); + return filteredMediaOptions; + } + function filterIframeMediaOptionsBasedOnCapOn1080p(mediaOptions, logger) { + // standard 1080p resolution with a slight tolerance of 1.2 + const PIXELS_CAP = 2488320; + const filteredMediaOptions = mediaOptions.filter((mediaOption) => !mediaOption.iframes || !mediaOption.width || !mediaOption.height || mediaOption.width * mediaOption.height <= PIXELS_CAP); + filterMediaOptionInfoPrint('1080p iFrame', mediaOptions, filteredMediaOptions, logger); + return filteredMediaOptions; + } + function filterMediaOptionsBasedOnSupportedVideoFormats(mediaOptions, videoDynamicRangeFormats, logger) { + const supportedFormats = getSupportedVideoFormats(videoDynamicRangeFormats); + const { doViSupported, hdr10Supported, hlgSupported } = supportedFormats; + logger.info(loggerName$1, `Supported videoDynamicRangeFormats DoVi/HDR10/HLG: ${doViSupported}/${hdr10Supported}/${hlgSupported}`); + // filter based on supported videoDynamicRangeFormats + // only keep level with correct codec for video-range + const validMediaOptions = mediaOptions.reduce((prev, option) => { + var _a; + const vdr = MediaUtil.getDynamicRangeType(option.videoRange, (_a = option.videoCodec) !== null && _a !== void 0 ? _a : ''); + switch (vdr) { + case VideoDynamicRangeType.HDR: + case VideoDynamicRangeType.HDR10: + if (hdr10Supported) { + prev.hdrMediaOptions.push(option); + } + break; + case VideoDynamicRangeType.DolbyVision: + if (doViSupported) { + prev.hdrMediaOptions.push(option); + } + break; + case VideoDynamicRangeType.HLG: + if (hlgSupported) { + prev.hdrMediaOptions.push(option); + } + break; + default: + // default is SDR + if (option.videoRange === 'SDR' || option.videoRange == null) { + prev.sdrMediaOptions.push(option); + } + } + return prev; + }, { hdrMediaOptions: new Array(), sdrMediaOptions: new Array() }); + logger.debug(loggerName$1, `HDR/SDR mediaOptions: ${validMediaOptions.hdrMediaOptions.length}/${validMediaOptions.sdrMediaOptions.length}`); + return validMediaOptions; + } + /** + * Filter root playlist. Remove all media options that should not ever be used on this platform based on + * capabilities (platformInfo and MediaCapabilities) + * + * @param mediaOptions Original mediaOption list + * @param audioMediaOptions Alternate mediaOption info + * @param platformInfo Platform information for filtering out media options + * @param config HLS Config option + * @returns { hdrMediaOptions: valid hdr media options, sdrMediaOptions: valid sdr media options } + */ + function filterByPlatformCapabilities(mediaOptions, audioMediaOptions, sessionKeys, platformInfo, config, logger) { + const maxHdcpLevel = (platformInfo === null || platformInfo === void 0 ? void 0 : platformInfo.maxHdcpLevel) || undefined; + let filteredMediaOptions = [...mediaOptions]; + if (config.disableVideoCodecList.size > 0 || config.disableAudioCodecList.size > 0) { + filteredMediaOptions = filterMediaOptionsBasedOnHlsConfig(filteredMediaOptions, config.disableVideoCodecList, config.disableAudioCodecList, logger); + } + if (maxHdcpLevel && isHdcpLevel(maxHdcpLevel)) { + filteredMediaOptions = filterMediaOptionsBasedOnMaxAllowedHdcpLevel(filteredMediaOptions, maxHdcpLevel, logger); + } + // Filter based on ALLOWED-CPC + const maxSecurityLevel = platformInfo === null || platformInfo === void 0 ? void 0 : platformInfo.maxSecurityLevel; + const keySystemId = config === null || config === void 0 ? void 0 : config.keySystemPreference; + if (maxSecurityLevel && keySystemId && isValidSecurityLevel(maxSecurityLevel, keySystemId)) { + filteredMediaOptions = filterMediaOptionsBasedOnSecurityLevel(filteredMediaOptions, maxSecurityLevel, keySystemId, logger); + } + // parse and set audioChannelCount property + filteredMediaOptions = filteredMediaOptions.map((mediaOption) => { + if (mediaOption.audioCodecList && mediaOption.audioGroupId) { + const matchingTrack = audioMediaOptions.find((audio) => audio.groupId === mediaOption.audioGroupId); + const channels = matchingTrack === null || matchingTrack === void 0 ? void 0 : matchingTrack.channels; + if (channels) { + mediaOption.audioChannelCount = parseInt(channels); + } + } + return mediaOption; + }); + const useMediaKeySystemAccessFilter = (config === null || config === void 0 ? void 0 : config.useMediaKeySystemAccessFilter) || false; + const keySystemAccessEnabled = useMediaKeySystemAccessFilter && keySystemId && navigator && typeof navigator.requestMediaKeySystemAccess === 'function'; + const source = keySystemAccessEnabled ? filterMediaOptionsBasedOnMediaKeySystemAccess(filteredMediaOptions, keySystemId, logger) : of(filteredMediaOptions); + return source.pipe(switchMap((keySystemMediaOptions) => { + if (keySystemMediaOptions.length === 0 || onlyIframes(keySystemMediaOptions)) { + throw new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR, undefined, 'no media option with compatible codecs found in playlist', undefined); + } + if (keySystemAccessEnabled) { + filterMediaOptionInfoPrint('Key System Support', filteredMediaOptions, keySystemMediaOptions, logger); + } + const mediaCapabilities = navigator && navigator.mediaCapabilities; + const useMediaCapabilities = (config === null || config === void 0 ? void 0 : config.useMediaCapabilities) || false; + const mediaCapabilitiesEnabled = useMediaCapabilities && mediaCapabilities && typeof mediaCapabilities.decodingInfo === 'function'; + let source; + if (mediaCapabilitiesEnabled) { + source = filterBasedOnMediaCapabilities(keySystemMediaOptions, audioMediaOptions, logger); + } + else { + keySystemMediaOptions = filterMediaOptionsBasedOnAudioAndVideoCodecs(keySystemMediaOptions, logger); + keySystemMediaOptions = filterMediaOptionsBasedOnExtendedAudioParameters(keySystemMediaOptions, audioMediaOptions, logger); + source = of(keySystemMediaOptions); + } + return source.pipe(map((compatibleMediaOptions) => { + if (compatibleMediaOptions.length === 0 || onlyIframes(compatibleMediaOptions)) { + throw new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR, undefined, 'no media option with compatible codecs found in manifest', undefined); + } + compatibleMediaOptions = filterMediaOptionsByCaps(compatibleMediaOptions, sessionKeys, platformInfo, logger); + compatibleMediaOptions = filterIframeMediaOptionsBasedOnCapOn1080p(compatibleMediaOptions, logger); + if (compatibleMediaOptions.length === 0 || onlyIframes(compatibleMediaOptions)) { + throw new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR, undefined, 'no media option with compatible codecs found in manifest', undefined); + } + // Seperate out fallback mediaOptions if needed + let videoDynamicRangeFormats = (platformInfo === null || platformInfo === void 0 ? void 0 : platformInfo.videoDynamicRangeFormats) || []; + if (mediaCapabilitiesEnabled && videoDynamicRangeFormats.length === 0) { + // Just allow all types; MediaCapabilities API should have filtered out anything that was unsupported + videoDynamicRangeFormats = [ + { type: VideoDynamicRangeType.SDR }, + { type: VideoDynamicRangeType.HDR }, + { type: VideoDynamicRangeType.HDR10 }, + { type: VideoDynamicRangeType.DolbyVision }, + { type: VideoDynamicRangeType.HLG }, + ]; + } + const { hdrMediaOptions, sdrMediaOptions } = filterMediaOptionsBasedOnSupportedVideoFormats(compatibleMediaOptions, videoDynamicRangeFormats, logger); + if ((hdrMediaOptions.length === 0 && sdrMediaOptions.length === 0) || (onlyIframes(hdrMediaOptions) && onlyIframes(sdrMediaOptions))) { + throw new PlaylistParsingError(ErrorTypes.MEDIA_ERROR, ErrorDetails.MANIFEST_INCOMPATIBLE_VIDEO_RANGE_ERROR, undefined, 'mediaOption with compatible VIDEO-RANGE not found in manifest', undefined); + } + return { hdrMediaOptions, sdrMediaOptions }; + }), catchError((err) => { + if (err instanceof PlaylistParsingError) { + err.fatal = true; + err.response = ErrorResponses.IncompatibleAsset; + } + throw err; + })); + })); + } + + function mediaOptionToString(mediaOption) { + const MediaOptionLogAttributes = [ + 'persistentId', + 'bitrate', + 'bandwidth', + 'avg-bandwidth', + 'width', + 'height', + 'videoCodec', + 'audioCodec', + 'videoRange', + 'iframes', + 'frameRate', + 'audioGroupId', + 'score', + ]; + return JSON.stringify(mediaOption, MediaOptionLogAttributes); + } + + function isValidCandidate(fromTrack, candidate) { + // Same persistentID but different rendition group + return candidate.mediaOptionId !== fromTrack.mediaOptionId && candidate.persistentID === fromTrack.persistentID && candidate.groupId !== fromTrack.groupId; + } + class AlternateMediaOptionListQuery extends MediaOptionListQuery { + constructor(store, itemId, mediaOptionType) { + super(store, itemId, mediaOptionType); + } + static makeFilters() { + return makeCommonFilters(); + } + _initFilters() { + return AlternateMediaOptionListQuery.kAllowFilters; + } + // For convenience + get _mediaOptionType() { + return this.mediaOptionType; + } + get preferredHost() { + return null; + } + get preferredHost$() { + return of(null); + } + get mediaOptionListInfo() { + var _a, _b; + return (_b = (_a = this.getEntity(this.itemId)) === null || _a === void 0 ? void 0 : _a.mediaOptionListTuple[this._mediaOptionType]) !== null && _b !== void 0 ? _b : null; + } + get mediaOptionListInfo$() { + return this.selectEntity(this.itemId, (entity) => (entity && entity.mediaOptionListTuple ? entity.mediaOptionListTuple[this._mediaOptionType] : null)).pipe(filterNullOrUndefined()); + } + // excludingVariants for skipping variants visited in successive getFallbackVariant; not applicable to alternates + // since we are looking for a *variant* fallback + getFallbackVariant(fromId, sdrOnly, shouldSwitchHosts, excludingVariants = undefined) { + var _a, _b; + const fromTrack = (_a = this.mediaOptionList) === null || _a === void 0 ? void 0 : _a.find((track) => track.mediaOptionId === fromId); + if (!fromTrack) { + return null; + } + const filteredList = this.filteredMediaOptionList; + if (!filteredList) { + return null; + } + // If shouldSwitchHosts is true, we must switch hosts + const fromHost = getHostName(fromTrack.url); + if (shouldSwitchHosts) { + return (_b = filteredList.find((candidate) => isValidCandidate(fromTrack, candidate) && !hasMatchingHost(fromHost, candidate.url))) !== null && _b !== void 0 ? _b : null; + } + // else, allow any host but favor current if available + let newTrack = null; + for (const candidate of filteredList) { + if (isValidCandidate(fromTrack, candidate) && (!newTrack || hasMatchingHost(fromHost, candidate.url))) { + newTrack = candidate; + } + } + return newTrack; + } + // excludingOptions for skipping soon-to-be-penalized alternates + getMatchingAlternateWithPersistentId(persistentId, variant, excludingOptions) { + var _a; + return ((_a = this.preferredMediaOptionList.find((option) => { + if ((excludingOptions === null || excludingOptions === void 0 ? void 0 : excludingOptions.length) > 0 && excludingOptions.includes(option.mediaOptionId)) { + return false; + } + if (!isFiniteNumber(persistentId) || option.persistentID === persistentId) { + if (variant) { + // match group and persistent id + return this.matchGroup(option, variant.audioGroupId, variant.subtitleGroupId, variant.closedcaption); + } + else { + // match only persistent id + return true; + } + } + else { + return false; + } + })) !== null && _a !== void 0 ? _a : null); + } + matchGroup(mediaOption, audioGroup, subtitleGroup, closedCaptionGroup) { + let result = false; + switch (mediaOption.type) { + case 'CLOSED-CAPTIONS': + result = !closedCaptionGroup || mediaOption.groupId === closedCaptionGroup; + break; + case 'SUBTITLES': + result = !subtitleGroup || mediaOption.groupId === subtitleGroup; + break; + case 'AUDIO': + result = !audioGroup || mediaOption.groupId === audioGroup; + break; + } + return result; + } + getMatchingAlternate(fromId, variant) { + const fromAlternate = this.mediaOptionFromId(fromId); + return this.getMatchingAlternateWithPersistentId(fromAlternate === null || fromAlternate === void 0 ? void 0 : fromAlternate.persistentID, variant, []); + } + /** + * Package Alternate media Option + * Augment closed-captions mediaOption with the playlist URL of a supporting subtitle mediaOption. + * This allows Apple-style closed-caption track to include forced subtitles from the supporting WebVTT playlist. + * + * @param variantOption The variantMediaOption that is to be augmented. + * @param altOption The alternate media option (has to be of type closed caption) to be augmented + * @param useFilteredList boolean if true use a filtered list for packaging, hence do not include media in penalty Box + * if false use an unfiltered raw list as seen from the manifest. + * @return AlternateMediaOption A packaged alternate media option or altOption if packaging was not possible + */ + packageAlternateMediaOption(variantOption, altOption, useFilteredList) { + return altOption.mediaType === MediaTypeFourCC.CLOSEDCAPTION ? this.augmentClosedCaptionsWithForcedSubtitles(variantOption === null || variantOption === void 0 ? void 0 : variantOption.subtitleGroupId, altOption, useFilteredList) : altOption; + } + augmentClosedCaptionsWithForcedSubtitles(subtitleGroup, closedCaptionMediaOption, useFilteredList) { + const forcedMediaOption = this.pairForcedSubtitleMediaOptionWithClosedCaption(subtitleGroup, closedCaptionMediaOption, useFilteredList); + return forcedMediaOption ? Object.assign(Object.assign({}, closedCaptionMediaOption), { url: forcedMediaOption.url, backingMediaOptionId: forcedMediaOption.mediaOptionId }) : closedCaptionMediaOption; + } + pairForcedSubtitleMediaOptionWithClosedCaption(subtitleGroup, selectedMediaOption, useFilteredList) { + let supportingOption; + if (selectedMediaOption && selectedMediaOption.mediaType === MediaTypeFourCC.CLOSEDCAPTION) { + let list = this.mediaOptionList; + if (useFilteredList) { + list = this.preferredMediaOptionList; + } + supportingOption = AlternateMediaOptionListQuery.pairForcedSubtitleMediaOptionWithClosedCaptionInList(subtitleGroup, selectedMediaOption, list); + } + return supportingOption; + } + static pairForcedSubtitleMediaOptionWithClosedCaptionInList(subtitleGroup, selectedMediaOption, mediaOptionList) { + return mediaOptionList.find(function (option) { + const match = option.mediaType === MediaTypeFourCC.SUBTITLE && option.lang === selectedMediaOption.lang && option.forced && option.autoselect && (!subtitleGroup || option.groupId === subtitleGroup); + if (match) { + getLogger().debug(`[subtitle] pick side track persistent id ${option.persistentID} option id ${option.id} option mediaOptionId ${option.mediaOptionId} group id ${option.groupId} (=${subtitleGroup})`); + } + return match; + }); + } + } + AlternateMediaOptionListQuery.kAllowFilters = AlternateMediaOptionListQuery.makeFilters(); + + /** + * @brief Query interface to the root playlist store + */ + class RootPlaylistQuery extends QueryEntity { + constructor(rootPlaylistStore, itemId) { + super(rootPlaylistStore); + this.itemId = itemId; + this.mediaOptionListQueries = [ + new VariantMediaOptionListQuery(rootPlaylistStore, this.itemId), + new AlternateMediaOptionListQuery(rootPlaylistStore, this.itemId, MediaOptionType.AltAudio), + new AlternateMediaOptionListQuery(rootPlaylistStore, this.itemId, MediaOptionType.Subtitle), + ]; + } + // getters + get rootPlaylistEntity() { + return this.getEntity(this.itemId); + } + /** + * @returns the unmodified original media options without filters applied + */ + get rootMediaOptionsTuple() { + var _a; + const mediaOptionListTuple = (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.mediaOptionListTuple; + if (mediaOptionListTuple) { + return [mediaOptionListTuple[0].mediaOptions, mediaOptionListTuple[1].mediaOptions, mediaOptionListTuple[2].mediaOptions]; + } + return [[], [], []]; + } + /** + * @returns the position in media corresponding to the start of this item + */ + get itemStartOffset() { + var _a, _b, _c; + if (((_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.itemStartOffset) && isFiniteNumber((_b = this.rootPlaylistEntity) === null || _b === void 0 ? void 0 : _b.itemStartOffset)) { + return (_c = this.rootPlaylistEntity) === null || _c === void 0 ? void 0 : _c.itemStartOffset; + } + return 0; + } + get highestVideoCodec() { + var _a; + return (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.highestVideoCodec; + } + get baseUrl() { + var _a; + return (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.baseUrl; + } + get anchorTime() { + var _a; + return (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.anchorTime; + } + get discoSeqNum() { + var _a, _b; + return (_b = (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.discoSeqNum) !== null && _b !== void 0 ? _b : NaN; + } + get discoSeqNum$() { + return this.selectEntity(this.itemId, 'discoSeqNum'); + } + get audioMediaSelectionGroup() { + var _a, _b; + return (_b = (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.audioMediaSelectionGroup) !== null && _b !== void 0 ? _b : null; + } + get subtitleMediaSelectionGroup() { + var _a, _b; + return (_b = (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.subtitleMediaSelectionGroup) !== null && _b !== void 0 ? _b : null; + } + get audioMediaSelectionOptions() { + var _a, _b, _c; + return (_c = (_b = (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.audioMediaSelectionGroup) === null || _b === void 0 ? void 0 : _b.MediaSelectionGroupOptions) !== null && _c !== void 0 ? _c : []; + } + get subtitleMediaSelectionOptions() { + var _a, _b, _c; + return (_c = (_b = (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.subtitleMediaSelectionGroup) === null || _b === void 0 ? void 0 : _b.MediaSelectionGroupOptions) !== null && _c !== void 0 ? _c : []; + } + get contentSteeringOption() { + var _a; + return (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.contentSteeringOption; + } + get masterVariableList() { + var _a; + return (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.masterVariableList; + } + get loadStats() { + var _a; + return (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.loadStats; + } + get isMediaPlaylist() { + var _a; + return (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.isMediaPlaylist; + } + getInitPTS(discoSeqNum) { + var _a; + return (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.initPtsRecord[discoSeqNum]; + } + get abrStatus$() { + return this.selectEntity(this.itemId, (entity) => entity === null || entity === void 0 ? void 0 : entity.abrStatus); + } + get abrStatus() { + var _a; + return (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.abrStatus; + } + get nextMaxAutoOptionId() { + var _a, _b; + return (_b = (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.abrStatus) === null || _b === void 0 ? void 0 : _b.nextMaxAutoOptionId; + } + get nextMinAutoOptionId() { + var _a, _b; + return (_b = (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.abrStatus) === null || _b === void 0 ? void 0 : _b.nextMinAutoOptionId; + } + initPTS$(discoSeqNum) { + return this.selectEntity(this.itemId, ({ initPtsRecord }) => initPtsRecord[discoSeqNum]); + } + // observables + get rootPlaylistEntity$() { + return this.selectEntity(this.itemId).pipe(filter((rootEntity) => Boolean(rootEntity)), map((rootEntity) => rootEntity)); + } + get rootPlaylistEntityAdded$() { + return this.selectEntityAction(EntityActions.Add).pipe(map((itemIds) => itemIds.map((itemId) => this.getEntity(itemId)))); + } + /** + * @returns the unmodified original media options without filters applied + */ + get rootMediaOptionsTuple$() { + return combineQueries([ + this.selectEntity(this.itemId, (entity) => entity.mediaOptionListTuple[0].mediaOptions), + this.selectEntity(this.itemId, (entity) => entity.mediaOptionListTuple[1].mediaOptions), + this.selectEntity(this.itemId, (entity) => entity.mediaOptionListTuple[2].mediaOptions), + ]); + } + get sessionData() { + var _a; + return (_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.sessionData; + } + get sessionData$() { + return this.selectEntity(this.itemId, ({ sessionData }) => sessionData).pipe(filterNullOrUndefined()); + } + get anchorTime$() { + return this.selectEntity(this.itemId, 'anchorTime').pipe(switchMap((anchorTime) => { + var _a, _b; + if (!isFiniteNumber(anchorTime === null || anchorTime === void 0 ? void 0 : anchorTime.pos)) { + return EMPTY; + } + if ((anchorTime === null || anchorTime === void 0 ? void 0 : anchorTime.pos) !== ((_a = this.anchorTime) === null || _a === void 0 ? void 0 : _a.pos)) { + // Kind of hacky but sometimes this happens???? + getLogger().warn(`anchorTime doesn't match stored value! ${anchorTime === null || anchorTime === void 0 ? void 0 : anchorTime.pos} !== ${(_b = this.anchorTime) === null || _b === void 0 ? void 0 : _b.pos}`); + return EMPTY; + } + return of(anchorTime); + })); + } + get pendingSeek$() { + return this.selectEntity(this.itemId, ({ pendingSeek }) => pendingSeek).pipe(distinctUntilChanged((a, b) => a === b || (typeof a === 'number' && typeof b === 'number' && isNaN(a) && isNaN(b)))); + } + get enabledMediaOptionKeys$() { + return this.selectEntity(this.itemId, 'enabledMediaOptionKeys').pipe(filter((keys) => Boolean(keys))); + } + get enabledMediaOptionKeys() { + var _a, _b; + return (_b = (_a = this.getEntity(this.itemId)) === null || _a === void 0 ? void 0 : _a.enabledMediaOptionKeys) !== null && _b !== void 0 ? _b : [NoMediaOption, NoMediaOption, NoMediaOption]; + } + get enabledMediaOptionSwitchContexts() { + var _a, _b; + return (_b = (_a = this.getEntity(this.itemId)) === null || _a === void 0 ? void 0 : _a.mediaOptionSwitchContexts) !== null && _b !== void 0 ? _b : [null, null, null]; + } + enabledMediaOptionSwitchContextsByType$(mediaOptionType) { + return this.selectEntity(this.itemId, 'mediaOptionSwitchContexts').pipe(map((contexts) => contexts === null || contexts === void 0 ? void 0 : contexts[mediaOptionType])); + } + get enabledMediaOptions$() { + return combineQueries([ + this.enabledMediaOptionByType$(MediaOptionType.Variant), + this.enabledMediaOptionByType$(MediaOptionType.AltAudio), + this.enabledMediaOptionByType$(MediaOptionType.Subtitle), + ]); + } + // Convenience method for just A/V + get enabledAVOptions$() { + return combineQueries([this.enabledMediaOptionByType$(MediaOptionType.Variant), this.enabledMediaOptionByType$(MediaOptionType.AltAudio)]); + } + rawEnabledMediaOptionByType$(mediaOptionType) { + return this.enabledMediaOptionKeys$.pipe(map((enabledMediaOptionKeys) => { + const mediaOptionKey = enabledMediaOptionKeys[mediaOptionType]; + if (!isEnabledMediaOption(mediaOptionKey)) { + // return NoMediaOption even if mediaOptionKey may have a valid itemId. + // callers typically check for NoMediaOption only to see if a track is disabled. + return NoMediaOption; + } + const options = this.rootMediaOptionsTuple[mediaOptionType]; + const foundOption = options.find((option) => mediaOptionKeyEquals(option, mediaOptionKey)); + return foundOption ? foundOption : NoMediaOption; + })); + } + enabledMediaOptionByType$(mediaOptionType) { + return this.rawEnabledMediaOptionByType$(mediaOptionType).pipe(distinctUntilChanged((a, b) => a.mediaOptionId === b.mediaOptionId && a.url === b.url)); + } + /** + * Emits when selected option changes and returns switch information + */ + enabledMediaOptionSwitchForType$(mediaOptionType) { + // Only trigger on option change, not when context is set + // rawEnabledMediaOptionByType does not de-dupe mediaOption changes + // so it's possible to snapshot/represent a switch to the same mediaOption { fromId: sameMediaOptionId, toId: sameMediaOptionId } + // when this happens, the observer will know no switching took place and it won't trigger any change event. + // enabledMediaOptionByType will always return a stale mediaOption switch from previous activities, + // potentially causing bogus emits and event triggers. + return this.rawEnabledMediaOptionByType$(mediaOptionType).pipe(withLatestFrom(this.enabledMediaOptionSwitchContextsByType$(mediaOptionType)), startWith(null), pairwise(), map(([a, b]) => { + return { fromId: a === null || a === void 0 ? void 0 : a[0].mediaOptionId, toId: b === null || b === void 0 ? void 0 : b[0].mediaOptionId, switchContext: b === null || b === void 0 ? void 0 : b[1] }; + }), distinctUntilChanged((x, y) => x.fromId === y.fromId && x.toId === y.toId)); + } + enableMediaOptionSwitchedForType$(mediaOptionType) { + return this.enabledMediaOptionByType$(mediaOptionType).pipe(switchMap((mediaOption) => waitFor(combineLatest([of(mediaOption), this.enabledMediaOptionSwitchContextsByType$(mediaOptionType).pipe(pairwise())]), ([option, contexts]) => contexts[0] && !contexts[1])), + // drop the contexts because contexts[0] may be wrong in this sequence: + // 1. Request 1: setEnabledMediaOptions( englishMediaOption, { switchPosition: 0.0 }); + // 2. Request 2: setEnabledMediaOptions( englishMediaOption, { switchPosition: 1.0 }); + // 3. Request 2 succeeded: setEnabledMediaOptions( englishMediaOption, null); + // But this function will return [englishMediaOption, [{ switchPosition: 0.0 }, null]] + // Nonetheless, englishMediaOption did switch successfully. + map(([option]) => option)); + } + enabledMediaOptionIdByType(mediaOptionType) { + return this.getEntity(this.itemId).enabledMediaOptionKeys[mediaOptionType].mediaOptionId; + } + get enabledVariantMediaOptionIdBeforeTrickplaySwitch() { + return this.getEntity(this.itemId).enabledVariantMediaOptionIdBeforeTrickplaySwitch; + } + variantMediaOptionById(mediaOptionId) { + return this.mediaOptionListQueries[MediaOptionType.Variant].mediaOptionFromId(mediaOptionId); + } + alternateMediaOptionById(mediaOptionType, mediaOptionId) { + return this.mediaOptionListQueries[mediaOptionType].mediaOptionFromId(mediaOptionId); + } + enabledAlternateMediaOptionByType(mediaOptionType) { + const enabledOptionId = this.enabledMediaOptionIdByType(mediaOptionType); + return this.alternateMediaOptionById(mediaOptionType, enabledOptionId); + } + get enabledVariantMediaOption() { + const enabledOptionId = this.enabledMediaOptionIdByType(MediaOptionType.Variant); + return this.variantMediaOptionById(enabledOptionId); + } + lastLoadedMediaOptionByType(mediaOptionType) { + var _a; + return (_a = this.getEntity(this.itemId).lastLoadedMediaOptionKeys) === null || _a === void 0 ? void 0 : _a[mediaOptionType]; + } + /** + * Set by ABR and error handling. Next enabled media options to be used next time we are allowed to switch + */ + get nextMediaOptionsKeys$() { + return this.selectEntity(this.itemId, 'nextMediaOptionKeys'); + } + /** + * Return the filtered list with preferred host filtering + */ + get preferredMediaOptions() { + return [this.mediaOptionListQueries[0].preferredMediaOptionList, this.mediaOptionListQueries[1].preferredMediaOptionList, this.mediaOptionListQueries[2].preferredMediaOptionList]; + } + get preferredMediaOptions$() { + return combineQueries([ + this.mediaOptionListQueries[0].preferredMediaOptionList$, + this.mediaOptionListQueries[1].preferredMediaOptionList$, + this.mediaOptionListQueries[2].preferredMediaOptionList$, + ]); + } + get filteredMediaOptions() { + return [this.mediaOptionListQueries[0].filteredMediaOptionList, this.mediaOptionListQueries[1].filteredMediaOptionList, this.mediaOptionListQueries[2].filteredMediaOptionList]; + } + getDisabledMediaOption(mediaOptionType) { + return { itemId: this.itemId, mediaOptionType, mediaOptionId: 'Nah' }; + } + getEnabledMediaOptionMask() { + return this.enabledMediaOptionKeys.map((key) => isEnabledMediaOption(key)); + } + /** + * Get filtered list with preferred host filtering by type + * @param mediaOptionType The type to get info about + */ + getPreferredMediaOptionsByType$(mediaOptionType) { + return this.mediaOptionListQueries[mediaOptionType].preferredMediaOptionList$; + } + altMediaOptionHasValidUrl(mediaOptionType, mediaOptionId) { + const altOption = this.alternateMediaOptionById(mediaOptionType, mediaOptionId); + return Boolean(altOption === null || altOption === void 0 ? void 0 : altOption.url); + } + /** + * Return whether the preferred variant set is HDR + */ + get hdrMode$() { + return this.mediaOptionListQueries[MediaOptionType.Variant].hdrMode$; + } + get maxHdcpLevel$() { + return this.mediaOptionListQueries[MediaOptionType.Variant].maxHdcpLevel$; + } + get currentPathwayID() { + return this.mediaOptionListQueries[MediaOptionType.Variant].currentPathwayID; + } + get preferredHost() { + return this.mediaOptionListQueries[MediaOptionType.Variant].preferredHost; + } + /** + * Get error info by type + * @param mediaOptionType The type to get info about + */ + getErrorInfoByType(mediaOptionType) { + var _a; + if (((_a = this.rootPlaylistEntity) === null || _a === void 0 ? void 0 : _a.errorsByType) != null) { + return this.rootPlaylistEntity.errorsByType[mediaOptionType]; + } + return null; + } + getInFlightFragByType(mediaOptionType) { + var _a, _b, _c; + return (_c = (_b = (_a = this.getEntity(this.itemId)) === null || _a === void 0 ? void 0 : _a.inFlightFrags) === null || _b === void 0 ? void 0 : _b[mediaOptionType]) !== null && _c !== void 0 ? _c : null; + } + getInFlightFragByType$(mediaOptionType) { + return this.selectEntity(this.itemId, (entity) => { var _a; return (_a = entity === null || entity === void 0 ? void 0 : entity.inFlightFrags) === null || _a === void 0 ? void 0 : _a[mediaOptionType]; }); + } + matchAlternates(currentVariant, audioPersistentId, subtitlePersistentId, excludingOptions) { + const audioAltMediaOption = isFiniteNumber(audioPersistentId) + ? this.mediaOptionListQueries[MediaOptionType.AltAudio].getMatchingAlternateWithPersistentId(audioPersistentId, currentVariant, excludingOptions) + : undefined; + const subtitleAltMediaOption = isFiniteNumber(subtitlePersistentId) + ? this.mediaOptionListQueries[MediaOptionType.Subtitle].getMatchingAlternateWithPersistentId(subtitlePersistentId, currentVariant, excludingOptions) + : undefined; + return [audioAltMediaOption ? audioAltMediaOption : NoMediaOption, subtitleAltMediaOption ? subtitleAltMediaOption : NoMediaOption]; + } + getLegacyMatchingAlternateWithPersistentId(mediaOptionType, persistentId, currentVariant) { + let altMediaOption = this.mediaOptionListQueries[mediaOptionType].getMatchingAlternateWithPersistentId(persistentId, currentVariant, []); + if (!altMediaOption) { + altMediaOption = this.mediaOptionListQueries[mediaOptionType].getMatchingAlternateWithPersistentId(persistentId, undefined, []); // just match persistent id + } + return altMediaOption; + } + isValidMediaOptionTuple(tuple, mediaOptionMask = undefined) { + const expectedEnabledMediaOptionMask = mediaOptionMask ? mediaOptionMask : this.getEnabledMediaOptionMask(); + const result = [MediaOptionType.Variant, MediaOptionType.AltAudio, MediaOptionType.Subtitle].reduce((prev, cur) => { + return prev && expectedEnabledMediaOptionMask[cur] === isEnabledMediaOption(tuple[cur]); + }, true); + return result; + } + matchGroup(mediaOption, audioGroup, subtitleGroup, closedCaptionGroup) { + const mediaType = mediaOption.mediaOptionType; + const altOptionQuery = this.mediaOptionListQueries[mediaType]; + return altOptionQuery.matchGroup(mediaOption, audioGroup, subtitleGroup, closedCaptionGroup); + } + get preferHDR() { + return this.mediaOptionListQueries[MediaOptionType.Variant].mediaOptionListInfo.preferHDR; + } + } + + class RootPlaylistStore extends EntityStore { + constructor() { + super({}, { name: 'root-playlist-store', idKey: 'itemId', producerFn: produce_1 }); + } + akitaPreAddEntity(newEntity) { + if (newEntity.errorsByType == null) { + return Object.assign(Object.assign({}, newEntity), { errorsByType: [{ timeouts: { load: 0, append: 0, key: 0 } }, { timeouts: { load: 0, append: 0, key: 0 } }, { timeouts: { load: 0, append: 0, key: 0 } }] }); + } + return newEntity; + } + } + + const loggerName = { name: 'rps' }; + /** + * @brief Service for managing root playlist state. Will fetch the root playlist and manage selected media options + */ + const PENALTYBOX_TIMEOUT_MS = 120000; // 2 minutes + class RootPlaylistService { + constructor(store, logger) { + this.store = store; + this.logger = logger; + } + getQuery() { + return new QueryEntity(this.store); + } + getQueryForId(itemId) { + return new RootPlaylistQuery(this.store, itemId); + } + set rootPlaylistEntity(rootPlaylistEntity) { + logAction('root.add.rootPlaylist'); + this.store.add(rootPlaylistEntity); + } + // Remove entity by item id + removeItems(itemIds) { + logAction(`root.add.remove ${JSON.stringify(itemIds)}`); + this.store.remove(itemIds); + } + // Remove all entities + removeAll() { + logAction('root.add.clear'); + this.store.remove(); + } + setRootPlaylistEntity(itemId, rootPlaylistEntity) { + logAction('root.set.rootPlaylistEntity'); + this.store.update(itemId, (entity) => { + // Must return this so that entire entity is updated + return (rootPlaylistEntity); + }); + } + setSessionData(itemId, sessionData) { + logAction('root.set.sessionData'); + this.store.update(itemId, (rootPlaylistEntity) => { + rootPlaylistEntity.sessionData = sessionData; + }); + } + /** + * Update the position and discontinuity sequence number used for choosing fragments + * @param anchorTime The anchor position + * @param discoSeqNum The discontinuity sequence number + */ + setAnchorTime(itemId, anchorTime) { + logAction(`root.set.anchorTime: ${anchorTime === null || anchorTime === void 0 ? void 0 : anchorTime.pos} ${anchorTime === null || anchorTime === void 0 ? void 0 : anchorTime.discoSeqNum}`); + this.store.update(itemId, (rootPlaylistEntity) => { + rootPlaylistEntity.anchorTime = anchorTime; + }); + } + setDiscoSeqNum(itemId, cc) { + logAction(`root.set.discoSeqNum: ${cc}`); + this.store.update(itemId, (rootPlaylistEntity) => { + rootPlaylistEntity.discoSeqNum = cc; + }); + } + setPendingSeek(itemId, pendingSeek) { + logAction('root.set.pendingSeek'); + this.store.update(itemId, (rootPlaylistEntity) => { + rootPlaylistEntity.pendingSeek = pendingSeek; + }); + if (pendingSeek === undefined) { + globalHlsService().setUserSeek(pendingSeek); + } + } + setEnabledMediaOptionSwitchContextByType(itemId, mediaOptionType, mediaOptionId, context) { + this.logger.info(`root.set.mediaOptionSwitchContextByType: ${mediaOptionType} ${mediaOptionId} ${context === null || context === void 0 ? void 0 : context.userInitiated}`); + this.store.update(itemId, (entity) => { + var _a; + if (entity.enabledMediaOptionKeys[mediaOptionType].mediaOptionId === mediaOptionId) { + const mediaOptionContextTuple = (_a = entity.mediaOptionSwitchContexts) !== null && _a !== void 0 ? _a : [null, null, null]; + mediaOptionContextTuple[mediaOptionType] = context ? { userInitiated: context.userInitiated, switchPosition: context.switchPosition } : null; + entity.mediaOptionSwitchContexts = mediaOptionContextTuple; + } + else { + // Don't update switch context for a different mediaOption + logAction(`root.set.mediaOptionSwitchContextByType ${mediaOptionId} doesn't match existing mediaOption ${entity.enabledMediaOptionKeys[mediaOptionType].mediaOptionId}`); + } + }); + } + setEnabledVariantMediaOptionIdBeforeTrickplaySwitch(itemId, id) { + this.logger.info(`root.set.enabledVariantMediaOptionIdBeforeTrickplaySwitch: ${id}`); + this.store.update(itemId, (entity) => { + entity.enabledVariantMediaOptionIdBeforeTrickplaySwitch = id; + }); + } + setEnabledMediaOptionByType(itemId, mediaOptionType, mediaOption, replaceContext = false, context = undefined) { + if (!mediaOption) { + mediaOption = { itemId, mediaOptionType, mediaOptionId: 'Nah' }; + } + this.logger.info(`root.set.mediaOptionByType: ${mediaOptionType} ${mediaOption.mediaOptionId} replaceCtxt ${replaceContext} ctxt ${JSON.stringify(context === null || context === void 0 ? void 0 : context.userInitiated)}`); + this.store.update(itemId, (entity) => { + var _a, _b; + const mediaOptionKeyTuple = (_a = [...entity.enabledMediaOptionKeys]) !== null && _a !== void 0 ? _a : [NoMediaOption, NoMediaOption, NoMediaOption]; + mediaOptionKeyTuple[mediaOptionType] = { itemId, mediaOptionId: mediaOption.mediaOptionId }; + this._updateEnabledMediaOptionKeys(entity, mediaOptionKeyTuple); + if (replaceContext) { + // consider this sequence of events: + // (1) user switches audio mediaOption. Such an alt audio switch will include a switchContext. + // (2) While audio mediaOption is being handled, user switches subtitle mediaOption, hls.js *might* also switch the audio mediaOption to match the latest group-ids. + // The "silent" audio track switch in (2) should not override the userInitiated switchContext in (1) + // replaceContext should be true in (1) and false in (2) + const mediaOptionContextTuple = (_b = entity.mediaOptionSwitchContexts) !== null && _b !== void 0 ? _b : [null, null, null]; + mediaOptionContextTuple[mediaOptionType] = context ? { userInitiated: context.userInitiated, switchPosition: context.switchPosition } : null; + entity.mediaOptionSwitchContexts = mediaOptionContextTuple; + } + }); + } + _associateForcedSubtitleWithClosedCaption(entity, variantMediaOptionId, closedCaption, rootQuery) { + if ((closedCaption === null || closedCaption === void 0 ? void 0 : closedCaption.mediaType) === MediaTypeFourCC.CLOSEDCAPTION) { + const variantOption = rootQuery.variantMediaOptionById(variantMediaOptionId); + const newClosedCaption = rootQuery.mediaOptionListQueries[MediaOptionType.Subtitle].packageAlternateMediaOption(variantOption, closedCaption, true); + getLogger().info(`[subtitle] new closedCaption ${redactUrl(newClosedCaption.url)} vs old ${redactUrl(closedCaption.url)}`); + if (newClosedCaption.url !== closedCaption.url) { + const newMediaOptions = replaceClosedCaptionInMediaOptionListIfNecessary(variantOption, newClosedCaption, entity.mediaOptionListTuple[MediaOptionType.Subtitle].mediaOptions, getLogger()); + entity.mediaOptionListTuple[MediaOptionType.Subtitle].mediaOptions = newMediaOptions; + } + } + } + // Stuff that must be done always when setting enabledMediaOptionKeys + _updateEnabledMediaOptionKeys(entity, mediaOptionKeyTuple) { + var _a; + const enabledMediaOptionKeys = (_a = entity.enabledMediaOptionKeys) !== null && _a !== void 0 ? _a : [NoMediaOption, NoMediaOption, NoMediaOption]; + let enabledVariantKey; + for (let type = 0; type < mediaOptionKeyTuple.length; ++type) { + const newOption = mediaOptionKeyTuple[type]; + const didChange = enabledMediaOptionKeys[type].mediaOptionId !== newOption.mediaOptionId; + if (didChange) { + enabledMediaOptionKeys[type] = Object.assign({}, newOption); + } + if (type === MediaOptionType.Variant) { + // Using all mediaOptions so that mediaOptions in PenaltyBox are also considered when setting highBWTrigger + const unfilteredList = this.getQueryForId(newOption.itemId).mediaOptionListQueries[type].mediaOptionList; + if (didChange) { + entity.abrStatus = initializeAbrStatus(newOption.mediaOptionId, unfilteredList); + } + else { + // Just update highBW trigger + entity.abrStatus.highBWTrigger = getLowestSuperiorBW(newOption.mediaOptionId, unfilteredList); + } + enabledVariantKey = newOption; + } + else if (type === MediaOptionType.Subtitle && isEnabledMediaOption(newOption)) { + const rootQuery = this.getQueryForId(newOption.itemId); + const subtitleOption = rootQuery.alternateMediaOptionById(type, newOption.mediaOptionId); + this._associateForcedSubtitleWithClosedCaption(entity, enabledVariantKey.mediaOptionId, subtitleOption, rootQuery); + } + } + entity.enabledMediaOptionKeys = enabledMediaOptionKeys; + entity.nextMediaOptionKeys = undefined; + } + // Disables ABR. for development + setManualMode(itemId, manualMode) { + this.store.update(itemId, (entity) => { + entity.manualMode = manualMode; + }); + } + setEnabledMediaOptions(itemId, mediaOptions) { + this.logger.info(`root.set.enabledMediaOptions: ${JSON.stringify(mediaOptions.map((x) => x.mediaOptionId))}`); + this.store.update(itemId, (entity) => { + const mediaOptionKeyTuple = mediaOptions.map(({ mediaOptionId, itemId }) => { + const result = { mediaOptionId, itemId }; + return result; + }); + this._updateEnabledMediaOptionKeys(entity, mediaOptionKeyTuple); + }); + } + setEnabledMediaOptionsAndSwitchContexts(itemId, mediaOptions, switchContexts) { + this.logger.info(`root.set.enabledMediaOptionsAndSwitchContexts: ${JSON.stringify(mediaOptions.map((x) => x.mediaOptionId))} ctxt ${JSON.stringify(switchContexts)}`); + this.store.update(itemId, (entity) => { + const mediaOptionKeyTuple = mediaOptions.map(({ mediaOptionId, itemId }) => { + const result = { mediaOptionId, itemId }; + return result; + }); + this._updateEnabledMediaOptionKeys(entity, mediaOptionKeyTuple); + entity.mediaOptionSwitchContexts = switchContexts; + }); + } + setNextMediaOptions(itemId, mediaOptions) { + logAction(`root.set.nextMediaOptions: ${JSON.stringify(mediaOptions === null || mediaOptions === void 0 ? void 0 : mediaOptions.map((x) => x.mediaOptionId))}`); + this.store.update(itemId, (entity) => { + const keys = mediaOptions + ? mediaOptions.map(({ itemId, mediaOptionId }) => { + const result = { itemId, mediaOptionId }; + return result; + }) + : null; + entity.nextMediaOptionKeys = keys; + }); + } + // updateEnabledMediaOptions should not touch the switch context + updateEnabledMediaOptions(itemId) { + logAction('root.set.updateEnabledMediaOptions'); + this.store.update(itemId, (entity) => { + if (entity.nextMediaOptionKeys && entity.manualMode !== true) { + logAction(`root.set.updateEnabledMediaOptions ${JSON.stringify(entity.nextMediaOptionKeys)}`); + this._updateEnabledMediaOptionKeys(entity, [...entity.nextMediaOptionKeys]); + } + entity.nextMediaOptionKeys = undefined; + }); + } + setLastLoadedMediaOptionByType(itemId, mediaOptionType, mediaOption) { + if (!mediaOption) { + mediaOption = { itemId, mediaOptionType, mediaOptionId: 'Nah' }; + } + logAction(`root.set.lastLoadedMediaOptionByType: ${mediaOptionType} ${mediaOption.mediaOptionId}`); + this.store.update(itemId, (entity) => { + var _a; + const mediaOptionKeyTuple = (_a = entity.lastLoadedMediaOptionKeys) !== null && _a !== void 0 ? _a : [NoMediaOption, NoMediaOption, NoMediaOption]; + mediaOptionKeyTuple[mediaOptionType] = { itemId, mediaOptionId: mediaOption.mediaOptionId }; + entity.lastLoadedMediaOptionKeys = mediaOptionKeyTuple; + }); + } + setPreferredHost(itemId, newHost) { + logAction(`root.set.preferredHost: ${newHost}`); + this.store.update(itemId, (entity) => { + if (!entity) { + return; + } + const optionListInfo = entity.mediaOptionListTuple[MediaOptionType.Variant]; + optionListInfo.preferredHost = newHost; + }); + } + setViewportInfo(itemId, viewportInfo) { + logAction(`root.set.viewportInfo: ${JSON.stringify(viewportInfo)}`); + this.store.update(itemId, (entity) => { + if (!entity) { + return; + } + const optionListInfo = entity.mediaOptionListTuple[MediaOptionType.Variant]; + optionListInfo.viewportInfo = viewportInfo; + }); + } + static getExistingPersistentIds(entity) { + var _a, _b; + const existingSelection = {}; + const audioOptionId = (_a = entity.enabledMediaOptionKeys[MediaOptionType.AltAudio]) === null || _a === void 0 ? void 0 : _a.mediaOptionId; + if (audioOptionId !== 'Nah') { + const audioOptionListInfo = entity.mediaOptionListTuple[MediaOptionType.AltAudio]; + const filteredAudio = applyFilters(audioOptionListInfo.mediaOptions, AlternateMediaOptionListQuery.kAllowFilters, audioOptionListInfo); + const altAudioOption = filteredAudio.find((altOption) => altOption.mediaOptionId === audioOptionId); + existingSelection.audioPersistentId = altAudioOption === null || altAudioOption === void 0 ? void 0 : altAudioOption.persistentID; + } + const subtitleOptionId = (_b = entity.enabledMediaOptionKeys[MediaOptionType.Subtitle]) === null || _b === void 0 ? void 0 : _b.mediaOptionId; + if (subtitleOptionId !== 'Nah') { + const subtitleOptionListInfo = entity.mediaOptionListTuple[MediaOptionType.Subtitle]; + const filteredSubtitles = applyFilters(subtitleOptionListInfo.mediaOptions, AlternateMediaOptionListQuery.kAllowFilters, subtitleOptionListInfo); + const subtitleOption = filteredSubtitles.find((altOption) => altOption.mediaOptionId === subtitleOptionId); + existingSelection.subtitlePersistentId = subtitleOption === null || subtitleOption === void 0 ? void 0 : subtitleOption.persistentID; + } + return existingSelection; + } + static doUpdateRootHDRSwitch(entity, preferHDR, hasHdrLevels, logger) { + // immer complains if you try to modify and update the object in one go so make deep copy of mediaOptionListTuple + const mediaOptionListTuple = entity.mediaOptionListTuple.map((info) => (Object.assign({}, info))); + mediaOptionListTuple[MediaOptionType.Variant].preferHDR = preferHDR; + mediaOptionListTuple[MediaOptionType.Variant].hasHdrLevels = hasHdrLevels; + const hlsConf = getCurrentConfig(); + const curItem = queueItemQuery.getEntity(entity.itemId); + const statsQuery = createStatsQuery(entity.itemId); + const bandwidthEstimate = statsQuery.getBandwidthEstimate(hlsConf, curItem === null || curItem === void 0 ? void 0 : curItem.serviceName); + const playlistEstimate = statsQuery.getPlaylistEstimate(hlsConf, curItem === null || curItem === void 0 ? void 0 : curItem.serviceName); + const fragEstimate = statsQuery.getFragEstimate(hlsConf, curItem === null || curItem === void 0 ? void 0 : curItem.serviceName); + const bufferEstimate = statsQuery.getBufferEstimate(hlsConf, curItem === null || curItem === void 0 ? void 0 : curItem.serviceName); + const adaptiveStartupConfig = { + targetDuration: fragEstimate.maxDurationSec || (hlsConf === null || hlsConf === void 0 ? void 0 : hlsConf.defaultTargetDuration), + targetStartupMs: hlsConf === null || hlsConf === void 0 ? void 0 : hlsConf.targetStartupMs, + }; + const existingSelection = RootPlaylistService.getExistingPersistentIds(entity); + return updateRootPlaylistEntityWithEnabledMediaOptionKeys(Object.assign(Object.assign({}, entity), { mediaOptionListTuple, nextMediaOptionKeys: null }), existingSelection, logger, bandwidthEstimate, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate); + } + // For error handling. disable HDR playback but don't re-calculate enabled media options + switchToSDROnly(itemId) { + logAction('root.switchToSDROnly'); + this.store.update(itemId, (entity) => { + const { mediaOptionListTuple } = RootPlaylistService.doUpdateRootHDRSwitch(entity, false, false, this.logger); + entity.mediaOptionListTuple = mediaOptionListTuple; + }); + } + /** + * Update HDR preference and set enabled media options based on the preference + */ + setHDRPreference(itemId, preferHDR, updateEnabledOptions) { + logAction(`root.set.HDRPreference: ${preferHDR}`); + this.store.update(itemId, (entity) => { + const optionListInfo = entity.mediaOptionListTuple[MediaOptionType.Variant]; + if (optionListInfo.preferHDR === preferHDR || (preferHDR && !optionListInfo.hasHdrLevels)) { + return; + } + const newEntity = RootPlaylistService.doUpdateRootHDRSwitch(entity, preferHDR, optionListInfo.hasHdrLevels, this.logger); + if (!updateEnabledOptions) { + entity.mediaOptionListTuple = newEntity.mediaOptionListTuple; + } + else { + // Must return this so that entire entity is updated + return (entity = newEntity); + } + }); + } + setPathwayPriority(itemId, pathwayPriority) { + logAction(`root.set.PathwayPriority: [ ${pathwayPriority.join(', ')} ]`); + this.store.update(itemId, (entity) => { + if (!entity) { + return; + } + const optionListInfo = entity.mediaOptionListTuple[MediaOptionType.Variant]; + optionListInfo.pathwayPriority = pathwayPriority; + optionListInfo.preferredHost = null; + }); + } + setCurrentPathwayID(itemId, currentPathwayID) { + logAction(`root.set.currentPathwayID: ${currentPathwayID}`); + this.store.update(itemId, (entity) => { + if (!entity) { + return; + } + const optionListInfo = entity.mediaOptionListTuple[MediaOptionType.Variant]; + optionListInfo.currentPathwayID = currentPathwayID; + }); + } + setInitPTS(itemId, discoSeqNum, variantDTS, timelineOffset, offsetTimestamp, iframeMode) { + logAction(`root.set.initPTS: ${itemId} ${discoSeqNum} variantDTS:${JSON.stringify(variantDTS)} timelineOffset: ${timelineOffset}`); + this.store.update(itemId, (entity) => { + entity.initPtsRecord[discoSeqNum] = { variantDTS, timelineOffset, offsetTimestamp, iframeMode }; + }); + } + /** + * Modify penaltyBoxQueue to remove all expired entries in place + * @param penaltyBoxQueue + * @param now + */ + static prunePenaltyBox(penaltyBoxQueue, now) { + return penaltyBoxQueue.filter((x) => !isExpired(x.expiry, now)); + } + /** + * Add entry to penaltyBoxQueue in place + * @param penaltyBoxQueue + * @param now + * @param mediaOptionId + */ + static addToPenaltyBox(penaltyBoxQueue, now, mediaOptionId) { + return penaltyBoxQueue.push({ mediaOptionId, expiry: now + PENALTYBOX_TIMEOUT_MS }); + } + /** + * Put a media option into the penalty box. It will stay there for 2 minutes + * @param filterListId The filter list to modify + * @param mediaOptionType The media option type + * @param mediaOptionId The media option to put into penalty box + */ + addToPenaltyBox(itemId, mediaOptionType, mediaOptionId) { + logAction(`root.set.penaltyBox: ${mediaOptionType}: ${mediaOptionId}`); + this.store.update(itemId, ({ mediaOptionListTuple: mediaOptionsStates }) => { + const optionListInfo = mediaOptionsStates[mediaOptionType]; + // TODO: dedup + const now = performance.now(); + optionListInfo.penaltyBoxQueue = RootPlaylistService.prunePenaltyBox(optionListInfo.penaltyBoxQueue, now); + RootPlaylistService.addToPenaltyBox(optionListInfo.penaltyBoxQueue, now, mediaOptionId); + }); + } + /** + * Remove all expired entries from penalty box + * @param itemId The queue item id + * @param mediaOptionType which media option type to prune penalty box for. if null, prune all + */ + prunePenaltyBox(itemId, mediaOptionType = null) { + logAction(`root.set.prunePenaltyBox: ${mediaOptionType}`); + this.store.update(itemId, ({ mediaOptionListTuple }) => { + const infoList = mediaOptionType ? [mediaOptionListTuple[mediaOptionType]] : mediaOptionListTuple; + const now = performance.now(); + for (const info of infoList) { + info.penaltyBoxQueue = RootPlaylistService.prunePenaltyBox(info.penaltyBoxQueue, now); + } + }); + } + /** + * Put a media option in the remove list. It will stay there permanently. + * @param itemId The filter list to modify + * @param mediaOptionType The media option type + * @param mediaOptionId The media option to put in remove list + */ + removePermanently(itemId, mediaOptionType, mediaOptionId) { + logAction(`root.set.removePermanently: ${mediaOptionType}: ${mediaOptionId}`); + this.store.update(itemId, ({ mediaOptionListTuple: mediaOptionsStates }) => { + const optionListInfo = mediaOptionsStates[mediaOptionType]; + // dedup. uncommon to remove so this should hopefully be small + const removeSet = new Set(optionListInfo.removed); + removeSet.add(mediaOptionId); + optionListInfo.removed = Array.from(removeSet); + }); + } + /** + * + * @param itemId The filter list to modify + * @param mediaOptionType Which media option list to update + * @param hostName The hostname used for matching + * @param remove if true, remove this host permanently from valid list. if false, put into penalty box + */ + moveAllWithMatchingHosts(itemId, mediaOptionType, hostName, remove) { + logAction(`root.set.moveAllMatchingHosts: ${mediaOptionType}:${hostName} remove:${remove}`); + this.store.update(itemId, ({ mediaOptionListTuple: mediaOptionsStates }) => { + const optionListInfo = mediaOptionsStates[mediaOptionType]; + const mediaOptionIdsToModify = [...optionListInfo.mediaOptions].filter((mo) => hasMatchingHost(hostName, mo.url)).map((x) => x.mediaOptionId); + if (remove) { + const merged = new Set([...optionListInfo.removed, ...mediaOptionIdsToModify]); + optionListInfo.removed = Array.from(merged); + } + else { + const now = performance.now(); + optionListInfo.penaltyBoxQueue = RootPlaylistService.prunePenaltyBox(optionListInfo.penaltyBoxQueue, now); + for (const mediaOptionId of mediaOptionIdsToModify) { + RootPlaylistService.addToPenaltyBox(optionListInfo.penaltyBoxQueue, now, mediaOptionId); + } + } + }); + } + /** + * @param itemId the item id to modify + * @param maxHdcpLevel the maximum hdcp level allowed for this asset (exclusive). Only allow variants with hdcpLevel < maxHdcpLevel + * @param force if true, always update. if false, only update if maxHdcpLevel is < current maxHdcpLevel + */ + setMaxHdcpLevel(itemId, maxHdcpLevel, force = false) { + logAction(`root.set.maxHdcpLevel: ${maxHdcpLevel}`); + this.store.update(itemId, ({ mediaOptionListTuple: mediaOptionsStates }) => { + const optionListInfo = mediaOptionsStates[MediaOptionType.Variant]; + // Only can lower max hdcp level unless forced + if (force || hdcpLevelToInt(maxHdcpLevel) < hdcpLevelToInt(optionListInfo.maxHdcpLevel)) { + optionListInfo.maxHdcpLevel = maxHdcpLevel; + } + }); + } + /** + * @param itemId The queue item id + * @param mediaOptionType the mediaOptionType + * @param increment if true, increment else reset + * @param timeout Which type of timeout it was + */ + updateConsecutiveTimeouts(itemId, mediaOptionType, increment, type) { + this.store.update(itemId, (entity) => { + const errorHandlingInfo = entity.errorsByType || [ + { timeouts: { load: 0, append: 0, key: 0 } }, + { timeouts: { load: 0, append: 0, key: 0 } }, + { timeouts: { load: 0, append: 0, key: 0 } }, + ]; + if (increment) { + ++errorHandlingInfo[mediaOptionType].timeouts[type]; + } + else { + errorHandlingInfo[mediaOptionType].timeouts[type] = 0; + } + entity.errorsByType = errorHandlingInfo; + }); + } + // Update inflight fragment information. For now just hooking up Variant & AltAudio + updateInflightFrag(itemId, type, frag, state, sample) { + logAction('root.set.updateInflightFrag'); + this.store.update(itemId, (entity) => { + if (!entity.inFlightFrags) { + entity.inFlightFrags = [null, null]; + } + if (type === MediaOptionType.Subtitle || (frag && frag.itemId !== itemId)) { + return; + } + if (!frag) { + entity.inFlightFrags[type] = null; + return; + } + let { start, duration } = frag; + const { mediaOptionId, mediaSeqNum, discoSeqNum } = frag; + const inFlight = entity.inFlightFrags[type]; + // Update tstart if state changed + let tstart = inFlight === null || inFlight === void 0 ? void 0 : inFlight.tstart; + if (state !== (inFlight === null || inFlight === void 0 ? void 0 : inFlight.state)) { + tstart = performance.now(); + } + if (fragEqual(inFlight, frag)) { + start = inFlight.start; + duration = inFlight.duration; + } + entity.inFlightFrags[type] = { + itemId, + mediaOptionId, + mediaSeqNum, + discoSeqNum, + start, + duration, + tstart, + state, + bwSample: Object.assign({}, sample), + }; + }); + } + setNextMaxAutoOptionId(itemId, nextMaxAutoOptionId) { + logAction(`root.set.nextMaxAutoOptionId: ${nextMaxAutoOptionId}`); + this.store.update(itemId, ({ abrStatus: abrs }) => { + abrs.nextMaxAutoOptionId = nextMaxAutoOptionId; + }); + } + setNextMinAutoOptionId(itemId, nextMinAutoOptionId) { + logAction(`root.set.nextMinAutoOptionId: ${nextMinAutoOptionId}`); + this.store.update(itemId, ({ abrStatus: abrs }) => { + abrs.nextMinAutoOptionId = nextMinAutoOptionId; + }); + } + setHighBWTrigger(itemId, value) { + logAction(`root.set.setHighBWTrigger: ${value}`); + this.store.update(itemId, ({ abrStatus: abrs }) => { + abrs.highBWTrigger = value; + }); + } + setFragLoadSlow(itemId, value) { + logAction(`root.set.setFragLoadSlow ${itemId} ${JSON.stringify(value)}`); + this.store.update(itemId, ({ abrStatus: abrs }) => { + abrs.fragDownloadSlow = value.fragDownloadSlow; + abrs.fragDownloadTooSlow = value.fragDownloadTooSlow; + }); + } + pickMediaOptionTupleByPersistentId(rootQuery, mediaOptionType, persistentId, sdrOnly = false, shouldSwitchHost = false) { + const currentVariantId = rootQuery.enabledMediaOptionIdByType(MediaOptionType.Variant); + const variant = rootQuery.variantMediaOptionById(currentVariantId); + let audioPersistentId; + let subtitlePersistentId; + if (mediaOptionType === MediaOptionType.AltAudio) { + const subtitleAltOption = rootQuery.enabledAlternateMediaOptionByType(MediaOptionType.Subtitle); + subtitlePersistentId = subtitleAltOption === null || subtitleAltOption === void 0 ? void 0 : subtitleAltOption.persistentID; + audioPersistentId = persistentId; + } + else { + const audioAltOption = rootQuery.enabledAlternateMediaOptionByType(MediaOptionType.AltAudio); + audioPersistentId = audioAltOption === null || audioAltOption === void 0 ? void 0 : audioAltOption.persistentID; + subtitlePersistentId = persistentId; + } + const expectedEnabledMediaOptionMask = rootQuery.getEnabledMediaOptionMask(); + expectedEnabledMediaOptionMask[mediaOptionType] = isFiniteNumber(persistentId) && persistentId >= 0 ? true : false; + return variant + ? this.getBestMediaOptionTupleFromVariantAndPersistentId(rootQuery, variant, audioPersistentId, subtitlePersistentId, expectedEnabledMediaOptionMask, undefined, sdrOnly, shouldSwitchHost, false) + : [NoMediaOption, NoMediaOption, NoMediaOption]; + } + getFallbackMediaOptionTupleFromMediaOptionId(rootQuery, mediaOptionType, mediaOptionId, backingMediaOptionId, sdrOnly = false, shouldSwitchHosts = false, shouldDownswitch = false) { + const excludingOptions = backingMediaOptionId ? [backingMediaOptionId] : [mediaOptionId]; // always exclude mediaOptionId or backingMediaOptionId from result list since we are looking for its fallback + const currentVariantId = rootQuery.enabledMediaOptionIdByType(MediaOptionType.Variant); // getBestMediaOptionTupleFromVariantAndPersistentId will skip currentVariantId if it's mediaOptionId (excluded above) + const variant = rootQuery.variantMediaOptionById(currentVariantId); + const altAudio = mediaOptionType === MediaOptionType.AltAudio + ? rootQuery.alternateMediaOptionById(MediaOptionType.AltAudio, mediaOptionId) + : rootQuery.enabledAlternateMediaOptionByType(MediaOptionType.AltAudio); + const audioPersistentId = altAudio === null || altAudio === void 0 ? void 0 : altAudio.persistentID; + const subtitle = mediaOptionType === MediaOptionType.Subtitle + ? rootQuery.alternateMediaOptionById(MediaOptionType.Subtitle, mediaOptionId) + : rootQuery.enabledAlternateMediaOptionByType(MediaOptionType.Subtitle); + const subtitlePersistentId = subtitle === null || subtitle === void 0 ? void 0 : subtitle.persistentID; + return variant + ? this.getBestMediaOptionTupleFromVariantAndPersistentId(rootQuery, variant, audioPersistentId, subtitlePersistentId, undefined, excludingOptions, sdrOnly, shouldSwitchHosts, shouldDownswitch) + : [NoMediaOption, NoMediaOption, NoMediaOption]; + } + hasFallbackMediaOptionTuple(rootQuery, mediaOptionType, fromId, shouldSwitchHosts) { + const fromOption = rootQuery.mediaOptionListQueries[mediaOptionType].mediaOptionFromId(fromId); + return rootQuery.isValidMediaOptionTuple(this.getFallbackMediaOptionTupleFromMediaOptionId(rootQuery, mediaOptionType, fromId, fromOption.backingMediaOptionId, false, shouldSwitchHosts)); + } + setLegacyAlternateMediaOption(rootQuery, itemId, mediaOptionType, persistentId, switchContext = undefined) { + const currentVariantId = rootQuery.enabledMediaOptionIdByType(MediaOptionType.Variant); + const currentVariant = rootQuery.variantMediaOptionById(currentVariantId); + const newAltOption = rootQuery.getLegacyMatchingAlternateWithPersistentId(mediaOptionType, persistentId, currentVariant); + if (newAltOption) { + this.setEnabledMediaOptionByType(itemId, mediaOptionType, newAltOption, true, switchContext); + } + else { + this.logger.warn(`${MediaOptionNames[mediaOptionType]} can't find matching mediaOption for persistent id ${persistentId}`); + } + } + setEnabledMediaOptionTupleWithMatchedGroups(itemId, mediaOptionType, persistentId, switchContext = undefined) { + const rootQuery = createRootPlaylistQuery(itemId); + const newOptions = this.pickMediaOptionTupleByPersistentId(rootQuery, mediaOptionType, persistentId); + if (!rootQuery.isValidMediaOptionTuple(newOptions)) { + // can't find matching variant and alternates combo, + // just switch the alternate media option of mediaOptionType, potentially ignore preferred host and group + return this.setLegacyAlternateMediaOption(rootQuery, itemId, mediaOptionType, persistentId, switchContext); + } + applyTransaction(() => { + this.setEnabledMediaOptionByType(itemId, mediaOptionType, newOptions[mediaOptionType], true, switchContext); + if (newOptions[MediaOptionType.Variant].mediaOptionId !== rootQuery.enabledMediaOptionIdByType(MediaOptionType.Variant)) { + this.setPreferredHost(itemId, getHostName(newOptions[MediaOptionType.Variant].url)); + } + // always update the variant mediaOption even if it has not changed. This will allow AVPipe's enabledMediaOptionSwitchForType call + // to reflect the latest fromId->toId mediaOption switching. + this.setEnabledMediaOptionByType(itemId, MediaOptionType.Variant, newOptions[MediaOptionType.Variant]); + const otherType = mediaOptionType === MediaOptionType.AltAudio ? MediaOptionType.Subtitle : MediaOptionType.AltAudio; + if (newOptions[otherType].mediaOptionId !== rootQuery.enabledMediaOptionIdByType(otherType)) { + this.setEnabledMediaOptionByType(itemId, otherType, newOptions[otherType], false); // don't override switchContext of the other alternate + } + }); + } + canSwitchToSDR(rootQuery, mediaOptionId, shouldSwitchHosts, shouldDownswitch = false) { + const mediaOption = rootQuery.mediaOptionListQueries[MediaOptionType.Variant].mediaOptionFromId(mediaOptionId); + const newMediaOptions = this.getFallbackMediaOptionTupleFromMediaOptionId(rootQuery, MediaOptionType.Variant, mediaOptionId, mediaOption.backingMediaOptionId, true, shouldSwitchHosts, shouldDownswitch); + return rootQuery.isValidMediaOptionTuple(newMediaOptions); + } + getBestMediaOptionTupleFromVariantAndPersistentId(rootQuery, currentVariant, audioPersistentId, subtitlePersistentId, expectedEnabledMediaOptionMask, excludingOptions, sdrOnly, shouldSwitchHosts, shouldDownswitch) { + let alternates; + const fallbackVariantList = rootQuery.mediaOptionListQueries[MediaOptionType.Variant].listFallbackVariants(currentVariant.mediaOptionId, sdrOnly, shouldSwitchHosts, shouldDownswitch, excludingOptions); + let preferredFallback = [NoMediaOption, NoMediaOption, NoMediaOption]; + for (let i = 0; i < fallbackVariantList.length; ++i) { + const nextVariant = fallbackVariantList[i]; + alternates = rootQuery.matchAlternates(nextVariant, audioPersistentId, subtitlePersistentId, excludingOptions); + if (rootQuery.isValidMediaOptionTuple([nextVariant, ...alternates], expectedEnabledMediaOptionMask)) { + preferredFallback = [nextVariant, ...alternates]; + break; // found variant with valid alternates + } + } + return preferredFallback; + } + } + const rootPlaylistStore = new RootPlaylistStore(); + new QueryEntity(rootPlaylistStore); + let rootService = null; // To be instantiated in rootPlaylistService + /*********************************************** + * Static helper functions that specifically use the above singletons + */ + function createRootPlaylistQuery(itemId) { + return new RootPlaylistQuery(rootPlaylistStore, itemId); + } + /** + * @returns The global instance of RootPlaylistService that operates on global RootPlaylistStore + */ + function rootPlaylistService(logger) { + rootService = new RootPlaylistService(rootPlaylistStore, logger); + return rootService; + } + /** + * Choose the audio track given the selection group and options + * @param groupId GroupID from variant + * @param audioMediaSelectionGroup + * @param audioAlternateOptions + */ + const getAutoAudio = (persistentId, groupId, audioMediaSelectionGroup, audioAlternateOptions) => { + if (audioMediaSelectionGroup) { + let audioMediaOption; + if (isFiniteNumber(persistentId)) { + audioMediaOption = audioMediaSelectionGroup.MediaSelectionGroupOptions.find(function (mediaOption) { + return mediaOption.MediaSelectionOptionsPersistentID === persistentId; + }); + } + else { + audioMediaOption = audioMediaSelectionGroup.MediaSelectionGroupOptions.find(function (mediaOption) { + return mediaOption.MediaSelectionOptionsIsDefault; + }); + } + if (!audioMediaOption) { + audioMediaOption = audioMediaSelectionGroup.MediaSelectionGroupOptions[0]; + getLogger().info(`no default audio: pick the first persistentId ${audioMediaOption.MediaSelectionOptionsPersistentID}`); + } + const audioTrackInfo = audioAlternateOptions.find((audioTrack) => { + return (!groupId || audioTrack.groupId === groupId) && audioTrack.persistentID === (audioMediaOption === null || audioMediaOption === void 0 ? void 0 : audioMediaOption.MediaSelectionOptionsPersistentID); + }); + return audioTrackInfo; + } + }; + function logPlatformCapabilitiesFilterResults(hdrMediaOptions, sdrMediaOptions, el, config, displaySupportsHdr, logger) { + hdrMediaOptions.concat(sdrMediaOptions); + const devicePixelRatio = typeof window === 'object' && window.devicePixelRatio ? window.devicePixelRatio : 1; + const displaySize = config.useViewportSizeForLevelCap && el ? { width: el.clientWidth * devicePixelRatio, height: el.clientHeight * devicePixelRatio } : undefined; + logger.info(loggerName, `valid media options: hdr=${hdrMediaOptions.length} sdr=${sdrMediaOptions.length}, supportsHdr=${displaySupportsHdr}, displaySize=${displaySize ? JSON.stringify(displaySize) : null}`); + } + const updateBasedOnPlatformCapabilities = (loadRootMediaOptionsResult, platformInfo, config, displaySupportsHdr, logger) => { + const { rootMediaOptionsTuple, sessionKeys } = loadRootMediaOptionsResult; + const variantMediaOptions = Array.from(rootMediaOptionsTuple[MediaOptionType.Variant]); + const audioAlternateOptions = Array.from(rootMediaOptionsTuple[MediaOptionType.AltAudio]); + let videoCodecFound = false; + let audioCodecFound = false; + // regroup redundant variants together + let regroupedVariantMediaOptions = variantMediaOptions.map((mediaOption) => { + videoCodecFound = videoCodecFound || Boolean(mediaOption.videoCodec); + audioCodecFound = audioCodecFound || Boolean(mediaOption.audioCodec) || Boolean(mediaOption.audioGroupId); + return mediaOption; + }); + // remove audio-only media options if we also have media options with audio+video codecs signaled + if (videoCodecFound && audioCodecFound) { + regroupedVariantMediaOptions = regroupedVariantMediaOptions.filter(({ videoCodec }) => Boolean(videoCodec)); + } + logger.info(loggerName, `playlist has ${regroupedVariantMediaOptions.length} variantMediaOptions`); + return filterByPlatformCapabilities(variantMediaOptions, audioAlternateOptions, sessionKeys, platformInfo, config, logger).pipe(map(({ hdrMediaOptions, sdrMediaOptions }) => { + const allMediaOptions = hdrMediaOptions.concat(sdrMediaOptions); + const hasHdrMediaOptions = hdrMediaOptions.length > 0; + logPlatformCapabilitiesFilterResults(hdrMediaOptions, sdrMediaOptions, undefined, config, displaySupportsHdr, logger); + return makeRootPlaylistEntity(loadRootMediaOptionsResult, allMediaOptions, hasHdrMediaOptions, displaySupportsHdr); + })); + }; + /** + * Choose the audio track given the selection group and options + * @param subtitleMediaSelectionGroup + * @param subtitleAlternateOptions + */ + const getAutoSubtitle = (persistentId, closedCaptionGroup, subtitleGroup, subtitleMediaSelectionGroup, subtitleAlternateOptions, logger) => { + if (subtitleMediaSelectionGroup) { + let subtitleMediaOption; + if (isFiniteNumber(persistentId)) { + subtitleMediaOption = subtitleMediaSelectionGroup.MediaSelectionGroupOptions.find(function (mediaOption) { + return mediaOption.MediaSelectionOptionsPersistentID === persistentId; + }); + } + else { + subtitleMediaOption = subtitleMediaSelectionGroup.MediaSelectionGroupOptions.find(function (mediaOption) { + return mediaOption.MediaSelectionOptionsIsDefault; + }); + } + let subtitleTrackInfo; + if (subtitleMediaOption) { + subtitleTrackInfo = subtitleAlternateOptions.find((subtitleTrack) => { + if (subtitleTrack.mediaType === MediaTypeFourCC.CLOSEDCAPTION) { + return (!closedCaptionGroup || subtitleTrack.groupId === closedCaptionGroup) && subtitleTrack.persistentID === subtitleMediaOption.MediaSelectionOptionsPersistentID; + } + else if (subtitleTrack.mediaType === MediaTypeFourCC.SUBTITLE) { + return (!subtitleGroup || subtitleTrack.groupId === subtitleGroup) && subtitleTrack.persistentID === subtitleMediaOption.MediaSelectionOptionsPersistentID; + } + else { + logger.warn(loggerName, `subtitle media option has unknown type ${subtitleTrack.mediaType}`); + } + }); + } + return subtitleTrackInfo; + } + }; + function makeRootPlaylistEntity(loadRootMediaOptionsResult, variantMediaOptions, hasHdrLevels, preferHDR) { + var _a; + const { itemId, itemStartOffset, rootMediaOptionsTuple, audioMediaSelectionGroup, subtitleMediaSelectionGroup } = loadRootMediaOptionsResult; + const audioAlternateOptions = Array.from(rootMediaOptionsTuple[MediaOptionType.AltAudio]); + const subtitleAlternateOptions = Array.from(rootMediaOptionsTuple[MediaOptionType.Subtitle]); + const hasScore = variantMediaOptions.every((x) => isFiniteNumber(x.score)); + const hasIframeLevels = variantMediaOptions.some((x) => isIframeLevel(x)); + const sortedVariantMediaOptions = sortVariants(variantMediaOptions, hasScore); + const compatibleVariants = null; + const compatibleAudioAlternates = null; + const baseUrl = loadRootMediaOptionsResult.baseUrl; + const initPathwayID = (_a = loadRootMediaOptionsResult.contentSteeringOption) === null || _a === void 0 ? void 0 : _a.initPathwayID; + const sessionData = loadRootMediaOptionsResult.sessionData; + const rootPlaylistEntity = { + itemId, + baseUrl, + mediaOptionListTuple: [ + { + mediaOptions: sortedVariantMediaOptions, + hasHdrLevels, + hasIframeLevels, + hasScore, + preferHDR, + compatibleIds: compatibleVariants, + penaltyBoxQueue: [], + removed: [], + currentPathwayID: initPathwayID, + }, + { + mediaOptions: audioAlternateOptions, + compatibleIds: compatibleAudioAlternates, + penaltyBoxQueue: [], + removed: [], + }, + { + mediaOptions: subtitleAlternateOptions, + penaltyBoxQueue: [], + removed: [], + }, + ], + audioMediaSelectionGroup, + subtitleMediaSelectionGroup, + enabledMediaOptionKeys: [NoMediaOption, NoMediaOption, NoMediaOption], + mediaOptionSwitchContexts: [null, null, null], + anchorTime: { pos: 0 }, + discoSeqNum: NaN, + pendingSeek: undefined, + itemStartOffset, + initPtsRecord: {}, + contentSteeringOption: loadRootMediaOptionsResult.contentSteeringOption, + masterVariableList: loadRootMediaOptionsResult.masterVariableList, + loadStats: loadRootMediaOptionsResult.stats, + isMediaPlaylist: loadRootMediaOptionsResult.isMediaPlaylist, + abrStatus: { + fragDownloadSlow: false, + fragDownloadTooSlow: false, + nextMinAutoOptionId: NoMediaOption.mediaOptionId, + nextMaxAutoOptionId: NoMediaOption.mediaOptionId, + highBWTrigger: NaN, + }, + sessionData, + }; + return rootPlaylistEntity; + } + function selectStartingVariant(rootPlaylistEntity, logger, bandwidthEstimate, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate) { + const mediaOptionListInfo = rootPlaylistEntity.mediaOptionListTuple[MediaOptionType.Variant]; + const filteredVariants = applyFilters(mediaOptionListInfo.mediaOptions, VariantMediaOptionListQuery.kAllowFilters, Object.assign(Object.assign({}, mediaOptionListInfo), { compatibleIds: null })); + const preferredVariants = getPreferredList(mediaOptionListInfo.preferredHost, filteredVariants); + const firstVariant = chooseFirstMediaOption(preferredVariants, firstMediaOptionSelectionMetrics, mediaOptionListInfo.hasScore, logger, bandwidthEstimate, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate); + return { firstVariant, filteredVariants, preferredVariants }; + } + function replaceClosedCaptionInMediaOptionListIfNecessary(variant, closedCaption, subtitleMediaOptions, logger) { + if ((closedCaption === null || closedCaption === void 0 ? void 0 : closedCaption.mediaType) === MediaTypeFourCC.CLOSEDCAPTION) { + const forcedMediaOption = AlternateMediaOptionListQuery.pairForcedSubtitleMediaOptionWithClosedCaptionInList(variant.subtitleGroupId, closedCaption, subtitleMediaOptions); + if (forcedMediaOption) { + closedCaption = Object.assign(Object.assign({}, closedCaption), { url: forcedMediaOption.url, backingMediaOptionId: forcedMediaOption.mediaOptionId }); + const newSubtitles = subtitleMediaOptions.map((option) => { + if (option.mediaOptionId === closedCaption.mediaOptionId) { + logger.info(`[subtitle] use closedCaption ${JSON.stringify(closedCaption)}`); + return closedCaption; + } + return option; + }); + return newSubtitles; + } + } + return subtitleMediaOptions; // no change + } + /** + * Modify rootPlaylistEntity (in-place), updating media options list and selecting a new set of enabled media options + */ + function updateRootPlaylistEntityWithEnabledMediaOptionKeys(rootPlaylistEntity, existingSelection, logger, bandwidthEstimate, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate) { + var _a, _b; + const itemId = rootPlaylistEntity.itemId; + const mediaOptionListInfo = rootPlaylistEntity.mediaOptionListTuple[MediaOptionType.Variant]; + const audioOptionListInfo = rootPlaylistEntity.mediaOptionListTuple[MediaOptionType.AltAudio]; + const subtitleOptionListInfo = rootPlaylistEntity.mediaOptionListTuple[MediaOptionType.Subtitle]; + const filteredAudio = applyFilters(audioOptionListInfo.mediaOptions, AlternateMediaOptionListQuery.kAllowFilters, audioOptionListInfo); + const filteredSubtitles = applyFilters(subtitleOptionListInfo.mediaOptions, AlternateMediaOptionListQuery.kAllowFilters, subtitleOptionListInfo); + let { firstVariant, filteredVariants } = selectStartingVariant(rootPlaylistEntity, logger, bandwidthEstimate, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate); + if (!firstVariant) { + const preferHDR = mediaOptionListInfo.preferHDR; + if (preferHDR) { + mediaOptionListInfo.preferHDR = false; + } + else { + mediaOptionListInfo.preferHDR = mediaOptionListInfo.hasHdrLevels; + } + if (mediaOptionListInfo.preferHDR !== preferHDR) { + logger.warn(`No valid first variant found, toggling hdr preference=${preferHDR}->${mediaOptionListInfo.preferHDR}`); + ({ firstVariant, filteredVariants } = selectStartingVariant(rootPlaylistEntity, logger, bandwidthEstimate, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate)); + } + } + if (!firstVariant) { + throw new ExceptionError(true, 'No valid first variant found', ErrorResponses.NoValidAlternates); + } + const preferredHost = getHostName(firstVariant.url); + logger.info(loggerName, `First level ${itemId}: ${mediaOptionToString(firstVariant)}`); + const variantId = (_a = firstVariant === null || firstVariant === void 0 ? void 0 : firstVariant.mediaOptionId) !== null && _a !== void 0 ? _a : null; + const variantKey = { itemId, mediaOptionId: variantId }; + const altAudioId = (filteredAudio === null || filteredAudio === void 0 ? void 0 : filteredAudio.length) + ? (_b = getAutoAudio(existingSelection === null || existingSelection === void 0 ? void 0 : existingSelection.audioPersistentId, firstVariant.audioGroupId, rootPlaylistEntity.audioMediaSelectionGroup, filteredAudio)) === null || _b === void 0 ? void 0 : _b.mediaOptionId + : null; + const altAudioKey = altAudioId ? { itemId, mediaOptionId: altAudioId } : NoMediaOption; + const firstSubtitle = getAutoSubtitle(existingSelection === null || existingSelection === void 0 ? void 0 : existingSelection.subtitlePersistentId, firstVariant.closedcaption, firstVariant.subtitleGroupId, rootPlaylistEntity.subtitleMediaSelectionGroup, filteredSubtitles, logger); + const subtitleId = (filteredSubtitles === null || filteredSubtitles === void 0 ? void 0 : filteredSubtitles.length) ? firstSubtitle === null || firstSubtitle === void 0 ? void 0 : firstSubtitle.mediaOptionId : null; + const subtitleKey = subtitleId ? { itemId, mediaOptionId: subtitleId, mediaOptionType: MediaOptionType.Subtitle } : NoMediaOption; + const { mediaOptions, audioGroups, subtitleGroups, closedCaptionGroups } = filterMediaOptionsBasedOnFirstMediaOptions(filteredVariants, firstVariant); + const compatibleVariants = Array.from(mediaOptions).map((mediaOption) => mediaOption.mediaOptionId); + const currentPathwayID = firstVariant.pathwayID; + const updatedMediaOptionListInfo = Object.assign(Object.assign({}, mediaOptionListInfo), { compatibleIds: compatibleVariants, preferredHost, + currentPathwayID }); + // audio + const compatibleAudioAlternates = []; + const audioMediaOptions = audioOptionListInfo.mediaOptions; + const filteredAudioResults = audioMediaOptions.reduce((prev, cur) => { + if (audioGroups.has(cur.groupId)) { + prev.persistentIds.add(cur.persistentID); + compatibleAudioAlternates.push(cur.mediaOptionId); + prev.filteredAudioMediaOptions.push(cur); + if (!prev.altAudio) { + prev.altAudio = !!cur.url; + } + } + return prev; + }, { filteredAudioMediaOptions: [], persistentIds: new Set(), altAudio: false }); + const updatedAudioOptionListInfo = Object.assign(Object.assign({}, audioOptionListInfo), { compatibleIds: compatibleAudioAlternates }); + let audioMediaSelectionGroup = rootPlaylistEntity.audioMediaSelectionGroup; + const audioMediaSelectionOptions = audioMediaSelectionGroup === null || audioMediaSelectionGroup === void 0 ? void 0 : audioMediaSelectionGroup.MediaSelectionGroupOptions; + if (audioMediaSelectionOptions) { + const filteredAudioSelectionOptions = audioMediaSelectionOptions.reduce((prev, cur) => { + if (filteredAudioResults.persistentIds.has(cur.MediaSelectionOptionsPersistentID)) { + prev.push(cur); + } + return prev; + }, new Array()); + audioMediaSelectionGroup = Object.assign(Object.assign({}, audioMediaSelectionGroup), { MediaSelectionGroupOptions: filteredAudioSelectionOptions }); + } + // subtitle + subtitleOptionListInfo.mediaOptions = replaceClosedCaptionInMediaOptionListIfNecessary(firstVariant, firstSubtitle, subtitleOptionListInfo.mediaOptions, logger); + const subtitleMediaOptions = subtitleOptionListInfo.mediaOptions; + const filteredSubtitleResults = subtitleMediaOptions.reduce((prev, cur) => { + if (subtitleGroups.has(cur.groupId)) { + // do NOT re-assign track id. They must be unique for the entire collection of subtitle and caption tracks + // cur.id = prev.filteredSubtitleMediaOptions.length; + prev.persistentIds.add(cur.persistentID); + prev.filteredSubtitleMediaOptions.push(cur); + } + return prev; + }, { filteredSubtitleMediaOptions: [], persistentIds: new Set() }); + let subtitleMediaSelectionGroup = rootPlaylistEntity.subtitleMediaSelectionGroup; + const subtitleMediaSelectionOptions = subtitleMediaSelectionGroup === null || subtitleMediaSelectionGroup === void 0 ? void 0 : subtitleMediaSelectionGroup.MediaSelectionGroupOptions; + if (subtitleMediaSelectionOptions) { + const filteredSubtitleSelectionOptions = subtitleMediaSelectionOptions.reduce((prev, cur) => { + if (filteredSubtitleResults.persistentIds.has(cur.MediaSelectionOptionsPersistentID)) { + prev.push(cur); + } + return prev; + }, new Array()); + subtitleMediaSelectionGroup = Object.assign(Object.assign({}, subtitleMediaSelectionGroup), { MediaSelectionGroupOptions: filteredSubtitleSelectionOptions }); + } + const mediaOptionListTuple = [updatedMediaOptionListInfo, updatedAudioOptionListInfo, subtitleOptionListInfo]; + // Find the highest codec string per codec family + let highestVideoCodec = new Map(); + const hlsConf = getCurrentConfig(); + if (hlsConf.useHighestVideoCodecPrivate) { + highestVideoCodec = updatedMediaOptionListInfo === null || updatedMediaOptionListInfo === void 0 ? void 0 : updatedMediaOptionListInfo.mediaOptions.reduce((codecStringMap, mediaOption) => { + const codecStringList = mediaOption.videoCodecList; + if (codecStringList) { + for (const codec of codecStringList) { + const key = getVideoCodecFamily(codec); + const currentCodec = codecStringMap.get(key); + if (MediaUtil.isHigherCodecByFamily(currentCodec, codec)) { + codecStringMap.set(key, codec); + } + } + } + return codecStringMap; + }, highestVideoCodec); + } + if (highestVideoCodec.size) { + highestVideoCodec.forEach((value, key) => logger.info(`override for ${key} family with codec ${value}`)); + } + const abrStatus = { + fragDownloadSlow: false, + fragDownloadTooSlow: false, + nextMinAutoOptionId: NoMediaOption.mediaOptionId, + nextMaxAutoOptionId: NoMediaOption.mediaOptionId, + highBWTrigger: getLowestSuperiorBW(variantKey.mediaOptionId, updatedMediaOptionListInfo.mediaOptions), + }; + return Object.assign(Object.assign({}, rootPlaylistEntity), { enabledMediaOptionKeys: [variantKey, altAudioKey, subtitleKey], mediaOptionListTuple, + audioMediaSelectionGroup, + abrStatus, + highestVideoCodec }); + } + /** + * @brief Load the root playlist + * + */ + const retrieveRootMediaOptions = (loadPolicy, rootPlaylistService, config, platformQuery, existingSelection, statsService, playerEvents) => (source) => { + return source.pipe(tag('retrieveRootMediaOptions.input'), switchMap((item) => { + var _a, _b; + if (!item) + return EMPTY; + const { itemId, platformInfo } = item; + const rootPlaylistQuery = createRootPlaylistQuery(itemId); + const { logger } = rootPlaylistService; + if (rootPlaylistQuery.hasEntity(itemId)) { + // these needs to handle failures and stuff + return of(rootPlaylistQuery); + } + rootPlaylistStore.setLoading(true); + const tManifestLoadStart = performance.now(); + return loadRootMediaOptions(item, loadPolicy, logger, config, statsService, (_b = (_a = globalHlsService()) === null || _a === void 0 ? void 0 : _a.getQuery()) === null || _b === void 0 ? void 0 : _b.extendMaxTTFB).pipe(tap((loadRootMediaOptionsResult) => playerEvents.triggerManifestLoaded(loadRootMediaOptionsResult)), tap(({ initialDetails, sessionData, stats }) => { + if (initialDetails) { + archiveMediaOptionDetails(initialDetails, stats, true); + } + }), withLatestFrom(platformQuery.displaySupportsHdr$), switchMap(([loadRootMediaOptionsResult, displaySupportsHdr]) => updateBasedOnPlatformCapabilities(loadRootMediaOptionsResult, platformInfo, config, displaySupportsHdr, logger)), map((rootPlaylistEntity) => { + rootPlaylistService.rootPlaylistEntity = initializeRootEntity(item, rootPlaylistEntity, existingSelection, tManifestLoadStart, config, logger); + return rootPlaylistQuery; + }), addLoadErrorHandlingPolicy(itemId, null, getLoadConfig(item, loadPolicy), 0, false, rootPlaylistQuery, rootPlaylistService, statsService), finalize$1(() => { + rootPlaylistStore.setLoading(false); + })); + }), tag('retrieveRootMediaOptions.emit')); + }; + /** + * @brief Choose first media options and update initial seek position + * @returns updated RootPlaylistEntity with selected media options and seek time + */ + function initializeRootEntity(item, rootPlaylistEntity, existingSelection, tManifestLoadStart, config, logger) { + const { itemId, initialSeekTime, itemStartOffset } = item; + const statsQuery = createStatsQuery(itemId); + const bandwidthEstimate = config.enableAdaptiveStartup ? statsQuery.getBandwidthEstimate(config, item.serviceName) : undefined; + const playlistEstimate = config.enableAdaptiveStartup ? statsQuery.getPlaylistEstimate(config, item.serviceName) : undefined; + const fragEstimate = config.enableAdaptiveStartup ? statsQuery.getFragEstimate(config, item.serviceName) : undefined; + const bufferEstimate = config.enableAdaptiveStartup ? statsQuery.getBufferEstimate(config, item.serviceName) : undefined; + const timeElapsed = performance.now() - tManifestLoadStart; + let targetStartupMs; + if (config.targetStartupMs > timeElapsed) { + targetStartupMs = config.targetStartupMs - timeElapsed; + } + else { + /* In some edge cases, such as network is too slow/lossy, it is seen that the manifestLoad takes longer resulting in timeElapsed go above config.targetStartupMs. + To avoid targetStartupMs being negative, fall back to config.targetStartupMs */ + targetStartupMs = config.targetStartupMs; + logger.warn(`Manifest load took ${timeElapsed}ms and exceeds targetStartupMs: ${config.targetStartupMs}; resetting targetStartupMs to ${config.targetStartupMs}`); + } + const adaptiveStartupConfig = config.enableAdaptiveStartup + ? { + targetDuration: fragEstimate.maxDurationSec || config.defaultTargetDuration, + targetStartupMs: targetStartupMs, + } + : undefined; + const updatedRootPlaylistEntity = updateRootPlaylistEntityWithEnabledMediaOptionKeys(rootPlaylistEntity, existingSelection, logger, bandwidthEstimate, adaptiveStartupConfig, playlistEstimate, fragEstimate, bufferEstimate); + // Initialize pendingSeek if this is not a preloading item. don't update anchorTime yet. + updatedRootPlaylistEntity.pendingSeek = initialSeekTime; + return updatedRootPlaylistEntity; + } + + /** + * @brief Create all adapters for hls. they will live while media is attached + */ + function makeAdapters(itemRemove$, ksService, config, platformQuery, eventEmitter, rtcService, logger) { + return (source$) => { + const keySystemAdapter = makeKeySystemService(ksService, source$, itemRemove$, config, platformQuery, eventEmitter, rtcService, logger); + const legibleSystemAdapter = makeLegibleService(source$, config, eventEmitter, logger); + return combineLatest([keySystemAdapter, legibleSystemAdapter, source$]).pipe(map(([keySystemAdapter, legibleSystemAdapter, mediaSink]) => { + return { keySystemAdapter, legibleSystemAdapter, mediaSink }; + })); + }; + } + function makeActiveItemAdapters(config, logger, rootPlaylistService, platformQuery, statsService, playerEvents, rpcClients) { + return (activeItem$) => activeItem$.pipe(filterNullOrUndefined(), switchMap((activeItem) => { + logger.info(`active item changed ${activeItem === null || activeItem === void 0 ? void 0 : activeItem.itemId}`); + if (!activeItem) { + return of(null); + } + const rootPlaylistQuery$ = of(activeItem).pipe(retrieveRootMediaOptions(config.manifestLoadPolicy, rootPlaylistService, config, platformQuery, null, statsService, playerEvents)); + return combineLatest([rootPlaylistQuery$, createMediaParser(config, logger, rpcClients.mux), createIframeMachine(config.trickPlaybackConfig, logger)]).pipe(map(([rootPlaylistQuery, mediaParser, iframeMachine]) => ({ rootPlaylistQuery, mediaParser, iframeMachine }))); + })); + } + /** + * @brief Observable for updating platform information + */ + function platformUpdater(platformService) { + return listenForHdrUpdates(platformService).pipe(switchMapTo(EMPTY)); + } + /** + * @brief Top level class that handles setup of playback for a queue of items + */ + class Hls$1 extends HlsEventEmitter { + constructor(userConfig = {}, logger) { + var _a; + super(); + this.destroy$ = new Subject(); + this.mediaElement$ = new BehaviorSubject(null); + this.publicQueriesInternal$ = new BehaviorSubject(null); + this.mediaElementAdapter = null; + this.rpcService = null; + this.rpcClients = null; + this.platformService = platformService(); + this.keySystemAdapter = null; + this.legibleSystemAdapter = null; + this.sessionID = guid(); + this.statsService = statsServiceSingleton(); + this.gaplessCapable = true; + this.teardownWG$ = new WaitGroup(); + this.itemQueue = new ItemQueue(); + // Sanitize userConfig + if ((userConfig.liveSyncDurationCount || userConfig.liveMaxLatencyDurationCount) && (userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)) { + throw new Error('Illegal hls.js config: don\'t mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration'); + } + const config = Object.assign(Object.assign({}, hlsDefaultConfig), userConfig); + if (config.maxRequiredStartDuration < config.minRequiredStartDuration || config.minRequiredStartDuration < 0) { + throw new Error('Illegal config: bad maxRequiredStartDuration or minRequiredStartDuration'); + } + this.hlsConfig = config; + // setup the redact url function + setupRedactUrl(config.buildType); + // set up logger + const { sessionID: sessionId } = this; + let logLevel = 'silent'; + if (userConfig.debug) { + logLevel = config.debugLevel; + } + // rdar://88106960 (HLS with buildType "production" much slower than with buildType "development") + // setting autoFreeze to true follows default behavior with immer 8.0+ + setAutoFreeze_1(true); + { + // for unit tests only + this.logger = logger; + } + this.logger = + (_a = this.logger) !== null && _a !== void 0 ? _a : setupLoggerSingleton(sessionId, 'hls', getLoggerConfig({ + sendLogs: config.sendLogs || (config.log ? LoggerExternals$1().logStore : null), + level: logLevel === 'log' ? 'debug' : logLevel, + consoleOverride: typeof userConfig.debug !== 'boolean' ? userConfig.debug : undefined, + buildType: config.buildType, + })); + this.logger.qe({ critical: true, name: 'playerVersion', data: { version: Hls$1.version } }); + { + this.logger.qe({ critical: true, name: 'playerCommit', data: { hash: '0c65bb95' } }); + this.logger.qe({ critical: true, name: 'playerBranch', data: { hash: 'release/2.162' } }); + initialize(userConfig.socketurl, userConfig.socketid); + setHls(this); + setLogger(this.logger); + } + this.hlsConfig.audioPrimingDelay = 0; + this.logger.info(`force audioPrimingDelay to ${this.hlsConfig.audioPrimingDelay}`); + this.rootPlaylistService = rootPlaylistService(this.logger); + this.customUrlLoader = getCustomUrlLoader(); + this.sessionDataLoader = new SessionDataLoader(config, fromXMLHttpRequest, this.customUrlLoader.load, this.logger); + const liveMaxLatencyDurationCount = config.liveMaxLatencyDurationCount; + const liveSyncDurationCount = config.liveSyncDurationCount; + if (isFiniteNumber(liveMaxLatencyDurationCount) && isFiniteNumber(liveSyncDurationCount) && liveMaxLatencyDurationCount <= liveSyncDurationCount) { + throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be gt "liveSyncDurationCount"'); + } + if (isFiniteNumber(config.liveMaxLatencyDuration) && (config.liveMaxLatencyDuration <= config.liveSyncDuration || !isFiniteNumber(config.liveSyncDuration))) { + throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be gt "liveSyncDuration"'); + } + const hlsService = globalHlsService(); + this.logger.info(`start Hls sessionId:${sessionId}`); + hlsService.setHlsEntity({ id: sessionId, config }); + const mediaLibService = mediaLibraryService(); + const platformQuery = createPlatformQuery(); + const ksService = keySystemService(); + this.accessLogInstance = new AccessLog(this, sessionId); + this.rtcService = new RTCService(this, config, this.accessLogInstance, this.logger); + this.playerEvents = new HlsPlayerEvents(this, this.logger, this.rtcService); + // Update platform information (display, etc) + const updatePlatformInfo$ = platformUpdater(this.platformService); + const mediaSink$ = this.mediaElement$.pipe(mediaElementServiceEpic(() => { + return new MediaSource(); + }, config, this, this.logger, this.teardownWG$, this.rtcService), share() // Prevent us from making a new MediaSink for each thing subscribing to this observable + ); + // Things that depend on active item. + const handleActiveItemChange$ = this.itemQueue.activeItemById$.pipe(switchMap((entity) => { + if (!entity) { + return EMPTY; + } + return statsProcessor(config, this.statsService, entity, this.logger); + })); + this.rpcService = (() => { + let create = null; + if (config.createRPCService != null) { + create = createRPCServiceWithFallbacks(config.createRPCService, createRPCInlineService); + } + if (hasUMDWorker() && config.enableWorker) { + if (create == null) { + create = createRPCService; + } + } + if (create == null) { + create = createRPCInlineService; + } + return create(this.logger); + })(); + this.rpcClients = createRPCClients(this.rpcService); + // Emits when we have all context available for pipeline. + // CAUTION: every time this observable emits it will cause pipelineCtxHandlers$ to + // teardown and re-subscribe (if applicable). Only emit on /changes/ to pipeline context! + const pipelineContext$ = combineLatest([ + // Emit when root playlist loaded + this.itemQueue.activeItemById$.pipe(makeActiveItemAdapters(config, this.logger, this.rootPlaylistService, platformQuery, this.statsService, this.playerEvents, this.rpcClients), tap((rootAdapters) => { + const rootPlaylistQuery = rootAdapters === null || rootAdapters === void 0 ? void 0 : rootAdapters.rootPlaylistQuery; + // prep publicQueriesInternal to serve api calls, that + // depend on rootPlaylistQuery exclusively. + this.publicQueriesInternal$.next([rootPlaylistQuery, null]); + this.iframeMachine = rootAdapters === null || rootAdapters === void 0 ? void 0 : rootAdapters.iframeMachine; + if (rootPlaylistQuery) { + // this is delayed trigger for manifest parsed, but doing it before + // setting in publicQueriesInternal will make some api such as + // sessionData non serviceable. + this.playerEvents.triggerManifestParsed(rootPlaylistQuery); + } + })), + // Emit when all adapters created / destroyed + mediaSink$.pipe(makeAdapters(this.itemQueue.removedItems$, ksService, config, platformQuery, this, this.rtcService, this.logger), tap(({ keySystemAdapter, legibleSystemAdapter, mediaSink }) => { + this.keySystemAdapter = keySystemAdapter; + this.legibleSystemAdapter = legibleSystemAdapter; + this.mediaElementAdapter = mediaSink; + })), + ]).pipe(map(([activeItemAdapters, mediaAdapters]) => { + const { keySystemAdapter, legibleSystemAdapter, mediaSink } = mediaAdapters; + if (!activeItemAdapters || !keySystemAdapter || !legibleSystemAdapter || !mediaSink) { + return null; // Stop everything depending on this + } + const { rootPlaylistQuery, iframeMachine, mediaParser } = activeItemAdapters; + return { + logger: this.logger, + config, + platformService: this.platformService, + statsService: this.statsService, + rtcService: this.rtcService, + rpcClients: this.rpcClients, + rootPlaylistService: this.rootPlaylistService, + rootPlaylistQuery, + mediaLibraryService: mediaLibService, + keySystemAdapter, + legibleSystemAdapter, + mediaSink, + mediaParser, + iframeMachine, + customUrlLoader: this.customUrlLoader, + gaplessInstance: this, + }; + }), share()); + // Things that depend on PipelineContext. This means rootPlaylist loaded && mediaSink != null + const pipelineCtxHandlers$ = pipelineContext$.pipe(switchMap((context) => { + if (!context) { + return EMPTY; + } + const { rootPlaylistQuery, mediaSink, mediaLibraryService } = context; + this.publicQueriesInternal$.next([rootPlaylistQuery, mediaSink.mediaQuery]); + const mediaQuery = mediaSink.mediaQuery; + const mediaFragmentPipeline$ = of(context).pipe(mediaFragmentPipelineEpic()); + // TODO: Fix rdar://81171922 and re-enable content steering + // const loadSteeringManifest$ = of(rootPlaylistQuery).pipe(contentSteeringEpic(context.rootPlaylistService, config, config.steeringManifestLoadPolicy, this.logger)); + // AirPlay may pick the alternates before hls.rootQuery and rootQuery.rootPlaylistEntity are ready. + // Delay these early selections when hls is instantiating. + // We should not need these early alternate selections for gapless items since + // (i) follow-up items should inherit the existing language selections. + // (ii) hls.rootQuery has been setup and rootQuery.rootPlaylistEntity lifecycle (for the next itemId) is controlled by hls.js + const earlySelection$ = rootPlaylistQuery.rootPlaylistEntity$.pipe(filterNullOrUndefined(), take(1), tap(() => { + this.commitEarlySelection(context.logger); // earliest point where this._activeRootQuery is valid + })); + const loadSessionData$ = waitFor(combineLatest([mediaQuery.haveEnough$, rootPlaylistQuery.sessionData$]), ([haveEnough]) => haveEnough === true, 1).pipe(switchMap(([, sessionData]) => { + return this.sessionDataLoader.loadSessionData(sessionData); + }), tap((sessionData) => { + this.rootPlaylistService.setSessionData(rootPlaylistQuery.itemId, sessionData); + }), catchError((err) => { + this.logger.error(err.message); + return EMPTY; + })); + // Is this a good place to put this.... + const statsQuery = createStatsQuery(rootPlaylistQuery.itemId); + const haveEnough$ = checkForHaveEnough(config, this.logger, rootPlaylistQuery, mediaSink, mediaLibraryService, statsQuery); + const ended$ = checkForEndOfStream(context); + const iframeAutoPause$ = checkForIframeAutoPause(context); + const iframePrefetch$ = checkForIframePrefetch(context); + const playbackInfo$ = waitFor(combineQueries([mediaQuery.gotPlaying$, mediaQuery.gotLoadStart$, mediaQuery.readyState$]), ([playing, loadStart, readyState]) => playing === true || loadStart === true || readyState >= 1).pipe(switchMap(() => mediaQuery.ended$), + // on ended, fire once and stop, else every 1s + switchMap((ended) => timer(0, ended ? undefined : 1000)), tap(() => { + this.playbackInfo(config, mediaQuery); + })); + const updatePlayingId$ = mediaQuery.timeupdate$.pipe(map((pos) => { + if (this.inGaplessMode && this.isPreloading) { + if (isFiniteNumber(this.loadingItem.itemStartOffset) && pos >= this.loadingItem.itemStartOffset) { + const prevItemId = this.itemQueue.playingItem.itemId; + const nextItemId = this.itemQueue.loadingItem.itemId; + const nextStartTime = this.loadingItem.itemStartOffset; + const nextDuration = mediaQuery.msDuration - this.loadingItem.itemStartOffset; + const data = { prevItemId, nextItemId, nextStartTime, nextDuration }; + this.logger.info(`[gapless] Item transitioned prevItem: ${prevItemId}, nextItem: ${nextItemId}, nextStartTime: ${nextStartTime}, nextDuration: ${nextDuration}`); + this.itemQueue.updatePlayingItemId(); + this.trigger(HlsEvent.ITEM_TRANSITIONED, data); + this.rtcService.itemTransitioned(prevItemId, nextItemId); + this.logger.qe({ critical: true, name: 'gapless', data: { transitionFrom: prevItemId, transitionTo: nextItemId } }); + } + } + })); + const liveSeekableRange$ = this.updateLiveSeekableRange(rootPlaylistQuery, mediaSink); + const observableList = [ + earlySelection$, + mediaFragmentPipeline$, + // TODO: Fix rdar://81171922 and re-enable content steering + // loadSteeringManifest$, // Load steering manifest + loadSessionData$, + haveEnough$, + playbackInfo$, + iframeAutoPause$, + iframePrefetch$, + updatePlayingId$, + liveSeekableRange$, + ended$, // Check for end of stream + ]; + if (config.enablePerformanceLogging) { + const tlog = this.logger.child({ name: 'timing' }); + // Log currently loading fragments + const inFlightFrags$ = zip(...AVMediaOptionTypes.map((type) => { + return rootPlaylistQuery.getInFlightFragByType$(type).pipe(distinctUntilChanged((a, b) => (a === null || a === void 0 ? void 0 : a.state) === (b === null || b === void 0 ? void 0 : b.state)), filterNullOrUndefined(), withLatestFrom(mediaQuery.bufferedRangeTuple$), tap(([inFlightInfo, bufferedRanges]) => { + const payload = Object.assign(Object.assign({}, inFlightInfo), { event: 'fragment', name: MediaOptionNames[type], buffered: undefined }); + if (inFlightInfo.state === 'appended') { + payload.buffered = bufferedRanges; + } + tlog.info(JSON.stringify(payload)); + }), catchError(() => EMPTY)); + })); + // Log whenever detailsLoading changes + const loadingPlaylist$ = zip(...MediaOptionTypes.map((type) => { + return rootPlaylistQuery.enabledMediaOptionByType$(type).pipe(switchMap((option) => { + if ((option === null || option === void 0 ? void 0 : option.url) == null || !isEnabledMediaOption(option)) { + return EMPTY; + } + const query = createMediaLibraryQuery(option); + return query.mediaOptionDetailsEntity$.pipe(filterNullOrUndefined(), map((entity) => ({ entity, option })), distinctUntilChanged((a, b) => (a === null || a === void 0 ? void 0 : a.entity.detailsLoading) === (b === null || b === void 0 ? void 0 : b.entity.detailsLoading)), tap(({ entity, option }) => { + if (!entity || !option) { + return; + } + const payload = { + event: 'playlist', + name: MediaOptionNames[option.mediaOptionType], + mediaOptionId: option.mediaOptionId, + state: entity.detailsLoading ? 'loading' : 'loaded', + }; + tlog.info(JSON.stringify(payload)); + })); + }), catchError(() => EMPTY)); + })); + observableList.push(inFlightFrags$, loadingPlaylist$); + } + return merge(...observableList); + })); + const handleQueueItemRemove$ = this.itemQueue.removedItems$.pipe(withTransaction((ids) => { + // Cleanup everything with lifecycle tied to QueueItem + mediaLibraryRemove(ids); + this.rootPlaylistService.removeItems(ids); + })); + const userSeek$ = hlsService.getQuery().userSeek$.pipe(userSeekEpic(this.itemQueue, this.rootPlaylistService)); + const gotNewHls$ = hlsService + .getQuery() + .selectEntityAction(EntityActions.Add) + .pipe(tap(() => { + this.logger.warn(`new Hls instance added while old one still active sessionId:${sessionId}`); + })); + merge( + // Keep mediaSink alive even if other stuff errors out just in case we need play to end of buffer handling + // PipelineContext is subscribed to by pipelineCtxHandler$ so errors should be caught in other observable. + mediaSink$.pipe(catchError(() => EMPTY)), merge(updatePlatformInfo$, userSeek$, handleActiveItemChange$, handleQueueItemRemove$, pipelineCtxHandlers$, this.teardownWG$).pipe(catchError((err) => this._handleError(err)))) + .pipe(finalize$1(() => { + var _a, _b; + try { + this.logger.info(`finalize Hls sessionId:${sessionId}`); + // Clean up everything with lifecycle tied to HLS instance: + this.detachMedia(); + this.trigger(HlsEvent.DESTROYING); + this.playerEvents.destroy(); + (_a = this.accessLogInstance) === null || _a === void 0 ? void 0 : _a.destroy(); + (_b = this.rtcService) === null || _b === void 0 ? void 0 : _b.destroy(); + mediaLibraryClear(); + this.rootPlaylistService.removeAll(); + this.itemQueue.clearQueue(); + hlsService.removeEntity(this.sessionID); + } + catch (err) { + this.logger.error(`Got error in finalize ${err.message}`); + } + }), takeUntil(race(this.destroy$, gotNewHls$))) + .subscribe(); + } + get publicQueries$() { + return this.publicQueriesInternal$.pipe(filter((queries) => Boolean(queries) && Boolean(queries[0]) && Boolean(queries[1]))); + } + get _activeRootQuery() { + var _a; + const publicQueries = this.publicQueriesInternal$.value; + return (_a = publicQueries === null || publicQueries === void 0 ? void 0 : publicQueries[0]) !== null && _a !== void 0 ? _a : null; + } + get _mediaElementQuery() { + var _a; + const publicQueries = this.publicQueriesInternal$.value; + return (_a = publicQueries === null || publicQueries === void 0 ? void 0 : publicQueries[1]) !== null && _a !== void 0 ? _a : null; + } + static get version() { + return '2.162.2'; + } + /** + * @type {HlsEvent} + */ + static get Events() { + return HlsEvent; + } + get Events() { + return Hls$1.Events; + } + /** + * @type {HlsConfig} + */ + static get DefaultConfig() { + return deepCpy(hlsDefaultConfig); + } + get DefaultConfig() { + return Hls$1.DefaultConfig; + } + /** + * @type {boolean} + */ + static isSupported() { + return isSupported(); + } + commitEarlySelection(logger) { + const audioPersistentId = this.itemQueue.earlyAudioSelection; + if (isFiniteNumber(audioPersistentId)) { + logger.info(`use early audio selection ${audioPersistentId}`); + this.audioSelectedPersistentID = audioPersistentId; + this.itemQueue.earlyAudioSelection = null; + } + const subtitlePersistentId = this.itemQueue.earlySubtitleSelection; + if (isFiniteNumber(subtitlePersistentId)) { + logger.info(`use early subtitle selection ${subtitlePersistentId}`); + this.subtitleSelectedPersistentID = subtitlePersistentId; + this.itemQueue.earlySubtitleSelection = null; + } + } + /** + * Global error handler. If we've reached here, we've exhausted all other options + * and it's likely fatal. All errors should bubble up from the observables in the pipe + */ + _handleError(err) { + var _a; + try { + let errMessage = err.message; + { + errMessage += `\n${err.stack}`; + } + this.logger.error(`Got unhandled or fatal error ${errMessage}`, err); + (_a = this.rtcService) === null || _a === void 0 ? void 0 : _a.handleError(err); + let newError; + if (err instanceof HlsError) { + newError = err; + } + else { + newError = new ExceptionError(true, err.message, ErrorResponses.InternalError); + } + if (newError.fatal && this.isPreloading) { + this.logger.warn('Fatal error seen while preloading, calling dequeueSource'); + this.dequeueSource('FatalErrorWhileLoading'); + } + const shouldEscalateError = newError.fatal; + if (shouldEscalateError) { + this.logger.qe({ critical: true, name: 'fatalError', data: { msg: newError.message, stack: newError.stack } }); + let triggerError$ = VOID; + if (this.mediaElementAdapter) { + const mediaQuery = this.mediaElementAdapter.mediaQuery; + const combinedBuffer = mediaQuery.getCombinedBufferInfo(mediaQuery.currentTime, 0); + if ((combinedBuffer === null || combinedBuffer === void 0 ? void 0 : combinedBuffer.len) > 0) { + this.logger.info(`playing to buffer end @${combinedBuffer.end}, pos=${mediaQuery.currentTime}`); + triggerError$ = waitFor(this.mediaElementAdapter.mediaQuery.stallInfo$, (stallInfo) => stallInfo != null).pipe(map(() => { })); + } + } + return triggerError$.pipe(switchMap(() => { + this.trigger(HlsEvent.ERROR, newError); + return EMPTY; + })); + } + this.trigger(HlsEvent.ERROR, newError); + } + catch (thrownError) { + this.logger.error(`Error thrown inside _handleError ${thrownError.message}`, thrownError); + throw thrownError; + } + return EMPTY; + } + updateLiveSeekableRange(rootPlaylistQuery, mediaSink) { + return rootPlaylistQuery.enabledMediaOptionByType$(MediaOptionType.Variant).pipe(switchMap((mediaOption) => { + const query = createMediaLibraryQuery(mediaOption); + let lastUpdate = 0; + // filter on entities that have loading set to false and have stats filled and have been updated + return query.mediaOptionDetailsEntity$.pipe(filterNullOrUndefined(), filter((entity) => { + var _a; + const retValue = entity.stats !== null && entity.detailsLoading === false && entity.lastUpdateMillis > lastUpdate; + lastUpdate = (_a = entity.lastUpdateMillis) !== null && _a !== void 0 ? _a : 0; + return retValue; + })); + }), switchMap((mediaDetailsEntity) => { + if (mediaDetailsEntity.unchangedCount === 0) { + if (mediaDetailsEntity.mediaOptionDetails.liveOrEvent) { + mediaSink.updateLiveSeekableRange(mediaDetailsEntity.mediaOptionDetails); + } + else { + mediaSink.clearLiveSeekableRange(); + } + } + return EMPTY; + })); + } + playbackInfo(config, mediaQuery) { + var _a; + const video = this.mediaElement$.getValue(); + if (!video) { + return; + } + const readyToPlay = video.readyState >= video.HAVE_FUTURE_DATA; + const playbackLikelyToKeepUp = mediaQuery.haveEnough && readyToPlay; + const playbackInfo = { + readyToPlay: readyToPlay, + playbackLikelyToKeepUp: playbackLikelyToKeepUp, + rate: video.playbackRate, + paused: video.paused, + position: video.currentTime, + duration: video.duration, + seekableTimeRanges: MediaElementHelper.timeRangeToArray(video.seekable), + loadedTimeRanges: MediaElementHelper.timeRangeToArray(video.buffered), + }; + let droppedVideoFrames = 0, decodedFrameCount = 0; + if (MediaElementHelper.isHtmlVideoElement(video)) { + const videoPlaybackQuality = video.getVideoPlaybackQuality; + if (videoPlaybackQuality && typeof videoPlaybackQuality === typeof Function) { + const videoQuality = video.getVideoPlaybackQuality(); + droppedVideoFrames = playbackInfo.droppedVideoFrames = videoQuality.droppedVideoFrames; + playbackInfo.corruptedVideoFrames = videoQuality.corruptedVideoFrames; + playbackInfo.totalVideoFrames = videoQuality.totalVideoFrames; + decodedFrameCount = playbackInfo.totalVideoFrames - droppedVideoFrames; + } + } + else if (MediaElementHelper.isWebkitMediaElement(video)) { + droppedVideoFrames = playbackInfo.droppedVideoFrames = video.webkitDroppedFrameCount; + decodedFrameCount = playbackInfo.decodedFrameCount = video.webkitDecodedFrameCount; + } + if (config.enablePerformanceLogging) { + this.logger.qe({ critical: true, name: 'playbackInfo', data: playbackInfo }); + const [variantBufferInfo, altAudioBufferInfo] = mediaQuery.getCombinedMediaSourceBufferInfo(config.maxBufferHole); + this.logger.qe({ critical: true, name: 'bufferInfo', data: { variant: variantBufferInfo, altAudio: altAudioBufferInfo } }); + } + (_a = this.rtcService) === null || _a === void 0 ? void 0 : _a.handlePlaybackInfo(droppedVideoFrames, decodedFrameCount); + } + get currentItem() { + if (this.isPreloading) { + this.logger.info('Currently preloading, returning playing item'); + return this.playingItem; + } + return this.itemQueue.activeItem; + } + get realCurrentTime() { + var _a, _b; + const mediaQuery = this._mediaElementQuery; + if (!mediaQuery) { + return NaN; + } + // during trick-playback + if ((_a = this.iframeMachine) === null || _a === void 0 ? void 0 : _a.isStarted) { + const duration = mediaQuery.mediaElementDuration; + const ifct = this.iframeMachine.iframeClockTimeSeconds; + return ifct > duration ? duration : ifct; + } + // After trick-play is stopped, before seek to postFlushSeek, return postFlushSeek time until postFlushSeek is set to undefined when seek complete + let currentTime = isFiniteNumber(mediaQuery.postFlushSeek) ? mediaQuery.postFlushSeek : mediaQuery.currentTime; + // if the playing item is a finite number, remove it from real current time. + // No need to check inGaplessMode mode here because there situations where inGaplessMode is false + // but itemStartOffset is finite e.g. when playing the last song in an album + if (isFiniteNumber(currentTime) && isFiniteNumber((_b = this.playingItem) === null || _b === void 0 ? void 0 : _b.itemStartOffset)) { + currentTime -= this.playingItem.itemStartOffset; + } + return currentTime; + } + set realCurrentTime(value) { + var _a; + this.logger.info(`[seek] realCurrentTime ${value}`); + // if the playing item is a finite number, add it to the value. + // No need to check inGaplessMode mode here because there situations where inGaplessMode is false + // but itemStartOffset is finite e.g. when playing the last song in an album + if (isFiniteNumber((_a = this.playingItem) === null || _a === void 0 ? void 0 : _a.itemStartOffset)) { + value += this.playingItem.itemStartOffset; + } + this.seekTo = value; + } + get bufferedDuration() { + var _a; + const mediaQuery = this._mediaElementQuery; + return (_a = mediaQuery === null || mediaQuery === void 0 ? void 0 : mediaQuery.getBufferedDuration()) !== null && _a !== void 0 ? _a : 0; + } + get sessionData() { + const rootPlaylistQuery = this._activeRootQuery; + return rootPlaylistQuery === null || rootPlaylistQuery === void 0 ? void 0 : rootPlaylistQuery.sessionData; + } + get supportedFrameRates() { + const { enabled } = this.hlsConfig.trickPlaybackConfig; + const supported = [0, 1]; + const rootQuery = this._activeRootQuery; + const libQuery = mediaLibraryService().getQuery(); + if (enabled && rootQuery && libQuery.getEntity(rootQuery.itemId)) { + const isLive = libQuery.getEntity(rootQuery.itemId).liveOrEvent; + if (isLive === false) { + supported.push(8, 24, 48, 96); + } + } + return supported; + } + loadSource(url, itemOptions, initialSeekTime) { + var _a, _b, _c, _d; + // If Playready key system is being requested and if it's not enabled for the browser, reject loading the source + if (this.config.keySystemPreference === 'playready' && !this.config.enablePlayReadyKeySystem) { + throw new ExceptionError(true, 'Playready key system is not supported now', ErrorResponses.UnsupportedKeySystemError); + } + if (!url || !url.trim().length) { + throw new ExceptionError(true, 'Empty loadSource url', ErrorResponses.EmptyLoadSourceError); + } + // inherit protocol from href + url = URLToolkit$1.buildAbsoluteURL(window.location.href, url, { alwaysNormalize: true }); + const logItemOptions = itemOptions && + Object.keys(itemOptions) + .filter((key) => ['itemId', 'streamID'].indexOf(key) >= 0) + .reduce((newObj, key) => (key in itemOptions ? Object.assign(newObj, { [key]: itemOptions[key] }) : newObj), {}); + this.logger.qe({ critical: true, name: 'loadSource', data: { url: redactUrl(url), itemOptions: logItemOptions, initialSeekTime } }); + if ((_a = itemOptions === null || itemOptions === void 0 ? void 0 : itemOptions.appData) === null || _a === void 0 ? void 0 : _a.reportingAgent) { + this.reportingAgent = itemOptions.appData.reportingAgent; + } + if (itemOptions === null || itemOptions === void 0 ? void 0 : itemOptions.userInfo) { + this.userInfo = itemOptions.userInfo; + } + (_b = this.accessLogInstance) === null || _b === void 0 ? void 0 : _b.setupReporter(itemOptions.appData); + if (itemOptions === null || itemOptions === void 0 ? void 0 : itemOptions.platformInfo) { + this.platformService.updatePlatformInfo(itemOptions.platformInfo); + } + // Append queries specific to certain hosts + if (urlNeedsUpdate(url, this.config.enableQueryParamsForITunes)) { + const queryParameters = { + language: itemOptions.language, + dsid: itemOptions.dsid, + subs: itemOptions.subs, + }; + url = updateUrlWithQueryStrings(url, itemOptions === null || itemOptions === void 0 ? void 0 : itemOptions.platformInfo, queryParameters); + itemOptions.inheritQuery = false; // don't inherit queries to subsequent requests + } + this.itemQueue.setQueueItem(`item:${(_c = itemOptions === null || itemOptions === void 0 ? void 0 : itemOptions.itemId) !== null && _c !== void 0 ? _c : guid()}`, url, initialSeekTime, itemOptions === null || itemOptions === void 0 ? void 0 : itemOptions.platformInfo, (_d = itemOptions === null || itemOptions === void 0 ? void 0 : itemOptions.appData) === null || _d === void 0 ? void 0 : _d.serviceName); + // clear this out. only makes sense for first asset + globalHlsService().setStartTime(undefined); + } + queueSource(url, itemOptions, initialSeekTime) { + var _a, _b, _c, _d; + this.logger.qe({ critical: true, name: 'queueSource', data: { url: redactUrl(url), itemOptions, initialSeekTime } }); + if (itemOptions === null || itemOptions === void 0 ? void 0 : itemOptions.userInfo) { + this.userInfo = itemOptions.userInfo; + } + const combinedBuffer = (_a = this._mediaElementQuery) === null || _a === void 0 ? void 0 : _a.getCombinedBufferInfo((_b = this._mediaElementQuery) === null || _b === void 0 ? void 0 : _b.currentTime, 0); + let duration = 0; + if (combinedBuffer) { + duration = combinedBuffer.end; + } + this.logger.info(`queueSource ${redactUrl(url)} initialSeekTime:${initialSeekTime === null || initialSeekTime === void 0 ? void 0 : initialSeekTime.toFixed(3)}`); + this.logger.qe({ critical: true, name: 'gapless', data: { itemLoadingAt: duration } }); + this.itemQueue.addQueueItem(`item:${(_c = itemOptions === null || itemOptions === void 0 ? void 0 : itemOptions.itemId) !== null && _c !== void 0 ? _c : guid()}`, url, initialSeekTime, itemOptions === null || itemOptions === void 0 ? void 0 : itemOptions.platformInfo, duration, (_d = itemOptions === null || itemOptions === void 0 ? void 0 : itemOptions.appData) === null || _d === void 0 ? void 0 : _d.serviceName); + } + dequeueSource(reason = 'ApplicationInitiated') { + // if we are not preloading and this is the first item and it's invalid format, disallow gapless + // but do not evict the item. It's probably a video + if (!this.isPreloading && reason === 'InvalidFormat' && this.isFirstItem) { + this.logger.error('First item has invalid format for gapless. Probably video. Disabling gapless.'); + this.gaplessCapable = false; + return; + } + if (!this.isPreloading) { + this.logger.warn(`Nothing to dequeue, no item is preloading dequeue reason: ${reason}`); + return; + } + this.logger.qe({ critical: true, name: 'gapless', data: { event: 'clean up current source' } }); + // Store loading item information + const itemToEvictUrl = this.loadingItem.url; + const itemToEvictId = this.loadingItem.itemId; + // flush all data from the loading item and set media sink duration + this.mediaElementAdapter.flushData(SourceBufferType.Variant, this.loadingItem.itemStartOffset, Infinity); + this.mediaElementAdapter.msDuration = this.loadingItem.itemStartOffset; + // clear loading item in queue and make playing item active. + this.itemQueue.resetLoadingItem(); + // Disallow gapless on invalidFormat and FatalErrorWhileLoading + if (reason === 'InvalidFormat' || reason === 'FatalErrorWhileLoading') { + // Disallow gapless + this.gaplessCapable = false; + } + // Send out item evicted event + this.triggerItemEvicted({ url: itemToEvictUrl, itemId: itemToEvictId }, reason); + } + triggerItemEvicted(itemToEvict, reason) { + if (itemToEvict === null) { + this.logger.error('dequeueSource called with no playing or loading item'); + return; + } + const data = { url: itemToEvict.url, evictedItemId: itemToEvict.itemId, reason }; + const loggableData = Object.assign(Object.assign({}, data), { url: redactUrl(itemToEvict.url) }); + this.logger.info('Item evicted evictedData: %o', loggableData); + this.trigger(HlsEvent.ITEM_EVICTED, data); + } + endSource() { + // When endSource is called, this hls instance is no longer gapless capable. + this.gaplessCapable = false; + this.logger.info('[gapless] end source'); + // if preloading clear buffers and remove loading item + if (this.isPreloading) { + this.logger.warn('EndSource called during preloading. Loading item will be removed'); + // flush all data from the loading item and set media sink duration + this.mediaElementAdapter.flushData(SourceBufferType.Variant, this.loadingItem.itemStartOffset, Infinity); + this.mediaElementAdapter.msDuration = this.loadingItem.itemStartOffset; + // clear loading item in queue and make playing item active. + this.itemQueue.resetLoadingItem(); + } + } + get inGaplessMode() { + //return this.config.gapless; //&& figure out who should know if gapless is allowed... item OR rootPlaylistService + // Don't call this.config because it does deepCpy and is deprecated + return getCurrentConfig().gapless && this.gaplessCapable; + } + get isPreloading() { + return this.itemQueue.isPreloading(); + } + get isFirstItem() { + return this.itemQueue.isFirstItem; + } + get loadingItem() { + return this.itemQueue.loadingItem; + } + get playingItem() { + return this.itemQueue.playingItem; + } + get url() { + if (this.playingItem) { + return this.playingItem.url; + } + if (this.loadingItem) { + return this.loadingItem.url; + } + return undefined; + } + destroy() { + const { logger } = this; + logger.info('destroy'); + // See finalize in Hls constructor for what happens on destroy$ + this.destroy$.next(); + if (this.rpcService != null) { + this.teardownWG$.add(); + this.rpcService.teardown((err) => { + if (err) { + logger.error('RPCService teardown error:', err); + } + this.teardownWG$.done(); + }); + this.rpcService = null; + } + if (this.iframeMachine != null) { + this.iframeMachine.destroy(); + this.iframeMachine = null; + } + return this.teardownWG$.toPromise(); + } + attachMedia(mediaElement) { + this.logger.info('attachMedia'); + this.trigger(HlsEvent.MEDIA_ATTACHING, { media: mediaElement }); + this.mediaElement$.next(mediaElement); + this.trigger(HlsEvent.MEDIA_ATTACHED, { media: mediaElement }); + } + detachMedia() { + var _a; + if (!this.mediaElement$.getValue()) { + this.logger.info('detachMedia called with no media element to detach'); + return; + } + this.logger.info('detachMedia'); + this.trigger(HlsEvent.MEDIA_DETACHING); + (_a = this.rtcService) === null || _a === void 0 ? void 0 : _a.detachMedia(); + if (this.iframeMachine != null) { + this.iframeMachine.stop(); + } + this.mediaElement$.next(null); + this.trigger(HlsEvent.MEDIA_DETACHED); + } + handleResolvedUri(originalURI, response) { + this.customUrlLoader.setCustomUrlResponse(originalURI, { uri: response.uri, response: response }); + } + get variantOptions$() { + return this.publicQueries$.pipe(switchMap((queries) => { + const [rootQuery, meQuery] = queries; + return combineLatest([rootQuery.preferredMediaOptions$, meQuery.desiredRate$]).pipe(map(([rootTuple]) => { + const iframeMode = meQuery.isIframeRate; + return rootTuple[MediaOptionType.Variant].filter((option) => { var _a; return ((_a = option.iframes) !== null && _a !== void 0 ? _a : false) === iframeMode; }).map((option) => option.mediaOptionId); + })); + })); + } + get altAudioOptions$() { + return this.publicQueries$.pipe(switchMap((queries) => { + const [rootQuery] = queries; + return of(rootQuery.audioMediaSelectionOptions); + })); + } + get subtitleOptions$() { + return this.publicQueries$.pipe(switchMap((queries) => { + const [rootQuery] = queries; + const disableOption = [ + { + MediaSelectionOptionsName: 'Disable subtitle', + MediaSelectionOptionsPersistentID: -1, + }, + ]; + return of(disableOption.concat(rootQuery.subtitleMediaSelectionOptions)); + })); + } + // Old public API used by webapp + get levels() { + var _a, _b; + return (_b = (_a = this._activeRootQuery) === null || _a === void 0 ? void 0 : _a.preferredMediaOptions[MediaOptionType.Variant]) !== null && _b !== void 0 ? _b : []; + } + // Old public API used by webapp + get audioTracks() { + var _a, _b; + return (_b = (_a = this._activeRootQuery) === null || _a === void 0 ? void 0 : _a.preferredMediaOptions[MediaOptionType.AltAudio]) !== null && _b !== void 0 ? _b : []; + } + get audioMediaOptions() { + var _a, _b; + return (_b = (_a = this._activeRootQuery) === null || _a === void 0 ? void 0 : _a.audioMediaSelectionOptions) !== null && _b !== void 0 ? _b : []; + } + get subtitleMediaOptions() { + var _a, _b; + return (_b = (_a = this._activeRootQuery) === null || _a === void 0 ? void 0 : _a.subtitleMediaSelectionOptions) !== null && _b !== void 0 ? _b : []; + } + /** + * @returns whether we are likely to keep up based on network and buffer + */ + get playbackLikelyToKeepUp() { + var _a, _b; + return (_b = (_a = this._mediaElementQuery) === null || _a === void 0 ? void 0 : _a.playbackLikelyToKeepUp) !== null && _b !== void 0 ? _b : false; + } + get duration$() { + return this.publicQueries$.pipe(switchMap((queries) => { + const [, mediaQuery] = queries; + return mediaQuery.mediaElementDuration$; + })); + } + get timeupdate$() { + return this.publicQueries$.pipe(switchMap((queries) => { + const [, mediaQuery] = queries; + return mediaQuery.timeupdate$; + })); + } + get playing$() { + return this.publicQueries$.pipe(switchMap((queries) => { + const [, mediaQuery] = queries; + return mediaQuery.gotPlaying$; + })); + } + get desiredRate$() { + return this.publicQueries$.pipe(switchMap((queries) => { + const [, mediaQuery] = queries; + return mediaQuery.desiredRate$; + })); + } + set desiredRate(desiredRate) { + if (desiredRate == null) { + return; + } + this.setRate(desiredRate); + } + get desiredRate() { + var _a, _b; + return (_b = (_a = this._mediaElementQuery) === null || _a === void 0 ? void 0 : _a.desiredRate) !== null && _b !== void 0 ? _b : 0; + } + get effectiveRate() { + var _a, _b; + return (_b = (_a = this._mediaElementQuery) === null || _a === void 0 ? void 0 : _a.effectiveRate) !== null && _b !== void 0 ? _b : 0; + } + get iframeMode() { + var _a, _b; + return (_b = (_a = this._mediaElementQuery) === null || _a === void 0 ? void 0 : _a.isIframeRate) !== null && _b !== void 0 ? _b : false; + } + get accessLog() { + return this.accessLogInstance && this._activeRootQuery ? this.accessLogInstance.getAccessLog(this._activeRootQuery.itemId) : []; + } + get errorLog() { + return this.accessLogInstance ? this.accessLogInstance.errorLog : []; + } + setRate(newRate) { + var _a; + const Errors = { UNABLE_TO_SWITCH: -1, ALREADY_IN_RATE: -2, UNSUPPORTED_RATE: -3 }; + const logger = this.logger.child({ name: 'iframes' }); + const oldRate = this.desiredRate; + if (newRate === oldRate) { + return Errors.ALREADY_IN_RATE; + } + logger.info(`setRate ${oldRate} -> ${newRate}`); + const mediaSink = this.mediaElementAdapter; + if (!mediaSink || isNaN(newRate)) { + logger.warn('unable to switch to rate, missing adapter or newRate isNaN'); + return Errors.UNABLE_TO_SWITCH; + } + newRate = Number(newRate); + const absRate = Math.abs(newRate); + const isSupportedRate = this.supportedFrameRates.some((rate) => rate === absRate); + if (!isSupportedRate) { + logger.warn(`unsupported rate(${newRate})`); + return Errors.UNSUPPORTED_RATE; + } + const iframeRate = isIframeRate(newRate); + const iframeMachine = this.iframeMachine; + if (iframeRate) { + const rootQuery = this._activeRootQuery; + if (!(rootQuery === null || rootQuery === void 0 ? void 0 : rootQuery.mediaOptionListQueries[MediaOptionType.Variant].hasIframes)) { + logger.warn('no iframe variants available'); + return Errors.UNABLE_TO_SWITCH; + } + mediaSink.postFlushSeek = null; + } + else if ((iframeMachine === null || iframeMachine === void 0 ? void 0 : iframeMachine.isStarted) && !isFiniteNumber((_a = mediaSink.mediaQuery) === null || _a === void 0 ? void 0 : _a.postFlushSeek)) { + iframeMachine.pause(); + logger.info(`resuming from trick-play postFlushSeek=${iframeMachine.iframeClockTimeSeconds}`); + mediaSink.postFlushSeek = iframeMachine.iframeClockTimeSeconds; + } + mediaSink.desiredRate = newRate; + return 0; + } + get sessionData$() { + return this.publicQueries$.pipe(switchMap(([rootPlaylistQuery]) => { + return rootPlaylistQuery.sessionData$; + })); + } + set skip(skip) { + this.logger.info(`skip=${skip}`); + if (this._mediaElementQuery) { + this.logger.info(`[seek] skip ${skip}`); + this.realCurrentTime = Math.max(0, this.realCurrentTime + skip); + } + } + // Helper function for seeking when in gapless mode + gaplessSeekTo(seekTo) { + // Adjust seekTo Value if needed + this.logger.debug(`seekTo before gapless adjust: ${seekTo}, startOffset: ${this.playingItem.itemStartOffset}`); + if (seekTo < this.playingItem.itemStartOffset) { + this.logger.warn(`[Gapless] Seeking past track boundary oldSeek=${seekTo}, adjustedSeek=${this.playingItem.itemStartOffset}`); + seekTo = this.playingItem.itemStartOffset; + } + if (this.isPreloading) { + if (seekTo > this.loadingItem.itemStartOffset) { + this.logger.warn(`[Gapless] Seeking past track boundary oldSeek=${seekTo}, adjustedSeek=${this.loadingItem.itemStartOffset}`); + seekTo = this.loadingItem.itemStartOffset; + } + const buf = this._mediaElementQuery.getBufferInfo(this._mediaElementQuery.currentTime, this.config.maxBufferHole); + this.logger.info('Hls seekTo during preloading currentTime:%d seekTo:%d Buf:%o', this.realCurrentTime, seekTo, buf); + if (seekTo < buf[0].buffered.start) { + this.dequeueSource('SeekToUnbufferedTimeRanges'); + } + } + globalHlsService().setUserSeek(seekTo); + } + isIframeInternalSeek(seekTo) { + var _a; + return seekTo === ((_a = this.iframeMachine) === null || _a === void 0 ? void 0 : _a.mediaRootTime); + } + set seekTo(seekValue) { + var _a; + const seekTo = Number(seekValue); + if (!isFiniteNumber(seekTo)) { + this.logger.error(`[seek] got invalid seek value ${seekValue}`); + return; + } + this.logger.info(`[seek] seekTo=${seekTo}`); + if (this.inGaplessMode) { + this.gaplessSeekTo(seekTo); + return; + } + // After trick-play is stopped, before seek to postFlushSeek, update the post flush resume time + const mediaSink = this.mediaElementAdapter; + if (mediaSink && isFiniteNumber((_a = mediaSink.mediaQuery) === null || _a === void 0 ? void 0 : _a.postFlushSeek) && (!mediaSink.mediaQuery.seekTo || this.isIframeInternalSeek(mediaSink.mediaQuery.seekTo.pos))) { + this.logger.info(`[seek] clearing seekTo ${JSON.stringify(mediaSink.mediaQuery.seekTo)} and resuming from trick-play postFlushSeek=${seekTo}`); + mediaSink.schedulePostFlushSeek(seekTo); + return; + } + // Store seek in userSeek to be processed by userSeekEpic + globalHlsService().setUserSeek(seekTo); + } + /** + * Seek to a date. Only valid for playlists with PROGRAM-DATE-TIME tags + * @param searchDate The date to seek to + */ + seekToDate(searchDate) { + // Note: Airplay allows seekToDate() to occur before loadSource() so we must store it at Hls level + this.logger.info(`[seek] seekToDate=${searchDate.toISOString()}`); + globalHlsService().setUserSeek(searchDate); + } + /** + * @returns a map of Date (ms) to time in media element (s) + */ + get availableProgramDateTime() { + return new Map(this._currentDateToMediaTimeTuple); + } + get _currentDateToMediaTimeTuple() { + var _a, _b; + if (!this._activeRootQuery) { + return []; + } + const curVariant = this._activeRootQuery.enabledMediaOptionKeys[MediaOptionType.Variant]; + if (!isEnabledMediaOption(curVariant)) { + return []; + } + const libQuery = mediaLibraryService().getQueryForOption(curVariant); + return (_b = (_a = libQuery.mediaOptionDetails) === null || _a === void 0 ? void 0 : _a.dateMediaTimePairs) !== null && _b !== void 0 ? _b : []; + } + /** + * Convert the playing time into equivalent DateTime + */ + get playingDate() { + return resolvePTSToDate(this._currentDateToMediaTimeTuple, this.realCurrentTime); + } + /** + * Manually set variantID. Only in development mode + */ + set variantId(variantId) { + { + const rootQuery = this._activeRootQuery; + if (!rootQuery) { + return; + } + this.logger.info(`variantId=${variantId}`); + const itemId = rootQuery.itemId; + const mediaOption = rootQuery.variantMediaOptionById(variantId); + this.rootPlaylistService.setManualMode(itemId, true); + this.rootPlaylistService.setEnabledMediaOptionByType(itemId, MediaOptionType.Variant, mediaOption); + } + } + set audioSelectedPersistentID(persistentID) { + const rootQuery = this._activeRootQuery; + const audioMediaOptions = rootQuery === null || rootQuery === void 0 ? void 0 : rootQuery.preferredMediaOptions[MediaOptionType.AltAudio]; + if (audioMediaOptions) { + const itemId = rootQuery.itemId; + if (persistentID === this.audioSelectedPersistentID) { + this.logger.info('skipping set audioSelectedPersistentID, id is same. PersistentID = %d', persistentID); + return; + } + this.rootPlaylistService.setEnabledMediaOptionTupleWithMatchedGroups(itemId, MediaOptionType.AltAudio, persistentID, { userInitiated: true }); + } + else { + // MatchPoint spams audioSelectedPersistentID(-1) during startup (when hls is not ready) + // hls 1.0 - 2.0 ignores them explicitly. + // Otherwise, they may override a valid selection. + if (!isFiniteNumber(persistentID) || persistentID < 0) { + this.logger.info(`ignore early invalid audio selection: ${persistentID}`); + return; + } + this.logger.warn(`[audio] no active item, defer audio track selection: persistentId ${persistentID}`); + this.itemQueue.earlyAudioSelection = persistentID; + } + } + get audioSelectedPersistentID() { + var _a; + if (this._activeRootQuery) { + return (_a = this._activeRootQuery.enabledAlternateMediaOptionByType(MediaOptionType.AltAudio)) === null || _a === void 0 ? void 0 : _a.persistentID; + } + else { + return this.itemQueue.earlyAudioSelection; + } + } + /** + * @param persistentID = the persistentId + */ + set subtitleSelectedPersistentID(persistentID) { + const rootQuery = this._activeRootQuery; + const subtitleMediaOptions = rootQuery === null || rootQuery === void 0 ? void 0 : rootQuery.preferredMediaOptions[MediaOptionType.Subtitle]; + if (subtitleMediaOptions) { + if (persistentID === this.subtitleSelectedPersistentID) { + this.logger.info('skipping set subtitleSelectedPersistentID, id is same. PersistentID = %d', persistentID); + return; + } + const itemId = rootQuery.itemId; + if (subtitleMediaOptions.length === 0 && (!isFiniteNumber(persistentID) || persistentID < 0)) { + this.logger.info(`ignore early invalid subtitle selection ${persistentID}; no subtitle media options yet`); + return; + } + this.logger.info(`subtitleSelectedPersistentID ${persistentID}`); + if (!isFiniteNumber(persistentID) || persistentID === -1) { + // Disable subtitles + this.rootPlaylistService.setEnabledMediaOptionByType(itemId, MediaOptionType.Subtitle, NoMediaOption); + } + else { + this.rootPlaylistService.setEnabledMediaOptionTupleWithMatchedGroups(itemId, MediaOptionType.Subtitle, persistentID); + } + } + else { + // MatchPoint spams subtitleSelectedPersistentID(-1) during startup (when hls is not ready) + // hls 1.0 - 2.0 ignores them explicitly. + // Otherwise, they may override a valid selection. + if (!isFiniteNumber(persistentID) || persistentID < 0) { + this.logger.info(`ignore early invalid subtitle selection ${persistentID}`); + return; + } + // cache valid selection + this.logger.warn(`[subtitle] no active item, defer subtitle track selection: persistentId ${persistentID}`); + this.itemQueue.earlySubtitleSelection = persistentID; + } + } + /** + * @returns the persistentID of the enabled subtitle + */ + get subtitleSelectedPersistentID() { + var _a; + if (this._activeRootQuery) { + return (_a = this._activeRootQuery.enabledAlternateMediaOptionByType(MediaOptionType.Subtitle)) === null || _a === void 0 ? void 0 : _a.persistentID; + } + else { + return this.itemQueue.earlySubtitleSelection; + } + } + /** + * Get array of selected tracks + */ + get selectedMediaArray() { + const rootQuery = this._activeRootQuery; + if (!rootQuery) { + return []; + } + const selectedMediaArray = []; + const audioMediaOption = rootQuery.enabledAlternateMediaOptionByType(MediaOptionType.AltAudio); + const subtitleMediaOption = rootQuery.enabledAlternateMediaOptionByType(MediaOptionType.Subtitle); + const audioSelectedMediaOption = audioMediaOption ? rootQuery.audioMediaSelectionOptions.find((option) => option.MediaSelectionOptionsPersistentID === audioMediaOption.persistentID) : undefined; + const subtitleSelectedMediaOption = subtitleMediaOption + ? rootQuery.subtitleMediaSelectionOptions.find((option) => option.MediaSelectionOptionsPersistentID === subtitleMediaOption.persistentID) + : undefined; + if (audioSelectedMediaOption) { + const selectedItem = { + MediaSelectionGroupMediaType: MediaTypeFourCC.AUDIO, + MediaSelectionOptionsPersistentID: audioSelectedMediaOption.MediaSelectionOptionsPersistentID, + }; + selectedMediaArray.push(selectedItem); + } + if (subtitleSelectedMediaOption) { + let showNonForcedSubtitles = Allowed.NO; + if (subtitleSelectedMediaOption.MediaSelectionOptionsDisplaysNonForcedSubtitles) { + showNonForcedSubtitles = subtitleSelectedMediaOption.MediaSelectionOptionsDisplaysNonForcedSubtitles; + } + const selectedItem = { + MediaSelectionGroupMediaType: MediaTypeFourCC.SUBTITLE, + MediaSelectionOptionsDisplaysNonForcedSubtitles: showNonForcedSubtitles, + MediaSelectionOptionsPersistentID: subtitleSelectedMediaOption.MediaSelectionOptionsPersistentID, + }; + selectedMediaArray.push(selectedItem); + } + this.logger.debug(`get selectedMediaArray: ${JSON.stringify(selectedMediaArray)}`); + return selectedMediaArray; + } + set selectedMediaArray(mediaArray) { + if (this._activeRootQuery) { + this.logger.debug(`selectedMediaArray=${JSON.stringify(mediaArray)}`); + mediaArray.forEach((selectedItem) => { + if (selectedItem.MediaSelectionGroupMediaType === MediaTypeFourCC.AUDIO || selectedItem.MediaSelectionOptionsMediaType === MediaTypeFourCC.AUDIO) { + this.audioSelectedPersistentID = selectedItem.MediaSelectionOptionsPersistentID; + } + else if (selectedItem.MediaSelectionGroupMediaType === MediaTypeFourCC.SUBTITLE || + selectedItem.MediaSelectionOptionsMediaType === MediaTypeFourCC.SUBTITLE || + selectedItem.MediaSelectionOptionsMediaType === MediaTypeFourCC.CLOSEDCAPTION) { + this.subtitleSelectedPersistentID = selectedItem.MediaSelectionOptionsPersistentID; + } + }); + } + else { + this.logger.warn('selectedMediaArray: no active item'); + } + } + getHTMLTextTrack(subtitleTrackId) { + return this.legibleSystemAdapter.getExistingHTMLTextTrackWithSubtitleTrackId(subtitleTrackId); + } + get keysystems() { + return this.keySystemAdapter.availableKeySystems; + } + setProtectionData(data) { + this.keySystemAdapter.initialize(data); + } + generateKeyRequest(keyuri, requestInfo) { + this.keySystemAdapter.generateRequest(keyuri, requestInfo); + this.rtcService.licenseChallengeReceived({ keyuri: keyuri }); + } + setLicenseResponse(keyuri, response) { + this.keySystemAdapter.setLicenseResponse(keyuri, response); + } + get bufferInfo$() { + return this.publicQueries$.pipe(switchMap((queries) => { + const [, mediaQuery] = queries; + const config = createHlsQuery().currentConfig; + return merge(mediaQuery.timeupdate$, mediaQuery.bufferedRangeTuple$).pipe(throttleTime(1000), map(() => { + const pos = mediaQuery.currentTime; + return { + combined: mediaQuery.getCombinedBufferInfo(pos, config.maxBufferHole), + sbTuple: mediaQuery.getBufferInfo(pos, config.maxBufferHole), + }; + })); + })); + } + bufferInfoByType$(type) { + return this.bufferInfo$.pipe(map((bufInfoTuple) => { + var _a; + return (_a = bufInfoTuple === null || bufInfoTuple === void 0 ? void 0 : bufInfoTuple.sbTuple) === null || _a === void 0 ? void 0 : _a[type]; + })); + } + /** + * DEPRECATED + */ + levelWithPersistentId(levelId) { + this.logger.warn('levelWithPersistentId is deprecated'); + } + /** + * DEPRECATED + */ + startLoad(startTimeSec) { + this.logger.warn('startLoad is deprecated'); + // Hack for Vuze. remove when they adopt initialSeekTime API. + if (isFiniteNumber(startTimeSec)) { + this.logger.warn(`[seek] Seeking to ${startTimeSec === null || startTimeSec === void 0 ? void 0 : startTimeSec.toFixed(3)} via deprecated "startLoad" method. Use loadSource(url, options, startTime) instead.`); + this.seekTo = startTimeSec; + } + } + /** + * DEPRECATED + */ + stopLoad() { } + /** + * DEPRECATED + * @returns current config + */ + get config() { + return Object.assign(Object.assign({}, deepCpy(getCurrentConfig())), { set startPosition(startTimeSec) { + // Hack for airplay. remove when they adopt initialSeekTime API + getLogger().warn(`Setting start position ${startTimeSec === null || startTimeSec === void 0 ? void 0 : startTimeSec.toFixed(3)} using deprecated method`); + globalHlsService().setStartTime(startTimeSec); + } }); + } + /** + * DEPRECATED + * @returns if media is attached + */ + get media() { + return this.mediaElement$.value != null; + } + /** + * DEPRECATED + */ + set subtitleDisplay(subtitleDisplay) { + this.logger.warn(`set subtitleDisplay ${subtitleDisplay} is deprecated`); + } + } + + var Hls = Hls$1; + + return Hls; + + })); + })(false); \ No newline at end of file diff --git a/src/renderer/main/vueapp.js b/src/renderer/main/vueapp.js index fb9f722a..6f2a677f 100644 --- a/src/renderer/main/vueapp.js +++ b/src/renderer/main/vueapp.js @@ -911,7 +911,7 @@ const app = new Vue({ } let type = (self.mk.nowPlayingItem != null) ? self.mk.nowPlayingItem["type"] ?? '' : ''; - if (type.includes("musicVideo") || type.includes("uploadedVideo") || type.includes("music-movie")) { + if (type.includes("musicVideo") || type.includes("uploadedVideo") || type.includes("music-movie") || (self.mk.nowPlayingItem?.type == "radioStation" & self.mk.nowPlayingItem?.attributes?.mediaKind == "video")) { document.getElementById("apple-music-video-container").style.display = "block"; document.body.setAttribute("video-playing", "true") // app.chrome.topChromeVisible = false