vendor: 4,005 bytes, lines 1-99
1typeof window !== "undefined" && 2(function webpackUniversalModuleDefinition(root, factory) { 3 if(typeof exports === 'object' && typeof module === 'object') 4 module.exports = factory(); 5 else if(typeof define === 'function' && define.amd) 6 define([], factory); 7 else if(typeof exports === 'object') 8 exports["Hls"] = factory(); 9 else 10 root["Hls"] = factory(); 11})(this, function() { 12return /******/ (function(modules) { // webpackBootstrap 13/******/ // The module cache 14/******/ var installedModules = {}; 15/******/ 16/******/ // The require function 17/******/ function __webpack_require__(moduleId) { 18/******/ 19/******/ // Check if module is in cache 20/******/ if(installedModules[moduleId]) { 21/******/ return installedModules[moduleId].exports; 22/******/ } 23/******/ // Create a new module (and put it into the cache) 24/******/ var module = installedModules[moduleId] = { 25/******/ i: moduleId, 26/******/ l: false, 27/******/ exports: {} 28/******/ }; 29/******/ 30/******/ // Execute the module function 31/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 32/******/ 33/******/ // Flag the module as loaded 34/******/ module.l = true; 35/******/ 36/******/ // Return the exports of the module 37/******/ return module.exports; 38/******/ } 39/******/ 40/******/ 41/******/ // expose the modules object (__webpack_modules__) 42/******/ __webpack_require__.m = modules; 43/******/ 44/******/ // expose the module cache 45/******/ __webpack_require__.c = installedModules; 46/******/ 47/******/ // define getter function for harmony exports 48/******/ __webpack_require__.d = function(exports, name, getter) { 49/******/ if(!__webpack_require__.o(exports, name)) { 50/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter }); 51/******/ } 52/******/ }; 53/******/ 54/******/ // define __esModule on exports 55/******/ __webpack_require__.r = function(exports) { 56/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { 57/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); 58/******/ } 59/******/ Object.defineProperty(exports, '__esModule', { value: true }); 60/******/ }; 61/******/ 62/******/ // create a fake namespace object 63/******/ // mode & 1: value is a module id, require it 64/******/ // mode & 2: merge all properties of value into the ns 65/******/ // mode & 4: return value when already ns object 66/******/ // mode & 8|1: behave like require 67/******/ __webpack_require__.t = function(value, mode) { 68/******/ if(mode & 1) value = __webpack_require__(value); 69/******/ if(mode & 8) return value; 70/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value; 71/******/ var ns = Object.create(null); 72/******/ __webpack_require__.r(ns); 73/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value }); 74/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key)); 75/******/ return ns; 76/******/ }; 77/******/ 78/******/ // getDefaultExport function for compatibility with non-harmony modules 79/******/ __webpack_require__.n = function(module) { 80/******/ var getter = module && module.__esModule ? 81/******/ function getDefault() { return module['default']; } : 82/******/ function getModuleExports() { return module; }; 83/******/ __webpack_require__.d(getter, 'a', getter); 84/******/ return getter; 85/******/ }; 86/******/ 87/******/ // Object.prototype.hasOwnProperty.call 88/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 89/******/ 90/******/ // __webpack_public_path__ 91/******/ __webpack_require__.p = "/dist/"; 92/******/ 93/******/ 94/******/ // Load entry module and return exports 95/******/ return __webpack_require__(__webpack_require__.s = "./src/hls.js"); 96/******/ }) 97/************************************************************************/ 98/******/ ({ 99
vendor: 9,488 bytes, lines 100-448
100/***/ "./node_modules/eventemitter3/index.js": 101/*!*********************************************!*\ 102 !*** ./node_modules/eventemitter3/index.js ***! 103 \*********************************************/ 104/*! no static exports found */ 105/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 106/***/ (function(module, exports, __webpack_require__) { 107 108"use strict"; 109 110 111var has = Object.prototype.hasOwnProperty 112 , prefix = '~'; 113 114/** 115 * Constructor to create a storage for our `EE` objects. 116 * An `Events` instance is a plain object whose properties are event names. 117 * 118 * @constructor 119 * @private 120 */ 121function Events() {} 122 123// 124// We try to not inherit from `Object.prototype`. In some engines creating an 125// instance in this way is faster than calling `Object.create(null)` directly. 126// If `Object.create(null)` is not supported we prefix the event names with a 127// character to make sure that the built-in object properties are not 128// overridden or used as an attack vector. 129// 130if (Object.create) { 131 Events.prototype = Object.create(null); 132 133 // 134 // This hack is needed because the `__proto__` property is still inherited in 135 // some old browsers like Android 4, iPhone 5.1, Opera 11 and Safari 5. 136 // 137 if (!new Events().__proto__) prefix = false; 138} 139 140/** 141 * Representation of a single event listener. 142 * 143 * @param {Function} fn The listener function. 144 * @param {*} context The context to invoke the listener with. 145 * @param {Boolean} [once=false] Specify if the listener is a one-time listener. 146 * @constructor 147 * @private 148 */ 149function EE(fn, context, once) { 150 this.fn = fn; 151 this.context = context; 152 this.once = once || false; 153} 154 155/** 156 * Add a listener for a given event. 157 * 158 * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. 159 * @param {(String|Symbol)} event The event name. 160 * @param {Function} fn The listener function. 161 * @param {*} context The context to invoke the listener with. 162 * @param {Boolean} once Specify if the listener is a one-time listener. 163 * @returns {EventEmitter} 164 * @private 165 */ 166function addListener(emitter, event, fn, context, once) { 167 if (typeof fn !== 'function') { 168 throw new TypeError('The listener must be a function'); 169 } 170 171 var listener = new EE(fn, context || emitter, once) 172 , evt = prefix ? prefix + event : event; 173 174 if (!emitter._events[evt]) emitter._events[evt] = listener, emitter._eventsCount++; 175 else if (!emitter._events[evt].fn) emitter._events[evt].push(listener); 176 else emitter._events[evt] = [emitter._events[evt], listener]; 177 178 return emitter; 179} 180 181/** 182 * Clear event by name. 183 * 184 * @param {EventEmitter} emitter Reference to the `EventEmitter` instance. 185 * @param {(String|Symbol)} evt The Event name. 186 * @private 187 */ 188function clearEvent(emitter, evt) { 189 if (--emitter._eventsCount === 0) emitter._events = new Events(); 190 else delete emitter._events[evt]; 191} 192 193/** 194 * Minimal `EventEmitter` interface that is molded against the Node.js 195 * `EventEmitter` interface. 196 * 197 * @constructor 198 * @public 199 */ 200function EventEmitter() { 201 this._events = new Events(); 202 this._eventsCount = 0; 203} 204 205/** 206 * Return an array listing the events for which the emitter has registered 207 * listeners. 208 * 209 * @returns {Array} 210 * @public 211 */ 212EventEmitter.prototype.eventNames = function eventNames() { 213 var names = [] 214 , events 215 , name; 216 217 if (this._eventsCount === 0) return names; 218 219 for (name in (events = this._events)) { 220 if (has.call(events, name)) names.push(prefix ? name.slice(1) : name); 221 } 222 223 if (Object.getOwnPropertySymbols) { 224 return names.concat(Object.getOwnPropertySymbols(events)); 225 } 226 227 return names; 228}; 229 230/** 231 * Return the listeners registered for a given event. 232 * 233 * @param {(String|Symbol)} event The event name. 234 * @returns {Array} The registered listeners. 235 * @public 236 */ 237EventEmitter.prototype.listeners = function listeners(event) { 238 var evt = prefix ? prefix + event : event 239 , handlers = this._events[evt]; 240 241 if (!handlers) return []; 242 if (handlers.fn) return [handlers.fn]; 243 244 for (var i = 0, l = handlers.length, ee = new Array(l); i < l; i++) { 245 ee[i] = handlers[i].fn; 246 } 247 248 return ee; 249}; 250 251/** 252 * Return the number of listeners listening to a given event. 253 * 254 * @param {(String|Symbol)} event The event name. 255 * @returns {Number} The number of listeners. 256 * @public 257 */ 258EventEmitter.prototype.listenerCount = function listenerCount(event) { 259 var evt = prefix ? prefix + event : event 260 , listeners = this._events[evt]; 261 262 if (!listeners) return 0; 263 if (listeners.fn) return 1; 264 return listeners.length; 265}; 266 267/** 268 * Calls each of the listeners registered for a given event. 269 * 270 * @param {(String|Symbol)} event The event name. 271 * @returns {Boolean} `true` if the event had listeners, else `false`. 272 * @public 273 */ 274EventEmitter.prototype.emit = function emit(event, a1, a2, a3, a4, a5) { 275 var evt = prefix ? prefix + event : event; 276 277 if (!this._events[evt]) return false; 278 279 var listeners = this._events[evt] 280 , len = arguments.length 281 , args 282 , i; 283 284 if (listeners.fn) { 285 if (listeners.once) this.removeListener(event, listeners.fn, undefined, true); 286 287 switch (len) { 288 case 1: return listeners.fn.call(listeners.context), true; 289 case 2: return listeners.fn.call(listeners.context, a1), true; 290 case 3: return listeners.fn.call(listeners.context, a1, a2), true; 291 case 4: return listeners.fn.call(listeners.context, a1, a2, a3), true; 292 case 5: return listeners.fn.call(listeners.context, a1, a2, a3, a4), true; 293 case 6: return listeners.fn.call(listeners.context, a1, a2, a3, a4, a5), true; 294 } 295 296 for (i = 1, args = new Array(len -1); i < len; i++) { 297 args[i - 1] = arguments[i]; 298 } 299 300 listeners.fn.apply(listeners.context, args); 301 } else { 302 var length = listeners.length 303 , j; 304 305 for (i = 0; i < length; i++) { 306 if (listeners[i].once) this.removeListener(event, listeners[i].fn, undefined, true); 307 308 switch (len) { 309 case 1: listeners[i].fn.call(listeners[i].context); break; 310 case 2: listeners[i].fn.call(listeners[i].context, a1); break; 311 case 3: listeners[i].fn.call(listeners[i].context, a1, a2); break; 312 case 4: listeners[i].fn.call(listeners[i].context, a1, a2, a3); break; 313 default: 314 if (!args) for (j = 1, args = new Array(len -1); j < len; j++) { 315 args[j - 1] = arguments[j]; 316 } 317 318 listeners[i].fn.apply(listeners[i].context, args); 319 } 320 } 321 } 322 323 return true; 324}; 325 326/** 327 * Add a listener for a given event. 328 * 329 * @param {(String|Symbol)} event The event name. 330 * @param {Function} fn The listener function. 331 * @param {*} [context=this] The context to invoke the listener with. 332 * @returns {EventEmitter} `this`. 333 * @public 334 */ 335EventEmitter.prototype.on = function on(event, fn, context) { 336 return addListener(this, event, fn, context, false); 337}; 338 339/** 340 * Add a one-time listener for a given event. 341 * 342 * @param {(String|Symbol)} event The event name. 343 * @param {Function} fn The listener function. 344 * @param {*} [context=this] The context to invoke the listener with. 345 * @returns {EventEmitter} `this`. 346 * @public 347 */ 348EventEmitter.prototype.once = function once(event, fn, context) { 349 return addListener(this, event, fn, context, true); 350}; 351 352/** 353 * Remove the listeners of a given event. 354 * 355 * @param {(String|Symbol)} event The event name. 356 * @param {Function} fn Only remove the listeners that match this function. 357 * @param {*} context Only remove the listeners that have this context. 358 * @param {Boolean} once Only remove one-time listeners. 359 * @returns {EventEmitter} `this`. 360 * @public 361 */ 362EventEmitter.prototype.removeListener = function removeListener(event, fn, context, once) { 363 var evt = prefix ? prefix + event : event; 364 365 if (!this._events[evt]) return this; 366 if (!fn) { 367 clearEvent(this, evt); 368 return this; 369 } 370 371 var listeners = this._events[evt]; 372 373 if (listeners.fn) { 374 if ( 375 listeners.fn === fn && 376 (!once || listeners.once) && 377 (!context || listeners.context === context) 378 ) { 379 clearEvent(this, evt); 380 } 381 } else { 382 for (var i = 0, events = [], length = listeners.length; i < length; i++) { 383 if ( 384 listeners[i].fn !== fn || 385 (once && !listeners[i].once) || 386 (context && listeners[i].context !== context) 387 ) { 388 events.push(listeners[i]); 389 } 390 } 391 392 // 393 // Reset the array, or remove it completely if we have no more listeners. 394 // 395 if (events.length) this._events[evt] = events.length === 1 ? events[0] : events; 396 else clearEvent(this, evt); 397 } 398 399 return this; 400}; 401 402/** 403 * Remove all listeners, or those of the specified event. 404 * 405 * @param {(String|Symbol)} [event] The event name. 406 * @returns {EventEmitter} `this`. 407 * @public 408 */ 409EventEmitter.prototype.removeAllListeners = function removeAllListeners(event) { 410 var evt; 411 412 if (event) { 413 evt = prefix ? prefix + event : event; 414 if (this._events[evt]) clearEvent(this, evt); 415 } else { 416 this._events = new Events(); 417 this._eventsCount = 0; 418 } 419 420 return this; 421}; 422 423// 424// Alias methods names because people roll like that. 425// 426EventEmitter.prototype.off = EventEmitter.prototype.removeListener; 427EventEmitter.prototype.addListener = EventEmitter.prototype.on; 428 429// 430// Expose the prefix. 431// 432EventEmitter.prefixed = prefix; 433 434// 435// Allow `EventEmitter` to be imported as module namespace. 436// 437EventEmitter.EventEmitter = EventEmitter; 438 439// 440// Expose the module. 441// 442if (true) { 443 module.exports = EventEmitter; 444} 445 446 447/***/ }), 448
vendor: 8,778 bytes, lines 449-762
449/***/ "./node_modules/node-libs-browser/node_modules/events/events.js": 450/*!**********************************************************************!*\ 451 !*** ./node_modules/node-libs-browser/node_modules/events/events.js ***! 452 \**********************************************************************/ 453/*! no static exports found */ 454/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 455/***/ (function(module, exports) { 456 457// Copyright Joyent, Inc. and other Node contributors. 458// 459// Permission is hereby granted, free of charge, to any person obtaining a 460// copy of this software and associated documentation files (the 461// "Software"), to deal in the Software without restriction, including 462// without limitation the rights to use, copy, modify, merge, publish, 463// distribute, sublicense, and/or sell copies of the Software, and to permit 464// persons to whom the Software is furnished to do so, subject to the 465// following conditions: 466// 467// The above copyright notice and this permission notice shall be included 468// in all copies or substantial portions of the Software. 469// 470// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 471// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 472// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 473// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 474// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 475// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 476// USE OR OTHER DEALINGS IN THE SOFTWARE. 477 478function EventEmitter() { 479 this._events = this._events || {}; 480 this._maxListeners = this._maxListeners || undefined; 481} 482module.exports = EventEmitter; 483 484// Backwards-compat with node 0.10.x 485EventEmitter.EventEmitter = EventEmitter; 486 487EventEmitter.prototype._events = undefined; 488EventEmitter.prototype._maxListeners = undefined; 489 490// By default EventEmitters will print a warning if more than 10 listeners are 491// added to it. This is a useful default which helps finding memory leaks. 492EventEmitter.defaultMaxListeners = 10; 493 494// Obviously not all Emitters should be limited to 10. This function allows 495// that to be increased. Set to zero for unlimited. 496EventEmitter.prototype.setMaxListeners = function(n) { 497 if (!isNumber(n) || n < 0 || isNaN(n)) 498 throw TypeError('n must be a positive number'); 499 this._maxListeners = n; 500 return this; 501}; 502 503EventEmitter.prototype.emit = function(type) { 504 var er, handler, len, args, i, listeners; 505 506 if (!this._events) 507 this._events = {}; 508 509 // If there is no 'error' event listener then throw. 510 if (type === 'error') { 511 if (!this._events.error || 512 (isObject(this._events.error) && !this._events.error.length)) { 513 er = arguments[1]; 514 if (er instanceof Error) { 515 throw er; // Unhandled 'error' event 516 } else { 517 // At least give some kind of context to the user 518 var err = new Error('Uncaught, unspecified "error" event. (' + er + ')'); 519 err.context = er; 520 throw err; 521 } 522 } 523 } 524 525 handler = this._events[type]; 526 527 if (isUndefined(handler)) 528 return false; 529 530 if (isFunction(handler)) { 531 switch (arguments.length) { 532 // fast cases 533 case 1: 534 handler.call(this); 535 break; 536 case 2: 537 handler.call(this, arguments[1]); 538 break; 539 case 3: 540 handler.call(this, arguments[1], arguments[2]); 541 break; 542 // slower 543 default: 544 args = Array.prototype.slice.call(arguments, 1); 545 handler.apply(this, args); 546 } 547 } else if (isObject(handler)) { 548 args = Array.prototype.slice.call(arguments, 1); 549 listeners = handler.slice(); 550 len = listeners.length; 551 for (i = 0; i < len; i++) 552 listeners[i].apply(this, args); 553 } 554 555 return true; 556}; 557 558EventEmitter.prototype.addListener = function(type, listener) { 559 var m; 560 561 if (!isFunction(listener)) 562 throw TypeError('listener must be a function'); 563 564 if (!this._events) 565 this._events = {}; 566 567 // To avoid recursion in the case that type === "newListener"! Before 568 // adding it to the listeners, first emit "newListener". 569 if (this._events.newListener) 570 this.emit('newListener', type, 571 isFunction(listener.listener) ? 572 listener.listener : listener); 573 574 if (!this._events[type]) 575 // Optimize the case of one listener. Don't need the extra array object. 576 this._events[type] = listener; 577 else if (isObject(this._events[type])) 578 // If we've already got an array, just append. 579 this._events[type].push(listener); 580 else 581 // Adding the second element, need to change to array. 582 this._events[type] = [this._events[type], listener]; 583 584 // Check for listener leak 585 if (isObject(this._events[type]) && !this._events[type].warned) { 586 if (!isUndefined(this._maxListeners)) { 587 m = this._maxListeners; 588 } else { 589 m = EventEmitter.defaultMaxListeners; 590 } 591 592 if (m && m > 0 && this._events[type].length > m) { 593 this._events[type].warned = true; 594 console.error('(node) warning: possible EventEmitter memory ' + 595 'leak detected. %d listeners added. ' + 596 'Use emitter.setMaxListeners() to increase limit.', 597 this._events[type].length); 598 if (typeof console.trace === 'function') { 599 // not supported in IE 10 600 console.trace(); 601 } 602 } 603 } 604 605 return this; 606}; 607 608EventEmitter.prototype.on = EventEmitter.prototype.addListener; 609 610EventEmitter.prototype.once = function(type, listener) { 611 if (!isFunction(listener)) 612 throw TypeError('listener must be a function'); 613 614 var fired = false; 615 616 function g() { 617 this.removeListener(type, g); 618 619 if (!fired) { 620 fired = true; 621 listener.apply(this, arguments); 622 } 623 } 624 625 g.listener = listener; 626 this.on(type, g); 627 628 return this; 629}; 630 631// emits a 'removeListener' event iff the listener was removed 632EventEmitter.prototype.removeListener = function(type, listener) { 633 var list, position, length, i; 634 635 if (!isFunction(listener)) 636 throw TypeError('listener must be a function'); 637 638 if (!this._events || !this._events[type]) 639 return this; 640 641 list = this._events[type]; 642 length = list.length; 643 position = -1; 644 645 if (list === listener || 646 (isFunction(list.listener) && list.listener === listener)) { 647 delete this._events[type]; 648 if (this._events.removeListener) 649 this.emit('removeListener', type, listener); 650 651 } else if (isObject(list)) { 652 for (i = length; i-- > 0;) { 653 if (list[i] === listener || 654 (list[i].listener && list[i].listener === listener)) { 655 position = i; 656 break; 657 } 658 } 659 660 if (position < 0) 661 return this; 662 663 if (list.length === 1) { 664 list.length = 0; 665 delete this._events[type]; 666 } else { 667 list.splice(position, 1); 668 } 669 670 if (this._events.removeListener) 671 this.emit('removeListener', type, listener); 672 } 673 674 return this; 675}; 676 677EventEmitter.prototype.removeAllListeners = function(type) { 678 var key, listeners; 679 680 if (!this._events) 681 return this; 682 683 // not listening for removeListener, no need to emit 684 if (!this._events.removeListener) { 685 if (arguments.length === 0) 686 this._events = {}; 687 else if (this._events[type]) 688 delete this._events[type]; 689 return this; 690 } 691 692 // emit removeListener for all listeners on all events 693 if (arguments.length === 0) { 694 for (key in this._events) { 695 if (key === 'removeListener') continue; 696 this.removeAllListeners(key); 697 } 698 this.removeAllListeners('removeListener'); 699 this._events = {}; 700 return this; 701 } 702 703 listeners = this._events[type]; 704 705 if (isFunction(listeners)) { 706 this.removeListener(type, listeners); 707 } else if (listeners) { 708 // LIFO order 709 while (listeners.length) 710 this.removeListener(type, listeners[listeners.length - 1]); 711 } 712 delete this._events[type]; 713 714 return this; 715}; 716 717EventEmitter.prototype.listeners = function(type) { 718 var ret; 719 if (!this._events || !this._events[type]) 720 ret = []; 721 else if (isFunction(this._events[type])) 722 ret = [this._events[type]]; 723 else 724 ret = this._events[type].slice(); 725 return ret; 726}; 727 728EventEmitter.prototype.listenerCount = function(type) { 729 if (this._events) { 730 var evlistener = this._events[type]; 731 732 if (isFunction(evlistener)) 733 return 1; 734 else if (evlistener) 735 return evlistener.length; 736 } 737 return 0; 738}; 739 740EventEmitter.listenerCount = function(emitter, type) { 741 return emitter.listenerCount(type); 742}; 743 744function isFunction(arg) { 745 return typeof arg === 'function'; 746} 747 748function isNumber(arg) { 749 return typeof arg === 'number'; 750} 751 752function isObject(arg) { 753 return typeof arg === 'object' && arg !== null; 754} 755 756function isUndefined(arg) { 757 return arg === void 0; 758} 759 760 761/***/ }), 762
vendor: 7,201 bytes, lines 763-932
763/***/ "./node_modules/url-toolkit/src/url-toolkit.js": 764/*!*****************************************************!*\ 765 !*** ./node_modules/url-toolkit/src/url-toolkit.js ***! 766 \*****************************************************/ 767/*! no static exports found */ 768/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 769/***/ (function(module, exports, __webpack_require__) { 770 771// see https://tools.ietf.org/html/rfc1808 772 773/* jshint ignore:start */ 774(function(root) { 775/* jshint ignore:end */ 776 777 var URL_REGEX = /^((?:[a-zA-Z0-9+\-.]+:)?)(\/\/[^\/?#]*)?((?:[^\/\?#]*\/)*.*?)??(;.*?)?(\?.*?)?(#.*?)?$/; 778 var FIRST_SEGMENT_REGEX = /^([^\/?#]*)(.*)$/; 779 var SLASH_DOT_REGEX = /(?:\/|^)\.(?=\/)/g; 780 var SLASH_DOT_DOT_REGEX = /(?:\/|^)\.\.\/(?!\.\.\/).*?(?=\/)/g; 781 782 var URLToolkit = { // jshint ignore:line 783 // If opts.alwaysNormalize is true then the path will always be normalized even when it starts with / or // 784 // E.g 785 // With opts.alwaysNormalize = false (default, spec compliant) 786 // http://a.com/b/cd + /e/f/../g => http://a.com/e/f/../g 787 // With opts.alwaysNormalize = true (not spec compliant) 788 // http://a.com/b/cd + /e/f/../g => http://a.com/e/g 789 buildAbsoluteURL: function(baseURL, relativeURL, opts) { 790 opts = opts || {}; 791 // remove any remaining space and CRLF 792 baseURL = baseURL.trim(); 793 relativeURL = relativeURL.trim(); 794 if (!relativeURL) { 795 // 2a) If the embedded URL is entirely empty, it inherits the 796 // entire base URL (i.e., is set equal to the base URL) 797 // and we are done. 798 if (!opts.alwaysNormalize) { 799 return baseURL; 800 } 801 var basePartsForNormalise = URLToolkit.parseURL(baseURL); 802 if (!basePartsForNormalise) { 803 throw new Error('Error trying to parse base URL.'); 804 } 805 basePartsForNormalise.path = URLToolkit.normalizePath(basePartsForNormalise.path); 806 return URLToolkit.buildURLFromParts(basePartsForNormalise); 807 } 808 var relativeParts = URLToolkit.parseURL(relativeURL); 809 if (!relativeParts) { 810 throw new Error('Error trying to parse relative URL.'); 811 } 812 if (relativeParts.scheme) { 813 // 2b) If the embedded URL starts with a scheme name, it is 814 // interpreted as an absolute URL and we are done. 815 if (!opts.alwaysNormalize) { 816 return relativeURL; 817 } 818 relativeParts.path = URLToolkit.normalizePath(relativeParts.path); 819 return URLToolkit.buildURLFromParts(relativeParts); 820 } 821 var baseParts = URLToolkit.parseURL(baseURL); 822 if (!baseParts) { 823 throw new Error('Error trying to parse base URL.'); 824 } 825 if (!baseParts.netLoc && baseParts.path && baseParts.path[0] !== '/') { 826 // If netLoc missing and path doesn't start with '/', assume everthing before the first '/' is the netLoc 827 // This causes 'example.com/a' to be handled as '//example.com/a' instead of '/example.com/a' 828 var pathParts = FIRST_SEGMENT_REGEX.exec(baseParts.path); 829 baseParts.netLoc = pathParts[1]; 830 baseParts.path = pathParts[2]; 831 } 832 if (baseParts.netLoc && !baseParts.path) { 833 baseParts.path = '/'; 834 } 835 var builtParts = { 836 // 2c) Otherwise, the embedded URL inherits the scheme of 837 // the base URL. 838 scheme: baseParts.scheme, 839 netLoc: relativeParts.netLoc, 840 path: null, 841 params: relativeParts.params, 842 query: relativeParts.query, 843 fragment: relativeParts.fragment 844 }; 845 if (!relativeParts.netLoc) { 846 // 3) If the embedded URL's <net_loc> is non-empty, we skip to 847 // Step 7. Otherwise, the embedded URL inherits the <net_loc> 848 // (if any) of the base URL. 849 builtParts.netLoc = baseParts.netLoc; 850 // 4) If the embedded URL path is preceded by a slash "/", the 851 // path is not relative and we skip to Step 7. 852 if (relativeParts.path[0] !== '/') { 853 if (!relativeParts.path) { 854 // 5) If the embedded URL path is empty (and not preceded by a 855 // slash), then the embedded URL inherits the base URL path 856 builtParts.path = baseParts.path; 857 // 5a) if the embedded URL's <params> is non-empty, we skip to 858 // step 7; otherwise, it inherits the <params> of the base 859 // URL (if any) and 860 if (!relativeParts.params) { 861 builtParts.params = baseParts.params; 862 // 5b) if the embedded URL's <query> is non-empty, we skip to 863 // step 7; otherwise, it inherits the <query> of the base 864 // URL (if any) and we skip to step 7. 865 if (!relativeParts.query) { 866 builtParts.query = baseParts.query; 867 } 868 } 869 } else { 870 // 6) The last segment of the base URL's path (anything 871 // following the rightmost slash "/", or the entire path if no 872 // slash is present) is removed and the embedded URL's path is 873 // appended in its place. 874 var baseURLPath = baseParts.path; 875 var newPath = baseURLPath.substring(0, baseURLPath.lastIndexOf('/') + 1) + relativeParts.path; 876 builtParts.path = URLToolkit.normalizePath(newPath); 877 } 878 } 879 } 880 if (builtParts.path === null) { 881 builtParts.path = opts.alwaysNormalize ? URLToolkit.normalizePath(relativeParts.path) : relativeParts.path; 882 } 883 return URLToolkit.buildURLFromParts(builtParts); 884 }, 885 parseURL: function(url) { 886 var parts = URL_REGEX.exec(url); 887 if (!parts) { 888 return null; 889 } 890 return { 891 scheme: parts[1] || '', 892 netLoc: parts[2] || '', 893 path: parts[3] || '', 894 params: parts[4] || '', 895 query: parts[5] || '', 896 fragment: parts[6] || '' 897 }; 898 }, 899 normalizePath: function(path) { 900 // The following operations are 901 // then applied, in order, to the new path: 902 // 6a) All occurrences of "./", where "." is a complete path 903 // segment, are removed. 904 // 6b) If the path ends with "." as a complete path segment, 905 // that "." is removed. 906 path = path.split('').reverse().join('').replace(SLASH_DOT_REGEX, ''); 907 // 6c) All occurrences of "<segment>/../", where <segment> is a 908 // complete path segment not equal to "..", are removed. 909 // Removal of these path segments is performed iteratively, 910 // removing the leftmost matching pattern on each iteration, 911 // until no matching pattern remains. 912 // 6d) If the path ends with "<segment>/..", where <segment> is a 913 // complete path segment not equal to "..", that 914 // "<segment>/.." is removed. 915 while (path.length !== (path = path.replace(SLASH_DOT_DOT_REGEX, '')).length) {} // jshint ignore:line 916 return path.split('').reverse().join(''); 917 }, 918 buildURLFromParts: function(parts) { 919 return parts.scheme + parts.netLoc + parts.path + parts.params + parts.query + parts.fragment; 920 } 921 }; 922 923/* jshint ignore:start */ 924 if(true) 925 module.exports = URLToolkit; 926 else {} 927})(this); 928/* jshint ignore:end */ 929 930 931/***/ }), 932
vendor: 7,825 bytes, lines 933-1146
933/***/ "./node_modules/webworkify-webpack/index.js": 934/*!**************************************************!*\ 935 !*** ./node_modules/webworkify-webpack/index.js ***! 936 \**************************************************/ 937/*! no static exports found */ 938/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 939/***/ (function(module, exports, __webpack_require__) { 940 941function webpackBootstrapFunc (modules) { 942/******/ // The module cache 943/******/ var installedModules = {}; 944 945/******/ // The require function 946/******/ function __webpack_require__(moduleId) { 947 948/******/ // Check if module is in cache 949/******/ if(installedModules[moduleId]) 950/******/ return installedModules[moduleId].exports; 951 952/******/ // Create a new module (and put it into the cache) 953/******/ var module = installedModules[moduleId] = { 954/******/ i: moduleId, 955/******/ l: false, 956/******/ exports: {} 957/******/ }; 958 959/******/ // Execute the module function 960/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); 961 962/******/ // Flag the module as loaded 963/******/ module.l = true; 964 965/******/ // Return the exports of the module 966/******/ return module.exports; 967/******/ } 968 969/******/ // expose the modules object (__webpack_modules__) 970/******/ __webpack_require__.m = modules; 971 972/******/ // expose the module cache 973/******/ __webpack_require__.c = installedModules; 974 975/******/ // identity function for calling harmony imports with the correct context 976/******/ __webpack_require__.i = function(value) { return value; }; 977 978/******/ // define getter function for harmony exports 979/******/ __webpack_require__.d = function(exports, name, getter) { 980/******/ if(!__webpack_require__.o(exports, name)) { 981/******/ Object.defineProperty(exports, name, { 982/******/ configurable: false, 983/******/ enumerable: true, 984/******/ get: getter 985/******/ }); 986/******/ } 987/******/ }; 988 989/******/ // define __esModule on exports 990/******/ __webpack_require__.r = function(exports) { 991/******/ Object.defineProperty(exports, '__esModule', { value: true }); 992/******/ }; 993 994/******/ // getDefaultExport function for compatibility with non-harmony modules 995/******/ __webpack_require__.n = function(module) { 996/******/ var getter = module && module.__esModule ? 997/******/ function getDefault() { return module['default']; } : 998/******/ function getModuleExports() { return module; }; 999/******/ __webpack_require__.d(getter, 'a', getter); 1000/******/ return getter; 1001/******/ }; 1002 1003/******/ // Object.prototype.hasOwnProperty.call 1004/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); }; 1005 1006/******/ // __webpack_public_path__ 1007/******/ __webpack_require__.p = "/"; 1008 1009/******/ // on error function for async loading 1010/******/ __webpack_require__.oe = function(err) { console.error(err); throw err; }; 1011 1012 var f = __webpack_require__(__webpack_require__.s = ENTRY_MODULE) 1013 return f.default || f // try to call default if defined to also support babel esmodule exports 1014} 1015 1016var moduleNameReqExp = '[\\.|\\-|\\+|\\w|\/|@]+' 1017var dependencyRegExp = '\\((\/\\*.*?\\*\/)?\s?.*?(' + moduleNameReqExp + ').*?\\)' // additional chars when output.pathinfo is true 1018 1019// http://stackoverflow.com/a/2593661/130442 1020function quoteRegExp (str) { 1021 return (str + '').replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&') 1022} 1023 1024function isNumeric(n) { 1025 return !isNaN(1 * n); // 1 * n converts integers, integers as string ("123"), 1e3 and "1e3" to integers and strings to NaN 1026} 1027 1028function getModuleDependencies (sources, module, queueName) { 1029 var retval = {} 1030 retval[queueName] = [] 1031 1032 var fnString = module.toString() 1033 var wrapperSignature = fnString.match(/^function\s?\(\w+,\s*\w+,\s*(\w+)\)/) 1034 if (!wrapperSignature) return retval 1035 var webpackRequireName = wrapperSignature[1] 1036 1037 // main bundle deps 1038 var re = new RegExp('(\\\\n|\\W)' + quoteRegExp(webpackRequireName) + dependencyRegExp, 'g') 1039 var match 1040 while ((match = re.exec(fnString))) { 1041 if (match[3] === 'dll-reference') continue 1042 retval[queueName].push(match[3]) 1043 } 1044 1045 // dll deps 1046 re = new RegExp('\\(' + quoteRegExp(webpackRequireName) + '\\("(dll-reference\\s(' + moduleNameReqExp + '))"\\)\\)' + dependencyRegExp, 'g') 1047 while ((match = re.exec(fnString))) { 1048 if (!sources[match[2]]) { 1049 retval[queueName].push(match[1]) 1050 sources[match[2]] = __webpack_require__(match[1]).m 1051 } 1052 retval[match[2]] = retval[match[2]] || [] 1053 retval[match[2]].push(match[4]) 1054 } 1055 1056 // convert 1e3 back to 1000 - this can be important after uglify-js converted 1000 to 1e3 1057 var keys = Object.keys(retval); 1058 for (var i = 0; i < keys.length; i++) { 1059 for (var j = 0; j < retval[keys[i]].length; j++) { 1060 if (isNumeric(retval[keys[i]][j])) { 1061 retval[keys[i]][j] = 1 * retval[keys[i]][j]; 1062 } 1063 } 1064 } 1065 1066 return retval 1067} 1068 1069function hasValuesInQueues (queues) { 1070 var keys = Object.keys(queues) 1071 return keys.reduce(function (hasValues, key) { 1072 return hasValues || queues[key].length > 0 1073 }, false) 1074} 1075 1076function getRequiredModules (sources, moduleId) { 1077 var modulesQueue = { 1078 main: [moduleId] 1079 } 1080 var requiredModules = { 1081 main: [] 1082 } 1083 var seenModules = { 1084 main: {} 1085 } 1086 1087 while (hasValuesInQueues(modulesQueue)) { 1088 var queues = Object.keys(modulesQueue) 1089 for (var i = 0; i < queues.length; i++) { 1090 var queueName = queues[i] 1091 var queue = modulesQueue[queueName] 1092 var moduleToCheck = queue.pop() 1093 seenModules[queueName] = seenModules[queueName] || {} 1094 if (seenModules[queueName][moduleToCheck] || !sources[queueName][moduleToCheck]) continue 1095 seenModules[queueName][moduleToCheck] = true 1096 requiredModules[queueName] = requiredModules[queueName] || [] 1097 requiredModules[queueName].push(moduleToCheck) 1098 var newModules = getModuleDependencies(sources, sources[queueName][moduleToCheck], queueName) 1099 var newModulesKeys = Object.keys(newModules) 1100 for (var j = 0; j < newModulesKeys.length; j++) { 1101 modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]] || [] 1102 modulesQueue[newModulesKeys[j]] = modulesQueue[newModulesKeys[j]].concat(newModules[newModulesKeys[j]]) 1103 } 1104 } 1105 } 1106 1107 return requiredModules 1108} 1109 1110module.exports = function (moduleId, options) { 1111 options = options || {} 1112 var sources = { 1113 main: __webpack_require__.m 1114 } 1115 1116 var requiredModules = options.all ? { main: Object.keys(sources.main) } : getRequiredModules(sources, moduleId) 1117 1118 var src = '' 1119 1120 Object.keys(requiredModules).filter(function (m) { return m !== 'main' }).forEach(function (module) { 1121 var entryModule = 0 1122 while (requiredModules[module][entryModule]) { 1123 entryModule++ 1124 } 1125 requiredModules[module].push(entryModule) 1126 sources[module][entryModule] = '(function(module, exports, __webpack_require__) { module.exports = __webpack_require__; })' 1127 src = src + 'var ' + module + ' = (' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(entryModule)) + ')({' + requiredModules[module].map(function (id) { return '' + JSON.stringify(id) + ': ' + sources[module][id].toString() }).join(',') + '});\n' 1128 }) 1129 1130 src = src + 'new ((' + webpackBootstrapFunc.toString().replace('ENTRY_MODULE', JSON.stringify(moduleId)) + ')({' + requiredModules.main.map(function (id) { return '' + JSON.stringify(id) + ': ' + sources.main[id].toString() }).join(',') + '}))(self);' 1131 1132 var blob = new window.Blob([src], { type: 'text/javascript' }) 1133 if (options.bare) { return blob } 1134 1135 var URL = window.URL || window.webkitURL || window.mozURL || window.msURL 1136 1137 var workerUrl = URL.createObjectURL(blob) 1138 var worker = new window.Worker(workerUrl) 1139 worker.objectURL = workerUrl 1140 1141 return worker 1142} 1143 1144 1145/***/ }), 1146
1147/***/ "./src/config.js": 1148/*!***********************!*\ 1149 !*** ./src/config.js ***! 1150 \***********************/ 1151/*! no static exports found */ 1152/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 1153/***/ (function(module, exports, __webpack_require__) { 1154 1155"use strict"; 1156 1157/** 1158 * HLS config 1159 */ 1160Object.defineProperty(exports, "__esModule", { value: true }); 1161var abr_controller_1 = __webpack_require__(/*! ./controller/abr-controller */ "./src/controller/abr-controller.js"); 1162var buffer_controller_1 = __webpack_require__(/*! ./controller/buffer-controller */ "./src/controller/buffer-controller.js"); 1163var cap_level_controller_1 = __webpack_require__(/*! ./controller/cap-level-controller */ "./src/controller/cap-level-controller.js"); 1164var fps_controller_1 = __webpack_require__(/*! ./controller/fps-controller */ "./src/controller/fps-controller.js"); 1165var xhr_loader_1 = __webpack_require__(/*! ./utils/xhr-loader */ "./src/utils/xhr-loader.js"); 1166// import FetchLoader from './utils/fetch-loader'; 1167var audio_track_controller_1 = __webpack_require__(/*! ./controller/audio-track-controller */ "./src/controller/audio-track-controller.js"); 1168var audio_stream_controller_1 = __webpack_require__(/*! ./controller/audio-stream-controller */ "./src/controller/audio-stream-controller.js"); 1169var Cues = __webpack_require__(/*! ./utils/cues */ "./src/utils/cues.js"); 1170var timeline_controller_1 = __webpack_require__(/*! ./controller/timeline-controller */ "./src/controller/timeline-controller.js"); 1171var subtitle_track_controller_1 = __webpack_require__(/*! ./controller/subtitle-track-controller */ "./src/controller/subtitle-track-controller.js"); 1172var subtitle_stream_controller_1 = __webpack_require__(/*! ./controller/subtitle-stream-controller */ "./src/controller/subtitle-stream-controller.js"); 1173var eme_controller_1 = __webpack_require__(/*! ./controller/eme-controller */ "./src/controller/eme-controller.js"); 1174var mediakeys_helper_1 = __webpack_require__(/*! ./utils/mediakeys-helper */ "./src/utils/mediakeys-helper.js"); 1175exports.hlsDefaultConfig = { 1176 autoStartLoad: true, 1177 startPosition: -1, 1178 defaultAudioCodec: undefined, 1179 debug: false, 1180 capLevelOnFPSDrop: false, 1181 capLevelToPlayerSize: false, 1182 initialLiveManifestSize: 1, 1183 maxBufferLength: 30, 1184 maxBufferSize: 60 * 1000 * 1000, 1185 maxBufferHole: 0.5, 1186 lowBufferWatchdogPeriod: 0.5, 1187 highBufferWatchdogPeriod: 3, 1188 nudgeOffset: 0.1, 1189 nudgeMaxRetry: 3, 1190 maxFragLookUpTolerance: 0.25, 1191 liveSyncDurationCount: 3, 1192 liveMaxLatencyDurationCount: Infinity, 1193 liveSyncDuration: undefined, 1194 liveMaxLatencyDuration: undefined, 1195 liveDurationInfinity: false, 1196 liveBackBufferLength: Infinity, 1197 maxMaxBufferLength: 600, 1198 enableWorker: true, 1199 enableSoftwareAES: true, 1200 manifestLoadingTimeOut: 10000, 1201 manifestLoadingMaxRetry: 1, 1202 manifestLoadingRetryDelay: 1000, 1203 manifestLoadingMaxRetryTimeout: 64000, 1204 startLevel: undefined, 1205 levelLoadingTimeOut: 10000, 1206 levelLoadingMaxRetry: 4, 1207 levelLoadingRetryDelay: 1000, 1208 levelLoadingMaxRetryTimeout: 64000, 1209 fragLoadingTimeOut: 20000, 1210 fragLoadingMaxRetry: 6, 1211 fragLoadingRetryDelay: 1000, 1212 fragLoadingMaxRetryTimeout: 64000, 1213 startFragPrefetch: false, 1214 fpsDroppedMonitoringPeriod: 5000, 1215 fpsDroppedMonitoringThreshold: 0.2, 1216 appendErrorMaxRetry: 3, 1217 loader: xhr_loader_1.default, 1218 // loader: FetchLoader, 1219 fLoader: undefined, 1220 pLoader: undefined, 1221 xhrSetup: undefined, 1222 licenseXhrSetup: undefined, 1223 // fetchSetup: undefined, 1224 abrController: abr_controller_1.default, 1225 bufferController: buffer_controller_1.default, 1226 capLevelController: cap_level_controller_1.default, 1227 fpsController: fps_controller_1.default, 1228 stretchShortVideoTrack: false, 1229 maxAudioFramesDrift: 1, 1230 forceKeyFrameOnDiscontinuity: true, 1231 abrEwmaFastLive: 3, 1232 abrEwmaSlowLive: 9, 1233 abrEwmaFastVoD: 3, 1234 abrEwmaSlowVoD: 9, 1235 abrEwmaDefaultEstimate: 5e5, 1236 abrBandWidthFactor: 0.95, 1237 abrBandWidthUpFactor: 0.7, 1238 abrMaxWithRealBitrate: false, 1239 maxStarvationDelay: 4, 1240 maxLoadingDelay: 4, 1241 minAutoBitrate: 0, 1242 emeEnabled: false, 1243 widevineLicenseUrl: undefined, 1244 requestMediaKeySystemAccessFunc: mediakeys_helper_1.requestMediaKeySystemAccess // used by eme-controller 1245}; 1246if (true) {
1247 exports.hlsDefaultConfig.subtitleStreamController = subtitle_stream_controller_1.SubtitleStreamController; 1248 exports.hlsDefaultConfig.subtitleTrackController = subtitle_track_controller_1.default; 1249 exports.hlsDefaultConfig.timelineController = timeline_controller_1.default; 1250 exports.hlsDefaultConfig.cueHandler = Cues; // used by timeline-controller 1251 exports.hlsDefaultConfig.enableCEA708Captions = true; // used by timeline-controller 1252 exports.hlsDefaultConfig.enableWebVTT = true; // used by timeline-controller 1253 exports.hlsDefaultConfig.captionsTextTrack1Label = 'English'; // used by timeline-controller 1254 exports.hlsDefaultConfig.captionsTextTrack1LanguageCode = 'en'; // used by timeline-controller 1255 exports.hlsDefaultConfig.captionsTextTrack2Label = 'Spanish'; // used by timeline-controller 1256 exports.hlsDefaultConfig.captionsTextTrack2LanguageCode = 'es'; // used by timeline-controller 1257} 1258if (true) { 1259 exports.hlsDefaultConfig.audioStreamController = audio_stream_controller_1.default; 1260 exports.hlsDefaultConfig.audioTrackController = audio_track_controller_1.default; 1261} 1262if (true) { 1263 exports.hlsDefaultConfig.emeController = eme_controller_1.default; 1264} 1265 1266 1267/***/ }), 1268
1269/***/ "./src/controller/abr-controller.js": 1270/*!******************************************!*\ 1271 !*** ./src/controller/abr-controller.js ***! 1272 \******************************************/ 1273/*! no static exports found */ 1274/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 1275/***/ (function(module, exports, __webpack_require__) { 1276 1277"use strict"; 1278/* WEBPACK VAR INJECTION */(function(Number) { 1279/* 1280 * simple ABR Controller 1281 * - compute next level based on last fragment bw heuristics 1282 * - implement an abandon rules triggered if we have less than 2 frag buffered and if computed bw shows that we risk buffer stalling 1283 */ 1284var __extends = (this && this.__extends) || (function () { 1285 var extendStatics = Object.setPrototypeOf || 1286 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 1287 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 1288 return function (d, b) { 1289 extendStatics(d, b); 1290 function __() { this.constructor = d; } 1291 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 1292 }; 1293})(); 1294Object.defineProperty(exports, "__esModule", { value: true }); 1295var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 1296var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 1297var buffer_helper_1 = __webpack_require__(/*! ../utils/buffer-helper */ "./src/utils/buffer-helper.js"); 1298var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 1299var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 1300var ewma_bandwidth_estimator_1 = __webpack_require__(/*! ../utils/ewma-bandwidth-estimator */ "./src/utils/ewma-bandwidth-estimator.js"); 1301var performance = window.performance; 1302var AbrController = /** @class */ (function (_super) { 1303 __extends(AbrController, _super); 1304 function AbrController(hls) { 1305 var _this = _super.call(this, hls, events_1.default.FRAG_LOADING, events_1.default.FRAG_LOADED, events_1.default.FRAG_BUFFERED, events_1.default.ERROR) || this; 1306 _this.lastLoadedFragLevel = 0; 1307 _this._nextAutoLevel = -1; 1308 _this.hls = hls; 1309 _this.timer = null; 1310 _this._bwEstimator = null; 1311 _this.onCheck = _this._abandonRulesCheck.bind(_this); 1312 return _this; 1313 } 1314 AbrController.prototype.destroy = function () { 1315 this.clearTimer(); 1316 event_handler_1.default.prototype.destroy.call(this); 1317 }; 1318 AbrController.prototype.onFragLoading = function (data) { 1319 var frag = data.frag; 1320 if (frag.type === 'main') { 1321 if (!this.timer) { 1322 this.fragCurrent = frag; 1323 this.timer = setInterval(this.onCheck, 100); 1324 } 1325 // lazy init of BwEstimator, rationale is that we use different params for Live/VoD 1326 // so we need to wait for stream manifest / playlist type to instantiate it. 1327 if (!this._bwEstimator) { 1328 var hls_1 = this.hls; 1329 var config = hls_1.config; 1330 var level = frag.level; 1331 var isLive = hls_1.levels[level].details.live; 1332 var ewmaFast = void 0, ewmaSlow = void 0; 1333 if (isLive) { 1334 ewmaFast = config.abrEwmaFastLive; 1335 ewmaSlow = config.abrEwmaSlowLive; 1336 } 1337 else { 1338 ewmaFast = config.abrEwmaFastVoD; 1339 ewmaSlow = config.abrEwmaSlowVoD; 1340 } 1341 this._bwEstimator = new ewma_bandwidth_estimator_1.default(hls_1, ewmaSlow, ewmaFast, config.abrEwmaDefaultEstimate); 1342 } 1343 } 1344 }; 1345 AbrController.prototype._abandonRulesCheck = function () { 1346 /* 1347 monitor fragment retrieval time... 1348 we compute expected time of arrival of the complete fragment. 1349 we compare it to expected time of buffer starvation 1350 */ 1351 var hls = this.hls; 1352 var video = hls.media; 1353 var frag = this.fragCurrent; 1354 if (!frag) { 1355 return; 1356 } 1357 var loader = frag.loader; 1358 var minAutoLevel = hls.minAutoLevel; 1359 // if loader has been destroyed or loading has been aborted, stop timer and return 1360 if (!loader || (loader.stats && loader.stats.aborted)) { 1361 logger_1.logger.warn('frag loader destroy or aborted, disarm abandonRules'); 1362 this.clearTimer(); 1363 // reset forced auto level value so that next level will be selected 1364 this._nextAutoLevel = -1; 1365 return; 1366 } 1367 var stats = loader.stats; 1368 /* only monitor frag retrieval time if 1369 (video not paused OR first fragment being loaded(ready state === HAVE_NOTHING = 0)) AND autoswitching enabled AND not lowest level (=> means that we have several levels) */ 1370 if (video && stats && ((!video.paused && (video.playbackRate !== 0)) || !video.readyState) && frag.autoLevel && frag.level) {
1371 var requestDelay = performance.now() - stats.trequest, playbackRate = Math.abs(video.playbackRate); 1372 // monitor fragment load progress after half of expected fragment duration,to stabilize bitrate 1373 if (requestDelay > (500 * frag.duration / playbackRate)) { 1374 var levels = hls.levels, loadRate = Math.max(1, stats.bw ? stats.bw / 8 : stats.loaded * 1000 / requestDelay), // byte/s; at least 1 byte/s to avoid division by zero 1375 // compute expected fragment length using frag duration and level bitrate. also ensure that expected len is gte than already loaded size 1376 level = levels[frag.level], levelBitrate = level.realBitrate ? Math.max(level.realBitrate, level.bitrate) : level.bitrate, expectedLen = stats.total ? stats.total : Math.max(stats.loaded, Math.round(frag.duration * levelBitrate / 8)), pos = video.currentTime, fragLoadedDelay = (expectedLen - stats.loaded) / loadRate, bufferStarvationDelay = (buffer_helper_1.BufferHelper.bufferInfo(video, pos, hls.config.maxBufferHole).end - pos) / playbackRate; 1377 // consider emergency switch down only if we have less than 2 frag buffered AND 1378 // time to finish loading current fragment is bigger than buffer starvation delay 1379 // ie if we risk buffer starvation if bw does not increase quickly 1380 if ((bufferStarvationDelay < (2 * frag.duration / playbackRate)) && (fragLoadedDelay > bufferStarvationDelay)) { 1381 var fragLevelNextLoadedDelay = void 0, nextLoadLevel = void 0; 1382 // lets iterate through lower level and try to find the biggest one that could avoid rebuffering 1383 // we start from current level - 1 and we step down , until we find a matching level 1384 for (nextLoadLevel = frag.level - 1; nextLoadLevel > minAutoLevel; nextLoadLevel--) { 1385 // compute time to load next fragment at lower level 1386 // 0.8 : consider only 80% of current bw to be conservative 1387 // 8 = bits per byte (bps/Bps) 1388 var levelNextBitrate = levels[nextLoadLevel].realBitrate ? Math.max(levels[nextLoadLevel].realBitrate, levels[nextLoadLevel].bitrate) : levels[nextLoadLevel].bitrate; 1389 fragLevelNextLoadedDelay = frag.duration * levelNextBitrate / (8 * 0.8 * loadRate); 1390 if (fragLevelNextLoadedDelay < bufferStarvationDelay) { 1391 // we found a lower level that be rebuffering free with current estimated bw ! 1392 break; 1393 } 1394 } 1395 // only emergency switch down if it takes less time to load new fragment at lowest level instead 1396 // of finishing loading current one ... 1397 if (fragLevelNextLoadedDelay < fragLoadedDelay) { 1398 logger_1.logger.warn("loading too slow, abort fragment loading and switch to level " + nextLoadLevel + ":fragLoadedDelay[" + nextLoadLevel + "]<fragLoadedDelay[" + (frag.level - 1) + "];bufferStarvationDelay:" + fragLevelNextLoadedDelay.toFixed(1) + "<" + fragLoadedDelay.toFixed(1) + ":" + bufferStarvationDelay.toFixed(1)); 1399 // force next load level in auto mode 1400 hls.nextLoadLevel = nextLoadLevel; 1401 // update bw estimate for this fragment before cancelling load (this will help reducing the bw) 1402 this._bwEstimator.sample(requestDelay, stats.loaded); 1403 // abort fragment loading 1404 loader.abort(); 1405 // stop abandon rules timer 1406 this.clearTimer(); 1407 hls.trigger(events_1.default.FRAG_LOAD_EMERGENCY_ABORTED, { frag: frag, stats: stats }); 1408 } 1409 } 1410 } 1411 } 1412 }; 1413 AbrController.prototype.onFragLoaded = function (data) { 1414 var frag = data.frag; 1415 if (frag.type === 'main' && Number.isFinite(frag.sn)) { 1416 // stop monitoring bw once frag loaded 1417 this.clearTimer(); 1418 // store level id after successful fragment load 1419 this.lastLoadedFragLevel = frag.level; 1420 // reset forced auto level value so that next level will be selected 1421 this._nextAutoLevel = -1; 1422 // compute level average bitrate 1423 if (this.hls.config.abrMaxWithRealBitrate) { 1424 var level = this.hls.levels[frag.level]; 1425 var loadedBytes = (level.loaded ? level.loaded.bytes : 0) + data.stats.loaded; 1426 var loadedDuration = (level.loaded ? level.loaded.duration : 0) + data.frag.duration; 1427 level.loaded = { bytes: loadedBytes, duration: loadedDuration }; 1428 level.realBitrate = Math.round(8 * loadedBytes / loadedDuration); 1429 } 1430 // if fragment has been loaded to perform a bitrate test, 1431 if (data.frag.bitrateTest) { 1432 var stats = data.stats; 1433 stats.tparsed = stats.tbuffered = stats.tload; 1434 this.onFragBuffered(data); 1435 } 1436 } 1437 }; 1438 AbrController.prototype.onFragBuffered = function (data) { 1439 var stats = data.stats; 1440 var frag = data.frag; 1441 // only update stats on first frag buffering 1442 // if same frag is loaded multiple times, it might be in browser cache, and loaded quickly 1443 // and leading to wrong bw estimation 1444 // on bitrate test, also only update stats once (if tload = tbuffered == on FRAG_LOADED) 1445 if (stats.aborted !== true && frag.type === 'main' && Number.isFinite(frag.sn) && ((!frag.bitrateTest || stats.tload === stats.tbuffered))) { 1446 // use tparsed-trequest instead of tbuffered-trequest to compute fragLoadingProcessing; rationale is that buffer appending only happens once media is attached 1447 // in case we use config.startFragPrefetch while media is not attached yet, fragment might be parsed while media not attached yet, but it will only be buffered on media attached 1448 // as a consequence it could happen really late in the process. meaning that appending duration might appears huge ... leading to underestimated throughput estimation 1449 var fragLoadingProcessingMs = stats.tparsed - stats.trequest; 1450 logger_1.logger.log("latency/loading/parsing/append/kbps:" + Math.round(stats.tfirst - stats.trequest) + "/" + Math.round(stats.tload - stats.tfirst) + "/" + Math.round(stats.tparsed - stats.tload) + "/" + Math.round(stats.tbuffered - stats.tparsed) + "/" + Math.round(8 * stats.loaded / (stats.tbuffered - stats.trequest))); 1451 this._bwEstimator.sample(fragLoadingProcessingMs, stats.loaded); 1452 stats.bwEstimate = this._bwEstimator.getEstimate(); 1453 // if fragment has been loaded to perform a bitrate test, (hls.startLevel = -1), store bitrate test delay duration 1454 if (frag.bitrateTest) {
1455 this.bitrateTestDelay = fragLoadingProcessingMs / 1000; 1456 } 1457 else { 1458 this.bitrateTestDelay = 0; 1459 } 1460 } 1461 }; 1462 AbrController.prototype.onError = function (data) { 1463 // stop timer in case of frag loading error 1464 switch (data.details) { 1465 case errors_1.ErrorDetails.FRAG_LOAD_ERROR: 1466 case errors_1.ErrorDetails.FRAG_LOAD_TIMEOUT: 1467 this.clearTimer(); 1468 break; 1469 default: 1470 break; 1471 } 1472 }; 1473 AbrController.prototype.clearTimer = function () { 1474 clearInterval(this.timer); 1475 this.timer = null; 1476 }; 1477 Object.defineProperty(AbrController.prototype, "nextAutoLevel", { 1478 // return next auto level 1479 get: function () { 1480 var forcedAutoLevel = this._nextAutoLevel; 1481 var bwEstimator = this._bwEstimator; 1482 // in case next auto level has been forced, and bw not available or not reliable, return forced value 1483 if (forcedAutoLevel !== -1 && (!bwEstimator || !bwEstimator.canEstimate())) { 1484 return forcedAutoLevel; 1485 } 1486 // compute next level using ABR logic 1487 var nextABRAutoLevel = this._nextABRAutoLevel; 1488 // if forced auto level has been defined, use it to cap ABR computed quality level 1489 if (forcedAutoLevel !== -1) { 1490 nextABRAutoLevel = Math.min(forcedAutoLevel, nextABRAutoLevel); 1491 } 1492 return nextABRAutoLevel; 1493 }, 1494 set: function (nextLevel) { 1495 this._nextAutoLevel = nextLevel; 1496 }, 1497 enumerable: true, 1498 configurable: true 1499 }); 1500 Object.defineProperty(AbrController.prototype, "_nextABRAutoLevel", { 1501 get: function () { 1502 var hls = this.hls, maxAutoLevel = hls.maxAutoLevel, levels = hls.levels, config = hls.config, minAutoLevel = hls.minAutoLevel; 1503 var video = hls.media, currentLevel = this.lastLoadedFragLevel, currentFragDuration = this.fragCurrent ? this.fragCurrent.duration : 0, pos = (video ? video.currentTime : 0), 1504 // playbackRate is the absolute value of the playback rate; if video.playbackRate is 0, we use 1 to load as 1505 // if we're playing back at the normal rate. 1506 playbackRate = ((video && (video.playbackRate !== 0)) ? Math.abs(video.playbackRate) : 1.0), avgbw = this._bwEstimator ? this._bwEstimator.getEstimate() : config.abrEwmaDefaultEstimate, 1507 // bufferStarvationDelay is the wall-clock time left until the playback buffer is exhausted. 1508 bufferStarvationDelay = (buffer_helper_1.BufferHelper.bufferInfo(video, pos, config.maxBufferHole).end - pos) / playbackRate; 1509 // First, look to see if we can find a level matching with our avg bandwidth AND that could also guarantee no rebuffering at all 1510 var bestLevel = this._findBestLevel(currentLevel, currentFragDuration, avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay, config.abrBandWidthFactor, config.abrBandWidthUpFactor, levels); 1511 if (bestLevel >= 0) { 1512 return bestLevel; 1513 } 1514 else { 1515 logger_1.logger.trace('rebuffering expected to happen, lets try to find a quality level minimizing the rebuffering'); 1516 // not possible to get rid of rebuffering ... let's try to find level that will guarantee less than maxStarvationDelay of rebuffering 1517 // if no matching level found, logic will return 0 1518 var maxStarvationDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxStarvationDelay) : config.maxStarvationDelay, bwFactor = config.abrBandWidthFactor, bwUpFactor = config.abrBandWidthUpFactor; 1519 if (bufferStarvationDelay === 0) { 1520 // in case buffer is empty, let's check if previous fragment was loaded to perform a bitrate test 1521 var bitrateTestDelay = this.bitrateTestDelay; 1522 if (bitrateTestDelay) { 1523 // if it is the case, then we need to adjust our max starvation delay using maxLoadingDelay config value 1524 // max video loading delay used in automatic start level selection : 1525 // in that mode ABR controller will ensure that video loading time (ie the time to fetch the first fragment at lowest quality level + 1526 // the time to fetch the fragment at the appropriate quality level is less than ```maxLoadingDelay``` ) 1527 // cap maxLoadingDelay and ensure it is not bigger 'than bitrate test' frag duration 1528 var maxLoadingDelay = currentFragDuration ? Math.min(currentFragDuration, config.maxLoadingDelay) : config.maxLoadingDelay; 1529 maxStarvationDelay = maxLoadingDelay - bitrateTestDelay; 1530 logger_1.logger.trace("bitrate test took " + Math.round(1000 * bitrateTestDelay) + "ms, set first fragment max fetchDuration to " + Math.round(1000 * maxStarvationDelay) + " ms"); 1531 // don't use conservative factor on bitrate test 1532 bwFactor = bwUpFactor = 1; 1533 } 1534 } 1535 bestLevel = this._findBestLevel(currentLevel, currentFragDuration, avgbw, minAutoLevel, maxAutoLevel, bufferStarvationDelay + maxStarvationDelay, bwFactor, bwUpFactor, levels); 1536 return Math.max(bestLevel, 0); 1537 } 1538 }, 1539 enumerable: true, 1540 configurable: true 1541 }); 1542 AbrController.prototype._findBestLevel = function (currentLevel, currentFragDuration, currentBw, minAutoLevel, maxAutoLevel, maxFetchDuration, bwFactor, bwUpFactor, levels) { 1543 for (var i = maxAutoLevel; i >= minAutoLevel; i--) { 1544 var levelInfo = levels[i]; 1545 if (!levelInfo) { 1546 continue; 1547 } 1548 var levelDetails = levelInfo.details, avgDuration = levelDetails ? levelDetails.totalduration / levelDetails.fragments.length : currentFragDuration, live = levelDetails ? levelDetails.live : false, adjustedbw = void 0; 1549 // follow algorithm captured from stagefright : 1550 // https://android.googlesource.com/platform/frameworks/av/+/master/media/libstagefright/httplive/LiveSession.cpp 1551 // Pick the highest bandwidth stream below or equal to estimated bandwidth. 1552 // consider only 80% of the available bandwidth, but if we are switching up, 1553 // be even more conservative (70%) to avoid overestimating and immediately 1554 // switching back. 1555 if (i <= currentLevel) { 1556 adjustedbw = bwFactor * currentBw; 1557 } 1558 else { 1559 adjustedbw = bwUpFactor * currentBw; 1560 } 1561 var bitrate = levels[i].realBitrate ? Math.max(levels[i].realBitrate, levels[i].bitrate) : levels[i].bitrate, fetchDuration = bitrate * avgDuration / adjustedbw; 1562 logger_1.logger.trace("level/adjustedbw/bitrate/avgDuration/maxFetchDuration/fetchDuration: " + i + "/" + Math.round(adjustedbw) + "/" + bitrate + "/" + avgDuration + "/" + maxFetchDuration + "/" + fetchDuration);
1563 // if adjusted bw is greater than level bitrate AND 1564 if (adjustedbw > bitrate && 1565 // fragment fetchDuration unknown OR live stream OR fragment fetchDuration less than max allowed fetch duration, then this level matches 1566 // 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 ... 1567 // special case to support startLevel = -1 (bitrateTest) on live streams : in that case we should not exit loop so that _findBestLevel will return -1 1568 (!fetchDuration || (live && !this.bitrateTestDelay) || fetchDuration < maxFetchDuration)) { 1569 // as we are looping from highest to lowest, this will return the best achievable quality level 1570 return i; 1571 } 1572 } 1573 // not enough time budget even with quality level 0 ... rebuffering might happen 1574 return -1; 1575 }; 1576 return AbrController; 1577}(event_handler_1.default)); 1578exports.default = AbrController; 1579 1580/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 1581 1582/***/ }), 1583
1584/***/ "./src/controller/audio-stream-controller.js": 1585/*!***************************************************!*\ 1586 !*** ./src/controller/audio-stream-controller.js ***! 1587 \***************************************************/ 1588/*! no static exports found */ 1589/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 1590/***/ (function(module, exports, __webpack_require__) { 1591 1592"use strict"; 1593/* WEBPACK VAR INJECTION */(function(Number) { 1594/* 1595 * Audio Stream Controller 1596*/ 1597var __extends = (this && this.__extends) || (function () { 1598 var extendStatics = Object.setPrototypeOf || 1599 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 1600 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 1601 return function (d, b) { 1602 extendStatics(d, b); 1603 function __() { this.constructor = d; } 1604 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 1605 }; 1606})(); 1607Object.defineProperty(exports, "__esModule", { value: true }); 1608var binary_search_1 = __webpack_require__(/*! ../utils/binary-search */ "./src/utils/binary-search.js"); 1609var buffer_helper_1 = __webpack_require__(/*! ../utils/buffer-helper */ "./src/utils/buffer-helper.js"); 1610var demuxer_1 = __webpack_require__(/*! ../demux/demuxer */ "./src/demux/demuxer.js"); 1611var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 1612var LevelHelper = __webpack_require__(/*! ./level-helper */ "./src/controller/level-helper.js"); 1613var time_ranges_1 = __webpack_require__(/*! ../utils/time-ranges */ "./src/utils/time-ranges.js"); 1614var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 1615var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 1616var discontinuities_1 = __webpack_require__(/*! ../utils/discontinuities */ "./src/utils/discontinuities.js"); 1617var fragment_tracker_1 = __webpack_require__(/*! ./fragment-tracker */ "./src/controller/fragment-tracker.js"); 1618var fragment_1 = __webpack_require__(/*! ../loader/fragment */ "./src/loader/fragment.js"); 1619var base_stream_controller_1 = __webpack_require__(/*! ./base-stream-controller */ "./src/controller/base-stream-controller.js"); 1620var performance = window.performance; 1621var TICK_INTERVAL = 100; // how often to tick in ms 1622var AudioStreamController = /** @class */ (function (_super) { 1623 __extends(AudioStreamController, _super); 1624 function AudioStreamController(hls, fragmentTracker) { 1625 var _this = _super.call(this, hls, events_1.default.MEDIA_ATTA
1625CHED, events_1.default.MEDIA_DETACHING, events_1.default.AUDIO_TRACKS_UPDATED, events_1.default.AUDIO_TRACK_SWITCHING, events_1.default.AUDIO_TRACK_LOADED, events_1.default.KEY_LOADED, events_1.default.FRAG_LOADED, events_1.default.FRAG_PARSING_INIT_SEGMENT, events_1.default.FRAG_PARSING_DATA, events_1.default.FRAG_PARSED, events_1.default.ERROR, events_1.default.BUFFER_RESET, events_1.default.BUFFER_CREATED, events_1.default.BUFFER_APPENDED, events_1.default.BUFFER_FLUSHED, events_1.default.INIT_PTS_FOUND) || this; 1626 _this.fragmentTracker = fragmentTracker; 1627 _this.config = hls.config; 1628 _this.audioCodecSwap = false; 1629 _this._state = base_stream_controller_1.State.STOPPED; 1630 _this.initPTS = []; 1631 _this.waitingFragment = null; 1632 _this.videoTrackCC = null; 1633 return _this; 1634 } 1635 // Signal that video PTS was found 1636 AudioStreamController.prototype.onInitPtsFound = function (data) { 1637 var demuxerId = data.id, cc = data.frag.cc, initPTS = data.initPTS; 1638 if (demuxerId === 'main') { 1639 // Always update the new INIT PTS 1640 // Can change due level switch 1641 this.initPTS[cc] = initPTS; 1642 this.videoTrackCC = cc; 1643 logger_1.logger.log("InitPTS for cc: " + cc + " found from video track: " + initPTS); 1644 // If we are waiting we need to demux/remux the waiting frag
1645 // With the new initPTS 1646 if (this.state === base_stream_controller_1.State.WAITING_INIT_PTS) { 1647 this.tick(); 1648 } 1649 } 1650 }; 1651 AudioStreamController.prototype.startLoad = function (startPosition) { 1652 if (this.tracks) { 1653 var lastCurrentTime = this.lastCurrentTime; 1654 this.stopLoad(); 1655 this.setInterval(TICK_INTERVAL); 1656 this.fragLoadError = 0; 1657 if (lastCurrentTime > 0 && startPosition === -1) { 1658 logger_1.logger.log("audio:override startPosition with lastCurrentTime @" + lastCurrentTime.toFixed(3)); 1659 this.state = base_stream_controller_1.State.IDLE; 1660 } 1661 else { 1662 this.lastCurrentTime = this.startPosition ? this.startPosition : startPosition; 1663 this.state = base_stream_controller_1.State.STARTING; 1664 } 1665 this.nextLoadPosition = this.startPosition = this.lastCurrentTime; 1666 this.tick(); 1667 } 1668 else { 1669 this.startPosition = startPosition; 1670 this.state = base_stream_controller_1.State.STOPPED; 1671 } 1672 }; 1673 Object.defineProperty(AudioStreamController.prototype, "state", { 1674 get: function () { 1675 return this._state; 1676 }, 1677 set: function (nextState) { 1678 if (this.state !== nextState) { 1679 var previousState = this.state; 1680 this._state = nextState; 1681 logger_1.logger.log("audio stream:" + previousState + "->" + nextState); 1682 } 1683 }, 1684 enumerable: true, 1685 configurable: true 1686 }); 1687 AudioStreamController.prototype.doTick = function () { 1688 var pos, track, trackDetails, hls = this.hls, config = hls.config; 1689 // logger.log('audioStream:' + this.state); 1690 switch (this.state) { 1691 case base_stream_controller_1.State.ERROR: 1692 // don't do anything in error state to avoid breaking further ... 1693 case base_stream_controller_1.State.PAUSED: 1694 // don't do anything in paused state either ... 1695 case base_stream_controller_1.State.BUFFER_FLUSHING: 1696 break; 1697 case base_stream_controller_1.State.STARTING: 1698 this.state = base_stream_controller_1.State.WAITING_TRACK; 1699 this.loadedmetadata = false; 1700 break; 1701 case base_stream_controller_1.State.IDLE: 1702 var tracks = this.tracks; 1703 // audio tracks not received => exit loop 1704 if (!tracks) { 1705 break; 1706 } 1707 // if video not attached AND 1708 // start fragment already requested OR start frag prefetch disable 1709 // exit loop 1710 // => if media not attached but start frag prefetch is enabled and start frag not requested yet, we will not exit loop 1711 if (!this.media && 1712 (this.startFragRequested || !config.startFragPrefetch)) { 1713 break; 1714 } 1715 // determine next candidate fragment to be loaded, based on current position and 1716 // end of buffer position 1717 // if we have not yet loaded any fragment, start loading from start position 1718 if (this.loadedmetadata) { 1719 pos = this.media.currentTime; 1720 } 1721 else { 1722 pos = this.nextLoadPosition; 1723 if (pos === undefined) { 1724 break; 1725 } 1726 } 1727 var media = this.mediaBuffer ? this.mediaBuffer : this.media, videoBuffer = this.videoBuffer ? this.videoBuffer : this.media, bufferInfo = buffer_helper_1.BufferHelper.bufferInfo(media, pos, config.maxBufferHole), mainBufferInfo = buffer_helper_1.BufferHelper.bufferInfo(videoBuffer, pos, config.maxBufferHole), bufferLen = bufferInfo.len, bufferEnd_1 = bufferInfo.end, fragPrevious = this.fragPrevious, 1728 // ensure we buffer at least config.maxBufferLength (default 30s) or config.max
vendor: 5,304 bytes, lines 1728-1800
1728MaxBufferLength (default: 600s) 1729 // whichever is smaller. 1730 // once we reach that threshold, don't buffer more than video (mainBufferInfo.len) 1731 maxConfigBuffer = Math.min(config.maxBufferLength, config.maxMaxBufferLength), maxBufLen = Math.max(maxConfigBuffer, mainBufferInfo.len), audioSwitch = this.audioSwitch, trackId = this.trackId; 1732 // if buffer length is less than maxBufLen try to load a new fragment 1733 if ((bufferLen < maxBufLen || audioSwitch) && trackId < tracks.length) { 1734 trackDetails = tracks[trackId].details; 1735 // if track info not retrieved yet, switch state and wait for track retrieval 1736 if (typeof trackDetails === 'undefined') { 1737 this.state = base_stream_controller_1.State.WAITING_TRACK; 1738 break; 1739 } 1740 if (!audioSwitch && this._streamEnded(bufferInfo, trackDetails)) { 1741 this.hls.trigger(events_1.default.BUFFER_EOS, { type: 'audio' }); 1742 this.state = base_stream_controller_1.State.ENDED; 1743 return; 1744 } 1745 // find fragment index, contiguous with end of buffer position 1746 var fragments = trackDetails.fragments, fragLen = fragments.length, start = fragments[0].start, end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration, frag = void 0; 1747 // When switching audio track, reload audio as close as possible to currentTime 1748 if (audioSwitch) { 1749 if (trackDetails.live && !trackDetails.PTSKnown) { 1750 logger_1.logger.log('switching audiotrack, live stream, unknown PTS,load first fragment'); 1751 bufferEnd_1 = 0; 1752 } 1753 else { 1754 bufferEnd_1 = pos; 1755 // if currentTime (pos) is less than alt audio playlist start time, it means that alt audio is ahead of currentTime 1756 if (trackDetails.PTSKnown && pos < start) { 1757 // if everything is buffered from pos to start or if audio buffer upfront, let's seek to start 1758 if (bufferInfo.end > start || bufferInfo.nextStart) { 1759 logger_1.logger.log('alt audio track ahead of main track, seek to start of alt audio track'); 1760 this.media.currentTime = start + 0.05; 1761 } 1762 else { 1763 return; 1764 } 1765 } 1766 } 1767 } 1768 if (trackDetails.initSegment && !trackDetails.initSegment.data) { 1769 frag = trackDetails.initSegment; 1770 } // eslint-disable-line brace-style 1771 // if bufferEnd before start of playlist, load first fragment 1772 else if (bufferEnd_1 <= start) { 1773 frag = fragments[0]; 1774 if (this.videoTrackCC !== null && frag.cc !== this.videoTrackCC) { 1775 // Ensure we find a fragment which matches the continuity of the video track 1776 frag = discontinuities_1.findFragWithCC(fragments, this.videoTrackCC); 1777 } 1778 if (trackDetails.live && frag.loadIdx && frag.loadIdx === this.fragLoadIdx) { 1779 // we just loaded this first fragment, and we are still lagging behind the start of the live playlist 1780 // let's force seek to start 1781 var nextBuffered = bufferInfo.nextStart ? bufferInfo.nextStart : start; 1782 logger_1.logger.log("no alt audio available @currentTime:" + this.media.currentTime + ", seeking @" + (nextBuffered + 0.05)); 1783 this.media.currentTime = nextBuffered + 0.05; 1784 return; 1785 } 1786 } 1787 else { 1788 var foundFrag = void 0; 1789 var maxFragLookUpTolerance_1 = config.maxFragLookUpTolerance; 1790 var fragNext = fragPrevious ? fragments[fragPrevious.sn - fragments[0].sn + 1] : undefined; 1791 var fragmentWithinToleranceTest = function (candidate) { 1792 // offset should be within fragment boundary - config.maxFragLookUpTolerance 1793 // this is to cope with situations like 1794 // bufferEnd = 9.991 1795 // frag[Ã] : [0,10] 1796 // frag[1] : [10,20] 1797 // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here 1798 // frag start frag start+duration 1799 // |-----------------------------| 1800 // <---> <--->
1801 // ...--------><-----------------------------><---------.... 1802 // previous frag matching fragment next frag 1803 // return -1 return 0 return 1 1804 // logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`); 1805 // Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments 1806 var candidateLookupTolerance = Math.min(maxFragLookUpTolerance_1, candidate.duration); 1807 if ((candidate.start + candidate.duration - candidateLookupTolerance) <= bufferEnd_1) { 1808 return 1; 1809 } 1810 else if (candidate.start - candidateLookupTolerance > bufferEnd_1 && candidate.start) { 1811 // if maxFragLookUpTolerance will have negative value then don't return -1 for first element 1812 return -1; 1813 } 1814 return 0; 1815 }; 1816 if (bufferEnd_1 < end) { 1817 if (bufferEnd_1 > end - maxFragLookUpTolerance_1) { 1818 maxFragLookUpTolerance_1 = 0; 1819 } 1820 // Prefer the next fragment if it's within tolerance 1821 if (fragNext && !fragmentWithinToleranceTest(fragNext)) { 1822 foundFrag = fragNext; 1823 } 1824 else { 1825 foundFrag = binary_search_1.default.search(fragments, fragmentWithinToleranceTest); 1826 } 1827 } 1828 else { 1829 // reach end of playlist 1830 foundFrag = fragments[fragLen - 1]; 1831 } 1832 if (foundFrag) { 1833 frag = foundFrag; 1834 start = foundFrag.start; 1835 // logger.log('find SN matching with pos:' + bufferEnd + ':' + frag.sn); 1836 if (fragPrevious && frag.level === fragPrevious.level && frag.sn === fragPrevious.sn) { 1837 if (frag.sn < trackDetails.endSN) { 1838 frag = fragments[frag.sn + 1 - trackDetails.startSN]; 1839 logger_1.logger.log("SN just loaded, load next one: " + frag.sn); 1840 } 1841 else { 1842 frag = null; 1843 } 1844 } 1845 } 1846 } 1847 if (frag) { 1848 // logger.log(' loading frag ' + i +',pos/bufEnd:' + pos.toFixed(3) + '/' + bufferEnd.toFixed(3)); 1849 if (frag.encrypted) { 1850 logger_1.logger.log("Loading key for " + frag.sn + " of [" + trackDetails.startSN + " ," + trackDetails.endSN + "],track " + trackId); 1851 this.state = base_stream_controller_1.State.KEY_LOADING; 1852 hls.trigger(events_1.default.KEY_LOADING, { frag: frag }); 1853 } 1854 else { 1855 logger_1.logger.log("Loading " + frag.sn + ", cc: " + frag.cc + " of [" + trackDetails.startSN + " ," + trackDetails.endSN + "],track " + trackId + ", currentTime:" + pos + ",bufferEnd:" + bufferEnd_1.toFixed(3)); 1856 // only load if fragment is not loaded or if in audio switch 1857 // we force a frag loading in audio switch as fragment tracker might not have evicted previous frags in case of quick audio switch 1858 this.fragCurrent = frag; 1859 if (audioSwitch || this.fragmentTracker.getState(frag) === fragment_tracker_1.FragmentState.NOT_LOADED) { 1860 this.startFragRequested = true; 1861 if (Number.isFinite(frag.sn)) { 1862 this.nextLoadPosition = frag.start + frag.duration; 1863 } 1864 hls.trigger(events_1.default.FRAG_LOADING, { frag: frag }); 1865 this.state = base_stream_controller_1.State.FRAG_LOADING; 1866 } 1867 } 1868 } 1869 } 1870 break; 1871 case base_stream_controller_1.State.WAITING_TRACK: 1872 track = this.tracks[this.trackId]; 1873 // check if playlist is already loaded 1874 if (track && track.details) { 1875 this.state = base_stream_controller_1.State.IDLE; 1876 } 1877 break; 1878 case base_stream_controller_1.State.FRAG_LOADING_WAITING_RETRY: 1879 var now = performance.now(); 1880 var retryDate = this.retryDate; 1881 media = this.media; 1882 var isSeeking = media && media.seeking; 1883 // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading 1884 if (!retryDate || (now >= retryDate) || isSeeking) { 1885 logger_1.logger.log('audioStreamController: retryDate reached, switch back to IDLE state'); 1886 this.state = base_stream_controller_1.State.IDLE; 1887 } 1888 break; 1889 case base_stream_controller_1.State.WAITING_INIT_PTS: 1890 var videoTrackCC = this.videoTrackCC; 1891 if (this.initPTS[videoTrackCC] === undefined) { 1892 break; 1893 } 1894 // Ensure we don't get stuck in the WAITING_INIT_PTS state if the waiting frag CC doesn't match any initPTS 1895 var waitingFrag = this.waitingFragment; 1896 if (waitingFrag) { 1897 var waitingFragCC = waitingFrag.frag.cc; 1898 if (videoTrackCC !== waitingFragCC) { 1899 track = this.tracks[this.trackId]; 1900 if (track.details && track.details.live) { 1901 logger_1.logger.warn("Waiting fragment CC (" + waitingFragCC + ") does not match video track CC (" + videoTrackCC + ")"); 1902 this.waitingFragment = null; 1903 this.state = base_stream_controller_1.State.IDLE; 1904 } 1905 } 1906 else { 1907 this.state = base_stream_controller_1.State.FRAG_LOADING; 1908 this.onFragLoaded(this.waitingFragment); 1909 this.waitingFragment = null; 1910 } 1911 } 1912 else { 1913 this.state = base_stream_controller_1.State.IDLE; 1914 } 1915 break; 1916 case base_stream_controller_1.State.STOPPED: 1917 case base_stream_controller_1.State.FRAG_LOADING: 1918 case base_stream_controller_1.State.PARSING: 1919 case base_stream_controller_1.State.PARSED: 1920 case base_stream_controller_1.State.ENDED: 1921 break; 1922 default: 1923 break; 1924 } 1925 }; 1926 AudioStreamController.prototype.onMediaAttached = function (data) { 1927 var media = this.media = this.mediaBuffer = data.media; 1928 this.onvseeking = this.onMediaSeeking.bind(this); 1929 this.onvended = this.onMediaEnded.bind(this); 1930 media.addEventListener('seeking', this.onvseeking); 1931 media.addEventListener('ended', this.onvended); 1932 var config = this.config; 1933 if (this.tracks && config.autoStartLoad) { 1934 this.startLoad(config.startPosition); 1935 } 1936 }; 1937 AudioStreamController.prototype.onMediaDetaching = function () { 1938 var media = this.media; 1939 if (media && media.ended) { 1940 logger_1.logger.log('MSE detaching and video ended, reset startPosition'); 1941 this.startPosition = this.lastCurrentTime = 0; 1942 } 1943 // remove video listeners 1944 if (media) { 1945 media.removeEventListener('seeking', this.onvseeking); 1946 media.removeEventListener('ended', this.onvended); 1947 this.onvseeking = this.onvseeked = this.onvended = null; 1948 } 1949 this.media = this.mediaBuffer = this.videoBuffer = null; 1950 this.loadedmetadata = false;
1951 this.stopLoad(); 1952 }; 1953 AudioStreamController.prototype.onAudioTracksUpdated = function (data) { 1954 logger_1.logger.log('audio tracks updated'); 1955 this.tracks = data.audioTracks; 1956 }; 1957 AudioStreamController.prototype.onAudioTrackSwitching = function (data) { 1958 // if any URL found on new audio track, it is an alternate audio track 1959 var altAudio = !!data.url; 1960 this.trackId = data.id; 1961 this.fragCurrent = null; 1962 this.state = base_stream_controller_1.State.PAUSED; 1963 this.waitingFragment = null; 1964 // destroy useless demuxer when switching audio to main 1965 if (!altAudio) { 1966 if (this.demuxer) { 1967 this.demuxer.destroy(); 1968 this.demuxer = null; 1969 } 1970 } 1971 else { 1972 // switching to audio track, start timer if not already started 1973 this.setInterval(TICK_INTERVAL); 1974 } 1975 // should we switch tracks ? 1976 if (altAudio) { 1977 this.audioSwitch = true; 1978 // main audio track are handled by stream-controller, just do something if switching to alt audio track 1979 this.state = base_stream_controller_1.State.IDLE; 1980 } 1981 this.tick(); 1982 }; 1983 AudioStreamController.prototype.onAudioTrackLoaded = function (data) { 1984 var newDetails = data.details, trackId = data.id, track = this.tracks[trackId], duration = newDetails.totalduration, sliding = 0; 1985 logger_1.logger.log("track " + trackId + " loaded [" + newDetails.startSN + "," + newDetails.endSN + "],duration:" + duration); 1986 if (newDetails.live) { 1987 var curDetails = track.details; 1988 if (curDetails && newDetails.fragments.length > 0) { 1989 // we already have details for that level, merge them 1990 LevelHelper.mergeDetails(curDetails, newDetails); 1991 sliding = newDetails.fragments[0].start; 1992 // TODO 1993 // this.liveSyncPosition = this.computeLivePosition(sliding, curDetails); 1994 if (newDetails.PTSKnown) { 1995 logger_1.logger.log("live audio playlist sliding:" + sliding.toFixed(3)); 1996 } 1997 else { 1998 logger_1.logger.log('live audio playlist - outdated PTS, unknown sliding'); 1999 } 2000 } 2001 else { 2002 newDetails.PTSKnown = false; 2003 logger_1.logger.log('live audio playlist - first load, unknown sliding'); 2004 } 2005 } 2006 else { 2007 newDetails.PTSKnown = false; 2008 } 2009 track.details = newDetails; 2010 // compute start position 2011 if (!this.startFragRequested) { 2012 // compute start position if set to -1. use it straight away if value is defined 2013 if (this.startPosition === -1) { 2014 // first, check if start time offset has been set in playlist, if yes, use this value 2015 var startTimeOffset = newDetails.startTimeOffset; 2016 if (Number.isFinite(startTimeOffset)) { 2017 logger_1.logger.log("start time offset found in playlist, adjust startPosition to " + startTimeOffset); 2018 this.startPosition = startTimeOffset; 2019 } 2020 else { 2021 this.startPosition = 0; 2022 } 2023 } 2024 this.nextLoadPosition = this.startPosition; 2025 } 2026 // only switch batck to IDLE state if we were waiting for track to start downloading a new fragment 2027 if (this.state === base_stream_controller_1.State.WAITING_TRACK) { 2028 this.state = base_stream_controller_1.State.IDLE; 2029 } 2030 // trigger handler right now 2031 this.tick(); 2032 }; 2033 AudioStreamController.prototype.onKeyLoaded = function () { 2034 if (this.state === base_stream_controller_1.State.KEY_LOADING) { 2035 this.state = base_stream_controller_1.State.IDLE; 2036 this.tick(); 2037 } 2038 }; 2039 AudioStreamController.prototype.onFragLoaded = function (data) { 2040 var fragCurrent = this.fragCurrent, fragLoaded = data.frag; 2041 if (this.state === base_stream_controller_1.State.FRAG_LOADING && 2042 fragCurrent && 2043 fragLoaded.type === 'audio' && 2044 fragLoaded.level === fragCurrent.level && 2045 fragLoaded.sn === fragCurrent.sn) { 2046 var track = this.tracks[this.trackId], details = track.details, duration = details.totalduration, trackId = fragCurrent.level, sn = fragCurrent.sn, cc = fragCurrent.cc, audioCodec = this.config.defaultAudioCodec || track.audioCodec || 'mp4a.40.2', stats = this.stats = data.stats; 2047 if (sn === 'initSegment') { 2048 this.state = base_stream_controller_1.State.IDLE; 2049 stats.tparsed = stats.tbuffered = performance.now(); 2050 details.initSegment.data = data.payload; 2051 this.hls.trigger(events_1.default.FRAG_BUFFERED, { stats: stats, frag: fragCurrent, id: 'audio' }); 2052 this.tick(); 2053 } 2054 else { 2055 this.state = base_stream_controller_1.State.PARSING; 2056 // transmux the MPEG-TS data to ISO-BMFF segments 2057 this.appended = false;
2058 if (!this.demuxer) { 2059 this.demuxer = new demuxer_1.default(this.hls, 'audio'); 2060 } 2061 // Check if we have video initPTS 2062 // If not we need to wait for it 2063 var initPTS = this.initPTS[cc]; 2064 var initSegmentData = details.initSegment ? details.initSegment.data : []; 2065 if (details.initSegment || initPTS !== undefined) { 2066 this.pendingBuffering = true; 2067 logger_1.logger.log("Demuxing " + sn + " of [" + details.startSN + " ," + details.endSN + "],track " + trackId); 2068 // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live) 2069 var accurateTimeOffset = false; // details.PTSKnown || !details.live; 2070 this.demuxer.push(data.payload, initSegmentData, audioCodec, null, fragCurrent, duration, accurateTimeOffset, initPTS); 2071 } 2072 else { 2073 logger_1.logger.log("unknown video PTS for continuity counter " + cc + ", waiting for video PTS before demuxing audio frag " + sn + " of [" + details.startSN + " ," + details.endSN + "],track " + trackId); 2074 this.waitingFragment = data; 2075 this.state = base_stream_controller_1.State.WAITING_INIT_PTS; 2076 } 2077 } 2078 } 2079 this.fragLoadError = 0; 2080 }; 2081 AudioStreamController.prototype.onFragParsingInitSegment = function (data) { 2082 var fragCurrent = this.fragCurrent; 2083 var fragNew = data.frag; 2084 if (fragCurrent && 2085 data.id === 'audio' && 2086 fragNew.sn === fragCurrent.sn && 2087 fragNew.level === fragCurrent.level && 2088 this.state === base_stream_controller_1.State.PARSING) { 2089 var tracks = data.tracks, track = void 0; 2090 // delete any video track found on audio demuxer 2091 if (tracks.video) { 2092 delete tracks.video; 2093 } 2094 // include levelCodec in audio and video tracks 2095 track = tracks.audio; 2096 if (track) { 2097 track.levelCodec = track.codec; 2098 track.id = data.id; 2099 this.hls.trigger(events_1.default.BUFFER_CODECS, tracks); 2100 logger_1.logger.log("audio track:audio,container:" + track.container + ",codecs[level/parsed]=[" + track.levelCodec + "/" + track.codec + "]"); 2101 var initSegment = track.initSegment; 2102 if (initSegment) { 2103 var appendObj = { type: 'audio', data: initSegment, parent: 'audio', content: 'initSegment' }; 2104 if (this.audioSwitch) { 2105 this.pendingData = [appendObj]; 2106 } 2107 else { 2108 this.appended = true; 2109 // arm pending Buffering flag before appending a segment 2110 this.pendingBuffering = true; 2111 this.hls.trigger(events_1.default.BUFFER_APPENDING, appendObj); 2112 } 2113 } 2114 // trigger handler right now 2115 this.tick(); 2116 } 2117 } 2118 }; 2119 AudioStreamController.prototype.onFragParsingData = function (data) { 2120 var _this = this; 2121 var fragCurrent = this.fragCurrent; 2122 var fragNew = data.frag; 2123 if (fragCurrent && 2124 data.id === 'audio' && 2125 data.type === 'audio' && 2126 fragNew.sn === fragCurrent.sn && 2127 fragNew.level === fragCurrent.level && 2128 this.state === base_stream_controller_1.State.PARSING) { 2129 var trackId = this.trackId, track = this.tracks[trackId], hls_1 = this.hls; 2130 if (!Number.isFinite(data.endPTS)) { 2131 data.endPTS = data.startPTS + fragCurrent.duration; 2132 data.endDTS = data.startDTS + fragCurrent.duration; 2133 } 2134 fragCurrent.addElementaryStream(fragment_1.default.ElementaryStreamTypes.AUDIO); 2135 logger_1.logger.log("parsed " + data.type + ",PTS:[" + data.startPTS.toFixed(3) + "," + data.endPTS.toFixed(3) + "],DTS:[" + data.startDTS.toFixed(3) + "/" + data.endDTS.toFixed(3) + "],nb:" + data.nb); 2136 LevelHelper.updateFragPTSDTS(track.details, fragCurrent, data.startPTS, data.endPTS); 2137 var audioSwitch = this.audioSwitch, media = this.media, appendOnBufferFlush = false;
2138 // Only flush audio from old audio tracks when PTS is known on new audio track 2139 if (audioSwitch && media) { 2140 if (media.readyState) { 2141 var currentTime = media.currentTime; 2142 logger_1.logger.log('switching audio track : currentTime:' + currentTime); 2143 if (currentTime >= data.startPTS) { 2144 logger_1.logger.log('switching audio track : flushing all audio'); 2145 this.state = base_stream_controller_1.State.BUFFER_FLUSHING; 2146 hls_1.trigger(events_1.default.BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY, type: 'audio' }); 2147 appendOnBufferFlush = true; 2148 // Lets announce that the initial audio track switch flush occur 2149 this.audioSwitch = false; 2150 hls_1.trigger(events_1.default.AUDIO_TRACK_SWITCHED, { id: trackId }); 2151 } 2152 } 2153 else { 2154 // Lets announce that the initial audio track switch flush occur 2155 this.audioSwitch = false; 2156 hls_1.trigger(events_1.default.AUDIO_TRACK_SWITCHED, { id: trackId }); 2157 } 2158 } 2159 var pendingData_1 = this.pendingData; 2160 if (!pendingData_1) { 2161 logger_1.logger.warn('Apparently attempt to enqueue media payload without codec initialization data upfront'); 2162 hls_1.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.MEDIA_ERROR, details: null, fatal: true }); 2163 return; 2164 } 2165 if (!this.audioSwitch) { 2166 [data.data1, data.data2].forEach(function (buffer) { 2167 if (buffer && buffer.length) { 2168 pendingData_1.push({ type: data.type, data: buffer, parent: 'audio', content: 'data' }); 2169 } 2170 }); 2171 if (!appendOnBufferFlush && pendingData_1.length) { 2172 pendingData_1.forEach(function (appendObj) { 2173 // only append in PARSING state (rationale is that an appending error could happen synchronously on first segment appending) 2174 // in that case it is useless to append following segments 2175 if (_this.state === base_stream_controller_1.State.PARSING) { 2176 // arm pending Buffering flag before appending a segment 2177 _this.pendingBuffering = true; 2178 _this.hls.trigger(events_1.default.BUFFER_APPENDING, appendObj); 2179 } 2180 }); 2181 this.pendingData = []; 2182 this.appended = true; 2183 } 2184 } 2185 // trigger handler right now 2186 this.tick(); 2187 } 2188 }; 2189 AudioStreamController.prototype.onFragParsed = function (data) { 2190 var fragCurrent = this.fragCurrent; 2191 var fragNew = data.frag; 2192 if (fragCurrent && 2193 data.id === 'audio' && 2194 fragNew.sn === fragCurrent.sn && 2195 fragNew.level === fragCurrent.level && 2196 this.state === base_stream_controller_1.State.PARSING) { 2197 this.stats.tparsed = performance.now(); 2198 this.state = base_stream_controller_1.State.PARSED; 2199 this._checkAppendedParsed(); 2200 } 2201 }; 2202 AudioStreamController.prototype.onBufferReset = function () { 2203 // reset reference to sourcebuffers 2204 this.mediaBuffer = this.videoBuffer = null; 2205 this.loadedmetadata = false; 2206 }; 2207 AudioStreamController.prototype.onBufferCreated = function (data) { 2208 var audioTrack = data.tracks.audio; 2209 if (audioTrack) { 2210 this.mediaBuffer = audioTrack.buffer; 2211 this.loadedmetadata = true; 2212 } 2213 if (data.tracks.video) { 2214 this.videoBuffer = data.tracks.video.buffer; 2215 } 2216 }; 2217 AudioStreamController.prototype.onBufferAppended = function (data) { 2218 if (data.parent === 'audio') { 2219 var state = this.state; 2220 if (state === base_stream_controller_1.State.PARSING || state === base_stream_controller_1.State.PARSED) { 2221 // check if all buffers have been appended 2222 this.pendingBuffering = (data.pending > 0); 2223 this._checkAppendedParsed(); 2224 } 2225 } 2226 }; 2227 AudioStreamController.prototype._checkAppendedParsed = function () { 2228 // trigger handler right now 2229 if (this.state === base_stream_controller_1.State.PARSED && (!this.appended || !this.pendingBuffering)) { 2230 var frag = this.fragCurrent, stats = this.stats, hls_2 = this.hls; 2231 if (frag) { 2232 this.fragPrevious = frag; 2233 stats.tbuffered = performance.now(); 2234 hls_2.trigger(events_1.default.FRAG_BUFFERED, { stats: stats, frag: frag, id: 'audio' }); 2235 var media = this.mediaBuffer ? this.mediaBuffer : this.media; 2236 logger_1.logger.log("audio buffered : " + time_ranges_1.default.toString(media.buffered)); 2237 if (this.audioSwitch && this.appended) { 2238 this.audioSwitch = false;
2239 hls_2.trigger(events_1.default.AUDIO_TRACK_SWITCHED, { id: this.trackId }); 2240 } 2241 this.state = base_stream_controller_1.State.IDLE; 2242 } 2243 this.tick(); 2244 } 2245 }; 2246 AudioStreamController.prototype.onError = function (data) { 2247 var frag = data.frag; 2248 // don't handle frag error not related to audio fragment 2249 if (frag && frag.type !== 'audio') { 2250 return; 2251 } 2252 switch (data.details) { 2253 case errors_1.ErrorDetails.FRAG_LOAD_ERROR: 2254 case errors_1.ErrorDetails.FRAG_LOAD_TIMEOUT: 2255 var frag_1 = data.frag; 2256 // don't handle frag error not related to audio fragment 2257 if (frag_1 && frag_1.type !== 'audio') { 2258 break; 2259 } 2260 if (!data.fatal) { 2261 var loadError = this.fragLoadError; 2262 if (loadError) { 2263 loadError++; 2264 } 2265 else { 2266 loadError = 1; 2267 } 2268 var config = this.config; 2269 if (loadError <= config.fragLoadingMaxRetry) { 2270 this.fragLoadError = loadError; 2271 // exponential backoff capped to config.fragLoadingMaxRetryTimeout 2272 var delay = Math.min(Math.pow(2, loadError - 1) * config.fragLoadingRetryDelay, config.fragLoadingMaxRetryTimeout); 2273 logger_1.logger.warn("AudioStreamController: frag loading failed, retry in " + delay + " ms"); 2274 this.retryDate = performance.now() + delay; 2275 // retry loading state 2276 this.state = base_stream_controller_1.State.FRAG_LOADING_WAITING_RETRY; 2277 } 2278 else { 2279 logger_1.logger.error("AudioStreamController: " + data.details + " reaches max retry, redispatch as fatal ..."); 2280 // switch error to fatal 2281 data.fatal = true; 2282 this.state = base_stream_controller_1.State.ERROR; 2283 } 2284 } 2285 break; 2286 case errors_1.ErrorDetails.AUDIO_TRACK_LOAD_ERROR: 2287 case errors_1.ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT: 2288 case errors_1.ErrorDetails.KEY_LOAD_ERROR: 2289 case errors_1.ErrorDetails.KEY_LOAD_TIMEOUT: 2290 // when in ERROR state, don't switch back to IDLE state in case a non-fatal error is received 2291 if (this.state !== base_stream_controller_1.State.ERROR) { 2292 // if fatal error, stop processing, otherwise move to IDLE to retry loading 2293 this.state = data.fatal ? base_stream_controller_1.State.ERROR : base_stream_controller_1.State.IDLE; 2294 logger_1.logger.warn("AudioStreamController: " + data.details + " while loading frag, now switching to " + this.state + " state ..."); 2295 } 2296 break; 2297 case errors_1.ErrorDetails.BUFFER_FULL_ERROR: 2298 // if in appending state 2299 if (data.parent === 'audio' && (this.state === base_stream_controller_1.State.PARSING || this.state === base_stream_controller_1.State.PARSED)) { 2300 var media = this.mediaBuffer, currentTime = this.media.currentTime, mediaBuffered = media && buffer_helper_1.BufferHelper.isBuffered(media, currentTime) && buffer_helper_1.BufferHelper.isBuffered(media, currentTime + 0.5); 2301 // reduce max buf len if current position is buffered 2302 if (mediaBuffered) { 2303 var config = this.config; 2304 if (config.maxMaxBufferLength >= config.maxBufferLength) { 2305 // reduce max buffer length as it might be too high. we do this to avoid loop flushing ... 2306 config.maxMaxBufferLength /= 2; 2307 logger_1.logger.warn("AudioStreamController: reduce max buffer length to " + config.max
2307MaxBufferLength + "s"); 2308 } 2309 this.state = base_stream_controller_1.State.IDLE; 2310 } 2311 else { 2312 // current position is not buffered, but browser is still complaining about buffer full error 2313 // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708 2314 // in that case flush the whole audio buffer to recover 2315 logger_1.logger.warn('AudioStreamController: buffer full error also media.currentTime is not buffered, flush audio buffer'); 2316 this.fragCurrent = null; 2317 // flush everything 2318 this.state = base_stream_controller_1.State.BUFFER_FLUSHING; 2319 this.hls.trigger(events_1.default.BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY, type: 'audio' }); 2320 } 2321 } 2322 break; 2323 default: 2324 break; 2325 } 2326 }; 2327 AudioStreamController.prototype.onBufferFlushed = function () { 2328 var _this = this; 2329 var pendingData = this.pendingData; 2330 if (pendingData && pendingData.length) { 2331 logger_1.logger.log('AudioStreamController: appending pending audio data after buffer flushed'); 2332 pendingData.forEach(function (appendObj) { 2333 _this.hls.trigger(events_1.default.BUFFER_APPENDING, appendObj); 2334 }); 2335 this.appended = true; 2336 this.pendingData = []; 2337 this.state = base_stream_controller_1.State.PARSED; 2338 } 2339 else { 2340 // move to IDLE once flush complete. this should trigger new fragment loading 2341 this.state = base_stream_controller_1.State.IDLE; 2342 // reset reference to frag 2343 this.fragPrevious = null; 2344 this.tick(); 2345 } 2346 }; 2347 return AudioStreamController; 2348}(base_stream_controller_1.default)); 2349exports.default = AudioStreamController; 2350 2351/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 2352 2353/***/ }), 2354
2355/***/ "./src/controller/audio-track-controller.js": 2356/*!**************************************************!*\ 2357 !*** ./src/controller/audio-track-controller.js ***! 2358 \**************************************************/ 2359/*! no static exports found */ 2360/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 2361/***/ (function(module, exports, __webpack_require__) { 2362 2363"use strict"; 2364 2365var __extends = (this && this.__extends) || (function () { 2366 var extendStatics = Object.setPrototypeOf || 2367 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 2368 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 2369 return function (d, b) { 2370 extendStatics(d, b); 2371 function __() { this.constructor = d; } 2372 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 2373 }; 2374})(); 2375Object.defineProperty(exports, "__esModule", { value: true }); 2376var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 2377var task_loop_1 = __webpack_require__(/*! ../task-loop */ "./src/task-loop.js"); 2378var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 2379var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 2380/** 2381 * @class AudioTrackController 2382 * @implements {EventHandler} 2383 * 2384 * Handles main manifest and audio-track metadata loaded, 2385 * owns and exposes the selectable audio-tracks data-models. 2386 * 2387 * Exposes internal interface to select available audio-tracks. 2388 * 2389 * Handles errors on loading audio-track playlists. Manages fallback mechanism 2390 * with redundants tracks (group-IDs). 2391 * 2392 * Handles level-loading and group-ID switches for video (fallback on video levels), 2393 * and eventually adapts the audio-track group-ID to match. 2394 * 2395 * @fires AUDIO_TRACK_LOADING 2396 * @fires AUDIO_TRACK_SWITCHING 2397 * @fires AUDIO_TRACKS_UPDATED 2398 * @fires ERROR 2399 * 2400 */ 2401var AudioTrackController = /** @class */ (function (_super) { 2402 __extends(AudioTrackController, _super); 2403 function AudioTrackController(hls) { 2404 var _this = _super.call(this, hls, events_1.default.MANIFEST_LOADING, events_1.default.MANIFEST_PARSED, events_1.default.AUDIO_TRACK_LOADED, events_1.default.AUDIO_TRACK_SWITCHED, events_1.default.LEVEL_LOADED, events_1.default.ERROR) || this; 2405 /** 2406 * @private 2407 * Currently selected index in `tracks` 2408 * @member {number} trackId 2409 */ 2410 _this._trackId = -1; 2411 /** 2412 * @private 2413 * If should select tracks according to default track attribute 2414 * @member {boolean} _selectDefaultTrack 2415 */ 2416 _this._selectDefaultTrack = true; 2417 /** 2418 * @public 2419 * All tracks available 2420 * @member {AudioTrack[]} 2421 */ 2422 _this.tracks = []; 2423 /** 2424 * @public 2425 * List of blacklisted audio track IDs (that have caused failure) 2426 * @member {number[]} 2427 */ 2428 _this.trackIdBlacklist = Object.create(null); 2429 /** 2430 * @public 2431 * The currently running group ID for audio 2432 * (we grab this on manifest-parsed and new level-loaded) 2433 * @member {string} 2434 */ 2435 _this.audioGroupId = null; 2436 return _this; 2437 } 2438 /** 2439 * Reset audio tracks on new manifest loading. 2440 */ 2441 AudioTrackController.prototype.onManifestLoading = function () { 2442 this.tracks = []; 2443 this._trackId = -1; 2444 this._selectDefaultTrack = true; 2445 }; 2446 /** 2447 * Store tracks data from manifest parsed data. 2448 * 2449 * Trigger AUDIO_TRACKS_UPDATED event. 2450 * 2451 * @param {*} data 2452 */ 2453 AudioTrackController.prototype.onManifestParsed = function (data) { 2454 var tracks = this.tracks = data.audioTracks || []; 2455 this.hls.trigger(events_1.default.AUDIO_TRACKS_UPDATED, { audioTracks: tracks }); 2456 }; 2457 /** 2458 * Store track details of loaded track in our data-model. 2459 * 2460 * Set-up metadata update interval task for live-mode streams. 2461 * 2462 * @param {} data 2463 */ 2464 AudioTrackController.prototype.onAudioTrackLoaded = function (data) { 2465 if (data.id >= this.tracks.length) { 2466 logger_1.logger.warn('Invalid audio track id:', data.id); 2467 return; 2468 } 2469 logger_1.logger.log("audioTrack " + data.id + " loaded"); 2470 this.tracks[data.id].details = data.details; 2471 // check if current playlist is a live playlist 2472 // and if we have already our reload interval setup 2473 if (data.details.live && !this.hasInterval()) { 2474 // if live playlist we will have to reload it periodically 2475 // set reload period to playlist target duration 2476 var updatePeriodMs = data.details.targetduration * 1000; 2477 this.setInterval(updatePeriodMs); 2478 } 2479 if (!data.details.live && this.hasInterval()) { 2480 // playlist is not live and timer is scheduled: cancel it 2481 this.clearInterval(); 2482 } 2483 }; 2484 /** 2485 * Update the internal group ID to any audio-track we may have set manually 2486 * or because of a failure-handling fallback. 2487 * 2488 * Quality-levels should update to that group ID in this case. 2489 * 2490 * @param {*} data 2491 */ 2492 AudioTrackController.prototype.onAudioTrackSwitched = function (data) { 2493 var audioGroupId = this.tracks[data.id].groupId; 2494 if (audioGroupId && (this.audioGroupId !== audioGroupId)) { 2495 this.audioGroupId = audioGroupId; 2496 } 2497 }; 2498 /** 2499 * When a level gets loaded, if it has redundant audioGroupIds (in the same ordinality as it's redundant URLs) 2500 * we are setting our audio-group ID internally to the one set, if it is different from the group ID currently set. 2501 * 2502 * If group-ID got update, we re-select the appropriate audio-track with this group-ID matching the currently 2503 * selected one (based on NAME property). 2504 * 2505 * @param {*} data 2506 */ 2507 AudioTrackController.prototype.onLevelLoaded = function (data) { 2508 // FIXME: crashes because currentLevel is undefined 2509 // const levelInfo = this.hls.levels[this.hls.currentLevel]; 2510 var levelInfo = this.hls.levels[data.level]; 2511 if (!levelInfo.audioGroupIds) { 2512 return; 2513 } 2514 var audioGroupId = levelInfo.audioGroupIds[levelInfo.urlId]; 2515 if (this.audioGroupId !== audioGroupId) { 2516 this.audioGroupId = audioGroupId; 2517 this._selectInitialAudioTrack(); 2518 } 2519 }; 2520 /** 2521 * Handle network errors loading audio track manifests 2522 * and also pausing on any netwok errors. 2523 * 2524 * @param {ErrorEventData} data 2525 */ 2526 AudioTrackController.prototype.onError = function (data) { 2527 // Only handle network errors
2528 if (data.type !== errors_1.ErrorTypes.NETWORK_ERROR) { 2529 return; 2530 } 2531 // If fatal network error, cancel update task 2532 if (data.fatal) { 2533 this.clearInterval(); 2534 } 2535 // If not an audio-track loading error don't handle further 2536 if (data.details !== errors_1.ErrorDetails.AUDIO_TRACK_LOAD_ERROR) { 2537 return; 2538 } 2539 logger_1.logger.warn('Network failure on audio-track id:', data.context.id); 2540 this._handleLoadError(); 2541 }; 2542 Object.defineProperty(AudioTrackController.prototype, "audioTracks", { 2543 /** 2544 * @type {AudioTrack[]} Audio-track list we own 2545 */ 2546 get: function () { 2547 return this.tracks; 2548 }, 2549 enumerable: true, 2550 configurable: true 2551 }); 2552 Object.defineProperty(AudioTrackController.prototype, "audioTrack", { 2553 /** 2554 * @type {number} Index into audio-tracks list of currently selected track. 2555 */ 2556 get: function () { 2557 return this._trackId; 2558 }, 2559 /** 2560 * Select current track by index 2561 */ 2562 set: function (newId) { 2563 this._setAudioTrack(newId); 2564 // If audio track is selected from API then don't choose from the manifest default track 2565 this._selectDefaultTrack = false; 2566 }, 2567 enumerable: true, 2568 configurable: true 2569 }); 2570 /** 2571 * @private 2572 * @param {number} newId 2573 */ 2574 AudioTrackController.prototype._setAudioTrack = function (newId) { 2575 // noop on same audio track id as already set 2576 if (this._trackId === newId && this.tracks[this._trackId].details) { 2577 logger_1.logger.debug('Same id as current audio-track passed, and track details available -> no-op'); 2578 return; 2579 } 2580 // check if level idx is valid 2581 if (newId < 0 || newId >= this.tracks.length) { 2582 logger_1.logger.warn('Invalid id passed to audio-track controller'); 2583 return; 2584 } 2585 var audioTrack = this.tracks[newId]; 2586 logger_1.logger.log("Now switching to audio-track index " + newId); 2587 // stopping live reloading timer if any 2588 this.clearInterval(); 2589 this._trackId = newId; 2590 var url = audioTrack.url, type = audioTrack.type, id = audioTrack.id; 2591 this.hls.trigger(events_1.default.AUDIO_TRACK_SWITCHING, { id: id, type: type, url: url }); 2592 this._loadTrackDetailsIfNeeded(audioTrack); 2593 }; 2594 /** 2595 * @override 2596 */ 2597 AudioTrackController.prototype.doTick = function () { 2598 this._updateTrack(this._trackId); 2599 }; 2600 /** 2601 * Select initial track 2602 * @private 2603 */ 2604 AudioTrackController.prototype._selectInitialAudioTrack = function () { 2605 var _this = this; 2606 var tracks = this.tracks; 2607 if (!tracks.length) { 2608 return; 2609 } 2610 var currentAudioTrack = this.tracks[this._trackId]; 2611 var name = null; 2612 if (currentAudioTrack) { 2613 name = currentAudioTrack.name; 2614 } 2615 // Pre-select default tracks if there are any 2616 if (this._selectDefaultTrack) { 2617 var defaultTracks = tracks.filter(function (track) { return track.default; }); 2618 if (defaultTracks.length) { 2619 tracks = defaultTracks; 2620 } 2621 else { 2622 logger_1.logger.warn('No default audio tracks defined'); 2623 } 2624 } 2625 var trackFound = false; 2626 var traverseTracks = function () { 2627 // Select track with right group ID 2628 tracks.forEach(function (track) { 2629 if (trackFound) { 2630 return; 2631 } 2632 // We need to match the (pre-)selected group ID 2633 // and the NAME of the current track. 2634 if ((!_this.audioGroupId || track.groupId === _this.audioGroupId) && 2635 (!name || name === track.name)) { 2636 // If there was a previous track try to stay with the same `NAME`. 2637 // It should be unique across tracks of same group, and consistent through redundant track groups. 2638 _this._setAudioTrack(track.id); 2639 trackFound = true; 2640 } 2641 }); 2642 }; 2643 traverseTracks(); 2644 if (!trackFound) { 2645 name = null; 2646 traverseTracks(); 2647 } 2648 if (!trackFound) { 2649 logger_1.logger.error("No track found for running audio group-ID: " + this.audioGroupId); 2650 this.hls.trigger(events_1.default.ERROR, { 2651 type: errors_1.ErrorTypes.MEDIA_ERROR, 2652 details: errors_1.ErrorDetails.AUDIO_TRACK_LOAD_ERROR, 2653 fatal: true 2654 }); 2655 } 2656 }; 2657 /** 2658 * @private 2659 * @param {AudioTrack} audioTrack 2660 * @returns {boolean} 2661 */ 2662 AudioTrackController.prototype._needsTrackLoading = function (audioTrack) { 2663 var details = audioTrack.details, url = audioTrack.url;
2664 if (!details || details.live) { 2665 // check if we face an audio track embedded in main playlist (audio track without URI attribute) 2666 return !!url; 2667 } 2668 return false; 2669 }; 2670 /** 2671 * @private 2672 * @param {AudioTrack} audioTrack 2673 */ 2674 AudioTrackController.prototype._loadTrackDetailsIfNeeded = function (audioTrack) { 2675 if (this._needsTrackLoading(audioTrack)) { 2676 var url = audioTrack.url, id = audioTrack.id; 2677 // track not retrieved yet, or live playlist we need to (re)load it 2678 logger_1.logger.log("loading audio-track playlist for id: " + id); 2679 this.hls.trigger(events_1.default.AUDIO_TRACK_LOADING, { url: url, id: id }); 2680 } 2681 }; 2682 /** 2683 * @private 2684 * @param {number} newId 2685 */ 2686 AudioTrackController.prototype._updateTrack = function (newId) { 2687 // check if level idx is valid 2688 if (newId < 0 || newId >= this.tracks.length) { 2689 return; 2690 } 2691 // stopping live reloading timer if any 2692 this.clearInterval(); 2693 this._trackId = newId; 2694 logger_1.logger.log("trying to update audio-track " + newId); 2695 var audioTrack = this.tracks[newId]; 2696 this._loadTrackDetailsIfNeeded(audioTrack); 2697 }; 2698 /** 2699 * @private 2700 */ 2701 AudioTrackController.prototype._handleLoadError = function () { 2702 // First, let's black list current track id 2703 this.trackIdBlacklist[this._trackId] = true; 2704 // Let's try to fall back on a functional audio-track with the same group ID 2705 var previousId = this._trackId; 2706 var _a = this.tracks[previousId], name = _a.name, language = _a.language, groupId = _a.groupId; 2707 logger_1.logger.warn("Loading failed on audio track id: " + previousId + ", group-id: " + groupId + ", name/language: \"" + name + "\" / \"" + language + "\""); 2708 // Find a non-blacklisted track ID with the same NAME 2709 // At least a track that is not blacklisted, thus on another group-ID. 2710 var newId = previousId; 2711 for (var i = 0; i < this.tracks.length; i++) { 2712 if (this.trackIdBlacklist[i]) { 2713 continue; 2714 } 2715 var newTrack = this.tracks[i]; 2716 if (newTrack.name === name) { 2717 newId = i; 2718 break; 2719 } 2720 } 2721 if (newId === previousId) { 2722 logger_1.logger.warn("No fallback audio-track found for name/language: \"" + name + "\" / \"" + language + "\""); 2723 return; 2724 } 2725 logger_1.logger.log('Attempting audio-track fallback id:', newId, 'group-id:', this.tracks[newId].groupId); 2726 this._setAudioTrack(newId); 2727 }; 2728 return AudioTrackController; 2729}(task_loop_1.default)); 2730exports.default = AudioTrackController; 2731 2732 2733/***/ }), 2734
2735/***/ "./src/controller/base-stream-controller.js": 2736/*!**************************************************!*\ 2737 !*** ./src/controller/base-stream-controller.js ***! 2738 \**************************************************/ 2739/*! no static exports found */ 2740/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 2741/***/ (function(module, exports, __webpack_require__) { 2742 2743"use strict"; 2744/* WEBPACK VAR INJECTION */(function(Number) { 2745var __extends = (this && this.__extends) || (function () { 2746 var extendStatics = Object.setPrototypeOf || 2747 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 2748 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 2749 return function (d, b) { 2750 extendStatics(d, b); 2751 function __() { this.constructor = d; } 2752 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 2753 }; 2754})(); 2755Object.defineProperty(exports, "__esModule", { value: true }); 2756var task_loop_1 = __webpack_require__(/*! ../task-loop */ "./src/task-loop.js"); 2757var fragment_tracker_1 = __webpack_require__(/*! ./fragment-tracker */ "./src/controller/fragment-tracker.js"); 2758var buffer_helper_1 = __webpack_require__(/*! ../utils/buffer-helper */ "./src/utils/buffer-helper.js"); 2759var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 2760exports.State = { 2761 STOPPED: 'STOPPED', 2762 STARTING: 'STARTING', 2763 IDLE: 'IDLE', 2764 PAUSED: 'PAUSED', 2765 KEY_LOADING: 'KEY_LOADING', 2766 FRAG_LOADING: 'FRAG_LOADING', 2767 FRAG_LOADING_WAITING_RETRY: 'FRAG_LOADING_WAITING_RETRY', 2768 WAITING_TRACK: 'WAITING_TRACK', 2769 PARSING: 'PARSING', 2770 PARSED: 'PARSED', 2771 BUFFER_FLUSHING: 'BUFFER_FLUSHING', 2772 ENDED: 'ENDED', 2773 ERROR: 'ERROR', 2774 WAITING_INIT_PTS: 'WAITING_INIT_PTS', 2775 WAITING_LEVEL: 'WAITING_LEVEL' 2776}; 2777var BaseStreamController = /** @class */ (function (_super) { 2778 __extends(BaseStreamController, _super); 2779 function BaseStreamController() { 2780 return _super !== null && _super.apply(this, arguments) || this; 2781 } 2782 BaseStreamController.prototype.doTick = function () { }; 2783 BaseStreamController.prototype.startLoad = function () { }; 2784 BaseStreamController.prototype.stopLoad = function () { 2785 var frag = this.fragCurrent; 2786 if (frag) { 2787 if (frag.loader) { 2788 frag.loader.abort(); 2789 } 2790 this.fragmentTracker.removeFragment(frag); 2791 } 2792 if (this.demuxer) { 2793 this.demuxer.destroy(); 2794 this.demuxer = null; 2795 } 2796 this.fragCurrent = null; 2797 this.fragPrevious = null; 2798 this.clearInterval(); 2799 this.clearNextTick(); 2800 this.state = exports.State.STOPPED; 2801 }; 2802 BaseStreamController.prototype._streamEnded = function (bufferInfo, levelDetails) { 2803 var _a = this, fragCurrent = _a.fragCurrent, fragmentTracker = _a.fragmentTracker; 2804 // we just got done loading the final fragment and there is no other buffered range after ... 2805 // rationale is that in case there are any buffered ranges after, it means that there are unbuffered portion in between 2806 // so we should not switch to ENDED in that case, to be able to buffer them 2807 // dont switch to ENDED if we need to backtrack last fragment 2808 if (!levelDetails.live && fragCurrent && !fragCurrent.backtracked && fragCurrent.sn === levelDetails.endSN && !bufferInfo.nextStart) { 2809 var fragState = fragmentTracker.getState(fragCurrent); 2810 return fragState === fragment_tracker_1.FragmentState.PARTIAL || fragState === fragment_tracker_1.FragmentState.OK; 2811 } 2812 return false; 2813 }; 2814 BaseStreamController.prototype.onMediaSeeking = function () { 2815 var _a = this, config = _a.config, media = _a.media, mediaBuffer = _a.mediaBuffer, state = _a.state; 2816 var currentTime = media ? media.currentTime : null; 2817 var bufferInfo = buffer_helper_1.BufferHelper.bufferInfo(mediaBuffer || media, currentTime, this.config.maxBufferHole); 2818 if (Number.isFinite(currentTime)) { 2819 logger_1.logger.log("media seeking to " + currentTime.toFixed(3)); 2820 } 2821 if (state === exports.State.FRAG_LOADING) { 2822 var fragCurrent = this.fragCurrent; 2823 // check if we are seeking to a unbuffered area AND if frag loading is in progress 2824 if (bufferInfo.len === 0 && fragCurrent) { 2825 var tolerance = config.maxFragLookUpTolerance; 2826 var fragStartOffset = fragCurrent.start - tolerance; 2827 var fragEndOffset = fragCurrent.start + fragCurrent.duration + tolerance; 2828 // check if we seek position will be out of currently loaded frag range : if out cancel frag load, if in, don't do anything 2829 if (currentTime < fragStartOffset || currentTime > fragEndOffset) { 2830 if (fragCurrent.loader) { 2831 logger_1.logger.log('seeking outside of buffer while fragment load in progress, cancel fragment load'); 2832 fragCurrent.loader.abort(); 2833 } 2834 this.fragCurrent = null; 2835 this.fragPrevious = null; 2836 // switch to IDLE state to load new fragment 2837 this.state = exports.State.IDLE; 2838 } 2839 else { 2840 logger_1.logger.log('seeking outside of buffer but within currently loaded fragment range'); 2841 } 2842 } 2843 } 2844 else if (state === exports.State.ENDED) { 2845 // if seeking to unbuffered area, clean up fragPrevious 2846 if (bufferInfo.len === 0) { 2847 this.fragPrevious = null; 2848 this.fragCurrent = null; 2849 } 2850 // switch to IDLE state to check for potential new fragment 2851 this.state = exports.State.IDLE; 2852 } 2853 if (media) { 2854 this.lastCurrentTime = currentTime; 2855 } 2856 // in case seeking occurs although no media buffered, adjust startPosition and nextLoadPosition to seek target 2857 if (!this.loadedmetadata) { 2858 this.nextLoadPosition = this.startPosition = currentTime; 2859 } 2860 // tick to speed up processing 2861 this.tick(); 2862 }; 2863 BaseStreamController.prototype.onMediaEnded = function () {
2864 // reset startPosition and lastCurrentTime to restart playback @ stream beginning 2865 this.startPosition = this.lastCurrentTime = 0; 2866 }; 2867 BaseStreamController.prototype.onHandlerDestroying = function () { 2868 this.stopLoad(); 2869 _super.prototype.onHandlerDestroying.call(this); 2870 }; 2871 BaseStreamController.prototype.onHandlerDestroyed = function () { 2872 this.state = exports.State.STOPPED; 2873 this.fragmentTracker = null; 2874 }; 2875 return BaseStreamController; 2876}(task_loop_1.default)); 2877exports.default = BaseStreamController; 2878 2879/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 2880 2881/***/ }), 2882
2883/***/ "./src/controller/buffer-controller.js": 2884/*!*********************************************!*\ 2885 !*** ./src/controller/buffer-controller.js ***! 2886 \*********************************************/ 2887/*! no static exports found */ 2888/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 2889/***/ (function(module, exports, __webpack_require__) { 2890 2891"use strict"; 2892/* WEBPACK VAR INJECTION */(function(Number) { 2893/* 2894 * Buffer Controller 2895*/ 2896var __extends = (this && this.__extends) || (function () { 2897 var extendStatics = Object.setPrototypeOf || 2898 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 2899 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 2900 return function (d, b) { 2901 extendStatics(d, b); 2902 function __() { this.constructor = d; } 2903 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 2904 }; 2905})(); 2906Object.defineProperty(exports, "__esModule", { value: true }); 2907var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 2908var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 2909var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 2910var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 2911var mediasource_helper_1 = __webpack_require__(/*! ../utils/mediasource-helper */ "./src/utils/mediasource-helper.js"); 2912var MediaSource = mediasource_helper_1.getMediaSource(); 2913var BufferController = /** @class */ (function (_super) { 2914 __extends(BufferController, _super); 2915 function BufferController(hls) { 2916 var _this = _super.call(this, hls, events_1.default.MEDIA_ATTA
2916CHING, events_1.default.MEDIA_DETACHING, events_1.default.MANIFEST_PARSED, events_1.default.BUFFER_RESET, events_1.default.BUFFER_APPENDING, events_1.default.BUFFER_CODECS, events_1.default.BUFFER_EOS, events_1.default.BUFFER_FLUSHING, events_1.default.LEVEL_PTS_UPDATED, events_1.default.LEVEL_UPDATED) || this; 2917 // the value that we have set mediasource.duration to 2918 // (the actual duration may be tweaked slighly by the browser) 2919 _this._msDuration = null; 2920 // the value that we want to set mediaSource.duration to 2921 _this._levelDuration = null; 2922 // the target duration of the current media playlist 2923 _this._levelTargetDuration = 10; 2924 // current stream state: true - for live broadcast, false - for VoD content 2925 _this._live = null; 2926 // cache the self generated object url to detect hijack of video tag 2927 _this._objectUrl = null; 2928 // The number of BUFFER_CODEC events received before any sourceBuffers are created 2929 _this.bufferCodecEventsExpected = 0; 2930 // Source Buffer listeners 2931 _this.onsbue = _this.onSBUpdateEnd.bind(_this); 2932 _this.onsbe = _this.onSBUpdateError.bind(_this); 2933 _this.pendingTracks = {}; 2934 _this.tracks = {}; 2935 return _this; 2936 } 2937 BufferController.prototype.destroy = function () { 2938 event_handler_1.default.prototype.destroy.call(this); 2939 }; 2940 BufferController.prototype.onLevelPtsUpdated = function (data) { 2941 var type = data.type; 2942 var audioTrack = this.tracks.audio; 2943 // Adjusting `SourceBuffer.timestampOffset` (desired point in the timeline where the next frames should be appended) 2944 // in Chrome browser when we detect MPEG audio container and time delta between level PTS and `SourceBuffer.timestampOffset` 2945 // is greater than 100ms (this is enough to handle seek for VOD or level change for LIVE videos). At the time of change we issue 2946 // `SourceBuffer.abort()` and adjusting `SourceBuffer.timestampOffset` if `SourceBuffer.updating` is false or awaiting `updateend` 2947 // event if SB is in updating state. 2948 // More info here: https://github.com/video-dev/hls.js/issues/332#issuecomment-257986486 2949 if (type === 'audio' && audioTrack && audioTrack.container === 'audio/mpeg') { // Chrome audio mp3 track 2950 var audioBuffer = this.sourceBuffer.audio; 2951 var delta = Math.abs(audioBuffer.timestampOffset - data.start); 2952 // adjust timestamp offset if time delta is greater than 100ms 2953 if (delta > 0.1) { 2954 var updating = audioBuffer.updating; 2955 try { 2956 audioBuffer.abort(); 2957 } 2958 catch (err) { 2959 logger_1.logger.warn('can not abort audio buffer: ' + err); 2960 } 2961 if (!updating) { 2962 logger_1.logger.warn('change mpeg audio timestamp offset from ' + audioBuffer.timestampOffset + ' to ' + data.start); 2963 audioBuffer.timestampOffset = data.start; 2964 } 2965 else { 2966 this.audioTimestampOffset = data.start; 2967 } 2968 } 2969 } 2970 }; 2971 BufferController.prototype.onManifestParsed = function (data) { 2972 // in case of alt audio 2 BUFFER_CODECS events will be triggered, one per stream controller 2973 // sourcebuffers will be created all at once when the expected nb of tracks will be reached 2974 // in case alt audio is not used, only one BUFFER_CODEC event will be fired from main stream controller 2975 // it will contain the expected nb of source buffers, no need to compute it 2976 this.bufferCodecEventsExpected = data.altAudio ? 2 : 1; 2977 logger_1.logger.log(this.bufferCodecEventsExpected + " bufferCodec event(s) expected"); 2978 }; 2979 BufferController.prototype.onMediaAttaching = function (data) { 2980 var media = this.media = data.media; 2981 if (media) { 2982 // setup the media source 2983 var ms = this.mediaSource = new MediaSource(); 2984 // Media Source listeners 2985 this.onmso = this.onMediaSourceOpen.bind(this); 2986 this.onmse = this.onMediaSourceEnded.bind(this); 2987 this.onmsc = this.onMediaSourceClose.bind(this); 2988 ms.addEventListener('sourceopen', this.onmso); 2989 ms.addEventListener('sourceended', this.onmse); 2990 ms.addEventListener('sourceclose', this.onmsc); 2991 // link video and media Source 2992 media.src = window.URL.createObjectURL(ms); 2993 // cache the locally generated object url 2994 this._objectUrl = media.src; 2995 } 2996 }; 2997 BufferController.prototype.onMediaDetaching = function () { 2998 logger_1.logger.log('media source detaching'); 2999 var ms = this.mediaSource; 3000 if (ms) { 3001 if (ms.readyState === 'open') { 3002 try { 3003 // endOfStream could trigger exception if any sourcebuffer is in updating state 3004 // we don't really care about checking sourcebuffer state here, 3005 // as we are anyway detaching the MediaSource 3006 // let's just avoid this exception to propagate 3007 ms.endOfStream(); 3008 } 3009 catch (err) { 3010 logger_1.logger.warn("onMediaDetaching:" + err.message + " while calling endOfStream"); 3011 } 3012 } 3013 ms.removeEventListener('sourceopen', this.onmso); 3014 ms.removeEventListener('sourceended', this.onmse); 3015 ms.removeEventListener('sourceclose', this.onmsc); 3016 // Detach properly the MediaSource from the HTMLMediaElement as 3017 // suggested in https://github.com/w3c/media-source/issues/53. 3018 if (this.media) { 3019 window.URL.revokeObjectURL(this._objectUrl); 3020 // clean up video tag src only if it's our own url. some external libraries might 3021 // hijack the video tag and change its 'src' without destroying the Hls instance first 3022 if (this.media.src === this._objectUrl) { 3023 this.media.removeAttribute('src'); 3024 this.media.load(); 3025 } 3026 else { 3027 logger_1.logger.warn('media.src was changed by a third party - skip cleanup'); 3028 } 3029 } 3030 this.mediaSource = null; 3031 this.media = null; 3032 this._objectUrl = null; 3033 this.pendingTracks = {}; 3034 this.tracks = {}; 3035 this.sourceBuffer = {}; 3036 this.flushRange = []; 3037 this.segments = []; 3038 this.appended = 0; 3039 } 3040 this.onmso = this.onmse = this.onmsc = null; 3041 this.hls.trigger(events_1.default.MEDIA_DETACHED); 3042 }; 3043 BufferController.prototype.onMediaSourceOpen = function () { 3044 logger_1.logger.log('media source opened');
3045 this.hls.trigger(events_1.default.MEDIA_ATTACHED, { media: this.media }); 3046 var mediaSource = this.mediaSource; 3047 if (mediaSource) { 3048 // once received, don't listen anymore to sourceopen event 3049 mediaSource.removeEventListener('sourceopen', this.onmso); 3050 } 3051 this.checkPendingTracks(); 3052 }; 3053 BufferController.prototype.checkPendingTracks = function () { 3054 var _a = this, bufferCodecEventsExpected = _a.bufferCodecEventsExpected, pendingTracks = _a.pendingTracks; 3055 // Check if we've received all of the expected bufferCodec events. When none remain, create all the sourceBuffers at once. 3056 // This is important because the MSE spec allows implementations to throw QuotaExceededErrors if creating new sourceBuffers after 3057 // data has been appended to existing ones. 3058 // 2 tracks is the max (one for audio, one for video). If we've reach this max go ahead and create the buffers. 3059 var pendingTracksCount = Object.keys(pendingTracks).length; 3060 if ((pendingTracksCount && !bufferCodecEventsExpected) || pendingTracksCount === 2) { 3061 // ok, let's create them now ! 3062 this.createSourceBuffers(pendingTracks); 3063 this.pendingTracks = {}; 3064 // append any pending segments now ! 3065 this.doAppending(); 3066 } 3067 }; 3068 BufferController.prototype.onMediaSourceClose = function () { 3069 logger_1.logger.log('media source closed'); 3070 }; 3071 BufferController.prototype.onMediaSourceEnded = function () { 3072 logger_1.logger.log('media source ended'); 3073 }; 3074 BufferController.prototype.onSBUpdateEnd = function () { 3075 // update timestampOffset 3076 if (this.audioTimestampOffset) { 3077 var audioBuffer = this.sourceBuffer.audio; 3078 logger_1.logger.warn("change mpeg audio timestamp offset from " + audioBuffer.timestampOffset + " to " + this.audioTimestampOffset); 3079 audioBuffer.timestampOffset = this.audioTimestampOffset; 3080 delete this.audioTimestampOffset; 3081 } 3082 if (this._needsFlush) { 3083 this.doFlush(); 3084 } 3085 if (this._needsEos) { 3086 this.checkEos(); 3087 } 3088 this.appending = false; 3089 var parent = this.parent; 3090 // count nb of pending segments waiting for appending on this sourcebuffer 3091 var pending = this.segments.reduce(function (counter, segment) { return (segment.parent === parent) ? counter + 1 : counter; }, 0); 3092 // this.sourceBuffer is better to use than media.buffered as it is closer to the PTS data from the fragments 3093 var timeRanges = {}; 3094 var sourceBuffer = this.sourceBuffer; 3095 for (var streamType in sourceBuffer) { 3096 timeRanges[streamType] = sourceBuffer[streamType].buffered; 3097 } 3098 this.hls.trigger(events_1.default.BUFFER_APPENDED, { parent: parent, pending: pending, timeRanges: timeRanges }); 3099 // don't append in flushing mode 3100 if (!this._needsFlush) { 3101 this.doAppending(); 3102 } 3103 this.updateMediaElementDuration(); 3104 // appending goes first 3105 if (pending === 0) { 3106 this.flushLiveBackBuffer(); 3107 } 3108 }; 3109 BufferController.prototype.onSBUpdateError = function (event) { 3110 logger_1.logger.error('sourceBuffer error:', event); 3111 // according to http://www.w3.org/TR/media-source/#sourcebuffer-append-error 3112 // this error might not always be fatal (it is fatal if decode error is set, in that case 3113 // it will be followed by a mediaElement error ...) 3114 this.hls.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.MEDIA_ERROR, details: errors_1.ErrorDetails.BUFFER_APPENDING_ERROR, fatal: false }); 3115 // we don't need to do more than that, as accordin to the spec, updateend will be fired just after 3116 }; 3117 BufferController.prototype.onBufferReset = function () { 3118 var sourceBuffer = this.sourceBuffer; 3119 for (var type in sourceBuffer) { 3120 var sb = sourceBuffer[type]; 3121 try { 3122 this.mediaSource.removeSourceBuffer(sb); 3123 sb.removeEventListener('updateend', this.onsbue); 3124 sb.removeEventListener('error', this.onsbe); 3125 } 3126 catch (err) { 3127 } 3128 } 3129 this.sourceBuffer = {}; 3130 this.flushRange = []; 3131 this.segments = []; 3132 this.appended = 0; 3133 }; 3134 BufferController.prototype.onBufferCodecs = function (tracks) { 3135 var _this = this; 3136 // if source buffer(s) not created yet, appended buffer tracks in this.pendingTracks 3137 // if sourcebuffers already created, do nothing ... 3138 if (Object.keys(this.sourceBuffer).length) { 3139 return; 3140 }
3141 Object.keys(tracks).forEach(function (trackName) { 3142 _this.pendingTracks[trackName] = tracks[trackName]; 3143 }); 3144 var mediaSource = this.mediaSource; 3145 this.bufferCodecEventsExpected = Math.max(this.bufferCodecEventsExpected - 1, 0); 3146 if (mediaSource && mediaSource.readyState === 'open') { 3147 this.checkPendingTracks(); 3148 } 3149 }; 3150 BufferController.prototype.createSourceBuffers = function (tracks) { 3151 var sourceBuffer = this.sourceBuffer, mediaSource = this.mediaSource; 3152 for (var trackName in tracks) { 3153 if (!sourceBuffer[trackName]) { 3154 var track = tracks[trackName]; 3155 // use levelCodec as first priority 3156 var codec = track.levelCodec || track.codec; 3157 var mimeType = track.container + ";codecs=" + codec; 3158 logger_1.logger.log("creating sourceBuffer(" + mimeType + ")"); 3159 try { 3160 var sb = sourceBuffer[trackName] = mediaSource.addSourceBuffer(mimeType); 3161 sb.addEventListener('updateend', this.onsbue); 3162 sb.addEventListener('error', this.onsbe); 3163 this.tracks[trackName] = { codec: codec, container: track.container }; 3164 track.buffer = sb; 3165 } 3166 catch (err) { 3167 logger_1.logger.error("error while trying to add sourceBuffer:" + err.message); 3168 this.hls.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.MEDIA_ERROR, details: errors_1.ErrorDetails.BUFFER_ADD_CODEC_ERROR, fatal: false, err: err, mimeType: mimeType }); 3169 } 3170 } 3171 } 3172 this.hls.trigger(events_1.default.BUFFER_CREATED, { tracks: tracks }); 3173 }; 3174 BufferController.prototype.onBufferAppending = function (data) { 3175 if (!this._needsFlush) { 3176 if (!this.segments) { 3177 this.segments = [data]; 3178 } 3179 else { 3180 this.segments.push(data); 3181 } 3182 this.doAppending(); 3183 } 3184 }; 3185 BufferController.prototype.onBufferAppendFail = function (data) { 3186 logger_1.logger.error('sourceBuffer error:', data.event); 3187 // according to http://www.w3.org/TR/media-source/#sourcebuffer-append-error 3188 // this error might not always be fatal (it is fatal if decode error is set, in that case 3189 // it will be followed by a mediaElement error ...) 3190 this.hls.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.MEDIA_ERROR, details: errors_1.ErrorDetails.BUFFER_APPENDING_ERROR, fatal: false }); 3191 }; 3192 // on BUFFER_EOS mark matching sourcebuffer(s) as ended and trigger checkEos() 3193 BufferController.prototype.onBufferEos = function (data) { 3194 var sb = this.sourceBuffer; 3195 var dataType = data.type; 3196 for (var type in sb) { 3197 if (!dataType || type === dataType) { 3198 if (!sb[type].ended) { 3199 sb[type].ended = true; 3200 logger_1.logger.log(type + " sourceBuffer now EOS"); 3201 } 3202 } 3203 } 3204 this.checkEos(); 3205 }; 3206 // if all source buffers are marked as ended, signal endOfStream() to MediaSource. 3207 BufferController.prototype.checkEos = function () { 3208 var sb = this.sourceBuffer, mediaSource = this.mediaSource; 3209 if (!mediaSource || mediaSource.readyState !== 'open') { 3210 this._needsEos = false; 3211 return; 3212 } 3213 for (var type in sb) { 3214 var sbobj = sb[type]; 3215 if (!sbobj.ended) { 3216 return; 3217 } 3218 if (sbobj.updating) { 3219 this._needsEos = true; 3220 return; 3221 } 3222 } 3223 logger_1.logger.log('all media data are available, signal endOfStream() to MediaSource and stop loading fragment'); 3224 // Notify the media element that it now has all of the media data 3225 try { 3226 mediaSource.endOfStream(); 3227 } 3228 catch (e) { 3229 logger_1.logger.warn('exception while calling mediaSource.endOfStream()'); 3230 } 3231 this._needsEos = false;
3232 }; 3233 BufferController.prototype.onBufferFlushing = function (data) { 3234 this.flushRange.push({ start: data.startOffset, end: data.endOffset, type: data.type }); 3235 // attempt flush immediately 3236 this.flushBufferCounter = 0; 3237 this.doFlush(); 3238 }; 3239 BufferController.prototype.flushLiveBackBuffer = function () { 3240 // clear back buffer for live only 3241 if (!this._live) { 3242 return; 3243 } 3244 var liveBackBufferLength = this.hls.config.liveBackBufferLength; 3245 if (!isFinite(liveBackBufferLength) || liveBackBufferLength < 0) { 3246 return; 3247 } 3248 var currentTime = this.media.currentTime; 3249 var sourceBuffer = this.sourceBuffer; 3250 var bufferTypes = Object.keys(sourceBuffer); 3251 var targetBackBufferPosition = currentTime - Math.max(liveBackBufferLength, this._levelTargetDuration); 3252 for (var index = bufferTypes.length - 1; index >= 0; index--) { 3253 var bufferType = bufferTypes[index], buffered = sourceBuffer[bufferType].buffered; 3254 // when target buffer start exceeds actual buffer start 3255 if (buffered.length > 0 && targetBackBufferPosition > buffered.start(0)) { 3256 // remove buffer up until current time minus minimum back buffer length (removing buffer too close to current 3257 // time will lead to playback freezing) 3258 // credits for level target duration - https://github.com/videojs/http-streaming/blob/3132933b6aa99ddefab29c10447624efd6fd6e52/src/segment-loader.js#L91 3259 this.removeBufferRange(bufferType, sourceBuffer[bufferType], 0, targetBackBufferPosition); 3260 } 3261 } 3262 }; 3263 BufferController.prototype.onLevelUpdated = function (_a) { 3264 var details = _a.details; 3265 if (details.fragments.length > 0) { 3266 this._levelDuration = details.totalduration + details.fragments[0].start; 3267 this._levelTargetDuration = details.averagetargetduration || details.targetduration || 10; 3268 this._live = details.live; 3269 this.updateMediaElementDuration(); 3270 } 3271 }; 3272 /** 3273 * Update Media Source duration to current level duration or override to Infinity if configuration parameter 3274 * 'liveDurationInfinity` is set to `true` 3275 * More details: https://github.com/video-dev/hls.js/issues/355 3276 */ 3277 BufferController.prototype.updateMediaElementDuration = function () { 3278 var config = this.hls.config; 3279 var duration; 3280 if (this._levelDuration === null || 3281 !this.media || 3282 !this.mediaSource || 3283 !this.sourceBuffer || 3284 this.media.readyState === 0 || 3285 this.mediaSource.readyState !== 'open') { 3286 return; 3287 } 3288 for (var type in this.sourceBuffer) { 3289 if (this.sourceBuffer[type].updating === true) { 3290 // can't set duration whilst a buffer is updating 3291 return; 3292 } 3293 } 3294 duration = this.media.duration; 3295 // initialise to the value that the media source is reporting 3296 if (this._msDuration === null) { 3297 this._msDuration = this.mediaSource.duration; 3298 } 3299 if (this._live === true && config.liveDurationInfinity === true) { 3300 // Override duration to Infinity 3301 logger_1.logger.log('Media Source duration is set to Infinity'); 3302 this._msDuration = this.mediaSource.duration = Infinity; 3303 } 3304 else if ((this._levelDuration > this._msDuration && this._levelDuration > duration) || !Number.isFinite(duration)) { 3305 // levelDuration was the last value we set. 3306 // not using mediaSource.duration as the browser may tweak this value 3307 // only update Media Source duration if its value increase, this is to avoid 3308 // flushing already buffered portion when switching between quality level 3309 logger_1.logger.log("Updating Media Source duration to " + this._levelDuration.toFixed(3)); 3310 this._msDuration = this.mediaSource.duration = this._levelDuration; 3311 } 3312 }; 3313 BufferController.prototype.doFlush = function () { 3314 // loop through all buffer ranges to flush 3315 while (this.flushRange.length) { 3316 var range = this.flushRange[0]; 3317 // flushBuffer will abort any buffer append in progress and flush Audio/Video Buffer 3318 if (this.flushBuffer(range.start, range.end, range.type)) { 3319 // range flushed, remove from flush array 3320 this.flushRange.shift(); 3321 this.flushBufferCounter = 0; 3322 } 3323 else { 3324 this._needsFlush = true; 3325 // avoid looping, wait for SB update end to retrigger a flush 3326 return; 3327 } 3328 } 3329 if (this.flushRange.length === 0) { 3330 // everything flushed 3331 this._needsFlush = false;
3332 // let's recompute this.appended, which is used to avoid flush looping 3333 var appended = 0; 3334 var sourceBuffer = this.sourceBuffer; 3335 try { 3336 for (var type in sourceBuffer) { 3337 appended += sourceBuffer[type].buffered.length; 3338 } 3339 } 3340 catch (error) { 3341 // error could be thrown while accessing buffered, in case sourcebuffer has already been removed from MediaSource 3342 // this is harmess at this stage, catch this to avoid reporting an internal exception 3343 logger_1.logger.error('error while accessing sourceBuffer.buffered'); 3344 } 3345 this.appended = appended; 3346 this.hls.trigger(events_1.default.BUFFER_FLUSHED); 3347 } 3348 }; 3349 BufferController.prototype.doAppending = function () { 3350 var _a = this, hls = _a.hls, segments = _a.segments, sourceBuffer = _a.sourceBuffer; 3351 if (Object.keys(sourceBuffer).length) { 3352 if (this.media.error) { 3353 this.segments = []; 3354 logger_1.logger.error('trying to append although a media error occured, flush segment and abort'); 3355 return; 3356 } 3357 if (this.appending) { 3358 // logger.log(`sb appending in progress`); 3359 return; 3360 } 3361 if (segments && segments.length) { 3362 var segment = segments.shift(); 3363 try { 3364 var type = segment.type, sb = sourceBuffer[type]; 3365 if (sb) { 3366 if (!sb.updating) { 3367 // reset sourceBuffer ended flag before appending segment 3368 sb.ended = false; 3369 // logger.log(`appending ${segment.content} ${type} SB, size:${segment.data.length}, ${segment.parent}`); 3370 this.parent = segment.parent; 3371 sb.appendBuffer(segment.data); 3372 this.appendError = 0; 3373 this.appended++; 3374 this.appending = true; 3375 } 3376 else { 3377 segments.unshift(segment); 3378 } 3379 } 3380 else { 3381 // in case we don't have any source buffer matching with this segment type, 3382 // it means that Mediasource fails to create sourcebuffer 3383 // discard this segment, and trigger update end 3384 this.onSBUpdateEnd(); 3385 } 3386 } 3387 catch (err) { 3388 // in case any error occured while appending, put back segment in segments table 3389 logger_1.logger.error("error while trying to append buffer:" + err.message); 3390 segments.unshift(segment); 3391 var event_1 = { type: errors_1.ErrorTypes.MEDIA_ERROR, parent: segment.parent }; 3392 if (err.code !== 22) { 3393 if (this.appendError) { 3394 this.appendError++; 3395 } 3396 else { 3397 this.appendError = 1; 3398 } 3399 event_1.details = errors_1.ErrorDetails.BUFFER_APPEND_ERROR; 3400 /* with UHD content, we could get loop of quota exceeded error until 3401 browser is able to evict some data from sourcebuffer. retrying help recovering this 3402 */ 3403 if (this.appendError > hls.config.appendErrorMaxRetry) { 3404 logger_1.logger.log("fail " + hls.config.appendErrorMaxRetry + " times to append segment in sourceBuffer"); 3405 this.segments = []; 3406 event_1.fatal = true; 3407 hls.trigger(events_1.default.ERROR, event_1); 3408 } 3409 else { 3410 event_1.fatal = false;
3411 hls.trigger(events_1.default.ERROR, event_1); 3412 } 3413 } 3414 else { 3415 // QuotaExceededError: http://www.w3.org/TR/html5/infrastructure.html#quotaexceedederror 3416 // let's stop appending any segments, and report BUFFER_FULL_ERROR error 3417 this.segments = []; 3418 event_1.details = errors_1.ErrorDetails.BUFFER_FULL_ERROR; 3419 event_1.fatal = false; 3420 hls.trigger(events_1.default.ERROR, event_1); 3421 } 3422 } 3423 } 3424 } 3425 }; 3426 /* 3427 flush specified buffered range, 3428 return true once range has been flushed. 3429 as sourceBuffer.remove() is asynchronous, flushBuffer will be retriggered on sourceBuffer update end 3430 */ 3431 BufferController.prototype.flushBuffer = function (startOffset, endOffset, typeIn) { 3432 var sb; 3433 var sourceBuffer = this.sourceBuffer; 3434 if (Object.keys(sourceBuffer).length) { 3435 logger_1.logger.log("flushBuffer,pos/start/end: " + this.media.currentTime.toFixed(3) + "/" + startOffset + "/" + endOffset); 3436 // safeguard to avoid infinite looping : don't try to flush more than the nb of appended segments 3437 if (this.flushBufferCounter < this.appended) { 3438 for (var type in sourceBuffer) { 3439 // check if sourcebuffer type is defined (typeIn): if yes, let's only flush this one 3440 // if no, let's flush all sourcebuffers 3441 if (typeIn && type !== typeIn) { 3442 continue; 3443 } 3444 sb = sourceBuffer[type]; 3445 // we are going to flush buffer, mark source buffer as 'not ended' 3446 sb.ended = false; 3447 if (!sb.updating) { 3448 if (this.removeBufferRange(type, sb, startOffset, endOffset)) { 3449 this.flushBufferCounter++; 3450 return false; 3451 } 3452 } 3453 else { 3454 logger_1.logger.warn('cannot flush, sb updating in progress'); 3455 return false; 3456 } 3457 } 3458 } 3459 else { 3460 logger_1.logger.warn('abort flushing too many retries'); 3461 } 3462 logger_1.logger.log('buffer flushed'); 3463 } 3464 // everything flushed ! 3465 return true; 3466 }; 3467 /** 3468 * Removes first buffered range from provided source buffer that lies within given start and end offsets. 3469 * 3470 * @param type Type of the source buffer, logging purposes only. 3471 * @param sb Target SourceBuffer instance. 3472 * @param startOffset 3473 * @param endOffset 3474 * 3475 * @returns {boolean} True when source buffer remove requested. 3476 */ 3477 BufferController.prototype.removeBufferRange = function (type, sb, startOffset, endOffset) { 3478 try { 3479 for (var i = 0; i < sb.buffered.length; i++) { 3480 var bufStart = sb.buffered.start(i); 3481 var bufEnd = sb.buffered.end(i); 3482 var removeStart = Math.max(bufStart, startOffset); 3483 var removeEnd = Math.min(bufEnd, endOffset); 3484 /* sometimes sourcebuffer.remove() does not flush 3485 the exact expected time range. 3486 to avoid rounding issues/infinite loop, 3487 only flush buffer range of length greater than 500ms. 3488 */ 3489 if (Math.min(removeEnd, bufEnd) - removeStart > 0.5) { 3490 logger_1.logger.log("sb remove " + type + " [" + removeStart + "," + removeEnd + "], of [" + bufStart + "," + bufEnd + "], pos:" + this.media.currentTime); 3491 sb.remove(removeStart, removeEnd); 3492 return true; 3493 } 3494 } 3495 } 3496 catch (error) { 3497 logger_1.logger.warn('removeBufferRange failed', error); 3498 } 3499 return false;
3500 }; 3501 return BufferController; 3502}(event_handler_1.default)); 3503exports.default = BufferController; 3504 3505/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 3506 3507/***/ }), 3508
3509/***/ "./src/controller/cap-level-controller.js": 3510/*!************************************************!*\ 3511 !*** ./src/controller/cap-level-controller.js ***! 3512 \************************************************/ 3513/*! no static exports found */ 3514/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 3515/***/ (function(module, exports, __webpack_require__) { 3516 3517"use strict"; 3518/* WEBPACK VAR INJECTION */(function(Number) { 3519/* 3520 * cap stream level to media size dimension controller 3521*/ 3522var __extends = (this && this.__extends) || (function () { 3523 var extendStatics = Object.setPrototypeOf || 3524 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 3525 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 3526 return function (d, b) { 3527 extendStatics(d, b); 3528 function __() { this.constructor = d; } 3529 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 3530 }; 3531})(); 3532Object.defineProperty(exports, "__esModule", { value: true }); 3533var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 3534var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 3535var CapLevelController = /** @class */ (function (_super) { 3536 __extends(CapLevelController, _super); 3537 function CapLevelController(hls) { 3538 var _this = _super.call(this, hls, events_1.default.FPS_DROP_LEVEL_CAPPING, events_1.default.MEDIA_ATTA
vendor: 4,301 bytes, lines 3538-3634
3538CHING, events_1.default.MANIFEST_PARSED, events_1.default.BUFFER_CODECS, events_1.default.MEDIA_DETACHING) || this; 3539 _this.autoLevelCapping = Number.POSITIVE_INFINITY; 3540 _this.firstLevel = null; 3541 _this.levels = []; 3542 _this.media = null; 3543 _this.restrictedLevels = []; 3544 _this.timer = null; 3545 return _this; 3546 } 3547 CapLevelController.prototype.destroy = function () { 3548 if (this.hls.config.capLevelToPlayerSize) { 3549 this.media = null; 3550 this._stopCapping(); 3551 } 3552 }; 3553 CapLevelController.prototype.onFpsDropLevelCapping = function (data) { 3554 // Don't add a restricted level more than once 3555 if (CapLevelController.isLevelAllowed(data.droppedLevel, this.restrictedLevels)) { 3556 this.restrictedLevels.push(data.droppedLevel); 3557 } 3558 }; 3559 CapLevelController.prototype.onMediaAttaching = function (data) { 3560 this.media = data.media instanceof window.HTMLVideoElement ? data.media : null; 3561 }; 3562 CapLevelController.prototype.onManifestParsed = function (data) { 3563 var hls = this.hls; 3564 this.restrictedLevels = []; 3565 this.levels = data.levels; 3566 this.firstLevel = data.firstLevel; 3567 if (hls.config.capLevelToPlayerSize && data.video) { 3568 // Start capping immediately if the manifest has signaled video codecs 3569 this._startCapping(); 3570 } 3571 }; 3572 // Only activate capping when playing a video stream; otherwise, multi-bitrate audio-only streams will be restricted 3573 // to the first level 3574 CapLevelController.prototype.onBufferCodecs = function (data) { 3575 var hls = this.hls; 3576 if (hls.config.capLevelToPlayerSize && data.video) { 3577 // If the manifest did not signal a video codec capping has been deferred until we're certain video is present 3578 this._startCapping(); 3579 } 3580 }; 3581 CapLevelController.prototype.onLevelsUpdated = function (data) { 3582 this.levels = data.levels; 3583 }; 3584 CapLevelController.prototype.onMediaDetaching = function () { 3585 this._stopCapping(); 3586 }; 3587 CapLevelController.prototype.detectPlayerSize = function () { 3588 if (this.media) { 3589 var levelsLength = this.levels ? this.levels.length : 0; 3590 if (levelsLength) { 3591 var hls_1 = this.hls; 3592 hls_1.autoLevelCapping = this.getMaxLevel(levelsLength - 1); 3593 if (hls_1.autoLevelCapping > this.autoLevelCapping) { 3594 // if auto level capping has a higher value for the previous one, flush the buffer using nextLevelSwitch 3595 // usually happen when the user go to the fullscreen mode. 3596 hls_1.streamController.nextLevelSwitch(); 3597 } 3598 this.autoLevelCapping = hls_1.autoLevelCapping; 3599 } 3600 } 3601 }; 3602 /* 3603 * returns level should be the one with the dimensions equal or greater than the media (player) dimensions (so the video will be downscaled) 3604 */ 3605 CapLevelController.prototype.getMaxLevel = function (capLevelIndex) { 3606 var _this = this; 3607 if (!this.levels) { 3608 return -1; 3609 } 3610 var validLevels = this.levels.filter(function (level, index) { 3611 return CapLevelController.isLevelAllowed(index, _this.restrictedLevels) && index <= capLevelIndex; 3612 }); 3613 return CapLevelController.getMaxLevelByMediaSize(validLevels, this.mediaWidth, this.mediaHeight); 3614 }; 3615 CapLevelController.prototype._startCapping = function () { 3616 if (this.timer) { 3617 // Don't reset capping if started twice; this can happen if the manifest signals a video codec 3618 return; 3619 } 3620 this.autoLevelCapping = Number.POSITIVE_INFINITY; 3621 this.hls.firstLevel = this.getMaxLevel(this.firstLevel); 3622 clearInterval(this.timer); 3623 this.timer = setInterval(this.detectPlayerSize.bind(this), 1000); 3624 this.detectPlayerSize(); 3625 }; 3626 CapLevelController.prototype._stopCapping = function () { 3627 this.restrictedLevels = []; 3628 this.firstLevel = null; 3629 this.autoLevelCapping = Number.POSITIVE_INFINITY; 3630 if (this.timer) { 3631 this.timer = clearInterval(this.timer); 3632 this.timer = null; 3633 } 3634 };
3635 Object.defineProperty(CapLevelController.prototype, "mediaWidth", { 3636 get: function () { 3637 var width; 3638 var media = this.media; 3639 if (media) { 3640 width = media.width || media.clientWidth || media.offsetWidth; 3641 width *= CapLevelController.contentScaleFactor; 3642 } 3643 return width; 3644 }, 3645 enumerable: true, 3646 configurable: true 3647 }); 3648 Object.defineProperty(CapLevelController.prototype, "mediaHeight", { 3649 get: function () { 3650 var height; 3651 var media = this.media; 3652 if (media) { 3653 height = media.height || media.clientHeight || media.offsetHeight; 3654 height *= CapLevelController.contentScaleFactor; 3655 } 3656 return height; 3657 }, 3658 enumerable: true, 3659 configurable: true 3660 }); 3661 Object.defineProperty(CapLevelController, "contentScaleFactor", { 3662 get: function () { 3663 var pixelRatio = 1; 3664 try { 3665 pixelRatio = window.devicePixelRatio; 3666 } 3667 catch (e) { } 3668 return pixelRatio; 3669 }, 3670 enumerable: true, 3671 configurable: true 3672 }); 3673 CapLevelController.isLevelAllowed = function (level, restrictedLevels) { 3674 if (restrictedLevels === void 0) { restrictedLevels = []; } 3675 return restrictedLevels.indexOf(level) === -1; 3676 }; 3677 CapLevelController.getMaxLevelByMediaSize = function (levels, width, height) { 3678 if (!levels || (levels && !levels.length)) { 3679 return -1; 3680 } 3681 // Levels can have the same dimensions but differing bandwidths - since levels are ordered, we can look to the next 3682 // to determine whether we've chosen the greatest bandwidth for the media's dimensions 3683 var atGreatestBandiwdth = function (curLevel, nextLevel) { 3684 if (!nextLevel) { 3685 return true; 3686 } 3687 return curLevel.width !== nextLevel.width || curLevel.height !== nextLevel.height; 3688 }; 3689 // If we run through the loop without breaking, the media's dimensions are greater than every level, so default to 3690 // the max level 3691 var maxLevelIndex = levels.length - 1; 3692 for (var i = 0; i < levels.length; i += 1) { 3693 var level = levels[i]; 3694 if ((level.width >= width || level.height >= height) && atGreatestBandiwdth(level, levels[i + 1])) { 3695 maxLevelIndex = i; 3696 break; 3697 } 3698 } 3699 return maxLevelIndex; 3700 }; 3701 return CapLevelController; 3702}(event_handler_1.default)); 3703exports.default = CapLevelController; 3704 3705/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 3706 3707/***/ }), 3708
3709/***/ "./src/controller/eme-controller.js": 3710/*!******************************************!*\ 3711 !*** ./src/controller/eme-controller.js ***! 3712 \******************************************/ 3713/*! no static exports found */ 3714/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 3715/***/ (function(module, exports, __webpack_require__) { 3716 3717"use strict"; 3718 3719/** 3720 * @author Stephan Hesse <[email protected]> | <[email protected]> 3721 * 3722 * DRM support for Hls.js 3723 */ 3724var __extends = (this && this.__extends) || (function () { 3725 var extendStatics = Object.setPrototypeOf || 3726 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 3727 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 3728 return function (d, b) { 3729 extendStatics(d, b); 3730 function __() { this.constructor = d; } 3731 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 3732 }; 3733})(); 3734Object.defineProperty(exports, "__esModule", { value: true }); 3735var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 3736var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 3737var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 3738var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 3739var XMLHttpRequest = window.XMLHttpRequest; 3740var MAX_LICENSE_REQUEST_FAILURES = 3; 3741/** 3742 * @see https://developer.mozilla.org/en-US/docs/Web/API/Navigator/requestMediaKeySystemAccess 3743 */ 3744var KeySystems = { 3745 WIDEVINE: 'com.widevine.alpha', 3746 PLAYREADY: 'com.microsoft.playready' 3747}; 3748/** 3749 * @see https://developer.mozilla.org/en-US/docs/Web/API/MediaKeySystemConfiguration 3750 * @param {Array<string>} audioCodecs List of required audio codecs to support 3751 * @param {Array<string>} videoCodecs List of required video codecs to support 3752 * @param {object} drmSystemOptions Optional parameters/requirements for the key-system 3753 * @returns {Array<MediaSystemConfiguration>} An array of supported configurations 3754 */ 3755var createWidevineMediaKeySystemConfigurations = function (audioCodecs, videoCodecs, drmSystemOptions) { 3756 var baseConfig = { 3757 // initDataTypes: ['keyids', 'mp4'], 3758 // label: "", 3759 // persistentState: "not-allowed", // or "required" ? 3760 // distinctiveIdentifier: "not-allowed", // or "required" ? 3761 // sessionTypes: ['temporary'], 3762 videoCapabilities: [ 3763 // { contentType: 'video/mp4; codecs="avc1.42E01E"' } 3764 ] 3765 }; 3766 videoCodecs.forEach(function (codec) { 3767 baseConfig.videoCapabilities.push({ 3768 contentType: "video/mp4; codecs=\"" + codec + "\"" 3769 }); 3770 }); 3771 return [ 3772 baseConfig 3773 ]; 3774}; 3775/** 3776 * The idea here is to handle key-system (and their respective platforms) specific configuration differences 3777 * in order to work with the local requestMediaKeySystemAccess method. 3778 * 3779 * We can also rule-out platform-related key-system support at this point b
3779y throwing an error or returning null. 3780 * 3781 * @param {string} keySystem Identifier for the key-system, see `KeySystems` enum 3782 * @param {Array<string>} audioCodecs List of required audio codecs to support 3783 * @param {Array<string>} videoCodecs List of required video codecs to support 3784 * @returns {Array<MediaSystemConfiguration> | null} A non-empty Array of MediaKeySystemConfiguration objects or `null` 3785 */ 3786var getSupportedMediaKeySystemConfigurations = function (keySystem, audioCodecs, videoCodecs) { 3787 switch (keySystem) { 3788 case KeySystems.WIDEVINE: 3789 return createWidevineMediaKeySystemConfigurations(audioCodecs, videoCodecs); 3790 default: 3791 throw Error('Unknown key-system: ' + keySystem); 3792 } 3793}; 3794/** 3795 * Controller to deal with encrypted media extensions (EME) 3796 * @see https://developer.mozilla.org/en-US/docs/Web/API/Encrypted_Media_Extensions_API 3797 * 3798 * @class 3799 * @constructor 3800 */ 3801var EMEController = /** @class */ (function (_super) { 3802 __extends(EMEController, _super); 3803 /** 3804 * @constructs 3805 * @param {Hls} hls Our Hls.js instance 3806 */ 3807 function EMEController(hls) { 3808 var _this = _super.call(this, hls, events_1.default.MEDIA_ATTA
3808CHED, events_1.default.MANIFEST_PARSED) || this; 3809 _this._widevineLicenseUrl = hls.config.widevineLicenseUrl; 3810 _this._licenseXhrSetup = hls.config.licenseXhrSetup; 3811 _this._emeEnabled = hls.config.emeEnabled; 3812 _this._requestMediaKeySystemAccess = hls.config.requestMediaKeySystemAccessFunc; 3813 _this._mediaKeysList = []; 3814 _this._media = null; 3815 _this._hasSetMediaKeys = false; 3816 _this._isMediaEncrypted = false; 3817 _this._requestLicenseFailureCount = 0; 3818 return _this; 3819 } 3820 /** 3821 * 3822 * @param {string} keySystem Identifier for the key-system, see `KeySystems` enum 3823 * @returns {string} License server URL for key-system (if any configured, otherwise causes error) 3824 */ 3825 EMEController.prototype.getLicenseServerUrl = function (keySystem) { 3826 var url; 3827 switch (keySystem) { 3828 case KeySystems.WIDEVINE: 3829 url = this._widevineLicenseUrl; 3830 break; 3831 default: 3832 url = null; 3833 break; 3834 } 3835 if (!url) { 3836 logger_1.logger.error("No license server URL configured for key-system \"" + keySystem + "\""); 3837 this.hls.trigger(events_1.default.ERROR, { 3838 type: errors_1.ErrorTypes.KEY_SYSTEM_ERROR, 3839 details: errors_1.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED, 3840 fatal: true 3841 }); 3842 } 3843 return url; 3844 }; 3845 /** 3846 * Requests access object and adds it to our list upon success 3847 * @private 3848 * @param {string} keySystem System ID (see `KeySystems`) 3849 * @param {Array<string>} audioCodecs List of required audio codecs to support 3850 * @param {Array<string>} videoCodecs List of required video codecs to support 3851 */ 3852 EMEController.prototype._attemptKeySystemAccess = function (keySystem, audioCodecs, videoCodecs) { 3853 // TODO: add other DRM "options" 3854 var _this = this; 3855 var mediaKeySystemConfigs = getSupportedMediaKeySystemConfigurations(keySystem, audioCodecs, videoCodecs); 3856 if (!mediaKeySystemConfigs) { 3857 logger_1.logger.warn('Can not create config for key-system (maybe because platform is not supported):', keySystem); 3858 return; 3859 } 3860 logger_1.logger.log('Requesting encrypted media key-system access'); 3861 // expecting interface like window.navigator.requestMediaKeySystemAccess 3862 this.requestMediaKeySystemAccess(keySystem, mediaKeySystemConfigs) 3863 .then(function (mediaKeySystemAccess) { 3864 _this._onMediaKeySystemAccessObtained(keySystem, mediaKeySystemAccess); 3865 }) 3866 .catch(function (err) { 3867 logger_1.logger.error("Failed to obtain key-system \"" + keySystem + "\" access:", err); 3868 }); 3869 }; 3870 Object.defineProperty(EMEController.prototype, "requestMediaKeySystemAccess", { 3871 get: function () { 3872 if (!this._requestMediaKeySystemAccess) { 3873 throw new Error('No requestMediaKeySystemAccess function configured'); 3874 } 3875 return this._requestMediaKeySystemAccess; 3876 }, 3877 enumerable: true, 3878 configurable: true 3879 }); 3880 /** 3881 * Handles obtaining access to a key-system 3882 * 3883 * @param {string} keySystem 3884 * @param {MediaKeySystemAccess} mediaKeySystemAccess https://developer.mozilla.org/en-US/docs/Web/API/MediaKeySystemAccess 3885 */ 3886 EMEController.prototype._onMediaKeySystemAccessObtained = function (keySystem, mediaKeySystemAccess) { 3887 var _this = this; 3888 logger_1.logger.log("Access for key-system \"" + keySystem + "\" obtained"); 3889 var mediaKeysListItem = { 3890 mediaKeys: null, 3891 mediaKeysSession: null, 3892 mediaKeysSessionInitialized: false, 3893 mediaKeySystemAccess: mediaKeySystemAccess, 3894 mediaKeySystemDomain: keySystem 3895 }; 3896 this._mediaKeysList.push(mediaKeysListItem); 3897 mediaKeySystemAccess.createMediaKeys() 3898 .then(function (mediaKeys) { 3899 mediaKeysListItem.mediaKeys = mediaKeys; 3900 logger_1.logger.log("Media-keys created for key-system \"" + keySystem + "\""); 3901 _this._onMediaKeysCreated(); 3902 }) 3903 .catch(function (err) { 3904 logger_1.logger.error('Failed to create media-keys:', err); 3905 }); 3906 }; 3907 /** 3908 * Handles key-creation (represents access to CDM). We are going to create key-sessions upon this
3909 * for all existing keys where no session exists yet. 3910 */ 3911 EMEController.prototype._onMediaKeysCreated = function () { 3912 var _this = this; 3913 // check for all key-list items if a session exists, otherwise, create one 3914 this._mediaKeysList.forEach(function (mediaKeysListItem) { 3915 if (!mediaKeysListItem.mediaKeysSession) { 3916 mediaKeysListItem.mediaKeysSession = mediaKeysListItem.mediaKeys.createSession(); 3917 _this._onNewMediaKeySession(mediaKeysListItem.mediaKeysSession); 3918 } 3919 }); 3920 }; 3921 /** 3922 * 3923 * @param {*} keySession 3924 */ 3925 EMEController.prototype._onNewMediaKeySession = function (keySession) { 3926 var _this = this; 3927 logger_1.logger.log("New key-system session " + keySession.sessionId); 3928 keySession.addEventListener('message', function (event) { 3929 _this._onKeySessionMessage(keySession, event.message); 3930 }, false); 3931 }; 3932 EMEController.prototype._onKeySessionMessage = function (keySession, message) { 3933 logger_1.logger.log('Got EME message event, creating license request'); 3934 this._requestLicense(message, function (data) { 3935 logger_1.logger.log('Received license data, updating key-session'); 3936 keySession.update(data); 3937 }); 3938 }; 3939 EMEController.prototype._onMediaEncrypted = function (initDataType, initData) { 3940 logger_1.logger.log("Media is encrypted using \"" + initDataType + "\" init data type"); 3941 this._isMediaEncrypted = true; 3942 this._mediaEncryptionInitDataType = initDataType; 3943 this._mediaEncryptionInitData = initData; 3944 this._attemptSetMediaKeys(); 3945 this._generateRequestWithPreferredKeySession(); 3946 }; 3947 EMEController.prototype._attemptSetMediaKeys = function () { 3948 if (!this._hasSetMediaKeys) { 3949 // FIXME: see if we can/want/need-to really to deal with several potential key-sessions? 3950 var keysListItem = this._mediaKeysList[0]; 3951 if (!keysListItem || !keysListItem.mediaKeys) { 3952 logger_1.logger.error('Fatal: Media is encrypted but no CDM access or no keys have been obtained yet'); 3953 this.hls.trigger(events_1.default.ERROR, { 3954 type: errors_1.ErrorTypes.KEY_SYSTEM_ERROR, 3955 details: errors_1.ErrorDetails.KEY_SYSTEM_NO_KEYS, 3956 fatal: true 3957 }); 3958 return; 3959 } 3960 logger_1.logger.log('Setting keys for encrypted media'); 3961 this._media.setMediaKeys(keysListItem.mediaKeys); 3962 this._hasSetMediaKeys = true; 3963 } 3964 }; 3965 EMEController.prototype._generateRequestWithPreferredKeySession = function () { 3966 var _this = this; 3967 // FIXME: see if we can/want/need-to really to deal with several potential key-sessions? 3968 var keysListItem = this._mediaKeysList[0]; 3969 if (!keysListItem) { 3970 logger_1.logger.error('Fatal: Media is encrypted but not any key-system access has been obtained yet'); 3971 this.hls.trigger(events_1.default.ERROR, { 3972 type: errors_1.ErrorTypes.KEY_SYSTEM_ERROR, 3973 details: errors_1.ErrorDetails.KEY_SYSTEM_NO_ACCESS, 3974 fatal: true 3975 }); 3976 return; 3977 } 3978 if (keysListItem.mediaKeysSessionInitialized) { 3979 logger_1.logger.warn('Key-Session already initialized but requested again'); 3980 return; 3981 } 3982 var keySession = keysListItem.mediaKeysSession; 3983 if (!keySession) { 3984 logger_1.logger.error('Fatal: Media is encrypted but no key-session existing'); 3985 this.hls.trigger(events_1.default.ERROR, { 3986 type: errors_1.ErrorTypes.KEY_SYSTEM_ERROR, 3987 details: errors_1.ErrorDetails.KEY_SYSTEM_NO_SESSION, 3988 fatal: true 3989 }); 3990 } 3991 var initDataType = this._mediaEncryptionInitDataType; 3992 var initData = this._mediaEncryptionInitData; 3993 logger_1.logger.log("Generating key-session request for \"" + initDataType + "\" init data type"); 3994 keysListItem.mediaKeysSessionInitialized = true; 3995 keySession.generateRequest(initDataType, initData) 3996 .then(function () { 3997 logger_1.logger.debug('Key-session generation succeeded'); 3998 }) 3999 .catch(function (err) { 4000 logger_1.logger.error('Error generating key-session request:', err); 4001 _this.hls.trigger(events_1.default.ERROR, { 4002 type: errors_1.ErrorTypes.KEY_SYSTEM_ERROR, 4003 details: errors_1.ErrorDetails.KEY_SYSTEM_NO_SESSION, 4004 fatal: false 4005 }); 4006 }); 4007 }; 4008 /** 4009 * @param {string} url License server URL 4010 * @param {ArrayBuffer} keyMessage Message data issued by key-system 4011 * @param {function} callback Called when XHR has succeeded 4012 * @returns {XMLHttpRequest} Unsent (but opened state) XHR object 4013 */ 4014 EMEController.prototype._createLicenseXhr = function (url, keyMessage, callback) { 4015 var xhr = new XMLHttpRequest(); 4016 var licenseXhrSetup = this._licenseXhrSetup; 4017 try { 4018 if (licenseXhrSetup) { 4019 try { 4020 licenseXhrSetup(xhr, url); 4021 } 4022 catch (e) { 4023 // let's try to open before running setup 4024 xhr.open('POST', url, true); 4025 licenseXhrSetup(xhr, url); 4026 } 4027 } 4028 // if licenseXhrSetup did not yet call open, let's do it now 4029 if (!xhr.readyState) { 4030 xhr.open('POST', url, true); 4031 } 4032 } 4033 catch (e) { 4034 // IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS 4035 logger_1.logger.error('Error setting up key-system license XHR', e); 4036 this.hls.trigger(events_1.default.ERROR, { 4037 type: errors_1.ErrorTypes.KEY_SYSTEM_ERROR, 4038 details: errors_1.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED, 4039 fatal: true 4040 }); 4041 return; 4042 } 4043 xhr.responseType = 'arraybuffer'; 4044 xhr.onreadystatechange = 4045 this._onLicenseRequestReadyStageChange.bind(this, xhr, url, keyMessage, callback); 4046 return xhr; 4047 }; 4048 /** 4049 * @param {XMLHttpRequest} xhr 4050 * @param {string} url License server URL 4051 * @param {ArrayBuffer} keyMessage Message data issued by key-system 4052 * @param {function} callback Called when XHR has succeeded 4053 * 4054 */ 4055 EMEController.prototype._onLicenseRequestReadyStageChange = function (xhr, url, keyMessage, callback) { 4056 switch (xhr.readyState) { 4057 case 4: 4058 if (xhr.status === 200) { 4059 this._requestLicenseFailureCount = 0; 4060 logger_1.logger.log('License request succeeded'); 4061 callback(xhr.response); 4062 } 4063 else { 4064 logger_1.logger.error("License Request XHR failed (" + url + "). Status: " + xhr.status + " (" + xhr.statusText + ")"); 4065 this._requestLicenseFailureCount++; 4066 if (this._requestLicenseFailureCount <= MAX_LICENSE_REQUEST_FAILURES) { 4067 var attemptsLeft = MAX_LICENSE_REQUEST_FAILURES - this._requestLicenseFailureCount + 1; 4068 logger_1.logger.warn("Retrying license request, " + attemptsLeft + " attempts left"); 4069 this._requestLicense(keyMessage, callback); 4070 return; 4071 } 4072 this.hls.trigger(events_1.default.ERROR, { 4073 type: errors_1.ErrorTypes.KEY_SYSTEM_ERROR, 4074 details: errors_1.ErrorDetails.KEY_SYSTEM_LICENSE_REQUEST_FAILED, 4075 fatal: true 4076 }); 4077 } 4078 break; 4079 } 4080 }; 4081 /** 4082 * @param {object} keysListItem 4083 * @param {ArrayBuffer} keyMessage 4084 * @returns {ArrayBuffer} Challenge data posted to license server 4085 */ 4086 EMEController.prototype._generateLicenseRequestChallenge = function (keysListItem, keyMessage) { 4087 var challenge; 4088 if (keysListItem.mediaKeySystemDomain === KeySystems.PLAYREADY) { 4089 logger_1.logger.error('PlayReady is not supported (yet)'); 4090 // from https://github.com/MicrosoftEdge/Demos/blob/master/eme/scripts/demo.js 4091 /* 4092 if (this.licenseType !== this.LICENSE_TYPE_WIDEVINE) { 4093 // For PlayReady CDMs, we need to dig the Challenge out of the XML. 4094 var keyMessageXml = new DOMParser().parseFromString(String.fromCharCode.apply(null, new Uint16Array(keyMessage)), 'application/xml'); 4095 if (keyMessageXml.getElementsByTagName('Challenge')[0]) { 4096 challenge = atob(keyMessageXml.getElementsByTagName('Challenge')[0].childNodes[0].nodeValue); 4097 } else {
4098 throw 'Cannot find <Challenge> in key message'; 4099 } 4100 var headerNames = keyMessageXml.getElementsByTagName('name'); 4101 var headerValues = keyMessageXml.getElementsByTagName('value'); 4102 if (headerNames.length !== headerValues.length) { 4103 throw 'Mismatched header <name>/<value> pair in key message'; 4104 } 4105 for (var i = 0; i < headerNames.length; i++) { 4106 xhr.setRequestHeader(headerNames[i].childNodes[0].nodeValue, headerValues[i].childNodes[0].nodeValue); 4107 } 4108 } 4109 */ 4110 } 4111 else if (keysListItem.mediaKeySystemDomain === KeySystems.WIDEVINE) { 4112 // For Widevine CDMs, the challenge is the keyMessage. 4113 challenge = keyMessage; 4114 } 4115 else { 4116 logger_1.logger.error('Unsupported key-system:', keysListItem.mediaKeySystemDomain); 4117 } 4118 return challenge; 4119 }; 4120 EMEController.prototype._requestLicense = function (keyMessage, callback) { 4121 logger_1.logger.log('Requesting content license for key-system'); 4122 var keysListItem = this._mediaKeysList[0]; 4123 if (!keysListItem) { 4124 logger_1.logger.error('Fatal error: Media is encrypted but no key-system access has been obtained yet'); 4125 this.hls.trigger(events_1.default.ERROR, { 4126 type: errors_1.ErrorTypes.KEY_SYSTEM_ERROR, 4127 details: errors_1.ErrorDetails.KEY_SYSTEM_NO_ACCESS, 4128 fatal: true 4129 }); 4130 return; 4131 } 4132 var url = this.getLicenseServerUrl(keysListItem.mediaKeySystemDomain); 4133 var xhr = this._createLicenseXhr(url, keyMessage, callback); 4134 logger_1.logger.log("Sending license request to URL: " + url); 4135 xhr.send(this._generateLicenseRequestChallenge(keysListItem, keyMessage)); 4136 }; 4137 EMEController.prototype.onMediaAttached = function (data) { 4138 var _this = this; 4139 if (!this._emeEnabled) { 4140 return; 4141 } 4142 var media = data.media; 4143 // keep reference of media 4144 this._media = media; 4145 // FIXME: also handle detaching media ! 4146 media.addEventListener('encrypted', function (e) { 4147 _this._onMediaEncrypted(e.initDataType, e.initData); 4148 }); 4149 }; 4150 EMEController.prototype.onManifestParsed = function (data) { 4151 if (!this._emeEnabled) { 4152 return; 4153 } 4154 var audioCodecs = data.levels.map(function (level) { return level.audioCodec; }); 4155 var videoCodecs = data.levels.map(function (level) { return level.videoCodec; }); 4156 this._attemptKeySystemAccess(KeySystems.WIDEVINE, audioCodecs, videoCodecs); 4157 }; 4158 return EMEController; 4159}(event_handler_1.default)); 4160exports.default = EMEController; 4161 4162 4163/***/ }), 4164
4165/***/ "./src/controller/fps-controller.js": 4166/*!******************************************!*\ 4167 !*** ./src/controller/fps-controller.js ***! 4168 \******************************************/ 4169/*! no static exports found */ 4170/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 4171/***/ (function(module, exports, __webpack_require__) { 4172 4173"use strict"; 4174 4175/* 4176 * FPS Controller 4177*/ 4178var __extends = (this && this.__extends) || (function () { 4179 var extendStatics = Object.setPrototypeOf || 4180 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 4181 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 4182 return function (d, b) { 4183 extendStatics(d, b); 4184 function __() { this.constructor = d; } 4185 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 4186 }; 4187})(); 4188Object.defineProperty(exports, "__esModule", { value: true }); 4189var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 4190var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 4191var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 4192var performance = window.performance; 4193var FPSController = /** @class */ (function (_super) { 4194 __extends(FPSController, _super); 4195 function FPSController(hls) { 4196 return _super.call(this, hls, events_1.default.MEDIA_ATTA
vendor: 3,171 bytes, lines 4196-4258
4196CHING) || this; 4197 } 4198 FPSController.prototype.destroy = function () { 4199 if (this.timer) { 4200 clearInterval(this.timer); 4201 } 4202 this.isVideoPlaybackQualityAvailable = false; 4203 }; 4204 FPSController.prototype.onMediaAttaching = function (data) { 4205 var config = this.hls.config; 4206 if (config.capLevelOnFPSDrop) { 4207 var video_1 = this.video = data.media instanceof window.HTMLVideoElement ? data.media : null; 4208 if (typeof video_1.getVideoPlaybackQuality === 'function') { 4209 this.isVideoPlaybackQualityAvailable = true; 4210 } 4211 clearInterval(this.timer); 4212 this.timer = setInterval(this.checkFPSInterval.bind(this), config.fpsDroppedMonitoringPeriod); 4213 } 4214 }; 4215 FPSController.prototype.checkFPS = function (video, decodedFrames, droppedFrames) { 4216 var currentTime = performance.now(); 4217 if (decodedFrames) { 4218 if (this.lastTime) { 4219 var currentPeriod = currentTime - this.lastTime, currentDropped = droppedFrames - this.lastDroppedFrames, currentDecoded = decodedFrames - this.lastDecodedFrames, droppedFPS = 1000 * currentDropped / currentPeriod, hls_1 = this.hls; 4220 hls_1.trigger(events_1.default.FPS_DROP, { currentDropped: currentDropped, currentDecoded: currentDecoded, totalDroppedFrames: droppedFrames }); 4221 if (droppedFPS > 0) { 4222 // logger.log('checkFPS : droppedFPS/decodedFPS:' + droppedFPS/(1000 * currentDecoded / currentPeriod)); 4223 if (currentDropped > hls_1.config.fpsDroppedMonitoringThreshold * currentDecoded) { 4224 var currentLevel = hls_1.currentLevel; 4225 logger_1.logger.warn('drop FPS ratio greater than max allowed value for currentLevel: ' + currentLevel); 4226 if (currentLevel > 0 && (hls_1.autoLevelCapping === -1 || hls_1.autoLevelCapping >= currentLevel)) { 4227 currentLevel = currentLevel - 1; 4228 hls_1.trigger(events_1.default.FPS_DROP_LEVEL_CAPPING, { level: currentLevel, droppedLevel: hls_1.currentLevel }); 4229 hls_1.autoLevelCapping = currentLevel; 4230 hls_1.streamController.nextLevelSwitch(); 4231 } 4232 } 4233 } 4234 } 4235 this.lastTime = currentTime; 4236 this.lastDroppedFrames = droppedFrames; 4237 this.lastDecodedFrames = decodedFrames; 4238 } 4239 }; 4240 FPSController.prototype.checkFPSInterval = function () { 4241 var video = this.video; 4242 if (video) { 4243 if (this.isVideoPlaybackQualityAvailable) { 4244 var videoPlaybackQuality = video.getVideoPlaybackQuality(); 4245 this.checkFPS(video, videoPlaybackQuality.totalVideoFrames, videoPlaybackQuality.droppedVideoFrames); 4246 } 4247 else { 4248 this.checkFPS(video, video.webkitDecodedFrameCount, video.webkitDroppedFrameCount); 4249 } 4250 } 4251 }; 4252 return FPSController; 4253}(event_handler_1.default)); 4254exports.default = FPSController; 4255 4256 4257/***/ }), 4258
4259/***/ "./src/controller/fragment-finders.js": 4260/*!********************************************!*\ 4261 !*** ./src/controller/fragment-finders.js ***! 4262 \********************************************/ 4263/*! no static exports found */ 4264/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 4265/***/ (function(module, exports, __webpack_require__) { 4266 4267"use strict"; 4268/* WEBPACK VAR INJECTION */(function(Number) { 4269Object.defineProperty(exports, "__esModule", { value: true }); 4270var binary_search_1 = __webpack_require__(/*! ../utils/binary-search */ "./src/utils/binary-search.js"); 4271/** 4272 * Returns first fragment whose endPdt value exceeds the given PDT. 4273 * @param {Array<Fragment>} fragments - The array of candidate fragments 4274 * @param {number|null} [PDTValue = null] - The PDT value which must be exceeded 4275 * @param {number} [maxFragLookUpTolerance = 0] - The amount of time that a fragment's start/end can be within in order to be considered contiguous 4276 * @returns {*|null} fragment - The best matching fragment 4277 */ 4278function findFragmentByPDT(fragments, PDTValue, maxFragLookUpTolerance) { 4279 if (!Array.isArray(fragments) || !fragments.length || !Number.isFinite(PDTValue)) { 4280 return null; 4281 } 4282 // if less than start 4283 if (PDTValue < fragments[0].programDateTime) { 4284 return null; 4285 } 4286 if (PDTValue >= fragments[fragments.length - 1].endProgramDateTime) { 4287 return null; 4288 } 4289 maxFragLookUpTolerance = maxFragLookUpTolerance || 0; 4290 for (var seg = 0; seg < fragments.length; ++seg) { 4291 var frag = fragments[seg]; 4292 if (pdtWithinToleranceTest(PDTValue, maxFragLookUpTolerance, frag)) { 4293 return frag; 4294 } 4295 } 4296 return null; 4297} 4298exports.findFragmentByPDT = findFragmentByPDT; 4299/** 4300 * Finds a fragment based on the SN of the previous fragment; or based on the needs of the current buffer. 4301 * This method compensates for small buffer gaps by applying a tolerance to the start of any candidate fragment, thus 4302 * breaking any traps which would cause the same fragment to be continuously selected within a small range. 4303 * @param {*} fragPrevious - The last frag successfully appended 4304 * @param {Array<Fragment>} fragments - The array of candidate fragments 4305 * @param {number} [bufferEnd = 0] - The end of the contiguous buffered range the playhead is currently within 4306 * @param {number} maxFragLookUpTolerance - The amount of time that a fragment's start/end can be within in order to be considered contiguous 4307 * @returns {*} foundFrag - The best matching fragment 4308 */ 4309function findFragmentByPTS(fragPrevious, fragments, bufferEnd, maxFragLookUpTolerance) { 4310 if (bufferEnd === void 0) { bufferEnd = 0; } 4311 if (maxFragLookUpTolerance === void 0) { maxFragLookUpTolerance = 0; } 4312 var fragNext = fragPrevious ? fragments[fragPrevious.sn - fragments[0].sn + 1] : null; 4313 // Prefer the next fragment if it's within tolerance 4314 if (fragNext && !fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, fragNext)) { 4315 return fragNext; 4316 } 4317 return binary_search_1.default.search(fragments, fragmentWithinToleranceTest.bind(null, bufferEnd, maxFragLookUpTolerance)); 4318} 4319exports.findFragmentByPTS = findFragmentByPTS; 4320/** 4321 * The test function used by the findFragmentBySn's BinarySearch to look for the best match to the current buffer conditions. 4322 * @param {*} candidate - The fragment to test 4323 * @param {number} [bufferEnd = 0] - The end of the current buffered range the playhead is currently within 4324 * @param {number} [maxFragLookUpTolerance = 0] - The amount of time that a fragment's start can be within in order to be considered contiguous 4325 * @returns {number} - 0 if it matches, 1 if too low, -1 if too high 4326 */ 4327function fragmentWithinToleranceTest(bufferEnd, maxFragLookUpTolerance, candidate) { 4328 if (bufferEnd === void 0) { bufferEnd = 0; } 4329 if (maxFragLookUpTolerance === void 0) { maxFragLookUpTolerance = 0; } 4330 // offset should be within fragment boundary - config.maxFragLookUpTolerance 4331 // this is to cope with situations like 4332 // bufferEnd = 9.991 4333 // frag[Ã] : [0,10] 4334 // frag[1] : [10,20] 4335 // bufferEnd is within frag[0] range ... although what we are expecting is to return frag[1] here 4336 // frag start frag start+duration 4337 // |-----------------------------| 4338 // <---> <--->
4339 // ...--------><-----------------------------><---------.... 4340 // previous frag matching fragment next frag 4341 // return -1 return 0 return 1 4342 // logger.log(`level/sn/start/end/bufEnd:${level}/${candidate.sn}/${candidate.start}/${(candidate.start+candidate.duration)}/${bufferEnd}`); 4343 // Set the lookup tolerance to be small enough to detect the current segment - ensures we don't skip over very small segments 4344 var candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)); 4345 if (candidate.start + candidate.duration - candidateLookupTolerance <= bufferEnd) { 4346 return 1; 4347 } 4348 else if (candidate.start - candidateLookupTolerance > bufferEnd && candidate.start) { 4349 // if maxFragLookUpTolerance will have negative value then don't return -1 for first element 4350 return -1; 4351 } 4352 return 0; 4353} 4354exports.fragmentWithinToleranceTest = fragmentWithinToleranceTest; 4355/** 4356 * The test function used by the findFragmentByPdt's BinarySearch to look for the best match to the current buffer conditions. 4357 * This function tests the candidate's program date time values, as represented in Unix time 4358 * @param {*} candidate - The fragment to test 4359 * @param {number} [pdtBufferEnd = 0] - The Unix time representing the end of the current buffered range 4360 * @param {number} [maxFragLookUpTolerance = 0] - The amount of time that a fragment's start can be within in order to be considered contiguous 4361 * @returns {boolean} True if contiguous, false otherwise 4362 */ 4363function pdtWithinToleranceTest(pdtBufferEnd, maxFragLookUpTolerance, candidate) { 4364 var candidateLookupTolerance = Math.min(maxFragLookUpTolerance, candidate.duration + (candidate.deltaPTS ? candidate.deltaPTS : 0)) * 1000; 4365 return candidate.endProgramDateTime - candidateLookupTolerance > pdtBufferEnd; 4366} 4367exports.pdtWithinToleranceTest = pdtWithinToleranceTest; 4368 4369/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 4370 4371/***/ }), 4372
vendor: 10,373 bytes, lines 4373-4599
4373/***/ "./src/controller/fragment-tracker.js": 4374/*!********************************************!*\ 4375 !*** ./src/controller/fragment-tracker.js ***! 4376 \********************************************/ 4377/*! no static exports found */ 4378/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 4379/***/ (function(module, exports, __webpack_require__) { 4380 4381"use strict"; 4382/* WEBPACK VAR INJECTION */(function(Number) { 4383var __extends = (this && this.__extends) || (function () { 4384 var extendStatics = Object.setPrototypeOf || 4385 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 4386 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 4387 return function (d, b) { 4388 extendStatics(d, b); 4389 function __() { this.constructor = d; } 4390 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 4391 }; 4392})(); 4393Object.defineProperty(exports, "__esModule", { value: true }); 4394var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 4395var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 4396exports.FragmentState = { 4397 NOT_LOADED: 'NOT_LOADED', 4398 APPENDING: 'APPENDING', 4399 PARTIAL: 'PARTIAL', 4400 OK: 'OK' 4401}; 4402var FragmentTracker = /** @class */ (function (_super) { 4403 __extends(FragmentTracker, _super); 4404 function FragmentTracker(hls) { 4405 var _this = _super.call(this, hls, events_1.default.BUFFER_APPENDED, events_1.default.FRAG_BUFFERED, events_1.default.FRAG_LOADED) || this; 4406 _this.bufferPadding = 0.2; 4407 _this.fragments = Object.create(null); 4408 _this.timeRanges = Object.create(null); 4409 _this.config = hls.config; 4410 return _this; 4411 } 4412 FragmentTracker.prototype.destroy = function () { 4413 this.fragments = Object.create(null); 4414 this.timeRanges = Object.create(null); 4415 this.config = null; 4416 event_handler_1.default.prototype.destroy.call(this); 4417 _super.prototype.destroy.call(this); 4418 }; 4419 /** 4420 * Return a Fragment that match the position and levelType. 4421 * If not found any Fragment, return null 4422 * @param {number} position 4423 * @param {LevelType} levelType 4424 * @returns {Fragment|null} 4425 */ 4426 FragmentTracker.prototype.getBufferedFrag = function (position, levelType) { 4427 var fragments = this.fragments; 4428 var bufferedFrags = Object.keys(fragments).filter(function (key) { 4429 var fragmentEntity = fragments[key]; 4430 if (fragmentEntity.body.type !== levelType) { 4431 return false; 4432 } 4433 if (!fragmentEntity.buffered) { 4434 return false; 4435 } 4436 var frag = fragmentEntity.body; 4437 return frag.startPTS <= position && position <= frag.endPTS; 4438 }); 4439 if (bufferedFrags.length === 0) { 4440 return null; 4441 } 4442 else { 4443 // https://github.com/video-dev/hls.js/pull/1545#discussion_r166229566 4444 var bufferedFragKey = bufferedFrags.pop(); 4445 return fragments[bufferedFragKey].body; 4446 } 4447 }; 4448 /** 4449 * Partial fragments effected by coded frame eviction will be removed 4450 * The browser will unload parts of the buffer to free up memory for new buffer data 4451 * 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) 4452 * @param {String} elementaryStream The elementaryStream of media this is (eg. video/audio) 4453 * @param {TimeRanges} timeRange TimeRange object from a sourceBuffer 4454 */ 4455 FragmentTracker.prototype.detectEvictedFragments = function (elementaryStream, timeRange) { 4456 var _this = this; 4457 var fragmentTimes, time; 4458 // Check if any flagged fragments have been unloaded 4459 Object.keys(this.fragments).forEach(function (key) { 4460 var fragmentEntity = _this.fragments[key]; 4461 if (fragmentEntity.buffered === true) { 4462 var esData = fragmentEntity.range[elementaryStream]; 4463 if (esData) { 4464 fragmentTimes = esData.time; 4465 for (var i = 0; i < fragmentTimes.length; i++) { 4466 time = fragmentTimes[i]; 4467 if (_this.isTimeBuffered(time.startPTS, time.endPTS, timeRange) === false) { 4468 // Unregister partial fragment as it needs to load again to be reused 4469 _this.removeFragment(fragmentEntity.body); 4470 break; 4471 } 4472 } 4473 } 4474 } 4475 }); 4476 }; 4477 /** 4478 * Checks if the fragment passed in is loaded in the buffer properly 4479 * Partially loaded fragments will be registered as a partial fragment 4480 * @param {Object} fragment Check the fragment against all sourceBuffers loaded 4481 */ 4482 FragmentTracker.prototype.detectPartialFragments = function (fragment) { 4483 var _this = this; 4484 var fragKey = this.getFragmentKey(fragment); 4485 var fragmentEntity = this.fragments[fragKey]; 4486 if (fragmentEntity) { 4487 fragmentEntity.buffered = true; 4488 Object.keys(this.timeRanges).forEach(function (elementaryStream) { 4489 if (fragment.hasElementaryStream(elementaryStream)) { 4490 var timeRange = _this.timeRanges[elementaryStream]; 4491 // Check for malformed fragments 4492 // Gaps need to be calculated for each elementaryStream 4493 fragmentEntity.range[elementaryStream] = _this.getBufferedTimes(fragment.startPTS, fragment.endPTS, timeRange); 4494 } 4495 }); 4496 } 4497 }; 4498 FragmentTracker.prototype.getBufferedTimes = function (startPTS, endPTS, timeRange) { 4499 var fragmentTimes = []; 4500 var startTime, endTime; 4501 var fragmentPartial = false; 4502 for (var i = 0; i < timeRange.length; i++) { 4503 startTime = timeRange.start(i) - this.bufferPadding; 4504 endTime = timeRange.end(i) + this.bufferPadding; 4505 if (startPTS >= startTime && endPTS <= endTime) { 4506 // Fragment is entirely contained in buffer 4507 // No need to check the other timeRange times since it's completely playable 4508 fragmentTimes.push({ 4509 startPTS: Math.max(startPTS, timeRange.start(i)), 4510 endPTS: Math.min(endPTS, timeRange.end(i)) 4511 }); 4512 break; 4513 } 4514 else if (startPTS < endTime && endPTS > startTime) { 4515 // Check for intersection with buffer 4516 // Get playable sections of the fragment 4517 fragmentTimes.push({ 4518 startPTS: Math.max(startPTS, timeRange.start(i)), 4519 endPTS: Math.min(endPTS, timeRange.end(i)) 4520 }); 4521 fragmentPartial = true; 4522 } 4523 else if (endPTS <= startTime) { 4524 // No need to check the rest of the timeRange as it is in order 4525 break; 4526 } 4527 } 4528 return { 4529 time: fragmentTimes, 4530 partial: fragmentPartial 4531 }; 4532 }; 4533 FragmentTracker.prototype.getFragmentKey = function (fragment) { 4534 return fragment.type + "_" + fragment.level + "_" + fragment.urlId + "_" + fragment.sn; 4535 }; 4536 /** 4537 * Gets the partial fragment for a certain time 4538 * @param {Number} time 4539 * @returns {Object} fragment Returns a partial fragment at a time or null if there is no partial fragment 4540 */ 4541 FragmentTracker.prototype.getPartialFragment = function (time) { 4542 var _this = this; 4543 var timePadding, startTime, endTime; 4544 var bestFragment = null; 4545 var bestOverlap = 0; 4546 Object.keys(this.fragments).forEach(function (key) { 4547 var fragmentEntity = _this.fragments[key]; 4548 if (_this.isPartial(fragmentEntity)) { 4549 startTime = fragmentEntity.body.startPTS - _this.bufferPadding; 4550 endTime = fragmentEntity.body.endPTS + _this.bufferPadding; 4551 if (time >= startTime && time <= endTime) { 4552 // Use the fragment that has the most padding from start and end time 4553 timePadding = Math.min(time - startTime, endTime - time); 4554 if (bestOverlap <= timePadding) { 4555 bestFragment = fragmentEntity.body; 4556 bestOverlap = timePadding; 4557 } 4558 } 4559 } 4560 }); 4561 return bestFragment; 4562 }; 4563 /** 4564 * @param {Object} fragment The fragment to check 4565 * @returns {String} Returns the fragment state when a fragment never loaded or if it partially loaded 4566 */ 4567 FragmentTracker.prototype.getState = function (fragment) { 4568 var fragKey = this.getFragmentKey(fragment); 4569 var fragmentEntity = this.fragments[fragKey]; 4570 var state = exports.FragmentState.NOT_LOADED; 4571 if (fragmentEntity !== undefined) { 4572 if (!fragmentEntity.buffered) { 4573 state = exports.FragmentState.APPENDING; 4574 } 4575 else if (this.isPartial(fragmentEntity) === true) { 4576 state = exports.FragmentState.PARTIAL; 4577 } 4578 else { 4579 state = exports.FragmentState.OK; 4580 } 4581 } 4582 return state; 4583 }; 4584 FragmentTracker.prototype.isPartial = function (fragmentEntity) { 4585 return fragmentEntity.buffered === true && 4586 ((fragmentEntity.range.video !== undefined && fragmentEntity.range.video.partial === true) || 4587 (fragmentEntity.range.audio !== undefined && fragmentEntity.range.audio.partial === true)); 4588 }; 4589 FragmentTracker.prototype.isTimeBuffered = function (startPTS, endPTS, timeRange) { 4590 var startTime, endTime; 4591 for (var i = 0; i < timeRange.length; i++) { 4592 startTime = timeRange.start(i) - this.bufferPadding; 4593 endTime = timeRange.end(i) + this.bufferPadding; 4594 if (startPTS >= startTime && endPTS <= endTime) { 4595 return true; 4596 } 4597 if (endPTS <= startTime) { 4598 // No need to check the rest of the timeRange as it is in order 4599 return false;
4600 } 4601 } 4602 return false; 4603 }; 4604 /** 4605 * Fires when a fragment loading is completed 4606 */ 4607 FragmentTracker.prototype.onFragLoaded = function (e) { 4608 var fragment = e.frag; 4609 // don't track initsegment (for which sn is not a number) 4610 // don't track frags used for bitrateTest, they're irrelevant. 4611 if (!Number.isFinite(fragment.sn) || fragment.bitrateTest) { 4612 return; 4613 } 4614 this.fragments[this.getFragmentKey(fragment)] = { 4615 body: fragment, 4616 range: Object.create(null), 4617 buffered: false 4618 }; 4619 }; 4620 /** 4621 * Fires when the buffer is updated 4622 */ 4623 FragmentTracker.prototype.onBufferAppended = function (e) { 4624 var _this = this; 4625 // Store the latest timeRanges loaded in the buffer 4626 this.timeRanges = e.timeRanges; 4627 Object.keys(this.timeRanges).forEach(function (elementaryStream) { 4628 var timeRange = _this.timeRanges[elementaryStream]; 4629 _this.detectEvictedFragments(elementaryStream, timeRange); 4630 }); 4631 }; 4632 /** 4633 * Fires after a fragment has been loaded into the source buffer 4634 */ 4635 FragmentTracker.prototype.onFragBuffered = function (e) { 4636 this.detectPartialFragments(e.frag); 4637 }; 4638 /** 4639 * Return true if fragment tracker has the fragment. 4640 * @param {Object} fragment 4641 * @returns {boolean} 4642 */ 4643 FragmentTracker.prototype.hasFragment = function (fragment) { 4644 var fragKey = this.getFragmentKey(fragment); 4645 return this.fragments[fragKey] !== undefined; 4646 }; 4647 /** 4648 * Remove a fragment from fragment tracker until it is loaded again 4649 * @param {Object} fragment The fragment to remove 4650 */ 4651 FragmentTracker.prototype.removeFragment = function (fragment) { 4652 var fragKey = this.getFragmentKey(fragment); 4653 delete this.fragments[fragKey]; 4654 }; 4655 /** 4656 * Remove all fragments from fragment tracker. 4657 */ 4658 FragmentTracker.prototype.removeAllFragments = function () { 4659 this.fragments = Object.create(null); 4660 }; 4661 return FragmentTracker; 4662}(event_handler_1.default)); 4663exports.FragmentTracker = FragmentTracker; 4664 4665/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 4666 4667/***/ }), 4668
4669/***/ "./src/controller/gap-controller.js": 4670/*!******************************************!*\ 4671 !*** ./src/controller/gap-controller.js ***! 4672 \******************************************/ 4673/*! no static exports found */ 4674/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 4675/***/ (function(module, exports, __webpack_require__) { 4676 4677"use strict"; 4678 4679Object.defineProperty(exports, "__esModule", { value: true }); 4680var buffer_helper_1 = __webpack_require__(/*! ../utils/buffer-helper */ "./src/utils/buffer-helper.js"); 4681var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 4682var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 4683var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 4684var stallDebounceInterval = 1000; 4685var jumpThreshold = 0.5; // tolerance needed as some browsers stalls playback before reaching buffered range end 4686var GapController = /** @class */ (function () { 4687 function GapController(config, media, fragmentTracker, hls) { 4688 this.config = config; 4689 this.media = media; 4690 this.fragmentTracker = fragmentTracker; 4691 this.hls = hls; 4692 this.stallReported = false; 4693 } 4694 /** 4695 * Checks if the playhead is stuck within a gap, and if so, attempts to free it. 4696 * A gap is an unbuffered range between two buffered ranges (or the start and the first buffered range). 4697 * @param lastCurrentTime 4698 * @param buffered 4699 */ 4700 GapController.prototype.poll = function (lastCurrentTime, buffered) { 4701 var _a = this, config = _a.config, media = _a.media; 4702 var currentTime = media.currentTime; 4703 var tnow = window.performance.now(); 4704 if (currentTime !== lastCurrentTime) { 4705 // The playhead is now moving, but was previously stalled 4706 if (this.stallReported) { 4707 logger_1.logger.warn("playback not stuck anymore @" + currentTime + ", after " + Math.round(tnow - this.stalled) + "ms"); 4708 this.stallReported = false; 4709 } 4710 this.stalled = null; 4711 this.nudgeRetry = 0; 4712 return; 4713 } 4714 if (media.ended || !media.buffered.length || media.readyState > 2) { 4715 return; 4716 } 4717 if (media.seeking && buffer_helper_1.BufferHelper.isBuffered(media, currentTime)) { 4718 return; 4719 } 4720 // The playhead isn't moving but it should be 4721 // Allow some slack time to for small stalls to resolve themselves 4722 var stalledDuration = tnow - this.stalled; 4723 var bufferInfo = buffer_helper_1.BufferHelper.bufferInfo(media, currentTime, config.maxBufferHole); 4724 if (!this.stalled) { 4725 this.stalled = tnow; 4726 return; 4727 } 4728 else if (stalledDuration >= stallDebounceInterval) { 4729 // Report stalling after trying to fix 4730 this._reportStall(bufferInfo.len); 4731 } 4732 this._tryFixBufferStall(bufferInfo, stalledDuration); 4733 }; 4734 /** 4735 * Detects and attempts to fix known buffer stalling issues. 4736 * @param bufferInfo - The properties of the current buffer. 4737 * @param stalledDuration - The amount of time Hls.js has been stalling for. 4738 * @private 4739 */ 4740 GapController.prototype._tryFixBufferStall = function (bufferInfo, stalledDuration) { 4741 var _a = this, config = _a.config, fragmentTracker = _a.fragmentTracker, media = _a.media; 4742 var currentTime = media.currentTime; 4743 var partial = fragmentTracker.getPartialFragment(currentTime); 4744 if (partial) { 4745 // Try to skip over the buffer hole caused by a partial fragment 4746 // This method isn't limited by the size of the gap between buffered ranges 4747 this._trySkipBufferHole(partial); 4748 } 4749 if (bufferInfo.len > jumpThreshold && stalledDuration > config.highBufferWatchdogPeriod * 1000) { 4750 // Try to nudge currentTime over a buffer hole if we've been stalling for the configured amount of seconds 4751 // We only try to jump the hole if it's under the configured size 4752 // Reset stalled so to rearm watchdog timer 4753 this.stalled = null; 4754 this._tryNudgeBuffer(); 4755 } 4756 }; 4757 /** 4758 * Triggers a BUFFER_STALLED_ERROR event, but only once per stall period. 4759 * @param bufferLen - The playhead distance from the end of the current buffer segment. 4760 * @private 4761 */ 4762 GapController.prototype._reportStall = function (bufferLen) { 4763 var _a = this, hls = _a.hls, media = _a.media, stallReported = _a.stallReported; 4764 if (!stallReported) { 4765 // Report stalled error once 4766 this.stallReported = true; 4767 logger_1.logger.warn("Playback stalling at @" + media.currentTime + " due to low buffer"); 4768 hls.trigger(events_1.default.ERROR, { 4769 type: errors_1.ErrorTypes.MEDIA_ERROR, 4770 details: errors_1.ErrorDetails.BUFFER_STALLED_ERROR, 4771 fatal: false, 4772 buffer: bufferLen 4773 }); 4774 } 4775 }; 4776 /** 4777 * Attempts to fix buffer stalls by jumping over known gaps caused by partial fragments 4778 * @param partial - The partial fragment found at the current time (where playback is stalling). 4779 * @private 4780 */ 4781 GapController.prototype._trySkipBufferHole = function (partial) { 4782 var _a = this, hls = _a.hls, media = _a.media; 4783 var currentTime = media.currentTime; 4784 var lastEndTime = 0; 4785 // Check if currentTime is between unbuffered regions of partial fragments 4786 for (var i = 0; i < media.buffered.length; i++) { 4787 var startTime = media.buffered.start(i); 4788 if (currentTime >= lastEndTime && currentTime < startTime) { 4789 media.currentTime = Math.max(startTime, media.currentTime + 0.1); 4790 logger_1.logger.warn("skipping hole, adjusting currentTime from " + currentTime + " to " + media.currentTime); 4791 this.stalled = null; 4792 hls.trigger(events_1.default.ERROR, { 4793 type: errors_1.ErrorTypes.MEDIA_ERROR, 4794 details: errors_1.ErrorDetails.BUFFER_SEEK_OVER_HOLE, 4795 fatal: false, 4796 reason: "fragment loaded with buffer holes, seeking from " + currentTime + " to " + media.currentTime, 4797 frag: partial 4798 }); 4799 return; 4800 } 4801 lastEndTime = media.buffered.end(i); 4802 } 4803 }; 4804 /** 4805 * Attempts to fix buffer stalls by advancing the mediaElement's current time by a small amount. 4806 * @private 4807 */ 4808 GapController.prototype._tryNudgeBuffer = function () {
4809 var _a = this, config = _a.config, hls = _a.hls, media = _a.media; 4810 var currentTime = media.currentTime; 4811 var nudgeRetry = (this.nudgeRetry || 0) + 1; 4812 this.nudgeRetry = nudgeRetry; 4813 if (nudgeRetry < config.nudgeMaxRetry) { 4814 var targetTime = currentTime + nudgeRetry * config.nudgeOffset; 4815 logger_1.logger.log("adjust currentTime from " + currentTime + " to " + targetTime); 4816 // playback stalled in buffered area ... let's nudge currentTime to try to overcome this 4817 media.currentTime = targetTime; 4818 hls.trigger(events_1.default.ERROR, { 4819 type: errors_1.ErrorTypes.MEDIA_ERROR, 4820 details: errors_1.ErrorDetails.BUFFER_NUDGE_ON_STALL, 4821 fatal: false 4822 }); 4823 } 4824 else { 4825 logger_1.logger.error("still stuck in high buffer @" + currentTime + " after " + config.nudgeMaxRetry + ", raise fatal error"); 4826 hls.trigger(events_1.default.ERROR, { 4827 type: errors_1.ErrorTypes.MEDIA_ERROR, 4828 details: errors_1.ErrorDetails.BUFFER_STALLED_ERROR, 4829 fatal: true 4830 }); 4831 } 4832 }; 4833 return GapController; 4834}()); 4835exports.default = GapController; 4836 4837 4838/***/ }), 4839
4840/***/ "./src/controller/id3-track-controller.js": 4841/*!************************************************!*\ 4842 !*** ./src/controller/id3-track-controller.js ***! 4843 \************************************************/ 4844/*! no static exports found */ 4845/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 4846/***/ (function(module, exports, __webpack_require__) { 4847 4848"use strict"; 4849 4850/* 4851 * id3 metadata track controller 4852*/ 4853var __extends = (this && this.__extends) || (function () { 4854 var extendStatics = Object.setPrototypeOf || 4855 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 4856 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 4857 return function (d, b) { 4858 extendStatics(d, b); 4859 function __() { this.constructor = d; } 4860 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 4861 }; 4862})(); 4863Object.defineProperty(exports, "__esModule", { value: true }); 4864var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 4865var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 4866var id3_1 = __webpack_require__(/*! ../demux/id3 */ "./src/demux/id3.js"); 4867var texttrack_utils_1 = __webpack_require__(/*! ../utils/texttrack-utils */ "./src/utils/texttrack-utils.js"); 4868var ID3TrackController = /** @class */ (function (_super) { 4869 __extends(ID3TrackController, _super); 4870 function ID3TrackController(hls) { 4871 var _this = _super.call(this, hls, events_1.default.MEDIA_ATTA
4871CHED, events_1.default.MEDIA_DETACHING, events_1.default.FRAG_PARSING_METADATA) || this; 4872 _this.id3Track = undefined; 4873 _this.media = undefined; 4874 return _this; 4875 } 4876 ID3TrackController.prototype.destroy = function () { 4877 event_handler_1.default.prototype.destroy.call(this); 4878 }; 4879 // Add ID3 metatadata text track. 4880 ID3TrackController.prototype.onMediaAttached = function (data) { 4881 this.media = data.media; 4882 if (!this.media) { 4883 } 4884 }; 4885 ID3TrackController.prototype.onMediaDetaching = function () { 4886 texttrack_utils_1.clearCurrentCues(this.id3Track); 4887 this.id3Track = undefined; 4888 this.media = undefined; 4889 }; 4890 ID3TrackController.prototype.getID3Track = function (textTracks) { 4891 for (var i = 0; i < textTracks.length; i++) { 4892 var textTrack = textTracks[i]; 4893 if (textTrack.kind === 'metadata' && textTrack.label === 'id3') { 4894 // send 'addtrack' when reusing the textTrack for metadata, 4895 // same as what we do for captions 4896 texttrack_utils_1.sendAddTrackEvent(textTrack, this.media); 4897 return textTrack; 4898 } 4899 } 4900 return this.media.addTextTrack('metadata', 'id3'); 4901 }; 4902 ID3TrackController.prototype.onFragParsingMetadata = function (data) { 4903 var fragment = data.frag; 4904 var samples = data.samples; 4905 // create track dynamically 4906 if (!this.id3Track) { 4907 this.id3Track = this.getID3Track(this.media.textTracks); 4908 this.id3Track.mode = 'hidden'; 4909 } 4910 // Attempt to recreate Safari functionality by creating 4911 // WebKitDataCue objects when available and store the decoded 4912 // ID3 data in the value property of the cue 4913 var Cue = window.WebKitDataCue || window.VTTCue || window.TextTrackCue; 4914 for (var i = 0; i < samples.length; i++) { 4915 var frames_1 = id3_1.default.getID3Frames(samples[i].data); 4916 if (frames_1) { 4917 var startTime = samples[i].pts; 4918 var endTime = i < samples.length - 1 ? samples[i + 1].pts : fragment.endPTS; 4919 // Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE 4920 if (startTime === endTime) { 4921 endTime += 0.0001; 4922 } 4923 for (var j = 0; j < frames_1.length; j++) { 4924 var frame = frames_1[j]; 4925 // Safari doesn't put the timestamp frame in the TextTrack 4926 if (!id3_1.default.isTimeStampFrame(frame)) { 4927 var cue = new Cue(startTime, endTime, ''); 4928 cue.value = frame; 4929 this.id3Track.addCue(cue); 4930 } 4931 } 4932 } 4933 } 4934 }; 4935 return ID3TrackController; 4936}(event_handler_1.default)); 4937exports.default = ID3TrackController; 4938 4939 4940/***/ }), 4941
vendor: 7,571 bytes, lines 4942-5110
4942/***/ "./src/controller/level-controller.js": 4943/*!********************************************!*\ 4944 !*** ./src/controller/level-controller.js ***! 4945 \********************************************/ 4946/*! no static exports found */ 4947/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 4948/***/ (function(module, exports, __webpack_require__) { 4949 4950"use strict"; 4951 4952/* 4953 * Level Controller 4954*/ 4955var __extends = (this && this.__extends) || (function () { 4956 var extendStatics = Object.setPrototypeOf || 4957 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 4958 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 4959 return function (d, b) { 4960 extendStatics(d, b); 4961 function __() { this.constructor = d; } 4962 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 4963 }; 4964})(); 4965Object.defineProperty(exports, "__esModule", { value: true }); 4966var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 4967var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 4968var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 4969var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 4970var codecs_1 = __webpack_require__(/*! ../utils/codecs */ "./src/utils/codecs.js"); 4971var level_helper_1 = __webpack_require__(/*! ./level-helper */ "./src/controller/level-helper.js"); 4972var performance = window.performance; 4973var chromeOrFirefox; 4974var LevelController = /** @class */ (function (_super) { 4975 __extends(LevelController, _super); 4976 function LevelController(hls) { 4977 var _this = _super.call(this, hls, events_1.default.MANIFEST_LOADED, events_1.default.LEVEL_LOADED, events_1.default.AUDIO_TRACK_SWITCHED, events_1.default.FRAG_LOADED, events_1.default.ERROR) || this; 4978 _this.canload = false; 4979 _this.currentLevelIndex = null; 4980 _this.manualLevelIndex = -1; 4981 _this.timer = null; 4982 chromeOrFirefox = /chrome|firefox/.test(navigator.userAgent.toLowerCase()); 4983 return _this; 4984 } 4985 LevelController.prototype.onHandlerDestroying = function () { 4986 this.clearTimer(); 4987 this.manualLevelIndex = -1; 4988 }; 4989 LevelController.prototype.clearTimer = function () { 4990 if (this.timer !== null) { 4991 clearTimeout(this.timer); 4992 this.timer = null; 4993 } 4994 }; 4995 LevelController.prototype.startLoad = function () { 4996 var levels = this._levels; 4997 this.canload = true; 4998 this.levelRetryCount = 0; 4999 // clean up live level details to force reload them, and reset load errors 5000 if (levels) { 5001 levels.forEach(function (level) { 5002 level.loadError = 0; 5003 var levelDetails = level.details; 5004 if (levelDetails && levelDetails.live) { 5005 level.details = undefined; 5006 } 5007 }); 5008 } 5009 // speed up live playlist refresh if timer exists 5010 if (this.timer !== null) { 5011 this.loadLevel(); 5012 } 5013 }; 5014 LevelController.prototype.stopLoad = function () { 5015 this.canload = false; 5016 }; 5017 LevelController.prototype.onManifestLoaded = function (data) { 5018 var levels = []; 5019 var audioTracks = []; 5020 var bitrateStart; 5021 var levelSet = {}; 5022 var levelFromSet = null; 5023 var videoCodecFound = false; 5024 var audioCodecFound = false; 5025 // regroup redundant levels together 5026 data.levels.forEach(function (level) { 5027 var attributes = level.attrs; 5028 level.loadError = 0; 5029 level.fragmentError = false; 5030 videoCodecFound = videoCodecFound || !!level.videoCodec; 5031 audioCodecFound = audioCodecFound || !!level.audioCodec; 5032 // erase audio codec info if browser does not support mp4a.40.34. 5033 // demuxer will autodetect codec and fallback to mpeg/audio 5034 if (chromeOrFirefox && level.audioCodec && level.audioCodec.indexOf('mp4a.40.34') !== -1) { 5035 level.audioCodec = undefined; 5036 } 5037 levelFromSet = levelSet[level.bitrate]; // FIXME: we would also have to match the resolution here 5038 if (!levelFromSet) { 5039 level.url = [level.url]; 5040 level.urlId = 0; 5041 levelSet[level.bitrate] = level; 5042 levels.push(level); 5043 } 5044 else { 5045 levelFromSet.url.push(level.url); 5046 } 5047 if (attributes) { 5048 if (attributes.AUDIO) { 5049 audioCodecFound = true; 5050 level_helper_1.addGroupId(levelFromSet || level, 'audio', attributes.AUDIO); 5051 } 5052 if (attributes.SUBTITLES) { 5053 level_helper_1.addGroupId(levelFromSet || level, 'text', attributes.SUBTITLES); 5054 } 5055 } 5056 }); 5057 // remove audio-only level if we also have levels with audio+video codecs signalled 5058 if (videoCodecFound && audioCodecFound) { 5059 levels = levels.filter(function (_a) { 5060 var videoCodec = _a.videoCodec; 5061 return !!videoCodec; 5062 }); 5063 } 5064 // only keep levels with supported audio/video codecs 5065 levels = levels.filter(function (_a) { 5066 var audioCodec = _a.audioCodec, videoCodec = _a.videoCodec; 5067 return (!audioCodec || codecs_1.isCodecSupportedInMp4(audioCodec, 'audio')) && (!videoCodec || codecs_1.isCodecSupportedInMp4(videoCodec, 'video')); 5068 }); 5069 if (data.audioTracks) { 5070 audioTracks = data.audioTracks.filter(function (track) { return !track.audioCodec || codecs_1.isCodecSupportedInMp4(track.audioCodec, 'audio'); }); 5071 // Reassign id's after filtering since they're used as array indices 5072 audioTracks.forEach(function (track, index) { 5073 track.id = index; 5074 }); 5075 } 5076 if (levels.length > 0) { 5077 // start bitrate is the first bitrate of the manifest 5078 bitrateStart = levels[0].bitrate; 5079 // sort level on bitrate 5080 levels.sort(function (a, b) { return a.bitrate - b.bitrate; }); 5081 this._levels = levels; 5082 // find index of first level in sorted levels 5083 for (var i = 0; i < levels.length; i++) { 5084 if (levels[i].bitrate === bitrateStart) { 5085 this._firstLevel = i; 5086 logger_1.logger.log("manifest loaded," + levels.length + " level(s) found, first bitrate:" + bitrateStart); 5087 break; 5088 } 5089 } 5090 // Audio is only alternate if manifest include a URI along with the audio group tag 5091 this.hls.trigger(events_1.default.MANIFEST_PARSED, { 5092 levels: levels, 5093 audioTracks: audioTracks, 5094 firstLevel: this._firstLevel, 5095 stats: data.stats, 5096 audio: audioCodecFound, 5097 video: videoCodecFound, 5098 altAudio: audioTracks.some(function (t) { return !!t.url; }) 5099 }); 5100 } 5101 else { 5102 this.hls.trigger(events_1.default.ERROR, { 5103 type: errors_1.ErrorTypes.MEDIA_ERROR, 5104 details: errors_1.ErrorDetails.MANIFEST_INCOMPATIBLE_CODECS_ERROR, 5105 fatal: true, 5106 url: this.hls.url, 5107 reason: 'no level with compatible codecs found in manifest' 5108 }); 5109 } 5110 };
5111 Object.defineProperty(LevelController.prototype, "levels", { 5112 get: function () { 5113 return this._levels; 5114 }, 5115 enumerable: true, 5116 configurable: true 5117 }); 5118 Object.defineProperty(LevelController.prototype, "level", { 5119 get: function () { 5120 return this.currentLevelIndex; 5121 }, 5122 set: function (newLevel) { 5123 var levels = this._levels; 5124 if (levels) { 5125 newLevel = Math.min(newLevel, levels.length - 1); 5126 if (this.currentLevelIndex !== newLevel || !levels[newLevel].details) { 5127 this.setLevelInternal(newLevel); 5128 } 5129 } 5130 }, 5131 enumerable: true, 5132 configurable: true 5133 }); 5134 LevelController.prototype.setLevelInternal = function (newLevel) { 5135 var levels = this._levels; 5136 var hls = this.hls; 5137 // check if level idx is valid 5138 if (newLevel >= 0 && newLevel < levels.length) { 5139 // stopping live reloading timer if any 5140 this.clearTimer(); 5141 if (this.currentLevelIndex !== newLevel) { 5142 logger_1.logger.log("switching to level " + newLevel); 5143 this.currentLevelIndex = newLevel; 5144 var levelProperties = levels[newLevel]; 5145 levelProperties.level = newLevel; 5146 hls.trigger(events_1.default.LEVEL_SWITCHING, levelProperties); 5147 } 5148 var level = levels[newLevel]; 5149 var levelDetails = level.details; 5150 // check if we need to load playlist for this level 5151 if (!levelDetails || levelDetails.live) { 5152 // level not retrieved yet, or live playlist we need to (re)load it 5153 var urlId = level.urlId; 5154 hls.trigger(events_1.default.LEVEL_LOADING, { url: level.url[urlId], level: newLevel, id: urlId }); 5155 } 5156 } 5157 else { 5158 // invalid level id given, trigger error 5159 hls.trigger(events_1.default.ERROR, { 5160 type: errors_1.ErrorTypes.OTHER_ERROR, 5161 details: errors_1.ErrorDetails.LEVEL_SWITCH_ERROR, 5162 level: newLevel, 5163 fatal: false, 5164 reason: 'invalid level idx' 5165 }); 5166 } 5167 }; 5168 Object.defineProperty(LevelController.prototype, "manualLevel", { 5169 get: function () { 5170 return this.manualLevelIndex; 5171 }, 5172 set: function (newLevel) { 5173 this.manualLevelIndex = newLevel; 5174 if (this._startLevel === undefined) { 5175 this._startLevel = newLevel; 5176 } 5177 if (newLevel !== -1) { 5178 this.level = newLevel; 5179 } 5180 }, 5181 enumerable: true, 5182 configurable: true 5183 }); 5184 Object.defineProperty(LevelController.prototype, "firstLevel", { 5185 get: function () { 5186 return this._firstLevel; 5187 }, 5188 set: function (newLevel) { 5189 this._firstLevel = newLevel; 5190 }, 5191 enumerable: true, 5192 configurable: true 5193 }); 5194 Object.defineProperty(LevelController.prototype, "startLevel", { 5195 get: function () { 5196 // hls.startLevel takes precedence over config.startLevel 5197 // if none of these values are defined, fallback on this._firstLevel (first quality level appearing in variant manifest) 5198 if (this._startLevel === undefined) { 5199 var configStartLevel = this.hls.config.startLevel; 5200 if (configStartLevel !== undefined) { 5201 return configStartLevel; 5202 } 5203 else { 5204 return this._firstLevel; 5205 } 5206 } 5207 else { 5208 return this._startLevel; 5209 } 5210 }, 5211 set: function (newLevel) { 5212 this._startLevel = newLevel; 5213 }, 5214 enumerable: true, 5215 configurable: true 5216 }); 5217 LevelController.prototype.onError = function (data) { 5218 if (data.fatal) { 5219 if (data.type === errors_1.ErrorTypes.NETWORK_ERROR) { 5220 this.clearTimer(); 5221 } 5222 return; 5223 } 5224 var levelError = false, fragmentError = false;
5225 var levelIndex; 5226 // try to recover not fatal errors 5227 switch (data.details) { 5228 case errors_1.ErrorDetails.FRAG_LOAD_ERROR: 5229 case errors_1.ErrorDetails.FRAG_LOAD_TIMEOUT: 5230 case errors_1.ErrorDetails.KEY_LOAD_ERROR: 5231 case errors_1.ErrorDetails.KEY_LOAD_TIMEOUT: 5232 levelIndex = data.frag.level; 5233 fragmentError = true; 5234 break; 5235 case errors_1.ErrorDetails.LEVEL_LOAD_ERROR: 5236 case errors_1.ErrorDetails.LEVEL_LOAD_TIMEOUT: 5237 levelIndex = data.context.level; 5238 levelError = true; 5239 break; 5240 case errors_1.ErrorDetails.REMUX_ALLOC_ERROR: 5241 levelIndex = data.level; 5242 levelError = true; 5243 break; 5244 } 5245 if (levelIndex !== undefined) { 5246 this.recoverLevel(data, levelIndex, levelError, fragmentError); 5247 } 5248 }; 5249 /** 5250 * Switch to a redundant stream if any available. 5251 * If redundant stream is not available, emergency switch down if ABR mode is enabled. 5252 * 5253 * @param {Object} errorEvent 5254 * @param {Number} levelIndex current level index 5255 * @param {Boolean} levelError 5256 * @param {Boolean} fragmentError 5257 */ 5258 // FIXME Find a better abstraction where fragment/level retry management is well decoupled 5259 LevelController.prototype.recoverLevel = function (errorEvent, levelIndex, levelError, fragmentError) { 5260 var _this = this; 5261 var config = this.hls.config; 5262 var errorDetails = errorEvent.details; 5263 var level = this._levels[levelIndex]; 5264 var redundantLevels, delay, nextLevel; 5265 level.loadError++; 5266 level.fragmentError = fragmentError; 5267 if (levelError) { 5268 if ((this.levelRetryCount + 1) <= config.levelLoadingMaxRetry) { 5269 // exponential backoff capped to max retry timeout 5270 delay = Math.min(Math.pow(2, this.levelRetryCount) * config.levelLoadingRetryDelay, config.levelLoadingMaxRetryTimeout); 5271 // Schedule level reload 5272 this.timer = setTimeout(function () { return _this.loadLevel(); }, delay); 5273 // boolean used to inform stream controller not to switch back to IDLE on non fatal error 5274 errorEvent.levelRetry = true; 5275 this.levelRetryCount++; 5276 logger_1.logger.warn("level controller, " + errorDetails + ", retry in " + delay + " ms, current retry count is " + this.levelRetryCount); 5277 } 5278 else { 5279 logger_1.logger.error("level controller, cannot recover from " + errorDetails + " error"); 5280 this.currentLevelIndex = null; 5281 // stopping live reloading timer if any 5282 this.clearTimer(); 5283 // switch error to fatal 5284 errorEvent.fatal = true; 5285 return; 5286 } 5287 } 5288 // Try any redundant streams if available for both errors: level and fragment 5289 // If level.loadError reaches redundantLevels it means that we tried them all, no hope => let's switch down 5290 if (levelError || fragmentError) { 5291 redundantLevels = level.url.length; 5292 if (redundantLevels > 1 && level.loadError < redundantLevels) { 5293 level.urlId = (level.urlId + 1) % redundantLevels; 5294 level.details = undefined; 5295 logger_1.logger.warn("level controller, " + errorDetails + " for level " + levelIndex + ": switching to redundant URL-id " + level.urlId); 5296 // console.log('Current audio track group ID:', this.hls.audioTracks[this.hls.audioTrack].groupId); 5297 // console.log('New video quality level audio group id:', level.attrs.AUDIO); 5298 } 5299 else { 5300 // Search for available level 5301 if (this.manualLevelIndex === -1) { 5302 // When lowest level has been reached, let's start hunt from the top 5303 nextLevel = (levelIndex === 0) ? this._levels.length - 1 : levelIndex - 1; 5304 logger_1.logger.warn("level controller, " + errorDetails + ": switch to " + nextLevel); 5305 this.hls.nextAutoLevel = this.currentLevelIndex = nextLevel; 5306 } 5307 else if (fragmentError) { 5308 // Allow fragment retry as long as configuration allows. 5309 // reset this._level so that another call to set level() will trigger again a frag load 5310 logger_1.logger.warn("level controller, " + errorDetails + ": reload a fragment"); 5311 this.currentLevelIndex = null; 5312 } 5313 } 5314 } 5315 }; 5316 // reset errors on the successful load of a fragment 5317 LevelController.prototype.onFragLoaded = function (_a) { 5318 var frag = _a.frag; 5319 if (frag !== undefined && frag.type === 'main') { 5320 var level = this._levels[frag.level]; 5321 if (level !== undefined) { 5322 level.fragmentError = false;
5323 level.loadError = 0; 5324 this.levelRetryCount = 0; 5325 } 5326 } 5327 }; 5328 LevelController.prototype.onLevelLoaded = function (data) { 5329 var _this = this; 5330 var level = data.level, details = data.details; 5331 // only process level loaded events matching with expected level 5332 if (level !== this.currentLevelIndex) { 5333 return; 5334 } 5335 var curLevel = this._levels[level]; 5336 // reset level load error counter on successful level loaded only if there is no issues with fragments 5337 if (!curLevel.fragmentError) { 5338 curLevel.loadError = 0; 5339 this.levelRetryCount = 0; 5340 } 5341 // if current playlist is a live playlist, arm a timer to reload it 5342 if (details.live) { 5343 var reloadInterval = level_helper_1.computeReloadInterval(curLevel.details, details, data.stats.trequest); 5344 logger_1.logger.log("live playlist, reload in " + Math.round(reloadInterval) + " ms"); 5345 this.timer = setTimeout(function () { return _this.loadLevel(); }, reloadInterval); 5346 } 5347 else { 5348 this.clearTimer(); 5349 } 5350 }; 5351 LevelController.prototype.onAudioTrackSwitched = function (data) { 5352 var audioGroupId = this.hls.audioTracks[data.id].groupId; 5353 var currentLevel = this.hls.levels[this.currentLevelIndex]; 5354 if (!currentLevel) { 5355 return; 5356 } 5357 if (currentLevel.audioGroupIds) { 5358 var urlId = -1; 5359 for (var i = 0; i < currentLevel.audioGroupIds.length; i++) { 5360 if (currentLevel.audioGroupIds[i] === audioGroupId) { 5361 urlId = i; 5362 break; 5363 } 5364 } 5365 if (urlId !== currentLevel.urlId) { 5366 currentLevel.urlId = urlId; 5367 this.startLoad(); 5368 } 5369 } 5370 }; 5371 LevelController.prototype.loadLevel = function () { 5372 logger_1.logger.debug('call to loadLevel'); 5373 if (this.currentLevelIndex !== null && this.canload) { 5374 var levelObject = this._levels[this.currentLevelIndex]; 5375 if (typeof levelObject === 'object' && 5376 levelObject.url.length > 0) { 5377 var level = this.currentLevelIndex; 5378 var id = levelObject.urlId; 5379 var url = levelObject.url[id]; 5380 logger_1.logger.log("Attempt loading level index " + level + " with URL-id " + id); 5381 // console.log('Current audio track group ID:', this.hls.audioTracks[this.hls.audioTrack].groupId); 5382 // console.log('New video quality level audio group id:', levelObject.attrs.AUDIO, level); 5383 this.hls.trigger(events_1.default.LEVEL_LOADING, { url: url, level: level, id: id }); 5384 } 5385 } 5386 }; 5387 Object.defineProperty(LevelController.prototype, "nextLoadLevel", { 5388 get: function () { 5389 if (this.manualLevelIndex !== -1) { 5390 return this.manualLevelIndex; 5391 } 5392 else { 5393 return this.hls.nextAutoLevel; 5394 } 5395 }, 5396 set: function (nextLevel) { 5397 this.level = nextLevel; 5398 if (this.manualLevelIndex === -1) { 5399 this.hls.nextAutoLevel = nextLevel; 5400 } 5401 }, 5402 enumerable: true, 5403 configurable: true 5404 }); 5405 return LevelController; 5406}(event_handler_1.default)); 5407exports.default = LevelController; 5408 5409 5410/***/ }), 5411
vendor: 9,495 bytes, lines 5412-5645
5412/***/ "./src/controller/level-helper.js": 5413/*!****************************************!*\ 5414 !*** ./src/controller/level-helper.js ***! 5415 \****************************************/ 5416/*! no static exports found */ 5417/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 5418/***/ (function(module, exports, __webpack_require__) { 5419 5420"use strict"; 5421/* WEBPACK VAR INJECTION */(function(Number) { 5422/** 5423 * @module LevelHelper 5424 * 5425 * Providing methods dealing with playlist sliding and drift 5426 * 5427 * TODO: Create an actual `Level` class/model that deals with all this logic in an object-oriented-manner. 5428 * 5429 * */ 5430Object.defineProperty(exports, "__esModule", { value: true }); 5431var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 5432function addGroupId(level, type, id) { 5433 switch (type) { 5434 case 'audio': 5435 if (!level.audioGroupIds) { 5436 level.audioGroupIds = []; 5437 } 5438 level.audioGroupIds.push(id); 5439 break; 5440 case 'text': 5441 if (!level.textGroupIds) { 5442 level.textGroupIds = []; 5443 } 5444 level.textGroupIds.push(id); 5445 break; 5446 } 5447} 5448exports.addGroupId = addGroupId; 5449function updatePTS(fragments, fromIdx, toIdx) { 5450 var fragFrom = fragments[fromIdx], fragTo = fragments[toIdx], fragToPTS = fragTo.startPTS; 5451 // if we know startPTS[toIdx] 5452 if (Number.isFinite(fragToPTS)) { 5453 // update fragment duration. 5454 // it helps to fix drifts between playlist reported duration and fragment real duration 5455 if (toIdx > fromIdx) { 5456 fragFrom.duration = fragToPTS - fragFrom.start; 5457 if (fragFrom.duration < 0) { 5458 logger_1.logger.warn("negative duration computed for frag " + fragFrom.sn + ",level " + fragFrom.level + ", there should be some duration drift between playlist and fragment!"); 5459 } 5460 } 5461 else { 5462 fragTo.duration = fragFrom.start - fragToPTS; 5463 if (fragTo.duration < 0) { 5464 logger_1.logger.warn("negative duration computed for frag " + fragTo.sn + ",level " + fragTo.level + ", there should be some duration drift between playlist and fragment!"); 5465 } 5466 } 5467 } 5468 else { 5469 // we dont know startPTS[toIdx] 5470 if (toIdx > fromIdx) { 5471 fragTo.start = fragFrom.start + fragFrom.duration; 5472 } 5473 else { 5474 fragTo.start = Math.max(fragFrom.start - fragTo.duration, 0); 5475 } 5476 } 5477} 5478exports.updatePTS = updatePTS; 5479function updateFragPTSDTS(details, frag, startPTS, endPTS, startDTS, endDTS) { 5480 // update frag PTS/DTS 5481 var maxStartPTS = startPTS; 5482 if (Number.isFinite(frag.startPTS)) { 5483 // delta PTS between audio and video 5484 var deltaPTS = Math.abs(frag.startPTS - startPTS); 5485 if (!Number.isFinite(frag.deltaPTS)) { 5486 frag.deltaPTS = deltaPTS; 5487 } 5488 else { 5489 frag.deltaPTS = Math.max(deltaPTS, frag.deltaPTS); 5490 } 5491 maxStartPTS = Math.max(startPTS, frag.startPTS); 5492 startPTS = Math.min(startPTS, frag.startPTS); 5493 endPTS = Math.max(endPTS, frag.endPTS); 5494 startDTS = Math.min(startDTS, frag.startDTS); 5495 endDTS = Math.max(endDTS, frag.endDTS); 5496 } 5497 var drift = startPTS - frag.start; 5498 frag.start = frag.startPTS = startPTS; 5499 frag.maxStartPTS = maxStartPTS; 5500 frag.endPTS = endPTS; 5501 frag.startDTS = startDTS; 5502 frag.endDTS = endDTS; 5503 frag.duration = endPTS - startPTS; 5504 var sn = frag.sn; 5505 // exit if sn out of range 5506 if (!details || sn < details.startSN || sn > details.endSN) { 5507 return 0; 5508 } 5509 var fragIdx, fragments, i; 5510 fragIdx = sn - details.startSN; 5511 fragments = details.fragments; 5512 // update frag reference in fragments array 5513 // rationale is that fragments array might not contain this frag object. 5514 // this will happen if playlist has been refreshed between frag loading and call to updateFragPTSDTS() 5515 // if we don't update frag, we won't be able to propagate PTS info on the playlist 5516 // resulting in invalid sliding computation 5517 fragments[fragIdx] = frag; 5518 // adjust fragment PTS/duration from seqnum-1 to frag 0 5519 for (i = fragIdx; i > 0; i--) { 5520 updatePTS(fragments, i, i - 1); 5521 } 5522 // adjust fragment PTS/duration from seqnum to last frag 5523 for (i = fragIdx; i < fragments.length - 1; i++) { 5524 updatePTS(fragments, i, i + 1); 5525 } 5526 details.PTSKnown = true; 5527 return drift; 5528} 5529exports.updateFragPTSDTS = updateFragPTSDTS; 5530function mergeDetails(oldDetails, newDetails) { 5531 // potentially retrieve cached initsegment 5532 if (newDetails.initSegment && oldDetails.initSegment) { 5533 newDetails.initSegment = oldDetails.initSegment; 5534 } 5535 // check if old/new playlists have fragments in common 5536 // loop through overlapping SN and update startPTS , cc, and duration if any found 5537 var ccOffset = 0; 5538 var PTSFrag; 5539 mapFragmentIntersection(oldDetails, newDetails, function (oldFrag, newFrag) { 5540 ccOffset = oldFrag.cc - newFrag.cc; 5541 if (Number.isFinite(oldFrag.startPTS)) { 5542 newFrag.start = newFrag.startPTS = oldFrag.startPTS; 5543 newFrag.endPTS = oldFrag.endPTS; 5544 newFrag.duration = oldFrag.duration; 5545 newFrag.backtracked = oldFrag.backtracked; 5546 newFrag.dropped = oldFrag.dropped; 5547 PTSFrag = newFrag; 5548 } 5549 // PTS is known when there are overlapping segments 5550 newDetails.PTSKnown = true; 5551 }); 5552 if (!newDetails.PTSKnown) { 5553 return; 5554 } 5555 if (ccOffset) { 5556 logger_1.logger.log('discontinuity sliding from playlist, take drift into account'); 5557 var newFragments = newDetails.fragments; 5558 for (var i = 0; i < newFragments.length; i++) { 5559 newFragments[i].cc += ccOffset; 5560 } 5561 } 5562 // if at least one fragment contains PTS info, recompute PTS information for all fragments 5563 if (PTSFrag) { 5564 updateFragPTSDTS(newDetails, PTSFrag, PTSFrag.startPTS, PTSFrag.endPTS, PTSFrag.startDTS, PTSFrag.endDTS); 5565 } 5566 else { 5567 // ensure that delta is within oldFragments range 5568 // also adjust sliding in case delta is 0 (we could have old=[50-60] and new=old=[50-61]) 5569 // in that case we also need to adjust start offset of all fragments 5570 adjustSliding(oldDetails, newDetails); 5571 } 5572 // if we are here, it means we have fragments overlapping between 5573 // old and new level. reliable PTS info is thus relying on old level 5574 newDetails.PTSKnown = oldDetails.PTSKnown; 5575} 5576exports.mergeDetails = mergeDetails; 5577function mergeSubtitlePlaylists(oldPlaylist, newPlaylist, referenceStart) { 5578 if (referenceStart === void 0) { referenceStart = 0; } 5579 var lastIndex = -1; 5580 mapFragmentIntersection(oldPlaylist, newPlaylist, function (oldFrag, newFrag, index) { 5581 newFrag.start = oldFrag.start; 5582 lastIndex = index; 5583 }); 5584 var frags = newPlaylist.fragments; 5585 if (lastIndex < 0) { 5586 frags.forEach(function (frag) { 5587 frag.start += referenceStart; 5588 }); 5589 return; 5590 } 5591 for (var i = lastIndex + 1; i < frags.length; i++) { 5592 frags[i].start = (frags[i - 1].start + frags[i - 1].duration); 5593 } 5594} 5595exports.mergeSubtitlePlaylists = mergeSubtitlePlaylists; 5596function mapFragmentIntersection(oldPlaylist, newPlaylist, intersectionFn) { 5597 if (!oldPlaylist || !newPlaylist) { 5598 return; 5599 } 5600 var start = Math.max(oldPlaylist.startSN, newPlaylist.startSN) - newPlaylist.startSN; 5601 var end = Math.min(oldPlaylist.endSN, newPlaylist.endSN) - newPlaylist.startSN; 5602 var delta = newPlaylist.startSN - oldPlaylist.startSN; 5603 for (var i = start; i <= end; i++) { 5604 var oldFrag = oldPlaylist.fragments[delta + i]; 5605 var newFrag = newPlaylist.fragments[i]; 5606 if (!oldFrag || !newFrag) { 5607 break; 5608 } 5609 intersectionFn(oldFrag, newFrag, i); 5610 } 5611} 5612exports.mapFragmentIntersection = mapFragmentIntersection; 5613function adjustSliding(oldPlaylist, newPlaylist) { 5614 var delta = newPlaylist.startSN - oldPlaylist.startSN; 5615 var oldFragments = oldPlaylist.fragments; 5616 var newFragments = newPlaylist.fragments; 5617 if (delta < 0 || delta > oldFragments.length) { 5618 return; 5619 } 5620 for (var i = 0; i < newFragments.length; i++) { 5621 newFragments[i].start += oldFragments[delta].start; 5622 } 5623} 5624exports.adjustSliding = adjustSliding; 5625function computeReloadInterval(currentPlaylist, newPlaylist, lastRequestTime) { 5626 var reloadInterval = 1000 * (newPlaylist.averagetargetduration ? newPlaylist.averagetargetduration : newPlaylist.targetduration); 5627 var minReloadInterval = reloadInterval / 2; 5628 if (currentPlaylist && newPlaylist.endSN === currentPlaylist.endSN) { 5629 // follow HLS Spec, If the client reloads a Playlist file and finds that it has not 5630 // changed then it MUST wait for a period of one-half the target 5631 // duration before retrying. 5632 reloadInterval = minReloadInterval; 5633 } 5634 if (lastRequestTime) { 5635 reloadInterval = Math.max(minReloadInterval, reloadInterval - (window.performance.now() - lastRequestTime)); 5636 } 5637 // in any case, don't reload more than half of target duration 5638 return Math.round(reloadInterval); 5639} 5640exports.computeReloadInterval = computeReloadInterval; 5641 5642/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 5643 5644/***/ }), 5645
5646/***/ "./src/controller/stream-controller.js": 5647/*!*********************************************!*\ 5648 !*** ./src/controller/stream-controller.js ***! 5649 \*********************************************/ 5650/*! no static exports found */ 5651/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 5652/***/ (function(module, exports, __webpack_require__) { 5653 5654"use strict"; 5655/* WEBPACK VAR INJECTION */(function(Number) { 5656/* 5657 * Stream Controller 5658*/ 5659var __extends = (this && this.__extends) || (function () { 5660 var extendStatics = Object.setPrototypeOf || 5661 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 5662 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 5663 return function (d, b) { 5664 extendStatics(d, b); 5665 function __() { this.constructor = d; } 5666 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 5667 }; 5668})(); 5669Object.defineProperty(exports, "__esModule", { value: true }); 5670var binary_search_1 = __webpack_require__(/*! ../utils/binary-search */ "./src/utils/binary-search.js"); 5671var buffer_helper_1 = __webpack_require__(/*! ../utils/buffer-helper */ "./src/utils/buffer-helper.js"); 5672var demuxer_1 = __webpack_require__(/*! ../demux/demuxer */ "./src/demux/demuxer.js"); 5673var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 5674var fragment_tracker_1 = __webpack_require__(/*! ./fragment-tracker */ "./src/controller/fragment-tracker.js"); 5675var fragment_1 = __webpack_require__(/*! ../loader/fragment */ "./src/loader/fragment.js"); 5676var playlist_loader_1 = __webpack_require__(/*! ../loader/playlist-loader */ "./src/loader/playlist-loader.js"); 5677var LevelHelper = __webpack_require__(/*! ./level-helper */ "./src/controller/level-helper.js"); 5678var time_ranges_1 = __webpack_require__(/*! ../utils/time-ranges */ "./src/utils/time-ranges.js"); 5679var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 5680var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 5681var discontinuities_1 = __webpack_require__(/*! ../utils/discontinuities */ "./src/utils/discontinuities.js"); 5682var fragment_finders_1 = __webpack_require__(/*! ./fragment-finders */ "./src/controller/fragment-finders.js"); 5683var gap_controller_1 = __webpack_require__(/*! ./gap-controller */ "./src/controller/gap-controller.js"); 5684var base_stream_controller_1 = __webpack_require__(/*! ./base-stream-controller */ "./src/controller/base-stream-controller.js"); 5685var TICK_INTERVAL = 100; // how often to tick in ms 5686var StreamController = /** @class */ (function (_super) { 5687 __extends(StreamController, _super); 5688 function StreamController(hls, fragmentTracker) { 5689 var _this = _super.call(this, hls, events_1.default.MEDIA_ATTA
5689CHED, events_1.default.MEDIA_DETACHING, events_1.default.MANIFEST_LOADING, events_1.default.MANIFEST_PARSED, events_1.default.LEVEL_LOADED, events_1.default.KEY_LOADED, events_1.default.FRAG_LOADED, events_1.default.FRAG_LOAD_EMERGENCY_ABORTED, events_1.default.FRAG_PARSING_INIT_SEGMENT, events_1.default.FRAG_PARSING_DATA, events_1.default.FRAG_PARSED, events_1.default.ERROR, events_1.default.AUDIO_TRACK_SWITCHING, events_1.default.AUDIO_TRACK_SWITCHED, events_1.default.BUFFER_CREATED, events_1.default.BUFFER_APPENDED, events_1.default.BUFFER_FLUSHED) || this; 5690 _this.fragmentTracker = fragmentTracker; 5691 _this.config = hls.config; 5692 _this.audioCodecSwap = false; 5693 _this._state = base_stream_controller_1.State.STOPPED; 5694 _this.stallReported = false; 5695 _this.gapController = null; 5696 return _this; 5697 } 5698 StreamController.prototype.startLoad = function (startPosition) { 5699 if (this.levels) { 5700 var lastCurrentTime = this.lastCurrentTime, hls_1 = this.hls; 5701 this.stopLoad(); 5702 this.setInterval(TICK_INTERVAL); 5703 this.level = -1; 5704 this.fragLoadError = 0; 5705 if (!this.startFragRequested) { 5706 // determine load level 5707 var startLevel = hls_1.startLevel; 5708 if (startLevel === -1) { 5709 // -1 : guess start Level by doing a bitrate test by loading first fragment of lowest quality level 5710 startLevel = 0; 5711 this.bitrateTest = true; 5712 } 5713 // set new level to playlist loader : this will trigger start level load 5714 // hls.nextLoadLevel remains until it is set to a new value or until a new frag is successfully loaded 5715 this.level = hls_1.nextLoadLevel = startLevel; 5716 this.loadedmetadata = false; 5717 } 5718 // if startPosition undefined but lastCurrentTime set, set startPosition to last currentTime 5719 if (lastCurrentTime > 0 && startPosition === -1) { 5720 logger_1.logger.log("override startPosition with lastCurrentTime @" + lastCurrentTime.toFixed(3)); 5721 startPosition = lastCurrentTime; 5722 } 5723 this.state = base_stream_controller_1.State.IDLE; 5724 this.nextLoadPosition = this.startPosition = this.lastCurrentTime = startPosition; 5725 this.tick(); 5726 } 5727 else { 5728 this.forceStartLoad = true; 5729 this.state = base_stream_controller_1.State.STOPPED; 5730 } 5731 }; 5732 StreamController.prototype.stopLoad = function () { 5733 this.forceStartLoad = false; 5734 _super.prototype.stopLoad.call(this); 5735 }; 5736 StreamController.prototype.doTick = function () { 5737 switch (this.state) { 5738 case base_stream_controller_1.State.BUFFER_FLUSHING: 5739 // in buffer flushing state, reset fragLoadError counter 5740 this.fragLoadError = 0; 5741 break; 5742 case base_stream_controller_1.State.IDLE: 5743 this._doTickIdle(); 5744 break; 5745 case base_stream_controller_1.State.WAITING_LEVEL: 5746 var level = this.levels[this.level]; 5747 // check if playlist is already loaded 5748 if (level && level.details) { 5749 this.state = base_stream_controller_1.State.IDLE; 5750 } 5751 break; 5752 case base_stream_controller_1.State.FRAG_LOADING_WAITING_RETRY: 5753 var now = window.performance.now(); 5754 var retryDate = this.retryDate; 5755 // if current time is gt than retryDate, or if media seeking let's switch to IDLE state to retry loading 5756 if (!retryDate || (now >= retryDate) || (this.media && this.media.seeking)) { 5757 logger_1.logger.log('mediaController: retryDate reached, switch back to IDLE state'); 5758 this.state = base_stream_controller_1.State.IDLE; 5759 } 5760 break; 5761 case base_stream_controller_1.State.ERROR: 5762 case base_stream_controller_1.State.STOPPED: 5763 case base_stream_controller_1.State.FRAG_LOADING: 5764 case base_stream_controller_1.State.PARSING: 5765 case base_stream_controller_1.State.PARSED: 5766 case base_stream_controller_1.State.ENDED: 5767 break; 5768 default: 5769 break; 5770 } 5771 // check buffer 5772 this._checkBuffer(); 5773 // check/update current fragment 5774 this._checkFragmentChanged(); 5775 }; 5776 // Ironically the "idle" state is the on we do the most logic in it seems .... 5777 // NOTE: Maybe we could rather schedule a check for buffer length after half of the currently 5778 // played segment, or on pause/play/seek instead of naively checking every 100ms? 5779 StreamController.prototype._doTickIdle = function () { 5780 var hls = this.hls, config = hls.config, media = this.media; 5781 // if start level not parsed yet OR 5782 // if video not attached AND start fragment already requested OR start frag prefetch disable 5783 // exit loop, as we either need more info (level not parsed) or we need media to be attached to load new fragment 5784 if (this.levelLastLoaded === undefined || (!media && (this.startFragRequested || !config.startFragPrefetch))) { 5785 return; 5786 } 5787 // if we have not yet loaded any fragment, start loading from start position 5788 var pos; 5789 if (this.loadedmetadata) { 5790 pos = media.currentTime; 5791 } 5792 else { 5793 pos = this.nextLoadPosition; 5794 } 5795 // determine next load level 5796 var level = hls.nextLoadLevel, levelInfo = this.levels[level]; 5797 if (!levelInfo) { 5798 return; 5799 } 5800 var levelBitrate = levelInfo.bitrate, maxBufLen; 5801 // 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 5802 if (levelBitrate) { 5803 maxBufLen = Math.max(8 * config.maxBufferSize / levelBitrate, config.maxBufferLength); 5804 } 5805 else { 5806 maxBufLen = config.maxBufferLength; 5807 }
vendor: 9,873 bytes, lines 5808-5967
5808 maxBufLen = Math.min(maxBufLen, config.maxMaxBufferLength); 5809 // determine next candidate fragment to be loaded, based on current position and end of buffer position 5810 // ensure up to `config.maxMaxBufferLength` of buffer upfront 5811 var bufferInfo = buffer_helper_1.BufferHelper.bufferInfo(this.mediaBuffer ? this.mediaBuffer : media, pos, config.maxBufferHole), bufferLen = bufferInfo.len; 5812 // Stay idle if we are still with buffer margins 5813 if (bufferLen >= maxBufLen) { 5814 return; 5815 } 5816 // if buffer length is less than maxBufLen try to load a new fragment ... 5817 logger_1.logger.trace("buffer length of " + bufferLen.toFixed(3) + " is below max of " + maxBufLen.toFixed(3) + ". checking for more payload ..."); 5818 // set next load level : this will trigger a playlist load if needed 5819 this.level = hls.nextLoadLevel = level; 5820 var levelDetails = levelInfo.details; 5821 // if level info not retrieved yet, switch state and wait for level retrieval 5822 // if live playlist, ensure that new playlist has been refreshed to avoid loading/try to load 5823 // a useless and outdated fragment (that might even introduce load error if it is already out of the live playlist) 5824 if (!levelDetails || (levelDetails.live && this.levelLastLoaded !== level)) { 5825 this.state = base_stream_controller_1.State.WAITING_LEVEL; 5826 return; 5827 } 5828 if (this._streamEnded(bufferInfo, levelDetails)) { 5829 var data = {}; 5830 if (this.altAudio) { 5831 data.type = 'video'; 5832 } 5833 this.hls.trigger(events_1.default.BUFFER_EOS, data); 5834 this.state = base_stream_controller_1.State.ENDED; 5835 return; 5836 } 5837 // if we have the levelDetails for the selected variant, lets continue enrichen our stream (load keys/fragments or trigger EOS, etc..) 5838 this._fetchPayloadOrEos(pos, bufferInfo, levelDetails); 5839 }; 5840 StreamController.prototype._fetchPayloadOrEos = function (pos, bufferInfo, levelDetails) { 5841 var fragPrevious = this.fragPrevious, level = this.level, fragments = levelDetails.fragments, fragLen = fragments.length; 5842 // empty playlist 5843 if (fragLen === 0) { 5844 return; 5845 } 5846 // find fragment index, contiguous with end of buffer position 5847 var start = fragments[0].start, end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration, bufferEnd = bufferInfo.end, frag; 5848 if (levelDetails.initSegment && !levelDetails.initSegment.data) { 5849 frag = levelDetails.initSegment; 5850 } 5851 else { 5852 // in case of live playlist we need to ensure that requested position is not located before playlist start 5853 if (levelDetails.live) { 5854 var initialLiveManifestSize = this.config.initialLiveManifestSize; 5855 if (fragLen < initialLiveManifestSize) { 5856 logger_1.logger.warn("Can not start playback of a level, reason: not enough fragments " + fragLen + " < " + initialLiveManifestSize); 5857 return; 5858 } 5859 frag = this._ensureFragmentAtLivePoint(levelDetails, bufferEnd, start, end, fragPrevious, fragments, fragLen); 5860 // if it explicitely returns null don't load any fragment and exit function now 5861 if (frag === null) { 5862 return; 5863 } 5864 } 5865 else { 5866 // VoD playlist: if bufferEnd before start of playlist, load first fragment 5867 if (bufferEnd < start) { 5868 frag = fragments[0]; 5869 } 5870 } 5871 } 5872 if (!frag) { 5873 frag = this._findFragment(start, fragPrevious, fragLen, fragments, bufferEnd, end, levelDetails); 5874 } 5875 if (frag) { 5876 if (frag.encrypted) { 5877 logger_1.logger.log("Loading key for " + frag.sn + " of [" + levelDetails.startSN + " ," + levelDetails.endSN + "],level " + level); 5878 this._loadKey(frag); 5879 } 5880 else { 5881 logger_1.logger.log("Loading " + frag.sn + " of [" + levelDetails.startSN + " ," + levelDetails.endSN + "],level " + level + ", currentTime:" + pos.toFixed(3) + ",bufferEnd:" + bufferEnd.toFixed(3)); 5882 this._loadFragment(frag); 5883 } 5884 } 5885 }; 5886 StreamController.prototype._ensureFragmentAtLivePoint = function (levelDetails, bufferEnd, start, end, fragPrevious, fragments, fragLen) { 5887 var config = this.hls.config, media = this.media; 5888 var frag; 5889 // check if requested position is within seekable boundaries : 5890 // logger.log(`start/pos/bufEnd/seeking:${start.toFixed(3)}/${pos.toFixed(3)}/${bufferEnd.toFixed(3)}/${this.media.seeking}`); 5891 var maxLatency = config.liveMaxLatencyDuration !== undefined ? config.liveMaxLatencyDuration : config.liveMaxLatencyDurationCount * levelDetails.targetduration; 5892 if (bufferEnd < Math.max(start - config.maxFragLookUpTolerance, end - maxLatency)) { 5893 var liveSyncPosition = this.liveSyncPosition = this.computeLivePosition(start, levelDetails); 5894 logger_1.logger.log("buffer end: " + bufferEnd.toFixed(3) + " is located too far from the end of live sliding playlist, reset currentTime to : " + liveSyncPosition.toFixed(3)); 5895 bufferEnd = liveSyncPosition; 5896 if (media && media.readyState && media.duration > liveSyncPosition) { 5897 media.currentTime = liveSyncPosition; 5898 } 5899 this.nextLoadPosition = liveSyncPosition; 5900 } 5901 // if end of buffer greater than live edge, don't load any fragment 5902 // this could happen if live playlist intermittently slides in the past. 5903 // level 1 loaded [182580161,182580167] 5904 // level 1 loaded [182580162,182580169] 5905 // Loading 182580168 of [182580162 ,182580169],level 1 .. 5906 // Loading 182580169 of [182580162 ,182580169],level 1 .. 5907 // level 1 loaded [182580162,182580168] <============= here we should have bufferEnd > end. in that case break to avoid reloading 182580168 5908 // level 1 loaded [182580164,182580171] 5909 // 5910 // don't return null in case media not loaded yet (readystate === 0) 5911 if (levelDetails.PTSKnown && bufferEnd > end && media && media.readyState) { 5912 return null; 5913 } 5914 if (this.startFragRequested && !levelDetails.PTSKnown) { 5915 /* we are switching level on live playlist, but we don't have any PTS info for that quality level ... 5916 try to load frag matching with next SN. 5917 even if SN are not synchronized between playlists, loading this frag will help us 5918 compute playlist sliding and find the right one after in case it was not the right consecutive one */ 5919 if (fragPrevious) { 5920 if (levelDetails.hasProgramDateTime) { 5921 // Relies on PDT in order to switch bitrates (Support EXT-X-DISCONTINUITY without EXT-X-DISCONTINUITY-SEQUENCE) 5922 logger_1.logger.log("live playlist, switching playlist, load frag with same PDT: " + fragPrevious.programDateTime); 5923 frag = fragment_finders_1.findFragmentByPDT(fragments, fragPrevious.endProgramDateTime, config.maxFragLookUpTolerance); 5924 } 5925 else { 5926 // Uses buffer and sequence number to calculate switch segment (required if using EXT-X-DISCONTINUITY-SEQUENCE) 5927 var targetSN = fragPrevious.sn + 1; 5928 if (targetSN >= levelDetails.startSN && targetSN <= levelDetails.endSN) { 5929 var fragNext = fragments[targetSN - levelDetails.startSN]; 5930 if (fragPrevious.cc === fragNext.cc) { 5931 frag = fragNext; 5932 logger_1.logger.log("live playlist, switching playlist, load frag with next SN: " + frag.sn); 5933 } 5934 } 5935 // next frag SN not available (or not with same continuity counter) 5936 // look for a frag sharing the same CC 5937 if (!frag) { 5938 frag = binary_search_1.default.search(fragments, function (frag) { 5939 return fragPrevious.cc - frag.cc; 5940 }); 5941 if (frag) { 5942 logger_1.logger.log("live playlist, switching playlist, load frag with same CC: " + frag.sn); 5943 } 5944 } 5945 } 5946 } 5947 if (!frag) { 5948 /* we have no idea about which fragment should be loaded. 5949 so let's load mid fragment. it will help computing playlist sliding and find the right one 5950 */ 5951 frag = fragments[Math.min(fragLen - 1, Math.round(fragLen / 2))]; 5952 logger_1.logger.log("live playlist, switching playlist, unknown, load middle frag : " + frag.sn); 5953 } 5954 } 5955 return frag; 5956 }; 5957 StreamController.prototype._findFragment = function (start, fragPrevious, fragLen, fragments, bufferEnd, end, levelDetails) { 5958 var config = this.hls.config; 5959 var frag; 5960 if (bufferEnd < end) { 5961 var lookupTolerance = (bufferEnd > end - config.maxFragLookUpTolerance) ? 0 : config.maxFragLookUpTolerance; 5962 // Remove the tolerance if it would put the bufferEnd past the actual end of stream 5963 // Uses buffer and sequence number to calculate switch segment (required if using EXT-X-DISCONTINUITY-SEQUENCE) 5964 frag = fragment_finders_1.findFragmentByPTS(fragPrevious, fragments, bufferEnd, lookupTolerance); 5965 } 5966 else { 5967 // reach end of playlist
5968 frag = fragments[fragLen - 1]; 5969 } 5970 if (frag) { 5971 var curSNIdx = frag.sn - levelDetails.startSN; 5972 var sameLevel = fragPrevious && frag.level === fragPrevious.level; 5973 var prevFrag = fragments[curSNIdx - 1]; 5974 var nextFrag = fragments[curSNIdx + 1]; 5975 // logger.log('find SN matching with pos:' + bufferEnd + ':' + frag.sn); 5976 if (fragPrevious && frag.sn === fragPrevious.sn) { 5977 if (sameLevel && !frag.backtracked) { 5978 if (frag.sn < levelDetails.endSN) { 5979 var deltaPTS = fragPrevious.deltaPTS; 5980 // if there is a significant delta between audio and video, larger than max allowed hole, 5981 // and if previous remuxed fragment did not start with a keyframe. (fragPrevious.dropped) 5982 // let's try to load previous fragment again to get last keyframe 5983 // then we will reload again current fragment (that way we should be able to fill the buffer hole ...) 5984 if (deltaPTS && deltaPTS > config.maxBufferHole && fragPrevious.dropped && curSNIdx) { 5985 frag = prevFrag; 5986 logger_1.logger.warn('SN just loaded, with large PTS gap between audio and video, maybe frag is not starting with a keyframe ? load previous one to try to overcome this'); 5987 } 5988 else { 5989 frag = nextFrag; 5990 logger_1.logger.log("SN just loaded, load next one: " + frag.sn, frag); 5991 } 5992 } 5993 else { 5994 frag = null; 5995 } 5996 } 5997 else if (frag.backtracked) { 5998 // Only backtrack a max of 1 consecutive fragment to prevent sliding back too far when little or no frags start with keyframes 5999 if (nextFrag && nextFrag.backtracked) { 6000 logger_1.logger.warn("Already backtracked from fragment " + nextFrag.sn + ", will not backtrack to fragment " + frag.sn + ". Loading fragment " + nextFrag.sn); 6001 frag = nextFrag; 6002 } 6003 else { 6004 // If a fragment has dropped frames and it's in a same level/sequence, load the previous fragment to try and find the keyframe 6005 // Reset the dropped count now since it won't be reset until we parse the fragment again, which prevents infinite backtracking on the same segment 6006 logger_1.logger.warn('Loaded fragment with dropped frames, backtracking 1 segment to find a keyframe'); 6007 frag.dropped = 0; 6008 if (prevFrag) { 6009 frag = prevFrag; 6010 frag.backtracked = true; 6011 } 6012 else if (curSNIdx) { 6013 // can't backtrack on very first fragment 6014 frag = null; 6015 } 6016 } 6017 } 6018 } 6019 } 6020 return frag; 6021 }; 6022 StreamController.prototype._loadKey = function (frag) { 6023 this.state = base_stream_controller_1.State.KEY_LOADING; 6024 this.hls.trigger(events_1.default.KEY_LOADING, { frag: frag }); 6025 }; 6026 StreamController.prototype._loadFragment = function (frag) { 6027 // Check if fragment is not loaded 6028 var fragState = this.fragmentTracker.getState(frag); 6029 this.fragCurrent = frag; 6030 this.startFragRequested = true; 6031 // Don't update nextLoadPosition for fragments which are not buffered 6032 if (Number.isFinite(frag.sn) && !frag.bitrateTest) { 6033 this.nextLoadPosition = frag.start + frag.duration; 6034 } 6035 // Allow backtracked fragments to load 6036 if (frag.backtracked || fragState === fragment_tracker_1.FragmentState.NOT_LOADED || fragState === fragment_tracker_1.FragmentState.PARTIAL) { 6037 frag.autoLevel = this.hls.autoLevelEnabled; 6038 frag.bitrateTest = this.bitrateTest; 6039 this.hls.trigger(events_1.default.FRAG_LOADING, { frag: frag }); 6040 // lazy demuxer init, as this could take some time ... do it during frag loading 6041 if (!this.demuxer) { 6042 this.demuxer = new demuxer_1.default(this.hls, 'main'); 6043 } 6044 this.state = base_stream_controller_1.State.FRAG_LOADING; 6045 } 6046 else if (fragState === fragment_tracker_1.FragmentState.APPENDING) { 6047 // Lower the buffer size and try again 6048 if (this._reduceMaxBufferLength(frag.duration)) { 6049 this.fragmentTracker.removeFragment(frag); 6050 } 6051 } 6052 };
6053 Object.defineProperty(StreamController.prototype, "state", { 6054 get: function () { 6055 return this._state; 6056 }, 6057 set: function (nextState) { 6058 if (this.state !== nextState) { 6059 var previousState = this.state; 6060 this._state = nextState; 6061 logger_1.logger.log("main stream:" + previousState + "->" + nextState); 6062 this.hls.trigger(events_1.default.STREAM_STATE_TRANSITION, { previousState: previousState, nextState: nextState }); 6063 } 6064 }, 6065 enumerable: true, 6066 configurable: true 6067 }); 6068 StreamController.prototype.getBufferedFrag = function (position) { 6069 return this.fragmentTracker.getBufferedFrag(position, playlist_loader_1.default.LevelType.MAIN); 6070 }; 6071 Object.defineProperty(StreamController.prototype, "currentLevel", { 6072 get: function () { 6073 var media = this.media; 6074 if (media) { 6075 var frag = this.getBufferedFrag(media.currentTime); 6076 if (frag) { 6077 return frag.level; 6078 } 6079 } 6080 return -1; 6081 }, 6082 enumerable: true, 6083 configurable: true 6084 }); 6085 Object.defineProperty(StreamController.prototype, "nextBufferedFrag", { 6086 get: function () { 6087 var media = this.media; 6088 if (media) { 6089 // first get end range of current fragment 6090 return this.followingBufferedFrag(this.getBufferedFrag(media.currentTime)); 6091 } 6092 else { 6093 return null; 6094 } 6095 }, 6096 enumerable: true, 6097 configurable: true 6098 }); 6099 StreamController.prototype.followingBufferedFrag = function (frag) { 6100 if (frag) { 6101 // try to get range of next fragment (500ms after this range) 6102 return this.getBufferedFrag(frag.endPTS + 0.5); 6103 } 6104 return null; 6105 }; 6106 Object.defineProperty(StreamController.prototype, "nextLevel", { 6107 get: function () { 6108 var frag = this.nextBufferedFrag; 6109 if (frag) { 6110 return frag.level; 6111 } 6112 else { 6113 return -1; 6114 } 6115 }, 6116 enumerable: true, 6117 configurable: true 6118 }); 6119 StreamController.prototype._checkFragmentChanged = function () { 6120 var fragPlayingCurrent, currentTime, video = this.media; 6121 if (video && video.readyState && video.seeking === false) { 6122 currentTime = video.currentTime; 6123 /* if video element is in seeked state, currentTime can only increase. 6124 (assuming that playback rate is positive ...) 6125 As sometimes currentTime jumps back to zero after a 6126 media decode error, check this, to avoid seeking back to 6127 wrong position after a media decode error 6128 */ 6129 if (currentTime > this.lastCurrentTime) { 6130 this.lastCurrentTime = currentTime; 6131 } 6132 if (buffer_helper_1.BufferHelper.isBuffered(video, currentTime)) { 6133 fragPlayingCurrent = this.getBufferedFrag(currentTime); 6134 } 6135 else if (buffer_helper_1.BufferHelper.isBuffered(video, currentTime + 0.1)) { 6136 /* ensure that FRAG_CHANGED event is triggered at startup, 6137 when first video frame is displayed and playback is paused. 6138 add a tolerance of 100ms, in case current position is not buffered, 6139 check if current pos+100ms is buffered and use that buffer range 6140 for FRAG_CHANGED event reporting */ 6141 fragPlayingCurrent = this.getBufferedFrag(currentTime + 0.1); 6142 } 6143 if (fragPlayingCurrent) { 6144 var fragPlaying = fragPlayingCurrent; 6145 if (fragPlaying !== this.fragPlaying) { 6146 this.hls.trigger(events_1.default.FRAG_CHANGED, { frag: fragPlaying }); 6147 var fragPlayingLevel = fragPlaying.level; 6148 if (!this.fragPlaying || this.fragPlaying.level !== fragPlayingLevel) { 6149 this.hls.trigger(events_1.default.LEVEL_SWITCHED, { level: fragPlayingLevel }); 6150 } 6151 this.fragPlaying = fragPlaying; 6152 } 6153 } 6154 } 6155 }; 6156 /* 6157 on immediate level switch : 6158 - pause playback if playing
6159 - cancel any pending load request 6160 - and trigger a buffer flush 6161 */ 6162 StreamController.prototype.immediateLevelSwitch = function () { 6163 logger_1.logger.log('immediateLevelSwitch'); 6164 if (!this.immediateSwitch) { 6165 this.immediateSwitch = true; 6166 var media = this.media, previouslyPaused = void 0; 6167 if (media) { 6168 previouslyPaused = media.paused; 6169 media.pause(); 6170 } 6171 else { 6172 // don't restart playback after instant level switch in case media not attached 6173 previouslyPaused = true; 6174 } 6175 this.previouslyPaused = previouslyPaused; 6176 } 6177 var fragCurrent = this.fragCurrent; 6178 if (fragCurrent && fragCurrent.loader) { 6179 fragCurrent.loader.abort(); 6180 } 6181 this.fragCurrent = null; 6182 // flush everything 6183 this.flushMainBuffer(0, Number.POSITIVE_INFINITY); 6184 }; 6185 /** 6186 * on immediate level switch end, after new fragment has been buffered: 6187 * - nudge video decoder by slightly adjusting video currentTime (if currentTime buffered) 6188 * - resume the playback if needed 6189 */ 6190 StreamController.prototype.immediateLevelSwitchEnd = function () { 6191 var media = this.media; 6192 if (media && media.buffered.length) { 6193 this.immediateSwitch = false; 6194 if (buffer_helper_1.BufferHelper.isBuffered(media, media.currentTime)) { 6195 // only nudge if currentTime is buffered 6196 media.currentTime -= 0.0001; 6197 } 6198 if (!this.previouslyPaused) { 6199 media.play(); 6200 } 6201 } 6202 }; 6203 /** 6204 * try to switch ASAP without breaking video playback: 6205 * in order to ensure smooth but quick level switching, 6206 * we need to find the next flushable buffer range 6207 * we should take into account new segment fetch time 6208 */ 6209 StreamController.prototype.nextLevelSwitch = function () { 6210 var media = this.media; 6211 // ensure that media is defined and that metadata are available (to retrieve currentTime) 6212 if (media && media.readyState) { 6213 var fetchdelay = void 0, fragPlayingCurrent = void 0, nextBufferedFrag = void 0; 6214 fragPlayingCurrent = this.getBufferedFrag(media.currentTime); 6215 if (fragPlayingCurrent && fragPlayingCurrent.startPTS > 1) { 6216 // flush buffer preceding current fragment (flush until current fragment start offset) 6217 // minus 1s to avoid video freezing, that could happen if we flush ke
vendor: 4,255 bytes, lines 6217-6302
6217yframe of current video ... 6218 this.flushMainBuffer(0, fragPlayingCurrent.startPTS - 1); 6219 } 6220 if (!media.paused) { 6221 // add a safety delay of 1s 6222 var nextLevelId = this.hls.nextLoadLevel, nextLevel = this.levels[nextLevelId], fragLastKbps = this.fragLastKbps; 6223 if (fragLastKbps && this.fragCurrent) { 6224 fetchdelay = this.fragCurrent.duration * nextLevel.bitrate / (1000 * fragLastKbps) + 1; 6225 } 6226 else { 6227 fetchdelay = 0; 6228 } 6229 } 6230 else { 6231 fetchdelay = 0; 6232 } 6233 // logger.log('fetchdelay:'+fetchdelay); 6234 // find buffer range that will be reached once new fragment will be fetched 6235 nextBufferedFrag = this.getBufferedFrag(media.currentTime + fetchdelay); 6236 if (nextBufferedFrag) { 6237 // we can flush buffer range following this one without stalling playback 6238 nextBufferedFrag = this.followingBufferedFrag(nextBufferedFrag); 6239 if (nextBufferedFrag) { 6240 // if we are here, we can also cancel any loading/demuxing in progress, as they are useless 6241 var fragCurrent = this.fragCurrent; 6242 if (fragCurrent && fragCurrent.loader) { 6243 fragCurrent.loader.abort(); 6244 } 6245 this.fragCurrent = null; 6246 // start flush position is the start PTS of next buffered frag. 6247 // we use frag.naxStartPTS which is max(audio startPTS, video startPTS). 6248 // in case there is a small PTS Delta between audio and video, using maxStartPTS avoids flushing last samples from current fragment 6249 this.flushMainBuffer(nextBufferedFrag.maxStartPTS, Number.POSITIVE_INFINITY); 6250 } 6251 } 6252 } 6253 }; 6254 StreamController.prototype.flushMainBuffer = function (startOffset, endOffset) { 6255 this.state = base_stream_controller_1.State.BUFFER_FLUSHING; 6256 var flushScope = { startOffset: startOffset, endOffset: endOffset }; 6257 // if alternate audio tracks are used, only flush video, otherwise flush everything 6258 if (this.altAudio) { 6259 flushScope.type = 'video'; 6260 } 6261 this.hls.trigger(events_1.default.BUFFER_FLUSHING, flushScope); 6262 }; 6263 StreamController.prototype.onMediaAttached = function (data) { 6264 var media = this.media = this.mediaBuffer = data.media; 6265 this.onvseeking = this.onMediaSeeking.bind(this); 6266 this.onvseeked = this.onMediaSeeked.bind(this); 6267 this.onvended = this.onMediaEnded.bind(this); 6268 media.addEventListener('seeking', this.onvseeking); 6269 media.addEventListener('seeked', this.onvseeked); 6270 media.addEventListener('ended', this.onvended); 6271 var config = this.config; 6272 if (this.levels && config.autoStartLoad) { 6273 this.hls.startLoad(config.startPosition); 6274 } 6275 this.gapController = new gap_controller_1.default(config, media, this.fragmentTracker, this.hls); 6276 }; 6277 StreamController.prototype.onMediaDetaching = function () { 6278 var media = this.media; 6279 if (media && media.ended) { 6280 logger_1.logger.log('MSE detaching and video ended, reset startPosition'); 6281 this.startPosition = this.lastCurrentTime = 0; 6282 } 6283 // reset fragment backtracked flag 6284 var levels = this.levels; 6285 if (levels) { 6286 levels.forEach(function (level) { 6287 if (level.details) { 6288 level.details.fragments.forEach(function (fragment) { 6289 fragment.backtracked = undefined; 6290 }); 6291 } 6292 }); 6293 } 6294 // remove video listeners 6295 if (media) { 6296 media.removeEventListener('seeking', this.onvseeking); 6297 media.removeEventListener('seeked', this.onvseeked); 6298 media.removeEventListener('ended', this.onvended); 6299 this.onvseeking = this.onvseeked = this.onvended = null; 6300 } 6301 this.media = this.mediaBuffer = null; 6302 this.loadedmetadata = false;
6303 this.stopLoad(); 6304 }; 6305 StreamController.prototype.onMediaSeeked = function () { 6306 var media = this.media, currentTime = media ? media.currentTime : undefined; 6307 if (Number.isFinite(currentTime)) { 6308 logger_1.logger.log("media seeked to " + currentTime.toFixed(3)); 6309 } 6310 // tick to speed up FRAGMENT_PLAYING triggering 6311 this.tick(); 6312 }; 6313 StreamController.prototype.onManifestLoading = function () { 6314 // reset buffer on manifest loading 6315 logger_1.logger.log('trigger BUFFER_RESET'); 6316 this.hls.trigger(events_1.default.BUFFER_RESET); 6317 this.fragmentTracker.removeAllFragments(); 6318 this.stalled = false; 6319 this.startPosition = this.lastCurrentTime = 0; 6320 }; 6321 StreamController.prototype.onManifestParsed = function (data) { 6322 var aac = false, heaac = false, codec; 6323 data.levels.forEach(function (level) { 6324 // detect if we have different kind of audio codecs used amongst playlists 6325 codec = level.audioCodec; 6326 if (codec) { 6327 if (codec.indexOf('mp4a.40.2') !== -1) { 6328 aac = true; 6329 } 6330 if (codec.indexOf('mp4a.40.5') !== -1) { 6331 heaac = true; 6332 } 6333 } 6334 }); 6335 this.audioCodecSwitch = (aac && heaac); 6336 if (this.audioCodecSwitch) { 6337 logger_1.logger.log('both AAC/HE-AAC audio found in levels; declaring level codec as HE-AAC'); 6338 } 6339 this.levels = data.levels; 6340 this.startFragRequested = false; 6341 var config = this.config; 6342 if (config.autoStartLoad || this.forceStartLoad) { 6343 this.hls.startLoad(config.startPosition); 6344 } 6345 }; 6346 StreamController.prototype.onLevelLoaded = function (data) { 6347 var newDetails = data.details; 6348 var newLevelId = data.level; 6349 var lastLevel = this.levels[this.levelLastLoaded]; 6350 var curLevel = this.levels[newLevelId]; 6351 var duration = newDetails.totalduration; 6352 var sliding = 0; 6353 logger_1.logger.log("level " + newLevelId + " loaded [" + newDetails.startSN + "," + newDetails.endSN + "],duration:" + duration); 6354 if (newDetails.live) { 6355 var curDetails = curLevel.details; 6356 if (curDetails && newDetails.fragments.length > 0) { 6357 // we already have details for that level, merge them 6358 LevelHelper.mergeDetails(curDetails, newDetails); 6359 sliding = newDetails.fragments[0].start; 6360 this.liveSyncPosition = this.computeLivePosition(sliding, curDetails); 6361 if (newDetails.PTSKnown && Number.isFinite(sliding)) { 6362 logger_1.logger.log("live playlist sliding:" + sliding.toFixed(3)); 6363 } 6364 else { 6365 logger_1.logger.log('live playlist - outdated PTS, unknown sliding'); 6366 discontinuities_1.alignStream(this.fragPrevious, lastLevel, newDetails); 6367 } 6368 } 6369 else { 6370 logger_1.logger.log('live playlist - first load, unknown sliding'); 6371 newDetails.PTSKnown = false; 6372 discontinuities_1.alignStream(this.fragPrevious, lastLevel, newDetails); 6373 } 6374 } 6375 else { 6376 newDetails.PTSKnown = false; 6377 } 6378 // override level info 6379 curLevel.details = newDetails; 6380 this.levelLastLoaded = newLevelId; 6381 this.hls.trigger(events_1.default.LEVEL_UPDATED, { details: newDetails, level: newLevelId }); 6382 if (this.startFragRequested === false) { 6383 // compute start position if set to -1. use it straight away if value is defined 6384 if (this.startPosition === -1 || this.lastCurrentTime === -1) { 6385 // first, check if start time offset has been set in playlist, if yes, use this value 6386 var startTimeOffset = newDetails.startTimeOffset; 6387 if (Number.isFinite(startTimeOffset)) { 6388 if (startTimeOffset < 0) { 6389 logger_1.logger.log("negative start time offset " + startTimeOffset + ", count from end of last fragment"); 6390 startTimeOffset = sliding + duration + startTimeOffset; 6391 } 6392 logger_1.logger.log("start time offset found in playlist, adjust startPosition to " + startTimeOffset); 6393 this.startPosition = startTimeOffset; 6394 } 6395 else { 6396 // if live playlist, set start position to be fragment N-this.config.liveSyncDurationCount (usually 3) 6397 if (newDetails.live) { 6398 this.startPosition = this.computeLivePosition(sliding, newDetails); 6399 logger_1.logger.log("configure startPosition to " + this.startPosition); 6400 } 6401 else { 6402 this.startPosition = 0; 6403 } 6404 } 6405 this.lastCurrentTime = this.startPosition; 6406 } 6407 this.nextLoadPosition = this.startPosition; 6408 } 6409 // only switch batck to IDLE state if we were waiting for level to start downloading a new fragment 6410 if (this.state === base_stream_controller_1.State.WAITING_LEVEL) { 6411 this.state = base_stream_controller_1.State.IDLE; 6412 } 6413 // trigger handler right now 6414 this.tick(); 6415 }; 6416 StreamController.prototype.onKeyLoaded = function () { 6417 if (this.state === base_stream_controller_1.State.KEY_LOADING) { 6418 this.state = base_stream_controller_1.State.IDLE; 6419 this.tick(); 6420 } 6421 }; 6422 StreamController.prototype.onFragLoaded = function (data) { 6423 var _a = this, fragCurrent = _a.fragCurrent, hls = _a.hls, levels = _a.levels, media = _a.media; 6424 var fragLoaded = data.frag; 6425 if (this.state === base_stream_controller_1.State.FRAG_LOADING && 6426 fragCurrent && 6427 fragLoaded.type === 'main' && 6428 fragLoaded.level === fragCurrent.level && 6429 fragLoaded.sn === fragCurrent.sn) { 6430 var stats = data.stats; 6431 var currentLevel = levels[fragCurrent.level]; 6432 var details = currentLevel.details; 6433 // reset frag bitrate test in any case after frag loaded event 6434 // if this frag was loaded to perform a bitrate test AND if hls.nextLoadLevel is greater than 0 6435 // then this means that we should be able to load a fragment at a higher quality level 6436 this.bitrateTest = false;
vendor: 4,179 bytes, lines 6437-6504
6437 this.stats = stats; 6438 logger_1.logger.log("Loaded " + fragCurrent.sn + " of [" + details.startSN + " ," + details.endSN + "],level " + fragCurrent.level); 6439 if (fragLoaded.bitrateTest && hls.nextLoadLevel) { 6440 // switch back to IDLE state ... we just loaded a fragment to determine adequate start bitrate and initialize autoswitch algo 6441 this.state = base_stream_controller_1.State.IDLE; 6442 this.startFragRequested = false; 6443 stats.tparsed = stats.tbuffered = window.performance.now(); 6444 hls.trigger(events_1.default.FRAG_BUFFERED, { stats: stats, frag: fragCurrent, id: 'main' }); 6445 this.tick(); 6446 } 6447 else if (fragLoaded.sn === 'initSegment') { 6448 this.state = base_stream_controller_1.State.IDLE; 6449 stats.tparsed = stats.tbuffered = window.performance.now(); 6450 details.initSegment.data = data.payload; 6451 hls.trigger(events_1.default.FRAG_BUFFERED, { stats: stats, frag: fragCurrent, id: 'main' }); 6452 this.tick(); 6453 } 6454 else { 6455 logger_1.logger.log("Parsing " + fragCurrent.sn + " of [" + details.startSN + " ," + details.endSN + "],level " + fragCurrent.level + ", cc " + fragCurrent.cc); 6456 this.state = base_stream_controller_1.State.PARSING; 6457 this.pendingBuffering = true; 6458 this.appended = false; 6459 // Bitrate test frags are not usually buffered so the fragment tracker ignores them. If Hls.js decides to buffer 6460 // it (and therefore ends up at this line), then the fragment tracker needs to be manually informed. 6461 if (fragLoaded.bitrateTest) { 6462 fragLoaded.bitrateTest = false; 6463 this.fragmentTracker.onFragLoaded({ 6464 frag: fragLoaded 6465 }); 6466 } 6467 // time Offset is accurate if level PTS is known, or if playlist is not sliding (not live) and if media is not seeking (this is to overcome potential timestamp drifts between playlists and fragments) 6468 var accurateTimeOffset = !(media && media.seeking) && (details.PTSKnown || !details.live); 6469 var initSegmentData = details.initSegment ? details.initSegment.data : []; 6470 var audioCodec = this._getAudioCodec(currentLevel); 6471 // transmux the MPEG-TS data to ISO-BMFF segments 6472 var demuxer = this.demuxer = this.demuxer || new demuxer_1.default(this.hls, 'main'); 6473 demuxer.push(data.payload, initSegmentData, audioCodec, currentLevel.videoCodec, fragCurrent, details.totalduration, accurateTimeOffset); 6474 } 6475 } 6476 this.fragLoadError = 0; 6477 }; 6478 StreamController.prototype.onFragParsingInitSegment = function (data) { 6479 var fragCurrent = this.fragCurrent; 6480 var fragNew = data.frag; 6481 if (fragCurrent && 6482 data.id === 'main' && 6483 fragNew.sn === fragCurrent.sn && 6484 fragNew.level === fragCurrent.level && 6485 this.state === base_stream_controller_1.State.PARSING) { 6486 var tracks = data.tracks, trackName = void 0, track = void 0; 6487 // if audio track is expected to come from audio stream controller, discard any coming from main 6488 if (tracks.audio && this.altAudio) { 6489 delete tracks.audio; 6490 } 6491 // include levelCodec in audio and video tracks 6492 track = tracks.audio; 6493 if (track) { 6494 var audioCodec = this.levels[this.level].audioCodec, ua = navigator.userAgent.toLowerCase(); 6495 if (audioCodec && this.audioCodecSwap) { 6496 logger_1.logger.log('swapping playlist audio codec'); 6497 if (audioCodec.indexOf('mp4a.40.5') !== -1) { 6498 audioCodec = 'mp4a.40.2'; 6499 } 6500 else { 6501 audioCodec = 'mp4a.40.5'; 6502 } 6503 } 6504 // in case AAC and HE-AAC audio codecs are signalled
6504in manifest 6505 // force HE-AAC , as it seems that most browsers prefers that way, 6506 // except for mono streams OR on FF 6507 // these conditions might need to be reviewed ... 6508 if (this.audioCodecSwitch) { 6509 // don't force HE-AAC if mono stream 6510 if (track.metadata.channelCount !== 1 && 6511 // don't force HE-AAC if firefox 6512 ua.indexOf('firefox') === -1) { 6513 audioCodec = 'mp4a.40.5'; 6514 } 6515 } 6516 // HE-AAC is broken on Android, always signal audio codec as AAC even if variant manifest states otherwise 6517 if (ua.indexOf('android') !== -1 && track.container !== 'audio/mpeg') { // Exclude mpeg audio 6518 audioCodec = 'mp4a.40.2'; 6519 logger_1.logger.log("Android: force audio codec to " + audioCodec); 6520 } 6521 track.levelCodec = audioCodec; 6522 track.id = data.id; 6523 } 6524 track = tracks.video; 6525 if (track) { 6526 track.levelCodec = this.levels[this.level].videoCodec; 6527 track.id = data.id; 6528 } 6529 this.hls.trigger(events_1.default.BUFFER_CODECS, tracks); 6530 // loop through tracks that are going to be provided to bufferController 6531 for (trackName in tracks) { 6532 track = tracks[trackName]; 6533 logger_1.logger.log("main track:" + trackName + ",container:" + track.container + ",codecs[level/parsed]=[" + track.levelCodec + "/" + track.codec + "]"); 6534 var initSegment = track.initSegment; 6535 if (initSegment) { 6536 this.appended = true; 6537 // arm pending Buffering flag before appending a segment 6538 this.pendingBuffering = true; 6539 this.hls.trigger(events_1.default.BUFFER_APPENDING, { type: trackName, data: initSegment, parent: 'main', content: 'initSegment' }); 6540 } 6541 } 6542 // trigger handler right now 6543 this.tick(); 6544 } 6545 }; 6546 StreamController.prototype.onFragParsingData = function (data) { 6547 var _this = this; 6548 var fragCurrent = this.fragCurrent; 6549 var fragNew = data.frag; 6550 if (fragCurrent && 6551 data.id === 'main' && 6552 fragNew.sn === fragCurrent.sn && 6553 fragNew.level === fragCurrent.level && 6554 !(data.type === 'audio' && this.altAudio) && // filter out main audio if audio track is loaded through audio stream controller 6555 this.state === base_stream_controller_1.State.PARSING) { 6556 var level = this.levels[this.level], frag = fragCurrent; 6557 if (!Number.isFinite(data.endPTS)) { 6558 data.endPTS = data.startPTS + fragCurrent.duration; 6559 data.endDTS = data.startDTS + fragCurrent.duration; 6560 } 6561 if (data.hasAudio === true) { 6562 frag.addElementaryStream(fragment_1.default.ElementaryStreamTypes.AUDIO); 6563 } 6564 if (data.hasVideo === true) { 6565 frag.addElementaryStream(fragment_1.default.ElementaryStreamTypes.VIDEO); 6566 } 6567 logger_1.logger.log("Parsed " + data.type + ",PTS:[" + data.startPTS.toFixed(3) + "," + data.endPTS.toFixed(3) + "],DTS:[" + data.startDTS.toFixed(3) + "/" + data.endDTS.toFixed(3) + "],nb:" + data.nb + ",dropped:" + (data.dropped || 0)); 6568 // Detect gaps in a fragment and try to fix it by finding a keyframe in the previous fragment (see _findFragments) 6569 if (data.type === 'video') { 6570 frag.dropped = data.dropped; 6571 if (frag.dropped) { 6572 if (!frag.backtracked) { 6573 var levelDetails = level.details; 6574 if (levelDetails && frag.sn === levelDetails.startSN) { 6575 logger_1.logger.warn('missing video frame(s) on first frag, appending with gap', frag.sn); 6576 } 6577 else { 6578 logger_1.logger.warn('missing video frame(s), backtracking fragment', frag.sn); 6579 // Return back to the IDLE state without appending to buffer 6580 // Causes findFragments to backtrack a segment and find the keyframe 6581 // Audio fragments arriving before video sets the nextLoadPosition, causing _findFragments to skip the backtracked fragment 6582 this.fragmentTracker.removeFragment(frag); 6583 frag.backtracked = true; 6584 this.nextLoadPosition = data.startPTS; 6585 this.state = base_stream_controller_1.State.IDLE; 6586 this.fragPrevious = frag; 6587 this.tick(); 6588 return; 6589 } 6590 } 6591 else { 6592 logger_1.logger.warn('Already backtracked on this fragment, appending with the gap', frag.sn); 6593 } 6594 } 6595 else { 6596 // Only reset the backtracked flag if we've loaded the frag without any dropped frames 6597 frag.backtracked = false;
6598 } 6599 } 6600 var drift = LevelHelper.updateFragPTSDTS(level.details, frag, data.startPTS, data.endPTS, data.startDTS, data.endDTS), hls_2 = this.hls; 6601 hls_2.trigger(events_1.default.LEVEL_PTS_UPDATED, { details: level.details, level: this.level, drift: drift, type: data.type, start: data.startPTS, end: data.endPTS }); 6602 // has remuxer dropped video frames located before first keyframe ? 6603 [data.data1, data.data2].forEach(function (buffer) { 6604 // only append in PARSING state (rationale is that an appending error could happen synchronously on first segment appending) 6605 // in that case it is useless to append following segments 6606 if (buffer && buffer.length && _this.state === base_stream_controller_1.State.PARSING) { 6607 _this.appended = true; 6608 // arm pending Buffering flag before appending a segment 6609 _this.pendingBuffering = true; 6610 hls_2.trigger(events_1.default.BUFFER_APPENDING, { type: data.type, data: buffer, parent: 'main', content: 'data' }); 6611 } 6612 }); 6613 // trigger handler right now 6614 this.tick(); 6615 } 6616 }; 6617 StreamController.prototype.onFragParsed = function (data) { 6618 var fragCurrent = this.fragCurrent; 6619 var fragNew = data.frag; 6620 if (fragCurrent && 6621 data.id === 'main' && 6622 fragNew.sn === fragCurrent.sn && 6623 fragNew.level === fragCurrent.level && 6624 this.state === base_stream_controller_1.State.PARSING) { 6625 this.stats.tparsed = window.performance.now(); 6626 this.state = base_stream_controller_1.State.PARSED; 6627 this._checkAppendedParsed(); 6628 } 6629 }; 6630 StreamController.prototype.onAudioTrackSwitching = function (data) { 6631 // if any URL found on new audio track, it is an alternate audio track 6632 var altAudio = !!data.url, trackId = data.id; 6633 // if we switch on main audio, ensure that main fragment scheduling is synced with media.buffered 6634 // don't do anything if we switch to alt audio: audio stream controller is handling it. 6635 // we will just have to change buffer scheduling on audioTrackSwitched 6636 if (!altAudio) { 6637 if (this.mediaBuffer !== this.media) { 6638 logger_1.logger.log('switching on main audio, use media.buffered to schedule main fragment loading'); 6639 this.mediaBuffer = this.media; 6640 var fragCurrent = this.fragCurrent; 6641 // we need to refill audio buffer from main: cancel any frag loading to speed up audio switch 6642 if (fragCurrent.loader) { 6643 logger_1.logger.log('switching to main audio track, cancel main fragment load'); 6644 fragCurrent.loader.abort(); 6645 } 6646 this.fragCurrent = null; 6647 this.fragPrevious = null; 6648 // destroy demuxer to force init segment generation (following audio switch) 6649 if (this.demuxer) { 6650 this.demuxer.destroy(); 6651 this.demuxer = null; 6652 } 6653 // switch to IDLE state to load new fragment 6654 this.state = base_stream_controller_1.State.IDLE; 6655 } 6656 var hls_3 = this.hls; 6657 // switching to main audio, flush all audio and trigger track switched 6658 hls_3.trigger(events_1.default.BUFFER_FLUSHING, { startOffset: 0, endOffset: Number.POSITIVE_INFINITY, type: 'audio' }); 6659 hls_3.trigger(events_1.default.AUDIO_TRACK_SWITCHED, { id: trackId }); 6660 this.altAudio = false; 6661 } 6662 }; 6663 StreamController.prototype.onAudioTrackSwitched = function (data) { 6664 var trackId = data.id, altAudio = !!this.hls.audioTracks[trackId].url; 6665 if (altAudio) { 6666 var videoBuffer = this.videoBuffer; 6667 // if we switched on alternate audio, ensure that main fragment scheduling is synced with video sourcebuffer buffered 6668 if (videoBuffer && this.mediaBuffer !== videoBuffer) { 6669 logger_1.logger.log('switching on alternate audio, use video.buffered to schedule main fragment loading'); 6670 this.mediaBuffer = videoBuffer; 6671 } 6672 } 6673 this.altAudio = altAudio; 6674 this.tick(); 6675 }; 6676 StreamController.prototype.onBufferCreated = function (data) { 6677 var tracks = data.tracks, mediaTrack, name, alternate = false;
6678 for (var type in tracks) { 6679 var track = tracks[type]; 6680 if (track.id === 'main') { 6681 name = type; 6682 mediaTrack = track; 6683 // keep video source buffer reference 6684 if (type === 'video') { 6685 this.videoBuffer = tracks[type].buffer; 6686 } 6687 } 6688 else { 6689 alternate = true; 6690 } 6691 } 6692 if (alternate && mediaTrack) { 6693 logger_1.logger.log("alternate track found, use " + name + ".buffered to schedule main fragment loading"); 6694 this.mediaBuffer = mediaTrack.buffer; 6695 } 6696 else { 6697 this.mediaBuffer = this.media; 6698 } 6699 }; 6700 StreamController.prototype.onBufferAppended = function (data) { 6701 if (data.parent === 'main') { 6702 var state = this.state; 6703 if (state === base_stream_controller_1.State.PARSING || state === base_stream_controller_1.State.PARSED) { 6704 // check if all buffers have been appended 6705 this.pendingBuffering = (data.pending > 0); 6706 this._checkAppendedParsed(); 6707 } 6708 } 6709 }; 6710 StreamController.prototype._checkAppendedParsed = function () { 6711 // trigger handler right now 6712 if (this.state === base_stream_controller_1.State.PARSED && (!this.appended || !this.pendingBuffering)) { 6713 var frag = this.fragCurrent; 6714 if (frag) { 6715 var media = this.mediaBuffer ? this.mediaBuffer : this.media; 6716 logger_1.logger.log("main buffered : " + time_ranges_1.default.toString(media.buffered)); 6717 this.fragPrevious = frag; 6718 var stats = this.stats; 6719 stats.tbuffered = window.performance.now(); 6720 // we should get rid of this.fragLastKbps 6721 this.fragLastKbps = Math.round(8 * stats.total / (stats.tbuffered - stats.tfirst)); 6722 this.hls.trigger(events_1.default.FRAG_BUFFERED, { stats: stats, frag: frag, id: 'main' }); 6723 this.state = base_stream_controller_1.State.IDLE; 6724 } 6725 this.tick(); 6726 } 6727 }; 6728 StreamController.prototype.onError = function (data) { 6729 var frag = data.frag || this.fragCurrent; 6730 // don't handle frag error not related to main fragment 6731 if (frag && frag.type !== 'main') { 6732 return; 6733 } 6734 // 0.5 : tolerance needed as some browsers stalls playback before reaching buffered end 6735 var mediaBuffered = !!this.media && buffer_helper_1.BufferHelper.isBuffered(this.media, this.media.currentTime) && buffer_helper_1.BufferHelper.isBuffered(this.media, this.media.currentTime + 0.5); 6736 switch (data.details) { 6737 case errors_1.ErrorDetails.FRAG_LOAD_ERROR: 6738 case errors_1.ErrorDetails.FRAG_LOAD_TIMEOUT: 6739 case errors_1.ErrorDetails.KEY_LOAD_ERROR: 6740 case errors_1.ErrorDetails.KEY_LOAD_TIMEOUT: 6741 if (!data.fatal) { 6742 // keep retrying until the limit will be reached 6743 if ((this.fragLoadError + 1) <= this.config.fragLoadingMaxRetry) { 6744 // exponential backoff capped to config.fragLoadingMaxRetryTimeout 6745 var delay = Math.min(Math.pow(2, this.fragLoadError) * this.config.fragLoadingRetryDelay, this.config.fragLoadingMaxRetryTimeout); 6746 logger_1.logger.warn("mediaController: frag loading failed, retry in " + delay + " ms"); 6747 this.retryDate = window.performance.now() + delay; 6748 // retry loading state 6749 // if loadedmetadata is not set, it means that we are emergency switch down on first frag 6750 // in that case, reset startFragRequested flag 6751 if (!this.loadedmetadata) { 6752 this.startFragRequested = false; 6753 this.nextLoadPosition = this.startPosition; 6754 } 6755 this.fragLoadError++; 6756 this.state = base_stream_controller_1.State.FRAG_LOADING_WAITING_RETRY; 6757 } 6758 else { 6759 logger_1.logger.error("mediaController: " + data.details + " reaches max retry, redispatch as fatal ..."); 6760 // switch error to fatal 6761 data.fatal = true; 6762 this.state = base_stream_controller_1.State.ERROR; 6763 } 6764 } 6765 break; 6766 case errors_1.ErrorDetails.LEVEL_LOAD_ERROR: 6767 case errors_1.ErrorDetails.LEVEL_LOAD_TIMEOUT: 6768 if (this.state !== base_stream_controller_1.State.ERROR) { 6769 if (data.fatal) { 6770 // if fatal error, stop processing 6771 this.state = base_stream_controller_1.State.ERROR; 6772 logger_1.logger.warn("streamController: " + data.details + ",switch to " + this.state + " state ..."); 6773 } 6774 else { 6775 // in case of non fatal error while loading level, if level controller is not retrying to load level , switch back to IDLE 6776 if (!data.levelRetry && this.state === base_stream_controller_1.State.WAITING_LEVEL) { 6777 this.state = base_stream_controller_1.State.IDLE; 6778 } 6779 } 6780 } 6781 break; 6782 case errors_1.ErrorDetails.BUFFER_FULL_ERROR: 6783 // if in appending state 6784 if (data.parent === 'main' && (this.state === base_stream_controller_1.State.PARSING || this.state === base_stream_controller_1.State.PARSED)) { 6785 // reduce max buf len if current position is buffered 6786 if (mediaBuffered) { 6787 this._reduceMaxBufferLength(this.config.maxBufferLength); 6788 this.state = base_stream_controller_1.State.IDLE; 6789 } 6790 else { 6791 // current position is not buffered, but browser is still complaining about buffer full error 6792 // this happens on IE/Edge, refer to https://github.com/video-dev/hls.js/pull/708 6793 // in that case flush the whole buffer to recover 6794 logger_1.logger.warn('buffer full error also media.currentTime is not buffered, flush everything'); 6795 this.fragCurrent = null; 6796 // flush everything 6797 this.flushMainBuffer(0, Number.POSITIVE_INFINITY); 6798 } 6799 } 6800 break; 6801 default: 6802 break; 6803 } 6804 }; 6805 StreamController.prototype._reduceMaxBufferLength = function (minLength) { 6806 var config = this.config;
vendor: 4,572 bytes, lines 6807-6900
6807 if (config.maxMaxBufferLength >= minLength) { 6808 // reduce max buffer length as it might be too high. we do this to avoid loop flushing ... 6809 config.maxMaxBufferLength /= 2; 6810 logger_1.logger.warn("main:reduce max buffer length to " + config.maxMaxBufferLength + "s"); 6811 return true; 6812 } 6813 return false; 6814 }; 6815 /** 6816 * Checks the health of the buffer and attempts to resolve playback stalls. 6817 * @private 6818 */ 6819 StreamController.prototype._checkBuffer = function () { 6820 var media = this.media; 6821 if (!media || media.readyState === 0) { 6822 // Exit early if we don't have media or if the media hasn't bufferd anything yet (readyState 0) 6823 return; 6824 } 6825 var mediaBuffer = this.mediaBuffer ? this.mediaBuffer : media; 6826 var buffered = mediaBuffer.buffered; 6827 if (!this.loadedmetadata && buffered.length) { 6828 this.loadedmetadata = true; 6829 this._seekToStartPos(); 6830 } 6831 else if (this.immediateSwitch) { 6832 this.immediateLevelSwitchEnd(); 6833 } 6834 else { 6835 this.gapController.poll(this.lastCurrentTime, buffered); 6836 } 6837 }; 6838 StreamController.prototype.onFragLoadEmergencyAborted = function () { 6839 this.state = base_stream_controller_1.State.IDLE; 6840 // if loadedmetadata is not set, it means that we are emergency switch down on first frag 6841 // in that case, reset startFragRequested flag 6842 if (!this.loadedmetadata) { 6843 this.startFragRequested = false; 6844 this.nextLoadPosition = this.startPosition; 6845 } 6846 this.tick(); 6847 }; 6848 StreamController.prototype.onBufferFlushed = function () { 6849 /* after successful buffer flushing, filter flushed fragments from bufferedFrags 6850 use mediaBuffered instead of media (so that we will check against video.buffered ranges in case of alt audio track) 6851 */ 6852 var media = this.mediaBuffer ? this.mediaBuffer : this.media; 6853 if (media) { 6854 // filter fragments potentially evicted from buffer. this is to avoid memleak on live streams 6855 this.fragmentTracker.detectEvictedFragments(fragment_1.default.ElementaryStreamTypes.VIDEO, media.buffered); 6856 } 6857 // move to IDLE once flush complete. this should trigger new fragment loading 6858 this.state = base_stream_controller_1.State.IDLE; 6859 // reset reference to frag 6860 this.fragPrevious = null; 6861 }; 6862 StreamController.prototype.swapAudioCodec = function () { 6863 this.audioCodecSwap = !this.audioCodecSwap; 6864 }; 6865 StreamController.prototype.computeLivePosition = function (sliding, levelDetails) { 6866 var targetLatency = this.config.liveSyncDuration !== undefined ? this.config.liveSyncDuration : this.config.liveSyncDurationCount * levelDetails.targetduration; 6867 return sliding + Math.max(0, levelDetails.totalduration - targetLatency); 6868 }; 6869 /** 6870 * Seeks to the set startPosition if not equal to the mediaElement's current time. 6871 * @private 6872 */ 6873 StreamController.prototype._seekToStartPos = function () { 6874 var media = this.media; 6875 var currentTime = media.currentTime; 6876 // only adjust currentTime if different from startPosition or if startPosition not buffered 6877 // at that stage, there should be only one buffered range, as we reach that code after first fragment has been buffered 6878 var startPosition = media.seeking ? currentTime : this.startPosition; 6879 // if currentTime not matching with expected startPosition or startPosition not buffered but close to first buffered 6880 if (currentTime !== startPosition) { 6881 // if startPosition not buffered, let's seek to buffered.start(0) 6882 logger_1.logger.log("target start position not buffered, seek to buffered.start(0) " + startPosition + " from current time " + currentTime + " "); 6883 media.currentTime = startPosition; 6884 } 6885 }; 6886 StreamController.prototype._getAudioCodec = function (currentLevel) { 6887 var audioCodec = this.config.defaultAudioCodec || currentLevel.audioCodec; 6888 if (this.audioCodecSwap) { 6889 logger_1.logger.log('swapping playlist audio codec'); 6890 if (audioCodec) { 6891 if (audioCodec.indexOf('mp4a.40.5') !== -1) { 6892 audioCodec = 'mp4a.40.2'; 6893 } 6894 else { 6895 audioCodec = 'mp4a.40.5'; 6896 } 6897 } 6898 } 6899 return audioCodec; 6900 };
6901 Object.defineProperty(StreamController.prototype, "liveSyncPosition", { 6902 get: function () { 6903 return this._liveSyncPosition; 6904 }, 6905 set: function (value) { 6906 this._liveSyncPosition = value; 6907 }, 6908 enumerable: true, 6909 configurable: true 6910 }); 6911 return StreamController; 6912}(base_stream_controller_1.default)); 6913exports.default = StreamController; 6914 6915/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 6916 6917/***/ }), 6918
6919/***/ "./src/controller/subtitle-stream-controller.js": 6920/*!******************************************************!*\ 6921 !*** ./src/controller/subtitle-stream-controller.js ***! 6922 \******************************************************/ 6923/*! no static exports found */ 6924/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 6925/***/ (function(module, exports, __webpack_require__) { 6926 6927"use strict"; 6928 6929/** 6930 * @class SubtitleStreamController 6931 */ 6932var __extends = (this && this.__extends) || (function () { 6933 var extendStatics = Object.setPrototypeOf || 6934 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 6935 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 6936 return function (d, b) { 6937 extendStatics(d, b); 6938 function __() { this.constructor = d; } 6939 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 6940 }; 6941})(); 6942Object.defineProperty(exports, "__esModule", { value: true }); 6943var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 6944var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 6945var decrypter_1 = __webpack_require__(/*! ../crypt/decrypter */ "./src/crypt/decrypter.js"); 6946var buffer_helper_1 = __webpack_require__(/*! ../utils/buffer-helper */ "./src/utils/buffer-helper.js"); 6947var fragment_finders_1 = __webpack_require__(/*! ./fragment-finders */ "./src/controller/fragment-finders.js"); 6948var fragment_tracker_1 = __webpack_require__(/*! ./fragment-tracker */ "./src/controller/fragment-tracker.js"); 6949var base_stream_controller_1 = __webpack_require__(/*! ./base-stream-controller */ "./src/controller/base-stream-controller.js"); 6950var level_helper_1 = __webpack_require__(/*! ./level-helper */ "./src/controller/level-helper.js"); 6951var performance = window.performance; 6952var TICK_INTERVAL = 500; // how often to tick in ms 6953var SubtitleStreamController = /** @class */ (function (_super) { 6954 __extends(SubtitleStreamController, _super); 6955 function SubtitleStreamController(hls, fragmentTracker) { 6956 var _this = _super.call(this, hls, events_1.default.MEDIA_ATTA
6956CHED, events_1.default.MEDIA_DETACHING, events_1.default.ERROR, events_1.default.KEY_LOADED, events_1.default.FRAG_LOADED, events_1.default.SUBTITLE_TRACKS_UPDATED, events_1.default.SUBTITLE_TRACK_SWITCH, events_1.default.SUBTITLE_TRACK_LOADED, events_1.default.SUBTITLE_FRAG_PROCESSED, events_1.default.LEVEL_UPDATED) || this; 6957 _this.fragmentTracker = fragmentTracker; 6958 _this.config = hls.config; 6959 _this.state = base_stream_controller_1.State.STOPPED; 6960 _this.tracks = []; 6961 _this.tracksBuffered = []; 6962 _this.currentTrackId = -1; 6963 _this.decrypter = new decrypter_1.default(hls, hls.config); 6964 // lastAVStart stores the time in seconds for the start time of a level load 6965 _this.lastAVStart = 0; 6966 _this._onMediaSeeking = _this.onMediaSeeking.bind(_this); 6967 return _this; 6968 } 6969 SubtitleStreamController.prototype.onSubtitleFragProcessed = function (data) { 6970 var frag = data.frag, success = data.success; 6971 this.fragPrevious = frag; 6972 this.state = base_stream_controller_1.State.IDLE; 6973 if (!success) { 6974 return; 6975 } 6976 var buffered = this.tracksBuffered[this.currentTrackId]; 6977 if (!buffered) { 6978 return; 6979 } 6980 // Create/update a buffered array matching the interface used by BufferHelper.bufferedInfo 6981 // so we can re-use the logic used to detect how much have been buffered 6982 var timeRange; 6983 var fragStart = frag.start; 6984 for (var i = 0; i < buffered.length; i++) { 6985 if (fragStart >= buffered[i].start && fragStart <= buffered[i].end) { 6986 timeRange = buffered[i]; 6987 break; 6988 } 6989 } 6990 var fragEnd = frag.start + frag.duration; 6991 if (timeRange) { 6992 timeRange.end = fragEnd; 6993 } 6994 else { 6995 timeRange = { 6996 start: fragStart, 6997 end: fragEnd 6998 }; 6999 buffered.push(timeRange); 7000 } 7001 }; 7002 SubtitleStreamController.prototype.onMediaAttached = function (_a) { 7003 var media = _a.media; 7004 this.media = media; 7005 media.addEventListener('seeking', this._onMediaSeeking); 7006 this.state = base_stream_controller_1.State.IDLE; 7007 }; 7008 SubtitleStreamController.prototype.onMediaDetaching = function () { 7009 this.media.removeEventListener('seeking', this._onMediaSeeking); 7010 this.media = null; 7011 this.state = base_stream_controller_1.State.STOPPED; 7012 }; 7013 // If something goes wrong, proceed to next frag, if we were processing one. 7014 SubtitleStreamController.prototype.onError = function (data) { 7015 var frag = data.frag; 7016 // don't handle error not related to subtitle fragment 7017 if (!frag || frag.type !== 'subtitle') { 7018 return; 7019 } 7020 this.state = base_stream_controller_1.State.IDLE; 7021 }; 7022 // Got all new subtitle tracks. 7023 SubtitleStreamController.prototype.onSubtitleTracksUpdated = function (data) { 7024 var _this = this; 7025 logger_1.logger.log('subtitle tracks updated'); 7026 this.tracksBuffered = []; 7027 this.tracks = data.subtitleTracks; 7028 this.tracks.forEach(function (track) { 7029 _this.tracksBuffered[track.id] = []; 7030 }); 7031 }; 7032 SubtitleStreamController.prototype.onSubtitleTrackSwitch = function (data) { 7033 this.currentTrackId = data.id; 7034 if (!this.tracks || this.currentTrackId === -1) { 7035 this.clearInterval(); 7036 return; 7037 } 7038 // Check if track has the necessary details to load fragments 7039 var currentTrack = this.tracks[this.currentTrackId]; 7040 if (currentTrack && currentTrack.details) { 7041 this.setInterval(TICK_INTERVAL); 7042 } 7043 }; 7044 // Got a new set of subtitle fragments. 7045 SubtitleStreamController.prototype.onSubtitleTrackLoaded = function (data) { 7046 var id = data.id, details = data.details; 7047 var _a = this, currentTrackId = _a.currentTrackId, tracks = _a.tracks; 7048 var currentTrack = tracks[currentTrackId]; 7049 if (id >= tracks.length || id !== currentTrackId || !currentTrack) { 7050 return; 7051 } 7052 if (details.live) { 7053 level_helper_1.mergeSubtitlePlaylists(currentTrack.details, details, this.lastAVStart); 7054 } 7055 currentTrack.details = details; 7056 this.setInterval(TICK_INTERVAL); 7057 }; 7058 SubtitleStreamController.prototype.onKeyLoaded = function () { 7059 if (this.state === base_stream_controller_1.State.KEY_LOADING) { 7060 this.state = base_stream_controller_1.State.IDLE; 7061 } 7062 }; 7063 SubtitleStreamController.prototype.onFragLoaded = function (data) { 7064 var fragCurrent = this.fragCurrent; 7065 var decryptData = data.frag.decryptdata; 7066 var fragLoaded = data.frag; 7067 var hls = this.hls; 7068 if (this.state === base_stream_controller_1.State.FRAG_LOADING && 7069 fragCurrent && 7070 data.frag.type === 'subtitle' && 7071 fragCurrent.sn === data.frag.sn) { 7072 // check to see if the payload needs to be decrypted 7073 if (data.payload.byteLength > 0 && (decryptData && decryptData.key && decryptData.method === 'AES-128')) { 7074 var startTime_1 = performance.now(); 7075 // decrypt the subtitles 7076 this.decrypter.decrypt(data.payload, decryptData.key.buffer, decryptData.iv.buffer, function (decryptedData) { 7077 var endTime = performance.now(); 7078 hls.trigger(events_1.default.FRAG_DECRYPTED, { frag: fragLoaded, payload: decryptedData, stats: { tstart: startTime_1, tdecrypt: endTime } }); 7079 }); 7080 } 7081 } 7082 }; 7083 SubtitleStreamController.prototype.onLevelUpdated = function (_a) { 7084 var details = _a.details; 7085 var frags = details.fragments; 7086 this.lastAVStart = frags.length ? frags[0].start : 0; 7087 }; 7088 SubtitleStreamController.prototype.doTick = function () { 7089 if (!this.media) { 7090 this.state = base_stream_controller_1.State.IDLE; 7091 return; 7092 } 7093 switch (this.state) { 7094 case base_stream_controller_1.State.IDLE: {
7095 var _a = this, config = _a.config, currentTrackId = _a.currentTrackId, fragmentTracker = _a.fragmentTracker, media = _a.media, tracks = _a.tracks; 7096 if (!tracks || !tracks[currentTrackId] || !tracks[currentTrackId].details) { 7097 break; 7098 } 7099 var maxBufferHole = config.maxBufferHole, maxFragLookUpTolerance = config.maxFragLookUpTolerance; 7100 var maxConfigBuffer = Math.min(config.maxBufferLength, config.maxMaxBufferLength); 7101 var bufferedInfo = buffer_helper_1.BufferHelper.bufferedInfo(this._getBuffered(), media.currentTime, maxBufferHole); 7102 var bufferEnd = bufferedInfo.end, bufferLen = bufferedInfo.len; 7103 var trackDetails = tracks[currentTrackId].details; 7104 var fragments = trackDetails.fragments; 7105 var fragLen = fragments.length; 7106 var end = fragments[fragLen - 1].start + fragments[fragLen - 1].duration; 7107 if (bufferLen > maxConfigBuffer) { 7108 return; 7109 } 7110 var foundFrag = void 0; 7111 var fragPrevious = this.fragPrevious; 7112 if (bufferEnd < end) { 7113 if (fragPrevious && trackDetails.hasProgramDateTime) { 7114 foundFrag = fragment_finders_1.findFragmentByPDT(fragments, fragPrevious.endProgramDateTime, maxFragLookUpTolerance); 7115 } 7116 if (!foundFrag) { 7117 foundFrag = fragment_finders_1.findFragmentByPTS(fragPrevious, fragments, bufferEnd, maxFragLookUpTolerance); 7118 } 7119 } 7120 else { 7121 foundFrag = fragments[fragLen - 1]; 7122 } 7123 if (foundFrag && foundFrag.encrypted) { 7124 logger_1.logger.log("Loading key for " + foundFrag.sn); 7125 this.state = base_stream_controller_1.State.KEY_LOADING; 7126 this.hls.trigger(events_1.default.KEY_LOADING, { frag: foundFrag }); 7127 } 7128 else if (foundFrag && fragmentTracker.getState(foundFrag) === fragment_tracker_1.FragmentState.NOT_LOADED) { 7129 // only load if fragment is not loaded 7130 this.fragCurrent = foundFrag; 7131 this.state = base_stream_controller_1.State.FRAG_LOADING; 7132 this.hls.trigger(events_1.default.FRAG_LOADING, { frag: foundFrag }); 7133 } 7134 } 7135 } 7136 }; 7137 SubtitleStreamController.prototype.stopLoad = function () { 7138 this.lastAVStart = 0; 7139 _super.prototype.stopLoad.call(this); 7140 }; 7141 SubtitleStreamController.prototype._getBuffered = function () { 7142 return this.tracksBuffered[this.currentTrackId] || []; 7143 }; 7144 SubtitleStreamController.prototype.onMediaSeeking = function () { 7145 this.fragPrevious = null; 7146 }; 7147 return SubtitleStreamController; 7148}(base_stream_controller_1.default)); 7149exports.SubtitleStreamController = SubtitleStreamController; 7150 7151 7152/***/ }), 7153
7154/***/ "./src/controller/subtitle-track-controller.js": 7155/*!*****************************************************!*\ 7156 !*** ./src/controller/subtitle-track-controller.js ***! 7157 \*****************************************************/ 7158/*! no static exports found */ 7159/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 7160/***/ (function(module, exports, __webpack_require__) { 7161 7162"use strict"; 7163/* WEBPACK VAR INJECTION */(function(Number) { 7164var __extends = (this && this.__extends) || (function () { 7165 var extendStatics = Object.setPrototypeOf || 7166 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 7167 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 7168 return function (d, b) { 7169 extendStatics(d, b); 7170 function __() { this.constructor = d; } 7171 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 7172 }; 7173})(); 7174Object.defineProperty(exports, "__esModule", { value: true }); 7175var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 7176var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 7177var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 7178var level_helper_1 = __webpack_require__(/*! ./level-helper */ "./src/controller/level-helper.js"); 7179var SubtitleTrackController = /** @class */ (function (_super) { 7180 __extends(SubtitleTrackController, _super); 7181 function SubtitleTrackController(hls) { 7182 var _this = _super.call(this, hls, events_1.default.MEDIA_ATTA
7182CHED, events_1.default.MEDIA_DETACHING, events_1.default.MANIFEST_LOADED, events_1.default.SUBTITLE_TRACK_LOADED) || this; 7183 _this.tracks = []; 7184 _this.trackId = -1; 7185 _this.media = null; 7186 _this.stopped = true; 7187 /** 7188 * @member {boolean} subtitleDisplay Enable/disable subtitle display rendering 7189 */ 7190 _this.subtitleDisplay = true; 7191 return _this; 7192 } 7193 SubtitleTrackController.prototype.destroy = function () { 7194 event_handler_1.default.prototype.destroy.call(this); 7195 }; 7196 // Listen for subtitle track change, then extract the current track ID. 7197 SubtitleTrackController.prototype.onMediaAttached = function (data) { 7198 var _this = this; 7199 this.media = data.media; 7200 if (!this.media) { 7201 return; 7202 } 7203 if (this.queuedDefaultTrack) { 7204 this.subtitleTrack = this.queuedDefaultTrack; 7205 delete this.queuedDefaultTrack; 7206 } 7207 this.trackChangeListener = this._onTextTracksChanged.bind(this); 7208 this.useTextTrackPolling = !(this.media.textTracks && 'onchange' in this.media.textTracks); 7209 if (this.useTextTrackPolling) { 7210 this.subtitlePollingInterval = setInterval(function () { 7211 _this.trackChangeListener(); 7212 }, 500); 7213 } 7214 else { 7215 this.media.textTracks.addEventListener('change', this.trackChangeListener); 7216 } 7217 }; 7218 SubtitleTrackController.prototype.onMediaDetaching = function () { 7219 if (!this.media) { 7220 return; 7221 } 7222 if (this.useTextTrackPolling) { 7223 clearInterval(this.subtitlePollingInterval); 7224 } 7225 else { 7226 this.media.textTracks.removeEventListener('change', this.trackChangeListener); 7227 } 7228 this.media = null; 7229 }; 7230 // Fired whenever a new manifest is loaded. 7231 SubtitleTrackController.prototype.onManifestLoaded = function (data) { 7232 var _this = this; 7233 var tracks = data.subtitles || []; 7234 this.tracks = tracks; 7235 this.hls.trigger(events_1.default.SUBTITLE_TRACKS_UPDATED, { subtitleTracks: tracks }); 7236 // loop through available subtitle tracks and autoselect default if needed 7237 // TODO: improve selection logic to handle forced, etc 7238 tracks.forEach(function (track) { 7239 if (track.default) { 7240 // setting this.subtitleTrack will trigger internal logic 7241 // if media has not been attached yet, it will fail 7242 // we keep a reference to the default track id 7243 // and we'll set subtitleTrack when onMediaAttached is triggered 7244 if (_this.media) { 7245 _this.subtitleTrack = track.id; 7246 } 7247 else { 7248 _this.queuedDefaultTrack = track.id; 7249 } 7250 } 7251 }); 7252 }; 7253 SubtitleTrackController.prototype.onSubtitleTrackLoaded = function (data) { 7254 var _this = this; 7255 var id = data.id, details = data.details; 7256 var _a = this, trackId = _a.trackId, tracks = _a.tracks; 7257 var currentTrack = tracks[trackId]; 7258 if (id >= tracks.length || id !== trackId || !currentTrack || this.stopped) { 7259 this._clearReloadTimer(); 7260 return; 7261 } 7262 logger_1.logger.log("subtitle track " + id + " loaded"); 7263 if (details.live) { 7264 var reloadInterval = level_helper_1.computeReloadInterval(currentTrack.details, details, data.stats.trequest); 7265 logger_1.logger.log("Reloading live subtitle playlist in " + reloadInterval + "ms"); 7266 this.timer = setTimeout(function () { 7267 _this._loadCurrentTrack(); 7268 }, reloadInterval); 7269 } 7270 else { 7271 this._clearReloadTimer(); 7272 } 7273 }; 7274 SubtitleTrackController.prototype.startLoad = function () { 7275 this.stopped = false; 7276 this._loadCurrentTrack(); 7277 }; 7278 SubtitleTrackController.prototype.stopLoad = function () { 7279 this.stopped = true; 7280 this._clearReloadTimer(); 7281 };
7282 Object.defineProperty(SubtitleTrackController.prototype, "subtitleTracks", { 7283 /** get alternate subtitle tracks list from playlist **/ 7284 get: function () { 7285 return this.tracks; 7286 }, 7287 enumerable: true, 7288 configurable: true 7289 }); 7290 Object.defineProperty(SubtitleTrackController.prototype, "subtitleTrack", { 7291 /** get index of the selected subtitle track (index in subtitle track lists) **/ 7292 get: function () { 7293 return this.trackId; 7294 }, 7295 /** select a subtitle track, based on its index in subtitle track lists**/ 7296 set: function (subtitleTrackId) { 7297 if (this.trackId !== subtitleTrackId) { 7298 this._toggleTrackModes(subtitleTrackId); 7299 this._setSubtitleTrackInternal(subtitleTrackId); 7300 } 7301 }, 7302 enumerable: true, 7303 configurable: true 7304 }); 7305 SubtitleTrackController.prototype._clearReloadTimer = function () { 7306 if (this.timer) { 7307 clearTimeout(this.timer); 7308 this.timer = null; 7309 } 7310 }; 7311 SubtitleTrackController.prototype._loadCurrentTrack = function () { 7312 var _a = this, trackId = _a.trackId, tracks = _a.tracks, hls = _a.hls; 7313 var currentTrack = tracks[trackId]; 7314 if (trackId < 0 || !currentTrack || (currentTrack.details && !currentTrack.details.live)) { 7315 return; 7316 } 7317 logger_1.logger.log("Loading subtitle track " + trackId); 7318 hls.trigger(events_1.default.SUBTITLE_TRACK_LOADING, { url: currentTrack.url, id: trackId }); 7319 }; 7320 /** 7321 * Disables the old subtitleTrack and sets current mode on the next subtitleTrack. 7322 * This operates on the DOM textTracks. 7323 * A value of -1 will disable all subtitle tracks. 7324 * @param newId - The id of the next track to enable 7325 * @private 7326 */ 7327 SubtitleTrackController.prototype._toggleTrackModes = function (newId) { 7328 var _a = this, media = _a.media, subtitleDisplay = _a.subtitleDisplay, trackId = _a.trackId; 7329 if (!media) { 7330 return; 7331 } 7332 var textTracks = filterSubtitleTracks(media.textTracks); 7333 if (newId === -1) {
vendor: 2,603 bytes, lines 7334-7402
7334 [].slice.call(textTracks).forEach(function (track) { 7335 track.mode = 'disabled'; 7336 }); 7337 } 7338 else { 7339 var oldTrack = textTracks[trackId]; 7340 if (oldTrack) { 7341 oldTrack.mode = 'disabled'; 7342 } 7343 } 7344 var nextTrack = textTracks[newId]; 7345 if (nextTrack) { 7346 nextTrack.mode = subtitleDisplay ? 'showing' : 'hidden'; 7347 } 7348 }; 7349 /** 7350 * This method is responsible for validating the subtitle index and periodically reloading if live. 7351 * Dispatches the SUBTITLE_TRACK_SWITCH event, which instructs the subtitle-stream-controller to load the selected track. 7352 * @param newId - The id of the subtitle track to activate. 7353 */ 7354 SubtitleTrackController.prototype._setSubtitleTrackInternal = function (newId) { 7355 var _a = this, hls = _a.hls, tracks = _a.tracks; 7356 if (!Number.isFinite(newId) || newId < -1 || newId >= tracks.length) { 7357 return; 7358 } 7359 this.trackId = newId; 7360 logger_1.logger.log("Switching to subtitle track " + newId); 7361 hls.trigger(events_1.default.SUBTITLE_TRACK_SWITCH, { id: newId }); 7362 this._loadCurrentTrack(); 7363 }; 7364 SubtitleTrackController.prototype._onTextTracksChanged = function () { 7365 // Media is undefined when switching streams via loadSource() 7366 if (!this.media) { 7367 return; 7368 } 7369 var trackId = -1; 7370 var tracks = filterSubtitleTracks(this.media.textTracks); 7371 for (var id = 0; id < tracks.length; id++) { 7372 if (tracks[id].mode === 'hidden') { 7373 // Do not break in case there is a following track with showing. 7374 trackId = id; 7375 } 7376 else if (tracks[id].mode === 'showing') { 7377 trackId = id; 7378 break; 7379 } 7380 } 7381 // Setting current subtitleTrack will invoke code. 7382 this.subtitleTrack = trackId; 7383 }; 7384 return SubtitleTrackController; 7385}(event_handler_1.default)); 7386function filterSubtitleTracks(textTrackList) { 7387 var tracks = []; 7388 for (var i = 0; i < textTrackList.length; i++) { 7389 var track = textTrackList[i]; 7390 // Edge adds a track without a label; we don't want to use it 7391 if (track.kind === 'subtitles' && track.label) { 7392 tracks.push(textTrackList[i]); 7393 } 7394 } 7395 return tracks; 7396} 7397exports.default = SubtitleTrackController; 7398 7399/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 7400 7401/***/ }), 7402
7403/***/ "./src/controller/timeline-controller.js": 7404/*!***********************************************!*\ 7405 !*** ./src/controller/timeline-controller.js ***! 7406 \***********************************************/ 7407/*! no static exports found */ 7408/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 7409/***/ (function(module, exports, __webpack_require__) { 7410 7411"use strict"; 7412/* WEBPACK VAR INJECTION */(function(Number) { 7413/* 7414 * Timeline Controller 7415*/ 7416var __extends = (this && this.__extends) || (function () { 7417 var extendStatics = Object.setPrototypeOf || 7418 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 7419 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 7420 return function (d, b) { 7421 extendStatics(d, b); 7422 function __() { this.constructor = d; } 7423 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 7424 }; 7425})(); 7426Object.defineProperty(exports, "__esModule", { value: true }); 7427var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 7428var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 7429var cea_608_parser_1 = __webpack_require__(/*! ../utils/cea-608-parser */ "./src/utils/cea-608-parser.js"); 7430var output_filter_1 = __webpack_require__(/*! ../utils/output-filter */ "./src/utils/output-filter.js"); 7431var webvtt_parser_1 = __webpack_require__(/*! ../utils/webvtt-parser */ "./src/utils/webvtt-parser.js"); 7432var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 7433var texttrack_utils_1 = __webpack_require__(/*! ../utils/texttrack-utils */ "./src/utils/texttrack-utils.js"); 7434function canReuseVttTextTrack(inUseTrack, manifestTrack) { 7435 return inUseTrack && inUseTrack.label === manifestTrack.name && !(inUseTrack.textTrack1 || inUseTrack.textTrack2); 7436} 7437function intersection(x1, x2, y1, y2) { 7438 return Math.min(x2, y2) - Math.max(x1, y1); 7439} 7440var TimelineController = /** @class */ (function (_super) { 7441 __extends(TimelineController, _super); 7442 function TimelineController(hls) { 7443 var _this = _super.call(this, hls, events_1.default.MEDIA_ATTA
7443CHING, events_1.default.MEDIA_DETACHING, events_1.default.FRAG_PARSING_USERDATA, events_1.default.FRAG_DECRYPTED, events_1.default.MANIFEST_LOADING, events_1.default.MANIFEST_LOADED, events_1.default.FRAG_LOADED, events_1.default.LEVEL_SWITCHING, events_1.default.INIT_PTS_FOUND) || this; 7444 _this.hls = hls; 7445 _this.config = hls.config; 7446 _this.enabled = true; 7447 _this.Cues = hls.config.cueHandler; 7448 _this.textTracks = []; 7449 _this.tracks = []; 7450 _this.unparsedVttFrags = []; 7451 _this.initPTS = []; 7452 _this.cueRanges = []; 7453 _this.captionsTracks = {}; 7454 _this.captionsProperties = { 7455 textTrack1: { 7456 label: _this.config.captionsTextTrack1Label, 7457 languageCode: _this.config.captionsTextTrack1LanguageCode 7458 }, 7459 textTrack2: { 7460 label: _this.config.captionsTextTrack2Label, 7461 languageCode: _this.config.captionsTextTrack2LanguageCode 7462 } 7463 }; 7464 if (_this.config.enableCEA708Captions) { 7465 var channel1 = new output_filter_1.default(_this, 'textTrack1'); 7466 var channel2 = new output_filter_1.default(_this, 'textTrack2'); 7467 _this.cea608Parser = new cea_608_parser_1.default(0, channel1, channel2); 7468 } 7469 return _this; 7470 } 7471 TimelineController.prototype.addCues = function (trackName, startTime, endTime, screen) { 7472 // skip cues which overlap more than 50% with previously parsed time ranges 7473 var ranges = this.cueRanges; 7474 var merged = false; 7475 for (var i = ranges.length; i--;) { 7476 var cueRange = ranges[i]; 7477 var overlap = intersection(cueRange[0], cueRange[1], startTime, endTime); 7478 if (overlap >= 0) { 7479 cueRange[0] = Math.min(cueRange[0], startTime); 7480 cueRange[1] = Math.max(cueRange[1], endTime); 7481 merged = true; 7482 if ((overlap / (endTime - startTime)) > 0.5) { 7483 return; 7484 } 7485 } 7486 } 7487 if (!merged) { 7488 ranges.push([startTime, endTime]); 7489 } 7490 this.Cues.newCue(this.captionsTracks[trackName], startTime, endTime, screen); 7491 }; 7492 // Triggered when an initial PTS is found; used for synchronisation of WebVTT. 7493 TimelineController.prototype.onInitPtsFound = function (data) { 7494 var _this = this; 7495 if (data.id === 'main') { 7496 this.initPTS[data.frag.cc] = data.initPTS; 7497 } 7498 // Due to asynchronous processing, initial PTS may arrive later than the first VTT fragments are loaded. 7499 // Parse any unparsed fragments upon receiving the initial PTS. 7500 if (this.unparsedVttFrags.length) { 7501 var unparsedVttFrags = this.unparsedVttFrags; 7502 this.unparsedVttFrags = []; 7503 unparsedVttFrags.forEach(function (frag) { 7504 _this.onFragLoaded(frag); 7505 }); 7506 } 7507 }; 7508 TimelineController.prototype.getExistingTrack = function (trackName) { 7509 var media = this.media; 7510 if (media) { 7511 for (var i = 0; i < media.textTracks.length; i++) { 7512 var textTrack = media.textTracks[i]; 7513 if (textTrack[trackName]) { 7514 return textTrack; 7515 } 7516 } 7517 } 7518 return null; 7519 }; 7520 TimelineController.prototype.createCaptionsTrack = function (trackName) { 7521 var _a = this.captionsProperties[trackName], label = _a.label, languageCode = _a.languageCode; 7522 var captionsTracks = this.captionsTracks; 7523 if (!captionsTracks[trackName]) { 7524 // Enable reuse of existing text track. 7525 var existingTrack = this.getExistingTrack(trackName); 7526 if (!existingTrack) { 7527 var textTrack = this.createTextTrack('captions', label, languageCode); 7528 if (textTrack) { 7529 // Set a special property on the track so we know it's managed by Hls.js 7530 textTrack[trackName] = true; 7531 captionsTracks[trackName] = textTrack; 7532 } 7533 } 7534 else { 7535 captionsTracks[trackName] = existingTrack; 7536 texttrack_utils_1.clearCurrentCues(captionsTracks[trackName]); 7537 texttrack_utils_1.sendAddTrackEvent(captionsTracks[trackName], this.media); 7538 } 7539 } 7540 }; 7541 TimelineController.prototype.createTextTrack = function (kind, label, lang) { 7542 var media = this.media; 7543 if (media) { 7544 return media.addTextTrack(kind, label, lang); 7545 } 7546 }; 7547 TimelineController.prototype.destroy = function () { 7548 event_handler_1.default.prototype.destroy.call(this); 7549 }; 7550 TimelineController.prototype.onMediaAttaching = function (data) { 7551 this.media = data.media; 7552 this._cleanTracks(); 7553 }; 7554 TimelineController.prototype.onMediaDetaching = function () { 7555 var captionsTracks = this.captionsTracks;
7556 Object.keys(captionsTracks).forEach(function (trackName) { 7557 texttrack_utils_1.clearCurrentCues(captionsTracks[trackName]); 7558 delete captionsTracks[trackName]; 7559 }); 7560 }; 7561 TimelineController.prototype.onManifestLoading = function () { 7562 this.lastSn = -1; // Detect discontiguity in fragment parsing 7563 this.prevCC = -1; 7564 this.vttCCs = { 7565 ccOffset: 0, 7566 presentationOffset: 0, 7567 0: { 7568 start: 0, prevCC: -1, new: false 7569 } 7570 }; 7571 this._cleanTracks(); 7572 }; 7573 TimelineController.prototype._cleanTracks = function () { 7574 // clear outdated subtitles 7575 var media = this.media; 7576 if (media) { 7577 var textTracks = media.textTracks; 7578 if (textTracks) { 7579 for (var i = 0; i < textTracks.length; i++) { 7580 texttrack_utils_1.clearCurrentCues(textTracks[i]); 7581 } 7582 } 7583 } 7584 }; 7585 TimelineController.prototype.onManifestLoaded = function (data) { 7586 var _this = this; 7587 this.textTracks = []; 7588 this.unparsedVttFrags = this.unparsedVttFrags || []; 7589 this.initPTS = []; 7590 this.cueRanges = []; 7591 if (this.config.enableWebVTT) { 7592 this.tracks = data.subtitles || []; 7593 var inUseTracks_1 = this.media ? this.media.textTracks : []; 7594 this.tracks.forEach(function (track, index) { 7595 var textTrack; 7596 if (index < inUseTracks_1.length) { 7597 var inUseTrack = null; 7598 for (var i = 0; i < inUseTracks_1.length; i++) { 7599 if (canReuseVttTextTrack(inUseTracks_1[i], track)) { 7600 inUseTrack = inUseTracks_1[i]; 7601 break; 7602 } 7603 } 7604 // Reuse tracks with the same label, but do not reuse 608/708 tracks 7605 if (inUseTrack) { 7606 textTrack = inUseTrack; 7607 } 7608 } 7609 if (!textTrack) { 7610 textTrack = _this.createTextTrack('subtitles', track.name, track.lang); 7611 } 7612 if (track.default) { 7613 textTrack.mode = _this.hls.subtitleDisplay ? 'showing' : 'hidden'; 7614 } 7615 else { 7616 textTrack.mode = 'disabled'; 7617 } 7618 _this.textTracks.push(textTrack); 7619 }); 7620 } 7621 }; 7622 TimelineController.prototype.onLevelSwitching = function () { 7623 this.enabled = this.hls.currentLevel.closedCaptions !== 'NONE'; 7624 }; 7625 TimelineController.prototype.onFragLoaded = function (data) { 7626 var frag = data.frag, payload = data.payload; 7627 if (frag.type === 'main') { 7628 var sn = frag.sn; 7629 // if this frag isn't contiguous, clear the parser so cues with bad start/end times aren't added to the textTrack 7630 if (sn !== this.lastSn + 1) { 7631 var cea608Parser = this.cea608Parser; 7632 if (cea608Parser) { 7633 cea608Parser.reset(); 7634 } 7635 } 7636 this.lastSn = sn; 7637 } // eslint-disable-line brace-style 7638 // If fragment is subtitle type, parse as WebVTT. 7639 else if (frag.type === 'subtitle') { 7640 if (payload.byteLength) { 7641 // We need an initial synchronisation PTS. Store fragments as long as none has arrived. 7642 if (!Number.isFinite(this.initPTS[frag.cc])) { 7643 this.unparsedVttFrags.push(data); 7644 if (this.initPTS.length) { 7645 // finish unsuccessfully, otherwise the subtitle-stream-controller could be blocked from loading new frags. 7646 this.hls.trigger(events_1.default.SUBTITLE_FRAG_PROCESSED, { success: false, frag: frag }); 7647 } 7648 return; 7649 } 7650 var decryptData = frag.decryptdata; 7651 // If the subtitles are not encrypted, parse VTTs now. Otherwise, we need to wait. 7652 if ((decryptData == null) || (decryptData.key == null) || (decryptData.method !== 'AES-128')) { 7653 this._parseVTTs(frag, payload); 7654 } 7655 } 7656 else { 7657 // In case there is no payload, finish unsuccessfully. 7658 this.hls.trigger(events_1.default.SUBTITLE_FRAG_PROCESSED, { success: false, frag: frag }); 7659 } 7660 } 7661 }; 7662 TimelineController.prototype._parseVTTs = function (frag, payload) { 7663 var vttCCs = this.vttCCs; 7664 if (!vttCCs[frag.cc]) { 7665 vttCCs[frag.cc] = { start: frag.start, prevCC: this.prevCC, new: true }; 7666 this.prevCC = frag.cc; 7667 } 7668 var textTracks = this.textTracks, hls = this.hls; 7669 // Parse the WebVTT file contents. 7670 webvtt_parser_1.default.parse(payload, this.initPTS[frag.cc], vttCCs, frag.cc, function (cues) { 7671 var currentTrack = textTracks[frag.level]; 7672 // WebVTTParser.parse is an async method and if the currently selected text track mode is set to "disabled" 7673 // before parsing is done then don't try to access currentTrack.cues.getCueById as cues will be null 7674 // and trying to access getCueById method of cues will throw an exception 7675 if (currentTrack.mode === 'disabled') { 7676 hls.trigger(events_1.default.SUBTITLE_FRAG_PROCESSED, { success: false, frag: frag }); 7677 return; 7678 } 7679 // Add cues and trigger event with success true.
7680 cues.forEach(function (cue) { 7681 // Sometimes there are cue overlaps on segmented vtts so the same 7682 // cue can appear more than once in different vtt files. 7683 // This avoid showing duplicated cues with same timecode and text. 7684 if (!currentTrack.cues.getCueById(cue.id)) { 7685 try { 7686 currentTrack.addCue(cue); 7687 } 7688 catch (err) { 7689 var textTrackCue = new window.TextTrackCue(cue.startTime, cue.endTime, cue.text); 7690 textTrackCue.id = cue.id; 7691 currentTrack.addCue(textTrackCue); 7692 } 7693 } 7694 }); 7695 hls.trigger(events_1.default.SUBTITLE_FRAG_PROCESSED, { success: true, frag: frag }); 7696 }, function (e) { 7697 // Something went wrong while parsing. Trigger event with success false. 7698 logger_1.logger.log("Failed to parse VTT cue: " + e); 7699 hls.trigger(events_1.default.SUBTITLE_FRAG_PROCESSED, { success: false, frag: frag }); 7700 }); 7701 }; 7702 TimelineController.prototype.onFragDecrypted = function (data) { 7703 var decryptedData = data.payload, frag = data.frag; 7704 if (frag.type === 'subtitle') { 7705 if (!Number.isFinite(this.initPTS[frag.cc])) { 7706 this.unparsedVttFrags.push(data); 7707 return; 7708 } 7709 this._parseVTTs(frag, decryptedData); 7710 } 7711 }; 7712 TimelineController.prototype.onFragParsingUserdata = function (data) { 7713 // push all of the CEA-708 messages into the interpreter 7714 // immediately. It will create the proper timestamps based on our PTS value 7715 if (this.enabled && this.config.enableCEA708Captions) { 7716 for (var i = 0; i < data.samples.length; i++) { 7717 var ccdatas = this.extractCea608Data(data.samples[i].bytes); 7718 this.cea608Parser.addData(data.samples[i].pts, ccdatas); 7719 } 7720 } 7721 }; 7722 TimelineController.prototype.extractCea608Data = function (byteArray) { 7723 var count = byteArray[0] & 31; 7724 var position = 2; 7725 var tmpByte, ccbyte1, ccbyte2, ccValid, ccType; 7726 var actualCCBytes = []; 7727 for (var j = 0; j < count; j++) { 7728 tmpByte = byteArray[position++]; 7729 ccbyte1 = 0x7F & byteArray[position++]; 7730 ccbyte2 = 0x7F & byteArray[position++]; 7731 ccValid = (4 & tmpByte) !== 0; 7732 ccType = 3 & tmpByte; 7733 if (ccbyte1 === 0 && ccbyte2 === 0) { 7734 continue; 7735 } 7736 if (ccValid) { 7737 if (ccType === 0) { // || ccType === 1 7738 actualCCBytes.push(ccbyte1); 7739 actualCCBytes.push(ccbyte2); 7740 } 7741 } 7742 } 7743 return actualCCBytes; 7744 }; 7745 return TimelineController; 7746}(event_handler_1.default)); 7747exports.default = TimelineController; 7748 7749/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 7750 7751/***/ }), 7752
7753/***/ "./src/crypt/aes-crypto.js": 7754/*!*********************************!*\ 7755 !*** ./src/crypt/aes-crypto.js ***! 7756 \*********************************/ 7757/*! no static exports found */ 7758/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 7759/***/ (function(module, exports, __webpack_require__) { 7760 7761"use strict"; 7762 7763Object.defineProperty(exports, "__esModule", { value: true }); 7764var AESCrypto = /** @class */ (function () { 7765 function AESCrypto(subtle, iv) { 7766 this.subtle = subtle; 7767 this.aesIV = iv; 7768 } 7769 AESCrypto.prototype.decrypt = function (data, key) { 7770 return this.subtle.decrypt({ name: 'AES-CBC', iv: this.aesIV }, key, data); 7771 }; 7772 return AESCrypto; 7773}()); 7774exports.default = AESCrypto; 7775 7776 7777/***/ }), 7778
7779/***/ "./src/crypt/aes-decryptor.js": 7780/*!************************************!*\ 7781 !*** ./src/crypt/aes-decryptor.js ***! 7782 \************************************/ 7783/*! no static exports found */ 7784/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 7785/***/ (function(module, exports, __webpack_require__) { 7786 7787"use strict"; 7788 7789Object.defineProperty(exports, "__esModule", { value: true }); 7790// PKCS7 7791function removePadding(buffer) { 7792 var outputBytes = buffer.byteLength; 7793 var paddingBytes = outputBytes && (new DataView(buffer)).getUint8(outputBytes - 1); 7794 if (paddingBytes) { 7795 return buffer.slice(0, outputBytes - paddingBytes); 7796 } 7797 else { 7798 return buffer; 7799 } 7800} 7801exports.removePadding = removePadding; 7802var AESDecryptor = /** @class */ (function () { 7803 function AESDecryptor() { 7804 // Static after running initTable 7805 this.rcon = [0x0, 0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36]; 7806 this.subMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]; 7807 this.invSubMix = [new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256)]; 7808 this.sBox = new Uint32Array(256); 7809 this.invSBox = new Uint32Array(256); 7810 // Changes during runtime 7811 this.key = new Uint32Array(0); 7812 this.initTable(); 7813 } 7814 // Using view.getUint32() also swaps the byte order. 7815 AESDecryptor.prototype.uint8ArrayToUint32Array_ = function (arrayBuffer) { 7816 var view = new DataView(arrayBuffer); 7817 var newArray = new Uint32Array(4); 7818 for (var i = 0; i < 4; i++) { 7819 newArray[i] = view.getUint32(i * 4); 7820 } 7821 return newArray; 7822 }; 7823 AESDecryptor.prototype.initTable = function () { 7824 var sBox = this.sBox; 7825 var invSBox = this.invSBox; 7826 var subMix = this.subMix; 7827 var subMix0 = subMix[0]; 7828 var subMix1 = subMix[1]; 7829 var subMix2 = subMix[2]; 7830 var subMix3 = subMix[3]; 7831 var invSubMix = this.invSubMix; 7832 var invSubMix0 = invSubMix[0]; 7833 var invSubMix1 = invSubMix[1]; 7834 var invSubMix2 = invSubMix[2]; 7835 var invSubMix3 = invSubMix[3]; 7836 var d = new Uint32Array(256); 7837 var x = 0; 7838 var xi = 0; 7839 var i = 0; 7840 for (i = 0; i < 256; i++) { 7841 if (i < 128) { 7842 d[i] = i << 1; 7843 } 7844 else { 7845 d[i] = (i << 1) ^ 0x11b; 7846 } 7847 } 7848 for (i = 0; i < 256; i++) { 7849 var sx = xi ^ (xi << 1) ^ (xi << 2) ^ (xi << 3) ^ (xi << 4); 7850 sx = (sx >>> 8) ^ (sx & 0xff) ^ 0x63; 7851 sBox[x] = sx; 7852 invSBox[sx] = x; 7853 // Compute multiplication 7854 var x2 = d[x]; 7855 var x4 = d[x2]; 7856 var x8 = d[x4]; 7857 // Compute sub/invSub bytes, mix columns tables 7858 var t = (d[sx] * 0x101) ^ (sx * 0x1010100); 7859 subMix0[x] = (t << 24) | (t >>> 8); 7860 subMix1[x] = (t << 16) | (t >>> 16); 7861 subMix2[x] = (t << 8) | (t >>> 24); 7862 subMix3[x] = t; 7863 // Compute inv sub bytes, inv mix columns tables 7864 t = (x8 * 0x1010101) ^ (x4 * 0x10001) ^ (x2 * 0x101) ^ (x * 0x1010100); 7865 invSubMix0[sx] = (t << 24) | (t >>> 8); 7866 invSubMix1[sx] = (t << 16) | (t >>> 16); 7867 invSubMix2[sx] = (t << 8) | (t >>> 24); 7868 invSubMix3[sx] = t; 7869 // Compute next counter 7870 if (!x) { 7871 x = xi = 1; 7872 } 7873 else { 7874 x = x2 ^ d[d[d[x8 ^ x2]]]; 7875 xi ^= d[d[xi]]; 7876 } 7877 } 7878 }; 7879 AESDecryptor.prototype.expandKey = function (keyBuffer) { 7880 // convert keyBuffer to Uint32Array 7881 var key = this.uint8ArrayToUint32Array_(keyBuffer); 7882 var sameKey = true; 7883 var offset = 0; 7884 while (offset < key.length && sameKey) { 7885 sameKey = (key[offset] === this.key[offset]); 7886 offset++; 7887 } 7888 if (sameKey) { 7889 return; 7890 } 7891 this.key = key;
7892 var keySize = this.keySize = key.length; 7893 if (keySize !== 4 && keySize !== 6 && keySize !== 8) { 7894 throw new Error('Invalid aes key size=' + keySize); 7895 } 7896 var ksRows = this.ksRows = (keySize + 6 + 1) * 4; 7897 var ksRow; 7898 var invKsRow; 7899 var keySchedule = this.keySchedule = new Uint32Array(ksRows); 7900 var invKeySchedule = this.invKeySchedule = new Uint32Array(ksRows); 7901 var sbox = this.sBox; 7902 var rcon = this.rcon; 7903 var invSubMix = this.invSubMix; 7904 var invSubMix0 = invSubMix[0]; 7905 var invSubMix1 = invSubMix[1]; 7906 var invSubMix2 = invSubMix[2]; 7907 var invSubMix3 = invSubMix[3]; 7908 var prev; 7909 var t; 7910 for (ksRow = 0; ksRow < ksRows; ksRow++) { 7911 if (ksRow < keySize) { 7912 prev = keySchedule[ksRow] = key[ksRow]; 7913 continue; 7914 } 7915 t = prev; 7916 if (ksRow % keySize === 0) { 7917 // Rot word 7918 t = (t << 8) | (t >>> 24); 7919 // Sub word 7920 t = (sbox[t >>> 24] << 24) | (sbox[(t >>> 16) & 0xff] << 16) | (sbox[(t >>> 8) & 0xff] << 8) | sbox[t & 0xff]; 7921 // Mix Rcon 7922 t ^= rcon[(ksRow / keySize) | 0] << 24; 7923 } 7924 else if (keySize > 6 && ksRow % keySize === 4) { 7925 // Sub word 7926 t = (sbox[t >>> 24] << 24) | (sbox[(t >>> 16) & 0xff] << 16) | (sbox[(t >>> 8) & 0xff] << 8) | sbox[t & 0xff]; 7927 } 7928 keySchedule[ksRow] = prev = (keySchedule[ksRow - keySize] ^ t) >>> 0; 7929 } 7930 for (invKsRow = 0; invKsRow < ksRows; invKsRow++) { 7931 ksRow = ksRows - invKsRow; 7932 if (invKsRow & 3) { 7933 t = keySchedule[ksRow]; 7934 } 7935 else { 7936 t = keySchedule[ksRow - 4]; 7937 } 7938 if (invKsRow < 4 || ksRow <= 4) { 7939 invKeySchedule[invKsRow] = t; 7940 } 7941 else { 7942 invKeySchedule[invKsRow] = invSubMix0[sbox[t >>> 24]] ^ invSubMix1[sbox[(t >>> 16) & 0xff]] ^ invSubMix2[sbox[(t >>> 8) & 0xff]] ^ invSubMix3[sbox[t & 0xff]]; 7943 } 7944 invKeySchedule[invKsRow] = invKeySchedule[invKsRow] >>> 0; 7945 } 7946 }; 7947 // Adding this as a method greatly improves performance. 7948 AESDecryptor.prototype.networkToHostOrderSwap = function (word) { 7949 return (word << 24) | ((word & 0xff00) << 8) | ((word & 0xff0000) >> 8) | (word >>> 24); 7950 }; 7951 AESDecryptor.prototype.decrypt = function (inputArrayBuffer, offset, aesIV, removePKCS7Padding) { 7952 var nRounds = this.keySize + 6; 7953 var invKeySchedule = this.invKeySchedule; 7954 var invSBOX = this.invSBox; 7955 var invSubMix = this.invSubMix; 7956 var invSubMix0 = invSubMix[0]; 7957 var invSubMix1 = invSubMix[1]; 7958 var invSubMix2 = invSubMix[2]; 7959 var invSubMix3 = invSubMix[3]; 7960 var initVector = this.uint8ArrayToUint32Array_(aesIV); 7961 var initVector0 = initVector[0]; 7962 var initVector1 = initVector[1]; 7963 var initVector2 = initVector[2]; 7964 var initVector3 = initVector[3]; 7965 var inputInt32 = new Int32Array(inputArrayBuffer); 7966 var outputInt32 = new Int32Array(inputInt32.length); 7967 var t0, t1, t2, t3; 7968 var s0, s1, s2, s3; 7969 var inputWords0, inputWords1, inputWords2, inputWords3; 7970 var ksRow, i; 7971 var swapWord = this.networkToHostOrderSwap; 7972 while (offset < inputInt32.length) { 7973 inputWords0 = swapWord(inputInt32[offset]); 7974 inputWords1 = swapWord(inputInt32[offset + 1]); 7975 inputWords2 = swapWord(inputInt32[offset + 2]); 7976 inputWords3 = swapWord(inputInt32[offset + 3]); 7977 s0 = inputWords0 ^ invKeySchedule[0]; 7978 s1 = inputWords3 ^ invKeySchedule[1]; 7979 s2 = inputWords2 ^ invKeySchedule[2]; 7980 s3 = inputWords1 ^ invKeySchedule[3]; 7981 ksRow = 4; 7982 // Iterate through the rounds of decryption 7983 for (i = 1; i < nRounds; i++) { 7984 t0 = invSubMix0[s0 >>> 24] ^ invSubMix1[(s1 >> 16) & 0xff] ^ invSubMix2[(s2 >> 8) & 0xff] ^ invSubMix3[s3 & 0xff] ^ invKeySchedule[ksRow]; 7985 t1 = invSubMix0[s1 >>> 24] ^ invSubMix1[(s2 >> 16) & 0xff] ^ invSubMix2[(s3 >> 8) & 0xff] ^ invSubMix3[s0 & 0xff] ^ invKeySchedule[ksRow + 1]; 7986 t2 = invSubMix0[s2 >>> 24] ^ invSubMix1[(s3 >> 16) & 0xff] ^ invSubMix2[(s0 >> 8) & 0xff] ^ invSubMix3[s1 & 0xff] ^ invKeySchedule[ksRow + 2]; 7987 t3 = invSubMix0[s3 >>> 24] ^ invSubMix1[(s0 >> 16) & 0xff] ^ invSubMix2[(s1 >> 8) & 0xff] ^ invSubMix3[s2 & 0xff] ^ invKeySchedule[ksRow + 3]; 7988 // Update state 7989 s0 = t0; 7990 s1 = t1; 7991 s2 = t2; 7992 s3 = t3; 7993 ksRow = ksRow + 4; 7994 } 7995 // Shift rows, sub bytes, add round key 7996 t0 = ((invSBOX[s0 >>> 24] << 24) ^ (invSBOX[(s1 >> 16) & 0xff] << 16) ^ (invSBOX[(s2 >> 8) & 0xff] << 8) ^ invSBOX[s3 & 0xff]) ^ invKeySchedule[ksRow]; 7997 t1 = ((invSBOX[s1 >>> 24] << 24) ^ (invSBOX[(s2 >> 16) & 0xff] << 16) ^ (invSBOX[(s3 >> 8) & 0xff] << 8) ^ invSBOX[s0 & 0xff]) ^ invKeySchedule[ksRow + 1]; 7998 t2 = ((invSBOX[s2 >>> 24] << 24) ^ (invSBOX[(s3 >> 16) & 0xff] << 16) ^ (invSBOX[(s0 >> 8) & 0xff] << 8) ^ invSBOX[s1 & 0xff]) ^ invKeySchedule[ksRow + 2]; 7999 t3 = ((invSBOX[s3 >>> 24] << 24) ^ (invSBOX[(s0 >> 16) & 0xff] << 16) ^ (invSBOX[(s1 >> 8) & 0xff] << 8) ^ invSBOX[s2 & 0xff]) ^ invKeySchedule[ksRow + 3]; 8000 ksRow = ksRow + 3; 8001 // Write 8002 outputInt32[offset] = swapWord(t0 ^ initVector0); 8003 outputInt32[offset + 1] = swapWord(t3 ^ initVector1); 8004 outputInt32[offset + 2] = swapWord(t2 ^ initVector2); 8005 outputInt32[offset + 3] = swapWord(t1 ^ initVector3); 8006 // reset initVector to last 4 unsigned int 8007 initVector0 = inputWords0; 8008 initVector1 = inputWords1; 8009 initVector2 = inputWords2; 8010 initVector3 = inputWords3; 8011 offset = offset + 4; 8012 } 8013 return removePKCS7Padding ? removePadding(outputInt32.buffer) : outputInt32.buffer; 8014 }; 8015 AESDecryptor.prototype.destroy = function () { 8016 this.key = undefined; 8017 this.keySize = undefined; 8018 this.ksRows = undefined; 8019 this.sBox = undefined; 8020 this.invSBox = undefined; 8021 this.subMix = undefined; 8022 this.invSubMix = undefined; 8023 this.keySchedule = undefined; 8024 this.invKeySchedule = undefined; 8025 this.rcon = undefined; 8026 }; 8027 return AESDecryptor; 8028}()); 8029exports.default = AESDecryptor; 8030 8031 8032/***/ }), 8033
8034/***/ "./src/crypt/decrypter.js": 8035/*!********************************!*\ 8036 !*** ./src/crypt/decrypter.js ***! 8037 \********************************/ 8038/*! no static exports found */ 8039/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 8040/***/ (function(module, exports, __webpack_require__) { 8041 8042"use strict"; 8043 8044Object.defineProperty(exports, "__esModule", { value: true }); 8045var aes_crypto_1 = __webpack_require__(/*! ./aes-crypto */ "./src/crypt/aes-crypto.js"); 8046var fast_aes_key_1 = __webpack_require__(/*! ./fast-aes-key */ "./src/crypt/fast-aes-key.js"); 8047var aes_decryptor_1 = __webpack_require__(/*! ./aes-decryptor */ "./src/crypt/aes-decryptor.js"); 8048var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 8049var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 8050var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 8051var get_self_scope_1 = __webpack_require__(/*! ../utils/get-self-scope */ "./src/utils/get-self-scope.js"); 8052// see https://stackoverflow.com/a/11237259/589493 8053var global = get_self_scope_1.getSelfScope(); // safeguard for code that might run both on worker and main thread 8054var Decrypter = /** @class */ (function () { 8055 function Decrypter(observer, config, _a) { 8056 var _b = (_a === void 0 ? {} : _a).removePKCS7Padding, removePKCS7Padding = _b === void 0 ? true : _b; 8057 this.logEnabled = true; 8058 this.observer = observer; 8059 this.config = config; 8060 this.removePKCS7Padding = removePKCS7Padding; 8061 // built in decryptor expects PKCS7 padding 8062 if (removePKCS7Padding) { 8063 try { 8064 var browserCrypto = global.crypto; 8065 if (browserCrypto) { 8066 this.subtle = browserCrypto.subtle || browserCrypto.webkitSubtle; 8067 } 8068 } 8069 catch (e) { } 8070 } 8071 this.disableWebCrypto = !this.subtle; 8072 } 8073 Decrypter.prototype.isSync = function () { 8074 return (this.disableWebCrypto && this.config.enableSoftwareAES); 8075 }; 8076 Decrypter.prototype.decrypt = function (data, key, iv, callback) { 8077 var _this = this; 8078 if (this.disableWebCrypto && this.config.enableSoftwareAES) { 8079 if (this.logEnabled) { 8080 logger_1.logger.log('JS AES decrypt'); 8081 this.logEnabled = false; 8082 } 8083 var decryptor = this.decryptor; 8084 if (!decryptor) { 8085 this.decryptor = decryptor = new aes_decryptor_1.default(); 8086 } 8087 decryptor.expandKey(key); 8088 callback(decryptor.decrypt(data, 0, iv, this.removePKCS7Padding)); 8089 } 8090 else { 8091 if (this.logEnabled) { 8092 logger_1.logger.log('WebCrypto AES decrypt'); 8093 this.logEnabled = false; 8094 } 8095 var subtle_1 = this.subtle; 8096 if (this.key !== key) { 8097 this.key = key; 8098 this.fastAesKey = new fast_aes_key_1.default(subtle_1, key); 8099 } 8100 this.fastAesKey.expandKey() 8101 .then(function (aesKey) { 8102 // decrypt using web crypto 8103 var crypto = new aes_crypto_1.default(subtle_1, iv); 8104 crypto.decrypt(data, aesKey) 8105 .catch(function (err) { 8106 _this.onWebCryptoError(err, data, key, iv, callback); 8107 }) 8108 .then(function (result) { 8109 callback(result); 8110 }); 8111 }) 8112 .catch(function (err) { 8113 _this.onWebCryptoError(err, data, key, iv, callback); 8114 }); 8115 } 8116 }; 8117 Decrypter.prototype.onWebCryptoError = function (err, data, key, iv, callback) { 8118 if (this.config.enableSoftwareAES) { 8119 logger_1.logger.log('WebCrypto Error, disable WebCrypto API'); 8120 this.disableWebCrypto = true; 8121 this.logEnabled = true; 8122 this.decrypt(data, key, iv, callback); 8123 } 8124 else { 8125 logger_1.logger.error("decrypting error : " + err.message); 8126 this.observer.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.MEDIA_ERROR, details: errors_1.ErrorDetails.FRAG_DECRYPT_ERROR, fatal: true, reason: err.message }); 8127 } 8128 }; 8129 Decrypter.prototype.destroy = function () { 8130 var decryptor = this.decryptor; 8131 if (decryptor) {
8132 decryptor.destroy(); 8133 this.decryptor = undefined; 8134 } 8135 }; 8136 return Decrypter; 8137}()); 8138exports.default = Decrypter; 8139 8140 8141/***/ }), 8142
8143/***/ "./src/crypt/fast-aes-key.js": 8144/*!***********************************!*\ 8145 !*** ./src/crypt/fast-aes-key.js ***! 8146 \***********************************/ 8147/*! no static exports found */ 8148/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 8149/***/ (function(module, exports, __webpack_require__) { 8150 8151"use strict"; 8152 8153Object.defineProperty(exports, "__esModule", { value: true }); 8154var FastAESKey = /** @class */ (function () { 8155 function FastAESKey(subtle, key) { 8156 this.subtle = subtle; 8157 this.key = key; 8158 } 8159 FastAESKey.prototype.expandKey = function () { 8160 return this.subtle.importKey('raw', this.key, { name: 'AES-CBC' }, false, ['encrypt', 'decrypt']); 8161 }; 8162 return FastAESKey; 8163}()); 8164exports.default = FastAESKey; 8165 8166 8167/***/ }), 8168
8169/***/ "./src/demux/aacdemuxer.js": 8170/*!*********************************!*\ 8171 !*** ./src/demux/aacdemuxer.js ***! 8172 \*********************************/ 8173/*! no static exports found */ 8174/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 8175/***/ (function(module, exports, __webpack_require__) { 8176 8177"use strict"; 8178/* WEBPACK VAR INJECTION */(function(Number) { 8179Object.defineProperty(exports, "__esModule", { value: true }); 8180/** 8181 * AAC demuxer 8182 */ 8183var ADTS = __webpack_require__(/*! ./adts */ "./src/demux/adts.js"); 8184var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 8185var id3_1 = __webpack_require__(/*! ../demux/id3 */ "./src/demux/id3.js"); 8186var AACDemuxer = /** @class */ (function () { 8187 function AACDemuxer(observer, remuxer, config) { 8188 this.observer = observer; 8189 this.config = config; 8190 this.remuxer = remuxer; 8191 } 8192 AACDemuxer.prototype.resetInitSegment = function (initSegment, audioCodec, videoCodec, duration) { 8193 this._audioTrack = { container: 'audio/adts', type: 'audio', id: 0, sequenceNumber: 0, isAAC: true, samples: [], len: 0, manifestCodec: audioCodec, duration: duration, inputTimeScale: 90000 }; 8194 }; 8195 AACDemuxer.prototype.resetTimeStamp = function () { 8196 }; 8197 AACDemuxer.probe = function (data) { 8198 if (!data) { 8199 return false; 8200 } 8201 // Check for the ADTS sync word 8202 // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1 8203 // Layer bits (position 14 and 15) in header should be always 0 for ADTS 8204 // More info https://wiki.multimedia.cx/index.php?title=ADTS 8205 var id3Data = id3_1.default.getID3Data(data, 0) || []; 8206 var offset = id3Data.length; 8207 for (var length_1 = data.length; offset < length_1; offset++) { 8208 if (ADTS.probe(data, offset)) { 8209 logger_1.logger.log('ADTS sync word found !'); 8210 return true; 8211 } 8212 } 8213 return false; 8214 }; 8215 // feed incoming data to the front of the parsing pipeline 8216 AACDemuxer.prototype.append = function (data, timeOffset, contiguous, accurateTimeOffset) { 8217 var track = this._audioTrack; 8218 var id3Data = id3_1.default.getID3Data(data, 0) || []; 8219 var timestamp = id3_1.default.getTimeStamp(id3Data); 8220 var pts = Number.isFinite(timestamp) ? timestamp * 90 : timeOffset * 90000; 8221 var frameIndex = 0; 8222 var stamp = pts; 8223 var length = data.length; 8224 var offset = id3Data.length; 8225 var id3Samples = [{ pts: stamp, dts: stamp, data: id3Data }]; 8226 while (offset < length - 1) { 8227 if (ADTS.isHeader(data, offset) && (offset + 5) < length) { 8228 ADTS.initTrackConfig(track, this.observer, data, offset, track.manifestCodec); 8229 var frame = ADTS.appendFrame(track, data, offset, pts, frameIndex); 8230 if (frame) { 8231 offset += frame.length; 8232 stamp = frame.sample.pts; 8233 frameIndex++; 8234 } 8235 else { 8236 logger_1.logger.log('Unable to parse AAC frame'); 8237 break; 8238 } 8239 } 8240 else if (id3_1.default.isHeader(data, offset)) { 8241 id3Data = id3_1.default.getID3Data(data, offset); 8242 id3Samples.push({ pts: stamp, dts: stamp, data: id3Data }); 8243 offset += id3Data.length; 8244 } 8245 else { 8246 // nothing found, keep looking 8247 offset++; 8248 } 8249 } 8250 this.remuxer.remux(track, { samples: [] }, { samples: id3Samples, inputTimeScale: 90000 }, { samples: [] }, timeOffset, contiguous, accurateTimeOffset); 8251 }; 8252 AACDemuxer.prototype.destroy = function () { 8253 }; 8254 return AACDemuxer; 8255}()); 8256exports.default = AACDemuxer; 8257 8258/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 8259 8260/***/ }), 8261
8262/***/ "./src/demux/adts.js": 8263/*!***************************!*\ 8264 !*** ./src/demux/adts.js ***! 8265 \***************************/ 8266/*! no static exports found */ 8267/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 8268/***/ (function(module, exports, __webpack_require__) { 8269 8270"use strict"; 8271 8272Object.defineProperty(exports, "__esModule", { value: true }); 8273/** 8274 * ADTS parser helper 8275 */ 8276var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 8277var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 8278var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 8279function getAudioConfig(observer, data, offset, audioCodec) { 8280 var adtsObjectType, // :int 8281 adtsSampleingIndex, // :int 8282 adtsExtensionSampleingIndex, // :int 8283 adtsChanelConfig, // :int 8284 config, userAgent = navigator.userAgent.toLowerCase(), manifestCodec = audioCodec, adtsSampleingRates = [ 8285 96000, 88200, 8286 64000, 48000, 8287 44100, 32000, 8288 24000, 22050, 8289 16000, 12000, 8290 11025, 8000, 8291 7350 8292 ]; 8293 // byte 2 8294 adtsObjectType = ((data[offset + 2] & 0xC0) >>> 6) + 1; 8295 adtsSampleingIndex = ((data[offset + 2] & 0x3C) >>> 2); 8296 if (adtsSampleingIndex > adtsSampleingRates.length - 1) { 8297 observer.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.MEDIA_ERROR, details: errors_1.ErrorDetails.FRAG_PARSING_ERROR, fatal: true, reason: "invalid ADTS sampling index:" + adtsSampleingIndex }); 8298 return; 8299 } 8300 adtsChanelConfig = ((data[offset + 2] & 0x01) << 2); 8301 // byte 3 8302 adtsChanelConfig |= ((data[offset + 3] & 0xC0) >>> 6); 8303 logger_1.logger.log("manifest codec:" + audioCodec + ",ADTS data:type:" + adtsObjectType + ",sampleingIndex:" + adtsSampleingIndex + "[" + adtsSampleingRates[adtsSampleingIndex] + "Hz],channelConfig:" + adtsChanelConfig); 8304 // firefox: freq less than 24kHz = AAC SBR (HE-AAC) 8305 if (/firefox/i.test(userAgent)) { 8306 if (adtsSampleingIndex >= 6) { 8307 adtsObjectType = 5; 8308 config = new Array(4); 8309 // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies 8310 // there is a factor 2 between frame sample rate and output sample rate 8311 // multiply frequency by 2 (see table below, equivalent to substract 3) 8312 adtsExtensionSampleingIndex = adtsSampleingIndex - 3; 8313 } 8314 else { 8315 adtsObjectType = 2; 8316 config = new Array(2); 8317 adtsExtensionSampleingIndex = adtsSampleingIndex; 8318 } 8319 // Android : always use AAC 8320 } 8321 else if (userAgent.indexOf('android') !== -1) { 8322 adtsObjectType = 2; 8323 config = new Array(2); 8324 adtsExtensionSampleingIndex = adtsSampleingIndex; 8325 } 8326 else { 8327 /* for other browsers (Chrome/Vivaldi/Opera ...) 8328 always force audio type to be HE-AAC SBR, as some browsers do not support audio codec switch properly (like Chrome ...) 8329 */ 8330 adtsObjectType = 5; 8331 config = new Array(4); 8332 // if (manifest codec is HE-AAC or HE-AACv2) OR (manifest codec not specified AND frequency less than 24kHz) 8333 if ((audioCodec && ((audioCodec.indexOf('mp4a.40.29') !== -1) || 8334 (audioCodec.indexOf('mp4a.40.5') !== -1))) || 8335 (!audioCodec && adtsSampleingIndex >= 6)) { 8336 // HE-AAC uses SBR (Spectral Band Replication) , high frequencies are constructed from low frequencies 8337 // there is a factor 2 between frame sample rate and output sample rate 8338 // multiply frequency by 2 (see table below, equivalent to substract 3) 8339 adtsExtensionSampleingIndex = adtsSampleingIndex - 3; 8340 } 8341 else { 8342 // if (manifest codec is AAC) AND (frequency less than 24kHz AND nb channel is 1) OR (manifest codec not specified and mono audio) 8343 // Chrome fails to play back with low frequency AAC LC mono when initialized with HE-AAC. This is not a problem with stereo. 8344 if (audioCodec && audioCodec.indexOf('mp4a.40.2') !== -1 && ((adtsSampleingIndex >= 6 && adtsChanelConfig === 1) || 8345 /vivaldi/i.test(userAgent)) || 8346 (!audioCodec && adtsChanelConfig === 1)) { 8347 adtsObjectType = 2; 8348 config = new Array(2); 8349 } 8350 adtsExtensionSampleingIndex = adtsSampleingIndex; 8351 } 8352 } 8353 /* refer to http://wiki.multimedia.cx/index.php?title=MPEG-4_Audio#Audio_Specific_Config 8354 ISO 14496-3 (AAC).pdf - Table 1.13 â Syntax of AudioSpecificConfig() 8355 Audio Profile / Audio Object Type 8356 0: Null 8357 1: AAC Main 8358 2: AAC LC (Low Complexity) 8359 3: AAC SSR (Scalable Sample Rate) 8360 4: AAC LTP (Long Term Prediction) 8361 5: SBR (Spectral Band Replication) 8362 6: AAC Scalable 8363 sampling freq 8364 0: 96000 Hz 8365 1: 88200 Hz 8366 2: 64000 Hz 8367 3: 48000 Hz 8368 4: 44100 Hz 8369 5: 32000 Hz 8370 6: 24000 Hz 8371 7: 22050 Hz 8372 8: 16000 Hz 8373 9: 12000 Hz 8374 10: 11025 Hz 8375 11: 8000 Hz 8376 12: 7350 Hz 8377 13: Reserved 8378 14: Reserved 8379 15: frequency is written explictly 8380 Channel Configurations 8381 These are the channel configurations: 8382 0: Defined in AOT Specifc Config 8383 1: 1 channel: front-center 8384 2: 2 channels: front-left, front-right 8385 */ 8386 // audioObjectType = profile => profile, the MPEG-4 Audio Object Type minus 1 8387 config[0] = adtsObjectType << 3; 8388 // samplingFrequencyIndex 8389 config[0] |= (adtsSampleingIndex & 0x0E) >> 1; 8390 config[1] |= (adtsSampleingIndex & 0x01) << 7; 8391 // channelConfiguration 8392 config[1] |= adtsChanelConfig << 3; 8393 if (adtsObjectType === 5) { 8394 // adtsExtensionSampleingIndex 8395 config[1] |= (adtsExtensionSampleingIndex & 0x0E) >> 1; 8396 config[2] = (adtsExtensionSampleingIndex & 0x01) << 7; 8397 // adtsObjectType (force to 2, chrome is checking that object type is less than 5 ??? 8398 // https://chromium.googlesource.com/chromium/src.git/+/master/media/formats/mp4/aac.cc 8399 config[2] |= 2 << 2; 8400 config[3] = 0; 8401 } 8402 return { config: config, samplerate: adtsSampleingRates[adtsSampleingIndex], channelCount: adtsChanelConfig, codec: ('mp4a.40.' + adtsObjectType), manifestCodec: manifestCodec }; 8403} 8404exports.getAudioConfig = getAudioConfig; 8405function isHeaderPattern(data, offset) { 8406 return data[offset] === 0xff && (data[offset + 1] & 0xf6) === 0xf0; 8407} 8408exports.isHeaderPattern = isHeaderPattern; 8409function getHeaderLength(data, offset) { 8410 return (data[offset + 1] & 0x01 ? 7 : 9); 8411} 8412exports.getHeaderLength = getHeaderLength; 8413function getFullFrameLength(data, offset) { 8414 return ((data[offset + 3] & 0x03) << 11) | 8415 (data[offset + 4] << 3) | 8416 ((data[offset + 5] & 0xE0) >>> 5); 8417} 8418exports.getFullFrameLength = getFullFrameLength; 8419function isHeader(data, offset) { 8420 // Look for ADTS header | 1111 1111 | 1111 X00X | where X can be either 0 or 1 8421 // Layer bits (position 14 and 15) in header should be always 0 for ADTS 8422 // More info https://wiki.multimedia.cx/index.php?title=ADTS 8423 if (offset + 1 < data.length && isHeaderPattern(data, offset)) { 8424 return true; 8425 } 8426 return false;
8427} 8428exports.isHeader = isHeader; 8429function probe(data, offset) { 8430 // same as isHeader but we also check that ADTS frame follows last ADTS frame 8431 // or end of data is reached 8432 if (offset + 1 < data.length && isHeaderPattern(data, offset)) { 8433 // ADTS header Length 8434 var headerLength = getHeaderLength(data, offset); 8435 // ADTS frame Length 8436 var frameLength = headerLength; 8437 if (offset + 5 < data.length) { 8438 frameLength = getFullFrameLength(data, offset); 8439 } 8440 var newOffset = offset + frameLength; 8441 if (newOffset === data.length || (newOffset + 1 < data.length && isHeaderPattern(data, newOffset))) { 8442 return true; 8443 } 8444 } 8445 return false; 8446} 8447exports.probe = probe; 8448function initTrackConfig(track, observer, data, offset, audioCodec) { 8449 if (!track.samplerate) { 8450 var config = getAudioConfig(observer, data, offset, audioCodec); 8451 track.config = config.config; 8452 track.samplerate = config.samplerate; 8453 track.channelCount = config.channelCount; 8454 track.codec = config.codec; 8455 track.manifestCodec = config.manifestCodec; 8456 logger_1.logger.log("parsed codec:" + track.codec + ",rate:" + config.samplerate + ",nb channel:" + config.channelCount); 8457 } 8458} 8459exports.initTrackConfig = initTrackConfig; 8460function getFrameDuration(samplerate) { 8461 return 1024 * 90000 / samplerate; 8462} 8463exports.getFrameDuration = getFrameDuration; 8464function parseFrameHeader(data, offset, pts, frameIndex, frameDuration) { 8465 var headerLength, frameLength, stamp; 8466 var length = data.length; 8467 // The protection skip bit tells us if we have 2 bytes of CRC data at the end of the ADTS header 8468 headerLength = getHeaderLength(data, offset); 8469 // retrieve frame size 8470 frameLength = getFullFrameLength(data, offset); 8471 frameLength -= headerLength; 8472 if ((frameLength > 0) && ((offset + headerLength + frameLength) <= length)) { 8473 stamp = pts + frameIndex * frameDuration; 8474 // logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`); 8475 return { headerLength: headerLength, frameLength: frameLength, stamp: stamp }; 8476 } 8477 return undefined; 8478} 8479exports.parseFrameHeader = parseFrameHeader; 8480function appendFrame(track, data, offset, pts, frameIndex) { 8481 var frameDuration = getFrameDuration(track.samplerate); 8482 var header = parseFrameHeader(data, offset, pts, frameIndex, frameDuration); 8483 if (header) { 8484 var stamp = header.stamp; 8485 var headerLength = header.headerLength; 8486 var frameLength = header.frameLength; 8487 // logger.log(`AAC frame, offset/length/total/pts:${offset+headerLength}/${frameLength}/${data.byteLength}/${(stamp/90).toFixed(0)}`); 8488 var aacSample = { 8489 unit: data.subarray(offset + headerLength, offset + headerLength + frameLength), 8490 pts: stamp, 8491 dts: stamp 8492 }; 8493 track.samples.push(aacSample); 8494 track.len += frameLength; 8495 return { sample: aacSample, length: frameLength + headerLength }; 8496 } 8497 return undefined; 8498} 8499exports.appendFrame = appendFrame; 8500 8501 8502/***/ }), 8503
8504/***/ "./src/demux/demuxer-inline.js": 8505/*!*************************************!*\ 8506 !*** ./src/demux/demuxer-inline.js ***! 8507 \*************************************/ 8508/*! no static exports found */ 8509/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 8510/***/ (function(module, exports, __webpack_require__) { 8511 8512"use strict"; 8513 8514/** 8515 * 8516 * inline demuxer: probe fragments and instantiate 8517 * appropriate demuxer depending on content type (TSDemuxer, AACDemuxer, ...) 8518 * 8519 */ 8520Object.defineProperty(exports, "__esModule", { value: true }); 8521var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 8522var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 8523var decrypter_1 = __webpack_require__(/*! ../crypt/decrypter */ "./src/crypt/decrypter.js"); 8524var aacdemuxer_1 = __webpack_require__(/*! ../demux/aacdemuxer */ "./src/demux/aacdemuxer.js"); 8525var mp4demuxer_1 = __webpack_require__(/*! ../demux/mp4demuxer */ "./src/demux/mp4demuxer.js"); 8526var tsdemuxer_1 = __webpack_require__(/*! ../demux/tsdemuxer */ "./src/demux/tsdemuxer.js"); 8527var mp3demuxer_1 = __webpack_require__(/*! ../demux/mp3demuxer */ "./src/demux/mp3demuxer.js"); 8528var mp4_remuxer_1 = __webpack_require__(/*! ../remux/mp4-remuxer */ "./src/remux/mp4-remuxer.js"); 8529var passthrough_remuxer_1 = __webpack_require__(/*! ../remux/passthrough-remuxer */ "./src/remux/passthrough-remuxer.js"); 8530var get_self_scope_1 = __webpack_require__(/*! ../utils/get-self-scope */ "./src/utils/get-self-scope.js"); 8531var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 8532// see https://stackoverflow.com/a/11237259/589493 8533var global = get_self_scope_1.getSelfScope(); // safeguard for code that might run both on worker and main thread 8534var now; 8535// performance.now() not available on WebWorker, at least on Safari Desktop 8536try { 8537 now = global.performance.now.bind(global.performance); 8538} 8539catch (err) { 8540 logger_1.logger.debug('Unable to use Performance API on this environment'); 8541 now = global.Date.now; 8542} 8543var DemuxerInline = /** @class */ (function () { 8544 function DemuxerInline(observer, typeSupported, config, vendor) { 8545 this.observer = observer; 8546 this.typeSupported = typeSupported; 8547 this.config = config; 8548 this.vendor = vendor; 8549 } 8550 DemuxerInline.prototype.destroy = function () { 8551 var demuxer = this.demuxer; 8552 if (demuxer) { 8553 demuxer.destroy(); 8554 } 8555 }; 8556 DemuxerInline.prototype.push = function (data, decryptdata, initSegment, audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS) { 8557 var _this = this; 8558 if ((data.byteLength > 0) && (decryptdata != null) && (decryptdata.key != null) && (decryptdata.method === 'AES-128')) { 8559 var decrypter = this.decrypter; 8560 if (decrypter == null) { 8561 decrypter = this.decrypter = new decrypter_1.default(this.observer, this.config); 8562 } 8563 var startTime_1 = now(); 8564 decrypter.decrypt(data, decryptdata.key.buffer, decryptdata.iv.buffer, function (decryptedData) { 8565 var endTime = now(); 8566 _this.observer.trigger(events_1.default.FRAG_DECRYPTED, { stats: { tstart: startTime_1, tdecrypt: endTime } }); 8567 _this.pushDecrypted(new Uint8Array(decryptedData), decryptdata, new Uint8Array(initSegment), audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS); 8568 }); 8569 } 8570 else { 8571 this.pushDecrypted(new Uint8Array(data), decryptdata, new Uint8Array(initSegment), audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS); 8572 } 8573 }; 8574 DemuxerInline.prototype.pushDecrypted = function (data, decryptdata, initSegment, audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS) { 8575 var demuxer = this.demuxer; 8576 if (!demuxer || 8577 // in case of continuity change, or track switch 8578 // we might switch from content type (AAC container to TS container, or TS to fmp4 for example) 8579 // so let's check that current demuxer is still valid 8580 ((discontinuity || trackSwitch) && !this.probe(data))) { 8581 var observer = this.observer;
8582 var typeSupported = this.typeSupported; 8583 var config = this.config; 8584 // probing order is TS/AAC/MP3/MP4 8585 var muxConfig = [ 8586 { demux: tsdemuxer_1.default, remux: mp4_remuxer_1.default }, 8587 { demux: mp4demuxer_1.default, remux: passthrough_remuxer_1.default }, 8588 { demux: aacdemuxer_1.default, remux: mp4_remuxer_1.default }, 8589 { demux: mp3demuxer_1.default, remux: mp4_remuxer_1.default } 8590 ]; 8591 // probe for content type 8592 for (var i = 0, len = muxConfig.length; i < len; i++) { 8593 var mux = muxConfig[i]; 8594 var probe = mux.demux.probe; 8595 if (probe(data)) { 8596 var remuxer_1 = this.remuxer = new mux.remux(observer, config, typeSupported, this.vendor); 8597 demuxer = new mux.demux(observer, remuxer_1, config, typeSupported); 8598 this.probe = probe; 8599 break; 8600 } 8601 } 8602 if (!demuxer) { 8603 observer.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.MEDIA_ERROR, details: errors_1.ErrorDetails.FRAG_PARSING_ERROR, fatal: true, reason: 'no demux matching with content found' }); 8604 return; 8605 } 8606 this.demuxer = demuxer; 8607 } 8608 var remuxer = this.remuxer; 8609 if (discontinuity || trackSwitch) { 8610 demuxer.resetInitSegment(initSegment, audioCodec, videoCodec, duration); 8611 remuxer.resetInitSegment(); 8612 } 8613 if (discontinuity) { 8614 demuxer.resetTimeStamp(defaultInitPTS); 8615 remuxer.resetTimeStamp(defaultInitPTS); 8616 } 8617 if (typeof demuxer.setDecryptData === 'function') { 8618 demuxer.setDecryptData(decryptdata); 8619 } 8620 demuxer.append(data, timeOffset, contiguous, accurateTimeOffset); 8621 }; 8622 return DemuxerInline; 8623}()); 8624exports.default = DemuxerInline; 8625 8626 8627/***/ }), 8628
vendor: 3,895 bytes, lines 8629-8715
8629/***/ "./src/demux/demuxer-worker.js": 8630/*!*************************************!*\ 8631 !*** ./src/demux/demuxer-worker.js ***! 8632 \*************************************/ 8633/*! no static exports found */ 8634/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 8635/***/ (function(module, exports, __webpack_require__) { 8636 8637"use strict"; 8638 8639/* demuxer web worker. 8640 * - listen to worker message, and trigger DemuxerInline upon reception of Fragments. 8641 * - provides MP4 Boxes back to main thread using [transferable objects](https://developers.google.com/web/updates/2011/12/Transferable-Objects-Lightning-Fast) in order to minimize message passing overhead. 8642 */ 8643Object.defineProperty(exports, "__esModule", { value: true }); 8644var demuxer_inline_1 = __webpack_require__(/*! ../demux/demuxer-inline */ "./src/demux/demuxer-inline.js"); 8645var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 8646var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 8647var events_2 = __webpack_require__(/*! events */ "./node_modules/node-libs-browser/node_modules/events/events.js"); 8648var DemuxerWorker = function (self) { 8649 // observer setup 8650 var observer = new events_2.EventEmitter(); 8651 observer.trigger = function trigger(event) { 8652 var data = []; 8653 for (var _i = 1; _i < arguments.length; _i++) { 8654 data[_i - 1] = arguments[_i]; 8655 } 8656 observer.emit.apply(observer, [event, event].concat(data)); 8657 }; 8658 observer.off = function off(event) { 8659 var data = []; 8660 for (var _i = 1; _i < arguments.length; _i++) { 8661 data[_i - 1] = arguments[_i]; 8662 } 8663 observer.removeListener.apply(observer, [event].concat(data)); 8664 }; 8665 var forwardMessage = function (ev, data) { 8666 self.postMessage({ event: ev, data: data }); 8667 }; 8668 self.addEventListener('message', function (ev) { 8669 var data = ev.data; 8670 // console.log('demuxer cmd:' + data.cmd); 8671 switch (data.cmd) { 8672 case 'init': 8673 var config = JSON.parse(data.config); 8674 self.demuxer = new demuxer_inline_1.default(observer, data.typeSupported, config, data.vendor); 8675 logger_1.enableLogs(config.debug); 8676 // signal end of worker init 8677 forwardMessage('init', null); 8678 break; 8679 case 'demux': 8680 self.demuxer.push(data.data, data.decryptdata, data.initSegment, data.audioCodec, data.videoCodec, data.timeOffset, data.discontinuity, data.trackSwitch, data.contiguous, data.duration, data.accurateTimeOffset, data.defaultInitPTS); 8681 break; 8682 default: 8683 break; 8684 } 8685 }); 8686 // forward events to main thread 8687 observer.on(events_1.default.FRAG_DECRYPTED, forwardMessage); 8688 observer.on(events_1.default.FRAG_PARSING_INIT_SEGMENT, forwardMessage); 8689 observer.on(events_1.default.FRAG_PARSED, forwardMessage); 8690 observer.on(events_1.default.ERROR, forwardMessage); 8691 observer.on(events_1.default.FRAG_PARSING_METADATA, forwardMessage); 8692 observer.on(events_1.default.FRAG_PARSING_USERDATA, forwardMessage); 8693 observer.on(events_1.default.INIT_PTS_FOUND, forwardMessage); 8694 // special case for FRAG_PARSING_DATA: pass data1/data2 as transferable object (no copy) 8695 observer.on(events_1.default.FRAG_PARSING_DATA, function (ev, data) { 8696 var transferable = []; 8697 var message = { event: ev, data: data }; 8698 if (data.data1) { 8699 message.data1 = data.data1.buffer; 8700 transferable.push(data.data1.buffer); 8701 delete data.data1; 8702 } 8703 if (data.data2) { 8704 message.data2 = data.data2.buffer; 8705 transferable.push(data.data2.buffer); 8706 delete data.data2; 8707 } 8708 self.postMessage(message, transferable); 8709 }); 8710}; 8711exports.default = DemuxerWorker; 8712 8713 8714/***/ }), 8715
8716/***/ "./src/demux/demuxer.js": 8717/*!******************************!*\ 8718 !*** ./src/demux/demuxer.js ***! 8719 \******************************/ 8720/*! no static exports found */ 8721/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 8722/***/ (function(module, exports, __webpack_require__) { 8723 8724"use strict"; 8725/* WEBPACK VAR INJECTION */(function(Number) { 8726Object.defineProperty(exports, "__esModule", { value: true }); 8727var work = __webpack_require__(/*! webworkify-webpack */ "./node_modules/webworkify-webpack/index.js"); 8728var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 8729var demuxer_inline_1 = __webpack_require__(/*! ../demux/demuxer-inline */ "./src/demux/demuxer-inline.js"); 8730var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 8731var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 8732var mediasource_helper_1 = __webpack_require__(/*! ../utils/mediasource-helper */ "./src/utils/mediasource-helper.js"); 8733var get_self_scope_1 = __webpack_require__(/*! ../utils/get-self-scope */ "./src/utils/get-self-scope.js"); 8734var observer_1 = __webpack_require__(/*! ../observer */ "./src/observer.js"); 8735// see https://stackoverflow.com/a/11237259/589493 8736var global = get_self_scope_1.getSelfScope(); // safeguard for code that might run both on worker and main thread 8737var MediaSource = mediasource_helper_1.getMediaSource(); 8738var Demuxer = /** @class */ (function () { 8739 function Demuxer(hls, id) { 8740 var _this = this; 8741 this.hls = hls; 8742 this.id = id; 8743 var observer = this.observer = new observer_1.Observer(); 8744 var config = hls.config; 8745 var forwardMessage = function (ev, data) { 8746 data = data || {}; 8747 data.frag = _this.frag; 8748 data.id = _this.id; 8749 hls.trigger(ev, data); 8750 }; 8751 // forward events to main thread 8752 observer.on(events_1.default.FRAG_DECRYPTED, forwardMessage); 8753 observer.on(events_1.default.FRAG_PARSING_INIT_SEGMENT, forwardMessage); 8754 observer.on(events_1.default.FRAG_PARSING_DATA, forwardMessage); 8755 observer.on(events_1.default.FRAG_PARSED, forwardMessage); 8756 observer.on(events_1.default.ERROR, forwardMessage); 8757 observer.on(events_1.default.FRAG_PARSING_METADATA, forwardMessage); 8758 observer.on(events_1.default.FRAG_PARSING_USERDATA, forwardMessage); 8759 observer.on(events_1.default.INIT_PTS_FOUND, forwardMessage); 8760 var typeSupported = { 8761 mp4: MediaSource.isTypeSupported('video/mp4'), 8762 mpeg: MediaSource.isTypeSupported('audio/mpeg'), 8763 mp3: MediaSource.isTypeSupported('audio/mp4; codecs="mp3"') 8764 }; 8765 // navigator.vendor is not always available in Web Worker 8766 // refer to https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/navigator 8767 var vendor = navigator.vendor; 8768 if (config.enableWorker && (typeof (Worker) !== 'undefined')) { 8769 logger_1.logger.log('demuxing in webworker'); 8770 var w = void 0; 8771 try { 8772 w = this.w = work(/*require.resolve*/(/*! ../demux/demuxer-worker.js */ "./src/demux/demuxer-worker.js")); 8773 this.onwmsg = this.onWorkerMessage.bind(this); 8774 w.addEventListener('message', this.onwmsg); 8775 w.onerror = function (event) { 8776 hls.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.OTHER_ERROR, details: errors_1.ErrorDetails.INTERNAL_EXCEPTION, fatal: true, event: 'demuxerWorker', err: { message: event.message + ' (' + event.filename + ':' + event.lineno + ')' } }); 8777 }; 8778 w.postMessage({ cmd: 'init', typeSupported: typeSupported, vendor: vendor, id: id, config: JSON.stringify(config) }); 8779 } 8780 catch (err) { 8781 logger_1.logger.warn('Error in worker:', err); 8782 logger_1.logger.error('Error while initializing DemuxerWorker, fallback on DemuxerInline'); 8783 if (w) { 8784 // revoke the Object URL that was used to create demuxer worker, so as not to leak it 8785 global.URL.revokeObjectURL(w.objectURL); 8786 } 8787 this.demuxer = new demuxer_inline_1.default(observer, typeSupp
8787orted, config, vendor); 8788 this.w = undefined; 8789 } 8790 } 8791 else { 8792 this.demuxer = new demuxer_inline_1.default(observer, typeSupported, config, vendor); 8793 } 8794 } 8795 Demuxer.prototype.destroy = function () { 8796 var w = this.w; 8797 if (w) { 8798 w.removeEventListener('message', this.onwmsg); 8799 w.terminate(); 8800 this.w = null; 8801 } 8802 else { 8803 var demuxer = this.demuxer; 8804 if (demuxer) { 8805 demuxer.destroy(); 8806 this.demuxer = null; 8807 } 8808 } 8809 var observer = this.observer; 8810 if (observer) { 8811 observer.removeAllListeners(); 8812 this.observer = null; 8813 } 8814 }; 8815 Demuxer.prototype.push = function (data, initSegment, audioCodec, videoCodec, frag, duration, accurateTimeOffset, defaultInitPTS) { 8816 var w = this.w; 8817 var timeOffset = Number.isFinite(frag.startPTS) ? frag.startPTS : frag.start; 8818 var decryptdata = frag.decryptdata; 8819 var lastFrag = this.frag; 8820 var discontinuity = !(lastFrag && (frag.cc === lastFrag.cc)); 8821 var trackSwitch = !(lastFrag && (frag.level === lastFrag.level)); 8822 var nextSN = lastFrag && (frag.sn === (lastFrag.sn + 1)); 8823 var contiguous = !trackSwitch && nextSN; 8824 if (discontinuity) { 8825 logger_1.logger.log(this.id + ":discontinuity detected"); 8826 } 8827 if (trackSwitch) { 8828 logger_1.logger.log(this.id + ":switch detected"); 8829 } 8830 this.frag = frag; 8831 if (w) { 8832 // post fragment payload as transferable objects for ArrayBuffer (no copy) 8833 w.postMessage({ cmd: 'demux', data: data, decryptdata: decryptdata, initSegment: initSegment, audioCodec: audioCodec, videoCodec: videoCodec, timeOffset: timeOffset, discontinuity: discontinuity, trackSwitch: trackSwitch, contiguous: contiguous, duration: duration, accurateTimeOffset: accurateTimeOffset, defaultInitPTS: defaultInitPTS }, data instanceof ArrayBuffer ? [data] : []); 8834 } 8835 else { 8836 var demuxer = this.demuxer; 8837 if (demuxer) { 8838 demuxer.push(data, decryptdata, initSegment, audioCodec, videoCodec, timeOffset, discontinuity, trackSwitch, contiguous, duration, accurateTimeOffset, defaultInitPTS); 8839 } 8840 } 8841 }; 8842 Demuxer.prototype.onWorkerMessage = function (ev) { 8843 var data = ev.data, hls = this.hls; 8844 switch (data.event) { 8845 case 'init': 8846 // revoke the Object URL that was used to create demuxer worker, so as not to leak it 8847 global.URL.revokeObjectURL(this.w.objectURL); 8848 break; 8849 // special case for FRAG_PARSING_DATA: data1 and data2 are transferable objects 8850 case events_1.default.FRAG_PARSING_DATA: 8851 data.data.data1 = new Uint8Array(data.data1); 8852 if (data.data2) { 8853 data.data.data2 = new Uint8Array(data.data2); 8854 } 8855 /* falls through */ 8856 default: 8857 data.data = data.data || {}; 8858 data.data.frag = this.frag; 8859 data.data.id = this.id; 8860 hls.trigger(data.event, data.data); 8861 break; 8862 } 8863 }; 8864 return Demuxer; 8865}()); 8866exports.default = Demuxer; 8867 8868/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 8869 8870/***/ }), 8871
vendor: 19,749 bytes, lines 8872-9385
8872/***/ "./src/demux/exp-golomb.js": 8873/*!*********************************!*\ 8874 !*** ./src/demux/exp-golomb.js ***! 8875 \*********************************/ 8876/*! no static exports found */ 8877/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 8878/***/ (function(module, exports, __webpack_require__) { 8879 8880"use strict"; 8881 8882/** 8883 * Parser for exponential Golomb codes, a variable-bitwidth number encoding scheme used by h264. 8884*/ 8885Object.defineProperty(exports, "__esModule", { value: true }); 8886var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 8887var ExpGolomb = /** @class */ (function () { 8888 function ExpGolomb(data) { 8889 this.data = data; 8890 // the number of bytes left to examine in this.data 8891 this.bytesAvailable = data.byteLength; 8892 // the current word being examined 8893 this.word = 0; // :uint 8894 // the number of bits left to examine in the current word 8895 this.bitsAvailable = 0; // :uint 8896 } 8897 // ():void 8898 ExpGolomb.prototype.loadWord = function () { 8899 var data = this.data, bytesAvailable = this.bytesAvailable, position = data.byteLength - bytesAvailable, workingBytes = new Uint8Array(4), availableBytes = Math.min(4, bytesAvailable); 8900 if (availableBytes === 0) { 8901 throw new Error('no bytes available'); 8902 } 8903 workingBytes.set(data.subarray(position, position + availableBytes)); 8904 this.word = new DataView(workingBytes.buffer).getUint32(0); 8905 // track the amount of this.data that has been processed 8906 this.bitsAvailable = availableBytes * 8; 8907 this.bytesAvailable -= availableBytes; 8908 }; 8909 // (count:int):void 8910 ExpGolomb.prototype.skipBits = function (count) { 8911 var skipBytes; // :int 8912 if (this.bitsAvailable > count) { 8913 this.word <<= count; 8914 this.bitsAvailable -= count; 8915 } 8916 else { 8917 count -= this.bitsAvailable; 8918 skipBytes = count >> 3; 8919 count -= (skipBytes >> 3); 8920 this.bytesAvailable -= skipBytes; 8921 this.loadWord(); 8922 this.word <<= count; 8923 this.bitsAvailable -= count; 8924 } 8925 }; 8926 // (size:int):uint 8927 ExpGolomb.prototype.readBits = function (size) { 8928 var bits = Math.min(this.bitsAvailable, size), // :uint 8929 valu = this.word >>> (32 - bits); // :uint 8930 if (size > 32) { 8931 logger_1.logger.error('Cannot read more than 32 bits at a time'); 8932 } 8933 this.bitsAvailable -= bits; 8934 if (this.bitsAvailable > 0) { 8935 this.word <<= bits; 8936 } 8937 else if (this.bytesAvailable > 0) { 8938 this.loadWord(); 8939 } 8940 bits = size - bits; 8941 if (bits > 0 && this.bitsAvailable) { 8942 return valu << bits | this.readBits(bits); 8943 } 8944 else { 8945 return valu; 8946 } 8947 }; 8948 // ():uint 8949 ExpGolomb.prototype.skipLZ = function () { 8950 var leadingZeroCount; // :uint 8951 for (leadingZeroCount = 0; leadingZeroCount < this.bitsAvailable; ++leadingZeroCount) { 8952 if ((this.word & (0x80000000 >>> leadingZeroCount)) !== 0) { 8953 // the first bit of working word is 1 8954 this.word <<= leadingZeroCount; 8955 this.bitsAvailable -= leadingZeroCount; 8956 return leadingZeroCount; 8957 } 8958 } 8959 // we exhausted word and still have not found a 1 8960 this.loadWord(); 8961 return leadingZeroCount + this.skipLZ(); 8962 }; 8963 // ():void 8964 ExpGolomb.prototype.skipUEG = function () { 8965 this.skipBits(1 + this.skipLZ()); 8966 }; 8967 // ():void 8968 ExpGolomb.prototype.skipEG = function () { 8969 this.skipBits(1 + this.skipLZ()); 8970 }; 8971 // ():uint 8972 ExpGolomb.prototype.readUEG = function () { 8973 var clz = this.skipLZ(); // :uint 8974 return this.readBits(clz + 1) - 1; 8975 }; 8976 // ():int 8977 ExpGolomb.prototype.readEG = function () { 8978 var valu = this.readUEG(); // :int 8979 if (0x01 & valu) { 8980 // the number is odd if the low order bit is set 8981 return (1 + valu) >>> 1; // add 1 to make it even, and divide by 2 8982 } 8983 else { 8984 return -1 * (valu >>> 1); // divide by two then make it negative 8985 } 8986 }; 8987 // Some convenience functions 8988 // :Boolean 8989 ExpGolomb.prototype.readBoolean = function () { 8990 return this.readBits(1) === 1; 8991 }; 8992 // ():int 8993 ExpGolomb.prototype.readUByte = function () { 8994 return this.readBits(8); 8995 }; 8996 // ():int 8997 ExpGolomb.prototype.readUShort = function () { 8998 return this.readBits(16); 8999 }; 9000 // ():int 9001 ExpGolomb.prototype.readUInt = function () { 9002 return this.readBits(32); 9003 }; 9004 /** 9005 * Advance the ExpGolomb decoder past a scaling list. The scaling 9006 * list is optionally transmitted as part of a sequence parameter 9007 * set and is not relevant to transmuxing. 9008 * @param count {number} the number of entries in this scaling list 9009 * @see Recommendation ITU-T H.264, Section 7.3.2.1.1.1 9010 */ 9011 ExpGolomb.prototype.skipScalingList = function (count) { 9012 var lastScale = 8, nextScale = 8, j, deltaScale; 9013 for (j = 0; j < count; j++) { 9014 if (nextScale !== 0) { 9015 deltaScale = this.readEG(); 9016 nextScale = (lastScale + deltaScale + 256) % 256; 9017 } 9018 lastScale = (nextScale === 0) ? lastScale : nextScale; 9019 } 9020 }; 9021 /** 9022 * Read a sequence parameter set and return some interesting video 9023 * properties. A sequence parameter set is the H264 metadata that 9024 * describes the properties of upcoming video frames. 9025 * @param data {Uint8Array} the bytes of a sequence parameter set 9026 * @return {object} an object with configuration parsed from the 9027 * sequence parameter set, including the dimensions of the 9028 * associated video frames. 9029 */ 9030 ExpGolomb.prototype.readSPS = function () { 9031 var frameCropLeftOffset = 0, frameCropRightOffset = 0, frameCropTopOffset = 0, frameCropBottomOffset = 0, profileIdc, profileCompat, levelIdc, numRefFramesInPicOrderCntCycle, picWidthInMbsMinus1, picHeightInMapUnitsMinus1, frameMbsOnlyFlag, scalingListCount, i, readUByte = this.readUByte.bind(this), readBits = this.readBits.bind(this), readUEG = this.readUEG.bind(this), readBoolean = this.readBoolean.bind(this), skipBits = this.skipBits.bind(this), skipEG = this.skipEG.bind(this), skipUEG = this.skipUEG.bind(this), skipScalingList = this.skipScalingList.bind(this); 9032 readUByte(); 9033 profileIdc = readUByte(); // profile_idc 9034 profileCompat = readBits(5); // constraint_set[0-4]_flag, u(5) 9035 skipBits(3); // reserved_zero_3bits u(3), 9036 levelIdc = readUByte(); // level_idc u(8) 9037 skipUEG(); // seq_parameter_set_id 9038 // some profiles have more optional data we don't need 9039 if (profileIdc === 100 || 9040 profileIdc === 110 || 9041 profileIdc === 122 || 9042 profileIdc === 244 || 9043 profileIdc === 44 || 9044 profileIdc === 83 || 9045 profileIdc === 86 || 9046 profileIdc === 118 || 9047 profileIdc === 128) { 9048 var chromaFormatIdc = readUEG(); 9049 if (chromaFormatIdc === 3) { 9050 skipBits(1); 9051 } // separate_colour_plane_flag 9052 skipUEG(); // bit_depth_luma_minus8 9053 skipUEG(); // bit_depth_chroma_minus8 9054 skipBits(1); // qpprime_y_zero_transform_bypass_flag 9055 if (readBoolean()) { // seq_scaling_matrix_present_flag 9056 scalingListCount = (chromaFormatIdc !== 3) ? 8 : 12; 9057 for (i = 0; i < scalingListCount; i++) { 9058 if (readBoolean()) { // seq_scaling_list_present_flag[ i ] 9059 if (i < 6) { 9060 skipScalingList(16); 9061 } 9062 else { 9063 skipScalingList(64); 9064 } 9065 } 9066 } 9067 } 9068 } 9069 skipUEG(); // log2_max_frame_num_minus4 9070 var picOrderCntType = readUEG(); 9071 if (picOrderCntType === 0) { 9072 readUEG(); // log2_max_pic_order_cnt_lsb_minus4 9073 } 9074 else if (picOrderCntType === 1) { 9075 skipBits(1); // delta_pic_order_always_zero_flag 9076 skipEG(); // offset_for_non_ref_pic 9077 skipEG(); // offset_for_top_to_bottom_field 9078 numRefFramesInPicOrderCntCycle = readUEG(); 9079 for (i = 0; i < numRefFramesInPicOrderCntCycle; i++) { 9080 skipEG(); 9081 } // offset_for_ref_frame[ i ] 9082 } 9083 skipUEG(); // max_num_ref_frames 9084 skipBits(1); // gaps_in_frame_num_value_allowed_flag 9085 picWidthInMbsMinus1 = readUEG(); 9086 picHeightInMapUnitsMinus1 = readUEG(); 9087 frameMbsOnlyFlag = readBits(1); 9088 if (frameMbsOnlyFlag === 0) { 9089 skipBits(1); 9090 } // mb_adaptive_frame_field_flag 9091 skipBits(1); // direct_8x8_inference_flag 9092 if (readBoolean()) { // frame_cropping_flag 9093 frameCropLeftOffset = readUEG(); 9094 frameCropRightOffset = readUEG(); 9095 frameCropTopOffset = readUEG(); 9096 frameCropBottomOffset = readUEG(); 9097 } 9098 var pixelRatio = [1, 1]; 9099 if (readBoolean()) { 9100 // vui_parameters_present_flag 9101 if (readBoolean()) { 9102 // aspect_ratio_info_present_flag 9103 var aspectRatioIdc = readUByte(); 9104 switch (aspectRatioIdc) { 9105 case 1: 9106 pixelRatio = [1, 1]; 9107 break; 9108 case 2: 9109 pixelRatio = [12, 11]; 9110 break; 9111 case 3: 9112 pixelRatio = [10, 11]; 9113 break; 9114 case 4: 9115 pixelRatio = [16, 11]; 9116 break; 9117 case 5: 9118 pixelRatio = [40, 33]; 9119 break; 9120 case 6: 9121 pixelRatio = [24, 11]; 9122 break; 9123 case 7: 9124 pixelRatio = [20, 11]; 9125 break; 9126 case 8: 9127 pixelRatio = [32, 11]; 9128 break; 9129 case 9: 9130 pixelRatio = [80, 33]; 9131 break; 9132 case 10: 9133 pixelRatio = [18, 11]; 9134 break; 9135 case 11: 9136 pixelRatio = [15, 11]; 9137 break; 9138 case 12: 9139 pixelRatio = [64, 33]; 9140 break; 9141 case 13: 9142 pixelRatio = [160, 99]; 9143 break; 9144 case 14: 9145 pixelRatio = [4, 3]; 9146 break; 9147 case 15: 9148 pixelRatio = [3, 2]; 9149 break; 9150 case 16: 9151 pixelRatio = [2, 1]; 9152 break; 9153 case 255: { 9154 pixelRatio = [readUByte() << 8 | readUByte(), readUByte() << 8 | readUByte()]; 9155 break; 9156 } 9157 } 9158 } 9159 } 9160 return { 9161 width: Math.ceil((((picWidthInMbsMinus1 + 1) * 16) - frameCropLeftOffset * 2 - frameCropRightOffset * 2)), 9162 height: ((2 - frameMbsOnlyFlag) * (picHeightInMapUnitsMinus1 + 1) * 16) - ((frameMbsOnlyFlag ? 2 : 4) * (frameCropTopOffset + frameCropBottomOffset)), 9163 pixelRatio: pixelRatio 9164 }; 9165 }; 9166 ExpGolomb.prototype.readSliceType = function () { 9167 // skip NALu type 9168 this.readUByte(); 9169 // discard first_mb_in_slice 9170 this.readUEG(); 9171 // return slice_type 9172 return this.readUEG(); 9173 }; 9174 return ExpGolomb; 9175}()); 9176exports.default = ExpGolomb; 9177 9178 9179/***/ }), 9180 9181/***/ "./src/demux/id3.js": 9182/*!**************************!*\ 9183 !*** ./src/demux/id3.js ***! 9184 \**************************/ 9185/*! no static exports found */ 9186/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 9187/***/ (function(module, exports, __webpack_require__) { 9188 9189"use strict"; 9190 9191Object.defineProperty(exports, "__esModule", { value: true }); 9192/** 9193 * ID3 parser 9194 */ 9195var ID3 = /** @class */ (function () { 9196 function ID3() { 9197 } 9198 /** 9199 * Returns true if an ID3 header can be found at offset in data 9200 * @param {Uint8Array} data - The data to search in 9201 * @param {number} offset - The offset at which to start searching 9202 * @return {boolean} - True if an ID3 header is found 9203 */ 9204 ID3.isHeader = function (data, offset) { 9205 /* 9206 * http://id3.org/id3v2.3.0 9207 * [0] = 'I' 9208 * [1] = 'D' 9209 * [2] = '3' 9210 * [3,4] = {Version} 9211 * [5] = {Flags} 9212 * [6-9] = {ID3 Size} 9213 * 9214 * An ID3v2 tag can be detected with the following pattern: 9215 * $49 44 33 yy yy xx zz zz zz zz 9216 * Where yy is less than $FF, xx is the 'flags' byte and zz is less than $80 9217 */ 9218 if (offset + 10 <= data.length) { 9219 // look for 'ID3' identifier 9220 if (data[offset] === 0x49 && data[offset + 1] === 0x44 && data[offset + 2] === 0x33) { 9221 // check version is within range 9222 if (data[offset + 3] < 0xFF && data[offset + 4] < 0xFF) { 9223 // check size is within range 9224 if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) { 9225 return true; 9226 } 9227 } 9228 } 9229 } 9230 return false; 9231 }; 9232 /** 9233 * Returns true if an ID3 footer can be found at offset in data 9234 * @param {Uint8Array} data - The data to search in 9235 * @param {number} offset - The offset at which to start searching 9236 * @return {boolean} - True if an ID3 footer is found 9237 */ 9238 ID3.isFooter = function (data, offset) { 9239 /* 9240 * The footer is a copy of the header, but with a different identifier 9241 */ 9242 if (offset + 10 <= data.length) { 9243 // look for '3DI' identifier 9244 if (data[offset] === 0x33 && data[offset + 1] === 0x44 && data[offset + 2] === 0x49) { 9245 // check version is within range 9246 if (data[offset + 3] < 0xFF && data[offset + 4] < 0xFF) { 9247 // check size is within range 9248 if (data[offset + 6] < 0x80 && data[offset + 7] < 0x80 && data[offset + 8] < 0x80 && data[offset + 9] < 0x80) { 9249 return true; 9250 } 9251 } 9252 } 9253 } 9254 return false; 9255 }; 9256 /** 9257 * Returns any adjacent ID3 tags found in data starting at offset, as one block of data 9258 * @param {Uint8Array} data - The data to search in 9259 * @param {number} offset - The offset at which to start searching 9260 * @return {Uint8Array} - The block of data containing any ID3 tags found 9261 */ 9262 ID3.getID3Data = function (data, offset) { 9263 var front = offset; 9264 var length = 0; 9265 while (ID3.isHeader(data, offset)) { 9266 // ID3 header is 10 bytes 9267 length += 10; 9268 var size = ID3._readSize(data, offset + 6); 9269 length += size; 9270 if (ID3.isFooter(data, offset + 10)) { 9271 // ID3 footer is 10 bytes 9272 length += 10; 9273 } 9274 offset += length; 9275 } 9276 if (length > 0) { 9277 return data.subarray(front, front + length); 9278 } 9279 return undefined; 9280 }; 9281 ID3._readSize = function (data, offset) { 9282 var size = 0; 9283 size = ((data[offset] & 0x7f) << 21); 9284 size |= ((data[offset + 1] & 0x7f) << 14); 9285 size |= ((data[offset + 2] & 0x7f) << 7); 9286 size |= (data[offset + 3] & 0x7f); 9287 return size; 9288 }; 9289 /** 9290 * Searches for the Elementary Stream timestamp found in the ID3 data chunk 9291 * @param {Uint8Array} data - Block of data containing one or more ID3 tags 9292 * @return {number} - The timestamp 9293 */ 9294 ID3.getTimeStamp = function (data) { 9295 var frames = ID3.getID3Frames(data); 9296 for (var i = 0; i < frames.length; i++) { 9297 var frame = frames[i]; 9298 if (ID3.isTimeStampFrame(frame)) { 9299 return ID3._readTimeStamp(frame); 9300 } 9301 } 9302 return undefined; 9303 }; 9304 /** 9305 * Returns true if the ID3 frame is an Elementary Stream timestamp frame 9306 * @param {ID3 frame} frame 9307 */ 9308 ID3.isTimeStampFrame = function (frame) { 9309 return (frame && frame.key === 'PRIV' && frame.info === 'com.apple.streaming.transportStreamTimestamp'); 9310 }; 9311 ID3._getFrameData = function (data) { 9312 /* 9313 Frame ID $xx xx xx xx (four characters) 9314 Size $xx xx xx xx 9315 Flags $xx xx 9316 */ 9317 var type = String.fromCharCode(data[0], data[1], data[2], data[3]); 9318 var size = ID3._readSize(data, 4); 9319 // skip frame id, size, and flags 9320 var offset = 10; 9321 return { type: type, size: size, data: data.subarray(offset, offset + size) }; 9322 }; 9323 /** 9324 * Returns an array of ID3 frames found in all the ID3 tags in the id3Data 9325 * @param {Uint8Array} id3Data - The ID3 data containing one or more ID3 tags 9326 * @return {ID3 frame[]} - Array of ID3 frame objects 9327 */ 9328 ID3.getID3Frames = function (id3Data) { 9329 var offset = 0; 9330 var frames = []; 9331 while (ID3.isHeader(id3Data, offset)) { 9332 var size = ID3._readSize(id3Data, offset + 6); 9333 // skip past ID3 header 9334 offset += 10; 9335 var end = offset + size; 9336 // loop through frames in the ID3 tag 9337 while (offset + 8 < end) { 9338 var frameData = ID3._getFrameData(id3Data.subarray(offset)); 9339 var frame = ID3._decodeFrame(frameData); 9340 if (frame) { 9341 frames.push(frame); 9342 } 9343 // skip frame header and frame data 9344 offset += frameData.size + 10; 9345 } 9346 if (ID3.isFooter(id3Data, offset)) { 9347 offset += 10; 9348 } 9349 } 9350 return frames; 9351 }; 9352 ID3._decodeFrame = function (frame) { 9353 if (frame.type === 'PRIV') { 9354 return ID3._decodePrivFrame(frame); 9355 } 9356 else if (frame.type[0] === 'T') { 9357 return ID3._decodeTextFrame(frame); 9358 } 9359 else if (frame.type[0] === 'W') { 9360 return ID3._decodeURLFrame(frame); 9361 } 9362 return undefined; 9363 }; 9364 ID3._readTimeStamp = function (timeStampFrame) { 9365 if (timeStampFrame.data.byteLength === 8) { 9366 var data = new Uint8Array(timeStampFrame.data); 9367 // timestamp is 33 bit expressed as a big-endian eight-octet number, 9368 // with the upper 31 bits set to zero. 9369 var pts33Bit = data[3] & 0x1; 9370 var timestamp = (data[4] << 23) + 9371 (data[5] << 15) + 9372 (data[6] << 7) + 9373 data[7]; 9374 timestamp /= 45; 9375 if (pts33Bit) { 9376 timestamp += 47721858.84; 9377 } // 2^32 / 90 9378 return Math.round(timestamp); 9379 } 9380 return undefined; 9381 }; 9382 ID3._decodePrivFrame = function (frame) { 9383 /* 9384 Format: <text string>\0<binary data> 9385 */
9386 if (frame.size < 2) { 9387 return undefined; 9388 } 9389 var owner = ID3._utf8ArrayToStr(frame.data, true); 9390 var privateData = new Uint8Array(frame.data.subarray(owner.length + 1)); 9391 return { key: frame.type, info: owner, data: privateData.buffer }; 9392 }; 9393 ID3._decodeTextFrame = function (frame) { 9394 if (frame.size < 2) { 9395 return undefined; 9396 } 9397 if (frame.type === 'TXXX') { 9398 /* 9399 Format: 9400 [0] = {Text Encoding} 9401 [1-?] = {Description}\0{Value} 9402 */ 9403 var index = 1; 9404 var description = ID3._utf8ArrayToStr(frame.data.subarray(index)); 9405 index += description.length + 1; 9406 var value = ID3._utf8ArrayToStr(frame.data.subarray(index)); 9407 return { key: frame.type, info: description, data: value }; 9408 } 9409 else { 9410 /* 9411 Format: 9412 [0] = {Text Encoding} 9413 [1-?] = {Value} 9414 */ 9415 var text = ID3._utf8ArrayToStr(frame.data.subarray(1)); 9416 return { key: frame.type, data: text }; 9417 } 9418 }; 9419 ID3._decodeURLFrame = function (frame) { 9420 if (frame.type === 'WXXX') { 9421 /* 9422 Format: 9423 [0] = {Text Encoding} 9424 [1-?] = {Description}\0{URL} 9425 */ 9426 if (frame.size < 2) { 9427 return undefined; 9428 } 9429 var index = 1; 9430 var description = ID3._utf8ArrayToStr(frame.data.subarray(index)); 9431 index += description.length + 1; 9432 var value = ID3._utf8ArrayToStr(frame.data.subarray(index)); 9433 return { key: frame.type, info: description, data: value }; 9434 } 9435 else { 9436 /* 9437 Format: 9438 [0-?] = {URL} 9439 */ 9440 var url = ID3._utf8ArrayToStr(frame.data); 9441 return { key: frame.type, data: url }; 9442 } 9443 }; 9444 // http://stackoverflow.com/questions/8936984/uint8array-to-string-in-javascript/22373197 9445 // http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt 9446 /* utf.js - UTF-8 <=> UTF-16 convertion 9447 * 9448 * Copyright (C) 1999 Masanao Izumo <[email protected]> 9449 * Version: 1.0 9450 * LastModified: Dec 25 1999 9451 * This library is free. You can redistribute it and/or modify it. 9452 */ 9453 ID3._utf8ArrayToStr = function (array, exitOnNull) { 9454 if (exitOnNull === void 0) { exitOnNull = false; } 9455 var len = array.length; 9456 var c; 9457 var char2; 9458 var char3; 9459 var out = ''; 9460 var i = 0; 9461 while (i < len) { 9462 c = array[i++]; 9463 if (c === 0x00 && exitOnNull) { 9464 return out; 9465 } 9466 else if (c === 0x00 || c === 0x03) { 9467 // If the character is 3 (END_OF_TEXT) or 0 (NULL) then skip it 9468 continue; 9469 } 9470 switch (c >> 4) { 9471 case 0: 9472 case 1: 9473 case 2: 9474 case 3: 9475 case 4: 9476 case 5: 9477 case 6: 9478 case 7: 9479 // 0xxxxxxx 9480 out += String.fromCharCode(c); 9481 break; 9482 case 12: 9483 case 13: 9484 // 110x xxxx 10xx xxxx 9485 char2 = array[i++]; 9486 out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F)); 9487 break; 9488 case 14: 9489 // 1110 xxxx 10xx xxxx 10xx xxxx 9490 char2 = array[i++]; 9491 char3 = array[i++]; 9492 out += String.fromCharCode(((c & 0x0F) << 12) | 9493 ((char2 & 0x3F) << 6) | 9494 ((char3 & 0x3F) << 0)); 9495 break; 9496 default: 9497 } 9498 } 9499 return out; 9500 }; 9501 return ID3; 9502}()); 9503var utf8ArrayToStr = ID3._utf8ArrayToStr; 9504exports.utf8ArrayToStr = utf8ArrayToStr; 9505exports.default = ID3; 9506 9507 9508/***/ }), 9509
9510/***/ "./src/demux/mp3demuxer.js": 9511/*!*********************************!*\ 9512 !*** ./src/demux/mp3demuxer.js ***! 9513 \*********************************/ 9514/*! no static exports found */ 9515/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 9516/***/ (function(module, exports, __webpack_require__) { 9517 9518"use strict"; 9519 9520Object.defineProperty(exports, "__esModule", { value: true }); 9521/** 9522 * MP3 demuxer 9523 */ 9524var id3_1 = __webpack_require__(/*! ../demux/id3 */ "./src/demux/id3.js"); 9525var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 9526var mpegaudio_1 = __webpack_require__(/*! ./mpegaudio */ "./src/demux/mpegaudio.js"); 9527var MP3Demuxer = /** @class */ (function () { 9528 function MP3Demuxer(observer, remuxer, config) { 9529 this.observer = observer; 9530 this.config = config; 9531 this.remuxer = remuxer; 9532 } 9533 MP3Demuxer.prototype.resetInitSegment = function (initSegment, audioCodec, videoCodec, duration) { 9534 this._audioTrack = { container: 'audio/mpeg', type: 'audio', id: -1, sequenceNumber: 0, isAAC: false, samples: [], len: 0, manifestCodec: audioCodec, duration: duration, inputTimeScale: 90000 }; 9535 }; 9536 MP3Demuxer.prototype.resetTimeStamp = function () { 9537 }; 9538 MP3Demuxer.probe = function (data) { 9539 // check if data contains ID3 timestamp and MPEG sync word 9540 var offset, length; 9541 var id3Data = id3_1.default.getID3Data(data, 0); 9542 if (id3Data && id3_1.default.getTimeStamp(id3Data) !== undefined) { 9543 // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1 9544 // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) 9545 // More info http://www.mp3-tech.org/programmer/frame_header.html 9546 for (offset = id3Data.length, length = Math.min(data.length - 1, offset + 100); offset < length; offset++) { 9547 if (mpegaudio_1.default.probe(data, offset)) { 9548 logger_1.logger.log('MPEG Audio sync word found !'); 9549 return true; 9550 } 9551 } 9552 } 9553 return false; 9554 }; 9555 // feed incoming data to the front of the parsing pipeline 9556 MP3Demuxer.prototype.append = function (data, timeOffset, contiguous, accurateTimeOffset) { 9557 var id3Data = id3_1.default.getID3Data(data, 0); 9558 var timestamp = id3_1.default.getTimeStamp(id3Data); 9559 var pts = timestamp ? 90 * timestamp : timeOffset * 90000; 9560 var offset = id3Data.length; 9561 var length = data.length; 9562 var frameIndex = 0, stamp = 0; 9563 var track = this._audioTrack; 9564 var id3Samples = [{ pts: pts, dts: pts, data: id3Data }]; 9565 while (offset < length) { 9566 if (mpegaudio_1.default.isHeader(data, offset)) { 9567 var frame = mpegaudio_1.default.appendFrame(track, data, offset, pts, frameIndex); 9568 if (frame) { 9569 offset += frame.length; 9570 stamp = frame.sample.pts; 9571 frameIndex++; 9572 } 9573 else { 9574 // logger.log('Unable to parse Mpeg audio frame'); 9575 break; 9576 } 9577 } 9578 else if (id3_1.default.isHeader(data, offset)) { 9579 id3Data = id3_1.default.getID3Data(data, offset); 9580 id3Samples.push({ pts: stamp, dts: stamp, data: id3Data }); 9581 offset += id3Data.length; 9582 } 9583 else { 9584 // nothing found, keep looking 9585 offset++; 9586 } 9587 } 9588 this.remuxer.remux(track, { samples: [] }, { samples: id3Samples, inputTimeScale: 90000 }, { samples: [] }, timeOffset, contiguous, accurateTimeOffset); 9589 }; 9590 MP3Demuxer.prototype.destroy = function () { 9591 }; 9592 return MP3Demuxer; 9593}()); 9594exports.default = MP3Demuxer; 9595 9596 9597/***/ }), 9598
vendor: 19,843 bytes, lines 9599-10065
9599/***/ "./src/demux/mp4demuxer.js": 9600/*!*********************************!*\ 9601 !*** ./src/demux/mp4demuxer.js ***! 9602 \*********************************/ 9603/*! no static exports found */ 9604/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 9605/***/ (function(module, exports, __webpack_require__) { 9606 9607"use strict"; 9608 9609Object.defineProperty(exports, "__esModule", { value: true }); 9610/** 9611 * MP4 demuxer 9612 */ 9613var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 9614var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 9615var UINT32_MAX = Math.pow(2, 32) - 1; 9616var MP4Demuxer = /** @class */ (function () { 9617 function MP4Demuxer(observer, remuxer) { 9618 this.observer = observer; 9619 this.remuxer = remuxer; 9620 } 9621 MP4Demuxer.prototype.resetTimeStamp = function (initPTS) { 9622 this.initPTS = initPTS; 9623 }; 9624 MP4Demuxer.prototype.resetInitSegment = function (initSegment, audioCodec, videoCodec, duration) { 9625 // jshint unused:false 9626 if (initSegment && initSegment.byteLength) { 9627 var initData = this.initData = MP4Demuxer.parseInitSegment(initSegment); 9628 // default audio codec if nothing specified 9629 // TODO : extract that from initsegment 9630 if (audioCodec == null) { 9631 audioCodec = 'mp4a.40.5'; 9632 } 9633 if (videoCodec == null) { 9634 videoCodec = 'avc1.42e01e'; 9635 } 9636 var tracks = {}; 9637 if (initData.audio && initData.video) { 9638 tracks.audiovideo = { container: 'video/mp4', codec: audioCodec + ',' + videoCodec, initSegment: duration ? initSegment : null }; 9639 } 9640 else { 9641 if (initData.audio) { 9642 tracks.audio = { container: 'audio/mp4', codec: audioCodec, initSegment: duration ? initSegment : null }; 9643 } 9644 if (initData.video) { 9645 tracks.video = { container: 'video/mp4', codec: videoCodec, initSegment: duration ? initSegment : null }; 9646 } 9647 } 9648 this.observer.trigger(events_1.default.FRAG_PARSING_INIT_SEGMENT, { tracks: tracks }); 9649 } 9650 else { 9651 if (audioCodec) { 9652 this.audioCodec = audioCodec; 9653 } 9654 if (videoCodec) { 9655 this.videoCodec = videoCodec; 9656 } 9657 } 9658 }; 9659 MP4Demuxer.probe = function (data) { 9660 // ensure we find a moof box in the first 16 kB 9661 return MP4Demuxer.findBox({ data: data, start: 0, end: Math.min(data.length, 16384) }, ['moof']).length > 0; 9662 }; 9663 MP4Demuxer.bin2str = function (buffer) { 9664 return String.fromCharCode.apply(null, buffer); 9665 }; 9666 MP4Demuxer.readUint16 = function (buffer, offset) { 9667 if (buffer.data) { 9668 offset += buffer.start; 9669 buffer = buffer.data; 9670 } 9671 var val = buffer[offset] << 8 | 9672 buffer[offset + 1]; 9673 return val < 0 ? 65536 + val : val; 9674 }; 9675 MP4Demuxer.readUint32 = function (buffer, offset) { 9676 if (buffer.data) { 9677 offset += buffer.start; 9678 buffer = buffer.data; 9679 } 9680 var val = buffer[offset] << 24 | 9681 buffer[offset + 1] << 16 | 9682 buffer[offset + 2] << 8 | 9683 buffer[offset + 3]; 9684 return val < 0 ? 4294967296 + val : val; 9685 }; 9686 MP4Demuxer.writeUint32 = function (buffer, offset, value) { 9687 if (buffer.data) { 9688 offset += buffer.start; 9689 buffer = buffer.data; 9690 } 9691 buffer[offset] = value >> 24; 9692 buffer[offset + 1] = (value >> 16) & 0xff; 9693 buffer[offset + 2] = (value >> 8) & 0xff; 9694 buffer[offset + 3] = value & 0xff; 9695 }; 9696 // Find the data for a box specified by its path 9697 MP4Demuxer.findBox = function (data, path) { 9698 var results = [], i, size, type, end, subresults, start, endbox; 9699 if (data.data) { 9700 start = data.start; 9701 end = data.end; 9702 data = data.data; 9703 } 9704 else { 9705 start = 0; 9706 end = data.byteLength; 9707 } 9708 if (!path.length) { 9709 // short-circuit the search for empty paths 9710 return null; 9711 } 9712 for (i = start; i < end;) { 9713 size = MP4Demuxer.readUint32(data, i); 9714 type = MP4Demuxer.bin2str(data.subarray(i + 4, i + 8)); 9715 endbox = size > 1 ? i + size : end; 9716 if (type === path[0]) { 9717 if (path.length === 1) { 9718 // this is the end of the path and we've found the box we were 9719 // looking for 9720 results.push({ data: data, start: i + 8, end: endbox }); 9721 } 9722 else { 9723 // recursively search for the next box along the path 9724 subresults = MP4Demuxer.findBox({ data: data, start: i + 8, end: endbox }, path.slice(1)); 9725 if (subresults.length) { 9726 results = results.concat(subresults); 9727 } 9728 } 9729 } 9730 i = endbox; 9731 } 9732 // we've finished searching all of data 9733 return results; 9734 }; 9735 MP4Demuxer.parseSegmentIndex = function (initSegment) { 9736 var moov = MP4Demuxer.findBox(initSegment, ['moov'])[0]; 9737 var moovEndOffset = moov ? moov.end : null; // we need this in case we need to chop of garbage of the end of current data 9738 var index = 0; 9739 var sidx = MP4Demuxer.findBox(initSegment, ['sidx']); 9740 var references; 9741 if (!sidx || !sidx[0]) { 9742 return null; 9743 } 9744 references = []; 9745 sidx = sidx[0]; 9746 var version = sidx.data[0]; 9747 // set initial offset, we skip the reference ID (not needed) 9748 index = version === 0 ? 8 : 16; 9749 var timescale = MP4Demuxer.readUint32(sidx, index); 9750 index += 4; 9751 // TODO: parse earliestPresentationTime and firstOffset 9752 // usually zero in our case 9753 var earliestPresentationTime = 0; 9754 var firstOffset = 0; 9755 if (version === 0) { 9756 index += 8; 9757 } 9758 else { 9759 index += 16; 9760 } 9761 // skip reserved 9762 index += 2; 9763 var startByte = sidx.end + firstOffset; 9764 var referencesCount = MP4Demuxer.readUint16(sidx, index); 9765 index += 2; 9766 for (var i = 0; i < referencesCount; i++) { 9767 var referenceIndex = index; 9768 var referenceInfo = MP4Demuxer.readUint32(sidx, referenceIndex); 9769 referenceIndex += 4; 9770 var referenceSize = referenceInfo & 0x7FFFFFFF; 9771 var referenceType = (referenceInfo & 0x80000000) >>> 31; 9772 if (referenceType === 1) { 9773 console.warn('SIDX has hierarchical references (not supported)'); 9774 return; 9775 } 9776 var subsegmentDuration = MP4Demuxer.readUint32(sidx, referenceIndex); 9777 referenceIndex += 4; 9778 references.push({ 9779 referenceSize: referenceSize, 9780 subsegmentDuration: subsegmentDuration, 9781 info: { 9782 duration: subsegmentDuration / timescale, 9783 start: startByte, 9784 end: startByte + referenceSize - 1 9785 } 9786 }); 9787 startByte += referenceSize; 9788 // Skipping 1 bit for |startsWithSap|, 3 bits for |sapType|, and 28 bits 9789 // for |sapDelta|. 9790 referenceIndex += 4; 9791 // skip to next ref 9792 index = referenceIndex; 9793 } 9794 return { 9795 earliestPresentationTime: earliestPresentationTime, 9796 timescale: timescale, 9797 version: version, 9798 referencesCount: referencesCount, 9799 references: references, 9800 moovEndOffset: moovEndOffset 9801 }; 9802 }; 9803 /** 9804 * Parses an MP4 initialization segment and extracts stream type and 9805 * timescale values for any declared tracks. Timescale values indicate the 9806 * number of clock ticks per second to assume for time-based values 9807 * elsewhere in the MP4. 9808 * 9809 * To determine the start time of an MP4, you need two pieces of 9810 * information: the timescale unit and the earliest base media decode 9811 * time. Multiple timescales can be specified within an MP4 but the 9812 * base media decode time is always expressed in the timescale from 9813 * the media header box for the track: 9814 * ``` 9815 * moov > trak > mdia > mdhd.timescale 9816 * moov > trak > mdia > hdlr 9817 * ``` 9818 * @param init {Uint8Array} the bytes of the init segment 9819 * @return {object} a hash of track type to timescale values or null if 9820 * the init segment is malformed. 9821 */ 9822 MP4Demuxer.parseInitSegment = function (initSegment) { 9823 var result = []; 9824 var traks = MP4Demuxer.findBox(initSegment, ['moov', 'trak']); 9825 traks.forEach(function (trak) { 9826 var tkhd = MP4Demuxer.findBox(trak, ['tkhd'])[0]; 9827 if (tkhd) { 9828 var version = tkhd.data[tkhd.start]; 9829 var index = version === 0 ? 12 : 20; 9830 var trackId = MP4Demuxer.readUint32(tkhd, index); 9831 var mdhd = MP4Demuxer.findBox(trak, ['mdia', 'mdhd'])[0]; 9832 if (mdhd) { 9833 version = mdhd.data[mdhd.start]; 9834 index = version === 0 ? 12 : 20; 9835 var timescale = MP4Demuxer.readUint32(mdhd, index); 9836 var hdlr = MP4Demuxer.findBox(trak, ['mdia', 'hdlr'])[0]; 9837 if (hdlr) { 9838 var hdlrType = MP4Demuxer.bin2str(hdlr.data.subarray(hdlr.start + 8, hdlr.start + 12)); 9839 var type = { 'soun': 'audio', 'vide': 'video' }[hdlrType]; 9840 if (type) { 9841 // extract codec info. TODO : parse codec details to be able to build MIME type 9842 var codecBox = MP4Demuxer.findBox(trak, ['mdia', 'minf', 'stbl', 'stsd']); 9843 if (codecBox.length) { 9844 codecBox = codecBox[0]; 9845 var codecType = MP4Demuxer.bin2str(codecBox.data.subarray(codecBox.start + 12, codecBox.start + 16)); 9846 logger_1.logger.log("MP4Demuxer:" + type + ":" + codecType + " found"); 9847 } 9848 result[trackId] = { timescale: timescale, type: type }; 9849 result[type] = { timescale: timescale, id: trackId }; 9850 } 9851 } 9852 } 9853 } 9854 }); 9855 return result; 9856 }; 9857 /** 9858 * Determine the base media decode start time, in seconds, for an MP4 9859 * fragment. If multiple fragments are specified, the earliest time is 9860 * returned. 9861 * 9862 * The base media decode time can be parsed from track fragment 9863 * metadata: 9864 * ``` 9865 * moof > traf > tfdt.baseMediaDecodeTime 9866 * ``` 9867 * It requires the timescale value from the mdhd to interpret. 9868 * 9869 * @param timescale {object} a hash of track ids to timescale values. 9870 * @return {number} the earliest base media decode start time for the 9871 * fragment, in seconds 9872 */ 9873 MP4Demuxer.getStartDTS = function (initData, fragment) { 9874 var trafs, baseTimes, result; 9875 // we need info from two childrend of each track fragment box 9876 trafs = MP4Demuxer.findBox(fragment, ['moof', 'traf']); 9877 // determine the start times for each track 9878 baseTimes = [].concat.apply([], trafs.map(function (traf) { 9879 return MP4Demuxer.findBox(traf, ['tfhd']).map(function (tfhd) { 9880 var id, scale, baseTime; 9881 // get the track id from the tfhd 9882 id = MP4Demuxer.readUint32(tfhd, 4); 9883 // assume a 90kHz clock if no timescale was specified 9884 scale = initData[id].timescale || 90e3; 9885 // get the base media decode time from the tfdt 9886 baseTime = MP4Demuxer.findBox(traf, ['tfdt']).map(function (tfdt) { 9887 var version, result; 9888 version = tfdt.data[tfdt.start]; 9889 result = MP4Demuxer.readUint32(tfdt, 4); 9890 if (version === 1) { 9891 result *= Math.pow(2, 32); 9892 result += MP4Demuxer.readUint32(tfdt, 8); 9893 } 9894 return result; 9895 })[0]; 9896 // convert base time to seconds 9897 return baseTime / scale; 9898 }); 9899 })); 9900 // return the minimum 9901 result = Math.min.apply(null, baseTimes); 9902 return isFinite(result) ? result : 0; 9903 }; 9904 MP4Demuxer.offsetStartDTS = function (initData, fragment, timeOffset) { 9905 MP4Demuxer.findBox(fragment, ['moof', 'traf']).map(function (traf) { 9906 return MP4Demuxer.findBox(traf, ['tfhd']).map(function (tfhd) { 9907 // get the track id from the tfhd 9908 var id = MP4Demuxer.readUint32(tfhd, 4); 9909 // assume a 90kHz clock if no timescale was specified 9910 var timescale = initData[id].timescale || 90e3; 9911 // get the base media decode time from the tfdt 9912 MP4Demuxer.findBox(traf, ['tfdt']).map(function (tfdt) { 9913 var version = tfdt.data[tfdt.start]; 9914 var baseMediaDecodeTime = MP4Demuxer.readUint32(tfdt, 4); 9915 if (version === 0) { 9916 MP4Demuxer.writeUint32(tfdt, 4, baseMediaDecodeTime - timeOffset * timescale); 9917 } 9918 else { 9919 baseMediaDecodeTime *= Math.pow(2, 32); 9920 baseMediaDecodeTime += MP4Demuxer.readUint32(tfdt, 8); 9921 baseMediaDecodeTime -= timeOffset * timescale; 9922 baseMediaDecodeTime = Math.max(baseMediaDecodeTime, 0); 9923 var upper = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1)); 9924 var lower = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1)); 9925 MP4Demuxer.writeUint32(tfdt, 4, upper); 9926 MP4Demuxer.writeUint32(tfdt, 8, lower); 9927 } 9928 }); 9929 }); 9930 }); 9931 }; 9932 // feed incoming data to the front of the parsing pipeline 9933 MP4Demuxer.prototype.append = function (data, timeOffset, contiguous, accurateTimeOffset) { 9934 var initData = this.initData; 9935 if (!initData) { 9936 this.resetInitSegment(data, this.audioCodec, this.videoCodec, false); 9937 initData = this.initData; 9938 } 9939 var startDTS, initPTS = this.initPTS; 9940 if (initPTS === undefined) { 9941 var startDTS_1 = MP4Demuxer.getStartDTS(initData, data); 9942 this.initPTS = initPTS = startDTS_1 - timeOffset; 9943 this.observer.trigger(events_1.default.INIT_PTS_FOUND, { initPTS: initPTS }); 9944 } 9945 MP4Demuxer.offsetStartDTS(initData, data, initPTS); 9946 startDTS = MP4Demuxer.getStartDTS(initData, data); 9947 this.remuxer.remux(initData.audio, initData.video, null, null, startDTS, contiguous, accurateTimeOffset, data); 9948 }; 9949 MP4Demuxer.prototype.destroy = function () { }; 9950 return MP4Demuxer; 9951}()); 9952exports.default = MP4Demuxer; 9953 9954 9955/***/ }), 9956 9957/***/ "./src/demux/mpegaudio.js": 9958/*!********************************!*\ 9959 !*** ./src/demux/mpegaudio.js ***! 9960 \********************************/ 9961/*! no static exports found */ 9962/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 9963/***/ (function(module, exports, __webpack_require__) { 9964 9965"use strict"; 9966 9967/** 9968 * MPEG parser helper 9969 */ 9970Object.defineProperty(exports, "__esModule", { value: true }); 9971var MpegAudio = { 9972 BitratesMap: [ 9973 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 9974 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 9975 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 9976 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 9977 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160 9978 ], 9979 SamplingRateMap: [44100, 48000, 32000, 22050, 24000, 16000, 11025, 12000, 8000], 9980 SamplesCoefficients: [ 9981 // MPEG 2.5 9982 [ 9983 0, 9984 72, 9985 144, 9986 12 // Layer1 9987 ], 9988 // Reserved 9989 [ 9990 0, 9991 0, 9992 0, 9993 0 // Layer1 9994 ], 9995 // MPEG 2 9996 [ 9997 0, 9998 72, 9999 144, 10000 12 // Layer1 10001 ], 10002 // MPEG 1 10003 [ 10004 0, 10005 144, 10006 144, 10007 12 // Layer1 10008 ] 10009 ], 10010 BytesInSlot: [ 10011 0, 10012 1, 10013 1, 10014 4 // Layer1 10015 ], 10016 appendFrame: function (track, data, offset, pts, frameIndex) { 10017 // Using http://www.datavoyage.com/mpgscript/mpeghdr.htm as a reference 10018 if (offset + 24 > data.length) { 10019 return undefined; 10020 } 10021 var header = this.parseHeader(data, offset); 10022 if (header && offset + header.frameLength <= data.length) { 10023 var frameDuration = header.samplesPerFrame * 90000 / header.sampleRate; 10024 var stamp = pts + frameIndex * frameDuration; 10025 var sample = { unit: data.subarray(offset, offset + header.frameLength), pts: stamp, dts: stamp }; 10026 track.config = []; 10027 track.channelCount = header.channelCount; 10028 track.samplerate = header.sampleRate; 10029 track.samples.push(sample); 10030 track.len += header.frameLength; 10031 return { sample: sample, length: header.frameLength }; 10032 } 10033 return undefined; 10034 }, 10035 parseHeader: function (data, offset) { 10036 var headerB = (data[offset + 1] >> 3) & 3; 10037 var headerC = (data[offset + 1] >> 1) & 3; 10038 var headerE = (data[offset + 2] >> 4) & 15; 10039 var headerF = (data[offset + 2] >> 2) & 3; 10040 var headerG = (data[offset + 2] >> 1) & 1; 10041 if (headerB !== 1 && headerE !== 0 && headerE !== 15 && headerF !== 3) { 10042 var columnInBitrates = headerB === 3 ? (3 - headerC) : (headerC === 3 ? 3 : 4); 10043 var bitRate = MpegAudio.BitratesMap[columnInBitrates * 14 + headerE - 1] * 1000; 10044 var columnInSampleRates = headerB === 3 ? 0 : headerB === 2 ? 1 : 2; 10045 var sampleRate = MpegAudio.SamplingRateMap[columnInSampleRates * 3 + headerF]; 10046 var channelCount = data[offset + 3] >> 6 === 3 ? 1 : 2; // If bits of channel mode are `11` then it is a single channel (Mono) 10047 var sampleCoefficient = MpegAudio.SamplesCoefficients[headerB][headerC]; 10048 var bytesInSlot = MpegAudio.BytesInSlot[headerC]; 10049 var samplesPerFrame = sampleCoefficient * 8 * bytesInSlot; 10050 var frameLength = parseInt(sampleCoefficient * bitRate / sampleRate + headerG, 10) * bytesInSlot; 10051 return { sampleRate: sampleRate, channelCount: channelCount, frameLength: frameLength, samplesPerFrame: samplesPerFrame }; 10052 } 10053 return undefined; 10054 }, 10055 isHeaderPattern: function (data, offset) { 10056 return data[offset] === 0xff && (data[offset + 1] & 0xe0) === 0xe0 && (data[offset + 1] & 0x06) !== 0x00; 10057 }, 10058 isHeader: function (data, offset) { 10059 // Look for MPEG header | 1111 1111 | 111X XYZX | where X can be either 0 or 1 and Y or Z should be 1 10060 // Layer bits (position 14 and 15) in header should be always different from 0 (Layer I or Layer II or Layer III) 10061 // More info http://www.mp3-tech.org/programmer/frame_header.html 10062 if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) { 10063 return true; 10064 } 10065 return false;
10066 }, 10067 probe: function (data, offset) { 10068 // same as isHeader but we also check that MPEG frame follows last MPEG frame 10069 // or end of data is reached 10070 if (offset + 1 < data.length && this.isHeaderPattern(data, offset)) { 10071 // MPEG header Length 10072 var headerLength = 4; 10073 // MPEG frame Length 10074 var header = this.parseHeader(data, offset); 10075 var frameLength = headerLength; 10076 if (header && header.frameLength) { 10077 frameLength = header.frameLength; 10078 } 10079 var newOffset = offset + frameLength; 10080 if (newOffset === data.length || (newOffset + 1 < data.length && this.isHeaderPattern(data, newOffset))) { 10081 return true; 10082 } 10083 } 10084 return false; 10085 } 10086}; 10087exports.default = MpegAudio; 10088 10089 10090/***/ }), 10091
vendor: 45,603 bytes, lines 10092-11073
10092/***/ "./src/demux/sample-aes.js": 10093/*!*********************************!*\ 10094 !*** ./src/demux/sample-aes.js ***! 10095 \*********************************/ 10096/*! no static exports found */ 10097/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 10098/***/ (function(module, exports, __webpack_require__) { 10099 10100"use strict"; 10101 10102/** 10103 * SAMPLE-AES decrypter 10104*/ 10105Object.defineProperty(exports, "__esModule", { value: true }); 10106var decrypter_1 = __webpack_require__(/*! ../crypt/decrypter */ "./src/crypt/decrypter.js"); 10107var SampleAesDecrypter = /** @class */ (function () { 10108 function SampleAesDecrypter(observer, config, decryptdata, discardEPB) { 10109 this.decryptdata = decryptdata; 10110 this.discardEPB = discardEPB; 10111 this.decrypter = new decrypter_1.default(observer, config, { removePKCS7Padding: false }); 10112 } 10113 SampleAesDecrypter.prototype.decryptBuffer = function (encryptedData, callback) { 10114 this.decrypter.decrypt(encryptedData, this.decryptdata.key.buffer, this.decryptdata.iv.buffer, callback); 10115 }; 10116 // AAC - encrypt all full 16 bytes blocks starting from offset 16 10117 SampleAesDecrypter.prototype.decryptAacSample = function (samples, sampleIndex, callback, sync) { 10118 var curUnit = samples[sampleIndex].unit; 10119 var encryptedData = curUnit.subarray(16, curUnit.length - curUnit.length % 16); 10120 var encryptedBuffer = encryptedData.buffer.slice(encryptedData.byteOffset, encryptedData.byteOffset + encryptedData.length); 10121 var localthis = this; 10122 this.decryptBuffer(encryptedBuffer, function (decryptedData) { 10123 decryptedData = new Uint8Array(decryptedData); 10124 curUnit.set(decryptedData, 16); 10125 if (!sync) { 10126 localthis.decryptAacSamples(samples, sampleIndex + 1, callback); 10127 } 10128 }); 10129 }; 10130 SampleAesDecrypter.prototype.decryptAacSamples = function (samples, sampleIndex, callback) { 10131 for (;; sampleIndex++) { 10132 if (sampleIndex >= samples.length) { 10133 callback(); 10134 return; 10135 } 10136 if (samples[sampleIndex].unit.length < 32) { 10137 continue; 10138 } 10139 var sync = this.decrypter.isSync(); 10140 this.decryptAacSample(samples, sampleIndex, callback, sync); 10141 if (!sync) { 10142 return; 10143 } 10144 } 10145 }; 10146 // AVC - encrypt one 16 bytes block out of ten, starting from offset 32 10147 SampleAesDecrypter.prototype.getAvcEncryptedData = function (decodedData) { 10148 var encryptedDataLen = Math.floor((decodedData.length - 48) / 160) * 16 + 16; 10149 var encryptedData = new Int8Array(encryptedDataLen); 10150 var outputPos = 0; 10151 for (var inputPos = 32; inputPos <= decodedData.length - 16; inputPos += 160, outputPos += 16) { 10152 encryptedData.set(decodedData.subarray(inputPos, inputPos + 16), outputPos); 10153 } 10154 return encryptedData; 10155 }; 10156 SampleAesDecrypter.prototype.getAvcDecryptedUnit = function (decodedData, decryptedData) { 10157 decryptedData = new Uint8Array(decryptedData); 10158 var inputPos = 0; 10159 for (var outputPos = 32; outputPos <= decodedData.length - 16; outputPos += 160, inputPos += 16) { 10160 decodedData.set(decryptedData.subarray(inputPos, inputPos + 16), outputPos); 10161 } 10162 return decodedData; 10163 }; 10164 SampleAesDecrypter.prototype.decryptAvcSample = function (samples, sampleIndex, unitIndex, callback, curUnit, sync) { 10165 var decodedData = this.discardEPB(curUnit.data); 10166 var encryptedData = this.getAvcEncryptedData(decodedData); 10167 var localthis = this; 10168 this.decryptBuffer(encryptedData.buffer, function (decryptedData) { 10169 curUnit.data = localthis.getAvcDecryptedUnit(decodedData, decryptedData); 10170 if (!sync) { 10171 localthis.decryptAvcSamples(samples, sampleIndex, unitIndex + 1, callback); 10172 } 10173 }); 10174 }; 10175 SampleAesDecrypter.prototype.decryptAvcSamples = function (samples, sampleIndex, unitIndex, callback) { 10176 for (;; sampleIndex++, unitIndex = 0) { 10177 if (sampleIndex >= samples.length) { 10178 callback(); 10179 return; 10180 } 10181 var curUnits = samples[sampleIndex].units; 10182 for (;; unitIndex++) { 10183 if (unitIndex >= curUnits.length) { 10184 break; 10185 } 10186 var curUnit = curUnits[unitIndex]; 10187 if (curUnit.length <= 48 || (curUnit.type !== 1 && curUnit.type !== 5)) { 10188 continue; 10189 } 10190 var sync = this.decrypter.isSync(); 10191 this.decryptAvcSample(samples, sampleIndex, unitIndex, callback, curUnit, sync); 10192 if (!sync) { 10193 return; 10194 } 10195 } 10196 } 10197 }; 10198 return SampleAesDecrypter; 10199}()); 10200exports.default = SampleAesDecrypter; 10201 10202 10203/***/ }), 10204 10205/***/ "./src/demux/tsdemuxer.js": 10206/*!********************************!*\ 10207 !*** ./src/demux/tsdemuxer.js ***! 10208 \********************************/ 10209/*! no static exports found */ 10210/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 10211/***/ (function(module, exports, __webpack_require__) { 10212 10213"use strict"; 10214 10215/** 10216 * highly optimized TS demuxer: 10217 * parse PAT, PMT 10218 * extract PES packet from audio and video PIDs 10219 * extract AVC/H264 NAL units and AAC/ADTS samples from PES packet 10220 * trigger the remuxer upon parsing completion 10221 * it also tries to workaround as best as it can audio codec switch (HE-AAC to AAC and vice versa), without having to restart the MediaSource. 10222 * it also controls the remuxing process : 10223 * upon discontinuity or level switch detection, it will also notifies the remuxer so that it can reset its state. 10224*/ 10225Object.defineProperty(exports, "__esModule", { value: true }); 10226var ADTS = __webpack_require__(/*! ./adts */ "./src/demux/adts.js"); 10227var mpegaudio_1 = __webpack_require__(/*! ./mpegaudio */ "./src/demux/mpegaudio.js"); 10228var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 10229var exp_golomb_1 = __webpack_require__(/*! ./exp-golomb */ "./src/demux/exp-golomb.js"); 10230var sample_aes_1 = __webpack_require__(/*! ./sample-aes */ "./src/demux/sample-aes.js"); 10231// import Hex from '../utils/hex'; 10232var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 10233var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 10234// We are using fixed track IDs for driving the MP4 remuxer 10235// instead of following the TS PIDs. 10236// There is no reason not to do this and some browsers/SourceBuffer-demuxers 10237// may not like if there are TrackID "switches" 10238// See https://github.com/video-dev/hls.js/issues/1331 10239// Here we are mapping our internal track types to constant MP4 track IDs 10240// With MSE currently one can only have one track of each, and we are muxing 10241// whatever video/audio rendition in them. 10242var RemuxerTrackIdConfig = { 10243 video: 1, 10244 audio: 2, 10245 id3: 3, 10246 text: 4 10247}; 10248var TSDemuxer = /** @class */ (function () { 10249 function TSDemuxer(observer, remuxer, config, typeSupported) { 10250 this.observer = observer; 10251 this.config = config; 10252 this.typeSupported = typeSupported; 10253 this.remuxer = remuxer; 10254 this.sampleAes = null; 10255 } 10256 TSDemuxer.prototype.setDecryptData = function (decryptdata) { 10257 if ((decryptdata != null) && (decryptdata.key != null) && (decryptdata.method === 'SAMPLE-AES')) { 10258 this.sampleAes = new sample_aes_1.default(this.observer, this.config, decryptdata, this.discardEPB); 10259 } 10260 else { 10261 this.sampleAes = null; 10262 } 10263 }; 10264 TSDemuxer.probe = function (data) { 10265 var syncOffset = TSDemuxer._syncOffset(data); 10266 if (syncOffset < 0) { 10267 return false; 10268 } 10269 else { 10270 if (syncOffset) { 10271 logger_1.logger.warn("MPEG2-TS detected but first sync word found @ offset " + syncOffset + ", junk ahead ?"); 10272 } 10273 return true; 10274 } 10275 }; 10276 TSDemuxer._syncOffset = function (data) { 10277 // scan 1000 first bytes 10278 var scanwindow = Math.min(1000, data.length - 3 * 188); 10279 var i = 0; 10280 while (i < scanwindow) { 10281 // a TS fragment should contain at least 3 TS packets, a PAT, a PMT, and one PID, each starting with 0x47 10282 if (data[i] === 0x47 && data[i + 188] === 0x47 && data[i + 2 * 188] === 0x47) { 10283 return i; 10284 } 10285 else { 10286 i++; 10287 } 10288 } 10289 return -1; 10290 }; 10291 /** 10292 * Creates a track model internal to demuxer used to drive remuxing input 10293 * 10294 * @param {string} type 'audio' | 'video' | 'id3' | 'text' 10295 * @param {number} duration 10296 * @return {object} TSDemuxer's internal track model 10297 */ 10298 TSDemuxer.createTrack = function (type, duration) { 10299 return { 10300 container: type === 'video' || type === 'audio' ? 'video/mp2t' : undefined, 10301 type: type, 10302 id: RemuxerTrackIdConfig[type], 10303 pid: -1, 10304 inputTimeScale: 90000, 10305 sequenceNumber: 0, 10306 samples: [], 10307 len: 0, 10308 dropped: type === 'video' ? 0 : undefined, 10309 isAAC: type === 'audio' ? true : undefined, 10310 duration: type === 'audio' ? duration : undefined 10311 }; 10312 }; 10313 /** 10314 * Initializes a new init segment on the demuxer/remuxer interface. Needed for discontinuities/track-switches (or at stream start) 10315 * Resets all internal track instances of the demuxer. 10316 * 10317 * @override Implements generic demuxing/remuxing interface (see DemuxerInline) 10318 * @param {object} initSegment 10319 * @param {string} audioCodec 10320 * @param {string} videoCodec 10321 * @param {number} duration (in TS timescale = 90kHz) 10322 */ 10323 TSDemuxer.prototype.resetInitSegment = function (initSegment, audioCodec, videoCodec, duration) { 10324 this.pmtParsed = false; 10325 this._pmtId = -1; 10326 this._avcTrack = TSDemuxer.createTrack('video', duration); 10327 this._audioTrack = TSDemuxer.createTrack('audio', duration); 10328 this._id3Track = TSDemuxer.createTrack('id3', duration); 10329 this._txtTrack = TSDemuxer.createTrack('text', duration); 10330 // flush any partial content 10331 this.aacOverFlow = null; 10332 this.aacLastPTS = null; 10333 this.avcSample = null; 10334 this.audioCodec = audioCodec; 10335 this.videoCodec = videoCodec; 10336 this._duration = duration; 10337 }; 10338 /** 10339 * 10340 * @override 10341 */ 10342 TSDemuxer.prototype.resetTimeStamp = function () { }; 10343 // feed incoming data to the front of the parsing pipeline 10344 TSDemuxer.prototype.append = function (data, timeOffset, contiguous, accurateTimeOffset) { 10345 var start, len = data.length, stt, pid, atf, offset, pes, unknownPIDs = false; 10346 this.contiguous = contiguous; 10347 var pmtParsed = this.pmtParsed, avcTrack = this._avcTrack, audioTrack = this._audioTrack, id3Track = this._id3Track, avcId = avcTrack.pid, audioId = audioTrack.pid, id3Id = id3Track.pid, pmtId = this._pmtId, avcData = avcTrack.pesData, audioData = audioTrack.pesData, id3Data = id3Track.pesData, parsePAT = this._parsePAT, parsePMT = this._parsePMT, parsePES = this._parsePES, parseAVCPES = this._parseAVCPES.bind(this), parseAACPES = this._parseAACPES.bind(this), parseMPEGPES = this._parseMPEGPES.bind(this), parseID3PES = this._parseID3PES.bind(this); 10348 var syncOffset = TSDemuxer._syncOffset(data); 10349 // don't parse last TS packet if incomplete 10350 len -= (len + syncOffset) % 188; 10351 // loop through TS packets 10352 for (start = syncOffset; start < len; start += 188) { 10353 if (data[start] === 0x47) { 10354 stt = !!(data[start + 1] & 0x40); 10355 // pid is a 13-bit field starting at the last bit of TS[1] 10356 pid = ((data[start + 1] & 0x1f) << 8) + data[start + 2]; 10357 atf = (data[start + 3] & 0x30) >> 4; 10358 // if an adaption field is present, its length is specified by the fifth byte of the TS packet header. 10359 if (atf > 1) { 10360 offset = start + 5 + data[start + 4]; 10361 // continue if there is only adaptation field 10362 if (offset === (start + 188)) { 10363 continue; 10364 } 10365 } 10366 else { 10367 offset = start + 4; 10368 } 10369 switch (pid) { 10370 case avcId: 10371 if (stt) { 10372 if (avcData && (pes = parsePES(avcData)) && pes.pts !== undefined) { 10373 parseAVCPES(pes, false); 10374 } 10375 avcData = { data: [], size: 0 }; 10376 } 10377 if (avcData) { 10378 avcData.data.push(data.subarray(offset, start + 188)); 10379 avcData.size += start + 188 - offset; 10380 } 10381 break; 10382 case audioId: 10383 if (stt) { 10384 if (audioData && (pes = parsePES(audioData)) && pes.pts !== undefined) { 10385 if (audioTrack.isAAC) { 10386 parseAACPES(pes); 10387 } 10388 else { 10389 parseMPEGPES(pes); 10390 } 10391 } 10392 audioData = { data: [], size: 0 }; 10393 } 10394 if (audioData) { 10395 audioData.data.push(data.subarray(offset, start + 188)); 10396 audioData.size += start + 188 - offset; 10397 } 10398 break; 10399 case id3Id: 10400 if (stt) { 10401 if (id3Data && (pes = parsePES(id3Data)) && pes.pts !== undefined) { 10402 parseID3PES(pes); 10403 } 10404 id3Data = { data: [], size: 0 }; 10405 } 10406 if (id3Data) { 10407 id3Data.data.push(data.subarray(offset, start + 188)); 10408 id3Data.size += start + 188 - offset; 10409 } 10410 break; 10411 case 0: 10412 if (stt) { 10413 offset += data[offset] + 1; 10414 } 10415 pmtId = this._pmtId = parsePAT(data, offset); 10416 break; 10417 case pmtId: 10418 if (stt) { 10419 offset += data[offset] + 1; 10420 } 10421 var parsedPIDs = parsePMT(data, offset, this.typeSupported.mpeg === true || this.typeSupported.mp3 === true, this.sampleAes != null); 10422 // only update track id if track PID found while parsing PMT 10423 // this is to avoid resetting the PID to -1 in case 10424 // track PID transiently disappears from the stream 10425 // this could happen in case of transient missing audio samples for example 10426 // NOTE this is only the PID of the track as found in TS, 10427 // but we are not using this for MP4 track IDs. 10428 avcId = parsedPIDs.avc; 10429 if (avcId > 0) { 10430 avcTrack.pid = avcId; 10431 } 10432 audioId = parsedPIDs.audio; 10433 if (audioId > 0) { 10434 audioTrack.pid = audioId; 10435 audioTrack.isAAC = parsedPIDs.isAAC; 10436 } 10437 id3Id = parsedPIDs.id3; 10438 if (id3Id > 0) { 10439 id3Track.pid = id3Id; 10440 } 10441 if (unknownPIDs && !pmtParsed) { 10442 logger_1.logger.log('reparse from beginning'); 10443 unknownPIDs = false; 10444 // we set it to -188, the += 188 in the for loop will reset start to 0 10445 start = syncOffset - 188; 10446 } 10447 pmtParsed = this.pmtParsed = true; 10448 break; 10449 case 17: 10450 case 0x1fff: 10451 break; 10452 default: 10453 unknownPIDs = true; 10454 break; 10455 } 10456 } 10457 else { 10458 this.observer.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.MEDIA_ERROR, details: errors_1.ErrorDetails.FRAG_PARSING_ERROR, fatal: false, reason: 'TS packet did not start with 0x47' }); 10459 } 10460 } 10461 // try to parse last PES packets 10462 if (avcData && (pes = parsePES(avcData)) && pes.pts !== undefined) { 10463 parseAVCPES(pes, true); 10464 avcTrack.pesData = null; 10465 } 10466 else { 10467 // either avcData null or PES truncated, keep it for next frag parsing 10468 avcTrack.pesData = avcData; 10469 } 10470 if (audioData && (pes = parsePES(audioData)) && pes.pts !== undefined) { 10471 if (audioTrack.isAAC) { 10472 parseAACPES(pes); 10473 } 10474 else { 10475 parseMPEGPES(pes); 10476 } 10477 audioTrack.pesData = null; 10478 } 10479 else { 10480 if (audioData && audioData.size) { 10481 logger_1.logger.log('last AAC PES packet truncated,might overlap between fragments'); 10482 } 10483 // either audioData null or PES truncated, keep it for next frag parsing 10484 audioTrack.pesData = audioData; 10485 } 10486 if (id3Data && (pes = parsePES(id3Data)) && pes.pts !== undefined) { 10487 parseID3PES(pes); 10488 id3Track.pesData = null; 10489 } 10490 else { 10491 // either id3Data null or PES truncated, keep it for next frag parsing 10492 id3Track.pesData = id3Data; 10493 } 10494 if (this.sampleAes == null) { 10495 this.remuxer.remux(audioTrack, avcTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset); 10496 } 10497 else { 10498 this.decryptAndRemux(audioTrack, avcTrack, id3Track, this._txtTrack, timeOffset, contiguous, accurateTimeOffset); 10499 } 10500 }; 10501 TSDemuxer.prototype.decryptAndRemux = function (audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) { 10502 if (audioTrack.samples && audioTrack.isAAC) { 10503 var localthis_1 = this; 10504 this.sampleAes.decryptAacSamples(audioTrack.samples, 0, function () { 10505 localthis_1.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset); 10506 }); 10507 } 10508 else { 10509 this.decryptAndRemuxAvc(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset); 10510 } 10511 }; 10512 TSDemuxer.prototype.decryptAndRemuxAvc = function (audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) { 10513 if (videoTrack.samples) { 10514 var localthis_2 = this; 10515 this.sampleAes.decryptAvcSamples(videoTrack.samples, 0, 0, function () { 10516 localthis_2.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset); 10517 }); 10518 } 10519 else { 10520 this.remuxer.remux(audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset); 10521 } 10522 }; 10523 TSDemuxer.prototype.destroy = function () { 10524 this._initPTS = this._initDTS = undefined; 10525 this._duration = 0; 10526 }; 10527 TSDemuxer.prototype._parsePAT = function (data, offset) { 10528 // skip the PSI header and parse the first PMT entry 10529 return (data[offset + 10] & 0x1F) << 8 | data[offset + 11]; 10530 // logger.log('PMT PID:' + this._pmtId); 10531 }; 10532 TSDemuxer.prototype._parsePMT = function (data, offset, mpegSupported, isSampleAes) { 10533 var sectionLength, tableEnd, programInfoLength, pid, result = { audio: -1, avc: -1, id3: -1, isAAC: true }; 10534 sectionLength = (data[offset + 1] & 0x0f) << 8 | data[offset + 2]; 10535 tableEnd = offset + 3 + sectionLength - 4; 10536 // to determine where the table is, we have to figure out how 10537 // long the program info descriptors are 10538 programInfoLength = (data[offset + 10] & 0x0f) << 8 | data[offset + 11]; 10539 // advance the offset to the first entry in the mapping table 10540 offset += 12 + programInfoLength; 10541 while (offset < tableEnd) { 10542 pid = (data[offset + 1] & 0x1F) << 8 | data[offset + 2]; 10543 switch (data[offset]) { 10544 case 0xcf: // SAMPLE-AES AAC 10545 if (!isSampleAes) { 10546 logger_1.logger.log('unkown stream type:' + data[offset]); 10547 break; 10548 } 10549 /* falls through */ 10550 // ISO/IEC 13818-7 ADTS AAC (MPEG-2 lower bit-rate audio) 10551 case 0x0f: 10552 // logger.log('AAC PID:' + pid); 10553 if (result.audio === -1) { 10554 result.audio = pid; 10555 } 10556 break; 10557 // Packetized metadata (ID3) 10558 case 0x15: 10559 // logger.log('ID3 PID:' + pid); 10560 if (result.id3 === -1) { 10561 result.id3 = pid; 10562 } 10563 break; 10564 case 0xdb: // SAMPLE-AES AVC 10565 if (!isSampleAes) { 10566 logger_1.logger.log('unkown stream type:' + data[offset]); 10567 break; 10568 } 10569 /* falls through */ 10570 // ITU-T Rec. H.264 and ISO/IEC 14496-10 (lower bit-rate video) 10571 case 0x1b: 10572 // logger.log('AVC PID:' + pid); 10573 if (result.avc === -1) { 10574 result.avc = pid; 10575 } 10576 break; 10577 // ISO/IEC 11172-3 (MPEG-1 audio) 10578 // or ISO/IEC 13818-3 (MPEG-2 halved sample rate audio) 10579 case 0x03: 10580 case 0x04: 10581 // logger.log('MPEG PID:' + pid); 10582 if (!mpegSupported) { 10583 logger_1.logger.log('MPEG audio found, not supported in this browser for now'); 10584 } 10585 else if (result.audio === -1) { 10586 result.audio = pid; 10587 result.isAAC = false; 10588 } 10589 break; 10590 case 0x24: 10591 logger_1.logger.warn('HEVC stream type found, not supported for now'); 10592 break; 10593 default: 10594 logger_1.logger.log('unkown stream type:' + data[offset]); 10595 break; 10596 } 10597 // move to the next table entry 10598 // skip past the elementary stream descriptors, if present 10599 offset += ((data[offset + 3] & 0x0F) << 8 | data[offset + 4]) + 5; 10600 } 10601 return result; 10602 }; 10603 TSDemuxer.prototype._parsePES = function (stream) { 10604 var i = 0, frag, pesFlags, pesPrefix, pesLen, pesHdrLen, pesData, pesPts, pesDts, payloadStartOffset, data = stream.data; 10605 // safety check 10606 if (!stream || stream.size === 0) { 10607 return null; 10608 } 10609 // we might need up to 19 bytes to read PES header 10610 // if first chunk of data is less than 19 bytes, let's merge it with following ones until we get 19 bytes 10611 // usually only one merge is needed (and this is rare ...) 10612 while (data[0].length < 19 && data.length > 1) { 10613 var newData = new Uint8Array(data[0].length + data[1].length); 10614 newData.set(data[0]); 10615 newData.set(data[1], data[0].length); 10616 data[0] = newData; 10617 data.splice(1, 1); 10618 } 10619 // retrieve PTS/DTS from first fragment 10620 frag = data[0]; 10621 pesPrefix = (frag[0] << 16) + (frag[1] << 8) + frag[2]; 10622 if (pesPrefix === 1) { 10623 pesLen = (frag[4] << 8) + frag[5]; 10624 // if PES parsed length is not zero and greater than total received length, stop parsing. PES might be truncated 10625 // minus 6 : PES header size 10626 if (pesLen && pesLen > stream.size - 6) { 10627 return null; 10628 } 10629 pesFlags = frag[7]; 10630 if (pesFlags & 0xC0) { 10631 /* PES header described here : http://dvd.sourceforge.net/dvdinfo/pes-hdr.html 10632 as PTS / DTS is 33 bit we cannot use bitwise operator in JS, 10633 as Bitwise operators treat their operands as a sequence of 32 bits */ 10634 pesPts = (frag[9] & 0x0E) * 536870912 + // 1 << 29 10635 (frag[10] & 0xFF) * 4194304 + // 1 << 22 10636 (frag[11] & 0xFE) * 16384 + // 1 << 14 10637 (frag[12] & 0xFF) * 128 + // 1 << 7 10638 (frag[13] & 0xFE) / 2; 10639 // check if greater than 2^32 -1 10640 if (pesPts > 4294967295) { 10641 // decrement 2^33 10642 pesPts -= 8589934592; 10643 } 10644 if (pesFlags & 0x40) { 10645 pesDts = (frag[14] & 0x0E) * 536870912 + // 1 << 29 10646 (frag[15] & 0xFF) * 4194304 + // 1 << 22 10647 (frag[16] & 0xFE) * 16384 + // 1 << 14 10648 (frag[17] & 0xFF) * 128 + // 1 << 7 10649 (frag[18] & 0xFE) / 2; 10650 // check if greater than 2^32 -1 10651 if (pesDts > 4294967295) { 10652 // decrement 2^33 10653 pesDts -= 8589934592; 10654 } 10655 if (pesPts - pesDts > 60 * 90000) { 10656 logger_1.logger.warn(Math.round((pesPts - pesDts) / 90000) + "s delta between PTS and DTS, align them"); 10657 pesPts = pesDts; 10658 } 10659 } 10660 else { 10661 pesDts = pesPts; 10662 } 10663 } 10664 pesHdrLen = frag[8]; 10665 // 9 bytes : 6 bytes for PES header + 3 bytes for PES extension 10666 payloadStartOffset = pesHdrLen + 9; 10667 stream.size -= payloadStartOffset; 10668 // reassemble PES packet 10669 pesData = new Uint8Array(stream.size); 10670 for (var j = 0, dataLen = data.length; j < dataLen; j++) { 10671 frag = data[j]; 10672 var len = frag.byteLength; 10673 if (payloadStartOffset) { 10674 if (payloadStartOffset > len) { 10675 // trim full frag if PES header bigger than frag 10676 payloadStartOffset -= len; 10677 continue; 10678 } 10679 else { 10680 // trim partial frag if PES header smaller than frag 10681 frag = frag.subarray(payloadStartOffset); 10682 len -= payloadStartOffset; 10683 payloadStartOffset = 0; 10684 } 10685 } 10686 pesData.set(frag, i); 10687 i += len; 10688 } 10689 if (pesLen) { 10690 // payload size : remove PES header + PES extension 10691 pesLen -= pesHdrLen + 3; 10692 } 10693 return { data: pesData, pts: pesPts, dts: pesDts, len: pesLen }; 10694 } 10695 else { 10696 return null; 10697 } 10698 }; 10699 TSDemuxer.prototype.pushAccesUnit = function (avcSample, avcTrack) { 10700 if (avcSample.units.length && avcSample.frame) { 10701 var samples = avcTrack.samples; 10702 var nbSamples = samples.length; 10703 // only push AVC sample if starting with a keyframe is not mandatory OR 10704 // if keyframe already found in this fragment OR 10705 // keyframe found in last fragment (track.sps) AND 10706 // samples already appended (we already found a keyframe in this fragment) OR fragment is contiguous 10707 if (!this.config.forceKeyFrameOnDiscontinuity || 10708 avcSample.key === true || 10709 (avcTrack.sps && (nbSamples || this.contiguous))) { 10710 avcSample.id = nbSamples; 10711 samples.push(avcSample); 10712 } 10713 else { 10714 // dropped samples, track it 10715 avcTrack.dropped++; 10716 } 10717 } 10718 if (avcSample.debug.length) { 10719 logger_1.logger.log(avcSample.pts + '/' + avcSample.dts + ':' + avcSample.debug); 10720 } 10721 }; 10722 TSDemuxer.prototype._parseAVCPES = function (pes, last) { 10723 var _this = this; 10724 // logger.log('parse new PES'); 10725 var track = this._avcTrack, units = this._parseAVCNALu(pes.data), debug = false, expGolombDecoder, avcSample = this.avcSample, push, spsfound = false, i, pushAccesUnit = this.pushAccesUnit.bind(this), createAVCSample = function (key, pts, dts, debug) { 10726 return { key: key, pts: pts, dts: dts, units: [], debug: debug }; 10727 }; 10728 // free pes.data to save up some memory 10729 pes.data = null; 10730 // if new NAL units found and last sample still there, let's push ... 10731 // this helps parsing streams with missing AUD (only do this if AUD never found) 10732 if (avcSample && units.length && !track.audFound) { 10733 pushAccesUnit(avcSample, track); 10734 avcSample = this.avcSample = createAVCSample(false, pes.pts, pes.dts, ''); 10735 } 10736 units.forEach(function (unit) { 10737 switch (unit.type) { 10738 // NDR 10739 case 1: 10740 push = true; 10741 if (!avcSample) { 10742 avcSample = _this.avcSample = createAVCSample(true, pes.pts, pes.dts, ''); 10743 } 10744 if (debug) { 10745 avcSample.debug += 'NDR '; 10746 } 10747 avcSample.frame = true; 10748 var data = unit.data; 10749 // only check slice type to detect KF in case SPS found in same packet (any keyframe is preceded by SPS ...) 10750 if (spsfound && data.length > 4) { 10751 // retrieve slice type by parsing beginning of NAL unit (follow H264 spec, slice_header definition) to detect keyframe embedded in NDR 10752 var sliceType = new exp_golomb_1.default(data).readSliceType(); 10753 // 2 : I slice, 4 : SI slice, 7 : I slice, 9: SI slice 10754 // SI slice : A slice that is coded using intra prediction only and using quantisation of the prediction samples. 10755 // An SI slice can be coded such that its decoded samples can be constructed identically to an SP slice. 10756 // I slice: A slice that is not an SI slice that is decoded using intra prediction only. 10757 // if (sliceType === 2 || sliceType === 7) { 10758 if (sliceType === 2 || sliceType === 4 || sliceType === 7 || sliceType === 9) { 10759 avcSample.key = true; 10760 } 10761 } 10762 break; 10763 // IDR 10764 case 5: 10765 push = true; 10766 // handle PES not starting with AUD 10767 if (!avcSample) { 10768 avcSample = _this.avcSample = createAVCSample(true, pes.pts, pes.dts, ''); 10769 } 10770 if (debug) { 10771 avcSample.debug += 'IDR '; 10772 } 10773 avcSample.key = true; 10774 avcSample.frame = true; 10775 break; 10776 // SEI 10777 case 6: 10778 push = true; 10779 if (debug && avcSample) { 10780 avcSample.debug += 'SEI '; 10781 } 10782 expGolombDecoder = new exp_golomb_1.default(_this.discardEPB(unit.data)); 10783 // skip frameType 10784 expGolombDecoder.readUByte(); 10785 var payloadType = 0; 10786 var payloadSize = 0; 10787 var endOfCaptions = false; 10788 var b = 0; 10789 while (!endOfCaptions && expGolombDecoder.bytesAvailable > 1) { 10790 payloadType = 0; 10791 do { 10792 b = expGolombDecoder.readUByte(); 10793 payloadType += b; 10794 } while (b === 0xFF); 10795 // Parse payload size. 10796 payloadSize = 0; 10797 do { 10798 b = expGolombDecoder.readUByte(); 10799 payloadSize += b; 10800 } while (b === 0xFF); 10801 // TODO: there can be more than one payload in an SEI packet... 10802 // TODO: need to read type and size in a while loop to get them all 10803 if (payloadType === 4 && expGolombDecoder.bytesAvailable !== 0) { 10804 endOfCaptions = true; 10805 var countryCode = expGolombDecoder.readUByte(); 10806 if (countryCode === 181) { 10807 var providerCode = expGolombDecoder.readUShort(); 10808 if (providerCode === 49) { 10809 var userStructure = expGolombDecoder.readUInt(); 10810 if (userStructure === 0x47413934) { 10811 var userDataType = expGolombDecoder.readUByte(); 10812 // Raw CEA-608 bytes wrapped in CEA-708 packet 10813 if (userDataType === 3) { 10814 var firstByte = expGolombDecoder.readUByte(); 10815 var secondByte = expGolombDecoder.readUByte(); 10816 var totalCCs = 31 & firstByte; 10817 var byteArray = [firstByte, secondByte]; 10818 for (i = 0; i < totalCCs; i++) { 10819 // 3 bytes per CC 10820 byteArray.push(expGolombDecoder.readUByte()); 10821 byteArray.push(expGolombDecoder.readUByte()); 10822 byteArray.push(expGolombDecoder.readUByte()); 10823 } 10824 _this._insertSampleInOrder(_this._txtTrack.samples, { type: 3, pts: pes.pts, bytes: byteArray }); 10825 } 10826 } 10827 } 10828 } 10829 } 10830 else if (payloadSize < expGolombDecoder.bytesAvailable) { 10831 for (i = 0; i < payloadSize; i++) { 10832 expGolombDecoder.readUByte(); 10833 } 10834 } 10835 } 10836 break; 10837 // SPS 10838 case 7: 10839 push = true; 10840 spsfound = true; 10841 if (debug && avcSample) { 10842 avcSample.debug += 'SPS '; 10843 } 10844 if (!track.sps) { 10845 expGolombDecoder = new exp_golomb_1.default(unit.data); 10846 var config = expGolombDecoder.readSPS(); 10847 track.width = config.width; 10848 track.height = config.height; 10849 track.pixelRatio = config.pixelRatio; 10850 track.sps = [unit.data]; 10851 track.duration = _this._duration; 10852 var codecarray = unit.data.subarray(1, 4); 10853 var codecstring = 'avc1.'; 10854 for (i = 0; i < 3; i++) { 10855 var h = codecarray[i].toString(16); 10856 if (h.length < 2) { 10857 h = '0' + h; 10858 } 10859 codecstring += h; 10860 } 10861 track.codec = codecstring; 10862 } 10863 break; 10864 // PPS 10865 case 8: 10866 push = true; 10867 if (debug && avcSample) { 10868 avcSample.debug += 'PPS '; 10869 } 10870 if (!track.pps) { 10871 track.pps = [unit.data]; 10872 } 10873 break; 10874 // AUD 10875 case 9: 10876 push = false; 10877 track.audFound = true; 10878 if (avcSample) { 10879 pushAccesUnit(avcSample, track); 10880 } 10881 avcSample = _this.avcSample = createAVCSample(false, pes.pts, pes.dts, debug ? 'AUD ' : ''); 10882 break; 10883 // Filler Data 10884 case 12: 10885 push = false; 10886 break; 10887 default: 10888 push = false; 10889 if (avcSample) { 10890 avcSample.debug += 'unknown NAL ' + unit.type + ' '; 10891 } 10892 break; 10893 } 10894 if (avcSample && push) { 10895 var units_1 = avcSample.units; 10896 units_1.push(unit); 10897 } 10898 }); 10899 // if last PES packet, push samples 10900 if (last && avcSample) { 10901 pushAccesUnit(avcSample, track); 10902 this.avcSample = null; 10903 } 10904 }; 10905 TSDemuxer.prototype._insertSampleInOrder = function (arr, data) { 10906 var len = arr.length; 10907 if (len > 0) { 10908 if (data.pts >= arr[len - 1].pts) { 10909 arr.push(data); 10910 } 10911 else { 10912 for (var pos = len - 1; pos >= 0; pos--) { 10913 if (data.pts < arr[pos].pts) { 10914 arr.splice(pos, 0, data); 10915 break; 10916 } 10917 } 10918 } 10919 } 10920 else { 10921 arr.push(data); 10922 } 10923 }; 10924 TSDemuxer.prototype._getLastNalUnit = function () { 10925 var avcSample = this.avcSample, lastUnit; 10926 // try to fallback to previous sample if current one is empty 10927 if (!avcSample || avcSample.units.length === 0) { 10928 var track = this._avcTrack, samples = track.samples; 10929 avcSample = samples[samples.length - 1]; 10930 } 10931 if (avcSample) { 10932 var units = avcSample.units; 10933 lastUnit = units[units.length - 1]; 10934 } 10935 return lastUnit; 10936 }; 10937 TSDemuxer.prototype._parseAVCNALu = function (array) { 10938 var i = 0, len = array.byteLength, value, overflow, track = this._avcTrack, state = track.naluState || 0, lastState = state; 10939 var units = [], unit, unitType, lastUnitStart = -1, lastUnitType; 10940 // logger.log('PES:' + Hex.hexDump(array)); 10941 if (state === -1) { 10942 // special use case where we found 3 or 4-byte start codes exactly at the end of previous PES packet 10943 lastUnitStart = 0; 10944 // NALu type is value read from offset 0 10945 lastUnitType = array[0] & 0x1f; 10946 state = 0; 10947 i = 1; 10948 } 10949 while (i < len) { 10950 value = array[i++]; 10951 // optimization. state 0 and 1 are the predominant case. let's handle them outside of the switch/case 10952 if (!state) { 10953 state = value ? 0 : 1; 10954 continue; 10955 } 10956 if (state === 1) { 10957 state = value ? 0 : 2; 10958 continue; 10959 } 10960 // here we have state either equal to 2 or 3 10961 if (!value) { 10962 state = 3; 10963 } 10964 else if (value === 1) { 10965 if (lastUnitStart >= 0) { 10966 unit = { data: array.subarray(lastUnitStart, i - state - 1), type: lastUnitType }; 10967 // logger.log('pushing NALU, type/size:' + unit.type + '/' + unit.data.byteLength); 10968 units.push(unit); 10969 } 10970 else { 10971 // lastUnitStart is undefined => this is the first start code found in this PES packet 10972 // first check if start code delimiter is overlapping between 2 PES packets, 10973 // ie it started in last packet (lastState not zero) 10974 // and ended at the beginning of this PES packet (i <= 4 - lastState) 10975 var lastUnit = this._getLastNalUnit(); 10976 if (lastUnit) { 10977 if (lastState && (i <= 4 - lastState)) { 10978 // start delimiter overlapping between PES packets 10979 // strip start delimiter bytes from the end of last NAL unit 10980 // check if lastUnit had a state different from zero 10981 if (lastUnit.state) { 10982 // strip last bytes 10983 lastUnit.data = lastUnit.data.subarray(0, lastUnit.data.byteLength - lastState); 10984 } 10985 } 10986 // If NAL units are not starting right at the beginning of the PES packet, push preceding data into previous NAL unit. 10987 overflow = i - state - 1; 10988 if (overflow > 0) { 10989 // logger.log('first NALU found with overflow:' + overflow); 10990 var tmp = new Uint8Array(lastUnit.data.byteLength + overflow); 10991 tmp.set(lastUnit.data, 0); 10992 tmp.set(array.subarray(0, overflow), lastUnit.data.byteLength); 10993 lastUnit.data = tmp; 10994 } 10995 } 10996 } 10997 // check if we can read unit type 10998 if (i < len) { 10999 unitType = array[i] & 0x1f; 11000 // logger.log('find NALU @ offset:' + i + ',type:' + unitType); 11001 lastUnitStart = i; 11002 lastUnitType = unitType; 11003 state = 0; 11004 } 11005 else { 11006 // not enough byte to read unit type. let's read it on next PES parsing 11007 state = -1; 11008 } 11009 } 11010 else { 11011 state = 0; 11012 } 11013 } 11014 if (lastUnitStart >= 0 && state >= 0) { 11015 unit = { data: array.subarray(lastUnitStart, len), type: lastUnitType, state: state }; 11016 units.push(unit); 11017 // logger.log('pushing NALU, type/size/state:' + unit.type + '/' + unit.data.byteLength + '/' + state); 11018 } 11019 // no NALu found 11020 if (units.length === 0) { 11021 // append pes.data to previous NAL unit 11022 var lastUnit = this._getLastNalUnit(); 11023 if (lastUnit) { 11024 var tmp = new Uint8Array(lastUnit.data.byteLength + array.byteLength); 11025 tmp.set(lastUnit.data, 0); 11026 tmp.set(array, lastUnit.data.byteLength); 11027 lastUnit.data = tmp; 11028 } 11029 } 11030 track.naluState = state; 11031 return units; 11032 }; 11033 /** 11034 * remove Emulation Prevention bytes from a RBSP 11035 */ 11036 TSDemuxer.prototype.discardEPB = function (data) { 11037 var length = data.byteLength, EPBPositions = [], i = 1, newLength, newData; 11038 // Find all `Emulation Prevention Bytes` 11039 while (i < length - 2) { 11040 if (data[i] === 0 && 11041 data[i + 1] === 0 && 11042 data[i + 2] === 0x03) { 11043 EPBPositions.push(i + 2); 11044 i += 2; 11045 } 11046 else { 11047 i++; 11048 } 11049 } 11050 // If no Emulation Prevention Bytes were found just return the original 11051 // array 11052 if (EPBPositions.length === 0) { 11053 return data; 11054 } 11055 // Create a new array to hold the NAL unit data 11056 newLength = length - EPBPositions.length; 11057 newData = new Uint8Array(newLength); 11058 var sourceIndex = 0; 11059 for (i = 0; i < newLength; sourceIndex++, i++) { 11060 if (sourceIndex === EPBPositions[0]) { 11061 // Skip this byte 11062 sourceIndex++; 11063 // Remove this position index 11064 EPBPositions.shift(); 11065 } 11066 newData[i] = data[sourceIndex]; 11067 } 11068 return newData; 11069 }; 11070 TSDemuxer.prototype._parseAACPES = function (pes) { 11071 var track = this._audioTrack, data = pes.data, pts = pes.pts, startOffset = 0, aacOverFlow = this.aacOverFlow, aacLastPTS = this.aacLastPTS, frameDuration, frameIndex, offset, stamp, len; 11072 if (aacOverFlow) { 11073 var tmp = new Uint8Array(aacOverFlow.byteLength + data.byteLength);
11074 tmp.set(aacOverFlow, 0); 11075 tmp.set(data, aacOverFlow.byteLength); 11076 // logger.log(`AAC: append overflowing ${aacOverFlow.byteLength} bytes to beginning of new PES`); 11077 data = tmp; 11078 } 11079 // look for ADTS header (0xFFFx) 11080 for (offset = startOffset, len = data.length; offset < len - 1; offset++) { 11081 if (ADTS.isHeader(data, offset)) { 11082 break; 11083 } 11084 } 11085 // if ADTS header does not start straight from the beginning of the PES payload, raise an error 11086 if (offset) { 11087 var reason = void 0, fatal = void 0; 11088 if (offset < len - 1) { 11089 reason = "AAC PES did not start with ADTS header,offset:" + offset; 11090 fatal = false; 11091 } 11092 else { 11093 reason = 'no ADTS header found in AAC PES'; 11094 fatal = true; 11095 } 11096 logger_1.logger.warn("parsing error:" + reason); 11097 this.observer.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.MEDIA_ERROR, details: errors_1.ErrorDetails.FRAG_PARSING_ERROR, fatal: fatal, reason: reason }); 11098 if (fatal) { 11099 return; 11100 } 11101 } 11102 ADTS.initTrackConfig(track, this.observer, data, offset, this.audioCodec); 11103 frameIndex = 0; 11104 frameDuration = ADTS.getFrameDuration(track.samplerate); 11105 // if last AAC frame is overflowing, we should ensure timestamps are contiguous: 11106 // first sample PTS should be equal to last sample PTS + frameDuration 11107 if (aacOverFlow && aacLastPTS) { 11108 var newPTS = aacLastPTS + frameDuration; 11109 if (Math.abs(newPTS - pts) > 1) { 11110 logger_1.logger.log("AAC: align PTS for overlapping frames by " + Math.round((newPTS - pts) / 90)); 11111 pts = newPTS; 11112 } 11113 } 11114 // scan for aac samples 11115 while (offset < len) { 11116 if (ADTS.isHeader(data, offset) && (offset + 5) < len) { 11117 var frame = ADTS.appendFrame(track, data, offset, pts, frameIndex); 11118 if (frame) { 11119 // logger.log(`${Math.round(frame.sample.pts)} : AAC`); 11120 offset += frame.length; 11121 stamp = frame.sample.pts; 11122 frameIndex++; 11123 } 11124 else { 11125 // logger.log('Unable to parse AAC frame'); 11126 break; 11127 } 11128 } 11129 else { 11130 // nothing found, keep looking 11131 offset++; 11132 } 11133 } 11134 if (offset < len) { 11135 aacOverFlow = data.subarray(offset, len); 11136 // logger.log(`AAC: overflow detected:${len-offset}`); 11137 } 11138 else { 11139 aacOverFlow = null; 11140 } 11141 this.aacOverFlow = aacOverFlow; 11142 this.aacLastPTS = stamp; 11143 }; 11144 TSDemuxer.prototype._parseMPEGPES = function (pes) { 11145 var data = pes.data; 11146 var length = data.length; 11147 var frameIndex = 0; 11148 var offset = 0; 11149 var pts = pes.pts; 11150 while (offset < length) { 11151 if (mpegaudio_1.default.isHeader(data, offset)) { 11152 var frame = mpegaudio_1.default.appendFrame(this._audioTrack, data, offset, pts, frameIndex); 11153 if (frame) { 11154 offset += frame.length; 11155 frameIndex++; 11156 } 11157 else { 11158 // logger.log('Unable to parse Mpeg audio frame'); 11159 break; 11160 } 11161 } 11162 else { 11163 // nothing found, keep looking 11164 offset++; 11165 } 11166 } 11167 }; 11168 TSDemuxer.prototype._parseID3PES = function (pes) { 11169 this._id3Track.samples.push(pes); 11170 }; 11171 return TSDemuxer; 11172}()); 11173exports.default = TSDemuxer; 11174 11175 11176/***/ }), 11177
11178/***/ "./src/errors.js": 11179/*!***********************!*\ 11180 !*** ./src/errors.js ***! 11181 \***********************/ 11182/*! no static exports found */ 11183/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 11184/***/ (function(module, exports, __webpack_require__) { 11185 11186"use strict"; 11187 11188Object.defineProperty(exports, "__esModule", { value: true }); 11189exports.ErrorTypes = { 11190 // Identifier for a network error (loading error / timeout ...) 11191 NETWORK_ERROR: 'networkError', 11192 // Identifier for a media Error (video/parsing/mediasource error) 11193 MEDIA_ERROR: 'mediaError', 11194 // EME (encrypted media extensions) errors 11195 KEY_SYSTEM_ERROR: 'keySystemError', 11196 // Identifier for a mux Error (demuxing/remuxing) 11197 MUX_ERROR: 'muxError', 11198 // Identifier for all other errors 11199 OTHER_ERROR: 'otherError' 11200}; 11201/** 11202 * @enum {ErrorDetails} 11203 * @typedef {string} ErrorDetail 11204 */ 11205exports.ErrorDetails = { 11206 KEY_SYSTEM_NO_KEYS: 'keySystemNoKeys', 11207 KEY_SYSTEM_NO_ACCESS: 'keySystemNoAccess', 11208 KEY_SYSTEM_NO_SESSION: 'keySystemNoSession', 11209 KEY_SYSTEM_LICENSE_REQUEST_FAILED: 'keySystemLicenseRequestFailed', 11210 // Identifier for a manifest load error - data: { url : faulty URL, response : { code: error code, text: error text }} 11211 MANIFEST_LOAD_ERROR: 'manifestLoadError', 11212 // Identifier for a manifest load timeout - data: { url : faulty URL, response : { code: error code, text: error text }} 11213 MANIFEST_LOAD_TIMEOUT: 'manifestLoadTimeOut', 11214 // Identifier for a manifest parsing error - data: { url : faulty URL, reason : error reason} 11215 MANIFEST_PARSING_ERROR: 'manifestParsingError', 11216 // Identifier for a manifest with only incompatible codecs error - data: { url : faulty URL, reason : error reason} 11217 MANIFEST_INCOMPATIBLE_CODECS_ERROR: 'manifestIncompatibleCodecsError', 11218 // Identifier for a level load error - data: { url : faulty URL, response : { code: error code, text: error text }} 11219 LEVEL_LOAD_ERROR: 'levelLoadError', 11220 // Identifier for a level load timeout - data: { url : faulty URL, response : { code: error code, text: error text }} 11221 LEVEL_LOAD_TIMEOUT: 'levelLoadTimeOut', 11222 // Identifier for a level switch error - data: { level : faulty level Id, event : error description} 11223 LEVEL_SWITCH_ERROR: 'levelSwitchError', 11224 // Identifier for an audio track load error - data: { url : faulty URL, response : { code: error code, text: error text }} 11225 AUDIO_TRACK_LOAD_ERROR: 'audioTrackLoadError', 11226 // Identifier for an audio track load timeout - data: { url : faulty URL, response : { code: error code, text: error text }} 11227 AUDIO_TRACK_LOAD_TIMEOUT: 'audioTrackLoadTimeOut', 11228 // Identifier for fragment load error - data: { frag : fragment object, response : { code: error code, text: error text }} 11229 FRAG_LOAD_ERROR: 'fragLoadError', 11230 // Identifier for fragment load timeout error - data: { frag : fragment object} 11231 FRAG_LOAD_TIMEOUT: 'fragLoadTimeOut', 11232 // Identifier for a fragment decryption error event - data: {id : demuxer Id,frag: fragment object, reason : parsing error description } 11233 FRAG_DECRYPT_ERROR: 'fragDecryptError', 11234 // Identifier for a fragment parsing error event - data: { id : demuxer Id, reason : parsing error description } 11235 // will be renamed DEMUX_PARSING_ERROR and switched to MUX_ERROR in the next major release 11236 FRAG_PARSING_ERROR: 'fragParsingError', 11237 // Identifier for a remux alloc error event - data: { id : demuxer Id, frag : fragment object, bytes : nb of bytes on which allocation failed , reason : error text } 11238 REMUX_ALLOC_ERROR: 'remuxAllocError', 11239 // Identifier for decrypt key load error - data: { frag : fragment object, response : { code: error code, text: error text }} 11240 KEY_LOAD_ERROR: 'keyLoadError', 11241 // Identifier for decrypt key load timeout error - data: { frag : fragment object} 11242 KEY_LOAD_TIMEOUT: 'keyLoadTimeOut', 11243 // Triggered when an exception occurs while adding a sourceBuffer to MediaSource - data : { err : exception , mimeType : mimeType } 11244 BUFFER_ADD_CODEC_ERROR: 'bufferAddCodecError', 11245 // Identifier for a buffer append error - data: append error description 11246 BUFFER_APPEND_ERROR: 'bufferAppendError', 11247 // Identifier for a buffer appending error event - data: appending error description 11248 BUFFER_APPENDING_ERROR: 'bufferAppendingError', 11249 // Identifier for a buffer stalled error event 11250 BUFFER_STALLED_ERROR: 'bufferStalledError', 11251 // Identifier for a buffer full event 11252 BUFFER_FULL_ERROR: 'bufferFullError', 11253 // Identifier for a buffer seek over hole event 11254 BUFFER_SEEK_OVER_HOLE: 'bufferSeekOverHole', 11255 // Identifier for a buffer nudge on stall (playback is stuck although currentTime is in a buffered area) 11256 BUFFER_NUDGE_ON_STALL: 'bufferNudgeOnStall', 11257 // Identifier for an internal exception happening inside hls.js while handling an event 11258 INTERNAL_EXCEPTION: 'internalException' 11259}; 11260 11261 11262/***/ }), 11263
vendor: 3,523 bytes, lines 11264-11355
11264/***/ "./src/event-handler.js": 11265/*!******************************!*\ 11266 !*** ./src/event-handler.js ***! 11267 \******************************/ 11268/*! no static exports found */ 11269/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 11270/***/ (function(module, exports, __webpack_require__) { 11271 11272"use strict"; 11273 11274/* 11275* 11276* All objects in the event handling chain should inherit from this class 11277* 11278*/ 11279Object.defineProperty(exports, "__esModule", { value: true }); 11280var logger_1 = __webpack_require__(/*! ./utils/logger */ "./src/utils/logger.js"); 11281var errors_1 = __webpack_require__(/*! ./errors */ "./src/errors.js"); 11282var events_1 = __webpack_require__(/*! ./events */ "./src/events.js"); 11283var FORBIDDEN_EVENT_NAMES = { 11284 'hlsEventGeneric': true, 11285 'hlsHandlerDestroying': true, 11286 'hlsHandlerDestroyed': true 11287}; 11288var EventHandler = /** @class */ (function () { 11289 function EventHandler(hls) { 11290 var events = []; 11291 for (var _i = 1; _i < arguments.length; _i++) { 11292 events[_i - 1] = arguments[_i]; 11293 } 11294 this.hls = hls; 11295 this.onEvent = this.onEvent.bind(this); 11296 this.handledEvents = events; 11297 this.useGenericHandler = true; 11298 this.registerListeners(); 11299 } 11300 EventHandler.prototype.destroy = function () { 11301 this.onHandlerDestroying(); 11302 this.unregisterListeners(); 11303 this.onHandlerDestroyed(); 11304 }; 11305 EventHandler.prototype.onHandlerDestroying = function () { }; 11306 EventHandler.prototype.onHandlerDestroyed = function () { }; 11307 EventHandler.prototype.isEventHandler = function () { 11308 return typeof this.handledEvents === 'object' && this.handledEvents.length && typeof this.onEvent === 'function'; 11309 }; 11310 EventHandler.prototype.registerListeners = function () { 11311 if (this.isEventHandler()) { 11312 this.handledEvents.forEach(function (event) { 11313 if (FORBIDDEN_EVENT_NAMES[event]) { 11314 throw new Error('Forbidden event-name: ' + event); 11315 } 11316 this.hls.on(event, this.onEvent); 11317 }, this); 11318 } 11319 }; 11320 EventHandler.prototype.unregisterListeners = function () { 11321 if (this.isEventHandler()) { 11322 this.handledEvents.forEach(function (event) { 11323 this.hls.off(event, this.onEvent); 11324 }, this); 11325 } 11326 }; 11327 /** 11328 * arguments: event (string), data (any) 11329 */ 11330 EventHandler.prototype.onEvent = function (event, data) { 11331 this.onEventGeneric(event, data); 11332 }; 11333 EventHandler.prototype.onEventGeneric = function (event, data) { 11334 var eventToFunction = function (event, data) { 11335 var funcName = 'on' + event.replace('hls', ''); 11336 if (typeof this[funcName] !== 'function') { 11337 throw new Error("Event " + event + " has no generic handler in this " + this.constructor.name + " class (tried " + funcName + ")"); 11338 } 11339 return this[funcName].bind(this, data); 11340 }; 11341 try { 11342 eventToFunction.call(this, event, data).call(); 11343 } 11344 catch (err) { 11345 logger_1.logger.error("An internal error happened while handling event " + event + ". Error message: \"" + err.message + "\". Here is a stacktrace:", err); 11346 this.hls.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.OTHER_ERROR, details: errors_1.ErrorDetails.INTERNAL_EXCEPTION, fatal: false, event: event, err: err }); 11347 } 11348 }; 11349 return EventHandler; 11350}()); 11351exports.default = EventHandler; 11352 11353 11354/***/ }), 11355
11356/***/ "./src/events.js": 11357/*!***********************!*\ 11358 !*** ./src/events.js ***! 11359 \***********************/ 11360/*! no static exports found */ 11361/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 11362/***/ (function(module, exports, __webpack_require__) { 11363 11364"use strict"; 11365 11366Object.defineProperty(exports, "__esModule", { value: true }); 11367/** 11368 * @readonly 11369 * @enum {string} 11370 */ 11371var HlsEvents = { 11372 // fired before MediaSource is attaching to media element - data: { media } 11373 MEDIA_ATTACHING: 'hlsMediaAttaching', 11374 // fired when MediaSource has been succesfully attached to media element - data: { } 11375 MEDIA_ATTACHED: 'hlsMediaAttached', 11376 // fired before detaching MediaSource from media element - data: { } 11377 MEDIA_DETACHING: 'hlsMediaDetaching', 11378 // fired when MediaSource has been detached from media element - data: { } 11379 MEDIA_DETACHED: 'hlsMediaDetached', 11380 // fired when we buffer is going to be reset - data: { } 11381 BUFFER_RESET: 'hlsBufferReset', 11382 // fired when we know about the codecs that we need buffers for to push into - data: {tracks : { container, codec, levelCodec, initSegment, metadata }} 11383 BUFFER_CODECS: 'hlsBufferCodecs', 11384 // fired when sourcebuffers have been created - data: { tracks : tracks } 11385 BUFFER_CREATED: 'hlsBufferCreated', 11386 // fired when we append a segment to the buffer - data: { segment: segment object } 11387 BUFFER_APPENDING: 'hlsBufferAppending', 11388 // fired when we are done with appending a media segment to the buffer - data : { parent : segment parent that triggered BUFFER_APPENDING, pending : nb of segments waiting for appending for this segment parent} 11389 BUFFER_APPENDED: 'hlsBufferAppended', 11390 // fired when the stream is finished and we want to notify the media buffer that there will be no more data - data: { } 11391 BUFFER_EOS: 'hlsBufferEos', 11392 // fired when the media buffer should be flushed - data { startOffset, endOffset } 11393 BUFFER_FLUSHING: 'hlsBufferFlushing', 11394 // fired when the media buffer has been flushed - data: { } 11395 BUFFER_FLUSHED: 'hlsBufferFlushed', 11396 // fired to signal that a manifest loading starts - data: { url : manifestURL} 11397 MANIFEST_LOADING: 'hlsManifestLoading', 11398 // fired after manifest has been loaded - data: { levels : [available quality levels], audioTracks : [ available audio tracks], url : manifestURL, stats : { trequest, tfirst, tload, mtime}} 11399 MANIFEST_LOADED: 'hlsManifestLoaded', 11400 // fired after manifest has been parsed - data: { levels : [available quality levels], firstLevel : index of first quality level appearing in Manifest} 11401 MANIFEST_PARSED: 'hlsManifestParsed', 11402 // fired when a level switch is requested - data: { level : id of new level } 11403 LEVEL_SWITCHING: 'hlsLevelSwitching', 11404 // fired when a level switch is effective - data: { level : id of new level } 11405 LEVEL_SWITCHED: 'hlsLevelSwitched', 11406 // fired when a level playlist loading starts - data: { url : level URL, level : id of level being loaded} 11407 LEVEL_LOADING: 'hlsLevelLoading', 11408 // fired when a level playlist loading finishes - data: { details : levelDetails object, level : id of loaded level, stats : { trequest, tfirst, tload, mtime} } 11409 LEVEL_LOADED: 'hlsLevelLoaded', 11410 // fired when a level's details have been updated based on previous details, after it has been loaded - data: { details : levelDetails object, level : id of updated level } 11411 LEVEL_UPDATED: 'hlsLevelUpdated', 11412 // fired when a level's PTS information has been updated after parsing a fragment - data: { details : levelDetails object, level : id of updated level, drift: PTS drift observed when parsing last fragment } 11413 LEVEL_PTS_UPDATED: 'hlsLevelPtsUpdated', 11414 // fired to notify that audio track lists has been updated - data: { audioTracks : audioTracks } 11415 AUDIO_TRACKS_UPDATED: 'hlsAudioTracksUpdated', 11416 // fired when an audio track switching is requested - data: { id : audio track id } 11417 AUDIO_TRACK_SWITCHING: 'hlsAudioTrackSwitching', 11418 // fired when an audio track switch actually occurs - data: { id : audio track id } 11419 AUDIO_TRACK_SWITCHED: 'hlsAudioTrackSwitched', 11420 // fired when an audio track loading starts - data: { url : audio track URL, id : audio track id } 11421 AUDIO_TRACK_LOADING: 'hlsAudioTrackLoading', 11422 // fired when an audio track loading finishes - data: { details : levelDetails object, id : audio track id, stats : { trequest, tfirst, tload, mtime } } 11423 AUDIO_TRACK_LOADED: 'hlsAudioTrackLoaded', 11424 // fired to notify that subtitle track lists has been updated - data: { subtitleTracks : subtitleTracks }
11425 SUBTITLE_TRACKS_UPDATED: 'hlsSubtitleTracksUpdated', 11426 // fired when an subtitle track switch occurs - data: { id : subtitle track id } 11427 SUBTITLE_TRACK_SWITCH: 'hlsSubtitleTrackSwitch', 11428 // fired when a subtitle track loading starts - data: { url : subtitle track URL, id : subtitle track id } 11429 SUBTITLE_TRACK_LOADING: 'hlsSubtitleTrackLoading', 11430 // fired when a subtitle track loading finishes - data: { details : levelDetails object, id : subtitle track id, stats : { trequest, tfirst, tload, mtime } } 11431 SUBTITLE_TRACK_LOADED: 'hlsSubtitleTrackLoaded', 11432 // fired when a subtitle fragment has been processed - data: { success : boolean, frag : the processed frag } 11433 SUBTITLE_FRAG_PROCESSED: 'hlsSubtitleFragProcessed', 11434 // fired when the first timestamp is found - data: { id : demuxer id, initPTS: initPTS, frag : fragment object } 11435 INIT_PTS_FOUND: 'hlsInitPtsFound', 11436 // fired when a fragment loading starts - data: { frag : fragment object } 11437 FRAG_LOADING: 'hlsFragLoading', 11438 // fired when a fragment loading is progressing - data: { frag : fragment object, { trequest, tfirst, loaded } } 11439 FRAG_LOAD_PROGRESS: 'hlsFragLoadProgress', 11440 // Identifier for fragment load aborting for emergency switch down - data: { frag : fragment object } 11441 FRAG_LOAD_EMERGENCY_ABORTED: 'hlsFragLoadEmergencyAborted', 11442 // fired when a fragment loading is completed - data: { frag : fragment object, payload : fragment payload, stats : { trequest, tfirst, tload, length } } 11443 FRAG_LOADED: 'hlsFragLoaded', 11444 // fired when a fragment has finished decrypting - data: { id : demuxer id, frag: fragment object, payload : fragment payload, stats : { tstart, tdecrypt } } 11445 FRAG_DECRYPTED: 'hlsFragDecrypted', 11446 // fired when Init Segment has been extracted from fragment - data: { id : demuxer id, frag: fragment object, moov : moov MP4 box, codecs : codecs found while parsing fragment } 11447 FRAG_PARSING_INIT_SEGMENT: 'hlsFragParsingInitSegment', 11448 // fired when parsing sei text is completed - data: { id : demuxer id, frag: fragment object, samples : [ sei samples pes ] } 11449 FRAG_PARSING_USERDATA: 'hlsFragParsingUserdata', 11450 // fired when parsing id3 is completed - data: { id : demuxer id, frag: fragment object, samples : [ id3 samples pes ] } 11451 FRAG_PARSING_METADATA: 'hlsFragParsingMetadata', 11452 // fired when data have been extracted from fragment - data: { id : demuxer id, frag: fragment object, data1 : moof MP4 box or TS fragments, data2 : mdat MP4 box or null} 11453 FRAG_PARSING_DATA: 'hlsFragParsingData', 11454 // fired when fragment parsing is completed - data: { id : demuxer id, frag: fragment object } 11455 FRAG_PARSED: 'hlsFragParsed', 11456 // fired when fragment remuxed MP4 boxes have all been appended into SourceBuffer - data: { id : demuxer id, frag : fragment object, stats : { trequest, tfirst, tload, tparsed, tbuffered, length, bwEstimate } } 11457 FRAG_BUFFERED: 'hlsFragBuffered', 11458 // fired when fragment matching with current media position is changing - data : { id : demuxer id, frag : fragment object } 11459 FRAG_CHANGED: 'hlsFragChanged', 11460 // Identifier for a FPS drop event - data: { curentDropped, currentDecoded, totalDroppedFrames } 11461 FPS_DROP: 'hlsFpsDrop', 11462 // triggered when FPS drop triggers auto level capping - data: { level, droppedlevel } 11463 FPS_DROP_LEVEL_CAPPING: 'hlsFpsDropLevelCapping', 11464 // Identifier for an error event - data: { type : error type, details : error details, fatal : if true, hls.js cannot/will not try to recover, if false, hls.js will try to recover,other error specific data } 11465 ERROR: 'hlsError', 11466 // fired when hls.js instance starts destroying. Different from MEDIA_DETACHED as one could want to detach and reattach a media to the instance of hls.js to handle mid-rolls for example - data: { } 11467 DESTROYING: 'hlsDestroying', 11468 // fired when a decrypt key loading starts - data: { frag : fragment object } 11469 KEY_LOADING: 'hlsKeyLoading', 11470 // fired when a decrypt key loading is completed - data: { frag : fragment object, payload : key payload, stats : { trequest, tfirst, tload, length } } 11471 KEY_LOADED: 'hlsKeyLoaded', 11472 // fired upon stream controller state transitions - data: { previousState, nextState } 11473 STREAM_STATE_TRANSITION: 'hlsStreamStateTransition' 11474}; 11475exports.default = HlsEvents; 11476 11477 11478/***/ }), 11479
11480/***/ "./src/hls.js": 11481/*!********************!*\ 11482 !*** ./src/hls.js ***! 11483 \********************/ 11484/*! no static exports found */ 11485/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 11486/***/ (function(module, exports, __webpack_require__) { 11487 11488"use strict"; 11489 11490var __extends = (this && this.__extends) || (function () { 11491 var extendStatics = Object.setPrototypeOf || 11492 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 11493 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 11494 return function (d, b) { 11495 extendStatics(d, b); 11496 function __() { this.constructor = d; } 11497 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 11498 }; 11499})(); 11500Object.defineProperty(exports, "__esModule", { value: true }); 11501var URLToolkit = __webpack_require__(/*! url-toolkit */ "./node_modules/url-toolkit/src/url-toolkit.js"); 11502var errors_1 = __webpack_require__(/*! ./errors */ "./src/errors.js"); 11503var playlist_loader_1 = __webpack_require__(/*! ./loader/playlist-loader */ "./src/loader/playlist-loader.js"); 11504var fragment_loader_1 = __webpack_require__(/*! ./loader/fragment-loader */ "./src/loader/fragment-loader.js"); 11505var key_loader_1 = __webpack_require__(/*! ./loader/key-loader */ "./src/loader/key-loader.js"); 11506var fragment_tracker_1 = __webpack_require__(/*! ./controller/fragment-tracker */ "./src/controller/fragment-tracker.js"); 11507var stream_controller_1 = __webpack_require__(/*! ./controller/stream-controller */ "./src/controller/stream-controller.js"); 11508var level_controller_1 = __webpack_require__(/*! ./controller/level-controller */ "./src/controller/level-controller.js"); 11509var id3_track_controller_1 = __webpack_require__(/*! ./controller/id3-track-controller */ "./src/controller/id3-track-controller.js"); 11510var is_supported_1 = __webpack_require__(/*! ./is-supported */ "./src/is-supported.js"); 11511var logger_1 = __webpack_require__(/*! ./utils/logger */ "./src/utils/logger.js"); 11512var config_1 = __webpack_require__(/*! ./config */ "./src/config.js"); 11513var events_1 = __webpack_require__(/*! ./events */ "./src/events.js"); 11514var observer_1 = __webpack_require__(/*! ./observer */ "./src/observer.js"); 11515/** 11516 * @module Hls 11517 * @class 11518 * @constructor 11519 */ 11520var Hls = /** @class */ (function (_super) { 11521 __extends(Hls, _super); 11522 /** 11523 * Creates an instance of an HLS client that can attach to exactly one `HTMLMediaElement`. 11524 * 11525 * @constructs Hls 11526 * @param {HlsConfig} config 11527 */ 11528 function Hls(config) { 11529 if (config === void 0) { config = {}; } 11530 var _this = _super.call(this) || this; 11531 var defaultConfig = Hls.DefaultConfig; 11532 if ((config.liveSyncDurationCount || config.liveMaxLatencyDurationCount) && (config.liveSyncDuration || config.liveMaxLatencyDuration)) { 11533 throw new Error('Illegal hls.js config: don\'t mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration'); 11534 } 11535 for (var prop in defaultConfig) { 11536 if (prop in config) 11537 continue; 11538 config[prop] = defaultConfig[prop]; 11539 } 11540 if (config.liveMaxLatencyDurationCount !== undefined && config.liveMaxLatencyDurationCount <= config.liveSyncDurationCount) { 11541 throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be gt "liveSyncDurationCount"'); 11542 } 11543 if (config.liveMaxLatencyDuration !== undefined && (config.liveMaxLatencyDuration <= config.liveSyncDuration || config.liveSyncDuration === undefined)) { 11544 throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be gt "liveSyncDuration"'); 11545 } 11546 logger_1.enableLogs(config.debug); 11547 _this.config = config; 11548 _this._autoLevelCapping = -1; 11549 // core controllers and network loaders 11550 /** 11551 * @member {AbrController} abrController 11552 */ 11553 var abrController = _this.abrController = new config.abrController(_this); 11554 var bufferController = new config.bufferController(_this); 11555 var capLevelController = new config.capLevelController(_this); 11556 var fpsController = new config.fpsController(_this); 11557 var playListLoader = new playlist_loader_1.default(_this); 11558 var fragmentLoader = new fragment_loader_1.default(_this); 11559 var keyLoader = new key_loader_1.default(_this); 11560 var id3TrackController = new id3_track_controller_1.default(_this); 11561 // network controllers 11562 /** 11563 * @member {LevelController} levelController 11564 */ 11565 var levelController = _this.levelController = new level_controller_1.default(_this); 11566 // FIXME: FragmentTracker must be defined before StreamController because the order of event handling is important 11567 var fragmentTracker = new fragment_tracker_1.FragmentTracker(_this); 11568 /** 11569 * @member {StreamController} streamController 11570 */ 11571 var streamController = _this.streamController = new stream_controller_1.default(_this, fragmentTracker); 11572 var networkControllers = [levelController, streamController]; 11573 // optional audio stream controller 11574 /** 11575 * @var {ICoreComponent | Controller} 11576 */ 11577 var Controller = config.audioStreamController; 11578 if (Controller) { 11579 networkControllers.push(new Controller(_this, fragmentTracker)); 11580 } 11581 /** 11582 * @member {INetworkController[]} networkControllers 11583 */ 11584 _this.networkControllers = networkControllers; 11585 /** 11586 * @var {ICoreComponent[]} 11587 */ 11588 var coreComponents = [ 11589 playListLoader, 11590 fragmentLoader, 11591 keyLoader, 11592 abrController, 11593 bufferController, 11594 capLevelController, 11595 fpsController, 11596 id3TrackController, 11597 fragmentTracker 11598 ]; 11599 // optional audio track and subtitle controller 11600 Controller = config.audioTrackController; 11601 if (Controller) { 11602 var audioTrackController = new Controller(_this); 11603 /** 11604 * @member {AudioTrackController} audioTrackController 11605 */ 11606 _this.audioTrackController = audioTrackController; 11607 coreComponents.push(audioTrackController); 11608 } 11609 Controller = config.subtitleTrackController; 11610 if (Controller) { 11611 var subtitleTrackController = new Controller(_this); 11612 /** 11613 * @member {SubtitleTrackController} subtitleTrackController 11614 */
11615 _this.subtitleTrackController = subtitleTrackController; 11616 networkControllers.push(subtitleTrackController); 11617 } 11618 Controller = config.emeController; 11619 if (Controller) { 11620 var emeController = new Controller(_this); 11621 /** 11622 * @member {EMEController} emeController 11623 */ 11624 _this.emeController = emeController; 11625 coreComponents.push(emeController); 11626 } 11627 // optional subtitle controllers 11628 Controller = config.subtitleStreamController; 11629 if (Controller) { 11630 networkControllers.push(new Controller(_this, fragmentTracker)); 11631 } 11632 Controller = config.timelineController; 11633 if (Controller) { 11634 coreComponents.push(new Controller(_this)); 11635 } 11636 /** 11637 * @member {ICoreComponent[]} 11638 */ 11639 _this.coreComponents = coreComponents; 11640 return _this; 11641 } 11642 Object.defineProperty(Hls, "version", { 11643 /** 11644 * @type {string} 11645 */ 11646 get: function () { 11647 return "0.12.4"; 11648 }, 11649 enumerable: true, 11650 configurable: true 11651 }); 11652 /** 11653 * @type {boolean} 11654 */ 11655 Hls.isSupported = function () { 11656 return is_supported_1.isSupported(); 11657 }; 11658 Object.defineProperty(Hls, "Events", { 11659 /** 11660 * @type {HlsEvents} 11661 */ 11662 get: function () { 11663 return events_1.default; 11664 }, 11665 enumerable: true, 11666 configurable: true 11667 }); 11668 Object.defineProperty(Hls, "ErrorTypes", { 11669 /** 11670 * @type {HlsErrorTypes} 11671 */ 11672 get: function () { 11673 return errors_1.ErrorTypes; 11674 }, 11675 enumerable: true, 11676 configurable: true 11677 }); 11678 Object.defineProperty(Hls, "ErrorDetails", { 11679 /** 11680 * @type {HlsErrorDetails} 11681 */ 11682 get: function () { 11683 return errors_1.ErrorDetails; 11684 }, 11685 enumerable: true, 11686 configurable: true 11687 }); 11688 Object.defineProperty(Hls, "DefaultConfig", { 11689 /** 11690 * @type {HlsConfig} 11691 */ 11692 get: function () { 11693 if (!Hls.defaultConfig) { 11694 return config_1.hlsDefaultConfig; 11695 } 11696 return Hls.defaultConfig; 11697 }, 11698 /** 11699 * @type {HlsConfig} 11700 */ 11701 set: function (defaultConfig) { 11702 Hls.defaultConfig = defaultConfig; 11703 }, 11704 enumerable: true, 11705 configurable: true 11706 }); 11707 /** 11708 * Dispose of the instance 11709 */ 11710 Hls.prototype.destroy = function () { 11711 logger_1.logger.log('destroy'); 11712 this.trigger(events_1.default.DESTROYING); 11713 this.detachMedia(); 11714 this.coreComponents.concat(this.networkControllers).forEach(function (component) { 11715 component.destroy(); 11716 }); 11717 this.url = null; 11718 this.removeAllListeners(); 11719 this._autoLevelCapping = -1; 11720 }; 11721 /** 11722 * Attach a media element 11723 * @param {HTMLMediaElement} media 11724 */ 11725 Hls.prototype.attachMedia = function (media) { 11726 logger_1.logger.log('attachMedia'); 11727 this.media = media;
11728 this.trigger(events_1.default.MEDIA_ATTACHING, { media: media }); 11729 }; 11730 /** 11731 * Detach from the media 11732 */ 11733 Hls.prototype.detachMedia = function () { 11734 logger_1.logger.log('detachMedia'); 11735 this.trigger(events_1.default.MEDIA_DETACHING); 11736 this.media = null; 11737 }; 11738 /** 11739 * Set the source URL. Can be relative or absolute. 11740 * @param {string} url 11741 */ 11742 Hls.prototype.loadSource = function (url) { 11743 url = URLToolkit.buildAbsoluteURL(window.location.href, url, { alwaysNormalize: true }); 11744 logger_1.logger.log("loadSource:" + url); 11745 this.url = url; 11746 // when attaching to a source URL, trigger a playlist load 11747 this.trigger(events_1.default.MANIFEST_LOADING, { url: url }); 11748 }; 11749 /** 11750 * Start loading data from the stream source. 11751 * Depending on default config, client starts loading automatically when a source is set. 11752 * 11753 * @param {number} startPosition Set the start position to stream from 11754 * @default -1 None (from earliest point) 11755 */ 11756 Hls.prototype.startLoad = function (startPosition) { 11757 if (startPosition === void 0) { startPosition = -1; } 11758 logger_1.logger.log("startLoad(" + startPosition + ")"); 11759 this.networkControllers.forEach(function (controller) { 11760 controller.startLoad(startPosition); 11761 }); 11762 }; 11763 /** 11764 * Stop loading of any stream data. 11765 */ 11766 Hls.prototype.stopLoad = function () { 11767 logger_1.logger.log('stopLoad'); 11768 this.networkControllers.forEach(function (controller) { 11769 controller.stopLoad(); 11770 }); 11771 }; 11772 /** 11773 * Swap through possible audio codecs in the stream (for example to switch from stereo to 5.1) 11774 */ 11775 Hls.prototype.swapAudioCodec = function () { 11776 logger_1.logger.log('swapAudioCodec'); 11777 this.streamController.swapAudioCodec(); 11778 }; 11779 /** 11780 * When the media-element fails, this allows to detach and then re-attach it 11781 * as one call (convenience method). 11782 * 11783 * Automatic recovery of media-errors by this process is configurable. 11784 */ 11785 Hls.prototype.recoverMediaError = function () { 11786 logger_1.logger.log('recoverMediaError'); 11787 var media = this.media; 11788 this.detachMedia(); 11789 this.attachMedia(media); 11790 }; 11791 Object.defineProperty(Hls.prototype, "levels", { 11792 /** 11793 * @type {QualityLevel[]} 11794 */ 11795 get: function () { 11796 return this.levelController.levels; 11797 }, 11798 enumerable: true, 11799 configurable: true 11800 }); 11801 Object.defineProperty(Hls.prototype, "currentLevel", { 11802 /** 11803 * Index of quality level currently played 11804 * @type {number} 11805 */ 11806 get: function () { 11807 return this.streamController.currentLevel; 11808 }, 11809 /** 11810 * Set quality level index immediately . 11811 * This will flush the current buffer to replace the quality asap. 11812 * That means playback will interrupt at least shortly to re-buffer and re-sync eventually. 11813 * @type {number} -1 for automatic level selection 11814 */ 11815 set: function (newLevel) { 11816 logger_1.logger.log("set currentLevel:" + newLevel); 11817 this.loadLevel = newLevel; 11818 this.streamController.immediateLevelSwitch(); 11819 }, 11820 enumerable: true, 11821 configurable: true 11822 }); 11823 Object.defineProperty(Hls.prototype, "nextLevel", { 11824 /** 11825 * Index of next quality level loaded as scheduled by stream controller. 11826 * @type {number} 11827 */ 11828 get: function () { 11829 return this.streamController.nextLevel; 11830 }, 11831 /** 11832 * Set quality level index for next loaded data. 11833 * This will switch the video quality asap, without interrupting playback. 11834 * May abort current loading of data, and flush parts of buffer (outside currently played fragment region). 11835 * @type {number} -1 for automatic level selection 11836 */ 11837 set: function (newLevel) { 11838 logger_1.logger.log("set nextLevel:" + newLevel); 11839 this.levelController.manualLevel = newLevel; 11840 this.streamController.nextLevelSwitch(); 11841 }, 11842 enumerable: true, 11843 configurable: true 11844 });
11845 Object.defineProperty(Hls.prototype, "loadLevel", { 11846 /** 11847 * Return the quality level of the currently or last (of none is loaded currently) segment 11848 * @type {number} 11849 */ 11850 get: function () { 11851 return this.levelController.level; 11852 }, 11853 /** 11854 * Set quality level index for next loaded data in a conservative way. 11855 * This will switch the quality without flushing, but interrupt current loading. 11856 * Thus the moment when the quality switch will appear in effect will only be after the already existing buffer. 11857 * @type {number} newLevel -1 for automatic level selection 11858 */ 11859 set: function (newLevel) { 11860 logger_1.logger.log("set loadLevel:" + newLevel); 11861 this.levelController.manualLevel = newLevel; 11862 }, 11863 enumerable: true, 11864 configurable: true 11865 }); 11866 Object.defineProperty(Hls.prototype, "nextLoadLevel", { 11867 /** 11868 * get next quality level loaded 11869 * @type {number} 11870 */ 11871 get: function () { 11872 return this.levelController.nextLoadLevel; 11873 }, 11874 /** 11875 * Set quality level of next loaded segment in a fully "non-destructive" way. 11876 * Same as `loadLevel` but will wait for next switch (until current loading is done). 11877 * @type {number} level 11878 */ 11879 set: function (level) { 11880 this.levelController.nextLoadLevel = level; 11881 }, 11882 enumerable: true, 11883 configurable: true 11884 }); 11885 Object.defineProperty(Hls.prototype, "firstLevel", { 11886 /** 11887 * Return "first level": like a default level, if not set, 11888 * falls back to index of first level referenced in manifest 11889 * @type {number} 11890 */ 11891 get: function () { 11892 return Math.max(this.levelController.firstLevel, this.minAutoLevel); 11893 }, 11894 /** 11895 * Sets "first-level", see getter. 11896 * @type {number} 11897 */ 11898 set: function (newLevel) { 11899 logger_1.logger.log("set firstLevel:" + newLevel); 11900 this.levelController.firstLevel = newLevel; 11901 }, 11902 enumerable: true, 11903 configurable: true 11904 }); 11905 Object.defineProperty(Hls.prototype, "startLevel", { 11906 /** 11907 * Return start level (level of first fragment that will be played back) 11908 * if not overrided by user, first level appearing in manifest will be used as start level 11909 * if -1 : automatic start level selection, playback will start from level matching download bandwidth 11910 * (determined from download of first segment) 11911 * @type {number} 11912 */ 11913 get: function () { 11914 return this.levelController.startLevel; 11915 }, 11916 /** 11917 * set start level (level of first fragment that will be played back) 11918 * if not overrided by user, first level appearing in manifest will be used as start level 11919 * if -1 : automatic start level selection, playback will start from level matching download bandwidth 11920 * (determined from download of first segment) 11921 * @type {number} newLevel 11922 */ 11923 set: function (newLevel) { 11924 logger_1.logger.log("set startLevel:" + newLevel); 11925 var hls = this; 11926 // if not in automatic start level detection, ensure startLevel is greater than minAutoLevel 11927 if (newLevel !== -1) { 11928 newLevel = Math.max(newLevel, hls.minAutoLevel); 11929 } 11930 hls.levelController.startLevel = newLevel; 11931 }, 11932 enumerable: true, 11933 configurable: true 11934 }); 11935 Object.defineProperty(Hls.prototype, "autoLevelCapping", { 11936 /** 11937 * Capping/max level value that should be used by automatic level selection algorithm (`ABRController`) 11938 * @type {number} 11939 */ 11940 get: function () { 11941 return this._autoLevelCapping; 11942 }, 11943 /** 11944 * Capping/max level value that should be used by automatic level selection algorithm (`ABRController`) 11945 * @type {number} 11946 */ 11947 set: function (newLevel) { 11948 logger_1.logger.log("set autoLevelCapping:" + newLevel); 11949 this._autoLevelCapping = newLevel; 11950 }, 11951 enumerable: true, 11952 configurable: true 11953 });
11954 Object.defineProperty(Hls.prototype, "autoLevelEnabled", { 11955 /** 11956 * True when automatic level selection enabled 11957 * @type {boolean} 11958 */ 11959 get: function () { 11960 return (this.levelController.manualLevel === -1); 11961 }, 11962 enumerable: true, 11963 configurable: true 11964 }); 11965 Object.defineProperty(Hls.prototype, "manualLevel", { 11966 /** 11967 * Level set manually (if any) 11968 * @type {number} 11969 */ 11970 get: function () { 11971 return this.levelController.manualLevel; 11972 }, 11973 enumerable: true, 11974 configurable: true 11975 }); 11976 Object.defineProperty(Hls.prototype, "minAutoLevel", { 11977 /** 11978 * min level selectable in auto mode according to config.minAutoBitrate 11979 * @type {number} 11980 */ 11981 get: function () { 11982 var hls = this, levels = hls.levels, minAutoBitrate = hls.config.minAutoBitrate, len = levels ? levels.length : 0; 11983 for (var i = 0; i < len; i++) { 11984 var levelNextBitrate = levels[i].realBitrate ? Math.max(levels[i].realBitrate, levels[i].bitrate) : levels[i].bitrate; 11985 if (levelNextBitrate > minAutoBitrate) { 11986 return i; 11987 } 11988 } 11989 return 0; 11990 }, 11991 enumerable: true, 11992 configurable: true 11993 }); 11994 Object.defineProperty(Hls.prototype, "maxAutoLevel", { 11995 /** 11996 * max level selectable in auto mode according to autoLevelCapping 11997 * @type {number} 11998 */ 11999 get: function () { 12000 var hls = this; 12001 var levels = hls.levels; 12002 var autoLevelCapping = hls.autoLevelCapping; 12003 var maxAutoLevel; 12004 if (autoLevelCapping === -1 && levels && levels.length) { 12005 maxAutoLevel = levels.length - 1; 12006 } 12007 else { 12008 maxAutoLevel = autoLevelCapping; 12009 } 12010 return maxAutoLevel; 12011 }, 12012 enumerable: true, 12013 configurable: true 12014 }); 12015 Object.defineProperty(Hls.prototype, "nextAutoLevel", { 12016 /** 12017 * next automatically selected quality level 12018 * @type {number} 12019 */ 12020 get: function () { 12021 var hls = this; 12022 // ensure next auto level is between min and max auto level 12023 return Math.min(Math.max(hls.abrController.nextAutoLevel, hls.minAutoLevel), hls.maxAutoLevel); 12024 }, 12025 /** 12026 * this setter is used to force next auto level. 12027 * this is useful to force a switch down in auto mode: 12028 * in case of load error on level N, hls.js can set nextAutoLevel to N-1 for example) 12029 * forced value is valid for one fragment. upon succesful frag loading at forced level, 12030 * this value will be resetted to -1 by ABR controller. 12031 * @type {number} 12032 */ 12033 set: function (nextLevel) { 12034 var hls = this; 12035 hls.abrController.nextAutoLevel = Math.max(hls.minAutoLevel, nextLevel); 12036 }, 12037 enumerable: true, 12038 configurable: true 12039 }); 12040 Object.defineProperty(Hls.prototype, "audioTracks", { 12041 /** 12042 * @type {AudioTrack[]} 12043 */ 12044 get: function () { 12045 var audioTrackController = this.audioTrackController; 12046 return audioTrackController ? audioTrackController.audioTracks : []; 12047 }, 12048 enumerable: true, 12049 configurable: true 12050 }); 12051 Object.defineProperty(Hls.prototype, "audioTrack", { 12052 /** 12053 * index of the selected audio track (index in audio track lists) 12054 * @type {number} 12055 */ 12056 get: function () { 12057 var audioTrackController = this.audioTrackController; 12058 return audioTrackController ? audioTrackController.audioTrack : -1; 12059 }, 12060 /** 12061 * selects an audio track, based on its index in audio track lists 12062 * @type {number} 12063 */ 12064 set: function (audioTrackId) { 12065 var audioTrackController = this.audioTrackController; 12066 if (audioTrackController) { 12067 audioTrackController.audioTrack = audioTrackId; 12068 } 12069 }, 12070 enumerable: true, 12071 configurable: true 12072 });
12073 Object.defineProperty(Hls.prototype, "liveSyncPosition", { 12074 /** 12075 * @type {Seconds} 12076 */ 12077 get: function () { 12078 return this.streamController.liveSyncPosition; 12079 }, 12080 enumerable: true, 12081 configurable: true 12082 }); 12083 Object.defineProperty(Hls.prototype, "subtitleTracks", { 12084 /** 12085 * get alternate subtitle tracks list from playlist 12086 * @type {SubtitleTrack[]} 12087 */ 12088 get: function () { 12089 var subtitleTrackController = this.subtitleTrackController; 12090 return subtitleTrackController ? subtitleTrackController.subtitleTracks : []; 12091 }, 12092 enumerable: true, 12093 configurable: true 12094 }); 12095 Object.defineProperty(Hls.prototype, "subtitleTrack", { 12096 /** 12097 * index of the selected subtitle track (index in subtitle track lists) 12098 * @type {number} 12099 */ 12100 get: function () { 12101 var subtitleTrackController = this.subtitleTrackController; 12102 return subtitleTrackController ? subtitleTrackController.subtitleTrack : -1; 12103 }, 12104 /** 12105 * select an subtitle track, based on its index in subtitle track lists 12106 * @type{number} 12107 */ 12108 set: function (subtitleTrackId) { 12109 var subtitleTrackController = this.subtitleTrackController; 12110 if (subtitleTrackController) { 12111 subtitleTrackController.subtitleTrack = subtitleTrackId; 12112 } 12113 }, 12114 enumerable: true, 12115 configurable: true 12116 }); 12117 Object.defineProperty(Hls.prototype, "subtitleDisplay", { 12118 /** 12119 * @type {boolean} 12120 */ 12121 get: function () { 12122 var subtitleTrackController = this.subtitleTrackController; 12123 return subtitleTrackController ? subtitleTrackController.subtitleDisplay : false; 12124 }, 12125 /** 12126 * Enable/disable subtitle display rendering 12127 * @type {boolean} 12128 */ 12129 set: function (value) { 12130 var subtitleTrackController = this.subtitleTrackController; 12131 if (subtitleTrackController) { 12132 subtitleTrackController.subtitleDisplay = value; 12133 } 12134 }, 12135 enumerable: true, 12136 configurable: true 12137 }); 12138 return Hls; 12139}(observer_1.Observer)); 12140exports.default = Hls; 12141 12142 12143/***/ }), 12144
12145/***/ "./src/is-supported.js": 12146/*!*****************************!*\ 12147 !*** ./src/is-supported.js ***! 12148 \*****************************/ 12149/*! no static exports found */ 12150/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 12151/***/ (function(module, exports, __webpack_require__) { 12152 12153"use strict"; 12154 12155Object.defineProperty(exports, "__esModule", { value: true }); 12156var mediasource_helper_1 = __webpack_require__(/*! ./utils/mediasource-helper */ "./src/utils/mediasource-helper.js"); 12157function isSupported() { 12158 var mediaSource = mediasource_helper_1.getMediaSource(); 12159 var sourceBuffer = window.SourceBuffer || window.WebKitSourceBuffer; 12160 var isTypeSupported = mediaSource && 12161 typeof mediaSource.isTypeSupported === 'function' && 12162 mediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"'); 12163 // if SourceBuffer is exposed ensure its API is valid 12164 // safari and old version of Chrome doe not expose SourceBuffer globally so checking SourceBuffer.prototype is impossible 12165 var sourceBufferValidAPI = !sourceBuffer || 12166 (sourceBuffer.prototype && 12167 typeof sourceBuffer.prototype.appendBuffer === 'function' && 12168 typeof sourceBuffer.prototype.remove === 'function'); 12169 return !!isTypeSupported && !!sourceBufferValidAPI; 12170} 12171exports.isSupported = isSupported; 12172 12173 12174/***/ }), 12175
12176/***/ "./src/loader/fragment-loader.js": 12177/*!***************************************!*\ 12178 !*** ./src/loader/fragment-loader.js ***! 12179 \***************************************/ 12180/*! no static exports found */ 12181/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 12182/***/ (function(module, exports, __webpack_require__) { 12183 12184"use strict"; 12185/* WEBPACK VAR INJECTION */(function(Number) { 12186/* 12187 * Fragment Loader 12188*/ 12189var __extends = (this && this.__extends) || (function () { 12190 var extendStatics = Object.setPrototypeOf || 12191 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 12192 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 12193 return function (d, b) { 12194 extendStatics(d, b); 12195 function __() { this.constructor = d; } 12196 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 12197 }; 12198})(); 12199Object.defineProperty(exports, "__esModule", { value: true }); 12200var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 12201var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 12202var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 12203var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 12204var FragmentLoader = /** @class */ (function (_super) { 12205 __extends(FragmentLoader, _super); 12206 function FragmentLoader(hls) { 12207 var _this = _super.call(this, hls, events_1.default.FRAG_LOADING) || this; 12208 _this.loaders = {}; 12209 return _this; 12210 } 12211 FragmentLoader.prototype.destroy = function () { 12212 var loaders = this.loaders; 12213 for (var loaderName in loaders) { 12214 var loader = loaders[loaderName]; 12215 if (loader) { 12216 loader.destroy(); 12217 } 12218 } 12219 this.loaders = {}; 12220 _super.prototype.destroy.call(this); 12221 }; 12222 FragmentLoader.prototype.onFragLoading = function (data) { 12223 var frag = data.frag, type = frag.type, loaders = this.loaders, config = this.hls.config, FragmentILoader = config.fLoader, DefaultILoader = config.loader; 12224 // reset fragment state 12225 frag.loaded = 0; 12226 var loader = loaders[type]; 12227 if (loader) { 12228 logger_1.logger.warn("abort previous fragment loader for type: " + type); 12229 loader.abort(); 12230 } 12231 loader = loaders[type] = frag.loader = 12232 config.fLoader ? new FragmentILoader(config) : new DefaultILoader(config); 12233 var loaderContext, loaderConfig, loaderCallbacks; 12234 loaderContext = { url: frag.url, frag: frag, responseType: 'arraybuffer', progressData: false }; 12235 var start = frag.byteRangeStartOffset, end = frag.byteRangeEndOffset; 12236 if (Number.isFinite(start) && Number.isFinite(end)) { 12237 loaderContext.rangeStart = start; 12238 loaderContext.rangeEnd = end; 12239 } 12240 loaderConfig = { 12241 timeout: config.fragLoadingTimeOut, 12242 maxRetry: 0, 12243 retryDelay: 0, 12244 maxRetryDelay: config.fragLoadingMaxRetryTimeout 12245 }; 12246 loaderCallbacks = { 12247 onSuccess: this.loadsuccess.bind(this), 12248 onError: this.loaderror.bind(this), 12249 onTimeout: this.loadtimeout.bind(this), 12250 onProgress: this.loadprogress.bind(this) 12251 }; 12252 loader.load(loaderContext, loaderConfig, loaderCallbacks); 12253 }; 12254 FragmentLoader.prototype.loadsuccess = function (response, stats, context, networkDetails) { 12255 if (networkDetails === void 0) { networkDetails = null; } 12256 var payload = response.data, frag = context.frag; 12257 // detach fragment loader on load success 12258 frag.loader = undefined; 12259 this.loaders[frag.type] = undefined; 12260 this.hls.trigger(events_1.default.FRAG_LOADED, { payload: payload, frag: frag, stats: stats, networkDetails: networkDetails }); 12261 }; 12262 FragmentLoader.prototype.loaderror = function (response, context, networkDetails) { 12263 if (networkDetails === void 0) { networkDetails = null; } 12264 var frag = context.frag; 12265 var loader = frag.loader; 12266 if (loader) { 12267 loader.abort(); 12268 } 12269 this.loaders[frag.type] = undefined; 12270 this.hls.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.NETWORK_E
12270RROR, details: errors_1.ErrorDetails.FRAG_LOAD_ERROR, fatal: false, frag: context.frag, response: response, networkDetails: networkDetails }); 12271 }; 12272 FragmentLoader.prototype.loadtimeout = function (stats, context, networkDetails) { 12273 if (networkDetails === void 0) { networkDetails = null; } 12274 var frag = context.frag; 12275 var loader = frag.loader; 12276 if (loader) { 12277 loader.abort(); 12278 } 12279 this.loaders[frag.type] = undefined; 12280 this.hls.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.NETWORK_ERROR, details: errors_1.ErrorDetails.FRAG_LOAD_TIMEOUT, fatal: false, frag: context.frag, networkDetails: networkDetails }); 12281 }; 12282 // data will be used for progressive parsing 12283 FragmentLoader.prototype.loadprogress = function (stats, context, data, networkDetails) { 12284 if (networkDetails === void 0) { networkDetails = null; } 12285 var frag = context.frag; 12286 frag.loaded = stats.loaded; 12287 this.hls.trigger(events_1.default.FRAG_LOAD_PROGRESS, { frag: frag, stats: stats, networkDetails: networkDetails }); 12288 }; 12289 return FragmentLoader; 12290}(event_handler_1.default)); 12291exports.default = FragmentLoader; 12292 12293/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 12294 12295/***/ }), 12296
12297/***/ "./src/loader/fragment.js": 12298/*!********************************!*\ 12299 !*** ./src/loader/fragment.js ***! 12300 \********************************/ 12301/*! no static exports found */ 12302/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 12303/***/ (function(module, exports, __webpack_require__) { 12304 12305"use strict"; 12306/* WEBPACK VAR INJECTION */(function(Number) { 12307Object.defineProperty(exports, "__esModule", { value: true }); 12308var URLToolkit = __webpack_require__(/*! url-toolkit */ "./node_modules/url-toolkit/src/url-toolkit.js"); 12309var level_key_1 = __webpack_require__(/*! ./level-key */ "./src/loader/level-key.js"); 12310var Fragment = /** @class */ (function () { 12311 function Fragment() { 12312 var _a; 12313 this._url = null; 12314 this._byteRange = null; 12315 this._decryptdata = null; 12316 this.tagList = []; 12317 this.programDateTime = null; 12318 this.rawProgramDateTime = null; 12319 // Holds the types of data this fragment supports 12320 this._elementaryStreams = (_a = {}, 12321 _a[Fragment.ElementaryStreamTypes.AUDIO] = false, 12322 _a[Fragment.ElementaryStreamTypes.VIDEO] = false, 12323 _a); 12324 } 12325 Object.defineProperty(Fragment, "ElementaryStreamTypes", { 12326 /** 12327 * `type` property for this._elementaryStreams 12328 * 12329 * @enum 12330 */ 12331 get: function () { 12332 return { 12333 AUDIO: 'audio', 12334 VIDEO: 'video' 12335 }; 12336 }, 12337 enumerable: true, 12338 configurable: true 12339 }); 12340 Object.defineProperty(Fragment.prototype, "url", { 12341 get: function () { 12342 if (!this._url && this.relurl) { 12343 this._url = URLToolkit.buildAbsoluteURL(this.baseurl, this.relurl, { alwaysNormalize: true }); 12344 } 12345 return this._url; 12346 }, 12347 set: function (value) { 12348 this._url = value; 12349 }, 12350 enumerable: true, 12351 configurable: true 12352 }); 12353 Object.defineProperty(Fragment.prototype, "byteRange", { 12354 get: function () { 12355 if (!this._byteRange && !this.rawByteRange) { 12356 return []; 12357 } 12358 if (this._byteRange) { 12359 return this._byteRange; 12360 } 12361 var byteRange = []; 12362 if (this.rawByteRange) { 12363 var params = this.rawByteRange.split('@', 2); 12364 if (params.length === 1) { 12365 var lastByteRangeEndOffset = this.lastByteRangeEndOffset; 12366 byteRange[0] = lastByteRangeEndOffset || 0; 12367 } 12368 else { 12369 byteRange[0] = parseInt(params[1]); 12370 } 12371 byteRange[1] = parseInt(params[0]) + byteRange[0]; 12372 this._byteRange = byteRange; 12373 } 12374 return byteRange; 12375 }, 12376 enumerable: true, 12377 configurable: true 12378 }); 12379 Object.defineProperty(Fragment.prototype, "byteRangeStartOffset", { 12380 /** 12381 * @type {number} 12382 */ 12383 get: function () { 12384 return this.byteRange[0]; 12385 }, 12386 enumerable: true, 12387 configurable: true 12388 }); 12389 Object.defineProperty(Fragment.prototype, "byteRangeEndOffset", { 12390 get: function () { 12391 return this.byteRange[1]; 12392 }, 12393 enumerable: true, 12394 configurable: true 12395 }); 12396 Object.defineProperty(Fragment.prototype, "decryptdata", { 12397 get: function () { 12398 if (!this._decryptdata) { 12399 this._decryptdata = this.fragmentDecryptdataFromLevelkey(this.levelkey, this.sn); 12400 } 12401 return this._decryptdata; 12402 }, 12403 enumerable: true, 12404 configurable: true 12405 }); 12406 Object.defineProperty(Fragment.prototype, "endProgramDateTime", { 12407 get: function () { 12408 if (!Number.isFinite(this.programDateTime)) { 12409 return null; 12410 } 12411 var duration = !Number.isFinite(this.duration) ? 0 : this.duration; 12412 return this.programDateTime + (duration * 1000); 12413 }, 12414 enumerable: true, 12415 configurable: true 12416 });
12417 Object.defineProperty(Fragment.prototype, "encrypted", { 12418 get: function () { 12419 return !!((this.decryptdata && this.decryptdata.uri !== null) && (this.decryptdata.key === null)); 12420 }, 12421 enumerable: true, 12422 configurable: true 12423 }); 12424 /** 12425 * @param {ElementaryStreamType} type 12426 */ 12427 Fragment.prototype.addElementaryStream = function (type) { 12428 this._elementaryStreams[type] = true; 12429 }; 12430 /** 12431 * @param {ElementaryStreamType} type 12432 */ 12433 Fragment.prototype.hasElementaryStream = function (type) { 12434 return this._elementaryStreams[type] === true; 12435 }; 12436 /** 12437 * Utility method for parseLevelPlaylist to create an initialization vector for a given segment 12438 * @returns {Uint8Array} 12439 */ 12440 Fragment.prototype.createInitializationVector = function (segmentNumber) { 12441 var uint8View = new Uint8Array(16); 12442 for (var i = 12; i < 16; i++) { 12443 uint8View[i] = (segmentNumber >> 8 * (15 - i)) & 0xff; 12444 } 12445 return uint8View; 12446 }; 12447 /** 12448 * Utility method for parseLevelPlaylist to get a fragment's decryption data from the currently parsed encryption key data 12449 * @param levelkey - a playlist's encryption info 12450 * @param segmentNumber - the fragment's segment number 12451 * @returns {*} - an object to be applied as a fragment's decryptdata 12452 */ 12453 Fragment.prototype.fragmentDecryptdataFromLevelkey = function (levelkey, segmentNumber) { 12454 var decryptdata = levelkey; 12455 if (levelkey && levelkey.method && levelkey.uri && !levelkey.iv) { 12456 decryptdata = new level_key_1.default(); 12457 decryptdata.method = levelkey.method; 12458 decryptdata.baseuri = levelkey.baseuri; 12459 decryptdata.reluri = levelkey.reluri; 12460 decryptdata.iv = this.createInitializationVector(segmentNumber); 12461 } 12462 return decryptdata; 12463 }; 12464 return Fragment; 12465}()); 12466exports.default = Fragment; 12467 12468/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 12469 12470/***/ }), 12471
12472/***/ "./src/loader/key-loader.js": 12473/*!**********************************!*\ 12474 !*** ./src/loader/key-loader.js ***! 12475 \**********************************/ 12476/*! no static exports found */ 12477/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 12478/***/ (function(module, exports, __webpack_require__) { 12479 12480"use strict"; 12481 12482/* 12483 * Decrypt key Loader 12484*/ 12485var __extends = (this && this.__extends) || (function () { 12486 var extendStatics = Object.setPrototypeOf || 12487 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 12488 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 12489 return function (d, b) { 12490 extendStatics(d, b); 12491 function __() { this.constructor = d; } 12492 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 12493 }; 12494})(); 12495Object.defineProperty(exports, "__esModule", { value: true }); 12496var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 12497var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 12498var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 12499var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 12500var KeyLoader = /** @class */ (function (_super) { 12501 __extends(KeyLoader, _super); 12502 function KeyLoader(hls) { 12503 var _this = _super.call(this, hls, events_1.default.KEY_LOADING) || this; 12504 _this.loaders = {}; 12505 _this.decryptkey = null; 12506 _this.decrypturl = null; 12507 return _this; 12508 } 12509 KeyLoader.prototype.destroy = function () { 12510 for (var loaderName in this.loaders) { 12511 var loader = this.loaders[loaderName]; 12512 if (loader) { 12513 loader.destroy(); 12514 } 12515 } 12516 this.loaders = {}; 12517 event_handler_1.default.prototype.destroy.call(this); 12518 }; 12519 KeyLoader.prototype.onKeyLoading = function (data) { 12520 var frag = data.frag, type = frag.type, loader = this.loaders[type], decryptdata = frag.decryptdata, uri = decryptdata.uri; 12521 // if uri is different from previous one or if decrypt key not retrieved yet 12522 if (uri !== this.decrypturl || this.decryptkey === null) { 12523 var config = this.hls.config; 12524 if (loader) { 12525 logger_1.logger.warn("abort previous key loader for type:" + type); 12526 loader.abort(); 12527 } 12528 frag.loader = this.loaders[type] = new config.loader(config); 12529 this.decrypturl = uri; 12530 this.decryptkey = null; 12531 var loaderContext = void 0, loaderConfig = void 0, loaderCallbacks = void 0; 12532 loaderContext = { url: uri, frag: frag, responseType: 'arraybuffer' }; 12533 // maxRetry is 0 so that instead of retrying the same key on the same variant multiple times, 12534 // key-loader will trigger an error and rely on stream-controller to handle retry logic. 12535 // this will also align retry logic with fragment-loader 12536 loaderConfig = { timeout: config.fragLoadingTimeOut, maxRetry: 0, retryDelay: config.fragLoadingRetryDelay, maxRetryDelay: config.fragLoadingMaxRetryTimeout }; 12537 loaderCallbacks = { onSuccess: this.loadsuccess.bind(this), onError: this.loaderror.bind(this), onTimeout: this.loadtimeout.bind(this) }; 12538 frag.loader.load(loaderContext, loaderConfig, loaderCallbacks); 12539 } 12540 else if (this.decryptkey) { 12541 // we already loaded this key, return it 12542 decryptdata.key = this.decryptkey; 12543 this.hls.trigger(events_1.default.KEY_LOADED, { frag: frag }); 12544 } 12545 }; 12546 KeyLoader.prototype.loadsuccess = function (response, stats, context) { 12547 var frag = context.frag; 12548 this.decryptkey = frag.decryptdata.key = new Uint8Array(response.data); 12549 // detach fragment loader on load success 12550 frag.loader = undefined; 12551 this.loaders[frag.type] = undefined; 12552 this.hls.trigger(events_1.default.KEY_LOADED, { frag: frag }); 12553 }; 12554 KeyLoader.prototype.loaderror = function (response, context) { 12555 var frag = context.frag, loader = frag.loader; 12556 if (loader) { 12557 loader.abort(); 12558 } 12559 this.loaders[context.type] = undefined; 12560 this.hls.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.NETWORK_E
12560RROR, details: errors_1.ErrorDetails.KEY_LOAD_ERROR, fatal: false, frag: frag, response: response }); 12561 }; 12562 KeyLoader.prototype.loadtimeout = function (stats, context) { 12563 var frag = context.frag, loader = frag.loader; 12564 if (loader) { 12565 loader.abort(); 12566 } 12567 this.loaders[context.type] = undefined; 12568 this.hls.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.NETWORK_ERROR, details: errors_1.ErrorDetails.KEY_LOAD_TIMEOUT, fatal: false, frag: frag }); 12569 }; 12570 return KeyLoader; 12571}(event_handler_1.default)); 12572exports.default = KeyLoader; 12573 12574 12575/***/ }), 12576
12577/***/ "./src/loader/level-key.js": 12578/*!*********************************!*\ 12579 !*** ./src/loader/level-key.js ***! 12580 \*********************************/ 12581/*! no static exports found */ 12582/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 12583/***/ (function(module, exports, __webpack_require__) { 12584 12585"use strict"; 12586 12587Object.defineProperty(exports, "__esModule", { value: true }); 12588var URLToolkit = __webpack_require__(/*! url-toolkit */ "./node_modules/url-toolkit/src/url-toolkit.js"); 12589var LevelKey = /** @class */ (function () { 12590 function LevelKey() { 12591 this.method = null; 12592 this.key = null; 12593 this.iv = null; 12594 this._uri = null; 12595 } 12596 Object.defineProperty(LevelKey.prototype, "uri", { 12597 get: function () { 12598 if (!this._uri && this.reluri) { 12599 this._uri = URLToolkit.buildAbsoluteURL(this.baseuri, this.reluri, { alwaysNormalize: true }); 12600 } 12601 return this._uri; 12602 }, 12603 enumerable: true, 12604 configurable: true 12605 }); 12606 return LevelKey; 12607}()); 12608exports.default = LevelKey; 12609 12610 12611/***/ }), 12612
12613/***/ "./src/loader/level.js": 12614/*!*****************************!*\ 12615 !*** ./src/loader/level.js ***! 12616 \*****************************/ 12617/*! no static exports found */ 12618/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 12619/***/ (function(module, exports, __webpack_require__) { 12620 12621"use strict"; 12622/* WEBPACK VAR INJECTION */(function(Number) { 12623Object.defineProperty(exports, "__esModule", { value: true }); 12624var Level = /** @class */ (function () { 12625 function Level(baseUrl) { 12626 // Please keep properties in alphabetical order 12627 this.endCC = 0; 12628 this.endSN = 0; 12629 this.fragments = []; 12630 this.initSegment = null; 12631 this.live = true; 12632 this.needSidxRanges = false; 12633 this.startCC = 0; 12634 this.startSN = 0; 12635 this.startTimeOffset = null; 12636 this.targetduration = 0; 12637 this.totalduration = 0; 12638 this.type = null; 12639 this.url = baseUrl; 12640 this.version = null; 12641 } 12642 Object.defineProperty(Level.prototype, "hasProgramDateTime", { 12643 get: function () { 12644 return !!(this.fragments[0] && Number.isFinite(this.fragments[0].programDateTime)); 12645 }, 12646 enumerable: true, 12647 configurable: true 12648 }); 12649 return Level; 12650}()); 12651exports.default = Level; 12652 12653/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 12654 12655/***/ }), 12656
vendor: 10,329 bytes, lines 12657-12878
12657/***/ "./src/loader/m3u8-parser.js": 12658/*!***********************************!*\ 12659 !*** ./src/loader/m3u8-parser.js ***! 12660 \***********************************/ 12661/*! no static exports found */ 12662/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 12663/***/ (function(module, exports, __webpack_require__) { 12664 12665"use strict"; 12666/* WEBPACK VAR INJECTION */(function(Number) { 12667Object.defineProperty(exports, "__esModule", { value: true }); 12668var URLToolkit = __webpack_require__(/*! url-toolkit */ "./node_modules/url-toolkit/src/url-toolkit.js"); 12669var fragment_1 = __webpack_require__(/*! ./fragment */ "./src/loader/fragment.js"); 12670var level_1 = __webpack_require__(/*! ./level */ "./src/loader/level.js"); 12671var level_key_1 = __webpack_require__(/*! ./level-key */ "./src/loader/level-key.js"); 12672var attr_list_1 = __webpack_require__(/*! ../utils/attr-list */ "./src/utils/attr-list.js"); 12673var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 12674var codecs_1 = __webpack_require__(/*! ../utils/codecs */ "./src/utils/codecs.js"); 12675/** 12676 * M3U8 parser 12677 * @module 12678 */ 12679// https://regex101.com is your friend 12680var MASTER_PLAYLIST_REGEX = /#EXT-X-STREAM-INF:([^\n\r]*)[\r\n]+([^\r\n]+)/g; 12681var MASTER_PLAYLIST_MEDIA_REGEX = /#EXT-X-MEDIA:(.*)/g; 12682var LEVEL_PLAYLIST_REGEX_FAST = new RegExp([ 12683 /#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source, 12684 /|(?!#)([\S+ ?]+)/.source, 12685 /|#EXT-X-BYTERANGE:*(.+)/.source, 12686 /|#EXT-X-PROGRAM-DATE-TIME:(.+)/.source, 12687 /|#.*/.source // All other non-segment oriented tags will match with all groups empty 12688].join(''), 'g'); 12689var LEVEL_PLAYLIST_REGEX_SLOW = /(?:(?:#(EXTM3U))|(?:#EXT-X-(PLAYLIST-TYPE):(.+))|(?:#EXT-X-(MEDIA-SEQUENCE): *(\d+))|(?:#EXT-X-(TARGETDURATION): *(\d+))|(?:#EXT-X-(KEY):(.+))|(?:#EXT-X-(START):(.+))|(?:#EXT-X-(ENDLIST))|(?:#EXT-X-(DISCONTINUITY-SEQ)UENCE:(\d+))|(?:#EXT-X-(DIS)CONTINUITY))|(?:#EXT-X-(VERSION):(\d+))|(?:#EXT-X-(MAP):(.+))|(?:(#)([^:]*):(.*))|(?:(#)(.*))(?:.*)\r?\n?/; 12690var MP4_REGEX_SUFFIX = /\.(mp4|m4s|m4v|m4a)$/i; 12691var M3U8Parser = /** @class */ (function () { 12692 function M3U8Parser() { 12693 } 12694 M3U8Parser.findGroup = function (groups, mediaGroupId) { 12695 if (!groups) { 12696 return null; 12697 } 12698 var matchingGroup = null; 12699 for (var i = 0; i < groups.length; i++) { 12700 var group = groups[i]; 12701 if (group.id === mediaGroupId) { 12702 matchingGroup = group; 12703 } 12704 } 12705 return matchingGroup; 12706 }; 12707 M3U8Parser.convertAVC1ToAVCOTI = function (codec) { 12708 var result, avcdata = codec.split('.'); 12709 if (avcdata.length > 2) { 12710 result = avcdata.shift() + '.'; 12711 result += parseInt(avcdata.shift()).toString(16); 12712 result += ('000' + parseInt(avcdata.shift()).toString(16)).substr(-4); 12713 } 12714 else { 12715 result = codec; 12716 } 12717 return result; 12718 }; 12719 M3U8Parser.resolve = function (url, baseUrl) { 12720 return URLToolkit.buildAbsoluteURL(baseUrl, url, { alwaysNormalize: true }); 12721 }; 12722 M3U8Parser.parseMasterPlaylist = function (string, baseurl) { 12723 var levels = [], result; 12724 MASTER_PLAYLIST_REGEX.lastIndex = 0; 12725 function setCodecs(codecs, level) { 12726 ['video', 'audio'].forEach(function (type) { 12727 var filtered = codecs.filter(function (codec) { return codecs_1.isCodecType(codec, type); }); 12728 if (filtered.length) { 12729 var preferred = filtered.filter(function (codec) { 12730 return codec.lastIndexOf('avc1', 0) === 0 || codec.lastIndexOf('mp4a', 0) === 0; 12731 }); 12732 level[type + "Codec"] = preferred.length > 0 ? preferred[0] : filtered[0]; 12733 // remove from list 12734 codecs = codecs.filter(function (codec) { return filtered.indexOf(codec) === -1; }); 12735 } 12736 }); 12737 level.unknownCodecs = codecs; 12738 } 12739 while ((result = MASTER_PLAYLIST_REGEX.exec(string)) != null) { 12740 var level = {}; 12741 var attrs = level.attrs = new attr_list_1.default(result[1]); 12742 level.url = M3U8Parser.resolve(result[2], baseurl); 12743 var resolution = attrs.decimalResolution('RESOLUTION'); 12744 if (resolution) { 12745 level.width = resolution.width; 12746 level.height = resolution.height; 12747 } 12748 level.bitrate = attrs.decimalInteger('AVERAGE-BANDWIDTH') || attrs.decimalInteger('BANDWIDTH'); 12749 level.name = attrs.NAME; 12750 setCodecs([].concat((attrs.CODECS || '').split(/[ ,]+/)), level); 12751 if (level.videoCodec && level.videoCodec.indexOf('avc1') !== -1) { 12752 level.videoCodec = M3U8Parser.convertAVC1ToAVCOTI(level.videoCodec); 12753 } 12754 levels.push(level); 12755 } 12756 return levels; 12757 }; 12758 M3U8Parser.parseMasterPlaylistMedia = function (string, baseurl, type, audioGroups) { 12759 if (audioGroups === void 0) { audioGroups = []; } 12760 var result; 12761 var medias = []; 12762 var id = 0; 12763 MASTER_PLAYLIST_MEDIA_REGEX.lastIndex = 0; 12764 while ((result = MASTER_PLAYLIST_MEDIA_REGEX.exec(string)) !== null) { 12765 var media = {}; 12766 var attrs = new attr_list_1.default(result[1]); 12767 if (attrs.TYPE === type) { 12768 media.groupId = attrs['GROUP-ID']; 12769 media.name = attrs.NAME; 12770 media.type = type; 12771 media.default = (attrs.DEFAULT === 'YES'); 12772 media.autoselect = (attrs.AUTOSELECT === 'YES'); 12773 media.forced = (attrs.FORCED === 'YES'); 12774 if (attrs.URI) { 12775 media.url = M3U8Parser.resolve(attrs.URI, baseurl); 12776 } 12777 media.lang = attrs.LANGUAGE; 12778 if (!media.name) { 12779 media.name = media.lang; 12780 } 12781 if (audioGroups.length) { 12782 var groupCodec = M3U8Parser.findGroup(audioGroups, media.groupId); 12783 media.audioCodec = groupCodec ? groupCodec.codec : audioGroups[0].codec; 12784 } 12785 media.id = id++; 12786 medias.push(media); 12787 } 12788 } 12789 return medias; 12790 }; 12791 M3U8Parser.parseLevelPlaylist = function (string, baseurl, id, type, levelUrlId) { 12792 var currentSN = 0; 12793 var totalduration = 0; 12794 var level = new level_1.default(baseurl); 12795 var levelkey = new level_key_1.default(); 12796 var cc = 0; 12797 var prevFrag = null; 12798 var frag = new fragment_1.default(); 12799 var result; 12800 var i; 12801 var firstPdtIndex = null; 12802 LEVEL_PLAYLIST_REGEX_FAST.lastIndex = 0; 12803 while ((result = LEVEL_PLAYLIST_REGEX_FAST.exec(string)) !== null) { 12804 var duration = result[1]; 12805 if (duration) { // INF 12806 frag.duration = parseFloat(duration); 12807 // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 12808 var title = (' ' + result[2]).slice(1); 12809 frag.title = title || null; 12810 frag.tagList.push(title ? ['INF', duration, title] : ['INF', duration]); 12811 } 12812 else if (result[3]) { // url 12813 if (Number.isFinite(frag.duration)) { 12814 var sn = currentSN++; 12815 frag.type = type; 12816 frag.start = totalduration; 12817 frag.levelkey = levelkey; 12818 frag.sn = sn; 12819 frag.level = id; 12820 frag.cc = cc; 12821 frag.urlId = levelUrlId; 12822 frag.baseurl = baseurl; 12823 // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 12824 frag.relurl = (' ' + result[3]).slice(1); 12825 assignProgramDateTime(frag, prevFrag); 12826 level.fragments.push(frag); 12827 prevFrag = frag; 12828 totalduration += frag.duration; 12829 frag = new fragment_1.default(); 12830 } 12831 } 12832 else if (result[4]) { // X-BYTERANGE 12833 frag.rawByteRange = (' ' + result[4]).slice(1); 12834 if (prevFrag) { 12835 var lastByteRangeEndOffset = prevFrag.byteRangeEndOffset; 12836 if (lastByteRangeEndOffset) { 12837 frag.lastByteRangeEndOffset = lastByteRangeEndOffset; 12838 } 12839 } 12840 } 12841 else if (result[5]) { // PROGRAM-DATE-TIME 12842 // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 12843 frag.rawProgramDateTime = (' ' + result[5]).slice(1); 12844 frag.tagList.push(['PROGRAM-DATE-TIME', frag.rawProgramDateTime]); 12845 if (firstPdtIndex === null) { 12846 firstPdtIndex = level.fragments.length; 12847 } 12848 } 12849 else { 12850 result = result[0].match(LEVEL_PLAYLIST_REGEX_SLOW); 12851 for (i = 1; i < result.length; i++) { 12852 if (result[i] !== undefined) { 12853 break; 12854 } 12855 } 12856 // avoid sliced strings https://github.com/video-dev/hls.js/issues/939 12857 var value1 = (' ' + result[i + 1]).slice(1); 12858 var value2 = (' ' + result[i + 2]).slice(1); 12859 switch (result[i]) { 12860 case '#': 12861 frag.tagList.push(value2 ? [value1, value2] : [value1]); 12862 break; 12863 case 'PLAYLIST-TYPE': 12864 level.type = value1.toUpperCase(); 12865 break; 12866 case 'MEDIA-SEQUENCE': 12867 currentSN = level.startSN = parseInt(value1); 12868 break; 12869 case 'TARGETDURATION': 12870 level.targetduration = parseFloat(value1); 12871 break; 12872 case 'VERSION': 12873 level.version = parseInt(value1); 12874 break; 12875 case 'EXTM3U': 12876 break; 12877 case 'ENDLIST': 12878 level.live = false;
12879 break; 12880 case 'DIS': 12881 cc++; 12882 frag.tagList.push(['DIS']); 12883 break; 12884 case 'DISCONTINUITY-SEQ': 12885 cc = parseInt(value1); 12886 break; 12887 case 'KEY': 12888 // https://tools.ietf.org/html/draft-pantos-http-live-streaming-08#section-3.4.4 12889 var decryptparams = value1; 12890 var keyAttrs = new attr_list_1.default(decryptparams); 12891 var decryptmethod = keyAttrs.enumeratedString('METHOD'), decrypturi = keyAttrs.URI, decryptiv = keyAttrs.hexadecimalInteger('IV'); 12892 if (decryptmethod) { 12893 levelkey = new level_key_1.default(); 12894 if ((decrypturi) && (['AES-128', 'SAMPLE-AES', 'SAMPLE-AES-CENC'].indexOf(decryptmethod) >= 0)) { 12895 levelkey.method = decryptmethod; 12896 // URI to get the key 12897 levelkey.baseuri = baseurl; 12898 levelkey.reluri = decrypturi; 12899 levelkey.key = null; 12900 // Initialization Vector (IV) 12901 levelkey.iv = decryptiv; 12902 } 12903 } 12904 break; 12905 case 'START': 12906 var startParams = value1; 12907 var startAttrs = new attr_list_1.default(startParams); 12908 var startTimeOffset = startAttrs.decimalFloatingPoint('TIME-OFFSET'); 12909 // TIME-OFFSET can be 0 12910 if (Number.isFinite(startTimeOffset)) { 12911 level.startTimeOffset = startTimeOffset; 12912 } 12913 break; 12914 case 'MAP': 12915 var mapAttrs = new attr_list_1.default(value1); 12916 frag.relurl = mapAttrs.URI; 12917 frag.rawByteRange = mapAttrs.BYTERANGE; 12918 frag.baseurl = baseurl; 12919 frag.level = id; 12920 frag.type = type; 12921 frag.sn = 'initSegment'; 12922 level.initSegment = frag; 12923 frag = new fragment_1.default(); 12924 frag.rawProgramDateTime = level.initSegment.rawProgramDateTime; 12925 break; 12926 default: 12927 logger_1.logger.warn("line parsed but not handled: " + result); 12928 break; 12929 } 12930 } 12931 } 12932 frag = prevFrag; 12933 // logger.log('found ' + level.fragments.length + ' fragments'); 12934 if (frag && !frag.relurl) { 12935 level.fragments.pop(); 12936 totalduration -= frag.duration; 12937 } 12938 level.totalduration = totalduration; 12939 level.averagetargetduration = totalduration / level.fragments.length; 12940 level.endSN = currentSN - 1; 12941 level.startCC = level.fragments[0] ? level.fragments[0].cc : 0; 12942 level.endCC = cc; 12943 if (!level.initSegment && level.fragments.length) { 12944 // this is a bit lurky but HLS really has no other way to tell us 12945 // if the fragments are TS or MP4, except if we download them :/ 12946 // but this is to be able to handle SIDX. 12947 if (level.fragments.every(function (frag) { return MP4_REGEX_SUFFIX.test(frag.relurl); })) { 12948 logger_1.logger.warn('MP4 fragments found but no init segment (probably no MAP, incomplete M3U8), trying to fetch SIDX'); 12949 frag = new fragment_1.default(); 12950 frag.relurl = level.fragments[0].relurl; 12951 frag.baseurl = baseurl; 12952 frag.level = id; 12953 frag.type = type; 12954 frag.sn = 'initSegment'; 12955 level.initSegment = frag; 12956 level.needSidxRanges = true; 12957 } 12958 } 12959 /** 12960 * Backfill any missing PDT values 12961 "If the first EXT-X-PROGRAM-DATE-TIME tag in a Playlist appears after 12962 one or more Media Segment URIs, the client SHOULD extrapolate 12963 backward from that tag (using EXTINF durations and/or media 12964 timestamps) to associate dates with those segments." 12965 * We have already extrapolated forward, but all fragments up to the first instance of PDT do not have their PDTs 12966 * computed. 12967 */ 12968 if (firstPdtIndex) { 12969 backfillProgramDateTimes(level.fragments, firstPdtIndex); 12970 } 12971 return level; 12972 }; 12973 return M3U8Parser; 12974}()); 12975exports.default = M3U8Parser; 12976function backfillProgramDateTimes(fragments, startIndex) { 12977 var fragPrev = fragments[startIndex]; 12978 for (var i = startIndex - 1; i >= 0; i--) { 12979 var frag = fragments[i]; 12980 frag.programDateTime = fragPrev.programDateTime - (frag.duration * 1000); 12981 fragPrev = frag; 12982 } 12983} 12984function assignProgramDateTime(frag, prevFrag) { 12985 if (frag.rawProgramDateTime) { 12986 frag.programDateTime = Date.parse(frag.rawProgramDateTime); 12987 } 12988 else if (prevFrag && prevFrag.programDateTime) { 12989 frag.programDateTime = prevFrag.endProgramDateTime; 12990 } 12991 if (!Number.isFinite(frag.programDateTime)) { 12992 frag.programDateTime = null; 12993 frag.rawProgramDateTime = null; 12994 } 12995} 12996 12997/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 12998 12999/***/ }), 13000
13001/***/ "./src/loader/playlist-loader.js": 13002/*!***************************************!*\ 13003 !*** ./src/loader/playlist-loader.js ***! 13004 \***************************************/ 13005/*! no static exports found */ 13006/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 13007/***/ (function(module, exports, __webpack_require__) { 13008 13009"use strict"; 13010/* WEBPACK VAR INJECTION */(function(Number) { 13011/** 13012 * PlaylistLoader - delegate for media manifest/playlist loading tasks. Takes care of parsing media to internal data-models. 13013 * 13014 * Once loaded, dispatches events with parsed data-models of manifest/levels/audio/subtitle tracks. 13015 * 13016 * Uses loader(s) set in config to do actual internal loading of resource tasks. 13017 * 13018 * @module 13019 * 13020 */ 13021var __extends = (this && this.__extends) || (function () { 13022 var extendStatics = Object.setPrototypeOf || 13023 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 13024 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 13025 return function (d, b) { 13026 extendStatics(d, b); 13027 function __() { this.constructor = d; } 13028 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 13029 }; 13030})(); 13031Object.defineProperty(exports, "__esModule", { value: true }); 13032var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 13033var event_handler_1 = __webpack_require__(/*! ../event-handler */ "./src/event-handler.js"); 13034var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 13035var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 13036var mp4demuxer_1 = __webpack_require__(/*! ../demux/mp4demuxer */ "./src/demux/mp4demuxer.js"); 13037var m3u8_parser_1 = __webpack_require__(/*! ./m3u8-parser */ "./src/loader/m3u8-parser.js"); 13038var performance = window.performance; 13039/** 13040 * `type` property values for this loaders' context object 13041 * @enum 13042 * 13043 */ 13044var ContextType = { 13045 MANIFEST: 'manifest', 13046 LEVEL: 'level', 13047 AUDIO_TRACK: 'audioTrack', 13048 SUBTITLE_TRACK: 'subtitleTrack' 13049}; 13050/** 13051 * @enum {string} 13052 */ 13053var LevelType = { 13054 MAIN: 'main', 13055 AUDIO: 'audio', 13056 SUBTITLE: 'subtitle' 13057}; 13058/** 13059 * @constructor 13060 */ 13061var PlaylistLoader = /** @class */ (function (_super) { 13062 __extends(PlaylistLoader, _super); 13063 /** 13064 * @constructs 13065 * @param {Hls} hls 13066 */ 13067 function PlaylistLoader(hls) { 13068 var _this = _super.call(this, hls, events_1.default.MANIFEST_LOADING, events_1.default.LEVEL_LOADING, events_1.default.AUDIO_TRACK_LOADING, events_1.default.SUBTITLE_TRACK_LOADING) || this; 13069 _this.loaders = {}; 13070 return _this; 13071 } 13072 Object.defineProperty(PlaylistLoader, "ContextType", { 13073 get: function () { 13074 return ContextType; 13075 }, 13076 enumerable: true, 13077 configurable: true 13078 }); 13079 Object.defineProperty(PlaylistLoader, "LevelType", { 13080 get: function () { 13081 return LevelType; 13082 }, 13083 enumerable: true, 13084 configurable: true 13085 }); 13086 /** 13087 * @param {ContextType} type 13088 * @returns {boolean} 13089 */ 13090 PlaylistLoader.canHaveQualityLevels = function (type) { 13091 return (type !== ContextType.AUDIO_TRACK && 13092 type !== ContextType.SUBTITLE_TRACK); 13093 }; 13094 /** 13095 * Map context.type to LevelType 13096 * @param {{type: ContextType}} context 13097 * @returns {LevelType} 13098 */ 13099 PlaylistLoader.mapContextToLevelType = function (context) { 13100 var type = context.type; 13101 switch (type) { 13102 case ContextType.AUDIO_TRACK: 13103 return LevelType.AUDIO; 13104 case ContextType.SUBTITLE_TRACK: 13105 return LevelType.SUBTITLE; 13106 default: 13107 return LevelType.MAIN; 13108 } 13109 }; 13110 PlaylistLoader.getResponseUrl = function (response, context) { 13111 var url = response.url; 13112 // responseURL not supported on some browsers (it is used to detect URL redirection) 13113 // data-uri mode also not supported (but no need to detect redirection) 13114 if (url === undefined || url.indexOf('data:') === 0) { 13115 // fallback to initial URL 13116 url = context.url; 13117 } 13118 return url; 13119 }; 13120 /** 13121 * Returns defaults or configured loader-type overloads (pLoader and loader config params) 13122 * Default loader is XHRLoader (see utils) 13123 * @param {object} context 13124 * @returns {XHRLoader} or other compatible configured overload 13125 */ 13126 PlaylistLoader.prototype.createInternalLoader = function (context) { 13127 var config = this.hls.config; 13128 var PLoader = config.pLoader; 13129 var Loader = config.loader; 13130 var InternalLoader = PLoader || Loader; 13131 var loader = new InternalLoader(config); 13132 context.loader = loader; 13133 this.loaders[context.type] = loader; 13134 return loader; 13135 }; 13136 PlaylistLoader.prototype.getInternalLoader = function (context) { 13137 return this.loaders[context.type]; 13138 }; 13139 PlaylistLoader.prototype.resetInternalLoader = function (contextType) { 13140 if (this.loaders[contextType]) { 13141 delete this.loaders[contextType]; 13142 } 13143 }; 13144 /** 13145 * Call `destroy` on all internal loader instances mapped (one per context type) 13146 */ 13147 PlaylistLoader.prototype.destroyInternalLoaders = function () { 13148 for (var contextType in this.loaders) { 13149 var loader = this.loaders[contextType]; 13150 if (loader) { 13151 loader.destroy(); 13152 } 13153 this.resetInternalLoader(contextType); 13154 } 13155 }; 13156 PlaylistLoader.prototype.destroy = function () { 13157 this.destroyInternalLoaders(); 13158 _super.prototype.destroy.call(this); 13159 }; 13160 PlaylistLoader.prototype.onManifestLoading = function (data) { 13161 this.load(data.url, { type: ContextType.MANIFEST, level: 0, id: null }); 13162 }; 13163 PlaylistLoader.prototype.onLevelLoading = function (data) { 13164 this.load(data.url, { type: ContextType.LEVEL, level: data.level, id: data.id }); 13165 }; 13166 PlaylistLoader.prototype.onAudioTrackLoading = function (data) { 13167 this.load(data.url, { type: ContextType.AUDIO_TRACK, level: null, id: data.id }); 13168 }; 13169 PlaylistLoader.prototype.onSubtitleTrackLoading = function (data) { 13170 this.load(data.url, { type: ContextType.SUBTITLE_TRACK, level: null, id: data.id }); 13171 }; 13172 PlaylistLoader.prototype.load = function (url, context) { 13173 var config = this.hls.config; 13174 logger_1.logger.debug("Loading playlist of type " + context.type + ", level: " + context.level + ", id: " + context.
13174id); 13175 // Check if a loader for this context already exists 13176 var loader = this.getInternalLoader(context); 13177 if (loader) { 13178 var loaderContext = loader.context; 13179 if (loaderContext && loaderContext.url === url) { // same URL can't overlap 13180 logger_1.logger.trace('playlist request ongoing'); 13181 return false; 13182 } 13183 else { 13184 logger_1.logger.warn("aborting previous loader for type: " + context.type); 13185 loader.abort(); 13186 } 13187 } 13188 var maxRetry, timeout, retryDelay, maxRetryDelay; 13189 // apply different configs for retries depending on 13190 // context (manifest, level, audio/subs playlist) 13191 switch (context.type) { 13192 case ContextType.MANIFEST: 13193 maxRetry = config.manifestLoadingMaxRetry; 13194 timeout = config.manifestLoadingTimeOut; 13195 retryDelay = config.manifestLoadingRetryDelay; 13196 maxRetryDelay = config.manifestLoadingMaxRetryTimeout; 13197 break; 13198 case ContextType.LEVEL: 13199 // Disable internal loader retry logic, since we are managing retries in Level Controller 13200 maxRetry = 0; 13201 timeout = config.levelLoadingTimeOut; 13202 // TODO Introduce retry settings for audio-track and subtitle-track, it should not use level retry config 13203 break; 13204 default: 13205 maxRetry = config.levelLoadingMaxRetry; 13206 timeout = config.levelLoadingTimeOut; 13207 retryDelay = config.levelLoadingRetryDelay; 13208 maxRetryDelay = config.levelLoadingMaxRetryTimeout; 13209 break; 13210 } 13211 loader = this.createInternalLoader(context); 13212 context.url = url; 13213 context.responseType = context.responseType || ''; // FIXME: (should not be necessary to do this) 13214 var loaderConfig = { 13215 timeout: timeout, 13216 maxRetry: maxRetry, 13217 retryDelay: retryDelay, 13218 maxRetryDelay: maxRetryDelay 13219 }; 13220 var loaderCallbacks = { 13221 onSuccess: this.loadsuccess.bind(this), 13222 onError: this.loaderror.bind(this), 13223 onTimeout: this.loadtimeout.bind(this) 13224 }; 13225 logger_1.logger.debug("Calling internal loader delegate for URL: " + url); 13226 loader.load(context, loaderConfig, loaderCallbacks); 13227 return true; 13228 }; 13229 PlaylistLoader.prototype.loadsuccess = function (response, stats, context, networkDetails) { 13230 if (networkDetails === void 0) { networkDetails = null; } 13231 if (context.isSidxRequest) { 13232 this._handleSidxRequest(response, context); 13233 this._handlePlaylistLoaded(response, stats, context, networkDetails); 13234 return; 13235 } 13236 this.resetInternalLoader(context.type); 13237 var string = response.data; 13238 stats.tload = performance.now(); 13239 // stats.mtime = new Date(target.getResponseHeader('Last-Modified')); 13240 // Validate if it is an M3U8 at all 13241 if (string.indexOf('#EXTM3U') !== 0) { 13242 this._handleManifestParsingError(response, context, 'no EXTM3U delimiter', networkDetails); 13243 return; 13244 } 13245 // Check if chunk-list or master. handle empty chunk list case (first EXTINF not signaled, but TARGETDURATION present) 13246 if (string.indexOf('#EXTINF:') > 0 || string.indexOf('#EXT-X-TARGETDURATION:') > 0) { 13247 this._handleTrackOrLevelPlaylist(response, stats, context, networkDetails); 13248 } 13249 else { 13250 this._handleMasterPlaylist(response, stats, context, networkDetails); 13251 } 13252 }; 13253 PlaylistLoader.prototype.loaderror = function (response, context, networkDetails) { 13254 if (networkDetails === void 0) { networkDetails = null; } 13255 this._handleNetworkError(context, networkDetails, false, response); 13256 }; 13257 PlaylistLoader.prototype.loadtimeout = function (stats, context, networkDetails) { 13258 if (networkDetails === void 0) { networkDetails = null; } 13259 this._handleNetworkError(context, networkDetails, true); 13260 }; 13261 PlaylistLoader.prototype._handleMasterPlaylist = function (response, stats, context, networkDetails) { 13262 var hls = this.hls; 13263 var string = response.data; 13264 var url = PlaylistLoader.getResponseUrl(response, context); 13265 var levels = m3u8_parser_1.default.parseMasterPlaylist(string, url); 13266 if (!levels.length) { 13267 this._handleManifestParsingError(response, context, 'no level found in manifest', networkDetails); 13268 return; 13269 } 13270 // multi level playlist, parse level info 13271 var audioGroups = levels.map(function (level) { return ({ 13272 id: level.attrs.AUDIO, 13273 codec: level.audioCodec 13274 }); }); 13275 var audioTracks = m3u8_parser_1.default.parseMasterPlaylistMedia(string, url, 'AUDIO', audioGroups); 13276 var subtitles = m3u8_parser_1.default.parseMasterPlaylistMedia(string, url, 'SUBTITLES'); 13277 if (audioTracks.length) { 13278 // check if we have found an audio track embedded in main playlist (audio track without URI attribute) 13279 var embeddedAudioFound_1 = false;
13280 audioTracks.forEach(function (audioTrack) { 13281 if (!audioTrack.url) { 13282 embeddedAudioFound_1 = true; 13283 } 13284 }); 13285 // if no embedded audio track defined, but audio codec signaled in quality level, 13286 // we need to signal this main audio track this could happen with playlists with 13287 // alt audio rendition in which quality levels (main) 13288 // contains both audio+video. but with mixed audio track not signaled 13289 if (embeddedAudioFound_1 === false && levels[0].audioCodec && !levels[0].attrs.AUDIO) { 13290 logger_1.logger.log('audio codec signaled in quality level, but no embedded audio track signaled, create one'); 13291 audioTracks.unshift({ 13292 type: 'main', 13293 name: 'main' 13294 }); 13295 } 13296 } 13297 hls.trigger(events_1.default.MANIFEST_LOADED, { 13298 levels: levels, 13299 audioTracks: audioTracks, 13300 subtitles: subtitles, 13301 url: url, 13302 stats: stats, 13303 networkDetails: networkDetails 13304 }); 13305 }; 13306 PlaylistLoader.prototype._handleTrackOrLevelPlaylist = function (response, stats, context, networkDetails) { 13307 var hls = this.hls; 13308 var id = context.id, level = context.level, type = context.type; 13309 var url = PlaylistLoader.getResponseUrl(response, context); 13310 var levelUrlId = Number.isFinite(id) ? id : 0; 13311 var levelId = Number.isFinite(level) ? level : levelUrlId; 13312 var levelType = PlaylistLoader.mapContextToLevelType(context); 13313 var levelDetails = m3u8_parser_1.default.parseLevelPlaylist(response.data, url, levelId, levelType, levelUrlId); 13314 // set stats on level structure 13315 levelDetails.tload = stats.tload; 13316 // We have done our first request (Manifest-type) and receive 13317 // not a master playlist but a chunk-list (track/level) 13318 // We fire the manifest-loaded event anyway with the parsed level-details 13319 // by creating a single-level structure for it. 13320 if (type === ContextType.MANIFEST) { 13321 var singleLevel = { 13322 url: url, 13323 details: levelDetails 13324 }; 13325 hls.trigger(events_1.default.MANIFEST_LOADED, { 13326 levels: [singleLevel], 13327 audioTracks: [], 13328 url: url, 13329 stats: stats, 13330 networkDetails: networkDetails 13331 }); 13332 } 13333 // save parsing time 13334 stats.tparsed = performance.now(); 13335 // in case we need SIDX ranges 13336 // return early after calling load for 13337 // the SIDX box. 13338 if (levelDetails.needSidxRanges) { 13339 var sidxUrl = levelDetails.initSegment.url; 13340 this.load(sidxUrl, { 13341 isSidxRequest: true, 13342 type: type, 13343 level: level, 13344 levelDetails: levelDetails, 13345 id: id, 13346 rangeStart: 0, 13347 rangeEnd: 2048, 13348 responseType: 'arraybuffer' 13349 }); 13350 return; 13351 } 13352 // extend the context with the new levelDetails property 13353 context.levelDetails = levelDetails; 13354 this._handlePlaylistLoaded(response, stats, context, networkDetails); 13355 }; 13356 PlaylistLoader.prototype._handleSidxRequest = function (response, context) { 13357 var sidxInfo = mp4demuxer_1.default.parseSegmentIndex(new Uint8Array(response.data)); 13358 // if provided fragment does not contain sidx, early return 13359 if (!sidxInfo) { 13360 return; 13361 } 13362 var sidxReferences = sidxInfo.references; 13363 var levelDetails = context.levelDetails; 13364 sidxReferences.forEach(function (segmentRef, index) { 13365 var segRefInfo = segmentRef.info; 13366 var frag = levelDetails.fragments[index]; 13367 if (frag.byteRange.length === 0) { 13368 frag.rawByteRange = String(1 + segRefInfo.end - segRefInfo.start) + '@' + String(segRefInfo.start); 13369 } 13370 }); 13371 levelDetails.initSegment.rawByteRange = String(sidxInfo.moovEndOffset) + '@0'; 13372 }; 13373 PlaylistLoader.prototype._handleManifestParsingError = function (response, context, reason, networkDetails) { 13374 this.hls.trigger(events_1.default.ERROR, {
13375 type: errors_1.ErrorTypes.NETWORK_ERROR, 13376 details: errors_1.ErrorDetails.MANIFEST_PARSING_ERROR, 13377 fatal: true, 13378 url: response.url, 13379 reason: reason, 13380 networkDetails: networkDetails 13381 }); 13382 }; 13383 PlaylistLoader.prototype._handleNetworkError = function (context, networkDetails, timeout, response) { 13384 if (timeout === void 0) { timeout = false; } 13385 if (response === void 0) { response = null; } 13386 logger_1.logger.info("A network error occured while loading a " + context.type + "-type playlist"); 13387 var details; 13388 var fatal; 13389 var loader = this.getInternalLoader(context); 13390 switch (context.type) { 13391 case ContextType.MANIFEST: 13392 details = (timeout ? errors_1.ErrorDetails.MANIFEST_LOAD_TIMEOUT : errors_1.ErrorDetails.MANIFEST_LOAD_ERROR); 13393 fatal = true; 13394 break; 13395 case ContextType.LEVEL: 13396 details = (timeout ? errors_1.ErrorDetails.LEVEL_LOAD_TIMEOUT : errors_1.ErrorDetails.LEVEL_LOAD_ERROR); 13397 fatal = false; 13398 break; 13399 case ContextType.AUDIO_TRACK: 13400 details = (timeout ? errors_1.ErrorDetails.AUDIO_TRACK_LOAD_TIMEOUT : errors_1.ErrorDetails.AUDIO_TRACK_LOAD_ERROR); 13401 fatal = false; 13402 break; 13403 default: 13404 // details = ...? 13405 fatal = false; 13406 } 13407 if (loader) { 13408 loader.abort(); 13409 this.resetInternalLoader(context.type); 13410 } 13411 var errorData = { 13412 type: errors_1.ErrorTypes.NETWORK_ERROR, 13413 details: details, 13414 fatal: fatal, 13415 url: loader.url, 13416 loader: loader, 13417 context: context, 13418 networkDetails: networkDetails 13419 }; 13420 if (response) { 13421 errorData.response = response; 13422 } 13423 this.hls.trigger(events_1.default.ERROR, errorData); 13424 }; 13425 PlaylistLoader.prototype._handlePlaylistLoaded = function (response, stats, context, networkDetails) { 13426 var type = context.type, level = context.level, id = context.id, levelDetails = context.levelDetails; 13427 if (!levelDetails.targetduration) { 13428 this._handleManifestParsingError(response, context, 'invalid target duration', networkDetails); 13429 return; 13430 } 13431 var canHaveLevels = PlaylistLoader.canHaveQualityLevels(context.type); 13432 if (canHaveLevels) { 13433 this.hls.trigger(events_1.default.LEVEL_LOADED, { 13434 details: levelDetails, 13435 level: level || 0, 13436 id: id || 0, 13437 stats: stats, 13438 networkDetails: networkDetails 13439 }); 13440 } 13441 else { 13442 switch (type) { 13443 case ContextType.AUDIO_TRACK: 13444 this.hls.trigger(events_1.default.AUDIO_TRACK_LOADED, { 13445 details: levelDetails, 13446 id: id, 13447 stats: stats, 13448 networkDetails: networkDetails 13449 }); 13450 break; 13451 case ContextType.SUBTITLE_TRACK: 13452 this.hls.trigger(events_1.default.SUBTITLE_TRACK_LOADED, { 13453 details: levelDetails, 13454 id: id, 13455 stats: stats, 13456 networkDetails: networkDetails 13457 }); 13458 break; 13459 } 13460 } 13461 }; 13462 return PlaylistLoader; 13463}(event_handler_1.default)); 13464exports.default = PlaylistLoader; 13465 13466/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 13467 13468/***/ }), 13469
13470/***/ "./src/observer.js": 13471/*!*************************!*\ 13472 !*** ./src/observer.js ***! 13473 \*************************/ 13474/*! no static exports found */ 13475/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 13476/***/ (function(module, exports, __webpack_require__) { 13477 13478"use strict"; 13479 13480var __extends = (this && this.__extends) || (function () { 13481 var extendStatics = Object.setPrototypeOf || 13482 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 13483 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 13484 return function (d, b) { 13485 extendStatics(d, b); 13486 function __() { this.constructor = d; } 13487 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 13488 }; 13489})(); 13490Object.defineProperty(exports, "__esModule", { value: true }); 13491var eventemitter3_1 = __webpack_require__(/*! eventemitter3 */ "./node_modules/eventemitter3/index.js"); 13492/** 13493 * @class 13494 * 13495 * Simple adapter sub-class of Nodejs-like EventEmitter. 13496 * 13497 * We simply want to pass along the event-name itself 13498 * in every call to a handler, which is the purpose of our `trigger` method 13499 * extending the standard API. 13500 * 13501 */ 13502var Observer = /** @class */ (function (_super) { 13503 __extends(Observer, _super); 13504 function Observer() { 13505 return _super !== null && _super.apply(this, arguments) || this; 13506 } 13507 /** 13508 * 13509 * @param {string} event 13510 * @param {any} data 13511 */ 13512 Observer.prototype.trigger = function (event) { 13513 var data = []; 13514 for (var _i = 1; _i < arguments.length; _i++) { 13515 data[_i - 1] = arguments[_i]; 13516 } 13517 this.emit.apply(this, [event, event].concat(data)); 13518 }; 13519 return Observer; 13520}(eventemitter3_1.EventEmitter)); 13521exports.Observer = Observer; 13522 13523 13524/***/ }), 13525
13526/***/ "./src/polyfills/number.js": 13527/*!*********************************!*\ 13528 !*** ./src/polyfills/number.js ***! 13529 \*********************************/ 13530/*! no static exports found */ 13531/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 13532/***/ (function(module, exports, __webpack_require__) { 13533 13534"use strict"; 13535 13536Object.defineProperty(exports, "__esModule", { value: true }); 13537var get_self_scope_1 = __webpack_require__(/*! ../utils/get-self-scope */ "./src/utils/get-self-scope.js"); 13538var self = get_self_scope_1.getSelfScope(); 13539var Number = self.Number; 13540exports.Number = Number; 13541// TODO: get rid of global polyfills and replace them with wrappers ("ponyfills") 13542Number.isFinite = Number.isFinite || function (value) { 13543 return typeof value === 'number' && isFinite(value); 13544}; 13545 13546 13547/***/ }), 13548
13549/***/ "./src/remux/aac-helper.js": 13550/*!*********************************!*\ 13551 !*** ./src/remux/aac-helper.js ***! 13552 \*********************************/ 13553/*! no static exports found */ 13554/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 13555/***/ (function(module, exports, __webpack_require__) { 13556 13557"use strict"; 13558 13559/** 13560 * AAC helper 13561 */ 13562Object.defineProperty(exports, "__esModule", { value: true }); 13563var AAC = /** @class */ (function () { 13564 function AAC() { 13565 } 13566 AAC.getSilentFrame = function (codec, channelCount) { 13567 switch (codec) { 13568 case 'mp4a.40.2': 13569 if (channelCount === 1) { 13570 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]); 13571 } 13572 else if (channelCount === 2) { 13573 return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]); 13574 } 13575 else if (channelCount === 3) { 13576 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]); 13577 } 13578 else if (channelCount === 4) { 13579 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]); 13580 } 13581 else if (channelCount === 5) { 13582 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]); 13583 } 13584 else if (channelCount === 6) { 13585 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]); 13586 } 13587 break; 13588 // handle HE-AAC below (mp4a.40.5 / mp4a.40.29) 13589 default: 13590 if (channelCount === 1) { 13591 // 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 13592 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]); 13593 } 13594 else if (channelCount === 2) { 13595 // 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 13596 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]); 13597 } 13598 else if (channelCount === 3) { 13599 // 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 13600 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]); 13601 } 13602 break; 13603 } 13604 return null; 13605 }; 13606 return AAC; 13607}()); 13608exports.default = AAC; 13609 13610 13611/***/ }), 13612
vendor: 4,508 bytes, lines 13613-13762
13613/***/ "./src/remux/mp4-generator.js": 13614/*!************************************!*\ 13615 !*** ./src/remux/mp4-generator.js ***! 13616 \************************************/ 13617/*! no static exports found */ 13618/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 13619/***/ (function(module, exports, __webpack_require__) { 13620 13621"use strict"; 13622 13623/** 13624 * Generate MP4 Box 13625*/ 13626Object.defineProperty(exports, "__esModule", { value: true }); 13627var UINT32_MAX = Math.pow(2, 32) - 1; 13628var MP4 = /** @class */ (function () { 13629 function MP4() { 13630 } 13631 MP4.init = function () { 13632 MP4.types = { 13633 avc1: [], 13634 avcC: [], 13635 btrt: [], 13636 dinf: [], 13637 dref: [], 13638 esds: [], 13639 ftyp: [], 13640 hdlr: [], 13641 mdat: [], 13642 mdhd: [], 13643 mdia: [], 13644 mfhd: [], 13645 minf: [], 13646 moof: [], 13647 moov: [], 13648 mp4a: [], 13649 '.mp3': [], 13650 mvex: [], 13651 mvhd: [], 13652 pasp: [], 13653 sdtp: [], 13654 stbl: [], 13655 stco: [], 13656 stsc: [], 13657 stsd: [], 13658 stsz: [], 13659 stts: [], 13660 tfdt: [], 13661 tfhd: [], 13662 traf: [], 13663 trak: [], 13664 trun: [], 13665 trex: [], 13666 tkhd: [], 13667 vmhd: [], 13668 smhd: [] 13669 }; 13670 var i; 13671 for (i in MP4.types) { 13672 if (MP4.types.hasOwnProperty(i)) { 13673 MP4.types[i] = [ 13674 i.charCodeAt(0), 13675 i.charCodeAt(1), 13676 i.charCodeAt(2), 13677 i.charCodeAt(3) 13678 ]; 13679 } 13680 } 13681 var videoHdlr = new Uint8Array([ 13682 0x00, 13683 0x00, 0x00, 0x00, 13684 0x00, 0x00, 0x00, 0x00, 13685 0x76, 0x69, 0x64, 0x65, 13686 0x00, 0x00, 0x00, 0x00, 13687 0x00, 0x00, 0x00, 0x00, 13688 0x00, 0x00, 0x00, 0x00, 13689 0x56, 0x69, 0x64, 0x65, 13690 0x6f, 0x48, 0x61, 0x6e, 13691 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'VideoHandler' 13692 ]); 13693 var audioHdlr = new Uint8Array([ 13694 0x00, 13695 0x00, 0x00, 0x00, 13696 0x00, 0x00, 0x00, 0x00, 13697 0x73, 0x6f, 0x75, 0x6e, 13698 0x00, 0x00, 0x00, 0x00, 13699 0x00, 0x00, 0x00, 0x00, 13700 0x00, 0x00, 0x00, 0x00, 13701 0x53, 0x6f, 0x75, 0x6e, 13702 0x64, 0x48, 0x61, 0x6e, 13703 0x64, 0x6c, 0x65, 0x72, 0x00 // name: 'SoundHandler' 13704 ]); 13705 MP4.HDLR_TYPES = { 13706 'video': videoHdlr, 13707 'audio': audioHdlr 13708 }; 13709 var dref = new Uint8Array([ 13710 0x00, 13711 0x00, 0x00, 0x00, 13712 0x00, 0x00, 0x00, 0x01, 13713 0x00, 0x00, 0x00, 0x0c, 13714 0x75, 0x72, 0x6c, 0x20, 13715 0x00, 13716 0x00, 0x00, 0x01 // entry_flags 13717 ]); 13718 var stco = new Uint8Array([ 13719 0x00, 13720 0x00, 0x00, 0x00, 13721 0x00, 0x00, 0x00, 0x00 // entry_count 13722 ]); 13723 MP4.STTS = MP4.STSC = MP4.STCO = stco; 13724 MP4.STSZ = new Uint8Array([ 13725 0x00, 13726 0x00, 0x00, 0x00, 13727 0x00, 0x00, 0x00, 0x00, 13728 0x00, 0x00, 0x00, 0x00 // sample_count 13729 ]); 13730 MP4.VMHD = new Uint8Array([ 13731 0x00, 13732 0x00, 0x00, 0x01, 13733 0x00, 0x00, 13734 0x00, 0x00, 13735 0x00, 0x00, 13736 0x00, 0x00 // opcolor 13737 ]); 13738 MP4.SMHD = new Uint8Array([ 13739 0x00, 13740 0x00, 0x00, 0x00, 13741 0x00, 0x00, 13742 0x00, 0x00 // reserved 13743 ]); 13744 MP4.STSD = new Uint8Array([ 13745 0x00, 13746 0x00, 0x00, 0x00, 13747 0x00, 0x00, 0x00, 0x01 13748 ]); // entry_count 13749 var majorBrand = new Uint8Array([105, 115, 111, 109]); // isom 13750 var avc1Brand = new Uint8Array([97, 118, 99, 49]); // avc1 13751 var minorVersion = new Uint8Array([0, 0, 0, 1]); 13752 MP4.FTYP = MP4.box(MP4.types.ftyp, majorBrand, minorVersion, majorBrand, avc1Brand); 13753 MP4.DINF = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, dref)); 13754 }; 13755 MP4.box = function (type) { 13756 var payload = Array.prototype.slice.call(arguments, 1), size = 8, i = payload.length, len = i, result; 13757 // calculate the total size we need to allocate 13758 while (i--) { 13759 size += payload[i].byteLength; 13760 } 13761 result = new Uint8Array(size); 13762 result[0] = (size >> 24) & 0xff;
13763 result[1] = (size >> 16) & 0xff; 13764 result[2] = (size >> 8) & 0xff; 13765 result[3] = size & 0xff; 13766 result.set(type, 4); 13767 // copy the payload into the result 13768 for (i = 0, size = 8; i < len; i++) { 13769 // copy payload[i] array @ offset size 13770 result.set(payload[i], size); 13771 size += payload[i].byteLength; 13772 } 13773 return result; 13774 }; 13775 MP4.hdlr = function (type) { 13776 return MP4.box(MP4.types.hdlr, MP4.HDLR_TYPES[type]); 13777 }; 13778 MP4.mdat = function (data) { 13779 return MP4.box(MP4.types.mdat, data); 13780 }; 13781 MP4.mdhd = function (timescale, duration) { 13782 duration *= timescale; 13783 var upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)); 13784 var lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); 13785 return MP4.box(MP4.types.mdhd, new Uint8Array([ 13786 0x01, 13787 0x00, 0x00, 0x00, 13788 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 13789 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 13790 (timescale >> 24) & 0xFF, 13791 (timescale >> 16) & 0xFF, 13792 (timescale >> 8) & 0xFF, 13793 timescale & 0xFF, 13794 (upperWordDuration >> 24), 13795 (upperWordDuration >> 16) & 0xFF, 13796 (upperWordDuration >> 8) & 0xFF, 13797 upperWordDuration & 0xFF, 13798 (lowerWordDuration >> 24), 13799 (lowerWordDuration >> 16) & 0xFF, 13800 (lowerWordDuration >> 8) & 0xFF, 13801 lowerWordDuration & 0xFF, 13802 0x55, 0xc4, 13803 0x00, 0x00 13804 ])); 13805 }; 13806 MP4.mdia = function (track) { 13807 return MP4.box(MP4.types.mdia, MP4.mdhd(track.timescale, track.duration), MP4.hdlr(track.type), MP4.minf(track)); 13808 }; 13809 MP4.mfhd = function (sequenceNumber) { 13810 return MP4.box(MP4.types.mfhd, new Uint8Array([ 13811 0x00, 13812 0x00, 0x00, 0x00, 13813 (sequenceNumber >> 24), 13814 (sequenceNumber >> 16) & 0xFF, 13815 (sequenceNumber >> 8) & 0xFF, 13816 sequenceNumber & 0xFF // sequence_number 13817 ])); 13818 }; 13819 MP4.minf = function (track) { 13820 if (track.type === 'audio') { 13821 return MP4.box(MP4.types.minf, MP4.box(MP4.types.smhd, MP4.SMHD), MP4.DINF, MP4.stbl(track)); 13822 } 13823 else { 13824 return MP4.box(MP4.types.minf, MP4.box(MP4.types.vmhd, MP4.VMHD), MP4.DINF, MP4.stbl(track)); 13825 } 13826 }; 13827 MP4.moof = function (sn, baseMediaDecodeTime, track) { 13828 return MP4.box(MP4.types.moof, MP4.mfhd(sn), MP4.traf(track, baseMediaDecodeTime)); 13829 }; 13830 /** 13831 * @param tracks... (optional) {array} the tracks associated with this movie 13832 */ 13833 MP4.moov = function (tracks) { 13834 var i = tracks.length, boxes = []; 13835 while (i--) { 13836 boxes[i] = MP4.trak(tracks[i]); 13837 } 13838 return MP4.box.apply(null, [MP4.types.moov, MP4.mvhd(tracks[0].timescale, tracks[0].duration)].concat(boxes).concat(MP4.mvex(tracks))); 13839 }; 13840 MP4.mvex = function (tracks) { 13841 var i = tracks.length, boxes = []; 13842 while (i--) { 13843 boxes[i] = MP4.trex(tracks[i]); 13844 } 13845 return MP4.box.apply(null, [MP4.types.mvex].concat(boxes)); 13846 }; 13847 MP4.mvhd = function (timescale, duration) { 13848 duration *= timescale; 13849 var upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)); 13850 var lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); 13851 var bytes = new Uint8Array([ 13852 0x01, 13853 0x00, 0x00, 0x00, 13854 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 13855 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 13856 (timescale >> 24) & 0xFF, 13857 (timescale >> 16) & 0xFF, 13858 (timescale >> 8) & 0xFF, 13859 timescale & 0xFF, 13860 (upperWordDuration >> 24), 13861 (upperWordDuration >> 16) & 0xFF, 13862 (upperWordDuration >> 8) & 0xFF, 13863 upperWordDuration & 0xFF, 13864 (lowerWordDuration >> 24), 13865 (lowerWordDuration >> 16) & 0xFF, 13866 (lowerWordDuration >> 8) & 0xFF, 13867 lowerWordDuration & 0xFF, 13868 0x00, 0x01, 0x00, 0x00, 13869 0x01, 0x00, 13870 0x00, 0x00, 13871 0x00, 0x00, 0x00, 0x00, 13872 0x00, 0x00, 0x00, 0x00, 13873 0x00, 0x01, 0x00, 0x00, 13874 0x00, 0x00, 0x00, 0x00, 13875 0x00, 0x00, 0x00, 0x00, 13876 0x00, 0x00, 0x00, 0x00, 13877 0x00, 0x01, 0x00, 0x00, 13878 0x00, 0x00, 0x00, 0x00, 13879 0x00, 0x00, 0x00, 0x00, 13880 0x00, 0x00, 0x00, 0x00, 13881 0x40, 0x00, 0x00, 0x00, 13882 0x00, 0x00, 0x00, 0x00, 13883 0x00, 0x00, 0x00, 0x00, 13884 0x00, 0x00, 0x00, 0x00, 13885 0x00, 0x00, 0x00, 0x00, 13886 0x00, 0x00, 0x00, 0x00, 13887 0x00, 0x00, 0x00, 0x00, 13888 0xff, 0xff, 0xff, 0xff // next_track_ID 13889 ]); 13890 return MP4.box(MP4.types.mvhd, bytes); 13891 }; 13892 MP4.sdtp = function (track) { 13893 var samples = track.samples || [], bytes = new Uint8Array(4 + samples.length), flags, i; 13894 // leave the full box header (4 bytes) all zero 13895 // write the sample table 13896 for (i = 0; i < samples.length; i++) { 13897 flags = samples[i].flags; 13898 bytes[i + 4] = (flags.dependsOn << 4) | 13899 (flags.isDependedOn << 2) | 13900 (flags.hasRedundancy); 13901 } 13902 return MP4.box(MP4.types.sdtp, bytes); 13903 }; 13904 MP4.stbl = function (track) { 13905 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)); 13906 }; 13907 MP4.avc1 = function (track) { 13908 var sps = [], pps = [], i, data, len; 13909 // assemble the SPSs 13910 for (i = 0; i < track.sps.length; i++) { 13911 data = track.sps[i]; 13912 len = data.byteLength; 13913 sps.push((len >>> 8) & 0xFF); 13914 sps.push((len & 0xFF)); 13915 // SPS 13916 sps = sps.concat(Array.prototype.slice.call(data)); 13917 } 13918 // assemble the PPSs 13919 for (i = 0; i < track.pps.length; i++) { 13920 data = track.pps[i]; 13921 len = data.byteLength; 13922 pps.push((len >>> 8) & 0xFF); 13923 pps.push((len & 0xFF)); 13924 pps = pps.concat(Array.prototype.slice.call(data)); 13925 } 13926 var avcc = MP4.box(MP4.types.avcC, new Uint8Array([ 13927 0x01, 13928 sps[3], 13929 sps[4], 13930 sps[5], 13931 0xfc | 3, 13932 0xE0 | track.sps.length // 3bit reserved (111) + numOfSequenceParameterSets
13933 ].concat(sps).concat([ 13934 track.pps.length // numOfPictureParameterSets 13935 ]).concat(pps))), // "PPS" 13936 width = track.width, height = track.height, hSpacing = track.pixelRatio[0], vSpacing = track.pixelRatio[1]; 13937 return MP4.box(MP4.types.avc1, new Uint8Array([ 13938 0x00, 0x00, 0x00, 13939 0x00, 0x00, 0x00, 13940 0x00, 0x01, 13941 0x00, 0x00, 13942 0x00, 0x00, 13943 0x00, 0x00, 0x00, 0x00, 13944 0x00, 0x00, 0x00, 0x00, 13945 0x00, 0x00, 0x00, 0x00, 13946 (width >> 8) & 0xFF, 13947 width & 0xff, 13948 (height >> 8) & 0xFF, 13949 height & 0xff, 13950 0x00, 0x48, 0x00, 0x00, 13951 0x00, 0x48, 0x00, 0x00, 13952 0x00, 0x00, 0x00, 0x00, 13953 0x00, 0x01, 13954 0x12, 13955 0x64, 0x61, 0x69, 0x6C, 13956 0x79, 0x6D, 0x6F, 0x74, 13957 0x69, 0x6F, 0x6E, 0x2F, 13958 0x68, 0x6C, 0x73, 0x2E, 13959 0x6A, 0x73, 0x00, 0x00, 13960 0x00, 0x00, 0x00, 0x00, 13961 0x00, 0x00, 0x00, 0x00, 13962 0x00, 0x00, 0x00, 13963 0x00, 0x18, 13964 0x11, 0x11 13965 ]), // pre_defined = -1 13966 avcc, MP4.box(MP4.types.btrt, new Uint8Array([ 13967 0x00, 0x1c, 0x9c, 0x80, 13968 0x00, 0x2d, 0xc6, 0xc0, 13969 0x00, 0x2d, 0xc6, 0xc0 13970 ])), // avgBitrate 13971 MP4.box(MP4.types.pasp, new Uint8Array([ 13972 (hSpacing >> 24), 13973 (hSpacing >> 16) & 0xFF, 13974 (hSpacing >> 8) & 0xFF, 13975 hSpacing & 0xFF, 13976 (vSpacing >> 24), 13977 (vSpacing >> 16) & 0xFF, 13978 (vSpacing >> 8) & 0xFF, 13979 vSpacing & 0xFF 13980 ]))); 13981 }; 13982 MP4.esds = function (track) { 13983 var configlen = track.config.length; 13984 return new Uint8Array([ 13985 0x00, 13986 0x00, 0x00, 0x00, 13987 0x03, 13988 0x17 + configlen, 13989 0x00, 0x01, 13990 0x00, 13991 0x04, 13992 0x0f + configlen, 13993 0x40, 13994 0x15, 13995 0x00, 0x00, 0x00, 13996 0x00, 0x00, 0x00, 0x00, 13997 0x00, 0x00, 0x00, 0x00, 13998 0x05 // descriptor_type 13999 ].concat([configlen]).concat(track.config).concat([0x06, 0x01, 0x02])); // GASpecificConfig)); // length + audio config descriptor 14000 }; 14001 MP4.mp4a = function (track) { 14002 var samplerate = track.samplerate; 14003 return MP4.box(MP4.types.mp4a, new Uint8Array([ 14004 0x00, 0x00, 0x00, 14005 0x00, 0x00, 0x00, 14006 0x00, 0x01, 14007 0x00, 0x00, 0x00, 0x00, 14008 0x00, 0x00, 0x00, 0x00, 14009 0x00, track.channelCount, 14010 0x00, 0x10, 14011 0x00, 0x00, 0x00, 0x00, 14012 (samplerate >> 8) & 0xFF, 14013 samplerate & 0xff, 14014 0x00, 0x00 14015 ]), MP4.box(MP4.types.esds, MP4.esds(track))); 14016 }; 14017 MP4.mp3 = function (track) { 14018 var samplerate = track.samplerate; 14019 return MP4.box(MP4.types['.mp3'], new Uint8Array([ 14020 0x00, 0x00, 0x00, 14021 0x00, 0x00, 0x00, 14022 0x00, 0x01, 14023 0x00, 0x00, 0x00, 0x00, 14024 0x00, 0x00, 0x00, 0x00, 14025 0x00, track.channelCount, 14026 0x00, 0x10, 14027 0x00, 0x00, 0x00, 0x00, 14028 (samplerate >> 8) & 0xFF, 14029 samplerate & 0xff, 14030 0x00, 0x00 14031 ])); 14032 }; 14033 MP4.stsd = function (track) { 14034 if (track.type === 'audio') { 14035 if (!track.isAAC && track.codec === 'mp3') { 14036 return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp3(track)); 14037 } 14038 return MP4.box(MP4.types.stsd, MP4.STSD, MP4.mp4a(track)); 14039 } 14040 else { 14041 return MP4.box(MP4.types.stsd, MP4.STSD, MP4.avc1(track)); 14042 } 14043 }; 14044 MP4.tkhd = function (track) { 14045 var id = track.id, duration = track.duration * track.timescale, width = track.width, height = track.height, upperWordDuration = Math.floor(duration / (UINT32_MAX + 1)), lowerWordDuration = Math.floor(duration % (UINT32_MAX + 1)); 14046 return MP4.box(MP4.types.tkhd, new Uint8Array([ 14047 0x01, 14048 0x00, 0x00, 0x07, 14049 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 14050 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 14051 (id >> 24) & 0xFF, 14052 (id >> 16) & 0xFF, 14053 (id >> 8) & 0xFF, 14054 id & 0xFF, 14055 0x00, 0x00, 0x00, 0x00, 14056 (upperWordDuration >> 24), 14057 (upperWordDuration >> 16) & 0xFF, 14058 (upperWordDuration >> 8) & 0xFF, 14059 upperWordDuration & 0xFF, 14060 (lowerWordDuration >> 24), 14061 (lowerWordDuration >> 16) & 0xFF, 14062 (lowerWordDuration >> 8) & 0xFF, 14063 lowerWordDuration & 0xFF, 14064 0x00, 0x00, 0x00, 0x00, 14065 0x00, 0x00, 0x00, 0x00, 14066 0x00, 0x00, 14067 0x00, 0x00, 14068 0x00, 0x00, 14069 0x00, 0x00, 14070 0x00, 0x01, 0x00, 0x00, 14071 0x00, 0x00, 0x00, 0x00, 14072 0x00, 0x00, 0x00, 0x00, 14073 0x00, 0x00, 0x00, 0x00, 14074 0x00, 0x01, 0x00, 0x00, 14075 0x00, 0x00, 0x00, 0x00, 14076 0x00, 0x00, 0x00, 0x00, 14077 0x00, 0x00, 0x00, 0x00, 14078 0x40, 0x00, 0x00, 0x00, 14079 (width >> 8) & 0xFF, 14080 width & 0xFF, 14081 0x00, 0x00, 14082 (height >> 8) & 0xFF, 14083 height & 0xFF, 14084 0x00, 0x00 // height 14085 ])); 14086 }; 14087 MP4.traf = function (track, baseMediaDecodeTime) { 14088 var sampleDependencyTable = MP4.sdtp(track), id = track.id, upperWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime / (UINT32_MAX + 1)), lowerWordBaseMediaDecodeTime = Math.floor(baseMediaDecodeTime % (UINT32_MAX + 1)); 14089 return MP4.box(MP4.types.traf, MP4.box(MP4.types.tfhd, new Uint8Array([ 14090 0x00, 14091 0x00, 0x00, 0x00, 14092 (id >> 24), 14093 (id >> 16) & 0XFF, 14094 (id >> 8) & 0XFF, 14095 (id & 0xFF) // track_ID 14096 ])), MP4.box(MP4.types.tfdt, new Uint8Array([ 14097 0x01, 14098 0x00, 0x00, 0x00, 14099 (upperWordBaseMediaDecodeTime >> 24), 14100 (upperWordBaseMediaDecodeTime >> 16) & 0XFF, 14101 (upperWordBaseMediaDecodeTime >> 8) & 0XFF, 14102 (upperWordBaseMediaDecodeTime & 0xFF), 14103 (lowerWordBaseMediaDecodeTime >> 24), 14104 (lowerWordBaseMediaDecodeTime >> 16) & 0XFF, 14105 (lowerWordBaseMediaDecodeTime >> 8) & 0XFF, 14106 (lowerWordBaseMediaDecodeTime & 0xFF) 14107 ])), MP4.trun(track, sampleDependencyTable.length + 14108 16 + // tfhd 14109 20 + // tfdt 14110 8 + // traf header 14111 16 + // mfhd 14112 8 + // moof header 14113 8), // mdat header 14114 sampleDependencyTable); 14115 }; 14116 /** 14117 * Generate a track box. 14118 * @param track {object} a track definition 14119 * @return {Uint8Array} the track box 14120 */ 14121 MP4.trak = function (track) { 14122 track.duration = track.duration || 0xffffffff; 14123 return MP4.box(MP4.types.trak, MP4.tkhd(track), MP4.mdia(track)); 14124 }; 14125 MP4.trex = function (track) { 14126 var id = track.id; 14127 return MP4.box(MP4.types.trex, new Uint8Array([ 14128 0x00, 14129 0x00, 0x00, 0x00, 14130 (id >> 24), 14131 (id >> 16) & 0XFF, 14132 (id >> 8) & 0XFF, 14133 (id & 0xFF), 14134 0x00, 0x00, 0x00, 0x01, 14135 0x00, 0x00, 0x00, 0x00, 14136 0x00, 0x00, 0x00, 0x00, 14137 0x00, 0x01, 0x00, 0x01 // default_sample_flags 14138 ])); 14139 }; 14140 MP4.trun = function (track, offset) {
14141 var samples = track.samples || [], len = samples.length, arraylen = 12 + (16 * len), array = new Uint8Array(arraylen), i, sample, duration, size, flags, cts; 14142 offset += 8 + arraylen; 14143 array.set([ 14144 0x00, 14145 0x00, 0x0f, 0x01, 14146 (len >>> 24) & 0xFF, 14147 (len >>> 16) & 0xFF, 14148 (len >>> 8) & 0xFF, 14149 len & 0xFF, 14150 (offset >>> 24) & 0xFF, 14151 (offset >>> 16) & 0xFF, 14152 (offset >>> 8) & 0xFF, 14153 offset & 0xFF // data_offset 14154 ], 0); 14155 for (i = 0; i < len; i++) { 14156 sample = samples[i]; 14157 duration = sample.duration; 14158 size = sample.size; 14159 flags = sample.flags; 14160 cts = sample.cts; 14161 array.set([ 14162 (duration >>> 24) & 0xFF, 14163 (duration >>> 16) & 0xFF, 14164 (duration >>> 8) & 0xFF, 14165 duration & 0xFF, 14166 (size >>> 24) & 0xFF, 14167 (size >>> 16) & 0xFF, 14168 (size >>> 8) & 0xFF, 14169 size & 0xFF, 14170 (flags.isLeading << 2) | flags.dependsOn, 14171 (flags.isDependedOn << 6) | 14172 (flags.hasRedundancy << 4) | 14173 (flags.paddingValue << 1) | 14174 flags.isNonSync, 14175 flags.degradPrio & 0xF0 << 8, 14176 flags.degradPrio & 0x0F, 14177 (cts >>> 24) & 0xFF, 14178 (cts >>> 16) & 0xFF, 14179 (cts >>> 8) & 0xFF, 14180 cts & 0xFF // sample_composition_time_offset 14181 ], 12 + 16 * i); 14182 } 14183 return MP4.box(MP4.types.trun, array); 14184 }; 14185 MP4.initSegment = function (tracks) { 14186 if (!MP4.types) { 14187 MP4.init(); 14188 } 14189 var movie = MP4.moov(tracks), result; 14190 result = new Uint8Array(MP4.FTYP.byteLength + movie.byteLength); 14191 result.set(MP4.FTYP); 14192 result.set(movie, MP4.FTYP.byteLength); 14193 return result; 14194 }; 14195 return MP4; 14196}()); 14197exports.default = MP4; 14198 14199 14200/***/ }), 14201
vendor: 8,842 bytes, lines 14202-14370
14202/***/ "./src/remux/mp4-remuxer.js": 14203/*!**********************************!*\ 14204 !*** ./src/remux/mp4-remuxer.js ***! 14205 \**********************************/ 14206/*! no static exports found */ 14207/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 14208/***/ (function(module, exports, __webpack_require__) { 14209 14210"use strict"; 14211 14212/** 14213 * fMP4 remuxer 14214*/ 14215Object.defineProperty(exports, "__esModule", { value: true }); 14216var aac_helper_1 = __webpack_require__(/*! ./aac-helper */ "./src/remux/aac-helper.js"); 14217var mp4_generator_1 = __webpack_require__(/*! ./mp4-generator */ "./src/remux/mp4-generator.js"); 14218var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 14219var errors_1 = __webpack_require__(/*! ../errors */ "./src/errors.js"); 14220var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 14221// 10 seconds 14222var MAX_SILENT_FRAME_DURATION = 10 * 1000; 14223var MP4Remuxer = /** @class */ (function () { 14224 function MP4Remuxer(observer, config, typeSupported, vendor) { 14225 this.observer = observer; 14226 this.config = config; 14227 this.typeSupported = typeSupported; 14228 var userAgent = navigator.userAgent; 14229 this.isSafari = vendor && vendor.indexOf('Apple') > -1 && userAgent && !userAgent.match('CriOS'); 14230 this.ISGenerated = false; 14231 } 14232 MP4Remuxer.prototype.destroy = function () { 14233 }; 14234 MP4Remuxer.prototype.resetTimeStamp = function (defaultTimeStamp) { 14235 this._initPTS = this._initDTS = defaultTimeStamp; 14236 }; 14237 MP4Remuxer.prototype.resetInitSegment = function () { 14238 this.ISGenerated = false; 14239 }; 14240 MP4Remuxer.prototype.remux = function (audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset) { 14241 // generate Init Segment if needed 14242 if (!this.ISGenerated) { 14243 this.generateIS(audioTrack, videoTrack, timeOffset); 14244 } 14245 if (this.ISGenerated) { 14246 var nbAudioSamples = audioTrack.samples.length; 14247 var nbVideoSamples = videoTrack.samples.length; 14248 var audioTimeOffset = timeOffset; 14249 var videoTimeOffset = timeOffset; 14250 if (nbAudioSamples && nbVideoSamples) { 14251 // timeOffset is expected to be the offset of the first timestamp of this fragment (first DTS) 14252 // if first audio DTS is not aligned with first video DTS then we need to take that into account 14253 // when providing timeOffset to remuxAudio / remuxVideo. if we don't do that, there might be a permanent / small 14254 // drift between audio and video streams 14255 var audiovideoDeltaDts = (audioTrack.samples[0].pts - videoTrack.samples[0].pts) / videoTrack.inputTimeScale; 14256 audioTimeOffset += Math.max(0, audiovideoDeltaDts); 14257 videoTimeOffset += Math.max(0, -audiovideoDeltaDts); 14258 } 14259 // Purposefully remuxing audio before video, so that remuxVideo can use nextAudioPts, which is 14260 // calculated in remuxAudio. 14261 // logger.log('nb AAC samples:' + audioTrack.samples.length); 14262 if (nbAudioSamples) { 14263 // if initSegment was generated without video samples, regenerate it again 14264 if (!audioTrack.timescale) { 14265 logger_1.logger.warn('regenerate InitSegment as audio detected'); 14266 this.generateIS(audioTrack, videoTrack, timeOffset); 14267 } 14268 var audioData = this.remuxAudio(audioTrack, audioTimeOffset, contiguous, accurateTimeOffset); 14269 // logger.log('nb AVC samples:' + videoTrack.samples.length); 14270 if (nbVideoSamples) { 14271 var audioTrackLength = void 0; 14272 if (audioData) { 14273 audioTrackLength = audioData.endPTS - audioData.startPTS; 14274 } 14275 // if initSegment was generated without video samples, regenerate it again 14276 if (!videoTrack.timescale) { 14277 logger_1.logger.warn('regenerate InitSegment as video detected'); 14278 this.generateIS(audioTrack, videoTrack, timeOffset); 14279 } 14280 this.remuxVideo(videoTrack, videoTimeOffset, contiguous, audioTrackLength, accurateTimeOffset); 14281 } 14282 } 14283 else { 14284 // logger.log('nb AVC samples:' + videoTrack.samples.length); 14285 if (nbVideoSamples) { 14286 var videoData = this.remuxVideo(videoTrack, videoTimeOffset, contiguous, 0, accurateTimeOffset); 14287 if (videoData && audioTrack.codec) { 14288 this.remuxEmptyAudio(audioTrack, audioTimeOffset, contiguous, videoData); 14289 } 14290 } 14291 } 14292 } 14293 // logger.log('nb ID3 samples:' + audioTrack.samples.length); 14294 if (id3Track.samples.length) { 14295 this.remuxID3(id3Track, timeOffset); 14296 } 14297 // logger.log('nb ID3 samples:' + audioTrack.samples.length); 14298 if (textTrack.samples.length) { 14299 this.remuxText(textTrack, timeOffset); 14300 } 14301 // notify end of parsing 14302 this.observer.trigger(events_1.default.FRAG_PARSED); 14303 }; 14304 MP4Remuxer.prototype.generateIS = function (audioTrack, videoTrack, timeOffset) { 14305 var observer = this.observer, audioSamples = audioTrack.samples, videoSamples = videoTrack.samples, typeSupported = this.typeSupported, container = 'audio/mp4', tracks = {}, data = { tracks: tracks }, computePTSDTS = (this._initPTS === undefined), initPTS, initDTS; 14306 if (computePTSDTS) { 14307 initPTS = initDTS = Infinity; 14308 } 14309 if (audioTrack.config && audioSamples.length) { 14310 // let's use audio sampling rate as MP4 time scale. 14311 // rationale is that there is a integer nb of audio frames per audio sample (1024 for AAC) 14312 // using audio sampling rate here helps having an integer MP4 frame duration 14313 // this avoids potential rounding issue and AV sync issue 14314 audioTrack.timescale = audioTrack.samplerate; 14315 logger_1.logger.log("audio sampling rate : " + audioTrack.samplerate); 14316 if (!audioTrack.isAAC) { 14317 if (typeSupported.mpeg) { // Chrome and Safari 14318 container = 'audio/mpeg'; 14319 audioTrack.codec = ''; 14320 } 14321 else if (typeSupported.mp3) { // Firefox 14322 audioTrack.codec = 'mp3'; 14323 } 14324 } 14325 tracks.audio = { 14326 container: container, 14327 codec: audioTrack.codec, 14328 initSegment: !audioTrack.isAAC && typeSupported.mpeg ? new Uint8Array() : mp4_generator_1.default.initSegment([audioTrack]), 14329 metadata: { 14330 channelCount: audioTrack.channelCount 14331 } 14332 }; 14333 if (computePTSDTS) { 14334 // remember first PTS of this demuxing context. for audio, PTS = DTS 14335 initPTS = initDTS = audioSamples[0].pts - audioTrack.inputTimeScale * timeOffset; 14336 } 14337 } 14338 if (videoTrack.sps && videoTrack.pps && videoSamples.length) { 14339 // let's use input time scale as MP4 video timescale 14340 // we use input time scale straight away to avoid rounding issues on frame duration / cts computation 14341 var inputTimeScale = videoTrack.inputTimeScale; 14342 videoTrack.timescale = inputTimeScale; 14343 tracks.video = { 14344 container: 'video/mp4', 14345 codec: videoTrack.codec, 14346 initSegment: mp4_generator_1.default.initSegment([videoTrack]), 14347 metadata: { 14348 width: videoTrack.width, 14349 height: videoTrack.height 14350 } 14351 }; 14352 if (computePTSDTS) { 14353 initPTS = Math.min(initPTS, videoSamples[0].pts - inputTimeScale * timeOffset); 14354 initDTS = Math.min(initDTS, videoSamples[0].dts - inputTimeScale * timeOffset); 14355 this.observer.trigger(events_1.default.INIT_PTS_FOUND, { initPTS: initPTS }); 14356 } 14357 } 14358 if (Object.keys(tracks).length) { 14359 observer.trigger(events_1.default.FRAG_PARSING_INIT_SEGMENT, data); 14360 this.ISGenerated = true; 14361 if (computePTSDTS) { 14362 this._initPTS = initPTS; 14363 this._initDTS = initDTS; 14364 } 14365 } 14366 else { 14367 observer.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.MEDIA_ERROR, details: errors_1.ErrorDetails.FRAG_PARSING_ERROR, fatal: false, reason: 'no audio/video samples found' }); 14368 } 14369 }; 14370 MP4Remuxer.prototype.remuxVideo = function (track, timeOffset, contiguous, audioTrackLength, accurateTimeOff
14370set) { 14371 var offset = 8; 14372 var mp4SampleDuration; 14373 var mdat; 14374 var moof; 14375 var firstPTS; 14376 var firstDTS; 14377 var lastPTS; 14378 var lastDTS; 14379 var timeScale = track.timescale; 14380 var inputSamples = track.samples; 14381 var outputSamples = []; 14382 var nbSamples = inputSamples.length; 14383 var ptsNormalize = this._PTSNormalize; 14384 var initPTS = this._initPTS; 14385 // if parsed fragment is contiguous with last one, let's use last DTS value as reference 14386 var nextAvcDts = this.nextAvcDts; 14387 var isSafari = this.isSafari; 14388 if (nbSamples === 0) { 14389 return; 14390 } 14391 // Safari does not like overlapping DTS on consecutive fragments. let's use nextAvcDts to overcome this if fragments are consecutive 14392 if (isSafari) { 14393 // also consider consecutive fragments as being contiguous (even if a level switch occurs), 14394 // for sake of clarity: 14395 // consecutive fragments are frags with 14396 // - less than 100ms gaps between new time offset (if accurate) and next expected PTS OR 14397 // - less than 200 ms PTS gaps (timeScale/5) 14398 contiguous |= (inputSamples.length && nextAvcDts && 14399 ((accurateTimeOffset && Math.abs(timeOffset - nextAvcDts / timeScale) < 0.1) || 14400 Math.abs((inputSamples[0].pts - nextAvcDts - initPTS)) < timeScale / 5)); 14401 } 14402 if (!contiguous) { 14403 // if not contiguous, let's use target timeOffset 14404 nextAvcDts = timeOffset * timeScale; 14405 } 14406 // PTS is coded on 33bits, and can loop from -2^32 to 2^32 14407 // ptsNormalize will make PTS/DTS value monotonic, we use last known DTS value as reference value 14408 inputSamples.forEach(function (sample) { 14409 sample.pts = ptsNormalize(sample.pts - initPTS, nextAvcDts); 14410 sample.dts = ptsNormalize(sample.dts - initPTS, nextAvcDts); 14411 }); 14412 // sort video samples by DTS then PTS then demux id order 14413 inputSamples.sort(function (a, b) { 14414 var deltadts = a.dts - b.dts; 14415 var deltapts = a.pts - b.pts; 14416 return deltadts || (deltapts || (a.id - b.id)); 14417 }); 14418 // handle broken streams with PTS < DTS, tolerance up 200ms (18000 in 90kHz timescale) 14419 var PTSDTSshift = inputSamples.reduce(function (prev, curr) { return Math.max(Math.min(prev, curr.pts - curr.dts), -18000); }, 0); 14420 if (PTSDTSshift < 0) { 14421 logger_1.logger.warn("PTS < DTS detected in video samples, shifting DTS by " + Math.round(PTSDTSshift / 90) + " ms to overcome this issue"); 14422 for (var i = 0; i < inputSamples.length; i++) { 14423 inputSamples[i].dts += PTSDTSshift; 14424 } 14425 } 14426 // compute first DTS and last DTS, normalize them against reference value 14427 var sample = inputSamples[0]; 14428 firstDTS = Math.max(sample.dts, 0); 14429 firstPTS = Math.max(sample.pts, 0); 14430 // check timestamp continuity accross consecutive fragments (this is to remove inter-fragment gap/hole) 14431 var delta = Math.round((firstDTS - nextAvcDts) / 90); 14432 // if fragment are contiguous, detect hole/overlapping between fragments 14433 if (contiguous) { 14434 if (delta) { 14435 if (delta > 1) { 14436 logger_1.logger.log("AVC:" + delta + " ms hole between fragments detected,filling it"); 14437 } 14438 else if (delta < -1) { 14439 logger_1.logger.log("AVC:" + (-delta) + " ms overlapping between fragments detected"); 14440 } 14441 // remove hole/gap : set DTS to next expected DTS 14442 firstDTS = nextAvcDts; 14443 inputSamples[0].dts = firstDTS; 14444 // offset PTS as well, ensure that PTS is smaller or equal than new DTS 14445 firstPTS = Math.max(firstPTS - delta, nextAvcDts); 14446 inputSamples[0].pts = firstPTS; 14447 logger_1.logger.log("Video/PTS/DTS adjusted: " + Math.round(firstPTS / 90) + "/" + Math.round(firstDTS / 90) + ",delta:" + delta + " ms"); 14448 } 14449 } 14450 // compute lastPTS/lastDTS 14451 sample = inputSamples[inputSamples.length - 1]; 14452 lastDTS = Math.max(sample.dts, 0); 14453 lastPTS = Math.max(sample.pts, 0, lastDTS); 14454 // on Safari let's signal the same sample duration for all samples 14455 // sample duration (as expected by trun MP4 boxes), should be the delta between sample DTS 14456 // set this constant duration as being the avg delta between consecutive DTS. 14457 if (isSafari) { 14458 mp4SampleDuration = Math.round((lastDTS - firstDTS) / (inputSamples.length - 1)); 14459 } 14460 var nbNalu = 0, naluLen = 0; 14461 for (var i = 0; i < nbSamples; i++) { 14462 // compute total/avc sample length and nb of NAL units
vendor: 6,912 bytes, lines 14463-14590
14463 var sample_1 = inputSamples[i], units = sample_1.units, nbUnits = units.length, sampleLen = 0; 14464 for (var j = 0; j < nbUnits; j++) { 14465 sampleLen += units[j].data.length; 14466 } 14467 naluLen += sampleLen; 14468 nbNalu += nbUnits; 14469 sample_1.length = sampleLen; 14470 // normalize PTS/DTS 14471 if (isSafari) { 14472 // sample DTS is computed using a constant decoding offset (mp4SampleDuration) between samples 14473 sample_1.dts = firstDTS + i * mp4SampleDuration; 14474 } 14475 else { 14476 // ensure sample monotonic DTS 14477 sample_1.dts = Math.max(sample_1.dts, firstDTS); 14478 } 14479 // ensure that computed value is greater or equal than sample DTS 14480 sample_1.pts = Math.max(sample_1.pts, sample_1.dts); 14481 } 14482 /* concatenate the video data and construct the mdat in place 14483 (need 8 more bytes to fill length and mpdat type) */ 14484 var mdatSize = naluLen + (4 * nbNalu) + 8; 14485 try { 14486 mdat = new Uint8Array(mdatSize); 14487 } 14488 catch (err) { 14489 this.observer.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.MUX_ERROR, details: errors_1.ErrorDetails.REMUX_ALLOC_ERROR, fatal: false, bytes: mdatSize, reason: "fail allocating video mdat " + mdatSize }); 14490 return; 14491 } 14492 var view = new DataView(mdat.buffer); 14493 view.setUint32(0, mdatSize); 14494 mdat.set(mp4_generator_1.default.types.mdat, 4); 14495 for (var i = 0; i < nbSamples; i++) { 14496 var avcSample = inputSamples[i], avcSampleUnits = avcSample.units, mp4SampleLength = 0, compositionTimeOffset = void 0; 14497 // convert NALU bitstream to MP4 format (prepend NALU with size field) 14498 for (var j = 0, nbUnits = avcSampleUnits.length; j < nbUnits; j++) { 14499 var unit = avcSampleUnits[j], unitData = unit.data, unitDataLen = unit.data.byteLength; 14500 view.setUint32(offset, unitDataLen); 14501 offset += 4; 14502 mdat.set(unitData, offset); 14503 offset += unitDataLen; 14504 mp4SampleLength += 4 + unitDataLen; 14505 } 14506 if (!isSafari) { 14507 // expected sample duration is the Decoding Timestamp diff of consecutive samples 14508 if (i < nbSamples - 1) { 14509 mp4SampleDuration = inputSamples[i + 1].dts - avcSample.dts; 14510 } 14511 else { 14512 var config = this.config, lastFrameDuration = avcSample.dts - inputSamples[i > 0 ? i - 1 : i].dts; 14513 if (config.stretchShortVideoTrack) { 14514 // In some cases, a segment's audio track duration may exceed the video track duration. 14515 // Since we've already remuxed audio, and we know how long the audio track is, we look to 14516 // see if the delta to the next segment is longer than maxBufferHole. 14517 // If so, playback would potentially get stuck, so we artificially inflate 14518 // the duration of the last frame to minimize any potential gap between segments. 14519 var maxBufferHole = config.maxBufferHole, gapTolerance = Math.floor(maxBufferHole * timeScale), deltaToFrameEnd = (audioTrackLength ? firstPTS + audioTrackLength * timeScale : this.nextAudioPts) - avcSample.pts; 14520 if (deltaToFrameEnd > gapTolerance) { 14521 // We subtract lastFrameDuration from deltaToFrameEnd to try to prevent any video 14522 // frame overlap. maxBufferHole should be >> lastFrameDuration anyway. 14523 mp4SampleDuration = deltaToFrameEnd - lastFrameDuration; 14524 if (mp4SampleDuration < 0) { 14525 mp4SampleDuration = lastFrameDuration; 14526 } 14527 logger_1.logger.log("It is approximately " + deltaToFrameEnd / 90 + " ms to the next segment; using duration " + mp4SampleDuration / 90 + " ms for the last video frame."); 14528 } 14529 else { 14530 mp4SampleDuration = lastFrameDuration; 14531 } 14532 } 14533 else { 14534 mp4SampleDuration = lastFrameDuration; 14535 } 14536 } 14537 compositionTimeOffset = Math.round(avcSample.pts - avcSample.dts); 14538 } 14539 else { 14540 compositionTimeOffset = Math.max(0, mp4SampleDuration * Math.round((avcSample.pts - avcSample.dts) / mp4SampleDuration)); 14541 } 14542 // console.log('PTS/DTS/initDTS/normPTS/normDTS/relative PTS : ${avcSample.pts}/${avcSample.dts}/${initDTS}/${ptsnorm}/${dtsnorm}/${(avcSample.pts/4294967296).toFixed(3)}'); 14543 outputSamples.push({ 14544 size: mp4SampleLength, 14545 // constant duration 14546 duration: mp4SampleDuration, 14547 cts: compositionTimeOffset, 14548 flags: { 14549 isLeading: 0, 14550 isDependedOn: 0, 14551 hasRedundancy: 0, 14552 degradPrio: 0, 14553 dependsOn: avcSample.key ? 2 : 1, 14554 isNonSync: avcSample.key ? 0 : 1 14555 } 14556 }); 14557 } 14558 // next AVC sample DTS should be equal to last sample DTS + last sample duration (in PES timescale) 14559 this.nextAvcDts = lastDTS + mp4SampleDuration; 14560 var dropped = track.dropped; 14561 track.len = 0; 14562 track.nbNalu = 0; 14563 track.dropped = 0; 14564 if (outputSamples.length && navigator.userAgent.toLowerCase().indexOf('chrome') > -1) { 14565 var flags = outputSamples[0].flags; 14566 // chrome workaround, mark first sample as being a Random Access Point to avoid sourcebuffer append issue 14567 // https://code.google.com/p/chromium/issues/detail?id=229412 14568 flags.dependsOn = 2; 14569 flags.isNonSync = 0; 14570 } 14571 track.samples = outputSamples; 14572 moof = mp4_generator_1.default.moof(track.sequenceNumber++, firstDTS, track); 14573 track.samples = []; 14574 var data = { 14575 data1: moof, 14576 data2: mdat, 14577 startPTS: firstPTS / timeScale, 14578 endPTS: (lastPTS + mp4SampleDuration) / timeScale, 14579 startDTS: firstDTS / timeScale, 14580 endDTS: this.nextAvcDts / timeScale, 14581 type: 'video', 14582 hasAudio: false, 14583 hasVideo: true, 14584 nb: outputSamples.length, 14585 dropped: dropped 14586 }; 14587 this.observer.trigger(events_1.default.FRAG_PARSING_DATA, data); 14588 return data; 14589 }; 14590 MP4Remuxer.prototype.remuxAudio = function (track, timeOffset, contiguous, accurateTimeOff
14590set) { 14591 var inputTimeScale = track.inputTimeScale; 14592 var mp4timeScale = track.timescale; 14593 var scaleFactor = inputTimeScale / mp4timeScale; 14594 var mp4SampleDuration = track.isAAC ? 1024 : 1152; 14595 var inputSampleDuration = mp4SampleDuration * scaleFactor; 14596 var ptsNormalize = this._PTSNormalize; 14597 var initPTS = this._initPTS; 14598 var rawMPEG = !track.isAAC && this.typeSupported.mpeg; 14599 var offset, mp4Sample, fillFrame, mdat, moof, firstPTS, lastPTS, inputSamples = track.samples, outputSamples = [], nextAudioPts = this.nextAudioPts; 14600 // for audio samples, also consider consecutive fragments as being contiguous (even if a level switch occurs), 14601 // for sake of clarity: 14602 // consecutive fragments are frags with 14603 // - less than 100ms gaps between new time offset (if accurate) and next expected PTS OR 14604 // - less than 20 audio frames distance 14605 // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1) 14606 // this helps ensuring audio continuity 14607 // and this also avoids audio glitches/cut when switching quality, or reporting wrong duration on first audio frame 14608 contiguous |= (inputSamples.length && nextAudioPts && 14609 ((accurateTimeOffset && Math.abs(timeOffset - nextAudioPts / inputTimeScale) < 0.1) || 14610 Math.abs((inputSamples[0].pts - nextAudioPts - initPTS)) < 20 * inputSampleDuration)); 14611 // compute normalized PTS 14612 inputSamples.forEach(function (sample) { 14613 sample.pts = sample.dts = ptsNormalize(sample.pts - initPTS, timeOffset * inputTimeScale); 14614 }); 14615 // filter out sample with negative PTS that are not playable anyway 14616 // if we don't remove these negative samples, they will shift all audio samples forward. 14617 // leading to audio overlap between current / next fragment 14618 inputSamples = inputSamples.filter(function (sample) { 14619 return sample.pts >= 0; 14620 }); 14621 // in case all samples have negative PTS, and have been filtered out, return now 14622 if (inputSamples.length === 0) { 14623 return; 14624 } 14625 if (!contiguous) { 14626 if (!accurateTimeOffset) { 14627 // if frag are mot contiguous and if we cant trust time offset, let's use first sample PTS as next audio PTS 14628 nextAudioPts = inputSamples[0].pts; 14629 } 14630 else { 14631 // if timeOffset is accurate, let's use it as predicted next audio PTS 14632 nextAudioPts = timeOffset * inputTimeScale; 14633 } 14634 } 14635 // If the audio track is missing samples, the frames seem to get "left-shifted" within the 14636 // resulting mp4 segment, causing sync issues and leaving gaps at the end of the audio segment. 14637 // In an effort to prevent this from happening, we inject frames here where there are gaps. 14638 // When possible, we inject a silent frame; when that's not possible, we duplicate the last 14639 // frame. 14640 if (track.isAAC) { 14641 var maxAudioFramesDrift = this.config.maxAudioFramesDrift; 14642 for (var i = 0, nextPts = nextAudioPts; i < inputSamples.length;) { 14643 // First, let's see how far off this frame is from where we expect it to be 14644 var sample = inputSamples[i], delta; 14645 var pts = sample.pts; 14646 delta = pts - nextPts; 14647 var duration = Math.abs(1000 * delta / inputTimeScale); 14648 // If we're overlapping by more than a duration, drop this sample 14649 if (delta <= -maxAudioFramesDrift * inputSampleDuration) { 14650 logger_1.logger.warn("Dropping 1 audio frame @ " + (nextPts / inputTimeScale).toFixed(3) + "s due to " + Math.round(duration) + " ms overlap."); 14651 inputSamples.splice(i, 1); 14652 track.len -= sample.unit.length; 14653 // Don't touch nextPtsNorm or i 14654 } // eslint-disable-line brace-style 14655 // Insert missing frames if: 14656 // 1: We're more than maxAudioFramesDrift frame away 14657 // 2: Not more than MAX_SILENT_FRAME_DURATION away 14658 // 3: currentTime (aka nextPtsNorm) is not 0 14659 else if (delta >= maxAudioFramesDrift * inputSampleDuration && duration < MAX_SILENT_FRAME_DURATION && nextPts) { 14660 var missing = Math.round(delta / inputSampleDuration); 14661 logger_1.logger.warn("Injecting " + missing + " audio frame @ " + (nextPts / inputTimeScale).toFixed(3) + "s due to " + Math.round(1000 * delta / inputTimeScale) + " ms gap."); 14662 for (var j = 0; j < missing; j++) { 14663 var newStamp = Math.max(nextPts, 0); 14664 fillFrame = aac_helper_1.default.getSilentFrame(track.manifestCodec || track.codec, track.channelCount); 14665 if (!fillFrame) { 14666 logger_1.logger.log('Unable to get silent frame for given audio codec; duplicating last frame instead.'); 14667 fillFrame = sample.unit.subarray(); 14668 } 14669 inputSamples.splice(i, 0, { unit: fillFrame, pts: newStamp, dts: newStamp }); 14670 track.len += fillFrame.length; 14671 nextPts += inputSampleDuration; 14672 i++; 14673 } 14674 // Adjust sample to next expected pts
14675 sample.pts = sample.dts = nextPts; 14676 nextPts += inputSampleDuration; 14677 i++; 14678 } 14679 else { 14680 // Otherwise, just adjust pts 14681 if (Math.abs(delta) > (0.1 * inputSampleDuration)) { 14682 // logger.log(`Invalid frame delta ${Math.round(delta + inputSampleDuration)} at PTS ${Math.round(pts / 90)} (should be ${Math.round(inputSampleDuration)}).`); 14683 } 14684 sample.pts = sample.dts = nextPts; 14685 nextPts += inputSampleDuration; 14686 i++; 14687 } 14688 } 14689 } 14690 for (var j = 0, nbSamples_1 = inputSamples.length; j < nbSamples_1; j++) { 14691 var audioSample = inputSamples[j]; 14692 var unit = audioSample.unit; 14693 var pts = audioSample.pts; 14694 // logger.log(`Audio/PTS:${Math.round(pts/90)}`); 14695 // if not first sample 14696 if (lastPTS !== undefined) { 14697 mp4Sample.duration = Math.round((pts - lastPTS) / scaleFactor); 14698 } 14699 else { 14700 var delta_1 = Math.round(1000 * (pts - nextAudioPts) / inputTimeScale), numMissingFrames = 0; 14701 // if fragment are contiguous, detect hole/overlapping between fragments 14702 // contiguous fragments are consecutive fragments from same quality level (same level, new SN = old SN + 1) 14703 if (contiguous && track.isAAC) { 14704 // log delta 14705 if (delta_1) { 14706 if (delta_1 > 0 && delta_1 < MAX_SILENT_FRAME_DURATION) { 14707 numMissingFrames = Math.round((pts - nextAudioPts) / inputSampleDuration); 14708 logger_1.logger.log(delta_1 + " ms hole between AAC samples detected,filling it"); 14709 if (numMissingFrames > 0) { 14710 fillFrame = aac_helper_1.default.getSilentFrame(track.manifestCodec || track.codec, track.channelCount); 14711 if (!fillFrame) { 14712 fillFrame = unit.subarray(); 14713 } 14714 track.len += numMissingFrames * fillFrame.length; 14715 } 14716 // if we have frame overlap, overlapping for more than half a frame duraion 14717 } 14718 else if (delta_1 < -12) { 14719 // drop overlapping audio frames... browser will deal with it 14720 logger_1.logger.log("drop overlapping AAC sample, expected/parsed/delta:" + (nextAudioPts / inputTimeScale).toFixed(3) + "s/" + (pts / inputTimeScale).toFixed(3) + "s/" + (-delta_1) + "ms"); 14721 track.len -= unit.byteLength; 14722 continue; 14723 } 14724 // set PTS/DTS to expected PTS/DTS 14725 pts = nextAudioPts; 14726 } 14727 } 14728 // remember first PTS of our audioSamples 14729 firstPTS = pts; 14730 if (track.len > 0) { 14731 /* concatenate the audio data and construct the mdat in place 14732 (need 8 more bytes to fill length and mdat type) */ 14733 var mdatSize = rawMPEG ? track.len : track.len + 8; 14734 offset = rawMPEG ? 0 : 8; 14735 try { 14736 mdat = new Uint8Array(mdatSize); 14737 } 14738 catch (err) { 14739 this.observer.trigger(events_1.default.ERROR, { type: errors_1.ErrorTypes.MUX_ERROR, details: errors_1.ErrorDetails.REMUX_ALLOC_ERROR, fatal: false, bytes: mdatSize, reason: "fail allocating audio mdat " + mdatSize }); 14740 return; 14741 } 14742 if (!rawMPEG) { 14743 var view = new DataView(mdat.buffer); 14744 view.setUint32(0, mdatSize); 14745 mdat.set(mp4_generator_1.default.types.mdat, 4); 14746 } 14747 } 14748 else { 14749 // no audio samples 14750 return; 14751 } 14752 for (var i = 0; i < numMissingFrames; i++) { 14753 fillFrame = aac_helper_1.default.getSilentFrame(track.manifestCodec || track.codec, track.channelCount); 14754 if (!fillFrame) { 14755 logger_1.logger.log('Unable to get silent frame for given audio codec; duplicating this frame instead.'); 14756 fillFrame = unit.subarray(); 14757 } 14758 mdat.set(fillFrame, offset); 14759 offset += fillFrame.byteLength; 14760 mp4Sample = { 14761 size: fillFrame.byteLength, 14762 cts: 0, 14763 duration: 1024, 14764 flags: { 14765 isLeading: 0, 14766 isDependedOn: 0, 14767 hasRedundancy: 0, 14768 degradPrio: 0, 14769 dependsOn: 1 14770 } 14771 }; 14772 outputSamples.push(mp4Sample); 14773 } 14774 } 14775 mdat.set(unit, offset); 14776 var unitLen = unit.byteLength; 14777 offset += unitLen; 14778 // console.log('PTS/DTS/initDTS/normPTS/normDTS/relative PTS : ${audioSample.pts}/${audioSample.dts}/${initDTS}/${ptsnorm}/${dtsnorm}/${(audioSample.pts/4294967296).toFixed(3)}'); 14779 mp4Sample = { 14780 size: unitLen, 14781 cts: 0, 14782 duration: 0, 14783 flags: { 14784 isLeading: 0, 14785 isDependedOn: 0, 14786 hasRedundancy: 0, 14787 degradPrio: 0, 14788 dependsOn: 1 14789 } 14790 }; 14791 outputSamples.push(mp4Sample); 14792 lastPTS = pts; 14793 } 14794 var lastSampleDuration = 0; 14795 var nbSamples = outputSamples.length; 14796 // set last sample duration as being identical to previous sample 14797 if (nbSamples >= 2) { 14798 lastSampleDuration = outputSamples[nbSamples - 2].duration; 14799 mp4Sample.duration = lastSampleDuration; 14800 } 14801 if (nbSamples) { 14802 // next audio sample PTS should be equal to last sample PTS + duration 14803 this.nextAudioPts = nextAudioPts = lastPTS + scaleFactor * lastSampleDuration;
14804 // logger.log('Audio/PTS/PTSend:' + audioSample.pts.toFixed(0) + '/' + this.nextAacDts.toFixed(0)); 14805 track.len = 0; 14806 track.samples = outputSamples; 14807 if (rawMPEG) { 14808 moof = new Uint8Array(); 14809 } 14810 else { 14811 moof = mp4_generator_1.default.moof(track.sequenceNumber++, firstPTS / scaleFactor, track); 14812 } 14813 track.samples = []; 14814 var start = firstPTS / inputTimeScale; 14815 var end = nextAudioPts / inputTimeScale; 14816 var audioData = { 14817 data1: moof, 14818 data2: mdat, 14819 startPTS: start, 14820 endPTS: end, 14821 startDTS: start, 14822 endDTS: end, 14823 type: 'audio', 14824 hasAudio: true, 14825 hasVideo: false, 14826 nb: nbSamples 14827 }; 14828 this.observer.trigger(events_1.default.FRAG_PARSING_DATA, audioData); 14829 return audioData; 14830 } 14831 return null; 14832 }; 14833 MP4Remuxer.prototype.remuxEmptyAudio = function (track, timeOffset, contiguous, videoData) { 14834 var inputTimeScale = track.inputTimeScale, mp4timeScale = track.samplerate ? track.samplerate : inputTimeScale, scaleFactor = inputTimeScale / mp4timeScale, nextAudioPts = this.nextAudioPts, 14835 // sync with video's timestamp 14836 startDTS = (nextAudioPts !== undefined ? nextAudioPts : videoData.startDTS * inputTimeScale) + this._initDTS, endDTS = videoData.endDTS * inputTimeScale + this._initDTS, 14837 // one sample's duration value 14838 sampleDuration = 1024, frameDuration = scaleFactor * sampleDuration, 14839 // samples count of this segment's duration
14840 nbSamples = Math.ceil((endDTS - startDTS) / frameDuration), 14841 // silent frame 14842 silentFrame = aac_helper_1.default.getSilentFrame(track.manifestCodec || track.codec, track.channelCount); 14843 logger_1.logger.warn('remux empty Audio'); 14844 // Can't remux if we can't generate a silent frame... 14845 if (!silentFrame) { 14846 logger_1.logger.trace('Unable to remuxEmptyAudio since we were unable to get a silent frame for given audio codec!'); 14847 return; 14848 } 14849 var samples = []; 14850 for (var i = 0; i < nbSamples; i++) { 14851 var stamp = startDTS + i * frameDuration; 14852 samples.push({ unit: silentFrame, pts: stamp, dts: stamp }); 14853 track.len += silentFrame.length; 14854 } 14855 track.samples = samples; 14856 this.remuxAudio(track, timeOffset, contiguous); 14857 }; 14858 MP4Remuxer.prototype.remuxID3 = function (track) { 14859 var length = track.samples.length, sample; 14860 var inputTimeScale = track.inputTimeScale; 14861 var initPTS = this._initPTS; 14862 var initDTS = this._initDTS; 14863 // consume samples 14864 if (length) { 14865 for (var index = 0; index < length; index++) { 14866 sample = track.samples[index]; 14867 // setting id3 pts, dts to relative time 14868 // using this._initPTS and this._initDTS to calculate relative time 14869 sample.pts = ((sample.pts - initPTS) / inputTimeScale); 14870 sample.dts = ((sample.dts - initDTS) / inputTimeScale); 14871 } 14872 this.observer.trigger(events_1.default.FRAG_PARSING_METADATA, { 14873 samples: track.samples 14874 }); 14875 } 14876 track.samples = []; 14877 }; 14878 MP4Remuxer.prototype.remuxText = function (track) { 14879 track.samples.sort(function (a, b) { 14880 return (a.pts - b.pts); 14881 }); 14882 var length = track.samples.length, sample; 14883 var inputTimeScale = track.inputTimeScale; 14884 var initPTS = this._initPTS; 14885 // consume samples 14886 if (length) { 14887 for (var index = 0; index < length; index++) { 14888 sample = track.samples[index]; 14889 // setting text pts, dts to relative time 14890 // using this._initPTS and this._initDTS to calculate relative time 14891 sample.pts = ((sample.pts - initPTS) / inputTimeScale); 14892 } 14893 this.observer.trigger(events_1.default.FRAG_PARSING_USERDATA, { 14894 samples: track.samples 14895 }); 14896 } 14897 track.samples = []; 14898 }; 14899 MP4Remuxer.prototype._PTSNormalize = function (value, reference) { 14900 var offset; 14901 if (reference === undefined) { 14902 return value; 14903 } 14904 if (reference < value) { 14905 // - 2^33 14906 offset = -8589934592; 14907 } 14908 else { 14909 // + 2^33 14910 offset = 8589934592; 14911 } 14912 /* PTS is 33bit (from 0 to 2^33 -1) 14913 if diff between value and reference is bigger than half of the amplitude (2^32) then it means that 14914 PTS looping occured. fill the gap */ 14915 while (Math.abs(value - reference) > 4294967296) { 14916 value += offset; 14917 } 14918 return value; 14919 }; 14920 return MP4Remuxer; 14921}()); 14922exports.default = MP4Remuxer; 14923 14924 14925/***/ }), 14926
14927/***/ "./src/remux/passthrough-remuxer.js": 14928/*!******************************************!*\ 14929 !*** ./src/remux/passthrough-remuxer.js ***! 14930 \******************************************/ 14931/*! no static exports found */ 14932/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 14933/***/ (function(module, exports, __webpack_require__) { 14934 14935"use strict"; 14936 14937Object.defineProperty(exports, "__esModule", { value: true }); 14938/** 14939 * passthrough remuxer 14940*/ 14941var events_1 = __webpack_require__(/*! ../events */ "./src/events.js"); 14942var PassThroughRemuxer = /** @class */ (function () { 14943 function PassThroughRemuxer(observer) { 14944 this.observer = observer; 14945 } 14946 PassThroughRemuxer.prototype.destroy = function () { 14947 }; 14948 PassThroughRemuxer.prototype.resetTimeStamp = function () { 14949 }; 14950 PassThroughRemuxer.prototype.resetInitSegment = function () { 14951 }; 14952 PassThroughRemuxer.prototype.remux = function (audioTrack, videoTrack, id3Track, textTrack, timeOffset, contiguous, accurateTimeOffset, rawData) { 14953 var observer = this.observer; 14954 var streamType = ''; 14955 if (audioTrack) { 14956 streamType += 'audio'; 14957 } 14958 if (videoTrack) { 14959 streamType += 'video'; 14960 } 14961 observer.trigger(events_1.default.FRAG_PARSING_DATA, { 14962 data1: rawData, 14963 startPTS: timeOffset, 14964 startDTS: timeOffset, 14965 type: streamType, 14966 hasAudio: !!audioTrack, 14967 hasVideo: !!videoTrack, 14968 nb: 1, 14969 dropped: 0 14970 }); 14971 // notify end of parsing 14972 observer.trigger(events_1.default.FRAG_PARSED); 14973 }; 14974 return PassThroughRemuxer; 14975}()); 14976exports.default = PassThroughRemuxer; 14977 14978 14979/***/ }), 14980
14981/***/ "./src/task-loop.js": 14982/*!**************************!*\ 14983 !*** ./src/task-loop.js ***! 14984 \**************************/ 14985/*! no static exports found */ 14986/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 14987/***/ (function(module, exports, __webpack_require__) { 14988 14989"use strict"; 14990 14991var __extends = (this && this.__extends) || (function () { 14992 var extendStatics = Object.setPrototypeOf || 14993 ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || 14994 function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; 14995 return function (d, b) { 14996 extendStatics(d, b); 14997 function __() { this.constructor = d; } 14998 d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); 14999 }; 15000})(); 15001Object.defineProperty(exports, "__esModule", { value: true }); 15002var event_handler_1 = __webpack_require__(/*! ./event-handler */ "./src/event-handler.js"); 15003/** 15004 * Sub-class specialization of EventHandler base class. 15005 * 15006 * TaskLoop allows to schedule a task function being called (optionnaly repeatedly) on the main loop, 15007 * scheduled asynchroneously, avoiding recursive calls in the same tick. 15008 * 15009 * The task itself is implemented in `doTick`. It can be requested and called for single execution 15010 * using the `tick` method. 15011 * 15012 * It will be assured that the task execution method (`tick`) only gets called once per main loop "tick", 15013 * no matter how often it gets requested for execution. Execution in further ticks will be scheduled accordingly. 15014 * 15015 * If further execution requests have already been scheduled on the next tick, it can be checked with `hasNextTick`, 15016 * and cancelled with `clearNextTick`. 15017 * 15018 * The task can be scheduled as an interval repeatedly with a period as parameter (see `setInterval`, `clearInterval`). 15019 * 15020 * Sub-classes need to implement the `doTick` method which will effectively have the task execution routine. 15021 * 15022 * Further explanations: 15023 * 15024 * The baseclass has a `tick` method that will schedule the doTick call. It may be called synchroneously 15025 * only for a stack-depth of one. On re-entrant calls, sub-sequent calls are scheduled for next main loop ticks. 15026 * 15027 * When the task execution (`tick` method) is called in re-entrant way this is detected and 15028 * we are limiting the task execution per call stack to exactly one, but scheduling/post-poning further 15029 * task processing on the next main loop iteration (also known as "next tick" in the Node/JS runtime lingo). 15030 */ 15031var TaskLoop = /** @class */ (function (_super) { 15032 __extends(TaskLoop, _super); 15033 function TaskLoop(hls) { 15034 var events = []; 15035 for (var _i = 1; _i < arguments.length; _i++) { 15036 events[_i - 1] = arguments[_i]; 15037 } 15038 var _this = _super.apply(this, [hls].concat(events)) || this; 15039 _this._tickInterval = null; 15040 _this._tickTimer = null; 15041 _this._tickCallCount = 0; 15042 _this._boundTick = _this.tick.bind(_this); 15043 return _this; 15044 } 15045 /** 15046 * @override 15047 */ 15048 TaskLoop.prototype.onHandlerDestroying = function () { 15049 // clear all timers before unregistering from event bus 15050 this.clearNextTick(); 15051 this.clearInterval(); 15052 }; 15053 /** 15054 * @returns {boolean} 15055 */ 15056 TaskLoop.prototype.hasInterval = function () { 15057 return !!this._tickInterval; 15058 }; 15059 /** 15060 * @returns {boolean} 15061 */ 15062 TaskLoop.prototype.hasNextTick = function () { 15063 return !!this._tickTimer; 15064 }; 15065 /** 15066 * @param {number} millis Interval time (ms) 15067 * @returns {boolean} True when interval has been scheduled, false when already scheduled (no effect) 15068 */ 15069 TaskLoop.prototype.setInterval = function (millis) { 15070 if (!this._tickInterval) { 15071 this._tickInterval = setInterval(this._boundTick, millis); 15072 return true; 15073 } 15074 return false; 15075 }; 15076 /** 15077 * @returns {boolean} True when interval was cleared, false when none was set (no effect) 15078 */ 15079 TaskLoop.prototype.clearInterval = function () { 15080 if (this._tickInterval) { 15081 clearInterval(this._tickInterval); 15082 this._tickInterval = null; 15083 return true; 15084 } 15085 return false;
15086 }; 15087 /** 15088 * @returns {boolean} True when timeout was cleared, false when none was set (no effect) 15089 */ 15090 TaskLoop.prototype.clearNextTick = function () { 15091 if (this._tickTimer) { 15092 clearTimeout(this._tickTimer); 15093 this._tickTimer = null; 15094 return true; 15095 } 15096 return false; 15097 }; 15098 /** 15099 * Will call the subclass doTick implementation in this main loop tick 15100 * or in the next one (via setTimeout(,0)) in case it has already been called 15101 * in this tick (in case this is a re-entrant call). 15102 */ 15103 TaskLoop.prototype.tick = function () { 15104 this._tickCallCount++; 15105 if (this._tickCallCount === 1) { 15106 this.doTick(); 15107 // re-entrant call to tick from previous doTick call stack 15108 // -> schedule a call on the next main loop iteration to process this task processing request 15109 if (this._tickCallCount > 1) { 15110 // make sure only one timer exists at any time at max 15111 this.clearNextTick(); 15112 this._tickTimer = setTimeout(this._boundTick, 0); 15113 } 15114 this._tickCallCount = 0; 15115 } 15116 }; 15117 /** 15118 * For subclass to implement task logic 15119 * @abstract 15120 */ 15121 TaskLoop.prototype.doTick = function () { }; 15122 return TaskLoop; 15123}(event_handler_1.default)); 15124exports.default = TaskLoop; 15125 15126 15127/***/ }), 15128
vendor: 3,282 bytes, lines 15129-15218
15129/***/ "./src/utils/attr-list.js": 15130/*!********************************!*\ 15131 !*** ./src/utils/attr-list.js ***! 15132 \********************************/ 15133/*! no static exports found */ 15134/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 15135/***/ (function(module, exports, __webpack_require__) { 15136 15137"use strict"; 15138/* WEBPACK VAR INJECTION */(function(Number) { 15139Object.defineProperty(exports, "__esModule", { value: true }); 15140var DECIMAL_RESOLUTION_REGEX = /^(\d+)x(\d+)$/; // eslint-disable-line no-useless-escape 15141var ATTR_LIST_REGEX = /\s*(.+?)\s*=((?:\".*?\")|.*?)(?:,|$)/g; // eslint-disable-line no-useless-escape 15142// adapted from https://github.com/kanongil/node-m3u8parse/blob/master/attrlist.js 15143var AttrList = /** @class */ (function () { 15144 function AttrList(attrs) { 15145 if (typeof attrs === 'string') { 15146 attrs = AttrList.parseAttrList(attrs); 15147 } 15148 for (var attr in attrs) { 15149 if (attrs.hasOwnProperty(attr)) { 15150 this[attr] = attrs[attr]; 15151 } 15152 } 15153 } 15154 AttrList.prototype.decimalInteger = function (attrName) { 15155 var intValue = parseInt(this[attrName], 10); 15156 if (intValue > Number.MAX_SAFE_INTEGER) { 15157 return Infinity; 15158 } 15159 return intValue; 15160 }; 15161 AttrList.prototype.hexadecimalInteger = function (attrName) { 15162 if (this[attrName]) { 15163 var stringValue = (this[attrName] || '0x').slice(2); 15164 stringValue = ((stringValue.length & 1) ? '0' : '') + stringValue; 15165 var value = new Uint8Array(stringValue.length / 2); 15166 for (var i = 0; i < stringValue.length / 2; i++) { 15167 value[i] = parseInt(stringValue.slice(i * 2, i * 2 + 2), 16); 15168 } 15169 return value; 15170 } 15171 else { 15172 return null; 15173 } 15174 }; 15175 AttrList.prototype.hexadecimalIntegerAsNumber = function (attrName) { 15176 var intValue = parseInt(this[attrName], 16); 15177 if (intValue > Number.MAX_SAFE_INTEGER) { 15178 return Infinity; 15179 } 15180 return intValue; 15181 }; 15182 AttrList.prototype.decimalFloatingPoint = function (attrName) { 15183 return parseFloat(this[attrName]); 15184 }; 15185 AttrList.prototype.enumeratedString = function (attrName) { 15186 return this[attrName]; 15187 }; 15188 AttrList.prototype.decimalResolution = function (attrName) { 15189 var res = DECIMAL_RESOLUTION_REGEX.exec(this[attrName]); 15190 if (res === null) { 15191 return undefined; 15192 } 15193 return { 15194 width: parseInt(res[1], 10), 15195 height: parseInt(res[2], 10) 15196 }; 15197 }; 15198 AttrList.parseAttrList = function (input) { 15199 var match, attrs = {}; 15200 ATTR_LIST_REGEX.lastIndex = 0; 15201 while ((match = ATTR_LIST_REGEX.exec(input)) !== null) { 15202 var value = match[2], quote = '"'; 15203 if (value.indexOf(quote) === 0 && 15204 value.lastIndexOf(quote) === (value.length - 1)) { 15205 value = value.slice(1, -1); 15206 } 15207 attrs[match[1]] = value; 15208 } 15209 return attrs; 15210 }; 15211 return AttrList; 15212}()); 15213exports.default = AttrList; 15214 15215/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 15216 15217/***/ }), 15218
15219/***/ "./src/utils/binary-search.js": 15220/*!************************************!*\ 15221 !*** ./src/utils/binary-search.js ***! 15222 \************************************/ 15223/*! no static exports found */ 15224/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 15225/***/ (function(module, exports, __webpack_require__) { 15226 15227"use strict"; 15228 15229Object.defineProperty(exports, "__esModule", { value: true }); 15230var BinarySearch = { 15231 /** 15232 * Searches for an item in an array which matches a certain condition. 15233 * This requires the condition to only match one item in the array, 15234 * and for the array to be ordered. 15235 * 15236 * @param {Array} list The array to search. 15237 * @param {Function} comparisonFunction 15238 * Called and provided a candidate item as the first argument. 15239 * Should return: 15240 * > -1 if the item should be located at a lower index than the provided item. 15241 * > 1 if the item should be located at a higher index than the provided item. 15242 * > 0 if the item is the item you're looking for. 15243 * 15244 * @return {*} The object if it is found or null otherwise. 15245 */ 15246 search: function (list, comparisonFunction) { 15247 var minIndex = 0; 15248 var maxIndex = list.length - 1; 15249 var currentIndex = null; 15250 var currentElement = null; 15251 while (minIndex <= maxIndex) { 15252 currentIndex = (minIndex + maxIndex) / 2 | 0; 15253 currentElement = list[currentIndex]; 15254 var comparisonResult = comparisonFunction(currentElement); 15255 if (comparisonResult > 0) { 15256 minIndex = currentIndex + 1; 15257 } 15258 else if (comparisonResult < 0) { 15259 maxIndex = currentIndex - 1; 15260 } 15261 else { 15262 return currentElement; 15263 } 15264 } 15265 return null; 15266 } 15267}; 15268exports.default = BinarySearch; 15269 15270 15271/***/ }), 15272
15273/***/ "./src/utils/buffer-helper.js": 15274/*!************************************!*\ 15275 !*** ./src/utils/buffer-helper.js ***! 15276 \************************************/ 15277/*! no static exports found */ 15278/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 15279/***/ (function(module, exports, __webpack_require__) { 15280 15281"use strict"; 15282 15283/** 15284 * @module BufferHelper 15285 * 15286 * Providing methods dealing with buffer length retrieval for example. 15287 * 15288 * In general, a helper around HTML5 MediaElement TimeRanges gathered from `buffered` property. 15289 * 15290 * Also @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/buffered 15291*/ 15292Object.defineProperty(exports, "__esModule", { value: true }); 15293var BufferHelper = /** @class */ (function () { 15294 function BufferHelper() { 15295 } 15296 /** 15297 * Return true if `media`'s buffered include `position` 15298 * @param {HTMLMediaElement|SourceBuffer} media 15299 * @param {number} position 15300 * @returns {boolean} 15301 */ 15302 BufferHelper.isBuffered = function (media, position) { 15303 try { 15304 if (media) { 15305 var buffered = media.buffered; 15306 for (var i = 0; i < buffered.length; i++) { 15307 if (position >= buffered.start(i) && position <= buffered.end(i)) { 15308 return true; 15309 } 15310 } 15311 } 15312 } 15313 catch (error) { 15314 // this is to catch 15315 // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer': 15316 // This SourceBuffer has been removed from the parent media source 15317 } 15318 return false; 15319 }; 15320 BufferHelper.bufferInfo = function (media, pos, maxHoleDuration) { 15321 try { 15322 if (media) { 15323 var vbuffered = media.buffered, buffered = [], i = void 0; 15324 for (i = 0; i < vbuffered.length; i++) { 15325 buffered.push({ start: vbuffered.start(i), end: vbuffered.end(i) }); 15326 } 15327 return this.bufferedInfo(buffered, pos, maxHoleDuration); 15328 } 15329 } 15330 catch (error) { 15331 // this is to catch 15332 // InvalidStateError: Failed to read the 'buffered' property from 'SourceBuffer': 15333 // This SourceBuffer has been removed from the parent media source 15334 } 15335 return { len: 0, start: pos, end: pos, nextStart: undefined }; 15336 }; 15337 BufferHelper.bufferedInfo = function (buffered, pos, maxHoleDuration) { 15338 var buffered2 = [], 15339 // bufferStart and bufferEnd are buffer boundaries around current video position 15340 bufferLen, bufferStart, bufferEnd, bufferStartNext, i; 15341 // sort on buffer.start/smaller end (IE does not always return sorted buffered range) 15342 buffered.sort(function (a, b) { 15343 var diff = a.start - b.start; 15344 if (diff) { 15345 return diff; 15346 } 15347 else { 15348 return b.end - a.end; 15349 } 15350 }); 15351 // there might be some small holes between buffer time range 15352 // consider that holes smaller than maxHoleDuration are irrelevant and build another 15353 // buffer time range representations that discards those holes 15354 for (i = 0; i < buffered.length; i++) { 15355 var buf2len = buffered2.length; 15356 if (buf2len) { 15357 var buf2end = buffered2[buf2len - 1].end; 15358 // if small hole (value between 0 or maxHoleDuration ) or overlapping (negative) 15359 if ((buffered[i].start - buf2end) < maxHoleDuration) { 15360 // merge overlapping time ranges 15361 // update lastRange.end only if smaller than item.end 15362 // e.g. [ 1, 15] with [ 2,8] => [ 1,15] (no need to modify lastRange.end) 15363 // whereas [ 1, 8] with [ 2,15] => [ 1,15] ( lastRange should switch from [1,8] to [1,15]) 15364 if (buffered[i].end > buf2end) { 15365 buffered2[buf2len - 1].end = buffered[i].end; 15366 } 15367 } 15368 else { 15369 // big hole 15370 buffered2.push(buffered[i]); 15371 } 15372 } 15373 else { 15374 // first value 15375 buffered2.push(buffered[i]); 15376 } 15377 } 15378 for (i = 0, bufferLen = 0, bufferStart = bufferEnd = pos; i < buffered2.length; i++) { 15379 var start = buffered2[i].start, end = buffered2[i].end; 15380 // logger.log('buf start/end:' + buffered.start(i) + '/' + buffered.end(i)); 15381 if ((pos + maxHoleDuration) >= start && pos < end) { 15382 // play position is inside this buffer TimeRange, retrieve end of buffer position and buffer length 15383 bufferStart = start; 15384 bufferEnd = end; 15385 bufferLen = bufferEnd - pos; 15386 } 15387 else if ((pos + maxHoleDuration) < start) { 15388 bufferStartNext = start; 15389 break; 15390 } 15391 } 15392 return { len: bufferLen, start: bufferStart, end: bufferEnd, nextStart: bufferStartNext }; 15393 }; 15394 return BufferHelper; 15395}()); 15396exports.BufferHelper = BufferHelper; 15397 15398 15399/***/ }), 15400
15401/***/ "./src/utils/cea-608-parser.js": 15402/*!*************************************!*\ 15403 !*** ./src/utils/cea-608-parser.js ***! 15404 \*************************************/ 15405/*! no static exports found */ 15406/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 15407/***/ (function(module, exports, __webpack_require__) { 15408 15409"use strict"; 15410 15411/** 15412 * 15413 * This code was ported from the dash.js project at: 15414 * https://github.com/Dash-Industry-Forum/dash.js/blob/development/externals/cea608-parser.js 15415 * https://github.com/Dash-Industry-Forum/dash.js/commit/8269b26a761e0853bb21d78780ed945144ecdd4d#diff-71bc295a2d6b6b7093a1d3290d53a4b2 15416 * 15417 * The original copyright appears below: 15418 * 15419 * The copyright in this software is being made available under the BSD License, 15420 * included below. This software may be subject to other third party and contributor 15421 * rights, including patent rights, and no such rights are granted under this license. 15422 * 15423 * Copyright (c) 2015-2016, DASH Industry Forum. 15424 * All rights reserved. 15425 * 15426 * Redistribution and use in source and binary forms, with or without modification, 15427 * are permitted provided that the following conditions are met: 15428 * 1. Redistributions of source code must retain the above copyright notice, this 15429 * list of conditions and the following disclaimer. 15430 * * Redistributions in binary form must reproduce the above copyright notice, 15431 * this list of conditions and the following disclaimer in the documentation and/or 15432 * other materials provided with the distribution. 15433 * 2. Neither the name of Dash Industry Forum nor the names of its 15434 * contributors may be used to endorse or promote products derived from this software 15435 * without specific prior written permission. 15436 * 15437 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS AS IS AND ANY 15438 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15439 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 15440 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 15441 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 15442 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 15443 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 15444 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 15445 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 15446 * POSSIBILITY OF SUCH DAMAGE. 15447 */ 15448/** 15449 * Exceptions from regular ASCII. CodePoints are mapped to UTF-16 codes 15450 */ 15451Object.defineProperty(exports, "__esModule", { value: true }); 15452var specialCea608CharsCodes = { 15453 0x2a: 0xe1, 15454 0x5c: 0xe9, 15455 0x5e: 0xed, 15456 0x5f: 0xf3, 15457 0x60: 0xfa, 15458 0x7b: 0xe7, 15459 0x7c: 0xf7, 15460 0x7d: 0xd1, 15461 0x7e: 0xf1, 15462 0x7f: 0x2588, 15463 // THIS BLOCK INCLUDES THE 16 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS 15464 // THAT COME FROM HI BYTE=0x11 AND LOW BETWEEN 0x30 AND 0x3F 15465 // THIS MEANS THAT \x50 MUST BE ADDED TO THE VALUES 15466 0x80: 0xae, 15467 0x81: 0xb0, 15468 0x82: 0xbd, 15469 0x83: 0xbf, 15470 0x84: 0x2122, 15471 0x85: 0xa2, 15472 0x86: 0xa3, 15473 0x87: 0x266a, 15474 0x88: 0xe0, 15475 0x89: 0x20, 15476 0x8a: 0xe8, 15477 0x8b: 0xe2, 15478 0x8c: 0xea, 15479 0x8d: 0xee, 15480 0x8e: 0xf4, 15481 0x8f: 0xfb, 15482 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS 15483 // THAT COME FROM HI BYTE=0x12 AND LOW BETWEEN 0x20 AND 0x3F 15484 0x90: 0xc1, 15485 0x91: 0xc9, 15486 0x92: 0xd3, 15487 0x93: 0xda, 15488 0x94: 0xdc, 15489 0x95: 0xfc, 15490 0x96: 0x2018, 15491 0x97: 0xa1, 15492 0x98: 0x2a, 15493 0x99: 0x2019, 15494 0x9a: 0x2501, 15495 0x9b: 0xa9, 15496 0x9c: 0x2120, 15497 0x9d: 0x2022, 15498 0x9e: 0x201c, 15499 0x9f: 0x201d, 15500 0xa0: 0xc0, 15501 0xa1: 0xc2, 15502 0xa2: 0xc7, 15503 0xa3: 0xc8, 15504 0xa4: 0xca, 15505 0xa5: 0xcb, 15506 0xa6: 0xeb, 15507 0xa7: 0xce, 15508 0xa8: 0xcf, 15509 0xa9: 0xef, 15510 0xaa: 0xd4, 15511 0xab: 0xd9, 15512 0xac: 0xf9, 15513 0xad: 0xdb, 15514 0xae: 0xab, 15515 0xaf: 0xbb, 15516 // THIS BLOCK INCLUDES THE 32 EXTENDED (TWO-BYTE) LINE 21 CHARACTERS 15517 // THAT COME FROM HI BYTE=0x13 AND LOW BETWEEN 0x20 AND 0x3F 15518 0xb0: 0xc3, 15519 0xb1: 0xe3, 15520 0xb2: 0xcd, 15521 0xb3: 0xcc, 15522 0xb4: 0xec, 15523 0xb5: 0xd2, 15524 0xb6: 0xf2, 15525 0xb7: 0xd5, 15526 0xb8: 0xf5, 15527 0xb9: 0x7b, 15528 0xba: 0x7d, 15529 0xbb: 0x5c, 15530 0xbc: 0x5e, 15531 0xbd: 0x5f, 15532 0xbe: 0x7c, 15533 0xbf: 0x223c, 15534 0xc0: 0xc4, 15535 0xc1: 0xe4, 15536 0xc2: 0xd6,
15537 0xc3: 0xf6, 15538 0xc4: 0xdf, 15539 0xc5: 0xa5, 15540 0xc6: 0xa4, 15541 0xc7: 0x2503, 15542 0xc8: 0xc5, 15543 0xc9: 0xe5, 15544 0xca: 0xd8, 15545 0xcb: 0xf8, 15546 0xcc: 0x250f, 15547 0xcd: 0x2513, 15548 0xce: 0x2517, 15549 0xcf: 0x251b // Box drawings heavy up and left 15550}; 15551/** 15552 * Utils 15553 */ 15554var getCharForByte = function (byte) { 15555 var charCode = byte; 15556 if (specialCea608CharsCodes.hasOwnProperty(byte)) { 15557 charCode = specialCea608CharsCodes[byte]; 15558 } 15559 return String.fromCharCode(charCode); 15560}; 15561var NR_ROWS = 15, NR_COLS = 100; 15562// Tables to look up row from PAC data 15563var rowsLowCh1 = { 0x11: 1, 0x12: 3, 0x15: 5, 0x16: 7, 0x17: 9, 0x10: 11, 0x13: 12, 0x14: 14 }; 15564var rowsHighCh1 = { 0x11: 2, 0x12: 4, 0x15: 6, 0x16: 8, 0x17: 10, 0x13: 13, 0x14: 15 }; 15565var rowsLowCh2 = { 0x19: 1, 0x1A: 3, 0x1D: 5, 0x1E: 7, 0x1F: 9, 0x18: 11, 0x1B: 12, 0x1C: 14 }; 15566var rowsHighCh2 = { 0x19: 2, 0x1A: 4, 0x1D: 6, 0x1E: 8, 0x1F: 10, 0x1B: 13, 0x1C: 15 }; 15567var backgroundColors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'black', 'transparent']; 15568/** 15569 * Simple logger class to be able to write with time-stamps and filter on level. 15570 */ 15571var logger = { 15572 verboseFilter: { 'DATA': 3, 'DEBUG': 3, 'INFO': 2, 'WARNING': 2, 'TEXT': 1, 'ERROR': 0 }, 15573 time: null, 15574 verboseLevel: 0, 15575 setTime: function (newTime) { 15576 this.time = newTime; 15577 }, 15578 log: function (severity, msg) { 15579 var minLevel = this.verboseFilter[severity]; 15580 if (this.verboseLevel >= minLevel) { 15581 // console.log(this.time + ' [' + severity + '] ' + msg); 15582 } 15583 } 15584}; 15585var numArrayToHexArray = function (numArray) { 15586 var hexArray = []; 15587 for (var j = 0; j < numArray.length; j++) { 15588 hexArray.push(numArray[j].toString(16)); 15589 } 15590 return hexArray; 15591}; 15592var PenState = /** @class */ (function () { 15593 function PenState(foreground, underline, italics, background, flash) { 15594 this.foreground = foreground || 'white'; 15595 this.underline = underline || false; 15596 this.italics = italics || false; 15597 this.background = background || 'black'; 15598 this.flash = flash || false; 15599 } 15600 PenState.prototype.reset = function () { 15601 this.foreground = 'white'; 15602 this.underline = false; 15603 this.italics = false; 15604 this.background = 'black'; 15605 this.flash = false; 15606 }; 15607 PenState.prototype.setStyles = function (styles) { 15608 var attribs = ['foreground', 'underline', 'italics', 'background', 'flash']; 15609 for (var i = 0; i < attribs.length; i++) { 15610 var style = attribs[i]; 15611 if (styles.hasOwnProperty(style)) { 15612 this[style] = styles[style]; 15613 } 15614 } 15615 }; 15616 PenState.prototype.isDefault = function () { 15617 return (this.foreground === 'white' && !this.underline && !this.italics && 15618 this.background === 'black' && !this.flash); 15619 }; 15620 PenState.prototype.equals = function (other) { 15621 return ((this.foreground === other.foreground) && 15622 (this.underline === other.underline) && 15623 (this.italics === other.italics) && 15624 (this.background === other.background) && 15625 (this.flash === other.flash)); 15626 }; 15627 PenState.prototype.copy = function (newPenState) { 15628 this.foreground = newPenState.foreground; 15629 this.underline = newPenState.underline; 15630 this.italics = newPenState.italics; 15631 this.background = newPenState.background; 15632 this.flash = newPenState.flash; 15633 }; 15634 PenState.prototype.toString = function () { 15635 return ('color=' + this.foreground + ', underline=' + this.underline + ', italics=' + this.italics + 15636 ', background=' + this.background + ', flash=' + this.flash); 15637 }; 15638 return PenState; 15639}()); 15640/** 15641 * Unicode character with styling and background. 15642 * @constructor 15643 */ 15644var StyledUnicodeChar = /** @class */ (function () { 15645 function StyledUnicodeChar(uchar, foreground, underline, italics, background, flash) { 15646 this.uchar = uchar || ' '; // unicode character 15647 this.penState = new PenState(foreground, underline, italics, background, flash); 15648 } 15649 StyledUnicodeChar.prototype.reset = function () { 15650 this.uchar = ' '; 15651 this.penState.reset(); 15652 }; 15653 StyledUnicodeChar.prototype.setChar = function (uchar, newPenState) { 15654 this.uchar = uchar; 15655 this.penState.copy(newPenState); 15656 }; 15657 StyledUnicodeChar.prototype.setPenState = function (newPenState) { 15658 this.penState.copy(newPenState); 15659 }; 15660 StyledUnicodeChar.prototype.equals = function (other) { 15661 return this.uchar === other.uchar && this.penState.equals(other.penState); 15662 }; 15663 StyledUnicodeChar.prototype.copy = function (newChar) { 15664 this.uchar = newChar.uchar; 15665 this.penState.copy(newChar.penState); 15666 }; 15667 StyledUnicodeChar.prototype.isEmpty = function () { 15668 return this.uchar === ' ' && this.penState.isDefault(); 15669 }; 15670 return StyledUnicodeChar; 15671}()); 15672/** 15673 * CEA-608 row consisting of NR_COLS instances of StyledUnicodeChar. 15674 * @constructor 15675 */ 15676var Row = /** @class */ (function () { 15677 function Row() { 15678 this.chars = []; 15679 for (var i = 0; i < NR_COLS; i++) { 15680 this.chars.push(new StyledUnicodeChar()); 15681 } 15682 this.pos = 0; 15683 this.currPenState = new PenState(); 15684 } 15685 Row.prototype.equals = function (other) { 15686 var equal = true; 15687 for (var i = 0; i < NR_COLS; i++) { 15688 if (!this.chars[i].equals(other.chars[i])) { 15689 equal = false;
vendor: 4,350 bytes, lines 15690-15835
15690 break; 15691 } 15692 } 15693 return equal; 15694 }; 15695 Row.prototype.copy = function (other) { 15696 for (var i = 0; i < NR_COLS; i++) { 15697 this.chars[i].copy(other.chars[i]); 15698 } 15699 }; 15700 Row.prototype.isEmpty = function () { 15701 var empty = true; 15702 for (var i = 0; i < NR_COLS; i++) { 15703 if (!this.chars[i].isEmpty()) { 15704 empty = false; 15705 break; 15706 } 15707 } 15708 return empty; 15709 }; 15710 /** 15711 * Set the cursor to a valid column. 15712 */ 15713 Row.prototype.setCursor = function (absPos) { 15714 if (this.pos !== absPos) { 15715 this.pos = absPos; 15716 } 15717 if (this.pos < 0) { 15718 logger.log('ERROR', 'Negative cursor position ' + this.pos); 15719 this.pos = 0; 15720 } 15721 else if (this.pos > NR_COLS) { 15722 logger.log('ERROR', 'Too large cursor position ' + this.pos); 15723 this.pos = NR_COLS; 15724 } 15725 }; 15726 /** 15727 * Move the cursor relative to current position. 15728 */ 15729 Row.prototype.moveCursor = function (relPos) { 15730 var newPos = this.pos + relPos; 15731 if (relPos > 1) { 15732 for (var i = this.pos + 1; i < newPos + 1; i++) { 15733 this.chars[i].setPenState(this.currPenState); 15734 } 15735 } 15736 this.setCursor(newPos); 15737 }; 15738 /** 15739 * Backspace, move one step back and clear character. 15740 */ 15741 Row.prototype.backSpace = function () { 15742 this.moveCursor(-1); 15743 this.chars[this.pos].setChar(' ', this.currPenState); 15744 }; 15745 Row.prototype.insertChar = function (byte) { 15746 if (byte >= 0x90) { // Extended char 15747 this.backSpace(); 15748 } 15749 var char = getCharForByte(byte); 15750 if (this.pos >= NR_COLS) { 15751 logger.log('ERROR', 'Cannot insert ' + byte.toString(16) + 15752 ' (' + char + ') at position ' + this.pos + '. Skipping it!'); 15753 return; 15754 } 15755 this.chars[this.pos].setChar(char, this.currPenState); 15756 this.moveCursor(1); 15757 }; 15758 Row.prototype.clearFromPos = function (startPos) { 15759 var i; 15760 for (i = startPos; i < NR_COLS; i++) { 15761 this.chars[i].reset(); 15762 } 15763 }; 15764 Row.prototype.clear = function () { 15765 this.clearFromPos(0); 15766 this.pos = 0; 15767 this.currPenState.reset(); 15768 }; 15769 Row.prototype.clearToEndOfRow = function () { 15770 this.clearFromPos(this.pos); 15771 }; 15772 Row.prototype.getTextString = function () { 15773 var chars = []; 15774 var empty = true; 15775 for (var i = 0; i < NR_COLS; i++) { 15776 var char = this.chars[i].uchar; 15777 if (char !== ' ') { 15778 empty = false; 15779 } 15780 chars.push(char); 15781 } 15782 if (empty) { 15783 return ''; 15784 } 15785 else { 15786 return chars.join(''); 15787 } 15788 }; 15789 Row.prototype.setPenStyles = function (styles) { 15790 this.currPenState.setStyles(styles); 15791 var currChar = this.chars[this.pos]; 15792 currChar.setPenState(this.currPenState); 15793 }; 15794 return Row; 15795}()); 15796/** 15797 * Keep a CEA-608 screen of 32x15 styled characters 15798 * @constructor 15799*/ 15800var CaptionScreen = /** @class */ (function () { 15801 function CaptionScreen() { 15802 this.rows = []; 15803 for (var i = 0; i < NR_ROWS; i++) { 15804 this.rows.push(new Row()); 15805 } // Note that we use zero-based numbering (0-14) 15806 this.currRow = NR_ROWS - 1; 15807 this.nrRollUpRows = null; 15808 this.reset(); 15809 } 15810 CaptionScreen.prototype.reset = function () { 15811 for (var i = 0; i < NR_ROWS; i++) { 15812 this.rows[i].clear(); 15813 } 15814 this.currRow = NR_ROWS - 1; 15815 }; 15816 CaptionScreen.prototype.equals = function (other) { 15817 var equal = true; 15818 for (var i = 0; i < NR_ROWS; i++) { 15819 if (!this.rows[i].equals(other.rows[i])) { 15820 equal = false; 15821 break; 15822 } 15823 } 15824 return equal; 15825 }; 15826 CaptionScreen.prototype.copy = function (other) { 15827 for (var i = 0; i < NR_ROWS; i++) { 15828 this.rows[i].copy(other.rows[i]); 15829 } 15830 }; 15831 CaptionScreen.prototype.isEmpty = function () { 15832 var empty = true; 15833 for (var i = 0; i < NR_ROWS; i++) { 15834 if (!this.rows[i].isEmpty()) { 15835 empty = false;
vendor: 18,434 bytes, lines 15836-16328
15836 break; 15837 } 15838 } 15839 return empty; 15840 }; 15841 CaptionScreen.prototype.backSpace = function () { 15842 var row = this.rows[this.currRow]; 15843 row.backSpace(); 15844 }; 15845 CaptionScreen.prototype.clearToEndOfRow = function () { 15846 var row = this.rows[this.currRow]; 15847 row.clearToEndOfRow(); 15848 }; 15849 /** 15850 * Insert a character (without styling) in the current row. 15851 */ 15852 CaptionScreen.prototype.insertChar = function (char) { 15853 var row = this.rows[this.currRow]; 15854 row.insertChar(char); 15855 }; 15856 CaptionScreen.prototype.setPen = function (styles) { 15857 var row = this.rows[this.currRow]; 15858 row.setPenStyles(styles); 15859 }; 15860 CaptionScreen.prototype.moveCursor = function (relPos) { 15861 var row = this.rows[this.currRow]; 15862 row.moveCursor(relPos); 15863 }; 15864 CaptionScreen.prototype.setCursor = function (absPos) { 15865 logger.log('INFO', 'setCursor: ' + absPos); 15866 var row = this.rows[this.currRow]; 15867 row.setCursor(absPos); 15868 }; 15869 CaptionScreen.prototype.setPAC = function (pacData) { 15870 logger.log('INFO', 'pacData = ' + JSON.stringify(pacData)); 15871 var newRow = pacData.row - 1; 15872 if (this.nrRollUpRows && newRow < this.nrRollUpRows - 1) { 15873 newRow = this.nrRollUpRows - 1; 15874 } 15875 // Make sure this only affects Roll-up Captions by checking this.nrRollUpRows 15876 if (this.nrRollUpRows && this.currRow !== newRow) { 15877 // clear all rows first 15878 for (var i = 0; i < NR_ROWS; i++) { 15879 this.rows[i].clear(); 15880 } 15881 // Copy this.nrRollUpRows rows from lastOutputScreen and place it in the newRow location 15882 // topRowIndex - the start of rows to copy (inclusive index) 15883 var topRowIndex = this.currRow + 1 - (this.nrRollUpRows); 15884 // We only copy if the last position was already shown. 15885 // We use the cueStartTime value to check this. 15886 var lastOutputScreen = this.lastOutputScreen; 15887 if (lastOutputScreen) { 15888 var prevLineTime = lastOutputScreen.rows[topRowIndex].cueStartTime; 15889 if (prevLineTime && prevLineTime < logger.time) { 15890 for (var i = 0; i < this.nrRollUpRows; i++) { 15891 this.rows[newRow - this.nrRollUpRows + i + 1].copy(lastOutputScreen.rows[topRowIndex + i]); 15892 } 15893 } 15894 } 15895 } 15896 this.currRow = newRow; 15897 var row = this.rows[this.currRow]; 15898 if (pacData.indent !== null) { 15899 var indent = pacData.indent; 15900 var prevPos = Math.max(indent - 1, 0); 15901 row.setCursor(pacData.indent); 15902 pacData.color = row.chars[prevPos].penState.foreground; 15903 } 15904 var styles = { foreground: pacData.color, underline: pacData.underline, italics: pacData.italics, background: 'black', flash: false }; 15905 this.setPen(styles); 15906 }; 15907 /** 15908 * Set background/extra foreground, but first do back_space, and then insert space (backwards compatibility). 15909 */ 15910 CaptionScreen.prototype.setBkgData = function (bkgData) { 15911 logger.log('INFO', 'bkgData = ' + JSON.stringify(bkgData)); 15912 this.backSpace(); 15913 this.setPen(bkgData); 15914 this.insertChar(0x20); // Space 15915 }; 15916 CaptionScreen.prototype.setRollUpRows = function (nrRows) { 15917 this.nrRollUpRows = nrRows; 15918 }; 15919 CaptionScreen.prototype.rollUp = function () { 15920 if (this.nrRollUpRows === null) { 15921 logger.log('DEBUG', 'roll_up but nrRollUpRows not set yet'); 15922 return; // Not properly setup 15923 } 15924 logger.log('TEXT', this.getDisplayText()); 15925 var topRowIndex = this.currRow + 1 - this.nrRollUpRows; 15926 var topRow = this.rows.splice(topRowIndex, 1)[0]; 15927 topRow.clear(); 15928 this.rows.splice(this.currRow, 0, topRow); 15929 logger.log('INFO', 'Rolling up'); 15930 // logger.log('TEXT', this.get_display_text()) 15931 }; 15932 /** 15933 * Get all non-empty rows with as unicode text. 15934 */ 15935 CaptionScreen.prototype.getDisplayText = function (asOneRow) { 15936 asOneRow = asOneRow || false; 15937 var displayText = []; 15938 var text = ''; 15939 var rowNr = -1; 15940 for (var i = 0; i < NR_ROWS; i++) { 15941 var rowText = this.rows[i].getTextString(); 15942 if (rowText) { 15943 rowNr = i + 1; 15944 if (asOneRow) { 15945 displayText.push('Row ' + rowNr + ': \'' + rowText + '\''); 15946 } 15947 else { 15948 displayText.push(rowText.trim()); 15949 } 15950 } 15951 } 15952 if (displayText.length > 0) { 15953 if (asOneRow) { 15954 text = '[' + displayText.join(' | ') + ']'; 15955 } 15956 else { 15957 text = displayText.join('\n'); 15958 } 15959 } 15960 return text; 15961 }; 15962 CaptionScreen.prototype.getTextAndFormat = function () { 15963 return this.rows; 15964 }; 15965 return CaptionScreen; 15966}()); 15967// var modes = ['MODE_ROLL-UP', 'MODE_POP-ON', 'MODE_PAINT-ON', 'MODE_TEXT']; 15968var Cea608Channel = /** @class */ (function () { 15969 function Cea608Channel(channelNumber, outputFilter) { 15970 this.chNr = channelNumber; 15971 this.outputFilter = outputFilter; 15972 this.mode = null; 15973 this.verbose = 0; 15974 this.displayedMemory = new CaptionScreen(); 15975 this.nonDisplayedMemory = new CaptionScreen(); 15976 this.lastOutputScreen = new CaptionScreen(); 15977 this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1]; 15978 this.writeScreen = this.displayedMemory; 15979 this.mode = null; 15980 this.cueStartTime = null; // Keeps track of where a cue started. 15981 } 15982 Cea608Channel.prototype.reset = function () { 15983 this.mode = null; 15984 this.displayedMemory.reset(); 15985 this.nonDisplayedMemory.reset(); 15986 this.lastOutputScreen.reset(); 15987 this.currRollUpRow = this.displayedMemory.rows[NR_ROWS - 1]; 15988 this.writeScreen = this.displayedMemory; 15989 this.mode = null; 15990 this.cueStartTime = null; 15991 this.lastCueEndTime = null; 15992 }; 15993 Cea608Channel.prototype.getHandler = function () { 15994 return this.outputFilter; 15995 }; 15996 Cea608Channel.prototype.setHandler = function (newHandler) { 15997 this.outputFilter = newHandler; 15998 }; 15999 Cea608Channel.prototype.setPAC = function (pacData) { 16000 this.writeScreen.setPAC(pacData); 16001 }; 16002 Cea608Channel.prototype.setBkgData = function (bkgData) { 16003 this.writeScreen.setBkgData(bkgData); 16004 }; 16005 Cea608Channel.prototype.setMode = function (newMode) { 16006 if (newMode === this.mode) { 16007 return; 16008 } 16009 this.mode = newMode; 16010 logger.log('INFO', 'MODE=' + newMode); 16011 if (this.mode === 'MODE_POP-ON') { 16012 this.writeScreen = this.nonDisplayedMemory; 16013 } 16014 else { 16015 this.writeScreen = this.displayedMemory; 16016 this.writeScreen.reset(); 16017 } 16018 if (this.mode !== 'MODE_ROLL-UP') { 16019 this.displayedMemory.nrRollUpRows = null; 16020 this.nonDisplayedMemory.nrRollUpRows = null; 16021 } 16022 this.mode = newMode; 16023 }; 16024 Cea608Channel.prototype.insertChars = function (chars) { 16025 for (var i = 0; i < chars.length; i++) { 16026 this.writeScreen.insertChar(chars[i]); 16027 } 16028 var screen = this.writeScreen === this.displayedMemory ? 'DISP' : 'NON_DISP'; 16029 logger.log('INFO', screen + ': ' + this.writeScreen.getDisplayText(true)); 16030 if (this.mode === 'MODE_PAINT-ON' || this.mode === 'MODE_ROLL-UP') { 16031 logger.log('TEXT', 'DISPLAYED: ' + this.displayedMemory.getDisplayText(true)); 16032 this.outputDataUpdate(); 16033 } 16034 }; 16035 Cea608Channel.prototype.ccRCL = function () { 16036 logger.log('INFO', 'RCL - Resume Caption Loading'); 16037 this.setMode('MODE_POP-ON'); 16038 }; 16039 Cea608Channel.prototype.ccBS = function () { 16040 logger.log('INFO', 'BS - BackSpace'); 16041 if (this.mode === 'MODE_TEXT') { 16042 return; 16043 } 16044 this.writeScreen.backSpace(); 16045 if (this.writeScreen === this.displayedMemory) { 16046 this.outputDataUpdate(); 16047 } 16048 }; 16049 Cea608Channel.prototype.ccAOF = function () { 16050 }; 16051 Cea608Channel.prototype.ccAON = function () { 16052 }; 16053 Cea608Channel.prototype.ccDER = function () { 16054 logger.log('INFO', 'DER- Delete to End of Row'); 16055 this.writeScreen.clearToEndOfRow(); 16056 this.outputDataUpdate(); 16057 }; 16058 Cea608Channel.prototype.ccRU = function (nrRows) { 16059 logger.log('INFO', 'RU(' + nrRows + ') - Roll Up'); 16060 this.writeScreen = this.displayedMemory; 16061 this.setMode('MODE_ROLL-UP'); 16062 this.writeScreen.setRollUpRows(nrRows); 16063 }; 16064 Cea608Channel.prototype.ccFON = function () { 16065 logger.log('INFO', 'FON - Flash On'); 16066 this.writeScreen.setPen({ flash: true }); 16067 }; 16068 Cea608Channel.prototype.ccRDC = function () { 16069 logger.log('INFO', 'RDC - Resume Direct Captioning'); 16070 this.setMode('MODE_PAINT-ON'); 16071 }; 16072 Cea608Channel.prototype.ccTR = function () { 16073 logger.log('INFO', 'TR'); 16074 this.setMode('MODE_TEXT'); 16075 }; 16076 Cea608Channel.prototype.ccRTD = function () { 16077 logger.log('INFO', 'RTD'); 16078 this.setMode('MODE_TEXT'); 16079 }; 16080 Cea608Channel.prototype.ccEDM = function () { 16081 logger.log('INFO', 'EDM - Erase Displayed Memory'); 16082 this.displayedMemory.reset(); 16083 this.outputDataUpdate(true); 16084 }; 16085 Cea608Channel.prototype.ccCR = function () { 16086 logger.log('CR - Carriage Return'); 16087 this.writeScreen.rollUp(); 16088 this.outputDataUpdate(true); 16089 }; 16090 Cea608Channel.prototype.ccENM = function () { 16091 logger.log('INFO', 'ENM - Erase Non-displayed Memory'); 16092 this.nonDisplayedMemory.reset(); 16093 }; 16094 Cea608Channel.prototype.ccEOC = function () { 16095 logger.log('INFO', 'EOC - End Of Caption'); 16096 if (this.mode === 'MODE_POP-ON') { 16097 var tmp = this.displayedMemory; 16098 this.displayedMemory = this.nonDisplayedMemory; 16099 this.nonDisplayedMemory = tmp; 16100 this.writeScreen = this.nonDisplayedMemory; 16101 logger.log('TEXT', 'DISP: ' + this.displayedMemory.getDisplayText()); 16102 } 16103 this.outputDataUpdate(true); 16104 }; 16105 Cea608Channel.prototype.ccTO = function (nrCols) { 16106 logger.log('INFO', 'TO(' + nrCols + ') - Tab Offset'); 16107 this.writeScreen.moveCursor(nrCols); 16108 }; 16109 Cea608Channel.prototype.ccMIDROW = function (secondByte) { 16110 var styles = { flash: false }; 16111 styles.underline = secondByte % 2 === 1; 16112 styles.italics = secondByte >= 0x2e; 16113 if (!styles.italics) { 16114 var colorIndex = Math.floor(secondByte / 2) - 0x10; 16115 var colors = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta']; 16116 styles.foreground = colors[colorIndex]; 16117 } 16118 else { 16119 styles.foreground = 'white'; 16120 } 16121 logger.log('INFO', 'MIDROW: ' + JSON.stringify(styles)); 16122 this.writeScreen.setPen(styles); 16123 }; 16124 Cea608Channel.prototype.outputDataUpdate = function (dispatch) { 16125 if (dispatch === void 0) { dispatch = false; } 16126 var t = logger.time; 16127 if (t === null) { 16128 return; 16129 } 16130 if (this.outputFilter) { 16131 if (this.cueStartTime === null && !this.displayedMemory.isEmpty()) { // Start of a new cue 16132 this.cueStartTime = t; 16133 } 16134 else { 16135 if (!this.displayedMemory.equals(this.lastOutputScreen)) { 16136 if (this.outputFilter.newCue) { 16137 this.outputFilter.newCue(this.cueStartTime, t, this.lastOutputScreen); 16138 if (dispatch === true && this.outputFilter.dispatchCue) { 16139 this.outputFilter.dispatchCue(); 16140 } 16141 } 16142 this.cueStartTime = this.displayedMemory.isEmpty() ? null : t; 16143 } 16144 } 16145 this.lastOutputScreen.copy(this.displayedMemory); 16146 } 16147 }; 16148 Cea608Channel.prototype.cueSplitAtTime = function (t) { 16149 if (this.outputFilter) { 16150 if (!this.displayedMemory.isEmpty()) { 16151 if (this.outputFilter.newCue) { 16152 this.outputFilter.newCue(this.cueStartTime, t, this.displayedMemory); 16153 } 16154 this.cueStartTime = t; 16155 } 16156 } 16157 }; 16158 return Cea608Channel; 16159}()); 16160var Cea608Parser = /** @class */ (function () { 16161 function Cea608Parser(field, out1, out2) { 16162 this.field = field || 1; 16163 this.outputs = [out1, out2]; 16164 this.channels = [new Cea608Channel(1, out1), new Cea608Channel(2, out2)]; 16165 this.currChNr = -1; // Will be 1 or 2 16166 this.lastCmdA = null; // First byte of last command 16167 this.lastCmdB = null; // Second byte of last command 16168 this.bufferedData = []; 16169 this.startTime = null; 16170 this.lastTime = null; 16171 this.dataCounters = { 'padding': 0, 'char': 0, 'cmd': 0, 'other': 0 }; 16172 } 16173 Cea608Parser.prototype.getHandler = function (index) { 16174 return this.channels[index].getHandler(); 16175 }; 16176 Cea608Parser.prototype.setHandler = function (index, newHandler) { 16177 this.channels[index].setHandler(newHandler); 16178 }; 16179 /** 16180 * Add data for time t in forms of list of bytes (unsigned ints). The bytes are treated as pairs. 16181 */ 16182 Cea608Parser.prototype.addData = function (t, byteList) { 16183 var cmdFound, a, b, charsFound = false; 16184 this.lastTime = t; 16185 logger.setTime(t); 16186 for (var i = 0; i < byteList.length; i += 2) { 16187 a = byteList[i] & 0x7f; 16188 b = byteList[i + 1] & 0x7f; 16189 if (a === 0 && b === 0) { 16190 this.dataCounters.padding += 2; 16191 continue; 16192 } 16193 else { 16194 logger.log('DATA', '[' + numArrayToHexArray([byteList[i], byteList[i + 1]]) + '] -> (' + numArrayToHexArray([a, b]) + ')'); 16195 } 16196 cmdFound = this.parseCmd(a, b); 16197 if (!cmdFound) { 16198 cmdFound = this.parseMidrow(a, b); 16199 } 16200 if (!cmdFound) { 16201 cmdFound = this.parsePAC(a, b); 16202 } 16203 if (!cmdFound) { 16204 cmdFound = this.parseBackgroundAttributes(a, b); 16205 } 16206 if (!cmdFound) { 16207 charsFound = this.parseChars(a, b); 16208 if (charsFound) { 16209 if (this.currChNr && this.currChNr >= 0) { 16210 var channel = this.channels[this.currChNr - 1]; 16211 channel.insertChars(charsFound); 16212 } 16213 else { 16214 logger.log('WARNING', 'No channel found yet. TEXT-MODE?'); 16215 } 16216 } 16217 } 16218 if (cmdFound) { 16219 this.dataCounters.cmd += 2; 16220 } 16221 else if (charsFound) { 16222 this.dataCounters.char += 2; 16223 } 16224 else { 16225 this.dataCounters.other += 2; 16226 logger.log('WARNING', 'Couldn\'t parse cleaned data ' + numArrayToHexArray([a, b]) + 16227 ' orig: ' + numArrayToHexArray([byteList[i], byteList[i + 1]])); 16228 } 16229 } 16230 }; 16231 /** 16232 * Parse Command. 16233 * @returns {Boolean} Tells if a command was found 16234 */ 16235 Cea608Parser.prototype.parseCmd = function (a, b) { 16236 var chNr = null; 16237 var cond1 = (a === 0x14 || a === 0x1C) && (b >= 0x20 && b <= 0x2F); 16238 var cond2 = (a === 0x17 || a === 0x1F) && (b >= 0x21 && b <= 0x23); 16239 if (!(cond1 || cond2)) { 16240 return false; 16241 } 16242 if (a === this.lastCmdA && b === this.lastCmdB) { 16243 this.lastCmdA = null; 16244 this.lastCmdB = null; // Repeated commands are dropped (once) 16245 logger.log('DEBUG', 'Repeated command (' + numArrayToHexArray([a, b]) + ') is dropped'); 16246 return true; 16247 } 16248 if (a === 0x14 || a === 0x17) { 16249 chNr = 1; 16250 } 16251 else { 16252 chNr = 2; 16253 } // (a === 0x1C || a=== 0x1f) 16254 var channel = this.channels[chNr - 1]; 16255 if (a === 0x14 || a === 0x1C) { 16256 if (b === 0x20) { 16257 channel.ccRCL(); 16258 } 16259 else if (b === 0x21) { 16260 channel.ccBS(); 16261 } 16262 else if (b === 0x22) { 16263 channel.ccAOF(); 16264 } 16265 else if (b === 0x23) { 16266 channel.ccAON(); 16267 } 16268 else if (b === 0x24) { 16269 channel.ccDER(); 16270 } 16271 else if (b === 0x25) { 16272 channel.ccRU(2); 16273 } 16274 else if (b === 0x26) { 16275 channel.ccRU(3); 16276 } 16277 else if (b === 0x27) { 16278 channel.ccRU(4); 16279 } 16280 else if (b === 0x28) { 16281 channel.ccFON(); 16282 } 16283 else if (b === 0x29) { 16284 channel.ccRDC(); 16285 } 16286 else if (b === 0x2A) { 16287 channel.ccTR(); 16288 } 16289 else if (b === 0x2B) { 16290 channel.ccRTD(); 16291 } 16292 else if (b === 0x2C) { 16293 channel.ccEDM(); 16294 } 16295 else if (b === 0x2D) { 16296 channel.ccCR(); 16297 } 16298 else if (b === 0x2E) { 16299 channel.ccENM(); 16300 } 16301 else if (b === 0x2F) { 16302 channel.ccEOC(); 16303 } 16304 } 16305 else { // a == 0x17 || a == 0x1F 16306 channel.ccTO(b - 0x20); 16307 } 16308 this.lastCmdA = a; 16309 this.lastCmdB = b; 16310 this.currChNr = chNr; 16311 return true; 16312 }; 16313 /** 16314 * Parse midrow styling command 16315 * @returns {Boolean} 16316 */ 16317 Cea608Parser.prototype.parseMidrow = function (a, b) { 16318 var chNr = null; 16319 if (((a === 0x11) || (a === 0x19)) && b >= 0x20 && b <= 0x2f) { 16320 if (a === 0x11) { 16321 chNr = 1; 16322 } 16323 else { 16324 chNr = 2; 16325 } 16326 if (chNr !== this.currChNr) { 16327 logger.log('ERROR', 'Mismatch channel in midrow parsing'); 16328 return false;
16329 } 16330 var channel = this.channels[chNr - 1]; 16331 channel.ccMIDROW(b); 16332 logger.log('DEBUG', 'MIDROW (' + numArrayToHexArray([a, b]) + ')'); 16333 return true; 16334 } 16335 return false; 16336 }; 16337 /** 16338 * Parse Preable Access Codes (Table 53). 16339 * @returns {Boolean} Tells if PAC found 16340 */ 16341 Cea608Parser.prototype.parsePAC = function (a, b) { 16342 var chNr = null; 16343 var row = null; 16344 var case1 = ((a >= 0x11 && a <= 0x17) || (a >= 0x19 && a <= 0x1F)) && (b >= 0x40 && b <= 0x7F); 16345 var case2 = (a === 0x10 || a === 0x18) && (b >= 0x40 && b <= 0x5F); 16346 if (!(case1 || case2)) { 16347 return false; 16348 } 16349 if (a === this.lastCmdA && b === this.lastCmdB) { 16350 this.lastCmdA = null; 16351 this.lastCmdB = null; 16352 return true; // Repeated commands are dropped (once) 16353 } 16354 chNr = (a <= 0x17) ? 1 : 2; 16355 if (b >= 0x40 && b <= 0x5F) { 16356 row = (chNr === 1) ? rowsLowCh1[a] : rowsLowCh2[a]; 16357 } 16358 else { // 0x60 <= b <= 0x7F 16359 row = (chNr === 1) ? rowsHighCh1[a] : rowsHighCh2[a]; 16360 } 16361 var pacData = this.interpretPAC(row, b); 16362 var channel = this.channels[chNr - 1]; 16363 channel.setPAC(pacData); 16364 this.lastCmdA = a; 16365 this.lastCmdB = b; 16366 this.currChNr = chNr; 16367 return true; 16368 }; 16369 /** 16370 * Interpret the second byte of the pac, and return the information. 16371 * @returns {Object} pacData with style parameters. 16372 */ 16373 Cea608Parser.prototype.interpretPAC = function (row, byte) { 16374 var pacIndex = byte; 16375 var pacData = { color: null, italics: false, indent: null, underline: false, row: row }; 16376 if (byte > 0x5F) { 16377 pacIndex = byte - 0x60; 16378 } 16379 else { 16380 pacIndex = byte - 0x40; 16381 } 16382 pacData.underline = (pacIndex & 1) === 1; 16383 if (pacIndex <= 0xd) { 16384 pacData.color = ['white', 'green', 'blue', 'cyan', 'red', 'yellow', 'magenta', 'white'][Math.floor(pacIndex / 2)]; 16385 } 16386 else if (pacIndex <= 0xf) { 16387 pacData.italics = true; 16388 pacData.color = 'white'; 16389 } 16390 else { 16391 pacData.indent = (Math.floor((pacIndex - 0x10) / 2)) * 4; 16392 } 16393 return pacData; // Note that row has zero offset. The spec uses 1. 16394 }; 16395 /** 16396 * Parse characters. 16397 * @returns An array with 1 to 2 codes corresponding to chars, if found. null otherwise. 16398 */ 16399 Cea608Parser.prototype.parseChars = function (a, b) { 16400 var channelNr = null, charCodes = null, charCode1 = null; 16401 if (a >= 0x19) { 16402 channelNr = 2; 16403 charCode1 = a - 8; 16404 } 16405 else { 16406 channelNr = 1; 16407 charCode1 = a; 16408 } 16409 if (charCode1 >= 0x11 && charCode1 <= 0x13) { 16410 // Special character 16411 var oneCode = b; 16412 if (charCode1 === 0x11) { 16413 oneCode = b + 0x50; 16414 } 16415 else if (charCode1 === 0x12) { 16416 oneCode = b + 0x70; 16417 } 16418 else { 16419 oneCode = b + 0x90; 16420 } 16421 logger.log('INFO', 'Special char \'' + getCharForByte(oneCode) + '\' in channel ' + channelNr); 16422 charCodes = [oneCode]; 16423 } 16424 else if (a >= 0x20 && a <= 0x7f) { 16425 charCodes = (b === 0) ? [a] : [a, b]; 16426 } 16427 if (charCodes) { 16428 var hexCodes = numArrayToHexArray(charCodes); 16429 logger.log('DEBUG', 'Char codes = ' + hexCodes.join(',')); 16430 this.lastCmdA = null; 16431 this.lastCmdB = null; 16432 } 16433 return charCodes; 16434 }; 16435 /** 16436 * Parse extended background attributes as well as new foreground color black. 16437 * @returns{Boolean} Tells if background attributes are found 16438 */ 16439 Cea608Parser.prototype.parseBackgroundAttributes = function (a, b) { 16440 var bkgData, index, chNr, channel; 16441 var case1 = (a === 0x10 || a === 0x18) && (b >= 0x20 && b <= 0x2f); 16442 var case2 = (a === 0x17 || a === 0x1f) && (b >= 0x2d && b <= 0x2f); 16443 if (!(case1 || case2)) { 16444 return false;
vendor: 1,518 bytes, lines 16445-16498
16445 } 16446 bkgData = {}; 16447 if (a === 0x10 || a === 0x18) { 16448 index = Math.floor((b - 0x20) / 2); 16449 bkgData.background = backgroundColors[index]; 16450 if (b % 2 === 1) { 16451 bkgData.background = bkgData.background + '_semi'; 16452 } 16453 } 16454 else if (b === 0x2d) { 16455 bkgData.background = 'transparent'; 16456 } 16457 else { 16458 bkgData.foreground = 'black'; 16459 if (b === 0x2f) { 16460 bkgData.underline = true; 16461 } 16462 } 16463 chNr = (a < 0x18) ? 1 : 2; 16464 channel = this.channels[chNr - 1]; 16465 channel.setBkgData(bkgData); 16466 this.lastCmdA = null; 16467 this.lastCmdB = null; 16468 return true; 16469 }; 16470 /** 16471 * Reset state of parser and its channels. 16472 */ 16473 Cea608Parser.prototype.reset = function () { 16474 for (var i = 0; i < this.channels.length; i++) { 16475 if (this.channels[i]) { 16476 this.channels[i].reset(); 16477 } 16478 } 16479 this.lastCmdA = null; 16480 this.lastCmdB = null; 16481 }; 16482 /** 16483 * Trigger the generation of a cue, and the start of a new one if displayScreens are not empty. 16484 */ 16485 Cea608Parser.prototype.cueSplitAtTime = function (t) { 16486 for (var i = 0; i < this.channels.length; i++) { 16487 if (this.channels[i]) { 16488 this.channels[i].cueSplitAtTime(t); 16489 } 16490 } 16491 }; 16492 return Cea608Parser; 16493}()); 16494exports.default = Cea608Parser; 16495 16496 16497/***/ }), 16498
16499/***/ "./src/utils/codecs.js": 16500/*!*****************************!*\ 16501 !*** ./src/utils/codecs.js ***! 16502 \*****************************/ 16503/*! no static exports found */ 16504/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 16505/***/ (function(module, exports, __webpack_require__) { 16506 16507"use strict"; 16508 16509Object.defineProperty(exports, "__esModule", { value: true }); 16510// from http://mp4ra.org/codecs.html 16511var sampleEntryCodesISO = { 16512 audio: { 16513 'a3ds': true, 16514 'ac-3': true, 16515 'ac-4': true, 16516 'alac': true, 16517 'alaw': true, 16518 'dra1': true, 16519 'dts+': true, 16520 'dts-': true, 16521 'dtsc': true, 16522 'dtse': true, 16523 'dtsh': true, 16524 'ec-3': true, 16525 'enca': true, 16526 'g719': true, 16527 'g726': true, 16528 'm4ae': true, 16529 'mha1': true, 16530 'mha2': true, 16531 'mhm1': true, 16532 'mhm2': true, 16533 'mlpa': true, 16534 'mp4a': true, 16535 'raw ': true, 16536 'Opus': true, 16537 'samr': true, 16538 'sawb': true, 16539 'sawp': true, 16540 'sevc': true, 16541 'sqcp': true, 16542 'ssmv': true, 16543 'twos': true, 16544 'ulaw': true 16545 }, 16546 video: { 16547 'avc1': true, 16548 'avc2': true, 16549 'avc3': true, 16550 'avc4': true, 16551 'avcp': true, 16552 'drac': true, 16553 'dvav': true, 16554 'dvhe': true, 16555 'encv': true, 16556 'hev1': true, 16557 'hvc1': true, 16558 'mjp2': true, 16559 'mp4v': true, 16560 'mvc1': true, 16561 'mvc2': true, 16562 'mvc3': true, 16563 'mvc4': true, 16564 'resv': true, 16565 'rv60': true, 16566 's263': true, 16567 'svc1': true, 16568 'svc2': true, 16569 'vc-1': true, 16570 'vp08': true, 16571 'vp09': true 16572 } 16573}; 16574function isCodecType(codec, type) { 16575 var typeCodes = sampleEntryCodesISO[type]; 16576 return !!typeCodes && typeCodes[codec.slice(0, 4)] === true; 16577} 16578exports.isCodecType = isCodecType; 16579function isCodecSupportedInMp4(codec, type) { 16580 return window.MediaSource.isTypeSupported((type || 'video') + "/mp4;codecs=\"" + codec + "\""); 16581} 16582exports.isCodecSupportedInMp4 = isCodecSupportedInMp4; 16583 16584 16585/***/ }), 16586
16587/***/ "./src/utils/cues.js": 16588/*!***************************!*\ 16589 !*** ./src/utils/cues.js ***! 16590 \***************************/ 16591/*! no static exports found */ 16592/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 16593/***/ (function(module, exports, __webpack_require__) { 16594 16595"use strict"; 16596 16597Object.defineProperty(exports, "__esModule", { value: true }); 16598var vttparser_1 = __webpack_require__(/*! ./vttparser */ "./src/utils/vttparser.js"); 16599function newCue(track, startTime, endTime, captionScreen) { 16600 var row; 16601 var cue; 16602 var indenting; 16603 var indent; 16604 var text; 16605 var VTTCue = window.VTTCue || window.TextTrackCue; 16606 for (var r = 0; r < captionScreen.rows.length; r++) { 16607 row = captionScreen.rows[r]; 16608 indenting = true; 16609 indent = 0; 16610 text = ''; 16611 if (!row.isEmpty()) { 16612 for (var c = 0; c < row.chars.length; c++) { 16613 if (row.chars[c].uchar.match(/\s/) && indenting) { 16614 indent++; 16615 } 16616 else { 16617 text += row.chars[c].uchar; 16618 indenting = false; 16619 } 16620 } 16621 // To be used for cleaning-up orphaned roll-up captions 16622 row.cueStartTime = startTime; 16623 // Give a slight bump to the endTime if it's equal to startTime to avoid a SyntaxError in IE 16624 if (startTime === endTime) { 16625 endTime += 0.0001; 16626 } 16627 cue = new VTTCue(startTime, endTime, vttparser_1.fixLineBreaks(text.trim())); 16628 if (indent >= 16) { 16629 indent--; 16630 } 16631 else { 16632 indent++; 16633 } 16634 // VTTCue.line get's flakey when using controls, so let's now include line 13&14 16635 // also, drop line 1 since it's to close to the top 16636 if (navigator.userAgent.match(/Firefox\//)) { 16637 cue.line = r + 1; 16638 } 16639 else { 16640 cue.line = (r > 7 ? r - 2 : r + 1); 16641 } 16642 cue.align = 'left'; 16643 // Clamp the position between 0 and 100 - if out of these bounds, Firefox throws an exception and captions break 16644 cue.position = Math.max(0, Math.min(100, 100 * (indent / 32) + (navigator.userAgent.match(/Firefox\//) ? 50 : 0))); 16645 track.addCue(cue); 16646 } 16647 } 16648} 16649exports.newCue = newCue; 16650 16651 16652/***/ }), 16653
16654/***/ "./src/utils/discontinuities.js": 16655/*!**************************************!*\ 16656 !*** ./src/utils/discontinuities.js ***! 16657 \**************************************/ 16658/*! no static exports found */ 16659/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 16660/***/ (function(module, exports, __webpack_require__) { 16661 16662"use strict"; 16663/* WEBPACK VAR INJECTION */(function(Number) { 16664Object.defineProperty(exports, "__esModule", { value: true }); 16665var binary_search_1 = __webpack_require__(/*! ./binary-search */ "./src/utils/binary-search.js"); 16666var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 16667function findFirstFragWithCC(fragments, cc) { 16668 var firstFrag = null; 16669 for (var i = 0; i < fragments.length; i += 1) { 16670 var currentFrag = fragments[i]; 16671 if (currentFrag && currentFrag.cc === cc) { 16672 firstFrag = currentFrag; 16673 break; 16674 } 16675 } 16676 return firstFrag; 16677} 16678exports.findFirstFragWithCC = findFirstFragWithCC; 16679function findFragWithCC(fragments, CC) { 16680 return binary_search_1.default.search(fragments, function (candidate) { 16681 if (candidate.cc < CC) { 16682 return 1; 16683 } 16684 else if (candidate.cc > CC) { 16685 return -1; 16686 } 16687 else { 16688 return 0; 16689 } 16690 }); 16691} 16692exports.findFragWithCC = findFragWithCC; 16693function shouldAlignOnDiscontinuities(lastFrag, lastLevel, details) { 16694 var shouldAlign = false; 16695 if (lastLevel && lastLevel.details && details) { 16696 if (details.endCC > details.startCC || (lastFrag && lastFrag.cc < details.startCC)) { 16697 shouldAlign = true; 16698 } 16699 } 16700 return shouldAlign; 16701} 16702exports.shouldAlignOnDiscontinuities = shouldAlignOnDiscontinuities; 16703// Find the first frag in the previous level which matches the CC of the first frag of the new level 16704function findDiscontinuousReferenceFrag(prevDetails, curDetails) { 16705 var prevFrags = prevDetails.fragments; 16706 var curFrags = curDetails.fragments; 16707 if (!curFrags.length || !prevFrags.length) { 16708 logger_1.logger.log('No fragments to align'); 16709 return; 16710 } 16711 var prevStartFrag = findFirstFragWithCC(prevFrags, curFrags[0].cc); 16712 if (!prevStartFrag || (prevStartFrag && !prevStartFrag.startPTS)) { 16713 logger_1.logger.log('No frag in previous level to align on'); 16714 return; 16715 } 16716 return prevStartFrag; 16717} 16718exports.findDiscontinuousReferenceFrag = findDiscontinuousReferenceFrag; 16719function adjustPts(sliding, details) { 16720 details.fragments.forEach(function (frag) { 16721 if (frag) { 16722 var start = frag.start + sliding; 16723 frag.start = frag.startPTS = start; 16724 frag.endPTS = start + frag.duration; 16725 } 16726 }); 16727 details.PTSKnown = true; 16728} 16729exports.adjustPts = adjustPts; 16730/** 16731 * Using the parameters of the last level, this function computes PTS' of the new fragments so that they form a 16732 * contiguous stream with the last fragments. 16733 * The PTS of a fragment lets Hls.js know where it fits into a stream - by knowing every PTS, we know which fragment to 16734 * download at any given time. PTS is normally computed when the fragment is demuxed, so taking this step saves us time 16735 * and an extra download. 16736 * @param lastFrag 16737 * @param lastLevel 16738 * @param details 16739 */ 16740function alignStream(lastFrag, lastLevel, details) { 16741 alignDiscontinuities(lastFrag, details, lastLevel); 16742 if (!details.PTSKnown && lastLevel) { 16743 // If the PTS wasn't figured out via discontinuity sequence that means there was no CC increase within the level. 16744 // Aligning via Program Date Time should therefore be reliable, since PDT should be the same within the same 16745 // discontinuity sequence. 16746 alignPDT(details, lastLevel.details); 16747 } 16748} 16749exports.alignStream = alignStream; 16750/** 16751 * Computes the PTS if a new level's fragments using the PTS of a fragment in the last level which shares the same 16752 * discontinuity sequence. 16753 * @param lastLevel - The details of the last loaded level 16754 * @param details - The details of the new level 16755 */ 16756function alignDiscontinuities(lastFrag, details, lastLevel) { 16757 if (shouldAlignOnDiscontinuities(lastFrag, lastLevel, details)) { 16758 var referenceFrag = findDiscontinuousReferenceFrag(lastLevel.details, details); 16759 if (referenceFrag) { 16760 logger_1.logger.log('Adjusting PTS using last level due to CC increase within current level'); 16761 adjustPts(referenceFrag.start, details); 16762 } 16763 } 16764} 16765exports.alignDiscontinuities = alignDiscontinuities; 16766/** 16767 * Computes the PTS of a new level's fragments using the difference in Program Date Time from the last level. 16768 * @param details - The details of the new level 16769 * @param lastDetails - The details of the last loaded level 16770 */ 16771function alignPDT(details, lastDetails) { 16772 if (lastDetails && lastDetails.fragments.length) {
16773 if (!details.hasProgramDateTime || !lastDetails.hasProgramDateTime) { 16774 return; 16775 } 16776 // if last level sliding is 1000 and its first frag PROGRAM-DATE-TIME is 2017-08-20 1:10:00 AM 16777 // and if new details first frag PROGRAM DATE-TIME is 2017-08-20 1:10:08 AM 16778 // then we can deduce that playlist B sliding is 1000+8 = 1008s 16779 var lastPDT = lastDetails.fragments[0].programDateTime; 16780 var newPDT = details.fragments[0].programDateTime; 16781 // date diff is in ms. frag.start is in seconds 16782 var sliding = (newPDT - lastPDT) / 1000 + lastDetails.fragments[0].start; 16783 if (Number.isFinite(sliding)) { 16784 logger_1.logger.log("adjusting PTS using programDateTime delta, sliding:" + sliding.toFixed(3)); 16785 adjustPts(sliding, details); 16786 } 16787 } 16788} 16789exports.alignPDT = alignPDT; 16790 16791/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 16792 16793/***/ }), 16794
16795/***/ "./src/utils/ewma-bandwidth-estimator.js": 16796/*!***********************************************!*\ 16797 !*** ./src/utils/ewma-bandwidth-estimator.js ***! 16798 \***********************************************/ 16799/*! no static exports found */ 16800/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 16801/***/ (function(module, exports, __webpack_require__) { 16802 16803"use strict"; 16804 16805/* 16806 * EWMA Bandwidth Estimator 16807 * - heavily inspired from shaka-player 16808 * Tracks bandwidth samples and estimates available bandwidth. 16809 * Based on the minimum of two exponentially-weighted moving averages with 16810 * different half-lives. 16811 */ 16812Object.defineProperty(exports, "__esModule", { value: true }); 16813var ewma_1 = __webpack_require__(/*! ../utils/ewma */ "./src/utils/ewma.js"); 16814var EwmaBandWidthEstimator = /** @class */ (function () { 16815 function EwmaBandWidthEstimator(hls, slow, fast, defaultEstimate) { 16816 this.hls = hls; 16817 this.defaultEstimate_ = defaultEstimate; 16818 this.minWeight_ = 0.001; 16819 this.minDelayMs_ = 50; 16820 this.slow_ = new ewma_1.default(slow); 16821 this.fast_ = new ewma_1.default(fast); 16822 } 16823 EwmaBandWidthEstimator.prototype.sample = function (durationMs, numBytes) { 16824 durationMs = Math.max(durationMs, this.minDelayMs_); 16825 var bandwidth = 8000 * numBytes / durationMs, 16826 // console.log('instant bw:'+ Math.round(bandwidth)); 16827 // we weight sample using loading duration.... 16828 weight = durationMs / 1000; 16829 this.fast_.sample(weight, bandwidth); 16830 this.slow_.sample(weight, bandwidth); 16831 }; 16832 EwmaBandWidthEstimator.prototype.canEstimate = function () { 16833 var fast = this.fast_; 16834 return (fast && fast.getTotalWeight() >= this.minWeight_); 16835 }; 16836 EwmaBandWidthEstimator.prototype.getEstimate = function () { 16837 if (this.canEstimate()) { 16838 // console.log('slow estimate:'+ Math.round(this.slow_.getEstimate())); 16839 // console.log('fast estimate:'+ Math.round(this.fast_.getEstimate())); 16840 // Take the minimum of these two estimates. This should have the effect of 16841 // adapting down quickly, but up more slowly. 16842 return Math.min(this.fast_.getEstimate(), this.slow_.getEstimate()); 16843 } 16844 else { 16845 return this.defaultEstimate_; 16846 } 16847 }; 16848 EwmaBandWidthEstimator.prototype.destroy = function () { 16849 }; 16850 return EwmaBandWidthEstimator; 16851}()); 16852exports.default = EwmaBandWidthEstimator; 16853 16854 16855/***/ }), 16856
vendor: 1,566 bytes, lines 16857-16904
16857/***/ "./src/utils/ewma.js": 16858/*!***************************!*\ 16859 !*** ./src/utils/ewma.js ***! 16860 \***************************/ 16861/*! no static exports found */ 16862/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 16863/***/ (function(module, exports, __webpack_require__) { 16864 16865"use strict"; 16866 16867/* 16868 * compute an Exponential Weighted moving average 16869 * - https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average 16870 * - heavily inspired from shaka-player 16871 */ 16872Object.defineProperty(exports, "__esModule", { value: true }); 16873var EWMA = /** @class */ (function () { 16874 // About half of the estimated value will be from the last |halfLife| samples by weight. 16875 function EWMA(halfLife) { 16876 // Larger values of alpha expire historical data more slowly. 16877 this.alpha_ = halfLife ? Math.exp(Math.log(0.5) / halfLife) : 0; 16878 this.estimate_ = 0; 16879 this.totalWeight_ = 0; 16880 } 16881 EWMA.prototype.sample = function (weight, value) { 16882 var adjAlpha = Math.pow(this.alpha_, weight); 16883 this.estimate_ = value * (1 - adjAlpha) + adjAlpha * this.estimate_; 16884 this.totalWeight_ += weight; 16885 }; 16886 EWMA.prototype.getTotalWeight = function () { 16887 return this.totalWeight_; 16888 }; 16889 EWMA.prototype.getEstimate = function () { 16890 if (this.alpha_) { 16891 var zeroFactor = 1 - Math.pow(this.alpha_, this.totalWeight_); 16892 return this.estimate_ / zeroFactor; 16893 } 16894 else { 16895 return this.estimate_; 16896 } 16897 }; 16898 return EWMA; 16899}()); 16900exports.default = EWMA; 16901 16902 16903/***/ }), 16904
16905/***/ "./src/utils/get-self-scope.js": 16906/*!*************************************!*\ 16907 !*** ./src/utils/get-self-scope.js ***! 16908 \*************************************/ 16909/*! no static exports found */ 16910/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 16911/***/ (function(module, exports, __webpack_require__) { 16912 16913"use strict"; 16914 16915Object.defineProperty(exports, "__esModule", { value: true }); 16916function getSelfScope() { 16917 // see https://stackoverflow.com/a/11237259/589493 16918 if (typeof window === 'undefined') { 16919 /* eslint-disable-next-line no-undef */ 16920 return self; 16921 } 16922 else { 16923 return window; 16924 } 16925} 16926exports.getSelfScope = getSelfScope; 16927 16928 16929/***/ }), 16930
vendor: 2,497 bytes, lines 16931-17014
16931/***/ "./src/utils/logger.js": 16932/*!*****************************!*\ 16933 !*** ./src/utils/logger.js ***! 16934 \*****************************/ 16935/*! no static exports found */ 16936/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 16937/***/ (function(module, exports, __webpack_require__) { 16938 16939"use strict"; 16940 16941Object.defineProperty(exports, "__esModule", { value: true }); 16942var get_self_scope_1 = __webpack_require__(/*! ./get-self-scope */ "./src/utils/get-self-scope.js"); 16943function noop() { } 16944var fakeLogger = { 16945 trace: noop, 16946 debug: noop, 16947 log: noop, 16948 warn: noop, 16949 info: noop, 16950 error: noop 16951}; 16952var exportedLogger = fakeLogger; 16953// let lastCallTime; 16954// function formatMsgWithTimeInfo(type, msg) { 16955// const now = Date.now(); 16956// const diff = lastCallTime ? '+' + (now - lastCallTime) : '0'; 16957// lastCallTime = now; 16958// msg = (new Date(now)).toISOString() + ' | [' + type + '] > ' + msg + ' ( ' + diff + ' ms )'; 16959// return msg; 16960// } 16961function formatMsg(type, msg) { 16962 msg = '[' + type + '] > ' + msg; 16963 return msg; 16964} 16965var global = get_self_scope_1.getSelfScope(); 16966function consolePrintFn(type) { 16967 var func = global.console[type]; 16968 if (func) { 16969 return function () { 16970 var args = []; 16971 for (var _i = 0; _i < arguments.length; _i++) { 16972 args[_i] = arguments[_i]; 16973 } 16974 if (args[0]) { 16975 args[0] = formatMsg(type, args[0]); 16976 } 16977 func.apply(global.console, args); 16978 }; 16979 } 16980 return noop; 16981} 16982function exportLoggerFunctions(debugConfig) { 16983 var functions = []; 16984 for (var _i = 1; _i < arguments.length; _i++) { 16985 functions[_i - 1] = arguments[_i]; 16986 } 16987 functions.forEach(function (type) { 16988 exportedLogger[type] = debugConfig[type] ? debugConfig[type].bind(debugConfig) : consolePrintFn(type); 16989 }); 16990} 16991exports.enableLogs = function (debugConfig) { 16992 if (debugConfig === true || typeof debugConfig === 'object') { 16993 exportLoggerFunctions(debugConfig, 16994 // Remove out from list here to hard-disable a log-level 16995 // 'trace', 16996 'debug', 'log', 'info', 'warn', 'error'); 16997 // Some browsers don't allow to use bind on console object anyway 16998 // fallback to default if needed 16999 try { 17000 exportedLogger.log(); 17001 } 17002 catch (e) { 17003 exportedLogger = fakeLogger; 17004 } 17005 } 17006 else { 17007 exportedLogger = fakeLogger; 17008 } 17009}; 17010exports.logger = exportedLogger; 17011 17012 17013/***/ }), 17014
17015/***/ "./src/utils/mediakeys-helper.js": 17016/*!***************************************!*\ 17017 !*** ./src/utils/mediakeys-helper.js ***! 17018 \***************************************/ 17019/*! no static exports found */ 17020/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 17021/***/ (function(module, exports, __webpack_require__) { 17022 17023"use strict"; 17024 17025Object.defineProperty(exports, "__esModule", { value: true }); 17026var requestMediaKeySystemAccess = (function () { 17027 if (typeof window !== 'undefined' && window.navigator && window.navigator.requestMediaKeySystemAccess) { 17028 return window.navigator.requestMediaKeySystemAccess.bind(window.navigator); 17029 } 17030 else { 17031 return null; 17032 } 17033})(); 17034exports.requestMediaKeySystemAccess = requestMediaKeySystemAccess; 17035 17036 17037/***/ }), 17038
17039/***/ "./src/utils/mediasource-helper.js": 17040/*!*****************************************!*\ 17041 !*** ./src/utils/mediasource-helper.js ***! 17042 \*****************************************/ 17043/*! no static exports found */ 17044/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 17045/***/ (function(module, exports, __webpack_require__) { 17046 17047"use strict"; 17048 17049/** 17050 * MediaSource helper 17051 */ 17052Object.defineProperty(exports, "__esModule", { value: true }); 17053function getMediaSource() { 17054 if (typeof window !== 'undefined') { 17055 return window.MediaSource || window.WebKitMediaSource; 17056 } 17057} 17058exports.getMediaSource = getMediaSource; 17059 17060 17061/***/ }), 17062
17063/***/ "./src/utils/output-filter.js": 17064/*!************************************!*\ 17065 !*** ./src/utils/output-filter.js ***! 17066 \************************************/ 17067/*! no static exports found */ 17068/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 17069/***/ (function(module, exports, __webpack_require__) { 17070 17071"use strict"; 17072 17073Object.defineProperty(exports, "__esModule", { value: true }); 17074var OutputFilter = /** @class */ (function () { 17075 function OutputFilter(timelineController, trackName) { 17076 this.timelineController = timelineController; 17077 this.trackName = trackName; 17078 this.startTime = null; 17079 this.endTime = null; 17080 this.screen = null; 17081 } 17082 OutputFilter.prototype.dispatchCue = function () { 17083 if (this.startTime === null) { 17084 return; 17085 } 17086 this.timelineController.addCues(this.trackName, this.startTime, this.endTime, this.screen); 17087 this.startTime = null; 17088 }; 17089 OutputFilter.prototype.newCue = function (startTime, endTime, screen) { 17090 if (this.startTime === null || this.startTime > startTime) { 17091 this.startTime = startTime; 17092 } 17093 this.endTime = endTime; 17094 this.screen = screen; 17095 this.timelineController.createCaptionsTrack(this.trackName); 17096 }; 17097 return OutputFilter; 17098}()); 17099exports.default = OutputFilter; 17100 17101 17102/***/ }), 17103
17104/***/ "./src/utils/texttrack-utils.js": 17105/*!**************************************!*\ 17106 !*** ./src/utils/texttrack-utils.js ***! 17107 \**************************************/ 17108/*! no static exports found */ 17109/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 17110/***/ (function(module, exports, __webpack_require__) { 17111 17112"use strict"; 17113 17114Object.defineProperty(exports, "__esModule", { value: true }); 17115function sendAddTrackEvent(track, videoEl) { 17116 var event = null; 17117 try { 17118 event = new window.Event('addtrack'); 17119 } 17120 catch (err) { 17121 // for IE11 17122 event = document.createEvent('Event'); 17123 event.initEvent('addtrack', false, false); 17124 } 17125 event.track = track; 17126 videoEl.dispatchEvent(event); 17127} 17128exports.sendAddTrackEvent = sendAddTrackEvent; 17129function clearCurrentCues(track) { 17130 if (track && track.cues) { 17131 while (track.cues.length > 0) { 17132 track.removeCue(track.cues[0]); 17133 } 17134 } 17135} 17136exports.clearCurrentCues = clearCurrentCues; 17137 17138 17139/***/ }), 17140
17141/***/ "./src/utils/time-ranges.js": 17142/*!**********************************!*\ 17143 !*** ./src/utils/time-ranges.js ***! 17144 \**********************************/ 17145/*! no static exports found */ 17146/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 17147/***/ (function(module, exports, __webpack_require__) { 17148 17149"use strict"; 17150 17151/** 17152 * TimeRanges to string helper 17153 */ 17154Object.defineProperty(exports, "__esModule", { value: true }); 17155var TimeRanges = { 17156 toString: function (r) { 17157 var log = '', len = r.length; 17158 for (var i = 0; i < len; i++) { 17159 log += '[' + r.start(i).toFixed(3) + ',' + r.end(i).toFixed(3) + ']'; 17160 } 17161 return log; 17162 } 17163}; 17164exports.default = TimeRanges; 17165 17166 17167/***/ }), 17168
vendor: 6,870 bytes, lines 17169-17374
17169/***/ "./src/utils/vttcue.js": 17170/*!*****************************!*\ 17171 !*** ./src/utils/vttcue.js ***! 17172 \*****************************/ 17173/*! no static exports found */ 17174/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 17175/***/ (function(module, exports, __webpack_require__) { 17176 17177"use strict"; 17178 17179/** 17180 * Copyright 2013 vtt.js Contributors 17181 * 17182 * Licensed under the Apache License, Version 2.0 (the "License"); 17183 * you may not use this file except in compliance with the License. 17184 * You may obtain a copy of the License at 17185 * 17186 * http://www.apache.org/licenses/LICENSE-2.0 17187 * 17188 * Unless required by applicable law or agreed to in writing, software 17189 * distributed under the License is distributed on an "AS IS" BASIS, 17190 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17191 * See the License for the specific language governing permissions and 17192 * limitations under the License. 17193 */ 17194Object.defineProperty(exports, "__esModule", { value: true }); 17195exports.default = (function () { 17196 if (typeof window !== 'undefined' && window.VTTCue) { 17197 return window.VTTCue; 17198 } 17199 var autoKeyword = 'auto'; 17200 var directionSetting = { 17201 '': true, 17202 lr: true, 17203 rl: true 17204 }; 17205 var alignSetting = { 17206 start: true, 17207 middle: true, 17208 end: true, 17209 left: true, 17210 right: true 17211 }; 17212 function findDirectionSetting(value) { 17213 if (typeof value !== 'string') { 17214 return false; 17215 } 17216 var dir = directionSetting[value.toLowerCase()]; 17217 return dir ? value.toLowerCase() : false; 17218 } 17219 function findAlignSetting(value) { 17220 if (typeof value !== 'string') { 17221 return false; 17222 } 17223 var align = alignSetting[value.toLowerCase()]; 17224 return align ? value.toLowerCase() : false; 17225 } 17226 function extend(obj) { 17227 var i = 1; 17228 for (; i < arguments.length; i++) { 17229 var cobj = arguments[i]; 17230 for (var p in cobj) { 17231 obj[p] = cobj[p]; 17232 } 17233 } 17234 return obj; 17235 } 17236 function VTTCue(startTime, endTime, text) { 17237 var cue = this; 17238 var isIE8 = (function () { 17239 if (typeof navigator === 'undefined') { 17240 return; 17241 } 17242 return (/MSIE\s8\.0/).test(navigator.userAgent); 17243 })(); 17244 var baseObj = {}; 17245 if (isIE8) { 17246 cue = document.createElement('custom'); 17247 } 17248 else { 17249 baseObj.enumerable = true; 17250 } 17251 /** 17252 * Shim implementation specific properties. These properties are not in 17253 * the spec. 17254 */ 17255 // Lets us know when the VTTCue's data has changed in such a way that we need 17256 // to recompute its display state. This lets us compute its display state 17257 // lazily. 17258 cue.hasBeenReset = false; 17259 /** 17260 * VTTCue and TextTrackCue properties 17261 * http://dev.w3.org/html5/webvtt/#vttcue-interface 17262 */ 17263 var _id = ''; 17264 var _pauseOnExit = false; 17265 var _startTime = startTime; 17266 var _endTime = endTime; 17267 var _text = text; 17268 var _region = null; 17269 var _vertical = ''; 17270 var _snapToLines = true; 17271 var _line = 'auto'; 17272 var _lineAlign = 'start'; 17273 var _position = 50; 17274 var _positionAlign = 'middle'; 17275 var _size = 50; 17276 var _align = 'middle'; 17277 Object.defineProperty(cue, 'id', extend({}, baseObj, { 17278 get: function () { 17279 return _id; 17280 }, 17281 set: function (value) { 17282 _id = '' + value; 17283 } 17284 })); 17285 Object.defineProperty(cue, 'pauseOnExit', extend({}, baseObj, { 17286 get: function () { 17287 return _pauseOnExit; 17288 }, 17289 set: function (value) { 17290 _pauseOnExit = !!value; 17291 } 17292 })); 17293 Object.defineProperty(cue, 'startTime', extend({}, baseObj, { 17294 get: function () { 17295 return _startTime; 17296 }, 17297 set: function (value) { 17298 if (typeof value !== 'number') { 17299 throw new TypeError('Start time must be set to a number.'); 17300 } 17301 _startTime = value; 17302 this.hasBeenReset = true; 17303 } 17304 })); 17305 Object.defineProperty(cue, 'endTime', extend({}, baseObj, { 17306 get: function () { 17307 return _endTime; 17308 }, 17309 set: function (value) { 17310 if (typeof value !== 'number') { 17311 throw new TypeError('End time must be set to a number.'); 17312 } 17313 _endTime = value; 17314 this.hasBeenReset = true; 17315 } 17316 })); 17317 Object.defineProperty(cue, 'text', extend({}, baseObj, { 17318 get: function () { 17319 return _text; 17320 }, 17321 set: function (value) { 17322 _text = '' + value; 17323 this.hasBeenReset = true; 17324 } 17325 })); 17326 Object.defineProperty(cue, 'region', extend({}, baseObj, { 17327 get: function () { 17328 return _region; 17329 }, 17330 set: function (value) { 17331 _region = value; 17332 this.hasBeenReset = true; 17333 } 17334 })); 17335 Object.defineProperty(cue, 'vertical', extend({}, baseObj, { 17336 get: function () { 17337 return _vertical; 17338 }, 17339 set: function (value) { 17340 var setting = findDirectionSetting(value); 17341 // Have to check for false because the setting an be an empty string. 17342 if (setting === false) { 17343 throw new SyntaxError('An invalid or illegal string was specified.'); 17344 } 17345 _vertical = setting; 17346 this.hasBeenReset = true; 17347 } 17348 })); 17349 Object.defineProperty(cue, 'snapToLines', extend({}, baseObj, { 17350 get: function () { 17351 return _snapToLines; 17352 }, 17353 set: function (value) { 17354 _snapToLines = !!value; 17355 this.hasBeenReset = true; 17356 } 17357 })); 17358 Object.defineProperty(cue, 'line', extend({}, baseObj, { 17359 get: function () { 17360 return _line; 17361 }, 17362 set: function (value) { 17363 if (typeof value !== 'number' && value !== autoKeyword) { 17364 throw new SyntaxError('An invalid number or illegal string was specified.'); 17365 } 17366 _line = value; 17367 this.hasBeenReset = true; 17368 } 17369 })); 17370 Object.defineProperty(cue, 'lineAlign', extend({}, baseObj, { 17371 get: function () { 17372 return _lineAlign; 17373 }, 17374 set: function (value) {
17375 var setting = findAlignSetting(value); 17376 if (!setting) { 17377 throw new SyntaxError('An invalid or illegal string was specified.'); 17378 } 17379 _lineAlign = setting; 17380 this.hasBeenReset = true; 17381 } 17382 })); 17383 Object.defineProperty(cue, 'position', extend({}, baseObj, { 17384 get: function () { 17385 return _position; 17386 }, 17387 set: function (value) { 17388 if (value < 0 || value > 100) { 17389 throw new Error('Position must be between 0 and 100.'); 17390 } 17391 _position = value; 17392 this.hasBeenReset = true; 17393 } 17394 })); 17395 Object.defineProperty(cue, 'positionAlign', extend({}, baseObj, { 17396 get: function () { 17397 return _positionAlign; 17398 }, 17399 set: function (value) { 17400 var setting = findAlignSetting(value); 17401 if (!setting) { 17402 throw new SyntaxError('An invalid or illegal string was specified.'); 17403 } 17404 _positionAlign = setting; 17405 this.hasBeenReset = true; 17406 } 17407 })); 17408 Object.defineProperty(cue, 'size', extend({}, baseObj, { 17409 get: function () { 17410 return _size; 17411 }, 17412 set: function (value) { 17413 if (value < 0 || value > 100) { 17414 throw new Error('Size must be between 0 and 100.'); 17415 } 17416 _size = value; 17417 this.hasBeenReset = true; 17418 } 17419 })); 17420 Object.defineProperty(cue, 'align', extend({}, baseObj, { 17421 get: function () { 17422 return _align; 17423 }, 17424 set: function (value) { 17425 var setting = findAlignSetting(value); 17426 if (!setting) { 17427 throw new SyntaxError('An invalid or illegal string was specified.'); 17428 } 17429 _align = setting; 17430 this.hasBeenReset = true; 17431 } 17432 })); 17433 /** 17434 * Other <track> spec defined properties 17435 */ 17436 // http://www.whatwg.org/specs/web-apps/current-work/multipage/the-video-element.html#text-track-cue-display-state 17437 cue.displayState = undefined; 17438 if (isIE8) { 17439 return cue; 17440 } 17441 } 17442 /** 17443 * VTTCue methods 17444 */ 17445 VTTCue.prototype.getCueAsHTML = function () { 17446 // Assume WebVTT.convertCueToDOMTree is on the global. 17447 var WebVTT = window.WebVTT; 17448 return WebVTT.convertCueToDOMTree(window, this.text); 17449 }; 17450 return VTTCue; 17451})(); 17452 17453 17454/***/ }), 17455
vendor: 4,392 bytes, lines 17456-17587
17456/***/ "./src/utils/vttparser.js": 17457/*!********************************!*\ 17458 !*** ./src/utils/vttparser.js ***! 17459 \********************************/ 17460/*! no static exports found */ 17461/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 17462/***/ (function(module, exports, __webpack_require__) { 17463 17464"use strict"; 17465 17466/* 17467 * Source: https://github.com/mozilla/vtt.js/blob/master/dist/vtt.js#L1716 17468 */ 17469Object.defineProperty(exports, "__esModule", { value: true }); 17470var vttcue_1 = __webpack_require__(/*! ./vttcue */ "./src/utils/vttcue.js"); 17471var StringDecoder = function StringDecoder() { 17472 return { 17473 decode: function (data) { 17474 if (!data) { 17475 return ''; 17476 } 17477 if (typeof data !== 'string') { 17478 throw new Error('Error - expected string data.'); 17479 } 17480 return decodeURIComponent(encodeURIComponent(data)); 17481 } 17482 }; 17483}; 17484function VTTParser() { 17485 this.window = window; 17486 this.state = 'INITIAL'; 17487 this.buffer = ''; 17488 this.decoder = new StringDecoder(); 17489 this.regionList = []; 17490} 17491// Try to parse input as a time stamp. 17492function parseTimeStamp(input) { 17493 function computeSeconds(h, m, s, f) { 17494 return (h | 0) * 3600 + (m | 0) * 60 + (s | 0) + (f | 0) / 1000; 17495 } 17496 var m = input.match(/^(\d+):(\d{2})(:\d{2})?\.(\d{3})/); 17497 if (!m) { 17498 return null; 17499 } 17500 if (m[3]) { 17501 // Timestamp takes the form of [hours]:[minutes]:[seconds].[milliseconds] 17502 return computeSeconds(m[1], m[2], m[3].replace(':', ''), m[4]); 17503 } 17504 else if (m[1] > 59) { 17505 // Timestamp takes the form of [hours]:[minutes].[milliseconds] 17506 // First position is hours as it's over 59. 17507 return computeSeconds(m[1], m[2], 0, m[4]); 17508 } 17509 else { 17510 // Timestamp takes the form of [minutes]:[seconds].[milliseconds] 17511 return computeSeconds(0, m[1], m[2], m[4]); 17512 } 17513} 17514// A settings object holds key/value pairs and will ignore anything but the first 17515// assignment to a specific key. 17516function Settings() { 17517 this.values = Object.create(null); 17518} 17519Settings.prototype = { 17520 // Only accept the first assignment to any key. 17521 set: function (k, v) { 17522 if (!this.get(k) && v !== '') { 17523 this.values[k] = v; 17524 } 17525 }, 17526 // Return the value for a key, or a default value. 17527 // If 'defaultKey' is passed then 'dflt' is assumed to be an object with 17528 // a number of possible default values as properties where 'defaultKey' is 17529 // the key of the property that will be chosen; otherwise it's assumed to be 17530 // a single value. 17531 get: function (k, dflt, defaultKey) { 17532 if (defaultKey) { 17533 return this.has(k) ? this.values[k] : dflt[defaultKey]; 17534 } 17535 return this.has(k) ? this.values[k] : dflt; 17536 }, 17537 // Check whether we have a value for a key. 17538 has: function (k) { 17539 return k in this.values; 17540 }, 17541 // Accept a setting if its one of the given alternatives. 17542 alt: function (k, v, a) { 17543 for (var n = 0; n < a.length; ++n) { 17544 if (v === a[n]) { 17545 this.set(k, v); 17546 break; 17547 } 17548 } 17549 }, 17550 // Accept a setting if its a valid (signed) integer. 17551 integer: function (k, v) { 17552 if (/^-?\d+$/.test(v)) { // integer 17553 this.set(k, parseInt(v, 10)); 17554 } 17555 }, 17556 // Accept a setting if its a valid percentage. 17557 percent: function (k, v) { 17558 var m; 17559 if ((m = v.match(/^([\d]{1,3})(\.[\d]*)?%$/))) { 17560 v = parseFloat(v); 17561 if (v >= 0 && v <= 100) { 17562 this.set(k, v); 17563 return true; 17564 } 17565 } 17566 return false; 17567 } 17568}; 17569// Helper function to parse input into groups separated by 'groupDelim', and 17570// interprete each group as a key/value pair separated by 'keyValueDelim'. 17571function parseOptions(input, callback, keyValueDelim, groupDelim) { 17572 var groups = groupDelim ? input.split(groupDelim) : [input]; 17573 for (var i in groups) { 17574 if (typeof groups[i] !== 'string') { 17575 continue; 17576 } 17577 var kv = groups[i].split(keyValueDelim); 17578 if (kv.length !== 2) { 17579 continue; 17580 } 17581 var k = kv[0]; 17582 var v = kv[1]; 17583 callback(k, v); 17584 } 17585} 17586var defaults = new vttcue_1.default(0, 0, 0); 17587// 'middle' was changed to 'center' in the spec: https://github.com/w3c/webvtt/
vendor: 10,982 bytes, lines 17587-17849
17587pull/244 17588// Safari doesn't yet support this change, but FF and Chrome do. 17589var center = defaults.align === 'middle' ? 'middle' : 'center'; 17590function parseCue(input, cue, regionList) { 17591 // Remember the original input if we need to throw an error. 17592 var oInput = input; 17593 // 4.1 WebVTT timestamp 17594 function consumeTimeStamp() { 17595 var ts = parseTimeStamp(input); 17596 if (ts === null) { 17597 throw new Error('Malformed timestamp: ' + oInput); 17598 } 17599 // Remove time stamp from input. 17600 input = input.replace(/^[^\sa-zA-Z-]+/, ''); 17601 return ts; 17602 } 17603 // 4.4.2 WebVTT cue settings 17604 function consumeCueSettings(input, cue) { 17605 var settings = new Settings(); 17606 parseOptions(input, function (k, v) { 17607 switch (k) { 17608 case 'region': 17609 // Find the last region we parsed with the same region id. 17610 for (var i = regionList.length - 1; i >= 0; i--) { 17611 if (regionList[i].id === v) { 17612 settings.set(k, regionList[i].region); 17613 break; 17614 } 17615 } 17616 break; 17617 case 'vertical': 17618 settings.alt(k, v, ['rl', 'lr']); 17619 break; 17620 case 'line': 17621 var vals = v.split(','), vals0 = vals[0]; 17622 settings.integer(k, vals0); 17623 if (settings.percent(k, vals0)) { 17624 settings.set('snapToLines', false); 17625 } 17626 settings.alt(k, vals0, ['auto']); 17627 if (vals.length === 2) { 17628 settings.alt('lineAlign', vals[1], ['start', center, 'end']); 17629 } 17630 break; 17631 case 'position': 17632 vals = v.split(','); 17633 settings.percent(k, vals[0]); 17634 if (vals.length === 2) { 17635 settings.alt('positionAlign', vals[1], ['start', center, 'end', 'line-left', 'line-right', 'auto']); 17636 } 17637 break; 17638 case 'size': 17639 settings.percent(k, v); 17640 break; 17641 case 'align': 17642 settings.alt(k, v, ['start', center, 'end', 'left', 'right']); 17643 break; 17644 } 17645 }, /:/, /\s/); 17646 // Apply default values for any missing fields. 17647 cue.region = settings.get('region', null); 17648 cue.vertical = settings.get('vertical', ''); 17649 var line = settings.get('line', 'auto'); 17650 if (line === 'auto' && defaults.line === -1) { 17651 // set numeric line number for Safari 17652 line = -1; 17653 } 17654 cue.line = line; 17655 cue.lineAlign = settings.get('lineAlign', 'start'); 17656 cue.snapToLines = settings.get('snapToLines', true); 17657 cue.size = settings.get('size', 100); 17658 cue.align = settings.get('align', center); 17659 var position = settings.get('position', 'auto'); 17660 if (position === 'auto' && defaults.position === 50) { 17661 // set numeric position for Safari 17662 position = cue.align === 'start' || cue.align === 'left' ? 0 : cue.align === 'end' || cue.align === 'right' ? 100 : 50; 17663 } 17664 cue.position = position; 17665 } 17666 function skipWhitespace() { 17667 input = input.replace(/^\s+/, ''); 17668 } 17669 // 4.1 WebVTT cue timings. 17670 skipWhitespace(); 17671 cue.startTime = consumeTimeStamp(); // (1) collect cue start time 17672 skipWhitespace(); 17673 if (input.substr(0, 3) !== '-->') { // (3) next characters must match '-->' 17674 throw new Error('Malformed time stamp (time stamps must be separated by \'-->\'): ' + 17675 oInput); 17676 } 17677 input = input.substr(3); 17678 skipWhitespace(); 17679 cue.endTime = consumeTimeStamp(); // (5) collect cue end time 17680 // 4.1 WebVTT cue settings list. 17681 skipWhitespace(); 17682 consumeCueSettings(input, cue); 17683} 17684function fixLineBreaks(input) { 17685 return input.replace(/<br(?: \/)?>/gi, '\n'); 17686} 17687exports.fixLineBreaks = fixLineBreaks; 17688VTTParser.prototype = { 17689 parse: function (data) { 17690 var self = this; 17691 // If there is no data then we won't decode it, but will just try to parse 17692 // whatever is in buffer already. This may occur in circumstances, for 17693 // example when flush() is called. 17694 if (data) { 17695 // Try to decode the data that we received. 17696 self.buffer += self.decoder.decode(data, { stream: true }); 17697 } 17698 function collectNextLine() { 17699 var buffer = self.buffer; 17700 var pos = 0; 17701 buffer = fixLineBreaks(buffer); 17702 while (pos < buffer.length && buffer[pos] !== '\r' && buffer[pos] !== '\n') { 17703 ++pos; 17704 } 17705 var line = buffer.substr(0, pos); 17706 // Advance the buffer early in case we fail below. 17707 if (buffer[pos] === '\r') { 17708 ++pos; 17709 } 17710 if (buffer[pos] === '\n') { 17711 ++pos; 17712 } 17713 self.buffer = buffer.substr(pos); 17714 return line; 17715 } 17716 // 3.2 WebVTT metadata header syntax 17717 function parseHeader(input) { 17718 parseOptions(input, function (k, v) { 17719 switch (k) { 17720 case 'Region': 17721 // 3.3 WebVTT region metadata header syntax 17722 // console.log('parse region', v); 17723 // parseRegion(v); 17724 break; 17725 } 17726 }, /:/); 17727 } 17728 // 5.1 WebVTT file parsing. 17729 try { 17730 var line = void 0; 17731 if (self.state === 'INITIAL') { 17732 // We can't start parsing until we have the first line. 17733 if (!/\r\n|\n/.test(self.buffer)) { 17734 return this; 17735 } 17736 line = collectNextLine(); 17737 // strip of UTF-8 BOM if any 17738 // https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8 17739 var m = line.match(/^()?WEBVTT([ \t].*)?$/); 17740 if (!m || !m[0]) { 17741 throw new Error('Malformed WebVTT signature.'); 17742 } 17743 self.state = 'HEADER'; 17744 } 17745 var alreadyCollectedLine = false; 17746 while (self.buffer) { 17747 // We can't parse a line until we have the full line. 17748 if (!/\r\n|\n/.test(self.buffer)) { 17749 return this; 17750 } 17751 if (!alreadyCollectedLine) { 17752 line = collectNextLine(); 17753 } 17754 else { 17755 alreadyCollectedLine = false; 17756 } 17757 switch (self.state) { 17758 case 'HEADER': 17759 // 13-18 - Allow a header (metadata) under the WEBVTT line. 17760 if (/:/.test(line)) { 17761 parseHeader(line); 17762 } 17763 else if (!line) { 17764 // An empty line terminates the header and starts the body (cues). 17765 self.state = 'ID'; 17766 } 17767 continue; 17768 case 'NOTE': 17769 // Ignore NOTE blocks. 17770 if (!line) { 17771 self.state = 'ID'; 17772 } 17773 continue; 17774 case 'ID': 17775 // Check for the start of NOTE blocks. 17776 if (/^NOTE($|[ \t])/.test(line)) { 17777 self.state = 'NOTE'; 17778 break; 17779 } 17780 // 19-29 - Allow any number of line terminators, then initialize new cue values. 17781 if (!line) { 17782 continue; 17783 } 17784 self.cue = new vttcue_1.default(0, 0, ''); 17785 self.state = 'CUE'; 17786 // 30-39 - Check if self line contains an optional identifier or timing data. 17787 if (line.indexOf('-->') === -1) { 17788 self.cue.id = line; 17789 continue; 17790 } 17791 // Process line as start of a cue. 17792 /* falls through */ 17793 case 'CUE': 17794 // 40 - Collect cue timings and settings. 17795 try { 17796 parseCue(line, self.cue, self.regionList); 17797 } 17798 catch (e) { 17799 // In case of an error ignore rest of the cue. 17800 self.cue = null; 17801 self.state = 'BADCUE'; 17802 continue; 17803 } 17804 self.state = 'CUETEXT'; 17805 continue; 17806 case 'CUETEXT': 17807 var hasSubstring = line.indexOf('-->') !== -1; 17808 // 34 - If we have an empty line then report the cue. 17809 // 35 - If we have the special substring '-->' then report the cue, 17810 // but do not collect the line as we need to process the current 17811 // one as a new cue. 17812 if (!line || hasSubstring && (alreadyCollectedLine = true)) { 17813 // We are done parsing self cue. 17814 if (self.oncue) { 17815 self.oncue(self.cue); 17816 } 17817 self.cue = null; 17818 self.state = 'ID'; 17819 continue; 17820 } 17821 if (self.cue.text) { 17822 self.cue.text += '\n'; 17823 } 17824 self.cue.text += line; 17825 continue; 17826 case 'BADCUE': // BADCUE 17827 // 54-62 - Collect and discard the remaining cue. 17828 if (!line) { 17829 self.state = 'ID'; 17830 } 17831 continue; 17832 } 17833 } 17834 } 17835 catch (e) { 17836 // If we are currently parsing a cue, report what we have. 17837 if (self.state === 'CUETEXT' && self.cue && self.oncue) { 17838 self.oncue(self.cue); 17839 } 17840 self.cue = null; 17841 // Enter BADWEBVTT state if header was not parsed correctly otherwise 17842 // another exception occurred so enter BADCUE state. 17843 self.state = self.state === 'INITIAL' ? 'BADWEBVTT' : 'BADCUE'; 17844 } 17845 return this; 17846 }, 17847 flush: function () { 17848 var self = this; 17849 try {
17850 // Finish decoding the stream. 17851 self.buffer += self.decoder.decode(); 17852 // Synthesize the end of the current cue or region. 17853 if (self.cue || self.state === 'HEADER') { 17854 self.buffer += '\n\n'; 17855 self.parse(); 17856 } 17857 // If we've flushed, parsed, and we're still on the INITIAL state then 17858 // that means we don't have enough of the stream to parse the first 17859 // line. 17860 if (self.state === 'INITIAL') { 17861 throw new Error('Malformed WebVTT signature.'); 17862 } 17863 } 17864 catch (e) { 17865 throw e; 17866 } 17867 if (self.onflush) { 17868 self.onflush(); 17869 } 17870 return this; 17871 } 17872}; 17873exports.default = VTTParser; 17874 17875 17876/***/ }), 17877
vendor: 5,081 bytes, lines 17878-17992
17878/***/ "./src/utils/webvtt-parser.js": 17879/*!************************************!*\ 17880 !*** ./src/utils/webvtt-parser.js ***! 17881 \************************************/ 17882/*! no static exports found */ 17883/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 17884/***/ (function(module, exports, __webpack_require__) { 17885 17886"use strict"; 17887/* WEBPACK VAR INJECTION */(function(Number) { 17888Object.defineProperty(exports, "__esModule", { value: true }); 17889var vttparser_1 = __webpack_require__(/*! ./vttparser */ "./src/utils/vttparser.js"); 17890var id3_1 = __webpack_require__(/*! ../demux/id3 */ "./src/demux/id3.js"); 17891// String.prototype.startsWith is not supported in IE11 17892var startsWith = function (inputString, searchString, position) { 17893 return inputString.substr(position || 0, searchString.length) === searchString; 17894}; 17895var cueString2millis = function (timeString) { 17896 var ts = parseInt(timeString.substr(-3)); 17897 var secs = parseInt(timeString.substr(-6, 2)); 17898 var mins = parseInt(timeString.substr(-9, 2)); 17899 var hours = timeString.length > 9 ? parseInt(timeString.substr(0, timeString.indexOf(':'))) : 0; 17900 if (!Number.isFinite(ts) || !Number.isFinite(secs) || !Number.isFinite(mins) || !Number.isFinite(hours)) { 17901 return -1; 17902 } 17903 ts += 1000 * secs; 17904 ts += 60 * 1000 * mins; 17905 ts += 60 * 60 * 1000 * hours; 17906 return ts; 17907}; 17908// From https://github.com/darkskyapp/string-hash 17909var hash = function (text) { 17910 var hash = 5381; 17911 var i = text.length; 17912 while (i) { 17913 hash = (hash * 33) ^ text.charCodeAt(--i); 17914 } 17915 return (hash >>> 0).toString(); 17916}; 17917var calculateOffset = function (vttCCs, cc, presentationTime) { 17918 var currCC = vttCCs[cc]; 17919 var prevCC = vttCCs[currCC.prevCC]; 17920 // This is the first discontinuity or cues have been processed since the last discontinuity 17921 // Offset = current discontinuity time 17922 if (!prevCC || (!prevCC.new && currCC.new)) { 17923 vttCCs.ccOffset = vttCCs.presentationOffset = currCC.start; 17924 currCC.new = false; 17925 return; 17926 } 17927 // There have been discontinuities since cues were last parsed. 17928 // Offset = time elapsed 17929 while (prevCC && prevCC.new) { 17930 vttCCs.ccOffset += currCC.start - prevCC.start; 17931 currCC.new = false; 17932 currCC = prevCC; 17933 prevCC = vttCCs[currCC.prevCC]; 17934 } 17935 vttCCs.presentationOffset = presentationTime; 17936}; 17937var WebVTTParser = { 17938 parse: function (vttByteArray, syncPTS, vttCCs, cc, callBack, errorCallBack) { 17939 // Convert byteArray into string, replacing any somewhat exotic linefeeds with "\n", then split on that character. 17940 var re = /\r\n|\n\r|\n|\r/g; 17941 // Uint8Array.prototype.reduce is not implemented in IE11 17942 var vttLines = id3_1.utf8ArrayToStr(new Uint8Array(vttByteArray)).trim().replace(re, '\n').split('\n'); 17943 var cueTime = '00:00.000'; 17944 var mpegTs = 0; 17945 var localTime = 0; 17946 var presentationTime = 0; 17947 var cues = []; 17948 var parsingError; 17949 var inHeader = true; 17950 // let VTTCue = VTTCue || window.TextTrackCue; 17951 // Create parser object using VTTCue with TextTrackCue fallback on certain browsers. 17952 var parser = new vttparser_1.default(); 17953 parser.oncue = function (cue) { 17954 // Adjust cue timing; clamp cues to start no earlier than - and drop cues that don't end after - 0 on timeline. 17955 var currCC = vttCCs[cc]; 17956 var cueOffset = vttCCs.ccOffset; 17957 // Update offsets for new discontinuities 17958 if (currCC && currCC.new) { 17959 if (localTime !== undefined) { 17960 // When local time is provided, offset = discontinuity start time - local time 17961 cueOffset = vttCCs.ccOffset = currCC.start; 17962 } 17963 else { 17964 calculateOffset(vttCCs, cc, presentationTime); 17965 } 17966 } 17967 if (presentationTime) { 17968 // If we have MPEGTS, offset = presentation time + discontinuity offset 17969 cueOffset = presentationTime - vttCCs.presentationOffset; 17970 } 17971 cue.startTime += cueOffset - localTime; 17972 cue.endTime += cueOffset - localTime; 17973 // Create a unique hash id for a cue based on start/end times and text. 17974 // This helps timeline-controller to avoid showing repeated captions. 17975 cue.id = hash(cue.startTime.toString()) + hash(cue.endTime.toString()) + hash(cue.text); 17976 // Fix encoding of special characters. TODO: Test with all sorts of weird characters. 17977 cue.text = decodeURIComponent(encodeURIComponent(cue.text)); 17978 if (cue.endTime > 0) { 17979 cues.push(cue); 17980 } 17981 }; 17982 parser.onparsingerror = function (e) { 17983 parsingError = e; 17984 }; 17985 parser.onflush = function () { 17986 if (parsingError && errorCallBack) { 17987 errorCallBack(parsingError); 17988 return; 17989 } 17990 callBack(cues); 17991 }; 17992 // Go through contents line by line.
17993 vttLines.forEach(function (line) { 17994 if (inHeader) { 17995 // Look for X-TIMESTAMP-MAP in header. 17996 if (startsWith(line, 'X-TIMESTAMP-MAP=')) { 17997 // Once found, no more are allowed anyway, so stop searching. 17998 inHeader = false; 17999 // Extract LOCAL and MPEGTS. 18000 line.substr(16).split(',').forEach(function (timestamp) { 18001 if (startsWith(timestamp, 'LOCAL:')) { 18002 cueTime = timestamp.substr(6); 18003 } 18004 else if (startsWith(timestamp, 'MPEGTS:')) { 18005 mpegTs = parseInt(timestamp.substr(7)); 18006 } 18007 }); 18008 try { 18009 // Calculate subtitle offset in milliseconds. 18010 if (syncPTS + ((vttCCs[cc].start * 90000) || 0) < 0) { 18011 syncPTS += 8589934592; 18012 } 18013 // Adjust MPEGTS by sync PTS. 18014 mpegTs -= syncPTS; 18015 // Convert cue time to seconds 18016 localTime = cueString2millis(cueTime) / 1000; 18017 // Convert MPEGTS to seconds from 90kHz. 18018 presentationTime = mpegTs / 90000; 18019 if (localTime === -1) { 18020 parsingError = new Error("Malformed X-TIMESTAMP-MAP: " + line); 18021 } 18022 } 18023 catch (e) { 18024 parsingError = new Error("Malformed X-TIMESTAMP-MAP: " + line); 18025 } 18026 // Return without parsing X-TIMESTAMP-MAP line. 18027 return; 18028 } 18029 else if (line === '') { 18030 inHeader = false;
18031 } 18032 } 18033 // Parse line by default. 18034 parser.parse(line + '\n'); 18035 }); 18036 parser.flush(); 18037 } 18038}; 18039exports.default = WebVTTParser; 18040 18041/* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./src/polyfills/number */ "./src/polyfills/number.js")["Number"])) 18042 18043/***/ }), 18044
vendor: 6,843 bytes, lines 18045-18205
18045/***/ "./src/utils/xhr-loader.js": 18046/*!*********************************!*\ 18047 !*** ./src/utils/xhr-loader.js ***! 18048 \*********************************/ 18049/*! no static exports found */ 18050/*! ModuleConcatenation bailout: Module is not an ECMAScript module */ 18051/***/ (function(module, exports, __webpack_require__) { 18052 18053"use strict"; 18054 18055/** 18056 * XHR based logger 18057*/ 18058Object.defineProperty(exports, "__esModule", { value: true }); 18059var logger_1 = __webpack_require__(/*! ../utils/logger */ "./src/utils/logger.js"); 18060var performance = window.performance, XMLHttpRequest = window.XMLHttpRequest; 18061var XhrLoader = /** @class */ (function () { 18062 function XhrLoader(config) { 18063 if (config && config.xhrSetup) { 18064 this.xhrSetup = config.xhrSetup; 18065 } 18066 } 18067 XhrLoader.prototype.destroy = function () { 18068 this.abort(); 18069 this.loader = null; 18070 }; 18071 XhrLoader.prototype.abort = function () { 18072 var loader = this.loader; 18073 if (loader && loader.readyState !== 4) { 18074 this.stats.aborted = true; 18075 loader.abort(); 18076 } 18077 window.clearTimeout(this.requestTimeout); 18078 this.requestTimeout = null; 18079 window.clearTimeout(this.retryTimeout); 18080 this.retryTimeout = null; 18081 }; 18082 XhrLoader.prototype.load = function (context, config, callbacks) { 18083 this.context = context; 18084 this.config = config; 18085 this.callbacks = callbacks; 18086 this.stats = { trequest: performance.now(), retry: 0 }; 18087 this.retryDelay = config.retryDelay; 18088 this.loadInternal(); 18089 }; 18090 XhrLoader.prototype.loadInternal = function () { 18091 var xhr, context = this.context; 18092 xhr = this.loader = new XMLHttpRequest(); 18093 var stats = this.stats; 18094 stats.tfirst = 0; 18095 stats.loaded = 0; 18096 var xhrSetup = this.xhrSetup; 18097 try { 18098 if (xhrSetup) { 18099 try { 18100 xhrSetup(xhr, context.url); 18101 } 18102 catch (e) { 18103 // fix xhrSetup: (xhr, url) => {xhr.setRequestHeader("Content-Language", "test");} 18104 // not working, as xhr.setRequestHeader expects xhr.readyState === OPEN 18105 xhr.open('GET', context.url, true); 18106 xhrSetup(xhr, context.url); 18107 } 18108 } 18109 if (!xhr.readyState) { 18110 xhr.open('GET', context.url, true); 18111 } 18112 } 18113 catch (e) { 18114 // IE11 throws an exception on xhr.open if attempting to access an HTTP resource over HTTPS 18115 this.callbacks.onError({ code: xhr.status, text: e.message }, context, xhr); 18116 return; 18117 } 18118 if (context.rangeEnd) { 18119 xhr.setRequestHeader('Range', 'bytes=' + context.rangeStart + '-' + (context.rangeEnd - 1)); 18120 } 18121 xhr.onreadystatechange = this.readystatechange.bind(this); 18122 xhr.onprogress = this.loadprogress.bind(this); 18123 xhr.responseType = context.responseType; 18124 // setup timeout before we perform request 18125 this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), this.config.timeout); 18126 xhr.send(); 18127 }; 18128 XhrLoader.prototype.readystatechange = function (event) { 18129 var xhr = event.currentTarget, readyState = xhr.readyState, stats = this.stats, context = this.context, config = this.config; 18130 // don't proceed if xhr has been aborted 18131 if (stats.aborted) { 18132 return; 18133 } 18134 // >= HEADERS_RECEIVED 18135 if (readyState >= 2) { 18136 // clear xhr timeout and rearm it if readyState less than 4 18137 window.clearTimeout(this.requestTimeout); 18138 if (stats.tfirst === 0) { 18139 stats.tfirst = Math.max(performance.now(), stats.trequest); 18140 } 18141 if (readyState === 4) { 18142 var status_1 = xhr.status; 18143 // http status between 200 to 299 are all successful 18144 if (status_1 >= 200 && status_1 < 300) { 18145 stats.tload = Math.max(stats.tfirst, performance.now()); 18146 var data = void 0, len = void 0; 18147 if (context.responseType === 'arraybuffer') { 18148 data = xhr.response; 18149 len = data.byteLength; 18150 } 18151 else { 18152 data = xhr.responseText; 18153 len = data.length; 18154 } 18155 stats.loaded = stats.total = len; 18156 var response = { url: xhr.responseURL, data: data }; 18157 this.callbacks.onSuccess(response, stats, context, xhr); 18158 } 18159 else { 18160 // if max nb of retries reached or if http status between 400 and 499 (such error cannot be recovered, retrying is useless), return error 18161 if (stats.retry >= config.maxRetry || (status_1 >= 400 && status_1 < 499)) { 18162 logger_1.logger.error(status_1 + " while loading " + context.url); 18163 this.callbacks.onError({ code: status_1, text: xhr.statusText }, context, xhr); 18164 } 18165 else { 18166 // retry 18167 logger_1.logger.warn(status_1 + " while loading " + context.url + ", retrying in " + this.retryDelay + "..."); 18168 // aborts and resets internal state 18169 this.destroy(); 18170 // schedule retry 18171 this.retryTimeout = window.setTimeout(this.loadInternal.bind(this), this.retryDelay); 18172 // set exponential backoff 18173 this.retryDelay = Math.min(2 * this.retryDelay, config.maxRetryDelay); 18174 stats.retry++; 18175 } 18176 } 18177 } 18178 else { 18179 // readyState >= 2 AND readyState !==4 (readyState = HEADERS_RECEIVED || LOADING) rearm timeout as xhr not finished yet 18180 this.requestTimeout = window.setTimeout(this.loadtimeout.bind(this), config.timeout); 18181 } 18182 } 18183 }; 18184 XhrLoader.prototype.loadtimeout = function () { 18185 logger_1.logger.warn("timeout while loading " + this.context.url); 18186 this.callbacks.onTimeout(this.stats, this.context, null); 18187 }; 18188 XhrLoader.prototype.loadprogress = function (event) { 18189 var xhr = event.currentTarget, stats = this.stats; 18190 stats.loaded = event.loaded; 18191 if (event.lengthComputable) { 18192 stats.total = event.total; 18193 } 18194 var onProgress = this.callbacks.onProgress; 18195 if (onProgress) { 18196 // third arg is to provide on progress data 18197 onProgress(stats, this.context, null, xhr); 18198 } 18199 }; 18200 return XhrLoader; 18201}()); 18202exports.default = XhrLoader; 18203 18204 18205/***/ })
18205 18206 18207/******/ })["default"]; 18208}); 18209//# sourceMappingURL=hls.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.