github.com/cnotch/ipchub@v1.1.0/demos/flv/flv.js (about) 1 (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.flvjs = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(_dereq_,module,exports){ 2 (function (process,global){ 3 /*! 4 * @overview es6-promise - a tiny implementation of Promises/A+. 5 * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald) 6 * @license Licensed under MIT license 7 * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE 8 * @version v4.2.8+1e68dce6 9 */ 10 11 (function (global, factory) { 12 typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : 13 typeof define === 'function' && define.amd ? define(factory) : 14 (global.ES6Promise = factory()); 15 }(this, (function () { 'use strict'; 16 17 function objectOrFunction(x) { 18 var type = typeof x; 19 return x !== null && (type === 'object' || type === 'function'); 20 } 21 22 function isFunction(x) { 23 return typeof x === 'function'; 24 } 25 26 27 28 var _isArray = void 0; 29 if (Array.isArray) { 30 _isArray = Array.isArray; 31 } else { 32 _isArray = function (x) { 33 return Object.prototype.toString.call(x) === '[object Array]'; 34 }; 35 } 36 37 var isArray = _isArray; 38 39 var len = 0; 40 var vertxNext = void 0; 41 var customSchedulerFn = void 0; 42 43 var asap = function asap(callback, arg) { 44 queue[len] = callback; 45 queue[len + 1] = arg; 46 len += 2; 47 if (len === 2) { 48 // If len is 2, that means that we need to schedule an async flush. 49 // If additional callbacks are queued before the queue is flushed, they 50 // will be processed by this flush that we are scheduling. 51 if (customSchedulerFn) { 52 customSchedulerFn(flush); 53 } else { 54 scheduleFlush(); 55 } 56 } 57 }; 58 59 function setScheduler(scheduleFn) { 60 customSchedulerFn = scheduleFn; 61 } 62 63 function setAsap(asapFn) { 64 asap = asapFn; 65 } 66 67 var browserWindow = typeof window !== 'undefined' ? window : undefined; 68 var browserGlobal = browserWindow || {}; 69 var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; 70 var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; 71 72 // test for web worker but not in IE10 73 var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined'; 74 75 // node 76 function useNextTick() { 77 // node version 0.10.x displays a deprecation warning when nextTick is used recursively 78 // see https://github.com/cujojs/when/issues/410 for details 79 return function () { 80 return process.nextTick(flush); 81 }; 82 } 83 84 // vertx 85 function useVertxTimer() { 86 if (typeof vertxNext !== 'undefined') { 87 return function () { 88 vertxNext(flush); 89 }; 90 } 91 92 return useSetTimeout(); 93 } 94 95 function useMutationObserver() { 96 var iterations = 0; 97 var observer = new BrowserMutationObserver(flush); 98 var node = document.createTextNode(''); 99 observer.observe(node, { characterData: true }); 100 101 return function () { 102 node.data = iterations = ++iterations % 2; 103 }; 104 } 105 106 // web worker 107 function useMessageChannel() { 108 var channel = new MessageChannel(); 109 channel.port1.onmessage = flush; 110 return function () { 111 return channel.port2.postMessage(0); 112 }; 113 } 114 115 function useSetTimeout() { 116 // Store setTimeout reference so es6-promise will be unaffected by 117 // other code modifying setTimeout (like sinon.useFakeTimers()) 118 var globalSetTimeout = setTimeout; 119 return function () { 120 return globalSetTimeout(flush, 1); 121 }; 122 } 123 124 var queue = new Array(1000); 125 function flush() { 126 for (var i = 0; i < len; i += 2) { 127 var callback = queue[i]; 128 var arg = queue[i + 1]; 129 130 callback(arg); 131 132 queue[i] = undefined; 133 queue[i + 1] = undefined; 134 } 135 136 len = 0; 137 } 138 139 function attemptVertx() { 140 try { 141 var vertx = Function('return this')().require('vertx'); 142 vertxNext = vertx.runOnLoop || vertx.runOnContext; 143 return useVertxTimer(); 144 } catch (e) { 145 return useSetTimeout(); 146 } 147 } 148 149 var scheduleFlush = void 0; 150 // Decide what async method to use to triggering processing of queued callbacks: 151 if (isNode) { 152 scheduleFlush = useNextTick(); 153 } else if (BrowserMutationObserver) { 154 scheduleFlush = useMutationObserver(); 155 } else if (isWorker) { 156 scheduleFlush = useMessageChannel(); 157 } else if (browserWindow === undefined && typeof _dereq_ === 'function') { 158 scheduleFlush = attemptVertx(); 159 } else { 160 scheduleFlush = useSetTimeout(); 161 } 162 163 function then(onFulfillment, onRejection) { 164 var parent = this; 165 166 var child = new this.constructor(noop); 167 168 if (child[PROMISE_ID] === undefined) { 169 makePromise(child); 170 } 171 172 var _state = parent._state; 173 174 175 if (_state) { 176 var callback = arguments[_state - 1]; 177 asap(function () { 178 return invokeCallback(_state, child, callback, parent._result); 179 }); 180 } else { 181 subscribe(parent, child, onFulfillment, onRejection); 182 } 183 184 return child; 185 } 186 187 /** 188 `Promise.resolve` returns a promise that will become resolved with the 189 passed `value`. It is shorthand for the following: 190 191 ```javascript 192 let promise = new Promise(function(resolve, reject){ 193 resolve(1); 194 }); 195 196 promise.then(function(value){ 197 // value === 1 198 }); 199 ``` 200 201 Instead of writing the above, your code now simply becomes the following: 202 203 ```javascript 204 let promise = Promise.resolve(1); 205 206 promise.then(function(value){ 207 // value === 1 208 }); 209 ``` 210 211 @method resolve 212 @static 213 @param {Any} value value that the returned promise will be resolved with 214 Useful for tooling. 215 @return {Promise} a promise that will become fulfilled with the given 216 `value` 217 */ 218 function resolve$1(object) { 219 /*jshint validthis:true */ 220 var Constructor = this; 221 222 if (object && typeof object === 'object' && object.constructor === Constructor) { 223 return object; 224 } 225 226 var promise = new Constructor(noop); 227 resolve(promise, object); 228 return promise; 229 } 230 231 var PROMISE_ID = Math.random().toString(36).substring(2); 232 233 function noop() {} 234 235 var PENDING = void 0; 236 var FULFILLED = 1; 237 var REJECTED = 2; 238 239 function selfFulfillment() { 240 return new TypeError("You cannot resolve a promise with itself"); 241 } 242 243 function cannotReturnOwn() { 244 return new TypeError('A promises callback cannot return that same promise.'); 245 } 246 247 function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) { 248 try { 249 then$$1.call(value, fulfillmentHandler, rejectionHandler); 250 } catch (e) { 251 return e; 252 } 253 } 254 255 function handleForeignThenable(promise, thenable, then$$1) { 256 asap(function (promise) { 257 var sealed = false; 258 var error = tryThen(then$$1, thenable, function (value) { 259 if (sealed) { 260 return; 261 } 262 sealed = true; 263 if (thenable !== value) { 264 resolve(promise, value); 265 } else { 266 fulfill(promise, value); 267 } 268 }, function (reason) { 269 if (sealed) { 270 return; 271 } 272 sealed = true; 273 274 reject(promise, reason); 275 }, 'Settle: ' + (promise._label || ' unknown promise')); 276 277 if (!sealed && error) { 278 sealed = true; 279 reject(promise, error); 280 } 281 }, promise); 282 } 283 284 function handleOwnThenable(promise, thenable) { 285 if (thenable._state === FULFILLED) { 286 fulfill(promise, thenable._result); 287 } else if (thenable._state === REJECTED) { 288 reject(promise, thenable._result); 289 } else { 290 subscribe(thenable, undefined, function (value) { 291 return resolve(promise, value); 292 }, function (reason) { 293 return reject(promise, reason); 294 }); 295 } 296 } 297 298 function handleMaybeThenable(promise, maybeThenable, then$$1) { 299 if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) { 300 handleOwnThenable(promise, maybeThenable); 301 } else { 302 if (then$$1 === undefined) { 303 fulfill(promise, maybeThenable); 304 } else if (isFunction(then$$1)) { 305 handleForeignThenable(promise, maybeThenable, then$$1); 306 } else { 307 fulfill(promise, maybeThenable); 308 } 309 } 310 } 311 312 function resolve(promise, value) { 313 if (promise === value) { 314 reject(promise, selfFulfillment()); 315 } else if (objectOrFunction(value)) { 316 var then$$1 = void 0; 317 try { 318 then$$1 = value.then; 319 } catch (error) { 320 reject(promise, error); 321 return; 322 } 323 handleMaybeThenable(promise, value, then$$1); 324 } else { 325 fulfill(promise, value); 326 } 327 } 328 329 function publishRejection(promise) { 330 if (promise._onerror) { 331 promise._onerror(promise._result); 332 } 333 334 publish(promise); 335 } 336 337 function fulfill(promise, value) { 338 if (promise._state !== PENDING) { 339 return; 340 } 341 342 promise._result = value; 343 promise._state = FULFILLED; 344 345 if (promise._subscribers.length !== 0) { 346 asap(publish, promise); 347 } 348 } 349 350 function reject(promise, reason) { 351 if (promise._state !== PENDING) { 352 return; 353 } 354 promise._state = REJECTED; 355 promise._result = reason; 356 357 asap(publishRejection, promise); 358 } 359 360 function subscribe(parent, child, onFulfillment, onRejection) { 361 var _subscribers = parent._subscribers; 362 var length = _subscribers.length; 363 364 365 parent._onerror = null; 366 367 _subscribers[length] = child; 368 _subscribers[length + FULFILLED] = onFulfillment; 369 _subscribers[length + REJECTED] = onRejection; 370 371 if (length === 0 && parent._state) { 372 asap(publish, parent); 373 } 374 } 375 376 function publish(promise) { 377 var subscribers = promise._subscribers; 378 var settled = promise._state; 379 380 if (subscribers.length === 0) { 381 return; 382 } 383 384 var child = void 0, 385 callback = void 0, 386 detail = promise._result; 387 388 for (var i = 0; i < subscribers.length; i += 3) { 389 child = subscribers[i]; 390 callback = subscribers[i + settled]; 391 392 if (child) { 393 invokeCallback(settled, child, callback, detail); 394 } else { 395 callback(detail); 396 } 397 } 398 399 promise._subscribers.length = 0; 400 } 401 402 function invokeCallback(settled, promise, callback, detail) { 403 var hasCallback = isFunction(callback), 404 value = void 0, 405 error = void 0, 406 succeeded = true; 407 408 if (hasCallback) { 409 try { 410 value = callback(detail); 411 } catch (e) { 412 succeeded = false; 413 error = e; 414 } 415 416 if (promise === value) { 417 reject(promise, cannotReturnOwn()); 418 return; 419 } 420 } else { 421 value = detail; 422 } 423 424 if (promise._state !== PENDING) { 425 // noop 426 } else if (hasCallback && succeeded) { 427 resolve(promise, value); 428 } else if (succeeded === false) { 429 reject(promise, error); 430 } else if (settled === FULFILLED) { 431 fulfill(promise, value); 432 } else if (settled === REJECTED) { 433 reject(promise, value); 434 } 435 } 436 437 function initializePromise(promise, resolver) { 438 try { 439 resolver(function resolvePromise(value) { 440 resolve(promise, value); 441 }, function rejectPromise(reason) { 442 reject(promise, reason); 443 }); 444 } catch (e) { 445 reject(promise, e); 446 } 447 } 448 449 var id = 0; 450 function nextId() { 451 return id++; 452 } 453 454 function makePromise(promise) { 455 promise[PROMISE_ID] = id++; 456 promise._state = undefined; 457 promise._result = undefined; 458 promise._subscribers = []; 459 } 460 461 function validationError() { 462 return new Error('Array Methods must be provided an Array'); 463 } 464 465 var Enumerator = function () { 466 function Enumerator(Constructor, input) { 467 this._instanceConstructor = Constructor; 468 this.promise = new Constructor(noop); 469 470 if (!this.promise[PROMISE_ID]) { 471 makePromise(this.promise); 472 } 473 474 if (isArray(input)) { 475 this.length = input.length; 476 this._remaining = input.length; 477 478 this._result = new Array(this.length); 479 480 if (this.length === 0) { 481 fulfill(this.promise, this._result); 482 } else { 483 this.length = this.length || 0; 484 this._enumerate(input); 485 if (this._remaining === 0) { 486 fulfill(this.promise, this._result); 487 } 488 } 489 } else { 490 reject(this.promise, validationError()); 491 } 492 } 493 494 Enumerator.prototype._enumerate = function _enumerate(input) { 495 for (var i = 0; this._state === PENDING && i < input.length; i++) { 496 this._eachEntry(input[i], i); 497 } 498 }; 499 500 Enumerator.prototype._eachEntry = function _eachEntry(entry, i) { 501 var c = this._instanceConstructor; 502 var resolve$$1 = c.resolve; 503 504 505 if (resolve$$1 === resolve$1) { 506 var _then = void 0; 507 var error = void 0; 508 var didError = false; 509 try { 510 _then = entry.then; 511 } catch (e) { 512 didError = true; 513 error = e; 514 } 515 516 if (_then === then && entry._state !== PENDING) { 517 this._settledAt(entry._state, i, entry._result); 518 } else if (typeof _then !== 'function') { 519 this._remaining--; 520 this._result[i] = entry; 521 } else if (c === Promise$1) { 522 var promise = new c(noop); 523 if (didError) { 524 reject(promise, error); 525 } else { 526 handleMaybeThenable(promise, entry, _then); 527 } 528 this._willSettleAt(promise, i); 529 } else { 530 this._willSettleAt(new c(function (resolve$$1) { 531 return resolve$$1(entry); 532 }), i); 533 } 534 } else { 535 this._willSettleAt(resolve$$1(entry), i); 536 } 537 }; 538 539 Enumerator.prototype._settledAt = function _settledAt(state, i, value) { 540 var promise = this.promise; 541 542 543 if (promise._state === PENDING) { 544 this._remaining--; 545 546 if (state === REJECTED) { 547 reject(promise, value); 548 } else { 549 this._result[i] = value; 550 } 551 } 552 553 if (this._remaining === 0) { 554 fulfill(promise, this._result); 555 } 556 }; 557 558 Enumerator.prototype._willSettleAt = function _willSettleAt(promise, i) { 559 var enumerator = this; 560 561 subscribe(promise, undefined, function (value) { 562 return enumerator._settledAt(FULFILLED, i, value); 563 }, function (reason) { 564 return enumerator._settledAt(REJECTED, i, reason); 565 }); 566 }; 567 568 return Enumerator; 569 }(); 570 571 /** 572 `Promise.all` accepts an array of promises, and returns a new promise which 573 is fulfilled with an array of fulfillment values for the passed promises, or 574 rejected with the reason of the first passed promise to be rejected. It casts all 575 elements of the passed iterable to promises as it runs this algorithm. 576 577 Example: 578 579 ```javascript 580 let promise1 = resolve(1); 581 let promise2 = resolve(2); 582 let promise3 = resolve(3); 583 let promises = [ promise1, promise2, promise3 ]; 584 585 Promise.all(promises).then(function(array){ 586 // The array here would be [ 1, 2, 3 ]; 587 }); 588 ``` 589 590 If any of the `promises` given to `all` are rejected, the first promise 591 that is rejected will be given as an argument to the returned promises's 592 rejection handler. For example: 593 594 Example: 595 596 ```javascript 597 let promise1 = resolve(1); 598 let promise2 = reject(new Error("2")); 599 let promise3 = reject(new Error("3")); 600 let promises = [ promise1, promise2, promise3 ]; 601 602 Promise.all(promises).then(function(array){ 603 // Code here never runs because there are rejected promises! 604 }, function(error) { 605 // error.message === "2" 606 }); 607 ``` 608 609 @method all 610 @static 611 @param {Array} entries array of promises 612 @param {String} label optional string for labeling the promise. 613 Useful for tooling. 614 @return {Promise} promise that is fulfilled when all `promises` have been 615 fulfilled, or rejected if any of them become rejected. 616 @static 617 */ 618 function all(entries) { 619 return new Enumerator(this, entries).promise; 620 } 621 622 /** 623 `Promise.race` returns a new promise which is settled in the same way as the 624 first passed promise to settle. 625 626 Example: 627 628 ```javascript 629 let promise1 = new Promise(function(resolve, reject){ 630 setTimeout(function(){ 631 resolve('promise 1'); 632 }, 200); 633 }); 634 635 let promise2 = new Promise(function(resolve, reject){ 636 setTimeout(function(){ 637 resolve('promise 2'); 638 }, 100); 639 }); 640 641 Promise.race([promise1, promise2]).then(function(result){ 642 // result === 'promise 2' because it was resolved before promise1 643 // was resolved. 644 }); 645 ``` 646 647 `Promise.race` is deterministic in that only the state of the first 648 settled promise matters. For example, even if other promises given to the 649 `promises` array argument are resolved, but the first settled promise has 650 become rejected before the other promises became fulfilled, the returned 651 promise will become rejected: 652 653 ```javascript 654 let promise1 = new Promise(function(resolve, reject){ 655 setTimeout(function(){ 656 resolve('promise 1'); 657 }, 200); 658 }); 659 660 let promise2 = new Promise(function(resolve, reject){ 661 setTimeout(function(){ 662 reject(new Error('promise 2')); 663 }, 100); 664 }); 665 666 Promise.race([promise1, promise2]).then(function(result){ 667 // Code here never runs 668 }, function(reason){ 669 // reason.message === 'promise 2' because promise 2 became rejected before 670 // promise 1 became fulfilled 671 }); 672 ``` 673 674 An example real-world use case is implementing timeouts: 675 676 ```javascript 677 Promise.race([ajax('foo.json'), timeout(5000)]) 678 ``` 679 680 @method race 681 @static 682 @param {Array} promises array of promises to observe 683 Useful for tooling. 684 @return {Promise} a promise which settles in the same way as the first passed 685 promise to settle. 686 */ 687 function race(entries) { 688 /*jshint validthis:true */ 689 var Constructor = this; 690 691 if (!isArray(entries)) { 692 return new Constructor(function (_, reject) { 693 return reject(new TypeError('You must pass an array to race.')); 694 }); 695 } else { 696 return new Constructor(function (resolve, reject) { 697 var length = entries.length; 698 for (var i = 0; i < length; i++) { 699 Constructor.resolve(entries[i]).then(resolve, reject); 700 } 701 }); 702 } 703 } 704 705 /** 706 `Promise.reject` returns a promise rejected with the passed `reason`. 707 It is shorthand for the following: 708 709 ```javascript 710 let promise = new Promise(function(resolve, reject){ 711 reject(new Error('WHOOPS')); 712 }); 713 714 promise.then(function(value){ 715 // Code here doesn't run because the promise is rejected! 716 }, function(reason){ 717 // reason.message === 'WHOOPS' 718 }); 719 ``` 720 721 Instead of writing the above, your code now simply becomes the following: 722 723 ```javascript 724 let promise = Promise.reject(new Error('WHOOPS')); 725 726 promise.then(function(value){ 727 // Code here doesn't run because the promise is rejected! 728 }, function(reason){ 729 // reason.message === 'WHOOPS' 730 }); 731 ``` 732 733 @method reject 734 @static 735 @param {Any} reason value that the returned promise will be rejected with. 736 Useful for tooling. 737 @return {Promise} a promise rejected with the given `reason`. 738 */ 739 function reject$1(reason) { 740 /*jshint validthis:true */ 741 var Constructor = this; 742 var promise = new Constructor(noop); 743 reject(promise, reason); 744 return promise; 745 } 746 747 function needsResolver() { 748 throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); 749 } 750 751 function needsNew() { 752 throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); 753 } 754 755 /** 756 Promise objects represent the eventual result of an asynchronous operation. The 757 primary way of interacting with a promise is through its `then` method, which 758 registers callbacks to receive either a promise's eventual value or the reason 759 why the promise cannot be fulfilled. 760 761 Terminology 762 ----------- 763 764 - `promise` is an object or function with a `then` method whose behavior conforms to this specification. 765 - `thenable` is an object or function that defines a `then` method. 766 - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). 767 - `exception` is a value that is thrown using the throw statement. 768 - `reason` is a value that indicates why a promise was rejected. 769 - `settled` the final resting state of a promise, fulfilled or rejected. 770 771 A promise can be in one of three states: pending, fulfilled, or rejected. 772 773 Promises that are fulfilled have a fulfillment value and are in the fulfilled 774 state. Promises that are rejected have a rejection reason and are in the 775 rejected state. A fulfillment value is never a thenable. 776 777 Promises can also be said to *resolve* a value. If this value is also a 778 promise, then the original promise's settled state will match the value's 779 settled state. So a promise that *resolves* a promise that rejects will 780 itself reject, and a promise that *resolves* a promise that fulfills will 781 itself fulfill. 782 783 784 Basic Usage: 785 ------------ 786 787 ```js 788 let promise = new Promise(function(resolve, reject) { 789 // on success 790 resolve(value); 791 792 // on failure 793 reject(reason); 794 }); 795 796 promise.then(function(value) { 797 // on fulfillment 798 }, function(reason) { 799 // on rejection 800 }); 801 ``` 802 803 Advanced Usage: 804 --------------- 805 806 Promises shine when abstracting away asynchronous interactions such as 807 `XMLHttpRequest`s. 808 809 ```js 810 function getJSON(url) { 811 return new Promise(function(resolve, reject){ 812 let xhr = new XMLHttpRequest(); 813 814 xhr.open('GET', url); 815 xhr.onreadystatechange = handler; 816 xhr.responseType = 'json'; 817 xhr.setRequestHeader('Accept', 'application/json'); 818 xhr.send(); 819 820 function handler() { 821 if (this.readyState === this.DONE) { 822 if (this.status === 200) { 823 resolve(this.response); 824 } else { 825 reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']')); 826 } 827 } 828 }; 829 }); 830 } 831 832 getJSON('/posts.json').then(function(json) { 833 // on fulfillment 834 }, function(reason) { 835 // on rejection 836 }); 837 ``` 838 839 Unlike callbacks, promises are great composable primitives. 840 841 ```js 842 Promise.all([ 843 getJSON('/posts'), 844 getJSON('/comments') 845 ]).then(function(values){ 846 values[0] // => postsJSON 847 values[1] // => commentsJSON 848 849 return values; 850 }); 851 ``` 852 853 @class Promise 854 @param {Function} resolver 855 Useful for tooling. 856 @constructor 857 */ 858 859 var Promise$1 = function () { 860 function Promise(resolver) { 861 this[PROMISE_ID] = nextId(); 862 this._result = this._state = undefined; 863 this._subscribers = []; 864 865 if (noop !== resolver) { 866 typeof resolver !== 'function' && needsResolver(); 867 this instanceof Promise ? initializePromise(this, resolver) : needsNew(); 868 } 869 } 870 871 /** 872 The primary way of interacting with a promise is through its `then` method, 873 which registers callbacks to receive either a promise's eventual value or the 874 reason why the promise cannot be fulfilled. 875 ```js 876 findUser().then(function(user){ 877 // user is available 878 }, function(reason){ 879 // user is unavailable, and you are given the reason why 880 }); 881 ``` 882 Chaining 883 -------- 884 The return value of `then` is itself a promise. This second, 'downstream' 885 promise is resolved with the return value of the first promise's fulfillment 886 or rejection handler, or rejected if the handler throws an exception. 887 ```js 888 findUser().then(function (user) { 889 return user.name; 890 }, function (reason) { 891 return 'default name'; 892 }).then(function (userName) { 893 // If `findUser` fulfilled, `userName` will be the user's name, otherwise it 894 // will be `'default name'` 895 }); 896 findUser().then(function (user) { 897 throw new Error('Found user, but still unhappy'); 898 }, function (reason) { 899 throw new Error('`findUser` rejected and we're unhappy'); 900 }).then(function (value) { 901 // never reached 902 }, function (reason) { 903 // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'. 904 // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'. 905 }); 906 ``` 907 If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. 908 ```js 909 findUser().then(function (user) { 910 throw new PedagogicalException('Upstream error'); 911 }).then(function (value) { 912 // never reached 913 }).then(function (value) { 914 // never reached 915 }, function (reason) { 916 // The `PedgagocialException` is propagated all the way down to here 917 }); 918 ``` 919 Assimilation 920 ------------ 921 Sometimes the value you want to propagate to a downstream promise can only be 922 retrieved asynchronously. This can be achieved by returning a promise in the 923 fulfillment or rejection handler. The downstream promise will then be pending 924 until the returned promise is settled. This is called *assimilation*. 925 ```js 926 findUser().then(function (user) { 927 return findCommentsByAuthor(user); 928 }).then(function (comments) { 929 // The user's comments are now available 930 }); 931 ``` 932 If the assimliated promise rejects, then the downstream promise will also reject. 933 ```js 934 findUser().then(function (user) { 935 return findCommentsByAuthor(user); 936 }).then(function (comments) { 937 // If `findCommentsByAuthor` fulfills, we'll have the value here 938 }, function (reason) { 939 // If `findCommentsByAuthor` rejects, we'll have the reason here 940 }); 941 ``` 942 Simple Example 943 -------------- 944 Synchronous Example 945 ```javascript 946 let result; 947 try { 948 result = findResult(); 949 // success 950 } catch(reason) { 951 // failure 952 } 953 ``` 954 Errback Example 955 ```js 956 findResult(function(result, err){ 957 if (err) { 958 // failure 959 } else { 960 // success 961 } 962 }); 963 ``` 964 Promise Example; 965 ```javascript 966 findResult().then(function(result){ 967 // success 968 }, function(reason){ 969 // failure 970 }); 971 ``` 972 Advanced Example 973 -------------- 974 Synchronous Example 975 ```javascript 976 let author, books; 977 try { 978 author = findAuthor(); 979 books = findBooksByAuthor(author); 980 // success 981 } catch(reason) { 982 // failure 983 } 984 ``` 985 Errback Example 986 ```js 987 function foundBooks(books) { 988 } 989 function failure(reason) { 990 } 991 findAuthor(function(author, err){ 992 if (err) { 993 failure(err); 994 // failure 995 } else { 996 try { 997 findBoooksByAuthor(author, function(books, err) { 998 if (err) { 999 failure(err); 1000 } else { 1001 try { 1002 foundBooks(books); 1003 } catch(reason) { 1004 failure(reason); 1005 } 1006 } 1007 }); 1008 } catch(error) { 1009 failure(err); 1010 } 1011 // success 1012 } 1013 }); 1014 ``` 1015 Promise Example; 1016 ```javascript 1017 findAuthor(). 1018 then(findBooksByAuthor). 1019 then(function(books){ 1020 // found books 1021 }).catch(function(reason){ 1022 // something went wrong 1023 }); 1024 ``` 1025 @method then 1026 @param {Function} onFulfilled 1027 @param {Function} onRejected 1028 Useful for tooling. 1029 @return {Promise} 1030 */ 1031 1032 /** 1033 `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same 1034 as the catch block of a try/catch statement. 1035 ```js 1036 function findAuthor(){ 1037 throw new Error('couldn't find that author'); 1038 } 1039 // synchronous 1040 try { 1041 findAuthor(); 1042 } catch(reason) { 1043 // something went wrong 1044 } 1045 // async with promises 1046 findAuthor().catch(function(reason){ 1047 // something went wrong 1048 }); 1049 ``` 1050 @method catch 1051 @param {Function} onRejection 1052 Useful for tooling. 1053 @return {Promise} 1054 */ 1055 1056 1057 Promise.prototype.catch = function _catch(onRejection) { 1058 return this.then(null, onRejection); 1059 }; 1060 1061 /** 1062 `finally` will be invoked regardless of the promise's fate just as native 1063 try/catch/finally behaves 1064 1065 Synchronous example: 1066 1067 ```js 1068 findAuthor() { 1069 if (Math.random() > 0.5) { 1070 throw new Error(); 1071 } 1072 return new Author(); 1073 } 1074 1075 try { 1076 return findAuthor(); // succeed or fail 1077 } catch(error) { 1078 return findOtherAuther(); 1079 } finally { 1080 // always runs 1081 // doesn't affect the return value 1082 } 1083 ``` 1084 1085 Asynchronous example: 1086 1087 ```js 1088 findAuthor().catch(function(reason){ 1089 return findOtherAuther(); 1090 }).finally(function(){ 1091 // author was either found, or not 1092 }); 1093 ``` 1094 1095 @method finally 1096 @param {Function} callback 1097 @return {Promise} 1098 */ 1099 1100 1101 Promise.prototype.finally = function _finally(callback) { 1102 var promise = this; 1103 var constructor = promise.constructor; 1104 1105 if (isFunction(callback)) { 1106 return promise.then(function (value) { 1107 return constructor.resolve(callback()).then(function () { 1108 return value; 1109 }); 1110 }, function (reason) { 1111 return constructor.resolve(callback()).then(function () { 1112 throw reason; 1113 }); 1114 }); 1115 } 1116 1117 return promise.then(callback, callback); 1118 }; 1119 1120 return Promise; 1121 }(); 1122 1123 Promise$1.prototype.then = then; 1124 Promise$1.all = all; 1125 Promise$1.race = race; 1126 Promise$1.resolve = resolve$1; 1127 Promise$1.reject = reject$1; 1128 Promise$1._setScheduler = setScheduler; 1129 Promise$1._setAsap = setAsap; 1130 Promise$1._asap = asap; 1131 1132 /*global self*/ 1133 function polyfill() { 1134 var local = void 0; 1135 1136 if (typeof global !== 'undefined') { 1137 local = global; 1138 } else if (typeof self !== 'undefined') { 1139 local = self; 1140 } else { 1141 try { 1142 local = Function('return this')(); 1143 } catch (e) { 1144 throw new Error('polyfill failed because global object is unavailable in this environment'); 1145 } 1146 } 1147 1148 var P = local.Promise; 1149 1150 if (P) { 1151 var promiseToString = null; 1152 try { 1153 promiseToString = Object.prototype.toString.call(P.resolve()); 1154 } catch (e) { 1155 // silently ignored 1156 } 1157 1158 if (promiseToString === '[object Promise]' && !P.cast) { 1159 return; 1160 } 1161 } 1162 1163 local.Promise = Promise$1; 1164 } 1165 1166 // Strange compat.. 1167 Promise$1.polyfill = polyfill; 1168 Promise$1.Promise = Promise$1; 1169 1170 return Promise$1; 1171 1172 }))); 1173 1174 1175 1176 1177 1178 }).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {}) 1179 1180 },{"_process":3}],2:[function(_dereq_,module,exports){ 1181 // Copyright Joyent, Inc. and other Node contributors. 1182 // 1183 // Permission is hereby granted, free of charge, to any person obtaining a 1184 // copy of this software and associated documentation files (the 1185 // "Software"), to deal in the Software without restriction, including 1186 // without limitation the rights to use, copy, modify, merge, publish, 1187 // distribute, sublicense, and/or sell copies of the Software, and to permit 1188 // persons to whom the Software is furnished to do so, subject to the 1189 // following conditions: 1190 // 1191 // The above copyright notice and this permission notice shall be included 1192 // in all copies or substantial portions of the Software. 1193 // 1194 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 1195 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1196 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 1197 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 1198 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 1199 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 1200 // USE OR OTHER DEALINGS IN THE SOFTWARE. 1201 1202 function EventEmitter() { 1203 this._events = this._events || {}; 1204 this._maxListeners = this._maxListeners || undefined; 1205 } 1206 module.exports = EventEmitter; 1207 1208 // Backwards-compat with node 0.10.x 1209 EventEmitter.EventEmitter = EventEmitter; 1210 1211 EventEmitter.prototype._events = undefined; 1212 EventEmitter.prototype._maxListeners = undefined; 1213 1214 // By default EventEmitters will print a warning if more than 10 listeners are 1215 // added to it. This is a useful default which helps finding memory leaks. 1216 EventEmitter.defaultMaxListeners = 10; 1217 1218 // Obviously not all Emitters should be limited to 10. This function allows 1219 // that to be increased. Set to zero for unlimited. 1220 EventEmitter.prototype.setMaxListeners = function(n) { 1221 if (!isNumber(n) || n < 0 || isNaN(n)) 1222 throw TypeError('n must be a positive number'); 1223 this._maxListeners = n; 1224 return this; 1225 }; 1226 1227 EventEmitter.prototype.emit = function(type) { 1228 var er, handler, len, args, i, listeners; 1229 1230 if (!this._events) 1231 this._events = {}; 1232 1233 // If there is no 'error' event listener then throw. 1234 if (type === 'error') { 1235 if (!this._events.error || 1236 (isObject(this._events.error) && !this._events.error.length)) { 1237 er = arguments[1]; 1238 if (er instanceof Error) { 1239 throw er; // Unhandled 'error' event 1240 } else { 1241 // At least give some kind of context to the user 1242 var err = new Error('Uncaught, unspecified "error" event. (' + er + ')'); 1243 err.context = er; 1244 throw err; 1245 } 1246 } 1247 } 1248 1249 handler = this._events[type]; 1250 1251 if (isUndefined(handler)) 1252 return false; 1253 1254 if (isFunction(handler)) { 1255 switch (arguments.length) { 1256 // fast cases 1257 case 1: 1258 handler.call(this); 1259 break; 1260 case 2: 1261 handler.call(this, arguments[1]); 1262 break; 1263 case 3: 1264 handler.call(this, arguments[1], arguments[2]); 1265 break; 1266 // slower 1267 default: 1268 args = Array.prototype.slice.call(arguments, 1); 1269 handler.apply(this, args); 1270 } 1271 } else if (isObject(handler)) { 1272 args = Array.prototype.slice.call(arguments, 1); 1273 listeners = handler.slice(); 1274 len = listeners.length; 1275 for (i = 0; i < len; i++) 1276 listeners[i].apply(this, args); 1277 } 1278 1279 return true; 1280 }; 1281 1282 EventEmitter.prototype.addListener = function(type, listener) { 1283 var m; 1284 1285 if (!isFunction(listener)) 1286 throw TypeError('listener must be a function'); 1287 1288 if (!this._events) 1289 this._events = {}; 1290 1291 // To avoid recursion in the case that type === "newListener"! Before 1292 // adding it to the listeners, first emit "newListener". 1293 if (this._events.newListener) 1294 this.emit('newListener', type, 1295 isFunction(listener.listener) ? 1296 listener.listener : listener); 1297 1298 if (!this._events[type]) 1299 // Optimize the case of one listener. Don't need the extra array object. 1300 this._events[type] = listener; 1301 else if (isObject(this._events[type])) 1302 // If we've already got an array, just append. 1303 this._events[type].push(listener); 1304 else 1305 // Adding the second element, need to change to array. 1306 this._events[type] = [this._events[type], listener]; 1307 1308 // Check for listener leak 1309 if (isObject(this._events[type]) && !this._events[type].warned) { 1310 if (!isUndefined(this._maxListeners)) { 1311 m = this._maxListeners; 1312 } else { 1313 m = EventEmitter.defaultMaxListeners; 1314 } 1315 1316 if (m && m > 0 && this._events[type].length > m) { 1317 this._events[type].warned = true; 1318 console.error('(node) warning: possible EventEmitter memory ' + 1319 'leak detected. %d listeners added. ' + 1320 'Use emitter.setMaxListeners() to increase limit.', 1321 this._events[type].length); 1322 if (typeof console.trace === 'function') { 1323 // not supported in IE 10 1324 console.trace(); 1325 } 1326 } 1327 } 1328 1329 return this; 1330 }; 1331 1332 EventEmitter.prototype.on = EventEmitter.prototype.addListener; 1333 1334 EventEmitter.prototype.once = function(type, listener) { 1335 if (!isFunction(listener)) 1336 throw TypeError('listener must be a function'); 1337 1338 var fired = false; 1339 1340 function g() { 1341 this.removeListener(type, g); 1342 1343 if (!fired) { 1344 fired = true; 1345 listener.apply(this, arguments); 1346 } 1347 } 1348 1349 g.listener = listener; 1350 this.on(type, g); 1351 1352 return this; 1353 }; 1354 1355 // emits a 'removeListener' event iff the listener was removed 1356 EventEmitter.prototype.removeListener = function(type, listener) { 1357 var list, position, length, i; 1358 1359 if (!isFunction(listener)) 1360 throw TypeError('listener must be a function'); 1361 1362 if (!this._events || !this._events[type]) 1363 return this; 1364 1365 list = this._events[type]; 1366 length = list.length; 1367 position = -1; 1368 1369 if (list === listener || 1370 (isFunction(list.listener) && list.listener === listener)) { 1371 delete this._events[type]; 1372 if (this._events.removeListener) 1373 this.emit('removeListener', type, listener); 1374 1375 } else if (isObject(list)) { 1376 for (i = length; i-- > 0;) { 1377 if (list[i] === listener || 1378 (list[i].listener && list[i].listener === listener)) { 1379 position = i; 1380 break; 1381 } 1382 } 1383 1384 if (position < 0) 1385 return this; 1386 1387 if (list.length === 1) { 1388 list.length = 0; 1389 delete this._events[type]; 1390 } else { 1391 list.splice(position, 1); 1392 } 1393 1394 if (this._events.removeListener) 1395 this.emit('removeListener', type, listener); 1396 } 1397 1398 return this; 1399 }; 1400 1401 EventEmitter.prototype.removeAllListeners = function(type) { 1402 var key, listeners; 1403 1404 if (!this._events) 1405 return this; 1406 1407 // not listening for removeListener, no need to emit 1408 if (!this._events.removeListener) { 1409 if (arguments.length === 0) 1410 this._events = {}; 1411 else if (this._events[type]) 1412 delete this._events[type]; 1413 return this; 1414 } 1415 1416 // emit removeListener for all listeners on all events 1417 if (arguments.length === 0) { 1418 for (key in this._events) { 1419 if (key === 'removeListener') continue; 1420 this.removeAllListeners(key); 1421 } 1422 this.removeAllListeners('removeListener'); 1423 this._events = {}; 1424 return this; 1425 } 1426 1427 listeners = this._events[type]; 1428 1429 if (isFunction(listeners)) { 1430 this.removeListener(type, listeners); 1431 } else if (listeners) { 1432 // LIFO order 1433 while (listeners.length) 1434 this.removeListener(type, listeners[listeners.length - 1]); 1435 } 1436 delete this._events[type]; 1437 1438 return this; 1439 }; 1440 1441 EventEmitter.prototype.listeners = function(type) { 1442 var ret; 1443 if (!this._events || !this._events[type]) 1444 ret = []; 1445 else if (isFunction(this._events[type])) 1446 ret = [this._events[type]]; 1447 else 1448 ret = this._events[type].slice(); 1449 return ret; 1450 }; 1451 1452 EventEmitter.prototype.listenerCount = function(type) { 1453 if (this._events) { 1454 var evlistener = this._events[type]; 1455 1456 if (isFunction(evlistener)) 1457 return 1; 1458 else if (evlistener) 1459 return evlistener.length; 1460 } 1461 return 0; 1462 }; 1463 1464 EventEmitter.listenerCount = function(emitter, type) { 1465 return emitter.listenerCount(type); 1466 }; 1467 1468 function isFunction(arg) { 1469 return typeof arg === 'function'; 1470 } 1471 1472 function isNumber(arg) { 1473 return typeof arg === 'number'; 1474 } 1475 1476 function isObject(arg) { 1477 return typeof arg === 'object' && arg !== null; 1478 } 1479 1480 function isUndefined(arg) { 1481 return arg === void 0; 1482 } 1483 1484 },{}],3:[function(_dereq_,module,exports){ 1485 // shim for using process in browser 1486 var process = module.exports = {}; 1487 1488 // cached from whatever global is present so that test runners that stub it 1489 // don't break things. But we need to wrap it in a try catch in case it is 1490 // wrapped in strict mode code which doesn't define any globals. It's inside a 1491 // function because try/catches deoptimize in certain engines. 1492 1493 var cachedSetTimeout; 1494 var cachedClearTimeout; 1495 1496 function defaultSetTimout() { 1497 throw new Error('setTimeout has not been defined'); 1498 } 1499 function defaultClearTimeout () { 1500 throw new Error('clearTimeout has not been defined'); 1501 } 1502 (function () { 1503 try { 1504 if (typeof setTimeout === 'function') { 1505 cachedSetTimeout = setTimeout; 1506 } else { 1507 cachedSetTimeout = defaultSetTimout; 1508 } 1509 } catch (e) { 1510 cachedSetTimeout = defaultSetTimout; 1511 } 1512 try { 1513 if (typeof clearTimeout === 'function') { 1514 cachedClearTimeout = clearTimeout; 1515 } else { 1516 cachedClearTimeout = defaultClearTimeout; 1517 } 1518 } catch (e) { 1519 cachedClearTimeout = defaultClearTimeout; 1520 } 1521 } ()) 1522 function runTimeout(fun) { 1523 if (cachedSetTimeout === setTimeout) { 1524 //normal enviroments in sane situations 1525 return setTimeout(fun, 0); 1526 } 1527 // if setTimeout wasn't available but was latter defined 1528 if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) { 1529 cachedSetTimeout = setTimeout; 1530 return setTimeout(fun, 0); 1531 } 1532 try { 1533 // when when somebody has screwed with setTimeout but no I.E. maddness 1534 return cachedSetTimeout(fun, 0); 1535 } catch(e){ 1536 try { 1537 // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally 1538 return cachedSetTimeout.call(null, fun, 0); 1539 } catch(e){ 1540 // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error 1541 return cachedSetTimeout.call(this, fun, 0); 1542 } 1543 } 1544 1545 1546 } 1547 function runClearTimeout(marker) { 1548 if (cachedClearTimeout === clearTimeout) { 1549 //normal enviroments in sane situations 1550 return clearTimeout(marker); 1551 } 1552 // if clearTimeout wasn't available but was latter defined 1553 if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) { 1554 cachedClearTimeout = clearTimeout; 1555 return clearTimeout(marker); 1556 } 1557 try { 1558 // when when somebody has screwed with setTimeout but no I.E. maddness 1559 return cachedClearTimeout(marker); 1560 } catch (e){ 1561 try { 1562 // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally 1563 return cachedClearTimeout.call(null, marker); 1564 } catch (e){ 1565 // same as above but when it's a version of I.E. that must have the global object for 'this', hopfully our context correct otherwise it will throw a global error. 1566 // Some versions of I.E. have different rules for clearTimeout vs setTimeout 1567 return cachedClearTimeout.call(this, marker); 1568 } 1569 } 1570 1571 1572 1573 } 1574 var queue = []; 1575 var draining = false; 1576 var currentQueue; 1577 var queueIndex = -1; 1578 1579 function cleanUpNextTick() { 1580 if (!draining || !currentQueue) { 1581 return; 1582 } 1583 draining = false; 1584 if (currentQueue.length) { 1585 queue = currentQueue.concat(queue); 1586 } else { 1587 queueIndex = -1; 1588 } 1589 if (queue.length) { 1590 drainQueue(); 1591 } 1592 } 1593 1594 function drainQueue() { 1595 if (draining) { 1596 return; 1597 } 1598 var timeout = runTimeout(cleanUpNextTick); 1599 draining = true; 1600 1601 var len = queue.length; 1602 while(len) { 1603 currentQueue = queue; 1604 queue = []; 1605 while (++queueIndex < len) { 1606 if (currentQueue) { 1607 currentQueue[queueIndex].run(); 1608 } 1609 } 1610 queueIndex = -1; 1611 len = queue.length; 1612 } 1613 currentQueue = null; 1614 draining = false; 1615 runClearTimeout(timeout); 1616 } 1617 1618 process.nextTick = function (fun) { 1619 var args = new Array(arguments.length - 1); 1620 if (arguments.length > 1) { 1621 for (var i = 1; i < arguments.length; i++) { 1622 args[i - 1] = arguments[i]; 1623 } 1624 } 1625 queue.push(new Item(fun, args)); 1626 if (queue.length === 1 && !draining) { 1627 runTimeout(drainQueue); 1628 } 1629 }; 1630 1631 // v8 likes predictible objects 1632 function Item(fun, array) { 1633 this.fun = fun; 1634 this.array = array; 1635 } 1636 Item.prototype.run = function () { 1637 this.fun.apply(null, this.array); 1638 }; 1639 process.title = 'browser'; 1640 process.browser = true; 1641 process.env = {}; 1642 process.argv = []; 1643 process.version = ''; // empty string to avoid regexp issues 1644 process.versions = {}; 1645 1646 function noop() {} 1647 1648 process.on = noop; 1649 process.addListener = noop; 1650 process.once = noop; 1651 process.off = noop; 1652 process.removeListener = noop; 1653 process.removeAllListeners = noop; 1654 process.emit = noop; 1655 process.prependListener = noop; 1656 process.prependOnceListener = noop; 1657 1658 process.listeners = function (name) { return [] } 1659 1660 process.binding = function (name) { 1661 throw new Error('process.binding is not supported'); 1662 }; 1663 1664 process.cwd = function () { return '/' }; 1665 process.chdir = function (dir) { 1666 throw new Error('process.chdir is not supported'); 1667 }; 1668 process.umask = function() { return 0; }; 1669 1670 },{}],4:[function(_dereq_,module,exports){ 1671 var bundleFn = arguments[3]; 1672 var sources = arguments[4]; 1673 var cache = arguments[5]; 1674 1675 var stringify = JSON.stringify; 1676 1677 module.exports = function (fn, options) { 1678 var wkey; 1679 var cacheKeys = Object.keys(cache); 1680 1681 for (var i = 0, l = cacheKeys.length; i < l; i++) { 1682 var key = cacheKeys[i]; 1683 var exp = cache[key].exports; 1684 // Using babel as a transpiler to use esmodule, the export will always 1685 // be an object with the default export as a property of it. To ensure 1686 // the existing api and babel esmodule exports are both supported we 1687 // check for both 1688 if (exp === fn || exp && exp.default === fn) { 1689 wkey = key; 1690 break; 1691 } 1692 } 1693 1694 if (!wkey) { 1695 wkey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16); 1696 var wcache = {}; 1697 for (var i = 0, l = cacheKeys.length; i < l; i++) { 1698 var key = cacheKeys[i]; 1699 wcache[key] = key; 1700 } 1701 sources[wkey] = [ 1702 'function(require,module,exports){' + fn + '(self); }', 1703 wcache 1704 ]; 1705 } 1706 var skey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16); 1707 1708 var scache = {}; scache[wkey] = wkey; 1709 sources[skey] = [ 1710 'function(require,module,exports){' + 1711 // try to call default if defined to also support babel esmodule exports 1712 'var f = require(' + stringify(wkey) + ');' + 1713 '(f.default ? f.default : f)(self);' + 1714 '}', 1715 scache 1716 ]; 1717 1718 var workerSources = {}; 1719 resolveSources(skey); 1720 1721 function resolveSources(key) { 1722 workerSources[key] = true; 1723 1724 for (var depPath in sources[key][1]) { 1725 var depKey = sources[key][1][depPath]; 1726 if (!workerSources[depKey]) { 1727 resolveSources(depKey); 1728 } 1729 } 1730 } 1731 1732 var src = '(' + bundleFn + ')({' 1733 + Object.keys(workerSources).map(function (key) { 1734 return stringify(key) + ':[' 1735 + sources[key][0] 1736 + ',' + stringify(sources[key][1]) + ']' 1737 ; 1738 }).join(',') 1739 + '},{},[' + stringify(skey) + '])' 1740 ; 1741 1742 var URL = window.URL || window.webkitURL || window.mozURL || window.msURL; 1743 1744 var blob = new Blob([src], { type: 'text/javascript' }); 1745 if (options && options.bare) { return blob; } 1746 var workerUrl = URL.createObjectURL(blob); 1747 var worker = new Worker(workerUrl); 1748 worker.objectURL = workerUrl; 1749 return worker; 1750 }; 1751 1752 },{}],5:[function(_dereq_,module,exports){ 1753 'use strict'; 1754 1755 Object.defineProperty(exports, "__esModule", { 1756 value: true 1757 }); 1758 exports.createDefaultConfig = createDefaultConfig; 1759 /* 1760 * Copyright (C) 2016 Bilibili. All Rights Reserved. 1761 * 1762 * @author zheng qian <xqq@xqq.im> 1763 * 1764 * Licensed under the Apache License, Version 2.0 (the "License"); 1765 * you may not use this file except in compliance with the License. 1766 * You may obtain a copy of the License at 1767 * 1768 * http://www.apache.org/licenses/LICENSE-2.0 1769 * 1770 * Unless required by applicable law or agreed to in writing, software 1771 * distributed under the License is distributed on an "AS IS" BASIS, 1772 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1773 * See the License for the specific language governing permissions and 1774 * limitations under the License. 1775 */ 1776 1777 var defaultConfig = exports.defaultConfig = { 1778 enableWorker: false, 1779 enableStashBuffer: true, 1780 stashInitialSize: undefined, 1781 1782 isLive: false, 1783 1784 lazyLoad: true, 1785 lazyLoadMaxDuration: 3 * 60, 1786 lazyLoadRecoverDuration: 30, 1787 deferLoadAfterSourceOpen: true, 1788 1789 // autoCleanupSourceBuffer: default as false, leave unspecified 1790 autoCleanupMaxBackwardDuration: 3 * 60, 1791 autoCleanupMinBackwardDuration: 2 * 60, 1792 1793 statisticsInfoReportInterval: 600, 1794 1795 fixAudioTimestampGap: true, 1796 1797 accurateSeek: false, 1798 seekType: 'range', // [range, param, custom] 1799 seekParamStart: 'bstart', 1800 seekParamEnd: 'bend', 1801 rangeLoadZeroStart: false, 1802 customSeekHandler: undefined, 1803 reuseRedirectedURL: false, 1804 // referrerPolicy: leave as unspecified 1805 1806 headers: undefined, 1807 customLoader: undefined 1808 }; 1809 1810 function createDefaultConfig() { 1811 return Object.assign({}, defaultConfig); 1812 } 1813 1814 },{}],6:[function(_dereq_,module,exports){ 1815 'use strict'; 1816 1817 Object.defineProperty(exports, "__esModule", { 1818 value: true 1819 }); 1820 1821 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 1822 * Copyright (C) 2016 Bilibili. All Rights Reserved. 1823 * 1824 * @author zheng qian <xqq@xqq.im> 1825 * 1826 * Licensed under the Apache License, Version 2.0 (the "License"); 1827 * you may not use this file except in compliance with the License. 1828 * You may obtain a copy of the License at 1829 * 1830 * http://www.apache.org/licenses/LICENSE-2.0 1831 * 1832 * Unless required by applicable law or agreed to in writing, software 1833 * distributed under the License is distributed on an "AS IS" BASIS, 1834 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1835 * See the License for the specific language governing permissions and 1836 * limitations under the License. 1837 */ 1838 1839 var _ioController = _dereq_('../io/io-controller.js'); 1840 1841 var _ioController2 = _interopRequireDefault(_ioController); 1842 1843 var _config = _dereq_('../config.js'); 1844 1845 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 1846 1847 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 1848 1849 var Features = function () { 1850 function Features() { 1851 _classCallCheck(this, Features); 1852 } 1853 1854 _createClass(Features, null, [{ 1855 key: 'supportMSEH264Playback', 1856 value: function supportMSEH264Playback() { 1857 return window.MediaSource && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"'); 1858 } 1859 }, { 1860 key: 'supportNetworkStreamIO', 1861 value: function supportNetworkStreamIO() { 1862 var ioctl = new _ioController2.default({}, (0, _config.createDefaultConfig)()); 1863 var loaderType = ioctl.loaderType; 1864 ioctl.destroy(); 1865 return loaderType == 'fetch-stream-loader' || loaderType == 'xhr-moz-chunked-loader'; 1866 } 1867 }, { 1868 key: 'getNetworkLoaderTypeName', 1869 value: function getNetworkLoaderTypeName() { 1870 var ioctl = new _ioController2.default({}, (0, _config.createDefaultConfig)()); 1871 var loaderType = ioctl.loaderType; 1872 ioctl.destroy(); 1873 return loaderType; 1874 } 1875 }, { 1876 key: 'supportNativeMediaPlayback', 1877 value: function supportNativeMediaPlayback(mimeType) { 1878 if (Features.videoElement == undefined) { 1879 Features.videoElement = window.document.createElement('video'); 1880 } 1881 var canPlay = Features.videoElement.canPlayType(mimeType); 1882 return canPlay === 'probably' || canPlay == 'maybe'; 1883 } 1884 }, { 1885 key: 'getFeatureList', 1886 value: function getFeatureList() { 1887 var features = { 1888 mseFlvPlayback: false, 1889 mseLiveFlvPlayback: false, 1890 networkStreamIO: false, 1891 networkLoaderName: '', 1892 nativeMP4H264Playback: false, 1893 nativeWebmVP8Playback: false, 1894 nativeWebmVP9Playback: false 1895 }; 1896 1897 features.mseFlvPlayback = Features.supportMSEH264Playback(); 1898 features.networkStreamIO = Features.supportNetworkStreamIO(); 1899 features.networkLoaderName = Features.getNetworkLoaderTypeName(); 1900 features.mseLiveFlvPlayback = features.mseFlvPlayback && features.networkStreamIO; 1901 features.nativeMP4H264Playback = Features.supportNativeMediaPlayback('video/mp4; codecs="avc1.42001E, mp4a.40.2"'); 1902 features.nativeWebmVP8Playback = Features.supportNativeMediaPlayback('video/webm; codecs="vp8.0, vorbis"'); 1903 features.nativeWebmVP9Playback = Features.supportNativeMediaPlayback('video/webm; codecs="vp9"'); 1904 1905 return features; 1906 } 1907 }]); 1908 1909 return Features; 1910 }(); 1911 1912 exports.default = Features; 1913 1914 },{"../config.js":5,"../io/io-controller.js":23}],7:[function(_dereq_,module,exports){ 1915 "use strict"; 1916 1917 Object.defineProperty(exports, "__esModule", { 1918 value: true 1919 }); 1920 1921 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 1922 1923 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 1924 1925 /* 1926 * Copyright (C) 2016 Bilibili. All Rights Reserved. 1927 * 1928 * @author zheng qian <xqq@xqq.im> 1929 * 1930 * Licensed under the Apache License, Version 2.0 (the "License"); 1931 * you may not use this file except in compliance with the License. 1932 * You may obtain a copy of the License at 1933 * 1934 * http://www.apache.org/licenses/LICENSE-2.0 1935 * 1936 * Unless required by applicable law or agreed to in writing, software 1937 * distributed under the License is distributed on an "AS IS" BASIS, 1938 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1939 * See the License for the specific language governing permissions and 1940 * limitations under the License. 1941 */ 1942 1943 var MediaInfo = function () { 1944 function MediaInfo() { 1945 _classCallCheck(this, MediaInfo); 1946 1947 this.mimeType = null; 1948 this.duration = null; 1949 1950 this.hasAudio = null; 1951 this.hasVideo = null; 1952 this.audioCodec = null; 1953 this.videoCodec = null; 1954 this.audioDataRate = null; 1955 this.videoDataRate = null; 1956 1957 this.audioSampleRate = null; 1958 this.audioChannelCount = null; 1959 1960 this.width = null; 1961 this.height = null; 1962 this.fps = null; 1963 this.profile = null; 1964 this.level = null; 1965 this.refFrames = null; 1966 this.chromaFormat = null; 1967 this.sarNum = null; 1968 this.sarDen = null; 1969 1970 this.metadata = null; 1971 this.segments = null; // MediaInfo[] 1972 this.segmentCount = null; 1973 this.hasKeyframesIndex = null; 1974 this.keyframesIndex = null; 1975 } 1976 1977 _createClass(MediaInfo, [{ 1978 key: "isComplete", 1979 value: function isComplete() { 1980 var audioInfoComplete = this.hasAudio === false || this.hasAudio === true && this.audioCodec != null && this.audioSampleRate != null && this.audioChannelCount != null; 1981 1982 var videoInfoComplete = this.hasVideo === false || this.hasVideo === true && this.videoCodec != null && this.width != null && this.height != null && this.fps != null && this.profile != null && this.level != null && this.refFrames != null && this.chromaFormat != null && this.sarNum != null && this.sarDen != null; 1983 1984 // keyframesIndex may not be present 1985 return this.mimeType != null && this.duration != null && this.metadata != null && this.hasKeyframesIndex != null && audioInfoComplete && videoInfoComplete; 1986 } 1987 }, { 1988 key: "isSeekable", 1989 value: function isSeekable() { 1990 return this.hasKeyframesIndex === true; 1991 } 1992 }, { 1993 key: "getNearestKeyframe", 1994 value: function getNearestKeyframe(milliseconds) { 1995 if (this.keyframesIndex == null) { 1996 return null; 1997 } 1998 1999 var table = this.keyframesIndex; 2000 var keyframeIdx = this._search(table.times, milliseconds); 2001 2002 return { 2003 index: keyframeIdx, 2004 milliseconds: table.times[keyframeIdx], 2005 fileposition: table.filepositions[keyframeIdx] 2006 }; 2007 } 2008 }, { 2009 key: "_search", 2010 value: function _search(list, value) { 2011 var idx = 0; 2012 2013 var last = list.length - 1; 2014 var mid = 0; 2015 var lbound = 0; 2016 var ubound = last; 2017 2018 if (value < list[0]) { 2019 idx = 0; 2020 lbound = ubound + 1; // skip search 2021 } 2022 2023 while (lbound <= ubound) { 2024 mid = lbound + Math.floor((ubound - lbound) / 2); 2025 if (mid === last || value >= list[mid] && value < list[mid + 1]) { 2026 idx = mid; 2027 break; 2028 } else if (list[mid] < value) { 2029 lbound = mid + 1; 2030 } else { 2031 ubound = mid - 1; 2032 } 2033 } 2034 2035 return idx; 2036 } 2037 }]); 2038 2039 return MediaInfo; 2040 }(); 2041 2042 exports.default = MediaInfo; 2043 2044 },{}],8:[function(_dereq_,module,exports){ 2045 "use strict"; 2046 2047 Object.defineProperty(exports, "__esModule", { 2048 value: true 2049 }); 2050 2051 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 2052 2053 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 2054 2055 /* 2056 * Copyright (C) 2016 Bilibili. All Rights Reserved. 2057 * 2058 * @author zheng qian <xqq@xqq.im> 2059 * 2060 * Licensed under the Apache License, Version 2.0 (the "License"); 2061 * you may not use this file except in compliance with the License. 2062 * You may obtain a copy of the License at 2063 * 2064 * http://www.apache.org/licenses/LICENSE-2.0 2065 * 2066 * Unless required by applicable law or agreed to in writing, software 2067 * distributed under the License is distributed on an "AS IS" BASIS, 2068 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2069 * See the License for the specific language governing permissions and 2070 * limitations under the License. 2071 */ 2072 2073 // Represents an media sample (audio / video) 2074 var SampleInfo = exports.SampleInfo = function SampleInfo(dts, pts, duration, originalDts, isSync) { 2075 _classCallCheck(this, SampleInfo); 2076 2077 this.dts = dts; 2078 this.pts = pts; 2079 this.duration = duration; 2080 this.originalDts = originalDts; 2081 this.isSyncPoint = isSync; 2082 this.fileposition = null; 2083 }; 2084 2085 // Media Segment concept is defined in Media Source Extensions spec. 2086 // Particularly in ISO BMFF format, an Media Segment contains a moof box followed by a mdat box. 2087 2088 2089 var MediaSegmentInfo = exports.MediaSegmentInfo = function () { 2090 function MediaSegmentInfo() { 2091 _classCallCheck(this, MediaSegmentInfo); 2092 2093 this.beginDts = 0; 2094 this.endDts = 0; 2095 this.beginPts = 0; 2096 this.endPts = 0; 2097 this.originalBeginDts = 0; 2098 this.originalEndDts = 0; 2099 this.syncPoints = []; // SampleInfo[n], for video IDR frames only 2100 this.firstSample = null; // SampleInfo 2101 this.lastSample = null; // SampleInfo 2102 } 2103 2104 _createClass(MediaSegmentInfo, [{ 2105 key: "appendSyncPoint", 2106 value: function appendSyncPoint(sampleInfo) { 2107 // also called Random Access Point 2108 sampleInfo.isSyncPoint = true; 2109 this.syncPoints.push(sampleInfo); 2110 } 2111 }]); 2112 2113 return MediaSegmentInfo; 2114 }(); 2115 2116 // Ordered list for recording video IDR frames, sorted by originalDts 2117 2118 2119 var IDRSampleList = exports.IDRSampleList = function () { 2120 function IDRSampleList() { 2121 _classCallCheck(this, IDRSampleList); 2122 2123 this._list = []; 2124 } 2125 2126 _createClass(IDRSampleList, [{ 2127 key: "clear", 2128 value: function clear() { 2129 this._list = []; 2130 } 2131 }, { 2132 key: "appendArray", 2133 value: function appendArray(syncPoints) { 2134 var list = this._list; 2135 2136 if (syncPoints.length === 0) { 2137 return; 2138 } 2139 2140 if (list.length > 0 && syncPoints[0].originalDts < list[list.length - 1].originalDts) { 2141 this.clear(); 2142 } 2143 2144 Array.prototype.push.apply(list, syncPoints); 2145 } 2146 }, { 2147 key: "getLastSyncPointBeforeDts", 2148 value: function getLastSyncPointBeforeDts(dts) { 2149 if (this._list.length == 0) { 2150 return null; 2151 } 2152 2153 var list = this._list; 2154 var idx = 0; 2155 var last = list.length - 1; 2156 var mid = 0; 2157 var lbound = 0; 2158 var ubound = last; 2159 2160 if (dts < list[0].dts) { 2161 idx = 0; 2162 lbound = ubound + 1; 2163 } 2164 2165 while (lbound <= ubound) { 2166 mid = lbound + Math.floor((ubound - lbound) / 2); 2167 if (mid === last || dts >= list[mid].dts && dts < list[mid + 1].dts) { 2168 idx = mid; 2169 break; 2170 } else if (list[mid].dts < dts) { 2171 lbound = mid + 1; 2172 } else { 2173 ubound = mid - 1; 2174 } 2175 } 2176 return this._list[idx]; 2177 } 2178 }]); 2179 2180 return IDRSampleList; 2181 }(); 2182 2183 // Data structure for recording information of media segments in single track. 2184 2185 2186 var MediaSegmentInfoList = exports.MediaSegmentInfoList = function () { 2187 function MediaSegmentInfoList(type) { 2188 _classCallCheck(this, MediaSegmentInfoList); 2189 2190 this._type = type; 2191 this._list = []; 2192 this._lastAppendLocation = -1; // cached last insert location 2193 } 2194 2195 _createClass(MediaSegmentInfoList, [{ 2196 key: "isEmpty", 2197 value: function isEmpty() { 2198 return this._list.length === 0; 2199 } 2200 }, { 2201 key: "clear", 2202 value: function clear() { 2203 this._list = []; 2204 this._lastAppendLocation = -1; 2205 } 2206 }, { 2207 key: "_searchNearestSegmentBefore", 2208 value: function _searchNearestSegmentBefore(originalBeginDts) { 2209 var list = this._list; 2210 if (list.length === 0) { 2211 return -2; 2212 } 2213 var last = list.length - 1; 2214 var mid = 0; 2215 var lbound = 0; 2216 var ubound = last; 2217 2218 var idx = 0; 2219 2220 if (originalBeginDts < list[0].originalBeginDts) { 2221 idx = -1; 2222 return idx; 2223 } 2224 2225 while (lbound <= ubound) { 2226 mid = lbound + Math.floor((ubound - lbound) / 2); 2227 if (mid === last || originalBeginDts > list[mid].lastSample.originalDts && originalBeginDts < list[mid + 1].originalBeginDts) { 2228 idx = mid; 2229 break; 2230 } else if (list[mid].originalBeginDts < originalBeginDts) { 2231 lbound = mid + 1; 2232 } else { 2233 ubound = mid - 1; 2234 } 2235 } 2236 return idx; 2237 } 2238 }, { 2239 key: "_searchNearestSegmentAfter", 2240 value: function _searchNearestSegmentAfter(originalBeginDts) { 2241 return this._searchNearestSegmentBefore(originalBeginDts) + 1; 2242 } 2243 }, { 2244 key: "append", 2245 value: function append(mediaSegmentInfo) { 2246 var list = this._list; 2247 var msi = mediaSegmentInfo; 2248 var lastAppendIdx = this._lastAppendLocation; 2249 var insertIdx = 0; 2250 2251 if (lastAppendIdx !== -1 && lastAppendIdx < list.length && msi.originalBeginDts >= list[lastAppendIdx].lastSample.originalDts && (lastAppendIdx === list.length - 1 || lastAppendIdx < list.length - 1 && msi.originalBeginDts < list[lastAppendIdx + 1].originalBeginDts)) { 2252 insertIdx = lastAppendIdx + 1; // use cached location idx 2253 } else { 2254 if (list.length > 0) { 2255 insertIdx = this._searchNearestSegmentBefore(msi.originalBeginDts) + 1; 2256 } 2257 } 2258 2259 this._lastAppendLocation = insertIdx; 2260 this._list.splice(insertIdx, 0, msi); 2261 } 2262 }, { 2263 key: "getLastSegmentBefore", 2264 value: function getLastSegmentBefore(originalBeginDts) { 2265 var idx = this._searchNearestSegmentBefore(originalBeginDts); 2266 if (idx >= 0) { 2267 return this._list[idx]; 2268 } else { 2269 // -1 2270 return null; 2271 } 2272 } 2273 }, { 2274 key: "getLastSampleBefore", 2275 value: function getLastSampleBefore(originalBeginDts) { 2276 var segment = this.getLastSegmentBefore(originalBeginDts); 2277 if (segment != null) { 2278 return segment.lastSample; 2279 } else { 2280 return null; 2281 } 2282 } 2283 }, { 2284 key: "getLastSyncPointBefore", 2285 value: function getLastSyncPointBefore(originalBeginDts) { 2286 var segmentIdx = this._searchNearestSegmentBefore(originalBeginDts); 2287 var syncPoints = this._list[segmentIdx].syncPoints; 2288 while (syncPoints.length === 0 && segmentIdx > 0) { 2289 segmentIdx--; 2290 syncPoints = this._list[segmentIdx].syncPoints; 2291 } 2292 if (syncPoints.length > 0) { 2293 return syncPoints[syncPoints.length - 1]; 2294 } else { 2295 return null; 2296 } 2297 } 2298 }, { 2299 key: "type", 2300 get: function get() { 2301 return this._type; 2302 } 2303 }, { 2304 key: "length", 2305 get: function get() { 2306 return this._list.length; 2307 } 2308 }]); 2309 2310 return MediaSegmentInfoList; 2311 }(); 2312 2313 },{}],9:[function(_dereq_,module,exports){ 2314 'use strict'; 2315 2316 Object.defineProperty(exports, "__esModule", { 2317 value: true 2318 }); 2319 2320 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 2321 * Copyright (C) 2016 Bilibili. All Rights Reserved. 2322 * 2323 * @author zheng qian <xqq@xqq.im> 2324 * 2325 * Licensed under the Apache License, Version 2.0 (the "License"); 2326 * you may not use this file except in compliance with the License. 2327 * You may obtain a copy of the License at 2328 * 2329 * http://www.apache.org/licenses/LICENSE-2.0 2330 * 2331 * Unless required by applicable law or agreed to in writing, software 2332 * distributed under the License is distributed on an "AS IS" BASIS, 2333 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2334 * See the License for the specific language governing permissions and 2335 * limitations under the License. 2336 */ 2337 2338 var _events = _dereq_('events'); 2339 2340 var _events2 = _interopRequireDefault(_events); 2341 2342 var _logger = _dereq_('../utils/logger.js'); 2343 2344 var _logger2 = _interopRequireDefault(_logger); 2345 2346 var _browser = _dereq_('../utils/browser.js'); 2347 2348 var _browser2 = _interopRequireDefault(_browser); 2349 2350 var _mseEvents = _dereq_('./mse-events.js'); 2351 2352 var _mseEvents2 = _interopRequireDefault(_mseEvents); 2353 2354 var _mediaSegmentInfo = _dereq_('./media-segment-info.js'); 2355 2356 var _exception = _dereq_('../utils/exception.js'); 2357 2358 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 2359 2360 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 2361 2362 // Media Source Extensions controller 2363 var MSEController = function () { 2364 function MSEController(config) { 2365 _classCallCheck(this, MSEController); 2366 2367 this.TAG = 'MSEController'; 2368 2369 this._config = config; 2370 this._emitter = new _events2.default(); 2371 2372 if (this._config.isLive && this._config.autoCleanupSourceBuffer == undefined) { 2373 // For live stream, do auto cleanup by default 2374 this._config.autoCleanupSourceBuffer = true; 2375 } 2376 2377 this.e = { 2378 onSourceOpen: this._onSourceOpen.bind(this), 2379 onSourceEnded: this._onSourceEnded.bind(this), 2380 onSourceClose: this._onSourceClose.bind(this), 2381 onSourceBufferError: this._onSourceBufferError.bind(this), 2382 onSourceBufferUpdateEnd: this._onSourceBufferUpdateEnd.bind(this) 2383 }; 2384 2385 this._mediaSource = null; 2386 this._mediaSourceObjectURL = null; 2387 this._mediaElement = null; 2388 2389 this._isBufferFull = false; 2390 this._hasPendingEos = false; 2391 2392 this._requireSetMediaDuration = false; 2393 this._pendingMediaDuration = 0; 2394 2395 this._pendingSourceBufferInit = []; 2396 this._mimeTypes = { 2397 video: null, 2398 audio: null 2399 }; 2400 this._sourceBuffers = { 2401 video: null, 2402 audio: null 2403 }; 2404 this._lastInitSegments = { 2405 video: null, 2406 audio: null 2407 }; 2408 this._pendingSegments = { 2409 video: [], 2410 audio: [] 2411 }; 2412 this._pendingRemoveRanges = { 2413 video: [], 2414 audio: [] 2415 }; 2416 this._idrList = new _mediaSegmentInfo.IDRSampleList(); 2417 } 2418 2419 _createClass(MSEController, [{ 2420 key: 'destroy', 2421 value: function destroy() { 2422 if (this._mediaElement || this._mediaSource) { 2423 this.detachMediaElement(); 2424 } 2425 this.e = null; 2426 this._emitter.removeAllListeners(); 2427 this._emitter = null; 2428 } 2429 }, { 2430 key: 'on', 2431 value: function on(event, listener) { 2432 this._emitter.addListener(event, listener); 2433 } 2434 }, { 2435 key: 'off', 2436 value: function off(event, listener) { 2437 this._emitter.removeListener(event, listener); 2438 } 2439 }, { 2440 key: 'attachMediaElement', 2441 value: function attachMediaElement(mediaElement) { 2442 if (this._mediaSource) { 2443 throw new _exception.IllegalStateException('MediaSource has been attached to an HTMLMediaElement!'); 2444 } 2445 var ms = this._mediaSource = new window.MediaSource(); 2446 ms.addEventListener('sourceopen', this.e.onSourceOpen); 2447 ms.addEventListener('sourceended', this.e.onSourceEnded); 2448 ms.addEventListener('sourceclose', this.e.onSourceClose); 2449 2450 this._mediaElement = mediaElement; 2451 this._mediaSourceObjectURL = window.URL.createObjectURL(this._mediaSource); 2452 mediaElement.src = this._mediaSourceObjectURL; 2453 } 2454 }, { 2455 key: 'detachMediaElement', 2456 value: function detachMediaElement() { 2457 if (this._mediaSource) { 2458 var ms = this._mediaSource; 2459 for (var type in this._sourceBuffers) { 2460 // pending segments should be discard 2461 var ps = this._pendingSegments[type]; 2462 ps.splice(0, ps.length); 2463 this._pendingSegments[type] = null; 2464 this._pendingRemoveRanges[type] = null; 2465 this._lastInitSegments[type] = null; 2466 2467 // remove all sourcebuffers 2468 var sb = this._sourceBuffers[type]; 2469 if (sb) { 2470 if (ms.readyState !== 'closed') { 2471 // ms edge can throw an error: Unexpected call to method or property access 2472 try { 2473 ms.removeSourceBuffer(sb); 2474 } catch (error) { 2475 _logger2.default.e(this.TAG, error.message); 2476 } 2477 sb.removeEventListener('error', this.e.onSourceBufferError); 2478 sb.removeEventListener('updateend', this.e.onSourceBufferUpdateEnd); 2479 } 2480 this._mimeTypes[type] = null; 2481 this._sourceBuffers[type] = null; 2482 } 2483 } 2484 if (ms.readyState === 'open') { 2485 try { 2486 ms.endOfStream(); 2487 } catch (error) { 2488 _logger2.default.e(this.TAG, error.message); 2489 } 2490 } 2491 ms.removeEventListener('sourceopen', this.e.onSourceOpen); 2492 ms.removeEventListener('sourceended', this.e.onSourceEnded); 2493 ms.removeEventListener('sourceclose', this.e.onSourceClose); 2494 this._pendingSourceBufferInit = []; 2495 this._isBufferFull = false; 2496 this._idrList.clear(); 2497 this._mediaSource = null; 2498 } 2499 2500 if (this._mediaElement) { 2501 this._mediaElement.src = ''; 2502 this._mediaElement.removeAttribute('src'); 2503 this._mediaElement = null; 2504 } 2505 if (this._mediaSourceObjectURL) { 2506 window.URL.revokeObjectURL(this._mediaSourceObjectURL); 2507 this._mediaSourceObjectURL = null; 2508 } 2509 } 2510 }, { 2511 key: 'appendInitSegment', 2512 value: function appendInitSegment(initSegment, deferred) { 2513 if (!this._mediaSource || this._mediaSource.readyState !== 'open') { 2514 // sourcebuffer creation requires mediaSource.readyState === 'open' 2515 // so we defer the sourcebuffer creation, until sourceopen event triggered 2516 this._pendingSourceBufferInit.push(initSegment); 2517 // make sure that this InitSegment is in the front of pending segments queue 2518 this._pendingSegments[initSegment.type].push(initSegment); 2519 return; 2520 } 2521 2522 var is = initSegment; 2523 var mimeType = '' + is.container; 2524 if (is.codec && is.codec.length > 0) { 2525 mimeType += ';codecs=' + is.codec; 2526 } 2527 2528 var firstInitSegment = false; 2529 2530 _logger2.default.v(this.TAG, 'Received Initialization Segment, mimeType: ' + mimeType); 2531 this._lastInitSegments[is.type] = is; 2532 2533 if (mimeType !== this._mimeTypes[is.type]) { 2534 if (!this._mimeTypes[is.type]) { 2535 // empty, first chance create sourcebuffer 2536 firstInitSegment = true; 2537 try { 2538 var sb = this._sourceBuffers[is.type] = this._mediaSource.addSourceBuffer(mimeType); 2539 sb.addEventListener('error', this.e.onSourceBufferError); 2540 sb.addEventListener('updateend', this.e.onSourceBufferUpdateEnd); 2541 } catch (error) { 2542 _logger2.default.e(this.TAG, error.message); 2543 this._emitter.emit(_mseEvents2.default.ERROR, { code: error.code, msg: error.message }); 2544 return; 2545 } 2546 } else { 2547 _logger2.default.v(this.TAG, 'Notice: ' + is.type + ' mimeType changed, origin: ' + this._mimeTypes[is.type] + ', target: ' + mimeType); 2548 } 2549 this._mimeTypes[is.type] = mimeType; 2550 } 2551 2552 if (!deferred) { 2553 // deferred means this InitSegment has been pushed to pendingSegments queue 2554 this._pendingSegments[is.type].push(is); 2555 } 2556 if (!firstInitSegment) { 2557 // append immediately only if init segment in subsequence 2558 if (this._sourceBuffers[is.type] && !this._sourceBuffers[is.type].updating) { 2559 this._doAppendSegments(); 2560 } 2561 } 2562 if (_browser2.default.safari && is.container === 'audio/mpeg' && is.mediaDuration > 0) { 2563 // 'audio/mpeg' track under Safari may cause MediaElement's duration to be NaN 2564 // Manually correct MediaSource.duration to make progress bar seekable, and report right duration 2565 this._requireSetMediaDuration = true; 2566 this._pendingMediaDuration = is.mediaDuration / 1000; // in seconds 2567 this._updateMediaSourceDuration(); 2568 } 2569 } 2570 }, { 2571 key: 'appendMediaSegment', 2572 value: function appendMediaSegment(mediaSegment) { 2573 var ms = mediaSegment; 2574 this._pendingSegments[ms.type].push(ms); 2575 2576 if (this._config.autoCleanupSourceBuffer && this._needCleanupSourceBuffer()) { 2577 this._doCleanupSourceBuffer(); 2578 } 2579 2580 var sb = this._sourceBuffers[ms.type]; 2581 if (sb && !sb.updating && !this._hasPendingRemoveRanges()) { 2582 this._doAppendSegments(); 2583 } 2584 } 2585 }, { 2586 key: 'seek', 2587 value: function seek(seconds) { 2588 // remove all appended buffers 2589 for (var type in this._sourceBuffers) { 2590 if (!this._sourceBuffers[type]) { 2591 continue; 2592 } 2593 2594 // abort current buffer append algorithm 2595 var sb = this._sourceBuffers[type]; 2596 if (this._mediaSource.readyState === 'open') { 2597 try { 2598 // If range removal algorithm is running, InvalidStateError will be throwed 2599 // Ignore it. 2600 sb.abort(); 2601 } catch (error) { 2602 _logger2.default.e(this.TAG, error.message); 2603 } 2604 } 2605 2606 // IDRList should be clear 2607 this._idrList.clear(); 2608 2609 // pending segments should be discard 2610 var ps = this._pendingSegments[type]; 2611 ps.splice(0, ps.length); 2612 2613 if (this._mediaSource.readyState === 'closed') { 2614 // Parent MediaSource object has been detached from HTMLMediaElement 2615 continue; 2616 } 2617 2618 // record ranges to be remove from SourceBuffer 2619 for (var i = 0; i < sb.buffered.length; i++) { 2620 var start = sb.buffered.start(i); 2621 var end = sb.buffered.end(i); 2622 this._pendingRemoveRanges[type].push({ start: start, end: end }); 2623 } 2624 2625 // if sb is not updating, let's remove ranges now! 2626 if (!sb.updating) { 2627 this._doRemoveRanges(); 2628 } 2629 2630 // Safari 10 may get InvalidStateError in the later appendBuffer() after SourceBuffer.remove() call 2631 // Internal parser's state may be invalid at this time. Re-append last InitSegment to workaround. 2632 // Related issue: https://bugs.webkit.org/show_bug.cgi?id=159230 2633 if (_browser2.default.safari) { 2634 var lastInitSegment = this._lastInitSegments[type]; 2635 if (lastInitSegment) { 2636 this._pendingSegments[type].push(lastInitSegment); 2637 if (!sb.updating) { 2638 this._doAppendSegments(); 2639 } 2640 } 2641 } 2642 } 2643 } 2644 }, { 2645 key: 'endOfStream', 2646 value: function endOfStream() { 2647 var ms = this._mediaSource; 2648 var sb = this._sourceBuffers; 2649 if (!ms || ms.readyState !== 'open') { 2650 if (ms && ms.readyState === 'closed' && this._hasPendingSegments()) { 2651 // If MediaSource hasn't turned into open state, and there're pending segments 2652 // Mark pending endOfStream, defer call until all pending segments appended complete 2653 this._hasPendingEos = true; 2654 } 2655 return; 2656 } 2657 if (sb.video && sb.video.updating || sb.audio && sb.audio.updating) { 2658 // If any sourcebuffer is updating, defer endOfStream operation 2659 // See _onSourceBufferUpdateEnd() 2660 this._hasPendingEos = true; 2661 } else { 2662 this._hasPendingEos = false; 2663 // Notify media data loading complete 2664 // This is helpful for correcting total duration to match last media segment 2665 // Otherwise MediaElement's ended event may not be triggered 2666 ms.endOfStream(); 2667 } 2668 } 2669 }, { 2670 key: 'getNearestKeyframe', 2671 value: function getNearestKeyframe(dts) { 2672 return this._idrList.getLastSyncPointBeforeDts(dts); 2673 } 2674 }, { 2675 key: '_needCleanupSourceBuffer', 2676 value: function _needCleanupSourceBuffer() { 2677 if (!this._config.autoCleanupSourceBuffer) { 2678 return false; 2679 } 2680 2681 var currentTime = this._mediaElement.currentTime; 2682 2683 for (var type in this._sourceBuffers) { 2684 var sb = this._sourceBuffers[type]; 2685 if (sb) { 2686 var buffered = sb.buffered; 2687 if (buffered.length >= 1) { 2688 if (currentTime - buffered.start(0) >= this._config.autoCleanupMaxBackwardDuration) { 2689 return true; 2690 } 2691 } 2692 } 2693 } 2694 2695 return false; 2696 } 2697 }, { 2698 key: '_doCleanupSourceBuffer', 2699 value: function _doCleanupSourceBuffer() { 2700 var currentTime = this._mediaElement.currentTime; 2701 2702 for (var type in this._sourceBuffers) { 2703 var sb = this._sourceBuffers[type]; 2704 if (sb) { 2705 var buffered = sb.buffered; 2706 var doRemove = false; 2707 2708 for (var i = 0; i < buffered.length; i++) { 2709 var start = buffered.start(i); 2710 var end = buffered.end(i); 2711 2712 if (start <= currentTime && currentTime < end + 3) { 2713 // padding 3 seconds 2714 if (currentTime - start >= this._config.autoCleanupMaxBackwardDuration) { 2715 doRemove = true; 2716 var removeEnd = currentTime - this._config.autoCleanupMinBackwardDuration; 2717 this._pendingRemoveRanges[type].push({ start: start, end: removeEnd }); 2718 } 2719 } else if (end < currentTime) { 2720 doRemove = true; 2721 this._pendingRemoveRanges[type].push({ start: start, end: end }); 2722 } 2723 } 2724 2725 if (doRemove && !sb.updating) { 2726 this._doRemoveRanges(); 2727 } 2728 } 2729 } 2730 } 2731 }, { 2732 key: '_updateMediaSourceDuration', 2733 value: function _updateMediaSourceDuration() { 2734 var sb = this._sourceBuffers; 2735 if (this._mediaElement.readyState === 0 || this._mediaSource.readyState !== 'open') { 2736 return; 2737 } 2738 if (sb.video && sb.video.updating || sb.audio && sb.audio.updating) { 2739 return; 2740 } 2741 2742 var current = this._mediaSource.duration; 2743 var target = this._pendingMediaDuration; 2744 2745 if (target > 0 && (isNaN(current) || target > current)) { 2746 _logger2.default.v(this.TAG, 'Update MediaSource duration from ' + current + ' to ' + target); 2747 this._mediaSource.duration = target; 2748 } 2749 2750 this._requireSetMediaDuration = false; 2751 this._pendingMediaDuration = 0; 2752 } 2753 }, { 2754 key: '_doRemoveRanges', 2755 value: function _doRemoveRanges() { 2756 for (var type in this._pendingRemoveRanges) { 2757 if (!this._sourceBuffers[type] || this._sourceBuffers[type].updating) { 2758 continue; 2759 } 2760 var sb = this._sourceBuffers[type]; 2761 var ranges = this._pendingRemoveRanges[type]; 2762 while (ranges.length && !sb.updating) { 2763 var range = ranges.shift(); 2764 sb.remove(range.start, range.end); 2765 } 2766 } 2767 } 2768 }, { 2769 key: '_doAppendSegments', 2770 value: function _doAppendSegments() { 2771 var pendingSegments = this._pendingSegments; 2772 2773 for (var type in pendingSegments) { 2774 if (!this._sourceBuffers[type] || this._sourceBuffers[type].updating) { 2775 continue; 2776 } 2777 2778 if (pendingSegments[type].length > 0) { 2779 var segment = pendingSegments[type].shift(); 2780 2781 if (segment.timestampOffset) { 2782 // For MPEG audio stream in MSE, if unbuffered-seeking occurred 2783 // We need explicitly set timestampOffset to the desired point in timeline for mpeg SourceBuffer. 2784 var currentOffset = this._sourceBuffers[type].timestampOffset; 2785 var targetOffset = segment.timestampOffset / 1000; // in seconds 2786 2787 var delta = Math.abs(currentOffset - targetOffset); 2788 if (delta > 0.1) { 2789 // If time delta > 100ms 2790 _logger2.default.v(this.TAG, 'Update MPEG audio timestampOffset from ' + currentOffset + ' to ' + targetOffset); 2791 this._sourceBuffers[type].timestampOffset = targetOffset; 2792 } 2793 delete segment.timestampOffset; 2794 } 2795 2796 if (!segment.data || segment.data.byteLength === 0) { 2797 // Ignore empty buffer 2798 continue; 2799 } 2800 2801 try { 2802 this._sourceBuffers[type].appendBuffer(segment.data); 2803 this._isBufferFull = false; 2804 if (type === 'video' && segment.hasOwnProperty('info')) { 2805 this._idrList.appendArray(segment.info.syncPoints); 2806 } 2807 } catch (error) { 2808 this._pendingSegments[type].unshift(segment); 2809 if (error.code === 22) { 2810 // QuotaExceededError 2811 /* Notice that FireFox may not throw QuotaExceededError if SourceBuffer is full 2812 * Currently we can only do lazy-load to avoid SourceBuffer become scattered. 2813 * SourceBuffer eviction policy may be changed in future version of FireFox. 2814 * 2815 * Related issues: 2816 * https://bugzilla.mozilla.org/show_bug.cgi?id=1279885 2817 * https://bugzilla.mozilla.org/show_bug.cgi?id=1280023 2818 */ 2819 2820 // report buffer full, abort network IO 2821 if (!this._isBufferFull) { 2822 this._emitter.emit(_mseEvents2.default.BUFFER_FULL); 2823 } 2824 this._isBufferFull = true; 2825 } else { 2826 _logger2.default.e(this.TAG, error.message); 2827 this._emitter.emit(_mseEvents2.default.ERROR, { code: error.code, msg: error.message }); 2828 } 2829 } 2830 } 2831 } 2832 } 2833 }, { 2834 key: '_onSourceOpen', 2835 value: function _onSourceOpen() { 2836 _logger2.default.v(this.TAG, 'MediaSource onSourceOpen'); 2837 this._mediaSource.removeEventListener('sourceopen', this.e.onSourceOpen); 2838 // deferred sourcebuffer creation / initialization 2839 if (this._pendingSourceBufferInit.length > 0) { 2840 var pendings = this._pendingSourceBufferInit; 2841 while (pendings.length) { 2842 var segment = pendings.shift(); 2843 this.appendInitSegment(segment, true); 2844 } 2845 } 2846 // there may be some pending media segments, append them 2847 if (this._hasPendingSegments()) { 2848 this._doAppendSegments(); 2849 } 2850 this._emitter.emit(_mseEvents2.default.SOURCE_OPEN); 2851 } 2852 }, { 2853 key: '_onSourceEnded', 2854 value: function _onSourceEnded() { 2855 // fired on endOfStream 2856 _logger2.default.v(this.TAG, 'MediaSource onSourceEnded'); 2857 } 2858 }, { 2859 key: '_onSourceClose', 2860 value: function _onSourceClose() { 2861 // fired on detaching from media element 2862 _logger2.default.v(this.TAG, 'MediaSource onSourceClose'); 2863 if (this._mediaSource && this.e != null) { 2864 this._mediaSource.removeEventListener('sourceopen', this.e.onSourceOpen); 2865 this._mediaSource.removeEventListener('sourceended', this.e.onSourceEnded); 2866 this._mediaSource.removeEventListener('sourceclose', this.e.onSourceClose); 2867 } 2868 } 2869 }, { 2870 key: '_hasPendingSegments', 2871 value: function _hasPendingSegments() { 2872 var ps = this._pendingSegments; 2873 return ps.video.length > 0 || ps.audio.length > 0; 2874 } 2875 }, { 2876 key: '_hasPendingRemoveRanges', 2877 value: function _hasPendingRemoveRanges() { 2878 var prr = this._pendingRemoveRanges; 2879 return prr.video.length > 0 || prr.audio.length > 0; 2880 } 2881 }, { 2882 key: '_onSourceBufferUpdateEnd', 2883 value: function _onSourceBufferUpdateEnd() { 2884 if (this._requireSetMediaDuration) { 2885 this._updateMediaSourceDuration(); 2886 } else if (this._hasPendingRemoveRanges()) { 2887 this._doRemoveRanges(); 2888 } else if (this._hasPendingSegments()) { 2889 this._doAppendSegments(); 2890 } else if (this._hasPendingEos) { 2891 this.endOfStream(); 2892 } 2893 this._emitter.emit(_mseEvents2.default.UPDATE_END); 2894 } 2895 }, { 2896 key: '_onSourceBufferError', 2897 value: function _onSourceBufferError(e) { 2898 _logger2.default.e(this.TAG, 'SourceBuffer Error: ' + e); 2899 // this error might not always be fatal, just ignore it 2900 } 2901 }]); 2902 2903 return MSEController; 2904 }(); 2905 2906 exports.default = MSEController; 2907 2908 },{"../utils/browser.js":39,"../utils/exception.js":40,"../utils/logger.js":41,"./media-segment-info.js":8,"./mse-events.js":10,"events":2}],10:[function(_dereq_,module,exports){ 2909 'use strict'; 2910 2911 Object.defineProperty(exports, "__esModule", { 2912 value: true 2913 }); 2914 /* 2915 * Copyright (C) 2016 Bilibili. All Rights Reserved. 2916 * 2917 * @author zheng qian <xqq@xqq.im> 2918 * 2919 * Licensed under the Apache License, Version 2.0 (the "License"); 2920 * you may not use this file except in compliance with the License. 2921 * You may obtain a copy of the License at 2922 * 2923 * http://www.apache.org/licenses/LICENSE-2.0 2924 * 2925 * Unless required by applicable law or agreed to in writing, software 2926 * distributed under the License is distributed on an "AS IS" BASIS, 2927 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2928 * See the License for the specific language governing permissions and 2929 * limitations under the License. 2930 */ 2931 2932 var MSEEvents = { 2933 ERROR: 'error', 2934 SOURCE_OPEN: 'source_open', 2935 UPDATE_END: 'update_end', 2936 BUFFER_FULL: 'buffer_full' 2937 }; 2938 2939 exports.default = MSEEvents; 2940 2941 },{}],11:[function(_dereq_,module,exports){ 2942 'use strict'; 2943 2944 Object.defineProperty(exports, "__esModule", { 2945 value: true 2946 }); 2947 2948 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 2949 * Copyright (C) 2016 Bilibili. All Rights Reserved. 2950 * 2951 * @author zheng qian <xqq@xqq.im> 2952 * 2953 * Licensed under the Apache License, Version 2.0 (the "License"); 2954 * you may not use this file except in compliance with the License. 2955 * You may obtain a copy of the License at 2956 * 2957 * http://www.apache.org/licenses/LICENSE-2.0 2958 * 2959 * Unless required by applicable law or agreed to in writing, software 2960 * distributed under the License is distributed on an "AS IS" BASIS, 2961 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 2962 * See the License for the specific language governing permissions and 2963 * limitations under the License. 2964 */ 2965 2966 var _events = _dereq_('events'); 2967 2968 var _events2 = _interopRequireDefault(_events); 2969 2970 var _logger = _dereq_('../utils/logger.js'); 2971 2972 var _logger2 = _interopRequireDefault(_logger); 2973 2974 var _loggingControl = _dereq_('../utils/logging-control.js'); 2975 2976 var _loggingControl2 = _interopRequireDefault(_loggingControl); 2977 2978 var _transmuxingController = _dereq_('./transmuxing-controller.js'); 2979 2980 var _transmuxingController2 = _interopRequireDefault(_transmuxingController); 2981 2982 var _transmuxingEvents = _dereq_('./transmuxing-events.js'); 2983 2984 var _transmuxingEvents2 = _interopRequireDefault(_transmuxingEvents); 2985 2986 var _transmuxingWorker = _dereq_('./transmuxing-worker.js'); 2987 2988 var _transmuxingWorker2 = _interopRequireDefault(_transmuxingWorker); 2989 2990 var _mediaInfo = _dereq_('./media-info.js'); 2991 2992 var _mediaInfo2 = _interopRequireDefault(_mediaInfo); 2993 2994 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 2995 2996 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 2997 2998 var Transmuxer = function () { 2999 function Transmuxer(mediaDataSource, config) { 3000 _classCallCheck(this, Transmuxer); 3001 3002 this.TAG = 'Transmuxer'; 3003 this._emitter = new _events2.default(); 3004 3005 if (config.enableWorker && typeof Worker !== 'undefined') { 3006 try { 3007 var work = _dereq_('webworkify'); 3008 this._worker = work(_transmuxingWorker2.default); 3009 this._workerDestroying = false; 3010 this._worker.addEventListener('message', this._onWorkerMessage.bind(this)); 3011 this._worker.postMessage({ cmd: 'init', param: [mediaDataSource, config] }); 3012 this.e = { 3013 onLoggingConfigChanged: this._onLoggingConfigChanged.bind(this) 3014 }; 3015 _loggingControl2.default.registerListener(this.e.onLoggingConfigChanged); 3016 this._worker.postMessage({ cmd: 'logging_config', param: _loggingControl2.default.getConfig() }); 3017 } catch (error) { 3018 _logger2.default.e(this.TAG, 'Error while initialize transmuxing worker, fallback to inline transmuxing'); 3019 this._worker = null; 3020 this._controller = new _transmuxingController2.default(mediaDataSource, config); 3021 } 3022 } else { 3023 this._controller = new _transmuxingController2.default(mediaDataSource, config); 3024 } 3025 3026 if (this._controller) { 3027 var ctl = this._controller; 3028 ctl.on(_transmuxingEvents2.default.IO_ERROR, this._onIOError.bind(this)); 3029 ctl.on(_transmuxingEvents2.default.DEMUX_ERROR, this._onDemuxError.bind(this)); 3030 ctl.on(_transmuxingEvents2.default.INIT_SEGMENT, this._onInitSegment.bind(this)); 3031 ctl.on(_transmuxingEvents2.default.MEDIA_SEGMENT, this._onMediaSegment.bind(this)); 3032 ctl.on(_transmuxingEvents2.default.LOADING_COMPLETE, this._onLoadingComplete.bind(this)); 3033 ctl.on(_transmuxingEvents2.default.RECOVERED_EARLY_EOF, this._onRecoveredEarlyEof.bind(this)); 3034 ctl.on(_transmuxingEvents2.default.MEDIA_INFO, this._onMediaInfo.bind(this)); 3035 ctl.on(_transmuxingEvents2.default.METADATA_ARRIVED, this._onMetaDataArrived.bind(this)); 3036 ctl.on(_transmuxingEvents2.default.SCRIPTDATA_ARRIVED, this._onScriptDataArrived.bind(this)); 3037 ctl.on(_transmuxingEvents2.default.STATISTICS_INFO, this._onStatisticsInfo.bind(this)); 3038 ctl.on(_transmuxingEvents2.default.RECOMMEND_SEEKPOINT, this._onRecommendSeekpoint.bind(this)); 3039 } 3040 } 3041 3042 _createClass(Transmuxer, [{ 3043 key: 'destroy', 3044 value: function destroy() { 3045 if (this._worker) { 3046 if (!this._workerDestroying) { 3047 this._workerDestroying = true; 3048 this._worker.postMessage({ cmd: 'destroy' }); 3049 _loggingControl2.default.removeListener(this.e.onLoggingConfigChanged); 3050 this.e = null; 3051 } 3052 } else { 3053 this._controller.destroy(); 3054 this._controller = null; 3055 } 3056 this._emitter.removeAllListeners(); 3057 this._emitter = null; 3058 } 3059 }, { 3060 key: 'on', 3061 value: function on(event, listener) { 3062 this._emitter.addListener(event, listener); 3063 } 3064 }, { 3065 key: 'off', 3066 value: function off(event, listener) { 3067 this._emitter.removeListener(event, listener); 3068 } 3069 }, { 3070 key: 'hasWorker', 3071 value: function hasWorker() { 3072 return this._worker != null; 3073 } 3074 }, { 3075 key: 'open', 3076 value: function open() { 3077 if (this._worker) { 3078 this._worker.postMessage({ cmd: 'start' }); 3079 } else { 3080 this._controller.start(); 3081 } 3082 } 3083 }, { 3084 key: 'close', 3085 value: function close() { 3086 if (this._worker) { 3087 this._worker.postMessage({ cmd: 'stop' }); 3088 } else { 3089 this._controller.stop(); 3090 } 3091 } 3092 }, { 3093 key: 'seek', 3094 value: function seek(milliseconds) { 3095 if (this._worker) { 3096 this._worker.postMessage({ cmd: 'seek', param: milliseconds }); 3097 } else { 3098 this._controller.seek(milliseconds); 3099 } 3100 } 3101 }, { 3102 key: 'pause', 3103 value: function pause() { 3104 if (this._worker) { 3105 this._worker.postMessage({ cmd: 'pause' }); 3106 } else { 3107 this._controller.pause(); 3108 } 3109 } 3110 }, { 3111 key: 'resume', 3112 value: function resume() { 3113 if (this._worker) { 3114 this._worker.postMessage({ cmd: 'resume' }); 3115 } else { 3116 this._controller.resume(); 3117 } 3118 } 3119 }, { 3120 key: '_onInitSegment', 3121 value: function _onInitSegment(type, initSegment) { 3122 var _this = this; 3123 3124 // do async invoke 3125 Promise.resolve().then(function () { 3126 _this._emitter.emit(_transmuxingEvents2.default.INIT_SEGMENT, type, initSegment); 3127 }); 3128 } 3129 }, { 3130 key: '_onMediaSegment', 3131 value: function _onMediaSegment(type, mediaSegment) { 3132 var _this2 = this; 3133 3134 Promise.resolve().then(function () { 3135 _this2._emitter.emit(_transmuxingEvents2.default.MEDIA_SEGMENT, type, mediaSegment); 3136 }); 3137 } 3138 }, { 3139 key: '_onLoadingComplete', 3140 value: function _onLoadingComplete() { 3141 var _this3 = this; 3142 3143 Promise.resolve().then(function () { 3144 _this3._emitter.emit(_transmuxingEvents2.default.LOADING_COMPLETE); 3145 }); 3146 } 3147 }, { 3148 key: '_onRecoveredEarlyEof', 3149 value: function _onRecoveredEarlyEof() { 3150 var _this4 = this; 3151 3152 Promise.resolve().then(function () { 3153 _this4._emitter.emit(_transmuxingEvents2.default.RECOVERED_EARLY_EOF); 3154 }); 3155 } 3156 }, { 3157 key: '_onMediaInfo', 3158 value: function _onMediaInfo(mediaInfo) { 3159 var _this5 = this; 3160 3161 Promise.resolve().then(function () { 3162 _this5._emitter.emit(_transmuxingEvents2.default.MEDIA_INFO, mediaInfo); 3163 }); 3164 } 3165 }, { 3166 key: '_onMetaDataArrived', 3167 value: function _onMetaDataArrived(metadata) { 3168 var _this6 = this; 3169 3170 Promise.resolve().then(function () { 3171 _this6._emitter.emit(_transmuxingEvents2.default.METADATA_ARRIVED, metadata); 3172 }); 3173 } 3174 }, { 3175 key: '_onScriptDataArrived', 3176 value: function _onScriptDataArrived(data) { 3177 var _this7 = this; 3178 3179 Promise.resolve().then(function () { 3180 _this7._emitter.emit(_transmuxingEvents2.default.SCRIPTDATA_ARRIVED, data); 3181 }); 3182 } 3183 }, { 3184 key: '_onStatisticsInfo', 3185 value: function _onStatisticsInfo(statisticsInfo) { 3186 var _this8 = this; 3187 3188 Promise.resolve().then(function () { 3189 _this8._emitter.emit(_transmuxingEvents2.default.STATISTICS_INFO, statisticsInfo); 3190 }); 3191 } 3192 }, { 3193 key: '_onIOError', 3194 value: function _onIOError(type, info) { 3195 var _this9 = this; 3196 3197 Promise.resolve().then(function () { 3198 _this9._emitter.emit(_transmuxingEvents2.default.IO_ERROR, type, info); 3199 }); 3200 } 3201 }, { 3202 key: '_onDemuxError', 3203 value: function _onDemuxError(type, info) { 3204 var _this10 = this; 3205 3206 Promise.resolve().then(function () { 3207 _this10._emitter.emit(_transmuxingEvents2.default.DEMUX_ERROR, type, info); 3208 }); 3209 } 3210 }, { 3211 key: '_onRecommendSeekpoint', 3212 value: function _onRecommendSeekpoint(milliseconds) { 3213 var _this11 = this; 3214 3215 Promise.resolve().then(function () { 3216 _this11._emitter.emit(_transmuxingEvents2.default.RECOMMEND_SEEKPOINT, milliseconds); 3217 }); 3218 } 3219 }, { 3220 key: '_onLoggingConfigChanged', 3221 value: function _onLoggingConfigChanged(config) { 3222 if (this._worker) { 3223 this._worker.postMessage({ cmd: 'logging_config', param: config }); 3224 } 3225 } 3226 }, { 3227 key: '_onWorkerMessage', 3228 value: function _onWorkerMessage(e) { 3229 var message = e.data; 3230 var data = message.data; 3231 3232 if (message.msg === 'destroyed' || this._workerDestroying) { 3233 this._workerDestroying = false; 3234 this._worker.terminate(); 3235 this._worker = null; 3236 return; 3237 } 3238 3239 switch (message.msg) { 3240 case _transmuxingEvents2.default.INIT_SEGMENT: 3241 case _transmuxingEvents2.default.MEDIA_SEGMENT: 3242 this._emitter.emit(message.msg, data.type, data.data); 3243 break; 3244 case _transmuxingEvents2.default.LOADING_COMPLETE: 3245 case _transmuxingEvents2.default.RECOVERED_EARLY_EOF: 3246 this._emitter.emit(message.msg); 3247 break; 3248 case _transmuxingEvents2.default.MEDIA_INFO: 3249 Object.setPrototypeOf(data, _mediaInfo2.default.prototype); 3250 this._emitter.emit(message.msg, data); 3251 break; 3252 case _transmuxingEvents2.default.METADATA_ARRIVED: 3253 case _transmuxingEvents2.default.SCRIPTDATA_ARRIVED: 3254 case _transmuxingEvents2.default.STATISTICS_INFO: 3255 this._emitter.emit(message.msg, data); 3256 break; 3257 case _transmuxingEvents2.default.IO_ERROR: 3258 case _transmuxingEvents2.default.DEMUX_ERROR: 3259 this._emitter.emit(message.msg, data.type, data.info); 3260 break; 3261 case _transmuxingEvents2.default.RECOMMEND_SEEKPOINT: 3262 this._emitter.emit(message.msg, data); 3263 break; 3264 case 'logcat_callback': 3265 _logger2.default.emitter.emit('log', data.type, data.logcat); 3266 break; 3267 default: 3268 break; 3269 } 3270 } 3271 }]); 3272 3273 return Transmuxer; 3274 }(); 3275 3276 exports.default = Transmuxer; 3277 3278 },{"../utils/logger.js":41,"../utils/logging-control.js":42,"./media-info.js":7,"./transmuxing-controller.js":12,"./transmuxing-events.js":13,"./transmuxing-worker.js":14,"events":2,"webworkify":4}],12:[function(_dereq_,module,exports){ 3279 'use strict'; 3280 3281 Object.defineProperty(exports, "__esModule", { 3282 value: true 3283 }); 3284 3285 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 3286 * Copyright (C) 2016 Bilibili. All Rights Reserved. 3287 * 3288 * @author zheng qian <xqq@xqq.im> 3289 * 3290 * Licensed under the Apache License, Version 2.0 (the "License"); 3291 * you may not use this file except in compliance with the License. 3292 * You may obtain a copy of the License at 3293 * 3294 * http://www.apache.org/licenses/LICENSE-2.0 3295 * 3296 * Unless required by applicable law or agreed to in writing, software 3297 * distributed under the License is distributed on an "AS IS" BASIS, 3298 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3299 * See the License for the specific language governing permissions and 3300 * limitations under the License. 3301 */ 3302 3303 var _events = _dereq_('events'); 3304 3305 var _events2 = _interopRequireDefault(_events); 3306 3307 var _logger = _dereq_('../utils/logger.js'); 3308 3309 var _logger2 = _interopRequireDefault(_logger); 3310 3311 var _browser = _dereq_('../utils/browser.js'); 3312 3313 var _browser2 = _interopRequireDefault(_browser); 3314 3315 var _mediaInfo = _dereq_('./media-info.js'); 3316 3317 var _mediaInfo2 = _interopRequireDefault(_mediaInfo); 3318 3319 var _flvDemuxer = _dereq_('../demux/flv-demuxer.js'); 3320 3321 var _flvDemuxer2 = _interopRequireDefault(_flvDemuxer); 3322 3323 var _mp4Remuxer = _dereq_('../remux/mp4-remuxer.js'); 3324 3325 var _mp4Remuxer2 = _interopRequireDefault(_mp4Remuxer); 3326 3327 var _demuxErrors = _dereq_('../demux/demux-errors.js'); 3328 3329 var _demuxErrors2 = _interopRequireDefault(_demuxErrors); 3330 3331 var _ioController = _dereq_('../io/io-controller.js'); 3332 3333 var _ioController2 = _interopRequireDefault(_ioController); 3334 3335 var _transmuxingEvents = _dereq_('./transmuxing-events.js'); 3336 3337 var _transmuxingEvents2 = _interopRequireDefault(_transmuxingEvents); 3338 3339 var _loader = _dereq_('../io/loader.js'); 3340 3341 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 3342 3343 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 3344 3345 // Transmuxing (IO, Demuxing, Remuxing) controller, with multipart support 3346 var TransmuxingController = function () { 3347 function TransmuxingController(mediaDataSource, config) { 3348 _classCallCheck(this, TransmuxingController); 3349 3350 this.TAG = 'TransmuxingController'; 3351 this._emitter = new _events2.default(); 3352 3353 this._config = config; 3354 3355 // treat single part media as multipart media, which has only one segment 3356 if (!mediaDataSource.segments) { 3357 mediaDataSource.segments = [{ 3358 duration: mediaDataSource.duration, 3359 filesize: mediaDataSource.filesize, 3360 url: mediaDataSource.url 3361 }]; 3362 } 3363 3364 // fill in default IO params if not exists 3365 if (typeof mediaDataSource.cors !== 'boolean') { 3366 mediaDataSource.cors = true; 3367 } 3368 if (typeof mediaDataSource.withCredentials !== 'boolean') { 3369 mediaDataSource.withCredentials = false; 3370 } 3371 3372 this._mediaDataSource = mediaDataSource; 3373 this._currentSegmentIndex = 0; 3374 var totalDuration = 0; 3375 3376 this._mediaDataSource.segments.forEach(function (segment) { 3377 // timestampBase for each segment, and calculate total duration 3378 segment.timestampBase = totalDuration; 3379 totalDuration += segment.duration; 3380 // params needed by IOController 3381 segment.cors = mediaDataSource.cors; 3382 segment.withCredentials = mediaDataSource.withCredentials; 3383 // referrer policy control, if exist 3384 if (config.referrerPolicy) { 3385 segment.referrerPolicy = config.referrerPolicy; 3386 } 3387 }); 3388 3389 if (!isNaN(totalDuration) && this._mediaDataSource.duration !== totalDuration) { 3390 this._mediaDataSource.duration = totalDuration; 3391 } 3392 3393 this._mediaInfo = null; 3394 this._demuxer = null; 3395 this._remuxer = null; 3396 this._ioctl = null; 3397 3398 this._pendingSeekTime = null; 3399 this._pendingResolveSeekPoint = null; 3400 3401 this._statisticsReporter = null; 3402 } 3403 3404 _createClass(TransmuxingController, [{ 3405 key: 'destroy', 3406 value: function destroy() { 3407 this._mediaInfo = null; 3408 this._mediaDataSource = null; 3409 3410 if (this._statisticsReporter) { 3411 this._disableStatisticsReporter(); 3412 } 3413 if (this._ioctl) { 3414 this._ioctl.destroy(); 3415 this._ioctl = null; 3416 } 3417 if (this._demuxer) { 3418 this._demuxer.destroy(); 3419 this._demuxer = null; 3420 } 3421 if (this._remuxer) { 3422 this._remuxer.destroy(); 3423 this._remuxer = null; 3424 } 3425 3426 this._emitter.removeAllListeners(); 3427 this._emitter = null; 3428 } 3429 }, { 3430 key: 'on', 3431 value: function on(event, listener) { 3432 this._emitter.addListener(event, listener); 3433 } 3434 }, { 3435 key: 'off', 3436 value: function off(event, listener) { 3437 this._emitter.removeListener(event, listener); 3438 } 3439 }, { 3440 key: 'start', 3441 value: function start() { 3442 this._loadSegment(0); 3443 this._enableStatisticsReporter(); 3444 } 3445 }, { 3446 key: '_loadSegment', 3447 value: function _loadSegment(segmentIndex, optionalFrom) { 3448 this._currentSegmentIndex = segmentIndex; 3449 var dataSource = this._mediaDataSource.segments[segmentIndex]; 3450 3451 var ioctl = this._ioctl = new _ioController2.default(dataSource, this._config, segmentIndex); 3452 ioctl.onError = this._onIOException.bind(this); 3453 ioctl.onSeeked = this._onIOSeeked.bind(this); 3454 ioctl.onComplete = this._onIOComplete.bind(this); 3455 ioctl.onRedirect = this._onIORedirect.bind(this); 3456 ioctl.onRecoveredEarlyEof = this._onIORecoveredEarlyEof.bind(this); 3457 3458 if (optionalFrom) { 3459 this._demuxer.bindDataSource(this._ioctl); 3460 } else { 3461 ioctl.onDataArrival = this._onInitChunkArrival.bind(this); 3462 } 3463 3464 ioctl.open(optionalFrom); 3465 } 3466 }, { 3467 key: 'stop', 3468 value: function stop() { 3469 this._internalAbort(); 3470 this._disableStatisticsReporter(); 3471 } 3472 }, { 3473 key: '_internalAbort', 3474 value: function _internalAbort() { 3475 if (this._ioctl) { 3476 this._ioctl.destroy(); 3477 this._ioctl = null; 3478 } 3479 } 3480 }, { 3481 key: 'pause', 3482 value: function pause() { 3483 // take a rest 3484 if (this._ioctl && this._ioctl.isWorking()) { 3485 this._ioctl.pause(); 3486 this._disableStatisticsReporter(); 3487 } 3488 } 3489 }, { 3490 key: 'resume', 3491 value: function resume() { 3492 if (this._ioctl && this._ioctl.isPaused()) { 3493 this._ioctl.resume(); 3494 this._enableStatisticsReporter(); 3495 } 3496 } 3497 }, { 3498 key: 'seek', 3499 value: function seek(milliseconds) { 3500 if (this._mediaInfo == null || !this._mediaInfo.isSeekable()) { 3501 return; 3502 } 3503 3504 var targetSegmentIndex = this._searchSegmentIndexContains(milliseconds); 3505 3506 if (targetSegmentIndex === this._currentSegmentIndex) { 3507 // intra-segment seeking 3508 var segmentInfo = this._mediaInfo.segments[targetSegmentIndex]; 3509 3510 if (segmentInfo == undefined) { 3511 // current segment loading started, but mediainfo hasn't received yet 3512 // wait for the metadata loaded, then seek to expected position 3513 this._pendingSeekTime = milliseconds; 3514 } else { 3515 var keyframe = segmentInfo.getNearestKeyframe(milliseconds); 3516 this._remuxer.seek(keyframe.milliseconds); 3517 this._ioctl.seek(keyframe.fileposition); 3518 // Will be resolved in _onRemuxerMediaSegmentArrival() 3519 this._pendingResolveSeekPoint = keyframe.milliseconds; 3520 } 3521 } else { 3522 // cross-segment seeking 3523 var targetSegmentInfo = this._mediaInfo.segments[targetSegmentIndex]; 3524 3525 if (targetSegmentInfo == undefined) { 3526 // target segment hasn't been loaded. We need metadata then seek to expected time 3527 this._pendingSeekTime = milliseconds; 3528 this._internalAbort(); 3529 this._remuxer.seek(); 3530 this._remuxer.insertDiscontinuity(); 3531 this._loadSegment(targetSegmentIndex); 3532 // Here we wait for the metadata loaded, then seek to expected position 3533 } else { 3534 // We have target segment's metadata, direct seek to target position 3535 var _keyframe = targetSegmentInfo.getNearestKeyframe(milliseconds); 3536 this._internalAbort(); 3537 this._remuxer.seek(milliseconds); 3538 this._remuxer.insertDiscontinuity(); 3539 this._demuxer.resetMediaInfo(); 3540 this._demuxer.timestampBase = this._mediaDataSource.segments[targetSegmentIndex].timestampBase; 3541 this._loadSegment(targetSegmentIndex, _keyframe.fileposition); 3542 this._pendingResolveSeekPoint = _keyframe.milliseconds; 3543 this._reportSegmentMediaInfo(targetSegmentIndex); 3544 } 3545 } 3546 3547 this._enableStatisticsReporter(); 3548 } 3549 }, { 3550 key: '_searchSegmentIndexContains', 3551 value: function _searchSegmentIndexContains(milliseconds) { 3552 var segments = this._mediaDataSource.segments; 3553 var idx = segments.length - 1; 3554 3555 for (var i = 0; i < segments.length; i++) { 3556 if (milliseconds < segments[i].timestampBase) { 3557 idx = i - 1; 3558 break; 3559 } 3560 } 3561 return idx; 3562 } 3563 }, { 3564 key: '_onInitChunkArrival', 3565 value: function _onInitChunkArrival(data, byteStart) { 3566 var _this = this; 3567 3568 var probeData = null; 3569 var consumed = 0; 3570 3571 if (byteStart > 0) { 3572 // IOController seeked immediately after opened, byteStart > 0 callback may received 3573 this._demuxer.bindDataSource(this._ioctl); 3574 this._demuxer.timestampBase = this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase; 3575 3576 consumed = this._demuxer.parseChunks(data, byteStart); 3577 } else if ((probeData = _flvDemuxer2.default.probe(data)).match) { 3578 // Always create new FLVDemuxer 3579 this._demuxer = new _flvDemuxer2.default(probeData, this._config); 3580 3581 if (!this._remuxer) { 3582 this._remuxer = new _mp4Remuxer2.default(this._config); 3583 } 3584 3585 var mds = this._mediaDataSource; 3586 if (mds.duration != undefined && !isNaN(mds.duration)) { 3587 this._demuxer.overridedDuration = mds.duration; 3588 } 3589 if (typeof mds.hasAudio === 'boolean') { 3590 this._demuxer.overridedHasAudio = mds.hasAudio; 3591 } 3592 if (typeof mds.hasVideo === 'boolean') { 3593 this._demuxer.overridedHasVideo = mds.hasVideo; 3594 } 3595 3596 this._demuxer.timestampBase = mds.segments[this._currentSegmentIndex].timestampBase; 3597 3598 this._demuxer.onError = this._onDemuxException.bind(this); 3599 this._demuxer.onMediaInfo = this._onMediaInfo.bind(this); 3600 this._demuxer.onMetaDataArrived = this._onMetaDataArrived.bind(this); 3601 this._demuxer.onScriptDataArrived = this._onScriptDataArrived.bind(this); 3602 3603 this._remuxer.bindDataSource(this._demuxer.bindDataSource(this._ioctl)); 3604 3605 this._remuxer.onInitSegment = this._onRemuxerInitSegmentArrival.bind(this); 3606 this._remuxer.onMediaSegment = this._onRemuxerMediaSegmentArrival.bind(this); 3607 3608 consumed = this._demuxer.parseChunks(data, byteStart); 3609 } else { 3610 probeData = null; 3611 _logger2.default.e(this.TAG, 'Non-FLV, Unsupported media type!'); 3612 Promise.resolve().then(function () { 3613 _this._internalAbort(); 3614 }); 3615 this._emitter.emit(_transmuxingEvents2.default.DEMUX_ERROR, _demuxErrors2.default.FORMAT_UNSUPPORTED, 'Non-FLV, Unsupported media type'); 3616 3617 consumed = 0; 3618 } 3619 3620 return consumed; 3621 } 3622 }, { 3623 key: '_onMediaInfo', 3624 value: function _onMediaInfo(mediaInfo) { 3625 var _this2 = this; 3626 3627 if (this._mediaInfo == null) { 3628 // Store first segment's mediainfo as global mediaInfo 3629 this._mediaInfo = Object.assign({}, mediaInfo); 3630 this._mediaInfo.keyframesIndex = null; 3631 this._mediaInfo.segments = []; 3632 this._mediaInfo.segmentCount = this._mediaDataSource.segments.length; 3633 Object.setPrototypeOf(this._mediaInfo, _mediaInfo2.default.prototype); 3634 } 3635 3636 var segmentInfo = Object.assign({}, mediaInfo); 3637 Object.setPrototypeOf(segmentInfo, _mediaInfo2.default.prototype); 3638 this._mediaInfo.segments[this._currentSegmentIndex] = segmentInfo; 3639 3640 // notify mediaInfo update 3641 this._reportSegmentMediaInfo(this._currentSegmentIndex); 3642 3643 if (this._pendingSeekTime != null) { 3644 Promise.resolve().then(function () { 3645 var target = _this2._pendingSeekTime; 3646 _this2._pendingSeekTime = null; 3647 _this2.seek(target); 3648 }); 3649 } 3650 } 3651 }, { 3652 key: '_onMetaDataArrived', 3653 value: function _onMetaDataArrived(metadata) { 3654 this._emitter.emit(_transmuxingEvents2.default.METADATA_ARRIVED, metadata); 3655 } 3656 }, { 3657 key: '_onScriptDataArrived', 3658 value: function _onScriptDataArrived(data) { 3659 this._emitter.emit(_transmuxingEvents2.default.SCRIPTDATA_ARRIVED, data); 3660 } 3661 }, { 3662 key: '_onIOSeeked', 3663 value: function _onIOSeeked() { 3664 this._remuxer.insertDiscontinuity(); 3665 } 3666 }, { 3667 key: '_onIOComplete', 3668 value: function _onIOComplete(extraData) { 3669 var segmentIndex = extraData; 3670 var nextSegmentIndex = segmentIndex + 1; 3671 3672 if (nextSegmentIndex < this._mediaDataSource.segments.length) { 3673 this._internalAbort(); 3674 this._remuxer.flushStashedSamples(); 3675 this._loadSegment(nextSegmentIndex); 3676 } else { 3677 this._remuxer.flushStashedSamples(); 3678 this._emitter.emit(_transmuxingEvents2.default.LOADING_COMPLETE); 3679 this._disableStatisticsReporter(); 3680 } 3681 } 3682 }, { 3683 key: '_onIORedirect', 3684 value: function _onIORedirect(redirectedURL) { 3685 var segmentIndex = this._ioctl.extraData; 3686 this._mediaDataSource.segments[segmentIndex].redirectedURL = redirectedURL; 3687 } 3688 }, { 3689 key: '_onIORecoveredEarlyEof', 3690 value: function _onIORecoveredEarlyEof() { 3691 this._emitter.emit(_transmuxingEvents2.default.RECOVERED_EARLY_EOF); 3692 } 3693 }, { 3694 key: '_onIOException', 3695 value: function _onIOException(type, info) { 3696 _logger2.default.e(this.TAG, 'IOException: type = ' + type + ', code = ' + info.code + ', msg = ' + info.msg); 3697 this._emitter.emit(_transmuxingEvents2.default.IO_ERROR, type, info); 3698 this._disableStatisticsReporter(); 3699 } 3700 }, { 3701 key: '_onDemuxException', 3702 value: function _onDemuxException(type, info) { 3703 _logger2.default.e(this.TAG, 'DemuxException: type = ' + type + ', info = ' + info); 3704 this._emitter.emit(_transmuxingEvents2.default.DEMUX_ERROR, type, info); 3705 } 3706 }, { 3707 key: '_onRemuxerInitSegmentArrival', 3708 value: function _onRemuxerInitSegmentArrival(type, initSegment) { 3709 this._emitter.emit(_transmuxingEvents2.default.INIT_SEGMENT, type, initSegment); 3710 } 3711 }, { 3712 key: '_onRemuxerMediaSegmentArrival', 3713 value: function _onRemuxerMediaSegmentArrival(type, mediaSegment) { 3714 if (this._pendingSeekTime != null) { 3715 // Media segments after new-segment cross-seeking should be dropped. 3716 return; 3717 } 3718 this._emitter.emit(_transmuxingEvents2.default.MEDIA_SEGMENT, type, mediaSegment); 3719 3720 // Resolve pending seekPoint 3721 if (this._pendingResolveSeekPoint != null && type === 'video') { 3722 var syncPoints = mediaSegment.info.syncPoints; 3723 var seekpoint = this._pendingResolveSeekPoint; 3724 this._pendingResolveSeekPoint = null; 3725 3726 // Safari: Pass PTS for recommend_seekpoint 3727 if (_browser2.default.safari && syncPoints.length > 0 && syncPoints[0].originalDts === seekpoint) { 3728 seekpoint = syncPoints[0].pts; 3729 } 3730 // else: use original DTS (keyframe.milliseconds) 3731 3732 this._emitter.emit(_transmuxingEvents2.default.RECOMMEND_SEEKPOINT, seekpoint); 3733 } 3734 } 3735 }, { 3736 key: '_enableStatisticsReporter', 3737 value: function _enableStatisticsReporter() { 3738 if (this._statisticsReporter == null) { 3739 this._statisticsReporter = self.setInterval(this._reportStatisticsInfo.bind(this), this._config.statisticsInfoReportInterval); 3740 } 3741 } 3742 }, { 3743 key: '_disableStatisticsReporter', 3744 value: function _disableStatisticsReporter() { 3745 if (this._statisticsReporter) { 3746 self.clearInterval(this._statisticsReporter); 3747 this._statisticsReporter = null; 3748 } 3749 } 3750 }, { 3751 key: '_reportSegmentMediaInfo', 3752 value: function _reportSegmentMediaInfo(segmentIndex) { 3753 var segmentInfo = this._mediaInfo.segments[segmentIndex]; 3754 var exportInfo = Object.assign({}, segmentInfo); 3755 3756 exportInfo.duration = this._mediaInfo.duration; 3757 exportInfo.segmentCount = this._mediaInfo.segmentCount; 3758 delete exportInfo.segments; 3759 delete exportInfo.keyframesIndex; 3760 3761 this._emitter.emit(_transmuxingEvents2.default.MEDIA_INFO, exportInfo); 3762 } 3763 }, { 3764 key: '_reportStatisticsInfo', 3765 value: function _reportStatisticsInfo() { 3766 var info = {}; 3767 3768 info.url = this._ioctl.currentURL; 3769 info.hasRedirect = this._ioctl.hasRedirect; 3770 if (info.hasRedirect) { 3771 info.redirectedURL = this._ioctl.currentRedirectedURL; 3772 } 3773 3774 info.speed = this._ioctl.currentSpeed; 3775 info.loaderType = this._ioctl.loaderType; 3776 info.currentSegmentIndex = this._currentSegmentIndex; 3777 info.totalSegmentCount = this._mediaDataSource.segments.length; 3778 3779 this._emitter.emit(_transmuxingEvents2.default.STATISTICS_INFO, info); 3780 } 3781 }]); 3782 3783 return TransmuxingController; 3784 }(); 3785 3786 exports.default = TransmuxingController; 3787 3788 },{"../demux/demux-errors.js":16,"../demux/flv-demuxer.js":18,"../io/io-controller.js":23,"../io/loader.js":24,"../remux/mp4-remuxer.js":38,"../utils/browser.js":39,"../utils/logger.js":41,"./media-info.js":7,"./transmuxing-events.js":13,"events":2}],13:[function(_dereq_,module,exports){ 3789 'use strict'; 3790 3791 Object.defineProperty(exports, "__esModule", { 3792 value: true 3793 }); 3794 /* 3795 * Copyright (C) 2016 Bilibili. All Rights Reserved. 3796 * 3797 * @author zheng qian <xqq@xqq.im> 3798 * 3799 * Licensed under the Apache License, Version 2.0 (the "License"); 3800 * you may not use this file except in compliance with the License. 3801 * You may obtain a copy of the License at 3802 * 3803 * http://www.apache.org/licenses/LICENSE-2.0 3804 * 3805 * Unless required by applicable law or agreed to in writing, software 3806 * distributed under the License is distributed on an "AS IS" BASIS, 3807 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 3808 * See the License for the specific language governing permissions and 3809 * limitations under the License. 3810 */ 3811 3812 var TransmuxingEvents = { 3813 IO_ERROR: 'io_error', 3814 DEMUX_ERROR: 'demux_error', 3815 INIT_SEGMENT: 'init_segment', 3816 MEDIA_SEGMENT: 'media_segment', 3817 LOADING_COMPLETE: 'loading_complete', 3818 RECOVERED_EARLY_EOF: 'recovered_early_eof', 3819 MEDIA_INFO: 'media_info', 3820 METADATA_ARRIVED: 'metadata_arrived', 3821 SCRIPTDATA_ARRIVED: 'scriptdata_arrived', 3822 STATISTICS_INFO: 'statistics_info', 3823 RECOMMEND_SEEKPOINT: 'recommend_seekpoint' 3824 }; 3825 3826 exports.default = TransmuxingEvents; 3827 3828 },{}],14:[function(_dereq_,module,exports){ 3829 'use strict'; 3830 3831 Object.defineProperty(exports, "__esModule", { 3832 value: true 3833 }); 3834 3835 var _logger = _dereq_('../utils/logger.js'); 3836 3837 var _logger2 = _interopRequireDefault(_logger); 3838 3839 var _loggingControl = _dereq_('../utils/logging-control.js'); 3840 3841 var _loggingControl2 = _interopRequireDefault(_loggingControl); 3842 3843 var _polyfill = _dereq_('../utils/polyfill.js'); 3844 3845 var _polyfill2 = _interopRequireDefault(_polyfill); 3846 3847 var _transmuxingController = _dereq_('./transmuxing-controller.js'); 3848 3849 var _transmuxingController2 = _interopRequireDefault(_transmuxingController); 3850 3851 var _transmuxingEvents = _dereq_('./transmuxing-events.js'); 3852 3853 var _transmuxingEvents2 = _interopRequireDefault(_transmuxingEvents); 3854 3855 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 3856 3857 /* post message to worker: 3858 data: { 3859 cmd: string 3860 param: any 3861 } 3862 3863 receive message from worker: 3864 data: { 3865 msg: string, 3866 data: any 3867 } 3868 */ 3869 3870 var TransmuxingWorker = function TransmuxingWorker(self) { 3871 3872 var TAG = 'TransmuxingWorker'; 3873 var controller = null; 3874 var logcatListener = onLogcatCallback.bind(this); 3875 3876 _polyfill2.default.install(); 3877 3878 self.addEventListener('message', function (e) { 3879 switch (e.data.cmd) { 3880 case 'init': 3881 controller = new _transmuxingController2.default(e.data.param[0], e.data.param[1]); 3882 controller.on(_transmuxingEvents2.default.IO_ERROR, onIOError.bind(this)); 3883 controller.on(_transmuxingEvents2.default.DEMUX_ERROR, onDemuxError.bind(this)); 3884 controller.on(_transmuxingEvents2.default.INIT_SEGMENT, onInitSegment.bind(this)); 3885 controller.on(_transmuxingEvents2.default.MEDIA_SEGMENT, onMediaSegment.bind(this)); 3886 controller.on(_transmuxingEvents2.default.LOADING_COMPLETE, onLoadingComplete.bind(this)); 3887 controller.on(_transmuxingEvents2.default.RECOVERED_EARLY_EOF, onRecoveredEarlyEof.bind(this)); 3888 controller.on(_transmuxingEvents2.default.MEDIA_INFO, onMediaInfo.bind(this)); 3889 controller.on(_transmuxingEvents2.default.METADATA_ARRIVED, onMetaDataArrived.bind(this)); 3890 controller.on(_transmuxingEvents2.default.SCRIPTDATA_ARRIVED, onScriptDataArrived.bind(this)); 3891 controller.on(_transmuxingEvents2.default.STATISTICS_INFO, onStatisticsInfo.bind(this)); 3892 controller.on(_transmuxingEvents2.default.RECOMMEND_SEEKPOINT, onRecommendSeekpoint.bind(this)); 3893 break; 3894 case 'destroy': 3895 if (controller) { 3896 controller.destroy(); 3897 controller = null; 3898 } 3899 self.postMessage({ msg: 'destroyed' }); 3900 break; 3901 case 'start': 3902 controller.start(); 3903 break; 3904 case 'stop': 3905 controller.stop(); 3906 break; 3907 case 'seek': 3908 controller.seek(e.data.param); 3909 break; 3910 case 'pause': 3911 controller.pause(); 3912 break; 3913 case 'resume': 3914 controller.resume(); 3915 break; 3916 case 'logging_config': 3917 { 3918 var config = e.data.param; 3919 _loggingControl2.default.applyConfig(config); 3920 3921 if (config.enableCallback === true) { 3922 _loggingControl2.default.addLogListener(logcatListener); 3923 } else { 3924 _loggingControl2.default.removeLogListener(logcatListener); 3925 } 3926 break; 3927 } 3928 } 3929 }); 3930 3931 function onInitSegment(type, initSegment) { 3932 var obj = { 3933 msg: _transmuxingEvents2.default.INIT_SEGMENT, 3934 data: { 3935 type: type, 3936 data: initSegment 3937 } 3938 }; 3939 self.postMessage(obj, [initSegment.data]); // data: ArrayBuffer 3940 } 3941 3942 function onMediaSegment(type, mediaSegment) { 3943 var obj = { 3944 msg: _transmuxingEvents2.default.MEDIA_SEGMENT, 3945 data: { 3946 type: type, 3947 data: mediaSegment 3948 } 3949 }; 3950 self.postMessage(obj, [mediaSegment.data]); // data: ArrayBuffer 3951 } 3952 3953 function onLoadingComplete() { 3954 var obj = { 3955 msg: _transmuxingEvents2.default.LOADING_COMPLETE 3956 }; 3957 self.postMessage(obj); 3958 } 3959 3960 function onRecoveredEarlyEof() { 3961 var obj = { 3962 msg: _transmuxingEvents2.default.RECOVERED_EARLY_EOF 3963 }; 3964 self.postMessage(obj); 3965 } 3966 3967 function onMediaInfo(mediaInfo) { 3968 var obj = { 3969 msg: _transmuxingEvents2.default.MEDIA_INFO, 3970 data: mediaInfo 3971 }; 3972 self.postMessage(obj); 3973 } 3974 3975 function onMetaDataArrived(metadata) { 3976 var obj = { 3977 msg: _transmuxingEvents2.default.METADATA_ARRIVED, 3978 data: metadata 3979 }; 3980 self.postMessage(obj); 3981 } 3982 3983 function onScriptDataArrived(data) { 3984 var obj = { 3985 msg: _transmuxingEvents2.default.SCRIPTDATA_ARRIVED, 3986 data: data 3987 }; 3988 self.postMessage(obj); 3989 } 3990 3991 function onStatisticsInfo(statInfo) { 3992 var obj = { 3993 msg: _transmuxingEvents2.default.STATISTICS_INFO, 3994 data: statInfo 3995 }; 3996 self.postMessage(obj); 3997 } 3998 3999 function onIOError(type, info) { 4000 self.postMessage({ 4001 msg: _transmuxingEvents2.default.IO_ERROR, 4002 data: { 4003 type: type, 4004 info: info 4005 } 4006 }); 4007 } 4008 4009 function onDemuxError(type, info) { 4010 self.postMessage({ 4011 msg: _transmuxingEvents2.default.DEMUX_ERROR, 4012 data: { 4013 type: type, 4014 info: info 4015 } 4016 }); 4017 } 4018 4019 function onRecommendSeekpoint(milliseconds) { 4020 self.postMessage({ 4021 msg: _transmuxingEvents2.default.RECOMMEND_SEEKPOINT, 4022 data: milliseconds 4023 }); 4024 } 4025 4026 function onLogcatCallback(type, str) { 4027 self.postMessage({ 4028 msg: 'logcat_callback', 4029 data: { 4030 type: type, 4031 logcat: str 4032 } 4033 }); 4034 } 4035 }; /* 4036 * Copyright (C) 2016 Bilibili. All Rights Reserved. 4037 * 4038 * @author zheng qian <xqq@xqq.im> 4039 * 4040 * Licensed under the Apache License, Version 2.0 (the "License"); 4041 * you may not use this file except in compliance with the License. 4042 * You may obtain a copy of the License at 4043 * 4044 * http://www.apache.org/licenses/LICENSE-2.0 4045 * 4046 * Unless required by applicable law or agreed to in writing, software 4047 * distributed under the License is distributed on an "AS IS" BASIS, 4048 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4049 * See the License for the specific language governing permissions and 4050 * limitations under the License. 4051 */ 4052 4053 exports.default = TransmuxingWorker; 4054 4055 },{"../utils/logger.js":41,"../utils/logging-control.js":42,"../utils/polyfill.js":43,"./transmuxing-controller.js":12,"./transmuxing-events.js":13}],15:[function(_dereq_,module,exports){ 4056 'use strict'; 4057 4058 Object.defineProperty(exports, "__esModule", { 4059 value: true 4060 }); 4061 4062 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 4063 * Copyright (C) 2016 Bilibili. All Rights Reserved. 4064 * 4065 * @author zheng qian <xqq@xqq.im> 4066 * 4067 * Licensed under the Apache License, Version 2.0 (the "License"); 4068 * you may not use this file except in compliance with the License. 4069 * You may obtain a copy of the License at 4070 * 4071 * http://www.apache.org/licenses/LICENSE-2.0 4072 * 4073 * Unless required by applicable law or agreed to in writing, software 4074 * distributed under the License is distributed on an "AS IS" BASIS, 4075 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4076 * See the License for the specific language governing permissions and 4077 * limitations under the License. 4078 */ 4079 4080 var _logger = _dereq_('../utils/logger.js'); 4081 4082 var _logger2 = _interopRequireDefault(_logger); 4083 4084 var _utf8Conv = _dereq_('../utils/utf8-conv.js'); 4085 4086 var _utf8Conv2 = _interopRequireDefault(_utf8Conv); 4087 4088 var _exception = _dereq_('../utils/exception.js'); 4089 4090 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 4091 4092 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 4093 4094 var le = function () { 4095 var buf = new ArrayBuffer(2); 4096 new DataView(buf).setInt16(0, 256, true); // little-endian write 4097 return new Int16Array(buf)[0] === 256; // platform-spec read, if equal then LE 4098 }(); 4099 4100 var AMF = function () { 4101 function AMF() { 4102 _classCallCheck(this, AMF); 4103 } 4104 4105 _createClass(AMF, null, [{ 4106 key: 'parseScriptData', 4107 value: function parseScriptData(arrayBuffer, dataOffset, dataSize) { 4108 var data = {}; 4109 4110 try { 4111 var name = AMF.parseValue(arrayBuffer, dataOffset, dataSize); 4112 var value = AMF.parseValue(arrayBuffer, dataOffset + name.size, dataSize - name.size); 4113 4114 data[name.data] = value.data; 4115 } catch (e) { 4116 _logger2.default.e('AMF', e.toString()); 4117 } 4118 4119 return data; 4120 } 4121 }, { 4122 key: 'parseObject', 4123 value: function parseObject(arrayBuffer, dataOffset, dataSize) { 4124 if (dataSize < 3) { 4125 throw new _exception.IllegalStateException('Data not enough when parse ScriptDataObject'); 4126 } 4127 var name = AMF.parseString(arrayBuffer, dataOffset, dataSize); 4128 var value = AMF.parseValue(arrayBuffer, dataOffset + name.size, dataSize - name.size); 4129 var isObjectEnd = value.objectEnd; 4130 4131 return { 4132 data: { 4133 name: name.data, 4134 value: value.data 4135 }, 4136 size: name.size + value.size, 4137 objectEnd: isObjectEnd 4138 }; 4139 } 4140 }, { 4141 key: 'parseVariable', 4142 value: function parseVariable(arrayBuffer, dataOffset, dataSize) { 4143 return AMF.parseObject(arrayBuffer, dataOffset, dataSize); 4144 } 4145 }, { 4146 key: 'parseString', 4147 value: function parseString(arrayBuffer, dataOffset, dataSize) { 4148 if (dataSize < 2) { 4149 throw new _exception.IllegalStateException('Data not enough when parse String'); 4150 } 4151 var v = new DataView(arrayBuffer, dataOffset, dataSize); 4152 var length = v.getUint16(0, !le); 4153 4154 var str = void 0; 4155 if (length > 0) { 4156 str = (0, _utf8Conv2.default)(new Uint8Array(arrayBuffer, dataOffset + 2, length)); 4157 } else { 4158 str = ''; 4159 } 4160 4161 return { 4162 data: str, 4163 size: 2 + length 4164 }; 4165 } 4166 }, { 4167 key: 'parseLongString', 4168 value: function parseLongString(arrayBuffer, dataOffset, dataSize) { 4169 if (dataSize < 4) { 4170 throw new _exception.IllegalStateException('Data not enough when parse LongString'); 4171 } 4172 var v = new DataView(arrayBuffer, dataOffset, dataSize); 4173 var length = v.getUint32(0, !le); 4174 4175 var str = void 0; 4176 if (length > 0) { 4177 str = (0, _utf8Conv2.default)(new Uint8Array(arrayBuffer, dataOffset + 4, length)); 4178 } else { 4179 str = ''; 4180 } 4181 4182 return { 4183 data: str, 4184 size: 4 + length 4185 }; 4186 } 4187 }, { 4188 key: 'parseDate', 4189 value: function parseDate(arrayBuffer, dataOffset, dataSize) { 4190 if (dataSize < 10) { 4191 throw new _exception.IllegalStateException('Data size invalid when parse Date'); 4192 } 4193 var v = new DataView(arrayBuffer, dataOffset, dataSize); 4194 var timestamp = v.getFloat64(0, !le); 4195 var localTimeOffset = v.getInt16(8, !le); 4196 timestamp += localTimeOffset * 60 * 1000; // get UTC time 4197 4198 return { 4199 data: new Date(timestamp), 4200 size: 8 + 2 4201 }; 4202 } 4203 }, { 4204 key: 'parseValue', 4205 value: function parseValue(arrayBuffer, dataOffset, dataSize) { 4206 if (dataSize < 1) { 4207 throw new _exception.IllegalStateException('Data not enough when parse Value'); 4208 } 4209 4210 var v = new DataView(arrayBuffer, dataOffset, dataSize); 4211 4212 var offset = 1; 4213 var type = v.getUint8(0); 4214 var value = void 0; 4215 var objectEnd = false; 4216 4217 try { 4218 switch (type) { 4219 case 0: 4220 // Number(Double) type 4221 value = v.getFloat64(1, !le); 4222 offset += 8; 4223 break; 4224 case 1: 4225 { 4226 // Boolean type 4227 var b = v.getUint8(1); 4228 value = b ? true : false; 4229 offset += 1; 4230 break; 4231 } 4232 case 2: 4233 { 4234 // String type 4235 var amfstr = AMF.parseString(arrayBuffer, dataOffset + 1, dataSize - 1); 4236 value = amfstr.data; 4237 offset += amfstr.size; 4238 break; 4239 } 4240 case 3: 4241 { 4242 // Object(s) type 4243 value = {}; 4244 var terminal = 0; // workaround for malformed Objects which has missing ScriptDataObjectEnd 4245 if ((v.getUint32(dataSize - 4, !le) & 0x00FFFFFF) === 9) { 4246 terminal = 3; 4247 } 4248 while (offset < dataSize - 4) { 4249 // 4 === type(UI8) + ScriptDataObjectEnd(UI24) 4250 var amfobj = AMF.parseObject(arrayBuffer, dataOffset + offset, dataSize - offset - terminal); 4251 if (amfobj.objectEnd) break; 4252 value[amfobj.data.name] = amfobj.data.value; 4253 offset += amfobj.size; 4254 } 4255 if (offset <= dataSize - 3) { 4256 var marker = v.getUint32(offset - 1, !le) & 0x00FFFFFF; 4257 if (marker === 9) { 4258 offset += 3; 4259 } 4260 } 4261 break; 4262 } 4263 case 8: 4264 { 4265 // ECMA array type (Mixed array) 4266 value = {}; 4267 offset += 4; // ECMAArrayLength(UI32) 4268 var _terminal = 0; // workaround for malformed MixedArrays which has missing ScriptDataObjectEnd 4269 if ((v.getUint32(dataSize - 4, !le) & 0x00FFFFFF) === 9) { 4270 _terminal = 3; 4271 } 4272 while (offset < dataSize - 8) { 4273 // 8 === type(UI8) + ECMAArrayLength(UI32) + ScriptDataVariableEnd(UI24) 4274 var amfvar = AMF.parseVariable(arrayBuffer, dataOffset + offset, dataSize - offset - _terminal); 4275 if (amfvar.objectEnd) break; 4276 value[amfvar.data.name] = amfvar.data.value; 4277 offset += amfvar.size; 4278 } 4279 if (offset <= dataSize - 3) { 4280 var _marker = v.getUint32(offset - 1, !le) & 0x00FFFFFF; 4281 if (_marker === 9) { 4282 offset += 3; 4283 } 4284 } 4285 break; 4286 } 4287 case 9: 4288 // ScriptDataObjectEnd 4289 value = undefined; 4290 offset = 1; 4291 objectEnd = true; 4292 break; 4293 case 10: 4294 { 4295 // Strict array type 4296 // ScriptDataValue[n]. NOTE: according to video_file_format_spec_v10_1.pdf 4297 value = []; 4298 var strictArrayLength = v.getUint32(1, !le); 4299 offset += 4; 4300 for (var i = 0; i < strictArrayLength; i++) { 4301 var val = AMF.parseValue(arrayBuffer, dataOffset + offset, dataSize - offset); 4302 value.push(val.data); 4303 offset += val.size; 4304 } 4305 break; 4306 } 4307 case 11: 4308 { 4309 // Date type 4310 var date = AMF.parseDate(arrayBuffer, dataOffset + 1, dataSize - 1); 4311 value = date.data; 4312 offset += date.size; 4313 break; 4314 } 4315 case 12: 4316 { 4317 // Long string type 4318 var amfLongStr = AMF.parseString(arrayBuffer, dataOffset + 1, dataSize - 1); 4319 value = amfLongStr.data; 4320 offset += amfLongStr.size; 4321 break; 4322 } 4323 default: 4324 // ignore and skip 4325 offset = dataSize; 4326 _logger2.default.w('AMF', 'Unsupported AMF value type ' + type); 4327 } 4328 } catch (e) { 4329 _logger2.default.e('AMF', e.toString()); 4330 } 4331 4332 return { 4333 data: value, 4334 size: offset, 4335 objectEnd: objectEnd 4336 }; 4337 } 4338 }]); 4339 4340 return AMF; 4341 }(); 4342 4343 exports.default = AMF; 4344 4345 },{"../utils/exception.js":40,"../utils/logger.js":41,"../utils/utf8-conv.js":44}],16:[function(_dereq_,module,exports){ 4346 'use strict'; 4347 4348 Object.defineProperty(exports, "__esModule", { 4349 value: true 4350 }); 4351 /* 4352 * Copyright (C) 2016 Bilibili. All Rights Reserved. 4353 * 4354 * @author zheng qian <xqq@xqq.im> 4355 * 4356 * Licensed under the Apache License, Version 2.0 (the "License"); 4357 * you may not use this file except in compliance with the License. 4358 * You may obtain a copy of the License at 4359 * 4360 * http://www.apache.org/licenses/LICENSE-2.0 4361 * 4362 * Unless required by applicable law or agreed to in writing, software 4363 * distributed under the License is distributed on an "AS IS" BASIS, 4364 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4365 * See the License for the specific language governing permissions and 4366 * limitations under the License. 4367 */ 4368 4369 var DemuxErrors = { 4370 OK: 'OK', 4371 FORMAT_ERROR: 'FormatError', 4372 FORMAT_UNSUPPORTED: 'FormatUnsupported', 4373 CODEC_UNSUPPORTED: 'CodecUnsupported' 4374 }; 4375 4376 exports.default = DemuxErrors; 4377 4378 },{}],17:[function(_dereq_,module,exports){ 4379 'use strict'; 4380 4381 Object.defineProperty(exports, "__esModule", { 4382 value: true 4383 }); 4384 4385 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 4386 * Copyright (C) 2016 Bilibili. All Rights Reserved. 4387 * 4388 * @author zheng qian <xqq@xqq.im> 4389 * 4390 * Licensed under the Apache License, Version 2.0 (the "License"); 4391 * you may not use this file except in compliance with the License. 4392 * You may obtain a copy of the License at 4393 * 4394 * http://www.apache.org/licenses/LICENSE-2.0 4395 * 4396 * Unless required by applicable law or agreed to in writing, software 4397 * distributed under the License is distributed on an "AS IS" BASIS, 4398 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4399 * See the License for the specific language governing permissions and 4400 * limitations under the License. 4401 */ 4402 4403 var _exception = _dereq_('../utils/exception.js'); 4404 4405 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 4406 4407 // Exponential-Golomb buffer decoder 4408 var ExpGolomb = function () { 4409 function ExpGolomb(uint8array) { 4410 _classCallCheck(this, ExpGolomb); 4411 4412 this.TAG = 'ExpGolomb'; 4413 4414 this._buffer = uint8array; 4415 this._buffer_index = 0; 4416 this._total_bytes = uint8array.byteLength; 4417 this._total_bits = uint8array.byteLength * 8; 4418 this._current_word = 0; 4419 this._current_word_bits_left = 0; 4420 } 4421 4422 _createClass(ExpGolomb, [{ 4423 key: 'destroy', 4424 value: function destroy() { 4425 this._buffer = null; 4426 } 4427 }, { 4428 key: '_fillCurrentWord', 4429 value: function _fillCurrentWord() { 4430 var buffer_bytes_left = this._total_bytes - this._buffer_index; 4431 if (buffer_bytes_left <= 0) throw new _exception.IllegalStateException('ExpGolomb: _fillCurrentWord() but no bytes available'); 4432 4433 var bytes_read = Math.min(4, buffer_bytes_left); 4434 var word = new Uint8Array(4); 4435 word.set(this._buffer.subarray(this._buffer_index, this._buffer_index + bytes_read)); 4436 this._current_word = new DataView(word.buffer).getUint32(0, false); 4437 4438 this._buffer_index += bytes_read; 4439 this._current_word_bits_left = bytes_read * 8; 4440 } 4441 }, { 4442 key: 'readBits', 4443 value: function readBits(bits) { 4444 if (bits > 32) throw new _exception.InvalidArgumentException('ExpGolomb: readBits() bits exceeded max 32bits!'); 4445 4446 if (bits <= this._current_word_bits_left) { 4447 var _result = this._current_word >>> 32 - bits; 4448 this._current_word <<= bits; 4449 this._current_word_bits_left -= bits; 4450 return _result; 4451 } 4452 4453 var result = this._current_word_bits_left ? this._current_word : 0; 4454 result = result >>> 32 - this._current_word_bits_left; 4455 var bits_need_left = bits - this._current_word_bits_left; 4456 4457 this._fillCurrentWord(); 4458 var bits_read_next = Math.min(bits_need_left, this._current_word_bits_left); 4459 4460 var result2 = this._current_word >>> 32 - bits_read_next; 4461 this._current_word <<= bits_read_next; 4462 this._current_word_bits_left -= bits_read_next; 4463 4464 result = result << bits_read_next | result2; 4465 return result; 4466 } 4467 }, { 4468 key: 'readBool', 4469 value: function readBool() { 4470 return this.readBits(1) === 1; 4471 } 4472 }, { 4473 key: 'readByte', 4474 value: function readByte() { 4475 return this.readBits(8); 4476 } 4477 }, { 4478 key: '_skipLeadingZero', 4479 value: function _skipLeadingZero() { 4480 var zero_count = void 0; 4481 for (zero_count = 0; zero_count < this._current_word_bits_left; zero_count++) { 4482 if (0 !== (this._current_word & 0x80000000 >>> zero_count)) { 4483 this._current_word <<= zero_count; 4484 this._current_word_bits_left -= zero_count; 4485 return zero_count; 4486 } 4487 } 4488 this._fillCurrentWord(); 4489 return zero_count + this._skipLeadingZero(); 4490 } 4491 }, { 4492 key: 'readUEG', 4493 value: function readUEG() { 4494 // unsigned exponential golomb 4495 var leading_zeros = this._skipLeadingZero(); 4496 return this.readBits(leading_zeros + 1) - 1; 4497 } 4498 }, { 4499 key: 'readSEG', 4500 value: function readSEG() { 4501 // signed exponential golomb 4502 var value = this.readUEG(); 4503 if (value & 0x01) { 4504 return value + 1 >>> 1; 4505 } else { 4506 return -1 * (value >>> 1); 4507 } 4508 } 4509 }]); 4510 4511 return ExpGolomb; 4512 }(); 4513 4514 exports.default = ExpGolomb; 4515 4516 },{"../utils/exception.js":40}],18:[function(_dereq_,module,exports){ 4517 'use strict'; 4518 4519 Object.defineProperty(exports, "__esModule", { 4520 value: true 4521 }); 4522 4523 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 4524 4525 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 4526 * Copyright (C) 2016 Bilibili. All Rights Reserved. 4527 * 4528 * @author zheng qian <xqq@xqq.im> 4529 * 4530 * Licensed under the Apache License, Version 2.0 (the "License"); 4531 * you may not use this file except in compliance with the License. 4532 * You may obtain a copy of the License at 4533 * 4534 * http://www.apache.org/licenses/LICENSE-2.0 4535 * 4536 * Unless required by applicable law or agreed to in writing, software 4537 * distributed under the License is distributed on an "AS IS" BASIS, 4538 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 4539 * See the License for the specific language governing permissions and 4540 * limitations under the License. 4541 */ 4542 4543 var _logger = _dereq_('../utils/logger.js'); 4544 4545 var _logger2 = _interopRequireDefault(_logger); 4546 4547 var _amfParser = _dereq_('./amf-parser.js'); 4548 4549 var _amfParser2 = _interopRequireDefault(_amfParser); 4550 4551 var _spsParser = _dereq_('./sps-parser.js'); 4552 4553 var _spsParser2 = _interopRequireDefault(_spsParser); 4554 4555 var _demuxErrors = _dereq_('./demux-errors.js'); 4556 4557 var _demuxErrors2 = _interopRequireDefault(_demuxErrors); 4558 4559 var _mediaInfo = _dereq_('../core/media-info.js'); 4560 4561 var _mediaInfo2 = _interopRequireDefault(_mediaInfo); 4562 4563 var _exception = _dereq_('../utils/exception.js'); 4564 4565 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 4566 4567 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 4568 4569 function Swap16(src) { 4570 return src >>> 8 & 0xFF | (src & 0xFF) << 8; 4571 } 4572 4573 function Swap32(src) { 4574 return (src & 0xFF000000) >>> 24 | (src & 0x00FF0000) >>> 8 | (src & 0x0000FF00) << 8 | (src & 0x000000FF) << 24; 4575 } 4576 4577 function ReadBig32(array, index) { 4578 return array[index] << 24 | array[index + 1] << 16 | array[index + 2] << 8 | array[index + 3]; 4579 } 4580 4581 var FLVDemuxer = function () { 4582 function FLVDemuxer(probeData, config) { 4583 _classCallCheck(this, FLVDemuxer); 4584 4585 this.TAG = 'FLVDemuxer'; 4586 4587 this._config = config; 4588 4589 this._onError = null; 4590 this._onMediaInfo = null; 4591 this._onMetaDataArrived = null; 4592 this._onScriptDataArrived = null; 4593 this._onTrackMetadata = null; 4594 this._onDataAvailable = null; 4595 4596 this._dataOffset = probeData.dataOffset; 4597 this._firstParse = true; 4598 this._dispatch = false; 4599 4600 this._hasAudio = probeData.hasAudioTrack; 4601 this._hasVideo = probeData.hasVideoTrack; 4602 4603 this._hasAudioFlagOverrided = false; 4604 this._hasVideoFlagOverrided = false; 4605 4606 this._audioInitialMetadataDispatched = false; 4607 this._videoInitialMetadataDispatched = false; 4608 4609 this._mediaInfo = new _mediaInfo2.default(); 4610 this._mediaInfo.hasAudio = this._hasAudio; 4611 this._mediaInfo.hasVideo = this._hasVideo; 4612 this._metadata = null; 4613 this._audioMetadata = null; 4614 this._videoMetadata = null; 4615 4616 this._naluLengthSize = 4; 4617 this._timestampBase = 0; // int32, in milliseconds 4618 this._timescale = 1000; 4619 this._duration = 0; // int32, in milliseconds 4620 this._durationOverrided = false; 4621 this._referenceFrameRate = { 4622 fixed: true, 4623 fps: 23.976, 4624 fps_num: 23976, 4625 fps_den: 1000 4626 }; 4627 4628 this._flvSoundRateTable = [5500, 11025, 22050, 44100, 48000]; 4629 4630 this._mpegSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350]; 4631 4632 this._mpegAudioV10SampleRateTable = [44100, 48000, 32000, 0]; 4633 this._mpegAudioV20SampleRateTable = [22050, 24000, 16000, 0]; 4634 this._mpegAudioV25SampleRateTable = [11025, 12000, 8000, 0]; 4635 4636 this._mpegAudioL1BitRateTable = [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1]; 4637 this._mpegAudioL2BitRateTable = [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1]; 4638 this._mpegAudioL3BitRateTable = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1]; 4639 4640 this._videoTrack = { type: 'video', id: 1, sequenceNumber: 0, samples: [], length: 0 }; 4641 this._audioTrack = { type: 'audio', id: 2, sequenceNumber: 0, samples: [], length: 0 }; 4642 4643 this._littleEndian = function () { 4644 var buf = new ArrayBuffer(2); 4645 new DataView(buf).setInt16(0, 256, true); // little-endian write 4646 return new Int16Array(buf)[0] === 256; // platform-spec read, if equal then LE 4647 }(); 4648 } 4649 4650 _createClass(FLVDemuxer, [{ 4651 key: 'destroy', 4652 value: function destroy() { 4653 this._mediaInfo = null; 4654 this._metadata = null; 4655 this._audioMetadata = null; 4656 this._videoMetadata = null; 4657 this._videoTrack = null; 4658 this._audioTrack = null; 4659 4660 this._onError = null; 4661 this._onMediaInfo = null; 4662 this._onMetaDataArrived = null; 4663 this._onScriptDataArrived = null; 4664 this._onTrackMetadata = null; 4665 this._onDataAvailable = null; 4666 } 4667 }, { 4668 key: 'bindDataSource', 4669 value: function bindDataSource(loader) { 4670 loader.onDataArrival = this.parseChunks.bind(this); 4671 return this; 4672 } 4673 4674 // prototype: function(type: string, metadata: any): void 4675 4676 }, { 4677 key: 'resetMediaInfo', 4678 value: function resetMediaInfo() { 4679 this._mediaInfo = new _mediaInfo2.default(); 4680 } 4681 }, { 4682 key: '_isInitialMetadataDispatched', 4683 value: function _isInitialMetadataDispatched() { 4684 if (this._hasAudio && this._hasVideo) { 4685 // both audio & video 4686 return this._audioInitialMetadataDispatched && this._videoInitialMetadataDispatched; 4687 } 4688 if (this._hasAudio && !this._hasVideo) { 4689 // audio only 4690 return this._audioInitialMetadataDispatched; 4691 } 4692 if (!this._hasAudio && this._hasVideo) { 4693 // video only 4694 return this._videoInitialMetadataDispatched; 4695 } 4696 return false; 4697 } 4698 4699 // function parseChunks(chunk: ArrayBuffer, byteStart: number): number; 4700 4701 }, { 4702 key: 'parseChunks', 4703 value: function parseChunks(chunk, byteStart) { 4704 if (!this._onError || !this._onMediaInfo || !this._onTrackMetadata || !this._onDataAvailable) { 4705 throw new _exception.IllegalStateException('Flv: onError & onMediaInfo & onTrackMetadata & onDataAvailable callback must be specified'); 4706 } 4707 4708 var offset = 0; 4709 var le = this._littleEndian; 4710 4711 if (byteStart === 0) { 4712 // buffer with FLV header 4713 if (chunk.byteLength > 13) { 4714 var probeData = FLVDemuxer.probe(chunk); 4715 offset = probeData.dataOffset; 4716 } else { 4717 return 0; 4718 } 4719 } 4720 4721 if (this._firstParse) { 4722 // handle PreviousTagSize0 before Tag1 4723 this._firstParse = false; 4724 if (byteStart + offset !== this._dataOffset) { 4725 _logger2.default.w(this.TAG, 'First time parsing but chunk byteStart invalid!'); 4726 } 4727 4728 var v = new DataView(chunk, offset); 4729 var prevTagSize0 = v.getUint32(0, !le); 4730 if (prevTagSize0 !== 0) { 4731 _logger2.default.w(this.TAG, 'PrevTagSize0 !== 0 !!!'); 4732 } 4733 offset += 4; 4734 } 4735 4736 while (offset < chunk.byteLength) { 4737 this._dispatch = true; 4738 4739 var _v = new DataView(chunk, offset); 4740 4741 if (offset + 11 + 4 > chunk.byteLength) { 4742 // data not enough for parsing an flv tag 4743 break; 4744 } 4745 4746 var tagType = _v.getUint8(0); 4747 var dataSize = _v.getUint32(0, !le) & 0x00FFFFFF; 4748 4749 if (offset + 11 + dataSize + 4 > chunk.byteLength) { 4750 // data not enough for parsing actual data body 4751 break; 4752 } 4753 4754 if (tagType !== 8 && tagType !== 9 && tagType !== 18) { 4755 _logger2.default.w(this.TAG, 'Unsupported tag type ' + tagType + ', skipped'); 4756 // consume the whole tag (skip it) 4757 offset += 11 + dataSize + 4; 4758 continue; 4759 } 4760 4761 var ts2 = _v.getUint8(4); 4762 var ts1 = _v.getUint8(5); 4763 var ts0 = _v.getUint8(6); 4764 var ts3 = _v.getUint8(7); 4765 4766 var timestamp = ts0 | ts1 << 8 | ts2 << 16 | ts3 << 24; 4767 4768 var streamId = _v.getUint32(7, !le) & 0x00FFFFFF; 4769 if (streamId !== 0) { 4770 _logger2.default.w(this.TAG, 'Meet tag which has StreamID != 0!'); 4771 } 4772 4773 var dataOffset = offset + 11; 4774 4775 switch (tagType) { 4776 case 8: 4777 // Audio 4778 this._parseAudioData(chunk, dataOffset, dataSize, timestamp); 4779 break; 4780 case 9: 4781 // Video 4782 this._parseVideoData(chunk, dataOffset, dataSize, timestamp, byteStart + offset); 4783 break; 4784 case 18: 4785 // ScriptDataObject 4786 this._parseScriptData(chunk, dataOffset, dataSize); 4787 break; 4788 } 4789 4790 var prevTagSize = _v.getUint32(11 + dataSize, !le); 4791 if (prevTagSize !== 11 + dataSize) { 4792 _logger2.default.w(this.TAG, 'Invalid PrevTagSize ' + prevTagSize); 4793 } 4794 4795 offset += 11 + dataSize + 4; // tagBody + dataSize + prevTagSize 4796 } 4797 4798 // dispatch parsed frames to consumer (typically, the remuxer) 4799 if (this._isInitialMetadataDispatched()) { 4800 if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) { 4801 this._onDataAvailable(this._audioTrack, this._videoTrack); 4802 } 4803 } 4804 4805 return offset; // consumed bytes, just equals latest offset index 4806 } 4807 }, { 4808 key: '_parseScriptData', 4809 value: function _parseScriptData(arrayBuffer, dataOffset, dataSize) { 4810 var scriptData = _amfParser2.default.parseScriptData(arrayBuffer, dataOffset, dataSize); 4811 4812 if (scriptData.hasOwnProperty('onMetaData')) { 4813 if (scriptData.onMetaData == null || _typeof(scriptData.onMetaData) !== 'object') { 4814 _logger2.default.w(this.TAG, 'Invalid onMetaData structure!'); 4815 return; 4816 } 4817 if (this._metadata) { 4818 _logger2.default.w(this.TAG, 'Found another onMetaData tag!'); 4819 } 4820 this._metadata = scriptData; 4821 var onMetaData = this._metadata.onMetaData; 4822 4823 if (this._onMetaDataArrived) { 4824 this._onMetaDataArrived(Object.assign({}, onMetaData)); 4825 } 4826 4827 if (typeof onMetaData.hasAudio === 'boolean') { 4828 // hasAudio 4829 if (this._hasAudioFlagOverrided === false) { 4830 this._hasAudio = onMetaData.hasAudio; 4831 this._mediaInfo.hasAudio = this._hasAudio; 4832 } 4833 } 4834 if (typeof onMetaData.hasVideo === 'boolean') { 4835 // hasVideo 4836 if (this._hasVideoFlagOverrided === false) { 4837 this._hasVideo = onMetaData.hasVideo; 4838 this._mediaInfo.hasVideo = this._hasVideo; 4839 } 4840 } 4841 if (typeof onMetaData.audiodatarate === 'number') { 4842 // audiodatarate 4843 this._mediaInfo.audioDataRate = onMetaData.audiodatarate; 4844 } 4845 if (typeof onMetaData.videodatarate === 'number') { 4846 // videodatarate 4847 this._mediaInfo.videoDataRate = onMetaData.videodatarate; 4848 } 4849 if (typeof onMetaData.width === 'number') { 4850 // width 4851 this._mediaInfo.width = onMetaData.width; 4852 } 4853 if (typeof onMetaData.height === 'number') { 4854 // height 4855 this._mediaInfo.height = onMetaData.height; 4856 } 4857 if (typeof onMetaData.duration === 'number') { 4858 // duration 4859 if (!this._durationOverrided) { 4860 var duration = Math.floor(onMetaData.duration * this._timescale); 4861 this._duration = duration; 4862 this._mediaInfo.duration = duration; 4863 } 4864 } else { 4865 this._mediaInfo.duration = 0; 4866 } 4867 if (typeof onMetaData.framerate === 'number') { 4868 // framerate 4869 var fps_num = Math.floor(onMetaData.framerate * 1000); 4870 if (fps_num > 0) { 4871 var fps = fps_num / 1000; 4872 this._referenceFrameRate.fixed = true; 4873 this._referenceFrameRate.fps = fps; 4874 this._referenceFrameRate.fps_num = fps_num; 4875 this._referenceFrameRate.fps_den = 1000; 4876 this._mediaInfo.fps = fps; 4877 } 4878 } 4879 if (_typeof(onMetaData.keyframes) === 'object') { 4880 // keyframes 4881 this._mediaInfo.hasKeyframesIndex = true; 4882 var keyframes = onMetaData.keyframes; 4883 this._mediaInfo.keyframesIndex = this._parseKeyframesIndex(keyframes); 4884 onMetaData.keyframes = null; // keyframes has been extracted, remove it 4885 } else { 4886 this._mediaInfo.hasKeyframesIndex = false; 4887 } 4888 this._dispatch = false; 4889 this._mediaInfo.metadata = onMetaData; 4890 _logger2.default.v(this.TAG, 'Parsed onMetaData'); 4891 if (this._mediaInfo.isComplete()) { 4892 this._onMediaInfo(this._mediaInfo); 4893 } 4894 } 4895 4896 if (Object.keys(scriptData).length > 0) { 4897 if (this._onScriptDataArrived) { 4898 this._onScriptDataArrived(Object.assign({}, scriptData)); 4899 } 4900 } 4901 } 4902 }, { 4903 key: '_parseKeyframesIndex', 4904 value: function _parseKeyframesIndex(keyframes) { 4905 var times = []; 4906 var filepositions = []; 4907 4908 // ignore first keyframe which is actually AVC Sequence Header (AVCDecoderConfigurationRecord) 4909 for (var i = 1; i < keyframes.times.length; i++) { 4910 var time = this._timestampBase + Math.floor(keyframes.times[i] * 1000); 4911 times.push(time); 4912 filepositions.push(keyframes.filepositions[i]); 4913 } 4914 4915 return { 4916 times: times, 4917 filepositions: filepositions 4918 }; 4919 } 4920 }, { 4921 key: '_parseAudioData', 4922 value: function _parseAudioData(arrayBuffer, dataOffset, dataSize, tagTimestamp) { 4923 if (dataSize <= 1) { 4924 _logger2.default.w(this.TAG, 'Flv: Invalid audio packet, missing SoundData payload!'); 4925 return; 4926 } 4927 4928 if (this._hasAudioFlagOverrided === true && this._hasAudio === false) { 4929 // If hasAudio: false indicated explicitly in MediaDataSource, 4930 // Ignore all the audio packets 4931 return; 4932 } 4933 4934 var le = this._littleEndian; 4935 var v = new DataView(arrayBuffer, dataOffset, dataSize); 4936 4937 var soundSpec = v.getUint8(0); 4938 4939 var soundFormat = soundSpec >>> 4; 4940 if (soundFormat !== 2 && soundFormat !== 10) { 4941 // MP3 or AAC 4942 this._onError(_demuxErrors2.default.CODEC_UNSUPPORTED, 'Flv: Unsupported audio codec idx: ' + soundFormat); 4943 return; 4944 } 4945 4946 var soundRate = 0; 4947 var soundRateIndex = (soundSpec & 12) >>> 2; 4948 if (soundRateIndex >= 0 && soundRateIndex <= 4) { 4949 soundRate = this._flvSoundRateTable[soundRateIndex]; 4950 } else { 4951 this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: Invalid audio sample rate idx: ' + soundRateIndex); 4952 return; 4953 } 4954 4955 var soundSize = (soundSpec & 2) >>> 1; // unused 4956 var soundType = soundSpec & 1; 4957 4958 var meta = this._audioMetadata; 4959 var track = this._audioTrack; 4960 4961 if (!meta) { 4962 if (this._hasAudio === false && this._hasAudioFlagOverrided === false) { 4963 this._hasAudio = true; 4964 this._mediaInfo.hasAudio = true; 4965 } 4966 4967 // initial metadata 4968 meta = this._audioMetadata = {}; 4969 meta.type = 'audio'; 4970 meta.id = track.id; 4971 meta.timescale = this._timescale; 4972 meta.duration = this._duration; 4973 meta.audioSampleRate = soundRate; 4974 meta.channelCount = soundType === 0 ? 1 : 2; 4975 } 4976 4977 if (soundFormat === 10) { 4978 // AAC 4979 var aacData = this._parseAACAudioData(arrayBuffer, dataOffset + 1, dataSize - 1); 4980 if (aacData == undefined) { 4981 return; 4982 } 4983 4984 if (aacData.packetType === 0) { 4985 // AAC sequence header (AudioSpecificConfig) 4986 if (meta.config) { 4987 _logger2.default.w(this.TAG, 'Found another AudioSpecificConfig!'); 4988 } 4989 var misc = aacData.data; 4990 meta.audioSampleRate = misc.samplingRate; 4991 meta.channelCount = misc.channelCount; 4992 meta.codec = misc.codec; 4993 meta.originalCodec = misc.originalCodec; 4994 meta.config = misc.config; 4995 // The decode result of an aac sample is 1024 PCM samples 4996 meta.refSampleDuration = 1024 / meta.audioSampleRate * meta.timescale; 4997 _logger2.default.v(this.TAG, 'Parsed AudioSpecificConfig'); 4998 4999 if (this._isInitialMetadataDispatched()) { 5000 // Non-initial metadata, force dispatch (or flush) parsed frames to remuxer 5001 if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) { 5002 this._onDataAvailable(this._audioTrack, this._videoTrack); 5003 } 5004 } else { 5005 this._audioInitialMetadataDispatched = true; 5006 } 5007 // then notify new metadata 5008 this._dispatch = false; 5009 this._onTrackMetadata('audio', meta); 5010 5011 var mi = this._mediaInfo; 5012 mi.audioCodec = meta.originalCodec; 5013 mi.audioSampleRate = meta.audioSampleRate; 5014 mi.audioChannelCount = meta.channelCount; 5015 if (mi.hasVideo) { 5016 if (mi.videoCodec != null) { 5017 mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"'; 5018 } 5019 } else { 5020 mi.mimeType = 'video/x-flv; codecs="' + mi.audioCodec + '"'; 5021 } 5022 if (mi.isComplete()) { 5023 this._onMediaInfo(mi); 5024 } 5025 } else if (aacData.packetType === 1) { 5026 // AAC raw frame data 5027 var dts = this._timestampBase + tagTimestamp; 5028 var aacSample = { unit: aacData.data, length: aacData.data.byteLength, dts: dts, pts: dts }; 5029 track.samples.push(aacSample); 5030 track.length += aacData.data.length; 5031 } else { 5032 _logger2.default.e(this.TAG, 'Flv: Unsupported AAC data type ' + aacData.packetType); 5033 } 5034 } else if (soundFormat === 2) { 5035 // MP3 5036 if (!meta.codec) { 5037 // We need metadata for mp3 audio track, extract info from frame header 5038 var _misc = this._parseMP3AudioData(arrayBuffer, dataOffset + 1, dataSize - 1, true); 5039 if (_misc == undefined) { 5040 return; 5041 } 5042 meta.audioSampleRate = _misc.samplingRate; 5043 meta.channelCount = _misc.channelCount; 5044 meta.codec = _misc.codec; 5045 meta.originalCodec = _misc.originalCodec; 5046 // The decode result of an mp3 sample is 1152 PCM samples 5047 meta.refSampleDuration = 1152 / meta.audioSampleRate * meta.timescale; 5048 _logger2.default.v(this.TAG, 'Parsed MPEG Audio Frame Header'); 5049 5050 this._audioInitialMetadataDispatched = true; 5051 this._onTrackMetadata('audio', meta); 5052 5053 var _mi = this._mediaInfo; 5054 _mi.audioCodec = meta.codec; 5055 _mi.audioSampleRate = meta.audioSampleRate; 5056 _mi.audioChannelCount = meta.channelCount; 5057 _mi.audioDataRate = _misc.bitRate; 5058 if (_mi.hasVideo) { 5059 if (_mi.videoCodec != null) { 5060 _mi.mimeType = 'video/x-flv; codecs="' + _mi.videoCodec + ',' + _mi.audioCodec + '"'; 5061 } 5062 } else { 5063 _mi.mimeType = 'video/x-flv; codecs="' + _mi.audioCodec + '"'; 5064 } 5065 if (_mi.isComplete()) { 5066 this._onMediaInfo(_mi); 5067 } 5068 } 5069 5070 // This packet is always a valid audio packet, extract it 5071 var data = this._parseMP3AudioData(arrayBuffer, dataOffset + 1, dataSize - 1, false); 5072 if (data == undefined) { 5073 return; 5074 } 5075 var _dts = this._timestampBase + tagTimestamp; 5076 var mp3Sample = { unit: data, length: data.byteLength, dts: _dts, pts: _dts }; 5077 track.samples.push(mp3Sample); 5078 track.length += data.length; 5079 } 5080 } 5081 }, { 5082 key: '_parseAACAudioData', 5083 value: function _parseAACAudioData(arrayBuffer, dataOffset, dataSize) { 5084 if (dataSize <= 1) { 5085 _logger2.default.w(this.TAG, 'Flv: Invalid AAC packet, missing AACPacketType or/and Data!'); 5086 return; 5087 } 5088 5089 var result = {}; 5090 var array = new Uint8Array(arrayBuffer, dataOffset, dataSize); 5091 5092 result.packetType = array[0]; 5093 5094 if (array[0] === 0) { 5095 result.data = this._parseAACAudioSpecificConfig(arrayBuffer, dataOffset + 1, dataSize - 1); 5096 } else { 5097 result.data = array.subarray(1); 5098 } 5099 5100 return result; 5101 } 5102 }, { 5103 key: '_parseAACAudioSpecificConfig', 5104 value: function _parseAACAudioSpecificConfig(arrayBuffer, dataOffset, dataSize) { 5105 var array = new Uint8Array(arrayBuffer, dataOffset, dataSize); 5106 var config = null; 5107 5108 /* Audio Object Type: 5109 0: Null 5110 1: AAC Main 5111 2: AAC LC 5112 3: AAC SSR (Scalable Sample Rate) 5113 4: AAC LTP (Long Term Prediction) 5114 5: HE-AAC / SBR (Spectral Band Replication) 5115 6: AAC Scalable 5116 */ 5117 5118 var audioObjectType = 0; 5119 var originalAudioObjectType = 0; 5120 var audioExtensionObjectType = null; 5121 var samplingIndex = 0; 5122 var extensionSamplingIndex = null; 5123 5124 // 5 bits 5125 audioObjectType = originalAudioObjectType = array[0] >>> 3; 5126 // 4 bits 5127 samplingIndex = (array[0] & 0x07) << 1 | array[1] >>> 7; 5128 if (samplingIndex < 0 || samplingIndex >= this._mpegSamplingRates.length) { 5129 this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: AAC invalid sampling frequency index!'); 5130 return; 5131 } 5132 5133 var samplingFrequence = this._mpegSamplingRates[samplingIndex]; 5134 5135 // 4 bits 5136 var channelConfig = (array[1] & 0x78) >>> 3; 5137 if (channelConfig < 0 || channelConfig >= 8) { 5138 this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: AAC invalid channel configuration'); 5139 return; 5140 } 5141 5142 if (audioObjectType === 5) { 5143 // HE-AAC? 5144 // 4 bits 5145 extensionSamplingIndex = (array[1] & 0x07) << 1 | array[2] >>> 7; 5146 // 5 bits 5147 audioExtensionObjectType = (array[2] & 0x7C) >>> 2; 5148 } 5149 5150 // workarounds for various browsers 5151 var userAgent = self.navigator.userAgent.toLowerCase(); 5152 5153 if (userAgent.indexOf('firefox') !== -1) { 5154 // firefox: use SBR (HE-AAC) if freq less than 24kHz 5155 if (samplingIndex >= 6) { 5156 audioObjectType = 5; 5157 config = new Array(4); 5158 extensionSamplingIndex = samplingIndex - 3; 5159 } else { 5160 // use LC-AAC 5161 audioObjectType = 2; 5162 config = new Array(2); 5163 extensionSamplingIndex = samplingIndex; 5164 } 5165 } else if (userAgent.indexOf('android') !== -1) { 5166 // android: always use LC-AAC 5167 audioObjectType = 2; 5168 config = new Array(2); 5169 extensionSamplingIndex = samplingIndex; 5170 } else { 5171 // for other browsers, e.g. chrome... 5172 // Always use HE-AAC to make it easier to switch aac codec profile 5173 audioObjectType = 5; 5174 extensionSamplingIndex = samplingIndex; 5175 config = new Array(4); 5176 5177 if (samplingIndex >= 6) { 5178 extensionSamplingIndex = samplingIndex - 3; 5179 } else if (channelConfig === 1) { 5180 // Mono channel 5181 audioObjectType = 2; 5182 config = new Array(2); 5183 extensionSamplingIndex = samplingIndex; 5184 } 5185 } 5186 5187 config[0] = audioObjectType << 3; 5188 config[0] |= (samplingIndex & 0x0F) >>> 1; 5189 config[1] = (samplingIndex & 0x0F) << 7; 5190 config[1] |= (channelConfig & 0x0F) << 3; 5191 if (audioObjectType === 5) { 5192 config[1] |= (extensionSamplingIndex & 0x0F) >>> 1; 5193 config[2] = (extensionSamplingIndex & 0x01) << 7; 5194 // extended audio object type: force to 2 (LC-AAC) 5195 config[2] |= 2 << 2; 5196 config[3] = 0; 5197 } 5198 5199 return { 5200 config: config, 5201 samplingRate: samplingFrequence, 5202 channelCount: channelConfig, 5203 codec: 'mp4a.40.' + audioObjectType, 5204 originalCodec: 'mp4a.40.' + originalAudioObjectType 5205 }; 5206 } 5207 }, { 5208 key: '_parseMP3AudioData', 5209 value: function _parseMP3AudioData(arrayBuffer, dataOffset, dataSize, requestHeader) { 5210 if (dataSize < 4) { 5211 _logger2.default.w(this.TAG, 'Flv: Invalid MP3 packet, header missing!'); 5212 return; 5213 } 5214 5215 var le = this._littleEndian; 5216 var array = new Uint8Array(arrayBuffer, dataOffset, dataSize); 5217 var result = null; 5218 5219 if (requestHeader) { 5220 if (array[0] !== 0xFF) { 5221 return; 5222 } 5223 var ver = array[1] >>> 3 & 0x03; 5224 var layer = (array[1] & 0x06) >> 1; 5225 5226 var bitrate_index = (array[2] & 0xF0) >>> 4; 5227 var sampling_freq_index = (array[2] & 0x0C) >>> 2; 5228 5229 var channel_mode = array[3] >>> 6 & 0x03; 5230 var channel_count = channel_mode !== 3 ? 2 : 1; 5231 5232 var sample_rate = 0; 5233 var bit_rate = 0; 5234 var object_type = 34; // Layer-3, listed in MPEG-4 Audio Object Types 5235 5236 var codec = 'mp3'; 5237 5238 switch (ver) { 5239 case 0: 5240 // MPEG 2.5 5241 sample_rate = this._mpegAudioV25SampleRateTable[sampling_freq_index]; 5242 break; 5243 case 2: 5244 // MPEG 2 5245 sample_rate = this._mpegAudioV20SampleRateTable[sampling_freq_index]; 5246 break; 5247 case 3: 5248 // MPEG 1 5249 sample_rate = this._mpegAudioV10SampleRateTable[sampling_freq_index]; 5250 break; 5251 } 5252 5253 switch (layer) { 5254 case 1: 5255 // Layer 3 5256 object_type = 34; 5257 if (bitrate_index < this._mpegAudioL3BitRateTable.length) { 5258 bit_rate = this._mpegAudioL3BitRateTable[bitrate_index]; 5259 } 5260 break; 5261 case 2: 5262 // Layer 2 5263 object_type = 33; 5264 if (bitrate_index < this._mpegAudioL2BitRateTable.length) { 5265 bit_rate = this._mpegAudioL2BitRateTable[bitrate_index]; 5266 } 5267 break; 5268 case 3: 5269 // Layer 1 5270 object_type = 32; 5271 if (bitrate_index < this._mpegAudioL1BitRateTable.length) { 5272 bit_rate = this._mpegAudioL1BitRateTable[bitrate_index]; 5273 } 5274 break; 5275 } 5276 5277 result = { 5278 bitRate: bit_rate, 5279 samplingRate: sample_rate, 5280 channelCount: channel_count, 5281 codec: codec, 5282 originalCodec: codec 5283 }; 5284 } else { 5285 result = array; 5286 } 5287 5288 return result; 5289 } 5290 }, { 5291 key: '_parseVideoData', 5292 value: function _parseVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition) { 5293 if (dataSize <= 1) { 5294 _logger2.default.w(this.TAG, 'Flv: Invalid video packet, missing VideoData payload!'); 5295 return; 5296 } 5297 5298 if (this._hasVideoFlagOverrided === true && this._hasVideo === false) { 5299 // If hasVideo: false indicated explicitly in MediaDataSource, 5300 // Ignore all the video packets 5301 return; 5302 } 5303 5304 var spec = new Uint8Array(arrayBuffer, dataOffset, dataSize)[0]; 5305 5306 var frameType = (spec & 240) >>> 4; 5307 var codecId = spec & 15; 5308 5309 if (codecId !== 7) { 5310 this._onError(_demuxErrors2.default.CODEC_UNSUPPORTED, 'Flv: Unsupported codec in video frame: ' + codecId); 5311 return; 5312 } 5313 5314 this._parseAVCVideoPacket(arrayBuffer, dataOffset + 1, dataSize - 1, tagTimestamp, tagPosition, frameType); 5315 } 5316 }, { 5317 key: '_parseAVCVideoPacket', 5318 value: function _parseAVCVideoPacket(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType) { 5319 if (dataSize < 4) { 5320 _logger2.default.w(this.TAG, 'Flv: Invalid AVC packet, missing AVCPacketType or/and CompositionTime'); 5321 return; 5322 } 5323 5324 var le = this._littleEndian; 5325 var v = new DataView(arrayBuffer, dataOffset, dataSize); 5326 5327 var packetType = v.getUint8(0); 5328 var cts_unsigned = v.getUint32(0, !le) & 0x00FFFFFF; 5329 var cts = cts_unsigned << 8 >> 8; // convert to 24-bit signed int 5330 5331 if (packetType === 0) { 5332 // AVCDecoderConfigurationRecord 5333 this._parseAVCDecoderConfigurationRecord(arrayBuffer, dataOffset + 4, dataSize - 4); 5334 } else if (packetType === 1) { 5335 // One or more Nalus 5336 this._parseAVCVideoData(arrayBuffer, dataOffset + 4, dataSize - 4, tagTimestamp, tagPosition, frameType, cts); 5337 } else if (packetType === 2) { 5338 // empty, AVC end of sequence 5339 } else { 5340 this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: Invalid video packet type ' + packetType); 5341 return; 5342 } 5343 } 5344 }, { 5345 key: '_parseAVCDecoderConfigurationRecord', 5346 value: function _parseAVCDecoderConfigurationRecord(arrayBuffer, dataOffset, dataSize) { 5347 if (dataSize < 7) { 5348 _logger2.default.w(this.TAG, 'Flv: Invalid AVCDecoderConfigurationRecord, lack of data!'); 5349 return; 5350 } 5351 5352 var meta = this._videoMetadata; 5353 var track = this._videoTrack; 5354 var le = this._littleEndian; 5355 var v = new DataView(arrayBuffer, dataOffset, dataSize); 5356 5357 if (!meta) { 5358 if (this._hasVideo === false && this._hasVideoFlagOverrided === false) { 5359 this._hasVideo = true; 5360 this._mediaInfo.hasVideo = true; 5361 } 5362 5363 meta = this._videoMetadata = {}; 5364 meta.type = 'video'; 5365 meta.id = track.id; 5366 meta.timescale = this._timescale; 5367 meta.duration = this._duration; 5368 } else { 5369 if (typeof meta.avcc !== 'undefined') { 5370 _logger2.default.w(this.TAG, 'Found another AVCDecoderConfigurationRecord!'); 5371 } 5372 } 5373 5374 var version = v.getUint8(0); // configurationVersion 5375 var avcProfile = v.getUint8(1); // avcProfileIndication 5376 var profileCompatibility = v.getUint8(2); // profile_compatibility 5377 var avcLevel = v.getUint8(3); // AVCLevelIndication 5378 5379 if (version !== 1 || avcProfile === 0) { 5380 this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord'); 5381 return; 5382 } 5383 5384 this._naluLengthSize = (v.getUint8(4) & 3) + 1; // lengthSizeMinusOne 5385 if (this._naluLengthSize !== 3 && this._naluLengthSize !== 4) { 5386 // holy shit!!! 5387 this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: Strange NaluLengthSizeMinusOne: ' + (this._naluLengthSize - 1)); 5388 return; 5389 } 5390 5391 var spsCount = v.getUint8(5) & 31; // numOfSequenceParameterSets 5392 if (spsCount === 0) { 5393 this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord: No SPS'); 5394 return; 5395 } else if (spsCount > 1) { 5396 _logger2.default.w(this.TAG, 'Flv: Strange AVCDecoderConfigurationRecord: SPS Count = ' + spsCount); 5397 } 5398 5399 var offset = 6; 5400 5401 for (var i = 0; i < spsCount; i++) { 5402 var len = v.getUint16(offset, !le); // sequenceParameterSetLength 5403 offset += 2; 5404 5405 if (len === 0) { 5406 continue; 5407 } 5408 5409 // Notice: Nalu without startcode header (00 00 00 01) 5410 var sps = new Uint8Array(arrayBuffer, dataOffset + offset, len); 5411 offset += len; 5412 5413 var config = _spsParser2.default.parseSPS(sps); 5414 if (i !== 0) { 5415 // ignore other sps's config 5416 continue; 5417 } 5418 5419 meta.codecWidth = config.codec_size.width; 5420 meta.codecHeight = config.codec_size.height; 5421 meta.presentWidth = config.present_size.width; 5422 meta.presentHeight = config.present_size.height; 5423 5424 meta.profile = config.profile_string; 5425 meta.level = config.level_string; 5426 meta.bitDepth = config.bit_depth; 5427 meta.chromaFormat = config.chroma_format; 5428 meta.sarRatio = config.sar_ratio; 5429 meta.frameRate = config.frame_rate; 5430 5431 if (config.frame_rate.fixed === false || config.frame_rate.fps_num === 0 || config.frame_rate.fps_den === 0) { 5432 meta.frameRate = this._referenceFrameRate; 5433 } 5434 5435 var fps_den = meta.frameRate.fps_den; 5436 var fps_num = meta.frameRate.fps_num; 5437 meta.refSampleDuration = meta.timescale * (fps_den / fps_num); 5438 5439 var codecArray = sps.subarray(1, 4); 5440 var codecString = 'avc1.'; 5441 for (var j = 0; j < 3; j++) { 5442 var h = codecArray[j].toString(16); 5443 if (h.length < 2) { 5444 h = '0' + h; 5445 } 5446 codecString += h; 5447 } 5448 meta.codec = codecString; 5449 5450 var mi = this._mediaInfo; 5451 mi.width = meta.codecWidth; 5452 mi.height = meta.codecHeight; 5453 mi.fps = meta.frameRate.fps; 5454 mi.profile = meta.profile; 5455 mi.level = meta.level; 5456 mi.refFrames = config.ref_frames; 5457 mi.chromaFormat = config.chroma_format_string; 5458 mi.sarNum = meta.sarRatio.width; 5459 mi.sarDen = meta.sarRatio.height; 5460 mi.videoCodec = codecString; 5461 5462 if (mi.hasAudio) { 5463 if (mi.audioCodec != null) { 5464 mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"'; 5465 } 5466 } else { 5467 mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + '"'; 5468 } 5469 if (mi.isComplete()) { 5470 this._onMediaInfo(mi); 5471 } 5472 } 5473 5474 var ppsCount = v.getUint8(offset); // numOfPictureParameterSets 5475 if (ppsCount === 0) { 5476 this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord: No PPS'); 5477 return; 5478 } else if (ppsCount > 1) { 5479 _logger2.default.w(this.TAG, 'Flv: Strange AVCDecoderConfigurationRecord: PPS Count = ' + ppsCount); 5480 } 5481 5482 offset++; 5483 5484 for (var _i = 0; _i < ppsCount; _i++) { 5485 var _len = v.getUint16(offset, !le); // pictureParameterSetLength 5486 offset += 2; 5487 5488 if (_len === 0) { 5489 continue; 5490 } 5491 5492 // pps is useless for extracting video information 5493 offset += _len; 5494 } 5495 5496 meta.avcc = new Uint8Array(dataSize); 5497 meta.avcc.set(new Uint8Array(arrayBuffer, dataOffset, dataSize), 0); 5498 _logger2.default.v(this.TAG, 'Parsed AVCDecoderConfigurationRecord'); 5499 5500 if (this._isInitialMetadataDispatched()) { 5501 // flush parsed frames 5502 if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) { 5503 this._onDataAvailable(this._audioTrack, this._videoTrack); 5504 } 5505 } else { 5506 this._videoInitialMetadataDispatched = true; 5507 } 5508 // notify new metadata 5509 this._dispatch = false; 5510 this._onTrackMetadata('video', meta); 5511 } 5512 }, { 5513 key: '_parseAVCVideoData', 5514 value: function _parseAVCVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType, cts) { 5515 var le = this._littleEndian; 5516 var v = new DataView(arrayBuffer, dataOffset, dataSize); 5517 5518 var units = [], 5519 length = 0; 5520 5521 var offset = 0; 5522 var lengthSize = this._naluLengthSize; 5523 var dts = this._timestampBase + tagTimestamp; 5524 var keyframe = frameType === 1; // from FLV Frame Type constants 5525 5526 while (offset < dataSize) { 5527 if (offset + 4 >= dataSize) { 5528 _logger2.default.w(this.TAG, 'Malformed Nalu near timestamp ' + dts + ', offset = ' + offset + ', dataSize = ' + dataSize); 5529 break; // data not enough for next Nalu 5530 } 5531 // Nalu with length-header (AVC1) 5532 var naluSize = v.getUint32(offset, !le); // Big-Endian read 5533 if (lengthSize === 3) { 5534 naluSize >>>= 8; 5535 } 5536 if (naluSize > dataSize - lengthSize) { 5537 _logger2.default.w(this.TAG, 'Malformed Nalus near timestamp ' + dts + ', NaluSize > DataSize!'); 5538 return; 5539 } 5540 5541 var unitType = v.getUint8(offset + lengthSize) & 0x1F; 5542 5543 if (unitType === 5) { 5544 // IDR 5545 keyframe = true; 5546 } 5547 5548 var data = new Uint8Array(arrayBuffer, dataOffset + offset, lengthSize + naluSize); 5549 var unit = { type: unitType, data: data }; 5550 units.push(unit); 5551 length += data.byteLength; 5552 5553 offset += lengthSize + naluSize; 5554 } 5555 5556 if (units.length) { 5557 var track = this._videoTrack; 5558 var avcSample = { 5559 units: units, 5560 length: length, 5561 isKeyframe: keyframe, 5562 dts: dts, 5563 cts: cts, 5564 pts: dts + cts 5565 }; 5566 if (keyframe) { 5567 avcSample.fileposition = tagPosition; 5568 } 5569 track.samples.push(avcSample); 5570 track.length += length; 5571 } 5572 } 5573 }, { 5574 key: 'onTrackMetadata', 5575 get: function get() { 5576 return this._onTrackMetadata; 5577 }, 5578 set: function set(callback) { 5579 this._onTrackMetadata = callback; 5580 } 5581 5582 // prototype: function(mediaInfo: MediaInfo): void 5583 5584 }, { 5585 key: 'onMediaInfo', 5586 get: function get() { 5587 return this._onMediaInfo; 5588 }, 5589 set: function set(callback) { 5590 this._onMediaInfo = callback; 5591 } 5592 }, { 5593 key: 'onMetaDataArrived', 5594 get: function get() { 5595 return this._onMetaDataArrived; 5596 }, 5597 set: function set(callback) { 5598 this._onMetaDataArrived = callback; 5599 } 5600 }, { 5601 key: 'onScriptDataArrived', 5602 get: function get() { 5603 return this._onScriptDataArrived; 5604 }, 5605 set: function set(callback) { 5606 this._onScriptDataArrived = callback; 5607 } 5608 5609 // prototype: function(type: number, info: string): void 5610 5611 }, { 5612 key: 'onError', 5613 get: function get() { 5614 return this._onError; 5615 }, 5616 set: function set(callback) { 5617 this._onError = callback; 5618 } 5619 5620 // prototype: function(videoTrack: any, audioTrack: any): void 5621 5622 }, { 5623 key: 'onDataAvailable', 5624 get: function get() { 5625 return this._onDataAvailable; 5626 }, 5627 set: function set(callback) { 5628 this._onDataAvailable = callback; 5629 } 5630 5631 // timestamp base for output samples, must be in milliseconds 5632 5633 }, { 5634 key: 'timestampBase', 5635 get: function get() { 5636 return this._timestampBase; 5637 }, 5638 set: function set(base) { 5639 this._timestampBase = base; 5640 } 5641 }, { 5642 key: 'overridedDuration', 5643 get: function get() { 5644 return this._duration; 5645 } 5646 5647 // Force-override media duration. Must be in milliseconds, int32 5648 , 5649 set: function set(duration) { 5650 this._durationOverrided = true; 5651 this._duration = duration; 5652 this._mediaInfo.duration = duration; 5653 } 5654 5655 // Force-override audio track present flag, boolean 5656 5657 }, { 5658 key: 'overridedHasAudio', 5659 set: function set(hasAudio) { 5660 this._hasAudioFlagOverrided = true; 5661 this._hasAudio = hasAudio; 5662 this._mediaInfo.hasAudio = hasAudio; 5663 } 5664 5665 // Force-override video track present flag, boolean 5666 5667 }, { 5668 key: 'overridedHasVideo', 5669 set: function set(hasVideo) { 5670 this._hasVideoFlagOverrided = true; 5671 this._hasVideo = hasVideo; 5672 this._mediaInfo.hasVideo = hasVideo; 5673 } 5674 }], [{ 5675 key: 'probe', 5676 value: function probe(buffer) { 5677 var data = new Uint8Array(buffer); 5678 var mismatch = { match: false }; 5679 5680 if (data[0] !== 0x46 || data[1] !== 0x4C || data[2] !== 0x56 || data[3] !== 0x01) { 5681 return mismatch; 5682 } 5683 5684 var hasAudio = (data[4] & 4) >>> 2 !== 0; 5685 var hasVideo = (data[4] & 1) !== 0; 5686 5687 var offset = ReadBig32(data, 5); 5688 5689 if (offset < 9) { 5690 return mismatch; 5691 } 5692 5693 return { 5694 match: true, 5695 consumed: offset, 5696 dataOffset: offset, 5697 hasAudioTrack: hasAudio, 5698 hasVideoTrack: hasVideo 5699 }; 5700 } 5701 }]); 5702 5703 return FLVDemuxer; 5704 }(); 5705 5706 exports.default = FLVDemuxer; 5707 5708 },{"../core/media-info.js":7,"../utils/exception.js":40,"../utils/logger.js":41,"./amf-parser.js":15,"./demux-errors.js":16,"./sps-parser.js":19}],19:[function(_dereq_,module,exports){ 5709 'use strict'; 5710 5711 Object.defineProperty(exports, "__esModule", { 5712 value: true 5713 }); 5714 5715 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 5716 * Copyright (C) 2016 Bilibili. All Rights Reserved. 5717 * 5718 * @author zheng qian <xqq@xqq.im> 5719 * 5720 * Licensed under the Apache License, Version 2.0 (the "License"); 5721 * you may not use this file except in compliance with the License. 5722 * You may obtain a copy of the License at 5723 * 5724 * http://www.apache.org/licenses/LICENSE-2.0 5725 * 5726 * Unless required by applicable law or agreed to in writing, software 5727 * distributed under the License is distributed on an "AS IS" BASIS, 5728 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 5729 * See the License for the specific language governing permissions and 5730 * limitations under the License. 5731 */ 5732 5733 var _expGolomb = _dereq_('./exp-golomb.js'); 5734 5735 var _expGolomb2 = _interopRequireDefault(_expGolomb); 5736 5737 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 5738 5739 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 5740 5741 var SPSParser = function () { 5742 function SPSParser() { 5743 _classCallCheck(this, SPSParser); 5744 } 5745 5746 _createClass(SPSParser, null, [{ 5747 key: '_ebsp2rbsp', 5748 value: function _ebsp2rbsp(uint8array) { 5749 var src = uint8array; 5750 var src_length = src.byteLength; 5751 var dst = new Uint8Array(src_length); 5752 var dst_idx = 0; 5753 5754 for (var i = 0; i < src_length; i++) { 5755 if (i >= 2) { 5756 // Unescape: Skip 0x03 after 00 00 5757 if (src[i] === 0x03 && src[i - 1] === 0x00 && src[i - 2] === 0x00) { 5758 continue; 5759 } 5760 } 5761 dst[dst_idx] = src[i]; 5762 dst_idx++; 5763 } 5764 5765 return new Uint8Array(dst.buffer, 0, dst_idx); 5766 } 5767 }, { 5768 key: 'parseSPS', 5769 value: function parseSPS(uint8array) { 5770 var rbsp = SPSParser._ebsp2rbsp(uint8array); 5771 var gb = new _expGolomb2.default(rbsp); 5772 5773 gb.readByte(); 5774 var profile_idc = gb.readByte(); // profile_idc 5775 gb.readByte(); // constraint_set_flags[5] + reserved_zero[3] 5776 var level_idc = gb.readByte(); // level_idc 5777 gb.readUEG(); // seq_parameter_set_id 5778 5779 var profile_string = SPSParser.getProfileString(profile_idc); 5780 var level_string = SPSParser.getLevelString(level_idc); 5781 var chroma_format_idc = 1; 5782 var chroma_format = 420; 5783 var chroma_format_table = [0, 420, 422, 444]; 5784 var bit_depth = 8; 5785 5786 if (profile_idc === 100 || profile_idc === 110 || profile_idc === 122 || profile_idc === 244 || profile_idc === 44 || profile_idc === 83 || profile_idc === 86 || profile_idc === 118 || profile_idc === 128 || profile_idc === 138 || profile_idc === 144) { 5787 5788 chroma_format_idc = gb.readUEG(); 5789 if (chroma_format_idc === 3) { 5790 gb.readBits(1); // separate_colour_plane_flag 5791 } 5792 if (chroma_format_idc <= 3) { 5793 chroma_format = chroma_format_table[chroma_format_idc]; 5794 } 5795 5796 bit_depth = gb.readUEG() + 8; // bit_depth_luma_minus8 5797 gb.readUEG(); // bit_depth_chroma_minus8 5798 gb.readBits(1); // qpprime_y_zero_transform_bypass_flag 5799 if (gb.readBool()) { 5800 // seq_scaling_matrix_present_flag 5801 var scaling_list_count = chroma_format_idc !== 3 ? 8 : 12; 5802 for (var i = 0; i < scaling_list_count; i++) { 5803 if (gb.readBool()) { 5804 // seq_scaling_list_present_flag 5805 if (i < 6) { 5806 SPSParser._skipScalingList(gb, 16); 5807 } else { 5808 SPSParser._skipScalingList(gb, 64); 5809 } 5810 } 5811 } 5812 } 5813 } 5814 gb.readUEG(); // log2_max_frame_num_minus4 5815 var pic_order_cnt_type = gb.readUEG(); 5816 if (pic_order_cnt_type === 0) { 5817 gb.readUEG(); // log2_max_pic_order_cnt_lsb_minus_4 5818 } else if (pic_order_cnt_type === 1) { 5819 gb.readBits(1); // delta_pic_order_always_zero_flag 5820 gb.readSEG(); // offset_for_non_ref_pic 5821 gb.readSEG(); // offset_for_top_to_bottom_field 5822 var num_ref_frames_in_pic_order_cnt_cycle = gb.readUEG(); 5823 for (var _i = 0; _i < num_ref_frames_in_pic_order_cnt_cycle; _i++) { 5824 gb.readSEG(); // offset_for_ref_frame 5825 } 5826 } 5827 var ref_frames = gb.readUEG(); // max_num_ref_frames 5828 gb.readBits(1); // gaps_in_frame_num_value_allowed_flag 5829 5830 var pic_width_in_mbs_minus1 = gb.readUEG(); 5831 var pic_height_in_map_units_minus1 = gb.readUEG(); 5832 5833 var frame_mbs_only_flag = gb.readBits(1); 5834 if (frame_mbs_only_flag === 0) { 5835 gb.readBits(1); // mb_adaptive_frame_field_flag 5836 } 5837 gb.readBits(1); // direct_8x8_inference_flag 5838 5839 var frame_crop_left_offset = 0; 5840 var frame_crop_right_offset = 0; 5841 var frame_crop_top_offset = 0; 5842 var frame_crop_bottom_offset = 0; 5843 5844 var frame_cropping_flag = gb.readBool(); 5845 if (frame_cropping_flag) { 5846 frame_crop_left_offset = gb.readUEG(); 5847 frame_crop_right_offset = gb.readUEG(); 5848 frame_crop_top_offset = gb.readUEG(); 5849 frame_crop_bottom_offset = gb.readUEG(); 5850 } 5851 5852 var sar_width = 1, 5853 sar_height = 1; 5854 var fps = 0, 5855 fps_fixed = true, 5856 fps_num = 0, 5857 fps_den = 0; 5858 5859 var vui_parameters_present_flag = gb.readBool(); 5860 if (vui_parameters_present_flag) { 5861 if (gb.readBool()) { 5862 // aspect_ratio_info_present_flag 5863 var aspect_ratio_idc = gb.readByte(); 5864 var sar_w_table = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2]; 5865 var sar_h_table = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1]; 5866 5867 if (aspect_ratio_idc > 0 && aspect_ratio_idc < 16) { 5868 sar_width = sar_w_table[aspect_ratio_idc - 1]; 5869 sar_height = sar_h_table[aspect_ratio_idc - 1]; 5870 } else if (aspect_ratio_idc === 255) { 5871 sar_width = gb.readByte() << 8 | gb.readByte(); 5872 sar_height = gb.readByte() << 8 | gb.readByte(); 5873 } 5874 } 5875 5876 if (gb.readBool()) { 5877 // overscan_info_present_flag 5878 gb.readBool(); // overscan_appropriate_flag 5879 } 5880 if (gb.readBool()) { 5881 // video_signal_type_present_flag 5882 gb.readBits(4); // video_format & video_full_range_flag 5883 if (gb.readBool()) { 5884 // colour_description_present_flag 5885 gb.readBits(24); // colour_primaries & transfer_characteristics & matrix_coefficients 5886 } 5887 } 5888 if (gb.readBool()) { 5889 // chroma_loc_info_present_flag 5890 gb.readUEG(); // chroma_sample_loc_type_top_field 5891 gb.readUEG(); // chroma_sample_loc_type_bottom_field 5892 } 5893 if (gb.readBool()) { 5894 // timing_info_present_flag 5895 var num_units_in_tick = gb.readBits(32); 5896 var time_scale = gb.readBits(32); 5897 fps_fixed = gb.readBool(); // fixed_frame_rate_flag 5898 5899 fps_num = time_scale; 5900 fps_den = num_units_in_tick * 2; 5901 fps = fps_num / fps_den; 5902 } 5903 } 5904 5905 var sarScale = 1; 5906 if (sar_width !== 1 || sar_height !== 1) { 5907 sarScale = sar_width / sar_height; 5908 } 5909 5910 var crop_unit_x = 0, 5911 crop_unit_y = 0; 5912 if (chroma_format_idc === 0) { 5913 crop_unit_x = 1; 5914 crop_unit_y = 2 - frame_mbs_only_flag; 5915 } else { 5916 var sub_wc = chroma_format_idc === 3 ? 1 : 2; 5917 var sub_hc = chroma_format_idc === 1 ? 2 : 1; 5918 crop_unit_x = sub_wc; 5919 crop_unit_y = sub_hc * (2 - frame_mbs_only_flag); 5920 } 5921 5922 var codec_width = (pic_width_in_mbs_minus1 + 1) * 16; 5923 var codec_height = (2 - frame_mbs_only_flag) * ((pic_height_in_map_units_minus1 + 1) * 16); 5924 5925 codec_width -= (frame_crop_left_offset + frame_crop_right_offset) * crop_unit_x; 5926 codec_height -= (frame_crop_top_offset + frame_crop_bottom_offset) * crop_unit_y; 5927 5928 var present_width = Math.ceil(codec_width * sarScale); 5929 5930 gb.destroy(); 5931 gb = null; 5932 5933 return { 5934 profile_string: profile_string, // baseline, high, high10, ... 5935 level_string: level_string, // 3, 3.1, 4, 4.1, 5, 5.1, ... 5936 bit_depth: bit_depth, // 8bit, 10bit, ... 5937 ref_frames: ref_frames, 5938 chroma_format: chroma_format, // 4:2:0, 4:2:2, ... 5939 chroma_format_string: SPSParser.getChromaFormatString(chroma_format), 5940 5941 frame_rate: { 5942 fixed: fps_fixed, 5943 fps: fps, 5944 fps_den: fps_den, 5945 fps_num: fps_num 5946 }, 5947 5948 sar_ratio: { 5949 width: sar_width, 5950 height: sar_height 5951 }, 5952 5953 codec_size: { 5954 width: codec_width, 5955 height: codec_height 5956 }, 5957 5958 present_size: { 5959 width: present_width, 5960 height: codec_height 5961 } 5962 }; 5963 } 5964 }, { 5965 key: '_skipScalingList', 5966 value: function _skipScalingList(gb, count) { 5967 var last_scale = 8, 5968 next_scale = 8; 5969 var delta_scale = 0; 5970 for (var i = 0; i < count; i++) { 5971 if (next_scale !== 0) { 5972 delta_scale = gb.readSEG(); 5973 next_scale = (last_scale + delta_scale + 256) % 256; 5974 } 5975 last_scale = next_scale === 0 ? last_scale : next_scale; 5976 } 5977 } 5978 }, { 5979 key: 'getProfileString', 5980 value: function getProfileString(profile_idc) { 5981 switch (profile_idc) { 5982 case 66: 5983 return 'Baseline'; 5984 case 77: 5985 return 'Main'; 5986 case 88: 5987 return 'Extended'; 5988 case 100: 5989 return 'High'; 5990 case 110: 5991 return 'High10'; 5992 case 122: 5993 return 'High422'; 5994 case 244: 5995 return 'High444'; 5996 default: 5997 return 'Unknown'; 5998 } 5999 } 6000 }, { 6001 key: 'getLevelString', 6002 value: function getLevelString(level_idc) { 6003 return (level_idc / 10).toFixed(1); 6004 } 6005 }, { 6006 key: 'getChromaFormatString', 6007 value: function getChromaFormatString(chroma) { 6008 switch (chroma) { 6009 case 420: 6010 return '4:2:0'; 6011 case 422: 6012 return '4:2:2'; 6013 case 444: 6014 return '4:4:4'; 6015 default: 6016 return 'Unknown'; 6017 } 6018 } 6019 }]); 6020 6021 return SPSParser; 6022 }(); 6023 6024 exports.default = SPSParser; 6025 6026 },{"./exp-golomb.js":17}],20:[function(_dereq_,module,exports){ 6027 'use strict'; 6028 6029 Object.defineProperty(exports, "__esModule", { 6030 value: true 6031 }); 6032 6033 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; /* 6034 * Copyright (C) 2016 Bilibili. All Rights Reserved. 6035 * 6036 * @author zheng qian <xqq@xqq.im> 6037 * 6038 * Licensed under the Apache License, Version 2.0 (the "License"); 6039 * you may not use this file except in compliance with the License. 6040 * You may obtain a copy of the License at 6041 * 6042 * http://www.apache.org/licenses/LICENSE-2.0 6043 * 6044 * Unless required by applicable law or agreed to in writing, software 6045 * distributed under the License is distributed on an "AS IS" BASIS, 6046 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6047 * See the License for the specific language governing permissions and 6048 * limitations under the License. 6049 */ 6050 6051 var _polyfill = _dereq_('./utils/polyfill.js'); 6052 6053 var _polyfill2 = _interopRequireDefault(_polyfill); 6054 6055 var _features = _dereq_('./core/features.js'); 6056 6057 var _features2 = _interopRequireDefault(_features); 6058 6059 var _loader = _dereq_('./io/loader.js'); 6060 6061 var _flvPlayer = _dereq_('./player/flv-player.js'); 6062 6063 var _flvPlayer2 = _interopRequireDefault(_flvPlayer); 6064 6065 var _nativePlayer = _dereq_('./player/native-player.js'); 6066 6067 var _nativePlayer2 = _interopRequireDefault(_nativePlayer); 6068 6069 var _playerEvents = _dereq_('./player/player-events.js'); 6070 6071 var _playerEvents2 = _interopRequireDefault(_playerEvents); 6072 6073 var _playerErrors = _dereq_('./player/player-errors.js'); 6074 6075 var _loggingControl = _dereq_('./utils/logging-control.js'); 6076 6077 var _loggingControl2 = _interopRequireDefault(_loggingControl); 6078 6079 var _exception = _dereq_('./utils/exception.js'); 6080 6081 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 6082 6083 // here are all the interfaces 6084 6085 // install polyfills 6086 _polyfill2.default.install(); 6087 6088 // factory method 6089 function createPlayer(mediaDataSource, optionalConfig) { 6090 var mds = mediaDataSource; 6091 if (mds == null || (typeof mds === 'undefined' ? 'undefined' : _typeof(mds)) !== 'object') { 6092 throw new _exception.InvalidArgumentException('MediaDataSource must be an javascript object!'); 6093 } 6094 6095 if (!mds.hasOwnProperty('type')) { 6096 throw new _exception.InvalidArgumentException('MediaDataSource must has type field to indicate video file type!'); 6097 } 6098 6099 switch (mds.type) { 6100 case 'flv': 6101 return new _flvPlayer2.default(mds, optionalConfig); 6102 default: 6103 return new _nativePlayer2.default(mds, optionalConfig); 6104 } 6105 } 6106 6107 // feature detection 6108 function isSupported() { 6109 return _features2.default.supportMSEH264Playback(); 6110 } 6111 6112 function getFeatureList() { 6113 return _features2.default.getFeatureList(); 6114 } 6115 6116 // interfaces 6117 var flvjs = {}; 6118 6119 flvjs.createPlayer = createPlayer; 6120 flvjs.isSupported = isSupported; 6121 flvjs.getFeatureList = getFeatureList; 6122 6123 flvjs.BaseLoader = _loader.BaseLoader; 6124 flvjs.LoaderStatus = _loader.LoaderStatus; 6125 flvjs.LoaderErrors = _loader.LoaderErrors; 6126 6127 flvjs.Events = _playerEvents2.default; 6128 flvjs.ErrorTypes = _playerErrors.ErrorTypes; 6129 flvjs.ErrorDetails = _playerErrors.ErrorDetails; 6130 6131 flvjs.FlvPlayer = _flvPlayer2.default; 6132 flvjs.NativePlayer = _nativePlayer2.default; 6133 flvjs.LoggingControl = _loggingControl2.default; 6134 6135 Object.defineProperty(flvjs, 'version', { 6136 enumerable: true, 6137 get: function get() { 6138 // replaced by browserify-versionify transform 6139 return '1.5.0'; 6140 } 6141 }); 6142 6143 exports.default = flvjs; 6144 6145 },{"./core/features.js":6,"./io/loader.js":24,"./player/flv-player.js":32,"./player/native-player.js":33,"./player/player-errors.js":34,"./player/player-events.js":35,"./utils/exception.js":40,"./utils/logging-control.js":42,"./utils/polyfill.js":43}],21:[function(_dereq_,module,exports){ 6146 'use strict'; 6147 6148 // entry/index file 6149 6150 // make it compatible with browserify's umd wrapper 6151 module.exports = _dereq_('./flv.js').default; 6152 6153 },{"./flv.js":20}],22:[function(_dereq_,module,exports){ 6154 'use strict'; 6155 6156 Object.defineProperty(exports, "__esModule", { 6157 value: true 6158 }); 6159 6160 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 6161 6162 var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; 6163 6164 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 6165 6166 var _logger = _dereq_('../utils/logger.js'); 6167 6168 var _logger2 = _interopRequireDefault(_logger); 6169 6170 var _browser = _dereq_('../utils/browser.js'); 6171 6172 var _browser2 = _interopRequireDefault(_browser); 6173 6174 var _loader = _dereq_('./loader.js'); 6175 6176 var _exception = _dereq_('../utils/exception.js'); 6177 6178 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 6179 6180 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6181 6182 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 6183 6184 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* 6185 * Copyright (C) 2016 Bilibili. All Rights Reserved. 6186 * 6187 * @author zheng qian <xqq@xqq.im> 6188 * 6189 * Licensed under the Apache License, Version 2.0 (the "License"); 6190 * you may not use this file except in compliance with the License. 6191 * You may obtain a copy of the License at 6192 * 6193 * http://www.apache.org/licenses/LICENSE-2.0 6194 * 6195 * Unless required by applicable law or agreed to in writing, software 6196 * distributed under the License is distributed on an "AS IS" BASIS, 6197 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6198 * See the License for the specific language governing permissions and 6199 * limitations under the License. 6200 */ 6201 6202 /* fetch + stream IO loader. Currently working on chrome 43+. 6203 * fetch provides a better alternative http API to XMLHttpRequest 6204 * 6205 * fetch spec https://fetch.spec.whatwg.org/ 6206 * stream spec https://streams.spec.whatwg.org/ 6207 */ 6208 var FetchStreamLoader = function (_BaseLoader) { 6209 _inherits(FetchStreamLoader, _BaseLoader); 6210 6211 _createClass(FetchStreamLoader, null, [{ 6212 key: 'isSupported', 6213 value: function isSupported() { 6214 try { 6215 // fetch + stream is broken on Microsoft Edge. Disable before build 15048. 6216 // see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8196907/ 6217 // Fixed in Jan 10, 2017. Build 15048+ removed from blacklist. 6218 var isWorkWellEdge = _browser2.default.msedge && _browser2.default.version.minor >= 15048; 6219 var browserNotBlacklisted = _browser2.default.msedge ? isWorkWellEdge : true; 6220 return self.fetch && self.ReadableStream && browserNotBlacklisted; 6221 } catch (e) { 6222 return false; 6223 } 6224 } 6225 }]); 6226 6227 function FetchStreamLoader(seekHandler, config) { 6228 _classCallCheck(this, FetchStreamLoader); 6229 6230 var _this = _possibleConstructorReturn(this, (FetchStreamLoader.__proto__ || Object.getPrototypeOf(FetchStreamLoader)).call(this, 'fetch-stream-loader')); 6231 6232 _this.TAG = 'FetchStreamLoader'; 6233 6234 _this._seekHandler = seekHandler; 6235 _this._config = config; 6236 _this._needStash = true; 6237 6238 _this._requestAbort = false; 6239 _this._contentLength = null; 6240 _this._receivedLength = 0; 6241 return _this; 6242 } 6243 6244 _createClass(FetchStreamLoader, [{ 6245 key: 'destroy', 6246 value: function destroy() { 6247 if (this.isWorking()) { 6248 this.abort(); 6249 } 6250 _get(FetchStreamLoader.prototype.__proto__ || Object.getPrototypeOf(FetchStreamLoader.prototype), 'destroy', this).call(this); 6251 } 6252 }, { 6253 key: 'open', 6254 value: function open(dataSource, range) { 6255 var _this2 = this; 6256 6257 this._dataSource = dataSource; 6258 this._range = range; 6259 6260 var sourceURL = dataSource.url; 6261 if (this._config.reuseRedirectedURL && dataSource.redirectedURL != undefined) { 6262 sourceURL = dataSource.redirectedURL; 6263 } 6264 6265 var seekConfig = this._seekHandler.getConfig(sourceURL, range); 6266 6267 var headers = new self.Headers(); 6268 6269 if (_typeof(seekConfig.headers) === 'object') { 6270 var configHeaders = seekConfig.headers; 6271 for (var key in configHeaders) { 6272 if (configHeaders.hasOwnProperty(key)) { 6273 headers.append(key, configHeaders[key]); 6274 } 6275 } 6276 } 6277 6278 var params = { 6279 method: 'GET', 6280 headers: headers, 6281 mode: 'cors', 6282 cache: 'default', 6283 // The default policy of Fetch API in the whatwg standard 6284 // Safari incorrectly indicates 'no-referrer' as default policy, fuck it 6285 referrerPolicy: 'no-referrer-when-downgrade' 6286 }; 6287 6288 // add additional headers 6289 if (_typeof(this._config.headers) === 'object') { 6290 for (var _key in this._config.headers) { 6291 headers.append(_key, this._config.headers[_key]); 6292 } 6293 } 6294 6295 // cors is enabled by default 6296 if (dataSource.cors === false) { 6297 // no-cors means 'disregard cors policy', which can only be used in ServiceWorker 6298 params.mode = 'same-origin'; 6299 } 6300 6301 // withCredentials is disabled by default 6302 if (dataSource.withCredentials) { 6303 params.credentials = 'include'; 6304 } 6305 6306 // referrerPolicy from config 6307 if (dataSource.referrerPolicy) { 6308 params.referrerPolicy = dataSource.referrerPolicy; 6309 } 6310 6311 this._status = _loader.LoaderStatus.kConnecting; 6312 self.fetch(seekConfig.url, params).then(function (res) { 6313 if (_this2._requestAbort) { 6314 _this2._requestAbort = false; 6315 _this2._status = _loader.LoaderStatus.kIdle; 6316 return; 6317 } 6318 if (res.ok && res.status >= 200 && res.status <= 299) { 6319 if (res.url !== seekConfig.url) { 6320 if (_this2._onURLRedirect) { 6321 var redirectedURL = _this2._seekHandler.removeURLParameters(res.url); 6322 _this2._onURLRedirect(redirectedURL); 6323 } 6324 } 6325 6326 var lengthHeader = res.headers.get('Content-Length'); 6327 if (lengthHeader != null) { 6328 _this2._contentLength = parseInt(lengthHeader); 6329 if (_this2._contentLength !== 0) { 6330 if (_this2._onContentLengthKnown) { 6331 _this2._onContentLengthKnown(_this2._contentLength); 6332 } 6333 } 6334 } 6335 6336 return _this2._pump.call(_this2, res.body.getReader()); 6337 } else { 6338 _this2._status = _loader.LoaderStatus.kError; 6339 if (_this2._onError) { 6340 _this2._onError(_loader.LoaderErrors.HTTP_STATUS_CODE_INVALID, { code: res.status, msg: res.statusText }); 6341 } else { 6342 throw new _exception.RuntimeException('FetchStreamLoader: Http code invalid, ' + res.status + ' ' + res.statusText); 6343 } 6344 } 6345 }).catch(function (e) { 6346 _this2._status = _loader.LoaderStatus.kError; 6347 if (_this2._onError) { 6348 _this2._onError(_loader.LoaderErrors.EXCEPTION, { code: -1, msg: e.message }); 6349 } else { 6350 throw e; 6351 } 6352 }); 6353 } 6354 }, { 6355 key: 'abort', 6356 value: function abort() { 6357 this._requestAbort = true; 6358 } 6359 }, { 6360 key: '_pump', 6361 value: function _pump(reader) { 6362 var _this3 = this; 6363 6364 // ReadableStreamReader 6365 return reader.read().then(function (result) { 6366 if (result.done) { 6367 // First check received length 6368 if (_this3._contentLength !== null && _this3._receivedLength < _this3._contentLength) { 6369 // Report Early-EOF 6370 _this3._status = _loader.LoaderStatus.kError; 6371 var type = _loader.LoaderErrors.EARLY_EOF; 6372 var info = { code: -1, msg: 'Fetch stream meet Early-EOF' }; 6373 if (_this3._onError) { 6374 _this3._onError(type, info); 6375 } else { 6376 throw new _exception.RuntimeException(info.msg); 6377 } 6378 } else { 6379 // OK. Download complete 6380 _this3._status = _loader.LoaderStatus.kComplete; 6381 if (_this3._onComplete) { 6382 _this3._onComplete(_this3._range.from, _this3._range.from + _this3._receivedLength - 1); 6383 } 6384 } 6385 } else { 6386 if (_this3._requestAbort === true) { 6387 _this3._requestAbort = false; 6388 _this3._status = _loader.LoaderStatus.kComplete; 6389 return reader.cancel(); 6390 } 6391 6392 _this3._status = _loader.LoaderStatus.kBuffering; 6393 6394 var chunk = result.value.buffer; 6395 var byteStart = _this3._range.from + _this3._receivedLength; 6396 _this3._receivedLength += chunk.byteLength; 6397 6398 if (_this3._onDataArrival) { 6399 _this3._onDataArrival(chunk, byteStart, _this3._receivedLength); 6400 } 6401 6402 _this3._pump(reader); 6403 } 6404 }).catch(function (e) { 6405 if (e.code === 11 && _browser2.default.msedge) { 6406 // InvalidStateError on Microsoft Edge 6407 // Workaround: Edge may throw InvalidStateError after ReadableStreamReader.cancel() call 6408 // Ignore the unknown exception. 6409 // Related issue: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11265202/ 6410 return; 6411 } 6412 6413 _this3._status = _loader.LoaderStatus.kError; 6414 var type = 0; 6415 var info = null; 6416 6417 if ((e.code === 19 || e.message === 'network error') && ( // NETWORK_ERR 6418 _this3._contentLength === null || _this3._contentLength !== null && _this3._receivedLength < _this3._contentLength)) { 6419 type = _loader.LoaderErrors.EARLY_EOF; 6420 info = { code: e.code, msg: 'Fetch stream meet Early-EOF' }; 6421 } else { 6422 type = _loader.LoaderErrors.EXCEPTION; 6423 info = { code: e.code, msg: e.message }; 6424 } 6425 6426 if (_this3._onError) { 6427 _this3._onError(type, info); 6428 } else { 6429 throw new _exception.RuntimeException(info.msg); 6430 } 6431 }); 6432 } 6433 }]); 6434 6435 return FetchStreamLoader; 6436 }(_loader.BaseLoader); 6437 6438 exports.default = FetchStreamLoader; 6439 6440 },{"../utils/browser.js":39,"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24}],23:[function(_dereq_,module,exports){ 6441 'use strict'; 6442 6443 Object.defineProperty(exports, "__esModule", { 6444 value: true 6445 }); 6446 6447 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 6448 * Copyright (C) 2016 Bilibili. All Rights Reserved. 6449 * 6450 * @author zheng qian <xqq@xqq.im> 6451 * 6452 * Licensed under the Apache License, Version 2.0 (the "License"); 6453 * you may not use this file except in compliance with the License. 6454 * You may obtain a copy of the License at 6455 * 6456 * http://www.apache.org/licenses/LICENSE-2.0 6457 * 6458 * Unless required by applicable law or agreed to in writing, software 6459 * distributed under the License is distributed on an "AS IS" BASIS, 6460 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 6461 * See the License for the specific language governing permissions and 6462 * limitations under the License. 6463 */ 6464 6465 var _logger = _dereq_('../utils/logger.js'); 6466 6467 var _logger2 = _interopRequireDefault(_logger); 6468 6469 var _speedSampler = _dereq_('./speed-sampler.js'); 6470 6471 var _speedSampler2 = _interopRequireDefault(_speedSampler); 6472 6473 var _loader = _dereq_('./loader.js'); 6474 6475 var _fetchStreamLoader = _dereq_('./fetch-stream-loader.js'); 6476 6477 var _fetchStreamLoader2 = _interopRequireDefault(_fetchStreamLoader); 6478 6479 var _xhrMozChunkedLoader = _dereq_('./xhr-moz-chunked-loader.js'); 6480 6481 var _xhrMozChunkedLoader2 = _interopRequireDefault(_xhrMozChunkedLoader); 6482 6483 var _xhrMsstreamLoader = _dereq_('./xhr-msstream-loader.js'); 6484 6485 var _xhrMsstreamLoader2 = _interopRequireDefault(_xhrMsstreamLoader); 6486 6487 var _xhrRangeLoader = _dereq_('./xhr-range-loader.js'); 6488 6489 var _xhrRangeLoader2 = _interopRequireDefault(_xhrRangeLoader); 6490 6491 var _websocketLoader = _dereq_('./websocket-loader.js'); 6492 6493 var _websocketLoader2 = _interopRequireDefault(_websocketLoader); 6494 6495 var _rangeSeekHandler = _dereq_('./range-seek-handler.js'); 6496 6497 var _rangeSeekHandler2 = _interopRequireDefault(_rangeSeekHandler); 6498 6499 var _paramSeekHandler = _dereq_('./param-seek-handler.js'); 6500 6501 var _paramSeekHandler2 = _interopRequireDefault(_paramSeekHandler); 6502 6503 var _exception = _dereq_('../utils/exception.js'); 6504 6505 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 6506 6507 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 6508 6509 /** 6510 * DataSource: { 6511 * url: string, 6512 * filesize: number, 6513 * cors: boolean, 6514 * withCredentials: boolean 6515 * } 6516 * 6517 */ 6518 6519 // Manage IO Loaders 6520 var IOController = function () { 6521 function IOController(dataSource, config, extraData) { 6522 _classCallCheck(this, IOController); 6523 6524 this.TAG = 'IOController'; 6525 6526 this._config = config; 6527 this._extraData = extraData; 6528 6529 this._stashInitialSize = 1024 * 384; // default initial size: 384KB 6530 if (config.stashInitialSize != undefined && config.stashInitialSize > 0) { 6531 // apply from config 6532 this._stashInitialSize = config.stashInitialSize; 6533 } 6534 6535 this._stashUsed = 0; 6536 this._stashSize = this._stashInitialSize; 6537 this._bufferSize = 1024 * 1024 * 3; // initial size: 3MB 6538 this._stashBuffer = new ArrayBuffer(this._bufferSize); 6539 this._stashByteStart = 0; 6540 this._enableStash = true; 6541 if (config.enableStashBuffer === false) { 6542 this._enableStash = false; 6543 } 6544 6545 this._loader = null; 6546 this._loaderClass = null; 6547 this._seekHandler = null; 6548 6549 this._dataSource = dataSource; 6550 this._isWebSocketURL = /wss?:\/\/(.+?)/.test(dataSource.url); 6551 this._refTotalLength = dataSource.filesize ? dataSource.filesize : null; 6552 this._totalLength = this._refTotalLength; 6553 this._fullRequestFlag = false; 6554 this._currentRange = null; 6555 this._redirectedURL = null; 6556 6557 this._speedNormalized = 0; 6558 this._speedSampler = new _speedSampler2.default(); 6559 this._speedNormalizeList = [64, 128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096]; 6560 6561 this._isEarlyEofReconnecting = false; 6562 6563 this._paused = false; 6564 this._resumeFrom = 0; 6565 6566 this._onDataArrival = null; 6567 this._onSeeked = null; 6568 this._onError = null; 6569 this._onComplete = null; 6570 this._onRedirect = null; 6571 this._onRecoveredEarlyEof = null; 6572 6573 this._selectSeekHandler(); 6574 this._selectLoader(); 6575 this._createLoader(); 6576 } 6577 6578 _createClass(IOController, [{ 6579 key: 'destroy', 6580 value: function destroy() { 6581 if (this._loader.isWorking()) { 6582 this._loader.abort(); 6583 } 6584 this._loader.destroy(); 6585 this._loader = null; 6586 this._loaderClass = null; 6587 this._dataSource = null; 6588 this._stashBuffer = null; 6589 this._stashUsed = this._stashSize = this._bufferSize = this._stashByteStart = 0; 6590 this._currentRange = null; 6591 this._speedSampler = null; 6592 6593 this._isEarlyEofReconnecting = false; 6594 6595 this._onDataArrival = null; 6596 this._onSeeked = null; 6597 this._onError = null; 6598 this._onComplete = null; 6599 this._onRedirect = null; 6600 this._onRecoveredEarlyEof = null; 6601 6602 this._extraData = null; 6603 } 6604 }, { 6605 key: 'isWorking', 6606 value: function isWorking() { 6607 return this._loader && this._loader.isWorking() && !this._paused; 6608 } 6609 }, { 6610 key: 'isPaused', 6611 value: function isPaused() { 6612 return this._paused; 6613 } 6614 }, { 6615 key: '_selectSeekHandler', 6616 value: function _selectSeekHandler() { 6617 var config = this._config; 6618 6619 if (config.seekType === 'range') { 6620 this._seekHandler = new _rangeSeekHandler2.default(this._config.rangeLoadZeroStart); 6621 } else if (config.seekType === 'param') { 6622 var paramStart = config.seekParamStart || 'bstart'; 6623 var paramEnd = config.seekParamEnd || 'bend'; 6624 6625 this._seekHandler = new _paramSeekHandler2.default(paramStart, paramEnd); 6626 } else if (config.seekType === 'custom') { 6627 if (typeof config.customSeekHandler !== 'function') { 6628 throw new _exception.InvalidArgumentException('Custom seekType specified in config but invalid customSeekHandler!'); 6629 } 6630 this._seekHandler = new config.customSeekHandler(); 6631 } else { 6632 throw new _exception.InvalidArgumentException('Invalid seekType in config: ' + config.seekType); 6633 } 6634 } 6635 }, { 6636 key: '_selectLoader', 6637 value: function _selectLoader() { 6638 if (this._config.customLoader != null) { 6639 this._loaderClass = this._config.customLoader; 6640 } else if (this._isWebSocketURL) { 6641 this._loaderClass = _websocketLoader2.default; 6642 } else if (_fetchStreamLoader2.default.isSupported()) { 6643 this._loaderClass = _fetchStreamLoader2.default; 6644 } else if (_xhrMozChunkedLoader2.default.isSupported()) { 6645 this._loaderClass = _xhrMozChunkedLoader2.default; 6646 } else if (_xhrRangeLoader2.default.isSupported()) { 6647 this._loaderClass = _xhrRangeLoader2.default; 6648 } else { 6649 throw new _exception.RuntimeException('Your browser doesn\'t support xhr with arraybuffer responseType!'); 6650 } 6651 } 6652 }, { 6653 key: '_createLoader', 6654 value: function _createLoader() { 6655 this._loader = new this._loaderClass(this._seekHandler, this._config); 6656 if (this._loader.needStashBuffer === false) { 6657 this._enableStash = false; 6658 } 6659 this._loader.onContentLengthKnown = this._onContentLengthKnown.bind(this); 6660 this._loader.onURLRedirect = this._onURLRedirect.bind(this); 6661 this._loader.onDataArrival = this._onLoaderChunkArrival.bind(this); 6662 this._loader.onComplete = this._onLoaderComplete.bind(this); 6663 this._loader.onError = this._onLoaderError.bind(this); 6664 } 6665 }, { 6666 key: 'open', 6667 value: function open(optionalFrom) { 6668 this._currentRange = { from: 0, to: -1 }; 6669 if (optionalFrom) { 6670 this._currentRange.from = optionalFrom; 6671 } 6672 6673 this._speedSampler.reset(); 6674 if (!optionalFrom) { 6675 this._fullRequestFlag = true; 6676 } 6677 6678 this._loader.open(this._dataSource, Object.assign({}, this._currentRange)); 6679 } 6680 }, { 6681 key: 'abort', 6682 value: function abort() { 6683 this._loader.abort(); 6684 6685 if (this._paused) { 6686 this._paused = false; 6687 this._resumeFrom = 0; 6688 } 6689 } 6690 }, { 6691 key: 'pause', 6692 value: function pause() { 6693 if (this.isWorking()) { 6694 this._loader.abort(); 6695 6696 if (this._stashUsed !== 0) { 6697 this._resumeFrom = this._stashByteStart; 6698 this._currentRange.to = this._stashByteStart - 1; 6699 } else { 6700 this._resumeFrom = this._currentRange.to + 1; 6701 } 6702 this._stashUsed = 0; 6703 this._stashByteStart = 0; 6704 this._paused = true; 6705 } 6706 } 6707 }, { 6708 key: 'resume', 6709 value: function resume() { 6710 if (this._paused) { 6711 this._paused = false; 6712 var bytes = this._resumeFrom; 6713 this._resumeFrom = 0; 6714 this._internalSeek(bytes, true); 6715 } 6716 } 6717 }, { 6718 key: 'seek', 6719 value: function seek(bytes) { 6720 this._paused = false; 6721 this._stashUsed = 0; 6722 this._stashByteStart = 0; 6723 this._internalSeek(bytes, true); 6724 } 6725 6726 /** 6727 * When seeking request is from media seeking, unconsumed stash data should be dropped 6728 * However, stash data shouldn't be dropped if seeking requested from http reconnection 6729 * 6730 * @dropUnconsumed: Ignore and discard all unconsumed data in stash buffer 6731 */ 6732 6733 }, { 6734 key: '_internalSeek', 6735 value: function _internalSeek(bytes, dropUnconsumed) { 6736 if (this._loader.isWorking()) { 6737 this._loader.abort(); 6738 } 6739 6740 // dispatch & flush stash buffer before seek 6741 this._flushStashBuffer(dropUnconsumed); 6742 6743 this._loader.destroy(); 6744 this._loader = null; 6745 6746 var requestRange = { from: bytes, to: -1 }; 6747 this._currentRange = { from: requestRange.from, to: -1 }; 6748 6749 this._speedSampler.reset(); 6750 this._stashSize = this._stashInitialSize; 6751 this._createLoader(); 6752 this._loader.open(this._dataSource, requestRange); 6753 6754 if (this._onSeeked) { 6755 this._onSeeked(); 6756 } 6757 } 6758 }, { 6759 key: 'updateUrl', 6760 value: function updateUrl(url) { 6761 if (!url || typeof url !== 'string' || url.length === 0) { 6762 throw new _exception.InvalidArgumentException('Url must be a non-empty string!'); 6763 } 6764 6765 this._dataSource.url = url; 6766 6767 // TODO: replace with new url 6768 } 6769 }, { 6770 key: '_expandBuffer', 6771 value: function _expandBuffer(expectedBytes) { 6772 var bufferNewSize = this._stashSize; 6773 while (bufferNewSize + 1024 * 1024 * 1 < expectedBytes) { 6774 bufferNewSize *= 2; 6775 } 6776 6777 bufferNewSize += 1024 * 1024 * 1; // bufferSize = stashSize + 1MB 6778 if (bufferNewSize === this._bufferSize) { 6779 return; 6780 } 6781 6782 var newBuffer = new ArrayBuffer(bufferNewSize); 6783 6784 if (this._stashUsed > 0) { 6785 // copy existing data into new buffer 6786 var stashOldArray = new Uint8Array(this._stashBuffer, 0, this._stashUsed); 6787 var stashNewArray = new Uint8Array(newBuffer, 0, bufferNewSize); 6788 stashNewArray.set(stashOldArray, 0); 6789 } 6790 6791 this._stashBuffer = newBuffer; 6792 this._bufferSize = bufferNewSize; 6793 } 6794 }, { 6795 key: '_normalizeSpeed', 6796 value: function _normalizeSpeed(input) { 6797 var list = this._speedNormalizeList; 6798 var last = list.length - 1; 6799 var mid = 0; 6800 var lbound = 0; 6801 var ubound = last; 6802 6803 if (input < list[0]) { 6804 return list[0]; 6805 } 6806 6807 // binary search 6808 while (lbound <= ubound) { 6809 mid = lbound + Math.floor((ubound - lbound) / 2); 6810 if (mid === last || input >= list[mid] && input < list[mid + 1]) { 6811 return list[mid]; 6812 } else if (list[mid] < input) { 6813 lbound = mid + 1; 6814 } else { 6815 ubound = mid - 1; 6816 } 6817 } 6818 } 6819 }, { 6820 key: '_adjustStashSize', 6821 value: function _adjustStashSize(normalized) { 6822 var stashSizeKB = 0; 6823 6824 if (this._config.isLive) { 6825 // live stream: always use single normalized speed for size of stashSizeKB 6826 stashSizeKB = normalized; 6827 } else { 6828 if (normalized < 512) { 6829 stashSizeKB = normalized; 6830 } else if (normalized >= 512 && normalized <= 1024) { 6831 stashSizeKB = Math.floor(normalized * 1.5); 6832 } else { 6833 stashSizeKB = normalized * 2; 6834 } 6835 } 6836 6837 if (stashSizeKB > 8192) { 6838 stashSizeKB = 8192; 6839 } 6840 6841 var bufferSize = stashSizeKB * 1024 + 1024 * 1024 * 1; // stashSize + 1MB 6842 if (this._bufferSize < bufferSize) { 6843 this._expandBuffer(bufferSize); 6844 } 6845 this._stashSize = stashSizeKB * 1024; 6846 } 6847 }, { 6848 key: '_dispatchChunks', 6849 value: function _dispatchChunks(chunks, byteStart) { 6850 this._currentRange.to = byteStart + chunks.byteLength - 1; 6851 return this._onDataArrival(chunks, byteStart); 6852 } 6853 }, { 6854 key: '_onURLRedirect', 6855 value: function _onURLRedirect(redirectedURL) { 6856 this._redirectedURL = redirectedURL; 6857 if (this._onRedirect) { 6858 this._onRedirect(redirectedURL); 6859 } 6860 } 6861 }, { 6862 key: '_onContentLengthKnown', 6863 value: function _onContentLengthKnown(contentLength) { 6864 if (contentLength && this._fullRequestFlag) { 6865 this._totalLength = contentLength; 6866 this._fullRequestFlag = false; 6867 } 6868 } 6869 }, { 6870 key: '_onLoaderChunkArrival', 6871 value: function _onLoaderChunkArrival(chunk, byteStart, receivedLength) { 6872 if (!this._onDataArrival) { 6873 throw new _exception.IllegalStateException('IOController: No existing consumer (onDataArrival) callback!'); 6874 } 6875 if (this._paused) { 6876 return; 6877 } 6878 if (this._isEarlyEofReconnecting) { 6879 // Auto-reconnect for EarlyEof succeed, notify to upper-layer by callback 6880 this._isEarlyEofReconnecting = false; 6881 if (this._onRecoveredEarlyEof) { 6882 this._onRecoveredEarlyEof(); 6883 } 6884 } 6885 6886 this._speedSampler.addBytes(chunk.byteLength); 6887 6888 // adjust stash buffer size according to network speed dynamically 6889 var KBps = this._speedSampler.lastSecondKBps; 6890 if (KBps !== 0) { 6891 var normalized = this._normalizeSpeed(KBps); 6892 if (this._speedNormalized !== normalized) { 6893 this._speedNormalized = normalized; 6894 this._adjustStashSize(normalized); 6895 } 6896 } 6897 6898 if (!this._enableStash) { 6899 // disable stash 6900 if (this._stashUsed === 0) { 6901 // dispatch chunk directly to consumer; 6902 // check ret value (consumed bytes) and stash unconsumed to stashBuffer 6903 var consumed = this._dispatchChunks(chunk, byteStart); 6904 if (consumed < chunk.byteLength) { 6905 // unconsumed data remain. 6906 var remain = chunk.byteLength - consumed; 6907 if (remain > this._bufferSize) { 6908 this._expandBuffer(remain); 6909 } 6910 var stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize); 6911 stashArray.set(new Uint8Array(chunk, consumed), 0); 6912 this._stashUsed += remain; 6913 this._stashByteStart = byteStart + consumed; 6914 } 6915 } else { 6916 // else: Merge chunk into stashBuffer, and dispatch stashBuffer to consumer. 6917 if (this._stashUsed + chunk.byteLength > this._bufferSize) { 6918 this._expandBuffer(this._stashUsed + chunk.byteLength); 6919 } 6920 var _stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize); 6921 _stashArray.set(new Uint8Array(chunk), this._stashUsed); 6922 this._stashUsed += chunk.byteLength; 6923 var _consumed = this._dispatchChunks(this._stashBuffer.slice(0, this._stashUsed), this._stashByteStart); 6924 if (_consumed < this._stashUsed && _consumed > 0) { 6925 // unconsumed data remain 6926 var remainArray = new Uint8Array(this._stashBuffer, _consumed); 6927 _stashArray.set(remainArray, 0); 6928 } 6929 this._stashUsed -= _consumed; 6930 this._stashByteStart += _consumed; 6931 } 6932 } else { 6933 // enable stash 6934 if (this._stashUsed === 0 && this._stashByteStart === 0) { 6935 // seeked? or init chunk? 6936 // This is the first chunk after seek action 6937 this._stashByteStart = byteStart; 6938 } 6939 if (this._stashUsed + chunk.byteLength <= this._stashSize) { 6940 // just stash 6941 var _stashArray2 = new Uint8Array(this._stashBuffer, 0, this._stashSize); 6942 _stashArray2.set(new Uint8Array(chunk), this._stashUsed); 6943 this._stashUsed += chunk.byteLength; 6944 } else { 6945 // stashUsed + chunkSize > stashSize, size limit exceeded 6946 var _stashArray3 = new Uint8Array(this._stashBuffer, 0, this._bufferSize); 6947 if (this._stashUsed > 0) { 6948 // There're stash datas in buffer 6949 // dispatch the whole stashBuffer, and stash remain data 6950 // then append chunk to stashBuffer (stash) 6951 var buffer = this._stashBuffer.slice(0, this._stashUsed); 6952 var _consumed2 = this._dispatchChunks(buffer, this._stashByteStart); 6953 if (_consumed2 < buffer.byteLength) { 6954 if (_consumed2 > 0) { 6955 var _remainArray = new Uint8Array(buffer, _consumed2); 6956 _stashArray3.set(_remainArray, 0); 6957 this._stashUsed = _remainArray.byteLength; 6958 this._stashByteStart += _consumed2; 6959 } 6960 } else { 6961 this._stashUsed = 0; 6962 this._stashByteStart += _consumed2; 6963 } 6964 if (this._stashUsed + chunk.byteLength > this._bufferSize) { 6965 this._expandBuffer(this._stashUsed + chunk.byteLength); 6966 _stashArray3 = new Uint8Array(this._stashBuffer, 0, this._bufferSize); 6967 } 6968 _stashArray3.set(new Uint8Array(chunk), this._stashUsed); 6969 this._stashUsed += chunk.byteLength; 6970 } else { 6971 // stash buffer empty, but chunkSize > stashSize (oh, holy shit) 6972 // dispatch chunk directly and stash remain data 6973 var _consumed3 = this._dispatchChunks(chunk, byteStart); 6974 if (_consumed3 < chunk.byteLength) { 6975 var _remain = chunk.byteLength - _consumed3; 6976 if (_remain > this._bufferSize) { 6977 this._expandBuffer(_remain); 6978 _stashArray3 = new Uint8Array(this._stashBuffer, 0, this._bufferSize); 6979 } 6980 _stashArray3.set(new Uint8Array(chunk, _consumed3), 0); 6981 this._stashUsed += _remain; 6982 this._stashByteStart = byteStart + _consumed3; 6983 } 6984 } 6985 } 6986 } 6987 } 6988 }, { 6989 key: '_flushStashBuffer', 6990 value: function _flushStashBuffer(dropUnconsumed) { 6991 if (this._stashUsed > 0) { 6992 var buffer = this._stashBuffer.slice(0, this._stashUsed); 6993 var consumed = this._dispatchChunks(buffer, this._stashByteStart); 6994 var remain = buffer.byteLength - consumed; 6995 6996 if (consumed < buffer.byteLength) { 6997 if (dropUnconsumed) { 6998 _logger2.default.w(this.TAG, remain + ' bytes unconsumed data remain when flush buffer, dropped'); 6999 } else { 7000 if (consumed > 0) { 7001 var stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize); 7002 var remainArray = new Uint8Array(buffer, consumed); 7003 stashArray.set(remainArray, 0); 7004 this._stashUsed = remainArray.byteLength; 7005 this._stashByteStart += consumed; 7006 } 7007 return 0; 7008 } 7009 } 7010 this._stashUsed = 0; 7011 this._stashByteStart = 0; 7012 return remain; 7013 } 7014 return 0; 7015 } 7016 }, { 7017 key: '_onLoaderComplete', 7018 value: function _onLoaderComplete(from, to) { 7019 // Force-flush stash buffer, and drop unconsumed data 7020 this._flushStashBuffer(true); 7021 7022 if (this._onComplete) { 7023 this._onComplete(this._extraData); 7024 } 7025 } 7026 }, { 7027 key: '_onLoaderError', 7028 value: function _onLoaderError(type, data) { 7029 _logger2.default.e(this.TAG, 'Loader error, code = ' + data.code + ', msg = ' + data.msg); 7030 7031 this._flushStashBuffer(false); 7032 7033 if (this._isEarlyEofReconnecting) { 7034 // Auto-reconnect for EarlyEof failed, throw UnrecoverableEarlyEof error to upper-layer 7035 this._isEarlyEofReconnecting = false; 7036 type = _loader.LoaderErrors.UNRECOVERABLE_EARLY_EOF; 7037 } 7038 7039 switch (type) { 7040 case _loader.LoaderErrors.EARLY_EOF: 7041 { 7042 if (!this._config.isLive) { 7043 // Do internal http reconnect if not live stream 7044 if (this._totalLength) { 7045 var nextFrom = this._currentRange.to + 1; 7046 if (nextFrom < this._totalLength) { 7047 _logger2.default.w(this.TAG, 'Connection lost, trying reconnect...'); 7048 this._isEarlyEofReconnecting = true; 7049 this._internalSeek(nextFrom, false); 7050 } 7051 return; 7052 } 7053 // else: We don't know totalLength, throw UnrecoverableEarlyEof 7054 } 7055 // live stream: throw UnrecoverableEarlyEof error to upper-layer 7056 type = _loader.LoaderErrors.UNRECOVERABLE_EARLY_EOF; 7057 break; 7058 } 7059 case _loader.LoaderErrors.UNRECOVERABLE_EARLY_EOF: 7060 case _loader.LoaderErrors.CONNECTING_TIMEOUT: 7061 case _loader.LoaderErrors.HTTP_STATUS_CODE_INVALID: 7062 case _loader.LoaderErrors.EXCEPTION: 7063 break; 7064 } 7065 7066 if (this._onError) { 7067 this._onError(type, data); 7068 } else { 7069 throw new _exception.RuntimeException('IOException: ' + data.msg); 7070 } 7071 } 7072 }, { 7073 key: 'status', 7074 get: function get() { 7075 return this._loader.status; 7076 } 7077 }, { 7078 key: 'extraData', 7079 get: function get() { 7080 return this._extraData; 7081 }, 7082 set: function set(data) { 7083 this._extraData = data; 7084 } 7085 7086 // prototype: function onDataArrival(chunks: ArrayBuffer, byteStart: number): number 7087 7088 }, { 7089 key: 'onDataArrival', 7090 get: function get() { 7091 return this._onDataArrival; 7092 }, 7093 set: function set(callback) { 7094 this._onDataArrival = callback; 7095 } 7096 }, { 7097 key: 'onSeeked', 7098 get: function get() { 7099 return this._onSeeked; 7100 }, 7101 set: function set(callback) { 7102 this._onSeeked = callback; 7103 } 7104 7105 // prototype: function onError(type: number, info: {code: number, msg: string}): void 7106 7107 }, { 7108 key: 'onError', 7109 get: function get() { 7110 return this._onError; 7111 }, 7112 set: function set(callback) { 7113 this._onError = callback; 7114 } 7115 }, { 7116 key: 'onComplete', 7117 get: function get() { 7118 return this._onComplete; 7119 }, 7120 set: function set(callback) { 7121 this._onComplete = callback; 7122 } 7123 }, { 7124 key: 'onRedirect', 7125 get: function get() { 7126 return this._onRedirect; 7127 }, 7128 set: function set(callback) { 7129 this._onRedirect = callback; 7130 } 7131 }, { 7132 key: 'onRecoveredEarlyEof', 7133 get: function get() { 7134 return this._onRecoveredEarlyEof; 7135 }, 7136 set: function set(callback) { 7137 this._onRecoveredEarlyEof = callback; 7138 } 7139 }, { 7140 key: 'currentURL', 7141 get: function get() { 7142 return this._dataSource.url; 7143 } 7144 }, { 7145 key: 'hasRedirect', 7146 get: function get() { 7147 return this._redirectedURL != null || this._dataSource.redirectedURL != undefined; 7148 } 7149 }, { 7150 key: 'currentRedirectedURL', 7151 get: function get() { 7152 return this._redirectedURL || this._dataSource.redirectedURL; 7153 } 7154 7155 // in KB/s 7156 7157 }, { 7158 key: 'currentSpeed', 7159 get: function get() { 7160 if (this._loaderClass === _xhrRangeLoader2.default) { 7161 // SpeedSampler is inaccuracy if loader is RangeLoader 7162 return this._loader.currentSpeed; 7163 } 7164 return this._speedSampler.lastSecondKBps; 7165 } 7166 }, { 7167 key: 'loaderType', 7168 get: function get() { 7169 return this._loader.type; 7170 } 7171 }]); 7172 7173 return IOController; 7174 }(); 7175 7176 exports.default = IOController; 7177 7178 },{"../utils/exception.js":40,"../utils/logger.js":41,"./fetch-stream-loader.js":22,"./loader.js":24,"./param-seek-handler.js":25,"./range-seek-handler.js":26,"./speed-sampler.js":27,"./websocket-loader.js":28,"./xhr-moz-chunked-loader.js":29,"./xhr-msstream-loader.js":30,"./xhr-range-loader.js":31}],24:[function(_dereq_,module,exports){ 7179 'use strict'; 7180 7181 Object.defineProperty(exports, "__esModule", { 7182 value: true 7183 }); 7184 exports.BaseLoader = exports.LoaderErrors = exports.LoaderStatus = undefined; 7185 7186 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 7187 * Copyright (C) 2016 Bilibili. All Rights Reserved. 7188 * 7189 * @author zheng qian <xqq@xqq.im> 7190 * 7191 * Licensed under the Apache License, Version 2.0 (the "License"); 7192 * you may not use this file except in compliance with the License. 7193 * You may obtain a copy of the License at 7194 * 7195 * http://www.apache.org/licenses/LICENSE-2.0 7196 * 7197 * Unless required by applicable law or agreed to in writing, software 7198 * distributed under the License is distributed on an "AS IS" BASIS, 7199 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7200 * See the License for the specific language governing permissions and 7201 * limitations under the License. 7202 */ 7203 7204 var _exception = _dereq_('../utils/exception.js'); 7205 7206 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 7207 7208 var LoaderStatus = exports.LoaderStatus = { 7209 kIdle: 0, 7210 kConnecting: 1, 7211 kBuffering: 2, 7212 kError: 3, 7213 kComplete: 4 7214 }; 7215 7216 var LoaderErrors = exports.LoaderErrors = { 7217 OK: 'OK', 7218 EXCEPTION: 'Exception', 7219 HTTP_STATUS_CODE_INVALID: 'HttpStatusCodeInvalid', 7220 CONNECTING_TIMEOUT: 'ConnectingTimeout', 7221 EARLY_EOF: 'EarlyEof', 7222 UNRECOVERABLE_EARLY_EOF: 'UnrecoverableEarlyEof' 7223 }; 7224 7225 /* Loader has callbacks which have following prototypes: 7226 * function onContentLengthKnown(contentLength: number): void 7227 * function onURLRedirect(url: string): void 7228 * function onDataArrival(chunk: ArrayBuffer, byteStart: number, receivedLength: number): void 7229 * function onError(errorType: number, errorInfo: {code: number, msg: string}): void 7230 * function onComplete(rangeFrom: number, rangeTo: number): void 7231 */ 7232 7233 var BaseLoader = exports.BaseLoader = function () { 7234 function BaseLoader(typeName) { 7235 _classCallCheck(this, BaseLoader); 7236 7237 this._type = typeName || 'undefined'; 7238 this._status = LoaderStatus.kIdle; 7239 this._needStash = false; 7240 // callbacks 7241 this._onContentLengthKnown = null; 7242 this._onURLRedirect = null; 7243 this._onDataArrival = null; 7244 this._onError = null; 7245 this._onComplete = null; 7246 } 7247 7248 _createClass(BaseLoader, [{ 7249 key: 'destroy', 7250 value: function destroy() { 7251 this._status = LoaderStatus.kIdle; 7252 this._onContentLengthKnown = null; 7253 this._onURLRedirect = null; 7254 this._onDataArrival = null; 7255 this._onError = null; 7256 this._onComplete = null; 7257 } 7258 }, { 7259 key: 'isWorking', 7260 value: function isWorking() { 7261 return this._status === LoaderStatus.kConnecting || this._status === LoaderStatus.kBuffering; 7262 } 7263 }, { 7264 key: 'open', 7265 7266 7267 // pure virtual 7268 value: function open(dataSource, range) { 7269 throw new _exception.NotImplementedException('Unimplemented abstract function!'); 7270 } 7271 }, { 7272 key: 'abort', 7273 value: function abort() { 7274 throw new _exception.NotImplementedException('Unimplemented abstract function!'); 7275 } 7276 }, { 7277 key: 'type', 7278 get: function get() { 7279 return this._type; 7280 } 7281 }, { 7282 key: 'status', 7283 get: function get() { 7284 return this._status; 7285 } 7286 }, { 7287 key: 'needStashBuffer', 7288 get: function get() { 7289 return this._needStash; 7290 } 7291 }, { 7292 key: 'onContentLengthKnown', 7293 get: function get() { 7294 return this._onContentLengthKnown; 7295 }, 7296 set: function set(callback) { 7297 this._onContentLengthKnown = callback; 7298 } 7299 }, { 7300 key: 'onURLRedirect', 7301 get: function get() { 7302 return this._onURLRedirect; 7303 }, 7304 set: function set(callback) { 7305 this._onURLRedirect = callback; 7306 } 7307 }, { 7308 key: 'onDataArrival', 7309 get: function get() { 7310 return this._onDataArrival; 7311 }, 7312 set: function set(callback) { 7313 this._onDataArrival = callback; 7314 } 7315 }, { 7316 key: 'onError', 7317 get: function get() { 7318 return this._onError; 7319 }, 7320 set: function set(callback) { 7321 this._onError = callback; 7322 } 7323 }, { 7324 key: 'onComplete', 7325 get: function get() { 7326 return this._onComplete; 7327 }, 7328 set: function set(callback) { 7329 this._onComplete = callback; 7330 } 7331 }]); 7332 7333 return BaseLoader; 7334 }(); 7335 7336 },{"../utils/exception.js":40}],25:[function(_dereq_,module,exports){ 7337 'use strict'; 7338 7339 Object.defineProperty(exports, "__esModule", { 7340 value: true 7341 }); 7342 7343 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 7344 7345 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 7346 7347 /* 7348 * Copyright (C) 2016 Bilibili. All Rights Reserved. 7349 * 7350 * @author zheng qian <xqq@xqq.im> 7351 * 7352 * Licensed under the Apache License, Version 2.0 (the "License"); 7353 * you may not use this file except in compliance with the License. 7354 * You may obtain a copy of the License at 7355 * 7356 * http://www.apache.org/licenses/LICENSE-2.0 7357 * 7358 * Unless required by applicable law or agreed to in writing, software 7359 * distributed under the License is distributed on an "AS IS" BASIS, 7360 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7361 * See the License for the specific language governing permissions and 7362 * limitations under the License. 7363 */ 7364 7365 var ParamSeekHandler = function () { 7366 function ParamSeekHandler(paramStart, paramEnd) { 7367 _classCallCheck(this, ParamSeekHandler); 7368 7369 this._startName = paramStart; 7370 this._endName = paramEnd; 7371 } 7372 7373 _createClass(ParamSeekHandler, [{ 7374 key: 'getConfig', 7375 value: function getConfig(baseUrl, range) { 7376 var url = baseUrl; 7377 7378 if (range.from !== 0 || range.to !== -1) { 7379 var needAnd = true; 7380 if (url.indexOf('?') === -1) { 7381 url += '?'; 7382 needAnd = false; 7383 } 7384 7385 if (needAnd) { 7386 url += '&'; 7387 } 7388 7389 url += this._startName + '=' + range.from.toString(); 7390 7391 if (range.to !== -1) { 7392 url += '&' + this._endName + '=' + range.to.toString(); 7393 } 7394 } 7395 7396 return { 7397 url: url, 7398 headers: {} 7399 }; 7400 } 7401 }, { 7402 key: 'removeURLParameters', 7403 value: function removeURLParameters(seekedURL) { 7404 var baseURL = seekedURL.split('?')[0]; 7405 var params = undefined; 7406 7407 var queryIndex = seekedURL.indexOf('?'); 7408 if (queryIndex !== -1) { 7409 params = seekedURL.substring(queryIndex + 1); 7410 } 7411 7412 var resultParams = ''; 7413 7414 if (params != undefined && params.length > 0) { 7415 var pairs = params.split('&'); 7416 7417 for (var i = 0; i < pairs.length; i++) { 7418 var pair = pairs[i].split('='); 7419 var requireAnd = i > 0; 7420 7421 if (pair[0] !== this._startName && pair[0] !== this._endName) { 7422 if (requireAnd) { 7423 resultParams += '&'; 7424 } 7425 resultParams += pairs[i]; 7426 } 7427 } 7428 } 7429 7430 return resultParams.length === 0 ? baseURL : baseURL + '?' + resultParams; 7431 } 7432 }]); 7433 7434 return ParamSeekHandler; 7435 }(); 7436 7437 exports.default = ParamSeekHandler; 7438 7439 },{}],26:[function(_dereq_,module,exports){ 7440 'use strict'; 7441 7442 Object.defineProperty(exports, "__esModule", { 7443 value: true 7444 }); 7445 7446 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 7447 7448 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 7449 7450 /* 7451 * Copyright (C) 2016 Bilibili. All Rights Reserved. 7452 * 7453 * @author zheng qian <xqq@xqq.im> 7454 * 7455 * Licensed under the Apache License, Version 2.0 (the "License"); 7456 * you may not use this file except in compliance with the License. 7457 * You may obtain a copy of the License at 7458 * 7459 * http://www.apache.org/licenses/LICENSE-2.0 7460 * 7461 * Unless required by applicable law or agreed to in writing, software 7462 * distributed under the License is distributed on an "AS IS" BASIS, 7463 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7464 * See the License for the specific language governing permissions and 7465 * limitations under the License. 7466 */ 7467 7468 var RangeSeekHandler = function () { 7469 function RangeSeekHandler(zeroStart) { 7470 _classCallCheck(this, RangeSeekHandler); 7471 7472 this._zeroStart = zeroStart || false; 7473 } 7474 7475 _createClass(RangeSeekHandler, [{ 7476 key: 'getConfig', 7477 value: function getConfig(url, range) { 7478 var headers = {}; 7479 7480 if (range.from !== 0 || range.to !== -1) { 7481 var param = void 0; 7482 if (range.to !== -1) { 7483 param = 'bytes=' + range.from.toString() + '-' + range.to.toString(); 7484 } else { 7485 param = 'bytes=' + range.from.toString() + '-'; 7486 } 7487 headers['Range'] = param; 7488 } else if (this._zeroStart) { 7489 headers['Range'] = 'bytes=0-'; 7490 } 7491 7492 return { 7493 url: url, 7494 headers: headers 7495 }; 7496 } 7497 }, { 7498 key: 'removeURLParameters', 7499 value: function removeURLParameters(seekedURL) { 7500 return seekedURL; 7501 } 7502 }]); 7503 7504 return RangeSeekHandler; 7505 }(); 7506 7507 exports.default = RangeSeekHandler; 7508 7509 },{}],27:[function(_dereq_,module,exports){ 7510 "use strict"; 7511 7512 Object.defineProperty(exports, "__esModule", { 7513 value: true 7514 }); 7515 7516 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 7517 7518 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 7519 7520 /* 7521 * Copyright (C) 2016 Bilibili. All Rights Reserved. 7522 * 7523 * @author zheng qian <xqq@xqq.im> 7524 * 7525 * Licensed under the Apache License, Version 2.0 (the "License"); 7526 * you may not use this file except in compliance with the License. 7527 * You may obtain a copy of the License at 7528 * 7529 * http://www.apache.org/licenses/LICENSE-2.0 7530 * 7531 * Unless required by applicable law or agreed to in writing, software 7532 * distributed under the License is distributed on an "AS IS" BASIS, 7533 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7534 * See the License for the specific language governing permissions and 7535 * limitations under the License. 7536 */ 7537 7538 // Utility class to calculate realtime network I/O speed 7539 var SpeedSampler = function () { 7540 function SpeedSampler() { 7541 _classCallCheck(this, SpeedSampler); 7542 7543 // milliseconds 7544 this._firstCheckpoint = 0; 7545 this._lastCheckpoint = 0; 7546 this._intervalBytes = 0; 7547 this._totalBytes = 0; 7548 this._lastSecondBytes = 0; 7549 7550 // compatibility detection 7551 if (self.performance && self.performance.now) { 7552 this._now = self.performance.now.bind(self.performance); 7553 } else { 7554 this._now = Date.now; 7555 } 7556 } 7557 7558 _createClass(SpeedSampler, [{ 7559 key: "reset", 7560 value: function reset() { 7561 this._firstCheckpoint = this._lastCheckpoint = 0; 7562 this._totalBytes = this._intervalBytes = 0; 7563 this._lastSecondBytes = 0; 7564 } 7565 }, { 7566 key: "addBytes", 7567 value: function addBytes(bytes) { 7568 if (this._firstCheckpoint === 0) { 7569 this._firstCheckpoint = this._now(); 7570 this._lastCheckpoint = this._firstCheckpoint; 7571 this._intervalBytes += bytes; 7572 this._totalBytes += bytes; 7573 } else if (this._now() - this._lastCheckpoint < 1000) { 7574 this._intervalBytes += bytes; 7575 this._totalBytes += bytes; 7576 } else { 7577 // duration >= 1000 7578 this._lastSecondBytes = this._intervalBytes; 7579 this._intervalBytes = bytes; 7580 this._totalBytes += bytes; 7581 this._lastCheckpoint = this._now(); 7582 } 7583 } 7584 }, { 7585 key: "currentKBps", 7586 get: function get() { 7587 this.addBytes(0); 7588 7589 var durationSeconds = (this._now() - this._lastCheckpoint) / 1000; 7590 if (durationSeconds == 0) durationSeconds = 1; 7591 return this._intervalBytes / durationSeconds / 1024; 7592 } 7593 }, { 7594 key: "lastSecondKBps", 7595 get: function get() { 7596 this.addBytes(0); 7597 7598 if (this._lastSecondBytes !== 0) { 7599 return this._lastSecondBytes / 1024; 7600 } else { 7601 // lastSecondBytes === 0 7602 if (this._now() - this._lastCheckpoint >= 500) { 7603 // if time interval since last checkpoint has exceeded 500ms 7604 // the speed is nearly accurate 7605 return this.currentKBps; 7606 } else { 7607 // We don't know 7608 return 0; 7609 } 7610 } 7611 } 7612 }, { 7613 key: "averageKBps", 7614 get: function get() { 7615 var durationSeconds = (this._now() - this._firstCheckpoint) / 1000; 7616 return this._totalBytes / durationSeconds / 1024; 7617 } 7618 }]); 7619 7620 return SpeedSampler; 7621 }(); 7622 7623 exports.default = SpeedSampler; 7624 7625 },{}],28:[function(_dereq_,module,exports){ 7626 'use strict'; 7627 7628 Object.defineProperty(exports, "__esModule", { 7629 value: true 7630 }); 7631 7632 var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; 7633 7634 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 7635 7636 var _logger = _dereq_('../utils/logger.js'); 7637 7638 var _logger2 = _interopRequireDefault(_logger); 7639 7640 var _loader = _dereq_('./loader.js'); 7641 7642 var _exception = _dereq_('../utils/exception.js'); 7643 7644 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 7645 7646 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 7647 7648 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 7649 7650 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* 7651 * Copyright (C) 2016 Bilibili. All Rights Reserved. 7652 * 7653 * @author zheng qian <xqq@xqq.im> 7654 * 7655 * Licensed under the Apache License, Version 2.0 (the "License"); 7656 * you may not use this file except in compliance with the License. 7657 * You may obtain a copy of the License at 7658 * 7659 * http://www.apache.org/licenses/LICENSE-2.0 7660 * 7661 * Unless required by applicable law or agreed to in writing, software 7662 * distributed under the License is distributed on an "AS IS" BASIS, 7663 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7664 * See the License for the specific language governing permissions and 7665 * limitations under the License. 7666 */ 7667 7668 // For FLV over WebSocket live stream 7669 var WebSocketLoader = function (_BaseLoader) { 7670 _inherits(WebSocketLoader, _BaseLoader); 7671 7672 _createClass(WebSocketLoader, null, [{ 7673 key: 'isSupported', 7674 value: function isSupported() { 7675 try { 7676 return typeof self.WebSocket !== 'undefined'; 7677 } catch (e) { 7678 return false; 7679 } 7680 } 7681 }]); 7682 7683 function WebSocketLoader() { 7684 _classCallCheck(this, WebSocketLoader); 7685 7686 var _this = _possibleConstructorReturn(this, (WebSocketLoader.__proto__ || Object.getPrototypeOf(WebSocketLoader)).call(this, 'websocket-loader')); 7687 7688 _this.TAG = 'WebSocketLoader'; 7689 7690 _this._needStash = true; 7691 7692 _this._ws = null; 7693 _this._requestAbort = false; 7694 _this._receivedLength = 0; 7695 return _this; 7696 } 7697 7698 _createClass(WebSocketLoader, [{ 7699 key: 'destroy', 7700 value: function destroy() { 7701 if (this._ws) { 7702 this.abort(); 7703 } 7704 _get(WebSocketLoader.prototype.__proto__ || Object.getPrototypeOf(WebSocketLoader.prototype), 'destroy', this).call(this); 7705 } 7706 }, { 7707 key: 'open', 7708 value: function open(dataSource) { 7709 try { 7710 var ws = this._ws = new self.WebSocket(dataSource.url); 7711 ws.binaryType = 'arraybuffer'; 7712 ws.onopen = this._onWebSocketOpen.bind(this); 7713 ws.onclose = this._onWebSocketClose.bind(this); 7714 ws.onmessage = this._onWebSocketMessage.bind(this); 7715 ws.onerror = this._onWebSocketError.bind(this); 7716 7717 this._status = _loader.LoaderStatus.kConnecting; 7718 } catch (e) { 7719 this._status = _loader.LoaderStatus.kError; 7720 7721 var info = { code: e.code, msg: e.message }; 7722 7723 if (this._onError) { 7724 this._onError(_loader.LoaderErrors.EXCEPTION, info); 7725 } else { 7726 throw new _exception.RuntimeException(info.msg); 7727 } 7728 } 7729 } 7730 }, { 7731 key: 'abort', 7732 value: function abort() { 7733 var ws = this._ws; 7734 if (ws && (ws.readyState === 0 || ws.readyState === 1)) { 7735 // CONNECTING || OPEN 7736 this._requestAbort = true; 7737 ws.close(); 7738 } 7739 7740 this._ws = null; 7741 this._status = _loader.LoaderStatus.kComplete; 7742 } 7743 }, { 7744 key: '_onWebSocketOpen', 7745 value: function _onWebSocketOpen(e) { 7746 this._status = _loader.LoaderStatus.kBuffering; 7747 } 7748 }, { 7749 key: '_onWebSocketClose', 7750 value: function _onWebSocketClose(e) { 7751 if (this._requestAbort === true) { 7752 this._requestAbort = false; 7753 return; 7754 } 7755 7756 this._status = _loader.LoaderStatus.kComplete; 7757 7758 if (this._onComplete) { 7759 this._onComplete(0, this._receivedLength - 1); 7760 } 7761 } 7762 }, { 7763 key: '_onWebSocketMessage', 7764 value: function _onWebSocketMessage(e) { 7765 var _this2 = this; 7766 7767 if (e.data instanceof ArrayBuffer) { 7768 this._dispatchArrayBuffer(e.data); 7769 } else if (e.data instanceof Blob) { 7770 var reader = new FileReader(); 7771 reader.onload = function () { 7772 _this2._dispatchArrayBuffer(reader.result); 7773 }; 7774 reader.readAsArrayBuffer(e.data); 7775 } else { 7776 this._status = _loader.LoaderStatus.kError; 7777 var info = { code: -1, msg: 'Unsupported WebSocket message type: ' + e.data.constructor.name }; 7778 7779 if (this._onError) { 7780 this._onError(_loader.LoaderErrors.EXCEPTION, info); 7781 } else { 7782 throw new _exception.RuntimeException(info.msg); 7783 } 7784 } 7785 } 7786 }, { 7787 key: '_dispatchArrayBuffer', 7788 value: function _dispatchArrayBuffer(arraybuffer) { 7789 var chunk = arraybuffer; 7790 var byteStart = this._receivedLength; 7791 this._receivedLength += chunk.byteLength; 7792 7793 if (this._onDataArrival) { 7794 this._onDataArrival(chunk, byteStart, this._receivedLength); 7795 } 7796 } 7797 }, { 7798 key: '_onWebSocketError', 7799 value: function _onWebSocketError(e) { 7800 this._status = _loader.LoaderStatus.kError; 7801 7802 var info = { 7803 code: e.code, 7804 msg: e.message 7805 }; 7806 7807 if (this._onError) { 7808 this._onError(_loader.LoaderErrors.EXCEPTION, info); 7809 } else { 7810 throw new _exception.RuntimeException(info.msg); 7811 } 7812 } 7813 }]); 7814 7815 return WebSocketLoader; 7816 }(_loader.BaseLoader); 7817 7818 exports.default = WebSocketLoader; 7819 7820 },{"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24}],29:[function(_dereq_,module,exports){ 7821 'use strict'; 7822 7823 Object.defineProperty(exports, "__esModule", { 7824 value: true 7825 }); 7826 7827 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 7828 7829 var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; 7830 7831 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 7832 7833 var _logger = _dereq_('../utils/logger.js'); 7834 7835 var _logger2 = _interopRequireDefault(_logger); 7836 7837 var _loader = _dereq_('./loader.js'); 7838 7839 var _exception = _dereq_('../utils/exception.js'); 7840 7841 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 7842 7843 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 7844 7845 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 7846 7847 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* 7848 * Copyright (C) 2016 Bilibili. All Rights Reserved. 7849 * 7850 * @author zheng qian <xqq@xqq.im> 7851 * 7852 * Licensed under the Apache License, Version 2.0 (the "License"); 7853 * you may not use this file except in compliance with the License. 7854 * You may obtain a copy of the License at 7855 * 7856 * http://www.apache.org/licenses/LICENSE-2.0 7857 * 7858 * Unless required by applicable law or agreed to in writing, software 7859 * distributed under the License is distributed on an "AS IS" BASIS, 7860 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 7861 * See the License for the specific language governing permissions and 7862 * limitations under the License. 7863 */ 7864 7865 // For FireFox browser which supports `xhr.responseType = 'moz-chunked-arraybuffer'` 7866 var MozChunkedLoader = function (_BaseLoader) { 7867 _inherits(MozChunkedLoader, _BaseLoader); 7868 7869 _createClass(MozChunkedLoader, null, [{ 7870 key: 'isSupported', 7871 value: function isSupported() { 7872 try { 7873 var xhr = new XMLHttpRequest(); 7874 // Firefox 37- requires .open() to be called before setting responseType 7875 xhr.open('GET', 'https://example.com', true); 7876 xhr.responseType = 'moz-chunked-arraybuffer'; 7877 return xhr.responseType === 'moz-chunked-arraybuffer'; 7878 } catch (e) { 7879 _logger2.default.w('MozChunkedLoader', e.message); 7880 return false; 7881 } 7882 } 7883 }]); 7884 7885 function MozChunkedLoader(seekHandler, config) { 7886 _classCallCheck(this, MozChunkedLoader); 7887 7888 var _this = _possibleConstructorReturn(this, (MozChunkedLoader.__proto__ || Object.getPrototypeOf(MozChunkedLoader)).call(this, 'xhr-moz-chunked-loader')); 7889 7890 _this.TAG = 'MozChunkedLoader'; 7891 7892 _this._seekHandler = seekHandler; 7893 _this._config = config; 7894 _this._needStash = true; 7895 7896 _this._xhr = null; 7897 _this._requestAbort = false; 7898 _this._contentLength = null; 7899 _this._receivedLength = 0; 7900 return _this; 7901 } 7902 7903 _createClass(MozChunkedLoader, [{ 7904 key: 'destroy', 7905 value: function destroy() { 7906 if (this.isWorking()) { 7907 this.abort(); 7908 } 7909 if (this._xhr) { 7910 this._xhr.onreadystatechange = null; 7911 this._xhr.onprogress = null; 7912 this._xhr.onloadend = null; 7913 this._xhr.onerror = null; 7914 this._xhr = null; 7915 } 7916 _get(MozChunkedLoader.prototype.__proto__ || Object.getPrototypeOf(MozChunkedLoader.prototype), 'destroy', this).call(this); 7917 } 7918 }, { 7919 key: 'open', 7920 value: function open(dataSource, range) { 7921 this._dataSource = dataSource; 7922 this._range = range; 7923 7924 var sourceURL = dataSource.url; 7925 if (this._config.reuseRedirectedURL && dataSource.redirectedURL != undefined) { 7926 sourceURL = dataSource.redirectedURL; 7927 } 7928 7929 var seekConfig = this._seekHandler.getConfig(sourceURL, range); 7930 this._requestURL = seekConfig.url; 7931 7932 var xhr = this._xhr = new XMLHttpRequest(); 7933 xhr.open('GET', seekConfig.url, true); 7934 xhr.responseType = 'moz-chunked-arraybuffer'; 7935 xhr.onreadystatechange = this._onReadyStateChange.bind(this); 7936 xhr.onprogress = this._onProgress.bind(this); 7937 xhr.onloadend = this._onLoadEnd.bind(this); 7938 xhr.onerror = this._onXhrError.bind(this); 7939 7940 // cors is auto detected and enabled by xhr 7941 7942 // withCredentials is disabled by default 7943 if (dataSource.withCredentials) { 7944 xhr.withCredentials = true; 7945 } 7946 7947 if (_typeof(seekConfig.headers) === 'object') { 7948 var headers = seekConfig.headers; 7949 7950 for (var key in headers) { 7951 if (headers.hasOwnProperty(key)) { 7952 xhr.setRequestHeader(key, headers[key]); 7953 } 7954 } 7955 } 7956 7957 // add additional headers 7958 if (_typeof(this._config.headers) === 'object') { 7959 var _headers = this._config.headers; 7960 7961 for (var _key in _headers) { 7962 if (_headers.hasOwnProperty(_key)) { 7963 xhr.setRequestHeader(_key, _headers[_key]); 7964 } 7965 } 7966 } 7967 7968 this._status = _loader.LoaderStatus.kConnecting; 7969 xhr.send(); 7970 } 7971 }, { 7972 key: 'abort', 7973 value: function abort() { 7974 this._requestAbort = true; 7975 if (this._xhr) { 7976 this._xhr.abort(); 7977 } 7978 this._status = _loader.LoaderStatus.kComplete; 7979 } 7980 }, { 7981 key: '_onReadyStateChange', 7982 value: function _onReadyStateChange(e) { 7983 var xhr = e.target; 7984 7985 if (xhr.readyState === 2) { 7986 // HEADERS_RECEIVED 7987 if (xhr.responseURL != undefined && xhr.responseURL !== this._requestURL) { 7988 if (this._onURLRedirect) { 7989 var redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL); 7990 this._onURLRedirect(redirectedURL); 7991 } 7992 } 7993 7994 if (xhr.status !== 0 && (xhr.status < 200 || xhr.status > 299)) { 7995 this._status = _loader.LoaderStatus.kError; 7996 if (this._onError) { 7997 this._onError(_loader.LoaderErrors.HTTP_STATUS_CODE_INVALID, { code: xhr.status, msg: xhr.statusText }); 7998 } else { 7999 throw new _exception.RuntimeException('MozChunkedLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText); 8000 } 8001 } else { 8002 this._status = _loader.LoaderStatus.kBuffering; 8003 } 8004 } 8005 } 8006 }, { 8007 key: '_onProgress', 8008 value: function _onProgress(e) { 8009 if (this._status === _loader.LoaderStatus.kError) { 8010 // Ignore error response 8011 return; 8012 } 8013 8014 if (this._contentLength === null) { 8015 if (e.total !== null && e.total !== 0) { 8016 this._contentLength = e.total; 8017 if (this._onContentLengthKnown) { 8018 this._onContentLengthKnown(this._contentLength); 8019 } 8020 } 8021 } 8022 8023 var chunk = e.target.response; 8024 var byteStart = this._range.from + this._receivedLength; 8025 this._receivedLength += chunk.byteLength; 8026 8027 if (this._onDataArrival) { 8028 this._onDataArrival(chunk, byteStart, this._receivedLength); 8029 } 8030 } 8031 }, { 8032 key: '_onLoadEnd', 8033 value: function _onLoadEnd(e) { 8034 if (this._requestAbort === true) { 8035 this._requestAbort = false; 8036 return; 8037 } else if (this._status === _loader.LoaderStatus.kError) { 8038 return; 8039 } 8040 8041 this._status = _loader.LoaderStatus.kComplete; 8042 if (this._onComplete) { 8043 this._onComplete(this._range.from, this._range.from + this._receivedLength - 1); 8044 } 8045 } 8046 }, { 8047 key: '_onXhrError', 8048 value: function _onXhrError(e) { 8049 this._status = _loader.LoaderStatus.kError; 8050 var type = 0; 8051 var info = null; 8052 8053 if (this._contentLength && e.loaded < this._contentLength) { 8054 type = _loader.LoaderErrors.EARLY_EOF; 8055 info = { code: -1, msg: 'Moz-Chunked stream meet Early-Eof' }; 8056 } else { 8057 type = _loader.LoaderErrors.EXCEPTION; 8058 info = { code: -1, msg: e.constructor.name + ' ' + e.type }; 8059 } 8060 8061 if (this._onError) { 8062 this._onError(type, info); 8063 } else { 8064 throw new _exception.RuntimeException(info.msg); 8065 } 8066 } 8067 }]); 8068 8069 return MozChunkedLoader; 8070 }(_loader.BaseLoader); 8071 8072 exports.default = MozChunkedLoader; 8073 8074 },{"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24}],30:[function(_dereq_,module,exports){ 8075 'use strict'; 8076 8077 Object.defineProperty(exports, "__esModule", { 8078 value: true 8079 }); 8080 8081 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 8082 8083 var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; 8084 8085 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 8086 8087 var _logger = _dereq_('../utils/logger.js'); 8088 8089 var _logger2 = _interopRequireDefault(_logger); 8090 8091 var _loader = _dereq_('./loader.js'); 8092 8093 var _exception = _dereq_('../utils/exception.js'); 8094 8095 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 8096 8097 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 8098 8099 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 8100 8101 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* 8102 * Copyright (C) 2016 Bilibili. All Rights Reserved. 8103 * 8104 * @author zheng qian <xqq@xqq.im> 8105 * 8106 * Licensed under the Apache License, Version 2.0 (the "License"); 8107 * you may not use this file except in compliance with the License. 8108 * You may obtain a copy of the License at 8109 * 8110 * http://www.apache.org/licenses/LICENSE-2.0 8111 * 8112 * Unless required by applicable law or agreed to in writing, software 8113 * distributed under the License is distributed on an "AS IS" BASIS, 8114 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8115 * See the License for the specific language governing permissions and 8116 * limitations under the License. 8117 */ 8118 8119 /* Notice: ms-stream may cause IE/Edge browser crash if seek too frequently!!! 8120 * The browser may crash in wininet.dll. Disable for now. 8121 * 8122 * For IE11/Edge browser by microsoft which supports `xhr.responseType = 'ms-stream'` 8123 * Notice that ms-stream API sucks. The buffer is always expanding along with downloading. 8124 * 8125 * We need to abort the xhr if buffer size exceeded limit size (e.g. 16 MiB), then do reconnect. 8126 * in order to release previous ArrayBuffer to avoid memory leak 8127 * 8128 * Otherwise, the ArrayBuffer will increase to a terrible size that equals final file size. 8129 */ 8130 var MSStreamLoader = function (_BaseLoader) { 8131 _inherits(MSStreamLoader, _BaseLoader); 8132 8133 _createClass(MSStreamLoader, null, [{ 8134 key: 'isSupported', 8135 value: function isSupported() { 8136 try { 8137 if (typeof self.MSStream === 'undefined' || typeof self.MSStreamReader === 'undefined') { 8138 return false; 8139 } 8140 8141 var xhr = new XMLHttpRequest(); 8142 xhr.open('GET', 'https://example.com', true); 8143 xhr.responseType = 'ms-stream'; 8144 return xhr.responseType === 'ms-stream'; 8145 } catch (e) { 8146 _logger2.default.w('MSStreamLoader', e.message); 8147 return false; 8148 } 8149 } 8150 }]); 8151 8152 function MSStreamLoader(seekHandler, config) { 8153 _classCallCheck(this, MSStreamLoader); 8154 8155 var _this = _possibleConstructorReturn(this, (MSStreamLoader.__proto__ || Object.getPrototypeOf(MSStreamLoader)).call(this, 'xhr-msstream-loader')); 8156 8157 _this.TAG = 'MSStreamLoader'; 8158 8159 _this._seekHandler = seekHandler; 8160 _this._config = config; 8161 _this._needStash = true; 8162 8163 _this._xhr = null; 8164 _this._reader = null; // MSStreamReader 8165 8166 _this._totalRange = null; 8167 _this._currentRange = null; 8168 8169 _this._currentRequestURL = null; 8170 _this._currentRedirectedURL = null; 8171 8172 _this._contentLength = null; 8173 _this._receivedLength = 0; 8174 8175 _this._bufferLimit = 16 * 1024 * 1024; // 16MB 8176 _this._lastTimeBufferSize = 0; 8177 _this._isReconnecting = false; 8178 return _this; 8179 } 8180 8181 _createClass(MSStreamLoader, [{ 8182 key: 'destroy', 8183 value: function destroy() { 8184 if (this.isWorking()) { 8185 this.abort(); 8186 } 8187 if (this._reader) { 8188 this._reader.onprogress = null; 8189 this._reader.onload = null; 8190 this._reader.onerror = null; 8191 this._reader = null; 8192 } 8193 if (this._xhr) { 8194 this._xhr.onreadystatechange = null; 8195 this._xhr = null; 8196 } 8197 _get(MSStreamLoader.prototype.__proto__ || Object.getPrototypeOf(MSStreamLoader.prototype), 'destroy', this).call(this); 8198 } 8199 }, { 8200 key: 'open', 8201 value: function open(dataSource, range) { 8202 this._internalOpen(dataSource, range, false); 8203 } 8204 }, { 8205 key: '_internalOpen', 8206 value: function _internalOpen(dataSource, range, isSubrange) { 8207 this._dataSource = dataSource; 8208 8209 if (!isSubrange) { 8210 this._totalRange = range; 8211 } else { 8212 this._currentRange = range; 8213 } 8214 8215 var sourceURL = dataSource.url; 8216 if (this._config.reuseRedirectedURL) { 8217 if (this._currentRedirectedURL != undefined) { 8218 sourceURL = this._currentRedirectedURL; 8219 } else if (dataSource.redirectedURL != undefined) { 8220 sourceURL = dataSource.redirectedURL; 8221 } 8222 } 8223 8224 var seekConfig = this._seekHandler.getConfig(sourceURL, range); 8225 this._currentRequestURL = seekConfig.url; 8226 8227 var reader = this._reader = new self.MSStreamReader(); 8228 reader.onprogress = this._msrOnProgress.bind(this); 8229 reader.onload = this._msrOnLoad.bind(this); 8230 reader.onerror = this._msrOnError.bind(this); 8231 8232 var xhr = this._xhr = new XMLHttpRequest(); 8233 xhr.open('GET', seekConfig.url, true); 8234 xhr.responseType = 'ms-stream'; 8235 xhr.onreadystatechange = this._xhrOnReadyStateChange.bind(this); 8236 xhr.onerror = this._xhrOnError.bind(this); 8237 8238 if (dataSource.withCredentials) { 8239 xhr.withCredentials = true; 8240 } 8241 8242 if (_typeof(seekConfig.headers) === 'object') { 8243 var headers = seekConfig.headers; 8244 8245 for (var key in headers) { 8246 if (headers.hasOwnProperty(key)) { 8247 xhr.setRequestHeader(key, headers[key]); 8248 } 8249 } 8250 } 8251 8252 // add additional headers 8253 if (_typeof(this._config.headers) === 'object') { 8254 var _headers = this._config.headers; 8255 8256 for (var _key in _headers) { 8257 if (_headers.hasOwnProperty(_key)) { 8258 xhr.setRequestHeader(_key, _headers[_key]); 8259 } 8260 } 8261 } 8262 8263 if (this._isReconnecting) { 8264 this._isReconnecting = false; 8265 } else { 8266 this._status = _loader.LoaderStatus.kConnecting; 8267 } 8268 xhr.send(); 8269 } 8270 }, { 8271 key: 'abort', 8272 value: function abort() { 8273 this._internalAbort(); 8274 this._status = _loader.LoaderStatus.kComplete; 8275 } 8276 }, { 8277 key: '_internalAbort', 8278 value: function _internalAbort() { 8279 if (this._reader) { 8280 if (this._reader.readyState === 1) { 8281 // LOADING 8282 this._reader.abort(); 8283 } 8284 this._reader.onprogress = null; 8285 this._reader.onload = null; 8286 this._reader.onerror = null; 8287 this._reader = null; 8288 } 8289 if (this._xhr) { 8290 this._xhr.abort(); 8291 this._xhr.onreadystatechange = null; 8292 this._xhr = null; 8293 } 8294 } 8295 }, { 8296 key: '_xhrOnReadyStateChange', 8297 value: function _xhrOnReadyStateChange(e) { 8298 var xhr = e.target; 8299 8300 if (xhr.readyState === 2) { 8301 // HEADERS_RECEIVED 8302 if (xhr.status >= 200 && xhr.status <= 299) { 8303 this._status = _loader.LoaderStatus.kBuffering; 8304 8305 if (xhr.responseURL != undefined) { 8306 var redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL); 8307 if (xhr.responseURL !== this._currentRequestURL && redirectedURL !== this._currentRedirectedURL) { 8308 this._currentRedirectedURL = redirectedURL; 8309 if (this._onURLRedirect) { 8310 this._onURLRedirect(redirectedURL); 8311 } 8312 } 8313 } 8314 8315 var lengthHeader = xhr.getResponseHeader('Content-Length'); 8316 if (lengthHeader != null && this._contentLength == null) { 8317 var length = parseInt(lengthHeader); 8318 if (length > 0) { 8319 this._contentLength = length; 8320 if (this._onContentLengthKnown) { 8321 this._onContentLengthKnown(this._contentLength); 8322 } 8323 } 8324 } 8325 } else { 8326 this._status = _loader.LoaderStatus.kError; 8327 if (this._onError) { 8328 this._onError(_loader.LoaderErrors.HTTP_STATUS_CODE_INVALID, { code: xhr.status, msg: xhr.statusText }); 8329 } else { 8330 throw new _exception.RuntimeException('MSStreamLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText); 8331 } 8332 } 8333 } else if (xhr.readyState === 3) { 8334 // LOADING 8335 if (xhr.status >= 200 && xhr.status <= 299) { 8336 this._status = _loader.LoaderStatus.kBuffering; 8337 8338 var msstream = xhr.response; 8339 this._reader.readAsArrayBuffer(msstream); 8340 } 8341 } 8342 } 8343 }, { 8344 key: '_xhrOnError', 8345 value: function _xhrOnError(e) { 8346 this._status = _loader.LoaderStatus.kError; 8347 var type = _loader.LoaderErrors.EXCEPTION; 8348 var info = { code: -1, msg: e.constructor.name + ' ' + e.type }; 8349 8350 if (this._onError) { 8351 this._onError(type, info); 8352 } else { 8353 throw new _exception.RuntimeException(info.msg); 8354 } 8355 } 8356 }, { 8357 key: '_msrOnProgress', 8358 value: function _msrOnProgress(e) { 8359 var reader = e.target; 8360 var bigbuffer = reader.result; 8361 if (bigbuffer == null) { 8362 // result may be null, workaround for buggy M$ 8363 this._doReconnectIfNeeded(); 8364 return; 8365 } 8366 8367 var slice = bigbuffer.slice(this._lastTimeBufferSize); 8368 this._lastTimeBufferSize = bigbuffer.byteLength; 8369 var byteStart = this._totalRange.from + this._receivedLength; 8370 this._receivedLength += slice.byteLength; 8371 8372 if (this._onDataArrival) { 8373 this._onDataArrival(slice, byteStart, this._receivedLength); 8374 } 8375 8376 if (bigbuffer.byteLength >= this._bufferLimit) { 8377 _logger2.default.v(this.TAG, 'MSStream buffer exceeded max size near ' + (byteStart + slice.byteLength) + ', reconnecting...'); 8378 this._doReconnectIfNeeded(); 8379 } 8380 } 8381 }, { 8382 key: '_doReconnectIfNeeded', 8383 value: function _doReconnectIfNeeded() { 8384 if (this._contentLength == null || this._receivedLength < this._contentLength) { 8385 this._isReconnecting = true; 8386 this._lastTimeBufferSize = 0; 8387 this._internalAbort(); 8388 8389 var range = { 8390 from: this._totalRange.from + this._receivedLength, 8391 to: -1 8392 }; 8393 this._internalOpen(this._dataSource, range, true); 8394 } 8395 } 8396 }, { 8397 key: '_msrOnLoad', 8398 value: function _msrOnLoad(e) { 8399 // actually it is onComplete event 8400 this._status = _loader.LoaderStatus.kComplete; 8401 if (this._onComplete) { 8402 this._onComplete(this._totalRange.from, this._totalRange.from + this._receivedLength - 1); 8403 } 8404 } 8405 }, { 8406 key: '_msrOnError', 8407 value: function _msrOnError(e) { 8408 this._status = _loader.LoaderStatus.kError; 8409 var type = 0; 8410 var info = null; 8411 8412 if (this._contentLength && this._receivedLength < this._contentLength) { 8413 type = _loader.LoaderErrors.EARLY_EOF; 8414 info = { code: -1, msg: 'MSStream meet Early-Eof' }; 8415 } else { 8416 type = _loader.LoaderErrors.EARLY_EOF; 8417 info = { code: -1, msg: e.constructor.name + ' ' + e.type }; 8418 } 8419 8420 if (this._onError) { 8421 this._onError(type, info); 8422 } else { 8423 throw new _exception.RuntimeException(info.msg); 8424 } 8425 } 8426 }]); 8427 8428 return MSStreamLoader; 8429 }(_loader.BaseLoader); 8430 8431 exports.default = MSStreamLoader; 8432 8433 },{"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24}],31:[function(_dereq_,module,exports){ 8434 'use strict'; 8435 8436 Object.defineProperty(exports, "__esModule", { 8437 value: true 8438 }); 8439 8440 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 8441 8442 var _get = function get(object, property, receiver) { if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { return get(parent, property, receiver); } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } }; 8443 8444 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 8445 8446 var _logger = _dereq_('../utils/logger.js'); 8447 8448 var _logger2 = _interopRequireDefault(_logger); 8449 8450 var _speedSampler = _dereq_('./speed-sampler.js'); 8451 8452 var _speedSampler2 = _interopRequireDefault(_speedSampler); 8453 8454 var _loader = _dereq_('./loader.js'); 8455 8456 var _exception = _dereq_('../utils/exception.js'); 8457 8458 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 8459 8460 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 8461 8462 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 8463 8464 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } /* 8465 * Copyright (C) 2016 Bilibili. All Rights Reserved. 8466 * 8467 * @author zheng qian <xqq@xqq.im> 8468 * 8469 * Licensed under the Apache License, Version 2.0 (the "License"); 8470 * you may not use this file except in compliance with the License. 8471 * You may obtain a copy of the License at 8472 * 8473 * http://www.apache.org/licenses/LICENSE-2.0 8474 * 8475 * Unless required by applicable law or agreed to in writing, software 8476 * distributed under the License is distributed on an "AS IS" BASIS, 8477 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8478 * See the License for the specific language governing permissions and 8479 * limitations under the License. 8480 */ 8481 8482 // Universal IO Loader, implemented by adding Range header in xhr's request header 8483 var RangeLoader = function (_BaseLoader) { 8484 _inherits(RangeLoader, _BaseLoader); 8485 8486 _createClass(RangeLoader, null, [{ 8487 key: 'isSupported', 8488 value: function isSupported() { 8489 try { 8490 var xhr = new XMLHttpRequest(); 8491 xhr.open('GET', 'https://example.com', true); 8492 xhr.responseType = 'arraybuffer'; 8493 return xhr.responseType === 'arraybuffer'; 8494 } catch (e) { 8495 _logger2.default.w('RangeLoader', e.message); 8496 return false; 8497 } 8498 } 8499 }]); 8500 8501 function RangeLoader(seekHandler, config) { 8502 _classCallCheck(this, RangeLoader); 8503 8504 var _this = _possibleConstructorReturn(this, (RangeLoader.__proto__ || Object.getPrototypeOf(RangeLoader)).call(this, 'xhr-range-loader')); 8505 8506 _this.TAG = 'RangeLoader'; 8507 8508 _this._seekHandler = seekHandler; 8509 _this._config = config; 8510 _this._needStash = false; 8511 8512 _this._chunkSizeKBList = [128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 5120, 6144, 7168, 8192]; 8513 _this._currentChunkSizeKB = 384; 8514 _this._currentSpeedNormalized = 0; 8515 _this._zeroSpeedChunkCount = 0; 8516 8517 _this._xhr = null; 8518 _this._speedSampler = new _speedSampler2.default(); 8519 8520 _this._requestAbort = false; 8521 _this._waitForTotalLength = false; 8522 _this._totalLengthReceived = false; 8523 8524 _this._currentRequestURL = null; 8525 _this._currentRedirectedURL = null; 8526 _this._currentRequestRange = null; 8527 _this._totalLength = null; // size of the entire file 8528 _this._contentLength = null; // Content-Length of entire request range 8529 _this._receivedLength = 0; // total received bytes 8530 _this._lastTimeLoaded = 0; // received bytes of current request sub-range 8531 return _this; 8532 } 8533 8534 _createClass(RangeLoader, [{ 8535 key: 'destroy', 8536 value: function destroy() { 8537 if (this.isWorking()) { 8538 this.abort(); 8539 } 8540 if (this._xhr) { 8541 this._xhr.onreadystatechange = null; 8542 this._xhr.onprogress = null; 8543 this._xhr.onload = null; 8544 this._xhr.onerror = null; 8545 this._xhr = null; 8546 } 8547 _get(RangeLoader.prototype.__proto__ || Object.getPrototypeOf(RangeLoader.prototype), 'destroy', this).call(this); 8548 } 8549 }, { 8550 key: 'open', 8551 value: function open(dataSource, range) { 8552 this._dataSource = dataSource; 8553 this._range = range; 8554 this._status = _loader.LoaderStatus.kConnecting; 8555 8556 var useRefTotalLength = false; 8557 if (this._dataSource.filesize != undefined && this._dataSource.filesize !== 0) { 8558 useRefTotalLength = true; 8559 this._totalLength = this._dataSource.filesize; 8560 } 8561 8562 if (!this._totalLengthReceived && !useRefTotalLength) { 8563 // We need total filesize 8564 this._waitForTotalLength = true; 8565 this._internalOpen(this._dataSource, { from: 0, to: -1 }); 8566 } else { 8567 // We have filesize, start loading 8568 this._openSubRange(); 8569 } 8570 } 8571 }, { 8572 key: '_openSubRange', 8573 value: function _openSubRange() { 8574 var chunkSize = this._currentChunkSizeKB * 1024; 8575 8576 var from = this._range.from + this._receivedLength; 8577 var to = from + chunkSize; 8578 8579 if (this._contentLength != null) { 8580 if (to - this._range.from >= this._contentLength) { 8581 to = this._range.from + this._contentLength - 1; 8582 } 8583 } 8584 8585 this._currentRequestRange = { from: from, to: to }; 8586 this._internalOpen(this._dataSource, this._currentRequestRange); 8587 } 8588 }, { 8589 key: '_internalOpen', 8590 value: function _internalOpen(dataSource, range) { 8591 this._lastTimeLoaded = 0; 8592 8593 var sourceURL = dataSource.url; 8594 if (this._config.reuseRedirectedURL) { 8595 if (this._currentRedirectedURL != undefined) { 8596 sourceURL = this._currentRedirectedURL; 8597 } else if (dataSource.redirectedURL != undefined) { 8598 sourceURL = dataSource.redirectedURL; 8599 } 8600 } 8601 8602 var seekConfig = this._seekHandler.getConfig(sourceURL, range); 8603 this._currentRequestURL = seekConfig.url; 8604 8605 var xhr = this._xhr = new XMLHttpRequest(); 8606 xhr.open('GET', seekConfig.url, true); 8607 xhr.responseType = 'arraybuffer'; 8608 xhr.onreadystatechange = this._onReadyStateChange.bind(this); 8609 xhr.onprogress = this._onProgress.bind(this); 8610 xhr.onload = this._onLoad.bind(this); 8611 xhr.onerror = this._onXhrError.bind(this); 8612 8613 if (dataSource.withCredentials) { 8614 xhr.withCredentials = true; 8615 } 8616 8617 if (_typeof(seekConfig.headers) === 'object') { 8618 var headers = seekConfig.headers; 8619 8620 for (var key in headers) { 8621 if (headers.hasOwnProperty(key)) { 8622 xhr.setRequestHeader(key, headers[key]); 8623 } 8624 } 8625 } 8626 8627 // add additional headers 8628 if (_typeof(this._config.headers) === 'object') { 8629 var _headers = this._config.headers; 8630 8631 for (var _key in _headers) { 8632 if (_headers.hasOwnProperty(_key)) { 8633 xhr.setRequestHeader(_key, _headers[_key]); 8634 } 8635 } 8636 } 8637 8638 xhr.send(); 8639 } 8640 }, { 8641 key: 'abort', 8642 value: function abort() { 8643 this._requestAbort = true; 8644 this._internalAbort(); 8645 this._status = _loader.LoaderStatus.kComplete; 8646 } 8647 }, { 8648 key: '_internalAbort', 8649 value: function _internalAbort() { 8650 if (this._xhr) { 8651 this._xhr.onreadystatechange = null; 8652 this._xhr.onprogress = null; 8653 this._xhr.onload = null; 8654 this._xhr.onerror = null; 8655 this._xhr.abort(); 8656 this._xhr = null; 8657 } 8658 } 8659 }, { 8660 key: '_onReadyStateChange', 8661 value: function _onReadyStateChange(e) { 8662 var xhr = e.target; 8663 8664 if (xhr.readyState === 2) { 8665 // HEADERS_RECEIVED 8666 if (xhr.responseURL != undefined) { 8667 // if the browser support this property 8668 var redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL); 8669 if (xhr.responseURL !== this._currentRequestURL && redirectedURL !== this._currentRedirectedURL) { 8670 this._currentRedirectedURL = redirectedURL; 8671 if (this._onURLRedirect) { 8672 this._onURLRedirect(redirectedURL); 8673 } 8674 } 8675 } 8676 8677 if (xhr.status >= 200 && xhr.status <= 299) { 8678 if (this._waitForTotalLength) { 8679 return; 8680 } 8681 this._status = _loader.LoaderStatus.kBuffering; 8682 } else { 8683 this._status = _loader.LoaderStatus.kError; 8684 if (this._onError) { 8685 this._onError(_loader.LoaderErrors.HTTP_STATUS_CODE_INVALID, { code: xhr.status, msg: xhr.statusText }); 8686 } else { 8687 throw new _exception.RuntimeException('RangeLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText); 8688 } 8689 } 8690 } 8691 } 8692 }, { 8693 key: '_onProgress', 8694 value: function _onProgress(e) { 8695 if (this._status === _loader.LoaderStatus.kError) { 8696 // Ignore error response 8697 return; 8698 } 8699 8700 if (this._contentLength === null) { 8701 var openNextRange = false; 8702 8703 if (this._waitForTotalLength) { 8704 this._waitForTotalLength = false; 8705 this._totalLengthReceived = true; 8706 openNextRange = true; 8707 8708 var total = e.total; 8709 this._internalAbort(); 8710 if (total != null & total !== 0) { 8711 this._totalLength = total; 8712 } 8713 } 8714 8715 // calculate currrent request range's contentLength 8716 if (this._range.to === -1) { 8717 this._contentLength = this._totalLength - this._range.from; 8718 } else { 8719 // to !== -1 8720 this._contentLength = this._range.to - this._range.from + 1; 8721 } 8722 8723 if (openNextRange) { 8724 this._openSubRange(); 8725 return; 8726 } 8727 if (this._onContentLengthKnown) { 8728 this._onContentLengthKnown(this._contentLength); 8729 } 8730 } 8731 8732 var delta = e.loaded - this._lastTimeLoaded; 8733 this._lastTimeLoaded = e.loaded; 8734 this._speedSampler.addBytes(delta); 8735 } 8736 }, { 8737 key: '_normalizeSpeed', 8738 value: function _normalizeSpeed(input) { 8739 var list = this._chunkSizeKBList; 8740 var last = list.length - 1; 8741 var mid = 0; 8742 var lbound = 0; 8743 var ubound = last; 8744 8745 if (input < list[0]) { 8746 return list[0]; 8747 } 8748 8749 while (lbound <= ubound) { 8750 mid = lbound + Math.floor((ubound - lbound) / 2); 8751 if (mid === last || input >= list[mid] && input < list[mid + 1]) { 8752 return list[mid]; 8753 } else if (list[mid] < input) { 8754 lbound = mid + 1; 8755 } else { 8756 ubound = mid - 1; 8757 } 8758 } 8759 } 8760 }, { 8761 key: '_onLoad', 8762 value: function _onLoad(e) { 8763 if (this._status === _loader.LoaderStatus.kError) { 8764 // Ignore error response 8765 return; 8766 } 8767 8768 if (this._waitForTotalLength) { 8769 this._waitForTotalLength = false; 8770 return; 8771 } 8772 8773 this._lastTimeLoaded = 0; 8774 var KBps = this._speedSampler.lastSecondKBps; 8775 if (KBps === 0) { 8776 this._zeroSpeedChunkCount++; 8777 if (this._zeroSpeedChunkCount >= 3) { 8778 // Try get currentKBps after 3 chunks 8779 KBps = this._speedSampler.currentKBps; 8780 } 8781 } 8782 8783 if (KBps !== 0) { 8784 var normalized = this._normalizeSpeed(KBps); 8785 if (this._currentSpeedNormalized !== normalized) { 8786 this._currentSpeedNormalized = normalized; 8787 this._currentChunkSizeKB = normalized; 8788 } 8789 } 8790 8791 var chunk = e.target.response; 8792 var byteStart = this._range.from + this._receivedLength; 8793 this._receivedLength += chunk.byteLength; 8794 8795 var reportComplete = false; 8796 8797 if (this._contentLength != null && this._receivedLength < this._contentLength) { 8798 // continue load next chunk 8799 this._openSubRange(); 8800 } else { 8801 reportComplete = true; 8802 } 8803 8804 // dispatch received chunk 8805 if (this._onDataArrival) { 8806 this._onDataArrival(chunk, byteStart, this._receivedLength); 8807 } 8808 8809 if (reportComplete) { 8810 this._status = _loader.LoaderStatus.kComplete; 8811 if (this._onComplete) { 8812 this._onComplete(this._range.from, this._range.from + this._receivedLength - 1); 8813 } 8814 } 8815 } 8816 }, { 8817 key: '_onXhrError', 8818 value: function _onXhrError(e) { 8819 this._status = _loader.LoaderStatus.kError; 8820 var type = 0; 8821 var info = null; 8822 8823 if (this._contentLength && this._receivedLength > 0 && this._receivedLength < this._contentLength) { 8824 type = _loader.LoaderErrors.EARLY_EOF; 8825 info = { code: -1, msg: 'RangeLoader meet Early-Eof' }; 8826 } else { 8827 type = _loader.LoaderErrors.EXCEPTION; 8828 info = { code: -1, msg: e.constructor.name + ' ' + e.type }; 8829 } 8830 8831 if (this._onError) { 8832 this._onError(type, info); 8833 } else { 8834 throw new _exception.RuntimeException(info.msg); 8835 } 8836 } 8837 }, { 8838 key: 'currentSpeed', 8839 get: function get() { 8840 return this._speedSampler.lastSecondKBps; 8841 } 8842 }]); 8843 8844 return RangeLoader; 8845 }(_loader.BaseLoader); 8846 8847 exports.default = RangeLoader; 8848 8849 },{"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24,"./speed-sampler.js":27}],32:[function(_dereq_,module,exports){ 8850 'use strict'; 8851 8852 Object.defineProperty(exports, "__esModule", { 8853 value: true 8854 }); 8855 8856 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 8857 8858 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 8859 * Copyright (C) 2016 Bilibili. All Rights Reserved. 8860 * 8861 * @author zheng qian <xqq@xqq.im> 8862 * 8863 * Licensed under the Apache License, Version 2.0 (the "License"); 8864 * you may not use this file except in compliance with the License. 8865 * You may obtain a copy of the License at 8866 * 8867 * http://www.apache.org/licenses/LICENSE-2.0 8868 * 8869 * Unless required by applicable law or agreed to in writing, software 8870 * distributed under the License is distributed on an "AS IS" BASIS, 8871 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 8872 * See the License for the specific language governing permissions and 8873 * limitations under the License. 8874 */ 8875 8876 var _events = _dereq_('events'); 8877 8878 var _events2 = _interopRequireDefault(_events); 8879 8880 var _logger = _dereq_('../utils/logger.js'); 8881 8882 var _logger2 = _interopRequireDefault(_logger); 8883 8884 var _browser = _dereq_('../utils/browser.js'); 8885 8886 var _browser2 = _interopRequireDefault(_browser); 8887 8888 var _playerEvents = _dereq_('./player-events.js'); 8889 8890 var _playerEvents2 = _interopRequireDefault(_playerEvents); 8891 8892 var _transmuxer = _dereq_('../core/transmuxer.js'); 8893 8894 var _transmuxer2 = _interopRequireDefault(_transmuxer); 8895 8896 var _transmuxingEvents = _dereq_('../core/transmuxing-events.js'); 8897 8898 var _transmuxingEvents2 = _interopRequireDefault(_transmuxingEvents); 8899 8900 var _mseController = _dereq_('../core/mse-controller.js'); 8901 8902 var _mseController2 = _interopRequireDefault(_mseController); 8903 8904 var _mseEvents = _dereq_('../core/mse-events.js'); 8905 8906 var _mseEvents2 = _interopRequireDefault(_mseEvents); 8907 8908 var _playerErrors = _dereq_('./player-errors.js'); 8909 8910 var _config = _dereq_('../config.js'); 8911 8912 var _exception = _dereq_('../utils/exception.js'); 8913 8914 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 8915 8916 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 8917 8918 var FlvPlayer = function () { 8919 function FlvPlayer(mediaDataSource, config) { 8920 _classCallCheck(this, FlvPlayer); 8921 8922 this.TAG = 'FlvPlayer'; 8923 this._type = 'FlvPlayer'; 8924 this._emitter = new _events2.default(); 8925 8926 this._config = (0, _config.createDefaultConfig)(); 8927 if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { 8928 Object.assign(this._config, config); 8929 } 8930 8931 if (mediaDataSource.type.toLowerCase() !== 'flv') { 8932 throw new _exception.InvalidArgumentException('FlvPlayer requires an flv MediaDataSource input!'); 8933 } 8934 8935 if (mediaDataSource.isLive === true) { 8936 this._config.isLive = true; 8937 } 8938 8939 this.e = { 8940 onvLoadedMetadata: this._onvLoadedMetadata.bind(this), 8941 onvSeeking: this._onvSeeking.bind(this), 8942 onvCanPlay: this._onvCanPlay.bind(this), 8943 onvStalled: this._onvStalled.bind(this), 8944 onvProgress: this._onvProgress.bind(this) 8945 }; 8946 8947 if (self.performance && self.performance.now) { 8948 this._now = self.performance.now.bind(self.performance); 8949 } else { 8950 this._now = Date.now; 8951 } 8952 8953 this._pendingSeekTime = null; // in seconds 8954 this._requestSetTime = false; 8955 this._seekpointRecord = null; 8956 this._progressChecker = null; 8957 8958 this._mediaDataSource = mediaDataSource; 8959 this._mediaElement = null; 8960 this._msectl = null; 8961 this._transmuxer = null; 8962 8963 this._mseSourceOpened = false; 8964 this._hasPendingLoad = false; 8965 this._receivedCanPlay = false; 8966 8967 this._mediaInfo = null; 8968 this._statisticsInfo = null; 8969 8970 var chromeNeedIDRFix = _browser2.default.chrome && (_browser2.default.version.major < 50 || _browser2.default.version.major === 50 && _browser2.default.version.build < 2661); 8971 this._alwaysSeekKeyframe = chromeNeedIDRFix || _browser2.default.msedge || _browser2.default.msie ? true : false; 8972 8973 if (this._alwaysSeekKeyframe) { 8974 this._config.accurateSeek = false; 8975 } 8976 } 8977 8978 _createClass(FlvPlayer, [{ 8979 key: 'destroy', 8980 value: function destroy() { 8981 if (this._progressChecker != null) { 8982 window.clearInterval(this._progressChecker); 8983 this._progressChecker = null; 8984 } 8985 if (this._transmuxer) { 8986 this.unload(); 8987 } 8988 if (this._mediaElement) { 8989 this.detachMediaElement(); 8990 } 8991 this.e = null; 8992 this._mediaDataSource = null; 8993 8994 this._emitter.removeAllListeners(); 8995 this._emitter = null; 8996 } 8997 }, { 8998 key: 'on', 8999 value: function on(event, listener) { 9000 var _this = this; 9001 9002 if (event === _playerEvents2.default.MEDIA_INFO) { 9003 if (this._mediaInfo != null) { 9004 Promise.resolve().then(function () { 9005 _this._emitter.emit(_playerEvents2.default.MEDIA_INFO, _this.mediaInfo); 9006 }); 9007 } 9008 } else if (event === _playerEvents2.default.STATISTICS_INFO) { 9009 if (this._statisticsInfo != null) { 9010 Promise.resolve().then(function () { 9011 _this._emitter.emit(_playerEvents2.default.STATISTICS_INFO, _this.statisticsInfo); 9012 }); 9013 } 9014 } 9015 this._emitter.addListener(event, listener); 9016 } 9017 }, { 9018 key: 'off', 9019 value: function off(event, listener) { 9020 this._emitter.removeListener(event, listener); 9021 } 9022 }, { 9023 key: 'attachMediaElement', 9024 value: function attachMediaElement(mediaElement) { 9025 var _this2 = this; 9026 9027 this._mediaElement = mediaElement; 9028 mediaElement.addEventListener('loadedmetadata', this.e.onvLoadedMetadata); 9029 mediaElement.addEventListener('seeking', this.e.onvSeeking); 9030 mediaElement.addEventListener('canplay', this.e.onvCanPlay); 9031 mediaElement.addEventListener('stalled', this.e.onvStalled); 9032 mediaElement.addEventListener('progress', this.e.onvProgress); 9033 9034 this._msectl = new _mseController2.default(this._config); 9035 9036 this._msectl.on(_mseEvents2.default.UPDATE_END, this._onmseUpdateEnd.bind(this)); 9037 this._msectl.on(_mseEvents2.default.BUFFER_FULL, this._onmseBufferFull.bind(this)); 9038 this._msectl.on(_mseEvents2.default.SOURCE_OPEN, function () { 9039 _this2._mseSourceOpened = true; 9040 if (_this2._hasPendingLoad) { 9041 _this2._hasPendingLoad = false; 9042 _this2.load(); 9043 } 9044 }); 9045 this._msectl.on(_mseEvents2.default.ERROR, function (info) { 9046 _this2._emitter.emit(_playerEvents2.default.ERROR, _playerErrors.ErrorTypes.MEDIA_ERROR, _playerErrors.ErrorDetails.MEDIA_MSE_ERROR, info); 9047 }); 9048 9049 this._msectl.attachMediaElement(mediaElement); 9050 9051 if (this._pendingSeekTime != null) { 9052 try { 9053 mediaElement.currentTime = this._pendingSeekTime; 9054 this._pendingSeekTime = null; 9055 } catch (e) { 9056 // IE11 may throw InvalidStateError if readyState === 0 9057 // We can defer set currentTime operation after loadedmetadata 9058 } 9059 } 9060 } 9061 }, { 9062 key: 'detachMediaElement', 9063 value: function detachMediaElement() { 9064 if (this._mediaElement) { 9065 this._msectl.detachMediaElement(); 9066 this._mediaElement.removeEventListener('loadedmetadata', this.e.onvLoadedMetadata); 9067 this._mediaElement.removeEventListener('seeking', this.e.onvSeeking); 9068 this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay); 9069 this._mediaElement.removeEventListener('stalled', this.e.onvStalled); 9070 this._mediaElement.removeEventListener('progress', this.e.onvProgress); 9071 this._mediaElement = null; 9072 } 9073 if (this._msectl) { 9074 this._msectl.destroy(); 9075 this._msectl = null; 9076 } 9077 } 9078 }, { 9079 key: 'load', 9080 value: function load() { 9081 var _this3 = this; 9082 9083 if (!this._mediaElement) { 9084 throw new _exception.IllegalStateException('HTMLMediaElement must be attached before load()!'); 9085 } 9086 if (this._transmuxer) { 9087 throw new _exception.IllegalStateException('FlvPlayer.load() has been called, please call unload() first!'); 9088 } 9089 if (this._hasPendingLoad) { 9090 return; 9091 } 9092 9093 if (this._config.deferLoadAfterSourceOpen && this._mseSourceOpened === false) { 9094 this._hasPendingLoad = true; 9095 return; 9096 } 9097 9098 if (this._mediaElement.readyState > 0) { 9099 this._requestSetTime = true; 9100 // IE11 may throw InvalidStateError if readyState === 0 9101 this._mediaElement.currentTime = 0; 9102 } 9103 9104 this._transmuxer = new _transmuxer2.default(this._mediaDataSource, this._config); 9105 9106 this._transmuxer.on(_transmuxingEvents2.default.INIT_SEGMENT, function (type, is) { 9107 _this3._msectl.appendInitSegment(is); 9108 }); 9109 this._transmuxer.on(_transmuxingEvents2.default.MEDIA_SEGMENT, function (type, ms) { 9110 _this3._msectl.appendMediaSegment(ms); 9111 9112 // lazyLoad check 9113 if (_this3._config.lazyLoad && !_this3._config.isLive) { 9114 var currentTime = _this3._mediaElement.currentTime; 9115 if (ms.info.endDts >= (currentTime + _this3._config.lazyLoadMaxDuration) * 1000) { 9116 if (_this3._progressChecker == null) { 9117 _logger2.default.v(_this3.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task'); 9118 _this3._suspendTransmuxer(); 9119 } 9120 } 9121 } 9122 }); 9123 this._transmuxer.on(_transmuxingEvents2.default.LOADING_COMPLETE, function () { 9124 _this3._msectl.endOfStream(); 9125 _this3._emitter.emit(_playerEvents2.default.LOADING_COMPLETE); 9126 }); 9127 this._transmuxer.on(_transmuxingEvents2.default.RECOVERED_EARLY_EOF, function () { 9128 _this3._emitter.emit(_playerEvents2.default.RECOVERED_EARLY_EOF); 9129 }); 9130 this._transmuxer.on(_transmuxingEvents2.default.IO_ERROR, function (detail, info) { 9131 _this3._emitter.emit(_playerEvents2.default.ERROR, _playerErrors.ErrorTypes.NETWORK_ERROR, detail, info); 9132 }); 9133 this._transmuxer.on(_transmuxingEvents2.default.DEMUX_ERROR, function (detail, info) { 9134 _this3._emitter.emit(_playerEvents2.default.ERROR, _playerErrors.ErrorTypes.MEDIA_ERROR, detail, { code: -1, msg: info }); 9135 }); 9136 this._transmuxer.on(_transmuxingEvents2.default.MEDIA_INFO, function (mediaInfo) { 9137 _this3._mediaInfo = mediaInfo; 9138 _this3._emitter.emit(_playerEvents2.default.MEDIA_INFO, Object.assign({}, mediaInfo)); 9139 }); 9140 this._transmuxer.on(_transmuxingEvents2.default.METADATA_ARRIVED, function (metadata) { 9141 _this3._emitter.emit(_playerEvents2.default.METADATA_ARRIVED, metadata); 9142 }); 9143 this._transmuxer.on(_transmuxingEvents2.default.SCRIPTDATA_ARRIVED, function (data) { 9144 _this3._emitter.emit(_playerEvents2.default.SCRIPTDATA_ARRIVED, data); 9145 }); 9146 this._transmuxer.on(_transmuxingEvents2.default.STATISTICS_INFO, function (statInfo) { 9147 _this3._statisticsInfo = _this3._fillStatisticsInfo(statInfo); 9148 _this3._emitter.emit(_playerEvents2.default.STATISTICS_INFO, Object.assign({}, _this3._statisticsInfo)); 9149 }); 9150 this._transmuxer.on(_transmuxingEvents2.default.RECOMMEND_SEEKPOINT, function (milliseconds) { 9151 if (_this3._mediaElement && !_this3._config.accurateSeek) { 9152 _this3._requestSetTime = true; 9153 _this3._mediaElement.currentTime = milliseconds / 1000; 9154 } 9155 }); 9156 9157 this._transmuxer.open(); 9158 } 9159 }, { 9160 key: 'unload', 9161 value: function unload() { 9162 if (this._mediaElement) { 9163 this._mediaElement.pause(); 9164 } 9165 if (this._msectl) { 9166 this._msectl.seek(0); 9167 } 9168 if (this._transmuxer) { 9169 this._transmuxer.close(); 9170 this._transmuxer.destroy(); 9171 this._transmuxer = null; 9172 } 9173 } 9174 }, { 9175 key: 'play', 9176 value: function play() { 9177 return this._mediaElement.play(); 9178 } 9179 }, { 9180 key: 'pause', 9181 value: function pause() { 9182 this._mediaElement.pause(); 9183 } 9184 }, { 9185 key: '_fillStatisticsInfo', 9186 value: function _fillStatisticsInfo(statInfo) { 9187 statInfo.playerType = this._type; 9188 9189 if (!(this._mediaElement instanceof HTMLVideoElement)) { 9190 return statInfo; 9191 } 9192 9193 var hasQualityInfo = true; 9194 var decoded = 0; 9195 var dropped = 0; 9196 9197 if (this._mediaElement.getVideoPlaybackQuality) { 9198 var quality = this._mediaElement.getVideoPlaybackQuality(); 9199 decoded = quality.totalVideoFrames; 9200 dropped = quality.droppedVideoFrames; 9201 } else if (this._mediaElement.webkitDecodedFrameCount != undefined) { 9202 decoded = this._mediaElement.webkitDecodedFrameCount; 9203 dropped = this._mediaElement.webkitDroppedFrameCount; 9204 } else { 9205 hasQualityInfo = false; 9206 } 9207 9208 if (hasQualityInfo) { 9209 statInfo.decodedFrames = decoded; 9210 statInfo.droppedFrames = dropped; 9211 } 9212 9213 return statInfo; 9214 } 9215 }, { 9216 key: '_onmseUpdateEnd', 9217 value: function _onmseUpdateEnd() { 9218 if (!this._config.lazyLoad || this._config.isLive) { 9219 return; 9220 } 9221 9222 var buffered = this._mediaElement.buffered; 9223 var currentTime = this._mediaElement.currentTime; 9224 var currentRangeStart = 0; 9225 var currentRangeEnd = 0; 9226 9227 for (var i = 0; i < buffered.length; i++) { 9228 var start = buffered.start(i); 9229 var end = buffered.end(i); 9230 if (start <= currentTime && currentTime < end) { 9231 currentRangeStart = start; 9232 currentRangeEnd = end; 9233 break; 9234 } 9235 } 9236 9237 if (currentRangeEnd >= currentTime + this._config.lazyLoadMaxDuration && this._progressChecker == null) { 9238 _logger2.default.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task'); 9239 this._suspendTransmuxer(); 9240 } 9241 } 9242 }, { 9243 key: '_onmseBufferFull', 9244 value: function _onmseBufferFull() { 9245 _logger2.default.v(this.TAG, 'MSE SourceBuffer is full, suspend transmuxing task'); 9246 if (this._progressChecker == null) { 9247 this._suspendTransmuxer(); 9248 } 9249 } 9250 }, { 9251 key: '_suspendTransmuxer', 9252 value: function _suspendTransmuxer() { 9253 if (this._transmuxer) { 9254 this._transmuxer.pause(); 9255 9256 if (this._progressChecker == null) { 9257 this._progressChecker = window.setInterval(this._checkProgressAndResume.bind(this), 1000); 9258 } 9259 } 9260 } 9261 }, { 9262 key: '_checkProgressAndResume', 9263 value: function _checkProgressAndResume() { 9264 var currentTime = this._mediaElement.currentTime; 9265 var buffered = this._mediaElement.buffered; 9266 9267 var needResume = false; 9268 9269 for (var i = 0; i < buffered.length; i++) { 9270 var from = buffered.start(i); 9271 var to = buffered.end(i); 9272 if (currentTime >= from && currentTime < to) { 9273 if (currentTime >= to - this._config.lazyLoadRecoverDuration) { 9274 needResume = true; 9275 } 9276 break; 9277 } 9278 } 9279 9280 if (needResume) { 9281 window.clearInterval(this._progressChecker); 9282 this._progressChecker = null; 9283 if (needResume) { 9284 _logger2.default.v(this.TAG, 'Continue loading from paused position'); 9285 this._transmuxer.resume(); 9286 } 9287 } 9288 } 9289 }, { 9290 key: '_isTimepointBuffered', 9291 value: function _isTimepointBuffered(seconds) { 9292 var buffered = this._mediaElement.buffered; 9293 9294 for (var i = 0; i < buffered.length; i++) { 9295 var from = buffered.start(i); 9296 var to = buffered.end(i); 9297 if (seconds >= from && seconds < to) { 9298 return true; 9299 } 9300 } 9301 return false; 9302 } 9303 }, { 9304 key: '_internalSeek', 9305 value: function _internalSeek(seconds) { 9306 var directSeek = this._isTimepointBuffered(seconds); 9307 9308 var directSeekBegin = false; 9309 var directSeekBeginTime = 0; 9310 9311 if (seconds < 1.0 && this._mediaElement.buffered.length > 0) { 9312 var videoBeginTime = this._mediaElement.buffered.start(0); 9313 if (videoBeginTime < 1.0 && seconds < videoBeginTime || _browser2.default.safari) { 9314 directSeekBegin = true; 9315 // also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid 9316 directSeekBeginTime = _browser2.default.safari ? 0.1 : videoBeginTime; 9317 } 9318 } 9319 9320 if (directSeekBegin) { 9321 // seek to video begin, set currentTime directly if beginPTS buffered 9322 this._requestSetTime = true; 9323 this._mediaElement.currentTime = directSeekBeginTime; 9324 } else if (directSeek) { 9325 // buffered position 9326 if (!this._alwaysSeekKeyframe) { 9327 this._requestSetTime = true; 9328 this._mediaElement.currentTime = seconds; 9329 } else { 9330 var idr = this._msectl.getNearestKeyframe(Math.floor(seconds * 1000)); 9331 this._requestSetTime = true; 9332 if (idr != null) { 9333 this._mediaElement.currentTime = idr.dts / 1000; 9334 } else { 9335 this._mediaElement.currentTime = seconds; 9336 } 9337 } 9338 if (this._progressChecker != null) { 9339 this._checkProgressAndResume(); 9340 } 9341 } else { 9342 if (this._progressChecker != null) { 9343 window.clearInterval(this._progressChecker); 9344 this._progressChecker = null; 9345 } 9346 this._msectl.seek(seconds); 9347 this._transmuxer.seek(Math.floor(seconds * 1000)); // in milliseconds 9348 // no need to set mediaElement.currentTime if non-accurateSeek, 9349 // just wait for the recommend_seekpoint callback 9350 if (this._config.accurateSeek) { 9351 this._requestSetTime = true; 9352 this._mediaElement.currentTime = seconds; 9353 } 9354 } 9355 } 9356 }, { 9357 key: '_checkAndApplyUnbufferedSeekpoint', 9358 value: function _checkAndApplyUnbufferedSeekpoint() { 9359 if (this._seekpointRecord) { 9360 if (this._seekpointRecord.recordTime <= this._now() - 100) { 9361 var target = this._mediaElement.currentTime; 9362 this._seekpointRecord = null; 9363 if (!this._isTimepointBuffered(target)) { 9364 if (this._progressChecker != null) { 9365 window.clearTimeout(this._progressChecker); 9366 this._progressChecker = null; 9367 } 9368 // .currentTime is consists with .buffered timestamp 9369 // Chrome/Edge use DTS, while FireFox/Safari use PTS 9370 this._msectl.seek(target); 9371 this._transmuxer.seek(Math.floor(target * 1000)); 9372 // set currentTime if accurateSeek, or wait for recommend_seekpoint callback 9373 if (this._config.accurateSeek) { 9374 this._requestSetTime = true; 9375 this._mediaElement.currentTime = target; 9376 } 9377 } 9378 } else { 9379 window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50); 9380 } 9381 } 9382 } 9383 }, { 9384 key: '_checkAndResumeStuckPlayback', 9385 value: function _checkAndResumeStuckPlayback(stalled) { 9386 var media = this._mediaElement; 9387 if (stalled || !this._receivedCanPlay || media.readyState < 2) { 9388 // HAVE_CURRENT_DATA 9389 var buffered = media.buffered; 9390 if (buffered.length > 0 && media.currentTime < buffered.start(0)) { 9391 _logger2.default.w(this.TAG, 'Playback seems stuck at ' + media.currentTime + ', seek to ' + buffered.start(0)); 9392 this._requestSetTime = true; 9393 this._mediaElement.currentTime = buffered.start(0); 9394 this._mediaElement.removeEventListener('progress', this.e.onvProgress); 9395 } 9396 } else { 9397 // Playback didn't stuck, remove progress event listener 9398 this._mediaElement.removeEventListener('progress', this.e.onvProgress); 9399 } 9400 } 9401 }, { 9402 key: '_onvLoadedMetadata', 9403 value: function _onvLoadedMetadata(e) { 9404 if (this._pendingSeekTime != null) { 9405 this._mediaElement.currentTime = this._pendingSeekTime; 9406 this._pendingSeekTime = null; 9407 } 9408 } 9409 }, { 9410 key: '_onvSeeking', 9411 value: function _onvSeeking(e) { 9412 // handle seeking request from browser's progress bar 9413 var target = this._mediaElement.currentTime; 9414 var buffered = this._mediaElement.buffered; 9415 9416 if (this._requestSetTime) { 9417 this._requestSetTime = false; 9418 return; 9419 } 9420 9421 if (target < 1.0 && buffered.length > 0) { 9422 // seek to video begin, set currentTime directly if beginPTS buffered 9423 var videoBeginTime = buffered.start(0); 9424 if (videoBeginTime < 1.0 && target < videoBeginTime || _browser2.default.safari) { 9425 this._requestSetTime = true; 9426 // also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid 9427 this._mediaElement.currentTime = _browser2.default.safari ? 0.1 : videoBeginTime; 9428 return; 9429 } 9430 } 9431 9432 if (this._isTimepointBuffered(target)) { 9433 if (this._alwaysSeekKeyframe) { 9434 var idr = this._msectl.getNearestKeyframe(Math.floor(target * 1000)); 9435 if (idr != null) { 9436 this._requestSetTime = true; 9437 this._mediaElement.currentTime = idr.dts / 1000; 9438 } 9439 } 9440 if (this._progressChecker != null) { 9441 this._checkProgressAndResume(); 9442 } 9443 return; 9444 } 9445 9446 this._seekpointRecord = { 9447 seekPoint: target, 9448 recordTime: this._now() 9449 }; 9450 window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50); 9451 } 9452 }, { 9453 key: '_onvCanPlay', 9454 value: function _onvCanPlay(e) { 9455 this._receivedCanPlay = true; 9456 this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay); 9457 } 9458 }, { 9459 key: '_onvStalled', 9460 value: function _onvStalled(e) { 9461 this._checkAndResumeStuckPlayback(true); 9462 } 9463 }, { 9464 key: '_onvProgress', 9465 value: function _onvProgress(e) { 9466 this._checkAndResumeStuckPlayback(); 9467 } 9468 }, { 9469 key: 'type', 9470 get: function get() { 9471 return this._type; 9472 } 9473 }, { 9474 key: 'buffered', 9475 get: function get() { 9476 return this._mediaElement.buffered; 9477 } 9478 }, { 9479 key: 'duration', 9480 get: function get() { 9481 return this._mediaElement.duration; 9482 } 9483 }, { 9484 key: 'volume', 9485 get: function get() { 9486 return this._mediaElement.volume; 9487 }, 9488 set: function set(value) { 9489 this._mediaElement.volume = value; 9490 } 9491 }, { 9492 key: 'muted', 9493 get: function get() { 9494 return this._mediaElement.muted; 9495 }, 9496 set: function set(muted) { 9497 this._mediaElement.muted = muted; 9498 } 9499 }, { 9500 key: 'currentTime', 9501 get: function get() { 9502 if (this._mediaElement) { 9503 return this._mediaElement.currentTime; 9504 } 9505 return 0; 9506 }, 9507 set: function set(seconds) { 9508 if (this._mediaElement) { 9509 this._internalSeek(seconds); 9510 } else { 9511 this._pendingSeekTime = seconds; 9512 } 9513 } 9514 }, { 9515 key: 'mediaInfo', 9516 get: function get() { 9517 return Object.assign({}, this._mediaInfo); 9518 } 9519 }, { 9520 key: 'statisticsInfo', 9521 get: function get() { 9522 if (this._statisticsInfo == null) { 9523 this._statisticsInfo = {}; 9524 } 9525 this._statisticsInfo = this._fillStatisticsInfo(this._statisticsInfo); 9526 return Object.assign({}, this._statisticsInfo); 9527 } 9528 }]); 9529 9530 return FlvPlayer; 9531 }(); 9532 9533 exports.default = FlvPlayer; 9534 9535 },{"../config.js":5,"../core/mse-controller.js":9,"../core/mse-events.js":10,"../core/transmuxer.js":11,"../core/transmuxing-events.js":13,"../utils/browser.js":39,"../utils/exception.js":40,"../utils/logger.js":41,"./player-errors.js":34,"./player-events.js":35,"events":2}],33:[function(_dereq_,module,exports){ 9536 'use strict'; 9537 9538 Object.defineProperty(exports, "__esModule", { 9539 value: true 9540 }); 9541 9542 var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; 9543 9544 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 9545 * Copyright (C) 2016 Bilibili. All Rights Reserved. 9546 * 9547 * @author zheng qian <xqq@xqq.im> 9548 * 9549 * Licensed under the Apache License, Version 2.0 (the "License"); 9550 * you may not use this file except in compliance with the License. 9551 * You may obtain a copy of the License at 9552 * 9553 * http://www.apache.org/licenses/LICENSE-2.0 9554 * 9555 * Unless required by applicable law or agreed to in writing, software 9556 * distributed under the License is distributed on an "AS IS" BASIS, 9557 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9558 * See the License for the specific language governing permissions and 9559 * limitations under the License. 9560 */ 9561 9562 var _events = _dereq_('events'); 9563 9564 var _events2 = _interopRequireDefault(_events); 9565 9566 var _playerEvents = _dereq_('./player-events.js'); 9567 9568 var _playerEvents2 = _interopRequireDefault(_playerEvents); 9569 9570 var _config = _dereq_('../config.js'); 9571 9572 var _exception = _dereq_('../utils/exception.js'); 9573 9574 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 9575 9576 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 9577 9578 // Player wrapper for browser's native player (HTMLVideoElement) without MediaSource src. 9579 var NativePlayer = function () { 9580 function NativePlayer(mediaDataSource, config) { 9581 _classCallCheck(this, NativePlayer); 9582 9583 this.TAG = 'NativePlayer'; 9584 this._type = 'NativePlayer'; 9585 this._emitter = new _events2.default(); 9586 9587 this._config = (0, _config.createDefaultConfig)(); 9588 if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') { 9589 Object.assign(this._config, config); 9590 } 9591 9592 if (mediaDataSource.type.toLowerCase() === 'flv') { 9593 throw new _exception.InvalidArgumentException('NativePlayer does\'t support flv MediaDataSource input!'); 9594 } 9595 if (mediaDataSource.hasOwnProperty('segments')) { 9596 throw new _exception.InvalidArgumentException('NativePlayer(' + mediaDataSource.type + ') doesn\'t support multipart playback!'); 9597 } 9598 9599 this.e = { 9600 onvLoadedMetadata: this._onvLoadedMetadata.bind(this) 9601 }; 9602 9603 this._pendingSeekTime = null; 9604 this._statisticsReporter = null; 9605 9606 this._mediaDataSource = mediaDataSource; 9607 this._mediaElement = null; 9608 } 9609 9610 _createClass(NativePlayer, [{ 9611 key: 'destroy', 9612 value: function destroy() { 9613 if (this._mediaElement) { 9614 this.unload(); 9615 this.detachMediaElement(); 9616 } 9617 this.e = null; 9618 this._mediaDataSource = null; 9619 this._emitter.removeAllListeners(); 9620 this._emitter = null; 9621 } 9622 }, { 9623 key: 'on', 9624 value: function on(event, listener) { 9625 var _this = this; 9626 9627 if (event === _playerEvents2.default.MEDIA_INFO) { 9628 if (this._mediaElement != null && this._mediaElement.readyState !== 0) { 9629 // HAVE_NOTHING 9630 Promise.resolve().then(function () { 9631 _this._emitter.emit(_playerEvents2.default.MEDIA_INFO, _this.mediaInfo); 9632 }); 9633 } 9634 } else if (event === _playerEvents2.default.STATISTICS_INFO) { 9635 if (this._mediaElement != null && this._mediaElement.readyState !== 0) { 9636 Promise.resolve().then(function () { 9637 _this._emitter.emit(_playerEvents2.default.STATISTICS_INFO, _this.statisticsInfo); 9638 }); 9639 } 9640 } 9641 this._emitter.addListener(event, listener); 9642 } 9643 }, { 9644 key: 'off', 9645 value: function off(event, listener) { 9646 this._emitter.removeListener(event, listener); 9647 } 9648 }, { 9649 key: 'attachMediaElement', 9650 value: function attachMediaElement(mediaElement) { 9651 this._mediaElement = mediaElement; 9652 mediaElement.addEventListener('loadedmetadata', this.e.onvLoadedMetadata); 9653 9654 if (this._pendingSeekTime != null) { 9655 try { 9656 mediaElement.currentTime = this._pendingSeekTime; 9657 this._pendingSeekTime = null; 9658 } catch (e) { 9659 // IE11 may throw InvalidStateError if readyState === 0 9660 // Defer set currentTime operation after loadedmetadata 9661 } 9662 } 9663 } 9664 }, { 9665 key: 'detachMediaElement', 9666 value: function detachMediaElement() { 9667 if (this._mediaElement) { 9668 this._mediaElement.src = ''; 9669 this._mediaElement.removeAttribute('src'); 9670 this._mediaElement.removeEventListener('loadedmetadata', this.e.onvLoadedMetadata); 9671 this._mediaElement = null; 9672 } 9673 if (this._statisticsReporter != null) { 9674 window.clearInterval(this._statisticsReporter); 9675 this._statisticsReporter = null; 9676 } 9677 } 9678 }, { 9679 key: 'load', 9680 value: function load() { 9681 if (!this._mediaElement) { 9682 throw new _exception.IllegalStateException('HTMLMediaElement must be attached before load()!'); 9683 } 9684 this._mediaElement.src = this._mediaDataSource.url; 9685 9686 if (this._mediaElement.readyState > 0) { 9687 this._mediaElement.currentTime = 0; 9688 } 9689 9690 this._mediaElement.preload = 'auto'; 9691 this._mediaElement.load(); 9692 this._statisticsReporter = window.setInterval(this._reportStatisticsInfo.bind(this), this._config.statisticsInfoReportInterval); 9693 } 9694 }, { 9695 key: 'unload', 9696 value: function unload() { 9697 if (this._mediaElement) { 9698 this._mediaElement.src = ''; 9699 this._mediaElement.removeAttribute('src'); 9700 } 9701 if (this._statisticsReporter != null) { 9702 window.clearInterval(this._statisticsReporter); 9703 this._statisticsReporter = null; 9704 } 9705 } 9706 }, { 9707 key: 'play', 9708 value: function play() { 9709 return this._mediaElement.play(); 9710 } 9711 }, { 9712 key: 'pause', 9713 value: function pause() { 9714 this._mediaElement.pause(); 9715 } 9716 }, { 9717 key: '_onvLoadedMetadata', 9718 value: function _onvLoadedMetadata(e) { 9719 if (this._pendingSeekTime != null) { 9720 this._mediaElement.currentTime = this._pendingSeekTime; 9721 this._pendingSeekTime = null; 9722 } 9723 this._emitter.emit(_playerEvents2.default.MEDIA_INFO, this.mediaInfo); 9724 } 9725 }, { 9726 key: '_reportStatisticsInfo', 9727 value: function _reportStatisticsInfo() { 9728 this._emitter.emit(_playerEvents2.default.STATISTICS_INFO, this.statisticsInfo); 9729 } 9730 }, { 9731 key: 'type', 9732 get: function get() { 9733 return this._type; 9734 } 9735 }, { 9736 key: 'buffered', 9737 get: function get() { 9738 return this._mediaElement.buffered; 9739 } 9740 }, { 9741 key: 'duration', 9742 get: function get() { 9743 return this._mediaElement.duration; 9744 } 9745 }, { 9746 key: 'volume', 9747 get: function get() { 9748 return this._mediaElement.volume; 9749 }, 9750 set: function set(value) { 9751 this._mediaElement.volume = value; 9752 } 9753 }, { 9754 key: 'muted', 9755 get: function get() { 9756 return this._mediaElement.muted; 9757 }, 9758 set: function set(muted) { 9759 this._mediaElement.muted = muted; 9760 } 9761 }, { 9762 key: 'currentTime', 9763 get: function get() { 9764 if (this._mediaElement) { 9765 return this._mediaElement.currentTime; 9766 } 9767 return 0; 9768 }, 9769 set: function set(seconds) { 9770 if (this._mediaElement) { 9771 this._mediaElement.currentTime = seconds; 9772 } else { 9773 this._pendingSeekTime = seconds; 9774 } 9775 } 9776 }, { 9777 key: 'mediaInfo', 9778 get: function get() { 9779 var mediaPrefix = this._mediaElement instanceof HTMLAudioElement ? 'audio/' : 'video/'; 9780 var info = { 9781 mimeType: mediaPrefix + this._mediaDataSource.type 9782 }; 9783 if (this._mediaElement) { 9784 info.duration = Math.floor(this._mediaElement.duration * 1000); 9785 if (this._mediaElement instanceof HTMLVideoElement) { 9786 info.width = this._mediaElement.videoWidth; 9787 info.height = this._mediaElement.videoHeight; 9788 } 9789 } 9790 return info; 9791 } 9792 }, { 9793 key: 'statisticsInfo', 9794 get: function get() { 9795 var info = { 9796 playerType: this._type, 9797 url: this._mediaDataSource.url 9798 }; 9799 9800 if (!(this._mediaElement instanceof HTMLVideoElement)) { 9801 return info; 9802 } 9803 9804 var hasQualityInfo = true; 9805 var decoded = 0; 9806 var dropped = 0; 9807 9808 if (this._mediaElement.getVideoPlaybackQuality) { 9809 var quality = this._mediaElement.getVideoPlaybackQuality(); 9810 decoded = quality.totalVideoFrames; 9811 dropped = quality.droppedVideoFrames; 9812 } else if (this._mediaElement.webkitDecodedFrameCount != undefined) { 9813 decoded = this._mediaElement.webkitDecodedFrameCount; 9814 dropped = this._mediaElement.webkitDroppedFrameCount; 9815 } else { 9816 hasQualityInfo = false; 9817 } 9818 9819 if (hasQualityInfo) { 9820 info.decodedFrames = decoded; 9821 info.droppedFrames = dropped; 9822 } 9823 9824 return info; 9825 } 9826 }]); 9827 9828 return NativePlayer; 9829 }(); 9830 9831 exports.default = NativePlayer; 9832 9833 },{"../config.js":5,"../utils/exception.js":40,"./player-events.js":35,"events":2}],34:[function(_dereq_,module,exports){ 9834 'use strict'; 9835 9836 Object.defineProperty(exports, "__esModule", { 9837 value: true 9838 }); 9839 exports.ErrorDetails = exports.ErrorTypes = undefined; 9840 9841 var _loader = _dereq_('../io/loader.js'); 9842 9843 var _demuxErrors = _dereq_('../demux/demux-errors.js'); 9844 9845 var _demuxErrors2 = _interopRequireDefault(_demuxErrors); 9846 9847 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 9848 9849 /* 9850 * Copyright (C) 2016 Bilibili. All Rights Reserved. 9851 * 9852 * @author zheng qian <xqq@xqq.im> 9853 * 9854 * Licensed under the Apache License, Version 2.0 (the "License"); 9855 * you may not use this file except in compliance with the License. 9856 * You may obtain a copy of the License at 9857 * 9858 * http://www.apache.org/licenses/LICENSE-2.0 9859 * 9860 * Unless required by applicable law or agreed to in writing, software 9861 * distributed under the License is distributed on an "AS IS" BASIS, 9862 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9863 * See the License for the specific language governing permissions and 9864 * limitations under the License. 9865 */ 9866 9867 var ErrorTypes = exports.ErrorTypes = { 9868 NETWORK_ERROR: 'NetworkError', 9869 MEDIA_ERROR: 'MediaError', 9870 OTHER_ERROR: 'OtherError' 9871 }; 9872 9873 var ErrorDetails = exports.ErrorDetails = { 9874 NETWORK_EXCEPTION: _loader.LoaderErrors.EXCEPTION, 9875 NETWORK_STATUS_CODE_INVALID: _loader.LoaderErrors.HTTP_STATUS_CODE_INVALID, 9876 NETWORK_TIMEOUT: _loader.LoaderErrors.CONNECTING_TIMEOUT, 9877 NETWORK_UNRECOVERABLE_EARLY_EOF: _loader.LoaderErrors.UNRECOVERABLE_EARLY_EOF, 9878 9879 MEDIA_MSE_ERROR: 'MediaMSEError', 9880 9881 MEDIA_FORMAT_ERROR: _demuxErrors2.default.FORMAT_ERROR, 9882 MEDIA_FORMAT_UNSUPPORTED: _demuxErrors2.default.FORMAT_UNSUPPORTED, 9883 MEDIA_CODEC_UNSUPPORTED: _demuxErrors2.default.CODEC_UNSUPPORTED 9884 }; 9885 9886 },{"../demux/demux-errors.js":16,"../io/loader.js":24}],35:[function(_dereq_,module,exports){ 9887 'use strict'; 9888 9889 Object.defineProperty(exports, "__esModule", { 9890 value: true 9891 }); 9892 /* 9893 * Copyright (C) 2016 Bilibili. All Rights Reserved. 9894 * 9895 * @author zheng qian <xqq@xqq.im> 9896 * 9897 * Licensed under the Apache License, Version 2.0 (the "License"); 9898 * you may not use this file except in compliance with the License. 9899 * You may obtain a copy of the License at 9900 * 9901 * http://www.apache.org/licenses/LICENSE-2.0 9902 * 9903 * Unless required by applicable law or agreed to in writing, software 9904 * distributed under the License is distributed on an "AS IS" BASIS, 9905 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9906 * See the License for the specific language governing permissions and 9907 * limitations under the License. 9908 */ 9909 9910 var PlayerEvents = { 9911 ERROR: 'error', 9912 LOADING_COMPLETE: 'loading_complete', 9913 RECOVERED_EARLY_EOF: 'recovered_early_eof', 9914 MEDIA_INFO: 'media_info', 9915 METADATA_ARRIVED: 'metadata_arrived', 9916 SCRIPTDATA_ARRIVED: 'scriptdata_arrived', 9917 STATISTICS_INFO: 'statistics_info' 9918 }; 9919 9920 exports.default = PlayerEvents; 9921 9922 },{}],36:[function(_dereq_,module,exports){ 9923 'use strict'; 9924 9925 Object.defineProperty(exports, "__esModule", { 9926 value: true 9927 }); 9928 9929 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 9930 9931 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 9932 9933 /* 9934 * Copyright (C) 2016 Bilibili. All Rights Reserved. 9935 * 9936 * This file is modified from dailymotion's hls.js library (hls.js/src/helper/aac.js) 9937 * @author zheng qian <xqq@xqq.im> 9938 * 9939 * Licensed under the Apache License, Version 2.0 (the "License"); 9940 * you may not use this file except in compliance with the License. 9941 * You may obtain a copy of the License at 9942 * 9943 * http://www.apache.org/licenses/LICENSE-2.0 9944 * 9945 * Unless required by applicable law or agreed to in writing, software 9946 * distributed under the License is distributed on an "AS IS" BASIS, 9947 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 9948 * See the License for the specific language governing permissions and 9949 * limitations under the License. 9950 */ 9951 9952 var AAC = function () { 9953 function AAC() { 9954 _classCallCheck(this, AAC); 9955 } 9956 9957 _createClass(AAC, null, [{ 9958 key: 'getSilentFrame', 9959 value: function getSilentFrame(codec, channelCount) { 9960 if (codec === 'mp4a.40.2') { 9961 // handle LC-AAC 9962 if (channelCount === 1) { 9963 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]); 9964 } else if (channelCount === 2) { 9965 return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]); 9966 } else if (channelCount === 3) { 9967 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]); 9968 } else if (channelCount === 4) { 9969 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]); 9970 } else if (channelCount === 5) { 9971 return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]); 9972 } else if (channelCount === 6) { 9973 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]); 9974 } 9975 } else { 9976 // handle HE-AAC (mp4a.40.5 / mp4a.40.29) 9977 if (channelCount === 1) { 9978 // 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 9979 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]); 9980 } else if (channelCount === 2) { 9981 // 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 9982 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]); 9983 } else if (channelCount === 3) { 9984 // 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 9985 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]); 9986 } 9987 } 9988 return null; 9989 } 9990 }]); 9991 9992 return AAC; 9993 }(); 9994 9995 exports.default = AAC; 9996 9997 },{}],37:[function(_dereq_,module,exports){ 9998 'use strict'; 9999 10000 Object.defineProperty(exports, "__esModule", { 10001 value: true 10002 }); 10003 10004 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 10005 10006 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 10007 10008 /* 10009 * Copyright (C) 2016 Bilibili. All Rights Reserved. 10010 * 10011 * This file is derived from dailymotion's hls.js library (hls.js/src/remux/mp4-generator.js) 10012 * @author zheng qian <xqq@xqq.im> 10013 * 10014 * Licensed under the Apache License, Version 2.0 (the "License"); 10015 * you may not use this file except in compliance with the License. 10016 * You may obtain a copy of the License at 10017 * 10018 * http://www.apache.org/licenses/LICENSE-2.0 10019 * 10020 * Unless required by applicable law or agreed to in writing, software 10021 * distributed under the License is distributed on an "AS IS" BASIS, 10022 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10023 * See the License for the specific language governing permissions and 10024 * limitations under the License. 10025 */ 10026 10027 // MP4 boxes generator for ISO BMFF (ISO Base Media File Format, defined in ISO/IEC 14496-12) 10028 var MP4 = function () { 10029 function MP4() { 10030 _classCallCheck(this, MP4); 10031 } 10032 10033 _createClass(MP4, null, [{ 10034 key: 'init', 10035 value: function init() { 10036 MP4.types = { 10037 avc1: [], avcC: [], btrt: [], dinf: [], 10038 dref: [], esds: [], ftyp: [], hdlr: [], 10039 mdat: [], mdhd: [], mdia: [], mfhd: [], 10040 minf: [], moof: [], moov: [], mp4a: [], 10041 mvex: [], mvhd: [], sdtp: [], stbl: [], 10042 stco: [], stsc: [], stsd: [], stsz: [], 10043 stts: [], tfdt: [], tfhd: [], traf: [], 10044 trak: [], trun: [], trex: [], tkhd: [], 10045 vmhd: [], smhd: [], '.mp3': [] 10046 }; 10047 10048 for (var name in MP4.types) { 10049 if (MP4.types.hasOwnProperty(name)) { 10050 MP4.types[name] = [name.charCodeAt(0), name.charCodeAt(1), name.charCodeAt(2), name.charCodeAt(3)]; 10051 } 10052 } 10053 10054 var constants = MP4.constants = {}; 10055 10056 constants.FTYP = new Uint8Array([0x69, 0x73, 0x6F, 0x6D, // major_brand: isom 10057 0x0, 0x0, 0x0, 0x1, // minor_version: 0x01 10058 0x69, 0x73, 0x6F, 0x6D, // isom 10059 0x61, 0x76, 0x63, 0x31 // avc1 10060 ]); 10061 10062 constants.STSD_PREFIX = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 10063 0x00, 0x00, 0x00, 0x01 // entry_count 10064 ]); 10065 10066 constants.STTS = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 10067 0x00, 0x00, 0x00, 0x00 // entry_count 10068 ]); 10069 10070 constants.STSC = constants.STCO = constants.STTS; 10071 10072 constants.STSZ = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 10073 0x00, 0x00, 0x00, 0x00, // sample_size 10074 0x00, 0x00, 0x00, 0x00 // sample_count 10075 ]); 10076 10077 constants.HDLR_VIDEO = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 10078 0x00, 0x00, 0x00, 0x00, // pre_defined 10079 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide' 10080 0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes 10081 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x00 // name: VideoHandler 10082 ]); 10083 10084 constants.HDLR_AUDIO = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 10085 0x00, 0x00, 0x00, 0x00, // pre_defined 10086 0x73, 0x6F, 0x75, 0x6E, // handler_type: 'soun' 10087 0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes 10088 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x6F, 0x75, 0x6E, 0x64, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x00 // name: SoundHandler 10089 ]); 10090 10091 constants.DREF = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 10092 0x00, 0x00, 0x00, 0x01, // entry_count 10093 0x00, 0x00, 0x00, 0x0C, // entry_size 10094 0x75, 0x72, 0x6C, 0x20, // type 'url ' 10095 0x00, 0x00, 0x00, 0x01 // version(0) + flags 10096 ]); 10097 10098 // Sound media header 10099 constants.SMHD = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 10100 0x00, 0x00, 0x00, 0x00 // balance(2) + reserved(2) 10101 ]); 10102 10103 // video media header 10104 constants.VMHD = new Uint8Array([0x00, 0x00, 0x00, 0x01, // version(0) + flags 10105 0x00, 0x00, // graphicsmode: 2 bytes 10106 0x00, 0x00, 0x00, 0x00, // opcolor: 3 * 2 bytes 10107 0x00, 0x00]); 10108 } 10109 10110 // Generate a box 10111 10112 }, { 10113 key: 'box', 10114 value: function box(type) { 10115 var size = 8; 10116 var result = null; 10117 var datas = Array.prototype.slice.call(arguments, 1); 10118 var arrayCount = datas.length; 10119 10120 for (var i = 0; i < arrayCount; i++) { 10121 size += datas[i].byteLength; 10122 } 10123 10124 result = new Uint8Array(size); 10125 result[0] = size >>> 24 & 0xFF; // size 10126 result[1] = size >>> 16 & 0xFF; 10127 result[2] = size >>> 8 & 0xFF; 10128 result[3] = size & 0xFF; 10129 10130 result.set(type, 4); // type 10131 10132 var offset = 8; 10133 for (var _i = 0; _i < arrayCount; _i++) { 10134 // data body 10135 result.set(datas[_i], offset); 10136 offset += datas[_i].byteLength; 10137 } 10138 10139 return result; 10140 } 10141 10142 // emit ftyp & moov 10143 10144 }, { 10145 key: 'generateInitSegment', 10146 value: function generateInitSegment(meta) { 10147 var ftyp = MP4.box(MP4.types.ftyp, MP4.constants.FTYP); 10148 var moov = MP4.moov(meta); 10149 10150 var result = new Uint8Array(ftyp.byteLength + moov.byteLength); 10151 result.set(ftyp, 0); 10152 result.set(moov, ftyp.byteLength); 10153 return result; 10154 } 10155 10156 // Movie metadata box 10157 10158 }, { 10159 key: 'moov', 10160 value: function moov(meta) { 10161 var mvhd = MP4.mvhd(meta.timescale, meta.duration); 10162 var trak = MP4.trak(meta); 10163 var mvex = MP4.mvex(meta); 10164 return MP4.box(MP4.types.moov, mvhd, trak, mvex); 10165 } 10166 10167 // Movie header box 10168 10169 }, { 10170 key: 'mvhd', 10171 value: function mvhd(timescale, duration) { 10172 return MP4.box(MP4.types.mvhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 10173 0x00, 0x00, 0x00, 0x00, // creation_time 10174 0x00, 0x00, 0x00, 0x00, // modification_time 10175 timescale >>> 24 & 0xFF, // timescale: 4 bytes 10176 timescale >>> 16 & 0xFF, timescale >>> 8 & 0xFF, timescale & 0xFF, duration >>> 24 & 0xFF, // duration: 4 bytes 10177 duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, 0x00, 0x01, 0x00, 0x00, // Preferred rate: 1.0 10178 0x01, 0x00, 0x00, 0x00, // PreferredVolume(1.0, 2bytes) + reserved(2bytes) 10179 0x00, 0x00, 0x00, 0x00, // reserved: 4 + 4 bytes 10180 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // ----begin composition matrix---- 10181 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // ----end composition matrix---- 10182 0x00, 0x00, 0x00, 0x00, // ----begin pre_defined 6 * 4 bytes---- 10183 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // ----end pre_defined 6 * 4 bytes---- 10184 0xFF, 0xFF, 0xFF, 0xFF // next_track_ID 10185 ])); 10186 } 10187 10188 // Track box 10189 10190 }, { 10191 key: 'trak', 10192 value: function trak(meta) { 10193 return MP4.box(MP4.types.trak, MP4.tkhd(meta), MP4.mdia(meta)); 10194 } 10195 10196 // Track header box 10197 10198 }, { 10199 key: 'tkhd', 10200 value: function tkhd(meta) { 10201 var trackId = meta.id, 10202 duration = meta.duration; 10203 var width = meta.presentWidth, 10204 height = meta.presentHeight; 10205 10206 return MP4.box(MP4.types.tkhd, new Uint8Array([0x00, 0x00, 0x00, 0x07, // version(0) + flags 10207 0x00, 0x00, 0x00, 0x00, // creation_time 10208 0x00, 0x00, 0x00, 0x00, // modification_time 10209 trackId >>> 24 & 0xFF, // track_ID: 4 bytes 10210 trackId >>> 16 & 0xFF, trackId >>> 8 & 0xFF, trackId & 0xFF, 0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes 10211 duration >>> 24 & 0xFF, // duration: 4 bytes 10212 duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes 10213 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // layer(2bytes) + alternate_group(2bytes) 10214 0x00, 0x00, 0x00, 0x00, // volume(2bytes) + reserved(2bytes) 10215 0x00, 0x01, 0x00, 0x00, // ----begin composition matrix---- 10216 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, // ----end composition matrix---- 10217 width >>> 8 & 0xFF, // width and height 10218 width & 0xFF, 0x00, 0x00, height >>> 8 & 0xFF, height & 0xFF, 0x00, 0x00])); 10219 } 10220 10221 // Media Box 10222 10223 }, { 10224 key: 'mdia', 10225 value: function mdia(meta) { 10226 return MP4.box(MP4.types.mdia, MP4.mdhd(meta), MP4.hdlr(meta), MP4.minf(meta)); 10227 } 10228 10229 // Media header box 10230 10231 }, { 10232 key: 'mdhd', 10233 value: function mdhd(meta) { 10234 var timescale = meta.timescale; 10235 var duration = meta.duration; 10236 return MP4.box(MP4.types.mdhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 10237 0x00, 0x00, 0x00, 0x00, // creation_time 10238 0x00, 0x00, 0x00, 0x00, // modification_time 10239 timescale >>> 24 & 0xFF, // timescale: 4 bytes 10240 timescale >>> 16 & 0xFF, timescale >>> 8 & 0xFF, timescale & 0xFF, duration >>> 24 & 0xFF, // duration: 4 bytes 10241 duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, 0x55, 0xC4, // language: und (undetermined) 10242 0x00, 0x00 // pre_defined = 0 10243 ])); 10244 } 10245 10246 // Media handler reference box 10247 10248 }, { 10249 key: 'hdlr', 10250 value: function hdlr(meta) { 10251 var data = null; 10252 if (meta.type === 'audio') { 10253 data = MP4.constants.HDLR_AUDIO; 10254 } else { 10255 data = MP4.constants.HDLR_VIDEO; 10256 } 10257 return MP4.box(MP4.types.hdlr, data); 10258 } 10259 10260 // Media infomation box 10261 10262 }, { 10263 key: 'minf', 10264 value: function minf(meta) { 10265 var xmhd = null; 10266 if (meta.type === 'audio') { 10267 xmhd = MP4.box(MP4.types.smhd, MP4.constants.SMHD); 10268 } else { 10269 xmhd = MP4.box(MP4.types.vmhd, MP4.constants.VMHD); 10270 } 10271 return MP4.box(MP4.types.minf, xmhd, MP4.dinf(), MP4.stbl(meta)); 10272 } 10273 10274 // Data infomation box 10275 10276 }, { 10277 key: 'dinf', 10278 value: function dinf() { 10279 var result = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, MP4.constants.DREF)); 10280 return result; 10281 } 10282 10283 // Sample table box 10284 10285 }, { 10286 key: 'stbl', 10287 value: function stbl(meta) { 10288 var result = MP4.box(MP4.types.stbl, // type: stbl 10289 MP4.stsd(meta), // Sample Description Table 10290 MP4.box(MP4.types.stts, MP4.constants.STTS), // Time-To-Sample 10291 MP4.box(MP4.types.stsc, MP4.constants.STSC), // Sample-To-Chunk 10292 MP4.box(MP4.types.stsz, MP4.constants.STSZ), // Sample size 10293 MP4.box(MP4.types.stco, MP4.constants.STCO) // Chunk offset 10294 ); 10295 return result; 10296 } 10297 10298 // Sample description box 10299 10300 }, { 10301 key: 'stsd', 10302 value: function stsd(meta) { 10303 if (meta.type === 'audio') { 10304 if (meta.codec === 'mp3') { 10305 return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp3(meta)); 10306 } 10307 // else: aac -> mp4a 10308 return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp4a(meta)); 10309 } else { 10310 return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.avc1(meta)); 10311 } 10312 } 10313 }, { 10314 key: 'mp3', 10315 value: function mp3(meta) { 10316 var channelCount = meta.channelCount; 10317 var sampleRate = meta.audioSampleRate; 10318 10319 var data = new Uint8Array([0x00, 0x00, 0x00, 0x00, // reserved(4) 10320 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2) 10321 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes 10322 0x00, 0x00, 0x00, 0x00, 0x00, channelCount, // channelCount(2) 10323 0x00, 0x10, // sampleSize(2) 10324 0x00, 0x00, 0x00, 0x00, // reserved(4) 10325 sampleRate >>> 8 & 0xFF, // Audio sample rate 10326 sampleRate & 0xFF, 0x00, 0x00]); 10327 10328 return MP4.box(MP4.types['.mp3'], data); 10329 } 10330 }, { 10331 key: 'mp4a', 10332 value: function mp4a(meta) { 10333 var channelCount = meta.channelCount; 10334 var sampleRate = meta.audioSampleRate; 10335 10336 var data = new Uint8Array([0x00, 0x00, 0x00, 0x00, // reserved(4) 10337 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2) 10338 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes 10339 0x00, 0x00, 0x00, 0x00, 0x00, channelCount, // channelCount(2) 10340 0x00, 0x10, // sampleSize(2) 10341 0x00, 0x00, 0x00, 0x00, // reserved(4) 10342 sampleRate >>> 8 & 0xFF, // Audio sample rate 10343 sampleRate & 0xFF, 0x00, 0x00]); 10344 10345 return MP4.box(MP4.types.mp4a, data, MP4.esds(meta)); 10346 } 10347 }, { 10348 key: 'esds', 10349 value: function esds(meta) { 10350 var config = meta.config || []; 10351 var configSize = config.length; 10352 var data = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version 0 + flags 10353 10354 0x03, // descriptor_type 10355 0x17 + configSize, // length3 10356 0x00, 0x01, // es_id 10357 0x00, // stream_priority 10358 10359 0x04, // descriptor_type 10360 0x0F + configSize, // length 10361 0x40, // codec: mpeg4_audio 10362 0x15, // stream_type: Audio 10363 0x00, 0x00, 0x00, // buffer_size 10364 0x00, 0x00, 0x00, 0x00, // maxBitrate 10365 0x00, 0x00, 0x00, 0x00, // avgBitrate 10366 10367 0x05 // descriptor_type 10368 ].concat([configSize]).concat(config).concat([0x06, 0x01, 0x02 // GASpecificConfig 10369 ])); 10370 return MP4.box(MP4.types.esds, data); 10371 } 10372 }, { 10373 key: 'avc1', 10374 value: function avc1(meta) { 10375 var avcc = meta.avcc; 10376 var width = meta.codecWidth, 10377 height = meta.codecHeight; 10378 10379 var data = new Uint8Array([0x00, 0x00, 0x00, 0x00, // reserved(4) 10380 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2) 10381 0x00, 0x00, 0x00, 0x00, // pre_defined(2) + reserved(2) 10382 0x00, 0x00, 0x00, 0x00, // pre_defined: 3 * 4 bytes 10383 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, width >>> 8 & 0xFF, // width: 2 bytes 10384 width & 0xFF, height >>> 8 & 0xFF, // height: 2 bytes 10385 height & 0xFF, 0x00, 0x48, 0x00, 0x00, // horizresolution: 4 bytes 10386 0x00, 0x48, 0x00, 0x00, // vertresolution: 4 bytes 10387 0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes 10388 0x00, 0x01, // frame_count 10389 0x0A, // strlen 10390 0x78, 0x71, 0x71, 0x2F, // compressorname: 32 bytes 10391 0x66, 0x6C, 0x76, 0x2E, 0x6A, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, // depth 10392 0xFF, 0xFF // pre_defined = -1 10393 ]); 10394 return MP4.box(MP4.types.avc1, data, MP4.box(MP4.types.avcC, avcc)); 10395 } 10396 10397 // Movie Extends box 10398 10399 }, { 10400 key: 'mvex', 10401 value: function mvex(meta) { 10402 return MP4.box(MP4.types.mvex, MP4.trex(meta)); 10403 } 10404 10405 // Track Extends box 10406 10407 }, { 10408 key: 'trex', 10409 value: function trex(meta) { 10410 var trackId = meta.id; 10411 var data = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags 10412 trackId >>> 24 & 0xFF, // track_ID 10413 trackId >>> 16 & 0xFF, trackId >>> 8 & 0xFF, trackId & 0xFF, 0x00, 0x00, 0x00, 0x01, // default_sample_description_index 10414 0x00, 0x00, 0x00, 0x00, // default_sample_duration 10415 0x00, 0x00, 0x00, 0x00, // default_sample_size 10416 0x00, 0x01, 0x00, 0x01 // default_sample_flags 10417 ]); 10418 return MP4.box(MP4.types.trex, data); 10419 } 10420 10421 // Movie fragment box 10422 10423 }, { 10424 key: 'moof', 10425 value: function moof(track, baseMediaDecodeTime) { 10426 return MP4.box(MP4.types.moof, MP4.mfhd(track.sequenceNumber), MP4.traf(track, baseMediaDecodeTime)); 10427 } 10428 }, { 10429 key: 'mfhd', 10430 value: function mfhd(sequenceNumber) { 10431 var data = new Uint8Array([0x00, 0x00, 0x00, 0x00, sequenceNumber >>> 24 & 0xFF, // sequence_number: int32 10432 sequenceNumber >>> 16 & 0xFF, sequenceNumber >>> 8 & 0xFF, sequenceNumber & 0xFF]); 10433 return MP4.box(MP4.types.mfhd, data); 10434 } 10435 10436 // Track fragment box 10437 10438 }, { 10439 key: 'traf', 10440 value: function traf(track, baseMediaDecodeTime) { 10441 var trackId = track.id; 10442 10443 // Track fragment header box 10444 var tfhd = MP4.box(MP4.types.tfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) & flags 10445 trackId >>> 24 & 0xFF, // track_ID 10446 trackId >>> 16 & 0xFF, trackId >>> 8 & 0xFF, trackId & 0xFF])); 10447 // Track Fragment Decode Time 10448 var tfdt = MP4.box(MP4.types.tfdt, new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) & flags 10449 baseMediaDecodeTime >>> 24 & 0xFF, // baseMediaDecodeTime: int32 10450 baseMediaDecodeTime >>> 16 & 0xFF, baseMediaDecodeTime >>> 8 & 0xFF, baseMediaDecodeTime & 0xFF])); 10451 var sdtp = MP4.sdtp(track); 10452 var trun = MP4.trun(track, sdtp.byteLength + 16 + 16 + 8 + 16 + 8 + 8); 10453 10454 return MP4.box(MP4.types.traf, tfhd, tfdt, trun, sdtp); 10455 } 10456 10457 // Sample Dependency Type box 10458 10459 }, { 10460 key: 'sdtp', 10461 value: function sdtp(track) { 10462 var samples = track.samples || []; 10463 var sampleCount = samples.length; 10464 var data = new Uint8Array(4 + sampleCount); 10465 // 0~4 bytes: version(0) & flags 10466 for (var i = 0; i < sampleCount; i++) { 10467 var flags = samples[i].flags; 10468 data[i + 4] = flags.isLeading << 6 | // is_leading: 2 (bit) 10469 flags.dependsOn << 4 // sample_depends_on 10470 | flags.isDependedOn << 2 // sample_is_depended_on 10471 | flags.hasRedundancy; // sample_has_redundancy 10472 } 10473 return MP4.box(MP4.types.sdtp, data); 10474 } 10475 10476 // Track fragment run box 10477 10478 }, { 10479 key: 'trun', 10480 value: function trun(track, offset) { 10481 var samples = track.samples || []; 10482 var sampleCount = samples.length; 10483 var dataSize = 12 + 16 * sampleCount; 10484 var data = new Uint8Array(dataSize); 10485 offset += 8 + dataSize; 10486 10487 data.set([0x00, 0x00, 0x0F, 0x01, // version(0) & flags 10488 sampleCount >>> 24 & 0xFF, // sample_count 10489 sampleCount >>> 16 & 0xFF, sampleCount >>> 8 & 0xFF, sampleCount & 0xFF, offset >>> 24 & 0xFF, // data_offset 10490 offset >>> 16 & 0xFF, offset >>> 8 & 0xFF, offset & 0xFF], 0); 10491 10492 for (var i = 0; i < sampleCount; i++) { 10493 var duration = samples[i].duration; 10494 var size = samples[i].size; 10495 var flags = samples[i].flags; 10496 var cts = samples[i].cts; 10497 data.set([duration >>> 24 & 0xFF, // sample_duration 10498 duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, size >>> 24 & 0xFF, // sample_size 10499 size >>> 16 & 0xFF, size >>> 8 & 0xFF, size & 0xFF, flags.isLeading << 2 | flags.dependsOn, // sample_flags 10500 flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.isNonSync, 0x00, 0x00, // sample_degradation_priority 10501 cts >>> 24 & 0xFF, // sample_composition_time_offset 10502 cts >>> 16 & 0xFF, cts >>> 8 & 0xFF, cts & 0xFF], 12 + 16 * i); 10503 } 10504 return MP4.box(MP4.types.trun, data); 10505 } 10506 }, { 10507 key: 'mdat', 10508 value: function mdat(data) { 10509 return MP4.box(MP4.types.mdat, data); 10510 } 10511 }]); 10512 10513 return MP4; 10514 }(); 10515 10516 MP4.init(); 10517 10518 exports.default = MP4; 10519 10520 },{}],38:[function(_dereq_,module,exports){ 10521 'use strict'; 10522 10523 Object.defineProperty(exports, "__esModule", { 10524 value: true 10525 }); 10526 10527 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 10528 * Copyright (C) 2016 Bilibili. All Rights Reserved. 10529 * 10530 * @author zheng qian <xqq@xqq.im> 10531 * 10532 * Licensed under the Apache License, Version 2.0 (the "License"); 10533 * you may not use this file except in compliance with the License. 10534 * You may obtain a copy of the License at 10535 * 10536 * http://www.apache.org/licenses/LICENSE-2.0 10537 * 10538 * Unless required by applicable law or agreed to in writing, software 10539 * distributed under the License is distributed on an "AS IS" BASIS, 10540 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10541 * See the License for the specific language governing permissions and 10542 * limitations under the License. 10543 */ 10544 10545 var _logger = _dereq_('../utils/logger.js'); 10546 10547 var _logger2 = _interopRequireDefault(_logger); 10548 10549 var _mp4Generator = _dereq_('./mp4-generator.js'); 10550 10551 var _mp4Generator2 = _interopRequireDefault(_mp4Generator); 10552 10553 var _aacSilent = _dereq_('./aac-silent.js'); 10554 10555 var _aacSilent2 = _interopRequireDefault(_aacSilent); 10556 10557 var _browser = _dereq_('../utils/browser.js'); 10558 10559 var _browser2 = _interopRequireDefault(_browser); 10560 10561 var _mediaSegmentInfo = _dereq_('../core/media-segment-info.js'); 10562 10563 var _exception = _dereq_('../utils/exception.js'); 10564 10565 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 10566 10567 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 10568 10569 // Fragmented mp4 remuxer 10570 var MP4Remuxer = function () { 10571 function MP4Remuxer(config) { 10572 _classCallCheck(this, MP4Remuxer); 10573 10574 this.TAG = 'MP4Remuxer'; 10575 10576 this._config = config; 10577 this._isLive = config.isLive === true ? true : false; 10578 10579 this._dtsBase = -1; 10580 this._dtsBaseInited = false; 10581 this._audioDtsBase = Infinity; 10582 this._videoDtsBase = Infinity; 10583 this._audioNextDts = undefined; 10584 this._videoNextDts = undefined; 10585 this._audioStashedLastSample = null; 10586 this._videoStashedLastSample = null; 10587 10588 this._audioMeta = null; 10589 this._videoMeta = null; 10590 10591 this._audioSegmentInfoList = new _mediaSegmentInfo.MediaSegmentInfoList('audio'); 10592 this._videoSegmentInfoList = new _mediaSegmentInfo.MediaSegmentInfoList('video'); 10593 10594 this._onInitSegment = null; 10595 this._onMediaSegment = null; 10596 10597 // Workaround for chrome < 50: Always force first sample as a Random Access Point in media segment 10598 // see https://bugs.chromium.org/p/chromium/issues/detail?id=229412 10599 this._forceFirstIDR = _browser2.default.chrome && (_browser2.default.version.major < 50 || _browser2.default.version.major === 50 && _browser2.default.version.build < 2661) ? true : false; 10600 10601 // Workaround for IE11/Edge: Fill silent aac frame after keyframe-seeking 10602 // Make audio beginDts equals with video beginDts, in order to fix seek freeze 10603 this._fillSilentAfterSeek = _browser2.default.msedge || _browser2.default.msie; 10604 10605 // While only FireFox supports 'audio/mp4, codecs="mp3"', use 'audio/mpeg' for chrome, safari, ... 10606 this._mp3UseMpegAudio = !_browser2.default.firefox; 10607 10608 this._fillAudioTimestampGap = this._config.fixAudioTimestampGap; 10609 } 10610 10611 _createClass(MP4Remuxer, [{ 10612 key: 'destroy', 10613 value: function destroy() { 10614 this._dtsBase = -1; 10615 this._dtsBaseInited = false; 10616 this._audioMeta = null; 10617 this._videoMeta = null; 10618 this._audioSegmentInfoList.clear(); 10619 this._audioSegmentInfoList = null; 10620 this._videoSegmentInfoList.clear(); 10621 this._videoSegmentInfoList = null; 10622 this._onInitSegment = null; 10623 this._onMediaSegment = null; 10624 } 10625 }, { 10626 key: 'bindDataSource', 10627 value: function bindDataSource(producer) { 10628 producer.onDataAvailable = this.remux.bind(this); 10629 producer.onTrackMetadata = this._onTrackMetadataReceived.bind(this); 10630 return this; 10631 } 10632 10633 /* prototype: function onInitSegment(type: string, initSegment: ArrayBuffer): void 10634 InitSegment: { 10635 type: string, 10636 data: ArrayBuffer, 10637 codec: string, 10638 container: string 10639 } 10640 */ 10641 10642 }, { 10643 key: 'insertDiscontinuity', 10644 value: function insertDiscontinuity() { 10645 this._audioNextDts = this._videoNextDts = undefined; 10646 } 10647 }, { 10648 key: 'seek', 10649 value: function seek(originalDts) { 10650 this._audioStashedLastSample = null; 10651 this._videoStashedLastSample = null; 10652 this._videoSegmentInfoList.clear(); 10653 this._audioSegmentInfoList.clear(); 10654 } 10655 }, { 10656 key: 'remux', 10657 value: function remux(audioTrack, videoTrack) { 10658 if (!this._onMediaSegment) { 10659 throw new _exception.IllegalStateException('MP4Remuxer: onMediaSegment callback must be specificed!'); 10660 } 10661 if (!this._dtsBaseInited) { 10662 this._calculateDtsBase(audioTrack, videoTrack); 10663 } 10664 this._remuxVideo(videoTrack); 10665 this._remuxAudio(audioTrack); 10666 } 10667 }, { 10668 key: '_onTrackMetadataReceived', 10669 value: function _onTrackMetadataReceived(type, metadata) { 10670 var metabox = null; 10671 10672 var container = 'mp4'; 10673 var codec = metadata.codec; 10674 10675 if (type === 'audio') { 10676 this._audioMeta = metadata; 10677 if (metadata.codec === 'mp3' && this._mp3UseMpegAudio) { 10678 // 'audio/mpeg' for MP3 audio track 10679 container = 'mpeg'; 10680 codec = ''; 10681 metabox = new Uint8Array(); 10682 } else { 10683 // 'audio/mp4, codecs="codec"' 10684 metabox = _mp4Generator2.default.generateInitSegment(metadata); 10685 } 10686 } else if (type === 'video') { 10687 this._videoMeta = metadata; 10688 metabox = _mp4Generator2.default.generateInitSegment(metadata); 10689 } else { 10690 return; 10691 } 10692 10693 // dispatch metabox (Initialization Segment) 10694 if (!this._onInitSegment) { 10695 throw new _exception.IllegalStateException('MP4Remuxer: onInitSegment callback must be specified!'); 10696 } 10697 this._onInitSegment(type, { 10698 type: type, 10699 data: metabox.buffer, 10700 codec: codec, 10701 container: type + '/' + container, 10702 mediaDuration: metadata.duration // in timescale 1000 (milliseconds) 10703 }); 10704 } 10705 }, { 10706 key: '_calculateDtsBase', 10707 value: function _calculateDtsBase(audioTrack, videoTrack) { 10708 if (this._dtsBaseInited) { 10709 return; 10710 } 10711 10712 if (audioTrack.samples && audioTrack.samples.length) { 10713 this._audioDtsBase = audioTrack.samples[0].dts; 10714 } 10715 if (videoTrack.samples && videoTrack.samples.length) { 10716 this._videoDtsBase = videoTrack.samples[0].dts; 10717 } 10718 10719 this._dtsBase = Math.min(this._audioDtsBase, this._videoDtsBase); 10720 this._dtsBaseInited = true; 10721 } 10722 }, { 10723 key: 'flushStashedSamples', 10724 value: function flushStashedSamples() { 10725 var videoSample = this._videoStashedLastSample; 10726 var audioSample = this._audioStashedLastSample; 10727 10728 var videoTrack = { 10729 type: 'video', 10730 id: 1, 10731 sequenceNumber: 0, 10732 samples: [], 10733 length: 0 10734 }; 10735 10736 if (videoSample != null) { 10737 videoTrack.samples.push(videoSample); 10738 videoTrack.length = videoSample.length; 10739 } 10740 10741 var audioTrack = { 10742 type: 'audio', 10743 id: 2, 10744 sequenceNumber: 0, 10745 samples: [], 10746 length: 0 10747 }; 10748 10749 if (audioSample != null) { 10750 audioTrack.samples.push(audioSample); 10751 audioTrack.length = audioSample.length; 10752 } 10753 10754 this._videoStashedLastSample = null; 10755 this._audioStashedLastSample = null; 10756 10757 this._remuxVideo(videoTrack, true); 10758 this._remuxAudio(audioTrack, true); 10759 } 10760 }, { 10761 key: '_remuxAudio', 10762 value: function _remuxAudio(audioTrack, force) { 10763 if (this._audioMeta == null) { 10764 return; 10765 } 10766 10767 var track = audioTrack; 10768 var samples = track.samples; 10769 var dtsCorrection = undefined; 10770 var firstDts = -1, 10771 lastDts = -1, 10772 lastPts = -1; 10773 var refSampleDuration = this._audioMeta.refSampleDuration; 10774 10775 var mpegRawTrack = this._audioMeta.codec === 'mp3' && this._mp3UseMpegAudio; 10776 var firstSegmentAfterSeek = this._dtsBaseInited && this._audioNextDts === undefined; 10777 10778 var insertPrefixSilentFrame = false; 10779 10780 if (!samples || samples.length === 0) { 10781 return; 10782 } 10783 if (samples.length === 1 && !force) { 10784 // If [sample count in current batch] === 1 && (force != true) 10785 // Ignore and keep in demuxer's queue 10786 return; 10787 } // else if (force === true) do remux 10788 10789 var offset = 0; 10790 var mdatbox = null; 10791 var mdatBytes = 0; 10792 10793 // calculate initial mdat size 10794 if (mpegRawTrack) { 10795 // for raw mpeg buffer 10796 offset = 0; 10797 mdatBytes = track.length; 10798 } else { 10799 // for fmp4 mdat box 10800 offset = 8; // size + type 10801 mdatBytes = 8 + track.length; 10802 } 10803 10804 var lastSample = null; 10805 10806 // Pop the lastSample and waiting for stash 10807 if (samples.length > 1) { 10808 lastSample = samples.pop(); 10809 mdatBytes -= lastSample.length; 10810 } 10811 10812 // Insert [stashed lastSample in the previous batch] to the front 10813 if (this._audioStashedLastSample != null) { 10814 var sample = this._audioStashedLastSample; 10815 this._audioStashedLastSample = null; 10816 samples.unshift(sample); 10817 mdatBytes += sample.length; 10818 } 10819 10820 // Stash the lastSample of current batch, waiting for next batch 10821 if (lastSample != null) { 10822 this._audioStashedLastSample = lastSample; 10823 } 10824 10825 var firstSampleOriginalDts = samples[0].dts - this._dtsBase; 10826 10827 // calculate dtsCorrection 10828 if (this._audioNextDts) { 10829 dtsCorrection = firstSampleOriginalDts - this._audioNextDts; 10830 } else { 10831 // this._audioNextDts == undefined 10832 if (this._audioSegmentInfoList.isEmpty()) { 10833 dtsCorrection = 0; 10834 if (this._fillSilentAfterSeek && !this._videoSegmentInfoList.isEmpty()) { 10835 if (this._audioMeta.originalCodec !== 'mp3') { 10836 insertPrefixSilentFrame = true; 10837 } 10838 } 10839 } else { 10840 var _lastSample = this._audioSegmentInfoList.getLastSampleBefore(firstSampleOriginalDts); 10841 if (_lastSample != null) { 10842 var distance = firstSampleOriginalDts - (_lastSample.originalDts + _lastSample.duration); 10843 if (distance <= 3) { 10844 distance = 0; 10845 } 10846 var expectedDts = _lastSample.dts + _lastSample.duration + distance; 10847 dtsCorrection = firstSampleOriginalDts - expectedDts; 10848 } else { 10849 // lastSample == null, cannot found 10850 dtsCorrection = 0; 10851 } 10852 } 10853 } 10854 10855 if (insertPrefixSilentFrame) { 10856 // align audio segment beginDts to match with current video segment's beginDts 10857 var firstSampleDts = firstSampleOriginalDts - dtsCorrection; 10858 var videoSegment = this._videoSegmentInfoList.getLastSegmentBefore(firstSampleOriginalDts); 10859 if (videoSegment != null && videoSegment.beginDts < firstSampleDts) { 10860 var silentUnit = _aacSilent2.default.getSilentFrame(this._audioMeta.originalCodec, this._audioMeta.channelCount); 10861 if (silentUnit) { 10862 var dts = videoSegment.beginDts; 10863 var silentFrameDuration = firstSampleDts - videoSegment.beginDts; 10864 _logger2.default.v(this.TAG, 'InsertPrefixSilentAudio: dts: ' + dts + ', duration: ' + silentFrameDuration); 10865 samples.unshift({ unit: silentUnit, dts: dts, pts: dts }); 10866 mdatBytes += silentUnit.byteLength; 10867 } // silentUnit == null: Cannot generate, skip 10868 } else { 10869 insertPrefixSilentFrame = false; 10870 } 10871 } 10872 10873 var mp4Samples = []; 10874 10875 // Correct dts for each sample, and calculate sample duration. Then output to mp4Samples 10876 for (var i = 0; i < samples.length; i++) { 10877 var _sample = samples[i]; 10878 var unit = _sample.unit; 10879 var originalDts = _sample.dts - this._dtsBase; 10880 var _dts = originalDts - dtsCorrection; 10881 10882 if (firstDts === -1) { 10883 firstDts = _dts; 10884 } 10885 10886 var sampleDuration = 0; 10887 10888 if (i !== samples.length - 1) { 10889 var nextDts = samples[i + 1].dts - this._dtsBase - dtsCorrection; 10890 sampleDuration = nextDts - _dts; 10891 } else { 10892 // the last sample 10893 if (lastSample != null) { 10894 // use stashed sample's dts to calculate sample duration 10895 var _nextDts = lastSample.dts - this._dtsBase - dtsCorrection; 10896 sampleDuration = _nextDts - _dts; 10897 } else if (mp4Samples.length >= 1) { 10898 // use second last sample duration 10899 sampleDuration = mp4Samples[mp4Samples.length - 1].duration; 10900 } else { 10901 // the only one sample, use reference sample duration 10902 sampleDuration = Math.floor(refSampleDuration); 10903 } 10904 } 10905 10906 var needFillSilentFrames = false; 10907 var silentFrames = null; 10908 10909 // Silent frame generation, if large timestamp gap detected && config.fixAudioTimestampGap 10910 if (sampleDuration > refSampleDuration * 1.5 && this._audioMeta.codec !== 'mp3' && this._fillAudioTimestampGap && !_browser2.default.safari) { 10911 // We need to insert silent frames to fill timestamp gap 10912 needFillSilentFrames = true; 10913 var delta = Math.abs(sampleDuration - refSampleDuration); 10914 var frameCount = Math.ceil(delta / refSampleDuration); 10915 var currentDts = _dts + refSampleDuration; // Notice: in float 10916 10917 _logger2.default.w(this.TAG, 'Large audio timestamp gap detected, may cause AV sync to drift. ' + 'Silent frames will be generated to avoid unsync.\n' + ('dts: ' + (_dts + sampleDuration) + ' ms, expected: ' + (_dts + Math.round(refSampleDuration)) + ' ms, ') + ('delta: ' + Math.round(delta) + ' ms, generate: ' + frameCount + ' frames')); 10918 10919 var _silentUnit = _aacSilent2.default.getSilentFrame(this._audioMeta.originalCodec, this._audioMeta.channelCount); 10920 if (_silentUnit == null) { 10921 _logger2.default.w(this.TAG, 'Unable to generate silent frame for ' + (this._audioMeta.originalCodec + ' with ' + this._audioMeta.channelCount + ' channels, repeat last frame')); 10922 // Repeat last frame 10923 _silentUnit = unit; 10924 } 10925 silentFrames = []; 10926 10927 for (var j = 0; j < frameCount; j++) { 10928 var intDts = Math.round(currentDts); // round to integer 10929 if (silentFrames.length > 0) { 10930 // Set previous frame sample duration 10931 var previousFrame = silentFrames[silentFrames.length - 1]; 10932 previousFrame.duration = intDts - previousFrame.dts; 10933 } 10934 var frame = { 10935 dts: intDts, 10936 pts: intDts, 10937 cts: 0, 10938 unit: _silentUnit, 10939 size: _silentUnit.byteLength, 10940 duration: 0, // wait for next sample 10941 originalDts: originalDts, 10942 flags: { 10943 isLeading: 0, 10944 dependsOn: 1, 10945 isDependedOn: 0, 10946 hasRedundancy: 0 10947 } 10948 }; 10949 silentFrames.push(frame); 10950 mdatBytes += frame.size; 10951 currentDts += refSampleDuration; 10952 } 10953 10954 // last frame: align end time to next frame dts 10955 var lastFrame = silentFrames[silentFrames.length - 1]; 10956 lastFrame.duration = _dts + sampleDuration - lastFrame.dts; 10957 10958 // silentFrames.forEach((frame) => { 10959 // Log.w(this.TAG, `SilentAudio: dts: ${frame.dts}, duration: ${frame.duration}`); 10960 // }); 10961 10962 // Set correct sample duration for current frame 10963 sampleDuration = Math.round(refSampleDuration); 10964 } 10965 10966 mp4Samples.push({ 10967 dts: _dts, 10968 pts: _dts, 10969 cts: 0, 10970 unit: _sample.unit, 10971 size: _sample.unit.byteLength, 10972 duration: sampleDuration, 10973 originalDts: originalDts, 10974 flags: { 10975 isLeading: 0, 10976 dependsOn: 1, 10977 isDependedOn: 0, 10978 hasRedundancy: 0 10979 } 10980 }); 10981 10982 if (needFillSilentFrames) { 10983 // Silent frames should be inserted after wrong-duration frame 10984 mp4Samples.push.apply(mp4Samples, silentFrames); 10985 } 10986 } 10987 10988 // allocate mdatbox 10989 if (mpegRawTrack) { 10990 // allocate for raw mpeg buffer 10991 mdatbox = new Uint8Array(mdatBytes); 10992 } else { 10993 // allocate for fmp4 mdat box 10994 mdatbox = new Uint8Array(mdatBytes); 10995 // size field 10996 mdatbox[0] = mdatBytes >>> 24 & 0xFF; 10997 mdatbox[1] = mdatBytes >>> 16 & 0xFF; 10998 mdatbox[2] = mdatBytes >>> 8 & 0xFF; 10999 mdatbox[3] = mdatBytes & 0xFF; 11000 // type field (fourCC) 11001 mdatbox.set(_mp4Generator2.default.types.mdat, 4); 11002 } 11003 11004 // Write samples into mdatbox 11005 for (var _i = 0; _i < mp4Samples.length; _i++) { 11006 var _unit = mp4Samples[_i].unit; 11007 mdatbox.set(_unit, offset); 11008 offset += _unit.byteLength; 11009 } 11010 11011 var latest = mp4Samples[mp4Samples.length - 1]; 11012 lastDts = latest.dts + latest.duration; 11013 this._audioNextDts = lastDts; 11014 11015 // fill media segment info & add to info list 11016 var info = new _mediaSegmentInfo.MediaSegmentInfo(); 11017 info.beginDts = firstDts; 11018 info.endDts = lastDts; 11019 info.beginPts = firstDts; 11020 info.endPts = lastDts; 11021 info.originalBeginDts = mp4Samples[0].originalDts; 11022 info.originalEndDts = latest.originalDts + latest.duration; 11023 info.firstSample = new _mediaSegmentInfo.SampleInfo(mp4Samples[0].dts, mp4Samples[0].pts, mp4Samples[0].duration, mp4Samples[0].originalDts, false); 11024 info.lastSample = new _mediaSegmentInfo.SampleInfo(latest.dts, latest.pts, latest.duration, latest.originalDts, false); 11025 if (!this._isLive) { 11026 this._audioSegmentInfoList.append(info); 11027 } 11028 11029 track.samples = mp4Samples; 11030 track.sequenceNumber++; 11031 11032 var moofbox = null; 11033 11034 if (mpegRawTrack) { 11035 // Generate empty buffer, because useless for raw mpeg 11036 moofbox = new Uint8Array(); 11037 } else { 11038 // Generate moof for fmp4 segment 11039 moofbox = _mp4Generator2.default.moof(track, firstDts); 11040 } 11041 11042 track.samples = []; 11043 track.length = 0; 11044 11045 var segment = { 11046 type: 'audio', 11047 data: this._mergeBoxes(moofbox, mdatbox).buffer, 11048 sampleCount: mp4Samples.length, 11049 info: info 11050 }; 11051 11052 if (mpegRawTrack && firstSegmentAfterSeek) { 11053 // For MPEG audio stream in MSE, if seeking occurred, before appending new buffer 11054 // We need explicitly set timestampOffset to the desired point in timeline for mpeg SourceBuffer. 11055 segment.timestampOffset = firstDts; 11056 } 11057 11058 this._onMediaSegment('audio', segment); 11059 } 11060 }, { 11061 key: '_remuxVideo', 11062 value: function _remuxVideo(videoTrack, force) { 11063 if (this._videoMeta == null) { 11064 return; 11065 } 11066 11067 var track = videoTrack; 11068 var samples = track.samples; 11069 var dtsCorrection = undefined; 11070 var firstDts = -1, 11071 lastDts = -1; 11072 var firstPts = -1, 11073 lastPts = -1; 11074 11075 if (!samples || samples.length === 0) { 11076 return; 11077 } 11078 if (samples.length === 1 && !force) { 11079 // If [sample count in current batch] === 1 && (force != true) 11080 // Ignore and keep in demuxer's queue 11081 return; 11082 } // else if (force === true) do remux 11083 11084 var offset = 8; 11085 var mdatbox = null; 11086 var mdatBytes = 8 + videoTrack.length; 11087 11088 var lastSample = null; 11089 11090 // Pop the lastSample and waiting for stash 11091 if (samples.length > 1) { 11092 lastSample = samples.pop(); 11093 mdatBytes -= lastSample.length; 11094 } 11095 11096 // Insert [stashed lastSample in the previous batch] to the front 11097 if (this._videoStashedLastSample != null) { 11098 var sample = this._videoStashedLastSample; 11099 this._videoStashedLastSample = null; 11100 samples.unshift(sample); 11101 mdatBytes += sample.length; 11102 } 11103 11104 // Stash the lastSample of current batch, waiting for next batch 11105 if (lastSample != null) { 11106 this._videoStashedLastSample = lastSample; 11107 } 11108 11109 var firstSampleOriginalDts = samples[0].dts - this._dtsBase; 11110 11111 // calculate dtsCorrection 11112 if (this._videoNextDts) { 11113 dtsCorrection = firstSampleOriginalDts - this._videoNextDts; 11114 } else { 11115 // this._videoNextDts == undefined 11116 if (this._videoSegmentInfoList.isEmpty()) { 11117 dtsCorrection = 0; 11118 } else { 11119 var _lastSample2 = this._videoSegmentInfoList.getLastSampleBefore(firstSampleOriginalDts); 11120 if (_lastSample2 != null) { 11121 var distance = firstSampleOriginalDts - (_lastSample2.originalDts + _lastSample2.duration); 11122 if (distance <= 3) { 11123 distance = 0; 11124 } 11125 var expectedDts = _lastSample2.dts + _lastSample2.duration + distance; 11126 dtsCorrection = firstSampleOriginalDts - expectedDts; 11127 } else { 11128 // lastSample == null, cannot found 11129 dtsCorrection = 0; 11130 } 11131 } 11132 } 11133 11134 var info = new _mediaSegmentInfo.MediaSegmentInfo(); 11135 var mp4Samples = []; 11136 11137 // Correct dts for each sample, and calculate sample duration. Then output to mp4Samples 11138 for (var i = 0; i < samples.length; i++) { 11139 var _sample2 = samples[i]; 11140 var originalDts = _sample2.dts - this._dtsBase; 11141 var isKeyframe = _sample2.isKeyframe; 11142 var dts = originalDts - dtsCorrection; 11143 var cts = _sample2.cts; 11144 var pts = dts + cts; 11145 11146 if (firstDts === -1) { 11147 firstDts = dts; 11148 firstPts = pts; 11149 } 11150 11151 var sampleDuration = 0; 11152 11153 if (i !== samples.length - 1) { 11154 var nextDts = samples[i + 1].dts - this._dtsBase - dtsCorrection; 11155 sampleDuration = nextDts - dts; 11156 } else { 11157 // the last sample 11158 if (lastSample != null) { 11159 // use stashed sample's dts to calculate sample duration 11160 var _nextDts2 = lastSample.dts - this._dtsBase - dtsCorrection; 11161 sampleDuration = _nextDts2 - dts; 11162 } else if (mp4Samples.length >= 1) { 11163 // use second last sample duration 11164 sampleDuration = mp4Samples[mp4Samples.length - 1].duration; 11165 } else { 11166 // the only one sample, use reference sample duration 11167 sampleDuration = Math.floor(this._videoMeta.refSampleDuration); 11168 } 11169 } 11170 11171 if (isKeyframe) { 11172 var syncPoint = new _mediaSegmentInfo.SampleInfo(dts, pts, sampleDuration, _sample2.dts, true); 11173 syncPoint.fileposition = _sample2.fileposition; 11174 info.appendSyncPoint(syncPoint); 11175 } 11176 11177 mp4Samples.push({ 11178 dts: dts, 11179 pts: pts, 11180 cts: cts, 11181 units: _sample2.units, 11182 size: _sample2.length, 11183 isKeyframe: isKeyframe, 11184 duration: sampleDuration, 11185 originalDts: originalDts, 11186 flags: { 11187 isLeading: 0, 11188 dependsOn: isKeyframe ? 2 : 1, 11189 isDependedOn: isKeyframe ? 1 : 0, 11190 hasRedundancy: 0, 11191 isNonSync: isKeyframe ? 0 : 1 11192 } 11193 }); 11194 } 11195 11196 // allocate mdatbox 11197 mdatbox = new Uint8Array(mdatBytes); 11198 mdatbox[0] = mdatBytes >>> 24 & 0xFF; 11199 mdatbox[1] = mdatBytes >>> 16 & 0xFF; 11200 mdatbox[2] = mdatBytes >>> 8 & 0xFF; 11201 mdatbox[3] = mdatBytes & 0xFF; 11202 mdatbox.set(_mp4Generator2.default.types.mdat, 4); 11203 11204 // Write samples into mdatbox 11205 for (var _i2 = 0; _i2 < mp4Samples.length; _i2++) { 11206 var units = mp4Samples[_i2].units; 11207 while (units.length) { 11208 var unit = units.shift(); 11209 var data = unit.data; 11210 mdatbox.set(data, offset); 11211 offset += data.byteLength; 11212 } 11213 } 11214 11215 var latest = mp4Samples[mp4Samples.length - 1]; 11216 lastDts = latest.dts + latest.duration; 11217 lastPts = latest.pts + latest.duration; 11218 this._videoNextDts = lastDts; 11219 11220 // fill media segment info & add to info list 11221 info.beginDts = firstDts; 11222 info.endDts = lastDts; 11223 info.beginPts = firstPts; 11224 info.endPts = lastPts; 11225 info.originalBeginDts = mp4Samples[0].originalDts; 11226 info.originalEndDts = latest.originalDts + latest.duration; 11227 info.firstSample = new _mediaSegmentInfo.SampleInfo(mp4Samples[0].dts, mp4Samples[0].pts, mp4Samples[0].duration, mp4Samples[0].originalDts, mp4Samples[0].isKeyframe); 11228 info.lastSample = new _mediaSegmentInfo.SampleInfo(latest.dts, latest.pts, latest.duration, latest.originalDts, latest.isKeyframe); 11229 if (!this._isLive) { 11230 this._videoSegmentInfoList.append(info); 11231 } 11232 11233 track.samples = mp4Samples; 11234 track.sequenceNumber++; 11235 11236 // workaround for chrome < 50: force first sample as a random access point 11237 // see https://bugs.chromium.org/p/chromium/issues/detail?id=229412 11238 if (this._forceFirstIDR) { 11239 var flags = mp4Samples[0].flags; 11240 flags.dependsOn = 2; 11241 flags.isNonSync = 0; 11242 } 11243 11244 var moofbox = _mp4Generator2.default.moof(track, firstDts); 11245 track.samples = []; 11246 track.length = 0; 11247 11248 this._onMediaSegment('video', { 11249 type: 'video', 11250 data: this._mergeBoxes(moofbox, mdatbox).buffer, 11251 sampleCount: mp4Samples.length, 11252 info: info 11253 }); 11254 } 11255 }, { 11256 key: '_mergeBoxes', 11257 value: function _mergeBoxes(moof, mdat) { 11258 var result = new Uint8Array(moof.byteLength + mdat.byteLength); 11259 result.set(moof, 0); 11260 result.set(mdat, moof.byteLength); 11261 return result; 11262 } 11263 }, { 11264 key: 'onInitSegment', 11265 get: function get() { 11266 return this._onInitSegment; 11267 }, 11268 set: function set(callback) { 11269 this._onInitSegment = callback; 11270 } 11271 11272 /* prototype: function onMediaSegment(type: string, mediaSegment: MediaSegment): void 11273 MediaSegment: { 11274 type: string, 11275 data: ArrayBuffer, 11276 sampleCount: int32 11277 info: MediaSegmentInfo 11278 } 11279 */ 11280 11281 }, { 11282 key: 'onMediaSegment', 11283 get: function get() { 11284 return this._onMediaSegment; 11285 }, 11286 set: function set(callback) { 11287 this._onMediaSegment = callback; 11288 } 11289 }]); 11290 11291 return MP4Remuxer; 11292 }(); 11293 11294 exports.default = MP4Remuxer; 11295 11296 },{"../core/media-segment-info.js":8,"../utils/browser.js":39,"../utils/exception.js":40,"../utils/logger.js":41,"./aac-silent.js":36,"./mp4-generator.js":37}],39:[function(_dereq_,module,exports){ 11297 'use strict'; 11298 11299 Object.defineProperty(exports, "__esModule", { 11300 value: true 11301 }); 11302 /* 11303 * Copyright (C) 2016 Bilibili. All Rights Reserved. 11304 * 11305 * @author zheng qian <xqq@xqq.im> 11306 * 11307 * Licensed under the Apache License, Version 2.0 (the "License"); 11308 * you may not use this file except in compliance with the License. 11309 * You may obtain a copy of the License at 11310 * 11311 * http://www.apache.org/licenses/LICENSE-2.0 11312 * 11313 * Unless required by applicable law or agreed to in writing, software 11314 * distributed under the License is distributed on an "AS IS" BASIS, 11315 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11316 * See the License for the specific language governing permissions and 11317 * limitations under the License. 11318 */ 11319 11320 var Browser = {}; 11321 11322 function detect() { 11323 // modified from jquery-browser-plugin 11324 11325 var ua = self.navigator.userAgent.toLowerCase(); 11326 11327 var match = /(edge)\/([\w.]+)/.exec(ua) || /(opr)[\/]([\w.]+)/.exec(ua) || /(chrome)[ \/]([\w.]+)/.exec(ua) || /(iemobile)[\/]([\w.]+)/.exec(ua) || /(version)(applewebkit)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+).*(version)[ \/]([\w.]+).*(safari)[ \/]([\w.]+)/.exec(ua) || /(webkit)[ \/]([\w.]+)/.exec(ua) || /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || /(msie) ([\w.]+)/.exec(ua) || ua.indexOf('trident') >= 0 && /(rv)(?::| )([\w.]+)/.exec(ua) || ua.indexOf('compatible') < 0 && /(firefox)[ \/]([\w.]+)/.exec(ua) || []; 11328 11329 var platform_match = /(ipad)/.exec(ua) || /(ipod)/.exec(ua) || /(windows phone)/.exec(ua) || /(iphone)/.exec(ua) || /(kindle)/.exec(ua) || /(android)/.exec(ua) || /(windows)/.exec(ua) || /(mac)/.exec(ua) || /(linux)/.exec(ua) || /(cros)/.exec(ua) || []; 11330 11331 var matched = { 11332 browser: match[5] || match[3] || match[1] || '', 11333 version: match[2] || match[4] || '0', 11334 majorVersion: match[4] || match[2] || '0', 11335 platform: platform_match[0] || '' 11336 }; 11337 11338 var browser = {}; 11339 if (matched.browser) { 11340 browser[matched.browser] = true; 11341 11342 var versionArray = matched.majorVersion.split('.'); 11343 browser.version = { 11344 major: parseInt(matched.majorVersion, 10), 11345 string: matched.version 11346 }; 11347 if (versionArray.length > 1) { 11348 browser.version.minor = parseInt(versionArray[1], 10); 11349 } 11350 if (versionArray.length > 2) { 11351 browser.version.build = parseInt(versionArray[2], 10); 11352 } 11353 } 11354 11355 if (matched.platform) { 11356 browser[matched.platform] = true; 11357 } 11358 11359 if (browser.chrome || browser.opr || browser.safari) { 11360 browser.webkit = true; 11361 } 11362 11363 // MSIE. IE11 has 'rv' identifer 11364 if (browser.rv || browser.iemobile) { 11365 if (browser.rv) { 11366 delete browser.rv; 11367 } 11368 var msie = 'msie'; 11369 matched.browser = msie; 11370 browser[msie] = true; 11371 } 11372 11373 // Microsoft Edge 11374 if (browser.edge) { 11375 delete browser.edge; 11376 var msedge = 'msedge'; 11377 matched.browser = msedge; 11378 browser[msedge] = true; 11379 } 11380 11381 // Opera 15+ 11382 if (browser.opr) { 11383 var opera = 'opera'; 11384 matched.browser = opera; 11385 browser[opera] = true; 11386 } 11387 11388 // Stock android browsers are marked as Safari 11389 if (browser.safari && browser.android) { 11390 var android = 'android'; 11391 matched.browser = android; 11392 browser[android] = true; 11393 } 11394 11395 browser.name = matched.browser; 11396 browser.platform = matched.platform; 11397 11398 for (var key in Browser) { 11399 if (Browser.hasOwnProperty(key)) { 11400 delete Browser[key]; 11401 } 11402 } 11403 Object.assign(Browser, browser); 11404 } 11405 11406 detect(); 11407 11408 exports.default = Browser; 11409 11410 },{}],40:[function(_dereq_,module,exports){ 11411 'use strict'; 11412 11413 Object.defineProperty(exports, "__esModule", { 11414 value: true 11415 }); 11416 11417 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 11418 11419 function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; } 11420 11421 function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 11422 11423 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 11424 11425 /* 11426 * Copyright (C) 2016 Bilibili. All Rights Reserved. 11427 * 11428 * @author zheng qian <xqq@xqq.im> 11429 * 11430 * Licensed under the Apache License, Version 2.0 (the "License"); 11431 * you may not use this file except in compliance with the License. 11432 * You may obtain a copy of the License at 11433 * 11434 * http://www.apache.org/licenses/LICENSE-2.0 11435 * 11436 * Unless required by applicable law or agreed to in writing, software 11437 * distributed under the License is distributed on an "AS IS" BASIS, 11438 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11439 * See the License for the specific language governing permissions and 11440 * limitations under the License. 11441 */ 11442 11443 var RuntimeException = exports.RuntimeException = function () { 11444 function RuntimeException(message) { 11445 _classCallCheck(this, RuntimeException); 11446 11447 this._message = message; 11448 } 11449 11450 _createClass(RuntimeException, [{ 11451 key: 'toString', 11452 value: function toString() { 11453 return this.name + ': ' + this.message; 11454 } 11455 }, { 11456 key: 'name', 11457 get: function get() { 11458 return 'RuntimeException'; 11459 } 11460 }, { 11461 key: 'message', 11462 get: function get() { 11463 return this._message; 11464 } 11465 }]); 11466 11467 return RuntimeException; 11468 }(); 11469 11470 var IllegalStateException = exports.IllegalStateException = function (_RuntimeException) { 11471 _inherits(IllegalStateException, _RuntimeException); 11472 11473 function IllegalStateException(message) { 11474 _classCallCheck(this, IllegalStateException); 11475 11476 return _possibleConstructorReturn(this, (IllegalStateException.__proto__ || Object.getPrototypeOf(IllegalStateException)).call(this, message)); 11477 } 11478 11479 _createClass(IllegalStateException, [{ 11480 key: 'name', 11481 get: function get() { 11482 return 'IllegalStateException'; 11483 } 11484 }]); 11485 11486 return IllegalStateException; 11487 }(RuntimeException); 11488 11489 var InvalidArgumentException = exports.InvalidArgumentException = function (_RuntimeException2) { 11490 _inherits(InvalidArgumentException, _RuntimeException2); 11491 11492 function InvalidArgumentException(message) { 11493 _classCallCheck(this, InvalidArgumentException); 11494 11495 return _possibleConstructorReturn(this, (InvalidArgumentException.__proto__ || Object.getPrototypeOf(InvalidArgumentException)).call(this, message)); 11496 } 11497 11498 _createClass(InvalidArgumentException, [{ 11499 key: 'name', 11500 get: function get() { 11501 return 'InvalidArgumentException'; 11502 } 11503 }]); 11504 11505 return InvalidArgumentException; 11506 }(RuntimeException); 11507 11508 var NotImplementedException = exports.NotImplementedException = function (_RuntimeException3) { 11509 _inherits(NotImplementedException, _RuntimeException3); 11510 11511 function NotImplementedException(message) { 11512 _classCallCheck(this, NotImplementedException); 11513 11514 return _possibleConstructorReturn(this, (NotImplementedException.__proto__ || Object.getPrototypeOf(NotImplementedException)).call(this, message)); 11515 } 11516 11517 _createClass(NotImplementedException, [{ 11518 key: 'name', 11519 get: function get() { 11520 return 'NotImplementedException'; 11521 } 11522 }]); 11523 11524 return NotImplementedException; 11525 }(RuntimeException); 11526 11527 },{}],41:[function(_dereq_,module,exports){ 11528 'use strict'; 11529 11530 Object.defineProperty(exports, "__esModule", { 11531 value: true 11532 }); 11533 11534 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 11535 * Copyright (C) 2016 Bilibili. All Rights Reserved. 11536 * 11537 * @author zheng qian <xqq@xqq.im> 11538 * 11539 * Licensed under the Apache License, Version 2.0 (the "License"); 11540 * you may not use this file except in compliance with the License. 11541 * You may obtain a copy of the License at 11542 * 11543 * http://www.apache.org/licenses/LICENSE-2.0 11544 * 11545 * Unless required by applicable law or agreed to in writing, software 11546 * distributed under the License is distributed on an "AS IS" BASIS, 11547 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11548 * See the License for the specific language governing permissions and 11549 * limitations under the License. 11550 */ 11551 11552 var _events = _dereq_('events'); 11553 11554 var _events2 = _interopRequireDefault(_events); 11555 11556 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 11557 11558 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 11559 11560 var Log = function () { 11561 function Log() { 11562 _classCallCheck(this, Log); 11563 } 11564 11565 _createClass(Log, null, [{ 11566 key: 'e', 11567 value: function e(tag, msg) { 11568 if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG; 11569 11570 var str = '[' + tag + '] > ' + msg; 11571 11572 if (Log.ENABLE_CALLBACK) { 11573 Log.emitter.emit('log', 'error', str); 11574 } 11575 11576 if (!Log.ENABLE_ERROR) { 11577 return; 11578 } 11579 11580 if (console.error) { 11581 console.error(str); 11582 } else if (console.warn) { 11583 console.warn(str); 11584 } else { 11585 console.log(str); 11586 } 11587 } 11588 }, { 11589 key: 'i', 11590 value: function i(tag, msg) { 11591 if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG; 11592 11593 var str = '[' + tag + '] > ' + msg; 11594 11595 if (Log.ENABLE_CALLBACK) { 11596 Log.emitter.emit('log', 'info', str); 11597 } 11598 11599 if (!Log.ENABLE_INFO) { 11600 return; 11601 } 11602 11603 if (console.info) { 11604 console.info(str); 11605 } else { 11606 console.log(str); 11607 } 11608 } 11609 }, { 11610 key: 'w', 11611 value: function w(tag, msg) { 11612 if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG; 11613 11614 var str = '[' + tag + '] > ' + msg; 11615 11616 if (Log.ENABLE_CALLBACK) { 11617 Log.emitter.emit('log', 'warn', str); 11618 } 11619 11620 if (!Log.ENABLE_WARN) { 11621 return; 11622 } 11623 11624 if (console.warn) { 11625 console.warn(str); 11626 } else { 11627 console.log(str); 11628 } 11629 } 11630 }, { 11631 key: 'd', 11632 value: function d(tag, msg) { 11633 if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG; 11634 11635 var str = '[' + tag + '] > ' + msg; 11636 11637 if (Log.ENABLE_CALLBACK) { 11638 Log.emitter.emit('log', 'debug', str); 11639 } 11640 11641 if (!Log.ENABLE_DEBUG) { 11642 return; 11643 } 11644 11645 if (console.debug) { 11646 console.debug(str); 11647 } else { 11648 console.log(str); 11649 } 11650 } 11651 }, { 11652 key: 'v', 11653 value: function v(tag, msg) { 11654 if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG; 11655 11656 var str = '[' + tag + '] > ' + msg; 11657 11658 if (Log.ENABLE_CALLBACK) { 11659 Log.emitter.emit('log', 'verbose', str); 11660 } 11661 11662 if (!Log.ENABLE_VERBOSE) { 11663 return; 11664 } 11665 11666 console.log(str); 11667 } 11668 }]); 11669 11670 return Log; 11671 }(); 11672 11673 Log.GLOBAL_TAG = 'flv.js'; 11674 Log.FORCE_GLOBAL_TAG = false; 11675 Log.ENABLE_ERROR = true; 11676 Log.ENABLE_INFO = true; 11677 Log.ENABLE_WARN = true; 11678 Log.ENABLE_DEBUG = true; 11679 Log.ENABLE_VERBOSE = true; 11680 11681 Log.ENABLE_CALLBACK = false; 11682 11683 Log.emitter = new _events2.default(); 11684 11685 exports.default = Log; 11686 11687 },{"events":2}],42:[function(_dereq_,module,exports){ 11688 'use strict'; 11689 11690 Object.defineProperty(exports, "__esModule", { 11691 value: true 11692 }); 11693 11694 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* 11695 * Copyright (C) 2016 Bilibili. All Rights Reserved. 11696 * 11697 * @author zheng qian <xqq@xqq.im> 11698 * 11699 * Licensed under the Apache License, Version 2.0 (the "License"); 11700 * you may not use this file except in compliance with the License. 11701 * You may obtain a copy of the License at 11702 * 11703 * http://www.apache.org/licenses/LICENSE-2.0 11704 * 11705 * Unless required by applicable law or agreed to in writing, software 11706 * distributed under the License is distributed on an "AS IS" BASIS, 11707 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11708 * See the License for the specific language governing permissions and 11709 * limitations under the License. 11710 */ 11711 11712 var _events = _dereq_('events'); 11713 11714 var _events2 = _interopRequireDefault(_events); 11715 11716 var _logger = _dereq_('./logger.js'); 11717 11718 var _logger2 = _interopRequireDefault(_logger); 11719 11720 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } 11721 11722 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 11723 11724 var LoggingControl = function () { 11725 function LoggingControl() { 11726 _classCallCheck(this, LoggingControl); 11727 } 11728 11729 _createClass(LoggingControl, null, [{ 11730 key: 'getConfig', 11731 value: function getConfig() { 11732 return { 11733 globalTag: _logger2.default.GLOBAL_TAG, 11734 forceGlobalTag: _logger2.default.FORCE_GLOBAL_TAG, 11735 enableVerbose: _logger2.default.ENABLE_VERBOSE, 11736 enableDebug: _logger2.default.ENABLE_DEBUG, 11737 enableInfo: _logger2.default.ENABLE_INFO, 11738 enableWarn: _logger2.default.ENABLE_WARN, 11739 enableError: _logger2.default.ENABLE_ERROR, 11740 enableCallback: _logger2.default.ENABLE_CALLBACK 11741 }; 11742 } 11743 }, { 11744 key: 'applyConfig', 11745 value: function applyConfig(config) { 11746 _logger2.default.GLOBAL_TAG = config.globalTag; 11747 _logger2.default.FORCE_GLOBAL_TAG = config.forceGlobalTag; 11748 _logger2.default.ENABLE_VERBOSE = config.enableVerbose; 11749 _logger2.default.ENABLE_DEBUG = config.enableDebug; 11750 _logger2.default.ENABLE_INFO = config.enableInfo; 11751 _logger2.default.ENABLE_WARN = config.enableWarn; 11752 _logger2.default.ENABLE_ERROR = config.enableError; 11753 _logger2.default.ENABLE_CALLBACK = config.enableCallback; 11754 } 11755 }, { 11756 key: '_notifyChange', 11757 value: function _notifyChange() { 11758 var emitter = LoggingControl.emitter; 11759 11760 if (emitter.listenerCount('change') > 0) { 11761 var config = LoggingControl.getConfig(); 11762 emitter.emit('change', config); 11763 } 11764 } 11765 }, { 11766 key: 'registerListener', 11767 value: function registerListener(listener) { 11768 LoggingControl.emitter.addListener('change', listener); 11769 } 11770 }, { 11771 key: 'removeListener', 11772 value: function removeListener(listener) { 11773 LoggingControl.emitter.removeListener('change', listener); 11774 } 11775 }, { 11776 key: 'addLogListener', 11777 value: function addLogListener(listener) { 11778 _logger2.default.emitter.addListener('log', listener); 11779 if (_logger2.default.emitter.listenerCount('log') > 0) { 11780 _logger2.default.ENABLE_CALLBACK = true; 11781 LoggingControl._notifyChange(); 11782 } 11783 } 11784 }, { 11785 key: 'removeLogListener', 11786 value: function removeLogListener(listener) { 11787 _logger2.default.emitter.removeListener('log', listener); 11788 if (_logger2.default.emitter.listenerCount('log') === 0) { 11789 _logger2.default.ENABLE_CALLBACK = false; 11790 LoggingControl._notifyChange(); 11791 } 11792 } 11793 }, { 11794 key: 'forceGlobalTag', 11795 get: function get() { 11796 return _logger2.default.FORCE_GLOBAL_TAG; 11797 }, 11798 set: function set(enable) { 11799 _logger2.default.FORCE_GLOBAL_TAG = enable; 11800 LoggingControl._notifyChange(); 11801 } 11802 }, { 11803 key: 'globalTag', 11804 get: function get() { 11805 return _logger2.default.GLOBAL_TAG; 11806 }, 11807 set: function set(tag) { 11808 _logger2.default.GLOBAL_TAG = tag; 11809 LoggingControl._notifyChange(); 11810 } 11811 }, { 11812 key: 'enableAll', 11813 get: function get() { 11814 return _logger2.default.ENABLE_VERBOSE && _logger2.default.ENABLE_DEBUG && _logger2.default.ENABLE_INFO && _logger2.default.ENABLE_WARN && _logger2.default.ENABLE_ERROR; 11815 }, 11816 set: function set(enable) { 11817 _logger2.default.ENABLE_VERBOSE = enable; 11818 _logger2.default.ENABLE_DEBUG = enable; 11819 _logger2.default.ENABLE_INFO = enable; 11820 _logger2.default.ENABLE_WARN = enable; 11821 _logger2.default.ENABLE_ERROR = enable; 11822 LoggingControl._notifyChange(); 11823 } 11824 }, { 11825 key: 'enableDebug', 11826 get: function get() { 11827 return _logger2.default.ENABLE_DEBUG; 11828 }, 11829 set: function set(enable) { 11830 _logger2.default.ENABLE_DEBUG = enable; 11831 LoggingControl._notifyChange(); 11832 } 11833 }, { 11834 key: 'enableVerbose', 11835 get: function get() { 11836 return _logger2.default.ENABLE_VERBOSE; 11837 }, 11838 set: function set(enable) { 11839 _logger2.default.ENABLE_VERBOSE = enable; 11840 LoggingControl._notifyChange(); 11841 } 11842 }, { 11843 key: 'enableInfo', 11844 get: function get() { 11845 return _logger2.default.ENABLE_INFO; 11846 }, 11847 set: function set(enable) { 11848 _logger2.default.ENABLE_INFO = enable; 11849 LoggingControl._notifyChange(); 11850 } 11851 }, { 11852 key: 'enableWarn', 11853 get: function get() { 11854 return _logger2.default.ENABLE_WARN; 11855 }, 11856 set: function set(enable) { 11857 _logger2.default.ENABLE_WARN = enable; 11858 LoggingControl._notifyChange(); 11859 } 11860 }, { 11861 key: 'enableError', 11862 get: function get() { 11863 return _logger2.default.ENABLE_ERROR; 11864 }, 11865 set: function set(enable) { 11866 _logger2.default.ENABLE_ERROR = enable; 11867 LoggingControl._notifyChange(); 11868 } 11869 }]); 11870 11871 return LoggingControl; 11872 }(); 11873 11874 LoggingControl.emitter = new _events2.default(); 11875 11876 exports.default = LoggingControl; 11877 11878 },{"./logger.js":41,"events":2}],43:[function(_dereq_,module,exports){ 11879 'use strict'; 11880 11881 Object.defineProperty(exports, "__esModule", { 11882 value: true 11883 }); 11884 11885 var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); 11886 11887 function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 11888 11889 /* 11890 * Copyright (C) 2016 Bilibili. All Rights Reserved. 11891 * 11892 * @author zheng qian <xqq@xqq.im> 11893 * 11894 * Licensed under the Apache License, Version 2.0 (the "License"); 11895 * you may not use this file except in compliance with the License. 11896 * You may obtain a copy of the License at 11897 * 11898 * http://www.apache.org/licenses/LICENSE-2.0 11899 * 11900 * Unless required by applicable law or agreed to in writing, software 11901 * distributed under the License is distributed on an "AS IS" BASIS, 11902 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11903 * See the License for the specific language governing permissions and 11904 * limitations under the License. 11905 */ 11906 11907 var Polyfill = function () { 11908 function Polyfill() { 11909 _classCallCheck(this, Polyfill); 11910 } 11911 11912 _createClass(Polyfill, null, [{ 11913 key: 'install', 11914 value: function install() { 11915 // ES6 Object.setPrototypeOf 11916 Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) { 11917 obj.__proto__ = proto; 11918 return obj; 11919 }; 11920 11921 // ES6 Object.assign 11922 Object.assign = Object.assign || function (target) { 11923 if (target === undefined || target === null) { 11924 throw new TypeError('Cannot convert undefined or null to object'); 11925 } 11926 11927 var output = Object(target); 11928 for (var i = 1; i < arguments.length; i++) { 11929 var source = arguments[i]; 11930 if (source !== undefined && source !== null) { 11931 for (var key in source) { 11932 if (source.hasOwnProperty(key)) { 11933 output[key] = source[key]; 11934 } 11935 } 11936 } 11937 } 11938 return output; 11939 }; 11940 11941 // ES6 Promise (missing support in IE11) 11942 if (typeof self.Promise !== 'function') { 11943 _dereq_('es6-promise').polyfill(); 11944 } 11945 } 11946 }]); 11947 11948 return Polyfill; 11949 }(); 11950 11951 Polyfill.install(); 11952 11953 exports.default = Polyfill; 11954 11955 },{"es6-promise":1}],44:[function(_dereq_,module,exports){ 11956 'use strict'; 11957 11958 Object.defineProperty(exports, "__esModule", { 11959 value: true 11960 }); 11961 /* 11962 * Copyright (C) 2016 Bilibili. All Rights Reserved. 11963 * 11964 * This file is derived from C++ project libWinTF8 (https://github.com/m13253/libWinTF8) 11965 * @author zheng qian <xqq@xqq.im> 11966 * 11967 * Licensed under the Apache License, Version 2.0 (the "License"); 11968 * you may not use this file except in compliance with the License. 11969 * You may obtain a copy of the License at 11970 * 11971 * http://www.apache.org/licenses/LICENSE-2.0 11972 * 11973 * Unless required by applicable law or agreed to in writing, software 11974 * distributed under the License is distributed on an "AS IS" BASIS, 11975 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11976 * See the License for the specific language governing permissions and 11977 * limitations under the License. 11978 */ 11979 11980 function checkContinuation(uint8array, start, checkLength) { 11981 var array = uint8array; 11982 if (start + checkLength < array.length) { 11983 while (checkLength--) { 11984 if ((array[++start] & 0xC0) !== 0x80) return false; 11985 } 11986 return true; 11987 } else { 11988 return false; 11989 } 11990 } 11991 11992 function decodeUTF8(uint8array) { 11993 var out = []; 11994 var input = uint8array; 11995 var i = 0; 11996 var length = uint8array.length; 11997 11998 while (i < length) { 11999 if (input[i] < 0x80) { 12000 out.push(String.fromCharCode(input[i])); 12001 ++i; 12002 continue; 12003 } else if (input[i] < 0xC0) { 12004 // fallthrough 12005 } else if (input[i] < 0xE0) { 12006 if (checkContinuation(input, i, 1)) { 12007 var ucs4 = (input[i] & 0x1F) << 6 | input[i + 1] & 0x3F; 12008 if (ucs4 >= 0x80) { 12009 out.push(String.fromCharCode(ucs4 & 0xFFFF)); 12010 i += 2; 12011 continue; 12012 } 12013 } 12014 } else if (input[i] < 0xF0) { 12015 if (checkContinuation(input, i, 2)) { 12016 var _ucs = (input[i] & 0xF) << 12 | (input[i + 1] & 0x3F) << 6 | input[i + 2] & 0x3F; 12017 if (_ucs >= 0x800 && (_ucs & 0xF800) !== 0xD800) { 12018 out.push(String.fromCharCode(_ucs & 0xFFFF)); 12019 i += 3; 12020 continue; 12021 } 12022 } 12023 } else if (input[i] < 0xF8) { 12024 if (checkContinuation(input, i, 3)) { 12025 var _ucs2 = (input[i] & 0x7) << 18 | (input[i + 1] & 0x3F) << 12 | (input[i + 2] & 0x3F) << 6 | input[i + 3] & 0x3F; 12026 if (_ucs2 > 0x10000 && _ucs2 < 0x110000) { 12027 _ucs2 -= 0x10000; 12028 out.push(String.fromCharCode(_ucs2 >>> 10 | 0xD800)); 12029 out.push(String.fromCharCode(_ucs2 & 0x3FF | 0xDC00)); 12030 i += 4; 12031 continue; 12032 } 12033 } 12034 } 12035 out.push(String.fromCharCode(0xFFFD)); 12036 ++i; 12037 } 12038 12039 return out.join(''); 12040 } 12041 12042 exports.default = decodeUTF8; 12043 12044 },{}]},{},[21])(21) 12045 }); 12046 12047 //# sourceMappingURL=flv.js.map