import { init_define_process_env_UNI_STAT_TITLE_JSON } from "./chunk-ON7YJ3LY.js"; // dep:hls_js init_define_process_env_UNI_STAT_TITLE_JSON(); // ../../../../../../../../Desktop/项目/直播/liveH5-v3/node_modules/hls.js/dist/hls.mjs init_define_process_env_UNI_STAT_TITLE_JSON(); var isFiniteNumber = Number.isFinite || function(value) { return typeof value === "number" && isFinite(value); }; var isSafeInteger = Number.isSafeInteger || function(value) { return typeof value === "number" && Math.abs(value) <= MAX_SAFE_INTEGER; }; var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; var ErrorTypes = function(ErrorTypes2) { ErrorTypes2["NETWORK_ERROR"] = "networkError"; ErrorTypes2["MEDIA_ERROR"] = "mediaError"; ErrorTypes2["KEY_SYSTEM_ERROR"] = "keySystemError"; ErrorTypes2["MUX_ERROR"] = "muxError"; ErrorTypes2["OTHER_ERROR"] = "otherError"; return ErrorTypes2; }({}); var ErrorDetails = function(ErrorDetails2) { ErrorDetails2["KEY_SYSTEM_NO_KEYS"] = "keySystemNoKeys"; ErrorDetails2["KEY_SYSTEM_NO_ACCESS"] = "keySystemNoAccess"; ErrorDetails2["KEY_SYSTEM_NO_SESSION"] = "keySystemNoSession"; ErrorDetails2["KEY_SYSTEM_NO_CONFIGURED_LICENSE"] = "keySystemNoConfiguredLicense"; ErrorDetails2["KEY_SYSTEM_LICENSE_REQUEST_FAILED"] = "keySystemLicenseRequestFailed"; ErrorDetails2["KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED"] = "keySystemServerCertificateRequestFailed"; ErrorDetails2["KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED"] = "keySystemServerCertificateUpdateFailed"; ErrorDetails2["KEY_SYSTEM_SESSION_UPDATE_FAILED"] = "keySystemSessionUpdateFailed"; ErrorDetails2["KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED"] = "keySystemStatusOutputRestricted"; ErrorDetails2["KEY_SYSTEM_STATUS_INTERNAL_ERROR"] = "keySystemStatusInternalError"; ErrorDetails2["KEY_SYSTEM_DESTROY_MEDIA_KEYS_ERROR"] = "keySystemDestroyMediaKeysError"; ErrorDetails2["KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR"] = "keySystemDestroyCloseSessionError"; ErrorDetails2["KEY_SYSTEM_DESTROY_REMOVE_SESSION_ERROR"] = "keySystemDestroyRemoveSessionError"; ErrorDetails2["MANIFEST_LOAD_ERROR"] = "manifestLoadError"; ErrorDetails2["MANIFEST_LOAD_TIMEOUT"] = "manifestLoadTimeOut"; ErrorDetails2["MANIFEST_PARSING_ERROR"] = "manifestParsingError"; ErrorDetails2["MANIFEST_INCOMPATIBLE_CODECS_ERROR"] = "manifestIncompatibleCodecsError"; ErrorDetails2["LEVEL_EMPTY_ERROR"] = "levelEmptyError"; ErrorDetails2["LEVEL_LOAD_ERROR"] = "levelLoadError"; ErrorDetails2["LEVEL_LOAD_TIMEOUT"] = "levelLoadTimeOut"; ErrorDetails2["LEVEL_PARSING_ERROR"] = "levelParsingError"; ErrorDetails2["LEVEL_SWITCH_ERROR"] = "levelSwitchError"; ErrorDetails2["AUDIO_TRACK_LOAD_ERROR"] = "audioTrackLoadError"; ErrorDetails2["AUDIO_TRACK_LOAD_TIMEOUT"] = "audioTrackLoadTimeOut"; ErrorDetails2["SUBTITLE_LOAD_ERROR"] = "subtitleTrackLoadError"; ErrorDetails2["SUBTITLE_TRACK_LOAD_TIMEOUT"] = "subtitleTrackLoadTimeOut"; ErrorDetails2["FRAG_LOAD_ERROR"] = "fragLoadError"; ErrorDetails2["FRAG_LOAD_TIMEOUT"] = "fragLoadTimeOut"; ErrorDetails2["FRAG_DECRYPT_ERROR"] = "fragDecryptError"; ErrorDetails2["FRAG_PARSING_ERROR"] = "fragParsingError"; ErrorDetails2["FRAG_GAP"] = "fragGap"; ErrorDetails2["REMUX_ALLOC_ERROR"] = "remuxAllocError"; ErrorDetails2["KEY_LOAD_ERROR"] = "keyLoadError"; ErrorDetails2["KEY_LOAD_TIMEOUT"] = "keyLoadTimeOut"; ErrorDetails2["BUFFER_ADD_CODEC_ERROR"] = "bufferAddCodecError"; ErrorDetails2["BUFFER_INCOMPATIBLE_CODECS_ERROR"] = "bufferIncompatibleCodecsError"; ErrorDetails2["BUFFER_APPEND_ERROR"] = "bufferAppendError"; ErrorDetails2["BUFFER_APPENDING_ERROR"] = "bufferAppendingError"; ErrorDetails2["BUFFER_STALLED_ERROR"] = "bufferStalledError"; ErrorDetails2["BUFFER_FULL_ERROR"] = "bufferFullError"; ErrorDetails2["BUFFER_SEEK_OVER_HOLE"] = "bufferSeekOverHole"; ErrorDetails2["BUFFER_NUDGE_ON_STALL"] = "bufferNudgeOnStall"; ErrorDetails2["ASSET_LIST_LOAD_ERROR"] = "assetListLoadError"; ErrorDetails2["ASSET_LIST_LOAD_TIMEOUT"] = "assetListLoadTimeout"; ErrorDetails2["ASSET_LIST_PARSING_ERROR"] = "assetListParsingError"; ErrorDetails2["INTERSTITIAL_ASSET_ITEM_ERROR"] = "interstitialAssetItemError"; ErrorDetails2["INTERNAL_EXCEPTION"] = "internalException"; ErrorDetails2["INTERNAL_ABORTED"] = "aborted"; ErrorDetails2["ATTACH_MEDIA_ERROR"] = "attachMediaError"; ErrorDetails2["UNKNOWN"] = "unknown"; return ErrorDetails2; }({}); var Events = function(Events2) { Events2["MEDIA_ATTACHING"] = "hlsMediaAttaching"; Events2["MEDIA_ATTACHED"] = "hlsMediaAttached"; Events2["MEDIA_DETACHING"] = "hlsMediaDetaching"; Events2["MEDIA_DETACHED"] = "hlsMediaDetached"; Events2["MEDIA_ENDED"] = "hlsMediaEnded"; Events2["STALL_RESOLVED"] = "hlsStallResolved"; Events2["BUFFER_RESET"] = "hlsBufferReset"; Events2["BUFFER_CODECS"] = "hlsBufferCodecs"; Events2["BUFFER_CREATED"] = "hlsBufferCreated"; Events2["BUFFER_APPENDING"] = "hlsBufferAppending"; Events2["BUFFER_APPENDED"] = "hlsBufferAppended"; Events2["BUFFER_EOS"] = "hlsBufferEos"; Events2["BUFFERED_TO_END"] = "hlsBufferedToEnd"; Events2["BUFFER_FLUSHING"] = "hlsBufferFlushing"; Events2["BUFFER_FLUSHED"] = "hlsBufferFlushed"; Events2["MANIFEST_LOADING"] = "hlsManifestLoading"; Events2["MANIFEST_LOADED"] = "hlsManifestLoaded"; Events2["MANIFEST_PARSED"] = "hlsManifestParsed"; Events2["LEVEL_SWITCHING"] = "hlsLevelSwitching"; Events2["LEVEL_SWITCHED"] = "hlsLevelSwitched"; Events2["LEVEL_LOADING"] = "hlsLevelLoading"; Events2["LEVEL_LOADED"] = "hlsLevelLoaded"; Events2["LEVEL_UPDATED"] = "hlsLevelUpdated"; Events2["LEVEL_PTS_UPDATED"] = "hlsLevelPtsUpdated"; Events2["LEVELS_UPDATED"] = "hlsLevelsUpdated"; Events2["AUDIO_TRACKS_UPDATED"] = "hlsAudioTracksUpdated"; Events2["AUDIO_TRACK_SWITCHING"] = "hlsAudioTrackSwitching"; Events2["AUDIO_TRACK_SWITCHED"] = "hlsAudioTrackSwitched"; Events2["AUDIO_TRACK_LOADING"] = "hlsAudioTrackLoading"; Events2["AUDIO_TRACK_LOADED"] = "hlsAudioTrackLoaded"; Events2["AUDIO_TRACK_UPDATED"] = "hlsAudioTrackUpdated"; Events2["SUBTITLE_TRACKS_UPDATED"] = "hlsSubtitleTracksUpdated"; Events2["SUBTITLE_TRACKS_CLEARED"] = "hlsSubtitleTracksCleared"; Events2["SUBTITLE_TRACK_SWITCH"] = "hlsSubtitleTrackSwitch"; Events2["SUBTITLE_TRACK_LOADING"] = "hlsSubtitleTrackLoading"; Events2["SUBTITLE_TRACK_LOADED"] = "hlsSubtitleTrackLoaded"; Events2["SUBTITLE_TRACK_UPDATED"] = "hlsSubtitleTrackUpdated"; Events2["SUBTITLE_FRAG_PROCESSED"] = "hlsSubtitleFragProcessed"; Events2["CUES_PARSED"] = "hlsCuesParsed"; Events2["NON_NATIVE_TEXT_TRACKS_FOUND"] = "hlsNonNativeTextTracksFound"; Events2["INIT_PTS_FOUND"] = "hlsInitPtsFound"; Events2["FRAG_LOADING"] = "hlsFragLoading"; Events2["FRAG_LOAD_EMERGENCY_ABORTED"] = "hlsFragLoadEmergencyAborted"; Events2["FRAG_LOADED"] = "hlsFragLoaded"; Events2["FRAG_DECRYPTED"] = "hlsFragDecrypted"; Events2["FRAG_PARSING_INIT_SEGMENT"] = "hlsFragParsingInitSegment"; Events2["FRAG_PARSING_USERDATA"] = "hlsFragParsingUserdata"; Events2["FRAG_PARSING_METADATA"] = "hlsFragParsingMetadata"; Events2["FRAG_PARSED"] = "hlsFragParsed"; Events2["FRAG_BUFFERED"] = "hlsFragBuffered"; Events2["FRAG_CHANGED"] = "hlsFragChanged"; Events2["FPS_DROP"] = "hlsFpsDrop"; Events2["FPS_DROP_LEVEL_CAPPING"] = "hlsFpsDropLevelCapping"; Events2["MAX_AUTO_LEVEL_UPDATED"] = "hlsMaxAutoLevelUpdated"; Events2["ERROR"] = "hlsError"; Events2["DESTROYING"] = "hlsDestroying"; Events2["KEY_LOADING"] = "hlsKeyLoading"; Events2["KEY_LOADED"] = "hlsKeyLoaded"; Events2["LIVE_BACK_BUFFER_REACHED"] = "hlsLiveBackBufferReached"; Events2["BACK_BUFFER_REACHED"] = "hlsBackBufferReached"; Events2["STEERING_MANIFEST_LOADED"] = "hlsSteeringManifestLoaded"; Events2["ASSET_LIST_LOADING"] = "hlsAssetListLoading"; Events2["ASSET_LIST_LOADED"] = "hlsAssetListLoaded"; Events2["INTERSTITIALS_UPDATED"] = "hlsInterstitialsUpdated"; Events2["INTERSTITIALS_BUFFERED_TO_BOUNDARY"] = "hlsInterstitialsBufferedToBoundary"; Events2["INTERSTITIAL_ASSET_PLAYER_CREATED"] = "hlsInterstitialAssetPlayerCreated"; Events2["INTERSTITIAL_STARTED"] = "hlsInterstitialStarted"; Events2["INTERSTITIAL_ASSET_STARTED"] = "hlsInterstitialAssetStarted"; Events2["INTERSTITIAL_ASSET_ENDED"] = "hlsInterstitialAssetEnded"; Events2["INTERSTITIAL_ASSET_ERROR"] = "hlsInterstitialAssetError"; Events2["INTERSTITIAL_ENDED"] = "hlsInterstitialEnded"; Events2["INTERSTITIALS_PRIMARY_RESUMED"] = "hlsInterstitialsPrimaryResumed"; Events2["PLAYOUT_LIMIT_REACHED"] = "hlsPlayoutLimitReached"; Events2["EVENT_CUE_ENTER"] = "hlsEventCueEnter"; return Events2; }({}); var PlaylistContextType = { MANIFEST: "manifest", LEVEL: "level", AUDIO_TRACK: "audioTrack", SUBTITLE_TRACK: "subtitleTrack" }; var PlaylistLevelType = { MAIN: "main", AUDIO: "audio", SUBTITLE: "subtitle" }; var EWMA = class { constructor(halfLife, estimate = 0, weight = 0) { this.halfLife = void 0; this.alpha_ = void 0; this.estimate_ = void 0; this.totalWeight_ = void 0; this.halfLife = halfLife; this.alpha_ = halfLife ? Math.exp(Math.log(0.5) / halfLife) : 0; this.estimate_ = estimate; this.totalWeight_ = weight; } sample(weight, value) { const adjAlpha = Math.pow(this.alpha_, weight); this.estimate_ = value * (1 - adjAlpha) + adjAlpha * this.estimate_; this.totalWeight_ += weight; } getTotalWeight() { return this.totalWeight_; } getEstimate() { if (this.alpha_) { const zeroFactor = 1 - Math.pow(this.alpha_, this.totalWeight_); if (zeroFactor) { return this.estimate_ / zeroFactor; } } return this.estimate_; } }; var EwmaBandWidthEstimator = class { constructor(slow, fast, defaultEstimate, defaultTTFB = 100) { this.defaultEstimate_ = void 0; this.minWeight_ = void 0; this.minDelayMs_ = void 0; this.slow_ = void 0; this.fast_ = void 0; this.defaultTTFB_ = void 0; this.ttfb_ = void 0; this.defaultEstimate_ = defaultEstimate; this.minWeight_ = 1e-3; this.minDelayMs_ = 50; this.slow_ = new EWMA(slow); this.fast_ = new EWMA(fast); this.defaultTTFB_ = defaultTTFB; this.ttfb_ = new EWMA(slow); } update(slow, fast) { const { slow_, fast_, ttfb_ } = this; if (slow_.halfLife !== slow) { this.slow_ = new EWMA(slow, slow_.getEstimate(), slow_.getTotalWeight()); } if (fast_.halfLife !== fast) { this.fast_ = new EWMA(fast, fast_.getEstimate(), fast_.getTotalWeight()); } if (ttfb_.halfLife !== slow) { this.ttfb_ = new EWMA(slow, ttfb_.getEstimate(), ttfb_.getTotalWeight()); } } sample(durationMs, numBytes) { durationMs = Math.max(durationMs, this.minDelayMs_); const numBits = 8 * numBytes; const durationS = durationMs / 1e3; const bandwidthInBps = numBits / durationS; this.fast_.sample(durationS, bandwidthInBps); this.slow_.sample(durationS, bandwidthInBps); } sampleTTFB(ttfb) { const seconds = ttfb / 1e3; const weight = Math.sqrt(2) * Math.exp(-Math.pow(seconds, 2) / 2); this.ttfb_.sample(weight, Math.max(ttfb, 5)); } canEstimate() { return this.fast_.getTotalWeight() >= this.minWeight_; } getEstimate() { if (this.canEstimate()) { return Math.min(this.fast_.getEstimate(), this.slow_.getEstimate()); } else { return this.defaultEstimate_; } } getEstimateTTFB() { if (this.ttfb_.getTotalWeight() >= this.minWeight_) { return this.ttfb_.getEstimate(); } else { return this.defaultTTFB_; } } get defaultEstimate() { return this.defaultEstimate_; } destroy() { } }; function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: true, configurable: true, writable: true }) : e[r] = t, e; } function _extends() { return _extends = Object.assign ? Object.assign.bind() : function(n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); } function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function(r2) { return Object.getOwnPropertyDescriptor(e, r2).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread2(e) { for (var r = 1; r < arguments.length; r++) { var t = arguments[r] != null ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), true).forEach(function(r2) { _defineProperty(e, r2, t[r2]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function(r2) { Object.defineProperty(e, r2, Object.getOwnPropertyDescriptor(t, r2)); }); } return e; } function _toPrimitive(t, r) { if (typeof t != "object" || !t) return t; var e = t[Symbol.toPrimitive]; if (e !== void 0) { var i = e.call(t, r); if (typeof i != "object") return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return (r === "string" ? String : Number)(t); } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return typeof i == "symbol" ? i : i + ""; } var Logger = class { constructor(label, logger2) { this.trace = void 0; this.debug = void 0; this.log = void 0; this.warn = void 0; this.info = void 0; this.error = void 0; const lb = `[${label}]:`; this.trace = noop; this.debug = logger2.debug.bind(null, lb); this.log = logger2.log.bind(null, lb); this.warn = logger2.warn.bind(null, lb); this.info = logger2.info.bind(null, lb); this.error = logger2.error.bind(null, lb); } }; var noop = function noop2() { }; var fakeLogger = { trace: noop, debug: noop, log: noop, warn: noop, info: noop, error: noop }; function createLogger() { return _extends({}, fakeLogger); } function consolePrintFn(type, id) { const func = self.console[type]; return func ? func.bind(self.console, `${id ? "[" + id + "] " : ""}[${type}] >`) : noop; } function getLoggerFn(key, debugConfig, id) { return debugConfig[key] ? debugConfig[key].bind(debugConfig) : consolePrintFn(key, id); } var exportedLogger = createLogger(); function enableLogs(debugConfig, context, id) { const newLogger = createLogger(); if (typeof console === "object" && debugConfig === true || typeof debugConfig === "object") { const keys = [ "debug", "log", "info", "warn", "error" ]; keys.forEach((key) => { newLogger[key] = getLoggerFn(key, debugConfig, id); }); try { newLogger.log(`Debug logs enabled for "${context}" in hls.js version ${"1.6.7"}`); } catch (e) { return createLogger(); } keys.forEach((key) => { exportedLogger[key] = getLoggerFn(key, debugConfig); }); } else { _extends(exportedLogger, newLogger); } return newLogger; } var logger = exportedLogger; function getMediaSource(preferManagedMediaSource = true) { if (typeof self === "undefined") return void 0; const mms = (preferManagedMediaSource || !self.MediaSource) && self.ManagedMediaSource; return mms || self.MediaSource || self.WebKitMediaSource; } function isManagedMediaSource(source) { return typeof self !== "undefined" && source === self.ManagedMediaSource; } function isCompatibleTrackChange(currentTracks, requiredTracks) { const trackNames = Object.keys(currentTracks); const requiredTrackNames = Object.keys(requiredTracks); const trackCount = trackNames.length; const requiredTrackCount = requiredTrackNames.length; return !trackCount || !requiredTrackCount || trackCount === requiredTrackCount && !trackNames.some((name) => requiredTrackNames.indexOf(name) === -1); } function utf8ArrayToStr(array, exitOnNull = false) { if (typeof TextDecoder !== "undefined") { const decoder = new TextDecoder("utf-8"); const decoded = decoder.decode(array); if (exitOnNull) { const idx = decoded.indexOf("\0"); return idx !== -1 ? decoded.substring(0, idx) : decoded; } return decoded.replace(/\0/g, ""); } const len = array.length; let c; let char2; let char3; let out = ""; let i = 0; while (i < len) { c = array[i++]; if (c === 0 && exitOnNull) { return out; } else if (c === 0 || c === 3) { continue; } switch (c >> 4) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: out += String.fromCharCode(c); break; case 12: case 13: char2 = array[i++]; out += String.fromCharCode((c & 31) << 6 | char2 & 63); break; case 14: char2 = array[i++]; char3 = array[i++]; out += String.fromCharCode((c & 15) << 12 | (char2 & 63) << 6 | (char3 & 63) << 0); break; } } return out; } var Hex = { hexDump: function(array) { let str = ""; for (let i = 0; i < array.length; i++) { let h = array[i].toString(16); if (h.length < 2) { h = "0" + h; } str += h; } return str; } }; function hexToArrayBuffer(str) { return Uint8Array.from(str.replace(/^0x/, "").replace(/([\da-fA-F]{2}) ?/g, "0x$1 ").replace(/ +$/, "").split(" ")).buffer; } function getDefaultExportFromCjs(x) { return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x; } var urlToolkit = { exports: {} }; var hasRequiredUrlToolkit; function requireUrlToolkit() { if (hasRequiredUrlToolkit) return urlToolkit.exports; hasRequiredUrlToolkit = 1; (function(module, exports) { (function(root) { var URL_REGEX = /^(?=((?:[a-zA-Z0-9+\-.]+:)?))\1(?=((?:\/\/[^\/?#]*)?))\2(?=((?:(?:[^?#\/]*\/)*[^;?#\/]*)?))\3((?:;[^?#]*)?)(\?[^#]*)?(#[^]*)?$/; var FIRST_SEGMENT_REGEX = /^(?=([^\/?#]*))\1([^]*)$/; var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g; var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/)[^\/]*(?=\/)/g; var URLToolkit = { buildAbsoluteURL: function(baseURL, relativeURL, opts) { opts = opts || {}; baseURL = baseURL.trim(); relativeURL = relativeURL.trim(); if (!relativeURL) { if (!opts.alwaysNormalize) { return baseURL; } var 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); } var relativeParts = URLToolkit.parseURL(relativeURL); if (!relativeParts) { throw new Error("Error trying to parse relative URL."); } if (relativeParts.scheme) { if (!opts.alwaysNormalize) { return relativeURL; } relativeParts.path = URLToolkit.normalizePath(relativeParts.path); return URLToolkit.buildURLFromParts(relativeParts); } var baseParts = URLToolkit.parseURL(baseURL); if (!baseParts) { throw new Error("Error trying to parse base URL."); } if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== "/") { var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path); baseParts.netLoc = pathParts[1]; baseParts.path = pathParts[2]; } if (baseParts.netLoc && !baseParts.path) { baseParts.path = "/"; } var builtParts = { scheme: baseParts.scheme, netLoc: relativeParts.netLoc, path: null, params: relativeParts.params, query: relativeParts.query, fragment: relativeParts.fragment }; if (!relativeParts.netLoc) { builtParts.netLoc = baseParts.netLoc; if (relativeParts.path[0] !== "/") { if (!relativeParts.path) { builtParts.path = baseParts.path; if (!relativeParts.params) { builtParts.params = baseParts.params; if (!relativeParts.query) { builtParts.query = baseParts.query; } } } else { var baseURLPath = baseParts.path; var 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) { var 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) { path = path.split("").reverse().join("").replace(SLASH_DOT_REGEX, ""); while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, "")).length) { } return path.split("").reverse().join(""); }, buildURLFromParts: function(parts) { return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment; } }; module.exports = URLToolkit; })(); })(urlToolkit); return urlToolkit.exports; } var urlToolkitExports = requireUrlToolkit(); var LoadStats = class { constructor() { this.aborted = false; this.loaded = 0; this.retry = 0; this.total = 0; this.chunkCount = 0; this.bwEstimate = 0; this.loading = { start: 0, first: 0, end: 0 }; this.parsing = { start: 0, end: 0 }; this.buffering = { start: 0, first: 0, end: 0 }; } }; var ElementaryStreamTypes = { AUDIO: "audio", VIDEO: "video", AUDIOVIDEO: "audiovideo" }; var BaseSegment = class { constructor(base) { this._byteRange = null; this._url = null; this._stats = null; this._streams = null; this.base = void 0; this.relurl = void 0; if (typeof base === "string") { base = { url: base }; } this.base = base; makeEnumerable(this, "stats"); } setByteRange(value, previous) { const params = value.split("@", 2); let start; if (params.length === 1) { start = (previous == null ? void 0 : previous.byteRangeEndOffset) || 0; } else { start = parseInt(params[1]); } this._byteRange = [start, parseInt(params[0]) + start]; } get baseurl() { return this.base.url; } get byteRange() { if (this._byteRange === null) { return []; } return this._byteRange; } get byteRangeStartOffset() { return this.byteRange[0]; } get byteRangeEndOffset() { return this.byteRange[1]; } get elementaryStreams() { if (this._streams === null) { this._streams = { [ElementaryStreamTypes.AUDIO]: null, [ElementaryStreamTypes.VIDEO]: null, [ElementaryStreamTypes.AUDIOVIDEO]: null }; } return this._streams; } set elementaryStreams(value) { this._streams = value; } get hasStats() { return this._stats !== null; } get hasStreams() { return this._streams !== null; } get stats() { if (this._stats === null) { this._stats = new LoadStats(); } return this._stats; } set stats(value) { this._stats = value; } get url() { if (!this._url && this.baseurl && this.relurl) { this._url = urlToolkitExports.buildAbsoluteURL(this.baseurl, this.relurl, { alwaysNormalize: true }); } return this._url || ""; } set url(value) { this._url = value; } clearElementaryStreamInfo() { const { elementaryStreams } = this; elementaryStreams[ElementaryStreamTypes.AUDIO] = null; elementaryStreams[ElementaryStreamTypes.VIDEO] = null; elementaryStreams[ElementaryStreamTypes.AUDIOVIDEO] = null; } }; function isMediaFragment(frag) { return frag.sn !== "initSegment"; } var Fragment = class extends BaseSegment { constructor(type, base) { super(base); this._decryptdata = null; this._programDateTime = null; this._ref = null; this._bitrate = void 0; this.rawProgramDateTime = null; this.tagList = []; this.duration = 0; this.sn = 0; this.levelkeys = void 0; this.type = void 0; this.loader = null; this.keyLoader = null; this.level = -1; this.cc = 0; this.startPTS = void 0; this.endPTS = void 0; this.startDTS = void 0; this.endDTS = void 0; this.start = 0; this.playlistOffset = 0; this.deltaPTS = void 0; this.maxStartPTS = void 0; this.minEndPTS = void 0; this.data = void 0; this.bitrateTest = false; this.title = null; this.initSegment = null; this.endList = void 0; this.gap = void 0; this.urlId = 0; this.type = type; } get byteLength() { if (this.hasStats) { const total = this.stats.total; if (total) { return total; } } if (this.byteRange) { const start = this.byteRange[0]; const end = this.byteRange[1]; if (isFiniteNumber(start) && isFiniteNumber(end)) { return end - start; } } return null; } get bitrate() { if (this.byteLength) { return this.byteLength * 8 / this.duration; } if (this._bitrate) { return this._bitrate; } return null; } set bitrate(value) { this._bitrate = value; } get decryptdata() { const { levelkeys } = this; if (!levelkeys && !this._decryptdata) { return null; } if (!this._decryptdata && this.levelkeys && !this.levelkeys.NONE) { const key = this.levelkeys.identity; if (key) { this._decryptdata = key.getDecryptData(this.sn); } else { const keyFormats = Object.keys(this.levelkeys); if (keyFormats.length === 1) { return this._decryptdata = this.levelkeys[keyFormats[0]].getDecryptData(this.sn); } } } return this._decryptdata; } get end() { return this.start + this.duration; } get endProgramDateTime() { if (this.programDateTime === null) { return null; } const duration = !isFiniteNumber(this.duration) ? 0 : this.duration; return this.programDateTime + duration * 1e3; } get encrypted() { var _this$_decryptdata; if ((_this$_decryptdata = this._decryptdata) != null && _this$_decryptdata.encrypted) { return true; } else if (this.levelkeys) { const keyFormats = Object.keys(this.levelkeys); const len = keyFormats.length; if (len > 1 || len === 1 && this.levelkeys[keyFormats[0]].encrypted) { return true; } } return false; } get programDateTime() { if (this._programDateTime === null && this.rawProgramDateTime) { this.programDateTime = Date.parse(this.rawProgramDateTime); } return this._programDateTime; } set programDateTime(value) { if (!isFiniteNumber(value)) { this._programDateTime = this.rawProgramDateTime = null; return; } this._programDateTime = value; } get ref() { if (!isMediaFragment(this)) { return null; } if (!this._ref) { this._ref = { base: this.base, start: this.start, duration: this.duration, sn: this.sn, programDateTime: this.programDateTime }; } return this._ref; } addStart(value) { this.setStart(this.start + value); } setStart(value) { this.start = value; if (this._ref) { this._ref.start = value; } } setDuration(value) { this.duration = value; if (this._ref) { this._ref.duration = value; } } setKeyFormat(keyFormat) { if (this.levelkeys) { const key = this.levelkeys[keyFormat]; if (key && !this._decryptdata) { this._decryptdata = key.getDecryptData(this.sn); } } } abortRequests() { var _this$loader, _this$keyLoader; (_this$loader = this.loader) == null ? void 0 : _this$loader.abort(); (_this$keyLoader = this.keyLoader) == null ? void 0 : _this$keyLoader.abort(); } setElementaryStreamInfo(type, startPTS, endPTS, startDTS, endDTS, partial = false) { const { elementaryStreams } = this; const info = elementaryStreams[type]; if (!info) { elementaryStreams[type] = { startPTS, endPTS, startDTS, endDTS, partial }; return; } info.startPTS = Math.min(info.startPTS, startPTS); info.endPTS = Math.max(info.endPTS, endPTS); info.startDTS = Math.min(info.startDTS, startDTS); info.endDTS = Math.max(info.endDTS, endDTS); } }; var Part = class extends BaseSegment { constructor(partAttrs, frag, base, index, previous) { super(base); this.fragOffset = 0; this.duration = 0; this.gap = false; this.independent = false; this.relurl = void 0; this.fragment = void 0; this.index = void 0; this.duration = partAttrs.decimalFloatingPoint("DURATION"); this.gap = partAttrs.bool("GAP"); this.independent = partAttrs.bool("INDEPENDENT"); this.relurl = partAttrs.enumeratedString("URI"); this.fragment = frag; this.index = index; const byteRange = partAttrs.enumeratedString("BYTERANGE"); if (byteRange) { this.setByteRange(byteRange, previous); } if (previous) { this.fragOffset = previous.fragOffset + previous.duration; } } get start() { return this.fragment.start + this.fragOffset; } get end() { return this.start + this.duration; } get loaded() { const { elementaryStreams } = this; return !!(elementaryStreams.audio || elementaryStreams.video || elementaryStreams.audiovideo); } }; function getOwnPropertyDescriptorFromPrototypeChain(object, property) { const prototype = Object.getPrototypeOf(object); if (prototype) { const propertyDescriptor = Object.getOwnPropertyDescriptor(prototype, property); if (propertyDescriptor) { return propertyDescriptor; } return getOwnPropertyDescriptorFromPrototypeChain(prototype, property); } } function makeEnumerable(object, property) { const d = getOwnPropertyDescriptorFromPrototypeChain(object, property); if (d) { d.enumerable = true; Object.defineProperty(object, property, d); } } var UINT32_MAX$1 = Math.pow(2, 32) - 1; var push = [].push; var RemuxerTrackIdConfig = { video: 1, audio: 2, id3: 3, text: 4 }; function bin2str(data) { return String.fromCharCode.apply(null, data); } function readUint16(buffer, offset) { const val = buffer[offset] << 8 | buffer[offset + 1]; return val < 0 ? 65536 + val : val; } function readUint32(buffer, offset) { const val = readSint32(buffer, offset); return val < 0 ? 4294967296 + val : val; } function readUint64(buffer, offset) { let result = readUint32(buffer, offset); result *= Math.pow(2, 32); result += readUint32(buffer, offset + 4); return result; } function readSint32(buffer, offset) { return buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3]; } function hasMoofData(data) { const end = data.byteLength; for (let i = 0; i < end; ) { const size = readUint32(data, i); if (size > 8 && data[i + 4] === 109 && data[i + 5] === 111 && data[i + 6] === 111 && data[i + 7] === 102) { return true; } i = size > 1 ? i + size : end; } return false; } function findBox(data, path) { const results = []; if (!path.length) { return results; } const end = data.byteLength; for (let i = 0; i < end; ) { const size = readUint32(data, i); const type = bin2str(data.subarray(i + 4, i + 8)); const endbox = size > 1 ? i + size : end; if (type === path[0]) { if (path.length === 1) { results.push(data.subarray(i + 8, endbox)); } else { const subresults = findBox(data.subarray(i + 8, endbox), path.slice(1)); if (subresults.length) { push.apply(results, subresults); } } } i = endbox; } return results; } function parseSegmentIndex(sidx) { const references = []; const version2 = sidx[0]; let index = 8; const timescale = readUint32(sidx, index); index += 4; let earliestPresentationTime = 0; let firstOffset = 0; if (version2 === 0) { earliestPresentationTime = readUint32(sidx, index); firstOffset = readUint32(sidx, index + 4); index += 8; } else { earliestPresentationTime = readUint64(sidx, index); firstOffset = readUint64(sidx, index + 8); index += 16; } index += 2; let startByte = sidx.length + firstOffset; const referencesCount = readUint16(sidx, index); index += 2; for (let i = 0; i < referencesCount; i++) { let referenceIndex = index; const referenceInfo = readUint32(sidx, referenceIndex); referenceIndex += 4; const referenceSize = referenceInfo & 2147483647; const referenceType = (referenceInfo & 2147483648) >>> 31; if (referenceType === 1) { logger.warn("SIDX has hierarchical references (not supported)"); return null; } const subsegmentDuration = readUint32(sidx, referenceIndex); referenceIndex += 4; references.push({ referenceSize, subsegmentDuration, info: { duration: subsegmentDuration / timescale, start: startByte, end: startByte + referenceSize - 1 } }); startByte += referenceSize; referenceIndex += 4; index = referenceIndex; } return { earliestPresentationTime, timescale, version: version2, referencesCount, references }; } function parseInitSegment(initSegment) { const result = []; const traks = findBox(initSegment, ["moov", "trak"]); for (let i = 0; i < traks.length; i++) { const trak = traks[i]; const tkhd = findBox(trak, ["tkhd"])[0]; if (tkhd) { let version2 = tkhd[0]; const trackId = readUint32(tkhd, version2 === 0 ? 12 : 20); const mdhd = findBox(trak, ["mdia", "mdhd"])[0]; if (mdhd) { version2 = mdhd[0]; const timescale = readUint32(mdhd, version2 === 0 ? 12 : 20); const hdlr = findBox(trak, ["mdia", "hdlr"])[0]; if (hdlr) { const hdlrType = bin2str(hdlr.subarray(8, 12)); const type = { soun: ElementaryStreamTypes.AUDIO, vide: ElementaryStreamTypes.VIDEO }[hdlrType]; const stsdBox = findBox(trak, ["mdia", "minf", "stbl", "stsd"])[0]; const stsd = parseStsd(stsdBox); if (type) { result[trackId] = { timescale, type, stsd }; result[type] = _objectSpread2({ timescale, id: trackId }, stsd); } else { result[trackId] = { timescale, type: hdlrType, stsd }; } } } } } const trex = findBox(initSegment, ["moov", "mvex", "trex"]); trex.forEach((trex2) => { const trackId = readUint32(trex2, 4); const track = result[trackId]; if (track) { track.default = { duration: readUint32(trex2, 12), flags: readUint32(trex2, 20) }; } }); return result; } function parseStsd(stsd) { const sampleEntries = stsd.subarray(8); const sampleEntriesEnd = sampleEntries.subarray(8 + 78); const fourCC = bin2str(sampleEntries.subarray(4, 8)); let codec = fourCC; let supplemental; const encrypted = fourCC === "enca" || fourCC === "encv"; if (encrypted) { const encBox = findBox(sampleEntries, [fourCC])[0]; const encBoxChildren = encBox.subarray(fourCC === "enca" ? 28 : 78); const sinfs = findBox(encBoxChildren, ["sinf"]); sinfs.forEach((sinf) => { const schm = findBox(sinf, ["schm"])[0]; if (schm) { const scheme = bin2str(schm.subarray(4, 8)); if (scheme === "cbcs" || scheme === "cenc") { const frma = findBox(sinf, ["frma"])[0]; if (frma) { codec = bin2str(frma); } } } }); } const codecFourCC = codec; switch (codec) { case "avc1": case "avc2": case "avc3": case "avc4": { const avcCBox = findBox(sampleEntriesEnd, ["avcC"])[0]; if (avcCBox && avcCBox.length > 3) { codec += "." + toHex(avcCBox[1]) + toHex(avcCBox[2]) + toHex(avcCBox[3]); supplemental = parseSupplementalDoViCodec(codecFourCC === "avc1" ? "dva1" : "dvav", sampleEntriesEnd); } break; } case "mp4a": { const codecBox = findBox(sampleEntries, [fourCC])[0]; const esdsBox = findBox(codecBox.subarray(28), ["esds"])[0]; if (esdsBox && esdsBox.length > 7) { let i = 4; if (esdsBox[i++] !== 3) { break; } i = skipBERInteger(esdsBox, i); i += 2; const flags = esdsBox[i++]; if (flags & 128) { i += 2; } if (flags & 64) { i += esdsBox[i++]; } if (esdsBox[i++] !== 4) { break; } i = skipBERInteger(esdsBox, i); const objectType = esdsBox[i++]; if (objectType === 64) { codec += "." + toHex(objectType); } else { break; } i += 12; if (esdsBox[i++] !== 5) { break; } i = skipBERInteger(esdsBox, i); const firstByte = esdsBox[i++]; let audioObjectType = (firstByte & 248) >> 3; if (audioObjectType === 31) { audioObjectType += 1 + ((firstByte & 7) << 3) + ((esdsBox[i] & 224) >> 5); } codec += "." + audioObjectType; } break; } case "hvc1": case "hev1": { const hvcCBox = findBox(sampleEntriesEnd, ["hvcC"])[0]; if (hvcCBox && hvcCBox.length > 12) { const profileByte = hvcCBox[1]; const profileSpace = ["", "A", "B", "C"][profileByte >> 6]; const generalProfileIdc = profileByte & 31; const profileCompat = readUint32(hvcCBox, 2); const tierFlag = (profileByte & 32) >> 5 ? "H" : "L"; const levelIDC = hvcCBox[12]; const constraintIndicator = hvcCBox.subarray(6, 12); codec += "." + profileSpace + generalProfileIdc; codec += "." + reverse32BitInt(profileCompat).toString(16).toUpperCase(); codec += "." + tierFlag + levelIDC; let constraintString = ""; for (let i = constraintIndicator.length; i--; ) { const byte = constraintIndicator[i]; if (byte || constraintString) { const encodedByte = byte.toString(16).toUpperCase(); constraintString = "." + encodedByte + constraintString; } } codec += constraintString; } supplemental = parseSupplementalDoViCodec(codecFourCC == "hev1" ? "dvhe" : "dvh1", sampleEntriesEnd); break; } case "dvh1": case "dvhe": case "dvav": case "dva1": case "dav1": { codec = parseSupplementalDoViCodec(codec, sampleEntriesEnd) || codec; break; } case "vp09": { const vpcCBox = findBox(sampleEntriesEnd, ["vpcC"])[0]; if (vpcCBox && vpcCBox.length > 6) { const profile = vpcCBox[4]; const level = vpcCBox[5]; const bitDepth = vpcCBox[6] >> 4 & 15; codec += "." + addLeadingZero(profile) + "." + addLeadingZero(level) + "." + addLeadingZero(bitDepth); } break; } case "av01": { const av1CBox = findBox(sampleEntriesEnd, ["av1C"])[0]; if (av1CBox && av1CBox.length > 2) { const profile = av1CBox[1] >>> 5; const level = av1CBox[1] & 31; const tierFlag = av1CBox[2] >>> 7 ? "H" : "M"; const highBitDepth = (av1CBox[2] & 64) >> 6; const twelveBit = (av1CBox[2] & 32) >> 5; const bitDepth = profile === 2 && highBitDepth ? twelveBit ? 12 : 10 : highBitDepth ? 10 : 8; const monochrome = (av1CBox[2] & 16) >> 4; const chromaSubsamplingX = (av1CBox[2] & 8) >> 3; const chromaSubsamplingY = (av1CBox[2] & 4) >> 2; const chromaSamplePosition = av1CBox[2] & 3; const colorPrimaries = 1; const transferCharacteristics = 1; const matrixCoefficients = 1; const videoFullRangeFlag = 0; codec += "." + profile + "." + addLeadingZero(level) + tierFlag + "." + addLeadingZero(bitDepth) + "." + monochrome + "." + chromaSubsamplingX + chromaSubsamplingY + chromaSamplePosition + "." + addLeadingZero(colorPrimaries) + "." + addLeadingZero(transferCharacteristics) + "." + addLeadingZero(matrixCoefficients) + "." + videoFullRangeFlag; supplemental = parseSupplementalDoViCodec("dav1", sampleEntriesEnd); } break; } } return { codec, encrypted, supplemental }; } function parseSupplementalDoViCodec(fourCC, sampleEntriesEnd) { const dvvCResult = findBox(sampleEntriesEnd, ["dvvC"]); const dvXCBox = dvvCResult.length ? dvvCResult[0] : findBox(sampleEntriesEnd, ["dvcC"])[0]; if (dvXCBox) { const doViProfile = dvXCBox[2] >> 1 & 127; const doViLevel = dvXCBox[2] << 5 & 32 | dvXCBox[3] >> 3 & 31; return fourCC + "." + addLeadingZero(doViProfile) + "." + addLeadingZero(doViLevel); } } function reverse32BitInt(val) { let result = 0; for (let i = 0; i < 32; i++) { result |= (val >> i & 1) << 32 - 1 - i; } return result >>> 0; } function skipBERInteger(bytes, i) { const limit = i + 5; while (bytes[i++] & 128 && i < limit) { } return i; } function toHex(x) { return ("0" + x.toString(16).toUpperCase()).slice(-2); } function addLeadingZero(num) { return (num < 10 ? "0" : "") + num; } function patchEncyptionData(initSegment, decryptdata) { if (!initSegment || !decryptdata) { return initSegment; } const keyId = decryptdata.keyId; if (keyId && decryptdata.isCommonEncryption) { const traks = findBox(initSegment, ["moov", "trak"]); traks.forEach((trak) => { const stsd = findBox(trak, ["mdia", "minf", "stbl", "stsd"])[0]; const sampleEntries = stsd.subarray(8); let encBoxes = findBox(sampleEntries, ["enca"]); const isAudio = encBoxes.length > 0; if (!isAudio) { encBoxes = findBox(sampleEntries, ["encv"]); } encBoxes.forEach((enc) => { const encBoxChildren = isAudio ? enc.subarray(28) : enc.subarray(78); const sinfBoxes = findBox(encBoxChildren, ["sinf"]); sinfBoxes.forEach((sinf) => { const tenc = parseSinf(sinf); if (tenc) { const tencKeyId = tenc.subarray(8, 24); if (!tencKeyId.some((b) => b !== 0)) { logger.log(`[eme] Patching keyId in 'enc${isAudio ? "a" : "v"}>sinf>>tenc' box: ${Hex.hexDump(tencKeyId)} -> ${Hex.hexDump(keyId)}`); tenc.set(keyId, 8); } } }); }); }); } return initSegment; } function parseSinf(sinf) { const schm = findBox(sinf, ["schm"])[0]; if (schm) { const scheme = bin2str(schm.subarray(4, 8)); if (scheme === "cbcs" || scheme === "cenc") { return findBox(sinf, ["schi", "tenc"])[0]; } } return null; } function getSampleData(data, initData, logger2) { const tracks = {}; const trafs = findBox(data, ["moof", "traf"]); for (let i = 0; i < trafs.length; i++) { const traf = trafs[i]; const tfhd = findBox(traf, ["tfhd"])[0]; const id = readUint32(tfhd, 4); const track = initData[id]; if (!track) { continue; } const trackTimes = tracks[id] || (tracks[id] = { start: NaN, duration: 0, sampleCount: 0, timescale: track.timescale, type: track.type }); const tfdt = findBox(traf, ["tfdt"])[0]; if (tfdt) { const version2 = tfdt[0]; let baseTime = readUint32(tfdt, 4); if (version2 === 1) { if (baseTime === UINT32_MAX$1) { logger2.warn(`[mp4-demuxer]: Ignoring assumed invalid signed 64-bit track fragment decode time`); } else { baseTime *= UINT32_MAX$1 + 1; baseTime += readUint32(tfdt, 8); } } if (isFiniteNumber(baseTime) && (!isFiniteNumber(trackTimes.start) || baseTime < trackTimes.start)) { trackTimes.start = baseTime; } } const trackDefault = track.default; const tfhdFlags = readUint32(tfhd, 0) | (trackDefault == null ? void 0 : trackDefault.flags); let defaultSampleDuration = (trackDefault == null ? void 0 : trackDefault.duration) || 0; if (tfhdFlags & 8) { if (tfhdFlags & 2) { defaultSampleDuration = readUint32(tfhd, 12); } else { defaultSampleDuration = readUint32(tfhd, 8); } } const truns = findBox(traf, ["trun"]); let sampleDTS = trackTimes.start || 0; let rawDuration = 0; let sampleDuration = defaultSampleDuration; for (let j = 0; j < truns.length; j++) { const trun = truns[j]; const sampleCount = readUint32(trun, 4); const sampleIndex = trackTimes.sampleCount; trackTimes.sampleCount += sampleCount; const dataOffsetPresent = trun[3] & 1; const firstSampleFlagsPresent = trun[3] & 4; const sampleDurationPresent = trun[2] & 1; const sampleSizePresent = trun[2] & 2; const sampleFlagsPresent = trun[2] & 4; const sampleCompositionTimeOffsetPresent = trun[2] & 8; let offset = 8; let remaining = sampleCount; if (dataOffsetPresent) { offset += 4; } if (firstSampleFlagsPresent && sampleCount) { const isNonSyncSample = trun[offset + 1] & 1; if (!isNonSyncSample && trackTimes.keyFrameIndex === void 0) { trackTimes.keyFrameIndex = sampleIndex; } offset += 4; if (sampleDurationPresent) { sampleDuration = readUint32(trun, offset); offset += 4; } else { sampleDuration = defaultSampleDuration; } if (sampleSizePresent) { offset += 4; } if (sampleCompositionTimeOffsetPresent) { offset += 4; } sampleDTS += sampleDuration; rawDuration += sampleDuration; remaining--; } while (remaining--) { if (sampleDurationPresent) { sampleDuration = readUint32(trun, offset); offset += 4; } else { sampleDuration = defaultSampleDuration; } if (sampleSizePresent) { offset += 4; } if (sampleFlagsPresent) { const isNonSyncSample = trun[offset + 1] & 1; if (!isNonSyncSample) { if (trackTimes.keyFrameIndex === void 0) { trackTimes.keyFrameIndex = trackTimes.sampleCount - (remaining + 1); trackTimes.keyFrameStart = sampleDTS; } } offset += 4; } if (sampleCompositionTimeOffsetPresent) { offset += 4; } sampleDTS += sampleDuration; rawDuration += sampleDuration; } if (!rawDuration && defaultSampleDuration) { rawDuration += defaultSampleDuration * sampleCount; } } trackTimes.duration += rawDuration; } if (!Object.keys(tracks).some((trackId) => tracks[trackId].duration)) { let sidxMinStart = Infinity; let sidxMaxEnd = 0; const sidxs = findBox(data, ["sidx"]); for (let i = 0; i < sidxs.length; i++) { const sidx = parseSegmentIndex(sidxs[i]); if (sidx != null && sidx.references) { sidxMinStart = Math.min(sidxMinStart, sidx.earliestPresentationTime / sidx.timescale); const subSegmentDuration = sidx.references.reduce((dur, ref) => dur + ref.info.duration || 0, 0); sidxMaxEnd = Math.max(sidxMaxEnd, subSegmentDuration + sidx.earliestPresentationTime / sidx.timescale); } } if (sidxMaxEnd && isFiniteNumber(sidxMaxEnd)) { Object.keys(tracks).forEach((trackId) => { if (!tracks[trackId].duration) { tracks[trackId].duration = sidxMaxEnd * tracks[trackId].timescale - tracks[trackId].start; } }); } } return tracks; } function segmentValidRange(data) { const segmentedRange = { valid: null, remainder: null }; const moofs = findBox(data, ["moof"]); if (moofs.length < 2) { segmentedRange.remainder = data; return segmentedRange; } const last = moofs[moofs.length - 1]; segmentedRange.valid = data.slice(0, last.byteOffset - 8); segmentedRange.remainder = data.slice(last.byteOffset - 8); return segmentedRange; } function appendUint8Array(data1, data2) { const temp = new Uint8Array(data1.length + data2.length); temp.set(data1); temp.set(data2, data1.length); return temp; } function parseSamples(timeOffset, track) { const seiSamples = []; const videoData = track.samples; const timescale = track.timescale; const trackId = track.id; let isHEVCFlavor = false; const moofs = findBox(videoData, ["moof"]); moofs.map((moof) => { const moofOffset = moof.byteOffset - 8; const trafs = findBox(moof, ["traf"]); trafs.map((traf) => { const baseTime = findBox(traf, ["tfdt"]).map((tfdt) => { const version2 = tfdt[0]; let result = readUint32(tfdt, 4); if (version2 === 1) { result *= Math.pow(2, 32); result += readUint32(tfdt, 8); } return result / timescale; })[0]; if (baseTime !== void 0) { timeOffset = baseTime; } return findBox(traf, ["tfhd"]).map((tfhd) => { const id = readUint32(tfhd, 4); const tfhdFlags = 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 tfhdOffset = 8; if (id === trackId) { if (baseDataOffsetPresent) { tfhdOffset += 8; } if (sampleDescriptionIndexPresent) { tfhdOffset += 4; } if (defaultSampleDurationPresent) { defaultSampleDuration = readUint32(tfhd, tfhdOffset); tfhdOffset += 4; } if (defaultSampleSizePresent) { defaultSampleSize = readUint32(tfhd, tfhdOffset); tfhdOffset += 4; } if (defaultSampleFlagsPresent) { tfhdOffset += 4; } if (track.type === "video") { isHEVCFlavor = isHEVC(track.codec); } findBox(traf, ["trun"]).map((trun) => { const version2 = trun[0]; const flags = 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 = readUint32(trun, 4); let trunOffset = 8; if (dataOffsetPresent) { dataOffset = readUint32(trun, trunOffset); trunOffset += 4; } if (firstSampleFlagsPresent) { trunOffset += 4; } let sampleOffset = dataOffset + moofOffset; for (let ix = 0; ix < sampleCount; ix++) { if (sampleDurationPresent) { sampleDuration = readUint32(trun, trunOffset); trunOffset += 4; } else { sampleDuration = defaultSampleDuration; } if (sampleSizePresent) { sampleSize = readUint32(trun, trunOffset); trunOffset += 4; } else { sampleSize = defaultSampleSize; } if (sampleFlagsPresent) { trunOffset += 4; } if (sampleCompositionOffsetsPresent) { if (version2 === 0) { compositionOffset = readUint32(trun, trunOffset); } else { compositionOffset = readSint32(trun, trunOffset); } trunOffset += 4; } if (track.type === ElementaryStreamTypes.VIDEO) { let naluTotalSize = 0; while (naluTotalSize < sampleSize) { const naluSize = readUint32(videoData, sampleOffset); sampleOffset += 4; if (isSEIMessage(isHEVCFlavor, videoData[sampleOffset])) { const data = videoData.subarray(sampleOffset, sampleOffset + naluSize); parseSEIMessageFromNALu(data, isHEVCFlavor ? 2 : 1, timeOffset + compositionOffset / timescale, seiSamples); } sampleOffset += naluSize; naluTotalSize += naluSize + 4; } } timeOffset += sampleDuration / timescale; } }); } }); }); }); return seiSamples; } function isHEVC(codec) { if (!codec) { return false; } const baseCodec = codec.substring(0, 4); return baseCodec === "hvc1" || baseCodec === "hev1" || baseCodec === "dvh1" || baseCodec === "dvhe"; } function isSEIMessage(isHEVCFlavor, naluHeader) { if (isHEVCFlavor) { const naluType = naluHeader >> 1 & 63; return naluType === 39 || naluType === 40; } else { const naluType = naluHeader & 31; return naluType === 6; } } function parseSEIMessageFromNALu(unescapedData, headerSize, pts, samples) { const data = discardEPB(unescapedData); let seiPtr = 0; seiPtr += headerSize; let payloadType = 0; let payloadSize = 0; let b = 0; while (seiPtr < data.length) { payloadType = 0; do { if (seiPtr >= data.length) { break; } b = data[seiPtr++]; payloadType += b; } while (b === 255); payloadSize = 0; do { if (seiPtr >= data.length) { break; } b = data[seiPtr++]; payloadSize += b; } while (b === 255); const leftOver = data.length - seiPtr; let payPtr = seiPtr; if (payloadSize < leftOver) { seiPtr += payloadSize; } else if (payloadSize > leftOver) { logger.error(`Malformed SEI payload. ${payloadSize} is too small, only ${leftOver} bytes left to parse.`); break; } if (payloadType === 4) { const countryCode = data[payPtr++]; if (countryCode === 181) { const providerCode = readUint16(data, payPtr); payPtr += 2; if (providerCode === 49) { const userStructure = readUint32(data, payPtr); payPtr += 4; if (userStructure === 1195456820) { const userDataType = data[payPtr++]; if (userDataType === 3) { const firstByte = data[payPtr++]; const totalCCs = 31 & firstByte; const enabled = 64 & firstByte; const totalBytes = enabled ? 2 + totalCCs * 3 : 0; const byteArray = new Uint8Array(totalBytes); if (enabled) { byteArray[0] = firstByte; for (let i = 1; i < totalBytes; i++) { byteArray[i] = data[payPtr++]; } } samples.push({ type: userDataType, payloadType, pts, bytes: byteArray }); } } } } } else if (payloadType === 5) { if (payloadSize > 16) { const uuidStrArray = []; for (let i = 0; i < 16; i++) { const _b = data[payPtr++].toString(16); uuidStrArray.push(_b.length == 1 ? "0" + _b : _b); if (i === 3 || i === 5 || i === 7 || i === 9) { uuidStrArray.push("-"); } } const length = payloadSize - 16; const userDataBytes = new Uint8Array(length); for (let i = 0; i < length; i++) { userDataBytes[i] = data[payPtr++]; } samples.push({ payloadType, pts, uuid: uuidStrArray.join(""), userData: utf8ArrayToStr(userDataBytes), userDataBytes }); } } } } function discardEPB(data) { const length = data.byteLength; const EPBPositions = []; let i = 1; 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 (EPBPositions.length === 0) { return 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]) { sourceIndex++; EPBPositions.shift(); } newData[i] = data[sourceIndex]; } return newData; } function parseEmsg(data) { const version2 = data[0]; let schemeIdUri = ""; let value = ""; let timeScale = 0; let presentationTimeDelta = 0; let presentationTime = 0; let eventDuration = 0; let id = 0; let offset = 0; if (version2 === 0) { while (bin2str(data.subarray(offset, offset + 1)) !== "\0") { schemeIdUri += bin2str(data.subarray(offset, offset + 1)); offset += 1; } schemeIdUri += bin2str(data.subarray(offset, offset + 1)); offset += 1; while (bin2str(data.subarray(offset, offset + 1)) !== "\0") { value += bin2str(data.subarray(offset, offset + 1)); offset += 1; } value += bin2str(data.subarray(offset, offset + 1)); offset += 1; timeScale = readUint32(data, 12); presentationTimeDelta = readUint32(data, 16); eventDuration = readUint32(data, 20); id = readUint32(data, 24); offset = 28; } else if (version2 === 1) { offset += 4; timeScale = readUint32(data, offset); offset += 4; const leftPresentationTime = readUint32(data, offset); offset += 4; const rightPresentationTime = readUint32(data, offset); offset += 4; presentationTime = 2 ** 32 * leftPresentationTime + rightPresentationTime; if (!isSafeInteger(presentationTime)) { presentationTime = Number.MAX_SAFE_INTEGER; logger.warn("Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box"); } eventDuration = readUint32(data, offset); offset += 4; id = readUint32(data, offset); offset += 4; while (bin2str(data.subarray(offset, offset + 1)) !== "\0") { schemeIdUri += bin2str(data.subarray(offset, offset + 1)); offset += 1; } schemeIdUri += bin2str(data.subarray(offset, offset + 1)); offset += 1; while (bin2str(data.subarray(offset, offset + 1)) !== "\0") { value += bin2str(data.subarray(offset, offset + 1)); offset += 1; } value += bin2str(data.subarray(offset, offset + 1)); offset += 1; } const payload = data.subarray(offset, data.byteLength); return { schemeIdUri, value, timeScale, presentationTime, presentationTimeDelta, eventDuration, id, payload }; } function mp4Box(type, ...payload) { const len = payload.length; let size = 8; let i = len; 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); for (i = 0, size = 8; i < len; i++) { result.set(payload[i], size); size += payload[i].byteLength; } return result; } function mp4pssh(systemId, keyids, data) { if (systemId.byteLength !== 16) { throw new RangeError("Invalid system id"); } let version2; let kids; { version2 = 0; kids = new Uint8Array(); } let kidCount; if (version2 > 0) { kidCount = new Uint8Array(4); if (keyids.length > 0) { new DataView(kidCount.buffer).setUint32(0, keyids.length, false); } } else { kidCount = new Uint8Array(); } const dataSize = new Uint8Array(4); if (data && data.byteLength > 0) { new DataView(dataSize.buffer).setUint32(0, data.byteLength, false); } return mp4Box([112, 115, 115, 104], new Uint8Array([ version2, 0, 0, 0 ]), systemId, kidCount, kids, dataSize, data || new Uint8Array()); } var userAgentHevcSupportIsInaccurate = () => { return /\(Windows.+Firefox\//i.test(navigator.userAgent); }; var sampleEntryCodesISO = { audio: { a3ds: 1, "ac-3": 0.95, "ac-4": 1, alac: 0.9, alaw: 1, dra1: 1, "dts+": 1, "dts-": 1, dtsc: 1, dtse: 1, dtsh: 1, "ec-3": 0.9, enca: 1, fLaC: 0.9, flac: 0.9, FLAC: 0.9, g719: 1, g726: 1, m4ae: 1, mha1: 1, mha2: 1, mhm1: 1, mhm2: 1, mlpa: 1, mp4a: 1, "raw ": 1, Opus: 1, opus: 1, samr: 1, sawb: 1, sawp: 1, sevc: 1, sqcp: 1, ssmv: 1, twos: 1, ulaw: 1 }, video: { avc1: 1, avc2: 1, avc3: 1, avc4: 1, avcp: 1, av01: 0.8, dav1: 0.8, drac: 1, dva1: 1, dvav: 1, dvh1: 0.7, dvhe: 0.7, encv: 1, hev1: 0.75, hvc1: 0.75, mjp2: 1, mp4v: 1, mvc1: 1, mvc2: 1, mvc3: 1, mvc4: 1, resv: 1, rv60: 1, s263: 1, svc1: 1, svc2: 1, "vc-1": 1, vp08: 1, vp09: 0.9 }, text: { stpp: 1, wvtt: 1 } }; function isCodecType(codec, type) { const typeCodes = sampleEntryCodesISO[type]; return !!typeCodes && !!typeCodes[codec.slice(0, 4)]; } function areCodecsMediaSourceSupported(codecs, type, preferManagedMediaSource = true) { return !codecs.split(",").some((codec) => !isCodecMediaSourceSupported(codec, type, preferManagedMediaSource)); } function isCodecMediaSourceSupported(codec, type, preferManagedMediaSource = true) { var _MediaSource$isTypeSu; const MediaSource = getMediaSource(preferManagedMediaSource); return (_MediaSource$isTypeSu = MediaSource == null ? void 0 : MediaSource.isTypeSupported(mimeTypeForCodec(codec, type))) != null ? _MediaSource$isTypeSu : false; } function mimeTypeForCodec(codec, type) { return `${type}/mp4;codecs=${codec}`; } function videoCodecPreferenceValue(videoCodec) { if (videoCodec) { const fourCC = videoCodec.substring(0, 4); return sampleEntryCodesISO.video[fourCC]; } return 2; } function codecsSetSelectionPreferenceValue(codecSet) { const limitedHevcSupport = userAgentHevcSupportIsInaccurate(); return codecSet.split(",").reduce((num, fourCC) => { const lowerPriority = limitedHevcSupport && isHEVC(fourCC); const preferenceValue = lowerPriority ? 9 : sampleEntryCodesISO.video[fourCC]; if (preferenceValue) { return (preferenceValue * 2 + num) / (num ? 3 : 2); } return (sampleEntryCodesISO.audio[fourCC] + num) / (num ? 2 : 1); }, 0); } var CODEC_COMPATIBLE_NAMES = {}; function getCodecCompatibleNameLower(lowerCaseCodec, preferManagedMediaSource = true) { if (CODEC_COMPATIBLE_NAMES[lowerCaseCodec]) { return CODEC_COMPATIBLE_NAMES[lowerCaseCodec]; } const codecsToCheck = { flac: ["flac", "fLaC", "FLAC"], opus: ["opus", "Opus"], "mp4a.40.34": ["mp3"] }[lowerCaseCodec]; for (let i = 0; i < codecsToCheck.length; i++) { var _getMediaSource; if (isCodecMediaSourceSupported(codecsToCheck[i], "audio", preferManagedMediaSource)) { CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i]; return codecsToCheck[i]; } else if (codecsToCheck[i] === "mp3" && (_getMediaSource = getMediaSource(preferManagedMediaSource)) != null && _getMediaSource.isTypeSupported("audio/mpeg")) { return ""; } } return lowerCaseCodec; } var AUDIO_CODEC_REGEXP = /flac|opus|mp4a\.40\.34/i; function getCodecCompatibleName(codec, preferManagedMediaSource = true) { return codec.replace(AUDIO_CODEC_REGEXP, (m) => getCodecCompatibleNameLower(m.toLowerCase(), preferManagedMediaSource)); } function replaceVideoCodec(originalCodecs, newVideoCodec) { const codecs = []; if (originalCodecs) { const allCodecs = originalCodecs.split(","); for (let i = 0; i < allCodecs.length; i++) { if (!isCodecType(allCodecs[i], "video")) { codecs.push(allCodecs[i]); } } } if (newVideoCodec) { codecs.push(newVideoCodec); } return codecs.join(","); } function pickMostCompleteCodecName(parsedCodec, levelCodec) { if (parsedCodec && (parsedCodec.length > 4 || ["ac-3", "ec-3", "alac", "fLaC", "Opus"].indexOf(parsedCodec) !== -1)) { if (isCodecSupportedAsType(parsedCodec, "audio") || isCodecSupportedAsType(parsedCodec, "video")) { return parsedCodec; } } if (levelCodec) { const levelCodecs = levelCodec.split(","); if (levelCodecs.length > 1) { if (parsedCodec) { for (let i = levelCodecs.length; i--; ) { if (levelCodecs[i].substring(0, 4) === parsedCodec.substring(0, 4)) { return levelCodecs[i]; } } } return levelCodecs[0]; } } return levelCodec || parsedCodec; } function isCodecSupportedAsType(codec, type) { return isCodecType(codec, type) && isCodecMediaSourceSupported(codec, type); } function convertAVC1ToAVCOTI(videoCodecs) { const codecs = videoCodecs.split(","); for (let i = 0; i < codecs.length; i++) { const avcdata = codecs[i].split("."); if (avcdata.length > 2 && avcdata[0] === "avc1") { codecs[i] = `avc1.${parseInt(avcdata[1]).toString(16)}${("000" + parseInt(avcdata[2]).toString(16)).slice(-4)}`; } } return codecs.join(","); } function fillInMissingAV01Params(videoCodec) { if (videoCodec.startsWith("av01.")) { const av1params = videoCodec.split("."); const placeholders = ["0", "111", "01", "01", "01", "0"]; for (let i = av1params.length; i > 4 && i < 10; i++) { av1params[i] = placeholders[i - 4]; } return av1params.join("."); } return videoCodec; } function getM2TSSupportedAudioTypes(preferManagedMediaSource) { const MediaSource = getMediaSource(preferManagedMediaSource) || { isTypeSupported: () => false }; return { mpeg: MediaSource.isTypeSupported("audio/mpeg"), mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'), ac3: MediaSource.isTypeSupported('audio/mp4; codecs="ac-3"') }; } function getCodecsForMimeType(mimeType) { return mimeType.replace(/^.+codecs=["']?([^"']+).*$/, "$1"); } var SUPPORTED_INFO_DEFAULT = { supported: true, configurations: [], decodingInfoResults: [{ supported: true, powerEfficient: true, smooth: true }] }; function getUnsupportedResult(error, configurations) { return { supported: false, configurations, decodingInfoResults: [{ supported: false, smooth: false, powerEfficient: false }], error }; } function requiresMediaCapabilitiesDecodingInfo(level, audioTracksByGroup, currentVideoRange, currentFrameRate, currentBw, audioPreference) { const videoCodecs = level.videoCodec; const audioGroups = level.audioCodec ? level.audioGroups : null; const audioCodecPreference = audioPreference == null ? void 0 : audioPreference.audioCodec; const channelsPreference = audioPreference == null ? void 0 : audioPreference.channels; const maxChannels = channelsPreference ? parseInt(channelsPreference) : audioCodecPreference ? Infinity : 2; let audioChannels = null; if (audioGroups != null && audioGroups.length) { try { if (audioGroups.length === 1 && audioGroups[0]) { audioChannels = audioTracksByGroup.groups[audioGroups[0]].channels; } else { audioChannels = audioGroups.reduce((acc, groupId) => { if (groupId) { const audioTrackGroup = audioTracksByGroup.groups[groupId]; if (!audioTrackGroup) { throw new Error(`Audio track group ${groupId} not found`); } Object.keys(audioTrackGroup.channels).forEach((key) => { acc[key] = (acc[key] || 0) + audioTrackGroup.channels[key]; }); } return acc; }, { 2: 0 }); } } catch (error) { return true; } } return videoCodecs !== void 0 && (videoCodecs.split(",").some((videoCodec) => isHEVC(videoCodec)) || level.width > 1920 && level.height > 1088 || level.height > 1920 && level.width > 1088 || level.frameRate > Math.max(currentFrameRate, 30) || level.videoRange !== "SDR" && level.videoRange !== currentVideoRange || level.bitrate > Math.max(currentBw, 8e6)) || !!audioChannels && isFiniteNumber(maxChannels) && Object.keys(audioChannels).some((channels) => parseInt(channels) > maxChannels); } function getMediaDecodingInfoPromise(level, audioTracksByGroup, mediaCapabilities, cache = {}) { const videoCodecs = level.videoCodec; if (!videoCodecs && !level.audioCodec || !mediaCapabilities) { return Promise.resolve(SUPPORTED_INFO_DEFAULT); } const configurations = []; const videoDecodeList = makeVideoConfigurations(level); const videoCount = videoDecodeList.length; const audioDecodeList = makeAudioConfigurations(level, audioTracksByGroup, videoCount > 0); const audioCount = audioDecodeList.length; for (let i = videoCount || 1 * audioCount || 1; i--; ) { const configuration = { type: "media-source" }; if (videoCount) { configuration.video = videoDecodeList[i % videoCount]; } if (audioCount) { configuration.audio = audioDecodeList[i % audioCount]; const audioBitrate = configuration.audio.bitrate; if (configuration.video && audioBitrate) { configuration.video.bitrate -= audioBitrate; } } configurations.push(configuration); } if (videoCodecs) { const ua = navigator.userAgent; if (videoCodecs.split(",").some((videoCodec) => isHEVC(videoCodec)) && userAgentHevcSupportIsInaccurate()) { return Promise.resolve(getUnsupportedResult(new Error(`Overriding Windows Firefox HEVC MediaCapabilities result based on user-agent string: (${ua})`), configurations)); } } return Promise.all(configurations.map((configuration) => { const decodingInfoKey = getMediaDecodingInfoKey(configuration); return cache[decodingInfoKey] || (cache[decodingInfoKey] = mediaCapabilities.decodingInfo(configuration)); })).then((decodingInfoResults) => ({ supported: !decodingInfoResults.some((info) => !info.supported), configurations, decodingInfoResults })).catch((error) => ({ supported: false, configurations, decodingInfoResults: [], error })); } function makeVideoConfigurations(level) { var _level$videoCodec; const videoCodecs = (_level$videoCodec = level.videoCodec) == null ? void 0 : _level$videoCodec.split(","); const bitrate = getVariantDecodingBitrate(level); const width = level.width || 640; const height = level.height || 480; const framerate = level.frameRate || 30; const videoRange = level.videoRange.toLowerCase(); return videoCodecs ? videoCodecs.map((videoCodec) => { const videoConfiguration = { contentType: mimeTypeForCodec(fillInMissingAV01Params(videoCodec), "video"), width, height, bitrate, framerate }; if (videoRange !== "sdr") { videoConfiguration.transferFunction = videoRange; } return videoConfiguration; }) : []; } function makeAudioConfigurations(level, audioTracksByGroup, hasVideo) { var _level$audioCodec; const audioCodecs = (_level$audioCodec = level.audioCodec) == null ? void 0 : _level$audioCodec.split(","); const combinedBitrate = getVariantDecodingBitrate(level); if (audioCodecs && level.audioGroups) { return level.audioGroups.reduce((configurations, audioGroupId) => { var _audioTracksByGroup$g; const tracks = audioGroupId ? (_audioTracksByGroup$g = audioTracksByGroup.groups[audioGroupId]) == null ? void 0 : _audioTracksByGroup$g.tracks : null; if (tracks) { return tracks.reduce((configs, audioTrack) => { if (audioTrack.groupId === audioGroupId) { const channelsNumber = parseFloat(audioTrack.channels || ""); audioCodecs.forEach((audioCodec) => { const audioConfiguration = { contentType: mimeTypeForCodec(audioCodec, "audio"), bitrate: hasVideo ? estimatedAudioBitrate(audioCodec, combinedBitrate) : combinedBitrate }; if (channelsNumber) { audioConfiguration.channels = "" + channelsNumber; } configs.push(audioConfiguration); }); } return configs; }, configurations); } return configurations; }, []); } return []; } function estimatedAudioBitrate(audioCodec, levelBitrate) { if (levelBitrate <= 1) { return 1; } let audioBitrate = 128e3; if (audioCodec === "ec-3") { audioBitrate = 768e3; } else if (audioCodec === "ac-3") { audioBitrate = 64e4; } return Math.min(levelBitrate / 2, audioBitrate); } function getVariantDecodingBitrate(level) { return Math.ceil(Math.max(level.bitrate * 0.9, level.averageBitrate) / 1e3) * 1e3 || 1; } function getMediaDecodingInfoKey(config) { let key = ""; const { audio, video } = config; if (video) { const codec = getCodecsForMimeType(video.contentType); key += `${codec}_r${video.height}x${video.width}f${Math.ceil(video.framerate)}${video.transferFunction || "sd"}_${Math.ceil(video.bitrate / 1e5)}`; } if (audio) { const codec = getCodecsForMimeType(audio.contentType); key += `${video ? "_" : ""}${codec}_c${audio.channels}`; } return key; } var HdcpLevels = ["NONE", "TYPE-0", "TYPE-1", null]; function isHdcpLevel(value) { return HdcpLevels.indexOf(value) > -1; } var VideoRangeValues = ["SDR", "PQ", "HLG"]; function isVideoRange(value) { return !!value && VideoRangeValues.indexOf(value) > -1; } var HlsSkip = { No: "", Yes: "YES", v2: "v2" }; function getSkipValue(details) { const { canSkipUntil, canSkipDateRanges, age } = details; const playlistRecentEnough = age < canSkipUntil / 2; if (canSkipUntil && playlistRecentEnough) { if (canSkipDateRanges) { return HlsSkip.v2; } return HlsSkip.Yes; } return HlsSkip.No; } var HlsUrlParameters = class { constructor(msn, part, skip) { this.msn = void 0; this.part = void 0; this.skip = void 0; this.msn = msn; this.part = part; this.skip = skip; } addDirectives(uri) { const url = new self.URL(uri); if (this.msn !== void 0) { url.searchParams.set("_HLS_msn", this.msn.toString()); } if (this.part !== void 0) { url.searchParams.set("_HLS_part", this.part.toString()); } if (this.skip) { url.searchParams.set("_HLS_skip", this.skip); } return url.href; } }; var Level = class { constructor(data) { this._attrs = void 0; this.audioCodec = void 0; this.bitrate = void 0; this.codecSet = void 0; this.url = void 0; this.frameRate = void 0; this.height = void 0; this.id = void 0; this.name = void 0; this.supplemental = void 0; this.videoCodec = void 0; this.width = void 0; this.details = void 0; this.fragmentError = 0; this.loadError = 0; this.loaded = void 0; this.realBitrate = 0; this.supportedPromise = void 0; this.supportedResult = void 0; this._avgBitrate = 0; this._audioGroups = void 0; this._subtitleGroups = void 0; this._urlId = 0; this.url = [data.url]; this._attrs = [data.attrs]; this.bitrate = data.bitrate; if (data.details) { this.details = data.details; } this.id = data.id || 0; this.name = data.name; this.width = data.width || 0; this.height = data.height || 0; this.frameRate = data.attrs.optionalFloat("FRAME-RATE", 0); this._avgBitrate = data.attrs.decimalInteger("AVERAGE-BANDWIDTH"); this.audioCodec = data.audioCodec; this.videoCodec = data.videoCodec; this.codecSet = [data.videoCodec, data.audioCodec].filter((c) => !!c).map((s) => s.substring(0, 4)).join(","); if ("supplemental" in data) { var _data$supplemental; this.supplemental = data.supplemental; const supplementalVideo = (_data$supplemental = data.supplemental) == null ? void 0 : _data$supplemental.videoCodec; if (supplementalVideo && supplementalVideo !== data.videoCodec) { this.codecSet += `,${supplementalVideo.substring(0, 4)}`; } } this.addGroupId("audio", data.attrs.AUDIO); this.addGroupId("text", data.attrs.SUBTITLES); } get maxBitrate() { return Math.max(this.realBitrate, this.bitrate); } get averageBitrate() { return this._avgBitrate || this.realBitrate || this.bitrate; } get attrs() { return this._attrs[0]; } get codecs() { return this.attrs.CODECS || ""; } get pathwayId() { return this.attrs["PATHWAY-ID"] || "."; } get videoRange() { return this.attrs["VIDEO-RANGE"] || "SDR"; } get score() { return this.attrs.optionalFloat("SCORE", 0); } get uri() { return this.url[0] || ""; } hasAudioGroup(groupId) { return hasGroup(this._audioGroups, groupId); } hasSubtitleGroup(groupId) { return hasGroup(this._subtitleGroups, groupId); } get audioGroups() { return this._audioGroups; } get subtitleGroups() { return this._subtitleGroups; } addGroupId(type, groupId) { if (!groupId) { return; } if (type === "audio") { let audioGroups = this._audioGroups; if (!audioGroups) { audioGroups = this._audioGroups = []; } if (audioGroups.indexOf(groupId) === -1) { audioGroups.push(groupId); } } else if (type === "text") { let subtitleGroups = this._subtitleGroups; if (!subtitleGroups) { subtitleGroups = this._subtitleGroups = []; } if (subtitleGroups.indexOf(groupId) === -1) { subtitleGroups.push(groupId); } } } get urlId() { return 0; } set urlId(value) { } get audioGroupIds() { return this.audioGroups ? [this.audioGroupId] : void 0; } get textGroupIds() { return this.subtitleGroups ? [this.textGroupId] : void 0; } get audioGroupId() { var _this$audioGroups; return (_this$audioGroups = this.audioGroups) == null ? void 0 : _this$audioGroups[0]; } get textGroupId() { var _this$subtitleGroups; return (_this$subtitleGroups = this.subtitleGroups) == null ? void 0 : _this$subtitleGroups[0]; } addFallback() { } }; function hasGroup(groups, groupId) { if (!groupId || !groups) { return false; } return groups.indexOf(groupId) !== -1; } function isHdrSupported() { if (typeof matchMedia === "function") { const mediaQueryList = matchMedia("(dynamic-range: high)"); const badQuery = matchMedia("bad query"); if (mediaQueryList.media !== badQuery.media) { return mediaQueryList.matches === true; } } return false; } function getVideoSelectionOptions(currentVideoRange, videoPreference) { let preferHDR = false; let allowedVideoRanges = []; if (currentVideoRange) { preferHDR = currentVideoRange !== "SDR"; allowedVideoRanges = [currentVideoRange]; } if (videoPreference) { allowedVideoRanges = videoPreference.allowedVideoRanges || VideoRangeValues.slice(0); const allowAutoPreferHDR = allowedVideoRanges.join("") !== "SDR" && !videoPreference.videoCodec; preferHDR = videoPreference.preferHDR !== void 0 ? videoPreference.preferHDR : allowAutoPreferHDR && isHdrSupported(); if (!preferHDR) { allowedVideoRanges = ["SDR"]; } } return { preferHDR, allowedVideoRanges }; } var omitCircularRefsReplacer = (replacer) => { const known = /* @__PURE__ */ new WeakSet(); return (_, value) => { if (replacer) { value = replacer(_, value); } if (typeof value === "object" && value !== null) { if (known.has(value)) { return; } known.add(value); } return value; }; }; var stringify = (object, replacer) => JSON.stringify(object, omitCircularRefsReplacer(replacer)); function getStartCodecTier(codecTiers, currentVideoRange, currentBw, audioPreference, videoPreference) { const codecSets = Object.keys(codecTiers); const channelsPreference = audioPreference == null ? void 0 : audioPreference.channels; const audioCodecPreference = audioPreference == null ? void 0 : audioPreference.audioCodec; const videoCodecPreference = videoPreference == null ? void 0 : videoPreference.videoCodec; const preferStereo = channelsPreference && parseInt(channelsPreference) === 2; let hasStereo = false; let hasCurrentVideoRange = false; let minHeight = Infinity; let minFramerate = Infinity; let minBitrate = Infinity; let minIndex = Infinity; let selectedScore = 0; let videoRanges = []; const { preferHDR, allowedVideoRanges } = getVideoSelectionOptions(currentVideoRange, videoPreference); for (let i = codecSets.length; i--; ) { const tier = codecTiers[codecSets[i]]; hasStereo || (hasStereo = tier.channels[2] > 0); minHeight = Math.min(minHeight, tier.minHeight); minFramerate = Math.min(minFramerate, tier.minFramerate); minBitrate = Math.min(minBitrate, tier.minBitrate); const matchingVideoRanges = allowedVideoRanges.filter((range) => tier.videoRanges[range] > 0); if (matchingVideoRanges.length > 0) { hasCurrentVideoRange = true; } } minHeight = isFiniteNumber(minHeight) ? minHeight : 0; minFramerate = isFiniteNumber(minFramerate) ? minFramerate : 0; const maxHeight = Math.max(1080, minHeight); const maxFramerate = Math.max(30, minFramerate); minBitrate = isFiniteNumber(minBitrate) ? minBitrate : currentBw; currentBw = Math.max(minBitrate, currentBw); if (!hasCurrentVideoRange) { currentVideoRange = void 0; } const hasMultipleSets = codecSets.length > 1; const codecSet = codecSets.reduce((selected, candidate) => { const candidateTier = codecTiers[candidate]; if (candidate === selected) { return selected; } videoRanges = hasCurrentVideoRange ? allowedVideoRanges.filter((range) => candidateTier.videoRanges[range] > 0) : []; if (hasMultipleSets) { if (candidateTier.minBitrate > currentBw) { logStartCodecCandidateIgnored(candidate, `min bitrate of ${candidateTier.minBitrate} > current estimate of ${currentBw}`); return selected; } if (!candidateTier.hasDefaultAudio) { logStartCodecCandidateIgnored(candidate, `no renditions with default or auto-select sound found`); return selected; } if (audioCodecPreference && candidate.indexOf(audioCodecPreference.substring(0, 4)) % 5 !== 0) { logStartCodecCandidateIgnored(candidate, `audio codec preference "${audioCodecPreference}" not found`); return selected; } if (channelsPreference && !preferStereo) { if (!candidateTier.channels[channelsPreference]) { logStartCodecCandidateIgnored(candidate, `no renditions with ${channelsPreference} channel sound found (channels options: ${Object.keys(candidateTier.channels)})`); return selected; } } else if ((!audioCodecPreference || preferStereo) && hasStereo && candidateTier.channels["2"] === 0) { logStartCodecCandidateIgnored(candidate, `no renditions with stereo sound found`); return selected; } if (candidateTier.minHeight > maxHeight) { logStartCodecCandidateIgnored(candidate, `min resolution of ${candidateTier.minHeight} > maximum of ${maxHeight}`); return selected; } if (candidateTier.minFramerate > maxFramerate) { logStartCodecCandidateIgnored(candidate, `min framerate of ${candidateTier.minFramerate} > maximum of ${maxFramerate}`); return selected; } if (!videoRanges.some((range) => candidateTier.videoRanges[range] > 0)) { logStartCodecCandidateIgnored(candidate, `no variants with VIDEO-RANGE of ${stringify(videoRanges)} found`); return selected; } if (videoCodecPreference && candidate.indexOf(videoCodecPreference.substring(0, 4)) % 5 !== 0) { logStartCodecCandidateIgnored(candidate, `video codec preference "${videoCodecPreference}" not found`); return selected; } if (candidateTier.maxScore < selectedScore) { logStartCodecCandidateIgnored(candidate, `max score of ${candidateTier.maxScore} < selected max of ${selectedScore}`); return selected; } } if (selected && (codecsSetSelectionPreferenceValue(candidate) >= codecsSetSelectionPreferenceValue(selected) || candidateTier.fragmentError > codecTiers[selected].fragmentError)) { return selected; } minIndex = candidateTier.minIndex; selectedScore = candidateTier.maxScore; return candidate; }, void 0); return { codecSet, videoRanges, preferHDR, minFramerate, minBitrate, minIndex }; } function logStartCodecCandidateIgnored(codeSet, reason) { logger.log(`[abr] start candidates with "${codeSet}" ignored because ${reason}`); } function getAudioTracksByGroup(allAudioTracks) { return allAudioTracks.reduce((audioTracksByGroup, track) => { let trackGroup = audioTracksByGroup.groups[track.groupId]; if (!trackGroup) { trackGroup = audioTracksByGroup.groups[track.groupId] = { tracks: [], channels: { 2: 0 }, hasDefault: false, hasAutoSelect: false }; } trackGroup.tracks.push(track); const channelsKey = track.channels || "2"; trackGroup.channels[channelsKey] = (trackGroup.channels[channelsKey] || 0) + 1; trackGroup.hasDefault = trackGroup.hasDefault || track.default; trackGroup.hasAutoSelect = trackGroup.hasAutoSelect || track.autoselect; if (trackGroup.hasDefault) { audioTracksByGroup.hasDefaultAudio = true; } if (trackGroup.hasAutoSelect) { audioTracksByGroup.hasAutoSelectAudio = true; } return audioTracksByGroup; }, { hasDefaultAudio: false, hasAutoSelectAudio: false, groups: {} }); } function getCodecTiers(levels, audioTracksByGroup, minAutoLevel, maxAutoLevel) { return levels.slice(minAutoLevel, maxAutoLevel + 1).reduce((tiers, level, index) => { if (!level.codecSet) { return tiers; } const audioGroups = level.audioGroups; let tier = tiers[level.codecSet]; if (!tier) { tiers[level.codecSet] = tier = { minBitrate: Infinity, minHeight: Infinity, minFramerate: Infinity, minIndex: index, maxScore: 0, videoRanges: { SDR: 0 }, channels: { "2": 0 }, hasDefaultAudio: !audioGroups, fragmentError: 0 }; } tier.minBitrate = Math.min(tier.minBitrate, level.bitrate); const lesserWidthOrHeight = Math.min(level.height, level.width); tier.minHeight = Math.min(tier.minHeight, lesserWidthOrHeight); tier.minFramerate = Math.min(tier.minFramerate, level.frameRate); tier.minIndex = Math.min(tier.minIndex, index); tier.maxScore = Math.max(tier.maxScore, level.score); tier.fragmentError += level.fragmentError; tier.videoRanges[level.videoRange] = (tier.videoRanges[level.videoRange] || 0) + 1; if (audioGroups) { audioGroups.forEach((audioGroupId) => { if (!audioGroupId) { return; } const audioGroup = audioTracksByGroup.groups[audioGroupId]; if (!audioGroup) { return; } tier.hasDefaultAudio = tier.hasDefaultAudio || audioTracksByGroup.hasDefaultAudio ? audioGroup.hasDefault : audioGroup.hasAutoSelect || !audioTracksByGroup.hasDefaultAudio && !audioTracksByGroup.hasAutoSelectAudio; Object.keys(audioGroup.channels).forEach((channels) => { tier.channels[channels] = (tier.channels[channels] || 0) + audioGroup.channels[channels]; }); }); } return tiers; }, {}); } function getBasicSelectionOption(option) { if (!option) { return option; } const { lang, assocLang, characteristics, channels, audioCodec } = option; return { lang, assocLang, characteristics, channels, audioCodec }; } function findMatchingOption(option, tracks, matchPredicate) { if ("attrs" in option) { const index = tracks.indexOf(option); if (index !== -1) { return index; } } for (let i = 0; i < tracks.length; i++) { const track = tracks[i]; if (matchesOption(option, track, matchPredicate)) { return i; } } return -1; } function matchesOption(option, track, matchPredicate) { const { groupId, name, lang, assocLang, default: isDefault } = option; const forced = option.forced; return (groupId === void 0 || track.groupId === groupId) && (name === void 0 || track.name === name) && (lang === void 0 || languagesMatch(lang, track.lang)) && (lang === void 0 || track.assocLang === assocLang) && (isDefault === void 0 || track.default === isDefault) && (forced === void 0 || track.forced === forced) && (!("characteristics" in option) || characteristicsMatch(option.characteristics || "", track.characteristics)) && (matchPredicate === void 0 || matchPredicate(option, track)); } function languagesMatch(languageA, languageB = "--") { if (languageA.length === languageB.length) { return languageA === languageB; } return languageA.startsWith(languageB) || languageB.startsWith(languageA); } function characteristicsMatch(characteristicsA, characteristicsB = "") { const arrA = characteristicsA.split(","); const arrB = characteristicsB.split(","); return arrA.length === arrB.length && !arrA.some((el) => arrB.indexOf(el) === -1); } function audioMatchPredicate(option, track) { const { audioCodec, channels } = option; return (audioCodec === void 0 || (track.audioCodec || "").substring(0, 4) === audioCodec.substring(0, 4)) && (channels === void 0 || channels === (track.channels || "2")); } function findClosestLevelWithAudioGroup(option, levels, allAudioTracks, searchIndex, matchPredicate) { const currentLevel = levels[searchIndex]; const variants = levels.reduce((variantMap, level, index) => { const uri = level.uri; const renditions2 = variantMap[uri] || (variantMap[uri] = []); renditions2.push(index); return variantMap; }, {}); const renditions = variants[currentLevel.uri]; if (renditions.length > 1) { searchIndex = Math.max.apply(Math, renditions); } const currentVideoRange = currentLevel.videoRange; const currentFrameRate = currentLevel.frameRate; const currentVideoCodec = currentLevel.codecSet.substring(0, 4); const matchingVideo = searchDownAndUpList(levels, searchIndex, (level) => { if (level.videoRange !== currentVideoRange || level.frameRate !== currentFrameRate || level.codecSet.substring(0, 4) !== currentVideoCodec) { return false; } const audioGroups = level.audioGroups; const tracks = allAudioTracks.filter((track) => !audioGroups || audioGroups.indexOf(track.groupId) !== -1); return findMatchingOption(option, tracks, matchPredicate) > -1; }); if (matchingVideo > -1) { return matchingVideo; } return searchDownAndUpList(levels, searchIndex, (level) => { const audioGroups = level.audioGroups; const tracks = allAudioTracks.filter((track) => !audioGroups || audioGroups.indexOf(track.groupId) !== -1); return findMatchingOption(option, tracks, matchPredicate) > -1; }); } function searchDownAndUpList(arr, searchIndex, predicate) { for (let i = searchIndex; i > -1; i--) { if (predicate(arr[i])) { return i; } } for (let i = searchIndex + 1; i < arr.length; i++) { if (predicate(arr[i])) { return i; } } return -1; } function useAlternateAudio(audioTrackUrl, hls) { var _hls$loadLevelObj; return !!audioTrackUrl && audioTrackUrl !== ((_hls$loadLevelObj = hls.loadLevelObj) == null ? void 0 : _hls$loadLevelObj.uri); } var AbrController = class extends Logger { constructor(_hls) { super("abr", _hls.logger); this.hls = void 0; this.lastLevelLoadSec = 0; this.lastLoadedFragLevel = -1; this.firstSelection = -1; this._nextAutoLevel = -1; this.nextAutoLevelKey = ""; this.audioTracksByGroup = null; this.codecTiers = null; this.timer = -1; this.fragCurrent = null; this.partCurrent = null; this.bitrateTestDelay = 0; this.rebufferNotice = -1; this.supportedCache = {}; this.bwEstimator = void 0; this._abandonRulesCheck = (levelLoaded) => { var _ref; const { fragCurrent: frag, partCurrent: part, hls } = this; const { autoLevelEnabled, media } = hls; if (!frag || !media) { return; } const now2 = performance.now(); const stats = part ? part.stats : frag.stats; const duration = part ? part.duration : frag.duration; const timeLoading = now2 - stats.loading.start; const minAutoLevel = hls.minAutoLevel; const loadingFragForLevel = frag.level; const currentAutoLevel = this._nextAutoLevel; if (stats.aborted || stats.loaded && stats.loaded === stats.total || loadingFragForLevel <= minAutoLevel) { this.clearTimer(); this._nextAutoLevel = -1; return; } if (!autoLevelEnabled) { return; } const fragBlockingSwitch = currentAutoLevel > -1 && currentAutoLevel !== loadingFragForLevel; const levelChange = !!levelLoaded || fragBlockingSwitch; if (!levelChange && (media.paused || !media.playbackRate || !media.readyState)) { return; } const bufferInfo = hls.mainForwardBufferInfo; if (!levelChange && bufferInfo === null) { return; } const ttfbEstimate = this.bwEstimator.getEstimateTTFB(); const playbackRate = Math.abs(media.playbackRate); if (timeLoading <= Math.max(ttfbEstimate, 1e3 * (duration / (playbackRate * 2)))) { return; } const bufferStarvationDelay = bufferInfo ? bufferInfo.len / playbackRate : 0; const ttfb = stats.loading.first ? stats.loading.first - stats.loading.start : -1; const loadedFirstByte = stats.loaded && ttfb > -1; const bwEstimate = this.getBwEstimate(); const levels = hls.levels; const level = levels[loadingFragForLevel]; const expectedLen = Math.max(stats.loaded, Math.round(duration * (frag.bitrate || level.averageBitrate) / 8)); let timeStreaming = loadedFirstByte ? timeLoading - ttfb : timeLoading; if (timeStreaming < 1 && loadedFirstByte) { timeStreaming = Math.min(timeLoading, stats.loaded * 8 / bwEstimate); } const loadRate = loadedFirstByte ? stats.loaded * 1e3 / timeStreaming : 0; const ttfbSeconds = ttfbEstimate / 1e3; const fragLoadedDelay = loadRate ? (expectedLen - stats.loaded) / loadRate : expectedLen * 8 / bwEstimate + ttfbSeconds; if (fragLoadedDelay <= bufferStarvationDelay) { return; } const bwe = loadRate ? loadRate * 8 : bwEstimate; const live = ((_ref = (levelLoaded == null ? void 0 : levelLoaded.details) || this.hls.latestLevelDetails) == null ? void 0 : _ref.live) === true; const abrBandWidthUpFactor = this.hls.config.abrBandWidthUpFactor; let fragLevelNextLoadedDelay = Number.POSITIVE_INFINITY; let nextLoadLevel; for (nextLoadLevel = loadingFragForLevel - 1; nextLoadLevel > minAutoLevel; nextLoadLevel--) { const levelNextBitrate = levels[nextLoadLevel].maxBitrate; const requiresLevelLoad = !levels[nextLoadLevel].details || live; fragLevelNextLoadedDelay = this.getTimeToLoadFrag(ttfbSeconds, bwe, duration * levelNextBitrate, requiresLevelLoad); if (fragLevelNextLoadedDelay < Math.min(bufferStarvationDelay, duration + ttfbSeconds)) { break; } } if (fragLevelNextLoadedDelay >= fragLoadedDelay) { return; } if (fragLevelNextLoadedDelay > duration * 10) { return; } if (loadedFirstByte) { this.bwEstimator.sample(timeLoading - Math.min(ttfbEstimate, ttfb), stats.loaded); } else { this.bwEstimator.sampleTTFB(timeLoading); } const nextLoadLevelBitrate = levels[nextLoadLevel].maxBitrate; if (this.getBwEstimate() * abrBandWidthUpFactor > nextLoadLevelBitrate) { this.resetEstimator(nextLoadLevelBitrate); } const bestSwitchLevel = this.findBestLevel(nextLoadLevelBitrate, minAutoLevel, nextLoadLevel, 0, bufferStarvationDelay, 1, 1); if (bestSwitchLevel > -1) { nextLoadLevel = bestSwitchLevel; } this.warn(`Fragment ${frag.sn}${part ? " part " + part.index : ""} of level ${loadingFragForLevel} is loading too slowly; Fragment duration: ${frag.duration.toFixed(3)} Time to underbuffer: ${bufferStarvationDelay.toFixed(3)} s Estimated load time for current fragment: ${fragLoadedDelay.toFixed(3)} s Estimated load time for down switch fragment: ${fragLevelNextLoadedDelay.toFixed(3)} s TTFB estimate: ${ttfb | 0} ms Current BW estimate: ${isFiniteNumber(bwEstimate) ? bwEstimate | 0 : "Unknown"} bps New BW estimate: ${this.getBwEstimate() | 0} bps Switching to level ${nextLoadLevel} @ ${nextLoadLevelBitrate | 0} bps`); hls.nextLoadLevel = hls.nextAutoLevel = nextLoadLevel; this.clearTimer(); const abortAndSwitch = () => { this.clearTimer(); if (this.fragCurrent === frag && this.hls.loadLevel === nextLoadLevel && nextLoadLevel > 0) { const bufferStarvationDelay2 = this.getStarvationDelay(); this.warn(`Aborting inflight request ${nextLoadLevel > 0 ? "and switching down" : ""} Fragment duration: ${frag.duration.toFixed(3)} s Time to underbuffer: ${bufferStarvationDelay2.toFixed(3)} s`); frag.abortRequests(); this.fragCurrent = this.partCurrent = null; if (nextLoadLevel > minAutoLevel) { let lowestSwitchLevel = this.findBestLevel(this.hls.levels[minAutoLevel].bitrate, minAutoLevel, nextLoadLevel, 0, bufferStarvationDelay2, 1, 1); if (lowestSwitchLevel === -1) { lowestSwitchLevel = minAutoLevel; } this.hls.nextLoadLevel = this.hls.nextAutoLevel = lowestSwitchLevel; this.resetEstimator(this.hls.levels[lowestSwitchLevel].bitrate); } } }; if (fragBlockingSwitch || fragLoadedDelay > fragLevelNextLoadedDelay * 2) { abortAndSwitch(); } else { this.timer = self.setInterval(abortAndSwitch, fragLevelNextLoadedDelay * 1e3); } hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, { frag, part, stats }); }; this.hls = _hls; this.bwEstimator = this.initEstimator(); this.registerListeners(); } resetEstimator(abrEwmaDefaultEstimate) { if (abrEwmaDefaultEstimate) { this.log(`setting initial bwe to ${abrEwmaDefaultEstimate}`); this.hls.config.abrEwmaDefaultEstimate = abrEwmaDefaultEstimate; } this.firstSelection = -1; this.bwEstimator = this.initEstimator(); } initEstimator() { const config = this.hls.config; return new EwmaBandWidthEstimator(config.abrEwmaSlowVoD, config.abrEwmaFastVoD, config.abrEwmaDefaultEstimate); } registerListeners() { const { hls } = this; hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.on(Events.FRAG_LOADING, this.onFragLoading, this); hls.on(Events.FRAG_LOADED, this.onFragLoaded, this); hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); hls.on(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this); hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); hls.on(Events.MAX_AUTO_LEVEL_UPDATED, this.onMaxAutoLevelUpdated, this); hls.on(Events.ERROR, this.onError, this); } unregisterListeners() { const { hls } = this; if (!hls) { return; } hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.off(Events.FRAG_LOADING, this.onFragLoading, this); hls.off(Events.FRAG_LOADED, this.onFragLoaded, this); hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); hls.off(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this); hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); hls.off(Events.MAX_AUTO_LEVEL_UPDATED, this.onMaxAutoLevelUpdated, this); hls.off(Events.ERROR, this.onError, this); } destroy() { this.unregisterListeners(); this.clearTimer(); this.hls = this._abandonRulesCheck = this.supportedCache = null; this.fragCurrent = this.partCurrent = null; } onManifestLoading(event, data) { this.lastLoadedFragLevel = -1; this.firstSelection = -1; this.lastLevelLoadSec = 0; this.supportedCache = {}; this.fragCurrent = this.partCurrent = null; this.onLevelsUpdated(); this.clearTimer(); } onLevelsUpdated() { if (this.lastLoadedFragLevel > -1 && this.fragCurrent) { this.lastLoadedFragLevel = this.fragCurrent.level; } this._nextAutoLevel = -1; this.onMaxAutoLevelUpdated(); this.codecTiers = null; this.audioTracksByGroup = null; } onMaxAutoLevelUpdated() { this.firstSelection = -1; this.nextAutoLevelKey = ""; } onFragLoading(event, data) { const frag = data.frag; if (this.ignoreFragment(frag)) { return; } if (!frag.bitrateTest) { var _data$part; this.fragCurrent = frag; this.partCurrent = (_data$part = data.part) != null ? _data$part : null; } this.clearTimer(); this.timer = self.setInterval(this._abandonRulesCheck, 100); } onLevelSwitching(event, data) { this.clearTimer(); } onError(event, data) { if (data.fatal) { return; } switch (data.details) { case ErrorDetails.BUFFER_ADD_CODEC_ERROR: case ErrorDetails.BUFFER_APPEND_ERROR: this.lastLoadedFragLevel = -1; this.firstSelection = -1; break; case ErrorDetails.FRAG_LOAD_TIMEOUT: { const frag = data.frag; const { fragCurrent, partCurrent: part } = this; if (frag && fragCurrent && frag.sn === fragCurrent.sn && frag.level === fragCurrent.level) { const now2 = performance.now(); const stats = part ? part.stats : frag.stats; const timeLoading = now2 - stats.loading.start; const ttfb = stats.loading.first ? stats.loading.first - stats.loading.start : -1; const loadedFirstByte = stats.loaded && ttfb > -1; if (loadedFirstByte) { const ttfbEstimate = this.bwEstimator.getEstimateTTFB(); this.bwEstimator.sample(timeLoading - Math.min(ttfbEstimate, ttfb), stats.loaded); } else { this.bwEstimator.sampleTTFB(timeLoading); } } break; } } } getTimeToLoadFrag(timeToFirstByteSec, bandwidth, fragSizeBits, isSwitch) { const fragLoadSec = timeToFirstByteSec + fragSizeBits / bandwidth; const playlistLoadSec = isSwitch ? timeToFirstByteSec + this.lastLevelLoadSec : 0; return fragLoadSec + playlistLoadSec; } onLevelLoaded(event, data) { const config = this.hls.config; const { loading } = data.stats; const timeLoadingMs = loading.end - loading.first; if (isFiniteNumber(timeLoadingMs)) { this.lastLevelLoadSec = timeLoadingMs / 1e3; } if (data.details.live) { this.bwEstimator.update(config.abrEwmaSlowLive, config.abrEwmaFastLive); } else { this.bwEstimator.update(config.abrEwmaSlowVoD, config.abrEwmaFastVoD); } if (this.timer > -1) { this._abandonRulesCheck(data.levelInfo); } } onFragLoaded(event, { frag, part }) { const stats = part ? part.stats : frag.stats; if (frag.type === PlaylistLevelType.MAIN) { this.bwEstimator.sampleTTFB(stats.loading.first - stats.loading.start); } if (this.ignoreFragment(frag)) { return; } this.clearTimer(); if (frag.level === this._nextAutoLevel) { this._nextAutoLevel = -1; } this.firstSelection = -1; if (this.hls.config.abrMaxWithRealBitrate) { const duration = part ? part.duration : frag.duration; const level = this.hls.levels[frag.level]; const loadedBytes = (level.loaded ? level.loaded.bytes : 0) + stats.loaded; const loadedDuration = (level.loaded ? level.loaded.duration : 0) + duration; level.loaded = { bytes: loadedBytes, duration: loadedDuration }; level.realBitrate = Math.round(8 * loadedBytes / loadedDuration); } if (frag.bitrateTest) { const fragBufferedData = { stats, frag, part, id: frag.type }; this.onFragBuffered(Events.FRAG_BUFFERED, fragBufferedData); frag.bitrateTest = false; } else { this.lastLoadedFragLevel = frag.level; } } onFragBuffered(event, data) { const { frag, part } = data; const stats = part != null && part.stats.loaded ? part.stats : frag.stats; if (stats.aborted) { return; } if (this.ignoreFragment(frag)) { return; } const processingMs = stats.parsing.end - stats.loading.start - Math.min(stats.loading.first - stats.loading.start, this.bwEstimator.getEstimateTTFB()); this.bwEstimator.sample(processingMs, stats.loaded); stats.bwEstimate = this.getBwEstimate(); if (frag.bitrateTest) { this.bitrateTestDelay = processingMs / 1e3; } else { this.bitrateTestDelay = 0; } } ignoreFragment(frag) { return frag.type !== PlaylistLevelType.MAIN || frag.sn === "initSegment"; } clearTimer() { if (this.timer > -1) { self.clearInterval(this.timer); this.timer = -1; } } get firstAutoLevel() { const { maxAutoLevel, minAutoLevel } = this.hls; const bwEstimate = this.getBwEstimate(); const maxStartDelay = this.hls.config.maxStarvationDelay; const abrAutoLevel = this.findBestLevel(bwEstimate, minAutoLevel, maxAutoLevel, 0, maxStartDelay, 1, 1); if (abrAutoLevel > -1) { return abrAutoLevel; } const firstLevel = this.hls.firstLevel; const clamped = Math.min(Math.max(firstLevel, minAutoLevel), maxAutoLevel); this.warn(`Could not find best starting auto level. Defaulting to first in playlist ${firstLevel} clamped to ${clamped}`); return clamped; } get forcedAutoLevel() { if (this.nextAutoLevelKey) { return -1; } return this._nextAutoLevel; } get nextAutoLevel() { const forcedAutoLevel = this.forcedAutoLevel; const bwEstimator = this.bwEstimator; const useEstimate = bwEstimator.canEstimate(); const loadedFirstFrag = this.lastLoadedFragLevel > -1; if (forcedAutoLevel !== -1 && (!useEstimate || !loadedFirstFrag || this.nextAutoLevelKey === this.getAutoLevelKey())) { return forcedAutoLevel; } const nextABRAutoLevel = useEstimate && loadedFirstFrag ? this.getNextABRAutoLevel() : this.firstAutoLevel; if (forcedAutoLevel !== -1) { const levels = this.hls.levels; if (levels.length > Math.max(forcedAutoLevel, nextABRAutoLevel) && levels[forcedAutoLevel].loadError <= levels[nextABRAutoLevel].loadError) { return forcedAutoLevel; } } this._nextAutoLevel = nextABRAutoLevel; this.nextAutoLevelKey = this.getAutoLevelKey(); return nextABRAutoLevel; } getAutoLevelKey() { return `${this.getBwEstimate()}_${this.getStarvationDelay().toFixed(2)}`; } getNextABRAutoLevel() { const { fragCurrent, partCurrent, hls } = this; if (hls.levels.length <= 1) { return hls.loadLevel; } const { maxAutoLevel, config, minAutoLevel } = hls; const currentFragDuration = partCurrent ? partCurrent.duration : fragCurrent ? fragCurrent.duration : 0; const avgbw = this.getBwEstimate(); const bufferStarvationDelay = this.getStarvationDelay(); let bwFactor = config.abrBandWidthFactor; let bwUpFactor = config.abrBandWidthUpFactor; if (bufferStarvationDelay) { const _bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, 0, bwFactor, bwUpFactor); if (_bestLevel >= 0) { this.rebufferNotice = -1; return _bestLevel; } } let maxStarvationDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxStarvationDelay) : config.maxStarvationDelay; if (!bufferStarvationDelay) { const bitrateTestDelay = this.bitrateTestDelay; if (bitrateTestDelay) { const maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay; maxStarvationDelay = maxLoadingDelay - bitrateTestDelay; this.info(`bitrate test took ${Math.round(1e3 * bitrateTestDelay)}ms, set first fragment max fetchDuration to ${Math.round(1e3 * maxStarvationDelay)} ms`); bwFactor = bwUpFactor = 1; } } const bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, maxStarvationDelay, bwFactor, bwUpFactor); if (this.rebufferNotice !== bestLevel) { this.rebufferNotice = bestLevel; this.info(`${bufferStarvationDelay ? "rebuffering expected" : "buffer is empty"}, optimal quality level ${bestLevel}`); } if (bestLevel > -1) { return bestLevel; } const minLevel = hls.levels[minAutoLevel]; const autoLevel = hls.loadLevelObj; if (autoLevel && (minLevel == null ? void 0 : minLevel.bitrate) < autoLevel.bitrate) { return minAutoLevel; } return hls.loadLevel; } getStarvationDelay() { const hls = this.hls; const media = hls.media; if (!media) { return Infinity; } const playbackRate = media && media.playbackRate !== 0 ? Math.abs(media.playbackRate) : 1; const bufferInfo = hls.mainForwardBufferInfo; return (bufferInfo ? bufferInfo.len : 0) / playbackRate; } getBwEstimate() { return this.bwEstimator.canEstimate() ? this.bwEstimator.getEstimate() : this.hls.config.abrEwmaDefaultEstimate; } findBestLevel(currentBw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, maxStarvationDelay, bwFactor, bwUpFactor) { var _this$hls$latestLevel; const maxFetchDuration = bufferStarvationDelay + maxStarvationDelay; const lastLoadedFragLevel = this.lastLoadedFragLevel; const selectionBaseLevel = lastLoadedFragLevel === -1 ? this.hls.firstLevel : lastLoadedFragLevel; const { fragCurrent, partCurrent } = this; const { levels, allAudioTracks, loadLevel, config } = this.hls; if (levels.length === 1) { return 0; } const level = levels[selectionBaseLevel]; const live = !!((_this$hls$latestLevel = this.hls.latestLevelDetails) != null && _this$hls$latestLevel.live); const firstSelection = loadLevel === -1 || lastLoadedFragLevel === -1; let currentCodecSet; let currentVideoRange = "SDR"; let currentFrameRate = (level == null ? void 0 : level.frameRate) || 0; const { audioPreference, videoPreference } = config; const audioTracksByGroup = this.audioTracksByGroup || (this.audioTracksByGroup = getAudioTracksByGroup(allAudioTracks)); let minStartIndex = -1; if (firstSelection) { if (this.firstSelection !== -1) { return this.firstSelection; } const codecTiers = this.codecTiers || (this.codecTiers = getCodecTiers(levels, audioTracksByGroup, minAutoLevel, maxAutoLevel)); const startTier = getStartCodecTier(codecTiers, currentVideoRange, currentBw, audioPreference, videoPreference); const { codecSet, videoRanges, minFramerate, minBitrate, minIndex, preferHDR } = startTier; minStartIndex = minIndex; currentCodecSet = codecSet; currentVideoRange = preferHDR ? videoRanges[videoRanges.length - 1] : videoRanges[0]; currentFrameRate = minFramerate; currentBw = Math.max(currentBw, minBitrate); this.log(`picked start tier ${stringify(startTier)}`); } else { currentCodecSet = level == null ? void 0 : level.codecSet; currentVideoRange = level == null ? void 0 : level.videoRange; } const currentFragDuration = partCurrent ? partCurrent.duration : fragCurrent ? fragCurrent.duration : 0; const ttfbEstimateSec = this.bwEstimator.getEstimateTTFB() / 1e3; const levelsSkipped = []; for (let i = maxAutoLevel; i >= minAutoLevel; i--) { var _levelInfo$supportedR, _levelInfo$supportedR2; const levelInfo = levels[i]; const upSwitch = i > selectionBaseLevel; if (!levelInfo) { continue; } if (config.useMediaCapabilities && !levelInfo.supportedResult && !levelInfo.supportedPromise) { const mediaCapabilities = navigator.mediaCapabilities; if (typeof (mediaCapabilities == null ? void 0 : mediaCapabilities.decodingInfo) === "function" && requiresMediaCapabilitiesDecodingInfo(levelInfo, audioTracksByGroup, currentVideoRange, currentFrameRate, currentBw, audioPreference)) { levelInfo.supportedPromise = getMediaDecodingInfoPromise(levelInfo, audioTracksByGroup, mediaCapabilities, this.supportedCache); levelInfo.supportedPromise.then((decodingInfo) => { if (!this.hls) { return; } levelInfo.supportedResult = decodingInfo; const levels2 = this.hls.levels; const index = levels2.indexOf(levelInfo); if (decodingInfo.error) { this.warn(`MediaCapabilities decodingInfo error: "${decodingInfo.error}" for level ${index} ${stringify(decodingInfo)}`); } else if (!decodingInfo.supported) { this.warn(`Unsupported MediaCapabilities decodingInfo result for level ${index} ${stringify(decodingInfo)}`); if (index > -1 && levels2.length > 1) { this.log(`Removing unsupported level ${index}`); this.hls.removeLevel(index); if (this.hls.loadLevel === -1) { this.hls.nextLoadLevel = 0; } } } else if (decodingInfo.decodingInfoResults.some((info) => info.smooth === false || info.powerEfficient === false)) { this.log(`MediaCapabilities decodingInfo for level ${index} not smooth or powerEfficient: ${stringify(decodingInfo)}`); } }); } else { levelInfo.supportedResult = SUPPORTED_INFO_DEFAULT; } } if (currentCodecSet && levelInfo.codecSet !== currentCodecSet || currentVideoRange && levelInfo.videoRange !== currentVideoRange || upSwitch && currentFrameRate > levelInfo.frameRate || !upSwitch && currentFrameRate > 0 && currentFrameRate < levelInfo.frameRate || (_levelInfo$supportedR = levelInfo.supportedResult) != null && (_levelInfo$supportedR2 = _levelInfo$supportedR.decodingInfoResults) != null && _levelInfo$supportedR2.some((info) => info.smooth === false)) { if (!firstSelection || i !== minStartIndex) { levelsSkipped.push(i); continue; } } const levelDetails = levelInfo.details; const avgDuration = (partCurrent ? levelDetails == null ? void 0 : levelDetails.partTarget : levelDetails == null ? void 0 : levelDetails.averagetargetduration) || currentFragDuration; let adjustedbw; if (!upSwitch) { adjustedbw = bwFactor * currentBw; } else { adjustedbw = bwUpFactor * currentBw; } const bitrate = currentFragDuration && bufferStarvationDelay >= currentFragDuration * 2 && maxStarvationDelay === 0 ? levelInfo.averageBitrate : levelInfo.maxBitrate; const fetchDuration = this.getTimeToLoadFrag(ttfbEstimateSec, adjustedbw, bitrate * avgDuration, levelDetails === void 0); const canSwitchWithinTolerance = adjustedbw >= bitrate && (i === lastLoadedFragLevel || levelInfo.loadError === 0 && levelInfo.fragmentError === 0) && (fetchDuration <= ttfbEstimateSec || !isFiniteNumber(fetchDuration) || live && !this.bitrateTestDelay || fetchDuration < maxFetchDuration); if (canSwitchWithinTolerance) { const forcedAutoLevel = this.forcedAutoLevel; if (i !== loadLevel && (forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)) { if (levelsSkipped.length) { this.trace(`Skipped level(s) ${levelsSkipped.join(",")} of ${maxAutoLevel} max with CODECS and VIDEO-RANGE:"${levels[levelsSkipped[0]].codecs}" ${levels[levelsSkipped[0]].videoRange}; not compatible with "${currentCodecSet}" ${currentVideoRange}`); } this.info(`switch candidate:${selectionBaseLevel}->${i} adjustedbw(${Math.round(adjustedbw)})-bitrate=${Math.round(adjustedbw - bitrate)} ttfb:${ttfbEstimateSec.toFixed(1)} avgDuration:${avgDuration.toFixed(1)} maxFetchDuration:${maxFetchDuration.toFixed(1)} fetchDuration:${fetchDuration.toFixed(1)} firstSelection:${firstSelection} codecSet:${levelInfo.codecSet} videoRange:${levelInfo.videoRange} hls.loadLevel:${loadLevel}`); } if (firstSelection) { this.firstSelection = i; } return i; } } return -1; } set nextAutoLevel(nextLevel) { const value = this.deriveNextAutoLevel(nextLevel); if (this._nextAutoLevel !== value) { this.nextAutoLevelKey = ""; this._nextAutoLevel = value; } } deriveNextAutoLevel(nextLevel) { const { maxAutoLevel, minAutoLevel } = this.hls; return Math.min(Math.max(nextLevel, minAutoLevel), maxAutoLevel); } }; var BinarySearch = { search: function(list, comparisonFn) { let minIndex = 0; let maxIndex = list.length - 1; let currentIndex = null; let currentElement = null; while (minIndex <= maxIndex) { currentIndex = (minIndex + maxIndex) / 2 | 0; currentElement = list[currentIndex]; const comparisonResult = comparisonFn(currentElement); if (comparisonResult > 0) { minIndex = currentIndex + 1; } else if (comparisonResult < 0) { maxIndex = currentIndex - 1; } else { return currentElement; } } return null; } }; function findFragmentByPDT(fragments, PDTValue, maxFragLookUpTolerance) { if (PDTValue === null || !Array.isArray(fragments) || !fragments.length || !isFiniteNumber(PDTValue)) { return null; } const startPDT = fragments[0].programDateTime; if (PDTValue < (startPDT || 0)) { return null; } const endPDT = fragments[fragments.length - 1].endProgramDateTime; if (PDTValue >= (endPDT || 0)) { return null; } for (let seg = 0; seg < fragments.length; ++seg) { const frag = fragments[seg]; if (pdtWithinToleranceTest(PDTValue, maxFragLookUpTolerance, frag)) { return frag; } } return null; } function findFragmentByPTS(fragPrevious, fragments, bufferEnd = 0, maxFragLookUpTolerance = 0, nextFragLookupTolerance = 5e-3) { let fragNext = null; if (fragPrevious) { fragNext = fragments[1 + fragPrevious.sn - fragments[0].sn] || null; const bufferEdgeError = fragPrevious.endDTS - bufferEnd; if (bufferEdgeError > 0 && bufferEdgeError < 15e-7) { bufferEnd += 15e-7; } if (fragNext && fragPrevious.level !== fragNext.level && fragNext.end <= fragPrevious.end) { fragNext = fragments[2 + fragPrevious.sn - fragments[0].sn] || null; } } else if (bufferEnd === 0 && fragments[0].start === 0) { fragNext = fragments[0]; } if (fragNext && ((!fragPrevious || fragPrevious.level === fragNext.level) && fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, fragNext) === 0 || fragmentWithinFastStartSwitch(fragNext, fragPrevious, Math.min(nextFragLookupTolerance, maxFragLookUpTolerance)))) { return fragNext; } const foundFragment = BinarySearch.search(fragments, fragmentWithinToleranceTest.bind(null, bufferEnd, maxFragLookUpTolerance)); if (foundFragment && (foundFragment !== fragPrevious || !fragNext)) { return foundFragment; } return fragNext; } function fragmentWithinFastStartSwitch(fragNext, fragPrevious, nextFragLookupTolerance) { if (fragPrevious && fragPrevious.start === 0 && fragPrevious.level < fragNext.level && (fragPrevious.endPTS || 0) > 0) { const firstDuration = fragPrevious.tagList.reduce((duration, tag) => { if (tag[0] === "INF") { duration += parseFloat(tag[1]); } return duration; }, nextFragLookupTolerance); return fragNext.start <= firstDuration; } return false; } function fragmentWithinToleranceTest(bufferEnd = 0, maxFragLookUpTolerance = 0, candidate) { if (candidate.start <= bufferEnd && candidate.start + candidate.duration > bufferEnd) { return 0; } const candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)); if (candidate.start + candidate.duration - candidateLookupTolerance <= bufferEnd) { return 1; } else if (candidate.start - candidateLookupTolerance > bufferEnd && candidate.start) { return -1; } return 0; } function pdtWithinToleranceTest(pdtBufferEnd, maxFragLookUpTolerance, candidate) { const candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)) * 1e3; const endProgramDateTime = candidate.endProgramDateTime || 0; return endProgramDateTime - candidateLookupTolerance > pdtBufferEnd; } function findNearestWithCC(details, cc, pos) { if (details) { if (details.startCC <= cc && details.endCC >= cc) { let fragments = details.fragments; const { fragmentHint } = details; if (fragmentHint) { fragments = fragments.concat(fragmentHint); } let closest; BinarySearch.search(fragments, (candidate) => { if (candidate.cc < cc) { return 1; } if (candidate.cc > cc) { return -1; } closest = candidate; if (candidate.end <= pos) { return 1; } if (candidate.start > pos) { return -1; } return 0; }); return closest || null; } } return null; } function isTimeoutError(error) { switch (error.details) { case ErrorDetails.FRAG_LOAD_TIMEOUT: case ErrorDetails.KEY_LOAD_TIMEOUT: case ErrorDetails.LEVEL_LOAD_TIMEOUT: case ErrorDetails.MANIFEST_LOAD_TIMEOUT: return true; } return false; } function getRetryConfig(loadPolicy, error) { const isTimeout = isTimeoutError(error); return loadPolicy.default[`${isTimeout ? "timeout" : "error"}Retry`]; } function getRetryDelay(retryConfig, retryCount) { const backoffFactor = retryConfig.backoff === "linear" ? 1 : Math.pow(2, retryCount); return Math.min(backoffFactor * retryConfig.retryDelayMs, retryConfig.maxRetryDelayMs); } function getLoaderConfigWithoutReties(loderConfig) { return _objectSpread2(_objectSpread2({}, loderConfig), { errorRetry: null, timeoutRetry: null }); } function shouldRetry(retryConfig, retryCount, isTimeout, loaderResponse) { if (!retryConfig) { return false; } const httpStatus = loaderResponse == null ? void 0 : loaderResponse.code; const retry = retryCount < retryConfig.maxNumRetry && (retryForHttpStatus(httpStatus) || !!isTimeout); return retryConfig.shouldRetry ? retryConfig.shouldRetry(retryConfig, retryCount, isTimeout, loaderResponse, retry) : retry; } function retryForHttpStatus(httpStatus) { return httpStatus === 0 && navigator.onLine === false || !!httpStatus && (httpStatus < 400 || httpStatus > 499); } var NetworkErrorAction = { DoNothing: 0, SendEndCallback: 1, SendAlternateToPenaltyBox: 2, RemoveAlternatePermanently: 3, InsertDiscontinuity: 4, RetryRequest: 5 }; var ErrorActionFlags = { None: 0, MoveAllAlternatesMatchingHost: 1, MoveAllAlternatesMatchingHDCP: 2, SwitchToSDR: 4 }; var ErrorController = class extends Logger { constructor(hls) { super("error-controller", hls.logger); this.hls = void 0; this.playlistError = 0; this.penalizedRenditions = {}; this.hls = hls; this.registerListeners(); } registerListeners() { const hls = this.hls; hls.on(Events.ERROR, this.onError, this); hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); } unregisterListeners() { const hls = this.hls; if (!hls) { return; } hls.off(Events.ERROR, this.onError, this); hls.off(Events.ERROR, this.onErrorOut, this); hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); } destroy() { this.unregisterListeners(); this.hls = null; this.penalizedRenditions = {}; } startLoad(startPosition) { } stopLoad() { this.playlistError = 0; } getVariantLevelIndex(frag) { return (frag == null ? void 0 : frag.type) === PlaylistLevelType.MAIN ? frag.level : this.hls.loadLevel; } onManifestLoading() { this.playlistError = 0; this.penalizedRenditions = {}; } onLevelUpdated() { this.playlistError = 0; } onError(event, data) { var _data$frag; if (data.fatal) { return; } const hls = this.hls; const context = data.context; switch (data.details) { case ErrorDetails.FRAG_LOAD_ERROR: case ErrorDetails.FRAG_LOAD_TIMEOUT: case ErrorDetails.KEY_LOAD_ERROR: case ErrorDetails.KEY_LOAD_TIMEOUT: data.errorAction = this.getFragRetryOrSwitchAction(data); return; case ErrorDetails.FRAG_PARSING_ERROR: if ((_data$frag = data.frag) != null && _data$frag.gap) { data.errorAction = createDoNothingErrorAction(); return; } case ErrorDetails.FRAG_GAP: case ErrorDetails.FRAG_DECRYPT_ERROR: { data.errorAction = this.getFragRetryOrSwitchAction(data); data.errorAction.action = NetworkErrorAction.SendAlternateToPenaltyBox; return; } case ErrorDetails.LEVEL_EMPTY_ERROR: case ErrorDetails.LEVEL_PARSING_ERROR: { var _data$context, _data$context$levelDe; const levelIndex = data.parent === PlaylistLevelType.MAIN ? data.level : hls.loadLevel; if (data.details === ErrorDetails.LEVEL_EMPTY_ERROR && !!((_data$context = data.context) != null && (_data$context$levelDe = _data$context.levelDetails) != null && _data$context$levelDe.live)) { data.errorAction = this.getPlaylistRetryOrSwitchAction(data, levelIndex); } else { data.levelRetry = false; data.errorAction = this.getLevelSwitchAction(data, levelIndex); } } return; case ErrorDetails.LEVEL_LOAD_ERROR: case ErrorDetails.LEVEL_LOAD_TIMEOUT: if (typeof (context == null ? void 0 : context.level) === "number") { data.errorAction = this.getPlaylistRetryOrSwitchAction(data, context.level); } return; case ErrorDetails.AUDIO_TRACK_LOAD_ERROR: case ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT: case ErrorDetails.SUBTITLE_LOAD_ERROR: case ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT: if (context) { const level = hls.loadLevelObj; if (level && (context.type === PlaylistContextType.AUDIO_TRACK && level.hasAudioGroup(context.groupId) || context.type === PlaylistContextType.SUBTITLE_TRACK && level.hasSubtitleGroup(context.groupId))) { data.errorAction = this.getPlaylistRetryOrSwitchAction(data, hls.loadLevel); data.errorAction.action = NetworkErrorAction.SendAlternateToPenaltyBox; data.errorAction.flags = ErrorActionFlags.MoveAllAlternatesMatchingHost; return; } } return; case ErrorDetails.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED: { const level = hls.loadLevelObj; const restrictedHdcpLevel = level == null ? void 0 : level.attrs["HDCP-LEVEL"]; if (restrictedHdcpLevel) { data.errorAction = { action: NetworkErrorAction.SendAlternateToPenaltyBox, flags: ErrorActionFlags.MoveAllAlternatesMatchingHDCP, hdcpLevel: restrictedHdcpLevel }; } else { this.keySystemError(data); } } return; case ErrorDetails.BUFFER_ADD_CODEC_ERROR: case ErrorDetails.REMUX_ALLOC_ERROR: case ErrorDetails.BUFFER_APPEND_ERROR: if (!data.errorAction) { var _data$level; data.errorAction = this.getLevelSwitchAction(data, (_data$level = data.level) != null ? _data$level : hls.loadLevel); } return; case ErrorDetails.INTERNAL_EXCEPTION: case ErrorDetails.BUFFER_APPENDING_ERROR: case ErrorDetails.BUFFER_FULL_ERROR: case ErrorDetails.LEVEL_SWITCH_ERROR: case ErrorDetails.BUFFER_STALLED_ERROR: case ErrorDetails.BUFFER_SEEK_OVER_HOLE: case ErrorDetails.BUFFER_NUDGE_ON_STALL: data.errorAction = createDoNothingErrorAction(); return; } if (data.type === ErrorTypes.KEY_SYSTEM_ERROR) { this.keySystemError(data); } } keySystemError(data) { const levelIndex = this.getVariantLevelIndex(data.frag); data.levelRetry = false; data.errorAction = this.getLevelSwitchAction(data, levelIndex); } getPlaylistRetryOrSwitchAction(data, levelIndex) { const hls = this.hls; const retryConfig = getRetryConfig(hls.config.playlistLoadPolicy, data); const retryCount = this.playlistError++; const retry = shouldRetry(retryConfig, retryCount, isTimeoutError(data), data.response); if (retry) { return { action: NetworkErrorAction.RetryRequest, flags: ErrorActionFlags.None, retryConfig, retryCount }; } const errorAction = this.getLevelSwitchAction(data, levelIndex); if (retryConfig) { errorAction.retryConfig = retryConfig; errorAction.retryCount = retryCount; } return errorAction; } getFragRetryOrSwitchAction(data) { const hls = this.hls; const variantLevelIndex = this.getVariantLevelIndex(data.frag); const level = hls.levels[variantLevelIndex]; const { fragLoadPolicy, keyLoadPolicy } = hls.config; const retryConfig = getRetryConfig(data.details.startsWith("key") ? keyLoadPolicy : fragLoadPolicy, data); const fragmentErrors = hls.levels.reduce((acc, level2) => acc + level2.fragmentError, 0); if (level) { if (data.details !== ErrorDetails.FRAG_GAP) { level.fragmentError++; } const retry = shouldRetry(retryConfig, fragmentErrors, isTimeoutError(data), data.response); if (retry) { return { action: NetworkErrorAction.RetryRequest, flags: ErrorActionFlags.None, retryConfig, retryCount: fragmentErrors }; } } const errorAction = this.getLevelSwitchAction(data, variantLevelIndex); if (retryConfig) { errorAction.retryConfig = retryConfig; errorAction.retryCount = fragmentErrors; } return errorAction; } getLevelSwitchAction(data, levelIndex) { const hls = this.hls; if (levelIndex === null || levelIndex === void 0) { levelIndex = hls.loadLevel; } const level = this.hls.levels[levelIndex]; if (level) { var _data$frag2, _data$context2; const errorDetails = data.details; level.loadError++; if (errorDetails === ErrorDetails.BUFFER_APPEND_ERROR) { level.fragmentError++; } let nextLevel = -1; const { levels, loadLevel, minAutoLevel, maxAutoLevel } = hls; if (!hls.autoLevelEnabled && !hls.config.preserveManualLevelOnError) { hls.loadLevel = -1; } const fragErrorType = (_data$frag2 = data.frag) == null ? void 0 : _data$frag2.type; const isAudioCodecError = fragErrorType === PlaylistLevelType.AUDIO && errorDetails === ErrorDetails.FRAG_PARSING_ERROR || data.sourceBufferName === "audio" && (errorDetails === ErrorDetails.BUFFER_ADD_CODEC_ERROR || errorDetails === ErrorDetails.BUFFER_APPEND_ERROR); const findAudioCodecAlternate = isAudioCodecError && levels.some(({ audioCodec }) => level.audioCodec !== audioCodec); const isVideoCodecError = data.sourceBufferName === "video" && (errorDetails === ErrorDetails.BUFFER_ADD_CODEC_ERROR || errorDetails === ErrorDetails.BUFFER_APPEND_ERROR); const findVideoCodecAlternate = isVideoCodecError && levels.some(({ codecSet, audioCodec }) => level.codecSet !== codecSet && level.audioCodec === audioCodec); const { type: playlistErrorType, groupId: playlistErrorGroupId } = (_data$context2 = data.context) != null ? _data$context2 : {}; for (let i = levels.length; i--; ) { const candidate = (i + loadLevel) % levels.length; if (candidate !== loadLevel && candidate >= minAutoLevel && candidate <= maxAutoLevel && levels[candidate].loadError === 0) { var _level$audioGroups, _level$subtitleGroups; const levelCandidate = levels[candidate]; if (errorDetails === ErrorDetails.FRAG_GAP && fragErrorType === PlaylistLevelType.MAIN && data.frag) { const levelDetails = levels[candidate].details; if (levelDetails) { const fragCandidate = findFragmentByPTS(data.frag, levelDetails.fragments, data.frag.start); if (fragCandidate != null && fragCandidate.gap) { continue; } } } else if (playlistErrorType === PlaylistContextType.AUDIO_TRACK && levelCandidate.hasAudioGroup(playlistErrorGroupId) || playlistErrorType === PlaylistContextType.SUBTITLE_TRACK && levelCandidate.hasSubtitleGroup(playlistErrorGroupId)) { continue; } else if (fragErrorType === PlaylistLevelType.AUDIO && (_level$audioGroups = level.audioGroups) != null && _level$audioGroups.some((groupId) => levelCandidate.hasAudioGroup(groupId)) || fragErrorType === PlaylistLevelType.SUBTITLE && (_level$subtitleGroups = level.subtitleGroups) != null && _level$subtitleGroups.some((groupId) => levelCandidate.hasSubtitleGroup(groupId)) || findAudioCodecAlternate && level.audioCodec === levelCandidate.audioCodec || !findAudioCodecAlternate && level.audioCodec !== levelCandidate.audioCodec || findVideoCodecAlternate && level.codecSet === levelCandidate.codecSet) { continue; } nextLevel = candidate; break; } } if (nextLevel > -1 && hls.loadLevel !== nextLevel) { data.levelRetry = true; this.playlistError = 0; return { action: NetworkErrorAction.SendAlternateToPenaltyBox, flags: ErrorActionFlags.None, nextAutoLevel: nextLevel }; } } return { action: NetworkErrorAction.SendAlternateToPenaltyBox, flags: ErrorActionFlags.MoveAllAlternatesMatchingHost }; } onErrorOut(event, data) { var _data$errorAction; switch ((_data$errorAction = data.errorAction) == null ? void 0 : _data$errorAction.action) { case NetworkErrorAction.DoNothing: break; case NetworkErrorAction.SendAlternateToPenaltyBox: this.sendAlternateToPenaltyBox(data); if (!data.errorAction.resolved && data.details !== ErrorDetails.FRAG_GAP) { data.fatal = true; } else if (/MediaSource readyState: ended/.test(data.error.message)) { this.warn(`MediaSource ended after "${data.sourceBufferName}" sourceBuffer append error. Attempting to recover from media error.`); this.hls.recoverMediaError(); } break; case NetworkErrorAction.RetryRequest: break; } if (data.fatal) { this.hls.stopLoad(); return; } } sendAlternateToPenaltyBox(data) { const hls = this.hls; const errorAction = data.errorAction; if (!errorAction) { return; } const { flags, hdcpLevel, nextAutoLevel } = errorAction; switch (flags) { case ErrorActionFlags.None: this.switchLevel(data, nextAutoLevel); break; case ErrorActionFlags.MoveAllAlternatesMatchingHDCP: if (hdcpLevel) { hls.maxHdcpLevel = HdcpLevels[HdcpLevels.indexOf(hdcpLevel) - 1]; errorAction.resolved = true; } this.warn(`Restricting playback to HDCP-LEVEL of "${hls.maxHdcpLevel}" or lower`); break; } if (!errorAction.resolved) { this.switchLevel(data, nextAutoLevel); } } switchLevel(data, levelIndex) { if (levelIndex !== void 0 && data.errorAction) { this.warn(`switching to level ${levelIndex} after ${data.details}`); this.hls.nextAutoLevel = levelIndex; data.errorAction.resolved = true; this.hls.nextLoadLevel = this.hls.nextAutoLevel; if (data.details === ErrorDetails.BUFFER_ADD_CODEC_ERROR && data.mimeType && data.sourceBufferName !== "audiovideo") { const codec = getCodecsForMimeType(data.mimeType); const levels = this.hls.levels; for (let i = levels.length; i--; ) { if (levels[i][`${data.sourceBufferName}Codec`] === codec) { this.hls.removeLevel(i); } } } } } }; function createDoNothingErrorAction(resolved) { const errorAction = { action: NetworkErrorAction.DoNothing, flags: ErrorActionFlags.None }; if (resolved) { errorAction.resolved = true; } return errorAction; } var FragmentState = { NOT_LOADED: "NOT_LOADED", APPENDING: "APPENDING", PARTIAL: "PARTIAL", OK: "OK" }; var FragmentTracker = class { constructor(hls) { this.activePartLists = /* @__PURE__ */ Object.create(null); this.endListFragments = /* @__PURE__ */ Object.create(null); this.fragments = /* @__PURE__ */ Object.create(null); this.timeRanges = /* @__PURE__ */ Object.create(null); this.bufferPadding = 0.2; this.hls = void 0; this.hasGaps = false; this.hls = hls; this._registerListeners(); } _registerListeners() { const { hls } = this; hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this); hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); hls.on(Events.FRAG_LOADED, this.onFragLoaded, this); } _unregisterListeners() { const { hls } = this; hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this); hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); hls.off(Events.FRAG_LOADED, this.onFragLoaded, this); } destroy() { this._unregisterListeners(); this.fragments = this.activePartLists = this.endListFragments = this.timeRanges = null; } getAppendedFrag(position, levelType) { const activeParts = this.activePartLists[levelType]; if (activeParts) { for (let i = activeParts.length; i--; ) { const activePart = activeParts[i]; if (!activePart) { break; } const appendedPTS = activePart.end; if (activePart.start <= position && appendedPTS !== null && position <= appendedPTS) { return activePart; } } } return this.getBufferedFrag(position, levelType); } getBufferedFrag(position, levelType) { return this.getFragAtPos(position, levelType, true); } getFragAtPos(position, levelType, buffered) { const { fragments } = this; const keys = Object.keys(fragments); for (let i = keys.length; i--; ) { const fragmentEntity = fragments[keys[i]]; if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && (!buffered || fragmentEntity.buffered)) { const frag = fragmentEntity.body; if (frag.start <= position && position <= frag.end) { return frag; } } } return null; } detectEvictedFragments(elementaryStream, timeRange, playlistType, appendedPart, removeAppending) { if (this.timeRanges) { this.timeRanges[elementaryStream] = timeRange; } const appendedPartSn = (appendedPart == null ? void 0 : appendedPart.fragment.sn) || -1; Object.keys(this.fragments).forEach((key) => { const fragmentEntity = this.fragments[key]; if (!fragmentEntity) { return; } if (appendedPartSn >= fragmentEntity.body.sn) { return; } if (!fragmentEntity.buffered && (!fragmentEntity.loaded || removeAppending)) { if (fragmentEntity.body.type === playlistType) { this.removeFragment(fragmentEntity.body); } return; } const esData = fragmentEntity.range[elementaryStream]; if (!esData) { return; } if (esData.time.length === 0) { this.removeFragment(fragmentEntity.body); return; } esData.time.some((time) => { const isNotBuffered = !this.isTimeBuffered(time.startPTS, time.endPTS, timeRange); if (isNotBuffered) { this.removeFragment(fragmentEntity.body); } return isNotBuffered; }); }); } detectPartialFragments(data) { const timeRanges = this.timeRanges; if (!timeRanges || data.frag.sn === "initSegment") { return; } const frag = data.frag; const fragKey = getFragmentKey(frag); const fragmentEntity = this.fragments[fragKey]; if (!fragmentEntity || fragmentEntity.buffered && frag.gap) { return; } const isFragHint = !frag.relurl; Object.keys(timeRanges).forEach((elementaryStream) => { const streamInfo = frag.elementaryStreams[elementaryStream]; if (!streamInfo) { return; } const timeRange = timeRanges[elementaryStream]; const partial = isFragHint || streamInfo.partial === true; fragmentEntity.range[elementaryStream] = this.getBufferedTimes(frag, data.part, partial, timeRange); }); fragmentEntity.loaded = null; if (Object.keys(fragmentEntity.range).length) { fragmentEntity.buffered = true; const endList = fragmentEntity.body.endList = frag.endList || fragmentEntity.body.endList; if (endList) { this.endListFragments[fragmentEntity.body.type] = fragmentEntity; } if (!isPartial(fragmentEntity)) { this.removeParts(frag.sn - 1, frag.type); } } else { this.removeFragment(fragmentEntity.body); } } removeParts(snToKeep, levelType) { const activeParts = this.activePartLists[levelType]; if (!activeParts) { return; } this.activePartLists[levelType] = filterParts(activeParts, (part) => part.fragment.sn >= snToKeep); } fragBuffered(frag, force) { const fragKey = getFragmentKey(frag); let fragmentEntity = this.fragments[fragKey]; if (!fragmentEntity && force) { fragmentEntity = this.fragments[fragKey] = { body: frag, appendedPTS: null, loaded: null, buffered: false, range: /* @__PURE__ */ Object.create(null) }; if (frag.gap) { this.hasGaps = true; } } if (fragmentEntity) { fragmentEntity.loaded = null; fragmentEntity.buffered = true; } } getBufferedTimes(fragment, part, partial, timeRange) { const buffered = { time: [], partial }; const startPTS = fragment.start; const endPTS = fragment.end; const minEndPTS = fragment.minEndPTS || endPTS; const maxStartPTS = fragment.maxStartPTS || startPTS; for (let i = 0; i < timeRange.length; i++) { const startTime = timeRange.start(i) - this.bufferPadding; const endTime = timeRange.end(i) + this.bufferPadding; if (maxStartPTS >= startTime && minEndPTS <= endTime) { buffered.time.push({ startPTS: Math.max(startPTS, timeRange.start(i)), endPTS: Math.min(endPTS, timeRange.end(i)) }); break; } else if (startPTS < endTime && endPTS > startTime) { const start = Math.max(startPTS, timeRange.start(i)); const end = Math.min(endPTS, timeRange.end(i)); if (end > start) { buffered.partial = true; buffered.time.push({ startPTS: start, endPTS: end }); } } else if (endPTS <= startTime) { break; } } return buffered; } getPartialFragment(time) { let bestFragment = null; let timePadding; let startTime; let endTime; let bestOverlap = 0; const { bufferPadding, fragments } = this; Object.keys(fragments).forEach((key) => { const fragmentEntity = fragments[key]; if (!fragmentEntity) { return; } if (isPartial(fragmentEntity)) { startTime = fragmentEntity.body.start - bufferPadding; endTime = fragmentEntity.body.end + bufferPadding; if (time >= startTime && time <= endTime) { timePadding = Math.min(time - startTime, endTime - time); if (bestOverlap <= timePadding) { bestFragment = fragmentEntity.body; bestOverlap = timePadding; } } } }); return bestFragment; } isEndListAppended(type) { const lastFragmentEntity = this.endListFragments[type]; return lastFragmentEntity !== void 0 && (lastFragmentEntity.buffered || isPartial(lastFragmentEntity)); } getState(fragment) { const fragKey = getFragmentKey(fragment); const fragmentEntity = this.fragments[fragKey]; if (fragmentEntity) { if (!fragmentEntity.buffered) { return FragmentState.APPENDING; } else if (isPartial(fragmentEntity)) { return FragmentState.PARTIAL; } else { return FragmentState.OK; } } return FragmentState.NOT_LOADED; } isTimeBuffered(startPTS, endPTS, timeRange) { let startTime; let endTime; for (let i = 0; i < timeRange.length; i++) { startTime = timeRange.start(i) - this.bufferPadding; endTime = timeRange.end(i) + this.bufferPadding; if (startPTS >= startTime && endPTS <= endTime) { return true; } if (endPTS <= startTime) { return false; } } return false; } onManifestLoading() { this.removeAllFragments(); } onFragLoaded(event, data) { if (data.frag.sn === "initSegment" || data.frag.bitrateTest) { return; } const frag = data.frag; const loaded = data.part ? null : data; const fragKey = getFragmentKey(frag); this.fragments[fragKey] = { body: frag, appendedPTS: null, loaded, buffered: false, range: /* @__PURE__ */ Object.create(null) }; } onBufferAppended(event, data) { const { frag, part, timeRanges, type } = data; if (frag.sn === "initSegment") { return; } const playlistType = frag.type; if (part) { let activeParts = this.activePartLists[playlistType]; if (!activeParts) { this.activePartLists[playlistType] = activeParts = []; } activeParts.push(part); } this.timeRanges = timeRanges; const timeRange = timeRanges[type]; this.detectEvictedFragments(type, timeRange, playlistType, part); } onFragBuffered(event, data) { this.detectPartialFragments(data); } hasFragment(fragment) { const fragKey = getFragmentKey(fragment); return !!this.fragments[fragKey]; } hasFragments(type) { const { fragments } = this; const keys = Object.keys(fragments); if (!type) { return keys.length > 0; } for (let i = keys.length; i--; ) { const fragmentEntity = fragments[keys[i]]; if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === type) { return true; } } return false; } hasParts(type) { var _this$activePartLists; return !!((_this$activePartLists = this.activePartLists[type]) != null && _this$activePartLists.length); } removeFragmentsInRange(start, end, playlistType, withGapOnly, unbufferedOnly) { if (withGapOnly && !this.hasGaps) { return; } Object.keys(this.fragments).forEach((key) => { const fragmentEntity = this.fragments[key]; if (!fragmentEntity) { return; } const frag = fragmentEntity.body; if (frag.type !== playlistType || withGapOnly && !frag.gap) { return; } if (frag.start < end && frag.end > start && (fragmentEntity.buffered || unbufferedOnly)) { this.removeFragment(frag); } }); } removeFragment(fragment) { const fragKey = getFragmentKey(fragment); fragment.clearElementaryStreamInfo(); const activeParts = this.activePartLists[fragment.type]; if (activeParts) { const snToRemove = fragment.sn; this.activePartLists[fragment.type] = filterParts(activeParts, (part) => part.fragment.sn !== snToRemove); } delete this.fragments[fragKey]; if (fragment.endList) { delete this.endListFragments[fragment.type]; } } removeAllFragments() { var _this$hls, _this$hls$latestLevel; this.fragments = /* @__PURE__ */ Object.create(null); this.endListFragments = /* @__PURE__ */ Object.create(null); this.activePartLists = /* @__PURE__ */ Object.create(null); this.hasGaps = false; const partlist = (_this$hls = this.hls) == null ? void 0 : (_this$hls$latestLevel = _this$hls.latestLevelDetails) == null ? void 0 : _this$hls$latestLevel.partList; if (partlist) { partlist.forEach((part) => part.clearElementaryStreamInfo()); } } }; function isPartial(fragmentEntity) { var _fragmentEntity$range, _fragmentEntity$range2, _fragmentEntity$range3; return fragmentEntity.buffered && (fragmentEntity.body.gap || ((_fragmentEntity$range = fragmentEntity.range.video) == null ? void 0 : _fragmentEntity$range.partial) || ((_fragmentEntity$range2 = fragmentEntity.range.audio) == null ? void 0 : _fragmentEntity$range2.partial) || ((_fragmentEntity$range3 = fragmentEntity.range.audiovideo) == null ? void 0 : _fragmentEntity$range3.partial)); } function getFragmentKey(fragment) { return `${fragment.type}_${fragment.level}_${fragment.sn}`; } function filterParts(partList, predicate) { return partList.filter((part) => { const keep = predicate(part); if (!keep) { part.clearElementaryStreamInfo(); } return keep; }); } var DecrypterAesMode = { cbc: 0, ctr: 1 }; var AESCrypto = class { constructor(subtle, iv, aesMode) { this.subtle = void 0; this.aesIV = void 0; this.aesMode = void 0; this.subtle = subtle; this.aesIV = iv; this.aesMode = aesMode; } decrypt(data, key) { switch (this.aesMode) { case DecrypterAesMode.cbc: return this.subtle.decrypt({ name: "AES-CBC", iv: this.aesIV }, key, data); case DecrypterAesMode.ctr: return this.subtle.decrypt({ name: "AES-CTR", counter: this.aesIV, length: 64 }, key, data); default: throw new Error(`[AESCrypto] invalid aes mode ${this.aesMode}`); } } }; function removePadding(array) { const outputBytes = array.byteLength; const paddingBytes = outputBytes && new DataView(array.buffer).getUint8(outputBytes - 1); if (paddingBytes) { return array.slice(0, outputBytes - paddingBytes); } return array; } var AESDecryptor = class { constructor() { 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.ksRows = 0; this.keySize = 0; this.keySchedule = void 0; this.invKeySchedule = void 0; this.initTable(); } uint8ArrayToUint32Array_(arrayBuffer) { const view2 = new DataView(arrayBuffer); const newArray = new Uint32Array(4); for (let i = 0; i < 4; i++) { newArray[i] = view2.getUint32(i * 4); } return newArray; } initTable() { const sBox = this.sBox; const invSBox = this.invSBox; const subMix = this.subMix; const subMix0 = subMix[0]; const subMix1 = subMix[1]; const subMix2 = subMix[2]; const subMix3 = subMix[3]; const invSubMix = this.invSubMix; 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; const x2 = d[x]; const x4 = d[x2]; const x8 = d[x4]; 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; 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; if (!x) { x = xi = 1; } else { x = x2 ^ d[d[d[x8 ^ x2]]]; xi ^= d[d[xi]]; } } } expandKey(keyBuffer) { 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.rcon; const invSubMix = this.invSubMix; 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) { t = t << 8 | t >>> 24; t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 255] << 16 | sbox[t >>> 8 & 255] << 8 | sbox[t & 255]; t ^= rcon[ksRow / keySize | 0] << 24; } else if (keySize > 6 && ksRow % keySize === 4) { 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; } } 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.invKeySchedule; const invSBOX = this.invSBox; const invSubMix = this.invSubMix; 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; 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]; s0 = t0; s1 = t1; s2 = t2; s3 = t3; ksRow = ksRow + 4; } 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]; outputInt32[offset] = swapWord(t0 ^ initVector0); outputInt32[offset + 1] = swapWord(t3 ^ initVector1); outputInt32[offset + 2] = swapWord(t2 ^ initVector2); outputInt32[offset + 3] = swapWord(t1 ^ initVector3); initVector0 = inputWords0; initVector1 = inputWords1; initVector2 = inputWords2; initVector3 = inputWords3; offset = offset + 4; } return outputInt32.buffer; } }; var FastAESKey = class { constructor(subtle, key, aesMode) { this.subtle = void 0; this.key = void 0; this.aesMode = void 0; this.subtle = subtle; this.key = key; this.aesMode = aesMode; } expandKey() { const subtleAlgoName = getSubtleAlgoName(this.aesMode); return this.subtle.importKey("raw", this.key, { name: subtleAlgoName }, false, ["encrypt", "decrypt"]); } }; function getSubtleAlgoName(aesMode) { switch (aesMode) { case DecrypterAesMode.cbc: return "AES-CBC"; case DecrypterAesMode.ctr: return "AES-CTR"; default: throw new Error(`[FastAESKey] invalid aes mode ${aesMode}`); } } var CHUNK_SIZE = 16; var Decrypter = class { constructor(config, { removePKCS7Padding = true } = {}) { this.logEnabled = true; this.removePKCS7Padding = void 0; this.subtle = null; this.softwareDecrypter = null; this.key = null; this.fastAesKey = null; this.remainderData = null; this.currentIV = null; this.currentResult = null; this.useSoftware = void 0; this.enableSoftwareAES = void 0; this.enableSoftwareAES = config.enableSoftwareAES; this.removePKCS7Padding = removePKCS7Padding; if (removePKCS7Padding) { try { const browserCrypto = self.crypto; if (browserCrypto) { this.subtle = browserCrypto.subtle || browserCrypto.webkitSubtle; } } catch (e) { } } this.useSoftware = !this.subtle; } destroy() { this.subtle = null; this.softwareDecrypter = null; this.key = null; this.fastAesKey = null; this.remainderData = null; this.currentIV = null; this.currentResult = null; } isSync() { return this.useSoftware; } flush() { const { currentResult, remainderData } = this; if (!currentResult || remainderData) { this.reset(); return null; } const data = new Uint8Array(currentResult); this.reset(); if (this.removePKCS7Padding) { return removePadding(data); } return data; } reset() { this.currentResult = null; this.currentIV = null; this.remainderData = null; if (this.softwareDecrypter) { this.softwareDecrypter = null; } } decrypt(data, key, iv, aesMode) { if (this.useSoftware) { return new Promise((resolve, reject) => { const dataView = ArrayBuffer.isView(data) ? data : new Uint8Array(data); this.softwareDecrypt(dataView, key, iv, aesMode); const decryptResult = this.flush(); if (decryptResult) { resolve(decryptResult.buffer); } else { reject(new Error("[softwareDecrypt] Failed to decrypt data")); } }); } return this.webCryptoDecrypt(new Uint8Array(data), key, iv, aesMode); } softwareDecrypt(data, key, iv, aesMode) { const { currentIV, currentResult, remainderData } = this; if (aesMode !== DecrypterAesMode.cbc || key.byteLength !== 16) { logger.warn("SoftwareDecrypt: can only handle AES-128-CBC"); return null; } this.logOnce("JS AES decrypt"); if (remainderData) { data = appendUint8Array(remainderData, data); this.remainderData = null; } const currentChunk = this.getValidChunk(data); if (!currentChunk.length) { return null; } if (currentIV) { iv = currentIV; } let softwareDecrypter = this.softwareDecrypter; if (!softwareDecrypter) { softwareDecrypter = this.softwareDecrypter = new AESDecryptor(); } softwareDecrypter.expandKey(key); const result = currentResult; this.currentResult = softwareDecrypter.decrypt(currentChunk.buffer, 0, iv); this.currentIV = currentChunk.slice(-16).buffer; if (!result) { return null; } return result; } webCryptoDecrypt(data, key, iv, aesMode) { if (this.key !== key || !this.fastAesKey) { if (!this.subtle) { return Promise.resolve(this.onWebCryptoError(data, key, iv, aesMode)); } this.key = key; this.fastAesKey = new FastAESKey(this.subtle, key, aesMode); } return this.fastAesKey.expandKey().then((aesKey) => { if (!this.subtle) { return Promise.reject(new Error("web crypto not initialized")); } this.logOnce("WebCrypto AES decrypt"); const crypto2 = new AESCrypto(this.subtle, new Uint8Array(iv), aesMode); return crypto2.decrypt(data.buffer, aesKey); }).catch((err) => { logger.warn(`[decrypter]: WebCrypto Error, disable WebCrypto API, ${err.name}: ${err.message}`); return this.onWebCryptoError(data, key, iv, aesMode); }); } onWebCryptoError(data, key, iv, aesMode) { const enableSoftwareAES = this.enableSoftwareAES; if (enableSoftwareAES) { this.useSoftware = true; this.logEnabled = true; this.softwareDecrypt(data, key, iv, aesMode); const decryptResult = this.flush(); if (decryptResult) { return decryptResult.buffer; } } throw new Error("WebCrypto" + (enableSoftwareAES ? " and softwareDecrypt" : "") + ": failed to decrypt data"); } getValidChunk(data) { let currentChunk = data; const splitPoint = data.length - data.length % CHUNK_SIZE; if (splitPoint !== data.length) { currentChunk = data.slice(0, splitPoint); this.remainderData = data.slice(splitPoint); } return currentChunk; } logOnce(msg) { if (!this.logEnabled) { return; } logger.log(`[decrypter]: ${msg}`); this.logEnabled = false; } }; var MIN_CHUNK_SIZE = Math.pow(2, 17); var FragmentLoader = class { constructor(config) { this.config = void 0; this.loader = null; this.partLoadTimeout = -1; this.config = config; } destroy() { if (this.loader) { this.loader.destroy(); this.loader = null; } } abort() { if (this.loader) { this.loader.abort(); } } load(frag, onProgress) { const url = frag.url; if (!url) { return Promise.reject(new LoadError({ type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.FRAG_LOAD_ERROR, fatal: false, frag, error: new Error(`Fragment does not have a ${url ? "part list" : "url"}`), networkDetails: null })); } this.abort(); const config = this.config; const FragmentILoader = config.fLoader; const DefaultILoader = config.loader; return new Promise((resolve, reject) => { if (this.loader) { this.loader.destroy(); } if (frag.gap) { if (frag.tagList.some((tags) => tags[0] === "GAP")) { reject(createGapLoadError(frag)); return; } else { frag.gap = false; } } const loader = this.loader = FragmentILoader ? new FragmentILoader(config) : new DefaultILoader(config); const loaderContext = createLoaderContext(frag); frag.loader = loader; const loadPolicy = getLoaderConfigWithoutReties(config.fragLoadPolicy.default); const loaderConfig = { loadPolicy, timeout: loadPolicy.maxLoadTimeMs, maxRetry: 0, retryDelay: 0, maxRetryDelay: 0, highWaterMark: frag.sn === "initSegment" ? Infinity : MIN_CHUNK_SIZE }; frag.stats = loader.stats; const callbacks = { onSuccess: (response, stats, context, networkDetails) => { this.resetLoader(frag, loader); let payload = response.data; if (context.resetIV && frag.decryptdata) { frag.decryptdata.iv = new Uint8Array(payload.slice(0, 16)); payload = payload.slice(16); } resolve({ frag, part: null, payload, networkDetails }); }, onError: (response, context, networkDetails, stats) => { this.resetLoader(frag, loader); reject(new LoadError({ type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.FRAG_LOAD_ERROR, fatal: false, frag, response: _objectSpread2({ url, data: void 0 }, response), error: new Error(`HTTP Error ${response.code} ${response.text}`), networkDetails, stats })); }, onAbort: (stats, context, networkDetails) => { this.resetLoader(frag, loader); reject(new LoadError({ type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.INTERNAL_ABORTED, fatal: false, frag, error: new Error("Aborted"), networkDetails, stats })); }, onTimeout: (stats, context, networkDetails) => { this.resetLoader(frag, loader); reject(new LoadError({ type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.FRAG_LOAD_TIMEOUT, fatal: false, frag, error: new Error(`Timeout after ${loaderConfig.timeout}ms`), networkDetails, stats })); } }; if (onProgress) { callbacks.onProgress = (stats, context, data, networkDetails) => onProgress({ frag, part: null, payload: data, networkDetails }); } loader.load(loaderContext, loaderConfig, callbacks); }); } loadPart(frag, part, onProgress) { this.abort(); const config = this.config; const FragmentILoader = config.fLoader; const DefaultILoader = config.loader; return new Promise((resolve, reject) => { if (this.loader) { this.loader.destroy(); } if (frag.gap || part.gap) { reject(createGapLoadError(frag, part)); return; } const loader = this.loader = FragmentILoader ? new FragmentILoader(config) : new DefaultILoader(config); const loaderContext = createLoaderContext(frag, part); frag.loader = loader; const loadPolicy = getLoaderConfigWithoutReties(config.fragLoadPolicy.default); const loaderConfig = { loadPolicy, timeout: loadPolicy.maxLoadTimeMs, maxRetry: 0, retryDelay: 0, maxRetryDelay: 0, highWaterMark: MIN_CHUNK_SIZE }; part.stats = loader.stats; loader.load(loaderContext, loaderConfig, { onSuccess: (response, stats, context, networkDetails) => { this.resetLoader(frag, loader); this.updateStatsFromPart(frag, part); const partLoadedData = { frag, part, payload: response.data, networkDetails }; onProgress(partLoadedData); resolve(partLoadedData); }, onError: (response, context, networkDetails, stats) => { this.resetLoader(frag, loader); reject(new LoadError({ type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.FRAG_LOAD_ERROR, fatal: false, frag, part, response: _objectSpread2({ url: loaderContext.url, data: void 0 }, response), error: new Error(`HTTP Error ${response.code} ${response.text}`), networkDetails, stats })); }, onAbort: (stats, context, networkDetails) => { frag.stats.aborted = part.stats.aborted; this.resetLoader(frag, loader); reject(new LoadError({ type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.INTERNAL_ABORTED, fatal: false, frag, part, error: new Error("Aborted"), networkDetails, stats })); }, onTimeout: (stats, context, networkDetails) => { this.resetLoader(frag, loader); reject(new LoadError({ type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.FRAG_LOAD_TIMEOUT, fatal: false, frag, part, error: new Error(`Timeout after ${loaderConfig.timeout}ms`), networkDetails, stats })); } }); }); } updateStatsFromPart(frag, part) { const fragStats = frag.stats; const partStats = part.stats; const partTotal = partStats.total; fragStats.loaded += partStats.loaded; if (partTotal) { const estTotalParts = Math.round(frag.duration / part.duration); const estLoadedParts = Math.min(Math.round(fragStats.loaded / partTotal), estTotalParts); const estRemainingParts = estTotalParts - estLoadedParts; const estRemainingBytes = estRemainingParts * Math.round(fragStats.loaded / estLoadedParts); fragStats.total = fragStats.loaded + estRemainingBytes; } else { fragStats.total = Math.max(fragStats.loaded, fragStats.total); } const fragLoading = fragStats.loading; const partLoading = partStats.loading; if (fragLoading.start) { fragLoading.first += partLoading.first - partLoading.start; } else { fragLoading.start = partLoading.start; fragLoading.first = partLoading.first; } fragLoading.end = partLoading.end; } resetLoader(frag, loader) { frag.loader = null; if (this.loader === loader) { self.clearTimeout(this.partLoadTimeout); this.loader = null; } loader.destroy(); } }; function createLoaderContext(frag, part = null) { const segment = part || frag; const loaderContext = { frag, part, responseType: "arraybuffer", url: segment.url, headers: {}, rangeStart: 0, rangeEnd: 0 }; const start = segment.byteRangeStartOffset; const end = segment.byteRangeEndOffset; if (isFiniteNumber(start) && isFiniteNumber(end)) { var _frag$decryptdata; let byteRangeStart = start; let byteRangeEnd = end; if (frag.sn === "initSegment" && isMethodFullSegmentAesCbc((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method)) { const fragmentLen = end - start; if (fragmentLen % 16) { byteRangeEnd = end + (16 - fragmentLen % 16); } if (start !== 0) { loaderContext.resetIV = true; byteRangeStart = start - 16; } } loaderContext.rangeStart = byteRangeStart; loaderContext.rangeEnd = byteRangeEnd; } return loaderContext; } function createGapLoadError(frag, part) { const error = new Error(`GAP ${frag.gap ? "tag" : "attribute"} found`); const errorData = { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_GAP, fatal: false, frag, error, networkDetails: null }; if (part) { errorData.part = part; } (part ? part : frag).stats.aborted = true; return new LoadError(errorData); } function isMethodFullSegmentAesCbc(method) { return method === "AES-128" || method === "AES-256"; } var LoadError = class extends Error { constructor(data) { super(data.error.message); this.data = void 0; this.data = data; } }; var TaskLoop = class extends Logger { constructor(label, logger2) { super(label, logger2); this._boundTick = void 0; this._tickTimer = null; this._tickInterval = null; this._tickCallCount = 0; this._boundTick = this.tick.bind(this); } destroy() { this.onHandlerDestroying(); this.onHandlerDestroyed(); } onHandlerDestroying() { this.clearNextTick(); this.clearInterval(); } onHandlerDestroyed() { } hasInterval() { return !!this._tickInterval; } hasNextTick() { return !!this._tickTimer; } setInterval(millis) { if (!this._tickInterval) { this._tickCallCount = 0; this._tickInterval = self.setInterval(this._boundTick, millis); return true; } return false; } clearInterval() { if (this._tickInterval) { self.clearInterval(this._tickInterval); this._tickInterval = null; return true; } return false; } clearNextTick() { if (this._tickTimer) { self.clearTimeout(this._tickTimer); this._tickTimer = null; return true; } return false; } tick() { this._tickCallCount++; if (this._tickCallCount === 1) { this.doTick(); if (this._tickCallCount > 1) { this.tickImmediate(); } this._tickCallCount = 0; } } tickImmediate() { this.clearNextTick(); this._tickTimer = self.setTimeout(this._boundTick, 0); } doTick() { } }; var ChunkMetadata = class { constructor(level, sn, id, size = 0, part = -1, partial = false) { this.level = void 0; this.sn = void 0; this.part = void 0; this.id = void 0; this.size = void 0; this.partial = void 0; this.transmuxing = getNewPerformanceTiming(); this.buffering = { audio: getNewPerformanceTiming(), video: getNewPerformanceTiming(), audiovideo: getNewPerformanceTiming() }; this.level = level; this.sn = sn; this.id = id; this.size = size; this.part = part; this.partial = partial; } }; function getNewPerformanceTiming() { return { start: 0, executeStart: 0, executeEnd: 0, end: 0 }; } var noopBuffered = { length: 0, start: () => 0, end: () => 0 }; var BufferHelper = class { static isBuffered(media, position) { if (media) { const buffered = BufferHelper.getBuffered(media); for (let i = buffered.length; i--; ) { if (position >= buffered.start(i) && position <= buffered.end(i)) { return true; } } } return false; } static bufferedRanges(media) { if (media) { const timeRanges = BufferHelper.getBuffered(media); return BufferHelper.timeRangesToArray(timeRanges); } return []; } static timeRangesToArray(timeRanges) { const buffered = []; for (let i = 0; i < timeRanges.length; i++) { buffered.push({ start: timeRanges.start(i), end: timeRanges.end(i) }); } return buffered; } static bufferInfo(media, pos, maxHoleDuration) { if (media) { const buffered = BufferHelper.bufferedRanges(media); if (buffered.length) { return BufferHelper.bufferedInfo(buffered, pos, maxHoleDuration); } } return { len: 0, start: pos, end: pos, bufferedIndex: -1 }; } static bufferedInfo(buffered, pos, maxHoleDuration) { pos = Math.max(0, pos); if (buffered.length > 1) { buffered.sort((a, b) => a.start - b.start || b.end - a.end); } let bufferedIndex = -1; let buffered2 = []; if (maxHoleDuration) { for (let i = 0; i < buffered.length; i++) { if (pos >= buffered[i].start && pos <= buffered[i].end) { bufferedIndex = i; } const buf2len = buffered2.length; if (buf2len) { const buf2end = buffered2[buf2len - 1].end; if (buffered[i].start - buf2end < maxHoleDuration) { if (buffered[i].end > buf2end) { buffered2[buf2len - 1].end = buffered[i].end; } } else { buffered2.push(buffered[i]); } } else { buffered2.push(buffered[i]); } } } else { buffered2 = buffered; } let bufferLen = 0; let nextStart; let bufferStart = pos; let bufferEnd = pos; for (let i = 0; i < buffered2.length; i++) { const start = buffered2[i].start; const end = buffered2[i].end; if (bufferedIndex === -1 && pos >= start && pos <= end) { bufferedIndex = i; } if (pos + maxHoleDuration >= start && pos < end) { bufferStart = start; bufferEnd = end; bufferLen = bufferEnd - pos; } else if (pos + maxHoleDuration < start) { nextStart = start; break; } } return { len: bufferLen, start: bufferStart || 0, end: bufferEnd || 0, nextStart, buffered, bufferedIndex }; } static getBuffered(media) { try { return media.buffered || noopBuffered; } catch (e) { logger.log("failed to get media.buffered", e); return noopBuffered; } } }; var VARIABLE_REPLACEMENT_REGEX = /\{\$([a-zA-Z0-9-_]+)\}/g; function hasVariableReferences(str) { return VARIABLE_REPLACEMENT_REGEX.test(str); } function substituteVariables(parsed, value) { if (parsed.variableList !== null || parsed.hasVariableRefs) { const variableList = parsed.variableList; return value.replace(VARIABLE_REPLACEMENT_REGEX, (variableReference) => { const variableName = variableReference.substring(2, variableReference.length - 1); const variableValue = variableList == null ? void 0 : variableList[variableName]; if (variableValue === void 0) { parsed.playlistParsingError || (parsed.playlistParsingError = new Error(`Missing preceding EXT-X-DEFINE tag for Variable Reference: "${variableName}"`)); return variableReference; } return variableValue; }); } return value; } function addVariableDefinition(parsed, attr, parentUrl) { let variableList = parsed.variableList; if (!variableList) { parsed.variableList = variableList = {}; } let NAME; let VALUE; if ("QUERYPARAM" in attr) { NAME = attr.QUERYPARAM; try { const searchParams = new self.URL(parentUrl).searchParams; if (searchParams.has(NAME)) { VALUE = searchParams.get(NAME); } else { throw new Error(`"${NAME}" does not match any query parameter in URI: "${parentUrl}"`); } } catch (error) { parsed.playlistParsingError || (parsed.playlistParsingError = new Error(`EXT-X-DEFINE QUERYPARAM: ${error.message}`)); } } else { NAME = attr.NAME; VALUE = attr.VALUE; } if (NAME in variableList) { parsed.playlistParsingError || (parsed.playlistParsingError = new Error(`EXT-X-DEFINE duplicate Variable Name declarations: "${NAME}"`)); } else { variableList[NAME] = VALUE || ""; } } function importVariableDefinition(parsed, attr, sourceVariableList) { const IMPORT = attr.IMPORT; if (sourceVariableList && IMPORT in sourceVariableList) { let variableList = parsed.variableList; if (!variableList) { parsed.variableList = variableList = {}; } variableList[IMPORT] = sourceVariableList[IMPORT]; } else { parsed.playlistParsingError || (parsed.playlistParsingError = new Error(`EXT-X-DEFINE IMPORT attribute not found in Multivariant Playlist: "${IMPORT}"`)); } } var DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/; var ATTR_LIST_REGEX = /(.+?)=(".*?"|.*?)(?:,|$)/g; var AttrList = class { constructor(attrs, parsed) { if (typeof attrs === "string") { attrs = AttrList.parseAttrList(attrs, parsed); } _extends(this, attrs); } get clientAttrs() { return Object.keys(this).filter((attr) => attr.substring(0, 2) === "X-"); } decimalInteger(attrName) { const intValue = parseInt(this[attrName], 10); if (intValue > Number.MAX_SAFE_INTEGER) { return Infinity; } return intValue; } hexadecimalInteger(attrName) { if (this[attrName]) { let stringValue = (this[attrName] || "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++) { value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16); } return value; } return null; } hexadecimalIntegerAsNumber(attrName) { const intValue = parseInt(this[attrName], 16); if (intValue > Number.MAX_SAFE_INTEGER) { return Infinity; } return intValue; } decimalFloatingPoint(attrName) { return parseFloat(this[attrName]); } optionalFloat(attrName, defaultValue) { const value = this[attrName]; return value ? parseFloat(value) : defaultValue; } enumeratedString(attrName) { return this[attrName]; } enumeratedStringList(attrName, dict) { const attrValue = this[attrName]; return (attrValue ? attrValue.split(/[ ,]+/) : []).reduce((result, identifier) => { result[identifier.toLowerCase()] = true; return result; }, dict); } bool(attrName) { return this[attrName] === "YES"; } decimalResolution(attrName) { const res = DECIMAL_RESOLUTION_REGEX.exec(this[attrName]); if (res === null) { return void 0; } return { width: parseInt(res[1], 10), height: parseInt(res[2], 10) }; } static parseAttrList(input, parsed) { let match; const attrs = {}; const quote = '"'; ATTR_LIST_REGEX.lastIndex = 0; while ((match = ATTR_LIST_REGEX.exec(input)) !== null) { const name = match[1].trim(); let value = match[2]; const quotedString = value.indexOf(quote) === 0 && value.lastIndexOf(quote) === value.length - 1; let hexadecimalSequence = false; if (quotedString) { value = value.slice(1, -1); } else { switch (name) { case "IV": case "SCTE35-CMD": case "SCTE35-IN": case "SCTE35-OUT": hexadecimalSequence = true; } } if (parsed && (quotedString || hexadecimalSequence)) { { value = substituteVariables(parsed, value); } } else if (!hexadecimalSequence && !quotedString) { switch (name) { case "CLOSED-CAPTIONS": if (value === "NONE") { break; } case "ALLOWED-CPC": case "CLASS": case "ASSOC-LANGUAGE": case "AUDIO": case "BYTERANGE": case "CHANNELS": case "CHARACTERISTICS": case "CODECS": case "DATA-ID": case "END-DATE": case "GROUP-ID": case "ID": case "IMPORT": case "INSTREAM-ID": case "KEYFORMAT": case "KEYFORMATVERSIONS": case "LANGUAGE": case "NAME": case "PATHWAY-ID": case "QUERYPARAM": case "RECENTLY-REMOVED-DATERANGES": case "SERVER-URI": case "STABLE-RENDITION-ID": case "STABLE-VARIANT-ID": case "START-DATE": case "SUBTITLES": case "SUPPLEMENTAL-CODECS": case "URI": case "VALUE": case "VIDEO": case "X-ASSET-LIST": case "X-ASSET-URI": logger.warn(`${input}: attribute ${name} is missing quotes`); } } attrs[name] = value; } return attrs; } }; var CLASS_INTERSTITIAL = "com.apple.hls.interstitial"; function isDateRangeCueAttribute(attrName) { return attrName !== "ID" && attrName !== "CLASS" && attrName !== "CUE" && attrName !== "START-DATE" && attrName !== "DURATION" && attrName !== "END-DATE" && attrName !== "END-ON-NEXT"; } function isSCTE35Attribute(attrName) { return attrName === "SCTE35-OUT" || attrName === "SCTE35-IN" || attrName === "SCTE35-CMD"; } var DateRange = class { constructor(dateRangeAttr, dateRangeWithSameId, tagCount = 0) { var _dateRangeWithSameId$; this.attr = void 0; this.tagAnchor = void 0; this.tagOrder = void 0; this._startDate = void 0; this._endDate = void 0; this._dateAtEnd = void 0; this._cue = void 0; this._badValueForSameId = void 0; this.tagAnchor = (dateRangeWithSameId == null ? void 0 : dateRangeWithSameId.tagAnchor) || null; this.tagOrder = (_dateRangeWithSameId$ = dateRangeWithSameId == null ? void 0 : dateRangeWithSameId.tagOrder) != null ? _dateRangeWithSameId$ : tagCount; if (dateRangeWithSameId) { const previousAttr = dateRangeWithSameId.attr; for (const key in previousAttr) { if (Object.prototype.hasOwnProperty.call(dateRangeAttr, key) && dateRangeAttr[key] !== previousAttr[key]) { logger.warn(`DATERANGE tag attribute: "${key}" does not match for tags with ID: "${dateRangeAttr.ID}"`); this._badValueForSameId = key; break; } } dateRangeAttr = _extends(new AttrList({}), previousAttr, dateRangeAttr); } this.attr = dateRangeAttr; if (dateRangeWithSameId) { this._startDate = dateRangeWithSameId._startDate; this._cue = dateRangeWithSameId._cue; this._endDate = dateRangeWithSameId._endDate; this._dateAtEnd = dateRangeWithSameId._dateAtEnd; } else { this._startDate = new Date(dateRangeAttr["START-DATE"]); } if ("END-DATE" in this.attr) { const endDate = (dateRangeWithSameId == null ? void 0 : dateRangeWithSameId.endDate) || new Date(this.attr["END-DATE"]); if (isFiniteNumber(endDate.getTime())) { this._endDate = endDate; } } } get id() { return this.attr.ID; } get class() { return this.attr.CLASS; } get cue() { const _cue = this._cue; if (_cue === void 0) { return this._cue = this.attr.enumeratedStringList(this.attr.CUE ? "CUE" : "X-CUE", { pre: false, post: false, once: false }); } return _cue; } get startTime() { const { tagAnchor } = this; if (tagAnchor === null || tagAnchor.programDateTime === null) { logger.warn(`Expected tagAnchor Fragment with PDT set for DateRange "${this.id}": ${tagAnchor}`); return NaN; } return tagAnchor.start + (this.startDate.getTime() - tagAnchor.programDateTime) / 1e3; } get startDate() { return this._startDate; } get endDate() { const dateAtEnd = this._endDate || this._dateAtEnd; if (dateAtEnd) { return dateAtEnd; } const duration = this.duration; if (duration !== null) { return this._dateAtEnd = new Date(this._startDate.getTime() + duration * 1e3); } return null; } get duration() { if ("DURATION" in this.attr) { const duration = this.attr.decimalFloatingPoint("DURATION"); if (isFiniteNumber(duration)) { return duration; } } else if (this._endDate) { return (this._endDate.getTime() - this._startDate.getTime()) / 1e3; } return null; } get plannedDuration() { if ("PLANNED-DURATION" in this.attr) { return this.attr.decimalFloatingPoint("PLANNED-DURATION"); } return null; } get endOnNext() { return this.attr.bool("END-ON-NEXT"); } get isInterstitial() { return this.class === CLASS_INTERSTITIAL; } get isValid() { return !!this.id && !this._badValueForSameId && isFiniteNumber(this.startDate.getTime()) && (this.duration === null || this.duration >= 0) && (!this.endOnNext || !!this.class) && (!this.attr.CUE || !this.cue.pre && !this.cue.post || this.cue.pre !== this.cue.post) && (!this.isInterstitial || "X-ASSET-URI" in this.attr || "X-ASSET-LIST" in this.attr); } }; var DEFAULT_TARGET_DURATION = 10; var LevelDetails = class { constructor(baseUrl) { this.PTSKnown = false; this.alignedSliding = false; this.averagetargetduration = void 0; this.endCC = 0; this.endSN = 0; this.fragments = void 0; this.fragmentHint = void 0; this.partList = null; this.dateRanges = void 0; this.dateRangeTagCount = 0; this.live = true; this.requestScheduled = -1; this.ageHeader = 0; this.advancedDateTime = void 0; this.updated = true; this.advanced = true; this.misses = 0; this.startCC = 0; this.startSN = 0; this.startTimeOffset = null; this.targetduration = 0; this.totalduration = 0; this.type = null; this.url = void 0; this.m3u8 = ""; this.version = null; this.canBlockReload = false; this.canSkipUntil = 0; this.canSkipDateRanges = false; this.skippedSegments = 0; this.recentlyRemovedDateranges = void 0; this.partHoldBack = 0; this.holdBack = 0; this.partTarget = 0; this.preloadHint = void 0; this.renditionReports = void 0; this.tuneInGoal = 0; this.deltaUpdateFailed = void 0; this.driftStartTime = 0; this.driftEndTime = 0; this.driftStart = 0; this.driftEnd = 0; this.encryptedFragments = void 0; this.playlistParsingError = null; this.variableList = null; this.hasVariableRefs = false; this.appliedTimelineOffset = void 0; this.fragments = []; this.encryptedFragments = []; this.dateRanges = {}; this.url = baseUrl; } reloaded(previous) { if (!previous) { this.advanced = true; this.updated = true; return; } const partSnDiff = this.lastPartSn - previous.lastPartSn; const partIndexDiff = this.lastPartIndex - previous.lastPartIndex; this.updated = this.endSN !== previous.endSN || !!partIndexDiff || !!partSnDiff || !this.live; this.advanced = this.endSN > previous.endSN || partSnDiff > 0 || partSnDiff === 0 && partIndexDiff > 0; if (this.updated || this.advanced) { this.misses = Math.floor(previous.misses * 0.6); } else { this.misses = previous.misses + 1; } } get hasProgramDateTime() { if (this.fragments.length) { return isFiniteNumber(this.fragments[this.fragments.length - 1].programDateTime); } return false; } get levelTargetDuration() { return this.averagetargetduration || this.targetduration || DEFAULT_TARGET_DURATION; } get drift() { const runTime = this.driftEndTime - this.driftStartTime; if (runTime > 0) { const runDuration = this.driftEnd - this.driftStart; return runDuration * 1e3 / runTime; } return 1; } get edge() { return this.partEnd || this.fragmentEnd; } get partEnd() { var _this$partList; if ((_this$partList = this.partList) != null && _this$partList.length) { return this.partList[this.partList.length - 1].end; } return this.fragmentEnd; } get fragmentEnd() { var _this$fragments; if ((_this$fragments = this.fragments) != null && _this$fragments.length) { return this.fragments[this.fragments.length - 1].end; } return 0; } get fragmentStart() { var _this$fragments2; if ((_this$fragments2 = this.fragments) != null && _this$fragments2.length) { return this.fragments[0].start; } return 0; } get age() { if (this.advancedDateTime) { return Math.max(Date.now() - this.advancedDateTime, 0) / 1e3; } return 0; } get lastPartIndex() { var _this$partList2; if ((_this$partList2 = this.partList) != null && _this$partList2.length) { return this.partList[this.partList.length - 1].index; } return -1; } get maxPartIndex() { const partList = this.partList; if (partList) { const lastIndex = this.lastPartIndex; if (lastIndex !== -1) { for (let i = partList.length; i--; ) { if (partList[i].index > lastIndex) { return partList[i].index; } } return lastIndex; } } return 0; } get lastPartSn() { var _this$partList3; if ((_this$partList3 = this.partList) != null && _this$partList3.length) { return this.partList[this.partList.length - 1].fragment.sn; } return this.endSN; } get expired() { if (this.live && this.age && this.misses < 3) { const playlistWindowDuration = this.partEnd - this.fragmentStart; return this.age > Math.max(playlistWindowDuration, this.totalduration) + this.levelTargetDuration; } return false; } }; function isFullSegmentEncryption(method) { return method === "AES-128" || method === "AES-256" || method === "AES-256-CTR"; } function getAesModeFromFullSegmentMethod(method) { switch (method) { case "AES-128": case "AES-256": return DecrypterAesMode.cbc; case "AES-256-CTR": return DecrypterAesMode.ctr; default: throw new Error(`invalid full segment method ${method}`); } } function base64Decode(base64encodedStr) { return Uint8Array.from(atob(base64encodedStr), (c) => c.charCodeAt(0)); } function strToUtf8array(str) { return Uint8Array.from(unescape(encodeURIComponent(str)), (c) => c.charCodeAt(0)); } function getKeyIdBytes(str) { const keyIdbytes = strToUtf8array(str).subarray(0, 16); const paddedkeyIdbytes = new Uint8Array(16); paddedkeyIdbytes.set(keyIdbytes, 16 - keyIdbytes.length); return paddedkeyIdbytes; } function changeEndianness(keyId) { const swap = function swap2(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); } function convertDataUriToArrayBytes(uri) { 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); keydata = base64Decode(data); } else { keydata = getKeyIdBytes(data); } } } return keydata; } var optionalSelf = typeof self !== "undefined" ? self : void 0; var KeySystems = { CLEARKEY: "org.w3.clearkey", FAIRPLAY: "com.apple.fps", PLAYREADY: "com.microsoft.playready", WIDEVINE: "com.widevine.alpha" }; var KeySystemFormats = { CLEARKEY: "org.w3.clearkey", FAIRPLAY: "com.apple.streamingkeydelivery", PLAYREADY: "com.microsoft.playready", WIDEVINE: "urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed" }; function keySystemFormatToKeySystemDomain(format2) { switch (format2) { case KeySystemFormats.FAIRPLAY: return KeySystems.FAIRPLAY; case KeySystemFormats.PLAYREADY: return KeySystems.PLAYREADY; case KeySystemFormats.WIDEVINE: return KeySystems.WIDEVINE; case KeySystemFormats.CLEARKEY: return KeySystems.CLEARKEY; } } function keySystemDomainToKeySystemFormat(keySystem) { switch (keySystem) { case KeySystems.FAIRPLAY: return KeySystemFormats.FAIRPLAY; case KeySystems.PLAYREADY: return KeySystemFormats.PLAYREADY; case KeySystems.WIDEVINE: return KeySystemFormats.WIDEVINE; case KeySystems.CLEARKEY: return KeySystemFormats.CLEARKEY; } } function getKeySystemsForConfig(config) { const { drmSystems, widevineLicenseUrl } = config; const keySystemsToAttempt = drmSystems ? [KeySystems.FAIRPLAY, KeySystems.WIDEVINE, KeySystems.PLAYREADY, KeySystems.CLEARKEY].filter((keySystem) => !!drmSystems[keySystem]) : []; if (!keySystemsToAttempt[KeySystems.WIDEVINE] && widevineLicenseUrl) { keySystemsToAttempt.push(KeySystems.WIDEVINE); } return keySystemsToAttempt; } var requestMediaKeySystemAccess = function(_optionalSelf$navigat) { if (optionalSelf != null && (_optionalSelf$navigat = optionalSelf.navigator) != null && _optionalSelf$navigat.requestMediaKeySystemAccess) { return self.navigator.requestMediaKeySystemAccess.bind(self.navigator); } else { return null; } }(); function getSupportedMediaKeySystemConfigurations(keySystem, audioCodecs, videoCodecs, drmSystemOptions) { let initDataTypes; switch (keySystem) { case KeySystems.FAIRPLAY: initDataTypes = ["cenc", "sinf"]; break; case KeySystems.WIDEVINE: case KeySystems.PLAYREADY: initDataTypes = ["cenc"]; break; case KeySystems.CLEARKEY: initDataTypes = ["cenc", "keyids"]; break; default: throw new Error(`Unknown key-system: ${keySystem}`); } return createMediaKeySystemConfigurations(initDataTypes, audioCodecs, videoCodecs, drmSystemOptions); } function createMediaKeySystemConfigurations(initDataTypes, audioCodecs, videoCodecs, drmSystemOptions) { const baseConfig = { initDataTypes, persistentState: drmSystemOptions.persistentState || "optional", distinctiveIdentifier: drmSystemOptions.distinctiveIdentifier || "optional", sessionTypes: drmSystemOptions.sessionTypes || [drmSystemOptions.sessionType || "temporary"], audioCapabilities: audioCodecs.map((codec) => ({ contentType: `audio/mp4; codecs=${codec}`, robustness: drmSystemOptions.audioRobustness || "", encryptionScheme: drmSystemOptions.audioEncryptionScheme || null })), videoCapabilities: videoCodecs.map((codec) => ({ contentType: `video/mp4; codecs=${codec}`, robustness: drmSystemOptions.videoRobustness || "", encryptionScheme: drmSystemOptions.videoEncryptionScheme || null })) }; return [baseConfig]; } function isPersistentSessionType(drmSystemOptions) { var _drmSystemOptions$ses; return drmSystemOptions.sessionType === "persistent-license" || !!((_drmSystemOptions$ses = drmSystemOptions.sessionTypes) != null && _drmSystemOptions$ses.some((type) => type === "persistent-license")); } function parsePlayReadyWRM(keyBytes) { const keyBytesUtf16 = new Uint16Array(keyBytes.buffer, keyBytes.byteOffset, keyBytes.byteLength / 2); const keyByteStr = String.fromCharCode.apply(null, Array.from(keyBytesUtf16)); 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) { const keyId = keyData.childNodes[0] ? keyData.childNodes[0].nodeValue : keyData.getAttribute("VALUE"); if (keyId) { const keyIdArray = base64Decode(keyId).subarray(0, 16); changeEndianness(keyIdArray); return keyIdArray; } } return null; } var keyUriToKeyIdMap = {}; var LevelKey = class { static clearKeyUriToKeyIdMap() { keyUriToKeyIdMap = {}; } constructor(method, uri, format2, formatversions = [1], iv = null, keyId) { this.uri = void 0; this.method = void 0; this.keyFormat = void 0; this.keyFormatVersions = void 0; this.encrypted = void 0; this.isCommonEncryption = void 0; this.iv = null; this.key = null; this.keyId = null; this.pssh = null; this.method = method; this.uri = uri; this.keyFormat = format2; this.keyFormatVersions = formatversions; this.iv = iv; this.encrypted = method ? method !== "NONE" : false; this.isCommonEncryption = this.encrypted && !isFullSegmentEncryption(method); if (keyId != null && keyId.startsWith("0x")) { this.keyId = new Uint8Array(hexToArrayBuffer(keyId)); } } matches(key) { var _key$iv, _this$iv; return key.uri === this.uri && key.method === this.method && key.encrypted === this.encrypted && key.keyFormat === this.keyFormat && key.keyFormatVersions.join(",") === this.keyFormatVersions.join(",") && ((_key$iv = key.iv) == null ? void 0 : _key$iv.join(",")) === ((_this$iv = this.iv) == null ? void 0 : _this$iv.join(",")); } isSupported() { if (this.method) { if (isFullSegmentEncryption(this.method) || this.method === "NONE") { return true; } if (this.keyFormat === "identity") { return this.method === "SAMPLE-AES"; } else { switch (this.keyFormat) { case KeySystemFormats.FAIRPLAY: case KeySystemFormats.WIDEVINE: case KeySystemFormats.PLAYREADY: case KeySystemFormats.CLEARKEY: return ["ISO-23001-7", "SAMPLE-AES", "SAMPLE-AES-CENC", "SAMPLE-AES-CTR"].indexOf(this.method) !== -1; } } } return false; } getDecryptData(sn) { if (!this.encrypted || !this.uri) { return null; } if (isFullSegmentEncryption(this.method) && this.uri && !this.iv) { if (typeof sn !== "number") { logger.warn(`missing IV for initialization segment with method="${this.method}" - compliance issue`); sn = 0; } const iv = createInitializationVector(sn); const decryptdata = new LevelKey(this.method, this.uri, "identity", this.keyFormatVersions, iv); return decryptdata; } if (this.pssh && this.keyId) { return this; } const keyBytes = convertDataUriToArrayBytes(this.uri); if (keyBytes) { switch (this.keyFormat) { case KeySystemFormats.WIDEVINE: this.pssh = keyBytes; if (!this.keyId && keyBytes.length >= 22) { const offset = keyBytes.length - 22; this.keyId = keyBytes.subarray(offset, offset + 16); } break; case KeySystemFormats.PLAYREADY: { const PlayReadyKeySystemUUID = new Uint8Array([154, 4, 240, 121, 152, 64, 66, 134, 171, 146, 230, 91, 224, 136, 95, 149]); this.pssh = mp4pssh(PlayReadyKeySystemUUID, null, keyBytes); this.keyId = parsePlayReadyWRM(keyBytes); break; } default: { 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; } } } if (!this.keyId || this.keyId.byteLength !== 16) { let keyId = keyUriToKeyIdMap[this.uri]; if (!keyId) { const val = Object.keys(keyUriToKeyIdMap).length % Number.MAX_SAFE_INTEGER; keyId = new Uint8Array(16); const dv = new DataView(keyId.buffer, 12, 4); dv.setUint32(0, val); keyUriToKeyIdMap[this.uri] = keyId; } this.keyId = keyId; } return this; } }; function createInitializationVector(segmentNumber) { const uint8View = new Uint8Array(16); for (let i = 12; i < 16; i++) { uint8View[i] = segmentNumber >> 8 * (15 - i) & 255; } return uint8View; } var MASTER_PLAYLIST_REGEX = /#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-(SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\r\n]*)[\r\n]+/g; var MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g; var IS_MEDIA_PLAYLIST = /^#EXT(?:INF|-X-TARGETDURATION):/m; var LEVEL_PLAYLIST_REGEX_FAST = new RegExp([ /#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source, /(?!#) *(\S[^\r\n]*)/.source, /#.*/.source ].join("|"), "g"); var LEVEL_PLAYLIST_REGEX_SLOW = new RegExp([/#EXT-X-(PROGRAM-DATE-TIME|BYTERANGE|DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source, /#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/.source, /#EXT-X-(DISCONTINUITY|ENDLIST|GAP|INDEPENDENT-SEGMENTS)/.source, /(#)([^:]*):(.*)/.source, /(#)(.*)(?:.*)\r?\n?/.source].join("|")); var M3U8Parser = class { static findGroup(groups, mediaGroupId) { for (let i = 0; i < groups.length; i++) { const group = groups[i]; if (group.id === mediaGroupId) { return group; } } } static resolve(url, baseUrl) { return urlToolkitExports.buildAbsoluteURL(baseUrl, url, { alwaysNormalize: true }); } static isMediaPlaylist(str) { return IS_MEDIA_PLAYLIST.test(str); } static parseMasterPlaylist(string, baseurl) { const hasVariableRefs = hasVariableReferences(string); const parsed = { contentSteering: null, levels: [], playlistParsingError: null, sessionData: null, sessionKeys: null, startTimeOffset: null, variableList: null, hasVariableRefs }; const levelsWithKnownCodecs = []; MASTER_PLAYLIST_REGEX.lastIndex = 0; let result; while ((result = MASTER_PLAYLIST_REGEX.exec(string)) != null) { if (result[1]) { var _level$unknownCodecs; const attrs = new AttrList(result[1], parsed); const uri = substituteVariables(parsed, result[2]); const level = { attrs, bitrate: attrs.decimalInteger("BANDWIDTH") || attrs.decimalInteger("AVERAGE-BANDWIDTH"), name: attrs.NAME, url: M3U8Parser.resolve(uri, baseurl) }; const resolution = attrs.decimalResolution("RESOLUTION"); if (resolution) { level.width = resolution.width; level.height = resolution.height; } setCodecs(attrs.CODECS, level); const supplementalCodecs = attrs["SUPPLEMENTAL-CODECS"]; if (supplementalCodecs) { level.supplemental = {}; setCodecs(supplementalCodecs, level.supplemental); } if (!((_level$unknownCodecs = level.unknownCodecs) != null && _level$unknownCodecs.length)) { levelsWithKnownCodecs.push(level); } parsed.levels.push(level); } else if (result[3]) { const tag = result[3]; const attributes = result[4]; switch (tag) { case "SESSION-DATA": { const sessionAttrs = new AttrList(attributes, parsed); const dataId = sessionAttrs["DATA-ID"]; if (dataId) { if (parsed.sessionData === null) { parsed.sessionData = {}; } parsed.sessionData[dataId] = sessionAttrs; } break; } case "SESSION-KEY": { const sessionKey = parseKey(attributes, baseurl, parsed); if (sessionKey.encrypted && sessionKey.isSupported()) { if (parsed.sessionKeys === null) { parsed.sessionKeys = []; } parsed.sessionKeys.push(sessionKey); } else { logger.warn(`[Keys] Ignoring invalid EXT-X-SESSION-KEY tag: "${attributes}"`); } break; } case "DEFINE": { { const variableAttributes = new AttrList(attributes, parsed); addVariableDefinition(parsed, variableAttributes, baseurl); } break; } case "CONTENT-STEERING": { const contentSteeringAttributes = new AttrList(attributes, parsed); parsed.contentSteering = { uri: M3U8Parser.resolve(contentSteeringAttributes["SERVER-URI"], baseurl), pathwayId: contentSteeringAttributes["PATHWAY-ID"] || "." }; break; } case "START": { parsed.startTimeOffset = parseStartTimeOffset(attributes); break; } } } } const stripUnknownCodecLevels = levelsWithKnownCodecs.length > 0 && levelsWithKnownCodecs.length < parsed.levels.length; parsed.levels = stripUnknownCodecLevels ? levelsWithKnownCodecs : parsed.levels; if (parsed.levels.length === 0) { parsed.playlistParsingError = new Error("no levels found in manifest"); } return parsed; } static parseMasterPlaylistMedia(string, baseurl, parsed) { let result; const results = {}; const levels = parsed.levels; const groupsByType = { AUDIO: levels.map((level) => ({ id: level.attrs.AUDIO, audioCodec: level.audioCodec })), SUBTITLES: levels.map((level) => ({ id: level.attrs.SUBTITLES, textCodec: level.textCodec })), "CLOSED-CAPTIONS": [] }; let id = 0; MASTER_PLAYLIST_MEDIA_REGEX.lastIndex = 0; while ((result = MASTER_PLAYLIST_MEDIA_REGEX.exec(string)) !== null) { const attrs = new AttrList(result[1], parsed); const type = attrs.TYPE; if (type) { const groups = groupsByType[type]; const medias = results[type] || []; results[type] = medias; const lang = attrs.LANGUAGE; const assocLang = attrs["ASSOC-LANGUAGE"]; const channels = attrs.CHANNELS; const characteristics = attrs.CHARACTERISTICS; const instreamId = attrs["INSTREAM-ID"]; const media = { attrs, bitrate: 0, id: id++, groupId: attrs["GROUP-ID"] || "", name: attrs.NAME || lang || "", type, default: attrs.bool("DEFAULT"), autoselect: attrs.bool("AUTOSELECT"), forced: attrs.bool("FORCED"), lang, url: attrs.URI ? M3U8Parser.resolve(attrs.URI, baseurl) : "" }; if (assocLang) { media.assocLang = assocLang; } if (channels) { media.channels = channels; } if (characteristics) { media.characteristics = characteristics; } if (instreamId) { media.instreamId = instreamId; } if (groups != null && groups.length) { const groupCodec = M3U8Parser.findGroup(groups, media.groupId) || groups[0]; assignCodec(media, groupCodec, "audioCodec"); assignCodec(media, groupCodec, "textCodec"); } medias.push(media); } } return results; } static parseLevelPlaylist(string, baseurl, id, type, levelUrlId, multivariantVariableList) { var _LEVEL_PLAYLIST_REGEX; const base = { url: baseurl }; const level = new LevelDetails(baseurl); const fragments = level.fragments; const programDateTimes = []; let currentInitSegment = null; let currentSN = 0; let currentPart = 0; let totalduration = 0; let discontinuityCounter = 0; let currentBitrate = 0; let prevFrag = null; let frag = new Fragment(type, base); let result; let i; let levelkeys; let firstPdtIndex = -1; let createNextFrag = false; let nextByteRange = null; let serverControlAttrs; LEVEL_PLAYLIST_REGEX_FAST.lastIndex = 0; level.m3u8 = string; level.hasVariableRefs = hasVariableReferences(string); if (((_LEVEL_PLAYLIST_REGEX = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) == null ? void 0 : _LEVEL_PLAYLIST_REGEX[0]) !== "#EXTM3U") { level.playlistParsingError = new Error("Missing format identifier #EXTM3U"); return level; } while ((result = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) !== null) { if (createNextFrag) { createNextFrag = false; frag = new Fragment(type, base); frag.playlistOffset = totalduration; frag.start = totalduration; frag.sn = currentSN; frag.cc = discontinuityCounter; if (currentBitrate) { frag.bitrate = currentBitrate; } frag.level = id; if (currentInitSegment) { frag.initSegment = currentInitSegment; if (currentInitSegment.rawProgramDateTime) { frag.rawProgramDateTime = currentInitSegment.rawProgramDateTime; currentInitSegment.rawProgramDateTime = null; } if (nextByteRange) { frag.setByteRange(nextByteRange); nextByteRange = null; } } } const duration = result[1]; if (duration) { frag.duration = parseFloat(duration); const title = (" " + result[2]).slice(1); frag.title = title || null; frag.tagList.push(title ? ["INF", duration, title] : ["INF", duration]); } else if (result[3]) { if (isFiniteNumber(frag.duration)) { frag.playlistOffset = totalduration; frag.start = totalduration; if (levelkeys) { setFragLevelKeys(frag, levelkeys, level); } frag.sn = currentSN; frag.level = id; frag.cc = discontinuityCounter; fragments.push(frag); const uri = (" " + result[3]).slice(1); frag.relurl = substituteVariables(level, uri); assignProgramDateTime(frag, prevFrag, programDateTimes); prevFrag = frag; totalduration += frag.duration; currentSN++; currentPart = 0; createNextFrag = true; } } else { result = result[0].match(LEVEL_PLAYLIST_REGEX_SLOW); if (!result) { logger.warn("No matches on slow regex match for level playlist!"); continue; } for (i = 1; i < result.length; i++) { if (result[i] !== void 0) { break; } } const tag = (" " + result[i]).slice(1); const value1 = (" " + result[i + 1]).slice(1); const value2 = result[i + 2] ? (" " + result[i + 2]).slice(1) : null; switch (tag) { case "BYTERANGE": if (prevFrag) { frag.setByteRange(value1, prevFrag); } else { frag.setByteRange(value1); } break; case "PROGRAM-DATE-TIME": frag.rawProgramDateTime = value1; frag.tagList.push(["PROGRAM-DATE-TIME", value1]); if (firstPdtIndex === -1) { firstPdtIndex = fragments.length; } break; case "PLAYLIST-TYPE": if (level.type) { assignMultipleMediaPlaylistTagOccuranceError(level, tag, result); } level.type = value1.toUpperCase(); break; case "MEDIA-SEQUENCE": if (level.startSN !== 0) { assignMultipleMediaPlaylistTagOccuranceError(level, tag, result); } else if (fragments.length > 0) { assignMustAppearBeforeSegmentsError(level, tag, result); } currentSN = level.startSN = parseInt(value1); break; case "SKIP": { if (level.skippedSegments) { assignMultipleMediaPlaylistTagOccuranceError(level, tag, result); } const skipAttrs = new AttrList(value1, level); const skippedSegments = skipAttrs.decimalInteger("SKIPPED-SEGMENTS"); if (isFiniteNumber(skippedSegments)) { level.skippedSegments += skippedSegments; for (let _i = skippedSegments; _i--; ) { fragments.push(null); } currentSN += skippedSegments; } const recentlyRemovedDateranges = skipAttrs.enumeratedString("RECENTLY-REMOVED-DATERANGES"); if (recentlyRemovedDateranges) { level.recentlyRemovedDateranges = (level.recentlyRemovedDateranges || []).concat(recentlyRemovedDateranges.split(" ")); } break; } case "TARGETDURATION": if (level.targetduration !== 0) { assignMultipleMediaPlaylistTagOccuranceError(level, tag, result); } level.targetduration = Math.max(parseInt(value1), 1); break; case "VERSION": if (level.version !== null) { assignMultipleMediaPlaylistTagOccuranceError(level, tag, result); } level.version = parseInt(value1); break; case "INDEPENDENT-SEGMENTS": break; case "ENDLIST": if (!level.live) { assignMultipleMediaPlaylistTagOccuranceError(level, tag, result); } level.live = false; break; case "#": if (value1 || value2) { frag.tagList.push(value2 ? [value1, value2] : [value1]); } break; case "DISCONTINUITY": discontinuityCounter++; frag.tagList.push(["DIS"]); break; case "GAP": frag.gap = true; frag.tagList.push([tag]); break; case "BITRATE": frag.tagList.push([tag, value1]); currentBitrate = parseInt(value1) * 1e3; if (isFiniteNumber(currentBitrate)) { frag.bitrate = currentBitrate; } else { currentBitrate = 0; } break; case "DATERANGE": { const dateRangeAttr = new AttrList(value1, level); const dateRange = new DateRange(dateRangeAttr, level.dateRanges[dateRangeAttr.ID], level.dateRangeTagCount); level.dateRangeTagCount++; if (dateRange.isValid || level.skippedSegments) { level.dateRanges[dateRange.id] = dateRange; } else { logger.warn(`Ignoring invalid DATERANGE tag: "${value1}"`); } frag.tagList.push(["EXT-X-DATERANGE", value1]); break; } case "DEFINE": { { const variableAttributes = new AttrList(value1, level); if ("IMPORT" in variableAttributes) { importVariableDefinition(level, variableAttributes, multivariantVariableList); } else { addVariableDefinition(level, variableAttributes, baseurl); } } break; } case "DISCONTINUITY-SEQUENCE": if (level.startCC !== 0) { assignMultipleMediaPlaylistTagOccuranceError(level, tag, result); } else if (fragments.length > 0) { assignMustAppearBeforeSegmentsError(level, tag, result); } level.startCC = discontinuityCounter = parseInt(value1); break; case "KEY": { const levelKey = parseKey(value1, baseurl, level); if (levelKey.isSupported()) { if (levelKey.method === "NONE") { levelkeys = void 0; break; } if (!levelkeys) { levelkeys = {}; } const currentKey = levelkeys[levelKey.keyFormat]; if (!(currentKey != null && currentKey.matches(levelKey))) { if (currentKey) { levelkeys = _extends({}, levelkeys); } levelkeys[levelKey.keyFormat] = levelKey; } } else { logger.warn(`[Keys] Ignoring invalid EXT-X-KEY tag: "${value1}"`); } break; } case "START": level.startTimeOffset = parseStartTimeOffset(value1); break; case "MAP": { const mapAttrs = new AttrList(value1, level); if (frag.duration) { const init = new Fragment(type, base); setInitSegment(init, mapAttrs, id, levelkeys); currentInitSegment = init; frag.initSegment = currentInitSegment; if (currentInitSegment.rawProgramDateTime && !frag.rawProgramDateTime) { frag.rawProgramDateTime = currentInitSegment.rawProgramDateTime; } } else { const end = frag.byteRangeEndOffset; if (end) { const start = frag.byteRangeStartOffset; nextByteRange = `${end - start}@${start}`; } else { nextByteRange = null; } setInitSegment(frag, mapAttrs, id, levelkeys); currentInitSegment = frag; createNextFrag = true; } currentInitSegment.cc = discontinuityCounter; break; } case "SERVER-CONTROL": { if (serverControlAttrs) { assignMultipleMediaPlaylistTagOccuranceError(level, tag, result); } serverControlAttrs = new AttrList(value1); level.canBlockReload = serverControlAttrs.bool("CAN-BLOCK-RELOAD"); level.canSkipUntil = serverControlAttrs.optionalFloat("CAN-SKIP-UNTIL", 0); level.canSkipDateRanges = level.canSkipUntil > 0 && serverControlAttrs.bool("CAN-SKIP-DATERANGES"); level.partHoldBack = serverControlAttrs.optionalFloat("PART-HOLD-BACK", 0); level.holdBack = serverControlAttrs.optionalFloat("HOLD-BACK", 0); break; } case "PART-INF": { if (level.partTarget) { assignMultipleMediaPlaylistTagOccuranceError(level, tag, result); } const partInfAttrs = new AttrList(value1); level.partTarget = partInfAttrs.decimalFloatingPoint("PART-TARGET"); break; } case "PART": { let partList = level.partList; if (!partList) { partList = level.partList = []; } const previousFragmentPart = currentPart > 0 ? partList[partList.length - 1] : void 0; const index = currentPart++; const partAttrs = new AttrList(value1, level); const part = new Part(partAttrs, frag, base, index, previousFragmentPart); partList.push(part); frag.duration += part.duration; break; } case "PRELOAD-HINT": { const preloadHintAttrs = new AttrList(value1, level); level.preloadHint = preloadHintAttrs; break; } case "RENDITION-REPORT": { const renditionReportAttrs = new AttrList(value1, level); level.renditionReports = level.renditionReports || []; level.renditionReports.push(renditionReportAttrs); break; } default: logger.warn(`line parsed but not handled: ${result}`); break; } } } if (prevFrag && !prevFrag.relurl) { fragments.pop(); totalduration -= prevFrag.duration; if (level.partList) { level.fragmentHint = prevFrag; } } else if (level.partList) { assignProgramDateTime(frag, prevFrag, programDateTimes); frag.cc = discontinuityCounter; level.fragmentHint = frag; if (levelkeys) { setFragLevelKeys(frag, levelkeys, level); } } if (!level.targetduration) { level.playlistParsingError = new Error(`#EXT-X-TARGETDURATION is required`); } const fragmentLength = fragments.length; const firstFragment = fragments[0]; const lastFragment = fragments[fragmentLength - 1]; totalduration += level.skippedSegments * level.targetduration; if (totalduration > 0 && fragmentLength && lastFragment) { level.averagetargetduration = totalduration / fragmentLength; const lastSn = lastFragment.sn; level.endSN = lastSn !== "initSegment" ? lastSn : 0; if (!level.live) { lastFragment.endList = true; } if (firstFragment && level.startCC === void 0) { level.startCC = firstFragment.cc; } if (firstPdtIndex > 0) { backfillProgramDateTimes(fragments, firstPdtIndex); if (firstFragment) { programDateTimes.unshift(firstFragment); } } } else { level.endSN = 0; level.startCC = 0; } if (level.fragmentHint) { totalduration += level.fragmentHint.duration; } level.totalduration = totalduration; if (programDateTimes.length && level.dateRangeTagCount && firstFragment) { mapDateRanges(programDateTimes, level); } level.endCC = discontinuityCounter; return level; } }; function mapDateRanges(programDateTimes, details) { const programDateTimeCount = programDateTimes.length; if (!programDateTimeCount) { return; } const lastProgramDateTime = programDateTimes[programDateTimeCount - 1]; const playlistEnd = details.live ? Infinity : details.totalduration; const dateRangeIds = Object.keys(details.dateRanges); for (let i = dateRangeIds.length; i--; ) { const dateRange = details.dateRanges[dateRangeIds[i]]; const startDateTime = dateRange.startDate.getTime(); dateRange.tagAnchor = lastProgramDateTime.ref; for (let j = programDateTimeCount; j--; ) { const fragIndex = findFragmentWithStartDate(details, startDateTime, programDateTimes, j, playlistEnd); if (fragIndex !== -1) { dateRange.tagAnchor = details.fragments[fragIndex].ref; break; } } } } function findFragmentWithStartDate(details, startDateTime, programDateTimes, index, endTime) { const pdtFragment = programDateTimes[index]; if (pdtFragment) { const pdtStart = pdtFragment.programDateTime; if (startDateTime >= pdtStart || index === 0) { var _programDateTimes; const durationBetweenPdt = (((_programDateTimes = programDateTimes[index + 1]) == null ? void 0 : _programDateTimes.start) || endTime) - pdtFragment.start; if (startDateTime <= pdtStart + durationBetweenPdt * 1e3) { const startIndex = programDateTimes[index].sn - details.startSN; const fragments = details.fragments; if (fragments.length > programDateTimes.length) { const endSegment = programDateTimes[index + 1] || fragments[fragments.length - 1]; const endIndex = endSegment.sn - details.startSN; for (let i = endIndex; i > startIndex; i--) { const fragStartDateTime = fragments[i].programDateTime; if (startDateTime >= fragStartDateTime && startDateTime < fragStartDateTime + fragments[i].duration * 1e3) { return i; } } } return startIndex; } } } return -1; } function parseKey(keyTagAttributes, baseurl, parsed) { var _keyAttrs$METHOD, _keyAttrs$KEYFORMAT; const keyAttrs = new AttrList(keyTagAttributes, parsed); const decryptmethod = (_keyAttrs$METHOD = keyAttrs.METHOD) != null ? _keyAttrs$METHOD : ""; const decrypturi = keyAttrs.URI; const decryptiv = keyAttrs.hexadecimalInteger("IV"); const decryptkeyformatversions = keyAttrs.KEYFORMATVERSIONS; const decryptkeyformat = (_keyAttrs$KEYFORMAT = keyAttrs.KEYFORMAT) != null ? _keyAttrs$KEYFORMAT : "identity"; if (decrypturi && keyAttrs.IV && !decryptiv) { logger.error(`Invalid IV: ${keyAttrs.IV}`); } const resolvedUri = decrypturi ? M3U8Parser.resolve(decrypturi, baseurl) : ""; const keyFormatVersions = (decryptkeyformatversions ? decryptkeyformatversions : "1").split("/").map(Number).filter(Number.isFinite); return new LevelKey(decryptmethod, resolvedUri, decryptkeyformat, keyFormatVersions, decryptiv, keyAttrs.KEYID); } function parseStartTimeOffset(startAttributes) { const startAttrs = new AttrList(startAttributes); const startTimeOffset = startAttrs.decimalFloatingPoint("TIME-OFFSET"); if (isFiniteNumber(startTimeOffset)) { return startTimeOffset; } return null; } function setCodecs(codecsAttributeValue, level) { let codecs = (codecsAttributeValue || "").split(/[ ,]+/).filter((c) => c); ["video", "audio", "text"].forEach((type) => { const filtered = codecs.filter((codec) => isCodecType(codec, type)); if (filtered.length) { level[`${type}Codec`] = filtered.map((c) => c.split("/")[0]).join(","); codecs = codecs.filter((codec) => filtered.indexOf(codec) === -1); } }); level.unknownCodecs = codecs; } function assignCodec(media, groupItem, codecProperty) { const codecValue = groupItem[codecProperty]; if (codecValue) { media[codecProperty] = codecValue; } } function backfillProgramDateTimes(fragments, firstPdtIndex) { let fragPrev = fragments[firstPdtIndex]; for (let i = firstPdtIndex; i--; ) { const frag = fragments[i]; if (!frag) { return; } frag.programDateTime = fragPrev.programDateTime - frag.duration * 1e3; fragPrev = frag; } } function assignProgramDateTime(frag, prevFrag, programDateTimes) { if (frag.rawProgramDateTime) { programDateTimes.push(frag); } else if (prevFrag != null && prevFrag.programDateTime) { frag.programDateTime = prevFrag.endProgramDateTime; } } function setInitSegment(frag, mapAttrs, id, levelkeys) { frag.relurl = mapAttrs.URI; if (mapAttrs.BYTERANGE) { frag.setByteRange(mapAttrs.BYTERANGE); } frag.level = id; frag.sn = "initSegment"; if (levelkeys) { frag.levelkeys = levelkeys; } frag.initSegment = null; } function setFragLevelKeys(frag, levelkeys, level) { frag.levelkeys = levelkeys; const { encryptedFragments } = level; if ((!encryptedFragments.length || encryptedFragments[encryptedFragments.length - 1].levelkeys !== levelkeys) && Object.keys(levelkeys).some((format2) => levelkeys[format2].isCommonEncryption)) { encryptedFragments.push(frag); } } function assignMultipleMediaPlaylistTagOccuranceError(level, tag, result) { level.playlistParsingError = new Error(`#EXT-X-${tag} must not appear more than once (${result[0]})`); } function assignMustAppearBeforeSegmentsError(level, tag, result) { level.playlistParsingError = new Error(`#EXT-X-${tag} must appear before the first Media Segment (${result[0]})`); } function updateFromToPTS(fragFrom, fragTo) { const fragToPTS = fragTo.startPTS; if (isFiniteNumber(fragToPTS)) { let duration = 0; let frag; if (fragTo.sn > fragFrom.sn) { duration = fragToPTS - fragFrom.start; frag = fragFrom; } else { duration = fragFrom.start - fragToPTS; frag = fragTo; } if (frag.duration !== duration) { frag.setDuration(duration); } } else if (fragTo.sn > fragFrom.sn) { const contiguous = fragFrom.cc === fragTo.cc; if (contiguous && fragFrom.minEndPTS) { fragTo.setStart(fragFrom.start + (fragFrom.minEndPTS - fragFrom.start)); } else { fragTo.setStart(fragFrom.start + fragFrom.duration); } } else { fragTo.setStart(Math.max(fragFrom.start - fragTo.duration, 0)); } } function updateFragPTSDTS(details, frag, startPTS, endPTS, startDTS, endDTS) { const parsedMediaDuration = endPTS - startPTS; if (parsedMediaDuration <= 0) { logger.warn("Fragment should have a positive duration", frag); endPTS = startPTS + frag.duration; endDTS = startDTS + frag.duration; } let maxStartPTS = startPTS; let minEndPTS = endPTS; const fragStartPts = frag.startPTS; const fragEndPts = frag.endPTS; if (isFiniteNumber(fragStartPts)) { const deltaPTS = Math.abs(fragStartPts - startPTS); if (!isFiniteNumber(frag.deltaPTS)) { frag.deltaPTS = deltaPTS; } else { frag.deltaPTS = Math.max(deltaPTS, frag.deltaPTS); } maxStartPTS = Math.max(startPTS, fragStartPts); startPTS = Math.min(startPTS, fragStartPts); startDTS = Math.min(startDTS, frag.startDTS); minEndPTS = Math.min(endPTS, fragEndPts); endPTS = Math.max(endPTS, fragEndPts); endDTS = Math.max(endDTS, frag.endDTS); } const drift = startPTS - frag.start; if (frag.start !== 0) { frag.setStart(startPTS); } frag.setDuration(endPTS - frag.start); frag.startPTS = startPTS; frag.maxStartPTS = maxStartPTS; frag.startDTS = startDTS; frag.endPTS = endPTS; frag.minEndPTS = minEndPTS; frag.endDTS = endDTS; const sn = frag.sn; if (!details || sn < details.startSN || sn > details.endSN) { return 0; } let i; const fragIdx = sn - details.startSN; const fragments = details.fragments; fragments[fragIdx] = frag; for (i = fragIdx; i > 0; i--) { updateFromToPTS(fragments[i], fragments[i - 1]); } for (i = fragIdx; i < fragments.length - 1; i++) { updateFromToPTS(fragments[i], fragments[i + 1]); } if (details.fragmentHint) { updateFromToPTS(fragments[fragments.length - 1], details.fragmentHint); } details.PTSKnown = details.alignedSliding = true; return drift; } function mergeDetails(oldDetails, newDetails) { if (oldDetails === newDetails) { return; } let currentInitSegment = null; const oldFragments = oldDetails.fragments; for (let i = oldFragments.length - 1; i >= 0; i--) { const oldInit = oldFragments[i].initSegment; if (oldInit) { currentInitSegment = oldInit; break; } } if (oldDetails.fragmentHint) { delete oldDetails.fragmentHint.endPTS; } let PTSFrag; mapFragmentIntersection(oldDetails, newDetails, (oldFrag, newFrag, newFragIndex, newFragments2) => { if ((!newDetails.startCC || newDetails.skippedSegments) && newFrag.cc !== oldFrag.cc) { const ccOffset = oldFrag.cc - newFrag.cc; for (let i = newFragIndex; i < newFragments2.length; i++) { newFragments2[i].cc += ccOffset; } newDetails.endCC = newFragments2[newFragments2.length - 1].cc; } if (isFiniteNumber(oldFrag.startPTS) && isFiniteNumber(oldFrag.endPTS)) { newFrag.setStart(newFrag.startPTS = oldFrag.startPTS); newFrag.startDTS = oldFrag.startDTS; newFrag.maxStartPTS = oldFrag.maxStartPTS; newFrag.endPTS = oldFrag.endPTS; newFrag.endDTS = oldFrag.endDTS; newFrag.minEndPTS = oldFrag.minEndPTS; newFrag.setDuration(oldFrag.endPTS - oldFrag.startPTS); if (newFrag.duration) { PTSFrag = newFrag; } newDetails.PTSKnown = newDetails.alignedSliding = true; } if (oldFrag.hasStreams) { newFrag.elementaryStreams = oldFrag.elementaryStreams; } newFrag.loader = oldFrag.loader; if (oldFrag.hasStats) { newFrag.stats = oldFrag.stats; } if (oldFrag.initSegment) { newFrag.initSegment = oldFrag.initSegment; currentInitSegment = oldFrag.initSegment; } }); const newFragments = newDetails.fragments; const fragmentsToCheck = newDetails.fragmentHint ? newFragments.concat(newDetails.fragmentHint) : newFragments; if (currentInitSegment) { fragmentsToCheck.forEach((frag) => { var _currentInitSegment; if (frag && (!frag.initSegment || frag.initSegment.relurl === ((_currentInitSegment = currentInitSegment) == null ? void 0 : _currentInitSegment.relurl))) { frag.initSegment = currentInitSegment; } }); } if (newDetails.skippedSegments) { newDetails.deltaUpdateFailed = newFragments.some((frag) => !frag); if (newDetails.deltaUpdateFailed) { logger.warn("[level-helper] Previous playlist missing segments skipped in delta playlist"); for (let i = newDetails.skippedSegments; i--; ) { newFragments.shift(); } newDetails.startSN = newFragments[0].sn; } else { if (newDetails.canSkipDateRanges) { newDetails.dateRanges = mergeDateRanges(oldDetails.dateRanges, newDetails); } const programDateTimes = oldDetails.fragments.filter((frag) => frag.rawProgramDateTime); if (oldDetails.hasProgramDateTime && !newDetails.hasProgramDateTime) { for (let i = 1; i < fragmentsToCheck.length; i++) { if (fragmentsToCheck[i].programDateTime === null) { assignProgramDateTime(fragmentsToCheck[i], fragmentsToCheck[i - 1], programDateTimes); } } } mapDateRanges(programDateTimes, newDetails); } newDetails.endCC = newFragments[newFragments.length - 1].cc; } if (!newDetails.startCC) { var _fragPriorToNewStart$; const fragPriorToNewStart = getFragmentWithSN(oldDetails, newDetails.startSN - 1); newDetails.startCC = (_fragPriorToNewStart$ = fragPriorToNewStart == null ? void 0 : fragPriorToNewStart.cc) != null ? _fragPriorToNewStart$ : newFragments[0].cc; } mapPartIntersection(oldDetails.partList, newDetails.partList, (oldPart, newPart) => { newPart.elementaryStreams = oldPart.elementaryStreams; newPart.stats = oldPart.stats; }); if (PTSFrag) { updateFragPTSDTS(newDetails, PTSFrag, PTSFrag.startPTS, PTSFrag.endPTS, PTSFrag.startDTS, PTSFrag.endDTS); } else { adjustSliding(oldDetails, newDetails); } if (newFragments.length) { newDetails.totalduration = newDetails.edge - newFragments[0].start; } newDetails.driftStartTime = oldDetails.driftStartTime; newDetails.driftStart = oldDetails.driftStart; const advancedDateTime = newDetails.advancedDateTime; if (newDetails.advanced && advancedDateTime) { const edge = newDetails.edge; if (!newDetails.driftStart) { newDetails.driftStartTime = advancedDateTime; newDetails.driftStart = edge; } newDetails.driftEndTime = advancedDateTime; newDetails.driftEnd = edge; } else { newDetails.driftEndTime = oldDetails.driftEndTime; newDetails.driftEnd = oldDetails.driftEnd; newDetails.advancedDateTime = oldDetails.advancedDateTime; } if (newDetails.requestScheduled === -1) { newDetails.requestScheduled = oldDetails.requestScheduled; } } function mergeDateRanges(oldDateRanges, newDetails) { const { dateRanges: deltaDateRanges, recentlyRemovedDateranges } = newDetails; const dateRanges = _extends({}, oldDateRanges); if (recentlyRemovedDateranges) { recentlyRemovedDateranges.forEach((id) => { delete dateRanges[id]; }); } const mergeIds = Object.keys(dateRanges); const mergeCount = mergeIds.length; if (mergeCount) { Object.keys(deltaDateRanges).forEach((id) => { const mergedDateRange = dateRanges[id]; const dateRange = new DateRange(deltaDateRanges[id].attr, mergedDateRange); if (dateRange.isValid) { dateRanges[id] = dateRange; if (!mergedDateRange) { dateRange.tagOrder += mergeCount; } } else { logger.warn(`Ignoring invalid Playlist Delta Update DATERANGE tag: "${stringify(deltaDateRanges[id].attr)}"`); } }); } return dateRanges; } function mapPartIntersection(oldParts, newParts, intersectionFn) { if (oldParts && newParts) { let delta = 0; for (let i = 0, len = oldParts.length; i <= len; i++) { const oldPart = oldParts[i]; const newPart = newParts[i + delta]; if (oldPart && newPart && oldPart.index === newPart.index && oldPart.fragment.sn === newPart.fragment.sn) { intersectionFn(oldPart, newPart); } else { delta--; } } } } function mapFragmentIntersection(oldDetails, newDetails, intersectionFn) { const skippedSegments = newDetails.skippedSegments; const start = Math.max(oldDetails.startSN, newDetails.startSN) - newDetails.startSN; const end = (oldDetails.fragmentHint ? 1 : 0) + (skippedSegments ? newDetails.endSN : Math.min(oldDetails.endSN, newDetails.endSN)) - newDetails.startSN; const delta = newDetails.startSN - oldDetails.startSN; const newFrags = newDetails.fragmentHint ? newDetails.fragments.concat(newDetails.fragmentHint) : newDetails.fragments; const oldFrags = oldDetails.fragmentHint ? oldDetails.fragments.concat(oldDetails.fragmentHint) : oldDetails.fragments; for (let i = start; i <= end; i++) { const oldFrag = oldFrags[delta + i]; let newFrag = newFrags[i]; if (skippedSegments && !newFrag && oldFrag) { newFrag = newDetails.fragments[i] = oldFrag; } if (oldFrag && newFrag) { intersectionFn(oldFrag, newFrag, i, newFrags); if (oldFrag.url && oldFrag.url !== newFrag.url) { newDetails.playlistParsingError = getSequenceError(`media sequence mismatch ${newFrag.sn}:`, oldDetails, newDetails, oldFrag, newFrag); return; } else if (oldFrag.cc !== newFrag.cc) { newDetails.playlistParsingError = getSequenceError(`discontinuity sequence mismatch (${oldFrag.cc}!=${newFrag.cc})`, oldDetails, newDetails, oldFrag, newFrag); return; } } } } function getSequenceError(message, oldDetails, newDetails, oldFrag, newFrag) { return new Error(`${message} ${newFrag.url} Playlist starting @${oldDetails.startSN} ${oldDetails.m3u8} Playlist starting @${newDetails.startSN} ${newDetails.m3u8}`); } function adjustSliding(oldDetails, newDetails, matchingStableVariantOrRendition = true) { const delta = newDetails.startSN + newDetails.skippedSegments - oldDetails.startSN; const oldFragments = oldDetails.fragments; const advancedOrStable = delta >= 0; let sliding = 0; if (advancedOrStable && delta < oldFragments.length) { sliding = oldFragments[delta].start; } else if (advancedOrStable && newDetails.startSN === oldDetails.endSN + 1) { sliding = oldDetails.fragmentEnd; } else if (advancedOrStable && matchingStableVariantOrRendition) { sliding = oldDetails.fragmentStart + delta * newDetails.levelTargetDuration; } else if (!newDetails.skippedSegments && newDetails.fragmentStart === 0) { sliding = oldDetails.fragmentStart; } else { return; } addSliding(newDetails, sliding); } function addSliding(details, sliding) { if (sliding) { const fragments = details.fragments; for (let i = details.skippedSegments; i < fragments.length; i++) { fragments[i].addStart(sliding); } if (details.fragmentHint) { details.fragmentHint.addStart(sliding); } } } function computeReloadInterval(newDetails, distanceToLiveEdgeMs = Infinity) { let reloadInterval = 1e3 * newDetails.targetduration; if (newDetails.updated) { const fragments = newDetails.fragments; const liveEdgeMaxTargetDurations = 4; if (fragments.length && reloadInterval * liveEdgeMaxTargetDurations > distanceToLiveEdgeMs) { const lastSegmentDuration = fragments[fragments.length - 1].duration * 1e3; if (lastSegmentDuration < reloadInterval) { reloadInterval = lastSegmentDuration; } } } else { reloadInterval /= 2; } return Math.round(reloadInterval); } function getFragmentWithSN(details, sn, fragCurrent) { if (!details) { return null; } let fragment = details.fragments[sn - details.startSN]; if (fragment) { return fragment; } fragment = details.fragmentHint; if (fragment && fragment.sn === sn) { return fragment; } if (sn < details.startSN && fragCurrent && fragCurrent.sn === sn) { return fragCurrent; } return null; } function getPartWith(details, sn, partIndex) { if (!details) { return null; } return findPart(details.partList, sn, partIndex); } function findPart(partList, sn, partIndex) { if (partList) { for (let i = partList.length; i--; ) { const part = partList[i]; if (part.index === partIndex && part.fragment.sn === sn) { return part; } } } return null; } function reassignFragmentLevelIndexes(levels) { levels.forEach((level, index) => { var _level$details; (_level$details = level.details) == null ? void 0 : _level$details.fragments.forEach((fragment) => { fragment.level = index; if (fragment.initSegment) { fragment.initSegment.level = index; } }); }); } function findFirstFragWithCC(fragments, cc) { for (let i = 0, len = fragments.length; i < len; i++) { var _fragments$i; if (((_fragments$i = fragments[i]) == null ? void 0 : _fragments$i.cc) === cc) { return fragments[i]; } } return null; } function shouldAlignOnDiscontinuities(refDetails, details) { if (refDetails) { if (details.startCC < refDetails.endCC && details.endCC > refDetails.startCC) { return true; } } return false; } function adjustFragmentStart(frag, sliding) { if (frag) { const start = frag.start + sliding; frag.start = frag.startPTS = start; frag.endPTS = start + frag.duration; } } function adjustSlidingStart(sliding, details) { const fragments = details.fragments; for (let i = 0, len = fragments.length; i < len; i++) { adjustFragmentStart(fragments[i], sliding); } if (details.fragmentHint) { adjustFragmentStart(details.fragmentHint, sliding); } details.alignedSliding = true; } function alignStream(switchDetails, details) { if (!switchDetails) { return; } alignDiscontinuities(details, switchDetails); if (!details.alignedSliding && switchDetails) { alignMediaPlaylistByPDT(details, switchDetails); } if (!details.alignedSliding && switchDetails && !details.skippedSegments) { adjustSliding(switchDetails, details, false); } } function alignDiscontinuities(details, refDetails) { if (!shouldAlignOnDiscontinuities(refDetails, details)) { return; } const targetCC = Math.min(refDetails.endCC, details.endCC); const refFrag = findFirstFragWithCC(refDetails.fragments, targetCC); const frag = findFirstFragWithCC(details.fragments, targetCC); if (!refFrag || !frag) { return; } logger.log(`Aligning playlist at start of dicontinuity sequence ${targetCC}`); const delta = refFrag.start - frag.start; adjustSlidingStart(delta, details); } function alignMediaPlaylistByPDT(details, refDetails) { if (!details.hasProgramDateTime || !refDetails.hasProgramDateTime) { return; } const fragments = details.fragments; const refFragments = refDetails.fragments; if (!fragments.length || !refFragments.length) { return; } let refFrag; let frag; const targetCC = Math.min(refDetails.endCC, details.endCC); if (refDetails.startCC < targetCC && details.startCC < targetCC) { refFrag = findFirstFragWithCC(refFragments, targetCC); frag = findFirstFragWithCC(fragments, targetCC); } if (!refFrag || !frag) { refFrag = refFragments[Math.floor(refFragments.length / 2)]; frag = findFirstFragWithCC(fragments, refFrag.cc) || fragments[Math.floor(fragments.length / 2)]; } const refPDT = refFrag.programDateTime; const targetPDT = frag.programDateTime; if (!refPDT || !targetPDT) { return; } const delta = (targetPDT - refPDT) / 1e3 - (frag.start - refFrag.start); adjustSlidingStart(delta, details); } var TimeRanges = { toString: function(r) { let log = ""; const len = r.length; for (let i = 0; i < len; i++) { log += `[${r.start(i).toFixed(3)}-${r.end(i).toFixed(3)}]`; } return log; } }; var State = { STOPPED: "STOPPED", IDLE: "IDLE", KEY_LOADING: "KEY_LOADING", FRAG_LOADING: "FRAG_LOADING", FRAG_LOADING_WAITING_RETRY: "FRAG_LOADING_WAITING_RETRY", WAITING_TRACK: "WAITING_TRACK", PARSING: "PARSING", PARSED: "PARSED", ENDED: "ENDED", ERROR: "ERROR", WAITING_INIT_PTS: "WAITING_INIT_PTS", WAITING_LEVEL: "WAITING_LEVEL" }; var BaseStreamController = class extends TaskLoop { constructor(hls, fragmentTracker, keyLoader, logPrefix, playlistType) { super(logPrefix, hls.logger); this.hls = void 0; this.fragPrevious = null; this.fragCurrent = null; this.fragmentTracker = void 0; this.transmuxer = null; this._state = State.STOPPED; this.playlistType = void 0; this.media = null; this.mediaBuffer = null; this.config = void 0; this.bitrateTest = false; this.lastCurrentTime = 0; this.nextLoadPosition = 0; this.startPosition = 0; this.startTimeOffset = null; this.retryDate = 0; this.levels = null; this.fragmentLoader = void 0; this.keyLoader = void 0; this.levelLastLoaded = null; this.startFragRequested = false; this.decrypter = void 0; this.initPTS = []; this.buffering = true; this.loadingParts = false; this.loopSn = void 0; this.onMediaSeeking = () => { const { config, fragCurrent, media, mediaBuffer, state } = this; const currentTime = media ? media.currentTime : 0; const bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole); const noFowardBuffer = !bufferInfo.len; this.log(`Media seeking to ${isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime}, state: ${state}, ${noFowardBuffer ? "out of" : "in"} buffer`); if (this.state === State.ENDED) { this.resetLoadingState(); } else if (fragCurrent) { const tolerance = config.maxFragLookUpTolerance; const fragStartOffset = fragCurrent.start - tolerance; const fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance; if (noFowardBuffer || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) { const pastFragment = currentTime > fragEndOffset; if (currentTime < fragStartOffset || pastFragment) { if (pastFragment && fragCurrent.loader) { this.log(`Cancelling fragment load for seek (sn: ${fragCurrent.sn})`); fragCurrent.abortRequests(); this.resetLoadingState(); } this.fragPrevious = null; } } } if (media) { this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, this.playlistType, true); const lastCurrentTime = this.lastCurrentTime; if (currentTime > lastCurrentTime) { this.lastCurrentTime = currentTime; } if (!this.loadingParts) { const bufferEnd = Math.max(bufferInfo.end, currentTime); const shouldLoadParts = this.shouldLoadParts(this.getLevelDetails(), bufferEnd); if (shouldLoadParts) { this.log(`LL-Part loading ON after seeking to ${currentTime.toFixed(2)} with buffer @${bufferEnd.toFixed(2)}`); this.loadingParts = shouldLoadParts; } } } if (!this.hls.hasEnoughToStart) { this.log(`Setting ${noFowardBuffer ? "startPosition" : "nextLoadPosition"} to ${currentTime} for seek without enough to start`); this.nextLoadPosition = currentTime; if (noFowardBuffer) { this.startPosition = currentTime; } } this.tickImmediate(); }; this.onMediaEnded = () => { this.log(`setting startPosition to 0 because media ended`); this.startPosition = this.lastCurrentTime = 0; }; this.playlistType = playlistType; this.hls = hls; this.fragmentLoader = new FragmentLoader(hls.config); this.keyLoader = keyLoader; this.fragmentTracker = fragmentTracker; this.config = hls.config; this.decrypter = new Decrypter(hls.config); } registerListeners() { const { hls } = this; hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this); hls.on(Events.ERROR, this.onError, this); } unregisterListeners() { const { hls } = this; hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this); hls.off(Events.ERROR, this.onError, this); } doTick() { this.onTickEnd(); } onTickEnd() { } startLoad(startPosition) { } stopLoad() { if (this.state === State.STOPPED) { return; } this.fragmentLoader.abort(); this.keyLoader.abort(this.playlistType); const frag = this.fragCurrent; if (frag != null && frag.loader) { frag.abortRequests(); this.fragmentTracker.removeFragment(frag); } this.resetTransmuxer(); this.fragCurrent = null; this.fragPrevious = null; this.clearInterval(); this.clearNextTick(); this.state = State.STOPPED; } get startPositionValue() { const { nextLoadPosition, startPosition } = this; if (startPosition === -1 && nextLoadPosition) { return nextLoadPosition; } return startPosition; } get bufferingEnabled() { return this.buffering; } pauseBuffering() { this.buffering = false; } resumeBuffering() { this.buffering = true; } get inFlightFrag() { return { frag: this.fragCurrent, state: this.state }; } _streamEnded(bufferInfo, levelDetails) { if (levelDetails.live || !this.media) { return false; } const bufferEnd = bufferInfo.end || 0; const timelineStart = this.config.timelineOffset || 0; if (bufferEnd <= timelineStart) { return false; } const bufferedRanges = bufferInfo.buffered; if (this.config.maxBufferHole && bufferedRanges && bufferedRanges.length > 1) { bufferInfo = BufferHelper.bufferedInfo(bufferedRanges, bufferInfo.start, 0); } const nextStart = bufferInfo.nextStart; const hasSecondBufferedRange = nextStart && nextStart > timelineStart && nextStart < levelDetails.edge; if (hasSecondBufferedRange) { return false; } if (this.media.currentTime < bufferInfo.start) { return false; } const partList = levelDetails.partList; if (partList != null && partList.length) { const lastPart = partList[partList.length - 1]; const lastPartBuffered = BufferHelper.isBuffered(this.media, lastPart.start + lastPart.duration / 2); return lastPartBuffered; } const playlistType = levelDetails.fragments[levelDetails.fragments.length - 1].type; return this.fragmentTracker.isEndListAppended(playlistType); } getLevelDetails() { if (this.levels && this.levelLastLoaded !== null) { var _this$levelLastLoaded; return (_this$levelLastLoaded = this.levelLastLoaded) == null ? void 0 : _this$levelLastLoaded.details; } } get timelineOffset() { const configuredTimelineOffset = this.config.timelineOffset; if (configuredTimelineOffset) { var _this$getLevelDetails; return ((_this$getLevelDetails = this.getLevelDetails()) == null ? void 0 : _this$getLevelDetails.appliedTimelineOffset) || configuredTimelineOffset; } return 0; } onMediaAttached(event, data) { const media = this.media = this.mediaBuffer = data.media; media.removeEventListener("seeking", this.onMediaSeeking); media.removeEventListener("ended", this.onMediaEnded); media.addEventListener("seeking", this.onMediaSeeking); media.addEventListener("ended", this.onMediaEnded); const config = this.config; if (this.levels && config.autoStartLoad && this.state === State.STOPPED) { this.startLoad(config.startPosition); } } onMediaDetaching(event, data) { const transferringMedia = !!data.transferMedia; const media = this.media; if (media === null) { return; } if (media.ended) { this.log("MSE detaching and video ended, reset startPosition"); this.startPosition = this.lastCurrentTime = 0; } media.removeEventListener("seeking", this.onMediaSeeking); media.removeEventListener("ended", this.onMediaEnded); if (this.keyLoader && !transferringMedia) { this.keyLoader.detach(); } this.media = this.mediaBuffer = null; this.loopSn = void 0; if (transferringMedia) { this.resetLoadingState(); this.resetTransmuxer(); return; } this.loadingParts = false; this.fragmentTracker.removeAllFragments(); this.stopLoad(); } onManifestLoading() { this.initPTS = []; this.levels = this.levelLastLoaded = this.fragCurrent = null; this.lastCurrentTime = this.startPosition = 0; this.startFragRequested = false; } onError(event, data) { } onManifestLoaded(event, data) { this.startTimeOffset = data.startTimeOffset; } onHandlerDestroying() { this.stopLoad(); if (this.transmuxer) { this.transmuxer.destroy(); this.transmuxer = null; } super.onHandlerDestroying(); this.hls = this.onMediaSeeking = this.onMediaEnded = null; } onHandlerDestroyed() { this.state = State.STOPPED; if (this.fragmentLoader) { this.fragmentLoader.destroy(); } if (this.keyLoader) { this.keyLoader.destroy(); } if (this.decrypter) { this.decrypter.destroy(); } this.hls = this.log = this.warn = this.decrypter = this.keyLoader = this.fragmentLoader = this.fragmentTracker = null; super.onHandlerDestroyed(); } loadFragment(frag, level, targetBufferTime) { this.startFragRequested = true; this._loadFragForPlayback(frag, level, targetBufferTime); } _loadFragForPlayback(fragment, level, targetBufferTime) { const progressCallback = (data) => { const frag = data.frag; if (this.fragContextChanged(frag)) { this.warn(`${frag.type} sn: ${frag.sn}${data.part ? " part: " + data.part.index : ""} of ${this.fragInfo(frag, false, data.part)}) was dropped during download.`); this.fragmentTracker.removeFragment(frag); return; } frag.stats.chunkCount++; this._handleFragmentLoadProgress(data); }; this._doFragLoad(fragment, level, targetBufferTime, progressCallback).then((data) => { if (!data) { return; } const state = this.state; const frag = data.frag; if (this.fragContextChanged(frag)) { if (state === State.FRAG_LOADING || !this.fragCurrent && state === State.PARSING) { this.fragmentTracker.removeFragment(frag); this.state = State.IDLE; } return; } if ("payload" in data) { this.log(`Loaded ${frag.type} sn: ${frag.sn} of ${this.playlistLabel()} ${frag.level}`); this.hls.trigger(Events.FRAG_LOADED, data); } this._handleFragmentLoadComplete(data); }).catch((reason) => { if (this.state === State.STOPPED || this.state === State.ERROR) { return; } this.warn(`Frag error: ${(reason == null ? void 0 : reason.message) || reason}`); this.resetFragmentLoading(fragment); }); } clearTrackerIfNeeded(frag) { var _this$mediaBuffer; const { fragmentTracker } = this; const fragState = fragmentTracker.getState(frag); if (fragState === FragmentState.APPENDING) { const playlistType = frag.type; const bufferedInfo = this.getFwdBufferInfo(this.mediaBuffer, playlistType); const minForwardBufferLength = Math.max(frag.duration, bufferedInfo ? bufferedInfo.len : this.config.maxBufferLength); const backtrackFragment = this.backtrackFragment; const backtracked = backtrackFragment ? frag.sn - backtrackFragment.sn : 0; if (backtracked === 1 || this.reduceMaxBufferLength(minForwardBufferLength, frag.duration)) { fragmentTracker.removeFragment(frag); } } else if (((_this$mediaBuffer = this.mediaBuffer) == null ? void 0 : _this$mediaBuffer.buffered.length) === 0) { fragmentTracker.removeAllFragments(); } else if (fragmentTracker.hasParts(frag.type)) { fragmentTracker.detectPartialFragments({ frag, part: null, stats: frag.stats, id: frag.type }); if (fragmentTracker.getState(frag) === FragmentState.PARTIAL) { fragmentTracker.removeFragment(frag); } } } checkLiveUpdate(details) { if (details.updated && !details.live) { const lastFragment = details.fragments[details.fragments.length - 1]; this.fragmentTracker.detectPartialFragments({ frag: lastFragment, part: null, stats: lastFragment.stats, id: lastFragment.type }); } if (!details.fragments[0]) { details.deltaUpdateFailed = true; } } waitForLive(levelInfo) { const details = levelInfo.details; return (details == null ? void 0 : details.live) && details.type !== "EVENT" && (this.levelLastLoaded !== levelInfo || details.expired); } flushMainBuffer(startOffset, endOffset, type = null) { if (!(startOffset - endOffset)) { return; } const flushScope = { startOffset, endOffset, type }; this.hls.trigger(Events.BUFFER_FLUSHING, flushScope); } _loadInitSegment(fragment, level) { this._doFragLoad(fragment, level).then((data) => { const frag = data == null ? void 0 : data.frag; if (!frag || this.fragContextChanged(frag) || !this.levels) { throw new Error("init load aborted"); } return data; }).then((data) => { const { hls } = this; const { frag, payload } = data; const decryptData = frag.decryptdata; if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && isFullSegmentEncryption(decryptData.method)) { const startTime = self.performance.now(); return this.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer, getAesModeFromFullSegmentMethod(decryptData.method)).catch((err) => { hls.trigger(Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_DECRYPT_ERROR, fatal: false, error: err, reason: err.message, frag }); throw err; }).then((decryptedData) => { const endTime = self.performance.now(); hls.trigger(Events.FRAG_DECRYPTED, { frag, payload: decryptedData, stats: { tstart: startTime, tdecrypt: endTime } }); data.payload = decryptedData; return this.completeInitSegmentLoad(data); }); } return this.completeInitSegmentLoad(data); }).catch((reason) => { if (this.state === State.STOPPED || this.state === State.ERROR) { return; } this.warn(reason); this.resetFragmentLoading(fragment); }); } completeInitSegmentLoad(data) { const { levels } = this; if (!levels) { throw new Error("init load aborted, missing levels"); } const stats = data.frag.stats; if (this.state !== State.STOPPED) { this.state = State.IDLE; } data.frag.data = new Uint8Array(data.payload); stats.parsing.start = stats.buffering.start = self.performance.now(); stats.parsing.end = stats.buffering.end = self.performance.now(); this.tick(); } fragContextChanged(frag) { const { fragCurrent } = this; return !frag || !fragCurrent || frag.sn !== fragCurrent.sn || frag.level !== fragCurrent.level; } fragBufferedComplete(frag, part) { const media = this.mediaBuffer ? this.mediaBuffer : this.media; this.log(`Buffered ${frag.type} sn: ${frag.sn}${part ? " part: " + part.index : ""} of ${this.fragInfo(frag, false, part)} > buffer:${media ? TimeRanges.toString(BufferHelper.getBuffered(media)) : "(detached)"})`); if (isMediaFragment(frag)) { var _this$levels; if (frag.type !== PlaylistLevelType.SUBTITLE) { const el = frag.elementaryStreams; if (!Object.keys(el).some((type) => !!el[type])) { this.state = State.IDLE; return; } } const level = (_this$levels = this.levels) == null ? void 0 : _this$levels[frag.level]; if (level != null && level.fragmentError) { this.log(`Resetting level fragment error count of ${level.fragmentError} on frag buffered`); level.fragmentError = 0; } } this.state = State.IDLE; } _handleFragmentLoadComplete(fragLoadedEndData) { const { transmuxer } = this; if (!transmuxer) { return; } const { frag, part, partsLoaded } = fragLoadedEndData; const complete = !partsLoaded || partsLoaded.length === 0 || partsLoaded.some((fragLoaded) => !fragLoaded); const chunkMeta = new ChunkMetadata(frag.level, frag.sn, frag.stats.chunkCount + 1, 0, part ? part.index : -1, !complete); transmuxer.flush(chunkMeta); } _handleFragmentLoadProgress(frag) { } _doFragLoad(frag, level, targetBufferTime = null, progressCallback) { var _frag$decryptdata; this.fragCurrent = frag; const details = level == null ? void 0 : level.details; if (!this.levels || !details) { throw new Error(`frag load aborted, missing level${details ? "" : " detail"}s`); } let keyLoadingPromise = null; if (frag.encrypted && !((_frag$decryptdata = frag.decryptdata) != null && _frag$decryptdata.key)) { this.log(`Loading key for ${frag.sn} of [${details.startSN}-${details.endSN}], ${this.playlistLabel()} ${frag.level}`); this.state = State.KEY_LOADING; this.fragCurrent = frag; keyLoadingPromise = this.keyLoader.load(frag).then((keyLoadedData) => { if (!this.fragContextChanged(keyLoadedData.frag)) { this.hls.trigger(Events.KEY_LOADED, keyLoadedData); if (this.state === State.KEY_LOADING) { this.state = State.IDLE; } return keyLoadedData; } }); this.hls.trigger(Events.KEY_LOADING, { frag }); if (this.fragCurrent === null) { keyLoadingPromise = Promise.reject(new Error(`frag load aborted, context changed in KEY_LOADING`)); } } else if (!frag.encrypted) { keyLoadingPromise = this.keyLoader.loadClear(frag, details.encryptedFragments, this.startFragRequested); if (keyLoadingPromise) { this.log(`[eme] blocking frag load until media-keys acquired`); } } const fragPrevious = this.fragPrevious; if (isMediaFragment(frag) && (!fragPrevious || frag.sn !== fragPrevious.sn)) { const shouldLoadParts = this.shouldLoadParts(level.details, frag.end); if (shouldLoadParts !== this.loadingParts) { this.log(`LL-Part loading ${shouldLoadParts ? "ON" : "OFF"} loading sn ${fragPrevious == null ? void 0 : fragPrevious.sn}->${frag.sn}`); this.loadingParts = shouldLoadParts; } } targetBufferTime = Math.max(frag.start, targetBufferTime || 0); if (this.loadingParts && isMediaFragment(frag)) { const partList = details.partList; if (partList && progressCallback) { if (targetBufferTime > frag.end && details.fragmentHint) { frag = details.fragmentHint; } const partIndex = this.getNextPart(partList, frag, targetBufferTime); if (partIndex > -1) { const part = partList[partIndex]; frag = this.fragCurrent = part.fragment; this.log(`Loading ${frag.type} sn: ${frag.sn} part: ${part.index} (${partIndex}/${partList.length - 1}) of ${this.fragInfo(frag, false, part)}) cc: ${frag.cc} [${details.startSN}-${details.endSN}], target: ${parseFloat(targetBufferTime.toFixed(3))}`); this.nextLoadPosition = part.start + part.duration; this.state = State.FRAG_LOADING; let _result; if (keyLoadingPromise) { _result = keyLoadingPromise.then((keyLoadedData) => { if (!keyLoadedData || this.fragContextChanged(keyLoadedData.frag)) { return null; } return this.doFragPartsLoad(frag, part, level, progressCallback); }).catch((error) => this.handleFragLoadError(error)); } else { _result = this.doFragPartsLoad(frag, part, level, progressCallback).catch((error) => this.handleFragLoadError(error)); } this.hls.trigger(Events.FRAG_LOADING, { frag, part, targetBufferTime }); if (this.fragCurrent === null) { return Promise.reject(new Error(`frag load aborted, context changed in FRAG_LOADING parts`)); } return _result; } else if (!frag.url || this.loadedEndOfParts(partList, targetBufferTime)) { return Promise.resolve(null); } } } if (isMediaFragment(frag) && this.loadingParts) { this.log(`LL-Part loading OFF after next part miss @${targetBufferTime.toFixed(2)}`); this.loadingParts = false; } else if (!frag.url) { return Promise.resolve(null); } this.log(`Loading ${frag.type} sn: ${frag.sn} of ${this.fragInfo(frag, false)}) cc: ${frag.cc} ${details ? "[" + details.startSN + "-" + details.endSN + "]" : ""}, target: ${parseFloat(targetBufferTime.toFixed(3))}`); if (isFiniteNumber(frag.sn) && !this.bitrateTest) { this.nextLoadPosition = frag.start + frag.duration; } this.state = State.FRAG_LOADING; const dataOnProgress = this.config.progressive; let result; if (dataOnProgress && keyLoadingPromise) { result = keyLoadingPromise.then((keyLoadedData) => { if (!keyLoadedData || this.fragContextChanged(keyLoadedData == null ? void 0 : keyLoadedData.frag)) { return null; } return this.fragmentLoader.load(frag, progressCallback); }).catch((error) => this.handleFragLoadError(error)); } else { result = Promise.all([this.fragmentLoader.load(frag, dataOnProgress ? progressCallback : void 0), keyLoadingPromise]).then(([fragLoadedData]) => { if (!dataOnProgress && fragLoadedData && progressCallback) { progressCallback(fragLoadedData); } return fragLoadedData; }).catch((error) => this.handleFragLoadError(error)); } this.hls.trigger(Events.FRAG_LOADING, { frag, targetBufferTime }); if (this.fragCurrent === null) { return Promise.reject(new Error(`frag load aborted, context changed in FRAG_LOADING`)); } return result; } doFragPartsLoad(frag, fromPart, level, progressCallback) { return new Promise((resolve, reject) => { var _level$details; const partsLoaded = []; const initialPartList = (_level$details = level.details) == null ? void 0 : _level$details.partList; const loadPart = (part) => { this.fragmentLoader.loadPart(frag, part, progressCallback).then((partLoadedData) => { partsLoaded[part.index] = partLoadedData; const loadedPart = partLoadedData.part; this.hls.trigger(Events.FRAG_LOADED, partLoadedData); const nextPart = getPartWith(level.details, frag.sn, part.index + 1) || findPart(initialPartList, frag.sn, part.index + 1); if (nextPart) { loadPart(nextPart); } else { return resolve({ frag, part: loadedPart, partsLoaded }); } }).catch(reject); }; loadPart(fromPart); }); } handleFragLoadError(error) { if ("data" in error) { const data = error.data; if (error.data && data.details === ErrorDetails.INTERNAL_ABORTED) { this.handleFragLoadAborted(data.frag, data.part); } else { this.hls.trigger(Events.ERROR, data); } } else { this.hls.trigger(Events.ERROR, { type: ErrorTypes.OTHER_ERROR, details: ErrorDetails.INTERNAL_EXCEPTION, err: error, error, fatal: true }); } return null; } _handleTransmuxerFlush(chunkMeta) { const context = this.getCurrentContext(chunkMeta); if (!context || this.state !== State.PARSING) { if (!this.fragCurrent && this.state !== State.STOPPED && this.state !== State.ERROR) { this.state = State.IDLE; } return; } const { frag, part, level } = context; const now2 = self.performance.now(); frag.stats.parsing.end = now2; if (part) { part.stats.parsing.end = now2; } const levelDetails = this.getLevelDetails(); const loadingPartsAtEdge = levelDetails && frag.sn > levelDetails.endSN; const shouldLoadParts = loadingPartsAtEdge || this.shouldLoadParts(levelDetails, frag.end); if (shouldLoadParts !== this.loadingParts) { this.log(`LL-Part loading ${shouldLoadParts ? "ON" : "OFF"} after parsing segment ending @${frag.end.toFixed(2)}`); this.loadingParts = shouldLoadParts; } this.updateLevelTiming(frag, part, level, chunkMeta.partial); } shouldLoadParts(details, bufferEnd) { if (this.config.lowLatencyMode) { if (!details) { return this.loadingParts; } if (details != null && details.partList) { var _details$fragmentHint; const firstPart = details.partList[0]; const safePartStart = firstPart.end + (((_details$fragmentHint = details.fragmentHint) == null ? void 0 : _details$fragmentHint.duration) || 0); if (bufferEnd >= safePartStart) { var _this$media; const playhead = this.hls.hasEnoughToStart ? ((_this$media = this.media) == null ? void 0 : _this$media.currentTime) || this.lastCurrentTime : this.getLoadPosition(); if (playhead > firstPart.start - firstPart.fragment.duration) { return true; } } } } return false; } getCurrentContext(chunkMeta) { const { levels, fragCurrent } = this; const { level: levelIndex, sn, part: partIndex } = chunkMeta; if (!(levels != null && levels[levelIndex])) { this.warn(`Levels object was unset while buffering fragment ${sn} of ${this.playlistLabel()} ${levelIndex}. The current chunk will not be buffered.`); return null; } const level = levels[levelIndex]; const levelDetails = level.details; const part = partIndex > -1 ? getPartWith(levelDetails, sn, partIndex) : null; const frag = part ? part.fragment : getFragmentWithSN(levelDetails, sn, fragCurrent); if (!frag) { return null; } if (fragCurrent && fragCurrent !== frag) { frag.stats = fragCurrent.stats; } return { frag, part, level }; } bufferFragmentData(data, frag, part, chunkMeta, noBacktracking) { var _buffer; if (!data || this.state !== State.PARSING) { return; } const { data1, data2 } = data; let buffer = data1; if (data1 && data2) { buffer = appendUint8Array(data1, data2); } if (!((_buffer = buffer) != null && _buffer.length)) { return; } const offsetTimestamp = this.initPTS[frag.cc]; const offset = offsetTimestamp ? -offsetTimestamp.baseTime / offsetTimestamp.timescale : void 0; const segment = { type: data.type, frag, part, chunkMeta, offset, parent: frag.type, data: buffer }; this.hls.trigger(Events.BUFFER_APPENDING, segment); if (data.dropped && data.independent && !part) { if (noBacktracking) { return; } this.flushBufferGap(frag); } } flushBufferGap(frag) { const media = this.media; if (!media) { return; } if (!BufferHelper.isBuffered(media, media.currentTime)) { this.flushMainBuffer(0, frag.start); return; } const currentTime = media.currentTime; const bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0); const fragDuration = frag.duration; const segmentFraction = Math.min(this.config.maxFragLookUpTolerance * 2, fragDuration * 0.25); const start = Math.max(Math.min(frag.start - segmentFraction, bufferInfo.end - segmentFraction), currentTime + segmentFraction); if (frag.start - start > segmentFraction) { this.flushMainBuffer(start, frag.start); } } getFwdBufferInfo(bufferable, type) { var _this$media2; const pos = this.getLoadPosition(); if (!isFiniteNumber(pos)) { return null; } const backwardSeek = this.lastCurrentTime > pos; const maxBufferHole = backwardSeek || (_this$media2 = this.media) != null && _this$media2.paused ? 0 : this.config.maxBufferHole; return this.getFwdBufferInfoAtPos(bufferable, pos, type, maxBufferHole); } getFwdBufferInfoAtPos(bufferable, pos, type, maxBufferHole) { const bufferInfo = BufferHelper.bufferInfo(bufferable, pos, maxBufferHole); if (bufferInfo.len === 0 && bufferInfo.nextStart !== void 0) { const bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type); if (bufferedFragAtPos && (bufferInfo.nextStart <= bufferedFragAtPos.end || bufferedFragAtPos.gap)) { const gapDuration = Math.max(Math.min(bufferInfo.nextStart, bufferedFragAtPos.end) - pos, maxBufferHole); return BufferHelper.bufferInfo(bufferable, pos, gapDuration); } } return bufferInfo; } getMaxBufferLength(levelBitrate) { const { config } = this; let maxBufLen; if (levelBitrate) { maxBufLen = Math.max(8 * config.maxBufferSize / levelBitrate, config.maxBufferLength); } else { maxBufLen = config.maxBufferLength; } return Math.min(maxBufLen, config.maxMaxBufferLength); } reduceMaxBufferLength(threshold, fragDuration) { const config = this.config; const minLength = Math.max(Math.min(threshold - fragDuration, config.maxBufferLength), fragDuration); const reducedLength = Math.max(threshold - fragDuration * 3, config.maxMaxBufferLength / 2, minLength); if (reducedLength >= minLength) { config.maxMaxBufferLength = reducedLength; this.warn(`Reduce max buffer length to ${reducedLength}s`); return true; } return false; } getAppendedFrag(position, playlistType = PlaylistLevelType.MAIN) { var _this$fragmentTracker; const fragOrPart = (_this$fragmentTracker = this.fragmentTracker) == null ? void 0 : _this$fragmentTracker.getAppendedFrag(position, playlistType); if (fragOrPart && "fragment" in fragOrPart) { return fragOrPart.fragment; } return fragOrPart; } getNextFragment(pos, levelDetails) { const fragments = levelDetails.fragments; const fragLen = fragments.length; if (!fragLen) { return null; } const { config } = this; const start = fragments[0].start; const canLoadParts = config.lowLatencyMode && !!levelDetails.partList; let frag = null; if (levelDetails.live) { const initialLiveManifestSize = config.initialLiveManifestSize; if (fragLen < initialLiveManifestSize) { this.warn(`Not enough fragments to start playback (have: ${fragLen}, need: ${initialLiveManifestSize})`); return null; } if (!levelDetails.PTSKnown && !this.startFragRequested && this.startPosition === -1 || pos < start) { var _frag; if (canLoadParts && !this.loadingParts) { this.log(`LL-Part loading ON for initial live fragment`); this.loadingParts = true; } frag = this.getInitialLiveFragment(levelDetails); const mainStart = this.hls.startPosition; const liveSyncPosition = this.hls.liveSyncPosition; const startPosition = frag ? (mainStart !== -1 && mainStart >= start ? mainStart : liveSyncPosition) || frag.start : pos; this.log(`Setting startPosition to ${startPosition} to match start frag at live edge. mainStart: ${mainStart} liveSyncPosition: ${liveSyncPosition} frag.start: ${(_frag = frag) == null ? void 0 : _frag.start}`); this.startPosition = this.nextLoadPosition = startPosition; } } else if (pos <= start) { frag = fragments[0]; } if (!frag) { const end = this.loadingParts ? levelDetails.partEnd : levelDetails.fragmentEnd; frag = this.getFragmentAtPosition(pos, end, levelDetails); } let programFrag = this.filterReplacedPrimary(frag, levelDetails); if (!programFrag && frag) { const curSNIdx = frag.sn - levelDetails.startSN; programFrag = this.filterReplacedPrimary(fragments[curSNIdx + 1] || null, levelDetails); } return this.mapToInitFragWhenRequired(programFrag); } isLoopLoading(frag, targetBufferTime) { const trackerState = this.fragmentTracker.getState(frag); return (trackerState === FragmentState.OK || trackerState === FragmentState.PARTIAL && !!frag.gap) && this.nextLoadPosition > targetBufferTime; } getNextFragmentLoopLoading(frag, levelDetails, bufferInfo, playlistType, maxBufLen) { let nextFragment = null; if (frag.gap) { nextFragment = this.getNextFragment(this.nextLoadPosition, levelDetails); if (nextFragment && !nextFragment.gap && bufferInfo.nextStart) { const nextbufferInfo = this.getFwdBufferInfoAtPos(this.mediaBuffer ? this.mediaBuffer : this.media, bufferInfo.nextStart, playlistType, 0); if (nextbufferInfo !== null && bufferInfo.len + nextbufferInfo.len >= maxBufLen) { const sn = nextFragment.sn; if (this.loopSn !== sn) { this.log(`buffer full after gaps in "${playlistType}" playlist starting at sn: ${sn}`); this.loopSn = sn; } return null; } } } this.loopSn = void 0; return nextFragment; } get primaryPrefetch() { if (interstitialsEnabled(this.hls.config)) { var _this$hls$interstitia, _this$hls$interstitia2; const playingInterstitial = (_this$hls$interstitia = this.hls.interstitialsManager) == null ? void 0 : (_this$hls$interstitia2 = _this$hls$interstitia.playingItem) == null ? void 0 : _this$hls$interstitia2.event; if (playingInterstitial) { return true; } } return false; } filterReplacedPrimary(frag, details) { if (!frag) { return frag; } if (interstitialsEnabled(this.hls.config) && frag.type !== PlaylistLevelType.SUBTITLE) { const interstitials = this.hls.interstitialsManager; const bufferingItem = interstitials == null ? void 0 : interstitials.bufferingItem; if (bufferingItem) { const bufferingInterstitial = bufferingItem.event; if (bufferingInterstitial) { if (bufferingInterstitial.appendInPlace || Math.abs(frag.start - bufferingItem.start) > 1 || bufferingItem.start === 0) { return null; } } else { if (frag.end <= bufferingItem.start && (details == null ? void 0 : details.live) === false) { return null; } if (frag.start > bufferingItem.end && bufferingItem.nextEvent) { if (bufferingItem.nextEvent.appendInPlace || frag.start - bufferingItem.end > 1) { return null; } } } } const playerQueue = interstitials == null ? void 0 : interstitials.playerQueue; if (playerQueue) { for (let i = playerQueue.length; i--; ) { const interstitial = playerQueue[i].interstitial; if (interstitial.appendInPlace && frag.start >= interstitial.startTime && frag.end <= interstitial.resumeTime) { return null; } } } } return frag; } mapToInitFragWhenRequired(frag) { if (frag != null && frag.initSegment && !(frag != null && frag.initSegment.data) && !this.bitrateTest) { return frag.initSegment; } return frag; } getNextPart(partList, frag, targetBufferTime) { let nextPart = -1; let contiguous = false; let independentAttrOmitted = true; for (let i = 0, len = partList.length; i < len; i++) { const part = partList[i]; independentAttrOmitted = independentAttrOmitted && !part.independent; if (nextPart > -1 && targetBufferTime < part.start) { break; } const loaded = part.loaded; if (loaded) { nextPart = -1; } else if ((contiguous || part.independent || independentAttrOmitted) && part.fragment === frag) { nextPart = i; } contiguous = loaded; } return nextPart; } loadedEndOfParts(partList, targetBufferTime) { const lastPart = partList[partList.length - 1]; return lastPart && targetBufferTime > lastPart.start && lastPart.loaded; } getInitialLiveFragment(levelDetails) { const fragments = levelDetails.fragments; const fragPrevious = this.fragPrevious; let frag = null; if (fragPrevious) { if (levelDetails.hasProgramDateTime) { this.log(`Live playlist, switching playlist, load frag with same PDT: ${fragPrevious.programDateTime}`); frag = findFragmentByPDT(fragments, fragPrevious.endProgramDateTime, this.config.maxFragLookUpTolerance); } if (!frag) { const targetSN = fragPrevious.sn + 1; if (targetSN >= levelDetails.startSN && targetSN <= levelDetails.endSN) { const fragNext = fragments[targetSN - levelDetails.startSN]; if (fragPrevious.cc === fragNext.cc) { frag = fragNext; this.log(`Live playlist, switching playlist, load frag with next SN: ${frag.sn}`); } } if (!frag) { frag = findNearestWithCC(levelDetails, fragPrevious.cc, fragPrevious.end); if (frag) { this.log(`Live playlist, switching playlist, load frag with same CC: ${frag.sn}`); } } } } else { const liveStart = this.hls.liveSyncPosition; if (liveStart !== null) { frag = this.getFragmentAtPosition(liveStart, this.bitrateTest ? levelDetails.fragmentEnd : levelDetails.edge, levelDetails); } } return frag; } getFragmentAtPosition(bufferEnd, end, levelDetails) { const { config } = this; let { fragPrevious } = this; let { fragments, endSN } = levelDetails; const { fragmentHint } = levelDetails; const { maxFragLookUpTolerance } = config; const partList = levelDetails.partList; const loadingParts = !!(this.loadingParts && partList != null && partList.length && fragmentHint); if (loadingParts && fragmentHint && !this.bitrateTest && partList[partList.length - 1].fragment.sn === fragmentHint.sn) { fragments = fragments.concat(fragmentHint); endSN = fragmentHint.sn; } let frag; if (bufferEnd < end) { var _this$media3; const backwardSeek = bufferEnd < this.lastCurrentTime; const lookupTolerance = backwardSeek || bufferEnd > end - maxFragLookUpTolerance || (_this$media3 = this.media) != null && _this$media3.paused || !this.startFragRequested ? 0 : maxFragLookUpTolerance; frag = findFragmentByPTS(fragPrevious, fragments, bufferEnd, lookupTolerance); } else { frag = fragments[fragments.length - 1]; } if (frag) { const curSNIdx = frag.sn - levelDetails.startSN; const fragState = this.fragmentTracker.getState(frag); if (fragState === FragmentState.OK || fragState === FragmentState.PARTIAL && frag.gap) { fragPrevious = frag; } if (fragPrevious && frag.sn === fragPrevious.sn && (!loadingParts || partList[0].fragment.sn > frag.sn || !levelDetails.live && !loadingParts)) { const sameLevel = fragPrevious && frag.level === fragPrevious.level; if (sameLevel) { const nextFrag = fragments[curSNIdx + 1]; if (frag.sn < endSN && this.fragmentTracker.getState(nextFrag) !== FragmentState.OK) { frag = nextFrag; } else { frag = null; } } } } return frag; } alignPlaylists(details, previousDetails, switchDetails) { const length = details.fragments.length; if (!length) { this.warn(`No fragments in live playlist`); return 0; } const slidingStart = details.fragmentStart; const firstLevelLoad = !previousDetails; const aligned = details.alignedSliding && isFiniteNumber(slidingStart); if (firstLevelLoad || !aligned && !slidingStart) { alignStream(switchDetails, details); const alignedSlidingStart = details.fragmentStart; this.log(`Live playlist sliding: ${alignedSlidingStart.toFixed(2)} start-sn: ${previousDetails ? previousDetails.startSN : "na"}->${details.startSN} fragments: ${length}`); return alignedSlidingStart; } return slidingStart; } waitForCdnTuneIn(details) { const advancePartLimit = 3; return details.live && details.canBlockReload && details.partTarget && details.tuneInGoal > Math.max(details.partHoldBack, details.partTarget * advancePartLimit); } setStartPosition(details, sliding) { let startPosition = this.startPosition; if (startPosition < sliding) { startPosition = -1; } const timelineOffset = this.timelineOffset; if (startPosition === -1) { const offsetInMultivariantPlaylist = this.startTimeOffset !== null; const startTimeOffset = offsetInMultivariantPlaylist ? this.startTimeOffset : details.startTimeOffset; if (startTimeOffset !== null && isFiniteNumber(startTimeOffset)) { startPosition = sliding + startTimeOffset; if (startTimeOffset < 0) { startPosition += details.edge; } startPosition = Math.min(Math.max(sliding, startPosition), sliding + details.totalduration); this.log(`Setting startPosition to ${startPosition} for start time offset ${startTimeOffset} found in ${offsetInMultivariantPlaylist ? "multivariant" : "media"} playlist`); this.startPosition = startPosition; } else if (details.live) { startPosition = this.hls.liveSyncPosition || sliding; this.log(`Setting startPosition to -1 to start at live edge ${startPosition}`); this.startPosition = -1; } else { this.log(`setting startPosition to 0 by default`); this.startPosition = startPosition = 0; } this.lastCurrentTime = startPosition + timelineOffset; } this.nextLoadPosition = startPosition + timelineOffset; } getLoadPosition() { var _this$hls; const { media } = this; let pos = 0; if ((_this$hls = this.hls) != null && _this$hls.hasEnoughToStart && media) { pos = media.currentTime; } else if (this.nextLoadPosition >= 0) { pos = this.nextLoadPosition; } return pos; } handleFragLoadAborted(frag, part) { if (this.transmuxer && frag.type === this.playlistType && isMediaFragment(frag) && frag.stats.aborted) { this.log(`Fragment ${frag.sn}${part ? " part " + part.index : ""} of ${this.playlistLabel()} ${frag.level} was aborted`); this.resetFragmentLoading(frag); } } resetFragmentLoading(frag) { if (!this.fragCurrent || !this.fragContextChanged(frag) && this.state !== State.FRAG_LOADING_WAITING_RETRY) { this.state = State.IDLE; } } onFragmentOrKeyLoadError(filterType, data) { if (data.chunkMeta && !data.frag) { const context = this.getCurrentContext(data.chunkMeta); if (context) { data.frag = context.frag; } } const frag = data.frag; if (!frag || frag.type !== filterType || !this.levels) { return; } if (this.fragContextChanged(frag)) { var _this$fragCurrent; this.warn(`Frag load error must match current frag to retry ${frag.url} > ${(_this$fragCurrent = this.fragCurrent) == null ? void 0 : _this$fragCurrent.url}`); return; } const gapTagEncountered = data.details === ErrorDetails.FRAG_GAP; if (gapTagEncountered) { this.fragmentTracker.fragBuffered(frag, true); } const errorAction = data.errorAction; const { action, flags, retryCount = 0, retryConfig } = errorAction || {}; const couldRetry = !!errorAction && !!retryConfig; const retry = couldRetry && action === NetworkErrorAction.RetryRequest; const noAlternate = couldRetry && !errorAction.resolved && flags === ErrorActionFlags.MoveAllAlternatesMatchingHost; if (!retry && noAlternate && isMediaFragment(frag) && !frag.endList) { this.resetFragmentErrors(filterType); this.treatAsGap(frag); errorAction.resolved = true; } else if ((retry || noAlternate) && retryCount < retryConfig.maxNumRetry) { this.resetStartWhenNotLoaded(this.levelLastLoaded); const delay = getRetryDelay(retryConfig, retryCount); this.warn(`Fragment ${frag.sn} of ${filterType} ${frag.level} errored with ${data.details}, retrying loading ${retryCount + 1}/${retryConfig.maxNumRetry} in ${delay}ms`); errorAction.resolved = true; this.retryDate = self.performance.now() + delay; this.state = State.FRAG_LOADING_WAITING_RETRY; } else if (retryConfig && errorAction) { this.resetFragmentErrors(filterType); if (retryCount < retryConfig.maxNumRetry) { if (!gapTagEncountered && action !== NetworkErrorAction.RemoveAlternatePermanently) { errorAction.resolved = true; } } else { this.warn(`${data.details} reached or exceeded max retry (${retryCount})`); return; } } else if (action === NetworkErrorAction.SendAlternateToPenaltyBox) { this.state = State.WAITING_LEVEL; } else { this.state = State.ERROR; } this.tickImmediate(); } reduceLengthAndFlushBuffer(data) { if (this.state === State.PARSING || this.state === State.PARSED) { const frag = data.frag; const playlistType = data.parent; const bufferedInfo = this.getFwdBufferInfo(this.mediaBuffer, playlistType); const buffered = bufferedInfo && bufferedInfo.len > 0.5; if (buffered) { this.reduceMaxBufferLength(bufferedInfo.len, (frag == null ? void 0 : frag.duration) || 10); } const flushBuffer = !buffered; if (flushBuffer) { this.warn(`Buffer full error while media.currentTime is not buffered, flush ${playlistType} buffer`); } if (frag) { this.fragmentTracker.removeFragment(frag); this.nextLoadPosition = frag.start; } this.resetLoadingState(); return flushBuffer; } return false; } resetFragmentErrors(filterType) { if (filterType === PlaylistLevelType.AUDIO) { this.fragCurrent = null; } if (!this.hls.hasEnoughToStart) { this.startFragRequested = false; } if (this.state !== State.STOPPED) { this.state = State.IDLE; } } afterBufferFlushed(media, bufferType, playlistType) { if (!media) { return; } const bufferedTimeRanges = BufferHelper.getBuffered(media); this.fragmentTracker.detectEvictedFragments(bufferType, bufferedTimeRanges, playlistType); if (this.state === State.ENDED) { this.resetLoadingState(); } } resetLoadingState() { this.log("Reset loading state"); this.fragCurrent = null; this.fragPrevious = null; if (this.state !== State.STOPPED) { this.state = State.IDLE; } } resetStartWhenNotLoaded(level) { if (!this.hls.hasEnoughToStart) { this.startFragRequested = false; const details = level ? level.details : null; if (details != null && details.live) { this.log(`resetting startPosition for live start`); this.startPosition = -1; this.setStartPosition(details, details.fragmentStart); this.resetLoadingState(); } else { this.nextLoadPosition = this.startPosition; } } } resetWhenMissingContext(chunkMeta) { this.warn(`The loading context changed while buffering fragment ${chunkMeta.sn} of ${this.playlistLabel()} ${chunkMeta.level}. This chunk will not be buffered.`); this.removeUnbufferedFrags(); this.resetStartWhenNotLoaded(this.levelLastLoaded); this.resetLoadingState(); } removeUnbufferedFrags(start = 0) { this.fragmentTracker.removeFragmentsInRange(start, Infinity, this.playlistType, false, true); } updateLevelTiming(frag, part, level, partial) { const details = level.details; if (!details) { this.warn("level.details undefined"); return; } const parsed = Object.keys(frag.elementaryStreams).reduce((result, type) => { const info = frag.elementaryStreams[type]; if (info) { const parsedDuration = info.endPTS - info.startPTS; if (parsedDuration <= 0) { this.warn(`Could not parse fragment ${frag.sn} ${type} duration reliably (${parsedDuration})`); return result || false; } const drift = partial ? 0 : updateFragPTSDTS(details, frag, info.startPTS, info.endPTS, info.startDTS, info.endDTS); this.hls.trigger(Events.LEVEL_PTS_UPDATED, { details, level, drift, type, frag, start: info.startPTS, end: info.endPTS }); return true; } return result; }, false); if (!parsed) { var _this$transmuxer; if (level.fragmentError === 0) { this.treatAsGap(frag, level); } if (((_this$transmuxer = this.transmuxer) == null ? void 0 : _this$transmuxer.error) === null) { const error = new Error(`Found no media in fragment ${frag.sn} of ${this.playlistLabel()} ${frag.level} resetting transmuxer to fallback to playlist timing`); this.warn(error.message); this.hls.trigger(Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: false, error, frag, reason: `Found no media in msn ${frag.sn} of ${this.playlistLabel()} "${level.url}"` }); if (!this.hls) { return; } this.resetTransmuxer(); } } this.state = State.PARSED; this.log(`Parsed ${frag.type} sn: ${frag.sn}${part ? " part: " + part.index : ""} of ${this.fragInfo(frag, false, part)})`); this.hls.trigger(Events.FRAG_PARSED, { frag, part }); } playlistLabel() { return this.playlistType === PlaylistLevelType.MAIN ? "level" : "track"; } fragInfo(frag, pts = true, part) { var _ref, _ref2; return `${this.playlistLabel()} ${frag.level} (${part ? "part" : "frag"}:[${((_ref = pts && !part ? frag.startPTS : (part || frag).start) != null ? _ref : NaN).toFixed(3)}-${((_ref2 = pts && !part ? frag.endPTS : (part || frag).end) != null ? _ref2 : NaN).toFixed(3)}]${part && frag.type === "main" ? "INDEPENDENT=" + (part.independent ? "YES" : "NO") : ""}`; } treatAsGap(frag, level) { if (level) { level.fragmentError++; } frag.gap = true; this.fragmentTracker.removeFragment(frag); this.fragmentTracker.fragBuffered(frag, true); } resetTransmuxer() { var _this$transmuxer2; (_this$transmuxer2 = this.transmuxer) == null ? void 0 : _this$transmuxer2.reset(); } recoverWorkerError(data) { if (data.event === "demuxerWorker") { this.fragmentTracker.removeAllFragments(); if (this.transmuxer) { this.transmuxer.destroy(); this.transmuxer = null; } this.resetStartWhenNotLoaded(this.levelLastLoaded); this.resetLoadingState(); } } set state(nextState) { const previousState = this._state; if (previousState !== nextState) { this._state = nextState; this.log(`${previousState}->${nextState}`); } } get state() { return this._state; } }; function interstitialsEnabled(config) { return !!config.interstitialsController && config.enableInterstitialPlayback !== false; } var ChunkCache = class { constructor() { this.chunks = []; this.dataLength = 0; } push(chunk) { this.chunks.push(chunk); this.dataLength += chunk.length; } flush() { const { chunks, dataLength } = this; let result; if (!chunks.length) { return new Uint8Array(0); } else if (chunks.length === 1) { result = chunks[0]; } else { result = concatUint8Arrays(chunks, dataLength); } this.reset(); return result; } reset() { this.chunks.length = 0; this.dataLength = 0; } }; function concatUint8Arrays(chunks, dataLength) { const result = new Uint8Array(dataLength); let offset = 0; for (let i = 0; i < chunks.length; i++) { const chunk = chunks[i]; result.set(chunk, offset); offset += chunk.length; } return result; } var eventemitter3 = { exports: {} }; var hasRequiredEventemitter3; function requireEventemitter3() { if (hasRequiredEventemitter3) return eventemitter3.exports; hasRequiredEventemitter3 = 1; (function(module) { var has = Object.prototype.hasOwnProperty, prefix = "~"; function Events2() { } if (Object.create) { Events2.prototype = /* @__PURE__ */ Object.create(null); if (!new Events2().__proto__) prefix = false; } function EE(fn, context, once) { this.fn = fn; this.context = context; this.once = once || false; } function addListener(emitter, event, fn, context, once) { if (typeof fn !== "function") { throw new TypeError("The listener must be a function"); } var listener = new EE(fn, context || emitter, once), evt = prefix ? prefix + event : event; if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); else emitter._events[evt] = [emitter._events[evt], listener]; return emitter; } function clearEvent(emitter, evt) { if (--emitter._eventsCount === 0) emitter._events = new Events2(); else delete emitter._events[evt]; } function EventEmitter2() { this._events = new Events2(); this._eventsCount = 0; } EventEmitter2.prototype.eventNames = function eventNames() { var names = [], events, name; if (this._eventsCount === 0) return names; for (name in events = this._events) { if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); } if (Object.getOwnPropertySymbols) { return names.concat(Object.getOwnPropertySymbols(events)); } return names; }; EventEmitter2.prototype.listeners = function listeners(event) { var evt = prefix ? prefix + event : event, handlers = this._events[evt]; if (!handlers) return []; if (handlers.fn) return [handlers.fn]; for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { ee[i] = handlers[i].fn; } return ee; }; EventEmitter2.prototype.listenerCount = function listenerCount(event) { var evt = prefix ? prefix + event : event, listeners = this._events[evt]; if (!listeners) return 0; if (listeners.fn) return 1; return listeners.length; }; EventEmitter2.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return false; var listeners = this._events[evt], len = arguments.length, args, i; if (listeners.fn) { if (listeners.once) this.removeListener(event, listeners.fn, void 0, true); switch (len) { case 1: return listeners.fn.call(listeners.context), true; case 2: return listeners.fn.call(listeners.context, a1), true; case 3: return listeners.fn.call(listeners.context, a1, a2), true; case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; } for (i = 1, args = new Array(len - 1); i < len; i++) { args[i - 1] = arguments[i]; } listeners.fn.apply(listeners.context, args); } else { var length = listeners.length, j; for (i = 0; i < length; i++) { if (listeners[i].once) this.removeListener(event, listeners[i].fn, void 0, true); switch (len) { case 1: listeners[i].fn.call(listeners[i].context); break; case 2: listeners[i].fn.call(listeners[i].context, a1); break; case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; default: if (!args) for (j = 1, args = new Array(len - 1); j < len; j++) { args[j - 1] = arguments[j]; } listeners[i].fn.apply(listeners[i].context, args); } } } return true; }; EventEmitter2.prototype.on = function on(event, fn, context) { return addListener(this, event, fn, context, false); }; EventEmitter2.prototype.once = function once(event, fn, context) { return addListener(this, event, fn, context, true); }; EventEmitter2.prototype.removeListener = function removeListener(event, fn, context, once) { var evt = prefix ? prefix + event : event; if (!this._events[evt]) return this; if (!fn) { clearEvent(this, evt); return this; } var listeners = this._events[evt]; if (listeners.fn) { if (listeners.fn === fn && (!once || listeners.once) && (!context || listeners.context === context)) { clearEvent(this, evt); } } else { for (var i = 0, events = [], length = listeners.length; i < length; i++) { if (listeners[i].fn !== fn || once && !listeners[i].once || context && listeners[i].context !== context) { events.push(listeners[i]); } } if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; else clearEvent(this, evt); } return this; }; EventEmitter2.prototype.removeAllListeners = function removeAllListeners(event) { var evt; if (event) { evt = prefix ? prefix + event : event; if (this._events[evt]) clearEvent(this, evt); } else { this._events = new Events2(); this._eventsCount = 0; } return this; }; EventEmitter2.prototype.off = EventEmitter2.prototype.removeListener; EventEmitter2.prototype.addListener = EventEmitter2.prototype.on; EventEmitter2.prefixed = prefix; EventEmitter2.EventEmitter = EventEmitter2; { module.exports = EventEmitter2; } })(eventemitter3); return eventemitter3.exports; } var eventemitter3Exports = requireEventemitter3(); var EventEmitter = getDefaultExportFromCjs(eventemitter3Exports); var version = "1.6.7"; var workerStore = {}; function hasUMDWorker() { return typeof __HLS_WORKER_BUNDLE__ === "function"; } function injectWorker() { const workerContext = workerStore[version]; if (workerContext) { workerContext.clientCount++; return workerContext; } const blob = new self.Blob([`var exports={};var module={exports:exports};function define(f){f()};define.amd=true;(${__HLS_WORKER_BUNDLE__.toString()})(true);`], { type: "text/javascript" }); const objectURL = self.URL.createObjectURL(blob); const worker = new self.Worker(objectURL); const result = { worker, objectURL, clientCount: 1 }; workerStore[version] = result; return result; } function loadWorker(path) { const workerContext = workerStore[path]; if (workerContext) { workerContext.clientCount++; return workerContext; } const scriptURL = new self.URL(path, self.location.href).href; const worker = new self.Worker(scriptURL); const result = { worker, scriptURL, clientCount: 1 }; workerStore[path] = result; return result; } function removeWorkerFromStore(path) { const workerContext = workerStore[path || version]; if (workerContext) { const clientCount = workerContext.clientCount--; if (clientCount === 1) { const { worker, objectURL } = workerContext; delete workerStore[path || version]; if (objectURL) { self.URL.revokeObjectURL(objectURL); } worker.terminate(); } } } function isId3Footer(data, offset) { if (offset + 10 <= data.length) { if (data[offset] === 51 && data[offset + 1] === 68 && data[offset + 2] === 73) { if (data[offset + 3] < 255 && data[offset + 4] < 255) { if (data[offset + 6] < 128 && data[offset + 7] < 128 && data[offset + 8] < 128 && data[offset + 9] < 128) { return true; } } } } return false; } function isId3Header(data, offset) { if (offset + 10 <= data.length) { if (data[offset] === 73 && data[offset + 1] === 68 && data[offset + 2] === 51) { if (data[offset + 3] < 255 && data[offset + 4] < 255) { if (data[offset + 6] < 128 && data[offset + 7] < 128 && data[offset + 8] < 128 && data[offset + 9] < 128) { return true; } } } } return false; } function readId3Size(data, offset) { let size = 0; size = (data[offset] & 127) << 21; size |= (data[offset + 1] & 127) << 14; size |= (data[offset + 2] & 127) << 7; size |= data[offset + 3] & 127; return size; } function getId3Data(data, offset) { const front = offset; let length = 0; while (isId3Header(data, offset)) { length += 10; const size = readId3Size(data, offset + 6); length += size; if (isId3Footer(data, offset + 10)) { length += 10; } offset += length; } if (length > 0) { return data.subarray(front, front + length); } return void 0; } function getAudioConfig(observer, data, offset, manifestCodec) { const adtsSamplingRates = [96e3, 88200, 64e3, 48e3, 44100, 32e3, 24e3, 22050, 16e3, 12e3, 11025, 8e3, 7350]; const byte2 = data[offset + 2]; const adtsSamplingIndex = byte2 >> 2 & 15; if (adtsSamplingIndex > 12) { const error = new Error(`invalid ADTS sampling index:${adtsSamplingIndex}`); observer.emit(Events.ERROR, Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: true, error, reason: error.message }); return; } const adtsObjectType = (byte2 >> 6 & 3) + 1; const channelCount = data[offset + 3] >> 6 & 3 | (byte2 & 1) << 2; const codec = "mp4a.40." + adtsObjectType; const samplerate = adtsSamplingRates[adtsSamplingIndex]; let aacSampleIndex = adtsSamplingIndex; if (adtsObjectType === 5 || adtsObjectType === 29) { aacSampleIndex -= 3; } const config = [adtsObjectType << 3 | (aacSampleIndex & 14) >> 1, (aacSampleIndex & 1) << 7 | channelCount << 3]; logger.log(`manifest codec:${manifestCodec}, parsed codec:${codec}, channels:${channelCount}, rate:${samplerate} (ADTS object type:${adtsObjectType} sampling index:${adtsSamplingIndex})`); return { config, samplerate, channelCount, codec, parsedCodec: codec, manifestCodec }; } function isHeaderPattern$1(data, offset) { return data[offset] === 255 && (data[offset + 1] & 246) === 240; } function getHeaderLength(data, offset) { return data[offset + 1] & 1 ? 7 : 9; } function getFullFrameLength(data, offset) { return (data[offset + 3] & 3) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 224) >>> 5; } function canGetFrameLength(data, offset) { return offset + 5 < data.length; } function isHeader$1(data, offset) { return offset + 1 < data.length && isHeaderPattern$1(data, offset); } function canParse$1(data, offset) { return canGetFrameLength(data, offset) && isHeaderPattern$1(data, offset) && getFullFrameLength(data, offset) <= data.length - offset; } function probe$1(data, offset) { if (isHeader$1(data, offset)) { const headerLength = getHeaderLength(data, offset); if (offset + headerLength >= data.length) { return false; } const frameLength = getFullFrameLength(data, offset); if (frameLength <= headerLength) { return false; } const newOffset = offset + frameLength; return newOffset === data.length || isHeader$1(data, newOffset); } return false; } function initTrackConfig(track, observer, data, offset, audioCodec) { if (!track.samplerate) { const config = getAudioConfig(observer, data, offset, audioCodec); if (!config) { return; } _extends(track, config); } } function getFrameDuration(samplerate) { return 1024 * 9e4 / samplerate; } function parseFrameHeader(data, offset) { const headerLength = getHeaderLength(data, offset); if (offset + headerLength <= data.length) { const frameLength = getFullFrameLength(data, offset) - headerLength; if (frameLength > 0) { return { headerLength, frameLength }; } } } function appendFrame$2(track, data, offset, pts, frameIndex) { const frameDuration = getFrameDuration(track.samplerate); const stamp = pts + frameIndex * frameDuration; const header = parseFrameHeader(data, offset); let unit; if (header) { const { frameLength, headerLength } = header; const _length = headerLength + frameLength; const missing = Math.max(0, offset + _length - data.length); if (missing) { unit = new Uint8Array(_length - headerLength); unit.set(data.subarray(offset + headerLength, data.length), 0); } else { unit = data.subarray(offset + headerLength, offset + _length); } const _sample = { unit, pts: stamp }; if (!missing) { track.samples.push(_sample); } return { sample: _sample, length: _length, missing }; } const length = data.length - offset; unit = new Uint8Array(length); unit.set(data.subarray(offset, data.length), 0); const sample = { unit, pts: stamp }; return { sample, length, missing: -1 }; } function canParseId3(data, offset) { return isId3Header(data, offset) && readId3Size(data, offset + 6) + 10 <= data.length - offset; } function toArrayBuffer(view2) { if (view2 instanceof ArrayBuffer) { return view2; } else { if (view2.byteOffset == 0 && view2.byteLength == view2.buffer.byteLength) { return view2.buffer; } return new Uint8Array(view2).buffer; } } function toUint8(data, offset = 0, length = Infinity) { return view(data, offset, length, Uint8Array); } function view(data, offset, length, Type) { const buffer = unsafeGetArrayBuffer(data); let bytesPerElement = 1; if ("BYTES_PER_ELEMENT" in Type) { bytesPerElement = Type.BYTES_PER_ELEMENT; } const dataOffset = isArrayBufferView(data) ? data.byteOffset : 0; const dataEnd = (dataOffset + data.byteLength) / bytesPerElement; const rawStart = (dataOffset + offset) / bytesPerElement; const start = Math.floor(Math.max(0, Math.min(rawStart, dataEnd))); const end = Math.floor(Math.min(start + Math.max(length, 0), dataEnd)); return new Type(buffer, start, end - start); } function unsafeGetArrayBuffer(view2) { if (view2 instanceof ArrayBuffer) { return view2; } else { return view2.buffer; } } function isArrayBufferView(obj) { return obj && obj.buffer instanceof ArrayBuffer && obj.byteLength !== void 0 && obj.byteOffset !== void 0; } function decodeId3ImageFrame(frame) { const metadataFrame = { key: frame.type, description: "", data: "", mimeType: null, pictureType: null }; const utf8Encoding = 3; if (frame.size < 2) { return void 0; } if (frame.data[0] !== utf8Encoding) { console.log("Ignore frame with unrecognized character encoding"); return void 0; } const mimeTypeEndIndex = frame.data.subarray(1).indexOf(0); if (mimeTypeEndIndex === -1) { return void 0; } const mimeType = utf8ArrayToStr(toUint8(frame.data, 1, mimeTypeEndIndex)); const pictureType = frame.data[2 + mimeTypeEndIndex]; const descriptionEndIndex = frame.data.subarray(3 + mimeTypeEndIndex).indexOf(0); if (descriptionEndIndex === -1) { return void 0; } const description = utf8ArrayToStr(toUint8(frame.data, 3 + mimeTypeEndIndex, descriptionEndIndex)); let data; if (mimeType === "-->") { data = utf8ArrayToStr(toUint8(frame.data, 4 + mimeTypeEndIndex + descriptionEndIndex)); } else { data = toArrayBuffer(frame.data.subarray(4 + mimeTypeEndIndex + descriptionEndIndex)); } metadataFrame.mimeType = mimeType; metadataFrame.pictureType = pictureType; metadataFrame.description = description; metadataFrame.data = data; return metadataFrame; } function decodeId3PrivFrame(frame) { if (frame.size < 2) { return void 0; } const owner = utf8ArrayToStr(frame.data, true); const privateData = new Uint8Array(frame.data.subarray(owner.length + 1)); return { key: frame.type, info: owner, data: privateData.buffer }; } function decodeId3TextFrame(frame) { if (frame.size < 2) { return void 0; } if (frame.type === "TXXX") { let index = 1; const description = utf8ArrayToStr(frame.data.subarray(index), true); index += description.length + 1; const value = utf8ArrayToStr(frame.data.subarray(index)); return { key: frame.type, info: description, data: value }; } const text = utf8ArrayToStr(frame.data.subarray(1)); return { key: frame.type, info: "", data: text }; } function decodeId3UrlFrame(frame) { if (frame.type === "WXXX") { if (frame.size < 2) { return void 0; } let index = 1; const description = utf8ArrayToStr(frame.data.subarray(index), true); index += description.length + 1; const value = utf8ArrayToStr(frame.data.subarray(index)); return { key: frame.type, info: description, data: value }; } const url = utf8ArrayToStr(frame.data); return { key: frame.type, info: "", data: url }; } function decodeId3Frame(frame) { if (frame.type === "PRIV") { return decodeId3PrivFrame(frame); } else if (frame.type[0] === "W") { return decodeId3UrlFrame(frame); } else if (frame.type === "APIC") { return decodeId3ImageFrame(frame); } return decodeId3TextFrame(frame); } function getId3FrameData(data) { const type = String.fromCharCode(data[0], data[1], data[2], data[3]); const size = readId3Size(data, 4); const offset = 10; return { type, size, data: data.subarray(offset, offset + size) }; } var HEADER_FOOTER_SIZE = 10; var FRAME_SIZE = 10; function getId3Frames(id3Data) { let offset = 0; const frames = []; while (isId3Header(id3Data, offset)) { const size = readId3Size(id3Data, offset + 6); if (id3Data[offset + 5] >> 6 & 1) { offset += HEADER_FOOTER_SIZE; } offset += HEADER_FOOTER_SIZE; const end = offset + size; while (offset + FRAME_SIZE < end) { const frameData = getId3FrameData(id3Data.subarray(offset)); const frame = decodeId3Frame(frameData); if (frame) { frames.push(frame); } offset += frameData.size + HEADER_FOOTER_SIZE; } if (isId3Footer(id3Data, offset)) { offset += HEADER_FOOTER_SIZE; } } return frames; } function isId3TimestampFrame(frame) { return frame && frame.key === "PRIV" && frame.info === "com.apple.streaming.transportStreamTimestamp"; } function readId3Timestamp(timeStampFrame) { if (timeStampFrame.data.byteLength === 8) { const data = new Uint8Array(timeStampFrame.data); const pts33Bit = data[3] & 1; let timestamp = (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7]; timestamp /= 45; if (pts33Bit) { timestamp += 4772185884e-2; } return Math.round(timestamp); } return void 0; } function getId3Timestamp(data) { const frames = getId3Frames(data); for (let i = 0; i < frames.length; i++) { const frame = frames[i]; if (isId3TimestampFrame(frame)) { return readId3Timestamp(frame); } } return void 0; } var MetadataSchema = function(MetadataSchema2) { MetadataSchema2["audioId3"] = "org.id3"; MetadataSchema2["dateRange"] = "com.apple.quicktime.HLS"; MetadataSchema2["emsg"] = "https://aomedia.org/emsg/ID3"; MetadataSchema2["misbklv"] = "urn:misb:KLV:bin:1910.1"; return MetadataSchema2; }({}); function dummyTrack(type = "", inputTimeScale = 9e4) { return { type, id: -1, pid: -1, inputTimeScale, sequenceNumber: -1, samples: [], dropped: 0 }; } var BaseAudioDemuxer = class { constructor() { this._audioTrack = void 0; this._id3Track = void 0; this.frameIndex = 0; this.cachedData = null; this.basePTS = null; this.initPTS = null; this.lastPTS = null; } resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { this._id3Track = { type: "id3", id: 3, pid: -1, inputTimeScale: 9e4, sequenceNumber: 0, samples: [], dropped: 0 }; } resetTimeStamp(deaultTimestamp) { this.initPTS = deaultTimestamp; this.resetContiguity(); } resetContiguity() { this.basePTS = null; this.lastPTS = null; this.frameIndex = 0; } canParse(data, offset) { return false; } appendFrame(track, data, offset) { } demux(data, timeOffset) { if (this.cachedData) { data = appendUint8Array(this.cachedData, data); this.cachedData = null; } let id3Data = getId3Data(data, 0); let offset = id3Data ? id3Data.length : 0; let lastDataIndex; const track = this._audioTrack; const id3Track = this._id3Track; const timestamp = id3Data ? getId3Timestamp(id3Data) : void 0; const length = data.length; if (this.basePTS === null || this.frameIndex === 0 && isFiniteNumber(timestamp)) { this.basePTS = initPTSFn(timestamp, timeOffset, this.initPTS); this.lastPTS = this.basePTS; } if (this.lastPTS === null) { this.lastPTS = this.basePTS; } if (id3Data && id3Data.length > 0) { id3Track.samples.push({ pts: this.lastPTS, dts: this.lastPTS, data: id3Data, type: MetadataSchema.audioId3, duration: Number.POSITIVE_INFINITY }); } while (offset < length) { if (this.canParse(data, offset)) { const frame = this.appendFrame(track, data, offset); if (frame) { this.frameIndex++; this.lastPTS = frame.sample.pts; offset += frame.length; lastDataIndex = offset; } else { offset = length; } } else if (canParseId3(data, offset)) { id3Data = getId3Data(data, offset); id3Track.samples.push({ pts: this.lastPTS, dts: this.lastPTS, data: id3Data, type: MetadataSchema.audioId3, duration: Number.POSITIVE_INFINITY }); offset += id3Data.length; lastDataIndex = offset; } else { offset++; } if (offset === length && lastDataIndex !== length) { const partialData = data.slice(lastDataIndex); if (this.cachedData) { this.cachedData = appendUint8Array(this.cachedData, partialData); } else { this.cachedData = partialData; } } } return { audioTrack: track, videoTrack: dummyTrack(), id3Track, textTrack: dummyTrack() }; } demuxSampleAes(data, keyData, timeOffset) { return Promise.reject(new Error(`[${this}] This demuxer does not support Sample-AES decryption`)); } flush(timeOffset) { const cachedData = this.cachedData; if (cachedData) { this.cachedData = null; this.demux(cachedData, 0); } return { audioTrack: this._audioTrack, videoTrack: dummyTrack(), id3Track: this._id3Track, textTrack: dummyTrack() }; } destroy() { this.cachedData = null; this._audioTrack = this._id3Track = void 0; } }; var initPTSFn = (timestamp, timeOffset, initPTS) => { if (isFiniteNumber(timestamp)) { return timestamp * 90; } const init90kHz = initPTS ? initPTS.baseTime * 9e4 / initPTS.timescale : 0; return timeOffset * 9e4 + init90kHz; }; var chromeVersion$1 = null; var 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]; var SamplingRateMap = [44100, 48e3, 32e3, 22050, 24e3, 16e3, 11025, 12e3, 8e3]; var SamplesCoefficients = [ [ 0, 72, 144, 12 ], [ 0, 0, 0, 0 ], [ 0, 72, 144, 12 ], [ 0, 144, 144, 12 ] ]; var BytesInSlot = [ 0, 1, 1, 4 ]; function appendFrame$1(track, data, offset, pts, frameIndex) { if (offset + 24 > data.length) { return; } const header = parseHeader(data, offset); if (header && offset + header.frameLength <= data.length) { const frameDuration = header.samplesPerFrame * 9e4 / header.sampleRate; const stamp = pts + frameIndex * frameDuration; const sample = { unit: data.subarray(offset, offset + header.frameLength), pts: stamp, dts: stamp }; track.config = []; track.channelCount = header.channelCount; track.samplerate = header.sampleRate; track.samples.push(sample); return { sample, length: header.frameLength, missing: 0 }; } } function parseHeader(data, offset) { const mpegVersion = data[offset + 1] >> 3 & 3; const mpegLayer = data[offset + 1] >> 1 & 3; const bitRateIndex = data[offset + 2] >> 4 & 15; const sampleRateIndex = data[offset + 2] >> 2 & 3; if (mpegVersion !== 1 && bitRateIndex !== 0 && bitRateIndex !== 15 && sampleRateIndex !== 3) { const paddingBit = data[offset + 2] >> 1 & 1; const channelMode = data[offset + 3] >> 6; const columnInBitrates = mpegVersion === 3 ? 3 - mpegLayer : mpegLayer === 3 ? 3 : 4; const bitRate = BitratesMap[columnInBitrates * 14 + bitRateIndex - 1] * 1e3; const columnInSampleRates = mpegVersion === 3 ? 0 : mpegVersion === 2 ? 1 : 2; const sampleRate = SamplingRateMap[columnInSampleRates * 3 + sampleRateIndex]; const channelCount = channelMode === 3 ? 1 : 2; const sampleCoefficient = SamplesCoefficients[mpegVersion][mpegLayer]; const bytesInSlot = BytesInSlot[mpegLayer]; const samplesPerFrame = sampleCoefficient * 8 * bytesInSlot; const frameLength = Math.floor(sampleCoefficient * bitRate / sampleRate + paddingBit) * bytesInSlot; if (chromeVersion$1 === null) { const userAgent = navigator.userAgent || ""; const result = userAgent.match(/Chrome\/(\d+)/i); chromeVersion$1 = result ? parseInt(result[1]) : 0; } const needChromeFix = !!chromeVersion$1 && chromeVersion$1 <= 87; if (needChromeFix && mpegLayer === 2 && bitRate >= 224e3 && channelMode === 0) { data[offset + 3] = data[offset + 3] | 128; } return { sampleRate, channelCount, frameLength, samplesPerFrame }; } } function isHeaderPattern(data, offset) { return data[offset] === 255 && (data[offset + 1] & 224) === 224 && (data[offset + 1] & 6) !== 0; } function isHeader(data, offset) { return offset + 1 < data.length && isHeaderPattern(data, offset); } function canParse(data, offset) { const headerSize = 4; return isHeaderPattern(data, offset) && headerSize <= data.length - offset; } function probe(data, offset) { if (offset + 1 < data.length && isHeaderPattern(data, offset)) { const headerLength = 4; const header = parseHeader(data, offset); let frameLength = headerLength; if (header != null && header.frameLength) { frameLength = header.frameLength; } const newOffset = offset + frameLength; return newOffset === data.length || isHeader(data, newOffset); } return false; } var AACDemuxer = class extends BaseAudioDemuxer { constructor(observer, config) { super(); this.observer = void 0; this.config = void 0; this.observer = observer; this.config = config; } resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { super.resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration); this._audioTrack = { container: "audio/adts", type: "audio", id: 2, pid: -1, sequenceNumber: 0, segmentCodec: "aac", samples: [], manifestCodec: audioCodec, duration: trackDuration, inputTimeScale: 9e4, dropped: 0 }; } static probe(data, logger2) { if (!data) { return false; } const id3Data = getId3Data(data, 0); let offset = (id3Data == null ? void 0 : id3Data.length) || 0; if (probe(data, offset)) { return false; } for (let length = data.length; offset < length; offset++) { if (probe$1(data, offset)) { logger2.log("ADTS sync word found !"); return true; } } return false; } canParse(data, offset) { return canParse$1(data, offset); } appendFrame(track, data, offset) { initTrackConfig(track, this.observer, data, offset, track.manifestCodec); const frame = appendFrame$2(track, data, offset, this.basePTS, this.frameIndex); if (frame && frame.missing === 0) { return frame; } } }; var getAudioBSID = (data, offset) => { let bsid = 0; let numBits = 5; offset += numBits; const temp = new Uint32Array(1); const mask = new Uint32Array(1); const byte = new Uint8Array(1); while (numBits > 0) { byte[0] = data[offset]; const bits = Math.min(numBits, 8); const shift = 8 - bits; mask[0] = 4278190080 >>> 24 + shift << shift; temp[0] = (byte[0] & mask[0]) >> shift; bsid = !bsid ? temp[0] : bsid << bits | temp[0]; offset += 1; numBits -= bits; } return bsid; }; var AC3Demuxer = class extends BaseAudioDemuxer { constructor(observer) { super(); this.observer = void 0; this.observer = observer; } resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { super.resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration); this._audioTrack = { container: "audio/ac-3", type: "audio", id: 2, pid: -1, sequenceNumber: 0, segmentCodec: "ac3", samples: [], manifestCodec: audioCodec, duration: trackDuration, inputTimeScale: 9e4, dropped: 0 }; } canParse(data, offset) { return offset + 64 < data.length; } appendFrame(track, data, offset) { const frameLength = appendFrame(track, data, offset, this.basePTS, this.frameIndex); if (frameLength !== -1) { const sample = track.samples[track.samples.length - 1]; return { sample, length: frameLength, missing: 0 }; } } static probe(data) { if (!data) { return false; } const id3Data = getId3Data(data, 0); if (!id3Data) { return false; } const offset = id3Data.length; if (data[offset] === 11 && data[offset + 1] === 119 && getId3Timestamp(id3Data) !== void 0 && getAudioBSID(data, offset) < 16) { return true; } return false; } }; function appendFrame(track, data, start, pts, frameIndex) { if (start + 8 > data.length) { return -1; } if (data[start] !== 11 || data[start + 1] !== 119) { return -1; } const samplingRateCode = data[start + 4] >> 6; if (samplingRateCode >= 3) { return -1; } const samplingRateMap = [48e3, 44100, 32e3]; const sampleRate = samplingRateMap[samplingRateCode]; const frameSizeCode = data[start + 4] & 63; 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 frameLength = frameSizeMap[frameSizeCode * 3 + samplingRateCode] * 2; if (start + frameLength > data.length) { return -1; } const channelMode = data[start + 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[start + 6] << 8 | data[start + 7]) >> 12 - skipCount & 1; const channelsMap = [2, 1, 2, 3, 3, 4, 4, 5]; const channelCount = channelsMap[channelMode] + lfeon; const bsid = data[start + 5] >> 3; const bsmod = data[start + 5] & 7; const config = new Uint8Array([samplingRateCode << 6 | bsid << 1 | bsmod >> 2, (bsmod & 3) << 6 | channelMode << 3 | lfeon << 2 | frameSizeCode >> 4, frameSizeCode << 4 & 224]); const frameDuration = 1536 / sampleRate * 9e4; const stamp = pts + frameIndex * frameDuration; const unit = data.subarray(start, start + frameLength); track.config = config; track.channelCount = channelCount; track.samplerate = sampleRate; track.samples.push({ unit, pts: stamp }); return frameLength; } var MP3Demuxer = class extends BaseAudioDemuxer { resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { super.resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration); this._audioTrack = { container: "audio/mpeg", type: "audio", id: 2, pid: -1, sequenceNumber: 0, segmentCodec: "mp3", samples: [], manifestCodec: audioCodec, duration: trackDuration, inputTimeScale: 9e4, dropped: 0 }; } static probe(data) { if (!data) { return false; } const id3Data = getId3Data(data, 0); let offset = (id3Data == null ? void 0 : id3Data.length) || 0; if (id3Data && data[offset] === 11 && data[offset + 1] === 119 && getId3Timestamp(id3Data) !== void 0 && getAudioBSID(data, offset) <= 16) { return false; } for (let length = data.length; offset < length; offset++) { if (probe(data, offset)) { logger.log("MPEG Audio sync word found !"); return true; } } return false; } canParse(data, offset) { return canParse(data, offset); } appendFrame(track, data, offset) { if (this.basePTS === null) { return; } return appendFrame$1(track, data, offset, this.basePTS, this.frameIndex); } }; var emsgSchemePattern = /\/emsg[-/]ID3/i; var MP4Demuxer = class { constructor(observer, config) { this.remainderData = null; this.timeOffset = 0; this.config = void 0; this.videoTrack = void 0; this.audioTrack = void 0; this.id3Track = void 0; this.txtTrack = void 0; this.config = config; } resetTimeStamp() { } resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { const videoTrack = this.videoTrack = dummyTrack("video", 1); const audioTrack = this.audioTrack = dummyTrack("audio", 1); const captionTrack = this.txtTrack = dummyTrack("text", 1); this.id3Track = dummyTrack("id3", 1); this.timeOffset = 0; if (!(initSegment != null && initSegment.byteLength)) { return; } const initData = parseInitSegment(initSegment); if (initData.video) { const { id, timescale, codec, supplemental } = initData.video; videoTrack.id = id; videoTrack.timescale = captionTrack.timescale = timescale; videoTrack.codec = codec; videoTrack.supplemental = supplemental; } if (initData.audio) { const { id, timescale, codec } = initData.audio; audioTrack.id = id; audioTrack.timescale = timescale; audioTrack.codec = codec; } captionTrack.id = RemuxerTrackIdConfig.text; videoTrack.sampleDuration = 0; videoTrack.duration = audioTrack.duration = trackDuration; } resetContiguity() { this.remainderData = null; } static probe(data) { return hasMoofData(data); } demux(data, timeOffset) { this.timeOffset = timeOffset; let videoSamples = data; const videoTrack = this.videoTrack; const textTrack = this.txtTrack; if (this.config.progressive) { if (this.remainderData) { videoSamples = appendUint8Array(this.remainderData, data); } const segmentedData = segmentValidRange(videoSamples); this.remainderData = segmentedData.remainder; videoTrack.samples = segmentedData.valid || new Uint8Array(); } else { videoTrack.samples = videoSamples; } const id3Track = this.extractID3Track(videoTrack, timeOffset); textTrack.samples = parseSamples(timeOffset, videoTrack); return { videoTrack, audioTrack: this.audioTrack, id3Track, textTrack: this.txtTrack }; } flush() { const timeOffset = this.timeOffset; const videoTrack = this.videoTrack; const textTrack = this.txtTrack; videoTrack.samples = this.remainderData || new Uint8Array(); this.remainderData = null; const id3Track = this.extractID3Track(videoTrack, this.timeOffset); textTrack.samples = parseSamples(timeOffset, videoTrack); return { videoTrack, audioTrack: dummyTrack(), id3Track, textTrack: dummyTrack() }; } extractID3Track(videoTrack, timeOffset) { const id3Track = this.id3Track; if (videoTrack.samples.length) { const emsgs = findBox(videoTrack.samples, ["emsg"]); if (emsgs) { emsgs.forEach((data) => { const emsgInfo = parseEmsg(data); if (emsgSchemePattern.test(emsgInfo.schemeIdUri)) { const pts = getEmsgStartTime(emsgInfo, timeOffset); let duration = emsgInfo.eventDuration === 4294967295 ? Number.POSITIVE_INFINITY : emsgInfo.eventDuration / emsgInfo.timeScale; if (duration <= 1e-3) { duration = Number.POSITIVE_INFINITY; } const payload = emsgInfo.payload; id3Track.samples.push({ data: payload, len: payload.byteLength, dts: pts, pts, type: MetadataSchema.emsg, duration }); } else if (this.config.enableEmsgKLVMetadata && emsgInfo.schemeIdUri.startsWith("urn:misb:KLV:bin:1910.1")) { const pts = getEmsgStartTime(emsgInfo, timeOffset); id3Track.samples.push({ data: emsgInfo.payload, len: emsgInfo.payload.byteLength, dts: pts, pts, type: MetadataSchema.misbklv, duration: Number.POSITIVE_INFINITY }); } }); } } return id3Track; } demuxSampleAes(data, keyData, timeOffset) { return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption")); } destroy() { this.config = null; this.remainderData = null; this.videoTrack = this.audioTrack = this.id3Track = this.txtTrack = void 0; } }; function getEmsgStartTime(emsgInfo, timeOffset) { return isFiniteNumber(emsgInfo.presentationTime) ? emsgInfo.presentationTime / emsgInfo.timeScale : timeOffset + emsgInfo.presentationTimeDelta / emsgInfo.timeScale; } var SampleAesDecrypter = class { constructor(observer, config, keyData) { this.keyData = void 0; this.decrypter = void 0; this.keyData = keyData; this.decrypter = new Decrypter(config, { removePKCS7Padding: false }); } decryptBuffer(encryptedData) { return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer, DecrypterAesMode.cbc); } decryptAacSample(samples, sampleIndex, callback) { const curUnit = samples[sampleIndex].unit; if (curUnit.length <= 16) { return; } const encryptedData = curUnit.subarray(16, curUnit.length - curUnit.length % 16); const encryptedBuffer = encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length); this.decryptBuffer(encryptedBuffer).then((decryptedBuffer) => { const decryptedData = new Uint8Array(decryptedBuffer); curUnit.set(decryptedData, 16); if (!this.decrypter.isSync()) { this.decryptAacSamples(samples, sampleIndex + 1, callback); } }); } decryptAacSamples(samples, sampleIndex, callback) { for (; ; sampleIndex++) { if (sampleIndex >= samples.length) { callback(); return; } if (samples[sampleIndex].unit.length < 32) { continue; } this.decryptAacSample(samples, sampleIndex, callback); if (!this.decrypter.isSync()) { return; } } } getAvcEncryptedData(decodedData) { const encryptedDataLen = Math.floor((decodedData.length - 48) / 160) * 16 + 16; const encryptedData = new Int8Array(encryptedDataLen); let outputPos = 0; for (let inputPos = 32; inputPos < decodedData.length - 16; inputPos += 160, outputPos += 16) { encryptedData.set(decodedData.subarray(inputPos, inputPos + 16), outputPos); } return encryptedData; } getAvcDecryptedUnit(decodedData, decryptedData) { const uint8DecryptedData = new Uint8Array(decryptedData); let inputPos = 0; for (let outputPos = 32; outputPos < decodedData.length - 16; outputPos += 160, inputPos += 16) { decodedData.set(uint8DecryptedData.subarray(inputPos, inputPos + 16), outputPos); } return decodedData; } decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit) { const decodedData = discardEPB(curUnit.data); const encryptedData = this.getAvcEncryptedData(decodedData); this.decryptBuffer(encryptedData.buffer).then((decryptedBuffer) => { curUnit.data = this.getAvcDecryptedUnit(decodedData, decryptedBuffer); if (!this.decrypter.isSync()) { this.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback); } }); } decryptAvcSamples(samples, sampleIndex, unitIndex, callback) { if (samples instanceof Uint8Array) { throw new Error("Cannot decrypt samples of type Uint8Array"); } for (; ; sampleIndex++, unitIndex = 0) { if (sampleIndex >= samples.length) { callback(); return; } const curUnits = samples[sampleIndex].units; for (; ; unitIndex++) { if (unitIndex >= curUnits.length) { break; } const curUnit = curUnits[unitIndex]; if (curUnit.data.length <= 48 || curUnit.type !== 1 && curUnit.type !== 5) { continue; } this.decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit); if (!this.decrypter.isSync()) { return; } } } } }; var BaseVideoParser = class { constructor() { this.VideoSample = null; } createVideoSample(key, pts, dts) { return { key, frame: false, pts, dts, units: [], length: 0 }; } getLastNalUnit(samples) { var _VideoSample; let VideoSample = this.VideoSample; let lastUnit; if (!VideoSample || VideoSample.units.length === 0) { VideoSample = samples[samples.length - 1]; } if ((_VideoSample = VideoSample) != null && _VideoSample.units) { const units = VideoSample.units; lastUnit = units[units.length - 1]; } return lastUnit; } pushAccessUnit(VideoSample, videoTrack) { if (VideoSample.units.length && VideoSample.frame) { if (VideoSample.pts === void 0) { const samples = videoTrack.samples; const nbSamples = samples.length; if (nbSamples) { const lastSample = samples[nbSamples - 1]; VideoSample.pts = lastSample.pts; VideoSample.dts = lastSample.dts; } else { videoTrack.dropped++; return; } } videoTrack.samples.push(VideoSample); } } parseNALu(track, array, endOfSegment) { const len = array.byteLength; let state = track.naluState || 0; const lastState = state; const units = []; let i = 0; let value; let overflow; let unitType; let lastUnitStart = -1; let lastUnitType = 0; if (state === -1) { lastUnitStart = 0; lastUnitType = this.getNALuType(array, 0); state = 0; i = 1; } while (i < len) { value = array[i++]; if (!state) { state = value ? 0 : 1; continue; } if (state === 1) { state = value ? 0 : 2; continue; } if (!value) { state = 3; } else if (value === 1) { overflow = i - state - 1; if (lastUnitStart >= 0) { const unit = { data: array.subarray(lastUnitStart, overflow), type: lastUnitType }; units.push(unit); } else { const lastUnit = this.getLastNalUnit(track.samples); if (lastUnit) { if (lastState && i <= 4 - lastState) { if (lastUnit.state) { lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState); } } if (overflow > 0) { lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow)); lastUnit.state = 0; } } } if (i < len) { unitType = this.getNALuType(array, i); lastUnitStart = i; lastUnitType = unitType; state = 0; } else { state = -1; } } else { state = 0; } } if (lastUnitStart >= 0 && state >= 0) { const unit = { data: array.subarray(lastUnitStart, len), type: lastUnitType, state }; units.push(unit); } if (units.length === 0) { const lastUnit = this.getLastNalUnit(track.samples); if (lastUnit) { lastUnit.data = appendUint8Array(lastUnit.data, array); } } track.naluState = state; return units; } }; var ExpGolomb = class { constructor(data) { this.data = void 0; this.bytesAvailable = void 0; this.word = void 0; this.bitsAvailable = void 0; this.data = data; this.bytesAvailable = data.byteLength; this.word = 0; this.bitsAvailable = 0; } loadWord() { const data = this.data; const bytesAvailable = this.bytesAvailable; const position = data.byteLength - bytesAvailable; const workingBytes = new Uint8Array(4); const 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); this.bitsAvailable = availableBytes * 8; this.bytesAvailable -= availableBytes; } skipBits(count) { let skipBytes; count = Math.min(count, this.bytesAvailable * 8 + this.bitsAvailable); 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); const valu = this.word >>> 32 - bits; if (size > 32) { logger.error("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(); } else { throw new Error("no bits available"); } bits = size - bits; if (bits > 0 && this.bitsAvailable) { return valu << bits | this.readBits(bits); } else { return valu; } } skipLZ() { let leadingZeroCount; for (leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) { if ((this.word & 2147483648 >>> leadingZeroCount) !== 0) { this.word <<= leadingZeroCount; this.bitsAvailable -= leadingZeroCount; return leadingZeroCount; } } this.loadWord(); return leadingZeroCount + this.skipLZ(); } skipUEG() { this.skipBits(1 + this.skipLZ()); } skipEG() { this.skipBits(1 + this.skipLZ()); } readUEG() { const clz = this.skipLZ(); return this.readBits(clz + 1) - 1; } readEG() { const valu = this.readUEG(); if (1 & valu) { return 1 + valu >>> 1; } else { return -1 * (valu >>> 1); } } readBoolean() { return this.readBits(1) === 1; } readUByte() { return this.readBits(8); } readUShort() { return this.readBits(16); } readUInt() { return this.readBits(32); } }; var AvcVideoParser = class extends BaseVideoParser { parsePES(track, textTrack, pes, endOfSegment) { const units = this.parseNALu(track, pes.data, endOfSegment); let VideoSample = this.VideoSample; let push2; let spsfound = false; pes.data = null; if (VideoSample && units.length && !track.audFound) { this.pushAccessUnit(VideoSample, track); VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts); } units.forEach((unit) => { var _VideoSample2, _VideoSample3; switch (unit.type) { case 1: { let iskey = false; push2 = true; const data = unit.data; if (spsfound && data.length > 4) { const sliceType = this.readSliceType(data); if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) { iskey = true; } } if (iskey) { var _VideoSample; if ((_VideoSample = VideoSample) != null && _VideoSample.frame && !VideoSample.key) { this.pushAccessUnit(VideoSample, track); VideoSample = this.VideoSample = null; } } if (!VideoSample) { VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts); } VideoSample.frame = true; VideoSample.key = iskey; break; } case 5: push2 = true; if ((_VideoSample2 = VideoSample) != null && _VideoSample2.frame && !VideoSample.key) { this.pushAccessUnit(VideoSample, track); VideoSample = this.VideoSample = null; } if (!VideoSample) { VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts); } VideoSample.key = true; VideoSample.frame = true; break; case 6: { push2 = true; parseSEIMessageFromNALu(unit.data, 1, pes.pts, textTrack.samples); break; } case 7: { var _track$pixelRatio, _track$pixelRatio2; push2 = true; spsfound = true; const sps = unit.data; const config = this.readSPS(sps); if (!track.sps || track.width !== config.width || track.height !== config.height || ((_track$pixelRatio = track.pixelRatio) == null ? void 0 : _track$pixelRatio[0]) !== config.pixelRatio[0] || ((_track$pixelRatio2 = track.pixelRatio) == null ? void 0 : _track$pixelRatio2[1]) !== config.pixelRatio[1]) { track.width = config.width; track.height = config.height; track.pixelRatio = config.pixelRatio; track.sps = [sps]; const codecarray = sps.subarray(1, 4); let codecstring = "avc1."; for (let i = 0; i < 3; i++) { let h = codecarray[i].toString(16); if (h.length < 2) { h = "0" + h; } codecstring += h; } track.codec = codecstring; } break; } case 8: push2 = true; track.pps = [unit.data]; break; case 9: push2 = true; track.audFound = true; if ((_VideoSample3 = VideoSample) != null && _VideoSample3.frame) { this.pushAccessUnit(VideoSample, track); VideoSample = null; } if (!VideoSample) { VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts); } break; case 12: push2 = true; break; default: push2 = false; break; } if (VideoSample && push2) { const units2 = VideoSample.units; units2.push(unit); } }); if (endOfSegment && VideoSample) { this.pushAccessUnit(VideoSample, track); this.VideoSample = null; } } getNALuType(data, offset) { return data[offset] & 31; } readSliceType(data) { const eg = new ExpGolomb(data); eg.readUByte(); eg.readUEG(); return eg.readUEG(); } skipScalingList(count, reader) { let lastScale = 8; let nextScale = 8; let deltaScale; for (let j = 0; j < count; j++) { if (nextScale !== 0) { deltaScale = reader.readEG(); nextScale = (lastScale + deltaScale + 256) % 256; } lastScale = nextScale === 0 ? lastScale : nextScale; } } readSPS(sps) { const eg = new ExpGolomb(sps); let frameCropLeftOffset = 0; let frameCropRightOffset = 0; let frameCropTopOffset = 0; let frameCropBottomOffset = 0; let numRefFramesInPicOrderCntCycle; let scalingListCount; let i; const readUByte = eg.readUByte.bind(eg); const readBits = eg.readBits.bind(eg); const readUEG = eg.readUEG.bind(eg); const readBoolean = eg.readBoolean.bind(eg); const skipBits = eg.skipBits.bind(eg); const skipEG = eg.skipEG.bind(eg); const skipUEG = eg.skipUEG.bind(eg); const skipScalingList = this.skipScalingList.bind(this); readUByte(); const profileIdc = readUByte(); readBits(5); skipBits(3); readUByte(); skipUEG(); 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); } skipUEG(); skipUEG(); skipBits(1); if (readBoolean()) { scalingListCount = chromaFormatIdc !== 3 ? 8 : 12; for (i = 0; i < scalingListCount; i++) { if (readBoolean()) { if (i < 6) { skipScalingList(16, eg); } else { skipScalingList(64, eg); } } } } } skipUEG(); const picOrderCntType = readUEG(); if (picOrderCntType === 0) { readUEG(); } else if (picOrderCntType === 1) { skipBits(1); skipEG(); skipEG(); numRefFramesInPicOrderCntCycle = readUEG(); for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) { skipEG(); } } skipUEG(); skipBits(1); const picWidthInMbsMinus1 = readUEG(); const picHeightInMapUnitsMinus1 = readUEG(); const frameMbsOnlyFlag = readBits(1); if (frameMbsOnlyFlag === 0) { skipBits(1); } skipBits(1); if (readBoolean()) { frameCropLeftOffset = readUEG(); frameCropRightOffset = readUEG(); frameCropTopOffset = readUEG(); frameCropBottomOffset = readUEG(); } let pixelRatio = [1, 1]; if (readBoolean()) { if (readBoolean()) { 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 }; } }; var HevcVideoParser = class extends BaseVideoParser { constructor(...args) { super(...args); this.initVPS = null; } parsePES(track, textTrack, pes, endOfSegment) { const units = this.parseNALu(track, pes.data, endOfSegment); let VideoSample = this.VideoSample; let push2; let spsfound = false; pes.data = null; if (VideoSample && units.length && !track.audFound) { this.pushAccessUnit(VideoSample, track); VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts); } units.forEach((unit) => { var _VideoSample2, _VideoSample3; switch (unit.type) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: if (!VideoSample) { VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts); } VideoSample.frame = true; push2 = true; break; case 16: case 17: case 18: case 21: push2 = true; if (spsfound) { var _VideoSample; if ((_VideoSample = VideoSample) != null && _VideoSample.frame && !VideoSample.key) { this.pushAccessUnit(VideoSample, track); VideoSample = this.VideoSample = null; } } if (!VideoSample) { VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts); } VideoSample.key = true; VideoSample.frame = true; break; case 19: case 20: push2 = true; if ((_VideoSample2 = VideoSample) != null && _VideoSample2.frame && !VideoSample.key) { this.pushAccessUnit(VideoSample, track); VideoSample = this.VideoSample = null; } if (!VideoSample) { VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts); } VideoSample.key = true; VideoSample.frame = true; break; case 39: push2 = true; parseSEIMessageFromNALu(unit.data, 2, pes.pts, textTrack.samples); break; case 32: push2 = true; if (!track.vps) { if (typeof track.params !== "object") { track.params = {}; } track.params = _extends(track.params, this.readVPS(unit.data)); this.initVPS = unit.data; } track.vps = [unit.data]; break; case 33: push2 = true; spsfound = true; if (track.vps !== void 0 && track.vps[0] !== this.initVPS && track.sps !== void 0 && !this.matchSPS(track.sps[0], unit.data)) { this.initVPS = track.vps[0]; track.sps = track.pps = void 0; } if (!track.sps) { const config = this.readSPS(unit.data); track.width = config.width; track.height = config.height; track.pixelRatio = config.pixelRatio; track.codec = config.codecString; track.sps = []; if (typeof track.params !== "object") { track.params = {}; } for (const prop in config.params) { track.params[prop] = config.params[prop]; } } this.pushParameterSet(track.sps, unit.data, track.vps); if (!VideoSample) { VideoSample = this.VideoSample = this.createVideoSample(true, pes.pts, pes.dts); } VideoSample.key = true; break; case 34: push2 = true; if (typeof track.params === "object") { if (!track.pps) { track.pps = []; const config = this.readPPS(unit.data); for (const prop in config) { track.params[prop] = config[prop]; } } this.pushParameterSet(track.pps, unit.data, track.vps); } break; case 35: push2 = true; track.audFound = true; if ((_VideoSample3 = VideoSample) != null && _VideoSample3.frame) { this.pushAccessUnit(VideoSample, track); VideoSample = null; } if (!VideoSample) { VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts); } break; default: push2 = false; break; } if (VideoSample && push2) { const units2 = VideoSample.units; units2.push(unit); } }); if (endOfSegment && VideoSample) { this.pushAccessUnit(VideoSample, track); this.VideoSample = null; } } pushParameterSet(parameterSets, data, vps) { if (vps && vps[0] === this.initVPS || !vps && !parameterSets.length) { parameterSets.push(data); } } getNALuType(data, offset) { return (data[offset] & 126) >>> 1; } ebsp2rbsp(arr) { const dst = new Uint8Array(arr.byteLength); let dstIdx = 0; for (let i = 0; i < arr.byteLength; i++) { if (i >= 2) { if (arr[i] === 3 && arr[i - 1] === 0 && arr[i - 2] === 0) { continue; } } dst[dstIdx] = arr[i]; dstIdx++; } return new Uint8Array(dst.buffer, 0, dstIdx); } pushAccessUnit(VideoSample, videoTrack) { super.pushAccessUnit(VideoSample, videoTrack); if (this.initVPS) { this.initVPS = null; } } readVPS(vps) { const eg = new ExpGolomb(vps); eg.readUByte(); eg.readUByte(); eg.readBits(4); eg.skipBits(2); eg.readBits(6); const max_sub_layers_minus1 = eg.readBits(3); const temporal_id_nesting_flag = eg.readBoolean(); return { numTemporalLayers: max_sub_layers_minus1 + 1, temporalIdNested: temporal_id_nesting_flag }; } readSPS(sps) { const eg = new ExpGolomb(this.ebsp2rbsp(sps)); eg.readUByte(); eg.readUByte(); eg.readBits(4); const max_sub_layers_minus1 = eg.readBits(3); eg.readBoolean(); const general_profile_space = eg.readBits(2); const general_tier_flag = eg.readBoolean(); const general_profile_idc = eg.readBits(5); const general_profile_compatibility_flags_1 = eg.readUByte(); const general_profile_compatibility_flags_2 = eg.readUByte(); const general_profile_compatibility_flags_3 = eg.readUByte(); const general_profile_compatibility_flags_4 = eg.readUByte(); const general_constraint_indicator_flags_1 = eg.readUByte(); const general_constraint_indicator_flags_2 = eg.readUByte(); const general_constraint_indicator_flags_3 = eg.readUByte(); const general_constraint_indicator_flags_4 = eg.readUByte(); const general_constraint_indicator_flags_5 = eg.readUByte(); const general_constraint_indicator_flags_6 = eg.readUByte(); const general_level_idc = eg.readUByte(); const sub_layer_profile_present_flags = []; const sub_layer_level_present_flags = []; for (let i = 0; i < max_sub_layers_minus1; i++) { sub_layer_profile_present_flags.push(eg.readBoolean()); sub_layer_level_present_flags.push(eg.readBoolean()); } if (max_sub_layers_minus1 > 0) { for (let i = max_sub_layers_minus1; i < 8; i++) { eg.readBits(2); } } for (let i = 0; i < max_sub_layers_minus1; i++) { if (sub_layer_profile_present_flags[i]) { eg.readUByte(); eg.readUByte(); eg.readUByte(); eg.readUByte(); eg.readUByte(); eg.readUByte(); eg.readUByte(); eg.readUByte(); eg.readUByte(); eg.readUByte(); eg.readUByte(); } if (sub_layer_level_present_flags[i]) { eg.readUByte(); } } eg.readUEG(); const chroma_format_idc = eg.readUEG(); if (chroma_format_idc == 3) { eg.skipBits(1); } const pic_width_in_luma_samples = eg.readUEG(); const pic_height_in_luma_samples = eg.readUEG(); const conformance_window_flag = eg.readBoolean(); let pic_left_offset = 0, pic_right_offset = 0, pic_top_offset = 0, pic_bottom_offset = 0; if (conformance_window_flag) { pic_left_offset += eg.readUEG(); pic_right_offset += eg.readUEG(); pic_top_offset += eg.readUEG(); pic_bottom_offset += eg.readUEG(); } const bit_depth_luma_minus8 = eg.readUEG(); const bit_depth_chroma_minus8 = eg.readUEG(); const log2_max_pic_order_cnt_lsb_minus4 = eg.readUEG(); const sub_layer_ordering_info_present_flag = eg.readBoolean(); for (let i = sub_layer_ordering_info_present_flag ? 0 : max_sub_layers_minus1; i <= max_sub_layers_minus1; i++) { eg.skipUEG(); eg.skipUEG(); eg.skipUEG(); } eg.skipUEG(); eg.skipUEG(); eg.skipUEG(); eg.skipUEG(); eg.skipUEG(); eg.skipUEG(); const scaling_list_enabled_flag = eg.readBoolean(); if (scaling_list_enabled_flag) { const sps_scaling_list_data_present_flag = eg.readBoolean(); if (sps_scaling_list_data_present_flag) { for (let sizeId = 0; sizeId < 4; sizeId++) { for (let matrixId = 0; matrixId < (sizeId === 3 ? 2 : 6); matrixId++) { const scaling_list_pred_mode_flag = eg.readBoolean(); if (!scaling_list_pred_mode_flag) { eg.readUEG(); } else { const coefNum = Math.min(64, 1 << 4 + (sizeId << 1)); if (sizeId > 1) { eg.readEG(); } for (let i = 0; i < coefNum; i++) { eg.readEG(); } } } } } } eg.readBoolean(); eg.readBoolean(); const pcm_enabled_flag = eg.readBoolean(); if (pcm_enabled_flag) { eg.readUByte(); eg.skipUEG(); eg.skipUEG(); eg.readBoolean(); } const num_short_term_ref_pic_sets = eg.readUEG(); let num_delta_pocs = 0; for (let i = 0; i < num_short_term_ref_pic_sets; i++) { let inter_ref_pic_set_prediction_flag = false; if (i !== 0) { inter_ref_pic_set_prediction_flag = eg.readBoolean(); } if (inter_ref_pic_set_prediction_flag) { if (i === num_short_term_ref_pic_sets) { eg.readUEG(); } eg.readBoolean(); eg.readUEG(); let next_num_delta_pocs = 0; for (let j = 0; j <= num_delta_pocs; j++) { const used_by_curr_pic_flag = eg.readBoolean(); let use_delta_flag = false; if (!used_by_curr_pic_flag) { use_delta_flag = eg.readBoolean(); } if (used_by_curr_pic_flag || use_delta_flag) { next_num_delta_pocs++; } } num_delta_pocs = next_num_delta_pocs; } else { const num_negative_pics = eg.readUEG(); const num_positive_pics = eg.readUEG(); num_delta_pocs = num_negative_pics + num_positive_pics; for (let j = 0; j < num_negative_pics; j++) { eg.readUEG(); eg.readBoolean(); } for (let j = 0; j < num_positive_pics; j++) { eg.readUEG(); eg.readBoolean(); } } } const long_term_ref_pics_present_flag = eg.readBoolean(); if (long_term_ref_pics_present_flag) { const num_long_term_ref_pics_sps = eg.readUEG(); for (let i = 0; i < num_long_term_ref_pics_sps; i++) { for (let j = 0; j < log2_max_pic_order_cnt_lsb_minus4 + 4; j++) { eg.readBits(1); } eg.readBits(1); } } let min_spatial_segmentation_idc = 0; let sar_width = 1, sar_height = 1; let fps_fixed = true, fps_den = 1, fps_num = 0; eg.readBoolean(); eg.readBoolean(); let default_display_window_flag = false; const vui_parameters_present_flag = eg.readBoolean(); if (vui_parameters_present_flag) { const aspect_ratio_info_present_flag = eg.readBoolean(); if (aspect_ratio_info_present_flag) { const aspect_ratio_idc = eg.readUByte(); const sar_width_table = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2]; const sar_height_table = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1]; if (aspect_ratio_idc > 0 && aspect_ratio_idc < 16) { sar_width = sar_width_table[aspect_ratio_idc - 1]; sar_height = sar_height_table[aspect_ratio_idc - 1]; } else if (aspect_ratio_idc === 255) { sar_width = eg.readBits(16); sar_height = eg.readBits(16); } } const overscan_info_present_flag = eg.readBoolean(); if (overscan_info_present_flag) { eg.readBoolean(); } const video_signal_type_present_flag = eg.readBoolean(); if (video_signal_type_present_flag) { eg.readBits(3); eg.readBoolean(); const colour_description_present_flag = eg.readBoolean(); if (colour_description_present_flag) { eg.readUByte(); eg.readUByte(); eg.readUByte(); } } const chroma_loc_info_present_flag = eg.readBoolean(); if (chroma_loc_info_present_flag) { eg.readUEG(); eg.readUEG(); } eg.readBoolean(); eg.readBoolean(); eg.readBoolean(); default_display_window_flag = eg.readBoolean(); if (default_display_window_flag) { pic_left_offset += eg.readUEG(); pic_right_offset += eg.readUEG(); pic_top_offset += eg.readUEG(); pic_bottom_offset += eg.readUEG(); } const vui_timing_info_present_flag = eg.readBoolean(); if (vui_timing_info_present_flag) { fps_den = eg.readBits(32); fps_num = eg.readBits(32); const vui_poc_proportional_to_timing_flag = eg.readBoolean(); if (vui_poc_proportional_to_timing_flag) { eg.readUEG(); } const vui_hrd_parameters_present_flag = eg.readBoolean(); if (vui_hrd_parameters_present_flag) { const nal_hrd_parameters_present_flag = eg.readBoolean(); const vcl_hrd_parameters_present_flag = eg.readBoolean(); let sub_pic_hrd_params_present_flag = false; if (nal_hrd_parameters_present_flag || vcl_hrd_parameters_present_flag) { sub_pic_hrd_params_present_flag = eg.readBoolean(); if (sub_pic_hrd_params_present_flag) { eg.readUByte(); eg.readBits(5); eg.readBoolean(); eg.readBits(5); } eg.readBits(4); eg.readBits(4); if (sub_pic_hrd_params_present_flag) { eg.readBits(4); } eg.readBits(5); eg.readBits(5); eg.readBits(5); } for (let i = 0; i <= max_sub_layers_minus1; i++) { fps_fixed = eg.readBoolean(); const fixed_pic_rate_within_cvs_flag = fps_fixed || eg.readBoolean(); let low_delay_hrd_flag = false; if (fixed_pic_rate_within_cvs_flag) { eg.readEG(); } else { low_delay_hrd_flag = eg.readBoolean(); } const cpb_cnt = low_delay_hrd_flag ? 1 : eg.readUEG() + 1; if (nal_hrd_parameters_present_flag) { for (let j = 0; j < cpb_cnt; j++) { eg.readUEG(); eg.readUEG(); if (sub_pic_hrd_params_present_flag) { eg.readUEG(); eg.readUEG(); } eg.skipBits(1); } } if (vcl_hrd_parameters_present_flag) { for (let j = 0; j < cpb_cnt; j++) { eg.readUEG(); eg.readUEG(); if (sub_pic_hrd_params_present_flag) { eg.readUEG(); eg.readUEG(); } eg.skipBits(1); } } } } } const bitstream_restriction_flag = eg.readBoolean(); if (bitstream_restriction_flag) { eg.readBoolean(); eg.readBoolean(); eg.readBoolean(); min_spatial_segmentation_idc = eg.readUEG(); } } let width = pic_width_in_luma_samples, height = pic_height_in_luma_samples; if (conformance_window_flag || default_display_window_flag) { let chroma_scale_w = 1, chroma_scale_h = 1; if (chroma_format_idc === 1) { chroma_scale_w = chroma_scale_h = 2; } else if (chroma_format_idc == 2) { chroma_scale_w = 2; } width = pic_width_in_luma_samples - chroma_scale_w * pic_right_offset - chroma_scale_w * pic_left_offset; height = pic_height_in_luma_samples - chroma_scale_h * pic_bottom_offset - chroma_scale_h * pic_top_offset; } const profile_space_string = general_profile_space ? ["A", "B", "C"][general_profile_space] : ""; const profile_compatibility_buf = general_profile_compatibility_flags_1 << 24 | general_profile_compatibility_flags_2 << 16 | general_profile_compatibility_flags_3 << 8 | general_profile_compatibility_flags_4; let profile_compatibility_rev = 0; for (let i = 0; i < 32; i++) { profile_compatibility_rev = (profile_compatibility_rev | (profile_compatibility_buf >> i & 1) << 31 - i) >>> 0; } let profile_compatibility_flags_string = profile_compatibility_rev.toString(16); if (general_profile_idc === 1 && profile_compatibility_flags_string === "2") { profile_compatibility_flags_string = "6"; } const tier_flag_string = general_tier_flag ? "H" : "L"; return { codecString: `hvc1.${profile_space_string}${general_profile_idc}.${profile_compatibility_flags_string}.${tier_flag_string}${general_level_idc}.B0`, params: { general_tier_flag, general_profile_idc, general_profile_space, general_profile_compatibility_flags: [general_profile_compatibility_flags_1, general_profile_compatibility_flags_2, general_profile_compatibility_flags_3, general_profile_compatibility_flags_4], general_constraint_indicator_flags: [general_constraint_indicator_flags_1, general_constraint_indicator_flags_2, general_constraint_indicator_flags_3, general_constraint_indicator_flags_4, general_constraint_indicator_flags_5, general_constraint_indicator_flags_6], general_level_idc, bit_depth: bit_depth_luma_minus8 + 8, bit_depth_luma_minus8, bit_depth_chroma_minus8, min_spatial_segmentation_idc, chroma_format_idc, frame_rate: { fixed: fps_fixed, fps: fps_num / fps_den } }, width, height, pixelRatio: [sar_width, sar_height] }; } readPPS(pps) { const eg = new ExpGolomb(this.ebsp2rbsp(pps)); eg.readUByte(); eg.readUByte(); eg.skipUEG(); eg.skipUEG(); eg.skipBits(2); eg.skipBits(3); eg.skipBits(2); eg.skipUEG(); eg.skipUEG(); eg.skipEG(); eg.skipBits(2); const cu_qp_delta_enabled_flag = eg.readBoolean(); if (cu_qp_delta_enabled_flag) { eg.skipUEG(); } eg.skipEG(); eg.skipEG(); eg.skipBits(4); const tiles_enabled_flag = eg.readBoolean(); const entropy_coding_sync_enabled_flag = eg.readBoolean(); let parallelismType = 1; if (entropy_coding_sync_enabled_flag && tiles_enabled_flag) { parallelismType = 0; } else if (entropy_coding_sync_enabled_flag) { parallelismType = 3; } else if (tiles_enabled_flag) { parallelismType = 2; } return { parallelismType }; } matchSPS(sps1, sps2) { return String.fromCharCode.apply(null, sps1).substr(3) === String.fromCharCode.apply(null, sps2).substr(3); } }; var PACKET_LENGTH = 188; var TSDemuxer = class { constructor(observer, config, typeSupported, logger2) { this.logger = void 0; this.observer = void 0; this.config = void 0; this.typeSupported = void 0; this.sampleAes = null; this.pmtParsed = false; this.audioCodec = void 0; this.videoCodec = void 0; this._pmtId = -1; this._videoTrack = void 0; this._audioTrack = void 0; this._id3Track = void 0; this._txtTrack = void 0; this.aacOverFlow = null; this.remainderData = null; this.videoParser = void 0; this.observer = observer; this.config = config; this.typeSupported = typeSupported; this.logger = logger2; this.videoParser = null; } static probe(data, logger2) { const syncOffset = TSDemuxer.syncOffset(data); if (syncOffset > 0) { logger2.warn(`MPEG2-TS detected but first sync word found @ offset ${syncOffset}`); } return syncOffset !== -1; } static syncOffset(data) { const length = data.length; let scanwindow = Math.min(PACKET_LENGTH * 5, length - PACKET_LENGTH) + 1; let i = 0; while (i < scanwindow) { let foundPat = false; let packetStart = -1; let tsPackets = 0; for (let j = i; j < length; j += PACKET_LENGTH) { if (data[j] === 71 && (length - j === PACKET_LENGTH || data[j + PACKET_LENGTH] === 71)) { tsPackets++; if (packetStart === -1) { packetStart = j; if (packetStart !== 0) { scanwindow = Math.min(packetStart + PACKET_LENGTH * 99, data.length - PACKET_LENGTH) + 1; } } if (!foundPat) { foundPat = parsePID(data, j) === 0; } if (foundPat && tsPackets > 1 && (packetStart === 0 && tsPackets > 2 || j + PACKET_LENGTH > scanwindow)) { return packetStart; } } else if (tsPackets) { return -1; } else { break; } } i++; } return -1; } static createTrack(type, duration) { return { container: type === "video" || type === "audio" ? "video/mp2t" : void 0, type, id: RemuxerTrackIdConfig[type], pid: -1, inputTimeScale: 9e4, sequenceNumber: 0, samples: [], dropped: 0, duration: type === "audio" ? duration : void 0 }; } resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { this.pmtParsed = false; this._pmtId = -1; this._videoTrack = TSDemuxer.createTrack("video"); this._videoTrack.duration = trackDuration; this._audioTrack = TSDemuxer.createTrack("audio", trackDuration); this._id3Track = TSDemuxer.createTrack("id3"); this._txtTrack = TSDemuxer.createTrack("text"); this._audioTrack.segmentCodec = "aac"; this.aacOverFlow = null; this.remainderData = null; this.audioCodec = audioCodec; this.videoCodec = videoCodec; } resetTimeStamp() { } resetContiguity() { const { _audioTrack, _videoTrack, _id3Track } = this; if (_audioTrack) { _audioTrack.pesData = null; } if (_videoTrack) { _videoTrack.pesData = null; } if (_id3Track) { _id3Track.pesData = null; } this.aacOverFlow = null; this.remainderData = null; } demux(data, timeOffset, isSampleAes = false, flush = false) { if (!isSampleAes) { this.sampleAes = null; } let pes; const videoTrack = this._videoTrack; const audioTrack = this._audioTrack; const id3Track = this._id3Track; const textTrack = this._txtTrack; let videoPid = videoTrack.pid; let videoData = videoTrack.pesData; let audioPid = audioTrack.pid; let id3Pid = id3Track.pid; let audioData = audioTrack.pesData; let id3Data = id3Track.pesData; let unknownPID = null; let pmtParsed = this.pmtParsed; let pmtId = this._pmtId; let len = data.length; if (this.remainderData) { data = appendUint8Array(this.remainderData, data); len = data.length; this.remainderData = null; } if (len < PACKET_LENGTH && !flush) { this.remainderData = data; return { audioTrack, videoTrack, id3Track, textTrack }; } const syncOffset = Math.max(0, TSDemuxer.syncOffset(data)); len -= (len - syncOffset) % PACKET_LENGTH; if (len < data.byteLength && !flush) { this.remainderData = new Uint8Array(data.buffer, len, data.buffer.byteLength - len); } let tsPacketErrors = 0; for (let start = syncOffset; start < len; start += PACKET_LENGTH) { if (data[start] === 71) { const stt = !!(data[start + 1] & 64); const pid = parsePID(data, start); const atf = (data[start + 3] & 48) >> 4; let offset; if (atf > 1) { offset = start + 5 + data[start + 4]; if (offset === start + PACKET_LENGTH) { continue; } } else { offset = start + 4; } switch (pid) { case videoPid: if (stt) { if (videoData && (pes = parsePES(videoData, this.logger))) { if (this.videoParser === null) { switch (videoTrack.segmentCodec) { case "avc": this.videoParser = new AvcVideoParser(); break; case "hevc": { this.videoParser = new HevcVideoParser(); } break; } } if (this.videoParser !== null) { this.videoParser.parsePES(videoTrack, textTrack, pes, false); } } videoData = { data: [], size: 0 }; } if (videoData) { videoData.data.push(data.subarray(offset, start + PACKET_LENGTH)); videoData.size += start + PACKET_LENGTH - offset; } break; case audioPid: if (stt) { if (audioData && (pes = parsePES(audioData, this.logger))) { switch (audioTrack.segmentCodec) { case "aac": this.parseAACPES(audioTrack, pes); break; case "mp3": this.parseMPEGPES(audioTrack, pes); break; case "ac3": { this.parseAC3PES(audioTrack, pes); } break; } } audioData = { data: [], size: 0 }; } if (audioData) { audioData.data.push(data.subarray(offset, start + PACKET_LENGTH)); audioData.size += start + PACKET_LENGTH - offset; } break; case id3Pid: if (stt) { if (id3Data && (pes = parsePES(id3Data, this.logger))) { this.parseID3PES(id3Track, pes); } id3Data = { data: [], size: 0 }; } if (id3Data) { id3Data.data.push(data.subarray(offset, start + PACKET_LENGTH)); id3Data.size += start + PACKET_LENGTH - offset; } break; case 0: if (stt) { offset += data[offset] + 1; } pmtId = this._pmtId = parsePAT(data, offset); break; case pmtId: { if (stt) { offset += data[offset] + 1; } const parsedPIDs = parsePMT(data, offset, this.typeSupported, isSampleAes, this.observer, this.logger); videoPid = parsedPIDs.videoPid; if (videoPid > 0) { videoTrack.pid = videoPid; videoTrack.segmentCodec = parsedPIDs.segmentVideoCodec; } audioPid = parsedPIDs.audioPid; if (audioPid > 0) { audioTrack.pid = audioPid; audioTrack.segmentCodec = parsedPIDs.segmentAudioCodec; } id3Pid = parsedPIDs.id3Pid; if (id3Pid > 0) { id3Track.pid = id3Pid; } if (unknownPID !== null && !pmtParsed) { this.logger.warn(`MPEG-TS PMT found at ${start} after unknown PID '${unknownPID}'. Backtracking to sync byte @${syncOffset} to parse all TS packets.`); unknownPID = null; start = syncOffset - 188; } pmtParsed = this.pmtParsed = true; break; } case 17: case 8191: break; default: unknownPID = pid; break; } } else { tsPacketErrors++; } } if (tsPacketErrors > 0) { emitParsingError(this.observer, new Error(`Found ${tsPacketErrors} TS packet/s that do not start with 0x47`), void 0, this.logger); } videoTrack.pesData = videoData; audioTrack.pesData = audioData; id3Track.pesData = id3Data; const demuxResult = { audioTrack, videoTrack, id3Track, textTrack }; if (flush) { this.extractRemainingSamples(demuxResult); } return demuxResult; } flush() { const { remainderData } = this; this.remainderData = null; let result; if (remainderData) { result = this.demux(remainderData, -1, false, true); } else { result = { videoTrack: this._videoTrack, audioTrack: this._audioTrack, id3Track: this._id3Track, textTrack: this._txtTrack }; } this.extractRemainingSamples(result); if (this.sampleAes) { return this.decrypt(result, this.sampleAes); } return result; } extractRemainingSamples(demuxResult) { const { audioTrack, videoTrack, id3Track, textTrack } = demuxResult; const videoData = videoTrack.pesData; const audioData = audioTrack.pesData; const id3Data = id3Track.pesData; let pes; if (videoData && (pes = parsePES(videoData, this.logger))) { if (this.videoParser === null) { switch (videoTrack.segmentCodec) { case "avc": this.videoParser = new AvcVideoParser(); break; case "hevc": { this.videoParser = new HevcVideoParser(); } break; } } if (this.videoParser !== null) { this.videoParser.parsePES(videoTrack, textTrack, pes, true); videoTrack.pesData = null; } } else { videoTrack.pesData = videoData; } if (audioData && (pes = parsePES(audioData, this.logger))) { switch (audioTrack.segmentCodec) { case "aac": this.parseAACPES(audioTrack, pes); break; case "mp3": this.parseMPEGPES(audioTrack, pes); break; case "ac3": { this.parseAC3PES(audioTrack, pes); } break; } audioTrack.pesData = null; } else { if (audioData != null && audioData.size) { this.logger.log("last AAC PES packet truncated,might overlap between fragments"); } audioTrack.pesData = audioData; } if (id3Data && (pes = parsePES(id3Data, this.logger))) { this.parseID3PES(id3Track, pes); id3Track.pesData = null; } else { id3Track.pesData = id3Data; } } demuxSampleAes(data, keyData, timeOffset) { const demuxResult = this.demux(data, timeOffset, true, !this.config.progressive); const sampleAes = this.sampleAes = new SampleAesDecrypter(this.observer, this.config, keyData); return this.decrypt(demuxResult, sampleAes); } decrypt(demuxResult, sampleAes) { return new Promise((resolve) => { const { audioTrack, videoTrack } = demuxResult; if (audioTrack.samples && audioTrack.segmentCodec === "aac") { sampleAes.decryptAacSamples(audioTrack.samples, 0, () => { if (videoTrack.samples) { sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, () => { resolve(demuxResult); }); } else { resolve(demuxResult); } }); } else if (videoTrack.samples) { sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, () => { resolve(demuxResult); }); } }); } destroy() { if (this.observer) { this.observer.removeAllListeners(); } this.config = this.logger = this.observer = null; this.aacOverFlow = this.videoParser = this.remainderData = this.sampleAes = null; this._videoTrack = this._audioTrack = this._id3Track = this._txtTrack = void 0; } parseAACPES(track, pes) { let startOffset = 0; const aacOverFlow = this.aacOverFlow; let data = pes.data; if (aacOverFlow) { this.aacOverFlow = null; const frameMissingBytes = aacOverFlow.missing; const sampleLength = aacOverFlow.sample.unit.byteLength; if (frameMissingBytes === -1) { data = appendUint8Array(aacOverFlow.sample.unit, data); } else { const frameOverflowBytes = sampleLength - frameMissingBytes; aacOverFlow.sample.unit.set(data.subarray(0, frameMissingBytes), frameOverflowBytes); track.samples.push(aacOverFlow.sample); startOffset = aacOverFlow.missing; } } let offset; let len; for (offset = startOffset, len = data.length; offset < len - 1; offset++) { if (isHeader$1(data, offset)) { break; } } if (offset !== startOffset) { let reason; const recoverable = offset < len - 1; if (recoverable) { reason = `AAC PES did not start with ADTS header,offset:${offset}`; } else { reason = "No ADTS header found in AAC PES"; } emitParsingError(this.observer, new Error(reason), recoverable, this.logger); if (!recoverable) { return; } } initTrackConfig(track, this.observer, data, offset, this.audioCodec); let pts; if (pes.pts !== void 0) { pts = pes.pts; } else if (aacOverFlow) { const frameDuration = getFrameDuration(track.samplerate); pts = aacOverFlow.sample.pts + frameDuration; } else { this.logger.warn("[tsdemuxer]: AAC PES unknown PTS"); return; } let frameIndex = 0; let frame; while (offset < len) { frame = appendFrame$2(track, data, offset, pts, frameIndex); offset += frame.length; if (!frame.missing) { frameIndex++; for (; offset < len - 1; offset++) { if (isHeader$1(data, offset)) { break; } } } else { this.aacOverFlow = frame; break; } } } parseMPEGPES(track, pes) { const data = pes.data; const length = data.length; let frameIndex = 0; let offset = 0; const pts = pes.pts; if (pts === void 0) { this.logger.warn("[tsdemuxer]: MPEG PES unknown PTS"); return; } while (offset < length) { if (isHeader(data, offset)) { const frame = appendFrame$1(track, data, offset, pts, frameIndex); if (frame) { offset += frame.length; frameIndex++; } else { break; } } else { offset++; } } } parseAC3PES(track, pes) { { const data = pes.data; const pts = pes.pts; if (pts === void 0) { this.logger.warn("[tsdemuxer]: AC3 PES unknown PTS"); return; } const length = data.length; let frameIndex = 0; let offset = 0; let parsed; while (offset < length && (parsed = appendFrame(track, data, offset, pts, frameIndex++)) > 0) { offset += parsed; } } } parseID3PES(id3Track, pes) { if (pes.pts === void 0) { this.logger.warn("[tsdemuxer]: ID3 PES unknown PTS"); return; } const id3Sample = _extends({}, pes, { type: this._videoTrack ? MetadataSchema.emsg : MetadataSchema.audioId3, duration: Number.POSITIVE_INFINITY }); id3Track.samples.push(id3Sample); } }; function parsePID(data, offset) { return ((data[offset + 1] & 31) << 8) + data[offset + 2]; } function parsePAT(data, offset) { return (data[offset + 10] & 31) << 8 | data[offset + 11]; } function parsePMT(data, offset, typeSupported, isSampleAes, observer, logger2) { const result = { audioPid: -1, videoPid: -1, id3Pid: -1, segmentVideoCodec: "avc", segmentAudioCodec: "aac" }; const sectionLength = (data[offset + 1] & 15) << 8 | data[offset + 2]; const tableEnd = offset + 3 + sectionLength - 4; const programInfoLength = (data[offset + 10] & 15) << 8 | data[offset + 11]; offset += 12 + programInfoLength; while (offset < tableEnd) { const pid = parsePID(data, offset); const esInfoLength = (data[offset + 3] & 15) << 8 | data[offset + 4]; switch (data[offset]) { case 207: if (!isSampleAes) { logEncryptedSamplesFoundInUnencryptedStream("ADTS AAC", logger2); break; } case 15: if (result.audioPid === -1) { result.audioPid = pid; } break; case 21: if (result.id3Pid === -1) { result.id3Pid = pid; } break; case 219: if (!isSampleAes) { logEncryptedSamplesFoundInUnencryptedStream("H.264", logger2); break; } case 27: if (result.videoPid === -1) { result.videoPid = pid; } break; case 3: case 4: if (!typeSupported.mpeg && !typeSupported.mp3) { logger2.log("MPEG audio found, not supported in this browser"); } else if (result.audioPid === -1) { result.audioPid = pid; result.segmentAudioCodec = "mp3"; } break; case 193: if (!isSampleAes) { logEncryptedSamplesFoundInUnencryptedStream("AC-3", logger2); break; } case 129: { if (!typeSupported.ac3) { logger2.log("AC-3 audio found, not supported in this browser"); } else if (result.audioPid === -1) { result.audioPid = pid; result.segmentAudioCodec = "ac3"; } } break; case 6: if (result.audioPid === -1 && esInfoLength > 0) { let parsePos = offset + 5; let remaining = esInfoLength; while (remaining > 2) { const descriptorId = data[parsePos]; switch (descriptorId) { case 106: { if (typeSupported.ac3 !== true) { logger2.log("AC-3 audio found, not supported in this browser for now"); } else { result.audioPid = pid; result.segmentAudioCodec = "ac3"; } } break; } const descriptorLen = data[parsePos + 1] + 2; parsePos += descriptorLen; remaining -= descriptorLen; } } break; case 194: case 135: emitParsingError(observer, new Error("Unsupported EC-3 in M2TS found"), void 0, logger2); return result; case 36: { if (result.videoPid === -1) { result.videoPid = pid; result.segmentVideoCodec = "hevc"; logger2.log("HEVC in M2TS found"); } } break; } offset += esInfoLength + 5; } return result; } function emitParsingError(observer, error, levelRetry, logger2) { logger2.warn(`parsing error: ${error.message}`); observer.emit(Events.ERROR, Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: false, levelRetry, error, reason: error.message }); } function logEncryptedSamplesFoundInUnencryptedStream(type, logger2) { logger2.log(`${type} with AES-128-CBC encryption found in unencrypted stream`); } function parsePES(stream, logger2) { let i = 0; let frag; let pesLen; let pesHdrLen; let pesPts; let pesDts; const data = stream.data; if (!stream || stream.size === 0) { return null; } while (data[0].length < 19 && data.length > 1) { data[0] = appendUint8Array(data[0], data[1]); data.splice(1, 1); } frag = data[0]; const pesPrefix = (frag[0] << 16) + (frag[1] << 8) + frag[2]; if (pesPrefix === 1) { pesLen = (frag[4] << 8) + frag[5]; if (pesLen && pesLen > stream.size - 6) { return null; } const pesFlags = frag[7]; if (pesFlags & 192) { pesPts = (frag[9] & 14) * 536870912 + (frag[10] & 255) * 4194304 + (frag[11] & 254) * 16384 + (frag[12] & 255) * 128 + (frag[13] & 254) / 2; if (pesFlags & 64) { pesDts = (frag[14] & 14) * 536870912 + (frag[15] & 255) * 4194304 + (frag[16] & 254) * 16384 + (frag[17] & 255) * 128 + (frag[18] & 254) / 2; if (pesPts - pesDts > 60 * 9e4) { logger2.warn(`${Math.round((pesPts - pesDts) / 9e4)}s delta between PTS and DTS, align them`); pesPts = pesDts; } } else { pesDts = pesPts; } } pesHdrLen = frag[8]; let payloadStartOffset = pesHdrLen + 9; if (stream.size <= payloadStartOffset) { return null; } stream.size -= payloadStartOffset; const 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) { payloadStartOffset -= len; continue; } else { frag = frag.subarray(payloadStartOffset); len -= payloadStartOffset; payloadStartOffset = 0; } } pesData.set(frag, i); i += len; } if (pesLen) { pesLen -= pesHdrLen + 3; } return { data: pesData, pts: pesPts, dts: pesDts, len: pesLen }; } return null; } var AAC = class { static 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; default: if (channelCount === 1) { 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) { 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) { 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 void 0; } }; var UINT32_MAX = Math.pow(2, 32) - 1; var MP4 = class { static init() { MP4.types = { avc1: [], avcC: [], hvc1: [], hvcC: [], btrt: [], dinf: [], dref: [], esds: [], ftyp: [], hdlr: [], mdat: [], mdhd: [], mdia: [], mfhd: [], minf: [], moof: [], moov: [], mp4a: [], ".mp3": [], dac3: [], "ac-3": [], mvex: [], mvhd: [], pasp: [], sdtp: [], stbl: [], stco: [], stsc: [], stsd: [], stsz: [], stts: [], tfdt: [], tfhd: [], traf: [], trak: [], trun: [], trex: [], tkhd: [], vmhd: [], smhd: [] }; let i; for (i in MP4.types) { 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 ]); const majorBrand = new Uint8Array([105, 115, 111, 109]); const avc1Brand = new Uint8Array([97, 118, 99, 49]); 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 box(type, ...payload) { let size = 8; let i = payload.length; const len = i; 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); for (i = 0, size = 8; i < len; i++) { 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)); const lowerWordDuration = Math.floor(duration % (UINT32_MAX + 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) { return MP4.box(MP4.types.mdia, MP4.mdhd(track.timescale || 0, track.duration || 0), MP4.hdlr(track.type), MP4.minf(track)); } 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 ])); } 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(sn, baseMediaDecodeTime, track) { return MP4.box(MP4.types.moof, MP4.mfhd(sn), MP4.traf(track, baseMediaDecodeTime)); } 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].timescale || 0, tracks[0].duration || 0)].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.apply(null, [MP4.types.mvex, ...boxes]); } static mvhd(timescale, duration) { duration *= timescale; const upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)); const lowerWordDuration = Math.floor(duration % (UINT32_MAX + 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 || []; const bytes = new Uint8Array(4 + samples.length); let i; let flags; 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) { return MP4.box(MP4.types.stbl, MP4.stsd(track), MP4.box(MP4.types.stts, MP4.STTS), MP4.box(MP4.types.stsc, MP4.STSC), MP4.box(MP4.types.stsz, MP4.STSZ), MP4.box(MP4.types.stco, MP4.STCO)); } static avc1(track) { let sps = []; let pps = []; let i; let data; let len; for (i = 0; i < track.sps.length; i++) { data = track.sps[i]; len = data.byteLength; sps.push(len >>> 8 & 255); sps.push(len & 255); sps = sps.concat(Array.prototype.slice.call(data)); } for (i = 0; i < track.pps.length; i++) { data = track.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], 252 | 3, 224 | track.sps.length ].concat(sps).concat([ track.pps.length ]).concat(pps))); const width = track.width; const height = track.height; const hSpacing = track.pixelRatio[0]; const vSpacing = track.pixelRatio[1]; return MP4.box(MP4.types.avc1, 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 ]), avcc, MP4.box(MP4.types.btrt, new Uint8Array([ 0, 28, 156, 128, 0, 45, 198, 192, 0, 45, 198, 192 ])), 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(track) { const config = track.config; return new Uint8Array([ 0, 0, 0, 0, 3, 25, 0, 1, 0, 4, 17, 64, 21, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 2, ...config, 6, 1, 2 ]); } static audioStsd(track) { const samplerate = track.samplerate || 0; return new Uint8Array([ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, track.channelCount || 0, 0, 16, 0, 0, 0, 0, samplerate >> 8 & 255, samplerate & 255, 0, 0 ]); } static mp4a(track) { return MP4.box(MP4.types.mp4a, MP4.audioStsd(track), MP4.box(MP4.types.esds, MP4.esds(track))); } static mp3(track) { return MP4.box(MP4.types[".mp3"], MP4.audioStsd(track)); } static ac3(track) { return MP4.box(MP4.types["ac-3"], MP4.audioStsd(track), MP4.box(MP4.types.dac3, track.config)); } static stsd(track) { const { segmentCodec } = track; if (track.type === "audio") { if (segmentCodec === "aac") { return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track)); } if (segmentCodec === "ac3" && track.config) { return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track)); } if (segmentCodec === "mp3" && track.codec === "mp3") { return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp3(track)); } } else { if (track.pps && track.sps) { if (segmentCodec === "avc") { return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track)); } if (segmentCodec === "hevc" && track.vps) { return MP4.box(MP4.types.stsd, MP4.STSD, MP4.hvc1(track)); } } else { throw new Error(`video track missing pps or sps`); } } throw new Error(`unsupported ${track.type} segment codec (${segmentCodec}/${track.codec})`); } static tkhd(track) { const id = track.id; const duration = (track.duration || 0) * (track.timescale || 0); const width = track.width || 0; const height = track.height || 0; const upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)); const lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); 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 sampleDependencyTable = MP4.sdtp(track); const id = track.id; const upperWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1)); const lowerWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1)); return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([ 0, 0, 0, 0, id >> 24, id >> 16 & 255, id >> 8 & 255, id & 255 ])), 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 ])), MP4.trun(track, sampleDependencyTable.length + 16 + 20 + 8 + 16 + 8 + 8), sampleDependencyTable); } static trak(track) { track.duration = track.duration || 4294967295; return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track)); } static trex(track) { const id = track.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 || []; const len = samples.length; const arraylen = 12 + 16 * len; const array = new Uint8Array(arraylen); let i; let sample; let duration; let size; let flags; let cts; offset += 8 + arraylen; array.set([ track.type === "video" ? 1 : 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 ], 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 ], 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 = appendUint8Array(MP4.FTYP, movie); return result; } static hvc1(track) { const ps = track.params; const units = [track.vps, track.sps, track.pps]; const NALuLengthSize = 4; const config = new Uint8Array([1, ps.general_profile_space << 6 | (ps.general_tier_flag ? 32 : 0) | ps.general_profile_idc, ps.general_profile_compatibility_flags[0], ps.general_profile_compatibility_flags[1], ps.general_profile_compatibility_flags[2], ps.general_profile_compatibility_flags[3], ps.general_constraint_indicator_flags[0], ps.general_constraint_indicator_flags[1], ps.general_constraint_indicator_flags[2], ps.general_constraint_indicator_flags[3], ps.general_constraint_indicator_flags[4], ps.general_constraint_indicator_flags[5], ps.general_level_idc, 240 | ps.min_spatial_segmentation_idc >> 8, 255 & ps.min_spatial_segmentation_idc, 252 | ps.parallelismType, 252 | ps.chroma_format_idc, 248 | ps.bit_depth_luma_minus8, 248 | ps.bit_depth_chroma_minus8, 0, parseInt(ps.frame_rate.fps), NALuLengthSize - 1 | ps.temporal_id_nested << 2 | ps.num_temporal_layers << 3 | (ps.frame_rate.fixed ? 64 : 0), units.length]); let length = config.length; for (let i = 0; i < units.length; i += 1) { length += 3; for (let j = 0; j < units[i].length; j += 1) { length += 2 + units[i][j].length; } } const hvcC = new Uint8Array(length); hvcC.set(config, 0); length = config.length; const iMax = units.length - 1; for (let i = 0; i < units.length; i += 1) { hvcC.set(new Uint8Array([32 + i | (i === iMax ? 128 : 0), 0, units[i].length]), length); length += 3; for (let j = 0; j < units[i].length; j += 1) { hvcC.set(new Uint8Array([units[i][j].length >> 8, units[i][j].length & 255]), length); length += 2; hvcC.set(units[i][j], length); length += units[i][j].length; } } const hvcc = MP4.box(MP4.types.hvcC, hvcC); const width = track.width; const height = track.height; const hSpacing = track.pixelRatio[0]; const vSpacing = track.pixelRatio[1]; return MP4.box(MP4.types.hvc1, 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 ]), hvcc, MP4.box(MP4.types.btrt, new Uint8Array([ 0, 28, 156, 128, 0, 45, 198, 192, 0, 45, 198, 192 ])), 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 ]))); } }; MP4.types = void 0; MP4.HDLR_TYPES = void 0; MP4.STTS = void 0; MP4.STSC = void 0; MP4.STCO = void 0; MP4.STSZ = void 0; MP4.VMHD = void 0; MP4.SMHD = void 0; MP4.STSD = void 0; MP4.FTYP = void 0; MP4.DINF = void 0; var MPEG_TS_CLOCK_FREQ_HZ = 9e4; function toTimescaleFromBase(baseTime, destScale, srcBase = 1, round = false) { const result = baseTime * destScale * srcBase; return round ? Math.round(result) : result; } function toTimescaleFromScale(baseTime, destScale, srcScale = 1, round = false) { return toTimescaleFromBase(baseTime, destScale, 1 / srcScale, round); } function toMsFromMpegTsClock(baseTime, round = false) { return toTimescaleFromBase(baseTime, 1e3, 1 / MPEG_TS_CLOCK_FREQ_HZ, round); } function toMpegTsClockFromTimescale(baseTime, srcScale = 1) { return toTimescaleFromBase(baseTime, MPEG_TS_CLOCK_FREQ_HZ, 1 / srcScale); } var MAX_SILENT_FRAME_DURATION = 10 * 1e3; var AAC_SAMPLES_PER_FRAME = 1024; var MPEG_AUDIO_SAMPLE_PER_FRAME = 1152; var AC3_SAMPLES_PER_FRAME = 1536; var chromeVersion = null; var safariWebkitVersion = null; function createMp4Sample(isKeyframe, duration, size, cts) { return { duration, size, cts, flags: { isLeading: 0, isDependedOn: 0, hasRedundancy: 0, degradPrio: 0, dependsOn: isKeyframe ? 2 : 1, isNonSync: isKeyframe ? 0 : 1 } }; } var MP4Remuxer = class extends Logger { constructor(observer, config, typeSupported, logger2) { super("mp4-remuxer", logger2); this.observer = void 0; this.config = void 0; this.typeSupported = void 0; this.ISGenerated = false; this._initPTS = null; this._initDTS = null; this.nextVideoTs = null; this.nextAudioTs = null; this.videoSampleDuration = null; this.isAudioContiguous = false; this.isVideoContiguous = false; this.videoTrackConfig = void 0; this.observer = observer; this.config = config; this.typeSupported = typeSupported; this.ISGenerated = false; if (chromeVersion === null) { const userAgent = navigator.userAgent || ""; const result = userAgent.match(/Chrome\/(\d+)/i); chromeVersion = result ? parseInt(result[1]) : 0; } if (safariWebkitVersion === null) { const result = navigator.userAgent.match(/Safari\/(\d+)/i); safariWebkitVersion = result ? parseInt(result[1]) : 0; } } destroy() { this.config = this.videoTrackConfig = this._initPTS = this._initDTS = null; } resetTimeStamp(defaultTimeStamp) { this.log("initPTS & initDTS reset"); this._initPTS = this._initDTS = defaultTimeStamp; } resetNextTimestamp() { this.log("reset next timestamp"); this.isVideoContiguous = false; this.isAudioContiguous = false; } resetInitSegment() { this.log("ISGenerated flag reset"); this.ISGenerated = false; this.videoTrackConfig = void 0; } getVideoStartPts(videoSamples) { let rolloverDetected = false; const firstPts = videoSamples[0].pts; const startPTS = videoSamples.reduce((minPTS, sample) => { let pts = sample.pts; let delta = pts - minPTS; if (delta < -4294967296) { rolloverDetected = true; pts = normalizePts(pts, firstPts); delta = pts - minPTS; } if (delta > 0) { return minPTS; } return pts; }, firstPts); if (rolloverDetected) { this.debug("PTS rollover detected"); } return startPTS; } remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset, flush, playlistType) { let video; let audio; let initSegment; let text; let id3; let independent; let audioTimeOffset = timeOffset; let videoTimeOffset = timeOffset; const hasAudio = audioTrack.pid > -1; const hasVideo = videoTrack.pid > -1; const length = videoTrack.samples.length; const enoughAudioSamples = audioTrack.samples.length > 0; const enoughVideoSamples = flush && length > 0 || length > 1; const canRemuxAvc = (!hasAudio || enoughAudioSamples) && (!hasVideo || enoughVideoSamples) || this.ISGenerated || flush; if (canRemuxAvc) { if (this.ISGenerated) { var _videoTrack$pixelRati, _config$pixelRatio, _videoTrack$pixelRati2, _config$pixelRatio2; const config = this.videoTrackConfig; if (config && (videoTrack.width !== config.width || videoTrack.height !== config.height || ((_videoTrack$pixelRati = videoTrack.pixelRatio) == null ? void 0 : _videoTrack$pixelRati[0]) !== ((_config$pixelRatio = config.pixelRatio) == null ? void 0 : _config$pixelRatio[0]) || ((_videoTrack$pixelRati2 = videoTrack.pixelRatio) == null ? void 0 : _videoTrack$pixelRati2[1]) !== ((_config$pixelRatio2 = config.pixelRatio) == null ? void 0 : _config$pixelRatio2[1])) || !config && enoughVideoSamples || this.nextAudioTs === null && enoughAudioSamples) { this.resetInitSegment(); } } if (!this.ISGenerated) { initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset); } const isVideoContiguous = this.isVideoContiguous; let firstKeyFrameIndex = -1; let firstKeyFramePTS; if (enoughVideoSamples) { firstKeyFrameIndex = findKeyframeIndex(videoTrack.samples); if (!isVideoContiguous && this.config.forceKeyFrameOnDiscontinuity) { independent = true; if (firstKeyFrameIndex > 0) { this.warn(`Dropped ${firstKeyFrameIndex} out of ${length} video samples due to a missing keyframe`); const startPTS = this.getVideoStartPts(videoTrack.samples); videoTrack.samples = videoTrack.samples.slice(firstKeyFrameIndex); videoTrack.dropped += firstKeyFrameIndex; videoTimeOffset += (videoTrack.samples[0].pts - startPTS) / videoTrack.inputTimeScale; firstKeyFramePTS = videoTimeOffset; } else if (firstKeyFrameIndex === -1) { this.warn(`No keyframe found out of ${length} video samples`); independent = false; } } } if (this.ISGenerated) { if (enoughAudioSamples && enoughVideoSamples) { const startPTS = this.getVideoStartPts(videoTrack.samples); const tsDelta = normalizePts(audioTrack.samples[0].pts, startPTS) - startPTS; const audiovideoTimestampDelta = tsDelta / videoTrack.inputTimeScale; audioTimeOffset += Math.max(0, audiovideoTimestampDelta); videoTimeOffset += Math.max(0, -audiovideoTimestampDelta); } if (enoughAudioSamples) { if (!audioTrack.samplerate) { this.warn("regenerate InitSegment as audio detected"); initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset); } audio = this.remuxAudio(audioTrack, audioTimeOffset, this.isAudioContiguous, accurateTimeOffset, hasVideo || enoughVideoSamples || playlistType === PlaylistLevelType.AUDIO ? videoTimeOffset : void 0); if (enoughVideoSamples) { const audioTrackLength = audio ? audio.endPTS - audio.startPTS : 0; if (!videoTrack.inputTimeScale) { this.warn("regenerate InitSegment as video detected"); initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset); } video = this.remuxVideo(videoTrack, videoTimeOffset, isVideoContiguous, audioTrackLength); } } else if (enoughVideoSamples) { video = this.remuxVideo(videoTrack, videoTimeOffset, isVideoContiguous, 0); } if (video) { video.firstKeyFrame = firstKeyFrameIndex; video.independent = firstKeyFrameIndex !== -1; video.firstKeyFramePTS = firstKeyFramePTS; } } } if (this.ISGenerated && this._initPTS && this._initDTS) { if (id3Track.samples.length) { id3 = flushTextTrackMetadataCueSamples(id3Track, timeOffset, this._initPTS, this._initDTS); } if (textTrack.samples.length) { text = flushTextTrackUserdataCueSamples(textTrack, timeOffset, this._initPTS); } } return { audio, video, initSegment, independent, text, id3 }; } generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset) { const audioSamples = audioTrack.samples; const videoSamples = videoTrack.samples; const typeSupported = this.typeSupported; const tracks = {}; const _initPTS = this._initPTS; let computePTSDTS = !_initPTS || accurateTimeOffset; let container = "audio/mp4"; let initPTS; let initDTS; let timescale; let trackId; if (computePTSDTS) { initPTS = initDTS = Infinity; } if (audioTrack.config && audioSamples.length) { audioTrack.timescale = audioTrack.samplerate; switch (audioTrack.segmentCodec) { case "mp3": if (typeSupported.mpeg) { container = "audio/mpeg"; audioTrack.codec = ""; } else if (typeSupported.mp3) { audioTrack.codec = "mp3"; } break; case "ac3": audioTrack.codec = "ac-3"; break; } tracks.audio = { id: "audio", container, codec: audioTrack.codec, initSegment: audioTrack.segmentCodec === "mp3" && typeSupported.mpeg ? new Uint8Array(0) : MP4.initSegment([audioTrack]), metadata: { channelCount: audioTrack.channelCount } }; if (computePTSDTS) { trackId = audioTrack.id; timescale = audioTrack.inputTimeScale; if (!_initPTS || timescale !== _initPTS.timescale) { initPTS = initDTS = audioSamples[0].pts - Math.round(timescale * timeOffset); } else { computePTSDTS = false; } } } if (videoTrack.sps && videoTrack.pps && videoSamples.length) { videoTrack.timescale = videoTrack.inputTimeScale; tracks.video = { id: "main", container: "video/mp4", codec: videoTrack.codec, initSegment: MP4.initSegment([videoTrack]), metadata: { width: videoTrack.width, height: videoTrack.height } }; if (computePTSDTS) { trackId = videoTrack.id; timescale = videoTrack.inputTimeScale; if (!_initPTS || timescale !== _initPTS.timescale) { const startPTS = this.getVideoStartPts(videoSamples); const startOffset = Math.round(timescale * timeOffset); initDTS = Math.min(initDTS, normalizePts(videoSamples[0].dts, startPTS) - startOffset); initPTS = Math.min(initPTS, startPTS - startOffset); } else { computePTSDTS = false; } } this.videoTrackConfig = { width: videoTrack.width, height: videoTrack.height, pixelRatio: videoTrack.pixelRatio }; } if (Object.keys(tracks).length) { this.ISGenerated = true; if (computePTSDTS) { this._initPTS = { baseTime: initPTS, timescale }; this._initDTS = { baseTime: initDTS, timescale }; } else { initPTS = timescale = void 0; } return { tracks, initPTS, timescale, trackId }; } } remuxVideo(track, timeOffset, contiguous, audioTrackLength) { const timeScale = track.inputTimeScale; const inputSamples = track.samples; const outputSamples = []; const nbSamples = inputSamples.length; const initPTS = this._initPTS; const initTime = initPTS.baseTime * timeScale / initPTS.timescale; let nextVideoTs = this.nextVideoTs; let offset = 8; let mp4SampleDuration = this.videoSampleDuration; let firstDTS; let lastDTS; let minPTS = Number.POSITIVE_INFINITY; let maxPTS = Number.NEGATIVE_INFINITY; let sortSamples = false; if (!contiguous || nextVideoTs === null) { const pts = initTime + timeOffset * timeScale; const cts = inputSamples[0].pts - normalizePts(inputSamples[0].dts, inputSamples[0].pts); if (chromeVersion && nextVideoTs !== null && Math.abs(pts - cts - (nextVideoTs + initTime)) < 15e3) { contiguous = true; } else { nextVideoTs = pts - cts - initTime; } } const nextVideoPts = nextVideoTs + initTime; for (let i = 0; i < nbSamples; i++) { const sample = inputSamples[i]; sample.pts = normalizePts(sample.pts, nextVideoPts); sample.dts = normalizePts(sample.dts, nextVideoPts); if (sample.dts < inputSamples[i > 0 ? i - 1 : i].dts) { sortSamples = true; } } if (sortSamples) { inputSamples.sort(function(a, b) { const deltadts = a.dts - b.dts; const deltapts = a.pts - b.pts; return deltadts || deltapts; }); } firstDTS = inputSamples[0].dts; lastDTS = inputSamples[inputSamples.length - 1].dts; const inputDuration = lastDTS - firstDTS; const averageSampleDuration = inputDuration ? Math.round(inputDuration / (nbSamples - 1)) : mp4SampleDuration || track.inputTimeScale / 30; if (contiguous) { const delta = firstDTS - nextVideoPts; const foundHole = delta > averageSampleDuration; const foundOverlap = delta < -1; if (foundHole || foundOverlap) { if (foundHole) { this.warn(`${(track.segmentCodec || "").toUpperCase()}: ${toMsFromMpegTsClock(delta, true)} ms (${delta}dts) hole between fragments detected at ${timeOffset.toFixed(3)}`); } else { this.warn(`${(track.segmentCodec || "").toUpperCase()}: ${toMsFromMpegTsClock(-delta, true)} ms (${delta}dts) overlapping between fragments detected at ${timeOffset.toFixed(3)}`); } if (!foundOverlap || nextVideoPts >= inputSamples[0].pts || chromeVersion) { firstDTS = nextVideoPts; const firstPTS = inputSamples[0].pts - delta; if (foundHole) { inputSamples[0].dts = firstDTS; inputSamples[0].pts = firstPTS; } else { let isPTSOrderRetained = true; for (let i = 0; i < inputSamples.length; i++) { if (inputSamples[i].dts > firstPTS && isPTSOrderRetained) { break; } const prevPTS = inputSamples[i].pts; inputSamples[i].dts -= delta; inputSamples[i].pts -= delta; if (i < inputSamples.length - 1) { const nextSamplePTS = inputSamples[i + 1].pts; const currentSamplePTS = inputSamples[i].pts; const currentOrder = nextSamplePTS <= currentSamplePTS; const prevOrder = nextSamplePTS <= prevPTS; isPTSOrderRetained = currentOrder == prevOrder; } } } this.log(`Video: Initial PTS/DTS adjusted: ${toMsFromMpegTsClock(firstPTS, true)}/${toMsFromMpegTsClock(firstDTS, true)}, delta: ${toMsFromMpegTsClock(delta, true)} ms`); } } } firstDTS = Math.max(0, firstDTS); let nbNalu = 0; let naluLen = 0; let dtsStep = firstDTS; for (let i = 0; i < nbSamples; i++) { const sample = inputSamples[i]; const units = sample.units; const 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; if (sample.dts < dtsStep) { sample.dts = dtsStep; dtsStep += averageSampleDuration / 4 | 0 || 1; } else { dtsStep = sample.dts; } minPTS = Math.min(sample.pts, minPTS); maxPTS = Math.max(sample.pts, maxPTS); } lastDTS = inputSamples[nbSamples - 1].dts; const mdatSize = naluLen + 4 * nbNalu + 8; let mdat; try { mdat = new Uint8Array(mdatSize); } catch (err) { this.observer.emit(Events.ERROR, Events.ERROR, { type: ErrorTypes.MUX_ERROR, details: ErrorDetails.REMUX_ALLOC_ERROR, fatal: false, error: err, bytes: mdatSize, reason: `fail allocating video mdat ${mdatSize}` }); return; } const view2 = new DataView(mdat.buffer); view2.setUint32(0, mdatSize); mdat.set(MP4.types.mdat, 4); let stretchedLastFrame = false; let minDtsDelta = Number.POSITIVE_INFINITY; let minPtsDelta = Number.POSITIVE_INFINITY; let maxDtsDelta = Number.NEGATIVE_INFINITY; let maxPtsDelta = Number.NEGATIVE_INFINITY; for (let i = 0; i < nbSamples; i++) { const VideoSample = inputSamples[i]; const VideoSampleUnits = VideoSample.units; let mp4SampleLength = 0; for (let j = 0, nbUnits = VideoSampleUnits.length; j < nbUnits; j++) { const unit = VideoSampleUnits[j]; const unitData = unit.data; const unitDataLen = unit.data.byteLength; view2.setUint32(offset, unitDataLen); offset += 4; mdat.set(unitData, offset); offset += unitDataLen; mp4SampleLength += 4 + unitDataLen; } let ptsDelta; if (i < nbSamples - 1) { mp4SampleDuration = inputSamples[i + 1].dts - VideoSample.dts; ptsDelta = inputSamples[i + 1].pts - VideoSample.pts; } else { const config = this.config; const lastFrameDuration = i > 0 ? VideoSample.dts - inputSamples[i - 1].dts : averageSampleDuration; ptsDelta = i > 0 ? VideoSample.pts - inputSamples[i - 1].pts : averageSampleDuration; if (config.stretchShortVideoTrack && this.nextAudioTs !== null) { const gapTolerance = Math.floor(config.maxBufferHole * timeScale); const deltaToFrameEnd = (audioTrackLength ? minPTS + audioTrackLength * timeScale : this.nextAudioTs + initTime) - VideoSample.pts; if (deltaToFrameEnd > gapTolerance) { mp4SampleDuration = deltaToFrameEnd - lastFrameDuration; if (mp4SampleDuration < 0) { mp4SampleDuration = lastFrameDuration; } else { stretchedLastFrame = true; } this.log(`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; } } const compositionTimeOffset = Math.round(VideoSample.pts - VideoSample.dts); minDtsDelta = Math.min(minDtsDelta, mp4SampleDuration); maxDtsDelta = Math.max(maxDtsDelta, mp4SampleDuration); minPtsDelta = Math.min(minPtsDelta, ptsDelta); maxPtsDelta = Math.max(maxPtsDelta, ptsDelta); outputSamples.push(createMp4Sample(VideoSample.key, mp4SampleDuration, mp4SampleLength, compositionTimeOffset)); } if (outputSamples.length) { if (chromeVersion) { if (chromeVersion < 70) { const flags = outputSamples[0].flags; flags.dependsOn = 2; flags.isNonSync = 0; } } else if (safariWebkitVersion) { if (maxPtsDelta - minPtsDelta < maxDtsDelta - minDtsDelta && averageSampleDuration / maxDtsDelta < 0.025 && outputSamples[0].cts === 0) { this.warn("Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration."); let dts = firstDTS; for (let i = 0, len = outputSamples.length; i < len; i++) { const nextDts = dts + outputSamples[i].duration; const pts = dts + outputSamples[i].cts; if (i < len - 1) { const nextPts = nextDts + outputSamples[i + 1].cts; outputSamples[i].duration = nextPts - pts; } else { outputSamples[i].duration = i ? outputSamples[i - 1].duration : averageSampleDuration; } outputSamples[i].cts = 0; dts = nextDts; } } } } mp4SampleDuration = stretchedLastFrame || !mp4SampleDuration ? averageSampleDuration : mp4SampleDuration; const endDTS = lastDTS + mp4SampleDuration; this.nextVideoTs = nextVideoTs = endDTS - initTime; this.videoSampleDuration = mp4SampleDuration; this.isVideoContiguous = true; const moof = MP4.moof(track.sequenceNumber++, firstDTS, _extends(track, { samples: outputSamples })); const type = "video"; const data = { data1: moof, data2: mdat, startPTS: (minPTS - initTime) / timeScale, endPTS: (maxPTS + mp4SampleDuration - initTime) / timeScale, startDTS: (firstDTS - initTime) / timeScale, endDTS: nextVideoTs / timeScale, type, hasAudio: false, hasVideo: true, nb: outputSamples.length, dropped: track.dropped }; track.samples = []; track.dropped = 0; return data; } getSamplesPerFrame(track) { switch (track.segmentCodec) { case "mp3": return MPEG_AUDIO_SAMPLE_PER_FRAME; case "ac3": return AC3_SAMPLES_PER_FRAME; default: return AAC_SAMPLES_PER_FRAME; } } remuxAudio(track, timeOffset, contiguous, accurateTimeOffset, videoTimeOffset) { const inputTimeScale = track.inputTimeScale; const mp4timeScale = track.samplerate ? track.samplerate : inputTimeScale; const scaleFactor = inputTimeScale / mp4timeScale; const mp4SampleDuration = this.getSamplesPerFrame(track); const inputSampleDuration = mp4SampleDuration * scaleFactor; const initPTS = this._initPTS; const rawMPEG = track.segmentCodec === "mp3" && this.typeSupported.mpeg; const outputSamples = []; const alignedWithVideo = videoTimeOffset !== void 0; let inputSamples = track.samples; let offset = rawMPEG ? 0 : 8; let nextAudioTs = this.nextAudioTs || -1; const initTime = initPTS.baseTime * inputTimeScale / initPTS.timescale; const timeOffsetMpegTS = initTime + timeOffset * inputTimeScale; this.isAudioContiguous = contiguous = contiguous || inputSamples.length && nextAudioTs > 0 && (accurateTimeOffset && Math.abs(timeOffsetMpegTS - (nextAudioTs + initTime)) < 9e3 || Math.abs(normalizePts(inputSamples[0].pts, timeOffsetMpegTS) - (nextAudioTs + initTime)) < 20 * inputSampleDuration); inputSamples.forEach(function(sample) { sample.pts = normalizePts(sample.pts, timeOffsetMpegTS); }); if (!contiguous || nextAudioTs < 0) { inputSamples = inputSamples.filter((sample) => sample.pts >= 0); if (!inputSamples.length) { return; } if (videoTimeOffset === 0) { nextAudioTs = 0; } else if (accurateTimeOffset && !alignedWithVideo) { nextAudioTs = Math.max(0, timeOffsetMpegTS - initTime); } else { nextAudioTs = inputSamples[0].pts - initTime; } } if (track.segmentCodec === "aac") { const maxAudioFramesDrift = this.config.maxAudioFramesDrift; for (let i = 0, nextPts = nextAudioTs + initTime; i < inputSamples.length; i++) { const sample = inputSamples[i]; const pts = sample.pts; const delta = pts - nextPts; const duration = Math.abs(1e3 * delta / inputTimeScale); if (delta <= -maxAudioFramesDrift * inputSampleDuration && alignedWithVideo) { if (i === 0) { this.warn(`Audio frame @ ${(pts / inputTimeScale).toFixed(3)}s overlaps marker by ${Math.round(1e3 * delta / inputTimeScale)} ms.`); this.nextAudioTs = nextAudioTs = pts - initTime; nextPts = pts; } } else if (delta >= maxAudioFramesDrift * inputSampleDuration && duration < MAX_SILENT_FRAME_DURATION && alignedWithVideo) { let missing = Math.round(delta / inputSampleDuration); nextPts = pts - missing * inputSampleDuration; while (nextPts < 0 && missing && inputSampleDuration) { missing--; nextPts += inputSampleDuration; } if (i === 0) { this.nextAudioTs = nextAudioTs = nextPts - initTime; } this.warn(`Injecting ${missing} audio frames @ ${((nextPts - initTime) / inputTimeScale).toFixed(3)}s due to ${Math.round(1e3 * delta / inputTimeScale)} ms gap.`); for (let j = 0; j < missing; j++) { let fillFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount); if (!fillFrame) { this.log("Unable to get silent frame for given audio codec; duplicating last frame instead."); fillFrame = sample.unit.subarray(); } inputSamples.splice(i, 0, { unit: fillFrame, pts: nextPts }); nextPts += inputSampleDuration; i++; } } sample.pts = nextPts; nextPts += inputSampleDuration; } } let firstPTS = null; let lastPTS = null; let mdat; let mdatSize = 0; let sampleLength = inputSamples.length; while (sampleLength--) { mdatSize += inputSamples[sampleLength].unit.byteLength; } for (let j = 0, _nbSamples = inputSamples.length; j < _nbSamples; j++) { const audioSample = inputSamples[j]; const unit = audioSample.unit; let pts = audioSample.pts; if (lastPTS !== null) { const prevSample = outputSamples[j - 1]; prevSample.duration = Math.round((pts - lastPTS) / scaleFactor); } else { if (contiguous && track.segmentCodec === "aac") { pts = nextAudioTs + initTime; } firstPTS = pts; if (mdatSize > 0) { mdatSize += offset; try { mdat = new Uint8Array(mdatSize); } catch (err) { this.observer.emit(Events.ERROR, Events.ERROR, { type: ErrorTypes.MUX_ERROR, details: ErrorDetails.REMUX_ALLOC_ERROR, fatal: false, error: err, bytes: mdatSize, reason: `fail allocating audio mdat ${mdatSize}` }); return; } if (!rawMPEG) { const view2 = new DataView(mdat.buffer); view2.setUint32(0, mdatSize); mdat.set(MP4.types.mdat, 4); } } else { return; } } mdat.set(unit, offset); const unitLen = unit.byteLength; offset += unitLen; outputSamples.push(createMp4Sample(true, mp4SampleDuration, unitLen, 0)); lastPTS = pts; } const nbSamples = outputSamples.length; if (!nbSamples) { return; } const lastSample = outputSamples[outputSamples.length - 1]; nextAudioTs = lastPTS - initTime; this.nextAudioTs = nextAudioTs + scaleFactor * lastSample.duration; const moof = rawMPEG ? new Uint8Array(0) : MP4.moof(track.sequenceNumber++, firstPTS / scaleFactor, _extends({}, track, { samples: outputSamples })); track.samples = []; const start = (firstPTS - initTime) / inputTimeScale; const end = nextAudioTs / inputTimeScale; const type = "audio"; const audioData = { data1: moof, data2: mdat, startPTS: start, endPTS: end, startDTS: start, endDTS: end, type, hasAudio: true, hasVideo: false, nb: nbSamples }; this.isAudioContiguous = true; return audioData; } }; function normalizePts(value, reference) { let offset; if (reference === null) { return value; } if (reference < value) { offset = -8589934592; } else { offset = 8589934592; } while (Math.abs(value - reference) > 4294967296) { value += offset; } return value; } function findKeyframeIndex(samples) { for (let i = 0; i < samples.length; i++) { if (samples[i].key) { return i; } } return -1; } function flushTextTrackMetadataCueSamples(track, timeOffset, initPTS, initDTS) { const length = track.samples.length; if (!length) { return; } const inputTimeScale = track.inputTimeScale; for (let index = 0; index < length; index++) { const sample = track.samples[index]; sample.pts = normalizePts(sample.pts - initPTS.baseTime * inputTimeScale / initPTS.timescale, timeOffset * inputTimeScale) / inputTimeScale; sample.dts = normalizePts(sample.dts - initDTS.baseTime * inputTimeScale / initDTS.timescale, timeOffset * inputTimeScale) / inputTimeScale; } const samples = track.samples; track.samples = []; return { samples }; } function flushTextTrackUserdataCueSamples(track, timeOffset, initPTS) { const length = track.samples.length; if (!length) { return; } const inputTimeScale = track.inputTimeScale; for (let index = 0; index < length; index++) { const sample = track.samples[index]; sample.pts = normalizePts(sample.pts - initPTS.baseTime * inputTimeScale / initPTS.timescale, timeOffset * inputTimeScale) / inputTimeScale; } track.samples.sort((a, b) => a.pts - b.pts); const samples = track.samples; track.samples = []; return { samples }; } var PassThroughRemuxer = class extends Logger { constructor(observer, config, typeSupported, logger2) { super("passthrough-remuxer", logger2); this.emitInitSegment = false; this.audioCodec = void 0; this.videoCodec = void 0; this.initData = void 0; this.initPTS = null; this.initTracks = void 0; this.lastEndTime = null; this.isVideoContiguous = false; } destroy() { } resetTimeStamp(defaultInitPTS) { this.lastEndTime = null; const initPTS = this.initPTS; if (initPTS && defaultInitPTS) { if (initPTS.baseTime === defaultInitPTS.baseTime && initPTS.timescale === defaultInitPTS.timescale) { return; } } this.initPTS = defaultInitPTS; } resetNextTimestamp() { this.isVideoContiguous = false; this.lastEndTime = null; } resetInitSegment(initSegment, audioCodec, videoCodec, decryptdata) { this.audioCodec = audioCodec; this.videoCodec = videoCodec; this.generateInitSegment(patchEncyptionData(initSegment, decryptdata)); this.emitInitSegment = true; } generateInitSegment(initSegment) { let { audioCodec, videoCodec } = this; if (!(initSegment != null && initSegment.byteLength)) { this.initTracks = void 0; this.initData = void 0; return; } const initData = this.initData = parseInitSegment(initSegment); if (initData.audio) { audioCodec = getParsedTrackCodec(initData.audio, ElementaryStreamTypes.AUDIO, this); } if (initData.video) { videoCodec = getParsedTrackCodec(initData.video, ElementaryStreamTypes.VIDEO, this); } const tracks = {}; if (initData.audio && initData.video) { tracks.audiovideo = { container: "video/mp4", codec: audioCodec + "," + videoCodec, supplemental: initData.video.supplemental, initSegment, id: "main" }; } else if (initData.audio) { tracks.audio = { container: "audio/mp4", codec: audioCodec, initSegment, id: "audio" }; } else if (initData.video) { tracks.video = { container: "video/mp4", codec: videoCodec, supplemental: initData.video.supplemental, initSegment, id: "main" }; } else { this.warn("initSegment does not contain moov or trak boxes."); } this.initTracks = tracks; } remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset) { var _initData, _initData2; let { initPTS, lastEndTime } = this; const result = { audio: void 0, video: void 0, text: textTrack, id3: id3Track, initSegment: void 0 }; if (!isFiniteNumber(lastEndTime)) { lastEndTime = this.lastEndTime = timeOffset || 0; } const data = videoTrack.samples; if (!(data != null && data.length)) { return result; } const initSegment = { initPTS: void 0, timescale: void 0, trackId: void 0 }; let initData = this.initData; if (!((_initData = initData) != null && _initData.length)) { this.generateInitSegment(data); initData = this.initData; } if (!((_initData2 = initData) != null && _initData2.length)) { this.warn("Failed to generate initSegment."); return result; } if (this.emitInitSegment) { initSegment.tracks = this.initTracks; this.emitInitSegment = false; } const trackSampleData = getSampleData(data, initData, this); const audioSampleTimestamps = initData.audio ? trackSampleData[initData.audio.id] : null; const videoSampleTimestamps = initData.video ? trackSampleData[initData.video.id] : null; const videoStartTime = toStartEndOrDefault(videoSampleTimestamps, Infinity); const audioStartTime = toStartEndOrDefault(audioSampleTimestamps, Infinity); const videoEndTime = toStartEndOrDefault(videoSampleTimestamps, 0, true); const audioEndTime = toStartEndOrDefault(audioSampleTimestamps, 0, true); let baseOffsetSamples; let decodeTime = timeOffset; let duration = 0; if (audioSampleTimestamps && (!videoSampleTimestamps || !initPTS && audioStartTime < videoStartTime || initPTS && initPTS.trackId === initData.audio.id)) { initSegment.trackId = initData.audio.id; baseOffsetSamples = audioSampleTimestamps; duration = audioEndTime - audioStartTime; } else if (videoSampleTimestamps) { initSegment.trackId = initData.video.id; baseOffsetSamples = videoSampleTimestamps; duration = videoEndTime - videoStartTime; } if (baseOffsetSamples) { const timescale = baseOffsetSamples.timescale; decodeTime = baseOffsetSamples.start / timescale; initSegment.initPTS = baseOffsetSamples.start - timeOffset * timescale; initSegment.timescale = timescale; if (!initPTS) { this.initPTS = initPTS = { baseTime: initSegment.initPTS, timescale, trackId: initSegment.trackId }; } } if ((accurateTimeOffset || !initPTS) && (isInvalidInitPts(initPTS, decodeTime, timeOffset, duration) || initSegment.timescale !== initPTS.timescale)) { initSegment.initPTS = decodeTime - timeOffset; initSegment.timescale = 1; if (initPTS && initPTS.timescale === 1) { this.warn(`Adjusting initPTS @${timeOffset} from ${initPTS.baseTime / initPTS.timescale} to ${initSegment.initPTS}`); } this.initPTS = initPTS = { baseTime: initSegment.initPTS, timescale: 1 }; } const startTime = audioTrack ? decodeTime - initPTS.baseTime / initPTS.timescale : lastEndTime; const endTime = startTime + duration; if (duration > 0) { this.lastEndTime = endTime; } else { this.warn("Duration parsed from mp4 should be greater than zero"); this.resetNextTimestamp(); } const hasAudio = !!initData.audio; const hasVideo = !!initData.video; let type = ""; if (hasAudio) { type += "audio"; } if (hasVideo) { type += "video"; } const track = { data1: data, startPTS: startTime, startDTS: startTime, endPTS: endTime, endDTS: endTime, type, hasAudio, hasVideo, nb: 1, dropped: 0 }; result.audio = hasAudio && !hasVideo ? track : void 0; result.video = hasVideo ? track : void 0; const videoSampleCount = videoSampleTimestamps == null ? void 0 : videoSampleTimestamps.sampleCount; if (videoSampleCount) { const firstKeyFrame = videoSampleTimestamps.keyFrameIndex; const independent = firstKeyFrame !== -1; track.nb = videoSampleCount; track.dropped = firstKeyFrame === 0 || this.isVideoContiguous ? 0 : independent ? firstKeyFrame : videoSampleCount; track.independent = independent; track.firstKeyFrame = firstKeyFrame; if (independent && videoSampleTimestamps.keyFrameStart) { track.firstKeyFramePTS = (videoSampleTimestamps.keyFrameStart - initPTS.baseTime) / initPTS.timescale; } if (!this.isVideoContiguous) { result.independent = independent; } this.isVideoContiguous || (this.isVideoContiguous = independent); if (track.dropped) { this.warn(`fmp4 does not start with IDR: firstIDR ${firstKeyFrame}/${videoSampleCount} dropped: ${track.dropped} start: ${track.firstKeyFramePTS || "NA"}`); } } result.initSegment = initSegment; result.id3 = flushTextTrackMetadataCueSamples(id3Track, timeOffset, initPTS, initPTS); if (textTrack.samples.length) { result.text = flushTextTrackUserdataCueSamples(textTrack, timeOffset, initPTS); } return result; } }; function toStartEndOrDefault(trackTimes, defaultValue, end = false) { return (trackTimes == null ? void 0 : trackTimes.start) !== void 0 ? (trackTimes.start + (end ? trackTimes.duration : 0)) / trackTimes.timescale : defaultValue; } function isInvalidInitPts(initPTS, startDTS, timeOffset, duration) { if (initPTS === null) { return true; } const minDuration = Math.max(duration, 1); const startTime = startDTS - initPTS.baseTime / initPTS.timescale; return Math.abs(startTime - timeOffset) > minDuration; } function getParsedTrackCodec(track, type, logger2) { const parsedCodec = track == null ? void 0 : track.codec; if (parsedCodec && parsedCodec.length > 4) { return parsedCodec; } if (type === ElementaryStreamTypes.AUDIO) { if (parsedCodec === "ec-3" || parsedCodec === "ac-3" || parsedCodec === "alac") { return parsedCodec; } if (parsedCodec === "fLaC" || parsedCodec === "Opus") { const preferManagedMediaSource = false; return getCodecCompatibleName(parsedCodec, preferManagedMediaSource); } logger2.warn(`Unhandled audio codec "${parsedCodec}" in mp4 MAP`); return parsedCodec || "mp4a"; } logger2.warn(`Unhandled video codec "${parsedCodec}" in mp4 MAP`); return parsedCodec || "avc1"; } var now; try { now = self.performance.now.bind(self.performance); } catch (err) { now = Date.now; } var muxConfig = [{ demux: MP4Demuxer, remux: PassThroughRemuxer }, { demux: TSDemuxer, remux: MP4Remuxer }, { demux: AACDemuxer, remux: MP4Remuxer }, { demux: MP3Demuxer, remux: MP4Remuxer }]; { muxConfig.splice(2, 0, { demux: AC3Demuxer, remux: MP4Remuxer }); } var Transmuxer = class { constructor(observer, typeSupported, config, vendor, id, logger2) { this.asyncResult = false; this.logger = void 0; this.observer = void 0; this.typeSupported = void 0; this.config = void 0; this.id = void 0; this.demuxer = void 0; this.remuxer = void 0; this.decrypter = void 0; this.probe = void 0; this.decryptionPromise = null; this.transmuxConfig = void 0; this.currentTransmuxState = void 0; this.observer = observer; this.typeSupported = typeSupported; this.config = config; this.id = id; this.logger = logger2; } configure(transmuxConfig) { this.transmuxConfig = transmuxConfig; if (this.decrypter) { this.decrypter.reset(); } } push(data, decryptdata, chunkMeta, state) { const stats = chunkMeta.transmuxing; stats.executeStart = now(); let uintData = new Uint8Array(data); const { currentTransmuxState, transmuxConfig } = this; if (state) { this.currentTransmuxState = state; } const { contiguous, discontinuity, trackSwitch, accurateTimeOffset, timeOffset, initSegmentChange } = state || currentTransmuxState; const { audioCodec, videoCodec, defaultInitPts, duration, initSegmentData } = transmuxConfig; const keyData = getEncryptionType(uintData, decryptdata); if (keyData && isFullSegmentEncryption(keyData.method)) { const decrypter = this.getDecrypter(); const aesMode = getAesModeFromFullSegmentMethod(keyData.method); if (decrypter.isSync()) { let decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode); const loadingParts = chunkMeta.part > -1; if (loadingParts) { const _data = decrypter.flush(); decryptedData = _data ? _data.buffer : _data; } if (!decryptedData) { stats.executeEnd = now(); return emptyResult(chunkMeta); } uintData = new Uint8Array(decryptedData); } else { this.asyncResult = true; this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode).then((decryptedData) => { const result2 = this.push(decryptedData, null, chunkMeta); this.decryptionPromise = null; return result2; }); return this.decryptionPromise; } } const resetMuxers = this.needsProbing(discontinuity, trackSwitch); if (resetMuxers) { const error = this.configureTransmuxer(uintData); if (error) { this.logger.warn(`[transmuxer] ${error.message}`); this.observer.emit(Events.ERROR, Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, fatal: false, error, reason: error.message }); stats.executeEnd = now(); return emptyResult(chunkMeta); } } if (discontinuity || trackSwitch || initSegmentChange || resetMuxers) { this.resetInitSegment(initSegmentData, audioCodec, videoCodec, duration, decryptdata); } if (discontinuity || initSegmentChange || resetMuxers) { this.resetInitialTimestamp(defaultInitPts); } if (!contiguous) { this.resetContiguity(); } const result = this.transmux(uintData, keyData, timeOffset, accurateTimeOffset, chunkMeta); this.asyncResult = isPromise(result); const currentState = this.currentTransmuxState; currentState.contiguous = true; currentState.discontinuity = false; currentState.trackSwitch = false; stats.executeEnd = now(); return result; } flush(chunkMeta) { const stats = chunkMeta.transmuxing; stats.executeStart = now(); const { decrypter, currentTransmuxState, decryptionPromise } = this; if (decryptionPromise) { this.asyncResult = true; return decryptionPromise.then(() => { return this.flush(chunkMeta); }); } const transmuxResults = []; const { timeOffset } = currentTransmuxState; if (decrypter) { const decryptedData = decrypter.flush(); if (decryptedData) { transmuxResults.push(this.push(decryptedData.buffer, null, chunkMeta)); } } const { demuxer, remuxer } = this; if (!demuxer || !remuxer) { stats.executeEnd = now(); const emptyResults = [emptyResult(chunkMeta)]; if (this.asyncResult) { return Promise.resolve(emptyResults); } return emptyResults; } const demuxResultOrPromise = demuxer.flush(timeOffset); if (isPromise(demuxResultOrPromise)) { this.asyncResult = true; return demuxResultOrPromise.then((demuxResult) => { this.flushRemux(transmuxResults, demuxResult, chunkMeta); return transmuxResults; }); } this.flushRemux(transmuxResults, demuxResultOrPromise, chunkMeta); if (this.asyncResult) { return Promise.resolve(transmuxResults); } return transmuxResults; } flushRemux(transmuxResults, demuxResult, chunkMeta) { const { audioTrack, videoTrack, id3Track, textTrack } = demuxResult; const { accurateTimeOffset, timeOffset } = this.currentTransmuxState; this.logger.log(`[transmuxer.ts]: Flushed ${this.id} sn: ${chunkMeta.sn}${chunkMeta.part > -1 ? " part: " + chunkMeta.part : ""} of ${this.id === PlaylistLevelType.MAIN ? "level" : "track"} ${chunkMeta.level}`); const remuxResult = this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset, true, this.id); transmuxResults.push({ remuxResult, chunkMeta }); chunkMeta.transmuxing.executeEnd = now(); } resetInitialTimestamp(defaultInitPts) { const { demuxer, remuxer } = this; if (!demuxer || !remuxer) { return; } demuxer.resetTimeStamp(defaultInitPts); remuxer.resetTimeStamp(defaultInitPts); } resetContiguity() { const { demuxer, remuxer } = this; if (!demuxer || !remuxer) { return; } demuxer.resetContiguity(); remuxer.resetNextTimestamp(); } resetInitSegment(initSegmentData, audioCodec, videoCodec, trackDuration, decryptdata) { const { demuxer, remuxer } = this; if (!demuxer || !remuxer) { return; } demuxer.resetInitSegment(initSegmentData, audioCodec, videoCodec, trackDuration); remuxer.resetInitSegment(initSegmentData, audioCodec, videoCodec, decryptdata); } destroy() { if (this.demuxer) { this.demuxer.destroy(); this.demuxer = void 0; } if (this.remuxer) { this.remuxer.destroy(); this.remuxer = void 0; } } transmux(data, keyData, timeOffset, accurateTimeOffset, chunkMeta) { let result; if (keyData && keyData.method === "SAMPLE-AES") { result = this.transmuxSampleAes(data, keyData, timeOffset, accurateTimeOffset, chunkMeta); } else { result = this.transmuxUnencrypted(data, timeOffset, accurateTimeOffset, chunkMeta); } return result; } transmuxUnencrypted(data, timeOffset, accurateTimeOffset, chunkMeta) { const { audioTrack, videoTrack, id3Track, textTrack } = this.demuxer.demux(data, timeOffset, false, !this.config.progressive); const remuxResult = this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset, false, this.id); return { remuxResult, chunkMeta }; } transmuxSampleAes(data, decryptData, timeOffset, accurateTimeOffset, chunkMeta) { return this.demuxer.demuxSampleAes(data, decryptData, timeOffset).then((demuxResult) => { const remuxResult = this.remuxer.remux(demuxResult.audioTrack, demuxResult.videoTrack, demuxResult.id3Track, demuxResult.textTrack, timeOffset, accurateTimeOffset, false, this.id); return { remuxResult, chunkMeta }; }); } configureTransmuxer(data) { const { config, observer, typeSupported } = this; let mux; for (let i = 0, len = muxConfig.length; i < len; i++) { var _muxConfig$i$demux; if ((_muxConfig$i$demux = muxConfig[i].demux) != null && _muxConfig$i$demux.probe(data, this.logger)) { mux = muxConfig[i]; break; } } if (!mux) { return new Error("Failed to find demuxer by probing fragment data"); } const demuxer = this.demuxer; const remuxer = this.remuxer; const Remuxer = mux.remux; const Demuxer = mux.demux; if (!remuxer || !(remuxer instanceof Remuxer)) { this.remuxer = new Remuxer(observer, config, typeSupported, this.logger); } if (!demuxer || !(demuxer instanceof Demuxer)) { this.demuxer = new Demuxer(observer, config, typeSupported, this.logger); this.probe = Demuxer.probe; } } needsProbing(discontinuity, trackSwitch) { return !this.demuxer || !this.remuxer || discontinuity || trackSwitch; } getDecrypter() { let decrypter = this.decrypter; if (!decrypter) { decrypter = this.decrypter = new Decrypter(this.config); } return decrypter; } }; function getEncryptionType(data, decryptData) { let encryptionType = null; if (data.byteLength > 0 && (decryptData == null ? void 0 : decryptData.key) != null && decryptData.iv !== null && decryptData.method != null) { encryptionType = decryptData; } return encryptionType; } var emptyResult = (chunkMeta) => ({ remuxResult: {}, chunkMeta }); function isPromise(p) { return "then" in p && p.then instanceof Function; } var TransmuxConfig = class { constructor(audioCodec, videoCodec, initSegmentData, duration, defaultInitPts) { this.audioCodec = void 0; this.videoCodec = void 0; this.initSegmentData = void 0; this.duration = void 0; this.defaultInitPts = void 0; this.audioCodec = audioCodec; this.videoCodec = videoCodec; this.initSegmentData = initSegmentData; this.duration = duration; this.defaultInitPts = defaultInitPts || null; } }; var TransmuxState = class { constructor(discontinuity, contiguous, accurateTimeOffset, trackSwitch, timeOffset, initSegmentChange) { this.discontinuity = void 0; this.contiguous = void 0; this.accurateTimeOffset = void 0; this.trackSwitch = void 0; this.timeOffset = void 0; this.initSegmentChange = void 0; this.discontinuity = discontinuity; this.contiguous = contiguous; this.accurateTimeOffset = accurateTimeOffset; this.trackSwitch = trackSwitch; this.timeOffset = timeOffset; this.initSegmentChange = initSegmentChange; } }; var transmuxerInstanceCount = 0; var TransmuxerInterface = class { constructor(_hls, id, onTransmuxComplete, onFlush) { this.error = null; this.hls = void 0; this.id = void 0; this.instanceNo = transmuxerInstanceCount++; this.observer = void 0; this.frag = null; this.part = null; this.useWorker = void 0; this.workerContext = null; this.transmuxer = null; this.onTransmuxComplete = void 0; this.onFlush = void 0; this.onWorkerMessage = (event) => { const data = event.data; const hls = this.hls; if (!hls || !(data != null && data.event) || data.instanceNo !== this.instanceNo) { return; } switch (data.event) { case "init": { var _this$workerContext; const objectURL = (_this$workerContext = this.workerContext) == null ? void 0 : _this$workerContext.objectURL; if (objectURL) { self.URL.revokeObjectURL(objectURL); } break; } case "transmuxComplete": { this.handleTransmuxComplete(data.data); break; } case "flush": { this.onFlush(data.data); break; } case "workerLog": { if (hls.logger[data.data.logType]) { hls.logger[data.data.logType](data.data.message); } break; } default: { data.data = data.data || {}; data.data.frag = this.frag; data.data.part = this.part; data.data.id = this.id; hls.trigger(data.event, data.data); break; } } }; this.onWorkerError = (event) => { if (!this.hls) { return; } const error = new Error(`${event.message} (${event.filename}:${event.lineno})`); this.hls.config.enableWorker = false; this.hls.logger.warn(`Error in "${this.id}" Web Worker, fallback to inline`); this.hls.trigger(Events.ERROR, { type: ErrorTypes.OTHER_ERROR, details: ErrorDetails.INTERNAL_EXCEPTION, fatal: false, event: "demuxerWorker", error }); }; const config = _hls.config; this.hls = _hls; this.id = id; this.useWorker = !!config.enableWorker; this.onTransmuxComplete = onTransmuxComplete; this.onFlush = onFlush; const forwardMessage = (ev, data) => { data = data || {}; data.frag = this.frag || void 0; if (ev === Events.ERROR) { data = data; data.parent = this.id; data.part = this.part; this.error = data.error; } this.hls.trigger(ev, data); }; this.observer = new EventEmitter(); this.observer.on(Events.FRAG_DECRYPTED, forwardMessage); this.observer.on(Events.ERROR, forwardMessage); const m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource); if (this.useWorker && typeof Worker !== "undefined") { const logger2 = this.hls.logger; const canCreateWorker = config.workerPath || hasUMDWorker(); if (canCreateWorker) { try { if (config.workerPath) { logger2.log(`loading Web Worker ${config.workerPath} for "${id}"`); this.workerContext = loadWorker(config.workerPath); } else { logger2.log(`injecting Web Worker for "${id}"`); this.workerContext = injectWorker(); } const { worker } = this.workerContext; worker.addEventListener("message", this.onWorkerMessage); worker.addEventListener("error", this.onWorkerError); worker.postMessage({ instanceNo: this.instanceNo, cmd: "init", typeSupported: m2tsTypeSupported, id, config: stringify(config) }); } catch (err) { logger2.warn(`Error setting up "${id}" Web Worker, fallback to inline`, err); this.terminateWorker(); this.error = null; this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, "", id, _hls.logger); } return; } } this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, "", id, _hls.logger); } reset() { this.frag = null; this.part = null; if (this.workerContext) { const instanceNo = this.instanceNo; this.instanceNo = transmuxerInstanceCount++; const config = this.hls.config; const m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource); this.workerContext.worker.postMessage({ instanceNo: this.instanceNo, cmd: "reset", resetNo: instanceNo, typeSupported: m2tsTypeSupported, id: this.id, config: stringify(config) }); } } terminateWorker() { if (this.workerContext) { const { worker } = this.workerContext; this.workerContext = null; worker.removeEventListener("message", this.onWorkerMessage); worker.removeEventListener("error", this.onWorkerError); removeWorkerFromStore(this.hls.config.workerPath); } } destroy() { if (this.workerContext) { this.terminateWorker(); this.onWorkerMessage = this.onWorkerError = null; } else { const transmuxer = this.transmuxer; if (transmuxer) { transmuxer.destroy(); this.transmuxer = null; } } const observer = this.observer; if (observer) { observer.removeAllListeners(); } this.frag = null; this.part = null; this.observer = null; this.hls = null; } push(data, initSegmentData, audioCodec, videoCodec, frag, part, duration, accurateTimeOffset, chunkMeta, defaultInitPTS) { var _frag$initSegment, _lastFrag$initSegment; chunkMeta.transmuxing.start = self.performance.now(); const { instanceNo, transmuxer } = this; const timeOffset = part ? part.start : frag.start; const decryptdata = frag.decryptdata; const lastFrag = this.frag; const discontinuity = !(lastFrag && frag.cc === lastFrag.cc); const trackSwitch = !(lastFrag && chunkMeta.level === lastFrag.level); const snDiff = lastFrag ? chunkMeta.sn - lastFrag.sn : -1; const partDiff = this.part ? chunkMeta.part - this.part.index : -1; const progressive = snDiff === 0 && chunkMeta.id > 1 && chunkMeta.id === (lastFrag == null ? void 0 : lastFrag.stats.chunkCount); const contiguous = !trackSwitch && (snDiff === 1 || snDiff === 0 && (partDiff === 1 || progressive && partDiff <= 0)); const now2 = self.performance.now(); if (trackSwitch || snDiff || frag.stats.parsing.start === 0) { frag.stats.parsing.start = now2; } if (part && (partDiff || !contiguous)) { part.stats.parsing.start = now2; } const initSegmentChange = !(lastFrag && ((_frag$initSegment = frag.initSegment) == null ? void 0 : _frag$initSegment.url) === ((_lastFrag$initSegment = lastFrag.initSegment) == null ? void 0 : _lastFrag$initSegment.url)); const state = new TransmuxState(discontinuity, contiguous, accurateTimeOffset, trackSwitch, timeOffset, initSegmentChange); if (!contiguous || discontinuity || initSegmentChange) { this.hls.logger.log(`[transmuxer-interface]: Starting new transmux session for ${frag.type} sn: ${chunkMeta.sn}${chunkMeta.part > -1 ? " part: " + chunkMeta.part : ""} ${this.id === PlaylistLevelType.MAIN ? "level" : "track"}: ${chunkMeta.level} id: ${chunkMeta.id} discontinuity: ${discontinuity} trackSwitch: ${trackSwitch} contiguous: ${contiguous} accurateTimeOffset: ${accurateTimeOffset} timeOffset: ${timeOffset} initSegmentChange: ${initSegmentChange}`); const config = new TransmuxConfig(audioCodec, videoCodec, initSegmentData, duration, defaultInitPTS); this.configureTransmuxer(config); } this.frag = frag; this.part = part; if (this.workerContext) { this.workerContext.worker.postMessage({ instanceNo, cmd: "demux", data, decryptdata, chunkMeta, state }, data instanceof ArrayBuffer ? [data] : []); } else if (transmuxer) { const transmuxResult = transmuxer.push(data, decryptdata, chunkMeta, state); if (isPromise(transmuxResult)) { transmuxResult.then((data2) => { this.handleTransmuxComplete(data2); }).catch((error) => { this.transmuxerError(error, chunkMeta, "transmuxer-interface push error"); }); } else { this.handleTransmuxComplete(transmuxResult); } } } flush(chunkMeta) { chunkMeta.transmuxing.start = self.performance.now(); const { instanceNo, transmuxer } = this; if (this.workerContext) { this.workerContext.worker.postMessage({ instanceNo, cmd: "flush", chunkMeta }); } else if (transmuxer) { const transmuxResult = transmuxer.flush(chunkMeta); if (isPromise(transmuxResult)) { transmuxResult.then((data) => { this.handleFlushResult(data, chunkMeta); }).catch((error) => { this.transmuxerError(error, chunkMeta, "transmuxer-interface flush error"); }); } else { this.handleFlushResult(transmuxResult, chunkMeta); } } } transmuxerError(error, chunkMeta, reason) { if (!this.hls) { return; } this.error = error; this.hls.trigger(Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.FRAG_PARSING_ERROR, chunkMeta, frag: this.frag || void 0, part: this.part || void 0, fatal: false, error, err: error, reason }); } handleFlushResult(results, chunkMeta) { results.forEach((result) => { this.handleTransmuxComplete(result); }); this.onFlush(chunkMeta); } configureTransmuxer(config) { const { instanceNo, transmuxer } = this; if (this.workerContext) { this.workerContext.worker.postMessage({ instanceNo, cmd: "configure", config }); } else if (transmuxer) { transmuxer.configure(config); } } handleTransmuxComplete(result) { result.chunkMeta.transmuxing.end = self.performance.now(); this.onTransmuxComplete(result); } }; var TICK_INTERVAL$3 = 100; var AudioStreamController = class extends BaseStreamController { constructor(hls, fragmentTracker, keyLoader) { super(hls, fragmentTracker, keyLoader, "audio-stream-controller", PlaylistLevelType.AUDIO); this.mainAnchor = null; this.mainFragLoading = null; this.audioOnly = false; this.bufferedTrack = null; this.switchingTrack = null; this.trackId = -1; this.waitingData = null; this.mainDetails = null; this.flushing = false; this.bufferFlushed = false; this.cachedTrackLoadedData = null; this.registerListeners(); } onHandlerDestroying() { this.unregisterListeners(); super.onHandlerDestroying(); this.resetItem(); } resetItem() { this.mainDetails = this.mainAnchor = this.mainFragLoading = this.bufferedTrack = this.switchingTrack = this.waitingData = this.cachedTrackLoadedData = null; } registerListeners() { super.registerListeners(); const { hls } = this; hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this); hls.on(Events.AUDIO_TRACKS_UPDATED, this.onAudioTracksUpdated, this); hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this); hls.on(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this); hls.on(Events.BUFFER_RESET, this.onBufferReset, this); hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this); hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this); hls.on(Events.INIT_PTS_FOUND, this.onInitPtsFound, this); hls.on(Events.FRAG_LOADING, this.onFragLoading, this); hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); } unregisterListeners() { const { hls } = this; if (!hls) { return; } super.unregisterListeners(); hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this); hls.off(Events.AUDIO_TRACKS_UPDATED, this.onAudioTracksUpdated, this); hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this); hls.off(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this); hls.off(Events.BUFFER_RESET, this.onBufferReset, this); hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this); hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); hls.off(Events.BUFFER_FLUSHED, this.onBufferFlushed, this); hls.off(Events.INIT_PTS_FOUND, this.onInitPtsFound, this); hls.off(Events.FRAG_LOADING, this.onFragLoading, this); hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); } onInitPtsFound(event, { frag, id, initPTS, timescale }) { if (id === PlaylistLevelType.MAIN) { const cc = frag.cc; const inFlightFrag = this.fragCurrent; this.initPTS[cc] = { baseTime: initPTS, timescale }; this.log(`InitPTS for cc: ${cc} found from main: ${initPTS}/${timescale}`); this.mainAnchor = frag; if (this.state === State.WAITING_INIT_PTS) { const waitingData = this.waitingData; if (!waitingData && !this.loadingParts || waitingData && waitingData.frag.cc !== cc) { this.syncWithAnchor(frag, waitingData == null ? void 0 : waitingData.frag); } } else if (!this.hls.hasEnoughToStart && inFlightFrag && inFlightFrag.cc !== cc) { inFlightFrag.abortRequests(); this.syncWithAnchor(frag, inFlightFrag); } else if (this.state === State.IDLE) { this.tick(); } } } getLoadPosition() { if (!this.startFragRequested && this.nextLoadPosition >= 0) { return this.nextLoadPosition; } return super.getLoadPosition(); } syncWithAnchor(mainAnchor, waitingToAppend) { var _this$mainFragLoading; const mainFragLoading = ((_this$mainFragLoading = this.mainFragLoading) == null ? void 0 : _this$mainFragLoading.frag) || null; if (waitingToAppend) { if ((mainFragLoading == null ? void 0 : mainFragLoading.cc) === waitingToAppend.cc) { return; } } const targetDiscontinuity = (mainFragLoading || mainAnchor).cc; const trackDetails = this.getLevelDetails(); const pos = this.getLoadPosition(); const syncFrag = findNearestWithCC(trackDetails, targetDiscontinuity, pos); if (syncFrag) { this.log(`Syncing with main frag at ${syncFrag.start} cc ${syncFrag.cc}`); this.startFragRequested = false; this.nextLoadPosition = syncFrag.start; this.resetLoadingState(); if (this.state === State.IDLE) { this.doTickIdle(); } } } startLoad(startPosition, skipSeekToStartPosition) { if (!this.levels) { this.startPosition = startPosition; this.state = State.STOPPED; return; } const lastCurrentTime = this.lastCurrentTime; this.stopLoad(); this.setInterval(TICK_INTERVAL$3); if (lastCurrentTime > 0 && startPosition === -1) { this.log(`Override startPosition with lastCurrentTime @${lastCurrentTime.toFixed(3)}`); startPosition = lastCurrentTime; this.state = State.IDLE; } else { this.state = State.WAITING_TRACK; } this.nextLoadPosition = this.lastCurrentTime = startPosition + this.timelineOffset; this.startPosition = skipSeekToStartPosition ? -1 : startPosition; this.tick(); } doTick() { switch (this.state) { case State.IDLE: this.doTickIdle(); break; case State.WAITING_TRACK: { const { levels, trackId } = this; const currenTrack = levels == null ? void 0 : levels[trackId]; const details = currenTrack == null ? void 0 : currenTrack.details; if (details && !this.waitForLive(currenTrack)) { if (this.waitForCdnTuneIn(details)) { break; } this.state = State.WAITING_INIT_PTS; } break; } case State.FRAG_LOADING_WAITING_RETRY: { var _this$media; const now2 = performance.now(); const retryDate = this.retryDate; if (!retryDate || now2 >= retryDate || (_this$media = this.media) != null && _this$media.seeking) { const { levels, trackId } = this; this.log("RetryDate reached, switch back to IDLE state"); this.resetStartWhenNotLoaded((levels == null ? void 0 : levels[trackId]) || null); this.state = State.IDLE; } break; } case State.WAITING_INIT_PTS: { const waitingData = this.waitingData; if (waitingData) { const { frag, part, cache, complete } = waitingData; const mainAnchor = this.mainAnchor; if (this.initPTS[frag.cc] !== void 0) { this.waitingData = null; this.state = State.FRAG_LOADING; const payload = cache.flush().buffer; const data = { frag, part, payload, networkDetails: null }; this._handleFragmentLoadProgress(data); if (complete) { super._handleFragmentLoadComplete(data); } } else if (mainAnchor && mainAnchor.cc !== waitingData.frag.cc) { this.syncWithAnchor(mainAnchor, waitingData.frag); } } else { this.state = State.IDLE; } } } this.onTickEnd(); } resetLoadingState() { const waitingData = this.waitingData; if (waitingData) { this.fragmentTracker.removeFragment(waitingData.frag); this.waitingData = null; } super.resetLoadingState(); } onTickEnd() { const { media } = this; if (!(media != null && media.readyState)) { return; } this.lastCurrentTime = media.currentTime; } doTickIdle() { var _this$mainFragLoading2; const { hls, levels, media, trackId } = this; const config = hls.config; if (!this.buffering || !media && !this.primaryPrefetch && (this.startFragRequested || !config.startFragPrefetch) || !(levels != null && levels[trackId])) { return; } const levelInfo = levels[trackId]; const trackDetails = levelInfo.details; if (!trackDetails || this.waitForLive(levelInfo) || this.waitForCdnTuneIn(trackDetails)) { this.state = State.WAITING_TRACK; this.startFragRequested = false; return; } const bufferable = this.mediaBuffer ? this.mediaBuffer : this.media; if (this.bufferFlushed && bufferable) { this.bufferFlushed = false; this.afterBufferFlushed(bufferable, ElementaryStreamTypes.AUDIO, PlaylistLevelType.AUDIO); } const bufferInfo = this.getFwdBufferInfo(bufferable, PlaylistLevelType.AUDIO); if (bufferInfo === null) { return; } if (!this.switchingTrack && this._streamEnded(bufferInfo, trackDetails)) { hls.trigger(Events.BUFFER_EOS, { type: "audio" }); this.state = State.ENDED; return; } const bufferLen = bufferInfo.len; const maxBufLen = hls.maxBufferLength; const fragments = trackDetails.fragments; const start = fragments[0].start; const loadPosition = this.getLoadPosition(); const targetBufferTime = this.flushing ? loadPosition : bufferInfo.end; if (this.switchingTrack && media) { const pos = loadPosition; if (trackDetails.PTSKnown && pos < start) { if (bufferInfo.end > start || bufferInfo.nextStart) { this.log("Alt audio track ahead of main track, seek to start of alt audio track"); media.currentTime = start + 0.05; } } } if (bufferLen >= maxBufLen && !this.switchingTrack && targetBufferTime < fragments[fragments.length - 1].start) { return; } let frag = this.getNextFragment(targetBufferTime, trackDetails); if (frag && this.isLoopLoading(frag, targetBufferTime)) { frag = this.getNextFragmentLoopLoading(frag, trackDetails, bufferInfo, PlaylistLevelType.MAIN, maxBufLen); } if (!frag) { this.bufferFlushed = true; return; } let mainFragLoading = ((_this$mainFragLoading2 = this.mainFragLoading) == null ? void 0 : _this$mainFragLoading2.frag) || null; if (!this.audioOnly && this.startFragRequested && mainFragLoading && isMediaFragment(frag) && !frag.endList && (!trackDetails.live || !this.loadingParts && targetBufferTime < this.hls.liveSyncPosition)) { if (this.fragmentTracker.getState(mainFragLoading) === FragmentState.OK) { this.mainFragLoading = mainFragLoading = null; } if (mainFragLoading && isMediaFragment(mainFragLoading)) { if (frag.start > mainFragLoading.end) { const mainFragAtPos = this.fragmentTracker.getFragAtPos(targetBufferTime, PlaylistLevelType.MAIN); if (mainFragAtPos && mainFragAtPos.end > mainFragLoading.end) { mainFragLoading = mainFragAtPos; this.mainFragLoading = { frag: mainFragAtPos, targetBufferTime: null }; } } const atBufferSyncLimit = frag.start > mainFragLoading.end; if (atBufferSyncLimit) { return; } } } this.loadFragment(frag, levelInfo, targetBufferTime); } onMediaDetaching(event, data) { this.bufferFlushed = this.flushing = false; super.onMediaDetaching(event, data); } onAudioTracksUpdated(event, { audioTracks }) { this.resetTransmuxer(); this.levels = audioTracks.map((mediaPlaylist) => new Level(mediaPlaylist)); } onAudioTrackSwitching(event, data) { const altAudio = !!data.url; this.trackId = data.id; const { fragCurrent } = this; if (fragCurrent) { fragCurrent.abortRequests(); this.removeUnbufferedFrags(fragCurrent.start); } this.resetLoadingState(); if (altAudio) { this.switchingTrack = data; this.flushAudioIfNeeded(data); if (this.state !== State.STOPPED) { this.setInterval(TICK_INTERVAL$3); this.state = State.IDLE; this.tick(); } } else { this.resetTransmuxer(); this.switchingTrack = null; this.bufferedTrack = data; this.clearInterval(); } } onManifestLoading() { super.onManifestLoading(); this.bufferFlushed = this.flushing = this.audioOnly = false; this.resetItem(); this.trackId = -1; } onLevelLoaded(event, data) { this.mainDetails = data.details; const cachedTrackLoadedData = this.cachedTrackLoadedData; if (cachedTrackLoadedData) { this.cachedTrackLoadedData = null; this.onAudioTrackLoaded(Events.AUDIO_TRACK_LOADED, cachedTrackLoadedData); } } onAudioTrackLoaded(event, data) { var _trackLevel$details; const { levels } = this; const { details: newDetails, id: trackId, groupId, track } = data; if (!levels) { this.warn(`Audio tracks reset while loading track ${trackId} "${track.name}" of "${groupId}"`); return; } const mainDetails = this.mainDetails; if (!mainDetails || newDetails.endCC > mainDetails.endCC || mainDetails.expired) { this.cachedTrackLoadedData = data; if (this.state !== State.STOPPED) { this.state = State.WAITING_TRACK; } return; } this.cachedTrackLoadedData = null; this.log(`Audio track ${trackId} "${track.name}" of "${groupId}" loaded [${newDetails.startSN},${newDetails.endSN}]${newDetails.lastPartSn ? `[part-${newDetails.lastPartSn}-${newDetails.lastPartIndex}]` : ""},duration:${newDetails.totalduration}`); const trackLevel = levels[trackId]; let sliding = 0; if (newDetails.live || (_trackLevel$details = trackLevel.details) != null && _trackLevel$details.live) { this.checkLiveUpdate(newDetails); if (newDetails.deltaUpdateFailed) { return; } if (trackLevel.details) { var _this$levelLastLoaded; sliding = this.alignPlaylists(newDetails, trackLevel.details, (_this$levelLastLoaded = this.levelLastLoaded) == null ? void 0 : _this$levelLastLoaded.details); } if (!newDetails.alignedSliding) { alignDiscontinuities(newDetails, mainDetails); if (!newDetails.alignedSliding) { alignMediaPlaylistByPDT(newDetails, mainDetails); } sliding = newDetails.fragmentStart; } } trackLevel.details = newDetails; this.levelLastLoaded = trackLevel; if (!this.startFragRequested) { this.setStartPosition(mainDetails, sliding); } this.hls.trigger(Events.AUDIO_TRACK_UPDATED, { details: newDetails, id: trackId, groupId: data.groupId }); if (this.state === State.WAITING_TRACK && !this.waitForCdnTuneIn(newDetails)) { this.state = State.IDLE; } this.tick(); } _handleFragmentLoadProgress(data) { var _frag$initSegment; const frag = data.frag; const { part, payload } = data; const { config, trackId, levels } = this; if (!levels) { this.warn(`Audio tracks were reset while fragment load was in progress. Fragment ${frag.sn} of level ${frag.level} will not be buffered`); return; } const track = levels[trackId]; if (!track) { this.warn("Audio track is undefined on fragment load progress"); return; } const details = track.details; if (!details) { this.warn("Audio track details undefined on fragment load progress"); this.removeUnbufferedFrags(frag.start); return; } const audioCodec = config.defaultAudioCodec || track.audioCodec || "mp4a.40.2"; let transmuxer = this.transmuxer; if (!transmuxer) { transmuxer = this.transmuxer = new TransmuxerInterface(this.hls, PlaylistLevelType.AUDIO, this._handleTransmuxComplete.bind(this), this._handleTransmuxerFlush.bind(this)); } const initPTS = this.initPTS[frag.cc]; const initSegmentData = (_frag$initSegment = frag.initSegment) == null ? void 0 : _frag$initSegment.data; if (initPTS !== void 0) { const accurateTimeOffset = false; const partIndex = part ? part.index : -1; const partial = partIndex !== -1; const chunkMeta = new ChunkMetadata(frag.level, frag.sn, frag.stats.chunkCount, payload.byteLength, partIndex, partial); transmuxer.push(payload, initSegmentData, audioCodec, "", frag, part, details.totalduration, accurateTimeOffset, chunkMeta, initPTS); } else { this.log(`Unknown video PTS for cc ${frag.cc}, waiting for video PTS before demuxing audio frag ${frag.sn} of [${details.startSN} ,${details.endSN}],track ${trackId}`); const { cache } = this.waitingData = this.waitingData || { frag, part, cache: new ChunkCache(), complete: false }; cache.push(new Uint8Array(payload)); if (this.state !== State.STOPPED) { this.state = State.WAITING_INIT_PTS; } } } _handleFragmentLoadComplete(fragLoadedData) { if (this.waitingData) { this.waitingData.complete = true; return; } super._handleFragmentLoadComplete(fragLoadedData); } onBufferReset() { this.mediaBuffer = null; } onBufferCreated(event, data) { this.bufferFlushed = this.flushing = false; const audioTrack = data.tracks.audio; if (audioTrack) { this.mediaBuffer = audioTrack.buffer || null; } } onFragLoading(event, data) { if (!this.audioOnly && data.frag.type === PlaylistLevelType.MAIN && isMediaFragment(data.frag)) { this.mainFragLoading = data; if (this.state === State.IDLE) { this.tick(); } } } onFragBuffered(event, data) { const { frag, part } = data; if (frag.type !== PlaylistLevelType.AUDIO) { if (!this.audioOnly && frag.type === PlaylistLevelType.MAIN && !frag.elementaryStreams.video && !frag.elementaryStreams.audiovideo) { this.audioOnly = true; this.mainFragLoading = null; } return; } if (this.fragContextChanged(frag)) { this.warn(`Fragment ${frag.sn}${part ? " p: " + part.index : ""} of level ${frag.level} finished buffering, but was aborted. state: ${this.state}, audioSwitch: ${this.switchingTrack ? this.switchingTrack.name : "false"}`); return; } if (isMediaFragment(frag)) { this.fragPrevious = frag; const track = this.switchingTrack; if (track) { this.bufferedTrack = track; this.switchingTrack = null; this.hls.trigger(Events.AUDIO_TRACK_SWITCHED, _objectSpread2({}, track)); } } this.fragBufferedComplete(frag, part); if (this.media) { this.tick(); } } onError(event, data) { var _data$context; if (data.fatal) { this.state = State.ERROR; return; } switch (data.details) { case ErrorDetails.FRAG_GAP: case ErrorDetails.FRAG_PARSING_ERROR: case ErrorDetails.FRAG_DECRYPT_ERROR: case ErrorDetails.FRAG_LOAD_ERROR: case ErrorDetails.FRAG_LOAD_TIMEOUT: case ErrorDetails.KEY_LOAD_ERROR: case ErrorDetails.KEY_LOAD_TIMEOUT: this.onFragmentOrKeyLoadError(PlaylistLevelType.AUDIO, data); break; case ErrorDetails.AUDIO_TRACK_LOAD_ERROR: case ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT: case ErrorDetails.LEVEL_PARSING_ERROR: if (!data.levelRetry && this.state === State.WAITING_TRACK && ((_data$context = data.context) == null ? void 0 : _data$context.type) === PlaylistContextType.AUDIO_TRACK) { this.state = State.IDLE; } break; case ErrorDetails.BUFFER_ADD_CODEC_ERROR: case ErrorDetails.BUFFER_APPEND_ERROR: if (data.parent !== "audio") { return; } if (!this.reduceLengthAndFlushBuffer(data)) { this.resetLoadingState(); } break; case ErrorDetails.BUFFER_FULL_ERROR: if (data.parent !== "audio") { return; } if (this.reduceLengthAndFlushBuffer(data)) { this.bufferedTrack = null; super.flushMainBuffer(0, Number.POSITIVE_INFINITY, "audio"); } break; case ErrorDetails.INTERNAL_EXCEPTION: this.recoverWorkerError(data); break; } } onBufferFlushing(event, { type }) { if (type !== ElementaryStreamTypes.VIDEO) { this.flushing = true; } } onBufferFlushed(event, { type }) { if (type !== ElementaryStreamTypes.VIDEO) { this.flushing = false; this.bufferFlushed = true; if (this.state === State.ENDED) { this.state = State.IDLE; } const mediaBuffer = this.mediaBuffer || this.media; if (mediaBuffer) { this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.AUDIO); this.tick(); } } } _handleTransmuxComplete(transmuxResult) { var _id3$samples; const id = "audio"; const { hls } = this; const { remuxResult, chunkMeta } = transmuxResult; const context = this.getCurrentContext(chunkMeta); if (!context) { this.resetWhenMissingContext(chunkMeta); return; } const { frag, part, level } = context; const { details } = level; const { audio, text, id3, initSegment } = remuxResult; if (this.fragContextChanged(frag) || !details) { this.fragmentTracker.removeFragment(frag); return; } this.state = State.PARSING; if (this.switchingTrack && audio) { this.completeAudioSwitch(this.switchingTrack); } if (initSegment != null && initSegment.tracks) { const mapFragment = frag.initSegment || frag; this._bufferInitSegment(level, initSegment.tracks, mapFragment, chunkMeta); hls.trigger(Events.FRAG_PARSING_INIT_SEGMENT, { frag: mapFragment, id, tracks: initSegment.tracks }); } if (audio) { const { startPTS, endPTS, startDTS, endDTS } = audio; if (part) { part.elementaryStreams[ElementaryStreamTypes.AUDIO] = { startPTS, endPTS, startDTS, endDTS }; } frag.setElementaryStreamInfo(ElementaryStreamTypes.AUDIO, startPTS, endPTS, startDTS, endDTS); this.bufferFragmentData(audio, frag, part, chunkMeta); } if (id3 != null && (_id3$samples = id3.samples) != null && _id3$samples.length) { const emittedID3 = _extends({ id, frag, details }, id3); hls.trigger(Events.FRAG_PARSING_METADATA, emittedID3); } if (text) { const emittedText = _extends({ id, frag, details }, text); hls.trigger(Events.FRAG_PARSING_USERDATA, emittedText); } } _bufferInitSegment(currentLevel, tracks, frag, chunkMeta) { if (this.state !== State.PARSING) { return; } if (tracks.video) { delete tracks.video; } if (tracks.audiovideo) { delete tracks.audiovideo; } if (!tracks.audio) { return; } const track = tracks.audio; track.id = PlaylistLevelType.AUDIO; const variantAudioCodecs = currentLevel.audioCodec; this.log(`Init audio buffer, container:${track.container}, codecs[level/parsed]=[${variantAudioCodecs}/${track.codec}]`); if (variantAudioCodecs && variantAudioCodecs.split(",").length === 1) { track.levelCodec = variantAudioCodecs; } this.hls.trigger(Events.BUFFER_CODECS, tracks); const initSegment = track.initSegment; if (initSegment != null && initSegment.byteLength) { const segment = { type: "audio", frag, part: null, chunkMeta, parent: frag.type, data: initSegment }; this.hls.trigger(Events.BUFFER_APPENDING, segment); } this.tickImmediate(); } loadFragment(frag, track, targetBufferTime) { const fragState = this.fragmentTracker.getState(frag); if (this.switchingTrack || fragState === FragmentState.NOT_LOADED || fragState === FragmentState.PARTIAL) { var _track$details; if (!isMediaFragment(frag)) { this._loadInitSegment(frag, track); } else if ((_track$details = track.details) != null && _track$details.live && !this.initPTS[frag.cc]) { this.log(`Waiting for video PTS in continuity counter ${frag.cc} of live stream before loading audio fragment ${frag.sn} of level ${this.trackId}`); this.state = State.WAITING_INIT_PTS; const mainDetails = this.mainDetails; if (mainDetails && mainDetails.fragmentStart !== track.details.fragmentStart) { alignMediaPlaylistByPDT(track.details, mainDetails); } } else { super.loadFragment(frag, track, targetBufferTime); } } else { this.clearTrackerIfNeeded(frag); } } flushAudioIfNeeded(switchingTrack) { if (this.media && this.bufferedTrack) { const { name, lang, assocLang, characteristics, audioCodec, channels } = this.bufferedTrack; if (!matchesOption({ name, lang, assocLang, characteristics, audioCodec, channels }, switchingTrack, audioMatchPredicate)) { if (useAlternateAudio(switchingTrack.url, this.hls)) { this.log("Switching audio track : flushing all audio"); super.flushMainBuffer(0, Number.POSITIVE_INFINITY, "audio"); this.bufferedTrack = null; } else { this.bufferedTrack = switchingTrack; } } } } completeAudioSwitch(switchingTrack) { const { hls } = this; this.flushAudioIfNeeded(switchingTrack); this.bufferedTrack = switchingTrack; this.switchingTrack = null; hls.trigger(Events.AUDIO_TRACK_SWITCHED, _objectSpread2({}, switchingTrack)); } }; var BasePlaylistController = class extends Logger { constructor(hls, logPrefix) { super(logPrefix, hls.logger); this.hls = void 0; this.canLoad = false; this.timer = -1; this.hls = hls; } destroy() { this.clearTimer(); this.hls = this.log = this.warn = null; } clearTimer() { if (this.timer !== -1) { self.clearTimeout(this.timer); this.timer = -1; } } startLoad() { this.canLoad = true; this.loadPlaylist(); } stopLoad() { this.canLoad = false; this.clearTimer(); } switchParams(playlistUri, previous, current) { const renditionReports = previous == null ? void 0 : previous.renditionReports; if (renditionReports) { let foundIndex = -1; for (let i = 0; i < renditionReports.length; i++) { const attr = renditionReports[i]; let uri; try { uri = new self.URL(attr.URI, previous.url).href; } catch (error) { this.warn(`Could not construct new URL for Rendition Report: ${error}`); uri = attr.URI || ""; } if (uri === playlistUri) { foundIndex = i; break; } else if (uri === playlistUri.substring(0, uri.length)) { foundIndex = i; } } if (foundIndex !== -1) { const attr = renditionReports[foundIndex]; const msn = parseInt(attr["LAST-MSN"]) || (previous == null ? void 0 : previous.lastPartSn); let part = parseInt(attr["LAST-PART"]) || (previous == null ? void 0 : previous.lastPartIndex); if (this.hls.config.lowLatencyMode) { const currentGoal = Math.min(previous.age - previous.partTarget, previous.targetduration); if (part >= 0 && currentGoal > previous.partTarget) { part += 1; } } const skip = current && getSkipValue(current); return new HlsUrlParameters(msn, part >= 0 ? part : void 0, skip); } } } loadPlaylist(hlsUrlParameters) { this.clearTimer(); } loadingPlaylist(playlist, hlsUrlParameters) { this.clearTimer(); } shouldLoadPlaylist(playlist) { return this.canLoad && !!playlist && !!playlist.url && (!playlist.details || playlist.details.live); } getUrlWithDirectives(uri, hlsUrlParameters) { if (hlsUrlParameters) { try { return hlsUrlParameters.addDirectives(uri); } catch (error) { this.warn(`Could not construct new URL with HLS Delivery Directives: ${error}`); } } return uri; } playlistLoaded(index, data, previousDetails) { const { details, stats } = data; const now2 = self.performance.now(); const elapsed = stats.loading.first ? Math.max(0, now2 - stats.loading.first) : 0; details.advancedDateTime = Date.now() - elapsed; const timelineOffset = this.hls.config.timelineOffset; if (timelineOffset !== details.appliedTimelineOffset) { const offset = Math.max(timelineOffset || 0, 0); details.appliedTimelineOffset = offset; details.fragments.forEach((frag) => { frag.start = frag.playlistOffset + offset; }); } if (details.live || previousDetails != null && previousDetails.live) { const levelOrTrack = "levelInfo" in data ? data.levelInfo : data.track; details.reloaded(previousDetails); if (previousDetails && details.fragments.length > 0) { mergeDetails(previousDetails, details); const error = details.playlistParsingError; if (error) { this.warn(error); const hls = this.hls; if (!hls.config.ignorePlaylistParsingErrors) { var _details$fragments$; const { networkDetails } = data; hls.trigger(Events.ERROR, { type: ErrorTypes.NETWORK_ERROR, details: ErrorDetails.LEVEL_PARSING_ERROR, fatal: false, url: details.url, error, reason: error.message, level: data.level || void 0, parent: (_details$fragments$ = details.fragments[0]) == null ? void 0 : _details$fragments$.type, networkDetails, stats }); return; } details.playlistParsingError = null; } } if (details.requestScheduled === -1) { details.requestScheduled = stats.loading.start; } const bufferInfo = this.hls.mainForwardBufferInfo; const position = bufferInfo ? bufferInfo.end - bufferInfo.len : 0; const distanceToLiveEdgeMs = (details.edge - position) * 1e3; const reloadInterval = computeReloadInterval(details, distanceToLiveEdgeMs); if (details.requestScheduled + reloadInterval < now2) { details.requestScheduled = now2; } else { details.requestScheduled += reloadInterval; } this.log(`live playlist ${index} ${details.advanced ? "REFRESHED " + details.lastPartSn + "-" + details.lastPartIndex : details.updated ? "UPDATED" : "MISSED"}`); if (!this.canLoad || !details.live) { return; } let deliveryDirectives; let msn = void 0; let part = void 0; if (details.canBlockReload && details.endSN && details.advanced) { const lowLatencyMode = this.hls.config.lowLatencyMode; const lastPartSn = details.lastPartSn; const endSn = details.endSN; const lastPartIndex = details.lastPartIndex; const hasParts = lastPartIndex !== -1; const atLastPartOfSegment = lastPartSn === endSn; if (hasParts) { if (atLastPartOfSegment) { msn = endSn + 1; part = lowLatencyMode ? 0 : lastPartIndex; } else { msn = lastPartSn; part = lowLatencyMode ? lastPartIndex + 1 : details.maxPartIndex; } } else { msn = endSn + 1; } const lastAdvanced = details.age; const cdnAge = lastAdvanced + details.ageHeader; let currentGoal = Math.min(cdnAge - details.partTarget, details.targetduration * 1.5); if (currentGoal > 0) { if (cdnAge > details.targetduration * 3) { this.log(`Playlist last advanced ${lastAdvanced.toFixed(2)}s ago. Omitting segment and part directives.`); msn = void 0; part = void 0; } else if (previousDetails != null && previousDetails.tuneInGoal && cdnAge - details.partTarget > previousDetails.tuneInGoal) { this.warn(`CDN Tune-in goal increased from: ${previousDetails.tuneInGoal} to: ${currentGoal} with playlist age: ${details.age}`); currentGoal = 0; } else { const segments = Math.floor(currentGoal / details.targetduration); msn += segments; if (part !== void 0) { const parts = Math.round(currentGoal % details.targetduration / details.partTarget); part += parts; } this.log(`CDN Tune-in age: ${details.ageHeader}s last advanced ${lastAdvanced.toFixed(2)}s goal: ${currentGoal} skip sn ${segments} to part ${part}`); } details.tuneInGoal = currentGoal; } deliveryDirectives = this.getDeliveryDirectives(details, data.deliveryDirectives, msn, part); if (lowLatencyMode || !atLastPartOfSegment) { details.requestScheduled = now2; this.loadingPlaylist(levelOrTrack, deliveryDirectives); return; } } else if (details.canBlockReload || details.canSkipUntil) { deliveryDirectives = this.getDeliveryDirectives(details, data.deliveryDirectives, msn, part); } if (deliveryDirectives && msn !== void 0 && details.canBlockReload) { details.requestScheduled = stats.loading.first + Math.max(reloadInterval - elapsed * 2, reloadInterval / 2); } this.scheduleLoading(levelOrTrack, deliveryDirectives, details); } else { this.clearTimer(); } } scheduleLoading(levelOrTrack, deliveryDirectives, updatedDetails) { const details = updatedDetails || levelOrTrack.details; if (!details) { this.loadingPlaylist(levelOrTrack, deliveryDirectives); return; } const now2 = self.performance.now(); const requestScheduled = details.requestScheduled; if (now2 >= requestScheduled) { this.loadingPlaylist(levelOrTrack, deliveryDirectives); return; } const estimatedTimeUntilUpdate = requestScheduled - now2; this.log(`reload live playlist ${levelOrTrack.name || levelOrTrack.bitrate + "bps"} in ${Math.round(estimatedTimeUntilUpdate)} ms`); this.clearTimer(); this.timer = self.setTimeout(() => this.loadingPlaylist(levelOrTrack, deliveryDirectives), estimatedTimeUntilUpdate); } getDeliveryDirectives(details, previousDeliveryDirectives, msn, part) { let skip = getSkipValue(details); if (previousDeliveryDirectives != null && previousDeliveryDirectives.skip && details.deltaUpdateFailed) { msn = previousDeliveryDirectives.msn; part = previousDeliveryDirectives.part; skip = HlsSkip.No; } return new HlsUrlParameters(msn, part, skip); } checkRetry(errorEvent) { const errorDetails = errorEvent.details; const isTimeout = isTimeoutError(errorEvent); const errorAction = errorEvent.errorAction; const { action, retryCount = 0, retryConfig } = errorAction || {}; const retry = !!errorAction && !!retryConfig && (action === NetworkErrorAction.RetryRequest || !errorAction.resolved && action === NetworkErrorAction.SendAlternateToPenaltyBox); if (retry) { var _errorEvent$context; if (retryCount >= retryConfig.maxNumRetry) { return false; } if (isTimeout && (_errorEvent$context = errorEvent.context) != null && _errorEvent$context.deliveryDirectives) { this.warn(`Retrying playlist loading ${retryCount + 1}/${retryConfig.maxNumRetry} after "${errorDetails}" without delivery-directives`); this.loadPlaylist(); } else { const delay = getRetryDelay(retryConfig, retryCount); this.clearTimer(); this.timer = self.setTimeout(() => this.loadPlaylist(), delay); this.warn(`Retrying playlist loading ${retryCount + 1}/${retryConfig.maxNumRetry} after "${errorDetails}" in ${delay}ms`); } errorEvent.levelRetry = true; errorAction.resolved = true; } return retry; } }; function subtitleOptionsIdentical(trackList1, trackList2) { if (trackList1.length !== trackList2.length) { return false; } for (let i = 0; i < trackList1.length; i++) { if (!mediaAttributesIdentical(trackList1[i].attrs, trackList2[i].attrs)) { return false; } } return true; } function mediaAttributesIdentical(attrs1, attrs2, customAttributes) { const stableRenditionId = attrs1["STABLE-RENDITION-ID"]; if (stableRenditionId && !customAttributes) { return stableRenditionId === attrs2["STABLE-RENDITION-ID"]; } return !(customAttributes || ["LANGUAGE", "NAME", "CHARACTERISTICS", "AUTOSELECT", "DEFAULT", "FORCED", "ASSOC-LANGUAGE"]).some((subtitleAttribute) => attrs1[subtitleAttribute] !== attrs2[subtitleAttribute]); } function subtitleTrackMatchesTextTrack(subtitleTrack, textTrack) { return textTrack.label.toLowerCase() === subtitleTrack.name.toLowerCase() && (!textTrack.language || textTrack.language.toLowerCase() === (subtitleTrack.lang || "").toLowerCase()); } var AudioTrackController = class extends BasePlaylistController { constructor(hls) { super(hls, "audio-track-controller"); this.tracks = []; this.groupIds = null; this.tracksInGroup = []; this.trackId = -1; this.currentTrack = null; this.selectDefaultTrack = true; this.registerListeners(); } registerListeners() { const { hls } = this; hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this); hls.on(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); hls.on(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this); hls.on(Events.ERROR, this.onError, this); } unregisterListeners() { const { hls } = this; hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); hls.off(Events.LEVEL_LOADING, this.onLevelLoading, this); hls.off(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); hls.off(Events.AUDIO_TRACK_LOADED, this.onAudioTrackLoaded, this); hls.off(Events.ERROR, this.onError, this); } destroy() { this.unregisterListeners(); this.tracks.length = 0; this.tracksInGroup.length = 0; this.currentTrack = null; super.destroy(); } onManifestLoading() { this.tracks = []; this.tracksInGroup = []; this.groupIds = null; this.currentTrack = null; this.trackId = -1; this.selectDefaultTrack = true; } onManifestParsed(event, data) { this.tracks = data.audioTracks || []; } onAudioTrackLoaded(event, data) { const { id, groupId, details } = data; const trackInActiveGroup = this.tracksInGroup[id]; if (!trackInActiveGroup || trackInActiveGroup.groupId !== groupId) { this.warn(`Audio track with id:${id} and group:${groupId} not found in active group ${trackInActiveGroup == null ? void 0 : trackInActiveGroup.groupId}`); return; } const curDetails = trackInActiveGroup.details; trackInActiveGroup.details = data.details; this.log(`Audio track ${id} "${trackInActiveGroup.name}" lang:${trackInActiveGroup.lang} group:${groupId} loaded [${details.startSN}-${details.endSN}]`); if (id === this.trackId) { this.playlistLoaded(id, data, curDetails); } } onLevelLoading(event, data) { this.switchLevel(data.level); } onLevelSwitching(event, data) { this.switchLevel(data.level); } switchLevel(levelIndex) { const levelInfo = this.hls.levels[levelIndex]; if (!levelInfo) { return; } const audioGroups = levelInfo.audioGroups || null; const currentGroups = this.groupIds; let currentTrack = this.currentTrack; if (!audioGroups || (currentGroups == null ? void 0 : currentGroups.length) !== (audioGroups == null ? void 0 : audioGroups.length) || audioGroups != null && audioGroups.some((groupId) => (currentGroups == null ? void 0 : currentGroups.indexOf(groupId)) === -1)) { this.groupIds = audioGroups; this.trackId = -1; this.currentTrack = null; const audioTracks = this.tracks.filter((track) => !audioGroups || audioGroups.indexOf(track.groupId) !== -1); if (audioTracks.length) { if (this.selectDefaultTrack && !audioTracks.some((track) => track.default)) { this.selectDefaultTrack = false; } audioTracks.forEach((track, i) => { track.id = i; }); } else if (!currentTrack && !this.tracksInGroup.length) { return; } this.tracksInGroup = audioTracks; const audioPreference = this.hls.config.audioPreference; if (!currentTrack && audioPreference) { const groupIndex = findMatchingOption(audioPreference, audioTracks, audioMatchPredicate); if (groupIndex > -1) { currentTrack = audioTracks[groupIndex]; } else { const allIndex = findMatchingOption(audioPreference, this.tracks); currentTrack = this.tracks[allIndex]; } } let trackId = this.findTrackId(currentTrack); if (trackId === -1 && currentTrack) { trackId = this.findTrackId(null); } const audioTracksUpdated = { audioTracks }; this.log(`Updating audio tracks, ${audioTracks.length} track(s) found in group(s): ${audioGroups == null ? void 0 : audioGroups.join(",")}`); this.hls.trigger(Events.AUDIO_TRACKS_UPDATED, audioTracksUpdated); const selectedTrackId = this.trackId; if (trackId !== -1 && selectedTrackId === -1) { this.setAudioTrack(trackId); } else if (audioTracks.length && selectedTrackId === -1) { var _this$groupIds; const error = new Error(`No audio track selected for current audio group-ID(s): ${(_this$groupIds = this.groupIds) == null ? void 0 : _this$groupIds.join(",")} track count: ${audioTracks.length}`); this.warn(error.message); this.hls.trigger(Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.AUDIO_TRACK_LOAD_ERROR, fatal: true, error }); } } } onError(event, data) { if (data.fatal || !data.context) { return; } if (data.context.type === PlaylistContextType.AUDIO_TRACK && data.context.id === this.trackId && (!this.groupIds || this.groupIds.indexOf(data.context.groupId) !== -1)) { this.checkRetry(data); } } get allAudioTracks() { return this.tracks; } get audioTracks() { return this.tracksInGroup; } get audioTrack() { return this.trackId; } set audioTrack(newId) { this.selectDefaultTrack = false; this.setAudioTrack(newId); } setAudioOption(audioOption) { const hls = this.hls; hls.config.audioPreference = audioOption; if (audioOption) { const allAudioTracks = this.allAudioTracks; this.selectDefaultTrack = false; if (allAudioTracks.length) { const currentTrack = this.currentTrack; if (currentTrack && matchesOption(audioOption, currentTrack, audioMatchPredicate)) { return currentTrack; } const groupIndex = findMatchingOption(audioOption, this.tracksInGroup, audioMatchPredicate); if (groupIndex > -1) { const track = this.tracksInGroup[groupIndex]; this.setAudioTrack(groupIndex); return track; } else if (currentTrack) { let searchIndex = hls.loadLevel; if (searchIndex === -1) { searchIndex = hls.firstAutoLevel; } const switchIndex = findClosestLevelWithAudioGroup(audioOption, hls.levels, allAudioTracks, searchIndex, audioMatchPredicate); if (switchIndex === -1) { return null; } hls.nextLoadLevel = switchIndex; } if (audioOption.channels || audioOption.audioCodec) { const withoutCodecAndChannelsMatch = findMatchingOption(audioOption, allAudioTracks); if (withoutCodecAndChannelsMatch > -1) { return allAudioTracks[withoutCodecAndChannelsMatch]; } } } } return null; } setAudioTrack(newId) { const tracks = this.tracksInGroup; if (newId < 0 || newId >= tracks.length) { this.warn(`Invalid audio track id: ${newId}`); return; } this.selectDefaultTrack = false; const lastTrack = this.currentTrack; const track = tracks[newId]; const trackLoaded = track.details && !track.details.live; if (newId === this.trackId && track === lastTrack && trackLoaded) { return; } this.log(`Switching to audio-track ${newId} "${track.name}" lang:${track.lang} group:${track.groupId} channels:${track.channels}`); this.trackId = newId; this.currentTrack = track; this.hls.trigger(Events.AUDIO_TRACK_SWITCHING, _objectSpread2({}, track)); if (trackLoaded) { return; } const hlsUrlParameters = this.switchParams(track.url, lastTrack == null ? void 0 : lastTrack.details, track.details); this.loadPlaylist(hlsUrlParameters); } findTrackId(currentTrack) { const audioTracks = this.tracksInGroup; for (let i = 0; i < audioTracks.length; i++) { const track = audioTracks[i]; if (this.selectDefaultTrack && !track.default) { continue; } if (!currentTrack || matchesOption(currentTrack, track, audioMatchPredicate)) { return i; } } if (currentTrack) { const { name, lang, assocLang, characteristics, audioCodec, channels } = currentTrack; for (let i = 0; i < audioTracks.length; i++) { const track = audioTracks[i]; if (matchesOption({ name, lang, assocLang, characteristics, audioCodec, channels }, track, audioMatchPredicate)) { return i; } } for (let i = 0; i < audioTracks.length; i++) { const track = audioTracks[i]; if (mediaAttributesIdentical(currentTrack.attrs, track.attrs, ["LANGUAGE", "ASSOC-LANGUAGE", "CHARACTERISTICS"])) { return i; } } for (let i = 0; i < audioTracks.length; i++) { const track = audioTracks[i]; if (mediaAttributesIdentical(currentTrack.attrs, track.attrs, ["LANGUAGE"])) { return i; } } } return -1; } loadPlaylist(hlsUrlParameters) { super.loadPlaylist(); const audioTrack = this.currentTrack; if (!this.shouldLoadPlaylist(audioTrack)) { return; } if (useAlternateAudio(audioTrack.url, this.hls)) { this.scheduleLoading(audioTrack, hlsUrlParameters); } } loadingPlaylist(audioTrack, hlsUrlParameters) { super.loadingPlaylist(audioTrack, hlsUrlParameters); const id = audioTrack.id; const groupId = audioTrack.groupId; const url = this.getUrlWithDirectives(audioTrack.url, hlsUrlParameters); const details = audioTrack.details; const age = details == null ? void 0 : details.age; this.log(`Loading audio-track ${id} "${audioTrack.name}" lang:${audioTrack.lang} group:${groupId}${(hlsUrlParameters == null ? void 0 : hlsUrlParameters.msn) !== void 0 ? " at sn " + hlsUrlParameters.msn + " part " + hlsUrlParameters.part : ""}${age && details.live ? " age " + age.toFixed(1) + (details.type ? " " + details.type || "" : "") : ""} ${url}`); this.hls.trigger(Events.AUDIO_TRACK_LOADING, { url, id, groupId, deliveryDirectives: hlsUrlParameters || null, track: audioTrack }); } }; var BufferOperationQueue = class { constructor(sourceBufferReference) { this.tracks = void 0; this.queues = { video: [], audio: [], audiovideo: [] }; this.tracks = sourceBufferReference; } destroy() { this.tracks = this.queues = null; } append(operation, type, pending) { if (this.queues === null || this.tracks === null) { return; } const queue = this.queues[type]; queue.push(operation); if (queue.length === 1 && !pending) { this.executeNext(type); } } appendBlocker(type) { return new Promise((resolve) => { const operation = { label: "async-blocker", execute: resolve, onStart: () => { }, onComplete: () => { }, onError: () => { } }; this.append(operation, type); }); } prependBlocker(type) { return new Promise((resolve) => { if (this.queues) { const operation = { label: "async-blocker-prepend", execute: resolve, onStart: () => { }, onComplete: () => { }, onError: () => { } }; this.queues[type].unshift(operation); } }); } removeBlockers() { if (this.queues === null) { return; } [this.queues.video, this.queues.audio, this.queues.audiovideo].forEach((queue) => { var _queue$; const label = (_queue$ = queue[0]) == null ? void 0 : _queue$.label; if (label === "async-blocker" || label === "async-blocker-prepend") { queue[0].execute(); queue.splice(0, 1); } }); } unblockAudio(op) { if (this.queues === null) { return; } const queue = this.queues.audio; if (queue[0] === op) { this.shiftAndExecuteNext("audio"); } } executeNext(type) { if (this.queues === null || this.tracks === null) { return; } const queue = this.queues[type]; if (queue.length) { const operation = queue[0]; try { operation.execute(); } catch (error) { var _this$tracks$type; operation.onError(error); if (this.queues === null || this.tracks === null) { return; } const sb = (_this$tracks$type = this.tracks[type]) == null ? void 0 : _this$tracks$type.buffer; if (!(sb != null && sb.updating)) { this.shiftAndExecuteNext(type); } } } } shiftAndExecuteNext(type) { if (this.queues === null) { return; } this.queues[type].shift(); this.executeNext(type); } current(type) { var _this$queues; return ((_this$queues = this.queues) == null ? void 0 : _this$queues[type][0]) || null; } toString() { const { queues, tracks } = this; if (queues === null || tracks === null) { return ``; } return ` ${this.list("video")} ${this.list("audio")} ${this.list("audiovideo")}}`; } list(type) { var _this$queues2, _this$tracks; return (_this$queues2 = this.queues) != null && _this$queues2[type] || (_this$tracks = this.tracks) != null && _this$tracks[type] ? `${type}: (${this.listSbInfo(type)}) ${this.listOps(type)}` : ""; } listSbInfo(type) { var _this$tracks2; const track = (_this$tracks2 = this.tracks) == null ? void 0 : _this$tracks2[type]; const sb = track == null ? void 0 : track.buffer; if (!sb) { return "none"; } return `SourceBuffer${sb.updating ? " updating" : ""}${track.ended ? " ended" : ""}${track.ending ? " ending" : ""}`; } listOps(type) { var _this$queues3; return ((_this$queues3 = this.queues) == null ? void 0 : _this$queues3[type].map((op) => op.label).join(", ")) || ""; } }; var VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/; var TRACK_REMOVED_ERROR_NAME = "HlsJsTrackRemovedError"; var HlsJsTrackRemovedError = class extends Error { constructor(message) { super(message); this.name = TRACK_REMOVED_ERROR_NAME; } }; var BufferController = class extends Logger { constructor(hls, fragmentTracker) { super("buffer-controller", hls.logger); this.hls = void 0; this.fragmentTracker = void 0; this.details = null; this._objectUrl = null; this.operationQueue = null; this.bufferCodecEventsTotal = 0; this.media = null; this.mediaSource = null; this.lastMpegAudioChunk = null; this.blockedAudioAppend = null; this.lastVideoAppendEnd = 0; this.appendSource = void 0; this.transferData = void 0; this.overrides = void 0; this.appendErrors = { audio: 0, video: 0, audiovideo: 0 }; this.tracks = {}; this.sourceBuffers = [[null, null], [null, null]]; this._onEndStreaming = (event) => { var _this$mediaSource; if (!this.hls) { return; } if (((_this$mediaSource = this.mediaSource) == null ? void 0 : _this$mediaSource.readyState) !== "open") { return; } this.hls.pauseBuffering(); }; this._onStartStreaming = (event) => { if (!this.hls) { return; } this.hls.resumeBuffering(); }; this._onMediaSourceOpen = (e) => { const { media, mediaSource } = this; if (e) { this.log("Media source opened"); } if (!media || !mediaSource) { return; } mediaSource.removeEventListener("sourceopen", this._onMediaSourceOpen); media.removeEventListener("emptied", this._onMediaEmptied); this.updateDuration(); this.hls.trigger(Events.MEDIA_ATTACHED, { media, mediaSource }); if (this.mediaSource !== null) { this.checkPendingTracks(); } }; this._onMediaSourceClose = () => { this.log("Media source closed"); }; this._onMediaSourceEnded = () => { this.log("Media source ended"); }; this._onMediaEmptied = () => { const { mediaSrc, _objectUrl } = this; if (mediaSrc !== _objectUrl) { this.error(`Media element src was set while attaching MediaSource (${_objectUrl} > ${mediaSrc})`); } }; this.hls = hls; this.fragmentTracker = fragmentTracker; this.appendSource = isManagedMediaSource(getMediaSource(hls.config.preferManagedMediaSource)); this.initTracks(); this.registerListeners(); } hasSourceTypes() { return Object.keys(this.tracks).length > 0; } destroy() { this.unregisterListeners(); this.details = null; this.lastMpegAudioChunk = this.blockedAudioAppend = null; this.transferData = this.overrides = void 0; if (this.operationQueue) { this.operationQueue.destroy(); this.operationQueue = null; } this.hls = this.fragmentTracker = null; this._onMediaSourceOpen = this._onMediaSourceClose = null; this._onMediaSourceEnded = null; this._onStartStreaming = this._onEndStreaming = null; } registerListeners() { const { hls } = this; hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); hls.on(Events.BUFFER_RESET, this.onBufferReset, this); hls.on(Events.BUFFER_APPENDING, this.onBufferAppending, this); hls.on(Events.BUFFER_CODECS, this.onBufferCodecs, this); hls.on(Events.BUFFER_EOS, this.onBufferEos, this); hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); hls.on(Events.FRAG_PARSED, this.onFragParsed, this); hls.on(Events.FRAG_CHANGED, this.onFragChanged, this); hls.on(Events.ERROR, this.onError, this); } unregisterListeners() { const { hls } = this; hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); hls.off(Events.BUFFER_RESET, this.onBufferReset, this); hls.off(Events.BUFFER_APPENDING, this.onBufferAppending, this); hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this); hls.off(Events.BUFFER_EOS, this.onBufferEos, this); hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); hls.off(Events.FRAG_PARSED, this.onFragParsed, this); hls.off(Events.FRAG_CHANGED, this.onFragChanged, this); hls.off(Events.ERROR, this.onError, this); } transferMedia() { const { media, mediaSource } = this; if (!media) { return null; } const tracks = {}; if (this.operationQueue) { const updating = this.isUpdating(); if (!updating) { this.operationQueue.removeBlockers(); } const queued = this.isQueued(); if (updating || queued) { this.warn(`Transfering MediaSource with${queued ? " operations in queue" : ""}${updating ? " updating SourceBuffer(s)" : ""} ${this.operationQueue}`); } this.operationQueue.destroy(); } const transferData = this.transferData; if (!this.sourceBufferCount && transferData && transferData.mediaSource === mediaSource) { _extends(tracks, transferData.tracks); } else { this.sourceBuffers.forEach((tuple) => { const [type] = tuple; if (type) { tracks[type] = _extends({}, this.tracks[type]); this.removeBuffer(type); } tuple[0] = tuple[1] = null; }); } return { media, mediaSource, tracks }; } initTracks() { const tracks = {}; this.sourceBuffers = [[null, null], [null, null]]; this.tracks = tracks; this.resetQueue(); this.resetAppendErrors(); this.lastMpegAudioChunk = this.blockedAudioAppend = null; this.lastVideoAppendEnd = 0; } onManifestLoading() { this.bufferCodecEventsTotal = 0; this.details = null; } onManifestParsed(event, data) { var _this$transferData; let codecEvents = 2; if (data.audio && !data.video || !data.altAudio) { codecEvents = 1; } this.bufferCodecEventsTotal = codecEvents; this.log(`${codecEvents} bufferCodec event(s) expected.`); if ((_this$transferData = this.transferData) != null && _this$transferData.mediaSource && this.sourceBufferCount && codecEvents) { this.bufferCreated(); } } onMediaAttaching(event, data) { const media = this.media = data.media; const MediaSource = getMediaSource(this.appendSource); this.transferData = this.overrides = void 0; if (media && MediaSource) { const transferringMedia = !!data.mediaSource; if (transferringMedia || data.overrides) { this.transferData = data; this.overrides = data.overrides; } const ms = this.mediaSource = data.mediaSource || new MediaSource(); this.assignMediaSource(ms); if (transferringMedia) { this._objectUrl = media.src; this.attachTransferred(); } else { const objectUrl = this._objectUrl = self.URL.createObjectURL(ms); if (this.appendSource) { try { media.removeAttribute("src"); const MMS = self.ManagedMediaSource; media.disableRemotePlayback = media.disableRemotePlayback || MMS && ms instanceof MMS; removeSourceChildren(media); addSource(media, objectUrl); media.load(); } catch (error) { media.src = objectUrl; } } else { media.src = objectUrl; } } media.addEventListener("emptied", this._onMediaEmptied); } } assignMediaSource(ms) { var _this$transferData2, _ms$constructor; this.log(`${((_this$transferData2 = this.transferData) == null ? void 0 : _this$transferData2.mediaSource) === ms ? "transferred" : "created"} media source: ${(_ms$constructor = ms.constructor) == null ? void 0 : _ms$constructor.name}`); ms.addEventListener("sourceopen", this._onMediaSourceOpen); ms.addEventListener("sourceended", this._onMediaSourceEnded); ms.addEventListener("sourceclose", this._onMediaSourceClose); if (this.appendSource) { ms.addEventListener("startstreaming", this._onStartStreaming); ms.addEventListener("endstreaming", this._onEndStreaming); } } attachTransferred() { const media = this.media; const data = this.transferData; if (!data || !media) { return; } const requiredTracks = this.tracks; const transferredTracks = data.tracks; const trackNames = transferredTracks ? Object.keys(transferredTracks) : null; const trackCount = trackNames ? trackNames.length : 0; const mediaSourceOpenCallback = () => { if (this.media && this.mediaSourceOpenOrEnded) { this._onMediaSourceOpen(); } }; if (transferredTracks && trackNames && trackCount) { if (!this.tracksReady) { this.hls.config.startFragPrefetch = true; this.log(`attachTransferred: waiting for SourceBuffer track info`); return; } this.log(`attachTransferred: (bufferCodecEventsTotal ${this.bufferCodecEventsTotal}) required tracks: ${stringify(requiredTracks, (key, value) => key === "initSegment" ? void 0 : value)}; transfer tracks: ${stringify(transferredTracks, (key, value) => key === "initSegment" ? void 0 : value)}}`); if (!isCompatibleTrackChange(transferredTracks, requiredTracks)) { data.mediaSource = null; data.tracks = void 0; const currentTime = media.currentTime; const details = this.details; const startTime = Math.max(currentTime, (details == null ? void 0 : details.fragments[0].start) || 0); if (startTime - currentTime > 1) { this.log(`attachTransferred: waiting for playback to reach new tracks start time ${currentTime} -> ${startTime}`); return; } this.warn(`attachTransferred: resetting MediaSource for incompatible tracks ("${Object.keys(transferredTracks)}"->"${Object.keys(requiredTracks)}") start time: ${startTime} currentTime: ${currentTime}`); this.onMediaDetaching(Events.MEDIA_DETACHING, {}); this.onMediaAttaching(Events.MEDIA_ATTACHING, data); media.currentTime = startTime; return; } this.transferData = void 0; trackNames.forEach((trackName) => { const type = trackName; const track = transferredTracks[type]; if (track) { const sb = track.buffer; if (sb) { const fragmentTracker = this.fragmentTracker; const playlistType = track.id; if (fragmentTracker.hasFragments(playlistType) || fragmentTracker.hasParts(playlistType)) { const bufferedTimeRanges = BufferHelper.getBuffered(sb); fragmentTracker.detectEvictedFragments(type, bufferedTimeRanges, playlistType, null, true); } const sbIndex = sourceBufferNameToIndex(type); const sbTuple = [type, sb]; this.sourceBuffers[sbIndex] = sbTuple; if (sb.updating && this.operationQueue) { this.operationQueue.prependBlocker(type); } this.trackSourceBuffer(type, track); } } }); mediaSourceOpenCallback(); this.bufferCreated(); } else { this.log(`attachTransferred: MediaSource w/o SourceBuffers`); mediaSourceOpenCallback(); } } get mediaSourceOpenOrEnded() { var _this$mediaSource2; const readyState = (_this$mediaSource2 = this.mediaSource) == null ? void 0 : _this$mediaSource2.readyState; return readyState === "open" || readyState === "ended"; } onMediaDetaching(event, data) { const transferringMedia = !!data.transferMedia; this.transferData = this.overrides = void 0; const { media, mediaSource, _objectUrl } = this; if (mediaSource) { this.log(`media source ${transferringMedia ? "transferring" : "detaching"}`); if (transferringMedia) { this.sourceBuffers.forEach(([type]) => { if (type) { this.removeBuffer(type); } }); this.resetQueue(); } else { if (this.mediaSourceOpenOrEnded) { const open = mediaSource.readyState === "open"; try { const sourceBuffers = mediaSource.sourceBuffers; for (let i = sourceBuffers.length; i--; ) { if (open) { sourceBuffers[i].abort(); } mediaSource.removeSourceBuffer(sourceBuffers[i]); } if (open) { mediaSource.endOfStream(); } } catch (err) { this.warn(`onMediaDetaching: ${err.message} while calling endOfStream`); } } if (this.sourceBufferCount) { this.onBufferReset(); } } mediaSource.removeEventListener("sourceopen", this._onMediaSourceOpen); mediaSource.removeEventListener("sourceended", this._onMediaSourceEnded); mediaSource.removeEventListener("sourceclose", this._onMediaSourceClose); if (this.appendSource) { mediaSource.removeEventListener("startstreaming", this._onStartStreaming); mediaSource.removeEventListener("endstreaming", this._onEndStreaming); } this.mediaSource = null; this._objectUrl = null; } if (media) { media.removeEventListener("emptied", this._onMediaEmptied); if (!transferringMedia) { if (_objectUrl) { self.URL.revokeObjectURL(_objectUrl); } if (this.mediaSrc === _objectUrl) { media.removeAttribute("src"); if (this.appendSource) { removeSourceChildren(media); } media.load(); } else { this.warn("media|source.src was changed by a third party - skip cleanup"); } } this.media = null; } this.hls.trigger(Events.MEDIA_DETACHED, data); } onBufferReset() { this.sourceBuffers.forEach(([type]) => { if (type) { this.resetBuffer(type); } }); this.initTracks(); } resetBuffer(type) { var _this$tracks$type; const sb = (_this$tracks$type = this.tracks[type]) == null ? void 0 : _this$tracks$type.buffer; this.removeBuffer(type); if (sb) { try { var _this$mediaSource3; if ((_this$mediaSource3 = this.mediaSource) != null && _this$mediaSource3.sourceBuffers.length) { this.mediaSource.removeSourceBuffer(sb); } } catch (err) { this.warn(`onBufferReset ${type}`, err); } } delete this.tracks[type]; } removeBuffer(type) { this.removeBufferListeners(type); this.sourceBuffers[sourceBufferNameToIndex(type)] = [null, null]; const track = this.tracks[type]; if (track) { track.buffer = void 0; } } resetQueue() { if (this.operationQueue) { this.operationQueue.destroy(); } this.operationQueue = new BufferOperationQueue(this.tracks); } onBufferCodecs(event, data) { const tracks = this.tracks; const trackNames = Object.keys(data); this.log(`BUFFER_CODECS: "${trackNames}" (current SB count ${this.sourceBufferCount})`); const unmuxedToMuxed = "audiovideo" in data && (tracks.audio || tracks.video) || tracks.audiovideo && ("audio" in data || "video" in data); const muxedToUnmuxed = !unmuxedToMuxed && this.sourceBufferCount && this.media && trackNames.some((sbName) => !tracks[sbName]); if (unmuxedToMuxed || muxedToUnmuxed) { this.warn(`Unsupported transition between "${Object.keys(tracks)}" and "${trackNames}" SourceBuffers`); return; } trackNames.forEach((trackName) => { var _this$transferData3, _this$transferData3$t, _trackCodec; const parsedTrack = data[trackName]; const { id, codec, levelCodec, container, metadata, supplemental } = parsedTrack; let track = tracks[trackName]; const transferredTrack = (_this$transferData3 = this.transferData) == null ? void 0 : (_this$transferData3$t = _this$transferData3.tracks) == null ? void 0 : _this$transferData3$t[trackName]; const sbTrack = transferredTrack != null && transferredTrack.buffer ? transferredTrack : track; const sbCodec = (sbTrack == null ? void 0 : sbTrack.pendingCodec) || (sbTrack == null ? void 0 : sbTrack.codec); const trackLevelCodec = sbTrack == null ? void 0 : sbTrack.levelCodec; if (!track) { track = tracks[trackName] = { buffer: void 0, listeners: [], codec, supplemental, container, levelCodec, metadata, id }; } const currentCodecFull = pickMostCompleteCodecName(sbCodec, trackLevelCodec); const currentCodec = currentCodecFull == null ? void 0 : currentCodecFull.replace(VIDEO_CODEC_PROFILE_REPLACE, "$1"); let trackCodec = pickMostCompleteCodecName(codec, levelCodec); const nextCodec = (_trackCodec = trackCodec) == null ? void 0 : _trackCodec.replace(VIDEO_CODEC_PROFILE_REPLACE, "$1"); if (trackCodec && currentCodecFull && currentCodec !== nextCodec) { if (trackName.slice(0, 5) === "audio") { trackCodec = getCodecCompatibleName(trackCodec, this.appendSource); } this.log(`switching codec ${sbCodec} to ${trackCodec}`); if (trackCodec !== (track.pendingCodec || track.codec)) { track.pendingCodec = trackCodec; } track.container = container; this.appendChangeType(trackName, container, trackCodec); } }); if (this.tracksReady || this.sourceBufferCount) { data.tracks = this.sourceBufferTracks; } if (this.sourceBufferCount) { return; } if (this.mediaSourceOpenOrEnded) { this.checkPendingTracks(); } } get sourceBufferTracks() { return Object.keys(this.tracks).reduce((baseTracks, type) => { const track = this.tracks[type]; baseTracks[type] = { id: track.id, container: track.container, codec: track.codec, levelCodec: track.levelCodec }; return baseTracks; }, {}); } appendChangeType(type, container, codec) { const mimeType = `${container};codecs=${codec}`; const operation = { label: `change-type=${mimeType}`, execute: () => { const track = this.tracks[type]; if (track) { const sb = track.buffer; if (sb != null && sb.changeType) { this.log(`changing ${type} sourceBuffer type to ${mimeType}`); sb.changeType(mimeType); track.codec = codec; track.container = container; } } this.shiftAndExecuteNext(type); }, onStart: () => { }, onComplete: () => { }, onError: (error) => { this.warn(`Failed to change ${type} SourceBuffer type`, error); } }; this.append(operation, type, this.isPending(this.tracks[type])); } blockAudio(partOrFrag) { var _this$fragmentTracker; const pStart = partOrFrag.start; const pTime = pStart + partOrFrag.duration * 0.05; const atGap = ((_this$fragmentTracker = this.fragmentTracker.getAppendedFrag(pStart, PlaylistLevelType.MAIN)) == null ? void 0 : _this$fragmentTracker.gap) === true; if (atGap) { return; } const op = { label: "block-audio", execute: () => { var _this$fragmentTracker2; const videoTrack = this.tracks.video; if (this.lastVideoAppendEnd > pTime || videoTrack != null && videoTrack.buffer && BufferHelper.isBuffered(videoTrack.buffer, pTime) || ((_this$fragmentTracker2 = this.fragmentTracker.getAppendedFrag(pTime, PlaylistLevelType.MAIN)) == null ? void 0 : _this$fragmentTracker2.gap) === true) { this.blockedAudioAppend = null; this.shiftAndExecuteNext("audio"); } }, onStart: () => { }, onComplete: () => { }, onError: (error) => { this.warn("Error executing block-audio operation", error); } }; this.blockedAudioAppend = { op, frag: partOrFrag }; this.append(op, "audio", true); } unblockAudio() { const { blockedAudioAppend, operationQueue } = this; if (blockedAudioAppend && operationQueue) { this.blockedAudioAppend = null; operationQueue.unblockAudio(blockedAudioAppend.op); } } onBufferAppending(event, eventData) { const { tracks } = this; const { data, type, parent, frag, part, chunkMeta, offset } = eventData; const chunkStats = chunkMeta.buffering[type]; const { sn, cc } = frag; const bufferAppendingStart = self.performance.now(); chunkStats.start = bufferAppendingStart; const fragBuffering = frag.stats.buffering; const partBuffering = part ? part.stats.buffering : null; if (fragBuffering.start === 0) { fragBuffering.start = bufferAppendingStart; } if (partBuffering && partBuffering.start === 0) { partBuffering.start = bufferAppendingStart; } const audioTrack = tracks.audio; let checkTimestampOffset = false; if (type === "audio" && (audioTrack == null ? void 0 : audioTrack.container) === "audio/mpeg") { checkTimestampOffset = !this.lastMpegAudioChunk || chunkMeta.id === 1 || this.lastMpegAudioChunk.sn !== chunkMeta.sn; this.lastMpegAudioChunk = chunkMeta; } const videoTrack = tracks.video; const videoSb = videoTrack == null ? void 0 : videoTrack.buffer; if (videoSb && sn !== "initSegment") { const partOrFrag = part || frag; const blockedAudioAppend = this.blockedAudioAppend; if (type === "audio" && parent !== "main" && !this.blockedAudioAppend) { const pStart = partOrFrag.start; const pTime = pStart + partOrFrag.duration * 0.05; const vbuffered = videoSb.buffered; const vappending = this.currentOp("video"); if (!vbuffered.length && !vappending) { this.blockAudio(partOrFrag); } else if (!vappending && !BufferHelper.isBuffered(videoSb, pTime) && this.lastVideoAppendEnd < pTime) { this.blockAudio(partOrFrag); } } else if (type === "video") { const videoAppendEnd = partOrFrag.end; if (blockedAudioAppend) { const audioStart = blockedAudioAppend.frag.start; if (videoAppendEnd > audioStart || videoAppendEnd < this.lastVideoAppendEnd || BufferHelper.isBuffered(videoSb, audioStart)) { this.unblockAudio(); } } this.lastVideoAppendEnd = videoAppendEnd; } } const fragStart = (part || frag).start; const operation = { label: `append-${type}`, execute: () => { var _this$tracks$type2; chunkStats.executeStart = self.performance.now(); const sb = (_this$tracks$type2 = this.tracks[type]) == null ? void 0 : _this$tracks$type2.buffer; if (sb) { if (checkTimestampOffset) { this.updateTimestampOffset(sb, fragStart, 0.1, type, sn, cc); } else if (offset !== void 0 && isFiniteNumber(offset)) { this.updateTimestampOffset(sb, offset, 1e-6, type, sn, cc); } } this.appendExecutor(data, type); }, onStart: () => { }, onComplete: () => { const end = self.performance.now(); chunkStats.executeEnd = chunkStats.end = end; if (fragBuffering.first === 0) { fragBuffering.first = end; } if (partBuffering && partBuffering.first === 0) { partBuffering.first = end; } const timeRanges = {}; this.sourceBuffers.forEach(([type2, sb]) => { if (type2) { timeRanges[type2] = BufferHelper.getBuffered(sb); } }); this.appendErrors[type] = 0; if (type === "audio" || type === "video") { this.appendErrors.audiovideo = 0; } else { this.appendErrors.audio = 0; this.appendErrors.video = 0; } this.hls.trigger(Events.BUFFER_APPENDED, { type, frag, part, chunkMeta, parent: frag.type, timeRanges }); }, onError: (error) => { var _this$media; const event2 = { type: ErrorTypes.MEDIA_ERROR, parent: frag.type, details: ErrorDetails.BUFFER_APPEND_ERROR, sourceBufferName: type, frag, part, chunkMeta, error, err: error, fatal: false }; const mediaError = (_this$media = this.media) == null ? void 0 : _this$media.error; if (error.code === DOMException.QUOTA_EXCEEDED_ERR || error.name == "QuotaExceededError" || `quota` in error) { event2.details = ErrorDetails.BUFFER_FULL_ERROR; } else if (error.code === DOMException.INVALID_STATE_ERR && this.mediaSourceOpenOrEnded && !mediaError) { event2.errorAction = createDoNothingErrorAction(true); } else if (error.name === TRACK_REMOVED_ERROR_NAME && this.sourceBufferCount === 0) { event2.errorAction = createDoNothingErrorAction(true); } else { const appendErrorCount = ++this.appendErrors[type]; this.warn(`Failed ${appendErrorCount}/${this.hls.config.appendErrorMaxRetry} times to append segment in "${type}" sourceBuffer (${mediaError ? mediaError : "no media error"})`); if (appendErrorCount >= this.hls.config.appendErrorMaxRetry || !!mediaError) { event2.fatal = true; } } this.hls.trigger(Events.ERROR, event2); } }; this.append(operation, type, this.isPending(this.tracks[type])); } getFlushOp(type, start, end) { this.log(`queuing "${type}" remove ${start}-${end}`); return { label: "remove", execute: () => { this.removeExecutor(type, start, end); }, onStart: () => { }, onComplete: () => { this.hls.trigger(Events.BUFFER_FLUSHED, { type }); }, onError: (error) => { this.warn(`Failed to remove ${start}-${end} from "${type}" SourceBuffer`, error); } }; } onBufferFlushing(event, data) { const { type, startOffset, endOffset } = data; if (type) { this.append(this.getFlushOp(type, startOffset, endOffset), type); } else { this.sourceBuffers.forEach(([type2]) => { if (type2) { this.append(this.getFlushOp(type2, startOffset, endOffset), type2); } }); } } onFragParsed(event, data) { const { frag, part } = data; const buffersAppendedTo = []; const elementaryStreams = part ? part.elementaryStreams : frag.elementaryStreams; if (elementaryStreams[ElementaryStreamTypes.AUDIOVIDEO]) { buffersAppendedTo.push("audiovideo"); } else { if (elementaryStreams[ElementaryStreamTypes.AUDIO]) { buffersAppendedTo.push("audio"); } if (elementaryStreams[ElementaryStreamTypes.VIDEO]) { buffersAppendedTo.push("video"); } } const onUnblocked = () => { const now2 = self.performance.now(); frag.stats.buffering.end = now2; if (part) { part.stats.buffering.end = now2; } const stats = part ? part.stats : frag.stats; this.hls.trigger(Events.FRAG_BUFFERED, { frag, part, stats, id: frag.type }); }; if (buffersAppendedTo.length === 0) { this.warn(`Fragments must have at least one ElementaryStreamType set. type: ${frag.type} level: ${frag.level} sn: ${frag.sn}`); } this.blockBuffers(onUnblocked, buffersAppendedTo).catch((error) => { this.warn(`Fragment buffered callback ${error}`); this.stepOperationQueue(this.sourceBufferTypes); }); } onFragChanged(event, data) { this.trimBuffers(); } get bufferedToEnd() { return this.sourceBufferCount > 0 && !this.sourceBuffers.some(([type]) => { var _this$tracks$type3, _this$tracks$type4; return type && (!((_this$tracks$type3 = this.tracks[type]) != null && _this$tracks$type3.ended) || ((_this$tracks$type4 = this.tracks[type]) == null ? void 0 : _this$tracks$type4.ending)); }); } onBufferEos(event, data) { var _this$overrides; this.sourceBuffers.forEach(([type]) => { if (type) { const track = this.tracks[type]; if (!data.type || data.type === type) { track.ending = true; if (!track.ended) { track.ended = true; this.log(`${type} buffer reached EOS`); } } } }); const allowEndOfStream = ((_this$overrides = this.overrides) == null ? void 0 : _this$overrides.endOfStream) !== false; const allTracksEnding = this.sourceBufferCount > 0 && !this.sourceBuffers.some(([type]) => { var _this$tracks$type5; return type && !((_this$tracks$type5 = this.tracks[type]) != null && _this$tracks$type5.ended); }); if (allTracksEnding) { if (allowEndOfStream) { this.log(`Queueing EOS`); this.blockUntilOpen(() => { this.tracksEnded(); const { mediaSource } = this; if (!mediaSource || mediaSource.readyState !== "open") { if (mediaSource) { this.log(`Could not call mediaSource.endOfStream(). mediaSource.readyState: ${mediaSource.readyState}`); } return; } this.log(`Calling mediaSource.endOfStream()`); mediaSource.endOfStream(); this.hls.trigger(Events.BUFFERED_TO_END, void 0); }); } else { this.tracksEnded(); this.hls.trigger(Events.BUFFERED_TO_END, void 0); } } } tracksEnded() { this.sourceBuffers.forEach(([type]) => { if (type !== null) { const track = this.tracks[type]; if (track) { track.ending = false; } } }); } onLevelUpdated(event, { details }) { if (!details.fragments.length) { return; } this.details = details; this.updateDuration(); } updateDuration() { this.blockUntilOpen(() => { const durationAndRange = this.getDurationAndRange(); if (!durationAndRange) { return; } this.updateMediaSource(durationAndRange); }); } onError(event, data) { if (data.details === ErrorDetails.BUFFER_APPEND_ERROR && data.frag) { var _data$errorAction; const nextAutoLevel = (_data$errorAction = data.errorAction) == null ? void 0 : _data$errorAction.nextAutoLevel; if (isFiniteNumber(nextAutoLevel) && nextAutoLevel !== data.frag.level) { this.resetAppendErrors(); } } } resetAppendErrors() { this.appendErrors = { audio: 0, video: 0, audiovideo: 0 }; } trimBuffers() { const { hls, details, media } = this; if (!media || details === null) { return; } if (!this.sourceBufferCount) { return; } const config = hls.config; const currentTime = media.currentTime; const targetDuration = details.levelTargetDuration; const backBufferLength = details.live && config.liveBackBufferLength !== null ? config.liveBackBufferLength : config.backBufferLength; if (isFiniteNumber(backBufferLength) && backBufferLength >= 0) { const maxBackBufferLength = Math.max(backBufferLength, targetDuration); const targetBackBufferPosition = Math.floor(currentTime / targetDuration) * targetDuration - maxBackBufferLength; this.flushBackBuffer(currentTime, targetDuration, targetBackBufferPosition); } if (isFiniteNumber(config.frontBufferFlushThreshold) && config.frontBufferFlushThreshold > 0) { const frontBufferLength = Math.max(config.maxBufferLength, config.frontBufferFlushThreshold); const maxFrontBufferLength = Math.max(frontBufferLength, targetDuration); const targetFrontBufferPosition = Math.floor(currentTime / targetDuration) * targetDuration + maxFrontBufferLength; this.flushFrontBuffer(currentTime, targetDuration, targetFrontBufferPosition); } } flushBackBuffer(currentTime, targetDuration, targetBackBufferPosition) { this.sourceBuffers.forEach(([type, sb]) => { if (sb) { const buffered = BufferHelper.getBuffered(sb); if (buffered.length > 0 && targetBackBufferPosition > buffered.start(0)) { var _this$details; this.hls.trigger(Events.BACK_BUFFER_REACHED, { bufferEnd: targetBackBufferPosition }); const track = this.tracks[type]; if ((_this$details = this.details) != null && _this$details.live) { this.hls.trigger(Events.LIVE_BACK_BUFFER_REACHED, { bufferEnd: targetBackBufferPosition }); } else if (track != null && track.ended) { this.log(`Cannot flush ${type} back buffer while SourceBuffer is in ended state`); return; } this.hls.trigger(Events.BUFFER_FLUSHING, { startOffset: 0, endOffset: targetBackBufferPosition, type }); } } }); } flushFrontBuffer(currentTime, targetDuration, targetFrontBufferPosition) { this.sourceBuffers.forEach(([type, sb]) => { if (sb) { const buffered = BufferHelper.getBuffered(sb); const numBufferedRanges = buffered.length; if (numBufferedRanges < 2) { return; } const bufferStart = buffered.start(numBufferedRanges - 1); const bufferEnd = buffered.end(numBufferedRanges - 1); if (targetFrontBufferPosition > bufferStart || currentTime >= bufferStart && currentTime <= bufferEnd) { return; } this.hls.trigger(Events.BUFFER_FLUSHING, { startOffset: bufferStart, endOffset: Infinity, type }); } }); } getDurationAndRange() { var _this$overrides2; const { details, mediaSource } = this; if (!details || !this.media || (mediaSource == null ? void 0 : mediaSource.readyState) !== "open") { return null; } const playlistEnd = details.edge; if (details.live && this.hls.config.liveDurationInfinity) { const len = details.fragments.length; if (len && details.live && !!mediaSource.setLiveSeekableRange) { const start = Math.max(0, details.fragmentStart); const end = Math.max(start, playlistEnd); return { duration: Infinity, start, end }; } return { duration: Infinity }; } const overrideDuration = (_this$overrides2 = this.overrides) == null ? void 0 : _this$overrides2.duration; if (overrideDuration) { if (!isFiniteNumber(overrideDuration)) { return null; } return { duration: overrideDuration }; } const mediaDuration = this.media.duration; const msDuration = isFiniteNumber(mediaSource.duration) ? mediaSource.duration : 0; if (playlistEnd > msDuration && playlistEnd > mediaDuration || !isFiniteNumber(mediaDuration)) { return { duration: playlistEnd }; } return null; } updateMediaSource({ duration, start, end }) { const mediaSource = this.mediaSource; if (!this.media || !mediaSource || mediaSource.readyState !== "open") { return; } if (mediaSource.duration !== duration) { if (isFiniteNumber(duration)) { this.log(`Updating MediaSource duration to ${duration.toFixed(3)}`); } mediaSource.duration = duration; } if (start !== void 0 && end !== void 0) { this.log(`MediaSource duration is set to ${mediaSource.duration}. Setting seekable range to ${start}-${end}.`); mediaSource.setLiveSeekableRange(start, end); } } get tracksReady() { const pendingTrackCount = this.pendingTrackCount; return pendingTrackCount > 0 && (pendingTrackCount >= this.bufferCodecEventsTotal || this.isPending(this.tracks.audiovideo)); } checkPendingTracks() { const { bufferCodecEventsTotal, pendingTrackCount, tracks } = this; this.log(`checkPendingTracks (pending: ${pendingTrackCount} codec events expected: ${bufferCodecEventsTotal}) ${stringify(tracks)}`); if (this.tracksReady) { var _this$transferData4; const transferredTracks = (_this$transferData4 = this.transferData) == null ? void 0 : _this$transferData4.tracks; if (transferredTracks && Object.keys(transferredTracks).length) { this.attachTransferred(); } else { this.createSourceBuffers(); } } } bufferCreated() { if (this.sourceBufferCount) { const tracks = {}; this.sourceBuffers.forEach(([type, buffer]) => { if (type) { const track = this.tracks[type]; tracks[type] = { buffer, container: track.container, codec: track.codec, supplemental: track.supplemental, levelCodec: track.levelCodec, id: track.id, metadata: track.metadata }; } }); this.hls.trigger(Events.BUFFER_CREATED, { tracks }); this.log(`SourceBuffers created. Running queue: ${this.operationQueue}`); this.sourceBuffers.forEach(([type]) => { this.executeNext(type); }); } else { const error = new Error("could not create source buffer for media codec(s)"); this.hls.trigger(Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_INCOMPATIBLE_CODECS_ERROR, fatal: true, error, reason: error.message }); } } createSourceBuffers() { const { tracks, sourceBuffers, mediaSource } = this; if (!mediaSource) { throw new Error("createSourceBuffers called when mediaSource was null"); } for (const trackName in tracks) { const type = trackName; const track = tracks[type]; if (this.isPending(track)) { const codec = this.getTrackCodec(track, type); const mimeType = `${track.container};codecs=${codec}`; track.codec = codec; this.log(`creating sourceBuffer(${mimeType})${this.currentOp(type) ? " Queued" : ""} ${stringify(track)}`); try { const sb = mediaSource.addSourceBuffer(mimeType); const sbIndex = sourceBufferNameToIndex(type); const sbTuple = [type, sb]; sourceBuffers[sbIndex] = sbTuple; track.buffer = sb; } catch (error) { var _this$operationQueue; this.error(`error while trying to add sourceBuffer: ${error.message}`); this.shiftAndExecuteNext(type); (_this$operationQueue = this.operationQueue) == null ? void 0 : _this$operationQueue.removeBlockers(); delete this.tracks[type]; this.hls.trigger(Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_ADD_CODEC_ERROR, fatal: false, error, sourceBufferName: type, mimeType, parent: track.id }); return; } this.trackSourceBuffer(type, track); } } this.bufferCreated(); } getTrackCodec(track, trackName) { const supplementalCodec = track.supplemental; let trackCodec = track.codec; if (supplementalCodec && (trackName === "video" || trackName === "audiovideo") && areCodecsMediaSourceSupported(supplementalCodec, "video")) { trackCodec = replaceVideoCodec(trackCodec, supplementalCodec); } const codec = pickMostCompleteCodecName(trackCodec, track.levelCodec); if (codec) { if (trackName.slice(0, 5) === "audio") { return getCodecCompatibleName(codec, this.appendSource); } return codec; } return ""; } trackSourceBuffer(type, track) { const buffer = track.buffer; if (!buffer) { return; } const codec = this.getTrackCodec(track, type); this.tracks[type] = { buffer, codec, container: track.container, levelCodec: track.levelCodec, supplemental: track.supplemental, metadata: track.metadata, id: track.id, listeners: [] }; this.removeBufferListeners(type); this.addBufferListener(type, "updatestart", this.onSBUpdateStart); this.addBufferListener(type, "updateend", this.onSBUpdateEnd); this.addBufferListener(type, "error", this.onSBUpdateError); if (this.appendSource) { this.addBufferListener(type, "bufferedchange", (type2, event) => { const removedRanges = event.removedRanges; if (removedRanges != null && removedRanges.length) { this.hls.trigger(Events.BUFFER_FLUSHED, { type: type2 }); } }); } } get mediaSrc() { var _this$media2, _this$media2$querySel; const media = ((_this$media2 = this.media) == null ? void 0 : (_this$media2$querySel = _this$media2.querySelector) == null ? void 0 : _this$media2$querySel.call(_this$media2, "source")) || this.media; return media == null ? void 0 : media.src; } onSBUpdateStart(type) { const operation = this.currentOp(type); if (!operation) { return; } operation.onStart(); } onSBUpdateEnd(type) { var _this$mediaSource4; if (((_this$mediaSource4 = this.mediaSource) == null ? void 0 : _this$mediaSource4.readyState) === "closed") { this.resetBuffer(type); return; } const operation = this.currentOp(type); if (!operation) { return; } operation.onComplete(); this.shiftAndExecuteNext(type); } onSBUpdateError(type, event) { var _this$mediaSource5; const error = new Error(`${type} SourceBuffer error. MediaSource readyState: ${(_this$mediaSource5 = this.mediaSource) == null ? void 0 : _this$mediaSource5.readyState}`); this.error(`${error}`, event); this.hls.trigger(Events.ERROR, { type: ErrorTypes.MEDIA_ERROR, details: ErrorDetails.BUFFER_APPENDING_ERROR, sourceBufferName: type, error, fatal: false }); const operation = this.currentOp(type); if (operation) { operation.onError(error); } } updateTimestampOffset(sb, timestampOffset, tolerance, type, sn, cc) { const delta = timestampOffset - sb.timestampOffset; if (Math.abs(delta) >= tolerance) { this.log(`Updating ${type} SourceBuffer timestampOffset to ${timestampOffset} (sn: ${sn} cc: ${cc})`); sb.timestampOffset = timestampOffset; } } removeExecutor(type, startOffset, endOffset) { const { media, mediaSource } = this; const track = this.tracks[type]; const sb = track == null ? void 0 : track.buffer; if (!media || !mediaSource || !sb) { this.warn(`Attempting to remove from the ${type} SourceBuffer, but it does not exist`); this.shiftAndExecuteNext(type); return; } const mediaDuration = isFiniteNumber(media.duration) ? media.duration : Infinity; const msDuration = isFiniteNumber(mediaSource.duration) ? mediaSource.duration : Infinity; const removeStart = Math.max(0, startOffset); const removeEnd = Math.min(endOffset, mediaDuration, msDuration); if (removeEnd > removeStart && (!track.ending || track.ended)) { track.ended = false; this.log(`Removing [${removeStart},${removeEnd}] from the ${type} SourceBuffer`); sb.remove(removeStart, removeEnd); } else { this.shiftAndExecuteNext(type); } } appendExecutor(data, type) { const track = this.tracks[type]; const sb = track == null ? void 0 : track.buffer; if (!sb) { throw new HlsJsTrackRemovedError(`Attempting to append to the ${type} SourceBuffer, but it does not exist`); } track.ending = false; track.ended = false; sb.appendBuffer(data); } blockUntilOpen(callback) { if (this.isUpdating() || this.isQueued()) { this.blockBuffers(callback).catch((error) => { this.warn(`SourceBuffer blocked callback ${error}`); this.stepOperationQueue(this.sourceBufferTypes); }); } else { try { callback(); } catch (error) { this.warn(`Callback run without blocking ${this.operationQueue} ${error}`); } } } isUpdating() { return this.sourceBuffers.some(([type, sb]) => type && sb.updating); } isQueued() { return this.sourceBuffers.some(([type]) => type && !!this.currentOp(type)); } isPending(track) { return !!track && !track.buffer; } blockBuffers(onUnblocked, bufferNames = this.sourceBufferTypes) { if (!bufferNames.length) { this.log("Blocking operation requested, but no SourceBuffers exist"); return Promise.resolve().then(onUnblocked); } const { operationQueue } = this; const blockingOperations = bufferNames.map((type) => this.appendBlocker(type)); const audioBlocked = bufferNames.length > 1 && !!this.blockedAudioAppend; if (audioBlocked) { this.unblockAudio(); } return Promise.all(blockingOperations).then((result) => { if (operationQueue !== this.operationQueue) { return; } onUnblocked(); this.stepOperationQueue(this.sourceBufferTypes); }); } stepOperationQueue(bufferNames) { bufferNames.forEach((type) => { var _this$tracks$type6; const sb = (_this$tracks$type6 = this.tracks[type]) == null ? void 0 : _this$tracks$type6.buffer; if (!sb || sb.updating) { return; } this.shiftAndExecuteNext(type); }); } append(operation, type, pending) { if (this.operationQueue) { this.operationQueue.append(operation, type, pending); } } appendBlocker(type) { if (this.operationQueue) { return this.operationQueue.appendBlocker(type); } } currentOp(type) { if (this.operationQueue) { return this.operationQueue.current(type); } return null; } executeNext(type) { if (type && this.operationQueue) { this.operationQueue.executeNext(type); } } shiftAndExecuteNext(type) { if (this.operationQueue) { this.operationQueue.shiftAndExecuteNext(type); } } get pendingTrackCount() { return Object.keys(this.tracks).reduce((acc, type) => acc + (this.isPending(this.tracks[type]) ? 1 : 0), 0); } get sourceBufferCount() { return this.sourceBuffers.reduce((acc, [type]) => acc + (type ? 1 : 0), 0); } get sourceBufferTypes() { return this.sourceBuffers.map(([type]) => type).filter((type) => !!type); } addBufferListener(type, event, fn) { const track = this.tracks[type]; if (!track) { return; } const buffer = track.buffer; if (!buffer) { return; } const listener = fn.bind(this, type); track.listeners.push({ event, listener }); buffer.addEventListener(event, listener); } removeBufferListeners(type) { const track = this.tracks[type]; if (!track) { return; } const buffer = track.buffer; if (!buffer) { return; } track.listeners.forEach((l) => { buffer.removeEventListener(l.event, l.listener); }); track.listeners.length = 0; } }; function removeSourceChildren(node) { const sourceChildren = node.querySelectorAll("source"); [].slice.call(sourceChildren).forEach((source) => { node.removeChild(source); }); } function addSource(media, url) { const source = self.document.createElement("source"); source.type = "video/mp4"; source.src = url; media.appendChild(source); } function sourceBufferNameToIndex(type) { return type === "audio" ? 1 : 0; } var CapLevelController = class { constructor(hls) { this.hls = void 0; this.autoLevelCapping = void 0; this.firstLevel = void 0; this.media = void 0; this.restrictedLevels = void 0; this.timer = void 0; this.clientRect = void 0; this.streamController = void 0; this.hls = hls; this.autoLevelCapping = Number.POSITIVE_INFINITY; this.firstLevel = -1; this.media = null; this.restrictedLevels = []; this.timer = void 0; this.clientRect = null; this.registerListeners(); } setStreamController(streamController) { this.streamController = streamController; } destroy() { if (this.hls) { this.unregisterListener(); } if (this.timer) { this.stopCapping(); } this.media = null; this.clientRect = null; this.hls = this.streamController = null; } registerListeners() { const { hls } = this; hls.on(Events.FPS_DROP_LEVEL_CAPPING, this.onFpsDropLevelCapping, this); hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); hls.on(Events.BUFFER_CODECS, this.onBufferCodecs, this); hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); } unregisterListener() { const { hls } = this; hls.off(Events.FPS_DROP_LEVEL_CAPPING, this.onFpsDropLevelCapping, this); hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this); hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); } onFpsDropLevelCapping(event, data) { const level = this.hls.levels[data.droppedLevel]; if (this.isLevelAllowed(level)) { this.restrictedLevels.push({ bitrate: level.bitrate, height: level.height, width: level.width }); } } onMediaAttaching(event, data) { this.media = data.media instanceof HTMLVideoElement ? data.media : null; this.clientRect = null; if (this.timer && this.hls.levels.length) { this.detectPlayerSize(); } } onManifestParsed(event, data) { const hls = this.hls; this.restrictedLevels = []; this.firstLevel = data.firstLevel; if (hls.config.capLevelToPlayerSize && data.video) { this.startCapping(); } } onLevelsUpdated(event, data) { if (this.timer && isFiniteNumber(this.autoLevelCapping)) { this.detectPlayerSize(); } } onBufferCodecs(event, data) { const hls = this.hls; if (hls.config.capLevelToPlayerSize && data.video) { this.startCapping(); } } onMediaDetaching() { this.stopCapping(); this.media = null; } detectPlayerSize() { if (this.media) { if (this.mediaHeight <= 0 || this.mediaWidth <= 0) { this.clientRect = null; return; } const levels = this.hls.levels; if (levels.length) { const hls = this.hls; const maxLevel = this.getMaxLevel(levels.length - 1); if (maxLevel !== this.autoLevelCapping) { hls.logger.log(`Setting autoLevelCapping to ${maxLevel}: ${levels[maxLevel].height}p@${levels[maxLevel].bitrate} for media ${this.mediaWidth}x${this.mediaHeight}`); } hls.autoLevelCapping = maxLevel; if (hls.autoLevelEnabled && hls.autoLevelCapping > this.autoLevelCapping && this.streamController) { this.streamController.nextLevelSwitch(); } this.autoLevelCapping = hls.autoLevelCapping; } } } getMaxLevel(capLevelIndex) { const levels = this.hls.levels; if (!levels.length) { return -1; } const validLevels = levels.filter((level, index) => this.isLevelAllowed(level) && index <= capLevelIndex); this.clientRect = null; return CapLevelController.getMaxLevelByMediaSize(validLevels, this.mediaWidth, this.mediaHeight); } startCapping() { if (this.timer) { return; } this.autoLevelCapping = Number.POSITIVE_INFINITY; self.clearInterval(this.timer); this.timer = self.setInterval(this.detectPlayerSize.bind(this), 1e3); this.detectPlayerSize(); } stopCapping() { this.restrictedLevels = []; this.firstLevel = -1; this.autoLevelCapping = Number.POSITIVE_INFINITY; if (this.timer) { self.clearInterval(this.timer); this.timer = void 0; } } getDimensions() { if (this.clientRect) { return this.clientRect; } const media = this.media; const boundsRect = { width: 0, height: 0 }; if (media) { const clientRect = media.getBoundingClientRect(); boundsRect.width = clientRect.width; boundsRect.height = clientRect.height; if (!boundsRect.width && !boundsRect.height) { boundsRect.width = clientRect.right - clientRect.left || media.width || 0; boundsRect.height = clientRect.bottom - clientRect.top || media.height || 0; } } this.clientRect = boundsRect; return boundsRect; } get mediaWidth() { return this.getDimensions().width * this.contentScaleFactor; } get mediaHeight() { return this.getDimensions().height * this.contentScaleFactor; } get contentScaleFactor() { let pixelRatio = 1; if (!this.hls.config.ignoreDevicePixelRatio) { try { pixelRatio = self.devicePixelRatio; } catch (e) { } } return Math.min(pixelRatio, this.hls.config.maxDevicePixelRatio); } isLevelAllowed(level) { const restrictedLevels = this.restrictedLevels; return !restrictedLevels.some((restrictedLevel) => { return level.bitrate === restrictedLevel.bitrate && level.width === restrictedLevel.width && level.height === restrictedLevel.height; }); } static getMaxLevelByMediaSize(levels, width, height) { if (!(levels != null && levels.length)) { return -1; } const atGreatestBandwidth = (curLevel, nextLevel) => { if (!nextLevel) { return true; } return curLevel.width !== nextLevel.width || curLevel.height !== nextLevel.height; }; let maxLevelIndex = levels.length - 1; const squareSize = Math.max(width, height); for (let i = 0; i < levels.length; i += 1) { const level = levels[i]; if ((level.width >= squareSize || level.height >= squareSize) && atGreatestBandwidth(level, levels[i + 1])) { maxLevelIndex = i; break; } } return maxLevelIndex; } }; var CmObjectType = { MANIFEST: "m", AUDIO: "a", VIDEO: "v", MUXED: "av", INIT: "i", CAPTION: "c", TIMED_TEXT: "tt", KEY: "k", OTHER: "o" }; var CmcdObjectType = CmObjectType; var CmStreamingFormat = { HLS: "h" }; var CmcdStreamingFormat = CmStreamingFormat; var CMCD_OBJECT = "CMCD-Object"; var CMCD_REQUEST = "CMCD-Request"; var CMCD_SESSION = "CMCD-Session"; var CMCD_STATUS = "CMCD-Status"; var CmcdHeaderField = { OBJECT: CMCD_OBJECT, REQUEST: CMCD_REQUEST, SESSION: CMCD_SESSION, STATUS: CMCD_STATUS }; var CmcdHeaderMap = { [CmcdHeaderField.OBJECT]: ["br", "ab", "d", "ot", "tb", "tpb", "lb", "tab", "lab", "url"], [CmcdHeaderField.REQUEST]: ["pb", "bl", "tbl", "dl", "ltc", "mtp", "nor", "nrr", "rc", "sn", "sta", "su", "ttfb", "ttfbb", "ttlb", "cmsdd", "cmsds", "smrt", "df", "cs"], [CmcdHeaderField.SESSION]: ["cid", "pr", "sf", "sid", "st", "v", "msd"], [CmcdHeaderField.STATUS]: ["bs", "bsd", "cdn", "rtp", "bg", "pt", "ec", "e"] }; var SfItem = class { constructor(value, params) { if (Array.isArray(value)) { value = value.map((v) => v instanceof SfItem ? v : new SfItem(v)); } this.value = value; this.params = params; } }; var DICT = "Dict"; function format(value) { if (Array.isArray(value)) { return JSON.stringify(value); } if (value instanceof Map) { return "Map{}"; } if (value instanceof Set) { return "Set{}"; } if (typeof value === "object") { return JSON.stringify(value); } return String(value); } function throwError(action, src, type, cause) { return new Error(`failed to ${action} "${format(src)}" as ${type}`, { cause }); } function serializeError(src, type, cause) { return throwError("serialize", src, type, cause); } var SfToken = class { constructor(description) { this.description = description; } }; var BARE_ITEM = "Bare Item"; var BOOLEAN = "Boolean"; function serializeBoolean(value) { if (typeof value !== "boolean") { throw serializeError(value, BOOLEAN); } return value ? "?1" : "?0"; } function encodeBase64(binary) { return btoa(String.fromCharCode(...binary)); } var BYTES = "Byte Sequence"; function serializeByteSequence(value) { if (ArrayBuffer.isView(value) === false) { throw serializeError(value, BYTES); } return `:${encodeBase64(value)}:`; } var INTEGER = "Integer"; function isInvalidInt(value) { return value < -999999999999999 || 999999999999999 < value; } function serializeInteger(value) { if (isInvalidInt(value)) { throw serializeError(value, INTEGER); } return value.toString(); } function serializeDate(value) { return `@${serializeInteger(value.getTime() / 1e3)}`; } function roundToEven(value, precision) { if (value < 0) { return -roundToEven(-value, precision); } const decimalShift = Math.pow(10, precision); const isEquidistant = Math.abs(value * decimalShift % 1 - 0.5) < Number.EPSILON; if (isEquidistant) { const flooredValue = Math.floor(value * decimalShift); return (flooredValue % 2 === 0 ? flooredValue : flooredValue + 1) / decimalShift; } else { return Math.round(value * decimalShift) / decimalShift; } } var DECIMAL = "Decimal"; function serializeDecimal(value) { const roundedValue = roundToEven(value, 3); if (Math.floor(Math.abs(roundedValue)).toString().length > 12) { throw serializeError(value, DECIMAL); } const stringValue = roundedValue.toString(); return stringValue.includes(".") ? stringValue : `${stringValue}.0`; } var STRING = "String"; var STRING_REGEX = /[\x00-\x1f\x7f]+/; function serializeString(value) { if (STRING_REGEX.test(value)) { throw serializeError(value, STRING); } return `"${value.replace(/\\/g, `\\\\`).replace(/"/g, `\\"`)}"`; } function symbolToStr(symbol) { return symbol.description || symbol.toString().slice(7, -1); } var TOKEN = "Token"; function serializeToken(token) { const value = symbolToStr(token); if (/^([a-zA-Z*])([!#$%&'*+\-.^_`|~\w:/]*)$/.test(value) === false) { throw serializeError(value, TOKEN); } return value; } function serializeBareItem(value) { switch (typeof value) { case "number": if (!isFiniteNumber(value)) { throw serializeError(value, BARE_ITEM); } if (Number.isInteger(value)) { return serializeInteger(value); } return serializeDecimal(value); case "string": return serializeString(value); case "symbol": return serializeToken(value); case "boolean": return serializeBoolean(value); case "object": if (value instanceof Date) { return serializeDate(value); } if (value instanceof Uint8Array) { return serializeByteSequence(value); } if (value instanceof SfToken) { return serializeToken(value); } default: throw serializeError(value, BARE_ITEM); } } var KEY = "Key"; function serializeKey(value) { if (/^[a-z*][a-z0-9\-_.*]*$/.test(value) === false) { throw serializeError(value, KEY); } return value; } function serializeParams(params) { if (params == null) { return ""; } return Object.entries(params).map(([key, value]) => { if (value === true) { return `;${serializeKey(key)}`; } return `;${serializeKey(key)}=${serializeBareItem(value)}`; }).join(""); } function serializeItem(value) { if (value instanceof SfItem) { return `${serializeBareItem(value.value)}${serializeParams(value.params)}`; } else { return serializeBareItem(value); } } function serializeInnerList(value) { return `(${value.value.map(serializeItem).join(" ")})${serializeParams(value.params)}`; } function serializeDict(dict, options = { whitespace: true }) { if (typeof dict !== "object") { throw serializeError(dict, DICT); } const entries = dict instanceof Map ? dict.entries() : Object.entries(dict); const optionalWhiteSpace = (options === null || options === void 0 ? void 0 : options.whitespace) ? " " : ""; return Array.from(entries).map(([key, item]) => { if (item instanceof SfItem === false) { item = new SfItem(item); } let output = serializeKey(key); if (item.value === true) { output += serializeParams(item.params); } else { output += "="; if (Array.isArray(item.value)) { output += serializeInnerList(item); } else { output += serializeItem(item); } } return output; }).join(`,${optionalWhiteSpace}`); } function encodeSfDict(value, options) { return serializeDict(value, options); } function isTokenField(key) { return ["ot", "sf", "st", "e", "sta"].includes(key); } function isValid(value) { if (typeof value === "number") { return isFiniteNumber(value); } return value != null && value !== "" && value !== false; } function urlToRelativePath(url, base) { const to = new URL(url); const from = new URL(base); if (to.origin !== from.origin) { return url; } const toPath = to.pathname.split("/").slice(1); const fromPath = from.pathname.split("/").slice(1, -1); while (toPath[0] === fromPath[0]) { toPath.shift(); fromPath.shift(); } while (fromPath.length) { fromPath.shift(); toPath.unshift(".."); } return toPath.join("/"); } var toRounded = (value) => Math.round(value); var toUrlSafe = (value, options) => { if (options === null || options === void 0 ? void 0 : options.baseUrl) { value = urlToRelativePath(value, options.baseUrl); } return encodeURIComponent(value); }; var toHundred = (value) => toRounded(value / 100) * 100; var CmcdFormatters = { br: toRounded, d: toRounded, bl: toHundred, dl: toHundred, mtp: toHundred, nor: toUrlSafe, rtp: toHundred, tb: toRounded }; function processCmcd(obj, options) { const results = {}; if (obj == null || typeof obj !== "object") { return results; } const keys = Object.keys(obj).sort(); const formatters = _extends({}, CmcdFormatters, options === null || options === void 0 ? void 0 : options.formatters); const filter = options === null || options === void 0 ? void 0 : options.filter; keys.forEach((key) => { if ((filter === null || filter === void 0 ? void 0 : filter(key)) === false) { return; } let value = obj[key]; const formatter = formatters[key]; if (formatter) { value = formatter(value, options); } if (key === "v" && value === 1) { return; } if (key == "pr" && value === 1) { return; } if (!isValid(value)) { return; } if (isTokenField(key) && typeof value === "string") { value = new SfToken(value); } results[key] = value; }); return results; } function encodeCmcd(cmcd, options = {}) { if (!cmcd) { return ""; } return encodeSfDict(processCmcd(cmcd, options), _extends({ whitespace: false }, options)); } function toCmcdHeaders(cmcd, options = {}) { const result = {}; if (!cmcd) { return result; } const entries = Object.entries(cmcd); const headerMap = Object.entries(CmcdHeaderMap).concat(Object.entries((options === null || options === void 0 ? void 0 : options.customHeaderMap) || {})); const shards = entries.reduce((acc, entry) => { var _a, _b; const [key, value] = entry; const field = ((_a = headerMap.find((entry2) => entry2[1].includes(key))) === null || _a === void 0 ? void 0 : _a[0]) || CmcdHeaderField.REQUEST; (_b = acc[field]) !== null && _b !== void 0 ? _b : acc[field] = {}; acc[field][key] = value; return acc; }, {}); return Object.entries(shards).reduce((acc, [field, value]) => { acc[field] = encodeCmcd(value, options); return acc; }, result); } function appendCmcdHeaders(headers, cmcd, options) { return _extends(headers, toCmcdHeaders(cmcd, options)); } var CMCD_PARAM = "CMCD"; function toCmcdQuery(cmcd, options = {}) { if (!cmcd) { return ""; } const params = encodeCmcd(cmcd, options); return `${CMCD_PARAM}=${encodeURIComponent(params)}`; } var REGEX = /CMCD=[^&#]+/; function appendCmcdQuery(url, cmcd, options) { const query = toCmcdQuery(cmcd, options); if (!query) { return url; } if (REGEX.test(url)) { return url.replace(REGEX, query); } const separator = url.includes("?") ? "&" : "?"; return `${url}${separator}${query}`; } var CMCDController = class { constructor(hls) { this.hls = void 0; this.config = void 0; this.media = void 0; this.sid = void 0; this.cid = void 0; this.useHeaders = false; this.includeKeys = void 0; this.initialized = false; this.starved = false; this.buffering = true; this.audioBuffer = void 0; this.videoBuffer = void 0; this.onWaiting = () => { if (this.initialized) { this.starved = true; } this.buffering = true; }; this.onPlaying = () => { if (!this.initialized) { this.initialized = true; } this.buffering = false; }; this.applyPlaylistData = (context) => { try { this.apply(context, { ot: CmcdObjectType.MANIFEST, su: !this.initialized }); } catch (error) { this.hls.logger.warn("Could not generate manifest CMCD data.", error); } }; this.applyFragmentData = (context) => { try { const { frag, part } = context; const level = this.hls.levels[frag.level]; const ot = this.getObjectType(frag); const data = { d: (part || frag).duration * 1e3, ot }; if (ot === CmcdObjectType.VIDEO || ot === CmcdObjectType.AUDIO || ot == CmcdObjectType.MUXED) { data.br = level.bitrate / 1e3; data.tb = this.getTopBandwidth(ot) / 1e3; data.bl = this.getBufferLength(ot); } const next = part ? this.getNextPart(part) : this.getNextFrag(frag); if (next != null && next.url && next.url !== frag.url) { data.nor = next.url; } this.apply(context, data); } catch (error) { this.hls.logger.warn("Could not generate segment CMCD data.", error); } }; this.hls = hls; const config = this.config = hls.config; const { cmcd } = config; if (cmcd != null) { config.pLoader = this.createPlaylistLoader(); config.fLoader = this.createFragmentLoader(); this.sid = cmcd.sessionId || hls.sessionId; this.cid = cmcd.contentId; this.useHeaders = cmcd.useHeaders === true; this.includeKeys = cmcd.includeKeys; this.registerListeners(); } } registerListeners() { const hls = this.hls; hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); hls.on(Events.MEDIA_DETACHED, this.onMediaDetached, this); hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this); } unregisterListeners() { const hls = this.hls; hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); hls.off(Events.MEDIA_DETACHED, this.onMediaDetached, this); hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this); } destroy() { this.unregisterListeners(); this.onMediaDetached(); this.hls = this.config = this.audioBuffer = this.videoBuffer = null; this.onWaiting = this.onPlaying = this.media = null; } onMediaAttached(event, data) { this.media = data.media; this.media.addEventListener("waiting", this.onWaiting); this.media.addEventListener("playing", this.onPlaying); } onMediaDetached() { if (!this.media) { return; } this.media.removeEventListener("waiting", this.onWaiting); this.media.removeEventListener("playing", this.onPlaying); this.media = null; } onBufferCreated(event, data) { var _data$tracks$audio, _data$tracks$video; this.audioBuffer = (_data$tracks$audio = data.tracks.audio) == null ? void 0 : _data$tracks$audio.buffer; this.videoBuffer = (_data$tracks$video = data.tracks.video) == null ? void 0 : _data$tracks$video.buffer; } createData() { var _this$media; return { v: 1, sf: CmcdStreamingFormat.HLS, sid: this.sid, cid: this.cid, pr: (_this$media = this.media) == null ? void 0 : _this$media.playbackRate, mtp: this.hls.bandwidthEstimate / 1e3 }; } apply(context, data = {}) { _extends(data, this.createData()); const isVideo = data.ot === CmcdObjectType.INIT || data.ot === CmcdObjectType.VIDEO || data.ot === CmcdObjectType.MUXED; if (this.starved && isVideo) { data.bs = true; data.su = true; this.starved = false; } if (data.su == null) { data.su = this.buffering; } const { includeKeys } = this; if (includeKeys) { data = Object.keys(data).reduce((acc, key) => { includeKeys.includes(key) && (acc[key] = data[key]); return acc; }, {}); } const options = { baseUrl: context.url }; if (this.useHeaders) { if (!context.headers) { context.headers = {}; } appendCmcdHeaders(context.headers, data, options); } else { context.url = appendCmcdQuery(context.url, data, options); } } getNextFrag(fragment) { var _this$hls$levels$frag; const levelDetails = (_this$hls$levels$frag = this.hls.levels[fragment.level]) == null ? void 0 : _this$hls$levels$frag.details; if (levelDetails) { const index = fragment.sn - levelDetails.startSN; return levelDetails.fragments[index + 1]; } return void 0; } getNextPart(part) { var _this$hls$levels$frag2, _this$hls$levels$frag3; const { index, fragment } = part; const partList = (_this$hls$levels$frag2 = this.hls.levels[fragment.level]) == null ? void 0 : (_this$hls$levels$frag3 = _this$hls$levels$frag2.details) == null ? void 0 : _this$hls$levels$frag3.partList; if (partList) { const { sn } = fragment; for (let i = partList.length - 1; i >= 0; i--) { const p = partList[i]; if (p.index === index && p.fragment.sn === sn) { return partList[i + 1]; } } } return void 0; } getObjectType(fragment) { const { type } = fragment; if (type === "subtitle") { return CmcdObjectType.TIMED_TEXT; } if (fragment.sn === "initSegment") { return CmcdObjectType.INIT; } if (type === "audio") { return CmcdObjectType.AUDIO; } if (type === "main") { if (!this.hls.audioTracks.length) { return CmcdObjectType.MUXED; } return CmcdObjectType.VIDEO; } return void 0; } getTopBandwidth(type) { let bitrate = 0; let levels; const hls = this.hls; if (type === CmcdObjectType.AUDIO) { levels = hls.audioTracks; } else { const max = hls.maxAutoLevel; const len = max > -1 ? max + 1 : hls.levels.length; levels = hls.levels.slice(0, len); } levels.forEach((level) => { if (level.bitrate > bitrate) { bitrate = level.bitrate; } }); return bitrate > 0 ? bitrate : NaN; } getBufferLength(type) { const media = this.media; const buffer = type === CmcdObjectType.AUDIO ? this.audioBuffer : this.videoBuffer; if (!buffer || !media) { return NaN; } const info = BufferHelper.bufferInfo(buffer, media.currentTime, this.config.maxBufferHole); return info.len * 1e3; } createPlaylistLoader() { const { pLoader } = this.config; const apply = this.applyPlaylistData; const Ctor = pLoader || this.config.loader; return class CmcdPlaylistLoader { constructor(config) { this.loader = void 0; this.loader = new Ctor(config); } get stats() { return this.loader.stats; } get context() { return this.loader.context; } destroy() { this.loader.destroy(); } abort() { this.loader.abort(); } load(context, config, callbacks) { apply(context); this.loader.load(context, config, callbacks); } }; } createFragmentLoader() { const { fLoader } = this.config; const apply = this.applyFragmentData; const Ctor = fLoader || this.config.loader; return class CmcdFragmentLoader { constructor(config) { this.loader = void 0; this.loader = new Ctor(config); } get stats() { return this.loader.stats; } get context() { return this.loader.context; } destroy() { this.loader.destroy(); } abort() { this.loader.abort(); } load(context, config, callbacks) { apply(context); this.loader.load(context, config, callbacks); } }; } }; var PATHWAY_PENALTY_DURATION_MS = 3e5; var ContentSteeringController = class extends Logger { constructor(hls) { super("content-steering", hls.logger); this.hls = void 0; this.loader = null; this.uri = null; this.pathwayId = "."; this._pathwayPriority = null; this.timeToLoad = 300; this.reloadTimer = -1; this.updated = 0; this.started = false; this.enabled = true; this.levels = null; this.audioTracks = null; this.subtitleTracks = null; this.penalizedPathways = {}; this.hls = hls; this.registerListeners(); } registerListeners() { const hls = this.hls; hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this); hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); hls.on(Events.ERROR, this.onError, this); } unregisterListeners() { const hls = this.hls; if (!hls) { return; } hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this); hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); hls.off(Events.ERROR, this.onError, this); } pathways() { return (this.levels || []).reduce((pathways, level) => { if (pathways.indexOf(level.pathwayId) === -1) { pathways.push(level.pathwayId); } return pathways; }, []); } get pathwayPriority() { return this._pathwayPriority; } set pathwayPriority(pathwayPriority) { this.updatePathwayPriority(pathwayPriority); } startLoad() { this.started = true; this.clearTimeout(); if (this.enabled && this.uri) { if (this.updated) { const ttl = this.timeToLoad * 1e3 - (performance.now() - this.updated); if (ttl > 0) { this.scheduleRefresh(this.uri, ttl); return; } } this.loadSteeringManifest(this.uri); } } stopLoad() { this.started = false; if (this.loader) { this.loader.destroy(); this.loader = null; } this.clearTimeout(); } clearTimeout() { if (this.reloadTimer !== -1) { self.clearTimeout(this.reloadTimer); this.reloadTimer = -1; } } destroy() { this.unregisterListeners(); this.stopLoad(); this.hls = null; this.levels = this.audioTracks = this.subtitleTracks = null; } removeLevel(levelToRemove) { const levels = this.levels; if (levels) { this.levels = levels.filter((level) => level !== levelToRemove); } } onManifestLoading() { this.stopLoad(); this.enabled = true; this.timeToLoad = 300; this.updated = 0; this.uri = null; this.pathwayId = "."; this.levels = this.audioTracks = this.subtitleTracks = null; } onManifestLoaded(event, data) { const { contentSteering } = data; if (contentSteering === null) { return; } this.pathwayId = contentSteering.pathwayId; this.uri = contentSteering.uri; if (this.started) { this.startLoad(); } } onManifestParsed(event, data) { this.audioTracks = data.audioTracks; this.subtitleTracks = data.subtitleTracks; } onError(event, data) { const { errorAction } = data; if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox && errorAction.flags === ErrorActionFlags.MoveAllAlternatesMatchingHost) { const levels = this.levels; let pathwayPriority = this._pathwayPriority; let errorPathway = this.pathwayId; if (data.context) { const { groupId, pathwayId, type } = data.context; if (groupId && levels) { errorPathway = this.getPathwayForGroupId(groupId, type, errorPathway); } else if (pathwayId) { errorPathway = pathwayId; } } if (!(errorPathway in this.penalizedPathways)) { this.penalizedPathways[errorPathway] = performance.now(); } if (!pathwayPriority && levels) { pathwayPriority = this.pathways(); } if (pathwayPriority && pathwayPriority.length > 1) { this.updatePathwayPriority(pathwayPriority); errorAction.resolved = this.pathwayId !== errorPathway; } if (data.details === ErrorDetails.BUFFER_APPEND_ERROR && !data.fatal) { errorAction.resolved = true; } else if (!errorAction.resolved) { this.warn(`Could not resolve ${data.details} ("${data.error.message}") with content-steering for Pathway: ${errorPathway} levels: ${levels ? levels.length : levels} priorities: ${stringify(pathwayPriority)} penalized: ${stringify(this.penalizedPathways)}`); } } } filterParsedLevels(levels) { this.levels = levels; let pathwayLevels = this.getLevelsForPathway(this.pathwayId); if (pathwayLevels.length === 0) { const pathwayId = levels[0].pathwayId; this.log(`No levels found in Pathway ${this.pathwayId}. Setting initial Pathway to "${pathwayId}"`); pathwayLevels = this.getLevelsForPathway(pathwayId); this.pathwayId = pathwayId; } if (pathwayLevels.length !== levels.length) { this.log(`Found ${pathwayLevels.length}/${levels.length} levels in Pathway "${this.pathwayId}"`); } return pathwayLevels; } getLevelsForPathway(pathwayId) { if (this.levels === null) { return []; } return this.levels.filter((level) => pathwayId === level.pathwayId); } updatePathwayPriority(pathwayPriority) { this._pathwayPriority = pathwayPriority; let levels; const penalizedPathways = this.penalizedPathways; const now2 = performance.now(); Object.keys(penalizedPathways).forEach((pathwayId) => { if (now2 - penalizedPathways[pathwayId] > PATHWAY_PENALTY_DURATION_MS) { delete penalizedPathways[pathwayId]; } }); for (let i = 0; i < pathwayPriority.length; i++) { const pathwayId = pathwayPriority[i]; if (pathwayId in penalizedPathways) { continue; } if (pathwayId === this.pathwayId) { return; } const selectedIndex = this.hls.nextLoadLevel; const selectedLevel = this.hls.levels[selectedIndex]; levels = this.getLevelsForPathway(pathwayId); if (levels.length > 0) { this.log(`Setting Pathway to "${pathwayId}"`); this.pathwayId = pathwayId; reassignFragmentLevelIndexes(levels); this.hls.trigger(Events.LEVELS_UPDATED, { levels }); const levelAfterChange = this.hls.levels[selectedIndex]; if (selectedLevel && levelAfterChange && this.levels) { if (levelAfterChange.attrs["STABLE-VARIANT-ID"] !== selectedLevel.attrs["STABLE-VARIANT-ID"] && levelAfterChange.bitrate !== selectedLevel.bitrate) { this.log(`Unstable Pathways change from bitrate ${selectedLevel.bitrate} to ${levelAfterChange.bitrate}`); } this.hls.nextLoadLevel = selectedIndex; } break; } } } getPathwayForGroupId(groupId, type, defaultPathway) { const levels = this.getLevelsForPathway(defaultPathway).concat(this.levels || []); for (let i = 0; i < levels.length; i++) { if (type === PlaylistContextType.AUDIO_TRACK && levels[i].hasAudioGroup(groupId) || type === PlaylistContextType.SUBTITLE_TRACK && levels[i].hasSubtitleGroup(groupId)) { return levels[i].pathwayId; } } return defaultPathway; } clonePathways(pathwayClones) { const levels = this.levels; if (!levels) { return; } const audioGroupCloneMap = {}; const subtitleGroupCloneMap = {}; pathwayClones.forEach((pathwayClone) => { const { ID: cloneId, "BASE-ID": baseId, "URI-REPLACEMENT": uriReplacement } = pathwayClone; if (levels.some((level) => level.pathwayId === cloneId)) { return; } const clonedVariants = this.getLevelsForPathway(baseId).map((baseLevel) => { const attributes = new AttrList(baseLevel.attrs); attributes["PATHWAY-ID"] = cloneId; const clonedAudioGroupId = attributes.AUDIO && `${attributes.AUDIO}_clone_${cloneId}`; const clonedSubtitleGroupId = attributes.SUBTITLES && `${attributes.SUBTITLES}_clone_${cloneId}`; if (clonedAudioGroupId) { audioGroupCloneMap[attributes.AUDIO] = clonedAudioGroupId; attributes.AUDIO = clonedAudioGroupId; } if (clonedSubtitleGroupId) { subtitleGroupCloneMap[attributes.SUBTITLES] = clonedSubtitleGroupId; attributes.SUBTITLES = clonedSubtitleGroupId; } const url = performUriReplacement(baseLevel.uri, attributes["STABLE-VARIANT-ID"], "PER-VARIANT-URIS", uriReplacement); const clonedLevel = new Level({ attrs: attributes, audioCodec: baseLevel.audioCodec, bitrate: baseLevel.bitrate, height: baseLevel.height, name: baseLevel.name, url, videoCodec: baseLevel.videoCodec, width: baseLevel.width }); if (baseLevel.audioGroups) { for (let i = 1; i < baseLevel.audioGroups.length; i++) { clonedLevel.addGroupId("audio", `${baseLevel.audioGroups[i]}_clone_${cloneId}`); } } if (baseLevel.subtitleGroups) { for (let i = 1; i < baseLevel.subtitleGroups.length; i++) { clonedLevel.addGroupId("text", `${baseLevel.subtitleGroups[i]}_clone_${cloneId}`); } } return clonedLevel; }); levels.push(...clonedVariants); cloneRenditionGroups(this.audioTracks, audioGroupCloneMap, uriReplacement, cloneId); cloneRenditionGroups(this.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId); }); } loadSteeringManifest(uri) { const config = this.hls.config; const Loader = config.loader; if (this.loader) { this.loader.destroy(); } this.loader = new Loader(config); let url; try { url = new self.URL(uri); } catch (error) { this.enabled = false; this.log(`Failed to parse Steering Manifest URI: ${uri}`); return; } if (url.protocol !== "data:") { const throughput = (this.hls.bandwidthEstimate || config.abrEwmaDefaultEstimate) | 0; url.searchParams.set("_HLS_pathway", this.pathwayId); url.searchParams.set("_HLS_throughput", "" + throughput); } const context = { responseType: "json", url: url.href }; const loadPolicy = config.steeringManifestLoadPolicy.default; const legacyRetryCompatibility = loadPolicy.errorRetry || loadPolicy.timeoutRetry || {}; const loaderConfig = { loadPolicy, timeout: loadPolicy.maxLoadTimeMs, maxRetry: legacyRetryCompatibility.maxNumRetry || 0, retryDelay: legacyRetryCompatibility.retryDelayMs || 0, maxRetryDelay: legacyRetryCompatibility.maxRetryDelayMs || 0 }; const callbacks = { onSuccess: (response, stats, context2, networkDetails) => { this.log(`Loaded steering manifest: "${url}"`); const steeringData = response.data; if ((steeringData == null ? void 0 : steeringData.VERSION) !== 1) { this.log(`Steering VERSION ${steeringData.VERSION} not supported!`); return; } this.updated = performance.now(); this.timeToLoad = steeringData.TTL; const { "RELOAD-URI": reloadUri, "PATHWAY-CLONES": pathwayClones, "PATHWAY-PRIORITY": pathwayPriority } = steeringData; if (reloadUri) { try { this.uri = new self.URL(reloadUri, url).href; } catch (error) { this.enabled = false; this.log(`Failed to parse Steering Manifest RELOAD-URI: ${reloadUri}`); return; } } this.scheduleRefresh(this.uri || context2.url); if (pathwayClones) { this.clonePathways(pathwayClones); } const loadedSteeringData = { steeringManifest: steeringData, url: url.toString() }; this.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData); if (pathwayPriority) { this.updatePathwayPriority(pathwayPriority); } }, onError: (error, context2, networkDetails, stats) => { this.log(`Error loading steering manifest: ${error.code} ${error.text} (${context2.url})`); this.stopLoad(); if (error.code === 410) { this.enabled = false; this.log(`Steering manifest ${context2.url} no longer available`); return; } let ttl = this.timeToLoad * 1e3; if (error.code === 429) { const loader = this.loader; if (typeof (loader == null ? void 0 : loader.getResponseHeader) === "function") { const retryAfter = loader.getResponseHeader("Retry-After"); if (retryAfter) { ttl = parseFloat(retryAfter) * 1e3; } } this.log(`Steering manifest ${context2.url} rate limited`); return; } this.scheduleRefresh(this.uri || context2.url, ttl); }, onTimeout: (stats, context2, networkDetails) => { this.log(`Timeout loading steering manifest (${context2.url})`); this.scheduleRefresh(this.uri || context2.url); } }; this.log(`Requesting steering manifest: ${url}`); this.loader.load(context, loaderConfig, callbacks); } scheduleRefresh(uri, ttlMs = this.timeToLoad * 1e3) { this.clearTimeout(); this.reloadTimer = self.setTimeout(() => { var _this$hls; const media = (_this$hls = this.hls) == null ? void 0 : _this$hls.media; if (media && !media.ended) { this.loadSteeringManifest(uri); return; } this.scheduleRefresh(uri, this.timeToLoad * 1e3); }, ttlMs); } }; function cloneRenditionGroups(tracks, groupCloneMap, uriReplacement, cloneId) { if (!tracks) { return; } Object.keys(groupCloneMap).forEach((audioGroupId) => { const clonedTracks = tracks.filter((track) => track.groupId === audioGroupId).map((track) => { const clonedTrack = _extends({}, track); clonedTrack.details = void 0; clonedTrack.attrs = new AttrList(clonedTrack.attrs); clonedTrack.url = clonedTrack.attrs.URI = performUriReplacement(track.url, track.attrs["STABLE-RENDITION-ID"], "PER-RENDITION-URIS", uriReplacement); clonedTrack.groupId = clonedTrack.attrs["GROUP-ID"] = groupCloneMap[audioGroupId]; clonedTrack.attrs["PATHWAY-ID"] = cloneId; return clonedTrack; }); tracks.push(...clonedTracks); }); } function performUriReplacement(uri, stableId, perOptionKey, uriReplacement) { const { HOST: host, PARAMS: params, [perOptionKey]: perOptionUris } = uriReplacement; let perVariantUri; if (stableId) { perVariantUri = perOptionUris == null ? void 0 : perOptionUris[stableId]; if (perVariantUri) { uri = perVariantUri; } } const url = new self.URL(uri); if (host && !perVariantUri) { url.host = host; } if (params) { Object.keys(params).sort().forEach((key) => { if (key) { url.searchParams.set(key, params[key]); } }); } return url.href; } function addEventListener(el, type, listener) { removeEventListener(el, type, listener); el.addEventListener(type, listener); } function removeEventListener(el, type, listener) { el.removeEventListener(type, listener); } var EMEController = class extends Logger { constructor(hls) { super("eme", hls.logger); this.hls = void 0; this.config = void 0; this.media = null; this.keyFormatPromise = null; this.keySystemAccessPromises = {}; this._requestLicenseFailureCount = 0; this.mediaKeySessions = []; this.keyIdToKeySessionPromise = {}; this.mediaKeys = null; this.setMediaKeysQueue = EMEController.CDMCleanupPromise ? [EMEController.CDMCleanupPromise] : []; this.onWaitingForKey = (event) => { this.log(`"${event.type}" event`); }; this.hls = hls; this.config = hls.config; this.registerListeners(); } destroy() { this.onDestroying(); this.onMediaDetached(); const config = this.config; config.requestMediaKeySystemAccessFunc = null; config.licenseXhrSetup = config.licenseResponseCallback = void 0; config.drmSystems = config.drmSystemOptions = {}; this.hls = this.config = this.keyIdToKeySessionPromise = null; this.onWaitingForKey = null; } registerListeners() { this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); this.hls.on(Events.MEDIA_DETACHED, this.onMediaDetached, this); this.hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); this.hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this); this.hls.on(Events.DESTROYING, this.onDestroying, this); } unregisterListeners() { this.hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); this.hls.off(Events.MEDIA_DETACHED, this.onMediaDetached, this); this.hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); this.hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this); this.hls.off(Events.DESTROYING, this.onDestroying, this); } getLicenseServerUrl(keySystem) { const { drmSystems, widevineLicenseUrl } = this.config; const keySystemConfiguration = drmSystems[keySystem]; if (keySystemConfiguration) { return keySystemConfiguration.licenseUrl; } if (keySystem === KeySystems.WIDEVINE && widevineLicenseUrl) { return widevineLicenseUrl; } } getLicenseServerUrlOrThrow(keySystem) { const url = this.getLicenseServerUrl(keySystem); if (url === void 0) { throw new Error(`no license server URL configured for key-system "${keySystem}"`); } return url; } getServerCertificateUrl(keySystem) { const { drmSystems } = this.config; const keySystemConfiguration = drmSystems[keySystem]; if (keySystemConfiguration) { return keySystemConfiguration.serverCertificateUrl; } else { this.log(`No Server Certificate in config.drmSystems["${keySystem}"]`); } } attemptKeySystemAccess(keySystemsToAttempt) { const levels = this.hls.levels; const uniqueCodec = (value, i, a) => !!value && a.indexOf(value) === i; const audioCodecs = levels.map((level) => level.audioCodec).filter(uniqueCodec); const videoCodecs = levels.map((level) => level.videoCodec).filter(uniqueCodec); if (audioCodecs.length + videoCodecs.length === 0) { videoCodecs.push("avc1.42e01e"); } return new Promise((resolve, reject) => { const attempt = (keySystems) => { const keySystem = keySystems.shift(); this.getMediaKeysPromise(keySystem, audioCodecs, videoCodecs).then((mediaKeys) => resolve({ keySystem, mediaKeys })).catch((error) => { if (keySystems.length) { attempt(keySystems); } else if (error instanceof EMEKeyError) { reject(error); } else { reject(new EMEKeyError({ type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_NO_ACCESS, error, fatal: true }, error.message)); } }); }; attempt(keySystemsToAttempt); }); } requestMediaKeySystemAccess(keySystem, supportedConfigurations) { const { requestMediaKeySystemAccessFunc } = this.config; if (!(typeof requestMediaKeySystemAccessFunc === "function")) { let errMessage = `Configured requestMediaKeySystemAccess is not a function ${requestMediaKeySystemAccessFunc}`; if (requestMediaKeySystemAccess === null && self.location.protocol === "http:") { errMessage = `navigator.requestMediaKeySystemAccess is not available over insecure protocol ${location.protocol}`; } return Promise.reject(new Error(errMessage)); } return requestMediaKeySystemAccessFunc(keySystem, supportedConfigurations); } getMediaKeysPromise(keySystem, audioCodecs, videoCodecs) { const mediaKeySystemConfigs = getSupportedMediaKeySystemConfigurations(keySystem, audioCodecs, videoCodecs, this.config.drmSystemOptions); const keySystemAccessPromises = this.keySystemAccessPromises[keySystem]; let keySystemAccess = keySystemAccessPromises == null ? void 0 : keySystemAccessPromises.keySystemAccess; if (!keySystemAccess) { this.log(`Requesting encrypted media "${keySystem}" key-system access with config: ${stringify(mediaKeySystemConfigs)}`); keySystemAccess = this.requestMediaKeySystemAccess(keySystem, mediaKeySystemConfigs); const _keySystemAccessPromises = this.keySystemAccessPromises[keySystem] = { keySystemAccess }; keySystemAccess.catch((error) => { this.log(`Failed to obtain access to key-system "${keySystem}": ${error}`); }); return keySystemAccess.then((mediaKeySystemAccess) => { this.log(`Access for key-system "${mediaKeySystemAccess.keySystem}" obtained`); const certificateRequest = this.fetchServerCertificate(keySystem); this.log(`Create media-keys for "${keySystem}"`); _keySystemAccessPromises.mediaKeys = mediaKeySystemAccess.createMediaKeys().then((mediaKeys) => { this.log(`Media-keys created for "${keySystem}"`); _keySystemAccessPromises.hasMediaKeys = true; return certificateRequest.then((certificate) => { if (certificate) { return this.setMediaKeysServerCertificate(mediaKeys, keySystem, certificate); } return mediaKeys; }); }); _keySystemAccessPromises.mediaKeys.catch((error) => { this.error(`Failed to create media-keys for "${keySystem}"}: ${error}`); }); return _keySystemAccessPromises.mediaKeys; }); } return keySystemAccess.then(() => keySystemAccessPromises.mediaKeys); } createMediaKeySessionContext({ decryptdata, keySystem, mediaKeys }) { this.log(`Creating key-system session "${keySystem}" keyId: ${Hex.hexDump(decryptdata.keyId || [])}`); const mediaKeysSession = mediaKeys.createSession(); const mediaKeySessionContext = { decryptdata, keySystem, mediaKeys, mediaKeysSession, keyStatus: "status-pending" }; this.mediaKeySessions.push(mediaKeySessionContext); return mediaKeySessionContext; } renewKeySession(mediaKeySessionContext) { const decryptdata = mediaKeySessionContext.decryptdata; if (decryptdata.pssh) { const keySessionContext = this.createMediaKeySessionContext(mediaKeySessionContext); const keyId = this.getKeyIdString(decryptdata); const scheme = "cenc"; this.keyIdToKeySessionPromise[keyId] = this.generateRequestWithPreferredKeySession(keySessionContext, scheme, decryptdata.pssh.buffer, "expired"); } else { this.warn(`Could not renew expired session. Missing pssh initData.`); } this.removeSession(mediaKeySessionContext); } getKeyIdString(decryptdata) { if (!decryptdata) { throw new Error("Could not read keyId of undefined decryptdata"); } if (decryptdata.keyId === null) { throw new Error("keyId is null"); } return Hex.hexDump(decryptdata.keyId); } updateKeySession(mediaKeySessionContext, data) { var _mediaKeySessionConte; const keySession = mediaKeySessionContext.mediaKeysSession; this.log(`Updating key-session "${keySession.sessionId}" for keyID ${Hex.hexDump(((_mediaKeySessionConte = mediaKeySessionContext.decryptdata) == null ? void 0 : _mediaKeySessionConte.keyId) || [])} } (data length: ${data ? data.byteLength : data})`); return keySession.update(data); } getSelectedKeySystemFormats() { return Object.keys(this.keySystemAccessPromises).map((keySystem) => ({ keySystem, hasMediaKeys: this.keySystemAccessPromises[keySystem].hasMediaKeys })).filter(({ hasMediaKeys }) => !!hasMediaKeys).map(({ keySystem }) => keySystemDomainToKeySystemFormat(keySystem)).filter((keySystem) => !!keySystem); } getKeySystemAccess(keySystemsToAttempt) { return this.getKeySystemSelectionPromise(keySystemsToAttempt).then(({ keySystem, mediaKeys }) => { return this.attemptSetMediaKeys(keySystem, mediaKeys); }); } selectKeySystem(keySystemsToAttempt) { return new Promise((resolve, reject) => { return this.getKeySystemSelectionPromise(keySystemsToAttempt).then(({ keySystem }) => { const keySystemFormat = keySystemDomainToKeySystemFormat(keySystem); if (keySystemFormat) { resolve(keySystemFormat); } else { reject(new Error(`Unable to find format for key-system "${keySystem}"`)); } }).catch(reject); }); } selectKeySystemFormat(frag) { const keyFormats = Object.keys(frag.levelkeys || {}); if (!this.keyFormatPromise) { this.log(`Selecting key-system from fragment (sn: ${frag.sn} ${frag.type}: ${frag.level}) key formats ${keyFormats.join(", ")}`); this.keyFormatPromise = this.getKeyFormatPromise(keyFormats); } return this.keyFormatPromise; } getKeyFormatPromise(keyFormats) { const keySystemsInConfig = getKeySystemsForConfig(this.config); const keySystemsToAttempt = keyFormats.map(keySystemFormatToKeySystemDomain).filter((value) => !!value && keySystemsInConfig.indexOf(value) !== -1); return this.selectKeySystem(keySystemsToAttempt); } loadKey(data) { const decryptdata = data.keyInfo.decryptdata; const keyId = this.getKeyIdString(decryptdata); const keyDetails = `(keyId: ${keyId} format: "${decryptdata.keyFormat}" method: ${decryptdata.method} uri: ${decryptdata.uri})`; this.log(`Starting session for key ${keyDetails}`); let keyContextPromise = this.keyIdToKeySessionPromise[keyId]; if (!keyContextPromise) { keyContextPromise = this.getKeySystemForKeyPromise(decryptdata).then(({ keySystem, mediaKeys }) => { this.throwIfDestroyed(); this.log(`Handle encrypted media sn: ${data.frag.sn} ${data.frag.type}: ${data.frag.level} using key ${keyDetails}`); return this.attemptSetMediaKeys(keySystem, mediaKeys).then(() => { this.throwIfDestroyed(); return this.createMediaKeySessionContext({ keySystem, mediaKeys, decryptdata }); }); }); const keySessionContextPromise = this.keyIdToKeySessionPromise[keyId] = keyContextPromise.then((keySessionContext) => { const scheme = "cenc"; const initData = decryptdata.pssh ? decryptdata.pssh.buffer : null; return this.generateRequestWithPreferredKeySession(keySessionContext, scheme, initData, "playlist-key"); }); keySessionContextPromise.catch((error) => this.handleError(error)); } return keyContextPromise; } throwIfDestroyed(message = "Invalid state") { if (!this.hls) { throw new Error("invalid state"); } } handleError(error) { if (!this.hls) { return; } this.error(error.message); if (error instanceof EMEKeyError) { this.hls.trigger(Events.ERROR, error.data); } else { this.hls.trigger(Events.ERROR, { type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_NO_KEYS, error, fatal: true }); } } getKeySystemForKeyPromise(decryptdata) { const keyId = this.getKeyIdString(decryptdata); const mediaKeySessionContext = this.keyIdToKeySessionPromise[keyId]; if (!mediaKeySessionContext) { const keySystem = keySystemFormatToKeySystemDomain(decryptdata.keyFormat); const keySystemsToAttempt = keySystem ? [keySystem] : getKeySystemsForConfig(this.config); return this.attemptKeySystemAccess(keySystemsToAttempt); } return mediaKeySessionContext; } getKeySystemSelectionPromise(keySystemsToAttempt) { if (!keySystemsToAttempt.length) { keySystemsToAttempt = getKeySystemsForConfig(this.config); } if (keySystemsToAttempt.length === 0) { throw new EMEKeyError({ type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_NO_CONFIGURED_LICENSE, fatal: true }, `Missing key-system license configuration options ${stringify({ drmSystems: this.config.drmSystems })}`); } return this.attemptKeySystemAccess(keySystemsToAttempt); } attemptSetMediaKeys(keySystem, mediaKeys) { if (this.mediaKeys === mediaKeys) { return Promise.resolve(); } const queue = this.setMediaKeysQueue.slice(); this.log(`Setting media-keys for "${keySystem}"`); const setMediaKeysPromise = Promise.all(queue).then(() => { if (!this.media) { this.mediaKeys = null; throw new Error("Attempted to set mediaKeys without media element attached"); } return this.media.setMediaKeys(mediaKeys); }); this.mediaKeys = mediaKeys; this.setMediaKeysQueue.push(setMediaKeysPromise); return setMediaKeysPromise.then(() => { this.log(`Media-keys set for "${keySystem}"`); queue.push(setMediaKeysPromise); this.setMediaKeysQueue = this.setMediaKeysQueue.filter((p) => queue.indexOf(p) === -1); }); } generateRequestWithPreferredKeySession(context, initDataType, initData, reason) { var _this$config$drmSyste, _this$config$drmSyste2; const generateRequestFilter = (_this$config$drmSyste = this.config.drmSystems) == null ? void 0 : (_this$config$drmSyste2 = _this$config$drmSyste[context.keySystem]) == null ? void 0 : _this$config$drmSyste2.generateRequest; if (generateRequestFilter) { try { const mappedInitData = generateRequestFilter.call(this.hls, initDataType, initData, context); if (!mappedInitData) { throw new Error("Invalid response from configured generateRequest filter"); } initDataType = mappedInitData.initDataType; initData = mappedInitData.initData ? mappedInitData.initData : null; context.decryptdata.pssh = initData ? new Uint8Array(initData) : null; } catch (error) { var _this$hls; this.warn(error.message); if ((_this$hls = this.hls) != null && _this$hls.config.debug) { throw error; } } } if (initData === null) { this.log(`Skipping key-session request for "${reason}" (no initData)`); return Promise.resolve(context); } const keyId = this.getKeyIdString(context.decryptdata); this.log(`Generating key-session request for "${reason}": ${keyId} (init data type: ${initDataType} length: ${initData ? initData.byteLength : null})`); const licenseStatus = new EventEmitter(); const onmessage = context._onmessage = (event) => { const keySession = context.mediaKeysSession; if (!keySession) { licenseStatus.emit("error", new Error("invalid state")); return; } const { messageType, message } = event; this.log(`"${messageType}" message event for session "${keySession.sessionId}" message size: ${message.byteLength}`); if (messageType === "license-request" || messageType === "license-renewal") { this.renewLicense(context, message).catch((error) => { if (licenseStatus.eventNames().length) { licenseStatus.emit("error", error); } else { this.handleError(error); } }); } else if (messageType === "license-release") { if (context.keySystem === KeySystems.FAIRPLAY) { this.updateKeySession(context, strToUtf8array("acknowledged")); this.removeSession(context); } } else { this.warn(`unhandled media key message type "${messageType}"`); } }; const onkeystatuseschange = context._onkeystatuseschange = (event) => { const keySession = context.mediaKeysSession; if (!keySession) { licenseStatus.emit("error", new Error("invalid state")); return; } this.onKeyStatusChange(context); const keyStatus = context.keyStatus; licenseStatus.emit("keyStatus", keyStatus); if (keyStatus === "expired") { this.warn(`${context.keySystem} expired for key ${keyId}`); this.renewKeySession(context); } }; addEventListener(context.mediaKeysSession, "message", onmessage); addEventListener(context.mediaKeysSession, "keystatuseschange", onkeystatuseschange); const keyUsablePromise = new Promise((resolve, reject) => { licenseStatus.on("error", reject); licenseStatus.on("keyStatus", (keyStatus) => { if (keyStatus.startsWith("usable")) { resolve(); } else if (keyStatus === "output-restricted") { reject(new EMEKeyError({ type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED, fatal: false }, "HDCP level output restricted")); } else if (keyStatus === "internal-error") { reject(new EMEKeyError({ type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_STATUS_INTERNAL_ERROR, fatal: true }, `key status changed to "${keyStatus}"`)); } else if (keyStatus === "expired") { reject(new Error("key expired while generating request")); } else { this.warn(`unhandled key status change "${keyStatus}"`); } }); }); return context.mediaKeysSession.generateRequest(initDataType, initData).then(() => { var _context$mediaKeysSes; this.log(`Request generated for key-session "${(_context$mediaKeysSes = context.mediaKeysSession) == null ? void 0 : _context$mediaKeysSes.sessionId}" keyId: ${keyId}`); }).catch((error) => { throw new EMEKeyError({ type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_NO_SESSION, error, fatal: false }, `Error generating key-session request: ${error}`); }).then(() => keyUsablePromise).catch((error) => { licenseStatus.removeAllListeners(); this.removeSession(context); throw error; }).then(() => { licenseStatus.removeAllListeners(); return context; }); } onKeyStatusChange(mediaKeySessionContext) { mediaKeySessionContext.mediaKeysSession.keyStatuses.forEach((status2, keyId) => { if (typeof keyId === "string" && typeof status2 === "object") { const temp = keyId; keyId = status2; status2 = temp; } this.log(`key status change "${status2}" for keyStatuses keyId: ${Hex.hexDump("buffer" in keyId ? new Uint8Array(keyId.buffer, keyId.byteOffset, keyId.byteLength) : new Uint8Array(keyId))} session keyId: ${Hex.hexDump(new Uint8Array(mediaKeySessionContext.decryptdata.keyId || []))} uri: ${mediaKeySessionContext.decryptdata.uri}`); mediaKeySessionContext.keyStatus = status2; }); } fetchServerCertificate(keySystem) { const config = this.config; const Loader = config.loader; const certLoader = new Loader(config); const url = this.getServerCertificateUrl(keySystem); if (!url) { return Promise.resolve(); } this.log(`Fetching server certificate for "${keySystem}"`); return new Promise((resolve, reject) => { const loaderContext = { responseType: "arraybuffer", url }; const loadPolicy = config.certLoadPolicy.default; const loaderConfig = { loadPolicy, timeout: loadPolicy.maxLoadTimeMs, maxRetry: 0, retryDelay: 0, maxRetryDelay: 0 }; const loaderCallbacks = { onSuccess: (response, stats, context, networkDetails) => { resolve(response.data); }, onError: (response, contex, networkDetails, stats) => { reject(new EMEKeyError({ type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED, fatal: true, networkDetails, response: _objectSpread2({ url: loaderContext.url, data: void 0 }, response) }, `"${keySystem}" certificate request failed (${url}). Status: ${response.code} (${response.text})`)); }, onTimeout: (stats, context, networkDetails) => { reject(new EMEKeyError({ type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED, fatal: true, networkDetails, response: { url: loaderContext.url, data: void 0 } }, `"${keySystem}" certificate request timed out (${url})`)); }, onAbort: (stats, context, networkDetails) => { reject(new Error("aborted")); } }; certLoader.load(loaderContext, loaderConfig, loaderCallbacks); }); } setMediaKeysServerCertificate(mediaKeys, keySystem, cert) { return new Promise((resolve, reject) => { mediaKeys.setServerCertificate(cert).then((success) => { this.log(`setServerCertificate ${success ? "success" : "not supported by CDM"} (${cert == null ? void 0 : cert.byteLength}) on "${keySystem}"`); resolve(mediaKeys); }).catch((error) => { reject(new EMEKeyError({ type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED, error, fatal: true }, error.message)); }); }); } renewLicense(context, keyMessage) { return this.requestLicense(context, new Uint8Array(keyMessage)).then((data) => { return this.updateKeySession(context, new Uint8Array(data)).catch((error) => { throw new EMEKeyError({ type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_SESSION_UPDATE_FAILED, error, fatal: true }, error.message); }); }); } unpackPlayReadyKeyMessage(xhr, licenseChallenge) { const xmlString = String.fromCharCode.apply(null, new Uint16Array(licenseChallenge.buffer)); if (!xmlString.includes("PlayReadyKeyMessage")) { xhr.setRequestHeader("Content-Type", "text/xml; charset=utf-8"); return licenseChallenge; } const keyMessageXml = new DOMParser().parseFromString(xmlString, "application/xml"); const headers = keyMessageXml.querySelectorAll("HttpHeader"); if (headers.length > 0) { let header; for (let i = 0, len = headers.length; i < len; i++) { var _header$querySelector, _header$querySelector2; header = headers[i]; const name = (_header$querySelector = header.querySelector("name")) == null ? void 0 : _header$querySelector.textContent; const value = (_header$querySelector2 = header.querySelector("value")) == null ? void 0 : _header$querySelector2.textContent; if (name && value) { xhr.setRequestHeader(name, value); } } } const challengeElement = keyMessageXml.querySelector("Challenge"); const challengeText = challengeElement == null ? void 0 : challengeElement.textContent; if (!challengeText) { throw new Error(`Cannot find in key message`); } return strToUtf8array(atob(challengeText)); } setupLicenseXHR(xhr, url, keysListItem, licenseChallenge) { const licenseXhrSetup = this.config.licenseXhrSetup; if (!licenseXhrSetup) { xhr.open("POST", url, true); return Promise.resolve({ xhr, licenseChallenge }); } return Promise.resolve().then(() => { if (!keysListItem.decryptdata) { throw new Error("Key removed"); } return licenseXhrSetup.call(this.hls, xhr, url, keysListItem, licenseChallenge); }).catch((error) => { if (!keysListItem.decryptdata) { throw error; } xhr.open("POST", url, true); return licenseXhrSetup.call(this.hls, xhr, url, keysListItem, licenseChallenge); }).then((licenseXhrSetupResult) => { if (!xhr.readyState) { xhr.open("POST", url, true); } const finalLicenseChallenge = licenseXhrSetupResult ? licenseXhrSetupResult : licenseChallenge; return { xhr, licenseChallenge: finalLicenseChallenge }; }); } requestLicense(keySessionContext, licenseChallenge) { const keyLoadPolicy = this.config.keyLoadPolicy.default; return new Promise((resolve, reject) => { const url = this.getLicenseServerUrlOrThrow(keySessionContext.keySystem); this.log(`Sending license request to URL: ${url}`); const xhr = new XMLHttpRequest(); xhr.responseType = "arraybuffer"; xhr.onreadystatechange = () => { if (!this.hls || !keySessionContext.mediaKeysSession) { return reject(new Error("invalid state")); } if (xhr.readyState === 4) { if (xhr.status === 200) { this._requestLicenseFailureCount = 0; let data = xhr.response; this.log(`License received ${data instanceof ArrayBuffer ? data.byteLength : data}`); const licenseResponseCallback = this.config.licenseResponseCallback; if (licenseResponseCallback) { try { data = licenseResponseCallback.call(this.hls, xhr, url, keySessionContext); } catch (error) { this.error(error); } } resolve(data); } else { const retryConfig = keyLoadPolicy.errorRetry; const maxNumRetry = retryConfig ? retryConfig.maxNumRetry : 0; this._requestLicenseFailureCount++; if (this._requestLicenseFailureCount > maxNumRetry || xhr.status >= 400 && xhr.status < 500) { reject(new EMEKeyError({ type: ErrorTypes.KEY_SYSTEM_ERROR, details: ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED, fatal: true, networkDetails: xhr, response: { url, data: void 0, code: xhr.status, text: xhr.statusText } }, `License Request XHR failed (${url}). Status: ${xhr.status} (${xhr.statusText})`)); } else { const attemptsLeft = maxNumRetry - this._requestLicenseFailureCount + 1; this.warn(`Retrying license request, ${attemptsLeft} attempts left`); this.requestLicense(keySessionContext, licenseChallenge).then(resolve, reject); } } } }; if (keySessionContext.licenseXhr && keySessionContext.licenseXhr.readyState !== XMLHttpRequest.DONE) { keySessionContext.licenseXhr.abort(); } keySessionContext.licenseXhr = xhr; this.setupLicenseXHR(xhr, url, keySessionContext, licenseChallenge).then(({ xhr: xhr2, licenseChallenge: licenseChallenge2 }) => { if (keySessionContext.keySystem == KeySystems.PLAYREADY) { licenseChallenge2 = this.unpackPlayReadyKeyMessage(xhr2, licenseChallenge2); } xhr2.send(licenseChallenge2); }); }); } onDestroying() { this.unregisterListeners(); this._clear(); } onMediaAttached(event, data) { if (!this.config.emeEnabled) { return; } const media = data.media; this.media = media; addEventListener(media, "waitingforkey", this.onWaitingForKey); } onMediaDetached() { const media = this.media; if (media) { removeEventListener(media, "waitingforkey", this.onWaitingForKey); this.media = null; this.mediaKeys = null; } } _clear() { var _media$setMediaKeys; this._requestLicenseFailureCount = 0; this.keyIdToKeySessionPromise = {}; if (!this.mediaKeys && !this.mediaKeySessions.length) { return; } const media = this.media; const mediaKeysList = this.mediaKeySessions.slice(); this.mediaKeySessions = []; this.mediaKeys = null; LevelKey.clearKeyUriToKeyIdMap(); const keySessionCount = mediaKeysList.length; EMEController.CDMCleanupPromise = Promise.all(mediaKeysList.map((mediaKeySessionContext) => this.removeSession(mediaKeySessionContext)).concat(media == null ? void 0 : (_media$setMediaKeys = media.setMediaKeys(null)) == null ? void 0 : _media$setMediaKeys.catch((error) => { var _this$hls2; this.log(`Could not clear media keys: ${error}`); (_this$hls2 = this.hls) == null ? void 0 : _this$hls2.trigger(Events.ERROR, { type: ErrorTypes.OTHER_ERROR, details: ErrorDetails.KEY_SYSTEM_DESTROY_MEDIA_KEYS_ERROR, fatal: false, error: new Error(`Could not clear media keys: ${error}`) }); }))).catch((error) => { var _this$hls3; this.log(`Could not close sessions and clear media keys: ${error}`); (_this$hls3 = this.hls) == null ? void 0 : _this$hls3.trigger(Events.ERROR, { type: ErrorTypes.OTHER_ERROR, details: ErrorDetails.KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR, fatal: false, error: new Error(`Could not close sessions and clear media keys: ${error}`) }); }).then(() => { if (keySessionCount) { this.log("finished closing key sessions and clearing media keys"); } }); } onManifestLoading() { this.keyFormatPromise = null; } onManifestLoaded(event, { sessionKeys }) { if (!sessionKeys || !this.config.emeEnabled) { return; } if (!this.keyFormatPromise) { const keyFormats = sessionKeys.reduce((formats, sessionKey) => { if (formats.indexOf(sessionKey.keyFormat) === -1) { formats.push(sessionKey.keyFormat); } return formats; }, []); this.log(`Selecting key-system from session-keys ${keyFormats.join(", ")}`); this.keyFormatPromise = this.getKeyFormatPromise(keyFormats); } } removeSession(mediaKeySessionContext) { const { mediaKeysSession, licenseXhr } = mediaKeySessionContext; if (mediaKeysSession) { this.log(`Remove licenses and keys and close session ${mediaKeysSession.sessionId}`); if (mediaKeySessionContext._onmessage) { mediaKeysSession.removeEventListener("message", mediaKeySessionContext._onmessage); mediaKeySessionContext._onmessage = void 0; } if (mediaKeySessionContext._onkeystatuseschange) { mediaKeysSession.removeEventListener("keystatuseschange", mediaKeySessionContext._onkeystatuseschange); mediaKeySessionContext._onkeystatuseschange = void 0; } if (licenseXhr && licenseXhr.readyState !== XMLHttpRequest.DONE) { licenseXhr.abort(); } mediaKeySessionContext.mediaKeysSession = mediaKeySessionContext.decryptdata = mediaKeySessionContext.licenseXhr = void 0; const index = this.mediaKeySessions.indexOf(mediaKeySessionContext); if (index > -1) { this.mediaKeySessions.splice(index, 1); } const { drmSystemOptions } = this.config; const removePromise = isPersistentSessionType(drmSystemOptions) ? new Promise((resolve, reject) => { self.setTimeout(() => reject(new Error(`MediaKeySession.remove() timeout`)), 8e3); mediaKeysSession.remove().then(resolve); }) : Promise.resolve(); return removePromise.catch((error) => { var _this$hls4; this.log(`Could not remove session: ${error}`); (_this$hls4 = this.hls) == null ? void 0 : _this$hls4.trigger(Events.ERROR, { type: ErrorTypes.OTHER_ERROR, details: ErrorDetails.KEY_SYSTEM_DESTROY_REMOVE_SESSION_ERROR, fatal: false, error: new Error(`Could not remove session: ${error}`) }); }).then(() => { return mediaKeysSession.close(); }).catch((error) => { var _this$hls5; this.log(`Could not close session: ${error}`); (_this$hls5 = this.hls) == null ? void 0 : _this$hls5.trigger(Events.ERROR, { type: ErrorTypes.OTHER_ERROR, details: ErrorDetails.KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR, fatal: false, error: new Error(`Could not close session: ${error}`) }); }); } } }; EMEController.CDMCleanupPromise = void 0; var EMEKeyError = class extends Error { constructor(data, message) { super(message); this.data = void 0; data.error || (data.error = new Error(message)); this.data = data; data.err = data.error; } }; var FPSController = class { constructor(hls) { this.hls = void 0; this.isVideoPlaybackQualityAvailable = false; this.timer = void 0; this.media = null; this.lastTime = void 0; this.lastDroppedFrames = 0; this.lastDecodedFrames = 0; this.streamController = void 0; this.hls = hls; this.registerListeners(); } setStreamController(streamController) { this.streamController = streamController; } registerListeners() { this.hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); this.hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); } unregisterListeners() { this.hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); this.hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); } destroy() { if (this.timer) { clearInterval(this.timer); } this.unregisterListeners(); this.isVideoPlaybackQualityAvailable = false; this.media = null; } onMediaAttaching(event, data) { const config = this.hls.config; if (config.capLevelOnFPSDrop) { const media = data.media instanceof self.HTMLVideoElement ? data.media : null; this.media = media; if (media && typeof media.getVideoPlaybackQuality === "function") { this.isVideoPlaybackQualityAvailable = true; } self.clearInterval(this.timer); this.timer = self.setInterval(this.checkFPSInterval.bind(this), config.fpsDroppedMonitoringPeriod); } } onMediaDetaching() { this.media = null; } checkFPS(video, decodedFrames, droppedFrames) { const currentTime = performance.now(); if (decodedFrames) { if (this.lastTime) { const currentPeriod = currentTime - this.lastTime; const currentDropped = droppedFrames - this.lastDroppedFrames; const currentDecoded = decodedFrames - this.lastDecodedFrames; const droppedFPS = 1e3 * currentDropped / currentPeriod; const hls = this.hls; hls.trigger(Events.FPS_DROP, { currentDropped, currentDecoded, totalDroppedFrames: droppedFrames }); if (droppedFPS > 0) { if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) { let currentLevel = hls.currentLevel; hls.logger.warn("drop FPS ratio greater than max allowed value for currentLevel: " + currentLevel); if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) { currentLevel = currentLevel - 1; hls.trigger(Events.FPS_DROP_LEVEL_CAPPING, { level: currentLevel, droppedLevel: hls.currentLevel }); hls.autoLevelCapping = currentLevel; this.streamController.nextLevelSwitch(); } } } } this.lastTime = currentTime; this.lastDroppedFrames = droppedFrames; this.lastDecodedFrames = decodedFrames; } } checkFPSInterval() { const video = this.media; if (video) { if (this.isVideoPlaybackQualityAvailable) { const videoPlaybackQuality = video.getVideoPlaybackQuality(); this.checkFPS(video, videoPlaybackQuality.totalVideoFrames, videoPlaybackQuality.droppedVideoFrames); } else { this.checkFPS(video, video.webkitDecodedFrameCount, video.webkitDroppedFrameCount); } } } }; function sendAddTrackEvent(track, videoEl) { let event; try { event = new Event("addtrack"); } catch (err) { event = document.createEvent("Event"); event.initEvent("addtrack", false, false); } event.track = track; videoEl.dispatchEvent(event); } function addCueToTrack(track, cue) { const mode = track.mode; if (mode === "disabled") { track.mode = "hidden"; } if (track.cues && !track.cues.getCueById(cue.id)) { try { track.addCue(cue); if (!track.cues.getCueById(cue.id)) { throw new Error(`addCue is failed for: ${cue}`); } } catch (err) { logger.debug(`[texttrack-utils]: ${err}`); try { const textTrackCue = new self.TextTrackCue(cue.startTime, cue.endTime, cue.text); textTrackCue.id = cue.id; track.addCue(textTrackCue); } catch (err2) { logger.debug(`[texttrack-utils]: Legacy TextTrackCue fallback failed: ${err2}`); } } } if (mode === "disabled") { track.mode = mode; } } function clearCurrentCues(track, enterHandler) { const mode = track.mode; if (mode === "disabled") { track.mode = "hidden"; } if (track.cues) { for (let i = track.cues.length; i--; ) { if (enterHandler) { track.cues[i].removeEventListener("enter", enterHandler); } track.removeCue(track.cues[i]); } } if (mode === "disabled") { track.mode = mode; } } function removeCuesInRange(track, start, end, predicate) { const mode = track.mode; if (mode === "disabled") { track.mode = "hidden"; } if (track.cues && track.cues.length > 0) { const cues = getCuesInRange(track.cues, start, end); for (let i = 0; i < cues.length; i++) { if (!predicate || predicate(cues[i])) { track.removeCue(cues[i]); } } } if (mode === "disabled") { track.mode = mode; } } function getFirstCueIndexFromTime(cues, time) { if (time <= cues[0].startTime) { return 0; } const len = cues.length - 1; if (time > cues[len].endTime) { return -1; } let left = 0; let right = len; let mid; while (left <= right) { mid = Math.floor((right + left) / 2); if (time < cues[mid].startTime) { right = mid - 1; } else if (time > cues[mid].startTime && left < len) { left = mid + 1; } else { return mid; } } return cues[left].startTime - time < time - cues[right].startTime ? left : right; } function getCuesInRange(cues, start, end) { const cuesFound = []; const firstCueInRange = getFirstCueIndexFromTime(cues, start); if (firstCueInRange > -1) { for (let i = firstCueInRange, len = cues.length; i < len; i++) { const cue = cues[i]; if (cue.startTime >= start && cue.endTime <= end) { cuesFound.push(cue); } else if (cue.startTime > end) { return cuesFound; } } } return cuesFound; } function filterSubtitleTracks(textTrackList) { const tracks = []; for (let i = 0; i < textTrackList.length; i++) { const track = textTrackList[i]; if ((track.kind === "subtitles" || track.kind === "captions") && track.label) { tracks.push(textTrackList[i]); } } return tracks; } var SubtitleTrackController = class extends BasePlaylistController { constructor(hls) { super(hls, "subtitle-track-controller"); this.media = null; this.tracks = []; this.groupIds = null; this.tracksInGroup = []; this.trackId = -1; this.currentTrack = null; this.selectDefaultTrack = true; this.queuedDefaultTrack = -1; this.useTextTrackPolling = false; this.subtitlePollingInterval = -1; this._subtitleDisplay = true; this.asyncPollTrackChange = () => this.pollTrackChange(0); this.onTextTracksChanged = () => { if (!this.useTextTrackPolling) { self.clearInterval(this.subtitlePollingInterval); } if (!this.media || !this.hls.config.renderTextTracksNatively) { return; } let textTrack = null; const tracks = filterSubtitleTracks(this.media.textTracks); for (let i = 0; i < tracks.length; i++) { if (tracks[i].mode === "hidden") { textTrack = tracks[i]; } else if (tracks[i].mode === "showing") { textTrack = tracks[i]; break; } } const trackId = this.findTrackForTextTrack(textTrack); if (this.subtitleTrack !== trackId) { this.setSubtitleTrack(trackId); } }; this.registerListeners(); } destroy() { this.unregisterListeners(); this.tracks.length = 0; this.tracksInGroup.length = 0; this.currentTrack = null; this.onTextTracksChanged = this.asyncPollTrackChange = null; super.destroy(); } get subtitleDisplay() { return this._subtitleDisplay; } set subtitleDisplay(value) { this._subtitleDisplay = value; if (this.trackId > -1) { this.toggleTrackModes(); } } registerListeners() { const { hls } = this; hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this); hls.on(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); hls.on(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this); hls.on(Events.ERROR, this.onError, this); } unregisterListeners() { const { hls } = this; hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); hls.off(Events.LEVEL_LOADING, this.onLevelLoading, this); hls.off(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); hls.off(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this); hls.off(Events.ERROR, this.onError, this); } onMediaAttached(event, data) { this.media = data.media; if (!this.media) { return; } if (this.queuedDefaultTrack > -1) { this.subtitleTrack = this.queuedDefaultTrack; this.queuedDefaultTrack = -1; } this.useTextTrackPolling = !(this.media.textTracks && "onchange" in this.media.textTracks); if (this.useTextTrackPolling) { this.pollTrackChange(500); } else { this.media.textTracks.addEventListener("change", this.asyncPollTrackChange); } } pollTrackChange(timeout) { self.clearInterval(this.subtitlePollingInterval); this.subtitlePollingInterval = self.setInterval(this.onTextTracksChanged, timeout); } onMediaDetaching(event, data) { const media = this.media; if (!media) { return; } const transferringMedia = !!data.transferMedia; self.clearInterval(this.subtitlePollingInterval); if (!this.useTextTrackPolling) { media.textTracks.removeEventListener("change", this.asyncPollTrackChange); } if (this.trackId > -1) { this.queuedDefaultTrack = this.trackId; } this.subtitleTrack = -1; this.media = null; if (transferringMedia) { return; } const textTracks = filterSubtitleTracks(media.textTracks); textTracks.forEach((track) => { clearCurrentCues(track); }); } onManifestLoading() { this.tracks = []; this.groupIds = null; this.tracksInGroup = []; this.trackId = -1; this.currentTrack = null; this.selectDefaultTrack = true; } onManifestParsed(event, data) { this.tracks = data.subtitleTracks; } onSubtitleTrackLoaded(event, data) { const { id, groupId, details } = data; const trackInActiveGroup = this.tracksInGroup[id]; if (!trackInActiveGroup || trackInActiveGroup.groupId !== groupId) { this.warn(`Subtitle track with id:${id} and group:${groupId} not found in active group ${trackInActiveGroup == null ? void 0 : trackInActiveGroup.groupId}`); return; } const curDetails = trackInActiveGroup.details; trackInActiveGroup.details = data.details; this.log(`Subtitle track ${id} "${trackInActiveGroup.name}" lang:${trackInActiveGroup.lang} group:${groupId} loaded [${details.startSN}-${details.endSN}]`); if (id === this.trackId) { this.playlistLoaded(id, data, curDetails); } } onLevelLoading(event, data) { this.switchLevel(data.level); } onLevelSwitching(event, data) { this.switchLevel(data.level); } switchLevel(levelIndex) { const levelInfo = this.hls.levels[levelIndex]; if (!levelInfo) { return; } const subtitleGroups = levelInfo.subtitleGroups || null; const currentGroups = this.groupIds; let currentTrack = this.currentTrack; if (!subtitleGroups || (currentGroups == null ? void 0 : currentGroups.length) !== (subtitleGroups == null ? void 0 : subtitleGroups.length) || subtitleGroups != null && subtitleGroups.some((groupId) => (currentGroups == null ? void 0 : currentGroups.indexOf(groupId)) === -1)) { this.groupIds = subtitleGroups; this.trackId = -1; this.currentTrack = null; const subtitleTracks = this.tracks.filter((track) => !subtitleGroups || subtitleGroups.indexOf(track.groupId) !== -1); if (subtitleTracks.length) { if (this.selectDefaultTrack && !subtitleTracks.some((track) => track.default)) { this.selectDefaultTrack = false; } subtitleTracks.forEach((track, i) => { track.id = i; }); } else if (!currentTrack && !this.tracksInGroup.length) { return; } this.tracksInGroup = subtitleTracks; const subtitlePreference = this.hls.config.subtitlePreference; if (!currentTrack && subtitlePreference) { this.selectDefaultTrack = false; const groupIndex = findMatchingOption(subtitlePreference, subtitleTracks); if (groupIndex > -1) { currentTrack = subtitleTracks[groupIndex]; } else { const allIndex = findMatchingOption(subtitlePreference, this.tracks); currentTrack = this.tracks[allIndex]; } } let trackId = this.findTrackId(currentTrack); if (trackId === -1 && currentTrack) { trackId = this.findTrackId(null); } const subtitleTracksUpdated = { subtitleTracks }; this.log(`Updating subtitle tracks, ${subtitleTracks.length} track(s) found in "${subtitleGroups == null ? void 0 : subtitleGroups.join(",")}" group-id`); this.hls.trigger(Events.SUBTITLE_TRACKS_UPDATED, subtitleTracksUpdated); if (trackId !== -1 && this.trackId === -1) { this.setSubtitleTrack(trackId); } } } findTrackId(currentTrack) { const tracks = this.tracksInGroup; const selectDefault = this.selectDefaultTrack; for (let i = 0; i < tracks.length; i++) { const track = tracks[i]; if (selectDefault && !track.default || !selectDefault && !currentTrack) { continue; } if (!currentTrack || matchesOption(track, currentTrack)) { return i; } } if (currentTrack) { for (let i = 0; i < tracks.length; i++) { const track = tracks[i]; if (mediaAttributesIdentical(currentTrack.attrs, track.attrs, ["LANGUAGE", "ASSOC-LANGUAGE", "CHARACTERISTICS"])) { return i; } } for (let i = 0; i < tracks.length; i++) { const track = tracks[i]; if (mediaAttributesIdentical(currentTrack.attrs, track.attrs, ["LANGUAGE"])) { return i; } } } return -1; } findTrackForTextTrack(textTrack) { if (textTrack) { const tracks = this.tracksInGroup; for (let i = 0; i < tracks.length; i++) { const track = tracks[i]; if (subtitleTrackMatchesTextTrack(track, textTrack)) { return i; } } } return -1; } onError(event, data) { if (data.fatal || !data.context) { return; } if (data.context.type === PlaylistContextType.SUBTITLE_TRACK && data.context.id === this.trackId && (!this.groupIds || this.groupIds.indexOf(data.context.groupId) !== -1)) { this.checkRetry(data); } } get allSubtitleTracks() { return this.tracks; } get subtitleTracks() { return this.tracksInGroup; } get subtitleTrack() { return this.trackId; } set subtitleTrack(newId) { this.selectDefaultTrack = false; this.setSubtitleTrack(newId); } setSubtitleOption(subtitleOption) { this.hls.config.subtitlePreference = subtitleOption; if (subtitleOption) { if (subtitleOption.id === -1) { this.setSubtitleTrack(-1); return null; } const allSubtitleTracks = this.allSubtitleTracks; this.selectDefaultTrack = false; if (allSubtitleTracks.length) { const currentTrack = this.currentTrack; if (currentTrack && matchesOption(subtitleOption, currentTrack)) { return currentTrack; } const groupIndex = findMatchingOption(subtitleOption, this.tracksInGroup); if (groupIndex > -1) { const track = this.tracksInGroup[groupIndex]; this.setSubtitleTrack(groupIndex); return track; } else if (currentTrack) { return null; } else { const allIndex = findMatchingOption(subtitleOption, allSubtitleTracks); if (allIndex > -1) { return allSubtitleTracks[allIndex]; } } } } return null; } loadPlaylist(hlsUrlParameters) { super.loadPlaylist(); if (this.shouldLoadPlaylist(this.currentTrack)) { this.scheduleLoading(this.currentTrack, hlsUrlParameters); } } loadingPlaylist(currentTrack, hlsUrlParameters) { super.loadingPlaylist(currentTrack, hlsUrlParameters); const id = currentTrack.id; const groupId = currentTrack.groupId; const url = this.getUrlWithDirectives(currentTrack.url, hlsUrlParameters); const details = currentTrack.details; const age = details == null ? void 0 : details.age; this.log(`Loading subtitle ${id} "${currentTrack.name}" lang:${currentTrack.lang} group:${groupId}${(hlsUrlParameters == null ? void 0 : hlsUrlParameters.msn) !== void 0 ? " at sn " + hlsUrlParameters.msn + " part " + hlsUrlParameters.part : ""}${age && details.live ? " age " + age.toFixed(1) + (details.type ? " " + details.type || "" : "") : ""} ${url}`); this.hls.trigger(Events.SUBTITLE_TRACK_LOADING, { url, id, groupId, deliveryDirectives: hlsUrlParameters || null, track: currentTrack }); } toggleTrackModes() { const { media } = this; if (!media) { return; } const textTracks = filterSubtitleTracks(media.textTracks); const currentTrack = this.currentTrack; let nextTrack; if (currentTrack) { nextTrack = textTracks.filter((textTrack) => subtitleTrackMatchesTextTrack(currentTrack, textTrack))[0]; if (!nextTrack) { this.warn(`Unable to find subtitle TextTrack with name "${currentTrack.name}" and language "${currentTrack.lang}"`); } } [].slice.call(textTracks).forEach((track) => { if (track.mode !== "disabled" && track !== nextTrack) { track.mode = "disabled"; } }); if (nextTrack) { const mode = this.subtitleDisplay ? "showing" : "hidden"; if (nextTrack.mode !== mode) { nextTrack.mode = mode; } } } setSubtitleTrack(newId) { const tracks = this.tracksInGroup; if (!this.media) { this.queuedDefaultTrack = newId; return; } if (newId < -1 || newId >= tracks.length || !isFiniteNumber(newId)) { this.warn(`Invalid subtitle track id: ${newId}`); return; } this.selectDefaultTrack = false; const lastTrack = this.currentTrack; const track = tracks[newId] || null; this.trackId = newId; this.currentTrack = track; this.toggleTrackModes(); if (!track) { this.hls.trigger(Events.SUBTITLE_TRACK_SWITCH, { id: newId }); return; } const trackLoaded = !!track.details && !track.details.live; if (newId === this.trackId && track === lastTrack && trackLoaded) { return; } this.log(`Switching to subtitle-track ${newId}` + (track ? ` "${track.name}" lang:${track.lang} group:${track.groupId}` : "")); const { id, groupId = "", name, type, url } = track; this.hls.trigger(Events.SUBTITLE_TRACK_SWITCH, { id, groupId, name, type, url }); const hlsUrlParameters = this.switchParams(track.url, lastTrack == null ? void 0 : lastTrack.details, track.details); this.loadPlaylist(hlsUrlParameters); } }; function uuid() { try { return crypto.randomUUID(); } catch (error) { try { const url = URL.createObjectURL(new Blob()); const uuid2 = url.toString(); URL.revokeObjectURL(url); return uuid2.slice(uuid2.lastIndexOf("/") + 1); } catch (error2) { let dt = new Date().getTime(); const uuid2 = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => { const r = (dt + Math.random() * 16) % 16 | 0; dt = Math.floor(dt / 16); return (c == "x" ? r : r & 3 | 8).toString(16); }); return uuid2; } } } function hash(text) { let hash2 = 5381; let i = text.length; while (i) { hash2 = hash2 * 33 ^ text.charCodeAt(--i); } return (hash2 >>> 0).toString(); } var ALIGNED_END_THRESHOLD_SECONDS = 0.025; var TimelineOccupancy = function(TimelineOccupancy2) { TimelineOccupancy2[TimelineOccupancy2["Point"] = 0] = "Point"; TimelineOccupancy2[TimelineOccupancy2["Range"] = 1] = "Range"; return TimelineOccupancy2; }({}); function generateAssetIdentifier(interstitial, uri, assetListIndex) { return `${interstitial.identifier}-${assetListIndex + 1}-${hash(uri)}`; } var InterstitialEvent = class { constructor(dateRange, base) { this.base = void 0; this._duration = null; this._timelineStart = null; this.appendInPlaceDisabled = void 0; this.appendInPlaceStarted = void 0; this.dateRange = void 0; this.hasPlayed = false; this.cumulativeDuration = 0; this.resumeOffset = NaN; this.playoutLimit = NaN; this.restrictions = { skip: false, jump: false }; this.snapOptions = { out: false, in: false }; this.assetList = []; this.assetListLoader = void 0; this.assetListResponse = null; this.resumeAnchor = void 0; this.error = void 0; this.resetOnResume = void 0; this.base = base; this.dateRange = dateRange; this.setDateRange(dateRange); } setDateRange(dateRange) { this.dateRange = dateRange; this.resumeOffset = dateRange.attr.optionalFloat("X-RESUME-OFFSET", this.resumeOffset); this.playoutLimit = dateRange.attr.optionalFloat("X-PLAYOUT-LIMIT", this.playoutLimit); this.restrictions = dateRange.attr.enumeratedStringList("X-RESTRICT", this.restrictions); this.snapOptions = dateRange.attr.enumeratedStringList("X-SNAP", this.snapOptions); } reset() { var _this$assetListLoader; this.appendInPlaceStarted = false; (_this$assetListLoader = this.assetListLoader) == null ? void 0 : _this$assetListLoader.destroy(); this.assetListLoader = void 0; if (!this.supplementsPrimary) { this.assetListResponse = null; this.assetList = []; this._duration = null; } } isAssetPastPlayoutLimit(assetIndex) { var _this$assetList$asset; if (assetIndex > 0 && assetIndex >= this.assetList.length) { return true; } const playoutLimit = this.playoutLimit; if (assetIndex <= 0 || isNaN(playoutLimit)) { return false; } if (playoutLimit === 0) { return true; } const assetOffset = ((_this$assetList$asset = this.assetList[assetIndex]) == null ? void 0 : _this$assetList$asset.startOffset) || 0; return assetOffset > playoutLimit; } findAssetIndex(asset) { const index = this.assetList.indexOf(asset); return index; } get identifier() { return this.dateRange.id; } get startDate() { return this.dateRange.startDate; } get startTime() { const startTime = this.dateRange.startTime; if (this.snapOptions.out) { const frag = this.dateRange.tagAnchor; if (frag) { return getSnapToFragmentTime(startTime, frag); } } return startTime; } get startOffset() { return this.cue.pre ? 0 : this.startTime; } get startIsAligned() { if (this.startTime === 0 || this.snapOptions.out) { return true; } const frag = this.dateRange.tagAnchor; if (frag) { const startTime = this.dateRange.startTime; const snappedStart = getSnapToFragmentTime(startTime, frag); return startTime - snappedStart < 0.1; } return false; } get resumptionOffset() { const resumeOffset = this.resumeOffset; const offset = isFiniteNumber(resumeOffset) ? resumeOffset : this.duration; return this.cumulativeDuration + offset; } get resumeTime() { const resumeTime = this.startOffset + this.resumptionOffset; if (this.snapOptions.in) { const frag = this.resumeAnchor; if (frag) { return getSnapToFragmentTime(resumeTime, frag); } } return resumeTime; } get appendInPlace() { if (this.appendInPlaceStarted) { return true; } if (this.appendInPlaceDisabled) { return false; } if (!this.cue.once && !this.cue.pre && this.startIsAligned && (isNaN(this.playoutLimit) && isNaN(this.resumeOffset) || this.resumeOffset && this.duration && Math.abs(this.resumeOffset - this.duration) < ALIGNED_END_THRESHOLD_SECONDS)) { return true; } return false; } set appendInPlace(value) { if (this.appendInPlaceStarted) { this.resetOnResume = !value; return; } this.appendInPlaceDisabled = !value; } get timelineStart() { if (this._timelineStart !== null) { return this._timelineStart; } return this.startTime; } set timelineStart(value) { this._timelineStart = value; } get duration() { const playoutLimit = this.playoutLimit; let duration; if (this._duration !== null) { duration = this._duration; } else if (this.dateRange.duration) { duration = this.dateRange.duration; } else { duration = this.dateRange.plannedDuration || 0; } if (!isNaN(playoutLimit) && playoutLimit < duration) { duration = playoutLimit; } return duration; } set duration(value) { this._duration = value; } get cue() { return this.dateRange.cue; } get timelineOccupancy() { if (this.dateRange.attr["X-TIMELINE-OCCUPIES"] === "RANGE") { return TimelineOccupancy.Range; } return TimelineOccupancy.Point; } get supplementsPrimary() { return this.dateRange.attr["X-TIMELINE-STYLE"] === "PRIMARY"; } get contentMayVary() { return this.dateRange.attr["X-CONTENT-MAY-VARY"] !== "NO"; } get assetUrl() { return this.dateRange.attr["X-ASSET-URI"]; } get assetListUrl() { return this.dateRange.attr["X-ASSET-LIST"]; } get baseUrl() { return this.base.url; } get assetListLoaded() { return this.assetList.length > 0 || this.assetListResponse !== null; } toString() { return eventToString(this); } }; function getSnapToFragmentTime(time, frag) { return time - frag.start < frag.duration / 2 && !(Math.abs(time - (frag.start + frag.duration)) < ALIGNED_END_THRESHOLD_SECONDS) ? frag.start : frag.start + frag.duration; } function getInterstitialUrl(uri, sessionId, baseUrl) { const url = new self.URL(uri, baseUrl); if (url.protocol !== "data:") { url.searchParams.set("_HLS_primary_id", sessionId); } return url; } function getNextAssetIndex(interstitial, assetListIndex) { while ((_interstitial$assetLi = interstitial.assetList[++assetListIndex]) != null && _interstitial$assetLi.error) { var _interstitial$assetLi; } return assetListIndex; } function eventToString(interstitial) { return `["${interstitial.identifier}" ${interstitial.cue.pre ? "
" : interstitial.cue.post ? "" : ""}${interstitial.timelineStart.toFixed(2)}-${interstitial.resumeTime.toFixed(2)}]`;
}
function eventAssetToString(asset) {
  const start = asset.timelineStart;
  const duration = asset.duration || 0;
  return `["${asset.identifier}" ${start.toFixed(2)}-${(start + duration).toFixed(2)}]`;
}
var HlsAssetPlayer = class {
  constructor(HlsPlayerClass, userConfig, interstitial, assetItem) {
    this.hls = void 0;
    this.interstitial = void 0;
    this.assetItem = void 0;
    this.tracks = null;
    this.hasDetails = false;
    this.mediaAttached = null;
    this._currentTime = void 0;
    this._bufferedEosTime = void 0;
    this.checkPlayout = () => {
      if (this.reachedPlayout(this.currentTime)) {
        this.hls.trigger(Events.PLAYOUT_LIMIT_REACHED, {});
      }
    };
    const hls = this.hls = new HlsPlayerClass(userConfig);
    this.interstitial = interstitial;
    this.assetItem = assetItem;
    let uri = assetItem.uri;
    try {
      uri = getInterstitialUrl(uri, userConfig.primarySessionId).href;
    } catch (error) {
    }
    hls.loadSource(uri);
    const detailsLoaded = () => {
      this.hasDetails = true;
    };
    hls.once(Events.LEVEL_LOADED, detailsLoaded);
    hls.once(Events.AUDIO_TRACK_LOADED, detailsLoaded);
    hls.once(Events.SUBTITLE_TRACK_LOADED, detailsLoaded);
    hls.on(Events.MEDIA_ATTACHING, (name, {
      media
    }) => {
      this.removeMediaListeners();
      this.mediaAttached = media;
      const event = this.interstitial;
      if (event.playoutLimit) {
        media.addEventListener("timeupdate", this.checkPlayout);
        if (this.appendInPlace) {
          hls.on(Events.BUFFER_APPENDED, () => {
            const bufferedEnd = this.bufferedEnd;
            if (this.reachedPlayout(bufferedEnd)) {
              this._bufferedEosTime = bufferedEnd;
              hls.trigger(Events.BUFFERED_TO_END, void 0);
            }
          });
        }
      }
    });
  }
  get appendInPlace() {
    var _this$interstitial;
    return ((_this$interstitial = this.interstitial) == null ? void 0 : _this$interstitial.appendInPlace) || false;
  }
  bufferedInPlaceToEnd(media) {
    var _this$hls;
    if (!this.appendInPlace) {
      return false;
    }
    if ((_this$hls = this.hls) != null && _this$hls.bufferedToEnd) {
      return true;
    }
    if (!media || !this._bufferedEosTime) {
      return false;
    }
    const start = this.timelineOffset;
    const bufferInfo = BufferHelper.bufferInfo(media, start, 0);
    const bufferedEnd = this.getAssetTime(bufferInfo.end);
    return bufferedEnd >= this._bufferedEosTime - 0.02;
  }
  reachedPlayout(time) {
    const interstitial = this.interstitial;
    const playoutLimit = interstitial.playoutLimit;
    return this.startOffset + time >= playoutLimit;
  }
  get destroyed() {
    var _this$hls2;
    return !((_this$hls2 = this.hls) != null && _this$hls2.userConfig);
  }
  get assetId() {
    return this.assetItem.identifier;
  }
  get interstitialId() {
    return this.assetItem.parentIdentifier;
  }
  get media() {
    var _this$hls3;
    return ((_this$hls3 = this.hls) == null ? void 0 : _this$hls3.media) || null;
  }
  get bufferedEnd() {
    const media = this.media || this.mediaAttached;
    if (!media) {
      if (this._bufferedEosTime) {
        return this._bufferedEosTime;
      }
      return this.currentTime;
    }
    const bufferInfo = BufferHelper.bufferInfo(media, media.currentTime, 1e-3);
    return this.getAssetTime(bufferInfo.end);
  }
  get currentTime() {
    const media = this.media || this.mediaAttached;
    if (!media) {
      return this._currentTime || 0;
    }
    return this.getAssetTime(media.currentTime);
  }
  get duration() {
    const duration = this.assetItem.duration;
    if (!duration) {
      return 0;
    }
    return duration;
  }
  get remaining() {
    const duration = this.duration;
    if (!duration) {
      return 0;
    }
    return Math.max(0, duration - this.currentTime);
  }
  get startOffset() {
    return this.assetItem.startOffset;
  }
  get timelineOffset() {
    var _this$hls4;
    return ((_this$hls4 = this.hls) == null ? void 0 : _this$hls4.config.timelineOffset) || 0;
  }
  set timelineOffset(value) {
    const timelineOffset = this.timelineOffset;
    if (value !== timelineOffset) {
      const diff = value - timelineOffset;
      if (Math.abs(diff) > 1 / 9e4) {
        if (this.hasDetails) {
          throw new Error(`Cannot set timelineOffset after playlists are loaded`);
        }
        this.hls.config.timelineOffset = value;
      }
    }
  }
  getAssetTime(time) {
    const timelineOffset = this.timelineOffset;
    const duration = this.duration;
    return Math.min(Math.max(0, time - timelineOffset), duration);
  }
  removeMediaListeners() {
    const media = this.mediaAttached;
    if (media) {
      this._currentTime = media.currentTime;
      this.bufferSnapShot();
      media.removeEventListener("timeupdate", this.checkPlayout);
    }
  }
  bufferSnapShot() {
    if (this.mediaAttached) {
      var _this$hls5;
      if ((_this$hls5 = this.hls) != null && _this$hls5.bufferedToEnd) {
        this._bufferedEosTime = this.bufferedEnd;
      }
    }
  }
  destroy() {
    this.removeMediaListeners();
    this.hls.destroy();
    this.hls = this.interstitial = null;
    this.tracks = this.mediaAttached = this.checkPlayout = null;
  }
  attachMedia(data) {
    this.hls.attachMedia(data);
  }
  detachMedia() {
    this.removeMediaListeners();
    this.mediaAttached = null;
    this.hls.detachMedia();
  }
  resumeBuffering() {
    this.hls.resumeBuffering();
  }
  pauseBuffering() {
    this.hls.pauseBuffering();
  }
  transferMedia() {
    this.bufferSnapShot();
    return this.hls.transferMedia();
  }
  resetDetails() {
    const hls = this.hls;
    if (this.hasDetails) {
      hls.stopLoad();
      const deleteDetails = (obj) => delete obj.details;
      hls.levels.forEach(deleteDetails);
      hls.allAudioTracks.forEach(deleteDetails);
      hls.allSubtitleTracks.forEach(deleteDetails);
      this.hasDetails = false;
    }
  }
  on(event, listener, context) {
    this.hls.on(event, listener);
  }
  once(event, listener, context) {
    this.hls.once(event, listener);
  }
  off(event, listener, context) {
    this.hls.off(event, listener);
  }
  toString() {
    var _this$hls6;
    return `HlsAssetPlayer: ${eventAssetToString(this.assetItem)} ${(_this$hls6 = this.hls) == null ? void 0 : _this$hls6.sessionId} ${this.appendInPlace ? "append-in-place" : ""}`;
  }
};
var ABUTTING_THRESHOLD_SECONDS = 0.033;
var InterstitialsSchedule = class extends Logger {
  constructor(onScheduleUpdate, logger2) {
    super("interstitials-sched", logger2);
    this.onScheduleUpdate = void 0;
    this.eventMap = {};
    this.events = null;
    this.items = null;
    this.durations = {
      primary: 0,
      playout: 0,
      integrated: 0
    };
    this.onScheduleUpdate = onScheduleUpdate;
  }
  destroy() {
    this.reset();
    this.onScheduleUpdate = null;
  }
  reset() {
    this.eventMap = {};
    this.setDurations(0, 0, 0);
    if (this.events) {
      this.events.forEach((interstitial) => interstitial.reset());
    }
    this.events = this.items = null;
  }
  resetErrorsInRange(start, end) {
    if (this.events) {
      return this.events.reduce((count, interstitial) => {
        if (start <= interstitial.startOffset && end > interstitial.startOffset) {
          delete interstitial.error;
          return count + 1;
        }
        return count;
      }, 0);
    }
    return 0;
  }
  get duration() {
    const items = this.items;
    return items ? items[items.length - 1].end : 0;
  }
  get length() {
    return this.items ? this.items.length : 0;
  }
  getEvent(identifier) {
    return identifier ? this.eventMap[identifier] || null : null;
  }
  hasEvent(identifier) {
    return identifier in this.eventMap;
  }
  findItemIndex(item, time) {
    if (item.event) {
      return this.findEventIndex(item.event.identifier);
    }
    let index = -1;
    if (item.nextEvent) {
      index = this.findEventIndex(item.nextEvent.identifier) - 1;
    } else if (item.previousEvent) {
      index = this.findEventIndex(item.previousEvent.identifier) + 1;
    }
    const items = this.items;
    if (items) {
      if (!items[index]) {
        if (time === void 0) {
          time = item.start;
        }
        index = this.findItemIndexAtTime(time);
      }
      while (index >= 0 && (_items$index = items[index]) != null && _items$index.event) {
        var _items$index;
        index--;
      }
    }
    return index;
  }
  findItemIndexAtTime(timelinePos, timelineType) {
    const items = this.items;
    if (items) {
      for (let i = 0; i < items.length; i++) {
        let timeRange = items[i];
        if (timelineType && timelineType !== "primary") {
          timeRange = timeRange[timelineType];
        }
        if (timelinePos === timeRange.start || timelinePos > timeRange.start && timelinePos < timeRange.end) {
          return i;
        }
      }
    }
    return -1;
  }
  findJumpRestrictedIndex(startIndex, endIndex) {
    const items = this.items;
    if (items) {
      for (let i = startIndex; i <= endIndex; i++) {
        if (!items[i]) {
          break;
        }
        const event = items[i].event;
        if (event != null && event.restrictions.jump && !event.appendInPlace) {
          return i;
        }
      }
    }
    return -1;
  }
  findEventIndex(identifier) {
    const items = this.items;
    if (items) {
      for (let i = items.length; i--; ) {
        var _items$i$event;
        if (((_items$i$event = items[i].event) == null ? void 0 : _items$i$event.identifier) === identifier) {
          return i;
        }
      }
    }
    return -1;
  }
  findAssetIndex(event, timelinePos) {
    const assetList = event.assetList;
    const length = assetList.length;
    if (length > 1) {
      for (let i = 0; i < length; i++) {
        const asset = assetList[i];
        if (!asset.error) {
          const timelineStart = asset.timelineStart;
          if (timelinePos === timelineStart || timelinePos > timelineStart && timelinePos < timelineStart + (asset.duration || 0)) {
            return i;
          }
        }
      }
    }
    return 0;
  }
  get assetIdAtEnd() {
    var _this$items, _this$items2;
    const interstitialAtEnd = (_this$items = this.items) == null ? void 0 : (_this$items2 = _this$items[this.length - 1]) == null ? void 0 : _this$items2.event;
    if (interstitialAtEnd) {
      const assetList = interstitialAtEnd.assetList;
      const assetAtEnd = assetList[assetList.length - 1];
      if (assetAtEnd) {
        return assetAtEnd.identifier;
      }
    }
    return null;
  }
  parseInterstitialDateRanges(mediaSelection, enableAppendInPlace) {
    const details = mediaSelection.main.details;
    const {
      dateRanges
    } = details;
    const previousInterstitialEvents = this.events;
    const interstitialEvents = this.parseDateRanges(dateRanges, {
      url: details.url
    }, enableAppendInPlace);
    const ids = Object.keys(dateRanges);
    const removedInterstitials = previousInterstitialEvents ? previousInterstitialEvents.filter((event) => !ids.includes(event.identifier)) : [];
    if (interstitialEvents.length) {
      interstitialEvents.sort((a, b) => {
        const aPre = a.cue.pre;
        const aPost = a.cue.post;
        const bPre = b.cue.pre;
        const bPost = b.cue.post;
        if (aPre && !bPre) {
          return -1;
        }
        if (bPre && !aPre) {
          return 1;
        }
        if (aPost && !bPost) {
          return 1;
        }
        if (bPost && !aPost) {
          return -1;
        }
        if (!aPre && !bPre && !aPost && !bPost) {
          const startA = a.startTime;
          const startB = b.startTime;
          if (startA !== startB) {
            return startA - startB;
          }
        }
        return a.dateRange.tagOrder - b.dateRange.tagOrder;
      });
    }
    this.events = interstitialEvents;
    removedInterstitials.forEach((interstitial) => {
      this.removeEvent(interstitial);
    });
    this.updateSchedule(mediaSelection, removedInterstitials);
  }
  updateSchedule(mediaSelection, removedInterstitials = []) {
    const events = this.events || [];
    if (events.length || removedInterstitials.length || this.length < 2) {
      const currentItems = this.items;
      const updatedItems = this.parseSchedule(events, mediaSelection);
      const updated = removedInterstitials.length || (currentItems == null ? void 0 : currentItems.length) !== updatedItems.length || updatedItems.some((item, i) => {
        return Math.abs(item.playout.start - currentItems[i].playout.start) > 5e-3 || Math.abs(item.playout.end - currentItems[i].playout.end) > 5e-3;
      });
      if (updated) {
        this.items = updatedItems;
        this.onScheduleUpdate(removedInterstitials, currentItems);
      }
    }
  }
  parseDateRanges(dateRanges, baseData, enableAppendInPlace) {
    const interstitialEvents = [];
    const ids = Object.keys(dateRanges);
    for (let i = 0; i < ids.length; i++) {
      const id = ids[i];
      const dateRange = dateRanges[id];
      if (dateRange.isInterstitial) {
        let interstitial = this.eventMap[id];
        if (interstitial) {
          interstitial.setDateRange(dateRange);
        } else {
          interstitial = new InterstitialEvent(dateRange, baseData);
          this.eventMap[id] = interstitial;
          if (enableAppendInPlace === false) {
            interstitial.appendInPlace = enableAppendInPlace;
          }
        }
        interstitialEvents.push(interstitial);
      }
    }
    return interstitialEvents;
  }
  parseSchedule(interstitialEvents, mediaSelection) {
    const schedule = [];
    const details = mediaSelection.main.details;
    const primaryDuration = details.live ? Infinity : details.edge;
    let playoutDuration = 0;
    interstitialEvents = interstitialEvents.filter((event) => !event.error && !(event.cue.once && event.hasPlayed));
    if (interstitialEvents.length) {
      this.resolveOffsets(interstitialEvents, mediaSelection);
      let primaryPosition = 0;
      let integratedTime = 0;
      interstitialEvents.forEach((interstitial, i) => {
        const preroll = interstitial.cue.pre;
        const postroll = interstitial.cue.post;
        const previousEvent = interstitialEvents[i - 1] || null;
        const appendInPlace = interstitial.appendInPlace;
        const eventStart = postroll ? primaryDuration : interstitial.startOffset;
        const interstitialDuration = interstitial.duration;
        const timelineDuration = interstitial.timelineOccupancy === TimelineOccupancy.Range ? interstitialDuration : 0;
        const resumptionOffset = interstitial.resumptionOffset;
        const inSameStartTimeSequence = (previousEvent == null ? void 0 : previousEvent.startTime) === eventStart;
        const start = eventStart + interstitial.cumulativeDuration;
        let end = appendInPlace ? start + interstitialDuration : eventStart + resumptionOffset;
        if (preroll || !postroll && eventStart <= 0) {
          const integratedStart = integratedTime;
          integratedTime += timelineDuration;
          interstitial.timelineStart = start;
          const playoutStart = playoutDuration;
          playoutDuration += interstitialDuration;
          schedule.push({
            event: interstitial,
            start,
            end,
            playout: {
              start: playoutStart,
              end: playoutDuration
            },
            integrated: {
              start: integratedStart,
              end: integratedTime
            }
          });
        } else if (eventStart <= primaryDuration) {
          if (!inSameStartTimeSequence) {
            const segmentDuration = eventStart - primaryPosition;
            if (segmentDuration > ABUTTING_THRESHOLD_SECONDS) {
              const timelineStart = primaryPosition;
              const _integratedStart = integratedTime;
              integratedTime += segmentDuration;
              const _playoutStart = playoutDuration;
              playoutDuration += segmentDuration;
              const primarySegment = {
                previousEvent: interstitialEvents[i - 1] || null,
                nextEvent: interstitial,
                start: timelineStart,
                end: timelineStart + segmentDuration,
                playout: {
                  start: _playoutStart,
                  end: playoutDuration
                },
                integrated: {
                  start: _integratedStart,
                  end: integratedTime
                }
              };
              schedule.push(primarySegment);
            } else if (segmentDuration > 0 && previousEvent) {
              previousEvent.cumulativeDuration += segmentDuration;
              schedule[schedule.length - 1].end = eventStart;
            }
          }
          if (postroll) {
            end = start;
          }
          interstitial.timelineStart = start;
          const integratedStart = integratedTime;
          integratedTime += timelineDuration;
          const playoutStart = playoutDuration;
          playoutDuration += interstitialDuration;
          schedule.push({
            event: interstitial,
            start,
            end,
            playout: {
              start: playoutStart,
              end: playoutDuration
            },
            integrated: {
              start: integratedStart,
              end: integratedTime
            }
          });
        } else {
          return;
        }
        const resumeTime = interstitial.resumeTime;
        if (postroll || resumeTime > primaryDuration) {
          primaryPosition = primaryDuration;
        } else {
          primaryPosition = resumeTime;
        }
      });
      if (primaryPosition < primaryDuration) {
        var _schedule;
        const timelineStart = primaryPosition;
        const integratedStart = integratedTime;
        const segmentDuration = primaryDuration - primaryPosition;
        integratedTime += segmentDuration;
        const playoutStart = playoutDuration;
        playoutDuration += segmentDuration;
        schedule.push({
          previousEvent: ((_schedule = schedule[schedule.length - 1]) == null ? void 0 : _schedule.event) || null,
          nextEvent: null,
          start: primaryPosition,
          end: timelineStart + segmentDuration,
          playout: {
            start: playoutStart,
            end: playoutDuration
          },
          integrated: {
            start: integratedStart,
            end: integratedTime
          }
        });
      }
      this.setDurations(primaryDuration, playoutDuration, integratedTime);
    } else {
      const start = 0;
      schedule.push({
        previousEvent: null,
        nextEvent: null,
        start,
        end: primaryDuration,
        playout: {
          start,
          end: primaryDuration
        },
        integrated: {
          start,
          end: primaryDuration
        }
      });
      this.setDurations(primaryDuration, primaryDuration, primaryDuration);
    }
    return schedule;
  }
  setDurations(primary, playout, integrated) {
    this.durations = {
      primary,
      playout,
      integrated
    };
  }
  resolveOffsets(interstitialEvents, mediaSelection) {
    const details = mediaSelection.main.details;
    const primaryDuration = details.live ? Infinity : details.edge;
    let cumulativeDuration = 0;
    let lastScheduledStart = -1;
    interstitialEvents.forEach((interstitial, i) => {
      const preroll = interstitial.cue.pre;
      const postroll = interstitial.cue.post;
      const eventStart = preroll ? 0 : postroll ? primaryDuration : interstitial.startTime;
      this.updateAssetDurations(interstitial);
      const inSameStartTimeSequence = lastScheduledStart === eventStart;
      if (inSameStartTimeSequence) {
        interstitial.cumulativeDuration = cumulativeDuration;
      } else {
        cumulativeDuration = 0;
        lastScheduledStart = eventStart;
      }
      if (!postroll && interstitial.snapOptions.in) {
        interstitial.resumeAnchor = findFragmentByPTS(null, details.fragments, interstitial.startOffset + interstitial.resumptionOffset, 0, 0) || void 0;
      }
      if (interstitial.appendInPlace && !interstitial.appendInPlaceStarted) {
        const alignedSegmentStart = this.primaryCanResumeInPlaceAt(interstitial, mediaSelection);
        if (!alignedSegmentStart) {
          interstitial.appendInPlace = false;
        }
      }
      if (!interstitial.appendInPlace && i + 1 < interstitialEvents.length) {
        const timeBetween = interstitialEvents[i + 1].startTime - interstitialEvents[i].resumeTime;
        if (timeBetween < ABUTTING_THRESHOLD_SECONDS) {
          interstitialEvents[i + 1].appendInPlace = false;
          if (interstitialEvents[i + 1].appendInPlace) {
            this.warn(`Could not change append strategy for abutting event ${interstitial}`);
          }
        }
      }
      const resumeOffset = isFiniteNumber(interstitial.resumeOffset) ? interstitial.resumeOffset : interstitial.duration;
      cumulativeDuration += resumeOffset;
    });
  }
  primaryCanResumeInPlaceAt(interstitial, mediaSelection) {
    const resumeTime = interstitial.resumeTime;
    const resumesInPlaceAt = interstitial.startTime + interstitial.resumptionOffset;
    if (Math.abs(resumeTime - resumesInPlaceAt) > ALIGNED_END_THRESHOLD_SECONDS) {
      this.log(`"${interstitial.identifier}" resumption ${resumeTime} not aligned with estimated timeline end ${resumesInPlaceAt}`);
      return false;
    }
    if (!mediaSelection) {
      this.log(`"${interstitial.identifier}" resumption ${resumeTime} can not be aligned with media (none selected)`);
      return false;
    }
    const playlists = Object.keys(mediaSelection);
    return !playlists.some((playlistType) => {
      const details = mediaSelection[playlistType].details;
      const playlistEnd = details.edge;
      if (resumeTime >= playlistEnd) {
        this.log(`"${interstitial.identifier}" resumption ${resumeTime} past ${playlistType} playlist end ${playlistEnd}`);
        return false;
      }
      const startFragment = findFragmentByPTS(null, details.fragments, resumeTime);
      if (!startFragment) {
        this.log(`"${interstitial.identifier}" resumption ${resumeTime} does not align with any fragments in ${playlistType} playlist (${details.fragStart}-${details.fragmentEnd})`);
        return true;
      }
      const allowance = playlistType === "audio" ? 0.175 : 0;
      const alignedWithSegment = Math.abs(startFragment.start - resumeTime) < ALIGNED_END_THRESHOLD_SECONDS + allowance || Math.abs(startFragment.end - resumeTime) < ALIGNED_END_THRESHOLD_SECONDS + allowance;
      if (!alignedWithSegment) {
        this.log(`"${interstitial.identifier}" resumption ${resumeTime} not aligned with ${playlistType} fragment bounds (${startFragment.start}-${startFragment.end} sn: ${startFragment.sn} cc: ${startFragment.cc})`);
        return true;
      }
      return false;
    });
  }
  updateAssetDurations(interstitial) {
    if (!interstitial.assetListLoaded) {
      return;
    }
    const eventStart = interstitial.timelineStart;
    let sumDuration = 0;
    let hasUnknownDuration = false;
    let hasErrors = false;
    interstitial.assetList.forEach((asset, i) => {
      const timelineStart = eventStart + sumDuration;
      asset.startOffset = sumDuration;
      asset.timelineStart = timelineStart;
      hasUnknownDuration || (hasUnknownDuration = asset.duration === null);
      hasErrors || (hasErrors = !!asset.error);
      const duration = asset.error ? 0 : asset.duration || 0;
      sumDuration += duration;
    });
    if (hasUnknownDuration && !hasErrors) {
      interstitial.duration = Math.max(sumDuration, interstitial.duration);
    } else {
      interstitial.duration = sumDuration;
    }
  }
  removeEvent(interstitial) {
    interstitial.reset();
    delete this.eventMap[interstitial.identifier];
  }
};
function segmentToString(segment) {
  return `[${segment.event ? '"' + segment.event.identifier + '"' : "primary"}: ${segment.start.toFixed(2)}-${segment.end.toFixed(2)}]`;
}
var AssetListLoader = class {
  constructor(hls) {
    this.hls = void 0;
    this.hls = hls;
  }
  destroy() {
    this.hls = null;
  }
  loadAssetList(interstitial, hlsStartOffset) {
    const assetListUrl = interstitial.assetListUrl;
    let url;
    try {
      url = getInterstitialUrl(assetListUrl, this.hls.sessionId, interstitial.baseUrl);
    } catch (error) {
      const errorData = this.assignAssetListError(interstitial, ErrorDetails.ASSET_LIST_LOAD_ERROR, error, assetListUrl);
      this.hls.trigger(Events.ERROR, errorData);
      return;
    }
    if (hlsStartOffset && url.protocol !== "data:") {
      url.searchParams.set("_HLS_start_offset", "" + hlsStartOffset);
    }
    const config = this.hls.config;
    const Loader = config.loader;
    const loader = new Loader(config);
    const context = {
      responseType: "json",
      url: url.href
    };
    const loadPolicy = config.interstitialAssetListLoadPolicy.default;
    const loaderConfig = {
      loadPolicy,
      timeout: loadPolicy.maxLoadTimeMs,
      maxRetry: 0,
      retryDelay: 0,
      maxRetryDelay: 0
    };
    const callbacks = {
      onSuccess: (response, stats, context2, networkDetails) => {
        const assetListResponse = response.data;
        const assets = assetListResponse == null ? void 0 : assetListResponse.ASSETS;
        if (!Array.isArray(assets)) {
          const errorData = this.assignAssetListError(interstitial, ErrorDetails.ASSET_LIST_PARSING_ERROR, new Error(`Invalid interstitial asset list`), context2.url, stats, networkDetails);
          this.hls.trigger(Events.ERROR, errorData);
          return;
        }
        interstitial.assetListResponse = assetListResponse;
        this.hls.trigger(Events.ASSET_LIST_LOADED, {
          event: interstitial,
          assetListResponse,
          networkDetails
        });
      },
      onError: (error, context2, networkDetails, stats) => {
        const errorData = this.assignAssetListError(interstitial, ErrorDetails.ASSET_LIST_LOAD_ERROR, new Error(`Error loading X-ASSET-LIST: HTTP status ${error.code} ${error.text} (${context2.url})`), context2.url, stats, networkDetails);
        this.hls.trigger(Events.ERROR, errorData);
      },
      onTimeout: (stats, context2, networkDetails) => {
        const errorData = this.assignAssetListError(interstitial, ErrorDetails.ASSET_LIST_LOAD_TIMEOUT, new Error(`Timeout loading X-ASSET-LIST (${context2.url})`), context2.url, stats, networkDetails);
        this.hls.trigger(Events.ERROR, errorData);
      }
    };
    loader.load(context, loaderConfig, callbacks);
    this.hls.trigger(Events.ASSET_LIST_LOADING, {
      event: interstitial
    });
    return loader;
  }
  assignAssetListError(interstitial, details, error, url, stats, networkDetails) {
    interstitial.error = error;
    return {
      type: ErrorTypes.NETWORK_ERROR,
      details,
      fatal: false,
      interstitial,
      url,
      error,
      networkDetails,
      stats
    };
  }
};
function playWithCatch(media) {
  media == null ? void 0 : media.play().catch(() => {
  });
}
var InterstitialsController = class extends Logger {
  constructor(hls, HlsPlayerClass) {
    super("interstitials", hls.logger);
    this.HlsPlayerClass = void 0;
    this.hls = void 0;
    this.assetListLoader = void 0;
    this.mediaSelection = null;
    this.altSelection = null;
    this.media = null;
    this.detachedData = null;
    this.requiredTracks = null;
    this.manager = null;
    this.playerQueue = [];
    this.bufferedPos = -1;
    this.timelinePos = -1;
    this.schedule = void 0;
    this.playingItem = null;
    this.bufferingItem = null;
    this.waitingItem = null;
    this.endedItem = null;
    this.playingAsset = null;
    this.endedAsset = null;
    this.bufferingAsset = null;
    this.shouldPlay = false;
    this.onPlay = () => {
      this.shouldPlay = true;
    };
    this.onPause = () => {
      this.shouldPlay = false;
    };
    this.onSeeking = () => {
      const currentTime = this.currentTime;
      if (currentTime === void 0 || this.playbackDisabled) {
        return;
      }
      const diff = currentTime - this.timelinePos;
      const roundingError = Math.abs(diff) < 1 / 7056e5;
      if (roundingError) {
        return;
      }
      const backwardSeek = diff <= -0.01;
      this.timelinePos = currentTime;
      this.bufferedPos = currentTime;
      const playingItem = this.playingItem;
      if (!playingItem) {
        this.checkBuffer();
        return;
      }
      if (backwardSeek) {
        const resetCount = this.schedule.resetErrorsInRange(currentTime, currentTime - diff);
        if (resetCount) {
          this.updateSchedule();
        }
      }
      this.checkBuffer();
      if (backwardSeek && currentTime < playingItem.start || currentTime >= playingItem.end) {
        var _this$media;
        const scheduleIndex = this.schedule.findItemIndexAtTime(this.timelinePos);
        if (!this.isInterstitial(playingItem) && (_this$media = this.media) != null && _this$media.paused) {
          this.shouldPlay = false;
        }
        if (!backwardSeek) {
          const playingIndex = this.findItemIndex(playingItem);
          if (scheduleIndex > playingIndex) {
            const jumpIndex = this.schedule.findJumpRestrictedIndex(playingIndex + 1, scheduleIndex);
            if (jumpIndex > playingIndex) {
              this.setSchedulePosition(jumpIndex);
              return;
            }
          }
        }
        this.setSchedulePosition(scheduleIndex);
        return;
      }
      const playingAsset = this.playingAsset;
      if (!playingAsset) {
        if (this.playingLastItem && this.isInterstitial(playingItem)) {
          const restartAsset = playingItem.event.assetList[0];
          if (restartAsset) {
            this.endedItem = this.playingItem;
            this.playingItem = null;
            this.setScheduleToAssetAtTime(currentTime, restartAsset);
          }
        }
        return;
      }
      const start = playingAsset.timelineStart;
      const duration = playingAsset.duration || 0;
      if (backwardSeek && currentTime < start || currentTime >= start + duration) {
        this.setScheduleToAssetAtTime(currentTime, playingAsset);
      }
    };
    this.onTimeupdate = () => {
      const currentTime = this.currentTime;
      if (currentTime === void 0 || this.playbackDisabled) {
        return;
      }
      if (currentTime > this.timelinePos) {
        this.timelinePos = currentTime;
        if (currentTime > this.bufferedPos) {
          this.checkBuffer();
        }
      } else {
        return;
      }
      const playingItem = this.playingItem;
      if (!playingItem || this.playingLastItem) {
        return;
      }
      if (currentTime >= playingItem.end) {
        this.timelinePos = playingItem.end;
        const playingIndex = this.findItemIndex(playingItem);
        this.setSchedulePosition(playingIndex + 1);
      }
      const playingAsset = this.playingAsset;
      if (!playingAsset) {
        return;
      }
      const end = playingAsset.timelineStart + (playingAsset.duration || 0);
      if (currentTime >= end) {
        this.setScheduleToAssetAtTime(currentTime, playingAsset);
      }
    };
    this.onScheduleUpdate = (removedInterstitials, previousItems) => {
      const schedule = this.schedule;
      const playingItem = this.playingItem;
      const interstitialEvents = schedule.events || [];
      const scheduleItems = schedule.items || [];
      const durations = schedule.durations;
      const removedIds = removedInterstitials.map((interstitial) => interstitial.identifier);
      const interstitialsUpdated = !!(interstitialEvents.length || removedIds.length);
      if (interstitialsUpdated || previousItems) {
        this.log(`INTERSTITIALS_UPDATED (${interstitialEvents.length}): ${interstitialEvents}
Schedule: ${scheduleItems.map((seg) => segmentToString(seg))} pos: ${this.timelinePos}`);
      }
      if (removedIds.length) {
        this.log(`Removed events ${removedIds}`);
      }
      this.playerQueue.forEach((player) => {
        if (player.interstitial.appendInPlace) {
          const timelineStart = player.assetItem.timelineStart;
          const diff = player.timelineOffset - timelineStart;
          if (diff) {
            try {
              player.timelineOffset = timelineStart;
            } catch (e) {
              if (Math.abs(diff) > ALIGNED_END_THRESHOLD_SECONDS) {
                this.warn(`${e} ("${player.assetId}" ${player.timelineOffset}->${timelineStart})`);
              }
            }
          }
        }
      });
      let trimInPlaceForPlayout = null;
      if (playingItem) {
        const updatedPlayingItem = this.updateItem(playingItem, this.timelinePos);
        if (this.itemsMatch(playingItem, updatedPlayingItem)) {
          this.playingItem = updatedPlayingItem;
          this.waitingItem = this.endedItem = null;
          trimInPlaceForPlayout = () => this.trimInPlace(updatedPlayingItem, playingItem);
        }
      } else {
        this.waitingItem = this.updateItem(this.waitingItem);
        this.endedItem = this.updateItem(this.endedItem);
      }
      const bufferingItem = this.bufferingItem;
      if (bufferingItem) {
        const updatedBufferingItem = this.updateItem(bufferingItem, this.bufferedPos);
        if (this.itemsMatch(bufferingItem, updatedBufferingItem)) {
          this.bufferingItem = updatedBufferingItem;
          trimInPlaceForPlayout || (trimInPlaceForPlayout = () => this.trimInPlace(updatedBufferingItem, bufferingItem));
        } else if (bufferingItem.event) {
          this.bufferingItem = this.playingItem;
          this.clearInterstitial(bufferingItem.event, null);
        }
      }
      removedInterstitials.forEach((interstitial) => {
        interstitial.assetList.forEach((asset) => {
          this.clearAssetPlayer(asset.identifier, null);
        });
      });
      if (interstitialsUpdated || previousItems) {
        this.hls.trigger(Events.INTERSTITIALS_UPDATED, {
          events: interstitialEvents.slice(0),
          schedule: scheduleItems.slice(0),
          durations,
          removedIds
        });
        if (this.isInterstitial(playingItem) && removedIds.includes(playingItem.event.identifier)) {
          this.warn(`Interstitial "${playingItem.event.identifier}" removed while playing`);
          this.primaryFallback(playingItem.event);
          return;
        }
        if (trimInPlaceForPlayout) {
          trimInPlaceForPlayout();
        }
        this.checkBuffer();
      }
    };
    this.hls = hls;
    this.HlsPlayerClass = HlsPlayerClass;
    this.assetListLoader = new AssetListLoader(hls);
    this.schedule = new InterstitialsSchedule(this.onScheduleUpdate, hls.logger);
    this.registerListeners();
  }
  registerListeners() {
    const hls = this.hls;
    hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);
    hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
    hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
    hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this);
    hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
    hls.on(Events.AUDIO_TRACK_UPDATED, this.onAudioTrackUpdated, this);
    hls.on(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);
    hls.on(Events.SUBTITLE_TRACK_UPDATED, this.onSubtitleTrackUpdated, this);
    hls.on(Events.EVENT_CUE_ENTER, this.onInterstitialCueEnter, this);
    hls.on(Events.ASSET_LIST_LOADED, this.onAssetListLoaded, this);
    hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this);
    hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);
    hls.on(Events.BUFFERED_TO_END, this.onBufferedToEnd, this);
    hls.on(Events.MEDIA_ENDED, this.onMediaEnded, this);
    hls.on(Events.ERROR, this.onError, this);
    hls.on(Events.DESTROYING, this.onDestroying, this);
  }
  unregisterListeners() {
    const hls = this.hls;
    if (!hls) {
      return;
    }
    hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);
    hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
    hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
    hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this);
    hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
    hls.off(Events.AUDIO_TRACK_UPDATED, this.onAudioTrackUpdated, this);
    hls.off(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);
    hls.off(Events.SUBTITLE_TRACK_UPDATED, this.onSubtitleTrackUpdated, this);
    hls.off(Events.EVENT_CUE_ENTER, this.onInterstitialCueEnter, this);
    hls.off(Events.ASSET_LIST_LOADED, this.onAssetListLoaded, this);
    hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this);
    hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this);
    hls.off(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);
    hls.off(Events.BUFFERED_TO_END, this.onBufferedToEnd, this);
    hls.off(Events.MEDIA_ENDED, this.onMediaEnded, this);
    hls.off(Events.ERROR, this.onError, this);
    hls.off(Events.DESTROYING, this.onDestroying, this);
  }
  startLoad() {
    this.resumeBuffering();
  }
  stopLoad() {
    this.pauseBuffering();
  }
  resumeBuffering() {
    var _this$getBufferingPla;
    (_this$getBufferingPla = this.getBufferingPlayer()) == null ? void 0 : _this$getBufferingPla.resumeBuffering();
  }
  pauseBuffering() {
    var _this$getBufferingPla2;
    (_this$getBufferingPla2 = this.getBufferingPlayer()) == null ? void 0 : _this$getBufferingPla2.pauseBuffering();
  }
  destroy() {
    this.unregisterListeners();
    this.stopLoad();
    if (this.assetListLoader) {
      this.assetListLoader.destroy();
    }
    this.emptyPlayerQueue();
    this.clearScheduleState();
    if (this.schedule) {
      this.schedule.destroy();
    }
    this.media = this.detachedData = this.mediaSelection = this.requiredTracks = this.altSelection = this.manager = null;
    this.hls = this.HlsPlayerClass = this.schedule = this.log = null;
    this.assetListLoader = null;
    this.onPlay = this.onPause = this.onSeeking = this.onTimeupdate = null;
    this.onScheduleUpdate = null;
  }
  onDestroying() {
    const media = this.primaryMedia || this.media;
    if (media) {
      this.removeMediaListeners(media);
    }
  }
  removeMediaListeners(media) {
    removeEventListener(media, "play", this.onPlay);
    removeEventListener(media, "pause", this.onPause);
    removeEventListener(media, "seeking", this.onSeeking);
    removeEventListener(media, "timeupdate", this.onTimeupdate);
  }
  onMediaAttaching(event, data) {
    const media = this.media = data.media;
    addEventListener(media, "seeking", this.onSeeking);
    addEventListener(media, "timeupdate", this.onTimeupdate);
    addEventListener(media, "play", this.onPlay);
    addEventListener(media, "pause", this.onPause);
  }
  onMediaAttached(event, data) {
    const playingItem = this.effectivePlayingItem;
    const detachedMedia = this.detachedData;
    this.detachedData = null;
    if (playingItem === null) {
      this.checkStart();
    } else if (!detachedMedia) {
      this.clearScheduleState();
      const playingIndex = this.findItemIndex(playingItem);
      this.setSchedulePosition(playingIndex);
    }
  }
  clearScheduleState() {
    this.playingItem = this.bufferingItem = this.waitingItem = this.endedItem = this.playingAsset = this.endedAsset = this.bufferingAsset = null;
  }
  onMediaDetaching(event, data) {
    const transferringMedia = !!data.transferMedia;
    const media = this.media;
    this.media = null;
    if (transferringMedia) {
      return;
    }
    if (media) {
      this.removeMediaListeners(media);
    }
    if (this.detachedData) {
      const player = this.getBufferingPlayer();
      if (player) {
        this.playingAsset = this.endedAsset = this.bufferingAsset = this.bufferingItem = this.waitingItem = this.detachedData = null;
        player.detachMedia();
      }
      this.shouldPlay = false;
    }
  }
  get interstitialsManager() {
    if (!this.manager) {
      if (!this.hls) {
        return null;
      }
      const c = this;
      const effectiveBufferingItem = () => c.bufferingItem || c.waitingItem;
      const getAssetPlayer = (asset) => asset ? c.getAssetPlayer(asset.identifier) : asset;
      const getMappedTime = (item, timelineType, asset, controllerField, assetPlayerField) => {
        if (item) {
          let time = item[timelineType].start;
          const interstitial = item.event;
          if (interstitial) {
            if (timelineType === "playout" || interstitial.timelineOccupancy !== TimelineOccupancy.Point) {
              const assetPlayer = getAssetPlayer(asset);
              if ((assetPlayer == null ? void 0 : assetPlayer.interstitial) === interstitial) {
                time += assetPlayer.assetItem.startOffset + assetPlayer[assetPlayerField];
              }
            }
          } else {
            const value = controllerField === "bufferedPos" ? getBufferedEnd() : c[controllerField];
            time += value - item.start;
          }
          return time;
        }
        return 0;
      };
      const findMappedTime = (primaryTime, timelineType) => {
        if (primaryTime !== 0 && timelineType !== "primary" && c.schedule.length) {
          var _c$schedule$items;
          const index = c.schedule.findItemIndexAtTime(primaryTime);
          const item = (_c$schedule$items = c.schedule.items) == null ? void 0 : _c$schedule$items[index];
          if (item) {
            const diff = item[timelineType].start - item.start;
            return primaryTime + diff;
          }
        }
        return primaryTime;
      };
      const getBufferedEnd = () => {
        const value = c.bufferedPos;
        if (value === Number.MAX_VALUE) {
          return getMappedDuration("primary");
        }
        return Math.max(value, 0);
      };
      const getMappedDuration = (timelineType) => {
        var _c$primaryDetails;
        if ((_c$primaryDetails = c.primaryDetails) != null && _c$primaryDetails.live) {
          return c.primaryDetails.edge;
        }
        return c.schedule.durations[timelineType];
      };
      const seekTo = (time, timelineType) => {
        var _item$event, _c$schedule$items2;
        const item = c.effectivePlayingItem;
        if (item != null && (_item$event = item.event) != null && _item$event.restrictions.skip) {
          return;
        }
        c.log(`seek to ${time} "${timelineType}"`);
        const playingItem = c.effectivePlayingItem;
        const targetIndex = c.schedule.findItemIndexAtTime(time, timelineType);
        const targetItem = (_c$schedule$items2 = c.schedule.items) == null ? void 0 : _c$schedule$items2[targetIndex];
        const bufferingPlayer = c.getBufferingPlayer();
        const bufferingInterstitial = bufferingPlayer == null ? void 0 : bufferingPlayer.interstitial;
        const appendInPlace = bufferingInterstitial == null ? void 0 : bufferingInterstitial.appendInPlace;
        const seekInItem = playingItem && c.itemsMatch(playingItem, targetItem);
        if (playingItem && (appendInPlace || seekInItem)) {
          const assetPlayer = getAssetPlayer(c.playingAsset);
          const media = (assetPlayer == null ? void 0 : assetPlayer.media) || c.primaryMedia;
          if (media) {
            const currentTime = timelineType === "primary" ? media.currentTime : getMappedTime(playingItem, timelineType, c.playingAsset, "timelinePos", "currentTime");
            const diff = time - currentTime;
            const seekToTime = (appendInPlace ? currentTime : media.currentTime) + diff;
            if (seekToTime >= 0 && (!assetPlayer || appendInPlace || seekToTime <= assetPlayer.duration)) {
              media.currentTime = seekToTime;
              return;
            }
          }
        }
        if (targetItem) {
          let seekToTime = time;
          if (timelineType !== "primary") {
            const primarySegmentStart = targetItem[timelineType].start;
            const diff = time - primarySegmentStart;
            seekToTime = targetItem.start + diff;
          }
          const targetIsPrimary = !c.isInterstitial(targetItem);
          if ((!c.isInterstitial(playingItem) || playingItem.event.appendInPlace) && (targetIsPrimary || targetItem.event.appendInPlace)) {
            const media = c.media || (appendInPlace ? bufferingPlayer == null ? void 0 : bufferingPlayer.media : null);
            if (media) {
              media.currentTime = seekToTime;
            }
          } else if (playingItem) {
            const playingIndex = c.findItemIndex(playingItem);
            if (targetIndex > playingIndex) {
              const jumpIndex = c.schedule.findJumpRestrictedIndex(playingIndex + 1, targetIndex);
              if (jumpIndex > playingIndex) {
                c.setSchedulePosition(jumpIndex);
                return;
              }
            }
            let assetIndex = 0;
            if (targetIsPrimary) {
              c.timelinePos = seekToTime;
              c.checkBuffer();
            } else {
              var _targetItem$event;
              const assetList = targetItem == null ? void 0 : (_targetItem$event = targetItem.event) == null ? void 0 : _targetItem$event.assetList;
              if (assetList) {
                const eventTime = time - (targetItem[timelineType] || targetItem).start;
                for (let i = assetList.length; i--; ) {
                  const asset = assetList[i];
                  if (asset.duration && eventTime >= asset.startOffset && eventTime < asset.startOffset + asset.duration) {
                    assetIndex = i;
                    break;
                  }
                }
              }
            }
            c.setSchedulePosition(targetIndex, assetIndex);
          }
        }
      };
      const getActiveInterstitial = () => {
        const playingItem = c.effectivePlayingItem;
        if (c.isInterstitial(playingItem)) {
          return playingItem;
        }
        const bufferingItem = effectiveBufferingItem();
        if (c.isInterstitial(bufferingItem)) {
          return bufferingItem;
        }
        return null;
      };
      const interstitialPlayer = {
        get currentTime() {
          const interstitialItem = getActiveInterstitial();
          const playingItem = c.effectivePlayingItem;
          if (playingItem && playingItem === interstitialItem) {
            return getMappedTime(playingItem, "playout", c.effectivePlayingAsset, "timelinePos", "currentTime") - playingItem.playout.start;
          }
          return 0;
        },
        set currentTime(time) {
          const interstitialItem = getActiveInterstitial();
          const playingItem = c.effectivePlayingItem;
          if (playingItem && playingItem === interstitialItem) {
            seekTo(time + playingItem.playout.start, "playout");
          }
        },
        get duration() {
          const interstitialItem = getActiveInterstitial();
          if (interstitialItem) {
            return interstitialItem.playout.end - interstitialItem.playout.start;
          }
          return 0;
        },
        get assetPlayers() {
          var _getActiveInterstitia;
          const assetList = (_getActiveInterstitia = getActiveInterstitial()) == null ? void 0 : _getActiveInterstitia.event.assetList;
          if (assetList) {
            return assetList.map((asset) => c.getAssetPlayer(asset.identifier));
          }
          return [];
        },
        get playingIndex() {
          var _getActiveInterstitia2;
          const interstitial = (_getActiveInterstitia2 = getActiveInterstitial()) == null ? void 0 : _getActiveInterstitia2.event;
          if (interstitial && c.effectivePlayingAsset) {
            return interstitial.findAssetIndex(c.effectivePlayingAsset);
          }
          return -1;
        },
        get scheduleItem() {
          return getActiveInterstitial();
        }
      };
      this.manager = {
        get events() {
          var _c$schedule, _c$schedule$events;
          return ((_c$schedule = c.schedule) == null ? void 0 : (_c$schedule$events = _c$schedule.events) == null ? void 0 : _c$schedule$events.slice(0)) || [];
        },
        get schedule() {
          var _c$schedule2, _c$schedule2$items;
          return ((_c$schedule2 = c.schedule) == null ? void 0 : (_c$schedule2$items = _c$schedule2.items) == null ? void 0 : _c$schedule2$items.slice(0)) || [];
        },
        get interstitialPlayer() {
          if (getActiveInterstitial()) {
            return interstitialPlayer;
          }
          return null;
        },
        get playerQueue() {
          return c.playerQueue.slice(0);
        },
        get bufferingAsset() {
          return c.bufferingAsset;
        },
        get bufferingItem() {
          return effectiveBufferingItem();
        },
        get bufferingIndex() {
          const item = effectiveBufferingItem();
          return c.findItemIndex(item);
        },
        get playingAsset() {
          return c.effectivePlayingAsset;
        },
        get playingItem() {
          return c.effectivePlayingItem;
        },
        get playingIndex() {
          const item = c.effectivePlayingItem;
          return c.findItemIndex(item);
        },
        primary: {
          get bufferedEnd() {
            return getBufferedEnd();
          },
          get currentTime() {
            const timelinePos = c.timelinePos;
            return timelinePos > 0 ? timelinePos : 0;
          },
          set currentTime(time) {
            seekTo(time, "primary");
          },
          get duration() {
            return getMappedDuration("primary");
          },
          get seekableStart() {
            var _c$primaryDetails2;
            return ((_c$primaryDetails2 = c.primaryDetails) == null ? void 0 : _c$primaryDetails2.fragmentStart) || 0;
          }
        },
        integrated: {
          get bufferedEnd() {
            return getMappedTime(effectiveBufferingItem(), "integrated", c.bufferingAsset, "bufferedPos", "bufferedEnd");
          },
          get currentTime() {
            return getMappedTime(c.effectivePlayingItem, "integrated", c.effectivePlayingAsset, "timelinePos", "currentTime");
          },
          set currentTime(time) {
            seekTo(time, "integrated");
          },
          get duration() {
            return getMappedDuration("integrated");
          },
          get seekableStart() {
            var _c$primaryDetails3;
            return findMappedTime(((_c$primaryDetails3 = c.primaryDetails) == null ? void 0 : _c$primaryDetails3.fragmentStart) || 0, "integrated");
          }
        },
        skip: () => {
          const item = c.effectivePlayingItem;
          const event = item == null ? void 0 : item.event;
          if (event && !event.restrictions.skip) {
            const index = c.findItemIndex(item);
            if (event.appendInPlace) {
              const time = item.playout.start + item.event.duration;
              seekTo(time + 1e-3, "playout");
            } else {
              c.advanceAfterAssetEnded(event, index, Infinity);
            }
          }
        }
      };
    }
    return this.manager;
  }
  get effectivePlayingItem() {
    return this.waitingItem || this.playingItem || this.endedItem;
  }
  get effectivePlayingAsset() {
    return this.playingAsset || this.endedAsset;
  }
  get playingLastItem() {
    var _this$schedule;
    const playingItem = this.playingItem;
    const items = (_this$schedule = this.schedule) == null ? void 0 : _this$schedule.items;
    if (!this.playbackStarted || !playingItem || !items) {
      return false;
    }
    return this.findItemIndex(playingItem) === items.length - 1;
  }
  get playbackStarted() {
    return this.effectivePlayingItem !== null;
  }
  get currentTime() {
    var _this$bufferingItem, _this$bufferingItem$e, _media;
    if (this.mediaSelection === null) {
      return void 0;
    }
    const queuedForPlayback = this.waitingItem || this.playingItem;
    if (this.isInterstitial(queuedForPlayback) && !queuedForPlayback.event.appendInPlace) {
      return void 0;
    }
    let media = this.media;
    if (!media && (_this$bufferingItem = this.bufferingItem) != null && (_this$bufferingItem$e = _this$bufferingItem.event) != null && _this$bufferingItem$e.appendInPlace) {
      media = this.primaryMedia;
    }
    const currentTime = (_media = media) == null ? void 0 : _media.currentTime;
    if (currentTime === void 0 || !isFiniteNumber(currentTime)) {
      return void 0;
    }
    return currentTime;
  }
  get primaryMedia() {
    var _this$detachedData;
    return this.media || ((_this$detachedData = this.detachedData) == null ? void 0 : _this$detachedData.media) || null;
  }
  isInterstitial(item) {
    return !!(item != null && item.event);
  }
  retreiveMediaSource(assetId, toSegment) {
    const player = this.getAssetPlayer(assetId);
    if (player) {
      this.transferMediaFromPlayer(player, toSegment);
    }
  }
  transferMediaFromPlayer(player, toSegment) {
    const appendInPlace = player.interstitial.appendInPlace;
    const playerMedia = player.media;
    if (appendInPlace && playerMedia === this.primaryMedia) {
      this.bufferingAsset = null;
      if (!toSegment || this.isInterstitial(toSegment) && !toSegment.event.appendInPlace) {
        if (toSegment && playerMedia) {
          this.detachedData = {
            media: playerMedia
          };
          return;
        }
      }
      const attachMediaSourceData = player.transferMedia();
      this.log(`transfer MediaSource from ${player} ${stringify(attachMediaSourceData)}`);
      this.detachedData = attachMediaSourceData;
    } else if (toSegment && playerMedia) {
      this.shouldPlay || (this.shouldPlay = !playerMedia.paused);
    }
  }
  transferMediaTo(player, media) {
    var _this$detachedData2, _attachMediaSourceDat;
    if (player.media === media) {
      return;
    }
    let attachMediaSourceData = null;
    const primaryPlayer = this.hls;
    const isAssetPlayer = player !== primaryPlayer;
    const appendInPlace = isAssetPlayer && player.interstitial.appendInPlace;
    const detachedMediaSource = (_this$detachedData2 = this.detachedData) == null ? void 0 : _this$detachedData2.mediaSource;
    let logFromSource;
    if (primaryPlayer.media) {
      if (appendInPlace) {
        attachMediaSourceData = primaryPlayer.transferMedia();
        this.detachedData = attachMediaSourceData;
      }
      logFromSource = `Primary`;
    } else if (detachedMediaSource) {
      const bufferingPlayer = this.getBufferingPlayer();
      if (bufferingPlayer) {
        attachMediaSourceData = bufferingPlayer.transferMedia();
        logFromSource = `${bufferingPlayer}`;
      } else {
        logFromSource = `detached MediaSource`;
      }
    } else {
      logFromSource = `detached media`;
    }
    if (!attachMediaSourceData) {
      if (detachedMediaSource) {
        attachMediaSourceData = this.detachedData;
        this.log(`using detachedData: MediaSource ${stringify(attachMediaSourceData)}`);
      } else if (!this.detachedData || primaryPlayer.media === media) {
        const playerQueue = this.playerQueue;
        if (playerQueue.length > 1) {
          playerQueue.forEach((queuedPlayer) => {
            if (isAssetPlayer && queuedPlayer.interstitial.appendInPlace !== appendInPlace) {
              const interstitial = queuedPlayer.interstitial;
              this.clearInterstitial(queuedPlayer.interstitial, null);
              interstitial.appendInPlace = false;
              if (interstitial.appendInPlace) {
                this.warn(`Could not change append strategy for queued assets ${interstitial}`);
              }
            }
          });
        }
        this.hls.detachMedia();
        this.detachedData = {
          media
        };
      }
    }
    const transferring = attachMediaSourceData && "mediaSource" in attachMediaSourceData && ((_attachMediaSourceDat = attachMediaSourceData.mediaSource) == null ? void 0 : _attachMediaSourceDat.readyState) !== "closed";
    const dataToAttach = transferring && attachMediaSourceData ? attachMediaSourceData : media;
    this.log(`${transferring ? "transfering MediaSource" : "attaching media"} to ${isAssetPlayer ? player : "Primary"} from ${logFromSource}`);
    if (dataToAttach === attachMediaSourceData) {
      const isAssetAtEndOfSchedule = isAssetPlayer && player.assetId === this.schedule.assetIdAtEnd;
      dataToAttach.overrides = {
        duration: this.schedule.duration,
        endOfStream: !isAssetPlayer || isAssetAtEndOfSchedule,
        cueRemoval: !isAssetPlayer
      };
    }
    player.attachMedia(dataToAttach);
  }
  onInterstitialCueEnter() {
    this.onTimeupdate();
  }
  checkStart() {
    const schedule = this.schedule;
    const interstitialEvents = schedule.events;
    if (!interstitialEvents || this.playbackDisabled || !this.media) {
      return;
    }
    if (this.bufferedPos === -1) {
      this.bufferedPos = 0;
    }
    const timelinePos = this.timelinePos;
    const effectivePlayingItem = this.effectivePlayingItem;
    if (timelinePos === -1) {
      const startPosition = this.hls.startPosition;
      this.timelinePos = startPosition;
      if (interstitialEvents.length && interstitialEvents[0].cue.pre) {
        const index = schedule.findEventIndex(interstitialEvents[0].identifier);
        this.setSchedulePosition(index);
      } else if (startPosition >= 0 || !this.primaryLive) {
        const start = this.timelinePos = startPosition > 0 ? startPosition : 0;
        const index = schedule.findItemIndexAtTime(start);
        this.setSchedulePosition(index);
      }
    } else if (effectivePlayingItem && !this.playingItem) {
      const index = schedule.findItemIndex(effectivePlayingItem);
      this.setSchedulePosition(index);
    }
  }
  advanceAfterAssetEnded(interstitial, index, assetListIndex) {
    const nextAssetIndex = getNextAssetIndex(interstitial, assetListIndex);
    if (!interstitial.isAssetPastPlayoutLimit(nextAssetIndex)) {
      this.setSchedulePosition(index, nextAssetIndex);
    } else {
      const scheduleItems = this.schedule.items;
      if (scheduleItems) {
        const nextIndex = index + 1;
        const scheduleLength = scheduleItems.length;
        if (nextIndex >= scheduleLength) {
          this.setSchedulePosition(-1);
          return;
        }
        const resumptionTime = interstitial.resumeTime;
        if (this.timelinePos < resumptionTime) {
          this.timelinePos = resumptionTime;
          this.checkBuffer();
        }
        this.setSchedulePosition(nextIndex);
      }
    }
  }
  setScheduleToAssetAtTime(time, playingAsset) {
    const schedule = this.schedule;
    const parentIdentifier = playingAsset.parentIdentifier;
    const interstitial = schedule.getEvent(parentIdentifier);
    if (interstitial) {
      const itemIndex = schedule.findEventIndex(parentIdentifier);
      const assetListIndex = schedule.findAssetIndex(interstitial, time);
      this.advanceAfterAssetEnded(interstitial, itemIndex, assetListIndex - 1);
    }
  }
  setSchedulePosition(index, assetListIndex) {
    const scheduleItems = this.schedule.items;
    if (!scheduleItems || this.playbackDisabled) {
      return;
    }
    this.log(`setSchedulePosition ${index}, ${assetListIndex}`);
    const scheduledItem = index >= 0 ? scheduleItems[index] : null;
    const currentItem = this.playingItem;
    const playingLastItem = this.playingLastItem;
    if (this.isInterstitial(currentItem)) {
      var _interstitial$assetLi;
      const interstitial = currentItem.event;
      const playingAsset = this.playingAsset;
      const assetId = playingAsset == null ? void 0 : playingAsset.identifier;
      const player = assetId ? this.getAssetPlayer(assetId) : null;
      if (player && assetId && (!this.eventItemsMatch(currentItem, scheduledItem) || assetListIndex !== void 0 && assetId !== ((_interstitial$assetLi = interstitial.assetList) == null ? void 0 : _interstitial$assetLi[assetListIndex].identifier))) {
        var _this$detachedData3;
        const playingAssetListIndex = interstitial.findAssetIndex(playingAsset);
        this.log(`INTERSTITIAL_ASSET_ENDED ${playingAssetListIndex + 1}/${interstitial.assetList.length} ${eventAssetToString(playingAsset)}`);
        this.endedAsset = playingAsset;
        this.playingAsset = null;
        this.hls.trigger(Events.INTERSTITIAL_ASSET_ENDED, {
          asset: playingAsset,
          assetListIndex: playingAssetListIndex,
          event: interstitial,
          schedule: scheduleItems.slice(0),
          scheduleIndex: index,
          player
        });
        if (currentItem !== this.playingItem) {
          if (this.itemsMatch(currentItem, this.playingItem) && !this.playingAsset) {
            this.advanceAfterAssetEnded(interstitial, this.findItemIndex(this.playingItem), playingAssetListIndex);
          }
          return;
        }
        this.retreiveMediaSource(assetId, scheduledItem);
        if (player.media && !((_this$detachedData3 = this.detachedData) != null && _this$detachedData3.mediaSource)) {
          player.detachMedia();
        }
      }
      if (!this.eventItemsMatch(currentItem, scheduledItem)) {
        this.endedItem = currentItem;
        this.playingItem = null;
        this.log(`INTERSTITIAL_ENDED ${interstitial} ${segmentToString(currentItem)}`);
        interstitial.hasPlayed = true;
        this.hls.trigger(Events.INTERSTITIAL_ENDED, {
          event: interstitial,
          schedule: scheduleItems.slice(0),
          scheduleIndex: index
        });
        if (interstitial.cue.once) {
          this.updateSchedule();
          const items = this.schedule.items;
          if (scheduledItem && items) {
            const updatedIndex = this.findItemIndex(scheduledItem);
            this.advanceSchedule(updatedIndex, items, assetListIndex, currentItem, playingLastItem);
          }
          return;
        }
      }
    }
    this.advanceSchedule(index, scheduleItems, assetListIndex, currentItem, playingLastItem);
  }
  advanceSchedule(index, scheduleItems, assetListIndex, currentItem, playedLastItem) {
    const scheduledItem = index >= 0 ? scheduleItems[index] : null;
    const media = this.primaryMedia;
    const playerQueue = this.playerQueue;
    if (playerQueue.length) {
      playerQueue.forEach((player) => {
        const interstitial = player.interstitial;
        const queuedIndex = this.schedule.findEventIndex(interstitial.identifier);
        if (queuedIndex < index || queuedIndex > index + 1) {
          this.clearInterstitial(interstitial, scheduledItem);
        }
      });
    }
    if (this.isInterstitial(scheduledItem)) {
      this.timelinePos = Math.min(Math.max(this.timelinePos, scheduledItem.start), scheduledItem.end);
      const interstitial = scheduledItem.event;
      if (assetListIndex === void 0) {
        assetListIndex = this.schedule.findAssetIndex(interstitial, this.timelinePos);
        const assetIndexCandidate = getNextAssetIndex(interstitial, assetListIndex - 1);
        if (interstitial.isAssetPastPlayoutLimit(assetIndexCandidate)) {
          this.advanceAfterAssetEnded(interstitial, index, assetListIndex);
          return;
        }
        assetListIndex = assetIndexCandidate;
      }
      const waitingItem = this.waitingItem;
      if (!this.assetsBuffered(scheduledItem, media)) {
        this.setBufferingItem(scheduledItem);
      }
      let player = this.preloadAssets(interstitial, assetListIndex);
      if (!this.eventItemsMatch(scheduledItem, waitingItem || currentItem)) {
        this.waitingItem = scheduledItem;
        this.log(`INTERSTITIAL_STARTED ${segmentToString(scheduledItem)} ${interstitial.appendInPlace ? "append in place" : ""}`);
        this.hls.trigger(Events.INTERSTITIAL_STARTED, {
          event: interstitial,
          schedule: scheduleItems.slice(0),
          scheduleIndex: index
        });
      }
      if (!interstitial.assetListLoaded) {
        this.log(`Waiting for ASSET-LIST to complete loading ${interstitial}`);
        return;
      }
      if (interstitial.assetListLoader) {
        interstitial.assetListLoader.destroy();
        interstitial.assetListLoader = void 0;
      }
      if (!media) {
        this.log(`Waiting for attachMedia to start Interstitial ${interstitial}`);
        return;
      }
      this.waitingItem = this.endedItem = null;
      this.playingItem = scheduledItem;
      const assetItem = interstitial.assetList[assetListIndex];
      if (!assetItem) {
        const nextItem = scheduleItems[index + 1];
        const _media2 = this.media;
        if (nextItem && _media2 && !this.isInterstitial(nextItem) && _media2.currentTime < nextItem.start) {
          _media2.currentTime = this.timelinePos = nextItem.start;
        }
        this.advanceAfterAssetEnded(interstitial, index, assetListIndex || 0);
        return;
      }
      if (!player) {
        player = this.getAssetPlayer(assetItem.identifier);
      }
      if (player === null || player.destroyed) {
        const assetListLength = interstitial.assetList.length;
        this.warn(`asset ${assetListIndex + 1}/${assetListLength} player destroyed ${interstitial}`);
        player = this.createAssetPlayer(interstitial, assetItem, assetListIndex);
      }
      if (!this.eventItemsMatch(scheduledItem, this.bufferingItem)) {
        if (interstitial.appendInPlace && this.isAssetBuffered(assetItem)) {
          return;
        }
      }
      this.startAssetPlayer(player, assetListIndex, scheduleItems, index, media);
      if (this.shouldPlay) {
        playWithCatch(player.media);
      }
    } else if (scheduledItem !== null) {
      this.resumePrimary(scheduledItem, index, currentItem);
      if (this.shouldPlay) {
        playWithCatch(this.hls.media);
      }
    } else if (playedLastItem && this.isInterstitial(currentItem)) {
      this.endedItem = null;
      this.playingItem = currentItem;
      if (!currentItem.event.appendInPlace) {
        this.attachPrimary(this.schedule.durations.primary, null);
      }
    }
  }
  get playbackDisabled() {
    return this.hls.config.enableInterstitialPlayback === false;
  }
  get primaryDetails() {
    var _this$mediaSelection, _this$mediaSelection$;
    return (_this$mediaSelection = this.mediaSelection) == null ? void 0 : (_this$mediaSelection$ = _this$mediaSelection.main) == null ? void 0 : _this$mediaSelection$.details;
  }
  get primaryLive() {
    var _this$primaryDetails;
    return !!((_this$primaryDetails = this.primaryDetails) != null && _this$primaryDetails.live);
  }
  resumePrimary(scheduledItem, index, fromItem) {
    var _this$detachedData4;
    this.playingItem = scheduledItem;
    this.playingAsset = this.endedAsset = null;
    this.waitingItem = this.endedItem = null;
    this.bufferedToItem(scheduledItem);
    this.log(`resuming ${segmentToString(scheduledItem)}`);
    if (!((_this$detachedData4 = this.detachedData) != null && _this$detachedData4.mediaSource)) {
      let timelinePos = this.timelinePos;
      if (timelinePos < scheduledItem.start || timelinePos >= scheduledItem.end) {
        timelinePos = this.getPrimaryResumption(scheduledItem, index);
        this.timelinePos = timelinePos;
      }
      this.attachPrimary(timelinePos, scheduledItem);
    }
    if (!fromItem) {
      return;
    }
    const scheduleItems = this.schedule.items;
    if (!scheduleItems) {
      return;
    }
    this.log(`INTERSTITIALS_PRIMARY_RESUMED ${segmentToString(scheduledItem)}`);
    this.hls.trigger(Events.INTERSTITIALS_PRIMARY_RESUMED, {
      schedule: scheduleItems.slice(0),
      scheduleIndex: index
    });
    this.checkBuffer();
  }
  getPrimaryResumption(scheduledItem, index) {
    const itemStart = scheduledItem.start;
    if (this.primaryLive) {
      const details = this.primaryDetails;
      if (index === 0) {
        return this.hls.startPosition;
      } else if (details && (itemStart < details.fragmentStart || itemStart > details.edge)) {
        return this.hls.liveSyncPosition || -1;
      }
    }
    return itemStart;
  }
  isAssetBuffered(asset) {
    const player = this.getAssetPlayer(asset.identifier);
    if (player != null && player.hls) {
      return player.hls.bufferedToEnd;
    }
    const bufferInfo = BufferHelper.bufferInfo(this.primaryMedia, this.timelinePos, 0);
    return bufferInfo.end + 1 >= asset.timelineStart + (asset.duration || 0);
  }
  attachPrimary(timelinePos, item, skipSeekToStartPosition) {
    if (item) {
      this.setBufferingItem(item);
    } else {
      this.bufferingItem = this.playingItem;
    }
    this.bufferingAsset = null;
    const media = this.primaryMedia;
    if (!media) {
      return;
    }
    const hls = this.hls;
    if (hls.media) {
      this.checkBuffer();
    } else {
      this.transferMediaTo(hls, media);
      if (skipSeekToStartPosition) {
        this.startLoadingPrimaryAt(timelinePos, skipSeekToStartPosition);
      }
    }
    if (!skipSeekToStartPosition) {
      this.timelinePos = timelinePos;
      this.startLoadingPrimaryAt(timelinePos, skipSeekToStartPosition);
    }
  }
  startLoadingPrimaryAt(timelinePos, skipSeekToStartPosition) {
    var _hls$mainForwardBuffe;
    const hls = this.hls;
    if (!hls.loadingEnabled || !hls.media || Math.abs((((_hls$mainForwardBuffe = hls.mainForwardBufferInfo) == null ? void 0 : _hls$mainForwardBuffe.start) || hls.media.currentTime) - timelinePos) > 0.5) {
      hls.startLoad(timelinePos, skipSeekToStartPosition);
    } else if (!hls.bufferingEnabled) {
      hls.resumeBuffering();
    }
  }
  onManifestLoading() {
    this.stopLoad();
    this.schedule.reset();
    this.emptyPlayerQueue();
    this.clearScheduleState();
    this.shouldPlay = false;
    this.bufferedPos = this.timelinePos = -1;
    this.mediaSelection = this.altSelection = this.manager = this.requiredTracks = null;
    this.hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this);
    this.hls.on(Events.BUFFER_CODECS, this.onBufferCodecs, this);
  }
  onLevelUpdated(event, data) {
    if (data.level === -1) {
      return;
    }
    const main = this.hls.levels[data.level];
    const currentSelection = _objectSpread2(_objectSpread2({}, this.mediaSelection || this.altSelection), {}, {
      main
    });
    this.mediaSelection = currentSelection;
    this.schedule.parseInterstitialDateRanges(currentSelection, this.hls.config.interstitialAppendInPlace);
    if (!this.effectivePlayingItem && this.schedule.items) {
      this.checkStart();
    }
  }
  onAudioTrackUpdated(event, data) {
    const audio = this.hls.audioTracks[data.id];
    const previousSelection = this.mediaSelection;
    if (!previousSelection) {
      this.altSelection = _objectSpread2(_objectSpread2({}, this.altSelection), {}, {
        audio
      });
      return;
    }
    const currentSelection = _objectSpread2(_objectSpread2({}, previousSelection), {}, {
      audio
    });
    this.mediaSelection = currentSelection;
  }
  onSubtitleTrackUpdated(event, data) {
    const subtitles = this.hls.subtitleTracks[data.id];
    const previousSelection = this.mediaSelection;
    if (!previousSelection) {
      this.altSelection = _objectSpread2(_objectSpread2({}, this.altSelection), {}, {
        subtitles
      });
      return;
    }
    const currentSelection = _objectSpread2(_objectSpread2({}, previousSelection), {}, {
      subtitles
    });
    this.mediaSelection = currentSelection;
  }
  onAudioTrackSwitching(event, data) {
    const audioOption = getBasicSelectionOption(data);
    this.playerQueue.forEach((player) => player.hls.setAudioOption(data) || player.hls.setAudioOption(audioOption));
  }
  onSubtitleTrackSwitch(event, data) {
    const subtitleOption = getBasicSelectionOption(data);
    this.playerQueue.forEach((player) => player.hls.setSubtitleOption(data) || data.id !== -1 && player.hls.setSubtitleOption(subtitleOption));
  }
  onBufferCodecs(event, data) {
    const requiredTracks = data.tracks;
    if (requiredTracks) {
      this.requiredTracks = requiredTracks;
    }
  }
  onBufferAppended(event, data) {
    this.checkBuffer();
  }
  onBufferFlushed(event, data) {
    const playingItem = this.playingItem;
    if (playingItem && !this.itemsMatch(playingItem, this.bufferingItem) && !this.isInterstitial(playingItem)) {
      const timelinePos = this.timelinePos;
      this.bufferedPos = timelinePos;
      this.checkBuffer();
    }
  }
  onBufferedToEnd(event) {
    const interstitialEvents = this.schedule.events;
    if (this.bufferedPos < Number.MAX_VALUE && interstitialEvents) {
      for (let i = 0; i < interstitialEvents.length; i++) {
        const interstitial = interstitialEvents[i];
        if (interstitial.cue.post) {
          var _this$schedule$items;
          const scheduleIndex = this.schedule.findEventIndex(interstitial.identifier);
          const item = (_this$schedule$items = this.schedule.items) == null ? void 0 : _this$schedule$items[scheduleIndex];
          if (this.isInterstitial(item) && this.eventItemsMatch(item, this.bufferingItem)) {
            this.bufferedToItem(item, 0);
          }
          break;
        }
      }
      this.bufferedPos = Number.MAX_VALUE;
    }
  }
  onMediaEnded(event) {
    const playingItem = this.playingItem;
    if (!this.playingLastItem && playingItem) {
      const playingIndex = this.findItemIndex(playingItem);
      this.setSchedulePosition(playingIndex + 1);
    } else {
      this.shouldPlay = false;
    }
  }
  updateItem(previousItem, time) {
    const items = this.schedule.items;
    if (previousItem && items) {
      const index = this.findItemIndex(previousItem, time);
      return items[index] || null;
    }
    return null;
  }
  trimInPlace(updatedItem, itemBeforeUpdate) {
    if (this.isInterstitial(updatedItem) && updatedItem.event.appendInPlace && itemBeforeUpdate.end - updatedItem.end > 0.25) {
      updatedItem.event.assetList.forEach((asset, index) => {
        if (updatedItem.event.isAssetPastPlayoutLimit(index)) {
          this.clearAssetPlayer(asset.identifier, null);
        }
      });
      const flushStart = updatedItem.end + 0.25;
      const bufferInfo = BufferHelper.bufferInfo(this.primaryMedia, flushStart, 0);
      if (bufferInfo.end > flushStart || (bufferInfo.nextStart || 0) > flushStart) {
        this.attachPrimary(flushStart, null);
        this.flushFrontBuffer(flushStart);
      }
    }
  }
  itemsMatch(a, b) {
    return !!b && (a === b || a.event && b.event && this.eventItemsMatch(a, b) || !a.event && !b.event && this.findItemIndex(a) === this.findItemIndex(b));
  }
  eventItemsMatch(a, b) {
    var _b$event;
    return !!b && (a === b || a.event.identifier === ((_b$event = b.event) == null ? void 0 : _b$event.identifier));
  }
  findItemIndex(item, time) {
    return item ? this.schedule.findItemIndex(item, time) : -1;
  }
  updateSchedule() {
    const mediaSelection = this.mediaSelection;
    if (!mediaSelection) {
      return;
    }
    this.schedule.updateSchedule(mediaSelection, []);
  }
  checkBuffer(starved) {
    const items = this.schedule.items;
    if (!items) {
      return;
    }
    const bufferInfo = BufferHelper.bufferInfo(this.primaryMedia, this.timelinePos, 0);
    if (starved) {
      this.bufferedPos = this.timelinePos;
    }
    starved || (starved = bufferInfo.len < 1);
    this.updateBufferedPos(bufferInfo.end, items, starved);
  }
  updateBufferedPos(bufferEnd, items, bufferIsEmpty) {
    const schedule = this.schedule;
    const bufferingItem = this.bufferingItem;
    if (this.bufferedPos > bufferEnd) {
      return;
    }
    if (items.length === 1 && this.itemsMatch(items[0], bufferingItem)) {
      this.bufferedPos = bufferEnd;
      return;
    }
    const playingItem = this.playingItem;
    const playingIndex = this.findItemIndex(playingItem);
    let bufferEndIndex = schedule.findItemIndexAtTime(bufferEnd);
    if (this.bufferedPos < bufferEnd) {
      var _nextItemToBuffer$eve, _bufferingItem$event;
      const bufferingIndex = this.findItemIndex(bufferingItem);
      const nextToBufferIndex = Math.min(bufferingIndex + 1, items.length - 1);
      const nextItemToBuffer = items[nextToBufferIndex];
      if (bufferEndIndex === -1 && bufferingItem && bufferEnd >= bufferingItem.end || (_nextItemToBuffer$eve = nextItemToBuffer.event) != null && _nextItemToBuffer$eve.appendInPlace && bufferEnd + 0.01 >= nextItemToBuffer.start) {
        bufferEndIndex = nextToBufferIndex;
      }
      if (nextToBufferIndex - playingIndex > 1 && (bufferingItem == null ? void 0 : (_bufferingItem$event = bufferingItem.event) == null ? void 0 : _bufferingItem$event.appendInPlace) === false) {
        return;
      }
      this.bufferedPos = bufferEnd;
      if (bufferEndIndex > bufferingIndex && bufferEndIndex > playingIndex) {
        this.bufferedToItem(nextItemToBuffer);
      } else {
        const details = this.primaryDetails;
        if (this.primaryLive && details && bufferEnd > details.edge - details.targetduration && nextItemToBuffer.start < details.edge + this.hls.config.interstitialLiveLookAhead && this.isInterstitial(nextItemToBuffer)) {
          this.preloadAssets(nextItemToBuffer.event, 0);
        }
      }
    } else if (bufferIsEmpty && playingItem && !this.itemsMatch(playingItem, bufferingItem)) {
      if (bufferEndIndex === playingIndex) {
        this.bufferedToItem(playingItem);
      } else if (bufferEndIndex === playingIndex + 1) {
        this.bufferedToItem(items[bufferEndIndex]);
      }
    }
  }
  assetsBuffered(item, media) {
    const assetList = item.event.assetList;
    if (assetList.length === 0) {
      return false;
    }
    return !item.event.assetList.some((asset) => {
      const player = this.getAssetPlayer(asset.identifier);
      return !(player != null && player.bufferedInPlaceToEnd(media));
    });
  }
  setBufferingItem(item) {
    const bufferingLast = this.bufferingItem;
    const schedule = this.schedule;
    if (!this.itemsMatch(item, bufferingLast)) {
      const {
        items,
        events
      } = schedule;
      if (!items || !events) {
        return bufferingLast;
      }
      const isInterstitial = this.isInterstitial(item);
      const bufferingPlayer = this.getBufferingPlayer();
      this.bufferingItem = item;
      this.bufferedPos = Math.max(item.start, Math.min(item.end, this.timelinePos));
      const timeRemaining = bufferingPlayer ? bufferingPlayer.remaining : bufferingLast ? bufferingLast.end - this.timelinePos : 0;
      this.log(`INTERSTITIALS_BUFFERED_TO_BOUNDARY ${segmentToString(item)}` + (bufferingLast ? ` (${timeRemaining.toFixed(2)} remaining)` : ""));
      if (!this.playbackDisabled) {
        if (isInterstitial) {
          item.event.assetList.forEach((asset) => {
            const player = this.getAssetPlayer(asset.identifier);
            if (player) {
              player.resumeBuffering();
            }
          });
        } else {
          this.hls.resumeBuffering();
          this.playerQueue.forEach((player) => player.pauseBuffering());
        }
      }
      this.hls.trigger(Events.INTERSTITIALS_BUFFERED_TO_BOUNDARY, {
        events: events.slice(0),
        schedule: items.slice(0),
        bufferingIndex: this.findItemIndex(item),
        playingIndex: this.findItemIndex(this.playingItem)
      });
    } else if (this.bufferingItem !== item) {
      this.bufferingItem = item;
    }
    return bufferingLast;
  }
  bufferedToItem(item, assetListIndex = 0) {
    const bufferingLast = this.setBufferingItem(item);
    if (this.playbackDisabled) {
      return;
    }
    if (this.isInterstitial(item)) {
      this.bufferedToEvent(item, assetListIndex);
    } else if (bufferingLast !== null) {
      this.bufferingAsset = null;
      const detachedData = this.detachedData;
      if (detachedData) {
        if (detachedData.mediaSource) {
          const skipSeekToStartPosition = true;
          this.attachPrimary(item.start, item, skipSeekToStartPosition);
        } else {
          this.preloadPrimary(item);
        }
      } else {
        this.preloadPrimary(item);
      }
    }
  }
  preloadPrimary(item) {
    const index = this.findItemIndex(item);
    const timelinePos = this.getPrimaryResumption(item, index);
    this.startLoadingPrimaryAt(timelinePos);
  }
  bufferedToEvent(item, assetListIndex) {
    const interstitial = item.event;
    const neverLoaded = interstitial.assetList.length === 0 && !interstitial.assetListLoader;
    const playOnce = interstitial.cue.once;
    if (neverLoaded || !playOnce) {
      const player = this.preloadAssets(interstitial, assetListIndex);
      if (player != null && player.interstitial.appendInPlace) {
        const assetItem = interstitial.assetList[assetListIndex];
        const media = this.primaryMedia;
        if (assetItem && media) {
          this.bufferAssetPlayer(player, media);
        }
      }
    }
  }
  preloadAssets(interstitial, assetListIndex) {
    const uri = interstitial.assetUrl;
    const assetListLength = interstitial.assetList.length;
    const neverLoaded = assetListLength === 0 && !interstitial.assetListLoader;
    const playOnce = interstitial.cue.once;
    if (neverLoaded) {
      const timelineStart = interstitial.timelineStart;
      if (interstitial.appendInPlace) {
        var _playingItem$nextEven;
        const playingItem = this.playingItem;
        if (!this.isInterstitial(playingItem) && (playingItem == null ? void 0 : (_playingItem$nextEven = playingItem.nextEvent) == null ? void 0 : _playingItem$nextEven.identifier) === interstitial.identifier) {
          this.flushFrontBuffer(timelineStart + 0.25);
        }
      }
      let hlsStartOffset;
      let liveStartPosition = 0;
      if (!this.playingItem && this.primaryLive) {
        liveStartPosition = this.hls.startPosition;
        if (liveStartPosition === -1) {
          liveStartPosition = this.hls.liveSyncPosition || 0;
        }
      }
      if (liveStartPosition && !(interstitial.cue.pre || interstitial.cue.post)) {
        const startOffset = liveStartPosition - timelineStart;
        if (startOffset > 0) {
          hlsStartOffset = Math.round(startOffset * 1e3) / 1e3;
        }
      }
      this.log(`Load interstitial asset ${assetListIndex + 1}/${uri ? 1 : assetListLength} ${interstitial}${hlsStartOffset ? ` live-start: ${liveStartPosition} start-offset: ${hlsStartOffset}` : ""}`);
      if (uri) {
        return this.createAsset(interstitial, 0, 0, timelineStart, interstitial.duration, uri);
      }
      const assetListLoader = this.assetListLoader.loadAssetList(interstitial, hlsStartOffset);
      if (assetListLoader) {
        interstitial.assetListLoader = assetListLoader;
      }
    } else if (!playOnce && assetListLength) {
      for (let i = assetListIndex; i < assetListLength; i++) {
        const asset = interstitial.assetList[i];
        const playerIndex = this.getAssetPlayerQueueIndex(asset.identifier);
        if ((playerIndex === -1 || this.playerQueue[playerIndex].destroyed) && !asset.error) {
          this.createAssetPlayer(interstitial, asset, i);
        }
      }
      return this.getAssetPlayer(interstitial.assetList[assetListIndex].identifier);
    }
    return null;
  }
  flushFrontBuffer(startOffset) {
    const requiredTracks = this.requiredTracks;
    if (!requiredTracks) {
      return;
    }
    this.log(`Removing front buffer starting at ${startOffset}`);
    const sourceBufferNames = Object.keys(requiredTracks);
    sourceBufferNames.forEach((type) => {
      this.hls.trigger(Events.BUFFER_FLUSHING, {
        startOffset,
        endOffset: Infinity,
        type
      });
    });
  }
  getAssetPlayerQueueIndex(assetId) {
    const playerQueue = this.playerQueue;
    for (let i = 0; i < playerQueue.length; i++) {
      if (assetId === playerQueue[i].assetId) {
        return i;
      }
    }
    return -1;
  }
  getAssetPlayer(assetId) {
    const index = this.getAssetPlayerQueueIndex(assetId);
    return this.playerQueue[index] || null;
  }
  getBufferingPlayer() {
    const {
      playerQueue,
      primaryMedia
    } = this;
    if (primaryMedia) {
      for (let i = 0; i < playerQueue.length; i++) {
        if (playerQueue[i].media === primaryMedia) {
          return playerQueue[i];
        }
      }
    }
    return null;
  }
  createAsset(interstitial, assetListIndex, startOffset, timelineStart, duration, uri) {
    const assetItem = {
      parentIdentifier: interstitial.identifier,
      identifier: generateAssetIdentifier(interstitial, uri, assetListIndex),
      duration,
      startOffset,
      timelineStart,
      uri
    };
    return this.createAssetPlayer(interstitial, assetItem, assetListIndex);
  }
  createAssetPlayer(interstitial, assetItem, assetListIndex) {
    const primary = this.hls;
    const userConfig = primary.userConfig;
    let videoPreference = userConfig.videoPreference;
    const currentLevel = primary.loadLevelObj || primary.levels[primary.currentLevel];
    if (videoPreference || currentLevel) {
      videoPreference = _extends({}, videoPreference);
      if (currentLevel.videoCodec) {
        videoPreference.videoCodec = currentLevel.videoCodec;
      }
      if (currentLevel.videoRange) {
        videoPreference.allowedVideoRanges = [currentLevel.videoRange];
      }
    }
    const selectedAudio = primary.audioTracks[primary.audioTrack];
    const selectedSubtitle = primary.subtitleTracks[primary.subtitleTrack];
    let startPosition = 0;
    if (this.primaryLive || interstitial.appendInPlace) {
      const timePastStart = this.timelinePos - assetItem.timelineStart;
      if (timePastStart > 1) {
        const duration = assetItem.duration;
        if (duration && timePastStart < duration) {
          startPosition = timePastStart;
        }
      }
    }
    const assetId = assetItem.identifier;
    const playerConfig = _objectSpread2(_objectSpread2({}, userConfig), {}, {
      autoStartLoad: true,
      startFragPrefetch: true,
      primarySessionId: primary.sessionId,
      assetPlayerId: assetId,
      abrEwmaDefaultEstimate: primary.bandwidthEstimate,
      interstitialsController: void 0,
      startPosition,
      liveDurationInfinity: false,
      testBandwidth: false,
      videoPreference,
      audioPreference: selectedAudio || userConfig.audioPreference,
      subtitlePreference: selectedSubtitle || userConfig.subtitlePreference
    });
    if (interstitial.appendInPlace) {
      interstitial.appendInPlaceStarted = true;
      if (assetItem.timelineStart) {
        playerConfig.timelineOffset = assetItem.timelineStart;
      }
    }
    const cmcd = playerConfig.cmcd;
    if (cmcd != null && cmcd.sessionId && cmcd.contentId) {
      playerConfig.cmcd = _extends({}, cmcd, {
        contentId: hash(assetItem.uri)
      });
    }
    if (this.getAssetPlayer(assetId)) {
      this.warn(`Duplicate date range identifier ${interstitial} and asset ${assetId}`);
    }
    const player = new HlsAssetPlayer(this.HlsPlayerClass, playerConfig, interstitial, assetItem);
    this.playerQueue.push(player);
    interstitial.assetList[assetListIndex] = assetItem;
    const updateAssetPlayerDetails = (details) => {
      if (details.live) {
        const error = new Error(`Interstitials MUST be VOD assets ${interstitial}`);
        const errorData = {
          fatal: true,
          type: ErrorTypes.OTHER_ERROR,
          details: ErrorDetails.INTERSTITIAL_ASSET_ITEM_ERROR,
          error
        };
        this.handleAssetItemError(errorData, interstitial, this.schedule.findEventIndex(interstitial.identifier), assetListIndex, error.message);
        return;
      }
      const duration = details.edge - details.fragmentStart;
      const currentAssetDuration = assetItem.duration;
      if (currentAssetDuration === null || duration > currentAssetDuration) {
        this.log(`Interstitial asset "${assetId}" duration change ${currentAssetDuration} > ${duration}`);
        assetItem.duration = duration;
        this.updateSchedule();
      }
    };
    player.on(Events.LEVEL_UPDATED, (event, {
      details
    }) => updateAssetPlayerDetails(details));
    player.on(Events.LEVEL_PTS_UPDATED, (event, {
      details
    }) => updateAssetPlayerDetails(details));
    const onBufferCodecs = (event, data) => {
      const inQueuPlayer = this.getAssetPlayer(assetId);
      if (inQueuPlayer && data.tracks) {
        inQueuPlayer.off(Events.BUFFER_CODECS, onBufferCodecs);
        inQueuPlayer.tracks = data.tracks;
        const media = this.primaryMedia;
        if (this.bufferingAsset === inQueuPlayer.assetItem && media && !inQueuPlayer.media) {
          this.bufferAssetPlayer(inQueuPlayer, media);
        }
      }
    };
    player.on(Events.BUFFER_CODECS, onBufferCodecs);
    const bufferedToEnd = () => {
      var _this$schedule$items2;
      const inQueuPlayer = this.getAssetPlayer(assetId);
      this.log(`buffered to end of asset ${inQueuPlayer}`);
      if (!inQueuPlayer) {
        return;
      }
      const scheduleIndex = this.schedule.findEventIndex(interstitial.identifier);
      const item = (_this$schedule$items2 = this.schedule.items) == null ? void 0 : _this$schedule$items2[scheduleIndex];
      if (this.isInterstitial(item)) {
        const assetListIndex2 = interstitial.findAssetIndex(assetItem);
        const nextAssetIndex = getNextAssetIndex(interstitial, assetListIndex2);
        if (!interstitial.isAssetPastPlayoutLimit(nextAssetIndex)) {
          this.bufferedToItem(item, nextAssetIndex);
        } else {
          var _this$schedule$items3;
          const nextItem = (_this$schedule$items3 = this.schedule.items) == null ? void 0 : _this$schedule$items3[scheduleIndex + 1];
          if (nextItem) {
            this.bufferedToItem(nextItem);
          }
        }
      }
    };
    player.on(Events.BUFFERED_TO_END, bufferedToEnd);
    const endedWithAssetIndex = (assetIndex) => {
      return () => {
        const inQueuPlayer = this.getAssetPlayer(assetId);
        if (!inQueuPlayer) {
          return;
        }
        this.shouldPlay = true;
        const scheduleIndex = this.schedule.findEventIndex(interstitial.identifier);
        this.advanceAfterAssetEnded(interstitial, scheduleIndex, assetIndex);
      };
    };
    player.once(Events.MEDIA_ENDED, endedWithAssetIndex(assetListIndex));
    player.once(Events.PLAYOUT_LIMIT_REACHED, endedWithAssetIndex(Infinity));
    player.on(Events.ERROR, (event, data) => {
      const inQueuPlayer = this.getAssetPlayer(assetId);
      if (data.details === ErrorDetails.BUFFER_STALLED_ERROR) {
        if (inQueuPlayer != null && inQueuPlayer.media) {
          const assetCurrentTime = inQueuPlayer.currentTime;
          const distanceFromEnd = inQueuPlayer.duration - assetCurrentTime;
          if (assetCurrentTime && interstitial.appendInPlace && distanceFromEnd / inQueuPlayer.media.playbackRate < 0.5) {
            this.log(`Advancing buffer past end of asset ${assetId} ${interstitial} at ${inQueuPlayer.media.currentTime}`);
            bufferedToEnd();
          } else {
            this.warn(`Stalled at ${assetCurrentTime} of ${assetCurrentTime + distanceFromEnd} in asset ${assetId} ${interstitial}`);
            this.onTimeupdate();
            this.checkBuffer(true);
          }
        }
        return;
      }
      this.handleAssetItemError(data, interstitial, this.schedule.findEventIndex(interstitial.identifier), assetListIndex, `Asset player error ${data.error} ${interstitial}`);
    });
    player.on(Events.DESTROYING, () => {
      const inQueuPlayer = this.getAssetPlayer(assetId);
      if (!inQueuPlayer) {
        return;
      }
      const error = new Error(`Asset player destroyed unexpectedly ${assetId}`);
      const errorData = {
        fatal: true,
        type: ErrorTypes.OTHER_ERROR,
        details: ErrorDetails.INTERSTITIAL_ASSET_ITEM_ERROR,
        error
      };
      this.handleAssetItemError(errorData, interstitial, this.schedule.findEventIndex(interstitial.identifier), assetListIndex, error.message);
    });
    this.log(`INTERSTITIAL_ASSET_PLAYER_CREATED ${eventAssetToString(assetItem)}`);
    this.hls.trigger(Events.INTERSTITIAL_ASSET_PLAYER_CREATED, {
      asset: assetItem,
      assetListIndex,
      event: interstitial,
      player
    });
    return player;
  }
  clearInterstitial(interstitial, toSegment) {
    interstitial.assetList.forEach((asset) => {
      this.clearAssetPlayer(asset.identifier, toSegment);
    });
    interstitial.reset();
  }
  resetAssetPlayer(assetId) {
    const playerIndex = this.getAssetPlayerQueueIndex(assetId);
    if (playerIndex !== -1) {
      this.log(`reset asset player "${assetId}" after error`);
      const player = this.playerQueue[playerIndex];
      this.transferMediaFromPlayer(player, null);
      player.resetDetails();
    }
  }
  clearAssetPlayer(assetId, toSegment) {
    const playerIndex = this.getAssetPlayerQueueIndex(assetId);
    if (playerIndex !== -1) {
      this.log(`clear asset player "${assetId}" toSegment: ${toSegment ? segmentToString(toSegment) : toSegment}`);
      const player = this.playerQueue[playerIndex];
      this.transferMediaFromPlayer(player, toSegment);
      this.playerQueue.splice(playerIndex, 1);
      player.destroy();
    }
  }
  emptyPlayerQueue() {
    let player;
    while (player = this.playerQueue.pop()) {
      player.destroy();
    }
    this.playerQueue = [];
  }
  startAssetPlayer(player, assetListIndex, scheduleItems, scheduleIndex, media) {
    const {
      interstitial,
      assetItem,
      assetId
    } = player;
    const assetListLength = interstitial.assetList.length;
    const playingAsset = this.playingAsset;
    this.endedAsset = null;
    this.playingAsset = assetItem;
    if (!playingAsset || playingAsset.identifier !== assetId) {
      if (playingAsset) {
        this.clearAssetPlayer(playingAsset.identifier, scheduleItems[scheduleIndex]);
        delete playingAsset.error;
      }
      this.log(`INTERSTITIAL_ASSET_STARTED ${assetListIndex + 1}/${assetListLength} ${eventAssetToString(assetItem)}`);
      this.hls.trigger(Events.INTERSTITIAL_ASSET_STARTED, {
        asset: assetItem,
        assetListIndex,
        event: interstitial,
        schedule: scheduleItems.slice(0),
        scheduleIndex,
        player
      });
    }
    this.bufferAssetPlayer(player, media);
  }
  bufferAssetPlayer(player, media) {
    var _this$schedule$items4, _this$detachedData5;
    const {
      interstitial,
      assetItem
    } = player;
    const scheduleIndex = this.schedule.findEventIndex(interstitial.identifier);
    const item = (_this$schedule$items4 = this.schedule.items) == null ? void 0 : _this$schedule$items4[scheduleIndex];
    if (!item) {
      return;
    }
    this.setBufferingItem(item);
    this.bufferingAsset = assetItem;
    const bufferingPlayer = this.getBufferingPlayer();
    if (bufferingPlayer === player) {
      return;
    }
    const appendInPlaceNext = interstitial.appendInPlace;
    if (appendInPlaceNext && (bufferingPlayer == null ? void 0 : bufferingPlayer.interstitial.appendInPlace) === false) {
      return;
    }
    const activeTracks = (bufferingPlayer == null ? void 0 : bufferingPlayer.tracks) || ((_this$detachedData5 = this.detachedData) == null ? void 0 : _this$detachedData5.tracks) || this.requiredTracks;
    if (appendInPlaceNext && assetItem !== this.playingAsset) {
      if (!player.tracks) {
        return;
      }
      if (activeTracks && !isCompatibleTrackChange(activeTracks, player.tracks)) {
        const error = new Error(`Asset ${eventAssetToString(assetItem)} SourceBuffer tracks ('${Object.keys(player.tracks)}') are not compatible with primary content tracks ('${Object.keys(activeTracks)}')`);
        const errorData = {
          fatal: true,
          type: ErrorTypes.OTHER_ERROR,
          details: ErrorDetails.INTERSTITIAL_ASSET_ITEM_ERROR,
          error
        };
        const assetListIndex = interstitial.findAssetIndex(assetItem);
        this.handleAssetItemError(errorData, interstitial, scheduleIndex, assetListIndex, error.message);
        return;
      }
    }
    this.transferMediaTo(player, media);
  }
  handleAssetItemError(data, interstitial, scheduleIndex, assetListIndex, errorMessage) {
    if (data.details === ErrorDetails.BUFFER_STALLED_ERROR) {
      return;
    }
    const assetItem = interstitial.assetList[assetListIndex];
    this.warn(`INTERSTITIAL_ASSET_ERROR ${assetItem ? eventAssetToString(assetItem) : assetItem} ${data.error}`);
    const assetId = assetItem == null ? void 0 : assetItem.identifier;
    const playerIndex = this.getAssetPlayerQueueIndex(assetId);
    const player = this.playerQueue[playerIndex] || null;
    const items = this.schedule.items;
    const interstitialAssetError = _extends({}, data, {
      fatal: false,
      errorAction: createDoNothingErrorAction(true),
      asset: assetItem,
      assetListIndex,
      event: interstitial,
      schedule: items,
      scheduleIndex,
      player
    });
    this.hls.trigger(Events.INTERSTITIAL_ASSET_ERROR, interstitialAssetError);
    if (!data.fatal) {
      return;
    }
    const playingAsset = this.playingAsset;
    const error = new Error(errorMessage);
    if (assetItem) {
      this.clearAssetPlayer(assetId, null);
      assetItem.error = error;
    }
    if (!interstitial.assetList.some((asset) => !asset.error)) {
      interstitial.error = error;
    } else {
      for (let i = assetListIndex; i < interstitial.assetList.length; i++) {
        this.resetAssetPlayer(interstitial.assetList[i].identifier);
      }
      this.updateSchedule();
    }
    if (interstitial.error) {
      this.primaryFallback(interstitial);
    } else if (playingAsset && playingAsset.identifier === assetId) {
      this.advanceAfterAssetEnded(interstitial, scheduleIndex, assetListIndex);
    }
  }
  primaryFallback(interstitial) {
    const flushStart = interstitial.timelineStart;
    const playingItem = this.effectivePlayingItem;
    this.updateSchedule();
    if (playingItem) {
      this.log(`Fallback to primary from event "${interstitial.identifier}" start: ${flushStart} pos: ${this.timelinePos} playing: ${playingItem ? segmentToString(playingItem) : ""} error: ${interstitial.error}`);
      let timelinePos = this.timelinePos;
      if (timelinePos === -1) {
        timelinePos = this.hls.startPosition;
      }
      const newPlayingItem = this.updateItem(playingItem, timelinePos);
      if (this.itemsMatch(playingItem, newPlayingItem)) {
        this.clearInterstitial(interstitial, null);
      }
      if (interstitial.appendInPlace) {
        this.attachPrimary(flushStart, null);
        this.flushFrontBuffer(flushStart);
      }
      const scheduleIndex = this.schedule.findItemIndexAtTime(timelinePos);
      this.setSchedulePosition(scheduleIndex);
    } else {
      this.checkStart();
    }
  }
  onAssetListLoaded(event, data) {
    var _this$bufferingItem2;
    const interstitial = data.event;
    const interstitialId = interstitial.identifier;
    const assets = data.assetListResponse.ASSETS;
    if (!this.schedule.hasEvent(interstitialId)) {
      return;
    }
    const eventStart = interstitial.timelineStart;
    const previousDuration = interstitial.duration;
    let sumDuration = 0;
    assets.forEach((asset, assetListIndex) => {
      const duration = parseFloat(asset.DURATION);
      this.createAsset(interstitial, assetListIndex, sumDuration, eventStart + sumDuration, duration, asset.URI);
      sumDuration += duration;
    });
    interstitial.duration = sumDuration;
    this.log(`Loaded asset-list with duration: ${sumDuration} (was: ${previousDuration}) ${interstitial}`);
    const waitingItem = this.waitingItem;
    const waitingForItem = (waitingItem == null ? void 0 : waitingItem.event.identifier) === interstitialId;
    this.updateSchedule();
    const bufferingEvent = (_this$bufferingItem2 = this.bufferingItem) == null ? void 0 : _this$bufferingItem2.event;
    if (waitingForItem) {
      var _this$schedule$items5;
      const scheduleIndex = this.schedule.findEventIndex(interstitialId);
      const item = (_this$schedule$items5 = this.schedule.items) == null ? void 0 : _this$schedule$items5[scheduleIndex];
      if (item) {
        if (!this.playingItem && this.timelinePos > item.end) {
          const index = this.schedule.findItemIndexAtTime(this.timelinePos);
          if (index !== scheduleIndex) {
            interstitial.error = new Error(`Interstitial no longer within playback range ${this.timelinePos} ${interstitial}`);
            this.primaryFallback(interstitial);
            return;
          }
        }
        this.setBufferingItem(item);
      }
      this.setSchedulePosition(scheduleIndex);
    } else if ((bufferingEvent == null ? void 0 : bufferingEvent.identifier) === interstitialId && bufferingEvent.appendInPlace) {
      const assetItem = interstitial.assetList[0];
      const player = this.getAssetPlayer(assetItem.identifier);
      const media = this.primaryMedia;
      if (assetItem && player && media) {
        this.bufferAssetPlayer(player, media);
      }
    }
  }
  onError(event, data) {
    switch (data.details) {
      case ErrorDetails.ASSET_LIST_PARSING_ERROR:
      case ErrorDetails.ASSET_LIST_LOAD_ERROR:
      case ErrorDetails.ASSET_LIST_LOAD_TIMEOUT: {
        const interstitial = data.interstitial;
        if (interstitial) {
          this.primaryFallback(interstitial);
        }
        break;
      }
      case ErrorDetails.BUFFER_STALLED_ERROR: {
        this.onTimeupdate();
        this.checkBuffer(true);
        break;
      }
    }
  }
};
var TICK_INTERVAL$2 = 500;
var SubtitleStreamController = class extends BaseStreamController {
  constructor(hls, fragmentTracker, keyLoader) {
    super(hls, fragmentTracker, keyLoader, "subtitle-stream-controller", PlaylistLevelType.SUBTITLE);
    this.currentTrackId = -1;
    this.tracksBuffered = [];
    this.mainDetails = null;
    this.registerListeners();
  }
  onHandlerDestroying() {
    this.unregisterListeners();
    super.onHandlerDestroying();
    this.mainDetails = null;
  }
  registerListeners() {
    super.registerListeners();
    const {
      hls
    } = this;
    hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
    hls.on(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);
    hls.on(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);
    hls.on(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this);
    hls.on(Events.SUBTITLE_FRAG_PROCESSED, this.onSubtitleFragProcessed, this);
    hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
  }
  unregisterListeners() {
    super.unregisterListeners();
    const {
      hls
    } = this;
    hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
    hls.off(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);
    hls.off(Events.SUBTITLE_TRACK_SWITCH, this.onSubtitleTrackSwitch, this);
    hls.off(Events.SUBTITLE_TRACK_LOADED, this.onSubtitleTrackLoaded, this);
    hls.off(Events.SUBTITLE_FRAG_PROCESSED, this.onSubtitleFragProcessed, this);
    hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
  }
  startLoad(startPosition, skipSeekToStartPosition) {
    this.stopLoad();
    this.state = State.IDLE;
    this.setInterval(TICK_INTERVAL$2);
    this.nextLoadPosition = this.lastCurrentTime = startPosition + this.timelineOffset;
    this.startPosition = skipSeekToStartPosition ? -1 : startPosition;
    this.tick();
  }
  onManifestLoading() {
    super.onManifestLoading();
    this.mainDetails = null;
  }
  onMediaDetaching(event, data) {
    this.tracksBuffered = [];
    super.onMediaDetaching(event, data);
  }
  onLevelLoaded(event, data) {
    this.mainDetails = data.details;
  }
  onSubtitleFragProcessed(event, data) {
    const {
      frag,
      success
    } = data;
    if (!this.fragContextChanged(frag)) {
      if (isMediaFragment(frag)) {
        this.fragPrevious = frag;
      }
      this.state = State.IDLE;
    }
    if (!success) {
      return;
    }
    const buffered = this.tracksBuffered[this.currentTrackId];
    if (!buffered) {
      return;
    }
    let timeRange;
    const fragStart = frag.start;
    for (let i = 0; i < buffered.length; i++) {
      if (fragStart >= buffered[i].start && fragStart <= buffered[i].end) {
        timeRange = buffered[i];
        break;
      }
    }
    const fragEnd = frag.start + frag.duration;
    if (timeRange) {
      timeRange.end = fragEnd;
    } else {
      timeRange = {
        start: fragStart,
        end: fragEnd
      };
      buffered.push(timeRange);
    }
    this.fragmentTracker.fragBuffered(frag);
    this.fragBufferedComplete(frag, null);
    if (this.media) {
      this.tick();
    }
  }
  onBufferFlushing(event, data) {
    const {
      startOffset,
      endOffset
    } = data;
    if (startOffset === 0 && endOffset !== Number.POSITIVE_INFINITY) {
      const endOffsetSubtitles = endOffset - 1;
      if (endOffsetSubtitles <= 0) {
        return;
      }
      data.endOffsetSubtitles = Math.max(0, endOffsetSubtitles);
      this.tracksBuffered.forEach((buffered) => {
        for (let i = 0; i < buffered.length; ) {
          if (buffered[i].end <= endOffsetSubtitles) {
            buffered.shift();
            continue;
          } else if (buffered[i].start < endOffsetSubtitles) {
            buffered[i].start = endOffsetSubtitles;
          } else {
            break;
          }
          i++;
        }
      });
      this.fragmentTracker.removeFragmentsInRange(startOffset, endOffsetSubtitles, PlaylistLevelType.SUBTITLE);
    }
  }
  onError(event, data) {
    const frag = data.frag;
    if ((frag == null ? void 0 : frag.type) === PlaylistLevelType.SUBTITLE) {
      if (data.details === ErrorDetails.FRAG_GAP) {
        this.fragmentTracker.fragBuffered(frag, true);
      }
      if (this.fragCurrent) {
        this.fragCurrent.abortRequests();
      }
      if (this.state !== State.STOPPED) {
        this.state = State.IDLE;
      }
    }
  }
  onSubtitleTracksUpdated(event, {
    subtitleTracks
  }) {
    if (this.levels && subtitleOptionsIdentical(this.levels, subtitleTracks)) {
      this.levels = subtitleTracks.map((mediaPlaylist) => new Level(mediaPlaylist));
      return;
    }
    this.tracksBuffered = [];
    this.levels = subtitleTracks.map((mediaPlaylist) => {
      const level = new Level(mediaPlaylist);
      this.tracksBuffered[level.id] = [];
      return level;
    });
    this.fragmentTracker.removeFragmentsInRange(0, Number.POSITIVE_INFINITY, PlaylistLevelType.SUBTITLE);
    this.fragPrevious = null;
    this.mediaBuffer = null;
  }
  onSubtitleTrackSwitch(event, data) {
    var _this$levels;
    this.currentTrackId = data.id;
    if (!((_this$levels = this.levels) != null && _this$levels.length) || this.currentTrackId === -1) {
      this.clearInterval();
      return;
    }
    const currentTrack = this.levels[this.currentTrackId];
    if (currentTrack != null && currentTrack.details) {
      this.mediaBuffer = this.mediaBufferTimeRanges;
    } else {
      this.mediaBuffer = null;
    }
    if (currentTrack && this.state !== State.STOPPED) {
      this.setInterval(TICK_INTERVAL$2);
    }
  }
  onSubtitleTrackLoaded(event, data) {
    var _track$details;
    const {
      currentTrackId,
      levels
    } = this;
    const {
      details: newDetails,
      id: trackId
    } = data;
    if (!levels) {
      this.warn(`Subtitle tracks were reset while loading level ${trackId}`);
      return;
    }
    const track = levels[trackId];
    if (trackId >= levels.length || !track) {
      return;
    }
    this.log(`Subtitle track ${trackId} loaded [${newDetails.startSN},${newDetails.endSN}]${newDetails.lastPartSn ? `[part-${newDetails.lastPartSn}-${newDetails.lastPartIndex}]` : ""},duration:${newDetails.totalduration}`);
    this.mediaBuffer = this.mediaBufferTimeRanges;
    let sliding = 0;
    if (newDetails.live || (_track$details = track.details) != null && _track$details.live) {
      if (newDetails.deltaUpdateFailed) {
        return;
      }
      const mainDetails = this.mainDetails;
      if (!mainDetails) {
        this.startFragRequested = false;
        return;
      }
      const mainSlidingStartFragment = mainDetails.fragments[0];
      if (!track.details) {
        if (newDetails.hasProgramDateTime && mainDetails.hasProgramDateTime) {
          alignMediaPlaylistByPDT(newDetails, mainDetails);
          sliding = newDetails.fragmentStart;
        } else if (mainSlidingStartFragment) {
          sliding = mainSlidingStartFragment.start;
          addSliding(newDetails, sliding);
        }
      } else {
        var _this$levelLastLoaded;
        sliding = this.alignPlaylists(newDetails, track.details, (_this$levelLastLoaded = this.levelLastLoaded) == null ? void 0 : _this$levelLastLoaded.details);
        if (sliding === 0 && mainSlidingStartFragment) {
          sliding = mainSlidingStartFragment.start;
          addSliding(newDetails, sliding);
        }
      }
      if (mainDetails && !this.startFragRequested) {
        this.setStartPosition(mainDetails, sliding);
      }
    }
    track.details = newDetails;
    this.levelLastLoaded = track;
    if (trackId !== currentTrackId) {
      return;
    }
    this.hls.trigger(Events.SUBTITLE_TRACK_UPDATED, {
      details: newDetails,
      id: trackId,
      groupId: data.groupId
    });
    this.tick();
    if (newDetails.live && !this.fragCurrent && this.media && this.state === State.IDLE) {
      const foundFrag = findFragmentByPTS(null, newDetails.fragments, this.media.currentTime, 0);
      if (!foundFrag) {
        this.warn("Subtitle playlist not aligned with playback");
        track.details = void 0;
      }
    }
  }
  _handleFragmentLoadComplete(fragLoadedData) {
    const {
      frag,
      payload
    } = fragLoadedData;
    const decryptData = frag.decryptdata;
    const hls = this.hls;
    if (this.fragContextChanged(frag)) {
      return;
    }
    if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && isFullSegmentEncryption(decryptData.method)) {
      const startTime = performance.now();
      this.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer, getAesModeFromFullSegmentMethod(decryptData.method)).catch((err) => {
        hls.trigger(Events.ERROR, {
          type: ErrorTypes.MEDIA_ERROR,
          details: ErrorDetails.FRAG_DECRYPT_ERROR,
          fatal: false,
          error: err,
          reason: err.message,
          frag
        });
        throw err;
      }).then((decryptedData) => {
        const endTime = performance.now();
        hls.trigger(Events.FRAG_DECRYPTED, {
          frag,
          payload: decryptedData,
          stats: {
            tstart: startTime,
            tdecrypt: endTime
          }
        });
      }).catch((err) => {
        this.warn(`${err.name}: ${err.message}`);
        this.state = State.IDLE;
      });
    }
  }
  doTick() {
    if (!this.media) {
      this.state = State.IDLE;
      return;
    }
    if (this.state === State.IDLE) {
      const {
        currentTrackId,
        levels
      } = this;
      const track = levels == null ? void 0 : levels[currentTrackId];
      if (!track || !levels.length || !track.details) {
        return;
      }
      if (this.waitForLive(track)) {
        return;
      }
      const {
        config
      } = this;
      const currentTime = this.getLoadPosition();
      const bufferedInfo = BufferHelper.bufferedInfo(this.tracksBuffered[this.currentTrackId] || [], currentTime, config.maxBufferHole);
      const {
        end: targetBufferTime,
        len: bufferLen
      } = bufferedInfo;
      const trackDetails = track.details;
      const maxBufLen = this.hls.maxBufferLength + trackDetails.levelTargetDuration;
      if (bufferLen > maxBufLen) {
        return;
      }
      const fragments = trackDetails.fragments;
      const fragLen = fragments.length;
      const end = trackDetails.edge;
      let foundFrag = null;
      const fragPrevious = this.fragPrevious;
      if (targetBufferTime < end) {
        const tolerance = config.maxFragLookUpTolerance;
        const lookupTolerance = targetBufferTime > end - tolerance ? 0 : tolerance;
        foundFrag = findFragmentByPTS(fragPrevious, fragments, Math.max(fragments[0].start, targetBufferTime), lookupTolerance);
        if (!foundFrag && fragPrevious && fragPrevious.start < fragments[0].start) {
          foundFrag = fragments[0];
        }
      } else {
        foundFrag = fragments[fragLen - 1];
      }
      foundFrag = this.filterReplacedPrimary(foundFrag, track.details);
      if (!foundFrag) {
        return;
      }
      const curSNIdx = foundFrag.sn - trackDetails.startSN;
      const prevFrag = fragments[curSNIdx - 1];
      if (prevFrag && prevFrag.cc === foundFrag.cc && this.fragmentTracker.getState(prevFrag) === FragmentState.NOT_LOADED) {
        foundFrag = prevFrag;
      }
      if (this.fragmentTracker.getState(foundFrag) === FragmentState.NOT_LOADED) {
        const fragToLoad = this.mapToInitFragWhenRequired(foundFrag);
        if (fragToLoad) {
          this.loadFragment(fragToLoad, track, targetBufferTime);
        }
      }
    }
  }
  loadFragment(frag, level, targetBufferTime) {
    if (!isMediaFragment(frag)) {
      this._loadInitSegment(frag, level);
    } else {
      super.loadFragment(frag, level, targetBufferTime);
    }
  }
  get mediaBufferTimeRanges() {
    return new BufferableInstance(this.tracksBuffered[this.currentTrackId] || []);
  }
};
var BufferableInstance = class {
  constructor(timeranges) {
    this.buffered = void 0;
    const getRange = (name, index, length) => {
      index = index >>> 0;
      if (index > length - 1) {
        throw new DOMException(`Failed to execute '${name}' on 'TimeRanges': The index provided (${index}) is greater than the maximum bound (${length})`);
      }
      return timeranges[index][name];
    };
    this.buffered = {
      get length() {
        return timeranges.length;
      },
      end(index) {
        return getRange("end", index, timeranges.length);
      },
      start(index) {
        return getRange("start", index, timeranges.length);
      }
    };
  }
};
var specialCea608CharsCodes = {
  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
};
var getCharForByte = (byte) => String.fromCharCode(specialCea608CharsCodes[byte] || byte);
var NR_ROWS = 15;
var NR_COLS = 100;
var rowsLowCh1 = {
  17: 1,
  18: 3,
  21: 5,
  22: 7,
  23: 9,
  16: 11,
  19: 12,
  20: 14
};
var rowsHighCh1 = {
  17: 2,
  18: 4,
  21: 6,
  22: 8,
  23: 10,
  19: 13,
  20: 15
};
var rowsLowCh2 = {
  25: 1,
  26: 3,
  29: 5,
  30: 7,
  31: 9,
  24: 11,
  27: 12,
  28: 14
};
var rowsHighCh2 = {
  25: 2,
  26: 4,
  29: 6,
  30: 8,
  31: 10,
  27: 13,
  28: 15
};
var backgroundColors = ["white", "green", "blue", "cyan", "red", "yellow", "magenta", "black", "transparent"];
var CaptionsLogger = class {
  constructor() {
    this.time = null;
    this.verboseLevel = 0;
  }
  log(severity, msg) {
    if (this.verboseLevel >= severity) {
      const m = typeof msg === "function" ? msg() : msg;
      logger.log(`${this.time} [${severity}] ${m}`);
    }
  }
};
var numArrayToHexArray = function numArrayToHexArray2(numArray) {
  const hexArray = [];
  for (let j = 0; j < numArray.length; j++) {
    hexArray.push(numArray[j].toString(16));
  }
  return hexArray;
};
var PenState = class {
  constructor() {
    this.foreground = "white";
    this.underline = false;
    this.italics = false;
    this.background = "black";
    this.flash = false;
  }
  reset() {
    this.foreground = "white";
    this.underline = false;
    this.italics = false;
    this.background = "black";
    this.flash = false;
  }
  setStyles(styles) {
    const attribs = ["foreground", "underline", "italics", "background", "flash"];
    for (let i = 0; i < attribs.length; i++) {
      const style = attribs[i];
      if (styles.hasOwnProperty(style)) {
        this[style] = styles[style];
      }
    }
  }
  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;
  }
};
var StyledUnicodeChar = class {
  constructor() {
    this.uchar = " ";
    this.penState = new PenState();
  }
  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();
  }
};
var Row = class {
  constructor(logger2) {
    this.chars = [];
    this.pos = 0;
    this.currPenState = new PenState();
    this.cueStartTime = null;
    this.logger = void 0;
    for (let i = 0; i < NR_COLS; i++) {
      this.chars.push(new StyledUnicodeChar());
    }
    this.logger = logger2;
  }
  equals(other) {
    for (let i = 0; i < NR_COLS; i++) {
      if (!this.chars[i].equals(other.chars[i])) {
        return false;
      }
    }
    return true;
  }
  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;
  }
  setCursor(absPos) {
    if (this.pos !== absPos) {
      this.pos = absPos;
    }
    if (this.pos < 0) {
      this.logger.log(3, "Negative cursor position " + this.pos);
      this.pos = 0;
    } else if (this.pos > NR_COLS) {
      this.logger.log(3, "Too large cursor position " + this.pos);
      this.pos = NR_COLS;
    }
  }
  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() {
    this.moveCursor(-1);
    this.chars[this.pos].setChar(" ", this.currPenState);
  }
  insertChar(byte) {
    if (byte >= 144) {
      this.backSpace();
    }
    const char = getCharForByte(byte);
    if (this.pos >= NR_COLS) {
      this.logger.log(0, () => "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);
  }
};
var CaptionScreen = class {
  constructor(logger2) {
    this.rows = [];
    this.currRow = NR_ROWS - 1;
    this.nrRollUpRows = null;
    this.lastOutputScreen = null;
    this.logger = void 0;
    for (let i = 0; i < NR_ROWS; i++) {
      this.rows.push(new Row(logger2));
    }
    this.logger = logger2;
  }
  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();
  }
  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) {
    this.logger.log(2, "setCursor: " + absPos);
    const row = this.rows[this.currRow];
    row.setCursor(absPos);
  }
  setPAC(pacData) {
    this.logger.log(2, () => "pacData = " + stringify(pacData));
    let newRow = pacData.row - 1;
    if (this.nrRollUpRows && newRow < this.nrRollUpRows - 1) {
      newRow = this.nrRollUpRows - 1;
    }
    if (this.nrRollUpRows && this.currRow !== newRow) {
      for (let i = 0; i < NR_ROWS; i++) {
        this.rows[i].clear();
      }
      const topRowIndex = this.currRow + 1 - this.nrRollUpRows;
      const lastOutputScreen = this.lastOutputScreen;
      if (lastOutputScreen) {
        const prevLineTime = lastOutputScreen.rows[topRowIndex].cueStartTime;
        const time = this.logger.time;
        if (prevLineTime !== null && time !== null && prevLineTime < 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);
  }
  setBkgData(bkgData) {
    this.logger.log(2, () => "bkgData = " + stringify(bkgData));
    this.backSpace();
    this.setPen(bkgData);
    this.insertChar(32);
  }
  setRollUpRows(nrRows) {
    this.nrRollUpRows = nrRows;
  }
  rollUp() {
    if (this.nrRollUpRows === null) {
      this.logger.log(3, "roll_up but nrRollUpRows not set yet");
      return;
    }
    this.logger.log(1, () => 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);
    this.logger.log(2, "Rolling up");
  }
  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 Cea608Channel = class {
  constructor(channelNumber, outputFilter, logger2) {
    this.chNr = void 0;
    this.outputFilter = void 0;
    this.mode = void 0;
    this.verbose = void 0;
    this.displayedMemory = void 0;
    this.nonDisplayedMemory = void 0;
    this.lastOutputScreen = void 0;
    this.currRollUpRow = void 0;
    this.writeScreen = void 0;
    this.cueStartTime = void 0;
    this.logger = void 0;
    this.chNr = channelNumber;
    this.outputFilter = outputFilter;
    this.mode = null;
    this.verbose = 0;
    this.displayedMemory = new CaptionScreen(logger2);
    this.nonDisplayedMemory = new CaptionScreen(logger2);
    this.lastOutputScreen = new CaptionScreen(logger2);
    this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1];
    this.writeScreen = this.displayedMemory;
    this.mode = null;
    this.cueStartTime = null;
    this.logger = logger2;
  }
  reset() {
    this.mode = null;
    this.displayedMemory.reset();
    this.nonDisplayedMemory.reset();
    this.lastOutputScreen.reset();
    this.outputFilter.reset();
    this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1];
    this.writeScreen = this.displayedMemory;
    this.mode = null;
    this.cueStartTime = 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;
    this.logger.log(2, () => "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";
    this.logger.log(2, () => screen + ": " + this.writeScreen.getDisplayText(true));
    if (this.mode === "MODE_PAINT-ON" || this.mode === "MODE_ROLL-UP") {
      this.logger.log(1, () => "DISPLAYED: " + this.displayedMemory.getDisplayText(true));
      this.outputDataUpdate();
    }
  }
  ccRCL() {
    this.logger.log(2, "RCL - Resume Caption Loading");
    this.setMode("MODE_POP-ON");
  }
  ccBS() {
    this.logger.log(2, "BS - BackSpace");
    if (this.mode === "MODE_TEXT") {
      return;
    }
    this.writeScreen.backSpace();
    if (this.writeScreen === this.displayedMemory) {
      this.outputDataUpdate();
    }
  }
  ccAOF() {
  }
  ccAON() {
  }
  ccDER() {
    this.logger.log(2, "DER- Delete to End of Row");
    this.writeScreen.clearToEndOfRow();
    this.outputDataUpdate();
  }
  ccRU(nrRows) {
    this.logger.log(2, "RU(" + nrRows + ") - Roll Up");
    this.writeScreen = this.displayedMemory;
    this.setMode("MODE_ROLL-UP");
    this.writeScreen.setRollUpRows(nrRows);
  }
  ccFON() {
    this.logger.log(2, "FON - Flash On");
    this.writeScreen.setPen({
      flash: true
    });
  }
  ccRDC() {
    this.logger.log(2, "RDC - Resume Direct Captioning");
    this.setMode("MODE_PAINT-ON");
  }
  ccTR() {
    this.logger.log(2, "TR");
    this.setMode("MODE_TEXT");
  }
  ccRTD() {
    this.logger.log(2, "RTD");
    this.setMode("MODE_TEXT");
  }
  ccEDM() {
    this.logger.log(2, "EDM - Erase Displayed Memory");
    this.displayedMemory.reset();
    this.outputDataUpdate(true);
  }
  ccCR() {
    this.logger.log(2, "CR - Carriage Return");
    this.writeScreen.rollUp();
    this.outputDataUpdate(true);
  }
  ccENM() {
    this.logger.log(2, "ENM - Erase Non-displayed Memory");
    this.nonDisplayedMemory.reset();
  }
  ccEOC() {
    this.logger.log(2, "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;
      this.logger.log(1, () => "DISP: " + this.displayedMemory.getDisplayText());
    }
    this.outputDataUpdate(true);
  }
  ccTO(nrCols) {
    this.logger.log(2, "TO(" + nrCols + ") - Tab Offset");
    this.writeScreen.moveCursor(nrCols);
  }
  ccMIDROW(secondByte) {
    const styles = {
      flash: 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";
    }
    this.logger.log(2, "MIDROW: " + stringify(styles));
    this.writeScreen.setPen(styles);
  }
  outputDataUpdate(dispatch = false) {
    const time = this.logger.time;
    if (time === null) {
      return;
    }
    if (this.outputFilter) {
      if (this.cueStartTime === null && !this.displayedMemory.isEmpty()) {
        this.cueStartTime = time;
      } else {
        if (!this.displayedMemory.equals(this.lastOutputScreen)) {
          this.outputFilter.newCue(this.cueStartTime, time, this.lastOutputScreen);
          if (dispatch && this.outputFilter.dispatchCue) {
            this.outputFilter.dispatchCue();
          }
          this.cueStartTime = this.displayedMemory.isEmpty() ? null : time;
        }
      }
      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;
      }
    }
  }
};
var Cea608Parser = class {
  constructor(field, out1, out2) {
    this.channels = void 0;
    this.currentChannel = 0;
    this.cmdHistory = createCmdHistory();
    this.logger = void 0;
    const logger2 = this.logger = new CaptionsLogger();
    this.channels = [null, new Cea608Channel(field, out1, logger2), new Cea608Channel(field + 1, out2, logger2)];
  }
  getHandler(channel) {
    return this.channels[channel].getHandler();
  }
  setHandler(channel, newHandler) {
    this.channels[channel].setHandler(newHandler);
  }
  addData(time, byteList) {
    this.logger.time = time;
    for (let i = 0; i < byteList.length; i += 2) {
      const a = byteList[i] & 127;
      const b = byteList[i + 1] & 127;
      let cmdFound = false;
      let charsFound = null;
      if (a === 0 && b === 0) {
        continue;
      } else {
        this.logger.log(3, () => "[" + numArrayToHexArray([byteList[i], byteList[i + 1]]) + "] -> (" + numArrayToHexArray([a, b]) + ")");
      }
      const cmdHistory = this.cmdHistory;
      const isControlCode = a >= 16 && a <= 31;
      if (isControlCode) {
        if (hasCmdRepeated(a, b, cmdHistory)) {
          setLastCmd(null, null, cmdHistory);
          this.logger.log(3, () => "Repeated command (" + numArrayToHexArray([a, b]) + ") is dropped");
          continue;
        }
        setLastCmd(a, b, this.cmdHistory);
        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);
        }
      } else {
        setLastCmd(null, null, cmdHistory);
      }
      if (!cmdFound) {
        charsFound = this.parseChars(a, b);
        if (charsFound) {
          const currChNr = this.currentChannel;
          if (currChNr && currChNr > 0) {
            const channel = this.channels[currChNr];
            channel.insertChars(charsFound);
          } else {
            this.logger.log(2, "No channel found yet. TEXT-MODE?");
          }
        }
      }
      if (!cmdFound && !charsFound) {
        this.logger.log(2, () => "Couldn't parse cleaned data " + numArrayToHexArray([a, b]) + " orig: " + numArrayToHexArray([byteList[i], byteList[i + 1]]));
      }
    }
  }
  parseCmd(a, b) {
    const cond1 = (a === 20 || a === 28 || a === 21 || a === 29) && b >= 32 && b <= 47;
    const cond2 = (a === 23 || a === 31) && b >= 33 && b <= 35;
    if (!(cond1 || cond2)) {
      return false;
    }
    const chNr = a === 20 || a === 21 || a === 23 ? 1 : 2;
    const channel = this.channels[chNr];
    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 {
      channel.ccTO(b - 32);
    }
    this.currentChannel = chNr;
    return true;
  }
  parseMidrow(a, b) {
    let chNr = 0;
    if ((a === 17 || a === 25) && b >= 32 && b <= 47) {
      if (a === 17) {
        chNr = 1;
      } else {
        chNr = 2;
      }
      if (chNr !== this.currentChannel) {
        this.logger.log(0, "Mismatch channel in midrow parsing");
        return false;
      }
      const channel = this.channels[chNr];
      if (!channel) {
        return false;
      }
      channel.ccMIDROW(b);
      this.logger.log(3, () => "MIDROW (" + numArrayToHexArray([a, b]) + ")");
      return true;
    }
    return false;
  }
  parsePAC(a, b) {
    let row;
    const case1 = (a >= 17 && a <= 23 || a >= 25 && a <= 31) && b >= 64 && b <= 127;
    const case2 = (a === 16 || a === 24) && b >= 64 && b <= 95;
    if (!(case1 || case2)) {
      return false;
    }
    const chNr = a <= 23 ? 1 : 2;
    if (b >= 64 && b <= 95) {
      row = chNr === 1 ? rowsLowCh1[a] : rowsLowCh2[a];
    } else {
      row = chNr === 1 ? rowsHighCh1[a] : rowsHighCh2[a];
    }
    const channel = this.channels[chNr];
    if (!channel) {
      return false;
    }
    channel.setPAC(this.interpretPAC(row, b));
    this.currentChannel = chNr;
    return true;
  }
  interpretPAC(row, byte) {
    let pacIndex;
    const pacData = {
      color: null,
      italics: false,
      indent: null,
      underline: false,
      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;
  }
  parseChars(a, b) {
    let channelNr;
    let charCodes = null;
    let charCode1 = null;
    if (a >= 25) {
      channelNr = 2;
      charCode1 = a - 8;
    } else {
      channelNr = 1;
      charCode1 = a;
    }
    if (charCode1 >= 17 && charCode1 <= 19) {
      let oneCode;
      if (charCode1 === 17) {
        oneCode = b + 80;
      } else if (charCode1 === 18) {
        oneCode = b + 112;
      } else {
        oneCode = b + 144;
      }
      this.logger.log(2, () => "Special char '" + getCharForByte(oneCode) + "' in channel " + channelNr);
      charCodes = [oneCode];
    } else if (a >= 32 && a <= 127) {
      charCodes = b === 0 ? [a] : [a, b];
    }
    if (charCodes) {
      this.logger.log(3, () => "Char codes =  " + numArrayToHexArray(charCodes).join(","));
    }
    return charCodes;
  }
  parseBackgroundAttributes(a, b) {
    const case1 = (a === 16 || a === 24) && b >= 32 && b <= 47;
    const case2 = (a === 23 || a === 31) && b >= 45 && b <= 47;
    if (!(case1 || case2)) {
      return false;
    }
    let index;
    const bkgData = {};
    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;
      }
    }
    const chNr = a <= 23 ? 1 : 2;
    const channel = this.channels[chNr];
    channel.setBkgData(bkgData);
    return true;
  }
  reset() {
    for (let i = 0; i < Object.keys(this.channels).length; i++) {
      const channel = this.channels[i];
      if (channel) {
        channel.reset();
      }
    }
    setLastCmd(null, null, this.cmdHistory);
  }
  cueSplitAtTime(t) {
    for (let i = 0; i < this.channels.length; i++) {
      const channel = this.channels[i];
      if (channel) {
        channel.cueSplitAtTime(t);
      }
    }
  }
};
function setLastCmd(a, b, cmdHistory) {
  cmdHistory.a = a;
  cmdHistory.b = b;
}
function hasCmdRepeated(a, b, cmdHistory) {
  return cmdHistory.a === a && cmdHistory.b === b;
}
function createCmdHistory() {
  return {
    a: null,
    b: null
  };
}
var VTTCue = function() {
  if (optionalSelf != null && optionalSelf.VTTCue) {
    return self.VTTCue;
  }
  const AllowedDirections = ["", "lr", "rl"];
  const AllowedAlignments = ["start", "middle", "end", "left", "right"];
  function isAllowedValue(allowed, value) {
    if (typeof value !== "string") {
      return false;
    }
    if (!Array.isArray(allowed)) {
      return false;
    }
    const lcValue = value.toLowerCase();
    if (~allowed.indexOf(lcValue)) {
      return lcValue;
    }
    return false;
  }
  function findDirectionSetting(value) {
    return isAllowedValue(AllowedDirections, value);
  }
  function findAlignSetting(value) {
    return isAllowedValue(AllowedAlignments, value);
  }
  function extend(obj, ...rest) {
    let i = 1;
    for (; i < arguments.length; i++) {
      const cobj = arguments[i];
      for (const p in cobj) {
        obj[p] = cobj[p];
      }
    }
    return obj;
  }
  function VTTCue2(startTime, endTime, text) {
    const cue = this;
    const baseObj = {
      enumerable: true
    };
    cue.hasBeenReset = false;
    let _id = "";
    let _pauseOnExit = false;
    let _startTime = startTime;
    let _endTime = endTime;
    let _text = text;
    let _region = null;
    let _vertical = "";
    let _snapToLines = true;
    let _line = "auto";
    let _lineAlign = "start";
    let _position = 50;
    let _positionAlign = "middle";
    let _size = 50;
    let _align = "middle";
    Object.defineProperty(cue, "id", extend({}, baseObj, {
      get: function() {
        return _id;
      },
      set: function(value) {
        _id = "" + value;
      }
    }));
    Object.defineProperty(cue, "pauseOnExit", extend({}, baseObj, {
      get: function() {
        return _pauseOnExit;
      },
      set: function(value) {
        _pauseOnExit = !!value;
      }
    }));
    Object.defineProperty(cue, "startTime", extend({}, baseObj, {
      get: function() {
        return _startTime;
      },
      set: function(value) {
        if (typeof value !== "number") {
          throw new TypeError("Start time must be set to a number.");
        }
        _startTime = value;
        this.hasBeenReset = true;
      }
    }));
    Object.defineProperty(cue, "endTime", extend({}, baseObj, {
      get: function() {
        return _endTime;
      },
      set: function(value) {
        if (typeof value !== "number") {
          throw new TypeError("End time must be set to a number.");
        }
        _endTime = value;
        this.hasBeenReset = true;
      }
    }));
    Object.defineProperty(cue, "text", extend({}, baseObj, {
      get: function() {
        return _text;
      },
      set: function(value) {
        _text = "" + value;
        this.hasBeenReset = true;
      }
    }));
    Object.defineProperty(cue, "region", extend({}, baseObj, {
      get: function() {
        return _region;
      },
      set: function(value) {
        _region = value;
        this.hasBeenReset = true;
      }
    }));
    Object.defineProperty(cue, "vertical", extend({}, baseObj, {
      get: function() {
        return _vertical;
      },
      set: function(value) {
        const setting = findDirectionSetting(value);
        if (setting === false) {
          throw new SyntaxError("An invalid or illegal string was specified.");
        }
        _vertical = setting;
        this.hasBeenReset = true;
      }
    }));
    Object.defineProperty(cue, "snapToLines", extend({}, baseObj, {
      get: function() {
        return _snapToLines;
      },
      set: function(value) {
        _snapToLines = !!value;
        this.hasBeenReset = true;
      }
    }));
    Object.defineProperty(cue, "line", extend({}, baseObj, {
      get: function() {
        return _line;
      },
      set: function(value) {
        if (typeof value !== "number" && value !== "auto") {
          throw new SyntaxError("An invalid number or illegal string was specified.");
        }
        _line = value;
        this.hasBeenReset = true;
      }
    }));
    Object.defineProperty(cue, "lineAlign", extend({}, baseObj, {
      get: function() {
        return _lineAlign;
      },
      set: function(value) {
        const setting = findAlignSetting(value);
        if (!setting) {
          throw new SyntaxError("An invalid or illegal string was specified.");
        }
        _lineAlign = setting;
        this.hasBeenReset = true;
      }
    }));
    Object.defineProperty(cue, "position", extend({}, baseObj, {
      get: function() {
        return _position;
      },
      set: function(value) {
        if (value < 0 || value > 100) {
          throw new Error("Position must be between 0 and 100.");
        }
        _position = value;
        this.hasBeenReset = true;
      }
    }));
    Object.defineProperty(cue, "positionAlign", extend({}, baseObj, {
      get: function() {
        return _positionAlign;
      },
      set: function(value) {
        const setting = findAlignSetting(value);
        if (!setting) {
          throw new SyntaxError("An invalid or illegal string was specified.");
        }
        _positionAlign = setting;
        this.hasBeenReset = true;
      }
    }));
    Object.defineProperty(cue, "size", extend({}, baseObj, {
      get: function() {
        return _size;
      },
      set: function(value) {
        if (value < 0 || value > 100) {
          throw new Error("Size must be between 0 and 100.");
        }
        _size = value;
        this.hasBeenReset = true;
      }
    }));
    Object.defineProperty(cue, "align", extend({}, baseObj, {
      get: function() {
        return _align;
      },
      set: function(value) {
        const setting = findAlignSetting(value);
        if (!setting) {
          throw new SyntaxError("An invalid or illegal string was specified.");
        }
        _align = setting;
        this.hasBeenReset = true;
      }
    }));
    cue.displayState = void 0;
  }
  VTTCue2.prototype.getCueAsHTML = function() {
    const WebVTT = self.WebVTT;
    return WebVTT.convertCueToDOMTree(self, this.text);
  };
  return VTTCue2;
}();
var StringDecoder = class {
  decode(data, options) {
    if (!data) {
      return "";
    }
    if (typeof data !== "string") {
      throw new Error("Error - expected string data.");
    }
    return decodeURIComponent(encodeURIComponent(data));
  }
};
function parseTimeStamp(input) {
  function computeSeconds(h, m2, s, f) {
    return (h | 0) * 3600 + (m2 | 0) * 60 + (s | 0) + parseFloat(f || 0);
  }
  const m = input.match(/^(?:(\d+):)?(\d{2}):(\d{2})(\.\d+)?/);
  if (!m) {
    return null;
  }
  if (parseFloat(m[2]) > 59) {
    return computeSeconds(m[2], m[3], 0, m[4]);
  }
  return computeSeconds(m[1], m[2], m[3], m[4]);
}
var Settings = class {
  constructor() {
    this.values = /* @__PURE__ */ Object.create(null);
  }
  set(k, v) {
    if (!this.get(k) && v !== "") {
      this.values[k] = v;
    }
  }
  get(k, dflt, defaultKey) {
    if (defaultKey) {
      return this.has(k) ? this.values[k] : dflt[defaultKey];
    }
    return this.has(k) ? this.values[k] : dflt;
  }
  has(k) {
    return k in this.values;
  }
  alt(k, v, a) {
    for (let n = 0; n < a.length; ++n) {
      if (v === a[n]) {
        this.set(k, v);
        break;
      }
    }
  }
  integer(k, v) {
    if (/^-?\d+$/.test(v)) {
      this.set(k, parseInt(v, 10));
    }
  }
  percent(k, v) {
    if (/^([\d]{1,3})(\.[\d]*)?%$/.test(v)) {
      const percent = parseFloat(v);
      if (percent >= 0 && percent <= 100) {
        this.set(k, percent);
        return true;
      }
    }
    return false;
  }
};
function parseOptions(input, callback, keyValueDelim, groupDelim) {
  const groups = groupDelim ? input.split(groupDelim) : [input];
  for (const i in groups) {
    if (typeof groups[i] !== "string") {
      continue;
    }
    const kv = groups[i].split(keyValueDelim);
    if (kv.length !== 2) {
      continue;
    }
    const k = kv[0];
    const v = kv[1];
    callback(k, v);
  }
}
var defaults = new VTTCue(0, 0, "");
var center = defaults.align === "middle" ? "middle" : "center";
function parseCue(input, cue, regionList) {
  const oInput = input;
  function consumeTimeStamp() {
    const ts = parseTimeStamp(input);
    if (ts === null) {
      throw new Error("Malformed timestamp: " + oInput);
    }
    input = input.replace(/^[^\sa-zA-Z-]+/, "");
    return ts;
  }
  function consumeCueSettings(input2, cue2) {
    const settings = new Settings();
    parseOptions(input2, function(k, v) {
      let vals;
      switch (k) {
        case "region":
          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(",");
          settings.integer(k, vals[0]);
          if (settings.percent(k, vals[0])) {
            settings.set("snapToLines", false);
          }
          settings.alt(k, vals[0], ["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) {
            settings.alt("positionAlign", vals[1], ["start", center, "end", "line-left", "line-right", "auto"]);
          }
          break;
        case "size":
          settings.percent(k, v);
          break;
        case "align":
          settings.alt(k, v, ["start", center, "end", "left", "right"]);
          break;
      }
    }, /:/, /\s/);
    cue2.region = settings.get("region", null);
    cue2.vertical = settings.get("vertical", "");
    let line = settings.get("line", "auto");
    if (line === "auto" && defaults.line === -1) {
      line = -1;
    }
    cue2.line = line;
    cue2.lineAlign = settings.get("lineAlign", "start");
    cue2.snapToLines = settings.get("snapToLines", true);
    cue2.size = settings.get("size", 100);
    cue2.align = settings.get("align", center);
    let position = settings.get("position", "auto");
    if (position === "auto" && defaults.position === 50) {
      position = cue2.align === "start" || cue2.align === "left" ? 0 : cue2.align === "end" || cue2.align === "right" ? 100 : 50;
    }
    cue2.position = position;
  }
  function skipWhitespace() {
    input = input.replace(/^\s+/, "");
  }
  skipWhitespace();
  cue.startTime = consumeTimeStamp();
  skipWhitespace();
  if (input.slice(0, 3) !== "-->") {
    throw new Error("Malformed time stamp (time stamps must be separated by '-->'): " + oInput);
  }
  input = input.slice(3);
  skipWhitespace();
  cue.endTime = consumeTimeStamp();
  skipWhitespace();
  consumeCueSettings(input, cue);
}
function fixLineBreaks(input) {
  return input.replace(//gi, "\n");
}
var VTTParser = class {
  constructor() {
    this.state = "INITIAL";
    this.buffer = "";
    this.decoder = new StringDecoder();
    this.regionList = [];
    this.cue = null;
    this.oncue = void 0;
    this.onparsingerror = void 0;
    this.onflush = void 0;
  }
  parse(data) {
    const _this = this;
    if (data) {
      _this.buffer += _this.decoder.decode(data, {
        stream: true
      });
    }
    function collectNextLine() {
      let buffer = _this.buffer;
      let pos = 0;
      buffer = fixLineBreaks(buffer);
      while (pos < buffer.length && buffer[pos] !== "\r" && buffer[pos] !== "\n") {
        ++pos;
      }
      const line = buffer.slice(0, pos);
      if (buffer[pos] === "\r") {
        ++pos;
      }
      if (buffer[pos] === "\n") {
        ++pos;
      }
      _this.buffer = buffer.slice(pos);
      return line;
    }
    function parseHeader2(input) {
      parseOptions(input, function(k, v) {
      }, /:/);
    }
    try {
      let line = "";
      if (_this.state === "INITIAL") {
        if (!/\r\n|\n/.test(_this.buffer)) {
          return this;
        }
        line = collectNextLine();
        const m = line.match(/^()?WEBVTT([ \t].*)?$/);
        if (!(m != null && m[0])) {
          throw new Error("Malformed WebVTT signature.");
        }
        _this.state = "HEADER";
      }
      let alreadyCollectedLine = false;
      while (_this.buffer) {
        if (!/\r\n|\n/.test(_this.buffer)) {
          return this;
        }
        if (!alreadyCollectedLine) {
          line = collectNextLine();
        } else {
          alreadyCollectedLine = false;
        }
        switch (_this.state) {
          case "HEADER":
            if (/:/.test(line)) {
              parseHeader2(line);
            } else if (!line) {
              _this.state = "ID";
            }
            continue;
          case "NOTE":
            if (!line) {
              _this.state = "ID";
            }
            continue;
          case "ID":
            if (/^NOTE($|[ \t])/.test(line)) {
              _this.state = "NOTE";
              break;
            }
            if (!line) {
              continue;
            }
            _this.cue = new VTTCue(0, 0, "");
            _this.state = "CUE";
            if (line.indexOf("-->") === -1) {
              _this.cue.id = line;
              continue;
            }
          case "CUE":
            if (!_this.cue) {
              _this.state = "BADCUE";
              continue;
            }
            try {
              parseCue(line, _this.cue, _this.regionList);
            } catch (e) {
              _this.cue = null;
              _this.state = "BADCUE";
              continue;
            }
            _this.state = "CUETEXT";
            continue;
          case "CUETEXT":
            {
              const hasSubstring = line.indexOf("-->") !== -1;
              if (!line || hasSubstring && (alreadyCollectedLine = true)) {
                if (_this.oncue && _this.cue) {
                  _this.oncue(_this.cue);
                }
                _this.cue = null;
                _this.state = "ID";
                continue;
              }
              if (_this.cue === null) {
                continue;
              }
              if (_this.cue.text) {
                _this.cue.text += "\n";
              }
              _this.cue.text += line;
            }
            continue;
          case "BADCUE":
            if (!line) {
              _this.state = "ID";
            }
        }
      }
    } catch (e) {
      if (_this.state === "CUETEXT" && _this.cue && _this.oncue) {
        _this.oncue(_this.cue);
      }
      _this.cue = null;
      _this.state = _this.state === "INITIAL" ? "BADWEBVTT" : "BADCUE";
    }
    return this;
  }
  flush() {
    const _this = this;
    try {
      if (_this.cue || _this.state === "HEADER") {
        _this.buffer += "\n\n";
        _this.parse();
      }
      if (_this.state === "INITIAL" || _this.state === "BADWEBVTT") {
        throw new Error("Malformed WebVTT signature.");
      }
    } catch (e) {
      if (_this.onparsingerror) {
        _this.onparsingerror(e);
      }
    }
    if (_this.onflush) {
      _this.onflush();
    }
    return this;
  }
};
var LINEBREAKS = /\r\n|\n\r|\n|\r/g;
var startsWith = function startsWith2(inputString, searchString, position = 0) {
  return inputString.slice(position, position + searchString.length) === searchString;
};
var cueString2millis = function cueString2millis2(timeString) {
  let ts = parseInt(timeString.slice(-3));
  const secs = parseInt(timeString.slice(-6, -4));
  const mins = parseInt(timeString.slice(-9, -7));
  const hours = timeString.length > 9 ? parseInt(timeString.substring(0, timeString.indexOf(":"))) : 0;
  if (!isFiniteNumber(ts) || !isFiniteNumber(secs) || !isFiniteNumber(mins) || !isFiniteNumber(hours)) {
    throw Error(`Malformed X-TIMESTAMP-MAP: Local:${timeString}`);
  }
  ts += 1e3 * secs;
  ts += 60 * 1e3 * mins;
  ts += 60 * 60 * 1e3 * hours;
  return ts;
};
function generateCueId(startTime, endTime, text) {
  return hash(startTime.toString()) + hash(endTime.toString()) + hash(text);
}
var calculateOffset = function calculateOffset2(vttCCs, cc, presentationTime) {
  let currCC = vttCCs[cc];
  let prevCC = vttCCs[currCC.prevCC];
  if (!prevCC || !prevCC.new && currCC.new) {
    vttCCs.ccOffset = vttCCs.presentationOffset = currCC.start;
    currCC.new = false;
    return;
  }
  while ((_prevCC = prevCC) != null && _prevCC.new) {
    var _prevCC;
    vttCCs.ccOffset += currCC.start - prevCC.start;
    currCC.new = false;
    currCC = prevCC;
    prevCC = vttCCs[currCC.prevCC];
  }
  vttCCs.presentationOffset = presentationTime;
};
function parseWebVTT(vttByteArray, initPTS, vttCCs, cc, timeOffset, callBack, errorCallBack) {
  const parser = new VTTParser();
  const vttLines = utf8ArrayToStr(new Uint8Array(vttByteArray)).trim().replace(LINEBREAKS, "\n").split("\n");
  const cues = [];
  const init90kHz = initPTS ? toMpegTsClockFromTimescale(initPTS.baseTime, initPTS.timescale) : 0;
  let cueTime = "00:00.000";
  let timestampMapMPEGTS = 0;
  let timestampMapLOCAL = 0;
  let parsingError;
  let inHeader = true;
  parser.oncue = function(cue) {
    const currCC = vttCCs[cc];
    let cueOffset = vttCCs.ccOffset;
    const webVttMpegTsMapOffset = (timestampMapMPEGTS - init90kHz) / 9e4;
    if (currCC != null && currCC.new) {
      if (timestampMapLOCAL !== void 0) {
        cueOffset = vttCCs.ccOffset = currCC.start;
      } else {
        calculateOffset(vttCCs, cc, webVttMpegTsMapOffset);
      }
    }
    if (webVttMpegTsMapOffset) {
      if (!initPTS) {
        parsingError = new Error("Missing initPTS for VTT MPEGTS");
        return;
      }
      cueOffset = webVttMpegTsMapOffset - vttCCs.presentationOffset;
    }
    const duration = cue.endTime - cue.startTime;
    const startTime = normalizePts((cue.startTime + cueOffset - timestampMapLOCAL) * 9e4, timeOffset * 9e4) / 9e4;
    cue.startTime = Math.max(startTime, 0);
    cue.endTime = Math.max(startTime + duration, 0);
    const text = cue.text.trim();
    cue.text = decodeURIComponent(encodeURIComponent(text));
    if (!cue.id) {
      cue.id = generateCueId(cue.startTime, cue.endTime, text);
    }
    if (cue.endTime > 0) {
      cues.push(cue);
    }
  };
  parser.onparsingerror = function(error) {
    parsingError = error;
  };
  parser.onflush = function() {
    if (parsingError) {
      errorCallBack(parsingError);
      return;
    }
    callBack(cues);
  };
  vttLines.forEach((line) => {
    if (inHeader) {
      if (startsWith(line, "X-TIMESTAMP-MAP=")) {
        inHeader = false;
        line.slice(16).split(",").forEach((timestamp) => {
          if (startsWith(timestamp, "LOCAL:")) {
            cueTime = timestamp.slice(6);
          } else if (startsWith(timestamp, "MPEGTS:")) {
            timestampMapMPEGTS = parseInt(timestamp.slice(7));
          }
        });
        try {
          timestampMapLOCAL = cueString2millis(cueTime) / 1e3;
        } catch (error) {
          parsingError = error;
        }
        return;
      } else if (line === "") {
        inHeader = false;
      }
    }
    parser.parse(line + "\n");
  });
  parser.flush();
}
var IMSC1_CODEC = "stpp.ttml.im1t";
var HMSF_REGEX = /^(\d{2,}):(\d{2}):(\d{2}):(\d{2})\.?(\d+)?$/;
var TIME_UNIT_REGEX = /^(\d*(?:\.\d*)?)(h|m|s|ms|f|t)$/;
var textAlignToLineAlign = {
  left: "start",
  center: "center",
  right: "end",
  start: "start",
  end: "end"
};
function parseIMSC1(payload, initPTS, callBack, errorCallBack) {
  const results = findBox(new Uint8Array(payload), ["mdat"]);
  if (results.length === 0) {
    errorCallBack(new Error("Could not parse IMSC1 mdat"));
    return;
  }
  const ttmlList = results.map((mdat) => utf8ArrayToStr(mdat));
  const syncTime = toTimescaleFromScale(initPTS.baseTime, 1, initPTS.timescale);
  try {
    ttmlList.forEach((ttml) => callBack(parseTTML(ttml, syncTime)));
  } catch (error) {
    errorCallBack(error);
  }
}
function parseTTML(ttml, syncTime) {
  const parser = new DOMParser();
  const xmlDoc = parser.parseFromString(ttml, "text/xml");
  const tt = xmlDoc.getElementsByTagName("tt")[0];
  if (!tt) {
    throw new Error("Invalid ttml");
  }
  const defaultRateInfo = {
    frameRate: 30,
    subFrameRate: 1,
    frameRateMultiplier: 0,
    tickRate: 0
  };
  const rateInfo = Object.keys(defaultRateInfo).reduce((result, key) => {
    result[key] = tt.getAttribute(`ttp:${key}`) || defaultRateInfo[key];
    return result;
  }, {});
  const trim = tt.getAttribute("xml:space") !== "preserve";
  const styleElements = collectionToDictionary(getElementCollection(tt, "styling", "style"));
  const regionElements = collectionToDictionary(getElementCollection(tt, "layout", "region"));
  const cueElements = getElementCollection(tt, "body", "[begin]");
  return [].map.call(cueElements, (cueElement) => {
    const cueText = getTextContent(cueElement, trim);
    if (!cueText || !cueElement.hasAttribute("begin")) {
      return null;
    }
    const startTime = parseTtmlTime(cueElement.getAttribute("begin"), rateInfo);
    const duration = parseTtmlTime(cueElement.getAttribute("dur"), rateInfo);
    let endTime = parseTtmlTime(cueElement.getAttribute("end"), rateInfo);
    if (startTime === null) {
      throw timestampParsingError(cueElement);
    }
    if (endTime === null) {
      if (duration === null) {
        throw timestampParsingError(cueElement);
      }
      endTime = startTime + duration;
    }
    const cue = new VTTCue(startTime - syncTime, endTime - syncTime, cueText);
    cue.id = generateCueId(cue.startTime, cue.endTime, cue.text);
    const region = regionElements[cueElement.getAttribute("region")];
    const style = styleElements[cueElement.getAttribute("style")];
    const styles = getTtmlStyles(region, style, styleElements);
    const {
      textAlign
    } = styles;
    if (textAlign) {
      const lineAlign = textAlignToLineAlign[textAlign];
      if (lineAlign) {
        cue.lineAlign = lineAlign;
      }
      cue.align = textAlign;
    }
    _extends(cue, styles);
    return cue;
  }).filter((cue) => cue !== null);
}
function getElementCollection(fromElement, parentName, childName) {
  const parent = fromElement.getElementsByTagName(parentName)[0];
  if (parent) {
    return [].slice.call(parent.querySelectorAll(childName));
  }
  return [];
}
function collectionToDictionary(elementsWithId) {
  return elementsWithId.reduce((dict, element) => {
    const id = element.getAttribute("xml:id");
    if (id) {
      dict[id] = element;
    }
    return dict;
  }, {});
}
function getTextContent(element, trim) {
  return [].slice.call(element.childNodes).reduce((str, node, i) => {
    var _node$childNodes;
    if (node.nodeName === "br" && i) {
      return str + "\n";
    }
    if ((_node$childNodes = node.childNodes) != null && _node$childNodes.length) {
      return getTextContent(node, trim);
    } else if (trim) {
      return str + node.textContent.trim().replace(/\s+/g, " ");
    }
    return str + node.textContent;
  }, "");
}
function getTtmlStyles(region, style, styleElements) {
  const ttsNs = "http://www.w3.org/ns/ttml#styling";
  let regionStyle = null;
  const styleAttributes = [
    "displayAlign",
    "textAlign",
    "color",
    "backgroundColor",
    "fontSize",
    "fontFamily"
  ];
  const regionStyleName = region != null && region.hasAttribute("style") ? region.getAttribute("style") : null;
  if (regionStyleName && styleElements.hasOwnProperty(regionStyleName)) {
    regionStyle = styleElements[regionStyleName];
  }
  return styleAttributes.reduce((styles, name) => {
    const value = getAttributeNS(style, ttsNs, name) || getAttributeNS(region, ttsNs, name) || getAttributeNS(regionStyle, ttsNs, name);
    if (value) {
      styles[name] = value;
    }
    return styles;
  }, {});
}
function getAttributeNS(element, ns, name) {
  if (!element) {
    return null;
  }
  return element.hasAttributeNS(ns, name) ? element.getAttributeNS(ns, name) : null;
}
function timestampParsingError(node) {
  return new Error(`Could not parse ttml timestamp ${node}`);
}
function parseTtmlTime(timeAttributeValue, rateInfo) {
  if (!timeAttributeValue) {
    return null;
  }
  let seconds = parseTimeStamp(timeAttributeValue);
  if (seconds === null) {
    if (HMSF_REGEX.test(timeAttributeValue)) {
      seconds = parseHoursMinutesSecondsFrames(timeAttributeValue, rateInfo);
    } else if (TIME_UNIT_REGEX.test(timeAttributeValue)) {
      seconds = parseTimeUnits(timeAttributeValue, rateInfo);
    }
  }
  return seconds;
}
function parseHoursMinutesSecondsFrames(timeAttributeValue, rateInfo) {
  const m = HMSF_REGEX.exec(timeAttributeValue);
  const frames = (m[4] | 0) + (m[5] | 0) / rateInfo.subFrameRate;
  return (m[1] | 0) * 3600 + (m[2] | 0) * 60 + (m[3] | 0) + frames / rateInfo.frameRate;
}
function parseTimeUnits(timeAttributeValue, rateInfo) {
  const m = TIME_UNIT_REGEX.exec(timeAttributeValue);
  const value = Number(m[1]);
  const unit = m[2];
  switch (unit) {
    case "h":
      return value * 3600;
    case "m":
      return value * 60;
    case "ms":
      return value * 1e3;
    case "f":
      return value / rateInfo.frameRate;
    case "t":
      return value / rateInfo.tickRate;
  }
  return value;
}
var OutputFilter = class {
  constructor(timelineController, trackName) {
    this.timelineController = void 0;
    this.cueRanges = [];
    this.trackName = void 0;
    this.startTime = null;
    this.endTime = null;
    this.screen = null;
    this.timelineController = timelineController;
    this.trackName = trackName;
  }
  dispatchCue() {
    if (this.startTime === null) {
      return;
    }
    this.timelineController.addCues(this.trackName, this.startTime, this.endTime, this.screen, this.cueRanges);
    this.startTime = null;
  }
  newCue(startTime, endTime, screen) {
    if (this.startTime === null || this.startTime > startTime) {
      this.startTime = startTime;
    }
    this.endTime = endTime;
    this.screen = screen;
    this.timelineController.createCaptionsTrack(this.trackName);
  }
  reset() {
    this.cueRanges = [];
    this.startTime = null;
  }
};
var TimelineController = class {
  constructor(hls) {
    this.hls = void 0;
    this.media = null;
    this.config = void 0;
    this.enabled = true;
    this.Cues = void 0;
    this.textTracks = [];
    this.tracks = [];
    this.initPTS = [];
    this.unparsedVttFrags = [];
    this.captionsTracks = {};
    this.nonNativeCaptionsTracks = {};
    this.cea608Parser1 = void 0;
    this.cea608Parser2 = void 0;
    this.lastCc = -1;
    this.lastSn = -1;
    this.lastPartIndex = -1;
    this.prevCC = -1;
    this.vttCCs = newVTTCCs();
    this.captionsProperties = void 0;
    this.hls = hls;
    this.config = hls.config;
    this.Cues = hls.config.cueHandler;
    this.captionsProperties = {
      textTrack1: {
        label: this.config.captionsTextTrack1Label,
        languageCode: this.config.captionsTextTrack1LanguageCode
      },
      textTrack2: {
        label: this.config.captionsTextTrack2Label,
        languageCode: this.config.captionsTextTrack2LanguageCode
      },
      textTrack3: {
        label: this.config.captionsTextTrack3Label,
        languageCode: this.config.captionsTextTrack3LanguageCode
      },
      textTrack4: {
        label: this.config.captionsTextTrack4Label,
        languageCode: this.config.captionsTextTrack4LanguageCode
      }
    };
    hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);
    hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
    hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
    hls.on(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);
    hls.on(Events.FRAG_LOADING, this.onFragLoading, this);
    hls.on(Events.FRAG_LOADED, this.onFragLoaded, this);
    hls.on(Events.FRAG_PARSING_USERDATA, this.onFragParsingUserdata, this);
    hls.on(Events.FRAG_DECRYPTED, this.onFragDecrypted, this);
    hls.on(Events.INIT_PTS_FOUND, this.onInitPtsFound, this);
    hls.on(Events.SUBTITLE_TRACKS_CLEARED, this.onSubtitleTracksCleared, this);
    hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
  }
  destroy() {
    const {
      hls
    } = this;
    hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);
    hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
    hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
    hls.off(Events.SUBTITLE_TRACKS_UPDATED, this.onSubtitleTracksUpdated, this);
    hls.off(Events.FRAG_LOADING, this.onFragLoading, this);
    hls.off(Events.FRAG_LOADED, this.onFragLoaded, this);
    hls.off(Events.FRAG_PARSING_USERDATA, this.onFragParsingUserdata, this);
    hls.off(Events.FRAG_DECRYPTED, this.onFragDecrypted, this);
    hls.off(Events.INIT_PTS_FOUND, this.onInitPtsFound, this);
    hls.off(Events.SUBTITLE_TRACKS_CLEARED, this.onSubtitleTracksCleared, this);
    hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
    this.hls = this.config = this.media = null;
    this.cea608Parser1 = this.cea608Parser2 = void 0;
  }
  initCea608Parsers() {
    const channel1 = new OutputFilter(this, "textTrack1");
    const channel2 = new OutputFilter(this, "textTrack2");
    const channel3 = new OutputFilter(this, "textTrack3");
    const channel4 = new OutputFilter(this, "textTrack4");
    this.cea608Parser1 = new Cea608Parser(1, channel1, channel2);
    this.cea608Parser2 = new Cea608Parser(3, channel3, channel4);
  }
  addCues(trackName, startTime, endTime, screen, cueRanges) {
    let merged = false;
    for (let i = cueRanges.length; i--; ) {
      const cueRange = cueRanges[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) {
      cueRanges.push([startTime, endTime]);
    }
    if (this.config.renderTextTracksNatively) {
      const track = this.captionsTracks[trackName];
      this.Cues.newCue(track, startTime, endTime, screen);
    } else {
      const cues = this.Cues.newCue(null, startTime, endTime, screen);
      this.hls.trigger(Events.CUES_PARSED, {
        type: "captions",
        cues,
        track: trackName
      });
    }
  }
  onInitPtsFound(event, {
    frag,
    id,
    initPTS,
    timescale
  }) {
    const {
      unparsedVttFrags
    } = this;
    if (id === PlaylistLevelType.MAIN) {
      this.initPTS[frag.cc] = {
        baseTime: initPTS,
        timescale
      };
    }
    if (unparsedVttFrags.length) {
      this.unparsedVttFrags = [];
      unparsedVttFrags.forEach((data) => {
        if (this.initPTS[data.frag.cc]) {
          this.onFragLoaded(Events.FRAG_LOADED, data);
        } else {
          this.hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
            success: false,
            frag: data.frag,
            error: new Error("Subtitle discontinuity domain does not match main")
          });
        }
      });
    }
  }
  getExistingTrack(label, language) {
    const {
      media
    } = this;
    if (media) {
      for (let i = 0; i < media.textTracks.length; i++) {
        const textTrack = media.textTracks[i];
        if (canReuseVttTextTrack(textTrack, {
          name: label,
          lang: language,
          characteristics: "transcribes-spoken-dialog,describes-music-and-sound"
        })) {
          return textTrack;
        }
      }
    }
    return null;
  }
  createCaptionsTrack(trackName) {
    if (this.config.renderTextTracksNatively) {
      this.createNativeTrack(trackName);
    } else {
      this.createNonNativeTrack(trackName);
    }
  }
  createNativeTrack(trackName) {
    if (this.captionsTracks[trackName]) {
      return;
    }
    const {
      captionsProperties,
      captionsTracks,
      media
    } = this;
    const {
      label,
      languageCode
    } = captionsProperties[trackName];
    const existingTrack = this.getExistingTrack(label, languageCode);
    if (!existingTrack) {
      const textTrack = this.createTextTrack("captions", label, languageCode);
      if (textTrack) {
        textTrack[trackName] = true;
        captionsTracks[trackName] = textTrack;
      }
    } else {
      captionsTracks[trackName] = existingTrack;
      clearCurrentCues(captionsTracks[trackName]);
      sendAddTrackEvent(captionsTracks[trackName], media);
    }
  }
  createNonNativeTrack(trackName) {
    if (this.nonNativeCaptionsTracks[trackName]) {
      return;
    }
    const trackProperties = this.captionsProperties[trackName];
    if (!trackProperties) {
      return;
    }
    const label = trackProperties.label;
    const track = {
      _id: trackName,
      label,
      kind: "captions",
      default: trackProperties.media ? !!trackProperties.media.default : false,
      closedCaptions: trackProperties.media
    };
    this.nonNativeCaptionsTracks[trackName] = track;
    this.hls.trigger(Events.NON_NATIVE_TEXT_TRACKS_FOUND, {
      tracks: [track]
    });
  }
  createTextTrack(kind, label, lang) {
    const media = this.media;
    if (!media) {
      return;
    }
    return media.addTextTrack(kind, label, lang);
  }
  onMediaAttaching(event, data) {
    this.media = data.media;
    if (!data.mediaSource) {
      this._cleanTracks();
    }
  }
  onMediaDetaching(event, data) {
    const transferringMedia = !!data.transferMedia;
    this.media = null;
    if (transferringMedia) {
      return;
    }
    const {
      captionsTracks
    } = this;
    Object.keys(captionsTracks).forEach((trackName) => {
      clearCurrentCues(captionsTracks[trackName]);
      delete captionsTracks[trackName];
    });
    this.nonNativeCaptionsTracks = {};
  }
  onManifestLoading() {
    this.lastCc = -1;
    this.lastSn = -1;
    this.lastPartIndex = -1;
    this.prevCC = -1;
    this.vttCCs = newVTTCCs();
    this._cleanTracks();
    this.tracks = [];
    this.captionsTracks = {};
    this.nonNativeCaptionsTracks = {};
    this.textTracks = [];
    this.unparsedVttFrags = [];
    this.initPTS = [];
    if (this.cea608Parser1 && this.cea608Parser2) {
      this.cea608Parser1.reset();
      this.cea608Parser2.reset();
    }
  }
  _cleanTracks() {
    const {
      media
    } = this;
    if (!media) {
      return;
    }
    const textTracks = media.textTracks;
    if (textTracks) {
      for (let i = 0; i < textTracks.length; i++) {
        clearCurrentCues(textTracks[i]);
      }
    }
  }
  onSubtitleTracksUpdated(event, data) {
    const tracks = data.subtitleTracks || [];
    const hasIMSC1 = tracks.some((track) => track.textCodec === IMSC1_CODEC);
    if (this.config.enableWebVTT || hasIMSC1 && this.config.enableIMSC1) {
      const listIsIdentical = subtitleOptionsIdentical(this.tracks, tracks);
      if (listIsIdentical) {
        this.tracks = tracks;
        return;
      }
      this.textTracks = [];
      this.tracks = tracks;
      if (this.config.renderTextTracksNatively) {
        const media = this.media;
        const inUseTracks = media ? filterSubtitleTracks(media.textTracks) : null;
        this.tracks.forEach((track, index) => {
          let textTrack;
          if (inUseTracks) {
            let inUseTrack = null;
            for (let i = 0; i < inUseTracks.length; i++) {
              if (inUseTracks[i] && canReuseVttTextTrack(inUseTracks[i], track)) {
                inUseTrack = inUseTracks[i];
                inUseTracks[i] = null;
                break;
              }
            }
            if (inUseTrack) {
              textTrack = inUseTrack;
            }
          }
          if (textTrack) {
            clearCurrentCues(textTrack);
          } else {
            const textTrackKind = captionsOrSubtitlesFromCharacteristics(track);
            textTrack = this.createTextTrack(textTrackKind, track.name, track.lang);
            if (textTrack) {
              textTrack.mode = "disabled";
            }
          }
          if (textTrack) {
            this.textTracks.push(textTrack);
          }
        });
        if (inUseTracks != null && inUseTracks.length) {
          const unusedTextTracks = inUseTracks.filter((t) => t !== null).map((t) => t.label);
          if (unusedTextTracks.length) {
            this.hls.logger.warn(`Media element contains unused subtitle tracks: ${unusedTextTracks.join(", ")}. Replace media element for each source to clear TextTracks and captions menu.`);
          }
        }
      } else if (this.tracks.length) {
        const tracksList = this.tracks.map((track) => {
          return {
            label: track.name,
            kind: track.type.toLowerCase(),
            default: track.default,
            subtitleTrack: track
          };
        });
        this.hls.trigger(Events.NON_NATIVE_TEXT_TRACKS_FOUND, {
          tracks: tracksList
        });
      }
    }
  }
  onManifestLoaded(event, data) {
    if (this.config.enableCEA708Captions && data.captions) {
      data.captions.forEach((captionsTrack) => {
        const instreamIdMatch = /(?:CC|SERVICE)([1-4])/.exec(captionsTrack.instreamId);
        if (!instreamIdMatch) {
          return;
        }
        const trackName = `textTrack${instreamIdMatch[1]}`;
        const trackProperties = this.captionsProperties[trackName];
        if (!trackProperties) {
          return;
        }
        trackProperties.label = captionsTrack.name;
        if (captionsTrack.lang) {
          trackProperties.languageCode = captionsTrack.lang;
        }
        trackProperties.media = captionsTrack;
      });
    }
  }
  closedCaptionsForLevel(frag) {
    const level = this.hls.levels[frag.level];
    return level == null ? void 0 : level.attrs["CLOSED-CAPTIONS"];
  }
  onFragLoading(event, data) {
    if (this.enabled && data.frag.type === PlaylistLevelType.MAIN) {
      var _data$part$index, _data$part;
      const {
        cea608Parser1,
        cea608Parser2,
        lastSn
      } = this;
      const {
        cc,
        sn
      } = data.frag;
      const partIndex = (_data$part$index = (_data$part = data.part) == null ? void 0 : _data$part.index) != null ? _data$part$index : -1;
      if (cea608Parser1 && cea608Parser2) {
        if (sn !== lastSn + 1 || sn === lastSn && partIndex !== this.lastPartIndex + 1 || cc !== this.lastCc) {
          cea608Parser1.reset();
          cea608Parser2.reset();
        }
      }
      this.lastCc = cc;
      this.lastSn = sn;
      this.lastPartIndex = partIndex;
    }
  }
  onFragLoaded(event, data) {
    const {
      frag,
      payload
    } = data;
    if (frag.type === PlaylistLevelType.SUBTITLE) {
      if (payload.byteLength) {
        const decryptData = frag.decryptdata;
        const decrypted = "stats" in data;
        if (decryptData == null || !decryptData.encrypted || decrypted) {
          const trackPlaylistMedia = this.tracks[frag.level];
          const vttCCs = this.vttCCs;
          if (!vttCCs[frag.cc]) {
            vttCCs[frag.cc] = {
              start: frag.start,
              prevCC: this.prevCC,
              new: true
            };
            this.prevCC = frag.cc;
          }
          if (trackPlaylistMedia && trackPlaylistMedia.textCodec === IMSC1_CODEC) {
            this._parseIMSC1(frag, payload);
          } else {
            this._parseVTTs(data);
          }
        }
      } else {
        this.hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
          success: false,
          frag,
          error: new Error("Empty subtitle payload")
        });
      }
    }
  }
  _parseIMSC1(frag, payload) {
    const hls = this.hls;
    parseIMSC1(payload, this.initPTS[frag.cc], (cues) => {
      this._appendCues(cues, frag.level);
      hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
        success: true,
        frag
      });
    }, (error) => {
      hls.logger.log(`Failed to parse IMSC1: ${error}`);
      hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
        success: false,
        frag,
        error
      });
    });
  }
  _parseVTTs(data) {
    var _frag$initSegment;
    const {
      frag,
      payload
    } = data;
    const {
      initPTS,
      unparsedVttFrags
    } = this;
    const maxAvCC = initPTS.length - 1;
    if (!initPTS[frag.cc] && maxAvCC === -1) {
      unparsedVttFrags.push(data);
      return;
    }
    const hls = this.hls;
    const payloadWebVTT = (_frag$initSegment = frag.initSegment) != null && _frag$initSegment.data ? appendUint8Array(frag.initSegment.data, new Uint8Array(payload)).buffer : payload;
    parseWebVTT(payloadWebVTT, this.initPTS[frag.cc], this.vttCCs, frag.cc, frag.start, (cues) => {
      this._appendCues(cues, frag.level);
      hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
        success: true,
        frag
      });
    }, (error) => {
      const missingInitPTS = error.message === "Missing initPTS for VTT MPEGTS";
      if (missingInitPTS) {
        unparsedVttFrags.push(data);
      } else {
        this._fallbackToIMSC1(frag, payload);
      }
      hls.logger.log(`Failed to parse VTT cue: ${error}`);
      if (missingInitPTS && maxAvCC > frag.cc) {
        return;
      }
      hls.trigger(Events.SUBTITLE_FRAG_PROCESSED, {
        success: false,
        frag,
        error
      });
    });
  }
  _fallbackToIMSC1(frag, payload) {
    const trackPlaylistMedia = this.tracks[frag.level];
    if (!trackPlaylistMedia.textCodec) {
      parseIMSC1(payload, this.initPTS[frag.cc], () => {
        trackPlaylistMedia.textCodec = IMSC1_CODEC;
        this._parseIMSC1(frag, payload);
      }, () => {
        trackPlaylistMedia.textCodec = "wvtt";
      });
    }
  }
  _appendCues(cues, fragLevel) {
    const hls = this.hls;
    if (this.config.renderTextTracksNatively) {
      const textTrack = this.textTracks[fragLevel];
      if (!textTrack || textTrack.mode === "disabled") {
        return;
      }
      cues.forEach((cue) => addCueToTrack(textTrack, cue));
    } else {
      const currentTrack = this.tracks[fragLevel];
      if (!currentTrack) {
        return;
      }
      const track = currentTrack.default ? "default" : "subtitles" + fragLevel;
      hls.trigger(Events.CUES_PARSED, {
        type: "subtitles",
        cues,
        track
      });
    }
  }
  onFragDecrypted(event, data) {
    const {
      frag
    } = data;
    if (frag.type === PlaylistLevelType.SUBTITLE) {
      this.onFragLoaded(Events.FRAG_LOADED, data);
    }
  }
  onSubtitleTracksCleared() {
    this.tracks = [];
    this.captionsTracks = {};
  }
  onFragParsingUserdata(event, data) {
    if (!this.enabled || !this.config.enableCEA708Captions) {
      return;
    }
    const {
      frag,
      samples
    } = data;
    if (frag.type === PlaylistLevelType.MAIN && this.closedCaptionsForLevel(frag) === "NONE") {
      return;
    }
    for (let i = 0; i < samples.length; i++) {
      const ccBytes = samples[i].bytes;
      if (ccBytes) {
        if (!this.cea608Parser1) {
          this.initCea608Parsers();
        }
        const ccdatas = this.extractCea608Data(ccBytes);
        this.cea608Parser1.addData(samples[i].pts, ccdatas[0]);
        this.cea608Parser2.addData(samples[i].pts, ccdatas[1]);
      }
    }
  }
  onBufferFlushing(event, {
    startOffset,
    endOffset,
    endOffsetSubtitles,
    type
  }) {
    const {
      media
    } = this;
    if (!media || media.currentTime < endOffset) {
      return;
    }
    if (!type || type === "video") {
      const {
        captionsTracks
      } = this;
      Object.keys(captionsTracks).forEach((trackName) => removeCuesInRange(captionsTracks[trackName], startOffset, endOffset));
    }
    if (this.config.renderTextTracksNatively) {
      if (startOffset === 0 && endOffsetSubtitles !== void 0) {
        const {
          textTracks
        } = this;
        Object.keys(textTracks).forEach((trackName) => removeCuesInRange(textTracks[trackName], startOffset, endOffsetSubtitles));
      }
    }
  }
  extractCea608Data(byteArray) {
    const actualCCBytes = [[], []];
    const count = byteArray[0] & 31;
    let position = 2;
    for (let j = 0; j < count; j++) {
      const tmpByte = byteArray[position++];
      const ccbyte1 = 127 & byteArray[position++];
      const ccbyte2 = 127 & byteArray[position++];
      if (ccbyte1 === 0 && ccbyte2 === 0) {
        continue;
      }
      const ccValid = (4 & tmpByte) !== 0;
      if (ccValid) {
        const ccType = 3 & tmpByte;
        if (ccType === 0 || ccType === 1) {
          actualCCBytes[ccType].push(ccbyte1);
          actualCCBytes[ccType].push(ccbyte2);
        }
      }
    }
    return actualCCBytes;
  }
};
function captionsOrSubtitlesFromCharacteristics(track) {
  if (track.characteristics) {
    if (/transcribes-spoken-dialog/gi.test(track.characteristics) && /describes-music-and-sound/gi.test(track.characteristics)) {
      return "captions";
    }
  }
  return "subtitles";
}
function canReuseVttTextTrack(inUseTrack, manifestTrack) {
  return !!inUseTrack && inUseTrack.kind === captionsOrSubtitlesFromCharacteristics(manifestTrack) && subtitleTrackMatchesTextTrack(manifestTrack, inUseTrack);
}
function intersection(x1, x2, y1, y2) {
  return Math.min(x2, y2) - Math.max(x1, y1);
}
function newVTTCCs() {
  return {
    ccOffset: 0,
    presentationOffset: 0,
    0: {
      start: 0,
      prevCC: -1,
      new: true
    }
  };
}
var WHITESPACE_CHAR = /\s/;
var Cues = {
  newCue(track, startTime, endTime, captionScreen) {
    const result = [];
    let row;
    let cue;
    let indenting;
    let indent;
    let text;
    const Cue = self.VTTCue || self.TextTrackCue;
    for (let r = 0; r < captionScreen.rows.length; r++) {
      row = captionScreen.rows[r];
      indenting = true;
      indent = 0;
      text = "";
      if (!row.isEmpty()) {
        var _track$cues;
        for (let c = 0; c < row.chars.length; c++) {
          if (WHITESPACE_CHAR.test(row.chars[c].uchar) && indenting) {
            indent++;
          } else {
            text += row.chars[c].uchar;
            indenting = false;
          }
        }
        row.cueStartTime = startTime;
        if (startTime === endTime) {
          endTime += 1e-4;
        }
        if (indent >= 16) {
          indent--;
        } else {
          indent++;
        }
        const cueText = fixLineBreaks(text.trim());
        const id = generateCueId(startTime, endTime, cueText);
        if (!(track != null && (_track$cues = track.cues) != null && _track$cues.getCueById(id))) {
          cue = new Cue(startTime, endTime, cueText);
          cue.id = id;
          cue.line = r + 1;
          cue.align = "left";
          cue.position = 10 + Math.min(80, Math.floor(indent * 8 / 32) * 10);
          result.push(cue);
        }
      }
    }
    if (track && result.length) {
      result.sort((cueA, cueB) => {
        if (cueA.line === "auto" || cueB.line === "auto") {
          return 0;
        }
        if (cueA.line > 8 && cueB.line > 8) {
          return cueB.line - cueA.line;
        }
        return cueA.line - cueB.line;
      });
      result.forEach((cue2) => addCueToTrack(track, cue2));
    }
    return result;
  }
};
function fetchSupported() {
  if (self.fetch && self.AbortController && self.ReadableStream && self.Request) {
    try {
      new self.ReadableStream({});
      return true;
    } catch (e) {
    }
  }
  return false;
}
var BYTERANGE = /(\d+)-(\d+)\/(\d+)/;
var FetchLoader = class {
  constructor(config) {
    this.fetchSetup = void 0;
    this.requestTimeout = void 0;
    this.request = null;
    this.response = null;
    this.controller = void 0;
    this.context = null;
    this.config = null;
    this.callbacks = null;
    this.stats = void 0;
    this.loader = null;
    this.fetchSetup = config.fetchSetup || getRequest;
    this.controller = new self.AbortController();
    this.stats = new LoadStats();
  }
  destroy() {
    this.loader = this.callbacks = this.context = this.config = this.request = null;
    this.abortInternal();
    this.response = null;
    this.fetchSetup = this.controller = this.stats = null;
  }
  abortInternal() {
    if (this.controller && !this.stats.loading.end) {
      this.stats.aborted = true;
      this.controller.abort();
    }
  }
  abort() {
    var _this$callbacks;
    this.abortInternal();
    if ((_this$callbacks = this.callbacks) != null && _this$callbacks.onAbort) {
      this.callbacks.onAbort(this.stats, this.context, this.response);
    }
  }
  load(context, config, callbacks) {
    const stats = this.stats;
    if (stats.loading.start) {
      throw new Error("Loader can only be used once.");
    }
    stats.loading.start = self.performance.now();
    const initParams = getRequestParameters(context, this.controller.signal);
    const isArrayBuffer = context.responseType === "arraybuffer";
    const LENGTH = isArrayBuffer ? "byteLength" : "length";
    const {
      maxTimeToFirstByteMs,
      maxLoadTimeMs
    } = config.loadPolicy;
    this.context = context;
    this.config = config;
    this.callbacks = callbacks;
    this.request = this.fetchSetup(context, initParams);
    self.clearTimeout(this.requestTimeout);
    config.timeout = maxTimeToFirstByteMs && isFiniteNumber(maxTimeToFirstByteMs) ? maxTimeToFirstByteMs : maxLoadTimeMs;
    this.requestTimeout = self.setTimeout(() => {
      if (this.callbacks) {
        this.abortInternal();
        this.callbacks.onTimeout(stats, context, this.response);
      }
    }, config.timeout);
    const fetchPromise = isPromise(this.request) ? this.request.then(self.fetch) : self.fetch(this.request);
    fetchPromise.then((response) => {
      var _this$callbacks2;
      this.response = this.loader = response;
      const first = Math.max(self.performance.now(), stats.loading.start);
      self.clearTimeout(this.requestTimeout);
      config.timeout = maxLoadTimeMs;
      this.requestTimeout = self.setTimeout(() => {
        if (this.callbacks) {
          this.abortInternal();
          this.callbacks.onTimeout(stats, context, this.response);
        }
      }, maxLoadTimeMs - (first - stats.loading.start));
      if (!response.ok) {
        const {
          status: status2,
          statusText
        } = response;
        throw new FetchError(statusText || "fetch, bad network response", status2, response);
      }
      stats.loading.first = first;
      stats.total = getContentLength(response.headers) || stats.total;
      const onProgress = (_this$callbacks2 = this.callbacks) == null ? void 0 : _this$callbacks2.onProgress;
      if (onProgress && isFiniteNumber(config.highWaterMark)) {
        return this.loadProgressively(response, stats, context, config.highWaterMark, onProgress);
      }
      if (isArrayBuffer) {
        return response.arrayBuffer();
      }
      if (context.responseType === "json") {
        return response.json();
      }
      return response.text();
    }).then((responseData) => {
      var _this$callbacks3, _this$callbacks4;
      const response = this.response;
      if (!response) {
        throw new Error("loader destroyed");
      }
      self.clearTimeout(this.requestTimeout);
      stats.loading.end = Math.max(self.performance.now(), stats.loading.first);
      const total = responseData[LENGTH];
      if (total) {
        stats.loaded = stats.total = total;
      }
      const loaderResponse = {
        url: response.url,
        data: responseData,
        code: response.status
      };
      const onProgress = (_this$callbacks3 = this.callbacks) == null ? void 0 : _this$callbacks3.onProgress;
      if (onProgress && !isFiniteNumber(config.highWaterMark)) {
        onProgress(stats, context, responseData, response);
      }
      (_this$callbacks4 = this.callbacks) == null ? void 0 : _this$callbacks4.onSuccess(loaderResponse, stats, context, response);
    }).catch((error) => {
      var _this$callbacks5;
      self.clearTimeout(this.requestTimeout);
      if (stats.aborted) {
        return;
      }
      const code = !error ? 0 : error.code || 0;
      const text = !error ? null : error.message;
      (_this$callbacks5 = this.callbacks) == null ? void 0 : _this$callbacks5.onError({
        code,
        text
      }, context, error ? error.details : null, stats);
    });
  }
  getCacheAge() {
    let result = null;
    if (this.response) {
      const ageHeader = this.response.headers.get("age");
      result = ageHeader ? parseFloat(ageHeader) : null;
    }
    return result;
  }
  getResponseHeader(name) {
    return this.response ? this.response.headers.get(name) : null;
  }
  loadProgressively(response, stats, context, highWaterMark = 0, onProgress) {
    const chunkCache = new ChunkCache();
    const reader = response.body.getReader();
    const pump = () => {
      return reader.read().then((data) => {
        if (data.done) {
          if (chunkCache.dataLength) {
            onProgress(stats, context, chunkCache.flush().buffer, response);
          }
          return Promise.resolve(new ArrayBuffer(0));
        }
        const chunk = data.value;
        const len = chunk.length;
        stats.loaded += len;
        if (len < highWaterMark || chunkCache.dataLength) {
          chunkCache.push(chunk);
          if (chunkCache.dataLength >= highWaterMark) {
            onProgress(stats, context, chunkCache.flush().buffer, response);
          }
        } else {
          onProgress(stats, context, chunk.buffer, response);
        }
        return pump();
      }).catch(() => {
        return Promise.reject();
      });
    };
    return pump();
  }
};
function getRequestParameters(context, signal) {
  const initParams = {
    method: "GET",
    mode: "cors",
    credentials: "same-origin",
    signal,
    headers: new self.Headers(_extends({}, context.headers))
  };
  if (context.rangeEnd) {
    initParams.headers.set("Range", "bytes=" + context.rangeStart + "-" + String(context.rangeEnd - 1));
  }
  return initParams;
}
function getByteRangeLength(byteRangeHeader) {
  const result = BYTERANGE.exec(byteRangeHeader);
  if (result) {
    return parseInt(result[2]) - parseInt(result[1]) + 1;
  }
}
function getContentLength(headers) {
  const contentRange = headers.get("Content-Range");
  if (contentRange) {
    const byteRangeLength = getByteRangeLength(contentRange);
    if (isFiniteNumber(byteRangeLength)) {
      return byteRangeLength;
    }
  }
  const contentLength = headers.get("Content-Length");
  if (contentLength) {
    return parseInt(contentLength);
  }
}
function getRequest(context, initParams) {
  return new self.Request(context.url, initParams);
}
var FetchError = class extends Error {
  constructor(message, code, details) {
    super(message);
    this.code = void 0;
    this.details = void 0;
    this.code = code;
    this.details = details;
  }
};
var AGE_HEADER_LINE_REGEX = /^age:\s*[\d.]+\s*$/im;
var XhrLoader = class {
  constructor(config) {
    this.xhrSetup = void 0;
    this.requestTimeout = void 0;
    this.retryTimeout = void 0;
    this.retryDelay = void 0;
    this.config = null;
    this.callbacks = null;
    this.context = null;
    this.loader = null;
    this.stats = void 0;
    this.xhrSetup = config ? config.xhrSetup || null : null;
    this.stats = new LoadStats();
    this.retryDelay = 0;
  }
  destroy() {
    this.callbacks = null;
    this.abortInternal();
    this.loader = null;
    this.config = null;
    this.context = null;
    this.xhrSetup = null;
  }
  abortInternal() {
    const loader = this.loader;
    self.clearTimeout(this.requestTimeout);
    self.clearTimeout(this.retryTimeout);
    if (loader) {
      loader.onreadystatechange = null;
      loader.onprogress = null;
      if (loader.readyState !== 4) {
        this.stats.aborted = true;
        loader.abort();
      }
    }
  }
  abort() {
    var _this$callbacks;
    this.abortInternal();
    if ((_this$callbacks = this.callbacks) != null && _this$callbacks.onAbort) {
      this.callbacks.onAbort(this.stats, this.context, this.loader);
    }
  }
  load(context, config, callbacks) {
    if (this.stats.loading.start) {
      throw new Error("Loader can only be used once.");
    }
    this.stats.loading.start = self.performance.now();
    this.context = context;
    this.config = config;
    this.callbacks = callbacks;
    this.loadInternal();
  }
  loadInternal() {
    const {
      config,
      context
    } = this;
    if (!config || !context) {
      return;
    }
    const xhr = this.loader = new self.XMLHttpRequest();
    const stats = this.stats;
    stats.loading.first = 0;
    stats.loaded = 0;
    stats.aborted = false;
    const xhrSetup = this.xhrSetup;
    if (xhrSetup) {
      Promise.resolve().then(() => {
        if (this.loader !== xhr || this.stats.aborted)
          return;
        return xhrSetup(xhr, context.url);
      }).catch((error) => {
        if (this.loader !== xhr || this.stats.aborted)
          return;
        xhr.open("GET", context.url, true);
        return xhrSetup(xhr, context.url);
      }).then(() => {
        if (this.loader !== xhr || this.stats.aborted)
          return;
        this.openAndSendXhr(xhr, context, config);
      }).catch((error) => {
        var _this$callbacks2;
        (_this$callbacks2 = this.callbacks) == null ? void 0 : _this$callbacks2.onError({
          code: xhr.status,
          text: error.message
        }, context, xhr, stats);
        return;
      });
    } else {
      this.openAndSendXhr(xhr, context, config);
    }
  }
  openAndSendXhr(xhr, context, config) {
    if (!xhr.readyState) {
      xhr.open("GET", context.url, true);
    }
    const headers = context.headers;
    const {
      maxTimeToFirstByteMs,
      maxLoadTimeMs
    } = config.loadPolicy;
    if (headers) {
      for (const header in headers) {
        xhr.setRequestHeader(header, headers[header]);
      }
    }
    if (context.rangeEnd) {
      xhr.setRequestHeader("Range", "bytes=" + context.rangeStart + "-" + (context.rangeEnd - 1));
    }
    xhr.onreadystatechange = this.readystatechange.bind(this);
    xhr.onprogress = this.loadprogress.bind(this);
    xhr.responseType = context.responseType;
    self.clearTimeout(this.requestTimeout);
    config.timeout = maxTimeToFirstByteMs && isFiniteNumber(maxTimeToFirstByteMs) ? maxTimeToFirstByteMs : maxLoadTimeMs;
    this.requestTimeout = self.setTimeout(this.loadtimeout.bind(this), config.timeout);
    xhr.send();
  }
  readystatechange() {
    const {
      context,
      loader: xhr,
      stats
    } = this;
    if (!context || !xhr) {
      return;
    }
    const readyState = xhr.readyState;
    const config = this.config;
    if (stats.aborted) {
      return;
    }
    if (readyState >= 2) {
      if (stats.loading.first === 0) {
        stats.loading.first = Math.max(self.performance.now(), stats.loading.start);
        if (config.timeout !== config.loadPolicy.maxLoadTimeMs) {
          self.clearTimeout(this.requestTimeout);
          config.timeout = config.loadPolicy.maxLoadTimeMs;
          this.requestTimeout = self.setTimeout(this.loadtimeout.bind(this), config.loadPolicy.maxLoadTimeMs - (stats.loading.first - stats.loading.start));
        }
      }
      if (readyState === 4) {
        self.clearTimeout(this.requestTimeout);
        xhr.onreadystatechange = null;
        xhr.onprogress = null;
        const status2 = xhr.status;
        const useResponseText = xhr.responseType === "text" ? xhr.responseText : null;
        if (status2 >= 200 && status2 < 300) {
          const data = useResponseText != null ? useResponseText : xhr.response;
          if (data != null) {
            var _this$callbacks3, _this$callbacks4;
            stats.loading.end = Math.max(self.performance.now(), stats.loading.first);
            const len = xhr.responseType === "arraybuffer" ? data.byteLength : data.length;
            stats.loaded = stats.total = len;
            stats.bwEstimate = stats.total * 8e3 / (stats.loading.end - stats.loading.first);
            const onProgress = (_this$callbacks3 = this.callbacks) == null ? void 0 : _this$callbacks3.onProgress;
            if (onProgress) {
              onProgress(stats, context, data, xhr);
            }
            const _response = {
              url: xhr.responseURL,
              data,
              code: status2
            };
            (_this$callbacks4 = this.callbacks) == null ? void 0 : _this$callbacks4.onSuccess(_response, stats, context, xhr);
            return;
          }
        }
        const retryConfig = config.loadPolicy.errorRetry;
        const retryCount = stats.retry;
        const response = {
          url: context.url,
          data: void 0,
          code: status2
        };
        if (shouldRetry(retryConfig, retryCount, false, response)) {
          this.retry(retryConfig);
        } else {
          var _this$callbacks5;
          logger.error(`${status2} while loading ${context.url}`);
          (_this$callbacks5 = this.callbacks) == null ? void 0 : _this$callbacks5.onError({
            code: status2,
            text: xhr.statusText
          }, context, xhr, stats);
        }
      }
    }
  }
  loadtimeout() {
    if (!this.config)
      return;
    const retryConfig = this.config.loadPolicy.timeoutRetry;
    const retryCount = this.stats.retry;
    if (shouldRetry(retryConfig, retryCount, true)) {
      this.retry(retryConfig);
    } else {
      var _this$context;
      logger.warn(`timeout while loading ${(_this$context = this.context) == null ? void 0 : _this$context.url}`);
      const callbacks = this.callbacks;
      if (callbacks) {
        this.abortInternal();
        callbacks.onTimeout(this.stats, this.context, this.loader);
      }
    }
  }
  retry(retryConfig) {
    const {
      context,
      stats
    } = this;
    this.retryDelay = getRetryDelay(retryConfig, stats.retry);
    stats.retry++;
    logger.warn(`${status ? "HTTP Status " + status : "Timeout"} while loading ${context == null ? void 0 : context.url}, retrying ${stats.retry}/${retryConfig.maxNumRetry} in ${this.retryDelay}ms`);
    this.abortInternal();
    this.loader = null;
    self.clearTimeout(this.retryTimeout);
    this.retryTimeout = self.setTimeout(this.loadInternal.bind(this), this.retryDelay);
  }
  loadprogress(event) {
    const stats = this.stats;
    stats.loaded = event.loaded;
    if (event.lengthComputable) {
      stats.total = event.total;
    }
  }
  getCacheAge() {
    let result = null;
    if (this.loader && AGE_HEADER_LINE_REGEX.test(this.loader.getAllResponseHeaders())) {
      const ageHeader = this.loader.getResponseHeader("age");
      result = ageHeader ? parseFloat(ageHeader) : null;
    }
    return result;
  }
  getResponseHeader(name) {
    if (this.loader && new RegExp(`^${name}:\\s*[\\d.]+\\s*$`, "im").test(this.loader.getAllResponseHeaders())) {
      return this.loader.getResponseHeader(name);
    }
    return null;
  }
};
var defaultLoadPolicy = {
  maxTimeToFirstByteMs: 8e3,
  maxLoadTimeMs: 2e4,
  timeoutRetry: null,
  errorRetry: null
};
var hlsDefaultConfig = _objectSpread2(_objectSpread2({
  autoStartLoad: true,
  startPosition: -1,
  defaultAudioCodec: void 0,
  debug: false,
  capLevelOnFPSDrop: false,
  capLevelToPlayerSize: false,
  ignoreDevicePixelRatio: false,
  maxDevicePixelRatio: Number.POSITIVE_INFINITY,
  preferManagedMediaSource: true,
  initialLiveManifestSize: 1,
  maxBufferLength: 30,
  backBufferLength: Infinity,
  frontBufferFlushThreshold: Infinity,
  startOnSegmentBoundary: false,
  maxBufferSize: 60 * 1e3 * 1e3,
  maxFragLookUpTolerance: 0.25,
  maxBufferHole: 0.1,
  detectStallWithCurrentTimeMs: 1250,
  highBufferWatchdogPeriod: 2,
  nudgeOffset: 0.1,
  nudgeMaxRetry: 3,
  nudgeOnVideoHole: true,
  liveSyncMode: "edge",
  liveSyncDurationCount: 3,
  liveSyncOnStallIncrease: 1,
  liveMaxLatencyDurationCount: Infinity,
  liveSyncDuration: void 0,
  liveMaxLatencyDuration: void 0,
  maxLiveSyncPlaybackRate: 1,
  liveDurationInfinity: false,
  liveBackBufferLength: null,
  maxMaxBufferLength: 600,
  enableWorker: true,
  workerPath: null,
  enableSoftwareAES: true,
  startLevel: void 0,
  startFragPrefetch: false,
  fpsDroppedMonitoringPeriod: 5e3,
  fpsDroppedMonitoringThreshold: 0.2,
  appendErrorMaxRetry: 3,
  ignorePlaylistParsingErrors: false,
  loader: XhrLoader,
  fLoader: void 0,
  pLoader: void 0,
  xhrSetup: void 0,
  licenseXhrSetup: void 0,
  licenseResponseCallback: void 0,
  abrController: AbrController,
  bufferController: BufferController,
  capLevelController: CapLevelController,
  errorController: ErrorController,
  fpsController: FPSController,
  stretchShortVideoTrack: false,
  maxAudioFramesDrift: 1,
  forceKeyFrameOnDiscontinuity: true,
  abrEwmaFastLive: 3,
  abrEwmaSlowLive: 9,
  abrEwmaFastVoD: 3,
  abrEwmaSlowVoD: 9,
  abrEwmaDefaultEstimate: 5e5,
  abrEwmaDefaultEstimateMax: 5e6,
  abrBandWidthFactor: 0.95,
  abrBandWidthUpFactor: 0.7,
  abrMaxWithRealBitrate: false,
  maxStarvationDelay: 4,
  maxLoadingDelay: 4,
  minAutoBitrate: 0,
  emeEnabled: false,
  widevineLicenseUrl: void 0,
  drmSystems: {},
  drmSystemOptions: {},
  requestMediaKeySystemAccessFunc: requestMediaKeySystemAccess,
  requireKeySystemAccessOnStart: false,
  testBandwidth: true,
  progressive: false,
  lowLatencyMode: true,
  cmcd: void 0,
  enableDateRangeMetadataCues: true,
  enableEmsgMetadataCues: true,
  enableEmsgKLVMetadata: false,
  enableID3MetadataCues: true,
  enableInterstitialPlayback: true,
  interstitialAppendInPlace: true,
  interstitialLiveLookAhead: 10,
  useMediaCapabilities: true,
  preserveManualLevelOnError: false,
  certLoadPolicy: {
    default: defaultLoadPolicy
  },
  keyLoadPolicy: {
    default: {
      maxTimeToFirstByteMs: 8e3,
      maxLoadTimeMs: 2e4,
      timeoutRetry: {
        maxNumRetry: 1,
        retryDelayMs: 1e3,
        maxRetryDelayMs: 2e4,
        backoff: "linear"
      },
      errorRetry: {
        maxNumRetry: 8,
        retryDelayMs: 1e3,
        maxRetryDelayMs: 2e4,
        backoff: "linear"
      }
    }
  },
  manifestLoadPolicy: {
    default: {
      maxTimeToFirstByteMs: Infinity,
      maxLoadTimeMs: 2e4,
      timeoutRetry: {
        maxNumRetry: 2,
        retryDelayMs: 0,
        maxRetryDelayMs: 0
      },
      errorRetry: {
        maxNumRetry: 1,
        retryDelayMs: 1e3,
        maxRetryDelayMs: 8e3
      }
    }
  },
  playlistLoadPolicy: {
    default: {
      maxTimeToFirstByteMs: 1e4,
      maxLoadTimeMs: 2e4,
      timeoutRetry: {
        maxNumRetry: 2,
        retryDelayMs: 0,
        maxRetryDelayMs: 0
      },
      errorRetry: {
        maxNumRetry: 2,
        retryDelayMs: 1e3,
        maxRetryDelayMs: 8e3
      }
    }
  },
  fragLoadPolicy: {
    default: {
      maxTimeToFirstByteMs: 1e4,
      maxLoadTimeMs: 12e4,
      timeoutRetry: {
        maxNumRetry: 4,
        retryDelayMs: 0,
        maxRetryDelayMs: 0
      },
      errorRetry: {
        maxNumRetry: 6,
        retryDelayMs: 1e3,
        maxRetryDelayMs: 8e3
      }
    }
  },
  steeringManifestLoadPolicy: {
    default: {
      maxTimeToFirstByteMs: 1e4,
      maxLoadTimeMs: 2e4,
      timeoutRetry: {
        maxNumRetry: 2,
        retryDelayMs: 0,
        maxRetryDelayMs: 0
      },
      errorRetry: {
        maxNumRetry: 1,
        retryDelayMs: 1e3,
        maxRetryDelayMs: 8e3
      }
    }
  },
  interstitialAssetListLoadPolicy: {
    default: {
      maxTimeToFirstByteMs: 1e4,
      maxLoadTimeMs: 3e4,
      timeoutRetry: {
        maxNumRetry: 0,
        retryDelayMs: 0,
        maxRetryDelayMs: 0
      },
      errorRetry: {
        maxNumRetry: 0,
        retryDelayMs: 1e3,
        maxRetryDelayMs: 8e3
      }
    }
  },
  manifestLoadingTimeOut: 1e4,
  manifestLoadingMaxRetry: 1,
  manifestLoadingRetryDelay: 1e3,
  manifestLoadingMaxRetryTimeout: 64e3,
  levelLoadingTimeOut: 1e4,
  levelLoadingMaxRetry: 4,
  levelLoadingRetryDelay: 1e3,
  levelLoadingMaxRetryTimeout: 64e3,
  fragLoadingTimeOut: 2e4,
  fragLoadingMaxRetry: 6,
  fragLoadingRetryDelay: 1e3,
  fragLoadingMaxRetryTimeout: 64e3
}, timelineConfig()), {}, {
  subtitleStreamController: SubtitleStreamController,
  subtitleTrackController: SubtitleTrackController,
  timelineController: TimelineController,
  audioStreamController: AudioStreamController,
  audioTrackController: AudioTrackController,
  emeController: EMEController,
  cmcdController: CMCDController,
  contentSteeringController: ContentSteeringController,
  interstitialsController: InterstitialsController
});
function timelineConfig() {
  return {
    cueHandler: Cues,
    enableWebVTT: true,
    enableIMSC1: true,
    enableCEA708Captions: true,
    captionsTextTrack1Label: "English",
    captionsTextTrack1LanguageCode: "en",
    captionsTextTrack2Label: "Spanish",
    captionsTextTrack2LanguageCode: "es",
    captionsTextTrack3Label: "Unknown CC",
    captionsTextTrack3LanguageCode: "",
    captionsTextTrack4Label: "Unknown CC",
    captionsTextTrack4LanguageCode: "",
    renderTextTracksNatively: true
  };
}
function mergeConfig(defaultConfig, userConfig, logger2) {
  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");
  }
  if (userConfig.liveMaxLatencyDurationCount !== void 0 && (userConfig.liveSyncDurationCount === void 0 || userConfig.liveMaxLatencyDurationCount <= userConfig.liveSyncDurationCount)) {
    throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be greater than "liveSyncDurationCount"');
  }
  if (userConfig.liveMaxLatencyDuration !== void 0 && (userConfig.liveSyncDuration === void 0 || userConfig.liveMaxLatencyDuration <= userConfig.liveSyncDuration)) {
    throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be greater than "liveSyncDuration"');
  }
  const defaultsCopy = deepCpy(defaultConfig);
  const deprecatedSettingTypes = ["manifest", "level", "frag"];
  const deprecatedSettings = ["TimeOut", "MaxRetry", "RetryDelay", "MaxRetryTimeout"];
  deprecatedSettingTypes.forEach((type) => {
    const policyName = `${type === "level" ? "playlist" : type}LoadPolicy`;
    const policyNotSet = userConfig[policyName] === void 0;
    const report = [];
    deprecatedSettings.forEach((setting) => {
      const deprecatedSetting = `${type}Loading${setting}`;
      const value = userConfig[deprecatedSetting];
      if (value !== void 0 && policyNotSet) {
        report.push(deprecatedSetting);
        const settings = defaultsCopy[policyName].default;
        userConfig[policyName] = {
          default: settings
        };
        switch (setting) {
          case "TimeOut":
            settings.maxLoadTimeMs = value;
            settings.maxTimeToFirstByteMs = value;
            break;
          case "MaxRetry":
            settings.errorRetry.maxNumRetry = value;
            settings.timeoutRetry.maxNumRetry = value;
            break;
          case "RetryDelay":
            settings.errorRetry.retryDelayMs = value;
            settings.timeoutRetry.retryDelayMs = value;
            break;
          case "MaxRetryTimeout":
            settings.errorRetry.maxRetryDelayMs = value;
            settings.timeoutRetry.maxRetryDelayMs = value;
            break;
        }
      }
    });
    if (report.length) {
      logger2.warn(`hls.js config: "${report.join('", "')}" setting(s) are deprecated, use "${policyName}": ${stringify(userConfig[policyName])}`);
    }
  });
  return _objectSpread2(_objectSpread2({}, defaultsCopy), userConfig);
}
function deepCpy(obj) {
  if (obj && typeof obj === "object") {
    if (Array.isArray(obj)) {
      return obj.map(deepCpy);
    }
    return Object.keys(obj).reduce((result, key) => {
      result[key] = deepCpy(obj[key]);
      return result;
    }, {});
  }
  return obj;
}
function enableStreamingMode(config, logger2) {
  const currentLoader = config.loader;
  if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) {
    logger2.log("[config]: Custom loader detected, cannot enable progressive streaming");
    config.progressive = false;
  } else {
    const canStreamProgressively = fetchSupported();
    if (canStreamProgressively) {
      config.loader = FetchLoader;
      config.progressive = true;
      config.enableSoftwareAES = true;
      logger2.log("[config]: Progressive streaming enabled, using FetchLoader");
    }
  }
}
var MAX_START_GAP_JUMP = 2;
var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1;
var SKIP_BUFFER_RANGE_START = 0.05;
var TICK_INTERVAL$1 = 100;
var GapController = class extends TaskLoop {
  constructor(hls, fragmentTracker) {
    super("gap-controller", hls.logger);
    this.hls = null;
    this.fragmentTracker = null;
    this.media = null;
    this.mediaSource = void 0;
    this.nudgeRetry = 0;
    this.stallReported = false;
    this.stalled = null;
    this.moved = false;
    this.seeking = false;
    this.buffered = {};
    this.lastCurrentTime = 0;
    this.ended = 0;
    this.waiting = 0;
    this.onMediaPlaying = () => {
      this.ended = 0;
      this.waiting = 0;
    };
    this.onMediaWaiting = () => {
      var _this$media;
      if ((_this$media = this.media) != null && _this$media.seeking) {
        return;
      }
      this.waiting = self.performance.now();
      this.tick();
    };
    this.onMediaEnded = () => {
      if (this.hls) {
        var _this$media2;
        this.ended = ((_this$media2 = this.media) == null ? void 0 : _this$media2.currentTime) || 1;
        this.hls.trigger(Events.MEDIA_ENDED, {
          stalled: false
        });
      }
    };
    this.hls = hls;
    this.fragmentTracker = fragmentTracker;
    this.registerListeners();
  }
  registerListeners() {
    const {
      hls
    } = this;
    if (hls) {
      hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
      hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
      hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this);
    }
  }
  unregisterListeners() {
    const {
      hls
    } = this;
    if (hls) {
      hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
      hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
      hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this);
    }
  }
  destroy() {
    super.destroy();
    this.unregisterListeners();
    this.media = this.hls = this.fragmentTracker = null;
    this.mediaSource = void 0;
  }
  onMediaAttached(event, data) {
    this.setInterval(TICK_INTERVAL$1);
    this.mediaSource = data.mediaSource;
    const media = this.media = data.media;
    addEventListener(media, "playing", this.onMediaPlaying);
    addEventListener(media, "waiting", this.onMediaWaiting);
    addEventListener(media, "ended", this.onMediaEnded);
  }
  onMediaDetaching(event, data) {
    this.clearInterval();
    const {
      media
    } = this;
    if (media) {
      removeEventListener(media, "playing", this.onMediaPlaying);
      removeEventListener(media, "waiting", this.onMediaWaiting);
      removeEventListener(media, "ended", this.onMediaEnded);
      this.media = null;
    }
    this.mediaSource = void 0;
  }
  onBufferAppended(event, data) {
    this.buffered = data.timeRanges;
  }
  get hasBuffered() {
    return Object.keys(this.buffered).length > 0;
  }
  tick() {
    var _this$media3;
    if (!((_this$media3 = this.media) != null && _this$media3.readyState) || !this.hasBuffered) {
      return;
    }
    const currentTime = this.media.currentTime;
    this.poll(currentTime, this.lastCurrentTime);
    this.lastCurrentTime = currentTime;
  }
  poll(currentTime, lastCurrentTime) {
    var _this$hls, _this$hls2;
    const config = (_this$hls = this.hls) == null ? void 0 : _this$hls.config;
    if (!config) {
      return;
    }
    const media = this.media;
    if (!media) {
      return;
    }
    const {
      seeking
    } = media;
    const seeked = this.seeking && !seeking;
    const beginSeek = !this.seeking && seeking;
    const pausedEndedOrHalted = media.paused && !seeking || media.ended || media.playbackRate === 0;
    this.seeking = seeking;
    if (currentTime !== lastCurrentTime) {
      if (lastCurrentTime) {
        this.ended = 0;
      }
      this.moved = true;
      if (!seeking) {
        this.nudgeRetry = 0;
        if (config.nudgeOnVideoHole && !pausedEndedOrHalted && currentTime > lastCurrentTime) {
          this.nudgeOnVideoHole(currentTime, lastCurrentTime);
        }
      }
      if (this.waiting === 0) {
        this.stallResolved(currentTime);
      }
      return;
    }
    if (beginSeek || seeked) {
      if (seeked) {
        this.stallResolved(currentTime);
      }
      return;
    }
    if (pausedEndedOrHalted) {
      this.nudgeRetry = 0;
      this.stallResolved(currentTime);
      if (!this.ended && media.ended && this.hls) {
        this.ended = currentTime || 1;
        this.hls.trigger(Events.MEDIA_ENDED, {
          stalled: false
        });
      }
      return;
    }
    if (!BufferHelper.getBuffered(media).length) {
      this.nudgeRetry = 0;
      return;
    }
    const bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
    const nextStart = bufferInfo.nextStart || 0;
    const fragmentTracker = this.fragmentTracker;
    if (seeking && fragmentTracker && this.hls) {
      const inFlightDependency = getInFlightDependency(this.hls.inFlightFragments, currentTime);
      const hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP;
      const noBufferHole = !nextStart || inFlightDependency || nextStart - currentTime > MAX_START_GAP_JUMP && !fragmentTracker.getPartialFragment(currentTime);
      if (hasEnoughBuffer || noBufferHole) {
        return;
      }
      this.moved = false;
    }
    const levelDetails = (_this$hls2 = this.hls) == null ? void 0 : _this$hls2.latestLevelDetails;
    if (!this.moved && this.stalled !== null && fragmentTracker) {
      const isBuffered = bufferInfo.len > 0;
      if (!isBuffered && !nextStart) {
        return;
      }
      const startJump = Math.max(nextStart, bufferInfo.start || 0) - currentTime;
      const isLive = !!(levelDetails != null && levelDetails.live);
      const maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP;
      const partialOrGap = fragmentTracker.getPartialFragment(currentTime);
      if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) {
        if (!media.paused) {
          this._trySkipBufferHole(partialOrGap);
        }
        return;
      }
    }
    const detectStallWithCurrentTimeMs = config.detectStallWithCurrentTimeMs;
    const tnow = self.performance.now();
    const tWaiting = this.waiting;
    let stalled = this.stalled;
    if (stalled === null) {
      if (tWaiting > 0 && tnow - tWaiting < detectStallWithCurrentTimeMs) {
        stalled = this.stalled = tWaiting;
      } else {
        this.stalled = tnow;
        return;
      }
    }
    const stalledDuration = tnow - stalled;
    if (!seeking && (stalledDuration >= detectStallWithCurrentTimeMs || tWaiting) && this.hls) {
      var _this$mediaSource;
      if (((_this$mediaSource = this.mediaSource) == null ? void 0 : _this$mediaSource.readyState) === "ended" && !(levelDetails != null && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? void 0 : levelDetails.edge) || 0)) < 1) {
        if (this.ended) {
          return;
        }
        this.ended = currentTime || 1;
        this.hls.trigger(Events.MEDIA_ENDED, {
          stalled: true
        });
        return;
      }
      this._reportStall(bufferInfo);
      if (!this.media || !this.hls) {
        return;
      }
    }
    const bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole);
    this._tryFixBufferStall(bufferedWithHoles, stalledDuration, currentTime);
  }
  stallResolved(currentTime) {
    const stalled = this.stalled;
    if (stalled && this.hls) {
      this.stalled = null;
      if (this.stallReported) {
        const stalledDuration = self.performance.now() - stalled;
        this.log(`playback not stuck anymore @${currentTime}, after ${Math.round(stalledDuration)}ms`);
        this.stallReported = false;
        this.waiting = 0;
        this.hls.trigger(Events.STALL_RESOLVED, {});
      }
    }
  }
  nudgeOnVideoHole(currentTime, lastCurrentTime) {
    var _this$buffered$audio;
    const videoSourceBuffered = this.buffered.video;
    if (this.hls && this.media && this.fragmentTracker && (_this$buffered$audio = this.buffered.audio) != null && _this$buffered$audio.length && videoSourceBuffered && videoSourceBuffered.length > 1 && currentTime > videoSourceBuffered.end(0)) {
      const audioBufferInfo = BufferHelper.bufferedInfo(BufferHelper.timeRangesToArray(this.buffered.audio), currentTime, 0);
      if (audioBufferInfo.len > 1 && lastCurrentTime >= audioBufferInfo.start) {
        const videoTimes = BufferHelper.timeRangesToArray(videoSourceBuffered);
        const lastBufferedIndex = BufferHelper.bufferedInfo(videoTimes, lastCurrentTime, 0).bufferedIndex;
        if (lastBufferedIndex > -1 && lastBufferedIndex < videoTimes.length - 1) {
          const bufferedIndex = BufferHelper.bufferedInfo(videoTimes, currentTime, 0).bufferedIndex;
          const holeStart = videoTimes[lastBufferedIndex].end;
          const holeEnd = videoTimes[lastBufferedIndex + 1].start;
          if ((bufferedIndex === -1 || bufferedIndex > lastBufferedIndex) && holeEnd - holeStart < 1 && currentTime - holeStart < 2) {
            const error = new Error(`nudging playhead to flush pipeline after video hole. currentTime: ${currentTime} hole: ${holeStart} -> ${holeEnd} buffered index: ${bufferedIndex}`);
            this.warn(error.message);
            this.media.currentTime += 1e-6;
            const frag = this.fragmentTracker.getPartialFragment(currentTime) || void 0;
            const bufferInfo = BufferHelper.bufferInfo(this.media, currentTime, 0);
            this.hls.trigger(Events.ERROR, {
              type: ErrorTypes.MEDIA_ERROR,
              details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
              fatal: false,
              error,
              reason: error.message,
              frag,
              buffer: bufferInfo.len,
              bufferInfo
            });
          }
        }
      }
    }
  }
  _tryFixBufferStall(bufferInfo, stalledDurationMs, currentTime) {
    var _this$hls3, _this$hls4;
    const {
      fragmentTracker,
      media
    } = this;
    const config = (_this$hls3 = this.hls) == null ? void 0 : _this$hls3.config;
    if (!media || !fragmentTracker || !config) {
      return;
    }
    const levelDetails = (_this$hls4 = this.hls) == null ? void 0 : _this$hls4.latestLevelDetails;
    const partial = fragmentTracker.getPartialFragment(currentTime);
    if (partial || levelDetails != null && levelDetails.live && currentTime < levelDetails.fragmentStart) {
      const targetTime = this._trySkipBufferHole(partial);
      if (targetTime || !this.media) {
        return;
      }
    }
    const bufferedRanges = bufferInfo.buffered;
    const adjacentTraversal = this.adjacentTraversal(bufferInfo, currentTime);
    if ((bufferedRanges && bufferedRanges.length > 1 && bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && (bufferInfo.nextStart - currentTime < config.maxBufferHole || adjacentTraversal)) && (stalledDurationMs > config.highBufferWatchdogPeriod * 1e3 || this.waiting)) {
      this.warn("Trying to nudge playhead over buffer-hole");
      this._tryNudgeBuffer(bufferInfo);
    }
  }
  adjacentTraversal(bufferInfo, currentTime) {
    const fragmentTracker = this.fragmentTracker;
    const nextStart = bufferInfo.nextStart;
    if (fragmentTracker && nextStart) {
      const current = fragmentTracker.getFragAtPos(currentTime, PlaylistLevelType.MAIN);
      const next = fragmentTracker.getFragAtPos(nextStart, PlaylistLevelType.MAIN);
      if (current && next) {
        return next.sn - current.sn < 2;
      }
    }
    return false;
  }
  _reportStall(bufferInfo) {
    const {
      hls,
      media,
      stallReported,
      stalled
    } = this;
    if (!stallReported && stalled !== null && media && hls) {
      this.stallReported = true;
      const error = new Error(`Playback stalling at @${media.currentTime} due to low buffer (${stringify(bufferInfo)})`);
      this.warn(error.message);
      hls.trigger(Events.ERROR, {
        type: ErrorTypes.MEDIA_ERROR,
        details: ErrorDetails.BUFFER_STALLED_ERROR,
        fatal: false,
        error,
        buffer: bufferInfo.len,
        bufferInfo,
        stalled: {
          start: stalled
        }
      });
    }
  }
  _trySkipBufferHole(partial) {
    var _this$hls5;
    const {
      fragmentTracker,
      media
    } = this;
    const config = (_this$hls5 = this.hls) == null ? void 0 : _this$hls5.config;
    if (!media || !fragmentTracker || !config) {
      return 0;
    }
    const currentTime = media.currentTime;
    const bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0);
    const startTime = currentTime < bufferInfo.start ? bufferInfo.start : bufferInfo.nextStart;
    if (startTime && this.hls) {
      const bufferStarved = bufferInfo.len <= config.maxBufferHole;
      const waiting = bufferInfo.len > 0 && bufferInfo.len < 1 && media.readyState < 3;
      const gapLength = startTime - currentTime;
      if (gapLength > 0 && (bufferStarved || waiting)) {
        if (gapLength > config.maxBufferHole) {
          let startGap = false;
          if (currentTime === 0) {
            const startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN);
            if (startFrag && startTime < startFrag.end) {
              startGap = true;
            }
          }
          if (!startGap) {
            const startProvisioned = partial || fragmentTracker.getAppendedFrag(currentTime, PlaylistLevelType.MAIN);
            if (startProvisioned) {
              var _this$hls$loadLevelOb;
              if (!((_this$hls$loadLevelOb = this.hls.loadLevelObj) != null && _this$hls$loadLevelOb.details)) {
                return 0;
              }
              const inFlightDependency = getInFlightDependency(this.hls.inFlightFragments, startTime);
              if (inFlightDependency) {
                return 0;
              }
              let moreToLoad = false;
              let pos = startProvisioned.end;
              while (pos < startTime) {
                const provisioned = fragmentTracker.getAppendedFrag(pos, PlaylistLevelType.MAIN) || fragmentTracker.getPartialFragment(pos);
                if (provisioned) {
                  pos += provisioned.duration;
                } else {
                  moreToLoad = true;
                  break;
                }
              }
              if (moreToLoad) {
                return 0;
              }
            }
          }
        }
        const targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS);
        this.warn(`skipping hole, adjusting currentTime from ${currentTime} to ${targetTime}`);
        this.moved = true;
        media.currentTime = targetTime;
        if (!(partial != null && partial.gap)) {
          const error = new Error(`fragment loaded with buffer holes, seeking from ${currentTime} to ${targetTime}`);
          this.hls.trigger(Events.ERROR, {
            type: ErrorTypes.MEDIA_ERROR,
            details: ErrorDetails.BUFFER_SEEK_OVER_HOLE,
            fatal: false,
            error,
            reason: error.message,
            frag: partial || void 0,
            buffer: bufferInfo.len,
            bufferInfo
          });
        }
        return targetTime;
      }
    }
    return 0;
  }
  _tryNudgeBuffer(bufferInfo) {
    const {
      hls,
      media,
      nudgeRetry
    } = this;
    const config = hls == null ? void 0 : hls.config;
    if (!media || !config) {
      return 0;
    }
    const currentTime = media.currentTime;
    this.nudgeRetry++;
    if (nudgeRetry < config.nudgeMaxRetry) {
      const targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset;
      const error = new Error(`Nudging 'currentTime' from ${currentTime} to ${targetTime}`);
      this.warn(error.message);
      media.currentTime = targetTime;
      hls.trigger(Events.ERROR, {
        type: ErrorTypes.MEDIA_ERROR,
        details: ErrorDetails.BUFFER_NUDGE_ON_STALL,
        error,
        fatal: false,
        buffer: bufferInfo.len,
        bufferInfo
      });
    } else {
      const error = new Error(`Playhead still not moving while enough data buffered @${currentTime} after ${config.nudgeMaxRetry} nudges`);
      this.error(error.message);
      hls.trigger(Events.ERROR, {
        type: ErrorTypes.MEDIA_ERROR,
        details: ErrorDetails.BUFFER_STALLED_ERROR,
        error,
        fatal: true,
        buffer: bufferInfo.len,
        bufferInfo
      });
    }
  }
};
function getInFlightDependency(inFlightFragments, currentTime) {
  const main = inFlight(inFlightFragments.main);
  if (main && main.start <= currentTime) {
    return main;
  }
  const audio = inFlight(inFlightFragments.audio);
  if (audio && audio.start <= currentTime) {
    return audio;
  }
  return null;
}
function inFlight(inFlightData) {
  if (!inFlightData) {
    return null;
  }
  switch (inFlightData.state) {
    case State.IDLE:
    case State.STOPPED:
    case State.ENDED:
    case State.ERROR:
      return null;
  }
  return inFlightData.frag;
}
var MIN_CUE_DURATION = 0.25;
function getCueClass() {
  if (typeof self === "undefined")
    return void 0;
  return self.VTTCue || self.TextTrackCue;
}
function createCueWithDataFields(Cue, startTime, endTime, data, type) {
  let cue = new Cue(startTime, endTime, "");
  try {
    cue.value = data;
    if (type) {
      cue.type = type;
    }
  } catch (e) {
    cue = new Cue(startTime, endTime, stringify(type ? _objectSpread2({
      type
    }, data) : data));
  }
  return cue;
}
var MAX_CUE_ENDTIME = (() => {
  const Cue = getCueClass();
  try {
    Cue && new Cue(0, Number.POSITIVE_INFINITY, "");
  } catch (e) {
    return Number.MAX_VALUE;
  }
  return Number.POSITIVE_INFINITY;
})();
var ID3TrackController = class {
  constructor(hls) {
    this.hls = void 0;
    this.id3Track = null;
    this.media = null;
    this.dateRangeCuesAppended = {};
    this.removeCues = true;
    this.onEventCueEnter = () => {
      if (!this.hls) {
        return;
      }
      this.hls.trigger(Events.EVENT_CUE_ENTER, {});
    };
    this.hls = hls;
    this._registerListeners();
  }
  destroy() {
    this._unregisterListeners();
    this.id3Track = null;
    this.media = null;
    this.dateRangeCuesAppended = {};
    this.hls = this.onEventCueEnter = null;
  }
  _registerListeners() {
    const {
      hls
    } = this;
    hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);
    hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
    hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
    hls.on(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this);
    hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
    hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this);
    hls.on(Events.LEVEL_PTS_UPDATED, this.onLevelPtsUpdated, this);
  }
  _unregisterListeners() {
    const {
      hls
    } = this;
    hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this);
    hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
    hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
    hls.off(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this);
    hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this);
    hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this);
    hls.off(Events.LEVEL_PTS_UPDATED, this.onLevelPtsUpdated, this);
  }
  onMediaAttaching(event, data) {
    var _data$overrides;
    this.media = data.media;
    if (((_data$overrides = data.overrides) == null ? void 0 : _data$overrides.cueRemoval) === false) {
      this.removeCues = false;
    }
  }
  onMediaAttached() {
    const details = this.hls.latestLevelDetails;
    if (details) {
      this.updateDateRangeCues(details);
    }
  }
  onMediaDetaching(event, data) {
    this.media = null;
    const transferringMedia = !!data.transferMedia;
    if (transferringMedia) {
      return;
    }
    if (this.id3Track) {
      if (this.removeCues) {
        clearCurrentCues(this.id3Track, this.onEventCueEnter);
      }
      this.id3Track = null;
    }
    this.dateRangeCuesAppended = {};
  }
  onManifestLoading() {
    this.dateRangeCuesAppended = {};
  }
  createTrack(media) {
    const track = this.getID3Track(media.textTracks);
    track.mode = "hidden";
    return track;
  }
  getID3Track(textTracks) {
    if (!this.media) {
      return;
    }
    for (let i = 0; i < textTracks.length; i++) {
      const textTrack = textTracks[i];
      if (textTrack.kind === "metadata" && textTrack.label === "id3") {
        sendAddTrackEvent(textTrack, this.media);
        return textTrack;
      }
    }
    return this.media.addTextTrack("metadata", "id3");
  }
  onFragParsingMetadata(event, data) {
    if (!this.media) {
      return;
    }
    const {
      hls: {
        config: {
          enableEmsgMetadataCues,
          enableID3MetadataCues
        }
      }
    } = this;
    if (!enableEmsgMetadataCues && !enableID3MetadataCues) {
      return;
    }
    const {
      samples
    } = data;
    if (!this.id3Track) {
      this.id3Track = this.createTrack(this.media);
    }
    const Cue = getCueClass();
    if (!Cue) {
      return;
    }
    for (let i = 0; i < samples.length; i++) {
      const type = samples[i].type;
      if (type === MetadataSchema.emsg && !enableEmsgMetadataCues || !enableID3MetadataCues) {
        continue;
      }
      const frames = getId3Frames(samples[i].data);
      if (frames) {
        const startTime = samples[i].pts;
        let endTime = startTime + samples[i].duration;
        if (endTime > MAX_CUE_ENDTIME) {
          endTime = MAX_CUE_ENDTIME;
        }
        const timeDiff = endTime - startTime;
        if (timeDiff <= 0) {
          endTime = startTime + MIN_CUE_DURATION;
        }
        for (let j = 0; j < frames.length; j++) {
          const frame = frames[j];
          if (!isId3TimestampFrame(frame)) {
            this.updateId3CueEnds(startTime, type);
            const cue = createCueWithDataFields(Cue, startTime, endTime, frame, type);
            if (cue) {
              this.id3Track.addCue(cue);
            }
          }
        }
      }
    }
  }
  updateId3CueEnds(startTime, type) {
    var _this$id3Track;
    const cues = (_this$id3Track = this.id3Track) == null ? void 0 : _this$id3Track.cues;
    if (cues) {
      for (let i = cues.length; i--; ) {
        const cue = cues[i];
        if (cue.type === type && cue.startTime < startTime && cue.endTime === MAX_CUE_ENDTIME) {
          cue.endTime = startTime;
        }
      }
    }
  }
  onBufferFlushing(event, {
    startOffset,
    endOffset,
    type
  }) {
    const {
      id3Track,
      hls
    } = this;
    if (!hls) {
      return;
    }
    const {
      config: {
        enableEmsgMetadataCues,
        enableID3MetadataCues
      }
    } = hls;
    if (id3Track && (enableEmsgMetadataCues || enableID3MetadataCues)) {
      let predicate;
      if (type === "audio") {
        predicate = (cue) => cue.type === MetadataSchema.audioId3 && enableID3MetadataCues;
      } else if (type === "video") {
        predicate = (cue) => cue.type === MetadataSchema.emsg && enableEmsgMetadataCues;
      } else {
        predicate = (cue) => cue.type === MetadataSchema.audioId3 && enableID3MetadataCues || cue.type === MetadataSchema.emsg && enableEmsgMetadataCues;
      }
      removeCuesInRange(id3Track, startOffset, endOffset, predicate);
    }
  }
  onLevelUpdated(event, {
    details
  }) {
    this.updateDateRangeCues(details, true);
  }
  onLevelPtsUpdated(event, data) {
    if (Math.abs(data.drift) > 0.01) {
      this.updateDateRangeCues(data.details);
    }
  }
  updateDateRangeCues(details, removeOldCues) {
    if (!this.media || !details.hasProgramDateTime || !this.hls.config.enableDateRangeMetadataCues) {
      return;
    }
    const {
      id3Track
    } = this;
    const {
      dateRanges
    } = details;
    const ids = Object.keys(dateRanges);
    let dateRangeCuesAppended = this.dateRangeCuesAppended;
    if (id3Track && removeOldCues) {
      var _id3Track$cues;
      if ((_id3Track$cues = id3Track.cues) != null && _id3Track$cues.length) {
        const idsToRemove = Object.keys(dateRangeCuesAppended).filter((id) => !ids.includes(id));
        for (let i = idsToRemove.length; i--; ) {
          const id = idsToRemove[i];
          const cues = dateRangeCuesAppended[id].cues;
          delete dateRangeCuesAppended[id];
          Object.keys(cues).forEach((key) => {
            try {
              const cue = cues[key];
              cue.removeEventListener("enter", this.onEventCueEnter);
              id3Track.removeCue(cue);
            } catch (e) {
            }
          });
        }
      } else {
        dateRangeCuesAppended = this.dateRangeCuesAppended = {};
      }
    }
    const lastFragment = details.fragments[details.fragments.length - 1];
    if (ids.length === 0 || !isFiniteNumber(lastFragment == null ? void 0 : lastFragment.programDateTime)) {
      return;
    }
    if (!this.id3Track) {
      this.id3Track = this.createTrack(this.media);
    }
    const Cue = getCueClass();
    for (let i = 0; i < ids.length; i++) {
      const id = ids[i];
      const dateRange = dateRanges[id];
      const startTime = dateRange.startTime;
      const appendedDateRangeCues = dateRangeCuesAppended[id];
      const cues = (appendedDateRangeCues == null ? void 0 : appendedDateRangeCues.cues) || {};
      let durationKnown = (appendedDateRangeCues == null ? void 0 : appendedDateRangeCues.durationKnown) || false;
      let endTime = MAX_CUE_ENDTIME;
      const {
        duration,
        endDate
      } = dateRange;
      if (endDate && duration !== null) {
        endTime = startTime + duration;
        durationKnown = true;
      } else if (dateRange.endOnNext && !durationKnown) {
        const nextDateRangeWithSameClass = ids.reduce((candidateDateRange, id2) => {
          if (id2 !== dateRange.id) {
            const otherDateRange = dateRanges[id2];
            if (otherDateRange.class === dateRange.class && otherDateRange.startDate > dateRange.startDate && (!candidateDateRange || dateRange.startDate < candidateDateRange.startDate)) {
              return otherDateRange;
            }
          }
          return candidateDateRange;
        }, null);
        if (nextDateRangeWithSameClass) {
          endTime = nextDateRangeWithSameClass.startTime;
          durationKnown = true;
        }
      }
      const attributes = Object.keys(dateRange.attr);
      for (let j = 0; j < attributes.length; j++) {
        const key = attributes[j];
        if (!isDateRangeCueAttribute(key)) {
          continue;
        }
        const cue = cues[key];
        if (cue) {
          if (durationKnown && !appendedDateRangeCues.durationKnown) {
            cue.endTime = endTime;
          } else if (Math.abs(cue.startTime - startTime) > 0.01) {
            cue.startTime = startTime;
            cue.endTime = endTime;
          }
        } else if (Cue) {
          let data = dateRange.attr[key];
          if (isSCTE35Attribute(key)) {
            data = hexToArrayBuffer(data);
          }
          const payload = {
            key,
            data
          };
          const _cue = createCueWithDataFields(Cue, startTime, endTime, payload, MetadataSchema.dateRange);
          if (_cue) {
            _cue.id = id;
            this.id3Track.addCue(_cue);
            cues[key] = _cue;
            if (this.hls.config.interstitialsController) {
              if (key === "X-ASSET-LIST" || key === "X-ASSET-URL") {
                _cue.addEventListener("enter", this.onEventCueEnter);
              }
            }
          }
        }
      }
      dateRangeCuesAppended[id] = {
        cues,
        dateRange,
        durationKnown
      };
    }
  }
};
var LatencyController = class {
  constructor(hls) {
    this.hls = void 0;
    this.config = void 0;
    this.media = null;
    this.currentTime = 0;
    this.stallCount = 0;
    this._latency = null;
    this._targetLatencyUpdated = false;
    this.onTimeupdate = () => {
      const {
        media
      } = this;
      const levelDetails = this.levelDetails;
      if (!media || !levelDetails) {
        return;
      }
      this.currentTime = media.currentTime;
      const latency = this.computeLatency();
      if (latency === null) {
        return;
      }
      this._latency = latency;
      const {
        lowLatencyMode,
        maxLiveSyncPlaybackRate
      } = this.config;
      if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) {
        return;
      }
      const targetLatency = this.targetLatency;
      if (targetLatency === null) {
        return;
      }
      const distanceFromTarget = latency - targetLatency;
      const liveMinLatencyDuration = Math.min(this.maxLatency, targetLatency + levelDetails.targetduration);
      const inLiveRange = distanceFromTarget < liveMinLatencyDuration;
      if (inLiveRange && distanceFromTarget > 0.05 && this.forwardBufferLength > 1) {
        const max = Math.min(2, Math.max(1, maxLiveSyncPlaybackRate));
        const rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - this.edgeStalled)) * 20) / 20;
        const playbackRate = Math.min(max, Math.max(1, rate));
        this.changeMediaPlaybackRate(media, playbackRate);
      } else if (media.playbackRate !== 1 && media.playbackRate !== 0) {
        this.changeMediaPlaybackRate(media, 1);
      }
    };
    this.hls = hls;
    this.config = hls.config;
    this.registerListeners();
  }
  get levelDetails() {
    var _this$hls;
    return ((_this$hls = this.hls) == null ? void 0 : _this$hls.latestLevelDetails) || null;
  }
  get latency() {
    return this._latency || 0;
  }
  get maxLatency() {
    const {
      config
    } = this;
    if (config.liveMaxLatencyDuration !== void 0) {
      return config.liveMaxLatencyDuration;
    }
    const levelDetails = this.levelDetails;
    return levelDetails ? config.liveMaxLatencyDurationCount * levelDetails.targetduration : 0;
  }
  get targetLatency() {
    const levelDetails = this.levelDetails;
    if (levelDetails === null || this.hls === null) {
      return null;
    }
    const {
      holdBack,
      partHoldBack,
      targetduration
    } = levelDetails;
    const {
      liveSyncDuration,
      liveSyncDurationCount,
      lowLatencyMode
    } = this.config;
    const userConfig = this.hls.userConfig;
    let targetLatency = lowLatencyMode ? partHoldBack || holdBack : holdBack;
    if (this._targetLatencyUpdated || userConfig.liveSyncDuration || userConfig.liveSyncDurationCount || targetLatency === 0) {
      targetLatency = liveSyncDuration !== void 0 ? liveSyncDuration : liveSyncDurationCount * targetduration;
    }
    const maxLiveSyncOnStallIncrease = targetduration;
    return targetLatency + Math.min(this.stallCount * this.config.liveSyncOnStallIncrease, maxLiveSyncOnStallIncrease);
  }
  set targetLatency(latency) {
    this.stallCount = 0;
    this.config.liveSyncDuration = latency;
    this._targetLatencyUpdated = true;
  }
  get liveSyncPosition() {
    const liveEdge = this.estimateLiveEdge();
    const targetLatency = this.targetLatency;
    if (liveEdge === null || targetLatency === null) {
      return null;
    }
    const levelDetails = this.levelDetails;
    if (levelDetails === null) {
      return null;
    }
    const edge = levelDetails.edge;
    const syncPosition = liveEdge - targetLatency - this.edgeStalled;
    const min = edge - levelDetails.totalduration;
    const max = edge - (this.config.lowLatencyMode && levelDetails.partTarget || levelDetails.targetduration);
    return Math.min(Math.max(min, syncPosition), max);
  }
  get drift() {
    const levelDetails = this.levelDetails;
    if (levelDetails === null) {
      return 1;
    }
    return levelDetails.drift;
  }
  get edgeStalled() {
    const levelDetails = this.levelDetails;
    if (levelDetails === null) {
      return 0;
    }
    const maxLevelUpdateAge = (this.config.lowLatencyMode && levelDetails.partTarget || levelDetails.targetduration) * 3;
    return Math.max(levelDetails.age - maxLevelUpdateAge, 0);
  }
  get forwardBufferLength() {
    const {
      media
    } = this;
    const levelDetails = this.levelDetails;
    if (!media || !levelDetails) {
      return 0;
    }
    const bufferedRanges = media.buffered.length;
    return (bufferedRanges ? media.buffered.end(bufferedRanges - 1) : levelDetails.edge) - this.currentTime;
  }
  destroy() {
    this.unregisterListeners();
    this.onMediaDetaching();
    this.hls = null;
  }
  registerListeners() {
    const {
      hls
    } = this;
    if (!hls) {
      return;
    }
    hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
    hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
    hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this);
    hls.on(Events.ERROR, this.onError, this);
  }
  unregisterListeners() {
    const {
      hls
    } = this;
    if (!hls) {
      return;
    }
    hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this);
    hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this);
    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
    hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this);
    hls.off(Events.ERROR, this.onError, this);
  }
  onMediaAttached(event, data) {
    this.media = data.media;
    this.media.addEventListener("timeupdate", this.onTimeupdate);
  }
  onMediaDetaching() {
    if (this.media) {
      this.media.removeEventListener("timeupdate", this.onTimeupdate);
      this.media = null;
    }
  }
  onManifestLoading() {
    this._latency = null;
    this.stallCount = 0;
  }
  onLevelUpdated(event, {
    details
  }) {
    if (details.advanced) {
      this.onTimeupdate();
    }
    if (!details.live && this.media) {
      this.media.removeEventListener("timeupdate", this.onTimeupdate);
    }
  }
  onError(event, data) {
    var _this$levelDetails;
    if (data.details !== ErrorDetails.BUFFER_STALLED_ERROR) {
      return;
    }
    this.stallCount++;
    if (this.hls && (_this$levelDetails = this.levelDetails) != null && _this$levelDetails.live) {
      this.hls.logger.warn("[latency-controller]: Stall detected, adjusting target latency");
    }
  }
  changeMediaPlaybackRate(media, playbackRate) {
    var _this$hls2, _this$targetLatency;
    if (media.playbackRate === playbackRate) {
      return;
    }
    (_this$hls2 = this.hls) == null ? void 0 : _this$hls2.logger.debug(`[latency-controller]: latency=${this.latency.toFixed(3)}, targetLatency=${(_this$targetLatency = this.targetLatency) == null ? void 0 : _this$targetLatency.toFixed(3)}, forwardBufferLength=${this.forwardBufferLength.toFixed(3)}: adjusting playback rate from ${media.playbackRate} to ${playbackRate}`);
    media.playbackRate = playbackRate;
  }
  estimateLiveEdge() {
    const levelDetails = this.levelDetails;
    if (levelDetails === null) {
      return null;
    }
    return levelDetails.edge + levelDetails.age;
  }
  computeLatency() {
    const liveEdge = this.estimateLiveEdge();
    if (liveEdge === null) {
      return null;
    }
    return liveEdge - this.currentTime;
  }
};
var LevelController = class extends BasePlaylistController {
  constructor(hls, contentSteeringController) {
    super(hls, "level-controller");
    this._levels = [];
    this._firstLevel = -1;
    this._maxAutoLevel = -1;
    this._startLevel = void 0;
    this.currentLevel = null;
    this.currentLevelIndex = -1;
    this.manualLevelIndex = -1;
    this.steering = void 0;
    this.onParsedComplete = void 0;
    this.steering = contentSteeringController;
    this._registerListeners();
  }
  _registerListeners() {
    const {
      hls
    } = this;
    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
    hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
    hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
    hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
    hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
    hls.on(Events.ERROR, this.onError, this);
  }
  _unregisterListeners() {
    const {
      hls
    } = this;
    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
    hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this);
    hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
    hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
    hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
    hls.off(Events.ERROR, this.onError, this);
  }
  destroy() {
    this._unregisterListeners();
    this.steering = null;
    this.resetLevels();
    super.destroy();
  }
  stopLoad() {
    const levels = this._levels;
    levels.forEach((level) => {
      level.loadError = 0;
      level.fragmentError = 0;
    });
    super.stopLoad();
  }
  resetLevels() {
    this._startLevel = void 0;
    this.manualLevelIndex = -1;
    this.currentLevelIndex = -1;
    this.currentLevel = null;
    this._levels = [];
    this._maxAutoLevel = -1;
  }
  onManifestLoading(event, data) {
    this.resetLevels();
  }
  onManifestLoaded(event, data) {
    const preferManagedMediaSource = this.hls.config.preferManagedMediaSource;
    const levels = [];
    const redundantSet = {};
    const generatePathwaySet = {};
    let resolutionFound = false;
    let videoCodecFound = false;
    let audioCodecFound = false;
    data.levels.forEach((levelParsed) => {
      const attributes = levelParsed.attrs;
      let {
        audioCodec,
        videoCodec
      } = levelParsed;
      if (audioCodec) {
        levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource) || void 0;
      }
      if (videoCodec) {
        videoCodec = levelParsed.videoCodec = convertAVC1ToAVCOTI(videoCodec);
      }
      const {
        width,
        height,
        unknownCodecs
      } = levelParsed;
      let unknownUnsupportedCodecCount = unknownCodecs ? unknownCodecs.length : 0;
      if (unknownCodecs) {
        for (let i = unknownUnsupportedCodecCount; i--; ) {
          const unknownCodec = unknownCodecs[i];
          if (this.isAudioSupported(unknownCodec)) {
            levelParsed.audioCodec = audioCodec = audioCodec ? `${audioCodec},${unknownCodec}` : unknownCodec;
            unknownUnsupportedCodecCount--;
            sampleEntryCodesISO.audio[audioCodec.substring(0, 4)] = 2;
          } else if (this.isVideoSupported(unknownCodec)) {
            levelParsed.videoCodec = videoCodec = videoCodec ? `${videoCodec},${unknownCodec}` : unknownCodec;
            unknownUnsupportedCodecCount--;
            sampleEntryCodesISO.video[videoCodec.substring(0, 4)] = 2;
          }
        }
      }
      resolutionFound || (resolutionFound = !!(width && height));
      videoCodecFound || (videoCodecFound = !!videoCodec);
      audioCodecFound || (audioCodecFound = !!audioCodec);
      if (unknownUnsupportedCodecCount || audioCodec && !this.isAudioSupported(audioCodec) || videoCodec && !this.isVideoSupported(videoCodec)) {
        this.log(`Some or all CODECS not supported "${attributes.CODECS}"`);
        return;
      }
      const {
        CODECS,
        "FRAME-RATE": FRAMERATE,
        "HDCP-LEVEL": HDCP,
        "PATHWAY-ID": PATHWAY,
        RESOLUTION,
        "VIDEO-RANGE": VIDEO_RANGE
      } = attributes;
      const contentSteeringPrefix = `${PATHWAY || "."}-`;
      const levelKey = `${contentSteeringPrefix}${levelParsed.bitrate}-${RESOLUTION}-${FRAMERATE}-${CODECS}-${VIDEO_RANGE}-${HDCP}`;
      if (!redundantSet[levelKey]) {
        const level = this.createLevel(levelParsed);
        redundantSet[levelKey] = level;
        generatePathwaySet[levelKey] = 1;
        levels.push(level);
      } else if (redundantSet[levelKey].uri !== levelParsed.url && !levelParsed.attrs["PATHWAY-ID"]) {
        const pathwayCount = generatePathwaySet[levelKey] += 1;
        levelParsed.attrs["PATHWAY-ID"] = new Array(pathwayCount + 1).join(".");
        const level = this.createLevel(levelParsed);
        redundantSet[levelKey] = level;
        levels.push(level);
      } else {
        redundantSet[levelKey].addGroupId("audio", attributes.AUDIO);
        redundantSet[levelKey].addGroupId("text", attributes.SUBTITLES);
      }
    });
    this.filterAndSortMediaOptions(levels, data, resolutionFound, videoCodecFound, audioCodecFound);
  }
  createLevel(levelParsed) {
    const level = new Level(levelParsed);
    const supplemental = levelParsed.supplemental;
    if (supplemental != null && supplemental.videoCodec && !this.isVideoSupported(supplemental.videoCodec)) {
      const error = new Error(`SUPPLEMENTAL-CODECS not supported "${supplemental.videoCodec}"`);
      this.log(error.message);
      level.supportedResult = getUnsupportedResult(error, []);
    }
    return level;
  }
  isAudioSupported(codec) {
    return areCodecsMediaSourceSupported(codec, "audio", this.hls.config.preferManagedMediaSource);
  }
  isVideoSupported(codec) {
    return areCodecsMediaSourceSupported(codec, "video", this.hls.config.preferManagedMediaSource);
  }
  filterAndSortMediaOptions(filteredLevels, data, resolutionFound, videoCodecFound, audioCodecFound) {
    let audioTracks = [];
    let subtitleTracks = [];
    let levels = filteredLevels;
    if ((resolutionFound || videoCodecFound) && audioCodecFound) {
      levels = levels.filter(({
        videoCodec,
        videoRange,
        width,
        height
      }) => (!!videoCodec || !!(width && height)) && isVideoRange(videoRange));
    }
    if (levels.length === 0) {
      Promise.resolve().then(() => {
        if (this.hls) {
          let message = "no level with compatible codecs found in manifest";
          let reason = message;
          if (data.levels.length) {
            reason = `one or more CODECS in variant not supported: ${stringify(data.levels.map((level) => level.attrs.CODECS).filter((value, index, array) => array.indexOf(value) === index))}`;
            this.warn(reason);
            message += ` (${reason})`;
          }
          const error = new Error(message);
          this.hls.trigger(Events.ERROR, {
            type: ErrorTypes.MEDIA_ERROR,
            details: ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR,
            fatal: true,
            url: data.url,
            error,
            reason
          });
        }
      });
      return;
    }
    if (data.audioTracks) {
      audioTracks = data.audioTracks.filter((track) => !track.audioCodec || this.isAudioSupported(track.audioCodec));
      assignTrackIdsByGroup(audioTracks);
    }
    if (data.subtitles) {
      subtitleTracks = data.subtitles;
      assignTrackIdsByGroup(subtitleTracks);
    }
    const unsortedLevels = levels.slice(0);
    levels.sort((a, b) => {
      if (a.attrs["HDCP-LEVEL"] !== b.attrs["HDCP-LEVEL"]) {
        return (a.attrs["HDCP-LEVEL"] || "") > (b.attrs["HDCP-LEVEL"] || "") ? 1 : -1;
      }
      if (resolutionFound && a.height !== b.height) {
        return a.height - b.height;
      }
      if (a.frameRate !== b.frameRate) {
        return a.frameRate - b.frameRate;
      }
      if (a.videoRange !== b.videoRange) {
        return VideoRangeValues.indexOf(a.videoRange) - VideoRangeValues.indexOf(b.videoRange);
      }
      if (a.videoCodec !== b.videoCodec) {
        const valueA = videoCodecPreferenceValue(a.videoCodec);
        const valueB = videoCodecPreferenceValue(b.videoCodec);
        if (valueA !== valueB) {
          return valueB - valueA;
        }
      }
      if (a.uri === b.uri && a.codecSet !== b.codecSet) {
        const valueA = codecsSetSelectionPreferenceValue(a.codecSet);
        const valueB = codecsSetSelectionPreferenceValue(b.codecSet);
        if (valueA !== valueB) {
          return valueB - valueA;
        }
      }
      if (a.averageBitrate !== b.averageBitrate) {
        return a.averageBitrate - b.averageBitrate;
      }
      return 0;
    });
    let firstLevelInPlaylist = unsortedLevels[0];
    if (this.steering) {
      levels = this.steering.filterParsedLevels(levels);
      if (levels.length !== unsortedLevels.length) {
        for (let i = 0; i < unsortedLevels.length; i++) {
          if (unsortedLevels[i].pathwayId === levels[0].pathwayId) {
            firstLevelInPlaylist = unsortedLevels[i];
            break;
          }
        }
      }
    }
    this._levels = levels;
    for (let i = 0; i < levels.length; i++) {
      if (levels[i] === firstLevelInPlaylist) {
        var _this$hls$userConfig;
        this._firstLevel = i;
        const firstLevelBitrate = firstLevelInPlaylist.bitrate;
        const bandwidthEstimate = this.hls.bandwidthEstimate;
        this.log(`manifest loaded, ${levels.length} level(s) found, first bitrate: ${firstLevelBitrate}`);
        if (((_this$hls$userConfig = this.hls.userConfig) == null ? void 0 : _this$hls$userConfig.abrEwmaDefaultEstimate) === void 0) {
          const startingBwEstimate = Math.min(firstLevelBitrate, this.hls.config.abrEwmaDefaultEstimateMax);
          if (startingBwEstimate > bandwidthEstimate && bandwidthEstimate === this.hls.abrEwmaDefaultEstimate) {
            this.hls.bandwidthEstimate = startingBwEstimate;
          }
        }
        break;
      }
    }
    const audioOnly = audioCodecFound && !videoCodecFound;
    const config = this.hls.config;
    const altAudioEnabled = !!(config.audioStreamController && config.audioTrackController);
    const edata = {
      levels,
      audioTracks,
      subtitleTracks,
      sessionData: data.sessionData,
      sessionKeys: data.sessionKeys,
      firstLevel: this._firstLevel,
      stats: data.stats,
      audio: audioCodecFound,
      video: videoCodecFound,
      altAudio: altAudioEnabled && !audioOnly && audioTracks.some((t) => !!t.url)
    };
    this.hls.trigger(Events.MANIFEST_PARSED, edata);
  }
  get levels() {
    if (this._levels.length === 0) {
      return null;
    }
    return this._levels;
  }
  get loadLevelObj() {
    return this.currentLevel;
  }
  get level() {
    return this.currentLevelIndex;
  }
  set level(newLevel) {
    const levels = this._levels;
    if (levels.length === 0) {
      return;
    }
    if (newLevel < 0 || newLevel >= levels.length) {
      const error = new Error("invalid level idx");
      const fatal = newLevel < 0;
      this.hls.trigger(Events.ERROR, {
        type: ErrorTypes.OTHER_ERROR,
        details: ErrorDetails.LEVEL_SWITCH_ERROR,
        level: newLevel,
        fatal,
        error,
        reason: error.message
      });
      if (fatal) {
        return;
      }
      newLevel = Math.min(newLevel, levels.length - 1);
    }
    const lastLevelIndex = this.currentLevelIndex;
    const lastLevel = this.currentLevel;
    const lastPathwayId = lastLevel ? lastLevel.attrs["PATHWAY-ID"] : void 0;
    const level = levels[newLevel];
    const pathwayId = level.attrs["PATHWAY-ID"];
    this.currentLevelIndex = newLevel;
    this.currentLevel = level;
    if (lastLevelIndex === newLevel && lastLevel && lastPathwayId === pathwayId) {
      return;
    }
    this.log(`Switching to level ${newLevel} (${level.height ? level.height + "p " : ""}${level.videoRange ? level.videoRange + " " : ""}${level.codecSet ? level.codecSet + " " : ""}@${level.bitrate})${pathwayId ? " with Pathway " + pathwayId : ""} from level ${lastLevelIndex}${lastPathwayId ? " with Pathway " + lastPathwayId : ""}`);
    const levelSwitchingData = {
      level: newLevel,
      attrs: level.attrs,
      details: level.details,
      bitrate: level.bitrate,
      averageBitrate: level.averageBitrate,
      maxBitrate: level.maxBitrate,
      realBitrate: level.realBitrate,
      width: level.width,
      height: level.height,
      codecSet: level.codecSet,
      audioCodec: level.audioCodec,
      videoCodec: level.videoCodec,
      audioGroups: level.audioGroups,
      subtitleGroups: level.subtitleGroups,
      loaded: level.loaded,
      loadError: level.loadError,
      fragmentError: level.fragmentError,
      name: level.name,
      id: level.id,
      uri: level.uri,
      url: level.url,
      urlId: 0,
      audioGroupIds: level.audioGroupIds,
      textGroupIds: level.textGroupIds
    };
    this.hls.trigger(Events.LEVEL_SWITCHING, levelSwitchingData);
    const levelDetails = level.details;
    if (!levelDetails || levelDetails.live) {
      const hlsUrlParameters = this.switchParams(level.uri, lastLevel == null ? void 0 : lastLevel.details, levelDetails);
      this.loadPlaylist(hlsUrlParameters);
    }
  }
  get manualLevel() {
    return this.manualLevelIndex;
  }
  set manualLevel(newLevel) {
    this.manualLevelIndex = newLevel;
    if (this._startLevel === void 0) {
      this._startLevel = newLevel;
    }
    if (newLevel !== -1) {
      this.level = newLevel;
    }
  }
  get firstLevel() {
    return this._firstLevel;
  }
  set firstLevel(newLevel) {
    this._firstLevel = newLevel;
  }
  get startLevel() {
    if (this._startLevel === void 0) {
      const configStartLevel = this.hls.config.startLevel;
      if (configStartLevel !== void 0) {
        return configStartLevel;
      }
      return this.hls.firstAutoLevel;
    }
    return this._startLevel;
  }
  set startLevel(newLevel) {
    this._startLevel = newLevel;
  }
  get pathways() {
    if (this.steering) {
      return this.steering.pathways();
    }
    return [];
  }
  get pathwayPriority() {
    if (this.steering) {
      return this.steering.pathwayPriority;
    }
    return null;
  }
  set pathwayPriority(pathwayPriority) {
    if (this.steering) {
      const pathwaysList = this.steering.pathways();
      const filteredPathwayPriority = pathwayPriority.filter((pathwayId) => {
        return pathwaysList.indexOf(pathwayId) !== -1;
      });
      if (pathwayPriority.length < 1) {
        this.warn(`pathwayPriority ${pathwayPriority} should contain at least one pathway from list: ${pathwaysList}`);
        return;
      }
      this.steering.pathwayPriority = filteredPathwayPriority;
    }
  }
  onError(event, data) {
    if (data.fatal || !data.context) {
      return;
    }
    if (data.context.type === PlaylistContextType.LEVEL && data.context.level === this.level) {
      this.checkRetry(data);
    }
  }
  onFragBuffered(event, {
    frag
  }) {
    if (frag !== void 0 && frag.type === PlaylistLevelType.MAIN) {
      const el = frag.elementaryStreams;
      if (!Object.keys(el).some((type) => !!el[type])) {
        return;
      }
      const level = this._levels[frag.level];
      if (level != null && level.loadError) {
        this.log(`Resetting level error count of ${level.loadError} on frag buffered`);
        level.loadError = 0;
      }
    }
  }
  onLevelLoaded(event, data) {
    var _data$deliveryDirecti2;
    const {
      level,
      details
    } = data;
    const curLevel = data.levelInfo;
    if (!curLevel) {
      var _data$deliveryDirecti;
      this.warn(`Invalid level index ${level}`);
      if ((_data$deliveryDirecti = data.deliveryDirectives) != null && _data$deliveryDirecti.skip) {
        details.deltaUpdateFailed = true;
      }
      return;
    }
    if (curLevel === this.currentLevel || data.withoutMultiVariant) {
      if (curLevel.fragmentError === 0) {
        curLevel.loadError = 0;
      }
      let previousDetails = curLevel.details;
      if (previousDetails === data.details && previousDetails.advanced) {
        previousDetails = void 0;
      }
      this.playlistLoaded(level, data, previousDetails);
    } else if ((_data$deliveryDirecti2 = data.deliveryDirectives) != null && _data$deliveryDirecti2.skip) {
      details.deltaUpdateFailed = true;
    }
  }
  loadPlaylist(hlsUrlParameters) {
    super.loadPlaylist();
    if (this.shouldLoadPlaylist(this.currentLevel)) {
      this.scheduleLoading(this.currentLevel, hlsUrlParameters);
    }
  }
  loadingPlaylist(currentLevel, hlsUrlParameters) {
    super.loadingPlaylist(currentLevel, hlsUrlParameters);
    const url = this.getUrlWithDirectives(currentLevel.uri, hlsUrlParameters);
    const currentLevelIndex = this.currentLevelIndex;
    const pathwayId = currentLevel.attrs["PATHWAY-ID"];
    const details = currentLevel.details;
    const age = details == null ? void 0 : details.age;
    this.log(`Loading level index ${currentLevelIndex}${(hlsUrlParameters == null ? void 0 : hlsUrlParameters.msn) !== void 0 ? " at sn " + hlsUrlParameters.msn + " part " + hlsUrlParameters.part : ""}${pathwayId ? " Pathway " + pathwayId : ""}${age && details.live ? " age " + age.toFixed(1) + (details.type ? " " + details.type || "" : "") : ""} ${url}`);
    this.hls.trigger(Events.LEVEL_LOADING, {
      url,
      level: currentLevelIndex,
      levelInfo: currentLevel,
      pathwayId: currentLevel.attrs["PATHWAY-ID"],
      id: 0,
      deliveryDirectives: hlsUrlParameters || null
    });
  }
  get nextLoadLevel() {
    if (this.manualLevelIndex !== -1) {
      return this.manualLevelIndex;
    } else {
      return this.hls.nextAutoLevel;
    }
  }
  set nextLoadLevel(nextLevel) {
    this.level = nextLevel;
    if (this.manualLevelIndex === -1) {
      this.hls.nextAutoLevel = nextLevel;
    }
  }
  removeLevel(levelIndex) {
    var _this$currentLevel;
    if (this._levels.length === 1) {
      return;
    }
    const levels = this._levels.filter((level, index) => {
      if (index !== levelIndex) {
        return true;
      }
      if (this.steering) {
        this.steering.removeLevel(level);
      }
      if (level === this.currentLevel) {
        this.currentLevel = null;
        this.currentLevelIndex = -1;
        if (level.details) {
          level.details.fragments.forEach((f) => f.level = -1);
        }
      }
      return false;
    });
    reassignFragmentLevelIndexes(levels);
    this._levels = levels;
    if (this.currentLevelIndex > -1 && (_this$currentLevel = this.currentLevel) != null && _this$currentLevel.details) {
      this.currentLevelIndex = this.currentLevel.details.fragments[0].level;
    }
    if (this.manualLevelIndex > -1) {
      this.manualLevelIndex = this.currentLevelIndex;
    }
    const maxLevel = levels.length - 1;
    this._firstLevel = Math.min(this._firstLevel, maxLevel);
    if (this._startLevel) {
      this._startLevel = Math.min(this._startLevel, maxLevel);
    }
    this.hls.trigger(Events.LEVELS_UPDATED, {
      levels
    });
  }
  onLevelsUpdated(event, {
    levels
  }) {
    this._levels = levels;
  }
  checkMaxAutoUpdated() {
    const {
      autoLevelCapping,
      maxAutoLevel,
      maxHdcpLevel
    } = this.hls;
    if (this._maxAutoLevel !== maxAutoLevel) {
      this._maxAutoLevel = maxAutoLevel;
      this.hls.trigger(Events.MAX_AUTO_LEVEL_UPDATED, {
        autoLevelCapping,
        levels: this.levels,
        maxAutoLevel,
        minAutoLevel: this.hls.minAutoLevel,
        maxHdcpLevel
      });
    }
  }
};
function assignTrackIdsByGroup(tracks) {
  const groups = {};
  tracks.forEach((track) => {
    const groupId = track.groupId || "";
    track.id = groups[groupId] = groups[groupId] || 0;
    groups[groupId]++;
  });
}
function getSourceBuffer() {
  return self.SourceBuffer || self.WebKitSourceBuffer;
}
function isMSESupported() {
  const mediaSource = getMediaSource();
  if (!mediaSource) {
    return false;
  }
  const sourceBuffer = getSourceBuffer();
  return !sourceBuffer || sourceBuffer.prototype && typeof sourceBuffer.prototype.appendBuffer === "function" && typeof sourceBuffer.prototype.remove === "function";
}
function isSupported() {
  if (!isMSESupported()) {
    return false;
  }
  const mediaSource = getMediaSource();
  return typeof (mediaSource == null ? void 0 : mediaSource.isTypeSupported) === "function" && (["avc1.42E01E,mp4a.40.2", "av01.0.01M.08", "vp09.00.50.08"].some((codecsForVideoContainer) => mediaSource.isTypeSupported(mimeTypeForCodec(codecsForVideoContainer, "video"))) || ["mp4a.40.2", "fLaC"].some((codecForAudioContainer) => mediaSource.isTypeSupported(mimeTypeForCodec(codecForAudioContainer, "audio"))));
}
function changeTypeSupported() {
  var _sourceBuffer$prototy;
  const sourceBuffer = getSourceBuffer();
  return typeof (sourceBuffer == null ? void 0 : (_sourceBuffer$prototy = sourceBuffer.prototype) == null ? void 0 : _sourceBuffer$prototy.changeType) === "function";
}
var TICK_INTERVAL = 100;
var StreamController = class extends BaseStreamController {
  constructor(hls, fragmentTracker, keyLoader) {
    super(hls, fragmentTracker, keyLoader, "stream-controller", PlaylistLevelType.MAIN);
    this.audioCodecSwap = false;
    this.level = -1;
    this._forceStartLoad = false;
    this._hasEnoughToStart = false;
    this.altAudio = 0;
    this.audioOnly = false;
    this.fragPlaying = null;
    this.fragLastKbps = 0;
    this.couldBacktrack = false;
    this.backtrackFragment = null;
    this.audioCodecSwitch = false;
    this.videoBuffer = null;
    this.onMediaPlaying = () => {
      this.tick();
    };
    this.onMediaSeeked = () => {
      const media = this.media;
      const currentTime = media ? media.currentTime : null;
      if (currentTime === null || !isFiniteNumber(currentTime)) {
        return;
      }
      this.log(`Media seeked to ${currentTime.toFixed(3)}`);
      if (!this.getBufferedFrag(currentTime)) {
        return;
      }
      const bufferInfo = this.getFwdBufferInfoAtPos(media, currentTime, PlaylistLevelType.MAIN, 0);
      if (bufferInfo === null || bufferInfo.len === 0) {
        this.warn(`Main forward buffer length at ${currentTime} on "seeked" event ${bufferInfo ? bufferInfo.len : "empty"})`);
        return;
      }
      this.tick();
    };
    this.registerListeners();
  }
  registerListeners() {
    super.registerListeners();
    const {
      hls
    } = this;
    hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this);
    hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
    hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this);
    hls.on(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
    hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
    hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
    hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this);
    hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);
    hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
    hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this);
  }
  unregisterListeners() {
    super.unregisterListeners();
    const {
      hls
    } = this;
    hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this);
    hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this);
    hls.off(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this);
    hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this);
    hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this);
    hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this);
    hls.off(Events.BUFFER_FLUSHED, this.onBufferFlushed, this);
    hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
    hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this);
  }
  onHandlerDestroying() {
    this.onMediaPlaying = this.onMediaSeeked = null;
    this.unregisterListeners();
    super.onHandlerDestroying();
  }
  startLoad(startPosition, skipSeekToStartPosition) {
    if (this.levels) {
      const {
        lastCurrentTime,
        hls
      } = this;
      this.stopLoad();
      this.setInterval(TICK_INTERVAL);
      this.level = -1;
      if (!this.startFragRequested) {
        let startLevel = hls.startLevel;
        if (startLevel === -1) {
          if (hls.config.testBandwidth && this.levels.length > 1) {
            startLevel = 0;
            this.bitrateTest = true;
          } else {
            startLevel = hls.firstAutoLevel;
          }
        }
        hls.nextLoadLevel = startLevel;
        this.level = hls.loadLevel;
        this._hasEnoughToStart = !!skipSeekToStartPosition;
      }
      if (lastCurrentTime > 0 && startPosition === -1 && !skipSeekToStartPosition) {
        this.log(`Override startPosition with lastCurrentTime @${lastCurrentTime.toFixed(3)}`);
        startPosition = lastCurrentTime;
      }
      this.state = State.IDLE;
      this.nextLoadPosition = this.lastCurrentTime = startPosition + this.timelineOffset;
      this.startPosition = skipSeekToStartPosition ? -1 : startPosition;
      this.tick();
    } else {
      this._forceStartLoad = true;
      this.state = State.STOPPED;
    }
  }
  stopLoad() {
    this._forceStartLoad = false;
    super.stopLoad();
  }
  doTick() {
    switch (this.state) {
      case State.WAITING_LEVEL: {
        const {
          levels,
          level
        } = this;
        const currentLevel = levels == null ? void 0 : levels[level];
        const details = currentLevel == null ? void 0 : currentLevel.details;
        if (details && (!details.live || this.levelLastLoaded === currentLevel && !this.waitForLive(currentLevel))) {
          if (this.waitForCdnTuneIn(details)) {
            break;
          }
          this.state = State.IDLE;
          break;
        } else if (this.hls.nextLoadLevel !== this.level) {
          this.state = State.IDLE;
          break;
        }
        break;
      }
      case State.FRAG_LOADING_WAITING_RETRY:
        {
          var _this$media;
          const now2 = self.performance.now();
          const retryDate = this.retryDate;
          if (!retryDate || now2 >= retryDate || (_this$media = this.media) != null && _this$media.seeking) {
            const {
              levels,
              level
            } = this;
            const currentLevel = levels == null ? void 0 : levels[level];
            this.resetStartWhenNotLoaded(currentLevel || null);
            this.state = State.IDLE;
          }
        }
        break;
    }
    if (this.state === State.IDLE) {
      this.doTickIdle();
    }
    this.onTickEnd();
  }
  onTickEnd() {
    var _this$media2;
    super.onTickEnd();
    if ((_this$media2 = this.media) != null && _this$media2.readyState && this.media.seeking === false) {
      this.lastCurrentTime = this.media.currentTime;
    }
    this.checkFragmentChanged();
  }
  doTickIdle() {
    const {
      hls,
      levelLastLoaded,
      levels,
      media
    } = this;
    if (levelLastLoaded === null || !media && !this.primaryPrefetch && (this.startFragRequested || !hls.config.startFragPrefetch)) {
      return;
    }
    if (this.altAudio && this.audioOnly) {
      return;
    }
    const level = this.buffering ? hls.nextLoadLevel : hls.loadLevel;
    if (!(levels != null && levels[level])) {
      return;
    }
    const levelInfo = levels[level];
    const bufferInfo = this.getMainFwdBufferInfo();
    if (bufferInfo === null) {
      return;
    }
    const lastDetails = this.getLevelDetails();
    if (lastDetails && this._streamEnded(bufferInfo, lastDetails)) {
      const data = {};
      if (this.altAudio === 2) {
        data.type = "video";
      }
      this.hls.trigger(Events.BUFFER_EOS, data);
      this.state = State.ENDED;
      return;
    }
    if (!this.buffering) {
      return;
    }
    if (hls.loadLevel !== level && hls.manualLevel === -1) {
      this.log(`Adapting to level ${level} from level ${this.level}`);
    }
    this.level = hls.nextLoadLevel = level;
    const levelDetails = levelInfo.details;
    if (!levelDetails || this.state === State.WAITING_LEVEL || this.waitForLive(levelInfo)) {
      this.level = level;
      this.state = State.WAITING_LEVEL;
      this.startFragRequested = false;
      return;
    }
    const bufferLen = bufferInfo.len;
    const maxBufLen = this.getMaxBufferLength(levelInfo.maxBitrate);
    if (bufferLen >= maxBufLen) {
      return;
    }
    if (this.backtrackFragment && this.backtrackFragment.start > bufferInfo.end) {
      this.backtrackFragment = null;
    }
    const targetBufferTime = this.backtrackFragment ? this.backtrackFragment.start : bufferInfo.end;
    let frag = this.getNextFragment(targetBufferTime, levelDetails);
    if (this.couldBacktrack && !this.fragPrevious && frag && isMediaFragment(frag) && this.fragmentTracker.getState(frag) !== FragmentState.OK) {
      var _this$backtrackFragme;
      const backtrackSn = ((_this$backtrackFragme = this.backtrackFragment) != null ? _this$backtrackFragme : frag).sn;
      const fragIdx = backtrackSn - levelDetails.startSN;
      const backtrackFrag = levelDetails.fragments[fragIdx - 1];
      if (backtrackFrag && frag.cc === backtrackFrag.cc) {
        frag = backtrackFrag;
        this.fragmentTracker.removeFragment(backtrackFrag);
      }
    } else if (this.backtrackFragment && bufferInfo.len) {
      this.backtrackFragment = null;
    }
    if (frag && this.isLoopLoading(frag, targetBufferTime)) {
      const gapStart = frag.gap;
      if (!gapStart) {
        const type = this.audioOnly && !this.altAudio ? ElementaryStreamTypes.AUDIO : ElementaryStreamTypes.VIDEO;
        const mediaBuffer = (type === ElementaryStreamTypes.VIDEO ? this.videoBuffer : this.mediaBuffer) || this.media;
        if (mediaBuffer) {
          this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.MAIN);
        }
      }
      frag = this.getNextFragmentLoopLoading(frag, levelDetails, bufferInfo, PlaylistLevelType.MAIN, maxBufLen);
    }
    if (!frag) {
      return;
    }
    if (frag.initSegment && !frag.initSegment.data && !this.bitrateTest) {
      frag = frag.initSegment;
    }
    this.loadFragment(frag, levelInfo, targetBufferTime);
  }
  loadFragment(frag, level, targetBufferTime) {
    const fragState = this.fragmentTracker.getState(frag);
    if (fragState === FragmentState.NOT_LOADED || fragState === FragmentState.PARTIAL) {
      if (!isMediaFragment(frag)) {
        this._loadInitSegment(frag, level);
      } else if (this.bitrateTest) {
        this.log(`Fragment ${frag.sn} of level ${frag.level} is being downloaded to test bitrate and will not be buffered`);
        this._loadBitrateTestFrag(frag, level);
      } else {
        super.loadFragment(frag, level, targetBufferTime);
      }
    } else {
      this.clearTrackerIfNeeded(frag);
    }
  }
  getBufferedFrag(position) {
    return this.fragmentTracker.getBufferedFrag(position, PlaylistLevelType.MAIN);
  }
  followingBufferedFrag(frag) {
    if (frag) {
      return this.getBufferedFrag(frag.end + 0.5);
    }
    return null;
  }
  immediateLevelSwitch() {
    this.abortCurrentFrag();
    this.flushMainBuffer(0, Number.POSITIVE_INFINITY);
  }
  nextLevelSwitch() {
    const {
      levels,
      media
    } = this;
    if (media != null && media.readyState) {
      let fetchdelay;
      const fragPlayingCurrent = this.getAppendedFrag(media.currentTime);
      if (fragPlayingCurrent && fragPlayingCurrent.start > 1) {
        this.flushMainBuffer(0, fragPlayingCurrent.start - 1);
      }
      const levelDetails = this.getLevelDetails();
      if (levelDetails != null && levelDetails.live) {
        const bufferInfo = this.getMainFwdBufferInfo();
        if (!bufferInfo || bufferInfo.len < levelDetails.targetduration * 2) {
          return;
        }
      }
      if (!media.paused && levels) {
        const nextLevelId = this.hls.nextLoadLevel;
        const nextLevel = levels[nextLevelId];
        const fragLastKbps = this.fragLastKbps;
        if (fragLastKbps && this.fragCurrent) {
          fetchdelay = this.fragCurrent.duration * nextLevel.maxBitrate / (1e3 * fragLastKbps) + 1;
        } else {
          fetchdelay = 0;
        }
      } else {
        fetchdelay = 0;
      }
      const bufferedFrag = this.getBufferedFrag(media.currentTime + fetchdelay);
      if (bufferedFrag) {
        const nextBufferedFrag = this.followingBufferedFrag(bufferedFrag);
        if (nextBufferedFrag) {
          this.abortCurrentFrag();
          const maxStart = nextBufferedFrag.maxStartPTS ? nextBufferedFrag.maxStartPTS : nextBufferedFrag.start;
          const fragDuration = nextBufferedFrag.duration;
          const startPts = Math.max(bufferedFrag.end, maxStart + Math.min(Math.max(fragDuration - this.config.maxFragLookUpTolerance, fragDuration * (this.couldBacktrack ? 0.5 : 0.125)), fragDuration * (this.couldBacktrack ? 0.75 : 0.25)));
          this.flushMainBuffer(startPts, Number.POSITIVE_INFINITY);
        }
      }
    }
  }
  abortCurrentFrag() {
    const fragCurrent = this.fragCurrent;
    this.fragCurrent = null;
    this.backtrackFragment = null;
    if (fragCurrent) {
      fragCurrent.abortRequests();
      this.fragmentTracker.removeFragment(fragCurrent);
    }
    switch (this.state) {
      case State.KEY_LOADING:
      case State.FRAG_LOADING:
      case State.FRAG_LOADING_WAITING_RETRY:
      case State.PARSING:
      case State.PARSED:
        this.state = State.IDLE;
        break;
    }
    this.nextLoadPosition = this.getLoadPosition();
  }
  flushMainBuffer(startOffset, endOffset) {
    super.flushMainBuffer(startOffset, endOffset, this.altAudio === 2 ? "video" : null);
  }
  onMediaAttached(event, data) {
    super.onMediaAttached(event, data);
    const media = data.media;
    addEventListener(media, "playing", this.onMediaPlaying);
    addEventListener(media, "seeked", this.onMediaSeeked);
  }
  onMediaDetaching(event, data) {
    const {
      media
    } = this;
    if (media) {
      removeEventListener(media, "playing", this.onMediaPlaying);
      removeEventListener(media, "seeked", this.onMediaSeeked);
    }
    this.videoBuffer = null;
    this.fragPlaying = null;
    super.onMediaDetaching(event, data);
    const transferringMedia = !!data.transferMedia;
    if (transferringMedia) {
      return;
    }
    this._hasEnoughToStart = false;
  }
  onManifestLoading() {
    super.onManifestLoading();
    this.log("Trigger BUFFER_RESET");
    this.hls.trigger(Events.BUFFER_RESET, void 0);
    this.couldBacktrack = false;
    this.fragLastKbps = 0;
    this.fragPlaying = this.backtrackFragment = null;
    this.altAudio = 0;
    this.audioOnly = false;
  }
  onManifestParsed(event, data) {
    let aac = false;
    let heaac = false;
    data.levels.forEach((level) => {
      const codec = level.audioCodec;
      if (codec) {
        aac = aac || codec.indexOf("mp4a.40.2") !== -1;
        heaac = heaac || codec.indexOf("mp4a.40.5") !== -1;
      }
    });
    this.audioCodecSwitch = aac && heaac && !changeTypeSupported();
    if (this.audioCodecSwitch) {
      this.log("Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC");
    }
    this.levels = data.levels;
    this.startFragRequested = false;
  }
  onLevelLoading(event, data) {
    const {
      levels
    } = this;
    if (!levels || this.state !== State.IDLE) {
      return;
    }
    const level = data.levelInfo;
    if (!level.details || level.details.live && (this.levelLastLoaded !== level || level.details.expired) || this.waitForCdnTuneIn(level.details)) {
      this.state = State.WAITING_LEVEL;
    }
  }
  onLevelLoaded(event, data) {
    var _curLevel$details;
    const {
      levels,
      startFragRequested
    } = this;
    const newLevelId = data.level;
    const newDetails = data.details;
    const duration = newDetails.totalduration;
    if (!levels) {
      this.warn(`Levels were reset while loading level ${newLevelId}`);
      return;
    }
    this.log(`Level ${newLevelId} loaded [${newDetails.startSN},${newDetails.endSN}]${newDetails.lastPartSn ? `[part-${newDetails.lastPartSn}-${newDetails.lastPartIndex}]` : ""}, cc [${newDetails.startCC}, ${newDetails.endCC}] duration:${duration}`);
    const curLevel = data.levelInfo;
    const fragCurrent = this.fragCurrent;
    if (fragCurrent && (this.state === State.FRAG_LOADING || this.state === State.FRAG_LOADING_WAITING_RETRY)) {
      if (fragCurrent.level !== data.level && fragCurrent.loader) {
        this.abortCurrentFrag();
      }
    }
    let sliding = 0;
    if (newDetails.live || (_curLevel$details = curLevel.details) != null && _curLevel$details.live) {
      var _this$levelLastLoaded;
      this.checkLiveUpdate(newDetails);
      if (newDetails.deltaUpdateFailed) {
        return;
      }
      sliding = this.alignPlaylists(newDetails, curLevel.details, (_this$levelLastLoaded = this.levelLastLoaded) == null ? void 0 : _this$levelLastLoaded.details);
    }
    curLevel.details = newDetails;
    this.levelLastLoaded = curLevel;
    if (!startFragRequested) {
      this.setStartPosition(newDetails, sliding);
    }
    this.hls.trigger(Events.LEVEL_UPDATED, {
      details: newDetails,
      level: newLevelId
    });
    if (this.state === State.WAITING_LEVEL) {
      if (this.waitForCdnTuneIn(newDetails)) {
        return;
      }
      this.state = State.IDLE;
    }
    if (startFragRequested && newDetails.live) {
      this.synchronizeToLiveEdge(newDetails);
    }
    this.tick();
  }
  synchronizeToLiveEdge(levelDetails) {
    const {
      config,
      media
    } = this;
    if (!media) {
      return;
    }
    const liveSyncPosition = this.hls.liveSyncPosition;
    const currentTime = this.getLoadPosition();
    const start = levelDetails.fragmentStart;
    const end = levelDetails.edge;
    const withinSlidingWindow = currentTime >= start - config.maxFragLookUpTolerance && currentTime <= end;
    if (liveSyncPosition !== null && media.duration > liveSyncPosition && (currentTime < liveSyncPosition || !withinSlidingWindow)) {
      const maxLatency = config.liveMaxLatencyDuration !== void 0 ? config.liveMaxLatencyDuration : config.liveMaxLatencyDurationCount * levelDetails.targetduration;
      if (!withinSlidingWindow && media.readyState < 4 || currentTime < end - maxLatency) {
        if (!this._hasEnoughToStart) {
          this.nextLoadPosition = liveSyncPosition;
        }
        if (media.readyState) {
          this.warn(`Playback: ${currentTime.toFixed(3)} is located too far from the end of live sliding playlist: ${end}, reset currentTime to : ${liveSyncPosition.toFixed(3)}`);
          if (this.config.liveSyncMode === "buffered") {
            var _bufferInfo$buffered;
            const bufferInfo = BufferHelper.bufferInfo(media, liveSyncPosition, 0);
            if (!(bufferInfo != null && (_bufferInfo$buffered = bufferInfo.buffered) != null && _bufferInfo$buffered.length)) {
              media.currentTime = liveSyncPosition;
              return;
            }
            const isLiveSyncInBuffer = bufferInfo.start <= currentTime;
            if (isLiveSyncInBuffer) {
              media.currentTime = liveSyncPosition;
              return;
            }
            const {
              nextStart
            } = BufferHelper.bufferedInfo(bufferInfo.buffered, currentTime, 0);
            if (nextStart) {
              media.currentTime = nextStart;
            }
          } else {
            media.currentTime = liveSyncPosition;
          }
        }
      }
    }
  }
  _handleFragmentLoadProgress(data) {
    var _frag$initSegment;
    const frag = data.frag;
    const {
      part,
      payload
    } = data;
    const {
      levels
    } = this;
    if (!levels) {
      this.warn(`Levels were reset while fragment load was in progress. Fragment ${frag.sn} of level ${frag.level} will not be buffered`);
      return;
    }
    const currentLevel = levels[frag.level];
    if (!currentLevel) {
      this.warn(`Level ${frag.level} not found on progress`);
      return;
    }
    const details = currentLevel.details;
    if (!details) {
      this.warn(`Dropping fragment ${frag.sn} of level ${frag.level} after level details were reset`);
      this.fragmentTracker.removeFragment(frag);
      return;
    }
    const videoCodec = currentLevel.videoCodec;
    const accurateTimeOffset = details.PTSKnown || !details.live;
    const initSegmentData = (_frag$initSegment = frag.initSegment) == null ? void 0 : _frag$initSegment.data;
    const audioCodec = this._getAudioCodec(currentLevel);
    const transmuxer = this.transmuxer = this.transmuxer || new TransmuxerInterface(this.hls, PlaylistLevelType.MAIN, this._handleTransmuxComplete.bind(this), this._handleTransmuxerFlush.bind(this));
    const partIndex = part ? part.index : -1;
    const partial = partIndex !== -1;
    const chunkMeta = new ChunkMetadata(frag.level, frag.sn, frag.stats.chunkCount, payload.byteLength, partIndex, partial);
    const initPTS = this.initPTS[frag.cc];
    transmuxer.push(payload, initSegmentData, audioCodec, videoCodec, frag, part, details.totalduration, accurateTimeOffset, chunkMeta, initPTS);
  }
  onAudioTrackSwitching(event, data) {
    const hls = this.hls;
    const fromAltAudio = this.altAudio === 2;
    const altAudio = useAlternateAudio(data.url, hls);
    if (!altAudio) {
      if (this.mediaBuffer !== this.media) {
        this.log("Switching on main audio, use media.buffered to schedule main fragment loading");
        this.mediaBuffer = this.media;
        const fragCurrent = this.fragCurrent;
        if (fragCurrent) {
          this.log("Switching to main audio track, cancel main fragment load");
          fragCurrent.abortRequests();
          this.fragmentTracker.removeFragment(fragCurrent);
        }
        this.resetTransmuxer();
        this.resetLoadingState();
      } else if (this.audioOnly) {
        this.resetTransmuxer();
      }
      if (fromAltAudio) {
        this.fragmentTracker.removeAllFragments();
        hls.once(Events.BUFFER_FLUSHED, () => {
          var _this$hls;
          (_this$hls = this.hls) == null ? void 0 : _this$hls.trigger(Events.AUDIO_TRACK_SWITCHED, data);
        });
        hls.trigger(Events.BUFFER_FLUSHING, {
          startOffset: 0,
          endOffset: Number.POSITIVE_INFINITY,
          type: null
        });
        return;
      }
      hls.trigger(Events.AUDIO_TRACK_SWITCHED, data);
    } else {
      this.altAudio = 1;
    }
  }
  onAudioTrackSwitched(event, data) {
    const altAudio = useAlternateAudio(data.url, this.hls);
    if (altAudio) {
      const videoBuffer = this.videoBuffer;
      if (videoBuffer && this.mediaBuffer !== videoBuffer) {
        this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading");
        this.mediaBuffer = videoBuffer;
      }
    }
    this.altAudio = altAudio ? 2 : 0;
    this.tick();
  }
  onBufferCreated(event, data) {
    const tracks = data.tracks;
    let mediaTrack;
    let name;
    let alternate = false;
    for (const type in tracks) {
      const track = tracks[type];
      if (track.id === "main") {
        name = type;
        mediaTrack = track;
        if (type === "video") {
          const videoTrack = tracks[type];
          if (videoTrack) {
            this.videoBuffer = videoTrack.buffer;
          }
        }
      } else {
        alternate = true;
      }
    }
    if (alternate && mediaTrack) {
      this.log(`Alternate track found, use ${name}.buffered to schedule main fragment loading`);
      this.mediaBuffer = mediaTrack.buffer;
    } else {
      this.mediaBuffer = this.media;
    }
  }
  onFragBuffered(event, data) {
    const {
      frag,
      part
    } = data;
    const bufferedMainFragment = frag.type === PlaylistLevelType.MAIN;
    if (bufferedMainFragment) {
      if (this.fragContextChanged(frag)) {
        this.warn(`Fragment ${frag.sn}${part ? " p: " + part.index : ""} of level ${frag.level} finished buffering, but was aborted. state: ${this.state}`);
        if (this.state === State.PARSED) {
          this.state = State.IDLE;
        }
        return;
      }
      const stats = part ? part.stats : frag.stats;
      this.fragLastKbps = Math.round(8 * stats.total / (stats.buffering.end - stats.loading.first));
      if (isMediaFragment(frag)) {
        this.fragPrevious = frag;
      }
      this.fragBufferedComplete(frag, part);
    }
    const media = this.media;
    if (!media) {
      return;
    }
    if (!this._hasEnoughToStart && BufferHelper.getBuffered(media).length) {
      this._hasEnoughToStart = true;
      this.seekToStartPos();
    }
    if (bufferedMainFragment) {
      this.tick();
    }
  }
  get hasEnoughToStart() {
    return this._hasEnoughToStart;
  }
  onError(event, data) {
    var _data$context;
    if (data.fatal) {
      this.state = State.ERROR;
      return;
    }
    switch (data.details) {
      case ErrorDetails.FRAG_GAP:
      case ErrorDetails.FRAG_PARSING_ERROR:
      case ErrorDetails.FRAG_DECRYPT_ERROR:
      case ErrorDetails.FRAG_LOAD_ERROR:
      case ErrorDetails.FRAG_LOAD_TIMEOUT:
      case ErrorDetails.KEY_LOAD_ERROR:
      case ErrorDetails.KEY_LOAD_TIMEOUT:
        this.onFragmentOrKeyLoadError(PlaylistLevelType.MAIN, data);
        break;
      case ErrorDetails.LEVEL_LOAD_ERROR:
      case ErrorDetails.LEVEL_LOAD_TIMEOUT:
      case ErrorDetails.LEVEL_PARSING_ERROR:
        if (!data.levelRetry && this.state === State.WAITING_LEVEL && ((_data$context = data.context) == null ? void 0 : _data$context.type) === PlaylistContextType.LEVEL) {
          this.state = State.IDLE;
        }
        break;
      case ErrorDetails.BUFFER_ADD_CODEC_ERROR:
      case ErrorDetails.BUFFER_APPEND_ERROR:
        if (data.parent !== "main") {
          return;
        }
        if (this.reduceLengthAndFlushBuffer(data)) {
          this.resetLoadingState();
        }
        break;
      case ErrorDetails.BUFFER_FULL_ERROR:
        if (data.parent !== "main") {
          return;
        }
        if (this.reduceLengthAndFlushBuffer(data)) {
          this.flushMainBuffer(0, Number.POSITIVE_INFINITY);
        }
        break;
      case ErrorDetails.INTERNAL_EXCEPTION:
        this.recoverWorkerError(data);
        break;
    }
  }
  onFragLoadEmergencyAborted() {
    this.state = State.IDLE;
    if (!this._hasEnoughToStart) {
      this.startFragRequested = false;
      this.nextLoadPosition = this.lastCurrentTime;
    }
    this.tickImmediate();
  }
  onBufferFlushed(event, {
    type
  }) {
    if (type !== ElementaryStreamTypes.AUDIO || !this.altAudio) {
      const mediaBuffer = (type === ElementaryStreamTypes.VIDEO ? this.videoBuffer : this.mediaBuffer) || this.media;
      if (mediaBuffer) {
        this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.MAIN);
        this.tick();
      }
    }
  }
  onLevelsUpdated(event, data) {
    if (this.level > -1 && this.fragCurrent) {
      this.level = this.fragCurrent.level;
      if (this.level === -1) {
        this.resetWhenMissingContext(this.fragCurrent);
      }
    }
    this.levels = data.levels;
  }
  swapAudioCodec() {
    this.audioCodecSwap = !this.audioCodecSwap;
  }
  seekToStartPos() {
    const {
      media
    } = this;
    if (!media) {
      return;
    }
    const currentTime = media.currentTime;
    let startPosition = this.startPosition;
    if (startPosition >= 0 && currentTime < startPosition) {
      if (media.seeking) {
        this.log(`could not seek to ${startPosition}, already seeking at ${currentTime}`);
        return;
      }
      const timelineOffset = this.timelineOffset;
      if (timelineOffset && startPosition) {
        startPosition += timelineOffset;
      }
      const details = this.getLevelDetails();
      const buffered = BufferHelper.getBuffered(media);
      const bufferStart = buffered.length ? buffered.start(0) : 0;
      const delta = bufferStart - startPosition;
      const skipTolerance = Math.max(this.config.maxBufferHole, this.config.maxFragLookUpTolerance);
      if (this.config.startOnSegmentBoundary || delta > 0 && (delta < skipTolerance || this.loadingParts && delta < 2 * ((details == null ? void 0 : details.partTarget) || 0))) {
        this.log(`adjusting start position by ${delta} to match buffer start`);
        startPosition += delta;
        this.startPosition = startPosition;
      }
      if (currentTime < startPosition) {
        this.log(`seek to target start position ${startPosition} from current time ${currentTime} buffer start ${bufferStart}`);
        media.currentTime = startPosition;
      }
    }
  }
  _getAudioCodec(currentLevel) {
    let audioCodec = this.config.defaultAudioCodec || currentLevel.audioCodec;
    if (this.audioCodecSwap && audioCodec) {
      this.log("Swapping audio codec");
      if (audioCodec.indexOf("mp4a.40.5") !== -1) {
        audioCodec = "mp4a.40.2";
      } else {
        audioCodec = "mp4a.40.5";
      }
    }
    return audioCodec;
  }
  _loadBitrateTestFrag(fragment, level) {
    fragment.bitrateTest = true;
    this._doFragLoad(fragment, level).then((data) => {
      const {
        hls
      } = this;
      const frag = data == null ? void 0 : data.frag;
      if (!frag || this.fragContextChanged(frag)) {
        return;
      }
      level.fragmentError = 0;
      this.state = State.IDLE;
      this.startFragRequested = false;
      this.bitrateTest = false;
      const stats = frag.stats;
      stats.parsing.start = stats.parsing.end = stats.buffering.start = stats.buffering.end = self.performance.now();
      hls.trigger(Events.FRAG_LOADED, data);
      frag.bitrateTest = false;
    });
  }
  _handleTransmuxComplete(transmuxResult) {
    var _id3$samples;
    const id = this.playlistType;
    const {
      hls
    } = this;
    const {
      remuxResult,
      chunkMeta
    } = transmuxResult;
    const context = this.getCurrentContext(chunkMeta);
    if (!context) {
      this.resetWhenMissingContext(chunkMeta);
      return;
    }
    const {
      frag,
      part,
      level
    } = context;
    const {
      video,
      text,
      id3,
      initSegment
    } = remuxResult;
    const {
      details
    } = level;
    const audio = this.altAudio ? void 0 : remuxResult.audio;
    if (this.fragContextChanged(frag)) {
      this.fragmentTracker.removeFragment(frag);
      return;
    }
    this.state = State.PARSING;
    if (initSegment) {
      if (initSegment != null && initSegment.tracks) {
        const mapFragment = frag.initSegment || frag;
        this._bufferInitSegment(level, initSegment.tracks, mapFragment, chunkMeta);
        hls.trigger(Events.FRAG_PARSING_INIT_SEGMENT, {
          frag: mapFragment,
          id,
          tracks: initSegment.tracks
        });
      }
      const baseTime = initSegment.initPTS;
      const timescale = initSegment.timescale;
      const initPTS = this.initPTS[frag.cc];
      if (isFiniteNumber(baseTime) && (!initPTS || initPTS.baseTime !== baseTime || initPTS.timescale !== timescale)) {
        this.initPTS[frag.cc] = {
          baseTime,
          timescale
        };
        hls.trigger(Events.INIT_PTS_FOUND, {
          frag,
          id,
          initPTS: baseTime,
          timescale
        });
      }
    }
    if (video && details) {
      if (audio && video.type === "audiovideo") {
        this.logMuxedErr(frag);
      }
      const prevFrag = details.fragments[frag.sn - 1 - details.startSN];
      const isFirstFragment = frag.sn === details.startSN;
      const isFirstInDiscontinuity = !prevFrag || frag.cc > prevFrag.cc;
      if (remuxResult.independent !== false) {
        const {
          startPTS,
          endPTS,
          startDTS,
          endDTS
        } = video;
        if (part) {
          part.elementaryStreams[video.type] = {
            startPTS,
            endPTS,
            startDTS,
            endDTS
          };
        } else {
          if (video.firstKeyFrame && video.independent && chunkMeta.id === 1 && !isFirstInDiscontinuity) {
            this.couldBacktrack = true;
          }
          if (video.dropped && video.independent) {
            const bufferInfo = this.getMainFwdBufferInfo();
            const targetBufferTime = (bufferInfo ? bufferInfo.end : this.getLoadPosition()) + this.config.maxBufferHole;
            const startTime = video.firstKeyFramePTS ? video.firstKeyFramePTS : startPTS;
            if (!isFirstFragment && targetBufferTime < startTime - this.config.maxBufferHole && !isFirstInDiscontinuity) {
              this.backtrack(frag);
              return;
            } else if (isFirstInDiscontinuity) {
              frag.gap = true;
            }
            frag.setElementaryStreamInfo(video.type, frag.start, endPTS, frag.start, endDTS, true);
          } else if (isFirstFragment && startPTS - (details.appliedTimelineOffset || 0) > MAX_START_GAP_JUMP) {
            frag.gap = true;
          }
        }
        frag.setElementaryStreamInfo(video.type, startPTS, endPTS, startDTS, endDTS);
        if (this.backtrackFragment) {
          this.backtrackFragment = frag;
        }
        this.bufferFragmentData(video, frag, part, chunkMeta, isFirstFragment || isFirstInDiscontinuity);
      } else if (isFirstFragment || isFirstInDiscontinuity) {
        frag.gap = true;
      } else {
        this.backtrack(frag);
        return;
      }
    }
    if (audio) {
      const {
        startPTS,
        endPTS,
        startDTS,
        endDTS
      } = audio;
      if (part) {
        part.elementaryStreams[ElementaryStreamTypes.AUDIO] = {
          startPTS,
          endPTS,
          startDTS,
          endDTS
        };
      }
      frag.setElementaryStreamInfo(ElementaryStreamTypes.AUDIO, startPTS, endPTS, startDTS, endDTS);
      this.bufferFragmentData(audio, frag, part, chunkMeta);
    }
    if (details && id3 != null && (_id3$samples = id3.samples) != null && _id3$samples.length) {
      const emittedID3 = {
        id,
        frag,
        details,
        samples: id3.samples
      };
      hls.trigger(Events.FRAG_PARSING_METADATA, emittedID3);
    }
    if (details && text) {
      const emittedText = {
        id,
        frag,
        details,
        samples: text.samples
      };
      hls.trigger(Events.FRAG_PARSING_USERDATA, emittedText);
    }
  }
  logMuxedErr(frag) {
    this.warn(`${isMediaFragment(frag) ? "Media" : "Init"} segment with muxed audiovideo where only video expected: ${frag.url}`);
  }
  _bufferInitSegment(currentLevel, tracks, frag, chunkMeta) {
    if (this.state !== State.PARSING) {
      return;
    }
    this.audioOnly = !!tracks.audio && !tracks.video;
    if (this.altAudio && !this.audioOnly) {
      delete tracks.audio;
      if (tracks.audiovideo) {
        this.logMuxedErr(frag);
      }
    }
    const {
      audio,
      video,
      audiovideo
    } = tracks;
    if (audio) {
      const levelCodec = currentLevel.audioCodec;
      let audioCodec = pickMostCompleteCodecName(audio.codec, levelCodec);
      if (audioCodec === "mp4a") {
        audioCodec = "mp4a.40.5";
      }
      const ua = navigator.userAgent.toLowerCase();
      if (this.audioCodecSwitch) {
        if (audioCodec) {
          if (audioCodec.indexOf("mp4a.40.5") !== -1) {
            audioCodec = "mp4a.40.2";
          } else {
            audioCodec = "mp4a.40.5";
          }
        }
        const audioMetadata = audio.metadata;
        if (audioMetadata && "channelCount" in audioMetadata && (audioMetadata.channelCount || 1) !== 1 && ua.indexOf("firefox") === -1) {
          audioCodec = "mp4a.40.5";
        }
      }
      if (audioCodec && audioCodec.indexOf("mp4a.40.5") !== -1 && ua.indexOf("android") !== -1 && audio.container !== "audio/mpeg") {
        audioCodec = "mp4a.40.2";
        this.log(`Android: force audio codec to ${audioCodec}`);
      }
      if (levelCodec && levelCodec !== audioCodec) {
        this.log(`Swapping manifest audio codec "${levelCodec}" for "${audioCodec}"`);
      }
      audio.levelCodec = audioCodec;
      audio.id = PlaylistLevelType.MAIN;
      this.log(`Init audio buffer, container:${audio.container}, codecs[selected/level/parsed]=[${audioCodec || ""}/${levelCodec || ""}/${audio.codec}]`);
      delete tracks.audiovideo;
    }
    if (video) {
      video.levelCodec = currentLevel.videoCodec;
      video.id = PlaylistLevelType.MAIN;
      const parsedVideoCodec = video.codec;
      if ((parsedVideoCodec == null ? void 0 : parsedVideoCodec.length) === 4) {
        switch (parsedVideoCodec) {
          case "hvc1":
          case "hev1":
            video.codec = "hvc1.1.6.L120.90";
            break;
          case "av01":
            video.codec = "av01.0.04M.08";
            break;
          case "avc1":
            video.codec = "avc1.42e01e";
            break;
        }
      }
      this.log(`Init video buffer, container:${video.container}, codecs[level/parsed]=[${currentLevel.videoCodec || ""}/${parsedVideoCodec}]${video.codec !== parsedVideoCodec ? " parsed-corrected=" + video.codec : ""}${video.supplemental ? " supplemental=" + video.supplemental : ""}`);
      delete tracks.audiovideo;
    }
    if (audiovideo) {
      this.log(`Init audiovideo buffer, container:${audiovideo.container}, codecs[level/parsed]=[${currentLevel.codecs}/${audiovideo.codec}]`);
      delete tracks.video;
      delete tracks.audio;
    }
    const trackTypes = Object.keys(tracks);
    if (trackTypes.length) {
      this.hls.trigger(Events.BUFFER_CODECS, tracks);
      if (!this.hls) {
        return;
      }
      trackTypes.forEach((trackName) => {
        const track = tracks[trackName];
        const initSegment = track.initSegment;
        if (initSegment != null && initSegment.byteLength) {
          this.hls.trigger(Events.BUFFER_APPENDING, {
            type: trackName,
            data: initSegment,
            frag,
            part: null,
            chunkMeta,
            parent: frag.type
          });
        }
      });
    }
    this.tickImmediate();
  }
  getMainFwdBufferInfo() {
    const bufferOutput = this.mediaBuffer && this.altAudio === 2 ? this.mediaBuffer : this.media;
    return this.getFwdBufferInfo(bufferOutput, PlaylistLevelType.MAIN);
  }
  get maxBufferLength() {
    const {
      levels,
      level
    } = this;
    const levelInfo = levels == null ? void 0 : levels[level];
    if (!levelInfo) {
      return this.config.maxBufferLength;
    }
    return this.getMaxBufferLength(levelInfo.maxBitrate);
  }
  backtrack(frag) {
    this.couldBacktrack = true;
    this.backtrackFragment = frag;
    this.resetTransmuxer();
    this.flushBufferGap(frag);
    this.fragmentTracker.removeFragment(frag);
    this.fragPrevious = null;
    this.nextLoadPosition = frag.start;
    this.state = State.IDLE;
  }
  checkFragmentChanged() {
    const video = this.media;
    let fragPlayingCurrent = null;
    if (video && video.readyState > 1 && video.seeking === false) {
      const currentTime = video.currentTime;
      if (BufferHelper.isBuffered(video, currentTime)) {
        fragPlayingCurrent = this.getAppendedFrag(currentTime);
      } else if (BufferHelper.isBuffered(video, currentTime + 0.1)) {
        fragPlayingCurrent = this.getAppendedFrag(currentTime + 0.1);
      }
      if (fragPlayingCurrent) {
        this.backtrackFragment = null;
        const fragPlaying = this.fragPlaying;
        const fragCurrentLevel = fragPlayingCurrent.level;
        if (!fragPlaying || fragPlayingCurrent.sn !== fragPlaying.sn || fragPlaying.level !== fragCurrentLevel) {
          this.fragPlaying = fragPlayingCurrent;
          this.hls.trigger(Events.FRAG_CHANGED, {
            frag: fragPlayingCurrent
          });
          if (!fragPlaying || fragPlaying.level !== fragCurrentLevel) {
            this.hls.trigger(Events.LEVEL_SWITCHED, {
              level: fragCurrentLevel
            });
          }
        }
      }
    }
  }
  get nextLevel() {
    const frag = this.nextBufferedFrag;
    if (frag) {
      return frag.level;
    }
    return -1;
  }
  get currentFrag() {
    var _this$media3;
    if (this.fragPlaying) {
      return this.fragPlaying;
    }
    const currentTime = ((_this$media3 = this.media) == null ? void 0 : _this$media3.currentTime) || this.lastCurrentTime;
    if (isFiniteNumber(currentTime)) {
      return this.getAppendedFrag(currentTime);
    }
    return null;
  }
  get currentProgramDateTime() {
    var _this$media4;
    const currentTime = ((_this$media4 = this.media) == null ? void 0 : _this$media4.currentTime) || this.lastCurrentTime;
    if (isFiniteNumber(currentTime)) {
      const details = this.getLevelDetails();
      const frag = this.currentFrag || (details ? findFragmentByPTS(null, details.fragments, currentTime) : null);
      if (frag) {
        const programDateTime = frag.programDateTime;
        if (programDateTime !== null) {
          const epocMs = programDateTime + (currentTime - frag.start) * 1e3;
          return new Date(epocMs);
        }
      }
    }
    return null;
  }
  get currentLevel() {
    const frag = this.currentFrag;
    if (frag) {
      return frag.level;
    }
    return -1;
  }
  get nextBufferedFrag() {
    const frag = this.currentFrag;
    if (frag) {
      return this.followingBufferedFrag(frag);
    }
    return null;
  }
  get forceStartLoad() {
    return this._forceStartLoad;
  }
};
var KeyLoader = class {
  constructor(config) {
    this.config = void 0;
    this.keyUriToKeyInfo = {};
    this.emeController = null;
    this.config = config;
  }
  abort(type) {
    for (const uri in this.keyUriToKeyInfo) {
      const loader = this.keyUriToKeyInfo[uri].loader;
      if (loader) {
        var _loader$context;
        if (type && type !== ((_loader$context = loader.context) == null ? void 0 : _loader$context.frag.type)) {
          return;
        }
        loader.abort();
      }
    }
  }
  detach() {
    for (const uri in this.keyUriToKeyInfo) {
      const keyInfo = this.keyUriToKeyInfo[uri];
      if (keyInfo.mediaKeySessionContext || keyInfo.decryptdata.isCommonEncryption) {
        delete this.keyUriToKeyInfo[uri];
      }
    }
  }
  destroy() {
    this.detach();
    for (const uri in this.keyUriToKeyInfo) {
      const loader = this.keyUriToKeyInfo[uri].loader;
      if (loader) {
        loader.destroy();
      }
    }
    this.keyUriToKeyInfo = {};
  }
  createKeyLoadError(frag, details = ErrorDetails.KEY_LOAD_ERROR, error, networkDetails, response) {
    return new LoadError({
      type: ErrorTypes.NETWORK_ERROR,
      details,
      fatal: false,
      frag,
      response,
      error,
      networkDetails
    });
  }
  loadClear(loadingFrag, encryptedFragments, startFragRequested) {
    if (this.emeController && this.config.emeEnabled && !this.emeController.getSelectedKeySystemFormats().length) {
      if (encryptedFragments.length) {
        for (let i = 0, l = encryptedFragments.length; i < l; i++) {
          const frag = encryptedFragments[i];
          if (loadingFrag.cc <= frag.cc && (!isMediaFragment(loadingFrag) || !isMediaFragment(frag) || loadingFrag.sn < frag.sn) || !startFragRequested && i == l - 1) {
            return this.emeController.selectKeySystemFormat(frag).then((keySystemFormat) => {
              if (!this.emeController) {
                return;
              }
              frag.setKeyFormat(keySystemFormat);
              const keySystem = keySystemFormatToKeySystemDomain(keySystemFormat);
              if (keySystem) {
                return this.emeController.getKeySystemAccess([keySystem]);
              }
            });
          }
        }
      }
      if (this.config.requireKeySystemAccessOnStart) {
        const keySystemsInConfig = getKeySystemsForConfig(this.config);
        if (keySystemsInConfig.length) {
          return this.emeController.getKeySystemAccess(keySystemsInConfig);
        }
      }
    }
    return null;
  }
  load(frag) {
    if (!frag.decryptdata && frag.encrypted && this.emeController && this.config.emeEnabled) {
      return this.emeController.selectKeySystemFormat(frag).then((keySystemFormat) => {
        return this.loadInternal(frag, keySystemFormat);
      });
    }
    return this.loadInternal(frag);
  }
  loadInternal(frag, keySystemFormat) {
    var _keyInfo, _keyInfo2;
    if (keySystemFormat) {
      frag.setKeyFormat(keySystemFormat);
    }
    const decryptdata = frag.decryptdata;
    if (!decryptdata) {
      const error = new Error(keySystemFormat ? `Expected frag.decryptdata to be defined after setting format ${keySystemFormat}` : "Missing decryption data on fragment in onKeyLoading");
      return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, error));
    }
    const uri = decryptdata.uri;
    if (!uri) {
      return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error(`Invalid key URI: "${uri}"`)));
    }
    let keyInfo = this.keyUriToKeyInfo[uri];
    if ((_keyInfo = keyInfo) != null && _keyInfo.decryptdata.key) {
      decryptdata.key = keyInfo.decryptdata.key;
      return Promise.resolve({
        frag,
        keyInfo
      });
    }
    if ((_keyInfo2 = keyInfo) != null && _keyInfo2.keyLoadPromise) {
      var _keyInfo$mediaKeySess;
      switch ((_keyInfo$mediaKeySess = keyInfo.mediaKeySessionContext) == null ? void 0 : _keyInfo$mediaKeySess.keyStatus) {
        case void 0:
        case "status-pending":
        case "usable":
        case "usable-in-future":
          return keyInfo.keyLoadPromise.then((keyLoadedData) => {
            decryptdata.key = keyLoadedData.keyInfo.decryptdata.key;
            return {
              frag,
              keyInfo
            };
          });
      }
    }
    keyInfo = this.keyUriToKeyInfo[uri] = {
      decryptdata,
      keyLoadPromise: null,
      loader: null,
      mediaKeySessionContext: null
    };
    switch (decryptdata.method) {
      case "ISO-23001-7":
      case "SAMPLE-AES":
      case "SAMPLE-AES-CENC":
      case "SAMPLE-AES-CTR":
        if (decryptdata.keyFormat === "identity") {
          return this.loadKeyHTTP(keyInfo, frag);
        }
        return this.loadKeyEME(keyInfo, frag);
      case "AES-128":
      case "AES-256":
      case "AES-256-CTR":
        return this.loadKeyHTTP(keyInfo, frag);
      default:
        return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error(`Key supplied with unsupported METHOD: "${decryptdata.method}"`)));
    }
  }
  loadKeyEME(keyInfo, frag) {
    const keyLoadedData = {
      frag,
      keyInfo
    };
    if (this.emeController && this.config.emeEnabled) {
      const keySessionContextPromise = this.emeController.loadKey(keyLoadedData);
      if (keySessionContextPromise) {
        return (keyInfo.keyLoadPromise = keySessionContextPromise.then((keySessionContext) => {
          keyInfo.mediaKeySessionContext = keySessionContext;
          return keyLoadedData;
        })).catch((error) => {
          keyInfo.keyLoadPromise = null;
          throw error;
        });
      }
    }
    return Promise.resolve(keyLoadedData);
  }
  loadKeyHTTP(keyInfo, frag) {
    const config = this.config;
    const Loader = config.loader;
    const keyLoader = new Loader(config);
    frag.keyLoader = keyInfo.loader = keyLoader;
    return keyInfo.keyLoadPromise = new Promise((resolve, reject) => {
      const loaderContext = {
        keyInfo,
        frag,
        responseType: "arraybuffer",
        url: keyInfo.decryptdata.uri
      };
      const loadPolicy = config.keyLoadPolicy.default;
      const loaderConfig = {
        loadPolicy,
        timeout: loadPolicy.maxLoadTimeMs,
        maxRetry: 0,
        retryDelay: 0,
        maxRetryDelay: 0
      };
      const loaderCallbacks = {
        onSuccess: (response, stats, context, networkDetails) => {
          const {
            frag: frag2,
            keyInfo: keyInfo2,
            url: uri
          } = context;
          if (!frag2.decryptdata || keyInfo2 !== this.keyUriToKeyInfo[uri]) {
            return reject(this.createKeyLoadError(frag2, ErrorDetails.KEY_LOAD_ERROR, new Error("after key load, decryptdata unset or changed"), networkDetails));
          }
          keyInfo2.decryptdata.key = frag2.decryptdata.key = new Uint8Array(response.data);
          frag2.keyLoader = null;
          keyInfo2.loader = null;
          resolve({
            frag: frag2,
            keyInfo: keyInfo2
          });
        },
        onError: (response, context, networkDetails, stats) => {
          this.resetLoader(context);
          reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error(`HTTP Error ${response.code} loading key ${response.text}`), networkDetails, _objectSpread2({
            url: loaderContext.url,
            data: void 0
          }, response)));
        },
        onTimeout: (stats, context, networkDetails) => {
          this.resetLoader(context);
          reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_TIMEOUT, new Error("key loading timed out"), networkDetails));
        },
        onAbort: (stats, context, networkDetails) => {
          this.resetLoader(context);
          reject(this.createKeyLoadError(frag, ErrorDetails.INTERNAL_ABORTED, new Error("key loading aborted"), networkDetails));
        }
      };
      keyLoader.load(loaderContext, loaderConfig, loaderCallbacks);
    });
  }
  resetLoader(context) {
    const {
      frag,
      keyInfo,
      url: uri
    } = context;
    const loader = keyInfo.loader;
    if (frag.keyLoader === loader) {
      frag.keyLoader = null;
      keyInfo.loader = null;
    }
    delete this.keyUriToKeyInfo[uri];
    if (loader) {
      loader.destroy();
    }
  }
};
function mapContextToLevelType(context) {
  const {
    type
  } = context;
  switch (type) {
    case PlaylistContextType.AUDIO_TRACK:
      return PlaylistLevelType.AUDIO;
    case PlaylistContextType.SUBTITLE_TRACK:
      return PlaylistLevelType.SUBTITLE;
    default:
      return PlaylistLevelType.MAIN;
  }
}
function getResponseUrl(response, context) {
  let url = response.url;
  if (url === void 0 || url.indexOf("data:") === 0) {
    url = context.url;
  }
  return url;
}
var PlaylistLoader = class {
  constructor(hls) {
    this.hls = void 0;
    this.loaders = /* @__PURE__ */ Object.create(null);
    this.variableList = null;
    this.onManifestLoaded = this.checkAutostartLoad;
    this.hls = hls;
    this.registerListeners();
  }
  startLoad(startPosition) {
  }
  stopLoad() {
    this.destroyInternalLoaders();
  }
  registerListeners() {
    const {
      hls
    } = this;
    hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this);
    hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this);
    hls.on(Events.AUDIO_TRACK_LOADING, this.onAudioTrackLoading, this);
    hls.on(Events.SUBTITLE_TRACK_LOADING, this.onSubtitleTrackLoading, this);
    hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
  }
  unregisterListeners() {
    const {
      hls
    } = this;
    hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this);
    hls.off(Events.LEVEL_LOADING, this.onLevelLoading, this);
    hls.off(Events.AUDIO_TRACK_LOADING, this.onAudioTrackLoading, this);
    hls.off(Events.SUBTITLE_TRACK_LOADING, this.onSubtitleTrackLoading, this);
    hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this);
  }
  createInternalLoader(context) {
    const config = this.hls.config;
    const PLoader = config.pLoader;
    const Loader = config.loader;
    const InternalLoader = PLoader || Loader;
    const loader = new InternalLoader(config);
    this.loaders[context.type] = loader;
    return loader;
  }
  getInternalLoader(context) {
    return this.loaders[context.type];
  }
  resetInternalLoader(contextType) {
    if (this.loaders[contextType]) {
      delete this.loaders[contextType];
    }
  }
  destroyInternalLoaders() {
    for (const contextType in this.loaders) {
      const loader = this.loaders[contextType];
      if (loader) {
        loader.destroy();
      }
      this.resetInternalLoader(contextType);
    }
  }
  destroy() {
    this.variableList = null;
    this.unregisterListeners();
    this.destroyInternalLoaders();
  }
  onManifestLoading(event, data) {
    const {
      url
    } = data;
    this.variableList = null;
    this.load({
      id: null,
      level: 0,
      responseType: "text",
      type: PlaylistContextType.MANIFEST,
      url,
      deliveryDirectives: null,
      levelOrTrack: null
    });
  }
  onLevelLoading(event, data) {
    const {
      id,
      level,
      pathwayId,
      url,
      deliveryDirectives,
      levelInfo
    } = data;
    this.load({
      id,
      level,
      pathwayId,
      responseType: "text",
      type: PlaylistContextType.LEVEL,
      url,
      deliveryDirectives,
      levelOrTrack: levelInfo
    });
  }
  onAudioTrackLoading(event, data) {
    const {
      id,
      groupId,
      url,
      deliveryDirectives,
      track
    } = data;
    this.load({
      id,
      groupId,
      level: null,
      responseType: "text",
      type: PlaylistContextType.AUDIO_TRACK,
      url,
      deliveryDirectives,
      levelOrTrack: track
    });
  }
  onSubtitleTrackLoading(event, data) {
    const {
      id,
      groupId,
      url,
      deliveryDirectives,
      track
    } = data;
    this.load({
      id,
      groupId,
      level: null,
      responseType: "text",
      type: PlaylistContextType.SUBTITLE_TRACK,
      url,
      deliveryDirectives,
      levelOrTrack: track
    });
  }
  onLevelsUpdated(event, data) {
    const loader = this.loaders[PlaylistContextType.LEVEL];
    if (loader) {
      const context = loader.context;
      if (context && !data.levels.some((lvl) => lvl === context.levelOrTrack)) {
        loader.abort();
        delete this.loaders[PlaylistContextType.LEVEL];
      }
    }
  }
  load(context) {
    var _context$deliveryDire;
    const config = this.hls.config;
    let loader = this.getInternalLoader(context);
    if (loader) {
      const logger2 = this.hls.logger;
      const loaderContext = loader.context;
      if (loaderContext && loaderContext.levelOrTrack === context.levelOrTrack && (loaderContext.url === context.url || loaderContext.deliveryDirectives && !context.deliveryDirectives)) {
        if (loaderContext.url === context.url) {
          logger2.log(`[playlist-loader]: ignore ${context.url} ongoing request`);
        } else {
          logger2.log(`[playlist-loader]: ignore ${context.url} in favor of ${loaderContext.url}`);
        }
        return;
      }
      logger2.log(`[playlist-loader]: aborting previous loader for type: ${context.type}`);
      loader.abort();
    }
    let loadPolicy;
    if (context.type === PlaylistContextType.MANIFEST) {
      loadPolicy = config.manifestLoadPolicy.default;
    } else {
      loadPolicy = _extends({}, config.playlistLoadPolicy.default, {
        timeoutRetry: null,
        errorRetry: null
      });
    }
    loader = this.createInternalLoader(context);
    if (isFiniteNumber((_context$deliveryDire = context.deliveryDirectives) == null ? void 0 : _context$deliveryDire.part)) {
      let levelDetails;
      if (context.type === PlaylistContextType.LEVEL && context.level !== null) {
        levelDetails = this.hls.levels[context.level].details;
      } else if (context.type === PlaylistContextType.AUDIO_TRACK && context.id !== null) {
        levelDetails = this.hls.audioTracks[context.id].details;
      } else if (context.type === PlaylistContextType.SUBTITLE_TRACK && context.id !== null) {
        levelDetails = this.hls.subtitleTracks[context.id].details;
      }
      if (levelDetails) {
        const partTarget = levelDetails.partTarget;
        const targetDuration = levelDetails.targetduration;
        if (partTarget && targetDuration) {
          const maxLowLatencyPlaylistRefresh = Math.max(partTarget * 3, targetDuration * 0.8) * 1e3;
          loadPolicy = _extends({}, loadPolicy, {
            maxTimeToFirstByteMs: Math.min(maxLowLatencyPlaylistRefresh, loadPolicy.maxTimeToFirstByteMs),
            maxLoadTimeMs: Math.min(maxLowLatencyPlaylistRefresh, loadPolicy.maxTimeToFirstByteMs)
          });
        }
      }
    }
    const legacyRetryCompatibility = loadPolicy.errorRetry || loadPolicy.timeoutRetry || {};
    const loaderConfig = {
      loadPolicy,
      timeout: loadPolicy.maxLoadTimeMs,
      maxRetry: legacyRetryCompatibility.maxNumRetry || 0,
      retryDelay: legacyRetryCompatibility.retryDelayMs || 0,
      maxRetryDelay: legacyRetryCompatibility.maxRetryDelayMs || 0
    };
    const loaderCallbacks = {
      onSuccess: (response, stats, context2, networkDetails) => {
        const loader2 = this.getInternalLoader(context2);
        this.resetInternalLoader(context2.type);
        const string = response.data;
        if (string.indexOf("#EXTM3U") !== 0) {
          this.handleManifestParsingError(response, context2, new Error("no EXTM3U delimiter"), networkDetails || null, stats);
          return;
        }
        stats.parsing.start = performance.now();
        if (M3U8Parser.isMediaPlaylist(string) || context2.type !== PlaylistContextType.MANIFEST) {
          this.handleTrackOrLevelPlaylist(response, stats, context2, networkDetails || null, loader2);
        } else {
          this.handleMasterPlaylist(response, stats, context2, networkDetails);
        }
      },
      onError: (response, context2, networkDetails, stats) => {
        this.handleNetworkError(context2, networkDetails, false, response, stats);
      },
      onTimeout: (stats, context2, networkDetails) => {
        this.handleNetworkError(context2, networkDetails, true, void 0, stats);
      }
    };
    loader.load(context, loaderConfig, loaderCallbacks);
  }
  checkAutostartLoad() {
    if (!this.hls) {
      return;
    }
    const {
      config: {
        autoStartLoad,
        startPosition
      },
      forceStartLoad
    } = this.hls;
    if (autoStartLoad || forceStartLoad) {
      this.hls.logger.log(`${autoStartLoad ? "auto" : "force"} startLoad with configured startPosition ${startPosition}`);
      this.hls.startLoad(startPosition);
    }
  }
  handleMasterPlaylist(response, stats, context, networkDetails) {
    const hls = this.hls;
    const string = response.data;
    const url = getResponseUrl(response, context);
    const parsedResult = M3U8Parser.parseMasterPlaylist(string, url);
    if (parsedResult.playlistParsingError) {
      this.handleManifestParsingError(response, context, parsedResult.playlistParsingError, networkDetails, stats);
      return;
    }
    const {
      contentSteering,
      levels,
      sessionData,
      sessionKeys,
      startTimeOffset,
      variableList
    } = parsedResult;
    this.variableList = variableList;
    const {
      AUDIO: audioTracks = [],
      SUBTITLES: subtitles,
      "CLOSED-CAPTIONS": captions
    } = M3U8Parser.parseMasterPlaylistMedia(string, url, parsedResult);
    if (audioTracks.length) {
      const embeddedAudioFound = audioTracks.some((audioTrack) => !audioTrack.url);
      if (!embeddedAudioFound && levels[0].audioCodec && !levels[0].attrs.AUDIO) {
        this.hls.logger.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one");
        audioTracks.unshift({
          type: "main",
          name: "main",
          groupId: "main",
          default: false,
          autoselect: false,
          forced: false,
          id: -1,
          attrs: new AttrList({}),
          bitrate: 0,
          url: ""
        });
      }
    }
    hls.trigger(Events.MANIFEST_LOADED, {
      levels,
      audioTracks,
      subtitles,
      captions,
      contentSteering,
      url,
      stats,
      networkDetails,
      sessionData,
      sessionKeys,
      startTimeOffset,
      variableList
    });
  }
  handleTrackOrLevelPlaylist(response, stats, context, networkDetails, loader) {
    const hls = this.hls;
    const {
      id,
      level,
      type
    } = context;
    const url = getResponseUrl(response, context);
    const levelId = isFiniteNumber(level) ? level : isFiniteNumber(id) ? id : 0;
    const levelType = mapContextToLevelType(context);
    const levelDetails = M3U8Parser.parseLevelPlaylist(response.data, url, levelId, levelType, 0, this.variableList);
    if (type === PlaylistContextType.MANIFEST) {
      const singleLevel = {
        attrs: new AttrList({}),
        bitrate: 0,
        details: levelDetails,
        name: "",
        url
      };
      levelDetails.requestScheduled = stats.loading.start + computeReloadInterval(levelDetails, 0);
      hls.trigger(Events.MANIFEST_LOADED, {
        levels: [singleLevel],
        audioTracks: [],
        url,
        stats,
        networkDetails,
        sessionData: null,
        sessionKeys: null,
        contentSteering: null,
        startTimeOffset: null,
        variableList: null
      });
    }
    stats.parsing.end = performance.now();
    context.levelDetails = levelDetails;
    this.handlePlaylistLoaded(levelDetails, response, stats, context, networkDetails, loader);
  }
  handleManifestParsingError(response, context, error, networkDetails, stats) {
    this.hls.trigger(Events.ERROR, {
      type: ErrorTypes.NETWORK_ERROR,
      details: ErrorDetails.MANIFEST_PARSING_ERROR,
      fatal: context.type === PlaylistContextType.MANIFEST,
      url: response.url,
      err: error,
      error,
      reason: error.message,
      response,
      context,
      networkDetails,
      stats
    });
  }
  handleNetworkError(context, networkDetails, timeout = false, response, stats) {
    let message = `A network ${timeout ? "timeout" : "error" + (response ? " (status " + response.code + ")" : "")} occurred while loading ${context.type}`;
    if (context.type === PlaylistContextType.LEVEL) {
      message += `: ${context.level} id: ${context.id}`;
    } else if (context.type === PlaylistContextType.AUDIO_TRACK || context.type === PlaylistContextType.SUBTITLE_TRACK) {
      message += ` id: ${context.id} group-id: "${context.groupId}"`;
    }
    const error = new Error(message);
    this.hls.logger.warn(`[playlist-loader]: ${message}`);
    let details = ErrorDetails.UNKNOWN;
    let fatal = false;
    const loader = this.getInternalLoader(context);
    switch (context.type) {
      case PlaylistContextType.MANIFEST:
        details = timeout ? ErrorDetails.MANIFEST_LOAD_TIMEOUT : ErrorDetails.MANIFEST_LOAD_ERROR;
        fatal = true;
        break;
      case PlaylistContextType.LEVEL:
        details = timeout ? ErrorDetails.LEVEL_LOAD_TIMEOUT : ErrorDetails.LEVEL_LOAD_ERROR;
        fatal = false;
        break;
      case PlaylistContextType.AUDIO_TRACK:
        details = timeout ? ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT : ErrorDetails.AUDIO_TRACK_LOAD_ERROR;
        fatal = false;
        break;
      case PlaylistContextType.SUBTITLE_TRACK:
        details = timeout ? ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT : ErrorDetails.SUBTITLE_LOAD_ERROR;
        fatal = false;
        break;
    }
    if (loader) {
      this.resetInternalLoader(context.type);
    }
    const errorData = {
      type: ErrorTypes.NETWORK_ERROR,
      details,
      fatal,
      url: context.url,
      loader,
      context,
      error,
      networkDetails,
      stats
    };
    if (response) {
      const url = (networkDetails == null ? void 0 : networkDetails.url) || context.url;
      errorData.response = _objectSpread2({
        url,
        data: void 0
      }, response);
    }
    this.hls.trigger(Events.ERROR, errorData);
  }
  handlePlaylistLoaded(levelDetails, response, stats, context, networkDetails, loader) {
    const hls = this.hls;
    const {
      type,
      level,
      id,
      groupId,
      deliveryDirectives
    } = context;
    const url = getResponseUrl(response, context);
    const parent = mapContextToLevelType(context);
    const levelIndex = typeof context.level === "number" && parent === PlaylistLevelType.MAIN ? level : void 0;
    if (!levelDetails.fragments.length) {
      const _error = levelDetails.playlistParsingError = new Error("No Segments found in Playlist");
      hls.trigger(Events.ERROR, {
        type: ErrorTypes.NETWORK_ERROR,
        details: ErrorDetails.LEVEL_EMPTY_ERROR,
        fatal: false,
        url,
        error: _error,
        reason: _error.message,
        response,
        context,
        level: levelIndex,
        parent,
        networkDetails,
        stats
      });
      return;
    }
    if (!levelDetails.targetduration) {
      levelDetails.playlistParsingError = new Error("Missing Target Duration");
    }
    const error = levelDetails.playlistParsingError;
    if (error) {
      this.hls.logger.warn(error);
      if (!hls.config.ignorePlaylistParsingErrors) {
        hls.trigger(Events.ERROR, {
          type: ErrorTypes.NETWORK_ERROR,
          details: ErrorDetails.LEVEL_PARSING_ERROR,
          fatal: false,
          url,
          error,
          reason: error.message,
          response,
          context,
          level: levelIndex,
          parent,
          networkDetails,
          stats
        });
        return;
      }
      levelDetails.playlistParsingError = null;
    }
    if (levelDetails.live && loader) {
      if (loader.getCacheAge) {
        levelDetails.ageHeader = loader.getCacheAge() || 0;
      }
      if (!loader.getCacheAge || isNaN(levelDetails.ageHeader)) {
        levelDetails.ageHeader = 0;
      }
    }
    switch (type) {
      case PlaylistContextType.MANIFEST:
      case PlaylistContextType.LEVEL:
        hls.trigger(Events.LEVEL_LOADED, {
          details: levelDetails,
          levelInfo: context.levelOrTrack || hls.levels[0],
          level: levelIndex || 0,
          id: id || 0,
          stats,
          networkDetails,
          deliveryDirectives,
          withoutMultiVariant: type === PlaylistContextType.MANIFEST
        });
        break;
      case PlaylistContextType.AUDIO_TRACK:
        hls.trigger(Events.AUDIO_TRACK_LOADED, {
          details: levelDetails,
          track: context.levelOrTrack,
          id: id || 0,
          groupId: groupId || "",
          stats,
          networkDetails,
          deliveryDirectives
        });
        break;
      case PlaylistContextType.SUBTITLE_TRACK:
        hls.trigger(Events.SUBTITLE_TRACK_LOADED, {
          details: levelDetails,
          track: context.levelOrTrack,
          id: id || 0,
          groupId: groupId || "",
          stats,
          networkDetails,
          deliveryDirectives
        });
        break;
    }
  }
};
var Hls = class {
  static get version() {
    return version;
  }
  static isMSESupported() {
    return isMSESupported();
  }
  static isSupported() {
    return isSupported();
  }
  static getMediaSource() {
    return getMediaSource();
  }
  static get Events() {
    return Events;
  }
  static get MetadataSchema() {
    return MetadataSchema;
  }
  static get ErrorTypes() {
    return ErrorTypes;
  }
  static get ErrorDetails() {
    return ErrorDetails;
  }
  static get DefaultConfig() {
    if (!Hls.defaultConfig) {
      return hlsDefaultConfig;
    }
    return Hls.defaultConfig;
  }
  static set DefaultConfig(defaultConfig) {
    Hls.defaultConfig = defaultConfig;
  }
  constructor(userConfig = {}) {
    this.config = void 0;
    this.userConfig = void 0;
    this.logger = void 0;
    this.coreComponents = void 0;
    this.networkControllers = void 0;
    this._emitter = new EventEmitter();
    this._autoLevelCapping = -1;
    this._maxHdcpLevel = null;
    this.abrController = void 0;
    this.bufferController = void 0;
    this.capLevelController = void 0;
    this.latencyController = void 0;
    this.levelController = void 0;
    this.streamController = void 0;
    this.audioStreamController = void 0;
    this.subtititleStreamController = void 0;
    this.audioTrackController = void 0;
    this.subtitleTrackController = void 0;
    this.interstitialsController = void 0;
    this.gapController = void 0;
    this.emeController = void 0;
    this.cmcdController = void 0;
    this._media = null;
    this._url = null;
    this._sessionId = void 0;
    this.triggeringException = void 0;
    this.started = false;
    const logger2 = this.logger = enableLogs(userConfig.debug || false, "Hls instance", userConfig.assetPlayerId);
    const config = this.config = mergeConfig(Hls.DefaultConfig, userConfig, logger2);
    this.userConfig = userConfig;
    if (config.progressive) {
      enableStreamingMode(config, logger2);
    }
    const {
      abrController: _AbrController,
      bufferController: _BufferController,
      capLevelController: _CapLevelController,
      errorController: _ErrorController,
      fpsController: _FpsController
    } = config;
    const errorController = new _ErrorController(this);
    const abrController = this.abrController = new _AbrController(this);
    const fragmentTracker = new FragmentTracker(this);
    const _InterstitialsController = config.interstitialsController;
    const interstitialsController = _InterstitialsController ? this.interstitialsController = new _InterstitialsController(this, Hls) : null;
    const bufferController = this.bufferController = new _BufferController(this, fragmentTracker);
    const capLevelController = this.capLevelController = new _CapLevelController(this);
    const fpsController = new _FpsController(this);
    const playListLoader = new PlaylistLoader(this);
    const _ContentSteeringController = config.contentSteeringController;
    const contentSteering = _ContentSteeringController ? new _ContentSteeringController(this) : null;
    const levelController = this.levelController = new LevelController(this, contentSteering);
    const id3TrackController = new ID3TrackController(this);
    const keyLoader = new KeyLoader(this.config);
    const streamController = this.streamController = new StreamController(this, fragmentTracker, keyLoader);
    const gapController = this.gapController = new GapController(this, fragmentTracker);
    capLevelController.setStreamController(streamController);
    fpsController.setStreamController(streamController);
    const networkControllers = [playListLoader, levelController, streamController];
    if (interstitialsController) {
      networkControllers.splice(1, 0, interstitialsController);
    }
    if (contentSteering) {
      networkControllers.splice(1, 0, contentSteering);
    }
    this.networkControllers = networkControllers;
    const coreComponents = [abrController, bufferController, gapController, capLevelController, fpsController, id3TrackController, fragmentTracker];
    this.audioTrackController = this.createController(config.audioTrackController, networkControllers);
    const AudioStreamControllerClass = config.audioStreamController;
    if (AudioStreamControllerClass) {
      networkControllers.push(this.audioStreamController = new AudioStreamControllerClass(this, fragmentTracker, keyLoader));
    }
    this.subtitleTrackController = this.createController(config.subtitleTrackController, networkControllers);
    const SubtitleStreamControllerClass = config.subtitleStreamController;
    if (SubtitleStreamControllerClass) {
      networkControllers.push(this.subtititleStreamController = new SubtitleStreamControllerClass(this, fragmentTracker, keyLoader));
    }
    this.createController(config.timelineController, coreComponents);
    keyLoader.emeController = this.emeController = this.createController(config.emeController, coreComponents);
    this.cmcdController = this.createController(config.cmcdController, coreComponents);
    this.latencyController = this.createController(LatencyController, coreComponents);
    this.coreComponents = coreComponents;
    networkControllers.push(errorController);
    const onErrorOut = errorController.onErrorOut;
    if (typeof onErrorOut === "function") {
      this.on(Events.ERROR, onErrorOut, errorController);
    }
    this.on(Events.MANIFEST_LOADED, playListLoader.onManifestLoaded, playListLoader);
  }
  createController(ControllerClass, components) {
    if (ControllerClass) {
      const controllerInstance = new ControllerClass(this);
      if (components) {
        components.push(controllerInstance);
      }
      return controllerInstance;
    }
    return null;
  }
  on(event, listener, context = this) {
    this._emitter.on(event, listener, context);
  }
  once(event, listener, context = this) {
    this._emitter.once(event, listener, context);
  }
  removeAllListeners(event) {
    this._emitter.removeAllListeners(event);
  }
  off(event, listener, context = this, once) {
    this._emitter.off(event, listener, context, once);
  }
  listeners(event) {
    return this._emitter.listeners(event);
  }
  emit(event, name, eventObject) {
    return this._emitter.emit(event, name, eventObject);
  }
  trigger(event, eventObject) {
    if (this.config.debug) {
      return this.emit(event, event, eventObject);
    } else {
      try {
        return this.emit(event, event, eventObject);
      } catch (error) {
        this.logger.error("An internal error happened while handling event " + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error);
        if (!this.triggeringException) {
          this.triggeringException = true;
          const fatal = event === Events.ERROR;
          this.trigger(Events.ERROR, {
            type: ErrorTypes.OTHER_ERROR,
            details: ErrorDetails.INTERNAL_EXCEPTION,
            fatal,
            event,
            error
          });
          this.triggeringException = false;
        }
      }
    }
    return false;
  }
  listenerCount(event) {
    return this._emitter.listenerCount(event);
  }
  destroy() {
    this.logger.log("destroy");
    this.trigger(Events.DESTROYING, void 0);
    this.detachMedia();
    this.removeAllListeners();
    this._autoLevelCapping = -1;
    this._url = null;
    this.networkControllers.forEach((component) => component.destroy());
    this.networkControllers.length = 0;
    this.coreComponents.forEach((component) => component.destroy());
    this.coreComponents.length = 0;
    const config = this.config;
    config.xhrSetup = config.fetchSetup = void 0;
    this.userConfig = null;
  }
  attachMedia(data) {
    if (!data || "media" in data && !data.media) {
      const error = new Error(`attachMedia failed: invalid argument (${data})`);
      this.trigger(Events.ERROR, {
        type: ErrorTypes.OTHER_ERROR,
        details: ErrorDetails.ATTACH_MEDIA_ERROR,
        fatal: true,
        error
      });
      return;
    }
    this.logger.log(`attachMedia`);
    if (this._media) {
      this.logger.warn(`media must be detached before attaching`);
      this.detachMedia();
    }
    const attachMediaSource = "media" in data;
    const media = attachMediaSource ? data.media : data;
    const attachingData = attachMediaSource ? data : {
      media
    };
    this._media = media;
    this.trigger(Events.MEDIA_ATTACHING, attachingData);
  }
  detachMedia() {
    this.logger.log("detachMedia");
    this.trigger(Events.MEDIA_DETACHING, {});
    this._media = null;
  }
  transferMedia() {
    this._media = null;
    const transferMedia = this.bufferController.transferMedia();
    this.trigger(Events.MEDIA_DETACHING, {
      transferMedia
    });
    return transferMedia;
  }
  loadSource(url) {
    this.stopLoad();
    const media = this.media;
    const loadedSource = this._url;
    const loadingSource = this._url = urlToolkitExports.buildAbsoluteURL(self.location.href, url, {
      alwaysNormalize: true
    });
    this._autoLevelCapping = -1;
    this._maxHdcpLevel = null;
    this.logger.log(`loadSource:${loadingSource}`);
    if (media && loadedSource && (loadedSource !== loadingSource || this.bufferController.hasSourceTypes())) {
      this.detachMedia();
      this.attachMedia(media);
    }
    this.trigger(Events.MANIFEST_LOADING, {
      url
    });
  }
  get url() {
    return this._url;
  }
  get hasEnoughToStart() {
    return this.streamController.hasEnoughToStart;
  }
  get startPosition() {
    return this.streamController.startPositionValue;
  }
  startLoad(startPosition = -1, skipSeekToStartPosition) {
    this.logger.log(`startLoad(${startPosition + (skipSeekToStartPosition ? ", " : "")})`);
    this.started = true;
    this.resumeBuffering();
    for (let i = 0; i < this.networkControllers.length; i++) {
      this.networkControllers[i].startLoad(startPosition, skipSeekToStartPosition);
      if (!this.started || !this.networkControllers) {
        break;
      }
    }
  }
  stopLoad() {
    this.logger.log("stopLoad");
    this.started = false;
    for (let i = 0; i < this.networkControllers.length; i++) {
      this.networkControllers[i].stopLoad();
      if (this.started || !this.networkControllers) {
        break;
      }
    }
  }
  get loadingEnabled() {
    return this.started;
  }
  get bufferingEnabled() {
    return this.streamController.bufferingEnabled;
  }
  resumeBuffering() {
    if (!this.bufferingEnabled) {
      this.logger.log(`resume buffering`);
      this.networkControllers.forEach((controller) => {
        if (controller.resumeBuffering) {
          controller.resumeBuffering();
        }
      });
    }
  }
  pauseBuffering() {
    if (this.bufferingEnabled) {
      this.logger.log(`pause buffering`);
      this.networkControllers.forEach((controller) => {
        if (controller.pauseBuffering) {
          controller.pauseBuffering();
        }
      });
    }
  }
  get inFlightFragments() {
    const inFlightData = {
      [PlaylistLevelType.MAIN]: this.streamController.inFlightFrag
    };
    if (this.audioStreamController) {
      inFlightData[PlaylistLevelType.AUDIO] = this.audioStreamController.inFlightFrag;
    }
    if (this.subtititleStreamController) {
      inFlightData[PlaylistLevelType.SUBTITLE] = this.subtititleStreamController.inFlightFrag;
    }
    return inFlightData;
  }
  swapAudioCodec() {
    this.logger.log("swapAudioCodec");
    this.streamController.swapAudioCodec();
  }
  recoverMediaError() {
    this.logger.log("recoverMediaError");
    const media = this._media;
    const time = media == null ? void 0 : media.currentTime;
    this.detachMedia();
    if (media) {
      this.attachMedia(media);
      if (time) {
        this.startLoad(time);
      }
    }
  }
  removeLevel(levelIndex) {
    this.levelController.removeLevel(levelIndex);
  }
  get sessionId() {
    let _sessionId = this._sessionId;
    if (!_sessionId) {
      _sessionId = this._sessionId = uuid();
    }
    return _sessionId;
  }
  get levels() {
    const levels = this.levelController.levels;
    return levels ? levels : [];
  }
  get latestLevelDetails() {
    return this.streamController.getLevelDetails() || null;
  }
  get loadLevelObj() {
    return this.levelController.loadLevelObj;
  }
  get currentLevel() {
    return this.streamController.currentLevel;
  }
  set currentLevel(newLevel) {
    this.logger.log(`set currentLevel:${newLevel}`);
    this.levelController.manualLevel = newLevel;
    this.streamController.immediateLevelSwitch();
  }
  get nextLevel() {
    return this.streamController.nextLevel;
  }
  set nextLevel(newLevel) {
    this.logger.log(`set nextLevel:${newLevel}`);
    this.levelController.manualLevel = newLevel;
    this.streamController.nextLevelSwitch();
  }
  get loadLevel() {
    return this.levelController.level;
  }
  set loadLevel(newLevel) {
    this.logger.log(`set loadLevel:${newLevel}`);
    this.levelController.manualLevel = newLevel;
  }
  get nextLoadLevel() {
    return this.levelController.nextLoadLevel;
  }
  set nextLoadLevel(level) {
    this.levelController.nextLoadLevel = level;
  }
  get firstLevel() {
    return Math.max(this.levelController.firstLevel, this.minAutoLevel);
  }
  set firstLevel(newLevel) {
    this.logger.log(`set firstLevel:${newLevel}`);
    this.levelController.firstLevel = newLevel;
  }
  get startLevel() {
    const startLevel = this.levelController.startLevel;
    if (startLevel === -1 && this.abrController.forcedAutoLevel > -1) {
      return this.abrController.forcedAutoLevel;
    }
    return startLevel;
  }
  set startLevel(newLevel) {
    this.logger.log(`set startLevel:${newLevel}`);
    if (newLevel !== -1) {
      newLevel = Math.max(newLevel, this.minAutoLevel);
    }
    this.levelController.startLevel = newLevel;
  }
  get capLevelToPlayerSize() {
    return this.config.capLevelToPlayerSize;
  }
  set capLevelToPlayerSize(shouldStartCapping) {
    const newCapLevelToPlayerSize = !!shouldStartCapping;
    if (newCapLevelToPlayerSize !== this.config.capLevelToPlayerSize) {
      if (newCapLevelToPlayerSize) {
        this.capLevelController.startCapping();
      } else {
        this.capLevelController.stopCapping();
        this.autoLevelCapping = -1;
        this.streamController.nextLevelSwitch();
      }
      this.config.capLevelToPlayerSize = newCapLevelToPlayerSize;
    }
  }
  get autoLevelCapping() {
    return this._autoLevelCapping;
  }
  get bandwidthEstimate() {
    const {
      bwEstimator
    } = this.abrController;
    if (!bwEstimator) {
      return NaN;
    }
    return bwEstimator.getEstimate();
  }
  set bandwidthEstimate(abrEwmaDefaultEstimate) {
    this.abrController.resetEstimator(abrEwmaDefaultEstimate);
  }
  get abrEwmaDefaultEstimate() {
    const {
      bwEstimator
    } = this.abrController;
    if (!bwEstimator) {
      return NaN;
    }
    return bwEstimator.defaultEstimate;
  }
  get ttfbEstimate() {
    const {
      bwEstimator
    } = this.abrController;
    if (!bwEstimator) {
      return NaN;
    }
    return bwEstimator.getEstimateTTFB();
  }
  set autoLevelCapping(newLevel) {
    if (this._autoLevelCapping !== newLevel) {
      this.logger.log(`set autoLevelCapping:${newLevel}`);
      this._autoLevelCapping = newLevel;
      this.levelController.checkMaxAutoUpdated();
    }
  }
  get maxHdcpLevel() {
    return this._maxHdcpLevel;
  }
  set maxHdcpLevel(value) {
    if (isHdcpLevel(value) && this._maxHdcpLevel !== value) {
      this._maxHdcpLevel = value;
      this.levelController.checkMaxAutoUpdated();
    }
  }
  get autoLevelEnabled() {
    return this.levelController.manualLevel === -1;
  }
  get manualLevel() {
    return this.levelController.manualLevel;
  }
  get minAutoLevel() {
    const {
      levels,
      config: {
        minAutoBitrate
      }
    } = this;
    if (!levels)
      return 0;
    const len = levels.length;
    for (let i = 0; i < len; i++) {
      if (levels[i].maxBitrate >= minAutoBitrate) {
        return i;
      }
    }
    return 0;
  }
  get maxAutoLevel() {
    const {
      levels,
      autoLevelCapping,
      maxHdcpLevel
    } = this;
    let maxAutoLevel;
    if (autoLevelCapping === -1 && levels != null && levels.length) {
      maxAutoLevel = levels.length - 1;
    } else {
      maxAutoLevel = autoLevelCapping;
    }
    if (maxHdcpLevel) {
      for (let i = maxAutoLevel; i--; ) {
        const hdcpLevel = levels[i].attrs["HDCP-LEVEL"];
        if (hdcpLevel && hdcpLevel <= maxHdcpLevel) {
          return i;
        }
      }
    }
    return maxAutoLevel;
  }
  get firstAutoLevel() {
    return this.abrController.firstAutoLevel;
  }
  get nextAutoLevel() {
    return this.abrController.nextAutoLevel;
  }
  set nextAutoLevel(nextLevel) {
    this.abrController.nextAutoLevel = nextLevel;
  }
  get playingDate() {
    return this.streamController.currentProgramDateTime;
  }
  get mainForwardBufferInfo() {
    return this.streamController.getMainFwdBufferInfo();
  }
  get maxBufferLength() {
    return this.streamController.maxBufferLength;
  }
  setAudioOption(audioOption) {
    var _this$audioTrackContr;
    return ((_this$audioTrackContr = this.audioTrackController) == null ? void 0 : _this$audioTrackContr.setAudioOption(audioOption)) || null;
  }
  setSubtitleOption(subtitleOption) {
    var _this$subtitleTrackCo;
    return ((_this$subtitleTrackCo = this.subtitleTrackController) == null ? void 0 : _this$subtitleTrackCo.setSubtitleOption(subtitleOption)) || null;
  }
  get allAudioTracks() {
    const audioTrackController = this.audioTrackController;
    return audioTrackController ? audioTrackController.allAudioTracks : [];
  }
  get audioTracks() {
    const audioTrackController = this.audioTrackController;
    return audioTrackController ? audioTrackController.audioTracks : [];
  }
  get audioTrack() {
    const audioTrackController = this.audioTrackController;
    return audioTrackController ? audioTrackController.audioTrack : -1;
  }
  set audioTrack(audioTrackId) {
    const audioTrackController = this.audioTrackController;
    if (audioTrackController) {
      audioTrackController.audioTrack = audioTrackId;
    }
  }
  get allSubtitleTracks() {
    const subtitleTrackController = this.subtitleTrackController;
    return subtitleTrackController ? subtitleTrackController.allSubtitleTracks : [];
  }
  get subtitleTracks() {
    const subtitleTrackController = this.subtitleTrackController;
    return subtitleTrackController ? subtitleTrackController.subtitleTracks : [];
  }
  get subtitleTrack() {
    const subtitleTrackController = this.subtitleTrackController;
    return subtitleTrackController ? subtitleTrackController.subtitleTrack : -1;
  }
  get media() {
    return this._media;
  }
  set subtitleTrack(subtitleTrackId) {
    const subtitleTrackController = this.subtitleTrackController;
    if (subtitleTrackController) {
      subtitleTrackController.subtitleTrack = subtitleTrackId;
    }
  }
  get subtitleDisplay() {
    const subtitleTrackController = this.subtitleTrackController;
    return subtitleTrackController ? subtitleTrackController.subtitleDisplay : false;
  }
  set subtitleDisplay(value) {
    const subtitleTrackController = this.subtitleTrackController;
    if (subtitleTrackController) {
      subtitleTrackController.subtitleDisplay = value;
    }
  }
  get lowLatencyMode() {
    return this.config.lowLatencyMode;
  }
  set lowLatencyMode(mode) {
    this.config.lowLatencyMode = mode;
  }
  get liveSyncPosition() {
    return this.latencyController.liveSyncPosition;
  }
  get latency() {
    return this.latencyController.latency;
  }
  get maxLatency() {
    return this.latencyController.maxLatency;
  }
  get targetLatency() {
    return this.latencyController.targetLatency;
  }
  set targetLatency(latency) {
    this.latencyController.targetLatency = latency;
  }
  get drift() {
    return this.latencyController.drift;
  }
  get forceStartLoad() {
    return this.streamController.forceStartLoad;
  }
  get pathways() {
    return this.levelController.pathways;
  }
  get pathwayPriority() {
    return this.levelController.pathwayPriority;
  }
  set pathwayPriority(pathwayPriority) {
    this.levelController.pathwayPriority = pathwayPriority;
  }
  get bufferedToEnd() {
    var _this$bufferControlle;
    return !!((_this$bufferControlle = this.bufferController) != null && _this$bufferControlle.bufferedToEnd);
  }
  get interstitialsManager() {
    var _this$interstitialsCo;
    return ((_this$interstitialsCo = this.interstitialsController) == null ? void 0 : _this$interstitialsCo.interstitialsManager) || null;
  }
  getMediaDecodingInfo(level, audioTracks = this.allAudioTracks) {
    const audioTracksByGroup = getAudioTracksByGroup(audioTracks);
    return getMediaDecodingInfoPromise(level, audioTracksByGroup, navigator.mediaCapabilities);
  }
};
Hls.defaultConfig = void 0;

// dep:hls_js
var hls_js_default = Hls;
export {
  AbrController,
  AttrList,
  AudioStreamController,
  AudioTrackController,
  BasePlaylistController,
  BaseSegment,
  BaseStreamController,
  BufferController,
  CMCDController,
  CapLevelController,
  ChunkMetadata,
  ContentSteeringController,
  Cues,
  DateRange,
  EMEController,
  ErrorActionFlags,
  ErrorController,
  ErrorDetails,
  ErrorTypes,
  Events,
  FPSController,
  FetchLoader,
  Fragment,
  Hls,
  HlsSkip,
  HlsUrlParameters,
  KeySystemFormats,
  KeySystems,
  Level,
  LevelDetails,
  LevelKey,
  LoadStats,
  M3U8Parser,
  MetadataSchema,
  NetworkErrorAction,
  Part,
  PlaylistLevelType,
  SubtitleStreamController,
  SubtitleTrackController,
  TimelineController,
  XhrLoader,
  hls_js_default as default,
  fetchSupported,
  getMediaSource,
  isMSESupported,
  isSupported,
  requestMediaKeySystemAccess
};
//# sourceMappingURL=hls_js.js.map