vendor: 4,853 bytes, lines 1-134
1(function __HLS_WORKER_BUNDLE__(__IN_WORKER__){ 2(function (global, factory) { 3 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 4 typeof define === 'function' && define.amd ? define(factory) : 5 (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Hls = factory()); 6})(this, (function () { 'use strict'; 7 8 function _construct(t, e, r) { 9 if (_isNativeReflectConstruct()) return Reflect.construct.apply(null, arguments); 10 var o = [null]; 11 o.push.apply(o, e); 12 var p = new (t.bind.apply(t, o))(); 13 return r && _setPrototypeOf(p, r.prototype), p; 14 } 15 function _defineProperties(e, r) { 16 for (var t = 0; t < r.length; t++) { 17 var o = r[t]; 18 o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); 19 } 20 } 21 function _createClass(e, r, t) { 22 return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { 23 writable: !1 24 }), e; 25 } 26 function _defineProperty(e, r, t) { 27 return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { 28 value: t, 29 enumerable: !0, 30 configurable: !0, 31 writable: !0 32 }) : e[r] = t, e; 33 } 34 function _extends() { 35 return _extends = Object.assign ? Object.assign.bind() : function (n) { 36 for (var e = 1; e < arguments.length; e++) { 37 var t = arguments[e]; 38 for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); 39 } 40 return n; 41 }, _extends.apply(null, arguments); 42 } 43 function _getPrototypeOf(t) { 44 return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { 45 return t.__proto__ || Object.getPrototypeOf(t); 46 }, _getPrototypeOf(t); 47 } 48 function _inheritsLoose(t, o) { 49 t.prototype = Object.create(o.prototype), t.prototype.constructor = t, _setPrototypeOf(t, o); 50 } 51 function _isNativeFunction(t) { 52 try { 53 return -1 !== Function.toString.call(t).indexOf("[native code]"); 54 } catch (n) { 55 return "function" == typeof t; 56 } 57 } 58 function _isNativeReflectConstruct() { 59 try { 60 var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); 61 } catch (t) {} 62 return (_isNativeReflectConstruct = function () { 63 return !!t; 64 })(); 65 } 66 function ownKeys(e, r) { 67 var t = Object.keys(e); 68 if (Object.getOwnPropertySymbols) { 69 var o = Object.getOwnPropertySymbols(e); 70 r && (o = o.filter(function (r) { 71 return Object.getOwnPropertyDescriptor(e, r).enumerable; 72 })), t.push.apply(t, o); 73 } 74 return t; 75 } 76 function _objectSpread2(e) { 77 for (var r = 1; r < arguments.length; r++) { 78 var t = null != arguments[r] ? arguments[r] : {}; 79 r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { 80 _defineProperty(e, r, t[r]); 81 }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { 82 Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); 83 }); 84 } 85 return e; 86 } 87 function _setPrototypeOf(t, e) { 88 return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { 89 return t.__proto__ = e, t; 90 }, _setPrototypeOf(t, e); 91 } 92 function _toPrimitive(t, r) { 93 if ("object" != typeof t || !t) return t; 94 var e = t[Symbol.toPrimitive]; 95 if (void 0 !== e) { 96 var i = e.call(t, r || "default"); 97 if ("object" != typeof i) return i; 98 throw new TypeError("@@toPrimitive must return a primitive value."); 99 } 100 return ("string" === r ? String : Number)(t); 101 } 102 function _toPropertyKey(t) { 103 var i = _toPrimitive(t, "string"); 104 return "symbol" == typeof i ? i : i + ""; 105 } 106 function _wrapNativeSuper(t) { 107 var r = "function" == typeof Map ? new Map() : void 0; 108 return _wrapNativeSuper = function (t) { 109 if (null === t || !_isNativeFunction(t)) return t; 110 if ("function" != typeof t) throw new TypeError("Super expression must either be null or a function"); 111 if (void 0 !== r) { 112 if (r.has(t)) return r.get(t); 113 r.set(t, Wrapper); 114 } 115 function Wrapper() { 116 return _construct(t, arguments, _getPrototypeOf(this).constructor); 117 } 118 return Wrapper.prototype = Object.create(t.prototype, { 119 constructor: { 120 value: Wrapper, 121 enumerable: !1, 122 writable: !0, 123 configurable: !0 124 } 125 }), _setPrototypeOf(Wrapper, t); 126 }, _wrapNativeSuper(t); 127 } 128 129 function getDefaultExportFromCjs (x) { 130 return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x; 131 } 132 133 var urlToolkit = {exports: {}}; 134
vendor: 7,947 bytes, lines 135-318
135 (function (module, exports) { 136 // see https://tools.ietf.org/html/rfc1808 137 138 (function (root) { 139 var URL_REGEX = 140 /^(?=((?:[a-zA-Z0-9+\-.]+:)?))\1(?=((?:\/\/[^\/?#]*)?))\2(?=((?:(?:[^?#\/]*\/)*[^;?#\/]*)?))\3((?:;[^?#]*)?)(\?[^#]*)?(#[^]*)?$/; 141 var FIRST_SEGMENT_REGEX = /^(?=([^\/?#]*))\1([^]*)$/; 142 var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g; 143 var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/)[^\/]*(?=\/)/g; 144 145 var URLToolkit = { 146 // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or // 147 // E.g 148 // With opts.alwaysNormalize = false (default, spec compliant) 149 // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g 150 // With opts.alwaysNormalize = true (not spec compliant) 151 // http://a.com/b/cd + /e/f/../g => http://a.com/e/g 152 buildAbsoluteURL: function (baseURL, relativeURL, opts) { 153 opts = opts || {}; 154 // remove any remaining space and CRLF 155 baseURL = baseURL.trim(); 156 relativeURL = relativeURL.trim(); 157 if (!relativeURL) { 158 // 2a) If the embedded URL is entirely empty, it inherits the 159 // entire base URL (i.e., is set equal to the base URL) 160 // and we are done. 161 if (!opts.alwaysNormalize) { 162 return baseURL; 163 } 164 var basePartsForNormalise = URLToolkit.parseURL(baseURL); 165 if (!basePartsForNormalise) { 166 throw new Error('Error trying to parse base URL.'); 167 } 168 basePartsForNormalise.path = URLToolkit.normalizePath( 169 basePartsForNormalise.path 170 ); 171 return URLToolkit.buildURLFromParts(basePartsForNormalise); 172 } 173 var relativeParts = URLToolkit.parseURL(relativeURL); 174 if (!relativeParts) { 175 throw new Error('Error trying to parse relative URL.'); 176 } 177 if (relativeParts.scheme) { 178 // 2b) If the embedded URL starts with a scheme name, it is 179 // interpreted as an absolute URL and we are done. 180 if (!opts.alwaysNormalize) { 181 return relativeURL; 182 } 183 relativeParts.path = URLToolkit.normalizePath(relativeParts.path); 184 return URLToolkit.buildURLFromParts(relativeParts); 185 } 186 var baseParts = URLToolkit.parseURL(baseURL); 187 if (!baseParts) { 188 throw new Error('Error trying to parse base URL.'); 189 } 190 if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') { 191 // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc 192 // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a' 193 var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path); 194 baseParts.netLoc = pathParts[1]; 195 baseParts.path = pathParts[2]; 196 } 197 if (baseParts.netLoc && !baseParts.path) { 198 baseParts.path = '/'; 199 } 200 var builtParts = { 201 // 2c) Otherwise, the embedded URL inherits the scheme of 202 // the base URL. 203 scheme: baseParts.scheme, 204 netLoc: relativeParts.netLoc, 205 path: null, 206 params: relativeParts.params, 207 query: relativeParts.query, 208 fragment: relativeParts.fragment, 209 }; 210 if (!relativeParts.netLoc) { 211 // 3) If the embedded URL's <net_loc> is non-empty, we skip to 212 // Step 7. Otherwise, the embedded URL inherits the <net_loc> 213 // (if any) of the base URL. 214 builtParts.netLoc = baseParts.netLoc; 215 // 4) If the embedded URL path is preceded by a slash "/", the 216 // path is not relative and we skip to Step 7. 217 if (relativeParts.path[0] !== '/') { 218 if (!relativeParts.path) { 219 // 5) If the embedded URL path is empty (and not preceded by a 220 // slash), then the embedded URL inherits the base URL path 221 builtParts.path = baseParts.path; 222 // 5a) if the embedded URL's <params> is non-empty, we skip to 223 // step 7; otherwise, it inherits the <params> of the base 224 // URL (if any) and 225 if (!relativeParts.params) { 226 builtParts.params = baseParts.params; 227 // 5b) if the embedded URL's <query> is non-empty, we skip to 228 // step 7; otherwise, it inherits the <query> of the base 229 // URL (if any) and we skip to step 7. 230 if (!relativeParts.query) { 231 builtParts.query = baseParts.query; 232 } 233 } 234 } else { 235 // 6) The last segment of the base URL's path (anything 236 // following the rightmost slash "/", or the entire path if no 237 // slash is present) is removed and the embedded URL's path is 238 // appended in its place. 239 var baseURLPath = baseParts.path; 240 var newPath = 241 baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + 242 relativeParts.path; 243 builtParts.path = URLToolkit.normalizePath(newPath); 244 } 245 } 246 } 247 if (builtParts.path === null) { 248 builtParts.path = opts.alwaysNormalize 249 ? URLToolkit.normalizePath(relativeParts.path) 250 : relativeParts.path; 251 } 252 return URLToolkit.buildURLFromParts(builtParts); 253 }, 254 parseURL: function (url) { 255 var parts = URL_REGEX.exec(url); 256 if (!parts) { 257 return null; 258 } 259 return { 260 scheme: parts[1] || '', 261 netLoc: parts[2] || '', 262 path: parts[3] || '', 263 params: parts[4] || '', 264 query: parts[5] || '', 265 fragment: parts[6] || '', 266 }; 267 }, 268 normalizePath: function (path) { 269 // The following operations are 270 // then applied, in order, to the new path: 271 // 6a) All occurrences of "./", where "." is a complete path 272 // segment, are removed. 273 // 6b) If the path ends with "." as a complete path segment, 274 // that "." is removed. 275 path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, ''); 276 // 6c) All occurrences of "<segment>/../", where <segment> is a 277 // complete path segment not equal to "..", are removed. 278 // Removal of these path segments is performed iteratively, 279 // removing the leftmost matching pattern on each iteration, 280 // until no matching pattern remains. 281 // 6d) If the path ends with "<segment>/..", where <segment> is a 282 // complete path segment not equal to "..", that 283 // "<segment>/.." is removed. 284 while ( 285 path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length 286 ) {} 287 return path.split('').reverse().join(''); 288 }, 289 buildURLFromParts: function (parts) { 290 return ( 291 parts.scheme + 292 parts.netLoc + 293 parts.path + 294 parts.params + 295 parts.query + 296 parts.fragment 297 ); 298 }, 299 }; 300 301 module.exports = URLToolkit; 302 })(); 303 } (urlToolkit)); 304 305 var urlToolkitExports = urlToolkit.exports; 306 307 // https://caniuse.com/mdn-javascript_builtins_number_isfinite 308 var isFiniteNumber = Number.isFinite || function (value) { 309 return typeof value === 'number' && isFinite(value); 310 }; 311 312 // https://caniuse.com/mdn-javascript_builtins_number_issafeinteger 313 var isSafeInteger = Number.isSafeInteger || function (value) { 314 return typeof value === 'number' && Math.abs(value) <= MAX_SAFE_INTEGER; 315 }; 316 var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || 9007199254740991; 317 318 var Events = /*#__PURE__*/function (Events) {
319 Events["MEDIA_ATTACHING"] = "hlsMediaAttaching"; 320 Events["MEDIA_ATTACHED"] = "hlsMediaAttached"; 321 Events["MEDIA_DETACHING"] = "hlsMediaDetaching"; 322 Events["MEDIA_DETACHED"] = "hlsMediaDetached"; 323 Events["MEDIA_ENDED"] = "hlsMediaEnded"; 324 Events["BUFFER_RESET"] = "hlsBufferReset"; 325 Events["BUFFER_CODECS"] = "hlsBufferCodecs"; 326 Events["BUFFER_CREATED"] = "hlsBufferCreated"; 327 Events["BUFFER_APPENDING"] = "hlsBufferAppending"; 328 Events["BUFFER_APPENDED"] = "hlsBufferAppended"; 329 Events["BUFFER_EOS"] = "hlsBufferEos"; 330 Events["BUFFER_FLUSHING"] = "hlsBufferFlushing"; 331 Events["BUFFER_FLUSHED"] = "hlsBufferFlushed"; 332 Events["MANIFEST_LOADING"] = "hlsManifestLoading"; 333 Events["MANIFEST_LOADED"] = "hlsManifestLoaded"; 334 Events["MANIFEST_PARSED"] = "hlsManifestParsed"; 335 Events["LEVEL_SWITCHING"] = "hlsLevelSwitching"; 336 Events["LEVEL_SWITCHED"] = "hlsLevelSwitched"; 337 Events["LEVEL_LOADING"] = "hlsLevelLoading"; 338 Events["LEVEL_LOADED"] = "hlsLevelLoaded"; 339 Events["LEVEL_UPDATED"] = "hlsLevelUpdated"; 340 Events["LEVEL_PTS_UPDATED"] = "hlsLevelPtsUpdated"; 341 Events["LEVELS_UPDATED"] = "hlsLevelsUpdated"; 342 Events["AUDIO_TRACKS_UPDATED"] = "hlsAudioTracksUpdated"; 343 Events["AUDIO_TRACK_SWITCHING"] = "hlsAudioTrackSwitching"; 344 Events["AUDIO_TRACK_SWITCHED"] = "hlsAudioTrackSwitched"; 345 Events["AUDIO_TRACK_LOADING"] = "hlsAudioTrackLoading"; 346 Events["AUDIO_TRACK_LOADED"] = "hlsAudioTrackLoaded"; 347 Events["SUBTITLE_TRACKS_UPDATED"] = "hlsSubtitleTracksUpdated"; 348 Events["SUBTITLE_TRACKS_CLEARED"] = "hlsSubtitleTracksCleared"; 349 Events["SUBTITLE_TRACK_SWITCH"] = "hlsSubtitleTrackSwitch"; 350 Events["SUBTITLE_TRACK_LOADING"] = "hlsSubtitleTrackLoading"; 351 Events["SUBTITLE_TRACK_LOADED"] = "hlsSubtitleTrackLoaded"; 352 Events["SUBTITLE_FRAG_PROCESSED"] = "hlsSubtitleFragProcessed"; 353 Events["CUES_PARSED"] = "hlsCuesParsed"; 354 Events["NON_NATIVE_TEXT_TRACKS_FOUND"] = "hlsNonNativeTextTracksFound"; 355 Events["INIT_PTS_FOUND"] = "hlsInitPtsFound"; 356 Events["FRAG_LOADING"] = "hlsFragLoading"; 357 Events["FRAG_LOAD_EMERGENCY_ABORTED"] = "hlsFragLoadEmergencyAborted"; 358 Events["FRAG_LOADED"] = "hlsFragLoaded"; 359 Events["FRAG_DECRYPTED"] = "hlsFragDecrypted"; 360 Events["FRAG_PARSING_INIT_SEGMENT"] = "hlsFragParsingInitSegment"; 361 Events["FRAG_PARSING_USERDATA"] = "hlsFragParsingUserdata"; 362 Events["FRAG_PARSING_METADATA"] = "hlsFragParsingMetadata"; 363 Events["FRAG_PARSED"] = "hlsFragParsed"; 364 Events["FRAG_BUFFERED"] = "hlsFragBuffered"; 365 Events["FRAG_CHANGED"] = "hlsFragChanged"; 366 Events["FPS_DROP"] = "hlsFpsDrop"; 367 Events["FPS_DROP_LEVEL_CAPPING"] = "hlsFpsDropLevelCapping"; 368 Events["MAX_AUTO_LEVEL_UPDATED"] = "hlsMaxAutoLevelUpdated"; 369 Events["ERROR"] = "hlsError"; 370 Events["DESTROYING"] = "hlsDestroying"; 371 Events["KEY_LOADING"] = "hlsKeyLoading"; 372 Events["KEY_LOADED"] = "hlsKeyLoaded"; 373 Events["LIVE_BACK_BUFFER_REACHED"] = "hlsLiveBackBufferReached"; 374 Events["BACK_BUFFER_REACHED"] = "hlsBackBufferReached"; 375 Events["STEERING_MANIFEST_LOADED"] = "hlsSteeringManifestLoaded"; 376 return Events; 377 }({}); 378 379 /** 380 * Defines each Event type and payload by Event name. Used in {@link hls.js#HlsEventEmitter} to strongly type the event listener API. 381 */ 382 383 var ErrorTypes = /*#__PURE__*/function (ErrorTypes) { 384 ErrorTypes["NETWORK_ERROR"] = "networkError"; 385 ErrorTypes["MEDIA_ERROR"] = "mediaError"; 386 ErrorTypes["KEY_SYSTEM_ERROR"] = "keySystemError"; 387 ErrorTypes["MUX_ERROR"] = "muxError"; 388 ErrorTypes["OTHER_ERROR"] = "otherError"; 389 return ErrorTypes; 390 }({}); 391 var ErrorDetails = /*#__PURE__*/function (ErrorDetails) { 392 ErrorDetails["KEY_SYSTEM_NO_KEYS"] = "keySystemNoKeys"; 393 ErrorDetails["KEY_SYSTEM_NO_ACCESS"] = "keySystemNoAccess"; 394 ErrorDetails["KEY_SYSTEM_NO_SESSION"] = "keySystemNoSession"; 395 ErrorDetails["KEY_SYSTEM_NO_CONFIGURED_LICENSE"] = "keySystemNoConfiguredLicense"; 396 ErrorDetails["KEY_SYSTEM_LICENSE_REQUEST_FAILED"] = "keySystemLicenseRequestFailed"; 397 ErrorDetails["KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED"] = "keySystemServerCertificateRequestFailed"; 398 ErrorDetails["KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED"] = "keySystemServerCertificateUpdateFailed"; 399 ErrorDetails["KEY_SYSTEM_SESSION_UPDATE_FAILED"] = "keySystemSessionUpdateFailed"; 400 ErrorDetails["KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED"] = "keySystemStatusOutputRestricted"; 401 ErrorDetails["KEY_SYSTEM_STATUS_INTERNAL_ERROR"] = "keySystemStatusInternalError"; 402 ErrorDetails["MANIFEST_LOAD_ERROR"] = "manifestLoadError"; 403 ErrorDetails["MANIFEST_LOAD_TIMEOUT"] = "manifestLoadTimeOut"; 404 ErrorDetails["MANIFEST_PARSING_ERROR"] = "manifestParsingError"; 405 ErrorDetails["MANIFEST_INCOMPATIBLE_CODECS_ERROR"] = "manifestIncompatibleCodecsError"; 406 ErrorDetails["LEVEL_EMPTY_ERROR"] = "levelEmptyError"; 407 ErrorDetails["LEVEL_LOAD_ERROR"] = "levelLoadError"; 408 ErrorDetails["LEVEL_LOAD_TIMEOUT"] = "levelLoadTimeOut"; 409 ErrorDetails["LEVEL_PARSING_ERROR"] = "levelParsingError"; 410 ErrorDetails["LEVEL_SWITCH_ERROR"] = "levelSwitchError"; 411 ErrorDetails["AUDIO_TRACK_LOAD_ERROR"] = "audioTrackLoadError"; 412 ErrorDetails["AUDIO_TRACK_LOAD_TIMEOUT"] = "audioTrackLoadTimeOut"; 413 ErrorDetails["SUBTITLE_LOAD_ERROR"] = "subtitleTrackLoadError"; 414 ErrorDetails["SUBTITLE_TRACK_LOAD_TIMEOUT"] = "subtitleTrackLoadTimeOut"; 415 ErrorDetails["FRAG_LOAD_ERROR"] = "fragLoadError"; 416 ErrorDetails["FRAG_LOAD_TIMEOUT"] = "fragLoadTimeOut"; 417 ErrorDetails["FRAG_DECRYPT_ERROR"] = "fragDecryptError"; 418 ErrorDetails["FRAG_PARSING_ERROR"] = "fragParsingError"; 419 ErrorDetails["FRAG_GAP"] = "fragGap";
vendor: 4,332 bytes, lines 420-535
420 ErrorDetails["REMUX_ALLOC_ERROR"] = "remuxAllocError"; 421 ErrorDetails["KEY_LOAD_ERROR"] = "keyLoadError"; 422 ErrorDetails["KEY_LOAD_TIMEOUT"] = "keyLoadTimeOut"; 423 ErrorDetails["BUFFER_ADD_CODEC_ERROR"] = "bufferAddCodecError"; 424 ErrorDetails["BUFFER_INCOMPATIBLE_CODECS_ERROR"] = "bufferIncompatibleCodecsError"; 425 ErrorDetails["BUFFER_APPEND_ERROR"] = "bufferAppendError"; 426 ErrorDetails["BUFFER_APPENDING_ERROR"] = "bufferAppendingError"; 427 ErrorDetails["BUFFER_STALLED_ERROR"] = "bufferStalledError"; 428 ErrorDetails["BUFFER_FULL_ERROR"] = "bufferFullError"; 429 ErrorDetails["BUFFER_SEEK_OVER_HOLE"] = "bufferSeekOverHole"; 430 ErrorDetails["BUFFER_NUDGE_ON_STALL"] = "bufferNudgeOnStall"; 431 ErrorDetails["INTERNAL_EXCEPTION"] = "internalException"; 432 ErrorDetails["INTERNAL_ABORTED"] = "aborted"; 433 ErrorDetails["ATTACH_MEDIA_ERROR"] = "attachMediaError"; 434 ErrorDetails["UNKNOWN"] = "unknown"; 435 return ErrorDetails; 436 }({}); 437 438 var Logger = function Logger(label, logger) { 439 this.trace = void 0; 440 this.debug = void 0; 441 this.log = void 0; 442 this.warn = void 0; 443 this.info = void 0; 444 this.error = void 0; 445 var lb = "[" + label + "]:"; 446 this.trace = noop; 447 this.debug = logger.debug.bind(null, lb); 448 this.log = logger.log.bind(null, lb); 449 this.warn = logger.warn.bind(null, lb); 450 this.info = logger.info.bind(null, lb); 451 this.error = logger.error.bind(null, lb); 452 }; 453 var noop = function noop() {}; 454 var fakeLogger = { 455 trace: noop, 456 debug: noop, 457 log: noop, 458 warn: noop, 459 info: noop, 460 error: noop 461 }; 462 function createLogger() { 463 return _extends({}, fakeLogger); 464 } 465 466 // let lastCallTime; 467 // function formatMsgWithTimeInfo(type, msg) { 468 // const now = Date.now(); 469 // const diff = lastCallTime ? '+' + (now - lastCallTime) : '0'; 470 // lastCallTime = now; 471 // msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )'; 472 // return msg; 473 // } 474 475 function consolePrintFn(type, id) { 476 var func = self.console[type]; 477 return func ? func.bind(self.console, ('') + "[" + type + "] >") : noop; 478 } 479 function getLoggerFn(key, debugConfig, id) { 480 return debugConfig[key] ? debugConfig[key].bind(debugConfig) : consolePrintFn(key); 481 } 482 var exportedLogger = createLogger(); 483 function enableLogs(debugConfig, context, id) { 484 // check that console is available 485 var newLogger = createLogger(); 486 if (typeof console === 'object' && debugConfig === true || typeof debugConfig === 'object') { 487 var keys = [ 488 // Remove out from list here to hard-disable a log-level 489 // 'trace', 490 'debug', 'log', 'info', 'warn', 'error']; 491 keys.forEach(function (key) { 492 newLogger[key] = getLoggerFn(key, debugConfig); 493 }); 494 // Some browsers don't allow to use bind on console object anyway 495 // fallback to default if needed 496 try { 497 newLogger.log("Debug logs enabled for \"" + context + "\" in hls.js version " + undefined); 498 } catch (e) { 499 /* log fn threw an exception. All logger methods are no-ops. */ 500 return createLogger(); 501 } 502 // global exported logger uses the same functions as new logger without `id` 503 keys.forEach(function (key) { 504 exportedLogger[key] = getLoggerFn(key, debugConfig); 505 }); 506 } else { 507 // Reset global exported logger 508 _extends(exportedLogger, newLogger); 509 } 510 return newLogger; 511 } 512 var logger = exportedLogger; 513 514 var DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/; 515 var ATTR_LIST_REGEX = /(.+?)=(".*?"|.*?)(?:,|$)/g; 516 517 // adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js 518 var AttrList = /*#__PURE__*/function () { 519 function AttrList(attrs, parsed) { 520 if (typeof attrs === 'string') { 521 attrs = AttrList.parseAttrList(attrs, parsed); 522 } 523 _extends(this, attrs); 524 } 525 var _proto = AttrList.prototype; 526 _proto.decimalInteger = function decimalInteger(attrName) { 527 var intValue = parseInt(this[attrName], 10); 528 if (intValue > Number.MAX_SAFE_INTEGER) { 529 return Infinity; 530 } 531 return intValue; 532 }; 533 _proto.hexadecimalInteger = function hexadecimalInteger(attrName) { 534 if (this[attrName]) { 535 var stringValue = (this[attrName] || '0x').slice(2);
vendor: 3,810 bytes, lines 536-643
536 stringValue = (stringValue.length & 1 ? '0' : '') + stringValue; 537 var value = new Uint8Array(stringValue.length / 2); 538 for (var i = 0; i < stringValue.length / 2; i++) { 539 value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16); 540 } 541 return value; 542 } else { 543 return null; 544 } 545 }; 546 _proto.hexadecimalIntegerAsNumber = function hexadecimalIntegerAsNumber(attrName) { 547 var intValue = parseInt(this[attrName], 16); 548 if (intValue > Number.MAX_SAFE_INTEGER) { 549 return Infinity; 550 } 551 return intValue; 552 }; 553 _proto.decimalFloatingPoint = function decimalFloatingPoint(attrName) { 554 return parseFloat(this[attrName]); 555 }; 556 _proto.optionalFloat = function optionalFloat(attrName, defaultValue) { 557 var value = this[attrName]; 558 return value ? parseFloat(value) : defaultValue; 559 }; 560 _proto.enumeratedString = function enumeratedString(attrName) { 561 return this[attrName]; 562 }; 563 _proto.enumeratedStringList = function enumeratedStringList(attrName, dict) { 564 var attrValue = this[attrName]; 565 return (attrValue ? attrValue.split(/[ ,]+/) : []).reduce(function (result, identifier) { 566 result[identifier.toLowerCase()] = true; 567 return result; 568 }, dict); 569 }; 570 _proto.bool = function bool(attrName) { 571 return this[attrName] === 'YES'; 572 }; 573 _proto.decimalResolution = function decimalResolution(attrName) { 574 var res = DECIMAL_RESOLUTION_REGEX.exec(this[attrName]); 575 if (res === null) { 576 return undefined; 577 } 578 return { 579 width: parseInt(res[1], 10), 580 height: parseInt(res[2], 10) 581 }; 582 }; 583 AttrList.parseAttrList = function parseAttrList(input, parsed) { 584 var match; 585 var attrs = {}; 586 var quote = '"'; 587 ATTR_LIST_REGEX.lastIndex = 0; 588 while ((match = ATTR_LIST_REGEX.exec(input)) !== null) { 589 var name = match[1].trim(); 590 var value = match[2]; 591 var quotedString = value.indexOf(quote) === 0 && value.lastIndexOf(quote) === value.length - 1; 592 var hexadecimalSequence = false; 593 if (quotedString) { 594 value = value.slice(1, -1); 595 } else { 596 switch (name) { 597 case 'IV': 598 case 'SCTE35-CMD': 599 case 'SCTE35-IN': 600 case 'SCTE35-OUT': 601 hexadecimalSequence = true; 602 } 603 } 604 if (parsed && (quotedString || hexadecimalSequence)) ; else if (!hexadecimalSequence && !quotedString) { 605 switch (name) { 606 case 'CLOSED-CAPTIONS': 607 if (value === 'NONE') { 608 break; 609 } 610 // falls through 611 case 'ALLOWED-CPC': 612 case 'CLASS': 613 case 'ASSOC-LANGUAGE': 614 case 'AUDIO': 615 case 'BYTERANGE': 616 case 'CHANNELS': 617 case 'CHARACTERISTICS': 618 case 'CODECS': 619 case 'DATA-ID': 620 case 'END-DATE': 621 case 'GROUP-ID': 622 case 'ID': 623 case 'IMPORT': 624 case 'INSTREAM-ID': 625 case 'KEYFORMAT': 626 case 'KEYFORMATVERSIONS': 627 case 'LANGUAGE': 628 case 'NAME': 629 case 'PATHWAY-ID': 630 case 'QUERYPARAM': 631 case 'RECENTLY-REMOVED-DATERANGES': 632 case 'SERVER-URI': 633 case 'STABLE-RENDITION-ID': 634 case 'STABLE-VARIANT-ID': 635 case 'START-DATE': 636 case 'SUBTITLES': 637 case 'SUPPLEMENTAL-CODECS': 638 case 'URI': 639 case 'VALUE': 640 case 'VIDEO': 641 case 'X-ASSET-LIST': 642 case 'X-ASSET-URI': 643 // Since we are not checking tag:attribute combination, just warn rather than ignoring attribute
vendor: 10,398 bytes, lines 644-928
644 logger.warn(input + ": attribute " + name + " is missing quotes"); 645 // continue; 646 } 647 } 648 attrs[name] = value; 649 } 650 return attrs; 651 }; 652 return _createClass(AttrList, [{ 653 key: "clientAttrs", 654 get: function get() { 655 return Object.keys(this).filter(function (attr) { 656 return attr.substring(0, 2) === 'X-'; 657 }); 658 } 659 }]); 660 }(); 661 662 // Avoid exporting const enum so that these values can be inlined 663 664 var CLASS_INTERSTITIAL = 'com.apple.hls.interstitial'; 665 function isDateRangeCueAttribute(attrName) { 666 return attrName !== "ID" && attrName !== "CLASS" && attrName !== "CUE" && attrName !== "START-DATE" && attrName !== "DURATION" && attrName !== "END-DATE" && attrName !== "END-ON-NEXT"; 667 } 668 function isSCTE35Attribute(attrName) { 669 return attrName === "SCTE35-OUT" || attrName === "SCTE35-IN" || attrName === "SCTE35-CMD"; 670 } 671 var DateRange = /*#__PURE__*/function () { 672 function DateRange(dateRangeAttr, dateRangeWithSameId, tagCount) { 673 var _dateRangeWithSameId$; 674 if (tagCount === void 0) { 675 tagCount = 0; 676 } 677 this.attr = void 0; 678 this.tagAnchor = void 0; 679 this.tagOrder = void 0; 680 this._startDate = void 0; 681 this._endDate = void 0; 682 this._cue = void 0; 683 this._badValueForSameId = void 0; 684 this.tagAnchor = (dateRangeWithSameId == null ? void 0 : dateRangeWithSameId.tagAnchor) || null; 685 this.tagOrder = (_dateRangeWithSameId$ = dateRangeWithSameId == null ? void 0 : dateRangeWithSameId.tagOrder) != null ? _dateRangeWithSameId$ : tagCount; 686 if (dateRangeWithSameId) { 687 var previousAttr = dateRangeWithSameId.attr; 688 for (var key in previousAttr) { 689 if (Object.prototype.hasOwnProperty.call(dateRangeAttr, key) && dateRangeAttr[key] !== previousAttr[key]) { 690 logger.warn("DATERANGE tag attribute: \"" + key + "\" does not match for tags with ID: \"" + dateRangeAttr.ID + "\""); 691 this._badValueForSameId = key; 692 break; 693 } 694 } 695 // Merge DateRange tags with the same ID 696 dateRangeAttr = _extends(new AttrList({}), previousAttr, dateRangeAttr); 697 } 698 this.attr = dateRangeAttr; 699 this._startDate = dateRangeWithSameId ? dateRangeWithSameId.startDate : new Date(dateRangeAttr["START-DATE"]); 700 if ("END-DATE" in this.attr) { 701 var endDate = (dateRangeWithSameId == null ? void 0 : dateRangeWithSameId.endDate) || new Date(this.attr["END-DATE"]); 702 if (isFiniteNumber(endDate.getTime())) { 703 this._endDate = endDate; 704 } 705 } 706 } 707 return _createClass(DateRange, [{ 708 key: "id", 709 get: function get() { 710 return this.attr.ID; 711 } 712 }, { 713 key: "class", 714 get: function get() { 715 return this.attr.CLASS; 716 } 717 }, { 718 key: "cue", 719 get: function get() { 720 var _cue = this._cue; 721 if (_cue === undefined) { 722 return this._cue = this.attr.enumeratedStringList(this.attr.CUE ? 'CUE' : 'X-CUE', { 723 pre: false, 724 post: false, 725 once: false 726 }); 727 } 728 return _cue; 729 } 730 }, { 731 key: "startTime", 732 get: function get() { 733 var tagAnchor = this.tagAnchor; 734 // eslint-disable-next-line @typescript-eslint/prefer-optional-chain 735 if (tagAnchor === null || tagAnchor.programDateTime === null) { 736 logger.warn("Expected tagAnchor Fragment with PDT set for DateRange \"" + this.id + "\": " + tagAnchor); 737 return NaN; 738 } 739 return tagAnchor.start + (this.startDate.getTime() - tagAnchor.programDateTime) / 1000; 740 } 741 }, { 742 key: "startDate", 743 get: function get() { 744 return this._startDate; 745 } 746 }, { 747 key: "endDate", 748 get: function get() { 749 if (this._endDate) { 750 return this._endDate; 751 } 752 var duration = this.duration; 753 if (duration !== null) { 754 return new Date(this._startDate.getTime() + duration * 1000); 755 } 756 return null; 757 } 758 }, { 759 key: "duration", 760 get: function get() { 761 if ("DURATION" in this.attr) { 762 var duration = this.attr.decimalFloatingPoint("DURATION"); 763 if (isFiniteNumber(duration)) { 764 return duration; 765 } 766 } else if (this._endDate) { 767 return (this._endDate.getTime() - this._startDate.getTime()) / 1000; 768 } 769 return null; 770 } 771 }, { 772 key: "plannedDuration", 773 get: function get() { 774 if ("PLANNED-DURATION" in this.attr) { 775 return this.attr.decimalFloatingPoint("PLANNED-DURATION"); 776 } 777 return null; 778 } 779 }, { 780 key: "endOnNext", 781 get: function get() { 782 return this.attr.bool("END-ON-NEXT"); 783 } 784 }, { 785 key: "isInterstitial", 786 get: function get() { 787 return this.class === CLASS_INTERSTITIAL; 788 } 789 }, { 790 key: "isValid", 791 get: function get() { 792 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); 793 } 794 }]); 795 }(); 796 797 var LoadStats = function LoadStats() { 798 this.aborted = false; 799 this.loaded = 0; 800 this.retry = 0; 801 this.total = 0; 802 this.chunkCount = 0; 803 this.bwEstimate = 0; 804 this.loading = { 805 start: 0, 806 first: 0, 807 end: 0 808 }; 809 this.parsing = { 810 start: 0, 811 end: 0 812 }; 813 this.buffering = { 814 start: 0, 815 first: 0, 816 end: 0 817 }; 818 }; 819 820 var ElementaryStreamTypes = { 821 AUDIO: "audio", 822 VIDEO: "video", 823 AUDIOVIDEO: "audiovideo" 824 }; 825 var BaseSegment = /*#__PURE__*/function () { 826 function BaseSegment(baseurl) { 827 var _this$elementaryStrea; 828 this._byteRange = null; 829 this._url = null; 830 // baseurl is the URL to the playlist 831 this.baseurl = void 0; 832 // relurl is the portion of the URL that comes from inside the playlist. 833 this.relurl = void 0; 834 // Holds the types of data this fragment supports 835 this.elementaryStreams = (_this$elementaryStrea = {}, _this$elementaryStrea[ElementaryStreamTypes.AUDIO] = null, _this$elementaryStrea[ElementaryStreamTypes.VIDEO] = null, _this$elementaryStrea[ElementaryStreamTypes.AUDIOVIDEO] = null, _this$elementaryStrea); 836 this.baseurl = baseurl; 837 } 838 839 // setByteRange converts a EXT-X-BYTERANGE attribute into a two element array 840 var _proto = BaseSegment.prototype; 841 _proto.setByteRange = function setByteRange(value, previous) { 842 var params = value.split('@', 2); 843 var start; 844 if (params.length === 1) { 845 start = (previous == null ? void 0 : previous.byteRangeEndOffset) || 0; 846 } else { 847 start = parseInt(params[1]); 848 } 849 this._byteRange = [start, parseInt(params[0]) + start]; 850 }; 851 return _createClass(BaseSegment, [{ 852 key: "byteRange", 853 get: function get() { 854 if (!this._byteRange) { 855 return []; 856 } 857 return this._byteRange; 858 } 859 }, { 860 key: "byteRangeStartOffset", 861 get: function get() { 862 return this.byteRange[0]; 863 } 864 }, { 865 key: "byteRangeEndOffset", 866 get: function get() { 867 return this.byteRange[1]; 868 } 869 }, { 870 key: "url", 871 get: function get() { 872 if (!this._url && this.baseurl && this.relurl) { 873 this._url = urlToolkitExports.buildAbsoluteURL(this.baseurl, this.relurl, { 874 alwaysNormalize: true 875 }); 876 } 877 return this._url || ''; 878 }, 879 set: function set(value) { 880 this._url = value; 881 } 882 }]); 883 }(); 884 /** 885 * Object representing parsed data from an HLS Segment. Found in {@link hls.js#LevelDetails.fragments}. 886 */ 887 var Fragment = /*#__PURE__*/function (_BaseSegment2) { 888 function Fragment(type, baseurl) { 889 var _this; 890 _this = _BaseSegment2.call(this, baseurl) || this; 891 _this._decryptdata = null; 892 _this.rawProgramDateTime = null; 893 _this.programDateTime = null; 894 _this.tagList = []; 895 // EXTINF has to be present for a m3u8 to be considered valid 896 _this.duration = 0; 897 // sn notates the sequence number for a segment, and if set to a string can be 'initSegment' 898 _this.sn = 0; 899 // levelkeys are the EXT-X-KEY tags that apply to this segment for decryption 900 // core difference from the private field _decryptdata is the lack of the initialized IV 901 // _decryptdata will set the IV for this segment based on the segment number in the fragment 902 _this.levelkeys = void 0; 903 // A string representing the fragment type 904 _this.type = void 0; 905 // A reference to the loader. Set while the fragment is loading, and removed afterwards. Used to abort fragment loading 906 _this.loader = null; 907 // A reference to the key loader. Set while the key is loading, and removed afterwards. Used to abort key loading 908 _this.keyLoader = null; 909 // The level/track index to which the fragment belongs 910 _this.level = -1; 911 // The continuity counter of the fragment 912 _this.cc = 0; 913 // The starting Presentation Time Stamp (PTS) of the fragment. Set after transmux complete. 914 _this.startPTS = void 0; 915 // The ending Presentation Time Stamp (PTS) of the fragment. Set after transmux complete. 916 _this.endPTS = void 0; 917 // The starting Decode Time Stamp (DTS) of the fragment. Set after transmux complete. 918 _this.startDTS = void 0; 919 // The ending Decode Time Stamp (DTS) of the fragment. Set after transmux complete. 920 _this.endDTS = void 0; 921 // The start time of the fragment, as listed in the manifest. Updated after transmux complete. 922 _this.start = 0; 923 // Set by `updateFragPTSDTS` in level-helper 924 _this.deltaPTS = void 0; 925 // The maximum starting Presentation Time Stamp (audio/video PTS) of the fragment. Set after transmux complete. 926 _this.maxStartPTS = void 0; 927 // The minimum ending Presentation Time Stamp (audio/video PTS) of the fragment. Set after transmux complete. 928 _this.minEndPTS = void 0;
929 // Load/parse timing information 930 _this.stats = new LoadStats(); 931 // Init Segment bytes (unset for media segments) 932 _this.data = void 0; 933 // A flag indicating whether the segment was downloaded in order to test bitrate, and was not buffered 934 _this.bitrateTest = false; 935 // #EXTINF segment title 936 _this.title = null; 937 // The Media Initialization Section for this segment 938 _this.initSegment = null; 939 // Fragment is the last fragment in the media playlist 940 _this.endList = void 0; 941 // Fragment is marked by an EXT-X-GAP tag indicating that it does not contain media data and should not be loaded 942 _this.gap = void 0; 943 // Deprecated 944 _this.urlId = 0; 945 _this.type = type; 946 return _this; 947 } 948 _inheritsLoose(Fragment, _BaseSegment2); 949 var _proto2 = Fragment.prototype; 950 _proto2.setKeyFormat = function setKeyFormat(keyFormat) { 951 if (this.levelkeys) { 952 var key = this.levelkeys[keyFormat]; 953 if (key && !this._decryptdata) { 954 this._decryptdata = key.getDecryptData(this.sn); 955 } 956 } 957 }; 958 _proto2.abortRequests = function abortRequests() { 959 var _this$loader, _this$keyLoader; 960 (_this$loader = this.loader) == null ? void 0 : _this$loader.abort(); 961 (_this$keyLoader = this.keyLoader) == null ? void 0 : _this$keyLoader.abort(); 962 }; 963 _proto2.setElementaryStreamInfo = function setElementaryStreamInfo(type, startPTS, endPTS, startDTS, endDTS, partial) { 964 if (partial === void 0) { 965 partial = false; 966 } 967 var elementaryStreams = this.elementaryStreams; 968 var info = elementaryStreams[type]; 969 if (!info) { 970 elementaryStreams[type] = { 971 startPTS: startPTS, 972 endPTS: endPTS, 973 startDTS: startDTS, 974 endDTS: endDTS, 975 partial: partial 976 }; 977 return; 978 } 979 info.startPTS = Math.min(info.startPTS, startPTS); 980 info.endPTS = Math.max(info.endPTS, endPTS); 981 info.startDTS = Math.min(info.startDTS, startDTS); 982 info.endDTS = Math.max(info.endDTS, endDTS); 983 }; 984 _proto2.clearElementaryStreamInfo = function clearElementaryStreamInfo() { 985 var elementaryStreams = this.elementaryStreams; 986 elementaryStreams[ElementaryStreamTypes.AUDIO] = null; 987 elementaryStreams[ElementaryStreamTypes.VIDEO] = null; 988 elementaryStreams[ElementaryStreamTypes.AUDIOVIDEO] = null; 989 }; 990 return _createClass(Fragment, [{ 991 key: "decryptdata", 992 get: function get() { 993 var levelkeys = this.levelkeys; 994 if (!levelkeys && !this._decryptdata) { 995 return null; 996 } 997 if (!this._decryptdata && this.levelkeys && !this.levelkeys.NONE) { 998 var key = this.levelkeys.identity; 999 if (key) { 1000 this._decryptdata = key.getDecryptData(this.sn); 1001 } else { 1002 var keyFormats = Object.keys(this.levelkeys); 1003 if (keyFormats.length === 1) { 1004 return this._decryptdata = this.levelkeys[keyFormats[0]].getDecryptData(this.sn); 1005 } 1006 } 1007 } 1008 return this._decryptdata; 1009 } 1010 }, { 1011 key: "end", 1012 get: function get() { 1013 return this.start + this.duration; 1014 } 1015 }, { 1016 key: "endProgramDateTime", 1017 get: function get() { 1018 if (this.programDateTime === null) { 1019 return null; 1020 } 1021 if (!isFiniteNumber(this.programDateTime)) { 1022 return null; 1023 } 1024 var duration = !isFiniteNumber(this.duration) ? 0 : this.duration; 1025 return this.programDateTime + duration * 1000; 1026 } 1027 }, { 1028 key: "encrypted", 1029 get: function get() { 1030 var _this$_decryptdata; 1031 // At the m3u8-parser level we need to add support for manifest signalled keyformats 1032 // when we want the fragment to start reporting that it is encrypted. 1033 // Currently, keyFormat will only be set for identity keys 1034 if ((_this$_decryptdata = this._decryptdata) != null && _this$_decryptdata.encrypted) { 1035 return true; 1036 } else if (this.levelkeys) { 1037 var keyFormats = Object.keys(this.levelkeys); 1038 var len = keyFormats.length; 1039 if (len > 1 || len === 1 && this.levelkeys[keyFormats[0]].encrypted) { 1040 return true; 1041 } 1042 } 1043 return false;
vendor: 4,554 bytes, lines 1044-1181
1044 } 1045 }]); 1046 }(BaseSegment); 1047 1048 /** 1049 * Object representing parsed data from an HLS Partial Segment. Found in {@link hls.js#LevelDetails.partList}. 1050 */ 1051 var Part = /*#__PURE__*/function (_BaseSegment3) { 1052 function Part(partAttrs, frag, baseurl, index, previous) { 1053 var _this2; 1054 _this2 = _BaseSegment3.call(this, baseurl) || this; 1055 _this2.fragOffset = 0; 1056 _this2.duration = 0; 1057 _this2.gap = false; 1058 _this2.independent = false; 1059 _this2.relurl = void 0; 1060 _this2.fragment = void 0; 1061 _this2.index = void 0; 1062 _this2.stats = new LoadStats(); 1063 _this2.duration = partAttrs.decimalFloatingPoint('DURATION'); 1064 _this2.gap = partAttrs.bool('GAP'); 1065 _this2.independent = partAttrs.bool('INDEPENDENT'); 1066 _this2.relurl = partAttrs.enumeratedString('URI'); 1067 _this2.fragment = frag; 1068 _this2.index = index; 1069 var byteRange = partAttrs.enumeratedString('BYTERANGE'); 1070 if (byteRange) { 1071 _this2.setByteRange(byteRange, previous); 1072 } 1073 if (previous) { 1074 _this2.fragOffset = previous.fragOffset + previous.duration; 1075 } 1076 return _this2; 1077 } 1078 _inheritsLoose(Part, _BaseSegment3); 1079 return _createClass(Part, [{ 1080 key: "start", 1081 get: function get() { 1082 return this.fragment.start + this.fragOffset; 1083 } 1084 }, { 1085 key: "end", 1086 get: function get() { 1087 return this.start + this.duration; 1088 } 1089 }, { 1090 key: "loaded", 1091 get: function get() { 1092 var elementaryStreams = this.elementaryStreams; 1093 return !!(elementaryStreams.audio || elementaryStreams.video || elementaryStreams.audiovideo); 1094 } 1095 }]); 1096 }(BaseSegment); 1097 1098 var DEFAULT_TARGET_DURATION = 10; 1099 1100 /** 1101 * Object representing parsed data from an HLS Media Playlist. Found in {@link hls.js#Level.details}. 1102 */ 1103 var LevelDetails = /*#__PURE__*/function () { 1104 function LevelDetails(baseUrl) { 1105 this.PTSKnown = false; 1106 this.alignedSliding = false; 1107 this.averagetargetduration = void 0; 1108 this.endCC = 0; 1109 this.endSN = 0; 1110 this.fragments = void 0; 1111 this.fragmentHint = void 0; 1112 this.partList = null; 1113 this.dateRanges = void 0; 1114 this.dateRangeTagCount = 0; 1115 this.live = true; 1116 this.ageHeader = 0; 1117 this.advancedDateTime = void 0; 1118 this.updated = true; 1119 this.advanced = true; 1120 this.availabilityDelay = void 0; 1121 // Manifest reload synchronization 1122 this.misses = 0; 1123 this.startCC = 0; 1124 this.startSN = 0; 1125 this.startTimeOffset = null; 1126 this.targetduration = 0; 1127 this.totalduration = 0; 1128 this.type = null; 1129 this.url = void 0; 1130 this.m3u8 = ''; 1131 this.version = null; 1132 this.canBlockReload = false; 1133 this.canSkipUntil = 0; 1134 this.canSkipDateRanges = false; 1135 this.skippedSegments = 0; 1136 this.recentlyRemovedDateranges = void 0; 1137 this.partHoldBack = 0; 1138 this.holdBack = 0; 1139 this.partTarget = 0; 1140 this.preloadHint = void 0; 1141 this.renditionReports = void 0; 1142 this.tuneInGoal = 0; 1143 this.deltaUpdateFailed = void 0; 1144 this.driftStartTime = 0; 1145 this.driftEndTime = 0; 1146 this.driftStart = 0; 1147 this.driftEnd = 0; 1148 this.encryptedFragments = void 0; 1149 this.playlistParsingError = null; 1150 this.variableList = null; 1151 this.hasVariableRefs = false; 1152 this.fragments = []; 1153 this.encryptedFragments = []; 1154 this.dateRanges = {}; 1155 this.url = baseUrl; 1156 } 1157 var _proto = LevelDetails.prototype; 1158 _proto.reloaded = function reloaded(previous) { 1159 if (!previous) { 1160 this.advanced = true; 1161 this.updated = true; 1162 return; 1163 } 1164 var partSnDiff = this.lastPartSn - previous.lastPartSn; 1165 var partIndexDiff = this.lastPartIndex - previous.lastPartIndex; 1166 this.updated = this.endSN !== previous.endSN || !!partIndexDiff || !!partSnDiff || !this.live; 1167 this.advanced = this.endSN > previous.endSN || partSnDiff > 0 || partSnDiff === 0 && partIndexDiff > 0; 1168 if (this.updated || this.advanced) { 1169 this.misses = Math.floor(previous.misses * 0.6); 1170 } else { 1171 this.misses = previous.misses + 1; 1172 } 1173 this.availabilityDelay = previous.availabilityDelay; 1174 }; 1175 return _createClass(LevelDetails, [{ 1176 key: "hasProgramDateTime", 1177 get: function get() { 1178 if (this.fragments.length) { 1179 return isFiniteNumber(this.fragments[this.fragments.length - 1].programDateTime); 1180 } 1181 return false;
vendor: 4,887 bytes, lines 1182-1348
1182 } 1183 }, { 1184 key: "levelTargetDuration", 1185 get: function get() { 1186 return this.averagetargetduration || this.targetduration || DEFAULT_TARGET_DURATION; 1187 } 1188 }, { 1189 key: "drift", 1190 get: function get() { 1191 var runTime = this.driftEndTime - this.driftStartTime; 1192 if (runTime > 0) { 1193 var runDuration = this.driftEnd - this.driftStart; 1194 return runDuration * 1000 / runTime; 1195 } 1196 return 1; 1197 } 1198 }, { 1199 key: "edge", 1200 get: function get() { 1201 return this.partEnd || this.fragmentEnd; 1202 } 1203 }, { 1204 key: "partEnd", 1205 get: function get() { 1206 var _this$partList; 1207 if ((_this$partList = this.partList) != null && _this$partList.length) { 1208 return this.partList[this.partList.length - 1].end; 1209 } 1210 return this.fragmentEnd; 1211 } 1212 }, { 1213 key: "fragmentEnd", 1214 get: function get() { 1215 var _this$fragments; 1216 if ((_this$fragments = this.fragments) != null && _this$fragments.length) { 1217 return this.fragments[this.fragments.length - 1].end; 1218 } 1219 return 0; 1220 } 1221 }, { 1222 key: "age", 1223 get: function get() { 1224 if (this.advancedDateTime) { 1225 return Math.max(Date.now() - this.advancedDateTime, 0) / 1000; 1226 } 1227 return 0; 1228 } 1229 }, { 1230 key: "lastPartIndex", 1231 get: function get() { 1232 var _this$partList2; 1233 if ((_this$partList2 = this.partList) != null && _this$partList2.length) { 1234 return this.partList[this.partList.length - 1].index; 1235 } 1236 return -1; 1237 } 1238 }, { 1239 key: "lastPartSn", 1240 get: function get() { 1241 var _this$partList3; 1242 if ((_this$partList3 = this.partList) != null && _this$partList3.length) { 1243 return this.partList[this.partList.length - 1].fragment.sn; 1244 } 1245 return this.endSN; 1246 } 1247 }]); 1248 }(); 1249 1250 var DecrypterAesMode = { 1251 cbc: 0, 1252 ctr: 1 1253 }; 1254 1255 function isFullSegmentEncryption(method) { 1256 return method === 'AES-128' || method === 'AES-256' || method === 'AES-256-CTR'; 1257 } 1258 function getAesModeFromFullSegmentMethod(method) { 1259 switch (method) { 1260 case 'AES-128': 1261 case 'AES-256': 1262 return DecrypterAesMode.cbc; 1263 case 'AES-256-CTR': 1264 return DecrypterAesMode.ctr; 1265 default: 1266 throw new Error("invalid full segment method " + method); 1267 } 1268 } 1269 1270 // This file is inserted as a shim for modules which we do not want to include into the distro. 1271 // This replacement is done in the "alias" plugin of the rollup config. 1272 var empty = undefined; 1273 var HevcVideoParser = /*@__PURE__*/getDefaultExportFromCjs(empty); 1274 1275 function sliceUint8(array, start, end) { 1276 // @ts-expect-error This polyfills IE11 usage of Uint8Array slice. 1277 // It always exists in the TypeScript definition so fails, but it fails at runtime on IE11. 1278 return Uint8Array.prototype.slice ? array.slice(start, end) : new Uint8Array(Array.prototype.slice.call(array, start, end)); 1279 } 1280 1281 // http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197 1282 // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt 1283 /* utf.js - UTF-8 <=> UTF-16 convertion 1284 * 1285 * Copyright (C) 1999 Masanao Izumo <[email protected]> 1286 * Version: 1.0 1287 * LastModified: Dec 25 1999 1288 * This library is free. You can redistribute it and/or modify it. 1289 */ 1290 /** 1291 * Converts a UTF-8 array to a string. 1292 * 1293 * @param array - The UTF-8 array to convert 1294 * 1295 * @returns The string 1296 * 1297 * @group Utils 1298 * 1299 * @beta 1300 */ 1301 function utf8ArrayToStr(array, exitOnNull) { 1302 if (exitOnNull === void 0) { 1303 exitOnNull = false; 1304 } 1305 if (typeof TextDecoder !== 'undefined') { 1306 var decoder = new TextDecoder('utf-8'); 1307 var decoded = decoder.decode(array); 1308 if (exitOnNull) { 1309 // grab up to the first null 1310 var idx = decoded.indexOf('\0'); 1311 return idx !== -1 ? decoded.substring(0, idx) : decoded; 1312 } 1313 // remove any null characters 1314 return decoded.replace(/\0/g, ''); 1315 } 1316 var len = array.length; 1317 var c; 1318 var char2; 1319 var char3; 1320 var out = ''; 1321 var i = 0; 1322 while (i < len) { 1323 c = array[i++]; 1324 if (c === 0x00 && exitOnNull) { 1325 return out; 1326 } else if (c === 0x00 || c === 0x03) { 1327 // If the character is 3 (END_OF_TEXT) or 0 (NULL) then skip it 1328 continue; 1329 } 1330 switch (c >> 4) { 1331 case 0: 1332 case 1: 1333 case 2: 1334 case 3: 1335 case 4: 1336 case 5: 1337 case 6: 1338 case 7: 1339 // 0xxxxxxx 1340 out += String.fromCharCode(c); 1341 break; 1342 case 12: 1343 case 13: 1344 // 110x xxxx 10xx xxxx 1345 char2 = array[i++]; 1346 out += String.fromCharCode((c & 0x1f) << 6 | char2 & 0x3f); 1347 break; 1348 case 14:
vendor: 5,614 bytes, lines 1349-1532
1349 // 1110 xxxx 10xx xxxx 10xx xxxx 1350 char2 = array[i++]; 1351 char3 = array[i++]; 1352 out += String.fromCharCode((c & 0x0f) << 12 | (char2 & 0x3f) << 6 | (char3 & 0x3f) << 0); 1353 break; 1354 } 1355 } 1356 return out; 1357 } 1358 1359 /** 1360 * hex dump helper class 1361 */ 1362 1363 var Hex = { 1364 hexDump: function hexDump(array) { 1365 var str = ''; 1366 for (var i = 0; i < array.length; i++) { 1367 var h = array[i].toString(16); 1368 if (h.length < 2) { 1369 h = '0' + h; 1370 } 1371 str += h; 1372 } 1373 return str; 1374 } 1375 }; 1376 1377 var UINT32_MAX$1 = Math.pow(2, 32) - 1; 1378 var push = [].push; 1379 1380 // We are using fixed track IDs for driving the MP4 remuxer 1381 // instead of following the TS PIDs. 1382 // There is no reason not to do this and some browsers/SourceBuffer-demuxers 1383 // may not like if there are TrackID "switches" 1384 // See https://github.com/video-dev/hls.js/issues/1331 1385 // Here we are mapping our internal track types to constant MP4 track IDs 1386 // With MSE currently one can only have one track of each, and we are muxing 1387 // whatever video/audio rendition in them. 1388 var RemuxerTrackIdConfig = { 1389 video: 1, 1390 audio: 2, 1391 id3: 3, 1392 text: 4 1393 }; 1394 function bin2str(data) { 1395 return String.fromCharCode.apply(null, data); 1396 } 1397 function readUint16(buffer, offset) { 1398 var val = buffer[offset] << 8 | buffer[offset + 1]; 1399 return val < 0 ? 65536 + val : val; 1400 } 1401 function readUint32(buffer, offset) { 1402 var val = readSint32(buffer, offset); 1403 return val < 0 ? 4294967296 + val : val; 1404 } 1405 function readUint64(buffer, offset) { 1406 var result = readUint32(buffer, offset); 1407 result *= Math.pow(2, 32); 1408 result += readUint32(buffer, offset + 4); 1409 return result; 1410 } 1411 function readSint32(buffer, offset) { 1412 return buffer[offset] << 24 | buffer[offset + 1] << 16 | buffer[offset + 2] << 8 | buffer[offset + 3]; 1413 } 1414 function writeUint32(buffer, offset, value) { 1415 buffer[offset] = value >> 24; 1416 buffer[offset + 1] = value >> 16 & 0xff; 1417 buffer[offset + 2] = value >> 8 & 0xff; 1418 buffer[offset + 3] = value & 0xff; 1419 } 1420 1421 // Find "moof" box 1422 function hasMoofData(data) { 1423 var end = data.byteLength; 1424 for (var i = 0; i < end;) { 1425 var size = readUint32(data, i); 1426 if (size > 8 && data[i + 4] === 0x6d && data[i + 5] === 0x6f && data[i + 6] === 0x6f && data[i + 7] === 0x66) { 1427 return true; 1428 } 1429 i = size > 1 ? i + size : end; 1430 } 1431 return false; 1432 } 1433 1434 // Find the data for a box specified by its path 1435 function findBox(data, path) { 1436 var results = []; 1437 if (!path.length) { 1438 // short-circuit the search for empty paths 1439 return results; 1440 } 1441 var end = data.byteLength; 1442 for (var i = 0; i < end;) { 1443 var size = readUint32(data, i); 1444 var type = bin2str(data.subarray(i + 4, i + 8)); 1445 var endbox = size > 1 ? i + size : end; 1446 if (type === path[0]) { 1447 if (path.length === 1) { 1448 // this is the end of the path and we've found the box we were 1449 // looking for 1450 results.push(data.subarray(i + 8, endbox)); 1451 } else { 1452 // recursively search for the next box along the path 1453 var subresults = findBox(data.subarray(i + 8, endbox), path.slice(1)); 1454 if (subresults.length) { 1455 push.apply(results, subresults); 1456 } 1457 } 1458 } 1459 i = endbox; 1460 } 1461 1462 // we've finished searching all of data 1463 return results; 1464 } 1465 function parseSegmentIndex(sidx) { 1466 var references = []; 1467 var version = sidx[0]; 1468 1469 // set initial offset, we skip the reference ID (not needed) 1470 var index = 8; 1471 var timescale = readUint32(sidx, index); 1472 index += 4; 1473 var earliestPresentationTime = 0; 1474 var firstOffset = 0; 1475 if (version === 0) { 1476 earliestPresentationTime = readUint32(sidx, index); 1477 firstOffset = readUint32(sidx, index + 4); 1478 index += 8; 1479 } else { 1480 earliestPresentationTime = readUint64(sidx, index); 1481 firstOffset = readUint64(sidx, index + 8); 1482 index += 16; 1483 } 1484 1485 // skip reserved 1486 index += 2; 1487 var startByte = sidx.length + firstOffset; 1488 var referencesCount = readUint16(sidx, index); 1489 index += 2; 1490 for (var i = 0; i < referencesCount; i++) { 1491 var referenceIndex = index; 1492 var referenceInfo = readUint32(sidx, referenceIndex); 1493 referenceIndex += 4; 1494 var referenceSize = referenceInfo & 0x7fffffff; 1495 var referenceType = (referenceInfo & 0x80000000) >>> 31; 1496 if (referenceType === 1) { 1497 logger.warn('SIDX has hierarchical references (not supported)'); 1498 return null; 1499 } 1500 var subsegmentDuration = readUint32(sidx, referenceIndex); 1501 referenceIndex += 4; 1502 references.push({ 1503 referenceSize: referenceSize, 1504 subsegmentDuration: subsegmentDuration, 1505 // unscaled 1506 info: { 1507 duration: subsegmentDuration / timescale, 1508 start: startByte, 1509 end: startByte + referenceSize - 1 1510 } 1511 }); 1512 startByte += referenceSize; 1513 1514 // Skipping 1 bit for |startsWithSap|, 3 bits for |sapType|, and 28 bits 1515 // for |sapDelta|. 1516 referenceIndex += 4; 1517 1518 // skip to next ref 1519 index = referenceIndex; 1520 } 1521 return { 1522 earliestPresentationTime: earliestPresentationTime, 1523 timescale: timescale, 1524 version: version, 1525 referencesCount: referencesCount, 1526 references: references 1527 }; 1528 } 1529 1530 /** 1531 * Parses an MP4 initialization segment and extracts stream type and 1532 * timescale values for any declared tracks. Timescale values indicate the
1533 * number of clock ticks per second to assume for time-based values 1534 * elsewhere in the MP4. 1535 * 1536 * To determine the start time of an MP4, you need two pieces of 1537 * information: the timescale unit and the earliest base media decode 1538 * time. Multiple timescales can be specified within an MP4 but the 1539 * base media decode time is always expressed in the timescale from 1540 * the media header box for the track: 1541 * ``` 1542 * moov > trak > mdia > mdhd.timescale 1543 * moov > trak > mdia > hdlr 1544 * ``` 1545 * @param initSegment the bytes of the init segment 1546 * @returns a hash of track type to timescale values or null if 1547 * the init segment is malformed. 1548 */ 1549 1550 function parseInitSegment(initSegment) { 1551 var result = []; 1552 var traks = findBox(initSegment, ['moov', 'trak']); 1553 for (var i = 0; i < traks.length; i++) { 1554 var trak = traks[i]; 1555 var tkhd = findBox(trak, ['tkhd'])[0]; 1556 if (tkhd) { 1557 var version = tkhd[0]; 1558 var trackId = readUint32(tkhd, version === 0 ? 12 : 20); 1559 var mdhd = findBox(trak, ['mdia', 'mdhd'])[0]; 1560 if (mdhd) { 1561 version = mdhd[0]; 1562 var timescale = readUint32(mdhd, version === 0 ? 12 : 20); 1563 var hdlr = findBox(trak, ['mdia', 'hdlr'])[0]; 1564 if (hdlr) { 1565 var hdlrType = bin2str(hdlr.subarray(8, 12)); 1566 var type = { 1567 soun: ElementaryStreamTypes.AUDIO, 1568 vide: ElementaryStreamTypes.VIDEO 1569 }[hdlrType]; 1570 if (type) { 1571 // Parse codec details 1572 var stsd = findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0]; 1573 var stsdData = parseStsd(stsd); 1574 result[trackId] = { 1575 timescale: timescale, 1576 type: type 1577 }; 1578 result[type] = _objectSpread2({ 1579 timescale: timescale, 1580 id: trackId 1581 }, stsdData); 1582 } 1583 } 1584 } 1585 } 1586 } 1587 var trex = findBox(initSegment, ['moov', 'mvex', 'trex']); 1588 trex.forEach(function (trex) { 1589 var trackId = readUint32(trex, 4); 1590 var track = result[trackId]; 1591 if (track) { 1592 track.default = { 1593 duration: readUint32(trex, 12), 1594 flags: readUint32(trex, 20) 1595 }; 1596 } 1597 }); 1598 return result; 1599 } 1600 function parseStsd(stsd) { 1601 var sampleEntries = stsd.subarray(8); 1602 var sampleEntriesEnd = sampleEntries.subarray(8 + 78); 1603 var fourCC = bin2str(sampleEntries.subarray(4, 8)); 1604 var codec = fourCC; 1605 var encrypted = fourCC === 'enca' || fourCC === 'encv'; 1606 if (encrypted) { 1607 var encBox = findBox(sampleEntries, [fourCC])[0]; 1608 var encBoxChildren = encBox.subarray(fourCC === 'enca' ? 28 : 78); 1609 var sinfs = findBox(encBoxChildren, ['sinf']);
vendor: 6,551 bytes, lines 1610-1781
1610 sinfs.forEach(function (sinf) { 1611 var schm = findBox(sinf, ['schm'])[0]; 1612 if (schm) { 1613 var scheme = bin2str(schm.subarray(4, 8)); 1614 if (scheme === 'cbcs' || scheme === 'cenc') { 1615 var frma = findBox(sinf, ['frma'])[0]; 1616 if (frma) { 1617 // for encrypted content codec fourCC will be in frma 1618 codec = bin2str(frma); 1619 } 1620 } 1621 } 1622 }); 1623 } 1624 switch (codec) { 1625 case 'avc1': 1626 case 'avc2': 1627 case 'avc3': 1628 case 'avc4': 1629 { 1630 // extract profile + compatibility + level out of avcC box 1631 var avcCBox = findBox(sampleEntriesEnd, ['avcC'])[0]; 1632 codec += '.' + toHex(avcCBox[1]) + toHex(avcCBox[2]) + toHex(avcCBox[3]); 1633 break; 1634 } 1635 case 'mp4a': 1636 { 1637 var codecBox = findBox(sampleEntries, [fourCC])[0]; 1638 var esdsBox = findBox(codecBox.subarray(28), ['esds'])[0]; 1639 if (esdsBox && esdsBox.length > 7) { 1640 var i = 4; 1641 // ES Descriptor tag 1642 if (esdsBox[i++] !== 0x03) { 1643 break; 1644 } 1645 i = skipBERInteger(esdsBox, i); 1646 i += 2; // skip es_id; 1647 var flags = esdsBox[i++]; 1648 if (flags & 0x80) { 1649 i += 2; // skip dependency es_id 1650 } 1651 if (flags & 0x40) { 1652 i += esdsBox[i++]; // skip URL 1653 } 1654 // Decoder config descriptor 1655 if (esdsBox[i++] !== 0x04) { 1656 break; 1657 } 1658 i = skipBERInteger(esdsBox, i); 1659 var objectType = esdsBox[i++]; 1660 if (objectType === 0x40) { 1661 codec += '.' + toHex(objectType); 1662 } else { 1663 break; 1664 } 1665 i += 12; 1666 // Decoder specific info 1667 if (esdsBox[i++] !== 0x05) { 1668 break; 1669 } 1670 i = skipBERInteger(esdsBox, i); 1671 var firstByte = esdsBox[i++]; 1672 var audioObjectType = (firstByte & 0xf8) >> 3; 1673 if (audioObjectType === 31) { 1674 audioObjectType += 1 + ((firstByte & 0x7) << 3) + ((esdsBox[i] & 0xe0) >> 5); 1675 } 1676 codec += '.' + audioObjectType; 1677 } 1678 break; 1679 } 1680 case 'hvc1': 1681 case 'hev1': 1682 { 1683 var hvcCBox = findBox(sampleEntriesEnd, ['hvcC'])[0]; 1684 var profileByte = hvcCBox[1]; 1685 var profileSpace = ['', 'A', 'B', 'C'][profileByte >> 6]; 1686 var generalProfileIdc = profileByte & 0x1f; 1687 var profileCompat = readUint32(hvcCBox, 2); 1688 var tierFlag = (profileByte & 0x20) >> 5 ? 'H' : 'L'; 1689 var levelIDC = hvcCBox[12]; 1690 var constraintIndicator = hvcCBox.subarray(6, 12); 1691 codec += '.' + profileSpace + generalProfileIdc; 1692 codec += '.' + profileCompat.toString(16).toUpperCase(); 1693 codec += '.' + tierFlag + levelIDC; 1694 var constraintString = ''; 1695 for (var _i = constraintIndicator.length; _i--;) { 1696 var _byte = constraintIndicator[_i]; 1697 if (_byte || constraintString) { 1698 var encodedByte = _byte.toString(16).toUpperCase(); 1699 constraintString = '.' + encodedByte + constraintString; 1700 } 1701 } 1702 codec += constraintString; 1703 break; 1704 } 1705 case 'dvh1': 1706 case 'dvhe': 1707 { 1708 var dvcCBox = findBox(sampleEntriesEnd, ['dvcC'])[0]; 1709 var profile = dvcCBox[2] >> 1 & 0x7f; 1710 var level = dvcCBox[2] << 5 & 0x20 | dvcCBox[3] >> 3 & 0x1f; 1711 codec += '.' + addLeadingZero(profile) + '.' + addLeadingZero(level); 1712 break; 1713 } 1714 case 'vp09': 1715 { 1716 var vpcCBox = findBox(sampleEntriesEnd, ['vpcC'])[0]; 1717 var _profile = vpcCBox[4]; 1718 var _level = vpcCBox[5]; 1719 var bitDepth = vpcCBox[6] >> 4 & 0x0f; 1720 codec += '.' + addLeadingZero(_profile) + '.' + addLeadingZero(_level) + '.' + addLeadingZero(bitDepth); 1721 break; 1722 } 1723 case 'av01': 1724 { 1725 var av1CBox = findBox(sampleEntriesEnd, ['av1C'])[0]; 1726 var _profile2 = av1CBox[1] >>> 5; 1727 var _level2 = av1CBox[1] & 0x1f; 1728 var _tierFlag = av1CBox[2] >>> 7 ? 'H' : 'M'; 1729 var highBitDepth = (av1CBox[2] & 0x40) >> 6; 1730 var twelveBit = (av1CBox[2] & 0x20) >> 5; 1731 var _bitDepth = _profile2 === 2 && highBitDepth ? twelveBit ? 12 : 10 : highBitDepth ? 10 : 8; 1732 var monochrome = (av1CBox[2] & 0x10) >> 4; 1733 var chromaSubsamplingX = (av1CBox[2] & 0x08) >> 3; 1734 var chromaSubsamplingY = (av1CBox[2] & 0x04) >> 2; 1735 var chromaSamplePosition = av1CBox[2] & 0x03; 1736 // TODO: parse color_description_present_flag 1737 // default it to BT.709/limited range for now 1738 // more info https://aomediacodec.github.io/av1-isobmff/#av1codecconfigurationbox-syntax 1739 var colorPrimaries = 1; 1740 var transferCharacteristics = 1; 1741 var matrixCoefficients = 1; 1742 var videoFullRangeFlag = 0; 1743 codec += '.' + _profile2 + '.' + addLeadingZero(_level2) + _tierFlag + '.' + addLeadingZero(_bitDepth) + '.' + monochrome + '.' + chromaSubsamplingX + chromaSubsamplingY + chromaSamplePosition + '.' + addLeadingZero(colorPrimaries) + '.' + addLeadingZero(transferCharacteristics) + '.' + addLeadingZero(matrixCoefficients) + '.' + videoFullRangeFlag; 1744 break; 1745 } 1746 } 1747 return { 1748 codec: codec, 1749 encrypted: encrypted 1750 }; 1751 } 1752 function skipBERInteger(bytes, i) { 1753 var limit = i + 5; 1754 while (bytes[i++] & 0x80 && i < limit) { 1755 /* do nothing */ 1756 } 1757 return i; 1758 } 1759 function toHex(x) { 1760 return ('0' + x.toString(16).toUpperCase()).slice(-2); 1761 } 1762 function addLeadingZero(num) { 1763 return (num < 10 ? '0' : '') + num; 1764 } 1765 function patchEncyptionData(initSegment, decryptdata) { 1766 if (!initSegment || !decryptdata) { 1767 return initSegment; 1768 } 1769 var keyId = decryptdata.keyId; 1770 if (keyId && decryptdata.isCommonEncryption) { 1771 var traks = findBox(initSegment, ['moov', 'trak']); 1772 traks.forEach(function (trak) { 1773 var stsd = findBox(trak, ['mdia', 'minf', 'stbl', 'stsd'])[0]; 1774 1775 // skip the sample entry count 1776 var sampleEntries = stsd.subarray(8); 1777 var encBoxes = findBox(sampleEntries, ['enca']); 1778 var isAudio = encBoxes.length > 0; 1779 if (!isAudio) { 1780 encBoxes = findBox(sampleEntries, ['encv']); 1781 }
1782 encBoxes.forEach(function (enc) { 1783 var encBoxChildren = isAudio ? enc.subarray(28) : enc.subarray(78); 1784 var sinfBoxes = findBox(encBoxChildren, ['sinf']); 1785 sinfBoxes.forEach(function (sinf) { 1786 var tenc = parseSinf(sinf); 1787 if (tenc) { 1788 // Look for default key id (keyID offset is always 8 within the tenc box): 1789 var tencKeyId = tenc.subarray(8, 24); 1790 if (!tencKeyId.some(function (b) { 1791 return b !== 0; 1792 })) { 1793 logger.log("[eme] Patching keyId in 'enc" + (isAudio ? 'a' : 'v') + ">sinf>>tenc' box: " + Hex.hexDump(tencKeyId) + " -> " + Hex.hexDump(keyId)); 1794 tenc.set(keyId, 8); 1795 } 1796 } 1797 }); 1798 }); 1799 }); 1800 } 1801 return initSegment; 1802 } 1803 function parseSinf(sinf) { 1804 var schm = findBox(sinf, ['schm'])[0]; 1805 if (schm) { 1806 var scheme = bin2str(schm.subarray(4, 8)); 1807 if (scheme === 'cbcs' || scheme === 'cenc') { 1808 return findBox(sinf, ['schi', 'tenc'])[0]; 1809 } 1810 } 1811 return null; 1812 } 1813 1814 /** 1815 * Determine the base media decode start time, in seconds, for an MP4 1816 * fragment. If multiple fragments are specified, the earliest time is 1817 * returned. 1818 * 1819 * The base media decode time can be parsed from track fragment 1820 * metadata: 1821 * ``` 1822 * moof > traf > tfdt.baseMediaDecodeTime 1823 * ``` 1824 * It requires the timescale value from the mdhd to interpret. 1825 * 1826 * @param initData - a hash of track type to timescale values 1827 * @param fmp4 - the bytes of the mp4 fragment 1828 * @returns the earliest base media decode start time for the 1829 * fragment, in seconds 1830 */ 1831 function getStartDTS(initData, fmp4) { 1832 // we need info from two children of each track fragment box 1833 return findBox(fmp4, ['moof', 'traf']).reduce(function (result, traf) { 1834 var tfdt = findBox(traf, ['tfdt'])[0]; 1835 var version = tfdt[0]; 1836 var start = findBox(traf, ['tfhd']).reduce(function (result, tfhd) { 1837 // get the track id from the tfhd 1838 var id = readUint32(tfhd, 4); 1839 var track = initData[id]; 1840 if (track) { 1841 var baseTime = readUint32(tfdt, 4); 1842 if (version === 1) { 1843 // If value is too large, assume signed 64-bit. Negative track fragment decode times are invalid, but they exist in the wild. 1844 // This prevents large values from being used for initPTS, which can cause playlist sync issues. 1845 // https://github.com/video-dev/hls.js/issues/5303 1846 if (baseTime === UINT32_MAX$1) { 1847 logger.warn("[mp4-demuxer]: Ignoring assumed invalid signed 64-bit track fragment decode time"); 1848 return result; 1849 } 1850 baseTime *= UINT32_MAX$1 + 1; 1851 baseTime += readUint32(tfdt, 8); 1852 } 1853 // assume a 90kHz clock if no timescale was specified 1854 var scale = track.timescale || 90e3; 1855 // convert base time to seconds 1856 var startTime = baseTime / scale; 1857 if (isFiniteNumber(startTime) && (result === null || startTime < result)) { 1858 return startTime; 1859 } 1860 } 1861 return result; 1862 }, null); 1863 if (start !== null && isFiniteNumber(start) && (result === null || start < result)) { 1864 return start; 1865 } 1866 return result; 1867 }, null); 1868 } 1869 1870 /* 1871 For Reference: 1872 aligned(8) class TrackFragmentHeaderBox 1873 extends FullBox(‘tfhd’, 0, tf_flags){ 1874 unsigned int(32) track_ID; 1875 // all the following are optional fields 1876 unsigned int(64) base_data_offset; 1877 unsigned int(32) sample_description_index; 1878 unsigned int(32) default_sample_duration; 1879 unsigned int(32) default_sample_size; 1880 unsigned int(32) default_sample_flags 1881 } 1882 */ 1883 function getDuration(data, initData) { 1884 var rawDuration = 0; 1885 var videoDuration = 0; 1886 var audioDuration = 0; 1887 var trafs = findBox(data, ['moof', 'traf']); 1888 for (var i = 0; i < trafs.length; i++) { 1889 var traf = trafs[i]; 1890 // There is only one tfhd & trun per traf 1891 // This is true for CMAF style content, and we should perhaps check the ftyp 1892 // and only look for a single trun then, but for ISOBMFF we should check 1893 // for multiple track runs. 1894 var tfhd = findBox(traf, ['tfhd'])[0]; 1895 // get the track id from the tfhd 1896 var id = readUint32(tfhd, 4); 1897 var track = initData[id]; 1898 if (!track) { 1899 continue; 1900 } 1901 var trackDefault = track.default; 1902 var tfhdFlags = readUint32(tfhd, 0) | (trackDefault == null ? void 0 : trackDefault.flags); 1903 var sampleDuration = trackDefault == null ? void 0 : trackDefault.duration; 1904 if (tfhdFlags & 0x000008) { 1905 // 0x000008 indicates the presence of the default_sample_duration field 1906 if (tfhdFlags & 0x000002) { 1907 // 0x000002 indicates the presence of the sample_description_index field, which precedes default_sample_duration 1908 // If present, the default_sample_duration exists at byte offset 12 1909 sampleDuration = readUint32(tfhd, 12); 1910 } else { 1911 // Otherwise, the duration is at byte offset 8 1912 sampleDuration = readUint32(tfhd, 8); 1913 } 1914 } 1915 // assume a 90kHz clock if no timescale was specified 1916 var timescale = track.timescale || 90e3; 1917 var truns = findBox(traf, ['trun']); 1918 for (var j = 0; j < truns.length; j++) { 1919 rawDuration = computeRawDurationFromSamples(truns[j]); 1920 if (!rawDuration && sampleDuration) { 1921 var sampleCount = readUint32(truns[j], 4);
1922 rawDuration = sampleDuration * sampleCount; 1923 } 1924 if (track.type === ElementaryStreamTypes.VIDEO) { 1925 videoDuration += rawDuration / timescale; 1926 } else if (track.type === ElementaryStreamTypes.AUDIO) { 1927 audioDuration += rawDuration / timescale; 1928 } 1929 } 1930 } 1931 if (videoDuration === 0 && audioDuration === 0) { 1932 // If duration samples are not available in the traf use sidx subsegment_duration 1933 var sidxMinStart = Infinity; 1934 var sidxMaxEnd = 0; 1935 var sidxDuration = 0; 1936 var sidxs = findBox(data, ['sidx']); 1937 for (var _i2 = 0; _i2 < sidxs.length; _i2++) { 1938 var sidx = parseSegmentIndex(sidxs[_i2]); 1939 if (sidx != null && sidx.references) { 1940 sidxMinStart = Math.min(sidxMinStart, sidx.earliestPresentationTime / sidx.timescale); 1941 var subSegmentDuration = sidx.references.reduce(function (dur, ref) { 1942 return dur + ref.info.duration || 0; 1943 }, 0); 1944 sidxMaxEnd = Math.max(sidxMaxEnd, subSegmentDuration + sidx.earliestPresentationTime / sidx.timescale); 1945 sidxDuration = sidxMaxEnd - sidxMinStart; 1946 } 1947 } 1948 if (sidxDuration && isFiniteNumber(sidxDuration)) { 1949 return sidxDuration; 1950 } 1951 } 1952 if (videoDuration) { 1953 return videoDuration; 1954 } 1955 return audioDuration; 1956 } 1957 1958 /* 1959 For Reference: 1960 aligned(8) class TrackRunBox 1961 extends FullBox(‘trun’, version, tr_flags) { 1962 unsigned int(32) sample_count; 1963 // the following are optional fields 1964 signed int(32) data_offset; 1965 unsigned int(32) first_sample_flags; 1966 // all fields in the following array are optional 1967 { 1968 unsigned int(32) sample_duration; 1969 unsigned int(32) sample_size; 1970 unsigned int(32) sample_flags 1971 if (version == 0) 1972 { unsigned int(32) 1973 else 1974 { signed int(32) 1975 }[ sample_count ] 1976 } 1977 */ 1978 function computeRawDurationFromSamples(trun) { 1979 var flags = readUint32(trun, 0); 1980 // Flags are at offset 0, non-optional sample_count is at offset 4. Therefore we start 8 bytes in. 1981 // Each field is an int32, which is 4 bytes 1982 var offset = 8; 1983 // data-offset-present flag 1984 if (flags & 0x000001) { 1985 offset += 4; 1986 } 1987 // first-sample-flags-present flag 1988 if (flags & 0x000004) { 1989 offset += 4; 1990 } 1991 var duration = 0; 1992 var sampleCount = readUint32(trun, 4); 1993 for (var i = 0; i < sampleCount; i++) { 1994 // sample-duration-present flag 1995 if (flags & 0x000100) { 1996 var sampleDuration = readUint32(trun, offset); 1997 duration += sampleDuration; 1998 offset += 4; 1999 } 2000 // sample-size-present flag 2001 if (flags & 0x000200) { 2002 offset += 4; 2003 } 2004 // sample-flags-present flag 2005 if (flags & 0x000400) { 2006 offset += 4; 2007 } 2008 // sample-composition-time-offsets-present flag 2009 if (flags & 0x000800) { 2010 offset += 4; 2011 } 2012 } 2013 return duration; 2014 } 2015 function offsetStartDTS(initData, fmp4, timeOffset) { 2016 findBox(fmp4, ['moof', 'traf']).forEach(function (traf) {
vendor: 5,525 bytes, lines 2017-2155
2017 findBox(traf, ['tfhd']).forEach(function (tfhd) { 2018 // get the track id from the tfhd 2019 var id = readUint32(tfhd, 4); 2020 var track = initData[id]; 2021 if (!track) { 2022 return; 2023 } 2024 // assume a 90kHz clock if no timescale was specified 2025 var timescale = track.timescale || 90e3; 2026 // get the base media decode time from the tfdt 2027 findBox(traf, ['tfdt']).forEach(function (tfdt) { 2028 var version = tfdt[0]; 2029 var offset = timeOffset * timescale; 2030 if (offset) { 2031 var baseMediaDecodeTime = readUint32(tfdt, 4); 2032 if (version === 0) { 2033 baseMediaDecodeTime -= offset; 2034 baseMediaDecodeTime = Math.max(baseMediaDecodeTime, 0); 2035 writeUint32(tfdt, 4, baseMediaDecodeTime); 2036 } else { 2037 baseMediaDecodeTime *= Math.pow(2, 32); 2038 baseMediaDecodeTime += readUint32(tfdt, 8); 2039 baseMediaDecodeTime -= offset; 2040 baseMediaDecodeTime = Math.max(baseMediaDecodeTime, 0); 2041 var upper = Math.floor(baseMediaDecodeTime / (UINT32_MAX$1 + 1)); 2042 var lower = Math.floor(baseMediaDecodeTime % (UINT32_MAX$1 + 1)); 2043 writeUint32(tfdt, 4, upper); 2044 writeUint32(tfdt, 8, lower); 2045 } 2046 } 2047 }); 2048 }); 2049 }); 2050 } 2051 2052 // TODO: Check if the last moof+mdat pair is part of the valid range 2053 function segmentValidRange(data) { 2054 var segmentedRange = { 2055 valid: null, 2056 remainder: null 2057 }; 2058 var moofs = findBox(data, ['moof']); 2059 if (moofs.length < 2) { 2060 segmentedRange.remainder = data; 2061 return segmentedRange; 2062 } 2063 var last = moofs[moofs.length - 1]; 2064 // Offset by 8 bytes; findBox offsets the start by as much 2065 segmentedRange.valid = sliceUint8(data, 0, last.byteOffset - 8); 2066 segmentedRange.remainder = sliceUint8(data, last.byteOffset - 8); 2067 return segmentedRange; 2068 } 2069 function appendUint8Array(data1, data2) { 2070 var temp = new Uint8Array(data1.length + data2.length); 2071 temp.set(data1); 2072 temp.set(data2, data1.length); 2073 return temp; 2074 } 2075 function parseSamples(timeOffset, track) { 2076 var seiSamples = []; 2077 var videoData = track.samples; 2078 var timescale = track.timescale; 2079 var trackId = track.id; 2080 var isHEVCFlavor = false; 2081 var moofs = findBox(videoData, ['moof']); 2082 moofs.map(function (moof) { 2083 var moofOffset = moof.byteOffset - 8; 2084 var trafs = findBox(moof, ['traf']); 2085 trafs.map(function (traf) { 2086 // get the base media decode time from the tfdt 2087 var baseTime = findBox(traf, ['tfdt']).map(function (tfdt) { 2088 var version = tfdt[0]; 2089 var result = readUint32(tfdt, 4); 2090 if (version === 1) { 2091 result *= Math.pow(2, 32); 2092 result += readUint32(tfdt, 8); 2093 } 2094 return result / timescale; 2095 })[0]; 2096 if (baseTime !== undefined) { 2097 timeOffset = baseTime; 2098 } 2099 return findBox(traf, ['tfhd']).map(function (tfhd) { 2100 var id = readUint32(tfhd, 4); 2101 var tfhdFlags = readUint32(tfhd, 0) & 0xffffff; 2102 var baseDataOffsetPresent = (tfhdFlags & 0x000001) !== 0; 2103 var sampleDescriptionIndexPresent = (tfhdFlags & 0x000002) !== 0; 2104 var defaultSampleDurationPresent = (tfhdFlags & 0x000008) !== 0; 2105 var defaultSampleDuration = 0; 2106 var defaultSampleSizePresent = (tfhdFlags & 0x000010) !== 0; 2107 var defaultSampleSize = 0; 2108 var defaultSampleFlagsPresent = (tfhdFlags & 0x000020) !== 0; 2109 var tfhdOffset = 8; 2110 if (id === trackId) { 2111 if (baseDataOffsetPresent) { 2112 tfhdOffset += 8; 2113 } 2114 if (sampleDescriptionIndexPresent) { 2115 tfhdOffset += 4; 2116 } 2117 if (defaultSampleDurationPresent) { 2118 defaultSampleDuration = readUint32(tfhd, tfhdOffset); 2119 tfhdOffset += 4; 2120 } 2121 if (defaultSampleSizePresent) { 2122 defaultSampleSize = readUint32(tfhd, tfhdOffset); 2123 tfhdOffset += 4; 2124 } 2125 if (defaultSampleFlagsPresent) { 2126 tfhdOffset += 4; 2127 } 2128 if (track.type === 'video') { 2129 isHEVCFlavor = isHEVC(track.codec); 2130 } 2131 findBox(traf, ['trun']).map(function (trun) { 2132 var version = trun[0]; 2133 var flags = readUint32(trun, 0) & 0xffffff; 2134 var dataOffsetPresent = (flags & 0x000001) !== 0; 2135 var dataOffset = 0; 2136 var firstSampleFlagsPresent = (flags & 0x000004) !== 0; 2137 var sampleDurationPresent = (flags & 0x000100) !== 0; 2138 var sampleDuration = 0; 2139 var sampleSizePresent = (flags & 0x000200) !== 0; 2140 var sampleSize = 0; 2141 var sampleFlagsPresent = (flags & 0x000400) !== 0; 2142 var sampleCompositionOffsetsPresent = (flags & 0x000800) !== 0; 2143 var compositionOffset = 0; 2144 var sampleCount = readUint32(trun, 4); 2145 var trunOffset = 8; // past version, flags, and sample count 2146 2147 if (dataOffsetPresent) { 2148 dataOffset = readUint32(trun, trunOffset); 2149 trunOffset += 4; 2150 } 2151 if (firstSampleFlagsPresent) { 2152 trunOffset += 4; 2153 } 2154 var sampleOffset = dataOffset + moofOffset; 2155 for (var ix = 0;
vendor: 9,763 bytes, lines 2155-2447
2155 ix < sampleCount; ix++) { 2156 if (sampleDurationPresent) { 2157 sampleDuration = readUint32(trun, trunOffset); 2158 trunOffset += 4; 2159 } else { 2160 sampleDuration = defaultSampleDuration; 2161 } 2162 if (sampleSizePresent) { 2163 sampleSize = readUint32(trun, trunOffset); 2164 trunOffset += 4; 2165 } else { 2166 sampleSize = defaultSampleSize; 2167 } 2168 if (sampleFlagsPresent) { 2169 trunOffset += 4; 2170 } 2171 if (sampleCompositionOffsetsPresent) { 2172 if (version === 0) { 2173 compositionOffset = readUint32(trun, trunOffset); 2174 } else { 2175 compositionOffset = readSint32(trun, trunOffset); 2176 } 2177 trunOffset += 4; 2178 } 2179 if (track.type === ElementaryStreamTypes.VIDEO) { 2180 var naluTotalSize = 0; 2181 while (naluTotalSize < sampleSize) { 2182 var naluSize = readUint32(videoData, sampleOffset); 2183 sampleOffset += 4; 2184 if (isSEIMessage(isHEVCFlavor, videoData[sampleOffset])) { 2185 var data = videoData.subarray(sampleOffset, sampleOffset + naluSize); 2186 parseSEIMessageFromNALu(data, isHEVCFlavor ? 2 : 1, timeOffset + compositionOffset / timescale, seiSamples); 2187 } 2188 sampleOffset += naluSize; 2189 naluTotalSize += naluSize + 4; 2190 } 2191 } 2192 timeOffset += sampleDuration / timescale; 2193 } 2194 }); 2195 } 2196 }); 2197 }); 2198 }); 2199 return seiSamples; 2200 } 2201 function isHEVC(codec) { 2202 if (!codec) { 2203 return false; 2204 } 2205 var delimit = codec.indexOf('.'); 2206 var baseCodec = delimit < 0 ? codec : codec.substring(0, delimit); 2207 return baseCodec === 'hvc1' || baseCodec === 'hev1' || 2208 // Dolby Vision 2209 baseCodec === 'dvh1' || baseCodec === 'dvhe'; 2210 } 2211 function isSEIMessage(isHEVCFlavor, naluHeader) { 2212 if (isHEVCFlavor) { 2213 var naluType = naluHeader >> 1 & 0x3f; 2214 return naluType === 39 || naluType === 40; 2215 } else { 2216 var _naluType = naluHeader & 0x1f; 2217 return _naluType === 6; 2218 } 2219 } 2220 function parseSEIMessageFromNALu(unescapedData, headerSize, pts, samples) { 2221 var data = discardEPB(unescapedData); 2222 var seiPtr = 0; 2223 // skip nal header 2224 seiPtr += headerSize; 2225 var payloadType = 0; 2226 var payloadSize = 0; 2227 var b = 0; 2228 while (seiPtr < data.length) { 2229 payloadType = 0; 2230 do { 2231 if (seiPtr >= data.length) { 2232 break; 2233 } 2234 b = data[seiPtr++]; 2235 payloadType += b; 2236 } while (b === 0xff); 2237 2238 // Parse payload size. 2239 payloadSize = 0; 2240 do { 2241 if (seiPtr >= data.length) { 2242 break; 2243 } 2244 b = data[seiPtr++]; 2245 payloadSize += b; 2246 } while (b === 0xff); 2247 var leftOver = data.length - seiPtr; 2248 // Create a variable to process the payload 2249 var payPtr = seiPtr; 2250 2251 // Increment the seiPtr to the end of the payload 2252 if (payloadSize < leftOver) { 2253 seiPtr += payloadSize; 2254 } else if (payloadSize > leftOver) { 2255 // Some type of corruption has happened? 2256 logger.error("Malformed SEI payload. " + payloadSize + " is too small, only " + leftOver + " bytes left to parse."); 2257 // We might be able to parse some data, but let's be safe and ignore it. 2258 break; 2259 } 2260 if (payloadType === 4) { 2261 var countryCode = data[payPtr++]; 2262 if (countryCode === 181) { 2263 var providerCode = readUint16(data, payPtr); 2264 payPtr += 2; 2265 if (providerCode === 49) { 2266 var userStructure = readUint32(data, payPtr); 2267 payPtr += 4; 2268 if (userStructure === 0x47413934) { 2269 var userDataType = data[payPtr++]; 2270 2271 // Raw CEA-608 bytes wrapped in CEA-708 packet 2272 if (userDataType === 3) { 2273 var firstByte = data[payPtr++]; 2274 var totalCCs = 0x1f & firstByte; 2275 var enabled = 0x40 & firstByte; 2276 var totalBytes = enabled ? 2 + totalCCs * 3 : 0; 2277 var byteArray = new Uint8Array(totalBytes); 2278 if (enabled) { 2279 byteArray[0] = firstByte; 2280 for (var i = 1; i < totalBytes; i++) { 2281 byteArray[i] = data[payPtr++]; 2282 } 2283 } 2284 samples.push({ 2285 type: userDataType, 2286 payloadType: payloadType, 2287 pts: pts, 2288 bytes: byteArray 2289 }); 2290 } 2291 } 2292 } 2293 } 2294 } else if (payloadType === 5) { 2295 if (payloadSize > 16) { 2296 var uuidStrArray = []; 2297 for (var _i3 = 0; _i3 < 16; _i3++) { 2298 var _b = data[payPtr++].toString(16); 2299 uuidStrArray.push(_b.length == 1 ? '0' + _b : _b); 2300 if (_i3 === 3 || _i3 === 5 || _i3 === 7 || _i3 === 9) { 2301 uuidStrArray.push('-'); 2302 } 2303 } 2304 var length = payloadSize - 16; 2305 var userDataBytes = new Uint8Array(length); 2306 for (var _i4 = 0; _i4 < length; _i4++) { 2307 userDataBytes[_i4] = data[payPtr++]; 2308 } 2309 samples.push({ 2310 payloadType: payloadType, 2311 pts: pts, 2312 uuid: uuidStrArray.join(''), 2313 userData: utf8ArrayToStr(userDataBytes), 2314 userDataBytes: userDataBytes 2315 }); 2316 } 2317 } 2318 } 2319 } 2320 2321 /** 2322 * remove Emulation Prevention bytes from a RBSP 2323 */ 2324 function discardEPB(data) { 2325 var length = data.byteLength; 2326 var EPBPositions = []; 2327 var i = 1; 2328 2329 // Find all `Emulation Prevention Bytes` 2330 while (i < length - 2) { 2331 if (data[i] === 0 && data[i + 1] === 0 && data[i + 2] === 0x03) { 2332 EPBPositions.push(i + 2); 2333 i += 2; 2334 } else { 2335 i++; 2336 } 2337 } 2338 2339 // If no Emulation Prevention Bytes were found just return the original 2340 // array 2341 if (EPBPositions.length === 0) { 2342 return data; 2343 } 2344 2345 // Create a new array to hold the NAL unit data 2346 var newLength = length - EPBPositions.length; 2347 var newData = new Uint8Array(newLength); 2348 var sourceIndex = 0; 2349 for (i = 0; i < newLength; sourceIndex++, i++) { 2350 if (sourceIndex === EPBPositions[0]) { 2351 // Skip this byte 2352 sourceIndex++; 2353 // Remove this position index 2354 EPBPositions.shift(); 2355 } 2356 newData[i] = data[sourceIndex]; 2357 } 2358 return newData; 2359 } 2360 function parseEmsg(data) { 2361 var version = data[0]; 2362 var schemeIdUri = ''; 2363 var value = ''; 2364 var timeScale = 0; 2365 var presentationTimeDelta = 0; 2366 var presentationTime = 0; 2367 var eventDuration = 0; 2368 var id = 0; 2369 var offset = 0; 2370 if (version === 0) { 2371 while (bin2str(data.subarray(offset, offset + 1)) !== '\0') { 2372 schemeIdUri += bin2str(data.subarray(offset, offset + 1)); 2373 offset += 1; 2374 } 2375 schemeIdUri += bin2str(data.subarray(offset, offset + 1)); 2376 offset += 1; 2377 while (bin2str(data.subarray(offset, offset + 1)) !== '\0') { 2378 value += bin2str(data.subarray(offset, offset + 1)); 2379 offset += 1; 2380 } 2381 value += bin2str(data.subarray(offset, offset + 1)); 2382 offset += 1; 2383 timeScale = readUint32(data, 12); 2384 presentationTimeDelta = readUint32(data, 16); 2385 eventDuration = readUint32(data, 20); 2386 id = readUint32(data, 24); 2387 offset = 28; 2388 } else if (version === 1) { 2389 offset += 4; 2390 timeScale = readUint32(data, offset); 2391 offset += 4; 2392 var leftPresentationTime = readUint32(data, offset); 2393 offset += 4; 2394 var rightPresentationTime = readUint32(data, offset); 2395 offset += 4; 2396 presentationTime = Math.pow(2, 32) * leftPresentationTime + rightPresentationTime; 2397 if (!isSafeInteger(presentationTime)) { 2398 presentationTime = Number.MAX_SAFE_INTEGER; 2399 logger.warn('Presentation time exceeds safe integer limit and wrapped to max safe integer in parsing emsg box'); 2400 } 2401 eventDuration = readUint32(data, offset); 2402 offset += 4; 2403 id = readUint32(data, offset); 2404 offset += 4; 2405 while (bin2str(data.subarray(offset, offset + 1)) !== '\0') { 2406 schemeIdUri += bin2str(data.subarray(offset, offset + 1)); 2407 offset += 1; 2408 } 2409 schemeIdUri += bin2str(data.subarray(offset, offset + 1)); 2410 offset += 1; 2411 while (bin2str(data.subarray(offset, offset + 1)) !== '\0') { 2412 value += bin2str(data.subarray(offset, offset + 1)); 2413 offset += 1; 2414 } 2415 value += bin2str(data.subarray(offset, offset + 1)); 2416 offset += 1; 2417 } 2418 var payload = data.subarray(offset, data.byteLength); 2419 return { 2420 schemeIdUri: schemeIdUri, 2421 value: value, 2422 timeScale: timeScale, 2423 presentationTime: presentationTime, 2424 presentationTimeDelta: presentationTimeDelta, 2425 eventDuration: eventDuration, 2426 id: id, 2427 payload: payload 2428 }; 2429 } 2430 2431 var LevelKey = /*#__PURE__*/function () { 2432 function LevelKey(method, uri, format, formatversions, iv) { 2433 if (formatversions === void 0) { 2434 formatversions = [1]; 2435 } 2436 if (iv === void 0) { 2437 iv = null; 2438 } 2439 this.uri = void 0; 2440 this.method = void 0; 2441 this.keyFormat = void 0; 2442 this.keyFormatVersions = void 0; 2443 this.encrypted = void 0; 2444 this.isCommonEncryption = void 0; 2445 this.iv = null; 2446 this.key = null; 2447 this.keyId = null;
2448 this.pssh = null; 2449 this.method = method; 2450 this.uri = uri; 2451 this.keyFormat = format; 2452 this.keyFormatVersions = formatversions; 2453 this.iv = iv; 2454 this.encrypted = method ? method !== 'NONE' : false; 2455 this.isCommonEncryption = this.encrypted && !isFullSegmentEncryption(method); 2456 } 2457 LevelKey.clearKeyUriToKeyIdMap = function clearKeyUriToKeyIdMap() { 2458 }; 2459 var _proto = LevelKey.prototype; 2460 _proto.isSupported = function isSupported() { 2461 // If it's Segment encryption or No encryption, just select that key system 2462 if (this.method) { 2463 if (isFullSegmentEncryption(this.method) || this.method === 'NONE') { 2464 return true; 2465 } 2466 if (this.keyFormat === 'identity') { 2467 // Maintain support for clear SAMPLE-AES with MPEG-3 TS 2468 return this.method === 'SAMPLE-AES'; 2469 } 2470 } 2471 return false; 2472 }; 2473 _proto.getDecryptData = function getDecryptData(sn) { 2474 if (!this.encrypted || !this.uri) { 2475 return null; 2476 } 2477 if (isFullSegmentEncryption(this.method) && this.uri && !this.iv) { 2478 if (typeof sn !== 'number') { 2479 // We are fetching decryption data for a initialization segment 2480 // If the segment was encrypted with AES-128/256 2481 // It must have an IV defined. We cannot substitute the Segment Number in. 2482 logger.warn("missing IV for initialization segment with method=\"" + this.method + "\" - compliance issue"); 2483 2484 // Explicitly set sn to resulting value from implicit conversions 'initSegment' values for IV generation. 2485 sn = 0; 2486 } 2487 var iv = createInitializationVector(sn); 2488 var decryptdata = new LevelKey(this.method, this.uri, 'identity', this.keyFormatVersions, iv); 2489 return decryptdata; 2490 } 2491 { 2492 return this; 2493 } 2494 }; 2495 return LevelKey; 2496 }(); 2497 function createInitializationVector(segmentNumber) { 2498 var uint8View = new Uint8Array(16); 2499 for (var i = 12; i < 16; i++) { 2500 uint8View[i] = segmentNumber >> 8 * (15 - i) & 0xff; 2501 } 2502 return uint8View; 2503 } 2504 2505 /** 2506 * MediaSource helper 2507 */ 2508 2509 function getMediaSource(preferManagedMediaSource) { 2510 if (preferManagedMediaSource === void 0) { 2511 preferManagedMediaSource = true; 2512 } 2513 if (typeof self === 'undefined') return undefined; 2514 var mms = (preferManagedMediaSource || !self.MediaSource) && self.ManagedMediaSource; 2515 return mms || self.MediaSource || self.WebKitMediaSource; 2516 } 2517 function isManagedMediaSource(source) { 2518 return typeof self !== 'undefined' && source === self.ManagedMediaSource; 2519 } 2520 2521 // from http://mp4ra.org/codecs.html 2522 // values indicate codec selection preference (lower is higher priority) 2523 var sampleEntryCodesISO = { 2524 audio: { 2525 a3ds: 1, 2526 'ac-3': 0.95, 2527 'ac-4': 1, 2528 alac: 0.9, 2529 alaw: 1, 2530 dra1: 1, 2531 'dts+': 1, 2532 'dts-': 1, 2533 dtsc: 1, 2534 dtse: 1, 2535 dtsh: 1, 2536 'ec-3': 0.9, 2537 enca: 1, 2538 fLaC: 0.9, 2539 // MP4-RA listed codec entry for FLAC 2540 flac: 0.9, 2541 // legacy browser codec name for FLAC 2542 FLAC: 0.9, 2543 // some manifests may list "FLAC" with Apple's tools 2544 g719: 1, 2545 g726: 1, 2546 m4ae: 1, 2547 mha1: 1, 2548 mha2: 1, 2549 mhm1: 1, 2550 mhm2: 1, 2551 mlpa: 1, 2552 mp4a: 1, 2553 'raw ': 1, 2554 Opus: 1, 2555 opus: 1, 2556 // browsers expect this to be lowercase despite MP4RA says 'Opus' 2557 samr: 1, 2558 sawb: 1, 2559 sawp: 1, 2560 sevc: 1, 2561 sqcp: 1, 2562 ssmv: 1, 2563 twos: 1, 2564 ulaw: 1 2565 }, 2566 video: { 2567 avc1: 1, 2568 avc2: 1, 2569 avc3: 1, 2570 avc4: 1, 2571 avcp: 1, 2572 av01: 0.8, 2573 drac: 1, 2574 dva1: 1, 2575 dvav: 1, 2576 dvh1: 0.7, 2577 dvhe: 0.7, 2578 encv: 1, 2579 hev1: 0.75, 2580 hvc1: 0.75, 2581 mjp2: 1, 2582 mp4v: 1, 2583 mvc1: 1, 2584 mvc2: 1, 2585 mvc3: 1, 2586 mvc4: 1, 2587 resv: 1, 2588 rv60: 1, 2589 s263: 1, 2590 svc1: 1, 2591 svc2: 1, 2592 'vc-1': 1, 2593 vp08: 1, 2594 vp09: 0.9 2595 }, 2596 text: { 2597 stpp: 1, 2598 wvtt: 1 2599 } 2600 }; 2601 function isCodecType(codec, type) { 2602 var typeCodes = sampleEntryCodesISO[type]; 2603 return !!typeCodes && !!typeCodes[codec.slice(0, 4)]; 2604 }
2605 function areCodecsMediaSourceSupported(codecs, type, preferManagedMediaSource) { 2606 if (preferManagedMediaSource === void 0) { 2607 preferManagedMediaSource = true; 2608 } 2609 return !codecs.split(',').some(function (codec) { 2610 return !isCodecMediaSourceSupported(codec, type, preferManagedMediaSource); 2611 }); 2612 } 2613 function isCodecMediaSourceSupported(codec, type, preferManagedMediaSource) { 2614 var _MediaSource$isTypeSu; 2615 if (preferManagedMediaSource === void 0) { 2616 preferManagedMediaSource = true; 2617 } 2618 var MediaSource = getMediaSource(preferManagedMediaSource); 2619 return (_MediaSource$isTypeSu = MediaSource == null ? void 0 : MediaSource.isTypeSupported(mimeTypeForCodec(codec, type))) != null ? _MediaSource$isTypeSu : false; 2620 } 2621 function mimeTypeForCodec(codec, type) { 2622 return type + "/mp4;codecs=\"" + codec + "\""; 2623 } 2624 function videoCodecPreferenceValue(videoCodec) { 2625 if (videoCodec) { 2626 var fourCC = videoCodec.substring(0, 4); 2627 return sampleEntryCodesISO.video[fourCC]; 2628 } 2629 return 2; 2630 } 2631 function codecsSetSelectionPreferenceValue(codecSet) { 2632 return codecSet.split(',').reduce(function (num, fourCC) { 2633 var preferenceValue = sampleEntryCodesISO.video[fourCC]; 2634 if (preferenceValue) { 2635 return (preferenceValue * 2 + num) / (num ? 3 : 2); 2636 } 2637 return (sampleEntryCodesISO.audio[fourCC] + num) / (num ? 2 : 1); 2638 }, 0); 2639 } 2640 var CODEC_COMPATIBLE_NAMES = {}; 2641 function getCodecCompatibleNameLower(lowerCaseCodec, preferManagedMediaSource) { 2642 if (preferManagedMediaSource === void 0) { 2643 preferManagedMediaSource = true; 2644 } 2645 if (CODEC_COMPATIBLE_NAMES[lowerCaseCodec]) { 2646 return CODEC_COMPATIBLE_NAMES[lowerCaseCodec]; 2647 } 2648 var codecsToCheck = { 2649 // Idealy fLaC and Opus would be first (spec-compliant) but 2650 // some browsers will report that fLaC is supported then fail. 2651 // see: https://bugs.chromium.org/p/chromium/issues/detail?id=1422728 2652 flac: ['flac', 'fLaC', 'FLAC'], 2653 opus: ['opus', 'Opus'], 2654 // Replace audio codec info if browser does not support mp4a.40.34, 2655 // and demuxer can fallback to 'audio/mpeg' or 'audio/mp4;codecs="mp3"' 2656 'mp4a.40.34': ['mp3'] 2657 }[lowerCaseCodec]; 2658 for (var i = 0; i < codecsToCheck.length; i++) { 2659 var _getMediaSource; 2660 if (isCodecMediaSourceSupported(codecsToCheck[i], 'audio', preferManagedMediaSource)) { 2661 CODEC_COMPATIBLE_NAMES[lowerCaseCodec] = codecsToCheck[i]; 2662 return codecsToCheck[i]; 2663 } else if (codecsToCheck[i] === 'mp3' && (_getMediaSource = getMediaSource(preferManagedMediaSource)) != null && _getMediaSource.isTypeSupported('audio/mpeg')) { 2664 return ''; 2665 } 2666 } 2667 return lowerCaseCodec; 2668 } 2669 var AUDIO_CODEC_REGEXP = /flac|opus|mp4a\.40\.34/i; 2670 function getCodecCompatibleName(codec, preferManagedMediaSource) { 2671 if (preferManagedMediaSource === void 0) { 2672 preferManagedMediaSource = true; 2673 } 2674 return codec.replace(AUDIO_CODEC_REGEXP, function (m) { 2675 return getCodecCompatibleNameLower(m.toLowerCase(), preferManagedMediaSource); 2676 }); 2677 } 2678 function pickMostCompleteCodecName(parsedCodec, levelCodec) { 2679 // Parsing of mp4a codecs strings in mp4-tools from media is incomplete as of d8c6c7a 2680 // so use level codec is parsed codec is unavailable or incomplete 2681 if (parsedCodec && parsedCodec !== 'mp4a') { 2682 return parsedCodec; 2683 } 2684 return levelCodec ? levelCodec.split(',')[0] : levelCodec; 2685 } 2686 function convertAVC1ToAVCOTI(codec) { 2687 // Convert avc1 codec string from RFC-4281 to RFC-6381 for MediaSource.isTypeSupported 2688 // Examples: avc1.66.30 to avc1.42001e and avc1.77.30,avc1.66.30 to avc1.4d001e,avc1.42001e. 2689 var codecs = codec.split(','); 2690 for (var i = 0; i < codecs.length; i++) { 2691 var avcdata = codecs[i].split('.'); 2692 if (avcdata.length > 2) { 2693 var result = avcdata.shift() + '.'; 2694 result += parseInt(avcdata.shift()).toString(16); 2695 result += ('000' + parseInt(avcdata.shift()).toString(16)).slice(-4); 2696 codecs[i] = result; 2697 } 2698 } 2699 return codecs.join(','); 2700 } 2701 function getM2TSSupportedAudioTypes(preferManagedMediaSource) { 2702 var MediaSource = getMediaSource(preferManagedMediaSource) || {
vendor: 8,127 bytes, lines 2703-2904
2703 isTypeSupported: function isTypeSupported() { 2704 return false; 2705 } 2706 }; 2707 return { 2708 mpeg: MediaSource.isTypeSupported('audio/mpeg'), 2709 mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"'), 2710 ac3: false 2711 }; 2712 } 2713 2714 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; 2715 var MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g; 2716 var IS_MEDIA_PLAYLIST = /^#EXT(?:INF|-X-TARGETDURATION):/m; // Handle empty Media Playlist (first EXTINF not signaled, but TARGETDURATION present) 2717 2718 var LEVEL_PLAYLIST_REGEX_FAST = new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source, 2719 // duration (#EXTINF:<duration>,<title>), group 1 => duration, group 2 => title 2720 /(?!#) *(\S[^\r\n]*)/.source, 2721 // segment URI, group 3 => the URI (note newline is not eaten) 2722 /#EXT-X-BYTERANGE:*(.+)/.source, 2723 // next segment's byterange, group 4 => range spec (x@y) 2724 /#EXT-X-PROGRAM-DATE-TIME:(.+)/.source, 2725 // next segment's program date/time group 5 => the datetime spec 2726 /#.*/.source // All other non-segment oriented tags will match with all groups empty 2727 ].join('|'), 'g'); 2728 var LEVEL_PLAYLIST_REGEX_SLOW = new RegExp([/#(EXTM3U)/.source, /#EXT-X-(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('|')); 2729 var M3U8Parser = /*#__PURE__*/function () { 2730 function M3U8Parser() {} 2731 M3U8Parser.findGroup = function findGroup(groups, mediaGroupId) { 2732 for (var i = 0; i < groups.length; i++) { 2733 var group = groups[i]; 2734 if (group.id === mediaGroupId) { 2735 return group; 2736 } 2737 } 2738 }; 2739 M3U8Parser.resolve = function resolve(url, baseUrl) { 2740 return urlToolkitExports.buildAbsoluteURL(baseUrl, url, { 2741 alwaysNormalize: true 2742 }); 2743 }; 2744 M3U8Parser.isMediaPlaylist = function isMediaPlaylist(str) { 2745 return IS_MEDIA_PLAYLIST.test(str); 2746 }; 2747 M3U8Parser.parseMasterPlaylist = function parseMasterPlaylist(string, baseurl) { 2748 var hasVariableRefs = false; 2749 var parsed = { 2750 contentSteering: null, 2751 levels: [], 2752 playlistParsingError: null, 2753 sessionData: null, 2754 sessionKeys: null, 2755 startTimeOffset: null, 2756 variableList: null, 2757 hasVariableRefs: hasVariableRefs 2758 }; 2759 var levelsWithKnownCodecs = []; 2760 MASTER_PLAYLIST_REGEX.lastIndex = 0; 2761 var result; 2762 while ((result = MASTER_PLAYLIST_REGEX.exec(string)) != null) { 2763 if (result[1]) { 2764 var _level$unknownCodecs; 2765 // '#EXT-X-STREAM-INF' is found, parse level tag in group 1 2766 var attrs = new AttrList(result[1], parsed); 2767 var uri = result[2]; 2768 var level = { 2769 attrs: attrs, 2770 bitrate: attrs.decimalInteger('BANDWIDTH') || attrs.decimalInteger('AVERAGE-BANDWIDTH'), 2771 name: attrs.NAME, 2772 url: M3U8Parser.resolve(uri, baseurl) 2773 }; 2774 var resolution = attrs.decimalResolution('RESOLUTION'); 2775 if (resolution) { 2776 level.width = resolution.width; 2777 level.height = resolution.height; 2778 } 2779 setCodecs(attrs.CODECS, level); 2780 if (!((_level$unknownCodecs = level.unknownCodecs) != null && _level$unknownCodecs.length)) { 2781 levelsWithKnownCodecs.push(level); 2782 } 2783 parsed.levels.push(level); 2784 } else if (result[3]) { 2785 var tag = result[3]; 2786 var attributes = result[4]; 2787 switch (tag) { 2788 case 'SESSION-DATA': 2789 { 2790 // #EXT-X-SESSION-DATA 2791 var sessionAttrs = new AttrList(attributes, parsed); 2792 var dataId = sessionAttrs['DATA-ID']; 2793 if (dataId) { 2794 if (parsed.sessionData === null) { 2795 parsed.sessionData = {}; 2796 } 2797 parsed.sessionData[dataId] = sessionAttrs; 2798 } 2799 break; 2800 } 2801 case 'SESSION-KEY': 2802 { 2803 // #EXT-X-SESSION-KEY 2804 var sessionKey = parseKey(attributes, baseurl, parsed); 2805 if (sessionKey.encrypted && sessionKey.isSupported()) { 2806 if (parsed.sessionKeys === null) { 2807 parsed.sessionKeys = []; 2808 } 2809 parsed.sessionKeys.push(sessionKey); 2810 } else { 2811 logger.warn("[Keys] Ignoring invalid EXT-X-SESSION-KEY tag: \"" + attributes + "\""); 2812 } 2813 break; 2814 } 2815 case 'DEFINE': 2816 { 2817 break; 2818 } 2819 case 'CONTENT-STEERING': 2820 { 2821 // #EXT-X-CONTENT-STEERING 2822 var contentSteeringAttributes = new AttrList(attributes, parsed); 2823 parsed.contentSteering = { 2824 uri: M3U8Parser.resolve(contentSteeringAttributes['SERVER-URI'], baseurl), 2825 pathwayId: contentSteeringAttributes['PATHWAY-ID'] || '.' 2826 }; 2827 break; 2828 } 2829 case 'START': 2830 { 2831 // #EXT-X-START 2832 parsed.startTimeOffset = parseStartTimeOffset(attributes); 2833 break; 2834 } 2835 } 2836 } 2837 } 2838 // Filter out levels with unknown codecs if it does not remove all levels 2839 var stripUnknownCodecLevels = levelsWithKnownCodecs.length > 0 && levelsWithKnownCodecs.length < parsed.levels.length; 2840 parsed.levels = stripUnknownCodecLevels ? levelsWithKnownCodecs : parsed.levels; 2841 if (parsed.levels.length === 0) { 2842 parsed.playlistParsingError = new Error('no levels found in manifest'); 2843 } 2844 return parsed; 2845 }; 2846 M3U8Parser.parseMasterPlaylistMedia = function parseMasterPlaylistMedia(string, baseurl, parsed) { 2847 var result; 2848 var results = {}; 2849 var levels = parsed.levels; 2850 var groupsByType = { 2851 AUDIO: levels.map(function (level) { 2852 return { 2853 id: level.attrs.AUDIO, 2854 audioCodec: level.audioCodec 2855 }; 2856 }), 2857 SUBTITLES: levels.map(function (level) { 2858 return { 2859 id: level.attrs.SUBTITLES, 2860 textCodec: level.textCodec 2861 }; 2862 }), 2863 'CLOSED-CAPTIONS': [] 2864 }; 2865 var id = 0; 2866 MASTER_PLAYLIST_MEDIA_REGEX.lastIndex = 0; 2867 while ((result = MASTER_PLAYLIST_MEDIA_REGEX.exec(string)) !== null) { 2868 var attrs = new AttrList(result[1], parsed); 2869 var type = attrs.TYPE; 2870 if (type) { 2871 var groups = groupsByType[type]; 2872 var medias = results[type] || []; 2873 results[type] = medias; 2874 var lang = attrs.LANGUAGE; 2875 var assocLang = attrs['ASSOC-LANGUAGE']; 2876 var channels = attrs.CHANNELS; 2877 var characteristics = attrs.CHARACTERISTICS; 2878 var instreamId = attrs['INSTREAM-ID']; 2879 var media = { 2880 attrs: attrs, 2881 bitrate: 0, 2882 id: id++, 2883 groupId: attrs['GROUP-ID'] || '', 2884 name: attrs.NAME || lang || '', 2885 type: type, 2886 default: attrs.bool('DEFAULT'), 2887 autoselect: attrs.bool('AUTOSELECT'), 2888 forced: attrs.bool('FORCED'), 2889 lang: lang, 2890 url: attrs.URI ? M3U8Parser.resolve(attrs.URI, baseurl) : '' 2891 }; 2892 if (assocLang) { 2893 media.assocLang = assocLang; 2894 } 2895 if (channels) { 2896 media.channels = channels; 2897 } 2898 if (characteristics) { 2899 media.characteristics = characteristics; 2900 } 2901 if (instreamId) { 2902 media.instreamId = instreamId; 2903 } 2904 if (groups != null && groups.length) {
2905 // If there are audio or text groups signalled in the manifest, let's look for a matching codec string for this track 2906 // If we don't find the track signalled, lets use the first audio groups codec we have 2907 // Acting as a best guess 2908 var groupCodec = M3U8Parser.findGroup(groups, media.groupId) || groups[0]; 2909 assignCodec(media, groupCodec, 'audioCodec'); 2910 assignCodec(media, groupCodec, 'textCodec'); 2911 } 2912 medias.push(media); 2913 } 2914 } 2915 return results; 2916 }; 2917 M3U8Parser.parseLevelPlaylist = function parseLevelPlaylist(string, baseurl, id, type, levelUrlId, multivariantVariableList) { 2918 var level = new LevelDetails(baseurl); 2919 var fragments = level.fragments; 2920 var programDateTimes = []; 2921 // The most recent init segment seen (applies to all subsequent segments) 2922 var currentInitSegment = null; 2923 var currentSN = 0; 2924 var currentPart = 0; 2925 var totalduration = 0; 2926 var discontinuityCounter = 0; 2927 var prevFrag = null; 2928 var frag = new Fragment(type, baseurl); 2929 var result; 2930 var i; 2931 var levelkeys; 2932 var firstPdtIndex = -1; 2933 var createNextFrag = false; 2934 var nextByteRange = null; 2935 LEVEL_PLAYLIST_REGEX_FAST.lastIndex = 0; 2936 level.m3u8 = string; 2937 level.hasVariableRefs = false; 2938 while ((result = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) !== null) { 2939 if (createNextFrag) { 2940 createNextFrag = false; 2941 frag = new Fragment(type, baseurl); 2942 // setup the next fragment for part loading 2943 frag.start = totalduration; 2944 frag.sn = currentSN; 2945 frag.cc = discontinuityCounter; 2946 frag.level = id; 2947 if (currentInitSegment) { 2948 frag.initSegment = currentInitSegment; 2949 frag.rawProgramDateTime = currentInitSegment.rawProgramDateTime; 2950 currentInitSegment.rawProgramDateTime = null; 2951 if (nextByteRange) { 2952 frag.setByteRange(nextByteRange); 2953 nextByteRange = null; 2954 } 2955 } 2956 } 2957 var duration = result[1]; 2958 if (duration) { 2959 // INF 2960 frag.duration = parseFloat(duration); 2961 // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 2962 var title = (' ' + result[2]).slice(1); 2963 frag.title = title || null; 2964 frag.tagList.push(title ? ['INF', duration, title] : ['INF', duration]); 2965 } else if (result[3]) { 2966 // url 2967 if (isFiniteNumber(frag.duration)) { 2968 frag.start = totalduration; 2969 if (levelkeys) { 2970 setFragLevelKeys(frag, levelkeys, level); 2971 } 2972 frag.sn = currentSN; 2973 frag.level = id; 2974 frag.cc = discontinuityCounter; 2975 fragments.push(frag); 2976 // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 2977 var uri = (' ' + result[3]).slice(1); 2978 frag.relurl = uri; 2979 assignProgramDateTime(frag, prevFrag, programDateTimes); 2980 prevFrag = frag; 2981 totalduration += frag.duration; 2982 currentSN++; 2983 currentPart = 0; 2984 createNextFrag = true; 2985 } 2986 } else if (result[4]) { 2987 // X-BYTERANGE 2988 var data = (' ' + result[4]).slice(1); 2989 if (prevFrag) { 2990 frag.setByteRange(data, prevFrag); 2991 } else { 2992 frag.setByteRange(data); 2993 } 2994 } else if (result[5]) { 2995 // PROGRAM-DATE-TIME 2996 // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 2997 frag.rawProgramDateTime = (' ' + result[5]).slice(1); 2998 frag.tagList.push(['PROGRAM-DATE-TIME', frag.rawProgramDateTime]); 2999 if (firstPdtIndex === -1) { 3000 firstPdtIndex = fragments.length; 3001 } 3002 } else { 3003 result = result[0].match(LEVEL_PLAYLIST_REGEX_SLOW); 3004 if (!result) { 3005 logger.warn('No matches on slow regex match for level playlist!'); 3006 continue; 3007 } 3008 for (i = 1; i < result.length; i++) { 3009 if (typeof result[i] !== 'undefined') { 3010 break; 3011 } 3012 } 3013 3014 // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 3015 var tag = (' ' + result[i]).slice(1); 3016 var value1 = (' ' + result[i + 1]).slice(1); 3017 var value2 = result[i + 2] ? (' ' + result[i + 2]).slice(1) : ''; 3018 switch (tag) { 3019 case 'PLAYLIST-TYPE': 3020 level.type = value1.toUpperCase(); 3021 break; 3022 case 'MEDIA-SEQUENCE': 3023 currentSN = level.startSN = parseInt(value1); 3024 break; 3025 case 'SKIP': 3026 { 3027 if (level.skippedSegments) { 3028 level.playlistParsingError = new Error("#EXT-X-SKIP MUST NOT appear more than once in a Playlist"); 3029 } 3030 var skipAttrs = new AttrList(value1, level); 3031 var skippedSegments = skipAttrs.decimalInteger('SKIPPED-SEGMENTS'); 3032 if (isFiniteNumber(skippedSegments)) { 3033 level.skippedSegments += skippedSegments; 3034 // This will result in fragments[] containing undefined values, which we will fill in with `mergeDetails` 3035 for (var _i = skippedSegments; _i--;) { 3036 fragments.push(null); 3037 } 3038 currentSN += skippedSegments; 3039 } 3040 var recentlyRemovedDateranges = skipAttrs.enumeratedString('RECENTLY-REMOVED-DATERANGES'); 3041 if (recentlyRemovedDateranges) { 3042 level.recentlyRemovedDateranges = (level.recentlyRemovedDateranges || []).concat(recentlyRemovedDateranges.split('\t')); 3043 } 3044 break; 3045 } 3046 case 'TARGETDURATION': 3047 level.targetduration = Math.max(parseInt(value1), 1); 3048 break; 3049 case 'VERSION':
vendor: 20,004 bytes, lines 3050-3563
3050 level.version = parseInt(value1); 3051 break; 3052 case 'INDEPENDENT-SEGMENTS': 3053 case 'EXTM3U': 3054 break; 3055 case 'ENDLIST': 3056 level.live = false; 3057 break; 3058 case '#': 3059 if (value1 || value2) { 3060 frag.tagList.push(value2 ? [value1, value2] : [value1]); 3061 } 3062 break; 3063 case 'DISCONTINUITY': 3064 discontinuityCounter++; 3065 frag.tagList.push(['DIS']); 3066 break; 3067 case 'GAP': 3068 frag.gap = true; 3069 frag.tagList.push([tag]); 3070 break; 3071 case 'BITRATE': 3072 frag.tagList.push([tag, value1]); 3073 break; 3074 case 'DATERANGE': 3075 { 3076 var dateRangeAttr = new AttrList(value1, level); 3077 var dateRange = new DateRange(dateRangeAttr, level.dateRanges[dateRangeAttr.ID], level.dateRangeTagCount); 3078 level.dateRangeTagCount++; 3079 if (dateRange.isValid || level.skippedSegments) { 3080 level.dateRanges[dateRange.id] = dateRange; 3081 } else { 3082 logger.warn("Ignoring invalid DATERANGE tag: \"" + value1 + "\""); 3083 } 3084 // Add to fragment tag list for backwards compatibility (< v1.2.0) 3085 frag.tagList.push(['EXT-X-DATERANGE', value1]); 3086 break; 3087 } 3088 case 'DEFINE': 3089 { 3090 break; 3091 } 3092 case 'DISCONTINUITY-SEQUENCE': 3093 discontinuityCounter = parseInt(value1); 3094 break; 3095 case 'KEY': 3096 { 3097 var levelKey = parseKey(value1, baseurl, level); 3098 if (levelKey.isSupported()) { 3099 if (levelKey.method === 'NONE') { 3100 levelkeys = undefined; 3101 break; 3102 } 3103 if (!levelkeys) { 3104 levelkeys = {}; 3105 } 3106 if (levelkeys[levelKey.keyFormat]) { 3107 levelkeys = _extends({}, levelkeys); 3108 } 3109 levelkeys[levelKey.keyFormat] = levelKey; 3110 } else { 3111 logger.warn("[Keys] Ignoring invalid EXT-X-KEY tag: \"" + value1 + "\""); 3112 } 3113 break; 3114 } 3115 case 'START': 3116 level.startTimeOffset = parseStartTimeOffset(value1); 3117 break; 3118 case 'MAP': 3119 { 3120 var mapAttrs = new AttrList(value1, level); 3121 if (frag.duration) { 3122 // Initial segment tag is after segment duration tag. 3123 // #EXTINF: 6.0 3124 // #EXT-X-MAP:URI="init.mp4 3125 var init = new Fragment(type, baseurl); 3126 setInitSegment(init, mapAttrs, id, levelkeys); 3127 currentInitSegment = init; 3128 frag.initSegment = currentInitSegment; 3129 if (currentInitSegment.rawProgramDateTime && !frag.rawProgramDateTime) { 3130 frag.rawProgramDateTime = currentInitSegment.rawProgramDateTime; 3131 } 3132 } else { 3133 // Initial segment tag is before segment duration tag 3134 // Handle case where EXT-X-MAP is declared after EXT-X-BYTERANGE 3135 var end = frag.byteRangeEndOffset; 3136 if (end) { 3137 var start = frag.byteRangeStartOffset; 3138 nextByteRange = end - start + "@" + start; 3139 } else { 3140 nextByteRange = null; 3141 } 3142 setInitSegment(frag, mapAttrs, id, levelkeys); 3143 currentInitSegment = frag; 3144 createNextFrag = true; 3145 } 3146 currentInitSegment.cc = discontinuityCounter; 3147 break; 3148 } 3149 case 'SERVER-CONTROL': 3150 { 3151 var serverControlAttrs = new AttrList(value1); 3152 level.canBlockReload = serverControlAttrs.bool('CAN-BLOCK-RELOAD'); 3153 level.canSkipUntil = serverControlAttrs.optionalFloat('CAN-SKIP-UNTIL', 0); 3154 level.canSkipDateRanges = level.canSkipUntil > 0 && serverControlAttrs.bool('CAN-SKIP-DATERANGES'); 3155 level.partHoldBack = serverControlAttrs.optionalFloat('PART-HOLD-BACK', 0); 3156 level.holdBack = serverControlAttrs.optionalFloat('HOLD-BACK', 0); 3157 break; 3158 } 3159 case 'PART-INF': 3160 { 3161 var partInfAttrs = new AttrList(value1); 3162 level.partTarget = partInfAttrs.decimalFloatingPoint('PART-TARGET'); 3163 break; 3164 } 3165 case 'PART': 3166 { 3167 var partList = level.partList; 3168 if (!partList) { 3169 partList = level.partList = []; 3170 } 3171 var previousFragmentPart = currentPart > 0 ? partList[partList.length - 1] : undefined; 3172 var index = currentPart++; 3173 var partAttrs = new AttrList(value1, level); 3174 var part = new Part(partAttrs, frag, baseurl, index, previousFragmentPart); 3175 partList.push(part); 3176 frag.duration += part.duration; 3177 break; 3178 } 3179 case 'PRELOAD-HINT': 3180 { 3181 var preloadHintAttrs = new AttrList(value1, level); 3182 level.preloadHint = preloadHintAttrs; 3183 break; 3184 } 3185 case 'RENDITION-REPORT': 3186 { 3187 var renditionReportAttrs = new AttrList(value1, level); 3188 level.renditionReports = level.renditionReports || []; 3189 level.renditionReports.push(renditionReportAttrs); 3190 break; 3191 } 3192 default: 3193 logger.warn("line parsed but not handled: " + result); 3194 break; 3195 } 3196 } 3197 } 3198 if (prevFrag && !prevFrag.relurl) { 3199 fragments.pop(); 3200 totalduration -= prevFrag.duration; 3201 if (level.partList) { 3202 level.fragmentHint = prevFrag; 3203 } 3204 } else if (level.partList) { 3205 assignProgramDateTime(frag, prevFrag, programDateTimes); 3206 frag.cc = discontinuityCounter; 3207 level.fragmentHint = frag; 3208 if (levelkeys) { 3209 setFragLevelKeys(frag, levelkeys, level); 3210 } 3211 } 3212 var fragmentLength = fragments.length; 3213 var firstFragment = fragments[0]; 3214 var lastFragment = fragments[fragmentLength - 1]; 3215 totalduration += level.skippedSegments * level.targetduration; 3216 if (totalduration > 0 && fragmentLength && lastFragment) { 3217 level.averagetargetduration = totalduration / fragmentLength; 3218 var lastSn = lastFragment.sn; 3219 level.endSN = lastSn !== 'initSegment' ? lastSn : 0; 3220 if (!level.live) { 3221 lastFragment.endList = true; 3222 } 3223 if (firstFragment) { 3224 level.startCC = firstFragment.cc; 3225 } 3226 /** 3227 * Backfill any missing PDT values 3228 * "If the first EXT-X-PROGRAM-DATE-TIME tag in a Playlist appears after 3229 * one or more Media Segment URIs, the client SHOULD extrapolate 3230 * backward from that tag (using EXTINF durations and/or media 3231 * timestamps) to associate dates with those segments." 3232 * We have already extrapolated forward, but all fragments up to the first instance of PDT do not have their PDTs 3233 * computed. 3234 */ 3235 if (firstPdtIndex > 0) { 3236 backfillProgramDateTimes(fragments, firstPdtIndex); 3237 if (firstFragment) { 3238 programDateTimes.unshift(firstFragment); 3239 } 3240 } 3241 } else { 3242 level.endSN = 0; 3243 level.startCC = 0; 3244 } 3245 if (level.fragmentHint) { 3246 totalduration += level.fragmentHint.duration; 3247 } 3248 level.totalduration = totalduration; 3249 if (programDateTimes.length && level.dateRangeTagCount && firstFragment) { 3250 mapDateRanges(programDateTimes, level); 3251 } 3252 level.endCC = discontinuityCounter; 3253 return level; 3254 }; 3255 return M3U8Parser; 3256 }(); 3257 function mapDateRanges(programDateTimes, details) { 3258 // Make sure DateRanges are mapped to a ProgramDateTime tag that applies a date to a segment that overlaps with its start date 3259 var programDateTimeCount = programDateTimes.length; 3260 var lastProgramDateTime = programDateTimes[programDateTimeCount - 1]; 3261 var playlistEnd = details.live ? Infinity : details.totalduration; 3262 var dateRangeIds = Object.keys(details.dateRanges); 3263 for (var i = dateRangeIds.length; i--;) { 3264 var dateRange = details.dateRanges[dateRangeIds[i]]; 3265 var startDateTime = dateRange.startDate.getTime(); 3266 dateRange.tagAnchor = lastProgramDateTime; 3267 for (var j = programDateTimeCount; j--;) { 3268 var fragIndex = findFragmentWithStartDate(details, startDateTime, programDateTimes, j, playlistEnd); 3269 if (fragIndex !== -1) { 3270 dateRange.tagAnchor = details.fragments[fragIndex]; 3271 break; 3272 } 3273 } 3274 } 3275 } 3276 function findFragmentWithStartDate(details, startDateTime, programDateTimes, index, endTime) { 3277 var pdtFragment = programDateTimes[index]; 3278 if (pdtFragment) { 3279 var _programDateTimes; 3280 // find matching range between PDT tags 3281 var durationBetweenPdt = (((_programDateTimes = programDateTimes[index + 1]) == null ? void 0 : _programDateTimes.start) || endTime) - pdtFragment.start; 3282 var pdtStart = pdtFragment.programDateTime; 3283 if ((startDateTime >= pdtStart || index === 0) && startDateTime <= pdtStart + durationBetweenPdt * 1000) { 3284 // map to fragment with date-time range 3285 var startIndex = programDateTimes[index].sn - details.startSN; 3286 var fragments = details.fragments; 3287 if (fragments.length > programDateTimes.length) { 3288 var endSegment = programDateTimes[index + 1] || fragments[fragments.length - 1]; 3289 var endIndex = endSegment.sn - details.startSN; 3290 for (var i = endIndex; i > startIndex; i--) { 3291 var fragStartDateTime = fragments[i].programDateTime; 3292 if (startDateTime >= fragStartDateTime && startDateTime < fragStartDateTime + fragments[i].duration * 1000) { 3293 return i; 3294 } 3295 } 3296 } 3297 return startIndex; 3298 } 3299 } 3300 return -1; 3301 } 3302 function parseKey(keyTagAttributes, baseurl, parsed) { 3303 var _keyAttrs$METHOD, _keyAttrs$KEYFORMAT; 3304 // https://tools.ietf.org/html/rfc8216#section-4.3.2.4 3305 var keyAttrs = new AttrList(keyTagAttributes, parsed); 3306 var decryptmethod = (_keyAttrs$METHOD = keyAttrs.METHOD) != null ? _keyAttrs$METHOD : ''; 3307 var decrypturi = keyAttrs.URI; 3308 var decryptiv = keyAttrs.hexadecimalInteger('IV'); 3309 var decryptkeyformatversions = keyAttrs.KEYFORMATVERSIONS; 3310 // From RFC: This attribute is OPTIONAL; its absence indicates an implicit value of "identity". 3311 var decryptkeyformat = (_keyAttrs$KEYFORMAT = keyAttrs.KEYFORMAT) != null ? _keyAttrs$KEYFORMAT : 'identity'; 3312 if (decrypturi && keyAttrs.IV && !decryptiv) { 3313 logger.error("Invalid IV: " + keyAttrs.IV); 3314 } 3315 // If decrypturi is a URI with a scheme, then baseurl will be ignored 3316 // No uri is allowed when METHOD is NONE 3317 var resolvedUri = decrypturi ? M3U8Parser.resolve(decrypturi, baseurl) : ''; 3318 var keyFormatVersions = (decryptkeyformatversions ? decryptkeyformatversions : '1').split('/').map(Number).filter(Number.isFinite); 3319 return new LevelKey(decryptmethod, resolvedUri, decryptkeyformat, keyFormatVersions, decryptiv); 3320 } 3321 function parseStartTimeOffset(startAttributes) { 3322 var startAttrs = new AttrList(startAttributes); 3323 var startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET'); 3324 if (isFiniteNumber(startTimeOffset)) { 3325 return startTimeOffset; 3326 } 3327 return null; 3328 } 3329 function setCodecs(codecsAttributeValue, level) { 3330 var codecs = (codecsAttributeValue || '').split(/[ ,]+/).filter(function (c) { 3331 return c; 3332 }); 3333 ['video', 'audio', 'text'].forEach(function (type) { 3334 var filtered = codecs.filter(function (codec) { 3335 return isCodecType(codec, type); 3336 }); 3337 if (filtered.length) { 3338 // Comma separated list of all codecs for type 3339 level[type + "Codec"] = filtered.join(','); 3340 // Remove known codecs so that only unknownCodecs are left after iterating through each type 3341 codecs = codecs.filter(function (codec) { 3342 return filtered.indexOf(codec) === -1; 3343 }); 3344 } 3345 }); 3346 level.unknownCodecs = codecs; 3347 } 3348 function assignCodec(media, groupItem, codecProperty) { 3349 var codecValue = groupItem[codecProperty]; 3350 if (codecValue) { 3351 media[codecProperty] = codecValue; 3352 } 3353 } 3354 function backfillProgramDateTimes(fragments, firstPdtIndex) { 3355 var fragPrev = fragments[firstPdtIndex]; 3356 for (var i = firstPdtIndex; i--;) { 3357 var frag = fragments[i]; 3358 // Exit on delta-playlist skipped segments 3359 if (!frag) { 3360 return; 3361 } 3362 frag.programDateTime = fragPrev.programDateTime - frag.duration * 1000; 3363 fragPrev = frag; 3364 } 3365 } 3366 function assignProgramDateTime(frag, prevFrag, programDateTimes) { 3367 if (frag.rawProgramDateTime) { 3368 frag.programDateTime = Date.parse(frag.rawProgramDateTime); 3369 if (!isFiniteNumber(frag.programDateTime)) { 3370 frag.programDateTime = null; 3371 frag.rawProgramDateTime = null; 3372 return; 3373 } 3374 programDateTimes.push(frag); 3375 } else if (prevFrag != null && prevFrag.programDateTime) { 3376 frag.programDateTime = prevFrag.endProgramDateTime; 3377 } 3378 } 3379 function setInitSegment(frag, mapAttrs, id, levelkeys) { 3380 frag.relurl = mapAttrs.URI; 3381 if (mapAttrs.BYTERANGE) { 3382 frag.setByteRange(mapAttrs.BYTERANGE); 3383 } 3384 frag.level = id; 3385 frag.sn = 'initSegment'; 3386 if (levelkeys) { 3387 frag.levelkeys = levelkeys; 3388 } 3389 frag.initSegment = null; 3390 } 3391 function setFragLevelKeys(frag, levelkeys, level) { 3392 frag.levelkeys = levelkeys; 3393 var encryptedFragments = level.encryptedFragments; 3394 if ((!encryptedFragments.length || encryptedFragments[encryptedFragments.length - 1].levelkeys !== levelkeys) && Object.keys(levelkeys).some(function (format) { 3395 return levelkeys[format].isCommonEncryption; 3396 })) { 3397 encryptedFragments.push(frag); 3398 } 3399 } 3400 3401 var PlaylistContextType = { 3402 MANIFEST: "manifest", 3403 LEVEL: "level", 3404 AUDIO_TRACK: "audioTrack", 3405 SUBTITLE_TRACK: "subtitleTrack" 3406 }; 3407 var PlaylistLevelType = { 3408 MAIN: "main", 3409 AUDIO: "audio", 3410 SUBTITLE: "subtitle" 3411 }; 3412 3413 function mapContextToLevelType(context) { 3414 var type = context.type; 3415 switch (type) { 3416 case PlaylistContextType.AUDIO_TRACK: 3417 return PlaylistLevelType.AUDIO; 3418 case PlaylistContextType.SUBTITLE_TRACK: 3419 return PlaylistLevelType.SUBTITLE; 3420 default: 3421 return PlaylistLevelType.MAIN; 3422 } 3423 } 3424 function getResponseUrl(response, context) { 3425 var url = response.url; 3426 // responseURL not supported on some browsers (it is used to detect URL redirection) 3427 // data-uri mode also not supported (but no need to detect redirection) 3428 if (url === undefined || url.indexOf('data:') === 0) { 3429 // fallback to initial URL 3430 url = context.url; 3431 } 3432 return url; 3433 } 3434 var PlaylistLoader = /*#__PURE__*/function () { 3435 function PlaylistLoader(hls) { 3436 this.hls = void 0; 3437 this.loaders = Object.create(null); 3438 this.variableList = null; 3439 this.hls = hls; 3440 this.registerListeners(); 3441 } 3442 var _proto = PlaylistLoader.prototype; 3443 _proto.startLoad = function startLoad(startPosition) {}; 3444 _proto.stopLoad = function stopLoad() { 3445 this.destroyInternalLoaders(); 3446 }; 3447 _proto.registerListeners = function registerListeners() { 3448 var hls = this.hls; 3449 hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); 3450 hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this); 3451 hls.on(Events.AUDIO_TRACK_LOADING, this.onAudioTrackLoading, this); 3452 hls.on(Events.SUBTITLE_TRACK_LOADING, this.onSubtitleTrackLoading, this); 3453 }; 3454 _proto.unregisterListeners = function unregisterListeners() { 3455 var hls = this.hls; 3456 hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); 3457 hls.off(Events.LEVEL_LOADING, this.onLevelLoading, this); 3458 hls.off(Events.AUDIO_TRACK_LOADING, this.onAudioTrackLoading, this); 3459 hls.off(Events.SUBTITLE_TRACK_LOADING, this.onSubtitleTrackLoading, this); 3460 } 3461 3462 /** 3463 * Returns defaults or configured loader-type overloads (pLoader and loader config params) 3464 */; 3465 _proto.createInternalLoader = function createInternalLoader(context) { 3466 var config = this.hls.config; 3467 var PLoader = config.pLoader; 3468 var Loader = config.loader; 3469 var InternalLoader = PLoader || Loader; 3470 var loader = new InternalLoader(config); 3471 this.loaders[context.type] = loader; 3472 return loader; 3473 }; 3474 _proto.getInternalLoader = function getInternalLoader(context) { 3475 return this.loaders[context.type]; 3476 }; 3477 _proto.resetInternalLoader = function resetInternalLoader(contextType) { 3478 if (this.loaders[contextType]) { 3479 delete this.loaders[contextType]; 3480 } 3481 } 3482 3483 /** 3484 * Call `destroy` on all internal loader instances mapped (one per context type) 3485 */; 3486 _proto.destroyInternalLoaders = function destroyInternalLoaders() { 3487 for (var contextType in this.loaders) { 3488 var loader = this.loaders[contextType]; 3489 if (loader) { 3490 loader.destroy(); 3491 } 3492 this.resetInternalLoader(contextType); 3493 } 3494 }; 3495 _proto.destroy = function destroy() { 3496 this.variableList = null; 3497 this.unregisterListeners(); 3498 this.destroyInternalLoaders(); 3499 }; 3500 _proto.onManifestLoading = function onManifestLoading(event, data) { 3501 var url = data.url; 3502 this.variableList = null; 3503 this.load({ 3504 id: null, 3505 level: 0, 3506 responseType: 'text', 3507 type: PlaylistContextType.MANIFEST, 3508 url: url, 3509 deliveryDirectives: null 3510 }); 3511 }; 3512 _proto.onLevelLoading = function onLevelLoading(event, data) { 3513 var id = data.id, 3514 level = data.level, 3515 pathwayId = data.pathwayId, 3516 url = data.url, 3517 deliveryDirectives = data.deliveryDirectives; 3518 this.load({ 3519 id: id, 3520 level: level, 3521 pathwayId: pathwayId, 3522 responseType: 'text', 3523 type: PlaylistContextType.LEVEL, 3524 url: url, 3525 deliveryDirectives: deliveryDirectives 3526 }); 3527 }; 3528 _proto.onAudioTrackLoading = function onAudioTrackLoading(event, data) { 3529 var id = data.id, 3530 groupId = data.groupId, 3531 url = data.url, 3532 deliveryDirectives = data.deliveryDirectives; 3533 this.load({ 3534 id: id, 3535 groupId: groupId, 3536 level: null, 3537 responseType: 'text', 3538 type: PlaylistContextType.AUDIO_TRACK, 3539 url: url, 3540 deliveryDirectives: deliveryDirectives 3541 }); 3542 }; 3543 _proto.onSubtitleTrackLoading = function onSubtitleTrackLoading(event, data) { 3544 var id = data.id, 3545 groupId = data.groupId, 3546 url = data.url, 3547 deliveryDirectives = data.deliveryDirectives; 3548 this.load({ 3549 id: id, 3550 groupId: groupId, 3551 level: null, 3552 responseType: 'text', 3553 type: PlaylistContextType.SUBTITLE_TRACK, 3554 url: url, 3555 deliveryDirectives: deliveryDirectives 3556 }); 3557 }; 3558 _proto.load = function load(context) { 3559 var _context$deliveryDire, 3560 _this = this; 3561 var config = this.hls.config; 3562 3563 // logger.debug(`[playlist-loader]: Loading playlist of type ${context.type}, level: ${context.level}
3563, id: ${context.id}`); 3564 3565 // Check if a loader for this context already exists 3566 var loader = this.getInternalLoader(context); 3567 if (loader) { 3568 var loaderContext = loader.context; 3569 if (loaderContext && loaderContext.url === context.url && loaderContext.level === context.level) { 3570 // same URL can't overlap 3571 this.hls.logger.trace('[playlist-loader]: playlist request ongoing'); 3572 return; 3573 } 3574 this.hls.logger.log("[playlist-loader]: aborting previous loader for type: " + context.type); 3575 loader.abort(); 3576 } 3577 3578 // apply different configs for retries depending on 3579 // context (manifest, level, audio/subs playlist) 3580 var loadPolicy; 3581 if (context.type === PlaylistContextType.MANIFEST) { 3582 loadPolicy = config.manifestLoadPolicy.default; 3583 } else { 3584 loadPolicy = _extends({}, config.playlistLoadPolicy.default, { 3585 timeoutRetry: null, 3586 errorRetry: null 3587 }); 3588 } 3589 loader = this.createInternalLoader(context); 3590 3591 // Override level/track timeout for LL-HLS requests 3592 // (the default of 10000ms is counter productive to blocking playlist reload requests) 3593 if (isFiniteNumber((_context$deliveryDire = context.deliveryDirectives) == null ? void 0 : _context$deliveryDire.part)) { 3594 var levelDetails; 3595 if (context.type === PlaylistContextType.LEVEL && context.level !== null) { 3596 levelDetails = this.hls.levels[context.level].details; 3597 } else if (context.type === PlaylistContextType.AUDIO_TRACK && context.id !== null) { 3598 levelDetails = this.hls.audioTracks[context.id].details; 3599 } else if (context.type === PlaylistContextType.SUBTITLE_TRACK && context.id !== null) { 3600 levelDetails = this.hls.subtitleTracks[context.id].details; 3601 } 3602 if (levelDetails) { 3603 var partTarget = levelDetails.partTarget; 3604 var targetDuration = levelDetails.targetduration; 3605 if (partTarget && targetDuration) { 3606 var maxLowLatencyPlaylistRefresh = Math.max(partTarget * 3, targetDuration * 0.8) * 1000; 3607 loadPolicy = _extends({}, loadPolicy, { 3608 maxTimeToFirstByteMs: Math.min(maxLowLatencyPlaylistRefresh, loadPolicy.maxTimeToFirstByteMs), 3609 maxLoadTimeMs: Math.min(maxLowLatencyPlaylistRefresh, loadPolicy.maxTimeToFirstByteMs) 3610 }); 3611 } 3612 } 3613 } 3614 var legacyRetryCompatibility = loadPolicy.errorRetry || loadPolicy.timeoutRetry || {}; 3615 var loaderConfig = { 3616 loadPolicy: loadPolicy, 3617 timeout: loadPolicy.maxLoadTimeMs, 3618 maxRetry: legacyRetryCompatibility.maxNumRetry || 0, 3619 retryDelay: legacyRetryCompatibility.retryDelayMs || 0, 3620 maxRetryDelay: legacyRetryCompatibility.maxRetryDelayMs || 0 3621 }; 3622 var loaderCallbacks = { 3623 onSuccess: function onSuccess(response, stats, context, networkDetails) { 3624 var loader = _this.getInternalLoader(context); 3625 _this.resetInternalLoader(context.type); 3626 var string = response.data; 3627 3628 // Validate if it is an M3U8 at all 3629 if (string.indexOf('#EXTM3U') !== 0) { 3630 _this.handleManifestParsingError(response, context, new Error('no EXTM3U delimiter'), networkDetails || null, stats); 3631 return; 3632 } 3633 stats.parsing.start = performance.now(); 3634 if (M3U8Parser.isMediaPlaylist(string)) { 3635 _this.handleTrackOrLevelPlaylist(response, stats, context, networkDetails || null, loader); 3636 } else { 3637 _this.handleMasterPlaylist(response, stats, context, networkDetails); 3638 } 3639 }, 3640 onError: function onError(response, context, networkDetails, stats) { 3641 _this.handleNetworkError(context, networkDetails, false, response, stats); 3642 }, 3643 onTimeout: function onTimeout(stats, context, networkDetails) { 3644 _this.handleNetworkError(context, networkDetails, true, undefined, stats); 3645 } 3646 }; 3647 3648 // logger.debug(`[playlist-loader]: Calling internal loader delegate for URL: ${context.url}`); 3649 3650 loader.load(context, loaderConfig, loaderCallbacks); 3651 }; 3652 _proto.handleMasterPlaylist = function handleMasterPlaylist(response, stats, context, networkDetails) { 3653 var hls = this.hls; 3654 var string = response.data; 3655 var url = getResponseUrl(response, context); 3656 var parsedResult = M3U8Parser.parseMasterPlaylist(string, url); 3657 if (parsedResult.playlistParsingError) { 3658 this.handleManifestParsingError(response, context, parsedResult.playlistParsingError, networkDetails, stats); 3659 return; 3660 } 3661 var contentSteering = parsedResult.contentSteering, 3662 levels = parsedResult.levels, 3663 sessionData = parsedResult.sessionData, 3664 sessionKeys = parsedResult.sessionKeys, 3665 startTimeOffset = parsedResult.startTimeOffset, 3666 variableList = parsedResult.variableList; 3667 this.variableList = variableList; 3668 var _M3U8Parser$parseMast = M3U8Parser.parseMasterPlaylistMedia(string, url, parsedResult), 3669 _M3U8Parser$parseMast2 = _M3U8Parser$parseMast.AUDIO, 3670 audioTracks = _M3U8Parser$parseMast2 === void 0 ? [] : _M3U8Parser$parseMast2, 3671 subtitles = _M3U8Parser$parseMast.SUBTITLES, 3672 captions = _M3U8Parser$parseMast['CLOSED-CAPTIONS']; 3673 if (audioTracks.length) { 3674 // check if we have found an audio track embedded in main playlist (audio track without URI attribute) 3675 var embeddedAudioFound = audioTracks.some(function (audioTrack) { 3676 return !audioTrack.url; 3677 }); 3678 3679 // if no embedded audio track defined, but audio codec signaled in quality level, 3680 // we need to signal this main audio track this could happen with playlists with 3681 // alt audio rendition in which quality levels (main) 3682 // contains both audio+video. but with mixed audio track not signaled 3683 if (!embeddedAudioFound && levels[0].audioCodec && !levels[0].attrs.AUDIO) { 3684 this.hls.logger.log('[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one'); 3685 audioTracks.unshift({ 3686 type: 'main', 3687 name: 'main', 3688 groupId: 'main', 3689 default: false, 3690 autoselect: false, 3691 forced: false, 3692 id: -1, 3693 attrs: new AttrList({}), 3694 bitrate: 0, 3695 url: '' 3696 }); 3697 } 3698 } 3699 hls.trigger(Events.MANIFEST_LOADED, { 3700 levels: levels, 3701 audioTracks: audioTracks, 3702 subtitles: subtitles, 3703 captions: captions, 3704 contentSteering: contentSteering, 3705 url: url, 3706 stats: stats, 3707 networkDetails: networkDetails, 3708 sessionData: sessionData, 3709 sessionKeys: sessionKeys, 3710 startTimeOffset: startTimeOffset, 3711 variableList: variableList 3712 }); 3713 }; 3714 _proto.handleTrackOrLevelPlaylist = function handleTrackOrLevelPlaylist(response, stats, context, networkDetails, loader) { 3715 var hls = this.hls;
vendor: 4,236 bytes, lines 3716-3820
3716 var id = context.id, 3717 level = context.level, 3718 type = context.type; 3719 var url = getResponseUrl(response, context); 3720 var levelId = isFiniteNumber(level) ? level : isFiniteNumber(id) ? id : 0; 3721 var levelType = mapContextToLevelType(context); 3722 var levelDetails = M3U8Parser.parseLevelPlaylist(response.data, url, levelId, levelType, 0, this.variableList); 3723 3724 // We have done our first request (Manifest-type) and receive 3725 // not a master playlist but a chunk-list (track/level) 3726 // We fire the manifest-loaded event anyway with the parsed level-details 3727 // by creating a single-level structure for it. 3728 if (type === PlaylistContextType.MANIFEST) { 3729 var singleLevel = { 3730 attrs: new AttrList({}), 3731 bitrate: 0, 3732 details: levelDetails, 3733 name: '', 3734 url: url 3735 }; 3736 hls.trigger(Events.MANIFEST_LOADED, { 3737 levels: [singleLevel], 3738 audioTracks: [], 3739 url: url, 3740 stats: stats, 3741 networkDetails: networkDetails, 3742 sessionData: null, 3743 sessionKeys: null, 3744 contentSteering: null, 3745 startTimeOffset: null, 3746 variableList: null 3747 }); 3748 } 3749 3750 // save parsing time 3751 stats.parsing.end = performance.now(); 3752 3753 // extend the context with the new levelDetails property 3754 context.levelDetails = levelDetails; 3755 this.handlePlaylistLoaded(levelDetails, response, stats, context, networkDetails, loader); 3756 }; 3757 _proto.handleManifestParsingError = function handleManifestParsingError(response, context, error, networkDetails, stats) { 3758 this.hls.trigger(Events.ERROR, { 3759 type: ErrorTypes.NETWORK_ERROR, 3760 details: ErrorDetails.MANIFEST_PARSING_ERROR, 3761 fatal: context.type === PlaylistContextType.MANIFEST, 3762 url: response.url, 3763 err: error, 3764 error: error, 3765 reason: error.message, 3766 response: response, 3767 context: context, 3768 networkDetails: networkDetails, 3769 stats: stats 3770 }); 3771 }; 3772 _proto.handleNetworkError = function handleNetworkError(context, networkDetails, timeout, response, stats) { 3773 if (timeout === void 0) { 3774 timeout = false; 3775 } 3776 var message = "A network " + (timeout ? 'timeout' : 'error' + (response ? ' (status ' + response.code + ')' : '')) + " occurred while loading " + context.type; 3777 if (context.type === PlaylistContextType.LEVEL) { 3778 message += ": " + context.level + " id: " + context.id; 3779 } else if (context.type === PlaylistContextType.AUDIO_TRACK || context.type === PlaylistContextType.SUBTITLE_TRACK) { 3780 message += " id: " + context.id + " group-id: \"" + context.groupId + "\""; 3781 } 3782 var error = new Error(message); 3783 this.hls.logger.warn("[playlist-loader]: " + message); 3784 var details = ErrorDetails.UNKNOWN; 3785 var fatal = false; 3786 var loader = this.getInternalLoader(context); 3787 switch (context.type) { 3788 case PlaylistContextType.MANIFEST: 3789 details = timeout ? ErrorDetails.MANIFEST_LOAD_TIMEOUT : ErrorDetails.MANIFEST_LOAD_ERROR; 3790 fatal = true; 3791 break; 3792 case PlaylistContextType.LEVEL: 3793 details = timeout ? ErrorDetails.LEVEL_LOAD_TIMEOUT : ErrorDetails.LEVEL_LOAD_ERROR; 3794 fatal = false; 3795 break; 3796 case PlaylistContextType.AUDIO_TRACK: 3797 details = timeout ? ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT : ErrorDetails.AUDIO_TRACK_LOAD_ERROR; 3798 fatal = false; 3799 break; 3800 case PlaylistContextType.SUBTITLE_TRACK: 3801 details = timeout ? ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT : ErrorDetails.SUBTITLE_LOAD_ERROR; 3802 fatal = false; 3803 break; 3804 } 3805 if (loader) { 3806 this.resetInternalLoader(context.type); 3807 } 3808 var errorData = { 3809 type: ErrorTypes.NETWORK_ERROR, 3810 details: details, 3811 fatal: fatal, 3812 url: context.url, 3813 loader: loader, 3814 context: context, 3815 error: error, 3816 networkDetails: networkDetails, 3817 stats: stats 3818 }; 3819 if (response) { 3820 var url = (networkDetails == null ? void 0 : networkDetails.url) || context.url;
3821 errorData.response = _objectSpread2({ 3822 url: url, 3823 data: undefined 3824 }, response); 3825 } 3826 this.hls.trigger(Events.ERROR, errorData); 3827 }; 3828 _proto.handlePlaylistLoaded = function handlePlaylistLoaded(levelDetails, response, stats, context, networkDetails, loader) { 3829 var hls = this.hls; 3830 var type = context.type, 3831 level = context.level, 3832 id = context.id, 3833 groupId = context.groupId, 3834 deliveryDirectives = context.deliveryDirectives; 3835 var url = getResponseUrl(response, context); 3836 var parent = mapContextToLevelType(context); 3837 var levelIndex = typeof context.level === 'number' && parent === PlaylistLevelType.MAIN ? level : undefined; 3838 if (!levelDetails.fragments.length) { 3839 var _error = new Error('No Segments found in Playlist'); 3840 hls.trigger(Events.ERROR, { 3841 type: ErrorTypes.NETWORK_ERROR, 3842 details: ErrorDetails.LEVEL_EMPTY_ERROR, 3843 fatal: false, 3844 url: url, 3845 error: _error, 3846 reason: _error.message, 3847 response: response, 3848 context: context, 3849 level: levelIndex, 3850 parent: parent, 3851 networkDetails: networkDetails, 3852 stats: stats 3853 }); 3854 return; 3855 } 3856 if (!levelDetails.targetduration) { 3857 levelDetails.playlistParsingError = new Error('Missing Target Duration'); 3858 } 3859 var error = levelDetails.playlistParsingError; 3860 if (error) { 3861 hls.trigger(Events.ERROR, { 3862 type: ErrorTypes.NETWORK_ERROR, 3863 details: ErrorDetails.LEVEL_PARSING_ERROR, 3864 fatal: false, 3865 url: url, 3866 error: error, 3867 reason: error.message, 3868 response: response, 3869 context: context, 3870 level: levelIndex, 3871 parent: parent, 3872 networkDetails: networkDetails, 3873 stats: stats 3874 }); 3875 return; 3876 } 3877 if (levelDetails.live && loader) { 3878 if (loader.getCacheAge) { 3879 levelDetails.ageHeader = loader.getCacheAge() || 0; 3880 } 3881 if (!loader.getCacheAge || isNaN(levelDetails.ageHeader)) { 3882 levelDetails.ageHeader = 0; 3883 } 3884 } 3885 switch (type) { 3886 case PlaylistContextType.MANIFEST: 3887 case PlaylistContextType.LEVEL: 3888 hls.trigger(Events.LEVEL_LOADED, { 3889 details: levelDetails, 3890 level: levelIndex || 0, 3891 id: id || 0, 3892 stats: stats, 3893 networkDetails: networkDetails, 3894 deliveryDirectives: deliveryDirectives 3895 }); 3896 break; 3897 case PlaylistContextType.AUDIO_TRACK: 3898 hls.trigger(Events.AUDIO_TRACK_LOADED, { 3899 details: levelDetails, 3900 id: id || 0, 3901 groupId: groupId || '', 3902 stats: stats, 3903 networkDetails: networkDetails, 3904 deliveryDirectives: deliveryDirectives 3905 }); 3906 break; 3907 case PlaylistContextType.SUBTITLE_TRACK: 3908 hls.trigger(Events.SUBTITLE_TRACK_LOADED, { 3909 details: levelDetails, 3910 id: id || 0, 3911 groupId: groupId || '', 3912 stats: stats, 3913 networkDetails: networkDetails, 3914 deliveryDirectives: deliveryDirectives 3915 }); 3916 break; 3917 } 3918 }; 3919 return PlaylistLoader; 3920 }(); 3921 3922 function sendAddTrackEvent(track, videoEl) { 3923 var event; 3924 try { 3925 event = new Event('addtrack'); 3926 } catch (err) { 3927 // for IE11 3928 event = document.createEvent('Event'); 3929 event.initEvent('addtrack', false, false); 3930 } 3931 event.track = track; 3932 videoEl.dispatchEvent(event); 3933 } 3934 function clearCurrentCues(track) { 3935 // When track.mode is disabled, track.cues will be null. 3936 // To guarantee the removal of cues, we need to temporarily 3937 // change the mode to hidden 3938 var mode = track.mode; 3939 if (mode === 'disabled') { 3940 track.mode = 'hidden'; 3941 } 3942 if (track.cues) { 3943 for (var i = track.cues.length; i--;) { 3944 track.removeCue(track.cues[i]); 3945 } 3946 } 3947 if (mode === 'disabled') { 3948 track.mode = mode; 3949 } 3950 } 3951 function removeCuesInRange(track, start, end, predicate) { 3952 var mode = track.mode; 3953 if (mode === 'disabled') { 3954 track.mode = 'hidden'; 3955 } 3956 if (track.cues && track.cues.length > 0) { 3957 var cues = getCuesInRange(track.cues, start, end); 3958 for (var i = 0; i < cues.length; i++) { 3959 if (!predicate || predicate(cues[i])) { 3960 track.removeCue(cues[i]); 3961 } 3962 } 3963 } 3964 if (mode === 'disabled') { 3965 track.mode = mode; 3966 } 3967 } 3968 3969 // Find first cue starting after given time. 3970 // Modified version of binary search O(log(n)). 3971 function getFirstCueIndexAfterTime(cues, time) { 3972 // If first cue starts after time, start there 3973 if (time < cues[0].startTime) { 3974 return 0; 3975 } 3976 // If the last cue ends before time there is no overlap 3977 var len = cues.length - 1; 3978 if (time > cues[len].endTime) { 3979 return -1; 3980 } 3981 var left = 0; 3982 var right = len; 3983 while (left <= right) { 3984 var mid = Math.floor((right + left) / 2); 3985 if (time < cues[mid].startTime) { 3986 right = mid - 1; 3987 } else if (time > cues[mid].startTime && left < len) { 3988 left = mid + 1; 3989 } else { 3990 // If it's not lower or higher, it must be equal. 3991 return mid; 3992 } 3993 } 3994 // At this point, left and right have swapped. 3995 // No direct match was found, left or right element must be the closest. Check which one has the smallest diff. 3996 return cues[left].startTime - time < time - cues[right].startTime ? left : right; 3997 } 3998 function getCuesInRange(cues, start, end) { 3999 var cuesFound = []; 4000 var firstCueInRange = getFirstCueIndexAfterTime(cues, start); 4001 if (firstCueInRange > -1) { 4002 for (var i = firstCueInRange, len = cues.length; i < len; i++) { 4003 var cue = cues[i]; 4004 if (cue.startTime >= start && cue.endTime <= end) { 4005 cuesFound.push(cue); 4006 } else if (cue.startTime > end) { 4007 return cuesFound; 4008 } 4009 } 4010 } 4011 return cuesFound; 4012 } 4013 4014 var MetadataSchema = { 4015 audioId3: "org.id3", 4016 dateRange: "com.apple.quicktime.HLS", 4017 emsg: "https://aomedia.org/emsg/ID3" 4018 }; 4019 4020 /** 4021 * Decode an ID3 PRIV frame. 4022 * 4023 * @param frame - the ID3 PRIV frame 4024 * 4025 * @returns The decoded ID3 PRIV frame 4026 * 4027 * @internal 4028 * 4029 * @group ID3 4030 */ 4031 function decodeId3PrivFrame(frame) { 4032 /* 4033 Format: <text string>\0<binary data> 4034 */
vendor: 12,153 bytes, lines 4035-4478
4035 if (frame.size < 2) { 4036 return undefined; 4037 } 4038 var owner = utf8ArrayToStr(frame.data, true); 4039 var privateData = new Uint8Array(frame.data.subarray(owner.length + 1)); 4040 return { 4041 key: frame.type, 4042 info: owner, 4043 data: privateData.buffer 4044 }; 4045 } 4046 4047 /** 4048 * Decodes an ID3 text frame 4049 * 4050 * @param frame - the ID3 text frame 4051 * 4052 * @returns The decoded ID3 text frame 4053 * 4054 * @internal 4055 * 4056 * @group ID3 4057 */ 4058 function decodeId3TextFrame(frame) { 4059 if (frame.size < 2) { 4060 return undefined; 4061 } 4062 if (frame.type === 'TXXX') { 4063 /* 4064 Format: 4065 [0] = {Text Encoding} 4066 [1-?] = {Description}\0{Value} 4067 */ 4068 var index = 1; 4069 var description = utf8ArrayToStr(frame.data.subarray(index), true); 4070 index += description.length + 1; 4071 var value = utf8ArrayToStr(frame.data.subarray(index)); 4072 return { 4073 key: frame.type, 4074 info: description, 4075 data: value 4076 }; 4077 } 4078 /* 4079 Format: 4080 [0] = {Text Encoding} 4081 [1-?] = {Value} 4082 */ 4083 var text = utf8ArrayToStr(frame.data.subarray(1)); 4084 return { 4085 key: frame.type, 4086 info: '', 4087 data: text 4088 }; 4089 } 4090 4091 /** 4092 * Decode a URL frame 4093 * 4094 * @param frame - the ID3 URL frame 4095 * 4096 * @returns The decoded ID3 URL frame 4097 * 4098 * @internal 4099 * 4100 * @group ID3 4101 */ 4102 function decodeId3UrlFrame(frame) { 4103 if (frame.type === 'WXXX') { 4104 /* 4105 Format: 4106 [0] = {Text Encoding} 4107 [1-?] = {Description}\0{URL} 4108 */ 4109 if (frame.size < 2) { 4110 return undefined; 4111 } 4112 var index = 1; 4113 var description = utf8ArrayToStr(frame.data.subarray(index), true); 4114 index += description.length + 1; 4115 var value = utf8ArrayToStr(frame.data.subarray(index)); 4116 return { 4117 key: frame.type, 4118 info: description, 4119 data: value 4120 }; 4121 } 4122 /* 4123 Format: 4124 [0-?] = {URL} 4125 */ 4126 var url = utf8ArrayToStr(frame.data); 4127 return { 4128 key: frame.type, 4129 info: '', 4130 data: url 4131 }; 4132 } 4133 4134 function toUint8(data, offset, length) { 4135 if (offset === void 0) { 4136 offset = 0; 4137 } 4138 if (length === void 0) { 4139 length = Infinity; 4140 } 4141 return view(data, offset, length, Uint8Array); 4142 } 4143 function view(data, offset, length, Type) { 4144 var buffer = unsafeGetArrayBuffer(data); 4145 var bytesPerElement = 1; 4146 if ('BYTES_PER_ELEMENT' in Type) { 4147 bytesPerElement = Type.BYTES_PER_ELEMENT; 4148 } 4149 // Absolute end of the |data| view within |buffer|. 4150 var dataOffset = isArrayBufferView(data) ? data.byteOffset : 0; 4151 var dataEnd = (dataOffset + data.byteLength) / bytesPerElement; 4152 // Absolute start of the result within |buffer|. 4153 var rawStart = (dataOffset + offset) / bytesPerElement; 4154 var start = Math.floor(Math.max(0, Math.min(rawStart, dataEnd))); 4155 // Absolute end of the result within |buffer|. 4156 var end = Math.floor(Math.min(start + Math.max(length, 0), dataEnd)); 4157 return new Type(buffer, start, end - start); 4158 } 4159 function unsafeGetArrayBuffer(view) { 4160 if (view instanceof ArrayBuffer) { 4161 return view; 4162 } else { 4163 return view.buffer; 4164 } 4165 } 4166 function isArrayBufferView(obj) { 4167 return obj && obj.buffer instanceof ArrayBuffer && obj.byteLength !== undefined && obj.byteOffset !== undefined; 4168 } 4169 4170 function toArrayBuffer(view) { 4171 if (view instanceof ArrayBuffer) { 4172 return view; 4173 } else { 4174 if (view.byteOffset == 0 && view.byteLength == view.buffer.byteLength) { 4175 // This is a TypedArray over the whole buffer. 4176 return view.buffer; 4177 } 4178 // This is a 'view' on the buffer. Create a new buffer that only contains 4179 // the data. Note that since this isn't an ArrayBuffer, the 'new' call 4180 // will allocate a new buffer to hold the copy. 4181 return new Uint8Array(view).buffer; 4182 } 4183 } 4184 4185 function decodeId3ImageFrame(frame) { 4186 var metadataFrame = { 4187 key: frame.type, 4188 description: '', 4189 data: '', 4190 mimeType: null, 4191 pictureType: null 4192 }; 4193 var utf8Encoding = 0x03; 4194 if (frame.size < 2) { 4195 return undefined; 4196 } 4197 if (frame.data[0] !== utf8Encoding) { 4198 console.log('Ignore frame with unrecognized character ' + 'encoding'); 4199 return undefined; 4200 } 4201 var mimeTypeEndIndex = frame.data.subarray(1).indexOf(0); 4202 if (mimeTypeEndIndex === -1) { 4203 return undefined; 4204 } 4205 var mimeType = utf8ArrayToStr(toUint8(frame.data, 1, mimeTypeEndIndex)); 4206 var pictureType = frame.data[2 + mimeTypeEndIndex]; 4207 var descriptionEndIndex = frame.data.subarray(3 + mimeTypeEndIndex).indexOf(0); 4208 if (descriptionEndIndex === -1) { 4209 return undefined; 4210 } 4211 var description = utf8ArrayToStr(toUint8(frame.data, 3 + mimeTypeEndIndex, descriptionEndIndex)); 4212 var data; 4213 if (mimeType === '-->') { 4214 data = utf8ArrayToStr(toUint8(frame.data, 4 + mimeTypeEndIndex + descriptionEndIndex)); 4215 } else { 4216 data = toArrayBuffer(frame.data.subarray(4 + mimeTypeEndIndex + descriptionEndIndex)); 4217 } 4218 metadataFrame.mimeType = mimeType; 4219 metadataFrame.pictureType = pictureType; 4220 metadataFrame.description = description; 4221 metadataFrame.data = data; 4222 return metadataFrame; 4223 } 4224 4225 /** 4226 * Decode an ID3 frame. 4227 * 4228 * @param frame - the ID3 frame 4229 * 4230 * @returns The decoded ID3 frame 4231 * 4232 * @internal 4233 * 4234 * @group ID3 4235 */ 4236 function decodeId3Frame(frame) { 4237 if (frame.type === 'PRIV') { 4238 return decodeId3PrivFrame(frame); 4239 } else if (frame.type[0] === 'W') { 4240 return decodeId3UrlFrame(frame); 4241 } else if (frame.type === 'APIC') { 4242 return decodeId3ImageFrame(frame); 4243 } 4244 return decodeId3TextFrame(frame); 4245 } 4246 4247 /** 4248 * Read ID3 size 4249 * 4250 * @param data - The data to read from 4251 * @param offset - The offset at which to start reading 4252 * 4253 * @returns The size 4254 * 4255 * @internal 4256 * 4257 * @group ID3 4258 */ 4259 function readId3Size(data, offset) { 4260 var size = 0; 4261 size = (data[offset] & 0x7f) << 21; 4262 size |= (data[offset + 1] & 0x7f) << 14; 4263 size |= (data[offset + 2] & 0x7f) << 7; 4264 size |= data[offset + 3] & 0x7f; 4265 return size; 4266 } 4267 4268 /** 4269 * Returns the data of an ID3 frame. 4270 * 4271 * @param data - The data to read from 4272 * 4273 * @returns The data of the ID3 frame 4274 * 4275 * @internal 4276 * 4277 * @group ID3 4278 */ 4279 function getId3FrameData(data) { 4280 /* 4281 Frame ID $xx xx xx xx (four characters) 4282 Size $xx xx xx xx 4283 Flags $xx xx 4284 */ 4285 var type = String.fromCharCode(data[0], data[1], data[2], data[3]); 4286 var size = readId3Size(data, 4); 4287 // skip frame id, size, and flags 4288 var offset = 10; 4289 return { 4290 type: type, 4291 size: size, 4292 data: data.subarray(offset, offset + size) 4293 }; 4294 } 4295 4296 /** 4297 * Returns true if an ID3 footer can be found at offset in data 4298 * 4299 * @param data - The data to search in 4300 * @param offset - The offset at which to start searching 4301 * 4302 * @returns `true` if an ID3 footer is found 4303 * 4304 * @internal 4305 * 4306 * @group ID3 4307 */ 4308 function isId3Footer(data, offset) { 4309 /* 4310 * The footer is a copy of the header, but with a different identifier 4311 */ 4312 if (offset + 10 <= data.length) { 4313 // look for '3DI' identifier 4314 if (data[offset] === 0x33 && data[offset + 1] === 0x44 && data[offset + 2] === 0x49) { 4315 // check version is within range 4316 if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) { 4317 // check size is within range 4318 if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) { 4319 return true; 4320 } 4321 } 4322 } 4323 } 4324 return false; 4325 } 4326 4327 /** 4328 * Returns true if an ID3 header can be found at offset in data 4329 * 4330 * @param data - The data to search in 4331 * @param offset - The offset at which to start searching 4332 * 4333 * @returns `true` if an ID3 header is found 4334 * 4335 * @internal 4336 * 4337 * @group ID3 4338 */ 4339 function isId3Header(data, offset) { 4340 /* 4341 * http://id3.org/id3v2.3.0 4342 * [0] = 'I' 4343 * [1] = 'D' 4344 * [2] = '3' 4345 * [3,4] = {Version} 4346 * [5] = {Flags} 4347 * [6-9] = {ID3 Size} 4348 * 4349 * An ID3v2 tag can be detected with the following pattern: 4350 * $49 44 33 yy yy xx zz zz zz zz 4351 * Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80 4352 */ 4353 if (offset + 10 <= data.length) { 4354 // look for 'ID3' identifier 4355 if (data[offset] === 0x49 && data[offset + 1] === 0x44 && data[offset + 2] === 0x33) { 4356 // check version is within range 4357 if (data[offset + 3] < 0xff && data[offset + 4] < 0xff) { 4358 // check size is within range 4359 if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) { 4360 return true; 4361 } 4362 } 4363 } 4364 } 4365 return false; 4366 } 4367 4368 var HEADER_FOOTER_SIZE = 10; 4369 var FRAME_SIZE = 10; 4370 /** 4371 * Returns an array of ID3 frames found in all the ID3 tags in the id3Data 4372 * 4373 * @param id3Data - The ID3 data containing one or more ID3 tags 4374 * 4375 * @returns Array of ID3 frame objects 4376 * 4377 * @group ID3 4378 * 4379 * @beta 4380 */ 4381 function getId3Frames(id3Data) { 4382 var offset = 0; 4383 var frames = []; 4384 while (isId3Header(id3Data, offset)) { 4385 var size = readId3Size(id3Data, offset + 6); 4386 if (id3Data[offset + 5] >> 6 & 1) { 4387 // skip extended header 4388 offset += HEADER_FOOTER_SIZE; 4389 } 4390 // skip past ID3 header 4391 offset += HEADER_FOOTER_SIZE; 4392 var end = offset + size; 4393 // loop through frames in the ID3 tag 4394 while (offset + FRAME_SIZE < end) { 4395 var frameData = getId3FrameData(id3Data.subarray(offset)); 4396 var frame = decodeId3Frame(frameData); 4397 if (frame) { 4398 frames.push(frame); 4399 } 4400 // skip frame header and frame data 4401 offset += frameData.size + HEADER_FOOTER_SIZE; 4402 } 4403 if (isId3Footer(id3Data, offset)) { 4404 offset += HEADER_FOOTER_SIZE; 4405 } 4406 } 4407 return frames; 4408 } 4409 4410 /** 4411 * Returns true if the ID3 frame is an Elementary Stream timestamp frame 4412 * 4413 * @param frame - the ID3 frame 4414 * 4415 * @returns `true` if the ID3 frame is an Elementary Stream timestamp frame 4416 * 4417 * @internal 4418 * 4419 * @group ID3 4420 */ 4421 function isId3TimestampFrame(frame) { 4422 return frame && frame.key === 'PRIV' && frame.info === 'com.apple.streaming.transportStreamTimestamp'; 4423 } 4424 4425 var MIN_CUE_DURATION = 0.25; 4426 function getCueClass() { 4427 if (typeof self === 'undefined') return undefined; 4428 return self.VTTCue || self.TextTrackCue; 4429 } 4430 function createCueWithDataFields(Cue, startTime, endTime, data, type) { 4431 var cue = new Cue(startTime, endTime, ''); 4432 try { 4433 cue.value = data; 4434 if (type) { 4435 cue.type = type; 4436 } 4437 } catch (e) { 4438 cue = new Cue(startTime, endTime, JSON.stringify(type ? _objectSpread2({ 4439 type: type 4440 }, data) : data)); 4441 } 4442 return cue; 4443 } 4444 4445 // VTTCue latest draft allows an infinite duration, fallback 4446 // to MAX_VALUE if necessary 4447 var MAX_CUE_ENDTIME = function () { 4448 var Cue = getCueClass(); 4449 try { 4450 Cue && new Cue(0, Number.POSITIVE_INFINITY, ''); 4451 } catch (e) { 4452 return Number.MAX_VALUE; 4453 } 4454 return Number.POSITIVE_INFINITY; 4455 }(); 4456 function hexToArrayBuffer(str) { 4457 return Uint8Array.from(str.replace(/^0x/, '').replace(/([\da-fA-F]{2}) ?/g, '0x$1 ').replace(/ +$/, '').split(' ')).buffer; 4458 } 4459 var ID3TrackController = /*#__PURE__*/function () { 4460 function ID3TrackController(hls) { 4461 this.hls = void 0; 4462 this.id3Track = null; 4463 this.media = null; 4464 this.dateRangeCuesAppended = {}; 4465 this.hls = hls; 4466 this._registerListeners(); 4467 } 4468 var _proto = ID3TrackController.prototype; 4469 _proto.destroy = function destroy() { 4470 this._unregisterListeners(); 4471 this.id3Track = null; 4472 this.media = null; 4473 this.dateRangeCuesAppended = {}; 4474 // @ts-ignore 4475 this.hls = null; 4476 }; 4477 _proto._registerListeners = function _registerListeners() { 4478 var hls = this.hls;
vendor: 5,855 bytes, lines 4479-4628
4479 hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); 4480 hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); 4481 hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); 4482 hls.on(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this); 4483 hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); 4484 hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); 4485 hls.on(Events.LEVEL_PTS_UPDATED, this.onLevelPtsUpdated, this); 4486 }; 4487 _proto._unregisterListeners = function _unregisterListeners() { 4488 var hls = this.hls; 4489 hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); 4490 hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); 4491 hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); 4492 hls.off(Events.FRAG_PARSING_METADATA, this.onFragParsingMetadata, this); 4493 hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); 4494 hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); 4495 hls.off(Events.LEVEL_PTS_UPDATED, this.onLevelPtsUpdated, this); 4496 } 4497 4498 // Add ID3 metatadata text track. 4499 ; 4500 _proto.onMediaAttached = function onMediaAttached(event, data) { 4501 this.media = data.media; 4502 }; 4503 _proto.onMediaDetaching = function onMediaDetaching() { 4504 if (this.id3Track) { 4505 clearCurrentCues(this.id3Track); 4506 this.id3Track = null; 4507 } 4508 this.media = null; 4509 this.dateRangeCuesAppended = {}; 4510 }; 4511 _proto.onManifestLoading = function onManifestLoading() { 4512 this.dateRangeCuesAppended = {}; 4513 }; 4514 _proto.createTrack = function createTrack(media) { 4515 var track = this.getID3Track(media.textTracks); 4516 track.mode = 'hidden'; 4517 return track; 4518 }; 4519 _proto.getID3Track = function getID3Track(textTracks) { 4520 if (!this.media) { 4521 return; 4522 } 4523 for (var i = 0; i < textTracks.length; i++) { 4524 var textTrack = textTracks[i]; 4525 if (textTrack.kind === 'metadata' && textTrack.label === 'id3') { 4526 // send 'addtrack' when reusing the textTrack for metadata, 4527 // same as what we do for captions 4528 sendAddTrackEvent(textTrack, this.media); 4529 return textTrack; 4530 } 4531 } 4532 return this.media.addTextTrack('metadata', 'id3'); 4533 }; 4534 _proto.onFragParsingMetadata = function onFragParsingMetadata(event, data) { 4535 if (!this.media) { 4536 return; 4537 } 4538 var _this$hls$config = this.hls.config, 4539 enableEmsgMetadataCues = _this$hls$config.enableEmsgMetadataCues, 4540 enableID3MetadataCues = _this$hls$config.enableID3MetadataCues; 4541 if (!enableEmsgMetadataCues && !enableID3MetadataCues) { 4542 return; 4543 } 4544 var samples = data.samples; 4545 4546 // create track dynamically 4547 if (!this.id3Track) { 4548 this.id3Track = this.createTrack(this.media); 4549 } 4550 var Cue = getCueClass(); 4551 if (!Cue) { 4552 return; 4553 } 4554 for (var i = 0; i < samples.length; i++) { 4555 var type = samples[i].type; 4556 if (type === MetadataSchema.emsg && !enableEmsgMetadataCues || !enableID3MetadataCues) { 4557 continue; 4558 } 4559 var frames = getId3Frames(samples[i].data); 4560 if (frames) { 4561 var startTime = samples[i].pts; 4562 var endTime = startTime + samples[i].duration; 4563 if (endTime > MAX_CUE_ENDTIME) { 4564 endTime = MAX_CUE_ENDTIME; 4565 } 4566 var timeDiff = endTime - startTime; 4567 if (timeDiff <= 0) { 4568 endTime = startTime + MIN_CUE_DURATION; 4569 } 4570 for (var j = 0; j < frames.length; j++) { 4571 var frame = frames[j]; 4572 // Safari doesn't put the timestamp frame in the TextTrack 4573 if (!isId3TimestampFrame(frame)) { 4574 // add a bounds to any unbounded cues 4575 this.updateId3CueEnds(startTime, type); 4576 var cue = createCueWithDataFields(Cue, startTime, endTime, frame, type); 4577 if (cue) { 4578 this.id3Track.addCue(cue); 4579 } 4580 } 4581 } 4582 } 4583 } 4584 }; 4585 _proto.updateId3CueEnds = function updateId3CueEnds(startTime, type) { 4586 var _this$id3Track; 4587 var cues = (_this$id3Track = this.id3Track) == null ? void 0 : _this$id3Track.cues; 4588 if (cues) { 4589 for (var i = cues.length; i--;) { 4590 var cue = cues[i]; 4591 if (cue.type === type && cue.startTime < startTime && cue.endTime === MAX_CUE_ENDTIME) { 4592 cue.endTime = startTime; 4593 } 4594 } 4595 } 4596 }; 4597 _proto.onBufferFlushing = function onBufferFlushing(event, _ref) { 4598 var startOffset = _ref.startOffset, 4599 endOffset = _ref.endOffset, 4600 type = _ref.type; 4601 var id3Track = this.id3Track, 4602 hls = this.hls; 4603 if (!hls) { 4604 return; 4605 } 4606 var _hls$config = hls.config, 4607 enableEmsgMetadataCues = _hls$config.enableEmsgMetadataCues, 4608 enableID3MetadataCues = _hls$config.enableID3MetadataCues; 4609 if (id3Track && (enableEmsgMetadataCues || enableID3MetadataCues)) { 4610 var predicate; 4611 if (type === 'audio') { 4612 predicate = function predicate(cue) { 4613 return cue.type === MetadataSchema.audioId3 && enableID3MetadataCues; 4614 }; 4615 } else if (type === 'video') { 4616 predicate = function predicate(cue) { 4617 return cue.type === MetadataSchema.emsg && enableEmsgMetadataCues; 4618 }; 4619 } else { 4620 predicate = function predicate(cue) { 4621 return cue.type === MetadataSchema.audioId3 && enableID3MetadataCues || cue.type === MetadataSchema.emsg && enableEmsgMetadataCues; 4622 }; 4623 } 4624 removeCuesInRange(id3Track, startOffset, endOffset, predicate); 4625 } 4626 }; 4627 _proto.onLevelUpdated = function onLevelUpdated(event, _ref2) { 4628 var details = _ref2.details;
4629 this.updateDateRangeCues(details, true); 4630 }; 4631 _proto.onLevelPtsUpdated = function onLevelPtsUpdated(event, data) { 4632 if (Math.abs(data.drift) > 0.01) { 4633 this.updateDateRangeCues(data.details); 4634 } 4635 }; 4636 _proto.updateDateRangeCues = function updateDateRangeCues(details, removeOldCues) { 4637 var _this = this; 4638 if (!this.media || !details.hasProgramDateTime || !this.hls.config.enableDateRangeMetadataCues) { 4639 return; 4640 } 4641 var dateRangeCuesAppended = this.dateRangeCuesAppended, 4642 id3Track = this.id3Track; 4643 var dateRanges = details.dateRanges; 4644 var ids = Object.keys(dateRanges); 4645 // Remove cues from track not found in details.dateRanges 4646 if (id3Track && removeOldCues) { 4647 var idsToRemove = Object.keys(dateRangeCuesAppended).filter(function (id) { 4648 return !ids.includes(id); 4649 }); 4650 var _loop = function _loop() { 4651 var id = idsToRemove[i]; 4652 Object.keys(dateRangeCuesAppended[id].cues).forEach(function (key) { 4653 id3Track.removeCue(dateRangeCuesAppended[id].cues[key]); 4654 }); 4655 delete dateRangeCuesAppended[id]; 4656 }; 4657 for (var i = idsToRemove.length; i--;) { 4658 _loop(); 4659 } 4660 } 4661 // Exit if the playlist does not have Date Ranges or does not have Program Date Time 4662 var lastFragment = details.fragments[details.fragments.length - 1]; 4663 if (ids.length === 0 || !isFiniteNumber(lastFragment == null ? void 0 : lastFragment.programDateTime)) { 4664 return; 4665 } 4666 if (!this.id3Track) { 4667 this.id3Track = this.createTrack(this.media); 4668 } 4669 var Cue = getCueClass(); 4670 var _loop2 = function _loop2() { 4671 var id = ids[_i]; 4672 var dateRange = dateRanges[id]; 4673 var startTime = dateRange.startTime; 4674 4675 // Process DateRanges to determine end-time (known DURATION, END-DATE, or END-ON-NEXT) 4676 var appendedDateRangeCues = dateRangeCuesAppended[id]; 4677 var cues = (appendedDateRangeCues == null ? void 0 : appendedDateRangeCues.cues) || {}; 4678 var durationKnown = (appendedDateRangeCues == null ? void 0 : appendedDateRangeCues.durationKnown) || false; 4679 var endTime = MAX_CUE_ENDTIME; 4680 var duration = dateRange.duration, 4681 endDate = dateRange.endDate; 4682 if (endDate && duration !== null) { 4683 endTime = startTime + duration; 4684 durationKnown = true; 4685 } else if (dateRange.endOnNext && !durationKnown) { 4686 var nextDateRangeWithSameClass = ids.reduce(function (candidateDateRange, id) { 4687 if (id !== dateRange.id) { 4688 var otherDateRange = dateRanges[id]; 4689 if (otherDateRange.class === dateRange.class && otherDateRange.startDate > dateRange.startDate && (!candidateDateRange || dateRange.startDate < candidateDateRange.startDate)) { 4690 return otherDateRange; 4691 } 4692 } 4693 return candidateDateRange; 4694 }, null); 4695 if (nextDateRangeWithSameClass) { 4696 endTime = nextDateRangeWithSameClass.startTime; 4697 durationKnown = true; 4698 } 4699 } 4700 4701 // Create TextTrack Cues for each MetadataGroup Item (select DateRange attribute) 4702 // This is to emulate Safari HLS playback handling of DateRange tags 4703 var attributes = Object.keys(dateRange.attr); 4704 for (var j = 0; j < attributes.length; j++) { 4705 var key = attributes[j]; 4706 if (!isDateRangeCueAttribute(key)) { 4707 continue; 4708 } 4709 var cue = cues[key]; 4710 if (cue) { 4711 if (durationKnown && !appendedDateRangeCues.durationKnown) { 4712 cue.endTime = endTime; 4713 } else if (Math.abs(cue.startTime - startTime) > 0.01) { 4714 cue.startTime = startTime; 4715 cue.endTime = endTime; 4716 } 4717 } else if (Cue) { 4718 var data = dateRange.attr[key]; 4719 if (isSCTE35Attribute(key)) { 4720 data = hexToArrayBuffer(data); 4721 } 4722 var _cue = createCueWithDataFields(Cue, startTime, endTime, { 4723 key: key, 4724 data: data 4725 }, MetadataSchema.dateRange); 4726 if (_cue) { 4727 _cue.id = id; 4728 _this.id3Track.addCue(_cue); 4729 cues[key] = _cue; 4730 } 4731 } 4732 } 4733 4734 // Keep track of processed DateRanges by ID for updating cues with new DateRange tag attributes 4735 dateRangeCuesAppended[id] = { 4736 cues: cues, 4737 dateRange: dateRange, 4738 durationKnown: durationKnown 4739 }; 4740 }; 4741 for (var _i = 0; _i < ids.length; _i++) { 4742 _loop2(); 4743 } 4744 }; 4745 return ID3TrackController; 4746 }(); 4747 4748 var LatencyController = /*#__PURE__*/function () { 4749 function LatencyController(hls) { 4750 var _this = this; 4751 this.hls = void 0; 4752 this.config = void 0; 4753 this.media = null; 4754 this.levelDetails = null; 4755 this.currentTime = 0; 4756 this.stallCount = 0; 4757 this._latency = null; 4758 this._targetLatencyUpdated = false;
vendor: 12,681 bytes, lines 4759-5101
4759 this.onTimeupdate = function () { 4760 var media = _this.media, 4761 levelDetails = _this.levelDetails; 4762 if (!media || !levelDetails) { 4763 return; 4764 } 4765 _this.currentTime = media.currentTime; 4766 var latency = _this.computeLatency(); 4767 if (latency === null) { 4768 return; 4769 } 4770 _this._latency = latency; 4771 4772 // Adapt playbackRate to meet target latency in low-latency mode 4773 var _this$config = _this.config, 4774 lowLatencyMode = _this$config.lowLatencyMode, 4775 maxLiveSyncPlaybackRate = _this$config.maxLiveSyncPlaybackRate; 4776 if (!lowLatencyMode || maxLiveSyncPlaybackRate === 1 || !levelDetails.live) { 4777 return; 4778 } 4779 var targetLatency = _this.targetLatency; 4780 if (targetLatency === null) { 4781 return; 4782 } 4783 var distanceFromTarget = latency - targetLatency; 4784 // Only adjust playbackRate when within one target duration of targetLatency 4785 // and more than one second from under-buffering. 4786 // Playback further than one target duration from target can be considered DVR playback. 4787 var liveMinLatencyDuration = Math.min(_this.maxLatency, targetLatency + levelDetails.targetduration); 4788 var inLiveRange = distanceFromTarget < liveMinLatencyDuration; 4789 if (inLiveRange && distanceFromTarget > 0.05 && _this.forwardBufferLength > 1) { 4790 var max = Math.min(2, Math.max(1.0, maxLiveSyncPlaybackRate)); 4791 var rate = Math.round(2 / (1 + Math.exp(-0.75 * distanceFromTarget - _this.edgeStalled)) * 20) / 20; 4792 media.playbackRate = Math.min(max, Math.max(1, rate)); 4793 } else if (media.playbackRate !== 1 && media.playbackRate !== 0) { 4794 media.playbackRate = 1; 4795 } 4796 }; 4797 this.hls = hls; 4798 this.config = hls.config; 4799 this.registerListeners(); 4800 } 4801 var _proto = LatencyController.prototype; 4802 _proto.destroy = function destroy() { 4803 this.unregisterListeners(); 4804 this.onMediaDetaching(); 4805 this.levelDetails = null; 4806 // @ts-ignore 4807 this.hls = null; 4808 }; 4809 _proto.registerListeners = function registerListeners() { 4810 this.hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); 4811 this.hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); 4812 this.hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); 4813 this.hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); 4814 this.hls.on(Events.ERROR, this.onError, this); 4815 }; 4816 _proto.unregisterListeners = function unregisterListeners() { 4817 this.hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); 4818 this.hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); 4819 this.hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); 4820 this.hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); 4821 this.hls.off(Events.ERROR, this.onError, this); 4822 }; 4823 _proto.onMediaAttached = function onMediaAttached(event, data) { 4824 this.media = data.media; 4825 this.media.addEventListener('timeupdate', this.onTimeupdate); 4826 }; 4827 _proto.onMediaDetaching = function onMediaDetaching() { 4828 if (this.media) { 4829 this.media.removeEventListener('timeupdate', this.onTimeupdate); 4830 this.media = null; 4831 } 4832 }; 4833 _proto.onManifestLoading = function onManifestLoading() { 4834 this.levelDetails = null; 4835 this._latency = null; 4836 this.stallCount = 0; 4837 }; 4838 _proto.onLevelUpdated = function onLevelUpdated(event, _ref) { 4839 var details = _ref.details; 4840 this.levelDetails = details; 4841 if (details.advanced) { 4842 this.onTimeupdate(); 4843 } 4844 if (!details.live && this.media) { 4845 this.media.removeEventListener('timeupdate', this.onTimeupdate); 4846 } 4847 }; 4848 _proto.onError = function onError(event, data) { 4849 var _this$levelDetails; 4850 if (data.details !== ErrorDetails.BUFFER_STALLED_ERROR) { 4851 return; 4852 } 4853 this.stallCount++; 4854 if ((_this$levelDetails = this.levelDetails) != null && _this$levelDetails.live) { 4855 this.hls.logger.warn('[latency-controller]: Stall detected, adjusting target latency'); 4856 } 4857 }; 4858 _proto.estimateLiveEdge = function estimateLiveEdge() { 4859 var levelDetails = this.levelDetails; 4860 if (levelDetails === null) { 4861 return null; 4862 } 4863 return levelDetails.edge + levelDetails.age; 4864 }; 4865 _proto.computeLatency = function computeLatency() { 4866 var liveEdge = this.estimateLiveEdge(); 4867 if (liveEdge === null) { 4868 return null; 4869 } 4870 return liveEdge - this.currentTime; 4871 }; 4872 return _createClass(LatencyController, [{ 4873 key: "latency", 4874 get: function get() { 4875 return this._latency || 0; 4876 } 4877 }, { 4878 key: "maxLatency", 4879 get: function get() { 4880 var config = this.config, 4881 levelDetails = this.levelDetails; 4882 if (config.liveMaxLatencyDuration !== undefined) { 4883 return config.liveMaxLatencyDuration; 4884 } 4885 return levelDetails ? config.liveMaxLatencyDurationCount * levelDetails.targetduration : 0; 4886 } 4887 }, { 4888 key: "targetLatency", 4889 get: function get() { 4890 var levelDetails = this.levelDetails; 4891 if (levelDetails === null) { 4892 return null; 4893 } 4894 var holdBack = levelDetails.holdBack, 4895 partHoldBack = levelDetails.partHoldBack, 4896 targetduration = levelDetails.targetduration; 4897 var _this$config2 = this.config, 4898 liveSyncDuration = _this$config2.liveSyncDuration, 4899 liveSyncDurationCount = _this$config2.liveSyncDurationCount, 4900 lowLatencyMode = _this$config2.lowLatencyMode; 4901 var userConfig = this.hls.userConfig; 4902 var targetLatency = lowLatencyMode ? partHoldBack || holdBack : holdBack; 4903 if (this._targetLatencyUpdated || userConfig.liveSyncDuration || userConfig.liveSyncDurationCount || targetLatency === 0) { 4904 targetLatency = liveSyncDuration !== undefined ? liveSyncDuration : liveSyncDurationCount * targetduration; 4905 } 4906 var maxLiveSyncOnStallIncrease = targetduration; 4907 return targetLatency + Math.min(this.stallCount * this.config.liveSyncOnStallIncrease, maxLiveSyncOnStallIncrease); 4908 }, 4909 set: function set(latency) { 4910 this.stallCount = 0; 4911 this.config.liveSyncDuration = latency; 4912 this._targetLatencyUpdated = true; 4913 } 4914 }, { 4915 key: "liveSyncPosition", 4916 get: function get() { 4917 var liveEdge = this.estimateLiveEdge(); 4918 var targetLatency = this.targetLatency; 4919 var levelDetails = this.levelDetails; 4920 if (liveEdge === null || targetLatency === null || levelDetails === null) { 4921 return null; 4922 } 4923 var edge = levelDetails.edge; 4924 var syncPosition = liveEdge - targetLatency - this.edgeStalled; 4925 var min = edge - levelDetails.totalduration; 4926 var max = edge - (this.config.lowLatencyMode && levelDetails.partTarget || levelDetails.targetduration); 4927 return Math.min(Math.max(min, syncPosition), max); 4928 } 4929 }, { 4930 key: "drift", 4931 get: function get() { 4932 var levelDetails = this.levelDetails; 4933 if (levelDetails === null) { 4934 return 1; 4935 } 4936 return levelDetails.drift; 4937 } 4938 }, { 4939 key: "edgeStalled", 4940 get: function get() { 4941 var levelDetails = this.levelDetails; 4942 if (levelDetails === null) { 4943 return 0; 4944 } 4945 var maxLevelUpdateAge = (this.config.lowLatencyMode && levelDetails.partTarget || levelDetails.targetduration) * 3; 4946 return Math.max(levelDetails.age - maxLevelUpdateAge, 0); 4947 } 4948 }, { 4949 key: "forwardBufferLength", 4950 get: function get() { 4951 var media = this.media, 4952 levelDetails = this.levelDetails; 4953 if (!media || !levelDetails) { 4954 return 0; 4955 } 4956 var bufferedRanges = media.buffered.length; 4957 return (bufferedRanges ? media.buffered.end(bufferedRanges - 1) : levelDetails.edge) - this.currentTime; 4958 } 4959 }]); 4960 }(); 4961 4962 var HdcpLevels = ['NONE', 'TYPE-0', 'TYPE-1', null]; 4963 function isHdcpLevel(value) { 4964 return HdcpLevels.indexOf(value) > -1; 4965 } 4966 var VideoRangeValues = ['SDR', 'PQ', 'HLG']; 4967 function isVideoRange(value) { 4968 return !!value && VideoRangeValues.indexOf(value) > -1; 4969 } 4970 var HlsSkip = { 4971 No: "", 4972 Yes: "YES", 4973 v2: "v2" 4974 }; 4975 function getSkipValue(details) { 4976 var canSkipUntil = details.canSkipUntil, 4977 canSkipDateRanges = details.canSkipDateRanges, 4978 age = details.age; 4979 // A Client SHOULD NOT request a Playlist Delta Update unless it already 4980 // has a version of the Playlist that is no older than one-half of the Skip Boundary. 4981 // @see: https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis#section-6.3.7 4982 var playlistRecentEnough = age < canSkipUntil / 2; 4983 if (canSkipUntil && playlistRecentEnough) { 4984 if (canSkipDateRanges) { 4985 return HlsSkip.v2; 4986 } 4987 return HlsSkip.Yes; 4988 } 4989 return HlsSkip.No; 4990 } 4991 var HlsUrlParameters = /*#__PURE__*/function () { 4992 function HlsUrlParameters(msn, part, skip) { 4993 this.msn = void 0; 4994 this.part = void 0; 4995 this.skip = void 0; 4996 this.msn = msn; 4997 this.part = part; 4998 this.skip = skip; 4999 } 5000 var _proto = HlsUrlParameters.prototype; 5001 _proto.addDirectives = function addDirectives(uri) { 5002 var url = new self.URL(uri); 5003 if (this.msn !== undefined) { 5004 url.searchParams.set('_HLS_msn', this.msn.toString()); 5005 } 5006 if (this.part !== undefined) { 5007 url.searchParams.set('_HLS_part', this.part.toString()); 5008 } 5009 if (this.skip) { 5010 url.searchParams.set('_HLS_skip', this.skip); 5011 } 5012 return url.href; 5013 }; 5014 return HlsUrlParameters; 5015 }(); 5016 var Level = /*#__PURE__*/function () { 5017 function Level(data) { 5018 this._attrs = void 0; 5019 this.audioCodec = void 0; 5020 this.bitrate = void 0; 5021 this.codecSet = void 0; 5022 this.url = void 0; 5023 this.frameRate = void 0; 5024 this.height = void 0; 5025 this.id = void 0; 5026 this.name = void 0; 5027 this.videoCodec = void 0; 5028 this.width = void 0; 5029 this.details = void 0; 5030 this.fragmentError = 0; 5031 this.loadError = 0; 5032 this.loaded = void 0; 5033 this.realBitrate = 0; 5034 this.supportedPromise = void 0; 5035 this.supportedResult = void 0; 5036 this._avgBitrate = 0; 5037 this._audioGroups = void 0; 5038 this._subtitleGroups = void 0; 5039 // Deprecated (retained for backwards compatibility) 5040 this._urlId = 0; 5041 this.url = [data.url]; 5042 this._attrs = [data.attrs]; 5043 this.bitrate = data.bitrate; 5044 if (data.details) { 5045 this.details = data.details; 5046 } 5047 this.id = data.id || 0; 5048 this.name = data.name; 5049 this.width = data.width || 0; 5050 this.height = data.height || 0; 5051 this.frameRate = data.attrs.optionalFloat('FRAME-RATE', 0); 5052 this._avgBitrate = data.attrs.decimalInteger('AVERAGE-BANDWIDTH'); 5053 this.audioCodec = data.audioCodec; 5054 this.videoCodec = data.videoCodec; 5055 this.codecSet = [data.videoCodec, data.audioCodec].filter(function (c) { 5056 return !!c; 5057 }).map(function (s) { 5058 return s.substring(0, 4); 5059 }).join(','); 5060 this.addGroupId('audio', data.attrs.AUDIO); 5061 this.addGroupId('text', data.attrs.SUBTITLES); 5062 } 5063 var _proto2 = Level.prototype; 5064 _proto2.hasAudioGroup = function hasAudioGroup(groupId) { 5065 return hasGroup(this._audioGroups, groupId); 5066 }; 5067 _proto2.hasSubtitleGroup = function hasSubtitleGroup(groupId) { 5068 return hasGroup(this._subtitleGroups, groupId); 5069 }; 5070 _proto2.addGroupId = function addGroupId(type, groupId) { 5071 if (!groupId) { 5072 return; 5073 } 5074 if (type === 'audio') { 5075 var audioGroups = this._audioGroups; 5076 if (!audioGroups) { 5077 audioGroups = this._audioGroups = []; 5078 } 5079 if (audioGroups.indexOf(groupId) === -1) { 5080 audioGroups.push(groupId); 5081 } 5082 } else if (type === 'text') { 5083 var subtitleGroups = this._subtitleGroups; 5084 if (!subtitleGroups) { 5085 subtitleGroups = this._subtitleGroups = []; 5086 } 5087 if (subtitleGroups.indexOf(groupId) === -1) { 5088 subtitleGroups.push(groupId); 5089 } 5090 } 5091 } 5092 5093 // Deprecated methods (retained for backwards compatibility) 5094 ; 5095 _proto2.addFallback = function addFallback() {}; 5096 return _createClass(Level, [{ 5097 key: "maxBitrate", 5098 get: function get() { 5099 return Math.max(this.realBitrate, this.bitrate); 5100 } 5101 }, {
vendor: 4,142 bytes, lines 5102-5237
5102 key: "averageBitrate", 5103 get: function get() { 5104 return this._avgBitrate || this.realBitrate || this.bitrate; 5105 } 5106 }, { 5107 key: "attrs", 5108 get: function get() { 5109 return this._attrs[0]; 5110 } 5111 }, { 5112 key: "codecs", 5113 get: function get() { 5114 return this.attrs.CODECS || ''; 5115 } 5116 }, { 5117 key: "pathwayId", 5118 get: function get() { 5119 return this.attrs['PATHWAY-ID'] || '.'; 5120 } 5121 }, { 5122 key: "videoRange", 5123 get: function get() { 5124 return this.attrs['VIDEO-RANGE'] || 'SDR'; 5125 } 5126 }, { 5127 key: "score", 5128 get: function get() { 5129 return this.attrs.optionalFloat('SCORE', 0); 5130 } 5131 }, { 5132 key: "uri", 5133 get: function get() { 5134 return this.url[0] || ''; 5135 } 5136 }, { 5137 key: "audioGroups", 5138 get: function get() { 5139 return this._audioGroups; 5140 } 5141 }, { 5142 key: "subtitleGroups", 5143 get: function get() { 5144 return this._subtitleGroups; 5145 } 5146 }, { 5147 key: "urlId", 5148 get: function get() { 5149 return 0; 5150 }, 5151 set: function set(value) {} 5152 }, { 5153 key: "audioGroupIds", 5154 get: function get() { 5155 return this.audioGroups ? [this.audioGroupId] : undefined; 5156 } 5157 }, { 5158 key: "textGroupIds", 5159 get: function get() { 5160 return this.subtitleGroups ? [this.textGroupId] : undefined; 5161 } 5162 }, { 5163 key: "audioGroupId", 5164 get: function get() { 5165 var _this$audioGroups; 5166 return (_this$audioGroups = this.audioGroups) == null ? void 0 : _this$audioGroups[0]; 5167 } 5168 }, { 5169 key: "textGroupId", 5170 get: function get() { 5171 var _this$subtitleGroups; 5172 return (_this$subtitleGroups = this.subtitleGroups) == null ? void 0 : _this$subtitleGroups[0]; 5173 } 5174 }]); 5175 }(); 5176 function hasGroup(groups, groupId) { 5177 if (!groupId || !groups) { 5178 return false; 5179 } 5180 return groups.indexOf(groupId) !== -1; 5181 } 5182 5183 function updateFromToPTS(fragFrom, fragTo) { 5184 var fragToPTS = fragTo.startPTS; 5185 // if we know startPTS[toIdx] 5186 if (isFiniteNumber(fragToPTS)) { 5187 // update fragment duration. 5188 // it helps to fix drifts between playlist reported duration and fragment real duration 5189 var duration = 0; 5190 var frag; 5191 if (fragTo.sn > fragFrom.sn) { 5192 duration = fragToPTS - fragFrom.start; 5193 frag = fragFrom; 5194 } else { 5195 duration = fragFrom.start - fragToPTS; 5196 frag = fragTo; 5197 } 5198 if (frag.duration !== duration) { 5199 frag.duration = duration; 5200 } 5201 // we dont know startPTS[toIdx] 5202 } else if (fragTo.sn > fragFrom.sn) { 5203 var contiguous = fragFrom.cc === fragTo.cc; 5204 // TODO: With part-loading end/durations we need to confirm the whole fragment is loaded before using (or setting) minEndPTS 5205 if (contiguous && fragFrom.minEndPTS) { 5206 fragTo.start = fragFrom.start + (fragFrom.minEndPTS - fragFrom.start); 5207 } else { 5208 fragTo.start = fragFrom.start + fragFrom.duration; 5209 } 5210 } else { 5211 fragTo.start = Math.max(fragFrom.start - fragTo.duration, 0); 5212 } 5213 } 5214 function updateFragPTSDTS(details, frag, startPTS, endPTS, startDTS, endDTS) { 5215 var parsedMediaDuration = endPTS - startPTS; 5216 if (parsedMediaDuration <= 0) { 5217 logger.warn('Fragment should have a positive duration', frag); 5218 endPTS = startPTS + frag.duration; 5219 endDTS = startDTS + frag.duration; 5220 } 5221 var maxStartPTS = startPTS; 5222 var minEndPTS = endPTS; 5223 var fragStartPts = frag.startPTS; 5224 var fragEndPts = frag.endPTS; 5225 if (isFiniteNumber(fragStartPts)) { 5226 // delta PTS between audio and video 5227 var deltaPTS = Math.abs(fragStartPts - startPTS); 5228 if (!isFiniteNumber(frag.deltaPTS)) { 5229 frag.deltaPTS = deltaPTS; 5230 } else { 5231 frag.deltaPTS = Math.max(deltaPTS, frag.deltaPTS); 5232 } 5233 maxStartPTS = Math.max(startPTS, fragStartPts); 5234 startPTS = Math.min(startPTS, fragStartPts); 5235 startDTS = Math.min(startDTS, frag.startDTS); 5236 minEndPTS = Math.min(endPTS, fragEndPts); 5237 endPTS = Math.max(endPTS, fragEndPts);
5238 endDTS = Math.max(endDTS, frag.endDTS); 5239 } 5240 var drift = startPTS - frag.start; 5241 if (frag.start !== 0) { 5242 frag.start = startPTS; 5243 } 5244 frag.duration = endPTS - frag.start; 5245 frag.startPTS = startPTS; 5246 frag.maxStartPTS = maxStartPTS; 5247 frag.startDTS = startDTS; 5248 frag.endPTS = endPTS; 5249 frag.minEndPTS = minEndPTS; 5250 frag.endDTS = endDTS; 5251 var sn = frag.sn; 5252 // exit if sn out of range 5253 if (!details || sn < details.startSN || sn > details.endSN) { 5254 return 0; 5255 } 5256 var i; 5257 var fragIdx = sn - details.startSN; 5258 var fragments = details.fragments; 5259 // update frag reference in fragments array 5260 // rationale is that fragments array might not contain this frag object. 5261 // this will happen if playlist has been refreshed between frag loading and call to updateFragPTSDTS() 5262 // if we don't update frag, we won't be able to propagate PTS info on the playlist 5263 // resulting in invalid sliding computation 5264 fragments[fragIdx] = frag; 5265 // adjust fragment PTS/duration from seqnum-1 to frag 0 5266 for (i = fragIdx; i > 0; i--) { 5267 updateFromToPTS(fragments[i], fragments[i - 1]); 5268 } 5269 5270 // adjust fragment PTS/duration from seqnum to last frag 5271 for (i = fragIdx; i < fragments.length - 1; i++) { 5272 updateFromToPTS(fragments[i], fragments[i + 1]); 5273 } 5274 if (details.fragmentHint) { 5275 updateFromToPTS(fragments[fragments.length - 1], details.fragmentHint); 5276 } 5277 details.PTSKnown = details.alignedSliding = true; 5278 return drift; 5279 } 5280 function mergeDetails(oldDetails, newDetails) { 5281 // Track the last initSegment processed. Initialize it to the last one on the timeline. 5282 var currentInitSegment = null; 5283 var oldFragments = oldDetails.fragments; 5284 for (var i = oldFragments.length - 1; i >= 0; i--) { 5285 var oldInit = oldFragments[i].initSegment; 5286 if (oldInit) { 5287 currentInitSegment = oldInit; 5288 break; 5289 } 5290 } 5291 if (oldDetails.fragmentHint) { 5292 // prevent PTS and duration from being adjusted on the next hint 5293 delete oldDetails.fragmentHint.endPTS; 5294 } 5295 // check if old/new playlists have fragments in common 5296 // loop through overlapping SN and update startPTS , cc, and duration if any found 5297 var ccOffset = 0; 5298 var PTSFrag; 5299 mapFragmentIntersection(oldDetails, newDetails, function (oldFrag, newFrag) { 5300 if (oldFrag.relurl) { 5301 // Do not compare CC if the old fragment has no url. This is a level.fragmentHint used by LL-HLS parts. 5302 // It maybe be off by 1 if it was created before any parts or discontinuity tags were appended to the end 5303 // of the playlist. 5304 ccOffset = oldFrag.cc - newFrag.cc; 5305 } 5306 if (isFiniteNumber(oldFrag.startPTS) && isFiniteNumber(oldFrag.endPTS)) { 5307 newFrag.start = newFrag.startPTS = oldFrag.startPTS; 5308 newFrag.startDTS = oldFrag.startDTS; 5309 newFrag.maxStartPTS = oldFrag.maxStartPTS; 5310 newFrag.endPTS = oldFrag.endPTS; 5311 newFrag.endDTS = oldFrag.endDTS; 5312 newFrag.minEndPTS = oldFrag.minEndPTS; 5313 newFrag.duration = oldFrag.endPTS - oldFrag.startPTS; 5314 if (newFrag.duration) { 5315 PTSFrag = newFrag; 5316 } 5317 5318 // PTS is known when any segment has startPTS and endPTS 5319 newDetails.PTSKnown = newDetails.alignedSliding = true; 5320 } 5321 newFrag.elementaryStreams = oldFrag.elementaryStreams; 5322 newFrag.loader = oldFrag.loader; 5323 newFrag.stats = oldFrag.stats; 5324 if (oldFrag.initSegment) { 5325 newFrag.initSegment = oldFrag.initSegment; 5326 currentInitSegment = oldFrag.initSegment; 5327 } 5328 }); 5329 var fragmentsToCheck = newDetails.fragmentHint ? newDetails.fragments.concat(newDetails.fragmentHint) : newDetails.fragments; 5330 if (currentInitSegment) { 5331 fragmentsToCheck.forEach(function (frag) { 5332 var _currentInitSegment; 5333 if (frag && (!frag.initSegment || frag.initSegment.relurl === ((_currentInitSegment = currentInitSegment) == null ? void 0 : _currentInitSegment.relurl))) { 5334 frag.initSegment = currentInitSegment; 5335 } 5336 }); 5337 } 5338 if (newDetails.skippedSegments) { 5339 newDetails.deltaUpdateFailed = newDetails.fragments.some(function (frag) { 5340 return !frag; 5341 }); 5342 if (newDetails.deltaUpdateFailed) { 5343 logger.warn('[level-helper] Previous playlist missing segments ski
vendor: 9,069 bytes, lines 5343-5579
5343pped in delta playlist'); 5344 for (var _i = newDetails.skippedSegments; _i--;) { 5345 newDetails.fragments.shift(); 5346 } 5347 newDetails.startSN = newDetails.fragments[0].sn; 5348 newDetails.startCC = newDetails.fragments[0].cc; 5349 } else { 5350 if (newDetails.canSkipDateRanges) { 5351 newDetails.dateRanges = mergeDateRanges(oldDetails.dateRanges, newDetails); 5352 } 5353 var programDateTimes = oldDetails.fragments.filter(function (frag) { 5354 return frag.rawProgramDateTime; 5355 }); 5356 if (oldDetails.hasProgramDateTime && !newDetails.hasProgramDateTime) { 5357 for (var _i2 = 1; _i2 < fragmentsToCheck.length; _i2++) { 5358 if (fragmentsToCheck[_i2].programDateTime === null) { 5359 assignProgramDateTime(fragmentsToCheck[_i2], fragmentsToCheck[_i2 - 1], programDateTimes); 5360 } 5361 } 5362 } 5363 mapDateRanges(programDateTimes, newDetails); 5364 } 5365 } 5366 var newFragments = newDetails.fragments; 5367 if (ccOffset) { 5368 logger.warn('discontinuity sliding from playlist, take drift into account'); 5369 for (var _i3 = 0; _i3 < newFragments.length; _i3++) { 5370 newFragments[_i3].cc += ccOffset; 5371 } 5372 } 5373 if (newDetails.skippedSegments) { 5374 newDetails.startCC = newDetails.fragments[0].cc; 5375 } 5376 5377 // Merge parts 5378 mapPartIntersection(oldDetails.partList, newDetails.partList, function (oldPart, newPart) { 5379 newPart.elementaryStreams = oldPart.elementaryStreams; 5380 newPart.stats = oldPart.stats; 5381 }); 5382 5383 // if at least one fragment contains PTS info, recompute PTS information for all fragments 5384 if (PTSFrag) { 5385 updateFragPTSDTS(newDetails, PTSFrag, PTSFrag.startPTS, PTSFrag.endPTS, PTSFrag.startDTS, PTSFrag.endDTS); 5386 } else { 5387 // ensure that delta is within oldFragments range 5388 // also adjust sliding in case delta is 0 (we could have old=[50-60] and new=old=[50-61]) 5389 // in that case we also need to adjust start offset of all fragments 5390 adjustSliding(oldDetails, newDetails); 5391 } 5392 if (newFragments.length) { 5393 newDetails.totalduration = newDetails.edge - newFragments[0].start; 5394 } 5395 newDetails.driftStartTime = oldDetails.driftStartTime; 5396 newDetails.driftStart = oldDetails.driftStart; 5397 var advancedDateTime = newDetails.advancedDateTime; 5398 if (newDetails.advanced && advancedDateTime) { 5399 var edge = newDetails.edge; 5400 if (!newDetails.driftStart) { 5401 newDetails.driftStartTime = advancedDateTime; 5402 newDetails.driftStart = edge; 5403 } 5404 newDetails.driftEndTime = advancedDateTime; 5405 newDetails.driftEnd = edge; 5406 } else { 5407 newDetails.driftEndTime = oldDetails.driftEndTime; 5408 newDetails.driftEnd = oldDetails.driftEnd; 5409 newDetails.advancedDateTime = oldDetails.advancedDateTime; 5410 } 5411 } 5412 function mergeDateRanges(oldDateRanges, newDetails) { 5413 var deltaDateRanges = newDetails.dateRanges, 5414 recentlyRemovedDateranges = newDetails.recentlyRemovedDateranges; 5415 var dateRanges = _extends({}, oldDateRanges); 5416 if (recentlyRemovedDateranges) { 5417 recentlyRemovedDateranges.forEach(function (id) { 5418 delete dateRanges[id]; 5419 }); 5420 } 5421 var mergeIds = Object.keys(dateRanges); 5422 var mergeCount = mergeIds.length; 5423 if (mergeCount) { 5424 Object.keys(deltaDateRanges).forEach(function (id) { 5425 var mergedDateRange = dateRanges[id]; 5426 var dateRange = new DateRange(deltaDateRanges[id].attr, mergedDateRange); 5427 if (dateRange.isValid) { 5428 dateRanges[id] = dateRange; 5429 if (!mergedDateRange) { 5430 dateRange.tagOrder += mergeCount; 5431 } 5432 } else { 5433 logger.warn("Ignoring invalid Playlist Delta Update DATERANGE tag: \"" + JSON.stringify(deltaDateRanges[id].attr) + "\""); 5434 } 5435 }); 5436 } 5437 return dateRanges; 5438 } 5439 function mapPartIntersection(oldParts, newParts, intersectionFn) { 5440 if (oldParts && newParts) { 5441 var delta = 0; 5442 for (var i = 0, len = oldParts.length; i <= len; i++) { 5443 var _oldPart = oldParts[i]; 5444 var _newPart = newParts[i + delta]; 5445 if (_oldPart && _newPart && _oldPart.index === _newPart.index && _oldPart.fragment.sn === _newPart.fragment.sn) { 5446 intersectionFn(_oldPart, _newPart); 5447 } else { 5448 delta--; 5449 } 5450 } 5451 } 5452 } 5453 function mapFragmentIntersection(oldDetails, newDetails, intersectionFn) { 5454 var skippedSegments = newDetails.skippedSegments; 5455 var start = Math.max(oldDetails.startSN, newDetails.startSN) - newDetails.startSN; 5456 var end = (oldDetails.fragmentHint ? 1 : 0) + (skippedSegments ? newDetails.endSN : Math.min(oldDetails.endSN, newDetails.endSN)) - newDetails.startSN; 5457 var delta = newDetails.startSN - oldDetails.startSN; 5458 var newFrags = newDetails.fragmentHint ? newDetails.fragments.concat(newDetails.fragmentHint) : newDetails.fragments; 5459 var oldFrags = oldDetails.fragmentHint ? oldDetails.fragments.concat(oldDetails.fragmentHint) : oldDetails.fragments; 5460 for (var i = start; i <= end; i++) { 5461 var _oldFrag = oldFrags[delta + i]; 5462 var _newFrag = newFrags[i]; 5463 if (skippedSegments && !_newFrag && _oldFrag) { 5464 // Fill in skipped segments in delta playlist 5465 _newFrag = newDetails.fragments[i] = _oldFrag; 5466 } 5467 if (_oldFrag && _newFrag) { 5468 intersectionFn(_oldFrag, _newFrag); 5469 } 5470 } 5471 } 5472 function adjustSliding(oldDetails, newDetails) { 5473 var delta = newDetails.startSN + newDetails.skippedSegments - oldDetails.startSN; 5474 var oldFragments = oldDetails.fragments; 5475 if (delta < 0 || delta >= oldFragments.length) { 5476 return; 5477 } 5478 addSliding(newDetails, oldFragments[delta].start); 5479 } 5480 function addSliding(details, start) { 5481 if (start) { 5482 var fragments = details.fragments; 5483 for (var i = details.skippedSegments; i < fragments.length; i++) { 5484 fragments[i].start += start; 5485 } 5486 if (details.fragmentHint) { 5487 details.fragmentHint.start += start; 5488 } 5489 } 5490 } 5491 function computeReloadInterval(newDetails, distanceToLiveEdgeMs) { 5492 if (distanceToLiveEdgeMs === void 0) { 5493 distanceToLiveEdgeMs = Infinity; 5494 } 5495 var reloadInterval = 1000 * newDetails.targetduration; 5496 if (newDetails.updated) { 5497 // Use last segment duration when shorter than target duration and near live edge 5498 var fragments = newDetails.fragments; 5499 var liveEdgeMaxTargetDurations = 4; 5500 if (fragments.length && reloadInterval * liveEdgeMaxTargetDurations > distanceToLiveEdgeMs) { 5501 var lastSegmentDuration = fragments[fragments.length - 1].duration * 1000; 5502 if (lastSegmentDuration < reloadInterval) { 5503 reloadInterval = lastSegmentDuration; 5504 } 5505 } 5506 } else { 5507 // estimate = 'miss half average'; 5508 // follow HLS Spec, If the client reloads a Playlist file and finds that it has not 5509 // changed then it MUST wait for a period of one-half the target 5510 // duration before retrying. 5511 reloadInterval /= 2; 5512 } 5513 return Math.round(reloadInterval); 5514 } 5515 function getFragmentWithSN(details, sn, fragCurrent) { 5516 if (!details) { 5517 return null; 5518 } 5519 var fragment = details.fragments[sn - details.startSN]; 5520 if (fragment) { 5521 return fragment; 5522 } 5523 fragment = details.fragmentHint; 5524 if (fragment && fragment.sn === sn) { 5525 return fragment; 5526 } 5527 if (sn < details.startSN && fragCurrent && fragCurrent.sn === sn) { 5528 return fragCurrent; 5529 } 5530 return null; 5531 } 5532 function getPartWith(details, sn, partIndex) { 5533 if (!details) { 5534 return null; 5535 } 5536 return findPart(details.partList, sn, partIndex); 5537 } 5538 function findPart(partList, sn, partIndex) { 5539 if (partList) { 5540 for (var i = partList.length; i--;) { 5541 var part = partList[i]; 5542 if (part.index === partIndex && part.fragment.sn === sn) { 5543 return part; 5544 } 5545 } 5546 } 5547 return null; 5548 } 5549 function reassignFragmentLevelIndexes(levels) { 5550 levels.forEach(function (level, index) { 5551 var details = level.details; 5552 if (details != null && details.fragments) { 5553 details.fragments.forEach(function (fragment) { 5554 fragment.level = index; 5555 }); 5556 } 5557 }); 5558 } 5559 5560 function isTimeoutError(error) { 5561 switch (error.details) { 5562 case ErrorDetails.FRAG_LOAD_TIMEOUT: 5563 case ErrorDetails.KEY_LOAD_TIMEOUT: 5564 case ErrorDetails.LEVEL_LOAD_TIMEOUT: 5565 case ErrorDetails.MANIFEST_LOAD_TIMEOUT: 5566 return true; 5567 } 5568 return false; 5569 } 5570 function getRetryConfig(loadPolicy, error) { 5571 var isTimeout = isTimeoutError(error); 5572 return loadPolicy.default[(isTimeout ? 'timeout' : 'error') + "Retry"]; 5573 } 5574 function getRetryDelay(retryConfig, retryCount) { 5575 // exponential backoff capped to max retry delay 5576 var backoffFactor = retryConfig.backoff === 'linear' ? 1 : Math.pow(2, retryCount); 5577 return Math.min(backoffFactor * retryConfig.retryDelayMs, retryConfig.maxRetryDelayMs); 5578 } 5579 function getLoaderConfigWithoutReties(loderConfig) {
vendor: 13,909 bytes, lines 5580-5895
5580 return _objectSpread2(_objectSpread2({}, loderConfig), { 5581 errorRetry: null, 5582 timeoutRetry: null 5583 }); 5584 } 5585 function shouldRetry(retryConfig, retryCount, isTimeout, loaderResponse) { 5586 if (!retryConfig) { 5587 return false; 5588 } 5589 var httpStatus = loaderResponse == null ? void 0 : loaderResponse.code; 5590 var retry = retryCount < retryConfig.maxNumRetry && (retryForHttpStatus(httpStatus) || !!isTimeout); 5591 return retryConfig.shouldRetry ? retryConfig.shouldRetry(retryConfig, retryCount, isTimeout, loaderResponse, retry) : retry; 5592 } 5593 function retryForHttpStatus(httpStatus) { 5594 // Do not retry on status 4xx, status 0 (CORS error), or undefined (decrypt/gap/parse error) 5595 return httpStatus === 0 && navigator.onLine === false || !!httpStatus && (httpStatus < 400 || httpStatus > 499); 5596 } 5597 5598 var BinarySearch = { 5599 /** 5600 * Searches for an item in an array which matches a certain condition. 5601 * This requires the condition to only match one item in the array, 5602 * and for the array to be ordered. 5603 * 5604 * @param list The array to search. 5605 * @param comparisonFn 5606 * Called and provided a candidate item as the first argument. 5607 * Should return: 5608 * > -1 if the item should be located at a lower index than the provided item. 5609 * > 1 if the item should be located at a higher index than the provided item. 5610 * > 0 if the item is the item you're looking for. 5611 * 5612 * @returns the object if found, otherwise returns null 5613 */ 5614 search: function search(list, comparisonFn) { 5615 var minIndex = 0; 5616 var maxIndex = list.length - 1; 5617 var currentIndex = null; 5618 var currentElement = null; 5619 while (minIndex <= maxIndex) { 5620 currentIndex = (minIndex + maxIndex) / 2 | 0; 5621 currentElement = list[currentIndex]; 5622 var comparisonResult = comparisonFn(currentElement); 5623 if (comparisonResult > 0) { 5624 minIndex = currentIndex + 1; 5625 } else if (comparisonResult < 0) { 5626 maxIndex = currentIndex - 1; 5627 } else { 5628 return currentElement; 5629 } 5630 } 5631 return null; 5632 } 5633 }; 5634 5635 /** 5636 * Returns first fragment whose endPdt value exceeds the given PDT, or null. 5637 * @param fragments - The array of candidate fragments 5638 * @param PDTValue - The PDT value which must be exceeded 5639 * @param maxFragLookUpTolerance - The amount of time that a fragment's start/end can be within in order to be considered contiguous 5640 */ 5641 function findFragmentByPDT(fragments, PDTValue, maxFragLookUpTolerance) { 5642 if (PDTValue === null || !Array.isArray(fragments) || !fragments.length || !isFiniteNumber(PDTValue)) { 5643 return null; 5644 } 5645 5646 // if less than start 5647 var startPDT = fragments[0].programDateTime; 5648 if (PDTValue < (startPDT || 0)) { 5649 return null; 5650 } 5651 var endPDT = fragments[fragments.length - 1].endProgramDateTime; 5652 if (PDTValue >= (endPDT || 0)) { 5653 return null; 5654 } 5655 maxFragLookUpTolerance = maxFragLookUpTolerance || 0; 5656 for (var seg = 0; seg < fragments.length; ++seg) { 5657 var frag = fragments[seg]; 5658 if (pdtWithinToleranceTest(PDTValue, maxFragLookUpTolerance, frag)) { 5659 return frag; 5660 } 5661 } 5662 return null; 5663 } 5664 5665 /** 5666 * Finds a fragment based on the SN of the previous fragment; or based on the needs of the current buffer. 5667 * This method compensates for small buffer gaps by applying a tolerance to the start of any candidate fragment, thus 5668 * breaking any traps which would cause the same fragment to be continuously selected within a small range. 5669 * @param fragPrevious - The last frag successfully appended 5670 * @param fragments - The array of candidate fragments 5671 * @param bufferEnd - The end of the contiguous buffered range the playhead is currently within 5672 * @param maxFragLookUpTolerance - The amount of time that a fragment's start/end can be within in order to be considered contiguous 5673 * @returns a matching fragment or null 5674 */ 5675 function findFragmentByPTS(fragPrevious, fragments, bufferEnd, maxFragLookUpTolerance, nextFragLookupTolerance) { 5676 if (bufferEnd === void 0) { 5677 bufferEnd = 0; 5678 } 5679 if (maxFragLookUpTolerance === void 0) { 5680 maxFragLookUpTolerance = 0; 5681 } 5682 if (nextFragLookupTolerance === void 0) { 5683 nextFragLookupTolerance = 0.005; 5684 } 5685 var fragNext = null; 5686 if (fragPrevious) { 5687 fragNext = fragments[1 + fragPrevious.sn - fragments[0].sn] || null; 5688 // check for buffer-end rounding error 5689 var bufferEdgeError = fragPrevious.endDTS - bufferEnd; 5690 if (bufferEdgeError > 0 && bufferEdgeError < 0.0000015) { 5691 bufferEnd += 0.0000015; 5692 } 5693 if (fragNext && fragPrevious.level !== fragNext.level && fragNext.end <= fragPrevious.end) { 5694 fragNext = fragments[2 + fragPrevious.sn - fragments[0].sn] || null; 5695 } 5696 } else if (bufferEnd === 0 && fragments[0].start === 0) { 5697 fragNext = fragments[0]; 5698 } 5699 // Prefer the next fragment if it's within tolerance 5700 if (fragNext && ((!fragPrevious || fragPrevious.level === fragNext.level) && fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, fragNext) === 0 || fragmentWithinFastStartSwitch(fragNext, fragPrevious, Math.min(nextFragLookupTolerance, maxFragLookUpTolerance)))) { 5701 return fragNext; 5702 } 5703 // We might be seeking past the tolerance so find the best match 5704 var foundFragment = BinarySearch.search(fragments, fragmentWithinToleranceTest.bind(null, bufferEnd, maxFragLookUpTolerance)); 5705 if (foundFragment && (foundFragment !== fragPrevious || !fragNext)) { 5706 return foundFragment; 5707 } 5708 // If no match was found return the next fragment after fragPrevious, or null 5709 return fragNext; 5710 } 5711 function fragmentWithinFastStartSwitch(fragNext, fragPrevious, nextFragLookupTolerance) { 5712 if (fragPrevious && fragPrevious.start === 0 && fragPrevious.level < fragNext.level && (fragPrevious.endPTS || 0) > 0) { 5713 var firstDuration = fragPrevious.tagList.reduce(function (duration, tag) { 5714 if (tag[0] === 'INF') { 5715 duration += parseFloat(tag[1]); 5716 } 5717 return duration; 5718 }, nextFragLookupTolerance); 5719 return fragNext.start <= firstDuration; 5720 } 5721 return false; 5722 } 5723 5724 /** 5725 * The test function used by the findFragmentBySn's BinarySearch to look for the best match to the current buffer conditions. 5726 * @param candidate - The fragment to test 5727 * @param bufferEnd - The end of the current buffered range the playhead is currently within 5728 * @param maxFragLookUpTolerance - The amount of time that a fragment's start can be within in order to be considered contiguous 5729 * @returns 0 if it matches, 1 if too low, -1 if too high 5730 */ 5731 function fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, candidate) { 5732 if (bufferEnd === void 0) { 5733 bufferEnd = 0; 5734 } 5735 if (maxFragLookUpTolerance === void 0) { 5736 maxFragLookUpTolerance = 0; 5737 } 5738 // eagerly accept an accurate match (no tolerance) 5739 if (candidate.start <= bufferEnd && candidate.start + candidate.duration > bufferEnd) { 5740 return 0; 5741 } 5742 // offset should be within fragment boundary - config.maxFragLookUpTolerance 5743 // this is to cope with situations like 5744 // bufferEnd = 9.991 5745 // frag[Ø] : [0,10] 5746 // frag[1] : [10,20] 5747 // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here 5748 // frag start frag start+duration 5749 // |-----------------------------| 5750 // <---> <---> 5751 // ...--------><-----------------------------><---------.... 5752 // previous frag matching fragment next frag 5753 // return -1 return 0 return 1 5754 // logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`); 5755 // Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments 5756 var candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)); 5757 if (candidate.start + candidate.duration - candidateLookupTolerance <= bufferEnd) { 5758 return 1; 5759 } else if (candidate.start - candidateLookupTolerance > bufferEnd && candidate.start) { 5760 // if maxFragLookUpTolerance will have negative value then don't return -1 for first element 5761 return -1; 5762 } 5763 return 0; 5764 } 5765 5766 /** 5767 * The test function used by the findFragmentByPdt's BinarySearch to look for the best match to the current buffer conditions. 5768 * This function tests the candidate's program date time values, as represented in Unix time 5769 * @param candidate - The fragment to test 5770 * @param pdtBufferEnd - The Unix time representing the end of the current buffered range 5771 * @param maxFragLookUpTolerance - The amount of time that a fragment's start can be within in order to be considered contiguous 5772 * @returns true if contiguous, false otherwise 5773 */ 5774 function pdtWithinToleranceTest(pdtBufferEnd, maxFragLookUpTolerance, candidate) { 5775 var candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)) * 1000; 5776 5777 // endProgramDateTime can be null, default to zero 5778 var endProgramDateTime = candidate.endProgramDateTime || 0; 5779 return endProgramDateTime - candidateLookupTolerance > pdtBufferEnd; 5780 } 5781 function findFragWithCC(fragments, cc) { 5782 return BinarySearch.search(fragments, function (candidate) { 5783 if (candidate.cc < cc) { 5784 return 1; 5785 } else if (candidate.cc > cc) { 5786 return -1; 5787 } else { 5788 return 0; 5789 } 5790 }); 5791 } 5792 5793 var NetworkErrorAction = { 5794 DoNothing: 0, 5795 SendEndCallback: 1, 5796 SendAlternateToPenaltyBox: 2, 5797 RemoveAlternatePermanently: 3, 5798 InsertDiscontinuity: 4, 5799 RetryRequest: 5 5800 }; 5801 var ErrorActionFlags = { 5802 None: 0, 5803 MoveAllAlternatesMatchingHost: 1, 5804 MoveAllAlternatesMatchingHDCP: 2, 5805 SwitchToSDR: 4 5806 }; // Reserved for future use 5807 var ErrorController = /*#__PURE__*/function (_Logger) { 5808 function ErrorController(hls) { 5809 var _this; 5810 _this = _Logger.call(this, 'error-controller', hls.logger) || this; 5811 _this.hls = void 0; 5812 _this.playlistError = 0; 5813 _this.penalizedRenditions = {}; 5814 _this.hls = hls; 5815 _this.registerListeners(); 5816 return _this; 5817 } 5818 _inheritsLoose(ErrorController, _Logger); 5819 var _proto = ErrorController.prototype; 5820 _proto.registerListeners = function registerListeners() { 5821 var hls = this.hls; 5822 hls.on(Events.ERROR, this.onError, this); 5823 hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); 5824 hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); 5825 }; 5826 _proto.unregisterListeners = function unregisterListeners() { 5827 var hls = this.hls; 5828 if (!hls) { 5829 return; 5830 } 5831 hls.off(Events.ERROR, this.onError, this); 5832 hls.off(Events.ERROR, this.onErrorOut, this); 5833 hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); 5834 hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); 5835 }; 5836 _proto.destroy = function destroy() { 5837 this.unregisterListeners(); 5838 // @ts-ignore 5839 this.hls = null; 5840 this.penalizedRenditions = {}; 5841 }; 5842 _proto.startLoad = function startLoad(startPosition) {}; 5843 _proto.stopLoad = function stopLoad() { 5844 this.playlistError = 0; 5845 }; 5846 _proto.getVariantLevelIndex = function getVariantLevelIndex(frag) { 5847 return (frag == null ? void 0 : frag.type) === PlaylistLevelType.MAIN ? frag.level : this.hls.loadLevel; 5848 }; 5849 _proto.onManifestLoading = function onManifestLoading() { 5850 this.playlistError = 0; 5851 this.penalizedRenditions = {}; 5852 }; 5853 _proto.onLevelUpdated = function onLevelUpdated() { 5854 this.playlistError = 0; 5855 }; 5856 _proto.onError = function onError(event, data) { 5857 var _data$frag; 5858 if (data.fatal) { 5859 return; 5860 } 5861 var hls = this.hls; 5862 var context = data.context; 5863 switch (data.details) { 5864 case ErrorDetails.FRAG_LOAD_ERROR: 5865 case ErrorDetails.FRAG_LOAD_TIMEOUT: 5866 case ErrorDetails.KEY_LOAD_ERROR: 5867 case ErrorDetails.KEY_LOAD_TIMEOUT: 5868 data.errorAction = this.getFragRetryOrSwitchAction(data); 5869 return; 5870 case ErrorDetails.FRAG_PARSING_ERROR: 5871 // ignore empty segment errors marked as gap 5872 if ((_data$frag = data.frag) != null && _data$frag.gap) { 5873 data.errorAction = createDoNothingErrorAction(); 5874 return; 5875 } 5876 // falls through 5877 case ErrorDetails.FRAG_GAP: 5878 case ErrorDetails.FRAG_DECRYPT_ERROR: 5879 { 5880 // Switch level if possible, otherwise allow retry count to reach max error retries 5881 data.errorAction = this.getFragRetryOrSwitchAction(data); 5882 data.errorAction.action = NetworkErrorAction.SendAlternateToPenaltyBox; 5883 return; 5884 } 5885 case ErrorDetails.LEVEL_EMPTY_ERROR: 5886 case ErrorDetails.LEVEL_PARSING_ERROR: 5887 { 5888 var _data$context, _data$context$levelDe; 5889 // Only retry when empty and live 5890 var levelIndex = data.parent === PlaylistLevelType.MAIN ? data.level : hls.loadLevel; 5891 if (data.details === ErrorDetails.LEVEL_EMPTY_ERROR && !!((_data$context = data.context) != null && (_data$context$levelDe = _data$context.levelDetails) != null && _data$context$levelDe.live)) { 5892 data.errorAction = this.getPlaylistRetryOrSwitchAction(data, levelIndex); 5893 } else { 5894 // Escalate to fatal if not retrying or switching 5895 data.levelRetry = false;
vendor: 12,675 bytes, lines 5896-6155
5896 data.errorAction = this.getLevelSwitchAction(data, levelIndex); 5897 } 5898 } 5899 return; 5900 case ErrorDetails.LEVEL_LOAD_ERROR: 5901 case ErrorDetails.LEVEL_LOAD_TIMEOUT: 5902 if (typeof (context == null ? void 0 : context.level) === 'number') { 5903 data.errorAction = this.getPlaylistRetryOrSwitchAction(data, context.level); 5904 } 5905 return; 5906 case ErrorDetails.AUDIO_TRACK_LOAD_ERROR: 5907 case ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT: 5908 case ErrorDetails.SUBTITLE_LOAD_ERROR: 5909 case ErrorDetails.SUBTITLE_TRACK_LOAD_TIMEOUT: 5910 if (context) { 5911 var level = hls.levels[hls.loadLevel]; 5912 if (level && (context.type === PlaylistContextType.AUDIO_TRACK && level.hasAudioGroup(context.groupId) || context.type === PlaylistContextType.SUBTITLE_TRACK && level.hasSubtitleGroup(context.groupId))) { 5913 // Perform Pathway switch or Redundant failover if possible for fastest recovery 5914 // otherwise allow playlist retry count to reach max error retries 5915 data.errorAction = this.getPlaylistRetryOrSwitchAction(data, hls.loadLevel); 5916 data.errorAction.action = NetworkErrorAction.SendAlternateToPenaltyBox; 5917 data.errorAction.flags = ErrorActionFlags.MoveAllAlternatesMatchingHost; 5918 return; 5919 } 5920 } 5921 return; 5922 case ErrorDetails.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED: 5923 { 5924 var _level = hls.levels[hls.loadLevel]; 5925 var restrictedHdcpLevel = _level == null ? void 0 : _level.attrs['HDCP-LEVEL']; 5926 if (restrictedHdcpLevel) { 5927 data.errorAction = { 5928 action: NetworkErrorAction.SendAlternateToPenaltyBox, 5929 flags: ErrorActionFlags.MoveAllAlternatesMatchingHDCP, 5930 hdcpLevel: restrictedHdcpLevel 5931 }; 5932 } else { 5933 this.keySystemError(data); 5934 } 5935 } 5936 return; 5937 case ErrorDetails.BUFFER_ADD_CODEC_ERROR: 5938 case ErrorDetails.REMUX_ALLOC_ERROR: 5939 case ErrorDetails.BUFFER_APPEND_ERROR: 5940 // Buffer-controller can set errorAction when append errors can be ignored or resolved locally 5941 if (!data.errorAction) { 5942 var _data$level; 5943 data.errorAction = this.getLevelSwitchAction(data, (_data$level = data.level) != null ? _data$level : hls.loadLevel); 5944 } 5945 return; 5946 case ErrorDetails.INTERNAL_EXCEPTION: 5947 case ErrorDetails.BUFFER_APPENDING_ERROR: 5948 case ErrorDetails.BUFFER_FULL_ERROR: 5949 case ErrorDetails.LEVEL_SWITCH_ERROR: 5950 case ErrorDetails.BUFFER_STALLED_ERROR: 5951 case ErrorDetails.BUFFER_SEEK_OVER_HOLE: 5952 case ErrorDetails.BUFFER_NUDGE_ON_STALL: 5953 data.errorAction = createDoNothingErrorAction(); 5954 return; 5955 } 5956 if (data.type === ErrorTypes.KEY_SYSTEM_ERROR) { 5957 this.keySystemError(data); 5958 } 5959 }; 5960 _proto.keySystemError = function keySystemError(data) { 5961 var levelIndex = this.getVariantLevelIndex(data.frag); 5962 // Do not retry level. Escalate to fatal if switching levels fails. 5963 data.levelRetry = false; 5964 data.errorAction = this.getLevelSwitchAction(data, levelIndex); 5965 }; 5966 _proto.getPlaylistRetryOrSwitchAction = function getPlaylistRetryOrSwitchAction(data, levelIndex) { 5967 var hls = this.hls; 5968 var retryConfig = getRetryConfig(hls.config.playlistLoadPolicy, data); 5969 var retryCount = this.playlistError++; 5970 var retry = shouldRetry(retryConfig, retryCount, isTimeoutError(data), data.response); 5971 if (retry) { 5972 return { 5973 action: NetworkErrorAction.RetryRequest, 5974 flags: ErrorActionFlags.None, 5975 retryConfig: retryConfig, 5976 retryCount: retryCount 5977 }; 5978 } 5979 var errorAction = this.getLevelSwitchAction(data, levelIndex); 5980 if (retryConfig) { 5981 errorAction.retryConfig = retryConfig; 5982 errorAction.retryCount = retryCount; 5983 } 5984 return errorAction; 5985 }; 5986 _proto.getFragRetryOrSwitchAction = function getFragRetryOrSwitchAction(data) { 5987 var hls = this.hls; 5988 // Share fragment error count accross media options (main, audio, subs) 5989 // This allows for level based rendition switching when media option assets fail 5990 var variantLevelIndex = this.getVariantLevelIndex(data.frag); 5991 var level = hls.levels[variantLevelIndex]; 5992 var _hls$config = hls.config, 5993 fragLoadPolicy = _hls$config.fragLoadPolicy, 5994 keyLoadPolicy = _hls$config.keyLoadPolicy; 5995 var retryConfig = getRetryConfig(data.details.startsWith('key') ? keyLoadPolicy : fragLoadPolicy, data); 5996 var fragmentErrors = hls.levels.reduce(function (acc, level) { 5997 return acc + level.fragmentError; 5998 }, 0); 5999 // Switch levels when out of retried or level index out of bounds 6000 if (level) { 6001 if (data.details !== ErrorDetails.FRAG_GAP) { 6002 level.fragmentError++; 6003 } 6004 var retry = shouldRetry(retryConfig, fragmentErrors, isTimeoutError(data), data.response); 6005 if (retry) { 6006 return { 6007 action: NetworkErrorAction.RetryRequest, 6008 flags: ErrorActionFlags.None, 6009 retryConfig: retryConfig, 6010 retryCount: fragmentErrors 6011 }; 6012 } 6013 } 6014 // Reach max retry count, or Missing level reference 6015 // Switch to valid index 6016 var errorAction = this.getLevelSwitchAction(data, variantLevelIndex); 6017 // Add retry details to allow skipping of FRAG_PARSING_ERROR 6018 if (retryConfig) { 6019 errorAction.retryConfig = retryConfig; 6020 errorAction.retryCount = fragmentErrors; 6021 } 6022 return errorAction; 6023 }; 6024 _proto.getLevelSwitchAction = function getLevelSwitchAction(data, levelIndex) { 6025 var hls = this.hls; 6026 if (levelIndex === null || levelIndex === undefined) { 6027 levelIndex = hls.loadLevel; 6028 } 6029 var level = this.hls.levels[levelIndex]; 6030 if (level) { 6031 var _data$frag2, _data$context2; 6032 var errorDetails = data.details; 6033 level.loadError++; 6034 if (errorDetails === ErrorDetails.BUFFER_APPEND_ERROR) { 6035 level.fragmentError++; 6036 } 6037 // Search for next level to retry 6038 var nextLevel = -1; 6039 var levels = hls.levels, 6040 loadLevel = hls.loadLevel, 6041 minAutoLevel = hls.minAutoLevel, 6042 maxAutoLevel = hls.maxAutoLevel; 6043 if (!hls.autoLevelEnabled) { 6044 hls.loadLevel = -1; 6045 } 6046 var fragErrorType = (_data$frag2 = data.frag) == null ? void 0 : _data$frag2.type; 6047 // Find alternate audio codec if available on audio codec error 6048 var isAudioCodecError = fragErrorType === PlaylistLevelType.AUDIO && errorDetails === ErrorDetails.FRAG_PARSING_ERROR || data.sourceBufferName === 'audio' && (errorDetails === ErrorDetails.BUFFER_ADD_CODEC_ERROR || errorDetails === ErrorDetails.BUFFER_APPEND_ERROR); 6049 var findAudioCodecAlternate = isAudioCodecError && levels.some(function (_ref) { 6050 var audioCodec = _ref.audioCodec; 6051 return level.audioCodec !== audioCodec; 6052 }); 6053 // Find alternate video codec if available on video codec error 6054 var isVideoCodecError = data.sourceBufferName === 'video' && (errorDetails === ErrorDetails.BUFFER_ADD_CODEC_ERROR || errorDetails === ErrorDetails.BUFFER_APPEND_ERROR); 6055 var findVideoCodecAlternate = isVideoCodecError && levels.some(function (_ref2) { 6056 var codecSet = _ref2.codecSet, 6057 audioCodec = _ref2.audioCodec; 6058 return level.codecSet !== codecSet && level.audioCodec === audioCodec; 6059 }); 6060 var _ref3 = (_data$context2 = data.context) != null ? _data$context2 : {}, 6061 playlistErrorType = _ref3.type, 6062 playlistErrorGroupId = _ref3.groupId; 6063 var _loop = function _loop() { 6064 var candidate = (i + loadLevel) % levels.length; 6065 if (candidate !== loadLevel && candidate >= minAutoLevel && candidate <= maxAutoLevel && levels[candidate].loadError === 0) { 6066 var _level$audioGroups, _level$subtitleGroups; 6067 var levelCandidate = levels[candidate]; 6068 // Skip level switch if GAP tag is found in next level at same position 6069 if (errorDetails === ErrorDetails.FRAG_GAP && fragErrorType === PlaylistLevelType.MAIN && data.frag) { 6070 var levelDetails = levels[candidate].details; 6071 if (levelDetails) { 6072 var fragCandidate = findFragmentByPTS(data.frag, levelDetails.fragments, data.frag.start); 6073 if (fragCandidate != null && fragCandidate.gap) { 6074 return 0; // continue 6075 } 6076 } 6077 } else if (playlistErrorType === PlaylistContextType.AUDIO_TRACK && levelCandidate.hasAudioGroup(playlistErrorGroupId) || playlistErrorType === PlaylistContextType.SUBTITLE_TRACK && levelCandidate.hasSubtitleGroup(playlistErrorGroupId)) { 6078 // For audio/subs playlist errors find another group ID or fallthrough to redundant fail-over 6079 return 0; // continue 6080 } else if (fragErrorType === PlaylistLevelType.AUDIO && (_level$audioGroups = level.audioGroups) != null && _level$audioGroups.some(function (groupId) { 6081 return levelCandidate.hasAudioGroup(groupId); 6082 }) || fragErrorType === PlaylistLevelType.SUBTITLE && (_level$subtitleGroups = level.subtitleGroups) != null && _level$subtitleGroups.some(function (groupId) { 6083 return levelCandidate.hasSubtitleGroup(groupId); 6084 }) || findAudioCodecAlternate && level.audioCodec === levelCandidate.audioCodec || !findAudioCodecAlternate && level.audioCodec !== levelCandidate.audioCodec || findVideoCodecAlternate && level.codecSet === levelCandidate.codecSet) { 6085 // For video/audio/subs frag errors find another group ID or fallthrough to redundant fail-over 6086 return 0; // continue 6087 } 6088 nextLevel = candidate; 6089 return 1; // break 6090 } 6091 }, 6092 _ret; 6093 for (var i = levels.length; i--;) { 6094 _ret = _loop(); 6095 if (_ret === 0) continue; 6096 if (_ret === 1) break; 6097 } 6098 if (nextLevel > -1 && hls.loadLevel !== nextLevel) { 6099 data.levelRetry = true; 6100 this.playlistError = 0; 6101 return { 6102 action: NetworkErrorAction.SendAlternateToPenaltyBox, 6103 flags: ErrorActionFlags.None, 6104 nextAutoLevel: nextLevel 6105 }; 6106 } 6107 } 6108 // No levels to switch / Manual level selection / Level not found 6109 // Resolve with Pathway switch, Redundant fail-over, or stay on lowest Level 6110 return { 6111 action: NetworkErrorAction.SendAlternateToPenaltyBox, 6112 flags: ErrorActionFlags.MoveAllAlternatesMatchingHost 6113 }; 6114 }; 6115 _proto.onErrorOut = function onErrorOut(event, data) { 6116 var _data$errorAction; 6117 switch ((_data$errorAction = data.errorAction) == null ? void 0 : _data$errorAction.action) { 6118 case NetworkErrorAction.DoNothing: 6119 break; 6120 case NetworkErrorAction.SendAlternateToPenaltyBox: 6121 this.sendAlternateToPenaltyBox(data); 6122 if (!data.errorAction.resolved && data.details !== ErrorDetails.FRAG_GAP) { 6123 data.fatal = true; 6124 } else if (/MediaSource readyState: ended/.test(data.error.message)) { 6125 this.warn("MediaSource ended after \"" + data.sourceBufferName + "\" sourceBuffer append error. Attempting to recover from media error."); 6126 this.hls.recoverMediaError(); 6127 } 6128 break; 6129 } 6130 if (data.fatal) { 6131 this.hls.stopLoad(); 6132 return; 6133 } 6134 }; 6135 _proto.sendAlternateToPenaltyBox = function sendAlternateToPenaltyBox(data) { 6136 var hls = this.hls; 6137 var errorAction = data.errorAction; 6138 if (!errorAction) { 6139 return; 6140 } 6141 var flags = errorAction.flags, 6142 hdcpLevel = errorAction.hdcpLevel, 6143 nextAutoLevel = errorAction.nextAutoLevel; 6144 switch (flags) { 6145 case ErrorActionFlags.None: 6146 this.switchLevel(data, nextAutoLevel); 6147 break; 6148 case ErrorActionFlags.MoveAllAlternatesMatchingHDCP: 6149 if (hdcpLevel) { 6150 hls.maxHdcpLevel = HdcpLevels[HdcpLevels.indexOf(hdcpLevel) - 1]; 6151 errorAction.resolved = true; 6152 } 6153 this.warn("Restricting playback to HDCP-LEVEL of \"" + hls.maxHdcpLevel + "\" or lower"); 6154 break; 6155 }
vendor: 4,393 bytes, lines 6156-6268
6156 // If not resolved by previous actions try to switch to next level 6157 if (!errorAction.resolved) { 6158 this.switchLevel(data, nextAutoLevel); 6159 } 6160 }; 6161 _proto.switchLevel = function switchLevel(data, levelIndex) { 6162 if (levelIndex !== undefined && data.errorAction) { 6163 this.warn("switching to level " + levelIndex + " after " + data.details); 6164 this.hls.nextAutoLevel = levelIndex; 6165 data.errorAction.resolved = true; 6166 // Stream controller is responsible for this but won't switch on false start 6167 this.hls.nextLoadLevel = this.hls.nextAutoLevel; 6168 } 6169 }; 6170 return ErrorController; 6171 }(Logger); 6172 function createDoNothingErrorAction(resolved) { 6173 var errorAction = { 6174 action: NetworkErrorAction.DoNothing, 6175 flags: ErrorActionFlags.None 6176 }; 6177 return errorAction; 6178 } 6179 6180 var BasePlaylistController = /*#__PURE__*/function (_Logger) { 6181 function BasePlaylistController(hls, logPrefix) { 6182 var _this; 6183 _this = _Logger.call(this, logPrefix, hls.logger) || this; 6184 _this.hls = void 0; 6185 _this.timer = -1; 6186 _this.requestScheduled = -1; 6187 _this.canLoad = false; 6188 _this.hls = hls; 6189 return _this; 6190 } 6191 _inheritsLoose(BasePlaylistController, _Logger); 6192 var _proto = BasePlaylistController.prototype; 6193 _proto.destroy = function destroy() { 6194 this.clearTimer(); 6195 // @ts-ignore 6196 this.hls = this.log = this.warn = null; 6197 }; 6198 _proto.clearTimer = function clearTimer() { 6199 if (this.timer !== -1) { 6200 self.clearTimeout(this.timer); 6201 this.timer = -1; 6202 } 6203 }; 6204 _proto.startLoad = function startLoad() { 6205 this.canLoad = true; 6206 this.requestScheduled = -1; 6207 this.loadPlaylist(); 6208 }; 6209 _proto.stopLoad = function stopLoad() { 6210 this.canLoad = false; 6211 this.clearTimer(); 6212 }; 6213 _proto.switchParams = function switchParams(playlistUri, previous, current) { 6214 var renditionReports = previous == null ? void 0 : previous.renditionReports; 6215 if (renditionReports) { 6216 var foundIndex = -1; 6217 for (var i = 0; i < renditionReports.length; i++) { 6218 var attr = renditionReports[i]; 6219 var uri = void 0; 6220 try { 6221 uri = new self.URL(attr.URI, previous.url).href; 6222 } catch (error) { 6223 this.warn("Could not construct new URL for Rendition Report: " + error); 6224 uri = attr.URI || ''; 6225 } 6226 // Use exact match. Otherwise, the last partial match, if any, will be used 6227 // (Playlist URI includes a query string that the Rendition Report does not) 6228 if (uri === playlistUri) { 6229 foundIndex = i; 6230 break; 6231 } else if (uri === playlistUri.substring(0, uri.length)) { 6232 foundIndex = i; 6233 } 6234 } 6235 if (foundIndex !== -1) { 6236 var _attr = renditionReports[foundIndex]; 6237 var msn = parseInt(_attr['LAST-MSN']) || (previous == null ? void 0 : previous.lastPartSn); 6238 var part = parseInt(_attr['LAST-PART']) || (previous == null ? void 0 : previous.lastPartIndex); 6239 if (this.hls.config.lowLatencyMode) { 6240 var currentGoal = Math.min(previous.age - previous.partTarget, previous.targetduration); 6241 if (part >= 0 && currentGoal > previous.partTarget) { 6242 part += 1; 6243 } 6244 } 6245 var skip = current && getSkipValue(current); 6246 return new HlsUrlParameters(msn, part >= 0 ? part : undefined, skip); 6247 } 6248 } 6249 }; 6250 _proto.loadPlaylist = function loadPlaylist(hlsUrlParameters) { 6251 if (this.requestScheduled === -1) { 6252 this.requestScheduled = self.performance.now(); 6253 } 6254 // Loading is handled by the subclasses 6255 }; 6256 _proto.shouldLoadPlaylist = function shouldLoadPlaylist(playlist) { 6257 return this.canLoad && !!playlist && !!playlist.url && (!playlist.details || playlist.details.live); 6258 }; 6259 _proto.shouldReloadPlaylist = function shouldReloadPlaylist(playlist) { 6260 return this.timer === -1 && this.requestScheduled === -1 && this.shouldLoadPlaylist(playlist); 6261 }; 6262 _proto.playlistLoaded = function playlistLoaded(index, data, previousDetails) { 6263 var _this2 = this; 6264 var details = data.details, 6265 stats = data.stats; 6266 6267 // Set last updated date-time 6268 var now = self.performance.now();
6269 var elapsed = stats.loading.first ? Math.max(0, now - stats.loading.first) : 0; 6270 details.advancedDateTime = Date.now() - elapsed; 6271 6272 // if current playlist is a live playlist, arm a timer to reload it 6273 if (details.live || previousDetails != null && previousDetails.live) { 6274 details.reloaded(previousDetails); 6275 if (previousDetails) { 6276 this.log("live playlist " + index + " " + (details.advanced ? 'REFRESHED ' + details.lastPartSn + '-' + details.lastPartIndex : details.updated ? 'UPDATED' : 'MISSED')); 6277 } 6278 // Merge live playlists to adjust fragment starts and fill in delta playlist skipped segments 6279 if (previousDetails && details.fragments.length > 0) { 6280 mergeDetails(previousDetails, details); 6281 } 6282 if (!this.canLoad || !details.live) { 6283 return; 6284 } 6285 var deliveryDirectives; 6286 var msn = undefined; 6287 var part = undefined; 6288 if (details.canBlockReload && details.endSN && details.advanced) { 6289 // Load level with LL-HLS delivery directives 6290 var lowLatencyMode = this.hls.config.lowLatencyMode; 6291 var lastPartSn = details.lastPartSn; 6292 var endSn = details.endSN; 6293 var lastPartIndex = details.lastPartIndex; 6294 var hasParts = lastPartIndex !== -1; 6295 var lastPart = lastPartSn === endSn; 6296 // When low latency mode is disabled, we'll skip part requests once the last part index is found 6297 var nextSnStartIndex = lowLatencyMode ? 0 : lastPartIndex; 6298 if (hasParts) { 6299 msn = lastPart ? endSn + 1 : lastPartSn; 6300 part = lastPart ? nextSnStartIndex : lastPartIndex + 1; 6301 } else { 6302 msn = endSn + 1; 6303 } 6304 // Low-Latency CDN Tune-in: "age" header and time since load indicates we're behind by more than one part 6305 // Update directives to obtain the Playlist that has the estimated additional duration of media 6306 var lastAdvanced = details.age; 6307 var cdnAge = lastAdvanced + details.ageHeader; 6308 var currentGoal = Math.min(cdnAge - details.partTarget, details.targetduration * 1.5); 6309 if (currentGoal > 0) { 6310 if (cdnAge > details.targetduration * 3) { 6311 // Omit segment and part directives when the last response was more than 3 target durations ago, 6312 this.log("Playlist last advanced " + lastAdvanced.toFixed(2) + "s ago. Omitting segment and part directives."); 6313 msn = undefined; 6314 part = undefined; 6315 } else if (previousDetails != null && previousDetails.tuneInGoal && cdnAge - details.partTarget > previousDetails.tuneInGoal) { 6316 // If we attempted to get the next or latest playlist update, but currentGoal increased, 6317 // then we either can't catchup, or the "age" header cannot be trusted. 6318 this.warn("CDN Tune-in goal increased from: " + previousDetails.tuneInGoal + " to: " + currentGoal + " with playlist age: " + details.age); 6319 currentGoal = 0; 6320 } else { 6321 var segments = Math.floor(currentGoal / details.targetduration); 6322 msn += segments; 6323 if (part !== undefined) { 6324 var parts = Math.round(currentGoal % details.targetduration / details.partTarget); 6325 part += parts; 6326 } 6327 this.log("CDN Tune-in age: " + details.ageHeader + "s last advanced " + lastAdvanced.toFixed(2) + "s goal: " + currentGoal + " skip sn " + segments + " to part " + part); 6328 } 6329 details.tuneInGoal = currentGoal; 6330 } 6331 deliveryDirectives = this.getDeliveryDirectives(details, data.deliveryDirectives, msn, part); 6332 if (lowLatencyMode || !lastPart) { 6333 this.loadPlaylist(deliveryDirectives); 6334 return; 6335 } 6336 } else if (details.canBlockReload || details.canSkipUntil) { 6337 deliveryDirectives = this.getDeliveryDirectives(details, data.deliveryDirectives, msn, part); 6338 } 6339 var bufferInfo = this.hls.mainForwardBufferInfo; 6340 var position = bufferInfo ? bufferInfo.end - bufferInfo.len : 0; 6341 var distanceToLiveEdgeMs = (details.edge - position) * 1000; 6342 var reloadInterval = computeReloadInterval(details, distanceToLiveEdgeMs); 6343 if (details.updated && now > this.requestScheduled + reloadInterval) { 6344 this.requestScheduled = stats.loading.start; 6345 } 6346 if (msn !== undefined && details.canBlockReload) { 6347 this.requestScheduled = stats.loading.first + reloadInterval - (details.partTarget * 1000 || 1000); 6348 } else if (this.requestScheduled === -1 || this.requestScheduled + reloadInterval < now) { 6349 this.requestScheduled = now; 6350 } else if (this.requestScheduled - now <= 0) { 6351 this.requestScheduled += reloadInterval; 6352 }
vendor: 9,358 bytes, lines 6353-6590
6353 var estimatedTimeUntilUpdate = this.requestScheduled - now; 6354 estimatedTimeUntilUpdate = Math.max(0, estimatedTimeUntilUpdate); 6355 this.log("reload live playlist " + index + " in " + Math.round(estimatedTimeUntilUpdate) + " ms"); 6356 // this.log( 6357 // `live reload ${details.updated ? 'REFRESHED' : 'MISSED'} 6358 // reload in ${estimatedTimeUntilUpdate / 1000} 6359 // round trip ${(stats.loading.end - stats.loading.start) / 1000} 6360 // diff ${ 6361 // (reloadInterval - 6362 // (estimatedTimeUntilUpdate + 6363 // stats.loading.end - 6364 // stats.loading.start)) / 6365 // 1000 6366 // } 6367 // reload interval ${reloadInterval / 1000} 6368 // target duration ${details.targetduration} 6369 // distance to edge ${distanceToLiveEdgeMs / 1000}` 6370 // ); 6371 6372 this.timer = self.setTimeout(function () { 6373 return _this2.loadPlaylist(deliveryDirectives); 6374 }, estimatedTimeUntilUpdate); 6375 } else { 6376 this.clearTimer(); 6377 } 6378 }; 6379 _proto.getDeliveryDirectives = function getDeliveryDirectives(details, previousDeliveryDirectives, msn, part) { 6380 var skip = getSkipValue(details); 6381 if (previousDeliveryDirectives != null && previousDeliveryDirectives.skip && details.deltaUpdateFailed) { 6382 msn = previousDeliveryDirectives.msn; 6383 part = previousDeliveryDirectives.part; 6384 skip = HlsSkip.No; 6385 } 6386 return new HlsUrlParameters(msn, part, skip); 6387 }; 6388 _proto.checkRetry = function checkRetry(errorEvent) { 6389 var _this3 = this; 6390 var errorDetails = errorEvent.details; 6391 var isTimeout = isTimeoutError(errorEvent); 6392 var errorAction = errorEvent.errorAction; 6393 var _ref = errorAction || {}, 6394 action = _ref.action, 6395 _ref$retryCount = _ref.retryCount, 6396 retryCount = _ref$retryCount === void 0 ? 0 : _ref$retryCount, 6397 retryConfig = _ref.retryConfig; 6398 var retry = !!errorAction && !!retryConfig && (action === NetworkErrorAction.RetryRequest || !errorAction.resolved && action === NetworkErrorAction.SendAlternateToPenaltyBox); 6399 if (retry) { 6400 var _errorEvent$context; 6401 this.requestScheduled = -1; 6402 if (retryCount >= retryConfig.maxNumRetry) { 6403 return false; 6404 } 6405 if (isTimeout && (_errorEvent$context = errorEvent.context) != null && _errorEvent$context.deliveryDirectives) { 6406 // The LL-HLS request already timed out so retry immediately 6407 this.warn("Retrying playlist loading " + (retryCount + 1) + "/" + retryConfig.maxNumRetry + " after \"" + errorDetails + "\" without delivery-directives"); 6408 this.loadPlaylist(); 6409 } else { 6410 var delay = getRetryDelay(retryConfig, retryCount); 6411 // Schedule level/track reload 6412 this.timer = self.setTimeout(function () { 6413 return _this3.loadPlaylist(); 6414 }, delay); 6415 this.warn("Retrying playlist loading " + (retryCount + 1) + "/" + retryConfig.maxNumRetry + " after \"" + errorDetails + "\" in " + delay + "ms"); 6416 } 6417 // `levelRetry = true` used to inform other controllers that a retry is happening 6418 errorEvent.levelRetry = true; 6419 errorAction.resolved = true; 6420 } 6421 return retry; 6422 }; 6423 return BasePlaylistController; 6424 }(Logger); 6425 6426 /* 6427 * compute an Exponential Weighted moving average 6428 * - https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average 6429 * - heavily inspired from shaka-player 6430 */ 6431 var EWMA = /*#__PURE__*/function () { 6432 // About half of the estimated value will be from the last |halfLife| samples by weight. 6433 function EWMA(halfLife, estimate, weight) { 6434 if (estimate === void 0) { 6435 estimate = 0; 6436 } 6437 if (weight === void 0) { 6438 weight = 0; 6439 } 6440 this.halfLife = void 0; 6441 this.alpha_ = void 0; 6442 this.estimate_ = void 0; 6443 this.totalWeight_ = void 0; 6444 this.halfLife = halfLife; 6445 // Larger values of alpha expire historical data more slowly. 6446 this.alpha_ = halfLife ? Math.exp(Math.log(0.5) / halfLife) : 0; 6447 this.estimate_ = estimate; 6448 this.totalWeight_ = weight; 6449 } 6450 var _proto = EWMA.prototype; 6451 _proto.sample = function sample(weight, value) { 6452 var adjAlpha = Math.pow(this.alpha_, weight); 6453 this.estimate_ = value * (1 - adjAlpha) + adjAlpha * this.estimate_; 6454 this.totalWeight_ += weight; 6455 }; 6456 _proto.getTotalWeight = function getTotalWeight() { 6457 return this.totalWeight_; 6458 }; 6459 _proto.getEstimate = function getEstimate() { 6460 if (this.alpha_) { 6461 var zeroFactor = 1 - Math.pow(this.alpha_, this.totalWeight_); 6462 if (zeroFactor) { 6463 return this.estimate_ / zeroFactor; 6464 } 6465 } 6466 return this.estimate_; 6467 }; 6468 return EWMA; 6469 }(); 6470 6471 /* 6472 * EWMA Bandwidth Estimator 6473 * - heavily inspired from shaka-player 6474 * Tracks bandwidth samples and estimates available bandwidth. 6475 * Based on the minimum of two exponentially-weighted moving averages with 6476 * different half-lives. 6477 */ 6478 6479 var EwmaBandWidthEstimator = /*#__PURE__*/function () { 6480 function EwmaBandWidthEstimator(slow, fast, defaultEstimate, defaultTTFB) { 6481 if (defaultTTFB === void 0) { 6482 defaultTTFB = 100; 6483 } 6484 this.defaultEstimate_ = void 0; 6485 this.minWeight_ = void 0; 6486 this.minDelayMs_ = void 0; 6487 this.slow_ = void 0; 6488 this.fast_ = void 0; 6489 this.defaultTTFB_ = void 0; 6490 this.ttfb_ = void 0; 6491 this.defaultEstimate_ = defaultEstimate; 6492 this.minWeight_ = 0.001; 6493 this.minDelayMs_ = 50; 6494 this.slow_ = new EWMA(slow); 6495 this.fast_ = new EWMA(fast); 6496 this.defaultTTFB_ = defaultTTFB; 6497 this.ttfb_ = new EWMA(slow); 6498 } 6499 var _proto = EwmaBandWidthEstimator.prototype; 6500 _proto.update = function update(slow, fast) { 6501 var slow_ = this.slow_, 6502 fast_ = this.fast_, 6503 ttfb_ = this.ttfb_; 6504 if (slow_.halfLife !== slow) { 6505 this.slow_ = new EWMA(slow, slow_.getEstimate(), slow_.getTotalWeight()); 6506 } 6507 if (fast_.halfLife !== fast) { 6508 this.fast_ = new EWMA(fast, fast_.getEstimate(), fast_.getTotalWeight()); 6509 } 6510 if (ttfb_.halfLife !== slow) { 6511 this.ttfb_ = new EWMA(slow, ttfb_.getEstimate(), ttfb_.getTotalWeight()); 6512 } 6513 }; 6514 _proto.sample = function sample(durationMs, numBytes) { 6515 durationMs = Math.max(durationMs, this.minDelayMs_); 6516 var numBits = 8 * numBytes; 6517 // weight is duration in seconds 6518 var durationS = durationMs / 1000; 6519 // value is bandwidth in bits/s 6520 var bandwidthInBps = numBits / durationS; 6521 this.fast_.sample(durationS, bandwidthInBps); 6522 this.slow_.sample(durationS, bandwidthInBps); 6523 }; 6524 _proto.sampleTTFB = function sampleTTFB(ttfb) { 6525 // weight is frequency curve applied to TTFB in seconds 6526 // (longer times have less weight with expected input under 1 second) 6527 var seconds = ttfb / 1000; 6528 var weight = Math.sqrt(2) * Math.exp(-Math.pow(seconds, 2) / 2); 6529 this.ttfb_.sample(weight, Math.max(ttfb, 5)); 6530 }; 6531 _proto.canEstimate = function canEstimate() { 6532 return this.fast_.getTotalWeight() >= this.minWeight_; 6533 }; 6534 _proto.getEstimate = function getEstimate() { 6535 if (this.canEstimate()) { 6536 // console.log('slow estimate:'+ Math.round(this.slow_.getEstimate())); 6537 // console.log('fast estimate:'+ Math.round(this.fast_.getEstimate())); 6538 // Take the minimum of these two estimates. This should have the effect of 6539 // adapting down quickly, but up more slowly. 6540 return Math.min(this.fast_.getEstimate(), this.slow_.getEstimate()); 6541 } else { 6542 return this.defaultEstimate_; 6543 } 6544 }; 6545 _proto.getEstimateTTFB = function getEstimateTTFB() { 6546 if (this.ttfb_.getTotalWeight() >= this.minWeight_) { 6547 return this.ttfb_.getEstimate(); 6548 } else { 6549 return this.defaultTTFB_; 6550 } 6551 }; 6552 _proto.destroy = function destroy() {}; 6553 return EwmaBandWidthEstimator; 6554 }(); 6555 6556 /** 6557 * @returns Whether we can detect and validate HDR capability within the window context 6558 */ 6559 function isHdrSupported() { 6560 if (typeof matchMedia === 'function') { 6561 var mediaQueryList = matchMedia('(dynamic-range: high)'); 6562 var badQuery = matchMedia('bad query'); 6563 if (mediaQueryList.media !== badQuery.media) { 6564 return mediaQueryList.matches === true; 6565 } 6566 } 6567 return false; 6568 } 6569 6570 /** 6571 * Sanitizes inputs to return the active video selection options for HDR/SDR. 6572 * When both inputs are null: 6573 * 6574 * `{ preferHDR: false, allowedVideoRanges: [] }` 6575 * 6576 * When `currentVideoRange` non-null, maintain the active range: 6577 * 6578 * `{ preferHDR: currentVideoRange !== 'SDR', allowedVideoRanges: [currentVideoRange] }` 6579 * 6580 * When VideoSelectionOption non-null: 6581 * 6582 * - Allow all video ranges if `allowedVideoRanges` unspecified. 6583 * - If `preferHDR` is non-null use the value to filter `allowedVideoRanges`. 6584 * - Else check window for HDR support and set `preferHDR` to the result. 6585 * 6586 * @param currentVideoRange 6587 * @param videoPreference 6588 */ 6589 function getVideoSelectionOptions(currentVideoRange, videoPreference) { 6590 var preferHDR = false;
vendor: 14,621 bytes, lines 6591-6906
6591 var allowedVideoRanges = []; 6592 if (currentVideoRange) { 6593 preferHDR = currentVideoRange !== 'SDR'; 6594 allowedVideoRanges = [currentVideoRange]; 6595 } 6596 if (videoPreference) { 6597 allowedVideoRanges = videoPreference.allowedVideoRanges || VideoRangeValues.slice(0); 6598 var allowAutoPreferHDR = allowedVideoRanges.join('') !== 'SDR' && !videoPreference.videoCodec; 6599 preferHDR = videoPreference.preferHDR !== undefined ? videoPreference.preferHDR : allowAutoPreferHDR && isHdrSupported(); 6600 if (!preferHDR) { 6601 allowedVideoRanges = ['SDR']; 6602 } 6603 } 6604 return { 6605 preferHDR: preferHDR, 6606 allowedVideoRanges: allowedVideoRanges 6607 }; 6608 } 6609 6610 function getStartCodecTier(codecTiers, currentVideoRange, currentBw, audioPreference, videoPreference) { 6611 var codecSets = Object.keys(codecTiers); 6612 var channelsPreference = audioPreference == null ? void 0 : audioPreference.channels; 6613 var audioCodecPreference = audioPreference == null ? void 0 : audioPreference.audioCodec; 6614 var videoCodecPreference = videoPreference == null ? void 0 : videoPreference.videoCodec; 6615 var preferStereo = channelsPreference && parseInt(channelsPreference) === 2; 6616 // Use first level set to determine stereo, and minimum resolution and framerate 6617 var hasStereo = false; 6618 var hasCurrentVideoRange = false; 6619 var minHeight = Infinity; 6620 var minFramerate = Infinity; 6621 var minBitrate = Infinity; 6622 var minIndex = Infinity; 6623 var selectedScore = 0; 6624 var videoRanges = []; 6625 var _getVideoSelectionOpt = getVideoSelectionOptions(currentVideoRange, videoPreference), 6626 preferHDR = _getVideoSelectionOpt.preferHDR, 6627 allowedVideoRanges = _getVideoSelectionOpt.allowedVideoRanges; 6628 var _loop = function _loop() { 6629 var tier = codecTiers[codecSets[i]]; 6630 hasStereo || (hasStereo = tier.channels[2] > 0); 6631 minHeight = Math.min(minHeight, tier.minHeight); 6632 minFramerate = Math.min(minFramerate, tier.minFramerate); 6633 minBitrate = Math.min(minBitrate, tier.minBitrate); 6634 var matchingVideoRanges = allowedVideoRanges.filter(function (range) { 6635 return tier.videoRanges[range] > 0; 6636 }); 6637 if (matchingVideoRanges.length > 0) { 6638 hasCurrentVideoRange = true; 6639 } 6640 }; 6641 for (var i = codecSets.length; i--;) { 6642 _loop(); 6643 } 6644 minHeight = isFiniteNumber(minHeight) ? minHeight : 0; 6645 minFramerate = isFiniteNumber(minFramerate) ? minFramerate : 0; 6646 var maxHeight = Math.max(1080, minHeight); 6647 var maxFramerate = Math.max(30, minFramerate); 6648 minBitrate = isFiniteNumber(minBitrate) ? minBitrate : currentBw; 6649 currentBw = Math.max(minBitrate, currentBw); 6650 // If there are no variants with matching preference, set currentVideoRange to undefined 6651 if (!hasCurrentVideoRange) { 6652 currentVideoRange = undefined; 6653 } 6654 var hasMultipleSets = codecSets.length > 1; 6655 var codecSet = codecSets.reduce(function (selected, candidate) { 6656 // Remove candiates which do not meet bitrate, default audio, stereo or channels preference, 1080p or lower, 30fps or lower, or SDR/HDR selection if present 6657 var candidateTier = codecTiers[candidate]; 6658 if (candidate === selected) { 6659 return selected; 6660 } 6661 videoRanges = hasCurrentVideoRange ? allowedVideoRanges.filter(function (range) { 6662 return candidateTier.videoRanges[range] > 0; 6663 }) : []; 6664 if (hasMultipleSets) { 6665 if (candidateTier.minBitrate > currentBw) { 6666 logStartCodecCandidateIgnored(candidate, "min bitrate of " + candidateTier.minBitrate + " > current estimate of " + currentBw); 6667 return selected; 6668 } 6669 if (!candidateTier.hasDefaultAudio) { 6670 logStartCodecCandidateIgnored(candidate, "no renditions with default or auto-select sound found"); 6671 return selected; 6672 } 6673 if (audioCodecPreference && candidate.indexOf(audioCodecPreference.substring(0, 4)) % 5 !== 0) { 6674 logStartCodecCandidateIgnored(candidate, "audio codec preference \"" + audioCodecPreference + "\" not found"); 6675 return selected; 6676 } 6677 if (channelsPreference && !preferStereo) { 6678 if (!candidateTier.channels[channelsPreference]) { 6679 logStartCodecCandidateIgnored(candidate, "no renditions with " + channelsPreference + " channel sound found (channels options: " + Object.keys(candidateTier.channels) + ")"); 6680 return selected; 6681 } 6682 } else if ((!audioCodecPreference || preferStereo) && hasStereo && candidateTier.channels['2'] === 0) { 6683 logStartCodecCandidateIgnored(candidate, "no renditions with stereo sound found"); 6684 return selected; 6685 } 6686 if (candidateTier.minHeight > maxHeight) { 6687 logStartCodecCandidateIgnored(candidate, "min resolution of " + candidateTier.minHeight + " > maximum of " + maxHeight); 6688 return selected; 6689 } 6690 if (candidateTier.minFramerate > maxFramerate) { 6691 logStartCodecCandidateIgnored(candidate, "min framerate of " + candidateTier.minFramerate + " > maximum of " + maxFramerate); 6692 return selected; 6693 } 6694 if (!videoRanges.some(function (range) { 6695 return candidateTier.videoRanges[range] > 0; 6696 })) { 6697 logStartCodecCandidateIgnored(candidate, "no variants with VIDEO-RANGE of " + JSON.stringify(videoRanges) + " found"); 6698 return selected; 6699 } 6700 if (videoCodecPreference && candidate.indexOf(videoCodecPreference.substring(0, 4)) % 5 !== 0) { 6701 logStartCodecCandidateIgnored(candidate, "video codec preference \"" + videoCodecPreference + "\" not found"); 6702 return selected; 6703 } 6704 if (candidateTier.maxScore < selectedScore) { 6705 logStartCodecCandidateIgnored(candidate, "max score of " + candidateTier.maxScore + " < selected max of " + selectedScore); 6706 return selected; 6707 } 6708 } 6709 // Remove candiates with less preferred codecs or more errors 6710 if (selected && (codecsSetSelectionPreferenceValue(candidate) >= codecsSetSelectionPreferenceValue(selected) || candidateTier.fragmentError > codecTiers[selected].fragmentError)) { 6711 return selected; 6712 } 6713 minIndex = candidateTier.minIndex; 6714 selectedScore = candidateTier.maxScore; 6715 return candidate; 6716 }, undefined); 6717 return { 6718 codecSet: codecSet, 6719 videoRanges: videoRanges, 6720 preferHDR: preferHDR, 6721 minFramerate: minFramerate, 6722 minBitrate: minBitrate, 6723 minIndex: minIndex 6724 }; 6725 } 6726 function logStartCodecCandidateIgnored(codeSet, reason) { 6727 logger.log("[abr] start candidates with \"" + codeSet + "\" ignored because " + reason); 6728 } 6729 function getAudioTracksByGroup(allAudioTracks) { 6730 return allAudioTracks.reduce(function (audioTracksByGroup, track) { 6731 var trackGroup = audioTracksByGroup.groups[track.groupId]; 6732 if (!trackGroup) { 6733 trackGroup = audioTracksByGroup.groups[track.groupId] = { 6734 tracks: [], 6735 channels: { 6736 2: 0 6737 }, 6738 hasDefault: false, 6739 hasAutoSelect: false 6740 }; 6741 } 6742 trackGroup.tracks.push(track); 6743 var channelsKey = track.channels || '2'; 6744 trackGroup.channels[channelsKey] = (trackGroup.channels[channelsKey] || 0) + 1; 6745 trackGroup.hasDefault = trackGroup.hasDefault || track.default; 6746 trackGroup.hasAutoSelect = trackGroup.hasAutoSelect || track.autoselect; 6747 if (trackGroup.hasDefault) { 6748 audioTracksByGroup.hasDefaultAudio = true; 6749 } 6750 if (trackGroup.hasAutoSelect) { 6751 audioTracksByGroup.hasAutoSelectAudio = true; 6752 } 6753 return audioTracksByGroup; 6754 }, { 6755 hasDefaultAudio: false, 6756 hasAutoSelectAudio: false, 6757 groups: {} 6758 }); 6759 } 6760 function getCodecTiers(levels, audioTracksByGroup, minAutoLevel, maxAutoLevel) { 6761 return levels.slice(minAutoLevel, maxAutoLevel + 1).reduce(function (tiers, level, index) { 6762 if (!level.codecSet) { 6763 return tiers; 6764 } 6765 var audioGroups = level.audioGroups; 6766 var tier = tiers[level.codecSet]; 6767 if (!tier) { 6768 tiers[level.codecSet] = tier = { 6769 minBitrate: Infinity, 6770 minHeight: Infinity, 6771 minFramerate: Infinity, 6772 minIndex: index, 6773 maxScore: 0, 6774 videoRanges: { 6775 SDR: 0 6776 }, 6777 channels: { 6778 '2': 0 6779 }, 6780 hasDefaultAudio: !audioGroups, 6781 fragmentError: 0 6782 }; 6783 } 6784 tier.minBitrate = Math.min(tier.minBitrate, level.bitrate); 6785 var lesserWidthOrHeight = Math.min(level.height, level.width); 6786 tier.minHeight = Math.min(tier.minHeight, lesserWidthOrHeight); 6787 tier.minFramerate = Math.min(tier.minFramerate, level.frameRate); 6788 tier.minIndex = Math.min(tier.minIndex, index); 6789 tier.maxScore = Math.max(tier.maxScore, level.score); 6790 tier.fragmentError += level.fragmentError; 6791 tier.videoRanges[level.videoRange] = (tier.videoRanges[level.videoRange] || 0) + 1; 6792 return tiers; 6793 }, {}); 6794 } 6795 6796 var AbrController = /*#__PURE__*/function (_Logger) { 6797 function AbrController(_hls) { 6798 var _this; 6799 _this = _Logger.call(this, 'abr', _hls.logger) || this; 6800 _this.hls = void 0; 6801 _this.lastLevelLoadSec = 0; 6802 _this.lastLoadedFragLevel = -1; 6803 _this.firstSelection = -1; 6804 _this._nextAutoLevel = -1; 6805 _this.nextAutoLevelKey = ''; 6806 _this.audioTracksByGroup = null; 6807 _this.codecTiers = null; 6808 _this.timer = -1; 6809 _this.fragCurrent = null; 6810 _this.partCurrent = null; 6811 _this.bitrateTestDelay = 0; 6812 _this.bwEstimator = void 0; 6813 /* 6814 This method monitors the download rate of the current fragment, and will downswitch if that fragment will not load 6815 quickly enough to prevent underbuffering 6816 */ 6817 _this._abandonRulesCheck = function () { 6818 var _this2 = _this, 6819 frag = _this2.fragCurrent, 6820 part = _this2.partCurrent, 6821 hls = _this2.hls; 6822 var autoLevelEnabled = hls.autoLevelEnabled, 6823 media = hls.media; 6824 if (!frag || !media) { 6825 return; 6826 } 6827 var now = performance.now(); 6828 var stats = part ? part.stats : frag.stats; 6829 var duration = part ? part.duration : frag.duration; 6830 var timeLoading = now - stats.loading.start; 6831 var minAutoLevel = hls.minAutoLevel; 6832 // If frag loading is aborted, complete, or from lowest level, stop timer and return 6833 if (stats.aborted || stats.loaded && stats.loaded === stats.total || frag.level <= minAutoLevel) { 6834 _this.clearTimer(); 6835 // reset forced auto level value so that next level will be selected 6836 _this._nextAutoLevel = -1; 6837 return; 6838 } 6839 6840 // This check only runs if we're in ABR mode and actually playing 6841 if (!autoLevelEnabled || media.paused || !media.playbackRate || !media.readyState) { 6842 return; 6843 } 6844 var bufferInfo = hls.mainForwardBufferInfo; 6845 if (bufferInfo === null) { 6846 return; 6847 } 6848 var ttfbEstimate = _this.bwEstimator.getEstimateTTFB(); 6849 var playbackRate = Math.abs(media.playbackRate); 6850 // To maintain stable adaptive playback, only begin monitoring frag loading after half or more of its playback duration has passed 6851 if (timeLoading <= Math.max(ttfbEstimate, 1000 * (duration / (playbackRate * 2)))) { 6852 return; 6853 } 6854 6855 // bufferStarvationDelay is an estimate of the amount time (in seconds) it will take to exhaust the buffer 6856 var bufferStarvationDelay = bufferInfo.len / playbackRate; 6857 var ttfb = stats.loading.first ? stats.loading.first - stats.loading.start : -1; 6858 var loadedFirstByte = stats.loaded && ttfb > -1; 6859 var bwEstimate = _this.getBwEstimate(); 6860 var levels = hls.levels; 6861 var level = levels[frag.level]; 6862 var expectedLen = stats.total || Math.max(stats.loaded, Math.round(duration * level.averageBitrate / 8)); 6863 var timeStreaming = loadedFirstByte ? timeLoading - ttfb : timeLoading; 6864 if (timeStreaming < 1 && loadedFirstByte) { 6865 timeStreaming = Math.min(timeLoading, stats.loaded * 8 / bwEstimate); 6866 } 6867 var loadRate = loadedFirstByte ? stats.loaded * 1000 / timeStreaming : 0; 6868 // fragLoadDelay is an estimate of the time (in seconds) it will take to buffer the remainder of the fragment 6869 var fragLoadedDelay = loadRate ? (expectedLen - stats.loaded) / loadRate : expectedLen * 8 / bwEstimate + ttfbEstimate / 1000; 6870 // Only downswitch if the time to finish loading the current fragment is greater than the amount of buffer left 6871 if (fragLoadedDelay <= bufferStarvationDelay) { 6872 return; 6873 } 6874 var bwe = loadRate ? loadRate * 8 : bwEstimate; 6875 var fragLevelNextLoadedDelay = Number.POSITIVE_INFINITY; 6876 var nextLoadLevel; 6877 // Iterate through lower level and try to find the largest one that avoids rebuffering 6878 for (nextLoadLevel = frag.level - 1; nextLoadLevel > minAutoLevel; nextLoadLevel--) { 6879 // compute time to load next fragment at lower level 6880 // 8 = bits per byte (bps/Bps) 6881 var levelNextBitrate = levels[nextLoadLevel].maxBitrate; 6882 fragLevelNextLoadedDelay = _this.getTimeToLoadFrag(ttfbEstimate / 1000, bwe, duration * levelNextBitrate, !levels[nextLoadLevel].details); 6883 if (fragLevelNextLoadedDelay < bufferStarvationDelay) { 6884 break; 6885 } 6886 } 6887 // Only emergency switch down if it takes less time to load a new fragment at lowest level instead of continuing 6888 // to load the current one 6889 if (fragLevelNextLoadedDelay >= fragLoadedDelay) { 6890 return; 6891 } 6892 6893 // if estimated load time of new segment is completely unreasonable, ignore and do not emergency switch down 6894 if (fragLevelNextLoadedDelay > duration * 10) { 6895 return; 6896 } 6897 hls.nextLoadLevel = hls.nextAutoLevel = nextLoadLevel; 6898 if (loadedFirstByte) { 6899 // If there has been loading progress, sample bandwidth using loading time offset by minimum TTFB time 6900 _this.bwEstimator.sample(timeLoading - Math.min(ttfbEstimate, ttfb), stats.loaded); 6901 } else { 6902 // If there has been no loading progress, sample TTFB 6903 _this.bwEstimator.sampleTTFB(timeLoading); 6904 } 6905 var nextLoadLevelBitrate = levels[nextLoadLevel].maxBitrate; 6906 if (_this.getBwEstimate() * _this.hls.config.abrBandWidthUpFactor >
vendor: 15,769 bytes, lines 6906-7237
6906 nextLoadLevelBitrate) { 6907 _this.resetEstimator(nextLoadLevelBitrate); 6908 } 6909 _this.clearTimer(); 6910 _this.warn("Fragment " + frag.sn + (part ? ' part ' + part.index : '') + " of level " + frag.level + " is loading too slowly;\n Time to underbuffer: " + bufferStarvationDelay.toFixed(3) + " s\n Estimated load time for current fragment: " + fragLoadedDelay.toFixed(3) + " s\n Estimated load time for down switch fragment: " + fragLevelNextLoadedDelay.toFixed(3) + " s\n TTFB estimate: " + (ttfb | 0) + " ms\n Current BW estimate: " + (isFiniteNumber(bwEstimate) ? bwEstimate | 0 : 'Unknown') + " bps\n New BW estimate: " + (_this.getBwEstimate() | 0) + " bps\n Switching to level " + nextLoadLevel + " @ " + (nextLoadLevelBitrate | 0) + " bps"); 6911 hls.trigger(Events.FRAG_LOAD_EMERGENCY_ABORTED, { 6912 frag: frag, 6913 part: part, 6914 stats: stats 6915 }); 6916 }; 6917 _this.hls = _hls; 6918 _this.bwEstimator = _this.initEstimator(); 6919 _this.registerListeners(); 6920 return _this; 6921 } 6922 _inheritsLoose(AbrController, _Logger); 6923 var _proto = AbrController.prototype; 6924 _proto.resetEstimator = function resetEstimator(abrEwmaDefaultEstimate) { 6925 if (abrEwmaDefaultEstimate) { 6926 this.log("setting initial bwe to " + abrEwmaDefaultEstimate); 6927 this.hls.config.abrEwmaDefaultEstimate = abrEwmaDefaultEstimate; 6928 } 6929 this.firstSelection = -1; 6930 this.bwEstimator = this.initEstimator(); 6931 }; 6932 _proto.initEstimator = function initEstimator() { 6933 var config = this.hls.config; 6934 return new EwmaBandWidthEstimator(config.abrEwmaSlowVoD, config.abrEwmaFastVoD, config.abrEwmaDefaultEstimate); 6935 }; 6936 _proto.registerListeners = function registerListeners() { 6937 var hls = this.hls; 6938 hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); 6939 hls.on(Events.FRAG_LOADING, this.onFragLoading, this); 6940 hls.on(Events.FRAG_LOADED, this.onFragLoaded, this); 6941 hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); 6942 hls.on(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); 6943 hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this); 6944 hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); 6945 hls.on(Events.MAX_AUTO_LEVEL_UPDATED, this.onMaxAutoLevelUpdated, this); 6946 hls.on(Events.ERROR, this.onError, this); 6947 }; 6948 _proto.unregisterListeners = function unregisterListeners() { 6949 var hls = this.hls; 6950 if (!hls) { 6951 return; 6952 } 6953 hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); 6954 hls.off(Events.FRAG_LOADING, this.onFragLoading, this); 6955 hls.off(Events.FRAG_LOADED, this.onFragLoaded, this); 6956 hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); 6957 hls.off(Events.LEVEL_SWITCHING, this.onLevelSwitching, this); 6958 hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this); 6959 hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); 6960 hls.off(Events.MAX_AUTO_LEVEL_UPDATED, this.onMaxAutoLevelUpdated, this); 6961 hls.off(Events.ERROR, this.onError, this); 6962 }; 6963 _proto.destroy = function destroy() { 6964 this.unregisterListeners(); 6965 this.clearTimer(); 6966 // @ts-ignore 6967 this.hls = this._abandonRulesCheck = null; 6968 this.fragCurrent = this.partCurrent = null; 6969 }; 6970 _proto.onManifestLoading = function onManifestLoading(event, data) { 6971 this.lastLoadedFragLevel = -1; 6972 this.firstSelection = -1; 6973 this.lastLevelLoadSec = 0; 6974 this.fragCurrent = this.partCurrent = null; 6975 this.onLevelsUpdated(); 6976 this.clearTimer(); 6977 }; 6978 _proto.onLevelsUpdated = function onLevelsUpdated() { 6979 if (this.lastLoadedFragLevel > -1 && this.fragCurrent) { 6980 this.lastLoadedFragLevel = this.fragCurrent.level; 6981 } 6982 this._nextAutoLevel = -1; 6983 this.onMaxAutoLevelUpdated(); 6984 this.codecTiers = null; 6985 this.audioTracksByGroup = null; 6986 }; 6987 _proto.onMaxAutoLevelUpdated = function onMaxAutoLevelUpdated() { 6988 this.firstSelection = -1; 6989 this.nextAutoLevelKey = ''; 6990 }; 6991 _proto.onFragLoading = function onFragLoading(event, data) { 6992 var frag = data.frag; 6993 if (this.ignoreFragment(frag)) { 6994 return; 6995 } 6996 if (!frag.bitrateTest) { 6997 var _data$part; 6998 this.fragCurrent = frag; 6999 this.partCurrent = (_data$part = data.part) != null ? _data$part : null; 7000 } 7001 this.clearTimer(); 7002 this.timer = self.setInterval(this._abandonRulesCheck, 100); 7003 }; 7004 _proto.onLevelSwitching = function onLevelSwitching(event, data) { 7005 this.clearTimer(); 7006 }; 7007 _proto.onError = function onError(event, data) { 7008 if (data.fatal) { 7009 return; 7010 } 7011 switch (data.details) { 7012 case ErrorDetails.BUFFER_ADD_CODEC_ERROR: 7013 case ErrorDetails.BUFFER_APPEND_ERROR: 7014 // Reset last loaded level so that a new selection can be made after calling recoverMediaError 7015 this.lastLoadedFragLevel = -1; 7016 this.firstSelection = -1; 7017 break; 7018 case ErrorDetails.FRAG_LOAD_TIMEOUT: 7019 { 7020 var frag = data.frag; 7021 var fragCurrent = this.fragCurrent, 7022 part = this.partCurrent; 7023 if (frag && fragCurrent && frag.sn === fragCurrent.sn && frag.level === fragCurrent.level) { 7024 var now = performance.now(); 7025 var stats = part ? part.stats : frag.stats; 7026 var timeLoading = now - stats.loading.start; 7027 var ttfb = stats.loading.first ? stats.loading.first - stats.loading.start : -1; 7028 var loadedFirstByte = stats.loaded && ttfb > -1; 7029 if (loadedFirstByte) { 7030 var ttfbEstimate = this.bwEstimator.getEstimateTTFB(); 7031 this.bwEstimator.sample(timeLoading - Math.min(ttfbEstimate, ttfb), stats.loaded); 7032 } else { 7033 this.bwEstimator.sampleTTFB(timeLoading); 7034 } 7035 } 7036 break; 7037 } 7038 } 7039 }; 7040 _proto.getTimeToLoadFrag = function getTimeToLoadFrag(timeToFirstByteSec, bandwidth, fragSizeBits, isSwitch) { 7041 var fragLoadSec = timeToFirstByteSec + fragSizeBits / bandwidth; 7042 var playlistLoadSec = isSwitch ? this.lastLevelLoadSec : 0; 7043 return fragLoadSec + playlistLoadSec; 7044 }; 7045 _proto.onLevelLoaded = function onLevelLoaded(event, data) { 7046 var config = this.hls.config; 7047 var loading = data.stats.loading; 7048 var timeLoadingMs = loading.end - loading.start; 7049 if (isFiniteNumber(timeLoadingMs)) { 7050 this.lastLevelLoadSec = timeLoadingMs / 1000; 7051 } 7052 if (data.details.live) { 7053 this.bwEstimator.update(config.abrEwmaSlowLive, config.abrEwmaFastLive); 7054 } else { 7055 this.bwEstimator.update(config.abrEwmaSlowVoD, config.abrEwmaFastVoD); 7056 } 7057 }; 7058 _proto.onFragLoaded = function onFragLoaded(event, _ref) { 7059 var frag = _ref.frag, 7060 part = _ref.part; 7061 var stats = part ? part.stats : frag.stats; 7062 if (frag.type === PlaylistLevelType.MAIN) { 7063 this.bwEstimator.sampleTTFB(stats.loading.first - stats.loading.start); 7064 } 7065 if (this.ignoreFragment(frag)) { 7066 return; 7067 } 7068 // stop monitoring bw once frag loaded 7069 this.clearTimer(); 7070 // reset forced auto level value so that next level will be selected 7071 if (frag.level === this._nextAutoLevel) { 7072 this._nextAutoLevel = -1; 7073 } 7074 this.firstSelection = -1; 7075 7076 // compute level average bitrate 7077 if (this.hls.config.abrMaxWithRealBitrate) { 7078 var duration = part ? part.duration : frag.duration; 7079 var level = this.hls.levels[frag.level]; 7080 var loadedBytes = (level.loaded ? level.loaded.bytes : 0) + stats.loaded; 7081 var loadedDuration = (level.loaded ? level.loaded.duration : 0) + duration; 7082 level.loaded = { 7083 bytes: loadedBytes, 7084 duration: loadedDuration 7085 }; 7086 level.realBitrate = Math.round(8 * loadedBytes / loadedDuration); 7087 } 7088 if (frag.bitrateTest) { 7089 var fragBufferedData = { 7090 stats: stats, 7091 frag: frag, 7092 part: part, 7093 id: frag.type 7094 }; 7095 this.onFragBuffered(Events.FRAG_BUFFERED, fragBufferedData); 7096 frag.bitrateTest = false; 7097 } else { 7098 // store level id after successful fragment load for playback 7099 this.lastLoadedFragLevel = frag.level; 7100 } 7101 }; 7102 _proto.onFragBuffered = function onFragBuffered(event, data) { 7103 var frag = data.frag, 7104 part = data.part; 7105 var stats = part != null && part.stats.loaded ? part.stats : frag.stats; 7106 if (stats.aborted) { 7107 return; 7108 } 7109 if (this.ignoreFragment(frag)) { 7110 return; 7111 } 7112 // Use the difference between parsing and request instead of buffering and request to compute fragLoadingProcessing; 7113 // rationale is that buffer appending only happens once media is attached. This can happen when config.startFragPrefetch 7114 // is used. If we used buffering in that case, our BW estimate sample will be very large. 7115 var processingMs = stats.parsing.end - stats.loading.start - Math.min(stats.loading.first - stats.loading.start, this.bwEstimator.getEstimateTTFB()); 7116 this.bwEstimator.sample(processingMs, stats.loaded); 7117 stats.bwEstimate = this.getBwEstimate(); 7118 if (frag.bitrateTest) { 7119 this.bitrateTestDelay = processingMs / 1000; 7120 } else { 7121 this.bitrateTestDelay = 0; 7122 } 7123 }; 7124 _proto.ignoreFragment = function ignoreFragment(frag) { 7125 // Only count non-alt-audio frags which were actually buffered in our BW calculations 7126 return frag.type !== PlaylistLevelType.MAIN || frag.sn === 'initSegment'; 7127 }; 7128 _proto.clearTimer = function clearTimer() { 7129 if (this.timer > -1) { 7130 self.clearInterval(this.timer); 7131 this.timer = -1; 7132 } 7133 }; 7134 _proto.getAutoLevelKey = function getAutoLevelKey() { 7135 return this.getBwEstimate() + "_" + this.getStarvationDelay().toFixed(2); 7136 }; 7137 _proto.getNextABRAutoLevel = function getNextABRAutoLevel() { 7138 var fragCurrent = this.fragCurrent, 7139 partCurrent = this.partCurrent, 7140 hls = this.hls; 7141 if (hls.levels.length <= 1) { 7142 return hls.loadLevel; 7143 } 7144 var maxAutoLevel = hls.maxAutoLevel, 7145 config = hls.config, 7146 minAutoLevel = hls.minAutoLevel; 7147 var currentFragDuration = partCurrent ? partCurrent.duration : fragCurrent ? fragCurrent.duration : 0; 7148 var avgbw = this.getBwEstimate(); 7149 // bufferStarvationDelay is the wall-clock time left until the playback buffer is exhausted. 7150 var bufferStarvationDelay = this.getStarvationDelay(); 7151 var bwFactor = config.abrBandWidthFactor; 7152 var bwUpFactor = config.abrBandWidthUpFactor; 7153 7154 // First, look to see if we can find a level matching with our avg bandwidth AND that could also guarantee no rebuffering at all 7155 if (bufferStarvationDelay) { 7156 var _bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, 0, bwFactor, bwUpFactor); 7157 if (_bestLevel >= 0) { 7158 return _bestLevel; 7159 } 7160 } 7161 // not possible to get rid of rebuffering... try to find level that will guarantee less than maxStarvationDelay of rebuffering 7162 var maxStarvationDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxStarvationDelay) : config.maxStarvationDelay; 7163 if (!bufferStarvationDelay) { 7164 // in case buffer is empty, let's check if previous fragment was loaded to perform a bitrate test 7165 var bitrateTestDelay = this.bitrateTestDelay; 7166 if (bitrateTestDelay) { 7167 // if it is the case, then we need to adjust our max starvation delay using maxLoadingDelay config value 7168 // max video loading delay used in automatic start level selection : 7169 // in that mode ABR controller will ensure that video loading time (ie the time to fetch the first fragment at lowest quality level + 7170 // the time to fetch the fragment at the appropriate quality level is less than ```maxLoadingDelay``` ) 7171 // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration 7172 var maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay; 7173 maxStarvationDelay = maxLoadingDelay - bitrateTestDelay; 7174 this.info("bitrate test took " + Math.round(1000 * bitrateTestDelay) + "ms, set first fragment max fetchDuration to " + Math.round(1000 * maxStarvationDelay) + " ms"); 7175 // don't use conservative factor on bitrate test 7176 bwFactor = bwUpFactor = 1; 7177 } 7178 } 7179 var bestLevel = this.findBestLevel(avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, maxStarvationDelay, bwFactor, bwUpFactor); 7180 this.info((bufferStarvationDelay ? 'rebuffering expected' : 'buffer is empty') + ", optimal quality level " + bestLevel); 7181 if (bestLevel > -1) { 7182 return bestLevel; 7183 } 7184 // If no matching level found, see if min auto level would be a better option 7185 var minLevel = hls.levels[minAutoLevel]; 7186 var autoLevel = hls.levels[hls.loadLevel]; 7187 if ((minLevel == null ? void 0 : minLevel.bitrate) < (autoLevel == null ? void 0 : autoLevel.bitrate)) { 7188 return minAutoLevel; 7189 } 7190 // or if bitrate is not lower, continue to use loadLevel 7191 return hls.loadLevel; 7192 }; 7193 _proto.getStarvationDelay = function getStarvationDelay() { 7194 var hls = this.hls; 7195 var media = hls.media; 7196 if (!media) { 7197 return Infinity; 7198 } 7199 // playbackRate is the absolute value of the playback rate; if media.playbackRate is 0, we use 1 to load as 7200 // if we're playing back at the normal rate. 7201 var playbackRate = media && media.playbackRate !== 0 ? Math.abs(media.playbackRate) : 1.0; 7202 var bufferInfo = hls.mainForwardBufferInfo; 7203 return (bufferInfo ? bufferInfo.len : 0) / playbackRate; 7204 }; 7205 _proto.getBwEstimate = function getBwEstimate() { 7206 return this.bwEstimator.canEstimate() ? this.bwEstimator.getEstimate() : this.hls.config.abrEwmaDefaultEstimate; 7207 }; 7208 _proto.findBestLevel = function findBestLevel(currentBw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, maxStarvationDelay, bwFactor, bwUpFactor) { 7209 var _level$details, 7210 _this3 = this; 7211 var maxFetchDuration = bufferStarvationDelay + maxStarvationDelay; 7212 var lastLoadedFragLevel = this.lastLoadedFragLevel; 7213 var selectionBaseLevel = lastLoadedFragLevel === -1 ? this.hls.firstLevel : lastLoadedFragLevel; 7214 var fragCurrent = this.fragCurrent, 7215 partCurrent = this.partCurrent; 7216 var _this$hls = this.hls, 7217 levels = _this$hls.levels, 7218 allAudioTracks = _this$hls.allAudioTracks, 7219 loadLevel = _this$hls.loadLevel, 7220 config = _this$hls.config; 7221 if (levels.length === 1) { 7222 return 0; 7223 } 7224 var level = levels[selectionBaseLevel]; 7225 var live = !!(level != null && (_level$details = level.details) != null && _level$details.live); 7226 var firstSelection = loadLevel === -1 || lastLoadedFragLevel === -1; 7227 var currentCodecSet; 7228 var currentVideoRange = 'SDR'; 7229 var currentFrameRate = (level == null ? void 0 : level.frameRate) || 0; 7230 var audioPreference = config.audioPreference, 7231 videoPreference = config.videoPreference; 7232 var audioTracksByGroup = this.audioTracksByGroup || (this.audioTracksByGroup = getAudioTracksByGroup(allAudioTracks)); 7233 var minStartIndex = -1; 7234 if (firstSelection) { 7235 if (this.firstSelection !== -1) { 7236 return this.firstSelection; 7237 }
7238 var codecTiers = this.codecTiers || (this.codecTiers = getCodecTiers(levels, audioTracksByGroup, minAutoLevel, maxAutoLevel)); 7239 var startTier = getStartCodecTier(codecTiers, currentVideoRange, currentBw, audioPreference, videoPreference); 7240 var codecSet = startTier.codecSet, 7241 videoRanges = startTier.videoRanges, 7242 minFramerate = startTier.minFramerate, 7243 minBitrate = startTier.minBitrate, 7244 minIndex = startTier.minIndex, 7245 preferHDR = startTier.preferHDR; 7246 minStartIndex = minIndex; 7247 currentCodecSet = codecSet; 7248 currentVideoRange = preferHDR ? videoRanges[videoRanges.length - 1] : videoRanges[0]; 7249 currentFrameRate = minFramerate; 7250 currentBw = Math.max(currentBw, minBitrate); 7251 this.log("picked start tier " + JSON.stringify(startTier)); 7252 } else { 7253 currentCodecSet = level == null ? void 0 : level.codecSet; 7254 currentVideoRange = level == null ? void 0 : level.videoRange; 7255 } 7256 var currentFragDuration = partCurrent ? partCurrent.duration : fragCurrent ? fragCurrent.duration : 0; 7257 var ttfbEstimateSec = this.bwEstimator.getEstimateTTFB() / 1000; 7258 var levelsSkipped = []; 7259 var _loop = function _loop() { 7260 var _levelInfo$supportedR; 7261 var levelInfo = levels[i]; 7262 var upSwitch = i > selectionBaseLevel; 7263 if (!levelInfo) { 7264 return 0; // continue 7265 } 7266 7267 // skip candidates which change codec-family or video-range, 7268 // and which decrease or increase frame-rate for up and down-switch respectfully 7269 if (currentCodecSet && levelInfo.codecSet !== currentCodecSet || currentVideoRange && levelInfo.videoRange !== currentVideoRange || upSwitch && currentFrameRate > levelInfo.frameRate || !upSwitch && currentFrameRate > 0 && currentFrameRate < levelInfo.frameRate || levelInfo.supportedResult && !((_levelInfo$supportedR = levelInfo.supportedResult.decodingInfoResults) != null && _levelInfo$supportedR[0].smooth)) { 7270 if (!firstSelection || i !== minStartIndex) { 7271 levelsSkipped.push(i); 7272 return 0; // continue 7273 } 7274 } 7275 var levelDetails = levelInfo.details; 7276 var avgDuration = (partCurrent ? levelDetails == null ? void 0 : levelDetails.partTarget : levelDetails == null ? void 0 : levelDetails.averagetargetduration) || currentFragDuration; 7277 var adjustedbw; 7278 // follow algorithm captured from stagefright : 7279 // https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp 7280 // Pick the highest bandwidth stream below or equal to estimated bandwidth. 7281 // consider only 80% of the available bandwidth, but if we are switching up, 7282 // be even more conservative (70%) to avoid overestimating and immediately 7283 // switching back. 7284 if (!upSwitch) { 7285 adjustedbw = bwFactor * currentBw; 7286 } else { 7287 adjustedbw = bwUpFactor * currentBw; 7288 } 7289 7290 // Use average bitrate when starvation delay (buffer length) is gt or eq two segment durations and rebuffering is not expected (maxStarvationDelay > 0) 7291 var bitrate = currentFragDuration && bufferStarvationDelay >= currentFragDuration * 2 && maxStarvationDelay === 0 ? levels[i].averageBitrate : levels[i].maxBitrate; 7292 var fetchDuration = _this3.getTimeToLoadFrag(ttfbEstimateSec, adjustedbw, bitrate * avgDuration, levelDetails === undefined); 7293 var canSwitchWithinTolerance =
vendor: 12,074 bytes, lines 7294-7593
7294 // if adjusted bw is greater than level bitrate AND 7295 adjustedbw >= bitrate && ( 7296 // no level change, or new level has no error history 7297 i === lastLoadedFragLevel || levelInfo.loadError === 0 && levelInfo.fragmentError === 0) && ( 7298 // fragment fetchDuration unknown OR live stream OR fragment fetchDuration less than max allowed fetch duration, then this level matches 7299 // we don't account for max Fetch Duration for live streams, this is to avoid switching down when near the edge of live sliding window ... 7300 // special case to support startLevel = -1 (bitrateTest) on live streams : in that case we should not exit loop so that findBestLevel will return -1 7301 fetchDuration <= ttfbEstimateSec || !isFiniteNumber(fetchDuration) || live && !_this3.bitrateTestDelay || fetchDuration < maxFetchDuration); 7302 if (canSwitchWithinTolerance) { 7303 var forcedAutoLevel = _this3.forcedAutoLevel; 7304 if (i !== loadLevel && (forcedAutoLevel === -1 || forcedAutoLevel !== loadLevel)) { 7305 if (levelsSkipped.length) { 7306 _this3.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 \"" + level.codecs + "\" " + currentVideoRange); 7307 } 7308 _this3.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); 7309 } 7310 if (firstSelection) { 7311 _this3.firstSelection = i; 7312 } 7313 // as we are looping from highest to lowest, this will return the best achievable quality level 7314 return { 7315 v: i 7316 }; 7317 } 7318 }, 7319 _ret; 7320 for (var i = maxAutoLevel; i >= minAutoLevel; i--) { 7321 _ret = _loop(); 7322 if (_ret === 0) continue; 7323 if (_ret) return _ret.v; 7324 } 7325 // not enough time budget even with quality level 0 ... rebuffering might happen 7326 return -1; 7327 }; 7328 return _createClass(AbrController, [{ 7329 key: "firstAutoLevel", 7330 get: function get() { 7331 var _this$hls2 = this.hls, 7332 maxAutoLevel = _this$hls2.maxAutoLevel, 7333 minAutoLevel = _this$hls2.minAutoLevel; 7334 var bwEstimate = this.getBwEstimate(); 7335 var maxStartDelay = this.hls.config.maxStarvationDelay; 7336 var abrAutoLevel = this.findBestLevel(bwEstimate, minAutoLevel, maxAutoLevel, 0, maxStartDelay, 1, 1); 7337 if (abrAutoLevel > -1) { 7338 return abrAutoLevel; 7339 } 7340 var firstLevel = this.hls.firstLevel; 7341 var clamped = Math.min(Math.max(firstLevel, minAutoLevel), maxAutoLevel); 7342 this.warn("Could not find best starting auto level. Defaulting to first in playlist " + firstLevel + " clamped to " + clamped); 7343 return clamped; 7344 } 7345 }, { 7346 key: "forcedAutoLevel", 7347 get: function get() { 7348 if (this.nextAutoLevelKey) { 7349 return -1; 7350 } 7351 return this._nextAutoLevel; 7352 } 7353 7354 // return next auto level 7355 }, { 7356 key: "nextAutoLevel", 7357 get: function get() { 7358 var forcedAutoLevel = this.forcedAutoLevel; 7359 var bwEstimator = this.bwEstimator; 7360 var useEstimate = bwEstimator.canEstimate(); 7361 var loadedFirstFrag = this.lastLoadedFragLevel > -1; 7362 // in case next auto level has been forced, and bw not available or not reliable, return forced value 7363 if (forcedAutoLevel !== -1 && (!useEstimate || !loadedFirstFrag || this.nextAutoLevelKey === this.getAutoLevelKey())) { 7364 return forcedAutoLevel; 7365 } 7366 7367 // compute next level using ABR logic 7368 var nextABRAutoLevel = useEstimate && loadedFirstFrag ? this.getNextABRAutoLevel() : this.firstAutoLevel; 7369 7370 // use forced auto level while it hasn't errored more than ABR selection 7371 if (forcedAutoLevel !== -1) { 7372 var levels = this.hls.levels; 7373 if (levels.length > Math.max(forcedAutoLevel, nextABRAutoLevel) && levels[forcedAutoLevel].loadError <= levels[nextABRAutoLevel].loadError) { 7374 return forcedAutoLevel; 7375 } 7376 } 7377 7378 // save result until state has changed 7379 this._nextAutoLevel = nextABRAutoLevel; 7380 this.nextAutoLevelKey = this.getAutoLevelKey(); 7381 return nextABRAutoLevel; 7382 }, 7383 set: function set(nextLevel) { 7384 var _this$hls3 = this.hls, 7385 maxAutoLevel = _this$hls3.maxAutoLevel, 7386 minAutoLevel = _this$hls3.minAutoLevel; 7387 var value = Math.min(Math.max(nextLevel, minAutoLevel), maxAutoLevel); 7388 if (this._nextAutoLevel !== value) { 7389 this.nextAutoLevelKey = ''; 7390 this._nextAutoLevel = value; 7391 } 7392 } 7393 }]); 7394 }(Logger); 7395 7396 /** 7397 * Provides methods dealing with buffer length retrieval for example. 7398 * 7399 * In general, a helper around HTML5 MediaElement TimeRanges gathered from `buffered` property. 7400 * 7401 * Also @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered 7402 */ 7403 7404 var noopBuffered = { 7405 length: 0, 7406 start: function start() { 7407 return 0; 7408 }, 7409 end: function end() { 7410 return 0; 7411 } 7412 }; 7413 var BufferHelper = /*#__PURE__*/function () { 7414 function BufferHelper() {} 7415 /** 7416 * Return true if `media`'s buffered include `position` 7417 */ 7418 BufferHelper.isBuffered = function isBuffered(media, position) { 7419 if (media) { 7420 var buffered = BufferHelper.getBuffered(media); 7421 for (var i = buffered.length; i--;) { 7422 if (position >= buffered.start(i) && position <= buffered.end(i)) { 7423 return true; 7424 } 7425 } 7426 } 7427 return false; 7428 }; 7429 BufferHelper.bufferInfo = function bufferInfo(media, pos, maxHoleDuration) { 7430 if (media) { 7431 var vbuffered = BufferHelper.getBuffered(media); 7432 if (vbuffered.length) { 7433 var buffered = []; 7434 for (var i = 0; i < vbuffered.length; i++) { 7435 buffered.push({ 7436 start: vbuffered.start(i), 7437 end: vbuffered.end(i) 7438 }); 7439 } 7440 return BufferHelper.bufferedInfo(buffered, pos, maxHoleDuration); 7441 } 7442 } 7443 return { 7444 len: 0, 7445 start: pos, 7446 end: pos, 7447 nextStart: undefined 7448 }; 7449 }; 7450 BufferHelper.bufferedInfo = function bufferedInfo(buffered, pos, maxHoleDuration) { 7451 pos = Math.max(0, pos); 7452 // sort on buffer.start/smaller end (IE does not always return sorted buffered range) 7453 buffered.sort(function (a, b) { 7454 return a.start - b.start || b.end - a.end; 7455 }); 7456 var buffered2 = []; 7457 if (maxHoleDuration) { 7458 // there might be some small holes between buffer time range 7459 // consider that holes smaller than maxHoleDuration are irrelevant and build another 7460 // buffer time range representations that discards those holes 7461 for (var i = 0; i < buffered.length; i++) { 7462 var buf2len = buffered2.length; 7463 if (buf2len) { 7464 var buf2end = buffered2[buf2len - 1].end; 7465 // if small hole (value between 0 or maxHoleDuration ) or overlapping (negative) 7466 if (buffered[i].start - buf2end < maxHoleDuration) { 7467 // merge overlapping time ranges 7468 // update lastRange.end only if smaller than item.end 7469 // e.g. [ 1, 15] with [ 2,8] => [ 1,15] (no need to modify lastRange.end) 7470 // whereas [ 1, 8] with [ 2,15] => [ 1,15] ( lastRange should switch from [1,8] to [1,15]) 7471 if (buffered[i].end > buf2end) { 7472 buffered2[buf2len - 1].end = buffered[i].end; 7473 } 7474 } else { 7475 // big hole 7476 buffered2.push(buffered[i]); 7477 } 7478 } else { 7479 // first value 7480 buffered2.push(buffered[i]); 7481 } 7482 } 7483 } else { 7484 buffered2 = buffered; 7485 } 7486 var bufferLen = 0; 7487 7488 // bufferStartNext can possibly be undefined based on the conditional logic below 7489 var bufferStartNext; 7490 7491 // bufferStart and bufferEnd are buffer boundaries around current video position 7492 var bufferStart = pos; 7493 var bufferEnd = pos; 7494 for (var _i = 0; _i < buffered2.length; _i++) { 7495 var start = buffered2[_i].start; 7496 var end = buffered2[_i].end; 7497 // logger.log('buf start/end:' + buffered.start(i) + '/' + buffered.end(i)); 7498 if (pos + maxHoleDuration >= start && pos < end) { 7499 // play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length 7500 bufferStart = start; 7501 bufferEnd = end; 7502 bufferLen = bufferEnd - pos; 7503 } else if (pos + maxHoleDuration < start) { 7504 bufferStartNext = start; 7505 break; 7506 } 7507 } 7508 return { 7509 len: bufferLen, 7510 start: bufferStart || 0, 7511 end: bufferEnd || 0, 7512 nextStart: bufferStartNext 7513 }; 7514 } 7515 7516 /** 7517 * Safe method to get buffered property. 7518 * SourceBuffer.buffered may throw if SourceBuffer is removed from it's MediaSource 7519 */; 7520 BufferHelper.getBuffered = function getBuffered(media) { 7521 try { 7522 return media.buffered || noopBuffered; 7523 } catch (e) { 7524 logger.log('failed to get media.buffered', e); 7525 return noopBuffered; 7526 } 7527 }; 7528 return BufferHelper; 7529 }(); 7530 7531 var BufferOperationQueue = /*#__PURE__*/function () { 7532 function BufferOperationQueue(sourceBufferReference) { 7533 this.buffers = void 0; 7534 this.queues = { 7535 video: [], 7536 audio: [], 7537 audiovideo: [] 7538 }; 7539 this.buffers = sourceBufferReference; 7540 } 7541 var _proto = BufferOperationQueue.prototype; 7542 _proto.append = function append(operation, type, pending) { 7543 var queue = this.queues[type]; 7544 queue.push(operation); 7545 if (queue.length === 1 && !pending) { 7546 this.executeNext(type); 7547 } 7548 }; 7549 _proto.appendBlocker = function appendBlocker(type) { 7550 var _this = this; 7551 return new Promise(function (resolve) { 7552 var operation = { 7553 execute: resolve, 7554 onStart: function onStart() {}, 7555 onComplete: function onComplete() {}, 7556 onError: function onError() {} 7557 }; 7558 _this.append(operation, type); 7559 }); 7560 }; 7561 _proto.unblockAudio = function unblockAudio(op) { 7562 var queue = this.queues.audio; 7563 if (queue[0] === op) { 7564 this.shiftAndExecuteNext('audio'); 7565 } 7566 }; 7567 _proto.executeNext = function executeNext(type) { 7568 var queue = this.queues[type]; 7569 if (queue.length) { 7570 var operation = queue[0]; 7571 try { 7572 // Operations are expected to result in an 'updateend' event being fired. If not, the queue will lock. Operations 7573 // which do not end with this event must call _onSBUpdateEnd manually 7574 operation.execute(); 7575 } catch (error) { 7576 logger.warn("[buffer-operation-queue]: Exception executing \"" + type + "\" SourceBuffer operation: " + error); 7577 operation.onError(error); 7578 7579 // Only shift the current operation off, otherwise the updateend handler will do this for us 7580 var sb = this.buffers[type]; 7581 if (!(sb != null && sb.updating)) { 7582 this.shiftAndExecuteNext(type); 7583 } 7584 } 7585 } 7586 }; 7587 _proto.shiftAndExecuteNext = function shiftAndExecuteNext(type) { 7588 this.queues[type].shift(); 7589 this.executeNext(type); 7590 }; 7591 _proto.current = function current(type) { 7592 return this.queues[type][0]; 7593 };
vendor: 4,650 bytes, lines 7594-7711
7594 return BufferOperationQueue; 7595 }(); 7596 7597 var VIDEO_CODEC_PROFILE_REPLACE = /(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/; 7598 var BufferController = /*#__PURE__*/function (_Logger) { 7599 function BufferController(hls, fragmentTracker) { 7600 var _this; 7601 _this = _Logger.call(this, 'buffer-controller', hls.logger) || this; 7602 // The level details used to determine duration, target-duration and live 7603 _this.details = null; 7604 // cache the self generated object url to detect hijack of video tag 7605 _this._objectUrl = null; 7606 // A queue of buffer operations which require the SourceBuffer to not be updating upon execution 7607 _this.operationQueue = void 0; 7608 // References to event listeners for each SourceBuffer, so that they can be referenced for event removal 7609 _this.listeners = void 0; 7610 _this.hls = void 0; 7611 _this.fragmentTracker = void 0; 7612 // The number of BUFFER_CODEC events received before any sourceBuffers are created 7613 _this.bufferCodecEventsExpected = 0; 7614 // The total number of BUFFER_CODEC events received 7615 _this._bufferCodecEventsTotal = 0; 7616 // A reference to the attached media element 7617 _this.media = null; 7618 // A reference to the active media source 7619 _this.mediaSource = null; 7620 // Last MP3 audio chunk appended 7621 _this.lastMpegAudioChunk = null; 7622 // Audio fragment blocked from appending until corresponding video appends or context changes 7623 _this.blockedAudioAppend = null; 7624 // Keep track of video append position for unblocking audio 7625 _this.lastVideoAppendEnd = 0; 7626 _this.appendSource = void 0; 7627 // counters 7628 _this.appendErrors = { 7629 audio: 0, 7630 video: 0, 7631 audiovideo: 0 7632 }; 7633 _this.tracks = {}; 7634 _this.pendingTracks = {}; 7635 _this.sourceBuffer = void 0; 7636 _this._onEndStreaming = function (event) { 7637 if (!_this.hls) { 7638 return; 7639 } 7640 _this.hls.pauseBuffering(); 7641 }; 7642 _this._onStartStreaming = function (event) { 7643 if (!_this.hls) { 7644 return; 7645 } 7646 _this.hls.resumeBuffering(); 7647 }; 7648 // Keep as arrow functions so that we can directly reference these functions directly as event listeners 7649 _this._onMediaSourceOpen = function () { 7650 var _this2 = _this, 7651 media = _this2.media, 7652 mediaSource = _this2.mediaSource; 7653 _this.log('Media source opened'); 7654 if (media) { 7655 media.removeEventListener('emptied', _this._onMediaEmptied); 7656 var durationAndRange = _this.getDurationAndRange(); 7657 if (durationAndRange) { 7658 _this.updateMediaSource(durationAndRange); 7659 } 7660 _this.hls.trigger(Events.MEDIA_ATTACHED, { 7661 media: media, 7662 mediaSource: mediaSource 7663 }); 7664 } 7665 if (mediaSource) { 7666 // once received, don't listen anymore to sourceopen event 7667 mediaSource.removeEventListener('sourceopen', _this._onMediaSourceOpen); 7668 } 7669 _this.checkPendingTracks(); 7670 }; 7671 _this._onMediaSourceClose = function () { 7672 _this.log('Media source closed'); 7673 }; 7674 _this._onMediaSourceEnded = function () { 7675 _this.log('Media source ended'); 7676 }; 7677 _this._onMediaEmptied = function () { 7678 var _this3 = _this, 7679 mediaSrc = _this3.mediaSrc, 7680 _objectUrl = _this3._objectUrl; 7681 if (mediaSrc !== _objectUrl) { 7682 _this.error("Media element src was set while attaching MediaSource (" + _objectUrl + " > " + mediaSrc + ")"); 7683 } 7684 }; 7685 _this.hls = hls; 7686 _this.fragmentTracker = fragmentTracker; 7687 _this.appendSource = isManagedMediaSource(getMediaSource(hls.config.preferManagedMediaSource)); 7688 _this._initSourceBuffer(); 7689 _this.registerListeners(); 7690 return _this; 7691 } 7692 _inheritsLoose(BufferController, _Logger); 7693 var _proto = BufferController.prototype; 7694 _proto.hasSourceTypes = function hasSourceTypes() { 7695 return this.getSourceBufferTypes().length > 0 || Object.keys(this.pendingTracks).length > 0; 7696 }; 7697 _proto.destroy = function destroy() { 7698 this.unregisterListeners(); 7699 this.details = null; 7700 this.lastMpegAudioChunk = null; 7701 // @ts-ignore 7702 this.hls = this.fragmentTracker = null; 7703 // @ts-ignore 7704 this._onMediaSourceOpen = this._onMediaSourceClose = null; 7705 // @ts-ignore 7706 this._onMediaSourceEnded = null; 7707 // @ts-ignore 7708 this._onStartStreaming = this._onEndStreaming = null; 7709 }; 7710 _proto.registerListeners = function registerListeners() { 7711 var hls = this.hls;
7712 hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); 7713 hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); 7714 hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); 7715 hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); 7716 hls.on(Events.BUFFER_RESET, this.onBufferReset, this); 7717 hls.on(Events.BUFFER_APPENDING, this.onBufferAppending, this); 7718 hls.on(Events.BUFFER_CODECS, this.onBufferCodecs, this); 7719 hls.on(Events.BUFFER_EOS, this.onBufferEos, this); 7720 hls.on(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); 7721 hls.on(Events.LEVEL_UPDATED, this.onLevelUpdated, this); 7722 hls.on(Events.FRAG_PARSED, this.onFragParsed, this); 7723 hls.on(Events.FRAG_CHANGED, this.onFragChanged, this); 7724 hls.on(Events.ERROR, this.onError, this); 7725 }; 7726 _proto.unregisterListeners = function unregisterListeners() { 7727 var hls = this.hls; 7728 hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); 7729 hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); 7730 hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); 7731 hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); 7732 hls.off(Events.BUFFER_RESET, this.onBufferReset, this); 7733 hls.off(Events.BUFFER_APPENDING, this.onBufferAppending, this); 7734 hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this); 7735 hls.off(Events.BUFFER_EOS, this.onBufferEos, this); 7736 hls.off(Events.BUFFER_FLUSHING, this.onBufferFlushing, this); 7737 hls.off(Events.LEVEL_UPDATED, this.onLevelUpdated, this); 7738 hls.off(Events.FRAG_PARSED, this.onFragParsed, this); 7739 hls.off(Events.FRAG_CHANGED, this.onFragChanged, this); 7740 hls.off(Events.ERROR, this.onError, this); 7741 }; 7742 _proto._initSourceBuffer = function _initSourceBuffer() { 7743 this.sourceBuffer = {}; 7744 this.operationQueue = new BufferOperationQueue(this.sourceBuffer); 7745 this.listeners = { 7746 audio: [], 7747 video: [], 7748 audiovideo: [] 7749 }; 7750 this.resetAppendErrors(); 7751 this.lastMpegAudioChunk = null; 7752 this.blockedAudioAppend = null; 7753 this.lastVideoAppendEnd = 0; 7754 }; 7755 _proto.onManifestLoading = function onManifestLoading() { 7756 this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = 0; 7757 this.details = null; 7758 }; 7759 _proto.onManifestParsed = function onManifestParsed(event, data) { 7760 // in case of alt audio 2 BUFFER_CODECS events will be triggered, one per stream controller 7761 // sourcebuffers will be created all at once when the expected nb of tracks will be reached 7762 // in case alt audio is not used, only one BUFFER_CODEC event will be fired from main stream controller 7763 // it will contain the expected nb of source buffers, no need to compute it 7764 var codecEvents = 2; 7765 if (data.audio && !data.video || !data.altAudio || !false) { 7766 codecEvents = 1; 7767 } 7768 this.bufferCodecEventsExpected = this._bufferCodecEventsTotal = codecEvents; 7769 this.log(this.bufferCodecEventsExpected + " bufferCodec event(s) expected"); 7770 }; 7771 _proto.onMediaAttaching = function onMediaAttaching(event, data) { 7772 var media = this.media = data.media; 7773 var MediaSource = getMediaSource(this.appendSource); 7774 if (media && MediaSource) { 7775 var _ms$constructor; 7776 var ms = this.mediaSource = new MediaSource(); 7777 this.log("created media source: " + ((_ms$constructor = ms.constructor) == null ? void 0 : _ms$constructor.name)); 7778 // MediaSource listeners are arrow functions with a lexical scope, and do not need to be bound 7779 ms.addEventListener('sourceopen', this._onMediaSourceOpen); 7780 ms.addEventListener('sourceended', this._onMediaSourceEnded); 7781 ms.addEventListener('sourceclose', this._onMediaSourceClose); 7782 if (this.appendSource) { 7783 ms.addEventListener('startstreaming', this._onStartStreaming); 7784 ms.addEventListener('endstreaming', this._onEndStreaming); 7785 } 7786 7787 // cache the locally generated object url 7788 var objectUrl = this._objectUrl = self.URL.createObjectURL(ms); 7789 // link video and media Source 7790 if (this.appendSource) { 7791 try { 7792 media.removeAttribute('src'); 7793 // ManagedMediaSource will not open without disableRemotePlayback set to false or source alternatives 7794 var MMS = self.ManagedMediaSource; 7795 media.disableRemotePlayback = media.disableRemotePlayback || MMS && ms instanceof MMS; 7796 removeSourceChildren(media); 7797 addSource(media, objectUrl); 7798 media.load(); 7799 } catch (error) { 7800 media.src = objectUrl; 7801 } 7802 } else { 7803 media.src = objectUrl; 7804 } 7805 media.addEventListener('emptied', this._onMediaEmptied); 7806 } 7807 }; 7808 _proto.onMediaDetaching = function onMediaDetaching() { 7809 var media = this.media, 7810 mediaSource = this.mediaSource, 7811 _objectUrl = this._objectUrl; 7812 if (mediaSource) { 7813 this.log('media source detaching'); 7814 if (mediaSource.readyState === 'open') { 7815 try { 7816 // endOfStream could trigger exception if any sourcebuffer is in updating state 7817 // we don't really care about checking sourcebuffer state here, 7818 // as we are anyway detaching the MediaSource 7819 // let's just avoid this exception to propagate 7820 mediaSource.endOfStream(); 7821 } catch (err) { 7822 this.warn("onMediaDetaching: " + err.message + " while calling endOfStream"); 7823 } 7824 } 7825 // Clean up the SourceBuffers by invoking onBufferReset 7826 this.onBufferReset(); 7827 mediaSource.removeEventListener('sourceopen', this._onMediaSourceOpen); 7828 mediaSource.removeEventListener('sourceended', this._onMediaSourceEnded); 7829 mediaSource.removeEventListener('sourceclose', this._onMediaSourceClose); 7830 if (this.appendSource) { 7831 mediaSource.removeEventListener('startstreaming', this._onStartStreaming); 7832 mediaSource.removeEventListener('endstreaming', this._onEndStreaming); 7833 } 7834 this.mediaSource = null; 7835 this._objectUrl = null; 7836 } 7837 7838 // Detach properly the MediaSource from the HTMLMediaElement as 7839 // suggested in https://github.com/w3c/media-source/issues/53. 7840 if (media) { 7841 media.removeEventListener('emptied', this._onMediaEmptied); 7842 if (_objectUrl) { 7843 self.URL.revokeObjectURL(_objectUrl); 7844 } 7845 7846 // clean up video tag src only if it's our own url. some external libraries might 7847 // hijack the video tag and change its 'src' without destroying the Hls instance first 7848 if (this.mediaSrc === _objectUrl) { 7849 media.removeAttribute('src'); 7850 if (this.appendSource) { 7851 removeSourceChildren(media); 7852 } 7853 media.load(); 7854 } else { 7855 this.warn('media|source.src was changed by a third party - skip cleanup'); 7856 } 7857 this.media = null; 7858 } 7859 this.bufferCodecEventsExpected = this._bufferCodecEventsTotal; 7860 this.pendingTracks = {}; 7861 this.tracks = {}; 7862 this.hls.trigger(Events.MEDIA_DETACHED, undefined); 7863 }; 7864 _proto.onBufferReset = function onBufferReset() { 7865 var _this4 = this;
7866 this.getSourceBufferTypes().forEach(function (type) { 7867 _this4.resetBuffer(type); 7868 }); 7869 this._initSourceBuffer(); 7870 this.hls.resumeBuffering(); 7871 }; 7872 _proto.resetBuffer = function resetBuffer(type) { 7873 var sb = this.sourceBuffer[type]; 7874 try { 7875 if (sb) { 7876 var _this$mediaSource; 7877 this.removeBufferListeners(type); 7878 // Synchronously remove the SB from the map before the next call in order to prevent an async function from 7879 // accessing it 7880 this.sourceBuffer[type] = undefined; 7881 if ((_this$mediaSource = this.mediaSource) != null && _this$mediaSource.sourceBuffers.length) { 7882 this.mediaSource.removeSourceBuffer(sb); 7883 } 7884 } 7885 } catch (err) { 7886 this.warn("onBufferReset " + type, err); 7887 } 7888 }; 7889 _proto.onBufferCodecs = function onBufferCodecs(event, data) { 7890 var _this5 = this; 7891 var sourceBufferCount = this.getSourceBufferTypes().length; 7892 var trackNames = Object.keys(data); 7893 trackNames.forEach(function (trackName) { 7894 if (sourceBufferCount) { 7895 var _track$buffer; 7896 // check if SourceBuffer codec needs to change 7897 var track = _this5.tracks[trackName]; 7898 if (track && typeof ((_track$buffer = track.buffer) == null ? void 0 : _track$buffer.changeType) === 'function') { 7899 var _trackCodec; 7900 var _data$trackName = data[trackName], 7901 id = _data$trackName.id, 7902 codec = _data$trackName.codec, 7903 levelCodec = _data$trackName.levelCodec, 7904 container = _data$trackName.container, 7905 metadata = _data$trackName.metadata; 7906 var currentCodecFull = pickMostCompleteCodecName(track.codec, track.levelCodec); 7907 var currentCodec = currentCodecFull == null ? void 0 : currentCodecFull.replace(VIDEO_CODEC_PROFILE_REPLACE, '$1'); 7908 var trackCodec = pickMostCompleteCodecName(codec, levelCodec); 7909 var nextCodec = (_trackCodec = trackCodec) == null ? void 0 : _trackCodec.replace(VIDEO_CODEC_PROFILE_REPLACE, '$1'); 7910 if (trackCodec && currentCodec !== nextCodec) { 7911 if (trackName.slice(0, 5) === 'audio') { 7912 trackCodec = getCodecCompatibleName(trackCodec, _this5.appendSource); 7913 } 7914 var mimeType = container + ";codecs=" + trackCodec; 7915 _this5.appendChangeType(trackName, mimeType); 7916 _this5.log("switching codec " + currentCodecFull + " to " + trackCodec); 7917 _this5.tracks[trackName] = { 7918 buffer: track.buffer, 7919 codec: codec, 7920 container: container, 7921 levelCodec: levelCodec, 7922 metadata: metadata, 7923 id: id 7924 }; 7925 } 7926 } 7927 } else { 7928 // if source buffer(s) not created yet, appended buffer tracks in this.pendingTracks 7929 _this5.pendingTracks[trackName] = data[trackName]; 7930 } 7931 }); 7932 7933 // if sourcebuffers already created, do nothing ... 7934 if (sourceBufferCount) { 7935 return; 7936 } 7937 var bufferCodecEventsExpected = Math.max(this.bufferCodecEventsExpected - 1, 0); 7938 if (this.bufferCodecEventsExpected !== bufferCodecEventsExpected) { 7939 this.log(bufferCodecEventsExpected + " bufferCodec event(s) expected " + trackNames.join(',')); 7940 this.bufferCodecEventsExpected = bufferCodecEventsExpected; 7941 } 7942 if (this.mediaSource && this.mediaSource.readyState === 'open') { 7943 this.checkPendingTracks(); 7944 } 7945 }; 7946 _proto.appendChangeType = function appendChangeType(type, mimeType) { 7947 var _this6 = this; 7948 var operationQueue = this.operationQueue; 7949 var operation = { 7950 execute: function execute() { 7951 var sb = _this6.sourceBuffer[type]; 7952 if (sb) { 7953 _this6.log("changing " + type + " sourceBuffer type to " + mimeType); 7954 sb.changeType(mimeType); 7955 } 7956 operationQueue.shiftAndExecuteNext(type); 7957 }, 7958 onStart: function onStart() {}, 7959 onComplete: function onComplete() {}, 7960 onError: function onError(error) { 7961 _this6.warn("Failed to change " + type + " SourceBuffer type", error); 7962 } 7963 }; 7964 operationQueue.append(operation, type, !!this.pendingTracks[type]); 7965 }; 7966 _proto.blockAudio = function blockAudio(partOrFrag) { 7967 var _this$fragmentTracker, 7968 _this7 = this; 7969 var pStart = partOrFrag.start; 7970 var pTime = pStart + partOrFrag.duration * 0.05; 7971 var atGap = ((_this$fragmentTracker = this.fragmentTracker.getAppendedFrag(pStart, PlaylistLevelType.MAIN)) == null ? void 0 : _this$fragmentTracker.ga
7971p) === true; 7972 if (atGap) { 7973 return; 7974 } 7975 var op = { 7976 execute: function execute() { 7977 var _this7$fragmentTracke; 7978 if (_this7.lastVideoAppendEnd > pTime || _this7.sourceBuffer.video && BufferHelper.isBuffered(_this7.sourceBuffer.video, pTime) || ((_this7$fragmentTracke = _this7.fragmentTracker.getAppendedFrag(pTime, PlaylistLevelType.MAIN)) == null ? void 0 : _this7$fragmentTracke.gap) === true) { 7979 _this7.blockedAudioAppend = null; 7980 _this7.operationQueue.shiftAndExecuteNext('audio'); 7981 } 7982 }, 7983 onStart: function onStart() {}, 7984 onComplete: function onComplete() {}, 7985 onError: function onError() {} 7986 }; 7987 this.blockedAudioAppend = { 7988 op: op, 7989 frag: partOrFrag 7990 }; 7991 this.operationQueue.append(op, 'audio', true); 7992 }; 7993 _proto.unblockAudio = function unblockAudio() { 7994 var blockedAudioAppend = this.blockedAudioAppend; 7995 if (blockedAudioAppend) { 7996 this.blockedAudioAppend = null; 7997 this.operationQueue.unblockAudio(blockedAudioAppend.op); 7998 } 7999 }; 8000 _proto.onBufferAppending = function onBufferAppending(event, eventData) { 8001 var _this8 = this; 8002 var operationQueue = this.operationQueue, 8003 tracks = this.tracks; 8004 var data = eventData.data, 8005 type = eventData.type, 8006 parent = eventData.parent, 8007 frag = eventData.frag, 8008 part = eventData.part, 8009 chunkMeta = eventData.chunkMeta; 8010 var chunkStats = chunkMeta.buffering[type]; 8011 var sn = frag.sn; 8012 var bufferAppendingStart = self.performance.now(); 8013 chunkStats.start = bufferAppendingStart; 8014 var fragBuffering = frag.stats.buffering; 8015 var partBuffering = part ? part.stats.buffering : null; 8016 if (fragBuffering.start === 0) { 8017 fragBuffering.start = bufferAppendingStart; 8018 } 8019 if (partBuffering && partBuffering.start === 0) { 8020 partBuffering.start = bufferAppendingStart; 8021 } 8022 8023 // TODO: Only update timestampOffset when audio/mpeg fragment or part is not contiguous with previously appended 8024 // Adjusting `SourceBuffer.timestampOffset` (desired point in the timeline where the next frames should be appended) 8025 // in Chrome browser when we detect MPEG audio container and time delta between level PTS and `SourceBuffer.timestampOffset` 8026 // is greater than 100ms (this is enough to handle seek for VOD or level change for LIVE videos). 8027 // More info here: https://github.com/video-dev/hls.js/issues/332#issuecomment-257986486 8028 var audioTrack = tracks.audio; 8029 var checkTimestampOffset = false; 8030 if (type === 'audio' && (audioTrack == null ? void 0 : audioTrack.container) === 'audio/mpeg') { 8031 checkTimestampOffset = !this.lastMpegAudioChunk || chunkMeta.id === 1 || this.lastMpegAudioChunk.sn !== chunkMeta.sn; 8032 this.lastMpegAudioChunk = chunkMeta; 8033 } 8034 8035 // Block audio append until overlapping video append 8036 var videoSb = this.sourceBuffer.video; 8037 if (videoSb && sn !== 'initSegment') { 8038 var partOrFrag = part || frag; 8039 var blockedAudioAppend = this.blockedAudioAppend; 8040 if (type === 'audio' && parent !== 'main' && !this.blockedAudioAppend) { 8041 var pStart = partOrFrag.start; 8042 var pTime = pStart + partOrFrag.duration * 0.05; 8043 var vbuffered = videoSb.buffered; 8044 var vappending = this.operationQueue.current('video'); 8045 if (!vbuffered.length && !vappending) { 8046 // wait for video before appending audio 8047 this.blockAudio(partOrFrag); 8048 } else if (!vappending && !BufferHelper.isBuffered(videoSb, pTime) && this.lastVideoAppendEnd < pTime) { 8049 // audio is ahead of video 8050 this.blockAudio(partOrFrag); 8051 } 8052 } else if (type === 'video') { 8053 var videoAppendEnd = partOrFrag.end; 8054 if (blockedAudioAppend) { 8055 var audioStart = blockedAudioAppend.frag.start; 8056 if (videoAppendEnd > audioStart || videoAppendEnd < this.lastVideoAppendEnd || BufferHelper.isBuffered(videoSb, audioStart)) { 8057 this.unblockAudio(); 8058 } 8059 } 8060 this.lastVideoAppendEnd = videoAppendEnd; 8061 } 8062 } 8063 var fragStart = (part || frag).start; 8064 var operation = { 8065 execute: function execute() { 8066 chunkStats.executeStart = self.performance.now(); 8067 if (checkTimestampOffset) { 8068 var sb = _this8.sourceBuffer[type]; 8069 if (sb) { 8070 var delta = fragStart - sb.timestampOffset; 8071 if (Math.abs(delta) >= 0.1) { 8072 _this8.log("Updating audio SourceBuffer timestampOffset to " + fragStart + " (delta: " + delta + ") sn: " + sn + ")"); 8073 sb.timestampOffset = fragStart; 8074 } 8075 } 8076 } 8077 _this8.appendExecutor(data, type); 8078 }, 8079 onStart: function onStart() { 8080 // logger.debug(`[buffer-controller]: ${type} SourceBuffer updatestart`); 8081 }, 8082 onComplete: function onComplete() { 8083 // logger.debug(`[buffer-controller]: ${type} SourceBuffer updateend`); 8084 var end = self.performance.now(); 8085 chunkStats.executeEnd = chunkStats.end = end; 8086 if (fragBuffering.first === 0) { 8087 fragBuffering.first = end; 8088 }
8089 if (partBuffering && partBuffering.first === 0) { 8090 partBuffering.first = end; 8091 } 8092 var sourceBuffer = _this8.sourceBuffer; 8093 var timeRanges = {}; 8094 for (var _type in sourceBuffer) { 8095 timeRanges[_type] = BufferHelper.getBuffered(sourceBuffer[_type]); 8096 } 8097 _this8.appendErrors[type] = 0; 8098 if (type === 'audio' || type === 'video') { 8099 _this8.appendErrors.audiovideo = 0; 8100 } else { 8101 _this8.appendErrors.audio = 0; 8102 _this8.appendErrors.video = 0; 8103 } 8104 _this8.hls.trigger(Events.BUFFER_APPENDED, { 8105 type: type, 8106 frag: frag, 8107 part: part, 8108 chunkMeta: chunkMeta, 8109 parent: frag.type, 8110 timeRanges: timeRanges 8111 }); 8112 }, 8113 onError: function onError(error) { 8114 // in case any error occured while appending, put back segment in segments table 8115 var event = { 8116 type: ErrorTypes.MEDIA_ERROR, 8117 parent: frag.type, 8118 details: ErrorDetails.BUFFER_APPEND_ERROR, 8119 sourceBufferName: type, 8120 frag: frag, 8121 part: part, 8122 chunkMeta: chunkMeta, 8123 error: error, 8124 err: error, 8125 fatal: false 8126 }; 8127 if (error.code === DOMException.QUOTA_EXCEEDED_ERR) { 8128 // QuotaExceededError: http://www.w3.org/TR/html5/infrastructure.html#quotaexceedederror 8129 // let's stop appending any segments, and report BUFFER_FULL_ERROR error 8130 event.details = ErrorDetails.BUFFER_FULL_ERROR; 8131 } else { 8132 var appendErrorCount = ++_this8.appendErrors[type]; 8133 event.details = ErrorDetails.BUFFER_APPEND_ERROR; 8134 /* with UHD content, we could get loop of quota exceeded error until 8135 browser is able to evict some data from sourcebuffer. Retrying can help recover. 8136 */ 8137 _this8.warn("Failed " + appendErrorCount + "/" + _this8.hls.config.appendErrorMaxRetry + " times to append segment in \"" + type + "\" sourceBuffer"); 8138 if (appendErrorCount >= _this8.hls.config.appendErrorMaxRetry) { 8139 event.fatal = true; 8140 } 8141 } 8142 _this8.hls.trigger(Events.ERROR, event); 8143 } 8144 }; 8145 operationQueue.append(operation, type, !!this.pendingTracks[type]); 8146 }; 8147 _proto.getFlushOp = function getFlushOp(type, start, end) { 8148 var _this9 = this; 8149 return { 8150 execute: function execute() { 8151 _this9.removeExecutor(type, start, end); 8152 }, 8153 onStart: function onStart() { 8154 // logger.debug(`[buffer-controller]: Started flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`); 8155 }, 8156 onComplete: function onComplete() { 8157 // logger.debug(`[buffer-controller]: Finished flushing ${data.startOffset} -> ${data.endOffset} for ${type} Source Buffer`); 8158 _this9.hls.trigger(Events.BUFFER_FLUSHED, { 8159 type: type 8160 }); 8161 }, 8162 onError: function onError(error) { 8163 _this9.warn("Failed to remove from " + type + " SourceBuffer", error); 8164 } 8165 }; 8166 }; 8167 _proto.onBufferFlushing = function onBufferFlushing(event, data) { 8168 var _this10 = this; 8169 var operationQueue = this.operationQueue; 8170 var type = data.type, 8171 startOffset = data.startOffset, 8172 endOffset = data.endOffset; 8173 if (type) { 8174 operationQueue.append(this.getFlushOp(type, startOffset, endOffset), type); 8175 } else {
8176 this.getSourceBufferTypes().forEach(function (sbType) { 8177 operationQueue.append(_this10.getFlushOp(sbType, startOffset, endOffset), sbType); 8178 }); 8179 } 8180 }; 8181 _proto.onFragParsed = function onFragParsed(event, data) { 8182 var _this11 = this; 8183 var frag = data.frag, 8184 part = data.part; 8185 var buffersAppendedTo = []; 8186 var elementaryStreams = part ? part.elementaryStreams : frag.elementaryStreams; 8187 if (elementaryStreams[ElementaryStreamTypes.AUDIOVIDEO]) { 8188 buffersAppendedTo.push('audiovideo'); 8189 } else { 8190 if (elementaryStreams[ElementaryStreamTypes.AUDIO]) { 8191 buffersAppendedTo.push('audio'); 8192 } 8193 if (elementaryStreams[ElementaryStreamTypes.VIDEO]) { 8194 buffersAppendedTo.push('video'); 8195 } 8196 } 8197 var onUnblocked = function onUnblocked() { 8198 var now = self.performance.now(); 8199 frag.stats.buffering.end = now; 8200 if (part) { 8201 part.stats.buffering.end = now; 8202 } 8203 var stats = part ? part.stats : frag.stats; 8204 _this11.hls.trigger(Events.FRAG_BUFFERED, { 8205 frag: frag, 8206 part: part, 8207 stats: stats, 8208 id: frag.type 8209 }); 8210 }; 8211 if (buffersAppendedTo.length === 0) { 8212 this.warn("Fragments must have at least one ElementaryStreamType set. type: " + frag.type + " level: " + frag.level + " sn: " + frag.sn); 8213 } 8214 this.blockBuffers(onUnblocked, buffersAppendedTo); 8215 }; 8216 _proto.onFragChanged = function onFragChanged(event, data) { 8217 this.trimBuffers(); 8218 } 8219 8220 // on BUFFER_EOS mark matching sourcebuffer(s) as ended and trigger checkEos() 8221 // an undefined data.type will mark all buffers as EOS. 8222 ; 8223 _proto.onBufferEos = function onBufferEos(event, data) { 8224 var _this12 = this; 8225 if (data.type === 'video') { 8226 this.unblockAudio(); 8227 } 8228 var ended = this.getSourceBufferTypes().reduce(function (acc, type) { 8229 var sb = _this12.sourceBuffer[type]; 8230 if (sb && (!data.type || data.type === type)) { 8231 sb.ending = true; 8232 if (!sb.ended) { 8233 sb.ended = true; 8234 _this12.log(type + " sourceBuffer now EOS"); 8235 } 8236 } 8237 return acc && !!(!sb || sb.ended); 8238 }, true); 8239 if (ended) { 8240 this.log("Queueing mediaSource.endOfStream()"); 8241 this.blockBuffers(function () { 8242 _this12.getSourceBufferTypes().forEach(function (type) { 8243 var sb = _this12.sourceBuffer[type]; 8244 if (sb) { 8245 sb.ending = false; 8246 } 8247 }); 8248 var mediaSource = _this12.mediaSource; 8249 if (!mediaSource || mediaSource.readyState !== 'open') { 8250 if (mediaSource) { 8251 _this12.log("Could not call mediaSource.endOfStream(). mediaSource.readyState: " + mediaSource.readyState); 8252 } 8253 return; 8254 } 8255 _this12.log("Calling mediaSource.endOfStream()"); 8256 // Allow this to throw and be caught by the enqueueing function 8257 mediaSource.endOfStream(); 8258 }); 8259 } 8260 }; 8261 _proto.onLevelUpdated = function onLevelUpdated(event, _ref) { 8262 var _this13 = this; 8263 var details = _ref.details; 8264 if (!details.fragments.length) { 8265 return; 8266 } 8267 this.details = details; 8268 var durationAndRange = this.getDurationAndRange(); 8269 if (!durationAndRange) { 8270 return; 8271 } 8272 if (this.getSourceBufferTypes().length) { 8273 this.blockBuffers(function () { 8274 return _this13.updateMediaSource(durationAndRange); 8275 }); 8276 } else { 8277 this.updateMediaSource(durationAndRange); 8278 } 8279 }; 8280 _proto.onError = function onError(event, data) { 8281 if (data.details === ErrorDetails.BUFFER_APPEND_ERROR && data.frag) { 8282 var _data$errorAction; 8283 var nextAutoLevel = (_data$errorAction = data.errorAction) == null ? void 0 : _data$errorAction.nextAutoLevel; 8284 if (isFiniteNumber(nextAutoLevel) && nextAutoLevel !== data.frag.level) { 8285 this.resetAppendErrors(); 8286 } 8287 } 8288 }; 8289 _proto.resetAppendErrors = function resetAppendErrors() { 8290 this.appendErrors = { 8291 audio: 0, 8292 video: 0, 8293 audiovideo: 0 8294 }; 8295 }; 8296 _proto.trimBuffers = function trimBuffers() { 8297 var hls = this.hls, 8298 details = this.details, 8299 media = this.media; 8300 if (!media || details === null) { 8301 return; 8302 }
8303 var sourceBufferTypes = this.getSourceBufferTypes(); 8304 if (!sourceBufferTypes.length) { 8305 return; 8306 } 8307 var config = hls.config; 8308 var currentTime = media.currentTime; 8309 var targetDuration = details.levelTargetDuration; 8310 8311 // Support for deprecated liveBackBufferLength 8312 var backBufferLength = details.live && config.liveBackBufferLength !== null ? config.liveBackBufferLength : config.backBufferLength; 8313 if (isFiniteNumber(backBufferLength) && backBufferLength > 0) { 8314 var maxBackBufferLength = Math.max(backBufferLength, targetDuration); 8315 var targetBackBufferPosition = Math.floor(currentTime / targetDuration) * targetDuration - maxBackBufferLength; 8316 this.flushBackBuffer(currentTime, targetDuration, targetBackBufferPosition); 8317 } 8318 if (isFiniteNumber(config.frontBufferFlushThreshold) && config.frontBufferFlushThreshold > 0) { 8319 var frontBufferLength = Math.max(config.maxBufferLength, config.frontBufferFlushThreshold); 8320 var maxFrontBufferLength = Math.max(frontBufferLength, targetDuration); 8321 var targetFrontBufferPosition = Math.floor(currentTime / targetDuration) * targetDuration + maxFrontBufferLength; 8322 this.flushFrontBuffer(currentTime, targetDuration, targetFrontBufferPosition); 8323 } 8324 }; 8325 _proto.flushBackBuffer = function flushBackBuffer(currentTime, targetDuration, targetBackBufferPosition) { 8326 var _this14 = this; 8327 var details = this.details, 8328 sourceBuffer = this.sourceBuffer; 8329 var sourceBufferTypes = this.getSourceBufferTypes(); 8330 sourceBufferTypes.forEach(function (type) { 8331 var sb = sourceBuffer[type]; 8332 if (sb) { 8333 var buffered = BufferHelper.getBuffered(sb); 8334 // when target buffer start exceeds actual buffer start 8335 if (buffered.length > 0 && targetBackBufferPosition > buffered.start(0)) { 8336 _this14.hls.trigger(Events.BACK_BUFFER_REACHED, { 8337 bufferEnd: targetBackBufferPosition 8338 }); 8339 8340 // Support for deprecated event: 8341 if (details != null && details.live) { 8342 _this14.hls.trigger(Events.LIVE_BACK_BUFFER_REACHED, { 8343 bufferEnd: targetBackBufferPosition 8344 }); 8345 } else if (sb.ended && buffered.end(buffered.length - 1) - currentTime < targetDuration * 2) { 8346 _this14.log("Cannot flush " + type + " back buffer while SourceBuffer is in ended state"); 8347 return; 8348 } 8349 _this14.hls.trigger(Events.BUFFER_FLUSHING, { 8350 startOffset: 0, 8351 endOffset: targetBackBufferPosition, 8352 type: type 8353 }); 8354 } 8355 } 8356 }); 8357 }; 8358 _proto.flushFrontBuffer = function flushFrontBuffer(currentTime, targetDuration, targetFrontBufferPosition) { 8359 var _this15 = this; 8360 var sourceBuffer = this.sourceBuffer; 8361 var sourceBufferTypes = this.getSourceBufferTypes(); 8362 sourceBufferTypes.forEach(function (type) { 8363 var sb = sourceBuffer[type]; 8364 if (sb) { 8365 var buffered = BufferHelper.getBuffered(sb); 8366 var numBufferedRanges = buffered.length; 8367 // The buffer is either empty or contiguous 8368 if (numBufferedRanges < 2) { 8369 return; 8370 } 8371 var bufferStart = buffered.start(numBufferedRanges - 1); 8372 var bufferEnd = buffered.end(numBufferedRanges - 1); 8373 // No flush if we can tolerate the current buffer length or the current buffer range we would flush is contiguous with current position 8374 if (targetFrontBufferPosition > bufferStart || currentTime >= bufferStart && currentTime <= bufferEnd) { 8375 return; 8376 } else if (sb.ended && currentTime - bufferEnd < 2 * targetDuration) { 8377 _this15.log("Cannot flush " + type + " front buffer while SourceBuffer is in ended state"); 8378 return; 8379 } 8380 _this15.hls.trigger(Events.BUFFER_FLUSHING, { 8381 startOffset: bufferStart, 8382 endOffset: Infinity, 8383 type: type 8384 }); 8385 } 8386 }); 8387 } 8388 8389 /** 8390 * Update Media Source duration to current level duration or override to Infinity if configuration parameter 8391 * 'liveDurationInfinity` is set to `true` 8392 * More details: https://github.com/video-dev/hls.js/issues/355 8393 */; 8394 _proto.getDurationAndRange = function getDurationAndRange() { 8395 if (!this.details || !this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') { 8396 return null; 8397 } 8398 var details = this.details, 8399 hls = this.hls, 8400 media = this.media, 8401 mediaSource = this.mediaSource; 8402 var levelDuration = details.fragments[0].start + details.totalduration; 8403 var mediaDuration = media.duration; 8404 var msDuration = isFiniteNumber(mediaSource.duration) ? mediaSource.duration : 0; 8405 if (details.live && hls.config.liveDurationInfinity) { 8406 // Override duration to Infinity 8407 mediaSource.duration = Infinity; 8408 var len = details.fragments.length; 8409 if (len && details.live && !!mediaSource.setLiveSeekableRange) { 8410 var start = Math.max(0, details.fragments[0].start); 8411 var end = Math.max(start, start + details.totalduration); 8412 return { 8413 duration: Infinity, 8414 start: start, 8415 end: end 8416 }; 8417 } 8418 return { 8419 duration: Infinity 8420 }; 8421 } else if (levelDuration > msDuration && levelDuration > mediaDuration || !isFiniteNumber(mediaDuration)) { 8422 return { 8423 duration: levelDuration 8424 }; 8425 } 8426 return null; 8427 }; 8428 _proto.updateMediaSource = function updateMediaSource(_ref2) { 8429 var duration = _ref2.duration, 8430 start = _ref2.start, 8431 end = _ref2.end; 8432 if (!this.media || !this.mediaSource || this.mediaSource.readyState !== 'open') { 8433 return; 8434 } 8435 if (isFiniteNumber(duration)) { 8436 this.log("Updating Media Source duration to " + duration.toFixed(3)); 8437 } 8438 this.mediaSource.duration = duration; 8439 if (start !== undefined && end !== undefined) { 8440 this.log("Media Source duration is set to " + this.mediaSource.duration + ". Setting seekable range to " + start + "-" + end + "."); 8441 this.mediaSource.setLiveSeekableRange(start, end); 8442 } 8443 }; 8444 _proto.checkPendingTracks = function checkPendingTracks() { 8445 var bufferCodecEventsExpected = this.bufferCodecEventsExpected, 8446 operationQueue = this.operationQueue, 8447 pendingTracks = this.pendingTracks; 8448 8449 // Check if we've received all of the expected bufferCodec events. When none remain, create all the sourceBuffers at once.
8450 // This is important because the MSE spec allows implementations to throw QuotaExceededErrors if creating new sourceBuffers after 8451 // data has been appended to existing ones. 8452 // 2 tracks is the max (one for audio, one for video). If we've reach this max go ahead and create the buffers. 8453 var pendingTracksCount = Object.keys(pendingTracks).length; 8454 if (pendingTracksCount && (!bufferCodecEventsExpected || pendingTracksCount === 2 || 'audiovideo' in pendingTracks)) { 8455 // ok, let's create them now ! 8456 this.createSourceBuffers(pendingTracks); 8457 this.pendingTracks = {}; 8458 // append any pending segments now ! 8459 var buffers = this.getSourceBufferTypes(); 8460 if (buffers.length) { 8461 this.hls.trigger(Events.BUFFER_CREATED, { 8462 tracks: this.tracks 8463 }); 8464 buffers.forEach(function (type) { 8465 operationQueue.executeNext(type); 8466 }); 8467 } else { 8468 var error = new Error('could not create source buffer for media codec(s)'); 8469 this.hls.trigger(Events.ERROR, { 8470 type: ErrorTypes.MEDIA_ERROR, 8471 details: ErrorDetails.BUFFER_INCOMPATIBLE_CODECS_ERROR, 8472 fatal: true, 8473 error: error, 8474 reason: error.message 8475 }); 8476 } 8477 } 8478 }; 8479 _proto.createSourceBuffers = function createSourceBuffers(tracks) { 8480 var _this16 = this; 8481 var sourceBuffer = this.sourceBuffer, 8482 mediaSource = this.mediaSource; 8483 if (!mediaSource) { 8484 throw Error('createSourceBuffers called when mediaSource was null'); 8485 } 8486 var _loop = function _loop(trackName) { 8487 if (!sourceBuffer[trackName]) { 8488 var _track$levelCodec; 8489 var track = tracks[trackName]; 8490 if (!track) { 8491 throw Error("source buffer exists for track " + trackName + ", however track does not"); 8492 } 8493 // use levelCodec as first priority unless it contains multiple comma-separated codec values 8494 var codec = ((_track$levelCodec = track.levelCodec) == null ? void 0 : _track$levelCodec.indexOf(',')) === -1 ? track.levelCodec : track.codec; 8495 if (codec) { 8496 if (trackName.slice(0, 5) === 'audio') { 8497 codec = getCodecCompatibleName(codec, _this16.appendSource); 8498 } 8499 } 8500 var mimeType = track.container + ";codecs=" + codec; 8501 _this16.log("creating sourceBuffer(" + mimeType + ")"); 8502 try { 8503 var sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType); 8504 var sbName = trackName; 8505 _this16.addBufferListener(sbName, 'updatestart', _this16._onSBUpdateStart); 8506 _this16.addBufferListener(sbName, 'updateend', _this16._onSBUpdateEnd); 8507 _this16.addBufferListener(sbName, 'error', _this16._onSBUpdateError); 8508 // ManagedSourceBuffer bufferedchange event 8509 if (_this16.appendSource) { 8510 _this16.addBufferListener(sbName, 'bufferedchange', function (type, event) { 8511 // If media was ejected check for a change. Added ranges are redundant with changes on 'updateend' event. 8512 var removedRanges = event.removedRanges; 8513 if (removedRanges != null && removedRanges.length) { 8514 _this16.hls.trigger(Events.BUFFER_FLUSHED, { 8515 type: trackName 8516 }); 8517 } 8518 }); 8519 } 8520 _this16.tracks[trackName] = { 8521 buffer: sb, 8522 codec: codec, 8523 container: track.container, 8524 levelCodec: track.levelCodec, 8525 metadata: track.metadata, 8526 id: track.id 8527 }; 8528 } catch (err) { 8529 _this16.error("error while trying to add sourceBuffer: " + err.message); 8530 _this16.hls.trigger(Events.ERROR, { 8531 type: ErrorTypes.MEDIA_ERROR, 8532 details: ErrorDetails.BUFFER_ADD_CODEC_ERROR, 8533 fatal: false, 8534 error: err, 8535 sourceBufferName: trackName, 8536 mimeType: mimeType 8537 }); 8538 } 8539 } 8540 }; 8541 for (var trackName in tracks) { 8542 _loop(trackName); 8543 } 8544 }; 8545 _proto._onSBUpdateStart = function _onSBUpdateStart(type) { 8546 var operationQueue = this.operationQueue; 8547 var operation = operationQueue.current(type); 8548 operation.onStart(); 8549 }; 8550 _proto._onSBUpdateEnd = function _onSBUpdateEnd(type) { 8551 var _this$mediaSource2; 8552 if (((_this$mediaSource2 = this.mediaSource) == null ? void 0 : _this$mediaSource2.readyState) === 'closed') { 8553 this.resetBuffer(type); 8554 return; 8555 } 8556 var operationQueue = this.operationQueue; 8557 var operation = operationQueue.current(type); 8558 operation.onComplete(); 8559 operationQueue.shiftAndExecuteNext(type); 8560 }; 8561 _proto._onSBUpdateError = function _onSBUpdateError(type, event) { 8562 var _this$mediaSource3; 8563 var error = new Error(type + " SourceBuffer error. MediaSource readyState: " + ((_this$mediaSource3 = this.mediaSource) == null ? void 0 : _this$mediaSource3.readyState)); 8564 this.error("" + error, event); 8565 // according to http://www.w3.org/TR/media-source/#sourcebuffer-append-error 8566 // SourceBuffer errors are not necessarily fatal; if so, the HTMLMediaElement will fire an error event 8567 this.hls.trigger(Events.ERROR, { 8568 type: ErrorTypes.MEDIA_ERROR, 8569 details: ErrorDetails.BUFFER_APPENDING_ERROR, 8570 sourceBufferName: type, 8571 error: error, 8572 fatal: false 8573 }); 8574 // updateend is always fired after error, so we'll allow that to shift the current operation off of the queue 8575 var operation = this.operationQueue.current(type); 8576 if (operation) { 8577 operation.onError(error); 8578 } 8579 } 8580 8581 // This method must result in an updateend event; if remove is not called, _onSBUpdateEnd must be called manually 8582 ; 8583 _proto.removeExecutor = function removeExecutor(type, startOffset, endOffset) { 8584 var media = this.media, 8585 mediaSource = this.mediaSource, 8586 operationQueue = this.operationQueue, 8587 sourceBuffer = this.sourceBuffer; 8588 var sb = sourceBuffer[type]; 8589 if (!media || !mediaSource || !sb) { 8590 this.warn("Attempting to remove from the " + type + " SourceBuffer, but it does not exist"); 8591 operationQueue.shiftAndExecuteNext(type); 8592 return; 8593 } 8594 var mediaDuration = isFiniteNumber(media.duration) ? media.duration : Infinity; 8595 var msDuration = isFiniteNumber(mediaSource.duration) ? mediaSource.duration : Infinity; 8596 var removeStart = Math.max(0, startOffset); 8597 var removeEnd = Math.min(endOffset, mediaDuration, msDuration); 8598 if (removeEnd > removeStart && (!sb.ending || sb.ended)) { 8599 sb.ended = false;
8600 this.log("Removing [" + removeStart + "," + removeEnd + "] from the " + type + " SourceBuffer"); 8601 sb.remove(removeStart, removeEnd); 8602 } else { 8603 // Cycle the queue 8604 operationQueue.shiftAndExecuteNext(type); 8605 } 8606 } 8607 8608 // This method must result in an updateend event; if append is not called, _onSBUpdateEnd must be called manually 8609 ; 8610 _proto.appendExecutor = function appendExecutor(data, type) { 8611 var sb = this.sourceBuffer[type]; 8612 if (!sb) { 8613 if (!this.pendingTracks[type]) { 8614 throw new Error("Attempting to append to the " + type + " SourceBuffer, but it does not exist"); 8615 } 8616 return; 8617 } 8618 sb.ending = false; 8619 sb.ended = false; 8620 sb.appendBuffer(data); 8621 } 8622 8623 // Enqueues an operation to each SourceBuffer queue which, upon execution, resolves a promise. When all promises 8624 // resolve, the onUnblocked function is executed. Functions calling this method do not need to unblock the queue 8625 // upon completion, since we already do it here 8626 ; 8627 _proto.blockBuffers = function blockBuffers(onUnblocked, buffers) { 8628 var _this17 = this; 8629 if (buffers === void 0) { 8630 buffers = this.getSourceBufferTypes(); 8631 } 8632 if (!buffers.length) { 8633 this.log('Blocking operation requested, but no SourceBuffers exist'); 8634 Promise.resolve().then(onUnblocked); 8635 return; 8636 } 8637 var operationQueue = this.operationQueue; 8638 8639 // logger.debug(`[buffer-controller]: Blocking ${buffers} SourceBuffer`); 8640 var blockingOperations = buffers.map(function (type) { 8641 return operationQueue.appendBlocker(type); 8642 }); 8643 var audioBlocked = buffers.length > 1 && !!this.blockedAudioAppend; 8644 if (audioBlocked) { 8645 this.unblockAudio(); 8646 } 8647 Promise.all(blockingOperations).then(function (result) { 8648 // logger.debug(`[buffer-controller]: Blocking operation resolved; unblocking ${buffers} SourceBuffer`); 8649 onUnblocked(); 8650 buffers.forEach(function (type, i) { 8651 var sb = _this17.sourceBuffer[type]; 8652 // Only cycle the queue if the SB is not updating. There's a bug in Chrome which sets the SB updating flag to 8653 // true when changing the MediaSource duration (https://bugs.chromium.org/p/chromium/issues/detail?id=959359&can=2&q=mediasource%20duration) 8654 // While this is a workaround, it's probably useful to have around 8655 if (!(sb != null && sb.updating)) { 8656 operationQueue.shiftAndExecuteNext(type); 8657 } 8658 }); 8659 }); 8660 }; 8661 _proto.getSourceBufferTypes = function getSourceBufferTypes() { 8662 return Object.keys(this.sourceBuffer); 8663 }; 8664 _proto.addBufferListener = function addBufferListener(type, event, fn) { 8665 var buffer = this.sourceBuffer[type]; 8666 if (!buffer) { 8667 return; 8668 } 8669 var listener = fn.bind(this, type); 8670 this.listeners[type].push({ 8671 event: event, 8672 listener: listener 8673 }); 8674 buffer.addEventListener(event, listener); 8675 }; 8676 _proto.removeBufferListeners = function removeBufferListeners(type) { 8677 var buffer = this.sourceBuffer[type]; 8678 if (!buffer) { 8679 return; 8680 } 8681 this.listeners[type].forEach(function (l) { 8682 buffer.removeEventListener(l.event, l.listener); 8683 }); 8684 }; 8685 return _createClass(BufferController, [{ 8686 key: "mediaSrc", 8687 get: function get() { 8688 var _this$media, _this$media$querySele; 8689 var media = ((_this$media = this.media) == null ? void 0 : (_this$media$querySele = _this$media.querySelector) == null ? void 0 : _this$media$querySele.call(_this$media, 'source')) || this.media; 8690 return media == null ? void 0 : media.src; 8691 } 8692 }]); 8693 }(Logger); 8694 function removeSourceChildren(node) { 8695 var sourceChildren = node.querySelectorAll('source'); 8696 [].slice.call(sourceChildren).forEach(function (source) { 8697 node.removeChild(source); 8698 }); 8699 } 8700 function addSource(media, url) { 8701 var source = self.document.createElement('source'); 8702 source.type = 'video/mp4'; 8703 source.src = url; 8704 media.appendChild(source); 8705 } 8706 8707 var CapLevelController = /*#__PURE__*/function () { 8708 function CapLevelController(hls) { 8709 this.hls = void 0; 8710 this.autoLevelCapping = void 0; 8711 this.firstLevel = void 0; 8712 this.media = void 0; 8713 this.restrictedLevels = void 0; 8714 this.timer = void 0; 8715 this.clientRect = void 0; 8716 this.streamController = void 0; 8717 this.hls = hls; 8718 this.autoLevelCapping = Number.POSITIVE_INFINITY; 8719 this.firstLevel = -1; 8720 this.media = null; 8721 this.restrictedLevels = []; 8722 this.timer = undefined; 8723 this.clientRect = null; 8724 this.registerListeners(); 8725 } 8726 var _proto = CapLevelController.prototype; 8727 _proto.setStreamController = function setStreamController(streamController) { 8728 this.streamController = streamController; 8729 }; 8730 _proto.destroy = function destroy() { 8731 if (this.hls) { 8732 this.unregisterListener(); 8733 } 8734 if (this.timer) { 8735 this.stopCapping(); 8736 } 8737 this.media = null; 8738 this.clientRect = null; 8739 // @ts-ignore 8740 this.hls = this.streamController = null; 8741 }; 8742 _proto.registerListeners = function registerListeners() { 8743 var hls = this.hls; 8744 hls.on(Events.FPS_DROP_LEVEL_CAPPING, this.onFpsDropLevelCapping, this);
8745 hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); 8746 hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); 8747 hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); 8748 hls.on(Events.BUFFER_CODECS, this.onBufferCodecs, this); 8749 hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); 8750 }; 8751 _proto.unregisterListener = function unregisterListener() { 8752 var hls = this.hls; 8753 hls.off(Events.FPS_DROP_LEVEL_CAPPING, this.onFpsDropLevelCapping, this); 8754 hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); 8755 hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); 8756 hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); 8757 hls.off(Events.BUFFER_CODECS, this.onBufferCodecs, this); 8758 hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); 8759 }; 8760 _proto.onFpsDropLevelCapping = function onFpsDropLevelCapping(event, data) { 8761 // Don't add a restricted level more than once 8762 var level = this.hls.levels[data.droppedLevel]; 8763 if (this.isLevelAllowed(level)) { 8764 this.restrictedLevels.push({ 8765 bitrate: level.bitrate, 8766 height: level.height, 8767 width: level.width 8768 }); 8769 } 8770 }; 8771 _proto.onMediaAttaching = function onMediaAttaching(event, data) { 8772 this.media = data.media instanceof HTMLVideoElement ? data.media : null; 8773 this.clientRect = null; 8774 if (this.timer && this.hls.levels.length) { 8775 this.detectPlayerSize(); 8776 } 8777 }; 8778 _proto.onManifestParsed = function onManifestParsed(event, data) { 8779 var hls = this.hls; 8780 this.restrictedLevels = []; 8781 this.firstLevel = data.firstLevel; 8782 if (hls.config.capLevelToPlayerSize && data.video) { 8783 // Start capping immediately if the manifest has signaled video codecs 8784 this.startCapping(); 8785 } 8786 }; 8787 _proto.onLevelsUpdated = function onLevelsUpdated(event, data) { 8788 if (this.timer && isFiniteNumber(this.autoLevelCapping)) { 8789 this.detectPlayerSize(); 8790 } 8791 } 8792 8793 // Only activate capping when playing a video stream; otherwise, multi-bitrate audio-only streams will be restricted 8794 // to the first level 8795 ; 8796 _proto.onBufferCodecs = function onBufferCodecs(event, data) { 8797 var hls = this.hls; 8798 if (hls.config.capLevelToPlayerSize && data.video) { 8799 // If the manifest did not signal a video codec capping has been deferred until we're certain video is present 8800 this.startCapping(); 8801 } 8802 }; 8803 _proto.onMediaDetaching = function onMediaDetaching() { 8804 this.stopCapping(); 8805 this.media = null; 8806 }; 8807 _proto.detectPlayerSize = function detectPlayerSize() { 8808 if (this.media) { 8809 if (this.mediaHeight <= 0 || this.mediaWidth <= 0) { 8810 this.clientRect = null; 8811 return; 8812 } 8813 var levels = this.hls.levels; 8814 if (levels.length) { 8815 var hls = this.hls; 8816 var maxLevel = this.getMaxLevel(levels.length - 1); 8817 if (maxLevel !== this.autoLevelCapping) { 8818 hls.logger.log("Setting autoLevelCapping to " + maxLevel + ": " + levels[maxLevel].height + "p@" + levels[maxLevel].bitrate + " for media " + this.mediaWidth + "x" + this.mediaHeight); 8819 } 8820 hls.autoLevelCapping = maxLevel; 8821 if (hls.autoLevelEnabled && hls.autoLevelCapping > this.autoLevelCapping && this.streamController) { 8822 // if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch 8823 // usually happen when the user go to the fullscreen mode. 8824 this.streamController.nextLevelSwitch(); 8825 } 8826 this.autoLevelCapping = hls.autoLevelCapping; 8827 } 8828 } 8829 } 8830 8831 /* 8832 * returns level should be the one with the dimensions equal or greater than the media (player) dimensions (so the video will be downscaled) 8833 */; 8834 _proto.getMaxLevel = function getMaxLevel(capLevelIndex) { 8835 var _this = this; 8836 var levels = this.hls.levels; 8837 if (!levels.length) { 8838 return -1; 8839 }
vendor: 4,275 bytes, lines 8840-8951
8840 var validLevels = levels.filter(function (level, index) { 8841 return _this.isLevelAllowed(level) && index <= capLevelIndex; 8842 }); 8843 this.clientRect = null; 8844 return CapLevelController.getMaxLevelByMediaSize(validLevels, this.mediaWidth, this.mediaHeight); 8845 }; 8846 _proto.startCapping = function startCapping() { 8847 if (this.timer) { 8848 // Don't reset capping if started twice; this can happen if the manifest signals a video codec 8849 return; 8850 } 8851 this.autoLevelCapping = Number.POSITIVE_INFINITY; 8852 self.clearInterval(this.timer); 8853 this.timer = self.setInterval(this.detectPlayerSize.bind(this), 1000); 8854 this.detectPlayerSize(); 8855 }; 8856 _proto.stopCapping = function stopCapping() { 8857 this.restrictedLevels = []; 8858 this.firstLevel = -1; 8859 this.autoLevelCapping = Number.POSITIVE_INFINITY; 8860 if (this.timer) { 8861 self.clearInterval(this.timer); 8862 this.timer = undefined; 8863 } 8864 }; 8865 _proto.getDimensions = function getDimensions() { 8866 if (this.clientRect) { 8867 return this.clientRect; 8868 } 8869 var media = this.media; 8870 var boundsRect = { 8871 width: 0, 8872 height: 0 8873 }; 8874 if (media) { 8875 var clientRect = media.getBoundingClientRect(); 8876 boundsRect.width = clientRect.width; 8877 boundsRect.height = clientRect.height; 8878 if (!boundsRect.width && !boundsRect.height) { 8879 // When the media element has no width or height (equivalent to not being in the DOM), 8880 // then use its width and height attributes (media.width, media.height) 8881 boundsRect.width = clientRect.right - clientRect.left || media.width || 0; 8882 boundsRect.height = clientRect.bottom - clientRect.top || media.height || 0; 8883 } 8884 } 8885 this.clientRect = boundsRect; 8886 return boundsRect; 8887 }; 8888 _proto.isLevelAllowed = function isLevelAllowed(level) { 8889 var restrictedLevels = this.restrictedLevels; 8890 return !restrictedLevels.some(function (restrictedLevel) { 8891 return level.bitrate === restrictedLevel.bitrate && level.width === restrictedLevel.width && level.height === restrictedLevel.height; 8892 }); 8893 }; 8894 CapLevelController.getMaxLevelByMediaSize = function getMaxLevelByMediaSize(levels, width, height) { 8895 if (!(levels != null && levels.length)) { 8896 return -1; 8897 } 8898 8899 // Levels can have the same dimensions but differing bandwidths - since levels are ordered, we can look to the next 8900 // to determine whether we've chosen the greatest bandwidth for the media's dimensions 8901 var atGreatestBandwidth = function atGreatestBandwidth(curLevel, nextLevel) { 8902 if (!nextLevel) { 8903 return true; 8904 } 8905 return curLevel.width !== nextLevel.width || curLevel.height !== nextLevel.height; 8906 }; 8907 8908 // If we run through the loop without breaking, the media's dimensions are greater than every level, so default to 8909 // the max level 8910 var maxLevelIndex = levels.length - 1; 8911 // Prevent changes in aspect-ratio from causing capping to toggle back and forth 8912 var squareSize = Math.max(width, height); 8913 for (var i = 0; i < levels.length; i += 1) { 8914 var level = levels[i]; 8915 if ((level.width >= squareSize || level.height >= squareSize) && atGreatestBandwidth(level, levels[i + 1])) { 8916 maxLevelIndex = i; 8917 break; 8918 } 8919 } 8920 return maxLevelIndex; 8921 }; 8922 return _createClass(CapLevelController, [{ 8923 key: "mediaWidth", 8924 get: function get() { 8925 return this.getDimensions().width * this.contentScaleFactor; 8926 } 8927 }, { 8928 key: "mediaHeight", 8929 get: function get() { 8930 return this.getDimensions().height * this.contentScaleFactor; 8931 } 8932 }, { 8933 key: "contentScaleFactor", 8934 get: function get() { 8935 var pixelRatio = 1; 8936 if (!this.hls.config.ignoreDevicePixelRatio) { 8937 try { 8938 pixelRatio = self.devicePixelRatio; 8939 } catch (e) { 8940 /* no-op */ 8941 } 8942 } 8943 return pixelRatio; 8944 } 8945 }]); 8946 }(); 8947 8948 var FPSController = /*#__PURE__*/function () { 8949 function FPSController(hls) { 8950 this.hls = void 0; 8951 this.isVideoPlaybackQualityAvailable = false;
vendor: 4,598 bytes, lines 8952-9061
8952 this.timer = void 0; 8953 this.media = null; 8954 this.lastTime = void 0; 8955 this.lastDroppedFrames = 0; 8956 this.lastDecodedFrames = 0; 8957 // stream controller must be provided as a dependency! 8958 this.streamController = void 0; 8959 this.hls = hls; 8960 this.registerListeners(); 8961 } 8962 var _proto = FPSController.prototype; 8963 _proto.setStreamController = function setStreamController(streamController) { 8964 this.streamController = streamController; 8965 }; 8966 _proto.registerListeners = function registerListeners() { 8967 this.hls.on(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); 8968 this.hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); 8969 }; 8970 _proto.unregisterListeners = function unregisterListeners() { 8971 this.hls.off(Events.MEDIA_ATTACHING, this.onMediaAttaching, this); 8972 this.hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); 8973 }; 8974 _proto.destroy = function destroy() { 8975 if (this.timer) { 8976 clearInterval(this.timer); 8977 } 8978 this.unregisterListeners(); 8979 this.isVideoPlaybackQualityAvailable = false; 8980 this.media = null; 8981 }; 8982 _proto.onMediaAttaching = function onMediaAttaching(event, data) { 8983 var config = this.hls.config; 8984 if (config.capLevelOnFPSDrop) { 8985 var media = data.media instanceof self.HTMLVideoElement ? data.media : null; 8986 this.media = media; 8987 if (media && typeof media.getVideoPlaybackQuality === 'function') { 8988 this.isVideoPlaybackQualityAvailable = true; 8989 } 8990 self.clearInterval(this.timer); 8991 this.timer = self.setInterval(this.checkFPSInterval.bind(this), config.fpsDroppedMonitoringPeriod); 8992 } 8993 }; 8994 _proto.onMediaDetaching = function onMediaDetaching() { 8995 this.media = null; 8996 }; 8997 _proto.checkFPS = function checkFPS(video, decodedFrames, droppedFrames) { 8998 var currentTime = performance.now(); 8999 if (decodedFrames) { 9000 if (this.lastTime) { 9001 var currentPeriod = currentTime - this.lastTime; 9002 var currentDropped = droppedFrames - this.lastDroppedFrames; 9003 var currentDecoded = decodedFrames - this.lastDecodedFrames; 9004 var droppedFPS = 1000 * currentDropped / currentPeriod; 9005 var hls = this.hls; 9006 hls.trigger(Events.FPS_DROP, { 9007 currentDropped: currentDropped, 9008 currentDecoded: currentDecoded, 9009 totalDroppedFrames: droppedFrames 9010 }); 9011 if (droppedFPS > 0) { 9012 // hls.logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod)); 9013 if (currentDropped > hls.config.fpsDroppedMonitoringThreshold * currentDecoded) { 9014 var currentLevel = hls.currentLevel; 9015 hls.logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel); 9016 if (currentLevel > 0 && (hls.autoLevelCapping === -1 || hls.autoLevelCapping >= currentLevel)) { 9017 currentLevel = currentLevel - 1; 9018 hls.trigger(Events.FPS_DROP_LEVEL_CAPPING, { 9019 level: currentLevel, 9020 droppedLevel: hls.currentLevel 9021 }); 9022 hls.autoLevelCapping = currentLevel; 9023 this.streamController.nextLevelSwitch(); 9024 } 9025 } 9026 } 9027 } 9028 this.lastTime = currentTime; 9029 this.lastDroppedFrames = droppedFrames; 9030 this.lastDecodedFrames = decodedFrames; 9031 } 9032 }; 9033 _proto.checkFPSInterval = function checkFPSInterval() { 9034 var video = this.media; 9035 if (video) { 9036 if (this.isVideoPlaybackQualityAvailable) { 9037 var videoPlaybackQuality = video.getVideoPlaybackQuality(); 9038 this.checkFPS(video, videoPlaybackQuality.totalVideoFrames, videoPlaybackQuality.droppedVideoFrames); 9039 } else { 9040 // HTMLVideoElement doesn't include the webkit types 9041 this.checkFPS(video, video.webkitDecodedFrameCount, video.webkitDroppedFrameCount); 9042 } 9043 } 9044 }; 9045 return FPSController; 9046 }(); 9047 9048 var PATHWAY_PENALTY_DURATION_MS = 300000; 9049 var ContentSteeringController = /*#__PURE__*/function (_Logger) { 9050 function ContentSteeringController(hls) { 9051 var _this; 9052 _this = _Logger.call(this, 'content-steering', hls.logger) || this; 9053 _this.hls = void 0; 9054 _this.loader = null; 9055 _this.uri = null; 9056 _this.pathwayId = '.'; 9057 _this._pathwayPriority = null; 9058 _this.timeToLoad = 300; 9059 _this.reloadTimer = -1; 9060 _this.updated = 0; 9061 _this.started = false;
vendor: 5,899 bytes, lines 9062-9212
9062 _this.enabled = true; 9063 _this.levels = null; 9064 _this.audioTracks = null; 9065 _this.subtitleTracks = null; 9066 _this.penalizedPathways = {}; 9067 _this.hls = hls; 9068 _this.registerListeners(); 9069 return _this; 9070 } 9071 _inheritsLoose(ContentSteeringController, _Logger); 9072 var _proto = ContentSteeringController.prototype; 9073 _proto.registerListeners = function registerListeners() { 9074 var hls = this.hls; 9075 hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); 9076 hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this); 9077 hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); 9078 hls.on(Events.ERROR, this.onError, this); 9079 }; 9080 _proto.unregisterListeners = function unregisterListeners() { 9081 var hls = this.hls; 9082 if (!hls) { 9083 return; 9084 } 9085 hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); 9086 hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this); 9087 hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); 9088 hls.off(Events.ERROR, this.onError, this); 9089 }; 9090 _proto.pathways = function pathways() { 9091 return (this.levels || []).reduce(function (pathways, level) { 9092 if (pathways.indexOf(level.pathwayId) === -1) { 9093 pathways.push(level.pathwayId); 9094 } 9095 return pathways; 9096 }, []); 9097 }; 9098 _proto.startLoad = function startLoad() { 9099 this.started = true; 9100 this.clearTimeout(); 9101 if (this.enabled && this.uri) { 9102 if (this.updated) { 9103 var ttl = this.timeToLoad * 1000 - (performance.now() - this.updated); 9104 if (ttl > 0) { 9105 this.scheduleRefresh(this.uri, ttl); 9106 return; 9107 } 9108 } 9109 this.loadSteeringManifest(this.uri); 9110 } 9111 }; 9112 _proto.stopLoad = function stopLoad() { 9113 this.started = false; 9114 if (this.loader) { 9115 this.loader.destroy(); 9116 this.loader = null; 9117 } 9118 this.clearTimeout(); 9119 }; 9120 _proto.clearTimeout = function clearTimeout() { 9121 if (this.reloadTimer !== -1) { 9122 self.clearTimeout(this.reloadTimer); 9123 this.reloadTimer = -1; 9124 } 9125 }; 9126 _proto.destroy = function destroy() { 9127 this.unregisterListeners(); 9128 this.stopLoad(); 9129 // @ts-ignore 9130 this.hls = null; 9131 this.levels = this.audioTracks = this.subtitleTracks = null; 9132 }; 9133 _proto.removeLevel = function removeLevel(levelToRemove) { 9134 var levels = this.levels; 9135 if (levels) { 9136 this.levels = levels.filter(function (level) { 9137 return level !== levelToRemove; 9138 }); 9139 } 9140 }; 9141 _proto.onManifestLoading = function onManifestLoading() { 9142 this.stopLoad(); 9143 this.enabled = true; 9144 this.timeToLoad = 300; 9145 this.updated = 0; 9146 this.uri = null; 9147 this.pathwayId = '.'; 9148 this.levels = this.audioTracks = this.subtitleTracks = null; 9149 }; 9150 _proto.onManifestLoaded = function onManifestLoaded(event, data) { 9151 var contentSteering = data.contentSteering; 9152 if (contentSteering === null) { 9153 return; 9154 } 9155 this.pathwayId = contentSteering.pathwayId; 9156 this.uri = contentSteering.uri; 9157 if (this.started) { 9158 this.startLoad(); 9159 } 9160 }; 9161 _proto.onManifestParsed = function onManifestParsed(event, data) { 9162 this.audioTracks = data.audioTracks; 9163 this.subtitleTracks = data.subtitleTracks; 9164 }; 9165 _proto.onError = function onError(event, data) { 9166 var errorAction = data.errorAction; 9167 if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox && errorAction.flags === ErrorActionFlags.MoveAllAlternatesMatchingHost) { 9168 var levels = this.levels; 9169 var pathwayPriority = this._pathwayPriority; 9170 var errorPathway = this.pathwayId; 9171 if (data.context) { 9172 var _data$context = data.context, 9173 groupId = _data$context.groupId, 9174 pathwayId = _data$context.pathwayId, 9175 type = _data$context.type; 9176 if (groupId && levels) { 9177 errorPathway = this.getPathwayForGroupId(groupId, type, errorPathway); 9178 } else if (pathwayId) { 9179 errorPathway = pathwayId; 9180 } 9181 } 9182 if (!(errorPathway in this.penalizedPathways)) { 9183 this.penalizedPathways[errorPathway] = performance.now(); 9184 } 9185 if (!pathwayPriority && levels) { 9186 // If PATHWAY-PRIORITY was not provided, list pathways for error handling 9187 pathwayPriority = this.pathways(); 9188 } 9189 if (pathwayPriority && pathwayPriority.length > 1) { 9190 this.updatePathwayPriority(pathwayPriority); 9191 errorAction.resolved = this.pathwayId !== errorPathway; 9192 } 9193 if (!errorAction.resolved) { 9194 this.warn("Could not resolve " + data.details + " (\"" + data.error.message + "\") with content-steering for Pathway: " + errorPathway + " levels: " + (levels ? levels.length : levels) + " priorities: " + JSON.stringify(pathwayPriority) + " penalized: " + JSON.stringify(this.penalizedPathways)); 9195 } 9196 } 9197 }; 9198 _proto.filterParsedLevels = function filterParsedLevels(levels) { 9199 // Filter levels to only include those that are in the initial pathway 9200 this.levels = levels; 9201 var pathwayLevels = this.getLevelsForPathway(this.pathwayId); 9202 if (pathwayLevels.length === 0) { 9203 var pathwayId = levels[0].pathwayId; 9204 this.log("No levels found in Pathway " + this.pathwayId + ". Setting initial Pathway to \"" + pathwayId + "\""); 9205 pathwayLevels = this.getLevelsForPathway(pathwayId); 9206 this.pathwayId = pathwayId; 9207 } 9208 if (pathwayLevels.length !== levels.length) { 9209 this.log("Found " + pathwayLevels.length + "/" + levels.length + " levels in Pathway \"" + this.pathwayId + "\""); 9210 return pathwayLevels; 9211 } 9212 return l
vendor: 11,507 bytes, lines 9212-9488
9212evels; 9213 }; 9214 _proto.getLevelsForPathway = function getLevelsForPathway(pathwayId) { 9215 if (this.levels === null) { 9216 return []; 9217 } 9218 return this.levels.filter(function (level) { 9219 return pathwayId === level.pathwayId; 9220 }); 9221 }; 9222 _proto.updatePathwayPriority = function updatePathwayPriority(pathwayPriority) { 9223 this._pathwayPriority = pathwayPriority; 9224 var levels; 9225 9226 // Evaluate if we should remove the pathway from the penalized list 9227 var penalizedPathways = this.penalizedPathways; 9228 var now = performance.now(); 9229 Object.keys(penalizedPathways).forEach(function (pathwayId) { 9230 if (now - penalizedPathways[pathwayId] > PATHWAY_PENALTY_DURATION_MS) { 9231 delete penalizedPathways[pathwayId]; 9232 } 9233 }); 9234 for (var i = 0; i < pathwayPriority.length; i++) { 9235 var pathwayId = pathwayPriority[i]; 9236 if (pathwayId in penalizedPathways) { 9237 continue; 9238 } 9239 if (pathwayId === this.pathwayId) { 9240 return; 9241 } 9242 var selectedIndex = this.hls.nextLoadLevel; 9243 var selectedLevel = this.hls.levels[selectedIndex]; 9244 levels = this.getLevelsForPathway(pathwayId); 9245 if (levels.length > 0) { 9246 this.log("Setting Pathway to \"" + pathwayId + "\""); 9247 this.pathwayId = pathwayId; 9248 reassignFragmentLevelIndexes(levels); 9249 this.hls.trigger(Events.LEVELS_UPDATED, { 9250 levels: levels 9251 }); 9252 // Set LevelController's level to trigger LEVEL_SWITCHING which loads playlist if needed 9253 var levelAfterChange = this.hls.levels[selectedIndex]; 9254 if (selectedLevel && levelAfterChange && this.levels) { 9255 if (levelAfterChange.attrs['STABLE-VARIANT-ID'] !== selectedLevel.attrs['STABLE-VARIANT-ID'] && levelAfterChange.bitrate !== selectedLevel.bitrate) { 9256 this.log("Unstable Pathways change from bitrate " + selectedLevel.bitrate + " to " + levelAfterChange.bitrate); 9257 } 9258 this.hls.nextLoadLevel = selectedIndex; 9259 } 9260 break; 9261 } 9262 } 9263 }; 9264 _proto.getPathwayForGroupId = function getPathwayForGroupId(groupId, type, defaultPathway) { 9265 var levels = this.getLevelsForPathway(defaultPathway).concat(this.levels || []); 9266 for (var i = 0; i < levels.length; i++) { 9267 if (type === PlaylistContextType.AUDIO_TRACK && levels[i].hasAudioGroup(groupId) || type === PlaylistContextType.SUBTITLE_TRACK && levels[i].hasSubtitleGroup(groupId)) { 9268 return levels[i].pathwayId; 9269 } 9270 } 9271 return defaultPathway; 9272 }; 9273 _proto.clonePathways = function clonePathways(pathwayClones) { 9274 var _this2 = this; 9275 var levels = this.levels; 9276 if (!levels) { 9277 return; 9278 } 9279 var audioGroupCloneMap = {}; 9280 var subtitleGroupCloneMap = {}; 9281 pathwayClones.forEach(function (pathwayClone) { 9282 var cloneId = pathwayClone.ID, 9283 baseId = pathwayClone['BASE-ID'], 9284 uriReplacement = pathwayClone['URI-REPLACEMENT']; 9285 if (levels.some(function (level) { 9286 return level.pathwayId === cloneId; 9287 })) { 9288 return; 9289 } 9290 var clonedVariants = _this2.getLevelsForPathway(baseId).map(function (baseLevel) { 9291 var attributes = new AttrList(baseLevel.attrs); 9292 attributes['PATHWAY-ID'] = cloneId; 9293 var clonedAudioGroupId = attributes.AUDIO && attributes.AUDIO + "_clone_" + cloneId; 9294 var clonedSubtitleGroupId = attributes.SUBTITLES && attributes.SUBTITLES + "_clone_" + cloneId; 9295 if (clonedAudioGroupId) { 9296 audioGroupCloneMap[attributes.AUDIO] = clonedAudioGroupId; 9297 attributes.AUDIO = clonedAudioGroupId; 9298 } 9299 if (clonedSubtitleGroupId) { 9300 subtitleGroupCloneMap[attributes.SUBTITLES] = clonedSubtitleGroupId; 9301 attributes.SUBTITLES = clonedSubtitleGroupId; 9302 } 9303 var url = performUriReplacement(baseLevel.uri, attributes['STABLE-VARIANT-ID'], 'PER-VARIANT-URIS', uriReplacement); 9304 var clonedLevel = new Level({ 9305 attrs: attributes, 9306 audioCodec: baseLevel.audioCodec, 9307 bitrate: baseLevel.bitrate, 9308 height: baseLevel.height, 9309 name: baseLevel.name, 9310 url: url, 9311 videoCodec: baseLevel.videoCodec, 9312 width: baseLevel.width 9313 }); 9314 if (baseLevel.audioGroups) { 9315 for (var i = 1; i < baseLevel.audioGroups.length; i++) { 9316 clonedLevel.addGroupId('audio', baseLevel.audioGroups[i] + "_clone_" + cloneId); 9317 } 9318 } 9319 if (baseLevel.subtitleGroups) { 9320 for (var _i = 1; _i < baseLevel.subtitleGroups.length; _i++) { 9321 clonedLevel.addGroupId('text', baseLevel.subtitleGroups[_i] + "_clone_" + cloneId); 9322 } 9323 } 9324 return clonedLevel; 9325 }); 9326 levels.push.apply(levels, clonedVariants); 9327 cloneRenditionGroups(_this2.audioTracks, audioGroupCloneMap, uriReplacement, cloneId); 9328 cloneRenditionGroups(_this2.subtitleTracks, subtitleGroupCloneMap, uriReplacement, cloneId); 9329 }); 9330 }; 9331 _proto.loadSteeringManifest = function loadSteeringManifest(uri) { 9332 var _this3 = this; 9333 var config = this.hls.config; 9334 var Loader = config.loader; 9335 if (this.loader) { 9336 this.loader.destroy(); 9337 } 9338 this.loader = new Loader(config); 9339 var url; 9340 try { 9341 url = new self.URL(uri); 9342 } catch (error) { 9343 this.enabled = false; 9344 this.log("Failed to parse Steering Manifest URI: " + uri); 9345 return; 9346 } 9347 if (url.protocol !== 'data:') { 9348 var throughput = (this.hls.bandwidthEstimate || config.abrEwmaDefaultEstimate) | 0; 9349 url.searchParams.set('_HLS_pathway', this.pathwayId); 9350 url.searchParams.set('_HLS_throughput', '' + throughput); 9351 } 9352 var context = { 9353 responseType: 'json', 9354 url: url.href 9355 }; 9356 var loadPolicy = config.steeringManifestLoadPolicy.default; 9357 var legacyRetryCompatibility = loadPolicy.errorRetry || loadPolicy.timeoutRetry || {}; 9358 var loaderConfig = { 9359 loadPolicy: loadPolicy, 9360 timeout: loadPolicy.maxLoadTimeMs, 9361 maxRetry: legacyRetryCompatibility.maxNumRetry || 0, 9362 retryDelay: legacyRetryCompatibility.retryDelayMs || 0, 9363 maxRetryDelay: legacyRetryCompatibility.maxRetryDelayMs || 0 9364 }; 9365 var callbacks = { 9366 onSuccess: function onSuccess(response, stats, context, networkDetails) { 9367 _this3.log("Loaded steering manifest: \"" + url + "\""); 9368 var steeringData = response.data; 9369 if ((steeringData == null ? void 0 : steeringData.VERSION) !== 1) { 9370 _this3.log("Steering VERSION " + steeringData.VERSION + " not supported!"); 9371 return; 9372 } 9373 _this3.updated = performance.now(); 9374 _this3.timeToLoad = steeringData.TTL; 9375 var reloadUri = steeringData['RELOAD-URI'], 9376 pathwayClones = steeringData['PATHWAY-CLONES'], 9377 pathwayPriority = steeringData['PATHWAY-PRIORITY']; 9378 if (reloadUri) { 9379 try { 9380 _this3.uri = new self.URL(reloadUri, url).href; 9381 } catch (error) { 9382 _this3.enabled = false; 9383 _this3.log("Failed to parse Steering Manifest RELOAD-URI: " + reloadUri); 9384 return; 9385 } 9386 } 9387 _this3.scheduleRefresh(_this3.uri || context.url); 9388 if (pathwayClones) { 9389 _this3.clonePathways(pathwayClones); 9390 } 9391 var loadedSteeringData = { 9392 steeringManifest: steeringData, 9393 url: url.toString() 9394 }; 9395 _this3.hls.trigger(Events.STEERING_MANIFEST_LOADED, loadedSteeringData); 9396 if (pathwayPriority) { 9397 _this3.updatePathwayPriority(pathwayPriority); 9398 } 9399 }, 9400 onError: function onError(error, context, networkDetails, stats) { 9401 _this3.log("Error loading steering manifest: " + error.code + " " + error.text + " (" + context.url + ")"); 9402 _this3.stopLoad(); 9403 if (error.code === 410) { 9404 _this3.enabled = false; 9405 _this3.log("Steering manifest " + context.url + " no longer available"); 9406 return; 9407 } 9408 var ttl = _this3.timeToLoad * 1000; 9409 if (error.code === 429) { 9410 var loader = _this3.loader; 9411 if (typeof (loader == null ? void 0 : loader.getResponseHeader) === 'function') { 9412 var retryAfter = loader.getResponseHeader('Retry-After'); 9413 if (retryAfter) { 9414 ttl = parseFloat(retryAfter) * 1000; 9415 } 9416 } 9417 _this3.log("Steering manifest " + context.url + " rate limited"); 9418 return; 9419 } 9420 _this3.scheduleRefresh(_this3.uri || context.url, ttl); 9421 }, 9422 onTimeout: function onTimeout(stats, context, networkDetails) { 9423 _this3.log("Timeout loading steering manifest (" + context.url + ")"); 9424 _this3.scheduleRefresh(_this3.uri || context.url); 9425 } 9426 }; 9427 this.log("Requesting steering manifest: " + url); 9428 this.loader.load(context, loaderConfig, callbacks); 9429 }; 9430 _proto.scheduleRefresh = function scheduleRefresh(uri, ttlMs) { 9431 var _this4 = this; 9432 if (ttlMs === void 0) { 9433 ttlMs = this.timeToLoad * 1000; 9434 } 9435 this.clearTimeout(); 9436 this.reloadTimer = self.setTimeout(function () { 9437 var _this4$hls; 9438 var media = (_this4$hls = _this4.hls) == null ? void 0 : _this4$hls.media; 9439 if (media && !media.ended) { 9440 _this4.loadSteeringManifest(uri); 9441 return; 9442 } 9443 _this4.scheduleRefresh(uri, _this4.timeToLoad * 1000); 9444 }, ttlMs); 9445 }; 9446 return _createClass(ContentSteeringController, [{ 9447 key: "pathwayPriority", 9448 get: function get() { 9449 return this._pathwayPriority; 9450 }, 9451 set: function set(pathwayPriority) { 9452 this.updatePathwayPriority(pathwayPriority); 9453 } 9454 }]); 9455 }(Logger); 9456 function cloneRenditionGroups(tracks, groupCloneMap, uriReplacement, cloneId) { 9457 if (!tracks) { 9458 return; 9459 } 9460 Object.keys(groupCloneMap).forEach(function (audioGroupId) { 9461 var clonedTracks = tracks.filter(function (track) { 9462 return track.groupId === audioGroupId; 9463 }).map(function (track) { 9464 var clonedTrack = _extends({}, track); 9465 clonedTrack.details = undefined; 9466 clonedTrack.attrs = new AttrList(clonedTrack.attrs); 9467 clonedTrack.url = clonedTrack.attrs.URI = performUriReplacement(track.url, track.attrs['STABLE-RENDITION-ID'], 'PER-RENDITION-URIS', uriReplacement); 9468 clonedTrack.groupId = clonedTrack.attrs['GROUP-ID'] = groupCloneMap[audioGroupId]; 9469 clonedTrack.attrs['PATHWAY-ID'] = cloneId; 9470 return clonedTrack; 9471 }); 9472 tracks.push.apply(tracks, clonedTracks); 9473 }); 9474 } 9475 function performUriReplacement(uri, stableId, perOptionKey, uriReplacement) { 9476 var host = uriReplacement.HOST, 9477 params = uriReplacement.PARAMS, 9478 perOptionUris = uriReplacement[perOptionKey]; 9479 var perVariantUri; 9480 if (stableId) { 9481 perVariantUri = perOptionUris == null ? void 0 : perOptionUris[stableId]; 9482 if (perVariantUri) { 9483 uri = perVariantUri; 9484 } 9485 } 9486 var url = new self.URL(uri); 9487 if (host && !perVariantUri) { 9488 url.host
vendor: 19,122 bytes, lines 9488-10005
9488 = host; 9489 } 9490 if (params) { 9491 Object.keys(params).sort().forEach(function (key) { 9492 if (key) { 9493 url.searchParams.set(key, params[key]); 9494 } 9495 }); 9496 } 9497 return url.href; 9498 } 9499 9500 var AGE_HEADER_LINE_REGEX = /^age:\s*[\d.]+\s*$/im; 9501 var XhrLoader = /*#__PURE__*/function () { 9502 function XhrLoader(config) { 9503 this.xhrSetup = void 0; 9504 this.requestTimeout = void 0; 9505 this.retryTimeout = void 0; 9506 this.retryDelay = void 0; 9507 this.config = null; 9508 this.callbacks = null; 9509 this.context = null; 9510 this.loader = null; 9511 this.stats = void 0; 9512 this.xhrSetup = config ? config.xhrSetup || null : null; 9513 this.stats = new LoadStats(); 9514 this.retryDelay = 0; 9515 } 9516 var _proto = XhrLoader.prototype; 9517 _proto.destroy = function destroy() { 9518 this.callbacks = null; 9519 this.abortInternal(); 9520 this.loader = null; 9521 this.config = null; 9522 this.context = null; 9523 this.xhrSetup = null; 9524 }; 9525 _proto.abortInternal = function abortInternal() { 9526 var loader = this.loader; 9527 self.clearTimeout(this.requestTimeout); 9528 self.clearTimeout(this.retryTimeout); 9529 if (loader) { 9530 loader.onreadystatechange = null; 9531 loader.onprogress = null; 9532 if (loader.readyState !== 4) { 9533 this.stats.aborted = true; 9534 loader.abort(); 9535 } 9536 } 9537 }; 9538 _proto.abort = function abort() { 9539 var _this$callbacks; 9540 this.abortInternal(); 9541 if ((_this$callbacks = this.callbacks) != null && _this$callbacks.onAbort) { 9542 this.callbacks.onAbort(this.stats, this.context, this.loader); 9543 } 9544 }; 9545 _proto.load = function load(context, config, callbacks) { 9546 if (this.stats.loading.start) { 9547 throw new Error('Loader can only be used once.'); 9548 } 9549 this.stats.loading.start = self.performance.now(); 9550 this.context = context; 9551 this.config = config; 9552 this.callbacks = callbacks; 9553 this.loadInternal(); 9554 }; 9555 _proto.loadInternal = function loadInternal() { 9556 var _this = this; 9557 var config = this.config, 9558 context = this.context; 9559 if (!config || !context) { 9560 return; 9561 } 9562 var xhr = this.loader = new self.XMLHttpRequest(); 9563 var stats = this.stats; 9564 stats.loading.first = 0; 9565 stats.loaded = 0; 9566 stats.aborted = false; 9567 var xhrSetup = this.xhrSetup; 9568 if (xhrSetup) { 9569 Promise.resolve().then(function () { 9570 if (_this.loader !== xhr || _this.stats.aborted) return; 9571 return xhrSetup(xhr, context.url); 9572 }).catch(function (error) { 9573 if (_this.loader !== xhr || _this.stats.aborted) return; 9574 xhr.open('GET', context.url, true); 9575 return xhrSetup(xhr, context.url); 9576 }).then(function () { 9577 if (_this.loader !== xhr || _this.stats.aborted) return; 9578 _this.openAndSendXhr(xhr, context, config); 9579 }).catch(function (error) { 9580 // IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS 9581 _this.callbacks.onError({ 9582 code: xhr.status, 9583 text: error.message 9584 }, context, xhr, stats); 9585 return; 9586 }); 9587 } else { 9588 this.openAndSendXhr(xhr, context, config); 9589 } 9590 }; 9591 _proto.openAndSendXhr = function openAndSendXhr(xhr, context, config) { 9592 if (!xhr.readyState) { 9593 xhr.open('GET', context.url, true); 9594 } 9595 var headers = context.headers; 9596 var _config$loadPolicy = config.loadPolicy, 9597 maxTimeToFirstByteMs = _config$loadPolicy.maxTimeToFirstByteMs, 9598 maxLoadTimeMs = _config$loadPolicy.maxLoadTimeMs; 9599 if (headers) { 9600 for (var header in headers) { 9601 xhr.setRequestHeader(header, headers[header]); 9602 } 9603 } 9604 if (context.rangeEnd) { 9605 xhr.setRequestHeader('Range', 'bytes=' + context.rangeStart + '-' + (context.rangeEnd - 1)); 9606 } 9607 xhr.onreadystatechange = this.readystatechange.bind(this); 9608 xhr.onprogress = this.loadprogress.bind(this); 9609 xhr.responseType = context.responseType; 9610 // setup timeout before we perform request 9611 self.clearTimeout(this.requestTimeout); 9612 config.timeout = maxTimeToFirstByteMs && isFiniteNumber(maxTimeToFirstByteMs) ? maxTimeToFirstByteMs : maxLoadTimeMs; 9613 this.requestTimeout = self.setTimeout(this.loadtimeout.bind(this), config.timeout); 9614 xhr.send(); 9615 }; 9616 _proto.readystatechange = function readystatechange() { 9617 var context = this.context, 9618 xhr = this.loader, 9619 stats = this.stats; 9620 if (!context || !xhr) { 9621 return; 9622 } 9623 var readyState = xhr.readyState; 9624 var config = this.config; 9625 9626 // don't proceed if xhr has been aborted 9627 if (stats.aborted) { 9628 return; 9629 } 9630 9631 // >= HEADERS_RECEIVED 9632 if (readyState >= 2) { 9633 if (stats.loading.first === 0) { 9634 stats.loading.first = Math.max(self.performance.now(), stats.loading.start); 9635 // readyState >= 2 AND readyState !==4 (readyState = HEADERS_RECEIVED || LOADING) rearm timeout as xhr not finished yet 9636 if (config.timeout !== config.loadPolicy.maxLoadTimeMs) { 9637 self.clearTimeout(this.requestTimeout); 9638 config.timeout = config.loadPolicy.maxLoadTimeMs; 9639 this.requestTimeout = self.setTimeout(this.loadtimeout.bind(this), config.loadPolicy.maxLoadTimeMs - (stats.loading.first - stats.loading.start)); 9640 } 9641 } 9642 if (readyState === 4) { 9643 self.clearTimeout(this.requestTimeout); 9644 xhr.onreadystatechange = null; 9645 xhr.onprogress = null; 9646 var _status = xhr.status; 9647 // http status between 200 to 299 are all successful 9648 var useResponse = xhr.responseType !== 'text'; 9649 if (_status >= 200 && _status < 300 && (useResponse && xhr.response || xhr.responseText !== null)) { 9650 stats.loading.end = Math.max(self.performance.now(), stats.loading.first); 9651 var data = useResponse ? xhr.response : xhr.responseText; 9652 var len = xhr.responseType === 'arraybuffer' ? data.byteLength : data.length; 9653 stats.loaded = stats.total = len; 9654 stats.bwEstimate = stats.total * 8000 / (stats.loading.end - stats.loading.first); 9655 if (!this.callbacks) { 9656 return; 9657 } 9658 var onProgress = this.callbacks.onProgress; 9659 if (onProgress) { 9660 onProgress(stats, context, data, xhr); 9661 } 9662 if (!this.callbacks) { 9663 return; 9664 } 9665 var response = { 9666 url: xhr.responseURL, 9667 data: data, 9668 code: _status 9669 }; 9670 this.callbacks.onSuccess(response, stats, context, xhr); 9671 } else { 9672 var retryConfig = config.loadPolicy.errorRetry; 9673 var retryCount = stats.retry; 9674 // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error 9675 var _response = { 9676 url: context.url, 9677 data: undefined, 9678 code: _status 9679 }; 9680 if (shouldRetry(retryConfig, retryCount, false, _response)) { 9681 this.retry(retryConfig); 9682 } else { 9683 logger.error(_status + " while loading " + context.url); 9684 this.callbacks.onError({ 9685 code: _status, 9686 text: xhr.statusText 9687 }, context, xhr, stats); 9688 } 9689 } 9690 } 9691 } 9692 }; 9693 _proto.loadtimeout = function loadtimeout() { 9694 if (!this.config) return; 9695 var retryConfig = this.config.loadPolicy.timeoutRetry; 9696 var retryCount = this.stats.retry; 9697 if (shouldRetry(retryConfig, retryCount, true)) { 9698 this.retry(retryConfig); 9699 } else { 9700 var _this$context; 9701 logger.warn("timeout while loading " + ((_this$context = this.context) == null ? void 0 : _this$context.url)); 9702 var callbacks = this.callbacks; 9703 if (callbacks) { 9704 this.abortInternal(); 9705 callbacks.onTimeout(this.stats, this.context, this.loader); 9706 } 9707 } 9708 }; 9709 _proto.retry = function retry(retryConfig) { 9710 var context = this.context, 9711 stats = this.stats; 9712 this.retryDelay = getRetryDelay(retryConfig, stats.retry); 9713 stats.retry++; 9714 logger.warn((status ? 'HTTP Status ' + status : 'Timeout') + " while loading " + (context == null ? void 0 : context.url) + ", retrying " + stats.retry + "/" + retryConfig.maxNumRetry + " in " + this.retryDelay + "ms"); 9715 // abort and reset internal state 9716 this.abortInternal(); 9717 this.loader = null; 9718 // schedule retry 9719 self.clearTimeout(this.retryTimeout); 9720 this.retryTimeout = self.setTimeout(this.loadInternal.bind(this), this.retryDelay); 9721 }; 9722 _proto.loadprogress = function loadprogress(event) { 9723 var stats = this.stats; 9724 stats.loaded = event.loaded; 9725 if (event.lengthComputable) { 9726 stats.total = event.total; 9727 } 9728 }; 9729 _proto.getCacheAge = function getCacheAge() { 9730 var result = null; 9731 if (this.loader && AGE_HEADER_LINE_REGEX.test(this.loader.getAllResponseHeaders())) { 9732 var ageHeader = this.loader.getResponseHeader('age'); 9733 result = ageHeader ? parseFloat(ageHeader) : null; 9734 } 9735 return result; 9736 }; 9737 _proto.getResponseHeader = function getResponseHeader(name) { 9738 if (this.loader && new RegExp("^" + name + ":\\s*[\\d.]+\\s*$", 'im').test(this.loader.getAllResponseHeaders())) { 9739 return this.loader.getResponseHeader(name); 9740 } 9741 return null; 9742 }; 9743 return XhrLoader; 9744 }(); 9745 9746 var ChunkCache = /*#__PURE__*/function () { 9747 function ChunkCache() { 9748 this.chunks = []; 9749 this.dataLength = 0; 9750 } 9751 var _proto = ChunkCache.prototype; 9752 _proto.push = function push(chunk) { 9753 this.chunks.push(chunk); 9754 this.dataLength += chunk.length; 9755 }; 9756 _proto.flush = function flush() { 9757 var chunks = this.chunks, 9758 dataLength = this.dataLength; 9759 var result; 9760 if (!chunks.length) { 9761 return new Uint8Array(0); 9762 } else if (chunks.length === 1) { 9763 result = chunks[0]; 9764 } else { 9765 result = concatUint8Arrays(chunks, dataLength); 9766 } 9767 this.reset(); 9768 return result; 9769 }; 9770 _proto.reset = function reset() { 9771 this.chunks.length = 0; 9772 this.dataLength = 0; 9773 }; 9774 return ChunkCache; 9775 }(); 9776 function concatUint8Arrays(chunks, dataLength) { 9777 var result = new Uint8Array(dataLength); 9778 var offset = 0; 9779 for (var i = 0; i < chunks.length; i++) { 9780 var chunk = chunks[i]; 9781 result.set(chunk, offset); 9782 offset += chunk.length; 9783 } 9784 return result; 9785 } 9786 9787 function fetchSupported() { 9788 if ( 9789 // @ts-ignore 9790 self.fetch && self.AbortController && self.ReadableStream && self.Request) { 9791 try { 9792 new self.ReadableStream({}); // eslint-disable-line no-new 9793 return true; 9794 } catch (e) { 9795 /* noop */ 9796 } 9797 } 9798 return false; 9799 } 9800 var BYTERANGE = /(\d+)-(\d+)\/(\d+)/; 9801 var FetchLoader = /*#__PURE__*/function () { 9802 function FetchLoader(config /* HlsConfig */) { 9803 this.fetchSetup = void 0; 9804 this.requestTimeout = void 0; 9805 this.request = null; 9806 this.response = null; 9807 this.controller = void 0; 9808 this.context = null; 9809 this.config = null; 9810 this.callbacks = null; 9811 this.stats = void 0; 9812 this.loader = null; 9813 this.fetchSetup = config.fetchSetup || getRequest; 9814 this.controller = new self.AbortController(); 9815 this.stats = new LoadStats(); 9816 } 9817 var _proto = FetchLoader.prototype; 9818 _proto.destroy = function destroy() { 9819 this.loader = this.callbacks = this.context = this.config = this.request = null; 9820 this.abortInternal(); 9821 this.response = null; 9822 // @ts-ignore 9823 this.fetchSetup = this.controller = this.stats = null; 9824 }; 9825 _proto.abortInternal = function abortInternal() { 9826 if (this.controller && !this.stats.loading.end) { 9827 this.stats.aborted = true; 9828 this.controller.abort(); 9829 } 9830 }; 9831 _proto.abort = function abort() { 9832 var _this$callbacks; 9833 this.abortInternal(); 9834 if ((_this$callbacks = this.callbacks) != null && _this$callbacks.onAbort) { 9835 this.callbacks.onAbort(this.stats, this.context, this.response); 9836 } 9837 }; 9838 _proto.load = function load(context, config, callbacks) { 9839 var _this = this; 9840 var stats = this.stats; 9841 if (stats.loading.start) { 9842 throw new Error('Loader can only be used once.'); 9843 } 9844 stats.loading.start = self.performance.now(); 9845 var initParams = getRequestParameters(context, this.controller.signal); 9846 var onProgress = callbacks.onProgress; 9847 var isArrayBuffer = context.responseType === 'arraybuffer'; 9848 var LENGTH = isArrayBuffer ? 'byteLength' : 'length'; 9849 var _config$loadPolicy = config.loadPolicy, 9850 maxTimeToFirstByteMs = _config$loadPolicy.maxTimeToFirstByteMs, 9851 maxLoadTimeMs = _config$loadPolicy.maxLoadTimeMs; 9852 this.context = context; 9853 this.config = config; 9854 this.callbacks = callbacks; 9855 this.request = this.fetchSetup(context, initParams); 9856 self.clearTimeout(this.requestTimeout); 9857 config.timeout = maxTimeToFirstByteMs && isFiniteNumber(maxTimeToFirstByteMs) ? maxTimeToFirstByteMs : maxLoadTimeMs; 9858 this.requestTimeout = self.setTimeout(function () { 9859 _this.abortInternal(); 9860 callbacks.onTimeout(stats, context, _this.response); 9861 }, config.timeout); 9862 self.fetch(this.request).then(function (response) { 9863 _this.response = _this.loader = response; 9864 var first = Math.max(self.performance.now(), stats.loading.start); 9865 self.clearTimeout(_this.requestTimeout); 9866 config.timeout = maxLoadTimeMs; 9867 _this.requestTimeout = self.setTimeout(function () { 9868 _this.abortInternal(); 9869 callbacks.onTimeout(stats, context, _this.response); 9870 }, maxLoadTimeMs - (first - stats.loading.start)); 9871 if (!response.ok) { 9872 var status = response.status, 9873 statusText = response.statusText; 9874 throw new FetchError(statusText || 'fetch, bad network response', status, response); 9875 } 9876 stats.loading.first = first; 9877 stats.total = getContentLength(response.headers) || stats.total; 9878 if (onProgress && isFiniteNumber(config.highWaterMark)) { 9879 return _this.loadProgressively(response, stats, context, config.highWaterMark, onProgress); 9880 } 9881 if (isArrayBuffer) { 9882 return response.arrayBuffer(); 9883 } 9884 if (context.responseType === 'json') { 9885 return response.json(); 9886 } 9887 return response.text(); 9888 }).then(function (responseData) { 9889 var response = _this.response; 9890 if (!response) { 9891 throw new Error('loader destroyed'); 9892 } 9893 self.clearTimeout(_this.requestTimeout); 9894 stats.loading.end = Math.max(self.performance.now(), stats.loading.first); 9895 var total = responseData[LENGTH]; 9896 if (total) { 9897 stats.loaded = stats.total = total; 9898 } 9899 var loaderResponse = { 9900 url: response.url, 9901 data: responseData, 9902 code: response.status 9903 }; 9904 if (onProgress && !isFiniteNumber(config.highWaterMark)) { 9905 onProgress(stats, context, responseData, response); 9906 } 9907 callbacks.onSuccess(loaderResponse, stats, context, response); 9908 }).catch(function (error) { 9909 self.clearTimeout(_this.requestTimeout); 9910 if (stats.aborted) { 9911 return; 9912 } 9913 // CORS errors result in an undefined code. Set it to 0 here to align with XHR's behavior 9914 // when destroying, 'error' itself can be undefined 9915 var code = !error ? 0 : error.code || 0; 9916 var text = !error ? null : error.message; 9917 callbacks.onError({ 9918 code: code, 9919 text: text 9920 }, context, error ? error.details : null, stats); 9921 }); 9922 }; 9923 _proto.getCacheAge = function getCacheAge() { 9924 var result = null; 9925 if (this.response) { 9926 var ageHeader = this.response.headers.get('age'); 9927 result = ageHeader ? parseFloat(ageHeader) : null; 9928 } 9929 return result; 9930 }; 9931 _proto.getResponseHeader = function getResponseHeader(name) { 9932 return this.response ? this.response.headers.get(name) : null; 9933 }; 9934 _proto.loadProgressively = function loadProgressively(response, stats, context, highWaterMark, onProgress) { 9935 if (highWaterMark === void 0) { 9936 highWaterMark = 0; 9937 } 9938 var chunkCache = new ChunkCache(); 9939 var reader = response.body.getReader(); 9940 var _pump = function pump() { 9941 return reader.read().then(function (data) { 9942 if (data.done) { 9943 if (chunkCache.dataLength) { 9944 onProgress(stats, context, chunkCache.flush(), response); 9945 } 9946 return Promise.resolve(new ArrayBuffer(0)); 9947 } 9948 var chunk = data.value; 9949 var len = chunk.length; 9950 stats.loaded += len; 9951 if (len < highWaterMark || chunkCache.dataLength) { 9952 // The current chunk is too small to to be emitted or the cache already has data 9953 // Push it to the cache 9954 chunkCache.push(chunk); 9955 if (chunkCache.dataLength >= highWaterMark) { 9956 // flush in order to join the typed arrays 9957 onProgress(stats, context, chunkCache.flush(), response); 9958 } 9959 } else { 9960 // If there's nothing cached already, and the chache is large enough 9961 // just emit the progress event 9962 onProgress(stats, context, chunk, response); 9963 } 9964 return _pump(); 9965 }).catch(function () { 9966 /* aborted */ 9967 return Promise.reject(); 9968 }); 9969 }; 9970 return _pump(); 9971 }; 9972 return FetchLoader; 9973 }(); 9974 function getRequestParameters(context, signal) { 9975 var initParams = { 9976 method: 'GET', 9977 mode: 'cors', 9978 credentials: 'same-origin', 9979 signal: signal, 9980 headers: new self.Headers(_extends({}, context.headers)) 9981 }; 9982 if (context.rangeEnd) { 9983 initParams.headers.set('Range', 'bytes=' + context.rangeStart + '-' + String(context.rangeEnd - 1)); 9984 } 9985 return initParams; 9986 } 9987 function getByteRangeLength(byteRangeHeader) { 9988 var result = BYTERANGE.exec(byteRangeHeader); 9989 if (result) { 9990 return parseInt(result[2]) - parseInt(result[1]) + 1; 9991 } 9992 } 9993 function getContentLength(headers) { 9994 var contentRange = headers.get('Content-Range'); 9995 if (contentRange) { 9996 var byteRangeLength = getByteRangeLength(contentRange); 9997 if (isFiniteNumber(byteRangeLength)) { 9998 return byteRangeLength; 9999 } 10000 } 10001 var contentLength = headers.get('Content-Length'); 10002 if (contentLength) { 10003 return parseInt(contentLength); 10004 } 10005 }
vendor: 5,236 bytes, lines 10006-10180
10006 function getRequest(context, initParams) { 10007 return new self.Request(context.url, initParams); 10008 } 10009 var FetchError = /*#__PURE__*/function (_Error) { 10010 function FetchError(message, code, details) { 10011 var _this2; 10012 _this2 = _Error.call(this, message) || this; 10013 _this2.code = void 0; 10014 _this2.details = void 0; 10015 _this2.code = code; 10016 _this2.details = details; 10017 return _this2; 10018 } 10019 _inheritsLoose(FetchError, _Error); 10020 return FetchError; 10021 }( /*#__PURE__*/_wrapNativeSuper(Error)); 10022 10023 /** 10024 * @deprecated use fragLoadPolicy.default 10025 */ 10026 10027 /** 10028 * @deprecated use manifestLoadPolicy.default and playlistLoadPolicy.default 10029 */ 10030 10031 var defaultLoadPolicy = { 10032 maxTimeToFirstByteMs: 8000, 10033 maxLoadTimeMs: 20000, 10034 timeoutRetry: null, 10035 errorRetry: null 10036 }; 10037 10038 /** 10039 * @ignore 10040 * If possible, keep hlsDefaultConfig shallow 10041 * It is cloned whenever a new Hls instance is created, by keeping the config 10042 * shallow the properties are cloned, and we don't end up manipulating the default 10043 */ 10044 var hlsDefaultConfig = _objectSpread2(_objectSpread2({ 10045 autoStartLoad: true, 10046 // used by stream-controller 10047 startPosition: -1, 10048 // used by stream-controller 10049 defaultAudioCodec: undefined, 10050 // used by stream-controller 10051 debug: false, 10052 // used by logger 10053 capLevelOnFPSDrop: false, 10054 // used by fps-controller 10055 capLevelToPlayerSize: false, 10056 // used by cap-level-controller 10057 ignoreDevicePixelRatio: false, 10058 // used by cap-level-controller 10059 preferManagedMediaSource: true, 10060 initialLiveManifestSize: 1, 10061 // used by stream-controller 10062 maxBufferLength: 30, 10063 // used by stream-controller 10064 backBufferLength: Infinity, 10065 // used by buffer-controller 10066 frontBufferFlushThreshold: Infinity, 10067 maxBufferSize: 60 * 1000 * 1000, 10068 // used by stream-controller 10069 maxBufferHole: 0.1, 10070 // used by stream-controller 10071 highBufferWatchdogPeriod: 2, 10072 // used by stream-controller 10073 nudgeOffset: 0.1, 10074 // used by stream-controller 10075 nudgeMaxRetry: 3, 10076 // used by stream-controller 10077 maxFragLookUpTolerance: 0.25, 10078 // used by stream-controller 10079 liveSyncDurationCount: 3, 10080 // used by latency-controller 10081 liveSyncOnStallIncrease: 1, 10082 // used by latency-controller 10083 liveMaxLatencyDurationCount: Infinity, 10084 // used by latency-controller 10085 liveSyncDuration: undefined, 10086 // used by latency-controller 10087 liveMaxLatencyDuration: undefined, 10088 // used by latency-controller 10089 maxLiveSyncPlaybackRate: 1, 10090 // used by latency-controller 10091 liveDurationInfinity: false, 10092 // used by buffer-controller 10093 /** 10094 * @deprecated use backBufferLength 10095 */ 10096 liveBackBufferLength: null, 10097 // used by buffer-controller 10098 maxMaxBufferLength: 600, 10099 // used by stream-controller 10100 enableWorker: true, 10101 // used by transmuxer 10102 workerPath: null, 10103 // used by transmuxer 10104 enableSoftwareAES: true, 10105 // used by decrypter 10106 startLevel: undefined, 10107 // used by level-controller 10108 startFragPrefetch: false, 10109 // used by stream-controller 10110 fpsDroppedMonitoringPeriod: 5000, 10111 // used by fps-controller 10112 fpsDroppedMonitoringThreshold: 0.2, 10113 // used by fps-controller 10114 appendErrorMaxRetry: 3, 10115 // used by buffer-controller 10116 loader: XhrLoader, 10117 // loader: FetchLoader, 10118 fLoader: undefined, 10119 // used by fragment-loader 10120 pLoader: undefined, 10121 // used by playlist-loader 10122 xhrSetup: undefined, 10123 // used by xhr-loader 10124 licenseXhrSetup: undefined, 10125 // used by eme-controller 10126 licenseResponseCallback: undefined, 10127 // used by eme-controller 10128 abrController: AbrController, 10129 bufferController: BufferController, 10130 capLevelController: CapLevelController, 10131 errorController: ErrorController, 10132 fpsController: FPSController, 10133 stretchShortVideoTrack: false, 10134 // used by mp4-remuxer 10135 maxAudioFramesDrift: 1, 10136 // used by mp4-remuxer 10137 forceKeyFrameOnDiscontinuity: true, 10138 // used by ts-demuxer 10139 abrEwmaFastLive: 3, 10140 // used by abr-controller 10141 abrEwmaSlowLive: 9, 10142 // used by abr-controller 10143 abrEwmaFastVoD: 3, 10144 // used by abr-controller 10145 abrEwmaSlowVoD: 9, 10146 // used by abr-controller 10147 abrEwmaDefaultEstimate: 5e5, 10148 // 500 kbps // used by abr-controller 10149 abrEwmaDefaultEstimateMax: 5e6, 10150 // 5 mbps 10151 abrBandWidthFactor: 0.95, 10152 // used by abr-controller 10153 abrBandWidthUpFactor: 0.7, 10154 // used by abr-controller 10155 abrMaxWithRealBitrate: false, 10156 // used by abr-controller 10157 maxStarvationDelay: 4, 10158 // used by abr-controller 10159 maxLoadingDelay: 4, 10160 // used by abr-controller 10161 minAutoBitrate: 0, 10162 // used by hls 10163 emeEnabled: false, 10164 // used by eme-controller 10165 widevineLicenseUrl: undefined, 10166 // used by eme-controller 10167 drmSystems: {}, 10168 // used by eme-controller 10169 drmSystemOptions: {}, 10170 // used by eme-controller 10171 requestMediaKeySystemAccessFunc: null, 10172 // used by eme-controller 10173 testBandwidth: true, 10174 progressive: false, 10175 lowLatencyMode: true, 10176 cmcd: undefined, 10177 enableDateRangeMetadataCues: true, 10178 enableEmsgMetadataCues: true, 10179 enableID3MetadataCues: true, 10180 useMediaCapabilities: false,
10181 certLoadPolicy: { 10182 default: defaultLoadPolicy 10183 }, 10184 keyLoadPolicy: { 10185 default: { 10186 maxTimeToFirstByteMs: 8000, 10187 maxLoadTimeMs: 20000, 10188 timeoutRetry: { 10189 maxNumRetry: 1, 10190 retryDelayMs: 1000, 10191 maxRetryDelayMs: 20000, 10192 backoff: 'linear' 10193 }, 10194 errorRetry: { 10195 maxNumRetry: 8, 10196 retryDelayMs: 1000, 10197 maxRetryDelayMs: 20000, 10198 backoff: 'linear' 10199 } 10200 } 10201 }, 10202 manifestLoadPolicy: { 10203 default: { 10204 maxTimeToFirstByteMs: Infinity, 10205 maxLoadTimeMs: 20000, 10206 timeoutRetry: { 10207 maxNumRetry: 2, 10208 retryDelayMs: 0, 10209 maxRetryDelayMs: 0 10210 }, 10211 errorRetry: { 10212 maxNumRetry: 1, 10213 retryDelayMs: 1000, 10214 maxRetryDelayMs: 8000 10215 } 10216 } 10217 }, 10218 playlistLoadPolicy: { 10219 default: { 10220 maxTimeToFirstByteMs: 10000, 10221 maxLoadTimeMs: 20000, 10222 timeoutRetry: { 10223 maxNumRetry: 2, 10224 retryDelayMs: 0, 10225 maxRetryDelayMs: 0 10226 }, 10227 errorRetry: { 10228 maxNumRetry: 2, 10229 retryDelayMs: 1000, 10230 maxRetryDelayMs: 8000 10231 } 10232 } 10233 }, 10234 fragLoadPolicy: { 10235 default: { 10236 maxTimeToFirstByteMs: 10000, 10237 maxLoadTimeMs: 120000, 10238 timeoutRetry: { 10239 maxNumRetry: 4, 10240 retryDelayMs: 0, 10241 maxRetryDelayMs: 0 10242 }, 10243 errorRetry: { 10244 maxNumRetry: 6, 10245 retryDelayMs: 1000, 10246 maxRetryDelayMs: 8000 10247 } 10248 } 10249 }, 10250 steeringManifestLoadPolicy: { 10251 default: { 10252 maxTimeToFirstByteMs: 10000, 10253 maxLoadTimeMs: 20000, 10254 timeoutRetry: { 10255 maxNumRetry: 2, 10256 retryDelayMs: 0, 10257 maxRetryDelayMs: 0 10258 }, 10259 errorRetry: { 10260 maxNumRetry: 1, 10261 retryDelayMs: 1000, 10262 maxRetryDelayMs: 8000 10263 } 10264 } 10265 },
vendor: 4,717 bytes, lines 10266-10374
10266 // These default settings are deprecated in favor of the above policies 10267 // and are maintained for backwards compatibility 10268 manifestLoadingTimeOut: 10000, 10269 manifestLoadingMaxRetry: 1, 10270 manifestLoadingRetryDelay: 1000, 10271 manifestLoadingMaxRetryTimeout: 64000, 10272 levelLoadingTimeOut: 10000, 10273 levelLoadingMaxRetry: 4, 10274 levelLoadingRetryDelay: 1000, 10275 levelLoadingMaxRetryTimeout: 64000, 10276 fragLoadingTimeOut: 20000, 10277 fragLoadingMaxRetry: 6, 10278 fragLoadingRetryDelay: 1000, 10279 fragLoadingMaxRetryTimeout: 64000 10280 }, timelineConfig()), {}, { 10281 subtitleStreamController: undefined, 10282 subtitleTrackController: undefined, 10283 timelineController: undefined, 10284 audioStreamController: undefined, 10285 audioTrackController: undefined, 10286 emeController: undefined, 10287 cmcdController: undefined, 10288 contentSteeringController: ContentSteeringController 10289 }); 10290 function timelineConfig() { 10291 return { 10292 cueHandler: HevcVideoParser, 10293 // used by timeline-controller 10294 enableWebVTT: false, 10295 // used by timeline-controller 10296 enableIMSC1: false, 10297 // used by timeline-controller 10298 enableCEA708Captions: false, 10299 // used by timeline-controller 10300 captionsTextTrack1Label: 'English', 10301 // used by timeline-controller 10302 captionsTextTrack1LanguageCode: 'en', 10303 // used by timeline-controller 10304 captionsTextTrack2Label: 'Spanish', 10305 // used by timeline-controller 10306 captionsTextTrack2LanguageCode: 'es', 10307 // used by timeline-controller 10308 captionsTextTrack3Label: 'Unknown CC', 10309 // used by timeline-controller 10310 captionsTextTrack3LanguageCode: '', 10311 // used by timeline-controller 10312 captionsTextTrack4Label: 'Unknown CC', 10313 // used by timeline-controller 10314 captionsTextTrack4LanguageCode: '', 10315 // used by timeline-controller 10316 renderTextTracksNatively: true 10317 }; 10318 } 10319 10320 /** 10321 * @ignore 10322 */ 10323 function mergeConfig(defaultConfig, userConfig, logger) { 10324 if ((userConfig.liveSyncDurationCount || userConfig.liveMaxLatencyDurationCount) && (userConfig.liveSyncDuration || userConfig.liveMaxLatencyDuration)) { 10325 throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration"); 10326 } 10327 if (userConfig.liveMaxLatencyDurationCount !== undefined && (userConfig.liveSyncDurationCount === undefined || userConfig.liveMaxLatencyDurationCount <= userConfig.liveSyncDurationCount)) { 10328 throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be greater than "liveSyncDurationCount"'); 10329 } 10330 if (userConfig.liveMaxLatencyDuration !== undefined && (userConfig.liveSyncDuration === undefined || userConfig.liveMaxLatencyDuration <= userConfig.liveSyncDuration)) { 10331 throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be greater than "liveSyncDuration"'); 10332 } 10333 var defaultsCopy = deepCpy(defaultConfig); 10334 10335 // Backwards compatibility with deprecated config values 10336 var deprecatedSettingTypes = ['manifest', 'level', 'frag']; 10337 var deprecatedSettings = ['TimeOut', 'MaxRetry', 'RetryDelay', 'MaxRetryTimeout']; 10338 deprecatedSettingTypes.forEach(function (type) { 10339 var policyName = (type === 'level' ? 'playlist' : type) + "LoadPolicy"; 10340 var policyNotSet = userConfig[policyName] === undefined; 10341 var report = []; 10342 deprecatedSettings.forEach(function (setting) { 10343 var deprecatedSetting = type + "Loading" + setting; 10344 var value = userConfig[deprecatedSetting]; 10345 if (value !== undefined && policyNotSet) { 10346 report.push(deprecatedSetting); 10347 var settings = defaultsCopy[policyName].default; 10348 userConfig[policyName] = { 10349 default: settings 10350 }; 10351 switch (setting) { 10352 case 'TimeOut': 10353 settings.maxLoadTimeMs = value; 10354 settings.maxTimeToFirstByteMs = value; 10355 break; 10356 case 'MaxRetry': 10357 settings.errorRetry.maxNumRetry = value; 10358 settings.timeoutRetry.maxNumRetry = value; 10359 break; 10360 case 'RetryDelay': 10361 settings.errorRetry.retryDelayMs = value; 10362 settings.timeoutRetry.retryDelayMs = value; 10363 break; 10364 case 'MaxRetryTimeout': 10365 settings.errorRetry.maxRetryDelayMs = value; 10366 settings.timeoutRetry.maxRetryDelayMs = value; 10367 break; 10368 } 10369 } 10370 }); 10371 if (report.length) { 10372 logger.warn("hls.js config: \"" + report.join('", "') + "\" setting(s) are deprecated, use \"" + policyName + "\": " + JSON.stringify(userConfig[policyName])); 10373 } 10374 });
vendor: 5,169 bytes, lines 10375-10502
10375 return _objectSpread2(_objectSpread2({}, defaultsCopy), userConfig); 10376 } 10377 function deepCpy(obj) { 10378 if (obj && typeof obj === 'object') { 10379 if (Array.isArray(obj)) { 10380 return obj.map(deepCpy); 10381 } 10382 return Object.keys(obj).reduce(function (result, key) { 10383 result[key] = deepCpy(obj[key]); 10384 return result; 10385 }, {}); 10386 } 10387 return obj; 10388 } 10389 10390 /** 10391 * @ignore 10392 */ 10393 function enableStreamingMode(config, logger) { 10394 var currentLoader = config.loader; 10395 if (currentLoader !== FetchLoader && currentLoader !== XhrLoader) { 10396 // If a developer has configured their own loader, respect that choice 10397 logger.log('[config]: Custom loader detected, cannot enable progressive streaming'); 10398 config.progressive = false; 10399 } else { 10400 var canStreamProgressively = fetchSupported(); 10401 if (canStreamProgressively) { 10402 config.loader = FetchLoader; 10403 config.progressive = true; 10404 config.enableSoftwareAES = true; 10405 logger.log('[config]: Progressive streaming enabled, using FetchLoader'); 10406 } 10407 } 10408 } 10409 10410 var LevelController = /*#__PURE__*/function (_BasePlaylistControll) { 10411 function LevelController(hls, contentSteeringController) { 10412 var _this; 10413 _this = _BasePlaylistControll.call(this, hls, 'level-controller') || this; 10414 _this._levels = []; 10415 _this._firstLevel = -1; 10416 _this._maxAutoLevel = -1; 10417 _this._startLevel = void 0; 10418 _this.currentLevel = null; 10419 _this.currentLevelIndex = -1; 10420 _this.manualLevelIndex = -1; 10421 _this.steering = void 0; 10422 _this.onParsedComplete = void 0; 10423 _this.steering = contentSteeringController; 10424 _this._registerListeners(); 10425 return _this; 10426 } 10427 _inheritsLoose(LevelController, _BasePlaylistControll); 10428 var _proto = LevelController.prototype; 10429 _proto._registerListeners = function _registerListeners() { 10430 var hls = this.hls; 10431 hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); 10432 hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this); 10433 hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this); 10434 hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); 10435 hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); 10436 hls.on(Events.ERROR, this.onError, this); 10437 }; 10438 _proto._unregisterListeners = function _unregisterListeners() { 10439 var hls = this.hls; 10440 hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); 10441 hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this); 10442 hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this); 10443 hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); 10444 hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); 10445 hls.off(Events.ERROR, this.onError, this); 10446 }; 10447 _proto.destroy = function destroy() { 10448 this._unregisterListeners(); 10449 this.steering = null; 10450 this.resetLevels(); 10451 _BasePlaylistControll.prototype.destroy.call(this); 10452 }; 10453 _proto.stopLoad = function stopLoad() { 10454 var levels = this._levels; 10455 10456 // clean up live level details to force reload them, and reset load errors 10457 levels.forEach(function (level) { 10458 level.loadError = 0; 10459 level.fragmentError = 0; 10460 }); 10461 _BasePlaylistControll.prototype.stopLoad.call(this); 10462 }; 10463 _proto.resetLevels = function resetLevels() { 10464 this._startLevel = undefined; 10465 this.manualLevelIndex = -1; 10466 this.currentLevelIndex = -1; 10467 this.currentLevel = null; 10468 this._levels = []; 10469 this._maxAutoLevel = -1; 10470 }; 10471 _proto.onManifestLoading = function onManifestLoading(event, data) { 10472 this.resetLevels(); 10473 }; 10474 _proto.onManifestLoaded = function onManifestLoaded(event, data) { 10475 var preferManagedMediaSource = this.hls.config.preferManagedMediaSource; 10476 var levels = []; 10477 var redundantSet = {}; 10478 var generatePathwaySet = {}; 10479 var resolutionFound = false; 10480 var videoCodecFound = false; 10481 var audioCodecFound = false; 10482 data.levels.forEach(function (levelParsed) { 10483 var _videoCodec; 10484 var attributes = levelParsed.attrs; 10485 var audioCodec = levelParsed.audioCodec, 10486 videoCodec = levelParsed.videoCodec; 10487 if (audioCodec) { 10488 // Returns empty and set to undefined for 'mp4a.40.34' with fallback to 'audio/mpeg' SourceBuffer 10489 levelParsed.audioCodec = audioCodec = getCodecCompatibleName(audioCodec, preferManagedMediaSource) || undefined; 10490 } 10491 if (((_videoCodec = videoCodec) == null ? void 0 : _videoCodec.indexOf('avc1')) === 0) { 10492 videoCodec = levelParsed.videoCodec = convertAVC1ToAVCOTI(videoCodec); 10493 } 10494 10495 // only keep levels with supported audio/video codecs 10496 var width = levelParsed.width, 10497 height = levelParsed.height, 10498 unknownCodecs = levelParsed.unknownCodecs; 10499 resolutionFound || (resolutionFound = !!(width && height)); 10500 videoCodecFound || (videoCodecFound = !!videoCodec); 10501 audioCodecFound || (audioCodecFound = !!audioCodec); 10502 if (unknownCodecs != null && unknownCodecs.length || audioCodec && !areCodecsMediaSourceSupp
10502orted(audioCodec, 'audio', preferManagedMediaSource) || videoCodec && !areCodecsMediaSourceSupported(videoCodec, 'video', preferManagedMediaSource)) { 10503 return; 10504 } 10505 var CODECS = attributes.CODECS, 10506 FRAMERATE = attributes['FRAME-RATE'], 10507 HDCP = attributes['HDCP-LEVEL'], 10508 PATHWAY = attributes['PATHWAY-ID'], 10509 RESOLUTION = attributes.RESOLUTION, 10510 VIDEO_RANGE = attributes['VIDEO-RANGE']; 10511 var contentSteeringPrefix = (PATHWAY || '.') + "-"; 10512 var levelKey = "" + contentSteeringPrefix + levelParsed.bitrate + "-" + RESOLUTION + "-" + FRAMERATE + "-" + CODECS + "-" + VIDEO_RANGE + "-" + HDCP; 10513 if (!redundantSet[levelKey]) { 10514 var level = new Level(levelParsed); 10515 redundantSet[levelKey] = level; 10516 generatePathwaySet[levelKey] = 1; 10517 levels.push(level); 10518 } else if (redundantSet[levelKey].uri !== levelParsed.url && !levelParsed.attrs['PATHWAY-ID']) { 10519 // Assign Pathway IDs to Redundant Streams (default Pathways is ".". Redundant Streams "..", "...", and so on.) 10520 // Content Steering controller to handles Pathway fallback on error 10521 var pathwayCount = generatePathwaySet[levelKey] += 1; 10522 levelParsed.attrs['PATHWAY-ID'] = new Array(pathwayCount + 1).join('.'); 10523 var _level = new Level(levelParsed); 10524 redundantSet[levelKey] = _level; 10525 levels.push(_level); 10526 } else { 10527 redundantSet[levelKey].addGroupId('audio', attributes.AUDIO); 10528 redundantSet[levelKey].addGroupId('text', attributes.SUBTITLES); 10529 } 10530 }); 10531 this.filterAndSortMediaOptions(levels, data, resolutionFound, videoCodecFound, audioCodecFound); 10532 }; 10533 _proto.filterAndSortMediaOptions = function filterAndSortMediaOptions(filteredLevels, data, resolutionFound, videoCodecFound, audioCodecFound) { 10534 var _this2 = this; 10535 var audioTracks = []; 10536 var subtitleTracks = []; 10537 var levels = filteredLevels; 10538 10539 // remove audio-only and invalid video-range levels if we also have levels with video codecs or RESOLUTION signalled 10540 if ((resolutionFound || videoCodecFound) && audioCodecFound) { 10541 levels = levels.filter(function (_ref) { 10542 var videoCodec = _ref.videoCodec, 10543 videoRange = _ref.videoRange, 10544 width = _ref.width, 10545 height = _ref.height; 10546 return (!!videoCodec || !!(width && height)) && isVideoRange(videoRange); 10547 }); 10548 } 10549 if (levels.length === 0) { 10550 // Dispatch error after MANIFEST_LOADED is done propagating 10551 Promise.resolve().then(function () { 10552 if (_this2.hls) { 10553 if (data.levels.length) { 10554 _this2.warn("One or more CODECS in variant not supported: " + JSON.stringify(data.levels[0].attrs)); 10555 } 10556 var error = new Error('no level with compatible codecs found in manifest'); 10557 _this2.hls.trigger(Events.ERROR, { 10558 type: ErrorTypes.MEDIA_ERROR, 10559 details: ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR, 10560 fatal: true, 10561 url: data.url, 10562 error: error, 10563 reason: error.message 10564 }); 10565 } 10566 }); 10567 return; 10568 } 10569 if (data.audioTracks) { 10570 var preferManagedMediaSource = this.hls.config.preferManagedMediaSource; 10571 audioTracks = data.audioTracks.filter(function (track) { 10572 return !track.audioCodec || areCodecsMediaSourceSupported(track.audioCodec, 'audio', preferManagedMediaSource); 10573 }); 10574 // Assign ids after filtering as array indices by group-id 10575 assignTrackIdsByGroup(audioTracks); 10576 } 10577 if (data.subtitles) { 10578 subtitleTracks = data.subtitles; 10579 assignTrackIdsByGroup(subtitleTracks); 10580 } 10581 // start bitrate is the first bitrate of the manifest 10582 var unsortedLevels = levels.slice(0); 10583 // sort levels from lowest to highest 10584 levels.sort(function (a, b) { 10585 if (a.attrs['HDCP-LEVEL'] !== b.attrs['HDCP-LEVEL']) { 10586 return (a.attrs['HDCP-LEVEL'] || '') > (b.attrs['HDCP-LEVEL'] || '') ? 1 : -1; 10587 } 10588 // sort on height before bitrate for cap-level-controller 10589 if (resolutionFound && a.height !== b.height) { 10590 return a.height - b.height; 10591 } 10592 if (a.frameRate !== b.frameRate) { 10593 return a.frameRate - b.frameRate; 10594 } 10595 if (a.videoRange !== b.videoRange) { 10596 return VideoRangeValues.indexOf(a.videoRange) - VideoRangeValues.indexOf(b.videoRange); 10597 } 10598 if (a.videoCodec !== b.videoCodec) { 10599 var valueA = videoCodecPreferenceValue(a.videoCodec); 10600 var valueB = videoCodecPreferenceValue(b.videoCodec); 10601 if (valueA !== valueB) { 10602 return valueB - valueA; 10603 } 10604 }
vendor: 7,116 bytes, lines 10605-10774
10605 if (a.uri === b.uri && a.codecSet !== b.codecSet) { 10606 var _valueA = codecsSetSelectionPreferenceValue(a.codecSet); 10607 var _valueB = codecsSetSelectionPreferenceValue(b.codecSet); 10608 if (_valueA !== _valueB) { 10609 return _valueB - _valueA; 10610 } 10611 } 10612 if (a.averageBitrate !== b.averageBitrate) { 10613 return a.averageBitrate - b.averageBitrate; 10614 } 10615 return 0; 10616 }); 10617 var firstLevelInPlaylist = unsortedLevels[0]; 10618 if (this.steering) { 10619 levels = this.steering.filterParsedLevels(levels); 10620 if (levels.length !== unsortedLevels.length) { 10621 for (var i = 0; i < unsortedLevels.length; i++) { 10622 if (unsortedLevels[i].pathwayId === levels[0].pathwayId) { 10623 firstLevelInPlaylist = unsortedLevels[i]; 10624 break; 10625 } 10626 } 10627 } 10628 } 10629 this._levels = levels; 10630 10631 // find index of first level in sorted levels 10632 for (var _i = 0; _i < levels.length; _i++) { 10633 if (levels[_i] === firstLevelInPlaylist) { 10634 var _this$hls$userConfig; 10635 this._firstLevel = _i; 10636 var firstLevelBitrate = firstLevelInPlaylist.bitrate; 10637 var bandwidthEstimate = this.hls.bandwidthEstimate; 10638 this.log("manifest loaded, " + levels.length + " level(s) found, first bitrate: " + firstLevelBitrate); 10639 // Update default bwe to first variant bitrate as long it has not been configured or set 10640 if (((_this$hls$userConfig = this.hls.userConfig) == null ? void 0 : _this$hls$userConfig.abrEwmaDefaultEstimate) === undefined) { 10641 var startingBwEstimate = Math.min(firstLevelBitrate, this.hls.config.abrEwmaDefaultEstimateMax); 10642 if (startingBwEstimate > bandwidthEstimate && bandwidthEstimate === hlsDefaultConfig.abrEwmaDefaultEstimate) { 10643 this.hls.bandwidthEstimate = startingBwEstimate; 10644 } 10645 } 10646 break; 10647 } 10648 } 10649 10650 // Audio is only alternate if manifest include a URI along with the audio group tag, 10651 // and this is not an audio-only stream where levels contain audio-only 10652 var audioOnly = audioCodecFound && !videoCodecFound; 10653 var edata = { 10654 levels: levels, 10655 audioTracks: audioTracks, 10656 subtitleTracks: subtitleTracks, 10657 sessionData: data.sessionData, 10658 sessionKeys: data.sessionKeys, 10659 firstLevel: this._firstLevel, 10660 stats: data.stats, 10661 audio: audioCodecFound, 10662 video: videoCodecFound, 10663 altAudio: !audioOnly && audioTracks.some(function (t) { 10664 return !!t.url; 10665 }) 10666 }; 10667 this.hls.trigger(Events.MANIFEST_PARSED, edata); 10668 10669 // Initiate loading after all controllers have received MANIFEST_PARSED 10670 if (this.hls.config.autoStartLoad || this.hls.forceStartLoad) { 10671 this.hls.startLoad(this.hls.config.startPosition); 10672 } 10673 }; 10674 _proto.onError = function onError(event, data) { 10675 if (data.fatal || !data.context) { 10676 return; 10677 } 10678 if (data.context.type === PlaylistContextType.LEVEL && data.context.level === this.level) { 10679 this.checkRetry(data); 10680 } 10681 } 10682 10683 // reset errors on the successful load of a fragment 10684 ; 10685 _proto.onFragBuffered = function onFragBuffered(event, _ref2) { 10686 var frag = _ref2.frag; 10687 if (frag !== undefined && frag.type === PlaylistLevelType.MAIN) { 10688 var el = frag.elementaryStreams; 10689 if (!Object.keys(el).some(function (type) { 10690 return !!el[type]; 10691 })) { 10692 return; 10693 } 10694 var level = this._levels[frag.level]; 10695 if (level != null && level.loadError) { 10696 this.log("Resetting level error count of " + level.loadError + " on frag buffered"); 10697 level.loadError = 0; 10698 } 10699 } 10700 }; 10701 _proto.onLevelLoaded = function onLevelLoaded(event, data) { 10702 var _data$deliveryDirecti2; 10703 var level = data.level, 10704 details = data.details; 10705 var curLevel = this._levels[level]; 10706 if (!curLevel) { 10707 var _data$deliveryDirecti; 10708 this.warn("Invalid level index " + level); 10709 if ((_data$deliveryDirecti = data.deliveryDirectives) != null && _data$deliveryDirecti.skip) { 10710 details.deltaUpdateFailed = true; 10711 } 10712 return; 10713 } 10714 10715 // only process level loaded events matching with expected level 10716 if (level === this.currentLevelIndex) { 10717 // reset level load error counter on successful level loaded only if there is no issues with fragments 10718 if (curLevel.fragmentError === 0) { 10719 curLevel.loadError = 0; 10720 } 10721 // Ignore matching details populated by loading a Media Playlist directly 10722 var previousDetails = curLevel.details; 10723 if (previousDetails === data.details && previousDetails.advanced) { 10724 previousDetails = undefined; 10725 } 10726 this.playlistLoaded(level, data, previousDetails); 10727 } else if ((_data$deliveryDirecti2 = data.deliveryDirectives) != null && _data$deliveryDirecti2.skip) { 10728 // received a delta playlist update that cannot be merged 10729 details.deltaUpdateFailed = true; 10730 } 10731 }; 10732 _proto.loadPlaylist = function loadPlaylist(hlsUrlParameters) { 10733 _BasePlaylistControll.prototype.loadPlaylist.call(this); 10734 var currentLevelIndex = this.currentLevelIndex; 10735 var currentLevel = this.currentLevel; 10736 if (currentLevel && this.shouldLoadPlaylist(currentLevel)) { 10737 var url = currentLevel.uri; 10738 if (hlsUrlParameters) { 10739 try { 10740 url = hlsUrlParameters.addDirectives(url); 10741 } catch (error) { 10742 this.warn("Could not construct new URL with HLS Delivery Directives: " + error); 10743 } 10744 } 10745 var pathwayId = currentLevel.attrs['PATHWAY-ID']; 10746 this.log("Loading level index " + currentLevelIndex + ((hlsUrlParameters == null ? void 0 : hlsUrlParameters.msn) !== undefined ? ' at sn ' + hlsUrlParameters.msn + ' part ' + hlsUrlParameters.part : '') + " with" + (pathwayId ? ' Pathway ' + pathwayId : '') + " " + url); 10747 10748 // console.log('Current audio track group ID:', this.hls.audioTracks[this.hls.audioTrack].groupId); 10749 // console.log('New video quality level audio group id:', levelObject.attrs.AUDIO, level); 10750 this.clearTimer(); 10751 this.hls.trigger(Events.LEVEL_LOADING, { 10752 url: url, 10753 level: currentLevelIndex, 10754 pathwayId: currentLevel.attrs['PATHWAY-ID'], 10755 id: 0, 10756 // Deprecated Level urlId 10757 deliveryDirectives: hlsUrlParameters || null 10758 }); 10759 } 10760 }; 10761 _proto.removeLevel = function removeLevel(levelIndex) { 10762 var _this3 = this, 10763 _this$currentLevel; 10764 var levels = this._levels.filter(function (level, index) { 10765 if (index !== levelIndex) { 10766 return true; 10767 } 10768 if (_this3.steering) { 10769 _this3.steering.removeLevel(level); 10770 } 10771 if (level === _this3.currentLevel) { 10772 _this3.currentLevel = null; 10773 _this3.currentLevelIndex = -1; 10774 if (level.details) {
vendor: 4,616 bytes, lines 10775-10893
10775 level.details.fragments.forEach(function (f) { 10776 return f.level = -1; 10777 }); 10778 } 10779 } 10780 return false; 10781 }); 10782 reassignFragmentLevelIndexes(levels); 10783 this._levels = levels; 10784 if (this.currentLevelIndex > -1 && (_this$currentLevel = this.currentLevel) != null && _this$currentLevel.details) { 10785 this.currentLevelIndex = this.currentLevel.details.fragments[0].level; 10786 } 10787 this.hls.trigger(Events.LEVELS_UPDATED, { 10788 levels: levels 10789 }); 10790 }; 10791 _proto.onLevelsUpdated = function onLevelsUpdated(event, _ref3) { 10792 var levels = _ref3.levels; 10793 this._levels = levels; 10794 }; 10795 _proto.checkMaxAutoUpdated = function checkMaxAutoUpdated() { 10796 var _this$hls = this.hls, 10797 autoLevelCapping = _this$hls.autoLevelCapping, 10798 maxAutoLevel = _this$hls.maxAutoLevel, 10799 maxHdcpLevel = _this$hls.maxHdcpLevel; 10800 if (this._maxAutoLevel !== maxAutoLevel) { 10801 this._maxAutoLevel = maxAutoLevel; 10802 this.hls.trigger(Events.MAX_AUTO_LEVEL_UPDATED, { 10803 autoLevelCapping: autoLevelCapping, 10804 levels: this.levels, 10805 maxAutoLevel: maxAutoLevel, 10806 minAutoLevel: this.hls.minAutoLevel, 10807 maxHdcpLevel: maxHdcpLevel 10808 }); 10809 } 10810 }; 10811 return _createClass(LevelController, [{ 10812 key: "levels", 10813 get: function get() { 10814 if (this._levels.length === 0) { 10815 return null; 10816 } 10817 return this._levels; 10818 } 10819 }, { 10820 key: "level", 10821 get: function get() { 10822 return this.currentLevelIndex; 10823 }, 10824 set: function set(newLevel) { 10825 var levels = this._levels; 10826 if (levels.length === 0) { 10827 return; 10828 } 10829 // check if level idx is valid 10830 if (newLevel < 0 || newLevel >= levels.length) { 10831 // invalid level id given, trigger error 10832 var error = new Error('invalid level idx'); 10833 var fatal = newLevel < 0; 10834 this.hls.trigger(Events.ERROR, { 10835 type: ErrorTypes.OTHER_ERROR, 10836 details: ErrorDetails.LEVEL_SWITCH_ERROR, 10837 level: newLevel, 10838 fatal: fatal, 10839 error: error, 10840 reason: error.message 10841 }); 10842 if (fatal) { 10843 return; 10844 } 10845 newLevel = Math.min(newLevel, levels.length - 1); 10846 } 10847 var lastLevelIndex = this.currentLevelIndex; 10848 var lastLevel = this.currentLevel; 10849 var lastPathwayId = lastLevel ? lastLevel.attrs['PATHWAY-ID'] : undefined; 10850 var level = levels[newLevel]; 10851 var pathwayId = level.attrs['PATHWAY-ID']; 10852 this.currentLevelIndex = newLevel; 10853 this.currentLevel = level; 10854 if (lastLevelIndex === newLevel && level.details && lastLevel && lastPathwayId === pathwayId) { 10855 return; 10856 } 10857 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 : '')); 10858 var levelSwitchingData = { 10859 level: newLevel, 10860 attrs: level.attrs, 10861 details: level.details, 10862 bitrate: level.bitrate, 10863 averageBitrate: level.averageBitrate, 10864 maxBitrate: level.maxBitrate, 10865 realBitrate: level.realBitrate, 10866 width: level.width, 10867 height: level.height, 10868 codecSet: level.codecSet, 10869 audioCodec: level.audioCodec, 10870 videoCodec: level.videoCodec, 10871 audioGroups: level.audioGroups, 10872 subtitleGroups: level.subtitleGroups, 10873 loaded: level.loaded, 10874 loadError: level.loadError, 10875 fragmentError: level.fragmentError, 10876 name: level.name, 10877 id: level.id, 10878 uri: level.uri, 10879 url: level.url, 10880 urlId: 0, 10881 audioGroupIds: level.audioGroupIds, 10882 textGroupIds: level.textGroupIds 10883 }; 10884 this.hls.trigger(Events.LEVEL_SWITCHING, levelSwitchingData); 10885 // check if we need to load playlist for this level 10886 var levelDetails = level.details; 10887 if (!levelDetails || levelDetails.live) { 10888 // level not retrieved yet, or live playlist we need to (re)load it 10889 var hlsUrlParameters = this.switchParams(level.uri, lastLevel == null ? void 0 : lastLevel.details, levelDetails); 10890 this.loadPlaylist(hlsUrlParameters); 10891 } 10892 } 10893 }, {
vendor: 6,583 bytes, lines 10894-11077
10894 key: "manualLevel", 10895 get: function get() { 10896 return this.manualLevelIndex; 10897 }, 10898 set: function set(newLevel) { 10899 this.manualLevelIndex = newLevel; 10900 if (this._startLevel === undefined) { 10901 this._startLevel = newLevel; 10902 } 10903 if (newLevel !== -1) { 10904 this.level = newLevel; 10905 } 10906 } 10907 }, { 10908 key: "firstLevel", 10909 get: function get() { 10910 return this._firstLevel; 10911 }, 10912 set: function set(newLevel) { 10913 this._firstLevel = newLevel; 10914 } 10915 }, { 10916 key: "startLevel", 10917 get: function get() { 10918 // Setting hls.startLevel (this._startLevel) overrides config.startLevel 10919 if (this._startLevel === undefined) { 10920 var configStartLevel = this.hls.config.startLevel; 10921 if (configStartLevel !== undefined) { 10922 return configStartLevel; 10923 } 10924 return this.hls.firstAutoLevel; 10925 } 10926 return this._startLevel; 10927 }, 10928 set: function set(newLevel) { 10929 this._startLevel = newLevel; 10930 } 10931 }, { 10932 key: "pathwayPriority", 10933 get: function get() { 10934 if (this.steering) { 10935 return this.steering.pathwayPriority; 10936 } 10937 return null; 10938 }, 10939 set: function set(pathwayPriority) { 10940 if (this.steering) { 10941 var pathwaysList = this.steering.pathways(); 10942 var filteredPathwayPriority = pathwayPriority.filter(function (pathwayId) { 10943 return pathwaysList.indexOf(pathwayId) !== -1; 10944 }); 10945 if (pathwayPriority.length < 1) { 10946 this.warn("pathwayPriority " + pathwayPriority + " should contain at least one pathway from list: " + pathwaysList); 10947 return; 10948 } 10949 this.steering.pathwayPriority = filteredPathwayPriority; 10950 } 10951 } 10952 }, { 10953 key: "nextLoadLevel", 10954 get: function get() { 10955 if (this.manualLevelIndex !== -1) { 10956 return this.manualLevelIndex; 10957 } else { 10958 return this.hls.nextAutoLevel; 10959 } 10960 }, 10961 set: function set(nextLevel) { 10962 this.level = nextLevel; 10963 if (this.manualLevelIndex === -1) { 10964 this.hls.nextAutoLevel = nextLevel; 10965 } 10966 } 10967 }]); 10968 }(BasePlaylistController); 10969 function assignTrackIdsByGroup(tracks) { 10970 var groups = {}; 10971 tracks.forEach(function (track) { 10972 var groupId = track.groupId || ''; 10973 track.id = groups[groupId] = groups[groupId] || 0; 10974 groups[groupId]++; 10975 }); 10976 } 10977 10978 var FragmentState = { 10979 NOT_LOADED: "NOT_LOADED", 10980 APPENDING: "APPENDING", 10981 PARTIAL: "PARTIAL", 10982 OK: "OK" 10983 }; 10984 var FragmentTracker = /*#__PURE__*/function () { 10985 function FragmentTracker(hls) { 10986 this.activePartLists = Object.create(null); 10987 this.endListFragments = Object.create(null); 10988 this.fragments = Object.create(null); 10989 this.timeRanges = Object.create(null); 10990 this.bufferPadding = 0.2; 10991 this.hls = void 0; 10992 this.hasGaps = false; 10993 this.hls = hls; 10994 this._registerListeners(); 10995 } 10996 var _proto = FragmentTracker.prototype; 10997 _proto._registerListeners = function _registerListeners() { 10998 var hls = this.hls; 10999 hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); 11000 hls.on(Events.BUFFER_APPENDED, this.onBufferAppended, this); 11001 hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); 11002 hls.on(Events.FRAG_LOADED, this.onFragLoaded, this); 11003 }; 11004 _proto._unregisterListeners = function _unregisterListeners() { 11005 var hls = this.hls; 11006 hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); 11007 hls.off(Events.BUFFER_APPENDED, this.onBufferAppended, this); 11008 hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); 11009 hls.off(Events.FRAG_LOADED, this.onFragLoaded, this); 11010 }; 11011 _proto.destroy = function destroy() { 11012 this._unregisterListeners(); 11013 // @ts-ignore 11014 this.fragments = 11015 // @ts-ignore 11016 this.activePartLists = 11017 // @ts-ignore 11018 this.endListFragments = this.timeRanges = null; 11019 } 11020 11021 /** 11022 * Return a Fragment or Part with an appended range that matches the position and levelType 11023 * Otherwise, return null 11024 */; 11025 _proto.getAppendedFrag = function getAppendedFrag(position, levelType) { 11026 var activeParts = this.activePartLists[levelType]; 11027 if (activeParts) { 11028 for (var i = activeParts.length; i--;) { 11029 var activePart = activeParts[i]; 11030 if (!activePart) { 11031 break; 11032 } 11033 var appendedPTS = activePart.end; 11034 if (activePart.start <= position && appendedPTS !== null && position <= appendedPTS) { 11035 return activePart; 11036 } 11037 } 11038 } 11039 return this.getBufferedFrag(position, levelType); 11040 } 11041 11042 /** 11043 * Return a buffered Fragment that matches the position and levelType. 11044 * A buffered Fragment is one whose loading, parsing and appending is done (completed or "partial" meaning aborted). 11045 * If not found any Fragment, return null 11046 */; 11047 _proto.getBufferedFrag = function getBufferedFrag(position, levelType) { 11048 return this.getFragAtPos(position, levelType, true); 11049 }; 11050 _proto.getFragAtPos = function getFragAtPos(position, levelType, buffered) { 11051 var fragments = this.fragments; 11052 var keys = Object.keys(fragments); 11053 for (var i = keys.length; i--;) { 11054 var fragmentEntity = fragments[keys[i]]; 11055 if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === levelType && (!buffered || fragmentEntity.buffered)) { 11056 var frag = fragmentEntity.body; 11057 if (frag.start <= position && position <= frag.end) { 11058 return frag; 11059 } 11060 } 11061 } 11062 return null; 11063 } 11064 11065 /** 11066 * Partial fragments effected by coded frame eviction will be removed 11067 * The browser will unload parts of the buffer to free up memory for new buffer data 11068 * Fragments will need to be reloaded when the buffer is freed up, removing partial fragments will allow them to reload(since there might be parts that are still playable) 11069 */; 11070 _proto.detectEvictedFragments = function detectEvictedFragments(elementaryStream, timeRange, playlistType, appendedPart, removeAppending) { 11071 var _this = this; 11072 if (this.timeRanges) { 11073 this.timeRanges[elementaryStream] = timeRange; 11074 } 11075 // Check if any flagged fragments have been unloaded 11076 // excluding anything newer than appendedPartSn 11077 var appendedPartSn = (appendedPart == null ? void 0 : appendedPart.fragment.sn) || -1;
vendor: 5,361 bytes, lines 11078-11223
11078 Object.keys(this.fragments).forEach(function (key) { 11079 var fragmentEntity = _this.fragments[key]; 11080 if (!fragmentEntity) { 11081 return; 11082 } 11083 if (appendedPartSn >= fragmentEntity.body.sn) { 11084 return; 11085 } 11086 if (!fragmentEntity.buffered && (!fragmentEntity.loaded || removeAppending)) { 11087 if (fragmentEntity.body.type === playlistType) { 11088 _this.removeFragment(fragmentEntity.body); 11089 } 11090 return; 11091 } 11092 var esData = fragmentEntity.range[elementaryStream]; 11093 if (!esData) { 11094 return; 11095 } 11096 if (esData.time.length === 0) { 11097 _this.removeFragment(fragmentEntity.body); 11098 return; 11099 } 11100 esData.time.some(function (time) { 11101 var isNotBuffered = !_this.isTimeBuffered(time.startPTS, time.endPTS, timeRange); 11102 if (isNotBuffered) { 11103 // Unregister partial fragment as it needs to load again to be reused 11104 _this.removeFragment(fragmentEntity.body); 11105 } 11106 return isNotBuffered; 11107 }); 11108 }); 11109 } 11110 11111 /** 11112 * Checks if the fragment passed in is loaded in the buffer properly 11113 * Partially loaded fragments will be registered as a partial fragment 11114 */; 11115 _proto.detectPartialFragments = function detectPartialFragments(data) { 11116 var _this2 = this; 11117 var timeRanges = this.timeRanges; 11118 if (!timeRanges || data.frag.sn === 'initSegment') { 11119 return; 11120 } 11121 var frag = data.frag; 11122 var fragKey = getFragmentKey(frag); 11123 var fragmentEntity = this.fragments[fragKey]; 11124 if (!fragmentEntity || fragmentEntity.buffered && frag.gap) { 11125 return; 11126 } 11127 var isFragHint = !frag.relurl; 11128 Object.keys(timeRanges).forEach(function (elementaryStream) { 11129 var streamInfo = frag.elementaryStreams[elementaryStream]; 11130 if (!streamInfo) { 11131 return; 11132 } 11133 var timeRange = timeRanges[elementaryStream]; 11134 var partial = isFragHint || streamInfo.partial === true; 11135 fragmentEntity.range[elementaryStream] = _this2.getBufferedTimes(frag, data.part, partial, timeRange); 11136 }); 11137 fragmentEntity.loaded = null; 11138 if (Object.keys(fragmentEntity.range).length) { 11139 fragmentEntity.buffered = true; 11140 var endList = fragmentEntity.body.endList = frag.endList || fragmentEntity.body.endList; 11141 if (endList) { 11142 this.endListFragments[fragmentEntity.body.type] = fragmentEntity; 11143 } 11144 if (!isPartial(fragmentEntity)) { 11145 // Remove older fragment parts from lookup after frag is tracked as buffered 11146 this.removeParts(frag.sn - 1, frag.type); 11147 } 11148 } else { 11149 // remove fragment if nothing was appended 11150 this.removeFragment(fragmentEntity.body); 11151 } 11152 }; 11153 _proto.removeParts = function removeParts(snToKeep, levelType) { 11154 var activeParts = this.activePartLists[levelType]; 11155 if (!activeParts) { 11156 return; 11157 } 11158 this.activePartLists[levelType] = activeParts.filter(function (part) { 11159 return part.fragment.sn >= snToKeep; 11160 }); 11161 }; 11162 _proto.fragBuffered = function fragBuffered(frag, force) { 11163 var fragKey = getFragmentKey(frag); 11164 var fragmentEntity = this.fragments[fragKey]; 11165 if (!fragmentEntity && force) { 11166 fragmentEntity = this.fragments[fragKey] = { 11167 body: frag, 11168 appendedPTS: null, 11169 loaded: null, 11170 buffered: false, 11171 range: Object.create(null) 11172 }; 11173 if (frag.gap) { 11174 this.hasGaps = true; 11175 } 11176 } 11177 if (fragmentEntity) { 11178 fragmentEntity.loaded = null; 11179 fragmentEntity.buffered = true; 11180 } 11181 }; 11182 _proto.getBufferedTimes = function getBufferedTimes(fragment, part, partial, timeRange) { 11183 var buffered = { 11184 time: [], 11185 partial: partial 11186 }; 11187 var startPTS = fragment.start; 11188 var endPTS = fragment.end; 11189 var minEndPTS = fragment.minEndPTS || endPTS; 11190 var maxStartPTS = fragment.maxStartPTS || startPTS; 11191 for (var i = 0; i < timeRange.length; i++) { 11192 var startTime = timeRange.start(i) - this.bufferPadding; 11193 var endTime = timeRange.end(i) + this.bufferPadding; 11194 if (maxStartPTS >= startTime && minEndPTS <= endTime) { 11195 // Fragment is entirely contained in buffer 11196 // No need to check the other timeRange times since it's completely playable 11197 buffered.time.push({ 11198 startPTS: Math.max(startPTS, timeRange.start(i)), 11199 endPTS: Math.min(endPTS, timeRange.end(i)) 11200 }); 11201 break; 11202 } else if (startPTS < endTime && endPTS > startTime) { 11203 var start = Math.max(startPTS, timeRange.start(i)); 11204 var end = Math.min(endPTS, timeRange.end(i)); 11205 if (end > start) { 11206 buffered.partial = true; 11207 // Check for intersection with buffer 11208 // Get playable sections of the fragment 11209 buffered.time.push({ 11210 startPTS: start, 11211 endPTS: end 11212 }); 11213 } 11214 } else if (endPTS <= startTime) { 11215 // No need to check the rest of the timeRange as it is in order 11216 break; 11217 } 11218 } 11219 return buffered; 11220 } 11221 11222 /** 11223 * Gets the partial fragment for a certain time�
vendor: 5,501 bytes, lines 11223-11375
11223 11224 */; 11225 _proto.getPartialFragment = function getPartialFragment(time) { 11226 var bestFragment = null; 11227 var timePadding; 11228 var startTime; 11229 var endTime; 11230 var bestOverlap = 0; 11231 var bufferPadding = this.bufferPadding, 11232 fragments = this.fragments; 11233 Object.keys(fragments).forEach(function (key) { 11234 var fragmentEntity = fragments[key]; 11235 if (!fragmentEntity) { 11236 return; 11237 } 11238 if (isPartial(fragmentEntity)) { 11239 startTime = fragmentEntity.body.start - bufferPadding; 11240 endTime = fragmentEntity.body.end + bufferPadding; 11241 if (time >= startTime && time <= endTime) { 11242 // Use the fragment that has the most padding from start and end time 11243 timePadding = Math.min(time - startTime, endTime - time); 11244 if (bestOverlap <= timePadding) { 11245 bestFragment = fragmentEntity.body; 11246 bestOverlap = timePadding; 11247 } 11248 } 11249 } 11250 }); 11251 return bestFragment; 11252 }; 11253 _proto.isEndListAppended = function isEndListAppended(type) { 11254 var lastFragmentEntity = this.endListFragments[type]; 11255 return lastFragmentEntity !== undefined && (lastFragmentEntity.buffered || isPartial(lastFragmentEntity)); 11256 }; 11257 _proto.getState = function getState(fragment) { 11258 var fragKey = getFragmentKey(fragment); 11259 var fragmentEntity = this.fragments[fragKey]; 11260 if (fragmentEntity) { 11261 if (!fragmentEntity.buffered) { 11262 return FragmentState.APPENDING; 11263 } else if (isPartial(fragmentEntity)) { 11264 return FragmentState.PARTIAL; 11265 } else { 11266 return FragmentState.OK; 11267 } 11268 } 11269 return FragmentState.NOT_LOADED; 11270 }; 11271 _proto.isTimeBuffered = function isTimeBuffered(startPTS, endPTS, timeRange) { 11272 var startTime; 11273 var endTime; 11274 for (var i = 0; i < timeRange.length; i++) { 11275 startTime = timeRange.start(i) - this.bufferPadding; 11276 endTime = timeRange.end(i) + this.bufferPadding; 11277 if (startPTS >= startTime && endPTS <= endTime) { 11278 return true; 11279 } 11280 if (endPTS <= startTime) { 11281 // No need to check the rest of the timeRange as it is in order 11282 return false; 11283 } 11284 } 11285 return false; 11286 }; 11287 _proto.onManifestLoading = function onManifestLoading() { 11288 this.removeAllFragments(); 11289 }; 11290 _proto.onFragLoaded = function onFragLoaded(event, data) { 11291 // don't track initsegment (for which sn is not a number) 11292 // don't track frags used for bitrateTest, they're irrelevant. 11293 if (data.frag.sn === 'initSegment' || data.frag.bitrateTest) { 11294 return; 11295 } 11296 var frag = data.frag; 11297 // Fragment entity `loaded` FragLoadedData is null when loading parts 11298 var loaded = data.part ? null : data; 11299 var fragKey = getFragmentKey(frag); 11300 this.fragments[fragKey] = { 11301 body: frag, 11302 appendedPTS: null, 11303 loaded: loaded, 11304 buffered: false, 11305 range: Object.create(null) 11306 }; 11307 }; 11308 _proto.onBufferAppended = function onBufferAppended(event, data) { 11309 var frag = data.frag, 11310 part = data.part, 11311 timeRanges = data.timeRanges, 11312 type = data.type; 11313 if (frag.sn === 'initSegment') { 11314 return; 11315 } 11316 var playlistType = frag.type; 11317 if (part) { 11318 var activeParts = this.activePartLists[playlistType]; 11319 if (!activeParts) { 11320 this.activePartLists[playlistType] = activeParts = []; 11321 } 11322 activeParts.push(part); 11323 } 11324 // Store the latest timeRanges loaded in the buffer 11325 this.timeRanges = timeRanges; 11326 var timeRange = timeRanges[type]; 11327 this.detectEvictedFragments(type, timeRange, playlistType, part); 11328 }; 11329 _proto.onFragBuffered = function onFragBuffered(event, data) { 11330 this.detectPartialFragments(data); 11331 }; 11332 _proto.hasFragment = function hasFragment(fragment) { 11333 var fragKey = getFragmentKey(fragment); 11334 return !!this.fragments[fragKey]; 11335 }; 11336 _proto.hasFragments = function hasFragments(type) { 11337 var fragments = this.fragments; 11338 var keys = Object.keys(fragments); 11339 if (!type) { 11340 return keys.length > 0; 11341 } 11342 for (var i = keys.length; i--;) { 11343 var fragmentEntity = fragments[keys[i]]; 11344 if ((fragmentEntity == null ? void 0 : fragmentEntity.body.type) === type) { 11345 return true; 11346 } 11347 } 11348 return false; 11349 }; 11350 _proto.hasParts = function hasParts(type) { 11351 var _this$activePartLists; 11352 return !!((_this$activePartLists = this.activePartLists[type]) != null && _this$activePartLists.length); 11353 }; 11354 _proto.removeFragmentsInRange = function removeFragmentsInRange(start, end, playlistType, withGapOnly, unbufferedOnly) { 11355 var _this3 = this; 11356 if (withGapOnly && !this.hasGaps) { 11357 return; 11358 } 11359 Object.keys(this.fragments).forEach(function (key) { 11360 var fragmentEntity = _this3.fragments[key]; 11361 if (!fragmentEntity) { 11362 return; 11363 } 11364 var frag = fragmentEntity.body; 11365 if (frag.type !== playlistType || withGapOnly && !frag.gap) { 11366 return; 11367 } 11368 if (frag.start < end && frag.end > start && (fragmentEntity.buffered || unbufferedOnly)) { 11369 _this3.removeFragment(frag); 11370 } 11371 }); 11372 }; 11373 _proto.removeFragment = function removeFragment(fragment) { 11374 var fragKey = getFragmentKey(fragment); 11375 fragment.
vendor: 3,509 bytes, lines 11375-11467
11375stats.loaded = 0; 11376 fragment.clearElementaryStreamInfo(); 11377 var activeParts = this.activePartLists[fragment.type]; 11378 if (activeParts) { 11379 var snToRemove = fragment.sn; 11380 this.activePartLists[fragment.type] = activeParts.filter(function (part) { 11381 return part.fragment.sn !== snToRemove; 11382 }); 11383 } 11384 delete this.fragments[fragKey]; 11385 if (fragment.endList) { 11386 delete this.endListFragments[fragment.type]; 11387 } 11388 }; 11389 _proto.removeAllFragments = function removeAllFragments() { 11390 this.fragments = Object.create(null); 11391 this.endListFragments = Object.create(null); 11392 this.activePartLists = Object.create(null); 11393 this.hasGaps = false; 11394 }; 11395 return FragmentTracker; 11396 }(); 11397 function isPartial(fragmentEntity) { 11398 var _fragmentEntity$range, _fragmentEntity$range2, _fragmentEntity$range3; 11399 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)); 11400 } 11401 function getFragmentKey(fragment) { 11402 return fragment.type + "_" + fragment.level + "_" + fragment.sn; 11403 } 11404 11405 var MIN_CHUNK_SIZE = Math.pow(2, 17); // 128kb 11406 var FragmentLoader = /*#__PURE__*/function () { 11407 function FragmentLoader(config) { 11408 this.config = void 0; 11409 this.loader = null; 11410 this.partLoadTimeout = -1; 11411 this.config = config; 11412 } 11413 var _proto = FragmentLoader.prototype; 11414 _proto.destroy = function destroy() { 11415 if (this.loader) { 11416 this.loader.destroy(); 11417 this.loader = null; 11418 } 11419 }; 11420 _proto.abort = function abort() { 11421 if (this.loader) { 11422 // Abort the loader for current fragment. Only one may load at any given time 11423 this.loader.abort(); 11424 } 11425 }; 11426 _proto.load = function load(frag, _onProgress) { 11427 var _this = this; 11428 var url = frag.url; 11429 if (!url) { 11430 return Promise.reject(new LoadError({ 11431 type: ErrorTypes.NETWORK_ERROR, 11432 details: ErrorDetails.FRAG_LOAD_ERROR, 11433 fatal: false, 11434 frag: frag, 11435 error: new Error("Fragment does not have a " + (url ? 'part list' : 'url')), 11436 networkDetails: null 11437 })); 11438 } 11439 this.abort(); 11440 var config = this.config; 11441 var FragmentILoader = config.fLoader; 11442 var DefaultILoader = config.loader; 11443 return new Promise(function (resolve, reject) { 11444 if (_this.loader) { 11445 _this.loader.destroy(); 11446 } 11447 if (frag.gap) { 11448 if (frag.tagList.some(function (tags) { 11449 return tags[0] === 'GAP'; 11450 })) { 11451 reject(createGapLoadError(frag)); 11452 return; 11453 } else { 11454 // Reset temporary treatment as GAP tag 11455 frag.gap = false; 11456 } 11457 } 11458 var loader = _this.loader = FragmentILoader ? new FragmentILoader(config) : new DefaultILoader(config); 11459 var loaderContext = createLoaderContext(frag); 11460 frag.loader = loader; 11461 var loadPolicy = getLoaderConfigWithoutReties(config.fragLoadPolicy.default); 11462 var loaderConfig = { 11463 loadPolicy: loadPolicy, 11464 timeout: loadPolicy.maxLoadTimeMs, 11465 maxRetry: 0, 11466 retryDelay: 0, 11467 maxRetryDelay: 0,
vendor: 2,671 bytes, lines 11468-11537
11468 highWaterMark: frag.sn === 'initSegment' ? Infinity : MIN_CHUNK_SIZE 11469 }; 11470 // Assign frag stats to the loader's stats reference 11471 frag.stats = loader.stats; 11472 loader.load(loaderContext, loaderConfig, { 11473 onSuccess: function onSuccess(response, stats, context, networkDetails) { 11474 _this.resetLoader(frag, loader); 11475 var payload = response.data; 11476 if (context.resetIV && frag.decryptdata) { 11477 frag.decryptdata.iv = new Uint8Array(payload.slice(0, 16)); 11478 payload = payload.slice(16); 11479 } 11480 resolve({ 11481 frag: frag, 11482 part: null, 11483 payload: payload, 11484 networkDetails: networkDetails 11485 }); 11486 }, 11487 onError: function onError(response, context, networkDetails, stats) { 11488 _this.resetLoader(frag, loader); 11489 reject(new LoadError({ 11490 type: ErrorTypes.NETWORK_ERROR, 11491 details: ErrorDetails.FRAG_LOAD_ERROR, 11492 fatal: false, 11493 frag: frag, 11494 response: _objectSpread2({ 11495 url: url, 11496 data: undefined 11497 }, response), 11498 error: new Error("HTTP Error " + response.code + " " + response.text), 11499 networkDetails: networkDetails, 11500 stats: stats 11501 })); 11502 }, 11503 onAbort: function onAbort(stats, context, networkDetails) { 11504 _this.resetLoader(frag, loader); 11505 reject(new LoadError({ 11506 type: ErrorTypes.NETWORK_ERROR, 11507 details: ErrorDetails.INTERNAL_ABORTED, 11508 fatal: false, 11509 frag: frag, 11510 error: new Error('Aborted'), 11511 networkDetails: networkDetails, 11512 stats: stats 11513 })); 11514 }, 11515 onTimeout: function onTimeout(stats, context, networkDetails) { 11516 _this.resetLoader(frag, loader); 11517 reject(new LoadError({ 11518 type: ErrorTypes.NETWORK_ERROR, 11519 details: ErrorDetails.FRAG_LOAD_TIMEOUT, 11520 fatal: false, 11521 frag: frag, 11522 error: new Error("Timeout after " + loaderConfig.timeout + "ms"), 11523 networkDetails: networkDetails, 11524 stats: stats 11525 })); 11526 }, 11527 onProgress: function onProgress(stats, context, data, networkDetails) { 11528 if (_onProgress) { 11529 _onProgress({ 11530 frag: frag, 11531 part: null, 11532 payload: data, 11533 networkDetails: networkDetails 11534 }); 11535 } 11536 } 11537 }
vendor: 7,045 bytes, lines 11537-11731
11537); 11538 }); 11539 }; 11540 _proto.loadPart = function loadPart(frag, part, onProgress) { 11541 var _this2 = this; 11542 this.abort(); 11543 var config = this.config; 11544 var FragmentILoader = config.fLoader; 11545 var DefaultILoader = config.loader; 11546 return new Promise(function (resolve, reject) { 11547 if (_this2.loader) { 11548 _this2.loader.destroy(); 11549 } 11550 if (frag.gap || part.gap) { 11551 reject(createGapLoadError(frag, part)); 11552 return; 11553 } 11554 var loader = _this2.loader = FragmentILoader ? new FragmentILoader(config) : new DefaultILoader(config); 11555 var loaderContext = createLoaderContext(frag, part); 11556 frag.loader = loader; 11557 // Should we define another load policy for parts? 11558 var loadPolicy = getLoaderConfigWithoutReties(config.fragLoadPolicy.default); 11559 var loaderConfig = { 11560 loadPolicy: loadPolicy, 11561 timeout: loadPolicy.maxLoadTimeMs, 11562 maxRetry: 0, 11563 retryDelay: 0, 11564 maxRetryDelay: 0, 11565 highWaterMark: MIN_CHUNK_SIZE 11566 }; 11567 // Assign part stats to the loader's stats reference 11568 part.stats = loader.stats; 11569 loader.load(loaderContext, loaderConfig, { 11570 onSuccess: function onSuccess(response, stats, context, networkDetails) { 11571 _this2.resetLoader(frag, loader); 11572 _this2.updateStatsFromPart(frag, part); 11573 var partLoadedData = { 11574 frag: frag, 11575 part: part, 11576 payload: response.data, 11577 networkDetails: networkDetails 11578 }; 11579 onProgress(partLoadedData); 11580 resolve(partLoadedData); 11581 }, 11582 onError: function onError(response, context, networkDetails, stats) { 11583 _this2.resetLoader(frag, loader); 11584 reject(new LoadError({ 11585 type: ErrorTypes.NETWORK_ERROR, 11586 details: ErrorDetails.FRAG_LOAD_ERROR, 11587 fatal: false, 11588 frag: frag, 11589 part: part, 11590 response: _objectSpread2({ 11591 url: loaderContext.url, 11592 data: undefined 11593 }, response), 11594 error: new Error("HTTP Error " + response.code + " " + response.text), 11595 networkDetails: networkDetails, 11596 stats: stats 11597 })); 11598 }, 11599 onAbort: function onAbort(stats, context, networkDetails) { 11600 frag.stats.aborted = part.stats.aborted; 11601 _this2.resetLoader(frag, loader); 11602 reject(new LoadError({ 11603 type: ErrorTypes.NETWORK_ERROR, 11604 details: ErrorDetails.INTERNAL_ABORTED, 11605 fatal: false, 11606 frag: frag, 11607 part: part, 11608 error: new Error('Aborted'), 11609 networkDetails: networkDetails, 11610 stats: stats 11611 })); 11612 }, 11613 onTimeout: function onTimeout(stats, context, networkDetails) { 11614 _this2.resetLoader(frag, loader); 11615 reject(new LoadError({ 11616 type: ErrorTypes.NETWORK_ERROR, 11617 details: ErrorDetails.FRAG_LOAD_TIMEOUT, 11618 fatal: false, 11619 frag: frag, 11620 part: part, 11621 error: new Error("Timeout after " + loaderConfig.timeout + "ms"), 11622 networkDetails: networkDetails, 11623 stats: stats 11624 })); 11625 } 11626 }); 11627 }); 11628 }; 11629 _proto.updateStatsFromPart = function updateStatsFromPart(frag, part) { 11630 var fragStats = frag.stats; 11631 var partStats = part.stats; 11632 var partTotal = partStats.total; 11633 fragStats.loaded += partStats.loaded; 11634 if (partTotal) { 11635 var estTotalParts = Math.round(frag.duration / part.duration); 11636 var estLoadedParts = Math.min(Math.round(fragStats.loaded / partTotal), estTotalParts); 11637 var estRemainingParts = estTotalParts - estLoadedParts; 11638 var estRemainingBytes = estRemainingParts * Math.round(fragStats.loaded / estLoadedParts); 11639 fragStats.total = fragStats.loaded + estRemainingBytes; 11640 } else { 11641 fragStats.total = Math.max(fragStats.loaded, fragStats.total); 11642 } 11643 var fragLoading = fragStats.loading; 11644 var partLoading = partStats.loading; 11645 if (fragLoading.start) { 11646 // add to fragment loader latency 11647 fragLoading.first += partLoading.first - partLoading.start; 11648 } else { 11649 fragLoading.start = partLoading.start; 11650 fragLoading.first = partLoading.first; 11651 } 11652 fragLoading.end = partLoading.end; 11653 }; 11654 _proto.resetLoader = function resetLoader(frag, loader) { 11655 frag.loader = null; 11656 if (this.loader === loader) { 11657 self.clearTimeout(this.partLoadTimeout); 11658 this.loader = null; 11659 } 11660 loader.destroy(); 11661 }; 11662 return FragmentLoader; 11663 }(); 11664 function createLoaderContext(frag, part) { 11665 if (part === void 0) { 11666 part = null; 11667 } 11668 var segment = part || frag; 11669 var loaderContext = { 11670 frag: frag, 11671 part: part, 11672 responseType: 'arraybuffer', 11673 url: segment.url, 11674 headers: {}, 11675 rangeStart: 0, 11676 rangeEnd: 0 11677 }; 11678 var start = segment.byteRangeStartOffset; 11679 var end = segment.byteRangeEndOffset; 11680 if (isFiniteNumber(start) && isFiniteNumber(end)) { 11681 var _frag$decryptdata; 11682 var byteRangeStart = start; 11683 var byteRangeEnd = end; 11684 if (frag.sn === 'initSegment' && isMethodFullSegmentAesCbc((_frag$decryptdata = frag.decryptdata) == null ? void 0 : _frag$decryptdata.method)) { 11685 // MAP segment encrypted with method 'AES-128' or 'AES-256' (cbc), when served with HTTP Range, 11686 // has the unencrypted size specified in the range. 11687 // Ref: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-08#section-6.3.6 11688 var fragmentLen = end - start; 11689 if (fragmentLen % 16) { 11690 byteRangeEnd = end + (16 - fragmentLen % 16); 11691 } 11692 if (start !== 0) { 11693 loaderContext.resetIV = true; 11694 byteRangeStart = start - 16; 11695 } 11696 } 11697 loaderContext.rangeStart = byteRangeStart; 11698 loaderContext.rangeEnd = byteRangeEnd; 11699 } 11700 return loaderContext; 11701 } 11702 function createGapLoadError(frag, part) { 11703 var error = new Error("GAP " + (frag.gap ? 'tag' : 'attribute') + " found"); 11704 var errorData = { 11705 type: ErrorTypes.MEDIA_ERROR, 11706 details: ErrorDetails.FRAG_GAP, 11707 fatal: false, 11708 frag: frag, 11709 error: error, 11710 networkDetails: null 11711 }; 11712 if (part) { 11713 errorData.part = part; 11714 } 11715 (part ? part : frag).stats.aborted = true; 11716 return new LoadError(errorData); 11717 } 11718 function isMethodFullSegmentAesCbc(method) { 11719 return method === 'AES-128' || method === 'AES-256'; 11720 } 11721 var LoadError = /*#__PURE__*/function (_Error) { 11722 function LoadError(data) { 11723 var _this3; 11724 _this3 = _Error.call(this, data.error.message) || this; 11725 _this3.data = void 0; 11726 _this3.data = data; 11727 return _this3; 11728 } 11729 _inheritsLoose(LoadError, _Error); 11730 return LoadError; 11731 }(
11731 /*#__PURE__*/_wrapNativeSuper(Error)); 11732 11733 var KeyLoader = /*#__PURE__*/function () { 11734 function KeyLoader(config) { 11735 this.config = void 0; 11736 this.keyUriToKeyInfo = {}; 11737 this.emeController = null; 11738 this.config = config; 11739 } 11740 var _proto = KeyLoader.prototype; 11741 _proto.abort = function abort(type) { 11742 for (var uri in this.keyUriToKeyInfo) { 11743 var loader = this.keyUriToKeyInfo[uri].loader; 11744 if (loader) { 11745 var _loader$context; 11746 if (type && type !== ((_loader$context = loader.context) == null ? void 0 : _loader$context.frag.type)) { 11747 return; 11748 } 11749 loader.abort(); 11750 } 11751 } 11752 }; 11753 _proto.detach = function detach() { 11754 for (var uri in this.keyUriToKeyInfo) { 11755 var keyInfo = this.keyUriToKeyInfo[uri]; 11756 // Remove cached EME keys on detach 11757 if (keyInfo.mediaKeySessionContext || keyInfo.decryptdata.isCommonEncryption) { 11758 delete this.keyUriToKeyInfo[uri]; 11759 } 11760 } 11761 }; 11762 _proto.destroy = function destroy() { 11763 this.detach(); 11764 for (var uri in this.keyUriToKeyInfo) { 11765 var loader = this.keyUriToKeyInfo[uri].loader; 11766 if (loader) { 11767 loader.destroy(); 11768 } 11769 } 11770 this.keyUriToKeyInfo = {}; 11771 }; 11772 _proto.createKeyLoadError = function createKeyLoadError(frag, details, error, networkDetails, response) { 11773 if (details === void 0) { 11774 details = ErrorDetails.KEY_LOAD_ERROR; 11775 } 11776 return new LoadError({ 11777 type: ErrorTypes.NETWORK_ERROR, 11778 details: details, 11779 fatal: false, 11780 frag: frag, 11781 response: response, 11782 error: error, 11783 networkDetails: networkDetails 11784 }); 11785 }; 11786 _proto.loadClear = function loadClear(loadingFrag, encryptedFragments) { 11787 var _this = this; 11788 if (this.emeController && this.config.emeEnabled) { 11789 // access key-system with nearest key on start (loaidng frag is unencrypted) 11790 var sn = loadingFrag.sn, 11791 cc = loadingFrag.cc; 11792 var _loop = function _loop() { 11793 var frag = encryptedFragments[i]; 11794 if (cc <= frag.cc && (sn === 'initSegment' || frag.sn === 'initSegment' || sn < frag.sn)) { 11795 _this.emeController.selectKeySystemFormat(frag).then(function (keySystemFormat) { 11796 frag.setKeyFormat(keySystemFormat); 11797 }); 11798 return 1; // break 11799 } 11800 }; 11801 for (var i = 0; i < encryptedFragments.length; i++) { 11802 if (_loop()) break; 11803 } 11804 } 11805 }; 11806 _proto.load = function load(frag) { 11807 var _this2 = this; 11808 if (!frag.decryptdata && frag.encrypted && this.emeController) { 11809 // Multiple keys, but none selected, resolve in eme-controller 11810 return this.emeController.selectKeySystemFormat(frag).then(function (keySystemFormat) { 11811 return _this2.loadInternal(frag, keySystemFormat); 11812 }); 11813 } 11814 return this.loadInternal(frag); 11815 }; 11816 _proto.loadInternal = function loadInternal(frag, keySystemFormat) { 11817 var _keyInfo, _keyInfo2; 11818 if (keySystemFormat) { 11819 frag.setKeyFormat(keySystemFormat); 11820 } 11821 var decryptdata = frag.decryptdata; 11822 if (!decryptdata) { 11823 var error = new Error(keySystemFormat ? "Expected frag.decryptdata to be defined after setting format " + keySystemFormat : 'Missing decryption data on fragment in onKeyLoading'); 11824 return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, error)); 11825 } 11826 var uri = decryptdata.uri; 11827 if (!uri) { 11828 return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error("Invalid key URI: \"" + uri + "\""))); 11829 } 11830 var keyInfo = this.keyUriToKeyInfo[uri]; 11831 if ((_keyInfo = keyInfo) != null && _keyInfo.decryptdata.key) { 11832 decryptdata.key = keyInfo.decryptdata.key; 11833 return Promise.resolve({ 11834 frag: frag, 11835 keyInfo: keyInfo 11836 }); 11837 } 11838 // Return key load promise as long as it does not have a mediakey session with an unusable key status
vendor: 8,958 bytes, lines 11839-12052
11839 if ((_keyInfo2 = keyInfo) != null && _keyInfo2.keyLoadPromise) { 11840 var _keyInfo$mediaKeySess; 11841 switch ((_keyInfo$mediaKeySess = keyInfo.mediaKeySessionContext) == null ? void 0 : _keyInfo$mediaKeySess.keyStatus) { 11842 case undefined: 11843 case 'status-pending': 11844 case 'usable': 11845 case 'usable-in-future': 11846 return keyInfo.keyLoadPromise.then(function (keyLoadedData) { 11847 // Return the correct fragment with updated decryptdata key and loaded keyInfo 11848 decryptdata.key = keyLoadedData.keyInfo.decryptdata.key; 11849 return { 11850 frag: frag, 11851 keyInfo: keyInfo 11852 }; 11853 }); 11854 } 11855 // If we have a key session and status and it is not pending or usable, continue 11856 // This will go back to the eme-controller for expired keys to get a new keyLoadPromise 11857 } 11858 11859 // Load the key or return the loading promise 11860 keyInfo = this.keyUriToKeyInfo[uri] = { 11861 decryptdata: decryptdata, 11862 keyLoadPromise: null, 11863 loader: null, 11864 mediaKeySessionContext: null 11865 }; 11866 switch (decryptdata.method) { 11867 case 'ISO-23001-7': 11868 case 'SAMPLE-AES': 11869 case 'SAMPLE-AES-CENC': 11870 case 'SAMPLE-AES-CTR': 11871 if (decryptdata.keyFormat === 'identity') { 11872 // loadKeyHTTP handles http(s) and data URLs 11873 return this.loadKeyHTTP(keyInfo, frag); 11874 } 11875 return this.loadKeyEME(keyInfo, frag); 11876 case 'AES-128': 11877 case 'AES-256': 11878 case 'AES-256-CTR': 11879 return this.loadKeyHTTP(keyInfo, frag); 11880 default: 11881 return Promise.reject(this.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error("Key supplied with unsupported METHOD: \"" + decryptdata.method + "\""))); 11882 } 11883 }; 11884 _proto.loadKeyEME = function loadKeyEME(keyInfo, frag) { 11885 var keyLoadedData = { 11886 frag: frag, 11887 keyInfo: keyInfo 11888 }; 11889 if (this.emeController && this.config.emeEnabled) { 11890 var keySessionContextPromise = this.emeController.loadKey(keyLoadedData); 11891 if (keySessionContextPromise) { 11892 return (keyInfo.keyLoadPromise = keySessionContextPromise.then(function (keySessionContext) { 11893 keyInfo.mediaKeySessionContext = keySessionContext; 11894 return keyLoadedData; 11895 })).catch(function (error) { 11896 // Remove promise for license renewal or retry 11897 keyInfo.keyLoadPromise = null; 11898 throw error; 11899 }); 11900 } 11901 } 11902 return Promise.resolve(keyLoadedData); 11903 }; 11904 _proto.loadKeyHTTP = function loadKeyHTTP(keyInfo, frag) { 11905 var _this3 = this; 11906 var config = this.config; 11907 var Loader = config.loader; 11908 var keyLoader = new Loader(config); 11909 frag.keyLoader = keyInfo.loader = keyLoader; 11910 return keyInfo.keyLoadPromise = new Promise(function (resolve, reject) { 11911 var loaderContext = { 11912 keyInfo: keyInfo, 11913 frag: frag, 11914 responseType: 'arraybuffer', 11915 url: keyInfo.decryptdata.uri 11916 }; 11917 11918 // maxRetry is 0 so that instead of retrying the same key on the same variant multiple times, 11919 // key-loader will trigger an error and rely on stream-controller to handle retry logic. 11920 // this will also align retry logic with fragment-loader 11921 var loadPolicy = config.keyLoadPolicy.default; 11922 var loaderConfig = { 11923 loadPolicy: loadPolicy, 11924 timeout: loadPolicy.maxLoadTimeMs, 11925 maxRetry: 0, 11926 retryDelay: 0, 11927 maxRetryDelay: 0 11928 }; 11929 var loaderCallbacks = { 11930 onSuccess: function onSuccess(response, stats, context, networkDetails) { 11931 var frag = context.frag, 11932 keyInfo = context.keyInfo, 11933 uri = context.url; 11934 if (!frag.decryptdata || keyInfo !== _this3.keyUriToKeyInfo[uri]) { 11935 return reject(_this3.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error('after key load, decryptdata unset or changed'), networkDetails)); 11936 } 11937 keyInfo.decryptdata.key = frag.decryptdata.key = new Uint8Array(response.data); 11938 11939 // detach fragment key loader on load success 11940 frag.keyLoader = null; 11941 keyInfo.loader = null; 11942 resolve({ 11943 frag: frag, 11944 keyInfo: keyInfo 11945 }); 11946 }, 11947 onError: function onError(response, context, networkDetails, stats) { 11948 _this3.resetLoader(context); 11949 reject(_this3.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_ERROR, new Error("HTTP Error " + response.code + " loading key " + response.text), networkDetails, _objectSpread2({ 11950 url: loaderContext.url, 11951 data: undefined 11952 }, response))); 11953 }, 11954 onTimeout: function onTimeout(stats, context, networkDetails) { 11955 _this3.resetLoader(context); 11956 reject(_this3.createKeyLoadError(frag, ErrorDetails.KEY_LOAD_TIMEOUT, new Error('key loading timed out'), networkDetails)); 11957 }, 11958 onAbort: function onAbort(stats, context, networkDetails) { 11959 _this3.resetLoader(context); 11960 reject(_this3.createKeyLoadError(frag, ErrorDetails.INTERNAL_ABORTED, new Error('key loading aborted'), networkDetails)); 11961 } 11962 }; 11963 keyLoader.load(loaderContext, loaderConfig, loaderCallbacks); 11964 }); 11965 }; 11966 _proto.resetLoader = function resetLoader(context) { 11967 var frag = context.frag, 11968 keyInfo = context.keyInfo, 11969 uri = context.url; 11970 var loader = keyInfo.loader; 11971 if (frag.keyLoader === loader) { 11972 frag.keyLoader = null; 11973 keyInfo.loader = null; 11974 } 11975 delete this.keyUriToKeyInfo[uri]; 11976 if (loader) { 11977 loader.destroy(); 11978 } 11979 }; 11980 return KeyLoader; 11981 }(); 11982 11983 /** 11984 * @ignore 11985 * Sub-class specialization of EventHandler base class. 11986 * 11987 * TaskLoop allows to schedule a task function being called (optionnaly repeatedly) on the main loop, 11988 * scheduled asynchroneously, avoiding recursive calls in the same tick. 11989 * 11990 * The task itself is implemented in `doTick`. It can be requested and called for single execution 11991 * using the `tick` method. 11992 * 11993 * It will be assured that the task execution method (`tick`) only gets called once per main loop "tick", 11994 * no matter how often it gets requested for execution. Execution in further ticks will be scheduled accordingly. 11995 * 11996 * If further execution requests have already been scheduled on the next tick, it can be checked with `hasNextTick`, 11997 * and cancelled with `clearNextTick`. 11998 * 11999 * The task can be scheduled as an interval repeatedly with a period as parameter (see `setInterval`, `clearInterval`). 12000 * 12001 * Sub-classes need to implement the `doTick` method which will effectively have the task execution routine. 12002 * 12003 * Further explanations: 12004 * 12005 * The baseclass has a `tick` method that will schedule the doTick call. It may be called synchroneously 12006 * only for a stack-depth of one. On re-entrant calls, sub-sequent calls are scheduled for next main loop ticks. 12007 * 12008 * When the task execution (`tick` method) is called in re-entrant way this is detected and 12009 * we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further 12010 * task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo). 12011 */ 12012 var TaskLoop = /*#__PURE__*/function (_Logger) { 12013 function TaskLoop(label, logger) { 12014 var _this; 12015 _this = _Logger.call(this, label, logger) || this; 12016 _this._boundTick = void 0; 12017 _this._tickTimer = null; 12018 _this._tickInterval = null; 12019 _this._tickCallCount = 0; 12020 _this._boundTick = _this.tick.bind(_this); 12021 return _this; 12022 } 12023 _inheritsLoose(TaskLoop, _Logger); 12024 var _proto = TaskLoop.prototype; 12025 _proto.destroy = function destroy() { 12026 this.onHandlerDestroying(); 12027 this.onHandlerDestroyed(); 12028 }; 12029 _proto.onHandlerDestroying = function onHandlerDestroying() { 12030 // clear all timers before unregistering from event bus 12031 this.clearNextTick(); 12032 this.clearInterval(); 12033 }; 12034 _proto.onHandlerDestroyed = function onHandlerDestroyed() {}; 12035 _proto.hasInterval = function hasInterval() { 12036 return !!this._tickInterval; 12037 }; 12038 _proto.hasNextTick = function hasNextTick() { 12039 return !!this._tickTimer; 12040 } 12041 12042 /** 12043 * @param millis - Interval time (ms) 12044 * @eturns True when interval has been scheduled, false when already scheduled (no effect) 12045 */; 12046 _proto.setInterval = function setInterval(millis) { 12047 if (!this._tickInterval) { 12048 this._tickCallCount = 0; 12049 this._tickInterval = self.setInterval(this._boundTick, millis); 12050 return true; 12051 } 12052 return false;
vendor: 4,298 bytes, lines 12053-12198
12053 } 12054 12055 /** 12056 * @returns True when interval was cleared, false when none was set (no effect) 12057 */; 12058 _proto.clearInterval = function clearInterval() { 12059 if (this._tickInterval) { 12060 self.clearInterval(this._tickInterval); 12061 this._tickInterval = null; 12062 return true; 12063 } 12064 return false; 12065 } 12066 12067 /** 12068 * @returns True when timeout was cleared, false when none was set (no effect) 12069 */; 12070 _proto.clearNextTick = function clearNextTick() { 12071 if (this._tickTimer) { 12072 self.clearTimeout(this._tickTimer); 12073 this._tickTimer = null; 12074 return true; 12075 } 12076 return false; 12077 } 12078 12079 /** 12080 * Will call the subclass doTick implementation in this main loop tick 12081 * or in the next one (via setTimeout(,0)) in case it has already been called 12082 * in this tick (in case this is a re-entrant call). 12083 */; 12084 _proto.tick = function tick() { 12085 this._tickCallCount++; 12086 if (this._tickCallCount === 1) { 12087 this.doTick(); 12088 // re-entrant call to tick from previous doTick call stack 12089 // -> schedule a call on the next main loop iteration to process this task processing request 12090 if (this._tickCallCount > 1) { 12091 // make sure only one timer exists at any time at max 12092 this.tickImmediate(); 12093 } 12094 this._tickCallCount = 0; 12095 } 12096 }; 12097 _proto.tickImmediate = function tickImmediate() { 12098 this.clearNextTick(); 12099 this._tickTimer = self.setTimeout(this._boundTick, 0); 12100 } 12101 12102 /** 12103 * For subclass to implement task logic 12104 * @abstract 12105 */; 12106 _proto.doTick = function doTick() {}; 12107 return TaskLoop; 12108 }(Logger); 12109 12110 var ChunkMetadata = function ChunkMetadata(level, sn, id, size, part, partial) { 12111 if (size === void 0) { 12112 size = 0; 12113 } 12114 if (part === void 0) { 12115 part = -1; 12116 } 12117 if (partial === void 0) { 12118 partial = false; 12119 } 12120 this.level = void 0; 12121 this.sn = void 0; 12122 this.part = void 0; 12123 this.id = void 0; 12124 this.size = void 0; 12125 this.partial = void 0; 12126 this.transmuxing = getNewPerformanceTiming(); 12127 this.buffering = { 12128 audio: getNewPerformanceTiming(), 12129 video: getNewPerformanceTiming(), 12130 audiovideo: getNewPerformanceTiming() 12131 }; 12132 this.level = level; 12133 this.sn = sn; 12134 this.id = id; 12135 this.size = size; 12136 this.part = part; 12137 this.partial = partial; 12138 }; 12139 function getNewPerformanceTiming() { 12140 return { 12141 start: 0, 12142 executeStart: 0, 12143 executeEnd: 0, 12144 end: 0 12145 }; 12146 } 12147 12148 function findFirstFragWithCC(fragments, cc) { 12149 for (var i = 0, len = fragments.length; i < len; i++) { 12150 var _fragments$i; 12151 if (((_fragments$i = fragments[i]) == null ? void 0 : _fragments$i.cc) === cc) { 12152 return fragments[i]; 12153 } 12154 } 12155 return null; 12156 } 12157 function shouldAlignOnDiscontinuities(refDetails, details) { 12158 if (refDetails) { 12159 if (details.startCC < refDetails.endCC && details.endCC > refDetails.startCC) { 12160 return true; 12161 } 12162 } 12163 return false; 12164 } 12165 function adjustFragmentStart(frag, sliding) { 12166 if (frag) { 12167 var start = frag.start + sliding; 12168 frag.start = frag.startPTS = start; 12169 frag.endPTS = start + frag.duration; 12170 } 12171 } 12172 function adjustSlidingStart(sliding, details) { 12173 // Update segments 12174 var fragments = details.fragments; 12175 for (var i = 0, len = fragments.length; i < len; i++) { 12176 adjustFragmentStart(fragments[i], sliding); 12177 } 12178 // Update LL-HLS parts at the end of the playlist 12179 if (details.fragmentHint) { 12180 adjustFragmentStart(details.fragmentHint, sliding); 12181 } 12182 details.alignedSliding = true; 12183 } 12184 12185 /** 12186 * Using the parameters of the last level, this function computes PTS' of the new fragments so that they form a 12187 * contiguous stream with the last fragments. 12188 * The PTS of a fragment lets Hls.js know where it fits into a stream - by knowing every PTS, we know which fragment to 12189 * download at any given time. PTS is normally computed when the fragment is demuxed, so taking this step saves us time 12190 * and an extra download. 12191 * @param lastFrag 12192 * @param lastLevel 12193 * @param details 12194 */ 12195 function alignStream(lastFrag, switchDetails, details) { 12196 if (!switchDetails) { 12197 return; 12198 }
vendor: 5,294 bytes, lines 12199-12322
12199 alignDiscontinuities(details, switchDetails); 12200 if (!details.alignedSliding && switchDetails) { 12201 // If the PTS wasn't figured out via discontinuity sequence that means there was no CC increase within the level. 12202 // Aligning via Program Date Time should therefore be reliable, since PDT should be the same within the same 12203 // discontinuity sequence. 12204 alignMediaPlaylistByPDT(details, switchDetails); 12205 } 12206 if (!details.alignedSliding && switchDetails && !details.skippedSegments) { 12207 // Try to align on sn so that we pick a better start fragment. 12208 // Do not perform this on playlists with delta updates as this is only to align levels on switch 12209 // and adjustSliding only adjusts fragments after skippedSegments. 12210 adjustSliding(switchDetails, details); 12211 } 12212 } 12213 12214 /** 12215 * Ajust the start of fragments in `details` by the difference in time between fragments of the latest 12216 * shared discontinuity sequence change. 12217 * @param lastLevel - The details of the last loaded level 12218 * @param details - The details of the new level 12219 */ 12220 function alignDiscontinuities(details, refDetails) { 12221 if (!shouldAlignOnDiscontinuities(refDetails, details)) { 12222 return; 12223 } 12224 var targetCC = Math.min(refDetails.endCC, details.endCC); 12225 var refFrag = findFirstFragWithCC(refDetails.fragments, targetCC); 12226 var frag = findFirstFragWithCC(details.fragments, targetCC); 12227 if (!refFrag || !frag) { 12228 return; 12229 } 12230 logger.log("Aligning playlist at start of dicontinuity sequence " + targetCC); 12231 var delta = refFrag.start - frag.start; 12232 adjustSlidingStart(delta, details); 12233 } 12234 12235 /** 12236 * Ensures appropriate time-alignment between renditions based on PDT. 12237 * This function assumes the timelines represented in `refDetails` are accurate, including the PDTs 12238 * for the last discontinuity sequence number shared by both playlists when present, 12239 * and uses the "wallclock"/PDT timeline as a cross-reference to `details`, adjusting the presentation 12240 * times/timelines of `details` accordingly. 12241 * Given the asynchronous nature of fetches and initial loads of live `main` and audio/subtitle tracks, 12242 * the primary purpose of this function is to ensure the "local timelines" of audio/subtitle tracks 12243 * are aligned to the main/video timeline, using PDT as the cross-reference/"anchor" that should 12244 * be consistent across playlists, per the HLS spec. 12245 * @param details - The details of the rendition you'd like to time-align (e.g. an audio rendition). 12246 * @param refDetails - The details of the reference rendition with start and PDT times for alignment. 12247 */ 12248 function alignMediaPlaylistByPDT(details, refDetails) { 12249 if (!details.hasProgramDateTime || !refDetails.hasProgramDateTime) { 12250 return; 12251 } 12252 var fragments = details.fragments; 12253 var refFragments = refDetails.fragments; 12254 if (!fragments.length || !refFragments.length) { 12255 return; 12256 } 12257 12258 // Calculate a delta to apply to all fragments according to the delta in PDT times and start times 12259 // of a fragment in the reference details, and a fragment in the target details of the same discontinuity. 12260 // If a fragment of the same discontinuity was not found use the middle fragment of both. 12261 var refFrag; 12262 var frag; 12263 var targetCC = Math.min(refDetails.endCC, details.endCC); 12264 if (refDetails.startCC < targetCC && details.startCC < targetCC) { 12265 refFrag = findFirstFragWithCC(refFragments, targetCC); 12266 frag = findFirstFragWithCC(fragments, targetCC); 12267 } 12268 if (!refFrag || !frag) { 12269 refFrag = refFragments[Math.floor(refFragments.length / 2)]; 12270 frag = findFirstFragWithCC(fragments, refFrag.cc) || fragments[Math.floor(fragments.length / 2)]; 12271 } 12272 var refPDT = refFrag.programDateTime; 12273 var targetPDT = frag.programDateTime; 12274 if (!refPDT || !targetPDT) { 12275 return; 12276 } 12277 var delta = (targetPDT - refPDT) / 1000 - (frag.start - refFrag.start); 12278 adjustSlidingStart(delta, details); 12279 } 12280 12281 var AESCrypto = /*#__PURE__*/function () { 12282 function AESCrypto(subtle, iv, aesMode) { 12283 this.subtle = void 0; 12284 this.aesIV = void 0; 12285 this.aesMode = void 0; 12286 this.subtle = subtle; 12287 this.aesIV = iv; 12288 this.aesMode = aesMode; 12289 } 12290 var _proto = AESCrypto.prototype; 12291 _proto.decrypt = function decrypt(data, key) { 12292 switch (this.aesMode) { 12293 case DecrypterAesMode.cbc: 12294 return this.subtle.decrypt({ 12295 name: 'AES-CBC', 12296 iv: this.aesIV 12297 }, key, data); 12298 case DecrypterAesMode.ctr: 12299 return this.subtle.decrypt({ 12300 name: 'AES-CTR', 12301 counter: this.aesIV, 12302 length: 64 12303 }, 12304 //64 : NIST SP800-38A standard suggests that the counter should occupy half of the counter block 12305 key, data); 12306 default: 12307 throw new Error("[AESCrypto] invalid aes mode " + this.aesMode); 12308 } 12309 }; 12310 return AESCrypto; 12311 }(); 12312 12313 var FastAESKey = /*#__PURE__*/function () { 12314 function FastAESKey(subtle, key, aesMode) { 12315 this.subtle = void 0; 12316 this.key = void 0; 12317 this.aesMode = void 0; 12318 this.subtle = subtle; 12319 this.key = key; 12320 this.aesMode = aesMode; 12321 } 12322 var _proto = FastAESKey.prototype;
vendor: 9,796 bytes, lines 12323-12580
12323 _proto.expandKey = function expandKey() { 12324 var subtleAlgoName = getSubtleAlgoName(this.aesMode); 12325 return this.subtle.importKey('raw', this.key, { 12326 name: subtleAlgoName 12327 }, false, ['encrypt', 'decrypt']); 12328 }; 12329 return FastAESKey; 12330 }(); 12331 function getSubtleAlgoName(aesMode) { 12332 switch (aesMode) { 12333 case DecrypterAesMode.cbc: 12334 return 'AES-CBC'; 12335 case DecrypterAesMode.ctr: 12336 return 'AES-CTR'; 12337 default: 12338 throw new Error("[FastAESKey] invalid aes mode " + aesMode); 12339 } 12340 } 12341 12342 // PKCS7 12343 function removePadding(array) { 12344 var outputBytes = array.byteLength; 12345 var paddingBytes = outputBytes && new DataView(array.buffer).getUint8(outputBytes - 1); 12346 if (paddingBytes) { 12347 return sliceUint8(array, 0, outputBytes - paddingBytes); 12348 } 12349 return array; 12350 } 12351 var AESDecryptor = /*#__PURE__*/function () { 12352 function AESDecryptor() { 12353 this.rcon = [0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; 12354 this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]; 12355 this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]; 12356 this.sBox = new Uint32Array(256); 12357 this.invSBox = new Uint32Array(256); 12358 this.key = new Uint32Array(0); 12359 this.ksRows = 0; 12360 this.keySize = 0; 12361 this.keySchedule = void 0; 12362 this.invKeySchedule = void 0; 12363 this.initTable(); 12364 } 12365 12366 // Using view.getUint32() also swaps the byte order. 12367 var _proto = AESDecryptor.prototype; 12368 _proto.uint8ArrayToUint32Array_ = function uint8ArrayToUint32Array_(arrayBuffer) { 12369 var view = new DataView(arrayBuffer); 12370 var newArray = new Uint32Array(4); 12371 for (var i = 0; i < 4; i++) { 12372 newArray[i] = view.getUint32(i * 4); 12373 } 12374 return newArray; 12375 }; 12376 _proto.initTable = function initTable() { 12377 var sBox = this.sBox; 12378 var invSBox = this.invSBox; 12379 var subMix = this.subMix; 12380 var subMix0 = subMix[0]; 12381 var subMix1 = subMix[1]; 12382 var subMix2 = subMix[2]; 12383 var subMix3 = subMix[3]; 12384 var invSubMix = this.invSubMix; 12385 var invSubMix0 = invSubMix[0]; 12386 var invSubMix1 = invSubMix[1]; 12387 var invSubMix2 = invSubMix[2]; 12388 var invSubMix3 = invSubMix[3]; 12389 var d = new Uint32Array(256); 12390 var x = 0; 12391 var xi = 0; 12392 var i = 0; 12393 for (i = 0; i < 256; i++) { 12394 if (i < 128) { 12395 d[i] = i << 1; 12396 } else { 12397 d[i] = i << 1 ^ 0x11b; 12398 } 12399 } 12400 for (i = 0; i < 256; i++) { 12401 var sx = xi ^ xi << 1 ^ xi << 2 ^ xi << 3 ^ xi << 4; 12402 sx = sx >>> 8 ^ sx & 0xff ^ 0x63; 12403 sBox[x] = sx; 12404 invSBox[sx] = x; 12405 12406 // Compute multiplication 12407 var x2 = d[x]; 12408 var x4 = d[x2]; 12409 var x8 = d[x4]; 12410 12411 // Compute sub/invSub bytes, mix columns tables 12412 var t = d[sx] * 0x101 ^ sx * 0x1010100; 12413 subMix0[x] = t << 24 | t >>> 8; 12414 subMix1[x] = t << 16 | t >>> 16; 12415 subMix2[x] = t << 8 | t >>> 24; 12416 subMix3[x] = t; 12417 12418 // Compute inv sub bytes, inv mix columns tables 12419 t = x8 * 0x1010101 ^ x4 * 0x10001 ^ x2 * 0x101 ^ x * 0x1010100; 12420 invSubMix0[sx] = t << 24 | t >>> 8; 12421 invSubMix1[sx] = t << 16 | t >>> 16; 12422 invSubMix2[sx] = t << 8 | t >>> 24; 12423 invSubMix3[sx] = t; 12424 12425 // Compute next counter 12426 if (!x) { 12427 x = xi = 1; 12428 } else { 12429 x = x2 ^ d[d[d[x8 ^ x2]]]; 12430 xi ^= d[d[xi]]; 12431 } 12432 } 12433 }; 12434 _proto.expandKey = function expandKey(keyBuffer) { 12435 // convert keyBuffer to Uint32Array 12436 var key = this.uint8ArrayToUint32Array_(keyBuffer); 12437 var sameKey = true; 12438 var offset = 0; 12439 while (offset < key.length && sameKey) { 12440 sameKey = key[offset] === this.key[offset]; 12441 offset++; 12442 } 12443 if (sameKey) { 12444 return; 12445 } 12446 this.key = key; 12447 var keySize = this.keySize = key.length; 12448 if (keySize !== 4 && keySize !== 6 && keySize !== 8) { 12449 throw new Error('Invalid aes key size=' + keySize); 12450 } 12451 var ksRows = this.ksRows = (keySize + 6 + 1) * 4; 12452 var ksRow; 12453 var invKsRow; 12454 var keySchedule = this.keySchedule = new Uint32Array(ksRows); 12455 var invKeySchedule = this.invKeySchedule = new Uint32Array(ksRows); 12456 var sbox = this.sBox; 12457 var rcon = this.rcon; 12458 var invSubMix = this.invSubMix; 12459 var invSubMix0 = invSubMix[0]; 12460 var invSubMix1 = invSubMix[1]; 12461 var invSubMix2 = invSubMix[2]; 12462 var invSubMix3 = invSubMix[3]; 12463 var prev; 12464 var t; 12465 for (ksRow = 0; ksRow < ksRows; ksRow++) { 12466 if (ksRow < keySize) { 12467 prev = keySchedule[ksRow] = key[ksRow]; 12468 continue; 12469 } 12470 t = prev; 12471 if (ksRow % keySize === 0) { 12472 // Rot word 12473 t = t << 8 | t >>> 24; 12474 12475 // Sub word 12476 t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff]; 12477 12478 // Mix Rcon 12479 t ^= rcon[ksRow / keySize | 0] << 24; 12480 } else if (keySize > 6 && ksRow % keySize === 4) { 12481 // Sub word 12482 t = sbox[t >>> 24] << 24 | sbox[t >>> 16 & 0xff] << 16 | sbox[t >>> 8 & 0xff] << 8 | sbox[t & 0xff]; 12483 } 12484 keySchedule[ksRow] = prev = (keySchedule[ksRow - keySize] ^ t) >>> 0; 12485 } 12486 for (invKsRow = 0; invKsRow < ksRows; invKsRow++) { 12487 ksRow = ksRows - invKsRow; 12488 if (invKsRow & 3) { 12489 t = keySchedule[ksRow]; 12490 } else { 12491 t = keySchedule[ksRow - 4]; 12492 } 12493 if (invKsRow < 4 || ksRow <= 4) { 12494 invKeySchedule[invKsRow] = t; 12495 } else { 12496 invKeySchedule[invKsRow] = invSubMix0[sbox[t >>> 24]] ^ invSubMix1[sbox[t >>> 16 & 0xff]] ^ invSubMix2[sbox[t >>> 8 & 0xff]] ^ invSubMix3[sbox[t & 0xff]]; 12497 } 12498 invKeySchedule[invKsRow] = invKeySchedule[invKsRow] >>> 0; 12499 } 12500 } 12501 12502 // Adding this as a method greatly improves performance. 12503 ; 12504 _proto.networkToHostOrderSwap = function networkToHostOrderSwap(word) { 12505 return word << 24 | (word & 0xff00) << 8 | (word & 0xff0000) >> 8 | word >>> 24; 12506 }; 12507 _proto.decrypt = function decrypt(inputArrayBuffer, offset, aesIV) { 12508 var nRounds = this.keySize + 6; 12509 var invKeySchedule = this.invKeySchedule; 12510 var invSBOX = this.invSBox; 12511 var invSubMix = this.invSubMix; 12512 var invSubMix0 = invSubMix[0]; 12513 var invSubMix1 = invSubMix[1]; 12514 var invSubMix2 = invSubMix[2]; 12515 var invSubMix3 = invSubMix[3]; 12516 var initVector = this.uint8ArrayToUint32Array_(aesIV); 12517 var initVector0 = initVector[0]; 12518 var initVector1 = initVector[1]; 12519 var initVector2 = initVector[2]; 12520 var initVector3 = initVector[3]; 12521 var inputInt32 = new Int32Array(inputArrayBuffer); 12522 var outputInt32 = new Int32Array(inputInt32.length); 12523 var t0, t1, t2, t3; 12524 var s0, s1, s2, s3; 12525 var inputWords0, inputWords1, inputWords2, inputWords3; 12526 var ksRow, i; 12527 var swapWord = this.networkToHostOrderSwap; 12528 while (offset < inputInt32.length) { 12529 inputWords0 = swapWord(inputInt32[offset]); 12530 inputWords1 = swapWord(inputInt32[offset + 1]); 12531 inputWords2 = swapWord(inputInt32[offset + 2]); 12532 inputWords3 = swapWord(inputInt32[offset + 3]); 12533 s0 = inputWords0 ^ invKeySchedule[0]; 12534 s1 = inputWords3 ^ invKeySchedule[1]; 12535 s2 = inputWords2 ^ invKeySchedule[2]; 12536 s3 = inputWords1 ^ invKeySchedule[3]; 12537 ksRow = 4; 12538 12539 // Iterate through the rounds of decryption 12540 for (i = 1; i < nRounds; i++) { 12541 t0 = invSubMix0[s0 >>> 24] ^ invSubMix1[s1 >> 16 & 0xff] ^ invSubMix2[s2 >> 8 & 0xff] ^ invSubMix3[s3 & 0xff] ^ invKeySchedule[ksRow]; 12542 t1 = invSubMix0[s1 >>> 24] ^ invSubMix1[s2 >> 16 & 0xff] ^ invSubMix2[s3 >> 8 & 0xff] ^ invSubMix3[s0 & 0xff] ^ invKeySchedule[ksRow + 1]; 12543 t2 = invSubMix0[s2 >>> 24] ^ invSubMix1[s3 >> 16 & 0xff] ^ invSubMix2[s0 >> 8 & 0xff] ^ invSubMix3[s1 & 0xff] ^ invKeySchedule[ksRow + 2]; 12544 t3 = invSubMix0[s3 >>> 24] ^ invSubMix1[s0 >> 16 & 0xff] ^ invSubMix2[s1 >> 8 & 0xff] ^ invSubMix3[s2 & 0xff] ^ invKeySchedule[ksRow + 3]; 12545 // Update state 12546 s0 = t0; 12547 s1 = t1; 12548 s2 = t2; 12549 s3 = t3; 12550 ksRow = ksRow + 4; 12551 } 12552 12553 // Shift rows, sub bytes, add round key 12554 t0 = invSBOX[s0 >>> 24] << 24 ^ invSBOX[s1 >> 16 & 0xff] << 16 ^ invSBOX[s2 >> 8 & 0xff] << 8 ^ invSBOX[s3 & 0xff] ^ invKeySchedule[ksRow]; 12555 t1 = invSBOX[s1 >>> 24] << 24 ^ invSBOX[s2 >> 16 & 0xff] << 16 ^ invSBOX[s3 >> 8 & 0xff] << 8 ^ invSBOX[s0 & 0xff] ^ invKeySchedule[ksRow + 1]; 12556 t2 = invSBOX[s2 >>> 24] << 24 ^ invSBOX[s3 >> 16 & 0xff] << 16 ^ invSBOX[s0 >> 8 & 0xff] << 8 ^ invSBOX[s1 & 0xff] ^ invKeySchedule[ksRow + 2]; 12557 t3 = invSBOX[s3 >>> 24] << 24 ^ invSBOX[s0 >> 16 & 0xff] << 16 ^ invSBOX[s1 >> 8 & 0xff] << 8 ^ invSBOX[s2 & 0xff] ^ invKeySchedule[ksRow + 3]; 12558 12559 // Write 12560 outputInt32[offset] = swapWord(t0 ^ initVector0); 12561 outputInt32[offset + 1] = swapWord(t3 ^ initVector1); 12562 outputInt32[offset + 2] = swapWord(t2 ^ initVector2); 12563 outputInt32[offset + 3] = swapWord(t1 ^ initVector3); 12564 12565 // reset initVector to last 4 unsigned int 12566 initVector0 = inputWords0; 12567 initVector1 = inputWords1; 12568 initVector2 = inputWords2; 12569 initVector3 = inputWords3; 12570 offset = offset + 4; 12571 } 12572 return outputInt32.buffer; 12573 }; 12574 return AESDecryptor; 12575 }(); 12576 12577 var CHUNK_SIZE = 16; // 16 bytes, 128 bits 12578 var Decrypter = /*#__PURE__*/function () { 12579 function Decrypter(config, _temp) { 12580 var _ref = _temp === void 0 ? {}
vendor: 52,804 bytes, lines 12580-13787
12580 : _temp, 12581 _ref$removePKCS7Paddi = _ref.removePKCS7Padding, 12582 removePKCS7Padding = _ref$removePKCS7Paddi === void 0 ? true : _ref$removePKCS7Paddi; 12583 this.logEnabled = true; 12584 this.removePKCS7Padding = void 0; 12585 this.subtle = null; 12586 this.softwareDecrypter = null; 12587 this.key = null; 12588 this.fastAesKey = null; 12589 this.remainderData = null; 12590 this.currentIV = null; 12591 this.currentResult = null; 12592 this.useSoftware = void 0; 12593 this.enableSoftwareAES = void 0; 12594 this.enableSoftwareAES = config.enableSoftwareAES; 12595 this.removePKCS7Padding = removePKCS7Padding; 12596 // built in decryptor expects PKCS7 padding 12597 if (removePKCS7Padding) { 12598 try { 12599 var browserCrypto = self.crypto; 12600 if (browserCrypto) { 12601 this.subtle = browserCrypto.subtle || browserCrypto.webkitSubtle; 12602 } 12603 } catch (e) { 12604 /* no-op */ 12605 } 12606 } 12607 this.useSoftware = !this.subtle; 12608 } 12609 var _proto = Decrypter.prototype; 12610 _proto.destroy = function destroy() { 12611 this.subtle = null; 12612 this.softwareDecrypter = null; 12613 this.key = null; 12614 this.fastAesKey = null; 12615 this.remainderData = null; 12616 this.currentIV = null; 12617 this.currentResult = null; 12618 }; 12619 _proto.isSync = function isSync() { 12620 return this.useSoftware; 12621 }; 12622 _proto.flush = function flush() { 12623 var currentResult = this.currentResult, 12624 remainderData = this.remainderData; 12625 if (!currentResult || remainderData) { 12626 this.reset(); 12627 return null; 12628 } 12629 var data = new Uint8Array(currentResult); 12630 this.reset(); 12631 if (this.removePKCS7Padding) { 12632 return removePadding(data); 12633 } 12634 return data; 12635 }; 12636 _proto.reset = function reset() { 12637 this.currentResult = null; 12638 this.currentIV = null; 12639 this.remainderData = null; 12640 if (this.softwareDecrypter) { 12641 this.softwareDecrypter = null; 12642 } 12643 }; 12644 _proto.decrypt = function decrypt(data, key, iv, aesMode) { 12645 var _this = this; 12646 if (this.useSoftware) { 12647 return new Promise(function (resolve, reject) { 12648 _this.softwareDecrypt(new Uint8Array(data), key, iv, aesMode); 12649 var decryptResult = _this.flush(); 12650 if (decryptResult) { 12651 resolve(decryptResult.buffer); 12652 } else { 12653 reject(new Error('[softwareDecrypt] Failed to decrypt data')); 12654 } 12655 }); 12656 } 12657 return this.webCryptoDecrypt(new Uint8Array(data), key, iv, aesMode); 12658 } 12659 12660 // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached 12661 // data is handled in the flush() call 12662 ; 12663 _proto.softwareDecrypt = function softwareDecrypt(data, key, iv, aesMode) { 12664 var currentIV = this.currentIV, 12665 currentResult = this.currentResult, 12666 remainderData = this.remainderData; 12667 if (aesMode !== DecrypterAesMode.cbc || key.byteLength !== 16) { 12668 logger.warn('SoftwareDecrypt: can only handle AES-128-CBC'); 12669 return null; 12670 } 12671 this.logOnce('JS AES decrypt'); 12672 // The output is staggered during progressive parsing - the current result is cached, and emitted on the next call 12673 // This is done in order to strip PKCS7 padding, which is found at the end of each segment. We only know we've reached 12674 // the end on flush(), but by that time we have already received all bytes for the segment. 12675 // Progressive decryption does not work with WebCrypto 12676 12677 if (remainderData) { 12678 data = appendUint8Array(remainderData, data); 12679 this.remainderData = null; 12680 } 12681 12682 // Byte length must be a multiple of 16 (AES-128 = 128 bit blocks = 16 bytes) 12683 var currentChunk = this.getValidChunk(data); 12684 if (!currentChunk.length) { 12685 return null; 12686 } 12687 if (currentIV) { 12688 iv = currentIV; 12689 } 12690 var softwareDecrypter = this.softwareDecrypter; 12691 if (!softwareDecrypter) { 12692 softwareDecrypter = this.softwareDecrypter = new AESDecryptor(); 12693 } 12694 softwareDecrypter.expandKey(key); 12695 var result = currentResult; 12696 this.currentResult = softwareDecrypter.decrypt(currentChunk.buffer, 0, iv); 12697 this.currentIV = sliceUint8(currentChunk, -16).buffer; 12698 if (!result) { 12699 return null; 12700 } 12701 return result; 12702 }; 12703 _proto.webCryptoDecrypt = function webCryptoDecrypt(data, key, iv, aesMode) { 12704 var _this2 = this; 12705 if (this.key !== key || !this.fastAesKey) { 12706 if (!this.subtle) { 12707 return Promise.resolve(this.onWebCryptoError(data, key, iv, aesMode)); 12708 } 12709 this.key = key; 12710 this.fastAesKey = new FastAESKey(this.subtle, key, aesMode); 12711 } 12712 return this.fastAesKey.expandKey().then(function (aesKey) { 12713 // decrypt using web crypto 12714 if (!_this2.subtle) { 12715 return Promise.reject(new Error('web crypto not initialized')); 12716 } 12717 _this2.logOnce('WebCrypto AES decrypt'); 12718 var crypto = new AESCrypto(_this2.subtle, new Uint8Array(iv), aesMode); 12719 return crypto.decrypt(data.buffer, aesKey); 12720 }).catch(function (err) { 12721 logger.warn("[decrypter]: WebCrypto Error, disable WebCrypto API, " + err.name + ": " + err.message); 12722 return _this2.onWebCryptoError(data, key, iv, aesMode); 12723 }); 12724 }; 12725 _proto.onWebCryptoError = function onWebCryptoError(data, key, iv, aesMode) { 12726 var enableSoftwareAES = this.enableSoftwareAES; 12727 if (enableSoftwareAES) { 12728 this.useSoftware = true; 12729 this.logEnabled = true; 12730 this.softwareDecrypt(data, key, iv, aesMode); 12731 var decryptResult = this.flush(); 12732 if (decryptResult) { 12733 return decryptResult.buffer; 12734 } 12735 } 12736 throw new Error('WebCrypto' + (enableSoftwareAES ? ' and softwareDecrypt' : '') + ': failed to decrypt data'); 12737 }; 12738 _proto.getValidChunk = function getValidChunk(data) { 12739 var currentChunk = data; 12740 var splitPoint = data.length - data.length % CHUNK_SIZE; 12741 if (splitPoint !== data.length) { 12742 currentChunk = sliceUint8(data, 0, splitPoint); 12743 this.remainderData = sliceUint8(data, splitPoint); 12744 } 12745 return currentChunk; 12746 }; 12747 _proto.logOnce = function logOnce(msg) { 12748 if (!this.logEnabled) { 12749 return; 12750 } 12751 logger.log("[decrypter]: " + msg); 12752 this.logEnabled = false; 12753 }; 12754 return Decrypter; 12755 }(); 12756 12757 /** 12758 * TimeRanges to string helper 12759 */ 12760 12761 var TimeRanges = { 12762 toString: function toString(r) { 12763 var log = ''; 12764 var len = r.length; 12765 for (var i = 0; i < len; i++) { 12766 log += "[" + r.start(i).toFixed(3) + "-" + r.end(i).toFixed(3) + "]"; 12767 } 12768 return log; 12769 } 12770 }; 12771 12772 var State = { 12773 STOPPED: 'STOPPED', 12774 IDLE: 'IDLE', 12775 KEY_LOADING: 'KEY_LOADING', 12776 FRAG_LOADING: 'FRAG_LOADING', 12777 FRAG_LOADING_WAITING_RETRY: 'FRAG_LOADING_WAITING_RETRY', 12778 WAITING_TRACK: 'WAITING_TRACK', 12779 PARSING: 'PARSING', 12780 PARSED: 'PARSED', 12781 ENDED: 'ENDED', 12782 ERROR: 'ERROR', 12783 WAITING_INIT_PTS: 'WAITING_INIT_PTS', 12784 WAITING_LEVEL: 'WAITING_LEVEL' 12785 }; 12786 var BaseStreamController = /*#__PURE__*/function (_TaskLoop) { 12787 function BaseStreamController(hls, fragmentTracker, keyLoader, logPrefix, playlistType) { 12788 var _this; 12789 _this = _TaskLoop.call(this, logPrefix, hls.logger) || this; 12790 _this.hls = void 0; 12791 _this.fragPrevious = null; 12792 _this.fragCurrent = null; 12793 _this.fragmentTracker = void 0; 12794 _this.transmuxer = null; 12795 _this._state = State.STOPPED; 12796 _this.playlistType = void 0; 12797 _this.media = null; 12798 _this.mediaBuffer = null; 12799 _this.config = void 0; 12800 _this.bitrateTest = false; 12801 _this.lastCurrentTime = 0; 12802 _this.nextLoadPosition = 0; 12803 _this.startPosition = 0; 12804 _this.startTimeOffset = null; 12805 _this.loadedmetadata = false; 12806 _this.retryDate = 0; 12807 _this.levels = null; 12808 _this.fragmentLoader = void 0; 12809 _this.keyLoader = void 0; 12810 _this.levelLastLoaded = null; 12811 _this.startFragRequested = false; 12812 _this.decrypter = void 0; 12813 _this.initPTS = []; 12814 _this.buffering = true; 12815 _this.loadingParts = false; 12816 _this.loopSn = void 0; 12817 _this.onMediaSeeking = function () { 12818 var _this2 = _this, 12819 config = _this2.config, 12820 fragCurrent = _this2.fragCurrent, 12821 media = _this2.media, 12822 mediaBuffer = _this2.mediaBuffer, 12823 state = _this2.state; 12824 var currentTime = media ? media.currentTime : 0; 12825 var bufferInfo = BufferHelper.bufferInfo(mediaBuffer ? mediaBuffer : media, currentTime, config.maxBufferHole); 12826 _this.log("media seeking to " + (isFiniteNumber(currentTime) ? currentTime.toFixed(3) : currentTime) + ", state: " + state); 12827 if (_this.state === State.ENDED) { 12828 _this.resetLoadingState(); 12829 } else if (fragCurrent) { 12830 // Seeking while frag load is in progress 12831 var tolerance = config.maxFragLookUpTolerance; 12832 var fragStartOffset = fragCurrent.start - tolerance; 12833 var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance; 12834 // if seeking out of buffered range or into new one 12835 if (!bufferInfo.len || fragEndOffset < bufferInfo.start || fragStartOffset > bufferInfo.end) { 12836 var pastFragment = currentTime > fragEndOffset; 12837 // if the seek position is outside the current fragment range 12838 if (currentTime < fragStartOffset || pastFragment) { 12839 if (pastFragment && fragCurrent.loader) { 12840 _this.log('seeking outside of buffer while fragment load in progress, cancel fragment load'); 12841 fragCurrent.abortRequests(); 12842 _this.resetLoadingState(); 12843 } 12844 _this.fragPrevious = null; 12845 } 12846 } 12847 } 12848 if (media) { 12849 // Remove gap fragments 12850 _this.fragmentTracker.removeFragmentsInRange(currentTime, Infinity, _this.playlistType, true); 12851 12852 // Don't set lastCurrentTime with backward seeks (allows for frag selection with strict tolerances) 12853 var lastCurrentTime = _this.lastCurrentTime; 12854 if (currentTime > lastCurrentTime) { 12855 _this.lastCurrentTime = currentTime; 12856 } 12857 if (!_this.loadingParts) { 12858 var bufferEnd = Math.max(bufferInfo.end, currentTime); 12859 var shouldLoadParts = _this.shouldLoadParts(_this.getLevelDetails(), bufferEnd); 12860 if (shouldLoadParts) { 12861 _this.log("LL-Part loading ON after seeking to " + currentTime.toFixed(2) + " with buffer @" + bufferEnd.toFixed(2)); 12862 _this.loadingParts = shouldLoadParts; 12863 } 12864 } 12865 } 12866 12867 // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target 12868 if (!_this.loadedmetadata && !bufferInfo.len) { 12869 _this.nextLoadPosition = _this.startPosition = currentTime; 12870 } 12871 12872 // Async tick to speed up processing 12873 _this.tickImmediate(); 12874 }; 12875 _this.onMediaEnded = function () { 12876 // reset startPosition and lastCurrentTime to restart playback @ stream beginning 12877 _this.startPosition = _this.lastCurrentTime = 0; 12878 if (_this.playlistType === PlaylistLevelType.MAIN) { 12879 _this.hls.trigger(Events.MEDIA_ENDED, { 12880 stalled: false 12881 }); 12882 } 12883 }; 12884 _this.playlistType = playlistType; 12885 _this.hls = hls; 12886 _this.fragmentLoader = new FragmentLoader(hls.config); 12887 _this.keyLoader = keyLoader; 12888 _this.fragmentTracker = fragmentTracker; 12889 _this.config = hls.config; 12890 _this.decrypter = new Decrypter(hls.config); 12891 return _this; 12892 } 12893 _inheritsLoose(BaseStreamController, _TaskLoop); 12894 var _proto = BaseStreamController.prototype; 12895 _proto.registerListeners = function registerListeners() { 12896 var hls = this.hls; 12897 hls.on(Events.MEDIA_ATTACHED, this.onMediaAttached, this); 12898 hls.on(Events.MEDIA_DETACHING, this.onMediaDetaching, this); 12899 hls.on(Events.MANIFEST_LOADING, this.onManifestLoading, this); 12900 hls.on(Events.MANIFEST_LOADED, this.onManifestLoaded, this); 12901 hls.on(Events.ERROR, this.onError, this); 12902 }; 12903 _proto.unregisterListeners = function unregisterListeners() { 12904 var hls = this.hls; 12905 hls.off(Events.MEDIA_ATTACHED, this.onMediaAttached, this); 12906 hls.off(Events.MEDIA_DETACHING, this.onMediaDetaching, this); 12907 hls.off(Events.MANIFEST_LOADING, this.onManifestLoading, this); 12908 hls.off(Events.MANIFEST_LOADED, this.onManifestLoaded, this); 12909 hls.off(Events.ERROR, this.onError, this); 12910 }; 12911 _proto.doTick = function doTick() { 12912 this.onTickEnd(); 12913 }; 12914 _proto.onTickEnd = function onTickEnd() {} 12915 12916 // eslint-disable-next-line @typescript-eslint/no-unused-vars 12917 ; 12918 _proto.startLoad = function startLoad(startPosition) {}; 12919 _proto.stopLoad = function stopLoad() { 12920 if (this.state === State.STOPPED) { 12921 return; 12922 } 12923 this.fragmentLoader.abort(); 12924 this.keyLoader.abort(this.playlistType); 12925 var frag = this.fragCurrent; 12926 if (frag != null && frag.loader) { 12927 frag.abortRequests(); 12928 this.fragmentTracker.removeFragment(frag); 12929 } 12930 this.resetTransmuxer(); 12931 this.fragCurrent = null; 12932 this.fragPrevious = null; 12933 this.clearInterval(); 12934 this.clearNextTick(); 12935 this.state = State.STOPPED; 12936 }; 12937 _proto.pauseBuffering = function pauseBuffering() { 12938 this.buffering = false; 12939 }; 12940 _proto.resumeBuffering = function resumeBuffering() { 12941 this.buffering = true; 12942 }; 12943 _proto._streamEnded = function _streamEnded(bufferInfo, levelDetails) { 12944 // If playlist is live, there is another buffered range after the current range, nothing buffered, media is detached, 12945 // of nothing loading/loaded return false 12946 if (levelDetails.live || bufferInfo.nextStart || !bufferInfo.end || !this.media) { 12947 return false; 12948 } 12949 var partList = levelDetails.partList; 12950 // Since the last part isn't guaranteed to correspond to the last playlist segment for Low-Latency HLS, 12951 // check instead if the last part is buffered. 12952 if (partList != null && partList.length) { 12953 var lastPart = partList[partList.length - 1]; 12954 12955 // Checking the midpoint of the part for potential margin of error and related issues. 12956 // NOTE: Technically I believe parts could yield content that is < the computed duration (including potential a duration of 0) 12957 // and still be spec-compliant, so there may still be edge cases here. Likewise, there could be issues in end of stream 12958 // part mismatches for independent audio and video playlists/segments. 12959 var lastPartBuffered = BufferHelper.isBuffered(this.media, lastPart.start + lastPart.duration / 2); 12960 return lastPartBuffered; 12961 } 12962 var playlistType = levelDetails.fragments[levelDetails.fragments.length - 1].type; 12963 return this.fragmentTracker.isEndListAppended(playlistType); 12964 }; 12965 _proto.getLevelDetails = function getLevelDetails() { 12966 if (this.levels && this.levelLastLoaded !== null) { 12967 var _this$levelLastLoaded; 12968 return (_this$levelLastLoaded = this.levelLastLoaded) == null ? void 0 : _this$levelLastLoaded.details; 12969 } 12970 }; 12971 _proto.onMediaAttached = function onMediaAttached(event, data) { 12972 var media = this.media = this.mediaBuffer = data.media; 12973 media.addEventListener('seeking', this.onMediaSeeking); 12974 media.addEventListener('ended', this.onMediaEnded); 12975 var config = this.config; 12976 if (this.levels && config.autoStartLoad && this.state === State.STOPPED) { 12977 this.startLoad(config.startPosition); 12978 } 12979 }; 12980 _proto.onMediaDetaching = function onMediaDetaching() { 12981 var media = this.media; 12982 if (media != null && media.ended) { 12983 this.log('MSE detaching and video ended, reset startPosition'); 12984 this.startPosition = this.lastCurrentTime = 0; 12985 } 12986 12987 // remove video listeners 12988 if (media) { 12989 media.removeEventListener('seeking', this.onMediaSeeking); 12990 media.removeEventListener('ended', this.onMediaEnded); 12991 } 12992 if (this.keyLoader) { 12993 this.keyLoader.detach(); 12994 } 12995 this.media = this.mediaBuffer = null; 12996 this.loopSn = undefined; 12997 this.startFragRequested = this.loadedmetadata = this.loadingParts = false; 12998 this.fragmentTracker.removeAllFragments(); 12999 this.stopLoad(); 13000 }; 13001 _proto.onManifestLoading = function onManifestLoading() { 13002 this.initPTS = []; 13003 this.levels = this.levelLastLoaded = this.fragCurrent = null; 13004 this.lastCurrentTime = this.startPosition = 0; 13005 this.startFragRequested = false; 13006 }; 13007 _proto.onError = function onError(event, data) {}; 13008 _proto.onManifestLoaded = function onManifestLoaded(event, data) { 13009 this.startTimeOffset = data.startTimeOffset; 13010 }; 13011 _proto.onHandlerDestroying = function onHandlerDestroying() { 13012 this.stopLoad(); 13013 if (this.transmuxer) { 13014 this.transmuxer.destroy(); 13015 this.transmuxer = null; 13016 } 13017 _TaskLoop.prototype.onHandlerDestroying.call(this); 13018 // @ts-ignore 13019 this.hls = this.onMediaSeeking = this.onMediaEnded = null; 13020 }; 13021 _proto.onHandlerDestroyed = function onHandlerDestroyed() { 13022 this.state = State.STOPPED; 13023 if (this.fragmentLoader) { 13024 this.fragmentLoader.destroy(); 13025 } 13026 if (this.keyLoader) { 13027 this.keyLoader.destroy(); 13028 } 13029 if (this.decrypter) { 13030 this.decrypter.destroy(); 13031 } 13032 this.hls = this.log = this.warn = this.decrypter = this.keyLoader = this.fragmentLoader = this.fragmentTracker = null; 13033 _TaskLoop.prototype.onHandlerDestroyed.call(this); 13034 }; 13035 _proto.loadFragment = function loadFragment(frag, level, targetBufferTime) { 13036 this.startFragRequested = true; 13037 this._loadFragForPlayback(frag, level, targetBufferTime); 13038 }; 13039 _proto._loadFragForPlayback = function _loadFragForPlayback(frag, level, targetBufferTime) { 13040 var _this3 = this; 13041 var progressCallback = function progressCallback(data) { 13042 if (_this3.fragContextChanged(frag)) { 13043 _this3.warn("Fragment " + frag.sn + (data.part ? ' p: ' + data.part.index : '') + " of level " + frag.level + " was dropped during download."); 13044 _this3.fragmentTracker.removeFragment(frag); 13045 return; 13046 } 13047 frag.stats.chunkCount++; 13048 _this3._handleFragmentLoadProgress(data); 13049 }; 13050 this._doFragLoad(frag, level, targetBufferTime, progressCallback).then(function (data) { 13051 if (!data) { 13052 // if we're here we probably needed to backtrack or are waiting for more parts 13053 return; 13054 } 13055 var state = _this3.state; 13056 if (_this3.fragContextChanged(frag)) { 13057 if (state === State.FRAG_LOADING || !_this3.fragCurrent && state === State.PARSING) { 13058 _this3.fragmentTracker.removeFragment(frag); 13059 _this3.state = State.IDLE; 13060 } 13061 return; 13062 } 13063 if ('payload' in data) { 13064 _this3.log("Loaded " + frag.type + " sn: " + frag.sn + " of " + _this3.playlistLabel() + " " + frag.level); 13065 _this3.hls.trigger(Events.FRAG_LOADED, data); 13066 } 13067 13068 // Pass through the whole payload; controllers not implementing progressive loading receive data from this callback 13069 _this3._handleFragmentLoadComplete(data); 13070 }).catch(function (reason) { 13071 if (_this3.state === State.STOPPED || _this3.state === State.ERROR) { 13072 return; 13073 } 13074 _this3.warn("Frag error: " + ((reason == null ? void 0 : reason.message) || reason)); 13075 _this3.resetFragmentLoading(frag); 13076 }); 13077 }; 13078 _proto.clearTrackerIfNeeded = function clearTrackerIfNeeded(frag) { 13079 var _this$mediaBuffer; 13080 var fragmentTracker = this.fragmentTracker; 13081 var fragState = fragmentTracker.getState(frag); 13082 if (fragState === FragmentState.APPENDING) { 13083 // Lower the max buffer length and try again 13084 var playlistType = frag.type; 13085 var bufferedInfo = this.getFwdBufferInfo(this.mediaBuffer, playlistType); 13086 var minForwardBufferLength = Math.max(frag.duration, bufferedInfo ? bufferedInfo.len : this.config.maxBufferLength); 13087 // If backtracking, always remove from the tracker without reducing max buffer length 13088 var backtrackFragment = this.backtrackFragment; 13089 var backtracked = backtrackFragment ? frag.sn - backtrackFragment.sn : 0; 13090 if (backtracked === 1 || this.reduceMaxBufferLength(minForwardBufferLength, frag.duration)) { 13091 fragmentTracker.removeFragment(frag); 13092 } 13093 } else if (((_this$mediaBuffer = this.mediaBuffer) == null ? void 0 : _this$mediaBuffer.buffered.length) === 0) { 13094 // Stop gap for bad tracker / buffer flush behavior 13095 fragmentTracker.removeAllFragments(); 13096 } else if (fragmentTracker.hasParts(frag.type)) { 13097 // In low latency mode, remove fragments for which only some parts were buffered 13098 fragmentTracker.detectPartialFragments({ 13099 frag: frag, 13100 part: null, 13101 stats: frag.stats, 13102 id: frag.type 13103 }); 13104 if (fragmentTracker.getState(frag) === FragmentState.PARTIAL) { 13105 fragmentTracker.removeFragment(frag); 13106 } 13107 } 13108 }; 13109 _proto.checkLiveUpdate = function checkLiveUpdate(details) { 13110 if (details.updated && !details.live) { 13111 // Live stream ended, update fragment tracker 13112 var lastFragment = details.fragments[details.fragments.length - 1]; 13113 this.fragmentTracker.detectPartialFragments({ 13114 frag: lastFragment, 13115 part: null, 13116 stats: lastFragment.stats, 13117 id: lastFragment.type 13118 }); 13119 } 13120 if (!details.fragments[0]) { 13121 details.deltaUpdateFailed = true; 13122 } 13123 }; 13124 _proto.flushMainBuffer = function flushMainBuffer(startOffset, endOffset, type) { 13125 if (type === void 0) { 13126 type = null; 13127 } 13128 if (!(startOffset - endOffset)) { 13129 return; 13130 } 13131 // When alternate audio is playing, the audio-stream-controller is responsible for the audio buffer. Otherwise, 13132 // passing a null type flushes both buffers 13133 var flushScope = { 13134 startOffset: startOffset, 13135 endOffset: endOffset, 13136 type: type 13137 }; 13138 this.hls.trigger(Events.BUFFER_FLUSHING, flushScope); 13139 }; 13140 _proto._loadInitSegment = function _loadInitSegment(frag, level) { 13141 var _this4 = this; 13142 this._doFragLoad(frag, level).then(function (data) { 13143 if (!data || _this4.fragContextChanged(frag) || !_this4.levels) { 13144 throw new Error('init load aborted'); 13145 } 13146 return data; 13147 }).then(function (data) { 13148 var hls = _this4.hls; 13149 var payload = data.payload; 13150 var decryptData = frag.decryptdata; 13151 13152 // check to see if the payload needs to be decrypted 13153 if (payload && payload.byteLength > 0 && decryptData != null && decryptData.key && decryptData.iv && isFullSegmentEncryption(decryptData.method)) { 13154 var startTime = self.performance.now(); 13155 // decrypt init segment data 13156 return _this4.decrypter.decrypt(new Uint8Array(payload), decryptData.key.buffer, decryptData.iv.buffer, getAesModeFromFullSegmentMethod(decryptData.method)).catch(function (err) { 13157 hls.trigger(Events.ERROR, { 13158 type: ErrorTypes.MEDIA_ERROR, 13159 details: ErrorDetails.FRAG_DECRYPT_ERROR, 13160 fatal: false, 13161 error: err, 13162 reason: err.message, 13163 frag: frag 13164 }); 13165 throw err; 13166 }).then(function (decryptedData) { 13167 var endTime = self.performance.now(); 13168 hls.trigger(Events.FRAG_DECRYPTED, { 13169 frag: frag, 13170 payload: decryptedData, 13171 stats: { 13172 tstart: startTime, 13173 tdecrypt: endTime 13174 } 13175 }); 13176 data.payload = decryptedData; 13177 return _this4.completeInitSegmentLoad(data); 13178 }); 13179 } 13180 return _this4.completeInitSegmentLoad(data); 13181 }).catch(function (reason) { 13182 if (_this4.state === State.STOPPED || _this4.state === State.ERROR) { 13183 return; 13184 } 13185 _this4.warn(reason); 13186 _this4.resetFragmentLoading(frag); 13187 }); 13188 }; 13189 _proto.completeInitSegmentLoad = function completeInitSegmentLoad(data) { 13190 var levels = this.levels; 13191 if (!levels) { 13192 throw new Error('init load aborted, missing levels'); 13193 } 13194 var stats = data.frag.stats; 13195 if (this.state !== State.STOPPED) { 13196 this.state = State.IDLE; 13197 } 13198 data.frag.data = new Uint8Array(data.payload); 13199 stats.parsing.start = stats.buffering.start = self.performance.now(); 13200 stats.parsing.end = stats.buffering.end = self.performance.now(); 13201 this.tick(); 13202 }; 13203 _proto.fragContextChanged = function fragContextChanged(frag) { 13204 var fragCurrent = this.fragCurrent; 13205 return !frag || !fragCurrent || frag.sn !== fragCurrent.sn || frag.level !== fragCurrent.level; 13206 }; 13207 _proto.fragBufferedComplete = function fragBufferedComplete(frag, part) { 13208 var _this$fragCurrent, _this$fragPrevious; 13209 var media = this.mediaBuffer ? this.mediaBuffer : this.media; 13210 this.log("Buffered " + frag.type + " sn: " + frag.sn + (part ? ' part: ' + part.index : '') + " of " + this.fragInfo(frag) + " > buffer:" + (media ? TimeRanges.toString(BufferHelper.getBuffered(media)) : '(detached)') + ")"); 13211 if (frag.sn !== 'initSegment') { 13212 var _this$levels; 13213 if (frag.type !== PlaylistLevelType.SUBTITLE) { 13214 var el = frag.elementaryStreams; 13215 if (!Object.keys(el).some(function (type) { 13216 return !!el[type]; 13217 })) { 13218 // empty segment 13219 this.state = State.IDLE; 13220 return; 13221 } 13222 } 13223 var level = (_this$levels = this.levels) == null ? void 0 : _this$levels[frag.level]; 13224 if (level != null && level.fragmentError) { 13225 this.log("Resetting level fragment error count of " + level.fragmentError + " on frag buffered"); 13226 level.fragmentError = 0; 13227 } 13228 } 13229 this.state = State.IDLE; 13230 if (!media) { 13231 return; 13232 } 13233 if (!this.loadedmetadata && frag.type == PlaylistLevelType.MAIN && media.buffered.length && ((_this$fragCurrent = this.fragCurrent) == null ? void 0 : _this$fragCurrent.sn) === ((_this$fragPrevious = this.fragPrevious) == null ? void 0 : _this$fragPrevious.sn)) { 13234 this.loadedmetadata = true; 13235 this.seekToStartPos(); 13236 } 13237 this.tick(); 13238 }; 13239 _proto.seekToStartPos = function seekToStartPos() {}; 13240 _proto._handleFragmentLoadComplete = function _handleFragmentLoadComplete(fragLoadedEndData) { 13241 var transmuxer = this.transmuxer; 13242 if (!transmuxer) { 13243 return; 13244 } 13245 var frag = fragLoadedEndData.frag, 13246 part = fragLoadedEndData.part, 13247 partsLoaded = fragLoadedEndData.partsLoaded; 13248 // If we did not load parts, or loaded all parts, we have complete (not partial) fragment data 13249 var complete = !partsLoaded || partsLoaded.length === 0 || partsLoaded.some(function (fragLoaded) { 13250 return !fragLoaded; 13251 }); 13252 var chunkMeta = new ChunkMetadata(frag.level, frag.sn, frag.stats.chunkCount + 1, 0, part ? part.index : -1, !complete); 13253 transmuxer.flush(chunkMeta); 13254 } 13255 13256 // eslint-disable-next-line @typescript-eslint/no-unused-vars 13257 ; 13258 _proto._handleFragmentLoadProgress = function _handleFragmentLoadProgress(frag) {}; 13259 _proto._doFragLoad = function _doFragLoad(frag, level, targetBufferTime, progressCallback) { 13260 var _frag$decryptdata, 13261 _this5 = this; 13262 if (targetBufferTime === void 0) { 13263 targetBufferTime = null; 13264 } 13265 this.fragCurrent = frag; 13266 var details = level == null ? void 0 : level.details; 13267 if (!this.levels || !details) { 13268 throw new Error("frag load aborted, missing level" + (details ? '' : ' detail') + "s"); 13269 } 13270 var keyLoadingPromise = null; 13271 if (frag.encrypted && !((_frag$decryptdata = frag.decryptdata) != null && _frag$decryptdata.key)) { 13272 this.log("Loading key for " + frag.sn + " of [" + details.startSN + "-" + details.endSN + "], " + this.playlistLabel() + " " + frag.level); 13273 this.state = State.KEY_LOADING; 13274 this.fragCurrent = frag; 13275 keyLoadingPromise = this.keyLoader.load(frag).then(function (keyLoadedData) { 13276 if (!_this5.fragContextChanged(keyLoadedData.frag)) { 13277 _this5.hls.trigger(Events.KEY_LOADED, keyLoadedData); 13278 if (_this5.state === State.KEY_LOADING) { 13279 _this5.state = State.IDLE; 13280 } 13281 return keyLoadedData; 13282 } 13283 }); 13284 this.hls.trigger(Events.KEY_LOADING, { 13285 frag: frag 13286 }); 13287 if (this.fragCurrent === null) { 13288 keyLoadingPromise = Promise.reject(new Error("frag load aborted, context changed in KEY_LOADING")); 13289 } 13290 } else if (!frag.encrypted && details.encryptedFragments.length) { 13291 this.keyLoader.loadClear(frag, details.encryptedFragments); 13292 } 13293 var fragPrevious = this.fragPrevious; 13294 if (frag.sn !== 'initSegment' && (!fragPrevious || frag.sn !== fragPrevious.sn)) { 13295 var shouldLoadParts = this.shouldLoadParts(level.details, frag.end); 13296 if (shouldLoadParts !== this.loadingParts) { 13297 this.log("LL-Part loading " + (shouldLoadParts ? 'ON' : 'OFF') + " loading sn " + (fragPrevious == null ? void 0 : fragPrevious.sn) + "->" + frag.sn); 13298 this.loadingParts = shouldLoadParts; 13299 } 13300 } 13301 targetBufferTime = Math.max(frag.start, targetBufferTime || 0); 13302 if (this.loadingParts && frag.sn !== 'initSegment') { 13303 var partList = details.partList; 13304 if (partList && progressCallback) { 13305 if (targetBufferTime > frag.end && details.fragmentHint) { 13306 frag = details.fragmentHint; 13307 } 13308 var partIndex = this.getNextPart(partList, frag, targetBufferTime); 13309 if (partIndex > -1) { 13310 var part = partList[partIndex]; 13311 this.log("Loading part sn: " + frag.sn + " p: " + part.index + " cc: " + frag.cc + " of playlist [" + details.startSN + "-" + details.endSN + "] parts [0-" + partIndex + "-" + (partList.length - 1) + "] " + this.playlistLabel() + ": " + frag.level + ", target: " + parseFloat(targetBufferTime.toFixed(3))); 13312 this.nextLoadPosition = part.start + part.duration; 13313 this.state = State.FRAG_LOADING; 13314 var _result; 13315 if (keyLoadingPromise) { 13316 _result = keyLoadingPromise.then(function (keyLoadedData) { 13317 if (!keyLoadedData || _this5.fragContextChanged(keyLoadedData.frag)) { 13318 return null; 13319 } 13320 return _this5.doFragPartsLoad(frag, part, level, progressCallback); 13321 }).catch(function (error) { 13322 return _this5.handleFragLoadError(error); 13323 }); 13324 } else { 13325 _result = this.doFragPartsLoad(frag, part, level, progressCallback).catch(function (error) { 13326 return _this5.handleFragLoadError(error); 13327 }); 13328 } 13329 this.hls.trigger(Events.FRAG_LOADING, { 13330 frag: frag, 13331 part: part, 13332 targetBufferTime: targetBufferTime 13333 }); 13334 if (this.fragCurrent === null) { 13335 return Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING parts")); 13336 } 13337 return _result; 13338 } else if (!frag.url || this.loadedEndOfParts(partList, targetBufferTime)) { 13339 // Fragment hint has no parts 13340 return Promise.resolve(null); 13341 } 13342 } 13343 } 13344 if (frag.sn !== 'initSegment' && this.loadingParts) { 13345 this.log("LL-Part loading OFF after next part miss @" + targetBufferTime.toFixed(2)); 13346 this.loadingParts = false; 13347 } else if (!frag.url) { 13348 // Selected fragment hint for part but not loading parts 13349 return Promise.resolve(null); 13350 } 13351 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))); 13352 // Don't update nextLoadPosition for fragments which are not buffered 13353 if (isFiniteNumber(frag.sn) && !this.bitrateTest) { 13354 this.nextLoadPosition = frag.start + frag.duration; 13355 } 13356 this.state = State.FRAG_LOADING; 13357 13358 // Load key before streaming fragment data 13359 var dataOnProgress = this.config.progressive; 13360 var result; 13361 if (dataOnProgress && keyLoadingPromise) { 13362 result = keyLoadingPromise.then(function (keyLoadedData) { 13363 if (!keyLoadedData || _this5.fragContextChanged(keyLoadedData == null ? void 0 : keyLoadedData.frag)) { 13364 return null; 13365 } 13366 return _this5.fragmentLoader.load(frag, progressCallback); 13367 }).catch(function (error) { 13368 return _this5.handleFragLoadError(error); 13369 }); 13370 } else { 13371 // load unencrypted fragment data with progress event, 13372 // or handle fragment result after key and fragment are finished loading 13373 result = Promise.all([this.fragmentLoader.load(frag, dataOnProgress ? progressCallback : undefined), keyLoadingPromise]).then(function (_ref) { 13374 var fragLoadedData = _ref[0]; 13375 if (!dataOnProgress && fragLoadedData && progressCallback) { 13376 progressCallback(fragLoadedData); 13377 } 13378 return fragLoadedData; 13379 }).catch(function (error) { 13380 return _this5.handleFragLoadError(error); 13381 }); 13382 } 13383 this.hls.trigger(Events.FRAG_LOADING, { 13384 frag: frag, 13385 targetBufferTime: targetBufferTime 13386 }); 13387 if (this.fragCurrent === null) { 13388 return Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING")); 13389 } 13390 return result; 13391 }; 13392 _proto.doFragPartsLoad = function doFragPartsLoad(frag, fromPart, level, progressCallback) { 13393 var _this6 = this; 13394 return new Promise(function (resolve, reject) { 13395 var _level$details; 13396 var partsLoaded = []; 13397 var initialPartList = (_level$details = level.details) == null ? void 0 : _level$details.partList; 13398 var _loadPart = function loadPart(part) { 13399 _this6.fragmentLoader.loadPart(frag, part, progressCallback).then(function (partLoadedData) { 13400 partsLoaded[part.index] = partLoadedData; 13401 var loadedPart = partLoadedData.part; 13402 _this6.hls.trigger(Events.FRAG_LOADED, partLoadedData); 13403 var nextPart = getPartWith(level.details, frag.sn, part.index + 1) || findPart(initialPartList, frag.sn, part.index + 1); 13404 if (nextPart) { 13405 _loadPart(nextPart); 13406 } else { 13407 return resolve({ 13408 frag: frag, 13409 part: loadedPart, 13410 partsLoaded: partsLoaded 13411 }); 13412 } 13413 }).catch(reject); 13414 }; 13415 _loadPart(fromPart); 13416 }); 13417 }; 13418 _proto.handleFragLoadError = function handleFragLoadError(error) { 13419 if ('data' in error) { 13420 var data = error.data; 13421 if (error.data && data.details === ErrorDetails.INTERNAL_ABORTED) { 13422 this.handleFragLoadAborted(data.frag, data.part); 13423 } else { 13424 this.hls.trigger(Events.ERROR, data); 13425 } 13426 } else { 13427 this.hls.trigger(Events.ERROR, { 13428 type: ErrorTypes.OTHER_ERROR, 13429 details: ErrorDetails.INTERNAL_EXCEPTION, 13430 err: error, 13431 error: error, 13432 fatal: true 13433 }); 13434 } 13435 return null; 13436 }; 13437 _proto._handleTransmuxerFlush = function _handleTransmuxerFlush(chunkMeta) { 13438 var context = this.getCurrentContext(chunkMeta); 13439 if (!context || this.state !== State.PARSING) { 13440 if (!this.fragCurrent && this.state !== State.STOPPED && this.state !== State.ERROR) { 13441 this.state = State.IDLE; 13442 } 13443 return; 13444 } 13445 var frag = context.frag, 13446 part = context.part, 13447 level = context.level; 13448 var now = self.performance.now(); 13449 frag.stats.parsing.end = now; 13450 if (part) { 13451 part.stats.parsing.end = now; 13452 } 13453 // See if part loading should be disabled/enabled based on buffer and playback position. 13454 var levelDetails = this.getLevelDetails(); 13455 var loadingPartsAtEdge = levelDetails && frag.sn > levelDetails.endSN; 13456 var shouldLoadParts = loadingPartsAtEdge || this.shouldLoadParts(levelDetails, frag.end); 13457 if (shouldLoadParts !== this.loadingParts) { 13458 this.log("LL-Part loading " + (shouldLoadParts ? 'ON' : 'OFF') + " after parsing segment ending @" + frag.end.toFixed(2)); 13459 this.loadingParts = shouldLoadParts; 13460 } 13461 this.updateLevelTiming(frag, part, level, chunkMeta.partial); 13462 }; 13463 _proto.shouldLoadParts = function shouldLoadParts(details, bufferEnd) { 13464 if (this.config.lowLatencyMode) { 13465 if (!details) { 13466 return this.loadingParts; 13467 } 13468 if (details != null && details.partList) { 13469 var _details$fragmentHint; 13470 // Buffer must be ahead of first part + duration of parts after last segment 13471 // and playback must be at or past segment adjacent to part list 13472 var firstPart = details.partList[0]; 13473 var safePartStart = firstPart.end + (((_details$fragmentHint = details.fragmentHint) == null ? void 0 : _details$fragmentHint.duration) || 0); 13474 if (bufferEnd >= safePartStart && this.lastCurrentTime > firstPart.start - firstPart.fragment.duration) { 13475 return true; 13476 } 13477 } 13478 } 13479 return false; 13480 }; 13481 _proto.getCurrentContext = function getCurrentContext(chunkMeta) { 13482 var levels = this.levels, 13483 fragCurrent = this.fragCurrent; 13484 var levelIndex = chunkMeta.level, 13485 sn = chunkMeta.sn, 13486 partIndex = chunkMeta.part; 13487 if (!(levels != null && levels[levelIndex])) { 13488 this.warn("Levels object was unset while buffering fragment " + sn + " of level " + levelIndex + ". The current chunk will not be buffered."); 13489 return null; 13490 } 13491 var level = levels[levelIndex]; 13492 var levelDetails = level.details; 13493 var part = partIndex > -1 ? getPartWith(levelDetails, sn, partIndex) : null; 13494 var frag = part ? part.fragment : getFragmentWithSN(levelDetails, sn, fragCurrent); 13495 if (!frag) { 13496 return null; 13497 } 13498 if (fragCurrent && fragCurrent !== frag) { 13499 frag.stats = fragCurrent.stats; 13500 } 13501 return { 13502 frag: frag, 13503 part: part, 13504 level: level 13505 }; 13506 }; 13507 _proto.bufferFragmentData = function bufferFragmentData(data, frag, part, chunkMeta, noBacktracking) { 13508 var _buffer; 13509 if (!data || this.state !== State.PARSING) { 13510 return; 13511 } 13512 var data1 = data.data1, 13513 data2 = data.data2; 13514 var buffer = data1; 13515 if (data1 && data2) { 13516 // Combine the moof + mdat so that we buffer with a single append 13517 buffer = appendUint8Array(data1, data2); 13518 } 13519 if (!((_buffer = buffer) != null && _buffer.length)) { 13520 return; 13521 } 13522 var segment = { 13523 type: data.type, 13524 frag: frag, 13525 part: part, 13526 chunkMeta: chunkMeta, 13527 parent: frag.type, 13528 data: buffer 13529 }; 13530 this.hls.trigger(Events.BUFFER_APPENDING, segment); 13531 if (data.dropped && data.independent && !part) { 13532 if (noBacktracking) { 13533 return; 13534 } 13535 // Clear buffer so that we reload previous segments sequentially if required 13536 this.flushBufferGap(frag); 13537 } 13538 }; 13539 _proto.flushBufferGap = function flushBufferGap(frag) { 13540 var media = this.media; 13541 if (!media) { 13542 return; 13543 } 13544 // If currentTime is not buffered, clear the back buffer so that we can backtrack as much as needed 13545 if (!BufferHelper.isBuffered(media, media.currentTime)) { 13546 this.flushMainBuffer(0, frag.start); 13547 return; 13548 } 13549 // Remove back-buffer without interrupting playback to allow back tracking 13550 var currentTime = media.currentTime; 13551 var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0); 13552 var fragDuration = frag.duration; 13553 var segmentFraction = Math.min(this.config.maxFragLookUpTolerance * 2, fragDuration * 0.25); 13554 var start = Math.max(Math.min(frag.start - segmentFraction, bufferInfo.end - segmentFraction), currentTime + segmentFraction); 13555 if (frag.start - start > segmentFraction) { 13556 this.flushMainBuffer(start, frag.start); 13557 } 13558 }; 13559 _proto.getFwdBufferInfo = function getFwdBufferInfo(bufferable, type) { 13560 var _this$media; 13561 var pos = this.getLoadPosition(); 13562 if (!isFiniteNumber(pos)) { 13563 return null; 13564 } 13565 var backwardSeek = this.lastCurrentTime > pos; 13566 var maxBufferHole = backwardSeek || (_this$media = this.media) != null && _this$media.paused ? 0 : this.config.maxBufferHole; 13567 return this.getFwdBufferInfoAtPos(bufferable, pos, type, maxBufferHole); 13568 }; 13569 _proto.getFwdBufferInfoAtPos = function getFwdBufferInfoAtPos(bufferable, pos, type, maxBufferHole) { 13570 var bufferInfo = BufferHelper.bufferInfo(bufferable, pos, maxBufferHole); 13571 // Workaround flaw in getting forward buffer when maxBufferHole is smaller than gap at current pos 13572 if (bufferInfo.len === 0 && bufferInfo.nextStart !== undefined) { 13573 var bufferedFragAtPos = this.fragmentTracker.getBufferedFrag(pos, type); 13574 if (bufferedFragAtPos && (bufferInfo.nextStart <= bufferedFragAtPos.end || bufferedFragAtPos.gap)) { 13575 return BufferHelper.bufferInfo(bufferable, pos, Math.max(bufferInfo.nextStart, maxBufferHole)); 13576 } 13577 } 13578 return bufferInfo; 13579 }; 13580 _proto.getMaxBufferLength = function getMaxBufferLength(levelBitrate) { 13581 var config = this.config; 13582 var maxBufLen; 13583 if (levelBitrate) { 13584 maxBufLen = Math.max(8 * config.maxBufferSize / levelBitrate, config.maxBufferLength); 13585 } else { 13586 maxBufLen = config.maxBufferLength; 13587 } 13588 return Math.min(maxBufLen, config.maxMaxBufferLength); 13589 }; 13590 _proto.reduceMaxBufferLength = function reduceMaxBufferLength(threshold, fragDuration) { 13591 var config = this.config; 13592 var minLength = Math.max(Math.min(threshold - fragDuration, config.maxBufferLength), fragDuration); 13593 var reducedLength = Math.max(threshold - fragDuration * 3, config.maxMaxBufferLength / 2, minLength); 13594 if (reducedLength >= minLength) { 13595 // reduce max buffer length as it might be too high. we do this to avoid loop flushing ... 13596 config.maxMaxBufferLength = reducedLength; 13597 this.warn("Reduce max buffer length to " + reducedLength + "s"); 13598 return true; 13599 } 13600 return false; 13601 }; 13602 _proto.getAppendedFrag = function getAppendedFrag(position, playlistType) { 13603 var _this$fragmentTracker; 13604 if (playlistType === void 0) { 13605 playlistType = PlaylistLevelType.MAIN; 13606 } 13607 var fragOrPart = (_this$fragmentTracker = this.fragmentTracker) == null ? void 0 : _this$fragmentTracker.getAppendedFrag(position, playlistType); 13608 if (fragOrPart && 'fragment' in fragOrPart) { 13609 return fragOrPart.fragment; 13610 } 13611 return fragOrPart; 13612 }; 13613 _proto.getNextFragment = function getNextFragment(pos, levelDetails) { 13614 var fragments = levelDetails.fragments; 13615 var fragLen = fragments.length; 13616 if (!fragLen) { 13617 return null; 13618 } 13619 13620 // find fragment index, contiguous with end of buffer position 13621 var config = this.config; 13622 var start = fragments[0].start; 13623 var canLoadParts = config.lowLatencyMode && !!levelDetails.partList; 13624 var frag = null; 13625 if (levelDetails.live) { 13626 var initialLiveManifestSize = config.initialLiveManifestSize; 13627 if (fragLen < initialLiveManifestSize) { 13628 this.warn("Not enough fragments to start playback (have: " + fragLen + ", need: " + initialLiveManifestSize + ")"); 13629 return null; 13630 } 13631 // The real fragment start times for a live stream are only known after the PTS range for that level is known. 13632 // In order to discover the range, we load the best matching fragment for that level and demux it. 13633 // Do not load using live logic if the starting frag is requested - we want to use getFragmentAtPosition() so that 13634 // we get the fragment matching that start time 13635 if (!levelDetails.PTSKnown && !this.startFragRequested && this.startPosition === -1 || pos < start) { 13636 if (canLoadParts && !this.loadingParts) { 13637 this.log("LL-Part loading ON for initial live fragment"); 13638 this.loadingParts = true; 13639 } 13640 frag = this.getInitialLiveFragment(levelDetails, fragments); 13641 this.startPosition = this.nextLoadPosition = frag ? this.hls.liveSyncPosition || frag.start : pos; 13642 } 13643 } else if (pos <= start) { 13644 // VoD playlist: if loadPosition before start of playlist, load first fragment 13645 frag = fragments[0]; 13646 } 13647 13648 // If we haven't run into any special cases already, just load the fragment most closely matching the requested position 13649 if (!frag) { 13650 var end = this.loadingParts ? levelDetails.partEnd : levelDetails.fragmentEnd; 13651 frag = this.getFragmentAtPosition(pos, end, levelDetails); 13652 } 13653 return this.mapToInitFragWhenRequired(frag); 13654 }; 13655 _proto.isLoopLoading = function isLoopLoading(frag, targetBufferTime) { 13656 var trackerState = this.fragmentTracker.getState(frag); 13657 return (trackerState === FragmentState.OK || trackerState === FragmentState.PARTIAL && !!frag.gap) && this.nextLoadPosition > targetBufferTime; 13658 }; 13659 _proto.getNextFragmentLoopLoading = function getNextFragmentLoopLoading(frag, levelDetails, bufferInfo, playlistType, maxBufLen) { 13660 var nextFragment = null; 13661 if (frag.gap) { 13662 nextFragment = this.getNextFragment(this.nextLoadPosition, levelDetails); 13663 if (nextFragment && !nextFragment.gap && bufferInfo.nextStart) { 13664 // Media buffered after GAP tags should not make the next buffer timerange exceed forward buffer length 13665 var nextbufferInfo = this.getFwdBufferInfoAtPos(this.mediaBuffer ? this.mediaBuffer : this.media, bufferInfo.nextStart, playlistType, 0); 13666 if (nextbufferInfo !== null && bufferInfo.len + nextbufferInfo.len >= maxBufLen) { 13667 // Returning here might result in not finding an audio and video candiate to skip to 13668 var sn = nextFragment.sn; 13669 if (this.loopSn !== sn) { 13670 this.log("buffer full after gaps in \"" + playlistType + "\" playlist starting at sn: " + sn); 13671 this.loopSn = sn; 13672 } 13673 return null; 13674 } 13675 } 13676 } 13677 this.loopSn = undefined; 13678 return nextFragment; 13679 }; 13680 _proto.mapToInitFragWhenRequired = function mapToInitFragWhenRequired(frag) { 13681 // If an initSegment is present, it must be buffered first 13682 if (frag != null && frag.initSegment && !(frag != null && frag.initSegment.data) && !this.bitrateTest) { 13683 return frag.initSegment; 13684 } 13685 return frag; 13686 }; 13687 _proto.getNextPart = function getNextPart(partList, frag, targetBufferTime) { 13688 var nextPart = -1; 13689 var contiguous = false; 13690 var independentAttrOmitted = true; 13691 for (var i = 0, len = partList.length; i < len; i++) { 13692 var part = partList[i]; 13693 independentAttrOmitted = independentAttrOmitted && !part.independent; 13694 if (nextPart > -1 && targetBufferTime < part.start) { 13695 break; 13696 } 13697 var loaded = part.loaded; 13698 if (loaded) { 13699 nextPart = -1; 13700 } else if ((contiguous || part.independent || independentAttrOmitted) && part.fragment === frag) { 13701 nextPart = i; 13702 } 13703 contiguous = loaded; 13704 } 13705 return nextPart; 13706 }; 13707 _proto.loadedEndOfParts = function loadedEndOfParts(partList, targetBufferTime) { 13708 var lastPart = partList[partList.length - 1]; 13709 return lastPart && targetBufferTime > lastPart.start && lastPart.loaded; 13710 } 13711 13712 /* 13713 This method is used find the best matching first fragment for a live playlist. This fragment is used to calculate the 13714 "sliding" of the playlist, which is its offset from the start of playback. After sliding we can compute the real 13715 start and end times for each fragment in the playlist (after which this method will not need to be called). 13716 */; 13717 _proto.getInitialLiveFragment = function getInitialLiveFragment(levelDetails, fragments) { 13718 var fragPrevious = this.fragPrevious; 13719 var frag = null; 13720 if (fragPrevious) { 13721 if (levelDetails.hasProgramDateTime) { 13722 // Prefer using PDT, because it can be accurate enough to choose the correct fragment without knowing the level sliding 13723 this.log("Live playlist, switching playlist, load frag with same PDT: " + fragPrevious.programDateTime); 13724 frag = findFragmentByPDT(fragments, fragPrevious.endProgramDateTime, this.config.maxFragLookUpTolerance); 13725 } 13726 if (!frag) { 13727 // SN does not need to be accurate between renditions, but depending on the packaging it may be so. 13728 var targetSN = fragPrevious.sn + 1; 13729 if (targetSN >= levelDetails.startSN && targetSN <= levelDetails.endSN) { 13730 var fragNext = fragments[targetSN - levelDetails.startSN]; 13731 // Ensure that we're staying within the continuity range, since PTS resets upon a new range 13732 if (fragPrevious.cc === fragNext.cc) { 13733 frag = fragNext; 13734 this.log("Live playlist, switching playlist, load frag with next SN: " + frag.sn); 13735 } 13736 } 13737 // It's important to stay within the continuity range if available; otherwise the fragments in the playlist 13738 // will have the wrong start times 13739 if (!frag) { 13740 frag = findFragWithCC(fragments, fragPrevious.cc); 13741 if (frag) { 13742 this.log("Live playlist, switching playlist, load frag with same CC: " + frag.sn); 13743 } 13744 } 13745 } 13746 } else { 13747 // Find a new start fragment when fragPrevious is null 13748 var liveStart = this.hls.liveSyncPosition; 13749 if (liveStart !== null) { 13750 frag = this.getFragmentAtPosition(liveStart, this.bitrateTest ? levelDetails.fragmentEnd : levelDetails.edge, levelDetails); 13751 } 13752 } 13753 return frag; 13754 } 13755 13756 /* 13757 This method finds the best matching fragment given the provided position. 13758 */; 13759 _proto.getFragmentAtPosition = function getFragmentAtPosition(bufferEnd, end, levelDetails) { 13760 var config = this.config; 13761 var fragPrevious = this.fragPrevious; 13762 var fragments = levelDetails.fragments, 13763 endSN = levelDetails.endSN; 13764 var fragmentHint = levelDetails.fragmentHint; 13765 var maxFragLookUpTolerance = config.maxFragLookUpTolerance; 13766 var partList = levelDetails.partList; 13767 var loadingParts = !!(this.loadingParts && partList != null && partList.length && fragmentHint); 13768 if (loadingParts && fragmentHint && !this.bitrateTest) { 13769 // Include incomplete fragment with parts at end 13770 fragments = fragments.concat(fragmentHint); 13771 endSN = fragmentHint.sn; 13772 } 13773 var frag; 13774 if (bufferEnd < end) { 13775 var _this$media2; 13776 var backwardSeek = bufferEnd < this.lastCurrentTime; 13777 var lookupTolerance = backwardSeek || bufferEnd > end - maxFragLookUpTolerance || (_this$media2 = this.media) != null && _this$media2.paused ? 0 : maxFragLookUpTolerance; 13778 // Remove the tolerance if it would put the bufferEnd past the actual end of stream 13779 // Uses buffer and sequence number to calculate switch segment (required if using EXT-X-DISCONTINUITY-SEQUENCE) 13780 frag = findFragmentByPTS(fragPrevious, fragments, bufferEnd, lookupTolerance); 13781 } else { 13782 // reach end of playlist 13783 frag = fragments[fragments.length - 1]; 13784 } 13785 if (frag) { 13786 var curSNIdx = frag.sn - levelDetails.startSN; 13787 // Move fragPrevious forward to support forcing the next fragment to load
13788 // when the buffer catches up to a previously buffered range. 13789 var fragState = this.fragmentTracker.getState(frag); 13790 if (fragState === FragmentState.OK || fragState === FragmentState.PARTIAL && frag.gap) { 13791 fragPrevious = frag; 13792 } 13793 if (fragPrevious && frag.sn === fragPrevious.sn && (!loadingParts || partList[0].fragment.sn > frag.sn)) { 13794 // Force the next fragment to load if the previous one was already selected. This can occasionally happen with 13795 // non-uniform fragment durations 13796 var sameLevel = fragPrevious && frag.level === fragPrevious.level; 13797 if (sameLevel) { 13798 var nextFrag = fragments[curSNIdx + 1]; 13799 if (frag.sn < endSN && this.fragmentTracker.getState(nextFrag) !== FragmentState.OK) { 13800 frag = nextFrag; 13801 } else { 13802 frag = null; 13803 } 13804 } 13805 } 13806 } 13807 return frag; 13808 }; 13809 _proto.synchronizeToLiveEdge = function synchronizeToLiveEdge(levelDetails) { 13810 var config = this.config, 13811 media = this.media; 13812 if (!media) { 13813 return; 13814 } 13815 var liveSyncPosition = this.hls.liveSyncPosition; 13816 var currentTime = media.currentTime; 13817 var start = levelDetails.fragments[0].start; 13818 var end = levelDetails.edge; 13819 var withinSlidingWindow = currentTime >= start - config.maxFragLookUpTolerance && currentTime <= end; 13820 // Continue if we can seek forward to sync position or if current time is outside of sliding window 13821 if (liveSyncPosition !== null && media.duration > liveSyncPosition && (currentTime < liveSyncPosition || !withinSlidingWindow)) { 13822 // Continue if buffer is starving or if current time is behind max latency 13823 var maxLatency = config.liveMaxLatencyDuration !== undefined ? config.liveMaxLatencyDuration : config.liveMaxLatencyDurationCount * levelDetails.targetduration; 13824 if (!withinSlidingWindow && media.readyState < 4 || currentTime < end - maxLatency) { 13825 if (!this.loadedmetadata) { 13826 this.nextLoadPosition = liveSyncPosition; 13827 } 13828 // Only seek if ready and there is not a significant forward buffer available for playback 13829 if (media.readyState) { 13830 this.warn("Playback: " + currentTime.toFixed(3) + " is located too far from the end of live sliding playlist: " + end + ", reset currentTime to : " + liveSyncPosition.toFixed(3)); 13831 media.currentTime = liveSyncPosition; 13832 } 13833 } 13834 } 13835 }; 13836 _proto.alignPlaylists = function alignPlaylists(details, previousDetails, switchDetails) { 13837 // FIXME: If not for `shouldAlignOnDiscontinuities` requiring fragPrevious.cc, 13838 // this could all go in level-helper mergeDetails() 13839 var length = details.fragments.length; 13840 if (!length) { 13841 this.warn("No fragments in live playlist"); 13842 return 0; 13843 } 13844 var slidingStart = details.fragments[0].start; 13845 var firstLevelLoad = !previousDetails; 13846 var aligned = details.alignedSliding && isFiniteNumber(slidingStart); 13847 if (firstLevelLoad || !aligned && !slidingStart) { 13848 var fragPrevious = this.fragPrevious; 13849 alignStream(fragPrevious, switchDetails, details); 13850 var alignedSlidingStart = details.fragments[0].start; 13851 this.log("Live playlist sliding: " + alignedSlidingStart.toFixed(2) + " start-sn: " + (previousDetails ? previousDetails.startSN : 'na') + "->" + details.startSN + " prev-sn: " + (fragPrevious ? fragPrevious.sn : 'na') + " fragments: " + length); 13852 return alignedSlidingStart; 13853 } 13854 return slidingStart; 13855 }; 13856 _proto.waitForCdnTuneIn = function waitForCdnTuneIn(details) { 13857 // Wait for Low-Latency CDN Tune-in to get an updated playlist 13858 var advancePartLimit = 3; 13859 return details.live && details.canBlockReload && details.partTarget && details.tuneInGoal > Math.max(details.partHoldBack, details.partTarget * advancePartLimit); 13860 }; 13861 _proto.setStartPosition = function setStartPosition(details, sliding) { 13862 // compute start position if set to -1. use it straight away if value is defined 13863 var startPosition = this.startPosition; 13864 if (startPosition < sliding) { 13865 startPosition = -1; 13866 } 13867 if (startPosition === -1 || this.lastCurrentTime === -1) { 13868 // Use Playlist EXT-X-START:TIME-OFFSET when set 13869 // Prioritize Multivariant Playlist offset so that main, audio, and subtitle stream-controller start times match 13870 var offsetInMultivariantPlaylist = this.startTimeOffset !== null; 13871 var startTimeOffset = offsetInMultivariantPlaylist ? this.startTimeOffset : details.startTimeOffset; 13872 if (startTimeOffset !== null && isFiniteNumber(startTimeOffset)) { 13873 startPosition = sliding + startTimeOffset; 13874 if (startTimeOffset < 0) { 13875 startPosition += details.edge; 13876 } 13877 startPosition = Math.min(Math.max(sliding, startPosition), sliding + details.totalduration); 13878 this.log("Start time offset " + startTimeOffset + " found in " + (offsetInMultivariantPlaylist ? 'multivariant' : 'media') + " playlist, adjust startPosition to " + startPosition); 13879 this.startPosition = startPosition; 13880 } else if (details.live) { 13881 // Leave this.startPosition at -1, so that we can use `getInitialLiveFragment` logic when startPosition has 13882 // not been specified via the config or an as an argument to startLoad (#3736). 13883 startPosition = this.hls.liveSyncPosition || sliding; 13884 } else { 13885 this.startPosition = startPosition = 0; 13886 } 13887 this.lastCurrentTime = startPosition; 13888 } 13889 this.nextLoadPosition = startPosition; 13890 }; 13891 _proto.getLoadPosition = function getLoadPosition() { 13892 var media = this.media; 13893 // if we have not yet loaded any fragment, start loading from start position 13894 var pos = 0; 13895 if (this.loadedmetadata && media) { 13896 pos = media.currentTime; 13897 } else if (this.nextLoadPosition) { 13898 pos = this.nextLoadPosition; 13899 } 13900 return pos; 13901 }; 13902 _proto.handleFragLoadAborted = function handleFragLoadAborted(frag, part) { 13903 if (this.transmuxer && frag.sn !== 'initSegment' && frag.stats.aborted) { 13904 this.warn("Fragment " + frag.sn + (part ? ' part ' + part.index : '') + " of level " + frag.level + " was aborted"); 13905 this.resetFragmentLoading(frag); 13906 } 13907 }; 13908 _proto.resetFragmentLoading = function resetFragmentLoading(frag) { 13909 if (!this.fragCurrent || !this.fragContextChanged(frag) && this.state !== State.FRAG_LOADING_WAITING_RETRY) { 13910 this.state = State.IDLE; 13911 } 13912 }; 13913 _proto.onFragmentOrKeyLoadError = function onFragmentOrKeyLoadError(filterType, data) { 13914 if (data.chunkMeta && !data.frag) { 13915 var context = this.getCurrentContext(data.chunkMeta); 13916 if (context) { 13917 data.frag = context.frag; 13918 } 13919 } 13920 var frag = data.frag; 13921 // Handle frag error related to caller's filterType 13922 if (!frag || frag.type !== filterType || !this.levels) { 13923 return; 13924 } 13925 if (this.fragContextChanged(frag)) { 13926 var _this$fragCurrent2; 13927 this.warn("Frag load error must match current frag to retry " + frag.url + " > " + ((_this$fragCurrent2 = this.fragCurrent) == null ? void 0 : _this$fragCurrent2.url)); 13928 return; 13929 } 13930 var gapTagEncountered = data.details === ErrorDetails.FRAG_GAP; 13931 if (gapTagEncountered) { 13932 this.fragmentTracker.fragBuffered(frag, true); 13933 } 13934 // keep retrying until the limit will be reached 13935 var errorAction = data.errorAction; 13936 var _ref2 = errorAction || {}, 13937 action = _ref2.action, 13938 _ref2$retryCount = _ref2.retryCount, 13939 retryCount = _ref2$retryCount === void 0 ? 0 : _ref2$retryCount, 13940 retryConfig = _ref2.retryConfig; 13941 if (errorAction && action === NetworkErrorAction.RetryRequest && retryConfig) { 13942 this.resetStartWhenNotLoaded(this.levelLastLoaded);
vendor: 4,357 bytes, lines 13943-14033
13943 var delay = getRetryDelay(retryConfig, retryCount); 13944 this.warn("Fragment " + frag.sn + " of " + filterType + " " + frag.level + " errored with " + data.details + ", retrying loading " + (retryCount + 1) + "/" + retryConfig.maxNumRetry + " in " + delay + "ms"); 13945 errorAction.resolved = true; 13946 this.retryDate = self.performance.now() + delay; 13947 this.state = State.FRAG_LOADING_WAITING_RETRY; 13948 } else if (retryConfig && errorAction) { 13949 this.resetFragmentErrors(filterType); 13950 if (retryCount < retryConfig.maxNumRetry) { 13951 // Network retry is skipped when level switch is preferred 13952 if (!gapTagEncountered && action !== NetworkErrorAction.RemoveAlternatePermanently) { 13953 errorAction.resolved = true; 13954 } 13955 } else { 13956 this.warn(data.details + " reached or exceeded max retry (" + retryCount + ")"); 13957 return; 13958 } 13959 } else if ((errorAction == null ? void 0 : errorAction.action) === NetworkErrorAction.SendAlternateToPenaltyBox) { 13960 this.state = State.WAITING_LEVEL; 13961 } else { 13962 this.state = State.ERROR; 13963 } 13964 // Perform next async tick sooner to speed up error action resolution 13965 this.tickImmediate(); 13966 }; 13967 _proto.reduceLengthAndFlushBuffer = function reduceLengthAndFlushBuffer(data) { 13968 // if in appending state 13969 if (this.state === State.PARSING || this.state === State.PARSED) { 13970 var frag = data.frag; 13971 var playlistType = data.parent; 13972 var bufferedInfo = this.getFwdBufferInfo(this.mediaBuffer, playlistType); 13973 // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end 13974 // reduce max buf len if current position is buffered 13975 var buffered = bufferedInfo && bufferedInfo.len > 0.5; 13976 if (buffered) { 13977 this.reduceMaxBufferLength(bufferedInfo.len, (frag == null ? void 0 : frag.duration) || 10); 13978 } 13979 var flushBuffer = !buffered; 13980 if (flushBuffer) { 13981 // current position is not buffered, but browser is still complaining about buffer full error 13982 // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708 13983 // in that case flush the whole audio buffer to recover 13984 this.warn("Buffer full error while media.currentTime is not buffered, flush " + playlistType + " buffer"); 13985 } 13986 if (frag) { 13987 this.fragmentTracker.removeFragment(frag); 13988 this.nextLoadPosition = frag.start; 13989 } 13990 this.resetLoadingState(); 13991 return flushBuffer; 13992 } 13993 return false; 13994 }; 13995 _proto.resetFragmentErrors = function resetFragmentErrors(filterType) { 13996 if (filterType === PlaylistLevelType.AUDIO) { 13997 // Reset current fragment since audio track audio is essential and may not have a fail-over track 13998 this.fragCurrent = null; 13999 } 14000 // Fragment errors that result in a level switch or redundant fail-over 14001 // should reset the stream controller state to idle 14002 if (!this.loadedmetadata) { 14003 this.startFragRequested = false; 14004 } 14005 if (this.state !== State.STOPPED) { 14006 this.state = State.IDLE; 14007 } 14008 }; 14009 _proto.afterBufferFlushed = function afterBufferFlushed(media, bufferType, playlistType) { 14010 if (!media) { 14011 return; 14012 } 14013 // After successful buffer flushing, filter flushed fragments from bufferedFrags use mediaBuffered instead of media 14014 // (so that we will check against video.buffered ranges in case of alt audio track) 14015 var bufferedTimeRanges = BufferHelper.getBuffered(media); 14016 this.fragmentTracker.detectEvictedFragments(bufferType, bufferedTimeRanges, playlistType); 14017 if (this.state === State.ENDED) { 14018 this.resetLoadingState(); 14019 } 14020 }; 14021 _proto.resetLoadingState = function resetLoadingState() { 14022 this.log('Reset loading state'); 14023 this.fragCurrent = null; 14024 this.fragPrevious = null; 14025 if (this.state !== State.STOPPED) { 14026 this.state = State.IDLE; 14027 } 14028 }; 14029 _proto.resetStartWhenNotLoaded = function resetStartWhenNotLoaded(level) { 14030 // if loadedmetadata is not set, it means that first frag request failed 14031 // in that case, reset startFragRequested flag 14032 if (!this.loadedmetadata) { 14033 this.startFragRequested = false;
14034 var details = level ? level.details : null; 14035 if (details != null && details.live) { 14036 // Update the start position and return to IDLE to recover live start 14037 this.startPosition = -1; 14038 this.setStartPosition(details, 0); 14039 this.resetLoadingState(); 14040 } else { 14041 this.nextLoadPosition = this.startPosition; 14042 } 14043 } 14044 }; 14045 _proto.resetWhenMissingContext = function resetWhenMissingContext(chunkMeta) { 14046 this.warn("The loading context changed while buffering fragment " + chunkMeta.sn + " of level " + chunkMeta.level + ". This chunk will not be buffered."); 14047 this.removeUnbufferedFrags(); 14048 this.resetStartWhenNotLoaded(this.levelLastLoaded); 14049 this.resetLoadingState(); 14050 }; 14051 _proto.removeUnbufferedFrags = function removeUnbufferedFrags(start) { 14052 if (start === void 0) { 14053 start = 0; 14054 } 14055 this.fragmentTracker.removeFragmentsInRange(start, Infinity, this.playlistType, false, true); 14056 }; 14057 _proto.updateLevelTiming = function updateLevelTiming(frag, part, level, partial) { 14058 var _this7 = this, 14059 _this$transmuxer; 14060 var details = level.details; 14061 if (!details) { 14062 this.warn('level.details undefined'); 14063 return; 14064 } 14065 var parsed = Object.keys(frag.elementaryStreams).reduce(function (result, type) { 14066 var info = frag.elementaryStreams[type]; 14067 if (info) { 14068 var parsedDuration = info.endPTS - info.startPTS; 14069 if (parsedDuration <= 0) { 14070 // Destroy the transmuxer after it's next time offset failed to advance because duration was <= 0. 14071 // The new transmuxer will be configured with a time offset matching the next fragment start, 14072 // preventing the timeline from shifting. 14073 _this7.warn("Could not parse fragment " + frag.sn + " " + type + " duration reliably (" + parsedDuration + ")"); 14074 return result || false; 14075 } 14076 var drift = partial ? 0 : updateFragPTSDTS(details, frag, info.startPTS, info.endPTS, info.startDTS, info.endDTS); 14077 _this7.hls.trigger(Events.LEVEL_PTS_UPDATED, { 14078 details: details, 14079 level: level, 14080 drift: drift, 14081 type: type, 14082 frag: frag, 14083 start: info.startPTS, 14084 end: info.endPTS 14085 }); 14086 return true; 14087 } 14088 return result; 14089 }, false); 14090 if (!parsed && ((_this$transmuxer = this.transmuxer) == null ? void 0 : _this$transmuxer.error) === null) { 14091 var error = new Error("Found no media in fragment " + frag.sn + " of level " + frag.level + " resetting transmuxer to fallback to playlist timing"); 14092 if (level.fragmentError === 0) { 14093 // Mark and track the odd empty segment as a gap to avoid reloading 14094 level.fragmentError++; 14095 frag.gap = true; 14096 this.fragmentTracker.removeFragment(frag); 14097 this.fragmentTracker.fragBuffered(frag, true); 14098 } 14099 this.warn(error.message); 14100 this.hls.trigger(Events.ERROR, { 14101 type: ErrorTypes.MEDIA_ERROR, 14102 details: ErrorDetails.FRAG_PARSING_ERROR, 14103 fatal: false, 14104 error: error, 14105 frag: frag, 14106 reason: "Found no media in msn " + frag.sn + " of level \"" + level.url + "\"" 14107 }); 14108 if (!this.hls) { 14109 return; 14110 } 14111 this.resetTransmuxer(); 14112 // For this error fallthrough. Marking parsed will allow advancing to next fragment. 14113 } 14114 this.state = State.PARSED; 14115 this.log("Parsed " + frag.type + " sn: " + frag.sn + (part ? ' part: ' + part.index : '') + " of " + this.fragInfo(frag) + ")"); 14116 this.hls.trigger(Events.FRAG_PARSED, { 14117 frag: frag, 14118 part: part 14119 }); 14120 }; 14121 _proto.playlistLabel = function playlistLabel() { 14122 return this.playlistType === PlaylistLevelType.MAIN ? 'level' : 'track'; 14123 }; 14124 _proto.fragInfo = function fragInfo(frag, pts) { 14125 var _ref3, _ref4; 14126 if (pts === void 0) { 14127 pts = true; 14128 } 14129 return this.playlistLabel() + " " + frag.level + " (frag:[" + ((_ref3 = pts ? frag.startPTS : frag.start) != null ? _ref3 : NaN).toFixed(3) + "-" + ((_ref4 = pts ? frag.endPTS : frag.end) != null ? _ref4 : NaN).toFixed(3) + "]"; 14130 }; 14131 _proto.resetTransmuxer = function resetTransmuxer() { 14132 var _this$transmuxer2; 14133 (_this$transmuxer2 = this.transmuxer) == null ? void 0 : _this$transmuxer2.reset(); 14134 }; 14135 _proto.recoverWorkerError = function recoverWorkerError(data) { 14136 if (data.event === 'demuxerWorker') { 14137 this.fragmentTracker.removeAllFragments(); 14138 if (this.transmuxer) { 14139 this.transmuxer.destroy(); 14140 this.transmuxer = null; 14141 } 14142 this.resetStartWhenNotLoaded(this.levelLastLoaded); 14143 this.resetLoadingState(); 14144 } 14145 }; 14146 return _createClass(BaseStreamController, [{ 14147 key: "state", 14148 get: function get() { 14149 return this._state; 14150 }, 14151 set: function set(nextState) { 14152 var previousState = this._state; 14153 if (previousState !== nextState) { 14154 this._state = nextState; 14155 this.log(previousState + "->" + nextState); 14156 } 14157 } 14158 }]); 14159 }(TaskLoop); 14160 14161 function getSourceBuffer() { 14162 return self.SourceBuffer || self.WebKitSourceBuffer; 14163 } 14164 function isMSESupported() { 14165 var mediaSource = getMediaSource(); 14166 if (!mediaSource) { 14167 return false;
vendor: 5,113 bytes, lines 14168-14340
14168 } 14169 14170 // if SourceBuffer is exposed ensure its API is valid 14171 // Older browsers do not expose SourceBuffer globally so checking SourceBuffer.prototype is impossible 14172 var sourceBuffer = getSourceBuffer(); 14173 return !sourceBuffer || sourceBuffer.prototype && typeof sourceBuffer.prototype.appendBuffer === 'function' && typeof sourceBuffer.prototype.remove === 'function'; 14174 } 14175 function isSupported() { 14176 if (!isMSESupported()) { 14177 return false; 14178 } 14179 var mediaSource = getMediaSource(); 14180 return typeof (mediaSource == null ? void 0 : mediaSource.isTypeSupported) === 'function' && (['avc1.42E01E,mp4a.40.2', 'av01.0.01M.08', 'vp09.00.50.08'].some(function (codecsForVideoContainer) { 14181 return mediaSource.isTypeSupported(mimeTypeForCodec(codecsForVideoContainer, 'video')); 14182 }) || ['mp4a.40.2', 'fLaC'].some(function (codecForAudioContainer) { 14183 return mediaSource.isTypeSupported(mimeTypeForCodec(codecForAudioContainer, 'audio')); 14184 })); 14185 } 14186 function changeTypeSupported() { 14187 var _sourceBuffer$prototy; 14188 var sourceBuffer = getSourceBuffer(); 14189 return typeof (sourceBuffer == null ? void 0 : (_sourceBuffer$prototy = sourceBuffer.prototype) == null ? void 0 : _sourceBuffer$prototy.changeType) === 'function'; 14190 } 14191 14192 function dummyTrack(type, inputTimeScale) { 14193 if (type === void 0) { 14194 type = ''; 14195 } 14196 if (inputTimeScale === void 0) { 14197 inputTimeScale = 90000; 14198 } 14199 return { 14200 type: type, 14201 id: -1, 14202 pid: -1, 14203 inputTimeScale: inputTimeScale, 14204 sequenceNumber: -1, 14205 samples: [], 14206 dropped: 0 14207 }; 14208 } 14209 14210 /** 14211 * Returns any adjacent ID3 tags found in data starting at offset, as one block of data 14212 * 14213 * @param data - The data to search in 14214 * @param offset - The offset at which to start searching 14215 * 14216 * @returns The block of data containing any ID3 tags found 14217 * or `undefined` if no header is found at the starting offset 14218 * 14219 * @internal 14220 * 14221 * @group ID3 14222 */ 14223 function getId3Data(data, offset) { 14224 var front = offset; 14225 var length = 0; 14226 while (isId3Header(data, offset)) { 14227 // ID3 header is 10 bytes 14228 length += 10; 14229 var size = readId3Size(data, offset + 6); 14230 length += size; 14231 if (isId3Footer(data, offset + 10)) { 14232 // ID3 footer is 10 bytes 14233 length += 10; 14234 } 14235 offset += length; 14236 } 14237 if (length > 0) { 14238 return data.subarray(front, front + length); 14239 } 14240 return undefined; 14241 } 14242 14243 /** 14244 * Read a 33 bit timestamp from an ID3 frame. 14245 * 14246 * @param timeStampFrame - the ID3 frame 14247 * 14248 * @returns The timestamp 14249 * 14250 * @internal 14251 * 14252 * @group ID3 14253 */ 14254 function readId3Timestamp(timeStampFrame) { 14255 if (timeStampFrame.data.byteLength === 8) { 14256 var data = new Uint8Array(timeStampFrame.data); 14257 // timestamp is 33 bit expressed as a big-endian eight-octet number, 14258 // with the upper 31 bits set to zero. 14259 var pts33Bit = data[3] & 0x1; 14260 var timestamp = (data[4] << 23) + (data[5] << 15) + (data[6] << 7) + data[7]; 14261 timestamp /= 45; 14262 if (pts33Bit) { 14263 timestamp += 47721858.84; 14264 } // 2^32 / 90 14265 return Math.round(timestamp); 14266 } 14267 return undefined; 14268 } 14269 14270 /** 14271 * Searches for the Elementary Stream timestamp found in the ID3 data chunk 14272 * 14273 * @param data - Block of data containing one or more ID3 tags 14274 * 14275 * @returns The timestamp 14276 * 14277 * @group ID3 14278 * 14279 * @beta 14280 */ 14281 function getId3Timestamp(data) { 14282 var frames = getId3Frames(data); 14283 for (var i = 0; i < frames.length; i++) { 14284 var frame = frames[i]; 14285 if (isId3TimestampFrame(frame)) { 14286 return readId3Timestamp(frame); 14287 } 14288 } 14289 return undefined; 14290 } 14291 14292 /** 14293 * Checks if the given data contains an ID3 tag. 14294 * 14295 * @param data - The data to check 14296 * @param offset - The offset at which to start checking 14297 * 14298 * @returns `true` if an ID3 tag is found 14299 * 14300 * @group ID3 14301 * 14302 * @beta 14303 */ 14304 function canParseId3(data, offset) { 14305 return isId3Header(data, offset) && readId3Size(data, offset + 6) + 10 <= data.length - offset; 14306 } 14307 14308 var BaseAudioDemuxer = /*#__PURE__*/function () { 14309 function BaseAudioDemuxer() { 14310 this._audioTrack = void 0; 14311 this._id3Track = void 0; 14312 this.frameIndex = 0; 14313 this.cachedData = null; 14314 this.basePTS = null; 14315 this.initPTS = null; 14316 this.lastPTS = null; 14317 } 14318 var _proto = BaseAudioDemuxer.prototype; 14319 _proto.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { 14320 this._id3Track = { 14321 type: 'id3', 14322 id: 3, 14323 pid: -1, 14324 inputTimeScale: 90000, 14325 sequenceNumber: 0, 14326 samples: [], 14327 dropped: 0 14328 }; 14329 }; 14330 _proto.resetTimeStamp = function resetTimeStamp(deaultTimestamp) { 14331 this.initPTS = deaultTimestamp; 14332 this.resetContiguity(); 14333 }; 14334 _proto.resetContiguity = function resetContiguity() { 14335 this.basePTS = null; 14336 this.lastPTS = null; 14337 this.frameIndex = 0; 14338 }; 14339 _proto.canParse = function canParse(data, offset) { 14340 return false;
14341 }; 14342 _proto.appendFrame = function appendFrame(track, data, offset) {} 14343 14344 // feed incoming data to the front of the parsing pipeline 14345 ; 14346 _proto.demux = function demux(data, timeOffset) { 14347 if (this.cachedData) { 14348 data = appendUint8Array(this.cachedData, data); 14349 this.cachedData = null; 14350 } 14351 var id3Data = getId3Data(data, 0); 14352 var offset = id3Data ? id3Data.length : 0; 14353 var lastDataIndex; 14354 var track = this._audioTrack; 14355 var id3Track = this._id3Track; 14356 var timestamp = id3Data ? getId3Timestamp(id3Data) : undefined; 14357 var length = data.length; 14358 if (this.basePTS === null || this.frameIndex === 0 && isFiniteNumber(timestamp)) { 14359 this.basePTS = initPTSFn(timestamp, timeOffset, this.initPTS); 14360 this.lastPTS = this.basePTS; 14361 } 14362 if (this.lastPTS === null) { 14363 this.lastPTS = this.basePTS; 14364 } 14365 14366 // more expressive than alternative: id3Data?.length 14367 if (id3Data && id3Data.length > 0) { 14368 id3Track.samples.push({ 14369 pts: this.lastPTS, 14370 dts: this.lastPTS, 14371 data: id3Data, 14372 type: MetadataSchema.audioId3, 14373 duration: Number.POSITIVE_INFINITY 14374 }); 14375 } 14376 while (offset < length) { 14377 if (this.canParse(data, offset)) { 14378 var frame = this.appendFrame(track, data, offset); 14379 if (frame) { 14380 this.frameIndex++; 14381 this.lastPTS = frame.sample.pts; 14382 offset += frame.length; 14383 lastDataIndex = offset; 14384 } else { 14385 offset = length; 14386 } 14387 } else if (canParseId3(data, offset)) { 14388 // after a canParse, a call to getId3Data *should* always returns some data 14389 id3Data = getId3Data(data, offset); 14390 id3Track.samples.push({ 14391 pts: this.lastPTS, 14392 dts: this.lastPTS, 14393 data: id3Data, 14394 type: MetadataSchema.audioId3, 14395 duration: Number.POSITIVE_INFINITY 14396 }); 14397 offset += id3Data.length; 14398 lastDataIndex = offset; 14399 } else { 14400 offset++; 14401 } 14402 if (offset === length && lastDataIndex !== length) { 14403 var partialData = sliceUint8(data, lastDataIndex); 14404 if (this.cachedData) { 14405 this.cachedData = appendUint8Array(this.cachedData, partialData); 14406 } else { 14407 this.cachedData = partialData; 14408 } 14409 } 14410 } 14411 return { 14412 audioTrack: track, 14413 videoTrack: dummyTrack(), 14414 id3Track: id3Track, 14415 textTrack: dummyTrack() 14416 }; 14417 }; 14418 _proto.demuxSampleAes = function demuxSampleAes(data, keyData, timeOffset) { 14419 return Promise.reject(new Error("[" + this + "] This demuxer does not support Sample-AES decryption")); 14420 }; 14421 _proto.flush = function flush(timeOffset) { 14422 // Parse cache in case of remaining frames. 14423 var cachedData = this.cachedData; 14424 if (cachedData) { 14425 this.cachedData = null; 14426 this.demux(cachedData, 0); 14427 } 14428 return { 14429 audioTrack: this._audioTrack, 14430 videoTrack: dummyTrack(), 14431 id3Track: this._id3Track, 14432 textTrack: dummyTrack() 14433 }; 14434 }; 14435 _proto.destroy = function destroy() { 14436 this.cachedData = null; 14437 // @ts-ignore 14438 this._audioTrack = this._id3Track = undefined; 14439 }; 14440 return BaseAudioDemuxer; 14441 }(); 14442 /** 14443 * Initialize PTS 14444 * <p> 14445 * use timestamp unless it is undefined, NaN or Infinity 14446 * </p> 14447 */ 14448 var initPTSFn = function initPTSFn(timestamp, timeOffset, initPTS) { 14449 if (isFiniteNumber(timestamp)) { 14450 return timestamp * 90; 14451 } 14452 var init90kHz = initPTS ? initPTS.baseTime * 90000 / initPTS.timescale : 0; 14453 return timeOffset * 90000 + init90kHz; 14454 }; 14455 14456 /** 14457 * ADTS parser helper 14458 * @link https://wiki.multimedia.cx/index.php?title=ADTS 14459 */ 14460 function getAudioConfig(observer, data, offset, audioCodec) { 14461 var adtsObjectType; 14462 var originalAdtsObjectType; 14463 var adtsExtensionSamplingIndex; 14464 var adtsChannelConfig; 14465 var config; 14466 var userAgent = navigator.userAgent.toLowerCase(); 14467 var manifestCodec = audioCodec; 14468 var adtsSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350]; 14469 // byte 2 14470 adtsObjectType = originalAdtsObjectType = ((data[offset + 2] & 0xc0) >>> 6) + 1; 14471 var adtsSamplingIndex = (data[offset + 2] & 0x3c) >>> 2; 14472 if (adtsSamplingIndex > adtsSamplingRates.length - 1) { 14473 var error = new Error("invalid ADTS sampling index:" + adtsSamplingIndex); 14474 observer.emit(Events.ERROR, Events.ERROR, { 14475 type: ErrorTypes.MEDIA_ERROR, 14476 details: ErrorDetails.FRAG_PARSING_ERROR, 14477 fatal: true, 14478 error: error, 14479 reason: error.message 14480 }); 14481 return; 14482 } 14483 adtsChannelConfig = (data[offset + 2] & 0x01) << 2; 14484 // byte 3 14485 adtsChannelConfig |= (data[offset + 3] & 0xc0) >>> 6; 14486 logger.log("manifest codec:" + audioCodec + ", ADTS type:" + adtsObjectType + ", samplingIndex:" + adtsSamplingIndex); 14487 // Firefox and Pale Moon: freq less than 24kHz = AAC SBR (HE-AAC) 14488 if (/firefox|palemoon/i.test(userAgent)) { 14489 if (adtsSamplingIndex >= 6) { 14490 adtsObjectType = 5; 14491 config = new Array(4); 14492 // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies 14493 // there is a factor 2 between frame sample rate and output sample rate 14494 // multiply frequency by 2 (see table below, equivalent to substract 3) 14495 adtsExtensionSamplingIndex = adtsSamplingIndex - 3; 14496 } else { 14497 adtsObjectType = 2; 14498 config = new Array(2); 14499 adtsExtensionSamplingIndex = adtsSamplingIndex; 14500 } 14501 // Android : always use AAC 14502 } else if (userAgent.indexOf('android') !== -1) { 14503 adtsObjectType = 2; 14504 config = new Array(2); 14505 adtsExtensionSamplingIndex = adtsSamplingIndex; 14506 } else { 14507 /* for other browsers (Chrome/Vivaldi/Opera ...) 14508 always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...) 14509 */ 14510 adtsObjectType = 5; 14511 config = new Array(4); 14512 // if (manifest codec is HE-AAC or HE-AACv2) OR (manifest codec not specified AND frequency less than 24kHz) 14513 if (audioCodec && (audioCodec.indexOf('mp4a.40.29') !== -1 || audioCodec.indexOf('mp4a.40.5') !== -1) || !audioCodec && adtsSamplingIndex >= 6) { 14514 // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies 14515 // there is a factor 2 between frame sample rate and output sample rate 14516 // multiply frequency by 2 (see table below, equivalent to substract 3) 14517 adtsExtensionSamplingIndex = adtsSamplingIndex - 3; 14518 } else { 14519 // if (manifest codec is AAC) AND (frequency less than 24kHz AND nb channel is 1) OR (manifest codec not specified and mono audio) 14520 // Chrome fails to play back with low frequency AAC LC mono when initialized with HE-AAC. This is not a problem with stereo. 14521 if (audioCodec && audioCodec.indexOf('mp4a.40.2') !== -1 && (adtsSamplingIndex >= 6 && adtsChannelConfig === 1 || /vivaldi/i.test(userAgent)) || !audioCodec && adtsChannelConfig === 1) { 14522 adtsObjectType = 2; 14523 config = new Array(2); 14524 } 14525 adtsExtensionSamplingIndex = adtsSamplingIndex; 14526 } 14527 } 14528 /* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config 14529 ISO 14496-3 (AAC).pdf - Table 1.13 — Syntax of AudioSpecificConfig() 14530 Audio Profile / Audio Object Type
vendor: 8,984 bytes, lines 14531-14795
14531 0: Null 14532 1: AAC Main 14533 2: AAC LC (Low Complexity) 14534 3: AAC SSR (Scalable Sample Rate) 14535 4: AAC LTP (Long Term Prediction) 14536 5: SBR (Spectral Band Replication) 14537 6: AAC Scalable 14538 sampling freq 14539 0: 96000 Hz 14540 1: 88200 Hz 14541 2: 64000 Hz 14542 3: 48000 Hz 14543 4: 44100 Hz 14544 5: 32000 Hz 14545 6: 24000 Hz 14546 7: 22050 Hz 14547 8: 16000 Hz 14548 9: 12000 Hz 14549 10: 11025 Hz 14550 11: 8000 Hz 14551 12: 7350 Hz 14552 13: Reserved 14553 14: Reserved 14554 15: frequency is written explictly 14555 Channel Configurations 14556 These are the channel configurations: 14557 0: Defined in AOT Specifc Config 14558 1: 1 channel: front-center 14559 2: 2 channels: front-left, front-right 14560 */ 14561 // audioObjectType = profile => profile, the MPEG-4 Audio Object Type minus 1 14562 config[0] = adtsObjectType << 3; 14563 // samplingFrequencyIndex 14564 config[0] |= (adtsSamplingIndex & 0x0e) >> 1; 14565 config[1] |= (adtsSamplingIndex & 0x01) << 7; 14566 // channelConfiguration 14567 config[1] |= adtsChannelConfig << 3; 14568 if (adtsObjectType === 5) { 14569 // adtsExtensionSamplingIndex 14570 config[1] |= (adtsExtensionSamplingIndex & 0x0e) >> 1; 14571 config[2] = (adtsExtensionSamplingIndex & 0x01) << 7; 14572 // adtsObjectType (force to 2, chrome is checking that object type is less than 5 ??? 14573 // https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc 14574 config[2] |= 2 << 2; 14575 config[3] = 0; 14576 } 14577 return { 14578 config: config, 14579 samplerate: adtsSamplingRates[adtsSamplingIndex], 14580 channelCount: adtsChannelConfig, 14581 codec: 'mp4a.40.' + adtsObjectType, 14582 parsedCodec: 'mp4a.40.' + originalAdtsObjectType, 14583 manifestCodec: manifestCodec 14584 }; 14585 } 14586 function isHeaderPattern$1(data, offset) { 14587 return data[offset] === 0xff && (data[offset + 1] & 0xf6) === 0xf0; 14588 } 14589 function getHeaderLength(data, offset) { 14590 return data[offset + 1] & 0x01 ? 7 : 9; 14591 } 14592 function getFullFrameLength(data, offset) { 14593 return (data[offset + 3] & 0x03) << 11 | data[offset + 4] << 3 | (data[offset + 5] & 0xe0) >>> 5; 14594 } 14595 function canGetFrameLength(data, offset) { 14596 return offset + 5 < data.length; 14597 } 14598 function isHeader$1(data, offset) { 14599 // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1 14600 // Layer bits (position 14 and 15) in header should be always 0 for ADTS 14601 // More info https://wiki.multimedia.cx/index.php?title=ADTS 14602 return offset + 1 < data.length && isHeaderPattern$1(data, offset); 14603 } 14604 function canParse$1(data, offset) { 14605 return canGetFrameLength(data, offset) && isHeaderPattern$1(data, offset) && getFullFrameLength(data, offset) <= data.length - offset; 14606 } 14607 function probe$1(data, offset) { 14608 // same as isHeader but we also check that ADTS frame follows last ADTS frame 14609 // or end of data is reached 14610 if (isHeader$1(data, offset)) { 14611 // ADTS header Length 14612 var headerLength = getHeaderLength(data, offset); 14613 if (offset + headerLength >= data.length) { 14614 return false; 14615 } 14616 // ADTS frame Length 14617 var frameLength = getFullFrameLength(data, offset); 14618 if (frameLength <= headerLength) { 14619 return false; 14620 } 14621 var newOffset = offset + frameLength; 14622 return newOffset === data.length || isHeader$1(data, newOffset); 14623 } 14624 return false; 14625 } 14626 function initTrackConfig(track, observer, data, offset, audioCodec) { 14627 if (!track.samplerate) { 14628 var config = getAudioConfig(observer, data, offset, audioCodec); 14629 if (!config) { 14630 return; 14631 } 14632 track.config = config.config; 14633 track.samplerate = config.samplerate; 14634 track.channelCount = config.channelCount; 14635 track.codec = config.codec; 14636 track.manifestCodec = config.manifestCodec; 14637 track.parsedCodec = config.parsedCodec; 14638 logger.log("parsed codec:" + track.parsedCodec + ", codec:" + track.codec + ", rate:" + config.samplerate + ", channels:" + config.channelCount); 14639 } 14640 } 14641 function getFrameDuration(samplerate) { 14642 return 1024 * 90000 / samplerate; 14643 } 14644 function parseFrameHeader(data, offset) { 14645 // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header 14646 var headerLength = getHeaderLength(data, offset); 14647 if (offset + headerLength <= data.length) { 14648 // retrieve frame size 14649 var frameLength = getFullFrameLength(data, offset) - headerLength; 14650 if (frameLength > 0) { 14651 // logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}`); 14652 return { 14653 headerLength: headerLength, 14654 frameLength: frameLength 14655 }; 14656 } 14657 } 14658 } 14659 function appendFrame$1(track, data, offset, pts, frameIndex) { 14660 var frameDuration = getFrameDuration(track.samplerate); 14661 var stamp = pts + frameIndex * frameDuration; 14662 var header = parseFrameHeader(data, offset); 14663 var unit; 14664 if (header) { 14665 var frameLength = header.frameLength, 14666 headerLength = header.headerLength; 14667 var _length = headerLength + frameLength; 14668 var missing = Math.max(0, offset + _length - data.length); 14669 // logger.log(`AAC frame ${frameIndex}, pts:${stamp} length@offset/total: ${frameLength}@${offset+headerLength}/${data.byteLength} missing: ${missing}`); 14670 if (missing) { 14671 unit = new Uint8Array(_length - headerLength); 14672 unit.set(data.subarray(offset + headerLength, data.length), 0); 14673 } else { 14674 unit = data.subarray(offset + headerLength, offset + _length); 14675 } 14676 var _sample = { 14677 unit: unit, 14678 pts: stamp 14679 }; 14680 if (!missing) { 14681 track.samples.push(_sample); 14682 } 14683 return { 14684 sample: _sample, 14685 length: _length, 14686 missing: missing 14687 }; 14688 } 14689 // overflow incomplete header 14690 var length = data.length - offset; 14691 unit = new Uint8Array(length); 14692 unit.set(data.subarray(offset, data.length), 0); 14693 var sample = { 14694 unit: unit, 14695 pts: stamp 14696 }; 14697 return { 14698 sample: sample, 14699 length: length, 14700 missing: -1 14701 }; 14702 } 14703 14704 /** 14705 * MPEG parser helper 14706 */ 14707 14708 var chromeVersion$1 = null; 14709 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]; 14710 var SamplingRateMap = [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000]; 14711 var SamplesCoefficients = [ 14712 // MPEG 2.5 14713 [0, 14714 // Reserved 14715 72, 14716 // Layer3 14717 144, 14718 // Layer2 14719 12 // Layer1 14720 ], 14721 // Reserved 14722 [0, 14723 // Reserved 14724 0, 14725 // Layer3 14726 0, 14727 // Layer2 14728 0 // Layer1 14729 ], 14730 // MPEG 2 14731 [0, 14732 // Reserved 14733 72, 14734 // Layer3 14735 144, 14736 // Layer2 14737 12 // Layer1 14738 ], 14739 // MPEG 1 14740 [0, 14741 // Reserved 14742 144, 14743 // Layer3 14744 144, 14745 // Layer2 14746 12 // Layer1 14747 ]]; 14748 var BytesInSlot = [0, 14749 // Reserved 14750 1, 14751 // Layer3 14752 1, 14753 // Layer2 14754 4 // Layer1 14755 ]; 14756 function appendFrame(track, data, offset, pts, frameIndex) { 14757 // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference 14758 if (offset + 24 > data.length) { 14759 return; 14760 } 14761 var header = parseHeader(data, offset); 14762 if (header && offset + header.frameLength <= data.length) { 14763 var frameDuration = header.samplesPerFrame * 90000 / header.sampleRate; 14764 var stamp = pts + frameIndex * frameDuration; 14765 var sample = { 14766 unit: data.subarray(offset, offset + header.frameLength), 14767 pts: stamp, 14768 dts: stamp 14769 }; 14770 track.config = []; 14771 track.channelCount = header.channelCount; 14772 track.samplerate = header.sampleRate; 14773 track.samples.push(sample); 14774 return { 14775 sample: sample, 14776 length: header.frameLength, 14777 missing: 0 14778 }; 14779 } 14780 } 14781 function parseHeader(data, offset) { 14782 var mpegVersion = data[offset + 1] >> 3 & 3; 14783 var mpegLayer = data[offset + 1] >> 1 & 3; 14784 var bitRateIndex = data[offset + 2] >> 4 & 15; 14785 var sampleRateIndex = data[offset + 2] >> 2 & 3; 14786 if (mpegVersion !== 1 && bitRateIndex !== 0 && bitRateIndex !== 15 && sampleRateIndex !== 3) { 14787 var paddingBit = data[offset + 2] >> 1 & 1; 14788 var channelMode = data[offset + 3] >> 6; 14789 var columnInBitrates = mpegVersion === 3 ? 3 - mpegLayer : mpegLayer === 3 ? 3 : 4; 14790 var bitRate = BitratesMap[columnInBitrates * 14 + bitRateIndex - 1] * 1000; 14791 var columnInSampleRates = mpegVersion === 3 ? 0 : mpegVersion === 2 ? 1 : 2; 14792 var sampleRate = SamplingRateMap[columnInSampleRates * 3 + sampleRateIndex]; 14793 var channelCount = channelMode === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono) 14794 var sampleCoefficient = SamplesCoefficients[mpegVersion][mpegLayer]; 14795 var bytesInSlot = BytesInSlot[mpegLayer];
vendor: 16,679 bytes, lines 14796-15227
14796 var samplesPerFrame = sampleCoefficient * 8 * bytesInSlot; 14797 var frameLength = Math.floor(sampleCoefficient * bitRate / sampleRate + paddingBit) * bytesInSlot; 14798 if (chromeVersion$1 === null) { 14799 var userAgent = navigator.userAgent || ''; 14800 var result = userAgent.match(/Chrome\/(\d+)/i); 14801 chromeVersion$1 = result ? parseInt(result[1]) : 0; 14802 } 14803 var needChromeFix = !!chromeVersion$1 && chromeVersion$1 <= 87; 14804 if (needChromeFix && mpegLayer === 2 && bitRate >= 224000 && channelMode === 0) { 14805 // Work around bug in Chromium by setting channelMode to dual-channel (01) instead of stereo (00) 14806 data[offset + 3] = data[offset + 3] | 0x80; 14807 } 14808 return { 14809 sampleRate: sampleRate, 14810 channelCount: channelCount, 14811 frameLength: frameLength, 14812 samplesPerFrame: samplesPerFrame 14813 }; 14814 } 14815 } 14816 function isHeaderPattern(data, offset) { 14817 return data[offset] === 0xff && (data[offset + 1] & 0xe0) === 0xe0 && (data[offset + 1] & 0x06) !== 0x00; 14818 } 14819 function isHeader(data, offset) { 14820 // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1 14821 // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) 14822 // More info http://www.mp3-tech.org/programmer/frame_header.html 14823 return offset + 1 < data.length && isHeaderPattern(data, offset); 14824 } 14825 function canParse(data, offset) { 14826 var headerSize = 4; 14827 return isHeaderPattern(data, offset) && headerSize <= data.length - offset; 14828 } 14829 function probe(data, offset) { 14830 // same as isHeader but we also check that MPEG frame follows last MPEG frame 14831 // or end of data is reached 14832 if (offset + 1 < data.length && isHeaderPattern(data, offset)) { 14833 // MPEG header Length 14834 var headerLength = 4; 14835 // MPEG frame Length 14836 var header = parseHeader(data, offset); 14837 var frameLength = headerLength; 14838 if (header != null && header.frameLength) { 14839 frameLength = header.frameLength; 14840 } 14841 var newOffset = offset + frameLength; 14842 return newOffset === data.length || isHeader(data, newOffset); 14843 } 14844 return false; 14845 } 14846 14847 var AACDemuxer = /*#__PURE__*/function (_BaseAudioDemuxer) { 14848 function AACDemuxer(observer, config) { 14849 var _this; 14850 _this = _BaseAudioDemuxer.call(this) || this; 14851 _this.observer = void 0; 14852 _this.config = void 0; 14853 _this.observer = observer; 14854 _this.config = config; 14855 return _this; 14856 } 14857 _inheritsLoose(AACDemuxer, _BaseAudioDemuxer); 14858 var _proto = AACDemuxer.prototype; 14859 _proto.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { 14860 _BaseAudioDemuxer.prototype.resetInitSegment.call(this, initSegment, audioCodec, videoCodec, trackDuration); 14861 this._audioTrack = { 14862 container: 'audio/adts', 14863 type: 'audio', 14864 id: 2, 14865 pid: -1, 14866 sequenceNumber: 0, 14867 segmentCodec: 'aac', 14868 samples: [], 14869 manifestCodec: audioCodec, 14870 duration: trackDuration, 14871 inputTimeScale: 90000, 14872 dropped: 0 14873 }; 14874 } 14875 14876 // Source for probe info - https://wiki.multimedia.cx/index.php?title=ADTS 14877 ; 14878 AACDemuxer.probe = function probe$2(data, logger) { 14879 if (!data) { 14880 return false; 14881 } 14882 14883 // Check for the ADTS sync word 14884 // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1 14885 // Layer bits (position 14 and 15) in header should be always 0 for ADTS 14886 // More info https://wiki.multimedia.cx/index.php?title=ADTS 14887 var id3Data = getId3Data(data, 0); 14888 var offset = (id3Data == null ? void 0 : id3Data.length) || 0; 14889 if (probe(data, offset)) { 14890 return false; 14891 } 14892 for (var length = data.length; offset < length; offset++) { 14893 if (probe$1(data, offset)) { 14894 logger.log('ADTS sync word found !'); 14895 return true; 14896 } 14897 } 14898 return false; 14899 }; 14900 _proto.canParse = function canParse(data, offset) { 14901 return canParse$1(data, offset); 14902 }; 14903 _proto.appendFrame = function appendFrame(track, data, offset) { 14904 initTrackConfig(track, this.observer, data, offset, track.manifestCodec); 14905 var frame = appendFrame$1(track, data, offset, this.basePTS, this.frameIndex); 14906 if (frame && frame.missing === 0) { 14907 return frame; 14908 } 14909 }; 14910 return AACDemuxer; 14911 }(BaseAudioDemuxer); 14912 14913 var emsgSchemePattern = /\/emsg[-/]ID3/i; 14914 var MP4Demuxer = /*#__PURE__*/function () { 14915 function MP4Demuxer(observer, config) { 14916 this.remainderData = null; 14917 this.timeOffset = 0; 14918 this.config = void 0; 14919 this.videoTrack = void 0; 14920 this.audioTrack = void 0; 14921 this.id3Track = void 0; 14922 this.txtTrack = void 0; 14923 this.config = config; 14924 } 14925 var _proto = MP4Demuxer.prototype; 14926 _proto.resetTimeStamp = function resetTimeStamp() {}; 14927 _proto.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { 14928 var videoTrack = this.videoTrack = dummyTrack('video', 1); 14929 var audioTrack = this.audioTrack = dummyTrack('audio', 1); 14930 var captionTrack = this.txtTrack = dummyTrack('text', 1); 14931 this.id3Track = dummyTrack('id3', 1); 14932 this.timeOffset = 0; 14933 if (!(initSegment != null && initSegment.byteLength)) { 14934 return; 14935 } 14936 var initData = parseInitSegment(initSegment); 14937 if (initData.video) { 14938 var _initData$video = initData.video, 14939 id = _initData$video.id, 14940 timescale = _initData$video.timescale, 14941 codec = _initData$video.codec; 14942 videoTrack.id = id; 14943 videoTrack.timescale = captionTrack.timescale = timescale; 14944 videoTrack.codec = codec; 14945 } 14946 if (initData.audio) { 14947 var _initData$audio = initData.audio, 14948 _id = _initData$audio.id, 14949 _timescale = _initData$audio.timescale, 14950 _codec = _initData$audio.codec; 14951 audioTrack.id = _id; 14952 audioTrack.timescale = _timescale; 14953 audioTrack.codec = _codec; 14954 } 14955 captionTrack.id = RemuxerTrackIdConfig.text; 14956 videoTrack.sampleDuration = 0; 14957 videoTrack.duration = audioTrack.duration = trackDuration; 14958 }; 14959 _proto.resetContiguity = function resetContiguity() { 14960 this.remainderData = null; 14961 }; 14962 MP4Demuxer.probe = function probe(data) { 14963 return hasMoofData(data); 14964 }; 14965 _proto.demux = function demux(data, timeOffset) { 14966 this.timeOffset = timeOffset; 14967 // Load all data into the avc track. The CMAF remuxer will look for the data in the samples object; the rest of the fields do not matter 14968 var videoSamples = data; 14969 var videoTrack = this.videoTrack; 14970 var textTrack = this.txtTrack; 14971 if (this.config.progressive) { 14972 // Split the bytestream into two ranges: one encompassing all data up until the start of the last moof, and everything else. 14973 // This is done to guarantee that we're sending valid data to MSE - when demuxing progressively, we have no guarantee 14974 // that the fetch loader gives us flush moof+mdat pairs. If we push jagged data to MSE, it will throw an exception. 14975 if (this.remainderData) { 14976 videoSamples = appendUint8Array(this.remainderData, data); 14977 } 14978 var segmentedData = segmentValidRange(videoSamples); 14979 this.remainderData = segmentedData.remainder; 14980 videoTrack.samples = segmentedData.valid || new Uint8Array(); 14981 } else { 14982 videoTrack.samples = videoSamples; 14983 } 14984 var id3Track = this.extractID3Track(videoTrack, timeOffset); 14985 textTrack.samples = parseSamples(timeOffset, videoTrack); 14986 return { 14987 videoTrack: videoTrack, 14988 audioTrack: this.audioTrack, 14989 id3Track: id3Track, 14990 textTrack: this.txtTrack 14991 }; 14992 }; 14993 _proto.flush = function flush() { 14994 var timeOffset = this.timeOffset; 14995 var videoTrack = this.videoTrack; 14996 var textTrack = this.txtTrack; 14997 videoTrack.samples = this.remainderData || new Uint8Array(); 14998 this.remainderData = null; 14999 var id3Track = this.extractID3Track(videoTrack, this.timeOffset); 15000 textTrack.samples = parseSamples(timeOffset, videoTrack); 15001 return { 15002 videoTrack: videoTrack, 15003 audioTrack: dummyTrack(), 15004 id3Track: id3Track, 15005 textTrack: dummyTrack() 15006 }; 15007 }; 15008 _proto.extractID3Track = function extractID3Track(videoTrack, timeOffset) { 15009 var id3Track = this.id3Track; 15010 if (videoTrack.samples.length) { 15011 var emsgs = findBox(videoTrack.samples, ['emsg']); 15012 if (emsgs) { 15013 emsgs.forEach(function (data) { 15014 var emsgInfo = parseEmsg(data); 15015 if (emsgSchemePattern.test(emsgInfo.schemeIdUri)) { 15016 var pts = isFiniteNumber(emsgInfo.presentationTime) ? emsgInfo.presentationTime / emsgInfo.timeScale : timeOffset + emsgInfo.presentationTimeDelta / emsgInfo.timeScale; 15017 var duration = emsgInfo.eventDuration === 0xffffffff ? Number.POSITIVE_INFINITY : emsgInfo.eventDuration / emsgInfo.timeScale; 15018 // Safari takes anything <= 0.001 seconds and maps it to Infinity 15019 if (duration <= 0.001) { 15020 duration = Number.POSITIVE_INFINITY; 15021 } 15022 var payload = emsgInfo.payload; 15023 id3Track.samples.push({ 15024 data: payload, 15025 len: payload.byteLength, 15026 dts: pts, 15027 pts: pts, 15028 type: MetadataSchema.emsg, 15029 duration: duration 15030 }); 15031 } 15032 }); 15033 } 15034 } 15035 return id3Track; 15036 }; 15037 _proto.demuxSampleAes = function demuxSampleAes(data, keyData, timeOffset) { 15038 return Promise.reject(new Error('The MP4 demuxer does not support SAMPLE-AES decryption')); 15039 }; 15040 _proto.destroy = function destroy() { 15041 // @ts-ignore 15042 this.config = null; 15043 this.remainderData = null; 15044 this.videoTrack = this.audioTrack = this.id3Track = this.txtTrack = undefined; 15045 }; 15046 return MP4Demuxer; 15047 }(); 15048 15049 var getAudioBSID = function getAudioBSID(data, offset) { 15050 // check the bsid to confirm ac-3 | ec-3 15051 var bsid = 0; 15052 var numBits = 5; 15053 offset += numBits; 15054 var temp = new Uint32Array(1); // unsigned 32 bit for temporary storage 15055 var mask = new Uint32Array(1); // unsigned 32 bit mask value 15056 var _byte = new Uint8Array(1); // unsigned 8 bit for temporary storage 15057 while (numBits > 0) { 15058 _byte[0] = data[offset]; 15059 // read remaining bits, upto 8 bits at a time 15060 var bits = Math.min(numBits, 8); 15061 var shift = 8 - bits; 15062 mask[0] = 0xff000000 >>> 24 + shift << shift; 15063 temp[0] = (_byte[0] & mask[0]) >> shift; 15064 bsid = !bsid ? temp[0] : bsid << bits | temp[0]; 15065 offset += 1; 15066 numBits -= bits; 15067 } 15068 return bsid; 15069 }; 15070 15071 var BaseVideoParser = /*#__PURE__*/function () { 15072 function BaseVideoParser() { 15073 this.VideoSample = null; 15074 } 15075 var _proto = BaseVideoParser.prototype; 15076 _proto.createVideoSample = function createVideoSample(key, pts, dts) { 15077 return { 15078 key: key, 15079 frame: false, 15080 pts: pts, 15081 dts: dts, 15082 units: [], 15083 length: 0 15084 }; 15085 }; 15086 _proto.getLastNalUnit = function getLastNalUnit(samples) { 15087 var _VideoSample; 15088 var VideoSample = this.VideoSample; 15089 var lastUnit; 15090 // try to fallback to previous sample if current one is empty 15091 if (!VideoSample || VideoSample.units.length === 0) { 15092 VideoSample = samples[samples.length - 1]; 15093 } 15094 if ((_VideoSample = VideoSample) != null && _VideoSample.units) { 15095 var units = VideoSample.units; 15096 lastUnit = units[units.length - 1]; 15097 } 15098 return lastUnit; 15099 }; 15100 _proto.pushAccessUnit = function pushAccessUnit(VideoSample, videoTrack) { 15101 if (VideoSample.units.length && VideoSample.frame) { 15102 // if sample does not have PTS/DTS, patch with last sample PTS/DTS 15103 if (VideoSample.pts === undefined) { 15104 var samples = videoTrack.samples; 15105 var nbSamples = samples.length; 15106 if (nbSamples) { 15107 var lastSample = samples[nbSamples - 1]; 15108 VideoSample.pts = lastSample.pts; 15109 VideoSample.dts = lastSample.dts; 15110 } else { 15111 // dropping samples, no timestamp found 15112 videoTrack.dropped++; 15113 return; 15114 } 15115 } 15116 videoTrack.samples.push(VideoSample); 15117 } 15118 }; 15119 _proto.parseNALu = function parseNALu(track, array, endOfSegment) { 15120 var len = array.byteLength; 15121 var state = track.naluState || 0; 15122 var lastState = state; 15123 var units = []; 15124 var i = 0; 15125 var value; 15126 var overflow; 15127 var unitType; 15128 var lastUnitStart = -1; 15129 var lastUnitType = 0; 15130 // logger.log('PES:' + Hex.hexDump(array)); 15131 15132 if (state === -1) { 15133 // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet 15134 lastUnitStart = 0; 15135 // NALu type is value read from offset 0 15136 lastUnitType = this.getNALuType(array, 0); 15137 state = 0; 15138 i = 1; 15139 } 15140 while (i < len) { 15141 value = array[i++]; 15142 // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case 15143 if (!state) { 15144 state = value ? 0 : 1; 15145 continue; 15146 } 15147 if (state === 1) { 15148 state = value ? 0 : 2; 15149 continue; 15150 } 15151 // here we have state either equal to 2 or 3 15152 if (!value) { 15153 state = 3; 15154 } else if (value === 1) { 15155 overflow = i - state - 1; 15156 if (lastUnitStart >= 0) { 15157 var unit = { 15158 data: array.subarray(lastUnitStart, overflow), 15159 type: lastUnitType 15160 }; 15161 // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength); 15162 units.push(unit); 15163 } else { 15164 // lastUnitStart is undefined => this is the first start code found in this PES packet 15165 // first check if start code delimiter is overlapping between 2 PES packets, 15166 // ie it started in last packet (lastState not zero) 15167 // and ended at the beginning of this PES packet (i <= 4 - lastState) 15168 var lastUnit = this.getLastNalUnit(track.samples); 15169 if (lastUnit) { 15170 if (lastState && i <= 4 - lastState) { 15171 // start delimiter overlapping between PES packets 15172 // strip start delimiter bytes from the end of last NAL unit 15173 // check if lastUnit had a state different from zero 15174 if (lastUnit.state) { 15175 // strip last bytes 15176 lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState); 15177 } 15178 } 15179 // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit. 15180 15181 if (overflow > 0) { 15182 // logger.log('first NALU found with overflow:' + overflow); 15183 lastUnit.data = appendUint8Array(lastUnit.data, array.subarray(0, overflow)); 15184 lastUnit.state = 0; 15185 } 15186 } 15187 } 15188 // check if we can read unit type 15189 if (i < len) { 15190 unitType = this.getNALuType(array, i); 15191 // logger.log('find NALU @ offset:' + i + ',type:' + unitType); 15192 lastUnitStart = i; 15193 lastUnitType = unitType; 15194 state = 0; 15195 } else { 15196 // not enough byte to read unit type. let's read it on next PES parsing 15197 state = -1; 15198 } 15199 } else { 15200 state = 0; 15201 } 15202 } 15203 if (lastUnitStart >= 0 && state >= 0) { 15204 var _unit = { 15205 data: array.subarray(lastUnitStart, len), 15206 type: lastUnitType, 15207 state: state 15208 }; 15209 units.push(_unit); 15210 // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state); 15211 } 15212 // no NALu found 15213 if (units.length === 0) { 15214 // append pes.data to previous NAL unit 15215 var _lastUnit = this.getLastNalUnit(track.samples); 15216 if (_lastUnit) { 15217 _lastUnit.data = appendUint8Array(_lastUnit.data, array); 15218 } 15219 } 15220 track.naluState = state; 15221 return units; 15222 }; 15223 return BaseVideoParser; 15224 }(); 15225 15226 /** 15227 * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264.�
vendor: 10,677 bytes, lines 15227-15536
15227 15228 */ 15229 15230 var ExpGolomb = /*#__PURE__*/function () { 15231 function ExpGolomb(data) { 15232 this.data = void 0; 15233 this.bytesAvailable = void 0; 15234 this.word = void 0; 15235 this.bitsAvailable = void 0; 15236 this.data = data; 15237 // the number of bytes left to examine in this.data 15238 this.bytesAvailable = data.byteLength; 15239 // the current word being examined 15240 this.word = 0; // :uint 15241 // the number of bits left to examine in the current word 15242 this.bitsAvailable = 0; // :uint 15243 } 15244 15245 // ():void 15246 var _proto = ExpGolomb.prototype; 15247 _proto.loadWord = function loadWord() { 15248 var data = this.data; 15249 var bytesAvailable = this.bytesAvailable; 15250 var position = data.byteLength - bytesAvailable; 15251 var workingBytes = new Uint8Array(4); 15252 var availableBytes = Math.min(4, bytesAvailable); 15253 if (availableBytes === 0) { 15254 throw new Error('no bytes available'); 15255 } 15256 workingBytes.set(data.subarray(position, position + availableBytes)); 15257 this.word = new DataView(workingBytes.buffer).getUint32(0); 15258 // track the amount of this.data that has been processed 15259 this.bitsAvailable = availableBytes * 8; 15260 this.bytesAvailable -= availableBytes; 15261 } 15262 15263 // (count:int):void 15264 ; 15265 _proto.skipBits = function skipBits(count) { 15266 var skipBytes; // :int 15267 count = Math.min(count, this.bytesAvailable * 8 + this.bitsAvailable); 15268 if (this.bitsAvailable > count) { 15269 this.word <<= count; 15270 this.bitsAvailable -= count; 15271 } else { 15272 count -= this.bitsAvailable; 15273 skipBytes = count >> 3; 15274 count -= skipBytes << 3; 15275 this.bytesAvailable -= skipBytes; 15276 this.loadWord(); 15277 this.word <<= count; 15278 this.bitsAvailable -= count; 15279 } 15280 } 15281 15282 // (size:int):uint 15283 ; 15284 _proto.readBits = function readBits(size) { 15285 var bits = Math.min(this.bitsAvailable, size); // :uint 15286 var valu = this.word >>> 32 - bits; // :uint 15287 if (size > 32) { 15288 logger.error('Cannot read more than 32 bits at a time'); 15289 } 15290 this.bitsAvailable -= bits; 15291 if (this.bitsAvailable > 0) { 15292 this.word <<= bits; 15293 } else if (this.bytesAvailable > 0) { 15294 this.loadWord(); 15295 } else { 15296 throw new Error('no bits available'); 15297 } 15298 bits = size - bits; 15299 if (bits > 0 && this.bitsAvailable) { 15300 return valu << bits | this.readBits(bits); 15301 } else { 15302 return valu; 15303 } 15304 } 15305 15306 // ():uint 15307 ; 15308 _proto.skipLZ = function skipLZ() { 15309 var leadingZeroCount; // :uint 15310 for (leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) { 15311 if ((this.word & 0x80000000 >>> leadingZeroCount) !== 0) { 15312 // the first bit of working word is 1 15313 this.word <<= leadingZeroCount; 15314 this.bitsAvailable -= leadingZeroCount; 15315 return leadingZeroCount; 15316 } 15317 } 15318 // we exhausted word and still have not found a 1 15319 this.loadWord(); 15320 return leadingZeroCount + this.skipLZ(); 15321 } 15322 15323 // ():void 15324 ; 15325 _proto.skipUEG = function skipUEG() { 15326 this.skipBits(1 + this.skipLZ()); 15327 } 15328 15329 // ():void 15330 ; 15331 _proto.skipEG = function skipEG() { 15332 this.skipBits(1 + this.skipLZ()); 15333 } 15334 15335 // ():uint 15336 ; 15337 _proto.readUEG = function readUEG() { 15338 var clz = this.skipLZ(); // :uint 15339 return this.readBits(clz + 1) - 1; 15340 } 15341 15342 // ():int 15343 ; 15344 _proto.readEG = function readEG() { 15345 var valu = this.readUEG(); // :int 15346 if (0x01 & valu) { 15347 // the number is odd if the low order bit is set 15348 return 1 + valu >>> 1; // add 1 to make it even, and divide by 2 15349 } else { 15350 return -1 * (valu >>> 1); // divide by two then make it negative 15351 } 15352 } 15353 15354 // Some convenience functions 15355 // :Boolean 15356 ; 15357 _proto.readBoolean = function readBoolean() { 15358 return this.readBits(1) === 1; 15359 } 15360 15361 // ():int 15362 ; 15363 _proto.readUByte = function readUByte() { 15364 return this.readBits(8); 15365 } 15366 15367 // ():int 15368 ; 15369 _proto.readUShort = function readUShort() { 15370 return this.readBits(16); 15371 } 15372 15373 // ():int 15374 ; 15375 _proto.readUInt = function readUInt() { 15376 return this.readBits(32); 15377 }; 15378 return ExpGolomb; 15379 }(); 15380 15381 var AvcVideoParser = /*#__PURE__*/function (_BaseVideoParser) { 15382 function AvcVideoParser() { 15383 return _BaseVideoParser.apply(this, arguments) || this; 15384 } 15385 _inheritsLoose(AvcVideoParser, _BaseVideoParser); 15386 var _proto = AvcVideoParser.prototype; 15387 _proto.parsePES = function parsePES(track, textTrack, pes, endOfSegment) { 15388 var _this = this; 15389 var units = this.parseNALu(track, pes.data, endOfSegment); 15390 var VideoSample = this.VideoSample; 15391 var push; 15392 var spsfound = false; 15393 // free pes.data to save up some memory 15394 pes.data = null; 15395 15396 // if new NAL units found and last sample still there, let's push ... 15397 // this helps parsing streams with missing AUD (only do this if AUD never found) 15398 if (VideoSample && units.length && !track.audFound) { 15399 this.pushAccessUnit(VideoSample, track); 15400 VideoSample = this.VideoSample = this.createVideoSample(false, pes.pts, pes.dts); 15401 } 15402 units.forEach(function (unit) { 15403 var _VideoSample2, _VideoSample3; 15404 switch (unit.type) { 15405 // NDR 15406 case 1: 15407 { 15408 var iskey = false; 15409 push = true; 15410 var data = unit.data; 15411 // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...) 15412 if (spsfound && data.length > 4) { 15413 // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR 15414 var sliceType = _this.readSliceType(data); 15415 // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice 15416 // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples. 15417 // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice. 15418 // I slice: A slice that is not an SI slice that is decoded using intra prediction only. 15419 // if (sliceType === 2 || sliceType === 7) { 15420 if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) { 15421 iskey = true; 15422 } 15423 } 15424 if (iskey) { 15425 var _VideoSample; 15426 // if we have non-keyframe data already, that cannot belong to the same frame as a keyframe, so force a push 15427 if ((_VideoSample = VideoSample) != null && _VideoSample.frame && !VideoSample.key) { 15428 _this.pushAccessUnit(VideoSample, track); 15429 VideoSample = _this.VideoSample = null; 15430 } 15431 } 15432 if (!VideoSample) { 15433 VideoSample = _this.VideoSample = _this.createVideoSample(true, pes.pts, pes.dts); 15434 } 15435 VideoSample.frame = true; 15436 VideoSample.key = iskey; 15437 break; 15438 // IDR 15439 } 15440 case 5: 15441 push = true; 15442 // handle PES not starting with AUD 15443 // if we have frame data already, that cannot belong to the same frame, so force a push 15444 if ((_VideoSample2 = VideoSample) != null && _VideoSample2.frame && !VideoSample.key) { 15445 _this.pushAccessUnit(VideoSample, track); 15446 VideoSample = _this.VideoSample = null; 15447 } 15448 if (!VideoSample) { 15449 VideoSample = _this.VideoSample = _this.createVideoSample(true, pes.pts, pes.dts); 15450 } 15451 VideoSample.key = true; 15452 VideoSample.frame = true; 15453 break; 15454 // SEI 15455 case 6: 15456 { 15457 push = true; 15458 parseSEIMessageFromNALu(unit.data, 1, pes.pts, textTrack.samples); 15459 break; 15460 // SPS 15461 } 15462 case 7: 15463 { 15464 var _track$pixelRatio, _track$pixelRatio2; 15465 push = true; 15466 spsfound = true; 15467 var sps = unit.data; 15468 var config = _this.readSPS(sps); 15469 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]) { 15470 track.width = config.width; 15471 track.height = config.height; 15472 track.pixelRatio = config.pixelRatio; 15473 track.sps = [sps]; 15474 var codecarray = sps.subarray(1, 4); 15475 var codecstring = 'avc1.'; 15476 for (var i = 0; i < 3; i++) { 15477 var h = codecarray[i].toString(16); 15478 if (h.length < 2) { 15479 h = '0' + h; 15480 } 15481 codecstring += h; 15482 } 15483 track.codec = codecstring; 15484 } 15485 break; 15486 } 15487 // PPS 15488 case 8: 15489 push = true; 15490 track.pps = [unit.data]; 15491 break; 15492 // AUD 15493 case 9: 15494 push = true; 15495 track.audFound = true; 15496 if ((_VideoSample3 = VideoSample) != null && _VideoSample3.frame) { 15497 _this.pushAccessUnit(VideoSample, track); 15498 VideoSample = null; 15499 } 15500 if (!VideoSample) { 15501 VideoSample = _this.VideoSample = _this.createVideoSample(false, pes.pts, pes.dts); 15502 } 15503 break; 15504 // Filler Data 15505 case 12: 15506 push = true; 15507 break; 15508 default: 15509 push = false; 15510 break; 15511 } 15512 if (VideoSample && push) { 15513 var _units = VideoSample.units; 15514 _units.push(unit); 15515 } 15516 }); 15517 // if last PES packet, push samples 15518 if (endOfSegment && VideoSample) { 15519 this.pushAccessUnit(VideoSample, track); 15520 this.VideoSample = null; 15521 } 15522 }; 15523 _proto.getNALuType = function getNALuType(data, offset) { 15524 return data[offset] & 0x1f; 15525 }; 15526 _proto.readSliceType = function readSliceType(data) { 15527 var eg = new ExpGolomb(data); 15528 // skip NALu type 15529 eg.readUByte(); 15530 // discard first_mb_in_slice 15531 eg.readUEG(); 15532 // return slice_type 15533 return eg.readUEG(); 15534 } 15535 15536 /**
15536 15537 * The scaling list is optionally transmitted as part of a sequence parameter 15538 * set and is not relevant to transmuxing. 15539 * @param count the number of entries in this scaling list 15540 * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1 15541 */; 15542 _proto.skipScalingList = function skipScalingList(count, reader) { 15543 var lastScale = 8; 15544 var nextScale = 8; 15545 var deltaScale; 15546 for (var j = 0; j < count; j++) { 15547 if (nextScale !== 0) { 15548 deltaScale = reader.readEG(); 15549 nextScale = (lastScale + deltaScale + 256) % 256; 15550 } 15551 lastScale = nextScale === 0 ? lastScale : nextScale; 15552 } 15553 } 15554 15555 /** 15556 * Read a sequence parameter set and return some interesting video 15557 * properties. A sequence parameter set is the H264 metadata that 15558 * describes the properties of upcoming video frames. 15559 * @returns an object with configuration parsed from the 15560 * sequence parameter set, including the dimensions of the 15561 * associated video frames. 15562 */; 15563 _proto.readSPS = function readSPS(sps) { 15564 var eg = new ExpGolomb(sps); 15565 var frameCropLeftOffset = 0; 15566 var frameCropRightOffset = 0; 15567 var frameCropTopOffset = 0; 15568 var frameCropBottomOffset = 0; 15569 var numRefFramesInPicOrderCntCycle; 15570 var scalingListCount; 15571 var i; 15572 var readUByte = eg.readUByte.bind(eg); 15573 var readBits = eg.readBits.bind(eg); 15574 var readUEG = eg.readUEG.bind(eg); 15575 var readBoolean = eg.readBoolean.bind(eg); 15576 var skipBits = eg.skipBits.bind(eg); 15577 var skipEG = eg.skipEG.bind(eg); 15578 var skipUEG = eg.skipUEG.bind(eg); 15579 var skipScalingList = this.skipScalingList.bind(this); 15580 readUByte(); 15581 var profileIdc = readUByte(); // profile_idc 15582 readBits(5);
vendor: 13,657 bytes, lines 15582-15952
15582 // profileCompat constraint_set[0-4]_flag, u(5) 15583 skipBits(3); // reserved_zero_3bits u(3), 15584 readUByte(); // level_idc u(8) 15585 skipUEG(); // seq_parameter_set_id 15586 // some profiles have more optional data we don't need 15587 if (profileIdc === 100 || profileIdc === 110 || profileIdc === 122 || profileIdc === 244 || profileIdc === 44 || profileIdc === 83 || profileIdc === 86 || profileIdc === 118 || profileIdc === 128) { 15588 var chromaFormatIdc = readUEG(); 15589 if (chromaFormatIdc === 3) { 15590 skipBits(1); 15591 } // separate_colour_plane_flag 15592 15593 skipUEG(); // bit_depth_luma_minus8 15594 skipUEG(); // bit_depth_chroma_minus8 15595 skipBits(1); // qpprime_y_zero_transform_bypass_flag 15596 if (readBoolean()) { 15597 // seq_scaling_matrix_present_flag 15598 scalingListCount = chromaFormatIdc !== 3 ? 8 : 12; 15599 for (i = 0; i < scalingListCount; i++) { 15600 if (readBoolean()) { 15601 // seq_scaling_list_present_flag[ i ] 15602 if (i < 6) { 15603 skipScalingList(16, eg); 15604 } else { 15605 skipScalingList(64, eg); 15606 } 15607 } 15608 } 15609 } 15610 } 15611 skipUEG(); // log2_max_frame_num_minus4 15612 var picOrderCntType = readUEG(); 15613 if (picOrderCntType === 0) { 15614 readUEG(); // log2_max_pic_order_cnt_lsb_minus4 15615 } else if (picOrderCntType === 1) { 15616 skipBits(1); // delta_pic_order_always_zero_flag 15617 skipEG(); // offset_for_non_ref_pic 15618 skipEG(); // offset_for_top_to_bottom_field 15619 numRefFramesInPicOrderCntCycle = readUEG(); 15620 for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) { 15621 skipEG(); 15622 } // offset_for_ref_frame[ i ] 15623 } 15624 skipUEG(); // max_num_ref_frames 15625 skipBits(1); // gaps_in_frame_num_value_allowed_flag 15626 var picWidthInMbsMinus1 = readUEG(); 15627 var picHeightInMapUnitsMinus1 = readUEG(); 15628 var frameMbsOnlyFlag = readBits(1); 15629 if (frameMbsOnlyFlag === 0) { 15630 skipBits(1); 15631 } // mb_adaptive_frame_field_flag 15632 15633 skipBits(1); // direct_8x8_inference_flag 15634 if (readBoolean()) { 15635 // frame_cropping_flag 15636 frameCropLeftOffset = readUEG(); 15637 frameCropRightOffset = readUEG(); 15638 frameCropTopOffset = readUEG(); 15639 frameCropBottomOffset = readUEG(); 15640 } 15641 var pixelRatio = [1, 1]; 15642 if (readBoolean()) { 15643 // vui_parameters_present_flag 15644 if (readBoolean()) { 15645 // aspect_ratio_info_present_flag 15646 var aspectRatioIdc = readUByte(); 15647 switch (aspectRatioIdc) { 15648 case 1: 15649 pixelRatio = [1, 1]; 15650 break; 15651 case 2: 15652 pixelRatio = [12, 11]; 15653 break; 15654 case 3: 15655 pixelRatio = [10, 11]; 15656 break; 15657 case 4: 15658 pixelRatio = [16, 11]; 15659 break; 15660 case 5: 15661 pixelRatio = [40, 33]; 15662 break; 15663 case 6: 15664 pixelRatio = [24, 11]; 15665 break; 15666 case 7: 15667 pixelRatio = [20, 11]; 15668 break; 15669 case 8: 15670 pixelRatio = [32, 11]; 15671 break; 15672 case 9: 15673 pixelRatio = [80, 33]; 15674 break; 15675 case 10: 15676 pixelRatio = [18, 11]; 15677 break; 15678 case 11: 15679 pixelRatio = [15, 11]; 15680 break; 15681 case 12: 15682 pixelRatio = [64, 33]; 15683 break; 15684 case 13: 15685 pixelRatio = [160, 99]; 15686 break; 15687 case 14: 15688 pixelRatio = [4, 3]; 15689 break; 15690 case 15: 15691 pixelRatio = [3, 2]; 15692 break; 15693 case 16: 15694 pixelRatio = [2, 1]; 15695 break; 15696 case 255: 15697 { 15698 pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()]; 15699 break; 15700 } 15701 } 15702 } 15703 } 15704 return { 15705 width: Math.ceil((picWidthInMbsMinus1 + 1) * 16 - frameCropLeftOffset * 2 - frameCropRightOffset * 2), 15706 height: (2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16 - (frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset), 15707 pixelRatio: pixelRatio 15708 }; 15709 }; 15710 return AvcVideoParser; 15711 }(BaseVideoParser); 15712 15713 /** 15714 * SAMPLE-AES decrypter 15715 */ 15716 15717 var SampleAesDecrypter = /*#__PURE__*/function () { 15718 function SampleAesDecrypter(observer, config, keyData) { 15719 this.keyData = void 0; 15720 this.decrypter = void 0; 15721 this.keyData = keyData; 15722 this.decrypter = new Decrypter(config, { 15723 removePKCS7Padding: false 15724 }); 15725 } 15726 var _proto = SampleAesDecrypter.prototype; 15727 _proto.decryptBuffer = function decryptBuffer(encryptedData) { 15728 return this.decrypter.decrypt(encryptedData, this.keyData.key.buffer, this.keyData.iv.buffer, DecrypterAesMode.cbc); 15729 } 15730 15731 // AAC - encrypt all full 16 bytes blocks starting from offset 16 15732 ; 15733 _proto.decryptAacSample = function decryptAacSample(samples, sampleIndex, callback) { 15734 var _this = this; 15735 var curUnit = samples[sampleIndex].unit; 15736 if (curUnit.length <= 16) { 15737 // No encrypted portion in this sample (first 16 bytes is not 15738 // encrypted, see https://developer.apple.com/library/archive/documentation/AudioVideo/Conceptual/HLS_Sample_Encryption/Encryption/Encryption.html), 15739 return; 15740 } 15741 var encryptedData = curUnit.subarray(16, curUnit.length - curUnit.length % 16); 15742 var encryptedBuffer = encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length); 15743 this.decryptBuffer(encryptedBuffer).then(function (decryptedBuffer) { 15744 var decryptedData = new Uint8Array(decryptedBuffer); 15745 curUnit.set(decryptedData, 16); 15746 if (!_this.decrypter.isSync()) { 15747 _this.decryptAacSamples(samples, sampleIndex + 1, callback); 15748 } 15749 }); 15750 }; 15751 _proto.decryptAacSamples = function decryptAacSamples(samples, sampleIndex, callback) { 15752 for (;; sampleIndex++) { 15753 if (sampleIndex >= samples.length) { 15754 callback(); 15755 return; 15756 } 15757 if (samples[sampleIndex].unit.length < 32) { 15758 continue; 15759 } 15760 this.decryptAacSample(samples, sampleIndex, callback); 15761 if (!this.decrypter.isSync()) { 15762 return; 15763 } 15764 } 15765 } 15766 15767 // AVC - encrypt one 16 bytes block out of ten, starting from offset 32 15768 ; 15769 _proto.getAvcEncryptedData = function getAvcEncryptedData(decodedData) { 15770 var encryptedDataLen = Math.floor((decodedData.length - 48) / 160) * 16 + 16; 15771 var encryptedData = new Int8Array(encryptedDataLen); 15772 var outputPos = 0; 15773 for (var inputPos = 32; inputPos < decodedData.length - 16; inputPos += 160, outputPos += 16) { 15774 encryptedData.set(decodedData.subarray(inputPos, inputPos + 16), outputPos); 15775 } 15776 return encryptedData; 15777 }; 15778 _proto.getAvcDecryptedUnit = function getAvcDecryptedUnit(decodedData, decryptedData) { 15779 var uint8DecryptedData = new Uint8Array(decryptedData); 15780 var inputPos = 0; 15781 for (var outputPos = 32; outputPos < decodedData.length - 16; outputPos += 160, inputPos += 16) { 15782 decodedData.set(uint8DecryptedData.subarray(inputPos, inputPos + 16), outputPos); 15783 } 15784 return decodedData; 15785 }; 15786 _proto.decryptAvcSample = function decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit) { 15787 var _this2 = this; 15788 var decodedData = discardEPB(curUnit.data); 15789 var encryptedData = this.getAvcEncryptedData(decodedData); 15790 this.decryptBuffer(encryptedData.buffer).then(function (decryptedBuffer) { 15791 curUnit.data = _this2.getAvcDecryptedUnit(decodedData, decryptedBuffer); 15792 if (!_this2.decrypter.isSync()) { 15793 _this2.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback); 15794 } 15795 }); 15796 }; 15797 _proto.decryptAvcSamples = function decryptAvcSamples(samples, sampleIndex, unitIndex, callback) { 15798 if (samples instanceof Uint8Array) { 15799 throw new Error('Cannot decrypt samples of type Uint8Array'); 15800 } 15801 for (;; sampleIndex++, unitIndex = 0) { 15802 if (sampleIndex >= samples.length) { 15803 callback(); 15804 return; 15805 } 15806 var curUnits = samples[sampleIndex].units; 15807 for (;; unitIndex++) { 15808 if (unitIndex >= curUnits.length) { 15809 break; 15810 } 15811 var curUnit = curUnits[unitIndex]; 15812 if (curUnit.data.length <= 48 || curUnit.type !== 1 && curUnit.type !== 5) { 15813 continue; 15814 } 15815 this.decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit); 15816 if (!this.decrypter.isSync()) { 15817 return; 15818 } 15819 } 15820 } 15821 }; 15822 return SampleAesDecrypter; 15823 }(); 15824 15825 var PACKET_LENGTH = 188; 15826 var TSDemuxer = /*#__PURE__*/function () { 15827 function TSDemuxer(observer, config, typeSupported, logger) { 15828 this.logger = void 0; 15829 this.observer = void 0; 15830 this.config = void 0; 15831 this.typeSupported = void 0; 15832 this.sampleAes = null; 15833 this.pmtParsed = false; 15834 this.audioCodec = void 0; 15835 this.videoCodec = void 0; 15836 this._pmtId = -1; 15837 this._videoTrack = void 0; 15838 this._audioTrack = void 0; 15839 this._id3Track = void 0; 15840 this._txtTrack = void 0; 15841 this.aacOverFlow = null; 15842 this.remainderData = null; 15843 this.videoParser = void 0; 15844 this.observer = observer; 15845 this.config = config; 15846 this.typeSupported = typeSupported; 15847 this.logger = logger; 15848 this.videoParser = null; 15849 } 15850 TSDemuxer.probe = function probe(data, logger) { 15851 var syncOffset = TSDemuxer.syncOffset(data); 15852 if (syncOffset > 0) { 15853 logger.warn("MPEG2-TS detected but first sync word found @ offset " + syncOffset); 15854 } 15855 return syncOffset !== -1; 15856 }; 15857 TSDemuxer.syncOffset = function syncOffset(data) { 15858 var length = data.length; 15859 var scanwindow = Math.min(PACKET_LENGTH * 5, length - PACKET_LENGTH) + 1; 15860 var i = 0; 15861 while (i < scanwindow) { 15862 // a TS init segment should contain at least 2 TS packets: PAT and PMT, each starting with 0x47 15863 var foundPat = false; 15864 var packetStart = -1; 15865 var tsPackets = 0; 15866 for (var j = i; j < length; j += PACKET_LENGTH) { 15867 if (data[j] === 0x47 && (length - j === PACKET_LENGTH || data[j + PACKET_LENGTH] === 0x47)) { 15868 tsPackets++; 15869 if (packetStart === -1) { 15870 packetStart = j; 15871 // First sync word found at offset, increase scan length (#5251) 15872 if (packetStart !== 0) { 15873 scanwindow = Math.min(packetStart + PACKET_LENGTH * 99, data.length - PACKET_LENGTH) + 1; 15874 } 15875 } 15876 if (!foundPat) { 15877 foundPat = parsePID(data, j) === 0; 15878 } 15879 // Sync word found at 0 with 3 packets, or found at offset least 2 packets up to scanwindow (#5501) 15880 if (foundPat && tsPackets > 1 && (packetStart === 0 && tsPackets > 2 || j + PACKET_LENGTH > scanwindow)) { 15881 return packetStart; 15882 } 15883 } else if (tsPackets) { 15884 // Exit if sync word found, but does not contain contiguous packets 15885 return -1; 15886 } else { 15887 break; 15888 } 15889 } 15890 i++; 15891 } 15892 return -1; 15893 } 15894 15895 /** 15896 * Creates a track model internal to demuxer used to drive remuxing input 15897 */; 15898 TSDemuxer.createTrack = function createTrack(type, duration) { 15899 return { 15900 container: type === 'video' || type === 'audio' ? 'video/mp2t' : undefined, 15901 type: type, 15902 id: RemuxerTrackIdConfig[type], 15903 pid: -1, 15904 inputTimeScale: 90000, 15905 sequenceNumber: 0, 15906 samples: [], 15907 dropped: 0, 15908 duration: type === 'audio' ? duration : undefined 15909 }; 15910 } 15911 15912 /** 15913 * Initializes a new init segment on the demuxer/remuxer interface. Needed for discontinuities/track-switches (or at stream start) 15914 * Resets all internal track instances of the demuxer. 15915 */; 15916 var _proto = TSDemuxer.prototype; 15917 _proto.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { 15918 this.pmtParsed = false; 15919 this._pmtId = -1; 15920 this._videoTrack = TSDemuxer.createTrack('video'); 15921 this._videoTrack.duration = trackDuration; 15922 this._audioTrack = TSDemuxer.createTrack('audio', trackDuration); 15923 this._id3Track = TSDemuxer.createTrack('id3'); 15924 this._txtTrack = TSDemuxer.createTrack('text'); 15925 this._audioTrack.segmentCodec = 'aac'; 15926 15927 // flush any partial content 15928 this.aacOverFlow = null; 15929 this.remainderData = null; 15930 this.audioCodec = audioCodec; 15931 this.videoCodec = videoCodec; 15932 }; 15933 _proto.resetTimeStamp = function resetTimeStamp() {}; 15934 _proto.resetContiguity = function resetContiguity() { 15935 var _audioTrack = this._audioTrack, 15936 _videoTrack = this._videoTrack, 15937 _id3Track = this._id3Track; 15938 if (_audioTrack) { 15939 _audioTrack.pesData = null; 15940 } 15941 if (_videoTrack) { 15942 _videoTrack.pesData = null; 15943 } 15944 if (_id3Track) { 15945 _id3Track.pesData = null; 15946 } 15947 this.aacOverFlow = null; 15948 this.remainderData = null; 15949 }; 15950 _proto.demux = function demux(data, timeOffset, isSampleAes, flush) { 15951 if (isSampleAes === void 0) { 15952 isSampleAes = false;
vendor: 21,279 bytes, lines 15953-16539
15953 } 15954 if (flush === void 0) { 15955 flush = false; 15956 } 15957 if (!isSampleAes) { 15958 this.sampleAes = null; 15959 } 15960 var pes; 15961 var videoTrack = this._videoTrack; 15962 var audioTrack = this._audioTrack; 15963 var id3Track = this._id3Track; 15964 var textTrack = this._txtTrack; 15965 var videoPid = videoTrack.pid; 15966 var videoData = videoTrack.pesData; 15967 var audioPid = audioTrack.pid; 15968 var id3Pid = id3Track.pid; 15969 var audioData = audioTrack.pesData; 15970 var id3Data = id3Track.pesData; 15971 var unknownPID = null; 15972 var pmtParsed = this.pmtParsed; 15973 var pmtId = this._pmtId; 15974 var len = data.length; 15975 if (this.remainderData) { 15976 data = appendUint8Array(this.remainderData, data); 15977 len = data.length; 15978 this.remainderData = null; 15979 } 15980 if (len < PACKET_LENGTH && !flush) { 15981 this.remainderData = data; 15982 return { 15983 audioTrack: audioTrack, 15984 videoTrack: videoTrack, 15985 id3Track: id3Track, 15986 textTrack: textTrack 15987 }; 15988 } 15989 var syncOffset = Math.max(0, TSDemuxer.syncOffset(data)); 15990 len -= (len - syncOffset) % PACKET_LENGTH; 15991 if (len < data.byteLength && !flush) { 15992 this.remainderData = new Uint8Array(data.buffer, len, data.buffer.byteLength - len); 15993 } 15994 15995 // loop through TS packets 15996 var tsPacketErrors = 0; 15997 for (var start = syncOffset; start < len; start += PACKET_LENGTH) { 15998 if (data[start] === 0x47) { 15999 var stt = !!(data[start + 1] & 0x40); 16000 var pid = parsePID(data, start); 16001 var atf = (data[start + 3] & 0x30) >> 4; 16002 16003 // if an adaption field is present, its length is specified by the fifth byte of the TS packet header. 16004 var offset = void 0; 16005 if (atf > 1) { 16006 offset = start + 5 + data[start + 4]; 16007 // continue if there is only adaptation field 16008 if (offset === start + PACKET_LENGTH) { 16009 continue; 16010 } 16011 } else { 16012 offset = start + 4; 16013 } 16014 switch (pid) { 16015 case videoPid: 16016 if (stt) { 16017 if (videoData && (pes = parsePES(videoData, this.logger))) { 16018 if (this.videoParser === null) { 16019 switch (videoTrack.segmentCodec) { 16020 case 'avc': 16021 this.videoParser = new AvcVideoParser(); 16022 break; 16023 } 16024 } 16025 if (this.videoParser !== null) { 16026 this.videoParser.parsePES(videoTrack, textTrack, pes, false); 16027 } 16028 } 16029 videoData = { 16030 data: [], 16031 size: 0 16032 }; 16033 } 16034 if (videoData) { 16035 videoData.data.push(data.subarray(offset, start + PACKET_LENGTH)); 16036 videoData.size += start + PACKET_LENGTH - offset; 16037 } 16038 break; 16039 case audioPid: 16040 if (stt) { 16041 if (audioData && (pes = parsePES(audioData, this.logger))) { 16042 switch (audioTrack.segmentCodec) { 16043 case 'aac': 16044 this.parseAACPES(audioTrack, pes); 16045 break; 16046 case 'mp3': 16047 this.parseMPEGPES(audioTrack, pes); 16048 break; 16049 } 16050 } 16051 audioData = { 16052 data: [], 16053 size: 0 16054 }; 16055 } 16056 if (audioData) { 16057 audioData.data.push(data.subarray(offset, start + PACKET_LENGTH)); 16058 audioData.size += start + PACKET_LENGTH - offset; 16059 } 16060 break; 16061 case id3Pid: 16062 if (stt) { 16063 if (id3Data && (pes = parsePES(id3Data, this.logger))) { 16064 this.parseID3PES(id3Track, pes); 16065 } 16066 id3Data = { 16067 data: [], 16068 size: 0 16069 }; 16070 } 16071 if (id3Data) { 16072 id3Data.data.push(data.subarray(offset, start + PACKET_LENGTH)); 16073 id3Data.size += start + PACKET_LENGTH - offset; 16074 } 16075 break; 16076 case 0: 16077 if (stt) { 16078 offset += data[offset] + 1; 16079 } 16080 pmtId = this._pmtId = parsePAT(data, offset); 16081 // this.logger.log('PMT PID:' + this._pmtId); 16082 break; 16083 case pmtId: 16084 { 16085 if (stt) { 16086 offset += data[offset] + 1; 16087 } 16088 var parsedPIDs = parsePMT(data, offset, this.typeSupported, isSampleAes, this.observer, this.logger); 16089 16090 // only update track id if track PID found while parsing PMT 16091 // this is to avoid resetting the PID to -1 in case 16092 // track PID transiently disappears from the stream 16093 // this could happen in case of transient missing audio samples for example 16094 // NOTE this is only the PID of the track as found in TS, 16095 // but we are not using this for MP4 track IDs. 16096 videoPid = parsedPIDs.videoPid; 16097 if (videoPid > 0) { 16098 videoTrack.pid = videoPid; 16099 videoTrack.segmentCodec = parsedPIDs.segmentVideoCodec; 16100 } 16101 audioPid = parsedPIDs.audioPid; 16102 if (audioPid > 0) { 16103 audioTrack.pid = audioPid; 16104 audioTrack.segmentCodec = parsedPIDs.segmentAudioCodec; 16105 } 16106 id3Pid = parsedPIDs.id3Pid; 16107 if (id3Pid > 0) { 16108 id3Track.pid = id3Pid; 16109 } 16110 if (unknownPID !== null && !pmtParsed) { 16111 this.logger.warn("MPEG-TS PMT found at " + start + " after unknown PID '" + unknownPID + "'. Backtracking to sync byte @" + syncOffset + " to parse all TS packets."); 16112 unknownPID = null; 16113 // we set it to -188, the += 188 in the for loop will reset start to 0 16114 start = syncOffset - 188; 16115 } 16116 pmtParsed = this.pmtParsed = true; 16117 break; 16118 } 16119 case 0x11: 16120 case 0x1fff: 16121 break; 16122 default: 16123 unknownPID = pid; 16124 break; 16125 } 16126 } else { 16127 tsPacketErrors++; 16128 } 16129 } 16130 if (tsPacketErrors > 0) { 16131 emitParsingError(this.observer, new Error("Found " + tsPacketErrors + " TS packet/s that do not start with 0x47"), undefined, this.logger); 16132 } 16133 videoTrack.pesData = videoData; 16134 audioTrack.pesData = audioData; 16135 id3Track.pesData = id3Data; 16136 var demuxResult = { 16137 audioTrack: audioTrack, 16138 videoTrack: videoTrack, 16139 id3Track: id3Track, 16140 textTrack: textTrack 16141 }; 16142 if (flush) { 16143 this.extractRemainingSamples(demuxResult); 16144 } 16145 return demuxResult; 16146 }; 16147 _proto.flush = function flush() { 16148 var remainderData = this.remainderData; 16149 this.remainderData = null; 16150 var result; 16151 if (remainderData) { 16152 result = this.demux(remainderData, -1, false, true); 16153 } else { 16154 result = { 16155 videoTrack: this._videoTrack, 16156 audioTrack: this._audioTrack, 16157 id3Track: this._id3Track, 16158 textTrack: this._txtTrack 16159 }; 16160 } 16161 this.extractRemainingSamples(result); 16162 if (this.sampleAes) { 16163 return this.decrypt(result, this.sampleAes); 16164 } 16165 return result; 16166 }; 16167 _proto.extractRemainingSamples = function extractRemainingSamples(demuxResult) { 16168 var audioTrack = demuxResult.audioTrack, 16169 videoTrack = demuxResult.videoTrack, 16170 id3Track = demuxResult.id3Track, 16171 textTrack = demuxResult.textTrack; 16172 var videoData = videoTrack.pesData; 16173 var audioData = audioTrack.pesData; 16174 var id3Data = id3Track.pesData; 16175 // try to parse last PES packets 16176 var pes; 16177 if (videoData && (pes = parsePES(videoData, this.logger))) { 16178 if (this.videoParser === null) { 16179 switch (videoTrack.segmentCodec) { 16180 case 'avc': 16181 this.videoParser = new AvcVideoParser(); 16182 break; 16183 } 16184 } 16185 if (this.videoParser !== null) { 16186 this.videoParser.parsePES(videoTrack, textTrack, pes, true); 16187 videoTrack.pesData = null; 16188 } 16189 } else { 16190 // either avcData null or PES truncated, keep it for next frag parsing 16191 videoTrack.pesData = videoData; 16192 } 16193 if (audioData && (pes = parsePES(audioData, this.logger))) { 16194 switch (audioTrack.segmentCodec) { 16195 case 'aac': 16196 this.parseAACPES(audioTrack, pes); 16197 break; 16198 case 'mp3': 16199 this.parseMPEGPES(audioTrack, pes); 16200 break; 16201 } 16202 audioTrack.pesData = null; 16203 } else { 16204 if (audioData != null && audioData.size) { 16205 this.logger.log('last AAC PES packet truncated,might overlap between fragments'); 16206 } 16207 16208 // either audioData null or PES truncated, keep it for next frag parsing 16209 audioTrack.pesData = audioData; 16210 } 16211 if (id3Data && (pes = parsePES(id3Data, this.logger))) { 16212 this.parseID3PES(id3Track, pes); 16213 id3Track.pesData = null; 16214 } else { 16215 // either id3Data null or PES truncated, keep it for next frag parsing 16216 id3Track.pesData = id3Data; 16217 } 16218 }; 16219 _proto.demuxSampleAes = function demuxSampleAes(data, keyData, timeOffset) { 16220 var demuxResult = this.demux(data, timeOffset, true, !this.config.progressive); 16221 var sampleAes = this.sampleAes = new SampleAesDecrypter(this.observer, this.config, keyData); 16222 return this.decrypt(demuxResult, sampleAes); 16223 }; 16224 _proto.decrypt = function decrypt(demuxResult, sampleAes) { 16225 return new Promise(function (resolve) { 16226 var audioTrack = demuxResult.audioTrack, 16227 videoTrack = demuxResult.videoTrack; 16228 if (audioTrack.samples && audioTrack.segmentCodec === 'aac') { 16229 sampleAes.decryptAacSamples(audioTrack.samples, 0, function () { 16230 if (videoTrack.samples) { 16231 sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, function () { 16232 resolve(demuxResult); 16233 }); 16234 } else { 16235 resolve(demuxResult); 16236 } 16237 }); 16238 } else if (videoTrack.samples) { 16239 sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, function () { 16240 resolve(demuxResult); 16241 }); 16242 } 16243 }); 16244 }; 16245 _proto.destroy = function destroy() { 16246 if (this.observer) { 16247 this.observer.removeAllListeners(); 16248 } 16249 // @ts-ignore 16250 this.config = this.logger = this.observer = null; 16251 this.aacOverFlow = this.videoParser = this.remainderData = this.sampleAes = null; 16252 this._videoTrack = this._audioTrack = this._id3Track = this._txtTrack = undefined; 16253 }; 16254 _proto.parseAACPES = function parseAACPES(track, pes) { 16255 var startOffset = 0; 16256 var aacOverFlow = this.aacOverFlow; 16257 var data = pes.data; 16258 if (aacOverFlow) { 16259 this.aacOverFlow = null; 16260 var frameMissingBytes = aacOverFlow.missing; 16261 var sampleLength = aacOverFlow.sample.unit.byteLength; 16262 // logger.log(`AAC: append overflowing ${sampleLength} bytes to beginning of new PES`); 16263 if (frameMissingBytes === -1) { 16264 data = appendUint8Array(aacOverFlow.sample.unit, data); 16265 } else { 16266 var frameOverflowBytes = sampleLength - frameMissingBytes; 16267 aacOverFlow.sample.unit.set(data.subarray(0, frameMissingBytes), frameOverflowBytes); 16268 track.samples.push(aacOverFlow.sample); 16269 startOffset = aacOverFlow.missing; 16270 } 16271 } 16272 // look for ADTS header (0xFFFx) 16273 var offset; 16274 var len; 16275 for (offset = startOffset, len = data.length; offset < len - 1; offset++) { 16276 if (isHeader$1(data, offset)) { 16277 break; 16278 } 16279 } 16280 // if ADTS header does not start straight from the beginning of the PES payload, raise an error 16281 if (offset !== startOffset) { 16282 var reason; 16283 var recoverable = offset < len - 1; 16284 if (recoverable) { 16285 reason = "AAC PES did not start with ADTS header,offset:" + offset; 16286 } else { 16287 reason = 'No ADTS header found in AAC PES'; 16288 } 16289 emitParsingError(this.observer, new Error(reason), recoverable, this.logger); 16290 if (!recoverable) { 16291 return; 16292 } 16293 } 16294 initTrackConfig(track, this.observer, data, offset, this.audioCodec); 16295 var pts; 16296 if (pes.pts !== undefined) { 16297 pts = pes.pts; 16298 } else if (aacOverFlow) { 16299 // if last AAC frame is overflowing, we should ensure timestamps are contiguous: 16300 // first sample PTS should be equal to last sample PTS + frameDuration 16301 var frameDuration = getFrameDuration(track.samplerate); 16302 pts = aacOverFlow.sample.pts + frameDuration; 16303 } else { 16304 this.logger.warn('[tsdemuxer]: AAC PES unknown PTS'); 16305 return; 16306 } 16307 16308 // scan for aac samples 16309 var frameIndex = 0; 16310 var frame; 16311 while (offset < len) { 16312 frame = appendFrame$1(track, data, offset, pts, frameIndex); 16313 offset += frame.length; 16314 if (!frame.missing) { 16315 frameIndex++; 16316 for (; offset < len - 1; offset++) { 16317 if (isHeader$1(data, offset)) { 16318 break; 16319 } 16320 } 16321 } else { 16322 this.aacOverFlow = frame; 16323 break; 16324 } 16325 } 16326 }; 16327 _proto.parseMPEGPES = function parseMPEGPES(track, pes) { 16328 var data = pes.data; 16329 var length = data.length; 16330 var frameIndex = 0; 16331 var offset = 0; 16332 var pts = pes.pts; 16333 if (pts === undefined) { 16334 this.logger.warn('[tsdemuxer]: MPEG PES unknown PTS'); 16335 return; 16336 } 16337 while (offset < length) { 16338 if (isHeader(data, offset)) { 16339 var frame = appendFrame(track, data, offset, pts, frameIndex); 16340 if (frame) { 16341 offset += frame.length; 16342 frameIndex++; 16343 } else { 16344 // logger.log('Unable to parse Mpeg audio frame'); 16345 break; 16346 } 16347 } else { 16348 // nothing found, keep looking 16349 offset++; 16350 } 16351 } 16352 }; 16353 _proto.parseAC3PES = function parseAC3PES(track, pes) { 16354 }; 16355 _proto.parseID3PES = function parseID3PES(id3Track, pes) { 16356 if (pes.pts === undefined) { 16357 this.logger.warn('[tsdemuxer]: ID3 PES unknown PTS'); 16358 return; 16359 } 16360 var id3Sample = _extends({}, pes, { 16361 type: this._videoTrack ? MetadataSchema.emsg : MetadataSchema.audioId3, 16362 duration: Number.POSITIVE_INFINITY 16363 }); 16364 id3Track.samples.push(id3Sample); 16365 }; 16366 return TSDemuxer; 16367 }(); 16368 function parsePID(data, offset) { 16369 // pid is a 13-bit field starting at the last bit of TS[1] 16370 return ((data[offset + 1] & 0x1f) << 8) + data[offset + 2]; 16371 } 16372 function parsePAT(data, offset) { 16373 // skip the PSI header and parse the first PMT entry 16374 return (data[offset + 10] & 0x1f) << 8 | data[offset + 11]; 16375 } 16376 function parsePMT(data, offset, typeSupported, isSampleAes, observer, logger) { 16377 var result = { 16378 audioPid: -1, 16379 videoPid: -1, 16380 id3Pid: -1, 16381 segmentVideoCodec: 'avc', 16382 segmentAudioCodec: 'aac' 16383 }; 16384 var sectionLength = (data[offset + 1] & 0x0f) << 8 | data[offset + 2]; 16385 var tableEnd = offset + 3 + sectionLength - 4; 16386 // to determine where the table is, we have to figure out how 16387 // long the program info descriptors are 16388 var programInfoLength = (data[offset + 10] & 0x0f) << 8 | data[offset + 11]; 16389 // advance the offset to the first entry in the mapping table 16390 offset += 12 + programInfoLength; 16391 while (offset < tableEnd) { 16392 var pid = parsePID(data, offset); 16393 var esInfoLength = (data[offset + 3] & 0x0f) << 8 | data[offset + 4]; 16394 switch (data[offset]) { 16395 case 0xcf: 16396 // SAMPLE-AES AAC 16397 if (!isSampleAes) { 16398 logEncryptedSamplesFoundInUnencryptedStream('ADTS AAC', logger); 16399 break; 16400 } 16401 /* falls through */ 16402 case 0x0f: 16403 // ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio) 16404 // logger.log('AAC PID:' + pid); 16405 if (result.audioPid === -1) { 16406 result.audioPid = pid; 16407 } 16408 break; 16409 16410 // Packetized metadata (ID3) 16411 case 0x15: 16412 // logger.log('ID3 PID:' + pid); 16413 if (result.id3Pid === -1) { 16414 result.id3Pid = pid; 16415 } 16416 break; 16417 case 0xdb: 16418 // SAMPLE-AES AVC 16419 if (!isSampleAes) { 16420 logEncryptedSamplesFoundInUnencryptedStream('H.264', logger); 16421 break; 16422 } 16423 /* falls through */ 16424 case 0x1b: 16425 // ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video) 16426 // logger.log('AVC PID:' + pid); 16427 if (result.videoPid === -1) { 16428 result.videoPid = pid; 16429 result.segmentVideoCodec = 'avc'; 16430 } 16431 break; 16432 16433 // ISO/IEC 11172-3 (MPEG-1 audio) 16434 // or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio) 16435 case 0x03: 16436 case 0x04: 16437 // logger.log('MPEG PID:' + pid); 16438 if (!typeSupported.mpeg && !typeSupported.mp3) { 16439 logger.log('MPEG audio found, not supported in this browser'); 16440 } else if (result.audioPid === -1) { 16441 result.audioPid = pid; 16442 result.segmentAudioCodec = 'mp3'; 16443 } 16444 break; 16445 case 0xc1: 16446 // SAMPLE-AES AC3 16447 if (!isSampleAes) { 16448 logEncryptedSamplesFoundInUnencryptedStream('AC-3', logger); 16449 break; 16450 } 16451 /* falls through */ 16452 case 0x81: 16453 { 16454 logger.warn('AC-3 in M2TS support not included in build'); 16455 } 16456 break; 16457 case 0x06: 16458 // stream_type 6 can mean a lot of different things in case of DVB. 16459 // We need to look at the descriptors. Right now, we're only interested 16460 // in AC-3 audio, so we do the descriptor parsing only when we don't have 16461 // an audio PID yet. 16462 if (result.audioPid === -1 && esInfoLength > 0) { 16463 var parsePos = offset + 5; 16464 var remaining = esInfoLength; 16465 while (remaining > 2) { 16466 var descriptorId = data[parsePos]; 16467 switch (descriptorId) { 16468 case 0x6a: 16469 // DVB Descriptor for AC-3 16470 { 16471 logger.warn('AC-3 in M2TS support not included in build'); 16472 } 16473 break; 16474 } 16475 var descriptorLen = data[parsePos + 1] + 2; 16476 parsePos += descriptorLen; 16477 remaining -= descriptorLen; 16478 } 16479 } 16480 break; 16481 case 0xc2: // SAMPLE-AES EC3 16482 /* falls through */ 16483 case 0x87: 16484 emitParsingError(observer, new Error('Unsupported EC-3 in M2TS found'), undefined, logger); 16485 return result; 16486 case 0x24: 16487 // ITU-T Rec. H.265 and ISO/IEC 23008-2 (HEVC) 16488 { 16489 emitParsingError(observer, new Error('Unsupported HEVC in M2TS found'), undefined, logger); 16490 return result; 16491 } 16492 } 16493 // move to the next table entry 16494 // skip past the elementary stream descriptors, if present 16495 offset += esInfoLength + 5; 16496 } 16497 return result; 16498 } 16499 function emitParsingError(observer, error, levelRetry, logger) { 16500 logger.warn("parsing error: " + error.message); 16501 observer.emit(Events.ERROR, Events.ERROR, { 16502 type: ErrorTypes.MEDIA_ERROR, 16503 details: ErrorDetails.FRAG_PARSING_ERROR, 16504 fatal: false, 16505 levelRetry: levelRetry, 16506 error: error, 16507 reason: error.message 16508 }); 16509 } 16510 function logEncryptedSamplesFoundInUnencryptedStream(type, logger) { 16511 logger.log(type + " with AES-128-CBC encryption found in unencrypted stream"); 16512 } 16513 function parsePES(stream, logger) { 16514 var i = 0; 16515 var frag; 16516 var pesLen; 16517 var pesHdrLen; 16518 var pesPts; 16519 var pesDts; 16520 var data = stream.data; 16521 // safety check 16522 if (!stream || stream.size === 0) { 16523 return null; 16524 } 16525 16526 // we might need up to 19 bytes to read PES header 16527 // if first chunk of data is less than 19 bytes, let's merge it with following ones until we get 19 bytes 16528 // usually only one merge is needed (and this is rare ...) 16529 while (data[0].length < 19 && data.length > 1) { 16530 data[0] = appendUint8Array(data[0], data[1]); 16531 data.splice(1, 1); 16532 } 16533 // retrieve PTS/DTS from first fragment 16534 frag = data[0]; 16535 var pesPrefix = (frag[0] << 16) + (frag[1] << 8) + frag[2]; 16536 if (pesPrefix === 1) { 16537 pesLen = (frag[4] << 8) + frag[5]; 16538 // if PES parsed length is not zero and greater than total received length, stop parsing. PES might be truncated 16539 // minus 6 : PES header size
vendor: 4,104 bytes, lines 16540-16654
16540 if (pesLen && pesLen > stream.size - 6) { 16541 return null; 16542 } 16543 var pesFlags = frag[7]; 16544 if (pesFlags & 0xc0) { 16545 /* PES header described here : http://dvd.sourceforge.net/dvdinfo/pes-hdr.html 16546 as PTS / DTS is 33 bit we cannot use bitwise operator in JS, 16547 as Bitwise operators treat their operands as a sequence of 32 bits */ 16548 pesPts = (frag[9] & 0x0e) * 536870912 + 16549 // 1 << 29 16550 (frag[10] & 0xff) * 4194304 + 16551 // 1 << 22 16552 (frag[11] & 0xfe) * 16384 + 16553 // 1 << 14 16554 (frag[12] & 0xff) * 128 + 16555 // 1 << 7 16556 (frag[13] & 0xfe) / 2; 16557 if (pesFlags & 0x40) { 16558 pesDts = (frag[14] & 0x0e) * 536870912 + 16559 // 1 << 29 16560 (frag[15] & 0xff) * 4194304 + 16561 // 1 << 22 16562 (frag[16] & 0xfe) * 16384 + 16563 // 1 << 14 16564 (frag[17] & 0xff) * 128 + 16565 // 1 << 7 16566 (frag[18] & 0xfe) / 2; 16567 if (pesPts - pesDts > 60 * 90000) { 16568 logger.warn(Math.round((pesPts - pesDts) / 90000) + "s delta between PTS and DTS, align them"); 16569 pesPts = pesDts; 16570 } 16571 } else { 16572 pesDts = pesPts; 16573 } 16574 } 16575 pesHdrLen = frag[8]; 16576 // 9 bytes : 6 bytes for PES header + 3 bytes for PES extension 16577 var payloadStartOffset = pesHdrLen + 9; 16578 if (stream.size <= payloadStartOffset) { 16579 return null; 16580 } 16581 stream.size -= payloadStartOffset; 16582 // reassemble PES packet 16583 var pesData = new Uint8Array(stream.size); 16584 for (var j = 0, dataLen = data.length; j < dataLen; j++) { 16585 frag = data[j]; 16586 var len = frag.byteLength; 16587 if (payloadStartOffset) { 16588 if (payloadStartOffset > len) { 16589 // trim full frag if PES header bigger than frag 16590 payloadStartOffset -= len; 16591 continue; 16592 } else { 16593 // trim partial frag if PES header smaller than frag 16594 frag = frag.subarray(payloadStartOffset); 16595 len -= payloadStartOffset; 16596 payloadStartOffset = 0; 16597 } 16598 } 16599 pesData.set(frag, i); 16600 i += len; 16601 } 16602 if (pesLen) { 16603 // payload size : remove PES header + PES extension 16604 pesLen -= pesHdrLen + 3; 16605 } 16606 return { 16607 data: pesData, 16608 pts: pesPts, 16609 dts: pesDts, 16610 len: pesLen 16611 }; 16612 } 16613 return null; 16614 } 16615 16616 var MP3Demuxer = /*#__PURE__*/function (_BaseAudioDemuxer) { 16617 function MP3Demuxer() { 16618 return _BaseAudioDemuxer.apply(this, arguments) || this; 16619 } 16620 _inheritsLoose(MP3Demuxer, _BaseAudioDemuxer); 16621 var _proto = MP3Demuxer.prototype; 16622 _proto.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, trackDuration) { 16623 _BaseAudioDemuxer.prototype.resetInitSegment.call(this, initSegment, audioCodec, videoCodec, trackDuration); 16624 this._audioTrack = { 16625 container: 'audio/mpeg', 16626 type: 'audio', 16627 id: 2, 16628 pid: -1, 16629 sequenceNumber: 0, 16630 segmentCodec: 'mp3', 16631 samples: [], 16632 manifestCodec: audioCodec, 16633 duration: trackDuration, 16634 inputTimeScale: 90000, 16635 dropped: 0 16636 }; 16637 }; 16638 MP3Demuxer.probe = function probe$1(data) { 16639 if (!data) { 16640 return false; 16641 } 16642 16643 // check if data contains ID3 timestamp and MPEG sync word 16644 // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1 16645 // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) 16646 // More info http://www.mp3-tech.org/programmer/frame_header.html 16647 var id3Data = getId3Data(data, 0); 16648 var offset = (id3Data == null ? void 0 : id3Data.length) || 0; 16649 16650 // Check for ac-3|ec-3 sync bytes and return false if present 16651 if (id3Data && data[offset] === 0x0b && data[offset + 1] === 0x77 && getId3Timestamp(id3Data) !== undefined && 16652 // check the bsid to confirm ac-3 or ec-3 (not mp3) 16653 getAudioBSID(data, offset) <= 16) { 16654 return false;
vendor: 10,708 bytes, lines 16655-16941
16655 } 16656 for (var length = data.length; offset < length; offset++) { 16657 if (probe(data, offset)) { 16658 logger.log('MPEG Audio sync word found !'); 16659 return true; 16660 } 16661 } 16662 return false; 16663 }; 16664 _proto.canParse = function canParse$1(data, offset) { 16665 return canParse(data, offset); 16666 }; 16667 _proto.appendFrame = function appendFrame$1(track, data, offset) { 16668 if (this.basePTS === null) { 16669 return; 16670 } 16671 return appendFrame(track, data, offset, this.basePTS, this.frameIndex); 16672 }; 16673 return MP3Demuxer; 16674 }(BaseAudioDemuxer); 16675 16676 /** 16677 * AAC helper 16678 */ 16679 var AAC = /*#__PURE__*/function () { 16680 function AAC() {} 16681 AAC.getSilentFrame = function getSilentFrame(codec, channelCount) { 16682 switch (codec) { 16683 case 'mp4a.40.2': 16684 if (channelCount === 1) { 16685 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]); 16686 } else if (channelCount === 2) { 16687 return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]); 16688 } else if (channelCount === 3) { 16689 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]); 16690 } else if (channelCount === 4) { 16691 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]); 16692 } else if (channelCount === 5) { 16693 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]); 16694 } else if (channelCount === 6) { 16695 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x00, 0xb2, 0x00, 0x20, 0x08, 0xe0]); 16696 } 16697 break; 16698 // handle HE-AAC below (mp4a.40.5 / mp4a.40.29) 16699 default: 16700 if (channelCount === 1) { 16701 // ffmpeg -y -f lavfi -i "aevalsrc=0:d=0.05" -c:a libfdk_aac -profile:a aac_he -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac 16702 return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x4e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x1c, 0x6, 0xf1, 0xc1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]); 16703 } else if (channelCount === 2) { 16704 // ffmpeg -y -f lavfi -i "aevalsrc=0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac 16705 return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]); 16706 } else if (channelCount === 3) { 16707 // ffmpeg -y -f lavfi -i "aevalsrc=0|0|0:d=0.05" -c:a libfdk_aac -profile:a aac_he_v2 -b:a 4k output.aac && hexdump -v -e '16/1 "0x%x," "\n"' -v output.aac 16708 return new Uint8Array([0x1, 0x40, 0x22, 0x80, 0xa3, 0x5e, 0xe6, 0x80, 0xba, 0x8, 0x0, 0x0, 0x0, 0x0, 0x95, 0x0, 0x6, 0xf1, 0xa1, 0xa, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5a, 0x5e]); 16709 } 16710 break; 16711 } 16712 return undefined; 16713 }; 16714 return AAC; 16715 }(); 16716 16717 /** 16718 * Generate MP4 Box 16719 */ 16720 16721 var UINT32_MAX = Math.pow(2, 32) - 1; 16722 var MP4 = /*#__PURE__*/function () { 16723 function MP4() {} 16724 MP4.init = function init() { 16725 MP4.types = { 16726 avc1: [], 16727 // codingname 16728 avcC: [], 16729 hvc1: [], 16730 hvcC: [], 16731 btrt: [], 16732 dinf: [], 16733 dref: [], 16734 esds: [], 16735 ftyp: [], 16736 hdlr: [], 16737 mdat: [], 16738 mdhd: [], 16739 mdia: [], 16740 mfhd: [], 16741 minf: [], 16742 moof: [], 16743 moov: [], 16744 mp4a: [], 16745 '.mp3': [], 16746 dac3: [], 16747 'ac-3': [], 16748 mvex: [], 16749 mvhd: [], 16750 pasp: [], 16751 sdtp: [], 16752 stbl: [], 16753 stco: [], 16754 stsc: [], 16755 stsd: [], 16756 stsz: [], 16757 stts: [], 16758 tfdt: [], 16759 tfhd: [], 16760 traf: [], 16761 trak: [], 16762 trun: [], 16763 trex: [], 16764 tkhd: [], 16765 vmhd: [], 16766 smhd: [] 16767 }; 16768 var i; 16769 for (i in MP4.types) { 16770 if (MP4.types.hasOwnProperty(i)) { 16771 MP4.types[i] = [i.charCodeAt(0), i.charCodeAt(1), i.charCodeAt(2), i.charCodeAt(3)]; 16772 } 16773 } 16774 var videoHdlr = new Uint8Array([0x00, 16775 // version 0 16776 0x00, 0x00, 0x00, 16777 // flags 16778 0x00, 0x00, 0x00, 0x00, 16779 // pre_defined 16780 0x76, 0x69, 0x64, 0x65, 16781 // handler_type: 'vide' 16782 0x00, 0x00, 0x00, 0x00, 16783 // reserved 16784 0x00, 0x00, 0x00, 0x00, 16785 // reserved 16786 0x00, 0x00, 0x00, 0x00, 16787 // reserved 16788 0x56, 0x69, 0x64, 0x65, 0x6f, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler' 16789 ]); 16790 var audioHdlr = new Uint8Array([0x00, 16791 // version 0 16792 0x00, 0x00, 0x00, 16793 // flags 16794 0x00, 0x00, 0x00, 0x00, 16795 // pre_defined 16796 0x73, 0x6f, 0x75, 0x6e, 16797 // handler_type: 'soun' 16798 0x00, 0x00, 0x00, 0x00, 16799 // reserved 16800 0x00, 0x00, 0x00, 0x00, 16801 // reserved 16802 0x00, 0x00, 0x00, 0x00, 16803 // reserved 16804 0x53, 0x6f, 0x75, 0x6e, 0x64, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler' 16805 ]); 16806 MP4.HDLR_TYPES = { 16807 video: videoHdlr, 16808 audio: audioHdlr 16809 }; 16810 var dref = new Uint8Array([0x00, 16811 // version 0 16812 0x00, 0x00, 0x00, 16813 // flags 16814 0x00, 0x00, 0x00, 0x01, 16815 // entry_count 16816 0x00, 0x00, 0x00, 0x0c, 16817 // entry_size 16818 0x75, 0x72, 0x6c, 0x20, 16819 // 'url' type 16820 0x00, 16821 // version 0 16822 0x00, 0x00, 0x01 // entry_flags 16823 ]); 16824 var stco = new Uint8Array([0x00, 16825 // version 16826 0x00, 0x00, 0x00, 16827 // flags 16828 0x00, 0x00, 0x00, 0x00 // entry_count 16829 ]); 16830 MP4.STTS = MP4.STSC = MP4.STCO = stco; 16831 MP4.STSZ = new Uint8Array([0x00, 16832 // version 16833 0x00, 0x00, 0x00, 16834 // flags 16835 0x00, 0x00, 0x00, 0x00, 16836 // sample_size 16837 0x00, 0x00, 0x00, 0x00 // sample_count 16838 ]); 16839 MP4.VMHD = new Uint8Array([0x00, 16840 // version 16841 0x00, 0x00, 0x01, 16842 // flags 16843 0x00, 0x00, 16844 // graphicsmode 16845 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // opcolor 16846 ]); 16847 MP4.SMHD = new Uint8Array([0x00, 16848 // version 16849 0x00, 0x00, 0x00, 16850 // flags 16851 0x00, 0x00, 16852 // balance 16853 0x00, 0x00 // reserved 16854 ]); 16855 MP4.STSD = new Uint8Array([0x00, 16856 // version 0 16857 0x00, 0x00, 0x00, 16858 // flags 16859 0x00, 0x00, 0x00, 0x01]); // entry_count 16860 16861 var majorBrand = new Uint8Array([105, 115, 111, 109]); // isom 16862 var avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1 16863 var minorVersion = new Uint8Array([0, 0, 0, 1]); 16864 MP4.FTYP = MP4.box(MP4.types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand); 16865 MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref)); 16866 }; 16867 MP4.box = function box(type) { 16868 var size = 8; 16869 for (var _len = arguments.length, payload = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 16870 payload[_key - 1] = arguments[_key]; 16871 } 16872 var i = payload.length; 16873 var len = i; 16874 // calculate the total size we need to allocate 16875 while (i--) { 16876 size += payload[i].byteLength; 16877 } 16878 var result = new Uint8Array(size); 16879 result[0] = size >> 24 & 0xff; 16880 result[1] = size >> 16 & 0xff; 16881 result[2] = size >> 8 & 0xff; 16882 result[3] = size & 0xff; 16883 result.set(type, 4); 16884 // copy the payload into the result 16885 for (i = 0, size = 8; i < len; i++) { 16886 // copy payload[i] array @ offset size 16887 result.set(payload[i], size); 16888 size += payload[i].byteLength; 16889 } 16890 return result; 16891 }; 16892 MP4.hdlr = function hdlr(type) { 16893 return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]); 16894 }; 16895 MP4.mdat = function mdat(data) { 16896 return MP4.box(MP4.types.mdat, data); 16897 }; 16898 MP4.mdhd = function mdhd(timescale, duration) { 16899 duration *= timescale; 16900 var upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)); 16901 var lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); 16902 return MP4.box(MP4.types.mdhd, new Uint8Array([0x01, 16903 // version 1 16904 0x00, 0x00, 0x00, 16905 // flags 16906 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 16907 // creation_time 16908 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 16909 // modification_time 16910 timescale >> 24 & 0xff, timescale >> 16 & 0xff, timescale >> 8 & 0xff, timescale & 0xff, 16911 // timescale 16912 upperWordDuration >> 24, upperWordDuration >> 16 & 0xff, upperWordDuration >> 8 & 0xff, upperWordDuration & 0xff, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xff, lowerWordDuration >> 8 & 0xff, lowerWordDuration & 0xff, 0x55, 0xc4, 16913 // 'und' language (undetermined) 16914 0x00, 0x00])); 16915 }; 16916 MP4.mdia = function mdia(track) { 16917 return MP4.box(MP4.types.mdia, MP4.mdhd(track.timescale, track.duration), MP4.hdlr(track.type), MP4.minf(track)); 16918 }; 16919 MP4.mfhd = function mfhd(sequenceNumber) { 16920 return MP4.box(MP4.types.mfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, 16921 // flags 16922 sequenceNumber >> 24, sequenceNumber >> 16 & 0xff, sequenceNumber >> 8 & 0xff, sequenceNumber & 0xff // sequence_number 16923 ])); 16924 }; 16925 MP4.minf = function minf(track) { 16926 if (track.type === 'audio') { 16927 return MP4.box(MP4.types.minf, MP4.box(MP4.types.smhd, MP4.SMHD), MP4.DINF, MP4.stbl(track)); 16928 } else { 16929 return MP4.box(MP4.types.minf, MP4.box(MP4.types.vmhd, MP4.VMHD), MP4.DINF, MP4.stbl(track)); 16930 } 16931 }; 16932 MP4.moof = function moof(sn, baseMediaDecodeTime, track) { 16933 return MP4.box(MP4.types.moof, MP4.mfhd(sn), MP4.traf(track, baseMediaDecodeTime)); 16934 }; 16935 MP4.moov = function moov(tracks) { 16936 var i = tracks.length; 16937 var boxes = []; 16938 while (i--) { 16939 boxes[i] = MP4.trak(tracks[i]); 16940 } 16941 return MP4.box.apply(null, [MP4.types.moov, MP4.mvhd(tracks[0].timescale, tracks[0].duration
vendor: 5,355 bytes, lines 16941-17087
16941)].concat(boxes).concat(MP4.mvex(tracks))); 16942 }; 16943 MP4.mvex = function mvex(tracks) { 16944 var i = tracks.length; 16945 var boxes = []; 16946 while (i--) { 16947 boxes[i] = MP4.trex(tracks[i]); 16948 } 16949 return MP4.box.apply(null, [MP4.types.mvex].concat(boxes)); 16950 }; 16951 MP4.mvhd = function mvhd(timescale, duration) { 16952 duration *= timescale; 16953 var upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)); 16954 var lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); 16955 var bytes = new Uint8Array([0x01, 16956 // version 1 16957 0x00, 0x00, 0x00, 16958 // flags 16959 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 16960 // creation_time 16961 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 16962 // modification_time 16963 timescale >> 24 & 0xff, timescale >> 16 & 0xff, timescale >> 8 & 0xff, timescale & 0xff, 16964 // timescale 16965 upperWordDuration >> 24, upperWordDuration >> 16 & 0xff, upperWordDuration >> 8 & 0xff, upperWordDuration & 0xff, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xff, lowerWordDuration >> 8 & 0xff, lowerWordDuration & 0xff, 0x00, 0x01, 0x00, 0x00, 16966 // 1.0 rate 16967 0x01, 0x00, 16968 // 1.0 volume 16969 0x00, 0x00, 16970 // reserved 16971 0x00, 0x00, 0x00, 0x00, 16972 // reserved 16973 0x00, 0x00, 0x00, 0x00, 16974 // reserved 16975 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 16976 // transformation: unity matrix 16977 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 16978 // pre_defined 16979 0xff, 0xff, 0xff, 0xff // next_track_ID 16980 ]); 16981 return MP4.box(MP4.types.mvhd, bytes); 16982 }; 16983 MP4.sdtp = function sdtp(track) { 16984 var samples = track.samples || []; 16985 var bytes = new Uint8Array(4 + samples.length); 16986 var i; 16987 var flags; 16988 // leave the full box header (4 bytes) all zero 16989 // write the sample table 16990 for (i = 0; i < samples.length; i++) { 16991 flags = samples[i].flags; 16992 bytes[i + 4] = flags.dependsOn << 4 | flags.isDependedOn << 2 | flags.hasRedundancy; 16993 } 16994 return MP4.box(MP4.types.sdtp, bytes); 16995 }; 16996 MP4.stbl = function stbl(track) { 16997 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)); 16998 }; 16999 MP4.avc1 = function avc1(track) { 17000 var sps = []; 17001 var pps = []; 17002 var i; 17003 var data; 17004 var len; 17005 // assemble the SPSs 17006 17007 for (i = 0; i < track.sps.length; i++) { 17008 data = track.sps[i]; 17009 len = data.byteLength; 17010 sps.push(len >>> 8 & 0xff); 17011 sps.push(len & 0xff); 17012 17013 // SPS 17014 sps = sps.concat(Array.prototype.slice.call(data)); 17015 } 17016 17017 // assemble the PPSs 17018 for (i = 0; i < track.pps.length; i++) { 17019 data = track.pps[i]; 17020 len = data.byteLength; 17021 pps.push(len >>> 8 & 0xff); 17022 pps.push(len & 0xff); 17023 pps = pps.concat(Array.prototype.slice.call(data)); 17024 } 17025 var avcc = MP4.box(MP4.types.avcC, new Uint8Array([0x01, 17026 // version 17027 sps[3], 17028 // profile 17029 sps[4], 17030 // profile compat 17031 sps[5], 17032 // level 17033 0xfc | 3, 17034 // lengthSizeMinusOne, hard-coded to 4 bytes 17035 0xe0 | track.sps.length // 3bit reserved (111) + numOfSequenceParameterSets 17036 ].concat(sps).concat([track.pps.length // numOfPictureParameterSets 17037 ]).concat(pps))); // "PPS" 17038 var width = track.width; 17039 var height = track.height; 17040 var hSpacing = track.pixelRatio[0]; 17041 var vSpacing = track.pixelRatio[1]; 17042 return MP4.box(MP4.types.avc1, new Uint8Array([0x00, 0x00, 0x00, 17043 // reserved 17044 0x00, 0x00, 0x00, 17045 // reserved 17046 0x00, 0x01, 17047 // data_reference_index 17048 0x00, 0x00, 17049 // pre_defined 17050 0x00, 0x00, 17051 // reserved 17052 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 17053 // pre_defined 17054 width >> 8 & 0xff, width & 0xff, 17055 // width 17056 height >> 8 & 0xff, height & 0xff, 17057 // height 17058 0x00, 0x48, 0x00, 0x00, 17059 // horizresolution 17060 0x00, 0x48, 0x00, 0x00, 17061 // vertresolution 17062 0x00, 0x00, 0x00, 0x00, 17063 // reserved 17064 0x00, 0x01, 17065 // frame_count 17066 0x12, 0x64, 0x61, 0x69, 0x6c, 17067 // dailymotion/hls.js 17068 0x79, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x68, 0x6c, 0x73, 0x2e, 0x6a, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 17069 // compressorname 17070 0x00, 0x18, 17071 // depth = 24 17072 0x11, 0x11]), 17073 // pre_defined = -1 17074 avcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, 17075 // bufferSizeDB 17076 0x00, 0x2d, 0xc6, 0xc0, 17077 // maxBitrate 17078 0x00, 0x2d, 0xc6, 0xc0])), 17079 // avgBitrate 17080 MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24, 17081 // hSpacing 17082 hSpacing >> 16 & 0xff, hSpacing >> 8 & 0xff, hSpacing & 0xff, vSpacing >> 24, 17083 // vSpacing 17084 vSpacing >> 16 & 0xff, vSpacing >> 8 & 0xff, vSpacing & 0xff]))); 17085 }; 17086 MP4.esds = function esds(track) { 17087 var config
vendor: 5,207 bytes, lines 17087-17231
17087len = track.config.length; 17088 return new Uint8Array([0x00, 17089 // version 0 17090 0x00, 0x00, 0x00, 17091 // flags 17092 17093 0x03, 17094 // descriptor_type 17095 0x17 + configlen, 17096 // length 17097 0x00, 0x01, 17098 // es_id 17099 0x00, 17100 // stream_priority 17101 17102 0x04, 17103 // descriptor_type 17104 0x0f + configlen, 17105 // length 17106 0x40, 17107 // codec : mpeg4_audio 17108 0x15, 17109 // stream_type 17110 0x00, 0x00, 0x00, 17111 // buffer_size 17112 0x00, 0x00, 0x00, 0x00, 17113 // maxBitrate 17114 0x00, 0x00, 0x00, 0x00, 17115 // avgBitrate 17116 17117 0x05 // descriptor_type 17118 ].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor 17119 }; 17120 MP4.audioStsd = function audioStsd(track) { 17121 var samplerate = track.samplerate; 17122 return new Uint8Array([0x00, 0x00, 0x00, 17123 // reserved 17124 0x00, 0x00, 0x00, 17125 // reserved 17126 0x00, 0x01, 17127 // data_reference_index 17128 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 17129 // reserved 17130 0x00, track.channelCount, 17131 // channelcount 17132 0x00, 0x10, 17133 // sampleSize:16bits 17134 0x00, 0x00, 0x00, 0x00, 17135 // reserved2 17136 samplerate >> 8 & 0xff, samplerate & 0xff, 17137 // 17138 0x00, 0x00]); 17139 }; 17140 MP4.mp4a = function mp4a(track) { 17141 return MP4.box(MP4.types.mp4a, MP4.audioStsd(track), MP4.box(MP4.types.esds, MP4.esds(track))); 17142 }; 17143 MP4.mp3 = function mp3(track) { 17144 return MP4.box(MP4.types['.mp3'], MP4.audioStsd(track)); 17145 }; 17146 MP4.ac3 = function ac3(track) { 17147 return MP4.box(MP4.types['ac-3'], MP4.audioStsd(track), MP4.box(MP4.types.dac3, track.config)); 17148 }; 17149 MP4.stsd = function stsd(track) { 17150 if (track.type === 'audio') { 17151 if (track.segmentCodec === 'mp3' && track.codec === 'mp3') { 17152 return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp3(track)); 17153 } 17154 if (track.segmentCodec === 'ac3') { 17155 return MP4.box(MP4.types.stsd, MP4.STSD, MP4.ac3(track)); 17156 } 17157 return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track)); 17158 } else if (track.segmentCodec === 'avc') { 17159 return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track)); 17160 } else { 17161 return MP4.box(MP4.types.stsd, MP4.STSD, MP4.hvc1(track)); 17162 } 17163 }; 17164 MP4.tkhd = function tkhd(track) { 17165 var id = track.id; 17166 var duration = track.duration * track.timescale; 17167 var width = track.width; 17168 var height = track.height; 17169 var upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)); 17170 var lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); 17171 return MP4.box(MP4.types.tkhd, new Uint8Array([0x01, 17172 // version 1 17173 0x00, 0x00, 0x07, 17174 // flags 17175 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 17176 // creation_time 17177 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 17178 // modification_time 17179 id >> 24 & 0xff, id >> 16 & 0xff, id >> 8 & 0xff, id & 0xff, 17180 // track_ID 17181 0x00, 0x00, 0x00, 0x00, 17182 // reserved 17183 upperWordDuration >> 24, upperWordDuration >> 16 & 0xff, upperWordDuration >> 8 & 0xff, upperWordDuration & 0xff, lowerWordDuration >> 24, lowerWordDuration >> 16 & 0xff, lowerWordDuration >> 8 & 0xff, lowerWordDuration & 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 17184 // reserved 17185 0x00, 0x00, 17186 // layer 17187 0x00, 0x00, 17188 // alternate_group 17189 0x00, 0x00, 17190 // non-audio track volume 17191 0x00, 0x00, 17192 // reserved 17193 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 17194 // transformation: unity matrix 17195 width >> 8 & 0xff, width & 0xff, 0x00, 0x00, 17196 // width 17197 height >> 8 & 0xff, height & 0xff, 0x00, 0x00 // height 17198 ])); 17199 }; 17200 MP4.traf = function traf(track, baseMediaDecodeTime) { 17201 var sampleDependencyTable = MP4.sdtp(track); 17202 var id = track.id; 17203 var upperWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1)); 17204 var lowerWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1)); 17205 return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([0x00, 17206 // version 0 17207 0x00, 0x00, 0x00, 17208 // flags 17209 id >> 24, id >> 16 & 0xff, id >> 8 & 0xff, id & 0xff // track_ID 17210 ])), MP4.box(MP4.types.tfdt, new Uint8Array([0x01, 17211 // version 1 17212 0x00, 0x00, 0x00, 17213 // flags 17214 upperWordBaseMediaDecodeTime >> 24, upperWordBaseMediaDecodeTime >> 16 & 0xff, upperWordBaseMediaDecodeTime >> 8 & 0xff, upperWordBaseMediaDecodeTime & 0xff, lowerWordBaseMediaDecodeTime >> 24, lowerWordBaseMediaDecodeTime >> 16 & 0xff, lowerWordBaseMediaDecodeTime >> 8 & 0xff, lowerWordBaseMediaDecodeTime & 0xff])), MP4.trun(track, sampleDependencyTable.length + 16 + 17215 // tfhd 17216 20 + 17217 // tfdt 17218 8 + 17219 // traf header 17220 16 + 17221 // mfhd 17222 8 + 17223 // moof header 17224 8), 17225 // mdat header 17226 sampleDependencyTable); 17227 } 17228 17229 /** 17230 * Generate a track box. 17231 * @param track a track definition�
vendor: 6,523 bytes, lines 17231-17393
17231 17232 */; 17233 MP4.trak = function trak(track) { 17234 track.duration = track.duration || 0xffffffff; 17235 return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track)); 17236 }; 17237 MP4.trex = function trex(track) { 17238 var id = track.id; 17239 return MP4.box(MP4.types.trex, new Uint8Array([0x00, 17240 // version 0 17241 0x00, 0x00, 0x00, 17242 // flags 17243 id >> 24, id >> 16 & 0xff, id >> 8 & 0xff, id & 0xff, 17244 // track_ID 17245 0x00, 0x00, 0x00, 0x01, 17246 // default_sample_description_index 17247 0x00, 0x00, 0x00, 0x00, 17248 // default_sample_duration 17249 0x00, 0x00, 0x00, 0x00, 17250 // default_sample_size 17251 0x00, 0x01, 0x00, 0x01 // default_sample_flags 17252 ])); 17253 }; 17254 MP4.trun = function trun(track, offset) { 17255 var samples = track.samples || []; 17256 var len = samples.length; 17257 var arraylen = 12 + 16 * len; 17258 var array = new Uint8Array(arraylen); 17259 var i; 17260 var sample; 17261 var duration; 17262 var size; 17263 var flags; 17264 var cts; 17265 offset += 8 + arraylen; 17266 array.set([track.type === 'video' ? 0x01 : 0x00, 17267 // version 1 for video with signed-int sample_composition_time_offset 17268 0x00, 0x0f, 0x01, 17269 // flags 17270 len >>> 24 & 0xff, len >>> 16 & 0xff, len >>> 8 & 0xff, len & 0xff, 17271 // sample_count 17272 offset >>> 24 & 0xff, offset >>> 16 & 0xff, offset >>> 8 & 0xff, offset & 0xff // data_offset 17273 ], 0); 17274 for (i = 0; i < len; i++) { 17275 sample = samples[i]; 17276 duration = sample.duration; 17277 size = sample.size; 17278 flags = sample.flags; 17279 cts = sample.cts; 17280 array.set([duration >>> 24 & 0xff, duration >>> 16 & 0xff, duration >>> 8 & 0xff, duration & 0xff, 17281 // sample_duration 17282 size >>> 24 & 0xff, size >>> 16 & 0xff, size >>> 8 & 0xff, size & 0xff, 17283 // sample_size 17284 flags.isLeading << 2 | flags.dependsOn, flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.paddingValue << 1 | flags.isNonSync, flags.degradPrio & 0xf0 << 8, flags.degradPrio & 0x0f, 17285 // sample_flags 17286 cts >>> 24 & 0xff, cts >>> 16 & 0xff, cts >>> 8 & 0xff, cts & 0xff // sample_composition_time_offset 17287 ], 12 + 16 * i); 17288 } 17289 return MP4.box(MP4.types.trun, array); 17290 }; 17291 MP4.initSegment = function initSegment(tracks) { 17292 if (!MP4.types) { 17293 MP4.init(); 17294 } 17295 var movie = MP4.moov(tracks); 17296 var result = appendUint8Array(MP4.FTYP, movie); 17297 return result; 17298 }; 17299 MP4.hvc1 = function hvc1(track) { 17300 var ps = track.params; 17301 var units = [track.vps, track.sps, track.pps]; 17302 var NALuLengthSize = 4; 17303 var config = new Uint8Array([0x01, 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, 0x00, 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]); 17304 17305 // compute hvcC size in bytes 17306 var length = config.length; 17307 for (var i = 0; i < units.length; i += 1) { 17308 length += 3; 17309 for (var j = 0; j < units[i].length; j += 1) { 17310 length += 2 + units[i][j].length; 17311 } 17312 } 17313 var hvcC = new Uint8Array(length); 17314 hvcC.set(config, 0); 17315 length = config.length; 17316 // append parameter set units: one vps, one or more sps and pps 17317 var iMax = units.length - 1; 17318 for (var _i = 0; _i < units.length; _i += 1) { 17319 hvcC.set(new Uint8Array([32 + _i | (_i === iMax ? 128 : 0), 0x00, units[_i].length]), length); 17320 length += 3; 17321 for (var _j = 0; _j < units[_i].length; _j += 1) { 17322 hvcC.set(new Uint8Array([units[_i][_j].length >> 8, units[_i][_j].length & 255]), length); 17323 length += 2; 17324 hvcC.set(units[_i][_j], length); 17325 length += units[_i][_j].length; 17326 } 17327 } 17328 var hvcc = MP4.box(MP4.types.hvcC, hvcC); 17329 var width = track.width; 17330 var height = track.height; 17331 var hSpacing = track.pixelRatio[0]; 17332 var vSpacing = track.pixelRatio[1]; 17333 return MP4.box(MP4.types.hvc1, new Uint8Array([0x00, 0x00, 0x00, 17334 // reserved 17335 0x00, 0x00, 0x00, 17336 // reserved 17337 0x00, 0x01, 17338 // data_reference_index 17339 0x00, 0x00, 17340 // pre_defined 17341 0x00, 0x00, 17342 // reserved 17343 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 17344 // pre_defined 17345 width >> 8 & 0xff, width & 0xff, 17346 // width 17347 height >> 8 & 0xff, height & 0xff, 17348 // height 17349 0x00, 0x48, 0x00, 0x00, 17350 // horizresolution 17351 0x00, 0x48, 0x00, 0x00, 17352 // vertresolution 17353 0x00, 0x00, 0x00, 0x00, 17354 // reserved 17355 0x00, 0x01, 17356 // frame_count 17357 0x12, 0x64, 0x61, 0x69, 0x6c, 17358 // dailymotion/hls.js 17359 0x79, 0x6d, 0x6f, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x68, 0x6c, 0x73, 0x2e, 0x6a, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 17360 // compressorname 17361 0x00, 0x18, 17362 // depth = 24 17363 0x11, 0x11]), 17364 // pre_defined = -1 17365 hvcc, MP4.box(MP4.types.btrt, new Uint8Array([0x00, 0x1c, 0x9c, 0x80, 17366 // bufferSizeDB 17367 0x00, 0x2d, 0xc6, 0xc0, 17368 // maxBitrate 17369 0x00, 0x2d, 0xc6, 0xc0])), 17370 // avgBitrate 17371 MP4.box(MP4.types.pasp, new Uint8Array([hSpacing >> 24, 17372 // hSpacing 17373 hSpacing >> 16 & 0xff, hSpacing >> 8 & 0xff, hSpacing & 0xff, vSpacing >> 24, 17374 // vSpacing 17375 vSpacing >> 16 & 0xff, vSpacing >> 8 & 0xff, vSpacing & 0xff]))); 17376 }; 17377 return MP4; 17378 }(); 17379 MP4.types = void 0; 17380 MP4.HDLR_TYPES = void 0; 17381 MP4.STTS = void 0; 17382 MP4.STSC = void 0; 17383 MP4.STCO = void 0; 17384 MP4.STSZ = void 0; 17385 MP4.VMHD = void 0; 17386 MP4.SMHD = void 0; 17387 MP4.STSD = void 0; 17388 MP4.FTYP = void 0; 17389 MP4.DINF = void 0; 17390 17391 var MPEG_TS_CLOCK_FREQ_HZ = 90000; 17392 function toTimescaleFromBase(baseTime, destScale, srcBase, round) { 17393 if (
vendor: 3,323 bytes, lines 17393-17477
17393round === void 0) { 17394 round = false; 17395 } 17396 var result = baseTime * destScale * srcBase; // equivalent to `(value * scale) / (1 / base)` 17397 return round ? Math.round(result) : result; 17398 } 17399 function toMsFromMpegTsClock(baseTime, round) { 17400 return toTimescaleFromBase(baseTime, 1000, 1 / MPEG_TS_CLOCK_FREQ_HZ, round); 17401 } 17402 17403 var MAX_SILENT_FRAME_DURATION = 10 * 1000; // 10 seconds 17404 var AAC_SAMPLES_PER_FRAME = 1024; 17405 var MPEG_AUDIO_SAMPLE_PER_FRAME = 1152; 17406 var AC3_SAMPLES_PER_FRAME = 1536; 17407 var chromeVersion = null; 17408 var safariWebkitVersion = null; 17409 var MP4Remuxer = /*#__PURE__*/function () { 17410 function MP4Remuxer(observer, config, typeSupported, logger) { 17411 this.logger = void 0; 17412 this.observer = void 0; 17413 this.config = void 0; 17414 this.typeSupported = void 0; 17415 this.ISGenerated = false; 17416 this._initPTS = null; 17417 this._initDTS = null; 17418 this.nextAvcDts = null; 17419 this.nextAudioPts = null; 17420 this.videoSampleDuration = null; 17421 this.isAudioContiguous = false; 17422 this.isVideoContiguous = false; 17423 this.videoTrackConfig = void 0; 17424 this.observer = observer; 17425 this.config = config; 17426 this.typeSupported = typeSupported; 17427 this.logger = logger; 17428 this.ISGenerated = false; 17429 if (chromeVersion === null) { 17430 var userAgent = navigator.userAgent || ''; 17431 var result = userAgent.match(/Chrome\/(\d+)/i); 17432 chromeVersion = result ? parseInt(result[1]) : 0; 17433 } 17434 if (safariWebkitVersion === null) { 17435 var _result = navigator.userAgent.match(/Safari\/(\d+)/i); 17436 safariWebkitVersion = _result ? parseInt(_result[1]) : 0; 17437 } 17438 } 17439 var _proto = MP4Remuxer.prototype; 17440 _proto.destroy = function destroy() { 17441 // @ts-ignore 17442 this.config = this.videoTrackConfig = this._initPTS = this._initDTS = null; 17443 }; 17444 _proto.resetTimeStamp = function resetTimeStamp(defaultTimeStamp) { 17445 this.logger.log('[mp4-remuxer]: initPTS & initDTS reset'); 17446 this._initPTS = this._initDTS = defaultTimeStamp; 17447 }; 17448 _proto.resetNextTimestamp = function resetNextTimestamp() { 17449 this.logger.log('[mp4-remuxer]: reset next timestamp'); 17450 this.isVideoContiguous = false; 17451 this.isAudioContiguous = false; 17452 }; 17453 _proto.resetInitSegment = function resetInitSegment() { 17454 this.logger.log('[mp4-remuxer]: ISGenerated flag reset'); 17455 this.ISGenerated = false; 17456 this.videoTrackConfig = undefined; 17457 }; 17458 _proto.getVideoStartPts = function getVideoStartPts(videoSamples) { 17459 var rolloverDetected = false; 17460 var startPTS = videoSamples.reduce(function (minPTS, sample) { 17461 var delta = sample.pts - minPTS; 17462 if (delta < -4294967296) { 17463 // 2^32, see PTSNormalize for reasoning, but we're hitting a rollover here, and we don't want that to impact the timeOffset calculation 17464 rolloverDetected = true; 17465 return normalizePts(minPTS, sample.pts); 17466 } else if (delta > 0) { 17467 return minPTS; 17468 } else { 17469 return sample.pts; 17470 } 17471 }, videoSamples[0].pts); 17472 if (rolloverDetected) { 17473 this.logger.debug('PTS rollover detected'); 17474 } 17475 return startPTS; 17476 }; 17477 _proto.remux = function remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOff
vendor: 8,504 bytes, lines 17477-17638
17477set, flush, playlistType) { 17478 var video; 17479 var audio; 17480 var initSegment; 17481 var text; 17482 var id3; 17483 var independent; 17484 var audioTimeOffset = timeOffset; 17485 var videoTimeOffset = timeOffset; 17486 17487 // If we're remuxing audio and video progressively, wait until we've received enough samples for each track before proceeding. 17488 // This is done to synchronize the audio and video streams. We know if the current segment will have samples if the "pid" 17489 // parameter is greater than -1. The pid is set when the PMT is parsed, which contains the tracks list. 17490 // However, if the initSegment has already been generated, or we've reached the end of a segment (flush), 17491 // then we can remux one track without waiting for the other. 17492 var hasAudio = audioTrack.pid > -1; 17493 var hasVideo = videoTrack.pid > -1; 17494 var length = videoTrack.samples.length; 17495 var enoughAudioSamples = audioTrack.samples.length > 0; 17496 var enoughVideoSamples = flush && length > 0 || length > 1; 17497 var canRemuxAvc = (!hasAudio || enoughAudioSamples) && (!hasVideo || enoughVideoSamples) || this.ISGenerated || flush; 17498 if (canRemuxAvc) { 17499 if (this.ISGenerated) { 17500 var _videoTrack$pixelRati, _config$pixelRatio, _videoTrack$pixelRati2, _config$pixelRatio2; 17501 var config = this.videoTrackConfig; 17502 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.nextAudioPts === null && enoughAudioSamples) { 17503 this.resetInitSegment(); 17504 } 17505 } 17506 if (!this.ISGenerated) { 17507 initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset); 17508 } 17509 var isVideoContiguous = this.isVideoContiguous; 17510 var firstKeyFrameIndex = -1; 17511 var firstKeyFramePTS; 17512 if (enoughVideoSamples) { 17513 firstKeyFrameIndex = findKeyframeIndex(videoTrack.samples); 17514 if (!isVideoContiguous && this.config.forceKeyFrameOnDiscontinuity) { 17515 independent = true; 17516 if (firstKeyFrameIndex > 0) { 17517 this.logger.warn("[mp4-remuxer]: Dropped " + firstKeyFrameIndex + " out of " + length + " video samples due to a missing keyframe"); 17518 var startPTS = this.getVideoStartPts(videoTrack.samples); 17519 videoTrack.samples = videoTrack.samples.slice(firstKeyFrameIndex); 17520 videoTrack.dropped += firstKeyFrameIndex; 17521 videoTimeOffset += (videoTrack.samples[0].pts - startPTS) / videoTrack.inputTimeScale; 17522 firstKeyFramePTS = videoTimeOffset; 17523 } else if (firstKeyFrameIndex === -1) { 17524 this.logger.warn("[mp4-remuxer]: No keyframe found out of " + length + " video samples"); 17525 independent = false; 17526 } 17527 } 17528 } 17529 if (this.ISGenerated) { 17530 if (enoughAudioSamples && enoughVideoSamples) { 17531 // timeOffset is expected to be the offset of the first timestamp of this fragment (first DTS) 17532 // if first audio DTS is not aligned with first video DTS then we need to take that into account 17533 // when providing timeOffset to remuxAudio / remuxVideo. if we don't do that, there might be a permanent / small 17534 // drift between audio and video streams 17535 var _startPTS = this.getVideoStartPts(videoTrack.samples); 17536 var tsDelta = normalizePts(audioTrack.samples[0].pts, _startPTS) - _startPTS; 17537 var audiovideoTimestampDelta = tsDelta / videoTrack.inputTimeScale; 17538 audioTimeOffset += Math.max(0, audiovideoTimestampDelta); 17539 videoTimeOffset += Math.max(0, -audiovideoTimestampDelta); 17540 } 17541 17542 // Purposefully remuxing audio before video, so that remuxVideo can use nextAudioPts, which is calculated in remuxAudio. 17543 if (enoughAudioSamples) { 17544 // if initSegment was generated without audio samples, regenerate it again 17545 if (!audioTrack.samplerate) { 17546 this.logger.warn('[mp4-remuxer]: regenerate InitSegment as audio detected'); 17547 initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset); 17548 } 17549 audio = this.remuxAudio(audioTrack, audioTimeOffset, this.isAudioContiguous, accurateTimeOffset, hasVideo || enoughVideoSamples || playlistType === PlaylistLevelType.AUDIO ? videoTimeOffset : undefined); 17550 if (enoughVideoSamples) { 17551 var audioTrackLength = audio ? audio.endPTS - audio.startPTS : 0; 17552 // if initSegment was generated without video samples, regenerate it again 17553 if (!videoTrack.inputTimeScale) { 17554 this.logger.warn('[mp4-remuxer]: regenerate InitSegment as video detected'); 17555 initSegment = this.generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset); 17556 } 17557 video = this.remuxVideo(videoTrack, videoTimeOffset, isVideoContiguous, audioTrackLength); 17558 } 17559 } else if (enoughVideoSamples) { 17560 video = this.remuxVideo(videoTrack, videoTimeOffset, isVideoContiguous, 0); 17561 } 17562 if (video) { 17563 video.firstKeyFrame = firstKeyFrameIndex; 17564 video.independent = firstKeyFrameIndex !== -1; 17565 video.firstKeyFramePTS = firstKeyFramePTS; 17566 } 17567 } 17568 } 17569 17570 // Allow ID3 and text to remux, even if more audio/video samples are required 17571 if (this.ISGenerated && this._initPTS && this._initDTS) { 17572 if (id3Track.samples.length) { 17573 id3 = flushTextTrackMetadataCueSamples(id3Track, timeOffset, this._initPTS, this._initDTS); 17574 } 17575 if (textTrack.samples.length) { 17576 text = flushTextTrackUserdataCueSamples(textTrack, timeOffset, this._initPTS); 17577 } 17578 } 17579 return { 17580 audio: audio, 17581 video: video, 17582 initSegment: initSegment, 17583 independent: independent, 17584 text: text, 17585 id3: id3 17586 }; 17587 }; 17588 _proto.generateIS = function generateIS(audioTrack, videoTrack, timeOffset, accurateTimeOffset) { 17589 var audioSamples = audioTrack.samples; 17590 var videoSamples = videoTrack.samples; 17591 var typeSupported = this.typeSupported; 17592 var tracks = {}; 17593 var _initPTS = this._initPTS; 17594 var computePTSDTS = !_initPTS || accurateTimeOffset; 17595 var container = 'audio/mp4'; 17596 var initPTS; 17597 var initDTS; 17598 var timescale; 17599 if (computePTSDTS) { 17600 initPTS = initDTS = Infinity; 17601 } 17602 if (audioTrack.config && audioSamples.length) { 17603 // let's use audio sampling rate as MP4 time scale. 17604 // rationale is that there is a integer nb of audio frames per audio sample (1024 for AAC) 17605 // using audio sampling rate here helps having an integer MP4 frame duration 17606 // this avoids potential rounding issue and AV sync issue 17607 audioTrack.timescale = audioTrack.samplerate; 17608 switch (audioTrack.segmentCodec) { 17609 case 'mp3': 17610 if (typeSupported.mpeg) { 17611 // Chrome and Safari 17612 container = 'audio/mpeg'; 17613 audioTrack.codec = ''; 17614 } else if (typeSupported.mp3) { 17615 // Firefox 17616 audioTrack.codec = 'mp3'; 17617 } 17618 break; 17619 case 'ac3': 17620 audioTrack.codec = 'ac-3'; 17621 break; 17622 } 17623 tracks.audio = { 17624 id: 'audio', 17625 container: container, 17626 codec: audioTrack.codec, 17627 initSegment: audioTrack.segmentCodec === 'mp3' && typeSupported.mpeg ? new Uint8Array(0) : MP4.initSegment([audioTrack]), 17628 metadata: { 17629 channelCount: audioTrack.channelCount 17630 } 17631 }; 17632 if (computePTSDTS) { 17633 timescale = audioTrack.inputTimeScale; 17634 if (!_initPTS || timescale !== _initPTS.timescale) { 17635 // remember first PTS of this demuxing context. for audio, PTS = DTS 17636 initPTS = initDTS = audioSamples[0].pts - Math.round(timescale * timeOffset); 17637 } else { 17638 computePTSDTS = false;
17639 } 17640 } 17641 } 17642 if (videoTrack.sps && videoTrack.pps && videoSamples.length) { 17643 // let's use input time scale as MP4 video timescale 17644 // we use input time scale straight away to avoid rounding issues on frame duration / cts computation 17645 videoTrack.timescale = videoTrack.inputTimeScale; 17646 tracks.video = { 17647 id: 'main', 17648 container: 'video/mp4', 17649 codec: videoTrack.codec, 17650 initSegment: MP4.initSegment([videoTrack]), 17651 metadata: { 17652 width: videoTrack.width, 17653 height: videoTrack.height 17654 } 17655 }; 17656 if (computePTSDTS) { 17657 timescale = videoTrack.inputTimeScale; 17658 if (!_initPTS || timescale !== _initPTS.timescale) { 17659 var startPTS = this.getVideoStartPts(videoSamples); 17660 var startOffset = Math.round(timescale * timeOffset); 17661 initDTS = Math.min(initDTS, normalizePts(videoSamples[0].dts, startPTS) - startOffset); 17662 initPTS = Math.min(initPTS, startPTS - startOffset); 17663 } else { 17664 computePTSDTS = false; 17665 } 17666 } 17667 this.videoTrackConfig = { 17668 width: videoTrack.width, 17669 height: videoTrack.height, 17670 pixelRatio: videoTrack.pixelRatio 17671 }; 17672 } 17673 if (Object.keys(tracks).length) { 17674 this.ISGenerated = true; 17675 if (computePTSDTS) { 17676 this._initPTS = { 17677 baseTime: initPTS, 17678 timescale: timescale 17679 }; 17680 this._initDTS = { 17681 baseTime: initDTS, 17682 timescale: timescale 17683 }; 17684 } else { 17685 initPTS = timescale = undefined; 17686 } 17687 return { 17688 tracks: tracks, 17689 initPTS: initPTS, 17690 timescale: timescale 17691 }; 17692 } 17693 }; 17694 _proto.remuxVideo = function remuxVideo(track, timeOffset, contiguous, audioTrackLength) { 17695 var timeScale = track.inputTimeScale; 17696 var inputSamples = track.samples; 17697 var outputSamples = []; 17698 var nbSamples = inputSamples.length; 17699 var initPTS = this._initPTS; 17700 var nextAvcDts = this.nextAvcDts; 17701 var offset = 8; 17702 var mp4SampleDuration = this.videoSampleDuration; 17703 var firstDTS; 17704 var lastDTS; 17705 var minPTS = Number.POSITIVE_INFINITY; 17706 var maxPTS = Number.NEGATIVE_INFINITY; 17707 var sortSamples = false; 17708 17709 // if parsed fragment is contiguous with last one, let's use last DTS value as reference 17710 if (!contiguous || nextAvcDts === null) { 17711 var pts = timeOffset * timeScale; 17712 var cts = inputSamples[0].pts - normalizePts(inputSamples[0].dts, inputSamples[0].pts); 17713 if (chromeVersion && nextAvcDts !== null && Math.abs(pts - cts - nextAvcDts) < 15000) { 17714 // treat as contigous to adjust samples that would otherwise produce video buffer gaps in Chrome 17715 contiguous = true; 17716 } else { 17717 // if not contiguous, let's use target timeOffset 17718 nextAvcDts = pts - cts; 17719 } 17720 } 17721 17722 // PTS is coded on 33bits, and can loop from -2^32 to 2^32 17723 // PTSNormalize will make PTS/DTS value monotonic, we use last known DTS value as reference value 17724 var initTime = initPTS.baseTime * timeScale / initPTS.timescale; 17725 for (var i = 0; i < nbSamples; i++) { 17726 var sample = inputSamples[i]; 17727 sample.pts = normalizePts(sample.pts - initTime, nextAvcDts); 17728 sample.dts = normalizePts(sample.dts - initTime, nextAvcDts); 17729 if (sample.dts < inputSamples[i > 0 ? i - 1 : i].dts) { 17730 sortSamples = true; 17731 } 17732 } 17733 17734 // sort video samples by DTS then PTS then demux id order 17735 if (sortSamples) { 17736 inputSamples.sort(function (a, b) { 17737 var deltadts = a.dts - b.dts; 17738 var deltapts = a.pts - b.pts; 17739 return deltadts || deltapts; 17740 }); 17741 } 17742 17743 // Get first/last DTS 17744 firstDTS = inputSamples[0].dts; 17745 lastDTS = inputSamples[inputSamples.length - 1].dts; 17746 17747 // Sample duration (as expected by trun MP4 boxes), should be the delta between sample DTS 17748 // set this constant duration as being the avg delta between consecutive DTS. 17749 var inputDuration = lastDTS - firstDTS; 17750 var averageSampleDuration = inputDuration ? Math.round(inputDuration / (nbSamples - 1)) : mp4SampleDuration || track.inputTimeScale / 30; 17751 17752 // if fragment are contiguous, detect hole/overlapping between fragments 17753 if (contiguous) { 17754 // check timestamp continuity across consecutive fragments (this is to remove inter-fragment gap/hole) 17755 var delta = firstDTS - nextAvcDts; 17756 var foundHole = delta > averageSampleDuration; 17757 var foundOverlap = delta < -1;
vendor: 7,699 bytes, lines 17758-17916
17758 if (foundHole || foundOverlap) { 17759 if (foundHole) { 17760 this.logger.warn((track.segmentCodec || '').toUpperCase() + ": " + toMsFromMpegTsClock(delta, true) + " ms (" + delta + "dts) hole between fragments detected at " + timeOffset.toFixed(3)); 17761 } else { 17762 this.logger.warn((track.segmentCodec || '').toUpperCase() + ": " + toMsFromMpegTsClock(-delta, true) + " ms (" + delta + "dts) overlapping between fragments detected at " + timeOffset.toFixed(3)); 17763 } 17764 if (!foundOverlap || nextAvcDts >= inputSamples[0].pts || chromeVersion) { 17765 firstDTS = nextAvcDts; 17766 var firstPTS = inputSamples[0].pts - delta; 17767 if (foundHole) { 17768 inputSamples[0].dts = firstDTS; 17769 inputSamples[0].pts = firstPTS; 17770 } else { 17771 var isPTSOrderRetained = true; 17772 for (var _i = 0; _i < inputSamples.length; _i++) { 17773 if (inputSamples[_i].dts > firstPTS && isPTSOrderRetained) { 17774 break; 17775 } 17776 var prevPTS = inputSamples[_i].pts; 17777 inputSamples[_i].dts -= delta; 17778 inputSamples[_i].pts -= delta; 17779 17780 // check to see if this sample's PTS order has changed 17781 // relative to the next one 17782 if (_i < inputSamples.length - 1) { 17783 var nextSamplePTS = inputSamples[_i + 1].pts; 17784 var currentSamplePTS = inputSamples[_i].pts; 17785 var currentOrder = nextSamplePTS <= currentSamplePTS; 17786 var prevOrder = nextSamplePTS <= prevPTS; 17787 isPTSOrderRetained = currentOrder == prevOrder; 17788 } 17789 } 17790 } 17791 this.logger.log("Video: Initial PTS/DTS adjusted: " + toMsFromMpegTsClock(firstPTS, true) + "/" + toMsFromMpegTsClock(firstDTS, true) + ", delta: " + toMsFromMpegTsClock(delta, true) + " ms"); 17792 } 17793 } 17794 } 17795 firstDTS = Math.max(0, firstDTS); 17796 var nbNalu = 0; 17797 var naluLen = 0; 17798 var dtsStep = firstDTS; 17799 for (var _i2 = 0; _i2 < nbSamples; _i2++) { 17800 // compute total/avc sample length and nb of NAL units 17801 var _sample = inputSamples[_i2]; 17802 var units = _sample.units; 17803 var nbUnits = units.length; 17804 var sampleLen = 0; 17805 for (var j = 0; j < nbUnits; j++) { 17806 sampleLen += units[j].data.length; 17807 } 17808 naluLen += sampleLen; 17809 nbNalu += nbUnits; 17810 _sample.length = sampleLen; 17811 17812 // ensure sample monotonic DTS 17813 if (_sample.dts < dtsStep) { 17814 _sample.dts = dtsStep; 17815 dtsStep += averageSampleDuration / 4 | 0 || 1; 17816 } else { 17817 dtsStep = _sample.dts; 17818 } 17819 minPTS = Math.min(_sample.pts, minPTS); 17820 maxPTS = Math.max(_sample.pts, maxPTS); 17821 } 17822 lastDTS = inputSamples[nbSamples - 1].dts; 17823 17824 /* concatenate the video data and construct the mdat in place 17825 (need 8 more bytes to fill length and mpdat type) */ 17826 var mdatSize = naluLen + 4 * nbNalu + 8; 17827 var mdat; 17828 try { 17829 mdat = new Uint8Array(mdatSize); 17830 } catch (err) { 17831 this.observer.emit(Events.ERROR, Events.ERROR, { 17832 type: ErrorTypes.MUX_ERROR, 17833 details: ErrorDetails.REMUX_ALLOC_ERROR, 17834 fatal: false, 17835 error: err, 17836 bytes: mdatSize, 17837 reason: "fail allocating video mdat " + mdatSize 17838 }); 17839 return; 17840 } 17841 var view = new DataView(mdat.buffer); 17842 view.setUint32(0, mdatSize); 17843 mdat.set(MP4.types.mdat, 4); 17844 var stretchedLastFrame = false; 17845 var minDtsDelta = Number.POSITIVE_INFINITY; 17846 var minPtsDelta = Number.POSITIVE_INFINITY; 17847 var maxDtsDelta = Number.NEGATIVE_INFINITY; 17848 var maxPtsDelta = Number.NEGATIVE_INFINITY; 17849 for (var _i3 = 0; _i3 < nbSamples; _i3++) { 17850 var VideoSample = inputSamples[_i3]; 17851 var VideoSampleUnits = VideoSample.units; 17852 var mp4SampleLength = 0; 17853 // convert NALU bitstream to MP4 format (prepend NALU with size field) 17854 for (var _j = 0, _nbUnits = VideoSampleUnits.length; _j < _nbUnits; _j++) { 17855 var unit = VideoSampleUnits[_j]; 17856 var unitData = unit.data; 17857 var unitDataLen = unit.data.byteLength; 17858 view.setUint32(offset, unitDataLen); 17859 offset += 4; 17860 mdat.set(unitData, offset); 17861 offset += unitDataLen; 17862 mp4SampleLength += 4 + unitDataLen; 17863 } 17864 17865 // expected sample duration is the Decoding Timestamp diff of consecutive samples 17866 var ptsDelta = void 0; 17867 if (_i3 < nbSamples - 1) { 17868 mp4SampleDuration = inputSamples[_i3 + 1].dts - VideoSample.dts; 17869 ptsDelta = inputSamples[_i3 + 1].pts - VideoSample.pts; 17870 } else { 17871 var config = this.config; 17872 var lastFrameDuration = _i3 > 0 ? VideoSample.dts - inputSamples[_i3 - 1].dts : averageSampleDuration; 17873 ptsDelta = _i3 > 0 ? VideoSample.pts - inputSamples[_i3 - 1].pts : averageSampleDuration; 17874 if (config.stretchShortVideoTrack && this.nextAudioPts !== null) { 17875 // In some cases, a segment's audio track duration may exceed the video track duration. 17876 // Since we've already remuxed audio, and we know how long the audio track is, we look to 17877 // see if the delta to the next segment is longer than maxBufferHole. 17878 // If so, playback would potentially get stuck, so we artificially inflate 17879 // the duration of the last frame to minimize any potential gap between segments. 17880 var gapTolerance = Math.floor(config.maxBufferHole * timeScale); 17881 var deltaToFrameEnd = (audioTrackLength ? minPTS + audioTrackLength * timeScale : this.nextAudioPts) - VideoSample.pts; 17882 if (deltaToFrameEnd > gapTolerance) { 17883 // We subtract lastFrameDuration from deltaToFrameEnd to try to prevent any video 17884 // frame overlap. maxBufferHole should be >> lastFrameDuration anyway. 17885 mp4SampleDuration = deltaToFrameEnd - lastFrameDuration; 17886 if (mp4SampleDuration < 0) { 17887 mp4SampleDuration = lastFrameDuration; 17888 } else { 17889 stretchedLastFrame = true; 17890 } 17891 this.logger.log("[mp4-remuxer]: It is approximately " + deltaToFrameEnd / 90 + " ms to the next segment; using duration " + mp4SampleDuration / 90 + " ms for the last video frame."); 17892 } else { 17893 mp4SampleDuration = lastFrameDuration; 17894 } 17895 } else { 17896 mp4SampleDuration = lastFrameDuration; 17897 } 17898 } 17899 var compositionTimeOffset = Math.round(VideoSample.pts - VideoSample.dts); 17900 minDtsDelta = Math.min(minDtsDelta, mp4SampleDuration); 17901 maxDtsDelta = Math.max(maxDtsDelta, mp4SampleDuration); 17902 minPtsDelta = Math.min(minPtsDelta, ptsDelta); 17903 maxPtsDelta = Math.max(maxPtsDelta, ptsDelta); 17904 outputSamples.push(new Mp4Sample(VideoSample.key, mp4SampleDuration, mp4SampleLength, compositionTimeOffset)); 17905 } 17906 if (outputSamples.length) { 17907 if (chromeVersion) { 17908 if (chromeVersion < 70) { 17909 // Chrome workaround, mark first sample as being a Random Access Point (keyframe) to avoid sourcebuffer append issue 17910 // https://code.google.com/p/chromium/issues/detail?id=229412 17911 var flags = outputSamples[0].flags; 17912 flags.dependsOn = 2; 17913 flags.isNonSync = 0; 17914 } 17915 } else if (safariWebkitVersion) { 17916 // Fix for "CNN special report, with CC" in test-streams (Safari browser only
17916) 17917 // Ignore DTS when frame durations are irregular. Safari MSE does not handle this leading to gaps. 17918 if (maxPtsDelta - minPtsDelta < maxDtsDelta - minDtsDelta && averageSampleDuration / maxDtsDelta < 0.025 && outputSamples[0].cts === 0) { 17919 this.logger.warn('Found irregular gaps in sample duration. Using PTS instead of DTS to determine MP4 sample duration.'); 17920 var dts = firstDTS; 17921 for (var _i4 = 0, len = outputSamples.length; _i4 < len; _i4++) { 17922 var nextDts = dts + outputSamples[_i4].duration; 17923 var _pts = dts + outputSamples[_i4].cts; 17924 if (_i4 < len - 1) { 17925 var nextPts = nextDts + outputSamples[_i4 + 1].cts; 17926 outputSamples[_i4].duration = nextPts - _pts; 17927 } else { 17928 outputSamples[_i4].duration = _i4 ? outputSamples[_i4 - 1].duration : averageSampleDuration; 17929 } 17930 outputSamples[_i4].cts = 0; 17931 dts = nextDts; 17932 } 17933 } 17934 } 17935 } 17936 // next AVC/HEVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale) 17937 mp4SampleDuration = stretchedLastFrame || !mp4SampleDuration ? averageSampleDuration : mp4SampleDuration; 17938 this.nextAvcDts = nextAvcDts = lastDTS + mp4SampleDuration; 17939 this.videoSampleDuration = mp4SampleDuration; 17940 this.isVideoContiguous = true; 17941 var moof = MP4.moof(track.sequenceNumber++, firstDTS, _extends({}, track, { 17942 samples: outputSamples 17943 })); 17944 var type = 'video'; 17945 var data = { 17946 data1: moof, 17947 data2: mdat, 17948 startPTS: minPTS / timeScale, 17949 endPTS: (maxPTS + mp4SampleDuration) / timeScale, 17950 startDTS: firstDTS / timeScale, 17951 endDTS: nextAvcDts / timeScale, 17952 type: type, 17953 hasAudio: false, 17954 hasVideo: true, 17955 nb: outputSamples.length, 17956 dropped: track.dropped 17957 }; 17958 track.samples = []; 17959 track.dropped = 0; 17960 return data; 17961 }; 17962 _proto.getSamplesPerFrame = function getSamplesPerFrame(track) { 17963 switch (track.segmentCodec) { 17964 case 'mp3': 17965 return MPEG_AUDIO_SAMPLE_PER_FRAME; 17966 case 'ac3': 17967 return AC3_SAMPLES_PER_FRAME; 17968 default: 17969 return AAC_SAMPLES_PER_FRAME; 17970 } 17971 }; 17972 _proto.remuxAudio = function remuxAudio(track, timeOffset, contiguous, accurateTimeOffset, videoTimeOffset) { 17973 var inputTimeScale = track.inputTimeScale; 17974 var mp4timeScale = track.samplerate ? track.samplerate : inputTimeScale; 17975 var scaleFactor = inputTimeScale / mp4timeScale; 17976 var mp4SampleDuration = this.getSamplesPerFrame(track); 17977 var inputSampleDuration = mp4SampleDuration * scaleFactor; 17978 var initPTS = this._initPTS; 17979 var rawMPEG = track.segmentCodec === 'mp3' && this.typeSupported.mpeg; 17980 var outputSamples = []; 17981 var alignedWithVideo = videoTimeOffset !== undefined; 17982 var inputSamples = track.samples; 17983 var offset = rawMPEG ? 0 : 8; 17984 var nextAudioPts = this.nextAudioPts || -1; 17985 17986 // window.audioSamples ? window.audioSamples.push(inputSamples.map(s => s.pts)) : (window.audioSamples = [inputSamples.map(s => s.pts)]); 17987 17988 // for audio samples, also consider consecutive fragments as being contiguous (even if a level switch occurs), 17989 // for sake of clarity: 17990 // consecutive fragments are frags with 17991 // - less than 100ms gaps between new time offset (if accurate) and next expected PTS OR 17992 // - less than 20 audio frames distance 17993 // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1) 17994 // this helps ensuring audio continuity 17995 // and this also avoids audio glitches/cut when switching quality, or reporting wrong duration on first audio frame 17996 var timeOffsetMpegTS = timeOffset * inputTimeScale; 17997 var initTime = initPTS.baseTime * inputTimeScale / initPTS.timescale; 17998 this.isAudioContiguous = contiguous = contiguous || inputSamples.length && nextAudioPts >
vendor: 6,304 bytes, lines 17998-18120
17998 0 && (accurateTimeOffset && Math.abs(timeOffsetMpegTS - nextAudioPts) < 9000 || Math.abs(normalizePts(inputSamples[0].pts - initTime, timeOffsetMpegTS) - nextAudioPts) < 20 * inputSampleDuration); 17999 18000 // compute normalized PTS 18001 inputSamples.forEach(function (sample) { 18002 sample.pts = normalizePts(sample.pts - initTime, timeOffsetMpegTS); 18003 }); 18004 if (!contiguous || nextAudioPts < 0) { 18005 // filter out sample with negative PTS that are not playable anyway 18006 // if we don't remove these negative samples, they will shift all audio samples forward. 18007 // leading to audio overlap between current / next fragment 18008 inputSamples = inputSamples.filter(function (sample) { 18009 return sample.pts >= 0; 18010 }); 18011 18012 // in case all samples have negative PTS, and have been filtered out, return now 18013 if (!inputSamples.length) { 18014 return; 18015 } 18016 if (videoTimeOffset === 0) { 18017 // Set the start to 0 to match video so that start gaps larger than inputSampleDuration are filled with silence 18018 nextAudioPts = 0; 18019 } else if (accurateTimeOffset && !alignedWithVideo) { 18020 // When not seeking, not live, and LevelDetails.PTSKnown, use fragment start as predicted next audio PTS 18021 nextAudioPts = Math.max(0, timeOffsetMpegTS); 18022 } else { 18023 // if frags are not contiguous and if we cant trust time offset, let's use first sample PTS as next audio PTS 18024 nextAudioPts = inputSamples[0].pts; 18025 } 18026 } 18027 18028 // If the audio track is missing samples, the frames seem to get "left-shifted" within the 18029 // resulting mp4 segment, causing sync issues and leaving gaps at the end of the audio segment. 18030 // In an effort to prevent this from happening, we inject frames here where there are gaps. 18031 // When possible, we inject a silent frame; when that's not possible, we duplicate the last 18032 // frame. 18033 18034 if (track.segmentCodec === 'aac') { 18035 var maxAudioFramesDrift = this.config.maxAudioFramesDrift; 18036 for (var i = 0, nextPts = nextAudioPts; i < inputSamples.length; i++) { 18037 // First, let's see how far off this frame is from where we expect it to be 18038 var sample = inputSamples[i]; 18039 var pts = sample.pts; 18040 var delta = pts - nextPts; 18041 var duration = Math.abs(1000 * delta / inputTimeScale); 18042 18043 // When remuxing with video, if we're overlapping by more than a duration, drop this sample to stay in sync 18044 if (delta <= -maxAudioFramesDrift * inputSampleDuration && alignedWithVideo) { 18045 if (i === 0) { 18046 this.logger.warn("Audio frame @ " + (pts / inputTimeScale).toFixed(3) + "s overlaps nextAudioPts by " + Math.round(1000 * delta / inputTimeScale) + " ms."); 18047 this.nextAudioPts = nextAudioPts = nextPts = pts; 18048 } 18049 } // eslint-disable-line brace-style 18050 18051 // Insert missing frames if: 18052 // 1: We're more than maxAudioFramesDrift frame away 18053 // 2: Not more than MAX_SILENT_FRAME_DURATION away 18054 // 3: currentTime (aka nextPtsNorm) is not 0 18055 // 4: remuxing with video (videoTimeOffset !== undefined) 18056 else if (delta >= maxAudioFramesDrift * inputSampleDuration && duration < MAX_SILENT_FRAME_DURATION && alignedWithVideo) { 18057 var missing = Math.round(delta / inputSampleDuration); 18058 // Adjust nextPts so that silent samples are aligned with media pts. This will prevent media samples from 18059 // later being shifted if nextPts is based on timeOffset and delta is not a multiple of inputSampleDuration. 18060 nextPts = pts - missing * inputSampleDuration; 18061 if (nextPts < 0) { 18062 missing--; 18063 nextPts += inputSampleDuration; 18064 } 18065 if (i === 0) { 18066 this.nextAudioPts = nextAudioPts = nextPts; 18067 } 18068 this.logger.warn("[mp4-remuxer]: Injecting " + missing + " audio frame @ " + (nextPts / inputTimeScale).toFixed(3) + "s due to " + Math.round(1000 * delta / inputTimeScale) + " ms gap."); 18069 for (var j = 0; j < missing; j++) { 18070 var newStamp = Math.max(nextPts, 0); 18071 var fillFrame = AAC.getSilentFrame(track.parsedCodec || track.manifestCodec || track.codec, track.channelCount); 18072 if (!fillFrame) { 18073 this.logger.log('[mp4-remuxer]: Unable to get silent frame for given audio codec; duplicating last frame instead.'); 18074 fillFrame = sample.unit.subarray(); 18075 } 18076 inputSamples.splice(i, 0, { 18077 unit: fillFrame, 18078 pts: newStamp 18079 }); 18080 nextPts += inputSampleDuration; 18081 i++; 18082 } 18083 } 18084 sample.pts = nextPts; 18085 nextPts += inputSampleDuration; 18086 } 18087 } 18088 var firstPTS = null; 18089 var lastPTS = null; 18090 var mdat; 18091 var mdatSize = 0; 18092 var sampleLength = inputSamples.length; 18093 while (sampleLength--) { 18094 mdatSize += inputSamples[sampleLength].unit.byteLength; 18095 } 18096 for (var _j2 = 0, _nbSamples = inputSamples.length; _j2 < _nbSamples; _j2++) { 18097 var audioSample = inputSamples[_j2]; 18098 var unit = audioSample.unit; 18099 var _pts2 = audioSample.pts; 18100 if (lastPTS !== null) { 18101 // If we have more than one sample, set the duration of the sample to the "real" duration; the PTS diff with 18102 // the previous sample 18103 var prevSample = outputSamples[_j2 - 1]; 18104 prevSample.duration = Math.round((_pts2 - lastPTS) / scaleFactor); 18105 } else { 18106 if (contiguous && track.segmentCodec === 'aac') { 18107 // set PTS/DTS to expected PTS/DTS 18108 _pts2 = nextAudioPts; 18109 } 18110 // remember first PTS of our audioSamples 18111 firstPTS = _pts2; 18112 if (mdatSize > 0) { 18113 /* concatenate the audio data and construct the mdat in place 18114 (need 8 more bytes to fill length and mdat type) */ 18115 mdatSize += offset; 18116 try { 18117 mdat = new Uint8Array(mdatSize); 18118 } catch (err) { 18119 this.observer.emit(Events.ERROR, Events.ERROR, { 18120 type: ErrorTypes.MUX_ERROR,
vendor: 5,140 bytes, lines 18121-18272
18121 details: ErrorDetails.REMUX_ALLOC_ERROR, 18122 fatal: false, 18123 error: err, 18124 bytes: mdatSize, 18125 reason: "fail allocating audio mdat " + mdatSize 18126 }); 18127 return; 18128 } 18129 if (!rawMPEG) { 18130 var view = new DataView(mdat.buffer); 18131 view.setUint32(0, mdatSize); 18132 mdat.set(MP4.types.mdat, 4); 18133 } 18134 } else { 18135 // no audio samples 18136 return; 18137 } 18138 } 18139 mdat.set(unit, offset); 18140 var unitLen = unit.byteLength; 18141 offset += unitLen; 18142 // Default the sample's duration to the computed mp4SampleDuration, which will either be 1024 for AAC or 1152 for MPEG 18143 // In the case that we have 1 sample, this will be the duration. If we have more than one sample, the duration 18144 // becomes the PTS diff with the previous sample 18145 outputSamples.push(new Mp4Sample(true, mp4SampleDuration, unitLen, 0)); 18146 lastPTS = _pts2; 18147 } 18148 18149 // We could end up with no audio samples if all input samples were overlapping with the previously remuxed ones 18150 var nbSamples = outputSamples.length; 18151 if (!nbSamples) { 18152 return; 18153 } 18154 18155 // The next audio sample PTS should be equal to last sample PTS + duration 18156 var lastSample = outputSamples[outputSamples.length - 1]; 18157 this.nextAudioPts = nextAudioPts = lastPTS + scaleFactor * lastSample.duration; 18158 18159 // Set the track samples from inputSamples to outputSamples before remuxing 18160 var moof = rawMPEG ? new Uint8Array(0) : MP4.moof(track.sequenceNumber++, firstPTS / scaleFactor, _extends({}, track, { 18161 samples: outputSamples 18162 })); 18163 18164 // Clear the track samples. This also clears the samples array in the demuxer, since the reference is shared 18165 track.samples = []; 18166 var start = firstPTS / inputTimeScale; 18167 var end = nextAudioPts / inputTimeScale; 18168 var type = 'audio'; 18169 var audioData = { 18170 data1: moof, 18171 data2: mdat, 18172 startPTS: start, 18173 endPTS: end, 18174 startDTS: start, 18175 endDTS: end, 18176 type: type, 18177 hasAudio: true, 18178 hasVideo: false, 18179 nb: nbSamples 18180 }; 18181 this.isAudioContiguous = true; 18182 return audioData; 18183 }; 18184 return MP4Remuxer; 18185 }(); 18186 function normalizePts(value, reference) { 18187 var offset; 18188 if (reference === null) { 18189 return value; 18190 } 18191 if (reference < value) { 18192 // - 2^33 18193 offset = -8589934592; 18194 } else { 18195 // + 2^33 18196 offset = 8589934592; 18197 } 18198 /* PTS is 33bit (from 0 to 2^33 -1) 18199 if diff between value and reference is bigger than half of the amplitude (2^32) then it means that 18200 PTS looping occured. fill the gap */ 18201 while (Math.abs(value - reference) > 4294967296) { 18202 value += offset; 18203 } 18204 return value; 18205 } 18206 function findKeyframeIndex(samples) { 18207 for (var i = 0; i < samples.length; i++) { 18208 if (samples[i].key) { 18209 return i; 18210 } 18211 } 18212 return -1; 18213 } 18214 function flushTextTrackMetadataCueSamples(track, timeOffset, initPTS, initDTS) { 18215 var length = track.samples.length; 18216 if (!length) { 18217 return; 18218 } 18219 var inputTimeScale = track.inputTimeScale; 18220 for (var index = 0; index < length; index++) { 18221 var sample = track.samples[index]; 18222 // setting id3 pts, dts to relative time 18223 // using this._initPTS and this._initDTS to calculate relative time 18224 sample.pts = normalizePts(sample.pts - initPTS.baseTime * inputTimeScale / initPTS.timescale, timeOffset * inputTimeScale) / inputTimeScale; 18225 sample.dts = normalizePts(sample.dts - initDTS.baseTime * inputTimeScale / initDTS.timescale, timeOffset * inputTimeScale) / inputTimeScale; 18226 } 18227 var samples = track.samples; 18228 track.samples = []; 18229 return { 18230 samples: samples 18231 }; 18232 } 18233 function flushTextTrackUserdataCueSamples(track, timeOffset, initPTS) { 18234 var length = track.samples.length; 18235 if (!length) { 18236 return; 18237 } 18238 var inputTimeScale = track.inputTimeScale; 18239 for (var index = 0; index < length; index++) { 18240 var sample = track.samples[index]; 18241 // setting text pts, dts to relative time 18242 // using this._initPTS and this._initDTS to calculate relative time 18243 sample.pts = normalizePts(sample.pts - initPTS.baseTime * inputTimeScale / initPTS.timescale, timeOffset * inputTimeScale) / inputTimeScale; 18244 } 18245 track.samples.sort(function (a, b) { 18246 return a.pts - b.pts; 18247 }); 18248 var samples = track.samples; 18249 track.samples = []; 18250 return { 18251 samples: samples 18252 }; 18253 } 18254 var Mp4Sample = function Mp4Sample(isKeyframe, duration, size, cts) { 18255 this.size = void 0; 18256 this.duration = void 0; 18257 this.cts = void 0; 18258 this.flags = void 0; 18259 this.duration = duration; 18260 this.size = size; 18261 this.cts = cts; 18262 this.flags = { 18263 isLeading: 0, 18264 isDependedOn: 0, 18265 hasRedundancy: 0, 18266 degradPrio: 0, 18267 dependsOn: isKeyframe ? 2 : 1, 18268 isNonSync: isKeyframe ? 0 : 1 18269 }; 18270 }; 18271 18272 var PassThroughRemuxer = /*#__PURE__*/function () {
18273 function PassThroughRemuxer(observer, config, typeSupported, logger) { 18274 this.logger = void 0; 18275 this.emitInitSegment = false; 18276 this.audioCodec = void 0; 18277 this.videoCodec = void 0; 18278 this.initData = void 0; 18279 this.initPTS = null; 18280 this.initTracks = void 0; 18281 this.lastEndTime = null; 18282 this.logger = logger; 18283 } 18284 var _proto = PassThroughRemuxer.prototype; 18285 _proto.destroy = function destroy() {}; 18286 _proto.resetTimeStamp = function resetTimeStamp(defaultInitPTS) { 18287 this.initPTS = defaultInitPTS; 18288 this.lastEndTime = null; 18289 }; 18290 _proto.resetNextTimestamp = function resetNextTimestamp() { 18291 this.lastEndTime = null; 18292 }; 18293 _proto.resetInitSegment = function resetInitSegment(initSegment, audioCodec, videoCodec, decryptdata) { 18294 this.audioCodec = audioCodec; 18295 this.videoCodec = videoCodec; 18296 this.generateInitSegment(patchEncyptionData(initSegment, decryptdata)); 18297 this.emitInitSegment = true; 18298 }; 18299 _proto.generateInitSegment = function generateInitSegment(initSegment) { 18300 var audioCodec = this.audioCodec, 18301 videoCodec = this.videoCodec; 18302 if (!(initSegment != null && initSegment.byteLength)) { 18303 this.initTracks = undefined; 18304 this.initData = undefined; 18305 return; 18306 } 18307 var initData = this.initData = parseInitSegment(initSegment); 18308 18309 // Get codec from initSegment or fallback to default 18310 if (initData.audio) { 18311 audioCodec = getParsedTrackCodec(initData.audio, ElementaryStreamTypes.AUDIO); 18312 } 18313 if (initData.video) { 18314 videoCodec = getParsedTrackCodec(initData.video, ElementaryStreamTypes.VIDEO); 18315 } 18316 var tracks = {}; 18317 if (initData.audio && initData.video) { 18318 tracks.audiovideo = { 18319 container: 'video/mp4', 18320 codec: audioCodec + ',' + videoCodec, 18321 initSegment: initSegment, 18322 id: 'main' 18323 }; 18324 } else if (initData.audio) { 18325 tracks.audio = { 18326 container: 'audio/mp4', 18327 codec: audioCodec, 18328 initSegment: initSegment, 18329 id: 'audio' 18330 }; 18331 } else if (initData.video) { 18332 tracks.video = { 18333 container: 'video/mp4', 18334 codec: videoCodec, 18335 initSegment: initSegment, 18336 id: 'main' 18337 }; 18338 } else { 18339 this.logger.warn('[passthrough-remuxer.ts]: initSegment does not contain moov or trak boxes.'); 18340 } 18341 this.initTracks = tracks; 18342 }; 18343 _proto.remux = function remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset) { 18344 var _initData, _initData2; 18345 var initPTS = this.initPTS, 18346 lastEndTime = this.lastEndTime; 18347 var result = { 18348 audio: undefined, 18349 video: undefined, 18350 text: textTrack, 18351 id3: id3Track, 18352 initSegment: undefined 18353 }; 18354 18355 // If we haven't yet set a lastEndDTS, or it was reset, set it to the provided timeOffset. We want to use the 18356 // lastEndDTS over timeOffset whenever possible; during progressive playback, the media source will not update 18357 // the media duration (which is what timeOffset is provided as) before we need to process the next chunk. 18358 if (!isFiniteNumber(lastEndTime)) { 18359 lastEndTime = this.lastEndTime = timeOffset || 0; 18360 } 18361 18362 // The binary segment data is added to the videoTrack in the mp4demuxer. We don't check to see if the data is only 18363 // audio or video (or both); adding it to video was an arbitrary choice. 18364 var data = videoTrack.samples; 18365 if (!(data != null && data.length)) { 18366 return result; 18367 } 18368 var initSegment = { 18369 initPTS: undefined, 18370 timescale: 1 18371 }; 18372 var initData = this.initData; 18373 if (!((_initData = initData) != null && _initData.length)) { 18374 this.generateInitSegment(data); 18375 initData = this.initData; 18376 } 18377 if (!((_initData2 = initData) != null && _initData2.length)) { 18378 // We can't remux if the initSegment could not be generated 18379 this.logger.warn('[passthrough-remuxer.ts]: Failed to generate initSegment.'); 18380 return result; 18381 } 18382 if (this.emitInitSegment) { 18383 initSegment.tracks = this.initTracks; 18384 this.emitInitSegment = false;
18385 } 18386 var duration = getDuration(data, initData); 18387 var startDTS = getStartDTS(initData, data); 18388 var decodeTime = startDTS === null ? timeOffset : startDTS; 18389 if (isInvalidInitPts(initPTS, decodeTime, timeOffset, duration) || initSegment.timescale !== initPTS.timescale && accurateTimeOffset) { 18390 initSegment.initPTS = decodeTime - timeOffset; 18391 if (initPTS && initPTS.timescale === 1) { 18392 this.logger.warn("Adjusting initPTS @" + timeOffset + " from " + initPTS.baseTime / initPTS.timescale + " to " + initSegment.initPTS); 18393 } 18394 this.initPTS = initPTS = { 18395 baseTime: initSegment.initPTS, 18396 timescale: 1 18397 }; 18398 } 18399 var startTime = audioTrack ? decodeTime - initPTS.baseTime / initPTS.timescale : lastEndTime; 18400 var endTime = startTime + duration; 18401 offsetStartDTS(initData, data, initPTS.baseTime / initPTS.timescale); 18402 if (duration > 0) { 18403 this.lastEndTime = endTime; 18404 } else { 18405 this.logger.warn('Duration parsed from mp4 should be greater than zero'); 18406 this.resetNextTimestamp(); 18407 } 18408 var hasAudio = !!initData.audio; 18409 var hasVideo = !!initData.video; 18410 var type = ''; 18411 if (hasAudio) { 18412 type += 'audio'; 18413 } 18414 if (hasVideo) { 18415 type += 'video'; 18416 } 18417 var track = { 18418 data1: data, 18419 startPTS: startTime, 18420 startDTS: startTime, 18421 endPTS: endTime, 18422 endDTS: endTime, 18423 type: type, 18424 hasAudio: hasAudio, 18425 hasVideo: hasVideo, 18426 nb: 1, 18427 dropped: 0 18428 }; 18429 result.audio = track.type === 'audio' ? track : undefined; 18430 result.video = track.type !== 'audio' ? track : undefined; 18431 result.initSegment = initSegment; 18432 result.id3 = flushTextTrackMetadataCueSamples(id3Track, timeOffset, initPTS, initPTS); 18433 if (textTrack.samples.length) { 18434 result.text = flushTextTrackUserdataCueSamples(textTrack, timeOffset, initPTS); 18435 } 18436 return result; 18437 }; 18438 return PassThroughRemuxer; 18439 }(); 18440 function isInvalidInitPts(initPTS, startDTS, timeOffset, duration) { 18441 if (initPTS === null) { 18442 return true; 18443 }
18444 // InitPTS is invalid when distance from program would be more than segment duration or a minimum of one second 18445 var minDuration = Math.max(duration, 1); 18446 var startTime = startDTS - initPTS.baseTime / initPTS.timescale; 18447 return Math.abs(startTime - timeOffset) > minDuration; 18448 } 18449 function getParsedTrackCodec(track, type) { 18450 var parsedCodec = track == null ? void 0 : track.codec; 18451 if (parsedCodec && parsedCodec.length > 4) { 18452 return parsedCodec; 18453 } 18454 if (type === ElementaryStreamTypes.AUDIO) { 18455 if (parsedCodec === 'ec-3' || parsedCodec === 'ac-3' || parsedCodec === 'alac') { 18456 return parsedCodec; 18457 } 18458 if (parsedCodec === 'fLaC' || parsedCodec === 'Opus') { 18459 // Opting not to get `preferManagedMediaSource` from player config for isSupported() check for simplicity 18460 var preferManagedMediaSource = false; 18461 return getCodecCompatibleName(parsedCodec, preferManagedMediaSource); 18462 } 18463 var result = 'mp4a.40.5'; 18464 this.logger.info("Parsed audio codec \"" + parsedCodec + "\" or audio object type not handled. Using \"" + result + "\""); 18465 return result; 18466 } 18467 // Provide defaults based on codec type 18468 // This allows for some playback of some fmp4 playlists without CODECS defined in manifest 18469 this.logger.warn("Unhandled video codec \"" + parsedCodec + "\""); 18470 if (parsedCodec === 'hvc1' || parsedCodec === 'hev1') { 18471 return 'hvc1.1.6.L120.90'; 18472 } 18473 if (parsedCodec === 'av01') { 18474 return 'av01.0.04M.08'; 18475 } 18476 return 'avc1.42e01e'; 18477 } 18478 18479 var now; 18480 // performance.now() not available on WebWorker, at least on Safari Desktop 18481 try { 18482 now = self.performance.now.bind(self.performance); 18483 } catch (err) { 18484 now = Date.now; 18485 } 18486 var muxConfig = [{ 18487 demux: MP4Demuxer, 18488 remux: PassThroughRemuxer 18489 }, { 18490 demux: TSDemuxer, 18491 remux: MP4Remuxer 18492 }, { 18493 demux: AACDemuxer, 18494 remux: MP4Remuxer 18495 }, { 18496 demux: MP3Demuxer, 18497 remux: MP4Remuxer 18498 }]; 18499 var Transmuxer = /*#__PURE__*/function () { 18500 function Transmuxer(observer, typeSupported, config, vendor, id, logger) { 18501 this.asyncResult = false;
vendor: 13,616 bytes, lines 18502-18826
18502 this.logger = void 0; 18503 this.observer = void 0; 18504 this.typeSupported = void 0; 18505 this.config = void 0; 18506 this.id = void 0; 18507 this.demuxer = void 0; 18508 this.remuxer = void 0; 18509 this.decrypter = void 0; 18510 this.probe = void 0; 18511 this.decryptionPromise = null; 18512 this.transmuxConfig = void 0; 18513 this.currentTransmuxState = void 0; 18514 this.observer = observer; 18515 this.typeSupported = typeSupported; 18516 this.config = config; 18517 this.id = id; 18518 this.logger = logger; 18519 } 18520 var _proto = Transmuxer.prototype; 18521 _proto.configure = function configure(transmuxConfig) { 18522 this.transmuxConfig = transmuxConfig; 18523 if (this.decrypter) { 18524 this.decrypter.reset(); 18525 } 18526 }; 18527 _proto.push = function push(data, decryptdata, chunkMeta, state) { 18528 var _this = this; 18529 var stats = chunkMeta.transmuxing; 18530 stats.executeStart = now(); 18531 var uintData = new Uint8Array(data); 18532 var currentTransmuxState = this.currentTransmuxState, 18533 transmuxConfig = this.transmuxConfig; 18534 if (state) { 18535 this.currentTransmuxState = state; 18536 } 18537 var _ref = state || currentTransmuxState, 18538 contiguous = _ref.contiguous, 18539 discontinuity = _ref.discontinuity, 18540 trackSwitch = _ref.trackSwitch, 18541 accurateTimeOffset = _ref.accurateTimeOffset, 18542 timeOffset = _ref.timeOffset, 18543 initSegmentChange = _ref.initSegmentChange; 18544 var audioCodec = transmuxConfig.audioCodec, 18545 videoCodec = transmuxConfig.videoCodec, 18546 defaultInitPts = transmuxConfig.defaultInitPts, 18547 duration = transmuxConfig.duration, 18548 initSegmentData = transmuxConfig.initSegmentData; 18549 var keyData = getEncryptionType(uintData, decryptdata); 18550 if (keyData && isFullSegmentEncryption(keyData.method)) { 18551 var decrypter = this.getDecrypter(); 18552 var aesMode = getAesModeFromFullSegmentMethod(keyData.method); 18553 18554 // Software decryption is synchronous; webCrypto is not 18555 if (decrypter.isSync()) { 18556 // Software decryption is progressive. Progressive decryption may not return a result on each call. Any cached 18557 // data is handled in the flush() call 18558 var decryptedData = decrypter.softwareDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode); 18559 // For Low-Latency HLS Parts, decrypt in place, since part parsing is expected on push progress 18560 var loadingParts = chunkMeta.part > -1; 18561 if (loadingParts) { 18562 decryptedData = decrypter.flush(); 18563 } 18564 if (!decryptedData) { 18565 stats.executeEnd = now(); 18566 return emptyResult(chunkMeta); 18567 } 18568 uintData = new Uint8Array(decryptedData); 18569 } else { 18570 this.asyncResult = true; 18571 this.decryptionPromise = decrypter.webCryptoDecrypt(uintData, keyData.key.buffer, keyData.iv.buffer, aesMode).then(function (decryptedData) { 18572 // Calling push here is important; if flush() is called while this is still resolving, this ensures that 18573 // the decrypted data has been transmuxed 18574 var result = _this.push(decryptedData, null, chunkMeta); 18575 _this.decryptionPromise = null; 18576 return result; 18577 }); 18578 return this.decryptionPromise; 18579 } 18580 } 18581 var resetMuxers = this.needsProbing(discontinuity, trackSwitch); 18582 if (resetMuxers) { 18583 var error = this.configureTransmuxer(uintData); 18584 if (error) { 18585 this.logger.warn("[transmuxer] " + error.message); 18586 this.observer.emit(Events.ERROR, Events.ERROR, { 18587 type: ErrorTypes.MEDIA_ERROR, 18588 details: ErrorDetails.FRAG_PARSING_ERROR, 18589 fatal: false, 18590 error: error, 18591 reason: error.message 18592 }); 18593 stats.executeEnd = now(); 18594 return emptyResult(chunkMeta); 18595 } 18596 } 18597 if (discontinuity || trackSwitch || initSegmentChange || resetMuxers) { 18598 this.resetInitSegment(initSegmentData, audioCodec, videoCodec, duration, decryptdata); 18599 } 18600 if (discontinuity || initSegmentChange || resetMuxers) { 18601 this.resetInitialTimestamp(defaultInitPts); 18602 } 18603 if (!contiguous) { 18604 this.resetContiguity(); 18605 } 18606 var result = this.transmux(uintData, keyData, timeOffset, accurateTimeOffset, chunkMeta); 18607 this.asyncResult = isPromise(result); 18608 var currentState = this.currentTransmuxState; 18609 currentState.contiguous = true; 18610 currentState.discontinuity = false; 18611 currentState.trackSwitch = false; 18612 stats.executeEnd = now(); 18613 return result; 18614 } 18615 18616 // Due to data caching, flush calls can produce more than one TransmuxerResult (hence the Array type) 18617 ; 18618 _proto.flush = function flush(chunkMeta) { 18619 var _this2 = this; 18620 var stats = chunkMeta.transmuxing; 18621 stats.executeStart = now(); 18622 var decrypter = this.decrypter, 18623 currentTransmuxState = this.currentTransmuxState, 18624 decryptionPromise = this.decryptionPromise; 18625 if (decryptionPromise) { 18626 this.asyncResult = true; 18627 // Upon resolution, the decryption promise calls push() and returns its TransmuxerResult up the stack. Therefore 18628 // only flushing is required for async decryption 18629 return decryptionPromise.then(function () { 18630 return _this2.flush(chunkMeta); 18631 }); 18632 } 18633 var transmuxResults = []; 18634 var timeOffset = currentTransmuxState.timeOffset; 18635 if (decrypter) { 18636 // The decrypter may have data cached, which needs to be demuxed. In this case we'll have two TransmuxResults 18637 // This happens in the case that we receive only 1 push call for a segment (either for non-progressive downloads, 18638 // or for progressive downloads with small segments) 18639 var decryptedData = decrypter.flush(); 18640 if (decryptedData) { 18641 // Push always returns a TransmuxerResult if decryptdata is null 18642 transmuxResults.push(this.push(decryptedData, null, chunkMeta)); 18643 } 18644 } 18645 var demuxer = this.demuxer, 18646 remuxer = this.remuxer; 18647 if (!demuxer || !remuxer) { 18648 // If probing failed, then Hls.js has been given content its not able to handle 18649 stats.executeEnd = now(); 18650 var emptyResults = [emptyResult(chunkMeta)]; 18651 if (this.asyncResult) { 18652 return Promise.resolve(emptyResults); 18653 } 18654 return emptyResults; 18655 } 18656 var demuxResultOrPromise = demuxer.flush(timeOffset); 18657 if (isPromise(demuxResultOrPromise)) { 18658 this.asyncResult = true; 18659 // Decrypt final SAMPLE-AES samples 18660 return demuxResultOrPromise.then(function (demuxResult) { 18661 _this2.flushRemux(transmuxResults, demuxResult, chunkMeta); 18662 return transmuxResults; 18663 }); 18664 } 18665 this.flushRemux(transmuxResults, demuxResultOrPromise, chunkMeta); 18666 if (this.asyncResult) { 18667 return Promise.resolve(transmuxResults); 18668 } 18669 return transmuxResults; 18670 }; 18671 _proto.flushRemux = function flushRemux(transmuxResults, demuxResult, chunkMeta) { 18672 var audioTrack = demuxResult.audioTrack, 18673 videoTrack = demuxResult.videoTrack, 18674 id3Track = demuxResult.id3Track, 18675 textTrack = demuxResult.textTrack; 18676 var _this$currentTransmux = this.currentTransmuxState, 18677 accurateTimeOffset = _this$currentTransmux.accurateTimeOffset, 18678 timeOffset = _this$currentTransmux.timeOffset; 18679 this.logger.log("[transmuxer.ts]: Flushed " + this.id + " sn: " + chunkMeta.sn + (chunkMeta.part > -1 ? ' p: ' + chunkMeta.part : '') + " of " + (this.id === PlaylistLevelType.MAIN ? 'level' : 'track') + " " + chunkMeta.level); 18680 var remuxResult = this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset, true, this.id); 18681 transmuxResults.push({ 18682 remuxResult: remuxResult, 18683 chunkMeta: chunkMeta 18684 }); 18685 chunkMeta.transmuxing.executeEnd = now(); 18686 }; 18687 _proto.resetInitialTimestamp = function resetInitialTimestamp(defaultInitPts) { 18688 var demuxer = this.demuxer, 18689 remuxer = this.remuxer; 18690 if (!demuxer || !remuxer) { 18691 return; 18692 } 18693 demuxer.resetTimeStamp(defaultInitPts); 18694 remuxer.resetTimeStamp(defaultInitPts); 18695 }; 18696 _proto.resetContiguity = function resetContiguity() { 18697 var demuxer = this.demuxer, 18698 remuxer = this.remuxer; 18699 if (!demuxer || !remuxer) { 18700 return; 18701 } 18702 demuxer.resetContiguity(); 18703 remuxer.resetNextTimestamp(); 18704 }; 18705 _proto.resetInitSegment = function resetInitSegment(initSegmentData, audioCodec, videoCodec, trackDuration, decryptdata) { 18706 var demuxer = this.demuxer, 18707 remuxer = this.remuxer; 18708 if (!demuxer || !remuxer) { 18709 return; 18710 } 18711 demuxer.resetInitSegment(initSegmentData, audioCodec, videoCodec, trackDuration); 18712 remuxer.resetInitSegment(initSegmentData, audioCodec, videoCodec, decryptdata); 18713 }; 18714 _proto.destroy = function destroy() { 18715 if (this.demuxer) { 18716 this.demuxer.destroy(); 18717 this.demuxer = undefined; 18718 } 18719 if (this.remuxer) { 18720 this.remuxer.destroy(); 18721 this.remuxer = undefined; 18722 } 18723 }; 18724 _proto.transmux = function transmux(data, keyData, timeOffset, accurateTimeOffset, chunkMeta) { 18725 var result; 18726 if (keyData && keyData.method === 'SAMPLE-AES') { 18727 result = this.transmuxSampleAes(data, keyData, timeOffset, accurateTimeOffset, chunkMeta); 18728 } else { 18729 result = this.transmuxUnencrypted(data, timeOffset, accurateTimeOffset, chunkMeta); 18730 } 18731 return result; 18732 }; 18733 _proto.transmuxUnencrypted = function transmuxUnencrypted(data, timeOffset, accurateTimeOffset, chunkMeta) { 18734 var _demux = this.demuxer.demux(data, timeOffset, false, !this.config.progressive), 18735 audioTrack = _demux.audioTrack, 18736 videoTrack = _demux.videoTrack, 18737 id3Track = _demux.id3Track, 18738 textTrack = _demux.textTrack; 18739 var remuxResult = this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, accurateTimeOffset, false, this.id); 18740 return { 18741 remuxResult: remuxResult, 18742 chunkMeta: chunkMeta 18743 }; 18744 }; 18745 _proto.transmuxSampleAes = function transmuxSampleAes(data, decryptData, timeOffset, accurateTimeOffset, chunkMeta) { 18746 var _this3 = this; 18747 return this.demuxer.demuxSampleAes(data, decryptData, timeOffset).then(function (demuxResult) { 18748 var remuxResult = _this3.remuxer.remux(demuxResult.audioTrack, demuxResult.videoTrack, demuxResult.id3Track, demuxResult.textTrack, timeOffset, accurateTimeOffset, false, _this3.id); 18749 return { 18750 remuxResult: remuxResult, 18751 chunkMeta: chunkMeta 18752 }; 18753 }); 18754 }; 18755 _proto.configureTransmuxer = function configureTransmuxer(data) { 18756 var config = this.config, 18757 observer = this.observer, 18758 typeSupported = this.typeSupported; 18759 // probe for content type 18760 var mux; 18761 for (var i = 0, len = muxConfig.length; i < len; i++) { 18762 var _muxConfig$i$demux; 18763 if ((_muxConfig$i$demux = muxConfig[i].demux) != null && _muxConfig$i$demux.probe(data, this.logger)) { 18764 mux = muxConfig[i]; 18765 break; 18766 } 18767 } 18768 if (!mux) { 18769 return new Error('Failed to find demuxer by probing fragment data'); 18770 } 18771 // so let's check that current remuxer and demuxer are still valid 18772 var demuxer = this.demuxer; 18773 var remuxer = this.remuxer; 18774 var Remuxer = mux.remux; 18775 var Demuxer = mux.demux; 18776 if (!remuxer || !(remuxer instanceof Remuxer)) { 18777 this.remuxer = new Remuxer(observer, config, typeSupported, this.logger); 18778 } 18779 if (!demuxer || !(demuxer instanceof Demuxer)) { 18780 this.demuxer = new Demuxer(observer, config, typeSupported, this.logger); 18781 this.probe = Demuxer.probe; 18782 } 18783 }; 18784 _proto.needsProbing = function needsProbing(discontinuity, trackSwitch) { 18785 // in case of continuity change, or track switch 18786 // we might switch from content type (AAC container to TS container, or TS to fmp4 for example) 18787 return !this.demuxer || !this.remuxer || discontinuity || trackSwitch; 18788 }; 18789 _proto.getDecrypter = function getDecrypter() { 18790 var decrypter = this.decrypter; 18791 if (!decrypter) { 18792 decrypter = this.decrypter = new Decrypter(this.config); 18793 } 18794 return decrypter; 18795 }; 18796 return Transmuxer; 18797 }(); 18798 function getEncryptionType(data, decryptData) { 18799 var encryptionType = null; 18800 if (data.byteLength > 0 && (decryptData == null ? void 0 : decryptData.key) != null && decryptData.iv !== null && decryptData.method != null) { 18801 encryptionType = decryptData; 18802 } 18803 return encryptionType; 18804 } 18805 var emptyResult = function emptyResult(chunkMeta) { 18806 return { 18807 remuxResult: {}, 18808 chunkMeta: chunkMeta 18809 }; 18810 }; 18811 function isPromise(p) { 18812 return 'then' in p && p.then instanceof Function; 18813 } 18814 var TransmuxConfig = function TransmuxConfig(audioCodec, videoCodec, initSegmentData, duration, defaultInitPts) { 18815 this.audioCodec = void 0; 18816 this.videoCodec = void 0; 18817 this.initSegmentData = void 0; 18818 this.duration = void 0; 18819 this.defaultInitPts = void 0; 18820 this.audioCodec = audioCodec; 18821 this.videoCodec = videoCodec; 18822 this.initSegmentData = initSegmentData; 18823 this.duration = duration; 18824 this.defaultInitPts = defaultInitPts || null; 18825 }; 18826 var TransmuxState = function TransmuxState(discontinuity, contiguous, accurateTimeOff
vendor: 5,517 bytes, lines 18826-19011
18826set, trackSwitch, timeOffset, initSegmentChange) { 18827 this.discontinuity = void 0; 18828 this.contiguous = void 0; 18829 this.accurateTimeOffset = void 0; 18830 this.trackSwitch = void 0; 18831 this.timeOffset = void 0; 18832 this.initSegmentChange = void 0; 18833 this.discontinuity = discontinuity; 18834 this.contiguous = contiguous; 18835 this.accurateTimeOffset = accurateTimeOffset; 18836 this.trackSwitch = trackSwitch; 18837 this.timeOffset = timeOffset; 18838 this.initSegmentChange = initSegmentChange; 18839 }; 18840 18841 var eventemitter3 = {exports: {}}; 18842 18843 (function (module) { 18844 18845 var has = Object.prototype.hasOwnProperty 18846 , prefix = '~'; 18847 18848 /** 18849 * Constructor to create a storage for our `EE` objects. 18850 * An `Events` instance is a plain object whose properties are event names. 18851 * 18852 * @constructor 18853 * @private 18854 */ 18855 function Events() {} 18856 18857 // 18858 // We try to not inherit from `Object.prototype`. In some engines creating an 18859 // instance in this way is faster than calling `Object.create(null)` directly. 18860 // If `Object.create(null)` is not supported we prefix the event names with a 18861 // character to make sure that the built-in object properties are not 18862 // overridden or used as an attack vector. 18863 // 18864 if (Object.create) { 18865 Events.prototype = Object.create(null); 18866 18867 // 18868 // This hack is needed because the `__proto__` property is still inherited in 18869 // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. 18870 // 18871 if (!new Events().__proto__) prefix = false; 18872 } 18873 18874 /** 18875 * Representation of a single event listener. 18876 * 18877 * @param {Function} fn The listener function. 18878 * @param {*} context The context to invoke the listener with. 18879 * @param {Boolean} [once=false] Specify if the listener is a one-time listener. 18880 * @constructor 18881 * @private 18882 */ 18883 function EE(fn, context, once) { 18884 this.fn = fn; 18885 this.context = context; 18886 this.once = once || false; 18887 } 18888 18889 /** 18890 * Add a listener for a given event. 18891 * 18892 * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. 18893 * @param {(String|Symbol)} event The event name. 18894 * @param {Function} fn The listener function. 18895 * @param {*} context The context to invoke the listener with. 18896 * @param {Boolean} once Specify if the listener is a one-time listener. 18897 * @returns {EventEmitter} 18898 * @private 18899 */ 18900 function addListener(emitter, event, fn, context, once) { 18901 if (typeof fn !== 'function') { 18902 throw new TypeError('The listener must be a function'); 18903 } 18904 18905 var listener = new EE(fn, context || emitter, once) 18906 , evt = prefix ? prefix + event : event; 18907 18908 if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; 18909 else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); 18910 else emitter._events[evt] = [emitter._events[evt], listener]; 18911 18912 return emitter; 18913 } 18914 18915 /** 18916 * Clear event by name. 18917 * 18918 * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. 18919 * @param {(String|Symbol)} evt The Event name. 18920 * @private 18921 */ 18922 function clearEvent(emitter, evt) { 18923 if (--emitter._eventsCount === 0) emitter._events = new Events(); 18924 else delete emitter._events[evt]; 18925 } 18926 18927 /** 18928 * Minimal `EventEmitter` interface that is molded against the Node.js 18929 * `EventEmitter` interface. 18930 * 18931 * @constructor 18932 * @public 18933 */ 18934 function EventEmitter() { 18935 this._events = new Events(); 18936 this._eventsCount = 0; 18937 } 18938 18939 /** 18940 * Return an array listing the events for which the emitter has registered 18941 * listeners. 18942 * 18943 * @returns {Array} 18944 * @public 18945 */ 18946 EventEmitter.prototype.eventNames = function eventNames() { 18947 var names = [] 18948 , events 18949 , name; 18950 18951 if (this._eventsCount === 0) return names; 18952 18953 for (name in (events = this._events)) { 18954 if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); 18955 } 18956 18957 if (Object.getOwnPropertySymbols) { 18958 return names.concat(Object.getOwnPropertySymbols(events)); 18959 } 18960 18961 return names; 18962 }; 18963 18964 /** 18965 * Return the listeners registered for a given event. 18966 * 18967 * @param {(String|Symbol)} event The event name. 18968 * @returns {Array} The registered listeners. 18969 * @public 18970 */ 18971 EventEmitter.prototype.listeners = function listeners(event) { 18972 var evt = prefix ? prefix + event : event 18973 , handlers = this._events[evt]; 18974 18975 if (!handlers) return []; 18976 if (handlers.fn) return [handlers.fn]; 18977 18978 for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { 18979 ee[i] = handlers[i].fn; 18980 } 18981 18982 return ee; 18983 }; 18984 18985 /** 18986 * Return the number of listeners listening to a given event. 18987 * 18988 * @param {(String|Symbol)} event The event name. 18989 * @returns {Number} The number of listeners. 18990 * @public 18991 */ 18992 EventEmitter.prototype.listenerCount = function listenerCount(event) { 18993 var evt = prefix ? prefix + event : event 18994 , listeners = this._events[evt]; 18995 18996 if (!listeners) return 0; 18997 if (listeners.fn) return 1; 18998 return listeners.length; 18999 }; 19000 19001 /** 19002 * Calls each of the listeners registered for a given event. 19003 * 19004 * @param {(String|Symbol)} event The event name. 19005 * @returns {Boolean} `true` if the event had listeners, else `false`. 19006 * @public 19007 */ 19008 EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { 19009 var evt = prefix ? prefix + event : event; 19010 19011 if (!this._events[evt]) return false;
vendor: 19,133 bytes, lines 19012-19592
19012 19013 var listeners = this._events[evt] 19014 , len = arguments.length 19015 , args 19016 , i; 19017 19018 if (listeners.fn) { 19019 if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); 19020 19021 switch (len) { 19022 case 1: return listeners.fn.call(listeners.context), true; 19023 case 2: return listeners.fn.call(listeners.context, a1), true; 19024 case 3: return listeners.fn.call(listeners.context, a1, a2), true; 19025 case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; 19026 case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; 19027 case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; 19028 } 19029 19030 for (i = 1, args = new Array(len -1); i < len; i++) { 19031 args[i - 1] = arguments[i]; 19032 } 19033 19034 listeners.fn.apply(listeners.context, args); 19035 } else { 19036 var length = listeners.length 19037 , j; 19038 19039 for (i = 0; i < length; i++) { 19040 if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); 19041 19042 switch (len) { 19043 case 1: listeners[i].fn.call(listeners[i].context); break; 19044 case 2: listeners[i].fn.call(listeners[i].context, a1); break; 19045 case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; 19046 case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; 19047 default: 19048 if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { 19049 args[j - 1] = arguments[j]; 19050 } 19051 19052 listeners[i].fn.apply(listeners[i].context, args); 19053 } 19054 } 19055 } 19056 19057 return true; 19058 }; 19059 19060 /** 19061 * Add a listener for a given event. 19062 * 19063 * @param {(String|Symbol)} event The event name. 19064 * @param {Function} fn The listener function. 19065 * @param {*} [context=this] The context to invoke the listener with. 19066 * @returns {EventEmitter} `this`. 19067 * @public 19068 */ 19069 EventEmitter.prototype.on = function on(event, fn, context) { 19070 return addListener(this, event, fn, context, false); 19071 }; 19072 19073 /** 19074 * Add a one-time listener for a given event. 19075 * 19076 * @param {(String|Symbol)} event The event name. 19077 * @param {Function} fn The listener function. 19078 * @param {*} [context=this] The context to invoke the listener with. 19079 * @returns {EventEmitter} `this`. 19080 * @public 19081 */ 19082 EventEmitter.prototype.once = function once(event, fn, context) { 19083 return addListener(this, event, fn, context, true); 19084 }; 19085 19086 /** 19087 * Remove the listeners of a given event. 19088 * 19089 * @param {(String|Symbol)} event The event name. 19090 * @param {Function} fn Only remove the listeners that match this function. 19091 * @param {*} context Only remove the listeners that have this context. 19092 * @param {Boolean} once Only remove one-time listeners. 19093 * @returns {EventEmitter} `this`. 19094 * @public 19095 */ 19096 EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { 19097 var evt = prefix ? prefix + event : event; 19098 19099 if (!this._events[evt]) return this; 19100 if (!fn) { 19101 clearEvent(this, evt); 19102 return this; 19103 } 19104 19105 var listeners = this._events[evt]; 19106 19107 if (listeners.fn) { 19108 if ( 19109 listeners.fn === fn && 19110 (!once || listeners.once) && 19111 (!context || listeners.context === context) 19112 ) { 19113 clearEvent(this, evt); 19114 } 19115 } else { 19116 for (var i = 0, events = [], length = listeners.length; i < length; i++) { 19117 if ( 19118 listeners[i].fn !== fn || 19119 (once && !listeners[i].once) || 19120 (context && listeners[i].context !== context) 19121 ) { 19122 events.push(listeners[i]); 19123 } 19124 } 19125 19126 // 19127 // Reset the array, or remove it completely if we have no more listeners. 19128 // 19129 if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; 19130 else clearEvent(this, evt); 19131 } 19132 19133 return this; 19134 }; 19135 19136 /** 19137 * Remove all listeners, or those of the specified event. 19138 * 19139 * @param {(String|Symbol)} [event] The event name. 19140 * @returns {EventEmitter} `this`. 19141 * @public 19142 */ 19143 EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { 19144 var evt; 19145 19146 if (event) { 19147 evt = prefix ? prefix + event : event; 19148 if (this._events[evt]) clearEvent(this, evt); 19149 } else { 19150 this._events = new Events(); 19151 this._eventsCount = 0; 19152 } 19153 19154 return this; 19155 }; 19156 19157 // 19158 // Alias methods names because people roll like that. 19159 // 19160 EventEmitter.prototype.off = EventEmitter.prototype.removeListener; 19161 EventEmitter.prototype.addListener = EventEmitter.prototype.on; 19162 19163 // 19164 // Expose the prefix. 19165 // 19166 EventEmitter.prefixed = prefix; 19167 19168 // 19169 // Allow `EventEmitter` to be imported as module namespace. 19170 // 19171 EventEmitter.EventEmitter = EventEmitter; 19172 19173 // 19174 // Expose the module. 19175 // 19176 { 19177 module.exports = EventEmitter; 19178 } 19179 } (eventemitter3)); 19180 19181 var eventemitter3Exports = eventemitter3.exports; 19182 var EventEmitter = /*@__PURE__*/getDefaultExportFromCjs(eventemitter3Exports); 19183 19184 var transmuxers = []; 19185 if (typeof __IN_WORKER__ !== 'undefined' && __IN_WORKER__) { 19186 startWorker(); 19187 } 19188 function startWorker() { 19189 self.addEventListener('message', function (ev) { 19190 var data = ev.data; 19191 var instanceNo = data.instanceNo; 19192 if (instanceNo === undefined) { 19193 return; 19194 } 19195 var transmuxer = transmuxers[instanceNo]; 19196 if (data.cmd === 'reset') { 19197 delete transmuxers[data.resetNo]; 19198 if (transmuxer) { 19199 transmuxer.destroy(); 19200 } 19201 data.cmd = 'init'; 19202 } 19203 if (data.cmd === 'init') { 19204 var config = JSON.parse(data.config); 19205 var observer = new EventEmitter(); 19206 observer.on(Events.FRAG_DECRYPTED, forwardMessage); 19207 observer.on(Events.ERROR, forwardMessage); 19208 var logger = enableLogs(config.debug, data.id); 19209 forwardWorkerLogs(logger, instanceNo); 19210 transmuxers[instanceNo] = new Transmuxer(observer, data.typeSupported, config, '', data.id, logger); 19211 forwardMessage('init', null, instanceNo); 19212 return; 19213 } 19214 if (!transmuxer) { 19215 return; 19216 } 19217 switch (data.cmd) { 19218 case 'configure': 19219 { 19220 transmuxer.configure(data.config); 19221 break; 19222 } 19223 case 'demux': 19224 { 19225 var transmuxResult = transmuxer.push(data.data, data.decryptdata, data.chunkMeta, data.state); 19226 if (isPromise(transmuxResult)) { 19227 transmuxResult.then(function (data) { 19228 emitTransmuxComplete(self, data, instanceNo); 19229 }).catch(function (error) { 19230 forwardMessage(Events.ERROR, { 19231 instanceNo: instanceNo, 19232 type: ErrorTypes.MEDIA_ERROR, 19233 details: ErrorDetails.FRAG_PARSING_ERROR, 19234 chunkMeta: data.chunkMeta, 19235 fatal: false, 19236 error: error, 19237 err: error, 19238 reason: "transmuxer-worker push error" 19239 }, instanceNo); 19240 }); 19241 } else { 19242 emitTransmuxComplete(self, transmuxResult, instanceNo); 19243 } 19244 break; 19245 } 19246 case 'flush': 19247 { 19248 var chunkMeta = data.chunkMeta; 19249 var _transmuxResult = transmuxer.flush(chunkMeta); 19250 if (isPromise(_transmuxResult)) { 19251 _transmuxResult.then(function (results) { 19252 handleFlushResult(self, results, chunkMeta, instanceNo); 19253 }).catch(function (error) { 19254 forwardMessage(Events.ERROR, { 19255 type: ErrorTypes.MEDIA_ERROR, 19256 details: ErrorDetails.FRAG_PARSING_ERROR, 19257 chunkMeta: data.chunkMeta, 19258 fatal: false, 19259 error: error, 19260 err: error, 19261 reason: "transmuxer-worker flush error" 19262 }, instanceNo); 19263 }); 19264 } else { 19265 handleFlushResult(self, _transmuxResult, chunkMeta, instanceNo); 19266 } 19267 break; 19268 } 19269 } 19270 }); 19271 } 19272 function emitTransmuxComplete(self, transmuxResult, instanceNo) { 19273 if (isEmptyResult(transmuxResult.remuxResult)) { 19274 return false; 19275 } 19276 var transferable = []; 19277 var _transmuxResult$remux = transmuxResult.remuxResult, 19278 audio = _transmuxResult$remux.audio, 19279 video = _transmuxResult$remux.video; 19280 if (audio) { 19281 addToTransferable(transferable, audio); 19282 } 19283 if (video) { 19284 addToTransferable(transferable, video); 19285 } 19286 self.postMessage({ 19287 event: 'transmuxComplete', 19288 data: transmuxResult, 19289 instanceNo: instanceNo 19290 }, transferable); 19291 return true; 19292 } 19293 19294 // Converts data to a transferable object https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast) 19295 // in order to minimize message passing overhead 19296 function addToTransferable(transferable, track) { 19297 if (track.data1) { 19298 transferable.push(track.data1.buffer); 19299 } 19300 if (track.data2) { 19301 transferable.push(track.data2.buffer); 19302 } 19303 } 19304 function handleFlushResult(self, results, chunkMeta, instanceNo) { 19305 var parsed = results.reduce(function (parsed, result) { 19306 return emitTransmuxComplete(self, result, instanceNo) || parsed; 19307 }, false); 19308 if (!parsed) { 19309 // Emit at least one "transmuxComplete" message even if media is not found to update stream-controller state to PARSING 19310 self.postMessage({ 19311 event: 'transmuxComplete', 19312 data: results[0], 19313 instanceNo: instanceNo 19314 }); 19315 } 19316 self.postMessage({ 19317 event: 'flush', 19318 data: chunkMeta, 19319 instanceNo: instanceNo 19320 }); 19321 } 19322 function forwardMessage(event, data, instanceNo) { 19323 self.postMessage({ 19324 event: event, 19325 data: data, 19326 instanceNo: instanceNo 19327 }); 19328 } 19329 function forwardWorkerLogs(logger, instanceNo) { 19330 var _loop = function _loop(logFn) { 19331 var func = function func(message) { 19332 forwardMessage('workerLog', { 19333 logType: logFn, 19334 message: message 19335 }, instanceNo); 19336 }; 19337 logger[logFn] = func; 19338 }; 19339 for (var logFn in logger) { 19340 _loop(logFn); 19341 } 19342 } 19343 function isEmptyResult(remuxResult) { 19344 return !remuxResult.audio && !remuxResult.video && !remuxResult.text && !remuxResult.id3 && !remuxResult.initSegment; 19345 } 19346 19347 var version = undefined; 19348 19349 // ensure the worker ends up in the bundle 19350 // If the worker should not be included this gets aliased to empty.js 19351 var workerStore = {}; 19352 function hasUMDWorker() { 19353 return typeof __HLS_WORKER_BUNDLE__ === 'function'; 19354 } 19355 function injectWorker() { 19356 var workerContext = workerStore[version]; 19357 if (workerContext) { 19358 workerContext.clientCount++; 19359 return workerContext; 19360 } 19361 var blob = new self.Blob(["var exports={};var module={exports:exports};function define(f){f()};define.amd=true;(" + __HLS_WORKER_BUNDLE__.toString() + ")(true);"], { 19362 type: 'text/javascript' 19363 }); 19364 var objectURL = self.URL.createObjectURL(blob); 19365 var worker = new self.Worker(objectURL); 19366 var result = { 19367 worker: worker, 19368 objectURL: objectURL, 19369 clientCount: 1 19370 }; 19371 workerStore[version] = result; 19372 return result; 19373 } 19374 function loadWorker(path) { 19375 var workerContext = workerStore[path]; 19376 if (workerContext) { 19377 workerContext.clientCount++; 19378 return workerContext; 19379 } 19380 var scriptURL = new self.URL(path, self.location.href).href; 19381 var worker = new self.Worker(scriptURL); 19382 var result = { 19383 worker: worker, 19384 scriptURL: scriptURL, 19385 clientCount: 1 19386 }; 19387 workerStore[path] = result; 19388 return result; 19389 } 19390 function removeWorkerFromStore(path) { 19391 var workerContext = workerStore[path || version]; 19392 if (workerContext) { 19393 var clientCount = workerContext.clientCount--; 19394 if (clientCount === 1) { 19395 var worker = workerContext.worker, 19396 objectURL = workerContext.objectURL; 19397 delete workerStore[path || version]; 19398 if (objectURL) { 19399 // revoke the Object URL that was used to create transmuxer worker, so as not to leak it 19400 self.URL.revokeObjectURL(objectURL); 19401 } 19402 worker.terminate(); 19403 } 19404 } 19405 } 19406 19407 var transmuxerInstanceCount = 0; 19408 var TransmuxerInterface = /*#__PURE__*/function () { 19409 function TransmuxerInterface(_hls, id, onTransmuxComplete, onFlush) { 19410 var _this = this; 19411 this.error = null; 19412 this.hls = void 0; 19413 this.id = void 0; 19414 this.instanceNo = transmuxerInstanceCount++; 19415 this.observer = void 0; 19416 this.frag = null; 19417 this.part = null; 19418 this.useWorker = void 0; 19419 this.workerContext = null; 19420 this.transmuxer = null; 19421 this.onTransmuxComplete = void 0; 19422 this.onFlush = void 0; 19423 this.onWorkerMessage = function (event) { 19424 var data = event.data; 19425 var hls = _this.hls; 19426 if (!hls || !(data != null && data.event) || data.instanceNo !== _this.instanceNo) { 19427 return; 19428 } 19429 switch (data.event) { 19430 case 'init': 19431 { 19432 var _this$workerContext; 19433 var objectURL = (_this$workerContext = _this.workerContext) == null ? void 0 : _this$workerContext.objectURL; 19434 if (objectURL) { 19435 // revoke the Object URL that was used to create transmuxer worker, so as not to leak it 19436 self.URL.revokeObjectURL(objectURL); 19437 } 19438 break; 19439 } 19440 case 'transmuxComplete': 19441 { 19442 _this.handleTransmuxComplete(data.data); 19443 break; 19444 } 19445 case 'flush': 19446 { 19447 _this.onFlush(data.data); 19448 break; 19449 } 19450 19451 // pass logs from the worker thread to the main logger 19452 case 'workerLog': 19453 { 19454 if (hls.logger[data.data.logType]) { 19455 hls.logger[data.data.logType](data.data.message); 19456 } 19457 break; 19458 } 19459 default: 19460 { 19461 data.data = data.data || {}; 19462 data.data.frag = _this.frag; 19463 data.data.part = _this.part; 19464 data.data.id = _this.id; 19465 hls.trigger(data.event, data.data); 19466 break; 19467 } 19468 } 19469 }; 19470 this.onWorkerError = function (event) { 19471 if (!_this.hls) { 19472 return; 19473 } 19474 var error = new Error(event.message + " (" + event.filename + ":" + event.lineno + ")"); 19475 _this.hls.config.enableWorker = false; 19476 _this.hls.logger.warn("Error in \"" + _this.id + "\" Web Worker, fallback to inline"); 19477 _this.hls.trigger(Events.ERROR, { 19478 type: ErrorTypes.OTHER_ERROR, 19479 details: ErrorDetails.INTERNAL_EXCEPTION, 19480 fatal: false, 19481 event: 'demuxerWorker', 19482 error: error 19483 }); 19484 }; 19485 var config = _hls.config; 19486 this.hls = _hls; 19487 this.id = id; 19488 this.useWorker = !!config.enableWorker; 19489 this.onTransmuxComplete = onTransmuxComplete; 19490 this.onFlush = onFlush; 19491 var forwardMessage = function forwardMessage(ev, data) { 19492 data = data || {}; 19493 data.frag = _this.frag || undefined; 19494 if (ev === Events.ERROR) { 19495 data = data; 19496 data.parent = _this.id; 19497 data.part = _this.part; 19498 _this.error = data.error; 19499 } 19500 _this.hls.trigger(ev, data); 19501 }; 19502 19503 // forward events to main thread 19504 this.observer = new EventEmitter(); 19505 this.observer.on(Events.FRAG_DECRYPTED, forwardMessage); 19506 this.observer.on(Events.ERROR, forwardMessage); 19507 var m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource); 19508 if (this.useWorker && typeof Worker !== 'undefined') { 19509 var logger = this.hls.logger; 19510 var canCreateWorker = config.workerPath || hasUMDWorker(); 19511 if (canCreateWorker) { 19512 try { 19513 if (config.workerPath) { 19514 logger.log("loading Web Worker " + config.workerPath + " for \"" + id + "\""); 19515 this.workerContext = loadWorker(config.workerPath); 19516 } else { 19517 logger.log("injecting Web Worker for \"" + id + "\""); 19518 this.workerContext = injectWorker(); 19519 } 19520 var worker = this.workerContext.worker; 19521 worker.addEventListener('message', this.onWorkerMessage); 19522 worker.addEventListener('error', this.onWorkerError); 19523 worker.postMessage({ 19524 instanceNo: this.instanceNo, 19525 cmd: 'init', 19526 typeSupported: m2tsTypeSupported, 19527 id: id, 19528 config: JSON.stringify(config) 19529 }); 19530 } catch (err) { 19531 logger.warn("Error setting up \"" + id + "\" Web Worker, fallback to inline", err); 19532 this.terminateWorker(); 19533 this.error = null; 19534 this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, '', id, _hls.logger); 19535 } 19536 return; 19537 } 19538 } 19539 this.transmuxer = new Transmuxer(this.observer, m2tsTypeSupported, config, '', id, _hls.logger); 19540 } 19541 var _proto = TransmuxerInterface.prototype; 19542 _proto.reset = function reset() { 19543 this.frag = null; 19544 this.part = null; 19545 if (this.workerContext) { 19546 var instanceNo = this.instanceNo; 19547 this.instanceNo = transmuxerInstanceCount++; 19548 var config = this.hls.config; 19549 var m2tsTypeSupported = getM2TSSupportedAudioTypes(config.preferManagedMediaSource); 19550 this.workerContext.worker.postMessage({ 19551 instanceNo: this.instanceNo, 19552 cmd: 'reset', 19553 resetNo: instanceNo, 19554 typeSupported: m2tsTypeSupported, 19555 id: this.id, 19556 config: JSON.stringify(config) 19557 }); 19558 } 19559 }; 19560 _proto.terminateWorker = function terminateWorker() { 19561 if (this.workerContext) { 19562 var worker = this.workerContext.worker; 19563 this.workerContext = null; 19564 worker.removeEventListener('message', this.onWorkerMessage); 19565 worker.removeEventListener('error', this.onWorkerError); 19566 removeWorkerFromStore(this.hls.config.workerPath); 19567 } 19568 }; 19569 _proto.destroy = function destroy() { 19570 if (this.workerContext) { 19571 this.terminateWorker(); 19572 // @ts-ignore 19573 this.onWorkerMessage = this.onWorkerError = null; 19574 } else { 19575 var transmuxer = this.transmuxer; 19576 if (transmuxer) { 19577 transmuxer.destroy(); 19578 this.transmuxer = null; 19579 } 19580 } 19581 var observer = this.observer; 19582 if (observer) { 19583 observer.removeAllListeners(); 19584 } 19585 this.frag = null; 19586 this.part = null; 19587 // @ts-ignore 19588 this.observer = null; 19589 // @ts-ignore 19590 this.hls = null; 19591 }; 19592 _proto.push = function push(data, initSegmentData, audioCodec, videoCodec, frag, part, duration, accurateTimeOff
vendor: 4,789 bytes, lines 19592-19692
19592set, chunkMeta, defaultInitPTS) { 19593 var _frag$initSegment, 19594 _lastFrag$initSegment, 19595 _this2 = this; 19596 chunkMeta.transmuxing.start = self.performance.now(); 19597 var instanceNo = this.instanceNo, 19598 transmuxer = this.transmuxer; 19599 var timeOffset = part ? part.start : frag.start; 19600 // TODO: push "clear-lead" decrypt data for unencrypted fragments in streams with encrypted ones 19601 var decryptdata = frag.decryptdata; 19602 var lastFrag = this.frag; 19603 var discontinuity = !(lastFrag && frag.cc === lastFrag.cc); 19604 var trackSwitch = !(lastFrag && chunkMeta.level === lastFrag.level); 19605 var snDiff = lastFrag ? chunkMeta.sn - lastFrag.sn : -1; 19606 var partDiff = this.part ? chunkMeta.part - this.part.index : -1; 19607 var progressive = snDiff === 0 && chunkMeta.id > 1 && chunkMeta.id === (lastFrag == null ? void 0 : lastFrag.stats.chunkCount); 19608 var contiguous = !trackSwitch && (snDiff === 1 || snDiff === 0 && (partDiff === 1 || progressive && partDiff <= 0)); 19609 var now = self.performance.now(); 19610 if (trackSwitch || snDiff || frag.stats.parsing.start === 0) { 19611 frag.stats.parsing.start = now; 19612 } 19613 if (part && (partDiff || !contiguous)) { 19614 part.stats.parsing.start = now; 19615 } 19616 var initSegmentChange = !(lastFrag && ((_frag$initSegment = frag.initSegment) == null ? void 0 : _frag$initSegment.url) === ((_lastFrag$initSegment = lastFrag.initSegment) == null ? void 0 : _lastFrag$initSegment.url)); 19617 var state = new TransmuxState(discontinuity, contiguous, accurateTimeOffset, trackSwitch, timeOffset, initSegmentChange); 19618 if (!contiguous || discontinuity || initSegmentChange) { 19619 this.hls.logger.log("[transmuxer-interface, " + frag.type + "]: Starting new transmux session for sn: " + chunkMeta.sn + " p: " + chunkMeta.part + " level: " + chunkMeta.level + " id: " + chunkMeta.id + "\n discontinuity: " + discontinuity + "\n trackSwitch: " + trackSwitch + "\n contiguous: " + contiguous + "\n accurateTimeOffset: " + accurateTimeOffset + "\n timeOffset: " + timeOffset + "\n initSegmentChange: " + initSegmentChange); 19620 var config = new TransmuxConfig(audioCodec, videoCodec, initSegmentData, duration, defaultInitPTS); 19621 this.configureTransmuxer(config); 19622 } 19623 this.frag = frag; 19624 this.part = part; 19625 19626 // Frags with sn of 'initSegment' are not transmuxed 19627 if (this.workerContext) { 19628 // post fragment payload as transferable objects for ArrayBuffer (no copy) 19629 this.workerContext.worker.postMessage({ 19630 instanceNo: instanceNo, 19631 cmd: 'demux', 19632 data: data, 19633 decryptdata: decryptdata, 19634 chunkMeta: chunkMeta, 19635 state: state 19636 }, data instanceof ArrayBuffer ? [data] : []); 19637 } else if (transmuxer) { 19638 var transmuxResult = transmuxer.push(data, decryptdata, chunkMeta, state); 19639 if (isPromise(transmuxResult)) { 19640 transmuxResult.then(function (data) { 19641 _this2.handleTransmuxComplete(data); 19642 }).catch(function (error) { 19643 _this2.transmuxerError(error, chunkMeta, 'transmuxer-interface push error'); 19644 }); 19645 } else { 19646 this.handleTransmuxComplete(transmuxResult); 19647 } 19648 } 19649 }; 19650 _proto.flush = function flush(chunkMeta) { 19651 var _this3 = this; 19652 chunkMeta.transmuxing.start = self.performance.now(); 19653 var instanceNo = this.instanceNo, 19654 transmuxer = this.transmuxer; 19655 if (this.workerContext) { 19656 this.workerContext.worker.postMessage({ 19657 instanceNo: instanceNo, 19658 cmd: 'flush', 19659 chunkMeta: chunkMeta 19660 }); 19661 } else if (transmuxer) { 19662 var transmuxResult = transmuxer.flush(chunkMeta); 19663 if (isPromise(transmuxResult)) { 19664 transmuxResult.then(function (data) { 19665 _this3.handleFlushResult(data, chunkMeta); 19666 }).catch(function (error) { 19667 _this3.transmuxerError(error, chunkMeta, 'transmuxer-interface flush error'); 19668 }); 19669 } else { 19670 this.handleFlushResult(transmuxResult, chunkMeta); 19671 } 19672 } 19673 }; 19674 _proto.transmuxerError = function transmuxerError(error, chunkMeta, reason) { 19675 if (!this.hls) { 19676 return; 19677 } 19678 this.error = error; 19679 this.hls.trigger(Events.ERROR, { 19680 type: ErrorTypes.MEDIA_ERROR, 19681 details: ErrorDetails.FRAG_PARSING_ERROR, 19682 chunkMeta: chunkMeta, 19683 frag: this.frag || undefined, 19684 part: this.part || undefined, 19685 fatal: false, 19686 error: error, 19687 err: error, 19688 reason: reason 19689 }); 19690 }; 19691 _proto.handleFlushResult = function handleFlushResult(results, chunkMeta) { 19692 var _this4 = this;
19693 results.forEach(function (result) { 19694 _this4.handleTransmuxComplete(result); 19695 }); 19696 this.onFlush(chunkMeta); 19697 }; 19698 _proto.configureTransmuxer = function configureTransmuxer(config) { 19699 var instanceNo = this.instanceNo, 19700 transmuxer = this.transmuxer; 19701 if (this.workerContext) { 19702 this.workerContext.worker.postMessage({ 19703 instanceNo: instanceNo, 19704 cmd: 'configure', 19705 config: config 19706 }); 19707 } else if (transmuxer) { 19708 transmuxer.configure(config); 19709 } 19710 }; 19711 _proto.handleTransmuxComplete = function handleTransmuxComplete(result) { 19712 result.chunkMeta.transmuxing.end = self.performance.now(); 19713 this.onTransmuxComplete(result); 19714 }; 19715 return TransmuxerInterface; 19716 }(); 19717 19718 var STALL_MINIMUM_DURATION_MS = 250; 19719 var MAX_START_GAP_JUMP = 2.0; 19720 var SKIP_BUFFER_HOLE_STEP_SECONDS = 0.1; 19721 var SKIP_BUFFER_RANGE_START = 0.05; 19722 var GapController = /*#__PURE__*/function (_Logger) { 19723 function GapController(config, media, fragmentTracker, hls) { 19724 var _this; 19725 _this = _Logger.call(this, 'gap-controller', hls.logger) || this; 19726 _this.config = void 0; 19727 _this.media = null; 19728 _this.fragmentTracker = void 0; 19729 _this.hls = void 0; 19730 _this.nudgeRetry = 0; 19731 _this.stallReported = false; 19732 _this.stalled = null; 19733 _this.moved = false; 19734 _this.seeking = false; 19735 _this.ended = 0; 19736 _this.config = config; 19737 _this.media = media; 19738 _this.fragmentTracker = fragmentTracker; 19739 _this.hls = hls; 19740 return _this; 19741 } 19742 _inheritsLoose(GapController, _Logger); 19743 var _proto = GapController.prototype; 19744 _proto.destroy = function destroy() { 19745 this.media = null; 19746 // @ts-ignore 19747 this.hls = this.fragmentTracker = null; 19748 } 19749 19750 /** 19751 * Checks if the playhead is stuck within a gap, and if so, attempts to free it. 19752 * A gap is an unbuffered range between two buffered ranges (or the start and the first buffered range). 19753 * 19754 * @param lastCurrentTime - Previously read playhead position 19755 */; 19756 _proto.poll = function poll(lastCurrentTime, activeFrag, levelDetails, state) { 19757 var config = this.config, 19758 media = this.media, 19759 stalled = this.stalled; 19760 if (media === null) { 19761 return; 19762 } 19763 var currentTime = media.currentTime, 19764 seeking = media.seeking; 19765 var seeked = this.seeking && !seeking; 19766 var beginSeek = !this.seeking && seeking; 19767 this.seeking = seeking; 19768 19769 // The playhead is moving, no-op 19770 if (currentTime !== lastCurrentTime) { 19771 this.ended = 0; 19772 this.moved = true; 19773 if (!seeking) { 19774 this.nudgeRetry = 0; 19775 } 19776 if (stalled !== null) { 19777 // The playhead is now moving, but was previously stalled 19778 if (this.stallReported) { 19779 var _stalledDuration = self.performance.now() - stalled; 19780 this.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(_stalledDuration) + "ms"); 19781 this.stallReported = false; 19782 } 19783 this.stalled = null; 19784 } 19785 return; 19786 } 19787 19788 // Clear stalled state when beginning or finishing seeking so that we don't report stalls coming out of a seek 19789 if (beginSeek || seeked) { 19790 this.stalled = null; 19791 return; 19792 } 19793 19794 // The playhead should not be moving 19795 if (media.paused && !seeking || media.ended || media.playbackRate === 0 || !BufferHelper.getBuffered(media).length) { 19796 this.nudgeRetry = 0; 19797 return; 19798 } 19799 var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0); 19800 var nextStart = bufferInfo.nextStart || 0; 19801 if (seeking) { 19802 // Waiting for seeking in a buffered range to complete 19803 var hasEnoughBuffer = bufferInfo.len > MAX_START_GAP_JUMP; 19804 // Next buffered range is too far ahead to jump to while still seeking 19805 var noBufferGap = !nextStart || activeFrag && activeFrag.start <= currentTime || nextStart - currentTime > MAX_START_GAP_JUMP && !this.fragmentTracker.getPartialFragment(currentTime); 19806 if (hasEnoughBuffer || noBufferGap) { 19807 return; 19808 } 19809 // Reset moved state when seeking to a point in or before a gap 19810 this.moved = false;
19811 } 19812 19813 // Skip start gaps if we haven't played, but the last poll detected the start of a stall 19814 // The addition poll gives the browser a chance to jump the gap for us 19815 if (!this.moved && this.stalled !== null) { 19816 // There is no playable buffer (seeked, waiting for buffer) 19817 var isBuffered = bufferInfo.len > 0; 19818 if (!isBuffered && !nextStart) { 19819 return; 19820 } 19821 // Jump start gaps within jump threshold 19822 var startJump = Math.max(nextStart, bufferInfo.start || 0) - currentTime; 19823 19824 // When joining a live stream with audio tracks, account for live playlist window sliding by allowing 19825 // a larger jump over start gaps caused by the audio-stream-controller buffering a start fragment 19826 // that begins over 1 target duration after the video start position. 19827 var isLive = !!(levelDetails != null && levelDetails.live); 19828 var maxStartGapJump = isLive ? levelDetails.targetduration * 2 : MAX_START_GAP_JUMP; 19829 var partialOrGap = this.fragmentTracker.getPartialFragment(currentTime); 19830 if (startJump > 0 && (startJump <= maxStartGapJump || partialOrGap)) { 19831 if (!media.paused) { 19832 this._trySkipBufferHole(partialOrGap); 19833 } 19834 return; 19835 } 19836 } 19837 19838 // Start tracking stall time 19839 var tnow = self.performance.now(); 19840 if (stalled === null) { 19841 this.stalled = tnow; 19842 return; 19843 } 19844 var stalledDuration = tnow - stalled; 19845 if (!seeking && stalledDuration >= STALL_MINIMUM_DURATION_MS) { 19846 // Dispatch MEDIA_ENDED when media.ended/ended event is not signalled at end of stream 19847 if (state === State.ENDED && !(levelDetails != null && levelDetails.live) && Math.abs(currentTime - ((levelDetails == null ? void 0 : levelDetails.edge) || 0)) < 1) { 19848 if (stalledDuration < 1000 || this.ended) { 19849 return; 19850 } 19851 this.ended = currentTime; 19852 this.hls.trigger(Events.MEDIA_ENDED, { 19853 stalled: true 19854 }); 19855 return; 19856 } 19857 // Report stalling after trying to fix 19858 this._reportStall(bufferInfo); 19859 if (!this.media) { 19860 return; 19861 } 19862 } 19863 var bufferedWithHoles = BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole); 19864 this._tryFixBufferStall(bufferedWithHoles, stalledDuration); 19865 } 19866 19867 /** 19868 * Detects and attempts to fix known buffer stalling issues. 19869 * @param bufferInfo - The properties of the current buffer. 19870 * @param stalledDurationMs - The amount of time Hls.js has been stalling for. 19871 * @private 19872 */; 19873 _proto._tryFixBufferStall = function _tryFixBufferStall(bufferInfo, stalledDurationMs) { 19874 var config = this.config, 19875 fragmentTracker = this.fragmentTracker, 19876 media = this.media; 19877 if (media === null) { 19878 return; 19879 } 19880 var currentTime = media.currentTime; 19881 var partial = fragmentTracker.getPartialFragment(currentTime); 19882 if (partial) { 19883 // Try to skip over the buffer hole caused by a partial fragment 19884 // This method isn't limited by the size of the gap between buffered ranges 19885 var targetTime = this._trySkipBufferHole(partial); 19886 // we return here in this case, meaning 19887 // the branch below only executes when we haven't seeked to a new position 19888 if (targetTime || !this.media) { 19889 return; 19890 } 19891 } 19892 19893 // if we haven't had to skip over a buffer hole of a partial fragment 19894 // we may just have to "nudge" the playlist as the browser decoding/rendering engine 19895 // needs to cross some sort of threshold covering all source-buffers content 19896 // to start playing properly. 19897 if ((bufferInfo.len > config.maxBufferHole || bufferInfo.nextStart && bufferInfo.nextStart - currentTime < config.maxBufferHole) && stalledDurationMs > config.highBufferWatchdogPeriod * 1000) { 19898 this.warn('Trying to nudge playhead over buffer-hole'); 19899 // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds 19900 // We only try to jump the hole if it's under the configured size 19901 // Reset stalled so to rearm watchdog timer 19902 this.stalled = null; 19903 this._tryNudgeBuffer(); 19904 } 19905 } 19906 19907 /** 19908 * Triggers a BUFFER_STALLED_ERROR event, but only once per stall period. 19909 * @param bufferLen - The playhead distance from the end of the current buffer segment. 19910 * @private 19911 */; 19912 _proto._reportStall = function _reportStall(bufferInfo) { 19913 var hls = this.hls, 19914 media = this.media, 19915 stallReported = this.stallReported; 19916 if (!stallReported && media) { 19917 // Report stalled error once 19918 this.stallReported = true; 19919 var error = new Error("Playback stalling at @" + media.currentTime + " due to low buffer (" + JSON.stringify(bufferInfo) + ")"); 19920 this.warn(error.message); 19921 hls.trigger(Events.ERROR, { 19922 type: ErrorTypes.MEDIA_ERROR, 19923 details: ErrorDetails.BUFFER_STALLED_ERROR, 19924 fatal: false, 19925 error: error, 19926 buffer: bufferInfo.len 19927 }); 19928 } 19929 } 19930 19931 /** 19932 * Attempts to fix buffer stalls by jumping over known gaps caused by partial fragments 19933 * @param partial - The partial fragment found at the current time (where playback is stalling). 19934 * @private 19935 */; 19936 _proto._trySkipBufferHole = function _trySkipBufferHole(partial) { 19937 var config = this.config, 19938 hls = this.hls, 19939 media = this.media; 19940 if (media === null) { 19941 return 0; 19942 } 19943 19944 // Check if currentTime is between unbuffered regions of partial fragments 19945 var currentTime = media.currentTime; 19946 var bufferInfo = BufferHelper.bufferInfo(media, currentTime, 0); 19947 var startTime = currentTime < bufferInfo.start ? bufferInfo.start : bufferInfo.nextStart; 19948 if (startTime) { 19949 var bufferStarved = bufferInfo.len <= config.maxBufferHole; 19950 var waiting = bufferInfo.len > 0 && bufferInfo.len < 1 && media.readyState < 3; 19951 var gapLength = startTime - currentTime; 19952 if (gapLength > 0 && (bufferStarved || waiting)) { 19953 // Only allow large gaps to be skipped if it is a start gap, or all fragments in skip range are partial 19954 if (gapLength > config.maxBufferHole) { 19955 var fragmentTracker = this.fragmentTracker; 19956 var startGap = false;
19957 if (currentTime === 0) { 19958 var startFrag = fragmentTracker.getAppendedFrag(0, PlaylistLevelType.MAIN); 19959 if (startFrag && startTime < startFrag.end) { 19960 startGap = true; 19961 } 19962 } 19963 if (!startGap) { 19964 var startProvisioned = partial || fragmentTracker.getAppendedFrag(currentTime, PlaylistLevelType.MAIN); 19965 if (startProvisioned) { 19966 var moreToLoad = false; 19967 var pos = startProvisioned.end; 19968 while (pos < startTime) { 19969 var provisioned = fragmentTracker.getPartialFragment(pos); 19970 if (provisioned) { 19971 pos += provisioned.duration; 19972 } else { 19973 moreToLoad = true; 19974 break; 19975 } 19976 } 19977 if (moreToLoad) { 19978 return 0; 19979 } 19980 } 19981 } 19982 } 19983 var targetTime = Math.max(startTime + SKIP_BUFFER_RANGE_START, currentTime + SKIP_BUFFER_HOLE_STEP_SECONDS); 19984 this.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + targetTime); 19985 this.moved = true; 19986 this.stalled = null; 19987 media.currentTime = targetTime; 19988 if (partial && !partial.gap) { 19989 var error = new Error("fragment loaded with buffer holes, seeking from " + currentTime + " to " + targetTime); 19990 hls.trigger(Events.ERROR, { 19991 type: ErrorTypes.MEDIA_ERROR, 19992 details: ErrorDetails.BUFFER_SEEK_OVER_HOLE, 19993 fatal: false, 19994 error: error, 19995 reason: error.message, 19996 frag: partial 19997 }); 19998 } 19999 return targetTime; 20000 } 20001 } 20002 return 0; 20003 } 20004 20005 /** 20006 * Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount. 20007 * @private 20008 */; 20009 _proto._tryNudgeBuffer = function _tryNudgeBuffer() { 20010 var config = this.config, 20011 hls = this.hls, 20012 media = this.media, 20013 nudgeRetry = this.nudgeRetry; 20014 if (media === null) { 20015 return; 20016 } 20017 var currentTime = media.currentTime; 20018 this.nudgeRetry++; 20019 if (nudgeRetry < config.nudgeMaxRetry) { 20020 var targetTime = currentTime + (nudgeRetry + 1) * config.nudgeOffset; 20021 // playback stalled in buffered area ... let's nudge currentTime to try to overcome this 20022 var error = new Error("Nudging 'currentTime' from " + currentTime + " to " + targetTime); 20023 this.warn(error.message); 20024 media.currentTime = targetTime; 20025 hls.trigger(Events.ERROR, { 20026 type: ErrorTypes.MEDIA_ERROR, 20027 details: ErrorDetails.BUFFER_NUDGE_ON_STALL, 20028 error: error, 20029 fatal: false 20030 }); 20031 } else { 20032 var _error = new Error("Playhead still not moving while enough data buffered @" + currentTime + " after " + config.nudgeMaxRetry + " nudges"); 20033 this.error(_error.message); 20034 hls.trigger(Events.ERROR, { 20035 type: ErrorTypes.MEDIA_ERROR, 20036 details: ErrorDetails.BUFFER_STALLED_ERROR, 20037 error: _error, 20038 fatal: true 20039 }); 20040 } 20041 }; 20042 return GapController; 20043 }(Logger); 20044 20045 var TICK_INTERVAL = 100; // how often to tick in ms 20046 var StreamController = /*#__PURE__*/function (_BaseStreamController) { 20047 function StreamController(hls, fragmentTracker, keyLoader) { 20048 var _this; 20049 _this = _BaseStreamController.call(this, hls, fragmentTracker, keyLoader, 'stream-controller', PlaylistLevelType.MAIN) || this; 20050 _this.audioCodecSwap = false; 20051 _this.gapController = null; 20052 _this.level = -1; 20053 _this._forceStartLoad = false; 20054 _this.altAudio = false; 20055 _this.audioOnly = false; 20056 _this.fragPlaying = null; 20057 _this.fragLastKbps = 0; 20058 _this.couldBacktrack = false; 20059 _this.backtrackFragment = null; 20060 _this.audioCodecSwitch = false; 20061 _this.videoBuffer = null; 20062 _this.onMediaPlaying = function () { 20063 // tick to speed up FRAG_CHANGED triggering 20064 _this.tick(); 20065 }; 20066 _this.onMediaSeeked = function () { 20067 var media = _this.media; 20068 var currentTime = media ? media.currentTime : null; 20069 if (isFiniteNumber(currentTime)) { 20070 _this.log("Media seeked to " + currentTime.toFixed(3)); 20071 } 20072 20073 // If seeked was issued before buffer was appended do not tick immediately 20074 var bufferInfo = _this.getMainFwdBufferInfo(); 20075 if (bufferInfo === null || bufferInfo.len === 0) { 20076 _this.warn("Main forward buffer length on \"seeked\" event " + (bufferInfo ? bufferInfo.len : 'empty') + ")"); 20077 return; 20078 } 20079 20080 // tick to speed up FRAG_CHANGED triggering 20081 _this.tick(); 20082 }; 20083 _this.registerListeners(); 20084 return _this; 20085 } 20086 _inheritsLoose(StreamController, _BaseStreamController); 20087 var _proto = StreamController.prototype; 20088 _proto.registerListeners = function registerListeners() { 20089 _BaseStreamController.prototype.registerListeners.call(this); 20090 var hls = this.hls; 20091 hls.on(Events.MANIFEST_PARSED, this.onManifestParsed, this); 20092 hls.on(Events.LEVEL_LOADING, this.onLevelLoading, this); 20093 hls.on(Events.LEVEL_LOADED, this.onLevelLoaded, this); 20094 hls.on(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this); 20095 hls.on(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this); 20096 hls.on(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this); 20097 hls.on(Events.BUFFER_CREATED, this.onBufferCreated, this); 20098 hls.on(Events.BUFFER_FLUSHED, this.onBufferFlushed, this); 20099 hls.on(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); 20100 hls.on(Events.FRAG_BUFFERED, this.onFragBuffered, this); 20101 }; 20102 _proto.unregisterListeners = function unregisterListeners() { 20103 _BaseStreamController.prototype.unregisterListeners.call(this); 20104 var hls = this.hls; 20105 hls.off(Events.MANIFEST_PARSED, this.onManifestParsed, this); 20106 hls.off(Events.LEVEL_LOADED, this.onLevelLoaded, this); 20107 hls.off(Events.FRAG_LOAD_EMERGENCY_ABORTED, this.onFragLoadEmergencyAborted, this); 20108 hls.off(Events.AUDIO_TRACK_SWITCHING, this.onAudioTrackSwitching, this); 20109 hls.off(Events.AUDIO_TRACK_SWITCHED, this.onAudioTrackSwitched, this); 20110 hls.off(Events.BUFFER_CREATED, this.onBufferCreated, this); 20111 hls.off(Events.BUFFER_FLUSHED, this.onBufferFlushed, this); 20112 hls.off(Events.LEVELS_UPDATED, this.onLevelsUpdated, this); 20113 hls.off(Events.FRAG_BUFFERED, this.onFragBuffered, this); 20114 }; 20115 _proto.onHandlerDestroying = function onHandlerDestroying() { 20116 // @ts-ignore 20117 this.onMediaPlaying = this.onMediaSeeked = null; 20118 this.unregisterListeners(); 20119 _BaseStreamController.prototype.onHandlerDestroying.call(this); 20120 }; 20121 _proto.startLoad = function startLoad(startPosition) { 20122 if (this.levels) { 20123 var lastCurrentTime = this.lastCurrentTime, 20124 hls = this.hls; 20125 this.stopLoad(); 20126 this.setInterval(TICK_INTERVAL); 20127 this.level = -1; 20128 if (!this.startFragRequested) { 20129 // determine load level 20130 var startLevel = hls.startLevel; 20131 if (startLevel === -1) { 20132 if (hls.config.testBandwidth && this.levels.length > 1) {
vendor: 10,305 bytes, lines 20133-20367
20133 // -1 : guess start Level by doing a bitrate test by loading first fragment of lowest quality level 20134 startLevel = 0; 20135 this.bitrateTest = true; 20136 } else { 20137 startLevel = hls.firstAutoLevel; 20138 } 20139 } 20140 // set new level to playlist loader : this will trigger start level load 20141 // hls.nextLoadLevel remains until it is set to a new value or until a new frag is successfully loaded 20142 hls.nextLoadLevel = startLevel; 20143 this.level = hls.loadLevel; 20144 this.loadedmetadata = false; 20145 } 20146 // if startPosition undefined but lastCurrentTime set, set startPosition to last currentTime 20147 if (lastCurrentTime > 0 && startPosition === -1) { 20148 this.log("Override startPosition with lastCurrentTime @" + lastCurrentTime.toFixed(3)); 20149 startPosition = lastCurrentTime; 20150 } 20151 this.state = State.IDLE; 20152 this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition; 20153 this.tick(); 20154 } else { 20155 this._forceStartLoad = true; 20156 this.state = State.STOPPED; 20157 } 20158 }; 20159 _proto.stopLoad = function stopLoad() { 20160 this._forceStartLoad = false; 20161 _BaseStreamController.prototype.stopLoad.call(this); 20162 }; 20163 _proto.doTick = function doTick() { 20164 switch (this.state) { 20165 case State.WAITING_LEVEL: 20166 { 20167 var levels = this.levels, 20168 level = this.level; 20169 var currentLevel = levels == null ? void 0 : levels[level]; 20170 var details = currentLevel == null ? void 0 : currentLevel.details; 20171 if (details && (!details.live || this.levelLastLoaded === currentLevel)) { 20172 if (this.waitForCdnTuneIn(details)) { 20173 break; 20174 } 20175 this.state = State.IDLE; 20176 break; 20177 } else if (this.hls.nextLoadLevel !== this.level) { 20178 this.state = State.IDLE; 20179 break; 20180 } 20181 break; 20182 } 20183 case State.FRAG_LOADING_WAITING_RETRY: 20184 { 20185 var _this$media; 20186 var now = self.performance.now(); 20187 var retryDate = this.retryDate; 20188 // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading 20189 if (!retryDate || now >= retryDate || (_this$media = this.media) != null && _this$media.seeking) { 20190 var _levels = this.levels, 20191 _level = this.level; 20192 var _currentLevel = _levels == null ? void 0 : _levels[_level]; 20193 this.resetStartWhenNotLoaded(_currentLevel || null); 20194 this.state = State.IDLE; 20195 } 20196 } 20197 break; 20198 } 20199 if (this.state === State.IDLE) { 20200 this.doTickIdle(); 20201 } 20202 this.onTickEnd(); 20203 }; 20204 _proto.onTickEnd = function onTickEnd() { 20205 _BaseStreamController.prototype.onTickEnd.call(this); 20206 this.checkBuffer(); 20207 this.checkFragmentChanged(); 20208 }; 20209 _proto.doTickIdle = function doTickIdle() { 20210 if (!this.buffering) { 20211 return; 20212 } 20213 var hls = this.hls, 20214 levelLastLoaded = this.levelLastLoaded, 20215 levels = this.levels, 20216 media = this.media; 20217 20218 // if start level not parsed yet OR 20219 // if video not attached AND start fragment already requested OR start frag prefetch not enabled 20220 // exit loop, as we either need more info (level not parsed) or we need media to be attached to load new fragment 20221 if (levelLastLoaded === null || !media && (this.startFragRequested || !hls.config.startFragPrefetch)) { 20222 return; 20223 } 20224 20225 // If the "main" level is audio-only but we are loading an alternate track in the same group, do not load anything 20226 if (this.altAudio && this.audioOnly) { 20227 return; 20228 } 20229 var level = hls.nextLoadLevel; 20230 if (!(levels != null && levels[level])) { 20231 return; 20232 } 20233 var levelInfo = levels[level]; 20234 20235 // if buffer length is less than maxBufLen try to load a new fragment 20236 20237 var bufferInfo = this.getMainFwdBufferInfo(); 20238 if (bufferInfo === null) { 20239 return; 20240 } 20241 var lastDetails = this.getLevelDetails(); 20242 if (lastDetails && this._streamEnded(bufferInfo, lastDetails)) { 20243 var data = {}; 20244 if (this.altAudio) { 20245 data.type = 'video'; 20246 } 20247 this.hls.trigger(Events.BUFFER_EOS, data); 20248 this.state = State.ENDED; 20249 return; 20250 } 20251 20252 // set next load level : this will trigger a playlist load if needed 20253 if (hls.loadLevel !== level && hls.manualLevel === -1) { 20254 this.log("Adapting to level " + level + " from level " + this.level); 20255 } 20256 this.level = hls.nextLoadLevel = level; 20257 var levelDetails = levelInfo.details; 20258 // if level info not retrieved yet, switch state and wait for level retrieval 20259 // if live playlist, ensure that new playlist has been refreshed to avoid loading/try to load 20260 // a useless and outdated fragment (that might even introduce load error if it is already out of the live playlist) 20261 if (!levelDetails || this.state === State.WAITING_LEVEL || levelDetails.live && this.levelLastLoaded !== levelInfo) { 20262 this.level = level; 20263 this.state = State.WAITING_LEVEL; 20264 return; 20265 } 20266 var bufferLen = bufferInfo.len; 20267 20268 // compute max Buffer Length that we could get from this load level, based on level bitrate. don't buffer more than 60 MB and more than 30s 20269 var maxBufLen = this.getMaxBufferLength(levelInfo.maxBitrate); 20270 20271 // Stay idle if we are still with buffer margins 20272 if (bufferLen >= maxBufLen) { 20273 return; 20274 } 20275 if (this.backtrackFragment && this.backtrackFragment.start > bufferInfo.end) { 20276 this.backtrackFragment = null; 20277 } 20278 var targetBufferTime = this.backtrackFragment ? this.backtrackFragment.start : bufferInfo.end; 20279 var frag = this.getNextFragment(targetBufferTime, levelDetails); 20280 // Avoid backtracking by loading an earlier segment in streams with segments that do not start with a key frame (flagged by `couldBacktrack`) 20281 if (this.couldBacktrack && !this.fragPrevious && frag && frag.sn !== 'initSegment' && this.fragmentTracker.getState(frag) !== FragmentState.OK) { 20282 var _this$backtrackFragme; 20283 var backtrackSn = ((_this$backtrackFragme = this.backtrackFragment) != null ? _this$backtrackFragme : frag).sn; 20284 var fragIdx = backtrackSn - levelDetails.startSN; 20285 var backtrackFrag = levelDetails.fragments[fragIdx - 1]; 20286 if (backtrackFrag && frag.cc === backtrackFrag.cc) { 20287 frag = backtrackFrag; 20288 this.fragmentTracker.removeFragment(backtrackFrag); 20289 } 20290 } else if (this.backtrackFragment && bufferInfo.len) { 20291 this.backtrackFragment = null; 20292 } 20293 // Avoid loop loading by using nextLoadPosition set for backtracking and skipping consecutive GAP tags 20294 if (frag && this.isLoopLoading(frag, targetBufferTime)) { 20295 var gapStart = frag.gap; 20296 if (!gapStart) { 20297 // Cleanup the fragment tracker before trying to find the next unbuffered fragment 20298 var type = this.audioOnly && !this.altAudio ? ElementaryStreamTypes.AUDIO : ElementaryStreamTypes.VIDEO; 20299 var mediaBuffer = (type === ElementaryStreamTypes.VIDEO ? this.videoBuffer : this.mediaBuffer) || this.media; 20300 if (mediaBuffer) { 20301 this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.MAIN); 20302 } 20303 } 20304 frag = this.getNextFragmentLoopLoading(frag, levelDetails, bufferInfo, PlaylistLevelType.MAIN, maxBufLen); 20305 } 20306 if (!frag) { 20307 return; 20308 } 20309 if (frag.initSegment && !frag.initSegment.data && !this.bitrateTest) { 20310 frag = frag.initSegment; 20311 } 20312 this.loadFragment(frag, levelInfo, targetBufferTime); 20313 }; 20314 _proto.loadFragment = function loadFragment(frag, level, targetBufferTime) { 20315 // Check if fragment is not loaded 20316 var fragState = this.fragmentTracker.getState(frag); 20317 if (fragState === FragmentState.NOT_LOADED || fragState === FragmentState.PARTIAL) { 20318 if (frag.sn === 'initSegment') { 20319 this._loadInitSegment(frag, level); 20320 } else if (this.bitrateTest) { 20321 this.log("Fragment " + frag.sn + " of level " + frag.level + " is being downloaded to test bitrate and will not be buffered"); 20322 this._loadBitrateTestFrag(frag, level); 20323 } else { 20324 _BaseStreamController.prototype.loadFragment.call(this, frag, level, targetBufferTime); 20325 } 20326 } else { 20327 this.clearTrackerIfNeeded(frag); 20328 } 20329 }; 20330 _proto.getBufferedFrag = function getBufferedFrag(position) { 20331 return this.fragmentTracker.getBufferedFrag(position, PlaylistLevelType.MAIN); 20332 }; 20333 _proto.followingBufferedFrag = function followingBufferedFrag(frag) { 20334 if (frag) { 20335 // try to get range of next fragment (500ms after this range) 20336 return this.getBufferedFrag(frag.end + 0.5); 20337 } 20338 return null; 20339 } 20340 20341 /* 20342 on immediate level switch : 20343 - pause playback if playing 20344 - cancel any pending load request 20345 - and trigger a buffer flush 20346 */; 20347 _proto.immediateLevelSwitch = function immediateLevelSwitch() { 20348 this.abortCurrentFrag(); 20349 this.flushMainBuffer(0, Number.POSITIVE_INFINITY); 20350 } 20351 20352 /** 20353 * try to switch ASAP without breaking video playback: 20354 * in order to ensure smooth but quick level switching, 20355 * we need to find the next flushable buffer range 20356 * we should take into account new segment fetch time 20357 */; 20358 _proto.nextLevelSwitch = function nextLevelSwitch() { 20359 var levels = this.levels, 20360 media = this.media; 20361 // ensure that media is defined and that metadata are available (to retrieve currentTime) 20362 if (media != null && media.readyState) { 20363 var fetchdelay; 20364 var fragPlayingCurrent = this.getAppendedFrag(media.currentTime); 20365 if (fragPlayingCurrent && fragPlayingCurrent.start > 1) { 20366 // flush buffer preceding current fragment (flush until current fragment start offset) 20367 // minus 1s to avoid video freezing, that could happen if we flush ke
20367yframe of current video ... 20368 this.flushMainBuffer(0, fragPlayingCurrent.start - 1); 20369 } 20370 var levelDetails = this.getLevelDetails(); 20371 if (levelDetails != null && levelDetails.live) { 20372 var bufferInfo = this.getMainFwdBufferInfo(); 20373 // Do not flush in live stream with low buffer 20374 if (!bufferInfo || bufferInfo.len < levelDetails.targetduration * 2) { 20375 return; 20376 } 20377 } 20378 if (!media.paused && levels) { 20379 // add a safety delay of 1s 20380 var nextLevelId = this.hls.nextLoadLevel; 20381 var nextLevel = levels[nextLevelId]; 20382 var fragLastKbps = this.fragLastKbps; 20383 if (fragLastKbps && this.fragCurrent) { 20384 fetchdelay = this.fragCurrent.duration * nextLevel.maxBitrate / (1000 * fragLastKbps) + 1; 20385 } else { 20386 fetchdelay = 0; 20387 } 20388 } else { 20389 fetchdelay = 0; 20390 } 20391 // this.log('fetchdelay:'+fetchdelay); 20392 // find buffer range that will be reached once new fragment will be fetched 20393 var bufferedFrag = this.getBufferedFrag(media.currentTime + fetchdelay); 20394 if (bufferedFrag) { 20395 // we can flush buffer range following this one without stalling playback 20396 var nextBufferedFrag = this.followingBufferedFrag(bufferedFrag); 20397 if (nextBufferedFrag) { 20398 // if we are here, we can also cancel any loading/demuxing in progress, as they are useless 20399 this.abortCurrentFrag(); 20400 // start flush position is in next buffered frag. Leave some padding for non-independent segments and smoother playback. 20401 var maxStart = nextBufferedFrag.maxStartPTS ? nextBufferedFrag.maxStartPTS : nextBufferedFrag.start; 20402 var fragDuration = nextBufferedFrag.duration; 20403 var 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))); 20404 this.flushMainBuffer(startPts, Number.POSITIVE_INFINITY); 20405 } 20406 } 20407 } 20408 }; 20409 _proto.abortCurrentFrag = function abortCurrentFrag() { 20410 var fragCurrent = this.fragCurrent; 20411 this.fragCurrent = null; 20412 this.backtrackFragment = null; 20413 if (fragCurrent) { 20414 fragCurrent.abortRequests(); 20415 this.fragmentTracker.removeFragment(fragCurrent); 20416 } 20417 switch (this.state) { 20418 case State.KEY_LOADING: 20419 case State.FRAG_LOADING: 20420 case State.FRAG_LOADING_WAITING_RETRY: 20421 case State.PARSING: 20422 case State.PARSED: 20423 this.state = State.IDLE; 20424 break; 20425 } 20426 this.nextLoadPosition = this.getLoadPosition(); 20427 }; 20428 _proto.flushMainBuffer = function flushMainBuffer(startOffset, endOffset) { 20429 _BaseStreamController.prototype.flushMainBuffer.call(this, startOffset, endOffset, this.altAudio ? 'video' : null); 20430 }; 20431 _proto.onMediaAttached = function onMediaAttached(event, data) { 20432 _BaseStreamController.prototype.onMediaAttached.call(this, event, data); 20433 var media = data.media; 20434 media.addEventListener('playing', this.onMediaPlaying); 20435 media.addEventListener('seeked', this.onMediaSeeked); 20436 this.gapController = new GapController(this.config, media, this.fragmentTracker, this.hls); 20437 }; 20438 _proto.onMediaDetaching = function onMediaDetaching() { 20439 var media = this.media; 20440 if (media) { 20441 media.removeEventListener('playing', this.onMediaPlaying); 20442 media.removeEventListener('seeked', this.onMediaSeeked); 20443 } 20444 this.videoBuffer = null; 20445 this.fragPlaying = null; 20446 if (this.gapController) { 20447 this.gapController.destroy(); 20448 this.gapController = null; 20449 } 20450 _BaseStreamController.prototype.onMediaDetaching.call(this); 20451 }; 20452 _proto.onManifestLoading = function onManifestLoading() { 20453 _BaseStreamController.prototype.onManifestLoading.call(this); 20454 // reset buffer on manifest loading 20455 this.log('Trigger BUFFER_RESET'); 20456 this.hls.trigger(Events.BUFFER_RESET, undefined); 20457 this.couldBacktrack = false;
vendor: 4,344 bytes, lines 20458-20561
20458 this.fragLastKbps = 0; 20459 this.fragPlaying = this.backtrackFragment = null; 20460 this.altAudio = this.audioOnly = false; 20461 }; 20462 _proto.onManifestParsed = function onManifestParsed(event, data) { 20463 // detect if we have different kind of audio codecs used amongst playlists 20464 var aac = false; 20465 var heaac = false; 20466 data.levels.forEach(function (level) { 20467 var codec = level.audioCodec; 20468 if (codec) { 20469 aac = aac || codec.indexOf('mp4a.40.2') !== -1; 20470 heaac = heaac || codec.indexOf('mp4a.40.5') !== -1; 20471 } 20472 }); 20473 this.audioCodecSwitch = aac && heaac && !changeTypeSupported(); 20474 if (this.audioCodecSwitch) { 20475 this.log('Both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC'); 20476 } 20477 this.levels = data.levels; 20478 this.startFragRequested = false; 20479 }; 20480 _proto.onLevelLoading = function onLevelLoading(event, data) { 20481 var levels = this.levels; 20482 if (!levels || this.state !== State.IDLE) { 20483 return; 20484 } 20485 var level = levels[data.level]; 20486 if (!level.details || level.details.live && this.levelLastLoaded !== level || this.waitForCdnTuneIn(level.details)) { 20487 this.state = State.WAITING_LEVEL; 20488 } 20489 }; 20490 _proto.onLevelLoaded = function onLevelLoaded(event, data) { 20491 var _curLevel$details; 20492 var levels = this.levels; 20493 var newLevelId = data.level; 20494 var newDetails = data.details; 20495 var duration = newDetails.totalduration; 20496 if (!levels) { 20497 this.warn("Levels were reset while loading level " + newLevelId); 20498 return; 20499 } 20500 this.log("Level " + newLevelId + " loaded [" + newDetails.startSN + "," + newDetails.endSN + "]" + (newDetails.lastPartSn ? "[part-" + newDetails.lastPartSn + "-" + newDetails.lastPartIndex + "]" : '') + ", cc [" + newDetails.startCC + ", " + newDetails.endCC + "] duration:" + duration); 20501 var curLevel = levels[newLevelId]; 20502 var fragCurrent = this.fragCurrent; 20503 if (fragCurrent && (this.state === State.FRAG_LOADING || this.state === State.FRAG_LOADING_WAITING_RETRY)) { 20504 if (fragCurrent.level !== data.level && fragCurrent.loader) { 20505 this.abortCurrentFrag(); 20506 } 20507 } 20508 var sliding = 0; 20509 if (newDetails.live || (_curLevel$details = curLevel.details) != null && _curLevel$details.live) { 20510 var _this$levelLastLoaded; 20511 this.checkLiveUpdate(newDetails); 20512 if (newDetails.deltaUpdateFailed) { 20513 return; 20514 } 20515 sliding = this.alignPlaylists(newDetails, curLevel.details, (_this$levelLastLoaded = this.levelLastLoaded) == null ? void 0 : _this$levelLastLoaded.details); 20516 } 20517 // override level info 20518 curLevel.details = newDetails; 20519 this.levelLastLoaded = curLevel; 20520 this.hls.trigger(Events.LEVEL_UPDATED, { 20521 details: newDetails, 20522 level: newLevelId 20523 }); 20524 20525 // only switch back to IDLE state if we were waiting for level to start downloading a new fragment 20526 if (this.state === State.WAITING_LEVEL) { 20527 if (this.waitForCdnTuneIn(newDetails)) { 20528 // Wait for Low-Latency CDN Tune-in 20529 return; 20530 } 20531 this.state = State.IDLE; 20532 } 20533 if (!this.startFragRequested) { 20534 this.setStartPosition(newDetails, sliding); 20535 } else if (newDetails.live) { 20536 this.synchronizeToLiveEdge(newDetails); 20537 } 20538 20539 // trigger handler right now 20540 this.tick(); 20541 }; 20542 _proto._handleFragmentLoadProgress = function _handleFragmentLoadProgress(data) { 20543 var _frag$initSegment; 20544 var frag = data.frag; 20545 var part = data.part, 20546 payload = data.payload; 20547 var levels = this.levels; 20548 if (!levels) { 20549 this.warn("Levels were reset while fragment load was in progress. Fragment " + frag.sn + " of level " + frag.level + " will not be buffered"); 20550 return; 20551 } 20552 var currentLevel = levels[frag.level]; 20553 var details = currentLevel.details; 20554 if (!details) { 20555 this.warn("Dropping fragment " + frag.sn + " of level " + frag.level + " after level details were reset"); 20556 this.fragmentTracker.removeFragment(frag); 20557 return; 20558 } 20559 var videoCodec = currentLevel.videoCodec; 20560 20561 // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live)
vendor: 5,286 bytes, lines 20562-20671
20562 var accurateTimeOffset = details.PTSKnown || !details.live; 20563 var initSegmentData = (_frag$initSegment = frag.initSegment) == null ? void 0 : _frag$initSegment.data; 20564 var audioCodec = this._getAudioCodec(currentLevel); 20565 20566 // transmux the MPEG-TS data to ISO-BMFF segments 20567 // this.log(`Transmuxing ${frag.sn} of [${details.startSN} ,${details.endSN}],level ${frag.level}, cc ${frag.cc}`); 20568 var transmuxer = this.transmuxer = this.transmuxer || new TransmuxerInterface(this.hls, PlaylistLevelType.MAIN, this._handleTransmuxComplete.bind(this), this._handleTransmuxerFlush.bind(this)); 20569 var partIndex = part ? part.index : -1; 20570 var partial = partIndex !== -1; 20571 var chunkMeta = new ChunkMetadata(frag.level, frag.sn, frag.stats.chunkCount, payload.byteLength, partIndex, partial); 20572 var initPTS = this.initPTS[frag.cc]; 20573 transmuxer.push(payload, initSegmentData, audioCodec, videoCodec, frag, part, details.totalduration, accurateTimeOffset, chunkMeta, initPTS); 20574 }; 20575 _proto.onAudioTrackSwitching = function onAudioTrackSwitching(event, data) { 20576 // if any URL found on new audio track, it is an alternate audio track 20577 var fromAltAudio = this.altAudio; 20578 var altAudio = !!data.url; 20579 // if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered 20580 // don't do anything if we switch to alt audio: audio stream controller is handling it. 20581 // we will just have to change buffer scheduling on audioTrackSwitched 20582 if (!altAudio) { 20583 if (this.mediaBuffer !== this.media) { 20584 this.log('Switching on main audio, use media.buffered to schedule main fragment loading'); 20585 this.mediaBuffer = this.media; 20586 var fragCurrent = this.fragCurrent; 20587 // we need to refill audio buffer from main: cancel any frag loading to speed up audio switch 20588 if (fragCurrent) { 20589 this.log('Switching to main audio track, cancel main fragment load'); 20590 fragCurrent.abortRequests(); 20591 this.fragmentTracker.removeFragment(fragCurrent); 20592 } 20593 // destroy transmuxer to force init segment generation (following audio switch) 20594 this.resetTransmuxer(); 20595 // switch to IDLE state to load new fragment 20596 this.resetLoadingState(); 20597 } else if (this.audioOnly) { 20598 // Reset audio transmuxer so when switching back to main audio we're not still appending where we left off 20599 this.resetTransmuxer(); 20600 } 20601 var hls = this.hls; 20602 // If switching from alt to main audio, flush all audio and trigger track switched 20603 if (fromAltAudio) { 20604 hls.trigger(Events.BUFFER_FLUSHING, { 20605 startOffset: 0, 20606 endOffset: Number.POSITIVE_INFINITY, 20607 type: null 20608 }); 20609 this.fragmentTracker.removeAllFragments(); 20610 } 20611 hls.trigger(Events.AUDIO_TRACK_SWITCHED, data); 20612 } 20613 }; 20614 _proto.onAudioTrackSwitched = function onAudioTrackSwitched(event, data) { 20615 var trackId = data.id; 20616 var altAudio = !!this.hls.audioTracks[trackId].url; 20617 if (altAudio) { 20618 var videoBuffer = this.videoBuffer; 20619 // if we switched on alternate audio, ensure that main fragment scheduling is synced with video sourcebuffer buffered 20620 if (videoBuffer && this.mediaBuffer !== videoBuffer) { 20621 this.log('Switching on alternate audio, use video.buffered to schedule main fragment loading'); 20622 this.mediaBuffer = videoBuffer; 20623 } 20624 } 20625 this.altAudio = altAudio; 20626 this.tick(); 20627 }; 20628 _proto.onBufferCreated = function onBufferCreated(event, data) { 20629 var tracks = data.tracks; 20630 var mediaTrack; 20631 var name; 20632 var alternate = false; 20633 for (var type in tracks) { 20634 var track = tracks[type]; 20635 if (track.id === 'main') { 20636 name = type; 20637 mediaTrack = track; 20638 // keep video source buffer reference 20639 if (type === 'video') { 20640 var videoTrack = tracks[type]; 20641 if (videoTrack) { 20642 this.videoBuffer = videoTrack.buffer; 20643 } 20644 } 20645 } else { 20646 alternate = true; 20647 } 20648 } 20649 if (alternate && mediaTrack) { 20650 this.log("Alternate track found, use " + name + ".buffered to schedule main fragment loading"); 20651 this.mediaBuffer = mediaTrack.buffer; 20652 } else { 20653 this.mediaBuffer = this.media; 20654 } 20655 }; 20656 _proto.onFragBuffered = function onFragBuffered(event, data) { 20657 var frag = data.frag, 20658 part = data.part; 20659 if (frag && frag.type !== PlaylistLevelType.MAIN) { 20660 return; 20661 } 20662 if (this.fragContextChanged(frag)) { 20663 // If a level switch was requested while a fragment was buffering, it will emit the FRAG_BUFFERED event upon completion 20664 // Avoid setting state back to IDLE, since that will interfere with a level switch 20665 this.warn("Fragment " + frag.sn + (part ? ' p: ' + part.index : '') + " of level " + frag.level + " finished buffering, but was aborted. state: " + this.state); 20666 if (this.state === State.PARSED) { 20667 this.state = State.IDLE; 20668 } 20669 return; 20670 } 20671 var stats = part ? part.stats : frag.stats;
20672 this.fragLastKbps = Math.round(8 * stats.total / (stats.buffering.end - stats.loading.first)); 20673 if (frag.sn !== 'initSegment') { 20674 this.fragPrevious = frag; 20675 } 20676 this.fragBufferedComplete(frag, part); 20677 }; 20678 _proto.onError = function onError(event, data) { 20679 var _data$context; 20680 if (data.fatal) { 20681 this.state = State.ERROR; 20682 return; 20683 } 20684 switch (data.details) { 20685 case ErrorDetails.FRAG_GAP: 20686 case ErrorDetails.FRAG_PARSING_ERROR: 20687 case ErrorDetails.FRAG_DECRYPT_ERROR: 20688 case ErrorDetails.FRAG_LOAD_ERROR: 20689 case ErrorDetails.FRAG_LOAD_TIMEOUT: 20690 case ErrorDetails.KEY_LOAD_ERROR: 20691 case ErrorDetails.KEY_LOAD_TIMEOUT: 20692 this.onFragmentOrKeyLoadError(PlaylistLevelType.MAIN, data); 20693 break; 20694 case ErrorDetails.LEVEL_LOAD_ERROR: 20695 case ErrorDetails.LEVEL_LOAD_TIMEOUT: 20696 case ErrorDetails.LEVEL_PARSING_ERROR: 20697 // in case of non fatal error while loading level, if level controller is not retrying to load level, switch back to IDLE 20698 if (!data.levelRetry && this.state === State.WAITING_LEVEL && ((_data$context = data.context) == null ? void 0 : _data$context.type) === PlaylistContextType.LEVEL) { 20699 this.state = State.IDLE; 20700 } 20701 break; 20702 case ErrorDetails.BUFFER_APPEND_ERROR: 20703 case ErrorDetails.BUFFER_FULL_ERROR: 20704 if (!data.parent || data.parent !== 'main') { 20705 return; 20706 } 20707 if (data.details === ErrorDetails.BUFFER_APPEND_ERROR) { 20708 this.resetLoadingState(); 20709 return; 20710 } 20711 if (this.reduceLengthAndFlushBuffer(data)) { 20712 this.flushMainBuffer(0, Number.POSITIVE_INFINITY); 20713 } 20714 break; 20715 case ErrorDetails.INTERNAL_EXCEPTION: 20716 this.recoverWorkerError(data); 20717 break; 20718 } 20719 } 20720 20721 // Checks the health of the buffer and attempts to resolve playback stalls. 20722 ; 20723 _proto.checkBuffer = function checkBuffer() { 20724 var media = this.media, 20725 gapController = this.gapController; 20726 if (!media || !gapController || !media.readyState) { 20727 // Exit early if we don't have media or if the media hasn't buffered anything yet (readyState 0) 20728 return; 20729 } 20730 if (this.loadedmetadata || !BufferHelper.getBuffered(media).length) { 20731 // Resolve gaps using the main buffer, whose ranges are the intersections of the A/V sourcebuffers 20732 var state = this.state; 20733 var activeFrag = state !== State.IDLE ? this.fragCurrent : null; 20734 var levelDetails = this.getLevelDetails(); 20735 gapController.poll(this.lastCurrentTime, activeFrag, levelDetails, state); 20736 } 20737 this.lastCurrentTime = media.currentTime; 20738 }; 20739 _proto.onFragLoadEmergencyAborted = function onFragLoadEmergencyAborted() { 20740 this.state = State.IDLE; 20741 // if loadedmetadata is not set, it means that we are emergency switch down on first frag 20742 // in that case, reset startFragRequested flag 20743 if (!this.loadedmetadata) { 20744 this.startFragRequested = false; 20745 this.nextLoadPosition = this.lastCurrentTime; 20746 } 20747 this.tickImmediate(); 20748 }; 20749 _proto.onBufferFlushed = function onBufferFlushed(event, _ref) { 20750 var type = _ref.type; 20751 if (type !== ElementaryStreamTypes.AUDIO || this.audioOnly && !this.altAudio) { 20752 var mediaBuffer = (type === ElementaryStreamTypes.VIDEO ? this.videoBuffer : this.mediaBuffer) || this.media; 20753 this.afterBufferFlushed(mediaBuffer, type, PlaylistLevelType.MAIN); 20754 this.tick(); 20755 } 20756 }; 20757 _proto.onLevelsUpdated = function onLevelsUpdated(event, data) { 20758 if (this.level > -1 && this.fragCurrent) { 20759 this.level = this.fragCurrent.level; 20760 } 20761 this.levels = data.levels; 20762 }; 20763 _proto.swapAudioCodec = function swapAudioCodec() { 20764 this.audioCodecSwap = !this.audioCodecSwap; 20765 } 20766 20767 /** 20768 * Seeks to the set startPosition if not equal to the mediaElement's current time. 20769 */; 20770 _proto.seekToStartPos = function seekToStartPos() { 20771 var media = this.media; 20772 if (!media) { 20773 return; 20774 } 20775 var currentTime = media.currentTime; 20776 var startPosition = this.startPosition; 20777 // only adjust currentTime if different from startPosition or if startPosition not buffered 20778 // at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered 20779 if (startPosition >= 0 && currentTime < startPosition) { 20780 if (media.seeking) { 20781 this.log("could not seek to " + startPosition + ", already seeking at " + currentTime); 20782 return; 20783 } 20784 var buffered = BufferHelper.getBuffered(media); 20785 var bufferStart = buffered.length ? buffered.start(0) : 0; 20786 var delta = bufferStart - startPosition; 20787 if (delta > 0 && (delta < this.config.maxBufferHole || delta < this.config.maxFragLookUpTolerance)) { 20788 this.log("adjusting start position by " + delta + " to match buffer start"); 20789 startPosition += delta; 20790 this.startPosition = startPosition; 20791 } 20792 this.log("seek to target start position " + startPosition + " from current time " + currentTime); 20793 media.currentTime = startPosition; 20794 } 20795 }; 20796 _proto._getAudioCodec = function _getAudioCodec(currentLevel) { 20797 var audioCodec = this.config.defaultAudioCodec || currentLevel.audioCodec; 20798 if (this.audioCodecSwap && audioCodec) { 20799 this.log('Swapping audio codec'); 20800 if (audioCodec.indexOf('mp4a.40.5') !== -1) { 20801 audioCodec = 'mp4a.40.2'; 20802 } else { 20803 audioCodec = 'mp4a.40.5'; 20804 } 20805 } 20806 return audioCodec; 20807 }; 20808 _proto._loadBitrateTestFrag = function _loadBitrateTestFrag(frag, level) { 20809 var _this2 = this; 20810 frag.bitrateTest = true; 20811 this._doFragLoad(frag, level).then(function (data) { 20812 var hls = _this2.hls; 20813 if (!data || _this2.fragContextChanged(frag)) { 20814 return; 20815 } 20816 level.fragmentError = 0; 20817 _this2.state = State.IDLE; 20818 _this2.startFragRequested = false;
vendor: 20,019 bytes, lines 20819-21283
20819 _this2.bitrateTest = false; 20820 var stats = frag.stats; 20821 // Bitrate tests fragments are neither parsed nor buffered 20822 stats.parsing.start = stats.parsing.end = stats.buffering.start = stats.buffering.end = self.performance.now(); 20823 hls.trigger(Events.FRAG_LOADED, data); 20824 frag.bitrateTest = false; 20825 }); 20826 }; 20827 _proto._handleTransmuxComplete = function _handleTransmuxComplete(transmuxResult) { 20828 var _id3$samples; 20829 var id = this.playlistType; 20830 var hls = this.hls; 20831 var remuxResult = transmuxResult.remuxResult, 20832 chunkMeta = transmuxResult.chunkMeta; 20833 var context = this.getCurrentContext(chunkMeta); 20834 if (!context) { 20835 this.resetWhenMissingContext(chunkMeta); 20836 return; 20837 } 20838 var frag = context.frag, 20839 part = context.part, 20840 level = context.level; 20841 var video = remuxResult.video, 20842 text = remuxResult.text, 20843 id3 = remuxResult.id3, 20844 initSegment = remuxResult.initSegment; 20845 var details = level.details; 20846 // The audio-stream-controller handles audio buffering if Hls.js is playing an alternate audio track 20847 var audio = this.altAudio ? undefined : remuxResult.audio; 20848 20849 // Check if the current fragment has been aborted. We check this by first seeing if we're still playing the current level. 20850 // If we are, subsequently check if the currently loading fragment (fragCurrent) has changed. 20851 if (this.fragContextChanged(frag)) { 20852 this.fragmentTracker.removeFragment(frag); 20853 return; 20854 } 20855 this.state = State.PARSING; 20856 if (initSegment) { 20857 if (initSegment != null && initSegment.tracks) { 20858 var mapFragment = frag.initSegment || frag; 20859 this._bufferInitSegment(level, initSegment.tracks, mapFragment, chunkMeta); 20860 hls.trigger(Events.FRAG_PARSING_INIT_SEGMENT, { 20861 frag: mapFragment, 20862 id: id, 20863 tracks: initSegment.tracks 20864 }); 20865 } 20866 20867 // This would be nice if Number.isFinite acted as a typeguard, but it doesn't. See: https://github.com/Microsoft/TypeScript/issues/10038 20868 var initPTS = initSegment.initPTS; 20869 var timescale = initSegment.timescale; 20870 if (isFiniteNumber(initPTS)) { 20871 this.initPTS[frag.cc] = { 20872 baseTime: initPTS, 20873 timescale: timescale 20874 }; 20875 hls.trigger(Events.INIT_PTS_FOUND, { 20876 frag: frag, 20877 id: id, 20878 initPTS: initPTS, 20879 timescale: timescale 20880 }); 20881 } 20882 } 20883 20884 // Avoid buffering if backtracking this fragment 20885 if (video && details) { 20886 var prevFrag = details.fragments[frag.sn - 1 - details.startSN]; 20887 var isFirstFragment = frag.sn === details.startSN; 20888 var isFirstInDiscontinuity = !prevFrag || frag.cc > prevFrag.cc; 20889 if (remuxResult.independent !== false) { 20890 var startPTS = video.startPTS, 20891 endPTS = video.endPTS, 20892 startDTS = video.startDTS, 20893 endDTS = video.endDTS; 20894 if (part) { 20895 part.elementaryStreams[video.type] = { 20896 startPTS: startPTS, 20897 endPTS: endPTS, 20898 startDTS: startDTS, 20899 endDTS: endDTS 20900 }; 20901 } else { 20902 if (video.firstKeyFrame && video.independent && chunkMeta.id === 1 && !isFirstInDiscontinuity) { 20903 this.couldBacktrack = true; 20904 } 20905 if (video.dropped && video.independent) { 20906 // Backtrack if dropped frames create a gap after currentTime 20907 20908 var bufferInfo = this.getMainFwdBufferInfo(); 20909 var targetBufferTime = (bufferInfo ? bufferInfo.end : this.getLoadPosition()) + this.config.maxBufferHole; 20910 var startTime = video.firstKeyFramePTS ? video.firstKeyFramePTS : startPTS; 20911 if (!isFirstFragment && targetBufferTime < startTime - this.config.maxBufferHole && !isFirstInDiscontinuity) { 20912 this.backtrack(frag); 20913 return; 20914 } else if (isFirstInDiscontinuity) { 20915 // Mark segment with a gap to avoid loop loading 20916 frag.gap = true; 20917 } 20918 // Set video stream start to fragment start so that truncated samples do not distort the timeline, and mark it partial 20919 frag.setElementaryStreamInfo(video.type, frag.start, endPTS, frag.start, endDTS, true); 20920 } else if (isFirstFragment && startPTS > MAX_START_GAP_JUMP) { 20921 // Mark segment with a gap to skip large start gap 20922 frag.gap = true; 20923 } 20924 } 20925 frag.setElementaryStreamInfo(video.type, startPTS, endPTS, startDTS, endDTS); 20926 if (this.backtrackFragment) { 20927 this.backtrackFragment = frag; 20928 } 20929 this.bufferFragmentData(video, frag, part, chunkMeta, isFirstFragment || isFirstInDiscontinuity); 20930 } else if (isFirstFragment || isFirstInDiscontinuity) { 20931 // Mark segment with a gap to avoid loop loading 20932 frag.gap = true; 20933 } else { 20934 this.backtrack(frag); 20935 return; 20936 } 20937 } 20938 if (audio) { 20939 var _startPTS = audio.startPTS, 20940 _endPTS = audio.endPTS, 20941 _startDTS = audio.startDTS, 20942 _endDTS = audio.endDTS; 20943 if (part) { 20944 part.elementaryStreams[ElementaryStreamTypes.AUDIO] = { 20945 startPTS: _startPTS, 20946 endPTS: _endPTS, 20947 startDTS: _startDTS, 20948 endDTS: _endDTS 20949 }; 20950 } 20951 frag.setElementaryStreamInfo(ElementaryStreamTypes.AUDIO, _startPTS, _endPTS, _startDTS, _endDTS); 20952 this.bufferFragmentData(audio, frag, part, chunkMeta); 20953 } 20954 if (details && id3 != null && (_id3$samples = id3.samples) != null && _id3$samples.length) { 20955 var emittedID3 = { 20956 id: id, 20957 frag: frag, 20958 details: details, 20959 samples: id3.samples 20960 }; 20961 hls.trigger(Events.FRAG_PARSING_METADATA, emittedID3); 20962 } 20963 if (details && text) { 20964 var emittedText = { 20965 id: id, 20966 frag: frag, 20967 details: details, 20968 samples: text.samples 20969 }; 20970 hls.trigger(Events.FRAG_PARSING_USERDATA, emittedText); 20971 } 20972 }; 20973 _proto._bufferInitSegment = function _bufferInitSegment(currentLevel, tracks, frag, chunkMeta) { 20974 var _this3 = this; 20975 if (this.state !== State.PARSING) { 20976 return; 20977 } 20978 this.audioOnly = !!tracks.audio && !tracks.video; 20979 20980 // if audio track is expected to come from audio stream controller, discard any coming from main 20981 if (this.altAudio && !this.audioOnly) { 20982 delete tracks.audio; 20983 } 20984 // include levelCodec in audio and video tracks 20985 var audio = tracks.audio, 20986 video = tracks.video, 20987 audiovideo = tracks.audiovideo; 20988 if (audio) { 20989 var audioCodec = currentLevel.audioCodec; 20990 var ua = navigator.userAgent.toLowerCase(); 20991 if (this.audioCodecSwitch) { 20992 if (audioCodec) { 20993 if (audioCodec.indexOf('mp4a.40.5') !== -1) { 20994 audioCodec = 'mp4a.40.2'; 20995 } else { 20996 audioCodec = 'mp4a.40.5'; 20997 } 20998 } 20999 // In the case that AAC and HE-AAC audio codecs are signalled in manifest, 21000 // force HE-AAC, as it seems that most browsers prefers it. 21001 // don't force HE-AAC if mono stream, or in Firefox 21002 var audioMetadata = audio.metadata; 21003 if (audioMetadata && 'channelCount' in audioMetadata && (audioMetadata.channelCount || 1) !== 1 && ua.indexOf('firefox') === -1) { 21004 audioCodec = 'mp4a.40.5'; 21005 } 21006 } 21007 // HE-AAC is broken on Android, always signal audio codec as AAC even if variant manifest states otherwise 21008 if (audioCodec && audioCodec.indexOf('mp4a.40.5') !== -1 && ua.indexOf('android') !== -1 && audio.container !== 'audio/mpeg') { 21009 // Exclude mpeg audio 21010 audioCodec = 'mp4a.40.2'; 21011 this.log("Android: force audio codec to " + audioCodec); 21012 } 21013 if (currentLevel.audioCodec && currentLevel.audioCodec !== audioCodec) { 21014 this.log("Swapping manifest audio codec \"" + currentLevel.audioCodec + "\" for \"" + audioCodec + "\""); 21015 } 21016 audio.levelCodec = audioCodec; 21017 audio.id = 'main'; 21018 this.log("Init audio buffer, container:" + audio.container + ", codecs[selected/level/parsed]=[" + (audioCodec || '') + "/" + (currentLevel.audioCodec || '') + "/" + audio.codec + "]"); 21019 delete tracks.audiovideo; 21020 } 21021 if (video) { 21022 video.levelCodec = currentLevel.videoCodec; 21023 video.id = 'main'; 21024 this.log("Init video buffer, container:" + video.container + ", codecs[level/parsed]=[" + (currentLevel.videoCodec || '') + "/" + video.codec + "]"); 21025 delete tracks.audiovideo; 21026 } 21027 if (audiovideo) { 21028 this.log("Init audiovideo buffer, container:" + audiovideo.container + ", codecs[level/parsed]=[" + currentLevel.codecs + "/" + audiovideo.codec + "]"); 21029 delete tracks.video; 21030 delete tracks.audio; 21031 } 21032 var trackTypes = Object.keys(tracks); 21033 if (trackTypes.length) { 21034 this.hls.trigger(Events.BUFFER_CODECS, tracks); 21035 // loop through tracks that are going to be provided to bufferController 21036 trackTypes.forEach(function (trackName) { 21037 var track = tracks[trackName]; 21038 var initSegment = track.initSegment; 21039 if (initSegment != null && initSegment.byteLength) { 21040 _this3.hls.trigger(Events.BUFFER_APPENDING, { 21041 type: trackName, 21042 data: initSegment, 21043 frag: frag, 21044 part: null, 21045 chunkMeta: chunkMeta, 21046 parent: frag.type 21047 }); 21048 } 21049 }); 21050 } 21051 // trigger handler right now 21052 this.tickImmediate(); 21053 }; 21054 _proto.getMainFwdBufferInfo = function getMainFwdBufferInfo() { 21055 return this.getFwdBufferInfo(this.mediaBuffer ? this.mediaBuffer : this.media, PlaylistLevelType.MAIN); 21056 }; 21057 _proto.backtrack = function backtrack(frag) { 21058 this.couldBacktrack = true; 21059 // Causes findFragments to backtrack through fragments to find the keyframe 21060 this.backtrackFragment = frag; 21061 this.resetTransmuxer(); 21062 this.flushBufferGap(frag); 21063 this.fragmentTracker.removeFragment(frag); 21064 this.fragPrevious = null; 21065 this.nextLoadPosition = frag.start; 21066 this.state = State.IDLE; 21067 }; 21068 _proto.checkFragmentChanged = function checkFragmentChanged() { 21069 var video = this.media; 21070 var fragPlayingCurrent = null; 21071 if (video && video.readyState > 1 && video.seeking === false) { 21072 var currentTime = video.currentTime; 21073 /* if video element is in seeked state, currentTime can only increase. 21074 (assuming that playback rate is positive ...) 21075 As sometimes currentTime jumps back to zero after a 21076 media decode error, check this, to avoid seeking back to 21077 wrong position after a media decode error 21078 */ 21079 21080 if (BufferHelper.isBuffered(video, currentTime)) { 21081 fragPlayingCurrent = this.getAppendedFrag(currentTime); 21082 } else if (BufferHelper.isBuffered(video, currentTime + 0.1)) { 21083 /* ensure that FRAG_CHANGED event is triggered at startup, 21084 when first video frame is displayed and playback is paused. 21085 add a tolerance of 100ms, in case current position is not buffered, 21086 check if current pos+100ms is buffered and use that buffer range 21087 for FRAG_CHANGED event reporting */ 21088 fragPlayingCurrent = this.getAppendedFrag(currentTime + 0.1); 21089 } 21090 if (fragPlayingCurrent) { 21091 this.backtrackFragment = null; 21092 var fragPlaying = this.fragPlaying; 21093 var fragCurrentLevel = fragPlayingCurrent.level; 21094 if (!fragPlaying || fragPlayingCurrent.sn !== fragPlaying.sn || fragPlaying.level !== fragCurrentLevel) { 21095 this.fragPlaying = fragPlayingCurrent; 21096 this.hls.trigger(Events.FRAG_CHANGED, { 21097 frag: fragPlayingCurrent 21098 }); 21099 if (!fragPlaying || fragPlaying.level !== fragCurrentLevel) { 21100 this.hls.trigger(Events.LEVEL_SWITCHED, { 21101 level: fragCurrentLevel 21102 }); 21103 } 21104 } 21105 } 21106 } 21107 }; 21108 return _createClass(StreamController, [{ 21109 key: "maxBufferLength", 21110 get: function get() { 21111 var levels = this.levels, 21112 level = this.level; 21113 var levelInfo = levels == null ? void 0 : levels[level]; 21114 if (!levelInfo) { 21115 return this.config.maxBufferLength; 21116 } 21117 return this.getMaxBufferLength(levelInfo.maxBitrate); 21118 } 21119 }, { 21120 key: "nextLevel", 21121 get: function get() { 21122 var frag = this.nextBufferedFrag; 21123 if (frag) { 21124 return frag.level; 21125 } 21126 return -1; 21127 } 21128 }, { 21129 key: "currentFrag", 21130 get: function get() { 21131 var _this$media2; 21132 if (this.fragPlaying) { 21133 return this.fragPlaying; 21134 } 21135 var currentTime = ((_this$media2 = this.media) == null ? void 0 : _this$media2.currentTime) || this.lastCurrentTime; 21136 if (isFiniteNumber(currentTime)) { 21137 return this.getAppendedFrag(currentTime); 21138 } 21139 return null; 21140 } 21141 }, { 21142 key: "currentProgramDateTime", 21143 get: function get() { 21144 var _this$media3; 21145 var currentTime = ((_this$media3 = this.media) == null ? void 0 : _this$media3.currentTime) || this.lastCurrentTime; 21146 if (isFiniteNumber(currentTime)) { 21147 var details = this.getLevelDetails(); 21148 var frag = this.currentFrag || (details ? findFragmentByPTS(null, details.fragments, currentTime) : null); 21149 if (frag) { 21150 var programDateTime = frag.programDateTime; 21151 if (programDateTime !== null) { 21152 var epocMs = programDateTime + (currentTime - frag.start) * 1000; 21153 return new Date(epocMs); 21154 } 21155 } 21156 } 21157 return null; 21158 } 21159 }, { 21160 key: "currentLevel", 21161 get: function get() { 21162 var frag = this.currentFrag; 21163 if (frag) { 21164 return frag.level; 21165 } 21166 return -1; 21167 } 21168 }, { 21169 key: "nextBufferedFrag", 21170 get: function get() { 21171 var frag = this.currentFrag; 21172 if (frag) { 21173 return this.followingBufferedFrag(frag); 21174 } 21175 return null; 21176 } 21177 }, { 21178 key: "forceStartLoad", 21179 get: function get() { 21180 return this._forceStartLoad; 21181 } 21182 }]); 21183 }(BaseStreamController); 21184 21185 /** 21186 * The `Hls` class is the core of the HLS.js library used to instantiate player instances. 21187 * @public 21188 */ 21189 var Hls = /*#__PURE__*/function () { 21190 /** 21191 * Creates an instance of an HLS client that can attach to exactly one `HTMLMediaElement`. 21192 * @param userConfig - Configuration options applied over `Hls.DefaultConfig` 21193 */ 21194 function Hls(userConfig) { 21195 if (userConfig === void 0) { 21196 userConfig = {}; 21197 } 21198 /** 21199 * The runtime configuration used by the player. At instantiation this is combination of `hls.userConfig` merged over `Hls.DefaultConfig`. 21200 */ 21201 this.config = void 0; 21202 /** 21203 * The configuration object provided on player instantiation. 21204 */ 21205 this.userConfig = void 0; 21206 /** 21207 * The logger functions used by this player instance, configured on player instantiation. 21208 */ 21209 this.logger = void 0; 21210 this.coreComponents = void 0; 21211 this.networkControllers = void 0; 21212 this._emitter = new EventEmitter(); 21213 this._autoLevelCapping = -1; 21214 this._maxHdcpLevel = null; 21215 this.abrController = void 0; 21216 this.bufferController = void 0; 21217 this.capLevelController = void 0; 21218 this.latencyController = void 0; 21219 this.levelController = void 0; 21220 this.streamController = void 0; 21221 this.audioTrackController = void 0; 21222 this.subtitleTrackController = void 0; 21223 this.emeController = void 0; 21224 this.cmcdController = void 0; 21225 this._media = null; 21226 this._url = null; 21227 this.triggeringException = void 0; 21228 var logger = this.logger = enableLogs(userConfig.debug || false, 'Hls instance'); 21229 var config = this.config = mergeConfig(Hls.DefaultConfig, userConfig, logger); 21230 this.userConfig = userConfig; 21231 if (config.progressive) { 21232 enableStreamingMode(config, logger); 21233 } 21234 21235 // core controllers and network loaders 21236 var ConfigAbrController = config.abrController, 21237 ConfigBufferController = config.bufferController, 21238 ConfigCapLevelController = config.capLevelController, 21239 ConfigErrorController = config.errorController, 21240 ConfigFpsController = config.fpsController; 21241 var errorController = new ConfigErrorController(this); 21242 var abrController = this.abrController = new ConfigAbrController(this); 21243 // FragmentTracker must be defined before StreamController because the order of event handling is important 21244 var fragmentTracker = new FragmentTracker(this); 21245 var bufferController = this.bufferController = new ConfigBufferController(this, fragmentTracker); 21246 var capLevelController = this.capLevelController = new ConfigCapLevelController(this); 21247 var fpsController = new ConfigFpsController(this); 21248 var playListLoader = new PlaylistLoader(this); 21249 var id3TrackController = new ID3TrackController(this); 21250 var ConfigContentSteeringController = config.contentSteeringController; 21251 // ContentSteeringController is defined before LevelController to receive Multivariant Playlist events first 21252 var contentSteering = ConfigContentSteeringController ? new ConfigContentSteeringController(this) : null; 21253 var levelController = this.levelController = new LevelController(this, contentSteering); 21254 var keyLoader = new KeyLoader(this.config); 21255 var streamController = this.streamController = new StreamController(this, fragmentTracker, keyLoader); 21256 21257 // Cap level controller uses streamController to flush the buffer 21258 capLevelController.setStreamController(streamController); 21259 // fpsController uses streamController to switch when frames are being dropped 21260 fpsController.setStreamController(streamController); 21261 var networkControllers = [playListLoader, levelController, streamController]; 21262 if (contentSteering) { 21263 networkControllers.splice(1, 0, contentSteering); 21264 } 21265 this.networkControllers = networkControllers; 21266 var coreComponents = [abrController, bufferController, capLevelController, fpsController, id3TrackController, fragmentTracker]; 21267 this.audioTrackController = this.createController(config.audioTrackController, networkControllers); 21268 var AudioStreamControllerClass = config.audioStreamController; 21269 if (AudioStreamControllerClass) { 21270 networkControllers.push(new AudioStreamControllerClass(this, fragmentTracker, keyLoader)); 21271 } 21272 // Instantiate subtitleTrackController before SubtitleStreamController to receive level events first 21273 this.subtitleTrackController = this.createController(config.subtitleTrackController, networkControllers); 21274 var SubtitleStreamControllerClass = config.subtitleStreamController; 21275 if (SubtitleStreamControllerClass) { 21276 networkControllers.push(new SubtitleStreamControllerClass(this, fragmentTracker, keyLoader)); 21277 } 21278 this.createController(config.timelineController, coreComponents); 21279 keyLoader.emeController = this.emeController = this.createController(config.emeController, coreComponents); 21280 this.cmcdController = this.createController(config.cmcdController, coreComponents); 21281 this.latencyController = this.createController(LatencyController, coreComponents); 21282 this.coreComponents = coreComponents; 21283
vendor: 4,720 bytes, lines 21284-21422
21284 // Error controller handles errors before and after all other controllers 21285 // This listener will be invoked after all other controllers error listeners 21286 networkControllers.push(errorController); 21287 var onErrorOut = errorController.onErrorOut; 21288 if (typeof onErrorOut === 'function') { 21289 this.on(Events.ERROR, onErrorOut, errorController); 21290 } 21291 } 21292 /** 21293 * Check if the required MediaSource Extensions are available. 21294 */ 21295 Hls.isMSESupported = function isMSESupported$1() { 21296 return isMSESupported(); 21297 } 21298 21299 /** 21300 * Check if MediaSource Extensions are available and isTypeSupported checks pass for any baseline codecs. 21301 */; 21302 Hls.isSupported = function isSupported$1() { 21303 return isSupported(); 21304 } 21305 21306 /** 21307 * Get the MediaSource global used for MSE playback (ManagedMediaSource, MediaSource, or WebKitMediaSource). 21308 */; 21309 Hls.getMediaSource = function getMediaSource$1() { 21310 return getMediaSource(); 21311 }; 21312 var _proto = Hls.prototype; 21313 _proto.createController = function createController(ControllerClass, components) { 21314 if (ControllerClass) { 21315 var controllerInstance = new ControllerClass(this); 21316 if (components) { 21317 components.push(controllerInstance); 21318 } 21319 return controllerInstance; 21320 } 21321 return null; 21322 } 21323 21324 // Delegate the EventEmitter through the public API of Hls.js 21325 ; 21326 _proto.on = function on(event, listener, context) { 21327 if (context === void 0) { 21328 context = this; 21329 } 21330 this._emitter.on(event, listener, context); 21331 }; 21332 _proto.once = function once(event, listener, context) { 21333 if (context === void 0) { 21334 context = this; 21335 } 21336 this._emitter.once(event, listener, context); 21337 }; 21338 _proto.removeAllListeners = function removeAllListeners(event) { 21339 this._emitter.removeAllListeners(event); 21340 }; 21341 _proto.off = function off(event, listener, context, once) { 21342 if (context === void 0) { 21343 context = this; 21344 } 21345 this._emitter.off(event, listener, context, once); 21346 }; 21347 _proto.listeners = function listeners(event) { 21348 return this._emitter.listeners(event); 21349 }; 21350 _proto.emit = function emit(event, name, eventObject) { 21351 return this._emitter.emit(event, name, eventObject); 21352 }; 21353 _proto.trigger = function trigger(event, eventObject) { 21354 if (this.config.debug) { 21355 return this.emit(event, event, eventObject); 21356 } else { 21357 try { 21358 return this.emit(event, event, eventObject); 21359 } catch (error) { 21360 this.logger.error('An internal error happened while handling event ' + event + '. Error message: "' + error.message + '". Here is a stacktrace:', error); 21361 // Prevent recursion in error event handlers that throw #5497 21362 if (!this.triggeringException) { 21363 this.triggeringException = true; 21364 var fatal = event === Events.ERROR; 21365 this.trigger(Events.ERROR, { 21366 type: ErrorTypes.OTHER_ERROR, 21367 details: ErrorDetails.INTERNAL_EXCEPTION, 21368 fatal: fatal, 21369 event: event, 21370 error: error 21371 }); 21372 this.triggeringException = false; 21373 } 21374 } 21375 } 21376 return false; 21377 }; 21378 _proto.listenerCount = function listenerCount(event) { 21379 return this._emitter.listenerCount(event); 21380 } 21381 21382 /** 21383 * Dispose of the instance 21384 */; 21385 _proto.destroy = function destroy() { 21386 this.logger.log('destroy'); 21387 this.trigger(Events.DESTROYING, undefined); 21388 this.detachMedia(); 21389 this.removeAllListeners(); 21390 this._autoLevelCapping = -1; 21391 this._url = null; 21392 this.networkControllers.forEach(function (component) { 21393 return component.destroy(); 21394 }); 21395 this.networkControllers.length = 0; 21396 this.coreComponents.forEach(function (component) { 21397 return component.destroy(); 21398 }); 21399 this.coreComponents.length = 0; 21400 // Remove any references that could be held in config options or callbacks 21401 var config = this.config; 21402 config.xhrSetup = config.fetchSetup = undefined; 21403 // @ts-ignore 21404 this.userConfig = null; 21405 } 21406 21407 /** 21408 * Attaches Hls.js to a media element 21409 */; 21410 _proto.attachMedia = function attachMedia(media) { 21411 if (!media) { 21412 var error = new Error("attachMedia failed: media argument is " + media); 21413 this.trigger(Events.ERROR, { 21414 type: ErrorTypes.OTHER_ERROR, 21415 details: ErrorDetails.ATTACH_MEDIA_ERROR, 21416 fatal: true, 21417 error: error 21418 }); 21419 return; 21420 } 21421 this.logger.log('attachMedia'); 21422 this._media = media;
vendor: 5,025 bytes, lines 21423-21567
21423 this.trigger(Events.MEDIA_ATTACHING, { 21424 media: media 21425 }); 21426 } 21427 21428 /** 21429 * Detach Hls.js from the media 21430 */; 21431 _proto.detachMedia = function detachMedia() { 21432 this.logger.log('detachMedia'); 21433 this.trigger(Events.MEDIA_DETACHING, undefined); 21434 this._media = null; 21435 } 21436 21437 /** 21438 * Set the source URL. Can be relative or absolute. 21439 */; 21440 _proto.loadSource = function loadSource(url) { 21441 this.stopLoad(); 21442 var media = this.media; 21443 var loadedSource = this._url; 21444 var loadingSource = this._url = urlToolkitExports.buildAbsoluteURL(self.location.href, url, { 21445 alwaysNormalize: true 21446 }); 21447 this._autoLevelCapping = -1; 21448 this._maxHdcpLevel = null; 21449 this.logger.log("loadSource:" + loadingSource); 21450 if (media && loadedSource && (loadedSource !== loadingSource || this.bufferController.hasSourceTypes())) { 21451 this.detachMedia(); 21452 this.attachMedia(media); 21453 } 21454 // when attaching to a source URL, trigger a playlist load 21455 this.trigger(Events.MANIFEST_LOADING, { 21456 url: url 21457 }); 21458 } 21459 21460 /** 21461 * Gets the currently loaded URL 21462 */; 21463 /** 21464 * Start loading data from the stream source. 21465 * Depending on default config, client starts loading automatically when a source is set. 21466 * 21467 * @param startPosition - Set the start position to stream from. 21468 * Defaults to -1 (None: starts from earliest point) 21469 */ 21470 _proto.startLoad = function startLoad(startPosition) { 21471 if (startPosition === void 0) { 21472 startPosition = -1; 21473 } 21474 this.logger.log("startLoad(" + startPosition + ")"); 21475 this.networkControllers.forEach(function (controller) { 21476 controller.startLoad(startPosition); 21477 }); 21478 } 21479 21480 /** 21481 * Stop loading of any stream data. 21482 */; 21483 _proto.stopLoad = function stopLoad() { 21484 this.logger.log('stopLoad'); 21485 this.networkControllers.forEach(function (controller) { 21486 controller.stopLoad(); 21487 }); 21488 } 21489 21490 /** 21491 * Resumes stream controller segment loading after `pauseBuffering` has been called. 21492 */; 21493 _proto.resumeBuffering = function resumeBuffering() { 21494 this.networkControllers.forEach(function (controller) { 21495 if (controller.resumeBuffering) { 21496 controller.resumeBuffering(); 21497 } 21498 }); 21499 } 21500 21501 /** 21502 * Prevents stream controller from loading new segments until `resumeBuffering` is called. 21503 * This allows for media buffering to be paused without interupting playlist loading. 21504 */; 21505 _proto.pauseBuffering = function pauseBuffering() { 21506 this.networkControllers.forEach(function (controller) { 21507 if (controller.pauseBuffering) { 21508 controller.pauseBuffering(); 21509 } 21510 }); 21511 } 21512 21513 /** 21514 * Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1) 21515 */; 21516 _proto.swapAudioCodec = function swapAudioCodec() { 21517 this.logger.log('swapAudioCodec'); 21518 this.streamController.swapAudioCodec(); 21519 } 21520 21521 /** 21522 * When the media-element fails, this allows to detach and then re-attach it 21523 * as one call (convenience method). 21524 * 21525 * Automatic recovery of media-errors by this process is configurable. 21526 */; 21527 _proto.recoverMediaError = function recoverMediaError() { 21528 this.logger.log('recoverMediaError'); 21529 var media = this._media; 21530 this.detachMedia(); 21531 if (media) { 21532 this.attachMedia(media); 21533 } 21534 }; 21535 _proto.removeLevel = function removeLevel(levelIndex) { 21536 this.levelController.removeLevel(levelIndex); 21537 } 21538 21539 /** 21540 * @returns an array of levels (variants) sorted by HDCP-LEVEL, RESOLUTION (height), FRAME-RATE, CODECS, VIDEO-RANGE, and BANDWIDTH 21541 */; 21542 /** 21543 * Find and select the best matching audio track, making a level switch when a Group change is necessary. 21544 * Updates `hls.config.audioPreference`. Returns the selected track, or null when no matching track is found. 21545 */ 21546 _proto.setAudioOption = function setAudioOption(audioOption) { 21547 var _this$audioTrackContr; 21548 return ((_this$audioTrackContr = this.audioTrackController) == null ? void 0 : _this$audioTrackContr.setAudioOption(audioOption)) || null; 21549 } 21550 /** 21551 * Find and select the best matching subtitle track, making a level switch when a Group change is necessary. 21552 * Updates `hls.config.subtitlePreference`. Returns the selected track, or null when no matching track is found. 21553 */; 21554 _proto.setSubtitleOption = function setSubtitleOption(subtitleOption) { 21555 var _this$subtitleTrackCo; 21556 return ((_this$subtitleTrackCo = this.subtitleTrackController) == null ? void 0 : _this$subtitleTrackCo.setSubtitleOption(subtitleOption)) || null; 21557 } 21558 21559 /** 21560 * Get the complete list of audio tracks across all media groups 21561 */; 21562 return _createClass(Hls, [{ 21563 key: "url", 21564 get: function get() { 21565 return this._url; 21566 } 21567 }, {
vendor: 4,822 bytes, lines 21568-21703
21568 key: "levels", 21569 get: function get() { 21570 var levels = this.levelController.levels; 21571 return levels ? levels : []; 21572 } 21573 21574 /** 21575 * Index of quality level (variant) currently played 21576 */ 21577 }, { 21578 key: "currentLevel", 21579 get: function get() { 21580 return this.streamController.currentLevel; 21581 } 21582 21583 /** 21584 * Set quality level index immediately. This will flush the current buffer to replace the quality asap. That means playback will interrupt at least shortly to re-buffer and re-sync eventually. Set to -1 for automatic level selection. 21585 */, 21586 set: function set(newLevel) { 21587 this.logger.log("set currentLevel:" + newLevel); 21588 this.levelController.manualLevel = newLevel; 21589 this.streamController.immediateLevelSwitch(); 21590 } 21591 21592 /** 21593 * Index of next quality level loaded as scheduled by stream controller. 21594 */ 21595 }, { 21596 key: "nextLevel", 21597 get: function get() { 21598 return this.streamController.nextLevel; 21599 } 21600 21601 /** 21602 * Set quality level index for next loaded data. 21603 * This will switch the video quality asap, without interrupting playback. 21604 * May abort current loading of data, and flush parts of buffer (outside currently played fragment region). 21605 * @param newLevel - Pass -1 for automatic level selection 21606 */, 21607 set: function set(newLevel) { 21608 this.logger.log("set nextLevel:" + newLevel); 21609 this.levelController.manualLevel = newLevel; 21610 this.streamController.nextLevelSwitch(); 21611 } 21612 21613 /** 21614 * Return the quality level of the currently or last (of none is loaded currently) segment 21615 */ 21616 }, { 21617 key: "loadLevel", 21618 get: function get() { 21619 return this.levelController.level; 21620 } 21621 21622 /** 21623 * Set quality level index for next loaded data in a conservative way. 21624 * This will switch the quality without flushing, but interrupt current loading. 21625 * Thus the moment when the quality switch will appear in effect will only be after the already existing buffer. 21626 * @param newLevel - Pass -1 for automatic level selection 21627 */, 21628 set: function set(newLevel) { 21629 this.logger.log("set loadLevel:" + newLevel); 21630 this.levelController.manualLevel = newLevel; 21631 } 21632 21633 /** 21634 * get next quality level loaded 21635 */ 21636 }, { 21637 key: "nextLoadLevel", 21638 get: function get() { 21639 return this.levelController.nextLoadLevel; 21640 } 21641 21642 /** 21643 * Set quality level of next loaded segment in a fully "non-destructive" way. 21644 * Same as `loadLevel` but will wait for next switch (until current loading is done). 21645 */, 21646 set: function set(level) { 21647 this.levelController.nextLoadLevel = level; 21648 } 21649 21650 /** 21651 * Return "first level": like a default level, if not set, 21652 * falls back to index of first level referenced in manifest 21653 */ 21654 }, { 21655 key: "firstLevel", 21656 get: function get() { 21657 return Math.max(this.levelController.firstLevel, this.minAutoLevel); 21658 } 21659 21660 /** 21661 * Sets "first-level", see getter. 21662 */, 21663 set: function set(newLevel) { 21664 this.logger.log("set firstLevel:" + newLevel); 21665 this.levelController.firstLevel = newLevel; 21666 } 21667 21668 /** 21669 * Return the desired start level for the first fragment that will be loaded. 21670 * The default value of -1 indicates automatic start level selection. 21671 * Setting hls.nextAutoLevel without setting a startLevel will result in 21672 * the nextAutoLevel value being used for one fragment load. 21673 */ 21674 }, { 21675 key: "startLevel", 21676 get: function get() { 21677 var startLevel = this.levelController.startLevel; 21678 if (startLevel === -1 && this.abrController.forcedAutoLevel > -1) { 21679 return this.abrController.forcedAutoLevel; 21680 } 21681 return startLevel; 21682 } 21683 21684 /** 21685 * set start level (level of first fragment that will be played back) 21686 * if not overrided by user, first level appearing in manifest will be used as start level 21687 * if -1 : automatic start level selection, playback will start from level matching download bandwidth 21688 * (determined from download of first segment) 21689 */, 21690 set: function set(newLevel) { 21691 this.logger.log("set startLevel:" + newLevel); 21692 // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel 21693 if (newLevel !== -1) { 21694 newLevel = Math.max(newLevel, this.minAutoLevel); 21695 } 21696 this.levelController.startLevel = newLevel; 21697 } 21698 21699 /** 21700 * Whether level capping is enabled. 21701 * Default value is set via `config.capLevelToPlayerSize`. 21702 */ 21703 }, {
vendor: 4,391 bytes, lines 21704-21848
21704 key: "capLevelToPlayerSize", 21705 get: function get() { 21706 return this.config.capLevelToPlayerSize; 21707 } 21708 21709 /** 21710 * Enables or disables level capping. If disabled after previously enabled, `nextLevelSwitch` will be immediately called. 21711 */, 21712 set: function set(shouldStartCapping) { 21713 var newCapLevelToPlayerSize = !!shouldStartCapping; 21714 if (newCapLevelToPlayerSize !== this.config.capLevelToPlayerSize) { 21715 if (newCapLevelToPlayerSize) { 21716 this.capLevelController.startCapping(); // If capping occurs, nextLevelSwitch will happen based on size. 21717 } else { 21718 this.capLevelController.stopCapping(); 21719 this.autoLevelCapping = -1; 21720 this.streamController.nextLevelSwitch(); // Now we're uncapped, get the next level asap. 21721 } 21722 this.config.capLevelToPlayerSize = newCapLevelToPlayerSize; 21723 } 21724 } 21725 21726 /** 21727 * Capping/max level value that should be used by automatic level selection algorithm (`ABRController`) 21728 */ 21729 }, { 21730 key: "autoLevelCapping", 21731 get: function get() { 21732 return this._autoLevelCapping; 21733 } 21734 21735 /** 21736 * Returns the current bandwidth estimate in bits per second, when available. Otherwise, `NaN` is returned. 21737 */, 21738 set: 21739 /** 21740 * Capping/max level value that should be used by automatic level selection algorithm (`ABRController`) 21741 */ 21742 function set(newLevel) { 21743 if (this._autoLevelCapping !== newLevel) { 21744 this.logger.log("set autoLevelCapping:" + newLevel); 21745 this._autoLevelCapping = newLevel; 21746 this.levelController.checkMaxAutoUpdated(); 21747 } 21748 } 21749 }, { 21750 key: "bandwidthEstimate", 21751 get: function get() { 21752 var bwEstimator = this.abrController.bwEstimator; 21753 if (!bwEstimator) { 21754 return NaN; 21755 } 21756 return bwEstimator.getEstimate(); 21757 }, 21758 set: function set(abrEwmaDefaultEstimate) { 21759 this.abrController.resetEstimator(abrEwmaDefaultEstimate); 21760 } 21761 21762 /** 21763 * get time to first byte estimate 21764 * @type {number} 21765 */ 21766 }, { 21767 key: "ttfbEstimate", 21768 get: function get() { 21769 var bwEstimator = this.abrController.bwEstimator; 21770 if (!bwEstimator) { 21771 return NaN; 21772 } 21773 return bwEstimator.getEstimateTTFB(); 21774 } 21775 }, { 21776 key: "maxHdcpLevel", 21777 get: function get() { 21778 return this._maxHdcpLevel; 21779 }, 21780 set: function set(value) { 21781 if (isHdcpLevel(value) && this._maxHdcpLevel !== value) { 21782 this._maxHdcpLevel = value; 21783 this.levelController.checkMaxAutoUpdated(); 21784 } 21785 } 21786 21787 /** 21788 * True when automatic level selection enabled 21789 */ 21790 }, { 21791 key: "autoLevelEnabled", 21792 get: function get() { 21793 return this.levelController.manualLevel === -1; 21794 } 21795 21796 /** 21797 * Level set manually (if any) 21798 */ 21799 }, { 21800 key: "manualLevel", 21801 get: function get() { 21802 return this.levelController.manualLevel; 21803 } 21804 21805 /** 21806 * min level selectable in auto mode according to config.minAutoBitrate 21807 */ 21808 }, { 21809 key: "minAutoLevel", 21810 get: function get() { 21811 var levels = this.levels, 21812 minAutoBitrate = this.config.minAutoBitrate; 21813 if (!levels) return 0; 21814 var len = levels.length; 21815 for (var i = 0; i < len; i++) { 21816 if (levels[i].maxBitrate >= minAutoBitrate) { 21817 return i; 21818 } 21819 } 21820 return 0; 21821 } 21822 21823 /** 21824 * max level selectable in auto mode according to autoLevelCapping 21825 */ 21826 }, { 21827 key: "maxAutoLevel", 21828 get: function get() { 21829 var levels = this.levels, 21830 autoLevelCapping = this.autoLevelCapping, 21831 maxHdcpLevel = this.maxHdcpLevel; 21832 var maxAutoLevel; 21833 if (autoLevelCapping === -1 && levels != null && levels.length) { 21834 maxAutoLevel = levels.length - 1; 21835 } else { 21836 maxAutoLevel = autoLevelCapping; 21837 } 21838 if (maxHdcpLevel) { 21839 for (var i = maxAutoLevel; i--;) { 21840 var hdcpLevel = levels[i].attrs['HDCP-LEVEL']; 21841 if (hdcpLevel && hdcpLevel <= maxHdcpLevel) { 21842 return i; 21843 } 21844 } 21845 } 21846 return maxAutoLevel; 21847 } 21848 }, {
vendor: 8,114 bytes, lines 21849-22133
21849 key: "firstAutoLevel", 21850 get: function get() { 21851 return this.abrController.firstAutoLevel; 21852 } 21853 21854 /** 21855 * next automatically selected quality level 21856 */ 21857 }, { 21858 key: "nextAutoLevel", 21859 get: function get() { 21860 return this.abrController.nextAutoLevel; 21861 } 21862 21863 /** 21864 * this setter is used to force next auto level. 21865 * this is useful to force a switch down in auto mode: 21866 * in case of load error on level N, hls.js can set nextAutoLevel to N-1 for example) 21867 * forced value is valid for one fragment. upon successful frag loading at forced level, 21868 * this value will be resetted to -1 by ABR controller. 21869 */, 21870 set: function set(nextLevel) { 21871 this.abrController.nextAutoLevel = nextLevel; 21872 } 21873 21874 /** 21875 * get the datetime value relative to media.currentTime for the active level Program Date Time if present 21876 */ 21877 }, { 21878 key: "playingDate", 21879 get: function get() { 21880 return this.streamController.currentProgramDateTime; 21881 } 21882 }, { 21883 key: "mainForwardBufferInfo", 21884 get: function get() { 21885 return this.streamController.getMainFwdBufferInfo(); 21886 } 21887 }, { 21888 key: "maxBufferLength", 21889 get: function get() { 21890 return this.streamController.maxBufferLength; 21891 } 21892 }, { 21893 key: "allAudioTracks", 21894 get: function get() { 21895 var audioTrackController = this.audioTrackController; 21896 return audioTrackController ? audioTrackController.allAudioTracks : []; 21897 } 21898 21899 /** 21900 * Get the list of selectable audio tracks 21901 */ 21902 }, { 21903 key: "audioTracks", 21904 get: function get() { 21905 var audioTrackController = this.audioTrackController; 21906 return audioTrackController ? audioTrackController.audioTracks : []; 21907 } 21908 21909 /** 21910 * index of the selected audio track (index in audio track lists) 21911 */ 21912 }, { 21913 key: "audioTrack", 21914 get: function get() { 21915 var audioTrackController = this.audioTrackController; 21916 return audioTrackController ? audioTrackController.audioTrack : -1; 21917 } 21918 21919 /** 21920 * selects an audio track, based on its index in audio track lists 21921 */, 21922 set: function set(audioTrackId) { 21923 var audioTrackController = this.audioTrackController; 21924 if (audioTrackController) { 21925 audioTrackController.audioTrack = audioTrackId; 21926 } 21927 } 21928 21929 /** 21930 * get the complete list of subtitle tracks across all media groups 21931 */ 21932 }, { 21933 key: "allSubtitleTracks", 21934 get: function get() { 21935 var subtitleTrackController = this.subtitleTrackController; 21936 return subtitleTrackController ? subtitleTrackController.allSubtitleTracks : []; 21937 } 21938 21939 /** 21940 * get alternate subtitle tracks list from playlist 21941 */ 21942 }, { 21943 key: "subtitleTracks", 21944 get: function get() { 21945 var subtitleTrackController = this.subtitleTrackController; 21946 return subtitleTrackController ? subtitleTrackController.subtitleTracks : []; 21947 } 21948 21949 /** 21950 * index of the selected subtitle track (index in subtitle track lists) 21951 */ 21952 }, { 21953 key: "subtitleTrack", 21954 get: function get() { 21955 var subtitleTrackController = this.subtitleTrackController; 21956 return subtitleTrackController ? subtitleTrackController.subtitleTrack : -1; 21957 }, 21958 set: 21959 /** 21960 * select an subtitle track, based on its index in subtitle track lists 21961 */ 21962 function set(subtitleTrackId) { 21963 var subtitleTrackController = this.subtitleTrackController; 21964 if (subtitleTrackController) { 21965 subtitleTrackController.subtitleTrack = subtitleTrackId; 21966 } 21967 } 21968 21969 /** 21970 * Whether subtitle display is enabled or not 21971 */ 21972 }, { 21973 key: "media", 21974 get: function get() { 21975 return this._media; 21976 } 21977 }, { 21978 key: "subtitleDisplay", 21979 get: function get() { 21980 var subtitleTrackController = this.subtitleTrackController; 21981 return subtitleTrackController ? subtitleTrackController.subtitleDisplay : false; 21982 } 21983 21984 /** 21985 * Enable/disable subtitle display rendering 21986 */, 21987 set: function set(value) { 21988 var subtitleTrackController = this.subtitleTrackController; 21989 if (subtitleTrackController) { 21990 subtitleTrackController.subtitleDisplay = value; 21991 } 21992 } 21993 21994 /** 21995 * get mode for Low-Latency HLS loading 21996 */ 21997 }, { 21998 key: "lowLatencyMode", 21999 get: function get() { 22000 return this.config.lowLatencyMode; 22001 } 22002 22003 /** 22004 * Enable/disable Low-Latency HLS part playlist and segment loading, and start live streams at playlist PART-HOLD-BACK rather than HOLD-BACK. 22005 */, 22006 set: function set(mode) { 22007 this.config.lowLatencyMode = mode; 22008 } 22009 22010 /** 22011 * Position (in seconds) of live sync point (ie edge of live position minus safety delay defined by ```hls.config.liveSyncDuration```) 22012 * @returns null prior to loading live Playlist 22013 */ 22014 }, { 22015 key: "liveSyncPosition", 22016 get: function get() { 22017 return this.latencyController.liveSyncPosition; 22018 } 22019 22020 /** 22021 * Estimated position (in seconds) of live edge (ie edge of live playlist plus time sync playlist advanced) 22022 * @returns 0 before first playlist is loaded 22023 */ 22024 }, { 22025 key: "latency", 22026 get: function get() { 22027 return this.latencyController.latency; 22028 } 22029 22030 /** 22031 * maximum distance from the edge before the player seeks forward to ```hls.liveSyncPosition``` 22032 * configured using ```liveMaxLatencyDurationCount``` (multiple of target duration) or ```liveMaxLatencyDuration``` 22033 * @returns 0 before first playlist is loaded 22034 */ 22035 }, { 22036 key: "maxLatency", 22037 get: function get() { 22038 return this.latencyController.maxLatency; 22039 } 22040 22041 /** 22042 * target distance from the edge as calculated by the latency controller 22043 */ 22044 }, { 22045 key: "targetLatency", 22046 get: function get() { 22047 return this.latencyController.targetLatency; 22048 }, 22049 set: function set(latency) { 22050 this.latencyController.targetLatency = latency; 22051 } 22052 22053 /** 22054 * the rate at which the edge of the current live playlist is advancing or 1 if there is none 22055 */ 22056 }, { 22057 key: "drift", 22058 get: function get() { 22059 return this.latencyController.drift; 22060 } 22061 22062 /** 22063 * set to true when startLoad is called before MANIFEST_PARSED event 22064 */ 22065 }, { 22066 key: "forceStartLoad", 22067 get: function get() { 22068 return this.streamController.forceStartLoad; 22069 } 22070 22071 /** 22072 * ContentSteering pathwayPriority getter/setter 22073 */ 22074 }, { 22075 key: "pathwayPriority", 22076 get: function get() { 22077 return this.levelController.pathwayPriority; 22078 }, 22079 set: function set(pathwayPriority) { 22080 this.levelController.pathwayPriority = pathwayPriority; 22081 } 22082 }], [{ 22083 key: "version", 22084 get: 22085 /** 22086 * Get the video-dev/hls.js package version. 22087 */ 22088 function get() { 22089 return version; 22090 } 22091 }, { 22092 key: "Events", 22093 get: function get() { 22094 return Events; 22095 } 22096 }, { 22097 key: "ErrorTypes", 22098 get: function get() { 22099 return ErrorTypes; 22100 } 22101 }, { 22102 key: "ErrorDetails", 22103 get: function get() { 22104 return ErrorDetails; 22105 } 22106 22107 /** 22108 * Get the default configuration applied to new instances. 22109 */ 22110 }, { 22111 key: "DefaultConfig", 22112 get: function get() { 22113 if (!Hls.defaultConfig) { 22114 return hlsDefaultConfig; 22115 } 22116 return Hls.defaultConfig; 22117 } 22118 22119 /** 22120 * Replace the default configuration applied to new instances. 22121 */, 22122 set: function set(defaultConfig) { 22123 Hls.defaultConfig = defaultConfig; 22124 } 22125 }]); 22126 }(); 22127 Hls.defaultConfig = void 0; 22128 22129 return Hls; 22130 22131})); 22132})(false); 22133//# sourceMappingURL=hls.light.js.map
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.