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 e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({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.5+7f2b526d
|
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 | var TRY_CATCH_ERROR = { error: null };
|
240 |
|
241 | function selfFulfillment() {
|
242 | return new TypeError("You cannot resolve a promise with itself");
|
243 | }
|
244 |
|
245 | function cannotReturnOwn() {
|
246 | return new TypeError('A promises callback cannot return that same promise.');
|
247 | }
|
248 |
|
249 | function getThen(promise) {
|
250 | try {
|
251 | return promise.then;
|
252 | } catch (error) {
|
253 | TRY_CATCH_ERROR.error = error;
|
254 | return TRY_CATCH_ERROR;
|
255 | }
|
256 | }
|
257 |
|
258 | function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) {
|
259 | try {
|
260 | then$$1.call(value, fulfillmentHandler, rejectionHandler);
|
261 | } catch (e) {
|
262 | return e;
|
263 | }
|
264 | }
|
265 |
|
266 | function handleForeignThenable(promise, thenable, then$$1) {
|
267 | asap(function (promise) {
|
268 | var sealed = false;
|
269 | var error = tryThen(then$$1, thenable, function (value) {
|
270 | if (sealed) {
|
271 | return;
|
272 | }
|
273 | sealed = true;
|
274 | if (thenable !== value) {
|
275 | resolve(promise, value);
|
276 | } else {
|
277 | fulfill(promise, value);
|
278 | }
|
279 | }, function (reason) {
|
280 | if (sealed) {
|
281 | return;
|
282 | }
|
283 | sealed = true;
|
284 |
|
285 | reject(promise, reason);
|
286 | }, 'Settle: ' + (promise._label || ' unknown promise'));
|
287 |
|
288 | if (!sealed && error) {
|
289 | sealed = true;
|
290 | reject(promise, error);
|
291 | }
|
292 | }, promise);
|
293 | }
|
294 |
|
295 | function handleOwnThenable(promise, thenable) {
|
296 | if (thenable._state === FULFILLED) {
|
297 | fulfill(promise, thenable._result);
|
298 | } else if (thenable._state === REJECTED) {
|
299 | reject(promise, thenable._result);
|
300 | } else {
|
301 | subscribe(thenable, undefined, function (value) {
|
302 | return resolve(promise, value);
|
303 | }, function (reason) {
|
304 | return reject(promise, reason);
|
305 | });
|
306 | }
|
307 | }
|
308 |
|
309 | function handleMaybeThenable(promise, maybeThenable, then$$1) {
|
310 | if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) {
|
311 | handleOwnThenable(promise, maybeThenable);
|
312 | } else {
|
313 | if (then$$1 === TRY_CATCH_ERROR) {
|
314 | reject(promise, TRY_CATCH_ERROR.error);
|
315 | TRY_CATCH_ERROR.error = null;
|
316 | } else if (then$$1 === undefined) {
|
317 | fulfill(promise, maybeThenable);
|
318 | } else if (isFunction(then$$1)) {
|
319 | handleForeignThenable(promise, maybeThenable, then$$1);
|
320 | } else {
|
321 | fulfill(promise, maybeThenable);
|
322 | }
|
323 | }
|
324 | }
|
325 |
|
326 | function resolve(promise, value) {
|
327 | if (promise === value) {
|
328 | reject(promise, selfFulfillment());
|
329 | } else if (objectOrFunction(value)) {
|
330 | handleMaybeThenable(promise, value, getThen(value));
|
331 | } else {
|
332 | fulfill(promise, value);
|
333 | }
|
334 | }
|
335 |
|
336 | function publishRejection(promise) {
|
337 | if (promise._onerror) {
|
338 | promise._onerror(promise._result);
|
339 | }
|
340 |
|
341 | publish(promise);
|
342 | }
|
343 |
|
344 | function fulfill(promise, value) {
|
345 | if (promise._state !== PENDING) {
|
346 | return;
|
347 | }
|
348 |
|
349 | promise._result = value;
|
350 | promise._state = FULFILLED;
|
351 |
|
352 | if (promise._subscribers.length !== 0) {
|
353 | asap(publish, promise);
|
354 | }
|
355 | }
|
356 |
|
357 | function reject(promise, reason) {
|
358 | if (promise._state !== PENDING) {
|
359 | return;
|
360 | }
|
361 | promise._state = REJECTED;
|
362 | promise._result = reason;
|
363 |
|
364 | asap(publishRejection, promise);
|
365 | }
|
366 |
|
367 | function subscribe(parent, child, onFulfillment, onRejection) {
|
368 | var _subscribers = parent._subscribers;
|
369 | var length = _subscribers.length;
|
370 |
|
371 |
|
372 | parent._onerror = null;
|
373 |
|
374 | _subscribers[length] = child;
|
375 | _subscribers[length + FULFILLED] = onFulfillment;
|
376 | _subscribers[length + REJECTED] = onRejection;
|
377 |
|
378 | if (length === 0 && parent._state) {
|
379 | asap(publish, parent);
|
380 | }
|
381 | }
|
382 |
|
383 | function publish(promise) {
|
384 | var subscribers = promise._subscribers;
|
385 | var settled = promise._state;
|
386 |
|
387 | if (subscribers.length === 0) {
|
388 | return;
|
389 | }
|
390 |
|
391 | var child = void 0,
|
392 | callback = void 0,
|
393 | detail = promise._result;
|
394 |
|
395 | for (var i = 0; i < subscribers.length; i += 3) {
|
396 | child = subscribers[i];
|
397 | callback = subscribers[i + settled];
|
398 |
|
399 | if (child) {
|
400 | invokeCallback(settled, child, callback, detail);
|
401 | } else {
|
402 | callback(detail);
|
403 | }
|
404 | }
|
405 |
|
406 | promise._subscribers.length = 0;
|
407 | }
|
408 |
|
409 | function tryCatch(callback, detail) {
|
410 | try {
|
411 | return callback(detail);
|
412 | } catch (e) {
|
413 | TRY_CATCH_ERROR.error = e;
|
414 | return TRY_CATCH_ERROR;
|
415 | }
|
416 | }
|
417 |
|
418 | function invokeCallback(settled, promise, callback, detail) {
|
419 | var hasCallback = isFunction(callback),
|
420 | value = void 0,
|
421 | error = void 0,
|
422 | succeeded = void 0,
|
423 | failed = void 0;
|
424 |
|
425 | if (hasCallback) {
|
426 | value = tryCatch(callback, detail);
|
427 |
|
428 | if (value === TRY_CATCH_ERROR) {
|
429 | failed = true;
|
430 | error = value.error;
|
431 | value.error = null;
|
432 | } else {
|
433 | succeeded = true;
|
434 | }
|
435 |
|
436 | if (promise === value) {
|
437 | reject(promise, cannotReturnOwn());
|
438 | return;
|
439 | }
|
440 | } else {
|
441 | value = detail;
|
442 | succeeded = true;
|
443 | }
|
444 |
|
445 | if (promise._state !== PENDING) {
|
446 | // noop
|
447 | } else if (hasCallback && succeeded) {
|
448 | resolve(promise, value);
|
449 | } else if (failed) {
|
450 | reject(promise, error);
|
451 | } else if (settled === FULFILLED) {
|
452 | fulfill(promise, value);
|
453 | } else if (settled === REJECTED) {
|
454 | reject(promise, value);
|
455 | }
|
456 | }
|
457 |
|
458 | function initializePromise(promise, resolver) {
|
459 | try {
|
460 | resolver(function resolvePromise(value) {
|
461 | resolve(promise, value);
|
462 | }, function rejectPromise(reason) {
|
463 | reject(promise, reason);
|
464 | });
|
465 | } catch (e) {
|
466 | reject(promise, e);
|
467 | }
|
468 | }
|
469 |
|
470 | var id = 0;
|
471 | function nextId() {
|
472 | return id++;
|
473 | }
|
474 |
|
475 | function makePromise(promise) {
|
476 | promise[PROMISE_ID] = id++;
|
477 | promise._state = undefined;
|
478 | promise._result = undefined;
|
479 | promise._subscribers = [];
|
480 | }
|
481 |
|
482 | function validationError() {
|
483 | return new Error('Array Methods must be provided an Array');
|
484 | }
|
485 |
|
486 | var Enumerator = function () {
|
487 | function Enumerator(Constructor, input) {
|
488 | this._instanceConstructor = Constructor;
|
489 | this.promise = new Constructor(noop);
|
490 |
|
491 | if (!this.promise[PROMISE_ID]) {
|
492 | makePromise(this.promise);
|
493 | }
|
494 |
|
495 | if (isArray(input)) {
|
496 | this.length = input.length;
|
497 | this._remaining = input.length;
|
498 |
|
499 | this._result = new Array(this.length);
|
500 |
|
501 | if (this.length === 0) {
|
502 | fulfill(this.promise, this._result);
|
503 | } else {
|
504 | this.length = this.length || 0;
|
505 | this._enumerate(input);
|
506 | if (this._remaining === 0) {
|
507 | fulfill(this.promise, this._result);
|
508 | }
|
509 | }
|
510 | } else {
|
511 | reject(this.promise, validationError());
|
512 | }
|
513 | }
|
514 |
|
515 | Enumerator.prototype._enumerate = function _enumerate(input) {
|
516 | for (var i = 0; this._state === PENDING && i < input.length; i++) {
|
517 | this._eachEntry(input[i], i);
|
518 | }
|
519 | };
|
520 |
|
521 | Enumerator.prototype._eachEntry = function _eachEntry(entry, i) {
|
522 | var c = this._instanceConstructor;
|
523 | var resolve$$1 = c.resolve;
|
524 |
|
525 |
|
526 | if (resolve$$1 === resolve$1) {
|
527 | var _then = getThen(entry);
|
528 |
|
529 | if (_then === then && entry._state !== PENDING) {
|
530 | this._settledAt(entry._state, i, entry._result);
|
531 | } else if (typeof _then !== 'function') {
|
532 | this._remaining--;
|
533 | this._result[i] = entry;
|
534 | } else if (c === Promise$1) {
|
535 | var promise = new c(noop);
|
536 | handleMaybeThenable(promise, entry, _then);
|
537 | this._willSettleAt(promise, i);
|
538 | } else {
|
539 | this._willSettleAt(new c(function (resolve$$1) {
|
540 | return resolve$$1(entry);
|
541 | }), i);
|
542 | }
|
543 | } else {
|
544 | this._willSettleAt(resolve$$1(entry), i);
|
545 | }
|
546 | };
|
547 |
|
548 | Enumerator.prototype._settledAt = function _settledAt(state, i, value) {
|
549 | var promise = this.promise;
|
550 |
|
551 |
|
552 | if (promise._state === PENDING) {
|
553 | this._remaining--;
|
554 |
|
555 | if (state === REJECTED) {
|
556 | reject(promise, value);
|
557 | } else {
|
558 | this._result[i] = value;
|
559 | }
|
560 | }
|
561 |
|
562 | if (this._remaining === 0) {
|
563 | fulfill(promise, this._result);
|
564 | }
|
565 | };
|
566 |
|
567 | Enumerator.prototype._willSettleAt = function _willSettleAt(promise, i) {
|
568 | var enumerator = this;
|
569 |
|
570 | subscribe(promise, undefined, function (value) {
|
571 | return enumerator._settledAt(FULFILLED, i, value);
|
572 | }, function (reason) {
|
573 | return enumerator._settledAt(REJECTED, i, reason);
|
574 | });
|
575 | };
|
576 |
|
577 | return Enumerator;
|
578 | }();
|
579 |
|
580 | /**
|
581 | `Promise.all` accepts an array of promises, and returns a new promise which
|
582 | is fulfilled with an array of fulfillment values for the passed promises, or
|
583 | rejected with the reason of the first passed promise to be rejected. It casts all
|
584 | elements of the passed iterable to promises as it runs this algorithm.
|
585 |
|
586 | Example:
|
587 |
|
588 | ```javascript
|
589 | let promise1 = resolve(1);
|
590 | let promise2 = resolve(2);
|
591 | let promise3 = resolve(3);
|
592 | let promises = [ promise1, promise2, promise3 ];
|
593 |
|
594 | Promise.all(promises).then(function(array){
|
595 | // The array here would be [ 1, 2, 3 ];
|
596 | });
|
597 | ```
|
598 |
|
599 | If any of the `promises` given to `all` are rejected, the first promise
|
600 | that is rejected will be given as an argument to the returned promises's
|
601 | rejection handler. For example:
|
602 |
|
603 | Example:
|
604 |
|
605 | ```javascript
|
606 | let promise1 = resolve(1);
|
607 | let promise2 = reject(new Error("2"));
|
608 | let promise3 = reject(new Error("3"));
|
609 | let promises = [ promise1, promise2, promise3 ];
|
610 |
|
611 | Promise.all(promises).then(function(array){
|
612 | // Code here never runs because there are rejected promises!
|
613 | }, function(error) {
|
614 | // error.message === "2"
|
615 | });
|
616 | ```
|
617 |
|
618 | @method all
|
619 | @static
|
620 | @param {Array} entries array of promises
|
621 | @param {String} label optional string for labeling the promise.
|
622 | Useful for tooling.
|
623 | @return {Promise} promise that is fulfilled when all `promises` have been
|
624 | fulfilled, or rejected if any of them become rejected.
|
625 | @static
|
626 | */
|
627 | function all(entries) {
|
628 | return new Enumerator(this, entries).promise;
|
629 | }
|
630 |
|
631 | /**
|
632 | `Promise.race` returns a new promise which is settled in the same way as the
|
633 | first passed promise to settle.
|
634 |
|
635 | Example:
|
636 |
|
637 | ```javascript
|
638 | let promise1 = new Promise(function(resolve, reject){
|
639 | setTimeout(function(){
|
640 | resolve('promise 1');
|
641 | }, 200);
|
642 | });
|
643 |
|
644 | let promise2 = new Promise(function(resolve, reject){
|
645 | setTimeout(function(){
|
646 | resolve('promise 2');
|
647 | }, 100);
|
648 | });
|
649 |
|
650 | Promise.race([promise1, promise2]).then(function(result){
|
651 | // result === 'promise 2' because it was resolved before promise1
|
652 | // was resolved.
|
653 | });
|
654 | ```
|
655 |
|
656 | `Promise.race` is deterministic in that only the state of the first
|
657 | settled promise matters. For example, even if other promises given to the
|
658 | `promises` array argument are resolved, but the first settled promise has
|
659 | become rejected before the other promises became fulfilled, the returned
|
660 | promise will become rejected:
|
661 |
|
662 | ```javascript
|
663 | let promise1 = new Promise(function(resolve, reject){
|
664 | setTimeout(function(){
|
665 | resolve('promise 1');
|
666 | }, 200);
|
667 | });
|
668 |
|
669 | let promise2 = new Promise(function(resolve, reject){
|
670 | setTimeout(function(){
|
671 | reject(new Error('promise 2'));
|
672 | }, 100);
|
673 | });
|
674 |
|
675 | Promise.race([promise1, promise2]).then(function(result){
|
676 | // Code here never runs
|
677 | }, function(reason){
|
678 | // reason.message === 'promise 2' because promise 2 became rejected before
|
679 | // promise 1 became fulfilled
|
680 | });
|
681 | ```
|
682 |
|
683 | An example real-world use case is implementing timeouts:
|
684 |
|
685 | ```javascript
|
686 | Promise.race([ajax('foo.json'), timeout(5000)])
|
687 | ```
|
688 |
|
689 | @method race
|
690 | @static
|
691 | @param {Array} promises array of promises to observe
|
692 | Useful for tooling.
|
693 | @return {Promise} a promise which settles in the same way as the first passed
|
694 | promise to settle.
|
695 | */
|
696 | function race(entries) {
|
697 | /*jshint validthis:true */
|
698 | var Constructor = this;
|
699 |
|
700 | if (!isArray(entries)) {
|
701 | return new Constructor(function (_, reject) {
|
702 | return reject(new TypeError('You must pass an array to race.'));
|
703 | });
|
704 | } else {
|
705 | return new Constructor(function (resolve, reject) {
|
706 | var length = entries.length;
|
707 | for (var i = 0; i < length; i++) {
|
708 | Constructor.resolve(entries[i]).then(resolve, reject);
|
709 | }
|
710 | });
|
711 | }
|
712 | }
|
713 |
|
714 | /**
|
715 | `Promise.reject` returns a promise rejected with the passed `reason`.
|
716 | It is shorthand for the following:
|
717 |
|
718 | ```javascript
|
719 | let promise = new Promise(function(resolve, reject){
|
720 | reject(new Error('WHOOPS'));
|
721 | });
|
722 |
|
723 | promise.then(function(value){
|
724 | // Code here doesn't run because the promise is rejected!
|
725 | }, function(reason){
|
726 | // reason.message === 'WHOOPS'
|
727 | });
|
728 | ```
|
729 |
|
730 | Instead of writing the above, your code now simply becomes the following:
|
731 |
|
732 | ```javascript
|
733 | let promise = Promise.reject(new Error('WHOOPS'));
|
734 |
|
735 | promise.then(function(value){
|
736 | // Code here doesn't run because the promise is rejected!
|
737 | }, function(reason){
|
738 | // reason.message === 'WHOOPS'
|
739 | });
|
740 | ```
|
741 |
|
742 | @method reject
|
743 | @static
|
744 | @param {Any} reason value that the returned promise will be rejected with.
|
745 | Useful for tooling.
|
746 | @return {Promise} a promise rejected with the given `reason`.
|
747 | */
|
748 | function reject$1(reason) {
|
749 | /*jshint validthis:true */
|
750 | var Constructor = this;
|
751 | var promise = new Constructor(noop);
|
752 | reject(promise, reason);
|
753 | return promise;
|
754 | }
|
755 |
|
756 | function needsResolver() {
|
757 | throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
|
758 | }
|
759 |
|
760 | function needsNew() {
|
761 | throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");
|
762 | }
|
763 |
|
764 | /**
|
765 | Promise objects represent the eventual result of an asynchronous operation. The
|
766 | primary way of interacting with a promise is through its `then` method, which
|
767 | registers callbacks to receive either a promise's eventual value or the reason
|
768 | why the promise cannot be fulfilled.
|
769 |
|
770 | Terminology
|
771 | -----------
|
772 |
|
773 | - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
|
774 | - `thenable` is an object or function that defines a `then` method.
|
775 | - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
|
776 | - `exception` is a value that is thrown using the throw statement.
|
777 | - `reason` is a value that indicates why a promise was rejected.
|
778 | - `settled` the final resting state of a promise, fulfilled or rejected.
|
779 |
|
780 | A promise can be in one of three states: pending, fulfilled, or rejected.
|
781 |
|
782 | Promises that are fulfilled have a fulfillment value and are in the fulfilled
|
783 | state. Promises that are rejected have a rejection reason and are in the
|
784 | rejected state. A fulfillment value is never a thenable.
|
785 |
|
786 | Promises can also be said to *resolve* a value. If this value is also a
|
787 | promise, then the original promise's settled state will match the value's
|
788 | settled state. So a promise that *resolves* a promise that rejects will
|
789 | itself reject, and a promise that *resolves* a promise that fulfills will
|
790 | itself fulfill.
|
791 |
|
792 |
|
793 | Basic Usage:
|
794 | ------------
|
795 |
|
796 | ```js
|
797 | let promise = new Promise(function(resolve, reject) {
|
798 | // on success
|
799 | resolve(value);
|
800 |
|
801 | // on failure
|
802 | reject(reason);
|
803 | });
|
804 |
|
805 | promise.then(function(value) {
|
806 | // on fulfillment
|
807 | }, function(reason) {
|
808 | // on rejection
|
809 | });
|
810 | ```
|
811 |
|
812 | Advanced Usage:
|
813 | ---------------
|
814 |
|
815 | Promises shine when abstracting away asynchronous interactions such as
|
816 | `XMLHttpRequest`s.
|
817 |
|
818 | ```js
|
819 | function getJSON(url) {
|
820 | return new Promise(function(resolve, reject){
|
821 | let xhr = new XMLHttpRequest();
|
822 |
|
823 | xhr.open('GET', url);
|
824 | xhr.onreadystatechange = handler;
|
825 | xhr.responseType = 'json';
|
826 | xhr.setRequestHeader('Accept', 'application/json');
|
827 | xhr.send();
|
828 |
|
829 | function handler() {
|
830 | if (this.readyState === this.DONE) {
|
831 | if (this.status === 200) {
|
832 | resolve(this.response);
|
833 | } else {
|
834 | reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
|
835 | }
|
836 | }
|
837 | };
|
838 | });
|
839 | }
|
840 |
|
841 | getJSON('/posts.json').then(function(json) {
|
842 | // on fulfillment
|
843 | }, function(reason) {
|
844 | // on rejection
|
845 | });
|
846 | ```
|
847 |
|
848 | Unlike callbacks, promises are great composable primitives.
|
849 |
|
850 | ```js
|
851 | Promise.all([
|
852 | getJSON('/posts'),
|
853 | getJSON('/comments')
|
854 | ]).then(function(values){
|
855 | values[0] // => postsJSON
|
856 | values[1] // => commentsJSON
|
857 |
|
858 | return values;
|
859 | });
|
860 | ```
|
861 |
|
862 | @class Promise
|
863 | @param {Function} resolver
|
864 | Useful for tooling.
|
865 | @constructor
|
866 | */
|
867 |
|
868 | var Promise$1 = function () {
|
869 | function Promise(resolver) {
|
870 | this[PROMISE_ID] = nextId();
|
871 | this._result = this._state = undefined;
|
872 | this._subscribers = [];
|
873 |
|
874 | if (noop !== resolver) {
|
875 | typeof resolver !== 'function' && needsResolver();
|
876 | this instanceof Promise ? initializePromise(this, resolver) : needsNew();
|
877 | }
|
878 | }
|
879 |
|
880 | /**
|
881 | The primary way of interacting with a promise is through its `then` method,
|
882 | which registers callbacks to receive either a promise's eventual value or the
|
883 | reason why the promise cannot be fulfilled.
|
884 | ```js
|
885 | findUser().then(function(user){
|
886 | // user is available
|
887 | }, function(reason){
|
888 | // user is unavailable, and you are given the reason why
|
889 | });
|
890 | ```
|
891 | Chaining
|
892 | --------
|
893 | The return value of `then` is itself a promise. This second, 'downstream'
|
894 | promise is resolved with the return value of the first promise's fulfillment
|
895 | or rejection handler, or rejected if the handler throws an exception.
|
896 | ```js
|
897 | findUser().then(function (user) {
|
898 | return user.name;
|
899 | }, function (reason) {
|
900 | return 'default name';
|
901 | }).then(function (userName) {
|
902 | // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
|
903 | // will be `'default name'`
|
904 | });
|
905 | findUser().then(function (user) {
|
906 | throw new Error('Found user, but still unhappy');
|
907 | }, function (reason) {
|
908 | throw new Error('`findUser` rejected and we're unhappy');
|
909 | }).then(function (value) {
|
910 | // never reached
|
911 | }, function (reason) {
|
912 | // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
|
913 | // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
|
914 | });
|
915 | ```
|
916 | If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
|
917 | ```js
|
918 | findUser().then(function (user) {
|
919 | throw new PedagogicalException('Upstream error');
|
920 | }).then(function (value) {
|
921 | // never reached
|
922 | }).then(function (value) {
|
923 | // never reached
|
924 | }, function (reason) {
|
925 | // The `PedgagocialException` is propagated all the way down to here
|
926 | });
|
927 | ```
|
928 | Assimilation
|
929 | ------------
|
930 | Sometimes the value you want to propagate to a downstream promise can only be
|
931 | retrieved asynchronously. This can be achieved by returning a promise in the
|
932 | fulfillment or rejection handler. The downstream promise will then be pending
|
933 | until the returned promise is settled. This is called *assimilation*.
|
934 | ```js
|
935 | findUser().then(function (user) {
|
936 | return findCommentsByAuthor(user);
|
937 | }).then(function (comments) {
|
938 | // The user's comments are now available
|
939 | });
|
940 | ```
|
941 | If the assimliated promise rejects, then the downstream promise will also reject.
|
942 | ```js
|
943 | findUser().then(function (user) {
|
944 | return findCommentsByAuthor(user);
|
945 | }).then(function (comments) {
|
946 | // If `findCommentsByAuthor` fulfills, we'll have the value here
|
947 | }, function (reason) {
|
948 | // If `findCommentsByAuthor` rejects, we'll have the reason here
|
949 | });
|
950 | ```
|
951 | Simple Example
|
952 | --------------
|
953 | Synchronous Example
|
954 | ```javascript
|
955 | let result;
|
956 | try {
|
957 | result = findResult();
|
958 | // success
|
959 | } catch(reason) {
|
960 | // failure
|
961 | }
|
962 | ```
|
963 | Errback Example
|
964 | ```js
|
965 | findResult(function(result, err){
|
966 | if (err) {
|
967 | // failure
|
968 | } else {
|
969 | // success
|
970 | }
|
971 | });
|
972 | ```
|
973 | Promise Example;
|
974 | ```javascript
|
975 | findResult().then(function(result){
|
976 | // success
|
977 | }, function(reason){
|
978 | // failure
|
979 | });
|
980 | ```
|
981 | Advanced Example
|
982 | --------------
|
983 | Synchronous Example
|
984 | ```javascript
|
985 | let author, books;
|
986 | try {
|
987 | author = findAuthor();
|
988 | books = findBooksByAuthor(author);
|
989 | // success
|
990 | } catch(reason) {
|
991 | // failure
|
992 | }
|
993 | ```
|
994 | Errback Example
|
995 | ```js
|
996 | function foundBooks(books) {
|
997 | }
|
998 | function failure(reason) {
|
999 | }
|
1000 | findAuthor(function(author, err){
|
1001 | if (err) {
|
1002 | failure(err);
|
1003 | // failure
|
1004 | } else {
|
1005 | try {
|
1006 | findBoooksByAuthor(author, function(books, err) {
|
1007 | if (err) {
|
1008 | failure(err);
|
1009 | } else {
|
1010 | try {
|
1011 | foundBooks(books);
|
1012 | } catch(reason) {
|
1013 | failure(reason);
|
1014 | }
|
1015 | }
|
1016 | });
|
1017 | } catch(error) {
|
1018 | failure(err);
|
1019 | }
|
1020 | // success
|
1021 | }
|
1022 | });
|
1023 | ```
|
1024 | Promise Example;
|
1025 | ```javascript
|
1026 | findAuthor().
|
1027 | then(findBooksByAuthor).
|
1028 | then(function(books){
|
1029 | // found books
|
1030 | }).catch(function(reason){
|
1031 | // something went wrong
|
1032 | });
|
1033 | ```
|
1034 | @method then
|
1035 | @param {Function} onFulfilled
|
1036 | @param {Function} onRejected
|
1037 | Useful for tooling.
|
1038 | @return {Promise}
|
1039 | */
|
1040 |
|
1041 | /**
|
1042 | `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
|
1043 | as the catch block of a try/catch statement.
|
1044 | ```js
|
1045 | function findAuthor(){
|
1046 | throw new Error('couldn't find that author');
|
1047 | }
|
1048 | // synchronous
|
1049 | try {
|
1050 | findAuthor();
|
1051 | } catch(reason) {
|
1052 | // something went wrong
|
1053 | }
|
1054 | // async with promises
|
1055 | findAuthor().catch(function(reason){
|
1056 | // something went wrong
|
1057 | });
|
1058 | ```
|
1059 | @method catch
|
1060 | @param {Function} onRejection
|
1061 | Useful for tooling.
|
1062 | @return {Promise}
|
1063 | */
|
1064 |
|
1065 |
|
1066 | Promise.prototype.catch = function _catch(onRejection) {
|
1067 | return this.then(null, onRejection);
|
1068 | };
|
1069 |
|
1070 | /**
|
1071 | `finally` will be invoked regardless of the promise's fate just as native
|
1072 | try/catch/finally behaves
|
1073 |
|
1074 | Synchronous example:
|
1075 |
|
1076 | ```js
|
1077 | findAuthor() {
|
1078 | if (Math.random() > 0.5) {
|
1079 | throw new Error();
|
1080 | }
|
1081 | return new Author();
|
1082 | }
|
1083 |
|
1084 | try {
|
1085 | return findAuthor(); // succeed or fail
|
1086 | } catch(error) {
|
1087 | return findOtherAuther();
|
1088 | } finally {
|
1089 | // always runs
|
1090 | // doesn't affect the return value
|
1091 | }
|
1092 | ```
|
1093 |
|
1094 | Asynchronous example:
|
1095 |
|
1096 | ```js
|
1097 | findAuthor().catch(function(reason){
|
1098 | return findOtherAuther();
|
1099 | }).finally(function(){
|
1100 | // author was either found, or not
|
1101 | });
|
1102 | ```
|
1103 |
|
1104 | @method finally
|
1105 | @param {Function} callback
|
1106 | @return {Promise}
|
1107 | */
|
1108 |
|
1109 |
|
1110 | Promise.prototype.finally = function _finally(callback) {
|
1111 | var promise = this;
|
1112 | var constructor = promise.constructor;
|
1113 |
|
1114 | if (isFunction(callback)) {
|
1115 | return promise.then(function (value) {
|
1116 | return constructor.resolve(callback()).then(function () {
|
1117 | return value;
|
1118 | });
|
1119 | }, function (reason) {
|
1120 | return constructor.resolve(callback()).then(function () {
|
1121 | throw reason;
|
1122 | });
|
1123 | });
|
1124 | }
|
1125 |
|
1126 | return promise.then(callback, callback);
|
1127 | };
|
1128 |
|
1129 | return Promise;
|
1130 | }();
|
1131 |
|
1132 | Promise$1.prototype.then = then;
|
1133 | Promise$1.all = all;
|
1134 | Promise$1.race = race;
|
1135 | Promise$1.resolve = resolve$1;
|
1136 | Promise$1.reject = reject$1;
|
1137 | Promise$1._setScheduler = setScheduler;
|
1138 | Promise$1._setAsap = setAsap;
|
1139 | Promise$1._asap = asap;
|
1140 |
|
1141 | /*global self*/
|
1142 | function polyfill() {
|
1143 | var local = void 0;
|
1144 |
|
1145 | if (typeof global !== 'undefined') {
|
1146 | local = global;
|
1147 | } else if (typeof self !== 'undefined') {
|
1148 | local = self;
|
1149 | } else {
|
1150 | try {
|
1151 | local = Function('return this')();
|
1152 | } catch (e) {
|
1153 | throw new Error('polyfill failed because global object is unavailable in this environment');
|
1154 | }
|
1155 | }
|
1156 |
|
1157 | var P = local.Promise;
|
1158 |
|
1159 | if (P) {
|
1160 | var promiseToString = null;
|
1161 | try {
|
1162 | promiseToString = Object.prototype.toString.call(P.resolve());
|
1163 | } catch (e) {
|
1164 | // silently ignored
|
1165 | }
|
1166 |
|
1167 | if (promiseToString === '[object Promise]' && !P.cast) {
|
1168 | return;
|
1169 | }
|
1170 | }
|
1171 |
|
1172 | local.Promise = Promise$1;
|
1173 | }
|
1174 |
|
1175 | // Strange compat..
|
1176 | Promise$1.polyfill = polyfill;
|
1177 | Promise$1.Promise = Promise$1;
|
1178 |
|
1179 | return Promise$1;
|
1180 |
|
1181 | })));
|
1182 |
|
1183 |
|
1184 |
|
1185 |
|
1186 |
|
1187 | }).call(this,_dereq_('_process'),typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
1188 |
|
1189 | },{"_process":3}],2:[function(_dereq_,module,exports){
|
1190 | // Copyright Joyent, Inc. and other Node contributors.
|
1191 | //
|
1192 | // Permission is hereby granted, free of charge, to any person obtaining a
|
1193 | // copy of this software and associated documentation files (the
|
1194 | // "Software"), to deal in the Software without restriction, including
|
1195 | // without limitation the rights to use, copy, modify, merge, publish,
|
1196 | // distribute, sublicense, and/or sell copies of the Software, and to permit
|
1197 | // persons to whom the Software is furnished to do so, subject to the
|
1198 | // following conditions:
|
1199 | //
|
1200 | // The above copyright notice and this permission notice shall be included
|
1201 | // in all copies or substantial portions of the Software.
|
1202 | //
|
1203 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
1204 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
1205 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
1206 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
1207 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
1208 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
1209 | // USE OR OTHER DEALINGS IN THE SOFTWARE.
|
1210 |
|
1211 | function EventEmitter() {
|
1212 | this._events = this._events || {};
|
1213 | this._maxListeners = this._maxListeners || undefined;
|
1214 | }
|
1215 | module.exports = EventEmitter;
|
1216 |
|
1217 | // Backwards-compat with node 0.10.x
|
1218 | EventEmitter.EventEmitter = EventEmitter;
|
1219 |
|
1220 | EventEmitter.prototype._events = undefined;
|
1221 | EventEmitter.prototype._maxListeners = undefined;
|
1222 |
|
1223 | // By default EventEmitters will print a warning if more than 10 listeners are
|
1224 | // added to it. This is a useful default which helps finding memory leaks.
|
1225 | EventEmitter.defaultMaxListeners = 10;
|
1226 |
|
1227 | // Obviously not all Emitters should be limited to 10. This function allows
|
1228 | // that to be increased. Set to zero for unlimited.
|
1229 | EventEmitter.prototype.setMaxListeners = function(n) {
|
1230 | if (!isNumber(n) || n < 0 || isNaN(n))
|
1231 | throw TypeError('n must be a positive number');
|
1232 | this._maxListeners = n;
|
1233 | return this;
|
1234 | };
|
1235 |
|
1236 | EventEmitter.prototype.emit = function(type) {
|
1237 | var er, handler, len, args, i, listeners;
|
1238 |
|
1239 | if (!this._events)
|
1240 | this._events = {};
|
1241 |
|
1242 | // If there is no 'error' event listener then throw.
|
1243 | if (type === 'error') {
|
1244 | if (!this._events.error ||
|
1245 | (isObject(this._events.error) && !this._events.error.length)) {
|
1246 | er = arguments[1];
|
1247 | if (er instanceof Error) {
|
1248 | throw er; // Unhandled 'error' event
|
1249 | } else {
|
1250 | // At least give some kind of context to the user
|
1251 | var err = new Error('Uncaught, unspecified "error" event. (' + er + ')');
|
1252 | err.context = er;
|
1253 | throw err;
|
1254 | }
|
1255 | }
|
1256 | }
|
1257 |
|
1258 | handler = this._events[type];
|
1259 |
|
1260 | if (isUndefined(handler))
|
1261 | return false;
|
1262 |
|
1263 | if (isFunction(handler)) {
|
1264 | switch (arguments.length) {
|
1265 | // fast cases
|
1266 | case 1:
|
1267 | handler.call(this);
|
1268 | break;
|
1269 | case 2:
|
1270 | handler.call(this, arguments[1]);
|
1271 | break;
|
1272 | case 3:
|
1273 | handler.call(this, arguments[1], arguments[2]);
|
1274 | break;
|
1275 | // slower
|
1276 | default:
|
1277 | args = Array.prototype.slice.call(arguments, 1);
|
1278 | handler.apply(this, args);
|
1279 | }
|
1280 | } else if (isObject(handler)) {
|
1281 | args = Array.prototype.slice.call(arguments, 1);
|
1282 | listeners = handler.slice();
|
1283 | len = listeners.length;
|
1284 | for (i = 0; i < len; i++)
|
1285 | listeners[i].apply(this, args);
|
1286 | }
|
1287 |
|
1288 | return true;
|
1289 | };
|
1290 |
|
1291 | EventEmitter.prototype.addListener = function(type, listener) {
|
1292 | var m;
|
1293 |
|
1294 | if (!isFunction(listener))
|
1295 | throw TypeError('listener must be a function');
|
1296 |
|
1297 | if (!this._events)
|
1298 | this._events = {};
|
1299 |
|
1300 | // To avoid recursion in the case that type === "newListener"! Before
|
1301 | // adding it to the listeners, first emit "newListener".
|
1302 | if (this._events.newListener)
|
1303 | this.emit('newListener', type,
|
1304 | isFunction(listener.listener) ?
|
1305 | listener.listener : listener);
|
1306 |
|
1307 | if (!this._events[type])
|
1308 | // Optimize the case of one listener. Don't need the extra array object.
|
1309 | this._events[type] = listener;
|
1310 | else if (isObject(this._events[type]))
|
1311 | // If we've already got an array, just append.
|
1312 | this._events[type].push(listener);
|
1313 | else
|
1314 | // Adding the second element, need to change to array.
|
1315 | this._events[type] = [this._events[type], listener];
|
1316 |
|
1317 | // Check for listener leak
|
1318 | if (isObject(this._events[type]) && !this._events[type].warned) {
|
1319 | if (!isUndefined(this._maxListeners)) {
|
1320 | m = this._maxListeners;
|
1321 | } else {
|
1322 | m = EventEmitter.defaultMaxListeners;
|
1323 | }
|
1324 |
|
1325 | if (m && m > 0 && this._events[type].length > m) {
|
1326 | this._events[type].warned = true;
|
1327 | console.error('(node) warning: possible EventEmitter memory ' +
|
1328 | 'leak detected. %d listeners added. ' +
|
1329 | 'Use emitter.setMaxListeners() to increase limit.',
|
1330 | this._events[type].length);
|
1331 | if (typeof console.trace === 'function') {
|
1332 | // not supported in IE 10
|
1333 | console.trace();
|
1334 | }
|
1335 | }
|
1336 | }
|
1337 |
|
1338 | return this;
|
1339 | };
|
1340 |
|
1341 | EventEmitter.prototype.on = EventEmitter.prototype.addListener;
|
1342 |
|
1343 | EventEmitter.prototype.once = function(type, listener) {
|
1344 | if (!isFunction(listener))
|
1345 | throw TypeError('listener must be a function');
|
1346 |
|
1347 | var fired = false;
|
1348 |
|
1349 | function g() {
|
1350 | this.removeListener(type, g);
|
1351 |
|
1352 | if (!fired) {
|
1353 | fired = true;
|
1354 | listener.apply(this, arguments);
|
1355 | }
|
1356 | }
|
1357 |
|
1358 | g.listener = listener;
|
1359 | this.on(type, g);
|
1360 |
|
1361 | return this;
|
1362 | };
|
1363 |
|
1364 | // emits a 'removeListener' event iff the listener was removed
|
1365 | EventEmitter.prototype.removeListener = function(type, listener) {
|
1366 | var list, position, length, i;
|
1367 |
|
1368 | if (!isFunction(listener))
|
1369 | throw TypeError('listener must be a function');
|
1370 |
|
1371 | if (!this._events || !this._events[type])
|
1372 | return this;
|
1373 |
|
1374 | list = this._events[type];
|
1375 | length = list.length;
|
1376 | position = -1;
|
1377 |
|
1378 | if (list === listener ||
|
1379 | (isFunction(list.listener) && list.listener === listener)) {
|
1380 | delete this._events[type];
|
1381 | if (this._events.removeListener)
|
1382 | this.emit('removeListener', type, listener);
|
1383 |
|
1384 | } else if (isObject(list)) {
|
1385 | for (i = length; i-- > 0;) {
|
1386 | if (list[i] === listener ||
|
1387 | (list[i].listener && list[i].listener === listener)) {
|
1388 | position = i;
|
1389 | break;
|
1390 | }
|
1391 | }
|
1392 |
|
1393 | if (position < 0)
|
1394 | return this;
|
1395 |
|
1396 | if (list.length === 1) {
|
1397 | list.length = 0;
|
1398 | delete this._events[type];
|
1399 | } else {
|
1400 | list.splice(position, 1);
|
1401 | }
|
1402 |
|
1403 | if (this._events.removeListener)
|
1404 | this.emit('removeListener', type, listener);
|
1405 | }
|
1406 |
|
1407 | return this;
|
1408 | };
|
1409 |
|
1410 | EventEmitter.prototype.removeAllListeners = function(type) {
|
1411 | var key, listeners;
|
1412 |
|
1413 | if (!this._events)
|
1414 | return this;
|
1415 |
|
1416 | // not listening for removeListener, no need to emit
|
1417 | if (!this._events.removeListener) {
|
1418 | if (arguments.length === 0)
|
1419 | this._events = {};
|
1420 | else if (this._events[type])
|
1421 | delete this._events[type];
|
1422 | return this;
|
1423 | }
|
1424 |
|
1425 | // emit removeListener for all listeners on all events
|
1426 | if (arguments.length === 0) {
|
1427 | for (key in this._events) {
|
1428 | if (key === 'removeListener') continue;
|
1429 | this.removeAllListeners(key);
|
1430 | }
|
1431 | this.removeAllListeners('removeListener');
|
1432 | this._events = {};
|
1433 | return this;
|
1434 | }
|
1435 |
|
1436 | listeners = this._events[type];
|
1437 |
|
1438 | if (isFunction(listeners)) {
|
1439 | this.removeListener(type, listeners);
|
1440 | } else if (listeners) {
|
1441 | // LIFO order
|
1442 | while (listeners.length)
|
1443 | this.removeListener(type, listeners[listeners.length - 1]);
|
1444 | }
|
1445 | delete this._events[type];
|
1446 |
|
1447 | return this;
|
1448 | };
|
1449 |
|
1450 | EventEmitter.prototype.listeners = function(type) {
|
1451 | var ret;
|
1452 | if (!this._events || !this._events[type])
|
1453 | ret = [];
|
1454 | else if (isFunction(this._events[type]))
|
1455 | ret = [this._events[type]];
|
1456 | else
|
1457 | ret = this._events[type].slice();
|
1458 | return ret;
|
1459 | };
|
1460 |
|
1461 | EventEmitter.prototype.listenerCount = function(type) {
|
1462 | if (this._events) {
|
1463 | var evlistener = this._events[type];
|
1464 |
|
1465 | if (isFunction(evlistener))
|
1466 | return 1;
|
1467 | else if (evlistener)
|
1468 | return evlistener.length;
|
1469 | }
|
1470 | return 0;
|
1471 | };
|
1472 |
|
1473 | EventEmitter.listenerCount = function(emitter, type) {
|
1474 | return emitter.listenerCount(type);
|
1475 | };
|
1476 |
|
1477 | function isFunction(arg) {
|
1478 | return typeof arg === 'function';
|
1479 | }
|
1480 |
|
1481 | function isNumber(arg) {
|
1482 | return typeof arg === 'number';
|
1483 | }
|
1484 |
|
1485 | function isObject(arg) {
|
1486 | return typeof arg === 'object' && arg !== null;
|
1487 | }
|
1488 |
|
1489 | function isUndefined(arg) {
|
1490 | return arg === void 0;
|
1491 | }
|
1492 |
|
1493 | },{}],3:[function(_dereq_,module,exports){
|
1494 | // shim for using process in browser
|
1495 | var process = module.exports = {};
|
1496 |
|
1497 | // cached from whatever global is present so that test runners that stub it
|
1498 | // don't break things. But we need to wrap it in a try catch in case it is
|
1499 | // wrapped in strict mode code which doesn't define any globals. It's inside a
|
1500 | // function because try/catches deoptimize in certain engines.
|
1501 |
|
1502 | var cachedSetTimeout;
|
1503 | var cachedClearTimeout;
|
1504 |
|
1505 | function defaultSetTimout() {
|
1506 | throw new Error('setTimeout has not been defined');
|
1507 | }
|
1508 | function defaultClearTimeout () {
|
1509 | throw new Error('clearTimeout has not been defined');
|
1510 | }
|
1511 | (function () {
|
1512 | try {
|
1513 | if (typeof setTimeout === 'function') {
|
1514 | cachedSetTimeout = setTimeout;
|
1515 | } else {
|
1516 | cachedSetTimeout = defaultSetTimout;
|
1517 | }
|
1518 | } catch (e) {
|
1519 | cachedSetTimeout = defaultSetTimout;
|
1520 | }
|
1521 | try {
|
1522 | if (typeof clearTimeout === 'function') {
|
1523 | cachedClearTimeout = clearTimeout;
|
1524 | } else {
|
1525 | cachedClearTimeout = defaultClearTimeout;
|
1526 | }
|
1527 | } catch (e) {
|
1528 | cachedClearTimeout = defaultClearTimeout;
|
1529 | }
|
1530 | } ())
|
1531 | function runTimeout(fun) {
|
1532 | if (cachedSetTimeout === setTimeout) {
|
1533 | //normal enviroments in sane situations
|
1534 | return setTimeout(fun, 0);
|
1535 | }
|
1536 | // if setTimeout wasn't available but was latter defined
|
1537 | if ((cachedSetTimeout === defaultSetTimout || !cachedSetTimeout) && setTimeout) {
|
1538 | cachedSetTimeout = setTimeout;
|
1539 | return setTimeout(fun, 0);
|
1540 | }
|
1541 | try {
|
1542 | // when when somebody has screwed with setTimeout but no I.E. maddness
|
1543 | return cachedSetTimeout(fun, 0);
|
1544 | } catch(e){
|
1545 | try {
|
1546 | // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
1547 | return cachedSetTimeout.call(null, fun, 0);
|
1548 | } catch(e){
|
1549 | // 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
|
1550 | return cachedSetTimeout.call(this, fun, 0);
|
1551 | }
|
1552 | }
|
1553 |
|
1554 |
|
1555 | }
|
1556 | function runClearTimeout(marker) {
|
1557 | if (cachedClearTimeout === clearTimeout) {
|
1558 | //normal enviroments in sane situations
|
1559 | return clearTimeout(marker);
|
1560 | }
|
1561 | // if clearTimeout wasn't available but was latter defined
|
1562 | if ((cachedClearTimeout === defaultClearTimeout || !cachedClearTimeout) && clearTimeout) {
|
1563 | cachedClearTimeout = clearTimeout;
|
1564 | return clearTimeout(marker);
|
1565 | }
|
1566 | try {
|
1567 | // when when somebody has screwed with setTimeout but no I.E. maddness
|
1568 | return cachedClearTimeout(marker);
|
1569 | } catch (e){
|
1570 | try {
|
1571 | // When we are in I.E. but the script has been evaled so I.E. doesn't trust the global object when called normally
|
1572 | return cachedClearTimeout.call(null, marker);
|
1573 | } catch (e){
|
1574 | // 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.
|
1575 | // Some versions of I.E. have different rules for clearTimeout vs setTimeout
|
1576 | return cachedClearTimeout.call(this, marker);
|
1577 | }
|
1578 | }
|
1579 |
|
1580 |
|
1581 |
|
1582 | }
|
1583 | var queue = [];
|
1584 | var draining = false;
|
1585 | var currentQueue;
|
1586 | var queueIndex = -1;
|
1587 |
|
1588 | function cleanUpNextTick() {
|
1589 | if (!draining || !currentQueue) {
|
1590 | return;
|
1591 | }
|
1592 | draining = false;
|
1593 | if (currentQueue.length) {
|
1594 | queue = currentQueue.concat(queue);
|
1595 | } else {
|
1596 | queueIndex = -1;
|
1597 | }
|
1598 | if (queue.length) {
|
1599 | drainQueue();
|
1600 | }
|
1601 | }
|
1602 |
|
1603 | function drainQueue() {
|
1604 | if (draining) {
|
1605 | return;
|
1606 | }
|
1607 | var timeout = runTimeout(cleanUpNextTick);
|
1608 | draining = true;
|
1609 |
|
1610 | var len = queue.length;
|
1611 | while(len) {
|
1612 | currentQueue = queue;
|
1613 | queue = [];
|
1614 | while (++queueIndex < len) {
|
1615 | if (currentQueue) {
|
1616 | currentQueue[queueIndex].run();
|
1617 | }
|
1618 | }
|
1619 | queueIndex = -1;
|
1620 | len = queue.length;
|
1621 | }
|
1622 | currentQueue = null;
|
1623 | draining = false;
|
1624 | runClearTimeout(timeout);
|
1625 | }
|
1626 |
|
1627 | process.nextTick = function (fun) {
|
1628 | var args = new Array(arguments.length - 1);
|
1629 | if (arguments.length > 1) {
|
1630 | for (var i = 1; i < arguments.length; i++) {
|
1631 | args[i - 1] = arguments[i];
|
1632 | }
|
1633 | }
|
1634 | queue.push(new Item(fun, args));
|
1635 | if (queue.length === 1 && !draining) {
|
1636 | runTimeout(drainQueue);
|
1637 | }
|
1638 | };
|
1639 |
|
1640 | // v8 likes predictible objects
|
1641 | function Item(fun, array) {
|
1642 | this.fun = fun;
|
1643 | this.array = array;
|
1644 | }
|
1645 | Item.prototype.run = function () {
|
1646 | this.fun.apply(null, this.array);
|
1647 | };
|
1648 | process.title = 'browser';
|
1649 | process.browser = true;
|
1650 | process.env = {};
|
1651 | process.argv = [];
|
1652 | process.version = ''; // empty string to avoid regexp issues
|
1653 | process.versions = {};
|
1654 |
|
1655 | function noop() {}
|
1656 |
|
1657 | process.on = noop;
|
1658 | process.addListener = noop;
|
1659 | process.once = noop;
|
1660 | process.off = noop;
|
1661 | process.removeListener = noop;
|
1662 | process.removeAllListeners = noop;
|
1663 | process.emit = noop;
|
1664 | process.prependListener = noop;
|
1665 | process.prependOnceListener = noop;
|
1666 |
|
1667 | process.listeners = function (name) { return [] }
|
1668 |
|
1669 | process.binding = function (name) {
|
1670 | throw new Error('process.binding is not supported');
|
1671 | };
|
1672 |
|
1673 | process.cwd = function () { return '/' };
|
1674 | process.chdir = function (dir) {
|
1675 | throw new Error('process.chdir is not supported');
|
1676 | };
|
1677 | process.umask = function() { return 0; };
|
1678 |
|
1679 | },{}],4:[function(_dereq_,module,exports){
|
1680 | var bundleFn = arguments[3];
|
1681 | var sources = arguments[4];
|
1682 | var cache = arguments[5];
|
1683 |
|
1684 | var stringify = JSON.stringify;
|
1685 |
|
1686 | module.exports = function (fn, options) {
|
1687 | var wkey;
|
1688 | var cacheKeys = Object.keys(cache);
|
1689 |
|
1690 | for (var i = 0, l = cacheKeys.length; i < l; i++) {
|
1691 | var key = cacheKeys[i];
|
1692 | var exp = cache[key].exports;
|
1693 | // Using babel as a transpiler to use esmodule, the export will always
|
1694 | // be an object with the default export as a property of it. To ensure
|
1695 | // the existing api and babel esmodule exports are both supported we
|
1696 | // check for both
|
1697 | if (exp === fn || exp && exp.default === fn) {
|
1698 | wkey = key;
|
1699 | break;
|
1700 | }
|
1701 | }
|
1702 |
|
1703 | if (!wkey) {
|
1704 | wkey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
|
1705 | var wcache = {};
|
1706 | for (var i = 0, l = cacheKeys.length; i < l; i++) {
|
1707 | var key = cacheKeys[i];
|
1708 | wcache[key] = key;
|
1709 | }
|
1710 | sources[wkey] = [
|
1711 | 'function(require,module,exports){' + fn + '(self); }',
|
1712 | wcache
|
1713 | ];
|
1714 | }
|
1715 | var skey = Math.floor(Math.pow(16, 8) * Math.random()).toString(16);
|
1716 |
|
1717 | var scache = {}; scache[wkey] = wkey;
|
1718 | sources[skey] = [
|
1719 | 'function(require,module,exports){' +
|
1720 | // try to call default if defined to also support babel esmodule exports
|
1721 | 'var f = require(' + stringify(wkey) + ');' +
|
1722 | '(f.default ? f.default : f)(self);' +
|
1723 | '}',
|
1724 | scache
|
1725 | ];
|
1726 |
|
1727 | var workerSources = {};
|
1728 | resolveSources(skey);
|
1729 |
|
1730 | function resolveSources(key) {
|
1731 | workerSources[key] = true;
|
1732 |
|
1733 | for (var depPath in sources[key][1]) {
|
1734 | var depKey = sources[key][1][depPath];
|
1735 | if (!workerSources[depKey]) {
|
1736 | resolveSources(depKey);
|
1737 | }
|
1738 | }
|
1739 | }
|
1740 |
|
1741 | var src = '(' + bundleFn + ')({'
|
1742 | + Object.keys(workerSources).map(function (key) {
|
1743 | return stringify(key) + ':['
|
1744 | + sources[key][0]
|
1745 | + ',' + stringify(sources[key][1]) + ']'
|
1746 | ;
|
1747 | }).join(',')
|
1748 | + '},{},[' + stringify(skey) + '])'
|
1749 | ;
|
1750 |
|
1751 | var URL = window.URL || window.webkitURL || window.mozURL || window.msURL;
|
1752 |
|
1753 | var blob = new Blob([src], { type: 'text/javascript' });
|
1754 | if (options && options.bare) { return blob; }
|
1755 | var workerUrl = URL.createObjectURL(blob);
|
1756 | var worker = new Worker(workerUrl);
|
1757 | worker.objectURL = workerUrl;
|
1758 | return worker;
|
1759 | };
|
1760 |
|
1761 | },{}],5:[function(_dereq_,module,exports){
|
1762 | ;
|
1763 |
|
1764 | Object.defineProperty(exports, "__esModule", {
|
1765 | value: true
|
1766 | });
|
1767 | exports.createDefaultConfig = createDefaultConfig;
|
1768 | /*
|
1769 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
1770 | *
|
1771 | * @author zheng qian <xqq@xqq.im>
|
1772 | *
|
1773 | * Licensed under the Apache License, Version 2.0 (the "License");
|
1774 | * you may not use this file except in compliance with the License.
|
1775 | * You may obtain a copy of the License at
|
1776 | *
|
1777 | * http://www.apache.org/licenses/LICENSE-2.0
|
1778 | *
|
1779 | * Unless required by applicable law or agreed to in writing, software
|
1780 | * distributed under the License is distributed on an "AS IS" BASIS,
|
1781 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
1782 | * See the License for the specific language governing permissions and
|
1783 | * limitations under the License.
|
1784 | */
|
1785 |
|
1786 | var defaultConfig = exports.defaultConfig = {
|
1787 | enableWorker: false,
|
1788 | enableStashBuffer: true,
|
1789 | stashInitialSize: undefined,
|
1790 |
|
1791 | isLive: false,
|
1792 |
|
1793 | lazyLoad: true,
|
1794 | lazyLoadMaxDuration: 3 * 60,
|
1795 | lazyLoadRecoverDuration: 30,
|
1796 | deferLoadAfterSourceOpen: true,
|
1797 |
|
1798 | // autoCleanupSourceBuffer: default as false, leave unspecified
|
1799 | autoCleanupMaxBackwardDuration: 3 * 60,
|
1800 | autoCleanupMinBackwardDuration: 2 * 60,
|
1801 |
|
1802 | statisticsInfoReportInterval: 600,
|
1803 |
|
1804 | fixAudioTimestampGap: true,
|
1805 |
|
1806 | accurateSeek: false,
|
1807 | seekType: 'range', // [range, param, custom]
|
1808 | seekParamStart: 'bstart',
|
1809 | seekParamEnd: 'bend',
|
1810 | rangeLoadZeroStart: false,
|
1811 | customSeekHandler: undefined,
|
1812 | reuseRedirectedURL: false,
|
1813 | // referrerPolicy: leave as unspecified
|
1814 |
|
1815 | headers: undefined,
|
1816 | customLoader: undefined
|
1817 | };
|
1818 |
|
1819 | function createDefaultConfig() {
|
1820 | return Object.assign({}, defaultConfig);
|
1821 | }
|
1822 |
|
1823 | },{}],6:[function(_dereq_,module,exports){
|
1824 | ;
|
1825 |
|
1826 | Object.defineProperty(exports, "__esModule", {
|
1827 | value: true
|
1828 | });
|
1829 |
|
1830 | 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; }; }(); /*
|
1831 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
1832 | *
|
1833 | * @author zheng qian <xqq@xqq.im>
|
1834 | *
|
1835 | * Licensed under the Apache License, Version 2.0 (the "License");
|
1836 | * you may not use this file except in compliance with the License.
|
1837 | * You may obtain a copy of the License at
|
1838 | *
|
1839 | * http://www.apache.org/licenses/LICENSE-2.0
|
1840 | *
|
1841 | * Unless required by applicable law or agreed to in writing, software
|
1842 | * distributed under the License is distributed on an "AS IS" BASIS,
|
1843 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
1844 | * See the License for the specific language governing permissions and
|
1845 | * limitations under the License.
|
1846 | */
|
1847 |
|
1848 | var _ioController = _dereq_('../io/io-controller.js');
|
1849 |
|
1850 | var _ioController2 = _interopRequireDefault(_ioController);
|
1851 |
|
1852 | var _config = _dereq_('../config.js');
|
1853 |
|
1854 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
1855 |
|
1856 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
1857 |
|
1858 | var Features = function () {
|
1859 | function Features() {
|
1860 | _classCallCheck(this, Features);
|
1861 | }
|
1862 |
|
1863 | _createClass(Features, null, [{
|
1864 | key: 'supportMSEH264Playback',
|
1865 | value: function supportMSEH264Playback() {
|
1866 | return window.MediaSource && window.MediaSource.isTypeSupported('video/mp4; codecs="avc1.42E01E,mp4a.40.2"');
|
1867 | }
|
1868 | }, {
|
1869 | key: 'supportNetworkStreamIO',
|
1870 | value: function supportNetworkStreamIO() {
|
1871 | var ioctl = new _ioController2.default({}, (0, _config.createDefaultConfig)());
|
1872 | var loaderType = ioctl.loaderType;
|
1873 | ioctl.destroy();
|
1874 | return loaderType == 'fetch-stream-loader' || loaderType == 'xhr-moz-chunked-loader';
|
1875 | }
|
1876 | }, {
|
1877 | key: 'getNetworkLoaderTypeName',
|
1878 | value: function getNetworkLoaderTypeName() {
|
1879 | var ioctl = new _ioController2.default({}, (0, _config.createDefaultConfig)());
|
1880 | var loaderType = ioctl.loaderType;
|
1881 | ioctl.destroy();
|
1882 | return loaderType;
|
1883 | }
|
1884 | }, {
|
1885 | key: 'supportNativeMediaPlayback',
|
1886 | value: function supportNativeMediaPlayback(mimeType) {
|
1887 | if (Features.videoElement == undefined) {
|
1888 | Features.videoElement = window.document.createElement('video');
|
1889 | }
|
1890 | var canPlay = Features.videoElement.canPlayType(mimeType);
|
1891 | return canPlay === 'probably' || canPlay == 'maybe';
|
1892 | }
|
1893 | }, {
|
1894 | key: 'getFeatureList',
|
1895 | value: function getFeatureList() {
|
1896 | var features = {
|
1897 | mseFlvPlayback: false,
|
1898 | mseLiveFlvPlayback: false,
|
1899 | networkStreamIO: false,
|
1900 | networkLoaderName: '',
|
1901 | nativeMP4H264Playback: false,
|
1902 | nativeWebmVP8Playback: false,
|
1903 | nativeWebmVP9Playback: false
|
1904 | };
|
1905 |
|
1906 | features.mseFlvPlayback = Features.supportMSEH264Playback();
|
1907 | features.networkStreamIO = Features.supportNetworkStreamIO();
|
1908 | features.networkLoaderName = Features.getNetworkLoaderTypeName();
|
1909 | features.mseLiveFlvPlayback = features.mseFlvPlayback && features.networkStreamIO;
|
1910 | features.nativeMP4H264Playback = Features.supportNativeMediaPlayback('video/mp4; codecs="avc1.42001E, mp4a.40.2"');
|
1911 | features.nativeWebmVP8Playback = Features.supportNativeMediaPlayback('video/webm; codecs="vp8.0, vorbis"');
|
1912 | features.nativeWebmVP9Playback = Features.supportNativeMediaPlayback('video/webm; codecs="vp9"');
|
1913 |
|
1914 | return features;
|
1915 | }
|
1916 | }]);
|
1917 |
|
1918 | return Features;
|
1919 | }();
|
1920 |
|
1921 | exports.default = Features;
|
1922 |
|
1923 | },{"../config.js":5,"../io/io-controller.js":23}],7:[function(_dereq_,module,exports){
|
1924 | ;
|
1925 |
|
1926 | Object.defineProperty(exports, "__esModule", {
|
1927 | value: true
|
1928 | });
|
1929 |
|
1930 | 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; }; }();
|
1931 |
|
1932 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
1933 |
|
1934 | /*
|
1935 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
1936 | *
|
1937 | * @author zheng qian <xqq@xqq.im>
|
1938 | *
|
1939 | * Licensed under the Apache License, Version 2.0 (the "License");
|
1940 | * you may not use this file except in compliance with the License.
|
1941 | * You may obtain a copy of the License at
|
1942 | *
|
1943 | * http://www.apache.org/licenses/LICENSE-2.0
|
1944 | *
|
1945 | * Unless required by applicable law or agreed to in writing, software
|
1946 | * distributed under the License is distributed on an "AS IS" BASIS,
|
1947 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
1948 | * See the License for the specific language governing permissions and
|
1949 | * limitations under the License.
|
1950 | */
|
1951 |
|
1952 | var MediaInfo = function () {
|
1953 | function MediaInfo() {
|
1954 | _classCallCheck(this, MediaInfo);
|
1955 |
|
1956 | this.mimeType = null;
|
1957 | this.duration = null;
|
1958 |
|
1959 | this.hasAudio = null;
|
1960 | this.hasVideo = null;
|
1961 | this.audioCodec = null;
|
1962 | this.videoCodec = null;
|
1963 | this.audioDataRate = null;
|
1964 | this.videoDataRate = null;
|
1965 |
|
1966 | this.audioSampleRate = null;
|
1967 | this.audioChannelCount = null;
|
1968 |
|
1969 | this.width = null;
|
1970 | this.height = null;
|
1971 | this.fps = null;
|
1972 | this.profile = null;
|
1973 | this.level = null;
|
1974 | this.refFrames = null;
|
1975 | this.chromaFormat = null;
|
1976 | this.sarNum = null;
|
1977 | this.sarDen = null;
|
1978 |
|
1979 | this.metadata = null;
|
1980 | this.segments = null; // MediaInfo[]
|
1981 | this.segmentCount = null;
|
1982 | this.hasKeyframesIndex = null;
|
1983 | this.keyframesIndex = null;
|
1984 | }
|
1985 |
|
1986 | _createClass(MediaInfo, [{
|
1987 | key: "isComplete",
|
1988 | value: function isComplete() {
|
1989 | var audioInfoComplete = this.hasAudio === false || this.hasAudio === true && this.audioCodec != null && this.audioSampleRate != null && this.audioChannelCount != null;
|
1990 |
|
1991 | 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;
|
1992 |
|
1993 | // keyframesIndex may not be present
|
1994 | return this.mimeType != null && this.duration != null && this.metadata != null && this.hasKeyframesIndex != null && audioInfoComplete && videoInfoComplete;
|
1995 | }
|
1996 | }, {
|
1997 | key: "isSeekable",
|
1998 | value: function isSeekable() {
|
1999 | return this.hasKeyframesIndex === true;
|
2000 | }
|
2001 | }, {
|
2002 | key: "getNearestKeyframe",
|
2003 | value: function getNearestKeyframe(milliseconds) {
|
2004 | if (this.keyframesIndex == null) {
|
2005 | return null;
|
2006 | }
|
2007 |
|
2008 | var table = this.keyframesIndex;
|
2009 | var keyframeIdx = this._search(table.times, milliseconds);
|
2010 |
|
2011 | return {
|
2012 | index: keyframeIdx,
|
2013 | milliseconds: table.times[keyframeIdx],
|
2014 | fileposition: table.filepositions[keyframeIdx]
|
2015 | };
|
2016 | }
|
2017 | }, {
|
2018 | key: "_search",
|
2019 | value: function _search(list, value) {
|
2020 | var idx = 0;
|
2021 |
|
2022 | var last = list.length - 1;
|
2023 | var mid = 0;
|
2024 | var lbound = 0;
|
2025 | var ubound = last;
|
2026 |
|
2027 | if (value < list[0]) {
|
2028 | idx = 0;
|
2029 | lbound = ubound + 1; // skip search
|
2030 | }
|
2031 |
|
2032 | while (lbound <= ubound) {
|
2033 | mid = lbound + Math.floor((ubound - lbound) / 2);
|
2034 | if (mid === last || value >= list[mid] && value < list[mid + 1]) {
|
2035 | idx = mid;
|
2036 | break;
|
2037 | } else if (list[mid] < value) {
|
2038 | lbound = mid + 1;
|
2039 | } else {
|
2040 | ubound = mid - 1;
|
2041 | }
|
2042 | }
|
2043 |
|
2044 | return idx;
|
2045 | }
|
2046 | }]);
|
2047 |
|
2048 | return MediaInfo;
|
2049 | }();
|
2050 |
|
2051 | exports.default = MediaInfo;
|
2052 |
|
2053 | },{}],8:[function(_dereq_,module,exports){
|
2054 | ;
|
2055 |
|
2056 | Object.defineProperty(exports, "__esModule", {
|
2057 | value: true
|
2058 | });
|
2059 |
|
2060 | 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; }; }();
|
2061 |
|
2062 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
2063 |
|
2064 | /*
|
2065 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
2066 | *
|
2067 | * @author zheng qian <xqq@xqq.im>
|
2068 | *
|
2069 | * Licensed under the Apache License, Version 2.0 (the "License");
|
2070 | * you may not use this file except in compliance with the License.
|
2071 | * You may obtain a copy of the License at
|
2072 | *
|
2073 | * http://www.apache.org/licenses/LICENSE-2.0
|
2074 | *
|
2075 | * Unless required by applicable law or agreed to in writing, software
|
2076 | * distributed under the License is distributed on an "AS IS" BASIS,
|
2077 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
2078 | * See the License for the specific language governing permissions and
|
2079 | * limitations under the License.
|
2080 | */
|
2081 |
|
2082 | // Represents an media sample (audio / video)
|
2083 | var SampleInfo = exports.SampleInfo = function SampleInfo(dts, pts, duration, originalDts, isSync) {
|
2084 | _classCallCheck(this, SampleInfo);
|
2085 |
|
2086 | this.dts = dts;
|
2087 | this.pts = pts;
|
2088 | this.duration = duration;
|
2089 | this.originalDts = originalDts;
|
2090 | this.isSyncPoint = isSync;
|
2091 | this.fileposition = null;
|
2092 | };
|
2093 |
|
2094 | // Media Segment concept is defined in Media Source Extensions spec.
|
2095 | // Particularly in ISO BMFF format, an Media Segment contains a moof box followed by a mdat box.
|
2096 |
|
2097 |
|
2098 | var MediaSegmentInfo = exports.MediaSegmentInfo = function () {
|
2099 | function MediaSegmentInfo() {
|
2100 | _classCallCheck(this, MediaSegmentInfo);
|
2101 |
|
2102 | this.beginDts = 0;
|
2103 | this.endDts = 0;
|
2104 | this.beginPts = 0;
|
2105 | this.endPts = 0;
|
2106 | this.originalBeginDts = 0;
|
2107 | this.originalEndDts = 0;
|
2108 | this.syncPoints = []; // SampleInfo[n], for video IDR frames only
|
2109 | this.firstSample = null; // SampleInfo
|
2110 | this.lastSample = null; // SampleInfo
|
2111 | }
|
2112 |
|
2113 | _createClass(MediaSegmentInfo, [{
|
2114 | key: "appendSyncPoint",
|
2115 | value: function appendSyncPoint(sampleInfo) {
|
2116 | // also called Random Access Point
|
2117 | sampleInfo.isSyncPoint = true;
|
2118 | this.syncPoints.push(sampleInfo);
|
2119 | }
|
2120 | }]);
|
2121 |
|
2122 | return MediaSegmentInfo;
|
2123 | }();
|
2124 |
|
2125 | // Ordered list for recording video IDR frames, sorted by originalDts
|
2126 |
|
2127 |
|
2128 | var IDRSampleList = exports.IDRSampleList = function () {
|
2129 | function IDRSampleList() {
|
2130 | _classCallCheck(this, IDRSampleList);
|
2131 |
|
2132 | this._list = [];
|
2133 | }
|
2134 |
|
2135 | _createClass(IDRSampleList, [{
|
2136 | key: "clear",
|
2137 | value: function clear() {
|
2138 | this._list = [];
|
2139 | }
|
2140 | }, {
|
2141 | key: "appendArray",
|
2142 | value: function appendArray(syncPoints) {
|
2143 | var list = this._list;
|
2144 |
|
2145 | if (syncPoints.length === 0) {
|
2146 | return;
|
2147 | }
|
2148 |
|
2149 | if (list.length > 0 && syncPoints[0].originalDts < list[list.length - 1].originalDts) {
|
2150 | this.clear();
|
2151 | }
|
2152 |
|
2153 | Array.prototype.push.apply(list, syncPoints);
|
2154 | }
|
2155 | }, {
|
2156 | key: "getLastSyncPointBeforeDts",
|
2157 | value: function getLastSyncPointBeforeDts(dts) {
|
2158 | if (this._list.length == 0) {
|
2159 | return null;
|
2160 | }
|
2161 |
|
2162 | var list = this._list;
|
2163 | var idx = 0;
|
2164 | var last = list.length - 1;
|
2165 | var mid = 0;
|
2166 | var lbound = 0;
|
2167 | var ubound = last;
|
2168 |
|
2169 | if (dts < list[0].dts) {
|
2170 | idx = 0;
|
2171 | lbound = ubound + 1;
|
2172 | }
|
2173 |
|
2174 | while (lbound <= ubound) {
|
2175 | mid = lbound + Math.floor((ubound - lbound) / 2);
|
2176 | if (mid === last || dts >= list[mid].dts && dts < list[mid + 1].dts) {
|
2177 | idx = mid;
|
2178 | break;
|
2179 | } else if (list[mid].dts < dts) {
|
2180 | lbound = mid + 1;
|
2181 | } else {
|
2182 | ubound = mid - 1;
|
2183 | }
|
2184 | }
|
2185 | return this._list[idx];
|
2186 | }
|
2187 | }]);
|
2188 |
|
2189 | return IDRSampleList;
|
2190 | }();
|
2191 |
|
2192 | // Data structure for recording information of media segments in single track.
|
2193 |
|
2194 |
|
2195 | var MediaSegmentInfoList = exports.MediaSegmentInfoList = function () {
|
2196 | function MediaSegmentInfoList(type) {
|
2197 | _classCallCheck(this, MediaSegmentInfoList);
|
2198 |
|
2199 | this._type = type;
|
2200 | this._list = [];
|
2201 | this._lastAppendLocation = -1; // cached last insert location
|
2202 | }
|
2203 |
|
2204 | _createClass(MediaSegmentInfoList, [{
|
2205 | key: "isEmpty",
|
2206 | value: function isEmpty() {
|
2207 | return this._list.length === 0;
|
2208 | }
|
2209 | }, {
|
2210 | key: "clear",
|
2211 | value: function clear() {
|
2212 | this._list = [];
|
2213 | this._lastAppendLocation = -1;
|
2214 | }
|
2215 | }, {
|
2216 | key: "_searchNearestSegmentBefore",
|
2217 | value: function _searchNearestSegmentBefore(originalBeginDts) {
|
2218 | var list = this._list;
|
2219 | if (list.length === 0) {
|
2220 | return -2;
|
2221 | }
|
2222 | var last = list.length - 1;
|
2223 | var mid = 0;
|
2224 | var lbound = 0;
|
2225 | var ubound = last;
|
2226 |
|
2227 | var idx = 0;
|
2228 |
|
2229 | if (originalBeginDts < list[0].originalBeginDts) {
|
2230 | idx = -1;
|
2231 | return idx;
|
2232 | }
|
2233 |
|
2234 | while (lbound <= ubound) {
|
2235 | mid = lbound + Math.floor((ubound - lbound) / 2);
|
2236 | if (mid === last || originalBeginDts > list[mid].lastSample.originalDts && originalBeginDts < list[mid + 1].originalBeginDts) {
|
2237 | idx = mid;
|
2238 | break;
|
2239 | } else if (list[mid].originalBeginDts < originalBeginDts) {
|
2240 | lbound = mid + 1;
|
2241 | } else {
|
2242 | ubound = mid - 1;
|
2243 | }
|
2244 | }
|
2245 | return idx;
|
2246 | }
|
2247 | }, {
|
2248 | key: "_searchNearestSegmentAfter",
|
2249 | value: function _searchNearestSegmentAfter(originalBeginDts) {
|
2250 | return this._searchNearestSegmentBefore(originalBeginDts) + 1;
|
2251 | }
|
2252 | }, {
|
2253 | key: "append",
|
2254 | value: function append(mediaSegmentInfo) {
|
2255 | var list = this._list;
|
2256 | var msi = mediaSegmentInfo;
|
2257 | var lastAppendIdx = this._lastAppendLocation;
|
2258 | var insertIdx = 0;
|
2259 |
|
2260 | 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)) {
|
2261 | insertIdx = lastAppendIdx + 1; // use cached location idx
|
2262 | } else {
|
2263 | if (list.length > 0) {
|
2264 | insertIdx = this._searchNearestSegmentBefore(msi.originalBeginDts) + 1;
|
2265 | }
|
2266 | }
|
2267 |
|
2268 | this._lastAppendLocation = insertIdx;
|
2269 | this._list.splice(insertIdx, 0, msi);
|
2270 | }
|
2271 | }, {
|
2272 | key: "getLastSegmentBefore",
|
2273 | value: function getLastSegmentBefore(originalBeginDts) {
|
2274 | var idx = this._searchNearestSegmentBefore(originalBeginDts);
|
2275 | if (idx >= 0) {
|
2276 | return this._list[idx];
|
2277 | } else {
|
2278 | // -1
|
2279 | return null;
|
2280 | }
|
2281 | }
|
2282 | }, {
|
2283 | key: "getLastSampleBefore",
|
2284 | value: function getLastSampleBefore(originalBeginDts) {
|
2285 | var segment = this.getLastSegmentBefore(originalBeginDts);
|
2286 | if (segment != null) {
|
2287 | return segment.lastSample;
|
2288 | } else {
|
2289 | return null;
|
2290 | }
|
2291 | }
|
2292 | }, {
|
2293 | key: "getLastSyncPointBefore",
|
2294 | value: function getLastSyncPointBefore(originalBeginDts) {
|
2295 | var segmentIdx = this._searchNearestSegmentBefore(originalBeginDts);
|
2296 | var syncPoints = this._list[segmentIdx].syncPoints;
|
2297 | while (syncPoints.length === 0 && segmentIdx > 0) {
|
2298 | segmentIdx--;
|
2299 | syncPoints = this._list[segmentIdx].syncPoints;
|
2300 | }
|
2301 | if (syncPoints.length > 0) {
|
2302 | return syncPoints[syncPoints.length - 1];
|
2303 | } else {
|
2304 | return null;
|
2305 | }
|
2306 | }
|
2307 | }, {
|
2308 | key: "type",
|
2309 | get: function get() {
|
2310 | return this._type;
|
2311 | }
|
2312 | }, {
|
2313 | key: "length",
|
2314 | get: function get() {
|
2315 | return this._list.length;
|
2316 | }
|
2317 | }]);
|
2318 |
|
2319 | return MediaSegmentInfoList;
|
2320 | }();
|
2321 |
|
2322 | },{}],9:[function(_dereq_,module,exports){
|
2323 | ;
|
2324 |
|
2325 | Object.defineProperty(exports, "__esModule", {
|
2326 | value: true
|
2327 | });
|
2328 |
|
2329 | 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; }; }(); /*
|
2330 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
2331 | *
|
2332 | * @author zheng qian <xqq@xqq.im>
|
2333 | *
|
2334 | * Licensed under the Apache License, Version 2.0 (the "License");
|
2335 | * you may not use this file except in compliance with the License.
|
2336 | * You may obtain a copy of the License at
|
2337 | *
|
2338 | * http://www.apache.org/licenses/LICENSE-2.0
|
2339 | *
|
2340 | * Unless required by applicable law or agreed to in writing, software
|
2341 | * distributed under the License is distributed on an "AS IS" BASIS,
|
2342 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
2343 | * See the License for the specific language governing permissions and
|
2344 | * limitations under the License.
|
2345 | */
|
2346 |
|
2347 | var _events = _dereq_('events');
|
2348 |
|
2349 | var _events2 = _interopRequireDefault(_events);
|
2350 |
|
2351 | var _logger = _dereq_('../utils/logger.js');
|
2352 |
|
2353 | var _logger2 = _interopRequireDefault(_logger);
|
2354 |
|
2355 | var _browser = _dereq_('../utils/browser.js');
|
2356 |
|
2357 | var _browser2 = _interopRequireDefault(_browser);
|
2358 |
|
2359 | var _mseEvents = _dereq_('./mse-events.js');
|
2360 |
|
2361 | var _mseEvents2 = _interopRequireDefault(_mseEvents);
|
2362 |
|
2363 | var _mediaSegmentInfo = _dereq_('./media-segment-info.js');
|
2364 |
|
2365 | var _exception = _dereq_('../utils/exception.js');
|
2366 |
|
2367 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
2368 |
|
2369 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
2370 |
|
2371 | // Media Source Extensions controller
|
2372 | var MSEController = function () {
|
2373 | function MSEController(config) {
|
2374 | _classCallCheck(this, MSEController);
|
2375 |
|
2376 | this.TAG = 'MSEController';
|
2377 |
|
2378 | this._config = config;
|
2379 | this._emitter = new _events2.default();
|
2380 |
|
2381 | if (this._config.isLive && this._config.autoCleanupSourceBuffer == undefined) {
|
2382 | // For live stream, do auto cleanup by default
|
2383 | this._config.autoCleanupSourceBuffer = true;
|
2384 | }
|
2385 |
|
2386 | this.e = {
|
2387 | onSourceOpen: this._onSourceOpen.bind(this),
|
2388 | onSourceEnded: this._onSourceEnded.bind(this),
|
2389 | onSourceClose: this._onSourceClose.bind(this),
|
2390 | onSourceBufferError: this._onSourceBufferError.bind(this),
|
2391 | onSourceBufferUpdateEnd: this._onSourceBufferUpdateEnd.bind(this)
|
2392 | };
|
2393 |
|
2394 | this._mediaSource = null;
|
2395 | this._mediaSourceObjectURL = null;
|
2396 | this._mediaElement = null;
|
2397 |
|
2398 | this._isBufferFull = false;
|
2399 | this._hasPendingEos = false;
|
2400 |
|
2401 | this._requireSetMediaDuration = false;
|
2402 | this._pendingMediaDuration = 0;
|
2403 |
|
2404 | this._pendingSourceBufferInit = [];
|
2405 | this._mimeTypes = {
|
2406 | video: null,
|
2407 | audio: null
|
2408 | };
|
2409 | this._sourceBuffers = {
|
2410 | video: null,
|
2411 | audio: null
|
2412 | };
|
2413 | this._lastInitSegments = {
|
2414 | video: null,
|
2415 | audio: null
|
2416 | };
|
2417 | this._pendingSegments = {
|
2418 | video: [],
|
2419 | audio: []
|
2420 | };
|
2421 | this._pendingRemoveRanges = {
|
2422 | video: [],
|
2423 | audio: []
|
2424 | };
|
2425 | this._idrList = new _mediaSegmentInfo.IDRSampleList();
|
2426 | }
|
2427 |
|
2428 | _createClass(MSEController, [{
|
2429 | key: 'destroy',
|
2430 | value: function destroy() {
|
2431 | if (this._mediaElement || this._mediaSource) {
|
2432 | this.detachMediaElement();
|
2433 | }
|
2434 | this.e = null;
|
2435 | this._emitter.removeAllListeners();
|
2436 | this._emitter = null;
|
2437 | }
|
2438 | }, {
|
2439 | key: 'on',
|
2440 | value: function on(event, listener) {
|
2441 | this._emitter.addListener(event, listener);
|
2442 | }
|
2443 | }, {
|
2444 | key: 'off',
|
2445 | value: function off(event, listener) {
|
2446 | this._emitter.removeListener(event, listener);
|
2447 | }
|
2448 | }, {
|
2449 | key: 'attachMediaElement',
|
2450 | value: function attachMediaElement(mediaElement) {
|
2451 | if (this._mediaSource) {
|
2452 | throw new _exception.IllegalStateException('MediaSource has been attached to an HTMLMediaElement!');
|
2453 | }
|
2454 | var ms = this._mediaSource = new window.MediaSource();
|
2455 | ms.addEventListener('sourceopen', this.e.onSourceOpen);
|
2456 | ms.addEventListener('sourceended', this.e.onSourceEnded);
|
2457 | ms.addEventListener('sourceclose', this.e.onSourceClose);
|
2458 |
|
2459 | this._mediaElement = mediaElement;
|
2460 | this._mediaSourceObjectURL = window.URL.createObjectURL(this._mediaSource);
|
2461 | mediaElement.src = this._mediaSourceObjectURL;
|
2462 | }
|
2463 | }, {
|
2464 | key: 'detachMediaElement',
|
2465 | value: function detachMediaElement() {
|
2466 | if (this._mediaSource) {
|
2467 | var ms = this._mediaSource;
|
2468 | for (var type in this._sourceBuffers) {
|
2469 | // pending segments should be discard
|
2470 | var ps = this._pendingSegments[type];
|
2471 | ps.splice(0, ps.length);
|
2472 | this._pendingSegments[type] = null;
|
2473 | this._pendingRemoveRanges[type] = null;
|
2474 | this._lastInitSegments[type] = null;
|
2475 |
|
2476 | // remove all sourcebuffers
|
2477 | var sb = this._sourceBuffers[type];
|
2478 | if (sb) {
|
2479 | if (ms.readyState !== 'closed') {
|
2480 | // ms edge can throw an error: Unexpected call to method or property access
|
2481 | try {
|
2482 | ms.removeSourceBuffer(sb);
|
2483 | } catch (error) {
|
2484 | _logger2.default.e(this.TAG, error.message);
|
2485 | }
|
2486 | sb.removeEventListener('error', this.e.onSourceBufferError);
|
2487 | sb.removeEventListener('updateend', this.e.onSourceBufferUpdateEnd);
|
2488 | }
|
2489 | this._mimeTypes[type] = null;
|
2490 | this._sourceBuffers[type] = null;
|
2491 | }
|
2492 | }
|
2493 | if (ms.readyState === 'open') {
|
2494 | try {
|
2495 | ms.endOfStream();
|
2496 | } catch (error) {
|
2497 | _logger2.default.e(this.TAG, error.message);
|
2498 | }
|
2499 | }
|
2500 | ms.removeEventListener('sourceopen', this.e.onSourceOpen);
|
2501 | ms.removeEventListener('sourceended', this.e.onSourceEnded);
|
2502 | ms.removeEventListener('sourceclose', this.e.onSourceClose);
|
2503 | this._pendingSourceBufferInit = [];
|
2504 | this._isBufferFull = false;
|
2505 | this._idrList.clear();
|
2506 | this._mediaSource = null;
|
2507 | }
|
2508 |
|
2509 | if (this._mediaElement) {
|
2510 | this._mediaElement.src = '';
|
2511 | this._mediaElement.removeAttribute('src');
|
2512 | this._mediaElement = null;
|
2513 | }
|
2514 | if (this._mediaSourceObjectURL) {
|
2515 | window.URL.revokeObjectURL(this._mediaSourceObjectURL);
|
2516 | this._mediaSourceObjectURL = null;
|
2517 | }
|
2518 | }
|
2519 | }, {
|
2520 | key: 'appendInitSegment',
|
2521 | value: function appendInitSegment(initSegment, deferred) {
|
2522 | if (!this._mediaSource || this._mediaSource.readyState !== 'open') {
|
2523 | // sourcebuffer creation requires mediaSource.readyState === 'open'
|
2524 | // so we defer the sourcebuffer creation, until sourceopen event triggered
|
2525 | this._pendingSourceBufferInit.push(initSegment);
|
2526 | // make sure that this InitSegment is in the front of pending segments queue
|
2527 | this._pendingSegments[initSegment.type].push(initSegment);
|
2528 | return;
|
2529 | }
|
2530 |
|
2531 | var is = initSegment;
|
2532 | var mimeType = '' + is.container;
|
2533 | if (is.codec && is.codec.length > 0) {
|
2534 | mimeType += ';codecs=' + is.codec;
|
2535 | }
|
2536 |
|
2537 | var firstInitSegment = false;
|
2538 |
|
2539 | _logger2.default.v(this.TAG, 'Received Initialization Segment, mimeType: ' + mimeType);
|
2540 | this._lastInitSegments[is.type] = is;
|
2541 |
|
2542 | if (mimeType !== this._mimeTypes[is.type]) {
|
2543 | if (!this._mimeTypes[is.type]) {
|
2544 | // empty, first chance create sourcebuffer
|
2545 | firstInitSegment = true;
|
2546 | try {
|
2547 | var sb = this._sourceBuffers[is.type] = this._mediaSource.addSourceBuffer(mimeType);
|
2548 | sb.addEventListener('error', this.e.onSourceBufferError);
|
2549 | sb.addEventListener('updateend', this.e.onSourceBufferUpdateEnd);
|
2550 | } catch (error) {
|
2551 | _logger2.default.e(this.TAG, error.message);
|
2552 | this._emitter.emit(_mseEvents2.default.ERROR, { code: error.code, msg: error.message });
|
2553 | return;
|
2554 | }
|
2555 | } else {
|
2556 | _logger2.default.v(this.TAG, 'Notice: ' + is.type + ' mimeType changed, origin: ' + this._mimeTypes[is.type] + ', target: ' + mimeType);
|
2557 | }
|
2558 | this._mimeTypes[is.type] = mimeType;
|
2559 | }
|
2560 |
|
2561 | if (!deferred) {
|
2562 | // deferred means this InitSegment has been pushed to pendingSegments queue
|
2563 | this._pendingSegments[is.type].push(is);
|
2564 | }
|
2565 | if (!firstInitSegment) {
|
2566 | // append immediately only if init segment in subsequence
|
2567 | if (this._sourceBuffers[is.type] && !this._sourceBuffers[is.type].updating) {
|
2568 | this._doAppendSegments();
|
2569 | }
|
2570 | }
|
2571 | if (_browser2.default.safari && is.container === 'audio/mpeg' && is.mediaDuration > 0) {
|
2572 | // 'audio/mpeg' track under Safari may cause MediaElement's duration to be NaN
|
2573 | // Manually correct MediaSource.duration to make progress bar seekable, and report right duration
|
2574 | this._requireSetMediaDuration = true;
|
2575 | this._pendingMediaDuration = is.mediaDuration / 1000; // in seconds
|
2576 | this._updateMediaSourceDuration();
|
2577 | }
|
2578 | }
|
2579 | }, {
|
2580 | key: 'appendMediaSegment',
|
2581 | value: function appendMediaSegment(mediaSegment) {
|
2582 | var ms = mediaSegment;
|
2583 | this._pendingSegments[ms.type].push(ms);
|
2584 |
|
2585 | if (this._config.autoCleanupSourceBuffer && this._needCleanupSourceBuffer()) {
|
2586 | this._doCleanupSourceBuffer();
|
2587 | }
|
2588 |
|
2589 | var sb = this._sourceBuffers[ms.type];
|
2590 | if (sb && !sb.updating && !this._hasPendingRemoveRanges()) {
|
2591 | this._doAppendSegments();
|
2592 | }
|
2593 | }
|
2594 | }, {
|
2595 | key: 'seek',
|
2596 | value: function seek(seconds) {
|
2597 | // remove all appended buffers
|
2598 | for (var type in this._sourceBuffers) {
|
2599 | if (!this._sourceBuffers[type]) {
|
2600 | continue;
|
2601 | }
|
2602 |
|
2603 | // abort current buffer append algorithm
|
2604 | var sb = this._sourceBuffers[type];
|
2605 | if (this._mediaSource.readyState === 'open') {
|
2606 | try {
|
2607 | // If range removal algorithm is running, InvalidStateError will be throwed
|
2608 | // Ignore it.
|
2609 | sb.abort();
|
2610 | } catch (error) {
|
2611 | _logger2.default.e(this.TAG, error.message);
|
2612 | }
|
2613 | }
|
2614 |
|
2615 | // IDRList should be clear
|
2616 | this._idrList.clear();
|
2617 |
|
2618 | // pending segments should be discard
|
2619 | var ps = this._pendingSegments[type];
|
2620 | ps.splice(0, ps.length);
|
2621 |
|
2622 | if (this._mediaSource.readyState === 'closed') {
|
2623 | // Parent MediaSource object has been detached from HTMLMediaElement
|
2624 | continue;
|
2625 | }
|
2626 |
|
2627 | // record ranges to be remove from SourceBuffer
|
2628 | for (var i = 0; i < sb.buffered.length; i++) {
|
2629 | var start = sb.buffered.start(i);
|
2630 | var end = sb.buffered.end(i);
|
2631 | this._pendingRemoveRanges[type].push({ start: start, end: end });
|
2632 | }
|
2633 |
|
2634 | // if sb is not updating, let's remove ranges now!
|
2635 | if (!sb.updating) {
|
2636 | this._doRemoveRanges();
|
2637 | }
|
2638 |
|
2639 | // Safari 10 may get InvalidStateError in the later appendBuffer() after SourceBuffer.remove() call
|
2640 | // Internal parser's state may be invalid at this time. Re-append last InitSegment to workaround.
|
2641 | // Related issue: https://bugs.webkit.org/show_bug.cgi?id=159230
|
2642 | if (_browser2.default.safari) {
|
2643 | var lastInitSegment = this._lastInitSegments[type];
|
2644 | if (lastInitSegment) {
|
2645 | this._pendingSegments[type].push(lastInitSegment);
|
2646 | if (!sb.updating) {
|
2647 | this._doAppendSegments();
|
2648 | }
|
2649 | }
|
2650 | }
|
2651 | }
|
2652 | }
|
2653 | }, {
|
2654 | key: 'endOfStream',
|
2655 | value: function endOfStream() {
|
2656 | var ms = this._mediaSource;
|
2657 | var sb = this._sourceBuffers;
|
2658 | if (!ms || ms.readyState !== 'open') {
|
2659 | if (ms && ms.readyState === 'closed' && this._hasPendingSegments()) {
|
2660 | // If MediaSource hasn't turned into open state, and there're pending segments
|
2661 | // Mark pending endOfStream, defer call until all pending segments appended complete
|
2662 | this._hasPendingEos = true;
|
2663 | }
|
2664 | return;
|
2665 | }
|
2666 | if (sb.video && sb.video.updating || sb.audio && sb.audio.updating) {
|
2667 | // If any sourcebuffer is updating, defer endOfStream operation
|
2668 | // See _onSourceBufferUpdateEnd()
|
2669 | this._hasPendingEos = true;
|
2670 | } else {
|
2671 | this._hasPendingEos = false;
|
2672 | // Notify media data loading complete
|
2673 | // This is helpful for correcting total duration to match last media segment
|
2674 | // Otherwise MediaElement's ended event may not be triggered
|
2675 | ms.endOfStream();
|
2676 | }
|
2677 | }
|
2678 | }, {
|
2679 | key: 'getNearestKeyframe',
|
2680 | value: function getNearestKeyframe(dts) {
|
2681 | return this._idrList.getLastSyncPointBeforeDts(dts);
|
2682 | }
|
2683 | }, {
|
2684 | key: '_needCleanupSourceBuffer',
|
2685 | value: function _needCleanupSourceBuffer() {
|
2686 | if (!this._config.autoCleanupSourceBuffer) {
|
2687 | return false;
|
2688 | }
|
2689 |
|
2690 | var currentTime = this._mediaElement.currentTime;
|
2691 |
|
2692 | for (var type in this._sourceBuffers) {
|
2693 | var sb = this._sourceBuffers[type];
|
2694 | if (sb) {
|
2695 | var buffered = sb.buffered;
|
2696 | if (buffered.length >= 1) {
|
2697 | if (currentTime - buffered.start(0) >= this._config.autoCleanupMaxBackwardDuration) {
|
2698 | return true;
|
2699 | }
|
2700 | }
|
2701 | }
|
2702 | }
|
2703 |
|
2704 | return false;
|
2705 | }
|
2706 | }, {
|
2707 | key: '_doCleanupSourceBuffer',
|
2708 | value: function _doCleanupSourceBuffer() {
|
2709 | var currentTime = this._mediaElement.currentTime;
|
2710 |
|
2711 | for (var type in this._sourceBuffers) {
|
2712 | var sb = this._sourceBuffers[type];
|
2713 | if (sb) {
|
2714 | var buffered = sb.buffered;
|
2715 | var doRemove = false;
|
2716 |
|
2717 | for (var i = 0; i < buffered.length; i++) {
|
2718 | var start = buffered.start(i);
|
2719 | var end = buffered.end(i);
|
2720 |
|
2721 | if (start <= currentTime && currentTime < end + 3) {
|
2722 | // padding 3 seconds
|
2723 | if (currentTime - start >= this._config.autoCleanupMaxBackwardDuration) {
|
2724 | doRemove = true;
|
2725 | var removeEnd = currentTime - this._config.autoCleanupMinBackwardDuration;
|
2726 | this._pendingRemoveRanges[type].push({ start: start, end: removeEnd });
|
2727 | }
|
2728 | } else if (end < currentTime) {
|
2729 | doRemove = true;
|
2730 | this._pendingRemoveRanges[type].push({ start: start, end: end });
|
2731 | }
|
2732 | }
|
2733 |
|
2734 | if (doRemove && !sb.updating) {
|
2735 | this._doRemoveRanges();
|
2736 | }
|
2737 | }
|
2738 | }
|
2739 | }
|
2740 | }, {
|
2741 | key: '_updateMediaSourceDuration',
|
2742 | value: function _updateMediaSourceDuration() {
|
2743 | var sb = this._sourceBuffers;
|
2744 | if (this._mediaElement.readyState === 0 || this._mediaSource.readyState !== 'open') {
|
2745 | return;
|
2746 | }
|
2747 | if (sb.video && sb.video.updating || sb.audio && sb.audio.updating) {
|
2748 | return;
|
2749 | }
|
2750 |
|
2751 | var current = this._mediaSource.duration;
|
2752 | var target = this._pendingMediaDuration;
|
2753 |
|
2754 | if (target > 0 && (isNaN(current) || target > current)) {
|
2755 | _logger2.default.v(this.TAG, 'Update MediaSource duration from ' + current + ' to ' + target);
|
2756 | this._mediaSource.duration = target;
|
2757 | }
|
2758 |
|
2759 | this._requireSetMediaDuration = false;
|
2760 | this._pendingMediaDuration = 0;
|
2761 | }
|
2762 | }, {
|
2763 | key: '_doRemoveRanges',
|
2764 | value: function _doRemoveRanges() {
|
2765 | for (var type in this._pendingRemoveRanges) {
|
2766 | if (!this._sourceBuffers[type] || this._sourceBuffers[type].updating) {
|
2767 | continue;
|
2768 | }
|
2769 | var sb = this._sourceBuffers[type];
|
2770 | var ranges = this._pendingRemoveRanges[type];
|
2771 | while (ranges.length && !sb.updating) {
|
2772 | var range = ranges.shift();
|
2773 | sb.remove(range.start, range.end);
|
2774 | }
|
2775 | }
|
2776 | }
|
2777 | }, {
|
2778 | key: '_doAppendSegments',
|
2779 | value: function _doAppendSegments() {
|
2780 | var pendingSegments = this._pendingSegments;
|
2781 |
|
2782 | for (var type in pendingSegments) {
|
2783 | if (!this._sourceBuffers[type] || this._sourceBuffers[type].updating) {
|
2784 | continue;
|
2785 | }
|
2786 |
|
2787 | if (pendingSegments[type].length > 0) {
|
2788 | var segment = pendingSegments[type].shift();
|
2789 |
|
2790 | if (segment.timestampOffset) {
|
2791 | // For MPEG audio stream in MSE, if unbuffered-seeking occurred
|
2792 | // We need explicitly set timestampOffset to the desired point in timeline for mpeg SourceBuffer.
|
2793 | var currentOffset = this._sourceBuffers[type].timestampOffset;
|
2794 | var targetOffset = segment.timestampOffset / 1000; // in seconds
|
2795 |
|
2796 | var delta = Math.abs(currentOffset - targetOffset);
|
2797 | if (delta > 0.1) {
|
2798 | // If time delta > 100ms
|
2799 | _logger2.default.v(this.TAG, 'Update MPEG audio timestampOffset from ' + currentOffset + ' to ' + targetOffset);
|
2800 | this._sourceBuffers[type].timestampOffset = targetOffset;
|
2801 | }
|
2802 | delete segment.timestampOffset;
|
2803 | }
|
2804 |
|
2805 | if (!segment.data || segment.data.byteLength === 0) {
|
2806 | // Ignore empty buffer
|
2807 | continue;
|
2808 | }
|
2809 |
|
2810 | try {
|
2811 | this._sourceBuffers[type].appendBuffer(segment.data);
|
2812 | this._isBufferFull = false;
|
2813 | if (type === 'video' && segment.hasOwnProperty('info')) {
|
2814 | this._idrList.appendArray(segment.info.syncPoints);
|
2815 | }
|
2816 | } catch (error) {
|
2817 | this._pendingSegments[type].unshift(segment);
|
2818 | if (error.code === 22) {
|
2819 | // QuotaExceededError
|
2820 | /* Notice that FireFox may not throw QuotaExceededError if SourceBuffer is full
|
2821 | * Currently we can only do lazy-load to avoid SourceBuffer become scattered.
|
2822 | * SourceBuffer eviction policy may be changed in future version of FireFox.
|
2823 | *
|
2824 | * Related issues:
|
2825 | * https://bugzilla.mozilla.org/show_bug.cgi?id=1279885
|
2826 | * https://bugzilla.mozilla.org/show_bug.cgi?id=1280023
|
2827 | */
|
2828 |
|
2829 | // report buffer full, abort network IO
|
2830 | if (!this._isBufferFull) {
|
2831 | this._emitter.emit(_mseEvents2.default.BUFFER_FULL);
|
2832 | }
|
2833 | this._isBufferFull = true;
|
2834 | } else {
|
2835 | _logger2.default.e(this.TAG, error.message);
|
2836 | this._emitter.emit(_mseEvents2.default.ERROR, { code: error.code, msg: error.message });
|
2837 | }
|
2838 | }
|
2839 | }
|
2840 | }
|
2841 | }
|
2842 | }, {
|
2843 | key: '_onSourceOpen',
|
2844 | value: function _onSourceOpen() {
|
2845 | _logger2.default.v(this.TAG, 'MediaSource onSourceOpen');
|
2846 | this._mediaSource.removeEventListener('sourceopen', this.e.onSourceOpen);
|
2847 | // deferred sourcebuffer creation / initialization
|
2848 | if (this._pendingSourceBufferInit.length > 0) {
|
2849 | var pendings = this._pendingSourceBufferInit;
|
2850 | while (pendings.length) {
|
2851 | var segment = pendings.shift();
|
2852 | this.appendInitSegment(segment, true);
|
2853 | }
|
2854 | }
|
2855 | // there may be some pending media segments, append them
|
2856 | if (this._hasPendingSegments()) {
|
2857 | this._doAppendSegments();
|
2858 | }
|
2859 | this._emitter.emit(_mseEvents2.default.SOURCE_OPEN);
|
2860 | }
|
2861 | }, {
|
2862 | key: '_onSourceEnded',
|
2863 | value: function _onSourceEnded() {
|
2864 | // fired on endOfStream
|
2865 | _logger2.default.v(this.TAG, 'MediaSource onSourceEnded');
|
2866 | }
|
2867 | }, {
|
2868 | key: '_onSourceClose',
|
2869 | value: function _onSourceClose() {
|
2870 | // fired on detaching from media element
|
2871 | _logger2.default.v(this.TAG, 'MediaSource onSourceClose');
|
2872 | if (this._mediaSource && this.e != null) {
|
2873 | this._mediaSource.removeEventListener('sourceopen', this.e.onSourceOpen);
|
2874 | this._mediaSource.removeEventListener('sourceended', this.e.onSourceEnded);
|
2875 | this._mediaSource.removeEventListener('sourceclose', this.e.onSourceClose);
|
2876 | }
|
2877 | }
|
2878 | }, {
|
2879 | key: '_hasPendingSegments',
|
2880 | value: function _hasPendingSegments() {
|
2881 | var ps = this._pendingSegments;
|
2882 | return ps.video.length > 0 || ps.audio.length > 0;
|
2883 | }
|
2884 | }, {
|
2885 | key: '_hasPendingRemoveRanges',
|
2886 | value: function _hasPendingRemoveRanges() {
|
2887 | var prr = this._pendingRemoveRanges;
|
2888 | return prr.video.length > 0 || prr.audio.length > 0;
|
2889 | }
|
2890 | }, {
|
2891 | key: '_onSourceBufferUpdateEnd',
|
2892 | value: function _onSourceBufferUpdateEnd() {
|
2893 | if (this._requireSetMediaDuration) {
|
2894 | this._updateMediaSourceDuration();
|
2895 | } else if (this._hasPendingRemoveRanges()) {
|
2896 | this._doRemoveRanges();
|
2897 | } else if (this._hasPendingSegments()) {
|
2898 | this._doAppendSegments();
|
2899 | } else if (this._hasPendingEos) {
|
2900 | this.endOfStream();
|
2901 | }
|
2902 | this._emitter.emit(_mseEvents2.default.UPDATE_END);
|
2903 | }
|
2904 | }, {
|
2905 | key: '_onSourceBufferError',
|
2906 | value: function _onSourceBufferError(e) {
|
2907 | _logger2.default.e(this.TAG, 'SourceBuffer Error: ' + e);
|
2908 | // this error might not always be fatal, just ignore it
|
2909 | }
|
2910 | }]);
|
2911 |
|
2912 | return MSEController;
|
2913 | }();
|
2914 |
|
2915 | exports.default = MSEController;
|
2916 |
|
2917 | },{"../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){
|
2918 | ;
|
2919 |
|
2920 | Object.defineProperty(exports, "__esModule", {
|
2921 | value: true
|
2922 | });
|
2923 | /*
|
2924 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
2925 | *
|
2926 | * @author zheng qian <xqq@xqq.im>
|
2927 | *
|
2928 | * Licensed under the Apache License, Version 2.0 (the "License");
|
2929 | * you may not use this file except in compliance with the License.
|
2930 | * You may obtain a copy of the License at
|
2931 | *
|
2932 | * http://www.apache.org/licenses/LICENSE-2.0
|
2933 | *
|
2934 | * Unless required by applicable law or agreed to in writing, software
|
2935 | * distributed under the License is distributed on an "AS IS" BASIS,
|
2936 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
2937 | * See the License for the specific language governing permissions and
|
2938 | * limitations under the License.
|
2939 | */
|
2940 |
|
2941 | var MSEEvents = {
|
2942 | ERROR: 'error',
|
2943 | SOURCE_OPEN: 'source_open',
|
2944 | UPDATE_END: 'update_end',
|
2945 | BUFFER_FULL: 'buffer_full'
|
2946 | };
|
2947 |
|
2948 | exports.default = MSEEvents;
|
2949 |
|
2950 | },{}],11:[function(_dereq_,module,exports){
|
2951 | ;
|
2952 |
|
2953 | Object.defineProperty(exports, "__esModule", {
|
2954 | value: true
|
2955 | });
|
2956 |
|
2957 | 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; }; }(); /*
|
2958 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
2959 | *
|
2960 | * @author zheng qian <xqq@xqq.im>
|
2961 | *
|
2962 | * Licensed under the Apache License, Version 2.0 (the "License");
|
2963 | * you may not use this file except in compliance with the License.
|
2964 | * You may obtain a copy of the License at
|
2965 | *
|
2966 | * http://www.apache.org/licenses/LICENSE-2.0
|
2967 | *
|
2968 | * Unless required by applicable law or agreed to in writing, software
|
2969 | * distributed under the License is distributed on an "AS IS" BASIS,
|
2970 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
2971 | * See the License for the specific language governing permissions and
|
2972 | * limitations under the License.
|
2973 | */
|
2974 |
|
2975 | var _events = _dereq_('events');
|
2976 |
|
2977 | var _events2 = _interopRequireDefault(_events);
|
2978 |
|
2979 | var _logger = _dereq_('../utils/logger.js');
|
2980 |
|
2981 | var _logger2 = _interopRequireDefault(_logger);
|
2982 |
|
2983 | var _loggingControl = _dereq_('../utils/logging-control.js');
|
2984 |
|
2985 | var _loggingControl2 = _interopRequireDefault(_loggingControl);
|
2986 |
|
2987 | var _transmuxingController = _dereq_('./transmuxing-controller.js');
|
2988 |
|
2989 | var _transmuxingController2 = _interopRequireDefault(_transmuxingController);
|
2990 |
|
2991 | var _transmuxingEvents = _dereq_('./transmuxing-events.js');
|
2992 |
|
2993 | var _transmuxingEvents2 = _interopRequireDefault(_transmuxingEvents);
|
2994 |
|
2995 | var _transmuxingWorker = _dereq_('./transmuxing-worker.js');
|
2996 |
|
2997 | var _transmuxingWorker2 = _interopRequireDefault(_transmuxingWorker);
|
2998 |
|
2999 | var _mediaInfo = _dereq_('./media-info.js');
|
3000 |
|
3001 | var _mediaInfo2 = _interopRequireDefault(_mediaInfo);
|
3002 |
|
3003 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
3004 |
|
3005 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
3006 |
|
3007 | var Transmuxer = function () {
|
3008 | function Transmuxer(mediaDataSource, config) {
|
3009 | _classCallCheck(this, Transmuxer);
|
3010 |
|
3011 | this.TAG = 'Transmuxer';
|
3012 | this._emitter = new _events2.default();
|
3013 |
|
3014 | if (config.enableWorker && typeof Worker !== 'undefined') {
|
3015 | try {
|
3016 | var work = _dereq_('webworkify');
|
3017 | this._worker = work(_transmuxingWorker2.default);
|
3018 | this._workerDestroying = false;
|
3019 | this._worker.addEventListener('message', this._onWorkerMessage.bind(this));
|
3020 | this._worker.postMessage({ cmd: 'init', param: [mediaDataSource, config] });
|
3021 | this.e = {
|
3022 | onLoggingConfigChanged: this._onLoggingConfigChanged.bind(this)
|
3023 | };
|
3024 | _loggingControl2.default.registerListener(this.e.onLoggingConfigChanged);
|
3025 | this._worker.postMessage({ cmd: 'logging_config', param: _loggingControl2.default.getConfig() });
|
3026 | } catch (error) {
|
3027 | _logger2.default.e(this.TAG, 'Error while initialize transmuxing worker, fallback to inline transmuxing');
|
3028 | this._worker = null;
|
3029 | this._controller = new _transmuxingController2.default(mediaDataSource, config);
|
3030 | }
|
3031 | } else {
|
3032 | this._controller = new _transmuxingController2.default(mediaDataSource, config);
|
3033 | }
|
3034 |
|
3035 | if (this._controller) {
|
3036 | var ctl = this._controller;
|
3037 | ctl.on(_transmuxingEvents2.default.IO_ERROR, this._onIOError.bind(this));
|
3038 | ctl.on(_transmuxingEvents2.default.DEMUX_ERROR, this._onDemuxError.bind(this));
|
3039 | ctl.on(_transmuxingEvents2.default.INIT_SEGMENT, this._onInitSegment.bind(this));
|
3040 | ctl.on(_transmuxingEvents2.default.MEDIA_SEGMENT, this._onMediaSegment.bind(this));
|
3041 | ctl.on(_transmuxingEvents2.default.LOADING_COMPLETE, this._onLoadingComplete.bind(this));
|
3042 | ctl.on(_transmuxingEvents2.default.RECOVERED_EARLY_EOF, this._onRecoveredEarlyEof.bind(this));
|
3043 | ctl.on(_transmuxingEvents2.default.MEDIA_INFO, this._onMediaInfo.bind(this));
|
3044 | ctl.on(_transmuxingEvents2.default.METADATA_ARRIVED, this._onMetaDataArrived.bind(this));
|
3045 | ctl.on(_transmuxingEvents2.default.SCRIPTDATA_ARRIVED, this._onScriptDataArrived.bind(this));
|
3046 | ctl.on(_transmuxingEvents2.default.STATISTICS_INFO, this._onStatisticsInfo.bind(this));
|
3047 | ctl.on(_transmuxingEvents2.default.RECOMMEND_SEEKPOINT, this._onRecommendSeekpoint.bind(this));
|
3048 | }
|
3049 | }
|
3050 |
|
3051 | _createClass(Transmuxer, [{
|
3052 | key: 'destroy',
|
3053 | value: function destroy() {
|
3054 | if (this._worker) {
|
3055 | if (!this._workerDestroying) {
|
3056 | this._workerDestroying = true;
|
3057 | this._worker.postMessage({ cmd: 'destroy' });
|
3058 | _loggingControl2.default.removeListener(this.e.onLoggingConfigChanged);
|
3059 | this.e = null;
|
3060 | }
|
3061 | } else {
|
3062 | this._controller.destroy();
|
3063 | this._controller = null;
|
3064 | }
|
3065 | this._emitter.removeAllListeners();
|
3066 | this._emitter = null;
|
3067 | }
|
3068 | }, {
|
3069 | key: 'on',
|
3070 | value: function on(event, listener) {
|
3071 | this._emitter.addListener(event, listener);
|
3072 | }
|
3073 | }, {
|
3074 | key: 'off',
|
3075 | value: function off(event, listener) {
|
3076 | this._emitter.removeListener(event, listener);
|
3077 | }
|
3078 | }, {
|
3079 | key: 'hasWorker',
|
3080 | value: function hasWorker() {
|
3081 | return this._worker != null;
|
3082 | }
|
3083 | }, {
|
3084 | key: 'open',
|
3085 | value: function open() {
|
3086 | if (this._worker) {
|
3087 | this._worker.postMessage({ cmd: 'start' });
|
3088 | } else {
|
3089 | this._controller.start();
|
3090 | }
|
3091 | }
|
3092 | }, {
|
3093 | key: 'close',
|
3094 | value: function close() {
|
3095 | if (this._worker) {
|
3096 | this._worker.postMessage({ cmd: 'stop' });
|
3097 | } else {
|
3098 | this._controller.stop();
|
3099 | }
|
3100 | }
|
3101 | }, {
|
3102 | key: 'seek',
|
3103 | value: function seek(milliseconds) {
|
3104 | if (this._worker) {
|
3105 | this._worker.postMessage({ cmd: 'seek', param: milliseconds });
|
3106 | } else {
|
3107 | this._controller.seek(milliseconds);
|
3108 | }
|
3109 | }
|
3110 | }, {
|
3111 | key: 'pause',
|
3112 | value: function pause() {
|
3113 | if (this._worker) {
|
3114 | this._worker.postMessage({ cmd: 'pause' });
|
3115 | } else {
|
3116 | this._controller.pause();
|
3117 | }
|
3118 | }
|
3119 | }, {
|
3120 | key: 'resume',
|
3121 | value: function resume() {
|
3122 | if (this._worker) {
|
3123 | this._worker.postMessage({ cmd: 'resume' });
|
3124 | } else {
|
3125 | this._controller.resume();
|
3126 | }
|
3127 | }
|
3128 | }, {
|
3129 | key: '_onInitSegment',
|
3130 | value: function _onInitSegment(type, initSegment) {
|
3131 | var _this = this;
|
3132 |
|
3133 | // do async invoke
|
3134 | Promise.resolve().then(function () {
|
3135 | _this._emitter.emit(_transmuxingEvents2.default.INIT_SEGMENT, type, initSegment);
|
3136 | });
|
3137 | }
|
3138 | }, {
|
3139 | key: '_onMediaSegment',
|
3140 | value: function _onMediaSegment(type, mediaSegment) {
|
3141 | var _this2 = this;
|
3142 |
|
3143 | Promise.resolve().then(function () {
|
3144 | _this2._emitter.emit(_transmuxingEvents2.default.MEDIA_SEGMENT, type, mediaSegment);
|
3145 | });
|
3146 | }
|
3147 | }, {
|
3148 | key: '_onLoadingComplete',
|
3149 | value: function _onLoadingComplete() {
|
3150 | var _this3 = this;
|
3151 |
|
3152 | Promise.resolve().then(function () {
|
3153 | _this3._emitter.emit(_transmuxingEvents2.default.LOADING_COMPLETE);
|
3154 | });
|
3155 | }
|
3156 | }, {
|
3157 | key: '_onRecoveredEarlyEof',
|
3158 | value: function _onRecoveredEarlyEof() {
|
3159 | var _this4 = this;
|
3160 |
|
3161 | Promise.resolve().then(function () {
|
3162 | _this4._emitter.emit(_transmuxingEvents2.default.RECOVERED_EARLY_EOF);
|
3163 | });
|
3164 | }
|
3165 | }, {
|
3166 | key: '_onMediaInfo',
|
3167 | value: function _onMediaInfo(mediaInfo) {
|
3168 | var _this5 = this;
|
3169 |
|
3170 | Promise.resolve().then(function () {
|
3171 | _this5._emitter.emit(_transmuxingEvents2.default.MEDIA_INFO, mediaInfo);
|
3172 | });
|
3173 | }
|
3174 | }, {
|
3175 | key: '_onMetaDataArrived',
|
3176 | value: function _onMetaDataArrived(metadata) {
|
3177 | var _this6 = this;
|
3178 |
|
3179 | Promise.resolve().then(function () {
|
3180 | _this6._emitter.emit(_transmuxingEvents2.default.METADATA_ARRIVED, metadata);
|
3181 | });
|
3182 | }
|
3183 | }, {
|
3184 | key: '_onScriptDataArrived',
|
3185 | value: function _onScriptDataArrived(data) {
|
3186 | var _this7 = this;
|
3187 |
|
3188 | Promise.resolve().then(function () {
|
3189 | _this7._emitter.emit(_transmuxingEvents2.default.SCRIPTDATA_ARRIVED, data);
|
3190 | });
|
3191 | }
|
3192 | }, {
|
3193 | key: '_onStatisticsInfo',
|
3194 | value: function _onStatisticsInfo(statisticsInfo) {
|
3195 | var _this8 = this;
|
3196 |
|
3197 | Promise.resolve().then(function () {
|
3198 | _this8._emitter.emit(_transmuxingEvents2.default.STATISTICS_INFO, statisticsInfo);
|
3199 | });
|
3200 | }
|
3201 | }, {
|
3202 | key: '_onIOError',
|
3203 | value: function _onIOError(type, info) {
|
3204 | var _this9 = this;
|
3205 |
|
3206 | Promise.resolve().then(function () {
|
3207 | _this9._emitter.emit(_transmuxingEvents2.default.IO_ERROR, type, info);
|
3208 | });
|
3209 | }
|
3210 | }, {
|
3211 | key: '_onDemuxError',
|
3212 | value: function _onDemuxError(type, info) {
|
3213 | var _this10 = this;
|
3214 |
|
3215 | Promise.resolve().then(function () {
|
3216 | _this10._emitter.emit(_transmuxingEvents2.default.DEMUX_ERROR, type, info);
|
3217 | });
|
3218 | }
|
3219 | }, {
|
3220 | key: '_onRecommendSeekpoint',
|
3221 | value: function _onRecommendSeekpoint(milliseconds) {
|
3222 | var _this11 = this;
|
3223 |
|
3224 | Promise.resolve().then(function () {
|
3225 | _this11._emitter.emit(_transmuxingEvents2.default.RECOMMEND_SEEKPOINT, milliseconds);
|
3226 | });
|
3227 | }
|
3228 | }, {
|
3229 | key: '_onLoggingConfigChanged',
|
3230 | value: function _onLoggingConfigChanged(config) {
|
3231 | if (this._worker) {
|
3232 | this._worker.postMessage({ cmd: 'logging_config', param: config });
|
3233 | }
|
3234 | }
|
3235 | }, {
|
3236 | key: '_onWorkerMessage',
|
3237 | value: function _onWorkerMessage(e) {
|
3238 | var message = e.data;
|
3239 | var data = message.data;
|
3240 |
|
3241 | if (message.msg === 'destroyed' || this._workerDestroying) {
|
3242 | this._workerDestroying = false;
|
3243 | this._worker.terminate();
|
3244 | this._worker = null;
|
3245 | return;
|
3246 | }
|
3247 |
|
3248 | switch (message.msg) {
|
3249 | case _transmuxingEvents2.default.INIT_SEGMENT:
|
3250 | case _transmuxingEvents2.default.MEDIA_SEGMENT:
|
3251 | this._emitter.emit(message.msg, data.type, data.data);
|
3252 | break;
|
3253 | case _transmuxingEvents2.default.LOADING_COMPLETE:
|
3254 | case _transmuxingEvents2.default.RECOVERED_EARLY_EOF:
|
3255 | this._emitter.emit(message.msg);
|
3256 | break;
|
3257 | case _transmuxingEvents2.default.MEDIA_INFO:
|
3258 | Object.setPrototypeOf(data, _mediaInfo2.default.prototype);
|
3259 | this._emitter.emit(message.msg, data);
|
3260 | break;
|
3261 | case _transmuxingEvents2.default.METADATA_ARRIVED:
|
3262 | case _transmuxingEvents2.default.SCRIPTDATA_ARRIVED:
|
3263 | case _transmuxingEvents2.default.STATISTICS_INFO:
|
3264 | this._emitter.emit(message.msg, data);
|
3265 | break;
|
3266 | case _transmuxingEvents2.default.IO_ERROR:
|
3267 | case _transmuxingEvents2.default.DEMUX_ERROR:
|
3268 | this._emitter.emit(message.msg, data.type, data.info);
|
3269 | break;
|
3270 | case _transmuxingEvents2.default.RECOMMEND_SEEKPOINT:
|
3271 | this._emitter.emit(message.msg, data);
|
3272 | break;
|
3273 | case 'logcat_callback':
|
3274 | _logger2.default.emitter.emit('log', data.type, data.logcat);
|
3275 | break;
|
3276 | default:
|
3277 | break;
|
3278 | }
|
3279 | }
|
3280 | }]);
|
3281 |
|
3282 | return Transmuxer;
|
3283 | }();
|
3284 |
|
3285 | exports.default = Transmuxer;
|
3286 |
|
3287 | },{"../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){
|
3288 | ;
|
3289 |
|
3290 | Object.defineProperty(exports, "__esModule", {
|
3291 | value: true
|
3292 | });
|
3293 |
|
3294 | 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; }; }(); /*
|
3295 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
3296 | *
|
3297 | * @author zheng qian <xqq@xqq.im>
|
3298 | *
|
3299 | * Licensed under the Apache License, Version 2.0 (the "License");
|
3300 | * you may not use this file except in compliance with the License.
|
3301 | * You may obtain a copy of the License at
|
3302 | *
|
3303 | * http://www.apache.org/licenses/LICENSE-2.0
|
3304 | *
|
3305 | * Unless required by applicable law or agreed to in writing, software
|
3306 | * distributed under the License is distributed on an "AS IS" BASIS,
|
3307 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
3308 | * See the License for the specific language governing permissions and
|
3309 | * limitations under the License.
|
3310 | */
|
3311 |
|
3312 | var _events = _dereq_('events');
|
3313 |
|
3314 | var _events2 = _interopRequireDefault(_events);
|
3315 |
|
3316 | var _logger = _dereq_('../utils/logger.js');
|
3317 |
|
3318 | var _logger2 = _interopRequireDefault(_logger);
|
3319 |
|
3320 | var _browser = _dereq_('../utils/browser.js');
|
3321 |
|
3322 | var _browser2 = _interopRequireDefault(_browser);
|
3323 |
|
3324 | var _mediaInfo = _dereq_('./media-info.js');
|
3325 |
|
3326 | var _mediaInfo2 = _interopRequireDefault(_mediaInfo);
|
3327 |
|
3328 | var _flvDemuxer = _dereq_('../demux/flv-demuxer.js');
|
3329 |
|
3330 | var _flvDemuxer2 = _interopRequireDefault(_flvDemuxer);
|
3331 |
|
3332 | var _mp4Remuxer = _dereq_('../remux/mp4-remuxer.js');
|
3333 |
|
3334 | var _mp4Remuxer2 = _interopRequireDefault(_mp4Remuxer);
|
3335 |
|
3336 | var _demuxErrors = _dereq_('../demux/demux-errors.js');
|
3337 |
|
3338 | var _demuxErrors2 = _interopRequireDefault(_demuxErrors);
|
3339 |
|
3340 | var _ioController = _dereq_('../io/io-controller.js');
|
3341 |
|
3342 | var _ioController2 = _interopRequireDefault(_ioController);
|
3343 |
|
3344 | var _transmuxingEvents = _dereq_('./transmuxing-events.js');
|
3345 |
|
3346 | var _transmuxingEvents2 = _interopRequireDefault(_transmuxingEvents);
|
3347 |
|
3348 | var _loader = _dereq_('../io/loader.js');
|
3349 |
|
3350 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
3351 |
|
3352 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
3353 |
|
3354 | // Transmuxing (IO, Demuxing, Remuxing) controller, with multipart support
|
3355 | var TransmuxingController = function () {
|
3356 | function TransmuxingController(mediaDataSource, config) {
|
3357 | _classCallCheck(this, TransmuxingController);
|
3358 |
|
3359 | this.TAG = 'TransmuxingController';
|
3360 | this._emitter = new _events2.default();
|
3361 |
|
3362 | this._config = config;
|
3363 |
|
3364 | // treat single part media as multipart media, which has only one segment
|
3365 | if (!mediaDataSource.segments) {
|
3366 | mediaDataSource.segments = [{
|
3367 | duration: mediaDataSource.duration,
|
3368 | filesize: mediaDataSource.filesize,
|
3369 | url: mediaDataSource.url
|
3370 | }];
|
3371 | }
|
3372 |
|
3373 | // fill in default IO params if not exists
|
3374 | if (typeof mediaDataSource.cors !== 'boolean') {
|
3375 | mediaDataSource.cors = true;
|
3376 | }
|
3377 | if (typeof mediaDataSource.withCredentials !== 'boolean') {
|
3378 | mediaDataSource.withCredentials = false;
|
3379 | }
|
3380 |
|
3381 | this._mediaDataSource = mediaDataSource;
|
3382 | this._currentSegmentIndex = 0;
|
3383 | var totalDuration = 0;
|
3384 |
|
3385 | this._mediaDataSource.segments.forEach(function (segment) {
|
3386 | // timestampBase for each segment, and calculate total duration
|
3387 | segment.timestampBase = totalDuration;
|
3388 | totalDuration += segment.duration;
|
3389 | // params needed by IOController
|
3390 | segment.cors = mediaDataSource.cors;
|
3391 | segment.withCredentials = mediaDataSource.withCredentials;
|
3392 | // referrer policy control, if exist
|
3393 | if (config.referrerPolicy) {
|
3394 | segment.referrerPolicy = config.referrerPolicy;
|
3395 | }
|
3396 | });
|
3397 |
|
3398 | if (!isNaN(totalDuration) && this._mediaDataSource.duration !== totalDuration) {
|
3399 | this._mediaDataSource.duration = totalDuration;
|
3400 | }
|
3401 |
|
3402 | this._mediaInfo = null;
|
3403 | this._demuxer = null;
|
3404 | this._remuxer = null;
|
3405 | this._ioctl = null;
|
3406 |
|
3407 | this._pendingSeekTime = null;
|
3408 | this._pendingResolveSeekPoint = null;
|
3409 |
|
3410 | this._statisticsReporter = null;
|
3411 | }
|
3412 |
|
3413 | _createClass(TransmuxingController, [{
|
3414 | key: 'destroy',
|
3415 | value: function destroy() {
|
3416 | this._mediaInfo = null;
|
3417 | this._mediaDataSource = null;
|
3418 |
|
3419 | if (this._statisticsReporter) {
|
3420 | this._disableStatisticsReporter();
|
3421 | }
|
3422 | if (this._ioctl) {
|
3423 | this._ioctl.destroy();
|
3424 | this._ioctl = null;
|
3425 | }
|
3426 | if (this._demuxer) {
|
3427 | this._demuxer.destroy();
|
3428 | this._demuxer = null;
|
3429 | }
|
3430 | if (this._remuxer) {
|
3431 | this._remuxer.destroy();
|
3432 | this._remuxer = null;
|
3433 | }
|
3434 |
|
3435 | this._emitter.removeAllListeners();
|
3436 | this._emitter = null;
|
3437 | }
|
3438 | }, {
|
3439 | key: 'on',
|
3440 | value: function on(event, listener) {
|
3441 | this._emitter.addListener(event, listener);
|
3442 | }
|
3443 | }, {
|
3444 | key: 'off',
|
3445 | value: function off(event, listener) {
|
3446 | this._emitter.removeListener(event, listener);
|
3447 | }
|
3448 | }, {
|
3449 | key: 'start',
|
3450 | value: function start() {
|
3451 | this._loadSegment(0);
|
3452 | this._enableStatisticsReporter();
|
3453 | }
|
3454 | }, {
|
3455 | key: '_loadSegment',
|
3456 | value: function _loadSegment(segmentIndex, optionalFrom) {
|
3457 | this._currentSegmentIndex = segmentIndex;
|
3458 | var dataSource = this._mediaDataSource.segments[segmentIndex];
|
3459 |
|
3460 | var ioctl = this._ioctl = new _ioController2.default(dataSource, this._config, segmentIndex);
|
3461 | ioctl.onError = this._onIOException.bind(this);
|
3462 | ioctl.onSeeked = this._onIOSeeked.bind(this);
|
3463 | ioctl.onComplete = this._onIOComplete.bind(this);
|
3464 | ioctl.onRedirect = this._onIORedirect.bind(this);
|
3465 | ioctl.onRecoveredEarlyEof = this._onIORecoveredEarlyEof.bind(this);
|
3466 |
|
3467 | if (optionalFrom) {
|
3468 | this._demuxer.bindDataSource(this._ioctl);
|
3469 | } else {
|
3470 | ioctl.onDataArrival = this._onInitChunkArrival.bind(this);
|
3471 | }
|
3472 |
|
3473 | ioctl.open(optionalFrom);
|
3474 | }
|
3475 | }, {
|
3476 | key: 'stop',
|
3477 | value: function stop() {
|
3478 | this._internalAbort();
|
3479 | this._disableStatisticsReporter();
|
3480 | }
|
3481 | }, {
|
3482 | key: '_internalAbort',
|
3483 | value: function _internalAbort() {
|
3484 | if (this._ioctl) {
|
3485 | this._ioctl.destroy();
|
3486 | this._ioctl = null;
|
3487 | }
|
3488 | }
|
3489 | }, {
|
3490 | key: 'pause',
|
3491 | value: function pause() {
|
3492 | // take a rest
|
3493 | if (this._ioctl && this._ioctl.isWorking()) {
|
3494 | this._ioctl.pause();
|
3495 | this._disableStatisticsReporter();
|
3496 | }
|
3497 | }
|
3498 | }, {
|
3499 | key: 'resume',
|
3500 | value: function resume() {
|
3501 | if (this._ioctl && this._ioctl.isPaused()) {
|
3502 | this._ioctl.resume();
|
3503 | this._enableStatisticsReporter();
|
3504 | }
|
3505 | }
|
3506 | }, {
|
3507 | key: 'seek',
|
3508 | value: function seek(milliseconds) {
|
3509 | if (this._mediaInfo == null || !this._mediaInfo.isSeekable()) {
|
3510 | return;
|
3511 | }
|
3512 |
|
3513 | var targetSegmentIndex = this._searchSegmentIndexContains(milliseconds);
|
3514 |
|
3515 | if (targetSegmentIndex === this._currentSegmentIndex) {
|
3516 | // intra-segment seeking
|
3517 | var segmentInfo = this._mediaInfo.segments[targetSegmentIndex];
|
3518 |
|
3519 | if (segmentInfo == undefined) {
|
3520 | // current segment loading started, but mediainfo hasn't received yet
|
3521 | // wait for the metadata loaded, then seek to expected position
|
3522 | this._pendingSeekTime = milliseconds;
|
3523 | } else {
|
3524 | var keyframe = segmentInfo.getNearestKeyframe(milliseconds);
|
3525 | this._remuxer.seek(keyframe.milliseconds);
|
3526 | this._ioctl.seek(keyframe.fileposition);
|
3527 | // Will be resolved in _onRemuxerMediaSegmentArrival()
|
3528 | this._pendingResolveSeekPoint = keyframe.milliseconds;
|
3529 | }
|
3530 | } else {
|
3531 | // cross-segment seeking
|
3532 | var targetSegmentInfo = this._mediaInfo.segments[targetSegmentIndex];
|
3533 |
|
3534 | if (targetSegmentInfo == undefined) {
|
3535 | // target segment hasn't been loaded. We need metadata then seek to expected time
|
3536 | this._pendingSeekTime = milliseconds;
|
3537 | this._internalAbort();
|
3538 | this._remuxer.seek();
|
3539 | this._remuxer.insertDiscontinuity();
|
3540 | this._loadSegment(targetSegmentIndex);
|
3541 | // Here we wait for the metadata loaded, then seek to expected position
|
3542 | } else {
|
3543 | // We have target segment's metadata, direct seek to target position
|
3544 | var _keyframe = targetSegmentInfo.getNearestKeyframe(milliseconds);
|
3545 | this._internalAbort();
|
3546 | this._remuxer.seek(milliseconds);
|
3547 | this._remuxer.insertDiscontinuity();
|
3548 | this._demuxer.resetMediaInfo();
|
3549 | this._demuxer.timestampBase = this._mediaDataSource.segments[targetSegmentIndex].timestampBase;
|
3550 | this._loadSegment(targetSegmentIndex, _keyframe.fileposition);
|
3551 | this._pendingResolveSeekPoint = _keyframe.milliseconds;
|
3552 | this._reportSegmentMediaInfo(targetSegmentIndex);
|
3553 | }
|
3554 | }
|
3555 |
|
3556 | this._enableStatisticsReporter();
|
3557 | }
|
3558 | }, {
|
3559 | key: '_searchSegmentIndexContains',
|
3560 | value: function _searchSegmentIndexContains(milliseconds) {
|
3561 | var segments = this._mediaDataSource.segments;
|
3562 | var idx = segments.length - 1;
|
3563 |
|
3564 | for (var i = 0; i < segments.length; i++) {
|
3565 | if (milliseconds < segments[i].timestampBase) {
|
3566 | idx = i - 1;
|
3567 | break;
|
3568 | }
|
3569 | }
|
3570 | return idx;
|
3571 | }
|
3572 | }, {
|
3573 | key: '_onInitChunkArrival',
|
3574 | value: function _onInitChunkArrival(data, byteStart) {
|
3575 | var _this = this;
|
3576 |
|
3577 | var probeData = null;
|
3578 | var consumed = 0;
|
3579 |
|
3580 | if (byteStart > 0) {
|
3581 | // IOController seeked immediately after opened, byteStart > 0 callback may received
|
3582 | this._demuxer.bindDataSource(this._ioctl);
|
3583 | this._demuxer.timestampBase = this._mediaDataSource.segments[this._currentSegmentIndex].timestampBase;
|
3584 |
|
3585 | consumed = this._demuxer.parseChunks(data, byteStart);
|
3586 | } else if ((probeData = _flvDemuxer2.default.probe(data)).match) {
|
3587 | // Always create new FLVDemuxer
|
3588 | this._demuxer = new _flvDemuxer2.default(probeData, this._config);
|
3589 |
|
3590 | if (!this._remuxer) {
|
3591 | this._remuxer = new _mp4Remuxer2.default(this._config);
|
3592 | }
|
3593 |
|
3594 | var mds = this._mediaDataSource;
|
3595 | if (mds.duration != undefined && !isNaN(mds.duration)) {
|
3596 | this._demuxer.overridedDuration = mds.duration;
|
3597 | }
|
3598 | if (typeof mds.hasAudio === 'boolean') {
|
3599 | this._demuxer.overridedHasAudio = mds.hasAudio;
|
3600 | }
|
3601 | if (typeof mds.hasVideo === 'boolean') {
|
3602 | this._demuxer.overridedHasVideo = mds.hasVideo;
|
3603 | }
|
3604 |
|
3605 | this._demuxer.timestampBase = mds.segments[this._currentSegmentIndex].timestampBase;
|
3606 |
|
3607 | this._demuxer.onError = this._onDemuxException.bind(this);
|
3608 | this._demuxer.onMediaInfo = this._onMediaInfo.bind(this);
|
3609 | this._demuxer.onMetaDataArrived = this._onMetaDataArrived.bind(this);
|
3610 | this._demuxer.onScriptDataArrived = this._onScriptDataArrived.bind(this);
|
3611 |
|
3612 | this._remuxer.bindDataSource(this._demuxer.bindDataSource(this._ioctl));
|
3613 |
|
3614 | this._remuxer.onInitSegment = this._onRemuxerInitSegmentArrival.bind(this);
|
3615 | this._remuxer.onMediaSegment = this._onRemuxerMediaSegmentArrival.bind(this);
|
3616 |
|
3617 | consumed = this._demuxer.parseChunks(data, byteStart);
|
3618 | } else {
|
3619 | probeData = null;
|
3620 | _logger2.default.e(this.TAG, 'Non-FLV, Unsupported media type!');
|
3621 | Promise.resolve().then(function () {
|
3622 | _this._internalAbort();
|
3623 | });
|
3624 | this._emitter.emit(_transmuxingEvents2.default.DEMUX_ERROR, _demuxErrors2.default.FORMAT_UNSUPPORTED, 'Non-FLV, Unsupported media type');
|
3625 |
|
3626 | consumed = 0;
|
3627 | }
|
3628 |
|
3629 | return consumed;
|
3630 | }
|
3631 | }, {
|
3632 | key: '_onMediaInfo',
|
3633 | value: function _onMediaInfo(mediaInfo) {
|
3634 | var _this2 = this;
|
3635 |
|
3636 | if (this._mediaInfo == null) {
|
3637 | // Store first segment's mediainfo as global mediaInfo
|
3638 | this._mediaInfo = Object.assign({}, mediaInfo);
|
3639 | this._mediaInfo.keyframesIndex = null;
|
3640 | this._mediaInfo.segments = [];
|
3641 | this._mediaInfo.segmentCount = this._mediaDataSource.segments.length;
|
3642 | Object.setPrototypeOf(this._mediaInfo, _mediaInfo2.default.prototype);
|
3643 | }
|
3644 |
|
3645 | var segmentInfo = Object.assign({}, mediaInfo);
|
3646 | Object.setPrototypeOf(segmentInfo, _mediaInfo2.default.prototype);
|
3647 | this._mediaInfo.segments[this._currentSegmentIndex] = segmentInfo;
|
3648 |
|
3649 | // notify mediaInfo update
|
3650 | this._reportSegmentMediaInfo(this._currentSegmentIndex);
|
3651 |
|
3652 | if (this._pendingSeekTime != null) {
|
3653 | Promise.resolve().then(function () {
|
3654 | var target = _this2._pendingSeekTime;
|
3655 | _this2._pendingSeekTime = null;
|
3656 | _this2.seek(target);
|
3657 | });
|
3658 | }
|
3659 | }
|
3660 | }, {
|
3661 | key: '_onMetaDataArrived',
|
3662 | value: function _onMetaDataArrived(metadata) {
|
3663 | this._emitter.emit(_transmuxingEvents2.default.METADATA_ARRIVED, metadata);
|
3664 | }
|
3665 | }, {
|
3666 | key: '_onScriptDataArrived',
|
3667 | value: function _onScriptDataArrived(data) {
|
3668 | this._emitter.emit(_transmuxingEvents2.default.SCRIPTDATA_ARRIVED, data);
|
3669 | }
|
3670 | }, {
|
3671 | key: '_onIOSeeked',
|
3672 | value: function _onIOSeeked() {
|
3673 | this._remuxer.insertDiscontinuity();
|
3674 | }
|
3675 | }, {
|
3676 | key: '_onIOComplete',
|
3677 | value: function _onIOComplete(extraData) {
|
3678 | var segmentIndex = extraData;
|
3679 | var nextSegmentIndex = segmentIndex + 1;
|
3680 |
|
3681 | if (nextSegmentIndex < this._mediaDataSource.segments.length) {
|
3682 | this._internalAbort();
|
3683 | this._remuxer.flushStashedSamples();
|
3684 | this._loadSegment(nextSegmentIndex);
|
3685 | } else {
|
3686 | this._remuxer.flushStashedSamples();
|
3687 | this._emitter.emit(_transmuxingEvents2.default.LOADING_COMPLETE);
|
3688 | this._disableStatisticsReporter();
|
3689 | }
|
3690 | }
|
3691 | }, {
|
3692 | key: '_onIORedirect',
|
3693 | value: function _onIORedirect(redirectedURL) {
|
3694 | var segmentIndex = this._ioctl.extraData;
|
3695 | this._mediaDataSource.segments[segmentIndex].redirectedURL = redirectedURL;
|
3696 | }
|
3697 | }, {
|
3698 | key: '_onIORecoveredEarlyEof',
|
3699 | value: function _onIORecoveredEarlyEof() {
|
3700 | this._emitter.emit(_transmuxingEvents2.default.RECOVERED_EARLY_EOF);
|
3701 | }
|
3702 | }, {
|
3703 | key: '_onIOException',
|
3704 | value: function _onIOException(type, info) {
|
3705 | _logger2.default.e(this.TAG, 'IOException: type = ' + type + ', code = ' + info.code + ', msg = ' + info.msg);
|
3706 | this._emitter.emit(_transmuxingEvents2.default.IO_ERROR, type, info);
|
3707 | this._disableStatisticsReporter();
|
3708 | }
|
3709 | }, {
|
3710 | key: '_onDemuxException',
|
3711 | value: function _onDemuxException(type, info) {
|
3712 | _logger2.default.e(this.TAG, 'DemuxException: type = ' + type + ', info = ' + info);
|
3713 | this._emitter.emit(_transmuxingEvents2.default.DEMUX_ERROR, type, info);
|
3714 | }
|
3715 | }, {
|
3716 | key: '_onRemuxerInitSegmentArrival',
|
3717 | value: function _onRemuxerInitSegmentArrival(type, initSegment) {
|
3718 | this._emitter.emit(_transmuxingEvents2.default.INIT_SEGMENT, type, initSegment);
|
3719 | }
|
3720 | }, {
|
3721 | key: '_onRemuxerMediaSegmentArrival',
|
3722 | value: function _onRemuxerMediaSegmentArrival(type, mediaSegment) {
|
3723 | if (this._pendingSeekTime != null) {
|
3724 | // Media segments after new-segment cross-seeking should be dropped.
|
3725 | return;
|
3726 | }
|
3727 | this._emitter.emit(_transmuxingEvents2.default.MEDIA_SEGMENT, type, mediaSegment);
|
3728 |
|
3729 | // Resolve pending seekPoint
|
3730 | if (this._pendingResolveSeekPoint != null && type === 'video') {
|
3731 | var syncPoints = mediaSegment.info.syncPoints;
|
3732 | var seekpoint = this._pendingResolveSeekPoint;
|
3733 | this._pendingResolveSeekPoint = null;
|
3734 |
|
3735 | // Safari: Pass PTS for recommend_seekpoint
|
3736 | if (_browser2.default.safari && syncPoints.length > 0 && syncPoints[0].originalDts === seekpoint) {
|
3737 | seekpoint = syncPoints[0].pts;
|
3738 | }
|
3739 | // else: use original DTS (keyframe.milliseconds)
|
3740 |
|
3741 | this._emitter.emit(_transmuxingEvents2.default.RECOMMEND_SEEKPOINT, seekpoint);
|
3742 | }
|
3743 | }
|
3744 | }, {
|
3745 | key: '_enableStatisticsReporter',
|
3746 | value: function _enableStatisticsReporter() {
|
3747 | if (this._statisticsReporter == null) {
|
3748 | this._statisticsReporter = self.setInterval(this._reportStatisticsInfo.bind(this), this._config.statisticsInfoReportInterval);
|
3749 | }
|
3750 | }
|
3751 | }, {
|
3752 | key: '_disableStatisticsReporter',
|
3753 | value: function _disableStatisticsReporter() {
|
3754 | if (this._statisticsReporter) {
|
3755 | self.clearInterval(this._statisticsReporter);
|
3756 | this._statisticsReporter = null;
|
3757 | }
|
3758 | }
|
3759 | }, {
|
3760 | key: '_reportSegmentMediaInfo',
|
3761 | value: function _reportSegmentMediaInfo(segmentIndex) {
|
3762 | var segmentInfo = this._mediaInfo.segments[segmentIndex];
|
3763 | var exportInfo = Object.assign({}, segmentInfo);
|
3764 |
|
3765 | exportInfo.duration = this._mediaInfo.duration;
|
3766 | exportInfo.segmentCount = this._mediaInfo.segmentCount;
|
3767 | delete exportInfo.segments;
|
3768 | delete exportInfo.keyframesIndex;
|
3769 |
|
3770 | this._emitter.emit(_transmuxingEvents2.default.MEDIA_INFO, exportInfo);
|
3771 | }
|
3772 | }, {
|
3773 | key: '_reportStatisticsInfo',
|
3774 | value: function _reportStatisticsInfo() {
|
3775 | var info = {};
|
3776 |
|
3777 | info.url = this._ioctl.currentURL;
|
3778 | info.hasRedirect = this._ioctl.hasRedirect;
|
3779 | if (info.hasRedirect) {
|
3780 | info.redirectedURL = this._ioctl.currentRedirectedURL;
|
3781 | }
|
3782 |
|
3783 | info.speed = this._ioctl.currentSpeed;
|
3784 | info.loaderType = this._ioctl.loaderType;
|
3785 | info.currentSegmentIndex = this._currentSegmentIndex;
|
3786 | info.totalSegmentCount = this._mediaDataSource.segments.length;
|
3787 |
|
3788 | this._emitter.emit(_transmuxingEvents2.default.STATISTICS_INFO, info);
|
3789 | }
|
3790 | }]);
|
3791 |
|
3792 | return TransmuxingController;
|
3793 | }();
|
3794 |
|
3795 | exports.default = TransmuxingController;
|
3796 |
|
3797 | },{"../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){
|
3798 | ;
|
3799 |
|
3800 | Object.defineProperty(exports, "__esModule", {
|
3801 | value: true
|
3802 | });
|
3803 | /*
|
3804 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
3805 | *
|
3806 | * @author zheng qian <xqq@xqq.im>
|
3807 | *
|
3808 | * Licensed under the Apache License, Version 2.0 (the "License");
|
3809 | * you may not use this file except in compliance with the License.
|
3810 | * You may obtain a copy of the License at
|
3811 | *
|
3812 | * http://www.apache.org/licenses/LICENSE-2.0
|
3813 | *
|
3814 | * Unless required by applicable law or agreed to in writing, software
|
3815 | * distributed under the License is distributed on an "AS IS" BASIS,
|
3816 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
3817 | * See the License for the specific language governing permissions and
|
3818 | * limitations under the License.
|
3819 | */
|
3820 |
|
3821 | var TransmuxingEvents = {
|
3822 | IO_ERROR: 'io_error',
|
3823 | DEMUX_ERROR: 'demux_error',
|
3824 | INIT_SEGMENT: 'init_segment',
|
3825 | MEDIA_SEGMENT: 'media_segment',
|
3826 | LOADING_COMPLETE: 'loading_complete',
|
3827 | RECOVERED_EARLY_EOF: 'recovered_early_eof',
|
3828 | MEDIA_INFO: 'media_info',
|
3829 | METADATA_ARRIVED: 'metadata_arrived',
|
3830 | SCRIPTDATA_ARRIVED: 'scriptdata_arrived',
|
3831 | STATISTICS_INFO: 'statistics_info',
|
3832 | RECOMMEND_SEEKPOINT: 'recommend_seekpoint'
|
3833 | };
|
3834 |
|
3835 | exports.default = TransmuxingEvents;
|
3836 |
|
3837 | },{}],14:[function(_dereq_,module,exports){
|
3838 | ;
|
3839 |
|
3840 | Object.defineProperty(exports, "__esModule", {
|
3841 | value: true
|
3842 | });
|
3843 |
|
3844 | var _logger = _dereq_('../utils/logger.js');
|
3845 |
|
3846 | var _logger2 = _interopRequireDefault(_logger);
|
3847 |
|
3848 | var _loggingControl = _dereq_('../utils/logging-control.js');
|
3849 |
|
3850 | var _loggingControl2 = _interopRequireDefault(_loggingControl);
|
3851 |
|
3852 | var _polyfill = _dereq_('../utils/polyfill.js');
|
3853 |
|
3854 | var _polyfill2 = _interopRequireDefault(_polyfill);
|
3855 |
|
3856 | var _transmuxingController = _dereq_('./transmuxing-controller.js');
|
3857 |
|
3858 | var _transmuxingController2 = _interopRequireDefault(_transmuxingController);
|
3859 |
|
3860 | var _transmuxingEvents = _dereq_('./transmuxing-events.js');
|
3861 |
|
3862 | var _transmuxingEvents2 = _interopRequireDefault(_transmuxingEvents);
|
3863 |
|
3864 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
3865 |
|
3866 | /* post message to worker:
|
3867 | data: {
|
3868 | cmd: string
|
3869 | param: any
|
3870 | }
|
3871 |
|
3872 | receive message from worker:
|
3873 | data: {
|
3874 | msg: string,
|
3875 | data: any
|
3876 | }
|
3877 | */
|
3878 |
|
3879 | var TransmuxingWorker = function TransmuxingWorker(self) {
|
3880 |
|
3881 | var TAG = 'TransmuxingWorker';
|
3882 | var controller = null;
|
3883 | var logcatListener = onLogcatCallback.bind(this);
|
3884 |
|
3885 | _polyfill2.default.install();
|
3886 |
|
3887 | self.addEventListener('message', function (e) {
|
3888 | switch (e.data.cmd) {
|
3889 | case 'init':
|
3890 | controller = new _transmuxingController2.default(e.data.param[0], e.data.param[1]);
|
3891 | controller.on(_transmuxingEvents2.default.IO_ERROR, onIOError.bind(this));
|
3892 | controller.on(_transmuxingEvents2.default.DEMUX_ERROR, onDemuxError.bind(this));
|
3893 | controller.on(_transmuxingEvents2.default.INIT_SEGMENT, onInitSegment.bind(this));
|
3894 | controller.on(_transmuxingEvents2.default.MEDIA_SEGMENT, onMediaSegment.bind(this));
|
3895 | controller.on(_transmuxingEvents2.default.LOADING_COMPLETE, onLoadingComplete.bind(this));
|
3896 | controller.on(_transmuxingEvents2.default.RECOVERED_EARLY_EOF, onRecoveredEarlyEof.bind(this));
|
3897 | controller.on(_transmuxingEvents2.default.MEDIA_INFO, onMediaInfo.bind(this));
|
3898 | controller.on(_transmuxingEvents2.default.METADATA_ARRIVED, onMetaDataArrived.bind(this));
|
3899 | controller.on(_transmuxingEvents2.default.SCRIPTDATA_ARRIVED, onScriptDataArrived.bind(this));
|
3900 | controller.on(_transmuxingEvents2.default.STATISTICS_INFO, onStatisticsInfo.bind(this));
|
3901 | controller.on(_transmuxingEvents2.default.RECOMMEND_SEEKPOINT, onRecommendSeekpoint.bind(this));
|
3902 | break;
|
3903 | case 'destroy':
|
3904 | if (controller) {
|
3905 | controller.destroy();
|
3906 | controller = null;
|
3907 | }
|
3908 | self.postMessage({ msg: 'destroyed' });
|
3909 | break;
|
3910 | case 'start':
|
3911 | controller.start();
|
3912 | break;
|
3913 | case 'stop':
|
3914 | controller.stop();
|
3915 | break;
|
3916 | case 'seek':
|
3917 | controller.seek(e.data.param);
|
3918 | break;
|
3919 | case 'pause':
|
3920 | controller.pause();
|
3921 | break;
|
3922 | case 'resume':
|
3923 | controller.resume();
|
3924 | break;
|
3925 | case 'logging_config':
|
3926 | {
|
3927 | var config = e.data.param;
|
3928 | _loggingControl2.default.applyConfig(config);
|
3929 |
|
3930 | if (config.enableCallback === true) {
|
3931 | _loggingControl2.default.addLogListener(logcatListener);
|
3932 | } else {
|
3933 | _loggingControl2.default.removeLogListener(logcatListener);
|
3934 | }
|
3935 | break;
|
3936 | }
|
3937 | }
|
3938 | });
|
3939 |
|
3940 | function onInitSegment(type, initSegment) {
|
3941 | var obj = {
|
3942 | msg: _transmuxingEvents2.default.INIT_SEGMENT,
|
3943 | data: {
|
3944 | type: type,
|
3945 | data: initSegment
|
3946 | }
|
3947 | };
|
3948 | self.postMessage(obj, [initSegment.data]); // data: ArrayBuffer
|
3949 | }
|
3950 |
|
3951 | function onMediaSegment(type, mediaSegment) {
|
3952 | var obj = {
|
3953 | msg: _transmuxingEvents2.default.MEDIA_SEGMENT,
|
3954 | data: {
|
3955 | type: type,
|
3956 | data: mediaSegment
|
3957 | }
|
3958 | };
|
3959 | self.postMessage(obj, [mediaSegment.data]); // data: ArrayBuffer
|
3960 | }
|
3961 |
|
3962 | function onLoadingComplete() {
|
3963 | var obj = {
|
3964 | msg: _transmuxingEvents2.default.LOADING_COMPLETE
|
3965 | };
|
3966 | self.postMessage(obj);
|
3967 | }
|
3968 |
|
3969 | function onRecoveredEarlyEof() {
|
3970 | var obj = {
|
3971 | msg: _transmuxingEvents2.default.RECOVERED_EARLY_EOF
|
3972 | };
|
3973 | self.postMessage(obj);
|
3974 | }
|
3975 |
|
3976 | function onMediaInfo(mediaInfo) {
|
3977 | var obj = {
|
3978 | msg: _transmuxingEvents2.default.MEDIA_INFO,
|
3979 | data: mediaInfo
|
3980 | };
|
3981 | self.postMessage(obj);
|
3982 | }
|
3983 |
|
3984 | function onMetaDataArrived(metadata) {
|
3985 | var obj = {
|
3986 | msg: _transmuxingEvents2.default.METADATA_ARRIVED,
|
3987 | data: metadata
|
3988 | };
|
3989 | self.postMessage(obj);
|
3990 | }
|
3991 |
|
3992 | function onScriptDataArrived(data) {
|
3993 | var obj = {
|
3994 | msg: _transmuxingEvents2.default.SCRIPTDATA_ARRIVED,
|
3995 | data: data
|
3996 | };
|
3997 | self.postMessage(obj);
|
3998 | }
|
3999 |
|
4000 | function onStatisticsInfo(statInfo) {
|
4001 | var obj = {
|
4002 | msg: _transmuxingEvents2.default.STATISTICS_INFO,
|
4003 | data: statInfo
|
4004 | };
|
4005 | self.postMessage(obj);
|
4006 | }
|
4007 |
|
4008 | function onIOError(type, info) {
|
4009 | self.postMessage({
|
4010 | msg: _transmuxingEvents2.default.IO_ERROR,
|
4011 | data: {
|
4012 | type: type,
|
4013 | info: info
|
4014 | }
|
4015 | });
|
4016 | }
|
4017 |
|
4018 | function onDemuxError(type, info) {
|
4019 | self.postMessage({
|
4020 | msg: _transmuxingEvents2.default.DEMUX_ERROR,
|
4021 | data: {
|
4022 | type: type,
|
4023 | info: info
|
4024 | }
|
4025 | });
|
4026 | }
|
4027 |
|
4028 | function onRecommendSeekpoint(milliseconds) {
|
4029 | self.postMessage({
|
4030 | msg: _transmuxingEvents2.default.RECOMMEND_SEEKPOINT,
|
4031 | data: milliseconds
|
4032 | });
|
4033 | }
|
4034 |
|
4035 | function onLogcatCallback(type, str) {
|
4036 | self.postMessage({
|
4037 | msg: 'logcat_callback',
|
4038 | data: {
|
4039 | type: type,
|
4040 | logcat: str
|
4041 | }
|
4042 | });
|
4043 | }
|
4044 | }; /*
|
4045 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
4046 | *
|
4047 | * @author zheng qian <xqq@xqq.im>
|
4048 | *
|
4049 | * Licensed under the Apache License, Version 2.0 (the "License");
|
4050 | * you may not use this file except in compliance with the License.
|
4051 | * You may obtain a copy of the License at
|
4052 | *
|
4053 | * http://www.apache.org/licenses/LICENSE-2.0
|
4054 | *
|
4055 | * Unless required by applicable law or agreed to in writing, software
|
4056 | * distributed under the License is distributed on an "AS IS" BASIS,
|
4057 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
4058 | * See the License for the specific language governing permissions and
|
4059 | * limitations under the License.
|
4060 | */
|
4061 |
|
4062 | exports.default = TransmuxingWorker;
|
4063 |
|
4064 | },{"../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){
|
4065 | ;
|
4066 |
|
4067 | Object.defineProperty(exports, "__esModule", {
|
4068 | value: true
|
4069 | });
|
4070 |
|
4071 | 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; }; }(); /*
|
4072 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
4073 | *
|
4074 | * @author zheng qian <xqq@xqq.im>
|
4075 | *
|
4076 | * Licensed under the Apache License, Version 2.0 (the "License");
|
4077 | * you may not use this file except in compliance with the License.
|
4078 | * You may obtain a copy of the License at
|
4079 | *
|
4080 | * http://www.apache.org/licenses/LICENSE-2.0
|
4081 | *
|
4082 | * Unless required by applicable law or agreed to in writing, software
|
4083 | * distributed under the License is distributed on an "AS IS" BASIS,
|
4084 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
4085 | * See the License for the specific language governing permissions and
|
4086 | * limitations under the License.
|
4087 | */
|
4088 |
|
4089 | var _logger = _dereq_('../utils/logger.js');
|
4090 |
|
4091 | var _logger2 = _interopRequireDefault(_logger);
|
4092 |
|
4093 | var _utf8Conv = _dereq_('../utils/utf8-conv.js');
|
4094 |
|
4095 | var _utf8Conv2 = _interopRequireDefault(_utf8Conv);
|
4096 |
|
4097 | var _exception = _dereq_('../utils/exception.js');
|
4098 |
|
4099 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
4100 |
|
4101 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
4102 |
|
4103 | var le = function () {
|
4104 | var buf = new ArrayBuffer(2);
|
4105 | new DataView(buf).setInt16(0, 256, true); // little-endian write
|
4106 | return new Int16Array(buf)[0] === 256; // platform-spec read, if equal then LE
|
4107 | }();
|
4108 |
|
4109 | var AMF = function () {
|
4110 | function AMF() {
|
4111 | _classCallCheck(this, AMF);
|
4112 | }
|
4113 |
|
4114 | _createClass(AMF, null, [{
|
4115 | key: 'parseScriptData',
|
4116 | value: function parseScriptData(arrayBuffer, dataOffset, dataSize) {
|
4117 | var data = {};
|
4118 |
|
4119 | try {
|
4120 | var name = AMF.parseValue(arrayBuffer, dataOffset, dataSize);
|
4121 | var value = AMF.parseValue(arrayBuffer, dataOffset + name.size, dataSize - name.size);
|
4122 |
|
4123 | data[name.data] = value.data;
|
4124 | } catch (e) {
|
4125 | _logger2.default.e('AMF', e.toString());
|
4126 | }
|
4127 |
|
4128 | return data;
|
4129 | }
|
4130 | }, {
|
4131 | key: 'parseObject',
|
4132 | value: function parseObject(arrayBuffer, dataOffset, dataSize) {
|
4133 | if (dataSize < 3) {
|
4134 | throw new _exception.IllegalStateException('Data not enough when parse ScriptDataObject');
|
4135 | }
|
4136 | var name = AMF.parseString(arrayBuffer, dataOffset, dataSize);
|
4137 | var value = AMF.parseValue(arrayBuffer, dataOffset + name.size, dataSize - name.size);
|
4138 | var isObjectEnd = value.objectEnd;
|
4139 |
|
4140 | return {
|
4141 | data: {
|
4142 | name: name.data,
|
4143 | value: value.data
|
4144 | },
|
4145 | size: name.size + value.size,
|
4146 | objectEnd: isObjectEnd
|
4147 | };
|
4148 | }
|
4149 | }, {
|
4150 | key: 'parseVariable',
|
4151 | value: function parseVariable(arrayBuffer, dataOffset, dataSize) {
|
4152 | return AMF.parseObject(arrayBuffer, dataOffset, dataSize);
|
4153 | }
|
4154 | }, {
|
4155 | key: 'parseString',
|
4156 | value: function parseString(arrayBuffer, dataOffset, dataSize) {
|
4157 | if (dataSize < 2) {
|
4158 | throw new _exception.IllegalStateException('Data not enough when parse String');
|
4159 | }
|
4160 | var v = new DataView(arrayBuffer, dataOffset, dataSize);
|
4161 | var length = v.getUint16(0, !le);
|
4162 |
|
4163 | var str = void 0;
|
4164 | if (length > 0) {
|
4165 | str = (0, _utf8Conv2.default)(new Uint8Array(arrayBuffer, dataOffset + 2, length));
|
4166 | } else {
|
4167 | str = '';
|
4168 | }
|
4169 |
|
4170 | return {
|
4171 | data: str,
|
4172 | size: 2 + length
|
4173 | };
|
4174 | }
|
4175 | }, {
|
4176 | key: 'parseLongString',
|
4177 | value: function parseLongString(arrayBuffer, dataOffset, dataSize) {
|
4178 | if (dataSize < 4) {
|
4179 | throw new _exception.IllegalStateException('Data not enough when parse LongString');
|
4180 | }
|
4181 | var v = new DataView(arrayBuffer, dataOffset, dataSize);
|
4182 | var length = v.getUint32(0, !le);
|
4183 |
|
4184 | var str = void 0;
|
4185 | if (length > 0) {
|
4186 | str = (0, _utf8Conv2.default)(new Uint8Array(arrayBuffer, dataOffset + 4, length));
|
4187 | } else {
|
4188 | str = '';
|
4189 | }
|
4190 |
|
4191 | return {
|
4192 | data: str,
|
4193 | size: 4 + length
|
4194 | };
|
4195 | }
|
4196 | }, {
|
4197 | key: 'parseDate',
|
4198 | value: function parseDate(arrayBuffer, dataOffset, dataSize) {
|
4199 | if (dataSize < 10) {
|
4200 | throw new _exception.IllegalStateException('Data size invalid when parse Date');
|
4201 | }
|
4202 | var v = new DataView(arrayBuffer, dataOffset, dataSize);
|
4203 | var timestamp = v.getFloat64(0, !le);
|
4204 | var localTimeOffset = v.getInt16(8, !le);
|
4205 | timestamp += localTimeOffset * 60 * 1000; // get UTC time
|
4206 |
|
4207 | return {
|
4208 | data: new Date(timestamp),
|
4209 | size: 8 + 2
|
4210 | };
|
4211 | }
|
4212 | }, {
|
4213 | key: 'parseValue',
|
4214 | value: function parseValue(arrayBuffer, dataOffset, dataSize) {
|
4215 | if (dataSize < 1) {
|
4216 | throw new _exception.IllegalStateException('Data not enough when parse Value');
|
4217 | }
|
4218 |
|
4219 | var v = new DataView(arrayBuffer, dataOffset, dataSize);
|
4220 |
|
4221 | var offset = 1;
|
4222 | var type = v.getUint8(0);
|
4223 | var value = void 0;
|
4224 | var objectEnd = false;
|
4225 |
|
4226 | try {
|
4227 | switch (type) {
|
4228 | case 0:
|
4229 | // Number(Double) type
|
4230 | value = v.getFloat64(1, !le);
|
4231 | offset += 8;
|
4232 | break;
|
4233 | case 1:
|
4234 | {
|
4235 | // Boolean type
|
4236 | var b = v.getUint8(1);
|
4237 | value = b ? true : false;
|
4238 | offset += 1;
|
4239 | break;
|
4240 | }
|
4241 | case 2:
|
4242 | {
|
4243 | // String type
|
4244 | var amfstr = AMF.parseString(arrayBuffer, dataOffset + 1, dataSize - 1);
|
4245 | value = amfstr.data;
|
4246 | offset += amfstr.size;
|
4247 | break;
|
4248 | }
|
4249 | case 3:
|
4250 | {
|
4251 | // Object(s) type
|
4252 | value = {};
|
4253 | var terminal = 0; // workaround for malformed Objects which has missing ScriptDataObjectEnd
|
4254 | if ((v.getUint32(dataSize - 4, !le) & 0x00FFFFFF) === 9) {
|
4255 | terminal = 3;
|
4256 | }
|
4257 | while (offset < dataSize - 4) {
|
4258 | // 4 === type(UI8) + ScriptDataObjectEnd(UI24)
|
4259 | var amfobj = AMF.parseObject(arrayBuffer, dataOffset + offset, dataSize - offset - terminal);
|
4260 | if (amfobj.objectEnd) break;
|
4261 | value[amfobj.data.name] = amfobj.data.value;
|
4262 | offset += amfobj.size;
|
4263 | }
|
4264 | if (offset <= dataSize - 3) {
|
4265 | var marker = v.getUint32(offset - 1, !le) & 0x00FFFFFF;
|
4266 | if (marker === 9) {
|
4267 | offset += 3;
|
4268 | }
|
4269 | }
|
4270 | break;
|
4271 | }
|
4272 | case 8:
|
4273 | {
|
4274 | // ECMA array type (Mixed array)
|
4275 | value = {};
|
4276 | offset += 4; // ECMAArrayLength(UI32)
|
4277 | var _terminal = 0; // workaround for malformed MixedArrays which has missing ScriptDataObjectEnd
|
4278 | if ((v.getUint32(dataSize - 4, !le) & 0x00FFFFFF) === 9) {
|
4279 | _terminal = 3;
|
4280 | }
|
4281 | while (offset < dataSize - 8) {
|
4282 | // 8 === type(UI8) + ECMAArrayLength(UI32) + ScriptDataVariableEnd(UI24)
|
4283 | var amfvar = AMF.parseVariable(arrayBuffer, dataOffset + offset, dataSize - offset - _terminal);
|
4284 | if (amfvar.objectEnd) break;
|
4285 | value[amfvar.data.name] = amfvar.data.value;
|
4286 | offset += amfvar.size;
|
4287 | }
|
4288 | if (offset <= dataSize - 3) {
|
4289 | var _marker = v.getUint32(offset - 1, !le) & 0x00FFFFFF;
|
4290 | if (_marker === 9) {
|
4291 | offset += 3;
|
4292 | }
|
4293 | }
|
4294 | break;
|
4295 | }
|
4296 | case 9:
|
4297 | // ScriptDataObjectEnd
|
4298 | value = undefined;
|
4299 | offset = 1;
|
4300 | objectEnd = true;
|
4301 | break;
|
4302 | case 10:
|
4303 | {
|
4304 | // Strict array type
|
4305 | // ScriptDataValue[n]. NOTE: according to video_file_format_spec_v10_1.pdf
|
4306 | value = [];
|
4307 | var strictArrayLength = v.getUint32(1, !le);
|
4308 | offset += 4;
|
4309 | for (var i = 0; i < strictArrayLength; i++) {
|
4310 | var val = AMF.parseValue(arrayBuffer, dataOffset + offset, dataSize - offset);
|
4311 | value.push(val.data);
|
4312 | offset += val.size;
|
4313 | }
|
4314 | break;
|
4315 | }
|
4316 | case 11:
|
4317 | {
|
4318 | // Date type
|
4319 | var date = AMF.parseDate(arrayBuffer, dataOffset + 1, dataSize - 1);
|
4320 | value = date.data;
|
4321 | offset += date.size;
|
4322 | break;
|
4323 | }
|
4324 | case 12:
|
4325 | {
|
4326 | // Long string type
|
4327 | var amfLongStr = AMF.parseString(arrayBuffer, dataOffset + 1, dataSize - 1);
|
4328 | value = amfLongStr.data;
|
4329 | offset += amfLongStr.size;
|
4330 | break;
|
4331 | }
|
4332 | default:
|
4333 | // ignore and skip
|
4334 | offset = dataSize;
|
4335 | _logger2.default.w('AMF', 'Unsupported AMF value type ' + type);
|
4336 | }
|
4337 | } catch (e) {
|
4338 | _logger2.default.e('AMF', e.toString());
|
4339 | }
|
4340 |
|
4341 | return {
|
4342 | data: value,
|
4343 | size: offset,
|
4344 | objectEnd: objectEnd
|
4345 | };
|
4346 | }
|
4347 | }]);
|
4348 |
|
4349 | return AMF;
|
4350 | }();
|
4351 |
|
4352 | exports.default = AMF;
|
4353 |
|
4354 | },{"../utils/exception.js":40,"../utils/logger.js":41,"../utils/utf8-conv.js":44}],16:[function(_dereq_,module,exports){
|
4355 | ;
|
4356 |
|
4357 | Object.defineProperty(exports, "__esModule", {
|
4358 | value: true
|
4359 | });
|
4360 | /*
|
4361 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
4362 | *
|
4363 | * @author zheng qian <xqq@xqq.im>
|
4364 | *
|
4365 | * Licensed under the Apache License, Version 2.0 (the "License");
|
4366 | * you may not use this file except in compliance with the License.
|
4367 | * You may obtain a copy of the License at
|
4368 | *
|
4369 | * http://www.apache.org/licenses/LICENSE-2.0
|
4370 | *
|
4371 | * Unless required by applicable law or agreed to in writing, software
|
4372 | * distributed under the License is distributed on an "AS IS" BASIS,
|
4373 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
4374 | * See the License for the specific language governing permissions and
|
4375 | * limitations under the License.
|
4376 | */
|
4377 |
|
4378 | var DemuxErrors = {
|
4379 | OK: 'OK',
|
4380 | FORMAT_ERROR: 'FormatError',
|
4381 | FORMAT_UNSUPPORTED: 'FormatUnsupported',
|
4382 | CODEC_UNSUPPORTED: 'CodecUnsupported'
|
4383 | };
|
4384 |
|
4385 | exports.default = DemuxErrors;
|
4386 |
|
4387 | },{}],17:[function(_dereq_,module,exports){
|
4388 | ;
|
4389 |
|
4390 | Object.defineProperty(exports, "__esModule", {
|
4391 | value: true
|
4392 | });
|
4393 |
|
4394 | 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; }; }(); /*
|
4395 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
4396 | *
|
4397 | * @author zheng qian <xqq@xqq.im>
|
4398 | *
|
4399 | * Licensed under the Apache License, Version 2.0 (the "License");
|
4400 | * you may not use this file except in compliance with the License.
|
4401 | * You may obtain a copy of the License at
|
4402 | *
|
4403 | * http://www.apache.org/licenses/LICENSE-2.0
|
4404 | *
|
4405 | * Unless required by applicable law or agreed to in writing, software
|
4406 | * distributed under the License is distributed on an "AS IS" BASIS,
|
4407 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
4408 | * See the License for the specific language governing permissions and
|
4409 | * limitations under the License.
|
4410 | */
|
4411 |
|
4412 | var _exception = _dereq_('../utils/exception.js');
|
4413 |
|
4414 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
4415 |
|
4416 | // Exponential-Golomb buffer decoder
|
4417 | var ExpGolomb = function () {
|
4418 | function ExpGolomb(uint8array) {
|
4419 | _classCallCheck(this, ExpGolomb);
|
4420 |
|
4421 | this.TAG = 'ExpGolomb';
|
4422 |
|
4423 | this._buffer = uint8array;
|
4424 | this._buffer_index = 0;
|
4425 | this._total_bytes = uint8array.byteLength;
|
4426 | this._total_bits = uint8array.byteLength * 8;
|
4427 | this._current_word = 0;
|
4428 | this._current_word_bits_left = 0;
|
4429 | }
|
4430 |
|
4431 | _createClass(ExpGolomb, [{
|
4432 | key: 'destroy',
|
4433 | value: function destroy() {
|
4434 | this._buffer = null;
|
4435 | }
|
4436 | }, {
|
4437 | key: '_fillCurrentWord',
|
4438 | value: function _fillCurrentWord() {
|
4439 | var buffer_bytes_left = this._total_bytes - this._buffer_index;
|
4440 | if (buffer_bytes_left <= 0) throw new _exception.IllegalStateException('ExpGolomb: _fillCurrentWord() but no bytes available');
|
4441 |
|
4442 | var bytes_read = Math.min(4, buffer_bytes_left);
|
4443 | var word = new Uint8Array(4);
|
4444 | word.set(this._buffer.subarray(this._buffer_index, this._buffer_index + bytes_read));
|
4445 | this._current_word = new DataView(word.buffer).getUint32(0, false);
|
4446 |
|
4447 | this._buffer_index += bytes_read;
|
4448 | this._current_word_bits_left = bytes_read * 8;
|
4449 | }
|
4450 | }, {
|
4451 | key: 'readBits',
|
4452 | value: function readBits(bits) {
|
4453 | if (bits > 32) throw new _exception.InvalidArgumentException('ExpGolomb: readBits() bits exceeded max 32bits!');
|
4454 |
|
4455 | if (bits <= this._current_word_bits_left) {
|
4456 | var _result = this._current_word >>> 32 - bits;
|
4457 | this._current_word <<= bits;
|
4458 | this._current_word_bits_left -= bits;
|
4459 | return _result;
|
4460 | }
|
4461 |
|
4462 | var result = this._current_word_bits_left ? this._current_word : 0;
|
4463 | result = result >>> 32 - this._current_word_bits_left;
|
4464 | var bits_need_left = bits - this._current_word_bits_left;
|
4465 |
|
4466 | this._fillCurrentWord();
|
4467 | var bits_read_next = Math.min(bits_need_left, this._current_word_bits_left);
|
4468 |
|
4469 | var result2 = this._current_word >>> 32 - bits_read_next;
|
4470 | this._current_word <<= bits_read_next;
|
4471 | this._current_word_bits_left -= bits_read_next;
|
4472 |
|
4473 | result = result << bits_read_next | result2;
|
4474 | return result;
|
4475 | }
|
4476 | }, {
|
4477 | key: 'readBool',
|
4478 | value: function readBool() {
|
4479 | return this.readBits(1) === 1;
|
4480 | }
|
4481 | }, {
|
4482 | key: 'readByte',
|
4483 | value: function readByte() {
|
4484 | return this.readBits(8);
|
4485 | }
|
4486 | }, {
|
4487 | key: '_skipLeadingZero',
|
4488 | value: function _skipLeadingZero() {
|
4489 | var zero_count = void 0;
|
4490 | for (zero_count = 0; zero_count < this._current_word_bits_left; zero_count++) {
|
4491 | if (0 !== (this._current_word & 0x80000000 >>> zero_count)) {
|
4492 | this._current_word <<= zero_count;
|
4493 | this._current_word_bits_left -= zero_count;
|
4494 | return zero_count;
|
4495 | }
|
4496 | }
|
4497 | this._fillCurrentWord();
|
4498 | return zero_count + this._skipLeadingZero();
|
4499 | }
|
4500 | }, {
|
4501 | key: 'readUEG',
|
4502 | value: function readUEG() {
|
4503 | // unsigned exponential golomb
|
4504 | var leading_zeros = this._skipLeadingZero();
|
4505 | return this.readBits(leading_zeros + 1) - 1;
|
4506 | }
|
4507 | }, {
|
4508 | key: 'readSEG',
|
4509 | value: function readSEG() {
|
4510 | // signed exponential golomb
|
4511 | var value = this.readUEG();
|
4512 | if (value & 0x01) {
|
4513 | return value + 1 >>> 1;
|
4514 | } else {
|
4515 | return -1 * (value >>> 1);
|
4516 | }
|
4517 | }
|
4518 | }]);
|
4519 |
|
4520 | return ExpGolomb;
|
4521 | }();
|
4522 |
|
4523 | exports.default = ExpGolomb;
|
4524 |
|
4525 | },{"../utils/exception.js":40}],18:[function(_dereq_,module,exports){
|
4526 | ;
|
4527 |
|
4528 | Object.defineProperty(exports, "__esModule", {
|
4529 | value: true
|
4530 | });
|
4531 |
|
4532 | 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; };
|
4533 |
|
4534 | 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; }; }(); /*
|
4535 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
4536 | *
|
4537 | * @author zheng qian <xqq@xqq.im>
|
4538 | *
|
4539 | * Licensed under the Apache License, Version 2.0 (the "License");
|
4540 | * you may not use this file except in compliance with the License.
|
4541 | * You may obtain a copy of the License at
|
4542 | *
|
4543 | * http://www.apache.org/licenses/LICENSE-2.0
|
4544 | *
|
4545 | * Unless required by applicable law or agreed to in writing, software
|
4546 | * distributed under the License is distributed on an "AS IS" BASIS,
|
4547 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
4548 | * See the License for the specific language governing permissions and
|
4549 | * limitations under the License.
|
4550 | */
|
4551 |
|
4552 | var _logger = _dereq_('../utils/logger.js');
|
4553 |
|
4554 | var _logger2 = _interopRequireDefault(_logger);
|
4555 |
|
4556 | var _amfParser = _dereq_('./amf-parser.js');
|
4557 |
|
4558 | var _amfParser2 = _interopRequireDefault(_amfParser);
|
4559 |
|
4560 | var _spsParser = _dereq_('./sps-parser.js');
|
4561 |
|
4562 | var _spsParser2 = _interopRequireDefault(_spsParser);
|
4563 |
|
4564 | var _demuxErrors = _dereq_('./demux-errors.js');
|
4565 |
|
4566 | var _demuxErrors2 = _interopRequireDefault(_demuxErrors);
|
4567 |
|
4568 | var _mediaInfo = _dereq_('../core/media-info.js');
|
4569 |
|
4570 | var _mediaInfo2 = _interopRequireDefault(_mediaInfo);
|
4571 |
|
4572 | var _exception = _dereq_('../utils/exception.js');
|
4573 |
|
4574 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
4575 |
|
4576 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
4577 |
|
4578 | function Swap16(src) {
|
4579 | return src >>> 8 & 0xFF | (src & 0xFF) << 8;
|
4580 | }
|
4581 |
|
4582 | function Swap32(src) {
|
4583 | return (src & 0xFF000000) >>> 24 | (src & 0x00FF0000) >>> 8 | (src & 0x0000FF00) << 8 | (src & 0x000000FF) << 24;
|
4584 | }
|
4585 |
|
4586 | function ReadBig32(array, index) {
|
4587 | return array[index] << 24 | array[index + 1] << 16 | array[index + 2] << 8 | array[index + 3];
|
4588 | }
|
4589 |
|
4590 | var FLVDemuxer = function () {
|
4591 | function FLVDemuxer(probeData, config) {
|
4592 | _classCallCheck(this, FLVDemuxer);
|
4593 |
|
4594 | this.TAG = 'FLVDemuxer';
|
4595 |
|
4596 | this._config = config;
|
4597 |
|
4598 | this._onError = null;
|
4599 | this._onMediaInfo = null;
|
4600 | this._onMetaDataArrived = null;
|
4601 | this._onScriptDataArrived = null;
|
4602 | this._onTrackMetadata = null;
|
4603 | this._onDataAvailable = null;
|
4604 |
|
4605 | this._dataOffset = probeData.dataOffset;
|
4606 | this._firstParse = true;
|
4607 | this._dispatch = false;
|
4608 |
|
4609 | this._hasAudio = probeData.hasAudioTrack;
|
4610 | this._hasVideo = probeData.hasVideoTrack;
|
4611 |
|
4612 | this._hasAudioFlagOverrided = false;
|
4613 | this._hasVideoFlagOverrided = false;
|
4614 |
|
4615 | this._audioInitialMetadataDispatched = false;
|
4616 | this._videoInitialMetadataDispatched = false;
|
4617 |
|
4618 | this._mediaInfo = new _mediaInfo2.default();
|
4619 | this._mediaInfo.hasAudio = this._hasAudio;
|
4620 | this._mediaInfo.hasVideo = this._hasVideo;
|
4621 | this._metadata = null;
|
4622 | this._audioMetadata = null;
|
4623 | this._videoMetadata = null;
|
4624 |
|
4625 | this._naluLengthSize = 4;
|
4626 | this._timestampBase = 0; // int32, in milliseconds
|
4627 | this._timescale = 1000;
|
4628 | this._duration = 0; // int32, in milliseconds
|
4629 | this._durationOverrided = false;
|
4630 | this._referenceFrameRate = {
|
4631 | fixed: true,
|
4632 | fps: 23.976,
|
4633 | fps_num: 23976,
|
4634 | fps_den: 1000
|
4635 | };
|
4636 |
|
4637 | this._flvSoundRateTable = [5500, 11025, 22050, 44100, 48000];
|
4638 |
|
4639 | this._mpegSamplingRates = [96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350];
|
4640 |
|
4641 | this._mpegAudioV10SampleRateTable = [44100, 48000, 32000, 0];
|
4642 | this._mpegAudioV20SampleRateTable = [22050, 24000, 16000, 0];
|
4643 | this._mpegAudioV25SampleRateTable = [11025, 12000, 8000, 0];
|
4644 |
|
4645 | this._mpegAudioL1BitRateTable = [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, -1];
|
4646 | this._mpegAudioL2BitRateTable = [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, -1];
|
4647 | this._mpegAudioL3BitRateTable = [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, -1];
|
4648 |
|
4649 | this._videoTrack = { type: 'video', id: 1, sequenceNumber: 0, samples: [], length: 0 };
|
4650 | this._audioTrack = { type: 'audio', id: 2, sequenceNumber: 0, samples: [], length: 0 };
|
4651 |
|
4652 | this._littleEndian = function () {
|
4653 | var buf = new ArrayBuffer(2);
|
4654 | new DataView(buf).setInt16(0, 256, true); // little-endian write
|
4655 | return new Int16Array(buf)[0] === 256; // platform-spec read, if equal then LE
|
4656 | }();
|
4657 | }
|
4658 |
|
4659 | _createClass(FLVDemuxer, [{
|
4660 | key: 'destroy',
|
4661 | value: function destroy() {
|
4662 | this._mediaInfo = null;
|
4663 | this._metadata = null;
|
4664 | this._audioMetadata = null;
|
4665 | this._videoMetadata = null;
|
4666 | this._videoTrack = null;
|
4667 | this._audioTrack = null;
|
4668 |
|
4669 | this._onError = null;
|
4670 | this._onMediaInfo = null;
|
4671 | this._onMetaDataArrived = null;
|
4672 | this._onScriptDataArrived = null;
|
4673 | this._onTrackMetadata = null;
|
4674 | this._onDataAvailable = null;
|
4675 | }
|
4676 | }, {
|
4677 | key: 'bindDataSource',
|
4678 | value: function bindDataSource(loader) {
|
4679 | loader.onDataArrival = this.parseChunks.bind(this);
|
4680 | return this;
|
4681 | }
|
4682 |
|
4683 | // prototype: function(type: string, metadata: any): void
|
4684 |
|
4685 | }, {
|
4686 | key: 'resetMediaInfo',
|
4687 | value: function resetMediaInfo() {
|
4688 | this._mediaInfo = new _mediaInfo2.default();
|
4689 | }
|
4690 | }, {
|
4691 | key: '_isInitialMetadataDispatched',
|
4692 | value: function _isInitialMetadataDispatched() {
|
4693 | if (this._hasAudio && this._hasVideo) {
|
4694 | // both audio & video
|
4695 | return this._audioInitialMetadataDispatched && this._videoInitialMetadataDispatched;
|
4696 | }
|
4697 | if (this._hasAudio && !this._hasVideo) {
|
4698 | // audio only
|
4699 | return this._audioInitialMetadataDispatched;
|
4700 | }
|
4701 | if (!this._hasAudio && this._hasVideo) {
|
4702 | // video only
|
4703 | return this._videoInitialMetadataDispatched;
|
4704 | }
|
4705 | return false;
|
4706 | }
|
4707 |
|
4708 | // function parseChunks(chunk: ArrayBuffer, byteStart: number): number;
|
4709 |
|
4710 | }, {
|
4711 | key: 'parseChunks',
|
4712 | value: function parseChunks(chunk, byteStart) {
|
4713 | if (!this._onError || !this._onMediaInfo || !this._onTrackMetadata || !this._onDataAvailable) {
|
4714 | throw new _exception.IllegalStateException('Flv: onError & onMediaInfo & onTrackMetadata & onDataAvailable callback must be specified');
|
4715 | }
|
4716 |
|
4717 | var offset = 0;
|
4718 | var le = this._littleEndian;
|
4719 |
|
4720 | if (byteStart === 0) {
|
4721 | // buffer with FLV header
|
4722 | if (chunk.byteLength > 13) {
|
4723 | var probeData = FLVDemuxer.probe(chunk);
|
4724 | offset = probeData.dataOffset;
|
4725 | } else {
|
4726 | return 0;
|
4727 | }
|
4728 | }
|
4729 |
|
4730 | if (this._firstParse) {
|
4731 | // handle PreviousTagSize0 before Tag1
|
4732 | this._firstParse = false;
|
4733 | if (byteStart + offset !== this._dataOffset) {
|
4734 | _logger2.default.w(this.TAG, 'First time parsing but chunk byteStart invalid!');
|
4735 | }
|
4736 |
|
4737 | var v = new DataView(chunk, offset);
|
4738 | var prevTagSize0 = v.getUint32(0, !le);
|
4739 | if (prevTagSize0 !== 0) {
|
4740 | _logger2.default.w(this.TAG, 'PrevTagSize0 !== 0 !!!');
|
4741 | }
|
4742 | offset += 4;
|
4743 | }
|
4744 |
|
4745 | while (offset < chunk.byteLength) {
|
4746 | this._dispatch = true;
|
4747 |
|
4748 | var _v = new DataView(chunk, offset);
|
4749 |
|
4750 | if (offset + 11 + 4 > chunk.byteLength) {
|
4751 | // data not enough for parsing an flv tag
|
4752 | break;
|
4753 | }
|
4754 |
|
4755 | var tagType = _v.getUint8(0);
|
4756 | var dataSize = _v.getUint32(0, !le) & 0x00FFFFFF;
|
4757 |
|
4758 | if (offset + 11 + dataSize + 4 > chunk.byteLength) {
|
4759 | // data not enough for parsing actual data body
|
4760 | break;
|
4761 | }
|
4762 |
|
4763 | if (tagType !== 8 && tagType !== 9 && tagType !== 18) {
|
4764 | _logger2.default.w(this.TAG, 'Unsupported tag type ' + tagType + ', skipped');
|
4765 | // consume the whole tag (skip it)
|
4766 | offset += 11 + dataSize + 4;
|
4767 | continue;
|
4768 | }
|
4769 |
|
4770 | var ts2 = _v.getUint8(4);
|
4771 | var ts1 = _v.getUint8(5);
|
4772 | var ts0 = _v.getUint8(6);
|
4773 | var ts3 = _v.getUint8(7);
|
4774 |
|
4775 | var timestamp = ts0 | ts1 << 8 | ts2 << 16 | ts3 << 24;
|
4776 |
|
4777 | var streamId = _v.getUint32(7, !le) & 0x00FFFFFF;
|
4778 | if (streamId !== 0) {
|
4779 | _logger2.default.w(this.TAG, 'Meet tag which has StreamID != 0!');
|
4780 | }
|
4781 |
|
4782 | var dataOffset = offset + 11;
|
4783 |
|
4784 | switch (tagType) {
|
4785 | case 8:
|
4786 | // Audio
|
4787 | this._parseAudioData(chunk, dataOffset, dataSize, timestamp);
|
4788 | break;
|
4789 | case 9:
|
4790 | // Video
|
4791 | this._parseVideoData(chunk, dataOffset, dataSize, timestamp, byteStart + offset);
|
4792 | break;
|
4793 | case 18:
|
4794 | // ScriptDataObject
|
4795 | this._parseScriptData(chunk, dataOffset, dataSize);
|
4796 | break;
|
4797 | }
|
4798 |
|
4799 | var prevTagSize = _v.getUint32(11 + dataSize, !le);
|
4800 | if (prevTagSize !== 11 + dataSize) {
|
4801 | _logger2.default.w(this.TAG, 'Invalid PrevTagSize ' + prevTagSize);
|
4802 | }
|
4803 |
|
4804 | offset += 11 + dataSize + 4; // tagBody + dataSize + prevTagSize
|
4805 | }
|
4806 |
|
4807 | // dispatch parsed frames to consumer (typically, the remuxer)
|
4808 | if (this._isInitialMetadataDispatched()) {
|
4809 | if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {
|
4810 | this._onDataAvailable(this._audioTrack, this._videoTrack);
|
4811 | }
|
4812 | }
|
4813 |
|
4814 | return offset; // consumed bytes, just equals latest offset index
|
4815 | }
|
4816 | }, {
|
4817 | key: '_parseScriptData',
|
4818 | value: function _parseScriptData(arrayBuffer, dataOffset, dataSize) {
|
4819 | var scriptData = _amfParser2.default.parseScriptData(arrayBuffer, dataOffset, dataSize);
|
4820 |
|
4821 | if (scriptData.hasOwnProperty('onMetaData')) {
|
4822 | if (scriptData.onMetaData == null || _typeof(scriptData.onMetaData) !== 'object') {
|
4823 | _logger2.default.w(this.TAG, 'Invalid onMetaData structure!');
|
4824 | return;
|
4825 | }
|
4826 | if (this._metadata) {
|
4827 | _logger2.default.w(this.TAG, 'Found another onMetaData tag!');
|
4828 | }
|
4829 | this._metadata = scriptData;
|
4830 | var onMetaData = this._metadata.onMetaData;
|
4831 |
|
4832 | if (this._onMetaDataArrived) {
|
4833 | this._onMetaDataArrived(Object.assign({}, onMetaData));
|
4834 | }
|
4835 |
|
4836 | if (typeof onMetaData.hasAudio === 'boolean') {
|
4837 | // hasAudio
|
4838 | if (this._hasAudioFlagOverrided === false) {
|
4839 | this._hasAudio = onMetaData.hasAudio;
|
4840 | this._mediaInfo.hasAudio = this._hasAudio;
|
4841 | }
|
4842 | }
|
4843 | if (typeof onMetaData.hasVideo === 'boolean') {
|
4844 | // hasVideo
|
4845 | if (this._hasVideoFlagOverrided === false) {
|
4846 | this._hasVideo = onMetaData.hasVideo;
|
4847 | this._mediaInfo.hasVideo = this._hasVideo;
|
4848 | }
|
4849 | }
|
4850 | if (typeof onMetaData.audiodatarate === 'number') {
|
4851 | // audiodatarate
|
4852 | this._mediaInfo.audioDataRate = onMetaData.audiodatarate;
|
4853 | }
|
4854 | if (typeof onMetaData.videodatarate === 'number') {
|
4855 | // videodatarate
|
4856 | this._mediaInfo.videoDataRate = onMetaData.videodatarate;
|
4857 | }
|
4858 | if (typeof onMetaData.width === 'number') {
|
4859 | // width
|
4860 | this._mediaInfo.width = onMetaData.width;
|
4861 | }
|
4862 | if (typeof onMetaData.height === 'number') {
|
4863 | // height
|
4864 | this._mediaInfo.height = onMetaData.height;
|
4865 | }
|
4866 | if (typeof onMetaData.duration === 'number') {
|
4867 | // duration
|
4868 | if (!this._durationOverrided) {
|
4869 | var duration = Math.floor(onMetaData.duration * this._timescale);
|
4870 | this._duration = duration;
|
4871 | this._mediaInfo.duration = duration;
|
4872 | }
|
4873 | } else {
|
4874 | this._mediaInfo.duration = 0;
|
4875 | }
|
4876 | if (typeof onMetaData.framerate === 'number') {
|
4877 | // framerate
|
4878 | var fps_num = Math.floor(onMetaData.framerate * 1000);
|
4879 | if (fps_num > 0) {
|
4880 | var fps = fps_num / 1000;
|
4881 | this._referenceFrameRate.fixed = true;
|
4882 | this._referenceFrameRate.fps = fps;
|
4883 | this._referenceFrameRate.fps_num = fps_num;
|
4884 | this._referenceFrameRate.fps_den = 1000;
|
4885 | this._mediaInfo.fps = fps;
|
4886 | }
|
4887 | }
|
4888 | if (_typeof(onMetaData.keyframes) === 'object') {
|
4889 | // keyframes
|
4890 | this._mediaInfo.hasKeyframesIndex = true;
|
4891 | var keyframes = onMetaData.keyframes;
|
4892 | this._mediaInfo.keyframesIndex = this._parseKeyframesIndex(keyframes);
|
4893 | onMetaData.keyframes = null; // keyframes has been extracted, remove it
|
4894 | } else {
|
4895 | this._mediaInfo.hasKeyframesIndex = false;
|
4896 | }
|
4897 | this._dispatch = false;
|
4898 | this._mediaInfo.metadata = onMetaData;
|
4899 | _logger2.default.v(this.TAG, 'Parsed onMetaData');
|
4900 | if (this._mediaInfo.isComplete()) {
|
4901 | this._onMediaInfo(this._mediaInfo);
|
4902 | }
|
4903 | }
|
4904 |
|
4905 | if (Object.keys(scriptData).length > 0) {
|
4906 | if (this._onScriptDataArrived) {
|
4907 | this._onScriptDataArrived(Object.assign({}, scriptData));
|
4908 | }
|
4909 | }
|
4910 | }
|
4911 | }, {
|
4912 | key: '_parseKeyframesIndex',
|
4913 | value: function _parseKeyframesIndex(keyframes) {
|
4914 | var times = [];
|
4915 | var filepositions = [];
|
4916 |
|
4917 | // ignore first keyframe which is actually AVC Sequence Header (AVCDecoderConfigurationRecord)
|
4918 | for (var i = 1; i < keyframes.times.length; i++) {
|
4919 | var time = this._timestampBase + Math.floor(keyframes.times[i] * 1000);
|
4920 | times.push(time);
|
4921 | filepositions.push(keyframes.filepositions[i]);
|
4922 | }
|
4923 |
|
4924 | return {
|
4925 | times: times,
|
4926 | filepositions: filepositions
|
4927 | };
|
4928 | }
|
4929 | }, {
|
4930 | key: '_parseAudioData',
|
4931 | value: function _parseAudioData(arrayBuffer, dataOffset, dataSize, tagTimestamp) {
|
4932 | if (dataSize <= 1) {
|
4933 | _logger2.default.w(this.TAG, 'Flv: Invalid audio packet, missing SoundData payload!');
|
4934 | return;
|
4935 | }
|
4936 |
|
4937 | if (this._hasAudioFlagOverrided === true && this._hasAudio === false) {
|
4938 | // If hasAudio: false indicated explicitly in MediaDataSource,
|
4939 | // Ignore all the audio packets
|
4940 | return;
|
4941 | }
|
4942 |
|
4943 | var le = this._littleEndian;
|
4944 | var v = new DataView(arrayBuffer, dataOffset, dataSize);
|
4945 |
|
4946 | var soundSpec = v.getUint8(0);
|
4947 |
|
4948 | var soundFormat = soundSpec >>> 4;
|
4949 | if (soundFormat !== 2 && soundFormat !== 10) {
|
4950 | // MP3 or AAC
|
4951 | this._onError(_demuxErrors2.default.CODEC_UNSUPPORTED, 'Flv: Unsupported audio codec idx: ' + soundFormat);
|
4952 | return;
|
4953 | }
|
4954 |
|
4955 | var soundRate = 0;
|
4956 | var soundRateIndex = (soundSpec & 12) >>> 2;
|
4957 | if (soundRateIndex >= 0 && soundRateIndex <= 4) {
|
4958 | soundRate = this._flvSoundRateTable[soundRateIndex];
|
4959 | } else {
|
4960 | this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: Invalid audio sample rate idx: ' + soundRateIndex);
|
4961 | return;
|
4962 | }
|
4963 |
|
4964 | var soundSize = (soundSpec & 2) >>> 1; // unused
|
4965 | var soundType = soundSpec & 1;
|
4966 |
|
4967 | var meta = this._audioMetadata;
|
4968 | var track = this._audioTrack;
|
4969 |
|
4970 | if (!meta) {
|
4971 | if (this._hasAudio === false && this._hasAudioFlagOverrided === false) {
|
4972 | this._hasAudio = true;
|
4973 | this._mediaInfo.hasAudio = true;
|
4974 | }
|
4975 |
|
4976 | // initial metadata
|
4977 | meta = this._audioMetadata = {};
|
4978 | meta.type = 'audio';
|
4979 | meta.id = track.id;
|
4980 | meta.timescale = this._timescale;
|
4981 | meta.duration = this._duration;
|
4982 | meta.audioSampleRate = soundRate;
|
4983 | meta.channelCount = soundType === 0 ? 1 : 2;
|
4984 | }
|
4985 |
|
4986 | if (soundFormat === 10) {
|
4987 | // AAC
|
4988 | var aacData = this._parseAACAudioData(arrayBuffer, dataOffset + 1, dataSize - 1);
|
4989 | if (aacData == undefined) {
|
4990 | return;
|
4991 | }
|
4992 |
|
4993 | if (aacData.packetType === 0) {
|
4994 | // AAC sequence header (AudioSpecificConfig)
|
4995 | if (meta.config) {
|
4996 | _logger2.default.w(this.TAG, 'Found another AudioSpecificConfig!');
|
4997 | }
|
4998 | var misc = aacData.data;
|
4999 | meta.audioSampleRate = misc.samplingRate;
|
5000 | meta.channelCount = misc.channelCount;
|
5001 | meta.codec = misc.codec;
|
5002 | meta.originalCodec = misc.originalCodec;
|
5003 | meta.config = misc.config;
|
5004 | // The decode result of an aac sample is 1024 PCM samples
|
5005 | meta.refSampleDuration = 1024 / meta.audioSampleRate * meta.timescale;
|
5006 | _logger2.default.v(this.TAG, 'Parsed AudioSpecificConfig');
|
5007 |
|
5008 | if (this._isInitialMetadataDispatched()) {
|
5009 | // Non-initial metadata, force dispatch (or flush) parsed frames to remuxer
|
5010 | if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {
|
5011 | this._onDataAvailable(this._audioTrack, this._videoTrack);
|
5012 | }
|
5013 | } else {
|
5014 | this._audioInitialMetadataDispatched = true;
|
5015 | }
|
5016 | // then notify new metadata
|
5017 | this._dispatch = false;
|
5018 | this._onTrackMetadata('audio', meta);
|
5019 |
|
5020 | var mi = this._mediaInfo;
|
5021 | mi.audioCodec = meta.originalCodec;
|
5022 | mi.audioSampleRate = meta.audioSampleRate;
|
5023 | mi.audioChannelCount = meta.channelCount;
|
5024 | if (mi.hasVideo) {
|
5025 | if (mi.videoCodec != null) {
|
5026 | mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"';
|
5027 | }
|
5028 | } else {
|
5029 | mi.mimeType = 'video/x-flv; codecs="' + mi.audioCodec + '"';
|
5030 | }
|
5031 | if (mi.isComplete()) {
|
5032 | this._onMediaInfo(mi);
|
5033 | }
|
5034 | } else if (aacData.packetType === 1) {
|
5035 | // AAC raw frame data
|
5036 | var dts = this._timestampBase + tagTimestamp;
|
5037 | var aacSample = { unit: aacData.data, length: aacData.data.byteLength, dts: dts, pts: dts };
|
5038 | track.samples.push(aacSample);
|
5039 | track.length += aacData.data.length;
|
5040 | } else {
|
5041 | _logger2.default.e(this.TAG, 'Flv: Unsupported AAC data type ' + aacData.packetType);
|
5042 | }
|
5043 | } else if (soundFormat === 2) {
|
5044 | // MP3
|
5045 | if (!meta.codec) {
|
5046 | // We need metadata for mp3 audio track, extract info from frame header
|
5047 | var _misc = this._parseMP3AudioData(arrayBuffer, dataOffset + 1, dataSize - 1, true);
|
5048 | if (_misc == undefined) {
|
5049 | return;
|
5050 | }
|
5051 | meta.audioSampleRate = _misc.samplingRate;
|
5052 | meta.channelCount = _misc.channelCount;
|
5053 | meta.codec = _misc.codec;
|
5054 | meta.originalCodec = _misc.originalCodec;
|
5055 | // The decode result of an mp3 sample is 1152 PCM samples
|
5056 | meta.refSampleDuration = 1152 / meta.audioSampleRate * meta.timescale;
|
5057 | _logger2.default.v(this.TAG, 'Parsed MPEG Audio Frame Header');
|
5058 |
|
5059 | this._audioInitialMetadataDispatched = true;
|
5060 | this._onTrackMetadata('audio', meta);
|
5061 |
|
5062 | var _mi = this._mediaInfo;
|
5063 | _mi.audioCodec = meta.codec;
|
5064 | _mi.audioSampleRate = meta.audioSampleRate;
|
5065 | _mi.audioChannelCount = meta.channelCount;
|
5066 | _mi.audioDataRate = _misc.bitRate;
|
5067 | if (_mi.hasVideo) {
|
5068 | if (_mi.videoCodec != null) {
|
5069 | _mi.mimeType = 'video/x-flv; codecs="' + _mi.videoCodec + ',' + _mi.audioCodec + '"';
|
5070 | }
|
5071 | } else {
|
5072 | _mi.mimeType = 'video/x-flv; codecs="' + _mi.audioCodec + '"';
|
5073 | }
|
5074 | if (_mi.isComplete()) {
|
5075 | this._onMediaInfo(_mi);
|
5076 | }
|
5077 | }
|
5078 |
|
5079 | // This packet is always a valid audio packet, extract it
|
5080 | var data = this._parseMP3AudioData(arrayBuffer, dataOffset + 1, dataSize - 1, false);
|
5081 | if (data == undefined) {
|
5082 | return;
|
5083 | }
|
5084 | var _dts = this._timestampBase + tagTimestamp;
|
5085 | var mp3Sample = { unit: data, length: data.byteLength, dts: _dts, pts: _dts };
|
5086 | track.samples.push(mp3Sample);
|
5087 | track.length += data.length;
|
5088 | }
|
5089 | }
|
5090 | }, {
|
5091 | key: '_parseAACAudioData',
|
5092 | value: function _parseAACAudioData(arrayBuffer, dataOffset, dataSize) {
|
5093 | if (dataSize <= 1) {
|
5094 | _logger2.default.w(this.TAG, 'Flv: Invalid AAC packet, missing AACPacketType or/and Data!');
|
5095 | return;
|
5096 | }
|
5097 |
|
5098 | var result = {};
|
5099 | var array = new Uint8Array(arrayBuffer, dataOffset, dataSize);
|
5100 |
|
5101 | result.packetType = array[0];
|
5102 |
|
5103 | if (array[0] === 0) {
|
5104 | result.data = this._parseAACAudioSpecificConfig(arrayBuffer, dataOffset + 1, dataSize - 1);
|
5105 | } else {
|
5106 | result.data = array.subarray(1);
|
5107 | }
|
5108 |
|
5109 | return result;
|
5110 | }
|
5111 | }, {
|
5112 | key: '_parseAACAudioSpecificConfig',
|
5113 | value: function _parseAACAudioSpecificConfig(arrayBuffer, dataOffset, dataSize) {
|
5114 | var array = new Uint8Array(arrayBuffer, dataOffset, dataSize);
|
5115 | var config = null;
|
5116 |
|
5117 | /* Audio Object Type:
|
5118 | 0: Null
|
5119 | 1: AAC Main
|
5120 | 2: AAC LC
|
5121 | 3: AAC SSR (Scalable Sample Rate)
|
5122 | 4: AAC LTP (Long Term Prediction)
|
5123 | 5: HE-AAC / SBR (Spectral Band Replication)
|
5124 | 6: AAC Scalable
|
5125 | */
|
5126 |
|
5127 | var audioObjectType = 0;
|
5128 | var originalAudioObjectType = 0;
|
5129 | var audioExtensionObjectType = null;
|
5130 | var samplingIndex = 0;
|
5131 | var extensionSamplingIndex = null;
|
5132 |
|
5133 | // 5 bits
|
5134 | audioObjectType = originalAudioObjectType = array[0] >>> 3;
|
5135 | // 4 bits
|
5136 | samplingIndex = (array[0] & 0x07) << 1 | array[1] >>> 7;
|
5137 | if (samplingIndex < 0 || samplingIndex >= this._mpegSamplingRates.length) {
|
5138 | this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: AAC invalid sampling frequency index!');
|
5139 | return;
|
5140 | }
|
5141 |
|
5142 | var samplingFrequence = this._mpegSamplingRates[samplingIndex];
|
5143 |
|
5144 | // 4 bits
|
5145 | var channelConfig = (array[1] & 0x78) >>> 3;
|
5146 | if (channelConfig < 0 || channelConfig >= 8) {
|
5147 | this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: AAC invalid channel configuration');
|
5148 | return;
|
5149 | }
|
5150 |
|
5151 | if (audioObjectType === 5) {
|
5152 | // HE-AAC?
|
5153 | // 4 bits
|
5154 | extensionSamplingIndex = (array[1] & 0x07) << 1 | array[2] >>> 7;
|
5155 | // 5 bits
|
5156 | audioExtensionObjectType = (array[2] & 0x7C) >>> 2;
|
5157 | }
|
5158 |
|
5159 | // workarounds for various browsers
|
5160 | var userAgent = self.navigator.userAgent.toLowerCase();
|
5161 |
|
5162 | if (userAgent.indexOf('firefox') !== -1) {
|
5163 | // firefox: use SBR (HE-AAC) if freq less than 24kHz
|
5164 | if (samplingIndex >= 6) {
|
5165 | audioObjectType = 5;
|
5166 | config = new Array(4);
|
5167 | extensionSamplingIndex = samplingIndex - 3;
|
5168 | } else {
|
5169 | // use LC-AAC
|
5170 | audioObjectType = 2;
|
5171 | config = new Array(2);
|
5172 | extensionSamplingIndex = samplingIndex;
|
5173 | }
|
5174 | } else if (userAgent.indexOf('android') !== -1) {
|
5175 | // android: always use LC-AAC
|
5176 | audioObjectType = 2;
|
5177 | config = new Array(2);
|
5178 | extensionSamplingIndex = samplingIndex;
|
5179 | } else {
|
5180 | // for other browsers, e.g. chrome...
|
5181 | // Always use HE-AAC to make it easier to switch aac codec profile
|
5182 | audioObjectType = 5;
|
5183 | extensionSamplingIndex = samplingIndex;
|
5184 | config = new Array(4);
|
5185 |
|
5186 | if (samplingIndex >= 6) {
|
5187 | extensionSamplingIndex = samplingIndex - 3;
|
5188 | } else if (channelConfig === 1) {
|
5189 | // Mono channel
|
5190 | audioObjectType = 2;
|
5191 | config = new Array(2);
|
5192 | extensionSamplingIndex = samplingIndex;
|
5193 | }
|
5194 | }
|
5195 |
|
5196 | config[0] = audioObjectType << 3;
|
5197 | config[0] |= (samplingIndex & 0x0F) >>> 1;
|
5198 | config[1] = (samplingIndex & 0x0F) << 7;
|
5199 | config[1] |= (channelConfig & 0x0F) << 3;
|
5200 | if (audioObjectType === 5) {
|
5201 | config[1] |= (extensionSamplingIndex & 0x0F) >>> 1;
|
5202 | config[2] = (extensionSamplingIndex & 0x01) << 7;
|
5203 | // extended audio object type: force to 2 (LC-AAC)
|
5204 | config[2] |= 2 << 2;
|
5205 | config[3] = 0;
|
5206 | }
|
5207 |
|
5208 | return {
|
5209 | config: config,
|
5210 | samplingRate: samplingFrequence,
|
5211 | channelCount: channelConfig,
|
5212 | codec: 'mp4a.40.' + audioObjectType,
|
5213 | originalCodec: 'mp4a.40.' + originalAudioObjectType
|
5214 | };
|
5215 | }
|
5216 | }, {
|
5217 | key: '_parseMP3AudioData',
|
5218 | value: function _parseMP3AudioData(arrayBuffer, dataOffset, dataSize, requestHeader) {
|
5219 | if (dataSize < 4) {
|
5220 | _logger2.default.w(this.TAG, 'Flv: Invalid MP3 packet, header missing!');
|
5221 | return;
|
5222 | }
|
5223 |
|
5224 | var le = this._littleEndian;
|
5225 | var array = new Uint8Array(arrayBuffer, dataOffset, dataSize);
|
5226 | var result = null;
|
5227 |
|
5228 | if (requestHeader) {
|
5229 | if (array[0] !== 0xFF) {
|
5230 | return;
|
5231 | }
|
5232 | var ver = array[1] >>> 3 & 0x03;
|
5233 | var layer = (array[1] & 0x06) >> 1;
|
5234 |
|
5235 | var bitrate_index = (array[2] & 0xF0) >>> 4;
|
5236 | var sampling_freq_index = (array[2] & 0x0C) >>> 2;
|
5237 |
|
5238 | var channel_mode = array[3] >>> 6 & 0x03;
|
5239 | var channel_count = channel_mode !== 3 ? 2 : 1;
|
5240 |
|
5241 | var sample_rate = 0;
|
5242 | var bit_rate = 0;
|
5243 | var object_type = 34; // Layer-3, listed in MPEG-4 Audio Object Types
|
5244 |
|
5245 | var codec = 'mp3';
|
5246 |
|
5247 | switch (ver) {
|
5248 | case 0:
|
5249 | // MPEG 2.5
|
5250 | sample_rate = this._mpegAudioV25SampleRateTable[sampling_freq_index];
|
5251 | break;
|
5252 | case 2:
|
5253 | // MPEG 2
|
5254 | sample_rate = this._mpegAudioV20SampleRateTable[sampling_freq_index];
|
5255 | break;
|
5256 | case 3:
|
5257 | // MPEG 1
|
5258 | sample_rate = this._mpegAudioV10SampleRateTable[sampling_freq_index];
|
5259 | break;
|
5260 | }
|
5261 |
|
5262 | switch (layer) {
|
5263 | case 1:
|
5264 | // Layer 3
|
5265 | object_type = 34;
|
5266 | if (bitrate_index < this._mpegAudioL3BitRateTable.length) {
|
5267 | bit_rate = this._mpegAudioL3BitRateTable[bitrate_index];
|
5268 | }
|
5269 | break;
|
5270 | case 2:
|
5271 | // Layer 2
|
5272 | object_type = 33;
|
5273 | if (bitrate_index < this._mpegAudioL2BitRateTable.length) {
|
5274 | bit_rate = this._mpegAudioL2BitRateTable[bitrate_index];
|
5275 | }
|
5276 | break;
|
5277 | case 3:
|
5278 | // Layer 1
|
5279 | object_type = 32;
|
5280 | if (bitrate_index < this._mpegAudioL1BitRateTable.length) {
|
5281 | bit_rate = this._mpegAudioL1BitRateTable[bitrate_index];
|
5282 | }
|
5283 | break;
|
5284 | }
|
5285 |
|
5286 | result = {
|
5287 | bitRate: bit_rate,
|
5288 | samplingRate: sample_rate,
|
5289 | channelCount: channel_count,
|
5290 | codec: codec,
|
5291 | originalCodec: codec
|
5292 | };
|
5293 | } else {
|
5294 | result = array;
|
5295 | }
|
5296 |
|
5297 | return result;
|
5298 | }
|
5299 | }, {
|
5300 | key: '_parseVideoData',
|
5301 | value: function _parseVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition) {
|
5302 | if (dataSize <= 1) {
|
5303 | _logger2.default.w(this.TAG, 'Flv: Invalid video packet, missing VideoData payload!');
|
5304 | return;
|
5305 | }
|
5306 |
|
5307 | if (this._hasVideoFlagOverrided === true && this._hasVideo === false) {
|
5308 | // If hasVideo: false indicated explicitly in MediaDataSource,
|
5309 | // Ignore all the video packets
|
5310 | return;
|
5311 | }
|
5312 |
|
5313 | var spec = new Uint8Array(arrayBuffer, dataOffset, dataSize)[0];
|
5314 |
|
5315 | var frameType = (spec & 240) >>> 4;
|
5316 | var codecId = spec & 15;
|
5317 |
|
5318 | if (codecId !== 7) {
|
5319 | this._onError(_demuxErrors2.default.CODEC_UNSUPPORTED, 'Flv: Unsupported codec in video frame: ' + codecId);
|
5320 | return;
|
5321 | }
|
5322 |
|
5323 | this._parseAVCVideoPacket(arrayBuffer, dataOffset + 1, dataSize - 1, tagTimestamp, tagPosition, frameType);
|
5324 | }
|
5325 | }, {
|
5326 | key: '_parseAVCVideoPacket',
|
5327 | value: function _parseAVCVideoPacket(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType) {
|
5328 | if (dataSize < 4) {
|
5329 | _logger2.default.w(this.TAG, 'Flv: Invalid AVC packet, missing AVCPacketType or/and CompositionTime');
|
5330 | return;
|
5331 | }
|
5332 |
|
5333 | var le = this._littleEndian;
|
5334 | var v = new DataView(arrayBuffer, dataOffset, dataSize);
|
5335 |
|
5336 | var packetType = v.getUint8(0);
|
5337 | var cts_unsigned = v.getUint32(0, !le) & 0x00FFFFFF;
|
5338 | var cts = cts_unsigned << 8 >> 8; // convert to 24-bit signed int
|
5339 |
|
5340 | if (packetType === 0) {
|
5341 | // AVCDecoderConfigurationRecord
|
5342 | this._parseAVCDecoderConfigurationRecord(arrayBuffer, dataOffset + 4, dataSize - 4);
|
5343 | } else if (packetType === 1) {
|
5344 | // One or more Nalus
|
5345 | this._parseAVCVideoData(arrayBuffer, dataOffset + 4, dataSize - 4, tagTimestamp, tagPosition, frameType, cts);
|
5346 | } else if (packetType === 2) {
|
5347 | // empty, AVC end of sequence
|
5348 | } else {
|
5349 | this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: Invalid video packet type ' + packetType);
|
5350 | return;
|
5351 | }
|
5352 | }
|
5353 | }, {
|
5354 | key: '_parseAVCDecoderConfigurationRecord',
|
5355 | value: function _parseAVCDecoderConfigurationRecord(arrayBuffer, dataOffset, dataSize) {
|
5356 | if (dataSize < 7) {
|
5357 | _logger2.default.w(this.TAG, 'Flv: Invalid AVCDecoderConfigurationRecord, lack of data!');
|
5358 | return;
|
5359 | }
|
5360 |
|
5361 | var meta = this._videoMetadata;
|
5362 | var track = this._videoTrack;
|
5363 | var le = this._littleEndian;
|
5364 | var v = new DataView(arrayBuffer, dataOffset, dataSize);
|
5365 |
|
5366 | if (!meta) {
|
5367 | if (this._hasVideo === false && this._hasVideoFlagOverrided === false) {
|
5368 | this._hasVideo = true;
|
5369 | this._mediaInfo.hasVideo = true;
|
5370 | }
|
5371 |
|
5372 | meta = this._videoMetadata = {};
|
5373 | meta.type = 'video';
|
5374 | meta.id = track.id;
|
5375 | meta.timescale = this._timescale;
|
5376 | meta.duration = this._duration;
|
5377 | } else {
|
5378 | if (typeof meta.avcc !== 'undefined') {
|
5379 | _logger2.default.w(this.TAG, 'Found another AVCDecoderConfigurationRecord!');
|
5380 | }
|
5381 | }
|
5382 |
|
5383 | var version = v.getUint8(0); // configurationVersion
|
5384 | var avcProfile = v.getUint8(1); // avcProfileIndication
|
5385 | var profileCompatibility = v.getUint8(2); // profile_compatibility
|
5386 | var avcLevel = v.getUint8(3); // AVCLevelIndication
|
5387 |
|
5388 | if (version !== 1 || avcProfile === 0) {
|
5389 | this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord');
|
5390 | return;
|
5391 | }
|
5392 |
|
5393 | this._naluLengthSize = (v.getUint8(4) & 3) + 1; // lengthSizeMinusOne
|
5394 | if (this._naluLengthSize !== 3 && this._naluLengthSize !== 4) {
|
5395 | // holy shit!!!
|
5396 | this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: Strange NaluLengthSizeMinusOne: ' + (this._naluLengthSize - 1));
|
5397 | return;
|
5398 | }
|
5399 |
|
5400 | var spsCount = v.getUint8(5) & 31; // numOfSequenceParameterSets
|
5401 | if (spsCount === 0) {
|
5402 | this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord: No SPS');
|
5403 | return;
|
5404 | } else if (spsCount > 1) {
|
5405 | _logger2.default.w(this.TAG, 'Flv: Strange AVCDecoderConfigurationRecord: SPS Count = ' + spsCount);
|
5406 | }
|
5407 |
|
5408 | var offset = 6;
|
5409 |
|
5410 | for (var i = 0; i < spsCount; i++) {
|
5411 | var len = v.getUint16(offset, !le); // sequenceParameterSetLength
|
5412 | offset += 2;
|
5413 |
|
5414 | if (len === 0) {
|
5415 | continue;
|
5416 | }
|
5417 |
|
5418 | // Notice: Nalu without startcode header (00 00 00 01)
|
5419 | var sps = new Uint8Array(arrayBuffer, dataOffset + offset, len);
|
5420 | offset += len;
|
5421 |
|
5422 | var config = _spsParser2.default.parseSPS(sps);
|
5423 | if (i !== 0) {
|
5424 | // ignore other sps's config
|
5425 | continue;
|
5426 | }
|
5427 |
|
5428 | meta.codecWidth = config.codec_size.width;
|
5429 | meta.codecHeight = config.codec_size.height;
|
5430 | meta.presentWidth = config.present_size.width;
|
5431 | meta.presentHeight = config.present_size.height;
|
5432 |
|
5433 | meta.profile = config.profile_string;
|
5434 | meta.level = config.level_string;
|
5435 | meta.bitDepth = config.bit_depth;
|
5436 | meta.chromaFormat = config.chroma_format;
|
5437 | meta.sarRatio = config.sar_ratio;
|
5438 | meta.frameRate = config.frame_rate;
|
5439 |
|
5440 | if (config.frame_rate.fixed === false || config.frame_rate.fps_num === 0 || config.frame_rate.fps_den === 0) {
|
5441 | meta.frameRate = this._referenceFrameRate;
|
5442 | }
|
5443 |
|
5444 | var fps_den = meta.frameRate.fps_den;
|
5445 | var fps_num = meta.frameRate.fps_num;
|
5446 | meta.refSampleDuration = meta.timescale * (fps_den / fps_num);
|
5447 |
|
5448 | var codecArray = sps.subarray(1, 4);
|
5449 | var codecString = 'avc1.';
|
5450 | for (var j = 0; j < 3; j++) {
|
5451 | var h = codecArray[j].toString(16);
|
5452 | if (h.length < 2) {
|
5453 | h = '0' + h;
|
5454 | }
|
5455 | codecString += h;
|
5456 | }
|
5457 | meta.codec = codecString;
|
5458 |
|
5459 | var mi = this._mediaInfo;
|
5460 | mi.width = meta.codecWidth;
|
5461 | mi.height = meta.codecHeight;
|
5462 | mi.fps = meta.frameRate.fps;
|
5463 | mi.profile = meta.profile;
|
5464 | mi.level = meta.level;
|
5465 | mi.refFrames = config.ref_frames;
|
5466 | mi.chromaFormat = config.chroma_format_string;
|
5467 | mi.sarNum = meta.sarRatio.width;
|
5468 | mi.sarDen = meta.sarRatio.height;
|
5469 | mi.videoCodec = codecString;
|
5470 |
|
5471 | if (mi.hasAudio) {
|
5472 | if (mi.audioCodec != null) {
|
5473 | mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + ',' + mi.audioCodec + '"';
|
5474 | }
|
5475 | } else {
|
5476 | mi.mimeType = 'video/x-flv; codecs="' + mi.videoCodec + '"';
|
5477 | }
|
5478 | if (mi.isComplete()) {
|
5479 | this._onMediaInfo(mi);
|
5480 | }
|
5481 | }
|
5482 |
|
5483 | var ppsCount = v.getUint8(offset); // numOfPictureParameterSets
|
5484 | if (ppsCount === 0) {
|
5485 | this._onError(_demuxErrors2.default.FORMAT_ERROR, 'Flv: Invalid AVCDecoderConfigurationRecord: No PPS');
|
5486 | return;
|
5487 | } else if (ppsCount > 1) {
|
5488 | _logger2.default.w(this.TAG, 'Flv: Strange AVCDecoderConfigurationRecord: PPS Count = ' + ppsCount);
|
5489 | }
|
5490 |
|
5491 | offset++;
|
5492 |
|
5493 | for (var _i = 0; _i < ppsCount; _i++) {
|
5494 | var _len = v.getUint16(offset, !le); // pictureParameterSetLength
|
5495 | offset += 2;
|
5496 |
|
5497 | if (_len === 0) {
|
5498 | continue;
|
5499 | }
|
5500 |
|
5501 | // pps is useless for extracting video information
|
5502 | offset += _len;
|
5503 | }
|
5504 |
|
5505 | meta.avcc = new Uint8Array(dataSize);
|
5506 | meta.avcc.set(new Uint8Array(arrayBuffer, dataOffset, dataSize), 0);
|
5507 | _logger2.default.v(this.TAG, 'Parsed AVCDecoderConfigurationRecord');
|
5508 |
|
5509 | if (this._isInitialMetadataDispatched()) {
|
5510 | // flush parsed frames
|
5511 | if (this._dispatch && (this._audioTrack.length || this._videoTrack.length)) {
|
5512 | this._onDataAvailable(this._audioTrack, this._videoTrack);
|
5513 | }
|
5514 | } else {
|
5515 | this._videoInitialMetadataDispatched = true;
|
5516 | }
|
5517 | // notify new metadata
|
5518 | this._dispatch = false;
|
5519 | this._onTrackMetadata('video', meta);
|
5520 | }
|
5521 | }, {
|
5522 | key: '_parseAVCVideoData',
|
5523 | value: function _parseAVCVideoData(arrayBuffer, dataOffset, dataSize, tagTimestamp, tagPosition, frameType, cts) {
|
5524 | var le = this._littleEndian;
|
5525 | var v = new DataView(arrayBuffer, dataOffset, dataSize);
|
5526 |
|
5527 | var units = [],
|
5528 | length = 0;
|
5529 |
|
5530 | var offset = 0;
|
5531 | var lengthSize = this._naluLengthSize;
|
5532 | var dts = this._timestampBase + tagTimestamp;
|
5533 | var keyframe = frameType === 1; // from FLV Frame Type constants
|
5534 |
|
5535 | while (offset < dataSize) {
|
5536 | if (offset + 4 >= dataSize) {
|
5537 | _logger2.default.w(this.TAG, 'Malformed Nalu near timestamp ' + dts + ', offset = ' + offset + ', dataSize = ' + dataSize);
|
5538 | break; // data not enough for next Nalu
|
5539 | }
|
5540 | // Nalu with length-header (AVC1)
|
5541 | var naluSize = v.getUint32(offset, !le); // Big-Endian read
|
5542 | if (lengthSize === 3) {
|
5543 | naluSize >>>= 8;
|
5544 | }
|
5545 | if (naluSize > dataSize - lengthSize) {
|
5546 | _logger2.default.w(this.TAG, 'Malformed Nalus near timestamp ' + dts + ', NaluSize > DataSize!');
|
5547 | return;
|
5548 | }
|
5549 |
|
5550 | var unitType = v.getUint8(offset + lengthSize) & 0x1F;
|
5551 |
|
5552 | if (unitType === 5) {
|
5553 | // IDR
|
5554 | keyframe = true;
|
5555 | }
|
5556 |
|
5557 | var data = new Uint8Array(arrayBuffer, dataOffset + offset, lengthSize + naluSize);
|
5558 | var unit = { type: unitType, data: data };
|
5559 | units.push(unit);
|
5560 | length += data.byteLength;
|
5561 |
|
5562 | offset += lengthSize + naluSize;
|
5563 | }
|
5564 |
|
5565 | if (units.length) {
|
5566 | var track = this._videoTrack;
|
5567 | var avcSample = {
|
5568 | units: units,
|
5569 | length: length,
|
5570 | isKeyframe: keyframe,
|
5571 | dts: dts,
|
5572 | cts: cts,
|
5573 | pts: dts + cts
|
5574 | };
|
5575 | if (keyframe) {
|
5576 | avcSample.fileposition = tagPosition;
|
5577 | }
|
5578 | track.samples.push(avcSample);
|
5579 | track.length += length;
|
5580 | }
|
5581 | }
|
5582 | }, {
|
5583 | key: 'onTrackMetadata',
|
5584 | get: function get() {
|
5585 | return this._onTrackMetadata;
|
5586 | },
|
5587 | set: function set(callback) {
|
5588 | this._onTrackMetadata = callback;
|
5589 | }
|
5590 |
|
5591 | // prototype: function(mediaInfo: MediaInfo): void
|
5592 |
|
5593 | }, {
|
5594 | key: 'onMediaInfo',
|
5595 | get: function get() {
|
5596 | return this._onMediaInfo;
|
5597 | },
|
5598 | set: function set(callback) {
|
5599 | this._onMediaInfo = callback;
|
5600 | }
|
5601 | }, {
|
5602 | key: 'onMetaDataArrived',
|
5603 | get: function get() {
|
5604 | return this._onMetaDataArrived;
|
5605 | },
|
5606 | set: function set(callback) {
|
5607 | this._onMetaDataArrived = callback;
|
5608 | }
|
5609 | }, {
|
5610 | key: 'onScriptDataArrived',
|
5611 | get: function get() {
|
5612 | return this._onScriptDataArrived;
|
5613 | },
|
5614 | set: function set(callback) {
|
5615 | this._onScriptDataArrived = callback;
|
5616 | }
|
5617 |
|
5618 | // prototype: function(type: number, info: string): void
|
5619 |
|
5620 | }, {
|
5621 | key: 'onError',
|
5622 | get: function get() {
|
5623 | return this._onError;
|
5624 | },
|
5625 | set: function set(callback) {
|
5626 | this._onError = callback;
|
5627 | }
|
5628 |
|
5629 | // prototype: function(videoTrack: any, audioTrack: any): void
|
5630 |
|
5631 | }, {
|
5632 | key: 'onDataAvailable',
|
5633 | get: function get() {
|
5634 | return this._onDataAvailable;
|
5635 | },
|
5636 | set: function set(callback) {
|
5637 | this._onDataAvailable = callback;
|
5638 | }
|
5639 |
|
5640 | // timestamp base for output samples, must be in milliseconds
|
5641 |
|
5642 | }, {
|
5643 | key: 'timestampBase',
|
5644 | get: function get() {
|
5645 | return this._timestampBase;
|
5646 | },
|
5647 | set: function set(base) {
|
5648 | this._timestampBase = base;
|
5649 | }
|
5650 | }, {
|
5651 | key: 'overridedDuration',
|
5652 | get: function get() {
|
5653 | return this._duration;
|
5654 | }
|
5655 |
|
5656 | // Force-override media duration. Must be in milliseconds, int32
|
5657 | ,
|
5658 | set: function set(duration) {
|
5659 | this._durationOverrided = true;
|
5660 | this._duration = duration;
|
5661 | this._mediaInfo.duration = duration;
|
5662 | }
|
5663 |
|
5664 | // Force-override audio track present flag, boolean
|
5665 |
|
5666 | }, {
|
5667 | key: 'overridedHasAudio',
|
5668 | set: function set(hasAudio) {
|
5669 | this._hasAudioFlagOverrided = true;
|
5670 | this._hasAudio = hasAudio;
|
5671 | this._mediaInfo.hasAudio = hasAudio;
|
5672 | }
|
5673 |
|
5674 | // Force-override video track present flag, boolean
|
5675 |
|
5676 | }, {
|
5677 | key: 'overridedHasVideo',
|
5678 | set: function set(hasVideo) {
|
5679 | this._hasVideoFlagOverrided = true;
|
5680 | this._hasVideo = hasVideo;
|
5681 | this._mediaInfo.hasVideo = hasVideo;
|
5682 | }
|
5683 | }], [{
|
5684 | key: 'probe',
|
5685 | value: function probe(buffer) {
|
5686 | var data = new Uint8Array(buffer);
|
5687 | var mismatch = { match: false };
|
5688 |
|
5689 | if (data[0] !== 0x46 || data[1] !== 0x4C || data[2] !== 0x56 || data[3] !== 0x01) {
|
5690 | return mismatch;
|
5691 | }
|
5692 |
|
5693 | var hasAudio = (data[4] & 4) >>> 2 !== 0;
|
5694 | var hasVideo = (data[4] & 1) !== 0;
|
5695 |
|
5696 | var offset = ReadBig32(data, 5);
|
5697 |
|
5698 | if (offset < 9) {
|
5699 | return mismatch;
|
5700 | }
|
5701 |
|
5702 | return {
|
5703 | match: true,
|
5704 | consumed: offset,
|
5705 | dataOffset: offset,
|
5706 | hasAudioTrack: hasAudio,
|
5707 | hasVideoTrack: hasVideo
|
5708 | };
|
5709 | }
|
5710 | }]);
|
5711 |
|
5712 | return FLVDemuxer;
|
5713 | }();
|
5714 |
|
5715 | exports.default = FLVDemuxer;
|
5716 |
|
5717 | },{"../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){
|
5718 | ;
|
5719 |
|
5720 | Object.defineProperty(exports, "__esModule", {
|
5721 | value: true
|
5722 | });
|
5723 |
|
5724 | 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; }; }(); /*
|
5725 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
5726 | *
|
5727 | * @author zheng qian <xqq@xqq.im>
|
5728 | *
|
5729 | * Licensed under the Apache License, Version 2.0 (the "License");
|
5730 | * you may not use this file except in compliance with the License.
|
5731 | * You may obtain a copy of the License at
|
5732 | *
|
5733 | * http://www.apache.org/licenses/LICENSE-2.0
|
5734 | *
|
5735 | * Unless required by applicable law or agreed to in writing, software
|
5736 | * distributed under the License is distributed on an "AS IS" BASIS,
|
5737 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
5738 | * See the License for the specific language governing permissions and
|
5739 | * limitations under the License.
|
5740 | */
|
5741 |
|
5742 | var _expGolomb = _dereq_('./exp-golomb.js');
|
5743 |
|
5744 | var _expGolomb2 = _interopRequireDefault(_expGolomb);
|
5745 |
|
5746 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
5747 |
|
5748 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
5749 |
|
5750 | var SPSParser = function () {
|
5751 | function SPSParser() {
|
5752 | _classCallCheck(this, SPSParser);
|
5753 | }
|
5754 |
|
5755 | _createClass(SPSParser, null, [{
|
5756 | key: '_ebsp2rbsp',
|
5757 | value: function _ebsp2rbsp(uint8array) {
|
5758 | var src = uint8array;
|
5759 | var src_length = src.byteLength;
|
5760 | var dst = new Uint8Array(src_length);
|
5761 | var dst_idx = 0;
|
5762 |
|
5763 | for (var i = 0; i < src_length; i++) {
|
5764 | if (i >= 2) {
|
5765 | // Unescape: Skip 0x03 after 00 00
|
5766 | if (src[i] === 0x03 && src[i - 1] === 0x00 && src[i - 2] === 0x00) {
|
5767 | continue;
|
5768 | }
|
5769 | }
|
5770 | dst[dst_idx] = src[i];
|
5771 | dst_idx++;
|
5772 | }
|
5773 |
|
5774 | return new Uint8Array(dst.buffer, 0, dst_idx);
|
5775 | }
|
5776 | }, {
|
5777 | key: 'parseSPS',
|
5778 | value: function parseSPS(uint8array) {
|
5779 | var rbsp = SPSParser._ebsp2rbsp(uint8array);
|
5780 | var gb = new _expGolomb2.default(rbsp);
|
5781 |
|
5782 | gb.readByte();
|
5783 | var profile_idc = gb.readByte(); // profile_idc
|
5784 | gb.readByte(); // constraint_set_flags[5] + reserved_zero[3]
|
5785 | var level_idc = gb.readByte(); // level_idc
|
5786 | gb.readUEG(); // seq_parameter_set_id
|
5787 |
|
5788 | var profile_string = SPSParser.getProfileString(profile_idc);
|
5789 | var level_string = SPSParser.getLevelString(level_idc);
|
5790 | var chroma_format_idc = 1;
|
5791 | var chroma_format = 420;
|
5792 | var chroma_format_table = [0, 420, 422, 444];
|
5793 | var bit_depth = 8;
|
5794 |
|
5795 | 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) {
|
5796 |
|
5797 | chroma_format_idc = gb.readUEG();
|
5798 | if (chroma_format_idc === 3) {
|
5799 | gb.readBits(1); // separate_colour_plane_flag
|
5800 | }
|
5801 | if (chroma_format_idc <= 3) {
|
5802 | chroma_format = chroma_format_table[chroma_format_idc];
|
5803 | }
|
5804 |
|
5805 | bit_depth = gb.readUEG() + 8; // bit_depth_luma_minus8
|
5806 | gb.readUEG(); // bit_depth_chroma_minus8
|
5807 | gb.readBits(1); // qpprime_y_zero_transform_bypass_flag
|
5808 | if (gb.readBool()) {
|
5809 | // seq_scaling_matrix_present_flag
|
5810 | var scaling_list_count = chroma_format_idc !== 3 ? 8 : 12;
|
5811 | for (var i = 0; i < scaling_list_count; i++) {
|
5812 | if (gb.readBool()) {
|
5813 | // seq_scaling_list_present_flag
|
5814 | if (i < 6) {
|
5815 | SPSParser._skipScalingList(gb, 16);
|
5816 | } else {
|
5817 | SPSParser._skipScalingList(gb, 64);
|
5818 | }
|
5819 | }
|
5820 | }
|
5821 | }
|
5822 | }
|
5823 | gb.readUEG(); // log2_max_frame_num_minus4
|
5824 | var pic_order_cnt_type = gb.readUEG();
|
5825 | if (pic_order_cnt_type === 0) {
|
5826 | gb.readUEG(); // log2_max_pic_order_cnt_lsb_minus_4
|
5827 | } else if (pic_order_cnt_type === 1) {
|
5828 | gb.readBits(1); // delta_pic_order_always_zero_flag
|
5829 | gb.readSEG(); // offset_for_non_ref_pic
|
5830 | gb.readSEG(); // offset_for_top_to_bottom_field
|
5831 | var num_ref_frames_in_pic_order_cnt_cycle = gb.readUEG();
|
5832 | for (var _i = 0; _i < num_ref_frames_in_pic_order_cnt_cycle; _i++) {
|
5833 | gb.readSEG(); // offset_for_ref_frame
|
5834 | }
|
5835 | }
|
5836 | var ref_frames = gb.readUEG(); // max_num_ref_frames
|
5837 | gb.readBits(1); // gaps_in_frame_num_value_allowed_flag
|
5838 |
|
5839 | var pic_width_in_mbs_minus1 = gb.readUEG();
|
5840 | var pic_height_in_map_units_minus1 = gb.readUEG();
|
5841 |
|
5842 | var frame_mbs_only_flag = gb.readBits(1);
|
5843 | if (frame_mbs_only_flag === 0) {
|
5844 | gb.readBits(1); // mb_adaptive_frame_field_flag
|
5845 | }
|
5846 | gb.readBits(1); // direct_8x8_inference_flag
|
5847 |
|
5848 | var frame_crop_left_offset = 0;
|
5849 | var frame_crop_right_offset = 0;
|
5850 | var frame_crop_top_offset = 0;
|
5851 | var frame_crop_bottom_offset = 0;
|
5852 |
|
5853 | var frame_cropping_flag = gb.readBool();
|
5854 | if (frame_cropping_flag) {
|
5855 | frame_crop_left_offset = gb.readUEG();
|
5856 | frame_crop_right_offset = gb.readUEG();
|
5857 | frame_crop_top_offset = gb.readUEG();
|
5858 | frame_crop_bottom_offset = gb.readUEG();
|
5859 | }
|
5860 |
|
5861 | var sar_width = 1,
|
5862 | sar_height = 1;
|
5863 | var fps = 0,
|
5864 | fps_fixed = true,
|
5865 | fps_num = 0,
|
5866 | fps_den = 0;
|
5867 |
|
5868 | var vui_parameters_present_flag = gb.readBool();
|
5869 | if (vui_parameters_present_flag) {
|
5870 | if (gb.readBool()) {
|
5871 | // aspect_ratio_info_present_flag
|
5872 | var aspect_ratio_idc = gb.readByte();
|
5873 | var sar_w_table = [1, 12, 10, 16, 40, 24, 20, 32, 80, 18, 15, 64, 160, 4, 3, 2];
|
5874 | var sar_h_table = [1, 11, 11, 11, 33, 11, 11, 11, 33, 11, 11, 33, 99, 3, 2, 1];
|
5875 |
|
5876 | if (aspect_ratio_idc > 0 && aspect_ratio_idc < 16) {
|
5877 | sar_width = sar_w_table[aspect_ratio_idc - 1];
|
5878 | sar_height = sar_h_table[aspect_ratio_idc - 1];
|
5879 | } else if (aspect_ratio_idc === 255) {
|
5880 | sar_width = gb.readByte() << 8 | gb.readByte();
|
5881 | sar_height = gb.readByte() << 8 | gb.readByte();
|
5882 | }
|
5883 | }
|
5884 |
|
5885 | if (gb.readBool()) {
|
5886 | // overscan_info_present_flag
|
5887 | gb.readBool(); // overscan_appropriate_flag
|
5888 | }
|
5889 | if (gb.readBool()) {
|
5890 | // video_signal_type_present_flag
|
5891 | gb.readBits(4); // video_format & video_full_range_flag
|
5892 | if (gb.readBool()) {
|
5893 | // colour_description_present_flag
|
5894 | gb.readBits(24); // colour_primaries & transfer_characteristics & matrix_coefficients
|
5895 | }
|
5896 | }
|
5897 | if (gb.readBool()) {
|
5898 | // chroma_loc_info_present_flag
|
5899 | gb.readUEG(); // chroma_sample_loc_type_top_field
|
5900 | gb.readUEG(); // chroma_sample_loc_type_bottom_field
|
5901 | }
|
5902 | if (gb.readBool()) {
|
5903 | // timing_info_present_flag
|
5904 | var num_units_in_tick = gb.readBits(32);
|
5905 | var time_scale = gb.readBits(32);
|
5906 | fps_fixed = gb.readBool(); // fixed_frame_rate_flag
|
5907 |
|
5908 | fps_num = time_scale;
|
5909 | fps_den = num_units_in_tick * 2;
|
5910 | fps = fps_num / fps_den;
|
5911 | }
|
5912 | }
|
5913 |
|
5914 | var sarScale = 1;
|
5915 | if (sar_width !== 1 || sar_height !== 1) {
|
5916 | sarScale = sar_width / sar_height;
|
5917 | }
|
5918 |
|
5919 | var crop_unit_x = 0,
|
5920 | crop_unit_y = 0;
|
5921 | if (chroma_format_idc === 0) {
|
5922 | crop_unit_x = 1;
|
5923 | crop_unit_y = 2 - frame_mbs_only_flag;
|
5924 | } else {
|
5925 | var sub_wc = chroma_format_idc === 3 ? 1 : 2;
|
5926 | var sub_hc = chroma_format_idc === 1 ? 2 : 1;
|
5927 | crop_unit_x = sub_wc;
|
5928 | crop_unit_y = sub_hc * (2 - frame_mbs_only_flag);
|
5929 | }
|
5930 |
|
5931 | var codec_width = (pic_width_in_mbs_minus1 + 1) * 16;
|
5932 | var codec_height = (2 - frame_mbs_only_flag) * ((pic_height_in_map_units_minus1 + 1) * 16);
|
5933 |
|
5934 | codec_width -= (frame_crop_left_offset + frame_crop_right_offset) * crop_unit_x;
|
5935 | codec_height -= (frame_crop_top_offset + frame_crop_bottom_offset) * crop_unit_y;
|
5936 |
|
5937 | var present_width = Math.ceil(codec_width * sarScale);
|
5938 |
|
5939 | gb.destroy();
|
5940 | gb = null;
|
5941 |
|
5942 | return {
|
5943 | profile_string: profile_string, // baseline, high, high10, ...
|
5944 | level_string: level_string, // 3, 3.1, 4, 4.1, 5, 5.1, ...
|
5945 | bit_depth: bit_depth, // 8bit, 10bit, ...
|
5946 | ref_frames: ref_frames,
|
5947 | chroma_format: chroma_format, // 4:2:0, 4:2:2, ...
|
5948 | chroma_format_string: SPSParser.getChromaFormatString(chroma_format),
|
5949 |
|
5950 | frame_rate: {
|
5951 | fixed: fps_fixed,
|
5952 | fps: fps,
|
5953 | fps_den: fps_den,
|
5954 | fps_num: fps_num
|
5955 | },
|
5956 |
|
5957 | sar_ratio: {
|
5958 | width: sar_width,
|
5959 | height: sar_height
|
5960 | },
|
5961 |
|
5962 | codec_size: {
|
5963 | width: codec_width,
|
5964 | height: codec_height
|
5965 | },
|
5966 |
|
5967 | present_size: {
|
5968 | width: present_width,
|
5969 | height: codec_height
|
5970 | }
|
5971 | };
|
5972 | }
|
5973 | }, {
|
5974 | key: '_skipScalingList',
|
5975 | value: function _skipScalingList(gb, count) {
|
5976 | var last_scale = 8,
|
5977 | next_scale = 8;
|
5978 | var delta_scale = 0;
|
5979 | for (var i = 0; i < count; i++) {
|
5980 | if (next_scale !== 0) {
|
5981 | delta_scale = gb.readSEG();
|
5982 | next_scale = (last_scale + delta_scale + 256) % 256;
|
5983 | }
|
5984 | last_scale = next_scale === 0 ? last_scale : next_scale;
|
5985 | }
|
5986 | }
|
5987 | }, {
|
5988 | key: 'getProfileString',
|
5989 | value: function getProfileString(profile_idc) {
|
5990 | switch (profile_idc) {
|
5991 | case 66:
|
5992 | return 'Baseline';
|
5993 | case 77:
|
5994 | return 'Main';
|
5995 | case 88:
|
5996 | return 'Extended';
|
5997 | case 100:
|
5998 | return 'High';
|
5999 | case 110:
|
6000 | return 'High10';
|
6001 | case 122:
|
6002 | return 'High422';
|
6003 | case 244:
|
6004 | return 'High444';
|
6005 | default:
|
6006 | return 'Unknown';
|
6007 | }
|
6008 | }
|
6009 | }, {
|
6010 | key: 'getLevelString',
|
6011 | value: function getLevelString(level_idc) {
|
6012 | return (level_idc / 10).toFixed(1);
|
6013 | }
|
6014 | }, {
|
6015 | key: 'getChromaFormatString',
|
6016 | value: function getChromaFormatString(chroma) {
|
6017 | switch (chroma) {
|
6018 | case 420:
|
6019 | return '4:2:0';
|
6020 | case 422:
|
6021 | return '4:2:2';
|
6022 | case 444:
|
6023 | return '4:4:4';
|
6024 | default:
|
6025 | return 'Unknown';
|
6026 | }
|
6027 | }
|
6028 | }]);
|
6029 |
|
6030 | return SPSParser;
|
6031 | }();
|
6032 |
|
6033 | exports.default = SPSParser;
|
6034 |
|
6035 | },{"./exp-golomb.js":17}],20:[function(_dereq_,module,exports){
|
6036 | ;
|
6037 |
|
6038 | Object.defineProperty(exports, "__esModule", {
|
6039 | value: true
|
6040 | });
|
6041 |
|
6042 | 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; }; /*
|
6043 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
6044 | *
|
6045 | * @author zheng qian <xqq@xqq.im>
|
6046 | *
|
6047 | * Licensed under the Apache License, Version 2.0 (the "License");
|
6048 | * you may not use this file except in compliance with the License.
|
6049 | * You may obtain a copy of the License at
|
6050 | *
|
6051 | * http://www.apache.org/licenses/LICENSE-2.0
|
6052 | *
|
6053 | * Unless required by applicable law or agreed to in writing, software
|
6054 | * distributed under the License is distributed on an "AS IS" BASIS,
|
6055 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
6056 | * See the License for the specific language governing permissions and
|
6057 | * limitations under the License.
|
6058 | */
|
6059 |
|
6060 | var _polyfill = _dereq_('./utils/polyfill.js');
|
6061 |
|
6062 | var _polyfill2 = _interopRequireDefault(_polyfill);
|
6063 |
|
6064 | var _features = _dereq_('./core/features.js');
|
6065 |
|
6066 | var _features2 = _interopRequireDefault(_features);
|
6067 |
|
6068 | var _loader = _dereq_('./io/loader.js');
|
6069 |
|
6070 | var _flvPlayer = _dereq_('./player/flv-player.js');
|
6071 |
|
6072 | var _flvPlayer2 = _interopRequireDefault(_flvPlayer);
|
6073 |
|
6074 | var _nativePlayer = _dereq_('./player/native-player.js');
|
6075 |
|
6076 | var _nativePlayer2 = _interopRequireDefault(_nativePlayer);
|
6077 |
|
6078 | var _playerEvents = _dereq_('./player/player-events.js');
|
6079 |
|
6080 | var _playerEvents2 = _interopRequireDefault(_playerEvents);
|
6081 |
|
6082 | var _playerErrors = _dereq_('./player/player-errors.js');
|
6083 |
|
6084 | var _loggingControl = _dereq_('./utils/logging-control.js');
|
6085 |
|
6086 | var _loggingControl2 = _interopRequireDefault(_loggingControl);
|
6087 |
|
6088 | var _exception = _dereq_('./utils/exception.js');
|
6089 |
|
6090 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
6091 |
|
6092 | // here are all the interfaces
|
6093 |
|
6094 | // install polyfills
|
6095 | _polyfill2.default.install();
|
6096 |
|
6097 | // factory method
|
6098 | function createPlayer(mediaDataSource, optionalConfig) {
|
6099 | var mds = mediaDataSource;
|
6100 | if (mds == null || (typeof mds === 'undefined' ? 'undefined' : _typeof(mds)) !== 'object') {
|
6101 | throw new _exception.InvalidArgumentException('MediaDataSource must be an javascript object!');
|
6102 | }
|
6103 |
|
6104 | if (!mds.hasOwnProperty('type')) {
|
6105 | throw new _exception.InvalidArgumentException('MediaDataSource must has type field to indicate video file type!');
|
6106 | }
|
6107 |
|
6108 | switch (mds.type) {
|
6109 | case 'flv':
|
6110 | return new _flvPlayer2.default(mds, optionalConfig);
|
6111 | default:
|
6112 | return new _nativePlayer2.default(mds, optionalConfig);
|
6113 | }
|
6114 | }
|
6115 |
|
6116 | // feature detection
|
6117 | function isSupported() {
|
6118 | return _features2.default.supportMSEH264Playback();
|
6119 | }
|
6120 |
|
6121 | function getFeatureList() {
|
6122 | return _features2.default.getFeatureList();
|
6123 | }
|
6124 |
|
6125 | // interfaces
|
6126 | var flvjs = {};
|
6127 |
|
6128 | flvjs.createPlayer = createPlayer;
|
6129 | flvjs.isSupported = isSupported;
|
6130 | flvjs.getFeatureList = getFeatureList;
|
6131 |
|
6132 | flvjs.BaseLoader = _loader.BaseLoader;
|
6133 | flvjs.LoaderStatus = _loader.LoaderStatus;
|
6134 | flvjs.LoaderErrors = _loader.LoaderErrors;
|
6135 |
|
6136 | flvjs.Events = _playerEvents2.default;
|
6137 | flvjs.ErrorTypes = _playerErrors.ErrorTypes;
|
6138 | flvjs.ErrorDetails = _playerErrors.ErrorDetails;
|
6139 |
|
6140 | flvjs.FlvPlayer = _flvPlayer2.default;
|
6141 | flvjs.NativePlayer = _nativePlayer2.default;
|
6142 | flvjs.LoggingControl = _loggingControl2.default;
|
6143 |
|
6144 | Object.defineProperty(flvjs, 'version', {
|
6145 | enumerable: true,
|
6146 | get: function get() {
|
6147 | // replaced by browserify-versionify transform
|
6148 | return '1.5.0';
|
6149 | }
|
6150 | });
|
6151 |
|
6152 | exports.default = flvjs;
|
6153 |
|
6154 | },{"./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){
|
6155 | ;
|
6156 |
|
6157 | // entry/index file
|
6158 |
|
6159 | // make it compatible with browserify's umd wrapper
|
6160 | module.exports = _dereq_('./flv.js').default;
|
6161 |
|
6162 | },{"./flv.js":20}],22:[function(_dereq_,module,exports){
|
6163 | ;
|
6164 |
|
6165 | Object.defineProperty(exports, "__esModule", {
|
6166 | value: true
|
6167 | });
|
6168 |
|
6169 | 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; };
|
6170 |
|
6171 | 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); } };
|
6172 |
|
6173 | 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; }; }();
|
6174 |
|
6175 | var _logger = _dereq_('../utils/logger.js');
|
6176 |
|
6177 | var _logger2 = _interopRequireDefault(_logger);
|
6178 |
|
6179 | var _browser = _dereq_('../utils/browser.js');
|
6180 |
|
6181 | var _browser2 = _interopRequireDefault(_browser);
|
6182 |
|
6183 | var _loader = _dereq_('./loader.js');
|
6184 |
|
6185 | var _exception = _dereq_('../utils/exception.js');
|
6186 |
|
6187 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
6188 |
|
6189 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
6190 |
|
6191 | 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; }
|
6192 |
|
6193 | 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; } /*
|
6194 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
6195 | *
|
6196 | * @author zheng qian <xqq@xqq.im>
|
6197 | *
|
6198 | * Licensed under the Apache License, Version 2.0 (the "License");
|
6199 | * you may not use this file except in compliance with the License.
|
6200 | * You may obtain a copy of the License at
|
6201 | *
|
6202 | * http://www.apache.org/licenses/LICENSE-2.0
|
6203 | *
|
6204 | * Unless required by applicable law or agreed to in writing, software
|
6205 | * distributed under the License is distributed on an "AS IS" BASIS,
|
6206 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
6207 | * See the License for the specific language governing permissions and
|
6208 | * limitations under the License.
|
6209 | */
|
6210 |
|
6211 | /* fetch + stream IO loader. Currently working on chrome 43+.
|
6212 | * fetch provides a better alternative http API to XMLHttpRequest
|
6213 | *
|
6214 | * fetch spec https://fetch.spec.whatwg.org/
|
6215 | * stream spec https://streams.spec.whatwg.org/
|
6216 | */
|
6217 | var FetchStreamLoader = function (_BaseLoader) {
|
6218 | _inherits(FetchStreamLoader, _BaseLoader);
|
6219 |
|
6220 | _createClass(FetchStreamLoader, null, [{
|
6221 | key: 'isSupported',
|
6222 | value: function isSupported() {
|
6223 | try {
|
6224 | // fetch + stream is broken on Microsoft Edge. Disable before build 15048.
|
6225 | // see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/8196907/
|
6226 | // Fixed in Jan 10, 2017. Build 15048+ removed from blacklist.
|
6227 | var isWorkWellEdge = _browser2.default.msedge && _browser2.default.version.minor >= 15048;
|
6228 | var browserNotBlacklisted = _browser2.default.msedge ? isWorkWellEdge : true;
|
6229 | return self.fetch && self.ReadableStream && browserNotBlacklisted;
|
6230 | } catch (e) {
|
6231 | return false;
|
6232 | }
|
6233 | }
|
6234 | }]);
|
6235 |
|
6236 | function FetchStreamLoader(seekHandler, config) {
|
6237 | _classCallCheck(this, FetchStreamLoader);
|
6238 |
|
6239 | var _this = _possibleConstructorReturn(this, (FetchStreamLoader.__proto__ || Object.getPrototypeOf(FetchStreamLoader)).call(this, 'fetch-stream-loader'));
|
6240 |
|
6241 | _this.TAG = 'FetchStreamLoader';
|
6242 |
|
6243 | _this._seekHandler = seekHandler;
|
6244 | _this._config = config;
|
6245 | _this._needStash = true;
|
6246 |
|
6247 | _this._requestAbort = false;
|
6248 | _this._contentLength = null;
|
6249 | _this._receivedLength = 0;
|
6250 | return _this;
|
6251 | }
|
6252 |
|
6253 | _createClass(FetchStreamLoader, [{
|
6254 | key: 'destroy',
|
6255 | value: function destroy() {
|
6256 | if (this.isWorking()) {
|
6257 | this.abort();
|
6258 | }
|
6259 | _get(FetchStreamLoader.prototype.__proto__ || Object.getPrototypeOf(FetchStreamLoader.prototype), 'destroy', this).call(this);
|
6260 | }
|
6261 | }, {
|
6262 | key: 'open',
|
6263 | value: function open(dataSource, range) {
|
6264 | var _this2 = this;
|
6265 |
|
6266 | this._dataSource = dataSource;
|
6267 | this._range = range;
|
6268 |
|
6269 | var sourceURL = dataSource.url;
|
6270 | if (this._config.reuseRedirectedURL && dataSource.redirectedURL != undefined) {
|
6271 | sourceURL = dataSource.redirectedURL;
|
6272 | }
|
6273 |
|
6274 | var seekConfig = this._seekHandler.getConfig(sourceURL, range);
|
6275 |
|
6276 | var headers = new self.Headers();
|
6277 |
|
6278 | if (_typeof(seekConfig.headers) === 'object') {
|
6279 | var configHeaders = seekConfig.headers;
|
6280 | for (var key in configHeaders) {
|
6281 | if (configHeaders.hasOwnProperty(key)) {
|
6282 | headers.append(key, configHeaders[key]);
|
6283 | }
|
6284 | }
|
6285 | }
|
6286 |
|
6287 | var params = {
|
6288 | method: 'GET',
|
6289 | headers: headers,
|
6290 | mode: 'cors',
|
6291 | cache: 'default',
|
6292 | // The default policy of Fetch API in the whatwg standard
|
6293 | // Safari incorrectly indicates 'no-referrer' as default policy, fuck it
|
6294 | referrerPolicy: 'no-referrer-when-downgrade'
|
6295 | };
|
6296 |
|
6297 | // add additional headers
|
6298 | if (_typeof(this._config.headers) === 'object') {
|
6299 | for (var _key in this._config.headers) {
|
6300 | headers.append(_key, this._config.headers[_key]);
|
6301 | }
|
6302 | }
|
6303 |
|
6304 | // cors is enabled by default
|
6305 | if (dataSource.cors === false) {
|
6306 | // no-cors means 'disregard cors policy', which can only be used in ServiceWorker
|
6307 | params.mode = 'same-origin';
|
6308 | }
|
6309 |
|
6310 | // withCredentials is disabled by default
|
6311 | if (dataSource.withCredentials) {
|
6312 | params.credentials = 'include';
|
6313 | }
|
6314 |
|
6315 | // referrerPolicy from config
|
6316 | if (dataSource.referrerPolicy) {
|
6317 | params.referrerPolicy = dataSource.referrerPolicy;
|
6318 | }
|
6319 |
|
6320 | this._status = _loader.LoaderStatus.kConnecting;
|
6321 | self.fetch(seekConfig.url, params).then(function (res) {
|
6322 | if (_this2._requestAbort) {
|
6323 | _this2._requestAbort = false;
|
6324 | _this2._status = _loader.LoaderStatus.kIdle;
|
6325 | return;
|
6326 | }
|
6327 | if (res.ok && res.status >= 200 && res.status <= 299) {
|
6328 | if (res.url !== seekConfig.url) {
|
6329 | if (_this2._onURLRedirect) {
|
6330 | var redirectedURL = _this2._seekHandler.removeURLParameters(res.url);
|
6331 | _this2._onURLRedirect(redirectedURL);
|
6332 | }
|
6333 | }
|
6334 |
|
6335 | var lengthHeader = res.headers.get('Content-Length');
|
6336 | if (lengthHeader != null) {
|
6337 | _this2._contentLength = parseInt(lengthHeader);
|
6338 | if (_this2._contentLength !== 0) {
|
6339 | if (_this2._onContentLengthKnown) {
|
6340 | _this2._onContentLengthKnown(_this2._contentLength);
|
6341 | }
|
6342 | }
|
6343 | }
|
6344 |
|
6345 | return _this2._pump.call(_this2, res.body.getReader());
|
6346 | } else {
|
6347 | _this2._status = _loader.LoaderStatus.kError;
|
6348 | if (_this2._onError) {
|
6349 | _this2._onError(_loader.LoaderErrors.HTTP_STATUS_CODE_INVALID, { code: res.status, msg: res.statusText });
|
6350 | } else {
|
6351 | throw new _exception.RuntimeException('FetchStreamLoader: Http code invalid, ' + res.status + ' ' + res.statusText);
|
6352 | }
|
6353 | }
|
6354 | }).catch(function (e) {
|
6355 | _this2._status = _loader.LoaderStatus.kError;
|
6356 | if (_this2._onError) {
|
6357 | _this2._onError(_loader.LoaderErrors.EXCEPTION, { code: -1, msg: e.message });
|
6358 | } else {
|
6359 | throw e;
|
6360 | }
|
6361 | });
|
6362 | }
|
6363 | }, {
|
6364 | key: 'abort',
|
6365 | value: function abort() {
|
6366 | this._requestAbort = true;
|
6367 | }
|
6368 | }, {
|
6369 | key: '_pump',
|
6370 | value: function _pump(reader) {
|
6371 | var _this3 = this;
|
6372 |
|
6373 | // ReadableStreamReader
|
6374 | return reader.read().then(function (result) {
|
6375 | if (result.done) {
|
6376 | // First check received length
|
6377 | if (_this3._contentLength !== null && _this3._receivedLength < _this3._contentLength) {
|
6378 | // Report Early-EOF
|
6379 | _this3._status = _loader.LoaderStatus.kError;
|
6380 | var type = _loader.LoaderErrors.EARLY_EOF;
|
6381 | var info = { code: -1, msg: 'Fetch stream meet Early-EOF' };
|
6382 | if (_this3._onError) {
|
6383 | _this3._onError(type, info);
|
6384 | } else {
|
6385 | throw new _exception.RuntimeException(info.msg);
|
6386 | }
|
6387 | } else {
|
6388 | // OK. Download complete
|
6389 | _this3._status = _loader.LoaderStatus.kComplete;
|
6390 | if (_this3._onComplete) {
|
6391 | _this3._onComplete(_this3._range.from, _this3._range.from + _this3._receivedLength - 1);
|
6392 | }
|
6393 | }
|
6394 | } else {
|
6395 | if (_this3._requestAbort === true) {
|
6396 | _this3._requestAbort = false;
|
6397 | _this3._status = _loader.LoaderStatus.kComplete;
|
6398 | return reader.cancel();
|
6399 | }
|
6400 |
|
6401 | _this3._status = _loader.LoaderStatus.kBuffering;
|
6402 |
|
6403 | var chunk = result.value.buffer;
|
6404 | var byteStart = _this3._range.from + _this3._receivedLength;
|
6405 | _this3._receivedLength += chunk.byteLength;
|
6406 |
|
6407 | if (_this3._onDataArrival) {
|
6408 | _this3._onDataArrival(chunk, byteStart, _this3._receivedLength);
|
6409 | }
|
6410 |
|
6411 | _this3._pump(reader);
|
6412 | }
|
6413 | }).catch(function (e) {
|
6414 | if (e.code === 11 && _browser2.default.msedge) {
|
6415 | // InvalidStateError on Microsoft Edge
|
6416 | // Workaround: Edge may throw InvalidStateError after ReadableStreamReader.cancel() call
|
6417 | // Ignore the unknown exception.
|
6418 | // Related issue: https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/11265202/
|
6419 | return;
|
6420 | }
|
6421 |
|
6422 | _this3._status = _loader.LoaderStatus.kError;
|
6423 | var type = 0;
|
6424 | var info = null;
|
6425 |
|
6426 | if ((e.code === 19 || e.message === 'network error') && ( // NETWORK_ERR
|
6427 | _this3._contentLength === null || _this3._contentLength !== null && _this3._receivedLength < _this3._contentLength)) {
|
6428 | type = _loader.LoaderErrors.EARLY_EOF;
|
6429 | info = { code: e.code, msg: 'Fetch stream meet Early-EOF' };
|
6430 | } else {
|
6431 | type = _loader.LoaderErrors.EXCEPTION;
|
6432 | info = { code: e.code, msg: e.message };
|
6433 | }
|
6434 |
|
6435 | if (_this3._onError) {
|
6436 | _this3._onError(type, info);
|
6437 | } else {
|
6438 | throw new _exception.RuntimeException(info.msg);
|
6439 | }
|
6440 | });
|
6441 | }
|
6442 | }]);
|
6443 |
|
6444 | return FetchStreamLoader;
|
6445 | }(_loader.BaseLoader);
|
6446 |
|
6447 | exports.default = FetchStreamLoader;
|
6448 |
|
6449 | },{"../utils/browser.js":39,"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24}],23:[function(_dereq_,module,exports){
|
6450 | ;
|
6451 |
|
6452 | Object.defineProperty(exports, "__esModule", {
|
6453 | value: true
|
6454 | });
|
6455 |
|
6456 | 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; }; }(); /*
|
6457 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
6458 | *
|
6459 | * @author zheng qian <xqq@xqq.im>
|
6460 | *
|
6461 | * Licensed under the Apache License, Version 2.0 (the "License");
|
6462 | * you may not use this file except in compliance with the License.
|
6463 | * You may obtain a copy of the License at
|
6464 | *
|
6465 | * http://www.apache.org/licenses/LICENSE-2.0
|
6466 | *
|
6467 | * Unless required by applicable law or agreed to in writing, software
|
6468 | * distributed under the License is distributed on an "AS IS" BASIS,
|
6469 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
6470 | * See the License for the specific language governing permissions and
|
6471 | * limitations under the License.
|
6472 | */
|
6473 |
|
6474 | var _logger = _dereq_('../utils/logger.js');
|
6475 |
|
6476 | var _logger2 = _interopRequireDefault(_logger);
|
6477 |
|
6478 | var _speedSampler = _dereq_('./speed-sampler.js');
|
6479 |
|
6480 | var _speedSampler2 = _interopRequireDefault(_speedSampler);
|
6481 |
|
6482 | var _loader = _dereq_('./loader.js');
|
6483 |
|
6484 | var _fetchStreamLoader = _dereq_('./fetch-stream-loader.js');
|
6485 |
|
6486 | var _fetchStreamLoader2 = _interopRequireDefault(_fetchStreamLoader);
|
6487 |
|
6488 | var _xhrMozChunkedLoader = _dereq_('./xhr-moz-chunked-loader.js');
|
6489 |
|
6490 | var _xhrMozChunkedLoader2 = _interopRequireDefault(_xhrMozChunkedLoader);
|
6491 |
|
6492 | var _xhrMsstreamLoader = _dereq_('./xhr-msstream-loader.js');
|
6493 |
|
6494 | var _xhrMsstreamLoader2 = _interopRequireDefault(_xhrMsstreamLoader);
|
6495 |
|
6496 | var _xhrRangeLoader = _dereq_('./xhr-range-loader.js');
|
6497 |
|
6498 | var _xhrRangeLoader2 = _interopRequireDefault(_xhrRangeLoader);
|
6499 |
|
6500 | var _websocketLoader = _dereq_('./websocket-loader.js');
|
6501 |
|
6502 | var _websocketLoader2 = _interopRequireDefault(_websocketLoader);
|
6503 |
|
6504 | var _rangeSeekHandler = _dereq_('./range-seek-handler.js');
|
6505 |
|
6506 | var _rangeSeekHandler2 = _interopRequireDefault(_rangeSeekHandler);
|
6507 |
|
6508 | var _paramSeekHandler = _dereq_('./param-seek-handler.js');
|
6509 |
|
6510 | var _paramSeekHandler2 = _interopRequireDefault(_paramSeekHandler);
|
6511 |
|
6512 | var _exception = _dereq_('../utils/exception.js');
|
6513 |
|
6514 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
6515 |
|
6516 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
6517 |
|
6518 | /**
|
6519 | * DataSource: {
|
6520 | * url: string,
|
6521 | * filesize: number,
|
6522 | * cors: boolean,
|
6523 | * withCredentials: boolean
|
6524 | * }
|
6525 | *
|
6526 | */
|
6527 |
|
6528 | // Manage IO Loaders
|
6529 | var IOController = function () {
|
6530 | function IOController(dataSource, config, extraData) {
|
6531 | _classCallCheck(this, IOController);
|
6532 |
|
6533 | this.TAG = 'IOController';
|
6534 |
|
6535 | this._config = config;
|
6536 | this._extraData = extraData;
|
6537 |
|
6538 | this._stashInitialSize = 1024 * 384; // default initial size: 384KB
|
6539 | if (config.stashInitialSize != undefined && config.stashInitialSize > 0) {
|
6540 | // apply from config
|
6541 | this._stashInitialSize = config.stashInitialSize;
|
6542 | }
|
6543 |
|
6544 | this._stashUsed = 0;
|
6545 | this._stashSize = this._stashInitialSize;
|
6546 | this._bufferSize = 1024 * 1024 * 3; // initial size: 3MB
|
6547 | this._stashBuffer = new ArrayBuffer(this._bufferSize);
|
6548 | this._stashByteStart = 0;
|
6549 | this._enableStash = true;
|
6550 | if (config.enableStashBuffer === false) {
|
6551 | this._enableStash = false;
|
6552 | }
|
6553 |
|
6554 | this._loader = null;
|
6555 | this._loaderClass = null;
|
6556 | this._seekHandler = null;
|
6557 |
|
6558 | this._dataSource = dataSource;
|
6559 | this._isWebSocketURL = /wss?:\/\/(.+?)/.test(dataSource.url);
|
6560 | this._refTotalLength = dataSource.filesize ? dataSource.filesize : null;
|
6561 | this._totalLength = this._refTotalLength;
|
6562 | this._fullRequestFlag = false;
|
6563 | this._currentRange = null;
|
6564 | this._redirectedURL = null;
|
6565 |
|
6566 | this._speedNormalized = 0;
|
6567 | this._speedSampler = new _speedSampler2.default();
|
6568 | this._speedNormalizeList = [64, 128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096];
|
6569 |
|
6570 | this._isEarlyEofReconnecting = false;
|
6571 |
|
6572 | this._paused = false;
|
6573 | this._resumeFrom = 0;
|
6574 |
|
6575 | this._onDataArrival = null;
|
6576 | this._onSeeked = null;
|
6577 | this._onError = null;
|
6578 | this._onComplete = null;
|
6579 | this._onRedirect = null;
|
6580 | this._onRecoveredEarlyEof = null;
|
6581 |
|
6582 | this._selectSeekHandler();
|
6583 | this._selectLoader();
|
6584 | this._createLoader();
|
6585 | }
|
6586 |
|
6587 | _createClass(IOController, [{
|
6588 | key: 'destroy',
|
6589 | value: function destroy() {
|
6590 | if (this._loader.isWorking()) {
|
6591 | this._loader.abort();
|
6592 | }
|
6593 | this._loader.destroy();
|
6594 | this._loader = null;
|
6595 | this._loaderClass = null;
|
6596 | this._dataSource = null;
|
6597 | this._stashBuffer = null;
|
6598 | this._stashUsed = this._stashSize = this._bufferSize = this._stashByteStart = 0;
|
6599 | this._currentRange = null;
|
6600 | this._speedSampler = null;
|
6601 |
|
6602 | this._isEarlyEofReconnecting = false;
|
6603 |
|
6604 | this._onDataArrival = null;
|
6605 | this._onSeeked = null;
|
6606 | this._onError = null;
|
6607 | this._onComplete = null;
|
6608 | this._onRedirect = null;
|
6609 | this._onRecoveredEarlyEof = null;
|
6610 |
|
6611 | this._extraData = null;
|
6612 | }
|
6613 | }, {
|
6614 | key: 'isWorking',
|
6615 | value: function isWorking() {
|
6616 | return this._loader && this._loader.isWorking() && !this._paused;
|
6617 | }
|
6618 | }, {
|
6619 | key: 'isPaused',
|
6620 | value: function isPaused() {
|
6621 | return this._paused;
|
6622 | }
|
6623 | }, {
|
6624 | key: '_selectSeekHandler',
|
6625 | value: function _selectSeekHandler() {
|
6626 | var config = this._config;
|
6627 |
|
6628 | if (config.seekType === 'range') {
|
6629 | this._seekHandler = new _rangeSeekHandler2.default(this._config.rangeLoadZeroStart);
|
6630 | } else if (config.seekType === 'param') {
|
6631 | var paramStart = config.seekParamStart || 'bstart';
|
6632 | var paramEnd = config.seekParamEnd || 'bend';
|
6633 |
|
6634 | this._seekHandler = new _paramSeekHandler2.default(paramStart, paramEnd);
|
6635 | } else if (config.seekType === 'custom') {
|
6636 | if (typeof config.customSeekHandler !== 'function') {
|
6637 | throw new _exception.InvalidArgumentException('Custom seekType specified in config but invalid customSeekHandler!');
|
6638 | }
|
6639 | this._seekHandler = new config.customSeekHandler();
|
6640 | } else {
|
6641 | throw new _exception.InvalidArgumentException('Invalid seekType in config: ' + config.seekType);
|
6642 | }
|
6643 | }
|
6644 | }, {
|
6645 | key: '_selectLoader',
|
6646 | value: function _selectLoader() {
|
6647 | if (this._config.customLoader != null) {
|
6648 | this._loaderClass = this._config.customLoader;
|
6649 | } else if (this._isWebSocketURL) {
|
6650 | this._loaderClass = _websocketLoader2.default;
|
6651 | } else if (_fetchStreamLoader2.default.isSupported()) {
|
6652 | this._loaderClass = _fetchStreamLoader2.default;
|
6653 | } else if (_xhrMozChunkedLoader2.default.isSupported()) {
|
6654 | this._loaderClass = _xhrMozChunkedLoader2.default;
|
6655 | } else if (_xhrRangeLoader2.default.isSupported()) {
|
6656 | this._loaderClass = _xhrRangeLoader2.default;
|
6657 | } else {
|
6658 | throw new _exception.RuntimeException('Your browser doesn\'t support xhr with arraybuffer responseType!');
|
6659 | }
|
6660 | }
|
6661 | }, {
|
6662 | key: '_createLoader',
|
6663 | value: function _createLoader() {
|
6664 | this._loader = new this._loaderClass(this._seekHandler, this._config);
|
6665 | if (this._loader.needStashBuffer === false) {
|
6666 | this._enableStash = false;
|
6667 | }
|
6668 | this._loader.onContentLengthKnown = this._onContentLengthKnown.bind(this);
|
6669 | this._loader.onURLRedirect = this._onURLRedirect.bind(this);
|
6670 | this._loader.onDataArrival = this._onLoaderChunkArrival.bind(this);
|
6671 | this._loader.onComplete = this._onLoaderComplete.bind(this);
|
6672 | this._loader.onError = this._onLoaderError.bind(this);
|
6673 | }
|
6674 | }, {
|
6675 | key: 'open',
|
6676 | value: function open(optionalFrom) {
|
6677 | this._currentRange = { from: 0, to: -1 };
|
6678 | if (optionalFrom) {
|
6679 | this._currentRange.from = optionalFrom;
|
6680 | }
|
6681 |
|
6682 | this._speedSampler.reset();
|
6683 | if (!optionalFrom) {
|
6684 | this._fullRequestFlag = true;
|
6685 | }
|
6686 |
|
6687 | this._loader.open(this._dataSource, Object.assign({}, this._currentRange));
|
6688 | }
|
6689 | }, {
|
6690 | key: 'abort',
|
6691 | value: function abort() {
|
6692 | this._loader.abort();
|
6693 |
|
6694 | if (this._paused) {
|
6695 | this._paused = false;
|
6696 | this._resumeFrom = 0;
|
6697 | }
|
6698 | }
|
6699 | }, {
|
6700 | key: 'pause',
|
6701 | value: function pause() {
|
6702 | if (this.isWorking()) {
|
6703 | this._loader.abort();
|
6704 |
|
6705 | if (this._stashUsed !== 0) {
|
6706 | this._resumeFrom = this._stashByteStart;
|
6707 | this._currentRange.to = this._stashByteStart - 1;
|
6708 | } else {
|
6709 | this._resumeFrom = this._currentRange.to + 1;
|
6710 | }
|
6711 | this._stashUsed = 0;
|
6712 | this._stashByteStart = 0;
|
6713 | this._paused = true;
|
6714 | }
|
6715 | }
|
6716 | }, {
|
6717 | key: 'resume',
|
6718 | value: function resume() {
|
6719 | if (this._paused) {
|
6720 | this._paused = false;
|
6721 | var bytes = this._resumeFrom;
|
6722 | this._resumeFrom = 0;
|
6723 | this._internalSeek(bytes, true);
|
6724 | }
|
6725 | }
|
6726 | }, {
|
6727 | key: 'seek',
|
6728 | value: function seek(bytes) {
|
6729 | this._paused = false;
|
6730 | this._stashUsed = 0;
|
6731 | this._stashByteStart = 0;
|
6732 | this._internalSeek(bytes, true);
|
6733 | }
|
6734 |
|
6735 | /**
|
6736 | * When seeking request is from media seeking, unconsumed stash data should be dropped
|
6737 | * However, stash data shouldn't be dropped if seeking requested from http reconnection
|
6738 | *
|
6739 | * @dropUnconsumed: Ignore and discard all unconsumed data in stash buffer
|
6740 | */
|
6741 |
|
6742 | }, {
|
6743 | key: '_internalSeek',
|
6744 | value: function _internalSeek(bytes, dropUnconsumed) {
|
6745 | if (this._loader.isWorking()) {
|
6746 | this._loader.abort();
|
6747 | }
|
6748 |
|
6749 | // dispatch & flush stash buffer before seek
|
6750 | this._flushStashBuffer(dropUnconsumed);
|
6751 |
|
6752 | this._loader.destroy();
|
6753 | this._loader = null;
|
6754 |
|
6755 | var requestRange = { from: bytes, to: -1 };
|
6756 | this._currentRange = { from: requestRange.from, to: -1 };
|
6757 |
|
6758 | this._speedSampler.reset();
|
6759 | this._stashSize = this._stashInitialSize;
|
6760 | this._createLoader();
|
6761 | this._loader.open(this._dataSource, requestRange);
|
6762 |
|
6763 | if (this._onSeeked) {
|
6764 | this._onSeeked();
|
6765 | }
|
6766 | }
|
6767 | }, {
|
6768 | key: 'updateUrl',
|
6769 | value: function updateUrl(url) {
|
6770 | if (!url || typeof url !== 'string' || url.length === 0) {
|
6771 | throw new _exception.InvalidArgumentException('Url must be a non-empty string!');
|
6772 | }
|
6773 |
|
6774 | this._dataSource.url = url;
|
6775 |
|
6776 | // TODO: replace with new url
|
6777 | }
|
6778 | }, {
|
6779 | key: '_expandBuffer',
|
6780 | value: function _expandBuffer(expectedBytes) {
|
6781 | var bufferNewSize = this._stashSize;
|
6782 | while (bufferNewSize + 1024 * 1024 * 1 < expectedBytes) {
|
6783 | bufferNewSize *= 2;
|
6784 | }
|
6785 |
|
6786 | bufferNewSize += 1024 * 1024 * 1; // bufferSize = stashSize + 1MB
|
6787 | if (bufferNewSize === this._bufferSize) {
|
6788 | return;
|
6789 | }
|
6790 |
|
6791 | var newBuffer = new ArrayBuffer(bufferNewSize);
|
6792 |
|
6793 | if (this._stashUsed > 0) {
|
6794 | // copy existing data into new buffer
|
6795 | var stashOldArray = new Uint8Array(this._stashBuffer, 0, this._stashUsed);
|
6796 | var stashNewArray = new Uint8Array(newBuffer, 0, bufferNewSize);
|
6797 | stashNewArray.set(stashOldArray, 0);
|
6798 | }
|
6799 |
|
6800 | this._stashBuffer = newBuffer;
|
6801 | this._bufferSize = bufferNewSize;
|
6802 | }
|
6803 | }, {
|
6804 | key: '_normalizeSpeed',
|
6805 | value: function _normalizeSpeed(input) {
|
6806 | var list = this._speedNormalizeList;
|
6807 | var last = list.length - 1;
|
6808 | var mid = 0;
|
6809 | var lbound = 0;
|
6810 | var ubound = last;
|
6811 |
|
6812 | if (input < list[0]) {
|
6813 | return list[0];
|
6814 | }
|
6815 |
|
6816 | // binary search
|
6817 | while (lbound <= ubound) {
|
6818 | mid = lbound + Math.floor((ubound - lbound) / 2);
|
6819 | if (mid === last || input >= list[mid] && input < list[mid + 1]) {
|
6820 | return list[mid];
|
6821 | } else if (list[mid] < input) {
|
6822 | lbound = mid + 1;
|
6823 | } else {
|
6824 | ubound = mid - 1;
|
6825 | }
|
6826 | }
|
6827 | }
|
6828 | }, {
|
6829 | key: '_adjustStashSize',
|
6830 | value: function _adjustStashSize(normalized) {
|
6831 | var stashSizeKB = 0;
|
6832 |
|
6833 | if (this._config.isLive) {
|
6834 | // live stream: always use single normalized speed for size of stashSizeKB
|
6835 | stashSizeKB = normalized;
|
6836 | } else {
|
6837 | if (normalized < 512) {
|
6838 | stashSizeKB = normalized;
|
6839 | } else if (normalized >= 512 && normalized <= 1024) {
|
6840 | stashSizeKB = Math.floor(normalized * 1.5);
|
6841 | } else {
|
6842 | stashSizeKB = normalized * 2;
|
6843 | }
|
6844 | }
|
6845 |
|
6846 | if (stashSizeKB > 8192) {
|
6847 | stashSizeKB = 8192;
|
6848 | }
|
6849 |
|
6850 | var bufferSize = stashSizeKB * 1024 + 1024 * 1024 * 1; // stashSize + 1MB
|
6851 | if (this._bufferSize < bufferSize) {
|
6852 | this._expandBuffer(bufferSize);
|
6853 | }
|
6854 | this._stashSize = stashSizeKB * 1024;
|
6855 | }
|
6856 | }, {
|
6857 | key: '_dispatchChunks',
|
6858 | value: function _dispatchChunks(chunks, byteStart) {
|
6859 | this._currentRange.to = byteStart + chunks.byteLength - 1;
|
6860 | return this._onDataArrival(chunks, byteStart);
|
6861 | }
|
6862 | }, {
|
6863 | key: '_onURLRedirect',
|
6864 | value: function _onURLRedirect(redirectedURL) {
|
6865 | this._redirectedURL = redirectedURL;
|
6866 | if (this._onRedirect) {
|
6867 | this._onRedirect(redirectedURL);
|
6868 | }
|
6869 | }
|
6870 | }, {
|
6871 | key: '_onContentLengthKnown',
|
6872 | value: function _onContentLengthKnown(contentLength) {
|
6873 | if (contentLength && this._fullRequestFlag) {
|
6874 | this._totalLength = contentLength;
|
6875 | this._fullRequestFlag = false;
|
6876 | }
|
6877 | }
|
6878 | }, {
|
6879 | key: '_onLoaderChunkArrival',
|
6880 | value: function _onLoaderChunkArrival(chunk, byteStart, receivedLength) {
|
6881 | if (!this._onDataArrival) {
|
6882 | throw new _exception.IllegalStateException('IOController: No existing consumer (onDataArrival) callback!');
|
6883 | }
|
6884 | if (this._paused) {
|
6885 | return;
|
6886 | }
|
6887 | if (this._isEarlyEofReconnecting) {
|
6888 | // Auto-reconnect for EarlyEof succeed, notify to upper-layer by callback
|
6889 | this._isEarlyEofReconnecting = false;
|
6890 | if (this._onRecoveredEarlyEof) {
|
6891 | this._onRecoveredEarlyEof();
|
6892 | }
|
6893 | }
|
6894 |
|
6895 | this._speedSampler.addBytes(chunk.byteLength);
|
6896 |
|
6897 | // adjust stash buffer size according to network speed dynamically
|
6898 | var KBps = this._speedSampler.lastSecondKBps;
|
6899 | if (KBps !== 0) {
|
6900 | var normalized = this._normalizeSpeed(KBps);
|
6901 | if (this._speedNormalized !== normalized) {
|
6902 | this._speedNormalized = normalized;
|
6903 | this._adjustStashSize(normalized);
|
6904 | }
|
6905 | }
|
6906 |
|
6907 | if (!this._enableStash) {
|
6908 | // disable stash
|
6909 | if (this._stashUsed === 0) {
|
6910 | // dispatch chunk directly to consumer;
|
6911 | // check ret value (consumed bytes) and stash unconsumed to stashBuffer
|
6912 | var consumed = this._dispatchChunks(chunk, byteStart);
|
6913 | if (consumed < chunk.byteLength) {
|
6914 | // unconsumed data remain.
|
6915 | var remain = chunk.byteLength - consumed;
|
6916 | if (remain > this._bufferSize) {
|
6917 | this._expandBuffer(remain);
|
6918 | }
|
6919 | var stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
|
6920 | stashArray.set(new Uint8Array(chunk, consumed), 0);
|
6921 | this._stashUsed += remain;
|
6922 | this._stashByteStart = byteStart + consumed;
|
6923 | }
|
6924 | } else {
|
6925 | // else: Merge chunk into stashBuffer, and dispatch stashBuffer to consumer.
|
6926 | if (this._stashUsed + chunk.byteLength > this._bufferSize) {
|
6927 | this._expandBuffer(this._stashUsed + chunk.byteLength);
|
6928 | }
|
6929 | var _stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
|
6930 | _stashArray.set(new Uint8Array(chunk), this._stashUsed);
|
6931 | this._stashUsed += chunk.byteLength;
|
6932 | var _consumed = this._dispatchChunks(this._stashBuffer.slice(0, this._stashUsed), this._stashByteStart);
|
6933 | if (_consumed < this._stashUsed && _consumed > 0) {
|
6934 | // unconsumed data remain
|
6935 | var remainArray = new Uint8Array(this._stashBuffer, _consumed);
|
6936 | _stashArray.set(remainArray, 0);
|
6937 | }
|
6938 | this._stashUsed -= _consumed;
|
6939 | this._stashByteStart += _consumed;
|
6940 | }
|
6941 | } else {
|
6942 | // enable stash
|
6943 | if (this._stashUsed === 0 && this._stashByteStart === 0) {
|
6944 | // seeked? or init chunk?
|
6945 | // This is the first chunk after seek action
|
6946 | this._stashByteStart = byteStart;
|
6947 | }
|
6948 | if (this._stashUsed + chunk.byteLength <= this._stashSize) {
|
6949 | // just stash
|
6950 | var _stashArray2 = new Uint8Array(this._stashBuffer, 0, this._stashSize);
|
6951 | _stashArray2.set(new Uint8Array(chunk), this._stashUsed);
|
6952 | this._stashUsed += chunk.byteLength;
|
6953 | } else {
|
6954 | // stashUsed + chunkSize > stashSize, size limit exceeded
|
6955 | var _stashArray3 = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
|
6956 | if (this._stashUsed > 0) {
|
6957 | // There're stash datas in buffer
|
6958 | // dispatch the whole stashBuffer, and stash remain data
|
6959 | // then append chunk to stashBuffer (stash)
|
6960 | var buffer = this._stashBuffer.slice(0, this._stashUsed);
|
6961 | var _consumed2 = this._dispatchChunks(buffer, this._stashByteStart);
|
6962 | if (_consumed2 < buffer.byteLength) {
|
6963 | if (_consumed2 > 0) {
|
6964 | var _remainArray = new Uint8Array(buffer, _consumed2);
|
6965 | _stashArray3.set(_remainArray, 0);
|
6966 | this._stashUsed = _remainArray.byteLength;
|
6967 | this._stashByteStart += _consumed2;
|
6968 | }
|
6969 | } else {
|
6970 | this._stashUsed = 0;
|
6971 | this._stashByteStart += _consumed2;
|
6972 | }
|
6973 | if (this._stashUsed + chunk.byteLength > this._bufferSize) {
|
6974 | this._expandBuffer(this._stashUsed + chunk.byteLength);
|
6975 | _stashArray3 = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
|
6976 | }
|
6977 | _stashArray3.set(new Uint8Array(chunk), this._stashUsed);
|
6978 | this._stashUsed += chunk.byteLength;
|
6979 | } else {
|
6980 | // stash buffer empty, but chunkSize > stashSize (oh, holy shit)
|
6981 | // dispatch chunk directly and stash remain data
|
6982 | var _consumed3 = this._dispatchChunks(chunk, byteStart);
|
6983 | if (_consumed3 < chunk.byteLength) {
|
6984 | var _remain = chunk.byteLength - _consumed3;
|
6985 | if (_remain > this._bufferSize) {
|
6986 | this._expandBuffer(_remain);
|
6987 | _stashArray3 = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
|
6988 | }
|
6989 | _stashArray3.set(new Uint8Array(chunk, _consumed3), 0);
|
6990 | this._stashUsed += _remain;
|
6991 | this._stashByteStart = byteStart + _consumed3;
|
6992 | }
|
6993 | }
|
6994 | }
|
6995 | }
|
6996 | }
|
6997 | }, {
|
6998 | key: '_flushStashBuffer',
|
6999 | value: function _flushStashBuffer(dropUnconsumed) {
|
7000 | if (this._stashUsed > 0) {
|
7001 | var buffer = this._stashBuffer.slice(0, this._stashUsed);
|
7002 | var consumed = this._dispatchChunks(buffer, this._stashByteStart);
|
7003 | var remain = buffer.byteLength - consumed;
|
7004 |
|
7005 | if (consumed < buffer.byteLength) {
|
7006 | if (dropUnconsumed) {
|
7007 | _logger2.default.w(this.TAG, remain + ' bytes unconsumed data remain when flush buffer, dropped');
|
7008 | } else {
|
7009 | if (consumed > 0) {
|
7010 | var stashArray = new Uint8Array(this._stashBuffer, 0, this._bufferSize);
|
7011 | var remainArray = new Uint8Array(buffer, consumed);
|
7012 | stashArray.set(remainArray, 0);
|
7013 | this._stashUsed = remainArray.byteLength;
|
7014 | this._stashByteStart += consumed;
|
7015 | }
|
7016 | return 0;
|
7017 | }
|
7018 | }
|
7019 | this._stashUsed = 0;
|
7020 | this._stashByteStart = 0;
|
7021 | return remain;
|
7022 | }
|
7023 | return 0;
|
7024 | }
|
7025 | }, {
|
7026 | key: '_onLoaderComplete',
|
7027 | value: function _onLoaderComplete(from, to) {
|
7028 | // Force-flush stash buffer, and drop unconsumed data
|
7029 | this._flushStashBuffer(true);
|
7030 |
|
7031 | if (this._onComplete) {
|
7032 | this._onComplete(this._extraData);
|
7033 | }
|
7034 | }
|
7035 | }, {
|
7036 | key: '_onLoaderError',
|
7037 | value: function _onLoaderError(type, data) {
|
7038 | _logger2.default.e(this.TAG, 'Loader error, code = ' + data.code + ', msg = ' + data.msg);
|
7039 |
|
7040 | this._flushStashBuffer(false);
|
7041 |
|
7042 | if (this._isEarlyEofReconnecting) {
|
7043 | // Auto-reconnect for EarlyEof failed, throw UnrecoverableEarlyEof error to upper-layer
|
7044 | this._isEarlyEofReconnecting = false;
|
7045 | type = _loader.LoaderErrors.UNRECOVERABLE_EARLY_EOF;
|
7046 | }
|
7047 |
|
7048 | switch (type) {
|
7049 | case _loader.LoaderErrors.EARLY_EOF:
|
7050 | {
|
7051 | if (!this._config.isLive) {
|
7052 | // Do internal http reconnect if not live stream
|
7053 | if (this._totalLength) {
|
7054 | var nextFrom = this._currentRange.to + 1;
|
7055 | if (nextFrom < this._totalLength) {
|
7056 | _logger2.default.w(this.TAG, 'Connection lost, trying reconnect...');
|
7057 | this._isEarlyEofReconnecting = true;
|
7058 | this._internalSeek(nextFrom, false);
|
7059 | }
|
7060 | return;
|
7061 | }
|
7062 | // else: We don't know totalLength, throw UnrecoverableEarlyEof
|
7063 | }
|
7064 | // live stream: throw UnrecoverableEarlyEof error to upper-layer
|
7065 | type = _loader.LoaderErrors.UNRECOVERABLE_EARLY_EOF;
|
7066 | break;
|
7067 | }
|
7068 | case _loader.LoaderErrors.UNRECOVERABLE_EARLY_EOF:
|
7069 | case _loader.LoaderErrors.CONNECTING_TIMEOUT:
|
7070 | case _loader.LoaderErrors.HTTP_STATUS_CODE_INVALID:
|
7071 | case _loader.LoaderErrors.EXCEPTION:
|
7072 | break;
|
7073 | }
|
7074 |
|
7075 | if (this._onError) {
|
7076 | this._onError(type, data);
|
7077 | } else {
|
7078 | throw new _exception.RuntimeException('IOException: ' + data.msg);
|
7079 | }
|
7080 | }
|
7081 | }, {
|
7082 | key: 'status',
|
7083 | get: function get() {
|
7084 | return this._loader.status;
|
7085 | }
|
7086 | }, {
|
7087 | key: 'extraData',
|
7088 | get: function get() {
|
7089 | return this._extraData;
|
7090 | },
|
7091 | set: function set(data) {
|
7092 | this._extraData = data;
|
7093 | }
|
7094 |
|
7095 | // prototype: function onDataArrival(chunks: ArrayBuffer, byteStart: number): number
|
7096 |
|
7097 | }, {
|
7098 | key: 'onDataArrival',
|
7099 | get: function get() {
|
7100 | return this._onDataArrival;
|
7101 | },
|
7102 | set: function set(callback) {
|
7103 | this._onDataArrival = callback;
|
7104 | }
|
7105 | }, {
|
7106 | key: 'onSeeked',
|
7107 | get: function get() {
|
7108 | return this._onSeeked;
|
7109 | },
|
7110 | set: function set(callback) {
|
7111 | this._onSeeked = callback;
|
7112 | }
|
7113 |
|
7114 | // prototype: function onError(type: number, info: {code: number, msg: string}): void
|
7115 |
|
7116 | }, {
|
7117 | key: 'onError',
|
7118 | get: function get() {
|
7119 | return this._onError;
|
7120 | },
|
7121 | set: function set(callback) {
|
7122 | this._onError = callback;
|
7123 | }
|
7124 | }, {
|
7125 | key: 'onComplete',
|
7126 | get: function get() {
|
7127 | return this._onComplete;
|
7128 | },
|
7129 | set: function set(callback) {
|
7130 | this._onComplete = callback;
|
7131 | }
|
7132 | }, {
|
7133 | key: 'onRedirect',
|
7134 | get: function get() {
|
7135 | return this._onRedirect;
|
7136 | },
|
7137 | set: function set(callback) {
|
7138 | this._onRedirect = callback;
|
7139 | }
|
7140 | }, {
|
7141 | key: 'onRecoveredEarlyEof',
|
7142 | get: function get() {
|
7143 | return this._onRecoveredEarlyEof;
|
7144 | },
|
7145 | set: function set(callback) {
|
7146 | this._onRecoveredEarlyEof = callback;
|
7147 | }
|
7148 | }, {
|
7149 | key: 'currentURL',
|
7150 | get: function get() {
|
7151 | return this._dataSource.url;
|
7152 | }
|
7153 | }, {
|
7154 | key: 'hasRedirect',
|
7155 | get: function get() {
|
7156 | return this._redirectedURL != null || this._dataSource.redirectedURL != undefined;
|
7157 | }
|
7158 | }, {
|
7159 | key: 'currentRedirectedURL',
|
7160 | get: function get() {
|
7161 | return this._redirectedURL || this._dataSource.redirectedURL;
|
7162 | }
|
7163 |
|
7164 | // in KB/s
|
7165 |
|
7166 | }, {
|
7167 | key: 'currentSpeed',
|
7168 | get: function get() {
|
7169 | if (this._loaderClass === _xhrRangeLoader2.default) {
|
7170 | // SpeedSampler is inaccuracy if loader is RangeLoader
|
7171 | return this._loader.currentSpeed;
|
7172 | }
|
7173 | return this._speedSampler.lastSecondKBps;
|
7174 | }
|
7175 | }, {
|
7176 | key: 'loaderType',
|
7177 | get: function get() {
|
7178 | return this._loader.type;
|
7179 | }
|
7180 | }]);
|
7181 |
|
7182 | return IOController;
|
7183 | }();
|
7184 |
|
7185 | exports.default = IOController;
|
7186 |
|
7187 | },{"../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){
|
7188 | ;
|
7189 |
|
7190 | Object.defineProperty(exports, "__esModule", {
|
7191 | value: true
|
7192 | });
|
7193 | exports.BaseLoader = exports.LoaderErrors = exports.LoaderStatus = undefined;
|
7194 |
|
7195 | 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; }; }(); /*
|
7196 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
7197 | *
|
7198 | * @author zheng qian <xqq@xqq.im>
|
7199 | *
|
7200 | * Licensed under the Apache License, Version 2.0 (the "License");
|
7201 | * you may not use this file except in compliance with the License.
|
7202 | * You may obtain a copy of the License at
|
7203 | *
|
7204 | * http://www.apache.org/licenses/LICENSE-2.0
|
7205 | *
|
7206 | * Unless required by applicable law or agreed to in writing, software
|
7207 | * distributed under the License is distributed on an "AS IS" BASIS,
|
7208 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
7209 | * See the License for the specific language governing permissions and
|
7210 | * limitations under the License.
|
7211 | */
|
7212 |
|
7213 | var _exception = _dereq_('../utils/exception.js');
|
7214 |
|
7215 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
7216 |
|
7217 | var LoaderStatus = exports.LoaderStatus = {
|
7218 | kIdle: 0,
|
7219 | kConnecting: 1,
|
7220 | kBuffering: 2,
|
7221 | kError: 3,
|
7222 | kComplete: 4
|
7223 | };
|
7224 |
|
7225 | var LoaderErrors = exports.LoaderErrors = {
|
7226 | OK: 'OK',
|
7227 | EXCEPTION: 'Exception',
|
7228 | HTTP_STATUS_CODE_INVALID: 'HttpStatusCodeInvalid',
|
7229 | CONNECTING_TIMEOUT: 'ConnectingTimeout',
|
7230 | EARLY_EOF: 'EarlyEof',
|
7231 | UNRECOVERABLE_EARLY_EOF: 'UnrecoverableEarlyEof'
|
7232 | };
|
7233 |
|
7234 | /* Loader has callbacks which have following prototypes:
|
7235 | * function onContentLengthKnown(contentLength: number): void
|
7236 | * function onURLRedirect(url: string): void
|
7237 | * function onDataArrival(chunk: ArrayBuffer, byteStart: number, receivedLength: number): void
|
7238 | * function onError(errorType: number, errorInfo: {code: number, msg: string}): void
|
7239 | * function onComplete(rangeFrom: number, rangeTo: number): void
|
7240 | */
|
7241 |
|
7242 | var BaseLoader = exports.BaseLoader = function () {
|
7243 | function BaseLoader(typeName) {
|
7244 | _classCallCheck(this, BaseLoader);
|
7245 |
|
7246 | this._type = typeName || 'undefined';
|
7247 | this._status = LoaderStatus.kIdle;
|
7248 | this._needStash = false;
|
7249 | // callbacks
|
7250 | this._onContentLengthKnown = null;
|
7251 | this._onURLRedirect = null;
|
7252 | this._onDataArrival = null;
|
7253 | this._onError = null;
|
7254 | this._onComplete = null;
|
7255 | }
|
7256 |
|
7257 | _createClass(BaseLoader, [{
|
7258 | key: 'destroy',
|
7259 | value: function destroy() {
|
7260 | this._status = LoaderStatus.kIdle;
|
7261 | this._onContentLengthKnown = null;
|
7262 | this._onURLRedirect = null;
|
7263 | this._onDataArrival = null;
|
7264 | this._onError = null;
|
7265 | this._onComplete = null;
|
7266 | }
|
7267 | }, {
|
7268 | key: 'isWorking',
|
7269 | value: function isWorking() {
|
7270 | return this._status === LoaderStatus.kConnecting || this._status === LoaderStatus.kBuffering;
|
7271 | }
|
7272 | }, {
|
7273 | key: 'open',
|
7274 |
|
7275 |
|
7276 | // pure virtual
|
7277 | value: function open(dataSource, range) {
|
7278 | throw new _exception.NotImplementedException('Unimplemented abstract function!');
|
7279 | }
|
7280 | }, {
|
7281 | key: 'abort',
|
7282 | value: function abort() {
|
7283 | throw new _exception.NotImplementedException('Unimplemented abstract function!');
|
7284 | }
|
7285 | }, {
|
7286 | key: 'type',
|
7287 | get: function get() {
|
7288 | return this._type;
|
7289 | }
|
7290 | }, {
|
7291 | key: 'status',
|
7292 | get: function get() {
|
7293 | return this._status;
|
7294 | }
|
7295 | }, {
|
7296 | key: 'needStashBuffer',
|
7297 | get: function get() {
|
7298 | return this._needStash;
|
7299 | }
|
7300 | }, {
|
7301 | key: 'onContentLengthKnown',
|
7302 | get: function get() {
|
7303 | return this._onContentLengthKnown;
|
7304 | },
|
7305 | set: function set(callback) {
|
7306 | this._onContentLengthKnown = callback;
|
7307 | }
|
7308 | }, {
|
7309 | key: 'onURLRedirect',
|
7310 | get: function get() {
|
7311 | return this._onURLRedirect;
|
7312 | },
|
7313 | set: function set(callback) {
|
7314 | this._onURLRedirect = callback;
|
7315 | }
|
7316 | }, {
|
7317 | key: 'onDataArrival',
|
7318 | get: function get() {
|
7319 | return this._onDataArrival;
|
7320 | },
|
7321 | set: function set(callback) {
|
7322 | this._onDataArrival = callback;
|
7323 | }
|
7324 | }, {
|
7325 | key: 'onError',
|
7326 | get: function get() {
|
7327 | return this._onError;
|
7328 | },
|
7329 | set: function set(callback) {
|
7330 | this._onError = callback;
|
7331 | }
|
7332 | }, {
|
7333 | key: 'onComplete',
|
7334 | get: function get() {
|
7335 | return this._onComplete;
|
7336 | },
|
7337 | set: function set(callback) {
|
7338 | this._onComplete = callback;
|
7339 | }
|
7340 | }]);
|
7341 |
|
7342 | return BaseLoader;
|
7343 | }();
|
7344 |
|
7345 | },{"../utils/exception.js":40}],25:[function(_dereq_,module,exports){
|
7346 | ;
|
7347 |
|
7348 | Object.defineProperty(exports, "__esModule", {
|
7349 | value: true
|
7350 | });
|
7351 |
|
7352 | 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; }; }();
|
7353 |
|
7354 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
7355 |
|
7356 | /*
|
7357 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
7358 | *
|
7359 | * @author zheng qian <xqq@xqq.im>
|
7360 | *
|
7361 | * Licensed under the Apache License, Version 2.0 (the "License");
|
7362 | * you may not use this file except in compliance with the License.
|
7363 | * You may obtain a copy of the License at
|
7364 | *
|
7365 | * http://www.apache.org/licenses/LICENSE-2.0
|
7366 | *
|
7367 | * Unless required by applicable law or agreed to in writing, software
|
7368 | * distributed under the License is distributed on an "AS IS" BASIS,
|
7369 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
7370 | * See the License for the specific language governing permissions and
|
7371 | * limitations under the License.
|
7372 | */
|
7373 |
|
7374 | var ParamSeekHandler = function () {
|
7375 | function ParamSeekHandler(paramStart, paramEnd) {
|
7376 | _classCallCheck(this, ParamSeekHandler);
|
7377 |
|
7378 | this._startName = paramStart;
|
7379 | this._endName = paramEnd;
|
7380 | }
|
7381 |
|
7382 | _createClass(ParamSeekHandler, [{
|
7383 | key: 'getConfig',
|
7384 | value: function getConfig(baseUrl, range) {
|
7385 | var url = baseUrl;
|
7386 |
|
7387 | if (range.from !== 0 || range.to !== -1) {
|
7388 | var needAnd = true;
|
7389 | if (url.indexOf('?') === -1) {
|
7390 | url += '?';
|
7391 | needAnd = false;
|
7392 | }
|
7393 |
|
7394 | if (needAnd) {
|
7395 | url += '&';
|
7396 | }
|
7397 |
|
7398 | url += this._startName + '=' + range.from.toString();
|
7399 |
|
7400 | if (range.to !== -1) {
|
7401 | url += '&' + this._endName + '=' + range.to.toString();
|
7402 | }
|
7403 | }
|
7404 |
|
7405 | return {
|
7406 | url: url,
|
7407 | headers: {}
|
7408 | };
|
7409 | }
|
7410 | }, {
|
7411 | key: 'removeURLParameters',
|
7412 | value: function removeURLParameters(seekedURL) {
|
7413 | var baseURL = seekedURL.split('?')[0];
|
7414 | var params = undefined;
|
7415 |
|
7416 | var queryIndex = seekedURL.indexOf('?');
|
7417 | if (queryIndex !== -1) {
|
7418 | params = seekedURL.substring(queryIndex + 1);
|
7419 | }
|
7420 |
|
7421 | var resultParams = '';
|
7422 |
|
7423 | if (params != undefined && params.length > 0) {
|
7424 | var pairs = params.split('&');
|
7425 |
|
7426 | for (var i = 0; i < pairs.length; i++) {
|
7427 | var pair = pairs[i].split('=');
|
7428 | var requireAnd = i > 0;
|
7429 |
|
7430 | if (pair[0] !== this._startName && pair[0] !== this._endName) {
|
7431 | if (requireAnd) {
|
7432 | resultParams += '&';
|
7433 | }
|
7434 | resultParams += pairs[i];
|
7435 | }
|
7436 | }
|
7437 | }
|
7438 |
|
7439 | return resultParams.length === 0 ? baseURL : baseURL + '?' + resultParams;
|
7440 | }
|
7441 | }]);
|
7442 |
|
7443 | return ParamSeekHandler;
|
7444 | }();
|
7445 |
|
7446 | exports.default = ParamSeekHandler;
|
7447 |
|
7448 | },{}],26:[function(_dereq_,module,exports){
|
7449 | ;
|
7450 |
|
7451 | Object.defineProperty(exports, "__esModule", {
|
7452 | value: true
|
7453 | });
|
7454 |
|
7455 | 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; }; }();
|
7456 |
|
7457 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
7458 |
|
7459 | /*
|
7460 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
7461 | *
|
7462 | * @author zheng qian <xqq@xqq.im>
|
7463 | *
|
7464 | * Licensed under the Apache License, Version 2.0 (the "License");
|
7465 | * you may not use this file except in compliance with the License.
|
7466 | * You may obtain a copy of the License at
|
7467 | *
|
7468 | * http://www.apache.org/licenses/LICENSE-2.0
|
7469 | *
|
7470 | * Unless required by applicable law or agreed to in writing, software
|
7471 | * distributed under the License is distributed on an "AS IS" BASIS,
|
7472 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
7473 | * See the License for the specific language governing permissions and
|
7474 | * limitations under the License.
|
7475 | */
|
7476 |
|
7477 | var RangeSeekHandler = function () {
|
7478 | function RangeSeekHandler(zeroStart) {
|
7479 | _classCallCheck(this, RangeSeekHandler);
|
7480 |
|
7481 | this._zeroStart = zeroStart || false;
|
7482 | }
|
7483 |
|
7484 | _createClass(RangeSeekHandler, [{
|
7485 | key: 'getConfig',
|
7486 | value: function getConfig(url, range) {
|
7487 | var headers = {};
|
7488 |
|
7489 | if (range.from !== 0 || range.to !== -1) {
|
7490 | var param = void 0;
|
7491 | if (range.to !== -1) {
|
7492 | param = 'bytes=' + range.from.toString() + '-' + range.to.toString();
|
7493 | } else {
|
7494 | param = 'bytes=' + range.from.toString() + '-';
|
7495 | }
|
7496 | headers['Range'] = param;
|
7497 | } else if (this._zeroStart) {
|
7498 | headers['Range'] = 'bytes=0-';
|
7499 | }
|
7500 |
|
7501 | return {
|
7502 | url: url,
|
7503 | headers: headers
|
7504 | };
|
7505 | }
|
7506 | }, {
|
7507 | key: 'removeURLParameters',
|
7508 | value: function removeURLParameters(seekedURL) {
|
7509 | return seekedURL;
|
7510 | }
|
7511 | }]);
|
7512 |
|
7513 | return RangeSeekHandler;
|
7514 | }();
|
7515 |
|
7516 | exports.default = RangeSeekHandler;
|
7517 |
|
7518 | },{}],27:[function(_dereq_,module,exports){
|
7519 | ;
|
7520 |
|
7521 | Object.defineProperty(exports, "__esModule", {
|
7522 | value: true
|
7523 | });
|
7524 |
|
7525 | 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; }; }();
|
7526 |
|
7527 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
7528 |
|
7529 | /*
|
7530 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
7531 | *
|
7532 | * @author zheng qian <xqq@xqq.im>
|
7533 | *
|
7534 | * Licensed under the Apache License, Version 2.0 (the "License");
|
7535 | * you may not use this file except in compliance with the License.
|
7536 | * You may obtain a copy of the License at
|
7537 | *
|
7538 | * http://www.apache.org/licenses/LICENSE-2.0
|
7539 | *
|
7540 | * Unless required by applicable law or agreed to in writing, software
|
7541 | * distributed under the License is distributed on an "AS IS" BASIS,
|
7542 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
7543 | * See the License for the specific language governing permissions and
|
7544 | * limitations under the License.
|
7545 | */
|
7546 |
|
7547 | // Utility class to calculate realtime network I/O speed
|
7548 | var SpeedSampler = function () {
|
7549 | function SpeedSampler() {
|
7550 | _classCallCheck(this, SpeedSampler);
|
7551 |
|
7552 | // milliseconds
|
7553 | this._firstCheckpoint = 0;
|
7554 | this._lastCheckpoint = 0;
|
7555 | this._intervalBytes = 0;
|
7556 | this._totalBytes = 0;
|
7557 | this._lastSecondBytes = 0;
|
7558 |
|
7559 | // compatibility detection
|
7560 | if (self.performance && self.performance.now) {
|
7561 | this._now = self.performance.now.bind(self.performance);
|
7562 | } else {
|
7563 | this._now = Date.now;
|
7564 | }
|
7565 | }
|
7566 |
|
7567 | _createClass(SpeedSampler, [{
|
7568 | key: "reset",
|
7569 | value: function reset() {
|
7570 | this._firstCheckpoint = this._lastCheckpoint = 0;
|
7571 | this._totalBytes = this._intervalBytes = 0;
|
7572 | this._lastSecondBytes = 0;
|
7573 | }
|
7574 | }, {
|
7575 | key: "addBytes",
|
7576 | value: function addBytes(bytes) {
|
7577 | if (this._firstCheckpoint === 0) {
|
7578 | this._firstCheckpoint = this._now();
|
7579 | this._lastCheckpoint = this._firstCheckpoint;
|
7580 | this._intervalBytes += bytes;
|
7581 | this._totalBytes += bytes;
|
7582 | } else if (this._now() - this._lastCheckpoint < 1000) {
|
7583 | this._intervalBytes += bytes;
|
7584 | this._totalBytes += bytes;
|
7585 | } else {
|
7586 | // duration >= 1000
|
7587 | this._lastSecondBytes = this._intervalBytes;
|
7588 | this._intervalBytes = bytes;
|
7589 | this._totalBytes += bytes;
|
7590 | this._lastCheckpoint = this._now();
|
7591 | }
|
7592 | }
|
7593 | }, {
|
7594 | key: "currentKBps",
|
7595 | get: function get() {
|
7596 | this.addBytes(0);
|
7597 |
|
7598 | var durationSeconds = (this._now() - this._lastCheckpoint) / 1000;
|
7599 | if (durationSeconds == 0) durationSeconds = 1;
|
7600 | return this._intervalBytes / durationSeconds / 1024;
|
7601 | }
|
7602 | }, {
|
7603 | key: "lastSecondKBps",
|
7604 | get: function get() {
|
7605 | this.addBytes(0);
|
7606 |
|
7607 | if (this._lastSecondBytes !== 0) {
|
7608 | return this._lastSecondBytes / 1024;
|
7609 | } else {
|
7610 | // lastSecondBytes === 0
|
7611 | if (this._now() - this._lastCheckpoint >= 500) {
|
7612 | // if time interval since last checkpoint has exceeded 500ms
|
7613 | // the speed is nearly accurate
|
7614 | return this.currentKBps;
|
7615 | } else {
|
7616 | // We don't know
|
7617 | return 0;
|
7618 | }
|
7619 | }
|
7620 | }
|
7621 | }, {
|
7622 | key: "averageKBps",
|
7623 | get: function get() {
|
7624 | var durationSeconds = (this._now() - this._firstCheckpoint) / 1000;
|
7625 | return this._totalBytes / durationSeconds / 1024;
|
7626 | }
|
7627 | }]);
|
7628 |
|
7629 | return SpeedSampler;
|
7630 | }();
|
7631 |
|
7632 | exports.default = SpeedSampler;
|
7633 |
|
7634 | },{}],28:[function(_dereq_,module,exports){
|
7635 | ;
|
7636 |
|
7637 | Object.defineProperty(exports, "__esModule", {
|
7638 | value: true
|
7639 | });
|
7640 |
|
7641 | 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); } };
|
7642 |
|
7643 | 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; }; }();
|
7644 |
|
7645 | var _logger = _dereq_('../utils/logger.js');
|
7646 |
|
7647 | var _logger2 = _interopRequireDefault(_logger);
|
7648 |
|
7649 | var _loader = _dereq_('./loader.js');
|
7650 |
|
7651 | var _exception = _dereq_('../utils/exception.js');
|
7652 |
|
7653 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
7654 |
|
7655 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
7656 |
|
7657 | 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; }
|
7658 |
|
7659 | 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; } /*
|
7660 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
7661 | *
|
7662 | * @author zheng qian <xqq@xqq.im>
|
7663 | *
|
7664 | * Licensed under the Apache License, Version 2.0 (the "License");
|
7665 | * you may not use this file except in compliance with the License.
|
7666 | * You may obtain a copy of the License at
|
7667 | *
|
7668 | * http://www.apache.org/licenses/LICENSE-2.0
|
7669 | *
|
7670 | * Unless required by applicable law or agreed to in writing, software
|
7671 | * distributed under the License is distributed on an "AS IS" BASIS,
|
7672 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
7673 | * See the License for the specific language governing permissions and
|
7674 | * limitations under the License.
|
7675 | */
|
7676 |
|
7677 | // For FLV over WebSocket live stream
|
7678 | var WebSocketLoader = function (_BaseLoader) {
|
7679 | _inherits(WebSocketLoader, _BaseLoader);
|
7680 |
|
7681 | _createClass(WebSocketLoader, null, [{
|
7682 | key: 'isSupported',
|
7683 | value: function isSupported() {
|
7684 | try {
|
7685 | return typeof self.WebSocket !== 'undefined';
|
7686 | } catch (e) {
|
7687 | return false;
|
7688 | }
|
7689 | }
|
7690 | }]);
|
7691 |
|
7692 | function WebSocketLoader() {
|
7693 | _classCallCheck(this, WebSocketLoader);
|
7694 |
|
7695 | var _this = _possibleConstructorReturn(this, (WebSocketLoader.__proto__ || Object.getPrototypeOf(WebSocketLoader)).call(this, 'websocket-loader'));
|
7696 |
|
7697 | _this.TAG = 'WebSocketLoader';
|
7698 |
|
7699 | _this._needStash = true;
|
7700 |
|
7701 | _this._ws = null;
|
7702 | _this._requestAbort = false;
|
7703 | _this._receivedLength = 0;
|
7704 | return _this;
|
7705 | }
|
7706 |
|
7707 | _createClass(WebSocketLoader, [{
|
7708 | key: 'destroy',
|
7709 | value: function destroy() {
|
7710 | if (this._ws) {
|
7711 | this.abort();
|
7712 | }
|
7713 | _get(WebSocketLoader.prototype.__proto__ || Object.getPrototypeOf(WebSocketLoader.prototype), 'destroy', this).call(this);
|
7714 | }
|
7715 | }, {
|
7716 | key: 'open',
|
7717 | value: function open(dataSource) {
|
7718 | try {
|
7719 | var ws = this._ws = new self.WebSocket(dataSource.url);
|
7720 | ws.binaryType = 'arraybuffer';
|
7721 | ws.onopen = this._onWebSocketOpen.bind(this);
|
7722 | ws.onclose = this._onWebSocketClose.bind(this);
|
7723 | ws.onmessage = this._onWebSocketMessage.bind(this);
|
7724 | ws.onerror = this._onWebSocketError.bind(this);
|
7725 |
|
7726 | this._status = _loader.LoaderStatus.kConnecting;
|
7727 | } catch (e) {
|
7728 | this._status = _loader.LoaderStatus.kError;
|
7729 |
|
7730 | var info = { code: e.code, msg: e.message };
|
7731 |
|
7732 | if (this._onError) {
|
7733 | this._onError(_loader.LoaderErrors.EXCEPTION, info);
|
7734 | } else {
|
7735 | throw new _exception.RuntimeException(info.msg);
|
7736 | }
|
7737 | }
|
7738 | }
|
7739 | }, {
|
7740 | key: 'abort',
|
7741 | value: function abort() {
|
7742 | var ws = this._ws;
|
7743 | if (ws && (ws.readyState === 0 || ws.readyState === 1)) {
|
7744 | // CONNECTING || OPEN
|
7745 | this._requestAbort = true;
|
7746 | ws.close();
|
7747 | }
|
7748 |
|
7749 | this._ws = null;
|
7750 | this._status = _loader.LoaderStatus.kComplete;
|
7751 | }
|
7752 | }, {
|
7753 | key: '_onWebSocketOpen',
|
7754 | value: function _onWebSocketOpen(e) {
|
7755 | this._status = _loader.LoaderStatus.kBuffering;
|
7756 | }
|
7757 | }, {
|
7758 | key: '_onWebSocketClose',
|
7759 | value: function _onWebSocketClose(e) {
|
7760 | if (this._requestAbort === true) {
|
7761 | this._requestAbort = false;
|
7762 | return;
|
7763 | }
|
7764 |
|
7765 | this._status = _loader.LoaderStatus.kComplete;
|
7766 |
|
7767 | if (this._onComplete) {
|
7768 | this._onComplete(0, this._receivedLength - 1);
|
7769 | }
|
7770 | }
|
7771 | }, {
|
7772 | key: '_onWebSocketMessage',
|
7773 | value: function _onWebSocketMessage(e) {
|
7774 | var _this2 = this;
|
7775 |
|
7776 | if (e.data instanceof ArrayBuffer) {
|
7777 | this._dispatchArrayBuffer(e.data);
|
7778 | } else if (e.data instanceof Blob) {
|
7779 | var reader = new FileReader();
|
7780 | reader.onload = function () {
|
7781 | _this2._dispatchArrayBuffer(reader.result);
|
7782 | };
|
7783 | reader.readAsArrayBuffer(e.data);
|
7784 | } else {
|
7785 | this._status = _loader.LoaderStatus.kError;
|
7786 | var info = { code: -1, msg: 'Unsupported WebSocket message type: ' + e.data.constructor.name };
|
7787 |
|
7788 | if (this._onError) {
|
7789 | this._onError(_loader.LoaderErrors.EXCEPTION, info);
|
7790 | } else {
|
7791 | throw new _exception.RuntimeException(info.msg);
|
7792 | }
|
7793 | }
|
7794 | }
|
7795 | }, {
|
7796 | key: '_dispatchArrayBuffer',
|
7797 | value: function _dispatchArrayBuffer(arraybuffer) {
|
7798 | var chunk = arraybuffer;
|
7799 | var byteStart = this._receivedLength;
|
7800 | this._receivedLength += chunk.byteLength;
|
7801 |
|
7802 | if (this._onDataArrival) {
|
7803 | this._onDataArrival(chunk, byteStart, this._receivedLength);
|
7804 | }
|
7805 | }
|
7806 | }, {
|
7807 | key: '_onWebSocketError',
|
7808 | value: function _onWebSocketError(e) {
|
7809 | this._status = _loader.LoaderStatus.kError;
|
7810 |
|
7811 | var info = {
|
7812 | code: e.code,
|
7813 | msg: e.message
|
7814 | };
|
7815 |
|
7816 | if (this._onError) {
|
7817 | this._onError(_loader.LoaderErrors.EXCEPTION, info);
|
7818 | } else {
|
7819 | throw new _exception.RuntimeException(info.msg);
|
7820 | }
|
7821 | }
|
7822 | }]);
|
7823 |
|
7824 | return WebSocketLoader;
|
7825 | }(_loader.BaseLoader);
|
7826 |
|
7827 | exports.default = WebSocketLoader;
|
7828 |
|
7829 | },{"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24}],29:[function(_dereq_,module,exports){
|
7830 | ;
|
7831 |
|
7832 | Object.defineProperty(exports, "__esModule", {
|
7833 | value: true
|
7834 | });
|
7835 |
|
7836 | 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; };
|
7837 |
|
7838 | 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); } };
|
7839 |
|
7840 | 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; }; }();
|
7841 |
|
7842 | var _logger = _dereq_('../utils/logger.js');
|
7843 |
|
7844 | var _logger2 = _interopRequireDefault(_logger);
|
7845 |
|
7846 | var _loader = _dereq_('./loader.js');
|
7847 |
|
7848 | var _exception = _dereq_('../utils/exception.js');
|
7849 |
|
7850 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
7851 |
|
7852 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
7853 |
|
7854 | 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; }
|
7855 |
|
7856 | 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; } /*
|
7857 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
7858 | *
|
7859 | * @author zheng qian <xqq@xqq.im>
|
7860 | *
|
7861 | * Licensed under the Apache License, Version 2.0 (the "License");
|
7862 | * you may not use this file except in compliance with the License.
|
7863 | * You may obtain a copy of the License at
|
7864 | *
|
7865 | * http://www.apache.org/licenses/LICENSE-2.0
|
7866 | *
|
7867 | * Unless required by applicable law or agreed to in writing, software
|
7868 | * distributed under the License is distributed on an "AS IS" BASIS,
|
7869 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
7870 | * See the License for the specific language governing permissions and
|
7871 | * limitations under the License.
|
7872 | */
|
7873 |
|
7874 | // For FireFox browser which supports `xhr.responseType = 'moz-chunked-arraybuffer'`
|
7875 | var MozChunkedLoader = function (_BaseLoader) {
|
7876 | _inherits(MozChunkedLoader, _BaseLoader);
|
7877 |
|
7878 | _createClass(MozChunkedLoader, null, [{
|
7879 | key: 'isSupported',
|
7880 | value: function isSupported() {
|
7881 | try {
|
7882 | var xhr = new XMLHttpRequest();
|
7883 | // Firefox 37- requires .open() to be called before setting responseType
|
7884 | xhr.open('GET', 'https://example.com', true);
|
7885 | xhr.responseType = 'moz-chunked-arraybuffer';
|
7886 | return xhr.responseType === 'moz-chunked-arraybuffer';
|
7887 | } catch (e) {
|
7888 | _logger2.default.w('MozChunkedLoader', e.message);
|
7889 | return false;
|
7890 | }
|
7891 | }
|
7892 | }]);
|
7893 |
|
7894 | function MozChunkedLoader(seekHandler, config) {
|
7895 | _classCallCheck(this, MozChunkedLoader);
|
7896 |
|
7897 | var _this = _possibleConstructorReturn(this, (MozChunkedLoader.__proto__ || Object.getPrototypeOf(MozChunkedLoader)).call(this, 'xhr-moz-chunked-loader'));
|
7898 |
|
7899 | _this.TAG = 'MozChunkedLoader';
|
7900 |
|
7901 | _this._seekHandler = seekHandler;
|
7902 | _this._config = config;
|
7903 | _this._needStash = true;
|
7904 |
|
7905 | _this._xhr = null;
|
7906 | _this._requestAbort = false;
|
7907 | _this._contentLength = null;
|
7908 | _this._receivedLength = 0;
|
7909 | return _this;
|
7910 | }
|
7911 |
|
7912 | _createClass(MozChunkedLoader, [{
|
7913 | key: 'destroy',
|
7914 | value: function destroy() {
|
7915 | if (this.isWorking()) {
|
7916 | this.abort();
|
7917 | }
|
7918 | if (this._xhr) {
|
7919 | this._xhr.onreadystatechange = null;
|
7920 | this._xhr.onprogress = null;
|
7921 | this._xhr.onloadend = null;
|
7922 | this._xhr.onerror = null;
|
7923 | this._xhr = null;
|
7924 | }
|
7925 | _get(MozChunkedLoader.prototype.__proto__ || Object.getPrototypeOf(MozChunkedLoader.prototype), 'destroy', this).call(this);
|
7926 | }
|
7927 | }, {
|
7928 | key: 'open',
|
7929 | value: function open(dataSource, range) {
|
7930 | this._dataSource = dataSource;
|
7931 | this._range = range;
|
7932 |
|
7933 | var sourceURL = dataSource.url;
|
7934 | if (this._config.reuseRedirectedURL && dataSource.redirectedURL != undefined) {
|
7935 | sourceURL = dataSource.redirectedURL;
|
7936 | }
|
7937 |
|
7938 | var seekConfig = this._seekHandler.getConfig(sourceURL, range);
|
7939 | this._requestURL = seekConfig.url;
|
7940 |
|
7941 | var xhr = this._xhr = new XMLHttpRequest();
|
7942 | xhr.open('GET', seekConfig.url, true);
|
7943 | xhr.responseType = 'moz-chunked-arraybuffer';
|
7944 | xhr.onreadystatechange = this._onReadyStateChange.bind(this);
|
7945 | xhr.onprogress = this._onProgress.bind(this);
|
7946 | xhr.onloadend = this._onLoadEnd.bind(this);
|
7947 | xhr.onerror = this._onXhrError.bind(this);
|
7948 |
|
7949 | // cors is auto detected and enabled by xhr
|
7950 |
|
7951 | // withCredentials is disabled by default
|
7952 | if (dataSource.withCredentials) {
|
7953 | xhr.withCredentials = true;
|
7954 | }
|
7955 |
|
7956 | if (_typeof(seekConfig.headers) === 'object') {
|
7957 | var headers = seekConfig.headers;
|
7958 |
|
7959 | for (var key in headers) {
|
7960 | if (headers.hasOwnProperty(key)) {
|
7961 | xhr.setRequestHeader(key, headers[key]);
|
7962 | }
|
7963 | }
|
7964 | }
|
7965 |
|
7966 | // add additional headers
|
7967 | if (_typeof(this._config.headers) === 'object') {
|
7968 | var _headers = this._config.headers;
|
7969 |
|
7970 | for (var _key in _headers) {
|
7971 | if (_headers.hasOwnProperty(_key)) {
|
7972 | xhr.setRequestHeader(_key, _headers[_key]);
|
7973 | }
|
7974 | }
|
7975 | }
|
7976 |
|
7977 | this._status = _loader.LoaderStatus.kConnecting;
|
7978 | xhr.send();
|
7979 | }
|
7980 | }, {
|
7981 | key: 'abort',
|
7982 | value: function abort() {
|
7983 | this._requestAbort = true;
|
7984 | if (this._xhr) {
|
7985 | this._xhr.abort();
|
7986 | }
|
7987 | this._status = _loader.LoaderStatus.kComplete;
|
7988 | }
|
7989 | }, {
|
7990 | key: '_onReadyStateChange',
|
7991 | value: function _onReadyStateChange(e) {
|
7992 | var xhr = e.target;
|
7993 |
|
7994 | if (xhr.readyState === 2) {
|
7995 | // HEADERS_RECEIVED
|
7996 | if (xhr.responseURL != undefined && xhr.responseURL !== this._requestURL) {
|
7997 | if (this._onURLRedirect) {
|
7998 | var redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL);
|
7999 | this._onURLRedirect(redirectedURL);
|
8000 | }
|
8001 | }
|
8002 |
|
8003 | if (xhr.status !== 0 && (xhr.status < 200 || xhr.status > 299)) {
|
8004 | this._status = _loader.LoaderStatus.kError;
|
8005 | if (this._onError) {
|
8006 | this._onError(_loader.LoaderErrors.HTTP_STATUS_CODE_INVALID, { code: xhr.status, msg: xhr.statusText });
|
8007 | } else {
|
8008 | throw new _exception.RuntimeException('MozChunkedLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText);
|
8009 | }
|
8010 | } else {
|
8011 | this._status = _loader.LoaderStatus.kBuffering;
|
8012 | }
|
8013 | }
|
8014 | }
|
8015 | }, {
|
8016 | key: '_onProgress',
|
8017 | value: function _onProgress(e) {
|
8018 | if (this._status === _loader.LoaderStatus.kError) {
|
8019 | // Ignore error response
|
8020 | return;
|
8021 | }
|
8022 |
|
8023 | if (this._contentLength === null) {
|
8024 | if (e.total !== null && e.total !== 0) {
|
8025 | this._contentLength = e.total;
|
8026 | if (this._onContentLengthKnown) {
|
8027 | this._onContentLengthKnown(this._contentLength);
|
8028 | }
|
8029 | }
|
8030 | }
|
8031 |
|
8032 | var chunk = e.target.response;
|
8033 | var byteStart = this._range.from + this._receivedLength;
|
8034 | this._receivedLength += chunk.byteLength;
|
8035 |
|
8036 | if (this._onDataArrival) {
|
8037 | this._onDataArrival(chunk, byteStart, this._receivedLength);
|
8038 | }
|
8039 | }
|
8040 | }, {
|
8041 | key: '_onLoadEnd',
|
8042 | value: function _onLoadEnd(e) {
|
8043 | if (this._requestAbort === true) {
|
8044 | this._requestAbort = false;
|
8045 | return;
|
8046 | } else if (this._status === _loader.LoaderStatus.kError) {
|
8047 | return;
|
8048 | }
|
8049 |
|
8050 | this._status = _loader.LoaderStatus.kComplete;
|
8051 | if (this._onComplete) {
|
8052 | this._onComplete(this._range.from, this._range.from + this._receivedLength - 1);
|
8053 | }
|
8054 | }
|
8055 | }, {
|
8056 | key: '_onXhrError',
|
8057 | value: function _onXhrError(e) {
|
8058 | this._status = _loader.LoaderStatus.kError;
|
8059 | var type = 0;
|
8060 | var info = null;
|
8061 |
|
8062 | if (this._contentLength && e.loaded < this._contentLength) {
|
8063 | type = _loader.LoaderErrors.EARLY_EOF;
|
8064 | info = { code: -1, msg: 'Moz-Chunked stream meet Early-Eof' };
|
8065 | } else {
|
8066 | type = _loader.LoaderErrors.EXCEPTION;
|
8067 | info = { code: -1, msg: e.constructor.name + ' ' + e.type };
|
8068 | }
|
8069 |
|
8070 | if (this._onError) {
|
8071 | this._onError(type, info);
|
8072 | } else {
|
8073 | throw new _exception.RuntimeException(info.msg);
|
8074 | }
|
8075 | }
|
8076 | }]);
|
8077 |
|
8078 | return MozChunkedLoader;
|
8079 | }(_loader.BaseLoader);
|
8080 |
|
8081 | exports.default = MozChunkedLoader;
|
8082 |
|
8083 | },{"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24}],30:[function(_dereq_,module,exports){
|
8084 | ;
|
8085 |
|
8086 | Object.defineProperty(exports, "__esModule", {
|
8087 | value: true
|
8088 | });
|
8089 |
|
8090 | 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; };
|
8091 |
|
8092 | 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); } };
|
8093 |
|
8094 | 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; }; }();
|
8095 |
|
8096 | var _logger = _dereq_('../utils/logger.js');
|
8097 |
|
8098 | var _logger2 = _interopRequireDefault(_logger);
|
8099 |
|
8100 | var _loader = _dereq_('./loader.js');
|
8101 |
|
8102 | var _exception = _dereq_('../utils/exception.js');
|
8103 |
|
8104 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
8105 |
|
8106 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
8107 |
|
8108 | 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; }
|
8109 |
|
8110 | 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; } /*
|
8111 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
8112 | *
|
8113 | * @author zheng qian <xqq@xqq.im>
|
8114 | *
|
8115 | * Licensed under the Apache License, Version 2.0 (the "License");
|
8116 | * you may not use this file except in compliance with the License.
|
8117 | * You may obtain a copy of the License at
|
8118 | *
|
8119 | * http://www.apache.org/licenses/LICENSE-2.0
|
8120 | *
|
8121 | * Unless required by applicable law or agreed to in writing, software
|
8122 | * distributed under the License is distributed on an "AS IS" BASIS,
|
8123 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
8124 | * See the License for the specific language governing permissions and
|
8125 | * limitations under the License.
|
8126 | */
|
8127 |
|
8128 | /* Notice: ms-stream may cause IE/Edge browser crash if seek too frequently!!!
|
8129 | * The browser may crash in wininet.dll. Disable for now.
|
8130 | *
|
8131 | * For IE11/Edge browser by microsoft which supports `xhr.responseType = 'ms-stream'`
|
8132 | * Notice that ms-stream API sucks. The buffer is always expanding along with downloading.
|
8133 | *
|
8134 | * We need to abort the xhr if buffer size exceeded limit size (e.g. 16 MiB), then do reconnect.
|
8135 | * in order to release previous ArrayBuffer to avoid memory leak
|
8136 | *
|
8137 | * Otherwise, the ArrayBuffer will increase to a terrible size that equals final file size.
|
8138 | */
|
8139 | var MSStreamLoader = function (_BaseLoader) {
|
8140 | _inherits(MSStreamLoader, _BaseLoader);
|
8141 |
|
8142 | _createClass(MSStreamLoader, null, [{
|
8143 | key: 'isSupported',
|
8144 | value: function isSupported() {
|
8145 | try {
|
8146 | if (typeof self.MSStream === 'undefined' || typeof self.MSStreamReader === 'undefined') {
|
8147 | return false;
|
8148 | }
|
8149 |
|
8150 | var xhr = new XMLHttpRequest();
|
8151 | xhr.open('GET', 'https://example.com', true);
|
8152 | xhr.responseType = 'ms-stream';
|
8153 | return xhr.responseType === 'ms-stream';
|
8154 | } catch (e) {
|
8155 | _logger2.default.w('MSStreamLoader', e.message);
|
8156 | return false;
|
8157 | }
|
8158 | }
|
8159 | }]);
|
8160 |
|
8161 | function MSStreamLoader(seekHandler, config) {
|
8162 | _classCallCheck(this, MSStreamLoader);
|
8163 |
|
8164 | var _this = _possibleConstructorReturn(this, (MSStreamLoader.__proto__ || Object.getPrototypeOf(MSStreamLoader)).call(this, 'xhr-msstream-loader'));
|
8165 |
|
8166 | _this.TAG = 'MSStreamLoader';
|
8167 |
|
8168 | _this._seekHandler = seekHandler;
|
8169 | _this._config = config;
|
8170 | _this._needStash = true;
|
8171 |
|
8172 | _this._xhr = null;
|
8173 | _this._reader = null; // MSStreamReader
|
8174 |
|
8175 | _this._totalRange = null;
|
8176 | _this._currentRange = null;
|
8177 |
|
8178 | _this._currentRequestURL = null;
|
8179 | _this._currentRedirectedURL = null;
|
8180 |
|
8181 | _this._contentLength = null;
|
8182 | _this._receivedLength = 0;
|
8183 |
|
8184 | _this._bufferLimit = 16 * 1024 * 1024; // 16MB
|
8185 | _this._lastTimeBufferSize = 0;
|
8186 | _this._isReconnecting = false;
|
8187 | return _this;
|
8188 | }
|
8189 |
|
8190 | _createClass(MSStreamLoader, [{
|
8191 | key: 'destroy',
|
8192 | value: function destroy() {
|
8193 | if (this.isWorking()) {
|
8194 | this.abort();
|
8195 | }
|
8196 | if (this._reader) {
|
8197 | this._reader.onprogress = null;
|
8198 | this._reader.onload = null;
|
8199 | this._reader.onerror = null;
|
8200 | this._reader = null;
|
8201 | }
|
8202 | if (this._xhr) {
|
8203 | this._xhr.onreadystatechange = null;
|
8204 | this._xhr = null;
|
8205 | }
|
8206 | _get(MSStreamLoader.prototype.__proto__ || Object.getPrototypeOf(MSStreamLoader.prototype), 'destroy', this).call(this);
|
8207 | }
|
8208 | }, {
|
8209 | key: 'open',
|
8210 | value: function open(dataSource, range) {
|
8211 | this._internalOpen(dataSource, range, false);
|
8212 | }
|
8213 | }, {
|
8214 | key: '_internalOpen',
|
8215 | value: function _internalOpen(dataSource, range, isSubrange) {
|
8216 | this._dataSource = dataSource;
|
8217 |
|
8218 | if (!isSubrange) {
|
8219 | this._totalRange = range;
|
8220 | } else {
|
8221 | this._currentRange = range;
|
8222 | }
|
8223 |
|
8224 | var sourceURL = dataSource.url;
|
8225 | if (this._config.reuseRedirectedURL) {
|
8226 | if (this._currentRedirectedURL != undefined) {
|
8227 | sourceURL = this._currentRedirectedURL;
|
8228 | } else if (dataSource.redirectedURL != undefined) {
|
8229 | sourceURL = dataSource.redirectedURL;
|
8230 | }
|
8231 | }
|
8232 |
|
8233 | var seekConfig = this._seekHandler.getConfig(sourceURL, range);
|
8234 | this._currentRequestURL = seekConfig.url;
|
8235 |
|
8236 | var reader = this._reader = new self.MSStreamReader();
|
8237 | reader.onprogress = this._msrOnProgress.bind(this);
|
8238 | reader.onload = this._msrOnLoad.bind(this);
|
8239 | reader.onerror = this._msrOnError.bind(this);
|
8240 |
|
8241 | var xhr = this._xhr = new XMLHttpRequest();
|
8242 | xhr.open('GET', seekConfig.url, true);
|
8243 | xhr.responseType = 'ms-stream';
|
8244 | xhr.onreadystatechange = this._xhrOnReadyStateChange.bind(this);
|
8245 | xhr.onerror = this._xhrOnError.bind(this);
|
8246 |
|
8247 | if (dataSource.withCredentials) {
|
8248 | xhr.withCredentials = true;
|
8249 | }
|
8250 |
|
8251 | if (_typeof(seekConfig.headers) === 'object') {
|
8252 | var headers = seekConfig.headers;
|
8253 |
|
8254 | for (var key in headers) {
|
8255 | if (headers.hasOwnProperty(key)) {
|
8256 | xhr.setRequestHeader(key, headers[key]);
|
8257 | }
|
8258 | }
|
8259 | }
|
8260 |
|
8261 | // add additional headers
|
8262 | if (_typeof(this._config.headers) === 'object') {
|
8263 | var _headers = this._config.headers;
|
8264 |
|
8265 | for (var _key in _headers) {
|
8266 | if (_headers.hasOwnProperty(_key)) {
|
8267 | xhr.setRequestHeader(_key, _headers[_key]);
|
8268 | }
|
8269 | }
|
8270 | }
|
8271 |
|
8272 | if (this._isReconnecting) {
|
8273 | this._isReconnecting = false;
|
8274 | } else {
|
8275 | this._status = _loader.LoaderStatus.kConnecting;
|
8276 | }
|
8277 | xhr.send();
|
8278 | }
|
8279 | }, {
|
8280 | key: 'abort',
|
8281 | value: function abort() {
|
8282 | this._internalAbort();
|
8283 | this._status = _loader.LoaderStatus.kComplete;
|
8284 | }
|
8285 | }, {
|
8286 | key: '_internalAbort',
|
8287 | value: function _internalAbort() {
|
8288 | if (this._reader) {
|
8289 | if (this._reader.readyState === 1) {
|
8290 | // LOADING
|
8291 | this._reader.abort();
|
8292 | }
|
8293 | this._reader.onprogress = null;
|
8294 | this._reader.onload = null;
|
8295 | this._reader.onerror = null;
|
8296 | this._reader = null;
|
8297 | }
|
8298 | if (this._xhr) {
|
8299 | this._xhr.abort();
|
8300 | this._xhr.onreadystatechange = null;
|
8301 | this._xhr = null;
|
8302 | }
|
8303 | }
|
8304 | }, {
|
8305 | key: '_xhrOnReadyStateChange',
|
8306 | value: function _xhrOnReadyStateChange(e) {
|
8307 | var xhr = e.target;
|
8308 |
|
8309 | if (xhr.readyState === 2) {
|
8310 | // HEADERS_RECEIVED
|
8311 | if (xhr.status >= 200 && xhr.status <= 299) {
|
8312 | this._status = _loader.LoaderStatus.kBuffering;
|
8313 |
|
8314 | if (xhr.responseURL != undefined) {
|
8315 | var redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL);
|
8316 | if (xhr.responseURL !== this._currentRequestURL && redirectedURL !== this._currentRedirectedURL) {
|
8317 | this._currentRedirectedURL = redirectedURL;
|
8318 | if (this._onURLRedirect) {
|
8319 | this._onURLRedirect(redirectedURL);
|
8320 | }
|
8321 | }
|
8322 | }
|
8323 |
|
8324 | var lengthHeader = xhr.getResponseHeader('Content-Length');
|
8325 | if (lengthHeader != null && this._contentLength == null) {
|
8326 | var length = parseInt(lengthHeader);
|
8327 | if (length > 0) {
|
8328 | this._contentLength = length;
|
8329 | if (this._onContentLengthKnown) {
|
8330 | this._onContentLengthKnown(this._contentLength);
|
8331 | }
|
8332 | }
|
8333 | }
|
8334 | } else {
|
8335 | this._status = _loader.LoaderStatus.kError;
|
8336 | if (this._onError) {
|
8337 | this._onError(_loader.LoaderErrors.HTTP_STATUS_CODE_INVALID, { code: xhr.status, msg: xhr.statusText });
|
8338 | } else {
|
8339 | throw new _exception.RuntimeException('MSStreamLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText);
|
8340 | }
|
8341 | }
|
8342 | } else if (xhr.readyState === 3) {
|
8343 | // LOADING
|
8344 | if (xhr.status >= 200 && xhr.status <= 299) {
|
8345 | this._status = _loader.LoaderStatus.kBuffering;
|
8346 |
|
8347 | var msstream = xhr.response;
|
8348 | this._reader.readAsArrayBuffer(msstream);
|
8349 | }
|
8350 | }
|
8351 | }
|
8352 | }, {
|
8353 | key: '_xhrOnError',
|
8354 | value: function _xhrOnError(e) {
|
8355 | this._status = _loader.LoaderStatus.kError;
|
8356 | var type = _loader.LoaderErrors.EXCEPTION;
|
8357 | var info = { code: -1, msg: e.constructor.name + ' ' + e.type };
|
8358 |
|
8359 | if (this._onError) {
|
8360 | this._onError(type, info);
|
8361 | } else {
|
8362 | throw new _exception.RuntimeException(info.msg);
|
8363 | }
|
8364 | }
|
8365 | }, {
|
8366 | key: '_msrOnProgress',
|
8367 | value: function _msrOnProgress(e) {
|
8368 | var reader = e.target;
|
8369 | var bigbuffer = reader.result;
|
8370 | if (bigbuffer == null) {
|
8371 | // result may be null, workaround for buggy M$
|
8372 | this._doReconnectIfNeeded();
|
8373 | return;
|
8374 | }
|
8375 |
|
8376 | var slice = bigbuffer.slice(this._lastTimeBufferSize);
|
8377 | this._lastTimeBufferSize = bigbuffer.byteLength;
|
8378 | var byteStart = this._totalRange.from + this._receivedLength;
|
8379 | this._receivedLength += slice.byteLength;
|
8380 |
|
8381 | if (this._onDataArrival) {
|
8382 | this._onDataArrival(slice, byteStart, this._receivedLength);
|
8383 | }
|
8384 |
|
8385 | if (bigbuffer.byteLength >= this._bufferLimit) {
|
8386 | _logger2.default.v(this.TAG, 'MSStream buffer exceeded max size near ' + (byteStart + slice.byteLength) + ', reconnecting...');
|
8387 | this._doReconnectIfNeeded();
|
8388 | }
|
8389 | }
|
8390 | }, {
|
8391 | key: '_doReconnectIfNeeded',
|
8392 | value: function _doReconnectIfNeeded() {
|
8393 | if (this._contentLength == null || this._receivedLength < this._contentLength) {
|
8394 | this._isReconnecting = true;
|
8395 | this._lastTimeBufferSize = 0;
|
8396 | this._internalAbort();
|
8397 |
|
8398 | var range = {
|
8399 | from: this._totalRange.from + this._receivedLength,
|
8400 | to: -1
|
8401 | };
|
8402 | this._internalOpen(this._dataSource, range, true);
|
8403 | }
|
8404 | }
|
8405 | }, {
|
8406 | key: '_msrOnLoad',
|
8407 | value: function _msrOnLoad(e) {
|
8408 | // actually it is onComplete event
|
8409 | this._status = _loader.LoaderStatus.kComplete;
|
8410 | if (this._onComplete) {
|
8411 | this._onComplete(this._totalRange.from, this._totalRange.from + this._receivedLength - 1);
|
8412 | }
|
8413 | }
|
8414 | }, {
|
8415 | key: '_msrOnError',
|
8416 | value: function _msrOnError(e) {
|
8417 | this._status = _loader.LoaderStatus.kError;
|
8418 | var type = 0;
|
8419 | var info = null;
|
8420 |
|
8421 | if (this._contentLength && this._receivedLength < this._contentLength) {
|
8422 | type = _loader.LoaderErrors.EARLY_EOF;
|
8423 | info = { code: -1, msg: 'MSStream meet Early-Eof' };
|
8424 | } else {
|
8425 | type = _loader.LoaderErrors.EARLY_EOF;
|
8426 | info = { code: -1, msg: e.constructor.name + ' ' + e.type };
|
8427 | }
|
8428 |
|
8429 | if (this._onError) {
|
8430 | this._onError(type, info);
|
8431 | } else {
|
8432 | throw new _exception.RuntimeException(info.msg);
|
8433 | }
|
8434 | }
|
8435 | }]);
|
8436 |
|
8437 | return MSStreamLoader;
|
8438 | }(_loader.BaseLoader);
|
8439 |
|
8440 | exports.default = MSStreamLoader;
|
8441 |
|
8442 | },{"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24}],31:[function(_dereq_,module,exports){
|
8443 | ;
|
8444 |
|
8445 | Object.defineProperty(exports, "__esModule", {
|
8446 | value: true
|
8447 | });
|
8448 |
|
8449 | 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; };
|
8450 |
|
8451 | 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); } };
|
8452 |
|
8453 | 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; }; }();
|
8454 |
|
8455 | var _logger = _dereq_('../utils/logger.js');
|
8456 |
|
8457 | var _logger2 = _interopRequireDefault(_logger);
|
8458 |
|
8459 | var _speedSampler = _dereq_('./speed-sampler.js');
|
8460 |
|
8461 | var _speedSampler2 = _interopRequireDefault(_speedSampler);
|
8462 |
|
8463 | var _loader = _dereq_('./loader.js');
|
8464 |
|
8465 | var _exception = _dereq_('../utils/exception.js');
|
8466 |
|
8467 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
8468 |
|
8469 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
8470 |
|
8471 | 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; }
|
8472 |
|
8473 | 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; } /*
|
8474 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
8475 | *
|
8476 | * @author zheng qian <xqq@xqq.im>
|
8477 | *
|
8478 | * Licensed under the Apache License, Version 2.0 (the "License");
|
8479 | * you may not use this file except in compliance with the License.
|
8480 | * You may obtain a copy of the License at
|
8481 | *
|
8482 | * http://www.apache.org/licenses/LICENSE-2.0
|
8483 | *
|
8484 | * Unless required by applicable law or agreed to in writing, software
|
8485 | * distributed under the License is distributed on an "AS IS" BASIS,
|
8486 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
8487 | * See the License for the specific language governing permissions and
|
8488 | * limitations under the License.
|
8489 | */
|
8490 |
|
8491 | // Universal IO Loader, implemented by adding Range header in xhr's request header
|
8492 | var RangeLoader = function (_BaseLoader) {
|
8493 | _inherits(RangeLoader, _BaseLoader);
|
8494 |
|
8495 | _createClass(RangeLoader, null, [{
|
8496 | key: 'isSupported',
|
8497 | value: function isSupported() {
|
8498 | try {
|
8499 | var xhr = new XMLHttpRequest();
|
8500 | xhr.open('GET', 'https://example.com', true);
|
8501 | xhr.responseType = 'arraybuffer';
|
8502 | return xhr.responseType === 'arraybuffer';
|
8503 | } catch (e) {
|
8504 | _logger2.default.w('RangeLoader', e.message);
|
8505 | return false;
|
8506 | }
|
8507 | }
|
8508 | }]);
|
8509 |
|
8510 | function RangeLoader(seekHandler, config) {
|
8511 | _classCallCheck(this, RangeLoader);
|
8512 |
|
8513 | var _this = _possibleConstructorReturn(this, (RangeLoader.__proto__ || Object.getPrototypeOf(RangeLoader)).call(this, 'xhr-range-loader'));
|
8514 |
|
8515 | _this.TAG = 'RangeLoader';
|
8516 |
|
8517 | _this._seekHandler = seekHandler;
|
8518 | _this._config = config;
|
8519 | _this._needStash = false;
|
8520 |
|
8521 | _this._chunkSizeKBList = [128, 256, 384, 512, 768, 1024, 1536, 2048, 3072, 4096, 5120, 6144, 7168, 8192];
|
8522 | _this._currentChunkSizeKB = 384;
|
8523 | _this._currentSpeedNormalized = 0;
|
8524 | _this._zeroSpeedChunkCount = 0;
|
8525 |
|
8526 | _this._xhr = null;
|
8527 | _this._speedSampler = new _speedSampler2.default();
|
8528 |
|
8529 | _this._requestAbort = false;
|
8530 | _this._waitForTotalLength = false;
|
8531 | _this._totalLengthReceived = false;
|
8532 |
|
8533 | _this._currentRequestURL = null;
|
8534 | _this._currentRedirectedURL = null;
|
8535 | _this._currentRequestRange = null;
|
8536 | _this._totalLength = null; // size of the entire file
|
8537 | _this._contentLength = null; // Content-Length of entire request range
|
8538 | _this._receivedLength = 0; // total received bytes
|
8539 | _this._lastTimeLoaded = 0; // received bytes of current request sub-range
|
8540 | return _this;
|
8541 | }
|
8542 |
|
8543 | _createClass(RangeLoader, [{
|
8544 | key: 'destroy',
|
8545 | value: function destroy() {
|
8546 | if (this.isWorking()) {
|
8547 | this.abort();
|
8548 | }
|
8549 | if (this._xhr) {
|
8550 | this._xhr.onreadystatechange = null;
|
8551 | this._xhr.onprogress = null;
|
8552 | this._xhr.onload = null;
|
8553 | this._xhr.onerror = null;
|
8554 | this._xhr = null;
|
8555 | }
|
8556 | _get(RangeLoader.prototype.__proto__ || Object.getPrototypeOf(RangeLoader.prototype), 'destroy', this).call(this);
|
8557 | }
|
8558 | }, {
|
8559 | key: 'open',
|
8560 | value: function open(dataSource, range) {
|
8561 | this._dataSource = dataSource;
|
8562 | this._range = range;
|
8563 | this._status = _loader.LoaderStatus.kConnecting;
|
8564 |
|
8565 | var useRefTotalLength = false;
|
8566 | if (this._dataSource.filesize != undefined && this._dataSource.filesize !== 0) {
|
8567 | useRefTotalLength = true;
|
8568 | this._totalLength = this._dataSource.filesize;
|
8569 | }
|
8570 |
|
8571 | if (!this._totalLengthReceived && !useRefTotalLength) {
|
8572 | // We need total filesize
|
8573 | this._waitForTotalLength = true;
|
8574 | this._internalOpen(this._dataSource, { from: 0, to: -1 });
|
8575 | } else {
|
8576 | // We have filesize, start loading
|
8577 | this._openSubRange();
|
8578 | }
|
8579 | }
|
8580 | }, {
|
8581 | key: '_openSubRange',
|
8582 | value: function _openSubRange() {
|
8583 | var chunkSize = this._currentChunkSizeKB * 1024;
|
8584 |
|
8585 | var from = this._range.from + this._receivedLength;
|
8586 | var to = from + chunkSize;
|
8587 |
|
8588 | if (this._contentLength != null) {
|
8589 | if (to - this._range.from >= this._contentLength) {
|
8590 | to = this._range.from + this._contentLength - 1;
|
8591 | }
|
8592 | }
|
8593 |
|
8594 | this._currentRequestRange = { from: from, to: to };
|
8595 | this._internalOpen(this._dataSource, this._currentRequestRange);
|
8596 | }
|
8597 | }, {
|
8598 | key: '_internalOpen',
|
8599 | value: function _internalOpen(dataSource, range) {
|
8600 | this._lastTimeLoaded = 0;
|
8601 |
|
8602 | var sourceURL = dataSource.url;
|
8603 | if (this._config.reuseRedirectedURL) {
|
8604 | if (this._currentRedirectedURL != undefined) {
|
8605 | sourceURL = this._currentRedirectedURL;
|
8606 | } else if (dataSource.redirectedURL != undefined) {
|
8607 | sourceURL = dataSource.redirectedURL;
|
8608 | }
|
8609 | }
|
8610 |
|
8611 | var seekConfig = this._seekHandler.getConfig(sourceURL, range);
|
8612 | this._currentRequestURL = seekConfig.url;
|
8613 |
|
8614 | var xhr = this._xhr = new XMLHttpRequest();
|
8615 | xhr.open('GET', seekConfig.url, true);
|
8616 | xhr.responseType = 'arraybuffer';
|
8617 | xhr.onreadystatechange = this._onReadyStateChange.bind(this);
|
8618 | xhr.onprogress = this._onProgress.bind(this);
|
8619 | xhr.onload = this._onLoad.bind(this);
|
8620 | xhr.onerror = this._onXhrError.bind(this);
|
8621 |
|
8622 | if (dataSource.withCredentials) {
|
8623 | xhr.withCredentials = true;
|
8624 | }
|
8625 |
|
8626 | if (_typeof(seekConfig.headers) === 'object') {
|
8627 | var headers = seekConfig.headers;
|
8628 |
|
8629 | for (var key in headers) {
|
8630 | if (headers.hasOwnProperty(key)) {
|
8631 | xhr.setRequestHeader(key, headers[key]);
|
8632 | }
|
8633 | }
|
8634 | }
|
8635 |
|
8636 | // add additional headers
|
8637 | if (_typeof(this._config.headers) === 'object') {
|
8638 | var _headers = this._config.headers;
|
8639 |
|
8640 | for (var _key in _headers) {
|
8641 | if (_headers.hasOwnProperty(_key)) {
|
8642 | xhr.setRequestHeader(_key, _headers[_key]);
|
8643 | }
|
8644 | }
|
8645 | }
|
8646 |
|
8647 | xhr.send();
|
8648 | }
|
8649 | }, {
|
8650 | key: 'abort',
|
8651 | value: function abort() {
|
8652 | this._requestAbort = true;
|
8653 | this._internalAbort();
|
8654 | this._status = _loader.LoaderStatus.kComplete;
|
8655 | }
|
8656 | }, {
|
8657 | key: '_internalAbort',
|
8658 | value: function _internalAbort() {
|
8659 | if (this._xhr) {
|
8660 | this._xhr.onreadystatechange = null;
|
8661 | this._xhr.onprogress = null;
|
8662 | this._xhr.onload = null;
|
8663 | this._xhr.onerror = null;
|
8664 | this._xhr.abort();
|
8665 | this._xhr = null;
|
8666 | }
|
8667 | }
|
8668 | }, {
|
8669 | key: '_onReadyStateChange',
|
8670 | value: function _onReadyStateChange(e) {
|
8671 | var xhr = e.target;
|
8672 |
|
8673 | if (xhr.readyState === 2) {
|
8674 | // HEADERS_RECEIVED
|
8675 | if (xhr.responseURL != undefined) {
|
8676 | // if the browser support this property
|
8677 | var redirectedURL = this._seekHandler.removeURLParameters(xhr.responseURL);
|
8678 | if (xhr.responseURL !== this._currentRequestURL && redirectedURL !== this._currentRedirectedURL) {
|
8679 | this._currentRedirectedURL = redirectedURL;
|
8680 | if (this._onURLRedirect) {
|
8681 | this._onURLRedirect(redirectedURL);
|
8682 | }
|
8683 | }
|
8684 | }
|
8685 |
|
8686 | if (xhr.status >= 200 && xhr.status <= 299) {
|
8687 | if (this._waitForTotalLength) {
|
8688 | return;
|
8689 | }
|
8690 | this._status = _loader.LoaderStatus.kBuffering;
|
8691 | } else {
|
8692 | this._status = _loader.LoaderStatus.kError;
|
8693 | if (this._onError) {
|
8694 | this._onError(_loader.LoaderErrors.HTTP_STATUS_CODE_INVALID, { code: xhr.status, msg: xhr.statusText });
|
8695 | } else {
|
8696 | throw new _exception.RuntimeException('RangeLoader: Http code invalid, ' + xhr.status + ' ' + xhr.statusText);
|
8697 | }
|
8698 | }
|
8699 | }
|
8700 | }
|
8701 | }, {
|
8702 | key: '_onProgress',
|
8703 | value: function _onProgress(e) {
|
8704 | if (this._status === _loader.LoaderStatus.kError) {
|
8705 | // Ignore error response
|
8706 | return;
|
8707 | }
|
8708 |
|
8709 | if (this._contentLength === null) {
|
8710 | var openNextRange = false;
|
8711 |
|
8712 | if (this._waitForTotalLength) {
|
8713 | this._waitForTotalLength = false;
|
8714 | this._totalLengthReceived = true;
|
8715 | openNextRange = true;
|
8716 |
|
8717 | var total = e.total;
|
8718 | this._internalAbort();
|
8719 | if (total != null & total !== 0) {
|
8720 | this._totalLength = total;
|
8721 | }
|
8722 | }
|
8723 |
|
8724 | // calculate currrent request range's contentLength
|
8725 | if (this._range.to === -1) {
|
8726 | this._contentLength = this._totalLength - this._range.from;
|
8727 | } else {
|
8728 | // to !== -1
|
8729 | this._contentLength = this._range.to - this._range.from + 1;
|
8730 | }
|
8731 |
|
8732 | if (openNextRange) {
|
8733 | this._openSubRange();
|
8734 | return;
|
8735 | }
|
8736 | if (this._onContentLengthKnown) {
|
8737 | this._onContentLengthKnown(this._contentLength);
|
8738 | }
|
8739 | }
|
8740 |
|
8741 | var delta = e.loaded - this._lastTimeLoaded;
|
8742 | this._lastTimeLoaded = e.loaded;
|
8743 | this._speedSampler.addBytes(delta);
|
8744 | }
|
8745 | }, {
|
8746 | key: '_normalizeSpeed',
|
8747 | value: function _normalizeSpeed(input) {
|
8748 | var list = this._chunkSizeKBList;
|
8749 | var last = list.length - 1;
|
8750 | var mid = 0;
|
8751 | var lbound = 0;
|
8752 | var ubound = last;
|
8753 |
|
8754 | if (input < list[0]) {
|
8755 | return list[0];
|
8756 | }
|
8757 |
|
8758 | while (lbound <= ubound) {
|
8759 | mid = lbound + Math.floor((ubound - lbound) / 2);
|
8760 | if (mid === last || input >= list[mid] && input < list[mid + 1]) {
|
8761 | return list[mid];
|
8762 | } else if (list[mid] < input) {
|
8763 | lbound = mid + 1;
|
8764 | } else {
|
8765 | ubound = mid - 1;
|
8766 | }
|
8767 | }
|
8768 | }
|
8769 | }, {
|
8770 | key: '_onLoad',
|
8771 | value: function _onLoad(e) {
|
8772 | if (this._status === _loader.LoaderStatus.kError) {
|
8773 | // Ignore error response
|
8774 | return;
|
8775 | }
|
8776 |
|
8777 | if (this._waitForTotalLength) {
|
8778 | this._waitForTotalLength = false;
|
8779 | return;
|
8780 | }
|
8781 |
|
8782 | this._lastTimeLoaded = 0;
|
8783 | var KBps = this._speedSampler.lastSecondKBps;
|
8784 | if (KBps === 0) {
|
8785 | this._zeroSpeedChunkCount++;
|
8786 | if (this._zeroSpeedChunkCount >= 3) {
|
8787 | // Try get currentKBps after 3 chunks
|
8788 | KBps = this._speedSampler.currentKBps;
|
8789 | }
|
8790 | }
|
8791 |
|
8792 | if (KBps !== 0) {
|
8793 | var normalized = this._normalizeSpeed(KBps);
|
8794 | if (this._currentSpeedNormalized !== normalized) {
|
8795 | this._currentSpeedNormalized = normalized;
|
8796 | this._currentChunkSizeKB = normalized;
|
8797 | }
|
8798 | }
|
8799 |
|
8800 | var chunk = e.target.response;
|
8801 | var byteStart = this._range.from + this._receivedLength;
|
8802 | this._receivedLength += chunk.byteLength;
|
8803 |
|
8804 | var reportComplete = false;
|
8805 |
|
8806 | if (this._contentLength != null && this._receivedLength < this._contentLength) {
|
8807 | // continue load next chunk
|
8808 | this._openSubRange();
|
8809 | } else {
|
8810 | reportComplete = true;
|
8811 | }
|
8812 |
|
8813 | // dispatch received chunk
|
8814 | if (this._onDataArrival) {
|
8815 | this._onDataArrival(chunk, byteStart, this._receivedLength);
|
8816 | }
|
8817 |
|
8818 | if (reportComplete) {
|
8819 | this._status = _loader.LoaderStatus.kComplete;
|
8820 | if (this._onComplete) {
|
8821 | this._onComplete(this._range.from, this._range.from + this._receivedLength - 1);
|
8822 | }
|
8823 | }
|
8824 | }
|
8825 | }, {
|
8826 | key: '_onXhrError',
|
8827 | value: function _onXhrError(e) {
|
8828 | this._status = _loader.LoaderStatus.kError;
|
8829 | var type = 0;
|
8830 | var info = null;
|
8831 |
|
8832 | if (this._contentLength && this._receivedLength > 0 && this._receivedLength < this._contentLength) {
|
8833 | type = _loader.LoaderErrors.EARLY_EOF;
|
8834 | info = { code: -1, msg: 'RangeLoader meet Early-Eof' };
|
8835 | } else {
|
8836 | type = _loader.LoaderErrors.EXCEPTION;
|
8837 | info = { code: -1, msg: e.constructor.name + ' ' + e.type };
|
8838 | }
|
8839 |
|
8840 | if (this._onError) {
|
8841 | this._onError(type, info);
|
8842 | } else {
|
8843 | throw new _exception.RuntimeException(info.msg);
|
8844 | }
|
8845 | }
|
8846 | }, {
|
8847 | key: 'currentSpeed',
|
8848 | get: function get() {
|
8849 | return this._speedSampler.lastSecondKBps;
|
8850 | }
|
8851 | }]);
|
8852 |
|
8853 | return RangeLoader;
|
8854 | }(_loader.BaseLoader);
|
8855 |
|
8856 | exports.default = RangeLoader;
|
8857 |
|
8858 | },{"../utils/exception.js":40,"../utils/logger.js":41,"./loader.js":24,"./speed-sampler.js":27}],32:[function(_dereq_,module,exports){
|
8859 | ;
|
8860 |
|
8861 | Object.defineProperty(exports, "__esModule", {
|
8862 | value: true
|
8863 | });
|
8864 |
|
8865 | 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; };
|
8866 |
|
8867 | 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; }; }(); /*
|
8868 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
8869 | *
|
8870 | * @author zheng qian <xqq@xqq.im>
|
8871 | *
|
8872 | * Licensed under the Apache License, Version 2.0 (the "License");
|
8873 | * you may not use this file except in compliance with the License.
|
8874 | * You may obtain a copy of the License at
|
8875 | *
|
8876 | * http://www.apache.org/licenses/LICENSE-2.0
|
8877 | *
|
8878 | * Unless required by applicable law or agreed to in writing, software
|
8879 | * distributed under the License is distributed on an "AS IS" BASIS,
|
8880 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
8881 | * See the License for the specific language governing permissions and
|
8882 | * limitations under the License.
|
8883 | */
|
8884 |
|
8885 | var _events = _dereq_('events');
|
8886 |
|
8887 | var _events2 = _interopRequireDefault(_events);
|
8888 |
|
8889 | var _logger = _dereq_('../utils/logger.js');
|
8890 |
|
8891 | var _logger2 = _interopRequireDefault(_logger);
|
8892 |
|
8893 | var _browser = _dereq_('../utils/browser.js');
|
8894 |
|
8895 | var _browser2 = _interopRequireDefault(_browser);
|
8896 |
|
8897 | var _playerEvents = _dereq_('./player-events.js');
|
8898 |
|
8899 | var _playerEvents2 = _interopRequireDefault(_playerEvents);
|
8900 |
|
8901 | var _transmuxer = _dereq_('../core/transmuxer.js');
|
8902 |
|
8903 | var _transmuxer2 = _interopRequireDefault(_transmuxer);
|
8904 |
|
8905 | var _transmuxingEvents = _dereq_('../core/transmuxing-events.js');
|
8906 |
|
8907 | var _transmuxingEvents2 = _interopRequireDefault(_transmuxingEvents);
|
8908 |
|
8909 | var _mseController = _dereq_('../core/mse-controller.js');
|
8910 |
|
8911 | var _mseController2 = _interopRequireDefault(_mseController);
|
8912 |
|
8913 | var _mseEvents = _dereq_('../core/mse-events.js');
|
8914 |
|
8915 | var _mseEvents2 = _interopRequireDefault(_mseEvents);
|
8916 |
|
8917 | var _playerErrors = _dereq_('./player-errors.js');
|
8918 |
|
8919 | var _config = _dereq_('../config.js');
|
8920 |
|
8921 | var _exception = _dereq_('../utils/exception.js');
|
8922 |
|
8923 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
8924 |
|
8925 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
8926 |
|
8927 | var FlvPlayer = function () {
|
8928 | function FlvPlayer(mediaDataSource, config) {
|
8929 | _classCallCheck(this, FlvPlayer);
|
8930 |
|
8931 | this.TAG = 'FlvPlayer';
|
8932 | this._type = 'FlvPlayer';
|
8933 | this._emitter = new _events2.default();
|
8934 |
|
8935 | this._config = (0, _config.createDefaultConfig)();
|
8936 | if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') {
|
8937 | Object.assign(this._config, config);
|
8938 | }
|
8939 |
|
8940 | if (mediaDataSource.type.toLowerCase() !== 'flv') {
|
8941 | throw new _exception.InvalidArgumentException('FlvPlayer requires an flv MediaDataSource input!');
|
8942 | }
|
8943 |
|
8944 | if (mediaDataSource.isLive === true) {
|
8945 | this._config.isLive = true;
|
8946 | }
|
8947 |
|
8948 | this.e = {
|
8949 | onvLoadedMetadata: this._onvLoadedMetadata.bind(this),
|
8950 | onvSeeking: this._onvSeeking.bind(this),
|
8951 | onvCanPlay: this._onvCanPlay.bind(this),
|
8952 | onvStalled: this._onvStalled.bind(this),
|
8953 | onvProgress: this._onvProgress.bind(this)
|
8954 | };
|
8955 |
|
8956 | if (self.performance && self.performance.now) {
|
8957 | this._now = self.performance.now.bind(self.performance);
|
8958 | } else {
|
8959 | this._now = Date.now;
|
8960 | }
|
8961 |
|
8962 | this._pendingSeekTime = null; // in seconds
|
8963 | this._requestSetTime = false;
|
8964 | this._seekpointRecord = null;
|
8965 | this._progressChecker = null;
|
8966 |
|
8967 | this._mediaDataSource = mediaDataSource;
|
8968 | this._mediaElement = null;
|
8969 | this._msectl = null;
|
8970 | this._transmuxer = null;
|
8971 |
|
8972 | this._mseSourceOpened = false;
|
8973 | this._hasPendingLoad = false;
|
8974 | this._receivedCanPlay = false;
|
8975 |
|
8976 | this._mediaInfo = null;
|
8977 | this._statisticsInfo = null;
|
8978 |
|
8979 | var chromeNeedIDRFix = _browser2.default.chrome && (_browser2.default.version.major < 50 || _browser2.default.version.major === 50 && _browser2.default.version.build < 2661);
|
8980 | this._alwaysSeekKeyframe = chromeNeedIDRFix || _browser2.default.msedge || _browser2.default.msie ? true : false;
|
8981 |
|
8982 | if (this._alwaysSeekKeyframe) {
|
8983 | this._config.accurateSeek = false;
|
8984 | }
|
8985 | }
|
8986 |
|
8987 | _createClass(FlvPlayer, [{
|
8988 | key: 'destroy',
|
8989 | value: function destroy() {
|
8990 | if (this._progressChecker != null) {
|
8991 | window.clearInterval(this._progressChecker);
|
8992 | this._progressChecker = null;
|
8993 | }
|
8994 | if (this._transmuxer) {
|
8995 | this.unload();
|
8996 | }
|
8997 | if (this._mediaElement) {
|
8998 | this.detachMediaElement();
|
8999 | }
|
9000 | this.e = null;
|
9001 | this._mediaDataSource = null;
|
9002 |
|
9003 | this._emitter.removeAllListeners();
|
9004 | this._emitter = null;
|
9005 | }
|
9006 | }, {
|
9007 | key: 'on',
|
9008 | value: function on(event, listener) {
|
9009 | var _this = this;
|
9010 |
|
9011 | if (event === _playerEvents2.default.MEDIA_INFO) {
|
9012 | if (this._mediaInfo != null) {
|
9013 | Promise.resolve().then(function () {
|
9014 | _this._emitter.emit(_playerEvents2.default.MEDIA_INFO, _this.mediaInfo);
|
9015 | });
|
9016 | }
|
9017 | } else if (event === _playerEvents2.default.STATISTICS_INFO) {
|
9018 | if (this._statisticsInfo != null) {
|
9019 | Promise.resolve().then(function () {
|
9020 | _this._emitter.emit(_playerEvents2.default.STATISTICS_INFO, _this.statisticsInfo);
|
9021 | });
|
9022 | }
|
9023 | }
|
9024 | this._emitter.addListener(event, listener);
|
9025 | }
|
9026 | }, {
|
9027 | key: 'off',
|
9028 | value: function off(event, listener) {
|
9029 | this._emitter.removeListener(event, listener);
|
9030 | }
|
9031 | }, {
|
9032 | key: 'attachMediaElement',
|
9033 | value: function attachMediaElement(mediaElement) {
|
9034 | var _this2 = this;
|
9035 |
|
9036 | this._mediaElement = mediaElement;
|
9037 | mediaElement.addEventListener('loadedmetadata', this.e.onvLoadedMetadata);
|
9038 | mediaElement.addEventListener('seeking', this.e.onvSeeking);
|
9039 | mediaElement.addEventListener('canplay', this.e.onvCanPlay);
|
9040 | mediaElement.addEventListener('stalled', this.e.onvStalled);
|
9041 | mediaElement.addEventListener('progress', this.e.onvProgress);
|
9042 |
|
9043 | this._msectl = new _mseController2.default(this._config);
|
9044 |
|
9045 | this._msectl.on(_mseEvents2.default.UPDATE_END, this._onmseUpdateEnd.bind(this));
|
9046 | this._msectl.on(_mseEvents2.default.BUFFER_FULL, this._onmseBufferFull.bind(this));
|
9047 | this._msectl.on(_mseEvents2.default.SOURCE_OPEN, function () {
|
9048 | _this2._mseSourceOpened = true;
|
9049 | if (_this2._hasPendingLoad) {
|
9050 | _this2._hasPendingLoad = false;
|
9051 | _this2.load();
|
9052 | }
|
9053 | });
|
9054 | this._msectl.on(_mseEvents2.default.ERROR, function (info) {
|
9055 | _this2._emitter.emit(_playerEvents2.default.ERROR, _playerErrors.ErrorTypes.MEDIA_ERROR, _playerErrors.ErrorDetails.MEDIA_MSE_ERROR, info);
|
9056 | });
|
9057 |
|
9058 | this._msectl.attachMediaElement(mediaElement);
|
9059 |
|
9060 | if (this._pendingSeekTime != null) {
|
9061 | try {
|
9062 | mediaElement.currentTime = this._pendingSeekTime;
|
9063 | this._pendingSeekTime = null;
|
9064 | } catch (e) {
|
9065 | // IE11 may throw InvalidStateError if readyState === 0
|
9066 | // We can defer set currentTime operation after loadedmetadata
|
9067 | }
|
9068 | }
|
9069 | }
|
9070 | }, {
|
9071 | key: 'detachMediaElement',
|
9072 | value: function detachMediaElement() {
|
9073 | if (this._mediaElement) {
|
9074 | this._msectl.detachMediaElement();
|
9075 | this._mediaElement.removeEventListener('loadedmetadata', this.e.onvLoadedMetadata);
|
9076 | this._mediaElement.removeEventListener('seeking', this.e.onvSeeking);
|
9077 | this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay);
|
9078 | this._mediaElement.removeEventListener('stalled', this.e.onvStalled);
|
9079 | this._mediaElement.removeEventListener('progress', this.e.onvProgress);
|
9080 | this._mediaElement = null;
|
9081 | }
|
9082 | if (this._msectl) {
|
9083 | this._msectl.destroy();
|
9084 | this._msectl = null;
|
9085 | }
|
9086 | }
|
9087 | }, {
|
9088 | key: 'load',
|
9089 | value: function load() {
|
9090 | var _this3 = this;
|
9091 |
|
9092 | if (!this._mediaElement) {
|
9093 | throw new _exception.IllegalStateException('HTMLMediaElement must be attached before load()!');
|
9094 | }
|
9095 | if (this._transmuxer) {
|
9096 | throw new _exception.IllegalStateException('FlvPlayer.load() has been called, please call unload() first!');
|
9097 | }
|
9098 | if (this._hasPendingLoad) {
|
9099 | return;
|
9100 | }
|
9101 |
|
9102 | if (this._config.deferLoadAfterSourceOpen && this._mseSourceOpened === false) {
|
9103 | this._hasPendingLoad = true;
|
9104 | return;
|
9105 | }
|
9106 |
|
9107 | if (this._mediaElement.readyState > 0) {
|
9108 | this._requestSetTime = true;
|
9109 | // IE11 may throw InvalidStateError if readyState === 0
|
9110 | this._mediaElement.currentTime = 0;
|
9111 | }
|
9112 |
|
9113 | this._transmuxer = new _transmuxer2.default(this._mediaDataSource, this._config);
|
9114 |
|
9115 | this._transmuxer.on(_transmuxingEvents2.default.INIT_SEGMENT, function (type, is) {
|
9116 | _this3._msectl.appendInitSegment(is);
|
9117 | });
|
9118 | this._transmuxer.on(_transmuxingEvents2.default.MEDIA_SEGMENT, function (type, ms) {
|
9119 | _this3._msectl.appendMediaSegment(ms);
|
9120 |
|
9121 | // lazyLoad check
|
9122 | if (_this3._config.lazyLoad && !_this3._config.isLive) {
|
9123 | var currentTime = _this3._mediaElement.currentTime;
|
9124 | if (ms.info.endDts >= (currentTime + _this3._config.lazyLoadMaxDuration) * 1000) {
|
9125 | if (_this3._progressChecker == null) {
|
9126 | _logger2.default.v(_this3.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task');
|
9127 | _this3._suspendTransmuxer();
|
9128 | }
|
9129 | }
|
9130 | }
|
9131 | });
|
9132 | this._transmuxer.on(_transmuxingEvents2.default.LOADING_COMPLETE, function () {
|
9133 | _this3._msectl.endOfStream();
|
9134 | _this3._emitter.emit(_playerEvents2.default.LOADING_COMPLETE);
|
9135 | });
|
9136 | this._transmuxer.on(_transmuxingEvents2.default.RECOVERED_EARLY_EOF, function () {
|
9137 | _this3._emitter.emit(_playerEvents2.default.RECOVERED_EARLY_EOF);
|
9138 | });
|
9139 | this._transmuxer.on(_transmuxingEvents2.default.IO_ERROR, function (detail, info) {
|
9140 | _this3._emitter.emit(_playerEvents2.default.ERROR, _playerErrors.ErrorTypes.NETWORK_ERROR, detail, info);
|
9141 | });
|
9142 | this._transmuxer.on(_transmuxingEvents2.default.DEMUX_ERROR, function (detail, info) {
|
9143 | _this3._emitter.emit(_playerEvents2.default.ERROR, _playerErrors.ErrorTypes.MEDIA_ERROR, detail, { code: -1, msg: info });
|
9144 | });
|
9145 | this._transmuxer.on(_transmuxingEvents2.default.MEDIA_INFO, function (mediaInfo) {
|
9146 | _this3._mediaInfo = mediaInfo;
|
9147 | _this3._emitter.emit(_playerEvents2.default.MEDIA_INFO, Object.assign({}, mediaInfo));
|
9148 | });
|
9149 | this._transmuxer.on(_transmuxingEvents2.default.METADATA_ARRIVED, function (metadata) {
|
9150 | _this3._emitter.emit(_playerEvents2.default.METADATA_ARRIVED, metadata);
|
9151 | });
|
9152 | this._transmuxer.on(_transmuxingEvents2.default.SCRIPTDATA_ARRIVED, function (data) {
|
9153 | _this3._emitter.emit(_playerEvents2.default.SCRIPTDATA_ARRIVED, data);
|
9154 | });
|
9155 | this._transmuxer.on(_transmuxingEvents2.default.STATISTICS_INFO, function (statInfo) {
|
9156 | _this3._statisticsInfo = _this3._fillStatisticsInfo(statInfo);
|
9157 | _this3._emitter.emit(_playerEvents2.default.STATISTICS_INFO, Object.assign({}, _this3._statisticsInfo));
|
9158 | });
|
9159 | this._transmuxer.on(_transmuxingEvents2.default.RECOMMEND_SEEKPOINT, function (milliseconds) {
|
9160 | if (_this3._mediaElement && !_this3._config.accurateSeek) {
|
9161 | _this3._requestSetTime = true;
|
9162 | _this3._mediaElement.currentTime = milliseconds / 1000;
|
9163 | }
|
9164 | });
|
9165 |
|
9166 | this._transmuxer.open();
|
9167 | }
|
9168 | }, {
|
9169 | key: 'unload',
|
9170 | value: function unload() {
|
9171 | if (this._mediaElement) {
|
9172 | this._mediaElement.pause();
|
9173 | }
|
9174 | if (this._msectl) {
|
9175 | this._msectl.seek(0);
|
9176 | }
|
9177 | if (this._transmuxer) {
|
9178 | this._transmuxer.close();
|
9179 | this._transmuxer.destroy();
|
9180 | this._transmuxer = null;
|
9181 | }
|
9182 | }
|
9183 | }, {
|
9184 | key: 'play',
|
9185 | value: function play() {
|
9186 | return this._mediaElement.play();
|
9187 | }
|
9188 | }, {
|
9189 | key: 'pause',
|
9190 | value: function pause() {
|
9191 | this._mediaElement.pause();
|
9192 | }
|
9193 | }, {
|
9194 | key: '_fillStatisticsInfo',
|
9195 | value: function _fillStatisticsInfo(statInfo) {
|
9196 | statInfo.playerType = this._type;
|
9197 |
|
9198 | if (!(this._mediaElement instanceof HTMLVideoElement)) {
|
9199 | return statInfo;
|
9200 | }
|
9201 |
|
9202 | var hasQualityInfo = true;
|
9203 | var decoded = 0;
|
9204 | var dropped = 0;
|
9205 |
|
9206 | if (this._mediaElement.getVideoPlaybackQuality) {
|
9207 | var quality = this._mediaElement.getVideoPlaybackQuality();
|
9208 | decoded = quality.totalVideoFrames;
|
9209 | dropped = quality.droppedVideoFrames;
|
9210 | } else if (this._mediaElement.webkitDecodedFrameCount != undefined) {
|
9211 | decoded = this._mediaElement.webkitDecodedFrameCount;
|
9212 | dropped = this._mediaElement.webkitDroppedFrameCount;
|
9213 | } else {
|
9214 | hasQualityInfo = false;
|
9215 | }
|
9216 |
|
9217 | if (hasQualityInfo) {
|
9218 | statInfo.decodedFrames = decoded;
|
9219 | statInfo.droppedFrames = dropped;
|
9220 | }
|
9221 |
|
9222 | return statInfo;
|
9223 | }
|
9224 | }, {
|
9225 | key: '_onmseUpdateEnd',
|
9226 | value: function _onmseUpdateEnd() {
|
9227 | if (!this._config.lazyLoad || this._config.isLive) {
|
9228 | return;
|
9229 | }
|
9230 |
|
9231 | var buffered = this._mediaElement.buffered;
|
9232 | var currentTime = this._mediaElement.currentTime;
|
9233 | var currentRangeStart = 0;
|
9234 | var currentRangeEnd = 0;
|
9235 |
|
9236 | for (var i = 0; i < buffered.length; i++) {
|
9237 | var start = buffered.start(i);
|
9238 | var end = buffered.end(i);
|
9239 | if (start <= currentTime && currentTime < end) {
|
9240 | currentRangeStart = start;
|
9241 | currentRangeEnd = end;
|
9242 | break;
|
9243 | }
|
9244 | }
|
9245 |
|
9246 | if (currentRangeEnd >= currentTime + this._config.lazyLoadMaxDuration && this._progressChecker == null) {
|
9247 | _logger2.default.v(this.TAG, 'Maximum buffering duration exceeded, suspend transmuxing task');
|
9248 | this._suspendTransmuxer();
|
9249 | }
|
9250 | }
|
9251 | }, {
|
9252 | key: '_onmseBufferFull',
|
9253 | value: function _onmseBufferFull() {
|
9254 | _logger2.default.v(this.TAG, 'MSE SourceBuffer is full, suspend transmuxing task');
|
9255 | if (this._progressChecker == null) {
|
9256 | this._suspendTransmuxer();
|
9257 | }
|
9258 | }
|
9259 | }, {
|
9260 | key: '_suspendTransmuxer',
|
9261 | value: function _suspendTransmuxer() {
|
9262 | if (this._transmuxer) {
|
9263 | this._transmuxer.pause();
|
9264 |
|
9265 | if (this._progressChecker == null) {
|
9266 | this._progressChecker = window.setInterval(this._checkProgressAndResume.bind(this), 1000);
|
9267 | }
|
9268 | }
|
9269 | }
|
9270 | }, {
|
9271 | key: '_checkProgressAndResume',
|
9272 | value: function _checkProgressAndResume() {
|
9273 | var currentTime = this._mediaElement.currentTime;
|
9274 | var buffered = this._mediaElement.buffered;
|
9275 |
|
9276 | var needResume = false;
|
9277 |
|
9278 | for (var i = 0; i < buffered.length; i++) {
|
9279 | var from = buffered.start(i);
|
9280 | var to = buffered.end(i);
|
9281 | if (currentTime >= from && currentTime < to) {
|
9282 | if (currentTime >= to - this._config.lazyLoadRecoverDuration) {
|
9283 | needResume = true;
|
9284 | }
|
9285 | break;
|
9286 | }
|
9287 | }
|
9288 |
|
9289 | if (needResume) {
|
9290 | window.clearInterval(this._progressChecker);
|
9291 | this._progressChecker = null;
|
9292 | if (needResume) {
|
9293 | _logger2.default.v(this.TAG, 'Continue loading from paused position');
|
9294 | this._transmuxer.resume();
|
9295 | }
|
9296 | }
|
9297 | }
|
9298 | }, {
|
9299 | key: '_isTimepointBuffered',
|
9300 | value: function _isTimepointBuffered(seconds) {
|
9301 | var buffered = this._mediaElement.buffered;
|
9302 |
|
9303 | for (var i = 0; i < buffered.length; i++) {
|
9304 | var from = buffered.start(i);
|
9305 | var to = buffered.end(i);
|
9306 | if (seconds >= from && seconds < to) {
|
9307 | return true;
|
9308 | }
|
9309 | }
|
9310 | return false;
|
9311 | }
|
9312 | }, {
|
9313 | key: '_internalSeek',
|
9314 | value: function _internalSeek(seconds) {
|
9315 | var directSeek = this._isTimepointBuffered(seconds);
|
9316 |
|
9317 | var directSeekBegin = false;
|
9318 | var directSeekBeginTime = 0;
|
9319 |
|
9320 | if (seconds < 1.0 && this._mediaElement.buffered.length > 0) {
|
9321 | var videoBeginTime = this._mediaElement.buffered.start(0);
|
9322 | if (videoBeginTime < 1.0 && seconds < videoBeginTime || _browser2.default.safari) {
|
9323 | directSeekBegin = true;
|
9324 | // also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid
|
9325 | directSeekBeginTime = _browser2.default.safari ? 0.1 : videoBeginTime;
|
9326 | }
|
9327 | }
|
9328 |
|
9329 | if (directSeekBegin) {
|
9330 | // seek to video begin, set currentTime directly if beginPTS buffered
|
9331 | this._requestSetTime = true;
|
9332 | this._mediaElement.currentTime = directSeekBeginTime;
|
9333 | } else if (directSeek) {
|
9334 | // buffered position
|
9335 | if (!this._alwaysSeekKeyframe) {
|
9336 | this._requestSetTime = true;
|
9337 | this._mediaElement.currentTime = seconds;
|
9338 | } else {
|
9339 | var idr = this._msectl.getNearestKeyframe(Math.floor(seconds * 1000));
|
9340 | this._requestSetTime = true;
|
9341 | if (idr != null) {
|
9342 | this._mediaElement.currentTime = idr.dts / 1000;
|
9343 | } else {
|
9344 | this._mediaElement.currentTime = seconds;
|
9345 | }
|
9346 | }
|
9347 | if (this._progressChecker != null) {
|
9348 | this._checkProgressAndResume();
|
9349 | }
|
9350 | } else {
|
9351 | if (this._progressChecker != null) {
|
9352 | window.clearInterval(this._progressChecker);
|
9353 | this._progressChecker = null;
|
9354 | }
|
9355 | this._msectl.seek(seconds);
|
9356 | this._transmuxer.seek(Math.floor(seconds * 1000)); // in milliseconds
|
9357 | // no need to set mediaElement.currentTime if non-accurateSeek,
|
9358 | // just wait for the recommend_seekpoint callback
|
9359 | if (this._config.accurateSeek) {
|
9360 | this._requestSetTime = true;
|
9361 | this._mediaElement.currentTime = seconds;
|
9362 | }
|
9363 | }
|
9364 | }
|
9365 | }, {
|
9366 | key: '_checkAndApplyUnbufferedSeekpoint',
|
9367 | value: function _checkAndApplyUnbufferedSeekpoint() {
|
9368 | if (this._seekpointRecord) {
|
9369 | if (this._seekpointRecord.recordTime <= this._now() - 100) {
|
9370 | var target = this._mediaElement.currentTime;
|
9371 | this._seekpointRecord = null;
|
9372 | if (!this._isTimepointBuffered(target)) {
|
9373 | if (this._progressChecker != null) {
|
9374 | window.clearTimeout(this._progressChecker);
|
9375 | this._progressChecker = null;
|
9376 | }
|
9377 | // .currentTime is consists with .buffered timestamp
|
9378 | // Chrome/Edge use DTS, while FireFox/Safari use PTS
|
9379 | this._msectl.seek(target);
|
9380 | this._transmuxer.seek(Math.floor(target * 1000));
|
9381 | // set currentTime if accurateSeek, or wait for recommend_seekpoint callback
|
9382 | if (this._config.accurateSeek) {
|
9383 | this._requestSetTime = true;
|
9384 | this._mediaElement.currentTime = target;
|
9385 | }
|
9386 | }
|
9387 | } else {
|
9388 | window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50);
|
9389 | }
|
9390 | }
|
9391 | }
|
9392 | }, {
|
9393 | key: '_checkAndResumeStuckPlayback',
|
9394 | value: function _checkAndResumeStuckPlayback(stalled) {
|
9395 | var media = this._mediaElement;
|
9396 | if (stalled || !this._receivedCanPlay || media.readyState < 2) {
|
9397 | // HAVE_CURRENT_DATA
|
9398 | var buffered = media.buffered;
|
9399 | if (buffered.length > 0 && media.currentTime < buffered.start(0)) {
|
9400 | _logger2.default.w(this.TAG, 'Playback seems stuck at ' + media.currentTime + ', seek to ' + buffered.start(0));
|
9401 | this._requestSetTime = true;
|
9402 | this._mediaElement.currentTime = buffered.start(0);
|
9403 | this._mediaElement.removeEventListener('progress', this.e.onvProgress);
|
9404 | }
|
9405 | } else {
|
9406 | // Playback didn't stuck, remove progress event listener
|
9407 | this._mediaElement.removeEventListener('progress', this.e.onvProgress);
|
9408 | }
|
9409 | }
|
9410 | }, {
|
9411 | key: '_onvLoadedMetadata',
|
9412 | value: function _onvLoadedMetadata(e) {
|
9413 | if (this._pendingSeekTime != null) {
|
9414 | this._mediaElement.currentTime = this._pendingSeekTime;
|
9415 | this._pendingSeekTime = null;
|
9416 | }
|
9417 | }
|
9418 | }, {
|
9419 | key: '_onvSeeking',
|
9420 | value: function _onvSeeking(e) {
|
9421 | // handle seeking request from browser's progress bar
|
9422 | var target = this._mediaElement.currentTime;
|
9423 | var buffered = this._mediaElement.buffered;
|
9424 |
|
9425 | if (this._requestSetTime) {
|
9426 | this._requestSetTime = false;
|
9427 | return;
|
9428 | }
|
9429 |
|
9430 | if (target < 1.0 && buffered.length > 0) {
|
9431 | // seek to video begin, set currentTime directly if beginPTS buffered
|
9432 | var videoBeginTime = buffered.start(0);
|
9433 | if (videoBeginTime < 1.0 && target < videoBeginTime || _browser2.default.safari) {
|
9434 | this._requestSetTime = true;
|
9435 | // also workaround for Safari: Seek to 0 may cause video stuck, use 0.1 to avoid
|
9436 | this._mediaElement.currentTime = _browser2.default.safari ? 0.1 : videoBeginTime;
|
9437 | return;
|
9438 | }
|
9439 | }
|
9440 |
|
9441 | if (this._isTimepointBuffered(target)) {
|
9442 | if (this._alwaysSeekKeyframe) {
|
9443 | var idr = this._msectl.getNearestKeyframe(Math.floor(target * 1000));
|
9444 | if (idr != null) {
|
9445 | this._requestSetTime = true;
|
9446 | this._mediaElement.currentTime = idr.dts / 1000;
|
9447 | }
|
9448 | }
|
9449 | if (this._progressChecker != null) {
|
9450 | this._checkProgressAndResume();
|
9451 | }
|
9452 | return;
|
9453 | }
|
9454 |
|
9455 | this._seekpointRecord = {
|
9456 | seekPoint: target,
|
9457 | recordTime: this._now()
|
9458 | };
|
9459 | window.setTimeout(this._checkAndApplyUnbufferedSeekpoint.bind(this), 50);
|
9460 | }
|
9461 | }, {
|
9462 | key: '_onvCanPlay',
|
9463 | value: function _onvCanPlay(e) {
|
9464 | this._receivedCanPlay = true;
|
9465 | this._mediaElement.removeEventListener('canplay', this.e.onvCanPlay);
|
9466 | }
|
9467 | }, {
|
9468 | key: '_onvStalled',
|
9469 | value: function _onvStalled(e) {
|
9470 | this._checkAndResumeStuckPlayback(true);
|
9471 | }
|
9472 | }, {
|
9473 | key: '_onvProgress',
|
9474 | value: function _onvProgress(e) {
|
9475 | this._checkAndResumeStuckPlayback();
|
9476 | }
|
9477 | }, {
|
9478 | key: 'type',
|
9479 | get: function get() {
|
9480 | return this._type;
|
9481 | }
|
9482 | }, {
|
9483 | key: 'buffered',
|
9484 | get: function get() {
|
9485 | return this._mediaElement.buffered;
|
9486 | }
|
9487 | }, {
|
9488 | key: 'duration',
|
9489 | get: function get() {
|
9490 | return this._mediaElement.duration;
|
9491 | }
|
9492 | }, {
|
9493 | key: 'volume',
|
9494 | get: function get() {
|
9495 | return this._mediaElement.volume;
|
9496 | },
|
9497 | set: function set(value) {
|
9498 | this._mediaElement.volume = value;
|
9499 | }
|
9500 | }, {
|
9501 | key: 'muted',
|
9502 | get: function get() {
|
9503 | return this._mediaElement.muted;
|
9504 | },
|
9505 | set: function set(muted) {
|
9506 | this._mediaElement.muted = muted;
|
9507 | }
|
9508 | }, {
|
9509 | key: 'currentTime',
|
9510 | get: function get() {
|
9511 | if (this._mediaElement) {
|
9512 | return this._mediaElement.currentTime;
|
9513 | }
|
9514 | return 0;
|
9515 | },
|
9516 | set: function set(seconds) {
|
9517 | if (this._mediaElement) {
|
9518 | this._internalSeek(seconds);
|
9519 | } else {
|
9520 | this._pendingSeekTime = seconds;
|
9521 | }
|
9522 | }
|
9523 | }, {
|
9524 | key: 'mediaInfo',
|
9525 | get: function get() {
|
9526 | return Object.assign({}, this._mediaInfo);
|
9527 | }
|
9528 | }, {
|
9529 | key: 'statisticsInfo',
|
9530 | get: function get() {
|
9531 | if (this._statisticsInfo == null) {
|
9532 | this._statisticsInfo = {};
|
9533 | }
|
9534 | this._statisticsInfo = this._fillStatisticsInfo(this._statisticsInfo);
|
9535 | return Object.assign({}, this._statisticsInfo);
|
9536 | }
|
9537 | }]);
|
9538 |
|
9539 | return FlvPlayer;
|
9540 | }();
|
9541 |
|
9542 | exports.default = FlvPlayer;
|
9543 |
|
9544 | },{"../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){
|
9545 | ;
|
9546 |
|
9547 | Object.defineProperty(exports, "__esModule", {
|
9548 | value: true
|
9549 | });
|
9550 |
|
9551 | 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; };
|
9552 |
|
9553 | 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; }; }(); /*
|
9554 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
9555 | *
|
9556 | * @author zheng qian <xqq@xqq.im>
|
9557 | *
|
9558 | * Licensed under the Apache License, Version 2.0 (the "License");
|
9559 | * you may not use this file except in compliance with the License.
|
9560 | * You may obtain a copy of the License at
|
9561 | *
|
9562 | * http://www.apache.org/licenses/LICENSE-2.0
|
9563 | *
|
9564 | * Unless required by applicable law or agreed to in writing, software
|
9565 | * distributed under the License is distributed on an "AS IS" BASIS,
|
9566 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
9567 | * See the License for the specific language governing permissions and
|
9568 | * limitations under the License.
|
9569 | */
|
9570 |
|
9571 | var _events = _dereq_('events');
|
9572 |
|
9573 | var _events2 = _interopRequireDefault(_events);
|
9574 |
|
9575 | var _playerEvents = _dereq_('./player-events.js');
|
9576 |
|
9577 | var _playerEvents2 = _interopRequireDefault(_playerEvents);
|
9578 |
|
9579 | var _config = _dereq_('../config.js');
|
9580 |
|
9581 | var _exception = _dereq_('../utils/exception.js');
|
9582 |
|
9583 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
9584 |
|
9585 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
9586 |
|
9587 | // Player wrapper for browser's native player (HTMLVideoElement) without MediaSource src.
|
9588 | var NativePlayer = function () {
|
9589 | function NativePlayer(mediaDataSource, config) {
|
9590 | _classCallCheck(this, NativePlayer);
|
9591 |
|
9592 | this.TAG = 'NativePlayer';
|
9593 | this._type = 'NativePlayer';
|
9594 | this._emitter = new _events2.default();
|
9595 |
|
9596 | this._config = (0, _config.createDefaultConfig)();
|
9597 | if ((typeof config === 'undefined' ? 'undefined' : _typeof(config)) === 'object') {
|
9598 | Object.assign(this._config, config);
|
9599 | }
|
9600 |
|
9601 | if (mediaDataSource.type.toLowerCase() === 'flv') {
|
9602 | throw new _exception.InvalidArgumentException('NativePlayer does\'t support flv MediaDataSource input!');
|
9603 | }
|
9604 | if (mediaDataSource.hasOwnProperty('segments')) {
|
9605 | throw new _exception.InvalidArgumentException('NativePlayer(' + mediaDataSource.type + ') doesn\'t support multipart playback!');
|
9606 | }
|
9607 |
|
9608 | this.e = {
|
9609 | onvLoadedMetadata: this._onvLoadedMetadata.bind(this)
|
9610 | };
|
9611 |
|
9612 | this._pendingSeekTime = null;
|
9613 | this._statisticsReporter = null;
|
9614 |
|
9615 | this._mediaDataSource = mediaDataSource;
|
9616 | this._mediaElement = null;
|
9617 | }
|
9618 |
|
9619 | _createClass(NativePlayer, [{
|
9620 | key: 'destroy',
|
9621 | value: function destroy() {
|
9622 | if (this._mediaElement) {
|
9623 | this.unload();
|
9624 | this.detachMediaElement();
|
9625 | }
|
9626 | this.e = null;
|
9627 | this._mediaDataSource = null;
|
9628 | this._emitter.removeAllListeners();
|
9629 | this._emitter = null;
|
9630 | }
|
9631 | }, {
|
9632 | key: 'on',
|
9633 | value: function on(event, listener) {
|
9634 | var _this = this;
|
9635 |
|
9636 | if (event === _playerEvents2.default.MEDIA_INFO) {
|
9637 | if (this._mediaElement != null && this._mediaElement.readyState !== 0) {
|
9638 | // HAVE_NOTHING
|
9639 | Promise.resolve().then(function () {
|
9640 | _this._emitter.emit(_playerEvents2.default.MEDIA_INFO, _this.mediaInfo);
|
9641 | });
|
9642 | }
|
9643 | } else if (event === _playerEvents2.default.STATISTICS_INFO) {
|
9644 | if (this._mediaElement != null && this._mediaElement.readyState !== 0) {
|
9645 | Promise.resolve().then(function () {
|
9646 | _this._emitter.emit(_playerEvents2.default.STATISTICS_INFO, _this.statisticsInfo);
|
9647 | });
|
9648 | }
|
9649 | }
|
9650 | this._emitter.addListener(event, listener);
|
9651 | }
|
9652 | }, {
|
9653 | key: 'off',
|
9654 | value: function off(event, listener) {
|
9655 | this._emitter.removeListener(event, listener);
|
9656 | }
|
9657 | }, {
|
9658 | key: 'attachMediaElement',
|
9659 | value: function attachMediaElement(mediaElement) {
|
9660 | this._mediaElement = mediaElement;
|
9661 | mediaElement.addEventListener('loadedmetadata', this.e.onvLoadedMetadata);
|
9662 |
|
9663 | if (this._pendingSeekTime != null) {
|
9664 | try {
|
9665 | mediaElement.currentTime = this._pendingSeekTime;
|
9666 | this._pendingSeekTime = null;
|
9667 | } catch (e) {
|
9668 | // IE11 may throw InvalidStateError if readyState === 0
|
9669 | // Defer set currentTime operation after loadedmetadata
|
9670 | }
|
9671 | }
|
9672 | }
|
9673 | }, {
|
9674 | key: 'detachMediaElement',
|
9675 | value: function detachMediaElement() {
|
9676 | if (this._mediaElement) {
|
9677 | this._mediaElement.src = '';
|
9678 | this._mediaElement.removeAttribute('src');
|
9679 | this._mediaElement.removeEventListener('loadedmetadata', this.e.onvLoadedMetadata);
|
9680 | this._mediaElement = null;
|
9681 | }
|
9682 | if (this._statisticsReporter != null) {
|
9683 | window.clearInterval(this._statisticsReporter);
|
9684 | this._statisticsReporter = null;
|
9685 | }
|
9686 | }
|
9687 | }, {
|
9688 | key: 'load',
|
9689 | value: function load() {
|
9690 | if (!this._mediaElement) {
|
9691 | throw new _exception.IllegalStateException('HTMLMediaElement must be attached before load()!');
|
9692 | }
|
9693 | this._mediaElement.src = this._mediaDataSource.url;
|
9694 |
|
9695 | if (this._mediaElement.readyState > 0) {
|
9696 | this._mediaElement.currentTime = 0;
|
9697 | }
|
9698 |
|
9699 | this._mediaElement.preload = 'auto';
|
9700 | this._mediaElement.load();
|
9701 | this._statisticsReporter = window.setInterval(this._reportStatisticsInfo.bind(this), this._config.statisticsInfoReportInterval);
|
9702 | }
|
9703 | }, {
|
9704 | key: 'unload',
|
9705 | value: function unload() {
|
9706 | if (this._mediaElement) {
|
9707 | this._mediaElement.src = '';
|
9708 | this._mediaElement.removeAttribute('src');
|
9709 | }
|
9710 | if (this._statisticsReporter != null) {
|
9711 | window.clearInterval(this._statisticsReporter);
|
9712 | this._statisticsReporter = null;
|
9713 | }
|
9714 | }
|
9715 | }, {
|
9716 | key: 'play',
|
9717 | value: function play() {
|
9718 | return this._mediaElement.play();
|
9719 | }
|
9720 | }, {
|
9721 | key: 'pause',
|
9722 | value: function pause() {
|
9723 | this._mediaElement.pause();
|
9724 | }
|
9725 | }, {
|
9726 | key: '_onvLoadedMetadata',
|
9727 | value: function _onvLoadedMetadata(e) {
|
9728 | if (this._pendingSeekTime != null) {
|
9729 | this._mediaElement.currentTime = this._pendingSeekTime;
|
9730 | this._pendingSeekTime = null;
|
9731 | }
|
9732 | this._emitter.emit(_playerEvents2.default.MEDIA_INFO, this.mediaInfo);
|
9733 | }
|
9734 | }, {
|
9735 | key: '_reportStatisticsInfo',
|
9736 | value: function _reportStatisticsInfo() {
|
9737 | this._emitter.emit(_playerEvents2.default.STATISTICS_INFO, this.statisticsInfo);
|
9738 | }
|
9739 | }, {
|
9740 | key: 'type',
|
9741 | get: function get() {
|
9742 | return this._type;
|
9743 | }
|
9744 | }, {
|
9745 | key: 'buffered',
|
9746 | get: function get() {
|
9747 | return this._mediaElement.buffered;
|
9748 | }
|
9749 | }, {
|
9750 | key: 'duration',
|
9751 | get: function get() {
|
9752 | return this._mediaElement.duration;
|
9753 | }
|
9754 | }, {
|
9755 | key: 'volume',
|
9756 | get: function get() {
|
9757 | return this._mediaElement.volume;
|
9758 | },
|
9759 | set: function set(value) {
|
9760 | this._mediaElement.volume = value;
|
9761 | }
|
9762 | }, {
|
9763 | key: 'muted',
|
9764 | get: function get() {
|
9765 | return this._mediaElement.muted;
|
9766 | },
|
9767 | set: function set(muted) {
|
9768 | this._mediaElement.muted = muted;
|
9769 | }
|
9770 | }, {
|
9771 | key: 'currentTime',
|
9772 | get: function get() {
|
9773 | if (this._mediaElement) {
|
9774 | return this._mediaElement.currentTime;
|
9775 | }
|
9776 | return 0;
|
9777 | },
|
9778 | set: function set(seconds) {
|
9779 | if (this._mediaElement) {
|
9780 | this._mediaElement.currentTime = seconds;
|
9781 | } else {
|
9782 | this._pendingSeekTime = seconds;
|
9783 | }
|
9784 | }
|
9785 | }, {
|
9786 | key: 'mediaInfo',
|
9787 | get: function get() {
|
9788 | var mediaPrefix = this._mediaElement instanceof HTMLAudioElement ? 'audio/' : 'video/';
|
9789 | var info = {
|
9790 | mimeType: mediaPrefix + this._mediaDataSource.type
|
9791 | };
|
9792 | if (this._mediaElement) {
|
9793 | info.duration = Math.floor(this._mediaElement.duration * 1000);
|
9794 | if (this._mediaElement instanceof HTMLVideoElement) {
|
9795 | info.width = this._mediaElement.videoWidth;
|
9796 | info.height = this._mediaElement.videoHeight;
|
9797 | }
|
9798 | }
|
9799 | return info;
|
9800 | }
|
9801 | }, {
|
9802 | key: 'statisticsInfo',
|
9803 | get: function get() {
|
9804 | var info = {
|
9805 | playerType: this._type,
|
9806 | url: this._mediaDataSource.url
|
9807 | };
|
9808 |
|
9809 | if (!(this._mediaElement instanceof HTMLVideoElement)) {
|
9810 | return info;
|
9811 | }
|
9812 |
|
9813 | var hasQualityInfo = true;
|
9814 | var decoded = 0;
|
9815 | var dropped = 0;
|
9816 |
|
9817 | if (this._mediaElement.getVideoPlaybackQuality) {
|
9818 | var quality = this._mediaElement.getVideoPlaybackQuality();
|
9819 | decoded = quality.totalVideoFrames;
|
9820 | dropped = quality.droppedVideoFrames;
|
9821 | } else if (this._mediaElement.webkitDecodedFrameCount != undefined) {
|
9822 | decoded = this._mediaElement.webkitDecodedFrameCount;
|
9823 | dropped = this._mediaElement.webkitDroppedFrameCount;
|
9824 | } else {
|
9825 | hasQualityInfo = false;
|
9826 | }
|
9827 |
|
9828 | if (hasQualityInfo) {
|
9829 | info.decodedFrames = decoded;
|
9830 | info.droppedFrames = dropped;
|
9831 | }
|
9832 |
|
9833 | return info;
|
9834 | }
|
9835 | }]);
|
9836 |
|
9837 | return NativePlayer;
|
9838 | }();
|
9839 |
|
9840 | exports.default = NativePlayer;
|
9841 |
|
9842 | },{"../config.js":5,"../utils/exception.js":40,"./player-events.js":35,"events":2}],34:[function(_dereq_,module,exports){
|
9843 | ;
|
9844 |
|
9845 | Object.defineProperty(exports, "__esModule", {
|
9846 | value: true
|
9847 | });
|
9848 | exports.ErrorDetails = exports.ErrorTypes = undefined;
|
9849 |
|
9850 | var _loader = _dereq_('../io/loader.js');
|
9851 |
|
9852 | var _demuxErrors = _dereq_('../demux/demux-errors.js');
|
9853 |
|
9854 | var _demuxErrors2 = _interopRequireDefault(_demuxErrors);
|
9855 |
|
9856 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
9857 |
|
9858 | /*
|
9859 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
9860 | *
|
9861 | * @author zheng qian <xqq@xqq.im>
|
9862 | *
|
9863 | * Licensed under the Apache License, Version 2.0 (the "License");
|
9864 | * you may not use this file except in compliance with the License.
|
9865 | * You may obtain a copy of the License at
|
9866 | *
|
9867 | * http://www.apache.org/licenses/LICENSE-2.0
|
9868 | *
|
9869 | * Unless required by applicable law or agreed to in writing, software
|
9870 | * distributed under the License is distributed on an "AS IS" BASIS,
|
9871 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
9872 | * See the License for the specific language governing permissions and
|
9873 | * limitations under the License.
|
9874 | */
|
9875 |
|
9876 | var ErrorTypes = exports.ErrorTypes = {
|
9877 | NETWORK_ERROR: 'NetworkError',
|
9878 | MEDIA_ERROR: 'MediaError',
|
9879 | OTHER_ERROR: 'OtherError'
|
9880 | };
|
9881 |
|
9882 | var ErrorDetails = exports.ErrorDetails = {
|
9883 | NETWORK_EXCEPTION: _loader.LoaderErrors.EXCEPTION,
|
9884 | NETWORK_STATUS_CODE_INVALID: _loader.LoaderErrors.HTTP_STATUS_CODE_INVALID,
|
9885 | NETWORK_TIMEOUT: _loader.LoaderErrors.CONNECTING_TIMEOUT,
|
9886 | NETWORK_UNRECOVERABLE_EARLY_EOF: _loader.LoaderErrors.UNRECOVERABLE_EARLY_EOF,
|
9887 |
|
9888 | MEDIA_MSE_ERROR: 'MediaMSEError',
|
9889 |
|
9890 | MEDIA_FORMAT_ERROR: _demuxErrors2.default.FORMAT_ERROR,
|
9891 | MEDIA_FORMAT_UNSUPPORTED: _demuxErrors2.default.FORMAT_UNSUPPORTED,
|
9892 | MEDIA_CODEC_UNSUPPORTED: _demuxErrors2.default.CODEC_UNSUPPORTED
|
9893 | };
|
9894 |
|
9895 | },{"../demux/demux-errors.js":16,"../io/loader.js":24}],35:[function(_dereq_,module,exports){
|
9896 | ;
|
9897 |
|
9898 | Object.defineProperty(exports, "__esModule", {
|
9899 | value: true
|
9900 | });
|
9901 | /*
|
9902 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
9903 | *
|
9904 | * @author zheng qian <xqq@xqq.im>
|
9905 | *
|
9906 | * Licensed under the Apache License, Version 2.0 (the "License");
|
9907 | * you may not use this file except in compliance with the License.
|
9908 | * You may obtain a copy of the License at
|
9909 | *
|
9910 | * http://www.apache.org/licenses/LICENSE-2.0
|
9911 | *
|
9912 | * Unless required by applicable law or agreed to in writing, software
|
9913 | * distributed under the License is distributed on an "AS IS" BASIS,
|
9914 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
9915 | * See the License for the specific language governing permissions and
|
9916 | * limitations under the License.
|
9917 | */
|
9918 |
|
9919 | var PlayerEvents = {
|
9920 | ERROR: 'error',
|
9921 | LOADING_COMPLETE: 'loading_complete',
|
9922 | RECOVERED_EARLY_EOF: 'recovered_early_eof',
|
9923 | MEDIA_INFO: 'media_info',
|
9924 | METADATA_ARRIVED: 'metadata_arrived',
|
9925 | SCRIPTDATA_ARRIVED: 'scriptdata_arrived',
|
9926 | STATISTICS_INFO: 'statistics_info'
|
9927 | };
|
9928 |
|
9929 | exports.default = PlayerEvents;
|
9930 |
|
9931 | },{}],36:[function(_dereq_,module,exports){
|
9932 | ;
|
9933 |
|
9934 | Object.defineProperty(exports, "__esModule", {
|
9935 | value: true
|
9936 | });
|
9937 |
|
9938 | 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; }; }();
|
9939 |
|
9940 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
9941 |
|
9942 | /*
|
9943 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
9944 | *
|
9945 | * This file is modified from dailymotion's hls.js library (hls.js/src/helper/aac.js)
|
9946 | * @author zheng qian <xqq@xqq.im>
|
9947 | *
|
9948 | * Licensed under the Apache License, Version 2.0 (the "License");
|
9949 | * you may not use this file except in compliance with the License.
|
9950 | * You may obtain a copy of the License at
|
9951 | *
|
9952 | * http://www.apache.org/licenses/LICENSE-2.0
|
9953 | *
|
9954 | * Unless required by applicable law or agreed to in writing, software
|
9955 | * distributed under the License is distributed on an "AS IS" BASIS,
|
9956 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
9957 | * See the License for the specific language governing permissions and
|
9958 | * limitations under the License.
|
9959 | */
|
9960 |
|
9961 | var AAC = function () {
|
9962 | function AAC() {
|
9963 | _classCallCheck(this, AAC);
|
9964 | }
|
9965 |
|
9966 | _createClass(AAC, null, [{
|
9967 | key: 'getSilentFrame',
|
9968 | value: function getSilentFrame(codec, channelCount) {
|
9969 | if (codec === 'mp4a.40.2') {
|
9970 | // handle LC-AAC
|
9971 | if (channelCount === 1) {
|
9972 | return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x23, 0x80]);
|
9973 | } else if (channelCount === 2) {
|
9974 | return new Uint8Array([0x21, 0x00, 0x49, 0x90, 0x02, 0x19, 0x00, 0x23, 0x80]);
|
9975 | } else if (channelCount === 3) {
|
9976 | return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x8e]);
|
9977 | } else if (channelCount === 4) {
|
9978 | return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x80, 0x2c, 0x80, 0x08, 0x02, 0x38]);
|
9979 | } else if (channelCount === 5) {
|
9980 | return new Uint8Array([0x00, 0xc8, 0x00, 0x80, 0x20, 0x84, 0x01, 0x26, 0x40, 0x08, 0x64, 0x00, 0x82, 0x30, 0x04, 0x99, 0x00, 0x21, 0x90, 0x02, 0x38]);
|
9981 | } else if (channelCount === 6) {
|
9982 | 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]);
|
9983 | }
|
9984 | } else {
|
9985 | // handle HE-AAC (mp4a.40.5 / mp4a.40.29)
|
9986 | if (channelCount === 1) {
|
9987 | // 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
|
9988 | 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]);
|
9989 | } else if (channelCount === 2) {
|
9990 | // 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
|
9991 | 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]);
|
9992 | } else if (channelCount === 3) {
|
9993 | // 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
|
9994 | 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]);
|
9995 | }
|
9996 | }
|
9997 | return null;
|
9998 | }
|
9999 | }]);
|
10000 |
|
10001 | return AAC;
|
10002 | }();
|
10003 |
|
10004 | exports.default = AAC;
|
10005 |
|
10006 | },{}],37:[function(_dereq_,module,exports){
|
10007 | ;
|
10008 |
|
10009 | Object.defineProperty(exports, "__esModule", {
|
10010 | value: true
|
10011 | });
|
10012 |
|
10013 | 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; }; }();
|
10014 |
|
10015 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
10016 |
|
10017 | /*
|
10018 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
10019 | *
|
10020 | * This file is derived from dailymotion's hls.js library (hls.js/src/remux/mp4-generator.js)
|
10021 | * @author zheng qian <xqq@xqq.im>
|
10022 | *
|
10023 | * Licensed under the Apache License, Version 2.0 (the "License");
|
10024 | * you may not use this file except in compliance with the License.
|
10025 | * You may obtain a copy of the License at
|
10026 | *
|
10027 | * http://www.apache.org/licenses/LICENSE-2.0
|
10028 | *
|
10029 | * Unless required by applicable law or agreed to in writing, software
|
10030 | * distributed under the License is distributed on an "AS IS" BASIS,
|
10031 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10032 | * See the License for the specific language governing permissions and
|
10033 | * limitations under the License.
|
10034 | */
|
10035 |
|
10036 | // MP4 boxes generator for ISO BMFF (ISO Base Media File Format, defined in ISO/IEC 14496-12)
|
10037 | var MP4 = function () {
|
10038 | function MP4() {
|
10039 | _classCallCheck(this, MP4);
|
10040 | }
|
10041 |
|
10042 | _createClass(MP4, null, [{
|
10043 | key: 'init',
|
10044 | value: function init() {
|
10045 | MP4.types = {
|
10046 | avc1: [], avcC: [], btrt: [], dinf: [],
|
10047 | dref: [], esds: [], ftyp: [], hdlr: [],
|
10048 | mdat: [], mdhd: [], mdia: [], mfhd: [],
|
10049 | minf: [], moof: [], moov: [], mp4a: [],
|
10050 | mvex: [], mvhd: [], sdtp: [], stbl: [],
|
10051 | stco: [], stsc: [], stsd: [], stsz: [],
|
10052 | stts: [], tfdt: [], tfhd: [], traf: [],
|
10053 | trak: [], trun: [], trex: [], tkhd: [],
|
10054 | vmhd: [], smhd: [], '.mp3': []
|
10055 | };
|
10056 |
|
10057 | for (var name in MP4.types) {
|
10058 | if (MP4.types.hasOwnProperty(name)) {
|
10059 | MP4.types[name] = [name.charCodeAt(0), name.charCodeAt(1), name.charCodeAt(2), name.charCodeAt(3)];
|
10060 | }
|
10061 | }
|
10062 |
|
10063 | var constants = MP4.constants = {};
|
10064 |
|
10065 | constants.FTYP = new Uint8Array([0x69, 0x73, 0x6F, 0x6D, // major_brand: isom
|
10066 | 0x0, 0x0, 0x0, 0x1, // minor_version: 0x01
|
10067 | 0x69, 0x73, 0x6F, 0x6D, // isom
|
10068 | 0x61, 0x76, 0x63, 0x31 // avc1
|
10069 | ]);
|
10070 |
|
10071 | constants.STSD_PREFIX = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
10072 | 0x00, 0x00, 0x00, 0x01 // entry_count
|
10073 | ]);
|
10074 |
|
10075 | constants.STTS = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
10076 | 0x00, 0x00, 0x00, 0x00 // entry_count
|
10077 | ]);
|
10078 |
|
10079 | constants.STSC = constants.STCO = constants.STTS;
|
10080 |
|
10081 | constants.STSZ = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
10082 | 0x00, 0x00, 0x00, 0x00, // sample_size
|
10083 | 0x00, 0x00, 0x00, 0x00 // sample_count
|
10084 | ]);
|
10085 |
|
10086 | constants.HDLR_VIDEO = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
10087 | 0x00, 0x00, 0x00, 0x00, // pre_defined
|
10088 | 0x76, 0x69, 0x64, 0x65, // handler_type: 'vide'
|
10089 | 0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes
|
10090 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x56, 0x69, 0x64, 0x65, 0x6F, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x00 // name: VideoHandler
|
10091 | ]);
|
10092 |
|
10093 | constants.HDLR_AUDIO = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
10094 | 0x00, 0x00, 0x00, 0x00, // pre_defined
|
10095 | 0x73, 0x6F, 0x75, 0x6E, // handler_type: 'soun'
|
10096 | 0x00, 0x00, 0x00, 0x00, // reserved: 3 * 4 bytes
|
10097 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x53, 0x6F, 0x75, 0x6E, 0x64, 0x48, 0x61, 0x6E, 0x64, 0x6C, 0x65, 0x72, 0x00 // name: SoundHandler
|
10098 | ]);
|
10099 |
|
10100 | constants.DREF = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
10101 | 0x00, 0x00, 0x00, 0x01, // entry_count
|
10102 | 0x00, 0x00, 0x00, 0x0C, // entry_size
|
10103 | 0x75, 0x72, 0x6C, 0x20, // type 'url '
|
10104 | 0x00, 0x00, 0x00, 0x01 // version(0) + flags
|
10105 | ]);
|
10106 |
|
10107 | // Sound media header
|
10108 | constants.SMHD = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
10109 | 0x00, 0x00, 0x00, 0x00 // balance(2) + reserved(2)
|
10110 | ]);
|
10111 |
|
10112 | // video media header
|
10113 | constants.VMHD = new Uint8Array([0x00, 0x00, 0x00, 0x01, // version(0) + flags
|
10114 | 0x00, 0x00, // graphicsmode: 2 bytes
|
10115 | 0x00, 0x00, 0x00, 0x00, // opcolor: 3 * 2 bytes
|
10116 | 0x00, 0x00]);
|
10117 | }
|
10118 |
|
10119 | // Generate a box
|
10120 |
|
10121 | }, {
|
10122 | key: 'box',
|
10123 | value: function box(type) {
|
10124 | var size = 8;
|
10125 | var result = null;
|
10126 | var datas = Array.prototype.slice.call(arguments, 1);
|
10127 | var arrayCount = datas.length;
|
10128 |
|
10129 | for (var i = 0; i < arrayCount; i++) {
|
10130 | size += datas[i].byteLength;
|
10131 | }
|
10132 |
|
10133 | result = new Uint8Array(size);
|
10134 | result[0] = size >>> 24 & 0xFF; // size
|
10135 | result[1] = size >>> 16 & 0xFF;
|
10136 | result[2] = size >>> 8 & 0xFF;
|
10137 | result[3] = size & 0xFF;
|
10138 |
|
10139 | result.set(type, 4); // type
|
10140 |
|
10141 | var offset = 8;
|
10142 | for (var _i = 0; _i < arrayCount; _i++) {
|
10143 | // data body
|
10144 | result.set(datas[_i], offset);
|
10145 | offset += datas[_i].byteLength;
|
10146 | }
|
10147 |
|
10148 | return result;
|
10149 | }
|
10150 |
|
10151 | // emit ftyp & moov
|
10152 |
|
10153 | }, {
|
10154 | key: 'generateInitSegment',
|
10155 | value: function generateInitSegment(meta) {
|
10156 | var ftyp = MP4.box(MP4.types.ftyp, MP4.constants.FTYP);
|
10157 | var moov = MP4.moov(meta);
|
10158 |
|
10159 | var result = new Uint8Array(ftyp.byteLength + moov.byteLength);
|
10160 | result.set(ftyp, 0);
|
10161 | result.set(moov, ftyp.byteLength);
|
10162 | return result;
|
10163 | }
|
10164 |
|
10165 | // Movie metadata box
|
10166 |
|
10167 | }, {
|
10168 | key: 'moov',
|
10169 | value: function moov(meta) {
|
10170 | var mvhd = MP4.mvhd(meta.timescale, meta.duration);
|
10171 | var trak = MP4.trak(meta);
|
10172 | var mvex = MP4.mvex(meta);
|
10173 | return MP4.box(MP4.types.moov, mvhd, trak, mvex);
|
10174 | }
|
10175 |
|
10176 | // Movie header box
|
10177 |
|
10178 | }, {
|
10179 | key: 'mvhd',
|
10180 | value: function mvhd(timescale, duration) {
|
10181 | return MP4.box(MP4.types.mvhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
10182 | 0x00, 0x00, 0x00, 0x00, // creation_time
|
10183 | 0x00, 0x00, 0x00, 0x00, // modification_time
|
10184 | timescale >>> 24 & 0xFF, // timescale: 4 bytes
|
10185 | timescale >>> 16 & 0xFF, timescale >>> 8 & 0xFF, timescale & 0xFF, duration >>> 24 & 0xFF, // duration: 4 bytes
|
10186 | duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, 0x00, 0x01, 0x00, 0x00, // Preferred rate: 1.0
|
10187 | 0x01, 0x00, 0x00, 0x00, // PreferredVolume(1.0, 2bytes) + reserved(2bytes)
|
10188 | 0x00, 0x00, 0x00, 0x00, // reserved: 4 + 4 bytes
|
10189 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, // ----begin composition matrix----
|
10190 | 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----
|
10191 | 0x00, 0x00, 0x00, 0x00, // ----begin pre_defined 6 * 4 bytes----
|
10192 | 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----
|
10193 | 0xFF, 0xFF, 0xFF, 0xFF // next_track_ID
|
10194 | ]));
|
10195 | }
|
10196 |
|
10197 | // Track box
|
10198 |
|
10199 | }, {
|
10200 | key: 'trak',
|
10201 | value: function trak(meta) {
|
10202 | return MP4.box(MP4.types.trak, MP4.tkhd(meta), MP4.mdia(meta));
|
10203 | }
|
10204 |
|
10205 | // Track header box
|
10206 |
|
10207 | }, {
|
10208 | key: 'tkhd',
|
10209 | value: function tkhd(meta) {
|
10210 | var trackId = meta.id,
|
10211 | duration = meta.duration;
|
10212 | var width = meta.presentWidth,
|
10213 | height = meta.presentHeight;
|
10214 |
|
10215 | return MP4.box(MP4.types.tkhd, new Uint8Array([0x00, 0x00, 0x00, 0x07, // version(0) + flags
|
10216 | 0x00, 0x00, 0x00, 0x00, // creation_time
|
10217 | 0x00, 0x00, 0x00, 0x00, // modification_time
|
10218 | trackId >>> 24 & 0xFF, // track_ID: 4 bytes
|
10219 | trackId >>> 16 & 0xFF, trackId >>> 8 & 0xFF, trackId & 0xFF, 0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes
|
10220 | duration >>> 24 & 0xFF, // duration: 4 bytes
|
10221 | duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
|
10222 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // layer(2bytes) + alternate_group(2bytes)
|
10223 | 0x00, 0x00, 0x00, 0x00, // volume(2bytes) + reserved(2bytes)
|
10224 | 0x00, 0x01, 0x00, 0x00, // ----begin composition matrix----
|
10225 | 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----
|
10226 | width >>> 8 & 0xFF, // width and height
|
10227 | width & 0xFF, 0x00, 0x00, height >>> 8 & 0xFF, height & 0xFF, 0x00, 0x00]));
|
10228 | }
|
10229 |
|
10230 | // Media Box
|
10231 |
|
10232 | }, {
|
10233 | key: 'mdia',
|
10234 | value: function mdia(meta) {
|
10235 | return MP4.box(MP4.types.mdia, MP4.mdhd(meta), MP4.hdlr(meta), MP4.minf(meta));
|
10236 | }
|
10237 |
|
10238 | // Media header box
|
10239 |
|
10240 | }, {
|
10241 | key: 'mdhd',
|
10242 | value: function mdhd(meta) {
|
10243 | var timescale = meta.timescale;
|
10244 | var duration = meta.duration;
|
10245 | return MP4.box(MP4.types.mdhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
10246 | 0x00, 0x00, 0x00, 0x00, // creation_time
|
10247 | 0x00, 0x00, 0x00, 0x00, // modification_time
|
10248 | timescale >>> 24 & 0xFF, // timescale: 4 bytes
|
10249 | timescale >>> 16 & 0xFF, timescale >>> 8 & 0xFF, timescale & 0xFF, duration >>> 24 & 0xFF, // duration: 4 bytes
|
10250 | duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, 0x55, 0xC4, // language: und (undetermined)
|
10251 | 0x00, 0x00 // pre_defined = 0
|
10252 | ]));
|
10253 | }
|
10254 |
|
10255 | // Media handler reference box
|
10256 |
|
10257 | }, {
|
10258 | key: 'hdlr',
|
10259 | value: function hdlr(meta) {
|
10260 | var data = null;
|
10261 | if (meta.type === 'audio') {
|
10262 | data = MP4.constants.HDLR_AUDIO;
|
10263 | } else {
|
10264 | data = MP4.constants.HDLR_VIDEO;
|
10265 | }
|
10266 | return MP4.box(MP4.types.hdlr, data);
|
10267 | }
|
10268 |
|
10269 | // Media infomation box
|
10270 |
|
10271 | }, {
|
10272 | key: 'minf',
|
10273 | value: function minf(meta) {
|
10274 | var xmhd = null;
|
10275 | if (meta.type === 'audio') {
|
10276 | xmhd = MP4.box(MP4.types.smhd, MP4.constants.SMHD);
|
10277 | } else {
|
10278 | xmhd = MP4.box(MP4.types.vmhd, MP4.constants.VMHD);
|
10279 | }
|
10280 | return MP4.box(MP4.types.minf, xmhd, MP4.dinf(), MP4.stbl(meta));
|
10281 | }
|
10282 |
|
10283 | // Data infomation box
|
10284 |
|
10285 | }, {
|
10286 | key: 'dinf',
|
10287 | value: function dinf() {
|
10288 | var result = MP4.box(MP4.types.dinf, MP4.box(MP4.types.dref, MP4.constants.DREF));
|
10289 | return result;
|
10290 | }
|
10291 |
|
10292 | // Sample table box
|
10293 |
|
10294 | }, {
|
10295 | key: 'stbl',
|
10296 | value: function stbl(meta) {
|
10297 | var result = MP4.box(MP4.types.stbl, // type: stbl
|
10298 | MP4.stsd(meta), // Sample Description Table
|
10299 | MP4.box(MP4.types.stts, MP4.constants.STTS), // Time-To-Sample
|
10300 | MP4.box(MP4.types.stsc, MP4.constants.STSC), // Sample-To-Chunk
|
10301 | MP4.box(MP4.types.stsz, MP4.constants.STSZ), // Sample size
|
10302 | MP4.box(MP4.types.stco, MP4.constants.STCO // Chunk offset
|
10303 | ));
|
10304 | return result;
|
10305 | }
|
10306 |
|
10307 | // Sample description box
|
10308 |
|
10309 | }, {
|
10310 | key: 'stsd',
|
10311 | value: function stsd(meta) {
|
10312 | if (meta.type === 'audio') {
|
10313 | if (meta.codec === 'mp3') {
|
10314 | return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp3(meta));
|
10315 | }
|
10316 | // else: aac -> mp4a
|
10317 | return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.mp4a(meta));
|
10318 | } else {
|
10319 | return MP4.box(MP4.types.stsd, MP4.constants.STSD_PREFIX, MP4.avc1(meta));
|
10320 | }
|
10321 | }
|
10322 | }, {
|
10323 | key: 'mp3',
|
10324 | value: function mp3(meta) {
|
10325 | var channelCount = meta.channelCount;
|
10326 | var sampleRate = meta.audioSampleRate;
|
10327 |
|
10328 | var data = new Uint8Array([0x00, 0x00, 0x00, 0x00, // reserved(4)
|
10329 | 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
10330 | 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
|
10331 | 0x00, 0x00, 0x00, 0x00, 0x00, channelCount, // channelCount(2)
|
10332 | 0x00, 0x10, // sampleSize(2)
|
10333 | 0x00, 0x00, 0x00, 0x00, // reserved(4)
|
10334 | sampleRate >>> 8 & 0xFF, // Audio sample rate
|
10335 | sampleRate & 0xFF, 0x00, 0x00]);
|
10336 |
|
10337 | return MP4.box(MP4.types['.mp3'], data);
|
10338 | }
|
10339 | }, {
|
10340 | key: 'mp4a',
|
10341 | value: function mp4a(meta) {
|
10342 | var channelCount = meta.channelCount;
|
10343 | var sampleRate = meta.audioSampleRate;
|
10344 |
|
10345 | var data = new Uint8Array([0x00, 0x00, 0x00, 0x00, // reserved(4)
|
10346 | 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
10347 | 0x00, 0x00, 0x00, 0x00, // reserved: 2 * 4 bytes
|
10348 | 0x00, 0x00, 0x00, 0x00, 0x00, channelCount, // channelCount(2)
|
10349 | 0x00, 0x10, // sampleSize(2)
|
10350 | 0x00, 0x00, 0x00, 0x00, // reserved(4)
|
10351 | sampleRate >>> 8 & 0xFF, // Audio sample rate
|
10352 | sampleRate & 0xFF, 0x00, 0x00]);
|
10353 |
|
10354 | return MP4.box(MP4.types.mp4a, data, MP4.esds(meta));
|
10355 | }
|
10356 | }, {
|
10357 | key: 'esds',
|
10358 | value: function esds(meta) {
|
10359 | var config = meta.config || [];
|
10360 | var configSize = config.length;
|
10361 | var data = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version 0 + flags
|
10362 |
|
10363 | 0x03, // descriptor_type
|
10364 | 0x17 + configSize, // length3
|
10365 | 0x00, 0x01, // es_id
|
10366 | 0x00, // stream_priority
|
10367 |
|
10368 | 0x04, // descriptor_type
|
10369 | 0x0F + configSize, // length
|
10370 | 0x40, // codec: mpeg4_audio
|
10371 | 0x15, // stream_type: Audio
|
10372 | 0x00, 0x00, 0x00, // buffer_size
|
10373 | 0x00, 0x00, 0x00, 0x00, // maxBitrate
|
10374 | 0x00, 0x00, 0x00, 0x00, // avgBitrate
|
10375 |
|
10376 | 0x05 // descriptor_type
|
10377 | ].concat([configSize]).concat(config).concat([0x06, 0x01, 0x02 // GASpecificConfig
|
10378 | ]));
|
10379 | return MP4.box(MP4.types.esds, data);
|
10380 | }
|
10381 | }, {
|
10382 | key: 'avc1',
|
10383 | value: function avc1(meta) {
|
10384 | var avcc = meta.avcc;
|
10385 | var width = meta.codecWidth,
|
10386 | height = meta.codecHeight;
|
10387 |
|
10388 | var data = new Uint8Array([0x00, 0x00, 0x00, 0x00, // reserved(4)
|
10389 | 0x00, 0x00, 0x00, 0x01, // reserved(2) + data_reference_index(2)
|
10390 | 0x00, 0x00, 0x00, 0x00, // pre_defined(2) + reserved(2)
|
10391 | 0x00, 0x00, 0x00, 0x00, // pre_defined: 3 * 4 bytes
|
10392 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, width >>> 8 & 0xFF, // width: 2 bytes
|
10393 | width & 0xFF, height >>> 8 & 0xFF, // height: 2 bytes
|
10394 | height & 0xFF, 0x00, 0x48, 0x00, 0x00, // horizresolution: 4 bytes
|
10395 | 0x00, 0x48, 0x00, 0x00, // vertresolution: 4 bytes
|
10396 | 0x00, 0x00, 0x00, 0x00, // reserved: 4 bytes
|
10397 | 0x00, 0x01, // frame_count
|
10398 | 0x0A, // strlen
|
10399 | 0x78, 0x71, 0x71, 0x2F, // compressorname: 32 bytes
|
10400 | 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
|
10401 | 0xFF, 0xFF // pre_defined = -1
|
10402 | ]);
|
10403 | return MP4.box(MP4.types.avc1, data, MP4.box(MP4.types.avcC, avcc));
|
10404 | }
|
10405 |
|
10406 | // Movie Extends box
|
10407 |
|
10408 | }, {
|
10409 | key: 'mvex',
|
10410 | value: function mvex(meta) {
|
10411 | return MP4.box(MP4.types.mvex, MP4.trex(meta));
|
10412 | }
|
10413 |
|
10414 | // Track Extends box
|
10415 |
|
10416 | }, {
|
10417 | key: 'trex',
|
10418 | value: function trex(meta) {
|
10419 | var trackId = meta.id;
|
10420 | var data = new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) + flags
|
10421 | trackId >>> 24 & 0xFF, // track_ID
|
10422 | trackId >>> 16 & 0xFF, trackId >>> 8 & 0xFF, trackId & 0xFF, 0x00, 0x00, 0x00, 0x01, // default_sample_description_index
|
10423 | 0x00, 0x00, 0x00, 0x00, // default_sample_duration
|
10424 | 0x00, 0x00, 0x00, 0x00, // default_sample_size
|
10425 | 0x00, 0x01, 0x00, 0x01 // default_sample_flags
|
10426 | ]);
|
10427 | return MP4.box(MP4.types.trex, data);
|
10428 | }
|
10429 |
|
10430 | // Movie fragment box
|
10431 |
|
10432 | }, {
|
10433 | key: 'moof',
|
10434 | value: function moof(track, baseMediaDecodeTime) {
|
10435 | return MP4.box(MP4.types.moof, MP4.mfhd(track.sequenceNumber), MP4.traf(track, baseMediaDecodeTime));
|
10436 | }
|
10437 | }, {
|
10438 | key: 'mfhd',
|
10439 | value: function mfhd(sequenceNumber) {
|
10440 | var data = new Uint8Array([0x00, 0x00, 0x00, 0x00, sequenceNumber >>> 24 & 0xFF, // sequence_number: int32
|
10441 | sequenceNumber >>> 16 & 0xFF, sequenceNumber >>> 8 & 0xFF, sequenceNumber & 0xFF]);
|
10442 | return MP4.box(MP4.types.mfhd, data);
|
10443 | }
|
10444 |
|
10445 | // Track fragment box
|
10446 |
|
10447 | }, {
|
10448 | key: 'traf',
|
10449 | value: function traf(track, baseMediaDecodeTime) {
|
10450 | var trackId = track.id;
|
10451 |
|
10452 | // Track fragment header box
|
10453 | var tfhd = MP4.box(MP4.types.tfhd, new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) & flags
|
10454 | trackId >>> 24 & 0xFF, // track_ID
|
10455 | trackId >>> 16 & 0xFF, trackId >>> 8 & 0xFF, trackId & 0xFF]));
|
10456 | // Track Fragment Decode Time
|
10457 | var tfdt = MP4.box(MP4.types.tfdt, new Uint8Array([0x00, 0x00, 0x00, 0x00, // version(0) & flags
|
10458 | baseMediaDecodeTime >>> 24 & 0xFF, // baseMediaDecodeTime: int32
|
10459 | baseMediaDecodeTime >>> 16 & 0xFF, baseMediaDecodeTime >>> 8 & 0xFF, baseMediaDecodeTime & 0xFF]));
|
10460 | var sdtp = MP4.sdtp(track);
|
10461 | var trun = MP4.trun(track, sdtp.byteLength + 16 + 16 + 8 + 16 + 8 + 8);
|
10462 |
|
10463 | return MP4.box(MP4.types.traf, tfhd, tfdt, trun, sdtp);
|
10464 | }
|
10465 |
|
10466 | // Sample Dependency Type box
|
10467 |
|
10468 | }, {
|
10469 | key: 'sdtp',
|
10470 | value: function sdtp(track) {
|
10471 | var samples = track.samples || [];
|
10472 | var sampleCount = samples.length;
|
10473 | var data = new Uint8Array(4 + sampleCount);
|
10474 | // 0~4 bytes: version(0) & flags
|
10475 | for (var i = 0; i < sampleCount; i++) {
|
10476 | var flags = samples[i].flags;
|
10477 | data[i + 4] = flags.isLeading << 6 | // is_leading: 2 (bit)
|
10478 | flags.dependsOn << 4 // sample_depends_on
|
10479 | | flags.isDependedOn << 2 // sample_is_depended_on
|
10480 | | flags.hasRedundancy; // sample_has_redundancy
|
10481 | }
|
10482 | return MP4.box(MP4.types.sdtp, data);
|
10483 | }
|
10484 |
|
10485 | // Track fragment run box
|
10486 |
|
10487 | }, {
|
10488 | key: 'trun',
|
10489 | value: function trun(track, offset) {
|
10490 | var samples = track.samples || [];
|
10491 | var sampleCount = samples.length;
|
10492 | var dataSize = 12 + 16 * sampleCount;
|
10493 | var data = new Uint8Array(dataSize);
|
10494 | offset += 8 + dataSize;
|
10495 |
|
10496 | data.set([0x00, 0x00, 0x0F, 0x01, // version(0) & flags
|
10497 | sampleCount >>> 24 & 0xFF, // sample_count
|
10498 | sampleCount >>> 16 & 0xFF, sampleCount >>> 8 & 0xFF, sampleCount & 0xFF, offset >>> 24 & 0xFF, // data_offset
|
10499 | offset >>> 16 & 0xFF, offset >>> 8 & 0xFF, offset & 0xFF], 0);
|
10500 |
|
10501 | for (var i = 0; i < sampleCount; i++) {
|
10502 | var duration = samples[i].duration;
|
10503 | var size = samples[i].size;
|
10504 | var flags = samples[i].flags;
|
10505 | var cts = samples[i].cts;
|
10506 | data.set([duration >>> 24 & 0xFF, // sample_duration
|
10507 | duration >>> 16 & 0xFF, duration >>> 8 & 0xFF, duration & 0xFF, size >>> 24 & 0xFF, // sample_size
|
10508 | size >>> 16 & 0xFF, size >>> 8 & 0xFF, size & 0xFF, flags.isLeading << 2 | flags.dependsOn, // sample_flags
|
10509 | flags.isDependedOn << 6 | flags.hasRedundancy << 4 | flags.isNonSync, 0x00, 0x00, // sample_degradation_priority
|
10510 | cts >>> 24 & 0xFF, // sample_composition_time_offset
|
10511 | cts >>> 16 & 0xFF, cts >>> 8 & 0xFF, cts & 0xFF], 12 + 16 * i);
|
10512 | }
|
10513 | return MP4.box(MP4.types.trun, data);
|
10514 | }
|
10515 | }, {
|
10516 | key: 'mdat',
|
10517 | value: function mdat(data) {
|
10518 | return MP4.box(MP4.types.mdat, data);
|
10519 | }
|
10520 | }]);
|
10521 |
|
10522 | return MP4;
|
10523 | }();
|
10524 |
|
10525 | MP4.init();
|
10526 |
|
10527 | exports.default = MP4;
|
10528 |
|
10529 | },{}],38:[function(_dereq_,module,exports){
|
10530 | ;
|
10531 |
|
10532 | Object.defineProperty(exports, "__esModule", {
|
10533 | value: true
|
10534 | });
|
10535 |
|
10536 | 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; }; }(); /*
|
10537 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
10538 | *
|
10539 | * @author zheng qian <xqq@xqq.im>
|
10540 | *
|
10541 | * Licensed under the Apache License, Version 2.0 (the "License");
|
10542 | * you may not use this file except in compliance with the License.
|
10543 | * You may obtain a copy of the License at
|
10544 | *
|
10545 | * http://www.apache.org/licenses/LICENSE-2.0
|
10546 | *
|
10547 | * Unless required by applicable law or agreed to in writing, software
|
10548 | * distributed under the License is distributed on an "AS IS" BASIS,
|
10549 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10550 | * See the License for the specific language governing permissions and
|
10551 | * limitations under the License.
|
10552 | */
|
10553 |
|
10554 | var _logger = _dereq_('../utils/logger.js');
|
10555 |
|
10556 | var _logger2 = _interopRequireDefault(_logger);
|
10557 |
|
10558 | var _mp4Generator = _dereq_('./mp4-generator.js');
|
10559 |
|
10560 | var _mp4Generator2 = _interopRequireDefault(_mp4Generator);
|
10561 |
|
10562 | var _aacSilent = _dereq_('./aac-silent.js');
|
10563 |
|
10564 | var _aacSilent2 = _interopRequireDefault(_aacSilent);
|
10565 |
|
10566 | var _browser = _dereq_('../utils/browser.js');
|
10567 |
|
10568 | var _browser2 = _interopRequireDefault(_browser);
|
10569 |
|
10570 | var _mediaSegmentInfo = _dereq_('../core/media-segment-info.js');
|
10571 |
|
10572 | var _exception = _dereq_('../utils/exception.js');
|
10573 |
|
10574 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
10575 |
|
10576 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
10577 |
|
10578 | // Fragmented mp4 remuxer
|
10579 | var MP4Remuxer = function () {
|
10580 | function MP4Remuxer(config) {
|
10581 | _classCallCheck(this, MP4Remuxer);
|
10582 |
|
10583 | this.TAG = 'MP4Remuxer';
|
10584 |
|
10585 | this._config = config;
|
10586 | this._isLive = config.isLive === true ? true : false;
|
10587 |
|
10588 | this._dtsBase = -1;
|
10589 | this._dtsBaseInited = false;
|
10590 | this._audioDtsBase = Infinity;
|
10591 | this._videoDtsBase = Infinity;
|
10592 | this._audioNextDts = undefined;
|
10593 | this._videoNextDts = undefined;
|
10594 | this._audioStashedLastSample = null;
|
10595 | this._videoStashedLastSample = null;
|
10596 |
|
10597 | this._audioMeta = null;
|
10598 | this._videoMeta = null;
|
10599 |
|
10600 | this._audioSegmentInfoList = new _mediaSegmentInfo.MediaSegmentInfoList('audio');
|
10601 | this._videoSegmentInfoList = new _mediaSegmentInfo.MediaSegmentInfoList('video');
|
10602 |
|
10603 | this._onInitSegment = null;
|
10604 | this._onMediaSegment = null;
|
10605 |
|
10606 | // Workaround for chrome < 50: Always force first sample as a Random Access Point in media segment
|
10607 | // see https://bugs.chromium.org/p/chromium/issues/detail?id=229412
|
10608 | this._forceFirstIDR = _browser2.default.chrome && (_browser2.default.version.major < 50 || _browser2.default.version.major === 50 && _browser2.default.version.build < 2661) ? true : false;
|
10609 |
|
10610 | // Workaround for IE11/Edge: Fill silent aac frame after keyframe-seeking
|
10611 | // Make audio beginDts equals with video beginDts, in order to fix seek freeze
|
10612 | this._fillSilentAfterSeek = _browser2.default.msedge || _browser2.default.msie;
|
10613 |
|
10614 | // While only FireFox supports 'audio/mp4, codecs="mp3"', use 'audio/mpeg' for chrome, safari, ...
|
10615 | this._mp3UseMpegAudio = !_browser2.default.firefox;
|
10616 |
|
10617 | this._fillAudioTimestampGap = this._config.fixAudioTimestampGap;
|
10618 | }
|
10619 |
|
10620 | _createClass(MP4Remuxer, [{
|
10621 | key: 'destroy',
|
10622 | value: function destroy() {
|
10623 | this._dtsBase = -1;
|
10624 | this._dtsBaseInited = false;
|
10625 | this._audioMeta = null;
|
10626 | this._videoMeta = null;
|
10627 | this._audioSegmentInfoList.clear();
|
10628 | this._audioSegmentInfoList = null;
|
10629 | this._videoSegmentInfoList.clear();
|
10630 | this._videoSegmentInfoList = null;
|
10631 | this._onInitSegment = null;
|
10632 | this._onMediaSegment = null;
|
10633 | }
|
10634 | }, {
|
10635 | key: 'bindDataSource',
|
10636 | value: function bindDataSource(producer) {
|
10637 | producer.onDataAvailable = this.remux.bind(this);
|
10638 | producer.onTrackMetadata = this._onTrackMetadataReceived.bind(this);
|
10639 | return this;
|
10640 | }
|
10641 |
|
10642 | /* prototype: function onInitSegment(type: string, initSegment: ArrayBuffer): void
|
10643 | InitSegment: {
|
10644 | type: string,
|
10645 | data: ArrayBuffer,
|
10646 | codec: string,
|
10647 | container: string
|
10648 | }
|
10649 | */
|
10650 |
|
10651 | }, {
|
10652 | key: 'insertDiscontinuity',
|
10653 | value: function insertDiscontinuity() {
|
10654 | this._audioNextDts = this._videoNextDts = undefined;
|
10655 | }
|
10656 | }, {
|
10657 | key: 'seek',
|
10658 | value: function seek(originalDts) {
|
10659 | this._audioStashedLastSample = null;
|
10660 | this._videoStashedLastSample = null;
|
10661 | this._videoSegmentInfoList.clear();
|
10662 | this._audioSegmentInfoList.clear();
|
10663 | }
|
10664 | }, {
|
10665 | key: 'remux',
|
10666 | value: function remux(audioTrack, videoTrack) {
|
10667 | if (!this._onMediaSegment) {
|
10668 | throw new _exception.IllegalStateException('MP4Remuxer: onMediaSegment callback must be specificed!');
|
10669 | }
|
10670 | if (!this._dtsBaseInited) {
|
10671 | this._calculateDtsBase(audioTrack, videoTrack);
|
10672 | }
|
10673 | this._remuxVideo(videoTrack);
|
10674 | this._remuxAudio(audioTrack);
|
10675 | }
|
10676 | }, {
|
10677 | key: '_onTrackMetadataReceived',
|
10678 | value: function _onTrackMetadataReceived(type, metadata) {
|
10679 | var metabox = null;
|
10680 |
|
10681 | var container = 'mp4';
|
10682 | var codec = metadata.codec;
|
10683 |
|
10684 | if (type === 'audio') {
|
10685 | this._audioMeta = metadata;
|
10686 | if (metadata.codec === 'mp3' && this._mp3UseMpegAudio) {
|
10687 | // 'audio/mpeg' for MP3 audio track
|
10688 | container = 'mpeg';
|
10689 | codec = '';
|
10690 | metabox = new Uint8Array();
|
10691 | } else {
|
10692 | // 'audio/mp4, codecs="codec"'
|
10693 | metabox = _mp4Generator2.default.generateInitSegment(metadata);
|
10694 | }
|
10695 | } else if (type === 'video') {
|
10696 | this._videoMeta = metadata;
|
10697 | metabox = _mp4Generator2.default.generateInitSegment(metadata);
|
10698 | } else {
|
10699 | return;
|
10700 | }
|
10701 |
|
10702 | // dispatch metabox (Initialization Segment)
|
10703 | if (!this._onInitSegment) {
|
10704 | throw new _exception.IllegalStateException('MP4Remuxer: onInitSegment callback must be specified!');
|
10705 | }
|
10706 | this._onInitSegment(type, {
|
10707 | type: type,
|
10708 | data: metabox.buffer,
|
10709 | codec: codec,
|
10710 | container: type + '/' + container,
|
10711 | mediaDuration: metadata.duration // in timescale 1000 (milliseconds)
|
10712 | });
|
10713 | }
|
10714 | }, {
|
10715 | key: '_calculateDtsBase',
|
10716 | value: function _calculateDtsBase(audioTrack, videoTrack) {
|
10717 | if (this._dtsBaseInited) {
|
10718 | return;
|
10719 | }
|
10720 |
|
10721 | if (audioTrack.samples && audioTrack.samples.length) {
|
10722 | this._audioDtsBase = audioTrack.samples[0].dts;
|
10723 | }
|
10724 | if (videoTrack.samples && videoTrack.samples.length) {
|
10725 | this._videoDtsBase = videoTrack.samples[0].dts;
|
10726 | }
|
10727 |
|
10728 | this._dtsBase = Math.min(this._audioDtsBase, this._videoDtsBase);
|
10729 | this._dtsBaseInited = true;
|
10730 | }
|
10731 | }, {
|
10732 | key: 'flushStashedSamples',
|
10733 | value: function flushStashedSamples() {
|
10734 | var videoSample = this._videoStashedLastSample;
|
10735 | var audioSample = this._audioStashedLastSample;
|
10736 |
|
10737 | var videoTrack = {
|
10738 | type: 'video',
|
10739 | id: 1,
|
10740 | sequenceNumber: 0,
|
10741 | samples: [],
|
10742 | length: 0
|
10743 | };
|
10744 |
|
10745 | if (videoSample != null) {
|
10746 | videoTrack.samples.push(videoSample);
|
10747 | videoTrack.length = videoSample.length;
|
10748 | }
|
10749 |
|
10750 | var audioTrack = {
|
10751 | type: 'audio',
|
10752 | id: 2,
|
10753 | sequenceNumber: 0,
|
10754 | samples: [],
|
10755 | length: 0
|
10756 | };
|
10757 |
|
10758 | if (audioSample != null) {
|
10759 | audioTrack.samples.push(audioSample);
|
10760 | audioTrack.length = audioSample.length;
|
10761 | }
|
10762 |
|
10763 | this._videoStashedLastSample = null;
|
10764 | this._audioStashedLastSample = null;
|
10765 |
|
10766 | this._remuxVideo(videoTrack, true);
|
10767 | this._remuxAudio(audioTrack, true);
|
10768 | }
|
10769 | }, {
|
10770 | key: '_remuxAudio',
|
10771 | value: function _remuxAudio(audioTrack, force) {
|
10772 | if (this._audioMeta == null) {
|
10773 | return;
|
10774 | }
|
10775 |
|
10776 | var track = audioTrack;
|
10777 | var samples = track.samples;
|
10778 | var dtsCorrection = undefined;
|
10779 | var firstDts = -1,
|
10780 | lastDts = -1,
|
10781 | lastPts = -1;
|
10782 | var refSampleDuration = this._audioMeta.refSampleDuration;
|
10783 |
|
10784 | var mpegRawTrack = this._audioMeta.codec === 'mp3' && this._mp3UseMpegAudio;
|
10785 | var firstSegmentAfterSeek = this._dtsBaseInited && this._audioNextDts === undefined;
|
10786 |
|
10787 | var insertPrefixSilentFrame = false;
|
10788 |
|
10789 | if (!samples || samples.length === 0) {
|
10790 | return;
|
10791 | }
|
10792 | if (samples.length === 1 && !force) {
|
10793 | // If [sample count in current batch] === 1 && (force != true)
|
10794 | // Ignore and keep in demuxer's queue
|
10795 | return;
|
10796 | } // else if (force === true) do remux
|
10797 |
|
10798 | var offset = 0;
|
10799 | var mdatbox = null;
|
10800 | var mdatBytes = 0;
|
10801 |
|
10802 | // calculate initial mdat size
|
10803 | if (mpegRawTrack) {
|
10804 | // for raw mpeg buffer
|
10805 | offset = 0;
|
10806 | mdatBytes = track.length;
|
10807 | } else {
|
10808 | // for fmp4 mdat box
|
10809 | offset = 8; // size + type
|
10810 | mdatBytes = 8 + track.length;
|
10811 | }
|
10812 |
|
10813 | var lastSample = null;
|
10814 |
|
10815 | // Pop the lastSample and waiting for stash
|
10816 | if (samples.length > 1) {
|
10817 | lastSample = samples.pop();
|
10818 | mdatBytes -= lastSample.length;
|
10819 | }
|
10820 |
|
10821 | // Insert [stashed lastSample in the previous batch] to the front
|
10822 | if (this._audioStashedLastSample != null) {
|
10823 | var sample = this._audioStashedLastSample;
|
10824 | this._audioStashedLastSample = null;
|
10825 | samples.unshift(sample);
|
10826 | mdatBytes += sample.length;
|
10827 | }
|
10828 |
|
10829 | // Stash the lastSample of current batch, waiting for next batch
|
10830 | if (lastSample != null) {
|
10831 | this._audioStashedLastSample = lastSample;
|
10832 | }
|
10833 |
|
10834 | var firstSampleOriginalDts = samples[0].dts - this._dtsBase;
|
10835 |
|
10836 | // calculate dtsCorrection
|
10837 | if (this._audioNextDts) {
|
10838 | dtsCorrection = firstSampleOriginalDts - this._audioNextDts;
|
10839 | } else {
|
10840 | // this._audioNextDts == undefined
|
10841 | if (this._audioSegmentInfoList.isEmpty()) {
|
10842 | dtsCorrection = 0;
|
10843 | if (this._fillSilentAfterSeek && !this._videoSegmentInfoList.isEmpty()) {
|
10844 | if (this._audioMeta.originalCodec !== 'mp3') {
|
10845 | insertPrefixSilentFrame = true;
|
10846 | }
|
10847 | }
|
10848 | } else {
|
10849 | var _lastSample = this._audioSegmentInfoList.getLastSampleBefore(firstSampleOriginalDts);
|
10850 | if (_lastSample != null) {
|
10851 | var distance = firstSampleOriginalDts - (_lastSample.originalDts + _lastSample.duration);
|
10852 | if (distance <= 3) {
|
10853 | distance = 0;
|
10854 | }
|
10855 | var expectedDts = _lastSample.dts + _lastSample.duration + distance;
|
10856 | dtsCorrection = firstSampleOriginalDts - expectedDts;
|
10857 | } else {
|
10858 | // lastSample == null, cannot found
|
10859 | dtsCorrection = 0;
|
10860 | }
|
10861 | }
|
10862 | }
|
10863 |
|
10864 | if (insertPrefixSilentFrame) {
|
10865 | // align audio segment beginDts to match with current video segment's beginDts
|
10866 | var firstSampleDts = firstSampleOriginalDts - dtsCorrection;
|
10867 | var videoSegment = this._videoSegmentInfoList.getLastSegmentBefore(firstSampleOriginalDts);
|
10868 | if (videoSegment != null && videoSegment.beginDts < firstSampleDts) {
|
10869 | var silentUnit = _aacSilent2.default.getSilentFrame(this._audioMeta.originalCodec, this._audioMeta.channelCount);
|
10870 | if (silentUnit) {
|
10871 | var dts = videoSegment.beginDts;
|
10872 | var silentFrameDuration = firstSampleDts - videoSegment.beginDts;
|
10873 | _logger2.default.v(this.TAG, 'InsertPrefixSilentAudio: dts: ' + dts + ', duration: ' + silentFrameDuration);
|
10874 | samples.unshift({ unit: silentUnit, dts: dts, pts: dts });
|
10875 | mdatBytes += silentUnit.byteLength;
|
10876 | } // silentUnit == null: Cannot generate, skip
|
10877 | } else {
|
10878 | insertPrefixSilentFrame = false;
|
10879 | }
|
10880 | }
|
10881 |
|
10882 | var mp4Samples = [];
|
10883 |
|
10884 | // Correct dts for each sample, and calculate sample duration. Then output to mp4Samples
|
10885 | for (var i = 0; i < samples.length; i++) {
|
10886 | var _sample = samples[i];
|
10887 | var unit = _sample.unit;
|
10888 | var originalDts = _sample.dts - this._dtsBase;
|
10889 | var _dts = originalDts - dtsCorrection;
|
10890 |
|
10891 | if (firstDts === -1) {
|
10892 | firstDts = _dts;
|
10893 | }
|
10894 |
|
10895 | var sampleDuration = 0;
|
10896 |
|
10897 | if (i !== samples.length - 1) {
|
10898 | var nextDts = samples[i + 1].dts - this._dtsBase - dtsCorrection;
|
10899 | sampleDuration = nextDts - _dts;
|
10900 | } else {
|
10901 | // the last sample
|
10902 | if (lastSample != null) {
|
10903 | // use stashed sample's dts to calculate sample duration
|
10904 | var _nextDts = lastSample.dts - this._dtsBase - dtsCorrection;
|
10905 | sampleDuration = _nextDts - _dts;
|
10906 | } else if (mp4Samples.length >= 1) {
|
10907 | // use second last sample duration
|
10908 | sampleDuration = mp4Samples[mp4Samples.length - 1].duration;
|
10909 | } else {
|
10910 | // the only one sample, use reference sample duration
|
10911 | sampleDuration = Math.floor(refSampleDuration);
|
10912 | }
|
10913 | }
|
10914 |
|
10915 | var needFillSilentFrames = false;
|
10916 | var silentFrames = null;
|
10917 |
|
10918 | // Silent frame generation, if large timestamp gap detected && config.fixAudioTimestampGap
|
10919 | if (sampleDuration > refSampleDuration * 1.5 && this._audioMeta.codec !== 'mp3' && this._fillAudioTimestampGap && !_browser2.default.safari) {
|
10920 | // We need to insert silent frames to fill timestamp gap
|
10921 | needFillSilentFrames = true;
|
10922 | var delta = Math.abs(sampleDuration - refSampleDuration);
|
10923 | var frameCount = Math.ceil(delta / refSampleDuration);
|
10924 | var currentDts = _dts + refSampleDuration; // Notice: in float
|
10925 |
|
10926 | _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'));
|
10927 |
|
10928 | var _silentUnit = _aacSilent2.default.getSilentFrame(this._audioMeta.originalCodec, this._audioMeta.channelCount);
|
10929 | if (_silentUnit == null) {
|
10930 | _logger2.default.w(this.TAG, 'Unable to generate silent frame for ' + (this._audioMeta.originalCodec + ' with ' + this._audioMeta.channelCount + ' channels, repeat last frame'));
|
10931 | // Repeat last frame
|
10932 | _silentUnit = unit;
|
10933 | }
|
10934 | silentFrames = [];
|
10935 |
|
10936 | for (var j = 0; j < frameCount; j++) {
|
10937 | var intDts = Math.round(currentDts); // round to integer
|
10938 | if (silentFrames.length > 0) {
|
10939 | // Set previous frame sample duration
|
10940 | var previousFrame = silentFrames[silentFrames.length - 1];
|
10941 | previousFrame.duration = intDts - previousFrame.dts;
|
10942 | }
|
10943 | var frame = {
|
10944 | dts: intDts,
|
10945 | pts: intDts,
|
10946 | cts: 0,
|
10947 | unit: _silentUnit,
|
10948 | size: _silentUnit.byteLength,
|
10949 | duration: 0, // wait for next sample
|
10950 | originalDts: originalDts,
|
10951 | flags: {
|
10952 | isLeading: 0,
|
10953 | dependsOn: 1,
|
10954 | isDependedOn: 0,
|
10955 | hasRedundancy: 0
|
10956 | }
|
10957 | };
|
10958 | silentFrames.push(frame);
|
10959 | mdatBytes += frame.size;
|
10960 | currentDts += refSampleDuration;
|
10961 | }
|
10962 |
|
10963 | // last frame: align end time to next frame dts
|
10964 | var lastFrame = silentFrames[silentFrames.length - 1];
|
10965 | lastFrame.duration = _dts + sampleDuration - lastFrame.dts;
|
10966 |
|
10967 | // silentFrames.forEach((frame) => {
|
10968 | // Log.w(this.TAG, `SilentAudio: dts: ${frame.dts}, duration: ${frame.duration}`);
|
10969 | // });
|
10970 |
|
10971 | // Set correct sample duration for current frame
|
10972 | sampleDuration = Math.round(refSampleDuration);
|
10973 | }
|
10974 |
|
10975 | mp4Samples.push({
|
10976 | dts: _dts,
|
10977 | pts: _dts,
|
10978 | cts: 0,
|
10979 | unit: _sample.unit,
|
10980 | size: _sample.unit.byteLength,
|
10981 | duration: sampleDuration,
|
10982 | originalDts: originalDts,
|
10983 | flags: {
|
10984 | isLeading: 0,
|
10985 | dependsOn: 1,
|
10986 | isDependedOn: 0,
|
10987 | hasRedundancy: 0
|
10988 | }
|
10989 | });
|
10990 |
|
10991 | if (needFillSilentFrames) {
|
10992 | // Silent frames should be inserted after wrong-duration frame
|
10993 | mp4Samples.push.apply(mp4Samples, silentFrames);
|
10994 | }
|
10995 | }
|
10996 |
|
10997 | // allocate mdatbox
|
10998 | if (mpegRawTrack) {
|
10999 | // allocate for raw mpeg buffer
|
11000 | mdatbox = new Uint8Array(mdatBytes);
|
11001 | } else {
|
11002 | // allocate for fmp4 mdat box
|
11003 | mdatbox = new Uint8Array(mdatBytes);
|
11004 | // size field
|
11005 | mdatbox[0] = mdatBytes >>> 24 & 0xFF;
|
11006 | mdatbox[1] = mdatBytes >>> 16 & 0xFF;
|
11007 | mdatbox[2] = mdatBytes >>> 8 & 0xFF;
|
11008 | mdatbox[3] = mdatBytes & 0xFF;
|
11009 | // type field (fourCC)
|
11010 | mdatbox.set(_mp4Generator2.default.types.mdat, 4);
|
11011 | }
|
11012 |
|
11013 | // Write samples into mdatbox
|
11014 | for (var _i = 0; _i < mp4Samples.length; _i++) {
|
11015 | var _unit = mp4Samples[_i].unit;
|
11016 | mdatbox.set(_unit, offset);
|
11017 | offset += _unit.byteLength;
|
11018 | }
|
11019 |
|
11020 | var latest = mp4Samples[mp4Samples.length - 1];
|
11021 | lastDts = latest.dts + latest.duration;
|
11022 | this._audioNextDts = lastDts;
|
11023 |
|
11024 | // fill media segment info & add to info list
|
11025 | var info = new _mediaSegmentInfo.MediaSegmentInfo();
|
11026 | info.beginDts = firstDts;
|
11027 | info.endDts = lastDts;
|
11028 | info.beginPts = firstDts;
|
11029 | info.endPts = lastDts;
|
11030 | info.originalBeginDts = mp4Samples[0].originalDts;
|
11031 | info.originalEndDts = latest.originalDts + latest.duration;
|
11032 | info.firstSample = new _mediaSegmentInfo.SampleInfo(mp4Samples[0].dts, mp4Samples[0].pts, mp4Samples[0].duration, mp4Samples[0].originalDts, false);
|
11033 | info.lastSample = new _mediaSegmentInfo.SampleInfo(latest.dts, latest.pts, latest.duration, latest.originalDts, false);
|
11034 | if (!this._isLive) {
|
11035 | this._audioSegmentInfoList.append(info);
|
11036 | }
|
11037 |
|
11038 | track.samples = mp4Samples;
|
11039 | track.sequenceNumber++;
|
11040 |
|
11041 | var moofbox = null;
|
11042 |
|
11043 | if (mpegRawTrack) {
|
11044 | // Generate empty buffer, because useless for raw mpeg
|
11045 | moofbox = new Uint8Array();
|
11046 | } else {
|
11047 | // Generate moof for fmp4 segment
|
11048 | moofbox = _mp4Generator2.default.moof(track, firstDts);
|
11049 | }
|
11050 |
|
11051 | track.samples = [];
|
11052 | track.length = 0;
|
11053 |
|
11054 | var segment = {
|
11055 | type: 'audio',
|
11056 | data: this._mergeBoxes(moofbox, mdatbox).buffer,
|
11057 | sampleCount: mp4Samples.length,
|
11058 | info: info
|
11059 | };
|
11060 |
|
11061 | if (mpegRawTrack && firstSegmentAfterSeek) {
|
11062 | // For MPEG audio stream in MSE, if seeking occurred, before appending new buffer
|
11063 | // We need explicitly set timestampOffset to the desired point in timeline for mpeg SourceBuffer.
|
11064 | segment.timestampOffset = firstDts;
|
11065 | }
|
11066 |
|
11067 | this._onMediaSegment('audio', segment);
|
11068 | }
|
11069 | }, {
|
11070 | key: '_remuxVideo',
|
11071 | value: function _remuxVideo(videoTrack, force) {
|
11072 | if (this._videoMeta == null) {
|
11073 | return;
|
11074 | }
|
11075 |
|
11076 | var track = videoTrack;
|
11077 | var samples = track.samples;
|
11078 | var dtsCorrection = undefined;
|
11079 | var firstDts = -1,
|
11080 | lastDts = -1;
|
11081 | var firstPts = -1,
|
11082 | lastPts = -1;
|
11083 |
|
11084 | if (!samples || samples.length === 0) {
|
11085 | return;
|
11086 | }
|
11087 | if (samples.length === 1 && !force) {
|
11088 | // If [sample count in current batch] === 1 && (force != true)
|
11089 | // Ignore and keep in demuxer's queue
|
11090 | return;
|
11091 | } // else if (force === true) do remux
|
11092 |
|
11093 | var offset = 8;
|
11094 | var mdatbox = null;
|
11095 | var mdatBytes = 8 + videoTrack.length;
|
11096 |
|
11097 | var lastSample = null;
|
11098 |
|
11099 | // Pop the lastSample and waiting for stash
|
11100 | if (samples.length > 1) {
|
11101 | lastSample = samples.pop();
|
11102 | mdatBytes -= lastSample.length;
|
11103 | }
|
11104 |
|
11105 | // Insert [stashed lastSample in the previous batch] to the front
|
11106 | if (this._videoStashedLastSample != null) {
|
11107 | var sample = this._videoStashedLastSample;
|
11108 | this._videoStashedLastSample = null;
|
11109 | samples.unshift(sample);
|
11110 | mdatBytes += sample.length;
|
11111 | }
|
11112 |
|
11113 | // Stash the lastSample of current batch, waiting for next batch
|
11114 | if (lastSample != null) {
|
11115 | this._videoStashedLastSample = lastSample;
|
11116 | }
|
11117 |
|
11118 | var firstSampleOriginalDts = samples[0].dts - this._dtsBase;
|
11119 |
|
11120 | // calculate dtsCorrection
|
11121 | if (this._videoNextDts) {
|
11122 | dtsCorrection = firstSampleOriginalDts - this._videoNextDts;
|
11123 | } else {
|
11124 | // this._videoNextDts == undefined
|
11125 | if (this._videoSegmentInfoList.isEmpty()) {
|
11126 | dtsCorrection = 0;
|
11127 | } else {
|
11128 | var _lastSample2 = this._videoSegmentInfoList.getLastSampleBefore(firstSampleOriginalDts);
|
11129 | if (_lastSample2 != null) {
|
11130 | var distance = firstSampleOriginalDts - (_lastSample2.originalDts + _lastSample2.duration);
|
11131 | if (distance <= 3) {
|
11132 | distance = 0;
|
11133 | }
|
11134 | var expectedDts = _lastSample2.dts + _lastSample2.duration + distance;
|
11135 | dtsCorrection = firstSampleOriginalDts - expectedDts;
|
11136 | } else {
|
11137 | // lastSample == null, cannot found
|
11138 | dtsCorrection = 0;
|
11139 | }
|
11140 | }
|
11141 | }
|
11142 |
|
11143 | var info = new _mediaSegmentInfo.MediaSegmentInfo();
|
11144 | var mp4Samples = [];
|
11145 |
|
11146 | // Correct dts for each sample, and calculate sample duration. Then output to mp4Samples
|
11147 | for (var i = 0; i < samples.length; i++) {
|
11148 | var _sample2 = samples[i];
|
11149 | var originalDts = _sample2.dts - this._dtsBase;
|
11150 | var isKeyframe = _sample2.isKeyframe;
|
11151 | var dts = originalDts - dtsCorrection;
|
11152 | var cts = _sample2.cts;
|
11153 | var pts = dts + cts;
|
11154 |
|
11155 | if (firstDts === -1) {
|
11156 | firstDts = dts;
|
11157 | firstPts = pts;
|
11158 | }
|
11159 |
|
11160 | var sampleDuration = 0;
|
11161 |
|
11162 | if (i !== samples.length - 1) {
|
11163 | var nextDts = samples[i + 1].dts - this._dtsBase - dtsCorrection;
|
11164 | sampleDuration = nextDts - dts;
|
11165 | } else {
|
11166 | // the last sample
|
11167 | if (lastSample != null) {
|
11168 | // use stashed sample's dts to calculate sample duration
|
11169 | var _nextDts2 = lastSample.dts - this._dtsBase - dtsCorrection;
|
11170 | sampleDuration = _nextDts2 - dts;
|
11171 | } else if (mp4Samples.length >= 1) {
|
11172 | // use second last sample duration
|
11173 | sampleDuration = mp4Samples[mp4Samples.length - 1].duration;
|
11174 | } else {
|
11175 | // the only one sample, use reference sample duration
|
11176 | sampleDuration = Math.floor(this._videoMeta.refSampleDuration);
|
11177 | }
|
11178 | }
|
11179 |
|
11180 | if (isKeyframe) {
|
11181 | var syncPoint = new _mediaSegmentInfo.SampleInfo(dts, pts, sampleDuration, _sample2.dts, true);
|
11182 | syncPoint.fileposition = _sample2.fileposition;
|
11183 | info.appendSyncPoint(syncPoint);
|
11184 | }
|
11185 |
|
11186 | mp4Samples.push({
|
11187 | dts: dts,
|
11188 | pts: pts,
|
11189 | cts: cts,
|
11190 | units: _sample2.units,
|
11191 | size: _sample2.length,
|
11192 | isKeyframe: isKeyframe,
|
11193 | duration: sampleDuration,
|
11194 | originalDts: originalDts,
|
11195 | flags: {
|
11196 | isLeading: 0,
|
11197 | dependsOn: isKeyframe ? 2 : 1,
|
11198 | isDependedOn: isKeyframe ? 1 : 0,
|
11199 | hasRedundancy: 0,
|
11200 | isNonSync: isKeyframe ? 0 : 1
|
11201 | }
|
11202 | });
|
11203 | }
|
11204 |
|
11205 | // allocate mdatbox
|
11206 | mdatbox = new Uint8Array(mdatBytes);
|
11207 | mdatbox[0] = mdatBytes >>> 24 & 0xFF;
|
11208 | mdatbox[1] = mdatBytes >>> 16 & 0xFF;
|
11209 | mdatbox[2] = mdatBytes >>> 8 & 0xFF;
|
11210 | mdatbox[3] = mdatBytes & 0xFF;
|
11211 | mdatbox.set(_mp4Generator2.default.types.mdat, 4);
|
11212 |
|
11213 | // Write samples into mdatbox
|
11214 | for (var _i2 = 0; _i2 < mp4Samples.length; _i2++) {
|
11215 | var units = mp4Samples[_i2].units;
|
11216 | while (units.length) {
|
11217 | var unit = units.shift();
|
11218 | var data = unit.data;
|
11219 | mdatbox.set(data, offset);
|
11220 | offset += data.byteLength;
|
11221 | }
|
11222 | }
|
11223 |
|
11224 | var latest = mp4Samples[mp4Samples.length - 1];
|
11225 | lastDts = latest.dts + latest.duration;
|
11226 | lastPts = latest.pts + latest.duration;
|
11227 | this._videoNextDts = lastDts;
|
11228 |
|
11229 | // fill media segment info & add to info list
|
11230 | info.beginDts = firstDts;
|
11231 | info.endDts = lastDts;
|
11232 | info.beginPts = firstPts;
|
11233 | info.endPts = lastPts;
|
11234 | info.originalBeginDts = mp4Samples[0].originalDts;
|
11235 | info.originalEndDts = latest.originalDts + latest.duration;
|
11236 | info.firstSample = new _mediaSegmentInfo.SampleInfo(mp4Samples[0].dts, mp4Samples[0].pts, mp4Samples[0].duration, mp4Samples[0].originalDts, mp4Samples[0].isKeyframe);
|
11237 | info.lastSample = new _mediaSegmentInfo.SampleInfo(latest.dts, latest.pts, latest.duration, latest.originalDts, latest.isKeyframe);
|
11238 | if (!this._isLive) {
|
11239 | this._videoSegmentInfoList.append(info);
|
11240 | }
|
11241 |
|
11242 | track.samples = mp4Samples;
|
11243 | track.sequenceNumber++;
|
11244 |
|
11245 | // workaround for chrome < 50: force first sample as a random access point
|
11246 | // see https://bugs.chromium.org/p/chromium/issues/detail?id=229412
|
11247 | if (this._forceFirstIDR) {
|
11248 | var flags = mp4Samples[0].flags;
|
11249 | flags.dependsOn = 2;
|
11250 | flags.isNonSync = 0;
|
11251 | }
|
11252 |
|
11253 | var moofbox = _mp4Generator2.default.moof(track, firstDts);
|
11254 | track.samples = [];
|
11255 | track.length = 0;
|
11256 |
|
11257 | this._onMediaSegment('video', {
|
11258 | type: 'video',
|
11259 | data: this._mergeBoxes(moofbox, mdatbox).buffer,
|
11260 | sampleCount: mp4Samples.length,
|
11261 | info: info
|
11262 | });
|
11263 | }
|
11264 | }, {
|
11265 | key: '_mergeBoxes',
|
11266 | value: function _mergeBoxes(moof, mdat) {
|
11267 | var result = new Uint8Array(moof.byteLength + mdat.byteLength);
|
11268 | result.set(moof, 0);
|
11269 | result.set(mdat, moof.byteLength);
|
11270 | return result;
|
11271 | }
|
11272 | }, {
|
11273 | key: 'onInitSegment',
|
11274 | get: function get() {
|
11275 | return this._onInitSegment;
|
11276 | },
|
11277 | set: function set(callback) {
|
11278 | this._onInitSegment = callback;
|
11279 | }
|
11280 |
|
11281 | /* prototype: function onMediaSegment(type: string, mediaSegment: MediaSegment): void
|
11282 | MediaSegment: {
|
11283 | type: string,
|
11284 | data: ArrayBuffer,
|
11285 | sampleCount: int32
|
11286 | info: MediaSegmentInfo
|
11287 | }
|
11288 | */
|
11289 |
|
11290 | }, {
|
11291 | key: 'onMediaSegment',
|
11292 | get: function get() {
|
11293 | return this._onMediaSegment;
|
11294 | },
|
11295 | set: function set(callback) {
|
11296 | this._onMediaSegment = callback;
|
11297 | }
|
11298 | }]);
|
11299 |
|
11300 | return MP4Remuxer;
|
11301 | }();
|
11302 |
|
11303 | exports.default = MP4Remuxer;
|
11304 |
|
11305 | },{"../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){
|
11306 | ;
|
11307 |
|
11308 | Object.defineProperty(exports, "__esModule", {
|
11309 | value: true
|
11310 | });
|
11311 | /*
|
11312 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
11313 | *
|
11314 | * @author zheng qian <xqq@xqq.im>
|
11315 | *
|
11316 | * Licensed under the Apache License, Version 2.0 (the "License");
|
11317 | * you may not use this file except in compliance with the License.
|
11318 | * You may obtain a copy of the License at
|
11319 | *
|
11320 | * http://www.apache.org/licenses/LICENSE-2.0
|
11321 | *
|
11322 | * Unless required by applicable law or agreed to in writing, software
|
11323 | * distributed under the License is distributed on an "AS IS" BASIS,
|
11324 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11325 | * See the License for the specific language governing permissions and
|
11326 | * limitations under the License.
|
11327 | */
|
11328 |
|
11329 | var Browser = {};
|
11330 |
|
11331 | function detect() {
|
11332 | // modified from jquery-browser-plugin
|
11333 |
|
11334 | var ua = self.navigator.userAgent.toLowerCase();
|
11335 |
|
11336 | 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) || [];
|
11337 |
|
11338 | 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) || [];
|
11339 |
|
11340 | var matched = {
|
11341 | browser: match[5] || match[3] || match[1] || '',
|
11342 | version: match[2] || match[4] || '0',
|
11343 | majorVersion: match[4] || match[2] || '0',
|
11344 | platform: platform_match[0] || ''
|
11345 | };
|
11346 |
|
11347 | var browser = {};
|
11348 | if (matched.browser) {
|
11349 | browser[matched.browser] = true;
|
11350 |
|
11351 | var versionArray = matched.majorVersion.split('.');
|
11352 | browser.version = {
|
11353 | major: parseInt(matched.majorVersion, 10),
|
11354 | string: matched.version
|
11355 | };
|
11356 | if (versionArray.length > 1) {
|
11357 | browser.version.minor = parseInt(versionArray[1], 10);
|
11358 | }
|
11359 | if (versionArray.length > 2) {
|
11360 | browser.version.build = parseInt(versionArray[2], 10);
|
11361 | }
|
11362 | }
|
11363 |
|
11364 | if (matched.platform) {
|
11365 | browser[matched.platform] = true;
|
11366 | }
|
11367 |
|
11368 | if (browser.chrome || browser.opr || browser.safari) {
|
11369 | browser.webkit = true;
|
11370 | }
|
11371 |
|
11372 | // MSIE. IE11 has 'rv' identifer
|
11373 | if (browser.rv || browser.iemobile) {
|
11374 | if (browser.rv) {
|
11375 | delete browser.rv;
|
11376 | }
|
11377 | var msie = 'msie';
|
11378 | matched.browser = msie;
|
11379 | browser[msie] = true;
|
11380 | }
|
11381 |
|
11382 | // Microsoft Edge
|
11383 | if (browser.edge) {
|
11384 | delete browser.edge;
|
11385 | var msedge = 'msedge';
|
11386 | matched.browser = msedge;
|
11387 | browser[msedge] = true;
|
11388 | }
|
11389 |
|
11390 | // Opera 15+
|
11391 | if (browser.opr) {
|
11392 | var opera = 'opera';
|
11393 | matched.browser = opera;
|
11394 | browser[opera] = true;
|
11395 | }
|
11396 |
|
11397 | // Stock android browsers are marked as Safari
|
11398 | if (browser.safari && browser.android) {
|
11399 | var android = 'android';
|
11400 | matched.browser = android;
|
11401 | browser[android] = true;
|
11402 | }
|
11403 |
|
11404 | browser.name = matched.browser;
|
11405 | browser.platform = matched.platform;
|
11406 |
|
11407 | for (var key in Browser) {
|
11408 | if (Browser.hasOwnProperty(key)) {
|
11409 | delete Browser[key];
|
11410 | }
|
11411 | }
|
11412 | Object.assign(Browser, browser);
|
11413 | }
|
11414 |
|
11415 | detect();
|
11416 |
|
11417 | exports.default = Browser;
|
11418 |
|
11419 | },{}],40:[function(_dereq_,module,exports){
|
11420 | ;
|
11421 |
|
11422 | Object.defineProperty(exports, "__esModule", {
|
11423 | value: true
|
11424 | });
|
11425 |
|
11426 | 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; }; }();
|
11427 |
|
11428 | 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; }
|
11429 |
|
11430 | 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; }
|
11431 |
|
11432 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
11433 |
|
11434 | /*
|
11435 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
11436 | *
|
11437 | * @author zheng qian <xqq@xqq.im>
|
11438 | *
|
11439 | * Licensed under the Apache License, Version 2.0 (the "License");
|
11440 | * you may not use this file except in compliance with the License.
|
11441 | * You may obtain a copy of the License at
|
11442 | *
|
11443 | * http://www.apache.org/licenses/LICENSE-2.0
|
11444 | *
|
11445 | * Unless required by applicable law or agreed to in writing, software
|
11446 | * distributed under the License is distributed on an "AS IS" BASIS,
|
11447 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11448 | * See the License for the specific language governing permissions and
|
11449 | * limitations under the License.
|
11450 | */
|
11451 |
|
11452 | var RuntimeException = exports.RuntimeException = function () {
|
11453 | function RuntimeException(message) {
|
11454 | _classCallCheck(this, RuntimeException);
|
11455 |
|
11456 | this._message = message;
|
11457 | }
|
11458 |
|
11459 | _createClass(RuntimeException, [{
|
11460 | key: 'toString',
|
11461 | value: function toString() {
|
11462 | return this.name + ': ' + this.message;
|
11463 | }
|
11464 | }, {
|
11465 | key: 'name',
|
11466 | get: function get() {
|
11467 | return 'RuntimeException';
|
11468 | }
|
11469 | }, {
|
11470 | key: 'message',
|
11471 | get: function get() {
|
11472 | return this._message;
|
11473 | }
|
11474 | }]);
|
11475 |
|
11476 | return RuntimeException;
|
11477 | }();
|
11478 |
|
11479 | var IllegalStateException = exports.IllegalStateException = function (_RuntimeException) {
|
11480 | _inherits(IllegalStateException, _RuntimeException);
|
11481 |
|
11482 | function IllegalStateException(message) {
|
11483 | _classCallCheck(this, IllegalStateException);
|
11484 |
|
11485 | return _possibleConstructorReturn(this, (IllegalStateException.__proto__ || Object.getPrototypeOf(IllegalStateException)).call(this, message));
|
11486 | }
|
11487 |
|
11488 | _createClass(IllegalStateException, [{
|
11489 | key: 'name',
|
11490 | get: function get() {
|
11491 | return 'IllegalStateException';
|
11492 | }
|
11493 | }]);
|
11494 |
|
11495 | return IllegalStateException;
|
11496 | }(RuntimeException);
|
11497 |
|
11498 | var InvalidArgumentException = exports.InvalidArgumentException = function (_RuntimeException2) {
|
11499 | _inherits(InvalidArgumentException, _RuntimeException2);
|
11500 |
|
11501 | function InvalidArgumentException(message) {
|
11502 | _classCallCheck(this, InvalidArgumentException);
|
11503 |
|
11504 | return _possibleConstructorReturn(this, (InvalidArgumentException.__proto__ || Object.getPrototypeOf(InvalidArgumentException)).call(this, message));
|
11505 | }
|
11506 |
|
11507 | _createClass(InvalidArgumentException, [{
|
11508 | key: 'name',
|
11509 | get: function get() {
|
11510 | return 'InvalidArgumentException';
|
11511 | }
|
11512 | }]);
|
11513 |
|
11514 | return InvalidArgumentException;
|
11515 | }(RuntimeException);
|
11516 |
|
11517 | var NotImplementedException = exports.NotImplementedException = function (_RuntimeException3) {
|
11518 | _inherits(NotImplementedException, _RuntimeException3);
|
11519 |
|
11520 | function NotImplementedException(message) {
|
11521 | _classCallCheck(this, NotImplementedException);
|
11522 |
|
11523 | return _possibleConstructorReturn(this, (NotImplementedException.__proto__ || Object.getPrototypeOf(NotImplementedException)).call(this, message));
|
11524 | }
|
11525 |
|
11526 | _createClass(NotImplementedException, [{
|
11527 | key: 'name',
|
11528 | get: function get() {
|
11529 | return 'NotImplementedException';
|
11530 | }
|
11531 | }]);
|
11532 |
|
11533 | return NotImplementedException;
|
11534 | }(RuntimeException);
|
11535 |
|
11536 | },{}],41:[function(_dereq_,module,exports){
|
11537 | ;
|
11538 |
|
11539 | Object.defineProperty(exports, "__esModule", {
|
11540 | value: true
|
11541 | });
|
11542 |
|
11543 | 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; }; }(); /*
|
11544 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
11545 | *
|
11546 | * @author zheng qian <xqq@xqq.im>
|
11547 | *
|
11548 | * Licensed under the Apache License, Version 2.0 (the "License");
|
11549 | * you may not use this file except in compliance with the License.
|
11550 | * You may obtain a copy of the License at
|
11551 | *
|
11552 | * http://www.apache.org/licenses/LICENSE-2.0
|
11553 | *
|
11554 | * Unless required by applicable law or agreed to in writing, software
|
11555 | * distributed under the License is distributed on an "AS IS" BASIS,
|
11556 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11557 | * See the License for the specific language governing permissions and
|
11558 | * limitations under the License.
|
11559 | */
|
11560 |
|
11561 | var _events = _dereq_('events');
|
11562 |
|
11563 | var _events2 = _interopRequireDefault(_events);
|
11564 |
|
11565 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
11566 |
|
11567 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
11568 |
|
11569 | var Log = function () {
|
11570 | function Log() {
|
11571 | _classCallCheck(this, Log);
|
11572 | }
|
11573 |
|
11574 | _createClass(Log, null, [{
|
11575 | key: 'e',
|
11576 | value: function e(tag, msg) {
|
11577 | if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG;
|
11578 |
|
11579 | var str = '[' + tag + '] > ' + msg;
|
11580 |
|
11581 | if (Log.ENABLE_CALLBACK) {
|
11582 | Log.emitter.emit('log', 'error', str);
|
11583 | }
|
11584 |
|
11585 | if (!Log.ENABLE_ERROR) {
|
11586 | return;
|
11587 | }
|
11588 |
|
11589 | if (console.error) {
|
11590 | console.error(str);
|
11591 | } else if (console.warn) {
|
11592 | console.warn(str);
|
11593 | } else {
|
11594 | console.log(str);
|
11595 | }
|
11596 | }
|
11597 | }, {
|
11598 | key: 'i',
|
11599 | value: function i(tag, msg) {
|
11600 | if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG;
|
11601 |
|
11602 | var str = '[' + tag + '] > ' + msg;
|
11603 |
|
11604 | if (Log.ENABLE_CALLBACK) {
|
11605 | Log.emitter.emit('log', 'info', str);
|
11606 | }
|
11607 |
|
11608 | if (!Log.ENABLE_INFO) {
|
11609 | return;
|
11610 | }
|
11611 |
|
11612 | if (console.info) {
|
11613 | console.info(str);
|
11614 | } else {
|
11615 | console.log(str);
|
11616 | }
|
11617 | }
|
11618 | }, {
|
11619 | key: 'w',
|
11620 | value: function w(tag, msg) {
|
11621 | if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG;
|
11622 |
|
11623 | var str = '[' + tag + '] > ' + msg;
|
11624 |
|
11625 | if (Log.ENABLE_CALLBACK) {
|
11626 | Log.emitter.emit('log', 'warn', str);
|
11627 | }
|
11628 |
|
11629 | if (!Log.ENABLE_WARN) {
|
11630 | return;
|
11631 | }
|
11632 |
|
11633 | if (console.warn) {
|
11634 | console.warn(str);
|
11635 | } else {
|
11636 | console.log(str);
|
11637 | }
|
11638 | }
|
11639 | }, {
|
11640 | key: 'd',
|
11641 | value: function d(tag, msg) {
|
11642 | if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG;
|
11643 |
|
11644 | var str = '[' + tag + '] > ' + msg;
|
11645 |
|
11646 | if (Log.ENABLE_CALLBACK) {
|
11647 | Log.emitter.emit('log', 'debug', str);
|
11648 | }
|
11649 |
|
11650 | if (!Log.ENABLE_DEBUG) {
|
11651 | return;
|
11652 | }
|
11653 |
|
11654 | if (console.debug) {
|
11655 | console.debug(str);
|
11656 | } else {
|
11657 | console.log(str);
|
11658 | }
|
11659 | }
|
11660 | }, {
|
11661 | key: 'v',
|
11662 | value: function v(tag, msg) {
|
11663 | if (!tag || Log.FORCE_GLOBAL_TAG) tag = Log.GLOBAL_TAG;
|
11664 |
|
11665 | var str = '[' + tag + '] > ' + msg;
|
11666 |
|
11667 | if (Log.ENABLE_CALLBACK) {
|
11668 | Log.emitter.emit('log', 'verbose', str);
|
11669 | }
|
11670 |
|
11671 | if (!Log.ENABLE_VERBOSE) {
|
11672 | return;
|
11673 | }
|
11674 |
|
11675 | console.log(str);
|
11676 | }
|
11677 | }]);
|
11678 |
|
11679 | return Log;
|
11680 | }();
|
11681 |
|
11682 | Log.GLOBAL_TAG = 'flv.js';
|
11683 | Log.FORCE_GLOBAL_TAG = false;
|
11684 | Log.ENABLE_ERROR = true;
|
11685 | Log.ENABLE_INFO = true;
|
11686 | Log.ENABLE_WARN = true;
|
11687 | Log.ENABLE_DEBUG = true;
|
11688 | Log.ENABLE_VERBOSE = true;
|
11689 |
|
11690 | Log.ENABLE_CALLBACK = false;
|
11691 |
|
11692 | Log.emitter = new _events2.default();
|
11693 |
|
11694 | exports.default = Log;
|
11695 |
|
11696 | },{"events":2}],42:[function(_dereq_,module,exports){
|
11697 | ;
|
11698 |
|
11699 | Object.defineProperty(exports, "__esModule", {
|
11700 | value: true
|
11701 | });
|
11702 |
|
11703 | 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; }; }(); /*
|
11704 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
11705 | *
|
11706 | * @author zheng qian <xqq@xqq.im>
|
11707 | *
|
11708 | * Licensed under the Apache License, Version 2.0 (the "License");
|
11709 | * you may not use this file except in compliance with the License.
|
11710 | * You may obtain a copy of the License at
|
11711 | *
|
11712 | * http://www.apache.org/licenses/LICENSE-2.0
|
11713 | *
|
11714 | * Unless required by applicable law or agreed to in writing, software
|
11715 | * distributed under the License is distributed on an "AS IS" BASIS,
|
11716 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11717 | * See the License for the specific language governing permissions and
|
11718 | * limitations under the License.
|
11719 | */
|
11720 |
|
11721 | var _events = _dereq_('events');
|
11722 |
|
11723 | var _events2 = _interopRequireDefault(_events);
|
11724 |
|
11725 | var _logger = _dereq_('./logger.js');
|
11726 |
|
11727 | var _logger2 = _interopRequireDefault(_logger);
|
11728 |
|
11729 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
11730 |
|
11731 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
11732 |
|
11733 | var LoggingControl = function () {
|
11734 | function LoggingControl() {
|
11735 | _classCallCheck(this, LoggingControl);
|
11736 | }
|
11737 |
|
11738 | _createClass(LoggingControl, null, [{
|
11739 | key: 'getConfig',
|
11740 | value: function getConfig() {
|
11741 | return {
|
11742 | globalTag: _logger2.default.GLOBAL_TAG,
|
11743 | forceGlobalTag: _logger2.default.FORCE_GLOBAL_TAG,
|
11744 | enableVerbose: _logger2.default.ENABLE_VERBOSE,
|
11745 | enableDebug: _logger2.default.ENABLE_DEBUG,
|
11746 | enableInfo: _logger2.default.ENABLE_INFO,
|
11747 | enableWarn: _logger2.default.ENABLE_WARN,
|
11748 | enableError: _logger2.default.ENABLE_ERROR,
|
11749 | enableCallback: _logger2.default.ENABLE_CALLBACK
|
11750 | };
|
11751 | }
|
11752 | }, {
|
11753 | key: 'applyConfig',
|
11754 | value: function applyConfig(config) {
|
11755 | _logger2.default.GLOBAL_TAG = config.globalTag;
|
11756 | _logger2.default.FORCE_GLOBAL_TAG = config.forceGlobalTag;
|
11757 | _logger2.default.ENABLE_VERBOSE = config.enableVerbose;
|
11758 | _logger2.default.ENABLE_DEBUG = config.enableDebug;
|
11759 | _logger2.default.ENABLE_INFO = config.enableInfo;
|
11760 | _logger2.default.ENABLE_WARN = config.enableWarn;
|
11761 | _logger2.default.ENABLE_ERROR = config.enableError;
|
11762 | _logger2.default.ENABLE_CALLBACK = config.enableCallback;
|
11763 | }
|
11764 | }, {
|
11765 | key: '_notifyChange',
|
11766 | value: function _notifyChange() {
|
11767 | var emitter = LoggingControl.emitter;
|
11768 |
|
11769 | if (emitter.listenerCount('change') > 0) {
|
11770 | var config = LoggingControl.getConfig();
|
11771 | emitter.emit('change', config);
|
11772 | }
|
11773 | }
|
11774 | }, {
|
11775 | key: 'registerListener',
|
11776 | value: function registerListener(listener) {
|
11777 | LoggingControl.emitter.addListener('change', listener);
|
11778 | }
|
11779 | }, {
|
11780 | key: 'removeListener',
|
11781 | value: function removeListener(listener) {
|
11782 | LoggingControl.emitter.removeListener('change', listener);
|
11783 | }
|
11784 | }, {
|
11785 | key: 'addLogListener',
|
11786 | value: function addLogListener(listener) {
|
11787 | _logger2.default.emitter.addListener('log', listener);
|
11788 | if (_logger2.default.emitter.listenerCount('log') > 0) {
|
11789 | _logger2.default.ENABLE_CALLBACK = true;
|
11790 | LoggingControl._notifyChange();
|
11791 | }
|
11792 | }
|
11793 | }, {
|
11794 | key: 'removeLogListener',
|
11795 | value: function removeLogListener(listener) {
|
11796 | _logger2.default.emitter.removeListener('log', listener);
|
11797 | if (_logger2.default.emitter.listenerCount('log') === 0) {
|
11798 | _logger2.default.ENABLE_CALLBACK = false;
|
11799 | LoggingControl._notifyChange();
|
11800 | }
|
11801 | }
|
11802 | }, {
|
11803 | key: 'forceGlobalTag',
|
11804 | get: function get() {
|
11805 | return _logger2.default.FORCE_GLOBAL_TAG;
|
11806 | },
|
11807 | set: function set(enable) {
|
11808 | _logger2.default.FORCE_GLOBAL_TAG = enable;
|
11809 | LoggingControl._notifyChange();
|
11810 | }
|
11811 | }, {
|
11812 | key: 'globalTag',
|
11813 | get: function get() {
|
11814 | return _logger2.default.GLOBAL_TAG;
|
11815 | },
|
11816 | set: function set(tag) {
|
11817 | _logger2.default.GLOBAL_TAG = tag;
|
11818 | LoggingControl._notifyChange();
|
11819 | }
|
11820 | }, {
|
11821 | key: 'enableAll',
|
11822 | get: function get() {
|
11823 | return _logger2.default.ENABLE_VERBOSE && _logger2.default.ENABLE_DEBUG && _logger2.default.ENABLE_INFO && _logger2.default.ENABLE_WARN && _logger2.default.ENABLE_ERROR;
|
11824 | },
|
11825 | set: function set(enable) {
|
11826 | _logger2.default.ENABLE_VERBOSE = enable;
|
11827 | _logger2.default.ENABLE_DEBUG = enable;
|
11828 | _logger2.default.ENABLE_INFO = enable;
|
11829 | _logger2.default.ENABLE_WARN = enable;
|
11830 | _logger2.default.ENABLE_ERROR = enable;
|
11831 | LoggingControl._notifyChange();
|
11832 | }
|
11833 | }, {
|
11834 | key: 'enableDebug',
|
11835 | get: function get() {
|
11836 | return _logger2.default.ENABLE_DEBUG;
|
11837 | },
|
11838 | set: function set(enable) {
|
11839 | _logger2.default.ENABLE_DEBUG = enable;
|
11840 | LoggingControl._notifyChange();
|
11841 | }
|
11842 | }, {
|
11843 | key: 'enableVerbose',
|
11844 | get: function get() {
|
11845 | return _logger2.default.ENABLE_VERBOSE;
|
11846 | },
|
11847 | set: function set(enable) {
|
11848 | _logger2.default.ENABLE_VERBOSE = enable;
|
11849 | LoggingControl._notifyChange();
|
11850 | }
|
11851 | }, {
|
11852 | key: 'enableInfo',
|
11853 | get: function get() {
|
11854 | return _logger2.default.ENABLE_INFO;
|
11855 | },
|
11856 | set: function set(enable) {
|
11857 | _logger2.default.ENABLE_INFO = enable;
|
11858 | LoggingControl._notifyChange();
|
11859 | }
|
11860 | }, {
|
11861 | key: 'enableWarn',
|
11862 | get: function get() {
|
11863 | return _logger2.default.ENABLE_WARN;
|
11864 | },
|
11865 | set: function set(enable) {
|
11866 | _logger2.default.ENABLE_WARN = enable;
|
11867 | LoggingControl._notifyChange();
|
11868 | }
|
11869 | }, {
|
11870 | key: 'enableError',
|
11871 | get: function get() {
|
11872 | return _logger2.default.ENABLE_ERROR;
|
11873 | },
|
11874 | set: function set(enable) {
|
11875 | _logger2.default.ENABLE_ERROR = enable;
|
11876 | LoggingControl._notifyChange();
|
11877 | }
|
11878 | }]);
|
11879 |
|
11880 | return LoggingControl;
|
11881 | }();
|
11882 |
|
11883 | LoggingControl.emitter = new _events2.default();
|
11884 |
|
11885 | exports.default = LoggingControl;
|
11886 |
|
11887 | },{"./logger.js":41,"events":2}],43:[function(_dereq_,module,exports){
|
11888 | ;
|
11889 |
|
11890 | Object.defineProperty(exports, "__esModule", {
|
11891 | value: true
|
11892 | });
|
11893 |
|
11894 | 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; }; }();
|
11895 |
|
11896 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
11897 |
|
11898 | /*
|
11899 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
11900 | *
|
11901 | * @author zheng qian <xqq@xqq.im>
|
11902 | *
|
11903 | * Licensed under the Apache License, Version 2.0 (the "License");
|
11904 | * you may not use this file except in compliance with the License.
|
11905 | * You may obtain a copy of the License at
|
11906 | *
|
11907 | * http://www.apache.org/licenses/LICENSE-2.0
|
11908 | *
|
11909 | * Unless required by applicable law or agreed to in writing, software
|
11910 | * distributed under the License is distributed on an "AS IS" BASIS,
|
11911 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11912 | * See the License for the specific language governing permissions and
|
11913 | * limitations under the License.
|
11914 | */
|
11915 |
|
11916 | var Polyfill = function () {
|
11917 | function Polyfill() {
|
11918 | _classCallCheck(this, Polyfill);
|
11919 | }
|
11920 |
|
11921 | _createClass(Polyfill, null, [{
|
11922 | key: 'install',
|
11923 | value: function install() {
|
11924 | // ES6 Object.setPrototypeOf
|
11925 | Object.setPrototypeOf = Object.setPrototypeOf || function (obj, proto) {
|
11926 | obj.__proto__ = proto;
|
11927 | return obj;
|
11928 | };
|
11929 |
|
11930 | // ES6 Object.assign
|
11931 | Object.assign = Object.assign || function (target) {
|
11932 | if (target === undefined || target === null) {
|
11933 | throw new TypeError('Cannot convert undefined or null to object');
|
11934 | }
|
11935 |
|
11936 | var output = Object(target);
|
11937 | for (var i = 1; i < arguments.length; i++) {
|
11938 | var source = arguments[i];
|
11939 | if (source !== undefined && source !== null) {
|
11940 | for (var key in source) {
|
11941 | if (source.hasOwnProperty(key)) {
|
11942 | output[key] = source[key];
|
11943 | }
|
11944 | }
|
11945 | }
|
11946 | }
|
11947 | return output;
|
11948 | };
|
11949 |
|
11950 | // ES6 Promise (missing support in IE11)
|
11951 | if (typeof self.Promise !== 'function') {
|
11952 | _dereq_('es6-promise').polyfill();
|
11953 | }
|
11954 | }
|
11955 | }]);
|
11956 |
|
11957 | return Polyfill;
|
11958 | }();
|
11959 |
|
11960 | Polyfill.install();
|
11961 |
|
11962 | exports.default = Polyfill;
|
11963 |
|
11964 | },{"es6-promise":1}],44:[function(_dereq_,module,exports){
|
11965 | ;
|
11966 |
|
11967 | Object.defineProperty(exports, "__esModule", {
|
11968 | value: true
|
11969 | });
|
11970 | /*
|
11971 | * Copyright (C) 2016 Bilibili. All Rights Reserved.
|
11972 | *
|
11973 | * This file is derived from C++ project libWinTF8 (https://github.com/m13253/libWinTF8)
|
11974 | * @author zheng qian <xqq@xqq.im>
|
11975 | *
|
11976 | * Licensed under the Apache License, Version 2.0 (the "License");
|
11977 | * you may not use this file except in compliance with the License.
|
11978 | * You may obtain a copy of the License at
|
11979 | *
|
11980 | * http://www.apache.org/licenses/LICENSE-2.0
|
11981 | *
|
11982 | * Unless required by applicable law or agreed to in writing, software
|
11983 | * distributed under the License is distributed on an "AS IS" BASIS,
|
11984 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
11985 | * See the License for the specific language governing permissions and
|
11986 | * limitations under the License.
|
11987 | */
|
11988 |
|
11989 | function checkContinuation(uint8array, start, checkLength) {
|
11990 | var array = uint8array;
|
11991 | if (start + checkLength < array.length) {
|
11992 | while (checkLength--) {
|
11993 | if ((array[++start] & 0xC0) !== 0x80) return false;
|
11994 | }
|
11995 | return true;
|
11996 | } else {
|
11997 | return false;
|
11998 | }
|
11999 | }
|
12000 |
|
12001 | function decodeUTF8(uint8array) {
|
12002 | var out = [];
|
12003 | var input = uint8array;
|
12004 | var i = 0;
|
12005 | var length = uint8array.length;
|
12006 |
|
12007 | while (i < length) {
|
12008 | if (input[i] < 0x80) {
|
12009 | out.push(String.fromCharCode(input[i]));
|
12010 | ++i;
|
12011 | continue;
|
12012 | } else if (input[i] < 0xC0) {
|
12013 | // fallthrough
|
12014 | } else if (input[i] < 0xE0) {
|
12015 | if (checkContinuation(input, i, 1)) {
|
12016 | var ucs4 = (input[i] & 0x1F) << 6 | input[i + 1] & 0x3F;
|
12017 | if (ucs4 >= 0x80) {
|
12018 | out.push(String.fromCharCode(ucs4 & 0xFFFF));
|
12019 | i += 2;
|
12020 | continue;
|
12021 | }
|
12022 | }
|
12023 | } else if (input[i] < 0xF0) {
|
12024 | if (checkContinuation(input, i, 2)) {
|
12025 | var _ucs = (input[i] & 0xF) << 12 | (input[i + 1] & 0x3F) << 6 | input[i + 2] & 0x3F;
|
12026 | if (_ucs >= 0x800 && (_ucs & 0xF800) !== 0xD800) {
|
12027 | out.push(String.fromCharCode(_ucs & 0xFFFF));
|
12028 | i += 3;
|
12029 | continue;
|
12030 | }
|
12031 | }
|
12032 | } else if (input[i] < 0xF8) {
|
12033 | if (checkContinuation(input, i, 3)) {
|
12034 | var _ucs2 = (input[i] & 0x7) << 18 | (input[i + 1] & 0x3F) << 12 | (input[i + 2] & 0x3F) << 6 | input[i + 3] & 0x3F;
|
12035 | if (_ucs2 > 0x10000 && _ucs2 < 0x110000) {
|
12036 | _ucs2 -= 0x10000;
|
12037 | out.push(String.fromCharCode(_ucs2 >>> 10 | 0xD800));
|
12038 | out.push(String.fromCharCode(_ucs2 & 0x3FF | 0xDC00));
|
12039 | i += 4;
|
12040 | continue;
|
12041 | }
|
12042 | }
|
12043 | }
|
12044 | out.push(String.fromCharCode(0xFFFD));
|
12045 | ++i;
|
12046 | }
|
12047 |
|
12048 | return out.join('');
|
12049 | }
|
12050 |
|
12051 | exports.default = decodeUTF8;
|
12052 |
|
12053 | },{}]},{},[21])(21)
|
12054 | });
|
12055 |
|
12056 | //# sourceMappingURL=flv.js.map
|