UNPKG

@tianfeng98/hls.js

Version:

HLS.js is a JavaScript library that supports playing MPEG-TS and HEVC encoded HLS streams in browsers with support for MSE.

1 lines 2.34 MB
{"version":3,"file":"hls.mjs.map","sources":["node_modules/url-toolkit/src/url-toolkit.js","src/polyfills/number.ts","src/events.ts","src/errors.ts","src/utils/logger.ts","src/utils/attr-list.ts","src/loader/date-range.ts","src/loader/load-stats.ts","src/loader/fragment.ts","src/loader/level-details.ts","src/utils/numeric-encoding-utils.ts","src/utils/keysystem-util.ts","src/utils/global.ts","src/utils/mediakeys-helper.ts","src/utils/typed-array.ts","src/demux/id3.ts","src/utils/hex.ts","src/utils/mp4-tools.ts","src/loader/level-key.ts","src/utils/variable-substitution.ts","src/utils/mediasource-helper.ts","src/utils/codecs.ts","src/loader/m3u8-parser.ts","src/types/loader.ts","src/loader/playlist-loader.ts","src/utils/texttrack-utils.ts","src/types/demuxer.ts","src/controller/id3-track-controller.ts","src/controller/latency-controller.ts","src/types/level.ts","src/utils/level-helper.ts","src/utils/error-helper.ts","src/utils/binary-search.ts","src/controller/fragment-finders.ts","src/controller/error-controller.ts","src/controller/base-playlist-controller.ts","src/utils/ewma.ts","src/utils/ewma-bandwidth-estimator.ts","src/utils/mediacapabilities-helper.ts","src/utils/rendition-helper.ts","src/controller/abr-controller.ts","src/task-loop.ts","src/controller/fragment-tracker.ts","src/utils/buffer-helper.ts","src/types/transmuxer.ts","src/utils/discontinuities.ts","src/loader/fragment-loader.ts","src/crypt/aes-crypto.ts","src/crypt/fast-aes-key.ts","src/crypt/aes-decryptor.ts","src/crypt/decrypter.ts","src/utils/time-ranges.ts","src/controller/base-stream-controller.ts","src/demux/chunk-cache.ts","src/demux/inject-worker.ts","src/demux/dummy-demuxed-track.ts","src/demux/audio/base-audio-demuxer.ts","src/demux/audio/adts.ts","src/demux/audio/mpegaudio.ts","src/demux/audio/aacdemuxer.ts","src/demux/mp4demuxer.ts","src/demux/audio/dolby.ts","src/demux/audio/ac3-demuxer.ts","src/demux/video/base-video-parser.ts","src/demux/video/exp-golomb.ts","src/demux/video/avc-video-parser.ts","src/demux/video/hevc-parser.ts","src/demux/video/hevc-video-parser.ts","src/demux/sample-aes.ts","src/demux/tsdemuxer.ts","src/demux/audio/mp3demuxer.ts","src/remux/aac-helper.ts","src/remux/hevc-helper.ts","src/remux/mp4-generator.ts","src/utils/timescale-conversion.ts","src/remux/mp4-remuxer.ts","src/remux/passthrough-remuxer.ts","src/demux/transmuxer.ts","node_modules/eventemitter3/index.js","src/demux/transmuxer-interface.ts","src/utils/media-option-attributes.ts","src/controller/audio-stream-controller.ts","src/controller/audio-track-controller.ts","src/controller/subtitle-stream-controller.ts","src/controller/subtitle-track-controller.ts","src/controller/buffer-operation-queue.ts","src/controller/buffer-controller.ts","src/utils/cea-608-parser.ts","src/utils/output-filter.ts","src/utils/vttcue.ts","src/utils/vttparser.ts","src/utils/webvtt-parser.ts","src/utils/imsc1-ttml-parser.ts","src/controller/timeline-controller.ts","src/controller/cap-level-controller.ts","src/controller/fps-controller.ts","src/controller/eme-controller.ts","src/types/cmcd.ts","src/controller/cmcd-controller.ts","src/controller/content-steering-controller.ts","src/utils/xhr-loader.ts","src/utils/fetch-loader.ts","src/utils/cues.ts","src/config.ts","src/controller/level-controller.ts","src/loader/key-loader.ts","src/is-supported.ts","src/controller/gap-controller.ts","src/controller/stream-controller.ts","src/hls.ts"],"sourcesContent":["// see https://tools.ietf.org/html/rfc1808\n\n(function (root) {\n var URL_REGEX =\n /^(?=((?:[a-zA-Z0-9+\\-.]+:)?))\\1(?=((?:\\/\\/[^\\/?#]*)?))\\2(?=((?:(?:[^?#\\/]*\\/)*[^;?#\\/]*)?))\\3((?:;[^?#]*)?)(\\?[^#]*)?(#[^]*)?$/;\n var FIRST_SEGMENT_REGEX = /^(?=([^\\/?#]*))\\1([^]*)$/;\n var SLASH_DOT_REGEX = /(?:\\/|^)\\.(?=\\/)/g;\n var SLASH_DOT_DOT_REGEX = /(?:\\/|^)\\.\\.\\/(?!\\.\\.\\/)[^\\/]*(?=\\/)/g;\n\n var URLToolkit = {\n // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or //\n // E.g\n // With opts.alwaysNormalize = false (default, spec compliant)\n // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g\n // With opts.alwaysNormalize = true (not spec compliant)\n // http://a.com/b/cd + /e/f/../g => http://a.com/e/g\n buildAbsoluteURL: function (baseURL, relativeURL, opts) {\n opts = opts || {};\n // remove any remaining space and CRLF\n baseURL = baseURL.trim();\n relativeURL = relativeURL.trim();\n if (!relativeURL) {\n // 2a) If the embedded URL is entirely empty, it inherits the\n // entire base URL (i.e., is set equal to the base URL)\n // and we are done.\n if (!opts.alwaysNormalize) {\n return baseURL;\n }\n var basePartsForNormalise = URLToolkit.parseURL(baseURL);\n if (!basePartsForNormalise) {\n throw new Error('Error trying to parse base URL.');\n }\n basePartsForNormalise.path = URLToolkit.normalizePath(\n basePartsForNormalise.path\n );\n return URLToolkit.buildURLFromParts(basePartsForNormalise);\n }\n var relativeParts = URLToolkit.parseURL(relativeURL);\n if (!relativeParts) {\n throw new Error('Error trying to parse relative URL.');\n }\n if (relativeParts.scheme) {\n // 2b) If the embedded URL starts with a scheme name, it is\n // interpreted as an absolute URL and we are done.\n if (!opts.alwaysNormalize) {\n return relativeURL;\n }\n relativeParts.path = URLToolkit.normalizePath(relativeParts.path);\n return URLToolkit.buildURLFromParts(relativeParts);\n }\n var baseParts = URLToolkit.parseURL(baseURL);\n if (!baseParts) {\n throw new Error('Error trying to parse base URL.');\n }\n if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') {\n // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc\n // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a'\n var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path);\n baseParts.netLoc = pathParts[1];\n baseParts.path = pathParts[2];\n }\n if (baseParts.netLoc && !baseParts.path) {\n baseParts.path = '/';\n }\n var builtParts = {\n // 2c) Otherwise, the embedded URL inherits the scheme of\n // the base URL.\n scheme: baseParts.scheme,\n netLoc: relativeParts.netLoc,\n path: null,\n params: relativeParts.params,\n query: relativeParts.query,\n fragment: relativeParts.fragment,\n };\n if (!relativeParts.netLoc) {\n // 3) If the embedded URL's <net_loc> is non-empty, we skip to\n // Step 7. Otherwise, the embedded URL inherits the <net_loc>\n // (if any) of the base URL.\n builtParts.netLoc = baseParts.netLoc;\n // 4) If the embedded URL path is preceded by a slash \"/\", the\n // path is not relative and we skip to Step 7.\n if (relativeParts.path[0] !== '/') {\n if (!relativeParts.path) {\n // 5) If the embedded URL path is empty (and not preceded by a\n // slash), then the embedded URL inherits the base URL path\n builtParts.path = baseParts.path;\n // 5a) if the embedded URL's <params> is non-empty, we skip to\n // step 7; otherwise, it inherits the <params> of the base\n // URL (if any) and\n if (!relativeParts.params) {\n builtParts.params = baseParts.params;\n // 5b) if the embedded URL's <query> is non-empty, we skip to\n // step 7; otherwise, it inherits the <query> of the base\n // URL (if any) and we skip to step 7.\n if (!relativeParts.query) {\n builtParts.query = baseParts.query;\n }\n }\n } else {\n // 6) The last segment of the base URL's path (anything\n // following the rightmost slash \"/\", or the entire path if no\n // slash is present) is removed and the embedded URL's path is\n // appended in its place.\n var baseURLPath = baseParts.path;\n var newPath =\n baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) +\n relativeParts.path;\n builtParts.path = URLToolkit.normalizePath(newPath);\n }\n }\n }\n if (builtParts.path === null) {\n builtParts.path = opts.alwaysNormalize\n ? URLToolkit.normalizePath(relativeParts.path)\n : relativeParts.path;\n }\n return URLToolkit.buildURLFromParts(builtParts);\n },\n parseURL: function (url) {\n var parts = URL_REGEX.exec(url);\n if (!parts) {\n return null;\n }\n return {\n scheme: parts[1] || '',\n netLoc: parts[2] || '',\n path: parts[3] || '',\n params: parts[4] || '',\n query: parts[5] || '',\n fragment: parts[6] || '',\n };\n },\n normalizePath: function (path) {\n // The following operations are\n // then applied, in order, to the new path:\n // 6a) All occurrences of \"./\", where \".\" is a complete path\n // segment, are removed.\n // 6b) If the path ends with \".\" as a complete path segment,\n // that \".\" is removed.\n path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, '');\n // 6c) All occurrences of \"<segment>/../\", where <segment> is a\n // complete path segment not equal to \"..\", are removed.\n // Removal of these path segments is performed iteratively,\n // removing the leftmost matching pattern on each iteration,\n // until no matching pattern remains.\n // 6d) If the path ends with \"<segment>/..\", where <segment> is a\n // complete path segment not equal to \"..\", that\n // \"<segment>/..\" is removed.\n while (\n path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length\n ) {}\n return path.split('').reverse().join('');\n },\n buildURLFromParts: function (parts) {\n return (\n parts.scheme +\n parts.netLoc +\n parts.path +\n parts.params +\n parts.query +\n parts.fragment\n );\n },\n };\n\n if (typeof exports === 'object' && typeof module === 'object')\n module.exports = URLToolkit;\n else if (typeof define === 'function' && define.amd)\n define([], function () {\n return URLToolkit;\n });\n else if (typeof exports === 'object') exports['URLToolkit'] = URLToolkit;\n else root['URLToolkit'] = URLToolkit;\n})(this);\n","export const isFiniteNumber =\n Number.isFinite ||\n function (value) {\n return typeof value === 'number' && isFinite(value);\n };\n\nexport const MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991;\n","import {\n ManifestLoadedData,\n ManifestLoadingData,\n MediaAttachedData,\n MediaAttachingData,\n LevelLoadingData,\n LevelLoadedData,\n ManifestParsedData,\n LevelUpdatedData,\n LevelsUpdatedData,\n FragParsingUserdataData,\n FragDecryptedData,\n FragLoadedData,\n InitPTSFoundData,\n CuesParsedData,\n SubtitleFragProcessedData,\n NonNativeTextTracksData,\n FragLoadingData,\n AudioTrackLoadedData,\n SubtitleTrackLoadedData,\n ErrorData,\n AudioTrackSwitchingData,\n AudioTrackSwitchedData,\n KeyLoadedData,\n KeyLoadingData,\n SubtitleTrackSwitchData,\n SubtitleTracksUpdatedData,\n LevelSwitchedData,\n FragChangedData,\n BufferAppendingData,\n BufferCodecsData,\n FragParsingMetadataData,\n FragParsingInitSegmentData,\n FragBufferedData,\n BufferFlushingData,\n BufferEOSData,\n LevelSwitchingData,\n MaxAutoLevelUpdatedData,\n FPSDropLevelCappingData,\n FPSDropData,\n BufferCreatedData,\n BufferAppendedData,\n LevelPTSUpdatedData,\n FragParsedData,\n AudioTracksUpdatedData,\n FragLoadEmergencyAbortedData,\n BackBufferData,\n LiveBackBufferData,\n TrackLoadingData,\n BufferFlushedData,\n SteeringManifestLoadedData,\n} from './types/events';\n\nexport enum Events {\n // Fired before MediaSource is attaching to media element\n MEDIA_ATTACHING = 'hlsMediaAttaching',\n // Fired when MediaSource has been successfully attached to media element\n MEDIA_ATTACHED = 'hlsMediaAttached',\n // Fired before detaching MediaSource from media element\n MEDIA_DETACHING = 'hlsMediaDetaching',\n // Fired when MediaSource has been detached from media element\n MEDIA_DETACHED = 'hlsMediaDetached',\n // Fired when the buffer is going to be reset\n BUFFER_RESET = 'hlsBufferReset',\n // Fired when we know about the codecs that we need buffers for to push into - data: {tracks : { container, codec, levelCodec, initSegment, metadata }}\n BUFFER_CODECS = 'hlsBufferCodecs',\n // fired when sourcebuffers have been created - data: { tracks : tracks }\n BUFFER_CREATED = 'hlsBufferCreated',\n // fired when we append a segment to the buffer - data: { segment: segment object }\n BUFFER_APPENDING = 'hlsBufferAppending',\n // fired when we are done with appending a media segment to the buffer - data : { parent : segment parent that triggered BUFFER_APPENDING, pending : nb of segments waiting for appending for this segment parent}\n BUFFER_APPENDED = 'hlsBufferAppended',\n // fired when the stream is finished and we want to notify the media buffer that there will be no more data - data: { }\n BUFFER_EOS = 'hlsBufferEos',\n // fired when the media buffer should be flushed - data { startOffset, endOffset }\n BUFFER_FLUSHING = 'hlsBufferFlushing',\n // fired when the media buffer has been flushed - data: { }\n BUFFER_FLUSHED = 'hlsBufferFlushed',\n // fired to signal that a manifest loading starts - data: { url : manifestURL}\n MANIFEST_LOADING = 'hlsManifestLoading',\n // fired after manifest has been loaded - data: { levels : [available quality levels], audioTracks : [ available audio tracks ], url : manifestURL, stats : LoaderStats }\n MANIFEST_LOADED = 'hlsManifestLoaded',\n // fired after manifest has been parsed - data: { levels : [available quality levels], firstLevel : index of first quality level appearing in Manifest}\n MANIFEST_PARSED = 'hlsManifestParsed',\n // fired when a level switch is requested - data: { level : id of new level }\n LEVEL_SWITCHING = 'hlsLevelSwitching',\n // fired when a level switch is effective - data: { level : id of new level }\n LEVEL_SWITCHED = 'hlsLevelSwitched',\n // fired when a level playlist loading starts - data: { url : level URL, level : id of level being loaded}\n LEVEL_LOADING = 'hlsLevelLoading',\n // fired when a level playlist loading finishes - data: { details : levelDetails object, level : id of loaded level, stats : LoaderStats }\n LEVEL_LOADED = 'hlsLevelLoaded',\n // fired when a level's details have been updated based on previous details, after it has been loaded - data: { details : levelDetails object, level : id of updated level }\n LEVEL_UPDATED = 'hlsLevelUpdated',\n // fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment }\n LEVEL_PTS_UPDATED = 'hlsLevelPtsUpdated',\n // fired to notify that levels have changed after removing a level - data: { levels : [available quality levels] }\n LEVELS_UPDATED = 'hlsLevelsUpdated',\n // fired to notify that audio track lists has been updated - data: { audioTracks : audioTracks }\n AUDIO_TRACKS_UPDATED = 'hlsAudioTracksUpdated',\n // fired when an audio track switching is requested - data: { id : audio track id }\n AUDIO_TRACK_SWITCHING = 'hlsAudioTrackSwitching',\n // fired when an audio track switch actually occurs - data: { id : audio track id }\n AUDIO_TRACK_SWITCHED = 'hlsAudioTrackSwitched',\n // fired when an audio track loading starts - data: { url : audio track URL, id : audio track id }\n AUDIO_TRACK_LOADING = 'hlsAudioTrackLoading',\n // fired when an audio track loading finishes - data: { details : levelDetails object, id : audio track id, stats : LoaderStats }\n AUDIO_TRACK_LOADED = 'hlsAudioTrackLoaded',\n // fired to notify that subtitle track lists has been updated - data: { subtitleTracks : subtitleTracks }\n SUBTITLE_TRACKS_UPDATED = 'hlsSubtitleTracksUpdated',\n // fired to notify that subtitle tracks were cleared as a result of stopping the media\n SUBTITLE_TRACKS_CLEARED = 'hlsSubtitleTracksCleared',\n // fired when an subtitle track switch occurs - data: { id : subtitle track id }\n SUBTITLE_TRACK_SWITCH = 'hlsSubtitleTrackSwitch',\n // fired when a subtitle track loading starts - data: { url : subtitle track URL, id : subtitle track id }\n SUBTITLE_TRACK_LOADING = 'hlsSubtitleTrackLoading',\n // fired when a subtitle track loading finishes - data: { details : levelDetails object, id : subtitle track id, stats : LoaderStats }\n SUBTITLE_TRACK_LOADED = 'hlsSubtitleTrackLoaded',\n // fired when a subtitle fragment has been processed - data: { success : boolean, frag : the processed frag }\n SUBTITLE_FRAG_PROCESSED = 'hlsSubtitleFragProcessed',\n // fired when a set of VTTCues to be managed externally has been parsed - data: { type: string, track: string, cues: [ VTTCue ] }\n CUES_PARSED = 'hlsCuesParsed',\n // fired when a text track to be managed externally is found - data: { tracks: [ { label: string, kind: string, default: boolean } ] }\n NON_NATIVE_TEXT_TRACKS_FOUND = 'hlsNonNativeTextTracksFound',\n // fired when the first timestamp is found - data: { id : demuxer id, initPTS: initPTS, timescale: timescale, frag : fragment object }\n INIT_PTS_FOUND = 'hlsInitPtsFound',\n // fired when a fragment loading starts - data: { frag : fragment object }\n FRAG_LOADING = 'hlsFragLoading',\n // fired when a fragment loading is progressing - data: { frag : fragment object, { trequest, tfirst, loaded } }\n // FRAG_LOAD_PROGRESS = 'hlsFragLoadProgress',\n // Identifier for fragment load aborting for emergency switch down - data: { frag : fragment object }\n FRAG_LOAD_EMERGENCY_ABORTED = 'hlsFragLoadEmergencyAborted',\n // fired when a fragment loading is completed - data: { frag : fragment object, payload : fragment payload, stats : LoaderStats }\n FRAG_LOADED = 'hlsFragLoaded',\n // fired when a fragment has finished decrypting - data: { id : demuxer id, frag: fragment object, payload : fragment payload, stats : { tstart, tdecrypt } }\n FRAG_DECRYPTED = 'hlsFragDecrypted',\n // fired when Init Segment has been extracted from fragment - data: { id : demuxer id, frag: fragment object, moov : moov MP4 box, codecs : codecs found while parsing fragment }\n FRAG_PARSING_INIT_SEGMENT = 'hlsFragParsingInitSegment',\n // fired when parsing sei text is completed - data: { id : demuxer id, frag: fragment object, samples : [ sei samples pes ] }\n FRAG_PARSING_USERDATA = 'hlsFragParsingUserdata',\n // fired when parsing id3 is completed - data: { id : demuxer id, frag: fragment object, samples : [ id3 samples pes ] }\n FRAG_PARSING_METADATA = 'hlsFragParsingMetadata',\n // fired when data have been extracted from fragment - data: { id : demuxer id, frag: fragment object, data1 : moof MP4 box or TS fragments, data2 : mdat MP4 box or null}\n // FRAG_PARSING_DATA = 'hlsFragParsingData',\n // fired when fragment parsing is completed - data: { id : demuxer id, frag: fragment object }\n FRAG_PARSED = 'hlsFragParsed',\n // fired when fragment remuxed MP4 boxes have all been appended into SourceBuffer - data: { id : demuxer id, frag : fragment object, stats : LoaderStats }\n FRAG_BUFFERED = 'hlsFragBuffered',\n // fired when fragment matching with current media position is changing - data : { id : demuxer id, frag : fragment object }\n FRAG_CHANGED = 'hlsFragChanged',\n // Identifier for a FPS drop event - data: { currentDropped, currentDecoded, totalDroppedFrames }\n FPS_DROP = 'hlsFpsDrop',\n // triggered when FPS drop triggers auto level capping - data: { level, droppedLevel }\n FPS_DROP_LEVEL_CAPPING = 'hlsFpsDropLevelCapping',\n // triggered when maxAutoLevel changes - data { autoLevelCapping, levels, maxAutoLevel, minAutoLevel, maxHdcpLevel }\n MAX_AUTO_LEVEL_UPDATED = 'hlsMaxAutoLevelUpdated',\n // Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data }\n ERROR = 'hlsError',\n // fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example - data: { }\n DESTROYING = 'hlsDestroying',\n // fired when a decrypt key loading starts - data: { frag : fragment object }\n KEY_LOADING = 'hlsKeyLoading',\n // fired when a decrypt key loading is completed - data: { frag : fragment object, keyInfo : KeyLoaderInfo }\n KEY_LOADED = 'hlsKeyLoaded',\n // deprecated; please use BACK_BUFFER_REACHED - data : { bufferEnd: number }\n LIVE_BACK_BUFFER_REACHED = 'hlsLiveBackBufferReached',\n // fired when the back buffer is reached as defined by the backBufferLength config option - data : { bufferEnd: number }\n BACK_BUFFER_REACHED = 'hlsBackBufferReached',\n // fired after steering manifest has been loaded - data: { steeringManifest: SteeringManifest object, url: steering manifest URL }\n STEERING_MANIFEST_LOADED = 'hlsSteeringManifestLoaded',\n}\n\n/**\n * Defines each Event type and payload by Event name. Used in {@link hls.js#HlsEventEmitter} to strongly type the event listener API.\n */\nexport interface HlsListeners {\n [Events.MEDIA_ATTACHING]: (\n event: Events.MEDIA_ATTACHING,\n data: MediaAttachingData,\n ) => void;\n [Events.MEDIA_ATTACHED]: (\n event: Events.MEDIA_ATTACHED,\n data: MediaAttachedData,\n ) => void;\n [Events.MEDIA_DETACHING]: (event: Events.MEDIA_DETACHING) => void;\n [Events.MEDIA_DETACHED]: (event: Events.MEDIA_DETACHED) => void;\n [Events.BUFFER_RESET]: (event: Events.BUFFER_RESET) => void;\n [Events.BUFFER_CODECS]: (\n event: Events.BUFFER_CODECS,\n data: BufferCodecsData,\n ) => void;\n [Events.BUFFER_CREATED]: (\n event: Events.BUFFER_CREATED,\n data: BufferCreatedData,\n ) => void;\n [Events.BUFFER_APPENDING]: (\n event: Events.BUFFER_APPENDING,\n data: BufferAppendingData,\n ) => void;\n [Events.BUFFER_APPENDED]: (\n event: Events.BUFFER_APPENDED,\n data: BufferAppendedData,\n ) => void;\n [Events.BUFFER_EOS]: (event: Events.BUFFER_EOS, data: BufferEOSData) => void;\n [Events.BUFFER_FLUSHING]: (\n event: Events.BUFFER_FLUSHING,\n data: BufferFlushingData,\n ) => void;\n [Events.BUFFER_FLUSHED]: (\n event: Events.BUFFER_FLUSHED,\n data: BufferFlushedData,\n ) => void;\n [Events.MANIFEST_LOADING]: (\n event: Events.MANIFEST_LOADING,\n data: ManifestLoadingData,\n ) => void;\n [Events.MANIFEST_LOADED]: (\n event: Events.MANIFEST_LOADED,\n data: ManifestLoadedData,\n ) => void;\n [Events.MANIFEST_PARSED]: (\n event: Events.MANIFEST_PARSED,\n data: ManifestParsedData,\n ) => void;\n [Events.LEVEL_SWITCHING]: (\n event: Events.LEVEL_SWITCHING,\n data: LevelSwitchingData,\n ) => void;\n [Events.LEVEL_SWITCHED]: (\n event: Events.LEVEL_SWITCHED,\n data: LevelSwitchedData,\n ) => void;\n [Events.LEVEL_LOADING]: (\n event: Events.LEVEL_LOADING,\n data: LevelLoadingData,\n ) => void;\n [Events.LEVEL_LOADED]: (\n event: Events.LEVEL_LOADED,\n data: LevelLoadedData,\n ) => void;\n [Events.LEVEL_UPDATED]: (\n event: Events.LEVEL_UPDATED,\n data: LevelUpdatedData,\n ) => void;\n [Events.LEVEL_PTS_UPDATED]: (\n event: Events.LEVEL_PTS_UPDATED,\n data: LevelPTSUpdatedData,\n ) => void;\n [Events.LEVELS_UPDATED]: (\n event: Events.LEVELS_UPDATED,\n data: LevelsUpdatedData,\n ) => void;\n [Events.AUDIO_TRACKS_UPDATED]: (\n event: Events.AUDIO_TRACKS_UPDATED,\n data: AudioTracksUpdatedData,\n ) => void;\n [Events.AUDIO_TRACK_SWITCHING]: (\n event: Events.AUDIO_TRACK_SWITCHING,\n data: AudioTrackSwitchingData,\n ) => void;\n [Events.AUDIO_TRACK_SWITCHED]: (\n event: Events.AUDIO_TRACK_SWITCHED,\n data: AudioTrackSwitchedData,\n ) => void;\n [Events.AUDIO_TRACK_LOADING]: (\n event: Events.AUDIO_TRACK_LOADING,\n data: TrackLoadingData,\n ) => void;\n [Events.AUDIO_TRACK_LOADED]: (\n event: Events.AUDIO_TRACK_LOADED,\n data: AudioTrackLoadedData,\n ) => void;\n [Events.SUBTITLE_TRACKS_UPDATED]: (\n event: Events.SUBTITLE_TRACKS_UPDATED,\n data: SubtitleTracksUpdatedData,\n ) => void;\n [Events.SUBTITLE_TRACKS_CLEARED]: (\n event: Events.SUBTITLE_TRACKS_CLEARED,\n ) => void;\n [Events.SUBTITLE_TRACK_SWITCH]: (\n event: Events.SUBTITLE_TRACK_SWITCH,\n data: SubtitleTrackSwitchData,\n ) => void;\n [Events.SUBTITLE_TRACK_LOADING]: (\n event: Events.SUBTITLE_TRACK_LOADING,\n data: TrackLoadingData,\n ) => void;\n [Events.SUBTITLE_TRACK_LOADED]: (\n event: Events.SUBTITLE_TRACK_LOADED,\n data: SubtitleTrackLoadedData,\n ) => void;\n [Events.SUBTITLE_FRAG_PROCESSED]: (\n event: Events.SUBTITLE_FRAG_PROCESSED,\n data: SubtitleFragProcessedData,\n ) => void;\n [Events.CUES_PARSED]: (\n event: Events.CUES_PARSED,\n data: CuesParsedData,\n ) => void;\n [Events.NON_NATIVE_TEXT_TRACKS_FOUND]: (\n event: Events.NON_NATIVE_TEXT_TRACKS_FOUND,\n data: NonNativeTextTracksData,\n ) => void;\n [Events.INIT_PTS_FOUND]: (\n event: Events.INIT_PTS_FOUND,\n data: InitPTSFoundData,\n ) => void;\n [Events.FRAG_LOADING]: (\n event: Events.FRAG_LOADING,\n data: FragLoadingData,\n ) => void;\n // [Events.FRAG_LOAD_PROGRESS]: TodoEventType\n [Events.FRAG_LOAD_EMERGENCY_ABORTED]: (\n event: Events.FRAG_LOAD_EMERGENCY_ABORTED,\n data: FragLoadEmergencyAbortedData,\n ) => void;\n [Events.FRAG_LOADED]: (\n event: Events.FRAG_LOADED,\n data: FragLoadedData,\n ) => void;\n [Events.FRAG_DECRYPTED]: (\n event: Events.FRAG_DECRYPTED,\n data: FragDecryptedData,\n ) => void;\n [Events.FRAG_PARSING_INIT_SEGMENT]: (\n event: Events.FRAG_PARSING_INIT_SEGMENT,\n data: FragParsingInitSegmentData,\n ) => void;\n [Events.FRAG_PARSING_USERDATA]: (\n event: Events.FRAG_PARSING_USERDATA,\n data: FragParsingUserdataData,\n ) => void;\n [Events.FRAG_PARSING_METADATA]: (\n event: Events.FRAG_PARSING_METADATA,\n data: FragParsingMetadataData,\n ) => void;\n // [Events.FRAG_PARSING_DATA]: TodoEventType\n [Events.FRAG_PARSED]: (\n event: Events.FRAG_PARSED,\n data: FragParsedData,\n ) => void;\n [Events.FRAG_BUFFERED]: (\n event: Events.FRAG_BUFFERED,\n data: FragBufferedData,\n ) => void;\n [Events.FRAG_CHANGED]: (\n event: Events.FRAG_CHANGED,\n data: FragChangedData,\n ) => void;\n [Events.FPS_DROP]: (event: Events.FPS_DROP, data: FPSDropData) => void;\n [Events.FPS_DROP_LEVEL_CAPPING]: (\n event: Events.FPS_DROP_LEVEL_CAPPING,\n data: FPSDropLevelCappingData,\n ) => void;\n [Events.MAX_AUTO_LEVEL_UPDATED]: (\n event: Events.MAX_AUTO_LEVEL_UPDATED,\n data: MaxAutoLevelUpdatedData,\n ) => void;\n [Events.ERROR]: (event: Events.ERROR, data: ErrorData) => void;\n [Events.DESTROYING]: (event: Events.DESTROYING) => void;\n [Events.KEY_LOADING]: (\n event: Events.KEY_LOADING,\n data: KeyLoadingData,\n ) => void;\n [Events.KEY_LOADED]: (event: Events.KEY_LOADED, data: KeyLoadedData) => void;\n [Events.LIVE_BACK_BUFFER_REACHED]: (\n event: Events.LIVE_BACK_BUFFER_REACHED,\n data: LiveBackBufferData,\n ) => void;\n [Events.BACK_BUFFER_REACHED]: (\n event: Events.BACK_BUFFER_REACHED,\n data: BackBufferData,\n ) => void;\n [Events.STEERING_MANIFEST_LOADED]: (\n event: Events.STEERING_MANIFEST_LOADED,\n data: SteeringManifestLoadedData,\n ) => void;\n}\nexport interface HlsEventEmitter {\n on<E extends keyof HlsListeners, Context = undefined>(\n event: E,\n listener: HlsListeners[E],\n context?: Context,\n ): void;\n once<E extends keyof HlsListeners, Context = undefined>(\n event: E,\n listener: HlsListeners[E],\n context?: Context,\n ): void;\n\n removeAllListeners<E extends keyof HlsListeners>(event?: E): void;\n off<E extends keyof HlsListeners, Context = undefined>(\n event: E,\n listener?: HlsListeners[E],\n context?: Context,\n once?: boolean,\n ): void;\n\n listeners<E extends keyof HlsListeners>(event: E): HlsListeners[E][];\n emit<E extends keyof HlsListeners>(\n event: E,\n name: E,\n eventObject: Parameters<HlsListeners[E]>[1],\n ): boolean;\n listenerCount<E extends keyof HlsListeners>(event: E): number;\n}\n","export enum ErrorTypes {\n // Identifier for a network error (loading error / timeout ...)\n NETWORK_ERROR = 'networkError',\n // Identifier for a media Error (video/parsing/mediasource error)\n MEDIA_ERROR = 'mediaError',\n // EME (encrypted media extensions) errors\n KEY_SYSTEM_ERROR = 'keySystemError',\n // Identifier for a mux Error (demuxing/remuxing)\n MUX_ERROR = 'muxError',\n // Identifier for all other errors\n OTHER_ERROR = 'otherError',\n}\n\nexport enum ErrorDetails {\n KEY_SYSTEM_NO_KEYS = 'keySystemNoKeys',\n KEY_SYSTEM_NO_ACCESS = 'keySystemNoAccess',\n KEY_SYSTEM_NO_SESSION = 'keySystemNoSession',\n KEY_SYSTEM_NO_CONFIGURED_LICENSE = 'keySystemNoConfiguredLicense',\n KEY_SYSTEM_LICENSE_REQUEST_FAILED = 'keySystemLicenseRequestFailed',\n KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED = 'keySystemServerCertificateRequestFailed',\n KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED = 'keySystemServerCertificateUpdateFailed',\n KEY_SYSTEM_SESSION_UPDATE_FAILED = 'keySystemSessionUpdateFailed',\n KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED = 'keySystemStatusOutputRestricted',\n KEY_SYSTEM_STATUS_INTERNAL_ERROR = 'keySystemStatusInternalError',\n // Identifier for a manifest load error - data: { url : faulty URL, response : { code: error code, text: error text }}\n MANIFEST_LOAD_ERROR = 'manifestLoadError',\n // Identifier for a manifest load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}\n MANIFEST_LOAD_TIMEOUT = 'manifestLoadTimeOut',\n // Identifier for a manifest parsing error - data: { url : faulty URL, reason : error reason}\n MANIFEST_PARSING_ERROR = 'manifestParsingError',\n // Identifier for a manifest with only incompatible codecs error - data: { url : faulty URL, reason : error reason}\n MANIFEST_INCOMPATIBLE_CODECS_ERROR = 'manifestIncompatibleCodecsError',\n // Identifier for a level which contains no fragments - data: { url: faulty URL, reason: \"no fragments found in level\", level: index of the bad level }\n LEVEL_EMPTY_ERROR = 'levelEmptyError',\n // Identifier for a level load error - data: { url : faulty URL, response : { code: error code, text: error text }}\n LEVEL_LOAD_ERROR = 'levelLoadError',\n // Identifier for a level load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}\n LEVEL_LOAD_TIMEOUT = 'levelLoadTimeOut',\n // Identifier for a level parse error - data: { url : faulty URL, error: Error, reason: error message }\n LEVEL_PARSING_ERROR = 'levelParsingError',\n // Identifier for a level switch error - data: { level : faulty level Id, event : error description}\n LEVEL_SWITCH_ERROR = 'levelSwitchError',\n // Identifier for an audio track load error - data: { url : faulty URL, response : { code: error code, text: error text }}\n AUDIO_TRACK_LOAD_ERROR = 'audioTrackLoadError',\n // Identifier for an audio track load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}\n AUDIO_TRACK_LOAD_TIMEOUT = 'audioTrackLoadTimeOut',\n // Identifier for a subtitle track load error - data: { url : faulty URL, response : { code: error code, text: error text }}\n SUBTITLE_LOAD_ERROR = 'subtitleTrackLoadError',\n // Identifier for a subtitle track load timeout - data: { url : faulty URL, response : { code: error code, text: error text }}\n SUBTITLE_TRACK_LOAD_TIMEOUT = 'subtitleTrackLoadTimeOut',\n // Identifier for fragment load error - data: { frag : fragment object, response : { code: error code, text: error text }}\n FRAG_LOAD_ERROR = 'fragLoadError',\n // Identifier for fragment load timeout error - data: { frag : fragment object}\n FRAG_LOAD_TIMEOUT = 'fragLoadTimeOut',\n // Identifier for a fragment decryption error event - data: {id : demuxer Id,frag: fragment object, reason : parsing error description }\n FRAG_DECRYPT_ERROR = 'fragDecryptError',\n // Identifier for a fragment parsing error event - data: { id : demuxer Id, reason : parsing error description }\n // will be renamed DEMUX_PARSING_ERROR and switched to MUX_ERROR in the next major release\n FRAG_PARSING_ERROR = 'fragParsingError',\n // Identifier for a fragment or part load skipped because of a GAP tag or attribute\n FRAG_GAP = 'fragGap',\n // Identifier for a remux alloc error event - data: { id : demuxer Id, frag : fragment object, bytes : nb of bytes on which allocation failed , reason : error text }\n REMUX_ALLOC_ERROR = 'remuxAllocError',\n // Identifier for decrypt key load error - data: { frag : fragment object, response : { code: error code, text: error text }}\n KEY_LOAD_ERROR = 'keyLoadError',\n // Identifier for decrypt key load timeout error - data: { frag : fragment object}\n KEY_LOAD_TIMEOUT = 'keyLoadTimeOut',\n // Triggered when an exception occurs while adding a sourceBuffer to MediaSource - data : { error : exception , mimeType : mimeType }\n BUFFER_ADD_CODEC_ERROR = 'bufferAddCodecError',\n // Triggered when source buffer(s) could not be created using level (manifest CODECS attribute), parsed media, or best guess codec(s) - data: { reason : error reason }\n BUFFER_INCOMPATIBLE_CODECS_ERROR = 'bufferIncompatibleCodecsError',\n // Identifier for a buffer append error - data: append error description\n BUFFER_APPEND_ERROR = 'bufferAppendError',\n // Identifier for a buffer appending error event - data: appending error description\n BUFFER_APPENDING_ERROR = 'bufferAppendingError',\n // Identifier for a buffer stalled error event\n BUFFER_STALLED_ERROR = 'bufferStalledError',\n // Identifier for a buffer full event\n BUFFER_FULL_ERROR = 'bufferFullError',\n // Identifier for a buffer seek over hole event\n BUFFER_SEEK_OVER_HOLE = 'bufferSeekOverHole',\n // Identifier for a buffer nudge on stall (playback is stuck although currentTime is in a buffered area)\n BUFFER_NUDGE_ON_STALL = 'bufferNudgeOnStall',\n // Identifier for an internal exception happening inside hls.js while handling an event\n INTERNAL_EXCEPTION = 'internalException',\n // Identifier for an internal call to abort a loader\n INTERNAL_ABORTED = 'aborted',\n // Uncategorized error\n UNKNOWN = 'unknown',\n}\n","export interface ILogFunction {\n (message?: any, ...optionalParams: any[]): void;\n}\n\nexport interface ILogger {\n trace: ILogFunction;\n debug: ILogFunction;\n log: ILogFunction;\n warn: ILogFunction;\n info: ILogFunction;\n error: ILogFunction;\n}\n\nconst noop: ILogFunction = function () {};\n\nconst fakeLogger: ILogger = {\n trace: noop,\n debug: noop,\n log: noop,\n warn: noop,\n info: noop,\n error: noop,\n};\n\nlet exportedLogger: ILogger = fakeLogger;\n\n// let lastCallTime;\n// function formatMsgWithTimeInfo(type, msg) {\n// const now = Date.now();\n// const diff = lastCallTime ? '+' + (now - lastCallTime) : '0';\n// lastCallTime = now;\n// msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )';\n// return msg;\n// }\n\nfunction consolePrintFn(type: string): ILogFunction {\n const func: ILogFunction = self.console[type];\n if (func) {\n return func.bind(self.console, `[${type}] >`);\n }\n return noop;\n}\n\nfunction exportLoggerFunctions(\n debugConfig: boolean | ILogger,\n ...functions: string[]\n): void {\n functions.forEach(function (type) {\n exportedLogger[type] = debugConfig[type]\n ? debugConfig[type].bind(debugConfig)\n : consolePrintFn(type);\n });\n}\n\nexport function enableLogs(debugConfig: boolean | ILogger, id: string): void {\n // check that console is available\n if (\n (self.console && debugConfig === true) ||\n typeof debugConfig === 'object'\n ) {\n exportLoggerFunctions(\n debugConfig,\n // Remove out from list here to hard-disable a log-level\n // 'trace',\n 'debug',\n 'log',\n 'info',\n 'warn',\n 'error',\n );\n // Some browsers don't allow to use bind on console object anyway\n // fallback to default if needed\n try {\n exportedLogger.log(\n `Debug logs enabled for \"${id}\" in hls.js version ${__VERSION__}`,\n );\n } catch (e) {\n exportedLogger = fakeLogger;\n }\n } else {\n exportedLogger = fakeLogger;\n }\n}\n\nexport const logger: ILogger = exportedLogger;\n","const DECIMAL_RESOLUTION_REGEX = /^(\\d+)x(\\d+)$/;\nconst ATTR_LIST_REGEX = /(.+?)=(\".*?\"|.*?)(?:,|$)/g;\n\n// adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js\nexport class AttrList {\n [key: string]: any;\n\n constructor(attrs: string | Record<string, any>) {\n if (typeof attrs === 'string') {\n attrs = AttrList.parseAttrList(attrs);\n }\n Object.assign(this, attrs);\n }\n\n get clientAttrs(): string[] {\n return Object.keys(this).filter((attr) => attr.substring(0, 2) === 'X-');\n }\n\n decimalInteger(attrName: string): number {\n const intValue = parseInt(this[attrName], 10);\n if (intValue > Number.MAX_SAFE_INTEGER) {\n return Infinity;\n }\n\n return intValue;\n }\n\n hexadecimalInteger(attrName: string) {\n if (this[attrName]) {\n let stringValue = (this[attrName] || '0x').slice(2);\n stringValue = (stringValue.length & 1 ? '0' : '') + stringValue;\n\n const value = new Uint8Array(stringValue.length / 2);\n for (let i = 0; i < stringValue.length / 2; i++) {\n value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16);\n }\n\n return value;\n } else {\n return null;\n }\n }\n\n hexadecimalIntegerAsNumber(attrName: string): number {\n const intValue = parseInt(this[attrName], 16);\n if (intValue > Number.MAX_SAFE_INTEGER) {\n return Infinity;\n }\n\n return intValue;\n }\n\n decimalFloatingPoint(attrName: string): number {\n return parseFloat(this[attrName]);\n }\n\n optionalFloat(attrName: string, defaultValue: number): number {\n const value = this[attrName];\n return value ? parseFloat(value) : defaultValue;\n }\n\n enumeratedString(attrName: string): string | undefined {\n return this[attrName];\n }\n\n bool(attrName: string): boolean {\n return this[attrName] === 'YES';\n }\n\n decimalResolution(attrName: string):\n | {\n width: number;\n height: number;\n }\n | undefined {\n const res = DECIMAL_RESOLUTION_REGEX.exec(this[attrName]);\n if (res === null) {\n return undefined;\n }\n\n return {\n width: parseInt(res[1], 10),\n height: parseInt(res[2], 10),\n };\n }\n\n static parseAttrList(input: string): Record<string, any> {\n let match;\n const attrs = {};\n const quote = '\"';\n ATTR_LIST_REGEX.lastIndex = 0;\n while ((match = ATTR_LIST_REGEX.exec(input)) !== null) {\n let value = match[2];\n\n if (\n value.indexOf(quote) === 0 &&\n value.lastIndexOf(quote) === value.length - 1\n ) {\n value = value.slice(1, -1);\n }\n const name = match[1].trim();\n attrs[name] = value;\n }\n return attrs;\n }\n}\n","import { AttrList } from '../utils/attr-list';\nimport { logger } from '../utils/logger';\n\n// Avoid exporting const enum so that these values can be inlined\nconst enum DateRangeAttribute {\n ID = 'ID',\n CLASS = 'CLASS',\n START_DATE = 'START-DATE',\n DURATION = 'DURATION',\n END_DATE = 'END-DATE',\n END_ON_NEXT = 'END-ON-NEXT',\n PLANNED_DURATION = 'PLANNED-DURATION',\n SCTE35_OUT = 'SCTE35-OUT',\n SCTE35_IN = 'SCTE35-IN',\n}\n\nexport function isDateRangeCueAttribute(attrName: string): boolean {\n return (\n attrName !== DateRangeAttribute.ID &&\n attrName !== DateRangeAttribute.CLASS &&\n attrName !== DateRangeAttribute.START_DATE &&\n attrName !== DateRangeAttribute.DURATION &&\n attrName !== DateRangeAttribute.END_DATE &&\n attrName !== DateRangeAttribute.END_ON_NEXT\n );\n}\n\nexport function isSCTE35Attribute(attrName: string): boolean {\n return (\n attrName === DateRangeAttribute.SCTE35_OUT ||\n attrName === DateRangeAttribute.SCTE35_IN\n );\n}\n\nexport class DateRange {\n public attr: AttrList;\n private _startDate: Date;\n private _endDate?: Date;\n private _badValueForSameId?: string;\n\n constructor(dateRangeAttr: AttrList, dateRangeWithSameId?: DateRange) {\n if (dateRangeWithSameId) {\n const previousAttr = dateRangeWithSameId.attr;\n for (const key in previousAttr) {\n if (\n Object.prototype.hasOwnProperty.call(dateRangeAttr, key) &&\n dateRangeAttr[key] !== previousAttr[key]\n ) {\n logger.warn(\n `DATERANGE tag attribute: \"${key}\" does not match for tags with ID: \"${dateRangeAttr.ID}\"`,\n );\n this._badValueForSameId = key;\n break;\n }\n }\n // Merge DateRange tags with the same ID\n dateRangeAttr = Object.assign(\n new AttrList({}),\n previousAttr,\n dateRangeAttr,\n );\n }\n this.attr = dateRangeAttr;\n this._startDate = new Date(dateRangeAttr[DateRangeAttribute.START_DATE]);\n if (DateRangeAttribute.END_DATE in this.attr) {\n const endDate = new Date(this.attr[DateRangeAttribute.END_DATE]);\n if (Number.isFinite(endDate.getTime())) {\n this._endDate = endDate;\n }\n }\n }\n\n get id(): string {\n return this.attr.ID;\n }\n\n get class(): string {\n return this.attr.CLASS;\n }\n\n get startDate(): Date {\n return this._startDate;\n }\n\n get endDate(): Date | null {\n if (this._endDate) {\n return this._endDate;\n }\n const duration = this.duration;\n if (duration !== null) {\n return new Date(this._startDate.getTime() + duration * 1000);\n }\n return null;\n }\n\n get duration(): number | null {\n if (DateRangeAttribute.DURATION in this.attr) {\n const duration = this.attr.decimalFloatingPoint(\n DateRangeAttribute.DURATION,\n );\n if (Number.isFinite(duration)) {\n return duration;\n }\n } else if (this._endDate) {\n return (this._endDate.getTime() - this._startDate.getTime()) / 1000;\n }\n return null;\n }\n\n get plannedDuration(): number | null {\n if (DateRangeAttribute.PLANNED_DURATION in this.attr) {\n return this.attr.decimalFloatingPoint(\n DateRangeAttribute.PLANNED_DURATION,\n );\n }\n return null;\n }\n\n get endOnNext(): boolean {\n return this.attr.bool(DateRangeAttribute.END_ON_NEXT);\n }\n\n get isValid(): boolean {\n return (\n !!this.id &&\n !this._badValueForSameId &&\n Number.isFinite(this.startDate.getTime()) &&\n (this.duration === null || this.duration >= 0) &&\n (!this.endOnNext || !!this.class)\n );\n }\n}\n","import type {\n HlsPerformanceTiming,\n HlsProgressivePerformanceTiming,\n LoaderStats,\n} from '../types/loader';\n\nexport class LoadStats implements LoaderStats {\n aborted: boolean = false;\n loaded: number = 0;\n retry: number = 0;\n total: number = 0;\n chunkCount: number = 0;\n bwEstimate: number = 0;\n loading: HlsProgressivePerformanceTiming = { start: 0, first: 0, end: 0 };\n parsing: HlsPerformanceTiming = { start: 0, end: 0 };\n buffering: HlsProgressivePerformanceTiming = { start: 0, first: 0, end: 0 };\n}\n","import { buildAbsoluteURL } from 'url-toolkit';\nimport { LevelKey } from './level-key';\nimport { LoadStats } from './load-stats';\nimport { AttrList } from '../utils/attr-list';\nimport type {\n FragmentLoaderContext,\n KeyLoaderContext,\n Loader,\n PlaylistLevelType,\n} from '../types/loader';\nimport type { KeySystemFormats } from '../utils/mediakeys-helper';\n\nexport const enum ElementaryStreamTypes {\n AUDIO = 'audio',\n VIDEO = 'video',\n AUDIOVIDEO = 'audiovideo',\n}\n\nexport interface ElementaryStreamInfo {\n startPTS: number;\n endPTS: number;\n startDTS: number;\n endDTS: number;\n partial?: boolean;\n}\n\nexport type ElementaryStreams = Record<\n ElementaryStreamTypes,\n ElementaryStreamInfo | null\n>;\n\nexport class BaseSegment {\n private _byteRange: [number, number] | null = null;\n private _url: string | null = null;\n\n // baseurl is the URL to the playlist\n public readonly baseurl: string;\n // relurl is the portion of the URL that comes from inside the playlist.\n public relurl?: string;\n // Holds the types of data this fragment supports\n public elementaryStreams: ElementaryStreams = {\n [ElementaryStreamTypes.AUDIO]: null,\n [ElementaryStreamTypes.VIDEO]: null,\n [ElementaryStreamTypes.AUDIOVIDEO]: null,\n };\n\n constructor(baseurl: string) {\n this.baseurl = baseurl;\n }\n\n // setByteRange converts a EXT-X-BYTERANGE attribute into a two element array\n setByteRange(value: string, previous?: BaseSegment) {\n const params = value.split('@', 2);\n let start: number;\n if (params.length === 1) {\n start = previous?.byteRangeEndOffset || 0;\n } else {\n start = parseInt(params[1]);\n }\n this._byteRange = [start, parseInt(params[0]) + start];\n }\n\n get byteRange(): [number, number] | [] {\n if (!this._byteRange) {\n return [];\n }\n\n return this._byteRange;\n }\n\n get byteRangeStartOffset(): number | undefined {\n return this.byteRange[0];\n }\n\n get byteRangeEndOffset(): number | undefined {\n return this.byteRange[1];\n }\n\n get url(): string {\n if (!this._url && this.baseurl && this.relurl) {\n this._url = buildAbsoluteURL(this.baseurl, this.relurl, {\n alwaysNormalize: true,\n });\n }\n return this._url || '';\n }\n\n set url(value: string) {\n this._url = value;\n }\n}\n\n/**\n * Object representing parsed data from an HLS Segment. Found in {@link hls.js#LevelDetails.fragments}.\n */\nexport class Fragment extends BaseSegment {\n private _decryptdata: LevelKey | null = null;\n\n public rawProgramDateTime: string | null = null;\n public programDateTime: number | null = null;\n public tagList: Array<string[]> = [];\n\n // EXTINF has to be present for a m3u8 to be considered valid\n public duration: number = 0;\n // sn notates the sequence number for a segment, and if set to a string can be 'initSegment'\n public sn: number | 'initSegment' = 0;\n // levelkeys are the EXT-X-KEY tags that apply to this segment for decryption\n // core difference from the private field _decryptdata is the lack of the initialized IV\n // _decryptdata will set the IV for this segment based on the segment number in the fragment\n public levelkeys?: { [key: string]: LevelKey };\n // A string representing the fragment type\n public readonly type: PlaylistLevelType;\n // A reference to the loader. Set while the fragment is loading, and removed afterwards. Used to abort fragment loading\n public loader: Loader<FragmentLoaderContext> | null = null;\n // A reference to the key loader. Set while the key is loading, and removed afterwards. Used to abort key loading\n public keyLoader: Loader<KeyLoaderContext> | null = null;\n // The level/track index to which the fragment belongs\n public level: number = -1;\n // The continuity counter of the fragment\n public cc: number = 0;\n // The starting Presentation Time Stamp (PTS) of the fragment. Set after transmux complete.\n public startPTS?: number;\n // The ending Presentation Time Stamp (PTS) of the fragment. Set after transmux complete.\n public endPTS?: number;\n // The starting Decode Time Stamp (DTS) of the fragment. Set after transmux complete.\n public startDTS!: number;\n // The ending Decode Time Stamp (DTS) of the fragment. Set after transmux complete.\n public endDTS!: number;\n // The start time of the fragment, as listed in the manifest. Updated after transmux complete.\n public start: number = 0;\n // Set by `updateFragPTSDTS` in level-helper\n public deltaPTS?: number;\n // The maximum starting Presentation Time Stamp (audio/video PTS) of the fragment. Set after transmux complete.\n public maxStartPTS?: number;\n // The minimum ending Presentation Time Stamp (audio/video PTS) of the fragment. Set after transmux complete.\n public minEndPTS?: number;\n // Load/parse timing information\n public stats: LoadStats = new LoadStats();\n public urlId: number = 0;\n public data?: Uint8Array;\n // A flag indicating whether the segment was downloaded in order to test bitrate, and was not buffered\n public bitrateTest: boolean = false;\n // #EXTINF segment title\n public title: string | null = null;\n // The Media Initialization Section for this segment\n public initSegment: Fragment | null = null;\n // Fragment is the last fragment in the media playlist\n public endList?: boolean;\n // Fragment is marked by an EXT-X-GAP tag indicating that it does not contain media data and should not be loaded\n public gap?: boolean;\n\n constructor(type: PlaylistLevelType, baseurl: string) {\n super(baseurl);\n this.type = type;\n }\n\n get decryptdata(): LevelKey | null {\n const { levelke